[
  {
    "path": ".bazelignore",
    "content": "tachyon/node/test\ntachyon/py/test\nvendors/circom\n"
  },
  {
    "path": ".bazelrc",
    "content": "# Tachyon Bazel configuration file.\n# This file tries to group and simply build option for Tachyon\n#\n# ----CONFIG OPTIONS----\n#\n# Other build options:\n#     dbg:       Build with debug info\n#\n# Hardware support options:\n#     cuda: Build with NVIDIA GPU support (cuda).\n#     rocm: Build with AMD GPU support (rocm).\n#     numa: Enable numa using hwloc.\n#\n# Default build options. These are applied first and unconditionally.\n\n# For projects which use Tachyon as part of a Bazel build process, putting\n# nothing in a bazelrc will default to a monolithic build. The following line\n# opts in to modular op registration support by default.\nbuild -c opt\n\n# Make Bazel print out all options from rc files.\nbuild --announce_rc\n\n# TODO(chokobole): Remove when `cc_shared_library` is enabled by default\nbuild --experimental_cc_shared_library\n\n# Enable platform specific configurations by default depending on host machine.\ncommon --enable_platform_specific_config\n\nbuild:macos_x86_64 --config=macos\nbuild:macos_x86_64 --cpu=darwin_x86_64\nbuild:macos_x86_64 --host_cpu=darwin_x86_64\nbuild:macos_arm64 --config=macos\nbuild:macos_arm64 --cpu=darwin_arm64\nbuild:macos_arm64 --host_cpu=darwin_arm64\n\n# This config refers to building sources with nvcc.\nbuild:cuda --repo_env TACHYON_NEED_CUDA=1\nbuild:cuda --crosstool_top=@local_config_cuda//crosstool:toolchain\nbuild:cuda --@local_config_cuda//:enable_cuda\nbuild:cuda --//:has_rtti\n\n# This config refers to building sources with clang.\n# NOTE(chokobole): I didn't test it, though!\nbuild:cuda_clang --config=cuda\nbuild:cuda_clang --repo_env TACHYON_CUDA_CLANG=1\nbuild:cuda_clang --@local_config_cuda//:cuda_compiler=clang\n\n# This config refers to building sources kernels.\nbuild:rocm --crosstool_top=@local_config_rocm//crosstool:toolchain\nbuild:rocm --define=using_rocm_hipcc=true\nbuild:rocm --define=tensorflow_mkldnn_contraction_kernel=0\nbuild:rocm --repo_env TACHYON_NEED_ROCM=1\n\n# Options extracted from configure script\nbuild:numa --//:has_numa\n\n# Fastbuild config\nbuild:fastbuild -c fastbuild\n\n# Debug config\nbuild:dbg -c dbg\n\n# Opt config\nbuild:opt -c opt\nbuild:opt --//:has_openmp\n\nbuild:maxopt -c opt\nbuild:maxopt --//:has_openmp\nbuild:maxopt --config=native\nbuild:maxopt --copt=-flto\nbuild:maxopt --linkopt=-flto\n\n# By default, build Tachyon in C++ 17 mode.\nbuild:linux --cxxopt=-std=c++17\nbuild:linux --host_cxxopt=-std=c++17\nbuild:macos --cxxopt=-std=c++17\nbuild:macos --host_cxxopt=-std=c++17\nbuild:macos --objccopt=-std=c++17\nbuild:windows --cxxopt=/std:c++17\nbuild:windows --host_cxxopt=/std:c++17\n\n# Instruction set optimizations\n# TODO(chokobole): Create a feature in toolchains for avx/avx2 to\n# avoid having to define linux/win separately.\nbuild:avx_linux --copt=-mavx\nbuild:avx2_linux --copt=-mavx2\nbuild:avx512_linux --copt=-mavx512f\nbuild:avx_windows --copt=/arch=AVX\nbuild:avx2_windows --copt=/arch=AVX2\nbuild:avx512_windows --copt=/arch=AVX512\n\n# Compile for the native architecture\n# WARN(batzor): this will make the build non-portable\nbuild:native --copt=-march=native\n\n# Enable googletest build with absl.\n# See https://github.com/google/googletest/blob/v1.13.0/BUILD.bazel#L67C1-L70\nbuild --define absl=1\n# FIXME(chokobole): If this option is enabled, gtest cannot be built with nvcc.\nbuild:cuda --define absl=0\n\n# gmp needs exception.\nbuild --//:has_exception\n\ntest --test_tag_filters -benchmark,-manual,-cuda,-rust\n# TODO(GideokKim): Currently, NTT operates assuming that GPU memory is always\n# sufficient. Until the issue is resolved, add a tag to manually test in cuda\n# tests.\ntest:cuda --test_tag_filters -benchmark,-manual,-cuda,-rust,-gpu_heavy_test\n\nbuild:linux --build_tag_filters -objc,-cuda,-rust\nbuild:macos --build_tag_filters -cuda,-rust,-doxygen\nbuild:windows --build_tag_filters -objc,-cuda,-rust,-doxygen\n\n# Config-specific options should come above this line.\n\n# Load rc file with user-specific options.\ntry-import %workspace%/.bazelrc.user\n"
  },
  {
    "path": ".bazelversion",
    "content": "6.3.0\n"
  },
  {
    "path": ".clang-format",
    "content": "BasedOnStyle: Google\n# This defaults to 'Auto'. Explicitly set it for a while, so that\n# 'vector<vector<int> >' in existing files gets formatted to\n# 'vector<vector<int>>'. ('Auto' means that clang-format will only use\n# 'int>>' if the file already contains at least one such instance.)\nStandard: Cpp11\n\n# Specify the #include statement order.  This implements the order mandated by\n# the Google C++ Style Guide: related header, C headers, C++ headers, library\n# headers, and finally the project headers.\n#\n# To obtain updated lists of system headers used in the below expressions, see:\n# http://stackoverflow.com/questions/2027991/list-of-standard-header-files-in-c-and-c/2029106#2029106.\n#\n# Reference URL: https://github.com/RobotLocomotion/drake/blob/master/.clang-format\nIncludeBlocks: Regroup\nIncludeCategories:\n  # C system headers.  The header_dependency_test.py contains a copy of this\n  # list; be sure to update that test anytime this list changes.\n  - Regex:    '^[<\"](aio|arm_neon|arpa/inet|assert|complex|cpio|ctype|curses|dirent|dlfcn|errno|fcntl|fenv|float|fmtmsg|fnmatch|ftw|glob|grp|iconv|immintrin|inttypes|iso646|langinfo|libgen|limits|locale|math|monetary|mqueue|ndbm|netdb|net/if|netinet/in|netinet/tcp|nl_types|poll|pthread|pwd|regex|sched|search|semaphore|setjmp|signal|spawn|stdalign|stdarg|stdatomic|stdbool|stddef|stdint|stdio|stdlib|stdnoreturn|string|strings|stropts|sys/ioctl|sys/ipc|syslog|sys/mman|sys/msg|sys/resource|sys/select|sys/sem|sys/shm|sys/socket|sys/stat|sys/statvfs|sys/time|sys/times|sys/types|sys/uio|sys/un|sys/utsname|sys/wait|tar|term|termios|tgmath|threads|time|trace|uchar|ulimit|uncntrl|unistd|utime|utmpx|wchar|wctype|wordexp)\\.h[\">]$'\n    Priority: 20\n  # C++ system headers (as of C++23).  The header_dependency_test.py contains a\n  # copy of this list; be sure to update that test anytime this list changes.\n  - Regex:    '^[<\"](algorithm|any|array|atomic|barrier|bit|bitset|cassert|ccomplex|cctype|cerrno|cfenv|cfloat|charconv|chrono|cinttypes|ciso646|climits|clocale|cmath|codecvt|compare|complex|concepts|condition_variable|coroutine|csetjmp|csignal|cstdalign|cstdarg|cstdbool|cstddef|cstdint|cstdio|cstdlib|cstring|ctgmath|ctime|cuchar|cwchar|cwctype|deque|exception|execution|expected|filesystem|flat_map|flat_set|format|forward_list|fstream|functional|future|generator|initializer_list|iomanip|ios|iosfwd|iostream|istream|iterator|latch|limits|list|locale|map|mdspan|memory|memory_resource|mutex|new|numbers|numeric|optional|ostream|print|queue|random|ranges|ratio|regex|scoped_allocator|semaphore|set|shared_mutex|source_location|span|spanstream|sstream|stack|stacktrace|stdexcept|stdfloat|stop_token|streambuf|string|string_view|strstream|syncstream|system_error|thread|tuple|type_traits|typeindex|typeinfo|unordered_map|unordered_set|utility|valarray|variant|vector|version)[\">]$'\n    Priority: 30\n  # Other libraries' h files (with angles).\n  - Regex:    '^<'\n    Priority: 40\n  # Your project's h files.\n  - Regex:    '^\"(circomlib|tachyon|vendors)'\n    Priority: 50\n  # Other libraries' h files (with quotes).\n  - Regex:    '^\"'\n    Priority: 40\n"
  },
  {
    "path": ".dockerignore",
    "content": "# Created by https://www.toptal.com/developers/gitignore/api/c++,vim,bazel,linux,macos,python,visualstudiocode,node\n# Edit at https://www.toptal.com/developers/gitignore?templates=c++,vim,bazel,linux,macos,python,visualstudiocode,node\n\n### Bazel ###\n# gitignore template for Bazel build system\n# website: https://bazel.build/\n\n# Ignore all bazel-* symlinks. There is no full list since this can change\n# based on the name of the directory bazel is cloned into.\n/bazel-*\n\n# Directories for the Bazel IntelliJ plugin containing the generated\n# IntelliJ project files and plugin configuration. Seperate directories are\n# for the IntelliJ, Android Studio and CLion versions of the plugin.\n/.ijwb/\n/.aswb/\n/.clwb/\n\n### C++ ###\n# Prerequisites\n*.d\n\n# Compiled Object files\n*.slo\n*.lo\n*.o\n*.obj\n\n# Precompiled Headers\n*.gch\n*.pch\n\n# Compiled Dynamic libraries\n*.so\n*.dylib\n*.dll\n\n# Fortran module files\n*.mod\n*.smod\n\n# Compiled Static libraries\n*.lai\n*.la\n*.a\n*.lib\n\n# Executables\n*.exe\n*.out\n*.app\n\n### Linux ###\n*~\n\n# temporary files which can be created if a process still has a handle open of a deleted file\n.fuse_hidden*\n\n# KDE directory preferences\n.directory\n\n# Linux trash folder which might appear on any partition or disk\n.Trash-*\n\n# .nfs files are created when an open file is removed but is still being accessed\n.nfs*\n\n### macOS ###\n# General\n.DS_Store\n.AppleDouble\n.LSOverride\n\n# Icon must end with two \\r\nIcon\n\n\n# Thumbnails\n._*\n\n# Files that might appear in the root of a volume\n.DocumentRevisions-V100\n.fseventsd\n.Spotlight-V100\n.TemporaryItems\n.Trashes\n.VolumeIcon.icns\n.com.apple.timemachine.donotpresent\n\n# Directories potentially created on remote AFP share\n.AppleDB\n.AppleDesktop\nNetwork Trash Folder\nTemporary Items\n.apdisk\n\n### macOS Patch ###\n# iCloud generated files\n*.icloud\n\n### Node ###\n# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nlerna-debug.log*\n.pnpm-debug.log*\n\n# Diagnostic reports (https://nodejs.org/api/report.html)\nreport.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n*.lcov\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (https://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\njspm_packages/\n\n# Snowpack dependency directory (https://snowpack.dev/)\nweb_modules/\n\n# TypeScript cache\n*.tsbuildinfo\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional stylelint cache\n.stylelintcache\n\n# Microbundle cache\n.rpt2_cache/\n.rts2_cache_cjs/\n.rts2_cache_es/\n.rts2_cache_umd/\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variable files\n.env\n.env.development.local\n.env.test.local\n.env.production.local\n.env.local\n\n# parcel-bundler cache (https://parceljs.org/)\n.cache\n.parcel-cache\n\n# Next.js build output\n.next\nout\n\n# Nuxt.js build / generate output\n.nuxt\ndist\n\n# Gatsby files\n.cache/\n# Comment in the public line in if your project uses Gatsby and not Next.js\n# https://nextjs.org/blog/next-9-1#public-directory-support\n# public\n\n# vuepress build output\n.vuepress/dist\n\n# vuepress v2.x temp and cache directory\n.temp\n\n# Docusaurus cache and generated files\n.docusaurus\n\n# Serverless directories\n.serverless/\n\n# FuseBox cache\n.fusebox/\n\n# DynamoDB Local files\n.dynamodb/\n\n# TernJS port file\n.tern-port\n\n# Stores VSCode versions used for testing VSCode extensions\n.vscode-test\n\n# yarn v2\n.yarn/cache\n.yarn/unplugged\n.yarn/build-state.yml\n.yarn/install-state.gz\n.pnp.*\n\n### Node Patch ###\n# Serverless Webpack directories\n.webpack/\n\n# Optional stylelint cache\n\n# SvelteKit build / generate output\n.svelte-kit\n\n### Python ###\n# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n\n# Distribution / packaging\n.Python\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\nwheels/\nshare/python-wheels/\n*.egg-info/\n.installed.cfg\n*.egg\nMANIFEST\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.nox/\n.coverage\n.coverage.*\nnosetests.xml\ncoverage.xml\n*.cover\n*.py,cover\n.hypothesis/\n.pytest_cache/\ncover/\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\nlocal_settings.py\ndb.sqlite3\ndb.sqlite3-journal\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\n\n# PyBuilder\n.pybuilder/\ntarget/\n\n# Jupyter Notebook\n.ipynb_checkpoints\n\n# IPython\nprofile_default/\nipython_config.py\n\n# pyenv\n#   For a library or package, you might want to ignore these files since the code is\n#   intended to run in multiple environments; otherwise, check them in:\n# .python-version\n\n# pipenv\n#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.\n#   However, in case of collaboration, if having platform-specific dependencies or dependencies\n#   having no cross-platform support, pipenv may install dependencies that don't work, or not\n#   install all needed dependencies.\n#Pipfile.lock\n\n# poetry\n#   Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.\n#   This is especially recommended for binary packages to ensure reproducibility, and is more\n#   commonly ignored for libraries.\n#   https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control\n#poetry.lock\n\n# pdm\n#   Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.\n#pdm.lock\n#   pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it\n#   in version control.\n#   https://pdm.fming.dev/#use-with-ide\n.pdm.toml\n\n# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm\n__pypackages__/\n\n# Celery stuff\ncelerybeat-schedule\ncelerybeat.pid\n\n# SageMath parsed files\n*.sage.py\n\n# Environments\n.venv\nenv/\nvenv/\nENV/\nenv.bak/\nvenv.bak/\n\n# Spyder project settings\n.spyderproject\n.spyproject\n\n# Rope project settings\n.ropeproject\n\n# mkdocs documentation\n/site\n\n# mypy\n.mypy_cache/\n.dmypy.json\ndmypy.json\n\n# Pyre type checker\n.pyre/\n\n# pytype static type analyzer\n.pytype/\n\n# Cython debug symbols\ncython_debug/\n\n# PyCharm\n#  JetBrains specific template is maintained in a separate JetBrains.gitignore that can\n#  be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore\n#  and can be added to the global gitignore or merged into this file.  For a more nuclear\n#  option (not recommended) you can uncomment the following to ignore the entire idea folder.\n#.idea/\n\n### Python Patch ###\n# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration\npoetry.toml\n\n# ruff\n.ruff_cache/\n\n# LSP config files\npyrightconfig.json\n\n### Vim ###\n# Swap\n[._]*.s[a-v][a-z]\n!*.svg  # comment out if you don't need vector files\n[._]*.sw[a-p]\n[._]s[a-rt-v][a-z]\n[._]ss[a-gi-z]\n[._]sw[a-p]\n\n# Session\nSession.vim\nSessionx.vim\n\n# Temporary\n.netrwhist\n# Auto-generated tag files\ntags\n# Persistent undo\n[._]*.un~\n\n### VisualStudioCode ###\n.vscode/*\n!.vscode/settings.json\n!.vscode/tasks.json\n!.vscode/launch.json\n!.vscode/extensions.json\n!.vscode/*.code-snippets\n\n# Local History for Visual Studio Code\n.history/\n\n# Built Visual Studio Code Extensions\n*.vsix\n\n### VisualStudioCode Patch ###\n# Ignore all local history of files\n.history\n.ionide\n\n# End of https://www.toptal.com/developers/gitignore/api/c++,vim,bazel,linux,macos,python,visualstudiocode,node\n\n!/tachyon/build\n!/third_party/env\n/.bazelrc.user\ndocker\ndocs\n/tachyon/node/test/bazel-*\n/tachyon/py/test/bazel-*\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\n\non:\n  push:\n    branches: [\"main\"]\n  pull_request:\n    branches: [\"main\"]\n\njobs:\n  test:\n    strategy:\n      matrix:\n        os: [ubuntu-latest, macos-latest-xlarge]\n        build_flag: [fastbuild, opt]\n\n    runs-on: ${{ matrix.os }}\n    steps:\n      - name: Free up space on linux\n        if: matrix.os == 'ubuntu-latest'\n        run: |\n          df -h\n          rm -rf /usr/share/dotnet/\n          rm -rf /opt/hostedtoolcache\n          df -h\n\n      - name: Free up space on macos\n        if: matrix.os == 'macos-latest-xlarge'\n        run: |\n          df -h\n          sudo rm -rf ~/Library/Caches/*\n          df -h\n\n      - name: Checkout code\n        uses: actions/checkout@v4\n\n      - name: Set up Bazel\n        uses: bazel-contrib/setup-bazel@0.8.5\n        with:\n          # Avoid downloading Bazel every time.\n          bazelisk-cache: true\n          # Share a single build cache between workflows.\n          disk-cache: ${{ matrix.os }}-${{ matrix.build_flag }}-build\n          # Share repository cache between workflows.\n          repository-cache: false\n          # Cache external repositories\n          external-cache: false\n\n      - name: Setup Python\n        uses: actions/setup-python@v5\n        with:\n          python-version: \"3.10\"\n\n      - name: Install numpy\n        run: python3 -m pip install numpy\n\n      - name: Install OpenMP on linux\n        if: matrix.os == 'ubuntu-latest' && matrix.build_flag == 'opt'\n        run: sudo apt-get install -y libomp-dev\n\n      - name: Install OpenMP on macos\n        if: matrix.os == 'macos-latest-xlarge' && matrix.build_flag == 'opt'\n        run: brew install libomp\n\n      - name: Add .bazelrc.user on linux\n        if: matrix.os == 'ubuntu-latest'\n        run: echo \"build --action_env=CARGO=$HOME/.cargo/bin/cargo\" >> .bazelrc.user &&\n          echo \"build --@rules_rust//rust/toolchain/channel=nightly\" >> .bazelrc.user\n\n      - name: Add .bazelrc.user on macos\n        if: matrix.os == 'macos-latest-xlarge'\n        run: brew install coreutils &&\n          export PATH=\"/opt/homebrew/opt/coreutils/libexec/gnubin:$PATH\" &&\n          echo \"build --action_env=CARGO=$HOME/.cargo/bin/cargo\" >> .bazelrc.user &&\n          echo \"build --@rules_rust//rust/toolchain/channel=nightly\" >> .bazelrc.user\n\n      - name: Build\n        run: bazel build --config ${{ matrix.build_flag }} //...\n\n      - name: Test\n        # NOTE(chokobole): Test timeouts are overridden 1.5x of the default timeout due to timeout failure on GitHub Actions.\n        # See https://github.com/kroma-network/tachyon/actions/runs/9581476338/job/26418352737.\n        run: bazel test --config ${{ matrix.build_flag }} --test_output=errors --test_tag_filters -benchmark,-manual,-cuda //... --test_timeout=90,450,1350,5400\n\n      - name: Test Node Binding\n        run: |\n          cd tachyon/node/test\n          bazel test -c ${{ matrix.build_flag}} --test_output=errors //...\n\n      - name: Test Py Binding\n        run: |\n          cd tachyon/py/test\n          bazel test -c ${{ matrix.build_flag}} --test_output=errors //...\n\n      - name: Test Circom\n        run: |\n          cd vendors/circom\n          CARGO_BAZEL_REPIN=true bazel test --config ${{ matrix.build_flag}} --test_output=errors //...\n\n  lint:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v4\n        with:\n          fetch-depth: 0\n\n      - name: Set up Git\n        run: |\n          git config --global user.name 'github-actions'\n          git config --global user.email 'github-actions@github.com'\n          git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}\n          git fetch --all\n\n      - name: Setup Python for cpplint\n        uses: actions/setup-python@v5\n        with:\n          python-version: \"3.10.12\"\n\n      - name: Install cpplint\n        run: pip install cpplint\n\n      - name: Run cpplint\n        if: github.event_name == 'pull_request'\n        run: git diff --name-status HEAD origin/${{ github.base_ref }} | cut -f 2,3 | xargs cpplint --filter=-legal/copyright,-whitespace/line_length,-whitespace/indent_namespace,-build/namespaces,-runtime/references\n\n      - name: Run clang-format lint\n        uses: jidicula/clang-format-action@v4.11.0\n        with:\n          clang-format-version: \"17\"\n          check-path: \".\"\n\n      - name: Install Buildifier\n        run: |\n          wget https://github.com/bazelbuild/buildtools/releases/download/v6.4.0/buildifier-linux-amd64\n          chmod +x buildifier-linux-amd64\n          sudo mv buildifier-linux-amd64 /usr/local/bin/buildifier\n\n      - name: Run Buildifier\n        run: |\n          find . -iname \"*.bazel\" -o -iname \"*.bzl\" -o -name \"WORKSPACE\" -o -iname \"*.BUILD\" | xargs buildifier --lint=fix\n          find . -iname \"*.bazel\" -o -iname \"*.bzl\" -o -name \"WORKSPACE\" -o -iname \"*.BUILD\" | xargs buildifier --mode=check\n          git diff --exit-code\n"
  },
  {
    "path": ".github/workflows/delete_cache.yml",
    "content": "name: Delete Cache\n\non:\n  pull_request:\n    branches: [\"closed\"]\n\njobs:\n  if_merged:\n    if: github.event.pull_request.merged == true\n    runs-on: ubuntu-latest\n    steps:\n    - run: |\n        # get the ref key\n        ref_key=\"refs/pull/${{ github.event.pull_request.number }}/merge\"\n\n        # delete cache\n        gh cache list --ref $ref_key --limit 100 | cut -f 1 | xargs -n1 gh cache delete\n      continue-on-error: true\n"
  },
  {
    "path": ".gitignore",
    "content": "# Created by https://www.toptal.com/developers/gitignore/api/c++,vim,bazel,linux,macos,python,visualstudiocode,node\n# Edit at https://www.toptal.com/developers/gitignore?templates=c++,vim,bazel,linux,macos,python,visualstudiocode,node\n\n### Bazel ###\n# gitignore template for Bazel build system\n# website: https://bazel.build/\n\n# Ignore all bazel-* symlinks. There is no full list since this can change\n# based on the name of the directory bazel is cloned into.\n/bazel-*\n\n# Directories for the Bazel IntelliJ plugin containing the generated\n# IntelliJ project files and plugin configuration. Seperate directories are\n# for the IntelliJ, Android Studio and CLion versions of the plugin.\n/.ijwb/\n/.aswb/\n/.clwb/\n\n### C++ ###\n# Prerequisites\n*.d\n\n# Compiled Object files\n*.slo\n*.lo\n*.o\n*.obj\n\n# Precompiled Headers\n*.gch\n*.pch\n\n# Compiled Dynamic libraries\n*.so\n*.dylib\n*.dll\n\n# Fortran module files\n*.mod\n*.smod\n\n# Compiled Static libraries\n*.lai\n*.la\n*.a\n*.lib\n\n# Executables\n*.exe\n*.out\n*.app\n\n### Linux ###\n*~\n\n# temporary files which can be created if a process still has a handle open of a deleted file\n.fuse_hidden*\n\n# KDE directory preferences\n.directory\n\n# Linux trash folder which might appear on any partition or disk\n.Trash-*\n\n# .nfs files are created when an open file is removed but is still being accessed\n.nfs*\n\n### macOS ###\n# General\n.DS_Store\n.AppleDouble\n.LSOverride\n\n# Icon must end with two \\r\nIcon\n\n\n# Thumbnails\n._*\n\n# Files that might appear in the root of a volume\n.DocumentRevisions-V100\n.fseventsd\n.Spotlight-V100\n.TemporaryItems\n.Trashes\n.VolumeIcon.icns\n.com.apple.timemachine.donotpresent\n\n# Directories potentially created on remote AFP share\n.AppleDB\n.AppleDesktop\nNetwork Trash Folder\nTemporary Items\n.apdisk\n\n### macOS Patch ###\n# iCloud generated files\n*.icloud\n\n### Node ###\n# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nlerna-debug.log*\n.pnpm-debug.log*\n\n# Diagnostic reports (https://nodejs.org/api/report.html)\nreport.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like Istanbul\ncoverage\n*.lcov\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (https://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\njspm_packages/\n\n# Snowpack dependency directory (https://snowpack.dev/)\nweb_modules/\n\n# TypeScript cache\n*.tsbuildinfo\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional stylelint cache\n.stylelintcache\n\n# Microbundle cache\n.rpt2_cache/\n.rts2_cache_cjs/\n.rts2_cache_es/\n.rts2_cache_umd/\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variable files\n.env\n.env.development.local\n.env.test.local\n.env.production.local\n.env.local\n\n# parcel-bundler cache (https://parceljs.org/)\n.cache\n.parcel-cache\n\n# Next.js build output\n.next\nout\n\n# Nuxt.js build / generate output\n.nuxt\ndist\n\n# Gatsby files\n.cache/\n# Comment in the public line if your project uses Gatsby and not Next.js\n# https://nextjs.org/blog/next-9-1#public-directory-support\n# public\n\n# vuepress build output\n.vuepress/dist\n\n# vuepress v2.x temp and cache directory\n.temp\n\n# Docusaurus cache and generated files\n.docusaurus\n\n# Serverless directories\n.serverless/\n\n# FuseBox cache\n.fusebox/\n\n# DynamoDB Local files\n.dynamodb/\n\n# TernJS port file\n.tern-port\n\n# Stores VSCode versions used for testing VSCode extensions\n.vscode-test\n\n# yarn v2\n.yarn/cache\n.yarn/unplugged\n.yarn/build-state.yml\n.yarn/install-state.gz\n.pnp.*\n\n### Node Patch ###\n# Serverless Webpack directories\n.webpack/\n\n# Optional stylelint cache\n\n# SvelteKit build / generate output\n.svelte-kit\n\n### Python ###\n# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n\n# Distribution / packaging\n.Python\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\nwheels/\nshare/python-wheels/\n*.egg-info/\n.installed.cfg\n*.egg\nMANIFEST\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.nox/\n.coverage\n.coverage.*\nnosetests.xml\ncoverage.xml\n*.cover\n*.py,cover\n.hypothesis/\n.pytest_cache/\ncover/\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\nlocal_settings.py\ndb.sqlite3\ndb.sqlite3-journal\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\n\n# PyBuilder\n.pybuilder/\ntarget/\n\n# Jupyter Notebook\n.ipynb_checkpoints\n\n# IPython\nprofile_default/\nipython_config.py\n\n# pyenv\n#   For a library or package, you might want to ignore these files since the code is\n#   intended to run in multiple environments; otherwise, check them in:\n# .python-version\n\n# pipenv\n#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.\n#   However, in case of collaboration, if having platform-specific dependencies or dependencies\n#   having no cross-platform support, pipenv may install dependencies that don't work, or not\n#   install all needed dependencies.\n#Pipfile.lock\n\n# poetry\n#   Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.\n#   This is especially recommended for binary packages to ensure reproducibility, and is more\n#   commonly ignored for libraries.\n#   https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control\n#poetry.lock\n\n# pdm\n#   Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.\n#pdm.lock\n#   pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it\n#   in version control.\n#   https://pdm.fming.dev/#use-with-ide\n.pdm.toml\n\n# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm\n__pypackages__/\n\n# Celery stuff\ncelerybeat-schedule\ncelerybeat.pid\n\n# SageMath parsed files\n*.sage.py\n\n# Environments\n.venv\nenv/\nvenv/\nENV/\nenv.bak/\nvenv.bak/\n\n# Spyder project settings\n.spyderproject\n.spyproject\n\n# Rope project settings\n.ropeproject\n\n# mkdocs documentation\n/site\n\n# mypy\n.mypy_cache/\n.dmypy.json\ndmypy.json\n\n# Pyre type checker\n.pyre/\n\n# pytype static type analyzer\n.pytype/\n\n# Cython debug symbols\ncython_debug/\n\n# PyCharm\n#  JetBrains specific template is maintained in a separate JetBrains.gitignore that can\n#  be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore\n#  and can be added to the global gitignore or merged into this file.  For a more nuclear\n#  option (not recommended) you can uncomment the following to ignore the entire idea folder.\n#.idea/\n\n### Python Patch ###\n# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration\npoetry.toml\n\n# ruff\n.ruff_cache/\n\n# LSP config files\npyrightconfig.json\n\n### Vim ###\n# Swap\n[._]*.s[a-v][a-z]\n!*.svg  # comment out if you don't need vector files\n[._]*.sw[a-p]\n[._]s[a-rt-v][a-z]\n[._]ss[a-gi-z]\n[._]sw[a-p]\n\n# Session\nSession.vim\nSessionx.vim\n\n# Temporary\n.netrwhist\n# Auto-generated tag files\ntags\n# Persistent undo\n[._]*.un~\n\n### VisualStudioCode ###\n.vscode/*\n!.vscode/settings.json\n!.vscode/tasks.json\n!.vscode/launch.json\n!.vscode/extensions.json\n!.vscode/*.code-snippets\n\n# Local History for Visual Studio Code\n.history/\n\n# Built Visual Studio Code Extensions\n*.vsix\n\n### VisualStudioCode Patch ###\n# Ignore all local history of files\n.history\n.ionide\n\n# End of https://www.toptal.com/developers/gitignore/api/c++,vim,bazel,linux,macos,python,visualstudiocode,node\n\n!/tachyon/build\n!/third_party/env\n/.bazelrc.user\n/tachyon/node/test/bazel-*\n/tachyon/py/test/bazel-*\n"
  },
  {
    "path": ".gitmessage",
    "content": "<type>(<scope>): <summary>\n# ──────────────────────────────── 80 chars ───────────────────────────────────┤\n\n<Describe the motivation behind this change - explain WHY you are making this\nchange. Wrap all lines at 80 characters.>\n\nSee #<reference>\n\nResolves: #<issue number>\nRelated: #<issue number>\n\n# ──────────────────────────────── 80 chars ───────────────────────────────────┤\n\n\n# Example Commit Messages\n# =======================\n\n\n# ─── Example: Simple refactor ────────────────────────────────────────────────┤\n# refac: remove system dictator\n#\n# Since the owner of all contracts in this system is ProxyAdmin, SystemDictator\n# is no longer necessary. Additionally, the deployment scripts have been\n# modified to update the proxy when deploying implementation contracts.\n#\n# Resolves: #36332\n# ─────────────────────────────────────────────────────────────────────────────┤\n\n\n# ─── Example: Simple docs change ─────────────────────────────────────────────┤\n# docs: update commit message format\n# ─────────────────────────────────────────────────────────────────────────────┤\n\n\n# ─── Example: A bug fix ──────────────────────────────────────────────────────┤\n# fix(trie): use hash once on account to compute path\n#\n# Path must be computed using poseidon hash once. But, by mistake, it\n# was computed using twice.\n# ─────────────────────────────────────────────────────────────────────────────┤\n\n# ─── Example: A feature ──────────────────────────────────────────────────────┤\n# feat!: use keccak256 for codehash\n#\n# BREAKING CHANGE: Previously, codehash was calculated using poseidon.\n# So the state root since this commit will be produced differently.\n#\n# See belows for details.\n#\n# - https://eips.ethereum.org/EIPS/eip-1052\n# - https://github.com/scroll-tech/go-ethereum/pull/188\n# ─────────────────────────────────────────────────────────────────────────────┤\n\n# Commit Message Format\n# =====================\n#\n# We have very precise rules over how our Git commit messages must be formatted.\n# This format leads to easier to read commit history.\n#\n# Each commit message consists of a header, a body, and a footer.\n#\n# <header>\n# <BLANK LINE>\n# <body>\n# <BLANK LINE>\n# <footer>\n#\n# The header is mandatory.\n#\n# The body is mandatory for all commits except for those of type “docs”. When\n# the body is present it must be at least 20 characters long.\n#\n# The footer is optional.\n#\n# Any line of the commit message cannot be longer than 80 characters.\n#\n# Commit Message Header\n# ---------------------\n#\n# ```\n# <type>(<scope>): <short summary>\n#   │       │             │\n#   │       │             └─⫸ Summary in present tense. Not capitalized.\n#   │       │                 No period at the end.\n#   │       │\n#   │       └─⫸ Commit Scope: base|benchmark|build|c(subscope)|cc(subscope)|\n#   │                         circom|crypto|device|examples|halo2|math|\n#   │                         node(subscope)|plonky3|rs(subscope)|sp1|zk\n#   │\n#   └─⫸ Commit Type: build|chore|ci|docs|feat|fix|perf|refac|style|test\n# ```\n#\n# Commit Message Body\n# -------------------\n#\n# Just as in the summary, use the imperative, present tense: “fix” not “fixed”\n# nor “fixes”.\n#\n# Explain the motivation for the change in the commit message body. This commit\n# message should explain why you are making the change. You can include a\n# comparison of the previous behavior with the new behavior in order to\n# illustrate the impact of the change.\n#\n# Commit Message Footer\n# ---------------------\n#\n# The footer can contain information about usage and is also the place to\n# reference GitHub issues, yona links, and other PRs that this commit closes or\n# is related to.\n#\n# ```\n# USAGE: <usage summary>\n# <BLANK LINE>\n# <usage description + instruction example>\n# <BLANK LINE>\n# See #<reference>\n# <BLANK LINE>\n# Resolves|Related #<issue number>\n# ```\n#\n# USAGE section should start with the phrase “USAGE: ” followed by a summary of\n# the usage summary, a blank line, and a detailed description of the usage.\n"
  },
  {
    "path": "BUILD.bazel",
    "content": "load(\"@bazel_skylib//rules:common_settings.bzl\", \"bool_flag\")\nload(\"@com_github_bazelbuild_buildtools//buildifier:def.bzl\", \"buildifier\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nbool_flag(\n    name = \"shared_object\",\n    build_setting_default = False,\n)\n\nbool_flag(\n    name = \"c_shared_object\",\n    build_setting_default = False,\n)\n\nbool_flag(\n    name = \"cc_shared_object\",\n    build_setting_default = False,\n)\n\nbool_flag(\n    name = \"has_exception\",\n    build_setting_default = False,\n)\n\nbool_flag(\n    name = \"has_rtti\",\n    build_setting_default = False,\n)\n\nbool_flag(\n    name = \"has_openmp\",\n    build_setting_default = False,\n)\n\nbool_flag(\n    name = \"has_intel_openmp\",\n    build_setting_default = False,\n)\n\nbool_flag(\n    name = \"has_avx512\",\n    build_setting_default = False,\n)\n\nbool_flag(\n    name = \"has_matplotlib\",\n    build_setting_default = False,\n)\n\nbool_flag(\n    name = \"has_numa\",\n    build_setting_default = False,\n)\n\nbool_flag(\n    name = \"has_asm_prime_field\",\n    build_setting_default = True,\n)\n\nbool_flag(\n    name = \"py_binding\",\n    build_setting_default = False,\n)\n\nbool_flag(\n    name = \"node_binding\",\n    build_setting_default = False,\n)\n\nconfig_setting(\n    name = \"linux_x86_64\",\n    constraint_values = [\n        \"@platforms//os:linux\",\n        \"@platforms//cpu:x86_64\",\n    ],\n)\n\nconfig_setting(\n    name = \"linux_aarch64\",\n    constraint_values = [\n        \"@platforms//os:linux\",\n        \"@platforms//cpu:aarch64\",\n    ],\n)\n\nconfig_setting(\n    name = \"linux_ppc\",\n    constraint_values = [\n        \"@platforms//os:linux\",\n        \"@platforms//cpu:ppc\",\n    ],\n)\n\nconfig_setting(\n    name = \"linux_s390x\",\n    constraint_values = [\n        \"@platforms//os:linux\",\n        \"@platforms//cpu:s390x\",\n    ],\n)\n\nconfig_setting(\n    name = \"macos_x86_64\",\n    constraint_values = [\n        \"@platforms//os:macos\",\n        \"@platforms//cpu:x86_64\",\n    ],\n)\n\nconfig_setting(\n    name = \"macos_aarch64\",\n    constraint_values = [\n        \"@platforms//os:macos\",\n        \"@platforms//cpu:aarch64\",\n    ],\n)\n\nconfig_setting(\n    name = \"optimized\",\n    values = {\"compilation_mode\": \"opt\"},\n)\n\nconfig_setting(\n    name = \"tachyon_shared_object\",\n    flag_values = {\":shared_object\": \"true\"},\n)\n\nconfig_setting(\n    name = \"tachyon_c_shared_object\",\n    flag_values = {\":c_shared_object\": \"true\"},\n)\n\nconfig_setting(\n    name = \"tachyon_cc_shared_object\",\n    flag_values = {\":cc_shared_object\": \"true\"},\n)\n\nconfig_setting(\n    name = \"tachyon_has_exception\",\n    flag_values = {\":has_exception\": \"true\"},\n)\n\nconfig_setting(\n    name = \"tachyon_has_rtti\",\n    flag_values = {\":has_rtti\": \"true\"},\n)\n\nconfig_setting(\n    name = \"tachyon_has_openmp\",\n    flag_values = {\":has_openmp\": \"true\"},\n)\n\nconfig_setting(\n    name = \"tachyon_has_intel_openmp\",\n    flag_values = {\n        \":has_openmp\": \"true\",\n        \":has_intel_openmp\": \"true\",\n    },\n)\n\nconfig_setting(\n    name = \"tachyon_has_openmp_on_macos\",\n    constraint_values = [\n        \"@platforms//os:macos\",\n    ],\n    flag_values = {\n        \":has_openmp\": \"true\",\n    },\n)\n\nconfig_setting(\n    name = \"tachyon_has_avx512\",\n    constraint_values = [\"@platforms//cpu:x86_64\"],\n    flag_values = {\":has_avx512\": \"true\"},\n)\n\nconfig_setting(\n    name = \"tachyon_has_matplotlib\",\n    flag_values = {\":has_matplotlib\": \"true\"},\n)\n\nconfig_setting(\n    name = \"tachyon_has_numa\",\n    flag_values = {\"has_numa\": \"true\"},\n)\n\nconfig_setting(\n    name = \"tachyon_has_asm_prime_field\",\n    flag_values = {\"has_asm_prime_field\": \"true\"},\n)\n\nconfig_setting(\n    name = \"tachyon_py_binding\",\n    flag_values = {\"py_binding\": \"true\"},\n)\n\nconfig_setting(\n    name = \"tachyon_node_binding\",\n    flag_values = {\"node_binding\": \"true\"},\n)\n\nbuildifier(\n    name = \"buildifier\",\n)\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[workspace]\nmembers = [\n    \"benchmark/msm/arkworks\",\n    \"benchmark/msm/bellman\",\n    \"benchmark/msm/halo2\",\n    \"benchmark/fft/arkworks\",\n    \"benchmark/fft/bellman\",\n    \"benchmark/fft/halo2\",\n    \"benchmark/fft_batch/plonky3\",\n    \"benchmark/fri/plonky3\",\n    \"benchmark/poseidon/arkworks\",\n    \"benchmark/poseidon2/horizen\",\n    \"benchmark/poseidon2/plonky3\",\n    \"tachyon/rs\",\n    \"vendors/scroll_halo2\",\n    \"vendors/plonky3\",\n    \"vendors/sp1\",\n]\n"
  },
  {
    "path": "LICENSE",
    "content": "(The MIT License)\n\nCopyright 2023 Lightscale Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "LICENSE-APACHE.EF",
    "content": "APACHE LICENSE\nVersion 2.0, January 2004\nhttp://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\nDefinitions.\n\n“License” shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.\n\n\"Licensor\" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.\n\n\"Legal Entity\" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, \"control\" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.\n\n\"You\" (or \"Your\") shall mean an individual or Legal Entity exercising permissions granted by this License.\n\n\"Source\" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.\n\n\"Object\" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.\n\n\"Work\" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).\n\n\"Derivative Works\" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.\n\n\"Contribution\" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, \"submitted\" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as \"Not a Contribution.\"\n\n\"Contributor\" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.\n\nGrant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.\n\nGrant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.\n\nRedistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:\n\nYou must give any other recipients of the Work or Derivative Works a copy of this License; and\nYou must cause any modified files to carry prominent notices stating that You changed the files; and\n(c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and\nIf the Work includes a \"NOTICE\" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.\n\nYou may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.\n\nSubmission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.\n\nTrademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.\n\nDisclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.\n\nLimitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.\n\nAccepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work.\n\nTo apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets \"[ ]\" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same \"printed page\" as the copyright notice for easier identification within third-party archives.\n\nCopyright 2022 Ethereum Foundation\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nYou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.\n"
  },
  {
    "path": "LICENSE-APACHE.arkworks-rs",
    "content": "                              Apache License\n                        Version 2.0, January 2004\n                     http://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n   \"License\" shall mean the terms and conditions for use, reproduction,\n   and distribution as defined by Sections 1 through 9 of this document.\n\n   \"Licensor\" shall mean the copyright owner or entity authorized by\n   the copyright owner that is granting the License.\n\n   \"Legal Entity\" shall mean the union of the acting entity and all\n   other entities that control, are controlled by, or are under common\n   control with that entity. For the purposes of this definition,\n   \"control\" means (i) the power, direct or indirect, to cause the\n   direction or management of such entity, whether by contract or\n   otherwise, or (ii) ownership of fifty percent (50%) or more of the\n   outstanding shares, or (iii) beneficial ownership of such entity.\n\n   \"You\" (or \"Your\") shall mean an individual or Legal Entity\n   exercising permissions granted by this License.\n\n   \"Source\" form shall mean the preferred form for making modifications,\n   including but not limited to software source code, documentation\n   source, and configuration files.\n\n   \"Object\" form shall mean any form resulting from mechanical\n   transformation or translation of a Source form, including but\n   not limited to compiled object code, generated documentation,\n   and conversions to other media types.\n\n   \"Work\" shall mean the work of authorship, whether in Source or\n   Object form, made available under the License, as indicated by a\n   copyright notice that is included in or attached to the work\n   (an example is provided in the Appendix below).\n\n   \"Derivative Works\" shall mean any work, whether in Source or Object\n   form, that is based on (or derived from) the Work and for which the\n   editorial revisions, annotations, elaborations, or other modifications\n   represent, as a whole, an original work of authorship. For the purposes\n   of this License, Derivative Works shall not include works that remain\n   separable from, or merely link (or bind by name) to the interfaces of,\n   the Work and Derivative Works thereof.\n\n   \"Contribution\" shall mean any work of authorship, including\n   the original version of the Work and any modifications or additions\n   to that Work or Derivative Works thereof, that is intentionally\n   submitted to Licensor for inclusion in the Work by the copyright owner\n   or by an individual or Legal Entity authorized to submit on behalf of\n   the copyright owner. For the purposes of this definition, \"submitted\"\n   means any form of electronic, verbal, or written communication sent\n   to the Licensor or its representatives, including but not limited to\n   communication on electronic mailing lists, source code control systems,\n   and issue tracking systems that are managed by, or on behalf of, the\n   Licensor for the purpose of discussing and improving the Work, but\n   excluding communication that is conspicuously marked or otherwise\n   designated in writing by the copyright owner as \"Not a Contribution.\"\n\n   \"Contributor\" shall mean Licensor and any individual or Legal Entity\n   on behalf of whom a Contribution has been received by Licensor and\n   subsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   copyright license to reproduce, prepare Derivative Works of,\n   publicly display, publicly perform, sublicense, and distribute the\n   Work and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   (except as stated in this section) patent license to make, have made,\n   use, offer to sell, sell, import, and otherwise transfer the Work,\n   where such license applies only to those patent claims licensable\n   by such Contributor that are necessarily infringed by their\n   Contribution(s) alone or by combination of their Contribution(s)\n   with the Work to which such Contribution(s) was submitted. If You\n   institute patent litigation against any entity (including a\n   cross-claim or counterclaim in a lawsuit) alleging that the Work\n   or a Contribution incorporated within the Work constitutes direct\n   or contributory patent infringement, then any patent licenses\n   granted to You under this License for that Work shall terminate\n   as of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the\n   Work or Derivative Works thereof in any medium, with or without\n   modifications, and in Source or Object form, provided that You\n   meet the following conditions:\n\n   (a) You must give any other recipients of the Work or\n       Derivative Works a copy of this License; and\n\n   (b) You must cause any modified files to carry prominent notices\n       stating that You changed the files; and\n\n   (c) You must retain, in the Source form of any Derivative Works\n       that You distribute, all copyright, patent, trademark, and\n       attribution notices from the Source form of the Work,\n       excluding those notices that do not pertain to any part of\n       the Derivative Works; and\n\n   (d) If the Work includes a \"NOTICE\" text file as part of its\n       distribution, then any Derivative Works that You distribute must\n       include a readable copy of the attribution notices contained\n       within such NOTICE file, excluding those notices that do not\n       pertain to any part of the Derivative Works, in at least one\n       of the following places: within a NOTICE text file distributed\n       as part of the Derivative Works; within the Source form or\n       documentation, if provided along with the Derivative Works; or,\n       within a display generated by the Derivative Works, if and\n       wherever such third-party notices normally appear. The contents\n       of the NOTICE file are for informational purposes only and\n       do not modify the License. You may add Your own attribution\n       notices within Derivative Works that You distribute, alongside\n       or as an addendum to the NOTICE text from the Work, provided\n       that such additional attribution notices cannot be construed\n       as modifying the License.\n\n   You may add Your own copyright statement to Your modifications and\n   may provide additional or different license terms and conditions\n   for use, reproduction, or distribution of Your modifications, or\n   for any such Derivative Works as a whole, provided Your use,\n   reproduction, and distribution of the Work otherwise complies with\n   the conditions stated in this License.\n\n5. Submission of Contributions. Unless You explicitly state otherwise,\n   any Contribution intentionally submitted for inclusion in the Work\n   by You to the Licensor shall be under the terms and conditions of\n   this License, without any additional terms or conditions.\n   Notwithstanding the above, nothing herein shall supersede or modify\n   the terms of any separate license agreement you may have executed\n   with Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade\n   names, trademarks, service marks, or product names of the Licensor,\n   except as required for reasonable and customary use in describing the\n   origin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or\n   agreed to in writing, Licensor provides the Work (and each\n   Contributor provides its Contributions) on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n   implied, including, without limitation, any warranties or conditions\n   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n   PARTICULAR PURPOSE. You are solely responsible for determining the\n   appropriateness of using or redistributing the Work and assume any\n   risks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory,\n   whether in tort (including negligence), contract, or otherwise,\n   unless required by applicable law (such as deliberate and grossly\n   negligent acts) or agreed to in writing, shall any Contributor be\n   liable to You for damages, including any direct, indirect, special,\n   incidental, or consequential damages of any character arising as a\n   result of this License or out of the use or inability to use the\n   Work (including but not limited to damages for loss of goodwill,\n   work stoppage, computer failure or malfunction, or any and all\n   other commercial damages or losses), even if such Contributor\n   has been advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability. While redistributing\n   the Work or Derivative Works thereof, You may choose to offer,\n   and charge a fee for, acceptance of support, warranty, indemnity,\n   or other liability obligations and/or rights consistent with this\n   License. However, in accepting such obligations, You may act only\n   on Your own behalf and on Your sole responsibility, not on behalf\n   of any other Contributor, and only if You agree to indemnify,\n   defend, and hold each Contributor harmless for any liability\n   incurred by, or claims asserted against, such Contributor by reason\n   of your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work.\n\n   To apply the Apache License to your work, attach the following\n   boilerplate notice, with the fields enclosed by brackets \"[]\"\n   replaced with your own identifying information. (Don't include\n   the brackets!)  The text should be enclosed in the appropriate\n   comment syntax for the file format. We also recommend that a\n   file or class name and description of purpose be included on the\n   same \"printed page\" as the copyright notice for easier\n   identification within third-party archives.\n\nCopyright 2022 arkworks contributors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n\thttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n"
  },
  {
    "path": "LICENSE-APACHE.era-bellman-cuda",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n   "
  },
  {
    "path": "LICENSE-APACHE.halo2",
    "content": "                              Apache License\n                        Version 2.0, January 2004\n                     http://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n   \"License\" shall mean the terms and conditions for use, reproduction,\n   and distribution as defined by Sections 1 through 9 of this document.\n\n   \"Licensor\" shall mean the copyright owner or entity authorized by\n   the copyright owner that is granting the License.\n\n   \"Legal Entity\" shall mean the union of the acting entity and all\n   other entities that control, are controlled by, or are under common\n   control with that entity. For the purposes of this definition,\n   \"control\" means (i) the power, direct or indirect, to cause the\n   direction or management of such entity, whether by contract or\n   otherwise, or (ii) ownership of fifty percent (50%) or more of the\n   outstanding shares, or (iii) beneficial ownership of such entity.\n\n   \"You\" (or \"Your\") shall mean an individual or Legal Entity\n   exercising permissions granted by this License.\n\n   \"Source\" form shall mean the preferred form for making modifications,\n   including but not limited to software source code, documentation\n   source, and configuration files.\n\n   \"Object\" form shall mean any form resulting from mechanical\n   transformation or translation of a Source form, including but\n   not limited to compiled object code, generated documentation,\n   and conversions to other media types.\n\n   \"Work\" shall mean the work of authorship, whether in Source or\n   Object form, made available under the License, as indicated by a\n   copyright notice that is included in or attached to the work\n   (an example is provided in the Appendix below).\n\n   \"Derivative Works\" shall mean any work, whether in Source or Object\n   form, that is based on (or derived from) the Work and for which the\n   editorial revisions, annotations, elaborations, or other modifications\n   represent, as a whole, an original work of authorship. For the purposes\n   of this License, Derivative Works shall not include works that remain\n   separable from, or merely link (or bind by name) to the interfaces of,\n   the Work and Derivative Works thereof.\n\n   \"Contribution\" shall mean any work of authorship, including\n   the original version of the Work and any modifications or additions\n   to that Work or Derivative Works thereof, that is intentionally\n   submitted to Licensor for inclusion in the Work by the copyright owner\n   or by an individual or Legal Entity authorized to submit on behalf of\n   the copyright owner. For the purposes of this definition, \"submitted\"\n   means any form of electronic, verbal, or written communication sent\n   to the Licensor or its representatives, including but not limited to\n   communication on electronic mailing lists, source code control systems,\n   and issue tracking systems that are managed by, or on behalf of, the\n   Licensor for the purpose of discussing and improving the Work, but\n   excluding communication that is conspicuously marked or otherwise\n   designated in writing by the copyright owner as \"Not a Contribution.\"\n\n   \"Contributor\" shall mean Licensor and any individual or Legal Entity\n   on behalf of whom a Contribution has been received by Licensor and\n   subsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   copyright license to reproduce, prepare Derivative Works of,\n   publicly display, publicly perform, sublicense, and distribute the\n   Work and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   (except as stated in this section) patent license to make, have made,\n   use, offer to sell, sell, import, and otherwise transfer the Work,\n   where such license applies only to those patent claims licensable\n   by such Contributor that are necessarily infringed by their\n   Contribution(s) alone or by combination of their Contribution(s)\n   with the Work to which such Contribution(s) was submitted. If You\n   institute patent litigation against any entity (including a\n   cross-claim or counterclaim in a lawsuit) alleging that the Work\n   or a Contribution incorporated within the Work constitutes direct\n   or contributory patent infringement, then any patent licenses\n   granted to You under this License for that Work shall terminate\n   as of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the\n   Work or Derivative Works thereof in any medium, with or without\n   modifications, and in Source or Object form, provided that You\n   meet the following conditions:\n\n   (a) You must give any other recipients of the Work or\n       Derivative Works a copy of this License; and\n\n   (b) You must cause any modified files to carry prominent notices\n       stating that You changed the files; and\n\n   (c) You must retain, in the Source form of any Derivative Works\n       that You distribute, all copyright, patent, trademark, and\n       attribution notices from the Source form of the Work,\n       excluding those notices that do not pertain to any part of\n       the Derivative Works; and\n\n   (d) If the Work includes a \"NOTICE\" text file as part of its\n       distribution, then any Derivative Works that You distribute must\n       include a readable copy of the attribution notices contained\n       within such NOTICE file, excluding those notices that do not\n       pertain to any part of the Derivative Works, in at least one\n       of the following places: within a NOTICE text file distributed\n       as part of the Derivative Works; within the Source form or\n       documentation, if provided along with the Derivative Works; or,\n       within a display generated by the Derivative Works, if and\n       wherever such third-party notices normally appear. The contents\n       of the NOTICE file are for informational purposes only and\n       do not modify the License. You may add Your own attribution\n       notices within Derivative Works that You distribute, alongside\n       or as an addendum to the NOTICE text from the Work, provided\n       that such additional attribution notices cannot be construed\n       as modifying the License.\n\n   You may add Your own copyright statement to Your modifications and\n   may provide additional or different license terms and conditions\n   for use, reproduction, or distribution of Your modifications, or\n   for any such Derivative Works as a whole, provided Your use,\n   reproduction, and distribution of the Work otherwise complies with\n   the conditions stated in this License.\n\n5. Submission of Contributions. Unless You explicitly state otherwise,\n   any Contribution intentionally submitted for inclusion in the Work\n   by You to the Licensor shall be under the terms and conditions of\n   this License, without any additional terms or conditions.\n   Notwithstanding the above, nothing herein shall supersede or modify\n   the terms of any separate license agreement you may have executed\n   with Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade\n   names, trademarks, service marks, or product names of the Licensor,\n   except as required for reasonable and customary use in describing the\n   origin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or\n   agreed to in writing, Licensor provides the Work (and each\n   Contributor provides its Contributions) on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n   implied, including, without limitation, any warranties or conditions\n   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n   PARTICULAR PURPOSE. You are solely responsible for determining the\n   appropriateness of using or redistributing the Work and assume any\n   risks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory,\n   whether in tort (including negligence), contract, or otherwise,\n   unless required by applicable law (such as deliberate and grossly\n   negligent acts) or agreed to in writing, shall any Contributor be\n   liable to You for damages, including any direct, indirect, special,\n   incidental, or consequential damages of any character arising as a\n   result of this License or out of the use or inability to use the\n   Work (including but not limited to damages for loss of goodwill,\n   work stoppage, computer failure or malfunction, or any and all\n   other commercial damages or losses), even if such Contributor\n   has been advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability. While redistributing\n   the Work or Derivative Works thereof, You may choose to offer,\n   and charge a fee for, acceptance of support, warranty, indemnity,\n   or other liability obligations and/or rights consistent with this\n   License. However, in accepting such obligations, You may act only\n   on Your own behalf and on Your sole responsibility, not on behalf\n   of any other Contributor, and only if You agree to indemnify,\n   defend, and hold each Contributor harmless for any liability\n   incurred by, or claims asserted against, such Contributor by reason\n   of your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work.\n\n   To apply the Apache License to your work, attach the following\n   boilerplate notice, with the fields enclosed by brackets \"[]\"\n   replaced with your own identifying information. (Don't include\n   the brackets!)  The text should be enclosed in the appropriate\n   comment syntax for the file format. We also recommend that a\n   file or class name and description of purpose be included on the\n   same \"printed page\" as the copyright notice for easier\n   identification within third-party archives.\n\nCopyright [yyyy] [name of copyright owner]\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n\thttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n"
  },
  {
    "path": "LICENSE-APACHE.plonky3",
    "content": "                              Apache License\n                        Version 2.0, January 2004\n                     http://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n   \"License\" shall mean the terms and conditions for use, reproduction,\n   and distribution as defined by Sections 1 through 9 of this document.\n\n   \"Licensor\" shall mean the copyright owner or entity authorized by\n   the copyright owner that is granting the License.\n\n   \"Legal Entity\" shall mean the union of the acting entity and all\n   other entities that control, are controlled by, or are under common\n   control with that entity. For the purposes of this definition,\n   \"control\" means (i) the power, direct or indirect, to cause the\n   direction or management of such entity, whether by contract or\n   otherwise, or (ii) ownership of fifty percent (50%) or more of the\n   outstanding shares, or (iii) beneficial ownership of such entity.\n\n   \"You\" (or \"Your\") shall mean an individual or Legal Entity\n   exercising permissions granted by this License.\n\n   \"Source\" form shall mean the preferred form for making modifications,\n   including but not limited to software source code, documentation\n   source, and configuration files.\n\n   \"Object\" form shall mean any form resulting from mechanical\n   transformation or translation of a Source form, including but\n   not limited to compiled object code, generated documentation,\n   and conversions to other media types.\n\n   \"Work\" shall mean the work of authorship, whether in Source or\n   Object form, made available under the License, as indicated by a\n   copyright notice that is included in or attached to the work\n   (an example is provided in the Appendix below).\n\n   \"Derivative Works\" shall mean any work, whether in Source or Object\n   form, that is based on (or derived from) the Work and for which the\n   editorial revisions, annotations, elaborations, or other modifications\n   represent, as a whole, an original work of authorship. For the purposes\n   of this License, Derivative Works shall not include works that remain\n   separable from, or merely link (or bind by name) to the interfaces of,\n   the Work and Derivative Works thereof.\n\n   \"Contribution\" shall mean any work of authorship, including\n   the original version of the Work and any modifications or additions\n   to that Work or Derivative Works thereof, that is intentionally\n   submitted to Licensor for inclusion in the Work by the copyright owner\n   or by an individual or Legal Entity authorized to submit on behalf of\n   the copyright owner. For the purposes of this definition, \"submitted\"\n   means any form of electronic, verbal, or written communication sent\n   to the Licensor or its representatives, including but not limited to\n   communication on electronic mailing lists, source code control systems,\n   and issue tracking systems that are managed by, or on behalf of, the\n   Licensor for the purpose of discussing and improving the Work, but\n   excluding communication that is conspicuously marked or otherwise\n   designated in writing by the copyright owner as \"Not a Contribution.\"\n\n   \"Contributor\" shall mean Licensor and any individual or Legal Entity\n   on behalf of whom a Contribution has been received by Licensor and\n   subsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   copyright license to reproduce, prepare Derivative Works of,\n   publicly display, publicly perform, sublicense, and distribute the\n   Work and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   (except as stated in this section) patent license to make, have made,\n   use, offer to sell, sell, import, and otherwise transfer the Work,\n   where such license applies only to those patent claims licensable\n   by such Contributor that are necessarily infringed by their\n   Contribution(s) alone or by combination of their Contribution(s)\n   with the Work to which such Contribution(s) was submitted. If You\n   institute patent litigation against any entity (including a\n   cross-claim or counterclaim in a lawsuit) alleging that the Work\n   or a Contribution incorporated within the Work constitutes direct\n   or contributory patent infringement, then any patent licenses\n   granted to You under this License for that Work shall terminate\n   as of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the\n   Work or Derivative Works thereof in any medium, with or without\n   modifications, and in Source or Object form, provided that You\n   meet the following conditions:\n\n   (a) You must give any other recipients of the Work or\n       Derivative Works a copy of this License; and\n\n   (b) You must cause any modified files to carry prominent notices\n       stating that You changed the files; and\n\n   (c) You must retain, in the Source form of any Derivative Works\n       that You distribute, all copyright, patent, trademark, and\n       attribution notices from the Source form of the Work,\n       excluding those notices that do not pertain to any part of\n       the Derivative Works; and\n\n   (d) If the Work includes a \"NOTICE\" text file as part of its\n       distribution, then any Derivative Works that You distribute must\n       include a readable copy of the attribution notices contained\n       within such NOTICE file, excluding those notices that do not\n       pertain to any part of the Derivative Works, in at least one\n       of the following places: within a NOTICE text file distributed\n       as part of the Derivative Works; within the Source form or\n       documentation, if provided along with the Derivative Works; or,\n       within a display generated by the Derivative Works, if and\n       wherever such third-party notices normally appear. The contents\n       of the NOTICE file are for informational purposes only and\n       do not modify the License. You may add Your own attribution\n       notices within Derivative Works that You distribute, alongside\n       or as an addendum to the NOTICE text from the Work, provided\n       that such additional attribution notices cannot be construed\n       as modifying the License.\n\n   You may add Your own copyright statement to Your modifications and\n   may provide additional or different license terms and conditions\n   for use, reproduction, or distribution of Your modifications, or\n   for any such Derivative Works as a whole, provided Your use,\n   reproduction, and distribution of the Work otherwise complies with\n   the conditions stated in this License.\n\n5. Submission of Contributions. Unless You explicitly state otherwise,\n   any Contribution intentionally submitted for inclusion in the Work\n   by You to the Licensor shall be under the terms and conditions of\n   this License, without any additional terms or conditions.\n   Notwithstanding the above, nothing herein shall supersede or modify\n   the terms of any separate license agreement you may have executed\n   with Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade\n   names, trademarks, service marks, or product names of the Licensor,\n   except as required for reasonable and customary use in describing the\n   origin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or\n   agreed to in writing, Licensor provides the Work (and each\n   Contributor provides its Contributions) on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n   implied, including, without limitation, any warranties or conditions\n   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n   PARTICULAR PURPOSE. You are solely responsible for determining the\n   appropriateness of using or redistributing the Work and assume any\n   risks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory,\n   whether in tort (including negligence), contract, or otherwise,\n   unless required by applicable law (such as deliberate and grossly\n   negligent acts) or agreed to in writing, shall any Contributor be\n   liable to You for damages, including any direct, indirect, special,\n   incidental, or consequential damages of any character arising as a\n   result of this License or out of the use or inability to use the\n   Work (including but not limited to damages for loss of goodwill,\n   work stoppage, computer failure or malfunction, or any and all\n   other commercial damages or losses), even if such Contributor\n   has been advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability. While redistributing\n   the Work or Derivative Works thereof, You may choose to offer,\n   and charge a fee for, acceptance of support, warranty, indemnity,\n   or other liability obligations and/or rights consistent with this\n   License. However, in accepting such obligations, You may act only\n   on Your own behalf and on Your sole responsibility, not on behalf\n   of any other Contributor, and only if You agree to indemnify,\n   defend, and hold each Contributor harmless for any liability\n   incurred by, or claims asserted against, such Contributor by reason\n   of your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work.\n\n   To apply the Apache License to your work, attach the following\n   boilerplate notice, with the fields enclosed by brackets \"[]\"\n   replaced with your own identifying information. (Don't include\n   the brackets!)  The text should be enclosed in the appropriate\n   comment syntax for the file format. We also recommend that a\n   file or class name and description of purpose be included on the\n   same \"printed page\" as the copyright notice for easier\n   identification within third-party archives.\n\nCopyright [yyyy] [name of copyright owner]\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n\thttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n"
  },
  {
    "path": "LICENSE-APACHE.scroll",
    "content": "                              Apache License\n                        Version 2.0, January 2004\n                     http://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n   \"License\" shall mean the terms and conditions for use, reproduction,\n   and distribution as defined by Sections 1 through 9 of this document.\n\n   \"Licensor\" shall mean the copyright owner or entity authorized by\n   the copyright owner that is granting the License.\n\n   \"Legal Entity\" shall mean the union of the acting entity and all\n   other entities that control, are controlled by, or are under common\n   control with that entity. For the purposes of this definition,\n   \"control\" means (i) the power, direct or indirect, to cause the\n   direction or management of such entity, whether by contract or\n   otherwise, or (ii) ownership of fifty percent (50%) or more of the\n   outstanding shares, or (iii) beneficial ownership of such entity.\n\n   \"You\" (or \"Your\") shall mean an individual or Legal Entity\n   exercising permissions granted by this License.\n\n   \"Source\" form shall mean the preferred form for making modifications,\n   including but not limited to software source code, documentation\n   source, and configuration files.\n\n   \"Object\" form shall mean any form resulting from mechanical\n   transformation or translation of a Source form, including but\n   not limited to compiled object code, generated documentation,\n   and conversions to other media types.\n\n   \"Work\" shall mean the work of authorship, whether in Source or\n   Object form, made available under the License, as indicated by a\n   copyright notice that is included in or attached to the work\n   (an example is provided in the Appendix below).\n\n   \"Derivative Works\" shall mean any work, whether in Source or Object\n   form, that is based on (or derived from) the Work and for which the\n   editorial revisions, annotations, elaborations, or other modifications\n   represent, as a whole, an original work of authorship. For the purposes\n   of this License, Derivative Works shall not include works that remain\n   separable from, or merely link (or bind by name) to the interfaces of,\n   the Work and Derivative Works thereof.\n\n   \"Contribution\" shall mean any work of authorship, including\n   the original version of the Work and any modifications or additions\n   to that Work or Derivative Works thereof, that is intentionally\n   submitted to Licensor for inclusion in the Work by the copyright owner\n   or by an individual or Legal Entity authorized to submit on behalf of\n   the copyright owner. For the purposes of this definition, \"submitted\"\n   means any form of electronic, verbal, or written communication sent\n   to the Licensor or its representatives, including but not limited to\n   communication on electronic mailing lists, source code control systems,\n   and issue tracking systems that are managed by, or on behalf of, the\n   Licensor for the purpose of discussing and improving the Work, but\n   excluding communication that is conspicuously marked or otherwise\n   designated in writing by the copyright owner as \"Not a Contribution.\"\n\n   \"Contributor\" shall mean Licensor and any individual or Legal Entity\n   on behalf of whom a Contribution has been received by Licensor and\n   subsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   copyright license to reproduce, prepare Derivative Works of,\n   publicly display, publicly perform, sublicense, and distribute the\n   Work and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   (except as stated in this section) patent license to make, have made,\n   use, offer to sell, sell, import, and otherwise transfer the Work,\n   where such license applies only to those patent claims licensable\n   by such Contributor that are necessarily infringed by their\n   Contribution(s) alone or by combination of their Contribution(s)\n   with the Work to which such Contribution(s) was submitted. If You\n   institute patent litigation against any entity (including a\n   cross-claim or counterclaim in a lawsuit) alleging that the Work\n   or a Contribution incorporated within the Work constitutes direct\n   or contributory patent infringement, then any patent licenses\n   granted to You under this License for that Work shall terminate\n   as of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the\n   Work or Derivative Works thereof in any medium, with or without\n   modifications, and in Source or Object form, provided that You\n   meet the following conditions:\n\n   (a) You must give any other recipients of the Work or\n       Derivative Works a copy of this License; and\n\n   (b) You must cause any modified files to carry prominent notices\n       stating that You changed the files; and\n\n   (c) You must retain, in the Source form of any Derivative Works\n       that You distribute, all copyright, patent, trademark, and\n       attribution notices from the Source form of the Work,\n       excluding those notices that do not pertain to any part of\n       the Derivative Works; and\n\n   (d) If the Work includes a \"NOTICE\" text file as part of its\n       distribution, then any Derivative Works that You distribute must\n       include a readable copy of the attribution notices contained\n       within such NOTICE file, excluding those notices that do not\n       pertain to any part of the Derivative Works, in at least one\n       of the following places: within a NOTICE text file distributed\n       as part of the Derivative Works; within the Source form or\n       documentation, if provided along with the Derivative Works; or,\n       within a display generated by the Derivative Works, if and\n       wherever such third-party notices normally appear. The contents\n       of the NOTICE file are for informational purposes only and\n       do not modify the License. You may add Your own attribution\n       notices within Derivative Works that You distribute, alongside\n       or as an addendum to the NOTICE text from the Work, provided\n       that such additional attribution notices cannot be construed\n       as modifying the License.\n\n   You may add Your own copyright statement to Your modifications and\n   may provide additional or different license terms and conditions\n   for use, reproduction, or distribution of Your modifications, or\n   for any such Derivative Works as a whole, provided Your use,\n   reproduction, and distribution of the Work otherwise complies with\n   the conditions stated in this License.\n\n5. Submission of Contributions. Unless You explicitly state otherwise,\n   any Contribution intentionally submitted for inclusion in the Work\n   by You to the Licensor shall be under the terms and conditions of\n   this License, without any additional terms or conditions.\n   Notwithstanding the above, nothing herein shall supersede or modify\n   the terms of any separate license agreement you may have executed\n   with Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade\n   names, trademarks, service marks, or product names of the Licensor,\n   except as required for reasonable and customary use in describing the\n   origin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or\n   agreed to in writing, Licensor provides the Work (and each\n   Contributor provides its Contributions) on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n   implied, including, without limitation, any warranties or conditions\n   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n   PARTICULAR PURPOSE. You are solely responsible for determining the\n   appropriateness of using or redistributing the Work and assume any\n   risks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory,\n   whether in tort (including negligence), contract, or otherwise,\n   unless required by applicable law (such as deliberate and grossly\n   negligent acts) or agreed to in writing, shall any Contributor be\n   liable to You for damages, including any direct, indirect, special,\n   incidental, or consequential damages of any character arising as a\n   result of this License or out of the use or inability to use the\n   Work (including but not limited to damages for loss of goodwill,\n   work stoppage, computer failure or malfunction, or any and all\n   other commercial damages or losses), even if such Contributor\n   has been advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability. While redistributing\n   the Work or Derivative Works thereof, You may choose to offer,\n   and charge a fee for, acceptance of support, warranty, indemnity,\n   or other liability obligations and/or rights consistent with this\n   License. However, in accepting such obligations, You may act only\n   on Your own behalf and on Your sole responsibility, not on behalf\n   of any other Contributor, and only if You agree to indemnify,\n   defend, and hold each Contributor harmless for any liability\n   incurred by, or claims asserted against, such Contributor by reason\n   of your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work.\n\n   To apply the Apache License to your work, attach the following\n   boilerplate notice, with the fields enclosed by brackets \"[]\"\n   replaced with your own identifying information. (Don't include\n   the brackets!)  The text should be enclosed in the appropriate\n   comment syntax for the file format. We also recommend that a\n   file or class name and description of purpose be included on the\n   same \"printed page\" as the copyright notice for easier\n   identification within third-party archives.\n\nCopyright [yyyy] [name of copyright owner]\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n\thttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n"
  },
  {
    "path": "LICENSE-APACHE.stwo",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright 2024 StarkWare Industries Ltd.\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License."
  },
  {
    "path": "LICENSE-MIT.EF",
    "content": "MIT LICENSE\n\nCopyright 2022 Ethereum Foundation\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "LICENSE-MIT.arkworks-rs",
    "content": "The MIT License (MIT)\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "LICENSE-MIT.era-bellman-cuda",
    "content": "MIT License\n\nCopyright (c) 2022 Matter Labs\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "LICENSE-MIT.halo2",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2020-2022 The Electric Coin Company\nCopyright (c) 2022 The Halo 2 developers\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "LICENSE-MIT.plonky3",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2022 The Plonky3 Authors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "LICENSE-MIT.scroll",
    "content": "MIT License\n\nCopyright (c) 2022-2024 Scroll\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "LICENSE.chromium",
    "content": "// Copyright 2015 The Chromium Authors\n//\n// Redistribution and use in source and binary forms, with or without\n// modification, are permitted provided that the following conditions are\n// met:\n//\n//    * Redistributions of source code must retain the above copyright\n// notice, this list of conditions and the following disclaimer.\n//    * Redistributions in binary form must reproduce the above\n// copyright notice, this list of conditions and the following disclaimer\n// in the documentation and/or other materials provided with the\n// distribution.\n//    * Neither the name of Google LLC nor the names of its\n// contributors may be used to endorse or promote products derived from\n// this software without specific prior written permission.\n//\n// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n// \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "LICENSE.lambdaworks",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.****\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "LICENSE.tensorflow",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n\n## Some of TensorFlow's code is derived from Caffe, which is subject to the following copyright notice:\n\nCOPYRIGHT\n\nAll contributions by the University of California:\n\nCopyright (c) 2014, The Regents of the University of California (Regents)\nAll rights reserved.\n\nAll other contributions:\n\nCopyright (c) 2014, the respective contributors\nAll rights reserved.\n\nCaffe uses a shared copyright model: each contributor holds copyright over\ntheir contributions to Caffe. The project versioning records all such\ncontribution and copyright details. If a contributor wants to further mark\ntheir specific copyright on a particular contribution, they should indicate\ntheir copyright solely in the commit message of the change when it is\ncommitted.\n\nLICENSE\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this\n   list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright notice,\n   this list of conditions and the following disclaimer in the documentation\n   and/or other materials provided with the distribution.\n\n   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n   ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n   DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n   ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nCONTRIBUTION AGREEMENT\n\nBy contributing to the BVLC/caffe repository through pull-request, comment,\nor otherwise, the contributor releases their content to the\nlicense and copyright terms herein."
  },
  {
    "path": "LICENSE.ulvetanna",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright 2023 Ulvetanna Inc.\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "<div align=\"center\">\n  <br /><br /><br />\n  <img src=\"tachyon_logo_horizontal.png\" style=\"width: 50%;\">\n  <br /><br /><br />\n</div>\n\n## Overview\n\n**Tachyon** is a Modular ZK Backend, powered by GPU.\n\n## Join Our Developer Community\n\nWe're thrilled to have you become a part of our community!\n\nIf your passion for Tachyon matches ours, we extend a warm invitation for you to join our developer community on [Telegram](https://t.me/zktachyon). Ask questions, share insights, or simply stay informed on Tachyon's newest updates with other developers and the original team through our Telegram group. We hope to see you there!\n\nThank you for your keen interest in contributing to Tachyon! ✨\n\n## Design Goals\n\n1. General Purpose: A versatile ZK library empowers developers to implement any proving scheme with minimal effort, often enhancing developer productivity. To create a general-purpose backend, aligning the code structure as closely as possible with the algebraic structure is paramount.\n2. Easy to Use: Achieving widespread adoption is essential for the success of any product. Consequently, one of the key focal points of the Tachyon project is to include offering packages for various programming languages and runtimes.\n3. Blazing Fast: Tachyon's foremost requirement is speed, and not just any speed, but blazing speed! This entails Tachyon delivering exceptional performance on both CPU and GPU platforms.\n4. GPU Interoperability: Tachyon's code is designed to be compatible with both CPU and GPU in the majority of scenarios.\n\n## List of Features\n\nSymbol Definitions:\n\n- :heavy_check_mark: Currently supported.\n- 🏗️ Partially implemented or is under active construction.\n- :x: Not currently supported.\n\n### Finite Fields\n\n|              | CPU                | GPU                |\n| ------------ | ------------------ | ------------------ |\n| BabyBear     | :heavy_check_mark: | :heavy_check_mark: |\n| BinaryFields | :heavy_check_mark: | :heavy_check_mark: |\n| Goldilocks   | :heavy_check_mark: | :heavy_check_mark: |\n| Mersenne-31  | :heavy_check_mark: | :heavy_check_mark: |\n| KoalaBear    | :heavy_check_mark: | :heavy_check_mark: |\n\n### Elliptic Curves\n\n|           | CPU                | GPU                |\n| --------- | ------------------ | ------------------ |\n| BN254     | :heavy_check_mark: | :heavy_check_mark: |\n| BLS12-381 | :heavy_check_mark: | :heavy_check_mark: |\n| Secp256k1 | :heavy_check_mark: | :heavy_check_mark: |\n| Pallas    | :heavy_check_mark: | :heavy_check_mark: |\n| Vesta     | :heavy_check_mark: | :heavy_check_mark: |\n\n### Commitment Schemes\n\n|          | CPU                | GPU                |\n| -------- | ------------------ | ------------------ |\n| GWC      | :heavy_check_mark: | :heavy_check_mark: |\n| SHPlonk  | :heavy_check_mark: | :heavy_check_mark: |\n| FRI      | :heavy_check_mark: | :x:                |\n| Pedersen | :heavy_check_mark: | :heavy_check_mark: |\n\n### Hashes\n\n|           | CPU                | GPU |\n| --------- | ------------------ | --- |\n| Poseidon  | :heavy_check_mark: | :x: |\n| Poseidon2 | :heavy_check_mark: | :x: |\n\n### FFT\n\n|               | CPU                | GPU                |\n| ------------- | ------------------ | ------------------ |\n| Radix2FFT     | :heavy_check_mark: | :heavy_check_mark: |\n| MixedRadixFFT | :heavy_check_mark: | :heavy_check_mark: |\n\n### Lookups\n\n|       | CPU                | GPU |\n| ----- | ------------------ | --- |\n| Halo2 | :heavy_check_mark: | :x: |\n\n### SNARKs\n\n|         | CPU                | GPU                |\n| ------- | ------------------ | ------------------ |\n| Groth16 | :heavy_check_mark: | :heavy_check_mark: |\n| Halo2   | :heavy_check_mark: | :x:                |\n\n### Frontends\n\n|                 | CPU                | GPU                |\n| --------------- | ------------------ | ------------------ |\n| Circom(groth16) | :heavy_check_mark: | :heavy_check_mark: |\n| Halo2           | :heavy_check_mark: | :x:                |\n\n## Roadmap\n\n- [x] 2024Q1 - Enable producing the [zkEVM](https://github.com/kroma-network/zkevm-circuits) proof.\n- [x] 2024Q2 - Replace Halo2 with Tachyon in [Kroma](https://kroma.network/) mainnet.\n- [ ] 2024Q3a - Implement and optimize [Plonky3](https://github.com/Plonky3/Plonky3) in Tachyon.\n- [ ] 2024Q3b - Replace zkEVM with [SP1](https://github.com/succinctlabs/sp1) zkVM in [Kroma](https://kroma.network/) mainnet.\n\n## Prerequisites\n\n### Bazel\n\nPlease follow the instructions [here](https://bazel.build/install).\n\n### Ubuntu\n\n```shell\nsudo apt install libgmp-dev libomp-dev\n```\n\n### Macos\n\n```shell\nbrew install gmp libomp\n```\n\n## Getting started\n\n### Build\n\n```shell\nbazel build //...\n```\n\n### Test\n\n```shell\nbazel test //...\n```\n\nCheck [How To Build](/docs/how_to_use/how_to_build.md) for more information.\n"
  },
  {
    "path": "WORKSPACE",
    "content": "workspace(name = \"kroma_network_tachyon\")\n\nload(\"//bazel:tachyon_deps.bzl\", \"tachyon_deps\")\n\ntachyon_deps()\n\n# Start of buildifier\nload(\"//bazel:buildifier_deps.bzl\", \"buildifier_deps\")\n\nbuildifier_deps()\n\nload(\"@io_bazel_rules_go//go:deps.bzl\", \"go_register_toolchains\", \"go_rules_dependencies\")\n\ngo_rules_dependencies()\n\ngo_register_toolchains(version = \"1.20.3\")\n\nload(\"@bazel_gazelle//:deps.bzl\", \"gazelle_dependencies\")\n\ngazelle_dependencies()\n\nload(\"@com_google_protobuf//:protobuf_deps.bzl\", \"protobuf_deps\")\n\nprotobuf_deps()\n# End of buildifier\n\n# Start of rules_rust\nload(\"@rules_rust//rust:repositories.bzl\", \"rules_rust_dependencies\", \"rust_register_toolchains\")\n\nrules_rust_dependencies()\n\nrust_register_toolchains(\n    edition = \"2021\",\n    versions = [\"nightly/2024-04-17\"],\n)\n\nload(\"@rules_rust//crate_universe:repositories.bzl\", \"crate_universe_dependencies\")\n\ncrate_universe_dependencies()\n\nload(\"@rules_rust//crate_universe:defs.bzl\", \"crate\", \"crates_repository\")\n\ncrates_repository(\n    name = \"crate_index\",\n    cargo_lockfile = \"//:Cargo.lock\",\n    lockfile = \"//:Cargo.Bazel.lock\",\n    manifests = [\n        \"//:Cargo.toml\",\n        \"//benchmark/msm/arkworks:Cargo.toml\",\n        \"//benchmark/msm/bellman:Cargo.toml\",\n        \"//benchmark/msm/halo2:Cargo.toml\",\n        \"//benchmark/fft/arkworks:Cargo.toml\",\n        \"//benchmark/fft/bellman:Cargo.toml\",\n        \"//benchmark/fft/halo2:Cargo.toml\",\n        \"//benchmark/fft_batch/plonky3:Cargo.toml\",\n        \"//benchmark/fri/plonky3:Cargo.toml\",\n        \"//benchmark/poseidon/arkworks:Cargo.toml\",\n        \"//benchmark/poseidon2/horizen:Cargo.toml\",\n        \"//benchmark/poseidon2/plonky3:Cargo.toml\",\n        \"//tachyon/rs:Cargo.toml\",\n        \"//vendors/plonky3:Cargo.toml\",\n        \"//vendors/scroll_halo2:Cargo.toml\",\n        \"//vendors/sp1:Cargo.toml\",\n    ],\n    packages = {\n        \"hashbrown\": crate.spec(\n            features = [\"raw\"],\n            version = \"0.12.3\",\n        ),\n        \"deranged\": crate.spec(\n            features = [\n                \"powerfmt\",\n                \"std\",\n            ],\n            version = \"0.3.9\",\n        ),\n    },\n    rust_version = \"nightly/2024-04-17\",\n)\n\nload(\n    \"@crate_index//:defs.bzl\",\n    tachyon_crate_repositories = \"crate_repositories\",\n)\n\ntachyon_crate_repositories()\n# End of rules_rust\n\nload(\"@cxx.rs//third-party/bazel:defs.bzl\", cxx_create_repositories = \"crate_repositories\")\n\ncxx_create_repositories()\n\nload(\"//bazel:pybind11_deps.bzl\", \"pybind11_deps\")\n\npybind11_deps()\n\nload(\"@rules_pkg//:deps.bzl\", \"rules_pkg_dependencies\")\n\nrules_pkg_dependencies()\n\n# Start of rules_js\nload(\"//bazel:js_deps.bzl\", \"js_deps\")\n\njs_deps()\n\nload(\"@aspect_rules_js//js:repositories.bzl\", \"rules_js_dependencies\")\n\nrules_js_dependencies()\n\nload(\"@aspect_bazel_lib//lib:repositories.bzl\", \"aspect_bazel_lib_dependencies\")\n\naspect_bazel_lib_dependencies()\n\n# Fetch and register node, if you haven't already\nload(\"@rules_nodejs//nodejs:repositories.bzl\", \"DEFAULT_NODE_VERSION\", \"nodejs_register_toolchains\")\n\nnodejs_register_toolchains(\n    name = \"nodejs\",\n    node_version = DEFAULT_NODE_VERSION,\n)\n\nload(\"@aspect_rules_js//npm:repositories.bzl\", \"npm_translate_lock\")\n\nnpm_translate_lock(\n    name = \"npm\",\n    data = [\"@iden3_ffiasm//:package.json\"],\n    npm_package_lock = \"@iden3_ffiasm//:package-lock.json\",\n    pnpm_lock = \"@iden3_ffiasm//:pnpm-lock.yaml\",\n    update_pnpm_lock = True,\n    verify_node_modules_ignored = \"@iden3_ffiasm//:.bazelignore\",\n)\n\nload(\"@npm//:repositories.bzl\", \"npm_repositories\")\n\nnpm_repositories()\n\n# End of rules_js\n"
  },
  {
    "path": "bazel/BUILD.bazel",
    "content": ""
  },
  {
    "path": "bazel/buildifier_deps.bzl",
    "content": "load(\"@bazel_tools//tools/build_defs/repo:http.bzl\", \"http_archive\")\n\n# See https://github.com/bazelbuild/buildtools/blob/master/buildifier/README.md\ndef buildifier_deps():\n    if not native.existing_rule(\"bazel_gazelle\"):\n        http_archive(\n            name = \"bazel_gazelle\",\n            sha256 = \"727f3e4edd96ea20c29e8c2ca9e8d2af724d8c7778e7923a854b2c80952bc405\",\n            urls = [\n                \"https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/v0.30.0/bazel-gazelle-v0.30.0.tar.gz\",\n                \"https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.30.0/bazel-gazelle-v0.30.0.tar.gz\",\n            ],\n        )\n\n    if not native.existing_rule(\"com_github_bazelbuild_buildtools\"):\n        http_archive(\n            name = \"com_github_bazelbuild_buildtools\",\n            sha256 = \"05c3c3602d25aeda1e9dbc91d3b66e624c1f9fdadf273e5480b489e744ca7269\",\n            strip_prefix = \"buildtools-6.4.0\",\n            urls = [\n                \"https://github.com/bazelbuild/buildtools/archive/refs/tags/v6.4.0.tar.gz\",\n            ],\n        )\n\n    if not native.existing_rule(\"com_google_protobuf\"):\n        http_archive(\n            name = \"com_google_protobuf\",\n            sha256 = \"3bd7828aa5af4b13b99c191e8b1e884ebfa9ad371b0ce264605d347f135d2568\",\n            strip_prefix = \"protobuf-3.19.4\",\n            urls = [\n                \"https://github.com/protocolbuffers/protobuf/archive/v3.19.4.tar.gz\",\n            ],\n        )\n\n    if not native.existing_rule(\"io_bazel_rules_go\"):\n        http_archive(\n            name = \"io_bazel_rules_go\",\n            sha256 = \"6dc2da7ab4cf5d7bfc7c949776b1b7c733f05e56edc4bcd9022bb249d2e2a996\",\n            urls = [\n                \"https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.39.1/rules_go-v0.39.1.zip\",\n                \"https://github.com/bazelbuild/rules_go/releases/download/v0.39.1/rules_go-v0.39.1.zip\",\n            ],\n        )\n"
  },
  {
    "path": "bazel/js_deps.bzl",
    "content": "load(\"@bazel_tools//tools/build_defs/repo:http.bzl\", \"http_archive\")\n\ndef js_deps():\n    if not native.existing_rule(\"aspect_bazel_lib\"):\n        http_archive(\n            name = \"aspect_bazel_lib\",\n            sha256 = \"262e3d6693cdc16dd43880785cdae13c64e6a3f63f75b1993c716295093d117f\",\n            strip_prefix = \"bazel-lib-1.38.1\",\n            url = \"https://github.com/aspect-build/bazel-lib/releases/download/v1.38.1/bazel-lib-v1.38.1.tar.gz\",\n        )\n\n    if not native.existing_rule(\"aspect_rules_js\"):\n        http_archive(\n            name = \"aspect_rules_js\",\n            sha256 = \"d9ceb89e97bb5ad53b278148e01a77a3e9100db272ce4ebdcd59889d26b9076e\",\n            strip_prefix = \"rules_js-1.34.0\",\n            url = \"https://github.com/aspect-build/rules_js/releases/download/v1.34.0/rules_js-v1.34.0.tar.gz\",\n        )\n\n    if not native.existing_rule(\"rules_nodejs\"):\n        http_archive(\n            name = \"rules_nodejs\",\n            sha256 = \"a50986c7d2f2dc43a5b9b81a6245fd89bdc4866f1d5e316d9cef2782dd859292\",\n            strip_prefix = \"rules_nodejs-6.0.5\",\n            url = \"https://github.com/bazelbuild/rules_nodejs/releases/download/v6.0.5/rules_nodejs-v6.0.5.tar.gz\",\n        )\n"
  },
  {
    "path": "bazel/pybind11_deps.bzl",
    "content": "load(\"@bazel_tools//tools/build_defs/repo:http.bzl\", \"http_archive\")\n\ndef pybind11_deps():\n    if not native.existing_rule(\"pybind11_bazel\"):\n        http_archive(\n            name = \"pybind11_bazel\",\n            strip_prefix = \"pybind11_bazel-b162c7c88a253e3f6b673df0c621aca27596ce6b\",\n            urls = [\"https://github.com/pybind/pybind11_bazel/archive/b162c7c88a253e3f6b673df0c621aca27596ce6b.zip\"],\n        )\n\n    if not native.existing_rule(\"pybind11\"):\n        http_archive(\n            name = \"pybind11\",\n            strip_prefix = \"pybind11-2.11.1\",\n            urls = [\"https://github.com/pybind/pybind11/archive/v2.11.1.tar.gz\"],\n            build_file = \"@pybind11_bazel//:pybind11.BUILD\",\n        )\n"
  },
  {
    "path": "bazel/tachyon.bzl",
    "content": "load(\"@local_config_cuda//cuda:build_defs.bzl\", \"if_cuda_is_configured\")\nload(\"@local_config_rocm//rocm:build_defs.bzl\", \"if_rocm_is_configured\")\n\n# See https://semver.org/\nVERSION_MAJOR = 0\nVERSION_MINOR = 4\nVERSION_PATCH = 0\nVERSION_PRERELEASE = \"\"\nVERSION = \".\".join([str(VERSION_MAJOR), str(VERSION_MINOR), str(VERSION_PATCH)])\n\ndef if_x86_32(a, b = []):\n    return select({\n        \"@platforms//cpu:x86_32\": a,\n        \"//conditions:default\": b,\n    })\n\ndef if_x86_64(a, b = []):\n    return select({\n        \"@platforms//cpu:x86_64\": a,\n        \"//conditions:default\": b,\n    })\n\ndef if_arm(a, b = []):\n    return select({\n        \"@platforms//cpu:arm\": a,\n        \"//conditions:default\": b,\n    })\n\ndef if_aarch64(a, b = []):\n    return select({\n        \"@platforms//cpu:aarch64\": a,\n        \"//conditions:default\": b,\n    })\n\ndef if_linux(a, b = []):\n    return select({\n        \"@platforms//os:linux\": a,\n        \"//conditions:default\": b,\n    })\n\ndef if_linux_x86_64(a, b = []):\n    return select({\n        \"@kroma_network_tachyon//:linux_x86_64\": a,\n        \"//conditions:default\": b,\n    })\n\ndef if_macos(a, b = []):\n    return select({\n        \"@platforms//os:macos\": a,\n        \"//conditions:default\": b,\n    })\n\ndef if_windows(a, b = []):\n    return select({\n        \"@platforms//os:windows\": a,\n        \"//conditions:default\": b,\n    })\n\ndef if_posix(a, b = []):\n    return select({\n        \"@platforms//os:windows\": b,\n        \"//conditions:default\": a,\n    })\n\ndef if_optimized(a, b = []):\n    return select({\n        \"@kroma_network_tachyon//:optimized\": a,\n        \"//conditions:default\": b,\n    })\n\ndef if_static(a, b = []):\n    return select({\n        \"@kroma_network_tachyon//:tachyon_shared_object\": b,\n        \"@kroma_network_tachyon//:tachyon_c_shared_object\": b,\n        \"@kroma_network_tachyon//:tachyon_cc_shared_object\": b,\n        \"//conditions:default\": a,\n    })\n\ndef if_has_exception(a, b = []):\n    return select({\n        \"@kroma_network_tachyon//:tachyon_has_exception\": a,\n        \"//conditions:default\": b,\n    })\n\ndef if_has_rtti(a, b = []):\n    return select({\n        \"@kroma_network_tachyon//:tachyon_has_rtti\": a,\n        \"//conditions:default\": b,\n    })\n\ndef if_has_openmp(a, b = []):\n    return select({\n        \"@kroma_network_tachyon//:tachyon_has_openmp\": a,\n        \"//conditions:default\": b,\n    })\n\ndef if_has_openmp_on_macos(a, b = []):\n    return select({\n        \"@kroma_network_tachyon//:tachyon_has_openmp_on_macos\": a,\n        \"//conditions:default\": b,\n    })\n\ndef if_has_avx512(a, b = []):\n    return select({\n        \"@kroma_network_tachyon//:tachyon_has_avx512\": a,\n        \"//conditions:default\": b,\n    })\n\ndef if_has_matplotlib(a, b = []):\n    return select({\n        \"@kroma_network_tachyon//:tachyon_has_matplotlib\": a,\n        \"//conditions:default\": b,\n    })\n\ndef if_has_numa(a, b = []):\n    return select({\n        \"@kroma_network_tachyon//:tachyon_has_numa\": a,\n        \"//conditions:default\": b,\n    })\n\ndef if_has_asm_prime_field(a, b = []):\n    return select({\n        \"@kroma_network_tachyon//:tachyon_has_asm_prime_field\": a,\n        \"//conditions:default\": b,\n    })\n\ndef if_c_shared_object(a, b = []):\n    return select({\n        \"@kroma_network_tachyon//:tachyon_c_shared_object\": a,\n        \"//conditions:default\": b,\n    })\n\ndef if_cc_shared_object(a, b = []):\n    return select({\n        \"@kroma_network_tachyon//:tachyon_cc_shared_object\": a,\n        \"//conditions:default\": b,\n    })\n\ndef if_py_binding(a, b = []):\n    return select({\n        \"@kroma_network_tachyon//:tachyon_py_binding\": a,\n        \"//conditions:default\": b,\n    })\n\ndef if_node_binding(a, b = []):\n    return select({\n        \"@kroma_network_tachyon//:tachyon_node_binding\": a,\n        \"//conditions:default\": b,\n    })\n\ndef if_gpu_is_configured(x):\n    return if_cuda_is_configured(x) + if_rocm_is_configured(x)\n"
  },
  {
    "path": "bazel/tachyon_cc.bzl",
    "content": "load(\"@local_config_cuda//cuda:build_defs.bzl\", \"cuda_library\", \"if_cuda\")\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\", \"cc_library\", \"cc_test\", \"objc_library\")\nload(\n    \"//bazel:tachyon.bzl\",\n    \"if_has_asm_prime_field\",\n    \"if_has_avx512\",\n    \"if_has_exception\",\n    \"if_has_matplotlib\",\n    \"if_has_openmp\",\n    \"if_has_rtti\",\n    \"if_linux_x86_64\",\n    \"if_static\",\n)\n\ndef tachyon_safe_code():\n    return [\"-Wall\", \"-Werror\"]\n\ndef tachyon_warnings(safe_code):\n    warnings = []\n    if safe_code:\n        warnings.extend(tachyon_safe_code())\n    return warnings\n\ndef tachyon_hide_symbols():\n    return if_static([], [\"-fvisibility=hidden\"])\n\ndef tachyon_exceptions(force_exceptions):\n    return if_has_exception([\"-fexceptions\"], ([\"-fexceptions\"] if force_exceptions else [\"-fno-exceptions\"]))\n\ndef tachyon_rtti(force_rtti):\n    return if_has_rtti([\"-frtti\"], ([\"-frtti\"] if force_rtti else [\"-fno-rtti\"]))\n\ndef tachyon_simd_copts():\n    return if_linux_x86_64([\"-msse3\"])\n\ndef tachyon_openmp_copts():\n    return select({\n        \"@kroma_network_tachyon//:tachyon_has_openmp_on_macos\": [\"-Xclang -fopenmp\"],\n        \"@kroma_network_tachyon//:tachyon_has_openmp\": [\"-fopenmp\"],\n        \"//conditions:default\": [],\n    })\n\ndef tachyon_copts(safe_code = True):\n    return tachyon_warnings(safe_code) + tachyon_hide_symbols() + tachyon_simd_copts() + tachyon_openmp_copts()\n\ndef tachyon_cxxopts(safe_code = True, force_exceptions = False, force_rtti = False):\n    return tachyon_copts(safe_code) + tachyon_exceptions(force_exceptions) + tachyon_rtti(force_rtti)\n\ndef tachyon_openmp_defines():\n    return if_has_openmp([\"TACHYON_HAS_OPENMP\"])\n\ndef tachyon_avx512_defines():\n    return if_has_avx512([\"TACHYON_HAS_AVX512\"])\n\ndef tachyon_asm_prime_field_defines():\n    return if_has_asm_prime_field([\"TACHYON_HAS_ASM_PRIME_FIELD\"])\n\ndef tachyon_cuda_defines():\n    return if_cuda([\"TACHYON_CUDA\"])\n\ndef tachyon_matplotlib_defines():\n    return if_has_matplotlib([\"TACHYON_HAS_MATPLOTLIB\"])\n\ndef tachyon_defines(use_cuda = False):\n    defines = tachyon_defines_shared_lib_build() + tachyon_openmp_defines()\n    if use_cuda:\n        defines += tachyon_cuda_defines()\n    return defines\n\ndef tachyon_defines_shared_lib_build():\n    return select({\n        \"@kroma_network_tachyon//:tachyon_shared_object\": [\"TACHYON_COMPONENT_BUILD\"],\n        \"@kroma_network_tachyon//:tachyon_c_shared_object\": [\"TACHYON_C_SHARED_LIB_BUILD\"],\n        \"@kroma_network_tachyon//:tachyon_cc_shared_object\": [\"TACHYON_CC_SHARED_LIB_BUILD\"],\n        \"//conditions:default\": [],\n    })\n\ndef tachyon_local_defines():\n    return tachyon_local_defines_compile_library()\n\ndef tachyon_local_defines_compile_library():\n    return if_static([], [\"TACHYON_COMPILE_LIBRARY\"])\n\ndef tachyon_openmp_linkopts():\n    return select({\n        \"@kroma_network_tachyon//:tachyon_has_openmp_on_macos\": [\"-Xclang -fopenmp\"],\n        \"@kroma_network_tachyon//:tachyon_has_openmp\": [\"-fopenmp\"],\n        \"@kroma_network_tachyon//:tachyon_has_intel_openmp\": [\"-liomp5\"],\n        \"//conditions:default\": [],\n    })\n\ndef tachyon_linkopts():\n    return tachyon_openmp_linkopts()\n\ndef tachyon_openmp_num_threads_env(n):\n    return if_has_openmp({\n        \"OMP_NUM_THREADS\": \"{}\".format(n),\n    }, {})\n\ndef tachyon_cc_library(\n        name,\n        copts = [],\n        defines = [],\n        local_defines = [],\n        linkopts = [],\n        alwayslink = True,\n        safe_code = True,\n        force_exceptions = False,\n        force_rtti = False,\n        **kwargs):\n    cc_library(\n        name = name,\n        copts = copts + tachyon_cxxopts(safe_code = safe_code, force_exceptions = force_exceptions, force_rtti = force_rtti),\n        defines = defines + tachyon_defines(),\n        local_defines = local_defines + tachyon_local_defines(),\n        linkopts = linkopts + tachyon_linkopts(),\n        alwayslink = alwayslink,\n        **kwargs\n    )\n\ndef tachyon_cc_binary(\n        name,\n        copts = [],\n        defines = [],\n        local_defines = [],\n        linkopts = [],\n        safe_code = True,\n        force_exceptions = False,\n        force_rtti = False,\n        **kwargs):\n    cc_binary(\n        name = name,\n        copts = copts + tachyon_cxxopts(safe_code = safe_code, force_exceptions = force_exceptions, force_rtti = force_rtti),\n        defines = defines + tachyon_defines(),\n        local_defines = local_defines + tachyon_local_defines(),\n        linkopts = linkopts + tachyon_linkopts(),\n        **kwargs\n    )\n\ndef tachyon_cc_test(\n        name,\n        copts = [],\n        defines = [],\n        local_defines = [],\n        linkopts = [],\n        linkstatic = True,\n        deps = [],\n        safe_code = True,\n        force_exceptions = False,\n        force_rtti = False,\n        **kwargs):\n    cc_test(\n        name = name,\n        copts = copts + tachyon_cxxopts(safe_code = safe_code, force_exceptions = force_exceptions, force_rtti = force_rtti),\n        defines = defines + tachyon_defines(),\n        local_defines = local_defines + tachyon_local_defines(),\n        linkopts = linkopts + tachyon_linkopts(),\n        linkstatic = linkstatic,\n        deps = deps + [\"@com_google_googletest//:gtest_main\"],\n        **kwargs\n    )\n\ndef tachyon_cc_unittest(\n        name,\n        size = \"small\",\n        **kwargs):\n    tachyon_cc_test(\n        name = name,\n        size = size,\n        **kwargs\n    )\n\ndef tachyon_cc_benchmark(\n        name,\n        copts = [],\n        defines = [],\n        local_defines = [],\n        linkopts = [],\n        tags = [],\n        linkstatic = True,\n        deps = [],\n        safe_code = True,\n        force_exceptions = False,\n        force_rtti = False,\n        **kwargs):\n    cc_test(\n        name = name,\n        copts = copts + tachyon_cxxopts(safe_code = safe_code, force_exceptions = force_exceptions, force_rtti = force_rtti),\n        defines = defines + tachyon_defines(),\n        local_defines = local_defines + tachyon_local_defines(),\n        linkopts = linkopts + tachyon_linkopts(),\n        tags = tags + [\"benchmark\"],\n        deps = deps + [\"@com_github_google_benchmark//:benchmark_main\"],\n        linkstatic = linkstatic,\n        **kwargs\n    )\n\n# This is taken and modified from tf_cc_shared_object in tensorflow/tensorflow.bzl\ndef tachyon_cc_shared_library(\n        name,\n        user_link_flags = [],\n        deps = [],\n        linkstatic = False,\n        soversion = None,\n        testonly = False,\n        visibility = [\"//visibility:public\"],\n        **kwargs):\n    cc_library_name = name + \"_cclib\"\n    cc_library(\n        name = cc_library_name,\n        linkstatic = linkstatic,\n        testonly = testonly,\n        deps = deps,\n    )\n\n    if soversion != None:\n        major_version = soversion.split(\".\")[0]\n\n        for os in [\"macos\", \"linux\"]:\n            native.genrule(\n                name = \"%s_sym_%s\" % (name, os),\n                outs = [(\"lib%s.dylib\" if os == \"macos\" else \"lib%s.so\") % name],\n                srcs = [\":%s_%s_sym_%s\" % (name, major_version, os)],\n                output_to_bindir = True,\n                cmd = \"ln -sf $$(basename $<) $@\",\n            )\n\n            native.genrule(\n                name = \"%s_%s_sym_%s\" % (name, major_version, os),\n                outs = [(\"lib%s.%s.dylib\" if os == \"macos\" else \"lib%s.so.%s\") % (name, major_version)],\n                srcs = [\":\" + name],\n                output_to_bindir = True,\n                cmd = \"ln -sf $$(basename $<) $@\",\n            )\n\n        native.cc_shared_library(\n            name = name,\n            shared_lib_name = select({\n                \"@platforms//os:macos\": \"lib%s.%s.dylib\" % (name, soversion),\n                \"@platforms//os:windows\": \"%s.dll\" % name,\n                \"//conditions:default\": \"lib%s.so.%s\" % (name, soversion),\n            }),\n            user_link_flags = user_link_flags + select({\n                \"@platforms//os:macos\": [\n                    \"-Wl,-install_name,@rpath/\" + \"lib%s.%s.dylib\" % (name, major_version),\n                ],\n                \"@platforms//os:windows\": [],\n                \"//conditions:default\": [\n                    \"-Wl,-soname,\" + \"lib%s.so.%s\" % (name, major_version),\n                ],\n            }),\n            visibility = visibility,\n            deps = [\":\" + cc_library_name],\n            testonly = testonly,\n            **kwargs\n        )\n    else:\n        native.cc_shared_library(\n            name = name,\n            user_link_flags = user_link_flags,\n            visibility = visibility,\n            deps = [\":\" + cc_library_name],\n            testonly = testonly,\n            **kwargs\n        )\n\ndef tachyon_objc_library(\n        name,\n        copts = [],\n        defines = [],\n        tags = [],\n        alwayslink = True,\n        safe_code = True,\n        force_exceptions = False,\n        force_rtti = False,\n        **kwargs):\n    objc_library(\n        name = name,\n        copts = copts + tachyon_cxxopts(safe_code = safe_code, force_exceptions = force_exceptions, force_rtti = force_rtti),\n        defines = defines + tachyon_defines(),\n        tags = tags + [\"objc\"],\n        alwayslink = alwayslink,\n        **kwargs\n    )\n\ndef tachyon_cuda_library(\n        name,\n        copts = [],\n        defines = [],\n        local_defines = [],\n        linkopts = [],\n        alwayslink = True,\n        safe_code = True,\n        force_exceptions = False,\n        force_rtti = False,\n        **kwargs):\n    cuda_library(\n        name = name,\n        copts = copts + tachyon_cxxopts(safe_code = safe_code, force_exceptions = force_exceptions, force_rtti = force_rtti),\n        defines = defines + tachyon_defines(use_cuda = True),\n        local_defines = local_defines + tachyon_local_defines(),\n        linkopts = linkopts + tachyon_linkopts(),\n        alwayslink = alwayslink,\n        **kwargs\n    )\n\ndef tachyon_cuda_binary(\n        name,\n        deps = [],\n        testonly = False,\n        **kwargs):\n    lib_name = \"{}_lib\".format(name)\n    tachyon_cuda_library(\n        name = lib_name,\n        deps = deps + if_cuda([\n            \"@local_config_cuda//cuda:cudart_static\",\n        ]),\n        testonly = testonly,\n        **kwargs\n    )\n\n    tachyon_cc_binary(\n        name = name,\n        deps = [\":\" + lib_name],\n        testonly = testonly,\n    )\n\ndef tachyon_cuda_test(\n        name,\n        deps = [],\n        tags = [],\n        size = \"medium\",\n        **kwargs):\n    lib_name = \"{}_lib\".format(name)\n    tachyon_cuda_library(\n        name = lib_name,\n        deps = deps + if_cuda([\n            \"@local_config_cuda//cuda:cudart_static\",\n        ]) + [\n            \"@com_google_googletest//:gtest\",\n        ],\n        testonly = True,\n        **kwargs\n    )\n\n    tachyon_cc_test(\n        name = name,\n        tags = tags + [\"cuda\"],\n        size = size,\n        deps = [\":\" + lib_name],\n    )\n\ndef tachyon_cuda_unittest(\n        name,\n        size = \"small\",\n        **kwargs):\n    tachyon_cuda_test(\n        name = name,\n        size = size,\n        **kwargs\n    )\n\ndef _get_hdrs(hdrs, deps):\n    return depset(\n        hdrs,\n        transitive = [\n                         dep[CcInfo].compilation_context.headers\n                         for dep in deps\n                         if CcInfo in dep\n                     ] +\n                     [\n                         dep[DefaultInfo].files\n                         for dep in deps\n                         if DefaultInfo in dep\n                     ],\n    )\n\ndef _collect_hdrs_impl(ctx):\n    result = _get_hdrs(ctx.files.hdrs, ctx.attr.deps)\n    return DefaultInfo(files = result)\n\ncollect_hdrs = rule(\n    attrs = {\n        \"hdrs\": attr.label_list(allow_files = [\".h\"]),\n        \"deps\": attr.label_list(\n            allow_rules = [\n                \"cc_library\",\n                \"filegroup\",\n            ],\n            providers = [[CcInfo], [DefaultInfo]],\n        ),\n    },\n    implementation = _collect_hdrs_impl,\n)\n"
  },
  {
    "path": "bazel/tachyon_deps.bzl",
    "content": "load(\"@bazel_tools//tools/build_defs/repo:http.bzl\", \"http_archive\")\nload(\"//third_party/circomlib:workspace.bzl\", circomlib = \"repo\")\nload(\"//third_party/doxygen:workspace.bzl\", doxygen = \"repo\")\nload(\"//third_party/eigen3:workspace.bzl\", eigen3 = \"repo\")\nload(\"//third_party/env:env_configure.bzl\", \"env_configure\")\nload(\"//third_party/ffiasm:workspace.bzl\", ffiasm = \"repo\")\nload(\"//third_party/gmp:gmp_configure.bzl\", \"gmp_configure\")\nload(\"//third_party/goldilocks:workspace.bzl\", goldilocks = \"repo\")\nload(\"//third_party/gpus:cuda_configure.bzl\", \"cuda_configure\")\nload(\"//third_party/gpus:rocm_configure.bzl\", \"rocm_configure\")\nload(\"//third_party/hwloc:workspace.bzl\", hwloc = \"repo\")\nload(\"//third_party/icicle:workspace.bzl\", icicle = \"repo\")\nload(\"//third_party/json:workspace.bzl\", json = \"repo\")\nload(\"//third_party/nasm:workspace.bzl\", nasm = \"repo\")\nload(\"//third_party/node_addon_api:install_node_addon_api.bzl\", \"install_node_addon_api\")\nload(\"//third_party/omp:omp_configure.bzl\", \"omp_configure\")\nload(\"//third_party/pdqsort:workspace.bzl\", pdqsort = \"repo\")\nload(\"//third_party/powersort:workspace.bzl\", powersort = \"repo\")\nload(\"//third_party/py:python_configure.bzl\", \"python_configure\")\nload(\"//third_party/rapidsnark:workspace.bzl\", rapidsnark = \"repo\")\n\ndef tachyon_deps():\n    cuda_configure(name = \"local_config_cuda\")\n    env_configure(name = \"local_config_env\")\n    gmp_configure(name = \"local_config_gmp\")\n    omp_configure(name = \"local_config_omp\")\n    rocm_configure(name = \"local_config_rocm\")\n    python_configure(name = \"local_config_python\")\n\n    circomlib()\n    doxygen()\n    eigen3()\n    ffiasm()\n    goldilocks()\n    hwloc()\n    icicle()\n    json()\n    nasm()\n    pdqsort()\n    powersort()\n    rapidsnark()\n\n    install_node_addon_api(name = \"node_addon_api\")\n\n    if not native.existing_rule(\"bazel_skylib\"):\n        http_archive(\n            name = \"bazel_skylib\",\n            sha256 = \"66ffd9315665bfaafc96b52278f57c7e2dd09f5ede279ea6d39b2be471e7e3aa\",\n            urls = [\n                \"https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.4.2/bazel-skylib-1.4.2.tar.gz\",\n                \"https://github.com/bazelbuild/bazel-skylib/releases/download/1.4.2/bazel-skylib-1.4.2.tar.gz\",\n            ],\n        )\n\n    if not native.existing_rule(\"com_github_google_benchmark\"):\n        http_archive(\n            name = \"com_github_google_benchmark\",\n            sha256 = \"2aab2980d0376137f969d92848fbb68216abb07633034534fc8c65cc4e7a0e93\",\n            strip_prefix = \"benchmark-1.8.2\",\n            urls = [\"https://github.com/google/benchmark/archive/v1.8.2.tar.gz\"],\n        )\n\n    # Needed by com_github_google_glog\n    if not native.existing_rule(\"com_github_gflags_gflags\"):\n        http_archive(\n            name = \"com_github_gflags_gflags\",\n            strip_prefix = \"gflags-2.2.2\",\n            urls = [\n                \"https://mirror.bazel.build/github.com/gflags/gflags/archive/v2.2.2.tar.gz\",\n                \"https://github.com/gflags/gflags/archive/v2.2.2.tar.gz\",\n            ],\n        )\n\n    if not native.existing_rule(\"com_github_google_glog\"):\n        # TODO(chokobole): Bump up to 0.6.0.\n        # glog v0.6.0 can't be built with --config cuda.\n        http_archive(\n            name = \"com_github_google_glog\",\n            sha256 = \"21bc744fb7f2fa701ee8db339ded7dce4f975d0d55837a97be7d46e8382dea5a\",\n            strip_prefix = \"glog-0.5.0\",\n            urls = [\"https://github.com/google/glog/archive/v0.5.0.zip\"],\n            patch_args = [\"-p1\"],\n            patches = [\n                \"@kroma_network_tachyon//third_party/glog:enable_constexpr_check_op.patch\",\n                \"@kroma_network_tachyon//third_party/glog:enable_elaborate_ostream_operator.patch\",\n            ],\n        )\n\n    if not native.existing_rule(\"com_github_soblin_matplotlibcpp17\"):\n        http_archive(\n            name = \"com_github_soblin_matplotlibcpp17\",\n            sha256 = \"f5889241b57c6a06e9f07745c2e4062bb2c584a67756aad3dd042a65147cc830\",\n            strip_prefix = \"matplotlibcpp17-779f108f192a27761cae3724929850fe65de625c\",\n            urls = [\"https://github.com/soblin/matplotlibcpp17/archive/779f108f192a27761cae3724929850fe65de625c.tar.gz\"],\n            build_file = \"@kroma_network_tachyon//third_party/matplotlibcpp17:matplotlibcpp17.BUILD\",\n        )\n\n    if not native.existing_rule(\"com_google_absl\"):\n        http_archive(\n            name = \"com_google_absl\",\n            sha256 = \"51d676b6846440210da48899e4df618a357e6e44ecde7106f1e44ea16ae8adc7\",\n            strip_prefix = \"abseil-cpp-20230125.3\",\n            urls = [\"https://github.com/abseil/abseil-cpp/archive/20230125.3.zip\"],\n            patch_args = [\"-p1\"],\n            patches = [\n                \"@kroma_network_tachyon//third_party/absl:add_missing_linkopts.patch\",\n                \"@kroma_network_tachyon//third_party/absl:ignore-maybe-uninitialized-warning.patch\",\n            ],\n        )\n\n    if not native.existing_rule(\"com_google_boringssl\"):\n        http_archive(\n            name = \"com_google_boringssl\",\n            sha256 = \"c17af0c3ddd72613a6cacf3761cebaa583806fc49fb53ac021ae479aa8265c93\",\n            strip_prefix = \"boringssl-b69f4d27a75dcf4b94138790883b44274fab56c2\",\n            urls = [\"https://github.com/google/boringssl/archive/b69f4d27a75dcf4b94138790883b44274fab56c2.tar.gz\"],\n            patch_args = [\"-p1\"],\n            patches = [\n                \"@kroma_network_tachyon//third_party/boringssl:blake2b512.patch\",\n                \"@kroma_network_tachyon//third_party/boringssl:chacha_core.patch\",\n            ],\n        )\n\n    if not native.existing_rule(\"com_google_googletest\"):\n        http_archive(\n            name = \"com_google_googletest\",\n            sha256 = \"ffa17fbc5953900994e2deec164bb8949879ea09b411e07f215bfbb1f87f4632\",\n            strip_prefix = \"googletest-1.13.0\",\n            urls = [\"https://github.com/google/googletest/archive/v1.13.0.zip\"],\n            patch_args = [\"-p1\"],\n            patches = [\n                \"@kroma_network_tachyon//third_party/gtest:add_missing_linkopts.patch\",\n                \"@kroma_network_tachyon//third_party/gtest:print_with_to_string.patch\",\n            ],\n        )\n\n    if not native.existing_rule(\"com_github_tencent_rapidjson\"):\n        http_archive(\n            name = \"com_github_tencent_rapidjson\",\n            strip_prefix = \"rapidjson-1.1.0\",\n            sha256 = \"8e00c38829d6785a2dfb951bb87c6974fa07dfe488aa5b25deec4b8bc0f6a3ab\",\n            urls = [\"https://github.com/Tencent/rapidjson/archive/v1.1.0.zip\"],\n            build_file = \"@kroma_network_tachyon//third_party:rapidjson/rapidjson.BUILD\",\n        )\n\n    # Needed by com_google_googletest\n    if not native.existing_rule(\"com_googlesource_code_re2\"):\n        http_archive(\n            name = \"com_googlesource_code_re2\",\n            sha256 = \"4ccdd5aafaa1bcc24181e6dd3581c3eee0354734bb9f3cb4306273ffa434b94f\",\n            strip_prefix = \"re2-2023-06-02\",\n            urls = [\"https://github.com/google/re2/archive/2023-06-02.tar.gz\"],\n        )\n\n    if not native.existing_rule(\"cxx.rs\"):\n        http_archive(\n            name = \"cxx.rs\",\n            sha256 = \"ab918028d85b1c0a5c21a24d9a78bb6a1adc78608191e6ff382bcbb2bcd57ccd\",\n            strip_prefix = \"cxx-1.0.102\",\n            urls = [\"https://github.com/dtolnay/cxx/archive/refs/tags/1.0.102.tar.gz\"],\n            patch_args = [\"-p1\"],\n            patches = [\"@kroma_network_tachyon//third_party/cxx_rs:add_more_args_to_cxx_bridge.patch\"],\n        )\n\n    if not native.existing_rule(\"perfetto\"):\n        http_archive(\n            name = \"perfetto\",\n            sha256 = \"dfc9b645c020d7a7469bae73d7432545b8005411c8176f46f04875058df0aa97\",\n            strip_prefix = \"perfetto-46.0\",\n            urls = [\"https://github.com/google/perfetto/archive/refs/tags/v46.0.tar.gz\"],\n            build_file = \"@kroma_network_tachyon//third_party:perfetto/perfetto.BUILD\",\n        )\n\n    if not native.existing_rule(\"rules_pkg\"):\n        http_archive(\n            name = \"rules_pkg\",\n            sha256 = \"8f9ee2dc10c1ae514ee599a8b42ed99fa262b757058f65ad3c384289ff70c4b8\",\n            urls = [\"https://github.com/bazelbuild/rules_pkg/releases/download/0.9.1/rules_pkg-0.9.1.tar.gz\"],\n        )\n\n    # Needed by com_google_googletest\n    if not native.existing_rule(\"rules_python\"):\n        http_archive(\n            name = \"rules_python\",\n            sha256 = \"84aec9e21cc56fbc7f1335035a71c850d1b9b5cc6ff497306f84cced9a769841\",\n            strip_prefix = \"rules_python-0.23.1\",\n            urls = [\"https://github.com/bazelbuild/rules_python/releases/download/0.23.1/rules_python-0.23.1.tar.gz\"],\n        )\n\n    if not native.existing_rule(\"rules_rust\"):\n        http_archive(\n            name = \"rules_rust\",\n            sha256 = \"9d04e658878d23f4b00163a72da3db03ddb451273eb347df7d7c50838d698f49\",\n            urls = [\"https://github.com/bazelbuild/rules_rust/releases/download/0.26.0/rules_rust-v0.26.0.tar.gz\"],\n        )\n"
  },
  {
    "path": "bazel/tachyon_node.bzl",
    "content": "load(\"//bazel:tachyon.bzl\", \"if_node_binding\")\nload(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_shared_library\")\n\ndef tachyon_node_library(\n        name,\n        testonly = False,\n        visibility = [\"//visibility:public\"],\n        **kwrargs):\n    tachyon_cc_library(\n        name = name + \"_lib\",\n        testonly = testonly,\n        **kwrargs\n    )\n\n    tachyon_cc_shared_library(\n        name = name + \"_shared_lib\",\n        deps = [\":{}_lib\".format(name)] + if_node_binding([\n            \"@node_addon_api\",\n        ]),\n        testonly = testonly,\n    )\n\n    native.genrule(\n        name = name,\n        srcs = [\":{}_shared_lib\".format(name)],\n        outs = [name + \".node\"],\n        cmd = \"cp -f $< $@\",\n        testonly = testonly,\n        visibility = visibility,\n    )\n"
  },
  {
    "path": "bazel/tachyon_py.bzl",
    "content": "load(\"@pybind11_bazel//:build_defs.bzl\", \"PYBIND_COPTS\", \"PYBIND_DEPS\", \"PYBIND_FEATURES\")\nload(\"@rules_python//python:defs.bzl\", \"py_binary\", \"py_library\", \"py_test\")\nload(\":tachyon_cc.bzl\", \"tachyon_cc_binary\", \"tachyon_cc_library\")\n\ndef tachyon_pybind_extension(\n        name,\n        copts = [],\n        features = [],\n        linkopts = [],\n        tags = [],\n        deps = [],\n        **kwargs):\n    # Mark common dependencies as required for build_cleaner.\n    tags = tags + [\"req_dep=%s\" % dep for dep in PYBIND_DEPS]\n\n    tachyon_cc_binary(\n        name = name + \".so\",\n        copts = copts + PYBIND_COPTS + select({\n            \"@pybind11//:msvc_compiler\": [],\n            \"//conditions:default\": [\n                \"-fvisibility=hidden\",\n            ],\n        }),\n        features = features + PYBIND_FEATURES,\n        linkopts = linkopts + select({\n            \"@pybind11//:msvc_compiler\": [],\n            \"@pybind11//:osx\": [\"-undefined\", \"dynamic_lookup\"],\n            \"//conditions:default\": [\"-Wl,-Bsymbolic\"],\n        }),\n        linkshared = True,\n        tags = tags,\n        deps = deps + PYBIND_DEPS,\n        **kwargs\n    )\n\ndef tachyon_pybind_library(\n        name,\n        copts = [],\n        features = [],\n        tags = [],\n        deps = [],\n        **kwargs):\n    # Mark common dependencies as required for build_cleaner.\n    tags = tags + [\"req_dep=%s\" % dep for dep in PYBIND_DEPS]\n\n    tachyon_cc_library(\n        name = name,\n        copts = copts + PYBIND_COPTS,\n        features = features + PYBIND_FEATURES,\n        tags = tags,\n        deps = deps + PYBIND_DEPS,\n        **kwargs\n    )\n\ndef tachyon_py_library(\n        name,\n        python_version = \"PY3\",\n        srcs_version = \"PY3\",\n        **kwargs):\n    py_library(\n        name = name,\n        python_version = python_version,\n        srcs_version = srcs_version,\n        **kwargs\n    )\n\ndef tachyon_py_binary(\n        name,\n        python_version = \"PY3\",\n        srcs_version = \"PY3\",\n        **kwargs):\n    py_binary(\n        name = name,\n        python_version = python_version,\n        srcs_version = srcs_version,\n        **kwargs\n    )\n\ndef tachyon_py_test(\n        name,\n        python_version = \"PY3\",\n        srcs_version = \"PY3\",\n        **kwargs):\n    py_test(\n        name = name,\n        python_version = python_version,\n        srcs_version = srcs_version,\n        **kwargs\n    )\n"
  },
  {
    "path": "bazel/tachyon_repo.bzl",
    "content": "load(\"@bazel_tools//tools/cpp:unix_cc_configure.bzl\", \"find_cc\")\n\n# This is taken from tensorflow/third_party/remote_config/common.bzl.\ndef execute(\n        repository_ctx,\n        cmdline,\n        error_msg = None,\n        error_details = None,\n        allow_failure = False):\n    \"\"\"Executes an arbitrary shell command.\n\n    Args:\n      repository_ctx: the repository_ctx object\n      cmdline: list of strings, the command to execute\n      error_msg: string, a summary of the error if the command fails\n      error_details: string, details about the error or steps to fix it\n      allow_failure: bool, if True, an empty stdout result or output to stderr\n        is fine, otherwise either of these is an error\n    Returns:\n      The result of repository_ctx.execute(cmdline)\n    \"\"\"\n    result = raw_exec(repository_ctx, cmdline)\n    if (result.stderr or not result.stdout) and not allow_failure:\n        fail(\n            \"\\n\".join([\n                error_msg.strip() if error_msg else \"Repository command failed\",\n                result.stderr.strip(),\n                error_details if error_details else \"\",\n            ]),\n        )\n    return result\n\ndef raw_exec(repository_ctx, cmdline):\n    \"\"\"Executes a command via repository_ctx.execute() and returns the result.\n\n    This method is useful for debugging purposes. For example, to print all\n    commands executed as well as their return code.\n\n    Args:\n      repository_ctx: the repository_ctx\n      cmdline: the list of args\n\n    Returns:\n      The 'exec_result' of repository_ctx.execute().\n    \"\"\"\n    return repository_ctx.execute(cmdline)\n\ndef symlink_dir(repository_ctx, src_dir, dest_dir, src_files = []):\n    files = repository_ctx.path(src_dir).readdir()\n    for src_file in files:\n        if len(src_files) == 0 or src_file.basename in src_files:\n            repository_ctx.symlink(src_file, dest_dir + \"/\" + src_file.basename)\n\ndef symlink_dir_with_prefix(repository_ctx, src_dir, dest_dir, prefix):\n    files = repository_ctx.path(src_dir).readdir()\n    for src_file in files:\n        if src_file.basename.startswith(prefix):\n            repository_ctx.symlink(src_file, dest_dir + \"/\" + src_file.basename)\n\ndef _is_wellknown_vendor(vendor):\n    return vendor == \"unknown\" or vendor == \"pc\"\n\ndef _is_wellknown_os(os):\n    return os == \"linux\" or \\\n           os == \"windows\" or \\\n           os == \"darwin\" or \\\n           os == \"illumos\" or \\\n           os == \"netbsd\" or \\\n           os == \"freebsd\" or \\\n           os == \"fuchsia\" or \\\n           os == \"android\"\n\ndef _get_target_triple(repository_ctx):\n    cc = find_cc(repository_ctx, overriden_tools = {})\n    result = execute(repository_ctx, [cc, \"-dumpmachine\"]).stdout.strip().split(\"-\")\n    machine = \"\"\n    vendor = \"\"\n    os = \"\"\n    if len(result) > 0:\n        machine = result[0]\n        result.pop(0)\n    if len(result) > 0:\n        if _is_wellknown_vendor(result[0]) or not _is_wellknown_os(result[0]):\n            vendor = result[0]\n            result.pop(0)\n    os = \"-\".join(result[0:])\n    return struct(\n        machine = machine,\n        vendor = vendor,\n        os = os,\n    )\n\ndef get_usr_lib_path_with_machine(repository_ctx):\n    target_triple = _get_target_triple(repository_ctx)\n    return \"/usr/lib/%s-%s\" % (target_triple.machine, target_triple.os)\n\ndef get_usr_include_path_with_machine(repository_ctx):\n    target_triple = _get_target_triple(repository_ctx)\n    return \"/usr/include/%s-%s\" % (target_triple.machine, target_triple.os)\n"
  },
  {
    "path": "bazel/tachyon_rust.bzl",
    "content": "load(\n    \"@rules_rust//rust:defs.bzl\",\n    \"rust_binary\",\n    \"rust_library\",\n    \"rust_static_library\",\n    \"rust_test\",\n)\n\ndef tachyon_rustc_flags():\n    return select({\n        \"@platforms//os:macos\": [\n            \"-C\",\n            \"link-arg=-framework\",\n            \"-C\",\n            \"link-arg=Foundation\",\n        ],\n        \"//conditions:default\": [],\n    })\n\ndef tachyon_rust_tags():\n    return [\"rust\"]\n\ndef tachyon_rust_binary(\n        name,\n        tags = [],\n        rustc_flags = [],\n        **kwargs):\n    rust_binary(\n        name = name,\n        rustc_flags = rustc_flags + tachyon_rustc_flags(),\n        tags = tags + tachyon_rust_tags(),\n        **kwargs\n    )\n\ndef tachyon_rust_library(\n        name,\n        tags = [],\n        rustc_flags = [],\n        **kwargs):\n    rust_library(\n        name = name,\n        rustc_flags = rustc_flags + tachyon_rustc_flags(),\n        tags = tags + tachyon_rust_tags(),\n        **kwargs\n    )\n\ndef tachyon_rust_static_library(\n        name,\n        tags = [],\n        rustc_flags = [],\n        **kwargs):\n    rust_static_library(\n        name = name,\n        rustc_flags = rustc_flags + tachyon_rustc_flags(),\n        tags = tags + tachyon_rust_tags(),\n        **kwargs\n    )\n\ndef tachyon_rust_test(\n        name,\n        tags = [],\n        rustc_flags = [],\n        **kwargs):\n    rust_test(\n        name = name,\n        rustc_flags = rustc_flags + tachyon_rustc_flags(),\n        tags = tags + tachyon_rust_tags(),\n        **kwargs\n    )\n"
  },
  {
    "path": "benchmark/BUILD.bazel",
    "content": "load(\"//bazel:tachyon.bzl\", \"if_has_matplotlib\")\nload(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_matplotlib_defines\")\n\ntachyon_cc_library(\n    name = \"simple_reporter\",\n    srcs = [\"simple_reporter.cc\"],\n    hdrs = [\"simple_reporter.h\"],\n    force_rtti = if_has_matplotlib(True, False),\n    local_defines = tachyon_matplotlib_defines(),\n    visibility = [\"//benchmark:__subpackages__\"],\n    deps = [\n        \":vendor\",\n        \"//tachyon/base/console:table_writer\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/base/strings:string_number_conversions\",\n        \"//tachyon/base/time\",\n    ] + if_has_matplotlib([\n        \"@com_github_soblin_matplotlibcpp17//:matplotlibcpp17\",\n    ]),\n)\n\ntachyon_cc_library(\n    name = \"field_type\",\n    hdrs = [\"field_type.h\"],\n    visibility = [\"//benchmark:__subpackages__\"],\n    deps = [\n        \"//tachyon/base:logging\",\n        \"//tachyon/base/flag\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"vendor\",\n    hdrs = [\"vendor.h\"],\n    visibility = [\"//benchmark:__subpackages__\"],\n    deps = [\n        \"//tachyon/base:logging\",\n        \"//tachyon/base/flag\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"config\",\n    srcs = [\"config.cc\"],\n    hdrs = [\"config.h\"],\n    visibility = [\"//benchmark:__subpackages__\"],\n    deps = [\n        \"//benchmark:vendor\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base/console\",\n        \"//tachyon/base/flag:flag_parser\",\n    ],\n)\n"
  },
  {
    "path": "benchmark/config.cc",
    "content": "#include \"benchmark/config.h\"\n\n#include <string>\n\n#include \"tachyon/base/console/iostream.h\"\n#include \"tachyon/base/flag/flag_parser.h\"\n\nnamespace tachyon::benchmark {\n\nConfig::Config() : Config(Options()) {}\n\nConfig::Config(const Options& options) {\n  if (options.include_check_results) {\n    parser_.AddFlag<base::BoolFlag>(&check_results_)\n        .set_long_name(\"--check_results\")\n        .set_default_value(false)\n        .set_help(\"Check results across different vendors. By default, false\");\n  }\n}\n\nbool Config::Parse(int argc, char** argv) {\n  std::string error;\n  if (!parser_.Parse(argc, argv, &error)) {\n    tachyon_cerr << error << std::endl;\n    return false;\n  }\n\n  PostParse();\n  return Validate();\n}\n\n}  // namespace tachyon::benchmark\n"
  },
  {
    "path": "benchmark/config.h",
    "content": "#ifndef BENCHMARK_CONFIG_H_\n#define BENCHMARK_CONFIG_H_\n\n#include <set>\n\n// clang-format off\n#include \"benchmark/vendor.h\"\n// clang-format on\n#include \"tachyon/base/flag/flag_parser.h\"\n\nnamespace tachyon::benchmark {\n\nclass Config {\n public:\n  struct Options {\n    bool include_check_results = true;\n  };\n\n  Config();\n  explicit Config(const Options& options);\n  Config(const Config& other) = delete;\n  Config& operator=(const Config& other) = delete;\n  virtual ~Config() = default;\n\n  const std::set<Vendor>& vendors() const { return vendors_; }\n  bool check_results() const { return check_results_; }\n\n  bool Parse(int argc, char** argv);\n\n  virtual bool Validate() const { return true; }\n\n protected:\n  // Override this method if you need to perform any actions after |Parse()| is\n  // called.\n  virtual void PostParse() {}\n\n  base::FlagParser parser_;\n  std::set<Vendor> vendors_;\n  bool check_results_;\n};\n\n}  // namespace tachyon::benchmark\n\n#endif  // BENCHMARK_CONFIG_H_\n"
  },
  {
    "path": "benchmark/ec/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cuda_binary\")\n\ntachyon_cc_library(\n    name = \"ec_config\",\n    srcs = [\"ec_config.cc\"],\n    hdrs = [\"ec_config.h\"],\n    deps = [\n        \"//benchmark:config\",\n        \"//tachyon/base/ranges:algorithm\",\n    ],\n)\n\ntachyon_cuda_binary(\n    name = \"ec_double_benchmark_gpu\",\n    testonly = True,\n    srcs = [\"ec_double_benchmark_gpu.cc\"],\n    deps = [\n        \":ec_config\",\n        \"//benchmark:simple_reporter\",\n        \"//tachyon/base:profiler\",\n        \"//tachyon/base/time:time_interval\",\n        \"//tachyon/device/gpu:gpu_memory\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:g1_gpu\",\n        \"//tachyon/math/elliptic_curves/short_weierstrass/kernels:elliptic_curve_ops\",\n        \"//tachyon/math/elliptic_curves/test:random\",\n    ],\n)\n"
  },
  {
    "path": "benchmark/ec/ec_config.cc",
    "content": "#include \"benchmark/ec/ec_config.h\"\n\n#include \"tachyon/base/ranges/algorithm.h\"\n\nnamespace tachyon::benchmark {\n\nECConfig::ECConfig() : Config({/*include_check_results=*/false}) {\n  parser_.AddFlag<base::Flag<std::vector<size_t>>>(&point_nums_)\n      .set_short_name(\"-n\")\n      .set_required()\n      .set_help(\"The number of points to test\");\n}\n\nvoid ECConfig::PostParse() {\n  base::ranges::sort(point_nums_);  // NOLINT(build/include_what_you_use)\n}\n\n}  // namespace tachyon::benchmark\n"
  },
  {
    "path": "benchmark/ec/ec_config.h",
    "content": "#ifndef BENCHMARK_EC_EC_CONFIG_H_\n#define BENCHMARK_EC_EC_CONFIG_H_\n\n#include <vector>\n\n// clang-format off\n#include \"benchmark/config.h\"\n// clang-format on\n\nnamespace tachyon::benchmark {\n\nclass ECConfig : public Config {\n public:\n  ECConfig();\n  ECConfig(const ECConfig& other) = delete;\n  ECConfig& operator=(const ECConfig& other) = delete;\n\n  const std::vector<size_t>& point_nums() const { return point_nums_; }\n\n private:\n  // Config methods\n  void PostParse() override;\n\n  std::vector<size_t> point_nums_;\n};\n\n}  // namespace tachyon::benchmark\n\n#endif  // BENCHMARK_EC_EC_CONFIG_H_\n"
  },
  {
    "path": "benchmark/ec/ec_double_benchmark_gpu.cc",
    "content": "#if TACHYON_CUDA || TACHYON_USE_ROCM\n\n#include <iostream>\n\n// clang-format off\n#include \"benchmark/simple_reporter.h\"\n#include \"benchmark/ec/ec_config.h\"\n// clang-format on\n#include \"tachyon/base/profiler.h\"\n#include \"tachyon/base/time/time_interval.h\"\n#include \"tachyon/device/gpu/gpu_memory.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/g1_gpu.h\"\n#include \"tachyon/math/elliptic_curves/short_weierstrass/kernels/elliptic_curve_ops.cu.h\"\n#include \"tachyon/math/elliptic_curves/test/random.h\"\n#include \"tachyon/math/geometry/point_conversions.h\"\n\nnamespace tachyon::benchmark {\n\nusing namespace device;\nusing namespace math;\n\nnamespace {\n\n// TODO(chokobole): Use openmp.\nvoid TestDoubleOnCPU(const std::vector<math::bn254::G1AffinePoint>& bases,\n                     std::vector<math::bn254::G1JacobianPoint>& results,\n                     size_t nums) {\n  for (size_t i = 0; i < nums; ++i) {\n    results[i] = bases[i].Double();\n  }\n}\n\ngpuError_t LaunchDouble(const math::bn254::G1AffinePointGpu* x,\n                        math::bn254::G1JacobianPointGpu* y, size_t count) {\n  math::kernels::Double<<<(count - 1) / 32 + 1, 32>>>(x, y, count);\n  gpuError_t error = LOG_IF_GPU_LAST_ERROR(\"Failed Double()\");\n  return error == gpuSuccess ? LOG_IF_GPU_ERROR(gpuDeviceSynchronize(),\n                                                \"Failed gpuDeviceSynchronize()\")\n                             : error;\n}\n\nvoid TestDoubleOnGPU(math::bn254::G1AffinePointGpu* bases_cuda,\n                     math::bn254::G1JacobianPointGpu* results_cuda,\n                     const std::vector<math::bn254::G1AffinePoint>& bases,\n                     size_t nums) {\n  for (size_t i = 0; i < nums; ++i) {\n    bases_cuda[i] = ConvertPoint<math::bn254::G1AffinePointGpu>(bases[i]);\n  }\n\n  LaunchDouble(bases_cuda, results_cuda, nums);\n}\n\n}  // namespace\n\nint RealMain(int argc, char** argv) {\n  base::FilePath tmp_file;\n  CHECK(base::GetTempDir(&tmp_file));\n  tmp_file = tmp_file.Append(\"ec_double_benchmark_gpu.perfetto-trace\");\n  base::Profiler profiler({tmp_file});\n\n  profiler.Init();\n  profiler.Start();\n\n  ECConfig config;\n  if (!config.Parse(argc, argv)) {\n    return 1;\n  }\n\n  math::bn254::G1Curve::Init();\n  math::bn254::G1CurveGpu::Init();\n\n  const std::vector<size_t>& point_nums = config.point_nums();\n\n  SimpleReporter reporter;\n  reporter.set_title(\"EC double benchmark\");\n  reporter.set_x_label(\"# of points\");\n  reporter.set_column_labels(base::Map(\n      point_nums, [](size_t num) { return base::NumberToString(num); }));\n\n  std::cout << \"Generating random points...\" << std::endl;\n  size_t max_point_num = point_nums.back();\n  std::vector<bn254::G1AffinePoint> bases =\n      CreatePseudoRandomPoints<bn254::G1AffinePoint>(max_point_num);\n  std::vector<bn254::Fr> scalars = base::CreateVectorParallel(\n      max_point_num, []() { return bn254::Fr::Random(); });\n  std::cout << \"Generation completed\" << std::endl;\n\n  std::vector<math::bn254::G1JacobianPoint> results_cpu;\n  results_cpu.resize(max_point_num);\n  reporter.AddVendor(Vendor::TachyonCPU());\n  base::TimeInterval interval(base::TimeTicks::Now());\n  for (size_t i = 0; i < point_nums.size(); ++i) {\n    TestDoubleOnCPU(bases, results_cpu, point_nums[i]);\n    reporter.AddTime(Vendor::TachyonCPU(), interval.GetTimeDelta());\n  }\n\n  GPU_MUST_SUCCEED(gpuDeviceReset(), \"Failed gpuDeviceReset()\");\n  auto bases_cuda =\n      gpu::GpuMemory<math::bn254::G1AffinePointGpu>::MallocManaged(\n          max_point_num);\n  auto results_cuda =\n      gpu::GpuMemory<math::bn254::G1JacobianPointGpu>::MallocManaged(\n          max_point_num);\n\n  reporter.AddVendor(Vendor::TachyonGPU());\n  interval.Reset();\n  for (size_t i = 0; i < point_nums.size(); ++i) {\n    TestDoubleOnGPU(bases_cuda.data(), results_cuda.data(), bases,\n                    point_nums[i]);\n    reporter.AddTime(Vendor::TachyonGPU(), interval.GetTimeDelta());\n  }\n\n  reporter.Show();\n\n  return 0;\n}\n\n}  // namespace tachyon::benchmark\n\nint main(int argc, char** argv) {\n  return tachyon::benchmark::RealMain(argc, argv);\n}\n#else\n#include \"tachyon/base/console/iostream.h\"\n\nint main(int argc, char **argv) {\n  tachyon_cerr << \"please build with --config cuda or --config rocm\"\n               << std::endl;\n  return 1;\n}\n#endif  // TACHYON_CUDA\n"
  },
  {
    "path": "benchmark/fft/BUILD.bazel",
    "content": "load(\n    \"//bazel:tachyon_cc.bzl\",\n    \"tachyon_cc_binary\",\n    \"tachyon_cc_library\",\n)\n\ntachyon_cc_library(\n    name = \"fft_config\",\n    testonly = True,\n    srcs = [\"fft_config.cc\"],\n    hdrs = [\"fft_config.h\"],\n    deps = [\n        \"//benchmark:config\",\n        \"//tachyon/base/console\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/base/ranges:algorithm\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"fft_runner\",\n    testonly = True,\n    hdrs = [\"fft_runner.h\"],\n    deps = [\n        \"//benchmark:simple_reporter\",\n        \"//tachyon/base/time\",\n        \"//tachyon/math/polynomials/univariate/icicle:icicle_ntt_holder\",\n    ],\n)\n\ntachyon_cc_binary(\n    name = \"fft_benchmark\",\n    testonly = True,\n    srcs = [\"fft_benchmark.cc\"],\n    deps = [\n        \":fft_config\",\n        \":fft_runner\",\n        \"//benchmark/fft/arkworks\",\n        \"//benchmark/fft/bellman\",\n        \"//benchmark/fft/halo2\",\n        \"//tachyon/base:profiler\",\n        \"//tachyon/c/math/polynomials/univariate:bn254_univariate_evaluation_domain\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:fr\",\n        \"//tachyon/math/elliptic_curves/bn/bn254/halo2:bn254\",\n        \"//tachyon/math/polynomials/univariate:univariate_evaluation_domain\",\n        \"//tachyon/math/polynomials/univariate:univariate_evaluation_domain_factory\",\n    ],\n)\n\ntachyon_cc_binary(\n    name = \"fft_benchmark_gpu\",\n    testonly = True,\n    srcs = [\"fft_benchmark_gpu.cc\"],\n    deps = [\n        \":fft_config\",\n        \":fft_runner\",\n        \"//tachyon/base:profiler\",\n        \"//tachyon/c/math/polynomials/univariate:bn254_univariate_evaluation_domain\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:fr\",\n        \"//tachyon/math/polynomials/univariate:univariate_evaluation_domain\",\n        \"//tachyon/math/polynomials/univariate:univariate_evaluation_domain_factory\",\n    ],\n)\n"
  },
  {
    "path": "benchmark/fft/README.md",
    "content": "# (I)FFT Benchmark\n\n## CPU\n\n```\nRun on 13th Gen Intel(R) Core(TM) i9-13900K (32 X 5500 MHz CPU s)\nCompiler: clang-15\nCPU Caches:\n  L1 Data 48 KiB (x16)\n  L1 Instruction 32 KiB (x16)\n  L2 Unified 2048 KiB (x16)\n  L3 Unified 36864 KiB (x1)\n\nRun on Apple M3 Pro (12 X 4050 MHz)\nCPU Caches:\n  L1 Data 64 KiB (x12)\n  L1 Instruction 128 KiB (x12)\n  L2 Unified 4096 KiB (x12)\n```\n\nNote: Run with `build --@rules_rust//:extra_rustc_flags=\"-Ctarget-cpu=native\"` in your .bazelrc.user\n\n### FFT\n\n```shell\nGOMP_SPINCOUNT=0 bazel run --config maxopt --//:has_matplotlib //benchmark/fft:fft_benchmark -- -k 16 -k 17 -k 18 -k 19 -k 20 -k 21 -k 22 -k 23 --vendor arkworks --vendor bellman --vendor halo2 --check_results\n```\n\n#### On Intel i9-13900K\n\n| Exponent | Tachyon      | Arkworks     | Bellman  | Halo2    |\n| :------: | ------------ | ------------ | -------- | -------- |\n|    16    | **0.002058** | 0.005143     | 0.006314 | 0.002249 |\n|    17    | **0.002246** | 0.00334      | 0.015646 | 0.006193 |\n|    18    | **0.010154** | 0.018807     | 0.046443 | 0.007574 |\n|    19    | 0.022984     | **0.014652** | 0.076281 | 0.014506 |\n|    20    | **0.02**     | 0.02497      | 0.100082 | 0.042877 |\n|    21    | **0.044831** | 0.075563     | 0.20222  | 0.067161 |\n|    22    | **0.130201** | 0.179075     | 0.402452 | 0.169194 |\n|    23    | **0.281398** | 0.394068     | 0.792004 | 0.372566 |\n\n![image](/benchmark/fft/fft_benchmark_ubuntu_i9.png)\n\n#### On Mac M3 Pro\n\n| Exponent | Tachyon      | Arkworks | Bellman  | Halo2    |\n| :------: | ------------ | -------- | -------- | -------- |\n|    16    | **0.002526** | 0.003804 | 0.00784  | 0.005689 |\n|    17    | **0.004694** | 0.005769 | 0.015577 | 0.01121  |\n|    18    | **0.009246** | 0.010243 | 0.027834 | 0.022379 |\n|    19    | **0.018328** | 0.020404 | 0.055661 | 0.041394 |\n|    20    | **0.039683** | 0.041085 | 0.110702 | 0.086299 |\n|    21    | **0.079138** | 0.087336 | 0.230857 | 0.175599 |\n|    22    | **0.166646** | 0.177959 | 0.474296 | 0.352872 |\n|    23    | **0.33996**  | 0.363612 | 0.971581 | 0.748284 |\n\n![image](/benchmark/fft/fft_benchmark_mac_m3.png)\n\n### IFFT\n\n```shell\nGOMP_SPINCOUNT=0 bazel run --config maxopt --//:has_matplotlib //benchmark/fft:fft_benchmark -- -k 16 -k 17 -k 18 -k 19 -k 20 -k 21 -k 22 -k 23 --vendor arkworks --vendor bellman --vendor halo2 --run_ifft --check_results\n```\n\n#### On Intel i9-13900K\n\n| Exponent | Tachyon      | Arkworks | Bellman  | Halo2        |\n| :------: | ------------ | -------- | -------- | ------------ |\n|    16    | **0.001392** | 0.012028 | 0.009913 | 0.002413     |\n|    17    | **0.002511** | 0.00427  | 0.01418  | 0.005731     |\n|    18    | 0.01762      | 0.021167 | 0.034676 | **0.010811** |\n|    19    | **0.009646** | 0.01447  | 0.058714 | 0.016038     |\n|    20    | **0.030303** | 0.034815 | 0.104936 | 0.05337      |\n|    21    | **0.047463** | 0.072579 | 0.199788 | 0.093146     |\n|    22    | **0.146697** | 0.181389 | 0.391296 | 0.19874      |\n|    23    | **0.285937** | 0.403596 | 0.82276  | 0.347876     |\n\n![image](/benchmark/fft/ifft_benchmark_ubuntu_i9.png)\n\n#### On Mac M3 Pro\n\n| Exponent | Tachyon      | Arkworks | Bellman  | Halo2    |\n| :------: | ------------ | -------- | -------- | -------- |\n|    16    | **0.002798** | 0.003867 | 0.008102 | 0.005665 |\n|    17    | **0.004882** | 0.005737 | 0.015998 | 0.011672 |\n|    18    | **0.010308** | 0.010962 | 0.028118 | 0.022723 |\n|    19    | **0.018724** | 0.021338 | 0.056855 | 0.042554 |\n|    20    | **0.037687** | 0.043237 | 0.113848 | 0.089899 |\n|    21    | **0.078429** | 0.092134 | 0.234585 | 0.174939 |\n|    22    | **0.162542** | 0.189442 | 0.484644 | 0.361127 |\n|    23    | **0.338646** | 0.392674 | 0.989173 | 0.765592 |\n\n![image](/benchmark/fft/ifft_benchmark_mac_m3.png)\n\n## GPU\n\n### FFT\n\n```shell\nGOMP_SPINCOUNT=0 bazel run --config maxopt --config cuda --//:has_matplotlib //benchmark/fft:fft_benchmark_gpu -- -k 16 -k 17 -k 18 -k 19 -k 20 -k 21 -k 22 -k 23 --check_results\n```\n\n#### On RTX-4090\n\n| Exponent | Tachyon CPU | Tachyon GPU  |\n| :------: | ----------- | ------------ |\n|    16    | 0.002348    | **0.001**    |\n|    17    | 0.00204     | **0.001182** |\n|    18    | 0.00393     | **0.002211** |\n|    19    | 0.009317    | **0.004079** |\n|    20    | 0.049204    | **0.008114** |\n|    21    | 0.044158    | **0.01616**  |\n|    22    | 0.134064    | **0.032785** |\n|    23    | 0.274101    | **0.066068** |\n\n![image](/benchmark/fft/fft_benchmark_ubuntu_rtx_4090.png)\n\n### IFFT\n\n```shell\nGOMP_SPINCOUNT=0 bazel run --config maxopt --config cuda --//:has_matplotlib //benchmark/fft:fft_benchmark_gpu -- -k 16 -k 17 -k 18 -k 19 -k 20 -k 21 -k 22 -k 23 --run_ifft --check_results\n```\n\n#### On RTX-4090\n\n| Exponent | Tachyon  | Tachyon GPU  |\n| :------: | -------- | ------------ |\n|    16    | 0.002138 | **0.001341** |\n|    17    | 0.00488  | **0.000933** |\n|    18    | 0.003887 | **0.002502** |\n|    19    | 0.00896  | **0.003806** |\n|    20    | 0.017953 | **0.007745** |\n|    21    | 0.043787 | **0.016268** |\n|    22    | 0.132048 | **0.033012** |\n|    23    | 0.291132 | **0.066022** |\n\n![image](/benchmark/fft/ifft_benchmark_ubuntu_rtx_4090.png)\n"
  },
  {
    "path": "benchmark/fft/arkworks/BUILD.bazel",
    "content": "load(\"@crate_index//:defs.bzl\", \"aliases\", \"all_crate_deps\")\nload(\"//bazel:tachyon_rust.bzl\", \"tachyon_rust_static_library\")\n\ntachyon_rust_static_library(\n    name = \"arkworks\",\n    srcs = glob([\"src/**/*.rs\"]),\n    aliases = aliases(),\n    proc_macro_deps = all_crate_deps(proc_macro = True),\n    visibility = [\"//benchmark/fft:__pkg__\"],\n    deps = all_crate_deps(normal = True) + [\n        \"//tachyon/rs:tachyon_rs\",\n    ],\n)\n"
  },
  {
    "path": "benchmark/fft/arkworks/Cargo.toml",
    "content": "[package]\nname = \"arkworks_fft_benchmark\"\nversion = \"0.0.1\"\nauthors = [\"The Tachyon Authors <tachyon-discuss@kroma.network>\"]\nedition = \"2021\"\nrust-version = \"1.56.1\"\ndescription = \"\"\"\nArkworks FFT Benchmark\n\"\"\"\nlicense = \"MIT OR Apache-2.0\"\nrepository = \"https://github.com/kroma-network/tachyon\"\nreadme = \"README.md\"\ncategories = [\"cryptography\"]\nkeywords = [\"tachyon\", \"benchmark\", \"arkworks\"]\npublish = false\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[dependencies]\nark-bn254 = { git = \"https://github.com/arkworks-rs/curves.git\", tag = \"v0.4.0-alpha.2\" }\nark-poly = { version = \"0.4.2\", features = [\"parallel\"] }\ntachyon_rs = { path = \"../../../tachyon/rs\" }\n"
  },
  {
    "path": "benchmark/fft/arkworks/src/lib.rs",
    "content": "use ark_bn254::Fr;\nuse ark_poly::domain::{EvaluationDomain, Radix2EvaluationDomain};\nuse std::{mem, slice, time::Instant};\nuse tachyon_rs::math::elliptic_curves::bn::bn254::Fr as CppFr;\n\n#[no_mangle]\npub extern \"C\" fn run_fft_arkworks(\n    coeffs: *const CppFr,\n    n: usize,\n    _omega: *const CppFr,\n    _k: u32,\n    duration: *mut u64,\n) -> *mut CppFr {\n    let domain = Radix2EvaluationDomain::<Fr>::new(n).unwrap();\n    let ret = unsafe {\n        let coeffs: &[CppFr] = slice::from_raw_parts(coeffs, n);\n\n        let coeffs: &[Fr] = mem::transmute(coeffs);\n        let mut coeffs = coeffs.to_vec();\n\n        let start = Instant::now();\n        domain.fft_in_place(&mut coeffs);\n        duration.write(start.elapsed().as_micros() as u64);\n        coeffs\n    };\n    Box::into_raw(ret.into_boxed_slice()) as *mut CppFr\n}\n\n#[no_mangle]\npub extern \"C\" fn run_ifft_arkworks(\n    coeffs: *const CppFr,\n    n: usize,\n    _omega_inv: *const CppFr,\n    _k: u32,\n    duration: *mut u64,\n) -> *mut CppFr {\n    let domain = Radix2EvaluationDomain::<Fr>::new(n).unwrap();\n    let ret = unsafe {\n        let coeffs: &[CppFr] = slice::from_raw_parts(coeffs, n);\n\n        let coeffs: &[Fr] = mem::transmute(coeffs);\n        let mut coeffs = coeffs.to_vec();\n\n        let start = Instant::now();\n        domain.ifft_in_place(&mut coeffs);\n        duration.write(start.elapsed().as_micros() as u64);\n        coeffs\n    };\n    Box::into_raw(ret.into_boxed_slice()) as *mut CppFr\n}\n"
  },
  {
    "path": "benchmark/fft/bellman/BUILD.bazel",
    "content": "load(\"@crate_index//:defs.bzl\", \"aliases\", \"all_crate_deps\")\nload(\"//bazel:tachyon_rust.bzl\", \"tachyon_rust_static_library\")\n\ntachyon_rust_static_library(\n    name = \"bellman\",\n    srcs = glob([\"src/**/*.rs\"]),\n    aliases = aliases(),\n    proc_macro_deps = all_crate_deps(proc_macro = True),\n    visibility = [\"//benchmark/fft:__pkg__\"],\n    deps = all_crate_deps(normal = True) + [\n        \"//tachyon/rs:tachyon_rs\",\n    ],\n)\n"
  },
  {
    "path": "benchmark/fft/bellman/Cargo.toml",
    "content": "[package]\nname = \"bellman_fft_benchmark\"\nversion = \"0.0.1\"\nauthors = [\"The Tachyon Authors <tachyon-discuss@kroma.network>\"]\nedition = \"2021\"\nrust-version = \"1.56.1\"\ndescription = \"\"\"\nBellman FFT Benchmark\n\"\"\"\nlicense = \"MIT OR Apache-2.0\"\nrepository = \"https://github.com/kroma-network/tachyon\"\nreadme = \"README.md\"\ncategories = [\"cryptography\"]\nkeywords = [\"tachyon\", \"benchmark\", \"bellman\"]\npublish = false\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[dependencies]\nbellman_ce = { git = \"https://github.com/kroma-network/bellman.git\", rev = \"bbac055\" }\ntachyon_rs = { path = \"../../../tachyon/rs\" }\n"
  },
  {
    "path": "benchmark/fft/bellman/src/lib.rs",
    "content": "use bellman_ce::{domain::EvaluationDomain, domain::Scalar, pairing::bn256::Bn256, worker::Worker};\nuse std::{mem, slice, time::Instant};\nuse tachyon_rs::math::elliptic_curves::bn::bn254::Fr as CppFr;\n\n#[no_mangle]\npub extern \"C\" fn run_fft_bellman(\n    coeffs: *const CppFr,\n    n: usize,\n    _omega: *const CppFr,\n    _k: u32,\n    duration: *mut u64,\n) -> *mut CppFr {\n    let ret = unsafe {\n        let coeffs: &[CppFr] = slice::from_raw_parts(coeffs, n);\n\n        let coeffs: &[Scalar<Bn256>] = mem::transmute(coeffs);\n        let coeffs = coeffs.to_vec();\n\n        let worker = Worker::new();\n        let mut domain = EvaluationDomain::from_coeffs(coeffs).unwrap();\n\n        let start = Instant::now();\n        domain.fft(&worker);\n        duration.write(start.elapsed().as_micros() as u64);\n        domain.into_coeffs()\n    };\n    Box::into_raw(ret.into_boxed_slice()) as *mut CppFr\n}\n\n#[no_mangle]\npub extern \"C\" fn run_ifft_bellman(\n    coeffs: *const CppFr,\n    n: usize,\n    _omega_inv: *const CppFr,\n    _k: u32,\n    duration: *mut u64,\n) -> *mut CppFr {\n    let ret = unsafe {\n        let coeffs: &[CppFr] = slice::from_raw_parts(coeffs, n);\n\n        let coeffs: &[Scalar<Bn256>] = mem::transmute(coeffs);\n        let coeffs = coeffs.to_vec();\n\n        let worker = Worker::new();\n        let mut domain = EvaluationDomain::from_coeffs(coeffs).unwrap();\n\n        let start = Instant::now();\n        domain.ifft(&worker);\n        duration.write(start.elapsed().as_micros() as u64);\n        domain.into_coeffs()\n    };\n    Box::into_raw(ret.into_boxed_slice()) as *mut CppFr\n}\n"
  },
  {
    "path": "benchmark/fft/fft_benchmark.cc",
    "content": "#include <stddef.h>\n#include <stdint.h>\n\n#include <memory>\n#include <vector>\n\n// clang-format off\n#include \"benchmark/fft/fft_config.h\"\n#include \"benchmark/fft/fft_runner.h\"\n#include \"benchmark/simple_reporter.h\"\n// clang-format on\n#include \"tachyon/base/profiler.h\"\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_dense_polynomial_type_traits.h\"\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_evaluation_domain_type_traits.h\"\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_evaluations_type_traits.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/fr.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/halo2/bn254.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluation_domain.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluation_domain_factory.h\"\n\nnamespace tachyon::benchmark {\n\nusing namespace math;\n\nextern \"C\" tachyon_bn254_fr* run_fft_arkworks(const tachyon_bn254_fr* coeffs,\n                                              size_t n,\n                                              const tachyon_bn254_fr* omega,\n                                              uint32_t k,\n                                              uint64_t* duration_in_us);\n\nextern \"C\" tachyon_bn254_fr* run_ifft_arkworks(\n    const tachyon_bn254_fr* coeffs, size_t n, const tachyon_bn254_fr* omega_inv,\n    uint32_t k, uint64_t* duration_in_us);\n\nextern \"C\" tachyon_bn254_fr* run_fft_bellman(const tachyon_bn254_fr* coeffs,\n                                             size_t n,\n                                             const tachyon_bn254_fr* omega,\n                                             uint32_t k,\n                                             uint64_t* duration_in_us);\n\nextern \"C\" tachyon_bn254_fr* run_ifft_bellman(const tachyon_bn254_fr* coeffs,\n                                              size_t n,\n                                              const tachyon_bn254_fr* omega_inv,\n                                              uint32_t k,\n                                              uint64_t* duration_in_us);\n\nextern \"C\" tachyon_bn254_fr* run_fft_halo2(const tachyon_bn254_fr* coeffs,\n                                           size_t n,\n                                           const tachyon_bn254_fr* omega,\n                                           uint32_t k,\n                                           uint64_t* duration_in_us);\n\nextern \"C\" tachyon_bn254_fr* run_ifft_halo2(const tachyon_bn254_fr* coeffs,\n                                            size_t n,\n                                            const tachyon_bn254_fr* omega_inv,\n                                            uint32_t k,\n                                            uint64_t* duration_in_us);\n\ntemplate <typename PolyOrEvals>\nvoid CheckResult(bool check_result, const PolyOrEvals& tachyon_result,\n                 const PolyOrEvals& vendor_result) {\n  if (check_result) {\n    CHECK_EQ(tachyon_result, vendor_result) << \"Results do not match\";\n  }\n}\n\ntemplate <typename Domain, typename PolyOrEvals,\n          typename RetPoly = std::conditional_t<\n              std::is_same_v<PolyOrEvals, typename Domain::Evals>,\n              typename Domain::DensePoly, typename Domain::Evals>>\nvoid Run(const FFTConfig& config) {\n  std::string_view name;\n  if (config.run_ifft()) {\n    name = \"IFFT Benchmark\";\n  } else {\n    name = \"FFT Benchmark\";\n  }\n\n  SimpleReporter reporter;\n  reporter.set_title(name);\n  reporter.set_x_label(\"Degree (2ˣ)\");\n  reporter.set_column_labels(base::Map(\n      config.exponents(),\n      [](uint32_t exponent) { return base::NumberToString(exponent); }));\n\n  reporter.AddVendor(Vendor::Tachyon());\n  for (const Vendor vendor : config.vendors()) {\n    reporter.AddVendor(vendor);\n  }\n\n  FFTRunner<Domain, PolyOrEvals> runner(reporter);\n\n  std::vector<size_t> degrees = config.GetDegrees();\n\n  bool need_halo2_results =\n      base::Contains(config.vendors(), Vendor::Bellman()) ||\n      base::Contains(config.vendors(), Vendor::ScrollHalo2());\n  for (size_t degree : degrees) {\n    PolyOrEvals input = PolyOrEvals::Random(degree);\n    std::unique_ptr<Domain> domain = Domain::Create(degree + 1);\n    std::unique_ptr<Domain> halo2_domain;\n    if (need_halo2_results) {\n      math::halo2::ScopedSubgroupGeneratorOverrider scoped_overrider;\n      halo2_domain = Domain::Create(degree + 1);\n    }\n\n    if constexpr (std::is_same_v<PolyOrEvals, typename Domain::Evals>) {\n      RetPoly tachyon_result, tachyon_halo2_result;\n      runner.Run(Vendor::Tachyon(),\n                 tachyon_bn254_univariate_evaluation_domain_ifft_inplace,\n                 domain.get(), input, true, tachyon_result);\n      if (halo2_domain) {\n        runner.Run(Vendor::Tachyon(),\n                   tachyon_bn254_univariate_evaluation_domain_ifft_inplace,\n                   halo2_domain.get(), input, false, tachyon_halo2_result);\n      }\n\n      for (const Vendor vendor : config.vendors()) {\n        if (vendor.value() == Vendor::kArkworks) {\n          RetPoly vendor_result;\n          runner.RunExternal(vendor, run_ifft_arkworks, domain.get(), input,\n                             vendor_result);\n          CheckResult(config.check_results(), tachyon_result, vendor_result);\n        } else if (vendor.value() == Vendor::kBellman) {\n          RetPoly vendor_result;\n          runner.RunExternal(vendor, run_ifft_bellman, halo2_domain.get(),\n                             input, vendor_result);\n          CheckResult(config.check_results(), tachyon_halo2_result,\n                      vendor_result);\n        } else if (vendor.value() == Vendor::kScrollHalo2) {\n          RetPoly vendor_result;\n          runner.RunExternal(vendor, run_ifft_halo2, halo2_domain.get(), input,\n                             vendor_result);\n          CheckResult(config.check_results(), tachyon_halo2_result,\n                      vendor_result);\n        } else {\n          NOTREACHED();\n        }\n      }\n      // NOLINTNEXTLINE(readability/braces)\n    } else if constexpr (std::is_same_v<PolyOrEvals,\n                                        typename Domain::DensePoly>) {\n      RetPoly tachyon_result, tachyon_halo2_result;\n      runner.Run(Vendor::Tachyon(),\n                 tachyon_bn254_univariate_evaluation_domain_fft_inplace,\n                 domain.get(), input, true, tachyon_result);\n      if (halo2_domain) {\n        runner.Run(Vendor::Tachyon(),\n                   tachyon_bn254_univariate_evaluation_domain_fft_inplace,\n                   halo2_domain.get(), input, false, tachyon_halo2_result);\n      }\n\n      for (const Vendor vendor : config.vendors()) {\n        if (vendor.value() == Vendor::kArkworks) {\n          RetPoly vendor_result;\n          runner.RunExternal(vendor, run_fft_arkworks, domain.get(), input,\n                             vendor_result);\n          CheckResult(config.check_results(), tachyon_result, vendor_result);\n        } else if (vendor.value() == Vendor::kBellman) {\n          RetPoly vendor_result;\n          runner.RunExternal(vendor, run_fft_bellman, halo2_domain.get(), input,\n                             vendor_result);\n          CheckResult(config.check_results(), tachyon_halo2_result,\n                      vendor_result);\n        } else if (vendor.value() == Vendor::kScrollHalo2) {\n          RetPoly vendor_result;\n          runner.RunExternal(vendor, run_fft_halo2, halo2_domain.get(), input,\n                             vendor_result);\n          CheckResult(config.check_results(), tachyon_halo2_result,\n                      vendor_result);\n        } else {\n          NOTREACHED();\n        }\n      }\n    }\n  }\n\n  reporter.Show();\n}\n\nint RealMain(int argc, char** argv) {\n  using Field = bn254::Fr;\n  constexpr size_t kMaxDegree = SIZE_MAX - 1;\n  using Domain = UnivariateEvaluationDomain<Field, kMaxDegree>;\n  using DensePoly = Domain::DensePoly;\n  using Evals = Domain::Evals;\n\n  base::FilePath tmp_file;\n  CHECK(base::GetTempDir(&tmp_file));\n  tmp_file = tmp_file.Append(\"fft_benchmark.perfetto-trace\");\n  base::Profiler profiler({tmp_file});\n\n  profiler.Init();\n  profiler.Start();\n\n  Field::Init();\n\n  FFTConfig::Options options;\n  options.include_vendors = true;\n  FFTConfig config(options);\n  if (!config.Parse(argc, argv)) {\n    return 1;\n  }\n\n  if (config.run_ifft()) {\n    Run<Domain, Evals>(config);\n  } else {\n    Run<Domain, DensePoly>(config);\n  }\n\n  return 0;\n}\n\n}  // namespace tachyon::benchmark\n\nint main(int argc, char** argv) {\n  return tachyon::benchmark::RealMain(argc, argv);\n}\n"
  },
  {
    "path": "benchmark/fft/fft_benchmark_gpu.cc",
    "content": "#include <memory>\n#include <vector>\n#if TACHYON_CUDA\n\n// clang-format off\n#include \"benchmark/fft/fft_config.h\"\n#include \"benchmark/fft/fft_runner.h\"\n#include \"benchmark/simple_reporter.h\"\n// clang-format on\n#include \"tachyon/base/profiler.h\"\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_dense_polynomial_type_traits.h\"\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_evaluation_domain_type_traits.h\"\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_evaluations_type_traits.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/fr.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluation_domain.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluation_domain_factory.h\"\n\nnamespace tachyon::benchmark {\n\nusing namespace math;\n\ntemplate <typename Domain, typename PolyOrEvals,\n          typename RetPoly = std::conditional_t<\n              std::is_same_v<PolyOrEvals, typename Domain::Evals>,\n              typename Domain::DensePoly, typename Domain::Evals>>\nvoid Run(const FFTConfig& config) {\n  using F = typename Domain::Field;\n\n  std::string_view name;\n  if (config.run_ifft()) {\n    name = \"IFFT Benchmark GPU\";\n  } else {\n    name = \"FFT Benchmark GPU\";\n  }\n\n  SimpleReporter reporter;\n  reporter.set_title(name);\n  reporter.set_x_label(\"Degree (2ˣ)\");\n  reporter.set_column_labels(base::Map(\n      config.exponents(),\n      [](uint32_t exponent) { return base::NumberToString(exponent); }));\n\n  reporter.AddVendor(Vendor::TachyonCPU());\n  reporter.AddVendor(Vendor::TachyonGPU());\n\n  std::vector<size_t> degrees = config.GetDegrees();\n\n  FFTRunner<Domain, PolyOrEvals> runner(reporter);\n\n  for (size_t degree : degrees) {\n    PolyOrEvals input = PolyOrEvals::Random(degree);\n    std::unique_ptr<Domain> domain = Domain::Create(degree + 1);\n    bool kShouldRecord = true;\n\n    IcicleNTTHolder<F> icicle_ntt_holder = IcicleNTTHolder<F>::Create();\n    CHECK(icicle_ntt_holder->Init(domain->group_gen()));\n\n    RetPoly cpu_result, gpu_result;\n    if constexpr (std::is_same_v<PolyOrEvals, typename Domain::Evals>) {\n      runner.Run(Vendor::TachyonCPU(),\n                 tachyon_bn254_univariate_evaluation_domain_ifft_inplace,\n                 domain.get(), input, kShouldRecord, cpu_result);\n\n      domain->set_icicle(&icicle_ntt_holder);\n\n      runner.Run(Vendor::TachyonGPU(),\n                 tachyon_bn254_univariate_evaluation_domain_ifft_inplace,\n                 domain.get(), input, kShouldRecord, gpu_result);\n      // NOLINTNEXTLINE(readability/braces)\n    } else if constexpr (std::is_same_v<PolyOrEvals,\n                                        typename Domain::DensePoly>) {\n      runner.Run(Vendor::TachyonCPU(),\n                 tachyon_bn254_univariate_evaluation_domain_fft_inplace,\n                 domain.get(), input, kShouldRecord, cpu_result);\n\n      domain->set_icicle(&icicle_ntt_holder);\n\n      runner.Run(Vendor::TachyonGPU(),\n                 tachyon_bn254_univariate_evaluation_domain_fft_inplace,\n                 domain.get(), input, kShouldRecord, gpu_result);\n    }\n    if (config.check_results()) {\n      CHECK_EQ(cpu_result, gpu_result) << \"Results do not match\";\n    }\n  }\n\n  reporter.Show();\n}\n\nint RealMain(int argc, char** argv) {\n  using Field = bn254::Fr;\n  constexpr size_t kMaxDegree = SIZE_MAX - 1;\n  using Domain = UnivariateEvaluationDomain<Field, kMaxDegree>;\n  using DensePoly = Domain::DensePoly;\n  using Evals = Domain::Evals;\n\n  base::FilePath tmp_file;\n  CHECK(base::GetTempDir(&tmp_file));\n  tmp_file = tmp_file.Append(\"fft_benchmark_gpu.perfetto-trace\");\n  base::Profiler profiler({tmp_file});\n\n  profiler.Init();\n  profiler.Start();\n\n  Field::Init();\n\n  FFTConfig config;\n  if (!config.Parse(argc, argv)) {\n    return 1;\n  }\n\n  if (config.run_ifft()) {\n    Run<Domain, Evals>(config);\n  } else {\n    Run<Domain, DensePoly>(config);\n  }\n\n  return 0;\n}\n\n}  // namespace tachyon::benchmark\n\nint main(int argc, char** argv) {\n  return tachyon::benchmark::RealMain(argc, argv);\n}\n#else\n#include \"tachyon/base/console/iostream.h\"\n\nint main(int argc, char **argv) {\n  tachyon_cerr << \"please build with --config cuda\" << std::endl;\n  return 1;\n}\n#endif  // TACHYON_CUDA\n"
  },
  {
    "path": "benchmark/fft/fft_config.cc",
    "content": "#include \"benchmark/fft/fft_config.h\"\n\n#include <set>\n\n#include \"tachyon/base/console/iostream.h\"\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/ranges/algorithm.h\"\n\nnamespace tachyon::benchmark {\n\nFFTConfig::FFTConfig() : FFTConfig(Options()) {}\n\nFFTConfig::FFTConfig(const Options& options)\n    : Config(options), include_vendors_(options.include_vendors) {\n  parser_.AddFlag<base::Flag<std::vector<uint32_t>>>(&exponents_)\n      .set_short_name(\"-k\")\n      .set_required()\n      .set_help(\n          \"Specify the exponent 'k's where the degree of poly to test is 2ᵏ.\");\n  parser_.AddFlag<base::BoolFlag>(&run_ifft_)\n      .set_long_name(\"--run_ifft\")\n      .set_default_value(false)\n      .set_help(\n          \"Run IFFT benchmark. Default is FFT benchmark. By default, false.\");\n  if (include_vendors_) {\n    parser_.AddFlag<base::Flag<std::set<Vendor>>>(&vendors_)\n        .set_long_name(\"--vendor\")\n        .set_help(\n            \"Vendors to be benchmarked with. (supported vendors: arkworks, \"\n            \"bellman, halo2)\");\n  }\n}\n\nvoid FFTConfig::PostParse() {\n  base::ranges::sort(exponents_);  // NOLINT(build/include_what_you_use)\n}\n\nbool FFTConfig::Validate() const {\n  if (include_vendors_) {\n    for (const Vendor vendor : vendors_) {\n      if ((vendor.value() != Vendor::kArkworks) &&\n          (vendor.value() != Vendor::kBellman) &&\n          (vendor.value() != Vendor::kScrollHalo2)) {\n        tachyon_cerr << \"Unsupported vendor \" << vendor.ToString() << std::endl;\n        return false;\n      }\n    }\n  }\n  return true;\n}\n\nstd::vector<size_t> FFTConfig::GetDegrees() const {\n  return base::Map(exponents_, [](uint32_t exponent) {\n    return (size_t{1} << exponent) - 1;\n  });\n}\n\n}  // namespace tachyon::benchmark\n"
  },
  {
    "path": "benchmark/fft/fft_config.h",
    "content": "#ifndef BENCHMARK_FFT_FFT_CONFIG_H_\n#define BENCHMARK_FFT_FFT_CONFIG_H_\n\n#include <stdint.h>\n\n#include <vector>\n\n// clang-format off\n#include \"benchmark/config.h\"\n// clang-format on\n\nnamespace tachyon::benchmark {\n\nclass FFTConfig : public Config {\n public:\n  struct Options : public Config::Options {\n    bool include_vendors = false;\n  };\n\n  FFTConfig();\n  explicit FFTConfig(const Options& options);\n  FFTConfig(const FFTConfig& other) = delete;\n  FFTConfig& operator=(const FFTConfig& other) = delete;\n\n  const std::vector<uint32_t>& exponents() const { return exponents_; }\n  bool run_ifft() const { return run_ifft_; }\n\n  std::vector<size_t> GetDegrees() const;\n\n private:\n  // Config methods\n  void PostParse() override;\n  bool Validate() const override;\n\n  std::vector<uint32_t> exponents_;\n  bool run_ifft_;\n  bool include_vendors_;\n};\n\n}  // namespace tachyon::benchmark\n\n#endif  // BENCHMARK_FFT_FFT_CONFIG_H_\n"
  },
  {
    "path": "benchmark/fft/fft_runner.h",
    "content": "#ifndef BENCHMARK_FFT_FFT_RUNNER_H_\n#define BENCHMARK_FFT_FFT_RUNNER_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <memory>\n#include <optional>\n#include <utility>\n#include <vector>\n\n// clang-format off\n#include \"benchmark/simple_reporter.h\"\n// clang-format on\n#include \"tachyon/base/functional/functor_traits.h\"\n#include \"tachyon/base/time/time.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/fr_type_traits.h\"\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_evaluation_domain.h\"\n\n#if TACHYON_CUDA\n#include \"tachyon/math/polynomials/univariate/icicle/icicle_ntt_holder.h\"\n#endif\n\nnamespace tachyon::benchmark {\n\n// NOTE(TomTaehoonKim): |PolyOrEvals| is the type of the input polynomial\n// |polys_|. It can be either |Evals|, which is in the evaluation form, or\n// |DensePoly|, which is in the coefficient form. |FFTRunner| is implemented\n// this way to avoid duplication of the code for benchmarking FFT and IFFT.\ntemplate <typename Domain, typename PolyOrEvals>\nclass FFTRunner {\n public:\n  using F = typename Domain::Field;\n  typedef tachyon_bn254_fr* (*FFTExternalFn)(\n      const tachyon_bn254_fr* coeffs, size_t n,\n      const tachyon_bn254_fr* omega_or_omega_inv, uint32_t k,\n      uint64_t* duration_in_us);\n\n  explicit FFTRunner(SimpleReporter& reporter) : reporter_(reporter) {}\n\n  template <typename Fn, typename RetPoly,\n            typename FunctorTraits = base::internal::MakeFunctorTraits<Fn>,\n            typename RunType = typename FunctorTraits::RunType,\n            typename ArgList = base::internal::ExtractArgs<RunType>,\n            typename CRetPoly = std::conditional_t<\n                std::is_same_v<RetPoly, typename Domain::Evals>,\n                tachyon_bn254_univariate_evaluations,\n                tachyon_bn254_univariate_dense_polynomial>>\n  void Run(Vendor vendor, Fn fn, Domain* domain, const PolyOrEvals& input,\n           bool should_record, RetPoly& result) {\n    PolyOrEvals poly = input;\n\n    base::TimeTicks now = base::TimeTicks::Now();\n    std::unique_ptr<CRetPoly> ret;\n    ret.reset(fn(c::base::c_cast(domain), c::base::c_cast(&poly)));\n    if (should_record) {\n      reporter_.AddTime(vendor, (base::TimeTicks::Now() - now));\n    }\n\n    result = std::move(*c::base::native_cast(ret.get()));\n  }\n\n  template <typename RetPoly>\n  void RunExternal(Vendor vendor, FFTExternalFn fn, Domain* domain,\n                   const PolyOrEvals& input, RetPoly& result) const {\n    uint64_t duration_in_us;\n\n    std::unique_ptr<F> ret;\n    if constexpr (std::is_same_v<PolyOrEvals, typename Domain::Evals>) {\n      const F& omega_inv = domain->group_gen_inv();\n      ret.reset(c::base::native_cast(\n          fn(c::base::c_cast(input.evaluations().data()), input.NumElements(),\n             c::base::c_cast(&omega_inv), domain->log_size_of_group(),\n             &duration_in_us)));\n      std::vector<F> res_vec(ret.get(), ret.get() + input.NumElements());\n      reporter_.AddTime(vendor, base::Microseconds(duration_in_us));\n      result = RetPoly(typename RetPoly::Coefficients(std::move(res_vec)));\n      // NOLINTNEXTLINE(readability/braces)\n    } else if constexpr (std::is_same_v<PolyOrEvals,\n                                        typename Domain::DensePoly>) {\n      const F& omega = domain->group_gen();\n      ret.reset(c::base::native_cast(\n          fn(c::base::c_cast(input.coefficients().coefficients().data()),\n             input.NumElements(), c::base::c_cast(&omega),\n             domain->log_size_of_group(), &duration_in_us)));\n      std::vector<F> res_vec(ret.get(), ret.get() + input.NumElements());\n      reporter_.AddTime(vendor, base::Microseconds(duration_in_us));\n      result = RetPoly(std::move(res_vec));\n    }\n  }\n\n private:\n  SimpleReporter& reporter_;\n};\n\n}  // namespace tachyon::benchmark\n\n#endif  // BENCHMARK_FFT_FFT_RUNNER_H_\n"
  },
  {
    "path": "benchmark/fft/halo2/BUILD.bazel",
    "content": "load(\"@crate_index//:defs.bzl\", \"aliases\", \"all_crate_deps\")\nload(\"//bazel:tachyon_rust.bzl\", \"tachyon_rust_static_library\")\n\ntachyon_rust_static_library(\n    name = \"halo2\",\n    srcs = glob([\"src/**/*.rs\"]),\n    aliases = aliases(),\n    proc_macro_deps = all_crate_deps(proc_macro = True),\n    visibility = [\"//benchmark/fft:__pkg__\"],\n    deps = all_crate_deps(normal = True) + [\n        \"//tachyon/rs:tachyon_rs\",\n    ],\n)\n"
  },
  {
    "path": "benchmark/fft/halo2/Cargo.toml",
    "content": "[package]\nname = \"halo2_fft_benchmark\"\nversion = \"0.0.1\"\nauthors = [\"The Tachyon Authors <tachyon-discuss@kroma.network>\"]\nedition = \"2021\"\nrust-version = \"1.56.1\"\ndescription = \"\"\"\nHalo2 FFT Benchmark\n\"\"\"\nlicense = \"MIT OR Apache-2.0\"\nrepository = \"https://github.com/kroma-network/tachyon\"\nreadme = \"README.md\"\ncategories = [\"cryptography\"]\nkeywords = [\"tachyon\", \"benchmark\", \"halo2\"]\npublish = false\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[dependencies]\nhalo2_proofs = { git = \"https://github.com/scroll-tech/halo2.git\", rev = \"e5ddf67\" }\ntachyon_rs = { path = \"../../../tachyon/rs\" }\n"
  },
  {
    "path": "benchmark/fft/halo2/src/lib.rs",
    "content": "use halo2_proofs::arithmetic::{best_fft, parallelize, Field};\nuse halo2_proofs::halo2curves::bn256::Fr;\nuse std::{mem, slice, time::Instant};\nuse tachyon_rs::math::elliptic_curves::bn::bn254::Fr as CppFr;\n\n#[no_mangle]\npub extern \"C\" fn run_fft_halo2(\n    coeffs: *const CppFr,\n    n: usize,\n    omega: *const CppFr,\n    k: u32,\n    duration: *mut u64,\n) -> *mut CppFr {\n    let ret = unsafe {\n        let coeffs: &[CppFr] = slice::from_raw_parts(coeffs, n);\n\n        let coeffs: &[Fr] = mem::transmute(coeffs);\n        let mut coeffs = coeffs.to_vec();\n        let omega: Fr = mem::transmute(*omega);\n\n        let start = Instant::now();\n        best_fft(&mut coeffs, omega, k);\n        duration.write(start.elapsed().as_micros() as u64);\n        coeffs\n    };\n    Box::into_raw(ret.into_boxed_slice()) as *mut CppFr\n}\n\n#[no_mangle]\npub extern \"C\" fn run_ifft_halo2(\n    coeffs: *const CppFr,\n    n: usize,\n    omega_inv: *const CppFr,\n    k: u32,\n    duration: *mut u64,\n) -> *mut CppFr {\n    let ret = unsafe {\n        let coeffs: &[CppFr] = slice::from_raw_parts(coeffs, n);\n\n        let coeffs: &[Fr] = mem::transmute(coeffs);\n        let mut coeffs = coeffs.to_vec();\n        let omega_inv: Fr = mem::transmute(*omega_inv);\n        let divisor = Fr::from(1 << k).invert().unwrap();\n\n        let start = Instant::now();\n        best_fft(&mut coeffs, omega_inv, k);\n        parallelize(&mut coeffs, |coeffs, _| {\n            for coeff in coeffs {\n                *coeff *= divisor;\n            }\n        });\n        duration.write(start.elapsed().as_micros() as u64);\n        coeffs\n    };\n    Box::into_raw(ret.into_boxed_slice()) as *mut CppFr\n}\n"
  },
  {
    "path": "benchmark/fft_batch/BUILD.bazel",
    "content": "load(\n    \"//bazel:tachyon_cc.bzl\",\n    \"tachyon_cc_binary\",\n    \"tachyon_cc_library\",\n)\n\ntachyon_cc_library(\n    name = \"fft_batch_config\",\n    testonly = True,\n    srcs = [\"fft_batch_config.cc\"],\n    hdrs = [\"fft_batch_config.h\"],\n    deps = [\n        \"//benchmark:config\",\n        \"//benchmark:field_type\",\n        \"//tachyon/base/console\",\n        \"//tachyon/base/containers:container_util\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"fft_batch_runner\",\n    testonly = True,\n    hdrs = [\"fft_batch_runner.h\"],\n    deps = [\n        \":fft_batch_config\",\n        \"//benchmark:simple_reporter\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/base/time\",\n        \"//tachyon/c/math/matrix\",\n        \"//tachyon/math/matrix:matrix_types\",\n        \"//tachyon/math/polynomials/univariate:radix2_evaluation_domain\",\n    ],\n)\n\ntachyon_cc_binary(\n    name = \"fft_batch_benchmark\",\n    testonly = True,\n    srcs = [\"fft_batch_benchmark.cc\"],\n    deps = [\n        \":fft_batch_config\",\n        \":fft_batch_runner\",\n        \"//benchmark:simple_reporter\",\n        \"//benchmark/fft_batch/plonky3\",\n        \"//tachyon/base:profiler\",\n        \"//tachyon/c/math/finite_fields/baby_bear\",\n        \"//tachyon/math/finite_fields:packed_field_traits_forward\",\n        \"//tachyon/math/matrix:matrix_types\",\n        \"//tachyon/math/polynomials/univariate:radix2_evaluation_domain\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n"
  },
  {
    "path": "benchmark/fft_batch/README.md",
    "content": "# FFTBatch/CosetLDEBatch Benchmark\n\n## CPU\n\n```\nRun on 13th Gen Intel(R) Core(TM) i9-13900K (32 X 5500 MHz CPU s)\nCompiler: clang-15\nCPU Caches:\n  L1 Data 48 KiB (x16)\n  L1 Instruction 32 KiB (x16)\n  L2 Unified 2048 KiB (x16)\n  L3 Unified 36864 KiB (x1)\n\nRun on Apple M3 Pro (12 X 4050 MHz)\nCPU Caches:\n  L1 Data 64 KiB (x12)\n  L1 Instruction 128 KiB (x12)\n  L2 Unified 4096 KiB (x12)\n```\n\nNote: Run with `build --@rules_rust//:extra_rustc_flags=\"-Ctarget-cpu=native\"` in your .bazelrc.user\n\n### FFTBatch\n\nWARNING: On Mac M3, tests beyond degree 24 are not feasible due to memory constraints.\n\n#### On Intel i9-13900K\n\n```shell\nGOMP_SPINCOUNT=0 bazel run --config maxopt --//:has_matplotlib //benchmark/fft_batch:fft_batch_benchmark -- -k 20 -k 21 -k 22 -k 23 -k 24 -k 25 -k 26 --vendor plonky3 -p baby_bear --check_results\n```\n\n| Exponent | Tachyon      | Plonky3      |\n| :------- | ------------ | ------------ |\n| 20       | **0.092595** | 0.094762     |\n| 21       | **0.191168** | 0.193567     |\n| 22       | 0.406239     | **0.384377** |\n| 23       | 0.892501     | **0.842694** |\n| 24       | 1.91177      | **1.90586**  |\n| 25       | **5.82862**  | 7.34128      |\n| 26       | **17.1807**  | 20.3968      |\n\n![image](/benchmark/fft_batch/fft_batch_benchmark_ubuntu_i9.png)\n\n#### On Mac M3 Pro\n\n```shell\nGOMP_SPINCOUNT=0 bazel run --config maxopt --//:has_matplotlib //benchmark/fft_batch:fft_batch_benchmark -- -k 20 -k 21 -k 22 -k 23 -k 24  --vendor plonky3 -p baby_bear --check_results\n```\n\n| Exponent | Tachyon  | Plonky3      |\n| :------- | -------- | ------------ |\n| 20       | 0.083416 | **0.066952** |\n| 21       | 0.194191 | **0.138168** |\n| 22       | 0.408045 | **0.299547** |\n| 23       | 0.955439 | **0.679252** |\n| 24       | 11.8495  | **6.47188**  |\n\n![image](/benchmark/fft_batch/fft_batch_benchmark_mac_m3.png)\n\n### CosetLDEBatch\n\nWARNING: On Intel i9-13900K, tests beyond degree 25 are not feasible due to memory constraints, and on Mac M3, tests beyond degree 24 are not feasible due to memory constraints.\n\n#### On Intel i9-13900K\n\n```shell\nGOMP_SPINCOUNT=0 bazel run --config maxopt --//:has_matplotlib //benchmark/fft_batch:fft_batch_benchmark -- -k 20 -k 21 -k 22 -k 23 -k 24 -k 25 --vendor plonky3 -p baby_bear --run_coset_lde --check_results\n```\n\n| Exponent | Tachyon     | Plonky3  |\n| :------- | ----------- | -------- |\n| 20       | **0.46917** | 0.639744 |\n| 21       | **0.92528** | 1.2923   |\n| 22       | **1.87363** | 2.68427  |\n| 23       | **4.06008** | 5.67987  |\n| 24       | **9.6627**  | 14.6164  |\n| 25       | **25.7953** | 39.5498  |\n\n![image](/benchmark/fft_batch/coset_lde_batch_benchmark_ubuntu_i9.png)\n\n#### On Mac M3 Pro\n\n```shell\nGOMP_SPINCOUNT=0 bazel run --config maxopt --//:has_matplotlib //benchmark/fft_batch:fft_batch_benchmark -- -k 20 -k 21 -k 22 -k 23 -k 24 --vendor plonky3 -p baby_bear --run_coset_lde --check_results\n```\n\n| Exponent | Tachyon      | Plonky3      |\n| :------- | ------------ | ------------ |\n| 20       | **0.318485** | 0.323865     |\n| 21       | 0.667106     | **0.660975** |\n| 22       | **1.44873**  | 3.40795      |\n| 23       | 8.27201      | **5.91238**  |\n| 24       | 39.9678      | **23.1033**  |\n\n![image](/benchmark/fft_batch/coset_lde_batch_benchmark_mac_m3.png)\n"
  },
  {
    "path": "benchmark/fft_batch/fft_batch_benchmark.cc",
    "content": "#include <string>\n#include <vector>\n\n#include \"absl/strings/substitute.h\"\n\n// clang-format off\n#include \"benchmark/fft_batch/fft_batch_config.h\"\n#include \"benchmark/fft_batch/fft_batch_runner.h\"\n#include \"benchmark/simple_reporter.h\"\n// clang-format on\n#include \"tachyon/base/profiler.h\"\n#include \"tachyon/c/math/finite_fields/baby_bear/baby_bear_type_traits.h\"\n#include \"tachyon/math/finite_fields/packed_field_traits_forward.h\"\n\nnamespace tachyon::benchmark {\n\nextern \"C\" tachyon_baby_bear* run_fft_batch_plonky3_baby_bear(\n    const tachyon_baby_bear* data, uint32_t n_log, size_t batch_size,\n    uint64_t* duration);\n\nextern \"C\" tachyon_baby_bear* run_coset_lde_batch_plonky3_baby_bear(\n    const tachyon_baby_bear* data, uint32_t n_log, size_t batch_size,\n    uint64_t* duration);\n\ntemplate <typename F>\nvoid CheckResults(bool check_results,\n                  const math::RowMajorMatrix<F>& tachyon_result,\n                  const math::RowMajorMatrix<F>& vendor_result) {\n  if (check_results) {\n    CHECK_EQ(tachyon_result, vendor_result) << \"Results do not match\";\n  }\n}\n\ntemplate <typename F>\nvoid Run(const FFTBatchConfig& config) {\n  using Domain = math::Radix2EvaluationDomain<F, SIZE_MAX - 1>;\n  using PackedPrimeField =\n      // NOLINTNEXTLINE(whitespace/operators)\n      std::conditional_t<F::Config::kModulusBits <= 32,\n                         typename math::PackedFieldTraits<F>::PackedField, F>;\n\n  PackedPrimeField::Init();\n\n  std::string name;\n  if (config.run_coset_lde()) {\n    name = absl::Substitute(\"CosetLDEBatch Benchmark (Batch Size: $0)\",\n                            config.batch_size());\n  } else {\n    name = absl::Substitute(\"FFTBatch Benchmark (Batch Size: $0)\",\n                            config.batch_size());\n  }\n\n  SimpleReporter reporter;\n  reporter.set_title(name);\n  reporter.set_x_label(\"Degree (2ˣ)\");\n  reporter.set_column_labels(base::Map(\n      config.exponents(),\n      [](uint32_t exponent) { return base::NumberToString(exponent); }));\n\n  std::vector<size_t> degrees = config.GetDegrees();\n\n  FFTBatchRunner<Domain> runner(reporter, config);\n\n  reporter.AddVendor(Vendor::Tachyon());\n  for (const Vendor vendor : config.vendors()) {\n    reporter.AddVendor(vendor);\n  }\n\n  for (size_t degree : degrees) {\n    math::RowMajorMatrix<F> input =\n        math::RowMajorMatrix<F>::Random(degree, config.batch_size());\n\n    math::RowMajorMatrix<F> tachyon_result =\n        runner.Run(Vendor::Tachyon(), config.run_coset_lde(), input);\n    for (const Vendor vendor : config.vendors()) {\n      math::RowMajorMatrix<F> vendor_result;\n      if (vendor.value() == Vendor::kPlonky3) {\n        if (config.run_coset_lde()) {\n          vendor_result = runner.RunExternal(\n              vendor, run_coset_lde_batch_plonky3_baby_bear, input);\n        } else {\n          vendor_result = runner.RunExternal(\n              vendor, run_fft_batch_plonky3_baby_bear, input);\n        }\n        CheckResults(config.check_results(), tachyon_result, vendor_result);\n      } else {\n        NOTREACHED();\n      }\n    }\n  }\n\n  reporter.Show();\n}\n\nint RealMain(int argc, char** argv) {\n  base::FilePath tmp_file;\n  CHECK(base::GetTempDir(&tmp_file));\n  tmp_file = tmp_file.Append(\"fft_batch_benchmark.perfetto-trace\");\n  base::Profiler profiler({tmp_file});\n\n  profiler.Init();\n  profiler.Start();\n\n  FFTBatchConfig config;\n  if (!config.Parse(argc, argv)) {\n    return 1;\n  }\n\n  if (config.prime_field().value() == FieldType::kBabyBear) {\n    Run<math::BabyBear>(config);\n  } else {\n    NOTREACHED();\n  }\n  return 0;\n}\n\n}  // namespace tachyon::benchmark\n\nint main(int argc, char** argv) {\n  return tachyon::benchmark::RealMain(argc, argv);\n}\n"
  },
  {
    "path": "benchmark/fft_batch/fft_batch_config.cc",
    "content": "#include \"benchmark/fft_batch/fft_batch_config.h\"\n\n#include <set>\n\n#include \"tachyon/base/console/iostream.h\"\n#include \"tachyon/base/containers/container_util.h\"\n\nnamespace tachyon::benchmark {\n\nFFTBatchConfig::FFTBatchConfig() {\n  parser_.AddFlag<base::Flag<std::vector<uint32_t>>>(&exponents_)\n      .set_short_name(\"-k\")\n      .set_required()\n      .set_help(\n          \"Specify the exponent 'k's where the degree of poly to test is 2ᵏ.\");\n  parser_.AddFlag<base::BoolFlag>(&run_coset_lde_)\n      .set_long_name(\"--run_coset_lde\")\n      .set_default_value(false)\n      .set_help(\"Run CosetLDE benchmark. Default is FFT benchmark.\");\n  parser_.AddFlag<base::Flag<size_t>>(&batch_size_)\n      .set_short_name(\"-b\")\n      .set_long_name(\"--batch_size\")\n      .set_default_value(100)\n      .set_help(\"Specify the batch size. By default, 100.\");\n  parser_.AddFlag<base::Flag<FieldType>>(&prime_field_)\n      .set_short_name(\"-p\")\n      .set_long_name(\"--prime_field\")\n      .set_default_value(FieldType::BabyBear())\n      .set_help(\n          \"A prime field to be benchmarked with. (supported prime fields: \"\n          \"baby_bear\");\n  parser_.AddFlag<base::Flag<std::set<Vendor>>>(&vendors_)\n      .set_long_name(\"--vendor\")\n      .set_help(\"Vendors to be benchmarked with. (supported vendors: plonky3\");\n}\n\nvoid FFTBatchConfig::PostParse() {\n  base::ranges::sort(exponents_);  // NOLINT(build/include_what_you_use)\n}\n\nstd::vector<size_t> FFTBatchConfig::GetDegrees() const {\n  return base::Map(exponents_,\n                   [](uint32_t exponent) { return (size_t{1} << exponent); });\n}\n\nbool FFTBatchConfig::Validate() const {\n  for (const Vendor vendor : vendors_) {\n    if (vendor.value() != Vendor::kPlonky3) {\n      tachyon_cerr << \"Unsupported vendor \" << vendor.ToString() << std::endl;\n      return false;\n    }\n  }\n  if (prime_field_.value() != FieldType::kBabyBear) {\n    tachyon_cerr << \"Unsupported prime field \" << prime_field_.ToString()\n                 << std::endl;\n    return false;\n  }\n  return true;\n}\n\n}  // namespace tachyon::benchmark\n"
  },
  {
    "path": "benchmark/fft_batch/fft_batch_config.h",
    "content": "#ifndef BENCHMARK_FFT_BATCH_FFT_BATCH_CONFIG_H_\n#define BENCHMARK_FFT_BATCH_FFT_BATCH_CONFIG_H_\n\n#include <stddef.h>\n\n#include <vector>\n\n// clang-format off\n#include \"benchmark/config.h\"\n#include \"benchmark/field_type.h\"\n// clang-format on\n\nnamespace tachyon::benchmark {\n\nclass FFTBatchConfig : public Config {\n public:\n  FFTBatchConfig();\n  FFTBatchConfig(const FFTBatchConfig& other) = delete;\n  FFTBatchConfig& operator=(const FFTBatchConfig& other) = delete;\n\n  const std::vector<uint32_t>& exponents() const { return exponents_; }\n  size_t batch_size() const { return batch_size_; }\n  bool run_coset_lde() const { return run_coset_lde_; }\n  FieldType prime_field() const { return prime_field_; }\n\n  std::vector<size_t> GetDegrees() const;\n\n private:\n  // Config methods\n  void PostParse() override;\n  bool Validate() const override;\n\n  std::vector<uint32_t> exponents_;\n  bool run_coset_lde_;\n  size_t batch_size_;\n  FieldType prime_field_;\n};\n\n}  // namespace tachyon::benchmark\n\n#endif  // BENCHMARK_FFT_BATCH_FFT_BATCH_CONFIG_H_\n"
  },
  {
    "path": "benchmark/fft_batch/fft_batch_runner.h",
    "content": "#ifndef BENCHMARK_FFT_BATCH_FFT_BATCH_RUNNER_H_\n#define BENCHMARK_FFT_BATCH_FFT_BATCH_RUNNER_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <memory>\n#include <utility>\n#include <vector>\n\n// clang-format off\n#include \"benchmark/fft_batch/fft_batch_config.h\"\n#include \"benchmark/simple_reporter.h\"\n// clang-format on\n#include \"tachyon/base/time/time.h\"\n#include \"tachyon/c/math/matrix/baby_bear_row_major_matrix_type_traits.h\"\n#include \"tachyon/math/matrix/matrix_types.h\"\n#include \"tachyon/math/polynomials/univariate/radix2_evaluation_domain.h\"\n\nnamespace tachyon::benchmark {\n\ntemplate <typename Domain>\nclass FFTBatchRunner {\n public:\n  using F = typename Domain::Field;\n\n  typedef tachyon_baby_bear* (*ExternalFn)(const tachyon_baby_bear* data,\n                                           uint32_t n_log, size_t batch_size,\n                                           uint64_t* duration);\n  FFTBatchRunner(SimpleReporter& reporter, const FFTBatchConfig& config)\n      : reporter_(reporter), config_(config) {}\n\n  math::RowMajorMatrix<F> Run(Vendor vendor, bool run_coset_lde,\n                              const math::RowMajorMatrix<F>& input) {\n    math::RowMajorMatrix<F> result;\n    std::unique_ptr<Domain> domain =\n        Domain::Create(static_cast<size_t>(input.rows()));\n    base::TimeTicks start;\n    if (run_coset_lde) {\n      const size_t kAddedBits = 1;\n      result =\n          math::RowMajorMatrix<F>(input.rows() << kAddedBits, input.cols());\n      start = base::TimeTicks::Now();\n      domain->CosetLDEBatch(input, kAddedBits,\n                            F::FromMontgomery(F::Config::kSubgroupGenerator),\n                            result);\n    } else {\n      result = input;\n      start = base::TimeTicks::Now();\n      domain->FFTBatch(result);\n    }\n    reporter_.AddTime(vendor, base::TimeTicks::Now() - start);\n    return result;\n  }\n\n  math::RowMajorMatrix<F> RunExternal(Vendor vendor, ExternalFn fn,\n                                      const math::RowMajorMatrix<F>& input) {\n    uint64_t duration_in_us;\n    std::unique_ptr<tachyon_baby_bear_row_major_matrix> ret;\n    tachyon_baby_bear* data = fn(c::base::c_cast(input.data()), input.rows(),\n                                 input.cols(), &duration_in_us);\n    ret.reset(tachyon_baby_bear_row_major_matrix_create(data, input.rows(),\n                                                        input.cols()));\n    reporter_.AddTime(vendor, base::Microseconds(duration_in_us));\n    return std::move(*c::base::native_cast(ret.get()));\n  }\n\n private:\n  SimpleReporter& reporter_;\n  const FFTBatchConfig& config_;\n};\n\n}  // namespace tachyon::benchmark\n\n#endif  // BENCHMARK_FFT_BATCH_FFT_BATCH_RUNNER_H_\n"
  },
  {
    "path": "benchmark/fft_batch/plonky3/BUILD.bazel",
    "content": "load(\"@crate_index//:defs.bzl\", \"aliases\", \"all_crate_deps\")\nload(\"//bazel:tachyon_rust.bzl\", \"tachyon_rust_static_library\")\n\ntachyon_rust_static_library(\n    name = \"plonky3\",\n    srcs = glob([\"src/**/*.rs\"]),\n    aliases = aliases(),\n    proc_macro_deps = all_crate_deps(proc_macro = True),\n    visibility = [\"//benchmark/fft_batch:__pkg__\"],\n    deps = all_crate_deps(normal = True) + [\n        \"//tachyon/rs:tachyon_rs\",\n    ],\n)\n"
  },
  {
    "path": "benchmark/fft_batch/plonky3/Cargo.toml",
    "content": "[package]\nname = \"plonky3_batch_fft_benchmark\"\nversion = \"0.0.1\"\nauthors = [\"The Tachyon Authors <tachyon-discuss@kroma.network>\"]\nedition = \"2021\"\ndescription = \"\"\"\nPlonky3 FFT Batch Benchmark\n\"\"\"\nlicense = \"MIT OR Apache-2.0\"\nrepository = \"https://github.com/kroma-network/tachyon\"\nreadme = \"README.md\"\ncategories = [\"cryptography\"]\nkeywords = [\"tachyon\", \"benchmark\", \"plonky3\"]\npublish = false\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[dependencies]\np3-baby-bear = \"0.1.3-succinct\"\np3-dft = \"0.1.3-succinct\"\np3-field = \"0.1.3-succinct\"\np3-matrix = \"0.1.3-succinct\"\ntachyon_rs = { path = \"../../../tachyon/rs\" }\n"
  },
  {
    "path": "benchmark/fft_batch/plonky3/src/lib.rs",
    "content": "use p3_baby_bear::BabyBear;\nuse p3_dft::{Radix2DitParallel, TwoAdicSubgroupDft};\nuse p3_field::AbstractField;\nuse p3_matrix::{dense::RowMajorMatrix, Matrix};\nuse std::time::Instant;\nuse tachyon_rs::math::finite_fields::baby_bear::BabyBear as CppBabyBear;\n\n#[no_mangle]\npub extern \"C\" fn run_fft_batch_plonky3_baby_bear(\n    data: *const BabyBear,\n    n: usize,\n    batch_size: usize,\n    duration: *mut u64,\n) -> *mut CppBabyBear {\n    let size = n * batch_size;\n    let values: Vec<BabyBear> = unsafe { Vec::from_raw_parts(data as *mut BabyBear, size, size) };\n\n    let messages = RowMajorMatrix::<BabyBear>::new(values, batch_size);\n    let dft = Radix2DitParallel::default();\n\n    let start = Instant::now();\n    let dft_result = dft.dft_batch(messages).to_row_major_matrix();\n    unsafe {\n        duration.write(start.elapsed().as_micros() as u64);\n    }\n    Box::into_raw(dft_result.values.into_boxed_slice()) as *mut CppBabyBear\n}\n\n#[no_mangle]\npub extern \"C\" fn run_coset_lde_batch_plonky3_baby_bear(\n    data: *const BabyBear,\n    n: usize,\n    batch_size: usize,\n    duration: *mut u64,\n) -> *mut CppBabyBear {\n    let size = n * batch_size;\n    let values: Vec<BabyBear> =\n        unsafe { std::slice::from_raw_parts(data as *mut BabyBear, size).to_vec() };\n\n    let messages = RowMajorMatrix::<BabyBear>::new(values, batch_size);\n    let dft = Radix2DitParallel::default();\n\n    let start = Instant::now();\n    let shift = BabyBear::generator();\n    let dft_result = dft\n        .coset_lde_batch(messages, 1, shift)\n        .to_row_major_matrix();\n    unsafe {\n        duration.write(start.elapsed().as_micros() as u64);\n    }\n    Box::into_raw(dft_result.values.into_boxed_slice()) as *mut CppBabyBear\n}\n"
  },
  {
    "path": "benchmark/field_type.h",
    "content": "#ifndef BENCHMARK_FIELD_TYPE_H_\n#define BENCHMARK_FIELD_TYPE_H_\n\n#include <stdint.h>\n\n#include <string>\n#include <string_view>\n\n#include \"tachyon/base/flag/flag_value_traits.h\"\n#include \"tachyon/base/logging.h\"\n\nnamespace tachyon {\nnamespace benchmark {\n\nclass FieldType {\n public:\n  enum Value : uint32_t {\n    // clang-format off\n    kBabyBear  = 1 << 1,\n    kBn254Fr   = 1 << 2,\n    // clang-format on\n  };\n\n  constexpr static FieldType BabyBear() { return FieldType(kBabyBear); }\n  constexpr static FieldType Bn254Fr() { return FieldType(kBn254Fr); }\n\n  FieldType() = default;\n\n  constexpr Value value() const { return value_; }\n  constexpr bool operator==(FieldType a) const { return value_ == a.value_; }\n  constexpr bool operator!=(FieldType a) const { return value_ != a.value_; }\n\n  std::string_view ToString() const {\n    switch (value_) {\n      case FieldType::kBabyBear:\n        return \"baby_bear\";\n      case FieldType::kBn254Fr:\n        return \"bn254_fr\";\n    }\n    NOTREACHED();\n    return \"\";\n  }\n\n private:\n  explicit constexpr FieldType(Value v) : value_(v) {}\n\n  Value value_;\n};\n\n}  // namespace benchmark\n\nnamespace base {\n\ntemplate <>\nclass FlagValueTraits<benchmark::FieldType> {\n public:\n  using FieldType = benchmark::FieldType;\n\n  static bool ParseValue(std::string_view input, FieldType* value,\n                         std::string* reason) {\n    if (input == \"baby_bear\") {\n      *value = FieldType::BabyBear();\n    } else if (input == \"bn254_fr\") {\n      *value = FieldType::Bn254Fr();\n    } else {\n      *reason = absl::Substitute(\"Unknown prime field: $0\", input);\n      return false;\n    }\n    return true;\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // BENCHMARK_FIELD_TYPE_H_\n"
  },
  {
    "path": "benchmark/fri/BUILD.bazel",
    "content": "load(\n    \"//bazel:tachyon_cc.bzl\",\n    \"tachyon_cc_binary\",\n    \"tachyon_cc_library\",\n)\n\ntachyon_cc_library(\n    name = \"fri_config\",\n    testonly = True,\n    srcs = [\"fri_config.cc\"],\n    hdrs = [\"fri_config.h\"],\n    deps = [\n        \"//benchmark:config\",\n        \"//tachyon/base/console\",\n        \"//tachyon/base/containers:container_util\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"fri_runner\",\n    testonly = True,\n    hdrs = [\"fri_runner.h\"],\n    deps = [\n        \":fri_config\",\n        \"//benchmark:simple_reporter\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/base/time\",\n        \"//tachyon/c/math/matrix\",\n        \"//tachyon/math/matrix:matrix_types\",\n    ],\n)\n\ntachyon_cc_binary(\n    name = \"fri_benchmark\",\n    testonly = True,\n    srcs = [\"fri_benchmark.cc\"],\n    deps = [\n        \":fri_config\",\n        \":fri_runner\",\n        \"//benchmark:simple_reporter\",\n        \"//benchmark/fri/plonky3\",\n        \"//tachyon/base:profiler\",\n        \"//tachyon/c/math/finite_fields/baby_bear\",\n        \"//tachyon/crypto/challenger:duplex_challenger\",\n        \"//tachyon/crypto/commitments/fri:fri_config\",\n        \"//tachyon/crypto/commitments/fri:two_adic_fri\",\n        \"//tachyon/crypto/commitments/fri:two_adic_multiplicative_coset\",\n        \"//tachyon/crypto/commitments/merkle_tree/field_merkle_tree:extension_field_merkle_tree_mmcs\",\n        \"//tachyon/crypto/commitments/merkle_tree/field_merkle_tree:field_merkle_tree_mmcs\",\n        \"//tachyon/crypto/hashes/sponge:padding_free_sponge\",\n        \"//tachyon/crypto/hashes/sponge:truncated_permutation\",\n        \"//tachyon/crypto/hashes/sponge/poseidon2\",\n        \"//tachyon/crypto/hashes/sponge/poseidon2:poseidon2_params\",\n        \"//tachyon/crypto/hashes/sponge/poseidon2/param_traits:poseidon2_baby_bear\",\n        \"//tachyon/math/finite_fields:packed_field_traits_forward\",\n        \"//tachyon/math/finite_fields/baby_bear:baby_bear4\",\n        \"//tachyon/math/finite_fields/baby_bear:packed_baby_bear4\",\n        \"//tachyon/math/matrix:matrix_types\",\n        \"//tachyon/math/polynomials/univariate:radix2_evaluation_domain\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n"
  },
  {
    "path": "benchmark/fri/README.md",
    "content": "# FRI Benchmark\n\n## CPU\n\n```\nRun on 13th Gen Intel(R) Core(TM) i9-13900K (32 X 5500 MHz CPU s)\nCompiler: clang-15\nCPU Caches:\n  L1 Data 48 KiB (x16)\n  L1 Instruction 32 KiB (x16)\n  L2 Unified 2048 KiB (x16)\n  L3 Unified 36864 KiB (x1)\n\nRun on Apple M3 Pro (12 X 4050 MHz)\nCPU Caches:\n  L1 Data 64 KiB (x12)\n  L1 Instruction 128 KiB (x12)\n  L2 Unified 4096 KiB (x12)\n```\n\nNote: Run with `build --@rules_rust//:extra_rustc_flags=\"-Ctarget-cpu=native\"` in your .bazelrc.user\n\n```shell\nGOMP_SPINCOUNT=0 bazel run --config maxopt --//:has_matplotlib //benchmark/fri:fri_benchmark -- -k 18 -k 19 -k 20 -k 21 -k 22 --batch_size 100 --input_num 4 --round_num 4 --log_blowup 2 --vendor plonky3 --check_results\n```\n\n## On Intel i9-13900K\n\n| Exponent | Tachyon     | Plonky3 |\n| :------- | ----------- | ------- |\n| 18       | **1.59124** | 2.36518 |\n| 19       | **2.87866** | 4.65791 |\n| 20       | **6.06711** | 9.5114  |\n| 21       | **12.1177** | 19.0475 |\n| 22       | **24.4839** | 38.4716 |\n\n![image](/benchmark/fri/fri_benchmark_ubuntu_i9.png)\n\n## On Mac M3 Pro\n\nWARNING: On Mac M3, high degree tests are not feasible due to memory constraints.\n\n| Exponent | Tachyon | Plonky3 |\n| :------- | ------- | ------- |\n| 18       | 3.96588 | 2.92354 |\n| 19       | 7.95329 | 5.89079 |\n| 20       | 15.8636 | 11.8225 |\n| 21       | 46.1967 | 34.4965 |\n| 22       | 182.084 | 124.7   |\n\n![image](/benchmark/fri/fri_benchmark_mac_m3.png)\n"
  },
  {
    "path": "benchmark/fri/fri_benchmark.cc",
    "content": "#include <iostream>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"absl/strings/substitute.h\"\n\n// clang-format off\n#include \"benchmark/simple_reporter.h\"\n#include \"benchmark/fri/fri_config.h\"\n#include \"benchmark/fri/fri_runner.h\"\n// clang-format on\n#include \"tachyon/base/profiler.h\"\n#include \"tachyon/c/math/finite_fields/baby_bear/baby_bear_type_traits.h\"\n#include \"tachyon/crypto/challenger/duplex_challenger.h\"\n#include \"tachyon/crypto/commitments/fri/fri_config.h\"\n#include \"tachyon/crypto/commitments/fri/two_adic_fri.h\"\n#include \"tachyon/crypto/commitments/fri/two_adic_multiplicative_coset.h\"\n#include \"tachyon/crypto/commitments/merkle_tree/field_merkle_tree/extension_field_merkle_tree_mmcs.h\"\n#include \"tachyon/crypto/commitments/merkle_tree/field_merkle_tree/field_merkle_tree_mmcs.h\"\n#include \"tachyon/crypto/hashes/sponge/padding_free_sponge.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/param_traits/poseidon2_baby_bear.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_params.h\"\n#include \"tachyon/crypto/hashes/sponge/truncated_permutation.h\"\n#include \"tachyon/math/finite_fields/baby_bear/baby_bear4.h\"\n#include \"tachyon/math/finite_fields/baby_bear/packed_baby_bear4.h\"\n#include \"tachyon/math/matrix/matrix_types.h\"\n#include \"tachyon/math/polynomials/univariate/radix2_evaluation_domain.h\"\n\nnamespace tachyon::benchmark {\n\nextern \"C\" tachyon_baby_bear* run_fri_plonky3_baby_bear(\n    const tachyon_baby_bear* data, size_t input_num, size_t round_num,\n    size_t max_degree, size_t batch_size, uint32_t log_blowup,\n    uint64_t* duration);\n\ntemplate <typename Result>\nvoid CheckResult(bool check_results, const Result& tachyon_result,\n                 const Result& vendor_result) {\n  if (check_results) {\n    CHECK_EQ(tachyon_result, vendor_result) << \"Results do not match\";\n  }\n}\n\nvoid Run(const FRIConfig& config) {\n  constexpr size_t kRate = 8;\n  constexpr size_t kChunk = 8;\n  constexpr size_t kN = 2;\n\n  using F = math::BabyBear;\n  using ExtF = math::BabyBear4;\n  using PackedF = math::PackedBabyBear;\n  using ExtPackedF = math::PackedBabyBear4;\n  using Params =\n      crypto::Poseidon2Params<crypto::Poseidon2Vendor::kHorizen,\n                              crypto::Poseidon2Vendor::kPlonky3, F, 15, 7>;\n  using PackedParams =\n      crypto::Poseidon2Params<crypto::Poseidon2Vendor::kHorizen,\n                              crypto::Poseidon2Vendor::kPlonky3, PackedF, 15,\n                              7>;\n  using Poseidon2 = crypto::Poseidon2Sponge<Params>;\n  using PackedPoseidon2 = crypto::Poseidon2Sponge<PackedParams>;\n  using MyHasher = crypto::PaddingFreeSponge<Poseidon2, kRate, kChunk>;\n  using MyPackedHasher =\n      crypto::PaddingFreeSponge<PackedPoseidon2, kRate, kChunk>;\n  using MyCompressor = crypto::TruncatedPermutation<Poseidon2, kChunk, kN>;\n  using MyPackedCompressor =\n      crypto::TruncatedPermutation<PackedPoseidon2, kChunk, kN>;\n  using MMCS =\n      crypto::FieldMerkleTreeMMCS<F, MyHasher, MyPackedHasher, MyCompressor,\n                                  MyPackedCompressor, kChunk>;\n  using ExtMMCS =\n      crypto::FieldMerkleTreeMMCS<ExtF, MyHasher, MyPackedHasher, MyCompressor,\n                                  MyPackedCompressor, kChunk>;\n  using ChallengeMMCS = crypto::ExtensionFieldMerkleTreeMMCS<ExtF, ExtMMCS>;\n  using Challenger = crypto::DuplexChallenger<Poseidon2, kRate>;\n  using MyPCS = crypto::TwoAdicFRI<ExtF, MMCS, ChallengeMMCS, Challenger>;\n\n  ExtF::Init();\n  ExtPackedF::Init();\n\n  MMCS mmcs;\n  ChallengeMMCS challenge_mmcs;\n  crypto::FRIConfig<ChallengeMMCS> fri_config{config.log_blowup(), 10, 8,\n                                              challenge_mmcs};\n  MyPCS pcs = MyPCS(std::move(mmcs), std::move(fri_config));\n  Challenger challenger;\n\n  SimpleReporter reporter;\n  std::string name;\n  name = absl::Substitute(\"FRI Benchmark (b: $0, l: $1)\", config.batch_size(),\n                          config.log_blowup());\n  reporter.set_title(name);\n  reporter.set_x_label(\"Max Exponent\");\n  reporter.set_column_labels(base::Map(\n      config.exponents(),\n      [](uint32_t exponent) { return base::NumberToString(exponent); }));\n\n  FRIRunner runner(reporter, config, pcs, challenger);\n\n  std::vector<size_t> degrees = config.GetDegrees();\n\n  reporter.AddVendor(Vendor::Tachyon());\n  for (const Vendor vendor : config.vendors()) {\n    reporter.AddVendor(vendor);\n  }\n\n  for (size_t degree : degrees) {\n    math::RowMajorMatrix<F> input =\n        math::RowMajorMatrix<F>::Random(degree, config.batch_size());\n\n    ExtF tachyon_result, vendor_result;\n    for (const Vendor vendor : config.vendors()) {\n      if (vendor.value() == Vendor::kPlonky3) {\n        vendor_result =\n            runner.RunExternal(vendor, run_fri_plonky3_baby_bear, input);\n      } else {\n        NOTREACHED();\n      }\n    }\n    tachyon_result = runner.Run(Vendor::Tachyon(), input);\n    CheckResult(config.check_results(), tachyon_result, vendor_result);\n  }\n\n  reporter.Show();\n}\n\nint RealMain(int argc, char** argv) {\n  base::FilePath tmp_file;\n  CHECK(base::GetTempDir(&tmp_file));\n  tmp_file = tmp_file.Append(\"fri_benchmark.perfetto-trace\");\n  base::Profiler profiler({tmp_file});\n\n  profiler.Init();\n  profiler.Start();\n\n  FRIConfig config;\n  if (!config.Parse(argc, argv)) {\n    return 1;\n  }\n\n  Run(config);\n  return 0;\n}\n\n}  // namespace tachyon::benchmark\n\nint main(int argc, char** argv) {\n  return tachyon::benchmark::RealMain(argc, argv);\n}\n"
  },
  {
    "path": "benchmark/fri/fri_config.cc",
    "content": "#include \"benchmark/fri/fri_config.h\"\n\n#include <set>\n\n#include \"tachyon/base/console/iostream.h\"\n#include \"tachyon/base/containers/container_util.h\"\n\nnamespace tachyon::benchmark {\n\nFRIConfig::FRIConfig() {\n  parser_.AddFlag<base::Flag<std::vector<uint32_t>>>(&exponents_)\n      .set_short_name(\"-k\")\n      .set_required()\n      .set_help(\n          \"Specify the exponent 'k's where the degree of poly to test is 2ᵏ.\");\n  parser_.AddFlag<base::Flag<size_t>>(&batch_size_)\n      .set_short_name(\"-b\")\n      .set_long_name(\"--batch_size\")\n      .set_default_value(100)\n      .set_help(\"Specify the batch size. By default, 100.\");\n  parser_.AddFlag<base::Flag<size_t>>(&input_num_)\n      .set_short_name(\"-i\")\n      .set_long_name(\"--input_num\")\n      .set_default_value(4)\n      .set_help(\n          \"Specify the number of inputs in a single round. By default, 4.\");\n  parser_.AddFlag<base::Flag<size_t>>(&round_num_)\n      .set_short_name(\"-r\")\n      .set_long_name(\"--round_num\")\n      .set_default_value(4)\n      .set_help(\"Specify the number of rounds. By default, 4.\");\n  parser_.AddFlag<base::Flag<uint32_t>>(&log_blowup_)\n      .set_short_name(\"-l\")\n      .set_long_name(\"--log_blowup\")\n      .set_default_value(1)\n      .set_help(\"Specify the log blowup. By default, 1.\");\n  parser_.AddFlag<base::Flag<std::set<Vendor>>>(&vendors_)\n      .set_long_name(\"--vendor\")\n      .set_help(\"Vendors to be benchmarked with. (supported vendors: plonky3\");\n}\n\nvoid FRIConfig::PostParse() {\n  base::ranges::sort(exponents_);  // NOLINT(build/include_what_you_use)\n}\n\nstd::vector<size_t> FRIConfig::GetDegrees() const {\n  return base::Map(exponents_,\n                   [](uint32_t exponent) { return (size_t{1} << exponent); });\n}\n\nbool FRIConfig::Validate() const {\n  for (const Vendor vendor : vendors_) {\n    if (vendor.value() != Vendor::kPlonky3) {\n      tachyon_cerr << \"Unsupported vendor \" << vendor.ToString() << std::endl;\n      return false;\n    }\n  }\n  return true;\n}\n\n}  // namespace tachyon::benchmark\n"
  },
  {
    "path": "benchmark/fri/fri_config.h",
    "content": "#ifndef BENCHMARK_FRI_FRI_CONFIG_H_\n#define BENCHMARK_FRI_FRI_CONFIG_H_\n\n#include <stddef.h>\n\n#include <vector>\n\n// clang-format off\n#include \"benchmark/config.h\"\n// clang-format on\n\nnamespace tachyon::benchmark {\n\nclass FRIConfig : public Config {\n public:\n  FRIConfig();\n  FRIConfig(const FRIConfig& other) = delete;\n  FRIConfig& operator=(const FRIConfig& other) = delete;\n\n  const std::vector<uint32_t>& exponents() const { return exponents_; }\n  size_t batch_size() const { return batch_size_; }\n  size_t input_num() const { return input_num_; }\n  size_t round_num() const { return round_num_; }\n  uint32_t log_blowup() const { return log_blowup_; }\n\n  std::vector<size_t> GetDegrees() const;\n\n private:\n  // Config methods\n  void PostParse() override;\n  bool Validate() const override;\n\n  std::vector<uint32_t> exponents_;\n  size_t batch_size_;\n  size_t input_num_;\n  size_t round_num_;\n  uint32_t log_blowup_;\n};\n\n}  // namespace tachyon::benchmark\n\n#endif  // BENCHMARK_FRI_FRI_CONFIG_H_\n"
  },
  {
    "path": "benchmark/fri/fri_runner.h",
    "content": "#ifndef BENCHMARK_FRI_FRI_RUNNER_H_\n#define BENCHMARK_FRI_FRI_RUNNER_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <memory>\n#include <utility>\n#include <vector>\n\n// clang-format off\n#include \"benchmark/fri/fri_config.h\"\n#include \"benchmark/simple_reporter.h\"\n// clang-format on\n#include \"tachyon/base/time/time.h\"\n#include \"tachyon/c/math/finite_fields/baby_bear/baby_bear_type_traits.h\"\n#include \"tachyon/crypto/commitments/fri/two_adic_multiplicative_coset.h\"\n#include \"tachyon/math/matrix/matrix_types.h\"\n\nnamespace tachyon::benchmark {\n\ntemplate <typename PCS>\nclass FRIRunner {\n public:\n  using F = typename PCS::F;\n  using ExtF = typename PCS::ExtF;\n  using Domain = typename PCS::Domain;\n  using Challenger = typename PCS::Challenger;\n  using Commitment = typename PCS::Commitment;\n  using ProverData = typename PCS::ProverData;\n  using FRIProof = typename PCS::FRIProof;\n  using OpenedValues = typename PCS::OpenedValues;\n\n  typedef tachyon_baby_bear* (*ExternalFn)(const tachyon_baby_bear* data,\n                                           size_t input_num, size_t round_num,\n                                           size_t max_degree, size_t batch_size,\n                                           uint32_t log_blowup,\n                                           uint64_t* duration);\n\n  FRIRunner(SimpleReporter& reporter, const FRIConfig& config, PCS& pcs,\n            Challenger& challenger)\n      : reporter_(reporter),\n        config_(config),\n        pcs_(pcs),\n        challenger_(challenger) {}\n\n  ExtF Run(Vendor vendor, const math::RowMajorMatrix<F>& input) {\n    size_t max_degree = static_cast<size_t>(input.rows());\n    std::vector<Commitment> commits_by_round(config_.round_num());\n    std::vector<std::unique_ptr<ProverData>> data_by_round(config_.round_num());\n    std::vector<std::vector<Domain>> domains_by_round(config_.round_num());\n    std::vector<std::vector<math::RowMajorMatrix<F>>> inner_polys_by_round(\n        config_.round_num());\n\n    Challenger p_challenger = challenger_;\n\n    for (size_t round = 0; round < config_.round_num(); round++) {\n      std::vector<size_t> degrees = GetInputDegrees(max_degree, round);\n\n      domains_by_round[round] = base::Map(degrees, [this](size_t degree) {\n        return this->pcs_.GetNaturalDomainForDegree(degree);\n      });\n\n      inner_polys_by_round[round] =\n          base::Map(degrees, [this, round, input](size_t degree) {\n            math::RowMajorMatrix<F> ret =\n                Eigen::Map<const math::RowMajorMatrix<F>>(\n                    &input.data()[round], degree, config_.batch_size());\n            return ret;\n          });\n    }\n\n    base::TimeTicks start = base::TimeTicks::Now();\n    for (size_t round = 0; round < config_.round_num(); round++) {\n      data_by_round[round].reset(new ProverData());\n      CHECK(pcs_.Commit(domains_by_round[round], inner_polys_by_round[round],\n                        &commits_by_round[round], data_by_round[round].get()));\n    }\n    p_challenger.ObserveContainer2D(commits_by_round);\n    ExtF zeta = p_challenger.template SampleExtElement<ExtF>();\n\n    std::vector<std::vector<std::vector<ExtF>>> points_by_round(\n        config_.round_num());\n    for (size_t round = 0; round < config_.round_num(); ++round) {\n      points_by_round[round] =\n          std::vector<std::vector<ExtF>>(config_.input_num(), {zeta});\n    }\n    OpenedValues openings;\n    FRIProof fri_proof;\n    CHECK(pcs_.CreateOpeningProof(data_by_round, points_by_round, p_challenger,\n                                  &openings, &fri_proof, pow_witness_));\n    reporter_.AddTime(vendor, base::TimeTicks::Now() - start);\n    return fri_proof.final_eval;\n  }\n\n  ExtF RunExternal(Vendor vendor, ExternalFn fn,\n                   const math::RowMajorMatrix<F>& input) {\n    size_t max_degree = static_cast<size_t>(input.rows());\n\n    uint64_t duration_in_us = 0;\n    tachyon_baby_bear* data =\n        fn(c::base::c_cast(input.data()), config_.input_num(),\n           config_.round_num(), max_degree, config_.batch_size(),\n           config_.log_blowup(), &duration_in_us);\n    reporter_.AddTime(vendor, base::Microseconds(duration_in_us));\n    pow_witness_ = c::base::native_cast(data)[0];\n    ExtF final_eval{\n        c::base::native_cast(data)[1], c::base::native_cast(data)[2],\n        c::base::native_cast(data)[3], c::base::native_cast(data)[4]};\n    return final_eval;\n  }\n\n private:\n  std::vector<size_t> GetInputDegrees(size_t max_degree, size_t round) {\n    std::vector<size_t> degrees;\n    degrees.reserve(config_.input_num());\n    for (size_t i = 0; i < config_.input_num(); ++i) {\n      degrees.push_back(max_degree >> (i + round));\n    }\n    return degrees;\n  }\n\n  SimpleReporter& reporter_;\n  const FRIConfig& config_;\n  PCS& pcs_;\n  Challenger& challenger_;\n  std::optional<F> pow_witness_;\n};\n\n}  // namespace tachyon::benchmark\n\n#endif  // BENCHMARK_FRI_FRI_RUNNER_H_\n"
  },
  {
    "path": "benchmark/fri/plonky3/BUILD.bazel",
    "content": "load(\"@crate_index//:defs.bzl\", \"aliases\", \"all_crate_deps\")\nload(\"//bazel:tachyon_rust.bzl\", \"tachyon_rust_static_library\")\n\ntachyon_rust_static_library(\n    name = \"plonky3\",\n    srcs = glob([\"src/**/*.rs\"]),\n    aliases = aliases(),\n    proc_macro_deps = all_crate_deps(proc_macro = True),\n    visibility = [\"//benchmark/fri:__pkg__\"],\n    deps = all_crate_deps(normal = True) + [\n        \"//tachyon/rs:tachyon_rs\",\n    ],\n)\n"
  },
  {
    "path": "benchmark/fri/plonky3/Cargo.toml",
    "content": "[package]\nname = \"plonky3_fri_benchmark\"\nversion = \"0.0.1\"\nauthors = [\"The Tachyon Authors <tachyon-discuss@kroma.network>\"]\nedition = \"2021\"\ndescription = \"\"\"\nPlonky3 FRI Benchmark\n\"\"\"\nlicense = \"MIT OR Apache-2.0\"\nrepository = \"https://github.com/kroma-network/tachyon\"\nreadme = \"README.md\"\ncategories = [\"cryptography\"]\nkeywords = [\"tachyon\", \"benchmark\", \"plonky3\"]\npublish = false\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[dependencies]\np3-baby-bear = \"0.1.3-succinct\"\np3-challenger = \"0.1.3-succinct\"\np3-commit = \"0.1.3-succinct\"\np3-dft = \"0.1.3-succinct\"\np3-field = \"0.1.3-succinct\"\np3-fri = \"0.1.3-succinct\"\np3-matrix = \"0.1.3-succinct\"\np3-merkle-tree = \"0.1.3-succinct\"\np3-poseidon2 = \"0.1.3-succinct\"\np3-symmetric = \"0.1.3-succinct\"\np3-util = \"0.1.3-succinct\"\ntachyon_rs = { path = \"../../../tachyon/rs\" }\nzkhash = { git = \"https://github.com/HorizenLabs/poseidon2.git\", rev = \"bb476b9\" }\n"
  },
  {
    "path": "benchmark/fri/plonky3/src/lib.rs",
    "content": "use std::fmt::Debug;\nuse std::time::Instant;\n\nuse p3_baby_bear::{BabyBear, DiffusionMatrixBabyBear};\nuse p3_challenger::{CanObserve, DuplexChallenger, FieldChallenger};\nuse p3_commit::{ExtensionMmcs, Pcs, PolynomialSpace};\nuse p3_dft::Radix2DitParallel;\nuse p3_field::extension::BinomialExtensionField;\nuse p3_field::{AbstractExtensionField, AbstractField, ExtensionField, Field};\nuse p3_fri::{FriConfig, TwoAdicFriPcs, TwoAdicFriPcsProof};\nuse p3_matrix::dense::RowMajorMatrix;\nuse p3_merkle_tree::FieldMerkleTreeMmcs;\nuse p3_poseidon2::{Poseidon2, Poseidon2ExternalMatrixHL};\nuse p3_symmetric::{PaddingFreeSponge, TruncatedPermutation};\nuse p3_util::log2_strict_usize;\n\nuse tachyon_rs::math::finite_fields::baby_bear::BabyBear as CppBabyBear;\nuse zkhash::ark_ff::PrimeField as ark_PrimeField;\nuse zkhash::poseidon2::poseidon2_instance_babybear::RC16 as BabyBearRC16;\n\ntype Val = BabyBear;\ntype Challenge = BinomialExtensionField<Val, 4>;\n\ntype Perm = Poseidon2<Val, Poseidon2ExternalMatrixHL, DiffusionMatrixBabyBear, 16, 7>;\ntype MyHash = PaddingFreeSponge<Perm, 16, 8, 8>;\ntype MyCompress = TruncatedPermutation<Perm, 2, 8, 16>;\n\ntype ValMmcs =\n    FieldMerkleTreeMmcs<<Val as Field>::Packing, <Val as Field>::Packing, MyHash, MyCompress, 8>;\ntype ChallengeMmcs = ExtensionMmcs<Val, Challenge, ValMmcs>;\n\ntype Dft = Radix2DitParallel;\ntype Challenger = DuplexChallenger<Val, Perm, 16, 8>;\ntype MyPcs = TwoAdicFriPcs<Val, Dft, ValMmcs, ChallengeMmcs>;\n\nfn get_perm(rounds_f: usize, rounds_p: usize) -> Perm {\n    let mut round_constants: Vec<[BabyBear; 16]> = BabyBearRC16\n        .iter()\n        .map(|vec| {\n            vec.iter()\n                .cloned()\n                .map(|ark_ff| BabyBear::from_canonical_u32(ark_ff.into_bigint().0[0] as u32))\n                .collect::<Vec<_>>()\n                .try_into()\n                .unwrap()\n        })\n        .collect();\n    let internal_start = rounds_f / 2;\n    let internal_end = (rounds_f / 2) + rounds_p;\n    let internal_round_constants = round_constants\n        .drain(internal_start..internal_end)\n        .map(|vec| vec[0])\n        .collect::<Vec<_>>();\n    let external_round_constants = round_constants;\n\n    Perm::new(\n        rounds_f,\n        external_round_constants,\n        Poseidon2ExternalMatrixHL,\n        rounds_p,\n        internal_round_constants,\n        DiffusionMatrixBabyBear,\n    )\n}\n\nfn get_pcs(log_blowup: usize, log_n: usize) -> (MyPcs, Challenger) {\n    let perm = get_perm(8, 13);\n\n    let hash = MyHash::new(perm.clone());\n    let compress = MyCompress::new(perm.clone());\n\n    let val_mmcs = ValMmcs::new(hash, compress);\n    let challenge_mmcs = ChallengeMmcs::new(val_mmcs.clone());\n\n    let fri_config = FriConfig {\n        log_blowup,\n        num_queries: 10,\n        proof_of_work_bits: 8,\n        mmcs: challenge_mmcs,\n    };\n\n    let pcs = MyPcs::new(log_n, Dft {}, val_mmcs, fri_config);\n    (pcs, Challenger::new(perm.clone()))\n}\n\nfn do_test_fri<Challenger, P>(\n    (pcs, challenger): &(P, Challenger),\n    degrees_by_round: Vec<Vec<usize>>,\n    data: *const Val,\n    batch_size: usize,\n    duration: *mut u64,\n) -> *mut CppBabyBear\nwhere\n    P: Pcs<Challenge, Challenger>,\n    P::Domain: PolynomialSpace<Val = Val>,\n    Challenge: ExtensionField<Val>,\n    P: Pcs<\n        Challenge,\n        Challenger,\n        Proof = TwoAdicFriPcsProof<Val, Challenge, ValMmcs, ChallengeMmcs>,\n    >,\n    Challenger: Clone + CanObserve<P::Commitment> + FieldChallenger<Val>,\n    <P as Pcs<Challenge, Challenger>>::Commitment: Debug,\n{\n    let num_rounds = degrees_by_round.len();\n    let mut p_challenger = challenger.clone();\n\n    let domains_and_polys_by_round: Vec<Vec<_>> = degrees_by_round\n        .iter()\n        .enumerate()\n        .map(|(r, degrees)| {\n            degrees\n                .iter()\n                .map(|&degree| {\n                    let size = degree * batch_size;\n                    let values: Vec<Val> = unsafe {\n                        std::slice::from_raw_parts(data.add(r) as *mut Val, size).to_vec()\n                    };\n                    (\n                        pcs.natural_domain_for_degree(degree),\n                        RowMajorMatrix::<Val>::new(values, batch_size),\n                    )\n                })\n                .collect()\n        })\n        .collect();\n\n    let start = Instant::now();\n    let (commits_by_round, data_by_round): (Vec<_>, Vec<_>) = domains_and_polys_by_round\n        .iter()\n        .map(|domains_and_polys| pcs.commit(domains_and_polys.clone()))\n        .unzip();\n    assert_eq!(commits_by_round.len(), num_rounds);\n    assert_eq!(data_by_round.len(), num_rounds);\n    p_challenger.observe_slice(&commits_by_round);\n\n    let zeta: Challenge = p_challenger.sample_ext_element();\n\n    let points_by_round: Vec<_> = degrees_by_round\n        .iter()\n        .map(|log_degrees| vec![vec![zeta]; log_degrees.len()])\n        .collect();\n    let data_and_points = data_by_round.iter().zip(points_by_round).collect();\n    let (_opening_by_round, proof) = pcs.open(data_and_points, &mut p_challenger);\n    unsafe {\n        duration.write(start.elapsed().as_micros() as u64);\n    }\n\n    let mut ret_values: [Val; 5] = [Val::zero(); 5];\n    ret_values[0] = proof.fri_proof.pow_witness;\n    ret_values[1..].copy_from_slice(proof.fri_proof.final_poly.as_base_slice());\n    Box::into_raw(Box::new(ret_values)) as *mut CppBabyBear\n}\n\n#[no_mangle]\npub extern \"C\" fn run_fri_plonky3_baby_bear(\n    data: *const BabyBear,\n    input_num: usize,\n    round_num: usize,\n    max_degree: usize,\n    batch_size: usize,\n    log_blowup: u32,\n    duration: *mut u64,\n) -> *mut CppBabyBear {\n    let degrees_by_round: Vec<Vec<usize>> = (0..round_num)\n        .map(|r| {\n            (0..input_num)\n                .map(|i| max_degree >> (r + i))\n                .collect::<Vec<_>>()\n        })\n        .collect();\n\n    let (pcs, challenger) = get_pcs(log_blowup as usize, log2_strict_usize(max_degree));\n\n    do_test_fri(\n        &(pcs, challenger),\n        degrees_by_round,\n        data,\n        batch_size,\n        duration,\n    )\n}\n"
  },
  {
    "path": "benchmark/msm/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_binary\", \"tachyon_cc_library\")\n\ntachyon_cc_library(\n    name = \"msm_config\",\n    testonly = True,\n    srcs = [\"msm_config.cc\"],\n    hdrs = [\"msm_config.h\"],\n    deps = [\n        \"//benchmark:config\",\n        \"//tachyon/base/console\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/base/ranges:algorithm\",\n        \"//tachyon/math/elliptic_curves/msm/test:variable_base_msm_test_set\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"msm_runner\",\n    hdrs = [\"msm_runner.h\"],\n    deps = [\n        \"//benchmark:simple_reporter\",\n        \"//tachyon/base/time\",\n        \"//tachyon/c/math/elliptic_curves:point_traits_forward\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ntachyon_cc_binary(\n    name = \"msm_benchmark\",\n    testonly = True,\n    srcs = [\"msm_benchmark.cc\"],\n    deps = [\n        \":msm_config\",\n        \":msm_runner\",\n        \"//benchmark:simple_reporter\",\n        \"//benchmark/msm/arkworks\",\n        \"//benchmark/msm/bellman\",\n        \"//benchmark/msm/halo2\",\n        \"//tachyon/base:profiler\",\n        \"//tachyon/c/math/elliptic_curves/bn/bn254:msm\",\n    ],\n)\n\ntachyon_cc_binary(\n    name = \"msm_benchmark_gpu\",\n    testonly = True,\n    srcs = [\"msm_benchmark_gpu.cc\"],\n    deps = [\n        \":msm_config\",\n        \":msm_runner\",\n        \"//benchmark:simple_reporter\",\n        \"//tachyon/base:profiler\",\n        \"//tachyon/c/math/elliptic_curves/bn/bn254:msm\",\n        \"//tachyon/c/math/elliptic_curves/bn/bn254:msm_gpu\",\n    ],\n)\n"
  },
  {
    "path": "benchmark/msm/README.md",
    "content": "# MSM Benchmark\n\n## CPU\n\n```\nRun on 13th Gen Intel(R) Core(TM) i9-13900K (32 X 5500 MHz CPU s)\nCompiler: clang-15\nCPU Caches:\n  L1 Data 48 KiB (x16)\n  L1 Instruction 32 KiB (x16)\n  L2 Unified 2048 KiB (x16)\n  L3 Unified 36864 KiB (x1)\n\nRun on Apple M3 Pro (12 X 4050 MHz)\nCPU Caches:\n  L1 Data 64 KiB (x12)\n  L1 Instruction 128 KiB (x12)\n  L2 Unified 4096 KiB (x12)\n```\n\nNote: Run with `build --@rules_rust//:extra_rustc_flags=\"-Ctarget-cpu=native\"` in your .bazelrc.user\n\n### Uniform points\n\n```shell\nGOMP_SPINCOUNT=0 bazel run --config maxopt --//:has_matplotlib //benchmark/msm:msm_benchmark -- -k 16 -k 17 -k 18 -k 19 -k 20 -k 21 -k 22 -k 23 --vendor arkworks --vendor bellman --vendor halo2 --check_results\n```\n\n#### On Intel i9-13900K\n\n| Exponent | Tachyon      | Arkworks | Bellman  | Halo2    |\n| :------: | ------------ | -------- | -------- | -------- |\n|    16    | **0.028461** | 0.037741 | 0.077416 | 0.045742 |\n|    17    | **0.059648** | 0.074936 | 0.105104 | 0.08211  |\n|    18    | **0.08743**  | 0.12735  | 0.196602 | 0.151715 |\n|    19    | **0.181646** | 0.252424 | 0.319185 | 0.282056 |\n|    20    | **0.303829** | 0.454595 | 0.471094 | 0.526231 |\n|    21    | **0.549287** | 0.951397 | 0.886244 | 1.00624  |\n|    22    | **1.11021**  | 2.00783  | 1.72011  | 1.9662   |\n|    23    | **2.06762**  | 3.78478  | 2.76673  | 3.68139  |\n\n![image](/benchmark/msm/msm_benchmark_uniform_ubuntu_i9.png)\n\n#### On Mac M3 Pro\n\n| Exponent | Tachyon      | Arkworks | Bellman  | Halo2    |\n| :------: | ------------ | -------- | -------- | -------- |\n|    16    | **0.046099** | 0.051773 | 0.110882 | 0.09505  |\n|    17    | **0.079298** | 0.097698 | 0.166183 | 0.174984 |\n|    18    | **0.151962** | 0.173607 | 0.296879 | 0.337657 |\n|    19    | **0.287848** | 0.34129  | 0.5563   | 0.592885 |\n|    20    | **0.504987** | 0.630489 | 0.840907 | 1.07097  |\n|    21    | **0.980302** | 1.33391  | 1.56196  | 1.98335  |\n|    22    | **1.89977**  | 2.86768  | 3.04392  | 3.9341   |\n|    23    | **3.73015**  | 5.71419  | 5.45636  | 7.51033  |\n\n![image](/benchmark/msm/msm_benchmark_uniform_mac_m3.png)\n\n### Non-uniform points\n\n```shell\nGOMP_SPINCOUNT=0 bazel run --config maxopt --//:has_matplotlib //benchmark/msm:msm_benchmark -- -k 16 -k 17 -k 18 -k 19 -k 20 -k 21 -k 22 -k 23 --vendor arkworks --vendor bellman --vendor halo2 --test_set non_uniform --check_results\n```\n\n#### On Intel i9-13900K\n\n| Exponent | Tachyon      | Arkworks | Bellman  | Halo2    |\n| :------: | ------------ | -------- | -------- | -------- |\n|    16    | **0.030188** | 0.033608 | 0.057795 | 0.060642 |\n|    17    | **0.048851** | 0.064059 | 0.132584 | 0.099568 |\n|    18    | **0.080146** | 0.121525 | 0.124192 | 0.147496 |\n|    19    | **0.147626** | 0.227517 | 0.234429 | 0.27793  |\n|    20    | **0.289661** | 0.445139 | 0.341189 | 0.509375 |\n|    21    | **0.495707** | 0.801975 | 0.702259 | 1.0386   |\n|    22    | **0.993738** | 1.51266  | 1.24812  | 1.88462  |\n|    23    | **1.69944**  | 3.07904  | 2.00071  | 3.57452  |\n\n![image](/benchmark/msm/msm_benchmark_non_uniform_ubuntu_i9.png)\n\n#### On Mac M3 Pro\n\n| Exponent | Tachyon      | Arkworks | Bellman  | Halo2    |\n| :------: | ------------ | -------- | -------- | -------- |\n|    16    | **0.040954** | 0.046663 | 0.076068 | 0.089352 |\n|    17    | **0.069956** | 0.089339 | 0.119363 | 0.166812 |\n|    18    | **0.146869** | 0.163578 | 0.225768 | 0.326553 |\n|    19    | **0.268475** | 0.302439 | 0.460063 | 0.579915 |\n|    20    | **0.501956** | 0.627272 | 0.723071 | 1.09316  |\n|    21    | **0.920728** | 1.20662  | 1.22352  | 1.98457  |\n|    22    | **1.78902**  | 2.40124  | 2.24543  | 3.83765  |\n|    23    | **3.47906**  | 4.70651  | 4.13381  | 7.43978  |\n\n![image](/benchmark/msm/msm_benchmark_non_uniform_mac_m3.png)\n\n## GPU\n\n### Uniform points\n\n```shell\nGOMP_SPINCOUNT=0 bazel run --config maxopt --//:has_matplotlib --config cuda //benchmark/msm:msm_benchmark_gpu -- -k 16 -k 17 -k 18 -k 19 -k 20 -k 21 -k 22 -k 23 --test_set non_uniform --check_results\n```\n\n#### On RTX-4090\n\n| Exponent | Tachyon CPU | Tachyon GPU  |\n| :------: | ----------- | ------------ |\n|    16    | 0.026688    | **0.01981**  |\n|    17    | 0.041291    | **0.006624** |\n|    18    | 0.081467    | **0.008306** |\n|    19    | 0.148929    | **0.012553** |\n|    20    | 0.260831    | **0.02423**  |\n|    21    | 0.474542    | **0.044591** |\n|    22    | 0.921276    | **0.088349** |\n|    23    | 1.70264     | **0.162646** |\n\n![image](/benchmark/msm/msm_benchmark_uniform_ubuntu_rtx_4090.png)\n\n### Non-uniform points\n\n```shell\nGOMP_SPINCOUNT=0 bazel run --config maxopt --//:has_matplotlib --config cuda //benchmark/msm:msm_benchmark_gpu -- -k 16 -k 17 -k 18 -k 19 -k 20 -k 21 -k 22 -k 23 --check_results\n```\n\n#### On RTX-4090\n\n| Exponent | Tachyon CPU | Tachyon GPU  |\n| :------: | ----------- | ------------ |\n|    16    | 0.029045    | **0.020228** |\n|    17    | 0.047588    | **0.006565** |\n|    18    | 0.089673    | **0.008864** |\n|    19    | 0.164875    | **0.012308** |\n|    20    | 0.29135     | **0.023396** |\n|    21    | 0.541067    | **0.043512** |\n|    22    | 1.0379      | **0.08407**  |\n|    23    | 2.08601     | **0.157046** |\n\n![image](/benchmark/msm/msm_benchmark_non_uniform_ubuntu_rtx_4090.png)\n"
  },
  {
    "path": "benchmark/msm/arkworks/BUILD.bazel",
    "content": "load(\"@crate_index//:defs.bzl\", \"aliases\", \"all_crate_deps\")\nload(\"//bazel:tachyon_rust.bzl\", \"tachyon_rust_static_library\")\n\ntachyon_rust_static_library(\n    name = \"arkworks\",\n    srcs = glob([\"src/**/*.rs\"]),\n    aliases = aliases(),\n    proc_macro_deps = all_crate_deps(proc_macro = True),\n    visibility = [\"//benchmark/msm:__pkg__\"],\n    deps = all_crate_deps(normal = True) + [\n        \"//tachyon/rs:tachyon_rs\",\n    ],\n)\n"
  },
  {
    "path": "benchmark/msm/arkworks/Cargo.toml",
    "content": "[package]\nname = \"arkworks_msm_benchmark\"\nversion = \"0.0.1\"\nauthors = [\"The Tachyon Authors <tachyon-discuss@kroma.network>\"]\nedition = \"2021\"\nrust-version = \"1.56.1\"\ndescription = \"\"\"\nArkworks MSM Benchmark\n\"\"\"\nlicense = \"MIT OR Apache-2.0\"\nrepository = \"https://github.com/kroma-network/tachyon\"\nreadme = \"README.md\"\ncategories = [\"cryptography\"]\nkeywords = [\"tachyon\", \"benchmark\", \"arkworks\"]\npublish = false\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[dependencies]\nark-bn254 = \"0.4.0\"\nark-ff = { version = \"^0.4.0\", features = [\"asm\"] }\nark-ec = { version = \"0.4.2\", features = [\"parallel\"] }\nark-std = \"0.4.0\"\ntachyon_rs = { path = \"../../../tachyon/rs\" }\n"
  },
  {
    "path": "benchmark/msm/arkworks/src/lib.rs",
    "content": "use ark_bn254::{Fr, G1Affine, G1Projective};\nuse ark_ec::VariableBaseMSM;\nuse ark_ff::Zero;\nuse std::{mem, slice, time::Instant};\nuse tachyon_rs::math::elliptic_curves::bn::bn254::{\n    Fr as CppFr, G1AffinePoint as CppG1AffinePoint, G1JacobianPoint as CppG1JacobianPoint,\n};\n\n#[no_mangle]\npub extern \"C\" fn run_msm_arkworks(\n    bases: *const CppG1AffinePoint,\n    scalars: *const CppFr,\n    size: usize,\n    duration: *mut u64,\n) -> *mut CppG1JacobianPoint {\n    let ret = unsafe {\n        let bases: &[CppG1AffinePoint] = slice::from_raw_parts(bases, size);\n        let scalars: &[CppFr] = slice::from_raw_parts(scalars, size);\n\n        let mut bases_vec = Vec::<G1Affine>::new();\n        bases_vec.reserve_exact(bases.len());\n        for i in 0..size {\n            let x = mem::transmute(bases[i].x);\n            let y = mem::transmute(bases[i].y);\n            bases_vec.push(G1Affine {\n                x,\n                y,\n                infinity: x.is_zero() && y.is_zero(),\n            });\n        }\n\n        let scalars: &[Fr] = mem::transmute(scalars);\n        let start = Instant::now();\n        let ret = G1Projective::msm(bases_vec.as_slice(), &scalars).unwrap();\n        duration.write(start.elapsed().as_micros() as u64);\n        ret\n    };\n    Box::into_raw(Box::new(ret)) as *mut CppG1JacobianPoint\n}\n"
  },
  {
    "path": "benchmark/msm/bellman/BUILD.bazel",
    "content": "load(\"@crate_index//:defs.bzl\", \"aliases\", \"all_crate_deps\")\nload(\"//bazel:tachyon_rust.bzl\", \"tachyon_rust_static_library\")\n\ntachyon_rust_static_library(\n    name = \"bellman\",\n    srcs = glob([\"src/**/*.rs\"]),\n    aliases = aliases(),\n    proc_macro_deps = all_crate_deps(proc_macro = True),\n    visibility = [\"//benchmark/msm:__pkg__\"],\n    deps = all_crate_deps(normal = True) + [\n        \"//tachyon/rs:tachyon_rs\",\n    ],\n)\n"
  },
  {
    "path": "benchmark/msm/bellman/Cargo.toml",
    "content": "[package]\nname = \"bellman_benchmark\"\nversion = \"0.0.1\"\nauthors = [\"The Tachyon Authors <tachyon-discuss@kroma.network>\"]\nedition = \"2021\"\nrust-version = \"1.56.1\"\ndescription = \"\"\"\nBellman MSM Benchmark\n\"\"\"\nlicense = \"MIT OR Apache-2.0\"\nrepository = \"https://github.com/kroma-network/tachyon\"\nreadme = \"README.md\"\ncategories = [\"cryptography\"]\nkeywords = [\"tachyon\", \"benchmark\", \"bellman\"]\npublish = false\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[dependencies]\nbellman_ce = { git = \"https://github.com/kroma-network/bellman.git\", rev = \"279a4bd\" }\ntachyon_rs = { path = \"../../../tachyon/rs\" }\n"
  },
  {
    "path": "benchmark/msm/bellman/src/lib.rs",
    "content": "use bellman_ce::{\n    multiexp,\n    pairing::CurveAffine,\n    pairing::{\n        bn256::{Fr, FrRepr, G1Affine},\n        ff::PrimeField,\n    },\n    worker::Worker,\n};\nuse std::{mem, slice, time::Instant};\nuse tachyon_rs::math::elliptic_curves::bn::bn254::{\n    Fr as CppFr, G1AffinePoint as CppG1AffinePoint, G1JacobianPoint as CppG1JacobianPoint,\n};\n\n#[no_mangle]\npub extern \"C\" fn run_msm_bellman(\n    bases: *const CppG1AffinePoint,\n    scalars: *const CppFr,\n    size: usize,\n    duration: *mut u64,\n) -> *mut CppG1JacobianPoint {\n    let ret = unsafe {\n        let bases: &[CppG1AffinePoint] = slice::from_raw_parts(bases, size);\n        let scalars: &[CppFr] = slice::from_raw_parts(scalars, size);\n\n        let mut bases_vec = Vec::<G1Affine>::new();\n        bases_vec.reserve_exact(bases.len());\n        for i in 0..size {\n            let x = mem::transmute(bases[i].x);\n            let y = mem::transmute(bases[i].y);\n            bases_vec.push(G1Affine::from_xy_checked(x, y).unwrap());\n        }\n        let pool = Worker::new();\n        let scalars: &[Fr] = mem::transmute(scalars);\n        let scalars_repr: Vec<FrRepr> = scalars.iter().map(|&fr| fr.into_repr()).collect();\n        let start = Instant::now();\n        let result = multiexp::dense_multiexp(&pool, &bases_vec, scalars_repr.as_slice());\n        duration.write(start.elapsed().as_micros() as u64);\n        let ret = match result {\n            Ok(g1_point) => g1_point,\n            Err(_) => panic!(\"multiexp failed\"),\n        };\n        ret\n    };\n    Box::into_raw(Box::new(ret)) as *mut CppG1JacobianPoint\n}\n"
  },
  {
    "path": "benchmark/msm/halo2/BUILD.bazel",
    "content": "load(\"@crate_index//:defs.bzl\", \"aliases\", \"all_crate_deps\")\nload(\"//bazel:tachyon_rust.bzl\", \"tachyon_rust_static_library\")\n\ntachyon_rust_static_library(\n    name = \"halo2\",\n    srcs = glob([\"src/**/*.rs\"]),\n    aliases = aliases(),\n    proc_macro_deps = all_crate_deps(proc_macro = True),\n    visibility = [\"//benchmark/msm:__pkg__\"],\n    deps = all_crate_deps(normal = True) + [\n        \"//tachyon/rs:tachyon_rs\",\n    ],\n)\n"
  },
  {
    "path": "benchmark/msm/halo2/Cargo.toml",
    "content": "[package]\nname = \"halo2_msm_benchmark\"\nversion = \"0.0.1\"\nauthors = [\"The Tachyon Authors <tachyon-discuss@kroma.network>\"]\nedition = \"2021\"\nrust-version = \"1.56.1\"\ndescription = \"\"\"\nHalo2 MSM Benchmark\n\"\"\"\nlicense = \"MIT OR Apache-2.0\"\nrepository = \"https://github.com/kroma-network/tachyon\"\nreadme = \"README.md\"\ncategories = [\"cryptography\"]\nkeywords = [\"tachyon\", \"benchmark\", \"halo2\"]\npublish = false\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[dependencies]\nhalo2_proofs = { git = \"https://github.com/scroll-tech/halo2.git\", rev = \"e5ddf67\" }\ntachyon_rs = { path = \"../../../tachyon/rs\" }\n"
  },
  {
    "path": "benchmark/msm/halo2/src/lib.rs",
    "content": "use halo2_proofs::halo2curves::bn256::{Bn256, Fr, G1Affine};\nuse halo2_proofs::poly::commitment::MSM;\nuse halo2_proofs::poly::kzg::msm::MSMKZG;\nuse std::{mem, slice, time::Instant};\nuse tachyon_rs::math::elliptic_curves::bn::bn254::{\n    Fr as CppFr, G1AffinePoint as CppG1AffinePoint, G1ProjectivePoint as CppG1ProjectivePoint,\n};\n\n#[no_mangle]\npub extern \"C\" fn run_msm_halo2(\n    bases: *const CppG1AffinePoint,\n    scalars: *const CppFr,\n    size: usize,\n    duration: *mut u64,\n) -> *mut CppG1ProjectivePoint {\n    let ret = unsafe {\n        let bases: &[CppG1AffinePoint] = slice::from_raw_parts(bases, size);\n        let scalars: &[CppFr] = slice::from_raw_parts(scalars, size);\n\n        let mut bases_vec = Vec::<G1Affine>::new();\n        bases_vec.reserve_exact(bases.len());\n        for i in 0..size {\n            bases_vec.push(G1Affine {\n                x: mem::transmute(bases[i].x),\n                y: mem::transmute(bases[i].y),\n            });\n        }\n\n        let scalars: &[Fr] = mem::transmute(scalars);\n\n        let mut msm = MSMKZG::<Bn256>::new();\n        for (base, scalar) in bases_vec.iter().zip(scalars.iter()) {\n            msm.append_term(*scalar, base.into());\n        }\n        let start = Instant::now();\n        let ret = msm.eval();\n        duration.write(start.elapsed().as_micros() as u64);\n        ret\n    };\n    Box::into_raw(Box::new(ret)) as *mut CppG1ProjectivePoint\n}\n"
  },
  {
    "path": "benchmark/msm/msm_benchmark.cc",
    "content": "#include <iostream>\n#include <memory>\n#include <vector>\n\n// clang-format off\n#include \"benchmark/msm/msm_config.h\"\n#include \"benchmark/msm/msm_runner.h\"\n#include \"benchmark/simple_reporter.h\"\n// clang-format on\n#include \"tachyon/base/profiler.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1_point_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1_point_type_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/msm.h\"\n\nnamespace tachyon::benchmark {\n\nusing namespace math;\n\nextern \"C\" tachyon_bn254_g1_jacobian* run_msm_arkworks(\n    const tachyon_bn254_g1_affine* bases, const tachyon_bn254_fr* scalars,\n    size_t size, uint64_t* duration_in_us);\n\nextern \"C\" tachyon_bn254_g1_jacobian* run_msm_bellman(\n    const tachyon_bn254_g1_affine* bases, const tachyon_bn254_fr* scalars,\n    size_t size, uint64_t* duration_in_us);\n\nextern \"C\" tachyon_bn254_g1_projective* run_msm_halo2(\n    const tachyon_bn254_g1_affine* bases, const tachyon_bn254_fr* scalars,\n    size_t size, uint64_t* duration_in_us);\n\ntachyon_bn254_g1_jacobian* run_msm_halo2_adapter(\n    const tachyon_bn254_g1_affine* bases, const tachyon_bn254_fr* scalars,\n    size_t size, uint64_t* duration_in_us) {\n  std::unique_ptr<tachyon_bn254_g1_projective> projective(\n      run_msm_halo2(bases, scalars, size, duration_in_us));\n  return c::base::c_cast(new bn254::G1JacobianPoint(\n      c::base::native_cast(projective.get())->ToJacobian()));\n}\n\nint RealMain(int argc, char** argv) {\n  base::FilePath tmp_file;\n  CHECK(base::GetTempDir(&tmp_file));\n  tmp_file = tmp_file.Append(\"msm_benchmark.perfetto-trace\");\n  base::Profiler profiler({tmp_file});\n\n  profiler.Init();\n  profiler.Start();\n\n  MSMConfig::Options options;\n  options.include_vendors = true;\n  MSMConfig config(options);\n  if (!config.Parse(argc, argv)) {\n    return 1;\n  }\n\n  SimpleReporter reporter;\n  reporter.set_title(\"MSM Benchmark\");\n  reporter.set_x_label(\"Degree (2ˣ)\");\n  reporter.set_column_labels(base::Map(\n      config.exponents(),\n      [](uint32_t exponent) { return base::NumberToString(exponent); }));\n\n  std::vector<size_t> point_nums = config.GetPointNums();\n\n  tachyon_bn254_g1_init();\n  tachyon_bn254_g1_msm_ptr msm =\n      tachyon_bn254_g1_create_msm(config.exponents().back());\n\n  std::cout << \"Generating random points...\" << std::endl;\n  size_t max_point_num = point_nums.back();\n  VariableBaseMSMTestSet<bn254::G1AffinePoint> test_set;\n  CHECK(config.GenerateTestSet(max_point_num, &test_set));\n  std::cout << \"Generation completed\" << std::endl;\n\n  MSMRunner<bn254::G1AffinePoint> runner(reporter);\n  runner.SetInputs(test_set.bases, test_set.scalars);\n  std::vector<bn254::G1JacobianPoint> results;\n  runner.Run(Vendor::Tachyon(), tachyon_bn254_g1_affine_msm, msm, point_nums,\n             results);\n  for (const Vendor vendor : config.vendors()) {\n    std::vector<bn254::G1JacobianPoint> results_vendor;\n    if (vendor.value() == Vendor::kArkworks) {\n      runner.RunExternal(vendor, run_msm_arkworks, point_nums, results_vendor);\n    } else if (vendor.value() == Vendor::kBellman) {\n      runner.RunExternal(vendor, run_msm_bellman, point_nums, results_vendor);\n    } else if (vendor.value() == Vendor::kScrollHalo2) {\n      runner.RunExternal(vendor, run_msm_halo2_adapter, point_nums,\n                         results_vendor);\n    } else {\n      NOTREACHED();\n    }\n\n    if (config.check_results()) {\n      CHECK(results == results_vendor) << \"Results do not match\";\n    }\n  }\n\n  reporter.Show();\n\n  tachyon_bn254_g1_destroy_msm(msm);\n\n  return 0;\n}\n\n}  // namespace tachyon::benchmark\n\nint main(int argc, char** argv) {\n  return tachyon::benchmark::RealMain(argc, argv);\n}\n"
  },
  {
    "path": "benchmark/msm/msm_benchmark_gpu.cc",
    "content": "#include <iostream>\n#include <vector>\n#if TACHYON_CUDA\n\n// clang-format off\n#include \"benchmark/msm/msm_config.h\"\n#include \"benchmark/msm/msm_runner.h\"\n#include \"benchmark/simple_reporter.h\"\n// clang-format on\n#include \"tachyon/base/profiler.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1_point_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1_point_type_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/msm.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/msm_gpu.h\"\n#include \"tachyon/math/elliptic_curves/msm/test/variable_base_msm_test_set.h\"\n\nnamespace tachyon::benchmark {\n\nusing namespace math;\n\nint RealMain(int argc, char** argv) {\n  base::FilePath tmp_file;\n  CHECK(base::GetTempDir(&tmp_file));\n  tmp_file = tmp_file.Append(\"msm_benchmark_gpu.perfetto-trace\");\n  base::Profiler profiler({tmp_file});\n\n  profiler.Init();\n  profiler.Start();\n\n  MSMConfig config;\n  if (!config.Parse(argc, argv)) {\n    return 1;\n  }\n\n  SimpleReporter reporter;\n  reporter.set_title(\"MSM Benchmark\");\n  reporter.set_x_label(\"Degree (2ˣ)\");\n  reporter.set_column_labels(base::Map(\n      config.exponents(),\n      [](uint32_t exponent) { return base::NumberToString(exponent); }));\n\n  std::vector<size_t> point_nums = config.GetPointNums();\n\n  tachyon_bn254_g1_init();\n  tachyon_bn254_g1_msm_ptr msm =\n      tachyon_bn254_g1_create_msm(config.exponents().back());\n\n  std::cout << \"Generating random points...\" << std::endl;\n  size_t max_point_num = point_nums.back();\n  VariableBaseMSMTestSet<bn254::G1AffinePoint> test_set;\n  CHECK(config.GenerateTestSet(max_point_num, &test_set));\n  std::cout << \"Generation completed\" << std::endl;\n\n  MSMRunner<bn254::G1AffinePoint> runner(reporter);\n  runner.SetInputs(test_set.bases, test_set.scalars);\n\n  std::vector<bn254::G1JacobianPoint> results_cpu;\n  runner.Run(Vendor::TachyonCPU(), tachyon_bn254_g1_affine_msm, msm, point_nums,\n             results_cpu);\n  tachyon_bn254_g1_destroy_msm(msm);\n\n  tachyon_bn254_g1_msm_gpu_ptr msm_gpu =\n      tachyon_bn254_g1_create_msm_gpu(config.exponents().back());\n  std::vector<bn254::G1JacobianPoint> results_gpu;\n  runner.Run(Vendor::TachyonGPU(), tachyon_bn254_g1_affine_msm_gpu, msm_gpu,\n             point_nums, results_gpu);\n  tachyon_bn254_g1_destroy_msm_gpu(msm_gpu);\n\n  if (config.check_results()) {\n    CHECK(results_cpu == results_gpu) << \"Results do not match\";\n  }\n\n  reporter.Show();\n\n  return 0;\n}\n\n}  // namespace tachyon::benchmark\n\nint main(int argc, char** argv) {\n  return tachyon::benchmark::RealMain(argc, argv);\n}\n#else\n#include \"tachyon/base/console/iostream.h\"\n\nint main(int argc, char **argv) {\n  tachyon_cerr << \"please build with --config cuda\" << std::endl;\n  return 1;\n}\n#endif  // TACHYON_CUDA\n"
  },
  {
    "path": "benchmark/msm/msm_config.cc",
    "content": "#include \"benchmark/msm/msm_config.h\"\n\n#include <set>\n#include <string>\n\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/base/console/iostream.h\"\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/ranges/algorithm.h\"\n\nnamespace tachyon {\n\nusing MSMConfig = benchmark::MSMConfig;\n\nnamespace base {\n\ntemplate <>\nclass FlagValueTraits<MSMConfig::TestSet> {\n public:\n  static bool ParseValue(std::string_view input, MSMConfig::TestSet* value,\n                         std::string* reason) {\n    if (input == \"random\") {\n      *value = MSMConfig::TestSet::kRandom;\n    } else if (input == \"non_uniform\") {\n      *value = MSMConfig::TestSet::kNonUniform;\n    } else {\n      *reason = absl::Substitute(\"Unknown test set: $0\", input);\n      return false;\n    }\n    return true;\n  }\n};\n\n}  // namespace base\n\nMSMConfig::MSMConfig() : MSMConfig(Options()) {}\n\nMSMConfig::MSMConfig(const Options& options)\n    : Config(options), include_vendors_(options.include_vendors) {\n  parser_.AddFlag<base::Flag<std::vector<uint32_t>>>(&exponents_)\n      .set_short_name(\"-k\")\n      .set_required()\n      .set_help(\n          \"Specify the exponent 'k' where the number of points to test is 2ᵏ.\");\n  parser_.AddFlag<base::Flag<TestSet>>(&test_set_)\n      .set_long_name(\"--test_set\")\n      .set_default_value(TestSet::kRandom)\n      .set_help(\n          \"Testset to be benchmarked with. (supported testset: random, \"\n          \"non_uniform). By default, random.\");\n  if (include_vendors_) {\n    parser_.AddFlag<base::Flag<std::set<benchmark::Vendor>>>(&vendors_)\n        .set_long_name(\"--vendor\")\n        .set_help(\n            \"Vendors to be benchmarked with. (supported vendors: arkworks, \"\n            \"bellman, halo2)\");\n  }\n}\n\nvoid MSMConfig::PostParse() {\n  base::ranges::sort(exponents_);  // NOLINT(build/include_what_you_use)\n}\n\nbool MSMConfig::Validate() const {\n  if (include_vendors_) {\n    for (const Vendor vendor : vendors_) {\n      if ((vendor.value() != Vendor::kArkworks) &&\n          (vendor.value() != Vendor::kBellman) &&\n          (vendor.value() != Vendor::kScrollHalo2)) {\n        tachyon_cerr << \"Unsupported vendor \" << vendor.ToString() << std::endl;\n        return false;\n      }\n    }\n  }\n  return true;\n}\n\nstd::vector<size_t> MSMConfig::GetPointNums() const {\n  return base::Map(exponents_,\n                   [](uint32_t exponent) { return size_t{1} << exponent; });\n}\n\n}  // namespace tachyon\n"
  },
  {
    "path": "benchmark/msm/msm_config.h",
    "content": "#ifndef BENCHMARK_MSM_MSM_CONFIG_H_\n#define BENCHMARK_MSM_MSM_CONFIG_H_\n\n#include <stdint.h>\n\n#include <vector>\n\n// clang-format off\n#include \"benchmark/config.h\"\n// clang-format on\n#include \"tachyon/math/elliptic_curves/msm/test/variable_base_msm_test_set.h\"\n\nnamespace tachyon::benchmark {\n\nclass MSMConfig : public Config {\n public:\n  enum class TestSet {\n    kRandom,\n    kNonUniform,\n  };\n\n  struct Options : public Config::Options {\n    bool include_vendors = false;\n  };\n\n  MSMConfig();\n  explicit MSMConfig(const Options& options);\n  MSMConfig(const MSMConfig& other) = delete;\n  MSMConfig& operator=(const MSMConfig& other) = delete;\n\n  const std::vector<uint32_t>& exponents() const { return exponents_; }\n\n  std::vector<size_t> GetPointNums() const;\n\n  template <typename Point, typename Bucket>\n  bool GenerateTestSet(size_t size,\n                       math::VariableBaseMSMTestSet<Point, Bucket>* out) const {\n    switch (test_set_) {\n      case TestSet::kRandom:\n        *out = math::VariableBaseMSMTestSet<Point, Bucket>::Random(\n            size, math::VariableBaseMSMMethod::kNone);\n        return true;\n      case TestSet::kNonUniform:\n        *out = math::VariableBaseMSMTestSet<Point, Bucket>::NonUniform(\n            size, 1, math::VariableBaseMSMMethod::kNone);\n        return true;\n    }\n    return false;\n  }\n\n private:\n  // Config methods\n  void PostParse() override;\n  bool Validate() const override;\n\n  std::vector<uint32_t> exponents_;\n  TestSet test_set_;\n  bool include_vendors_;\n};\n\n}  // namespace tachyon::benchmark\n\n#endif  // BENCHMARK_MSM_MSM_CONFIG_H_\n"
  },
  {
    "path": "benchmark/msm/msm_runner.h",
    "content": "#ifndef BENCHMARK_MSM_MSM_RUNNER_H_\n#define BENCHMARK_MSM_MSM_RUNNER_H_\n\n#include <stddef.h>\n\n#include <memory>\n#include <vector>\n\n#include \"absl/types/span.h\"\n\n// clang-format off\n#include \"benchmark/simple_reporter.h\"\n// clang-format on\n#include \"tachyon/base/time/time.h\"\n#include \"tachyon/c/base/type_traits_forward.h\"\n#include \"tachyon/c/math/elliptic_curves/point_traits_forward.h\"\n#include \"tachyon/math/base/semigroups.h\"\n\nnamespace tachyon::benchmark {\n\ntemplate <typename Point>\nclass MSMRunner {\n public:\n  using ScalarField = typename Point::ScalarField;\n  using RetPoint =\n      typename math::internal::AdditiveSemigroupTraits<Point>::ReturnTy;\n\n  using CPoint = typename c::math::PointTraits<Point>::CCurvePoint;\n  using CRetPoint = typename c::math::PointTraits<RetPoint>::CCurvePoint;\n  using CScalarField = typename c::math::PointTraits<Point>::CScalarField;\n\n  typedef CRetPoint* (*MSMAffineExternalFn)(const CPoint* bases,\n                                            const CScalarField* scalars,\n                                            size_t size,\n                                            uint64_t* duration_in_us);\n\n  explicit MSMRunner(SimpleReporter& reporter) : reporter_(reporter) {}\n\n  void SetInputs(absl::Span<const Point> bases,\n                 absl::Span<const ScalarField> scalars) {\n    bases_ = bases;\n    scalars_ = scalars;\n  }\n\n  template <typename Fn, typename MSMPtr>\n  void Run(Vendor vendor, Fn fn, MSMPtr msm,\n           const std::vector<size_t>& point_nums,\n           std::vector<RetPoint>& results) {\n    reporter_.AddVendor(vendor);\n\n    results.clear();\n    results.reserve(point_nums.size());\n    for (size_t i = 0; i < point_nums.size(); ++i) {\n      base::TimeTicks now = base::TimeTicks::Now();\n      std::unique_ptr<CRetPoint> ret;\n      ret.reset(fn(msm, c::base::c_cast(bases_.data()),\n                   c::base::c_cast(scalars_.data()), point_nums[i]));\n      reporter_.AddTime(vendor, (base::TimeTicks::Now() - now));\n      results.push_back(*c::base::native_cast(ret.get()));\n    }\n  }\n\n  void RunExternal(Vendor vendor, MSMAffineExternalFn fn,\n                   const std::vector<size_t>& point_nums,\n                   std::vector<RetPoint>& results) const {\n    reporter_.AddVendor(vendor);\n\n    results.clear();\n    results.reserve(point_nums.size());\n    for (size_t i = 0; i < point_nums.size(); ++i) {\n      std::unique_ptr<CRetPoint> ret;\n      uint64_t duration_in_us;\n      ret.reset(fn(c::base::c_cast(bases_.data()),\n                   c::base::c_cast(scalars_.data()), point_nums[i],\n                   &duration_in_us));\n      reporter_.AddTime(vendor, base::Microseconds(duration_in_us));\n      results.push_back(*c::base::native_cast(ret.get()));\n    }\n  }\n\n private:\n  SimpleReporter& reporter_;\n  absl::Span<const Point> bases_;\n  absl::Span<const ScalarField> scalars_;\n};\n\n}  // namespace tachyon::benchmark\n\n#endif  // BENCHMARK_MSM_MSM_RUNNER_H_\n"
  },
  {
    "path": "benchmark/poseidon/BUILD.bazel",
    "content": "load(\n    \"//bazel:tachyon_cc.bzl\",\n    \"tachyon_cc_binary\",\n    \"tachyon_cc_library\",\n)\n\npackage(default_visibility = [\"//benchmark/poseidon2:__pkg__\"])\n\ntachyon_cc_library(\n    name = \"poseidon_config\",\n    testonly = True,\n    srcs = [\"poseidon_config.cc\"],\n    hdrs = [\"poseidon_config.h\"],\n    deps = [\"//benchmark:config\"],\n)\n\ntachyon_cc_library(\n    name = \"poseidon_benchmark_runner\",\n    testonly = True,\n    hdrs = [\"poseidon_benchmark_runner.h\"],\n    deps = [\n        \":poseidon_config\",\n        \"//benchmark:simple_reporter\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/base/time\",\n        \"//tachyon/c/base:type_traits_forward\",\n        \"//tachyon/crypto/hashes/sponge/poseidon\",\n    ],\n)\n\ntachyon_cc_binary(\n    name = \"poseidon_benchmark\",\n    testonly = True,\n    srcs = [\"poseidon_benchmark.cc\"],\n    deps = [\n        \":poseidon_benchmark_runner\",\n        \":poseidon_config\",\n        \"//benchmark:simple_reporter\",\n        \"//benchmark/poseidon/arkworks\",\n        \"//tachyon/base:profiler\",\n        \"//tachyon/c/math/elliptic_curves/bn/bn254:fr\",\n        \"//tachyon/crypto/hashes/sponge/poseidon:poseidon_params\",\n    ],\n)\n"
  },
  {
    "path": "benchmark/poseidon/README.md",
    "content": "# Poseidon Hash Benchmark\n\n```\nRun on 13th Gen Intel(R) Core(TM) i9-13900K (32 X 5500 MHz CPU s)\nCompiler: clang-15\nCPU Caches:\n  L1 Data 48 KiB (x16)\n  L1 Instruction 32 KiB (x16)\n  L2 Unified 2048 KiB (x16)\n  L3 Unified 36864 KiB (x1)\n\nRun on Apple M3 Pro (12 X 4050 MHz)\nCPU Caches:\n  L1 Data 64 KiB (x12)\n  L1 Instruction 128 KiB (x12)\n  L2 Unified 4096 KiB (x12)\n```\n\n```shell\nGOMP_SPINCOUNT=0 bazel run --config maxopt --//:has_matplotlib //benchmark/poseidon:poseidon_benchmark -- --check_results\n```\n\n## On Intel i9-13900K\n\n| Repetition | Tachyon     | Arkworks |\n| :--------: | ----------- | -------- |\n|     0      | **3.5e-05** | 0.000107 |\n|     1      | **3.2e-05** | 0.000106 |\n|     2      | **3.2e-05** | 0.000108 |\n|     3      | **3.1e-05** | 0.000107 |\n|     4      | **3.1e-05** | 0.000107 |\n|     5      | **3.1e-05** | 0.000107 |\n|     6      | **3.1e-05** | 0.000104 |\n|     7      | **3.1e-05** | 0.000105 |\n|     8      | **3.1e-05** | 0.000106 |\n|     9      | **3.1e-05** | 0.000107 |\n|    avg     | **3.1e-05** | 0.000106 |\n\n![image](/benchmark/poseidon/poseidon_benchmark_ubuntu_i9.png)\n\n## On Mac M3 Pro\n\n| Repetition | Tachyon     | Arkworks |\n| :--------: | ----------- | -------- |\n|     0      | **3.7e-05** | 0.000111 |\n|     1      | **3.5e-05** | 0.000105 |\n|     2      | **3.3e-05** | 0.000103 |\n|     3      | **3.2e-05** | 0.000104 |\n|     4      | **3.2e-05** | 0.000101 |\n|     5      | **3.2e-05** | 0.000103 |\n|     6      | **3.2e-05** | 0.000105 |\n|     7      | **3.2e-05** | 0.000102 |\n|     8      | **3.2e-05** | 0.000102 |\n|     9      | **3.2e-05** | 0.000102 |\n|    avg     | **3.2e-05** | 0.000103 |\n\n![image](/benchmark/poseidon/poseidon_benchmark_mac_m3.png)\n"
  },
  {
    "path": "benchmark/poseidon/arkworks/BUILD.bazel",
    "content": "load(\"@crate_index//:defs.bzl\", \"aliases\", \"all_crate_deps\")\nload(\"//bazel:tachyon_rust.bzl\", \"tachyon_rust_static_library\")\n\ntachyon_rust_static_library(\n    name = \"arkworks\",\n    srcs = glob([\"src/**/*.rs\"]),\n    aliases = aliases(),\n    proc_macro_deps = all_crate_deps(proc_macro = True),\n    visibility = [\"//benchmark/poseidon:__pkg__\"],\n    deps = all_crate_deps(normal = True) + [\n        \"//tachyon/rs:tachyon_rs\",\n    ],\n)\n"
  },
  {
    "path": "benchmark/poseidon/arkworks/Cargo.toml",
    "content": "[package]\nname = \"arkworks_poseidon_benchmark\"\nversion = \"0.0.1\"\nauthors = [\"The Tachyon Authors <tachyon-discuss@kroma.network>\"]\nedition = \"2021\"\nrust-version = \"1.56.1\"\ndescription = \"\"\"\nArkworks Poseidon Hash Benchmark\n\"\"\"\nlicense = \"MIT OR Apache-2.0\"\nrepository = \"https://github.com/kroma-network/tachyon\"\nreadme = \"README.md\"\ncategories = [\"cryptography\"]\nkeywords = [\"tachyon\", \"benchmark\", \"arkworks\"]\npublish = false\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[dependencies]\nark-ff = { version = \"^0.4.0\", features = [\"asm\"] }\nark-bn254 = \"0.4.0\"\nark-crypto-primitives = { git = \"https://github.com/kroma-network/crypto-primitives.git\", features = [\n    \"sponge\",\n], rev = \"99f5aff\" }\ntachyon_rs = { path = \"../../../tachyon/rs\" }\n"
  },
  {
    "path": "benchmark/poseidon/arkworks/src/lib.rs",
    "content": "use ark_bn254::Fr;\nuse ark_crypto_primitives::sponge::poseidon::traits::find_poseidon_ark_and_mds;\nuse ark_crypto_primitives::sponge::poseidon::{PoseidonConfig, PoseidonSponge};\nuse ark_crypto_primitives::sponge::CryptographicSponge;\nuse std::time::Instant;\nuse tachyon_rs::math::elliptic_curves::bn::bn254::Fr as CppFr;\n\n#[no_mangle]\npub extern \"C\" fn run_poseidon_arkworks(duration: *mut u64) -> *mut CppFr {\n    let (ark, mds) = find_poseidon_ark_and_mds::<Fr>(254, 8, 8, 63, 0);\n    let poseidon_config = PoseidonConfig {\n        full_rounds: 8,\n        partial_rounds: 63,\n        alpha: 5,\n        ark,\n        mds,\n        rate: 8,\n        capacity: 1,\n    };\n\n    let mut sponge = PoseidonSponge::new(&poseidon_config);\n\n    let start = Instant::now();\n    sponge.permute();\n    unsafe {\n        duration.write(start.elapsed().as_micros() as u64);\n    }\n\n    Box::into_raw(Box::new(sponge.state[1])) as *mut CppFr\n}\n"
  },
  {
    "path": "benchmark/poseidon/poseidon_benchmark.cc",
    "content": "#include <iostream>\n\n// clang-format off\n#include \"benchmark/poseidon/poseidon_config.h\"\n#include \"benchmark/poseidon/poseidon_benchmark_runner.h\"\n#include \"benchmark/simple_reporter.h\"\n// clang-format on\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/profiler.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/fr.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/fr_type_traits.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon/poseidon_params.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/fr.h\"\n\nnamespace tachyon::benchmark {\n\nusing Field = math::bn254::Fr;\n\nextern \"C\" tachyon_bn254_fr* run_poseidon_arkworks(uint64_t* duration);\n\nint RealMain(int argc, char** argv) {\n  base::FilePath tmp_file;\n  CHECK(base::GetTempDir(&tmp_file));\n  tmp_file = tmp_file.Append(\"poseidon_benchmark.perfetto-trace\");\n  base::Profiler profiler({tmp_file});\n\n  profiler.Init();\n  profiler.Start();\n\n  PoseidonConfig config;\n  if (!config.Parse(argc, argv)) {\n    return 1;\n  }\n\n  Field::Init();\n  SimpleReporter reporter;\n  PoseidonBenchmarkRunner<Field> runner(reporter, config);\n\n  reporter.set_title(\"Poseidon Benchmark\");\n  reporter.set_x_label(\"Trial number\");\n  reporter.set_column_labels(\n      base::CreateVector(config.repeating_num(),\n                         [](size_t i) { return base::NumberToString(i); }));\n\n  Field result = runner.template Run<crypto::BN254PoseidonParams9>();\n  Field result_arkworks =\n      runner.RunExternal(Vendor::Arkworks(), run_poseidon_arkworks);\n\n  if (config.check_results()) {\n    CHECK_EQ(result, result_arkworks) << \"Results do not match\";\n  }\n\n  reporter.AddAverageAsLastColumn();\n  reporter.Show();\n\n  return 0;\n}\n\n}  // namespace tachyon::benchmark\n\nint main(int argc, char** argv) {\n  return tachyon::benchmark::RealMain(argc, argv);\n}\n"
  },
  {
    "path": "benchmark/poseidon/poseidon_benchmark_runner.h",
    "content": "#ifndef BENCHMARK_POSEIDON_POSEIDON_BENCHMARK_RUNNER_H_\n#define BENCHMARK_POSEIDON_POSEIDON_BENCHMARK_RUNNER_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <memory>\n#include <utility>\n#include <vector>\n\n// clang-format off\n#include \"benchmark/poseidon/poseidon_config.h\"\n#include \"benchmark/simple_reporter.h\"\n// clang-format on\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/time/time.h\"\n#include \"tachyon/c/base/type_traits_forward.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon/poseidon.h\"\n\nnamespace tachyon::benchmark {\n\ntemplate <typename Field>\nclass PoseidonBenchmarkRunner {\n public:\n  using CPrimeField = typename c::base::TypeTraits<Field>::CType;\n\n  typedef CPrimeField* (*PoseidonExternalFn)(uint64_t* duration);\n\n  PoseidonBenchmarkRunner(SimpleReporter& reporter,\n                          const PoseidonConfig& config)\n      : reporter_(reporter), config_(config) {}\n\n  template <typename Params>\n  Field Run() {\n    reporter_.AddVendor(Vendor::Tachyon());\n    Field ret;\n    for (size_t i = 0; i < config_.repeating_num(); ++i) {\n      auto config = crypto::PoseidonConfig<Params>::Create(0);\n      crypto::PoseidonSponge<Params> sponge(std::move(config));\n      crypto::SpongeState<Params> state;\n      base::TimeTicks start = base::TimeTicks::Now();\n      sponge.Permute(state);\n      reporter_.AddTime(Vendor::Tachyon(), base::TimeTicks::Now() - start);\n      if (i == 0) {\n        ret = state.elements[1];\n      }\n    }\n    return ret;\n  }\n\n  Field RunExternal(Vendor vendor, PoseidonExternalFn fn) {\n    reporter_.AddVendor(vendor);\n    std::unique_ptr<CPrimeField> ret;\n    for (size_t i = 0; i < config_.repeating_num(); ++i) {\n      uint64_t duration_in_us;\n      ret.reset(fn(&duration_in_us));\n      reporter_.AddTime(vendor, base::Microseconds(duration_in_us));\n    }\n    return *c::base::native_cast(ret.get());\n  }\n\n private:\n  SimpleReporter& reporter_;\n  const PoseidonConfig& config_;\n};\n\n}  // namespace tachyon::benchmark\n\n#endif  // BENCHMARK_POSEIDON_POSEIDON_BENCHMARK_RUNNER_H_\n"
  },
  {
    "path": "benchmark/poseidon/poseidon_config.cc",
    "content": "#include \"benchmark/poseidon/poseidon_config.h\"\n\nnamespace tachyon::benchmark {\n\nPoseidonConfig::PoseidonConfig() {\n  parser_.AddFlag<base::Flag<size_t>>(&repeating_num_)\n      .set_short_name(\"-n\")\n      .set_default_value(10)\n      .set_help(\"Specify the number of repetition 'n'. By default, 10.\");\n}\n\n}  // namespace tachyon::benchmark\n"
  },
  {
    "path": "benchmark/poseidon/poseidon_config.h",
    "content": "#ifndef BENCHMARK_POSEIDON_POSEIDON_CONFIG_H_\n#define BENCHMARK_POSEIDON_POSEIDON_CONFIG_H_\n\n#include <stddef.h>\n\n// clang-format off\n#include \"benchmark/config.h\"\n// clang-format on\n\nnamespace tachyon::benchmark {\n\nclass PoseidonConfig : public Config {\n public:\n  PoseidonConfig();\n  PoseidonConfig(const PoseidonConfig& other) = delete;\n  PoseidonConfig& operator=(const PoseidonConfig& other) = delete;\n\n  size_t repeating_num() const { return repeating_num_; }\n\n private:\n  size_t repeating_num_;\n};\n\n}  // namespace tachyon::benchmark\n\n#endif  // BENCHMARK_POSEIDON_POSEIDON_CONFIG_H_\n"
  },
  {
    "path": "benchmark/poseidon2/BUILD.bazel",
    "content": "load(\n    \"//bazel:tachyon_cc.bzl\",\n    \"tachyon_cc_binary\",\n    \"tachyon_cc_library\",\n)\n\ntachyon_cc_library(\n    name = \"poseidon2_config\",\n    testonly = True,\n    srcs = [\"poseidon2_config.cc\"],\n    hdrs = [\"poseidon2_config.h\"],\n    deps = [\n        \"//benchmark:config\",\n        \"//benchmark:field_type\",\n        \"//tachyon/base/console\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"poseidon2_benchmark_runner\",\n    testonly = True,\n    hdrs = [\"poseidon2_benchmark_runner.h\"],\n    deps = [\n        \":poseidon2_config\",\n        \"//benchmark:simple_reporter\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base:profiler\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/base/time\",\n        \"//tachyon/c/base:type_traits_forward\",\n        \"//tachyon/crypto/hashes/sponge/poseidon2\",\n    ],\n)\n\ntachyon_cc_binary(\n    name = \"poseidon2_benchmark\",\n    testonly = True,\n    srcs = [\"poseidon2_benchmark.cc\"],\n    deps = [\n        \":poseidon2_benchmark_runner\",\n        \":poseidon2_config\",\n        \"//benchmark:simple_reporter\",\n        \"//benchmark/poseidon2/horizen\",\n        \"//benchmark/poseidon2/plonky3\",\n        \"//tachyon/base/containers:contains\",\n        \"//tachyon/c/math/elliptic_curves/bn/bn254:fr\",\n        \"//tachyon/c/math/finite_fields/baby_bear\",\n        \"//tachyon/crypto/hashes/sponge/poseidon2:poseidon2_params\",\n        \"//tachyon/crypto/hashes/sponge/poseidon2/param_traits:poseidon2_baby_bear\",\n        \"//tachyon/crypto/hashes/sponge/poseidon2/param_traits:poseidon2_bn254\",\n    ],\n)\n"
  },
  {
    "path": "benchmark/poseidon2/README.md",
    "content": "# Poseidon2 Hash Benchmark\n\n```\nRun on 13th Gen Intel(R) Core(TM) i9-13900K (32 X 5500 MHz CPU s)\nCompiler: clang-15\nCPU Caches:\n  L1 Data 48 KiB (x16)\n  L1 Instruction 32 KiB (x16)\n  L2 Unified 2048 KiB (x16)\n  L3 Unified 36864 KiB (x1)\n\nRun on Apple M3 Pro (12 X 4050 MHz)\nCPU Caches:\n  L1 Data 64 KiB (x12)\n  L1 Instruction 128 KiB (x12)\n  L2 Unified 4096 KiB (x12)\n```\n\nNote: Run with `build --@rules_rust//:extra_rustc_flags=\"-Ctarget-cpu=native\"` in your .bazelrc.user\nNote that Poseidon2 runs 10000x per test due to some time results being too small when running a single iteration.\n\n## BN254\n\n```shell\nGOMP_SPINCOUNT=0 bazel run --config maxopt --//:has_matplotlib //benchmark/poseidon2:poseidon2_benchmark -- -p bn254_fr --vendor horizen --vendor plonky3 --check_results\n```\n\n### On Intel i9-13900K\n\n| Trial Number | Tachyon  | Horizen      | Plonky3  |\n| :----------- | -------- | ------------ | -------- |\n| 0            | 0.069228 | **0.050903** | 0.085679 |\n| 1            | 0.062046 | **0.050892** | 0.085772 |\n| 2            | 0.06053  | **0.050848** | 0.08553  |\n| 3            | 0.060648 | **0.050825** | 0.085643 |\n| 4            | 0.060553 | **0.051126** | 0.08583  |\n| 5            | 0.060592 | **0.05362**  | 0.085475 |\n| 6            | 0.060576 | **0.050936** | 0.085731 |\n| 7            | 0.06051  | **0.05081**  | 0.085613 |\n| 8            | 0.060561 | **0.050889** | 0.086382 |\n| 9            | 0.060558 | **0.050896** | 0.086557 |\n| avg          | 0.06158  | **0.051174** | 0.085821 |\n\n![image](/benchmark/poseidon2/poseidon2_benchmark_bn254_ubuntu_i9.png)\n\n### On Mac M3 Pro\n\n| Trial Number | Tachyon  | Horizen      | Plonky3  |\n| :----------- | -------- | ------------ | -------- |\n| 0            | 0.068967 | **0.058728** | 0.086994 |\n| 1            | 0.068786 | **0.05839**  | 0.086825 |\n| 2            | 0.068658 | **0.058245** | 0.086779 |\n| 3            | 0.068673 | **0.058189** | 0.086693 |\n| 4            | 0.068675 | **0.058303** | 0.08674  |\n| 5            | 0.068693 | **0.058109** | 0.08681  |\n| 6            | 0.068621 | **0.058405** | 0.086816 |\n| 7            | 0.068747 | **0.058247** | 0.086871 |\n| 8            | 0.068637 | **0.058383** | 0.086842 |\n| 9            | 0.068665 | **0.058162** | 0.086846 |\n| avg          | 0.068712 | **0.058316** | 0.086821 |\n\n![image](/benchmark/poseidon2/poseidon2_benchmark_bn254_mac_m3.png)\n\n## Baby Bear\n\nNote: Horizen and Plonky3 compute values with a different internal matrix, requiring them to be compared with Tachyon separately.\n\n### Horizen\n\n```shell\nGOMP_SPINCOUNT=0 bazel run --config maxopt --//:has_matplotlib //benchmark/poseidon2:poseidon2_benchmark -- -p baby_bear --vendor horizen --check_results\n```\n\n#### On Intel i9-13900K\n\n| Trial Number | Tachyon      | Horizen  |\n| :----------- | ------------ | -------- |\n| 0            | **0.011424** | 0.034238 |\n| 1            | **0.011975** | 0.034214 |\n| 2            | **0.011505** | 0.034245 |\n| 3            | **0.011304** | 0.03418  |\n| 4            | **0.011313** | 0.034231 |\n| 5            | **0.011354** | 0.034234 |\n| 6            | **0.010743** | 0.034487 |\n| 7            | **0.01071**  | 0.034259 |\n| 8            | **0.010706** | 0.034229 |\n| 9            | **0.010708** | 0.034246 |\n| avg          | **0.011174** | 0.034256 |\n\n![image](/benchmark/poseidon2/poseidon2_benchmark_baby_bear_horizen_ubuntu_i9.png)\n\n#### On Mac M3 Pro\n\n| Trial Number | Tachyon      | Horizen  |\n| :----------- | ------------ | -------- |\n| 0            | **0.010679** | 0.014511 |\n| 1            | **0.010448** | 0.014653 |\n| 2            | **0.010286** | 0.014961 |\n| 3            | **0.01024**  | 0.014769 |\n| 4            | **0.010233** | 0.014717 |\n| 5            | **0.010267** | 0.014761 |\n| 6            | **0.010226** | 0.01514  |\n| 7            | **0.010303** | 0.01475  |\n| 8            | **0.010253** | 0.014693 |\n| 9            | **0.010326** | 0.014533 |\n| avg          | **0.010326** | 0.014748 |\n\n![image](/benchmark/poseidon2/poseidon2_benchmark_baby_bear_horizen_mac_m3.png)\n\n### Plonky3\n\n```shell\nGOMP_SPINCOUNT=0 bazel run --config maxopt --//:has_matplotlib //benchmark/poseidon2:poseidon2_benchmark -- -p baby_bear --vendor plonky3 --check_results\n```\n\n#### On Intel i9-13900K\n\n| Trial Number | Tachyon  | Plonky3      |\n| :----------- | -------- | ------------ |\n| 0            | 0.00999  | **0.005391** |\n| 1            | 0.009882 | **0.005298** |\n| 2            | 0.009848 | **0.00513**  |\n| 3            | 0.009772 | **0.005157** |\n| 4            | 0.00977  | **0.005072** |\n| 5            | 0.009774 | **0.005032** |\n| 6            | 0.009783 | **0.005062** |\n| 7            | 0.009878 | **0.005077** |\n| 8            | 0.009778 | **0.005014** |\n| 9            | 0.009762 | **0.005016** |\n| avg          | 0.009823 | **0.005124** |\n\n![image](/benchmark/poseidon2/poseidon2_benchmark_baby_bear_plonky3_ubuntu_i9.png)\n\n#### On Mac M3 Pro\n\n| Trial Number | Tachyon  | Plonky3      |\n| :----------- | -------- | ------------ |\n| 0            | 0.009116 | **0.007311** |\n| 1            | 0.008967 | **0.007352** |\n| 2            | 0.008805 | **0.007312** |\n| 3            | 0.008748 | **0.007315** |\n| 4            | 0.008742 | **0.007339** |\n| 5            | 0.008741 | **0.007309** |\n| 6            | 0.008774 | **0.00732**  |\n| 7            | 0.00873  | **0.007696** |\n| 8            | 0.008791 | **0.007342** |\n| 9            | 0.008741 | **0.007353** |\n| avg          | 0.008815 | **0.007364** |\n\n![image](/benchmark/poseidon2/poseidon2_benchmark_baby_bear_plonky3_mac_m3.png)\\*\\*\\*\\*\n"
  },
  {
    "path": "benchmark/poseidon2/horizen/BUILD.bazel",
    "content": "load(\"@crate_index//:defs.bzl\", \"aliases\", \"all_crate_deps\")\nload(\"//bazel:tachyon_rust.bzl\", \"tachyon_rust_static_library\")\n\ntachyon_rust_static_library(\n    name = \"horizen\",\n    srcs = glob([\"src/**/*.rs\"]),\n    aliases = aliases(),\n    proc_macro_deps = all_crate_deps(proc_macro = True),\n    visibility = [\"//benchmark/poseidon2:__pkg__\"],\n    deps = all_crate_deps(normal = True) + [\n        \"//tachyon/rs:tachyon_rs\",\n    ],\n)\n"
  },
  {
    "path": "benchmark/poseidon2/horizen/Cargo.toml",
    "content": "[package]\nname = \"horizen_poseidon2_benchmark\"\nversion = \"0.0.1\"\nauthors = [\"The Tachyon Authors <tachyon-discuss@kroma.network>\"]\nedition = \"2021\"\nrust-version = \"1.56.1\"\ndescription = \"\"\"\nHorizen Poseidon2 Hash Benchmark\n\"\"\"\nlicense = \"MIT OR Apache-2.0\"\nrepository = \"https://github.com/kroma-network/tachyon\"\nreadme = \"README.md\"\ncategories = [\"cryptography\"]\nkeywords = [\"tachyon\", \"benchmark\", \"horizen\"]\npublish = false\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[dependencies]\nzkhash = { git = \"https://github.com/HorizenLabs/poseidon2.git\", rev = \"bb476b9\" }\ntachyon_rs = { path = \"../../../tachyon/rs\" }\n"
  },
  {
    "path": "benchmark/poseidon2/horizen/src/lib.rs",
    "content": "use std::{sync::Arc, time::Instant};\nuse tachyon_rs::math::{\n    elliptic_curves::bn::bn254::Fr as CppBn254Fr, finite_fields::baby_bear::BabyBear as CppBabyBear,\n};\nuse zkhash::{\n    ark_ff::PrimeField,\n    poseidon2::{\n        poseidon2::Poseidon2, poseidon2_instance_babybear::POSEIDON2_BABYBEAR_16_PARAMS,\n        poseidon2_instance_bn256::POSEIDON2_BN256_PARAMS, poseidon2_params::Poseidon2Params,\n    },\n};\n\nfn run_poseidon2<F: PrimeField + std::convert::From<i32>, R>(\n    duration: *mut u64,\n    params: &Arc<Poseidon2Params<F>>,\n) -> *mut R {\n    let poseidon2 = Poseidon2::new(params);\n\n    let t = poseidon2.get_t();\n    let mut input: Vec<F> = (0..t).map(|_| F::from(0)).collect();\n\n    let start = Instant::now();\n    for _ in 0..10000 {\n        input = poseidon2.permutation(&input);\n    }\n    unsafe {\n        duration.write(start.elapsed().as_micros() as u64);\n    }\n    Box::into_raw(Box::new(input[1] as F)) as *mut R\n}\n\n#[no_mangle]\npub extern \"C\" fn run_poseidon2_horizen_baby_bear(duration: *mut u64) -> *mut CppBabyBear {\n    run_poseidon2::<_, CppBabyBear>(duration, &POSEIDON2_BABYBEAR_16_PARAMS)\n}\n\n#[no_mangle]\npub extern \"C\" fn run_poseidon2_horizen_bn254_fr(duration: *mut u64) -> *mut CppBn254Fr {\n    run_poseidon2::<_, CppBn254Fr>(duration, &POSEIDON2_BN256_PARAMS)\n}\n"
  },
  {
    "path": "benchmark/poseidon2/plonky3/BUILD.bazel",
    "content": "load(\"@crate_index//:defs.bzl\", \"aliases\", \"all_crate_deps\")\nload(\"//bazel:tachyon_rust.bzl\", \"tachyon_rust_static_library\")\n\ntachyon_rust_static_library(\n    name = \"plonky3\",\n    srcs = glob([\"src/**/*.rs\"]),\n    aliases = aliases(),\n    proc_macro_deps = all_crate_deps(proc_macro = True),\n    visibility = [\"//benchmark/poseidon2:__pkg__\"],\n    deps = all_crate_deps(normal = True) + [\n        \"//tachyon/rs:tachyon_rs\",\n    ],\n)\n"
  },
  {
    "path": "benchmark/poseidon2/plonky3/Cargo.toml",
    "content": "[package]\nname = \"plonky3_poseidon2_benchmark\"\nversion = \"0.0.1\"\nauthors = [\"The Tachyon Authors <tachyon-discuss@kroma.network>\"]\nedition = \"2021\"\ndescription = \"\"\"\nPlonky3 Poseidon2 Hash Benchmark\n\"\"\"\nlicense = \"MIT OR Apache-2.0\"\nrepository = \"https://github.com/kroma-network/tachyon\"\nreadme = \"README.md\"\ncategories = [\"cryptography\"]\nkeywords = [\"tachyon\", \"benchmark\", \"plonky3\"]\npublish = false\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[dependencies]\nff = { version = \"0.13\", features = [\"derive\", \"derive_bits\"] }\np3-baby-bear = \"0.1.3-succinct\"\np3-bn254-fr = \"0.1.3-succinct\"\np3-field = \"0.1.3-succinct\"\np3-poseidon2 = \"0.1.3-succinct\"\np3-symmetric = \"0.1.3-succinct\"\nzkhash = { git = \"https://github.com/HorizenLabs/poseidon2.git\", rev = \"bb476b9\" }\ntachyon_rs = { path = \"../../../tachyon/rs\" }\n"
  },
  {
    "path": "benchmark/poseidon2/plonky3/src/lib.rs",
    "content": "use core::fmt;\nuse ff::PrimeField;\nuse p3_baby_bear::{BabyBear, DiffusionMatrixBabyBear};\nuse p3_bn254_fr::{Bn254Fr, DiffusionMatrixBN254, FFBn254Fr};\nuse p3_field::AbstractField;\nuse p3_poseidon2::{DiffusionPermutation, Poseidon2, Poseidon2ExternalMatrixHL};\nuse p3_symmetric::Permutation;\nuse std::time::Instant;\nuse tachyon_rs::math::{\n    elliptic_curves::bn::bn254::Fr as CppBn254Fr, finite_fields::baby_bear::BabyBear as CppBabyBear,\n};\nuse zkhash::ark_ff::{BigInteger, Field, PrimeField as ark_PrimeField};\nuse zkhash::fields::{babybear::FpBabyBear as ark_FpBabyBear, bn256::FpBN256 as ark_FpBN256};\nuse zkhash::poseidon2::{\n    poseidon2_instance_babybear::RC16 as BabyBearRC16, poseidon2_instance_bn256::RC3 as BN256RC3,\n};\n\nfn bn254_from_ark_ff(input: ark_FpBN256) -> Bn254Fr {\n    let bytes = input.into_bigint().to_bytes_le();\n\n    let mut res = <FFBn254Fr as PrimeField>::Repr::default();\n\n    for (i, digit) in res.0.as_mut().iter_mut().enumerate() {\n        *digit = bytes[i];\n    }\n\n    let value = FFBn254Fr::from_repr(res);\n\n    if value.is_some().into() {\n        Bn254Fr {\n            value: value.unwrap(),\n        }\n    } else {\n        panic!(\"Invalid field element\")\n    }\n}\n\nfn baby_bear_from_ark_ff(input: ark_FpBabyBear) -> BabyBear {\n    BabyBear::from_canonical_u32(input.into_bigint().0[0] as u32)\n}\n\nfn run_poseidon2<\n    const WIDTH: usize,\n    const D: u64,\n    const ROUNDS_F: usize,\n    const ROUNDS_P: usize,\n    NativeF: p3_field::PrimeField + fmt::Debug,\n    F: Field,\n    R,\n    DiffusionMatrix: DiffusionPermutation<NativeF, WIDTH>,\n>(\n    duration: *mut u64,\n    rc: &Vec<Vec<F>>,\n    from_ark_ff: &dyn Fn(F) -> NativeF,\n    diffusion_matrix: DiffusionMatrix,\n) -> *mut R {\n    // Copy over round constants from zkhash.\n    let mut round_constants: Vec<[NativeF; WIDTH]> = rc\n        .iter()\n        .map(|vec| {\n            vec.iter()\n                .cloned()\n                .map(from_ark_ff)\n                .collect::<Vec<_>>()\n                .try_into()\n                .unwrap()\n        })\n        .collect();\n    let internal_start = ROUNDS_F / 2;\n    let internal_end = (ROUNDS_F / 2) + ROUNDS_P;\n    let internal_round_constants = round_constants\n        .drain(internal_start..internal_end)\n        .map(|vec| vec[0])\n        .collect::<Vec<_>>();\n    let external_round_constants = round_constants;\n\n    let poseidon2 = Poseidon2::<NativeF, Poseidon2ExternalMatrixHL, DiffusionMatrix, WIDTH, D>::new(\n        ROUNDS_F,\n        external_round_constants,\n        Poseidon2ExternalMatrixHL,\n        ROUNDS_P,\n        internal_round_constants,\n        diffusion_matrix,\n    );\n\n    let mut input = (0..WIDTH)\n        .map(|_i| NativeF::zero())\n        .collect::<Vec<_>>()\n        .try_into()\n        .unwrap();\n\n    let start = Instant::now();\n    for _ in 0..10000 {\n        poseidon2.permute_mut(&mut input);\n    }\n    unsafe {\n        duration.write(start.elapsed().as_micros() as u64);\n    }\n\n    Box::into_raw(Box::new(input[1])) as *mut R\n}\n\n#[no_mangle]\npub extern \"C\" fn run_poseidon2_plonky3_baby_bear(duration: *mut u64) -> *mut CppBabyBear {\n    run_poseidon2::<16, 7, 8, 13, BabyBear, ark_FpBabyBear, CppBabyBear, DiffusionMatrixBabyBear>(\n        duration,\n        &BabyBearRC16,\n        &baby_bear_from_ark_ff,\n        DiffusionMatrixBabyBear,\n    )\n}\n\n#[no_mangle]\npub extern \"C\" fn run_poseidon2_plonky3_bn254_fr(duration: *mut u64) -> *mut CppBn254Fr {\n    run_poseidon2::<3, 5, 8, 56, Bn254Fr, ark_FpBN256, CppBn254Fr, DiffusionMatrixBN254>(\n        duration,\n        &BN256RC3,\n        &bn254_from_ark_ff,\n        DiffusionMatrixBN254,\n    )\n}\n"
  },
  {
    "path": "benchmark/poseidon2/poseidon2_benchmark.cc",
    "content": "#include <iostream>\n\n// clang-format off\n#include \"benchmark/simple_reporter.h\"\n#include \"benchmark/poseidon2/poseidon2_benchmark_runner.h\"\n#include \"benchmark/poseidon2/poseidon2_config.h\"\n// clang-format on\n#include \"tachyon/base/containers/contains.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/profiler.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/fr.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/fr_type_traits.h\"\n#include \"tachyon/c/math/finite_fields/baby_bear/baby_bear.h\"\n#include \"tachyon/c/math/finite_fields/baby_bear/baby_bear_type_traits.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/param_traits/poseidon2_baby_bear.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/param_traits/poseidon2_bn254.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_config.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_params.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/fr.h\"\n#include \"tachyon/math/finite_fields/baby_bear/baby_bear.h\"\n\nnamespace tachyon::benchmark {\n\nusing namespace crypto;\n\nextern \"C\" tachyon_baby_bear* run_poseidon2_horizen_baby_bear(\n    uint64_t* duration);\nextern \"C\" tachyon_baby_bear* run_poseidon2_plonky3_baby_bear(\n    uint64_t* duration);\nextern \"C\" tachyon_bn254_fr* run_poseidon2_horizen_bn254_fr(uint64_t* duration);\nextern \"C\" tachyon_bn254_fr* run_poseidon2_plonky3_bn254_fr(uint64_t* duration);\n\ntemplate <typename Field, typename Fn>\nvoid Run(SimpleReporter& reporter, const Poseidon2Config& config, Fn horizen_fn,\n         Fn plonky3_fn) {\n  Field::Init();\n\n  Poseidon2BenchmarkRunner<Field> runner(reporter, config);\n\n  Field result;\n  if constexpr (std::is_same_v<Field, math::BabyBear>) {\n    if (base::Contains(config.vendors(), Vendor::Plonky3())) {\n      using Params =\n          Poseidon2Params<Poseidon2Vendor::kHorizen, Poseidon2Vendor::kPlonky3,\n                          math::BabyBear, 15, 7>;\n      result = runner.template Run<Params>();\n    } else {\n      using Params =\n          Poseidon2Params<Poseidon2Vendor::kHorizen, Poseidon2Vendor::kHorizen,\n                          math::BabyBear, 15, 7>;\n      result = runner.template Run<Params>();\n    }\n  } else {\n    using Params =\n        Poseidon2Params<Poseidon2Vendor::kHorizen, Poseidon2Vendor::kHorizen,\n                        math::bn254::Fr, 2, 5>;\n    result = runner.template Run<Params>();\n  }\n  for (const Vendor vendor : config.vendors()) {\n    Field result_vendor;\n    if (vendor.value() == Vendor::kHorizen) {\n      result_vendor = runner.RunExternal(vendor, horizen_fn);\n    } else if (vendor.value() == Vendor::kPlonky3) {\n      result_vendor = runner.RunExternal(vendor, plonky3_fn);\n    } else {\n      NOTREACHED();\n    }\n\n    if (config.check_results()) {\n      if constexpr (Field::Config::kModulusBits < 32) {\n        if (vendor.value() == Vendor::kHorizen) {\n          // NOTE(ashjeong): horizen's montgomery R = tachyon's montgomery R²\n          CHECK_EQ(result, Field::FromMontgomery(result_vendor.ToBigInt()[0]))\n              << \"Tachyon and Horizen results do not match\";\n        } else if (vendor.value() == Vendor::kPlonky3) {\n          CHECK_EQ(result, result_vendor)\n              << \"Tachyon and Plonky3 results do not match\";\n        }\n      } else {\n        CHECK_EQ(result, result_vendor) << \"Results do not match\";\n      }\n    }\n  }\n}\n\nint RealMain(int argc, char** argv) {\n  base::FilePath tmp_file;\n  CHECK(base::GetTempDir(&tmp_file));\n  tmp_file = tmp_file.Append(\"poseidon2_benchmark.perfetto-trace\");\n  base::Profiler profiler({tmp_file});\n\n  profiler.Init();\n  profiler.Start();\n\n  Poseidon2Config config;\n  if (!config.Parse(argc, argv)) {\n    return 1;\n  }\n\n  SimpleReporter reporter;\n  reporter.set_title(\"Poseidon2 Benchmark\");\n  reporter.set_x_label(\"Trial number\");\n  reporter.set_column_labels(\n      base::CreateVector(config.repeating_num(),\n                         [](size_t i) { return base::NumberToString(i); }));\n\n  if (config.prime_field().value() == FieldType::kBabyBear) {\n    Run<math::BabyBear>(reporter, config, run_poseidon2_horizen_baby_bear,\n                        run_poseidon2_plonky3_baby_bear);\n  } else if (config.prime_field().value() == FieldType::kBn254Fr) {\n    Run<math::bn254::Fr>(reporter, config, run_poseidon2_horizen_bn254_fr,\n                         run_poseidon2_plonky3_bn254_fr);\n  } else {\n    NOTREACHED();\n  }\n\n  reporter.AddAverageAsLastColumn();\n  reporter.Show();\n\n  return 0;\n}\n\n}  // namespace tachyon::benchmark\n\nint main(int argc, char** argv) {\n  return tachyon::benchmark::RealMain(argc, argv);\n}\n"
  },
  {
    "path": "benchmark/poseidon2/poseidon2_benchmark_runner.h",
    "content": "#ifndef BENCHMARK_POSEIDON2_POSEIDON2_BENCHMARK_RUNNER_H_\n#define BENCHMARK_POSEIDON2_POSEIDON2_BENCHMARK_RUNNER_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <memory>\n#include <utility>\n\n// clang-format off\n#include \"benchmark/simple_reporter.h\"\n#include \"benchmark/poseidon2/poseidon2_config.h\"\n// clang-format on\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/time/time.h\"\n#include \"tachyon/c/base/type_traits_forward.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2.h\"\n\nnamespace tachyon::benchmark {\n\ntemplate <typename Field>\nclass Poseidon2BenchmarkRunner {\n public:\n  using CPrimeField = typename c::base::TypeTraits<Field>::CType;\n\n  typedef CPrimeField* (*PoseidonExternalFn)(uint64_t* duration);\n\n  Poseidon2BenchmarkRunner(SimpleReporter& reporter,\n                           const Poseidon2Config& config)\n      : reporter_(reporter), config_(config) {}\n\n  template <typename Params>\n  Field Run() {\n    reporter_.AddVendor(Vendor::Tachyon());\n    Field ret = Field::Zero();\n    for (size_t i = 0; i < config_.repeating_num(); ++i) {\n      crypto::Poseidon2Sponge<Params> sponge;\n      crypto::SpongeState<Params> state;\n      base::TimeTicks start = base::TimeTicks::Now();\n      for (size_t j = 0; j < 10000; ++j) {\n        sponge.Permute(state);\n      }\n      reporter_.AddTime(Vendor::Tachyon(), base::TimeTicks::Now() - start);\n      if (i == 0) {\n        ret = state.elements[1];\n      }\n    }\n    return ret;\n  }\n\n  Field RunExternal(Vendor vendor, PoseidonExternalFn fn) {\n    reporter_.AddVendor(vendor);\n    std::unique_ptr<CPrimeField> ret;\n    for (size_t i = 0; i < config_.repeating_num(); ++i) {\n      uint64_t duration_in_us;\n      ret.reset(fn(&duration_in_us));\n      reporter_.AddTime(vendor, base::Microseconds(duration_in_us));\n    }\n    return *c::base::native_cast(ret.get());\n  }\n\n private:\n  SimpleReporter& reporter_;\n  const Poseidon2Config& config_;\n};\n\n}  // namespace tachyon::benchmark\n\n#endif  // BENCHMARK_POSEIDON2_POSEIDON2_BENCHMARK_RUNNER_H_\n"
  },
  {
    "path": "benchmark/poseidon2/poseidon2_config.cc",
    "content": "#include \"benchmark/poseidon2/poseidon2_config.h\"\n\n#include <set>\n\n#include \"tachyon/base/console/iostream.h\"\n\nnamespace tachyon::benchmark {\n\nPoseidon2Config::Poseidon2Config() {\n  parser_.AddFlag<base::Flag<size_t>>(&repeating_num_)\n      .set_short_name(\"-n\")\n      .set_default_value(10)\n      .set_help(\"Specify the number of repetition 'n'. By default, 10.\");\n  parser_.AddFlag<base::Flag<FieldType>>(&prime_field_)\n      .set_short_name(\"-p\")\n      .set_long_name(\"--prime_field\")\n      .set_required()\n      .set_help(\n          \"A prime field to be benchmarked with. (supported prime fields: \"\n          \"baby_bear, bn254_fr)\");\n  parser_.AddFlag<base::Flag<std::set<Vendor>>>(&vendors_)\n      .set_long_name(\"--vendor\")\n      .set_help(\n          \"Vendors to be benchmarked with. (supported vendors: horizen, \"\n          \"plonky3)\");\n}\n\nbool Poseidon2Config::Validate() const {\n  for (const Vendor vendor : vendors_) {\n    if ((vendor.value() != Vendor::kHorizen) &&\n        (vendor.value() != Vendor::kPlonky3)) {\n      tachyon_cerr << \"Unsupported vendor \" << vendor.ToString() << std::endl;\n      return false;\n    }\n  }\n  if (prime_field_.value() == FieldType::kBabyBear && vendors_.size() != 1) {\n    tachyon_cerr << \"Please run one vendor at a time for Baby Bear!\"\n                 << std::endl;\n    return false;\n  }\n  if ((prime_field_.value() != FieldType::kBabyBear) &&\n      (prime_field_.value() != FieldType::kBn254Fr)) {\n    tachyon_cerr << \"Unsupported prime field \" << prime_field_.ToString()\n                 << std::endl;\n    return false;\n  }\n  return true;\n}\n\n}  // namespace tachyon::benchmark\n"
  },
  {
    "path": "benchmark/poseidon2/poseidon2_config.h",
    "content": "#ifndef BENCHMARK_POSEIDON2_POSEIDON2_CONFIG_H_\n#define BENCHMARK_POSEIDON2_POSEIDON2_CONFIG_H_\n\n#include <stddef.h>\n\n#include <vector>\n\n// clang-format off\n#include \"benchmark/config.h\"\n#include \"benchmark/field_type.h\"\n// clang-format on\n\nnamespace tachyon::benchmark {\n\nclass Poseidon2Config : public Config {\n public:\n  Poseidon2Config();\n  Poseidon2Config(const Poseidon2Config& other) = delete;\n  Poseidon2Config& operator=(const Poseidon2Config& other) = delete;\n\n  size_t repeating_num() const { return repeating_num_; }\n  FieldType prime_field() const { return prime_field_; }\n\n private:\n  // Config methods\n  bool Validate() const override;\n\n  size_t repeating_num_;\n  FieldType prime_field_;\n};\n\n}  // namespace tachyon::benchmark\n\n#endif  // BENCHMARK_POSEIDON2_POSEIDON2_CONFIG_H_\n"
  },
  {
    "path": "benchmark/simple_reporter.cc",
    "content": "#include \"benchmark/simple_reporter.h\"\n\n#if defined(TACHYON_HAS_MATPLOTLIB)\n#include \"third_party/matplotlibcpp17/include/pyplot.h\"\n\nusing namespace matplotlibcpp17;\n#endif  // defined(TACHYON_HAS_MATPLOTLIB)\n\n#include \"tachyon/base/console/table_writer.h\"\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/strings/string_number_conversions.h\"\n\nnamespace tachyon::benchmark {\n\nvoid SimpleReporter::AddTime(Vendor vendor, base::TimeDelta time_taken) {\n  measurements_[vendor].push_back(time_taken);\n}\n\nvoid SimpleReporter::AddVendor(Vendor vendor) { vendors_.push_back(vendor); }\n\nvoid SimpleReporter::AddAverageAsLastColumn() {\n  for (const Vendor vendor : vendors_) {\n    base::TimeDelta total =\n        std::accumulate(measurements_[vendor].begin(),\n                        measurements_[vendor].end(), base::TimeDelta());\n    AddTime(vendor, total / measurements_[vendor].size());\n  }\n  column_labels_.push_back(\"avg\");\n}\n\nvoid SimpleReporter::Show() {\n  base::TableWriterBuilder builder;\n  builder.AlignHeaderLeft()\n      .AddSpace(1)\n      .FitToTerminalWidth()\n      .StripTrailingAsciiWhitespace()\n      .AddColumn(\"\");\n  for (const Vendor vendor : vendors_) {\n    builder.AddColumn(vendor.ToString());\n  }\n  base::TableWriter writer = builder.Build();\n\n  for (size_t i = 0; i < column_labels_.size(); ++i) {\n    writer.SetElement(i, 0, column_labels_[i]);\n    for (size_t j = 0; j < vendors_.size(); ++j) {\n      writer.SetElement(\n          i, j + 1,\n          base::NumberToString(measurements_[vendors_[j]][i].InSecondsF()));\n    }\n  }\n  writer.Print(true);\n\n#if defined(TACHYON_HAS_MATPLOTLIB)\n  py::scoped_interpreter guard{};\n  auto plt = pyplot::import();\n\n  const double kBarWidth = 1.0 / (vendors_.size() + 1);\n\n  std::vector<size_t> x_positions =\n      base::CreateRangedVector(size_t{0}, column_labels_.size());\n\n  auto [fig, ax] = plt.subplots(Kwargs(\"layout\"_a = \"constrained\"));\n\n  for (size_t i = 0; i < vendors_.size(); ++i) {\n    double offset = kBarWidth * i;\n    std::vector<double> values =\n        base::Map(measurements_[vendors_[i]],\n                  [](base::TimeDelta delta) { return delta.InSecondsF(); });\n    auto rects = ax.bar(\n        Args(py::reinterpret_borrow<py::tuple>(py::cast(base::Map(\n                 x_positions,\n                 [offset](size_t x_position) { return x_position + offset; }))),\n             py::reinterpret_borrow<py::tuple>(py::cast(values)), kBarWidth),\n        Kwargs(\"label\"_a = vendors_[i].ToString()));\n  }\n\n  ax.set_title(Args(title_));\n  ax.set_xticks(\n      Args(py::reinterpret_borrow<py::tuple>(py::cast(x_positions)),\n           py::reinterpret_borrow<py::tuple>(py::cast(column_labels_))));\n  ax.set_xlabel(Args(x_label_));\n  ax.set_ylabel(Args(y_label_));\n  ax.legend();\n\n  plt.show();\n#endif  // defined(TACHYON_HAS_MATPLOTLIB)\n}\n\n}  // namespace tachyon::benchmark\n"
  },
  {
    "path": "benchmark/simple_reporter.h",
    "content": "#ifndef BENCHMARK_SIMPLE_REPORTER_H_\n#define BENCHMARK_SIMPLE_REPORTER_H_\n\n#include <string>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n// clang-format off\n#include \"benchmark/vendor.h\"\n// clang-format on\n#include \"tachyon/base/time/time.h\"\n\nnamespace tachyon::benchmark {\n\nclass SimpleReporter {\n public:\n  SimpleReporter() = default;\n  SimpleReporter(const SimpleReporter& other) = delete;\n  SimpleReporter& operator=(const SimpleReporter& other) = delete;\n\n  void set_title(std::string_view title) { title_ = std::string(title); }\n\n  void set_x_label(std::string_view x_label) {\n    x_label_ = std::string(x_label);\n  }\n\n  void set_y_label(std::string_view y_label) {\n    y_label_ = std::string(y_label);\n  }\n\n  void set_column_labels(const std::vector<std::string>& column_labels) {\n    column_labels_ = column_labels;\n  }\n\n  void set_column_labels(std::vector<std::string>&& column_labels) {\n    column_labels_ = std::move(column_labels);\n  }\n\n  void AddTime(Vendor vendor, base::TimeDelta time_taken);\n  void AddVendor(Vendor name);\n  void AddAverageAsLastColumn();\n\n  void Show();\n  void SetRepeatingNum(size_t repeating_num);\n\n protected:\n  std::string title_;\n  std::string x_label_;\n  std::string y_label_ = \"Time (s)\";\n\n  std::vector<Vendor> vendors_;\n  std::vector<std::string> column_labels_;\n  std::unordered_map<Vendor, std::vector<base::TimeDelta>> measurements_;\n};\n\n}  // namespace tachyon::benchmark\n\n#endif  // BENCHMARK_SIMPLE_REPORTER_H_\n"
  },
  {
    "path": "benchmark/vendor.h",
    "content": "#ifndef BENCHMARK_VENDOR_H_\n#define BENCHMARK_VENDOR_H_\n\n#include <stdint.h>\n\n#include <string>\n#include <string_view>\n\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/base/flag/flag_value_traits.h\"\n#include \"tachyon/base/logging.h\"\n\nnamespace tachyon {\nnamespace benchmark {\n\nclass Vendor {\n public:\n  enum Value : uint32_t {\n    // clang-format off\n    // Use |kTachyon| when comparing CPU results across vendors.\n    kTachyon     = 1 << 0,\n    // Use |kTachyonCPU| and |kTachyonGPU| when comparing CPU and GPU results.\n    kTachyonCPU  = 1 << 1,\n    kTachyonGPU  = 1 << 2,\n    kArkworks    = 1 << 3,\n    kBellman     = 1 << 4,\n    kScrollHalo2 = 1 << 5,\n    kPseHalo2    = 1 << 6,\n    kHorizen     = 1 << 7,\n    kPlonky3     = 1 << 8,\n    // clang-format on\n  };\n\n  constexpr static Vendor Tachyon() { return Vendor(kTachyon); }\n  constexpr static Vendor TachyonCPU() { return Vendor(kTachyonCPU); }\n  constexpr static Vendor TachyonGPU() { return Vendor(kTachyonGPU); }\n  constexpr static Vendor Arkworks() { return Vendor(kArkworks); }\n  constexpr static Vendor Bellman() { return Vendor(kBellman); }\n  constexpr static Vendor ScrollHalo2() { return Vendor(kScrollHalo2); }\n  constexpr static Vendor PseHalo2() { return Vendor(kPseHalo2); }\n  constexpr static Vendor Horizen() { return Vendor(kHorizen); }\n  constexpr static Vendor Plonky3() { return Vendor(kPlonky3); }\n\n  Vendor() = default;\n\n  constexpr Value value() const { return value_; }\n\n  constexpr bool operator==(Vendor a) const { return value_ == a.value_; }\n  constexpr bool operator!=(Vendor a) const { return value_ != a.value_; }\n  constexpr bool operator<(Vendor a) const { return value_ < a.value_; }\n\n  std::string_view ToString() const {\n    switch (value_) {\n      case Vendor::kTachyon:\n        return \"tachyon\";\n      case Vendor::kTachyonCPU:\n        return \"tachyon_cpu\";\n      case Vendor::kTachyonGPU:\n        return \"tachyon_gpu\";\n      case Vendor::kArkworks:\n        return \"arkworks\";\n      case Vendor::kBellman:\n        return \"bellman\";\n      case Vendor::kScrollHalo2:\n        return \"halo2\";\n      case Vendor::kPseHalo2:\n        return \"pse_halo2\";\n      case Vendor::kHorizen:\n        return \"horizen\";\n      case Vendor::kPlonky3:\n        return \"plonky3\";\n    }\n    NOTREACHED();\n    return \"\";\n  }\n\n private:\n  explicit constexpr Vendor(Value v) : value_(v) {}\n\n  Value value_;\n};\n\n}  // namespace benchmark\n\nnamespace base {\n\ntemplate <>\nclass FlagValueTraits<benchmark::Vendor> {\n public:\n  using Vendor = benchmark::Vendor;\n\n  static bool ParseValue(std::string_view input, Vendor* value,\n                         std::string* reason) {\n    if (input == \"tachyon_cpu\") {\n      *value = Vendor::TachyonCPU();\n    } else if (input == \"tachyon_gpu\") {\n      *value = Vendor::TachyonGPU();\n    } else if (input == \"arkworks\") {\n      *value = Vendor::Arkworks();\n    } else if (input == \"bellman\") {\n      *value = Vendor::Bellman();\n    } else if (input == \"halo2\") {\n      *value = Vendor::ScrollHalo2();\n    } else if (input == \"pse_halo2\") {\n      *value = Vendor::PseHalo2();\n    } else if (input == \"horizen\") {\n      *value = Vendor::Horizen();\n    } else if (input == \"plonky3\") {\n      *value = Vendor::Plonky3();\n    } else {\n      *reason = absl::Substitute(\"Unknown vendor: $0\", input);\n      return false;\n    }\n    return true;\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\ntemplate <>\nstruct std::hash<tachyon::benchmark::Vendor> {\n  std::size_t operator()(const tachyon::benchmark::Vendor& v) const noexcept {\n    return std::hash<uint32_t>{}(v.value());\n  }\n};\n\n#endif  // BENCHMARK_VENDOR_H_\n"
  },
  {
    "path": "docker/Dockerfile.base.jammy",
    "content": "FROM ubuntu:jammy AS builder\nLABEL maintainer=\"The Tachyon Authors <tachyon-discuss@kroma.network>\"\n\nARG PYTHON_VERSION=3.10.12\n\nRUN apt update && \\\n    apt install -y --no-install-recommends \\\n    wget \\\n    ca-certificates && \\\n    rm -rf /var/lib/apt/lists/*\nRUN wget https://github.com/bazelbuild/bazelisk/releases/latest/download/bazelisk-linux-amd64 && \\\n    chmod +x bazelisk-linux-amd64 && \\\n    mv bazelisk-linux-amd64 /usr/local/bin/bazel\n\nENV HOME /root\nRUN echo 'Etc/UTC' > /etc/timezone && \\\n    ln -s /usr/share/zoneinfo/Etc/UTC /etc/localtime && \\\n    apt update && \\\n    apt install -y --no-install-recommends \\\n    build-essential \\\n    curl \\\n    git \\\n    libbz2-dev \\\n    liblzma-dev \\\n    libncursesw5-dev \\\n    libreadline-dev \\\n    libsqlite3-dev \\\n    libssl-dev \\\n    make \\\n    python-is-python3 \\\n    tk-dev \\\n    xz-utils \\\n    zlib1g-dev && \\\n    rm -rf /var/lib/apt/lists/*\nRUN curl https://pyenv.run | bash\nRUN CONFIGURE_OPTS=--enable-shared $HOME/.pyenv/bin/pyenv install ${PYTHON_VERSION}\nRUN mkdir -p $HOME/bin\nENV PATH=\"${HOME}/.pyenv/shims:${HOME}/.pyenv/bin:${HOME}/bin:${PATH}\"\n\nRUN apt update && \\\n    apt install -y --no-install-recommends \\\n    libgmp-dev \\\n    libomp-dev && \\\n    rm -rf /var/lib/apt/lists/*\n"
  },
  {
    "path": "docker/Dockerfile.circom.jammy",
    "content": "FROM zktachyon/base:jammy-latest AS builder\nLABEL maintainer=\"The Tachyon Authors <tachyon-discuss@kroma.network>\"\n\n# Install python dependencies\nRUN apt update && apt install -y --no-install-recommends python3-pip\nRUN python3 -m pip install numpy\n\n# Set the working directory\nWORKDIR /tachyon\n\n# Copy the entire project\nCOPY . .\n\n# Build circom prover\nRUN cd vendors/circom && CARGO_BAZEL_REPIN=true bazel build --@kroma_network_tachyon//:has_openmp --config maxopt //:prover_main\n\n# Build compile tool for witness generator\nRUN cd vendors/circom && bazel build --config opt //circomlib/build:compile_witness_generator\n\n# Use the CUDA base image\nFROM nvidia/cuda:12.5.0-devel-ubuntu22.04 AS builder-cuda\n\n# Set the working directory\nWORKDIR /tachyon\n\n# Copy the entire project\nCOPY . .\n\nCOPY --from=builder /usr /usr\nCOPY --from=builder /root/.pyenv /root/.pyenv\n\nRUN echo 'export PYENV_ROOT=\"/root/.pyenv\"' >> ~/.bash_profile && \\\n    echo '[[ -d $PYENV_ROOT/bin ]] && export PATH=\"$PYENV_ROOT/bin:$PATH\"' >> ~/.bash_profile && \\\n    echo 'eval \"$(pyenv init -)\"' >> ~/.bash_profile\nRUN echo 'export PYENV_ROOT=\"/root/.pyenv\"' >> ~/.bashrc && \\\n    echo 'command -v pyenv >/dev/null || export PATH=\"$PYENV_ROOT/bin:$PATH\"' >> ~/.bashrc && \\\n    echo 'eval \"$(pyenv init -)\"' >> ~/.bashrc\nENV PYENV_ROOT=\"/root/.pyenv\"\nENV PATH=\"${PYENV_ROOT}/shims:${PYENV_ROOT}/bin:${PATH}\"\n\n# Build circom prover with CUDA acceleration\nRUN cd vendors/circom && CARGO_BAZEL_REPIN=true bazel build --@kroma_network_tachyon//:has_openmp --config maxopt --config cuda //:prover_main\n\n# Build compile tool for witness generator\nRUN cd vendors/circom && bazel build --config opt //circomlib/build:compile_witness_generator\n"
  },
  {
    "path": "docker/Dockerfile.halo2.jammy",
    "content": "FROM zktachyon/base:jammy-latest AS builder\nLABEL maintainer=\"The Tachyon Authors <tachyon-discuss@kroma.network>\"\n\nCOPY . /usr/src/tachyon\nWORKDIR /usr/src/tachyon\n\nRUN bazel build --config opt --config linux --//:c_shared_object //scripts/packages/debian/runtime:debian && \\\n    bazel build --config opt --config linux --//:c_shared_object //scripts/packages/debian/dev:debian\n\nFROM ubuntu:jammy AS tachyon-halo2\nLABEL maintainer=\"The Tachyon Authors <tachyon-discuss@kroma.network>\"\n\nCOPY --from=builder /usr/src/tachyon/bazel-bin/scripts/packages/debian/runtime/libtachyon_0.4.0_amd64.deb /usr/src/tachyon/bazel-bin/scripts/packages/debian/runtime/libtachyon_0.4.0_amd64.deb\nCOPY --from=builder /usr/src/tachyon/bazel-bin/scripts/packages/debian/dev/libtachyon-dev_0.4.0_amd64.deb /usr/src/tachyon/bazel-bin/scripts/packages/debian/dev/libtachyon-dev_0.4.0_amd64.deb\n\nRUN apt update && \\\n    apt install -y --no-install-recommends \\\n    libgmp-dev \\\n    libomp-dev && \\\n    rm -rf /var/lib/apt/lists/*\nRUN dpkg -i /usr/src/tachyon/bazel-bin/scripts/packages/debian/runtime/libtachyon_0.4.0_amd64.deb && \\\n    dpkg -i /usr/src/tachyon/bazel-bin/scripts/packages/debian/dev/libtachyon-dev_0.4.0_amd64.deb && \\\n    rm /usr/src/tachyon/bazel-bin/scripts/packages/debian/runtime/libtachyon_0.4.0_amd64.deb && \\\n    rm /usr/src/tachyon/bazel-bin/scripts/packages/debian/dev/libtachyon-dev_0.4.0_amd64.deb\n"
  },
  {
    "path": "docs/doxygen/BUILD.bazel",
    "content": "load(\"//tachyon/c:version.bzl\", \"VERSION\")\n\ngenrule(\n    name = \"doxyfile\",\n    srcs = [\"Doxyfile.in\"],\n    outs = [\"Doxyfile\"],\n    cmd = \"sed \" +\n          \"-e 's|$${TACHYON_VERSION}|\" + VERSION + \"|g' \" +\n          \"-e 's|$${TACHYON_BINARY_DIR}|$(GENDIR)|g' \" +\n          \"-e 's|$${TACHYON_SOURCE_DIR}|tachyon|g' \" +\n          \"$(location Doxyfile.in) > $(location Doxyfile)\",\n    tags = [\"doxygen\"],\n)\n\ngenrule(\n    name = \"generate_docs\",\n    srcs = [\n        \"main_page.md\",\n        \":doxyfile\",\n        \"//tachyon/c:tachyon_hdrs\",\n        \"//tachyon/c/examples\",\n    ],\n    outs = [\"tachyon_api_docs.zip\"],\n    cmd = \"\"\"\n    $(location @doxygen_archive//:doxygen_bin) $(locations //docs/doxygen:doxyfile) && \\\n    zip -r $(@) $(GENDIR)/docs/doxygen/html\n    \"\"\",\n    tags = [\"doxygen\"],\n    tools = [\"@doxygen_archive//:doxygen_bin\"],\n)\n"
  },
  {
    "path": "docs/doxygen/Doxyfile.in",
    "content": "# Doxyfile 1.10.0\n\n# This file describes the settings to be used by the documentation system\n# doxygen (www.doxygen.org) for a project.\n#\n# All text after a double hash (##) is considered a comment and is placed in\n# front of the TAG it is preceding.\n#\n# All text after a single hash (#) is considered a comment and will be ignored.\n# The format is:\n# TAG = value [value, ...]\n# For lists, items can also be appended using:\n# TAG += value [value, ...]\n# Values that contain spaces should be placed between quotes (\\\" \\\").\n#\n# Note:\n#\n# Use doxygen to compare the used configuration file with the template\n# configuration file:\n# doxygen -x [configFile]\n# Use doxygen to compare the used configuration file with the template\n# configuration file without replacing the environment variables or CMake type\n# replacement variables:\n# doxygen -x_noenv [configFile]\n\n#---------------------------------------------------------------------------\n# Project related configuration options\n#---------------------------------------------------------------------------\n\n# This tag specifies the encoding used for all characters in the configuration\n# file that follow. The default is UTF-8 which is also the encoding used for all\n# text before the first occurrence of this tag. Doxygen uses libiconv (or the\n# iconv built into libc) for the transcoding. See\n# https://www.gnu.org/software/libiconv/ for the list of possible encodings.\n# The default value is: UTF-8.\n\nDOXYFILE_ENCODING      = UTF-8\n\n# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by\n# double-quotes, unless you are using Doxywizard) that should identify the\n# project for which the documentation is generated. This name is used in the\n# title of most generated pages and in a few other places.\n# The default value is: My Project.\n\nPROJECT_NAME           = \"Tachyon\"\n\n# The PROJECT_NUMBER tag can be used to enter a project or revision number. This\n# could be handy for archiving the generated documentation or if some version\n# control system is used.\n\nPROJECT_NUMBER         = ${TACHYON_VERSION}\n\n# Using the PROJECT_BRIEF tag one can provide an optional one line description\n# for a project that appears at the top of each page and should give viewer a\n# quick idea about the purpose of the project. Keep the description short.\n\nPROJECT_BRIEF          =\n\n# With the PROJECT_LOGO tag one can specify a logo or an icon that is included\n# in the documentation. The maximum height of the logo should not exceed 55\n# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy\n# the logo to the output directory.\n\nPROJECT_LOGO           =\n\n# With the PROJECT_ICON tag one can specify an icon that is included in the tabs\n# when the HTML document is shown. Doxygen will copy the logo to the output\n# directory.\n\nPROJECT_ICON           =\n\n# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path\n# into which the generated documentation will be written. If a relative path is\n# entered, it will be relative to the location where doxygen was started. If\n# left blank the current directory will be used.\n\nOUTPUT_DIRECTORY       = ${TACHYON_BINARY_DIR}/docs/doxygen\n\n# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096\n# sub-directories (in 2 levels) under the output directory of each output format\n# and will distribute the generated files over these directories. Enabling this\n# option can be useful when feeding doxygen a huge amount of source files, where\n# putting all generated files in the same directory would otherwise cause\n# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to\n# control the number of sub-directories.\n# The default value is: NO.\n\nCREATE_SUBDIRS         = NO\n\n# Controls the number of sub-directories that will be created when\n# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every\n# level increment doubles the number of directories, resulting in 4096\n# directories at level 8 which is the default and also the maximum value. The\n# sub-directories are organized in 2 levels, the first level always has a fixed\n# number of 16 directories.\n# Minimum value: 0, maximum value: 8, default value: 8.\n# This tag requires that the tag CREATE_SUBDIRS is set to YES.\n\nCREATE_SUBDIRS_LEVEL   = 8\n\n# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII\n# characters to appear in the names of generated files. If set to NO, non-ASCII\n# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode\n# U+3044.\n# The default value is: NO.\n\nALLOW_UNICODE_NAMES    = NO\n\n# The OUTPUT_LANGUAGE tag is used to specify the language in which all\n# documentation generated by doxygen is written. Doxygen will use this\n# information to generate all constant output in the proper language.\n# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian,\n# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English\n# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek,\n# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with\n# English messages), Korean, Korean-en (Korean with English messages), Latvian,\n# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese,\n# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish,\n# Swedish, Turkish, Ukrainian and Vietnamese.\n# The default value is: English.\n\nOUTPUT_LANGUAGE        = English\n\n# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member\n# descriptions after the members that are listed in the file and class\n# documentation (similar to Javadoc). Set to NO to disable this.\n# The default value is: YES.\n\nBRIEF_MEMBER_DESC      = YES\n\n# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief\n# description of a member or function before the detailed description\n#\n# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the\n# brief descriptions will be completely suppressed.\n# The default value is: YES.\n\nREPEAT_BRIEF           = YES\n\n# This tag implements a quasi-intelligent brief description abbreviator that is\n# used to form the text in various listings. Each string in this list, if found\n# as the leading text of the brief description, will be stripped from the text\n# and the result, after processing the whole list, is used as the annotated\n# text. Otherwise, the brief description is used as-is. If left blank, the\n# following values are used ($name is automatically replaced with the name of\n# the entity):The $name class, The $name widget, The $name file, is, provides,\n# specifies, contains, represents, a, an and the.\n\nABBREVIATE_BRIEF       = \"The $name class\" \\\n                         \"The $name widget\" \\\n                         \"The $name file\" \\\n                         is \\\n                         provides \\\n                         specifies \\\n                         contains \\\n                         represents \\\n                         a \\\n                         an \\\n                         the\n\n# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then\n# doxygen will generate a detailed section even if there is only a brief\n# description.\n# The default value is: NO.\n\nALWAYS_DETAILED_SEC    = NO\n\n# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all\n# inherited members of a class in the documentation of that class as if those\n# members were ordinary class members. Constructors, destructors and assignment\n# operators of the base classes will not be shown.\n# The default value is: NO.\n\nINLINE_INHERITED_MEMB  = NO\n\n# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path\n# before files name in the file list and in the header files. If set to NO the\n# shortest path that makes the file name unique will be used\n# The default value is: YES.\n\nFULL_PATH_NAMES        = YES\n\n# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.\n# Stripping is only done if one of the specified strings matches the left-hand\n# part of the path. The tag can be used to show relative paths in the file list.\n# If left blank the directory from which doxygen is run is used as the path to\n# strip.\n#\n# Note that you can specify absolute paths here, but also relative paths, which\n# will be relative from the directory where doxygen is started.\n# This tag requires that the tag FULL_PATH_NAMES is set to YES.\n\nSTRIP_FROM_PATH        =\n\n# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the\n# path mentioned in the documentation of a class, which tells the reader which\n# header file to include in order to use a class. If left blank only the name of\n# the header file containing the class definition is used. Otherwise one should\n# specify the list of included paths that are normally passed to the compiler\n# using the -I flag.\n\nSTRIP_FROM_INC_PATH    =\n\n# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but\n# less readable) file names. This can be useful if your file systems doesn't\n# support long names like on DOS, Mac, or CD-ROM.\n# The default value is: NO.\n\nSHORT_NAMES            = NO\n\n# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the\n# first line (until the first dot) of a Javadoc-style comment as the brief\n# description. If set to NO, the Javadoc-style will behave just like regular Qt-\n# style comments (thus requiring an explicit @brief command for a brief\n# description.)\n# The default value is: NO.\n\nJAVADOC_AUTOBRIEF      = NO\n\n# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line\n# such as\n# /***************\n# as being the beginning of a Javadoc-style comment \"banner\". If set to NO, the\n# Javadoc-style will behave just like regular comments and it will not be\n# interpreted by doxygen.\n# The default value is: NO.\n\nJAVADOC_BANNER         = NO\n\n# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first\n# line (until the first dot) of a Qt-style comment as the brief description. If\n# set to NO, the Qt-style will behave just like regular Qt-style comments (thus\n# requiring an explicit \\brief command for a brief description.)\n# The default value is: NO.\n\nQT_AUTOBRIEF           = NO\n\n# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a\n# multi-line C++ special comment block (i.e. a block of //! or /// comments) as\n# a brief description. This used to be the default behavior. The new default is\n# to treat a multi-line C++ comment block as a detailed description. Set this\n# tag to YES if you prefer the old behavior instead.\n#\n# Note that setting this tag to YES also means that rational rose comments are\n# not recognized anymore.\n# The default value is: NO.\n\nMULTILINE_CPP_IS_BRIEF = NO\n\n# By default Python docstrings are displayed as preformatted text and doxygen's\n# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the\n# doxygen's special commands can be used and the contents of the docstring\n# documentation blocks is shown as doxygen documentation.\n# The default value is: YES.\n\nPYTHON_DOCSTRING       = YES\n\n# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the\n# documentation from any documented member that it re-implements.\n# The default value is: YES.\n\nINHERIT_DOCS           = YES\n\n# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new\n# page for each member. If set to NO, the documentation of a member will be part\n# of the file/class/namespace that contains it.\n# The default value is: NO.\n\nSEPARATE_MEMBER_PAGES  = NO\n\n# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen\n# uses this value to replace tabs by spaces in code fragments.\n# Minimum value: 1, maximum value: 16, default value: 4.\n\nTAB_SIZE               = 4\n\n# This tag can be used to specify a number of aliases that act as commands in\n# the documentation. An alias has the form:\n# name=value\n# For example adding\n# \"sideeffect=@par Side Effects:^^\"\n# will allow you to put the command \\sideeffect (or @sideeffect) in the\n# documentation, which will result in a user-defined paragraph with heading\n# \"Side Effects:\". Note that you cannot put \\n's in the value part of an alias\n# to insert newlines (in the resulting output). You can put ^^ in the value part\n# of an alias to insert a newline as if a physical newline was in the original\n# file. When you need a literal { or } or , in the value part of an alias you\n# have to escape them by means of a backslash (\\), this can lead to conflicts\n# with the commands \\{ and \\} for these it is advised to use the version @{ and\n# @} or use a double escape (\\\\{ and \\\\})\n\nALIASES                =\n\n# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources\n# only. Doxygen will then generate output that is more tailored for C. For\n# instance, some of the names that are used will be different. The list of all\n# members will be omitted, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_FOR_C  = NO\n\n# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or\n# Python sources only. Doxygen will then generate output that is more tailored\n# for that language. For instance, namespaces will be presented as packages,\n# qualified scopes will look different, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_JAVA   = NO\n\n# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran\n# sources. Doxygen will then generate output that is tailored for Fortran.\n# The default value is: NO.\n\nOPTIMIZE_FOR_FORTRAN   = NO\n\n# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL\n# sources. Doxygen will then generate output that is tailored for VHDL.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_VHDL   = NO\n\n# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice\n# sources only. Doxygen will then generate output that is more tailored for that\n# language. For instance, namespaces will be presented as modules, types will be\n# separated into more groups, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_SLICE  = NO\n\n# Doxygen selects the parser to use depending on the extension of the files it\n# parses. With this tag you can assign which parser to use for a given\n# extension. Doxygen has a built-in mapping, but you can override or extend it\n# using this tag. The format is ext=language, where ext is a file extension, and\n# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,\n# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice,\n# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:\n# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser\n# tries to guess whether the code is fixed or free formatted code, this is the\n# default for Fortran type files). For instance to make doxygen treat .inc files\n# as Fortran files (default is PHP), and .f files as C (default is Fortran),\n# use: inc=Fortran f=C.\n#\n# Note: For files without extension you can use no_extension as a placeholder.\n#\n# Note that for custom extensions you also need to set FILE_PATTERNS otherwise\n# the files are not read by doxygen. When specifying no_extension you should add\n# * to the FILE_PATTERNS.\n#\n# Note see also the list of default file extension mappings.\n\nEXTENSION_MAPPING      =\n\n# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments\n# according to the Markdown format, which allows for more readable\n# documentation. See https://daringfireball.net/projects/markdown/ for details.\n# The output of markdown processing is further processed by doxygen, so you can\n# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in\n# case of backward compatibilities issues.\n# The default value is: YES.\n\nMARKDOWN_SUPPORT       = YES\n\n# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up\n# to that level are automatically included in the table of contents, even if\n# they do not have an id attribute.\n# Note: This feature currently applies only to Markdown headings.\n# Minimum value: 0, maximum value: 99, default value: 5.\n# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.\n\nTOC_INCLUDE_HEADINGS   = 5\n\n# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to\n# generate identifiers for the Markdown headings. Note: Every identifier is\n# unique.\n# Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a\n# sequence number starting at 0 and GITHUB use the lower case version of title\n# with any whitespace replaced by '-' and punctuation characters removed.\n# The default value is: DOXYGEN.\n# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.\n\nMARKDOWN_ID_STYLE      = DOXYGEN\n\n# When enabled doxygen tries to link words that correspond to documented\n# classes, or namespaces to their corresponding documentation. Such a link can\n# be prevented in individual cases by putting a % sign in front of the word or\n# globally by setting AUTOLINK_SUPPORT to NO.\n# The default value is: YES.\n\nAUTOLINK_SUPPORT       = YES\n\n# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want\n# to include (a tag file for) the STL sources as input, then you should set this\n# tag to YES in order to let doxygen match functions declarations and\n# definitions whose arguments contain STL classes (e.g. func(std::string);\n# versus func(std::string) {}). This also make the inheritance and collaboration\n# diagrams that involve STL classes more complete and accurate.\n# The default value is: NO.\n\nBUILTIN_STL_SUPPORT    = NO\n\n# If you use Microsoft's C++/CLI language, you should set this option to YES to\n# enable parsing support.\n# The default value is: NO.\n\nCPP_CLI_SUPPORT        = NO\n\n# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:\n# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen\n# will parse them like normal C++ but will assume all classes use public instead\n# of private inheritance when no explicit protection keyword is present.\n# The default value is: NO.\n\nSIP_SUPPORT            = NO\n\n# For Microsoft's IDL there are propget and propput attributes to indicate\n# getter and setter methods for a property. Setting this option to YES will make\n# doxygen to replace the get and set methods by a property in the documentation.\n# This will only work if the methods are indeed getting or setting a simple\n# type. If this is not the case, or you want to show the methods anyway, you\n# should set this option to NO.\n# The default value is: YES.\n\nIDL_PROPERTY_SUPPORT   = YES\n\n# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC\n# tag is set to YES then doxygen will reuse the documentation of the first\n# member in the group (if any) for the other members of the group. By default\n# all members of a group must be documented explicitly.\n# The default value is: NO.\n\nDISTRIBUTE_GROUP_DOC   = NO\n\n# If one adds a struct or class to a group and this option is enabled, then also\n# any nested class or struct is added to the same group. By default this option\n# is disabled and one has to add nested compounds explicitly via \\ingroup.\n# The default value is: NO.\n\nGROUP_NESTED_COMPOUNDS = NO\n\n# Set the SUBGROUPING tag to YES to allow class member groups of the same type\n# (for instance a group of public functions) to be put as a subgroup of that\n# type (e.g. under the Public Functions section). Set it to NO to prevent\n# subgrouping. Alternatively, this can be done per class using the\n# \\nosubgrouping command.\n# The default value is: YES.\n\nSUBGROUPING            = YES\n\n# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions\n# are shown inside the group in which they are included (e.g. using \\ingroup)\n# instead of on a separate page (for HTML and Man pages) or section (for LaTeX\n# and RTF).\n#\n# Note that this feature does not work in combination with\n# SEPARATE_MEMBER_PAGES.\n# The default value is: NO.\n\nINLINE_GROUPED_CLASSES = NO\n\n# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions\n# with only public data fields or simple typedef fields will be shown inline in\n# the documentation of the scope in which they are defined (i.e. file,\n# namespace, or group documentation), provided this scope is documented. If set\n# to NO, structs, classes, and unions are shown on a separate page (for HTML and\n# Man pages) or section (for LaTeX and RTF).\n# The default value is: NO.\n\nINLINE_SIMPLE_STRUCTS  = NO\n\n# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or\n# enum is documented as struct, union, or enum with the name of the typedef. So\n# typedef struct TypeS {} TypeT, will appear in the documentation as a struct\n# with name TypeT. When disabled the typedef will appear as a member of a file,\n# namespace, or class. And the struct will be named TypeS. This can typically be\n# useful for C code in case the coding convention dictates that all compound\n# types are typedef'ed and only the typedef is referenced, never the tag name.\n# The default value is: NO.\n\nTYPEDEF_HIDES_STRUCT   = NO\n\n# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This\n# cache is used to resolve symbols given their name and scope. Since this can be\n# an expensive process and often the same symbol appears multiple times in the\n# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small\n# doxygen will become slower. If the cache is too large, memory is wasted. The\n# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range\n# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536\n# symbols. At the end of a run doxygen will report the cache usage and suggest\n# the optimal cache size from a speed point of view.\n# Minimum value: 0, maximum value: 9, default value: 0.\n\nLOOKUP_CACHE_SIZE      = 0\n\n# The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use\n# during processing. When set to 0 doxygen will based this on the number of\n# cores available in the system. You can set it explicitly to a value larger\n# than 0 to get more control over the balance between CPU load and processing\n# speed. At this moment only the input processing can be done using multiple\n# threads. Since this is still an experimental feature the default is set to 1,\n# which effectively disables parallel processing. Please report any issues you\n# encounter. Generating dot graphs in parallel is controlled by the\n# DOT_NUM_THREADS setting.\n# Minimum value: 0, maximum value: 32, default value: 1.\n\nNUM_PROC_THREADS       = 1\n\n# If the TIMESTAMP tag is set different from NO then each generated page will\n# contain the date or date and time when the page was generated. Setting this to\n# NO can help when comparing the output of multiple runs.\n# Possible values are: YES, NO, DATETIME and DATE.\n# The default value is: NO.\n\nTIMESTAMP              = NO\n\n#---------------------------------------------------------------------------\n# Build related configuration options\n#---------------------------------------------------------------------------\n\n# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in\n# documentation are documented, even if no documentation was available. Private\n# class members and static file members will be hidden unless the\n# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.\n# Note: This will also disable the warnings about undocumented members that are\n# normally produced when WARNINGS is set to YES.\n# The default value is: NO.\n\nEXTRACT_ALL            = NO\n\n# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will\n# be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PRIVATE        = NO\n\n# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual\n# methods of a class will be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PRIV_VIRTUAL   = NO\n\n# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal\n# scope will be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PACKAGE        = NO\n\n# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be\n# included in the documentation.\n# The default value is: NO.\n\nEXTRACT_STATIC         = NO\n\n# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined\n# locally in source files will be included in the documentation. If set to NO,\n# only classes defined in header files are included. Does not have any effect\n# for Java sources.\n# The default value is: YES.\n\nEXTRACT_LOCAL_CLASSES  = YES\n\n# This flag is only useful for Objective-C code. If set to YES, local methods,\n# which are defined in the implementation section but not in the interface are\n# included in the documentation. If set to NO, only methods in the interface are\n# included.\n# The default value is: NO.\n\nEXTRACT_LOCAL_METHODS  = NO\n\n# If this flag is set to YES, the members of anonymous namespaces will be\n# extracted and appear in the documentation as a namespace called\n# 'anonymous_namespace{file}', where file will be replaced with the base name of\n# the file that contains the anonymous namespace. By default anonymous namespace\n# are hidden.\n# The default value is: NO.\n\nEXTRACT_ANON_NSPACES   = NO\n\n# If this flag is set to YES, the name of an unnamed parameter in a declaration\n# will be determined by the corresponding definition. By default unnamed\n# parameters remain unnamed in the output.\n# The default value is: YES.\n\nRESOLVE_UNNAMED_PARAMS = YES\n\n# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all\n# undocumented members inside documented classes or files. If set to NO these\n# members will be included in the various overviews, but no documentation\n# section is generated. This option has no effect if EXTRACT_ALL is enabled.\n# The default value is: NO.\n\nHIDE_UNDOC_MEMBERS     = YES\n\n# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all\n# undocumented classes that are normally visible in the class hierarchy. If set\n# to NO, these classes will be included in the various overviews. This option\n# will also hide undocumented C++ concepts if enabled. This option has no effect\n# if EXTRACT_ALL is enabled.\n# The default value is: NO.\n\nHIDE_UNDOC_CLASSES     = YES\n\n# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend\n# declarations. If set to NO, these declarations will be included in the\n# documentation.\n# The default value is: NO.\n\nHIDE_FRIEND_COMPOUNDS  = YES\n\n# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any\n# documentation blocks found inside the body of a function. If set to NO, these\n# blocks will be appended to the function's detailed documentation block.\n# The default value is: NO.\n\nHIDE_IN_BODY_DOCS      = NO\n\n# The INTERNAL_DOCS tag determines if documentation that is typed after a\n# \\internal command is included. If the tag is set to NO then the documentation\n# will be excluded. Set it to YES to include the internal documentation.\n# The default value is: NO.\n\nINTERNAL_DOCS          = NO\n\n# With the correct setting of option CASE_SENSE_NAMES doxygen will better be\n# able to match the capabilities of the underlying filesystem. In case the\n# filesystem is case sensitive (i.e. it supports files in the same directory\n# whose names only differ in casing), the option must be set to YES to properly\n# deal with such files in case they appear in the input. For filesystems that\n# are not case sensitive the option should be set to NO to properly deal with\n# output files written for symbols that only differ in casing, such as for two\n# classes, one named CLASS and the other named Class, and to also support\n# references to files without having to specify the exact matching casing. On\n# Windows (including Cygwin) and MacOS, users should typically set this option\n# to NO, whereas on Linux or other Unix flavors it should typically be set to\n# YES.\n# Possible values are: SYSTEM, NO and YES.\n# The default value is: SYSTEM.\n\nCASE_SENSE_NAMES       = SYSTEM\n\n# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with\n# their full class and namespace scopes in the documentation. If set to YES, the\n# scope will be hidden.\n# The default value is: NO.\n\nHIDE_SCOPE_NAMES       = NO\n\n# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will\n# append additional text to a page's title, such as Class Reference. If set to\n# YES the compound reference will be hidden.\n# The default value is: NO.\n\nHIDE_COMPOUND_REFERENCE= NO\n\n# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class\n# will show which file needs to be included to use the class.\n# The default value is: YES.\n\nSHOW_HEADERFILE        = YES\n\n# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of\n# the files that are included by a file in the documentation of that file.\n# The default value is: YES.\n\nSHOW_INCLUDE_FILES     = YES\n\n# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each\n# grouped member an include statement to the documentation, telling the reader\n# which file to include in order to use the member.\n# The default value is: NO.\n\nSHOW_GROUPED_MEMB_INC  = NO\n\n# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include\n# files with double quotes in the documentation rather than with sharp brackets.\n# The default value is: NO.\n\nFORCE_LOCAL_INCLUDES   = NO\n\n# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the\n# documentation for inline members.\n# The default value is: YES.\n\nINLINE_INFO            = YES\n\n# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the\n# (detailed) documentation of file and class members alphabetically by member\n# name. If set to NO, the members will appear in declaration order.\n# The default value is: YES.\n\nSORT_MEMBER_DOCS       = YES\n\n# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief\n# descriptions of file, namespace and class members alphabetically by member\n# name. If set to NO, the members will appear in declaration order. Note that\n# this will also influence the order of the classes in the class list.\n# The default value is: NO.\n\nSORT_BRIEF_DOCS        = NO\n\n# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the\n# (brief and detailed) documentation of class members so that constructors and\n# destructors are listed first. If set to NO the constructors will appear in the\n# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.\n# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief\n# member documentation.\n# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting\n# detailed member documentation.\n# The default value is: NO.\n\nSORT_MEMBERS_CTORS_1ST = NO\n\n# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy\n# of group names into alphabetical order. If set to NO the group names will\n# appear in their defined order.\n# The default value is: NO.\n\nSORT_GROUP_NAMES       = NO\n\n# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by\n# fully-qualified names, including namespaces. If set to NO, the class list will\n# be sorted only by class name, not including the namespace part.\n# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.\n# Note: This option applies only to the class list, not to the alphabetical\n# list.\n# The default value is: NO.\n\nSORT_BY_SCOPE_NAME     = NO\n\n# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper\n# type resolution of all parameters of a function it will reject a match between\n# the prototype and the implementation of a member function even if there is\n# only one candidate or it is obvious which candidate to choose by doing a\n# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still\n# accept a match between prototype and implementation in such cases.\n# The default value is: NO.\n\nSTRICT_PROTO_MATCHING  = NO\n\n# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo\n# list. This list is created by putting \\todo commands in the documentation.\n# The default value is: YES.\n\nGENERATE_TODOLIST      = YES\n\n# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test\n# list. This list is created by putting \\test commands in the documentation.\n# The default value is: YES.\n\nGENERATE_TESTLIST      = YES\n\n# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug\n# list. This list is created by putting \\bug commands in the documentation.\n# The default value is: YES.\n\nGENERATE_BUGLIST       = YES\n\n# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)\n# the deprecated list. This list is created by putting \\deprecated commands in\n# the documentation.\n# The default value is: YES.\n\nGENERATE_DEPRECATEDLIST= YES\n\n# The ENABLED_SECTIONS tag can be used to enable conditional documentation\n# sections, marked by \\if <section_label> ... \\endif and \\cond <section_label>\n# ... \\endcond blocks.\n\nENABLED_SECTIONS       =\n\n# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the\n# initial value of a variable or macro / define can have for it to appear in the\n# documentation. If the initializer consists of more lines than specified here\n# it will be hidden. Use a value of 0 to hide initializers completely. The\n# appearance of the value of individual variables and macros / defines can be\n# controlled using \\showinitializer or \\hideinitializer command in the\n# documentation regardless of this setting.\n# Minimum value: 0, maximum value: 10000, default value: 30.\n\nMAX_INITIALIZER_LINES  = 30\n\n# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at\n# the bottom of the documentation of classes and structs. If set to YES, the\n# list will mention the files that were used to generate the documentation.\n# The default value is: YES.\n\nSHOW_USED_FILES        = YES\n\n# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This\n# will remove the Files entry from the Quick Index and from the Folder Tree View\n# (if specified).\n# The default value is: YES.\n\nSHOW_FILES             = YES\n\n# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces\n# page. This will remove the Namespaces entry from the Quick Index and from the\n# Folder Tree View (if specified).\n# The default value is: YES.\n\nSHOW_NAMESPACES        = YES\n\n# The FILE_VERSION_FILTER tag can be used to specify a program or script that\n# doxygen should invoke to get the current version for each file (typically from\n# the version control system). Doxygen will invoke the program by executing (via\n# popen()) the command command input-file, where command is the value of the\n# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided\n# by doxygen. Whatever the program writes to standard output is used as the file\n# version. For an example see the documentation.\n\nFILE_VERSION_FILTER    =\n\n# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed\n# by doxygen. The layout file controls the global structure of the generated\n# output files in an output format independent way. To create the layout file\n# that represents doxygen's defaults, run doxygen with the -l option. You can\n# optionally specify a file name after the option, if omitted DoxygenLayout.xml\n# will be used as the name of the layout file. See also section \"Changing the\n# layout of pages\" for information.\n#\n# Note that if you run doxygen from a directory containing a file called\n# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE\n# tag is left empty.\n\nLAYOUT_FILE            =\n\n# The CITE_BIB_FILES tag can be used to specify one or more bib files containing\n# the reference definitions. This must be a list of .bib files. The .bib\n# extension is automatically appended if omitted. This requires the bibtex tool\n# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.\n# For LaTeX the style of the bibliography can be controlled using\n# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the\n# search path. See also \\cite for info how to create references.\n\nCITE_BIB_FILES         =\n\n#---------------------------------------------------------------------------\n# Configuration options related to warning and progress messages\n#---------------------------------------------------------------------------\n\n# The QUIET tag can be used to turn on/off the messages that are generated to\n# standard output by doxygen. If QUIET is set to YES this implies that the\n# messages are off.\n# The default value is: NO.\n\nQUIET                  = NO\n\n# The WARNINGS tag can be used to turn on/off the warning messages that are\n# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES\n# this implies that the warnings are on.\n#\n# Tip: Turn warnings on while writing the documentation.\n# The default value is: YES.\n\nWARNINGS               = YES\n\n# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate\n# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag\n# will automatically be disabled.\n# The default value is: YES.\n\nWARN_IF_UNDOCUMENTED   = YES\n\n# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for\n# potential errors in the documentation, such as documenting some parameters in\n# a documented function twice, or documenting parameters that don't exist or\n# using markup commands wrongly.\n# The default value is: YES.\n\nWARN_IF_DOC_ERROR      = YES\n\n# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete\n# function parameter documentation. If set to NO, doxygen will accept that some\n# parameters have no documentation without warning.\n# The default value is: YES.\n\nWARN_IF_INCOMPLETE_DOC = YES\n\n# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that\n# are documented, but have no documentation for their parameters or return\n# value. If set to NO, doxygen will only warn about wrong parameter\n# documentation, but not about the absence of documentation. If EXTRACT_ALL is\n# set to YES then this flag will automatically be disabled. See also\n# WARN_IF_INCOMPLETE_DOC\n# The default value is: NO.\n\nWARN_NO_PARAMDOC       = NO\n\n# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about\n# undocumented enumeration values. If set to NO, doxygen will accept\n# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag\n# will automatically be disabled.\n# The default value is: NO.\n\nWARN_IF_UNDOC_ENUM_VAL = NO\n\n# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when\n# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS\n# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but\n# at the end of the doxygen process doxygen will return with a non-zero status.\n# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then doxygen behaves\n# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined doxygen will not\n# write the warning messages in between other messages but write them at the end\n# of a run, in case a WARN_LOGFILE is defined the warning messages will be\n# besides being in the defined file also be shown at the end of a run, unless\n# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case\n# the behavior will remain as with the setting FAIL_ON_WARNINGS.\n# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT.\n# The default value is: NO.\n\nWARN_AS_ERROR          = NO\n\n# The WARN_FORMAT tag determines the format of the warning messages that doxygen\n# can produce. The string should contain the $file, $line, and $text tags, which\n# will be replaced by the file and line number from which the warning originated\n# and the warning text. Optionally the format may contain $version, which will\n# be replaced by the version of the file (if it could be obtained via\n# FILE_VERSION_FILTER)\n# See also: WARN_LINE_FORMAT\n# The default value is: $file:$line: $text.\n\nWARN_FORMAT            = \"$file:$line: $text\"\n\n# In the $text part of the WARN_FORMAT command it is possible that a reference\n# to a more specific place is given. To make it easier to jump to this place\n# (outside of doxygen) the user can define a custom \"cut\" / \"paste\" string.\n# Example:\n# WARN_LINE_FORMAT = \"'vi $file +$line'\"\n# See also: WARN_FORMAT\n# The default value is: at line $line of file $file.\n\nWARN_LINE_FORMAT       = \"at line $line of file $file\"\n\n# The WARN_LOGFILE tag can be used to specify a file to which warning and error\n# messages should be written. If left blank the output is written to standard\n# error (stderr). In case the file specified cannot be opened for writing the\n# warning and error messages are written to standard error. When as file - is\n# specified the warning and error messages are written to standard output\n# (stdout).\n\nWARN_LOGFILE           =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the input files\n#---------------------------------------------------------------------------\n\n# The INPUT tag is used to specify the files and/or directories that contain\n# documented source files. You may enter file names like myfile.cpp or\n# directories like /usr/src/myproject. Separate the files or directories with\n# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING\n# Note: If this tag is empty the current directory is searched.\n\nINPUT                  = ${TACHYON_SOURCE_DIR}/c ${TACHYON_BINARY_DIR}/tachyon/c docs/doxygen/main_page.md\n\n# This tag can be used to specify the character encoding of the source files\n# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses\n# libiconv (or the iconv built into libc) for the transcoding. See the libiconv\n# documentation (see:\n# https://www.gnu.org/software/libiconv/) for the list of possible encodings.\n# See also: INPUT_FILE_ENCODING\n# The default value is: UTF-8.\n\nINPUT_ENCODING         = UTF-8\n\n# This tag can be used to specify the character encoding of the source files\n# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify\n# character encoding on a per file pattern basis. Doxygen will compare the file\n# name with each pattern and apply the encoding instead of the default\n# INPUT_ENCODING) if there is a match. The character encodings are a list of the\n# form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding\n# \"INPUT_ENCODING\" for further information on supported encodings.\n\nINPUT_FILE_ENCODING    =\n\n# If the value of the INPUT tag contains directories, you can use the\n# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and\n# *.h) to filter out the source-files in the directories.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# read by doxygen.\n#\n# Note the list of default checked file patterns might differ from the list of\n# default file extension mappings.\n#\n# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm,\n# *.cpp, *.cppm, *.ccm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl,\n# *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d,\n# *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to\n# be provided as doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,\n# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice.\n\nFILE_PATTERNS          = *.h\n\n# The RECURSIVE tag can be used to specify whether or not subdirectories should\n# be searched for input files as well.\n# The default value is: NO.\n\nRECURSIVE              = YES\n\n# The EXCLUDE tag can be used to specify files and/or directories that should be\n# excluded from the INPUT source files. This way you can easily exclude a\n# subdirectory from a directory tree whose root is specified with the INPUT tag.\n#\n# Note that relative paths are relative to the directory from which doxygen is\n# run.\n\nEXCLUDE                =\n\n# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or\n# directories that are symbolic links (a Unix file system feature) are excluded\n# from the input.\n# The default value is: NO.\n\nEXCLUDE_SYMLINKS       = NO\n\n# If the value of the INPUT tag contains directories, you can use the\n# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude\n# certain files from those directories.\n#\n# Note that the wildcards are matched against the file with absolute path, so to\n# exclude all test directories for example use the pattern */test/*\n\nEXCLUDE_PATTERNS       = *traits*\n\n# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names\n# (namespaces, classes, functions, etc.) that should be excluded from the\n# output. The symbol name can be a fully qualified name, a word, or if the\n# wildcard * is used, a substring. Examples: ANamespace, AClass,\n# ANamespace::AClass, ANamespace::*Test\n\nEXCLUDE_SYMBOLS        =\n\n# The EXAMPLE_PATH tag can be used to specify one or more files or directories\n# that contain example code fragments that are included (see the \\include\n# command).\n\nEXAMPLE_PATH           = ${TACHYON_SOURCE_DIR}/c/examples\n\n# If the value of the EXAMPLE_PATH tag contains directories, you can use the\n# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and\n# *.h) to filter out the source-files in the directories. If left blank all\n# files are included.\n\nEXAMPLE_PATTERNS       = *.cc\n\n# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be\n# searched for input files to be used with the \\include or \\dontinclude commands\n# irrespective of the value of the RECURSIVE tag.\n# The default value is: NO.\n\nEXAMPLE_RECURSIVE      = NO\n\n# The IMAGE_PATH tag can be used to specify one or more files or directories\n# that contain images that are to be included in the documentation (see the\n# \\image command).\n\nIMAGE_PATH             =\n\n# The INPUT_FILTER tag can be used to specify a program that doxygen should\n# invoke to filter for each input file. Doxygen will invoke the filter program\n# by executing (via popen()) the command:\n#\n# <filter> <input-file>\n#\n# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the\n# name of an input file. Doxygen will then use the output that the filter\n# program writes to standard output. If FILTER_PATTERNS is specified, this tag\n# will be ignored.\n#\n# Note that the filter must not add or remove lines; it is applied before the\n# code is scanned, but not when the output code is generated. If lines are added\n# or removed, the anchors will not be placed correctly.\n#\n# Note that doxygen will use the data processed and written to standard output\n# for further processing, therefore nothing else, like debug statements or used\n# commands (so in case of a Windows batch file always use @echo OFF), should be\n# written to standard output.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# properly processed by doxygen.\n\nINPUT_FILTER           =\n\n# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern\n# basis. Doxygen will compare the file name with each pattern and apply the\n# filter if there is a match. The filters are a list of the form: pattern=filter\n# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how\n# filters are used. If the FILTER_PATTERNS tag is empty or if none of the\n# patterns match the file name, INPUT_FILTER is applied.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# properly processed by doxygen.\n\nFILTER_PATTERNS        =\n\n# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using\n# INPUT_FILTER) will also be used to filter the input files that are used for\n# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).\n# The default value is: NO.\n\nFILTER_SOURCE_FILES    = NO\n\n# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file\n# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and\n# it is also possible to disable source filtering for a specific pattern using\n# *.ext= (so without naming a filter).\n# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.\n\nFILTER_SOURCE_PATTERNS =\n\n# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that\n# is part of the input, its contents will be placed on the main page\n# (index.html). This can be useful if you have a project on for instance GitHub\n# and want to reuse the introduction page also for the doxygen output.\n\nUSE_MDFILE_AS_MAINPAGE = docs/doxygen/main_page.md\n\n# The Fortran standard specifies that for fixed formatted Fortran code all\n# characters from position 72 are to be considered as comment. A common\n# extension is to allow longer lines before the automatic comment starts. The\n# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can\n# be processed before the automatic comment starts.\n# Minimum value: 7, maximum value: 10000, default value: 72.\n\nFORTRAN_COMMENT_AFTER  = 72\n\n#---------------------------------------------------------------------------\n# Configuration options related to source browsing\n#---------------------------------------------------------------------------\n\n# If the SOURCE_BROWSER tag is set to YES then a list of source files will be\n# generated. Documented entities will be cross-referenced with these sources.\n#\n# Note: To get rid of all source code in the generated output, make sure that\n# also VERBATIM_HEADERS is set to NO.\n# The default value is: NO.\n\nSOURCE_BROWSER         = NO\n\n# Setting the INLINE_SOURCES tag to YES will include the body of functions,\n# multi-line macros, enums or list initialized variables directly into the\n# documentation.\n# The default value is: NO.\n\nINLINE_SOURCES         = NO\n\n# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any\n# special comment blocks from generated source code fragments. Normal C, C++ and\n# Fortran comments will always remain visible.\n# The default value is: YES.\n\nSTRIP_CODE_COMMENTS    = YES\n\n# If the REFERENCED_BY_RELATION tag is set to YES then for each documented\n# entity all documented functions referencing it will be listed.\n# The default value is: NO.\n\nREFERENCED_BY_RELATION = NO\n\n# If the REFERENCES_RELATION tag is set to YES then for each documented function\n# all documented entities called/used by that function will be listed.\n# The default value is: NO.\n\nREFERENCES_RELATION    = NO\n\n# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set\n# to YES then the hyperlinks from functions in REFERENCES_RELATION and\n# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will\n# link to the documentation.\n# The default value is: YES.\n\nREFERENCES_LINK_SOURCE = YES\n\n# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the\n# source code will show a tooltip with additional information such as prototype,\n# brief description and links to the definition and documentation. Since this\n# will make the HTML file larger and loading of large files a bit slower, you\n# can opt to disable this feature.\n# The default value is: YES.\n# This tag requires that the tag SOURCE_BROWSER is set to YES.\n\nSOURCE_TOOLTIPS        = YES\n\n# If the USE_HTAGS tag is set to YES then the references to source code will\n# point to the HTML generated by the htags(1) tool instead of doxygen built-in\n# source browser. The htags tool is part of GNU's global source tagging system\n# (see https://www.gnu.org/software/global/global.html). You will need version\n# 4.8.6 or higher.\n#\n# To use it do the following:\n# - Install the latest version of global\n# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file\n# - Make sure the INPUT points to the root of the source tree\n# - Run doxygen as normal\n#\n# Doxygen will invoke htags (and that will in turn invoke gtags), so these\n# tools must be available from the command line (i.e. in the search path).\n#\n# The result: instead of the source browser generated by doxygen, the links to\n# source code will now point to the output of htags.\n# The default value is: NO.\n# This tag requires that the tag SOURCE_BROWSER is set to YES.\n\nUSE_HTAGS              = NO\n\n# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a\n# verbatim copy of the header file for each class for which an include is\n# specified. Set to NO to disable this.\n# See also: Section \\class.\n# The default value is: YES.\n\nVERBATIM_HEADERS       = YES\n\n# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the\n# clang parser (see:\n# http://clang.llvm.org/) for more accurate parsing at the cost of reduced\n# performance. This can be particularly helpful with template rich C++ code for\n# which doxygen's built-in parser lacks the necessary type information.\n# Note: The availability of this option depends on whether or not doxygen was\n# generated with the -Duse_libclang=ON option for CMake.\n# The default value is: NO.\n\nCLANG_ASSISTED_PARSING = NO\n\n# If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS\n# tag is set to YES then doxygen will add the directory of each input to the\n# include path.\n# The default value is: YES.\n# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.\n\nCLANG_ADD_INC_PATHS    = YES\n\n# If clang assisted parsing is enabled you can provide the compiler with command\n# line options that you would normally use when invoking the compiler. Note that\n# the include paths will already be set by doxygen for the files and directories\n# specified with INPUT and INCLUDE_PATH.\n# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.\n\nCLANG_OPTIONS          =\n\n# If clang assisted parsing is enabled you can provide the clang parser with the\n# path to the directory containing a file called compile_commands.json. This\n# file is the compilation database (see:\n# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the\n# options used when the source files were built. This is equivalent to\n# specifying the -p option to a clang tool, such as clang-check. These options\n# will then be passed to the parser. Any options specified with CLANG_OPTIONS\n# will be added as well.\n# Note: The availability of this option depends on whether or not doxygen was\n# generated with the -Duse_libclang=ON option for CMake.\n\nCLANG_DATABASE_PATH    =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the alphabetical class index\n#---------------------------------------------------------------------------\n\n# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all\n# compounds will be generated. Enable this if the project contains a lot of\n# classes, structs, unions or interfaces.\n# The default value is: YES.\n\nALPHABETICAL_INDEX     = YES\n\n# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes)\n# that should be ignored while generating the index headers. The IGNORE_PREFIX\n# tag works for classes, function and member names. The entity will be placed in\n# the alphabetical list under the first letter of the entity name that remains\n# after removing the prefix.\n# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.\n\nIGNORE_PREFIX          =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the HTML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output\n# The default value is: YES.\n\nGENERATE_HTML          = YES\n\n# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: html.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_OUTPUT            = html\n\n# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each\n# generated HTML page (for example: .htm, .php, .asp).\n# The default value is: .html.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FILE_EXTENSION    = .html\n\n# The HTML_HEADER tag can be used to specify a user-defined HTML header file for\n# each generated HTML page. If the tag is left blank doxygen will generate a\n# standard header.\n#\n# To get valid HTML the header file that includes any scripts and style sheets\n# that doxygen needs, which is dependent on the configuration options used (e.g.\n# the setting GENERATE_TREEVIEW). It is highly recommended to start with a\n# default header using\n# doxygen -w html new_header.html new_footer.html new_stylesheet.css\n# YourConfigFile\n# and then modify the file new_header.html. See also section \"Doxygen usage\"\n# for information on how to generate the default header that doxygen normally\n# uses.\n# Note: The header is subject to change so you typically have to regenerate the\n# default header when upgrading to a newer version of doxygen. For a description\n# of the possible markers and block names see the documentation.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_HEADER            =\n\n# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each\n# generated HTML page. If the tag is left blank doxygen will generate a standard\n# footer. See HTML_HEADER for more information on how to generate a default\n# footer and what special commands can be used inside the footer. See also\n# section \"Doxygen usage\" for information on how to generate the default footer\n# that doxygen normally uses.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FOOTER            =\n\n# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style\n# sheet that is used by each HTML page. It can be used to fine-tune the look of\n# the HTML output. If left blank doxygen will generate a default style sheet.\n# See also section \"Doxygen usage\" for information on how to generate the style\n# sheet that doxygen normally uses.\n# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as\n# it is more robust and this tag (HTML_STYLESHEET) will in the future become\n# obsolete.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_STYLESHEET        =\n\n# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined\n# cascading style sheets that are included after the standard style sheets\n# created by doxygen. Using this option one can overrule certain style aspects.\n# This is preferred over using HTML_STYLESHEET since it does not replace the\n# standard style sheet and is therefore more robust against future updates.\n# Doxygen will copy the style sheet files to the output directory.\n# Note: The order of the extra style sheet files is of importance (e.g. the last\n# style sheet in the list overrules the setting of the previous ones in the\n# list).\n# Note: Since the styling of scrollbars can currently not be overruled in\n# Webkit/Chromium, the styling will be left out of the default doxygen.css if\n# one or more extra stylesheets have been specified. So if scrollbar\n# customization is desired it has to be added explicitly. For an example see the\n# documentation.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_EXTRA_STYLESHEET  =\n\n# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or\n# other source files which should be copied to the HTML output directory. Note\n# that these files will be copied to the base HTML output directory. Use the\n# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these\n# files. In the HTML_STYLESHEET file, use the file name only. Also note that the\n# files will be copied as-is; there are no commands or markers available.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_EXTRA_FILES       =\n\n# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output\n# should be rendered with a dark or light theme.\n# Possible values are: LIGHT always generate light mode output, DARK always\n# generate dark mode output, AUTO_LIGHT automatically set the mode according to\n# the user preference, use light mode if no preference is set (the default),\n# AUTO_DARK automatically set the mode according to the user preference, use\n# dark mode if no preference is set and TOGGLE allow to user to switch between\n# light and dark mode via a button.\n# The default value is: AUTO_LIGHT.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE        = AUTO_LIGHT\n\n# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen\n# will adjust the colors in the style sheet and background images according to\n# this color. Hue is specified as an angle on a color-wheel, see\n# https://en.wikipedia.org/wiki/Hue for more information. For instance the value\n# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300\n# purple, and 360 is red again.\n# Minimum value: 0, maximum value: 359, default value: 220.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_HUE    = 220\n\n# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors\n# in the HTML output. For a value of 0 the output will use gray-scales only. A\n# value of 255 will produce the most vivid colors.\n# Minimum value: 0, maximum value: 255, default value: 100.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_SAT    = 100\n\n# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the\n# luminance component of the colors in the HTML output. Values below 100\n# gradually make the output lighter, whereas values above 100 make the output\n# darker. The value divided by 100 is the actual gamma applied, so 80 represents\n# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not\n# change the gamma.\n# Minimum value: 40, maximum value: 240, default value: 80.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_GAMMA  = 80\n\n# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML\n# documentation will contain a main index with vertical navigation menus that\n# are dynamically created via JavaScript. If disabled, the navigation index will\n# consists of multiple levels of tabs that are statically embedded in every HTML\n# page. Disable this option to support browsers that do not have JavaScript,\n# like the Qt help browser.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_DYNAMIC_MENUS     = YES\n\n# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML\n# documentation will contain sections that can be hidden and shown after the\n# page has loaded.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_DYNAMIC_SECTIONS  = NO\n\n# If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be\n# dynamically folded and expanded in the generated HTML source code.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_CODE_FOLDING      = YES\n\n# If the HTML_COPY_CLIPBOARD tag is set to YES then doxygen will show an icon in\n# the top right corner of code and text fragments that allows the user to copy\n# its content to the clipboard. Note this only works if supported by the browser\n# and the web page is served via a secure context (see:\n# https://www.w3.org/TR/secure-contexts/), i.e. using the https: or file:\n# protocol.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COPY_CLIPBOARD    = YES\n\n# Doxygen stores a couple of settings persistently in the browser (via e.g.\n# cookies). By default these settings apply to all HTML pages generated by\n# doxygen across all projects. The HTML_PROJECT_COOKIE tag can be used to store\n# the settings under a project specific key, such that the user preferences will\n# be stored separately.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_PROJECT_COOKIE    =\n\n# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries\n# shown in the various tree structured indices initially; the user can expand\n# and collapse entries dynamically later on. Doxygen will expand the tree to\n# such a level that at most the specified number of entries are visible (unless\n# a fully collapsed tree already exceeds this amount). So setting the number of\n# entries 1 will produce a full collapsed tree by default. 0 is a special value\n# representing an infinite number of entries and will result in a full expanded\n# tree by default.\n# Minimum value: 0, maximum value: 9999, default value: 100.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_INDEX_NUM_ENTRIES = 100\n\n# If the GENERATE_DOCSET tag is set to YES, additional index files will be\n# generated that can be used as input for Apple's Xcode 3 integrated development\n# environment (see:\n# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To\n# create a documentation set, doxygen will generate a Makefile in the HTML\n# output directory. Running make will produce the docset in that directory and\n# running make install will install the docset in\n# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at\n# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy\n# genXcode/_index.html for more information.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_DOCSET        = NO\n\n# This tag determines the name of the docset feed. A documentation feed provides\n# an umbrella under which multiple documentation sets from a single provider\n# (such as a company or product suite) can be grouped.\n# The default value is: Doxygen generated docs.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_FEEDNAME        = \"Doxygen generated docs\"\n\n# This tag determines the URL of the docset feed. A documentation feed provides\n# an umbrella under which multiple documentation sets from a single provider\n# (such as a company or product suite) can be grouped.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_FEEDURL         =\n\n# This tag specifies a string that should uniquely identify the documentation\n# set bundle. This should be a reverse domain-name style string, e.g.\n# com.mycompany.MyDocSet. Doxygen will append .docset to the name.\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_BUNDLE_ID       = org.doxygen.Project\n\n# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify\n# the documentation publisher. This should be a reverse domain-name style\n# string, e.g. com.mycompany.MyDocSet.documentation.\n# The default value is: org.doxygen.Publisher.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_PUBLISHER_ID    = org.doxygen.Publisher\n\n# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.\n# The default value is: Publisher.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_PUBLISHER_NAME  = Publisher\n\n# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three\n# additional HTML index files: index.hhp, index.hhc, and index.hhk. The\n# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop\n# on Windows. In the beginning of 2021 Microsoft took the original page, with\n# a.o. the download links, offline the HTML help workshop was already many years\n# in maintenance mode). You can download the HTML help workshop from the web\n# archives at Installation executable (see:\n# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo\n# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe).\n#\n# The HTML Help Workshop contains a compiler that can convert all HTML output\n# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML\n# files are now used as the Windows 98 help format, and will replace the old\n# Windows help format (.hlp) on all Windows platforms in the future. Compressed\n# HTML files also contain an index, a table of contents, and you can search for\n# words in the documentation. The HTML workshop also contains a viewer for\n# compressed HTML files.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_HTMLHELP      = NO\n\n# The CHM_FILE tag can be used to specify the file name of the resulting .chm\n# file. You can add a path in front of the file if the result should not be\n# written to the html output directory.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nCHM_FILE               =\n\n# The HHC_LOCATION tag can be used to specify the location (absolute path\n# including file name) of the HTML help compiler (hhc.exe). If non-empty,\n# doxygen will try to run the HTML help compiler on the generated index.hhp.\n# The file has to be specified with full path.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nHHC_LOCATION           =\n\n# The GENERATE_CHI flag controls if a separate .chi index file is generated\n# (YES) or that it should be included in the main .chm file (NO).\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nGENERATE_CHI           = NO\n\n# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)\n# and project file content.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nCHM_INDEX_ENCODING     =\n\n# The BINARY_TOC flag controls whether a binary table of contents is generated\n# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it\n# enables the Previous and Next buttons.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nBINARY_TOC             = NO\n\n# The TOC_EXPAND flag can be set to YES to add extra items for group members to\n# the table of contents of the HTML help documentation and to the tree view.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nTOC_EXPAND             = NO\n\n# The SITEMAP_URL tag is used to specify the full URL of the place where the\n# generated documentation will be placed on the server by the user during the\n# deployment of the documentation. The generated sitemap is called sitemap.xml\n# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL\n# is specified no sitemap is generated. For information about the sitemap\n# protocol see https://www.sitemaps.org\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nSITEMAP_URL            =\n\n# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and\n# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that\n# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help\n# (.qch) of the generated HTML documentation.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_QHP           = NO\n\n# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify\n# the file name of the resulting .qch file. The path specified is relative to\n# the HTML output folder.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQCH_FILE               =\n\n# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help\n# Project output. For more information please see Qt Help Project / Namespace\n# (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_NAMESPACE          = org.doxygen.Project\n\n# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt\n# Help Project output. For more information please see Qt Help Project / Virtual\n# Folders (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders).\n# The default value is: doc.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_VIRTUAL_FOLDER     = doc\n\n# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom\n# filter to add. For more information please see Qt Help Project / Custom\n# Filters (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_CUST_FILTER_NAME   =\n\n# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the\n# custom filter to add. For more information please see Qt Help Project / Custom\n# Filters (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_CUST_FILTER_ATTRS  =\n\n# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this\n# project's filter section matches. Qt Help Project / Filter Attributes (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_SECT_FILTER_ATTRS  =\n\n# The QHG_LOCATION tag can be used to specify the location (absolute path\n# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to\n# run qhelpgenerator on the generated .qhp file.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHG_LOCATION           =\n\n# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be\n# generated, together with the HTML files, they form an Eclipse help plugin. To\n# install this plugin and make it available under the help contents menu in\n# Eclipse, the contents of the directory containing the HTML and XML files needs\n# to be copied into the plugins directory of eclipse. The name of the directory\n# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.\n# After copying Eclipse needs to be restarted before the help appears.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_ECLIPSEHELP   = NO\n\n# A unique identifier for the Eclipse help plugin. When installing the plugin\n# the directory name containing the HTML and XML files should also have this\n# name. Each documentation set should have its own identifier.\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.\n\nECLIPSE_DOC_ID         = org.doxygen.Project\n\n# If you want full control over the layout of the generated HTML pages it might\n# be necessary to disable the index and replace it with your own. The\n# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top\n# of each HTML page. A value of NO enables the index and the value YES disables\n# it. Since the tabs in the index contain the same information as the navigation\n# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nDISABLE_INDEX          = NO\n\n# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index\n# structure should be generated to display hierarchical information. If the tag\n# value is set to YES, a side panel will be generated containing a tree-like\n# index structure (just like the one that is generated for HTML Help). For this\n# to work a browser that supports JavaScript, DHTML, CSS and frames is required\n# (i.e. any modern browser). Windows users are probably better off using the\n# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can\n# further fine tune the look of the index (see \"Fine-tuning the output\"). As an\n# example, the default style sheet generated by doxygen has an example that\n# shows how to put an image at the root of the tree instead of the PROJECT_NAME.\n# Since the tree basically has the same information as the tab index, you could\n# consider setting DISABLE_INDEX to YES when enabling this option.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_TREEVIEW      = NO\n\n# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the\n# FULL_SIDEBAR option determines if the side bar is limited to only the treeview\n# area (value NO) or if it should extend to the full height of the window (value\n# YES). Setting this to YES gives a layout similar to\n# https://docs.readthedocs.io with more room for contents, but less room for the\n# project logo, title, and description. If either GENERATE_TREEVIEW or\n# DISABLE_INDEX is set to NO, this option has no effect.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nFULL_SIDEBAR           = NO\n\n# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that\n# doxygen will group on one line in the generated HTML documentation.\n#\n# Note that a value of 0 will completely suppress the enum values from appearing\n# in the overview section.\n# Minimum value: 0, maximum value: 20, default value: 4.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nENUM_VALUES_PER_LINE   = 4\n\n# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used\n# to set the initial width (in pixels) of the frame in which the tree is shown.\n# Minimum value: 0, maximum value: 1500, default value: 250.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nTREEVIEW_WIDTH         = 250\n\n# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to\n# external symbols imported via tag files in a separate window.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nEXT_LINKS_IN_WINDOW    = NO\n\n# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email\n# addresses.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nOBFUSCATE_EMAILS       = YES\n\n# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg\n# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see\n# https://inkscape.org) to generate formulas as SVG images instead of PNGs for\n# the HTML output. These images will generally look nicer at scaled resolutions.\n# Possible values are: png (the default) and svg (looks nicer but requires the\n# pdf2svg or inkscape tool).\n# The default value is: png.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FORMULA_FORMAT    = png\n\n# Use this tag to change the font size of LaTeX formulas included as images in\n# the HTML documentation. When you change the font size after a successful\n# doxygen run you need to manually remove any form_*.png images from the HTML\n# output directory to force them to be regenerated.\n# Minimum value: 8, maximum value: 50, default value: 10.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nFORMULA_FONTSIZE       = 10\n\n# The FORMULA_MACROFILE can contain LaTeX \\newcommand and \\renewcommand commands\n# to create new LaTeX commands to be used in formulas as building blocks. See\n# the section \"Including formulas\" for details.\n\nFORMULA_MACROFILE      =\n\n# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see\n# https://www.mathjax.org) which uses client side JavaScript for the rendering\n# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX\n# installed or if you want to formulas look prettier in the HTML output. When\n# enabled you may also need to install MathJax separately and configure the path\n# to it using the MATHJAX_RELPATH option.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nUSE_MATHJAX            = NO\n\n# With MATHJAX_VERSION it is possible to specify the MathJax version to be used.\n# Note that the different versions of MathJax have different requirements with\n# regards to the different settings, so it is possible that also other MathJax\n# settings have to be changed when switching between the different MathJax\n# versions.\n# Possible values are: MathJax_2 and MathJax_3.\n# The default value is: MathJax_2.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_VERSION        = MathJax_2\n\n# When MathJax is enabled you can set the default output format to be used for\n# the MathJax output. For more details about the output format see MathJax\n# version 2 (see:\n# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3\n# (see:\n# http://docs.mathjax.org/en/latest/web/components/output.html).\n# Possible values are: HTML-CSS (which is slower, but has the best\n# compatibility. This is the name for Mathjax version 2, for MathJax version 3\n# this will be translated into chtml), NativeMML (i.e. MathML. Only supported\n# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This\n# is the name for Mathjax version 3, for MathJax version 2 this will be\n# translated into HTML-CSS) and SVG.\n# The default value is: HTML-CSS.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_FORMAT         = HTML-CSS\n\n# When MathJax is enabled you need to specify the location relative to the HTML\n# output directory using the MATHJAX_RELPATH option. The destination directory\n# should contain the MathJax.js script. For instance, if the mathjax directory\n# is located at the same level as the HTML output directory, then\n# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax\n# Content Delivery Network so you can quickly see the result without installing\n# MathJax. However, it is strongly recommended to install a local copy of\n# MathJax from https://www.mathjax.org before deployment. The default value is:\n# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2\n# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_RELPATH        =\n\n# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax\n# extension names that should be enabled during MathJax rendering. For example\n# for MathJax version 2 (see\n# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions):\n# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols\n# For example for MathJax version 3 (see\n# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html):\n# MATHJAX_EXTENSIONS = ams\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_EXTENSIONS     =\n\n# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces\n# of code that will be used on startup of the MathJax code. See the MathJax site\n# (see:\n# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an\n# example see the documentation.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_CODEFILE       =\n\n# When the SEARCHENGINE tag is enabled doxygen will generate a search box for\n# the HTML output. The underlying search engine uses javascript and DHTML and\n# should work on any modern browser. Note that when using HTML help\n# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)\n# there is already a search function so this one should typically be disabled.\n# For large projects the javascript based search engine can be slow, then\n# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to\n# search using the keyboard; to jump to the search box use <access key> + S\n# (what the <access key> is depends on the OS and browser, but it is typically\n# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down\n# key> to jump into the search results window, the results can be navigated\n# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel\n# the search. The filter options can be selected when the cursor is inside the\n# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>\n# to select a filter and <Enter> or <escape> to activate or cancel the filter\n# option.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nSEARCHENGINE           = YES\n\n# When the SERVER_BASED_SEARCH tag is enabled the search engine will be\n# implemented using a web server instead of a web client using JavaScript. There\n# are two flavors of web server based searching depending on the EXTERNAL_SEARCH\n# setting. When disabled, doxygen will generate a PHP script for searching and\n# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing\n# and searching needs to be provided by external tools. See the section\n# \"External Indexing and Searching\" for details.\n# The default value is: NO.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSERVER_BASED_SEARCH    = NO\n\n# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP\n# script for searching. Instead the search results are written to an XML file\n# which needs to be processed by an external indexer. Doxygen will invoke an\n# external search engine pointed to by the SEARCHENGINE_URL option to obtain the\n# search results.\n#\n# Doxygen ships with an example indexer (doxyindexer) and search engine\n# (doxysearch.cgi) which are based on the open source search engine library\n# Xapian (see:\n# https://xapian.org/).\n#\n# See the section \"External Indexing and Searching\" for details.\n# The default value is: NO.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTERNAL_SEARCH        = NO\n\n# The SEARCHENGINE_URL should point to a search engine hosted by a web server\n# which will return the search results when EXTERNAL_SEARCH is enabled.\n#\n# Doxygen ships with an example indexer (doxyindexer) and search engine\n# (doxysearch.cgi) which are based on the open source search engine library\n# Xapian (see:\n# https://xapian.org/). See the section \"External Indexing and Searching\" for\n# details.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSEARCHENGINE_URL       =\n\n# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed\n# search data is written to a file for indexing by an external tool. With the\n# SEARCHDATA_FILE tag the name of this file can be specified.\n# The default file is: searchdata.xml.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSEARCHDATA_FILE        = searchdata.xml\n\n# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the\n# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is\n# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple\n# projects and redirect the results back to the right project.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTERNAL_SEARCH_ID     =\n\n# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen\n# projects other than the one defined by this configuration file, but that are\n# all added to the same external search index. Each project needs to have a\n# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of\n# to a relative location where the documentation can be found. The format is:\n# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTRA_SEARCH_MAPPINGS  =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the LaTeX output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.\n# The default value is: YES.\n\nGENERATE_LATEX         = NO\n\n# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: latex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_OUTPUT           = latex\n\n# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be\n# invoked.\n#\n# Note that when not enabling USE_PDFLATEX the default is latex when enabling\n# USE_PDFLATEX the default is pdflatex and when in the later case latex is\n# chosen this is overwritten by pdflatex. For specific output languages the\n# default can have been set differently, this depends on the implementation of\n# the output language.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_CMD_NAME         =\n\n# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate\n# index for LaTeX.\n# Note: This tag is used in the Makefile / make.bat.\n# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file\n# (.tex).\n# The default file is: makeindex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nMAKEINDEX_CMD_NAME     = makeindex\n\n# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to\n# generate index for LaTeX. In case there is no backslash (\\) as first character\n# it will be automatically added in the LaTeX code.\n# Note: This tag is used in the generated output file (.tex).\n# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.\n# The default value is: makeindex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_MAKEINDEX_CMD    = makeindex\n\n# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX\n# documents. This may be useful for small projects and may help to save some\n# trees in general.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nCOMPACT_LATEX          = NO\n\n# The PAPER_TYPE tag can be used to set the paper type that is used by the\n# printer.\n# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x\n# 14 inches) and executive (7.25 x 10.5 inches).\n# The default value is: a4.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nPAPER_TYPE             = a4\n\n# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names\n# that should be included in the LaTeX output. The package can be specified just\n# by its name or with the correct syntax as to be used with the LaTeX\n# \\usepackage command. To get the times font for instance you can specify :\n# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}\n# To use the option intlimits with the amsmath package you can specify:\n# EXTRA_PACKAGES=[intlimits]{amsmath}\n# If left blank no extra packages will be included.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nEXTRA_PACKAGES         =\n\n# The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for\n# the generated LaTeX document. The header should contain everything until the\n# first chapter. If it is left blank doxygen will generate a standard header. It\n# is highly recommended to start with a default header using\n# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty\n# and then modify the file new_header.tex. See also section \"Doxygen usage\" for\n# information on how to generate the default header that doxygen normally uses.\n#\n# Note: Only use a user-defined header if you know what you are doing!\n# Note: The header is subject to change so you typically have to regenerate the\n# default header when upgrading to a newer version of doxygen. The following\n# commands have a special meaning inside the header (and footer): For a\n# description of the possible markers and block names see the documentation.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_HEADER           =\n\n# The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for\n# the generated LaTeX document. The footer should contain everything after the\n# last chapter. If it is left blank doxygen will generate a standard footer. See\n# LATEX_HEADER for more information on how to generate a default footer and what\n# special commands can be used inside the footer. See also section \"Doxygen\n# usage\" for information on how to generate the default footer that doxygen\n# normally uses. Note: Only use a user-defined footer if you know what you are\n# doing!\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_FOOTER           =\n\n# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined\n# LaTeX style sheets that are included after the standard style sheets created\n# by doxygen. Using this option one can overrule certain style aspects. Doxygen\n# will copy the style sheet files to the output directory.\n# Note: The order of the extra style sheet files is of importance (e.g. the last\n# style sheet in the list overrules the setting of the previous ones in the\n# list).\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EXTRA_STYLESHEET =\n\n# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or\n# other source files which should be copied to the LATEX_OUTPUT output\n# directory. Note that the files will be copied as-is; there are no commands or\n# markers available.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EXTRA_FILES      =\n\n# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is\n# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will\n# contain links (just like the HTML output) instead of page references. This\n# makes the output suitable for online browsing using a PDF viewer.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nPDF_HYPERLINKS         = YES\n\n# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as\n# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX\n# files. Set this option to YES, to get a higher quality PDF documentation.\n#\n# See also section LATEX_CMD_NAME for selecting the engine.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nUSE_PDFLATEX           = YES\n\n# The LATEX_BATCHMODE tag signals the behavior of LaTeX in case of an error.\n# Possible values are: NO same as ERROR_STOP, YES same as BATCH, BATCH In batch\n# mode nothing is printed on the terminal, errors are scrolled as if <return> is\n# hit at every error; missing files that TeX tries to input or request from\n# keyboard input (\\read on a not open input stream) cause the job to abort,\n# NON_STOP In nonstop mode the diagnostic message will appear on the terminal,\n# but there is no possibility of user interaction just like in batch mode,\n# SCROLL In scroll mode, TeX will stop only for missing files to input or if\n# keyboard input is necessary and ERROR_STOP In errorstop mode, TeX will stop at\n# each error, asking for user intervention.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_BATCHMODE        = NO\n\n# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the\n# index chapters (such as File Index, Compound Index, etc.) in the output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_HIDE_INDICES     = NO\n\n# The LATEX_BIB_STYLE tag can be used to specify the style to use for the\n# bibliography, e.g. plainnat, or ieeetr. See\n# https://en.wikipedia.org/wiki/BibTeX and \\cite for more info.\n# The default value is: plain.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_BIB_STYLE        = plain\n\n# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)\n# path from which the emoji images will be read. If a relative path is entered,\n# it will be relative to the LATEX_OUTPUT directory. If left blank the\n# LATEX_OUTPUT directory will be used.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EMOJI_DIRECTORY  =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the RTF output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The\n# RTF output is optimized for Word 97 and may not look too pretty with other RTF\n# readers/editors.\n# The default value is: NO.\n\nGENERATE_RTF           = NO\n\n# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: rtf.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_OUTPUT             = rtf\n\n# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF\n# documents. This may be useful for small projects and may help to save some\n# trees in general.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nCOMPACT_RTF            = NO\n\n# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will\n# contain hyperlink fields. The RTF file will contain links (just like the HTML\n# output) instead of page references. This makes the output suitable for online\n# browsing using Word or some other Word compatible readers that support those\n# fields.\n#\n# Note: WordPad (write) and others do not support links.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_HYPERLINKS         = NO\n\n# Load stylesheet definitions from file. Syntax is similar to doxygen's\n# configuration file, i.e. a series of assignments. You only have to provide\n# replacements, missing definitions are set to their default value.\n#\n# See also section \"Doxygen usage\" for information on how to generate the\n# default style sheet that doxygen normally uses.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_STYLESHEET_FILE    =\n\n# Set optional variables used in the generation of an RTF document. Syntax is\n# similar to doxygen's configuration file. A template extensions file can be\n# generated using doxygen -e rtf extensionFile.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_EXTENSIONS_FILE    =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the man page output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for\n# classes and files.\n# The default value is: NO.\n\nGENERATE_MAN           = NO\n\n# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it. A directory man3 will be created inside the directory specified by\n# MAN_OUTPUT.\n# The default directory is: man.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_OUTPUT             = man\n\n# The MAN_EXTENSION tag determines the extension that is added to the generated\n# man pages. In case the manual section does not start with a number, the number\n# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is\n# optional.\n# The default value is: .3.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_EXTENSION          = .3\n\n# The MAN_SUBDIR tag determines the name of the directory created within\n# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by\n# MAN_EXTENSION with the initial . removed.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_SUBDIR             =\n\n# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it\n# will generate one additional man file for each entity documented in the real\n# man page(s). These additional files only source the real man page, but without\n# them the man command would be unable to find the correct page.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_LINKS              = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the XML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that\n# captures the structure of the code including all documentation.\n# The default value is: NO.\n\nGENERATE_XML           = NO\n\n# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: xml.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_OUTPUT             = xml\n\n# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program\n# listings (including syntax highlighting and cross-referencing information) to\n# the XML output. Note that enabling this will significantly increase the size\n# of the XML output.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_PROGRAMLISTING     = YES\n\n# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include\n# namespace members in file scope as well, matching the HTML output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_NS_MEMB_FILE_SCOPE = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the DOCBOOK output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files\n# that can be used to generate PDF.\n# The default value is: NO.\n\nGENERATE_DOCBOOK       = NO\n\n# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.\n# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in\n# front of it.\n# The default directory is: docbook.\n# This tag requires that the tag GENERATE_DOCBOOK is set to YES.\n\nDOCBOOK_OUTPUT         = docbook\n\n#---------------------------------------------------------------------------\n# Configuration options for the AutoGen Definitions output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an\n# AutoGen Definitions (see https://autogen.sourceforge.net/) file that captures\n# the structure of the code including all documentation. Note that this feature\n# is still experimental and incomplete at the moment.\n# The default value is: NO.\n\nGENERATE_AUTOGEN_DEF   = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to Sqlite3 output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_SQLITE3 tag is set to YES doxygen will generate a Sqlite3\n# database with symbols found by doxygen stored in tables.\n# The default value is: NO.\n\nGENERATE_SQLITE3       = NO\n\n# The SQLITE3_OUTPUT tag is used to specify where the Sqlite3 database will be\n# put. If a relative path is entered the value of OUTPUT_DIRECTORY will be put\n# in front of it.\n# The default directory is: sqlite3.\n# This tag requires that the tag GENERATE_SQLITE3 is set to YES.\n\nSQLITE3_OUTPUT         = sqlite3\n\n# The SQLITE3_RECREATE_DB tag is set to YES, the existing doxygen_sqlite3.db\n# database file will be recreated with each doxygen run. If set to NO, doxygen\n# will warn if a database file is already found and not modify it.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_SQLITE3 is set to YES.\n\nSQLITE3_RECREATE_DB    = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to the Perl module output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module\n# file that captures the structure of the code including all documentation.\n#\n# Note that this feature is still experimental and incomplete at the moment.\n# The default value is: NO.\n\nGENERATE_PERLMOD       = NO\n\n# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary\n# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI\n# output from the Perl module output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_LATEX          = NO\n\n# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely\n# formatted so it can be parsed by a human reader. This is useful if you want to\n# understand what is going on. On the other hand, if this tag is set to NO, the\n# size of the Perl module output will be much smaller and Perl will parse it\n# just the same.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_PRETTY         = YES\n\n# The names of the make variables in the generated doxyrules.make file are\n# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful\n# so different doxyrules.make files included by the same Makefile don't\n# overwrite each other's variables.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_MAKEVAR_PREFIX =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the preprocessor\n#---------------------------------------------------------------------------\n\n# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all\n# C-preprocessor directives found in the sources and include files.\n# The default value is: YES.\n\nENABLE_PREPROCESSING   = YES\n\n# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names\n# in the source code. If set to NO, only conditional compilation will be\n# performed. Macro expansion can be done in a controlled way by setting\n# EXPAND_ONLY_PREDEF to YES.\n# The default value is: NO.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nMACRO_EXPANSION        = NO\n\n# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then\n# the macro expansion is limited to the macros specified with the PREDEFINED and\n# EXPAND_AS_DEFINED tags.\n# The default value is: NO.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nEXPAND_ONLY_PREDEF     = NO\n\n# If the SEARCH_INCLUDES tag is set to YES, the include files in the\n# INCLUDE_PATH will be searched if a #include is found.\n# The default value is: YES.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nSEARCH_INCLUDES        = YES\n\n# The INCLUDE_PATH tag can be used to specify one or more directories that\n# contain include files that are not input files but should be processed by the\n# preprocessor. Note that the INCLUDE_PATH is not recursive, so the setting of\n# RECURSIVE has no effect here.\n# This tag requires that the tag SEARCH_INCLUDES is set to YES.\n\nINCLUDE_PATH           =\n\n# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard\n# patterns (like *.h and *.hpp) to filter out the header-files in the\n# directories. If left blank, the patterns specified with FILE_PATTERNS will be\n# used.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nINCLUDE_FILE_PATTERNS  =\n\n# The PREDEFINED tag can be used to specify one or more macro names that are\n# defined before the preprocessor is started (similar to the -D option of e.g.\n# gcc). The argument of the tag is a list of macros of the form: name or\n# name=definition (no spaces). If the definition and the \"=\" are omitted, \"=1\"\n# is assumed. To prevent a macro definition from being undefined via #undef or\n# recursively expanded use the := operator instead of the = operator.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nPREDEFINED             =\n\n# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this\n# tag can be used to specify a list of macro names that should be expanded. The\n# macro definition that is found in the sources will be used. Use the PREDEFINED\n# tag if you want to use a different macro definition that overrules the\n# definition found in the source code.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nEXPAND_AS_DEFINED      =\n\n# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will\n# remove all references to function-like macros that are alone on a line, have\n# an all uppercase name, and do not end with a semicolon. Such function macros\n# are typically used for boiler-plate code, and will confuse the parser if not\n# removed.\n# The default value is: YES.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nSKIP_FUNCTION_MACROS   = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to external references\n#---------------------------------------------------------------------------\n\n# The TAGFILES tag can be used to specify one or more tag files. For each tag\n# file the location of the external documentation should be added. The format of\n# a tag file without this location is as follows:\n# TAGFILES = file1 file2 ...\n# Adding location for the tag files is done as follows:\n# TAGFILES = file1=loc1 \"file2 = loc2\" ...\n# where loc1 and loc2 can be relative or absolute paths or URLs. See the\n# section \"Linking to external documentation\" for more information about the use\n# of tag files.\n# Note: Each tag file must have a unique name (where the name does NOT include\n# the path). If a tag file is not located in the directory in which doxygen is\n# run, you must also specify the path to the tagfile here.\n\nTAGFILES               =\n\n# When a file name is specified after GENERATE_TAGFILE, doxygen will create a\n# tag file that is based on the input files it reads. See section \"Linking to\n# external documentation\" for more information about the usage of tag files.\n\nGENERATE_TAGFILE       =\n\n# If the ALLEXTERNALS tag is set to YES, all external classes and namespaces\n# will be listed in the class and namespace index. If set to NO, only the\n# inherited external classes will be listed.\n# The default value is: NO.\n\nALLEXTERNALS           = NO\n\n# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed\n# in the topic index. If set to NO, only the current project's groups will be\n# listed.\n# The default value is: YES.\n\nEXTERNAL_GROUPS        = YES\n\n# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in\n# the related pages index. If set to NO, only the current project's pages will\n# be listed.\n# The default value is: YES.\n\nEXTERNAL_PAGES         = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to diagram generator tools\n#---------------------------------------------------------------------------\n\n# If set to YES the inheritance and collaboration graphs will hide inheritance\n# and usage relations if the target is undocumented or is not a class.\n# The default value is: YES.\n\nHIDE_UNDOC_RELATIONS   = YES\n\n# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is\n# available from the path. This tool is part of Graphviz (see:\n# https://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent\n# Bell Labs. The other options in this section have no effect if this option is\n# set to NO\n# The default value is: NO.\n\nHAVE_DOT               = NO\n\n# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed\n# to run in parallel. When set to 0 doxygen will base this on the number of\n# processors available in the system. You can set it explicitly to a value\n# larger than 0 to get control over the balance between CPU load and processing\n# speed.\n# Minimum value: 0, maximum value: 32, default value: 0.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_NUM_THREADS        = 0\n\n# DOT_COMMON_ATTR is common attributes for nodes, edges and labels of\n# subgraphs. When you want a differently looking font in the dot files that\n# doxygen generates you can specify fontname, fontcolor and fontsize attributes.\n# For details please see <a href=https://graphviz.org/doc/info/attrs.html>Node,\n# Edge and Graph Attributes specification</a> You need to make sure dot is able\n# to find the font, which can be done by putting it in a standard location or by\n# setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the\n# directory containing the font. Default graphviz fontsize is 14.\n# The default value is: fontname=Helvetica,fontsize=10.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_COMMON_ATTR        = \"fontname=Helvetica,fontsize=10\"\n\n# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can\n# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. <a\n# href=https://graphviz.org/doc/info/arrows.html>Complete documentation about\n# arrows shapes.</a>\n# The default value is: labelfontname=Helvetica,labelfontsize=10.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_EDGE_ATTR          = \"labelfontname=Helvetica,labelfontsize=10\"\n\n# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes\n# around nodes set 'shape=plain' or 'shape=plaintext' <a\n# href=https://www.graphviz.org/doc/info/shapes.html>Shapes specification</a>\n# The default value is: shape=box,height=0.2,width=0.4.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_NODE_ATTR          = \"shape=box,height=0.2,width=0.4\"\n\n# You can set the path where dot can find font specified with fontname in\n# DOT_COMMON_ATTR and others dot attributes.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTPATH           =\n\n# If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then doxygen will\n# generate a graph for each documented class showing the direct and indirect\n# inheritance relations. In case the CLASS_GRAPH tag is set to YES or GRAPH and\n# HAVE_DOT is enabled as well, then dot will be used to draw the graph. In case\n# the CLASS_GRAPH tag is set to YES and HAVE_DOT is disabled or if the\n# CLASS_GRAPH tag is set to BUILTIN, then the built-in generator will be used.\n# If the CLASS_GRAPH tag is set to TEXT the direct and indirect inheritance\n# relations will be shown as texts / links. Explicit enabling an inheritance\n# graph or choosing a different representation for an inheritance graph of a\n# specific class, can be accomplished by means of the command \\inheritancegraph.\n# Disabling an inheritance graph can be accomplished by means of the command\n# \\hideinheritancegraph.\n# Possible values are: NO, YES, TEXT, GRAPH and BUILTIN.\n# The default value is: YES.\n\nCLASS_GRAPH            = YES\n\n# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a\n# graph for each documented class showing the direct and indirect implementation\n# dependencies (inheritance, containment, and class references variables) of the\n# class with other documented classes. Explicit enabling a collaboration graph,\n# when COLLABORATION_GRAPH is set to NO, can be accomplished by means of the\n# command \\collaborationgraph. Disabling a collaboration graph can be\n# accomplished by means of the command \\hidecollaborationgraph.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCOLLABORATION_GRAPH    = YES\n\n# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for\n# groups, showing the direct groups dependencies. Explicit enabling a group\n# dependency graph, when GROUP_GRAPHS is set to NO, can be accomplished by means\n# of the command \\groupgraph. Disabling a directory graph can be accomplished by\n# means of the command \\hidegroupgraph. See also the chapter Grouping in the\n# manual.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGROUP_GRAPHS           = YES\n\n# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and\n# collaboration diagrams in a style similar to the OMG's Unified Modeling\n# Language.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nUML_LOOK               = NO\n\n# If the UML_LOOK tag is enabled, the fields and methods are shown inside the\n# class node. If there are many fields or methods and many nodes the graph may\n# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the\n# number of items for each type to make the size more manageable. Set this to 0\n# for no limit. Note that the threshold may be exceeded by 50% before the limit\n# is enforced. So when you set the threshold to 10, up to 15 fields may appear,\n# but if the number exceeds 15, the total amount of fields shown is limited to\n# 10.\n# Minimum value: 0, maximum value: 100, default value: 10.\n# This tag requires that the tag UML_LOOK is set to YES.\n\nUML_LIMIT_NUM_FIELDS   = 10\n\n# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and\n# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS\n# tag is set to YES, doxygen will add type and arguments for attributes and\n# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen\n# will not generate fields with class member information in the UML graphs. The\n# class diagrams will look similar to the default class diagrams but using UML\n# notation for the relationships.\n# Possible values are: NO, YES and NONE.\n# The default value is: NO.\n# This tag requires that the tag UML_LOOK is set to YES.\n\nDOT_UML_DETAILS        = NO\n\n# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters\n# to display on a single line. If the actual line length exceeds this threshold\n# significantly it will be wrapped across multiple lines. Some heuristics are\n# applied to avoid ugly line breaks.\n# Minimum value: 0, maximum value: 1000, default value: 17.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_WRAP_THRESHOLD     = 17\n\n# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and\n# collaboration graphs will show the relations between templates and their\n# instances.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nTEMPLATE_RELATIONS     = NO\n\n# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to\n# YES then doxygen will generate a graph for each documented file showing the\n# direct and indirect include dependencies of the file with other documented\n# files. Explicit enabling an include graph, when INCLUDE_GRAPH is is set to NO,\n# can be accomplished by means of the command \\includegraph. Disabling an\n# include graph can be accomplished by means of the command \\hideincludegraph.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINCLUDE_GRAPH          = YES\n\n# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are\n# set to YES then doxygen will generate a graph for each documented file showing\n# the direct and indirect include dependencies of the file with other documented\n# files. Explicit enabling an included by graph, when INCLUDED_BY_GRAPH is set\n# to NO, can be accomplished by means of the command \\includedbygraph. Disabling\n# an included by graph can be accomplished by means of the command\n# \\hideincludedbygraph.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINCLUDED_BY_GRAPH      = YES\n\n# If the CALL_GRAPH tag is set to YES then doxygen will generate a call\n# dependency graph for every global function or class method.\n#\n# Note that enabling this option will significantly increase the time of a run.\n# So in most cases it will be better to enable call graphs for selected\n# functions only using the \\callgraph command. Disabling a call graph can be\n# accomplished by means of the command \\hidecallgraph.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCALL_GRAPH             = NO\n\n# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller\n# dependency graph for every global function or class method.\n#\n# Note that enabling this option will significantly increase the time of a run.\n# So in most cases it will be better to enable caller graphs for selected\n# functions only using the \\callergraph command. Disabling a caller graph can be\n# accomplished by means of the command \\hidecallergraph.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCALLER_GRAPH           = NO\n\n# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical\n# hierarchy of all classes instead of a textual one.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGRAPHICAL_HIERARCHY    = YES\n\n# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the\n# dependencies a directory has on other directories in a graphical way. The\n# dependency relations are determined by the #include relations between the\n# files in the directories. Explicit enabling a directory graph, when\n# DIRECTORY_GRAPH is set to NO, can be accomplished by means of the command\n# \\directorygraph. Disabling a directory graph can be accomplished by means of\n# the command \\hidedirectorygraph.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDIRECTORY_GRAPH        = YES\n\n# The DIR_GRAPH_MAX_DEPTH tag can be used to limit the maximum number of levels\n# of child directories generated in directory dependency graphs by dot.\n# Minimum value: 1, maximum value: 25, default value: 1.\n# This tag requires that the tag DIRECTORY_GRAPH is set to YES.\n\nDIR_GRAPH_MAX_DEPTH    = 1\n\n# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images\n# generated by dot. For an explanation of the image formats see the section\n# output formats in the documentation of the dot tool (Graphviz (see:\n# https://www.graphviz.org/)).\n# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order\n# to make the SVG files visible in IE 9+ (other browsers do not have this\n# requirement).\n# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,\n# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and\n# png:gdiplus:gdiplus.\n# The default value is: png.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_IMAGE_FORMAT       = png\n\n# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to\n# enable generation of interactive SVG images that allow zooming and panning.\n#\n# Note that this requires a modern browser other than Internet Explorer. Tested\n# and working are Firefox, Chrome, Safari, and Opera.\n# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make\n# the SVG files visible. Older versions of IE do not have SVG support.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINTERACTIVE_SVG        = NO\n\n# The DOT_PATH tag can be used to specify the path where the dot tool can be\n# found. If left blank, it is assumed the dot tool can be found in the path.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_PATH               =\n\n# The DOTFILE_DIRS tag can be used to specify one or more directories that\n# contain dot files that are included in the documentation (see the \\dotfile\n# command).\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOTFILE_DIRS           =\n\n# You can include diagrams made with dia in doxygen documentation. Doxygen will\n# then run dia to produce the diagram and insert it in the documentation. The\n# DIA_PATH tag allows you to specify the directory where the dia binary resides.\n# If left empty dia is assumed to be found in the default search path.\n\nDIA_PATH               =\n\n# The DIAFILE_DIRS tag can be used to specify one or more directories that\n# contain dia files that are included in the documentation (see the \\diafile\n# command).\n\nDIAFILE_DIRS           =\n\n# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the\n# path where java can find the plantuml.jar file or to the filename of jar file\n# to be used. If left blank, it is assumed PlantUML is not used or called during\n# a preprocessing step. Doxygen will generate a warning when it encounters a\n# \\startuml command in this case and will not generate output for the diagram.\n\nPLANTUML_JAR_PATH      =\n\n# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a\n# configuration file for plantuml.\n\nPLANTUML_CFG_FILE      =\n\n# When using plantuml, the specified paths are searched for files specified by\n# the !include statement in a plantuml block.\n\nPLANTUML_INCLUDE_PATH  =\n\n# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes\n# that will be shown in the graph. If the number of nodes in a graph becomes\n# larger than this value, doxygen will truncate the graph, which is visualized\n# by representing a node as a red box. Note that doxygen if the number of direct\n# children of the root node in a graph is already larger than\n# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that\n# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.\n# Minimum value: 0, maximum value: 10000, default value: 50.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_GRAPH_MAX_NODES    = 50\n\n# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs\n# generated by dot. A depth value of 3 means that only nodes reachable from the\n# root by following a path via at most 3 edges will be shown. Nodes that lay\n# further from the root node will be omitted. Note that setting this option to 1\n# or 2 may greatly reduce the computation time needed for large code bases. Also\n# note that the size of a graph can be further restricted by\n# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.\n# Minimum value: 0, maximum value: 1000, default value: 0.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nMAX_DOT_GRAPH_DEPTH    = 0\n\n# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output\n# files in one run (i.e. multiple -o and -T options on the command line). This\n# makes dot run faster, but since only newer versions of dot (>1.8.10) support\n# this, this feature is disabled by default.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_MULTI_TARGETS      = NO\n\n# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page\n# explaining the meaning of the various boxes and arrows in the dot generated\n# graphs.\n# Note: This tag requires that UML_LOOK isn't set, i.e. the doxygen internal\n# graphical representation for inheritance and collaboration diagrams is used.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGENERATE_LEGEND        = YES\n\n# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate\n# files that are used to generate the various graphs.\n#\n# Note: This setting is not only used for dot files but also for msc temporary\n# files.\n# The default value is: YES.\n\nDOT_CLEANUP            = YES\n\n# You can define message sequence charts within doxygen comments using the \\msc\n# command. If the MSCGEN_TOOL tag is left empty (the default), then doxygen will\n# use a built-in version of mscgen tool to produce the charts. Alternatively,\n# the MSCGEN_TOOL tag can also specify the name an external tool. For instance,\n# specifying prog as the value, doxygen will call the tool as prog -T\n# <outfile_format> -o <outputfile> <inputfile>. The external tool should support\n# output file formats \"png\", \"eps\", \"svg\", and \"ismap\".\n\nMSCGEN_TOOL            =\n\n# The MSCFILE_DIRS tag can be used to specify one or more directories that\n# contain msc files that are included in the documentation (see the \\mscfile\n# command).\n\nMSCFILE_DIRS           =\n"
  },
  {
    "path": "docs/doxygen/main_page.md",
    "content": "# Tachyon\n\nThis document lists and describes the APIs exposed in C by Tachyon.\n\nRefer to [the Tachyon repository](https://github.com/kroma-network/tachyon) for the full codebase.\n\n## Other references\n\n- [Getting Started](https://github.com/kroma-network/tachyon/blob/main/README.md)\n- [How to use](https://github.com/kroma-network/tachyon/tree/main/docs/how_to_use)\n- [How to contribute](https://github.com/kroma-network/tachyon/tree/main/docs/how_to_contribute)\n- [Benchmark](https://github.com/kroma-network/tachyon/blob/main/benchmark/README.md)\n"
  },
  {
    "path": "docs/how_to_contribute/conventions.md",
    "content": "# Conventions\n\n## Styling\n\nWe follow the standards of the [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html), but have adapted some of the conventions to better fit our needs. Differences and more detailed explanations of some rules are highlighted below.\n\n### Casting\n\nIf an implicit cast for a constant value integral is valid, do not use a redundant explicit cast.\n\n```c++\ntemplate <size_t N>\nclass BigInt {};\n\n// DO\nBigInt<1> bigint;\n\n// DON'T DO\n// This will be casted redundantly as BigInt<size_t{size_t{1}}>\nBigInt<size_t{1}> bigint;\n\n// DO\nsize_t GetSize() { return 1; }\n\n// Note: Continue to use explicit casts for cases that require it logically\nsize_t size = size_t{1} << bit;\n```\n\n[Correlating Google C++ casting rule here](https://google.github.io/styleguide/cppguide.html#Casting)\n\n### Comments\n\n#### Code-specific Names\n\nEnclose code-specific names in the comments with vertical bars \"|\". This includes names of variables, functions, files, objects, and more.\n\n```c++\n// DON'T DO:\n// \"foo\" is 10\n// `foo` is 10\n\n// DO:\n// |foo| is 10\nsize_t foo = 10;\n```\n\n[Correlating Google C++ commenting rule here](https://google.github.io/styleguide/cppguide.html#Function_Comments)\n\n#### Greek Letters\n\nGreek letters are often used in explanations of mathematical processes in our comments. We use the \"Small\" version of Greek unicode letters to standardize and assist searching capabilities.\n\nFor example:\n\n- Use θ (U+03B8 -- \"Greek Small Letter Theta\")\n- Do not use Θ (U+0398 -- \"Greek Capital Letter Theta\")\n\n### Build Targets\n\nSince styling Bazel files is not specified in the Google C++ style guide, here are the rules we abide by:\n\n1. Order all `tachyon_cc_unittest` targets after all `tachyon_cc_library` targets.\n2. Order targets alphabetically within each section.\n3. There should be only one unittest target per Bazel file. Name the unittest target name following the pattern `{directory-name}_unittests`. An example would be `base_unittests`.\n\nRefer to [`tachyon/base/BUILD.bazel`](/tachyon/base/BUILD.bazel) as an example.\n\n## Logging\n\nWe use `VLOG` messages in our code to show us the progress of the current running processes. 3 levels are currently employed, though more can be created as needed. Here is what our 3 levels signify:\n\n1. `VLOG(1)` FYI - FOR YOUR INFORMATION\n\n    Provides insight to basic information of the current process. Examples include:\n  \n      - Proving schemes/elliptic curves/parameters currently in use\n      - Time between steps of processes\n      - Start and end times of processes\n\n2. `VLOG(2)` FOR DEBUGGING\n\n    Exposes inner progress of the current process for easier debugging. Examples include:\n\n      - Values of variables such as theta, beta, gamma, challenge, etc\n\n3. `VLOG(3)` FOR TRACING\n\n    Details outputs designed to help tracing. Examples include:\n\n      - Output of the proof\n      - Output of the verifying key serialization\n\n## Profiling with Perfetto\n\nWe are currently using Perfetto for tracing and profiling Tachyon. Perfetto provides two primary macros for tracing events: `TRACE_EVENT()` and `TRACE_EVENT_BEGIN()`|`TRACE_EVENT_END()`.\n\n- **`TRACE_EVENT()`**: Use this macro when you want the trace slice to end when the scope it is defined in ends. This is the preferred method whenever possible as it simplifies the code and ensures the trace slice duration is managed by the scope itself.\n- **`TRACE_EVENT_BEGIN()` and `TRACE_EVENT_END()`**: Use these macros when you need to manually specify the beginning and end of a trace slice. This approach is suitable for tracing code segments that are too long or complex for a single scope, where adding a scope would be impractical.\n\n### Trace Categories\n\nThe trace categories are defined in the [`profiler.h`](/tachyon/base/profiler.h) header. When defining trace names, avoid adding scope to the trace name whenever possible to decrease verbosity. Note that private functions that are always called from other functions with a scoped trace name do not need their own scoped trace names. Scoped trace names should only be used when two different scopes can be described with the same trace name.\n\n## Commits\n\n### Commit Type Classification\n\nDeciding the commit type can be difficult if the change appears to fall within the definitions of multiple different types. Here are further clarifications on how to determine the proper commit type given previous cases we have come across.\n\n- Any modifications made to files associated with the build process, such as `BUILD.bazel`, are categorized as a **build** commit. This classification includes changes, typo corrections, and formatting.\n- Commits that add or modify tests are classified as **test** commits, while commits that remove tests are considered **refac** commits.\n\nFor more information on our Commit Message Format, see [here](https://github.com/kroma-network/.github/blob/main/CONTRIBUTING.md#commit-message-format).\n\n## GPU\n\n### Naming\n\n- If your gpu-based programs can be run independently to GPU vendors, please name them `Gpu`, not `Cuda` or `ROCm`.\n\n### File Suffixes\n\n- If a file contains kernels, append suffixes `.cu.xx` even though it can be run on `ROCm`.\n- If a file contains device-only codes, append suffixes `.cu.xx` even though it can be run on `ROCm`.\n- If a file contains host and device codes, then suffixes are not appended.\n  See files under `tachyon/devices/gpu` for examples.\n"
  },
  {
    "path": "docs/how_to_contribute/math/how_to_add_elliptic_curves.md",
    "content": "# How to add elliptic curves\n\nFollow this guide to add a new elliptic curve for Tachyon.\n\n_Note_: We have our own development conventions. Please read the [conventions doc](/docs/how_to_contribute/conventions.md) before contributing.\n\n## Add a `BUILD.bazel` file\n\nBegin by creating a directory named `<new_elliptic_curve>` in `/tachyon/math/elliptic_curves/`. Add a `BUILD.bazel` file into this directory. Note that once parameters are added to `BUILD.bazel`, Bazel will automatically generate the elliptic curve code based on these parameters when it builds the target.\n\nNext, implement the *scalar field* and *base field* for which the elliptic curve is defined on. See [here](/docs/how_to_contribute/math/how_to_add_prime_field.md) for how to add a *prime field*.\n\nAfter implementing the *fields*, input the parameters of the elliptic curve into the generator(`generate_ec_points`) like shown below:\n\n```bazel\nload(\"//tachyon/math/elliptic_curves/short_weierstrass/generator:build_defs.bzl\", \"generate_ec_points\")\n\ngenerate_ec_points(\n    name = \"curve\",\n    # y² = x³ + ax + b\n    a = [\"{a}\"],\n    b = [\"{b}\"],\n    base_field_degree = 1,\n    base_field = \"Fq\",\n    base_field_dep = \":fq\",\n    base_field_hdr = \"tachyon/math/elliptic_curves/<new_elliptic_curve>/fq.h\",\n    gen_gpu = True, # False if the GPU code is not needed\n    namespace = \"tachyon::math::<new_elliptic_curve>\",\n    scalar_field = \"Fr\",\n    scalar_field_dep = \":fr\",\n    scalar_field_hdr = \"tachyon/math/elliptic_curves/<new_elliptic_curve>/fr.h\",\n    x = [\"{x}\"], # x-coordinate of the generator\n    y = [\"{y}\"], # y-coordinate of the generator\n)\n```\n\n_Note_: As of now, only elliptic curves in Short Weierstrass form are supported.\n\nOnce all these steps are completed, the new elliptic curve is now ready to be used in Tachyon!\n"
  },
  {
    "path": "docs/how_to_contribute/math/how_to_add_prime_fields.md",
    "content": "# How to add prime fields\n\nFollow this guide to add a new prime field for Tachyon.\n\n_Note_: We have our own development conventions. Please read the [conventions doc](/docs/how_to_contribute/conventions.md) before contributing.\n\n## 1. Add a `BUILD.bazel` file\n\nBegin by creating a directory named `<new_prime_field>` in `/tachyon/math/finite_field/`. Add a `BUILD.bazel` file into this directory. Note that once parameters are added to `BUILD.bazel`, Bazel will automatically generate the prime field code based on these parameters when it builds the target.\n\n### Prime field generator\n\nTachyon currently supports 2 kinds of prime fields.\n\n- `Small Prime Field`: This prime field is used in the STARK protocol. It is generated when the modulus bit size is smaller than 32 bits. The config is generated based on [small_prime_field_config.h.tpl](/tachyon/math/finite_fields/generator/prime_field_generator/small_prime_field_config.h.tpl). If the prime field uses Montgomery Reduction, it is instantiated by [small_prime_field_mont.h](/tachyon/math/finite_fields/small_prime_field_mont.h); otherwise, it is instantiated by [small_prime_field.h](/tachyon/math/finite_fields/small_prime_field.h). The prime field for GPU is the same as the one for the CPU.\n- `EC(Elliptic Curve) Based Prime Field`: This prime field is used in the SNARK protocol. It is generated when the modulus bit size is greater than or equal to 64 bits. The config is generated based on [prime_field_config.h.tpl](/tachyon/math/finite_fields/generator/prime_field_generator/prime_field_config.h.tpl). For x86, the prime field for the CPU is generated based on [prime_field_x86.h.tpl](/tachyon/math/finite_fields/generator/prime_field_generator/prime_field_x86.h.tpl); otherwise, it is generated by [prime_field_fallback.h](/tachyon/math/finite_fields/prime_field_fallback.h). The prime field for the GPU is instantiated by [prime_field_gpu.h](/tachyon/math/finite_fields/prime_field_gpu.h).\n\nChoose the [prime field generator](/tachyon/math/finite_fields/generator/prime_field_generator/build_defs.bzl) depending on the prime field type. The list of required properties for each prime field generator is included below. Note that each successive field type in this list additionally requires all properties mentioned in previous generator types.\n\n1. **`generate_prime_fields()`**:\n   - `name`: The name of the prime field.\n   - `namespace`: The selected namespace, commonly set as `tachyon::math`.\n   - `class_name`: The name of the class, usually the same as the name of the prime field (in PascalCase).\n   - `modulus`: The modulus value of the prime field.\n   - `reduce32`: If the prime field uses a special reduction function, this property holds the string version of the code which overrides the default 32-bit reduction function.\n   - `reduce64`: If the prime field uses a special reduction function, this property holds the string version of the code which overrides the default 64-bit reduction function.\n   - `use_asm`: This flag indicates whether assembly is used. If `ffiasm` fails to generate the assembly code, setting this flag to false is recommended.\n   - `use_montgomery`: This flag indicates whether the prime field uses Montgomery Reduction.\n2. **`generate_fft_prime_fields()`**:\n   - `subgroup_generator`: The value used to generate elements of a subgroup within the prime field, facilitating Fast Fourier Transform (FFT) operations.\n     - _Note_: Every prime can be expressed in the following form: $p = 2^s * T + 1$, where $s$ and $T$ are integers, and $T$ is odd. According to Fermat's little Theorem, $a^{p-1} = 1 \\mod p$ holds for any element $a$ in $F_p$. That is to say, $a^{2^s * T} = 1 \\mod p$. A subgroup generator $g$ satisfies $(g^{2^{s-k} * T})^{2^k} = 1 \\mod p$ for some $k \\le s$, where $2^k = n$, making $\\omega = g^{2^{s-k} * T}$ a $n$-th root of unity.\n3. **`generate_large_fft_prime_fields()`**:\n   - `small_subgroup_base`: Refers to the base element $b$ of a small subgroup.\n   - `small_subgroup_adicity`: Refers to the largest adicity $a$ (the exponent of the base) of a small subgroup such that $b^a$ is a divisor of $T$.\n     - _Note_: Large FFT prime fields can construct a larger domain than FFT prime fields through a small subgroup. Given `small_subgroup_base` = $b$ and `small_subgroup_adicity` = $a$, if $b^a$ is a divisor of $T$ and $g^{2^s * T} = 1 \\mod p$, then $(g^{T/b^a})^{2^s*b^a} = 1 \\mod p$. This manipulation enables a domain size up to $b^a$ times larger than the maximum domain size of $2^s$ that general FFT prime fields can create. Additionally, the $n$-th root of unity is now $\\omega = g^{2^{s-k} * T/b^{a-q}}$, where $n = 2^k * b^{a-q} (k \\le s, q \\le a)$ since $\\omega$ satisfies $\\omega^{2^k * b^{a-q}} = (g^{2^{s-k} * T/b^{a-q}})^{2^k * b^{a-q}} = 1 \\mod p$.\n\nFor instance, to implement an FFT prime field, create a directory (`/tachyon/math/finite_fields/<new_prime_field>`) and add a `BUILD.bazel` file as shown below:\n\n```bazel\n# /tachyon/math/finite_fields/<new_prime_field>/BUILD.bazel\n\nload(\"@bazel_skylib//rules:common_settings.bzl\", \"string_flag\")\nload(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\nload(\n    \"//tachyon/math/finite_fields/generator/prime_field_generator:build_defs.bzl\",\n    \"SUBGROUP_GENERATOR\",\n    \"generate_fft_prime_fields\", # NOTE: Choose generator type\n)\n\npackage(default_visibility = [\"//visibility:public\"])\n\nstring_flag(\n    name = SUBGROUP_GENERATOR,\n    build_setting_default = \"{subgroup_generator}\", # input Subgroup generator value\n)\n\ngenerate_fft_prime_fields( # NOTE: Choose generator type\n    name = \"new_prime_field\",\n    namespace = \"tachyon::math\",\n    class_name = \"NewPrimeField\",\n    modulus = \"{modulus_value}\", # input modulus value\n    subgroup_generator = \":\" + SUBGROUP_GENERATOR,\n)\n```\n\nUse the following files for reference:\n\n- [Goldilocks `BUILD.bazel`](/tachyon/math/finite_fields/goldilocks/BUILD.bazel)\n- [Mersenne31 `BUILD.bazel`](/tachyon/math/finite_fields/mersenne31/BUILD.bazel)\n\n## 2. Add to `prime_field_generator_unittest.cc`\n\nFinally, ensure the prime field works well by incorporating it into `prime_field_generator_unittest.cc` at the two points shown below:\n\n```cpp\n...\n#include \"tachyon/math/finite_fields/goldilocks/goldilocks.h\"\n#include \"tachyon/math/finite_fields/mersenne31/mersenne31.h\"\n// ADD NEW PRIME FIELD HEADER FILE HERE\n// #include \"tachyon/math/finite_fields/...\"\n...\n// ADD NEW PRIME FIELD NAME HERE\nusing PrimeFieldTypes =\n    testing::Types<bls12_381::Fq, bls12_381::Fr, bn254::Fq, bn254::Fr,\n                   secp256k1::Fr, secp256k1::Fq, Goldilocks, Mersenne31 /*, NEW PRIME FIELD*/>;\n...\n```\n"
  },
  {
    "path": "docs/how_to_contribute/math/math_structure.md",
    "content": "# Math Structure\n\nThis is an abstract diagram of the **math** directory structure.\n\n![image](/docs/how_to_contribute/math/math_structure.png)\n"
  },
  {
    "path": "docs/how_to_use/how_to_build.md",
    "content": "# How to Build\n\nYou can build Tachyon with this guide. The instructions below will help you set up your environment, install necessary dependencies, and compile your projects efficiently. Follow along to start building with confidence.\n\n## Prerequisites\n\n### Bazel\n\nFollow the installation instructions for Bazel [here](https://bazel.build/install).\n\n### Requirements for each OS\n\n#### Ubuntu\n\n```shell\nsudo apt install libgmp-dev libomp-dev zip\n```\n\n#### MacOS\n\n```shell\nbrew install gmp\n```\n\n### Python dependencies\n\n#### Python\n\n- Install [Python](https://www.python.org/downloads/). Python **v3.10.12** is recommended.\n\n- If you are using pyenv, don't forget to add an option `--enable-shared`.\n\n  ```shell\n  CONFIGURE_OPTS=--enable-shared pyenv install <version>\n  ```\n\n#### Matplotlib(optional)\n\nIf you want to print a benchmark plot, you need to install matplotlib. See [here](/benchmark/README.md) for more details.\n\n```shell\npip install matplotlib\n```\n\n**_NOTE_:**\n\n- To build `mac_logging` in MacOS, you need to install objc, which can be done by installing XCode.\n\n- MacOS v14.0.0 or later is recommended. In certain versions of MacOS (prior to v13.5.1), a bug related to incorrect `BigInt` divide operations has been detected in the field generator when using the optimized build (`-c opt`). This [issue](https://github.com/kroma-network/tachyon/issues/98) will be fixed as soon as possible.\n\n## Build from source\n\n### Build\n\n```shell\nbazel build //...\n```\n\n### Test\n\n```shell\nbazel test //...\n```\n\n### Build options\n\n- `opt`: Default optimized build option\n- `dbg`: Build with debug info\n- `fastbuild`: Fast build option\n\n```shell\nbazel build --config dbg //...\n```\n\n### Hardware acceleration\n\n#### CUDA backend\n\n- `--config cuda`: Enable [cuda] backend.\n\n  ```shell\n  bazel build --config cuda //...\n  ```\n\n#### ROCm backend\n\n- `--config rocm`: Enable [rocm] backend.\n\n  ```shell\n  bazel build --config rocm //...\n  ```\n\n_NOTE_: The `rocm` option is not recommended for current use because it is not being tested yet.\n\n[cuda]: https://developer.nvidia.com/cuda-toolkit\n[rocm]: https://www.amd.com/en/graphics/servers-solutions-rocm\n\n### .bazelrc.user\n\nBuild options can be preset in `.bazelrc.user` for your convenience, eliminating the need to specify them on the command line.\n\nFor example:\n\n```\n# .bazelrc.user\n\nbuild --config dbg\n```\n\n```shell\nbazel build //...\n# With the preset options in .bazelrc.user, this is the same as:\n# bazel build --config dbg //...\n```\n\n## Building Tachyon from a Bazel repository\n\nTachyon can be built in your own Bazel project with the following two simple steps.\n\nFirst, obtain the Tachyon code from a specific commit hash and get a SHA256 value from the fetched code through these commands:\n\n```shell\nwget https://github.com/kroma-network/tachyon/archive/e02feac98608a6bb1efc75df07237f702db7cce7.tar.gz\n\nshasum -a 256 e02feac98608a6bb1efc75df07237f702db7cce7.tar.gz\n```\n\nSecond, input the shasum output into your `WORKSPACE` file as the `sha256` argument like shown below:\n\n```bzl\n# WORKSPACE\n\nload(\"@bazel_tools//tools/build_defs/repo:http.bzl\", \"http_archive\")\nhttp_archive(\n    name = \"kroma_network_tachyon\",\n    sha256 = \"e3bfefb20bcb425a5169c46cf0bd4dacbdeaceab94b144a3d5fa4214d9e974d2\",\n    strip_prefix = \"tachyon-e02feac98608a6bb1efc75df07237f702db7cce7\",\n    urls = [\"https://github.com/kroma-network/tachyon/archive/e02feac98608a6bb1efc75df07237f702db7cce7.tar.gz\"],\n)\n```\n\n## Debian packaging\n\nThere are two ways to install the Tachyon package. While it is recommended to install the package from the prebuilt binaries, building the package from the source is also viable if needed.\n\n### Install package from pre-built binaries\n\n```shell\ncurl -LO https://github.com/kroma-network/tachyon/releases/download/v0.4.0/libtachyon_0.4.0_amd64.deb\ncurl -LO https://github.com/kroma-network/tachyon/releases/download/v0.4.0/libtachyon-dev_0.4.0_amd64.deb\n\nsudo dpkg -i libtachyon_0.4.0_amd64.deb\nsudo dpkg -i libtachyon-dev_0.4.0_amd64.deb\n```\n\n### Build package from source\n\nBuild a Debian package with the supported scheme (only halo2 for now) and the options you want.\nTo build the Halo2 Debian package, the `has_openmp` option is recommended. Run the following commands:\n\n```shell\nbazel build --config opt --//:c_shared_object //scripts/packages/debian/runtime:debian\nbazel build --config opt --//:c_shared_object //scripts/packages/debian/dev:debian\n\nsudo dpkg -i bazel-bin/scripts/packages/debian/runtime/libtachyon_0.4.0_amd64.deb\nsudo dpkg -i bazel-bin/scripts/packages/debian/dev/libtachyon-dev_0.4.0_amd64.deb\n```\n\n## Other Info\n\n### Debugging on macOS\n\nPlease add this line to your `.bazelrc.user`.\n\n```\nbuild --spawn_strategy=local\n```\n\n### Control build && test\n\nAs you can see in [.bazelrc](/.bazelrc), we don't enable all of the build and test targets. To enable additional targets, you may add customized `--build_tag_filters` and `--test_tag_filters` to your `.bazelrc.user` file.\n\nFor example, add the following to `.bazelrc.user` to build rust on linux:\n\n```\nbuild --build_tag_filters -objc,-cuda\n```\n\n### Build Rust code\n\nSince the current toolchain is using the nightly channel, the following build flag is needed. Add this to your `.bazelrc.user`.\n\n```\nbuild --@rules_rust//rust/toolchain/channel=nightly\n```\n\n### Build SP1 Rust code\n\nSince [scale-info](https://crates.io/crates/scale-info) needs a `CARGO` environment variable, add this to your `.bazelrc.user`.\n\n```\nbuild --action_env=CARGO=<path to cargo>\n```\n\n### Py Binding\n\n`ModuleNotFoundError` may occur in certain python versions (v3.11.6). Python v3.10.12 is recommended.\n\n### Build on Ubuntu 20.04\n\nIf you are using Ubuntu 20.04, update your g++ version. The default `g++-9` does not work.\n\n```shell\nsudo apt install g++-10\nexport CC=/usr/bin/gcc-10\nexport CXX=/usr/bin/g++-10\nexport GCC_HOST_COMPILER_PATH=/usr/bin/gcc-10\n```\n\n### Building with Clang on Ubuntu 22.04\n\nTo utilize features like [always_inline](https://releases.llvm.org/15.0.0/tools/clang/docs/AttributeReference.html#always-inline-force-inline),\nyou'll need clang++ at version 15 or higher. By default, Ubuntu 22.04 installs `clang-14` via `apt install clang`,\nso you'll need to manually install a newer version of clang and update `openmp` accordingly.\n\nFirst, install `clang-15` and the appropriate OpenMP libraries:\n\n```shell\nsudo apt install clang-15 libomp5-15 libomp-15-dev\n```\n\nNext, update `CC` and `CXX` to point to the newly installed `clang-15` and `clang++-15`:\n\n```shell\nexport CC=/usr/bin/clang-15\nexport CXX=/usr/bin/clang++-15\n```\n\nMake sure `CC` and `CXX` are properly updated to `clang-15` and `clang++-15`.\nThese commands ensure that your build environment uses clang version 15 for compilation.\n\n### Build CUDA\n\n#### Set Action Environment Variable for Python\n\nYou may run into the following problem:\n\n```shell\nUse --sandbox_debug to see verbose messages from the sandbox and retain the sandbox build root for debugging\nerror: linking with `external/local_config_cuda/crosstool/clang/bin/crosstool_wrapper_driver_is_not_gcc` failed: exit status: 127\n...\n  = note: /usr/bin/env: 'python': No such file or directory\n```\n\nIf you install `python` through [Anaconda](https://www.anaconda.com/), please include these lines in your `.bazelc.user`.\n\n```\nbuild:cuda --action_env=PATH=/opt/conda/bin\nbuild:cuda --host_action_env=PATH=/opt/conda/bin\n```\n\nOtherwise, install `python3` and make `python` point to it.\n\n```shell\nsudo apt install python3\nsudo apt install python-is-python3\n```\n\nAdditionally, please include these lines in your `.bazelc.user`.\n\n```\nbuild:cuda --action_env=PATH=/usr/bin:/usr/local/bin\nbuild:cuda --host_action_env=PATH=/usr/bin:/usr/local/bin\n```\n\n#### Update CUDA [Compute Capabilities](https://developer.nvidia.com/cuda-gpus)\n\nYou may run into the following problem:\n\n```shell\nUse --sandbox_debug to see verbose messages from the sandbox and retain the sandbox build root for debugging\nnvcc fatal   : Unsupported gpu architecture 'compute_35'\n```\n\nTo solve this, please include these lines in your `.bazelc.user`.\n\n```\nbuild:cuda --action_env=TACHYON_CUDA_COMPUTE_CAPABILITIES=\"compute_52\"\n```\n\n### Generate C API documents using Doxygen\n\nDoxygen generates C API documents (`tachyon_api_docs.zip`) in the binary directory (`bazel-bin/docs/doxygen/`). Currently this feature is available only on Linux.\n\n```shell\nbazel build //docs/doxygen:generate_docs &&\nunzip -o bazel-bin/docs/doxygen/tachyon_api_docs.zip &&\ngoogle-chrome bazel-bin/docs/doxygen/html/index.html\n# generate HTML files and open on Chrome browser.\n```\n\n### Build without assembly-optimized prime field\n\nYou may encounter an illegal instruction error when running unit tests. Although the exact cause is not yet known, thanks to @zkbitcoin, we discovered that this issue is related to [ffiasm](https://github.com/iden3/ffiasm). Temporarily disabling assembly-optimized prime field can resolve this problem.\n\n```shell\nbazel build --//:has_asm_prime_field=false //...\n```\n\n## Performance Tuning\n\n### Visualizing and Profiling Traces\n\nTachyon utilizes [Perfetto](https://perfetto.dev/) for low-overhead profiling. You can visualize the generated trace by\nuploading it to the [Perfetto Trace Viewer](https://ui.perfetto.dev/). Typically, our traces are generated in the `/tmp` directory with a `perfetto-trace` extension.\n\n### Use Intel OpenMP Runtime Library(libiomp)\n\nBy default, Tachyon uses GNU OpenMP (GNU `libgomp`) for parallel computation. On Intel platforms, Intel OpenMP Runtime Library (`libiomp`) provides OpenMP API specification support. It sometimes brings more performance benefits compared to `libgomp`.\n\nYou can install `libomp` by following the [instructions](https://www.intel.com/content/www/us/en/developer/tools/oneapi/hpc-toolkit-download.html).\n\nTo link `libomp`, you need to add an additional flag `--//:has_intel_openmp`.\n\n```shell\nbazel build --//:has_openmp --//:has_intel_openmp //...\n```\n\nSee also [PyTorch Recipes/Performance Tuning Guide/Intel OpenMP Library](https://pytorch.org/tutorials/recipes/recipes/tuning_guide.html#intel-openmp-runtime-library-libiomp).\n"
  },
  {
    "path": "funding.json",
    "content": "{\n  \"opRetro\": {\n    \"projectId\": \"0x8c76c13d8d0e63a7de499d47b9da5a4495d1151c0b2003c92379f41f14e404c0\"\n  }\n}\n"
  },
  {
    "path": "scripts/packages/BUILD.bazel",
    "content": "sh_binary(\n    name = \"install_package\",\n    srcs = select({\n        \"@platforms//os:windows\": [],\n        \"//conditions:default\": [\"install_package.sh\"],\n    }),\n    data = [\n        \"copy_hdr.py\",\n        \"//tachyon/c:tachyon\",\n        \"//tachyon/c:tachyon_hdrs\",\n    ],\n)\n"
  },
  {
    "path": "scripts/packages/copy_hdr.py",
    "content": "#!/usr/bin/env python3\n\nimport argparse\nimport os\nimport shutil\nimport sys\n\n\ndef main():\n    argument_parser = argparse.ArgumentParser()\n    argument_parser.add_argument('file', help='The file to replace with')\n    argument_parser.add_argument('dst', help='The destination where the file moves to')\n    argument_parser.add_argument('--strip', help='The include path to be stripped')\n    args = argument_parser.parse_args()\n\n    dst_file = args.file\n    if len(args.strip) > 0:\n        dst_file = dst_file[len(args.strip):]\n\n    dst_file = os.path.join(args.dst, dst_file)\n    os.makedirs(os.path.dirname(dst_file), exist_ok=True)\n    shutil.copyfile(args.file, dst_file)\n\n    return 0\n\n\nif __name__ == '__main__':\n    sys.exit(main())\n"
  },
  {
    "path": "scripts/packages/debian/BUILD.bazel",
    "content": "exports_files([\n    \"copyright\",\n    \"description\",\n])\n"
  },
  {
    "path": "scripts/packages/debian/copyright",
    "content": "Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/\nUpstream-Name: tachyon\nSource: https://github.com/kroma-network/tachyon\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\nTachyon bundled with software with the following license:\n\nLicense: 3-clause BSD\nLicense: Apache License 2.0\n\n# Please refer to README.md under third_party/ directory to see which license is used by specific part of code.\n"
  },
  {
    "path": "scripts/packages/debian/debian.bzl",
    "content": "HOMEPAGE = \"https://github.com/kroma-network/tachyon\"\nMAINTAINER = \"The Tachyon Authors <tachyon-discuss@kroma.network>\"\n"
  },
  {
    "path": "scripts/packages/debian/description",
    "content": "Tachyon is a blazing fast Zero-Knowledge Proof (ZKP) library developed in C++.\n"
  },
  {
    "path": "scripts/packages/debian/dev/BUILD.bazel",
    "content": "load(\"@bazel_skylib//rules:expand_template.bzl\", \"expand_template\")\nload(\"@rules_pkg//pkg:deb.bzl\", \"pkg_deb\")\nload(\"@rules_pkg//pkg:tar.bzl\", \"pkg_tar\")\nload(\"//scripts/packages/debian:debian.bzl\", \"HOMEPAGE\", \"MAINTAINER\")\nload(\"//tachyon/c:version.bzl\", \"VERSION\")\n\nexpand_template(\n    name = \"pkg_config\",\n    out = \"tachyon.pc\",\n    substitutions = {\n        \"%{version}\": VERSION,\n    },\n    template = \"tachyon.pc.tpl\",\n)\n\npkg_tar(\n    name = \"debian_data\",\n    extension = \"tar.gz\",\n    deps = [\n        \":debian_hdrs\",\n        \":debian_lib\",\n        \":debian_pkgconfig\",\n    ],\n)\n\npkg_tar(\n    name = \"debian_hdrs\",\n    srcs = [\"//tachyon/c:tachyon_hdrs\"],\n    extension = \"tar.gz\",\n    remap_paths = {\n        \"\": \"./usr/include/\",\n    },\n    strip_prefix = \".\",\n)\n\npkg_tar(\n    name = \"debian_lib\",\n    extension = \"tar.gz\",\n    symlinks = {\n        \"./usr/lib/x86_64-linux-gnu/libtachyon.so\": \"libtachyon.so.%s\" % VERSION,\n    },\n)\n\npkg_tar(\n    name = \"debian_pkgconfig\",\n    srcs = [\":pkg_config\"],\n    extension = \"tar.gz\",\n    remap_paths = {\n        \"\": \"./usr/lib/x86_64-linux-gnu/pkgconfig\",\n    },\n)\n\npkg_deb(\n    name = \"debian\",\n    architecture = \"amd64\",\n    built_using = \"bazel\",\n    data = \":debian_data\",\n    depends = [\n        \"libgmp10-dev\",\n        \"libtachyon\",\n    ],\n    description_file = \"//scripts/packages/debian:description\",\n    homepage = HOMEPAGE,\n    license = \"MIT\",\n    maintainer = MAINTAINER,\n    package = \"libtachyon-dev\",\n    section = \"contrib/devel\",\n    version = VERSION,\n)\n"
  },
  {
    "path": "scripts/packages/debian/dev/tachyon.pc.tpl",
    "content": "prefix=/usr\nexec_prefix=${prefix}\nincludedir=${prefix}/include\nlibdir=/usr/lib/x86_64-linux-gnu\n\nName: Tachyon\nDescription: Tachyon is a blazing fast Zero-Knowledge Proof (ZKP) library developed in C++.\nURL: https://github.com/kroma-network/tachyon\nVersion: %{version}\nCflags: -I${includedir}\nLibs: -L${libdir} -ltachyon\n"
  },
  {
    "path": "scripts/packages/debian/runtime/BUILD.bazel",
    "content": "load(\"@rules_pkg//pkg:deb.bzl\", \"pkg_deb\")\nload(\"@rules_pkg//pkg:tar.bzl\", \"pkg_tar\")\nload(\"//scripts/packages/debian:debian.bzl\", \"HOMEPAGE\", \"MAINTAINER\")\nload(\"//tachyon/c:version.bzl\", \"VERSION\", \"VERSION_MAJOR\")\n\npkg_tar(\n    name = \"debian_data\",\n    extension = \"tar.gz\",\n    deps = [\n        \":debian_lib\",\n        \":debian_share\",\n    ],\n)\n\npkg_tar(\n    name = \"debian_lib\",\n    srcs = [\"//tachyon/c:tachyon\"],\n    extension = \"tar.gz\",\n    remap_paths = {\n        \"\": \"./usr/lib/x86_64-linux-gnu/\",\n    },\n    symlinks = {\n        \"./usr/lib/x86_64-linux-gnu/libtachyon.so.%s\" % VERSION_MAJOR: \"libtachyon.so.%s\" % VERSION,\n    },\n)\n\npkg_tar(\n    name = \"debian_share\",\n    srcs = [\"//scripts/packages/debian:copyright\"],\n    extension = \"tar.gz\",\n    remap_paths = {\n        \"\": \"./usr/share/doc/libtachyon%s/\" % VERSION_MAJOR,\n    },\n)\n\npkg_deb(\n    name = \"debian\",\n    architecture = \"amd64\",\n    built_using = \"bazel\",\n    data = \":debian_data\",\n    depends = [\n        \"libgmp10\",\n        \"libgmpxx4ldbl\",\n    ],\n    description_file = \"//scripts/packages/debian:description\",\n    homepage = HOMEPAGE,\n    license = \"MIT\",\n    maintainer = MAINTAINER,\n    package = \"libtachyon\",\n    section = \"contrib/lib\",\n    version = VERSION,\n)\n"
  },
  {
    "path": "scripts/packages/install_package.sh",
    "content": "#!/usr/bin/env bash\n\nset -e\n\nfunction copy_and_link_lib_linux() {\n  LIB=$1\n  BASENAME=$(basename $LIB)\n\n  LIBNAME=$(echo $BASENAME | awk -F. '{print $(NF - 4)}')\n  MAJOR=$(echo $BASENAME | awk -F. '{print $(NF - 2)}')\n  MINOR=$(echo $BASENAME | awk -F. '{print $(NF - 1)}')\n  PATCH=$(echo $BASENAME | awk -F. '{print $(NF)}')\n\n  LINK_LIB=${LIBNAME}.so\n  MAJOR_LIB=${LIBNAME}.so.${MAJOR}\n  FULL_LIB=${BASENAME}\n\n  cp $LIB ${DSTDIR}/lib\n  ln -sf ${DSTDIR}/lib/${FULL_LIB} ${DSTDIR}/lib/${MAJOR_LIB}\n  ln -sf ${DSTDIR}/lib/${MAJOR_LIB} ${DSTDIR}/lib/${LINK_LIB}\n}\n\nfunction copy_and_link_lib_macos() {\n  LIB=$1\n  BASENAME=$(basename $LIB)\n\n  LIBNAME=$(echo $BASENAME | awk -F. '{print $(NF - 4)}')\n  MAJOR=$(echo $BASENAME | awk -F. '{print $(NF - 3)}')\n  MINOR=$(echo $BASENAME | awk -F. '{print $(NF - 2)}')\n  PATCH=$(echo $BASENAME | awk -F. '{print $(NF - 1)}')\n\n  LINK_LIB=${LIBNAME}.dylib\n  MAJOR_LIB=${LIBNAME}.${MAJOR}.dylib\n  FULL_LIB=${BASENAME}\n\n  cp $LIB ${DSTDIR}/lib\n  ln -sf ${DSTDIR}/lib/${FULL_LIB} ${DSTDIR}/lib/${MAJOR_LIB}\n  ln -sf ${DSTDIR}/lib/${MAJOR_LIB} ${DSTDIR}/lib/${LINK_LIB}\n}\n\nfunction install_package() {\n  mkdir -p ${DSTDIR}/include ${DSTDIR}/lib\n  RUNFILES=bazel-bin/scripts/packages/install_package.runfiles/kroma_network_tachyon\n\n  FILES=`find ${RUNFILES}/tachyon -type l`\n  echo \"Copying libs to ${DSTDIR}/lib\"\n  for FILE in $FILES\n  do\n    if [[ \"$FILE\" =~ .*so.[[:digit:]].[[:digit:]].[[:digit:]] ]]; then\n      copy_and_link_lib_linux $FILE\n    elif [[ \"$FILE\" =~ .*.[[:digit:]].[[:digit:]].[[:digit:]].dylib ]]; then\n      copy_and_link_lib_macos $FILE\n    fi\n  done\n  echo \"Done copying libs to ${DSTDIR}/lib\"\n\n  FILES=`find ${RUNFILES}/tachyon -name \"*.h\"`\n  echo \"Copying headers to ${DSTDIR}/include\"\n  for FILE in $FILES\n  do\n    ${RUNFILES}/scripts/packages/copy_hdr.py ${FILE} ${DSTDIR}/include --strip ${RUNFILES}/\n  done\n  echo \"Done copying headers to ${DSTDIR}/include\"\n}\n\nfunction usage() {\n  echo \"Usage:\"\n  echo \"$0 [OPTION]...\"\n  echo \"\"\n  echo \"-h, --help                      help\"\n  echo \"-d, --dst                       absolute path to install. default: /opt/kroma/tachyon\"\n  echo \"\"\n}\n\nfunction main() {\n  DSTDIR=\"/opt/kroma/tachyon\"\n  while [[ $# -gt 0 ]]\n  do\n  case $1 in\n    -h | --help)\n    usage\n    exit 0\n    ;;\n    -d | --dst)\n    DSTDIR=\"$2\"\n    shift\n    shift\n    ;;\n    *)\n    echo \"Unknown option: $1\"\n    usage\n    exit 1\n    ;;\n  esac\n  done\n\n  install_package\n}\n\nmain \"$@\"\n"
  },
  {
    "path": "tachyon/BUILD.bazel",
    "content": "load(\n    \"//bazel:tachyon.bzl\",\n    \"VERSION_MAJOR\",\n    \"VERSION_MINOR\",\n    \"VERSION_PATCH\",\n    \"VERSION_PRERELEASE\",\n)\nload(\n    \"//bazel:tachyon_cc.bzl\",\n    \"tachyon_cc_library\",\n    \"tachyon_cc_unittest\",\n)\nload(\"//tachyon/build:version.bzl\", \"write_version_header\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nwrite_version_header(\n    name = \"version_generated\",\n    lastchange = \"//tachyon/build:lastchange\",\n    major = VERSION_MAJOR,\n    minor = VERSION_MINOR,\n    output = \"version_generated.h\",\n    patch = VERSION_PATCH,\n    prerelease = VERSION_PRERELEASE,\n    project = \"TACHYON\",\n)\n\ntachyon_cc_library(\n    name = \"export\",\n    hdrs = [\"export.h\"],\n)\n\ntachyon_cc_library(\n    name = \"version\",\n    srcs = [\"version.cc\"],\n    hdrs = [\n        \"version.h\",\n        \":version_generated\",\n    ],\n    deps = [\":export\"],\n)\n\ntachyon_cc_unittest(\n    name = \"tachyon_unittests\",\n    srcs = [\n        \"version_unittest.cc\",\n    ],\n    deps = [\n        \":version\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/base/BUILD.bazel",
    "content": "load(\"//bazel:tachyon.bzl\", \"if_has_openmp_on_macos\", \"if_posix\")\nload(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_benchmark\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"auto_reset\",\n    hdrs = [\"auto_reset.h\"],\n    deps = [\":logging\"],\n)\n\ntachyon_cc_library(\n    name = \"bit_cast\",\n    hdrs = [\"bit_cast.h\"],\n    deps = [\":compiler_specific\"],\n)\n\ntachyon_cc_library(\n    name = \"bits\",\n    srcs = [\"bits.cc\"],\n    hdrs = [\"bits.h\"],\n    deps = [\n        \":compiler_specific\",\n        \":logging\",\n        \"//tachyon/build:build_config\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"compiler_specific\",\n    hdrs = [\"compiler_specific.h\"],\n    deps = [\"//tachyon/build:build_config\"],\n)\n\ntachyon_cc_library(\n    name = \"cxx20_is_constant_evaluated\",\n    hdrs = [\"cxx20_is_constant_evaluated.h\"],\n)\n\ntachyon_cc_library(\n    name = \"endian\",\n    srcs = [\"endian.cc\"],\n    hdrs = [\"endian.h\"],\n    deps = [\":logging\"],\n)\n\ntachyon_cc_library(\n    name = \"endian_utils\",\n    hdrs = [\"endian_utils.h\"],\n    deps = [\"//tachyon/build:build_config\"],\n)\n\ntachyon_cc_library(\n    name = \"environment\",\n    srcs = if_posix([\n        \"environment_posix.cc\",\n    ]),\n    hdrs = [\"environment.h\"],\n    deps = [\"//tachyon:export\"],\n)\n\ntachyon_cc_library(\n    name = \"immediate_crash\",\n    hdrs = [\"immediate_crash.h\"],\n    deps = [\"//tachyon/build:build_config\"],\n)\n\ntachyon_cc_library(\n    name = \"logging\",\n    srcs = [\"logging.cc\"],\n    hdrs = [\"logging.h\"],\n    deps = [\n        \"//tachyon:export\",\n        \"@com_github_google_glog//:glog\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"maybe_owned\",\n    hdrs = [\"maybe_owned.h\"],\n    deps = [\":maybe_owned_traits\"],\n)\n\ntachyon_cc_library(\n    name = \"maybe_owned_traits\",\n    hdrs = [\"maybe_owned_traits.h\"],\n    deps = [\"@com_google_absl//absl/types:span\"],\n)\n\ntachyon_cc_library(\n    name = \"no_destructor\",\n    hdrs = [\"no_destructor.h\"],\n)\n\ntachyon_cc_library(\n    name = \"openmp_util\",\n    hdrs = [\"openmp_util.h\"],\n    deps = if_has_openmp_on_macos([\"@local_config_omp//:omp\"]),\n)\n\ntachyon_cc_library(\n    name = \"optional\",\n    hdrs = [\"optional.h\"],\n    deps = [\":logging\"],\n)\n\ntachyon_cc_library(\n    name = \"parallelize\",\n    hdrs = [\"parallelize.h\"],\n    deps = [\n        \":openmp_util\",\n        \"//tachyon/base/functional:functor_traits\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"profiler\",\n    srcs = [\"profiler.cc\"],\n    hdrs = [\"profiler.h\"],\n    deps = [\n        \"//tachyon:export\",\n        \"//tachyon/base/files:file\",\n        \"@perfetto\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"random\",\n    srcs = [\"random.cc\"],\n    hdrs = [\"random.h\"],\n    deps = [\n        \":logging\",\n        \":range\",\n        \"//tachyon:export\",\n        \"@com_google_absl//absl/algorithm:container\",\n        \"@com_google_absl//absl/numeric:int128\",\n        \"@com_google_absl//absl/random\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"range\",\n    hdrs = [\"range.h\"],\n    deps = [\"@com_google_absl//absl/strings\"],\n)\n\ntachyon_cc_library(\n    name = \"ref\",\n    hdrs = [\"ref.h\"],\n    deps = [\"@com_google_absl//absl/hash\"],\n)\n\ntachyon_cc_library(\n    name = \"scoped_generic\",\n    hdrs = [\"scoped_generic.h\"],\n    deps = [\":logging\"],\n)\n\ntachyon_cc_library(\n    name = \"sort\",\n    hdrs = [\"sort.h\"],\n    deps = [\n        \"@pdqsort\",\n        \"@powersort\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"static_storage\",\n    hdrs = [\"static_storage.h\"],\n    deps = [\":no_destructor\"],\n)\n\ntachyon_cc_library(\n    name = \"template_util\",\n    hdrs = [\"template_util.h\"],\n)\n\ntachyon_cc_library(\n    name = \"type_list\",\n    hdrs = [\"type_list.h\"],\n)\n\ntachyon_cc_benchmark(\n    name = \"sort_benchmark\",\n    srcs = [\"sort_benchmark.cc\"],\n    deps = [\n        \"//tachyon/base:random\",\n        \"//tachyon/base:sort\",\n        \"//tachyon/base/containers:container_util\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"base_unittests\",\n    srcs = [\n        \"auto_reset_unittest.cc\",\n        \"bit_cast_unittest.cc\",\n        \"bits_unittest.cc\",\n        \"cxx20_is_constant_evaluated_unittest.cc\",\n        \"endian_utils_unittest.cc\",\n        \"environment_unittest.cc\",\n        \"parallelize_unittest.cc\",\n        \"random_unittest.cc\",\n        \"range_unittest.cc\",\n        \"ref_unittest.cc\",\n        \"scoped_generic_unittest.cc\",\n    ],\n    deps = [\n        \":auto_reset\",\n        \":bit_cast\",\n        \":bits\",\n        \":cxx20_is_constant_evaluated\",\n        \":endian_utils\",\n        \":environment\",\n        \":parallelize\",\n        \":random\",\n        \":range\",\n        \":ref\",\n        \":scoped_generic\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/base/containers:contains\",\n        \"//tachyon/base/containers:cxx20_erase\",\n        \"@com_google_absl//absl/hash:hash_testing\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/base/apple/.clang-format",
    "content": "DisableFormat: true\nSortIncludes: true\n"
  },
  {
    "path": "tachyon/base/apple/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_objc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_objc_library(\n    name = \"bridging\",\n    hdrs = [\"bridging.h\"],\n    deps = [\n        \"//tachyon/base:logging\",\n        \"//tachyon/base/mac:scoped_cftyperef\",\n        \"//tachyon/base/types:always_false\",\n    ],\n)\n\ntachyon_objc_library(\n    name = \"bundle_locations\",\n    srcs = [\"bundle_locations.mm\"],\n    hdrs = [\"bundle_locations.h\"],\n    deps = [\"//tachyon/base/files:file_path\"],\n)\n"
  },
  {
    "path": "tachyon/base/apple/bridging.h",
    "content": "// Copyright 2021 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_APPLE_BRIDGING_H_\n#define TACHYON_BASE_APPLE_BRIDGING_H_\n\n#include <CoreText/CoreText.h>\n#import <Foundation/Foundation.h>\n\n#include \"tachyon/export.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/mac/scoped_cftyperef.h\"\n#include \"tachyon/base/types/always_false.h\"\n#include \"tachyon/build/build_config.h\"\n\n#if BUILDFLAG(IS_IOS)\n#import <UIKit/UIKit.h>\n#endif\n\n#if BUILDFLAG(IS_MAC)\n#import <AppKit/AppKit.h>\n#endif\n\n#if !defined(__has_feature) || !__has_feature(objc_arc)\n#error \"tachyon/base/apple/bridging.h requires ARC.\"\n#endif\n\n// These functions convert pointers of bridged CFTypes to NSTypes and\n// vice-versa. They come in two flavors: those that transfer ownership\n// (`OwnershipCast`) and those that just convert the pointer (`PtrCast`).\n//\n// Examples:\n//\n// Ownership transference (as in `CFBridgingRetain`/`Release`):\n//   CFStringRef cf_string = CFStringCreateWithCString(...);\n//   NSString* ns_string = CFToNSOwnershipCast(cf_string);\n//   // At this point, `cf_string` does not need releasing.\n//\n//   NSString* ns_string = [[NSString alloc] initWithString:...];\n//   CFStringRef cf_string = NSToCFOwnershipCast(ns_string);\n//   // At this point, `cf_string` must be released.\n//\n// Pointer conversion (as in `__bridge`):\n//   // `cf_data` is some `CFDataRef` from somewhere.\n//   NSImage* ns_image = [[NSImage alloc] initWithData:CFToNSPtrCast(cf_data)];\n//\n//   // `ns_data` is some `NSData *` from somewhere.\n//   SecKeyRef sec_key = SecKeyCreateFromData(..., NSToCFPtrCast(ns_data), ...);\n//\n// The reason to use these functions (rather than using `__bridge` and\n// `CFBridgingRetain`/`Release`) is because they are type-safe. The OS-provided\n// bridging calls do not type check, while these calls do the appropriate type\n// checking via the magic of macros.\n//\n// Implementation note: Why not templates? Type checking in Core Foundation\n// involves functions named in a specific pattern, and only macro token pasting\n// works for this purpose.\n\n#define CF_TO_NS_CAST_IMPL(TypeCF, TypeNS)                                  \\\n  namespace tachyon::base::apple {                                          \\\n  inline TACHYON_EXPORT TypeNS* _Nullable CFToNSOwnershipCast(              \\\n      TypeCF##Ref CF_CONSUMED _Nullable cf_val) {                           \\\n    DCHECK(!cf_val || TypeCF##GetTypeID() == CFGetTypeID(cf_val));          \\\n    return (__bridge_transfer TypeNS*)cf_val;                               \\\n  }                                                                         \\\n  inline TACHYON_EXPORT CF_RETURNS_RETAINED                                 \\\n      TypeCF##Ref _Nullable NSToCFOwnershipCast(TypeNS* _Nullable ns_val) { \\\n    TypeCF##Ref cf_val = (__bridge_retained TypeCF##Ref)ns_val;             \\\n    DCHECK(!cf_val || TypeCF##GetTypeID() == CFGetTypeID(cf_val));          \\\n    return cf_val;                                                          \\\n  }                                                                         \\\n  inline TACHYON_EXPORT TypeNS* _Nullable CFToNSPtrCast(                    \\\n      TypeCF##Ref _Nullable cf_val) {                                       \\\n    DCHECK(!cf_val || TypeCF##GetTypeID() == CFGetTypeID(cf_val));          \\\n    return (__bridge TypeNS*)cf_val;                                        \\\n  }                                                                         \\\n  inline TACHYON_EXPORT TypeCF##Ref _Nullable NSToCFPtrCast(                \\\n      TypeNS* _Nullable ns_val) {                                           \\\n    TypeCF##Ref cf_val = (__bridge TypeCF##Ref)ns_val;                      \\\n    DCHECK(!cf_val || TypeCF##GetTypeID() == CFGetTypeID(cf_val));          \\\n    return cf_val;                                                          \\\n  }                                                                         \\\n  }  // namespace tachyon::base::apple\n\n#define CF_TO_NS_MUTABLE_CAST_IMPL(name)                                 \\\n  CF_TO_NS_CAST_IMPL(CF##name, NS##name)                                 \\\n  namespace tachyon::base::apple {                                       \\\n  inline TACHYON_EXPORT NSMutable##name* _Nullable CFToNSOwnershipCast(  \\\n      CFMutable##name##Ref CF_CONSUMED _Nullable cf_val) {               \\\n    DCHECK(!cf_val || CF##name##GetTypeID() == CFGetTypeID(cf_val));     \\\n    return (__bridge_transfer NSMutable##name*)cf_val;                   \\\n  }                                                                      \\\n  inline TACHYON_EXPORT CF_RETURNS_RETAINED                              \\\n      CFMutable##name##Ref _Nullable NSToCFOwnershipCast(                \\\n          NSMutable##name* _Nullable ns_val) {                           \\\n    CFMutable##name##Ref cf_val =                                        \\\n        (__bridge_retained CFMutable##name##Ref)ns_val;                  \\\n    DCHECK(!cf_val || CF##name##GetTypeID() == CFGetTypeID(cf_val));     \\\n    return cf_val;                                                       \\\n  }                                                                      \\\n  inline TACHYON_EXPORT NSMutable##name* _Nullable CFToNSPtrCast(        \\\n      CFMutable##name##Ref _Nullable cf_val) {                           \\\n    DCHECK(!cf_val || CF##name##GetTypeID() == CFGetTypeID(cf_val));     \\\n    return (__bridge NSMutable##name*)cf_val;                            \\\n  }                                                                      \\\n  inline TACHYON_EXPORT CFMutable##name##Ref _Nullable NSToCFPtrCast(    \\\n      NSMutable##name* _Nullable ns_val) {                               \\\n    CFMutable##name##Ref cf_val = (__bridge CFMutable##name##Ref)ns_val; \\\n    DCHECK(!cf_val || CF##name##GetTypeID() == CFGetTypeID(cf_val));     \\\n    return cf_val;                                                       \\\n  }                                                                      \\\n  }  // namespace tachyon::base::apple\n\n// List of toll-free bridged types taken from:\n// https://web.archive.org/web/20111124025525/http://www.cocoadev.com/index.pl?TollFreeBridged\n// https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFDesignConcepts/Articles/tollFreeBridgedTypes.html#//apple_ref/doc/uid/TP40010677-SW4\n\n// Foundation\nCF_TO_NS_MUTABLE_CAST_IMPL(Array)\nCF_TO_NS_MUTABLE_CAST_IMPL(AttributedString)\nCF_TO_NS_CAST_IMPL(CFCalendar, NSCalendar)\nCF_TO_NS_MUTABLE_CAST_IMPL(CharacterSet)\nCF_TO_NS_MUTABLE_CAST_IMPL(Data)\nCF_TO_NS_CAST_IMPL(CFDate, NSDate)\nCF_TO_NS_MUTABLE_CAST_IMPL(Dictionary)\nCF_TO_NS_CAST_IMPL(CFError, NSError)\nCF_TO_NS_CAST_IMPL(CFLocale, NSLocale)\nCF_TO_NS_CAST_IMPL(CFNumber, NSNumber)\nCF_TO_NS_CAST_IMPL(CFRunLoopTimer, NSTimer)\nCF_TO_NS_CAST_IMPL(CFTimeZone, NSTimeZone)\nCF_TO_NS_MUTABLE_CAST_IMPL(Set)\nCF_TO_NS_CAST_IMPL(CFReadStream, NSInputStream)\nCF_TO_NS_CAST_IMPL(CFWriteStream, NSOutputStream)\nCF_TO_NS_MUTABLE_CAST_IMPL(String)\nCF_TO_NS_CAST_IMPL(CFURL, NSURL)\n\n// AppKit / UIKit\n#if BUILDFLAG(IS_IOS)\nCF_TO_NS_CAST_IMPL(CTFont, UIFont)\n#else\nCF_TO_NS_CAST_IMPL(CTFont, NSFont)\n#endif  // BUILDFLAG(IS_IOS)\n\n#undef CF_TO_NS_CAST_IMPL\n#undef CF_TO_NS_MUTABLE_CAST_IMPL\n\nnamespace tachyon::base::apple {\n\ntemplate <typename CFT>\nid _Nullable CFToNSOwnershipCast(base::ScopedCFTypeRef<CFT>) {\n  static_assert(\n      AlwaysFalse<CFT>,\n      \"Error: Do not pass a ScopedCFTypeRef to CFToNSOwnershipCast. \"\n      \"Call .release() on the ScopedCFTypeRef and pass the result in.\");\n  return nil;\n}\n\n}  // namespace tachyon::base::apple\n\n#endif  // TACHYON_BASE_APPLE_BRIDGING_H_\n"
  },
  {
    "path": "tachyon/base/apple/bundle_locations.h",
    "content": "// Copyright 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_APPLE_BUNDLE_LOCATIONS_H_\n#define TACHYON_BASE_APPLE_BUNDLE_LOCATIONS_H_\n\n#include \"tachyon/export.h\"\n#include \"tachyon/base/files/file_path.h\"\n\n#if defined(__OBJC__)\n#import <Foundation/Foundation.h>\n#endif  // __OBJC__\n\nnamespace tachyon::base {\nclass FilePath;\n}\n\n// NSBundle isn't thread-safe; all functions in this file must be called on the\n// main thread.\n\nnamespace tachyon::base::apple {\n\n// This file provides several functions to explicitly request the various\n// component bundles of Chrome.  Please use these methods rather than calling\n// `+[NSBundle mainBundle]` or `CFBundleGetMainBundle()`.\n//\n// Terminology\n//  - \"Outer Bundle\" - This is the main bundle for Chrome; it's what\n//  `+[NSBundle mainBundle]` returns when Chrome is launched normally.\n//\n//  - \"Main Bundle\" - This is the bundle from which Chrome was launched.\n//  This will be the same as the outer bundle except when Chrome is launched\n//  via an app shortcut, in which case this will return the app shortcut's\n//  bundle rather than the main Chrome bundle.\n//\n//  - \"Framework Bundle\" - This is the bundle corresponding to the Chrome\n//  framework.\n//\n// Guidelines for use:\n//  - To access a resource, the Framework bundle should be used.\n//  - If the choice is between the Outer or Main bundles then please choose\n//  carefully.  Most often the Outer bundle will be the right choice, but for\n//  cases such as adding an app to the \"launch on startup\" list, the Main\n//  bundle is probably the one to use.\n\n// Methods for retrieving the various bundles.\nTACHYON_EXPORT FilePath MainBundlePath();\nTACHYON_EXPORT FilePath OuterBundlePath();\nTACHYON_EXPORT FilePath FrameworkBundlePath();\n#if defined(__OBJC__)\nTACHYON_EXPORT NSBundle* MainBundle();\nTACHYON_EXPORT NSURL* MainBundleURL();\nTACHYON_EXPORT NSBundle* OuterBundle();\nTACHYON_EXPORT NSURL* OuterBundleURL();\nTACHYON_EXPORT NSBundle* FrameworkBundle();\n#endif  // __OBJC__\n\n// Set the bundle that the preceding functions will return, overriding the\n// default values. Restore the default by passing in `nil` or an empty\n// `FilePath`.\nTACHYON_EXPORT void SetOverrideOuterBundlePath(const FilePath& file_path);\nTACHYON_EXPORT void SetOverrideFrameworkBundlePath(const FilePath& file_path);\n#if defined(__OBJC__)\nTACHYON_EXPORT void SetOverrideOuterBundle(NSBundle* bundle);\nTACHYON_EXPORT void SetOverrideFrameworkBundle(NSBundle* bundle);\n#endif  // __OBJC__\n\n}  // namespace tachyon::base::apple\n\n#endif  // TACHYON_BASE_APPLE_BUNDLE_LOCATIONS_H_\n"
  },
  {
    "path": "tachyon/base/apple/bundle_locations.mm",
    "content": "// Copyright 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/apple/bundle_locations.h\"\n\n#if !defined(__has_feature) || !__has_feature(objc_arc)\n#error \"This file requires ARC support.\"\n#endif\n\nnamespace tachyon::base::apple {\n\nNSBundle* MainBundle() {\n  return NSBundle.mainBundle;\n}\n\nNSURL* MainBundleURL() {\n  return MainBundle().bundleURL;\n}\n\nNSURL* OuterBundleURL() {\n  return OuterBundle().bundleURL;\n}\n\n}  // namespace tachyon::base::apple\n"
  },
  {
    "path": "tachyon/base/auto_reset.h",
    "content": "// Copyright 2011 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_AUTO_RESET_H_\n#define TACHYON_BASE_AUTO_RESET_H_\n\n#include <utility>\n\n#include \"tachyon/base/logging.h\"\n// #include \"base/memory/raw_ptr_exclusion.h\"\n\n// base::AutoReset<> is useful for setting a variable to a new value only within\n// a particular scope. An base::AutoReset<> object resets a variable to its\n// original value upon destruction, making it an alternative to writing\n// \"var = false;\" or \"var = old_val;\" at all of a block's exit points.\n//\n// This should be obvious, but note that an base::AutoReset<> instance should\n// have a shorter lifetime than its scoped_variable, to prevent invalid memory\n// writes when the base::AutoReset<> object is destroyed.\n\nnamespace tachyon::base {\n\ntemplate <typename T>\nclass [[maybe_unused, nodiscard]] AutoReset {\n public:\n  template <typename U>\n  AutoReset(T* scoped_variable, U&& new_value)\n      : scoped_variable_(scoped_variable),\n        original_value_(\n            std::exchange(*scoped_variable_, std::forward<U>(new_value))) {}\n\n  // A constructor that's useful for asserting the old value of\n  // `scoped_variable`, especially when it's inconvenient to check this before\n  // constructing the AutoReset object (e.g. in a class member initializer\n  // list).\n  template <typename U>\n  AutoReset(T* scoped_variable, U&& new_value, const T& expected_old_value)\n      : AutoReset(scoped_variable, new_value) {\n    DCHECK_EQ(original_value_, expected_old_value);\n  }\n\n  AutoReset(AutoReset&& other)\n      : scoped_variable_(std::exchange(other.scoped_variable_, nullptr)),\n        original_value_(std::move(other.original_value_)) {}\n\n  AutoReset& operator=(AutoReset&& rhs) {\n    scoped_variable_ = std::exchange(rhs.scoped_variable_, nullptr);\n    original_value_ = std::move(rhs.original_value_);\n    return *this;\n  }\n\n  ~AutoReset() {\n    if (scoped_variable_) *scoped_variable_ = std::move(original_value_);\n  }\n\n private:\n  // `scoped_variable_` is not a raw_ptr<T> for performance reasons: Large\n  // number of non-PartitionAlloc pointees + AutoReset is typically short-lived\n  // (e.g. allocated on the stack).\n  // RAW_PTR_EXCLUSION T* scoped_variable_;\n  T* scoped_variable_;\n\n  T original_value_;\n};\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_AUTO_RESET_H_\n"
  },
  {
    "path": "tachyon/base/auto_reset_unittest.cc",
    "content": "// Copyright 2019 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/auto_reset.h\"\n\n#include <utility>\n\n#include \"gtest/gtest.h\"\n\nnamespace tachyon::base {\n\nTEST(AutoReset, Move) {\n  int value = 10;\n  {\n    AutoReset<int> resetter1{&value, 20};\n    EXPECT_EQ(20, value);\n    {\n      value = 15;\n      AutoReset<int> resetter2 = std::move(resetter1);\n      // Moving to a new resetter does not change the value;\n      EXPECT_EQ(15, value);\n    }\n    // Moved-to `resetter2` is out of scoped, and resets to the original value\n    // that was in moved-from `resetter1`.\n    EXPECT_EQ(10, value);\n    value = 105;\n  }\n  // Moved-from `resetter1` does not reset to anything.\n  EXPECT_EQ(105, value);\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/binding/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"binding_util\",\n    srcs = [\n        \"cpp_constructor.cc\",\n        \"cpp_constructor_matcher.cc\",\n        \"cpp_type_names.cc\",\n        \"cpp_value.cc\",\n    ],\n    hdrs = [\n        \"callable_util.h\",\n        \"cpp_constructor.h\",\n        \"cpp_constructor_matcher.h\",\n        \"cpp_raw_ptr.h\",\n        \"cpp_shared_ptr.h\",\n        \"cpp_stack_value.h\",\n        \"cpp_type.h\",\n        \"cpp_type_names.h\",\n        \"cpp_unique_ptr.h\",\n        \"cpp_value.h\",\n        \"cpp_value_factory.h\",\n        \"holder_util.h\",\n        \"property_util.h\",\n    ],\n    deps = [\n        \"//tachyon/base:logging\",\n        \"//tachyon/base:template_util\",\n        \"//tachyon/base:type_list\",\n        \"//tachyon/base/strings:string_util\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/base/binding/callable_util.h",
    "content": "#ifndef TACHYON_BASE_BINDING_CALLABLE_UTIL_H_\n#define TACHYON_BASE_BINDING_CALLABLE_UTIL_H_\n\n#include <tuple>\n#include <type_traits>\n#include <utility>\n\n#include \"tachyon/base/template_util.h\"\n#include \"tachyon/base/type_list.h\"\n\nnamespace tachyon::base {\nnamespace internal {\n\ntemplate <template <typename, typename SFINAE = void> class Predicate,\n          typename T, typename SFINAE = void>\nstruct DeclarableTupleTypeImpl;\n\ntemplate <template <typename, typename SFINAE = void> class Predicate,\n          typename T>\nstruct DeclarableTupleTypeImpl<\n    Predicate, T, std::enable_if_t<Predicate<std::decay_t<T>>::value>> {\n  using Type = std::decay_t<T>;\n};\n\ntemplate <template <typename, typename SFINAE = void> class Predicate,\n          typename T>\nstruct DeclarableTupleTypeImpl<\n    Predicate, T, std::enable_if_t<!Predicate<std::decay_t<T>>::value>> {\n  using Type = reference_to_pointer_t<T>;\n};\n\ntemplate <template <typename, typename SFINAE = void> class Predicate,\n          typename T>\nusing DeclarableTupleType =\n    typename DeclarableTupleTypeImpl<Predicate, T>::Type;\n\ntemplate <template <typename, typename SFINAE = void> class Predicate,\n          typename... List>\nstruct ConvertTypeListToDeclarableTupleImpl;\n\ntemplate <template <typename, typename SFINAE = void> class Predicate,\n          typename... List>\nstruct ConvertTypeListToDeclarableTupleImpl<Predicate, TypeList<List...>> {\n  using Type = std::tuple<DeclarableTupleType<Predicate, List>...>;\n};\n\ntemplate <template <typename, typename SFINAE = void> class Predicate,\n          typename List>\nusing ConvertTypeListToDeclarableTuple =\n    typename ConvertTypeListToDeclarableTupleImpl<Predicate, List>::Type;\n\n// pointer -> reference\ntemplate <typename R, typename T,\n          std::enable_if_t<std::is_reference<R>::value &&\n                           std::is_pointer<T>::value>* = nullptr>\nR arg_cast(T v) {\n  return *v;\n}\n\n// pointer -> pointer\ntemplate <typename R, typename T,\n          std::enable_if_t<std::is_pointer<R>::value &&\n                           std::is_pointer<T>::value>* = nullptr>\nR arg_cast(T v) {\n  return v;\n}\n\n// non-pointer -> non-pointer, movable\ntemplate <typename R, typename T,\n          std::enable_if_t<std::is_move_constructible<R>::value &&\n                           !std::is_pointer<T>::value>* = nullptr>\nR arg_cast(T&& v) {\n  return std::move(v);\n}\n\n// non-pointer -> non-pointer, not movable\ntemplate <typename R, typename T,\n          std::enable_if_t<!std::is_move_constructible<R>::value &&\n                           !std::is_pointer<T>::value>* = nullptr>\nR arg_cast(T&& v) {\n  return static_cast<R>(v);\n}\n\ntemplate <template <typename, typename SFINAE = void> class Predicate>\nstruct RetTypeCaster {\n  template <typename R, typename T,\n            std::enable_if_t<Predicate<std::decay_t<R>>::value &&\n                             std::is_move_constructible<R>::value>* = nullptr>\n  static R cast(T&& v) {\n    return std::move(v);\n  }\n\n  template <typename R, typename T,\n            std::enable_if_t<Predicate<std::decay_t<R>>::value &&\n                             !std::is_move_constructible<R>::value>* = nullptr>\n  static R cast(T&& v) {\n    return static_cast<R>(v);\n  }\n\n  template <typename R, typename T,\n            std::enable_if_t<!Predicate<std::decay_t<R>>::value &&\n                             std::is_reference<R>::value>* = nullptr>\n  static auto cast(T&& v) {\n    return std::ref(v);\n  }\n\n  template <typename R, typename T,\n            std::enable_if_t<!Predicate<std::decay_t<R>>::value &&\n                             !std::is_reference<R>::value>* = nullptr>\n  static R cast(T&& v) {\n    return std::move(v);\n  }\n};\n\n}  // namespace internal\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_BINDING_CALLABLE_UTIL_H_\n"
  },
  {
    "path": "tachyon/base/binding/cpp_constructor.cc",
    "content": "#include \"tachyon/base/binding/cpp_constructor.h\"\n\n#include \"tachyon/base/binding/cpp_type_names.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/strings/string_util.h\"\n\nnamespace tachyon::base {\n\n// static\nstd::string_view CppConstructor::UnwrapPointer(std::string_view arg_type_name) {\n  if (ConsumeSuffix(&arg_type_name, \"*\")) {\n    return arg_type_name;\n  } else if (ConsumePrefix(&arg_type_name, kCppSharedPtrTypePrefix) &&\n             ConsumeSuffix(&arg_type_name, \">\")) {\n    return arg_type_name;\n  } else if (ConsumePrefix(&arg_type_name, kCppUniquePtrTypePrefix) &&\n             ConsumeSuffix(&arg_type_name, \">\")) {\n    return arg_type_name;\n  }\n\n  return arg_type_name;\n}\n\nCppConstructor::CppConstructor() = default;\n\nCppConstructor::~CppConstructor() = default;\n\nbool CppConstructor::HasDoublePointerArgument() const {\n  for (std::string_view arg_type_name : arg_type_names_) {\n    std::string_view unwrapped = UnwrapPointer(arg_type_name);\n    if (unwrapped.length() == arg_type_name.length()) continue;\n    std::string_view unwrapped2 = UnwrapPointer(unwrapped);\n    if (unwrapped2.length() == unwrapped.length()) continue;\n    return true;\n  }\n  return false;\n}\n\nvoid CppConstructor::ValidateAndMaybeDie() const {\n  LOG_IF(DFATAL, HasDoublePointerArgument())\n      << \"You add the constructor that contains the double pointer (\"\n      << GetFunctionSignature(0) << \")\";\n}\n\nstd::string CppConstructor::GetFunctionSignature(\n    size_t default_args_num) const {\n  DCHECK_LE(default_args_num, arg_type_names_.size());\n\n  size_t size = arg_type_names_.size() - default_args_num;\n\n  if (size == 0) return \"\";\n\n  std::stringstream ss;\n  for (size_t i = 0; i < size - 1; ++i) {\n    ss << arg_type_names_[i] << \",\";\n  }\n  ss << arg_type_names_[size - 1];\n  return ss.str();\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/binding/cpp_constructor.h",
    "content": "#ifndef TACHYON_BASE_BINDING_CPP_CONSTRUCTOR_H_\n#define TACHYON_BASE_BINDING_CPP_CONSTRUCTOR_H_\n\n#include <memory>\n#include <string>\n#include <string_view>\n#include <vector>\n\n#include \"tachyon/export.h\"\n\nnamespace tachyon::base {\n\nclass TACHYON_EXPORT CppConstructor {\n public:\n  CppConstructor();\n  CppConstructor(const CppConstructor& other) = delete;\n  CppConstructor& operator=(const CppConstructor& other) = delete;\n  ~CppConstructor();\n\n  bool HasDoublePointerArgument() const;\n  void ValidateAndMaybeDie() const;\n\n  size_t GetArgsNum() const { return arg_type_names_.size(); }\n  size_t GetDefaultArgsNum() const { return default_args_num_; }\n  void SetDefaultArgsNum(size_t default_args_num) {\n    default_args_num_ = default_args_num;\n  }\n  std::string GetFunctionSignature(size_t default_args_num) const;\n\n protected:\n  static std::string_view UnwrapPointer(std::string_view arg_type_name);\n\n  size_t default_args_num_ = 0;\n  std::vector<std::string> arg_type_names_;\n};\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_BINDING_CPP_CONSTRUCTOR_H_\n"
  },
  {
    "path": "tachyon/base/binding/cpp_constructor_matcher.cc",
    "content": "#include \"tachyon/base/binding/cpp_constructor_matcher.h\"\n\n#include \"tachyon/base/logging.h\"\n\nnamespace tachyon::base {\n\nCppConstructorMatcher::CppConstructorMatcher() = default;\n\nCppConstructorMatcher::~CppConstructorMatcher() = default;\n\nvoid CppConstructorMatcher::ValidateAndMaybeDie(\n    const CppConstructor& constructor) const {\n  for (size_t i = 0; i < constructors_.size(); ++i) {\n    CppConstructor* constructor2 = constructors_[i].get();\n    for (size_t default_arg_num = 0;\n         default_arg_num < constructor.GetDefaultArgsNum(); ++default_arg_num) {\n      for (size_t default_arg_num2 = 0;\n           default_arg_num2 < constructor2->GetDefaultArgsNum();\n           ++default_arg_num2) {\n        if (constructor.GetArgsNum() - constructor.GetDefaultArgsNum() ==\n            constructor2->GetArgsNum() - constructor2->GetDefaultArgsNum()) {\n          if (constructor.GetFunctionSignature(default_arg_num) ==\n              constructor2->GetFunctionSignature(default_arg_num2)) {\n            LOG(DFATAL) << \"You add the constructor with the same signature (\"\n                        << constructor.GetFunctionSignature(default_arg_num)\n                        << \")\";\n            break;\n          }\n        }\n      }\n    }\n  }\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/binding/cpp_constructor_matcher.h",
    "content": "#ifndef TACHYON_BASE_BINDING_CPP_CONSTRUCTOR_MATCHER_H_\n#define TACHYON_BASE_BINDING_CPP_CONSTRUCTOR_MATCHER_H_\n\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/binding/cpp_constructor.h\"\n#include \"tachyon/export.h\"\n\nnamespace tachyon::base {\n\nclass TACHYON_EXPORT CppConstructorMatcher {\n public:\n  CppConstructorMatcher();\n  CppConstructorMatcher(const CppConstructorMatcher& other) = delete;\n  CppConstructorMatcher& operator=(const CppConstructorMatcher& other) = delete;\n  ~CppConstructorMatcher();\n\n  void Add(std::unique_ptr<CppConstructor> constructor) {\n    ValidateAndMaybeDie(*constructor.get());\n    constructors_.push_back(std::move(constructor));\n  }\n  size_t GetSize() const { return constructors_.size(); }\n\n protected:\n  void ValidateAndMaybeDie(const CppConstructor& constructor) const;\n\n  std::vector<std::unique_ptr<CppConstructor>> constructors_;\n};\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_BINDING_CPP_CONSTRUCTOR_MATCHER_H_\n"
  },
  {
    "path": "tachyon/base/binding/cpp_raw_ptr.h",
    "content": "#ifndef TACHYON_BASE_BINDING_CPP_RAW_PTR_H_\n#define TACHYON_BASE_BINDING_CPP_RAW_PTR_H_\n\n#include <type_traits>\n\n#include \"tachyon/base/binding/cpp_value.h\"\n\nnamespace tachyon::base {\n\ntemplate <typename T>\nclass CppRawPtr : public CppValue {\n public:\n  explicit CppRawPtr(T* ptr) : ptr_(ptr) {}\n  ~CppRawPtr() override = default;\n\n  bool IsCppRawPtr() const override { return true; }\n\n  void* raw_ptr() override {\n    return reinterpret_cast<void*>(const_cast<std::remove_const_t<T>*>(ptr_));\n  }\n  const void* raw_ptr() const override { return ptr_; }\n\n  bool is_const() const override { return std::is_const<T>::value; }\n\n private:\n  T* ptr_;\n};\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_BINDING_CPP_RAW_PTR_H_\n"
  },
  {
    "path": "tachyon/base/binding/cpp_shared_ptr.h",
    "content": "#ifndef TACHYON_BASE_BINDING_CPP_SHARED_PTR_H_\n#define TACHYON_BASE_BINDING_CPP_SHARED_PTR_H_\n\n#include <memory>\n#include <type_traits>\n\n#include \"tachyon/base/binding/cpp_value.h\"\n\nnamespace tachyon::base {\n\ntemplate <typename T>\nclass CppSharedPtr : public CppValue {\n public:\n  explicit CppSharedPtr(const std::shared_ptr<T>& ptr) : ptr_(ptr) {}\n  ~CppSharedPtr() override = default;\n\n  bool IsCppSharedPtr() const override { return true; }\n\n  void* raw_ptr() override {\n    return reinterpret_cast<void*>(\n        const_cast<std::remove_const_t<T>*>(ptr_.get()));\n  }\n  const void* raw_ptr() const override { return ptr_.get(); }\n\n  bool is_const() const override { return std::is_const<T>::value; }\n\n  std::shared_ptr<T> shared_ptr() { return ptr_; }\n\n private:\n  std::shared_ptr<T> ptr_;\n};\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_BINDING_CPP_SHARED_PTR_H_\n"
  },
  {
    "path": "tachyon/base/binding/cpp_stack_value.h",
    "content": "#ifndef TACHYON_BASE_BINDING_CPP_STACK_VALUE_H_\n#define TACHYON_BASE_BINDING_CPP_STACK_VALUE_H_\n\n#include <type_traits>\n#include <utility>\n\n#include \"tachyon/base/binding/cpp_value.h\"\n\nnamespace tachyon::base {\n\ntemplate <typename T>\nclass CppStackValue : public CppValue {\n public:\n  explicit CppStackValue(const T& value) : value_(value) {}\n  explicit CppStackValue(T&& value) : value_(std::move(value)) {}\n\n  ~CppStackValue() override = default;\n\n  bool IsCppStackValue() const override { return true; }\n\n  void* raw_ptr() override {\n    return reinterpret_cast<void*>(\n        const_cast<std::remove_const_t<T>*>(&value_));\n  }\n  const void* raw_ptr() const override { return &value_; }\n\n  bool is_const() const override { return std::is_const<T>::value; }\n\n private:\n  T value_;\n};\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_BINDING_CPP_STACK_VALUE_H_\n"
  },
  {
    "path": "tachyon/base/binding/cpp_type.h",
    "content": "#ifndef TACHYON_BASE_BINDING_CPP_TYPE_H_\n#define TACHYON_BASE_BINDING_CPP_TYPE_H_\n\n#include <string>\n\n#include \"tachyon/base/no_destructor.h\"\n\nnamespace tachyon::base {\n\ntemplate <typename T>\nclass CppType {\n public:\n  static CppType& Get() {\n    static NoDestructor<CppType<T>> cpp_type;\n    return *cpp_type;\n  }\n\n  void set_name(const std::string& name) { name_ = name; }\n\n  const std::string& name() { return name_; }\n\n  const void* unique_id() const {\n    static int unique_address;\n    return &unique_address;\n  }\n\n private:\n  friend class NoDestructor<CppType<T>>;\n\n  CppType() = default;\n  CppType(const CppType& other) = delete;\n  CppType& operator=(const CppType& other) = delete;\n\n  std::string name_;\n};\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_BINDING_CPP_TYPE_H_\n"
  },
  {
    "path": "tachyon/base/binding/cpp_type_names.cc",
    "content": "#include \"tachyon/base/binding/cpp_type_names.h\"\n\n#include \"absl/strings/substitute.h\"\n\nnamespace tachyon::base {\n\nstd::string MakeCppVectorTypeName(std::string_view type) {\n  return absl::Substitute(\"$0$1>\", kCppVectorTypePrefix, type);\n}\n\nstd::string MakeCppOptionalTypeName(std::string_view type) {\n  return absl::Substitute(\"$0$1>\", kCppOptionalPrefix, type);\n}\n\nstd::string MakeCppMapTypeName(std::string_view key_type,\n                               std::string_view value_type) {\n  return absl::Substitute(\"$0$1,$2>\", kCppMapTypePrefix, key_type, value_type);\n}\n\nstd::string MakeCppTupleTypeName(const std::vector<std::string>& types) {\n  return absl::Substitute(\"$0$1>\", kCppTupleTypePrefix,\n                          absl::StrJoin(types, \",\"));\n}\n\nstd::string MakeCppRawPtrTypeName(std::string_view type) {\n  return absl::Substitute(\"$0*\", type);\n}\n\nstd::string MakeCppSharedPtrTypeName(std::string_view type) {\n  return absl::Substitute(\"$0$1>\", kCppSharedPtrTypePrefix, type);\n}\n\nstd::string MakeCppUniquePtrTypeName(std::string_view type) {\n  return absl::Substitute(\"$0$1>\", kCppUniquePtrTypePrefix, type);\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/binding/cpp_type_names.h",
    "content": "#ifndef TACHYON_BASE_BINDING_CPP_TYPE_NAMES_H_\n#define TACHYON_BASE_BINDING_CPP_TYPE_NAMES_H_\n\n#include <string>\n#include <string_view>\n#include <vector>\n\n#include \"tachyon/export.h\"\n\nnamespace tachyon::base {\n\nconstexpr const char* kCppBoolTypeName = \"bool\";\nconstexpr const char* kCppIntTypeName = \"int\";\nconstexpr const char* kCppUintTypeName = \"uint\";\nconstexpr const char* kCppInt64TypeName = \"int64\";\nconstexpr const char* kCppUint64TypeName = \"uint64\";\nconstexpr const char* kCppNumberTypeName = \"number\";\nconstexpr const char* kCppStringTypeName = \"string\";\nconstexpr const char* kCppVectorTypePrefix = \"vector<\";\nconstexpr const char* kCppMapTypePrefix = \"map<\";\nconstexpr const char* kCppTupleTypePrefix = \"tuple<\";\nconstexpr const char* kCppSharedPtrTypePrefix = \"shared_ptr<\";\nconstexpr const char* kCppUniquePtrTypePrefix = \"unique_ptr<\";\nconstexpr const char* kCppOptionalPrefix = \"optional<\";\n\nTACHYON_EXPORT std::string MakeCppVectorTypeName(std::string_view type);\nTACHYON_EXPORT std::string MakeCppOptionalTypeName(std::string_view type);\nTACHYON_EXPORT std::string MakeCppMapTypeName(std::string_view key_type,\n                                              std::string_view value_type);\nTACHYON_EXPORT std::string MakeCppTupleTypeName(\n    const std::vector<std::string>& types);\nTACHYON_EXPORT std::string MakeCppRawPtrTypeName(std::string_view type);\nTACHYON_EXPORT std::string MakeCppSharedPtrTypeName(std::string_view type);\nTACHYON_EXPORT std::string MakeCppUniquePtrTypeName(std::string_view type);\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_BINDING_CPP_TYPE_NAMES_H_\n"
  },
  {
    "path": "tachyon/base/binding/cpp_unique_ptr.h",
    "content": "#ifndef TACHYON_BASE_BINDING_CPP_UNIQUE_PTR_H_\n#define TACHYON_BASE_BINDING_CPP_UNIQUE_PTR_H_\n\n#include <memory>\n#include <type_traits>\n#include <utility>\n\n#include \"tachyon/base/binding/cpp_value.h\"\n\nnamespace tachyon::base {\n\ntemplate <typename T, typename Deleter>\nclass CppUniquePtr : public CppValue {\n public:\n  explicit CppUniquePtr(std::unique_ptr<T, Deleter> ptr)\n      : ptr_(std::move(ptr)) {}\n  ~CppUniquePtr() override = default;\n\n  bool IsCppUniquePtr() const override { return true; }\n\n  void* raw_ptr() override {\n    return reinterpret_cast<void*>(\n        const_cast<std::remove_const_t<T>*>(ptr_.get()));\n  }\n  const void* raw_ptr() const override { return ptr_.get(); }\n\n  bool is_const() const override { return std::is_const<T>::value; }\n\n  std::unique_ptr<T, Deleter> unique_ptr() { return std::move(ptr_); }\n\n private:\n  std::unique_ptr<T, Deleter> ptr_;\n};\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_BINDING_CPP_UNIQUE_PTR_H_\n"
  },
  {
    "path": "tachyon/base/binding/cpp_value.cc",
    "content": "#include \"tachyon/base/binding/cpp_value.h\"\n\nnamespace tachyon::base {\n\nCppValue::CppValue() = default;\n\nCppValue::~CppValue() = default;\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/binding/cpp_value.h",
    "content": "#ifndef TACHYON_BASE_BINDING_CPP_VALUE_H_\n#define TACHYON_BASE_BINDING_CPP_VALUE_H_\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/export.h\"\n\nnamespace tachyon::base {\n\ntemplate <typename T>\nclass CppRawPtr;\ntemplate <typename T>\nclass CppSharedPtr;\ntemplate <typename T>\nclass CppStackValue;\ntemplate <typename T, typename Deleter>\nclass CppUniquePtr;\n\nclass TACHYON_EXPORT CppValue {\n public:\n  CppValue();\n  virtual ~CppValue();\n\n  virtual bool IsCppStackValue() const { return false; }\n\n  virtual bool IsCppRawPtr() const { return false; }\n\n  virtual bool IsCppSharedPtr() const { return false; }\n\n  virtual bool IsCppUniquePtr() const { return false; }\n\n  virtual void* raw_ptr() = 0;\n  virtual const void* raw_ptr() const = 0;\n\n  virtual bool is_const() const = 0;\n\n  template <typename T>\n  CppStackValue<T>* ToCppStackValue() {\n    DCHECK(IsCppStackValue());\n    return reinterpret_cast<CppStackValue<T>*>(this);\n  }\n\n  template <typename T>\n  CppRawPtr<T>* ToCppRawPtr() {\n    DCHECK(IsCppRawPtr());\n    return reinterpret_cast<CppRawPtr<T>*>(this);\n  }\n\n  template <typename T>\n  CppSharedPtr<T>* ToCppSharedPtr() {\n    DCHECK(IsCppSharedPtr());\n    return reinterpret_cast<CppSharedPtr<T>*>(this);\n  }\n\n  template <typename T, typename Deleter>\n  CppUniquePtr<T, Deleter>* ToCppUniquePtr() {\n    DCHECK(IsCppUniquePtr());\n    return reinterpret_cast<CppUniquePtr<T, Deleter>*>(this);\n  }\n};\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_BINDING_CPP_VALUE_H_\n"
  },
  {
    "path": "tachyon/base/binding/cpp_value_factory.h",
    "content": "#ifndef TACHYON_BASE_BINDING_CPP_VALUE_FACTORY_H_\n#define TACHYON_BASE_BINDING_CPP_VALUE_FACTORY_H_\n\n#include <memory>\n#include <utility>\n\n#include \"tachyon/base/binding/cpp_raw_ptr.h\"\n#include \"tachyon/base/binding/cpp_shared_ptr.h\"\n#include \"tachyon/base/binding/cpp_stack_value.h\"\n#include \"tachyon/base/binding/cpp_unique_ptr.h\"\n\nnamespace tachyon::base {\n\ntemplate <typename T>\nstruct CppValueFactory {\n  static std::unique_ptr<CppValue> Create(T value) {\n    return std::make_unique<CppStackValue<T>>(std::move(value));\n  }\n};\n\ntemplate <typename T>\nstruct CppValueFactory<std::shared_ptr<T>> {\n  static std::unique_ptr<CppValue> Create(std::shared_ptr<T> value) {\n    return std::make_unique<CppSharedPtr<T>>(std::move(value));\n  }\n};\n\ntemplate <typename T, typename Deleter>\nstruct CppValueFactory<std::unique_ptr<T, Deleter>> {\n  static std::unique_ptr<CppValue> Create(std::unique_ptr<T, Deleter> value) {\n    return std::make_unique<CppUniquePtr<T, Deleter>>(std::move(value));\n  }\n};\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_BINDING_CPP_VALUE_FACTORY_H_\n"
  },
  {
    "path": "tachyon/base/binding/holder_util.h",
    "content": "#ifndef TACHYON_BASE_BINDING_HOLDER_UTIL_H_\n#define TACHYON_BASE_BINDING_HOLDER_UTIL_H_\n\n#include <memory>\n#include <utility>\n\nnamespace tachyon::base {\nnamespace internal {\n\ntemplate <typename T, typename Holder>\nstruct is_holder_type : std::false_type {};\n\ntemplate <typename T>\nstruct is_holder_type<T, T> : std::true_type {};\n\ntemplate <typename T>\nstruct is_holder_type<T, std::shared_ptr<T>> : std::true_type {};\n\ntemplate <typename T, typename Deleter>\nstruct is_holder_type<T, std::unique_ptr<T, Deleter>> : std::true_type {};\n\ntemplate <typename Holder, typename Class>\nstruct HolderCreator;\n\n// TODO(chokobole): Need to use universal reference and perfect forwarding\ntemplate <typename Class, typename... Args>\nstruct HolderCreator<Class, Class(Args...)> {\n  static Class DoCreate(Args... args) {\n    return Class(std::forward<Args>(args)...);\n  }\n};\n\n// TODO(chokobole): Need to use universal reference and perfect forwarding\ntemplate <typename Class, typename... Args>\nstruct HolderCreator<std::shared_ptr<Class>, Class(Args...)> {\n  static std::shared_ptr<Class> DoCreate(Args... args) {\n    return std::shared_ptr<Class>(new Class(std::forward<Args>(args)...));\n  }\n};\n\n// TODO(chokobole): Need to use universal reference and perfect forwarding\ntemplate <typename Deleter, typename Class, typename... Args>\nstruct HolderCreator<std::unique_ptr<Class, Deleter>, Class(Args...)> {\n  static std::unique_ptr<Class, Deleter> DoCreate(Args... args) {\n    return std::unique_ptr<Class, Deleter>(\n        new Class(std::forward<Args>(args)...));\n  }\n};\n\ntemplate <typename T>\nstruct GetRawPtr {\n  static T* Get(T& v) { return &v; }\n};\n\ntemplate <typename T>\nstruct GetRawPtr<std::shared_ptr<T>> {\n  static T* Get(std::shared_ptr<T>& v) { return v.get(); }\n};\n\ntemplate <typename T, typename Deleter>\nstruct GetRawPtr<std::unique_ptr<T, Deleter>> {\n  static T* Get(std::unique_ptr<T>& v) { return v.get(); }\n};\n\n}  // namespace internal\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_BINDING_HOLDER_UTIL_H_\n"
  },
  {
    "path": "tachyon/base/binding/property_util.h",
    "content": "#ifndef TACHYON_BASE_BINDING_PROPERTY_UTIL_H_\n#define TACHYON_BASE_BINDING_PROPERTY_UTIL_H_\n\n#include <functional>\n#include <type_traits>\n#include <utility>\n\nnamespace tachyon::base {\nnamespace internal {\n\ntemplate <template <typename, typename SFINAE = void> class Predicate>\nstruct PropertyTypeCaster {\n  template <typename T,\n            std::enable_if_t<Predicate<std::decay_t<T>>::value>* = nullptr>\n  static auto cast(T&& v) {\n    return std::forward<T>(v);\n  }\n\n  template <typename T,\n            std::enable_if_t<!Predicate<std::decay_t<T>>::value>* = nullptr>\n  static auto cast(T&& v) {\n    return std::ref(v);\n  }\n};\n\n}  // namespace internal\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_BINDING_PROPERTY_UTIL_H_\n"
  },
  {
    "path": "tachyon/base/binding/test/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"class\",\n    testonly = True,\n    srcs = [\n        \"adder.cc\",\n        \"colored_point.cc\",\n        \"move_only_int.cc\",\n        \"point.cc\",\n        \"rect.cc\",\n        \"variant.cc\",\n    ],\n    hdrs = [\n        \"adder.h\",\n        \"colored_point.h\",\n        \"move_only_int.h\",\n        \"point.h\",\n        \"rect.h\",\n        \"variant.h\",\n    ],\n    deps = [\n        \":enum\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"enum\",\n    testonly = True,\n    srcs = [\"color.cc\"],\n    hdrs = [\"color.h\"],\n    deps = [\"//tachyon/base:logging\"],\n)\n\ntachyon_cc_library(\n    name = \"functions\",\n    testonly = True,\n    srcs = [\"functions.cc\"],\n    hdrs = [\"functions.h\"],\n)\n"
  },
  {
    "path": "tachyon/base/binding/test/adder.cc",
    "content": "#include \"tachyon/base/binding/test/adder.h\"\n\nnamespace tachyon::base::test {\n\nint Adder::Add(int a, int b, int c, int d) {\n  n += a + b + c + d;\n  return n;\n}\n\n// static\nint Adder::SAdd(int a, int b, int c, int d) { return a + b + c + d; }\n\n}  // namespace tachyon::base::test\n"
  },
  {
    "path": "tachyon/base/binding/test/adder.h",
    "content": "#ifndef TACHYON_BASE_BINDING_TEST_ADDER_H_\n#define TACHYON_BASE_BINDING_TEST_ADDER_H_\n\nnamespace tachyon::base::test {\n\nclass Adder {\n public:\n  int Add(int a, int b, int c, int d);\n\n  static int SAdd(int a, int b, int c, int d);\n\n private:\n  int n = 0;\n};\n\n}  // namespace tachyon::base::test\n\n#endif  // TACHYON_BASE_BINDING_TEST_ADDER_H_\n"
  },
  {
    "path": "tachyon/base/binding/test/color.cc",
    "content": "#include \"tachyon/base/binding/test/color.h\"\n\n#include \"tachyon/base/logging.h\"\n\nnamespace tachyon::base::test {\n\nstd::string ColorToString(Color c) {\n  switch (c) {\n    case Color::kRed:\n      return \"red\";\n    case Color::kGreen:\n      return \"green\";\n    case Color::kBlue:\n      return \"blue\";\n  }\n  NOTREACHED();\n  return \"\";\n}\n\n}  // namespace tachyon::base::test\n"
  },
  {
    "path": "tachyon/base/binding/test/color.h",
    "content": "#ifndef TACHYON_BASE_BINDING_TEST_COLOR_H_\n#define TACHYON_BASE_BINDING_TEST_COLOR_H_\n\n#include <string>\n\nnamespace tachyon::base::test {\n\nenum class Color { kRed, kGreen, kBlue };\n\nstd::string ColorToString(Color c);\n\n}  // namespace tachyon::base::test\n\n#endif  // TACHYON_BASE_BINDING_TEST_COLOR_H_\n"
  },
  {
    "path": "tachyon/base/binding/test/colored_point.cc",
    "content": "#include \"tachyon/base/binding/test/colored_point.h\"\n\nnamespace tachyon::base::test {\n\nColoredPoint::ColoredPoint() = default;\n\nColoredPoint::ColoredPoint(int x, int y, Color color)\n    : Point(x, y), color(color) {}\n\nstd::string ColoredPoint::ToString() const {\n  return absl::Substitute(\"($0, $1, $2)\", ColorToString(color), x, y);\n}\n\n}  // namespace tachyon::base::test\n"
  },
  {
    "path": "tachyon/base/binding/test/colored_point.h",
    "content": "#ifndef TACHYON_BASE_BINDING_TEST_COLORED_POINT_H_\n#define TACHYON_BASE_BINDING_TEST_COLORED_POINT_H_\n\n#include <string>\n\n#include \"tachyon/base/binding/test/color.h\"\n#include \"tachyon/base/binding/test/point.h\"\n\nnamespace tachyon::base::test {\n\nstruct ColoredPoint : public Point {\n  Color color;\n\n  ColoredPoint();\n  ColoredPoint(int x, int y, Color color);\n\n  std::string ToString() const override;\n};\n\n}  // namespace tachyon::base::test\n\n#endif  // TACHYON_BASE_BINDING_TEST_COLORED_POINT_H_\n"
  },
  {
    "path": "tachyon/base/binding/test/functions.cc",
    "content": "#include \"tachyon/base/binding/test/functions.h\"\n\nnamespace tachyon::base::test {\n\nnamespace {\nint g_global_value = 0;\n}  // namespace\n\nstd::string Hello() { return \"world\"; }\n\nint Sum(int a, int b) { return a + b; }\n\nint GetGlobalValue() { return g_global_value; }\n\nvoid SetGlobalValue(int v) { g_global_value = v; }\n\nstd::tuple<int, int, int> Next3(int v) {\n  return std::make_tuple(v + 1, v + 2, v + 3);\n}\n\nvoid DoNothing() {}\n\n}  // namespace tachyon::base::test\n"
  },
  {
    "path": "tachyon/base/binding/test/functions.h",
    "content": "#ifndef TACHYON_BASE_BINDING_TEST_FUNCTIONS_H_\n#define TACHYON_BASE_BINDING_TEST_FUNCTIONS_H_\n\n#include <string>\n#include <tuple>\n\nnamespace tachyon::base::test {\n\nstd::string Hello();\n\nint Sum(int a, int b);\n\nint GetGlobalValue();\n\nvoid SetGlobalValue(int v);\n\nstd::tuple<int, int, int> Next3(int v);\n\nvoid DoNothing();\n\n}  // namespace tachyon::base::test\n\n#endif  // TACHYON_BASE_BINDING_TEST_FUNCTIONS_H_\n"
  },
  {
    "path": "tachyon/base/binding/test/move_only_int.cc",
    "content": "#include \"tachyon/base/binding/test/move_only_int.h\"\n\nnamespace tachyon::base::test {\n\nMoveOnlyInt::MoveOnlyInt() : value_(0) {}\n\nMoveOnlyInt::MoveOnlyInt(int value) : value_(value) {}\n\nMoveOnlyInt::MoveOnlyInt(MoveOnlyInt&& other) : value_(other.value_) {\n  other.value_ = 0;\n}\n\nMoveOnlyInt& MoveOnlyInt::operator=(MoveOnlyInt&& other) {\n  value_ = other.value_;\n  other.value_ = 0;\n  return *this;\n}\n\n}  // namespace tachyon::base::test\n"
  },
  {
    "path": "tachyon/base/binding/test/move_only_int.h",
    "content": "#ifndef TACHYON_BASE_BINDING_TEST_MOVE_ONLY_INT_H_\n#define TACHYON_BASE_BINDING_TEST_MOVE_ONLY_INT_H_\n\nnamespace tachyon::base::test {\n\nclass MoveOnlyInt {\n public:\n  MoveOnlyInt();\n  explicit MoveOnlyInt(int value);\n  MoveOnlyInt(MoveOnlyInt&& other);\n  MoveOnlyInt& operator=(MoveOnlyInt&& other);\n\n  void set_value(int value) { value_ = value; }\n\n  int value() const { return value_; }\n\n private:\n  int value_;\n};\n\n}  // namespace tachyon::base::test\n\n#endif  // TACHYON_BASE_BINDING_TEST_MOVE_ONLY_INT_H_\n"
  },
  {
    "path": "tachyon/base/binding/test/point.cc",
    "content": "#include \"tachyon/base/binding/test/point.h\"\n\nnamespace tachyon::base::test {\n\nPoint::Point() = default;\nPoint::Point(int x, int y) : x(x), y(y) {}\n\nPoint::~Point() = default;\n\n// static\ndouble Point::Distance(const Point& p1, const Point& p2) {\n  return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));\n}\n\nstd::string Point::ToString() const {\n  return absl::Substitute(\"($0, $1)\", x, y);\n}\n\nint Point::s_dimension = 2;\n\n}  // namespace tachyon::base::test\n"
  },
  {
    "path": "tachyon/base/binding/test/point.h",
    "content": "#ifndef TACHYON_BASE_BINDING_TEST_POINT_H_\n#define TACHYON_BASE_BINDING_TEST_POINT_H_\n\n#include <math.h>\n\n#include <string>\n\n#include \"absl/strings/substitute.h\"\n\nnamespace tachyon::base::test {\n\nstruct Point {\n  static int s_dimension;\n\n  int x = 0;\n  int y = 0;\n\n  Point();\n  Point(int x, int y);\n  virtual ~Point();\n\n  static double Distance(const Point& p1, const Point& p2);\n\n  static void SetDimension(int dimension) { s_dimension = dimension; }\n  static int GetDimension() { return s_dimension; }\n\n  int GetX() const { return x; }\n  int GetY() const { return y; }\n  void SetX(int x) { this->x = x; }\n  void SetY(int y) { this->y = y; }\n\n  virtual std::string ToString() const;\n};\n\n}  // namespace tachyon::base::test\n\n#endif  // TACHYON_BASE_BINDING_TEST_POINT_H_\n"
  },
  {
    "path": "tachyon/base/binding/test/rect.cc",
    "content": "#include \"tachyon/base/binding/test/rect.h\"\n\nnamespace tachyon::base::test {\n\nRect::Rect() = default;\n\nRect::Rect(const Point& top_left, const Point& bottom_right)\n    : top_left(top_left), bottom_right(bottom_right) {}\n\n}  // namespace tachyon::base::test\n"
  },
  {
    "path": "tachyon/base/binding/test/rect.h",
    "content": "#ifndef TACHYON_BASE_BINDING_TEST_RECT_H_\n#define TACHYON_BASE_BINDING_TEST_RECT_H_\n\n#include \"tachyon/base/binding/test/point.h\"\n\nnamespace tachyon::base::test {\n\nstruct Rect {\n  Point top_left;\n  Point bottom_right;\n\n  Rect();\n  Rect(const Point& top_left, const Point& bottom_right);\n\n  Point& GetTopLeft() { return top_left; }\n  const Point& GetConstTopLeft() const { return top_left; }\n\n  Point& GetBottomRight() { return bottom_right; }\n  const Point& GetConstBottomRight() const { return bottom_right; }\n};\n\n}  // namespace tachyon::base::test\n\n#endif  // TACHYON_BASE_BINDING_TEST_RECT_H_\n"
  },
  {
    "path": "tachyon/base/binding/test/variant.cc",
    "content": "#include \"tachyon/base/binding/test/variant.h\"\n\nnamespace tachyon::base::test {\n\nVariant::Variant(bool b) : b(b) {}\n\nVariant::Variant(int i) : i(i) {}\n\nVariant::Variant(int64_t i64) : i64(i64) {}\n\nVariant::Variant(const std::string& s) : s(s) {}\n\nVariant::Variant(const std::vector<int>& ivec) : ivec(ivec) {}\n\nVariant::Variant(int i, const std::string& s) : i(i), s(s) {}\n\nVariant::Variant(const Point& p) : p(p) {}\n\n}  // namespace tachyon::base::test\n"
  },
  {
    "path": "tachyon/base/binding/test/variant.h",
    "content": "#ifndef TACHYON_BASE_BINDING_TEST_VARIANT_H_\n#define TACHYON_BASE_BINDING_TEST_VARIANT_H_\n\n#include <stdint.h>\n\n#include <string>\n#include <vector>\n\n#include \"tachyon/base/binding/test/point.h\"\n\nnamespace tachyon::base::test {\n\nstruct Variant {\n  explicit Variant(bool b);\n  explicit Variant(int i);\n  explicit Variant(int64_t i64);\n  explicit Variant(const std::string& s);\n  explicit Variant(const std::vector<int>& ivec);\n  Variant(int i, const std::string& s);\n  explicit Variant(const Point& p);\n\n  bool b = false;\n  int i = 0;\n  int64_t i64 = 0;\n  std::string s;\n  std::vector<int> ivec;\n  Point p;\n};\n\n}  // namespace tachyon::base::test\n\n#endif  // TACHYON_BASE_BINDING_TEST_VARIANT_H_\n"
  },
  {
    "path": "tachyon/base/bit_cast.h",
    "content": "// Copyright 2016 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n// clang-format off\n\n#ifndef TACHYON_BASE_BIT_CAST_H_\n#define TACHYON_BASE_BIT_CAST_H_\n\n#include <type_traits>\n\n#include \"tachyon/base/compiler_specific.h\"\n\n#if !HAS_BUILTIN(__builtin_bit_cast)\n#include <string.h>  // memcpy\n#endif\n\nnamespace tachyon::base {\n\n// This is C++20's std::bit_cast<>(). It morally does what\n// `*reinterpret_cast<Dest*>(&source)` does, but the cast/deref pair is\n// undefined behavior, while bit_cast<>() isn't.\ntemplate <class Dest, class Source>\n#if HAS_BUILTIN(__builtin_bit_cast)\nconstexpr\n#else\ninline\n#endif\n    Dest\n    bit_cast(const Source& source) {\n#if HAS_BUILTIN(__builtin_bit_cast)\n  // TODO(thakis): Keep only this codepath once nacl is gone or updated.\n  return __builtin_bit_cast(Dest, source);\n#else\n  static_assert(sizeof(Dest) == sizeof(Source),\n                \"bit_cast requires source and destination to be the same size\");\n  static_assert(std::is_trivially_copyable_v<Dest>,\n                \"bit_cast requires the destination type to be copyable\");\n  static_assert(std::is_trivially_copyable_v<Source>,\n                \"bit_cast requires the source type to be copyable\");\n\n  Dest dest;\n  memcpy(&dest, &source, sizeof(dest));\n  return dest;\n#endif\n}\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_BIT_CAST_H_\n\n// clang-format on\n"
  },
  {
    "path": "tachyon/base/bit_cast_unittest.cc",
    "content": "// Copyright 2016 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n// clang-format off\n#include \"tachyon/base/bit_cast.h\"\n\n#include \"gtest/gtest.h\"\n\nnamespace tachyon::base {\nnamespace {\n\nTEST(BitCastTest, FloatIntFloat) {\n  float f = 3.1415926f;\n  int i = bit_cast<int32_t>(f);\n  float f2 = bit_cast<float>(i);\n  EXPECT_EQ(f, f2);\n}\n\nstruct A {\n  int x;\n};\n\nTEST(BitCastTest, StructureInt) {\n  A a = { 1 };\n  int b = bit_cast<int>(a);\n  EXPECT_EQ(1, b);\n}\n\n}  // namespace\n}  // namespace tachyon::base\n\n// clang-format on\n"
  },
  {
    "path": "tachyon/base/bits.cc",
    "content": "#include \"tachyon/base/bits.h\"\n\nnamespace tachyon::base {\nnamespace bits {\n\nuint64_t BitRev(uint64_t n) {\n#if defined(__clang__) && HAS_BUILTIN(__builtin_convertvector)\n  return __builtin_bitreverse64(n);\n#else\n  size_t count = 63;\n  uint64_t rev = n;\n  while ((n >>= 1) > 0) {\n    rev <<= 1;\n    rev |= n & 1;\n    --count;\n  }\n  rev <<= count;\n  return rev;\n#endif\n}\n\n}  // namespace bits\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/bits.h",
    "content": "// Copyright 2013 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n// This file defines some bit utilities.\n\n#ifndef TACHYON_BASE_BITS_H_\n#define TACHYON_BASE_BITS_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <algorithm>\n#include <type_traits>\n\n#include \"tachyon/base/compiler_specific.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/build/build_config.h\"\n\nnamespace tachyon::base {\nnamespace bits {\n\n// Returns true iff |value| is a power of 2.\n//\n// TODO(pkasting): When C++20 is available, replace with std::has_single_bit().\ntemplate <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>\nconstexpr bool IsPowerOfTwo(T value) {\n  // From \"Hacker's Delight\": Section 2.1 Manipulating Rightmost Bits.\n  //\n  // Only positive integers with a single bit set are powers of two. If only one\n  // bit is set in x (e.g. 0b00000100000000) then |x-1| will have that bit set\n  // to zero and all bits to its right set to 1 (e.g. 0b00000011111111). Hence\n  // |x & (x-1)| is 0 iff x is a power of two.\n  return value > 0 && (value & (value - 1)) == 0;\n}\n\n// Round down |size| to a multiple of alignment, which must be a power of two.\ntemplate <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>\nconstexpr T AlignDown(T size, T alignment) {\n  DCHECK(IsPowerOfTwo(alignment));\n  return size & ~(alignment - 1);\n}\n\n// Move |ptr| back to the previous multiple of alignment, which must be a power\n// of two. Defined for types where sizeof(T) is one byte.\ntemplate <typename T, typename = typename std::enable_if<sizeof(T) == 1>::type>\ninline T* AlignDown(T* ptr, uintptr_t alignment) {\n  return reinterpret_cast<T*>(\n      AlignDown(reinterpret_cast<uintptr_t>(ptr), alignment));\n}\n\n// Round up |size| to a multiple of alignment, which must be a power of two.\ntemplate <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>\nconstexpr T AlignUp(T size, T alignment) {\n  DCHECK(IsPowerOfTwo(alignment));\n  return (size + alignment - 1) & ~(alignment - 1);\n}\n\n// Advance |ptr| to the next multiple of alignment, which must be a power of\n// two. Defined for types where sizeof(T) is one byte.\ntemplate <typename T, typename = typename std::enable_if<sizeof(T) == 1>::type>\ninline T* AlignUp(T* ptr, uintptr_t alignment) {\n  return reinterpret_cast<T*>(\n      AlignUp(reinterpret_cast<uintptr_t>(ptr), alignment));\n}\n\n// CountLeadingZeroBits(value) returns the number of zero bits following the\n// most significant 1 bit in |value| if |value| is non-zero, otherwise it\n// returns {sizeof(T) * 8}.\n// Example: 00100010 -> 2\n//\n// CountTrailingZeroBits(value) returns the number of zero bits preceding the\n// least significant 1 bit in |value| if |value| is non-zero, otherwise it\n// returns {sizeof(T) * 8}.\n// Example: 00100010 -> 1\n//\n// C does not have an operator to do this, but fortunately the various\n// compilers have built-ins that map to fast underlying processor instructions.\n//\n// TODO(pkasting): When C++20 is available, replace with std::countl_zero() and\n// similar.\n\n// __builtin_clz has undefined behaviour for an input of 0, even though there's\n// clearly a return value that makes sense, and even though some processor clz\n// instructions have defined behaviour for 0. We could drop to raw __asm__ to\n// do better, but we'll avoid doing that unless we see proof that we need to.\ntemplate <typename T, int bits = sizeof(T) * 8>\nALWAYS_INLINE constexpr\n    typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) <= 8,\n                            int>::type\n    CountLeadingZeroBits(T value) {\n  static_assert(bits > 0, \"invalid instantiation\");\n  return LIKELY(value)\n             ? bits == 64\n                   ? __builtin_clzll(static_cast<uint64_t>(value))\n                   : __builtin_clz(static_cast<uint32_t>(value)) - (32 - bits)\n             : bits;\n}\n\ntemplate <typename T, int bits = sizeof(T) * 8>\nALWAYS_INLINE constexpr\n    typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) <= 8,\n                            int>::type\n    CountTrailingZeroBits(T value) {\n  return LIKELY(value) ? bits == 64\n                             ? __builtin_ctzll(static_cast<uint64_t>(value))\n                             : __builtin_ctz(static_cast<uint32_t>(value))\n                       : bits;\n}\n\n// Returns the integer i such as 2ⁱ <= n < 2ⁱ⁺¹.\n//\n// There is a common `BitLength` function, which returns the number of bits\n// required to represent a value. Rather than implement that function,\n// use `Log2Floor` and add 1 to the result.\n//\n// TODO(pkasting): When C++20 is available, replace with std::bit_xxx().\ntemplate <typename T, int bits = sizeof(T) * 8>\nconstexpr typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) <= 8,\n                                  int>::type\nLog2Floor(T n) {\n  return bits - 1 - CountLeadingZeroBits(n);\n}\n\n// Returns the integer i such as 2ⁱ⁻¹ < n <= 2ⁱ.\ntemplate <typename T, int bits = sizeof(T) * 8>\nconstexpr typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) <= 8,\n                                  int>::type\nLog2Ceiling(T n) {\n  // When n == 0, we want the function to return -1.\n  // When n == 0, (n - 1) will underflow to 0xFFFFFFFF, which is\n  // why the statement below starts with (n ? 32 : -1).\n  return (n ? bits : -1) - CountLeadingZeroBits(n - 1);\n}\n\n// NOTE(TomTaehoonKim): |base::bits::Log2Ceiling(n)| returns -1 on |n|\n// is 0. This ensures that it returns non negative value even in that case.\ntemplate <typename T, int bits = sizeof(T) * 8>\nconstexpr typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) <= 8,\n                                  uint32_t>::type\nSafeLog2Ceiling(T n) {\n  return static_cast<uint32_t>(std::max(base::bits::Log2Ceiling(n), 0));\n}\n\n// Returns Log2 of a number. Fails if the inputted number is not a power of two.\ntemplate <typename T, int bits = sizeof(T) * 8>\nconstexpr typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) <= 8,\n                                  uint32_t>::type\nCheckedLog2(T n) {\n  CHECK(base::bits::IsPowerOfTwo(n));\n  return CountTrailingZeroBits(n);\n}\n\n// Returns a value of type T with a single bit set in the left-most position.\n// Can be used instead of manually shifting a 1 to the left.\ntemplate <typename T>\nconstexpr T LeftmostBit() {\n  static_assert(std::is_integral<T>::value,\n                \"This function can only be used with integral types.\");\n  T one(1u);\n  return one << (8 * sizeof(T) - 1);\n}\n\nTACHYON_EXPORT uint64_t BitRev(uint64_t n);\n\n// Reverses the |bit_len| least significant bits of |x|.\ninline size_t ReverseBitsLen(size_t x, size_t bit_len) {\n  return BitRev(x) >> (sizeof(size_t) * 8 - bit_len);\n}\n\n}  // namespace bits\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_BITS_H_\n"
  },
  {
    "path": "tachyon/base/bits_unittest.cc",
    "content": "// Copyright 2009 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n// This file contains the unit tests for the bit utilities.\n\n#include \"tachyon/base/bits.h\"\n\n#include <stddef.h>\n\n#include <bitset>\n#include <limits>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/random.h\"\n\nnamespace tachyon::base {\nnamespace bits {\n\nTEST(BitsTest, Log2Floor) {\n  EXPECT_EQ(-1, Log2Floor(uint32_t{0}));\n  EXPECT_EQ(0, Log2Floor(uint32_t{1}));\n  EXPECT_EQ(1, Log2Floor(uint32_t{2}));\n  EXPECT_EQ(1, Log2Floor(uint32_t{3}));\n  EXPECT_EQ(2, Log2Floor(uint32_t{4}));\n  for (int i = 3; i < 31; ++i) {\n    unsigned int value = 1U << i;\n    EXPECT_EQ(i, Log2Floor(value));\n    EXPECT_EQ(i, Log2Floor(value + 1));\n    EXPECT_EQ(i, Log2Floor(value + 2));\n    EXPECT_EQ(i - 1, Log2Floor(value - 1));\n    EXPECT_EQ(i - 1, Log2Floor(value - 2));\n  }\n  EXPECT_EQ(31, Log2Floor(0xffffffffU));\n}\n\nTEST(BitsTest, Log2Ceiling) {\n  EXPECT_EQ(-1, Log2Ceiling(uint32_t{0}));\n  EXPECT_EQ(0, Log2Ceiling(uint32_t{1}));\n  EXPECT_EQ(1, Log2Ceiling(uint32_t{2}));\n  EXPECT_EQ(2, Log2Ceiling(uint32_t{3}));\n  EXPECT_EQ(2, Log2Ceiling(uint32_t{4}));\n  for (int i = 3; i < 31; ++i) {\n    unsigned int value = 1U << i;\n    EXPECT_EQ(i, Log2Ceiling(value));\n    EXPECT_EQ(i + 1, Log2Ceiling(value + 1));\n    EXPECT_EQ(i + 1, Log2Ceiling(value + 2));\n    EXPECT_EQ(i, Log2Ceiling(value - 1));\n    EXPECT_EQ(i, Log2Ceiling(value - 2));\n  }\n  EXPECT_EQ(32, Log2Ceiling(0xffffffffU));\n}\n\nTEST(BitsTest, SafeLog2Ceiling) {\n  EXPECT_EQ(0, SafeLog2Ceiling(uint32_t{0}));\n  EXPECT_EQ(Log2Ceiling(uint32_t{1}), SafeLog2Ceiling(uint32_t{1}));\n  EXPECT_EQ(Log2Ceiling(uint32_t{2}), SafeLog2Ceiling(uint32_t{2}));\n  EXPECT_EQ(Log2Ceiling(uint32_t{3}), SafeLog2Ceiling(uint32_t{3}));\n  EXPECT_EQ(Log2Ceiling(uint32_t{4}), SafeLog2Ceiling(uint32_t{4}));\n  for (int i = 3; i < 31; ++i) {\n    unsigned int value = 1U << i;\n    EXPECT_EQ(Log2Ceiling(value), SafeLog2Ceiling(value));\n    EXPECT_EQ(Log2Ceiling(value + 1), SafeLog2Ceiling(value + 1));\n    EXPECT_EQ(Log2Ceiling(value + 2), SafeLog2Ceiling(value + 2));\n    EXPECT_EQ(Log2Ceiling(value - 1), SafeLog2Ceiling(value - 1));\n    EXPECT_EQ(Log2Ceiling(value - 2), SafeLog2Ceiling(value - 2));\n  }\n  EXPECT_EQ(Log2Ceiling(0xffffffffU), SafeLog2Ceiling(0xffffffffU));\n}\n\nTEST(BitsTest, AlignUp) {\n  static constexpr size_t kSizeTMax = std::numeric_limits<size_t>::max();\n  EXPECT_EQ(0, AlignUp(0, 4));\n  EXPECT_EQ(4, AlignUp(1, 4));\n  EXPECT_EQ(4096, AlignUp(1, 4096));\n  EXPECT_EQ(4096, AlignUp(4096, 4096));\n  EXPECT_EQ(4096, AlignUp(4095, 4096));\n  EXPECT_EQ(8192, AlignUp(4097, 4096));\n  EXPECT_EQ(kSizeTMax - 31, AlignUp(kSizeTMax - 62, size_t{32}));\n  EXPECT_EQ(kSizeTMax / 2 + 1, AlignUp(size_t{1}, kSizeTMax / 2 + 1));\n}\n\nTEST(BitsTest, AlignUpPointer) {\n  static constexpr uintptr_t kUintPtrTMax =\n      std::numeric_limits<uintptr_t>::max();\n  EXPECT_EQ(reinterpret_cast<uint8_t*>(0),\n            AlignUp(reinterpret_cast<uint8_t*>(0), 4));\n  EXPECT_EQ(reinterpret_cast<uint8_t*>(4),\n            AlignUp(reinterpret_cast<uint8_t*>(1), 4));\n  EXPECT_EQ(reinterpret_cast<uint8_t*>(4096),\n            AlignUp(reinterpret_cast<uint8_t*>(1), 4096));\n  EXPECT_EQ(reinterpret_cast<uint8_t*>(4096),\n            AlignUp(reinterpret_cast<uint8_t*>(4096), 4096));\n  EXPECT_EQ(reinterpret_cast<uint8_t*>(4096),\n            AlignUp(reinterpret_cast<uint8_t*>(4095), 4096));\n  EXPECT_EQ(reinterpret_cast<uint8_t*>(8192),\n            AlignUp(reinterpret_cast<uint8_t*>(4097), 4096));\n  EXPECT_EQ(reinterpret_cast<uint8_t*>(kUintPtrTMax - 31),\n            AlignUp(reinterpret_cast<uint8_t*>(kUintPtrTMax - 62), 32));\n  EXPECT_EQ(reinterpret_cast<uint8_t*>(kUintPtrTMax / 2 + 1),\n            AlignUp(reinterpret_cast<uint8_t*>(1), kUintPtrTMax / 2 + 1));\n}\n\nTEST(BitsTest, AlignDown) {\n  static constexpr size_t kSizeTMax = std::numeric_limits<size_t>::max();\n  EXPECT_EQ(0, AlignDown(0, 4));\n  EXPECT_EQ(0, AlignDown(1, 4));\n  EXPECT_EQ(0, AlignDown(1, 4096));\n  EXPECT_EQ(4096, AlignDown(4096, 4096));\n  EXPECT_EQ(0, AlignDown(4095, 4096));\n  EXPECT_EQ(4096, AlignDown(4097, 4096));\n  EXPECT_EQ(kSizeTMax - 63, AlignDown(kSizeTMax - 62, size_t{32}));\n  EXPECT_EQ(kSizeTMax - 31, AlignDown(kSizeTMax, size_t{32}));\n  EXPECT_EQ(0ul, AlignDown(size_t{1}, kSizeTMax / 2 + 1));\n}\n\nTEST(BitsTest, AlignDownPointer) {\n  static constexpr uintptr_t kUintPtrTMax =\n      std::numeric_limits<uintptr_t>::max();\n  EXPECT_EQ(reinterpret_cast<uint8_t*>(0),\n            AlignDown(reinterpret_cast<uint8_t*>(0), 4));\n  EXPECT_EQ(reinterpret_cast<uint8_t*>(0),\n            AlignDown(reinterpret_cast<uint8_t*>(1), 4));\n  EXPECT_EQ(reinterpret_cast<uint8_t*>(0),\n            AlignDown(reinterpret_cast<uint8_t*>(1), 4096));\n  EXPECT_EQ(reinterpret_cast<uint8_t*>(4096),\n            AlignDown(reinterpret_cast<uint8_t*>(4096), 4096));\n  EXPECT_EQ(reinterpret_cast<uint8_t*>(0),\n            AlignDown(reinterpret_cast<uint8_t*>(4095), 4096));\n  EXPECT_EQ(reinterpret_cast<uint8_t*>(4096),\n            AlignDown(reinterpret_cast<uint8_t*>(4097), 4096));\n  EXPECT_EQ(reinterpret_cast<uint8_t*>(kUintPtrTMax - 63),\n            AlignDown(reinterpret_cast<uint8_t*>(kUintPtrTMax - 62), 32));\n  EXPECT_EQ(reinterpret_cast<uint8_t*>(kUintPtrTMax - 31),\n            AlignDown(reinterpret_cast<uint8_t*>(kUintPtrTMax), 32));\n  EXPECT_EQ(reinterpret_cast<uint8_t*>(0),\n            AlignDown(reinterpret_cast<uint8_t*>(1), kUintPtrTMax / 2 + 1));\n}\n\nTEST(BitsTest, CountLeadingZeroBits8) {\n  EXPECT_EQ(8, CountLeadingZeroBits(uint8_t{0}));\n  EXPECT_EQ(7, CountLeadingZeroBits(uint8_t{1}));\n  for (int shift = 0; shift <= 7; ++shift) {\n    EXPECT_EQ(7 - shift,\n              CountLeadingZeroBits(static_cast<uint8_t>(1 << shift)));\n  }\n  EXPECT_EQ(4, CountLeadingZeroBits(uint8_t{0x0f}));\n}\n\nTEST(BitsTest, CountLeadingZeroBits16) {\n  EXPECT_EQ(16, CountLeadingZeroBits(uint16_t{0}));\n  EXPECT_EQ(15, CountLeadingZeroBits(uint16_t{1}));\n  for (int shift = 0; shift <= 15; ++shift) {\n    EXPECT_EQ(15 - shift,\n              CountLeadingZeroBits(static_cast<uint16_t>(1 << shift)));\n  }\n  EXPECT_EQ(4, CountLeadingZeroBits(uint16_t{0x0f0f}));\n}\n\nTEST(BitsTest, CountLeadingZeroBits32) {\n  EXPECT_EQ(32, CountLeadingZeroBits(uint32_t{0}));\n  EXPECT_EQ(31, CountLeadingZeroBits(uint32_t{1}));\n  for (int shift = 0; shift <= 31; ++shift) {\n    EXPECT_EQ(31 - shift, CountLeadingZeroBits(uint32_t{1} << shift));\n  }\n  EXPECT_EQ(4, CountLeadingZeroBits(uint32_t{0x0f0f0f0f}));\n}\n\nTEST(BitsTest, CountTrailingZeroBits8) {\n  EXPECT_EQ(8, CountTrailingZeroBits(uint8_t{0}));\n  EXPECT_EQ(7, CountTrailingZeroBits(uint8_t{128}));\n  for (int shift = 0; shift <= 7; ++shift) {\n    EXPECT_EQ(shift, CountTrailingZeroBits(static_cast<uint8_t>(1 << shift)));\n  }\n  EXPECT_EQ(4, CountTrailingZeroBits(uint8_t{0xf0}));\n}\n\nTEST(BitsTest, CountTrailingZeroBits16) {\n  EXPECT_EQ(16, CountTrailingZeroBits(uint16_t{0}));\n  EXPECT_EQ(15, CountTrailingZeroBits(uint16_t{32768}));\n  for (int shift = 0; shift <= 15; ++shift) {\n    EXPECT_EQ(shift, CountTrailingZeroBits(static_cast<uint16_t>(1 << shift)));\n  }\n  EXPECT_EQ(4, CountTrailingZeroBits(uint16_t{0xf0f0}));\n}\n\nTEST(BitsTest, CountTrailingZeroBits32) {\n  EXPECT_EQ(32, CountTrailingZeroBits(uint32_t{0}));\n  EXPECT_EQ(31, CountTrailingZeroBits(uint32_t{1} << 31));\n  for (int shift = 0; shift <= 31; ++shift) {\n    EXPECT_EQ(shift, CountTrailingZeroBits(uint32_t{1} << shift));\n  }\n  EXPECT_EQ(4, CountTrailingZeroBits(uint32_t{0xf0f0f0f0}));\n}\n\nTEST(BitsTest, CountLeadingZeroBits64) {\n  EXPECT_EQ(64, CountLeadingZeroBits(uint64_t{0}));\n  EXPECT_EQ(63, CountLeadingZeroBits(uint64_t{1}));\n  for (int shift = 0; shift <= 63; ++shift) {\n    EXPECT_EQ(63 - shift, CountLeadingZeroBits(uint64_t{1} << shift));\n  }\n  EXPECT_EQ(4, CountLeadingZeroBits(uint64_t{0x0f0f0f0f0f0f0f0f}));\n}\n\nTEST(BitsTest, CountTrailingZeroBits64) {\n  EXPECT_EQ(64, CountTrailingZeroBits(uint64_t{0}));\n  EXPECT_EQ(63, CountTrailingZeroBits(uint64_t{1} << 63));\n  for (int shift = 0; shift <= 31; ++shift) {\n    EXPECT_EQ(shift, CountTrailingZeroBits(uint64_t{1} << shift));\n  }\n  EXPECT_EQ(4, CountTrailingZeroBits(uint64_t{0xf0f0f0f0f0f0f0f0}));\n}\n\nTEST(BitsTest, CountLeadingZeroBitsSizeT) {\n#if defined(ARCH_CPU_64_BITS)\n  EXPECT_EQ(64, CountLeadingZeroBits(size_t{0}));\n  EXPECT_EQ(63, CountLeadingZeroBits(size_t{1}));\n  EXPECT_EQ(32, CountLeadingZeroBits(size_t{1} << 31));\n  EXPECT_EQ(1, CountLeadingZeroBits(size_t{1} << 62));\n  EXPECT_EQ(0, CountLeadingZeroBits(size_t{1} << 63));\n#else\n  EXPECT_EQ(32, CountLeadingZeroBits(size_t{0}));\n  EXPECT_EQ(31, CountLeadingZeroBits(size_t{1}));\n  EXPECT_EQ(1, CountLeadingZeroBits(size_t{1} << 30));\n  EXPECT_EQ(0, CountLeadingZeroBits(size_t{1} << 31));\n#endif  // ARCH_CPU_64_BITS\n}\n\nTEST(BitsTest, CountTrailingZeroBitsSizeT) {\n#if defined(ARCH_CPU_64_BITS)\n  EXPECT_EQ(64, CountTrailingZeroBits(size_t{0}));\n  EXPECT_EQ(63, CountTrailingZeroBits(size_t{1} << 63));\n  EXPECT_EQ(31, CountTrailingZeroBits(size_t{1} << 31));\n  EXPECT_EQ(1, CountTrailingZeroBits(size_t{2}));\n  EXPECT_EQ(0, CountTrailingZeroBits(size_t{1}));\n#else\n  EXPECT_EQ(32, CountTrailingZeroBits(size_t{0}));\n  EXPECT_EQ(31, CountTrailingZeroBits(size_t{1} << 31));\n  EXPECT_EQ(1, CountTrailingZeroBits(size_t{2}));\n  EXPECT_EQ(0, CountTrailingZeroBits(size_t{1}));\n#endif  // ARCH_CPU_64_BITS\n}\n\nTEST(BitsTest, PowerOfTwo) {\n  EXPECT_FALSE(IsPowerOfTwo(-1));\n  EXPECT_FALSE(IsPowerOfTwo(0));\n  EXPECT_TRUE(IsPowerOfTwo(1));\n  EXPECT_TRUE(IsPowerOfTwo(2));\n  // Unsigned 64 bit cases.\n  for (uint32_t i = 2; i < 64; i++) {\n    const uint64_t val = uint64_t{1} << i;\n    EXPECT_FALSE(IsPowerOfTwo(val - 1));\n    EXPECT_TRUE(IsPowerOfTwo(val));\n    EXPECT_FALSE(IsPowerOfTwo(val + 1));\n  }\n  // Signed 64 bit cases.\n  for (uint32_t i = 2; i < 63; i++) {\n    const int64_t val = int64_t{1} << i;\n    EXPECT_FALSE(IsPowerOfTwo(val - 1));\n    EXPECT_TRUE(IsPowerOfTwo(val));\n    EXPECT_FALSE(IsPowerOfTwo(val + 1));\n  }\n  // Signed integers with only the last bit set are negative, not powers of two.\n  EXPECT_FALSE(IsPowerOfTwo(int64_t{1} << 63));\n}\n\nTEST(BitsTest, LeftMostBit) {\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wsign-compare\"\n  // Construction of a signed type from an unsigned one of the same width\n  // preserves all bits. Explicitly confirming this behavior here to illustrate\n  // correctness of reusing unsigned literals to test behavior of signed types.\n  // Using signed literals does not work with EXPECT_EQ.\n  static_assert(int64_t(0xFFFFFFFFFFFFFFFFu) == 0xFFFFFFFFFFFFFFFFl,\n                \"Comparing signed with unsigned literals compares bits.\");\n  static_assert((0xFFFFFFFFFFFFFFFFu ^ 0xFFFFFFFFFFFFFFFFl) == 0,\n                \"Signed and unsigned literals have the same bits set\");\n#pragma GCC diagnostic pop\n\n  uint64_t unsigned_long_long_value = 0x8000000000000000u;\n  EXPECT_EQ(LeftmostBit<uint64_t>(), unsigned_long_long_value);\n  EXPECT_EQ(LeftmostBit<int64_t>(), int64_t(unsigned_long_long_value));\n\n  uint32_t unsigned_long_value = 0x80000000u;\n  EXPECT_EQ(LeftmostBit<uint32_t>(), unsigned_long_value);\n  EXPECT_EQ(LeftmostBit<int32_t>(), int32_t(unsigned_long_value));\n\n  uint16_t unsigned_short_value = 0x8000u;\n  EXPECT_EQ(LeftmostBit<uint16_t>(), unsigned_short_value);\n  EXPECT_EQ(LeftmostBit<int16_t>(), int16_t(unsigned_short_value));\n\n  uint8_t unsigned_byte_value = 0x80u;\n  EXPECT_EQ(LeftmostBit<uint8_t>(), unsigned_byte_value);\n  EXPECT_EQ(LeftmostBit<int8_t>(), int8_t(unsigned_byte_value));\n}\n\nTEST(BitsTest, BitRev) {\n  std::bitset<64> value;\n  for (size_t i = 0; i < 20; ++i) {\n    value.flip(UniformElement(value));\n  }\n  std::bitset<64> expected;\n  for (size_t i = 0; i < 64; ++i) {\n    expected[i] = value[64 - i - 1];\n  }\n  EXPECT_EQ(BitRev(value.to_ullong()), expected.to_ullong());\n}\n\n}  // namespace bits\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/buffer/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"buffer\",\n    srcs = [\"buffer.cc\"],\n    hdrs = [\"buffer.h\"],\n    deps = [\":read_only_buffer\"],\n)\n\ntachyon_cc_library(\n    name = \"copyable\",\n    hdrs = [\"copyable.h\"],\n    deps = [\n        \":buffer\",\n        \":copyable_forward\",\n        \"//tachyon/base:logging\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"copyable_forward\",\n    hdrs = [\"copyable_forward.h\"],\n    deps = [\"//tachyon/base/types:cxx20_is_bounded_array\"],\n)\n\ntachyon_cc_library(\n    name = \"endian_auto_reset\",\n    hdrs = [\"endian_auto_reset.h\"],\n    deps = [\n        \":buffer\",\n        \"//tachyon:export\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"read_only_buffer\",\n    srcs = [\"read_only_buffer.cc\"],\n    hdrs = [\"read_only_buffer.h\"],\n    deps = [\n        \":copyable_forward\",\n        \"//tachyon/base:endian\",\n        \"//tachyon/base/numerics:checked_math\",\n        \"@com_google_absl//absl/base:endian\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"string_buffer\",\n    hdrs = [\"string_buffer.h\"],\n    deps = [\":buffer\"],\n)\n\ntachyon_cc_library(\n    name = \"vector_buffer\",\n    srcs = [\"vector_buffer.cc\"],\n    hdrs = [\"vector_buffer.h\"],\n    deps = [\":buffer\"],\n)\n\ntachyon_cc_unittest(\n    name = \"buffer_unittests\",\n    srcs = [\"buffer_unittest.cc\"],\n    deps = [\n        \":copyable\",\n        \":vector_buffer\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/base/buffer/buffer.cc",
    "content": "#include \"tachyon/base/buffer/buffer.h\"\n\n#include <string.h>\n\n#include \"tachyon/base/numerics/checked_math.h\"\n\nnamespace tachyon::base {\n\nbool Buffer::WriteAt(size_t buffer_offset, const uint8_t* ptr, size_t size) {\n  base::CheckedNumeric<size_t> len = buffer_offset;\n  size_t size_needed;\n  if (!(len + size).AssignIfValid(&size_needed)) return false;\n  if (size_needed > buffer_len_) {\n    if (!Grow(size_needed)) return false;\n  }\n  memcpy(reinterpret_cast<char*>(buffer_) + buffer_offset, ptr, size);\n  buffer_offset_ = buffer_offset + size;\n  return true;\n}\n\n#define WRITE_BE_AT(bytes, bits, type)                               \\\n  bool Buffer::Write##bits##BEAt(size_t buffer_offset, type value) { \\\n    base::CheckedNumeric<size_t> len = buffer_offset;                \\\n    size_t size_needed;                                              \\\n    if (!(len + bytes).AssignIfValid(&size_needed)) return false;    \\\n    if (size_needed > buffer_len_) {                                 \\\n      if (!Grow(size_needed)) return false;                          \\\n    }                                                                \\\n    char* buffer = reinterpret_cast<char*>(buffer_);                 \\\n    absl::big_endian::Store##bits(&buffer[buffer_offset], value);    \\\n    buffer_offset_ = buffer_offset + bytes;                          \\\n    return true;                                                     \\\n  }\n\nWRITE_BE_AT(2, 16, uint16_t)\nWRITE_BE_AT(4, 32, uint32_t)\nWRITE_BE_AT(8, 64, uint64_t)\n\n#undef WRITE_BE_AT\n\n#define WRITE_LE_AT(bytes, bits, type)                               \\\n  bool Buffer::Write##bits##LEAt(size_t buffer_offset, type value) { \\\n    base::CheckedNumeric<size_t> len = buffer_offset;                \\\n    size_t size_needed;                                              \\\n    if (!(len + bytes).AssignIfValid(&size_needed)) return false;    \\\n    if (size_needed > buffer_len_) {                                 \\\n      if (!Grow(size_needed)) return false;                          \\\n    }                                                                \\\n    char* buffer = reinterpret_cast<char*>(buffer_);                 \\\n    absl::little_endian::Store##bits(&buffer[buffer_offset], value); \\\n    buffer_offset_ = buffer_offset + bytes;                          \\\n    return true;                                                     \\\n  }\n\nWRITE_LE_AT(2, 16, uint16_t)\nWRITE_LE_AT(4, 32, uint32_t)\nWRITE_LE_AT(8, 64, uint64_t)\n\n#undef WRITE_LE_AT\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/buffer/buffer.h",
    "content": "#ifndef TACHYON_BASE_BUFFER_BUFFER_H_\n#define TACHYON_BASE_BUFFER_BUFFER_H_\n\n#include \"tachyon/base/buffer/read_only_buffer.h\"\n\nnamespace tachyon::base {\n\nclass TACHYON_EXPORT Buffer : public ReadOnlyBuffer {\n public:\n  Buffer() = default;\n  Buffer(void* buffer, size_t buffer_len)\n      : ReadOnlyBuffer(buffer, buffer_len) {}\n  Buffer(const Buffer& other) = delete;\n  Buffer& operator=(const Buffer& other) = delete;\n  Buffer(Buffer&& other) = default;\n  Buffer& operator=(Buffer&& other) = default;\n  virtual ~Buffer() = default;\n\n  // NOTE(chokobole): Due to the existence of a constant getter named |buffer()|\n  // in the parent class, the name was chosen in snake case.\n  using ReadOnlyBuffer::buffer;\n  void* buffer() { return buffer_; }\n\n  [[nodiscard]] bool Write(const uint8_t* ptr, size_t size) {\n    return WriteAt(buffer_offset_, ptr, size);\n  }\n\n  template <typename T>\n  [[nodiscard]] bool Write(const T& value) {\n    return WriteAt(buffer_offset_, value);\n  }\n\n  [[nodiscard]] bool Write16BE(uint16_t value) {\n    return Write16BEAt(buffer_offset_, value);\n  }\n\n  [[nodiscard]] bool Write16LE(uint16_t value) {\n    return Write16LEAt(buffer_offset_, value);\n  }\n\n  [[nodiscard]] bool Write32BE(uint32_t value) {\n    return Write32BEAt(buffer_offset_, value);\n  }\n\n  [[nodiscard]] bool Write32LE(uint32_t value) {\n    return Write32LEAt(buffer_offset_, value);\n  }\n\n  [[nodiscard]] bool Write64BE(uint64_t value) {\n    return Write64BEAt(buffer_offset_, value);\n  }\n\n  [[nodiscard]] bool Write64LE(uint64_t value) {\n    return Write64LEAt(buffer_offset_, value);\n  }\n\n  template <typename T>\n  [[nodiscard]] bool WriteMany(const T& value) {\n    return Write(value);\n  }\n\n  template <typename T, typename... Args>\n  [[nodiscard]] bool WriteMany(const T& value, const Args&... args) {\n    if (!Write(value)) return false;\n    return WriteMany(args...);\n  }\n\n  // Returns false when either\n  // 1) |buffer_offset| + |size| overflows.\n  // 2) if it is not growable and it tries to write more than\n  // |buffer_len_|.\n  [[nodiscard]] bool WriteAt(size_t buffer_offset, const uint8_t* ptr,\n                             size_t size);\n\n  [[nodiscard]] bool Write16BEAt(size_t buffer_offset, uint16_t ptr);\n  [[nodiscard]] bool Write16LEAt(size_t buffer_offset, uint16_t ptr);\n  [[nodiscard]] bool Write32BEAt(size_t buffer_offset, uint32_t ptr);\n  [[nodiscard]] bool Write32LEAt(size_t buffer_offset, uint32_t ptr);\n  [[nodiscard]] bool Write64BEAt(size_t buffer_offset, uint64_t ptr);\n  [[nodiscard]] bool Write64LEAt(size_t buffer_offset, uint64_t ptr);\n\n#define DEFINE_WRITE_AT(bytes, bits, type)                                \\\n  template <typename T,                                                   \\\n            std::enable_if_t<internal::IsBuiltinSerializable<T>::value && \\\n                             (sizeof(T) == bytes)>* = nullptr>            \\\n  [[nodiscard]] bool WriteAt(size_t buffer_offset, T value) {             \\\n    switch (endian_) {                                                    \\\n      case Endian::kBig:                                                  \\\n        return Write##bits##BEAt(buffer_offset, value);                   \\\n      case Endian::kLittle:                                               \\\n        return Write##bits##LEAt(buffer_offset, value);                   \\\n      case Endian::kNative:                                               \\\n        return WriteAt(buffer_offset,                                     \\\n                       reinterpret_cast<const uint8_t*>(&value), bytes);  \\\n    }                                                                     \\\n    NOTREACHED();                                                         \\\n    return false;                                                         \\\n  }\n\n  DEFINE_WRITE_AT(2, 16, uint16_t)\n  DEFINE_WRITE_AT(4, 32, uint32_t)\n  DEFINE_WRITE_AT(8, 64, uint64_t)\n#undef DEFINE_WRITE_AT\n\n  template <typename T,\n            std::enable_if_t<internal::IsBuiltinSerializable<T>::value &&\n                             !((sizeof(T) == 2) || (sizeof(T) == 4) ||\n                               (sizeof(T) == 8))>* = nullptr>\n  [[nodiscard]] bool WriteAt(size_t buffer_offset, T value) {\n    return WriteAt(buffer_offset, reinterpret_cast<const uint8_t*>(&value),\n                   sizeof(T));\n  }\n\n  template <\n      typename T,\n      std::enable_if_t<internal::IsNonBuiltinSerializable<T>::value>* = nullptr>\n  [[nodiscard]] bool WriteAt(size_t buffer_offset, const T& value) {\n    buffer_offset_ = buffer_offset;\n    return Copyable<T>::WriteTo(value, this);\n  }\n\n  template <typename T>\n  [[nodiscard]] bool WriteManyAt(size_t buffer_offset, const T& value) {\n    return WriteAt(buffer_offset, value);\n  }\n\n  template <typename T, typename... Args>\n  [[nodiscard]] bool WriteManyAt(size_t buffer_offset, const T& value,\n                                 const Args&... args) {\n    if (!WriteAt(buffer_offset, value)) return false;\n    return WriteManyAt(buffer_offset, args...);\n  }\n\n  [[nodiscard]] virtual bool Grow(size_t size) { return false; }\n};\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_BUFFER_BUFFER_H_\n"
  },
  {
    "path": "tachyon/base/buffer/buffer_unittest.cc",
    "content": "#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/buffer/copyable.h\"\n#include \"tachyon/base/buffer/vector_buffer.h\"\n\nnamespace tachyon::base {\n\nenum class Color {\n  kRed,\n  kBlue,\n  kGreen,\n};\n\nTEST(CopyableTest, BuiltInSerializableTest) {\n#define TEST_BUILTIN_TYPES(type)                                   \\\n  EXPECT_TRUE(base::internal::IsBuiltinSerializable<type>::value); \\\n  EXPECT_FALSE(base::internal::IsNonBuiltinSerializable<type>::value)\n\n#define TEST_NON_BUILTIN_TYPES(type)                                \\\n  EXPECT_FALSE(base::internal::IsBuiltinSerializable<type>::value); \\\n  EXPECT_TRUE(base::internal::IsNonBuiltinSerializable<type>::value)\n\n  TEST_BUILTIN_TYPES(bool);\n  TEST_BUILTIN_TYPES(char);\n  TEST_BUILTIN_TYPES(uint16_t);\n  TEST_BUILTIN_TYPES(uint32_t);\n  TEST_BUILTIN_TYPES(uint64_t);\n  TEST_BUILTIN_TYPES(int16_t);\n  TEST_BUILTIN_TYPES(int32_t);\n  TEST_BUILTIN_TYPES(int64_t);\n\n  TEST_NON_BUILTIN_TYPES(Color);\n  TEST_NON_BUILTIN_TYPES(std::string_view);\n  TEST_NON_BUILTIN_TYPES(std::string);\n  TEST_NON_BUILTIN_TYPES(uint64_t[4]);\n  TEST_NON_BUILTIN_TYPES(std::vector<uint64_t>);\n\n  using Array = std::array<uint64_t, 4>;\n  TEST_NON_BUILTIN_TYPES(Array);\n\n#undef TEST_NON_BUILTIN_TYPES\n#undef TEST_BUILTIN_TYPES\n}\n\nTEST(BufferTest, Write) {\n  constexpr char kCharValue = 'c';\n  constexpr int kIntValue = 12345;\n  constexpr bool kBooleanValue = true;\n  Color kColor = Color::kBlue;\n  const char* kCharPtrValue = \"abc\";\n  std::string kStringValue = \"def\";\n  uint64_t kIntBoundedArray[4] = {1, 2, 3, 4};\n  std::vector<int> kIntVector = {5, 6, 7};\n  std::array<int, 4> kIntArray = {8, 9, 10, 11};\n\n  for (Endian endian : {Endian::kNative, Endian::kBig, Endian::kLittle}) {\n    Uint8VectorBuffer write_buf;\n    write_buf.set_endian(endian);\n    ASSERT_TRUE(write_buf.Write(kCharValue));\n    ASSERT_TRUE(write_buf.Write(kIntValue));\n    ASSERT_TRUE(write_buf.Write(kBooleanValue));\n    ASSERT_TRUE(write_buf.Write(kColor));\n    ASSERT_TRUE(write_buf.Write(kCharPtrValue));\n    ASSERT_TRUE(write_buf.Write(kStringValue));\n    ASSERT_TRUE(write_buf.Write(kIntBoundedArray));\n    ASSERT_TRUE(write_buf.Write(kIntVector));\n    ASSERT_TRUE(write_buf.Write(kIntArray));\n\n    Buffer read_buf(write_buf.buffer(), write_buf.buffer_len());\n    read_buf.set_endian(endian);\n    char c;\n    ASSERT_TRUE(read_buf.Read(&c));\n    int i;\n    ASSERT_TRUE(read_buf.Read(&i));\n    bool b;\n    ASSERT_TRUE(read_buf.Read(&b));\n    Color color;\n    ASSERT_TRUE(read_buf.Read(&color));\n    std::string s;\n    ASSERT_TRUE(read_buf.Read(&s));\n    std::string s2;\n    ASSERT_TRUE(read_buf.Read(&s2));\n    uint64_t iba[4];\n    ASSERT_TRUE(read_buf.Read(iba));\n    std::vector<int> iv;\n    ASSERT_TRUE(read_buf.Read(&iv));\n    std::array<int, 4> ia;\n    ASSERT_TRUE(read_buf.Read(&ia));\n    ASSERT_TRUE(read_buf.Done());\n    EXPECT_EQ(c, kCharValue);\n    EXPECT_EQ(i, kIntValue);\n    EXPECT_EQ(b, kBooleanValue);\n    EXPECT_EQ(color, kColor);\n    EXPECT_EQ(s, kCharPtrValue);\n    EXPECT_EQ(s2, kStringValue);\n    EXPECT_THAT(iba, testing::ElementsAreArray(iba));\n    EXPECT_EQ(iv, kIntVector);\n    EXPECT_EQ(ia, kIntArray);\n  }\n}\n\nTEST(BufferTest, WriteMany) {\n  constexpr char kCharValue = 'c';\n  constexpr int kIntValue = 12345;\n  constexpr bool kBooleanValue = true;\n  constexpr Color kColor = Color::kBlue;\n  const char* kCharPtrValue = \"abc\";\n  std::string kStringValue = \"def\";\n  uint64_t kIntBoundedArray[4] = {1, 2, 3, 4};\n  std::vector<int> kIntVector = {5, 6, 7};\n  std::array<int, 4> kIntArray = {8, 9, 10, 11};\n\n  for (Endian endian : {Endian::kNative, Endian::kBig, Endian::kLittle}) {\n    Uint8VectorBuffer write_buf;\n    write_buf.set_endian(endian);\n    ASSERT_TRUE(write_buf.WriteMany(kCharValue, kIntValue, kBooleanValue,\n                                    kColor, kCharPtrValue, kStringValue,\n                                    kIntBoundedArray, kIntVector, kIntArray));\n\n    Buffer read_buf(write_buf.buffer(), write_buf.buffer_len());\n    read_buf.set_endian(endian);\n    char c;\n    int i;\n    bool b;\n    Color color;\n    std::string s;\n    std::string s2;\n    uint64_t iba[4];\n    std::vector<int> iv;\n    std::array<int, 4> ia;\n    ASSERT_TRUE(read_buf.ReadMany(&c, &i, &b, &color, &s, &s2, iba, &iv, &ia));\n    EXPECT_EQ(c, kCharValue);\n    EXPECT_EQ(i, kIntValue);\n    EXPECT_EQ(b, kBooleanValue);\n    EXPECT_EQ(color, kColor);\n    EXPECT_EQ(s, kCharPtrValue);\n    EXPECT_EQ(s2, kStringValue);\n    EXPECT_THAT(iba, testing::ElementsAreArray(iba));\n    EXPECT_EQ(iv, kIntVector);\n    EXPECT_EQ(ia, kIntArray);\n    ASSERT_TRUE(read_buf.Done());\n  }\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/buffer/copyable.h",
    "content": "#ifndef TACHYON_BASE_BUFFER_COPYABLE_H_\n#define TACHYON_BASE_BUFFER_COPYABLE_H_\n\n#include <array>\n#include <numeric>\n#include <string>\n#include <vector>\n\n#include \"absl/types/span.h\"\n\n#include \"tachyon/base/buffer/buffer.h\"\n#include \"tachyon/base/buffer/copyable_forward.h\"\n#include \"tachyon/base/logging.h\"\n\nnamespace tachyon::base {\n\n// NOTE(chokobole): We omitted |WriteTo()| and |ReadFrom()| on purpose.\n// The reason why |EstimateSize()| is implemented here is to enable other\n// |Copyable<T>| to use this. (e.g, Copyable<int>::EstimateSize(1))\ntemplate <typename T>\nclass Copyable<T, std::enable_if_t<internal::IsBuiltinSerializable<T>::value>> {\n public:\n  static size_t EstimateSize(const T& value) { return sizeof(T); }\n};\n\ntemplate <typename T>\nclass Copyable<T, std::enable_if_t<std::is_enum_v<T>>> {\n public:\n  static bool WriteTo(const T& value, Buffer* buffer) {\n    return buffer->Write(static_cast<std::underlying_type_t<T>>(value));\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer, T* value) {\n    std::underlying_type_t<T> underlying_value;\n    if (!buffer.Read(&underlying_value)) return false;\n    *value = static_cast<T>(underlying_value);\n    return true;\n  }\n\n  static size_t EstimateSize(T value) { return sizeof(T); }\n};\n\ntemplate <typename CharTy>\nclass Copyable<std::basic_string_view<CharTy>> {\n public:\n  static bool WriteTo(const std::basic_string_view<CharTy>& value,\n                      Buffer* buffer) {\n    if (!buffer->Write(value.size())) return false;\n    return buffer->Write(reinterpret_cast<const uint8_t*>(value.data()),\n                         value.size());\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer,\n                       std::basic_string_view<CharTy>* value) {\n    NOTREACHED() << \"Not supported ReadFrom for std::basic_string_view<CharTy>\";\n    return false;\n  }\n\n  static size_t EstimateSize(const std::basic_string_view<CharTy>& value) {\n    return sizeof(size_t) + value.size();\n  }\n};\n\ntemplate <typename CharTy>\nclass Copyable<std::basic_string<CharTy>> {\n public:\n  static bool WriteTo(const std::basic_string<CharTy>& value, Buffer* buffer) {\n    if (!buffer->Write(value.size())) return false;\n    return buffer->Write(reinterpret_cast<const uint8_t*>(value.data()),\n                         value.size());\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer,\n                       std::basic_string<CharTy>* value) {\n    size_t size;\n    if (!buffer.Read(&size)) return false;\n    value->resize(size);\n    return buffer.Read(reinterpret_cast<uint8_t*>(value->data()), size);\n  }\n\n  static size_t EstimateSize(const std::basic_string<CharTy>& value) {\n    return sizeof(size_t) + value.size();\n  }\n};\n\ntemplate <typename CharTy>\nclass Copyable<\n    const CharTy*,\n    std::enable_if_t<\n        std::is_same_v<CharTy, char> || std::is_same_v<CharTy, wchar_t> ||\n        std::is_same_v<CharTy, char16_t> || std::is_same_v<CharTy, char32_t>>> {\n public:\n  static bool WriteTo(const CharTy* value, Buffer* buffer) {\n    size_t length = std::char_traits<CharTy>::length(value);\n    if (!buffer->Write(length)) return false;\n    return buffer->Write(reinterpret_cast<const uint8_t*>(value),\n                         length * sizeof(CharTy));\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer, const CharTy** value) {\n    NOTREACHED() << \"Not supported ReadFrom for const CharTy*\";\n    return false;\n  }\n\n  static size_t EstimateSize(const CharTy* value) {\n    return sizeof(size_t) +\n           std::char_traits<CharTy>::length(value) * sizeof(CharTy);\n  }\n};\n\ntemplate <typename T, size_t N>\nclass Copyable<T[N]> {\n public:\n  static bool WriteTo(const T* values, Buffer* buffer) {\n    for (size_t i = 0; i < N; ++i) {\n      if (!buffer->Write(values[i])) return false;\n    }\n    return true;\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer, T* values) {\n    for (size_t i = 0; i < N; ++i) {\n      if (!buffer.Read(&values[i])) return false;\n    }\n    return true;\n  }\n\n  static size_t EstimateSize(const T* values) {\n    return std::accumulate(values, &values[N], 0,\n                           [](size_t total, const T& value) {\n                             return total + base::EstimateSize(value);\n                           });\n  }\n};\n\ntemplate <typename T>\nclass Copyable<std::vector<T>> {\n public:\n  static bool WriteTo(const std::vector<T>& values, Buffer* buffer) {\n    if (!buffer->Write(values.size())) return false;\n    for (const T& value : values) {\n      if (!buffer->Write(value)) return false;\n    }\n    return true;\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer, std::vector<T>* values) {\n    size_t size;\n    if (!buffer.Read(&size)) return false;\n    values->resize(size);\n    for (T& value : (*values)) {\n      if (!buffer.Read(&value)) return false;\n    }\n    return true;\n  }\n\n  static size_t EstimateSize(const std::vector<T>& values) {\n    return std::accumulate(values.begin(), values.end(), sizeof(size_t),\n                           [](size_t total, const T& value) {\n                             return total + base::EstimateSize(value);\n                           });\n  }\n};\n\ntemplate <typename T, size_t N>\nclass Copyable<std::array<T, N>> {\n public:\n  static bool WriteTo(const std::array<T, N>& values, Buffer* buffer) {\n    for (const T& value : values) {\n      if (!buffer->Write(value)) return false;\n    }\n    return true;\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer, std::array<T, N>* values) {\n    for (T& value : (*values)) {\n      if (!buffer.Read(&value)) return false;\n    }\n    return true;\n  }\n\n  static size_t EstimateSize(const std::array<T, N>& values) {\n    return std::accumulate(values.begin(), values.end(), 0,\n                           [](size_t total, const T& value) {\n                             return total + base::EstimateSize(value);\n                           });\n  }\n};\n\ntemplate <typename T>\nclass Copyable<absl::Span<T>> {\n public:\n  static bool WriteTo(absl::Span<T> values, Buffer* buffer) {\n    if (!buffer->Write(values.size())) return false;\n    for (const T& value : values) {\n      if (!buffer->Write(value)) return false;\n    }\n    return true;\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer, absl::Span<T>* values) {\n    NOTREACHED() << \"Not supported ReadFrom for absl::Span<T>\";\n    return false;\n  }\n\n  static size_t EstimateSize(absl::Span<T> values) {\n    return std::accumulate(values.begin(), values.end(), sizeof(size_t),\n                           [](size_t total, const T& value) {\n                             return total + base::EstimateSize(value);\n                           });\n  }\n};\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_BUFFER_COPYABLE_H_\n"
  },
  {
    "path": "tachyon/base/buffer/copyable_forward.h",
    "content": "#ifndef TACHYON_BASE_BUFFER_COPYABLE_FORWARD_H_\n#define TACHYON_BASE_BUFFER_COPYABLE_FORWARD_H_\n\n#include <type_traits>\n\n#include \"tachyon/base/types/cxx20_is_bounded_array.h\"\n\nnamespace tachyon::base {\n\nclass Buffer;\nclass ReadOnlyBuffer;\n\n// NOTE: Do not implement for builtin serializable.\n// See tachyon/base/buffer/read_only_buffer.h\ntemplate <typename T, typename SFINAE = void>\nclass Copyable;\n\ntemplate <typename, typename = void>\nstruct IsCopyable : std::false_type {};\n\ntemplate <typename T>\nstruct IsCopyable<\n    T,\n    std::void_t<\n        decltype(Copyable<T>::WriteTo(std::declval<const T&>(),\n                                      std::declval<Buffer*>())),\n        decltype(Copyable<T>::ReadFrom(\n            std::declval<const ReadOnlyBuffer&>(),\n            std::declval<std::conditional_t<is_bounded_array_v<T>, T, T*>>())),\n        decltype(Copyable<T>::EstimateSize(std::declval<const T&>()))>>\n    : std::true_type {};\n\ntemplate <typename... Args>\nsize_t EstimateSize(const Args&... args) {\n  return (... + Copyable<Args>::EstimateSize(args));\n}\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_BUFFER_COPYABLE_FORWARD_H_\n"
  },
  {
    "path": "tachyon/base/buffer/endian_auto_reset.h",
    "content": "#ifndef TACHYON_BASE_BUFFER_ENDIAN_AUTO_RESET_H_\n#define TACHYON_BASE_BUFFER_ENDIAN_AUTO_RESET_H_\n\n#include \"tachyon/base/buffer/buffer.h\"\n#include \"tachyon/export.h\"\n\nnamespace tachyon::base {\n\nstruct TACHYON_EXPORT EndianAutoReset {\n  explicit EndianAutoReset(const base::ReadOnlyBuffer& buffer,\n                           base::Endian endian)\n      : buffer(buffer), old_endian(buffer.endian()) {\n    buffer.set_endian(endian);\n  }\n  ~EndianAutoReset() { buffer.set_endian(old_endian); }\n\n  const base::ReadOnlyBuffer& buffer;\n  base::Endian old_endian;\n};\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_BUFFER_ENDIAN_AUTO_RESET_H_\n"
  },
  {
    "path": "tachyon/base/buffer/read_only_buffer.cc",
    "content": "#include \"tachyon/base/buffer/read_only_buffer.h\"\n\n#include <string.h>\n\nnamespace tachyon::base {\n\nbool ReadOnlyBuffer::ReadAt(size_t buffer_offset, uint8_t* ptr,\n                            size_t size) const {\n  base::CheckedNumeric<size_t> len = buffer_offset;\n  size_t size_needed;\n  if (!(len + size).AssignIfValid(&size_needed)) return false;\n  if (size_needed > buffer_len_) {\n    return false;\n  }\n  const char* buffer = reinterpret_cast<const char*>(buffer_);\n  memcpy(ptr, &buffer[buffer_offset], size);\n  buffer_offset_ = buffer_offset + size;\n  return true;\n}\n\n#define READ_BE_AT(bytes, bits, type)                                    \\\n  bool ReadOnlyBuffer::Read##bits##BEAt(size_t buffer_offset, type* ptr) \\\n      const {                                                            \\\n    base::CheckedNumeric<size_t> len = buffer_offset;                    \\\n    size_t size_needed;                                                  \\\n    if (!(len + bytes).AssignIfValid(&size_needed)) return false;        \\\n    if (size_needed > buffer_len_) {                                     \\\n      return false;                                                      \\\n    }                                                                    \\\n    const char* buffer = reinterpret_cast<char*>(buffer_);               \\\n    type value = absl::big_endian::Load##bits(&buffer[buffer_offset]);   \\\n    memcpy(ptr, &value, bytes);                                          \\\n    buffer_offset_ = buffer_offset + bytes;                              \\\n    return true;                                                         \\\n  }\n\nREAD_BE_AT(2, 16, uint16_t)\nREAD_BE_AT(4, 32, uint32_t)\nREAD_BE_AT(8, 64, uint64_t)\n\n#undef READ_BE_AT\n\n#define READ_LE_AT(bytes, bits, type)                                     \\\n  bool ReadOnlyBuffer::Read##bits##LEAt(size_t buffer_offset, type* ptr)  \\\n      const {                                                             \\\n    base::CheckedNumeric<size_t> len = buffer_offset;                     \\\n    size_t size_needed;                                                   \\\n    if (!(len + bytes).AssignIfValid(&size_needed)) return false;         \\\n    if (size_needed > buffer_len_) {                                      \\\n      return false;                                                       \\\n    }                                                                     \\\n    const char* buffer = reinterpret_cast<const char*>(buffer_);          \\\n    type value = absl::little_endian::Load##bits(&buffer[buffer_offset]); \\\n    memcpy(ptr, &value, bytes);                                           \\\n    buffer_offset_ = buffer_offset + bytes;                               \\\n    return true;                                                          \\\n  }\n\nREAD_LE_AT(2, 16, uint16_t)\nREAD_LE_AT(4, 32, uint32_t)\nREAD_LE_AT(8, 64, uint64_t)\n\n#undef READ_LE_AT\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/buffer/read_only_buffer.h",
    "content": "#ifndef TACHYON_BASE_BUFFER_READ_ONLY_BUFFER_H_\n#define TACHYON_BASE_BUFFER_READ_ONLY_BUFFER_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <type_traits>\n#include <utility>\n\n#include \"absl/base/internal/endian.h\"\n\n#include \"tachyon/base/buffer/copyable_forward.h\"\n#include \"tachyon/base/endian.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/numerics/checked_math.h\"\n\nnamespace tachyon::base {\nnamespace internal {\n\ntemplate <typename, typename = void>\nstruct IsBuiltinSerializable : std::false_type {};\n\ntemplate <typename T>\nstruct IsBuiltinSerializable<T, std::enable_if_t<std::is_fundamental_v<T>>>\n    : std::true_type {};\n\ntemplate <typename, typename = void>\nstruct IsNonBuiltinSerializable : std::false_type {};\n\ntemplate <typename T>\nstruct IsNonBuiltinSerializable<\n    T,\n    std::enable_if_t<!IsBuiltinSerializable<T>::value && IsCopyable<T>::value>>\n    : std::true_type {};\n\n}  // namespace internal\n\nclass TACHYON_EXPORT ReadOnlyBuffer {\n public:\n  ReadOnlyBuffer() = default;\n  ReadOnlyBuffer(const void* buffer, size_t buffer_len)\n      : buffer_(const_cast<void*>(buffer)),\n        buffer_offset_(0),\n        buffer_len_(buffer_len) {}\n  ReadOnlyBuffer(const ReadOnlyBuffer& other) = delete;\n  ReadOnlyBuffer& operator=(const ReadOnlyBuffer& other) = delete;\n  ReadOnlyBuffer(ReadOnlyBuffer&& other)\n      : buffer_(std::exchange(other.buffer_, nullptr)),\n        buffer_offset_(std::exchange(other.buffer_offset_, 0)),\n        buffer_len_(std::exchange(other.buffer_len_, 0)) {}\n  ReadOnlyBuffer& operator=(ReadOnlyBuffer&& other) {\n    buffer_ = std::exchange(other.buffer_, nullptr);\n    buffer_offset_ = std::exchange(other.buffer_offset_, 0);\n    buffer_len_ = std::exchange(other.buffer_len_, 0);\n    return *this;\n  }\n  ~ReadOnlyBuffer() = default;\n\n  Endian endian() const { return endian_; }\n  // NOTE(chokobole): This is changed to const method to ensure\n  // |const ReadOnlyBuffer&| calls this.\n  void set_endian(Endian endian) const { endian_ = endian; }\n\n  const void* buffer() const { return buffer_; }\n\n  size_t buffer_offset() const { return buffer_offset_; }\n  // NOTE(chokobole): This is changed to const method to ensure\n  // |const ReadOnlyBuffer&| calls this.\n  void set_buffer_offset(size_t buffer_offset) const {\n    buffer_offset_ = buffer_offset;\n  }\n\n  size_t buffer_len() const { return buffer_len_; }\n\n  [[nodiscard]] bool Done() const { return buffer_offset_ == buffer_len_; }\n\n  // Returns false when either\n  // 1) |buffer_offset| + |size| overflows.\n  // 2) if it tries to read more than |buffer_len_|.\n  [[nodiscard]] bool ReadAt(size_t buffer_offset, uint8_t* ptr,\n                            size_t size) const;\n\n  template <\n      typename Ptr, typename T = std::remove_pointer_t<Ptr>,\n      std::enable_if_t<std::is_pointer_v<Ptr> &&\n                       internal::IsBuiltinSerializable<T>::value>* = nullptr>\n  [[nodiscard]] bool ReadAt(size_t buffer_offset, Ptr ptr) const {\n    switch (endian_) {\n      case Endian::kBig:\n        if constexpr (sizeof(T) == 8) {\n          return Read64BEAt(buffer_offset, reinterpret_cast<uint64_t*>(ptr));\n        } else if constexpr (sizeof(T) == 4) {\n          return Read32BEAt(buffer_offset, reinterpret_cast<uint32_t*>(ptr));\n        } else if constexpr (sizeof(T) == 2) {\n          return Read16BEAt(buffer_offset, reinterpret_cast<uint16_t*>(ptr));\n        }\n      case Endian::kLittle:\n        if constexpr (sizeof(T) == 8) {\n          return Read64LEAt(buffer_offset, reinterpret_cast<uint64_t*>(ptr));\n        } else if constexpr (sizeof(T) == 4) {\n          return Read32LEAt(buffer_offset, reinterpret_cast<uint32_t*>(ptr));\n        } else if constexpr (sizeof(T) == 2) {\n          return Read16LEAt(buffer_offset, reinterpret_cast<uint16_t*>(ptr));\n        }\n      case Endian::kNative:\n        return ReadAt(buffer_offset, reinterpret_cast<uint8_t*>(ptr),\n                      sizeof(T));\n    }\n    NOTREACHED();\n    return false;\n  }\n\n  [[nodiscard]] bool Read16BEAt(size_t buffer_offset, uint16_t* ptr) const;\n  [[nodiscard]] bool Read16LEAt(size_t buffer_offset, uint16_t* ptr) const;\n  [[nodiscard]] bool Read32BEAt(size_t buffer_offset, uint32_t* ptr) const;\n  [[nodiscard]] bool Read32LEAt(size_t buffer_offset, uint32_t* ptr) const;\n  [[nodiscard]] bool Read64BEAt(size_t buffer_offset, uint64_t* ptr) const;\n  [[nodiscard]] bool Read64LEAt(size_t buffer_offset, uint64_t* ptr) const;\n\n  template <\n      typename T,\n      std::enable_if_t<internal::IsNonBuiltinSerializable<T>::value>* = nullptr>\n  [[nodiscard]] bool ReadAt(size_t buffer_offset, T* value) const {\n    buffer_offset_ = buffer_offset;\n    return Copyable<T>::ReadFrom(*this, value);\n  }\n\n  template <typename T, size_t N>\n  [[nodiscard]] bool ReadAt(size_t buffer_offset, T (&array)[N]) const {\n    buffer_offset_ = buffer_offset;\n    for (size_t i = 0; i < N; ++i) {\n      if (!Read(&array[i])) return false;\n    }\n    return true;\n  }\n\n  template <typename T>\n  [[nodiscard]] bool ReadPtrAt(size_t buffer_offset, T** ptr,\n                               size_t ptr_num) const {\n    size_t size = sizeof(T) * ptr_num;\n    base::CheckedNumeric<size_t> len = buffer_offset;\n    size_t size_needed;\n    if (!(len + size).AssignIfValid(&size_needed)) return false;\n    if (size_needed > buffer_len_) {\n      return false;\n    }\n    const char* buffer = reinterpret_cast<const char*>(buffer_);\n    *ptr = const_cast<T*>(reinterpret_cast<const T*>(&buffer[buffer_offset]));\n    buffer_offset_ = buffer_offset + size;\n    return true;\n  }\n\n  [[nodiscard]] bool Read(uint8_t* ptr, size_t size) const {\n    return ReadAt(buffer_offset_, ptr, size);\n  }\n\n  template <typename T>\n  [[nodiscard]] bool Read(T&& value) const {\n    return ReadAt(buffer_offset_, std::forward<T>(value));\n  }\n\n  template <typename T>\n  [[nodiscard]] bool ReadPtr(T** ptr, size_t ptr_num) const {\n    return ReadPtrAt(buffer_offset_, ptr, ptr_num);\n  }\n\n  [[nodiscard]] bool Read16BE(uint16_t* ptr) const {\n    return Read16BEAt(buffer_offset_, ptr);\n  }\n\n  [[nodiscard]] bool Read16LE(uint16_t* ptr) const {\n    return Read16LEAt(buffer_offset_, ptr);\n  }\n\n  [[nodiscard]] bool Read32BE(uint32_t* ptr) const {\n    return Read32BEAt(buffer_offset_, ptr);\n  }\n\n  [[nodiscard]] bool Read32LE(uint32_t* ptr) const {\n    return Read32LEAt(buffer_offset_, ptr);\n  }\n\n  [[nodiscard]] bool Read64BE(uint64_t* ptr) const {\n    return Read64BEAt(buffer_offset_, ptr);\n  }\n\n  [[nodiscard]] bool Read64LE(uint64_t* ptr) const {\n    return Read64LEAt(buffer_offset_, ptr);\n  }\n\n  template <typename T>\n  [[nodiscard]] bool ReadMany(T&& value) const {\n    return Read(std::forward<T>(value));\n  }\n\n  template <typename T, typename... Args>\n  [[nodiscard]] bool ReadMany(T&& value, Args&&... args) const {\n    if (!Read(std::forward<T>(value))) return false;\n    return ReadMany(std::forward<Args>(args)...);\n  }\n\n  template <typename T, size_t N>\n  [[nodiscard]] bool ReadManyAt(size_t buffer_offset, T&& value) const {\n    return ReadAt(buffer_offset, std::forward<T>(value));\n  }\n\n  template <typename T, typename... Args>\n  [[nodiscard]] bool ReadManyAt(size_t buffer_offset, T&& value,\n                                Args&&... args) const {\n    if (!ReadAt(buffer_offset, std::forward<T>(value))) return false;\n    return ReadManyAt(buffer_offset, std::forward<Args>(args)...);\n  }\n\n protected:\n  mutable Endian endian_ = Endian::kNative;\n\n  void* buffer_ = nullptr;\n  mutable size_t buffer_offset_ = 0;\n  size_t buffer_len_ = 0;\n};\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_BUFFER_READ_ONLY_BUFFER_H_\n"
  },
  {
    "path": "tachyon/base/buffer/string_buffer.h",
    "content": "#ifndef TACHYON_BASE_BUFFER_STRING_BUFFER_H_\n#define TACHYON_BASE_BUFFER_STRING_BUFFER_H_\n\n#include <string>\n#include <utility>\n\n#include \"tachyon/base/buffer/buffer.h\"\n\nnamespace tachyon::base {\n\nclass TACHYON_EXPORT StringBuffer : public Buffer {\n public:\n  StringBuffer() = default;\n  StringBuffer(const StringBuffer& other) = delete;\n  StringBuffer& operator=(const StringBuffer& other) = delete;\n  StringBuffer(StringBuffer&& other)\n      : Buffer(std::move(other)),\n        owned_buffer_(std::move(other.owned_buffer_)) {}\n  StringBuffer& operator=(StringBuffer&& other) {\n    Buffer::operator=(std::move(other));\n    owned_buffer_ = std::move(other.owned_buffer_);\n    return *this;\n  }\n  ~StringBuffer() override = default;\n\n  const std::string& owned_buffer() const { return owned_buffer_; }\n\n  std::string&& TakeOwnedBuffer() && { return std::move(owned_buffer_); }\n\n  [[nodiscard]] bool Grow(size_t size) override {\n    owned_buffer_.resize(size);\n    buffer_ = owned_buffer_.data();\n    buffer_len_ = owned_buffer_.size();\n    return true;\n  }\n\n protected:\n  std::string owned_buffer_;\n};\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_BUFFER_STRING_BUFFER_H_\n"
  },
  {
    "path": "tachyon/base/buffer/vector_buffer.cc",
    "content": "#include \"tachyon/base/buffer/vector_buffer.h\"\n\nnamespace tachyon::base {\n\ntemplate class VectorBuffer<char>;\ntemplate class VectorBuffer<uint8_t>;\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/buffer/vector_buffer.h",
    "content": "#ifndef TACHYON_BASE_BUFFER_VECTOR_BUFFER_H_\n#define TACHYON_BASE_BUFFER_VECTOR_BUFFER_H_\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/buffer/buffer.h\"\n\nnamespace tachyon::base {\n\ntemplate <typename T>\nclass VectorBuffer : public Buffer {\n public:\n  static_assert(sizeof(T) == 1);\n\n  VectorBuffer() = default;\n  explicit VectorBuffer(const std::vector<T>& owned_buffer)\n      : owned_buffer_(owned_buffer) {\n    UpdateBuffer();\n  }\n  explicit VectorBuffer(std::vector<T>&& owned_buffer)\n      : owned_buffer_(std::move(owned_buffer)) {\n    UpdateBuffer();\n  }\n  VectorBuffer(const VectorBuffer& other) = delete;\n  VectorBuffer& operator=(const VectorBuffer& other) = delete;\n  VectorBuffer(VectorBuffer&& other)\n      : Buffer(std::move(other)),\n        owned_buffer_(std::move(other.owned_buffer_)) {}\n  VectorBuffer& operator=(VectorBuffer&& other) {\n    Buffer::operator=(std::move(other));\n    owned_buffer_ = std::move(other.owned_buffer_);\n    return *this;\n  }\n  ~VectorBuffer() override = default;\n\n  const std::vector<T>& owned_buffer() const { return owned_buffer_; }\n\n  std::vector<T>&& TakeOwnedBuffer() && { return std::move(owned_buffer_); }\n\n  [[nodiscard]] bool Grow(size_t size) override {\n    owned_buffer_.resize(size);\n    UpdateBuffer();\n    return true;\n  }\n\n protected:\n  void UpdateBuffer() {\n    buffer_ = owned_buffer_.data();\n    buffer_len_ = owned_buffer_.size();\n  }\n\n  std::vector<T> owned_buffer_;\n};\n\nusing CharVectorBuffer = VectorBuffer<char>;\nusing Uint8VectorBuffer = VectorBuffer<uint8_t>;\n\nextern template class TACHYON_EXPORT VectorBuffer<char>;\nextern template class TACHYON_EXPORT VectorBuffer<uint8_t>;\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_BUFFER_VECTOR_BUFFER_H_\n"
  },
  {
    "path": "tachyon/base/color/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"color\",\n    srcs = [\"color.cc\"],\n    hdrs = [\n        \"color.h\",\n        \"named_color.h\",\n    ],\n    deps = [\n        \"//tachyon:export\",\n        \"//tachyon/base/strings:string_number_conversions\",\n        \"//tachyon/base/strings:string_util\",\n        \"@com_google_absl//absl/strings:str_format\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"color_conversions\",\n    srcs = [\"color_conversions.cc\"],\n    hdrs = [\"color_conversions.h\"],\n    deps = [\":color\"],\n)\n\ntachyon_cc_unittest(\n    name = \"color_unittests\",\n    srcs = [\n        \"color_conversions_unittest.cc\",\n        \"color_unittest.cc\",\n    ],\n    deps = [\n        \":color\",\n        \":color_conversions\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/base/color/color.cc",
    "content": "// Copyright (c) 2019 The Color Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/color/color.h\"\n\n#include <vector>\n\n#include \"absl/strings/str_format.h\"\n#include \"absl/strings/str_split.h\"\n\n#include \"tachyon/base/strings/string_number_conversions.h\"\n#include \"tachyon/base/strings/string_util.h\"\n\nnamespace tachyon::base {\nnamespace {\n\n// 0 ~ 255\ntemplate <typename T,\n          std::enable_if_t<std::is_same<T, uint8_t>::value>* = nullptr>\nbool StringToNumber(std::string_view input, uint8_t* number) {\n  unsigned n;\n  if (!StringToUint(input, &n)) return false;\n  if (n > 255) return false;\n  *number = static_cast<uint8_t>(n);\n  return true;\n}\n\ntemplate <typename T,\n          std::enable_if_t<std::is_same<T, float>::value>* = nullptr>\nbool StringToNumber(std::string_view input, float* number) {\n  float n;\n  if (!StringToFloat(input, &n)) return false;\n  if (n > 1) return false;\n  *number = n;\n  return true;\n}\n\n// Parse comma separated numbers.\ntemplate <size_t N, typename T>\nbool ParseCommaSeparatedNumbers(std::string_view* input, T* numbers) {\n  std::vector<std::string_view> v =\n      absl::StrSplit(*input, ',', absl::SkipWhitespace());\n  if (v.size() != N) return false;\n  T n_temps[N];\n  for (size_t i = 0; i < N; ++i) {\n    if (!StringToNumber<T>(v[i], &n_temps[i])) return false;\n  }\n  for (size_t i = 0; i < N; ++i) {\n    numbers[i] = n_temps[i];\n  }\n  return true;\n}\n\nbool ParseRgbNumbers(std::string_view* input, uint8_t* numbers) {\n  return ParseCommaSeparatedNumbers<3>(input, numbers);\n}\n\nbool ParseRgbaNumbers(std::string_view* input, uint8_t* numbers) {\n  return ParseCommaSeparatedNumbers<4>(input, numbers);\n}\n\nbool ParseHsvNumbers(std::string_view* input, float* numbers) {\n  Hsv hsv;\n  if (!ParseCommaSeparatedNumbers<3>(input, hsv.array)) return false;\n  if (!hsv.IsValid()) return false;\n  memcpy(numbers, hsv.array, sizeof(float) * 3);\n  return true;\n}\n\nbool ParseHsvaNumbers(std::string_view* input, float* numbers) {\n  Hsv hsv;\n  if (!ParseCommaSeparatedNumbers<4>(input, hsv.array)) return false;\n  if (!hsv.IsValid()) return false;\n  memcpy(numbers, hsv.array, sizeof(float) * 4);\n  return true;\n}\n\nbool ConsumeHex(std::string_view* input, uint8_t* c) {\n  std::string_view c_sv(input->data(), 2);\n  uint32_t n;\n  if (!HexStringToUint(c_sv, &n)) return false;\n  if (n > 255) return false;\n  *c = static_cast<uint8_t>(n);\n  input->remove_prefix(2);\n  return true;\n}\n\nbool ConsumeHexRgba(std::string_view* input, uint8_t* r, uint8_t* g, uint8_t* b,\n                    uint8_t* a) {\n  uint8_t r_temp;\n  uint8_t g_temp;\n  uint8_t b_temp;\n  uint8_t a_temp = 255;\n  size_t len = input->length();\n  if (len == 6 || len == 8) {\n    if (!(ConsumeHex(input, &r_temp) && ConsumeHex(input, &g_temp) &&\n          ConsumeHex(input, &b_temp)))\n      return false;\n    if (len == 8) {\n      if (!ConsumeHex(input, &a_temp)) return false;\n    }\n  } else {\n    return false;\n  }\n\n  *r = r_temp;\n  *g = g_temp;\n  *b = b_temp;\n  *a = a_temp;\n\n  return true;\n}\n\n}  // namespace\n\nconst RgbaIndexes kRgbIndexes{0, 1, 2, -1};\nconst RgbaIndexes kRgbaIndexes{0, 1, 2, 3};\nconst RgbaIndexes kBgrIndexes{2, 1, 0, -1};\nconst RgbaIndexes kBgraIndexes{2, 1, 0, 3};\nconst RgbaIndexes kArgbIndexes{1, 2, 3, 0};\n\nstd::string Rgba::ToString() const { return ToRgbaString(); }\n\nstd::string Rgba::ToRgbString() const {\n  return absl::StrFormat(\"rgb(%u, %u, %u)\", r, g, b);\n}\n\nstd::string Rgba::ToRgbaString() const {\n  return absl::StrFormat(\"rgba(%u, %u, %u, %u)\", r, g, b, a);\n}\n\nstd::string Rgba::ToRgbHexString() const {\n  return absl::StrFormat(\"#%02x%02x%02x\", r, g, b);\n}\n\nstd::string Rgba::ToRgbaHexString() const {\n  return absl::StrFormat(\"#%02x%02x%02x%02x\", r, g, b, a);\n}\n\nbool Rgba::FromString(const std::string& text) {\n  std::string_view input(text);\n  if (ConsumePrefix(&input, \"rgb(\") && ConsumeSuffix(&input, \")\")) {\n    if (ParseRgbNumbers(&input, array)) {\n      a = 255;\n      return true;\n    }\n  } else if (ConsumePrefix(&input, \"rgba(\") && ConsumeSuffix(&input, \")\")) {\n    return ParseRgbaNumbers(&input, array);\n  } else if (ConsumePrefix(&input, \"#\")) {\n    return ConsumeHexRgba(&input, &r, &g, &b, &a);\n  }\n\n  return false;\n}\n\nRgba Rgba::Swap(const RgbaIndexes& rgba_indexes) const {\n  Rgba swapped;\n  swapped.array[rgba_indexes.r] = r;\n  swapped.array[rgba_indexes.g] = g;\n  swapped.array[rgba_indexes.b] = b;\n  if (rgba_indexes.a != -1) {\n    swapped.array[rgba_indexes.a] = a;\n  } else {\n    swapped.array[rgba_indexes.a] = 255;\n  }\n  return swapped;\n}\n\nstd::string Hsv::ToString() const { return ToHsvaString(); }\n\nstd::string Hsv::ToHsvString() const {\n  return absl::StrFormat(\"hsv(%f, %f, %f)\", h, s, v);\n}\n\nstd::string Hsv::ToHsvaString() const {\n  return absl::StrFormat(\"hsva(%f, %f, %f, %f)\", h, s, v, a);\n}\n\nbool Hsv::FromString(const std::string& text) {\n  std::string_view input(text);\n  if (ConsumePrefix(&input, \"hsv(\") && ConsumeSuffix(&input, \")\")) {\n    if (ParseHsvNumbers(&input, array)) {\n      a = 1;\n      return true;\n    }\n  } else if (ConsumePrefix(&input, \"hsva(\") && ConsumeSuffix(&input, \")\")) {\n    return ParseHsvaNumbers(&input, array);\n  } else {\n    return false;\n  }\n\n  return false;\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/color/color.h",
    "content": "// Copyright (c) 2019 The Color Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_COLOR_COLOR_H_\n#define TACHYON_BASE_COLOR_COLOR_H_\n\n#include <stdint.h>\n\n#include <array>\n#include <string>\n\n#include \"tachyon/export.h\"\n\nnamespace tachyon::base {\nstruct TACHYON_EXPORT RgbaIndexes {\n  int r;\n  int g;\n  int b;\n  int a;\n};\n\nTACHYON_EXPORT extern const RgbaIndexes kRgbIndexes;\nTACHYON_EXPORT extern const RgbaIndexes kRgbaIndexes;\nTACHYON_EXPORT extern const RgbaIndexes kBgrIndexes;\nTACHYON_EXPORT extern const RgbaIndexes kBgraIndexes;\nTACHYON_EXPORT extern const RgbaIndexes kArgbIndexes;\n\n// Rgba class is copyable, assignable, and occupies 32-bits per instance.\n// As a result, prefer passing them by value:\n//   void MyFunction(Rgba arg);\n// If circumstances require, you may also pass by const reference:\n//   void MyFunction(const Rgba& arg);  // Not preferred.\nstruct TACHYON_EXPORT Rgba {\n  constexpr Rgba() : Rgba(0, 0, 0, 0) {}\n  constexpr Rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a = 255)\n      : r(r), g(g), b(b), a(a) {}\n  constexpr explicit Rgba(uint32_t rgba) : rgba(rgba) {}\n  constexpr explicit Rgba(const uint8_t* data)\n      : Rgba(data[0], data[1], data[2], data[3]) {}\n  // This is convenience constructor.\n  // e.g) Without this, Rgba(0, 0, 0) makes you annoyed!\n  constexpr Rgba(int r, int g, int b, int a = 255)\n      : Rgba(static_cast<uint8_t>(r), static_cast<uint8_t>(g),\n             static_cast<uint8_t>(b), static_cast<uint8_t>(a)) {}\n  // |r|, |g|, |b| and |a| should be in 0 to 1.\n  constexpr Rgba(float r, float g, float b, float a = 1)\n      : Rgba(static_cast<uint8_t>(r * 255), static_cast<uint8_t>(g * 255),\n             static_cast<uint8_t>(b * 255), static_cast<uint8_t>(a * 255)) {}\n  // This is convenience constructor.\n  // e.g) Without this, Rgba(0.0, 0.0, 0.0) makes you annoyed!\n  constexpr Rgba(double r, double g, double b, double a = 1)\n      : Rgba(static_cast<float>(r), static_cast<float>(g),\n             static_cast<float>(b), static_cast<float>(a)) {}\n  constexpr explicit Rgba(const float* data)\n      : Rgba(data[0], data[1], data[2], data[3]) {}\n\n  // Converts to std::string. It's redirected to ToRgbaString().\n  std::string ToString() const;\n  // Converts to std::string.\n  // e.g) if (r, g, b, a) is (0, 0, 0, 1), then it returns \"rgb(0, 0, 0)\".\n  std::string ToRgbString() const;\n  // Converts to std::string.\n  // e.g) if (r, g, b, a) is (0, 0, 0, 1) and |hex| is false, then it returns\n  // \"rgba(0, 0, 0, 1)\". Or |hex| is true, it returns \"#00000001\".\n  std::string ToRgbaString() const;\n  // Converts to std::string.\n  // e.g) if (r, g, b, a) is (255, 0, 0, 1), it returns \"#ff0000\".\n  std::string ToRgbHexString() const;\n  // Converts to std::string.\n  // e.g) if (r, g, b, a) is (255, 0, 0, 1), it returns \"#ff000001\".\n  std::string ToRgbaHexString() const;\n  // Converts from std::string. Returns true if succeeded.\n  // Expected form is \"rgb(r, g, b)\", \"rgba(r, g, b, a), #abcdef or \"#ABCDEF\".\n  bool FromString(const std::string& text);\n\n  std::array<float, 3> ToFloatingArray3() const {\n    return {r / 255.f, g / 255.f, b / 255.f};\n  }\n\n  std::array<float, 4> ToFloatingArray4() const {\n    return {r / 255.f, g / 255.f, b / 255.f, a / 255.f};\n  }\n\n  constexpr bool IsValid() const { return true; }\n\n  constexpr bool IsTransparent() const { return a != 255; }\n\n  constexpr bool IsOpaque() const { return a == 255; }\n\n  Rgba Swap(const RgbaIndexes& rgba_indexes) const;\n\n  constexpr Rgba PremultipliedAlpha() const {\n    return {static_cast<uint8_t>(static_cast<float>(r) * a / 255.f),\n            static_cast<uint8_t>(static_cast<float>(g) * a / 255.f),\n            static_cast<uint8_t>(static_cast<float>(b) * a / 255.f), a};\n  }\n\n  union {\n    struct {\n      uint8_t r;\n      uint8_t g;\n      uint8_t b;\n      uint8_t a;\n    };\n    uint8_t array[4];\n    uint32_t rgba;\n  };\n};\n\ninline bool operator==(Rgba rgb, Rgba rgb2) { return rgb.rgba == rgb2.rgba; }\n\ninline bool operator!=(Rgba rgb, Rgba rgb2) { return !operator==(rgb, rgb2); }\n\nstruct TACHYON_EXPORT Hsv {\n  constexpr Hsv() : Hsv(0, 0, 0, 1) {}\n  constexpr Hsv(float h, float s, float v, float a = 1)\n      : h(h), s(s), v(v), a(a) {}\n  constexpr explicit Hsv(const float* data)\n      : Hsv(data[0], data[1], data[2], data[3]) {}\n\n  Hsv(const Hsv& other) = default;\n  Hsv& operator=(const Hsv& other) = default;\n\n  // Converts to std::string. It's redirected to ToHsvaString().\n  std::string ToString() const;\n  // Converts to std::string.\n  // e.g) if (h, s, v, a) is (0, 0, 0, 1), then it returns \"hsv(0, 0, 0)\".\n  std::string ToHsvString() const;\n  // e.g) if (h, s, v, a) is (0, 0, 0, 1), then it returns \"hsva(0, 0, 0, 1)\".\n  std::string ToHsvaString() const;\n  // Converts from std::string. Returns true if succeeded.\n  // Expected form is \"hsv(h, s, v)\" or \"hsva(h, s, v, a)\".\n  bool FromString(const std::string& text);\n\n  constexpr bool IsValid() const {\n    return h >= 0 && h <= 360 && s >= 0 && s <= 1 && v >= 0 && v <= 1 &&\n           a >= 0 && a <= 1;\n  }\n\n  constexpr bool IsTransparent() const { return a != 1; }\n\n  constexpr bool IsOpaque() const { return a == 1; }\n\n  union {\n    struct {\n      float h;\n      float s;\n      float v;\n      float a;\n    };\n    float array[4];\n  };\n};\n\ninline bool operator==(const Hsv& hsv, const Hsv& hsv2) {\n  return hsv.h == hsv2.h && hsv.s == hsv2.s && hsv.v == hsv2.v &&\n         hsv.a == hsv2.a;\n}\n\ninline bool operator!=(const Hsv& hsv, const Hsv& hsv2) {\n  return !operator==(hsv, hsv2);\n}\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_COLOR_COLOR_H_\n"
  },
  {
    "path": "tachyon/base/color/color_conversions.cc",
    "content": "// Copyright (c) 2019 The Color Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/color/color_conversions.h\"\n\n#include <algorithm>\n#include <cmath>\n\nnamespace tachyon::base {\n\nRgba HsvToRgba(const Hsv& hsv) {\n  if (!hsv.IsValid()) return {};\n\n  float h = hsv.h;\n  float s = hsv.s;\n  float v = hsv.v;\n  uint8_t r, g, b, a = hsv.a * 255;\n\n  float H = h / 60;\n\n  float c = v * s;\n  float x = c * (1 - std::abs(fmod(H, 2) - 1));\n  float m = v - c;\n\n  switch (static_cast<uint8_t>(H)) {\n    case 0:\n      r = (c + m) * 255;\n      g = (x + m) * 255;\n      b = m * 255;\n      break;\n    case 1:\n      r = (x + m) * 255;\n      g = (c + m) * 255;\n      b = m * 255;\n      break;\n    case 2:\n      r = m * 255;\n      g = (c + m) * 255;\n      b = (x + m) * 255;\n      break;\n    case 3:\n      r = m * 255;\n      g = (x + m) * 255;\n      b = (c + m) * 255;\n      break;\n    case 4:\n      r = (x + m) * 255;\n      g = m * 255;\n      b = (c + m) * 255;\n      break;\n    default:\n      r = (c + m) * 255;\n      g = m * 255;\n      b = (x + m) * 255;\n      break;\n  }\n\n  return {r, g, b, a};\n}\n\nHsv RgbaToHsv(Rgba rgba) {\n  uint8_t r = rgba.r;\n  uint8_t g = rgba.g;\n  uint8_t b = rgba.b;\n  float a = rgba.a / 255;\n\n  uint8_t min = std::min(r, std::min(g, b));\n  uint8_t max = std::max(r, std::max(g, b));\n  uint8_t delta = max - min;\n\n  float v = static_cast<float>(max) / 255;\n\n  if (delta == 0) {\n    return {0, 0, v, a};\n  }\n\n  float s = static_cast<float>(delta) / max;\n  float h;\n  if (r == max) {\n    h = static_cast<float>(g - b) / delta;\n  } else if (g == max) {\n    h = 2 + static_cast<float>(b - r) / delta;\n  } else {  // b == max\n    h = 4 + static_cast<float>(r - g) / delta;\n  }\n  h *= 60;\n  if (h < 0) {\n    h += 360;\n  }\n\n  return {h, s, v, a};\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/color/color_conversions.h",
    "content": "// Copyright (c) 2019 The Color Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_COLOR_COLOR_CONVERSIONS_H_\n#define TACHYON_BASE_COLOR_COLOR_CONVERSIONS_H_\n\n#include \"tachyon/base/color/color.h\"\n#include \"tachyon/export.h\"\n\nnamespace tachyon::base {\n\nTACHYON_EXPORT Rgba HsvToRgba(const Hsv& hsv);\n\nTACHYON_EXPORT Hsv RgbaToHsv(Rgba rgba);\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_COLOR_COLOR_CONVERSIONS_H_\n"
  },
  {
    "path": "tachyon/base/color/color_conversions_unittest.cc",
    "content": "// Copyright (c) 2019 The Color Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/color/color_conversions.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/color/named_color.h\"\n\nnamespace tachyon::base {\n\nTEST(ColorConversions, RgbaAndHsv) {\n  Hsv hsv = RgbaToHsv(colors::kMagenta);\n  EXPECT_EQ(300, hsv.h);\n  EXPECT_EQ(1, hsv.s);\n  EXPECT_EQ(1, hsv.v);\n  Rgba rgba = HsvToRgba(hsv);\n  EXPECT_EQ(colors::kMagenta, rgba);\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/color/color_unittest.cc",
    "content": "// Copyright (c) 2019 The Color Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/color/color.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/color/named_color.h\"\n\nnamespace tachyon::base {\n\nTEST(Rgba, StringConversion) {\n  EXPECT_EQ(colors::kGold.ToRgbString(), \"rgb(255, 215, 0)\");\n  EXPECT_EQ(colors::kGold.ToRgbaString(), \"rgba(255, 215, 0, 255)\");\n  EXPECT_EQ(colors::kGold.ToRgbHexString(), \"#ffd700\");\n  EXPECT_EQ(colors::kGold.ToRgbaHexString(), \"#ffd700ff\");\n\n  Rgba rgba;\n  EXPECT_FALSE(rgba.FromString(\"rgb(255, 215)\"));\n  EXPECT_FALSE(rgba.FromString(\"rgb(255, 215, 0\"));\n  EXPECT_TRUE(rgba.FromString(\"rgb(255, 215, 0)\"));\n  EXPECT_EQ(colors::kGold, rgba);\n  EXPECT_TRUE(rgba.FromString(\"rgba(255, 215, 0, 255)\"));\n  EXPECT_EQ(colors::kGold, rgba);\n  EXPECT_FALSE(rgba.FromString(\"rgb(255, 215, 0, 255\"));\n  EXPECT_FALSE(rgba.FromString(\"rgb(255, 215, 0, 255, 0)\"));\n  EXPECT_FALSE(rgba.FromString(\"#ffd7\"));\n  EXPECT_TRUE(rgba.FromString(\"#ffd700\"));\n  EXPECT_FALSE(rgba.FromString(\"#ffd70\"));\n  EXPECT_EQ(colors::kGold, rgba);\n  EXPECT_TRUE(rgba.FromString(\"#ffd700ff\"));\n  EXPECT_EQ(colors::kGold, rgba);\n  EXPECT_FALSE(rgba.FromString(\"#ffd700ff0\"));\n}\n\nTEST(Hsv, StringConversion) {\n  Hsv hsv(0.1, 0.2, 0.3, 0.4);\n  EXPECT_EQ(hsv.ToHsvString(), \"hsv(0.100000, 0.200000, 0.300000)\");\n  EXPECT_EQ(hsv.ToHsvaString(), \"hsva(0.100000, 0.200000, 0.300000, 0.400000)\");\n\n  Hsv hsv2;\n  EXPECT_TRUE(hsv2.FromString(\"hsv(0.1, 0.2, 0.3)\"));\n  EXPECT_EQ(Hsv(0.1, 0.2, 0.3, 1), hsv2);\n  EXPECT_TRUE(hsv2.FromString(\"hsva(0.1, 0.2, 0.3, 0.4)\"));\n  EXPECT_EQ(hsv, hsv2);\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/color/named_color.h",
    "content": "// Copyright (c) 2019 The Color Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_COLOR_NAMED_COLOR_H_\n#define TACHYON_BASE_COLOR_NAMED_COLOR_H_\n\n#include \"tachyon/base/color/color.h\"\n\nnamespace tachyon::base::colors {\n// https://developer.mozilla.org/ko/docs/Web/CSS/color_value\n\n// CSS Level 1\nconstexpr Rgba kBlack = Rgba(0, 0, 0);\nconstexpr Rgba kSilver = Rgba(192, 192, 192);\nconstexpr Rgba kGray = Rgba(128, 128, 128);\nconstexpr Rgba kWhite = Rgba(255, 255, 255);\nconstexpr Rgba kMaroon = Rgba(128, 0, 0);\nconstexpr Rgba kRed = Rgba(255, 0, 0);\nconstexpr Rgba kPurple = Rgba(128, 0, 128);\nconstexpr Rgba kFuchsia = Rgba(255, 0, 255);\nconstexpr Rgba kGreen = Rgba(0, 128, 0);\nconstexpr Rgba kLime = Rgba(0, 255, 0);\nconstexpr Rgba kOlive = Rgba(128, 128, 0);\nconstexpr Rgba kYellow = Rgba(255, 255, 0);\nconstexpr Rgba kNavy = Rgba(0, 0, 128);\nconstexpr Rgba kBlue = Rgba(0, 0, 255);\nconstexpr Rgba kTeal = Rgba(0, 128, 128);\nconstexpr Rgba kAqua = Rgba(0, 255, 255);\n// CSS Level 2(Revision 1)\nconstexpr Rgba kOrange = Rgba(255, 165, 0);\n// CSS Color Module Level 3\nconstexpr Rgba kAliceblue = Rgba(240, 248, 255);\nconstexpr Rgba kAntiquewhite = Rgba(250, 235, 215);\nconstexpr Rgba kAquamarine = Rgba(127, 255, 212);\nconstexpr Rgba kAzure = Rgba(240, 255, 255);\nconstexpr Rgba kBeige = Rgba(245, 245, 220);\nconstexpr Rgba kBisque = Rgba(255, 228, 196);\nconstexpr Rgba kBlanchedalmond = Rgba(255, 235, 205);\nconstexpr Rgba kBlueviolet = Rgba(138, 43, 226);\nconstexpr Rgba kBrown = Rgba(165, 42, 42);\nconstexpr Rgba kBurlywood = Rgba(222, 184, 135);\nconstexpr Rgba kCadetblue = Rgba(95, 158, 160);\nconstexpr Rgba kChartreuse = Rgba(127, 255, 0);\nconstexpr Rgba kChocolate = Rgba(210, 105, 30);\nconstexpr Rgba kCoral = Rgba(255, 127, 80);\nconstexpr Rgba kCornflowerblue = Rgba(100, 149, 237);\nconstexpr Rgba kCornsilk = Rgba(255, 248, 220);\nconstexpr Rgba kCrimson = Rgba(220, 20, 60);\nconstexpr Rgba kCyan = Rgba(0, 255, 255);\nconstexpr Rgba kDarkblue = Rgba(0, 0, 139);\nconstexpr Rgba kDarkcyan = Rgba(0, 139, 139);\nconstexpr Rgba kDarkgoldenrod = Rgba(184, 134, 11);\nconstexpr Rgba kDarkgray = Rgba(169, 169, 169);\nconstexpr Rgba kDarkgreen = Rgba(0, 100, 0);\nconstexpr Rgba kDarkgrey = Rgba(169, 169, 169);\nconstexpr Rgba kDarkkhaki = Rgba(189, 183, 107);\nconstexpr Rgba kDarkmagenta = Rgba(139, 0, 139);\nconstexpr Rgba kDarkolivegreen = Rgba(85, 107, 47);\nconstexpr Rgba kDarkorange = Rgba(255, 140, 0);\nconstexpr Rgba kDarkorchid = Rgba(153, 50, 204);\nconstexpr Rgba kDarkred = Rgba(139, 0, 0);\nconstexpr Rgba kDarksalmon = Rgba(233, 150, 122);\nconstexpr Rgba kDarkseagreen = Rgba(143, 188, 143);\nconstexpr Rgba kDarkslateblue = Rgba(72, 61, 139);\nconstexpr Rgba kDarkslategray = Rgba(47, 79, 79);\nconstexpr Rgba kDarkslategrey = Rgba(47, 79, 79);\nconstexpr Rgba kDarkturquoise = Rgba(0, 206, 209);\nconstexpr Rgba kDarkviolet = Rgba(148, 0, 211);\nconstexpr Rgba kDeeppink = Rgba(255, 20, 147);\nconstexpr Rgba kDeepskyblue = Rgba(0, 191, 255);\nconstexpr Rgba kDimgray = Rgba(105, 105, 105);\nconstexpr Rgba kDimgrey = Rgba(105, 105, 105);\nconstexpr Rgba kDodgerblue = Rgba(30, 144, 255);\nconstexpr Rgba kFirebrick = Rgba(178, 34, 34);\nconstexpr Rgba kFloralwhite = Rgba(255, 250, 240);\nconstexpr Rgba kForestgreen = Rgba(34, 139, 34);\nconstexpr Rgba kGainsboro = Rgba(220, 220, 220);\nconstexpr Rgba kGhostwhite = Rgba(248, 248, 255);\nconstexpr Rgba kGold = Rgba(255, 215, 0);\nconstexpr Rgba kGoldenrod = Rgba(218, 165, 32);\nconstexpr Rgba kGreenyellow = Rgba(173, 255, 47);\nconstexpr Rgba kGrey = Rgba(128, 128, 128);\nconstexpr Rgba kHoneydew = Rgba(240, 255, 240);\nconstexpr Rgba kHotpink = Rgba(255, 105, 180);\nconstexpr Rgba kIndianred = Rgba(205, 92, 92);\nconstexpr Rgba kIndigo = Rgba(75, 0, 130);\nconstexpr Rgba kIvory = Rgba(255, 255, 240);\nconstexpr Rgba kKhaki = Rgba(240, 230, 140);\nconstexpr Rgba kLavender = Rgba(230, 230, 250);\nconstexpr Rgba kLavenderblush = Rgba(255, 240, 245);\nconstexpr Rgba kLawngreen = Rgba(124, 252, 0);\nconstexpr Rgba kLemonchiffon = Rgba(255, 250, 205);\nconstexpr Rgba kLightblue = Rgba(173, 216, 230);\nconstexpr Rgba kLightcoral = Rgba(240, 128, 128);\nconstexpr Rgba kLightcyan = Rgba(224, 255, 255);\nconstexpr Rgba kLightgoldenrodyellow = Rgba(250, 250, 210);\nconstexpr Rgba kLightgray = Rgba(211, 211, 211);\nconstexpr Rgba kLightgreen = Rgba(144, 238, 144);\nconstexpr Rgba kLightgrey = Rgba(211, 211, 211);\nconstexpr Rgba kLightpink = Rgba(255, 182, 193);\nconstexpr Rgba kLightsalmon = Rgba(255, 160, 122);\nconstexpr Rgba kLightseagreen = Rgba(32, 178, 170);\nconstexpr Rgba kLightskyblue = Rgba(135, 206, 250);\nconstexpr Rgba kLightslategray = Rgba(119, 136, 153);\nconstexpr Rgba kLightslategrey = Rgba(119, 136, 153);\nconstexpr Rgba kLightsteelblue = Rgba(176, 196, 222);\nconstexpr Rgba kLightyellow = Rgba(255, 255, 224);\nconstexpr Rgba kLimegreen = Rgba(50, 205, 50);\nconstexpr Rgba kLinen = Rgba(250, 240, 230);\nconstexpr Rgba kMagenta = Rgba(255, 0, 255);\nconstexpr Rgba kMediumaquamarine = Rgba(102, 205, 170);\nconstexpr Rgba kMediumblue = Rgba(0, 0, 205);\nconstexpr Rgba kMediumorchid = Rgba(186, 85, 211);\nconstexpr Rgba kMediumpurple = Rgba(147, 112, 219);\nconstexpr Rgba kMediumseagreen = Rgba(60, 179, 113);\nconstexpr Rgba kMediumslateblue = Rgba(123, 104, 238);\nconstexpr Rgba kMediumspringgreen = Rgba(0, 250, 154);\nconstexpr Rgba kMediumturquoise = Rgba(72, 209, 204);\nconstexpr Rgba kMediumvioletred = Rgba(199, 21, 133);\nconstexpr Rgba kMidnightblue = Rgba(25, 25, 112);\nconstexpr Rgba kMintcream = Rgba(245, 255, 250);\nconstexpr Rgba kMistyrose = Rgba(255, 228, 225);\nconstexpr Rgba kMoccasin = Rgba(255, 228, 181);\nconstexpr Rgba kNavajowhite = Rgba(255, 222, 173);\nconstexpr Rgba kOldlace = Rgba(253, 245, 230);\nconstexpr Rgba kOlivedrab = Rgba(107, 142, 35);\nconstexpr Rgba kOrangered = Rgba(255, 69, 0);\nconstexpr Rgba kOrchid = Rgba(218, 112, 214);\nconstexpr Rgba kPalegoldenrod = Rgba(238, 232, 170);\nconstexpr Rgba kPalegreen = Rgba(152, 251, 152);\nconstexpr Rgba kPaleturquoise = Rgba(175, 238, 238);\nconstexpr Rgba kPalevioletred = Rgba(219, 112, 147);\nconstexpr Rgba kPapayawhip = Rgba(255, 239, 213);\nconstexpr Rgba kPeachpuff = Rgba(255, 218, 185);\nconstexpr Rgba kPeru = Rgba(205, 133, 63);\nconstexpr Rgba kPink = Rgba(255, 192, 203);\nconstexpr Rgba kPlum = Rgba(221, 160, 221);\nconstexpr Rgba kPowderblue = Rgba(176, 224, 230);\nconstexpr Rgba kRosybrown = Rgba(188, 143, 143);\nconstexpr Rgba kRoyalblue = Rgba(65, 105, 225);\nconstexpr Rgba kSaddlebrown = Rgba(139, 69, 19);\nconstexpr Rgba kSalmon = Rgba(250, 128, 114);\nconstexpr Rgba kSandybrown = Rgba(244, 164, 96);\nconstexpr Rgba kSeagreen = Rgba(46, 139, 87);\nconstexpr Rgba kSeashell = Rgba(255, 245, 238);\nconstexpr Rgba kSienna = Rgba(160, 82, 45);\nconstexpr Rgba kSkyblue = Rgba(135, 206, 235);\nconstexpr Rgba kSlateblue = Rgba(106, 90, 205);\nconstexpr Rgba kSlategray = Rgba(112, 128, 144);\nconstexpr Rgba kSlategrey = Rgba(112, 128, 144);\nconstexpr Rgba kSnow = Rgba(255, 250, 250);\nconstexpr Rgba kSpringgreen = Rgba(0, 255, 127);\nconstexpr Rgba kSteelblue = Rgba(70, 130, 180);\nconstexpr Rgba kTan = Rgba(210, 180, 140);\nconstexpr Rgba kThistle = Rgba(216, 191, 216);\nconstexpr Rgba kTomato = Rgba(255, 99, 71);\nconstexpr Rgba kTurquoise = Rgba(64, 224, 208);\nconstexpr Rgba kViolet = Rgba(238, 130, 238);\nconstexpr Rgba kWheat = Rgba(245, 222, 179);\nconstexpr Rgba kWhitesmoke = Rgba(245, 245, 245);\nconstexpr Rgba kYellowgreen = Rgba(154, 205, 50);\n// CSS Color Module Level 4\nconstexpr Rgba kRebeccapurple = Rgba(102, 51, 153);\n\n}  // namespace tachyon::base::colors\n\n#endif  // TACHYON_BASE_COLOR_NAMED_COLOR_H_\n"
  },
  {
    "path": "tachyon/base/compiler_specific.h",
    "content": "// Copyright (c) 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_COMPILER_SPECIFIC_H_\n#define TACHYON_BASE_COMPILER_SPECIFIC_H_\n\n#include \"tachyon/build/build_config.h\"\n\n// This is a wrapper around `__has_cpp_attribute`, which can be used to test for\n// the presence of an attribute. In case the compiler does not support this\n// macro it will simply evaluate to 0.\n//\n// References:\n// https://wg21.link/sd6#testing-for-the-presence-of-an-attribute-__has_cpp_attribute\n// https://wg21.link/cpp.cond#:__has_cpp_attribute\n#if defined(__has_cpp_attribute)\n#define HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x)\n#else\n#define HAS_CPP_ATTRIBUTE(x) 0\n#endif\n\n// A wrapper around `__has_attribute`, similar to HAS_CPP_ATTRIBUTE.\n#if defined(__has_attribute)\n#define HAS_ATTRIBUTE(x) __has_attribute(x)\n#else\n#define HAS_ATTRIBUTE(x) 0\n#endif\n\n// A wrapper around `__has_builtin`, similar to HAS_CPP_ATTRIBUTE.\n#if defined(__has_builtin)\n#define HAS_BUILTIN(x) __has_builtin(x)\n#else\n#define HAS_BUILTIN(x) 0\n#endif\n\n// Annotate a function indicating it should not be inlined.\n// Use like:\n//   NOINLINE void DoStuff() { ... }\n#if defined(__clang__) && HAS_ATTRIBUTE(noinline)\n#define NOINLINE [[clang::noinline]]\n#elif defined(COMPILER_GCC) && HAS_ATTRIBUTE(noinline)\n#define NOINLINE __attribute__((noinline))\n#elif defined(COMPILER_MSVC)\n#define NOINLINE __declspec(noinline)\n#else\n#define NOINLINE\n#endif\n\n#if defined(__clang__) && defined(NDEBUG) && HAS_ATTRIBUTE(always_inline)\n#define ALWAYS_INLINE [[clang::always_inline]] inline\n#elif defined(COMPILER_GCC) && defined(NDEBUG) && HAS_ATTRIBUTE(always_inline)\n#define ALWAYS_INLINE inline __attribute__((__always_inline__))\n#elif defined(COMPILER_MSVC) && defined(NDEBUG)\n#define ALWAYS_INLINE __forceinline\n#else\n#define ALWAYS_INLINE inline\n#endif\n\n// Annotate a function indicating it should never be tail called. Useful to make\n// sure callers of the annotated function are never omitted from call-stacks.\n// To provide the complementary behavior (prevent the annotated function from\n// being omitted) look at NOINLINE. Also note that this doesn't prevent code\n// folding of multiple identical caller functions into a single signature. To\n// prevent code folding, see NO_CODE_FOLDING() in base/debug/alias.h.\n// Use like:\n//   NOT_TAIL_CALLED void FooBar();\n#if defined(__clang__) && HAS_ATTRIBUTE(not_tail_called)\n#define NOT_TAIL_CALLED [[clang::not_tail_called]]\n#else\n#define NOT_TAIL_CALLED\n#endif\n\n// Specify memory alignment for structs, classes, etc.\n// Use like:\n//   class ALIGNAS(16) MyClass { ... }\n//   ALIGNAS(16) int array[4];\n//\n// In most places you can use the C++11 keyword \"alignas\", which is preferred.\n//\n// Historically, compilers had trouble mixing __attribute__((...)) syntax with\n// alignas(...) syntax. However, at least Clang is very accepting nowadays. It\n// may be that this macro can be removed entirely.\n#if defined(__clang__)\n#define ALIGNAS(byte_alignment) alignas(byte_alignment)\n#elif defined(COMPILER_MSVC)\n#define ALIGNAS(byte_alignment) __declspec(align(byte_alignment))\n#elif defined(COMPILER_GCC) && HAS_ATTRIBUTE(aligned)\n#define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment)))\n#endif\n\n// In case the compiler supports it NO_UNIQUE_ADDRESS evaluates to the C++20\n// attribute [[no_unique_address]]. This allows annotating data members so that\n// they need not have an address distinct from all other non-static data members\n// of its class.\n//\n// References:\n// * https://en.cppreference.com/w/cpp/language/attributes/no_unique_address\n// * https://wg21.link/dcl.attr.nouniqueaddr\n#if HAS_CPP_ATTRIBUTE(no_unique_address)\n#define NO_UNIQUE_ADDRESS [[no_unique_address]]\n#else\n#define NO_UNIQUE_ADDRESS\n#endif\n\n// Tells the compiler a function is using a printf-style format string.\n// |format_param| is the one-based index of the format string parameter;\n// |dots_param| is the one-based index of the \"...\" parameter.\n// For v*printf functions (which take a va_list), pass 0 for dots_param.\n// (This is undocumented but matches what the system C headers do.)\n// For member functions, the implicit this parameter counts as index 1.\n#if (defined(COMPILER_GCC) || defined(__clang__)) && HAS_ATTRIBUTE(format)\n#define PRINTF_FORMAT(format_param, dots_param) \\\n  __attribute__((format(printf, format_param, dots_param)))\n#else\n#define PRINTF_FORMAT(format_param, dots_param)\n#endif\n\n// WPRINTF_FORMAT is the same, but for wide format strings.\n// This doesn't appear to yet be implemented in any compiler.\n// See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=38308 .\n#define WPRINTF_FORMAT(format_param, dots_param)\n// If available, it would look like:\n//   __attribute__((format(wprintf, format_param, dots_param)))\n\n// Sanitizers annotations.\n#if HAS_ATTRIBUTE(no_sanitize)\n#define NO_SANITIZE(what) __attribute__((no_sanitize(what)))\n#endif\n#if !defined(NO_SANITIZE)\n#define NO_SANITIZE(what)\n#endif\n\n// MemorySanitizer annotations.\n#if defined(MEMORY_SANITIZER)\n#include <sanitizer/msan_interface.h>\n\n// Mark a memory region fully initialized.\n// Use this to annotate code that deliberately reads uninitialized data, for\n// example a GC scavenging root set pointers from the stack.\n#define MSAN_UNPOISON(p, size) __msan_unpoison(p, size)\n\n// Check a memory region for initializedness, as if it was being used here.\n// If any bits are uninitialized, crash with an MSan report.\n// Use this to sanitize data which MSan won't be able to track, e.g. before\n// passing data to another process via shared memory.\n#define MSAN_CHECK_MEM_IS_INITIALIZED(p, size) \\\n  __msan_check_mem_is_initialized(p, size)\n#else  // MEMORY_SANITIZER\n#define MSAN_UNPOISON(p, size)\n#define MSAN_CHECK_MEM_IS_INITIALIZED(p, size)\n#endif  // MEMORY_SANITIZER\n\n// DISABLE_CFI_PERF -- Disable Control Flow Integrity for perf reasons.\n#if !defined(DISABLE_CFI_PERF)\n#if defined(__clang__)\n#define DISABLE_CFI_PERF NO_SANITIZE(\"cfi\")\n#else\n#define DISABLE_CFI_PERF\n#endif\n#endif\n\n// Macro for hinting that an expression is likely to be false.\n#if !defined(UNLIKELY)\n#if defined(COMPILER_GCC) || defined(__clang__)\n#define UNLIKELY(x) __builtin_expect(!!(x), 0)\n#else\n#define UNLIKELY(x) (x)\n#endif  // defined(COMPILER_GCC)\n#endif  // !defined(UNLIKELY)\n\n#if !defined(LIKELY)\n#if defined(COMPILER_GCC) || defined(__clang__)\n#define LIKELY(x) __builtin_expect(!!(x), 1)\n#else\n#define LIKELY(x) (x)\n#endif  // defined(COMPILER_GCC)\n#endif  // !defined(LIKELY)\n\n// Compiler feature-detection.\n// clang.llvm.org/docs/LanguageExtensions.html#has-feature-and-has-extension\n#if defined(__has_feature)\n#define HAS_FEATURE(FEATURE) __has_feature(FEATURE)\n#else\n#define HAS_FEATURE(FEATURE) 0\n#endif\n\n#if defined(COMPILER_GCC)\n#define PRETTY_FUNCTION __PRETTY_FUNCTION__\n#elif defined(COMPILER_MSVC)\n#define PRETTY_FUNCTION __FUNCSIG__\n#else\n// See https://en.cppreference.com/w/c/language/function_definition#func\n#define PRETTY_FUNCTION __func__\n#endif\n\n#if !defined(CPU_ARM_NEON)\n#if defined(__arm__)\n#if !defined(__ARMEB__) && !defined(__ARM_EABI__) && !defined(__EABI__) && \\\n    !defined(__VFP_FP__) && !defined(_WIN32_WCE) && !defined(ANDROID)\n#error Tachyon does not support middle endian architecture\n#endif\n#if defined(__ARM_NEON__)\n#define CPU_ARM_NEON 1\n#endif\n#endif  // defined(__arm__)\n#endif  // !defined(CPU_ARM_NEON)\n\n#if !defined(HAVE_MIPS_MSA_INTRINSICS)\n#if defined(__mips_msa) && defined(__mips_isa_rev) && (__mips_isa_rev >= 5)\n#define HAVE_MIPS_MSA_INTRINSICS 1\n#endif\n#endif\n\n#if defined(__clang__) && HAS_ATTRIBUTE(uninitialized)\n// Attribute \"uninitialized\" disables -ftrivial-auto-var-init=pattern for\n// the specified variable.\n// Library-wide alternative is\n// 'configs -= [ \"//build/config/compiler:default_init_stack_vars\" ]' in .gn\n// file.\n//\n// See \"init_stack_vars\" in build/config/compiler/BUILD.gn and\n// http://crbug.com/977230\n// \"init_stack_vars\" is enabled for non-official builds and we hope to enable it\n// in official build in 2020 as well. The flag writes fixed pattern into\n// uninitialized parts of all local variables. In rare cases such initialization\n// is undesirable and attribute can be used:\n//   1. Degraded performance\n// In most cases compiler is able to remove additional stores. E.g. if memory is\n// never accessed or properly initialized later. Preserved stores mostly will\n// not affect program performance. However if compiler failed on some\n// performance critical code we can get a visible regression in a benchmark.\n//   2. memset, memcpy calls\n// Compiler may replaces some memory writes with memset or memcpy calls. This is\n// not -ftrivial-auto-var-init specific, but it can happen more likely with the\n// flag. It can be a problem if code is not linked with C run-time library.\n//\n// Note: The flag is security risk mitigation feature. So in future the\n// attribute uses should be avoided when possible. However to enable this\n// mitigation on the most of the code we need to be less strict now and minimize\n// number of exceptions later. So if in doubt feel free to use attribute, but\n// please document the problem for someone who is going to cleanup it later.\n// E.g. platform, bot, benchmark or test name in patch description or next to\n// the attribute.\n#define STACK_UNINITIALIZED [[clang::uninitialized]]\n#else\n#define STACK_UNINITIALIZED\n#endif\n\n// Attribute \"no_stack_protector\" disables -fstack-protector for the specified\n// function.\n//\n// \"stack_protector\" is enabled on most POSIX builds. The flag adds a canary\n// to each stack frame, which on function return is checked against a reference\n// canary. If the canaries do not match, it's likely that a stack buffer\n// overflow has occurred, so immediately crashing will prevent exploitation in\n// many cases.\n//\n// In some cases it's desirable to remove this, e.g. on hot functions, or if\n// we have purposely changed the reference canary.\n#if defined(COMPILER_GCC) || defined(__clang__)\n#if HAS_ATTRIBUTE(__no_stack_protector__)\n#define NO_STACK_PROTECTOR __attribute__((__no_stack_protector__))\n#else\n#define NO_STACK_PROTECTOR __attribute__((__optimize__(\"-fno-stack-protector\")))\n#endif\n#else\n#define NO_STACK_PROTECTOR\n#endif\n\n// The ANALYZER_ASSUME_TRUE(bool arg) macro adds compiler-specific hints\n// to Clang which control what code paths are statically analyzed,\n// and is meant to be used in conjunction with assert & assert-like functions.\n// The expression is passed straight through if analysis isn't enabled.\n//\n// ANALYZER_SKIP_THIS_PATH() suppresses static analysis for the current\n// codepath and any other branching codepaths that might follow.\n#if defined(__clang_analyzer__)\n\ninline constexpr bool AnalyzerNoReturn() __attribute__((analyzer_noreturn)) {\n  return false;\n}\n\ninline constexpr bool AnalyzerAssumeTrue(bool arg) {\n  // AnalyzerNoReturn() is invoked and analysis is terminated if |arg| is\n  // false.\n  return arg || AnalyzerNoReturn();\n}\n\n#define ANALYZER_ASSUME_TRUE(arg) ::AnalyzerAssumeTrue(!!(arg))\n#define ANALYZER_SKIP_THIS_PATH() static_cast<void>(::AnalyzerNoReturn())\n\n#else  // !defined(__clang_analyzer__)\n\n#define ANALYZER_ASSUME_TRUE(arg) (arg)\n#define ANALYZER_SKIP_THIS_PATH()\n\n#endif  // defined(__clang_analyzer__)\n\n// Use nomerge attribute to disable optimization of merging multiple same calls.\n#if defined(__clang__) && HAS_ATTRIBUTE(nomerge)\n#define NOMERGE [[clang::nomerge]]\n#else\n#define NOMERGE\n#endif\n\n// Marks a type as being eligible for the \"trivial\" ABI despite having a\n// non-trivial destructor or copy/move constructor. Such types can be relocated\n// after construction by simply copying their memory, which makes them eligible\n// to be passed in registers. The canonical example is std::unique_ptr.\n//\n// Use with caution; this has some subtle effects on constructor/destructor\n// ordering and will be very incorrect if the type relies on its address\n// remaining constant. When used as a function argument (by value), the value\n// may be constructed in the caller's stack frame, passed in a register, and\n// then used and destructed in the callee's stack frame. A similar thing can\n// occur when values are returned.\n//\n// TRIVIAL_ABI is not needed for types which have a trivial destructor and\n// copy/move constructors, such as base::TimeTicks and other POD.\n//\n// It is also not likely to be effective on types too large to be passed in one\n// or two registers on typical target ABIs.\n//\n// See also:\n//   https://clang.llvm.org/docs/AttributeReference.html#trivial-abi\n//   https://libcxx.llvm.org/docs/DesignDocs/UniquePtrTrivialAbi.html\n#if defined(__clang__) && HAS_ATTRIBUTE(trivial_abi)\n#define TRIVIAL_ABI [[clang::trivial_abi]]\n#else\n#define TRIVIAL_ABI\n#endif\n\n// Marks a member function as reinitializing a moved-from variable.\n// See also\n// https://clang.llvm.org/extra/clang-tidy/checks/bugprone-use-after-move.html#reinitialization\n#if defined(__clang__) && HAS_ATTRIBUTE(reinitializes)\n#define REINITIALIZES_AFTER_MOVE [[clang::reinitializes]]\n#else\n#define REINITIALIZES_AFTER_MOVE\n#endif\n\n// Requires constant initialization. See constinit in C++20. Allows to rely on a\n// variable being initialized before execution, and not requiring a global\n// constructor.\n#if HAS_ATTRIBUTE(require_constant_initialization)\n#define CONSTINIT __attribute__((require_constant_initialization))\n#endif\n#if !defined(CONSTINIT)\n#define CONSTINIT\n#endif\n\n#if defined(__clang__)\n#define GSL_OWNER [[gsl::Owner]]\n#define GSL_POINTER [[gsl::Pointer]]\n#else\n#define GSL_OWNER\n#define GSL_POINTER\n#endif\n\n// Adds the \"logically_const\" tag to a symbol's mangled name. The \"Mutable\n// Constants\" check [1] detects instances of constants that aren't in .rodata,\n// e.g. due to a missing `const`. Using this tag suppresses the check for this\n// symbol, allowing it to live outside .rodata without a warning.\n//\n// [1]:\n// https://crsrc.org/c/docs/speed/binary_size/android_binary_size_trybot.md#Mutable-Constants\n#if defined(COMPILER_GCC) || defined(__clang__)\n#define LOGICALLY_CONST [[gnu::abi_tag(\"logically_const\")]]\n#else\n#define LOGICALLY_CONST\n#endif\n\n// UNSAFE_BUFFERS() wraps code that violates the -Wunsafe-buffer-usage warning,\n// such as:\n// - pointer arithmetic,\n// - pointer subscripting, and\n// - calls to functions annotated with UNSAFE_BUFFER_USAGE.\n//\n// This indicates code whose bounds correctness cannot be ensured\n// systematically, and thus requires manual review.\n//\n// ** USE OF THIS MACRO SHOULD BE VERY RARE.** This should only be used when\n// strictly necessary. Prefer to use `base::span` instead of pointers, or other\n// safer coding patterns (like std containers) that avoid the opportunity for\n// out-of-bounds bugs to creep into the code. Any use of UNSAFE_BUFFERS() can\n// lead to a critical security bug if any assumptions are wrong, or ever become\n// wrong in the future.\n//\n// The macro should be used to wrap the minimum necessary code, to make it clear\n// what is unsafe, and prevent accidentally opting extra things out of the\n// warning.\n//\n// All usage of UNSAFE_BUFFERS() should come with a `// SAFETY: ...` comment\n// that explains how we have guaranteed that the pointer usage can never go\n// out-of-bounds, or that the requirements of the UNSAFE_BUFFER_USAGE function\n// are met. The safety comment should allow a reader to check that all\n// requirements have been met, using only local invariants. Examples of local\n// invariants include:\n// - Runtime conditions or CHECKs near the UNSAFE_BUFFERS macros\n// - Invariants guaranteed by types in the surrounding code\n// - Invariants guaranteed by function calls in the surrounding code\n// - Caller requirements, if the containing function is itself marked with\n//   UNSAFE_BUFFER_USAGE\n//\n// The last case should be an option of last resort. It is less safe and will\n// require the caller also use the UNSAFE_BUFFERS() macro. Prefer directly\n// capturing such invariants in types like `base::span`.\n//\n// Safety explanations may not rely on invariants that are not fully\n// encapsulated close to the UNSAFE_BUFFERS() usage. Instead, use safer coding\n// patterns or stronger invariants.\n#if defined(__clang__) && (__clang_major__ >= 17 && \\\n                           (__clang_minor__ > 0 || __clang_patchlevel__ >= 1))\n// See\n// https://releases.llvm.org/17.0.1/tools/clang/docs/ReleaseNotes.html#attribute-changes-in-clang.\n// clang-format off\n// Formatting is off so that we can put each _Pragma on its own line, as\n// recommended by the gcc docs.\n#define UNSAFE_BUFFERS(...)                  \\\n  _Pragma(\"clang unsafe_buffer_usage begin\") \\\n  __VA_ARGS__                                \\\n  _Pragma(\"clang unsafe_buffer_usage end\")\n// clang-format on\n#else\n#define UNSAFE_BUFFERS(...) __VA_ARGS__\n#endif\n\n#endif  // TACHYON_BASE_COMPILER_SPECIFIC_H_\n"
  },
  {
    "path": "tachyon/base/console/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\ntachyon_cc_library(\n    name = \"console\",\n    srcs = [\n        \"console.cc\",\n        \"console_stream.cc\",\n        \"iostream.cc\",\n        \"sgr_parameters.cc\",\n    ],\n    hdrs = [\n        \"console.h\",\n        \"console_stream.h\",\n        \"iostream.h\",\n        \"sgr_parameter_list.h\",\n        \"sgr_parameters.h\",\n    ],\n    visibility = [\"//visibility:public\"],\n    deps = [\n        \"//tachyon/base:environment\",\n        \"//tachyon/base/color:color_conversions\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"table_writer\",\n    srcs = [\"table_writer.cc\"],\n    hdrs = [\"table_writer.h\"],\n    visibility = [\"//visibility:public\"],\n    deps = [\n        \"//tachyon:export\",\n        \"@com_google_absl//absl/strings\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/base/console/console.cc",
    "content": "// Copyright (c) 2020 The Console Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/console/console.h\"\n\n#include <unistd.h>\n\n#include <algorithm>\n#include <iostream>\n\n#include \"tachyon/base/environment.h\"\n#include \"tachyon/base/strings/string_util.h\"\n\nnamespace tachyon::base {\n\nConsole::Info::Info() { Init(); }\n\nvoid Console::Info::Init() {\n  const char* kTerms[] = {\n      \"ansi\",  \"bvterm\", \"color\",   \"console\", \"cygwin\", \"konsole\",\n      \"linux\", \"putty\",  \"scoansi\", \"screen\",  \"tmux\",\n  };\n\n  const char* kTermsPrefix[] = {\n      \"eterm\", \"kterm\", \"vt100\", \"vt102\", \"vt220\", \"vt320\", \"xterm\",\n  };\n\n  std::string_view term;\n  if (!Environment::Get(\"TERM\", &term)) return;\n  support_ansi = std::any_of(std::begin(kTerms), std::end(kTerms),\n                             [term](const char* t) { return term == t; }) ||\n                 std::any_of(std::begin(kTermsPrefix), std::end(kTermsPrefix),\n                             [term](const char* prefix) {\n                               return StartsWith(term, prefix);\n                             });\n\n  std::string_view colorterm;\n  if (!Environment::Get(\"COLORTERM\", &colorterm)) return;\n  if (colorterm == \"truecolor\") {\n    support_truecolor = true;\n  } else if (EndsWith(colorterm, \"-256\")) {\n    support_8bit_color = true;\n  }\n}\n\n// static\nConsole::Info& Console::GetInfo() {\n  static Console::Info info;\n  return info;\n}\n\n// This was taken and modified from\n// LICENSE: undefined\n// URL:  https://github.com/agauniyal/rang/blob/master/include/rang.hpp\n// static\nbool Console::IsConnected(std::ostream& os) {\n  std::streambuf* osbuf = os.rdbuf();\n  if (osbuf == std::cout.rdbuf()) {\n    static const bool cout_term = isatty(fileno(stdout)) != 0;\n    return cout_term;\n  } else if (osbuf == std::cerr.rdbuf() || osbuf == std::clog.rdbuf()) {\n    static const bool cerr_term = isatty(fileno(stderr)) != 0;\n    return cerr_term;\n  }\n  return false;\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/console/console.h",
    "content": "// Copyright (c) 2020 The Console Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_CONSOLE_CONSOLE_H_\n#define TACHYON_BASE_CONSOLE_CONSOLE_H_\n\n#include <ostream>\n\n#include \"tachyon/export.h\"\n\nnamespace tachyon::base {\n\nclass TACHYON_EXPORT Console {\n public:\n  struct Info {\n    bool support_ansi = false;\n    bool support_8bit_color = false;\n    bool support_truecolor = false;\n\n    Info();\n\n   private:\n    void Init();\n  };\n\n  static Info& GetInfo();\n  static bool IsConnected(std::ostream& os);\n};\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_CONSOLE_CONSOLE_H_\n"
  },
  {
    "path": "tachyon/base/console/console_stream.cc",
    "content": "// Copyright (c) 2020 The Console Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/console/console_stream.h\"\n\n#include \"tachyon/base/color/color_conversions.h\"\n\nnamespace tachyon::base {\n\nConsoleStream::ConsoleStream(std::ostream& ostream)\n    : ostream_(ostream), console_info_(Console::GetInfo()) {}\n\nConsoleStream::~ConsoleStream() { Reset(); }\n\n#define SGR_PARAMETER_LIST(name, code)   \\\n  ConsoleStream& ConsoleStream::name() { \\\n    ostream_ << sgr_params::k##name;     \\\n    return *this;                        \\\n  }\n#include \"tachyon/base/console/sgr_parameter_list.h\"\n#undef SGR_PARAMETER_LIST\n\nConsoleStream& ConsoleStream::Rgb(Rgba rgba) {\n  return Rgb(rgba.r, rgba.g, rgba.b);\n}\n\nConsoleStream& ConsoleStream::Rgb(uint8_t r, uint8_t g, uint8_t b) {\n  if (console_info_.support_truecolor) {\n    ostream_ << Rgb24(r, g, b);\n  } else if (console_info_.support_8bit_color) {\n    if (r == g && g == b) {\n      ostream_ << Grayscale8(r * (23.0 / 255.0));\n    } else {\n      ostream_ << Rgb8(r, g, b);\n    }\n  }\n  return *this;\n}\n\nConsoleStream& ConsoleStream::BgRgb(Rgba rgba) {\n  return BgRgb(rgba.r, rgba.g, rgba.b);\n}\n\nConsoleStream& ConsoleStream::BgRgb(uint8_t r, uint8_t g, uint8_t b) {\n  if (console_info_.support_truecolor) {\n    ostream_ << BgRgb24(r, g, b);\n  } else if (console_info_.support_8bit_color) {\n    if (r == g && g == b) {\n      ostream_ << BgGrayscale8(r * (23.0 / 255.0));\n    } else {\n      ostream_ << BgRgb8(r, g, b);\n    }\n  }\n  return *this;\n}\n\nConsoleStream& ConsoleStream::SetCursor(size_t row, size_t column) {\n  ostream_ << \"\\e[\" << row << \";\" << column << \"H\";\n  return *this;\n}\n\nConsoleStream& ConsoleStream::CursorUp(size_t n) {\n  ostream_ << \"\\e[\" << n << \"A\";\n  return *this;\n}\n\nConsoleStream& ConsoleStream::CursorDown(size_t n) {\n  ostream_ << \"\\e[\" << n << \"B\";\n  return *this;\n}\n\nConsoleStream& ConsoleStream::CursorForward(size_t n) {\n  ostream_ << \"\\e[\" << n << \"C\";\n  return *this;\n}\n\nConsoleStream& ConsoleStream::CursorBackward(size_t n) {\n  ostream_ << \"\\e[\" << n << \"D\";\n  return *this;\n}\n\nConsoleStream& ConsoleStream::SaveCursor() {\n  ostream_ << \"\\e[s\";\n  return *this;\n}\n\nConsoleStream& ConsoleStream::RestoreCursor() {\n  ostream_ << \"\\e[u\";\n  return *this;\n}\n\nConsoleStream& ConsoleStream::SaveCursorAndAttributes() {\n  ostream_ << \"\\e7\";\n  return *this;\n}\n\nConsoleStream& ConsoleStream::RestoreCursorAndAttributes() {\n  ostream_ << \"\\e8\";\n  return *this;\n}\n\nConsoleStream& ConsoleStream::ScrollScreen() {\n  ostream_ << \"\\e[r\";\n  return *this;\n}\n\nConsoleStream& ConsoleStream::ScrollScreen(size_t start, size_t end) {\n  ostream_ << \"\\e[\" << start << \";\" << end << \"r\";\n  return *this;\n}\n\nConsoleStream& ConsoleStream::ScrollDown() {\n  ostream_ << \"\\eD\";\n  return *this;\n}\n\nConsoleStream& ConsoleStream::ScrollUp() {\n  ostream_ << \"\\eM\";\n  return *this;\n}\n\nConsoleStream& ConsoleStream::SetTab() {\n  ostream_ << \"\\eH\";\n  return *this;\n}\n\nConsoleStream& ConsoleStream::ClearTab() {\n  ostream_ << \"\\e[g\";\n  return *this;\n}\n\nConsoleStream& ConsoleStream::ClearAllTab() {\n  ostream_ << \"\\e[3g\";\n  return *this;\n}\n\nConsoleStream& ConsoleStream::EraseEndOfLine() {\n  ostream_ << \"\\e[K\";\n  return *this;\n}\n\nConsoleStream& ConsoleStream::EraseStartOfLine() {\n  ostream_ << \"\\e[1K\";\n  return *this;\n}\n\nConsoleStream& ConsoleStream::EraseEntireLine() {\n  ostream_ << \"\\e[2K\";\n  return *this;\n}\n\nConsoleStream& ConsoleStream::EraseDown() {\n  ostream_ << \"\\e[J\";\n  return *this;\n}\n\nConsoleStream& ConsoleStream::EraseUp() {\n  ostream_ << \"\\e[1J\";\n  return *this;\n}\n\nConsoleStream& ConsoleStream::EraseScreen() {\n  ostream_ << \"\\e[2J\";\n  return *this;\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/console/console_stream.h",
    "content": "// Copyright (c) 2020 The Console Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_CONSOLE_CONSOLE_STREAM_H_\n#define TACHYON_BASE_CONSOLE_CONSOLE_STREAM_H_\n\n#include <stdint.h>\n\n#include <iostream>\n\n#include \"tachyon/base/color/color.h\"\n#include \"tachyon/base/console/console.h\"\n#include \"tachyon/base/console/sgr_parameters.h\"\n#include \"tachyon/export.h\"\n\nnamespace tachyon::base {\n\nclass TACHYON_EXPORT ConsoleStream {\n public:\n  explicit ConsoleStream(std::ostream& ostream_ = std::cout);\n  ~ConsoleStream();\n\n#define SGR_PARAMETER_LIST(name, code) ConsoleStream& name();\n#include \"tachyon/base/console/sgr_parameter_list.h\"\n#undef SGR_PARAMETER_LIST\n\n  ConsoleStream& Rgb(Rgba rgba);\n  ConsoleStream& Rgb(uint8_t r, uint8_t g, uint8_t b);\n  ConsoleStream& BgRgb(Rgba rgba);\n  ConsoleStream& BgRgb(uint8_t r, uint8_t g, uint8_t b);\n\n  // Cursor Control\n  // Sets the cursor position where subsequent text will begin. If no row/column\n  // parameters are provided, the cursor will move to the home\n  // position, at the upper left of the screen.\n  ConsoleStream& SetCursor(size_t row = 0, size_t column = 0);\n  // Moves the cursor up by |n| rows.\n  ConsoleStream& CursorUp(size_t n = 1);\n  // Moves the cursor down by |n| rows.\n  ConsoleStream& CursorDown(size_t n = 1);\n  // Moves the cursor forward by |n| columns.\n  ConsoleStream& CursorForward(size_t n = 1);\n  // Moves the cursor backward by |n| columns.\n  ConsoleStream& CursorBackward(size_t n = 1);\n  // Saves current cursor position.\n  ConsoleStream& SaveCursor();\n  // Restores cursor position after the save point.\n  ConsoleStream& RestoreCursor();\n  // Save current cursor position and attributes.\n  ConsoleStream& SaveCursorAndAttributes();\n  // Restores cursor position after the save point and attributes.\n  ConsoleStream& RestoreCursorAndAttributes();\n\n  // Scrolling\n  // Enable scrolling for entire display.\n  ConsoleStream& ScrollScreen();\n  // Enable scrolling from row |start| to row |end|.\n  ConsoleStream& ScrollScreen(size_t start, size_t end);\n  // Scroll display down one line.\n  ConsoleStream& ScrollDown();\n  // Scroll display up one line.\n  ConsoleStream& ScrollUp();\n\n  // Tab Control\n  // Sets a tab at the current position.\n  ConsoleStream& SetTab();\n  // Clears tab at the current position.\n  ConsoleStream& ClearTab();\n  // Clears all tabs.\n  ConsoleStream& ClearAllTab();\n\n  // Erasing Text\n  // Erases from the current cursor position to the end of the current line.\n  ConsoleStream& EraseEndOfLine();\n  // Erases from the current cursor position to the start of the current line.\n  ConsoleStream& EraseStartOfLine();\n  // Erases the entire current line.\n  ConsoleStream& EraseEntireLine();\n  // Erases the screen from the current line down to the bottom of the screen.\n  ConsoleStream& EraseDown();\n  // Erases the screen from the current line up to the top of the screen.\n  ConsoleStream& EraseUp();\n  // Erases the screen with the background colour and moves the cursor to home.\n  ConsoleStream& EraseScreen();\n\n  std::ostream& ostream() { return ostream_; }\n\n private:\n  std::ostream& ostream_;\n  Console::Info console_info_;\n};\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_CONSOLE_CONSOLE_STREAM_H_\n"
  },
  {
    "path": "tachyon/base/console/iostream.cc",
    "content": "#include \"tachyon/base/console/iostream.h\"\n\n#include <iostream>\n\nnamespace tachyon::base::internal {\n\nConsoleErrStream::ConsoleErrStream() : console_stream_(std::cerr) {\n  console_stream_.Red();\n  std::cerr << \"[ERROR]: \";\n}\n\nConsoleErrStream::~ConsoleErrStream() = default;\n\n}  // namespace tachyon::base::internal\n"
  },
  {
    "path": "tachyon/base/console/iostream.h",
    "content": "#ifndef TACHYON_BASE_CONSOLE_IOSTREAM_H_\n#define TACHYON_BASE_CONSOLE_IOSTREAM_H_\n\n#include <ostream>\n\n#include \"tachyon/base/console/console_stream.h\"\n#include \"tachyon/export.h\"\n\nnamespace tachyon::base::internal {\n\nclass TACHYON_EXPORT ConsoleErrStream {\n public:\n  ConsoleErrStream();\n  ConsoleErrStream(const ConsoleErrStream& other) = delete;\n  ConsoleErrStream& operator=(const ConsoleErrStream& other) = delete;\n  ~ConsoleErrStream();\n\n  std::ostream& ostream() { return console_stream_.ostream(); }\n\n private:\n  ConsoleStream console_stream_;\n};\n\n}  // namespace tachyon::base::internal\n\n#define tachyon_cerr ::tachyon::base::internal::ConsoleErrStream().ostream()\n\n#endif  // TACHYON_BASE_CONSOLE_IOSTREAM_H_\n"
  },
  {
    "path": "tachyon/base/console/sgr_parameter_list.h",
    "content": "// NOLINT(build/header_guard)\n// Copyright (c) 2020 The Console Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n// https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters\n// name, code\nSGR_PARAMETER_LIST(Reset, 0)\nSGR_PARAMETER_LIST(Bold, 1)\nSGR_PARAMETER_LIST(Dim, 2)\nSGR_PARAMETER_LIST(Italic, 3)\nSGR_PARAMETER_LIST(Underline, 4)\nSGR_PARAMETER_LIST(BlinkSlow, 5)\nSGR_PARAMETER_LIST(BlinkRapid, 6)\nSGR_PARAMETER_LIST(Inverse, 7)\nSGR_PARAMETER_LIST(Conceal, 8)\nSGR_PARAMETER_LIST(StrikeThrough, 9)\nSGR_PARAMETER_LIST(BoldOff, 22)\nSGR_PARAMETER_LIST(DimOff, 22)\nSGR_PARAMETER_LIST(ItalicOff, 23)\nSGR_PARAMETER_LIST(UnderlineOff, 24)\nSGR_PARAMETER_LIST(BlinkOff, 25)\nSGR_PARAMETER_LIST(InverseOff, 27)\nSGR_PARAMETER_LIST(ConcealOff, 28)\nSGR_PARAMETER_LIST(StrikeThroughOff, 29)\nSGR_PARAMETER_LIST(Black, 30)\nSGR_PARAMETER_LIST(Red, 31)\nSGR_PARAMETER_LIST(Green, 32)\nSGR_PARAMETER_LIST(Yellow, 33)\nSGR_PARAMETER_LIST(Blue, 34)\nSGR_PARAMETER_LIST(Magenta, 35)\nSGR_PARAMETER_LIST(Cyan, 36)\nSGR_PARAMETER_LIST(White, 37)\nSGR_PARAMETER_LIST(ColorOff, 39)\nSGR_PARAMETER_LIST(BgBlack, 40)\nSGR_PARAMETER_LIST(BgRed, 41)\nSGR_PARAMETER_LIST(BgGreen, 42)\nSGR_PARAMETER_LIST(BgYellow, 43)\nSGR_PARAMETER_LIST(BgBlue, 44)\nSGR_PARAMETER_LIST(BgMagenta, 45)\nSGR_PARAMETER_LIST(BgCyan, 46)\nSGR_PARAMETER_LIST(BgWhite, 47)\nSGR_PARAMETER_LIST(BgColorOff, 49)\nSGR_PARAMETER_LIST(LightBlack, 90)\nSGR_PARAMETER_LIST(LightRed, 91)\nSGR_PARAMETER_LIST(LightGreen, 92)\nSGR_PARAMETER_LIST(LightYellow, 93)\nSGR_PARAMETER_LIST(LightBlue, 94)\nSGR_PARAMETER_LIST(LightMagenta, 95)\nSGR_PARAMETER_LIST(LightCyan, 96)\nSGR_PARAMETER_LIST(LightWhite, 97)\nSGR_PARAMETER_LIST(BgLightBlack, 100)\nSGR_PARAMETER_LIST(BgLightRed, 101)\nSGR_PARAMETER_LIST(BgLightGreen, 102)\nSGR_PARAMETER_LIST(BgLightYellow, 103)\nSGR_PARAMETER_LIST(BgLightBlue, 104)\nSGR_PARAMETER_LIST(BgLightMagenta, 105)\nSGR_PARAMETER_LIST(BgLightCyan, 106)\nSGR_PARAMETER_LIST(BgLightWhite, 107)\n"
  },
  {
    "path": "tachyon/base/console/sgr_parameters.cc",
    "content": "// Copyright (c) 2020 The Console Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/console/sgr_parameters.h\"\n\n#include \"absl/strings/substitute.h\"\n\nnamespace tachyon::base {\n\nstd::string Grayscale8(uint8_t level) {\n  if (level > 24) return \"\";\n  return absl::Substitute(\"\\e[38;5;$0m\", level + 232);\n}\n\nstd::string BgGrayscale8(uint8_t level) {\n  if (level > 24) return \"\";\n  return absl::Substitute(\"\\e[48;5;$0m\", level + 232);\n}\n\nstd::string Rgb8(uint8_t r, uint8_t g, uint8_t b) {\n  return absl::Substitute(\"\\e[38;5;$0m\", Ansi8BitColor(r, g, b));\n}\n\nstd::string BgRgb8(uint8_t r, uint8_t g, uint8_t b) {\n  return absl::Substitute(\"\\e[48;5;$0m\", Ansi8BitColor(r, g, b));\n}\n\nstd::string Rgb24(uint8_t r, uint8_t g, uint8_t b) {\n  return absl::Substitute(\"\\e[38;2;$0;$1;$2m\", r, g, b);\n}\n\nstd::string BgRgb24(uint8_t r, uint8_t g, uint8_t b) {\n  return absl::Substitute(\"\\e[48;2;$0;$1;$2m\", r, g, b);\n}\n\nuint8_t Ansi8BitColor(uint8_t r, uint8_t g, uint8_t b) {\n  double r_scaled = r / 255.0 * 5;\n  double g_scaled = g / 255.0 * 5;\n  double b_scaled = b / 255.0 * 5;\n  return 16 + 36 * r_scaled + 6 * g_scaled + b_scaled;\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/console/sgr_parameters.h",
    "content": "// Copyright (c) 2020 The Console Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_CONSOLE_SGR_PARAMETERS_H_\n#define TACHYON_BASE_CONSOLE_SGR_PARAMETERS_H_\n\n#include <stdint.h>\n\n#include <string>\n\n#include \"tachyon/export.h\"\n\nnamespace tachyon::base {\n\nnamespace sgr_params {\n#define SGR_PARAMETER_LIST(name, code) \\\n  constexpr const char* k##name = \"\\e[\" #code \"m\";\n#include \"tachyon/base/console/sgr_parameter_list.h\"\n#undef SGR_PARAMETER_LIST\n}  // namespace sgr_params\n\n// Grayscale |level| is 0 to 23.\nTACHYON_EXPORT std::string Grayscale8(uint8_t level = 0);\nTACHYON_EXPORT std::string BgGrayscale8(uint8_t level = 0);\n\n// Rgb8 and BgRgb8 gets color using Ansi8BitColor method.\nTACHYON_EXPORT std::string Rgb8(uint8_t r, uint8_t g, uint8_t b);\nTACHYON_EXPORT std::string BgRgb8(uint8_t r, uint8_t g, uint8_t b);\n\nTACHYON_EXPORT std::string Rgb24(uint8_t r, uint8_t g, uint8_t b);\nTACHYON_EXPORT std::string BgRgb24(uint8_t r, uint8_t g, uint8_t b);\n\n// Returns Ansi 8 bit color using the equation below.\n// 6 × 6 × 6 cube (216 colors): 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5)\nTACHYON_EXPORT uint8_t Ansi8BitColor(uint8_t r, uint8_t g, uint8_t b);\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_CONSOLE_SGR_PARAMETERS_H_\n"
  },
  {
    "path": "tachyon/base/console/table_writer.cc",
    "content": "#include \"tachyon/base/console/table_writer.h\"\n\n#include <sys/ioctl.h>\n#include <unistd.h>\n\n#include <algorithm>\n#include <iomanip>\n#include <iostream>\n#include <limits>\n#include <numeric>\n#include <sstream>\n\n#include \"absl/strings/strip.h\"\n\nnamespace tachyon::base {\n\nTableWriter::TableWriter() = default;\n\nTableWriter::TableWriter(const TableWriter& other) = default;\n\nTableWriter& TableWriter::operator=(const TableWriter& other) = default;\n\nTableWriter::TableWriter(TableWriter&& other) noexcept = default;\n\nTableWriter& TableWriter::operator=(TableWriter&& other) noexcept = default;\n\nTableWriter::~TableWriter() = default;\n\nvoid TableWriter::SetElement(size_t row, size_t col, std::string_view element) {\n  if (elements_.size() <= row) {\n    elements_.resize(row + 1);\n  }\n  if (elements_[row].size() <= col) {\n    elements_[row].resize(titles_.size());\n  }\n\n  if (strip_mode_ == StripMode::kBothAsciiWhitespace) {\n    elements_[row][col] = std::string(absl::StripAsciiWhitespace(element));\n  } else if (strip_mode_ == StripMode::kTrailingAsciiWhitespace) {\n    elements_[row][col] =\n        std::string(absl::StripTrailingAsciiWhitespace(element));\n  } else {\n    elements_[row][col] =\n        std::string(absl::StripLeadingAsciiWhitespace(element));\n  }\n\n  if (column_widths_[col].length == Length::kAuto) {\n    column_widths_[col].width =\n        std::max(column_widths_[col].width, elements_[row][col].length());\n  }\n}\n\nstd::string TableWriter::ToString() const {\n  std::stringstream ss;\n  AppendTable(ss);\n  return ss.str();\n}\n\nvoid TableWriter::Print(bool with_new_line) const {\n  AppendTable(std::cout);\n  if (with_new_line) {\n    std::cout << std::endl;\n  }\n}\n\nvoid TableWriter::AppendTable(std::ostream& os) const {\n  uint16_t max_width = std::numeric_limits<uint16_t>::max();\n  if (fit_to_terminal_width_) {\n    struct winsize w;\n    ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);\n    max_width = w.ws_col;\n  }\n\n  const std::vector<ColumnWidth>* final_column_widths = &column_widths_;\n  std::vector<ColumnWidth> column_widths_fit_to_window;\n\n  size_t width = 0;\n  size_t max_col = column_widths_.size();\n  for (size_t col = 0; col < max_col; ++col) {\n    width += column_widths_[col].width;\n    if (width > max_width) {\n      if (col == 0) return;\n      // Clip the last column\n      column_widths_fit_to_window = column_widths_;\n      column_widths_fit_to_window[col].width -= (width - max_width);\n      final_column_widths = &column_widths_fit_to_window;\n      max_col = col;\n      break;\n    }\n    if (space_ > 0) {\n      width += space_;\n    }\n  }\n  absl::Span<const std::string> titles(titles_);\n  AppendRow(os, titles.subspan(0, max_col + 1), final_column_widths,\n            header_align_);\n  if (elements_.size() > 0) os << std::endl;\n\n  for (size_t row = 0; row < elements_.size(); ++row) {\n    absl::Span<const std::string> cols(elements_[row]);\n    AppendRow(os, cols.subspan(0, max_col + 1), final_column_widths,\n              body_align_);\n    if (row != elements_.size() - 1) os << std::endl;\n  }\n}\n\nvoid TableWriter::AppendRow(std::ostream& os,\n                            absl::Span<const std::string> contents,\n                            const std::vector<ColumnWidth>* column_widths,\n                            Align align) const {\n  if (align == Align::kRight) {\n    os << std::right;\n  } else {\n    os << std::left;\n  }\n\n  for (size_t col = 0; col < contents.size(); ++col) {\n    size_t column_width = (*column_widths)[col].width;\n    std::string_view content = contents[col];\n    if (column_width >= content.length()) {\n      if (align == Align::kCenter) {\n        size_t left_padding = (column_width - content.length()) / 2;\n        size_t right_padding = column_width - left_padding - content.length();\n        os << std::string(left_padding, ' ') << content\n           << std::string(right_padding, ' ');\n      } else {\n        os << std::setw(column_width) << content;\n      }\n    } else {\n      os << content.substr(0, column_width);\n    }\n    if (space_ > 0 && col != contents.size() - 1) {\n      os << std::string(space_, ' ');\n    }\n  }\n}\n\nTableWriterBuilder::TableWriterBuilder() = default;\n\nTableWriterBuilder::~TableWriterBuilder() = default;\n\nTableWriterBuilder& TableWriterBuilder::AlignHeaderLeft() {\n  writer_.header_align_ = TableWriter::Align::kLeft;\n  return *this;\n}\n\nTableWriterBuilder& TableWriterBuilder::AlignHeaderRight() {\n  writer_.header_align_ = TableWriter::Align::kRight;\n  return *this;\n}\n\nTableWriterBuilder& TableWriterBuilder::AlignHeaderCenter() {\n  writer_.header_align_ = TableWriter::Align::kCenter;\n  return *this;\n}\n\nTableWriterBuilder& TableWriterBuilder::AlignBodyLeft() {\n  writer_.body_align_ = TableWriter::Align::kLeft;\n  return *this;\n}\n\nTableWriterBuilder& TableWriterBuilder::AlignBodyRight() {\n  writer_.body_align_ = TableWriter::Align::kRight;\n  return *this;\n}\n\nTableWriterBuilder& TableWriterBuilder::AlignBodyCenter() {\n  writer_.body_align_ = TableWriter::Align::kCenter;\n  return *this;\n}\n\nTableWriterBuilder& TableWriterBuilder::AddSpace(size_t space) {\n  writer_.space_ = space;\n  return *this;\n}\n\nTableWriterBuilder& TableWriterBuilder::AddColumn(std::string_view title) {\n  writer_.titles_.emplace_back(absl::StripAsciiWhitespace(title));\n  writer_.column_widths_.push_back(\n      {TableWriter::Length::kAuto, writer_.titles_.back().length()});\n  return *this;\n}\n\nTableWriterBuilder& TableWriterBuilder::AddColumn(std::string_view title,\n                                                  size_t width) {\n  writer_.titles_.emplace_back(absl::StripAsciiWhitespace(title));\n  writer_.column_widths_.push_back({TableWriter::Length::kFixed, width});\n  return *this;\n}\n\nTableWriterBuilder& TableWriterBuilder::FitToTerminalWidth() {\n  writer_.fit_to_terminal_width_ = true;\n  return *this;\n}\n\nTableWriterBuilder& TableWriterBuilder::StripBothAsciiWhitespace() {\n  writer_.strip_mode_ = TableWriter::StripMode::kBothAsciiWhitespace;\n  return *this;\n}\n\nTableWriterBuilder& TableWriterBuilder::StripTrailingAsciiWhitespace() {\n  writer_.strip_mode_ = TableWriter::StripMode::kTrailingAsciiWhitespace;\n  return *this;\n}\n\nTableWriterBuilder& TableWriterBuilder::StripLeadingAsciiWhitespace() {\n  writer_.strip_mode_ = TableWriter::StripMode::kLeadingAsciiWhitespace;\n  return *this;\n}\n\nTableWriter TableWriterBuilder::Build() const { return writer_; }\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/console/table_writer.h",
    "content": "#ifndef TACHYON_BASE_CONSOLE_TABLE_WRITER_H_\n#define TACHYON_BASE_CONSOLE_TABLE_WRITER_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <ostream>\n#include <string>\n#include <string_view>\n#include <vector>\n\n#include \"absl/types/span.h\"\n\n#include \"tachyon/export.h\"\n\nnamespace tachyon::base {\n\nclass TACHYON_EXPORT TableWriter {\n public:\n  enum class Align {\n    kLeft,\n    kRight,\n    kCenter,\n  };\n\n  enum class Length {\n    kAuto,\n    kFixed,\n  };\n\n  enum class StripMode {\n    kBothAsciiWhitespace,\n    kTrailingAsciiWhitespace,\n    kLeadingAsciiWhitespace,\n  };\n\n  TableWriter(const TableWriter& other);\n  TableWriter& operator=(const TableWriter& other);\n  TableWriter(TableWriter&& other) noexcept;\n  TableWriter& operator=(TableWriter&& other) noexcept;\n  ~TableWriter();\n\n  struct ColumnWidth {\n    Length length;\n    size_t width;\n  };\n\n  void SetElement(size_t row, size_t col, std::string_view element);\n  std::string ToString() const;\n\n  void Print(bool with_new_line = true) const;\n\n private:\n  friend class TableWriterBuilder;\n  TableWriter();\n\n  void AppendTable(std::ostream& os) const;\n  void AppendRow(std::ostream& ss, absl::Span<const std::string> contents,\n                 const std::vector<ColumnWidth>* column_widths,\n                 Align align) const;\n\n  Align header_align_ = Align::kLeft;\n  Align body_align_ = Align::kLeft;\n  size_t space_ = 0;\n  StripMode strip_mode_ = StripMode::kBothAsciiWhitespace;\n  bool fit_to_terminal_width_ = false;\n  std::vector<std::string> titles_;\n  std::vector<std::vector<std::string>> elements_;\n  std::vector<ColumnWidth> column_widths_;\n};\n\nclass TACHYON_EXPORT TableWriterBuilder {\n public:\n  TableWriterBuilder();\n  TableWriterBuilder(const TableWriterBuilder& other) = delete;\n  TableWriterBuilder& operator=(const TableWriterBuilder& other) = delete;\n  ~TableWriterBuilder();\n\n  TableWriterBuilder& AlignHeaderLeft();\n  TableWriterBuilder& AlignHeaderRight();\n  TableWriterBuilder& AlignHeaderCenter();\n  TableWriterBuilder& AlignBodyLeft();\n  TableWriterBuilder& AlignBodyRight();\n  TableWriterBuilder& AlignBodyCenter();\n  TableWriterBuilder& AddSpace(size_t space);\n  TableWriterBuilder& FitToTerminalWidth();\n  TableWriterBuilder& StripBothAsciiWhitespace();\n  TableWriterBuilder& StripTrailingAsciiWhitespace();\n  TableWriterBuilder& StripLeadingAsciiWhitespace();\n  TableWriterBuilder& AddColumn(std::string_view title);\n  TableWriterBuilder& AddColumn(std::string_view title, size_t width);\n\n  TableWriter Build() const;\n\n private:\n  TableWriter writer_;\n};\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_CONSOLE_TABLE_WRITER_H_\n"
  },
  {
    "path": "tachyon/base/containers/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"adapters\",\n    hdrs = [\"adapters.h\"],\n    deps = [\n        \":chunked_iterator\",\n        \":zipped_iterator\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"chunked_iterator\",\n    hdrs = [\"chunked_iterator.h\"],\n    deps = [\n        \"//tachyon/base:template_util\",\n        \"//tachyon/base/numerics:checked_math\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"circular_deque\",\n    hdrs = [\"circular_deque.h\"],\n    deps = [\n        \":vector_buffer\",\n        \"//tachyon/base:template_util\",\n        \"//tachyon/base/ranges:algorithm\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"container_util\",\n    hdrs = [\"container_util.h\"],\n    deps = [\n        \"//tachyon/base:logging\",\n        \"//tachyon/base:openmp_util\",\n        \"//tachyon/base:random\",\n        \"//tachyon/base/functional:functor_traits\",\n        \"//tachyon/base/numerics:checked_math\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"contains\",\n    hdrs = [\"contains.h\"],\n    deps = [\n        \"//tachyon/base/ranges\",\n        \"//tachyon/base/ranges:algorithm\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"cxx20_erase\",\n    hdrs = [\n        \"cxx20_erase.h\",\n        \"cxx20_erase_deque.h\",\n        \"cxx20_erase_forward_list.h\",\n        \"cxx20_erase_internal.h\",\n        \"cxx20_erase_list.h\",\n        \"cxx20_erase_map.h\",\n        \"cxx20_erase_set.h\",\n        \"cxx20_erase_string.h\",\n        \"cxx20_erase_unordered_map.h\",\n        \"cxx20_erase_unordered_set.h\",\n        \"cxx20_erase_vector.h\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"stack\",\n    hdrs = [\"stack.h\"],\n    deps = [\":circular_deque\"],\n)\n\ntachyon_cc_library(\n    name = \"util\",\n    hdrs = [\"util.h\"],\n)\n\ntachyon_cc_library(\n    name = \"vector_buffer\",\n    hdrs = [\"vector_buffer.h\"],\n    deps = [\n        \":util\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base/numerics:checked_math\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"zipped_iterator\",\n    hdrs = [\"zipped_iterator.h\"],\n    deps = [\"//tachyon/base:template_util\"],\n)\n\ntachyon_cc_unittest(\n    name = \"containers_unittests\",\n    srcs = [\n        \"adapters_unittest.cc\",\n        \"circular_deque_unittest.cc\",\n        \"container_util_unittest.cc\",\n        \"contains_unittest.cc\",\n        \"vector_buffer_unittest.cc\",\n    ],\n    deps = [\n        \":adapters\",\n        \":circular_deque\",\n        \":container_util\",\n        \":contains\",\n        \":vector_buffer\",\n        \"//tachyon/base/functional:identity\",\n        \"//tachyon/base/test:copy_only_int\",\n        \"//tachyon/base/test:move_only_int\",\n        \"@com_google_absl//absl/container:flat_hash_set\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/base/containers/adapters.h",
    "content": "// Copyright 2014 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_CONTAINERS_ADAPTERS_H_\n#define TACHYON_BASE_CONTAINERS_ADAPTERS_H_\n\n#include \"tachyon/base/containers/chunked_iterator.h\"\n#include \"tachyon/base/containers/zipped_iterator.h\"\n\nnamespace tachyon::base {\nnamespace internal {\n\n// Internal adapter class for implementing tachyon::base::Reversed.\ntemplate <typename T>\nclass ReversedAdapter {\n public:\n  explicit ReversedAdapter(T& t) : t_(t) {}\n  ReversedAdapter(const ReversedAdapter& ra) : t_(ra.t_) {}\n\n  ReversedAdapter& operator=(const ReversedAdapter& ra) = delete;\n\n  auto begin() const {\n    if constexpr (std::is_const_v<T>) {\n      return std::crbegin(t_);\n    } else {\n      return std::rbegin(t_);\n    }\n  }\n  auto end() const {\n    if constexpr (std::is_const_v<T>) {\n      return std::crend(t_);\n    } else {\n      return std::rend(t_);\n    }\n  }\n\n private:\n  T& t_;\n};\n\ntemplate <typename T>\nclass ChunkedAdapter {\n public:\n  ChunkedAdapter(T& t, size_t chunk_size) : t_(t), chunk_size_(chunk_size) {}\n  ChunkedAdapter(const ChunkedAdapter& ca) : t_(ca.t_) {}\n\n  ChunkedAdapter& operator=(const ChunkedAdapter& ca) = delete;\n\n  auto begin() const { return ChunkedBegin(t_, chunk_size_); }\n  auto end() const { return ChunkedEnd(t_, chunk_size_); }\n\n private:\n  T& t_;\n  size_t chunk_size_;\n};\n\ntemplate <typename T, typename U>\nclass ZippedAdapter {\n public:\n  ZippedAdapter(T& t, U& u) : t_(t), u_(u) {}\n  ZippedAdapter(const ZippedAdapter& za) : t_(za.t_), u_(za.u_) {}\n\n  ZippedAdapter& operator=(const ZippedAdapter& za) = delete;\n\n  auto begin() const { return ZippedBegin(t_, u_); }\n  auto end() const { return ZippedEnd(t_, u_); }\n\n private:\n  T& t_;\n  U& u_;\n};\n\n}  // namespace internal\n\n// Reversed returns a container adapter usable in a range-based \"for\" statement\n// for iterating a reversible container in reverse order.\n//\n// Example:\n//\n//   std::vector<int> v = {1, 2, 3, 4, 5};\n//   for (int i : tachyon::base::Reversed(v)) {\n//     // iterates through v from back to front\n//     // 5, 4, 3, 2, 1\n//   }\ntemplate <typename T>\ninternal::ReversedAdapter<T> Reversed(T& t) {\n  return internal::ReversedAdapter<T>(t);\n}\n\n// Chunked returns a container adapter that can be used in a range-based \"for\"\n// loop to iterate over a container in chunks of a specified size.\n//\n// Example:\n//\n//   std::vector<int> v = {1, 2, 3, 4, 5};\n//   for (const absl::Span<int>& i : tachyon::base::Chunked(v, 2)) {\n//     // iterates through v in chunks\n//     // {1, 2}, {3, 4}, {5}\n//   }\ntemplate <typename T>\ninternal::ChunkedAdapter<T> Chunked(T& t, size_t chunk_size) {\n  return internal::ChunkedAdapter<T>(t, chunk_size);\n}\n\n// Zipped returns a container adapter that can be used in a range-based \"for\"\n// loop to iterate over 2 containers.\n// Note that in order to iterate safely, both lengths of the containers should\n// be same.\n//\n// Example:\n//\n//   std::vector<int> v = {1, 2, 3};\n//   std::vector<int> w = {4, 5, 6};\n//   for (const std::tuple<int, int>& i : tachyon::base::Zipped(v, w)) {\n//     // iterates through v in tuple\n//     // {1, 4}, {2, 5}, {3, 6}\n//   }\ntemplate <typename T, typename U>\ninternal::ZippedAdapter<T, U> Zipped(T& t, U& u) {\n  return internal::ZippedAdapter<T, U>(t, u);\n}\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_CONTAINERS_ADAPTERS_H_\n"
  },
  {
    "path": "tachyon/base/containers/adapters_unittest.cc",
    "content": "// Copyright 2014 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/containers/adapters.h\"\n\n#include <tuple>\n#include <utility>\n#include <vector>\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n\nnamespace tachyon::base {\n\nTEST(AdaptersTest, Reversed) {\n  std::vector<int> v{3, 2, 1};\n  int j = 0;\n  for (int& i : Reversed(v)) {\n    EXPECT_EQ(++j, i);\n    i += 100;\n  }\n  EXPECT_THAT(v, testing::ContainerEq(std::vector<int>{103, 102, 101}));\n}\n\nTEST(AdaptersTest, ReversedArray) {\n  int v[3] = {3, 2, 1};\n  int j = 0;\n  for (int& i : Reversed(v)) {\n    EXPECT_EQ(++j, i);\n    i += 100;\n  }\n  EXPECT_THAT(v, testing::ElementsAreArray({103, 102, 101}));\n}\n\nTEST(AdaptersTest, ReversedConst) {\n  std::vector<int> v{3, 2, 1};\n  const std::vector<int>& cv = v;\n  int j = 0;\n  for (int i : Reversed(cv)) {\n    EXPECT_EQ(++j, i);\n  }\n}\n\ntemplate <typename V, typename T>\nvoid TestChunked(T&& v) {\n  for (size_t chunk_size = 1; chunk_size < 5; ++chunk_size) {\n    size_t expected_num_chunks = (std::size(v) + chunk_size - 1) / chunk_size;\n    size_t chunk_offset = 0;\n    size_t offset = 0;\n    size_t size = std::size(v);\n    for (const absl::Span<V>& j : Chunked(std::forward<T>(v), chunk_size)) {\n      EXPECT_EQ(*j.data(), v[offset]);\n      EXPECT_EQ(j.size(), chunk_offset == expected_num_chunks - 1\n                              ? size - offset\n                              : chunk_size);\n      ++chunk_offset;\n      offset += chunk_size;\n    }\n    EXPECT_EQ(chunk_offset, expected_num_chunks);\n  }\n}\n\nTEST(AdaptersTest, Chunked) {\n  std::vector<int> v{1, 2, 3};\n  TestChunked<int>(v);\n\n  absl::Span<const int> v_span = v;\n  TestChunked<const int>(v_span);\n}\n\nTEST(AdaptersTest, ChunkedArray) {\n  int v[3] = {1, 2, 3};\n  TestChunked<int>(v);\n}\n\nTEST(AdaptersTest, ChunkedConst) {\n  std::vector<int> v{1, 2, 3};\n  const std::vector<int>& cv = v;\n  TestChunked<const int>(cv);\n}\n\nTEST(AdaptersTest, Zipped) {\n  std::vector<int> v{1, 2, 3};\n  std::vector<int> w{4, 5, 6};\n  size_t offset = 0;\n  for (const std::tuple<int, int>& j : Zipped(v, w)) {\n    EXPECT_EQ(std::get<0>(j), v[offset]);\n    EXPECT_EQ(std::get<1>(j), w[offset]);\n    ++offset;\n  }\n  EXPECT_EQ(offset, std::size(v));\n}\n\nTEST(AdaptersTest, ZippedArray) {\n  int v[3] = {1, 2, 3};\n  int w[3] = {4, 5, 6};\n  size_t offset = 0;\n  for (const std::tuple<int, int>& j : Zipped(v, w)) {\n    EXPECT_EQ(std::get<0>(j), v[offset]);\n    EXPECT_EQ(std::get<1>(j), w[offset]);\n    ++offset;\n  }\n  EXPECT_EQ(offset, std::size(v));\n}\n\nTEST(AdaptersTest, ZippedConst) {\n  std::vector<int> v{1, 2, 3};\n  std::vector<int> w{4, 5, 6};\n  const std::vector<int>& cv = v;\n  const std::vector<int>& cw = w;\n  size_t offset = 0;\n  for (const std::tuple<int, int>& j : Zipped(cv, cw)) {\n    EXPECT_EQ(std::get<0>(j), v[offset]);\n    EXPECT_EQ(std::get<1>(j), w[offset]);\n    ++offset;\n  }\n  EXPECT_EQ(offset, std::size(v));\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/containers/chunked_iterator.h",
    "content": "#ifndef TACHYON_BASE_CONTAINERS_CHUNKED_ITERATOR_H_\n#define TACHYON_BASE_CONTAINERS_CHUNKED_ITERATOR_H_\n\n#include <algorithm>\n#include <type_traits>\n#include <utility>\n\n#include \"absl/types/span.h\"\n\n#include \"tachyon/base/numerics/checked_math.h\"\n#include \"tachyon/base/template_util.h\"\n\nnamespace tachyon::base {\n\ntemplate <typename Iter>\nclass ChunkedIterator {\n public:\n  constexpr static bool kIsConst = base::is_const_iterator_v<Iter>;\n  using underlying_value_type =\n      std::conditional_t<kIsConst, const iter_value_t<Iter>,\n                         iter_value_t<Iter>>;\n\n  using difference_type = iter_difference_t<Iter>;\n  using value_type = absl::Span<underlying_value_type>;\n  using pointer = value_type*;\n  using reference = value_type&;\n  using iterator_category =\n      typename std::iterator_traits<Iter>::iterator_category;\n\n  ChunkedIterator(Iter current, size_t chunk_size)\n      : current_(std::move(current)),\n        chunk_size_(chunk_size),\n        size_(0),\n        len_(0) {}\n  ChunkedIterator(Iter current, size_t chunk_size, size_t size)\n      : current_(std::move(current)),\n        chunk_size_(chunk_size),\n        size_(size),\n        len_(std::min(chunk_size, size)) {}\n\n  ChunkedIterator(const ChunkedIterator& other) = default;\n  ChunkedIterator& operator=(const ChunkedIterator& other) = default;\n\n  bool operator==(const ChunkedIterator& other) const {\n    return current_ == other.current_;\n  }\n  bool operator!=(const ChunkedIterator& other) const {\n    return !(*this == other);\n  }\n\n  // TODO(chokobole): Add more operators to support iterator category other than\n  // forward_iterator.\n  ChunkedIterator& operator++() {\n    current_ += len_;\n    offset_ += len_;\n    base::CheckedNumeric<size_t> len = offset_;\n    len += chunk_size_;\n    size_t new_len = len.ValueOrDie();\n    if (new_len > size_) {\n      len_ = size_ - offset_;\n    } else {\n      len_ = chunk_size_;\n    }\n    return *this;\n  }\n\n  ChunkedIterator operator++(int) {\n    ChunkedIterator iterator(*this);\n    ++(*this);\n    return iterator;\n  }\n\n  difference_type operator-(const ChunkedIterator& other) const {\n    return (current_ - other.current_ + chunk_size_ - 1) / chunk_size_;\n  }\n\n  // NOTE(chokobole): To suppress -Werror,-Wignored-reference-qualifiers on\n  // mac\n  const std::remove_const_t<pointer> operator->() const {\n    value_ = value_type(&*current_, len_);\n    return &value_;\n  }\n  pointer operator->() {\n    return const_cast<pointer>(std::as_const(*this).operator->());\n  }\n\n  // NOTE(chokobole): To suppress -Werror,-Wignored-reference-qualifiers on\n  // mac\n  const std::remove_const_t<reference> operator*() const {\n    value_ = value_type(&*current_, len_);\n    return value_;\n  }\n  reference operator*() {\n    return const_cast<reference>(std::as_const(*this).operator*());\n  }\n\n private:\n  Iter current_;\n  size_t offset_ = 0;\n  size_t chunk_size_;\n  size_t size_;\n  size_t len_;\n  mutable value_type value_;\n};\n\ntemplate <typename T>\nauto ChunkedBegin(T& t, size_t chunk_size) {\n  using Iter = decltype(std::begin(std::declval<T&>()));\n  return ChunkedIterator<Iter>(std::begin(t), chunk_size,\n                               std::distance(std::begin(t), std::end(t)));\n}\n\ntemplate <typename T>\nauto ChunkedEnd(T& t, size_t chunk_size) {\n  using Iter = decltype(std::end(std::declval<T&>()));\n  return ChunkedIterator<Iter>(std::end(t), chunk_size);\n}\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_CONTAINERS_CHUNKED_ITERATOR_H_\n"
  },
  {
    "path": "tachyon/base/containers/circular_deque.h",
    "content": "// Copyright 2017 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n// clang-format off\n\n#ifndef TACHYON_BASE_CONTAINERS_CIRCULAR_DEQUE_H_\n#define TACHYON_BASE_CONTAINERS_CIRCULAR_DEQUE_H_\n\n#include <algorithm>\n#include <cstddef>\n#include <iterator>\n#include <type_traits>\n#include <utility>\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/containers/vector_buffer.h\"\n// #include \"tachyon/base/memory/raw_ptr_exclusion.h\"\n#include \"tachyon/base/ranges/algorithm.h\"\n#include \"tachyon/base/template_util.h\"\n\n#if DCHECK_IS_ON()\n#include <ostream>\n#endif\n\n// base::circular_deque is similar to std::deque. Unlike std::deque, the\n// storage is provided in a flat circular buffer conceptually similar to a\n// vector. The beginning and end will wrap around as necessary so that\n// pushes and pops will be constant time as long as a capacity expansion is\n// not required.\n//\n// The API should be identical to std::deque with the following differences:\n//\n//  - ITERATORS ARE NOT STABLE. Mutating the container will invalidate all\n//    iterators.\n//\n//  - Insertions may resize the vector and so are not constant time (std::deque\n//    guarantees constant time for insertions at the ends).\n//\n//  - Container-wide comparisons are not implemented. If you want to compare\n//    two containers, use an algorithm so the expensive iteration is explicit.\n//\n// If you want a similar container with only a queue API, use base::queue in\n// base/containers/queue.h.\n//\n// Constructors:\n//   circular_deque();\n//   circular_deque(size_t count);\n//   circular_deque(size_t count, const T& value);\n//   circular_deque(InputIterator first, InputIterator last);\n//   circular_deque(const circular_deque&);\n//   circular_deque(circular_deque&&);\n//   circular_deque(std::initializer_list<value_type>);\n//\n// Assignment functions:\n//   circular_deque& operator=(const circular_deque&);\n//   circular_deque& operator=(circular_deque&&);\n//   circular_deque& operator=(std::initializer_list<T>);\n//   void assign(size_t count, const T& value);\n//   void assign(InputIterator first, InputIterator last);\n//   void assign(std::initializer_list<T> value);\n//\n// Random accessors:\n//   T& at(size_t);\n//   const T& at(size_t) const;\n//   T& operator[](size_t);\n//   const T& operator[](size_t) const;\n//\n// End accessors:\n//   T& front();\n//   const T& front() const;\n//   T& back();\n//   const T& back() const;\n//\n// Iterator functions:\n//   iterator               begin();\n//   const_iterator         begin() const;\n//   const_iterator         cbegin() const;\n//   iterator               end();\n//   const_iterator         end() const;\n//   const_iterator         cend() const;\n//   reverse_iterator       rbegin();\n//   const_reverse_iterator rbegin() const;\n//   const_reverse_iterator crbegin() const;\n//   reverse_iterator       rend();\n//   const_reverse_iterator rend() const;\n//   const_reverse_iterator crend() const;\n//\n// Memory management:\n//   void reserve(size_t);  // SEE IMPLEMENTATION FOR SOME GOTCHAS.\n//   size_t capacity() const;\n//   void shrink_to_fit();\n//\n// Size management:\n//   void clear();\n//   bool empty() const;\n//   size_t size() const;\n//   void resize(size_t);\n//   void resize(size_t count, const T& value);\n//\n// Positional insert and erase:\n//   void insert(const_iterator pos, size_type count, const T& value);\n//   void insert(const_iterator pos,\n//               InputIterator first, InputIterator last);\n//   iterator insert(const_iterator pos, const T& value);\n//   iterator insert(const_iterator pos, T&& value);\n//   iterator emplace(const_iterator pos, Args&&... args);\n//   iterator erase(const_iterator pos);\n//   iterator erase(const_iterator first, const_iterator last);\n//\n// End insert and erase:\n//   void push_front(const T&);\n//   void push_front(T&&);\n//   void push_back(const T&);\n//   void push_back(T&&);\n//   T& emplace_front(Args&&...);\n//   T& emplace_back(Args&&...);\n//   void pop_front();\n//   void pop_back();\n//\n// General:\n//   void swap(circular_deque&);\n\nnamespace tachyon::base {\n\ntemplate <class T>\nclass circular_deque;\n\nnamespace internal {\n\n// Start allocating nonempty buffers with this many entries. This is the\n// external capacity so the internal buffer will be one larger (= 4) which is\n// more even for the allocator. See the descriptions of internal vs. external\n// capacity on the comment above the buffer_ variable below.\nconstexpr size_t kCircularBufferInitialCapacity = 3;\n\ntemplate <typename T>\nclass circular_deque_const_iterator {\n public:\n  using difference_type = std::ptrdiff_t;\n  using value_type = T;\n  using pointer = const T*;\n  using reference = const T&;\n  using iterator_category = std::random_access_iterator_tag;\n\n  circular_deque_const_iterator() : parent_deque_(nullptr), index_(0) {\n#if DCHECK_IS_ON()\n    created_generation_ = 0;\n#endif  // DCHECK_IS_ON()\n  }\n\n  // Dereferencing.\n  const T& operator*() const {\n    CheckUnstableUsage();\n    parent_deque_->CheckValidIndex(index_);\n    return parent_deque_->buffer_[index_];\n  }\n  const T* operator->() const {\n    CheckUnstableUsage();\n    parent_deque_->CheckValidIndex(index_);\n    return &parent_deque_->buffer_[index_];\n  }\n  const value_type& operator[](difference_type i) const { return *(*this + i); }\n\n  // Increment and decrement.\n  circular_deque_const_iterator& operator++() {\n    Increment();\n    return *this;\n  }\n  circular_deque_const_iterator operator++(int) {\n    circular_deque_const_iterator ret = *this;\n    Increment();\n    return ret;\n  }\n  circular_deque_const_iterator& operator--() {\n    Decrement();\n    return *this;\n  }\n  circular_deque_const_iterator operator--(int) {\n    circular_deque_const_iterator ret = *this;\n    Decrement();\n    return ret;\n  }\n\n  // Random access mutation.\n  friend circular_deque_const_iterator operator+(\n      const circular_deque_const_iterator& iter,\n      difference_type offset) {\n    circular_deque_const_iterator ret = iter;\n    ret.Add(offset);\n    return ret;\n  }\n  circular_deque_const_iterator& operator+=(difference_type offset) {\n    Add(offset);\n    return *this;\n  }\n  friend circular_deque_const_iterator operator-(\n      const circular_deque_const_iterator& iter,\n      difference_type offset) {\n    circular_deque_const_iterator ret = iter;\n    ret.Add(-offset);\n    return ret;\n  }\n  circular_deque_const_iterator& operator-=(difference_type offset) {\n    Add(-offset);\n    return *this;\n  }\n\n  friend std::ptrdiff_t operator-(const circular_deque_const_iterator& lhs,\n                                  const circular_deque_const_iterator& rhs) {\n    lhs.CheckComparable(rhs);\n    return static_cast<std::ptrdiff_t>(lhs.OffsetFromBegin() -\n                                       rhs.OffsetFromBegin());\n  }\n\n  // Comparisons.\n  friend bool operator==(const circular_deque_const_iterator& lhs,\n                         const circular_deque_const_iterator& rhs) {\n    lhs.CheckComparable(rhs);\n    return lhs.index_ == rhs.index_;\n  }\n  friend bool operator!=(const circular_deque_const_iterator& lhs,\n                         const circular_deque_const_iterator& rhs) {\n    return !(lhs == rhs);\n  }\n  friend bool operator<(const circular_deque_const_iterator& lhs,\n                        const circular_deque_const_iterator& rhs) {\n    lhs.CheckComparable(rhs);\n    return lhs.OffsetFromBegin() < rhs.OffsetFromBegin();\n  }\n  friend bool operator<=(const circular_deque_const_iterator& lhs,\n                         const circular_deque_const_iterator& rhs) {\n    return !(lhs > rhs);\n  }\n  friend bool operator>(const circular_deque_const_iterator& lhs,\n                        const circular_deque_const_iterator& rhs) {\n    lhs.CheckComparable(rhs);\n    return lhs.OffsetFromBegin() > rhs.OffsetFromBegin();\n  }\n  friend bool operator>=(const circular_deque_const_iterator& lhs,\n                         const circular_deque_const_iterator& rhs) {\n    return !(lhs < rhs);\n  }\n\n protected:\n  friend class circular_deque<T>;\n\n  circular_deque_const_iterator(const circular_deque<T>* parent, size_t index)\n      : parent_deque_(parent), index_(index) {\n#if DCHECK_IS_ON()\n    created_generation_ = parent->generation_;\n#endif  // DCHECK_IS_ON()\n  }\n\n  // Returns the offset from the beginning index of the buffer to the current\n  // item.\n  size_t OffsetFromBegin() const {\n    if (index_ >= parent_deque_->begin_)\n      return index_ - parent_deque_->begin_;  // On the same side as begin.\n    return parent_deque_->buffer_.capacity() - parent_deque_->begin_ + index_;\n  }\n\n  // Most uses will be ++ and -- so use a simplified implementation.\n  void Increment() {\n    CheckUnstableUsage();\n    parent_deque_->CheckValidIndex(index_);\n    index_++;\n    if (index_ == parent_deque_->buffer_.capacity())\n      index_ = 0;\n  }\n  void Decrement() {\n    CheckUnstableUsage();\n    parent_deque_->CheckValidIndexOrEnd(index_);\n    if (index_ == 0)\n      index_ = parent_deque_->buffer_.capacity() - 1;\n    else\n      index_--;\n  }\n  void Add(difference_type delta) {\n    CheckUnstableUsage();\n#if DCHECK_IS_ON()\n    if (delta <= 0)\n      parent_deque_->CheckValidIndexOrEnd(index_);\n    else\n      parent_deque_->CheckValidIndex(index_);\n#endif\n    // It should be valid to add 0 to any iterator, even if the container is\n    // empty and the iterator points to end(). The modulo below will divide\n    // by 0 if the buffer capacity is empty, so it's important to check for\n    // this case explicitly.\n    if (delta == 0)\n      return;\n\n    difference_type new_offset = OffsetFromBegin() + delta;\n    DCHECK(new_offset >= 0 &&\n           new_offset <= static_cast<difference_type>(parent_deque_->size()));\n    index_ = (new_offset + parent_deque_->begin_) %\n             parent_deque_->buffer_.capacity();\n  }\n\n#if DCHECK_IS_ON()\n  void CheckUnstableUsage() const {\n    DCHECK(parent_deque_);\n    // Since circular_deque doesn't guarantee stability, any attempt to\n    // dereference this iterator after a mutation (i.e. the generation doesn't\n    // match the original) in the container is illegal.\n    DCHECK(created_generation_ == parent_deque_->generation_)\n        << \"circular_deque iterator dereferenced after mutation.\";\n  }\n  void CheckComparable(const circular_deque_const_iterator& other) const {\n    DCHECK(parent_deque_ == other.parent_deque_);\n    // Since circular_deque doesn't guarantee stability, two iterators that\n    // are compared must have been generated without mutating the container.\n    // If this fires, the container was mutated between generating the two\n    // iterators being compared.\n    DCHECK(created_generation_ == other.created_generation_);\n  }\n#else\n  inline void CheckUnstableUsage() const {}\n  inline void CheckComparable(const circular_deque_const_iterator&) const {}\n#endif  // DCHECK_IS_ON()\n\n  // `parent_deque_` is not a raw_ptr<...> for performance reasons: Usually\n  // on-stack pointer, pointing back to the collection being iterated, owned by\n  // object that iterates over it.  Additionally this is supported by the\n  // analysis of sampling profiler data and tab_search:top100:2020.\n  // TODO(chokobole):\n  // RAW_PTR_EXCLUSION const circular_deque<T>* parent_deque_;\n  const circular_deque<T>* parent_deque_;\n\n  size_t index_;\n\n#if DCHECK_IS_ON()\n  // The generation of the parent deque when this iterator was created. The\n  // container will update the generation for every modification so we can\n  // test if the container was modified by comparing them.\n  uint64_t created_generation_;\n#endif  // DCHECK_IS_ON()\n};\n\ntemplate <typename T>\nclass circular_deque_iterator : public circular_deque_const_iterator<T> {\n  using base = circular_deque_const_iterator<T>;\n\n public:\n  friend class circular_deque<T>;\n\n  using difference_type = std::ptrdiff_t;\n  using value_type = T;\n  using pointer = T*;\n  using reference = T&;\n  using iterator_category = std::random_access_iterator_tag;\n\n  // Expose the base class' constructor.\n  circular_deque_iterator() : circular_deque_const_iterator<T>() {}\n\n  // Dereferencing.\n  T& operator*() const { return const_cast<T&>(base::operator*()); }\n  T* operator->() const { return const_cast<T*>(base::operator->()); }\n  T& operator[](difference_type i) {\n    return const_cast<T&>(base::operator[](i));\n  }\n\n  // Random access mutation.\n  friend circular_deque_iterator operator+(const circular_deque_iterator& iter,\n                                           difference_type offset) {\n    circular_deque_iterator ret = iter;\n    ret.Add(offset);\n    return ret;\n  }\n  circular_deque_iterator& operator+=(difference_type offset) {\n    base::Add(offset);\n    return *this;\n  }\n  friend circular_deque_iterator operator-(const circular_deque_iterator& iter,\n                                           difference_type offset) {\n    circular_deque_iterator ret = iter;\n    ret.Add(-offset);\n    return ret;\n  }\n  circular_deque_iterator& operator-=(difference_type offset) {\n    base::Add(-offset);\n    return *this;\n  }\n\n  // Increment and decrement.\n  circular_deque_iterator& operator++() {\n    base::Increment();\n    return *this;\n  }\n  circular_deque_iterator operator++(int) {\n    circular_deque_iterator ret = *this;\n    base::Increment();\n    return ret;\n  }\n  circular_deque_iterator& operator--() {\n    base::Decrement();\n    return *this;\n  }\n  circular_deque_iterator operator--(int) {\n    circular_deque_iterator ret = *this;\n    base::Decrement();\n    return ret;\n  }\n\n private:\n  circular_deque_iterator(const circular_deque<T>* parent, size_t index)\n      : circular_deque_const_iterator<T>(parent, index) {}\n};\n\n}  // namespace internal\n\ntemplate <typename T>\nclass circular_deque {\n private:\n  using VectorBuffer = internal::VectorBuffer<T>;\n\n public:\n  using value_type = T;\n  using size_type = std::size_t;\n  using difference_type = std::ptrdiff_t;\n  using reference = value_type&;\n  using const_reference = const value_type&;\n  using pointer = value_type*;\n  using const_pointer = const value_type*;\n\n  using iterator = internal::circular_deque_iterator<T>;\n  using const_iterator = internal::circular_deque_const_iterator<T>;\n  using reverse_iterator = std::reverse_iterator<iterator>;\n  using const_reverse_iterator = std::reverse_iterator<const_iterator>;\n\n  // ---------------------------------------------------------------------------\n  // Constructor\n\n  constexpr circular_deque() = default;\n\n  // Constructs with |count| copies of |value| or default constructed version.\n  explicit circular_deque(size_type count) { resize(count); }\n  circular_deque(size_type count, const T& value) { resize(count, value); }\n\n  // Range constructor.\n  template <class InputIterator>\n  circular_deque(InputIterator first, InputIterator last) {\n    assign(first, last);\n  }\n\n  // Copy/move.\n  circular_deque(const circular_deque& other) : buffer_(other.size() + 1) {\n    assign(other.begin(), other.end());\n  }\n  circular_deque(circular_deque&& other) noexcept\n      : buffer_(std::move(other.buffer_)),\n        begin_(other.begin_),\n        end_(other.end_) {\n    other.begin_ = 0;\n    other.end_ = 0;\n  }\n\n  circular_deque(std::initializer_list<value_type> init) { assign(init); }\n\n  ~circular_deque() { DestructRange(begin_, end_); }\n\n  // ---------------------------------------------------------------------------\n  // Assignments.\n  //\n  // All of these may invalidate iterators and references.\n\n  circular_deque& operator=(const circular_deque& other) {\n    if (&other == this)\n      return *this;\n\n    reserve(other.size());\n    assign(other.begin(), other.end());\n    return *this;\n  }\n  circular_deque& operator=(circular_deque&& other) noexcept {\n    if (&other == this)\n      return *this;\n\n    // We're about to overwrite the buffer, so don't free it in clear to\n    // avoid doing it twice.\n    ClearRetainCapacity();\n    buffer_ = std::move(other.buffer_);\n    begin_ = other.begin_;\n    end_ = other.end_;\n\n    other.begin_ = 0;\n    other.end_ = 0;\n\n    IncrementGeneration();\n    return *this;\n  }\n  circular_deque& operator=(std::initializer_list<value_type> ilist) {\n    reserve(ilist.size());\n    assign(std::begin(ilist), std::end(ilist));\n    return *this;\n  }\n\n  void assign(size_type count, const value_type& value) {\n    ClearRetainCapacity();\n    reserve(count);\n    for (size_t i = 0; i < count; i++)\n      emplace_back(value);\n    IncrementGeneration();\n  }\n\n  // This variant should be enabled only when InputIterator is an iterator.\n  template <typename InputIterator>\n  std::enable_if_t<base::internal::is_iterator<InputIterator>::value, void>\n  assign(InputIterator first, InputIterator last) {\n    // Possible future enhancement, dispatch on iterator tag type. For forward\n    // iterators we can use std::difference to preallocate the space required\n    // and only do one copy.\n    ClearRetainCapacity();\n    for (; first != last; ++first)\n      emplace_back(*first);\n    IncrementGeneration();\n  }\n\n  void assign(std::initializer_list<value_type> value) {\n    reserve(std::distance(value.begin(), value.end()));\n    assign(value.begin(), value.end());\n  }\n\n  // ---------------------------------------------------------------------------\n  // Accessors.\n  //\n  // Since this class assumes no exceptions, at() and operator[] are equivalent.\n\n  const value_type& at(size_type i) const {\n    DCHECK(i < size());\n    size_t right_size = buffer_.capacity() - begin_;\n    if (begin_ <= end_ || i < right_size)\n      return buffer_[begin_ + i];\n    return buffer_[i - right_size];\n  }\n  value_type& at(size_type i) {\n    return const_cast<value_type&>(std::as_const(*this).at(i));\n  }\n\n  value_type& operator[](size_type i) {\n    return const_cast<value_type&>(std::as_const(*this)[i]);\n  }\n\n  const value_type& operator[](size_type i) const { return at(i); }\n\n  value_type& front() {\n    DCHECK(!empty());\n    return buffer_[begin_];\n  }\n  const value_type& front() const {\n    DCHECK(!empty());\n    return buffer_[begin_];\n  }\n\n  value_type& back() {\n    DCHECK(!empty());\n    return *(--end());\n  }\n  const value_type& back() const {\n    DCHECK(!empty());\n    return *(--end());\n  }\n\n  // ---------------------------------------------------------------------------\n  // Iterators.\n\n  iterator begin() { return iterator(this, begin_); }\n  const_iterator begin() const { return const_iterator(this, begin_); }\n  const_iterator cbegin() const { return const_iterator(this, begin_); }\n\n  iterator end() { return iterator(this, end_); }\n  const_iterator end() const { return const_iterator(this, end_); }\n  const_iterator cend() const { return const_iterator(this, end_); }\n\n  reverse_iterator rbegin() { return reverse_iterator(end()); }\n  const_reverse_iterator rbegin() const {\n    return const_reverse_iterator(end());\n  }\n  const_reverse_iterator crbegin() const { return rbegin(); }\n\n  reverse_iterator rend() { return reverse_iterator(begin()); }\n  const_reverse_iterator rend() const {\n    return const_reverse_iterator(begin());\n  }\n  const_reverse_iterator crend() const { return rend(); }\n\n  // ---------------------------------------------------------------------------\n  // Memory management.\n\n  // IMPORTANT NOTE ON reserve(...): This class implements auto-shrinking of\n  // the buffer when elements are deleted and there is \"too much\" wasted space.\n  // So if you call reserve() with a large size in anticipation of pushing many\n  // elements, but pop an element before the queue is full, the capacity you\n  // reserved may be lost.\n  //\n  // As a result, it's only worthwhile to call reserve() when you're adding\n  // many things at once with no intermediate operations.\n  void reserve(size_type new_capacity) {\n    if (new_capacity > capacity())\n      SetCapacityTo(new_capacity);\n  }\n\n  size_type capacity() const {\n    // One item is wasted to indicate end().\n    return buffer_.capacity() == 0 ? 0 : buffer_.capacity() - 1;\n  }\n\n  void shrink_to_fit() {\n    if (empty()) {\n      // Optimize empty case to really delete everything if there was\n      // something.\n      if (buffer_.capacity())\n        buffer_ = VectorBuffer();\n    } else {\n      SetCapacityTo(size());\n    }\n  }\n\n  // ---------------------------------------------------------------------------\n  // Size management.\n\n  // This will additionally reset the capacity() to 0.\n  void clear() {\n    // This can't resize(0) because that requires a default constructor to\n    // compile, which not all contained classes may implement.\n    ClearRetainCapacity();\n    buffer_ = VectorBuffer();\n  }\n\n  bool empty() const { return begin_ == end_; }\n\n  size_type size() const {\n    if (begin_ <= end_)\n      return end_ - begin_;\n    return buffer_.capacity() - begin_ + end_;\n  }\n\n  // When reducing size, the elements are deleted from the end. When expanding\n  // size, elements are added to the end with |value| or the default\n  // constructed version. Even when using resize(count) to shrink, a default\n  // constructor is required for the code to compile, even though it will not\n  // be called.\n  //\n  // There are two versions rather than using a default value to avoid\n  // creating a temporary when shrinking (when it's not needed). Plus if\n  // the default constructor is desired when expanding usually just calling it\n  // for each element is faster than making a default-constructed temporary and\n  // copying it.\n  void resize(size_type count) {\n    // SEE BELOW VERSION if you change this. The code is mostly the same.\n    if (count > size()) {\n      // This could be slightly more efficient but expanding a queue with\n      // identical elements is unusual and the extra computations of emplacing\n      // one-by-one will typically be small relative to calling the constructor\n      // for every item.\n      ExpandCapacityIfNecessary(count - size());\n      while (size() < count)\n        emplace_back();\n    } else if (count < size()) {\n      size_t new_end = (begin_ + count) % buffer_.capacity();\n      DestructRange(new_end, end_);\n      end_ = new_end;\n\n      ShrinkCapacityIfNecessary();\n    }\n    IncrementGeneration();\n  }\n  void resize(size_type count, const value_type& value) {\n    // SEE ABOVE VERSION if you change this. The code is mostly the same.\n    if (count > size()) {\n      ExpandCapacityIfNecessary(count - size());\n      while (size() < count)\n        emplace_back(value);\n    } else if (count < size()) {\n      size_t new_end = (begin_ + count) % buffer_.capacity();\n      DestructRange(new_end, end_);\n      end_ = new_end;\n\n      ShrinkCapacityIfNecessary();\n    }\n    IncrementGeneration();\n  }\n\n  // ---------------------------------------------------------------------------\n  // Insert and erase.\n  //\n  // Insertion and deletion in the middle is O(n) and invalidates all existing\n  // iterators.\n  //\n  // The implementation of insert isn't optimized as much as it could be. If\n  // the insertion requires that the buffer be grown, it will first be grown\n  // and everything moved, and then the items will be inserted, potentially\n  // moving some items twice. This simplifies the implementation substantially\n  // and means less generated templatized code. Since this is an uncommon\n  // operation for deques, and already relatively slow, it doesn't seem worth\n  // the benefit to optimize this.\n\n  void insert(const_iterator pos, size_type count, const T& value) {\n    ValidateIterator(pos);\n\n    // Optimize insert at the beginning.\n    if (pos == begin()) {\n      ExpandCapacityIfNecessary(count);\n      for (size_t i = 0; i < count; i++)\n        push_front(value);\n      return;\n    }\n\n    iterator insert_cur(this, pos.index_);\n    iterator insert_end;\n    MakeRoomFor(count, &insert_cur, &insert_end);\n    while (insert_cur < insert_end) {\n      new (&buffer_[insert_cur.index_]) T(value);\n      ++insert_cur;\n    }\n\n    IncrementGeneration();\n  }\n\n  // This enable_if keeps this call from getting confused with the (pos, count,\n  // value) version when value is an integer.\n  template <class InputIterator>\n  std::enable_if_t<base::internal::is_iterator<InputIterator>::value, void>\n  insert(const_iterator pos, InputIterator first, InputIterator last) {\n    ValidateIterator(pos);\n\n    const difference_type inserted_items_signed = std::distance(first, last);\n    if (inserted_items_signed == 0)\n      return;  // Can divide by 0 when doing modulo below, so return early.\n    CHECK(inserted_items_signed > 0);\n    const size_type inserted_items =\n        static_cast<size_type>(inserted_items_signed);\n\n    // Make a hole to copy the items into.\n    iterator insert_cur;\n    iterator insert_end;\n    if (pos == begin()) {\n      // Optimize insert at the beginning, nothing needs to be shifted and the\n      // hole is the |inserted_items| block immediately before |begin_|.\n      ExpandCapacityIfNecessary(inserted_items);\n      insert_end = begin();\n      begin_ =\n          (begin_ + buffer_.capacity() - inserted_items) % buffer_.capacity();\n      insert_cur = begin();\n    } else {\n      insert_cur = iterator(this, pos.index_);\n      MakeRoomFor(inserted_items, &insert_cur, &insert_end);\n    }\n\n    // Copy the items.\n    while (insert_cur < insert_end) {\n      new (&buffer_[insert_cur.index_]) T(*first);\n      ++insert_cur;\n      ++first;\n    }\n\n    IncrementGeneration();\n  }\n\n  // These all return an iterator to the inserted item. Existing iterators will\n  // be invalidated.\n  iterator insert(const_iterator pos, const T& value) {\n    return emplace(pos, value);\n  }\n  iterator insert(const_iterator pos, T&& value) {\n    return emplace(pos, std::move(value));\n  }\n  template <class... Args>\n  iterator emplace(const_iterator pos, Args&&... args) {\n    ValidateIterator(pos);\n\n    // Optimize insert at beginning which doesn't require shifting.\n    if (pos == cbegin()) {\n      emplace_front(std::forward<Args>(args)...);\n      return begin();\n    }\n\n    // Do this before we make the new iterators we return.\n    IncrementGeneration();\n\n    iterator insert_begin(this, pos.index_);\n    iterator insert_end;\n    MakeRoomFor(1, &insert_begin, &insert_end);\n    new (&buffer_[insert_begin.index_]) T(std::forward<Args>(args)...);\n\n    return insert_begin;\n  }\n\n  // Calling erase() won't automatically resize the buffer smaller like resize\n  // or the pop functions. Erase is slow and relatively uncommon, and for\n  // normal deque usage a pop will normally be done on a regular basis that\n  // will prevent excessive buffer usage over long periods of time. It's not\n  // worth having the extra code for every template instantiation of erase()\n  // to resize capacity downward to a new buffer.\n  iterator erase(const_iterator pos) { return erase(pos, pos + 1); }\n  iterator erase(const_iterator first, const_iterator last) {\n    ValidateIterator(first);\n    ValidateIterator(last);\n\n    IncrementGeneration();\n\n    // First, call the destructor on the deleted items.\n    if (first.index_ == last.index_) {\n      // Nothing deleted. Need to return early to avoid falling through to\n      // moving items on top of themselves.\n      return iterator(this, first.index_);\n    } else if (first.index_ < last.index_) {\n      // Contiguous range.\n      buffer_.DestructRange(&buffer_[first.index_], &buffer_[last.index_]);\n    } else {\n      // Deleted range wraps around.\n      buffer_.DestructRange(&buffer_[first.index_],\n                            &buffer_[buffer_.capacity()]);\n      buffer_.DestructRange(&buffer_[0], &buffer_[last.index_]);\n    }\n\n    if (first.index_ == begin_) {\n      // This deletion is from the beginning. Nothing needs to be copied, only\n      // begin_ needs to be updated.\n      begin_ = last.index_;\n      return iterator(this, last.index_);\n    }\n\n    // In an erase operation, the shifted items all move logically to the left,\n    // so move them from left-to-right.\n    iterator move_src(this, last.index_);\n    iterator move_src_end = end();\n    iterator move_dest(this, first.index_);\n    for (; move_src < move_src_end; move_src++, move_dest++) {\n      buffer_.MoveRange(&buffer_[move_src.index_],\n                        &buffer_[move_src.index_ + 1],\n                        &buffer_[move_dest.index_]);\n    }\n\n    end_ = move_dest.index_;\n\n    // Since we did not reallocate and only changed things after the erase\n    // element(s), the input iterator's index points to the thing following the\n    // deletion.\n    return iterator(this, first.index_);\n  }\n\n  // ---------------------------------------------------------------------------\n  // Begin/end operations.\n\n  void push_front(const T& value) { emplace_front(value); }\n  void push_front(T&& value) { emplace_front(std::move(value)); }\n\n  void push_back(const T& value) { emplace_back(value); }\n  void push_back(T&& value) { emplace_back(std::move(value)); }\n\n  template <class... Args>\n  reference emplace_front(Args&&... args) {\n    ExpandCapacityIfNecessary(1);\n    if (begin_ == 0)\n      begin_ = buffer_.capacity() - 1;\n    else\n      begin_--;\n    IncrementGeneration();\n    new (&buffer_[begin_]) T(std::forward<Args>(args)...);\n    return front();\n  }\n\n  template <class... Args>\n  reference emplace_back(Args&&... args) {\n    ExpandCapacityIfNecessary(1);\n    new (&buffer_[end_]) T(std::forward<Args>(args)...);\n    if (end_ == buffer_.capacity() - 1)\n      end_ = 0;\n    else\n      end_++;\n    IncrementGeneration();\n    return back();\n  }\n\n  void pop_front() {\n    DCHECK(size());\n    buffer_.DestructRange(&buffer_[begin_], &buffer_[begin_ + 1]);\n    begin_++;\n    if (begin_ == buffer_.capacity())\n      begin_ = 0;\n\n    ShrinkCapacityIfNecessary();\n\n    // Technically popping will not invalidate any iterators since the\n    // underlying buffer will be stable. But in the future we may want to add a\n    // feature that resizes the buffer smaller if there is too much wasted\n    // space. This ensures we can make such a change safely.\n    IncrementGeneration();\n  }\n  void pop_back() {\n    DCHECK(size());\n    if (end_ == 0)\n      end_ = buffer_.capacity() - 1;\n    else\n      end_--;\n    buffer_.DestructRange(&buffer_[end_], &buffer_[end_ + 1]);\n\n    ShrinkCapacityIfNecessary();\n\n    // See pop_front comment about why this is here.\n    IncrementGeneration();\n  }\n\n  // ---------------------------------------------------------------------------\n  // General operations.\n\n  void swap(circular_deque& other) {\n    std::swap(buffer_, other.buffer_);\n    std::swap(begin_, other.begin_);\n    std::swap(end_, other.end_);\n    IncrementGeneration();\n  }\n\n  friend void swap(circular_deque& lhs, circular_deque& rhs) { lhs.swap(rhs); }\n\n private:\n  friend internal::circular_deque_iterator<T>;\n  friend internal::circular_deque_const_iterator<T>;\n\n  // Moves the items in the given circular buffer to the current one. The\n  // source is moved from so will become invalid. The destination buffer must\n  // have already been allocated with enough size.\n  static void MoveBuffer(VectorBuffer& from_buf,\n                         size_t from_begin,\n                         size_t from_end,\n                         VectorBuffer* to_buf,\n                         size_t* to_begin,\n                         size_t* to_end) {\n    size_t from_capacity = from_buf.capacity();\n\n    *to_begin = 0;\n    if (from_begin < from_end) {\n      // Contiguous.\n      from_buf.MoveRange(&from_buf[from_begin], &from_buf[from_end],\n                         to_buf->begin());\n      *to_end = from_end - from_begin;\n    } else if (from_begin > from_end) {\n      // Discontiguous, copy the right side to the beginning of the new buffer.\n      from_buf.MoveRange(&from_buf[from_begin], &from_buf[from_capacity],\n                         to_buf->begin());\n      size_t right_size = from_capacity - from_begin;\n      // Append the left side.\n      from_buf.MoveRange(&from_buf[0], &from_buf[from_end],\n                         &(*to_buf)[right_size]);\n      *to_end = right_size + from_end;\n    } else {\n      // No items.\n      *to_end = 0;\n    }\n  }\n\n  // Expands the buffer size. This assumes the size is larger than the\n  // number of elements in the vector (it won't call delete on anything).\n  void SetCapacityTo(size_t new_capacity) {\n    // Use the capacity + 1 as the internal buffer size to differentiate\n    // empty and full (see definition of buffer_ below).\n    VectorBuffer new_buffer(new_capacity + 1);\n    MoveBuffer(buffer_, begin_, end_, &new_buffer, &begin_, &end_);\n    buffer_ = std::move(new_buffer);\n  }\n  void ExpandCapacityIfNecessary(size_t additional_elts) {\n    size_t min_new_capacity = size() + additional_elts;\n    if (capacity() >= min_new_capacity)\n      return;  // Already enough room.\n\n    min_new_capacity =\n        std::max(min_new_capacity, internal::kCircularBufferInitialCapacity);\n\n    // std::vector always grows by at least 50%. WTF::Deque grows by at least\n    // 25%. We expect queue workloads to generally stay at a similar size and\n    // grow less than a vector might, so use 25%.\n    size_t new_capacity =\n        std::max(min_new_capacity, capacity() + capacity() / 4);\n    SetCapacityTo(new_capacity);\n  }\n\n  void ShrinkCapacityIfNecessary() {\n    // Don't auto-shrink below this size.\n    if (capacity() <= internal::kCircularBufferInitialCapacity)\n      return;\n\n    // Shrink when 100% of the size() is wasted.\n    size_t sz = size();\n    size_t empty_spaces = capacity() - sz;\n    if (empty_spaces < sz)\n      return;\n\n    // Leave 1/4 the size as free capacity, not going below the initial\n    // capacity.\n    size_t new_capacity =\n        std::max(internal::kCircularBufferInitialCapacity, sz + sz / 4);\n    if (new_capacity < capacity()) {\n      // Count extra item to convert to internal capacity.\n      SetCapacityTo(new_capacity);\n    }\n  }\n\n  // Backend for clear() but does not resize the internal buffer.\n  void ClearRetainCapacity() {\n    // This can't resize(0) because that requires a default constructor to\n    // compile, which not all contained classes may implement.\n    DestructRange(begin_, end_);\n    begin_ = 0;\n    end_ = 0;\n    IncrementGeneration();\n  }\n\n  // Calls destructors for the given begin->end indices. The indices may wrap\n  // around. The buffer is not resized, and the begin_ and end_ members are\n  // not changed.\n  void DestructRange(size_t begin, size_t end) {\n    if (end == begin) {\n      return;\n    } else if (end > begin) {\n      buffer_.DestructRange(&buffer_[begin], &buffer_[end]);\n    } else {\n      buffer_.DestructRange(&buffer_[begin], &buffer_[buffer_.capacity()]);\n      buffer_.DestructRange(&buffer_[0], &buffer_[end]);\n    }\n  }\n\n  // Makes room for |count| items starting at |*insert_begin|. Since iterators\n  // are not stable across buffer resizes, |*insert_begin| will be updated to\n  // point to the beginning of the newly opened position in the new array (it's\n  // in/out), and the end of the newly opened position (it's out-only).\n  void MakeRoomFor(size_t count, iterator* insert_begin, iterator* insert_end) {\n    if (count == 0) {\n      *insert_end = *insert_begin;\n      return;\n    }\n\n    // The offset from the beginning will be stable across reallocations.\n    size_t begin_offset = insert_begin->OffsetFromBegin();\n    ExpandCapacityIfNecessary(count);\n\n    insert_begin->index_ = (begin_ + begin_offset) % buffer_.capacity();\n    *insert_end =\n        iterator(this, (insert_begin->index_ + count) % buffer_.capacity());\n\n    // Update the new end and prepare the iterators for copying.\n    iterator src = end();\n    end_ = (end_ + count) % buffer_.capacity();\n    iterator dest = end();\n\n    // Move the elements. This will always involve shifting logically to the\n    // right, so move in a right-to-left order.\n    while (true) {\n      if (src == *insert_begin)\n        break;\n      --src;\n      --dest;\n      buffer_.MoveRange(&buffer_[src.index_], &buffer_[src.index_ + 1],\n                        &buffer_[dest.index_]);\n    }\n  }\n\n#if DCHECK_IS_ON()\n  // Asserts the given index is dereferenceable. The index is an index into the\n  // buffer, not an index used by operator[] or at() which will be offsets from\n  // begin.\n  void CheckValidIndex(size_t i) const {\n    if (begin_ <= end_)\n      DCHECK(i >= begin_ && i < end_);\n    else\n      DCHECK((i >= begin_ && i < buffer_.capacity()) || i < end_);\n  }\n\n  // Asserts the given index is either dereferenceable or points to end().\n  void CheckValidIndexOrEnd(size_t i) const {\n    if (i != end_)\n      CheckValidIndex(i);\n  }\n\n  void ValidateIterator(const const_iterator& i) const {\n    DCHECK(i.parent_deque_ == this);\n    i.CheckUnstableUsage();\n  }\n\n  // See generation_ below.\n  void IncrementGeneration() { generation_++; }\n#else\n  // No-op versions of these functions for release builds.\n  void CheckValidIndex(size_t) const {}\n  void CheckValidIndexOrEnd(size_t) const {}\n  void ValidateIterator(const const_iterator& i) const {}\n  void IncrementGeneration() {}\n#endif\n\n  // Danger, the buffer_.capacity() is the \"internal capacity\" which is\n  // capacity() + 1 since there is an extra item to indicate the end. Otherwise\n  // being completely empty and completely full are indistinguishable (begin ==\n  // end). We could add a separate flag to avoid it, but that adds significant\n  // extra complexity since every computation will have to check for it. Always\n  // keeping one extra unused element in the buffer makes iterator computations\n  // much simpler.\n  //\n  // Container internal code will want to use buffer_.capacity() for offset\n  // computations rather than capacity().\n  VectorBuffer buffer_;\n  size_type begin_ = 0;\n  size_type end_ = 0;\n\n#if DCHECK_IS_ON()\n  // Incremented every time a modification is made that could affect iterator\n  // invalidations.\n  uint64_t generation_ = 0;\n#endif\n};\n\n// Implementations of base::Erase[If] (see base/stl_util.h).\ntemplate <class T, class Value>\nsize_t Erase(circular_deque<T>& container, const Value& value) {\n  auto it = ranges::remove(container, value);\n  size_t removed = std::distance(it, container.end());\n  container.erase(it, container.end());\n  return removed;\n}\n\ntemplate <class T, class Predicate>\nsize_t EraseIf(circular_deque<T>& container, Predicate pred) {\n  auto it = ranges::remove_if(container, pred);\n  size_t removed = std::distance(it, container.end());\n  container.erase(it, container.end());\n  return removed;\n}\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_CONTAINERS_CIRCULAR_DEQUE_H_\n\n// clang-format on\n"
  },
  {
    "path": "tachyon/base/containers/circular_deque_unittest.cc",
    "content": "// Copyright 2017 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n// clang-format off\n\n#include \"tachyon/base/containers/circular_deque.h\"\n\n#include \"gtest/gtest.h\"\n\n// #include \"tachyon/base/memory/raw_ptr.h\"\n#include \"tachyon/base/test/copy_only_int.h\"\n#include \"tachyon/base/test/move_only_int.h\"\n\nusing tachyon::base::internal::VectorBuffer;\n\nnamespace tachyon::base {\n\nnamespace {\n\ncircular_deque<int> MakeSequence(size_t max) {\n  circular_deque<int> ret;\n  for (size_t i = 0; i < max; i++)\n    ret.push_back(i);\n  return ret;\n}\n\n// Cycles through the queue, popping items from the back and pushing items\n// at the front to validate behavior across different configurations of the\n// queue in relation to the underlying buffer. The tester closure is run for\n// each cycle.\ntemplate <class QueueT, class Tester>\nvoid CycleTest(circular_deque<QueueT>& queue, const Tester& tester) {\n  size_t steps = queue.size() * 2;\n  for (size_t i = 0; i < steps; i++) {\n    tester(queue, i);\n    queue.pop_back();\n    queue.push_front(QueueT());\n  }\n}\n\nclass DestructorCounter {\n public:\n  DestructorCounter(int* counter) : counter_(counter) {}\n  ~DestructorCounter() { ++(*counter_); }\n\n private:\n  // TODO(chokobole):\n  // raw_ptr<int> counter_;\n  int* counter_;\n};\n\n}  // namespace\n\nTEST(CircularDeque, FillConstructor) {\n  constexpr size_t num_elts = 9;\n\n  std::vector<int> foo(15);\n  EXPECT_EQ(15u, foo.size());\n\n  // Fill with default constructor.\n  {\n    circular_deque<int> buf(num_elts);\n\n    EXPECT_EQ(num_elts, buf.size());\n    EXPECT_EQ(num_elts, static_cast<size_t>(buf.end() - buf.begin()));\n\n    for (size_t i = 0; i < num_elts; i++)\n      EXPECT_EQ(0, buf[i]);\n  }\n\n  // Fill with explicit value.\n  {\n    int value = 199;\n    circular_deque<int> buf(num_elts, value);\n\n    EXPECT_EQ(num_elts, buf.size());\n    EXPECT_EQ(num_elts, static_cast<size_t>(buf.end() - buf.begin()));\n\n    for (size_t i = 0; i < num_elts; i++)\n      EXPECT_EQ(value, buf[i]);\n  }\n}\n\nTEST(CircularDeque, CopyAndRangeConstructor) {\n  int values[] = {1, 2, 3, 4, 5, 6};\n  circular_deque<CopyOnlyInt> first(std::begin(values), std::end(values));\n\n  circular_deque<CopyOnlyInt> second(first);\n  EXPECT_EQ(6u, second.size());\n  for (int i = 0; i < 6; i++)\n    EXPECT_EQ(i + 1, second[i].data());\n}\n\nTEST(CircularDeque, MoveConstructor) {\n  int values[] = {1, 2, 3, 4, 5, 6};\n  circular_deque<MoveOnlyInt> first(std::begin(values), std::end(values));\n\n  circular_deque<MoveOnlyInt> second(std::move(first));\n  EXPECT_TRUE(first.empty());\n  EXPECT_EQ(6u, second.size());\n  for (int i = 0; i < 6; i++)\n    EXPECT_EQ(i + 1, second[i].data());\n}\n\nTEST(CircularDeque, InitializerListConstructor) {\n  circular_deque<int> empty({});\n  ASSERT_TRUE(empty.empty());\n\n  circular_deque<int> first({1, 2, 3, 4, 5, 6});\n  EXPECT_EQ(6u, first.size());\n  for (int i = 0; i < 6; i++)\n    EXPECT_EQ(i + 1, first[i]);\n}\n\nTEST(CircularDeque, Destructor) {\n  int destruct_count = 0;\n\n  // Contiguous buffer.\n  {\n    circular_deque<DestructorCounter> q;\n    q.resize(5, DestructorCounter(&destruct_count));\n\n    EXPECT_EQ(1, destruct_count);  // The temporary in the call to resize().\n    destruct_count = 0;\n  }\n  EXPECT_EQ(5, destruct_count);  // One call for each.\n\n  // Force a wraparound buffer.\n  {\n    circular_deque<DestructorCounter> q;\n    q.reserve(7);\n    q.resize(5, DestructorCounter(&destruct_count));\n\n    // Cycle throught some elements in our buffer to force a wraparound.\n    destruct_count = 0;\n    for (int i = 0; i < 4; i++) {\n      q.emplace_back(&destruct_count);\n      q.pop_front();\n    }\n    EXPECT_EQ(4, destruct_count);  // One for each cycle.\n    destruct_count = 0;\n  }\n  EXPECT_EQ(5, destruct_count);  // One call for each.\n}\n\nTEST(CircularDeque, EqualsCopy) {\n  circular_deque<int> first = {1, 2, 3, 4, 5, 6};\n  circular_deque<int> copy;\n  EXPECT_TRUE(copy.empty());\n  copy = first;\n  EXPECT_EQ(6u, copy.size());\n  for (int i = 0; i < 6; i++) {\n    EXPECT_EQ(i + 1, first[i]);\n    EXPECT_EQ(i + 1, copy[i]);\n    EXPECT_NE(&first[i], &copy[i]);\n  }\n}\n\nTEST(CircularDeque, EqualsMove) {\n  circular_deque<int> first = {1, 2, 3, 4, 5, 6};\n  circular_deque<int> move;\n  EXPECT_TRUE(move.empty());\n  move = std::move(first);\n  EXPECT_TRUE(first.empty());\n  EXPECT_EQ(6u, move.size());\n  for (int i = 0; i < 6; i++)\n    EXPECT_EQ(i + 1, move[i]);\n}\n\n// Tests that self-assignment is a no-op.\nTEST(CircularDeque, EqualsSelf) {\n  circular_deque<int> q = {1, 2, 3, 4, 5, 6};\n  q = *&q;  // The *& defeats Clang's -Wself-assign warning.\n  EXPECT_EQ(6u, q.size());\n  for (int i = 0; i < 6; i++)\n    EXPECT_EQ(i + 1, q[i]);\n}\n\nTEST(CircularDeque, EqualsInitializerList) {\n  circular_deque<int> q;\n  EXPECT_TRUE(q.empty());\n  q = {1, 2, 3, 4, 5, 6};\n  EXPECT_EQ(6u, q.size());\n  for (int i = 0; i < 6; i++)\n    EXPECT_EQ(i + 1, q[i]);\n}\n\nTEST(CircularDeque, AssignCountValue) {\n  circular_deque<int> empty;\n  empty.assign(0, 52);\n  EXPECT_EQ(0u, empty.size());\n\n  circular_deque<int> full;\n  size_t count = 13;\n  int value = 12345;\n  full.assign(count, value);\n  EXPECT_EQ(count, full.size());\n\n  for (size_t i = 0; i < count; i++)\n    EXPECT_EQ(value, full[i]);\n}\n\nTEST(CircularDeque, AssignIterator) {\n  int range[8] = {11, 12, 13, 14, 15, 16, 17, 18};\n\n  circular_deque<int> empty;\n  empty.assign(std::begin(range), std::begin(range));\n  EXPECT_TRUE(empty.empty());\n\n  circular_deque<int> full;\n  full.assign(std::begin(range), std::end(range));\n  EXPECT_EQ(8u, full.size());\n  for (size_t i = 0; i < 8; i++)\n    EXPECT_EQ(range[i], full[i]);\n}\n\nTEST(CircularDeque, AssignInitializerList) {\n  circular_deque<int> empty;\n  empty.assign({});\n  EXPECT_TRUE(empty.empty());\n\n  circular_deque<int> full;\n  full.assign({11, 12, 13, 14, 15, 16, 17, 18});\n  EXPECT_EQ(8u, full.size());\n  for (int i = 0; i < 8; i++)\n    EXPECT_EQ(11 + i, full[i]);\n}\n\n// Tests [] and .at().\nTEST(CircularDeque, At) {\n  circular_deque<int> q = MakeSequence(10);\n  CycleTest(q, [](const circular_deque<int>& q, size_t cycle) {\n    size_t expected_size = 10;\n    EXPECT_EQ(expected_size, q.size());\n\n    // A sequence of 0's.\n    size_t index = 0;\n    size_t num_zeros = std::min(expected_size, cycle);\n    for (size_t i = 0; i < num_zeros; i++, index++) {\n      EXPECT_EQ(0, q[index]);\n      EXPECT_EQ(0, q.at(index));\n    }\n\n    // Followed by a sequence of increasing ints.\n    size_t num_ints = expected_size - num_zeros;\n    for (int i = 0; i < static_cast<int>(num_ints); i++, index++) {\n      EXPECT_EQ(i, q[index]);\n      EXPECT_EQ(i, q.at(index));\n    }\n  });\n}\n\n// This also tests the copy constructor with lots of different types of\n// input configurations.\nTEST(CircularDeque, FrontBackPushPop) {\n  circular_deque<int> q = MakeSequence(10);\n\n  int expected_front = 0;\n  int expected_back = 9;\n\n  // Go in one direction.\n  for (int i = 0; i < 100; i++) {\n    const circular_deque<int> const_q(q);\n\n    EXPECT_EQ(expected_front, q.front());\n    EXPECT_EQ(expected_back, q.back());\n    EXPECT_EQ(expected_front, const_q.front());\n    EXPECT_EQ(expected_back, const_q.back());\n\n    expected_front++;\n    expected_back++;\n\n    q.pop_front();\n    q.push_back(expected_back);\n  }\n\n  // Go back in reverse.\n  for (int i = 0; i < 100; i++) {\n    const circular_deque<int> const_q(q);\n\n    EXPECT_EQ(expected_front, q.front());\n    EXPECT_EQ(expected_back, q.back());\n    EXPECT_EQ(expected_front, const_q.front());\n    EXPECT_EQ(expected_back, const_q.back());\n\n    expected_front--;\n    expected_back--;\n\n    q.pop_back();\n    q.push_front(expected_front);\n  }\n}\n\nTEST(CircularDeque, ReallocateWithSplitBuffer) {\n  // Tests reallocating a deque with an internal buffer that looks like this:\n  //   4   5   x   x   0   1   2   3\n  //       end-^       ^-begin\n  circular_deque<int> q;\n  q.reserve(7);  // Internal buffer is always 1 larger than requested.\n  q.push_back(-1);\n  q.push_back(-1);\n  q.push_back(-1);\n  q.push_back(-1);\n  q.push_back(0);\n  q.pop_front();\n  q.pop_front();\n  q.pop_front();\n  q.pop_front();\n  q.push_back(1);\n  q.push_back(2);\n  q.push_back(3);\n  q.push_back(4);\n  q.push_back(5);\n\n  q.shrink_to_fit();\n  EXPECT_EQ(6u, q.size());\n\n  EXPECT_EQ(0, q[0]);\n  EXPECT_EQ(1, q[1]);\n  EXPECT_EQ(2, q[2]);\n  EXPECT_EQ(3, q[3]);\n  EXPECT_EQ(4, q[4]);\n  EXPECT_EQ(5, q[5]);\n}\n\nTEST(CircularDeque, Swap) {\n  circular_deque<int> a = MakeSequence(10);\n  circular_deque<int> b = MakeSequence(100);\n\n  a.swap(b);\n  EXPECT_EQ(100u, a.size());\n  for (int i = 0; i < 100; i++)\n    EXPECT_EQ(i, a[i]);\n\n  EXPECT_EQ(10u, b.size());\n  for (int i = 0; i < 10; i++)\n    EXPECT_EQ(i, b[i]);\n}\n\nTEST(CircularDeque, Iteration) {\n  circular_deque<int> q = MakeSequence(10);\n\n  int expected_front = 0;\n  int expected_back = 9;\n\n  // This loop causes various combinations of begin and end to be tested.\n  for (int i = 0; i < 30; i++) {\n    // Range-based for loop going forward.\n    int current_expected = expected_front;\n    for (int cur : q) {\n      EXPECT_EQ(current_expected, cur);\n      current_expected++;\n    }\n\n    // Manually test reverse iterators.\n    current_expected = expected_back;\n    for (auto cur = q.crbegin(); cur < q.crend(); cur++) {\n      EXPECT_EQ(current_expected, *cur);\n      current_expected--;\n    }\n\n    expected_front++;\n    expected_back++;\n\n    q.pop_front();\n    q.push_back(expected_back);\n  }\n\n  // Go back in reverse.\n  for (int i = 0; i < 100; i++) {\n    const circular_deque<int> const_q(q);\n\n    EXPECT_EQ(expected_front, q.front());\n    EXPECT_EQ(expected_back, q.back());\n    EXPECT_EQ(expected_front, const_q.front());\n    EXPECT_EQ(expected_back, const_q.back());\n\n    expected_front--;\n    expected_back--;\n\n    q.pop_back();\n    q.push_front(expected_front);\n  }\n}\n\nTEST(CircularDeque, IteratorComparisons) {\n  circular_deque<int> q = MakeSequence(10);\n\n  // This loop causes various combinations of begin and end to be tested.\n  for (int i = 0; i < 30; i++) {\n    EXPECT_LT(q.begin(), q.end());\n    EXPECT_LE(q.begin(), q.end());\n    EXPECT_LE(q.begin(), q.begin());\n\n    EXPECT_GT(q.end(), q.begin());\n    EXPECT_GE(q.end(), q.begin());\n    EXPECT_GE(q.end(), q.end());\n\n    EXPECT_EQ(q.begin(), q.begin());\n    EXPECT_NE(q.begin(), q.end());\n\n    q.push_front(10);\n    q.pop_back();\n  }\n}\n\nTEST(CircularDeque, IteratorIncDec) {\n  circular_deque<int> q;\n\n  // No-op offset computations with no capacity.\n  EXPECT_EQ(q.end(), q.end() + 0);\n  EXPECT_EQ(q.end(), q.end() - 0);\n\n  q = MakeSequence(10);\n\n  // Mutable preincrement, predecrement.\n  {\n    circular_deque<int>::iterator it = q.begin();\n    circular_deque<int>::iterator op_result = ++it;\n    EXPECT_EQ(1, *op_result);\n    EXPECT_EQ(1, *it);\n\n    op_result = --it;\n    EXPECT_EQ(0, *op_result);\n    EXPECT_EQ(0, *it);\n  }\n\n  // Const preincrement, predecrement.\n  {\n    circular_deque<int>::const_iterator it = q.begin();\n    circular_deque<int>::const_iterator op_result = ++it;\n    EXPECT_EQ(1, *op_result);\n    EXPECT_EQ(1, *it);\n\n    op_result = --it;\n    EXPECT_EQ(0, *op_result);\n    EXPECT_EQ(0, *it);\n  }\n\n  // Mutable postincrement, postdecrement.\n  {\n    circular_deque<int>::iterator it = q.begin();\n    circular_deque<int>::iterator op_result = it++;\n    EXPECT_EQ(0, *op_result);\n    EXPECT_EQ(1, *it);\n\n    op_result = it--;\n    EXPECT_EQ(1, *op_result);\n    EXPECT_EQ(0, *it);\n  }\n\n  // Const postincrement, postdecrement.\n  {\n    circular_deque<int>::const_iterator it = q.begin();\n    circular_deque<int>::const_iterator op_result = it++;\n    EXPECT_EQ(0, *op_result);\n    EXPECT_EQ(1, *it);\n\n    op_result = it--;\n    EXPECT_EQ(1, *op_result);\n    EXPECT_EQ(0, *it);\n  }\n}\n\nTEST(CircularDeque, IteratorIntegerOps) {\n  circular_deque<int> q = MakeSequence(10);\n\n  int expected_front = 0;\n  int expected_back = 9;\n\n  for (int i = 0; i < 30; i++) {\n    EXPECT_EQ(0, q.begin() - q.begin());\n    EXPECT_EQ(0, q.end() - q.end());\n    EXPECT_EQ(q.size(), static_cast<size_t>(q.end() - q.begin()));\n\n    // +=\n    circular_deque<int>::iterator eight = q.begin();\n    eight += 8;\n    EXPECT_EQ(8, eight - q.begin());\n    EXPECT_EQ(expected_front + 8, *eight);\n\n    // -=\n    eight -= 8;\n    EXPECT_EQ(q.begin(), eight);\n\n    // +\n    eight = eight + 8;\n    EXPECT_EQ(8, eight - q.begin());\n\n    // -\n    eight = eight - 8;\n    EXPECT_EQ(q.begin(), eight);\n\n    expected_front++;\n    expected_back++;\n\n    q.pop_front();\n    q.push_back(expected_back);\n  }\n}\n\nTEST(CircularDeque, IteratorArrayAccess) {\n  circular_deque<int> q = MakeSequence(10);\n\n  circular_deque<int>::iterator begin = q.begin();\n  EXPECT_EQ(0, begin[0]);\n  EXPECT_EQ(9, begin[9]);\n\n  circular_deque<int>::iterator end = q.end();\n  EXPECT_EQ(0, end[-10]);\n  EXPECT_EQ(9, end[-1]);\n\n  begin[0] = 100;\n  EXPECT_EQ(100, end[-10]);\n}\n\nTEST(CircularDeque, ReverseIterator) {\n  circular_deque<int> q;\n  q.push_back(4);\n  q.push_back(3);\n  q.push_back(2);\n  q.push_back(1);\n\n  circular_deque<int>::reverse_iterator iter = q.rbegin();\n  EXPECT_EQ(1, *iter);\n  iter++;\n  EXPECT_EQ(2, *iter);\n  ++iter;\n  EXPECT_EQ(3, *iter);\n  iter++;\n  EXPECT_EQ(4, *iter);\n  ++iter;\n  EXPECT_EQ(q.rend(), iter);\n}\n\nTEST(CircularDeque, CapacityReserveShrink) {\n  circular_deque<int> q;\n\n  // A default constructed queue should have no capacity since it should waste\n  // no space.\n  EXPECT_TRUE(q.empty());\n  EXPECT_EQ(0u, q.size());\n  EXPECT_EQ(0u, q.capacity());\n\n  size_t new_capacity = 100;\n  q.reserve(new_capacity);\n  EXPECT_EQ(new_capacity, q.capacity());\n\n  // Adding that many items should not cause a resize.\n  for (size_t i = 0; i < new_capacity; i++)\n    q.push_back(i);\n  EXPECT_EQ(new_capacity, q.size());\n  EXPECT_EQ(new_capacity, q.capacity());\n\n  // Shrink to fit to a smaller size.\n  size_t capacity_2 = new_capacity / 2;\n  q.resize(capacity_2);\n  q.shrink_to_fit();\n  EXPECT_EQ(capacity_2, q.size());\n  EXPECT_EQ(capacity_2, q.capacity());\n}\n\nTEST(CircularDeque, CapacityAutoShrink) {\n  size_t big_size = 1000u;\n  circular_deque<int> q;\n  q.resize(big_size);\n\n  size_t big_capacity = q.capacity();\n\n  // Delete 3/4 of the items.\n  for (size_t i = 0; i < big_size / 4 * 3; i++)\n    q.pop_back();\n\n  // The capacity should have shrunk by deleting that many items.\n  size_t medium_capacity = q.capacity();\n  EXPECT_GT(big_capacity, medium_capacity);\n\n  // Using resize to shrink should keep some extra capacity.\n  q.resize(1);\n  EXPECT_LT(1u, q.capacity());\n\n  q.resize(0);\n  EXPECT_LT(0u, q.capacity());\n\n  // Using clear() should delete everything.\n  q.clear();\n  EXPECT_EQ(0u, q.capacity());\n}\n\nTEST(CircularDeque, ClearAndEmpty) {\n  circular_deque<int> q;\n  EXPECT_TRUE(q.empty());\n\n  q.resize(10);\n  EXPECT_EQ(10u, q.size());\n  EXPECT_FALSE(q.empty());\n\n  q.clear();\n  EXPECT_EQ(0u, q.size());\n  EXPECT_TRUE(q.empty());\n\n  // clear() also should reset the capacity.\n  EXPECT_EQ(0u, q.capacity());\n}\n\nTEST(CircularDeque, Resize) {\n  circular_deque<int> q;\n\n  // Resize with default constructor.\n  size_t first_size = 10;\n  q.resize(first_size);\n  EXPECT_EQ(first_size, q.size());\n  for (size_t i = 0; i < first_size; i++)\n    EXPECT_EQ(0, q[i]);\n\n  // Resize with different value.\n  size_t second_expand = 10;\n  q.resize(first_size + second_expand, 3);\n  EXPECT_EQ(first_size + second_expand, q.size());\n  for (size_t i = 0; i < first_size; i++)\n    EXPECT_EQ(0, q[i]);\n  for (size_t i = 0; i < second_expand; i++)\n    EXPECT_EQ(3, q[i + first_size]);\n\n  // Erase from the end and add to the beginning so resize is forced to cross\n  // a circular buffer wrap boundary.\n  q.shrink_to_fit();\n  for (int i = 0; i < 5; i++) {\n    q.pop_back();\n    q.push_front(6);\n  }\n  q.resize(10);\n\n  EXPECT_EQ(6, q[0]);\n  EXPECT_EQ(6, q[1]);\n  EXPECT_EQ(6, q[2]);\n  EXPECT_EQ(6, q[3]);\n  EXPECT_EQ(6, q[4]);\n  EXPECT_EQ(0, q[5]);\n  EXPECT_EQ(0, q[6]);\n  EXPECT_EQ(0, q[7]);\n  EXPECT_EQ(0, q[8]);\n  EXPECT_EQ(0, q[9]);\n}\n\n// Tests destructor behavior of resize.\nTEST(CircularDeque, ResizeDelete) {\n  int counter = 0;\n  circular_deque<DestructorCounter> q;\n  q.resize(10, DestructorCounter(&counter));\n  // The one temporary when calling resize() should be deleted, that's it.\n  EXPECT_EQ(1, counter);\n\n  // The loops below assume the capacity will be set by resize().\n  EXPECT_EQ(10u, q.capacity());\n\n  // Delete some via resize(). This is done so that the wasted items are\n  // still greater than the size() so that auto-shrinking is not triggered\n  // (which will mess up our destructor counting).\n  counter = 0;\n  q.resize(8, DestructorCounter(&counter));\n  // 2 deleted ones + the one temporary in the resize() call.\n  EXPECT_EQ(3, counter);\n\n  // Cycle through some items so two items will cross the boundary in the\n  // 11-item buffer (one more than the capacity).\n  //   Before: x x x x x x x x . . .\n  //   After:  x . . . x x x x x x x\n  counter = 0;\n  for (int i = 0; i < 4; i++) {\n    q.emplace_back(&counter);\n    q.pop_front();\n  }\n  EXPECT_EQ(4, counter);  // Loop should have deleted 7 items.\n\n  // Delete two items with resize, these should be on either side of the\n  // buffer wrap point.\n  counter = 0;\n  q.resize(6, DestructorCounter(&counter));\n  // 2 deleted ones + the one temporary in the resize() call.\n  EXPECT_EQ(3, counter);\n}\n\nTEST(CircularDeque, InsertEraseSingle) {\n  circular_deque<int> q;\n  q.push_back(1);\n  q.push_back(2);\n\n  // Insert at the beginning.\n  auto result = q.insert(q.begin(), 0);\n  EXPECT_EQ(q.begin(), result);\n  EXPECT_EQ(3u, q.size());\n  EXPECT_EQ(0, q[0]);\n  EXPECT_EQ(1, q[1]);\n  EXPECT_EQ(2, q[2]);\n\n  // Erase at the beginning.\n  result = q.erase(q.begin());\n  EXPECT_EQ(q.begin(), result);\n  EXPECT_EQ(2u, q.size());\n  EXPECT_EQ(1, q[0]);\n  EXPECT_EQ(2, q[1]);\n\n  // Insert at the end.\n  result = q.insert(q.end(), 3);\n  EXPECT_EQ(q.end() - 1, result);\n  EXPECT_EQ(1, q[0]);\n  EXPECT_EQ(2, q[1]);\n  EXPECT_EQ(3, q[2]);\n\n  // Erase at the end.\n  result = q.erase(q.end() - 1);\n  EXPECT_EQ(q.end(), result);\n  EXPECT_EQ(1, q[0]);\n  EXPECT_EQ(2, q[1]);\n\n  // Insert in the middle.\n  result = q.insert(q.begin() + 1, 10);\n  EXPECT_EQ(q.begin() + 1, result);\n  EXPECT_EQ(1, q[0]);\n  EXPECT_EQ(10, q[1]);\n  EXPECT_EQ(2, q[2]);\n\n  // Erase in the middle.\n  result = q.erase(q.begin() + 1);\n  EXPECT_EQ(q.begin() + 1, result);\n  EXPECT_EQ(1, q[0]);\n  EXPECT_EQ(2, q[1]);\n}\n\nTEST(CircularDeque, InsertFill) {\n  circular_deque<int> q;\n\n  // Fill when empty.\n  q.insert(q.begin(), 2, 1);\n\n  // 0's at the beginning.\n  q.insert(q.begin(), 2, 0);\n\n  // 50's in the middle (now at offset 3).\n  q.insert(q.begin() + 3, 2, 50);\n\n  // 200's at the end.\n  q.insert(q.end(), 2, 200);\n\n  ASSERT_EQ(8u, q.size());\n  EXPECT_EQ(0, q[0]);\n  EXPECT_EQ(0, q[1]);\n  EXPECT_EQ(1, q[2]);\n  EXPECT_EQ(50, q[3]);\n  EXPECT_EQ(50, q[4]);\n  EXPECT_EQ(1, q[5]);\n  EXPECT_EQ(200, q[6]);\n  EXPECT_EQ(200, q[7]);\n}\n\nTEST(CircularDeque, InsertEraseRange) {\n  circular_deque<int> q;\n\n  // Erase nothing from an empty deque should work.\n  q.erase(q.begin(), q.end());\n\n  // Loop index used below to shift the used items in the buffer.\n  for (int i = 0; i < 10; i++) {\n    circular_deque<int> source;\n\n    // Fill empty range.\n    q.insert(q.begin(), source.begin(), source.end());\n\n    // Have some stuff to insert.\n    source.push_back(1);\n    source.push_back(2);\n\n    q.insert(q.begin(), source.begin(), source.end());\n\n    // Shift the used items in the buffer by i which will place the two used\n    // elements in different places in the buffer each time through this loop.\n    for (int shift_i = 0; shift_i < i; shift_i++) {\n      q.push_back(0);\n      q.pop_front();\n    }\n\n    // Set the two items to notable values so we can check for them later.\n    ASSERT_EQ(2u, q.size());\n    q[0] = 100;\n    q[1] = 101;\n\n    // Insert at the beginning, middle (now at offset 3), and end.\n    q.insert(q.begin(), source.begin(), source.end());\n    q.insert(q.begin() + 3, source.begin(), source.end());\n    q.insert(q.end(), source.begin(), source.end());\n\n    ASSERT_EQ(8u, q.size());\n    EXPECT_EQ(1, q[0]);\n    EXPECT_EQ(2, q[1]);\n    EXPECT_EQ(100, q[2]);  // First inserted one.\n    EXPECT_EQ(1, q[3]);\n    EXPECT_EQ(2, q[4]);\n    EXPECT_EQ(101, q[5]);  // First inserted second one.\n    EXPECT_EQ(1, q[6]);\n    EXPECT_EQ(2, q[7]);\n\n    // Now erase the inserted ranges. Try each subsection also with no items\n    // being erased, which should be a no-op.\n    auto result = q.erase(q.begin(), q.begin());  // No-op.\n    EXPECT_EQ(q.begin(), result);\n    result = q.erase(q.begin(), q.begin() + 2);\n    EXPECT_EQ(q.begin(), result);\n\n    result = q.erase(q.begin() + 1, q.begin() + 1);  // No-op.\n    EXPECT_EQ(q.begin() + 1, result);\n    result = q.erase(q.begin() + 1, q.begin() + 3);\n    EXPECT_EQ(q.begin() + 1, result);\n\n    result = q.erase(q.end() - 2, q.end() - 2);  // No-op.\n    EXPECT_EQ(q.end() - 2, result);\n    result = q.erase(q.end() - 2, q.end());\n    EXPECT_EQ(q.end(), result);\n\n    ASSERT_EQ(2u, q.size());\n    EXPECT_EQ(100, q[0]);\n    EXPECT_EQ(101, q[1]);\n\n    // Erase everything.\n    result = q.erase(q.begin(), q.end());\n    EXPECT_EQ(q.end(), result);\n    EXPECT_TRUE(q.empty());\n  }\n}\n\nTEST(CircularDeque, EmplaceMoveOnly) {\n  int values[] = {1, 3};\n  circular_deque<MoveOnlyInt> q(std::begin(values), std::end(values));\n\n  q.emplace(q.begin(), MoveOnlyInt(0));\n  q.emplace(q.begin() + 2, MoveOnlyInt(2));\n  q.emplace(q.end(), MoveOnlyInt(4));\n\n  ASSERT_EQ(5u, q.size());\n  EXPECT_EQ(0, q[0].data());\n  EXPECT_EQ(1, q[1].data());\n  EXPECT_EQ(2, q[2].data());\n  EXPECT_EQ(3, q[3].data());\n  EXPECT_EQ(4, q[4].data());\n}\n\nTEST(CircularDeque, EmplaceFrontBackReturnsReference) {\n  circular_deque<int> q;\n  q.reserve(2);\n\n  int& front = q.emplace_front(1);\n  int& back = q.emplace_back(2);\n  ASSERT_EQ(2u, q.size());\n  EXPECT_EQ(1, q[0]);\n  EXPECT_EQ(2, q[1]);\n\n  EXPECT_EQ(&front, &q.front());\n  EXPECT_EQ(&back, &q.back());\n\n  front = 3;\n  back = 4;\n\n  ASSERT_EQ(2u, q.size());\n  EXPECT_EQ(3, q[0]);\n  EXPECT_EQ(4, q[1]);\n\n  EXPECT_EQ(&front, &q.front());\n  EXPECT_EQ(&back, &q.back());\n}\n\n/*\nThis test should assert in a debug build. It tries to dereference an iterator\nafter mutating the container. Uncomment to double-check that this works.\nTEST(CircularDeque, UseIteratorAfterMutate) {\n  circular_deque<int> q;\n  q.push_back(0);\n\n  auto old_begin = q.begin();\n  EXPECT_EQ(0, *old_begin);\n\n  q.push_back(1);\n  EXPECT_EQ(0, *old_begin);  // Should DCHECK.\n}\n*/\n\n}  // namespace tachyon::base\n\n// clang-format on\n"
  },
  {
    "path": "tachyon/base/containers/container_util.h",
    "content": "#ifndef TACHYON_BASE_CONTAINERS_CONTAINER_UTIL_H_\n#define TACHYON_BASE_CONTAINERS_CONTAINER_UTIL_H_\n\n#include <algorithm>\n#include <iterator>\n#include <optional>\n#include <utility>\n#include <vector>\n\n#include \"absl/algorithm/container.h\"\n\n#include \"tachyon/base/functional/functor_traits.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/numerics/checked_math.h\"\n#include \"tachyon/base/openmp_util.h\"\n#include \"tachyon/base/random.h\"\n\nnamespace tachyon::base {\n\ntemplate <typename T>\nstd::vector<T> CreateRangedVector(T start, T end, T step = 1) {\n  CHECK_LT(start, end);\n  CHECK_GT(step, T{0});\n  std::vector<T> ret;\n  size_t size = static_cast<size_t>((end - start + step - 1) / step);\n  ret.reserve(size);\n  T v = start;\n  std::generate_n(std::back_inserter(ret), size, [&v, step]() {\n    T ret = v;\n    v += step;\n    return ret;\n  });\n  return ret;\n}\n\ntemplate <typename Generator,\n          typename FunctorTraits = internal::MakeFunctorTraits<Generator>,\n          typename ReturnType = typename FunctorTraits::ReturnType,\n          typename RunType = typename FunctorTraits::RunType,\n          typename ArgList = internal::ExtractArgs<RunType>,\n          size_t ArgNum = internal::GetSize<ArgList>,\n          std::enable_if_t<ArgNum == 0>* = nullptr>\nstd::vector<ReturnType> CreateVector(size_t size, Generator&& generator) {\n  std::vector<ReturnType> ret;\n  ret.reserve(size);\n  std::generate_n(std::back_inserter(ret), size,\n                  std::forward<Generator>(generator));\n  return ret;\n}\n\n// |generator| is called in parallel, so it should be thread-safe.\ntemplate <typename Generator,\n          typename FunctorTraits = internal::MakeFunctorTraits<Generator>,\n          typename ReturnType = typename FunctorTraits::ReturnType,\n          typename RunType = typename FunctorTraits::RunType,\n          typename ArgList = internal::ExtractArgs<RunType>,\n          size_t ArgNum = internal::GetSize<ArgList>,\n          std::enable_if_t<ArgNum == 0>* = nullptr>\nstd::vector<ReturnType> CreateVectorParallel(size_t size,\n                                             Generator&& generator) {\n  std::vector<ReturnType> ret(size);\n  OMP_PARALLEL_FOR(size_t i = 0; i < size; ++i) { ret[i] = generator(); }\n  return ret;\n}\n\ntemplate <typename Generator,\n          typename FunctorTraits = internal::MakeFunctorTraits<Generator>,\n          typename ReturnType = typename FunctorTraits::ReturnType,\n          typename RunType = typename FunctorTraits::RunType,\n          typename ArgList = internal::ExtractArgs<RunType>,\n          size_t ArgNum = internal::GetSize<ArgList>,\n          std::enable_if_t<ArgNum == 1>* = nullptr>\nstd::vector<ReturnType> CreateVector(size_t size, Generator&& generator) {\n  std::vector<ReturnType> ret;\n  ret.reserve(size);\n  size_t idx = 0;\n  std::generate_n(\n      std::back_inserter(ret), size,\n      [&idx, generator = std::forward<Generator>(generator)]() mutable {\n        return generator(idx++);\n      });\n  return ret;\n}\n\n// |generator| is called in parallel, so it should be thread-safe.\ntemplate <typename Generator,\n          typename FunctorTraits = internal::MakeFunctorTraits<Generator>,\n          typename ReturnType = typename FunctorTraits::ReturnType,\n          typename RunType = typename FunctorTraits::RunType,\n          typename ArgList = internal::ExtractArgs<RunType>,\n          size_t ArgNum = internal::GetSize<ArgList>,\n          std::enable_if_t<ArgNum == 1>* = nullptr>\nstd::vector<ReturnType> CreateVectorParallel(size_t size,\n                                             Generator&& generator) {\n  std::vector<ReturnType> ret(size);\n  OMP_PARALLEL_FOR(size_t i = 0; i < size; ++i) { ret[i] = generator(i); }\n  return ret;\n}\n\ntemplate <typename Iterator, typename UnaryOp,\n          typename FunctorTraits = internal::MakeFunctorTraits<UnaryOp>,\n          typename ReturnType = typename FunctorTraits::ReturnType,\n          typename RunType = typename FunctorTraits::RunType,\n          typename ArgList = internal::ExtractArgs<RunType>,\n          size_t ArgNum = internal::GetSize<ArgList>,\n          std::enable_if_t<ArgNum == 1>* = nullptr>\nstd::vector<ReturnType> Map(Iterator begin, Iterator end, UnaryOp&& op) {\n  std::vector<ReturnType> ret;\n  ret.reserve(std::distance(begin, end));\n  std::transform(begin, end, std::back_inserter(ret),\n                 std::forward<UnaryOp>(op));\n  return ret;\n}\n\ntemplate <typename Iterator, typename UnaryOp,\n          typename FunctorTraits = internal::MakeFunctorTraits<UnaryOp>,\n          typename ReturnType = typename FunctorTraits::ReturnType,\n          typename RunType = typename FunctorTraits::RunType,\n          typename ArgList = internal::ExtractArgs<RunType>,\n          size_t ArgNum = internal::GetSize<ArgList>,\n          std::enable_if_t<ArgNum == 2>* = nullptr>\nstd::vector<ReturnType> Map(Iterator begin, Iterator end, UnaryOp&& op) {\n  std::vector<ReturnType> ret;\n  ret.reserve(std::distance(begin, end));\n  size_t idx = 0;\n  std::transform(begin, end, std::back_inserter(ret),\n                 [&idx, op = std::forward<UnaryOp>(op)](auto& item) mutable {\n                   return op(idx++, item);\n                 });\n  return ret;\n}\n\ntemplate <typename Container, typename UnaryOp>\nauto Map(Container&& container, UnaryOp&& op) {\n  return Map(std::begin(container), std::end(container),\n             std::forward<UnaryOp>(op));\n}\n\ntemplate <typename Iterator, typename UnaryOp,\n          typename FunctorTraits = internal::MakeFunctorTraits<UnaryOp>,\n          typename ReturnType = typename FunctorTraits::ReturnType::value_type>\nstd::vector<ReturnType> FlatMap(Iterator begin, Iterator end, UnaryOp&& op) {\n  std::vector<std::vector<ReturnType>> tmp;\n  tmp.reserve(std::distance(begin, end));\n  std::transform(begin, end, std::back_inserter(tmp),\n                 std::forward<UnaryOp>(op));\n\n  base::CheckedNumeric<size_t> size = 0;\n  for (size_t i = 0; i < tmp.size(); ++i) {\n    size += tmp[i].size();\n  }\n\n  std::vector<ReturnType> ret;\n  ret.reserve(size.ValueOrDie());\n  std::for_each(tmp.begin(), tmp.end(), [&ret](std::vector<ReturnType>& vec) {\n    ret.insert(ret.end(), std::make_move_iterator(vec.begin()),\n               std::make_move_iterator(vec.end()));\n  });\n  return ret;\n}\n\ntemplate <typename Container, typename UnaryOp>\nauto FlatMap(Container&& container, UnaryOp&& op) {\n  return FlatMap(std::begin(container), std::end(container),\n                 std::forward<UnaryOp>(op));\n}\n\ntemplate <typename Iterator, typename T, typename Comparator>\nIterator BinarySearchByKey(Iterator begin, Iterator end, const T& value,\n                           Comparator cmp) {\n  Iterator left = begin;\n  Iterator right = end;\n\n  while (left < right) {\n    Iterator mid = left + (std::distance(left, right) / 2);\n    if (cmp(*mid, value)) {\n      left = mid + 1;\n    } else if (cmp(value, *mid)) {\n      right = mid;\n    } else {\n      return mid;\n    }\n  }\n  return end;\n}\n\ntemplate <typename Iterator, typename T>\nstd::optional<size_t> FindIndex(Iterator begin, Iterator end, const T& value) {\n  auto it = std::find(begin, end, value);\n  if (it == end) return std::nullopt;\n  return std::distance(begin, it);\n}\n\ntemplate <typename Container, typename T>\nstd::optional<size_t> FindIndex(const Container& container, const T& value) {\n  return FindIndex(std::begin(container), std::end(container), value);\n}\n\ntemplate <typename Iterator, typename UnaryOp>\nstd::optional<size_t> FindIndexIf(Iterator begin, Iterator end, UnaryOp&& op) {\n  auto it = std::find_if(begin, end, std::forward<UnaryOp>(op));\n  if (it == end) return std::nullopt;\n  return std::distance(begin, it);\n}\n\ntemplate <typename Container, typename UnaryOp>\nstd::optional<size_t> FindIndexIf(const Container& container, UnaryOp&& op) {\n  return FindIndexIf(std::begin(container), std::end(container),\n                     std::forward<UnaryOp>(op));\n}\n\nnamespace internal {\n\ntemplate <typename Iterator, typename T>\nstd::vector<size_t> DoFindIndices(Iterator begin, Iterator end, const T& value,\n                                  const std::random_access_iterator_tag&) {\n  std::vector<size_t> matches;\n  auto it = std::find(begin, end, value);\n  while (it != end) {\n    size_t index = std::distance(begin, it);\n    matches.push_back(index);\n    it = std::find(it + 1, end, value);\n  }\n  return matches;\n}\n\ntemplate <typename Iterator, typename T>\nstd::vector<size_t> DoFindIndices(Iterator begin, Iterator end, const T& value,\n                                  const std::input_iterator_tag&) {\n  std::vector<size_t> matches;\n  auto it = std::find(begin, end, value);\n  auto last_it = begin;\n  size_t last_index = 0;\n  while (it != end) {\n    size_t index = last_index + std::distance(last_it, it);\n    matches.push_back(index);\n    last_it = it;\n    last_index = index;\n    it = std::find(last_it + 1, end, value);\n  }\n  return matches;\n}\n\ntemplate <typename Iterator, typename UnaryOp>\nstd::vector<size_t> DoFindIndicesIf(Iterator begin, Iterator end, UnaryOp&& op,\n                                    const std::random_access_iterator_tag&) {\n  std::vector<size_t> matches;\n  auto it = std::find_if(begin, end, std::forward<UnaryOp>(op));\n  while (it != end) {\n    size_t index = std::distance(begin, it);\n    matches.push_back(index);\n    it = std::find_if(it + 1, end, std::forward<UnaryOp>(op));\n  }\n  return matches;\n}\n\ntemplate <typename Iterator, typename UnaryOp>\nstd::vector<size_t> DoFindIndicesIf(Iterator begin, Iterator end, UnaryOp&& op,\n                                    const std::input_iterator_tag&) {\n  std::vector<size_t> matches;\n  auto it = std::find_if(begin, end, std::forward<UnaryOp>(op));\n  auto last_it = begin;\n  size_t last_index = 0;\n  while (it != end) {\n    size_t index = last_index + std::distance(last_it, it);\n    matches.push_back(index);\n    last_it = it;\n    last_index = index;\n    it = std::find_if(last_it + 1, end, std::forward<UnaryOp>(op));\n  }\n  return matches;\n}\n\n}  // namespace internal\n\ntemplate <typename Iterator, typename T>\nstd::vector<size_t> FindIndices(Iterator begin, Iterator end, const T& value) {\n  using iterator_category =\n      typename std::iterator_traits<Iterator>::iterator_category;\n  return internal::DoFindIndices(begin, end, value, iterator_category());\n}\n\ntemplate <typename Container, typename T>\nstd::vector<size_t> FindIndices(const Container& container, const T& value) {\n  return FindIndices(std::begin(container), std::end(container), value);\n}\n\ntemplate <typename Iterator, typename UnaryOp>\nstd::vector<size_t> FindIndicesIf(Iterator begin, Iterator end, UnaryOp&& op) {\n  using iterator_category =\n      typename std::iterator_traits<Iterator>::iterator_category;\n  return internal::DoFindIndicesIf(begin, end, std::forward<UnaryOp>(op),\n                                   iterator_category());\n}\n\ntemplate <typename Container, typename UnaryOp>\nstd::vector<size_t> FindIndicesIf(const Container& container, UnaryOp&& op) {\n  return FindIndicesIf(std::begin(container), std::end(container),\n                       std::forward<UnaryOp>(op));\n}\n\ntemplate <typename Container>\nvoid Shuffle(Container& container) {\n  absl::c_shuffle(container, GetAbslBitGen());\n}\n\ntemplate <typename T, size_t N>\nconst std::vector<T> ArrayToVector(const T (&arr)[N]) {\n  return std::vector<T>(std::begin(arr), std::end(arr));\n}\n\ntemplate <typename T, size_t N, size_t M>\nstd::vector<std::vector<T>> Array2DToVector2D(const T (&arr)[N][M]) {\n  std::vector<std::vector<T>> vec;\n  vec.reserve(N);\n  for (const auto& inner_array : arr) {\n    vec.emplace_back(std::begin(inner_array), std::end(inner_array));\n  }\n  return vec;\n}\n\ntemplate <size_t N, typename Generator,\n          typename FunctorTraits = internal::MakeFunctorTraits<Generator>,\n          typename ReturnType = typename FunctorTraits::ReturnType,\n          typename RunType = typename FunctorTraits::RunType,\n          typename ArgList = internal::ExtractArgs<RunType>,\n          size_t ArgNum = internal::GetSize<ArgList>,\n          std::enable_if_t<ArgNum == 0>* = nullptr>\nstd::array<ReturnType, N> CreateArray(Generator&& generator) {\n  std::array<ReturnType, N> ret;\n  for (size_t i = 0; i < N; ++i) {\n    ret[i] = generator();\n  }\n  return ret;\n}\n\ntemplate <size_t N, typename Generator,\n          typename FunctorTraits = internal::MakeFunctorTraits<Generator>,\n          typename ReturnType = typename FunctorTraits::ReturnType,\n          typename RunType = typename FunctorTraits::RunType,\n          typename ArgList = internal::ExtractArgs<RunType>,\n          size_t ArgNum = internal::GetSize<ArgList>,\n          std::enable_if_t<ArgNum == 1>* = nullptr>\nstd::array<ReturnType, N> CreateArray(Generator&& generator) {\n  std::array<ReturnType, N> ret;\n  for (size_t i = 0; i < N; ++i) {\n    ret[i] = generator(i);\n  }\n  return ret;\n}\n\ntemplate <typename Container, typename Container2>\nvoid Extend(Container& container, Container2&& container2) {\n  container.insert(container.end(), std::make_move_iterator(container2.begin()),\n                   std::make_move_iterator(container2.end()));\n  container2.clear();\n}\n\ntemplate <typename Container, typename Container2>\nvoid Extend(Container& container, const Container2& container2) {\n  container.insert(container.end(), container2.begin(), container2.end());\n}\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_CONTAINERS_CONTAINER_UTIL_H_\n"
  },
  {
    "path": "tachyon/base/containers/container_util_unittest.cc",
    "content": "#include \"tachyon/base/containers/container_util.h\"\n\n#include <memory>\n#include <vector>\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n\nnamespace tachyon::base {\n\nconstexpr size_t kCount = 1000;\n\nTEST(ContainerUtilTest, CreateRangedVector) {\n  struct {\n    int start;\n    int end;\n    int step;\n    std::vector<int> answers;\n  } tests[] = {\n      {0, 9, 3, {0, 3, 6}},\n      {1, 9, 3, {1, 4, 7}},\n  };\n\n  for (const auto& test : tests) {\n    auto ranges = CreateRangedVector(test.start, test.end, test.step);\n    EXPECT_THAT(ranges, testing::ContainerEq(test.answers));\n  }\n}\n\nTEST(ContainerUtilTest, CreateVectorWithGenerator) {\n  EXPECT_THAT(CreateVector(5, ([]() { return 3; })),\n              testing::ContainerEq(std::vector<int>{3, 3, 3, 3, 3}));\n  EXPECT_THAT(CreateVector(5, ([](int idx) { return idx + 1; })),\n              testing::ContainerEq(std::vector<int>{1, 2, 3, 4, 5}));\n}\n\nTEST(ContainerUtilTest, Map) {\n  std::vector<int> arr({1, 2, 3});\n  EXPECT_THAT(Map(arr.begin(), arr.end(),\n                  [](int v) { return static_cast<double>(v * 2); }),\n              testing::ContainerEq(std::vector<double>{2.0, 4.0, 6.0}));\n  EXPECT_THAT(Map(arr, [](int v) { return static_cast<double>(v * 2); }),\n              testing::ContainerEq(std::vector<double>{2.0, 4.0, 6.0}));\n}\n\nTEST(ContainerUtilTest, MapWithIdx) {\n  std::vector<int> arr({1, 2, 3});\n  EXPECT_THAT(\n      Map(arr.begin(), arr.end(),\n          [](size_t idx, int v) { return static_cast<double>(v * 2 + idx); }),\n      testing::ContainerEq(std::vector<double>{2.0, 5.0, 8.0}));\n  EXPECT_THAT(Map(arr, [](size_t idx,\n                          int v) { return static_cast<double>(v * 2 + idx); }),\n              testing::ContainerEq(std::vector<double>{2.0, 5.0, 8.0}));\n}\n\nTEST(ContainerUtilTest, FlatMap) {\n  std::vector<int> arr({1, 2});\n  arr = base::FlatMap(arr, [](int value) {\n    return std::vector<int>({\n        value,\n        value + 1,\n        value + 2,\n    });\n  });\n  EXPECT_THAT(arr, testing::ContainerEq(std::vector<int>{1, 2, 3, 2, 3, 4}));\n}\n\nTEST(ContainerUtilTest, FindIndex) {\n  std::vector<int> arr({1, 2});\n  std::optional<size_t> index = base::FindIndex(arr, 1);\n  EXPECT_EQ(index.value(), 0);\n  index = base::FindIndex(arr, 3);\n  EXPECT_FALSE(index.has_value());\n}\n\nTEST(ContainerUtilTest, FindIndexIf) {\n  std::vector<int> arr({1, 2});\n  std::optional<size_t> index =\n      base::FindIndexIf(arr, [](size_t v) { return v == 1; });\n  EXPECT_EQ(index.value(), 0);\n  index = base::FindIndexIf(arr, [](size_t v) { return v == 3; });\n  EXPECT_FALSE(index.has_value());\n}\n\nTEST(ContainerUtilTest, DoFindIndices) {\n  std::vector<int> arr({1, 2, 3, 1, 2, 3});\n  std::vector<std::unique_ptr<std::input_iterator_tag>> tags;\n  tags.push_back(std::make_unique<std::forward_iterator_tag>());\n  tags.push_back(std::make_unique<std::random_access_iterator_tag>());\n  for (const std::unique_ptr<std::input_iterator_tag>& tag : tags) {\n    std::vector<size_t> indices =\n        base::internal::DoFindIndices(arr.begin(), arr.end(), 1, *tag);\n    EXPECT_EQ(indices, std::vector<size_t>({0, 3}));\n    indices = base::internal::DoFindIndices(arr.begin(), arr.end(), 4, *tag);\n    EXPECT_TRUE(indices.empty());\n  }\n}\n\nTEST(ContainerUtilTest, FindIndices) {\n  std::vector<int> arr({1, 2, 3, 1, 2, 3});\n  std::vector<size_t> indices = base::FindIndices(arr, 1);\n  EXPECT_EQ(indices, std::vector<size_t>({0, 3}));\n  indices = base::FindIndices(arr, 4);\n  EXPECT_TRUE(indices.empty());\n}\n\nTEST(ContainerUtilTest, DoFindIndicesIf) {\n  std::vector<int> arr({1, 2, 3, 1, 2, 3});\n  std::vector<std::unique_ptr<std::input_iterator_tag>> tags;\n  tags.push_back(std::make_unique<std::forward_iterator_tag>());\n  tags.push_back(std::make_unique<std::random_access_iterator_tag>());\n  for (const std::unique_ptr<std::input_iterator_tag>& tag : tags) {\n    std::vector<size_t> indices = base::internal::DoFindIndicesIf(\n        arr.begin(), arr.end(), [](size_t v) { return v == 1; }, *tag);\n    EXPECT_EQ(indices, std::vector<size_t>({0, 3}));\n    indices = base::internal::DoFindIndicesIf(\n        arr.begin(), arr.end(), [](size_t v) { return v == 4; }, *tag);\n    EXPECT_TRUE(indices.empty());\n  }\n}\n\nTEST(ContainerUtilTest, FindIndicesIf) {\n  std::vector<int> arr({1, 2, 3, 1, 2, 3});\n  std::vector<size_t> indices =\n      base::FindIndicesIf(arr, [](size_t v) { return v == 1; });\n  EXPECT_EQ(indices, std::vector<size_t>({0, 3}));\n  indices = base::FindIndicesIf(arr, [](size_t v) { return v == 4; });\n  EXPECT_TRUE(indices.empty());\n}\n\nTEST(ContainerUtilTest, Shuffle) {\n  std::vector<int> vec = {1, 2, 3};\n  std::vector<int> vec2 = vec;\n  for (size_t i = 0; i < kCount; ++i) {\n    Shuffle(vec2);\n    if (vec != vec2) {\n      SUCCEED();\n      return;\n    }\n  }\n  FAIL() << \"shuffle seems not working\";\n}\n\nTEST(ContainerUtilTest, BinarySearchByKey) {\n  struct TestData {\n    std::vector<int> data;\n    int value;\n    std::optional<size_t> expected_index;\n  };\n\n  std::vector<TestData> tests = {\n      {{1, 2, 3, 4, 5}, 3, 2}, {{1, 2, 3, 4, 5}, 1, 0},\n      {{1, 2, 3, 4, 5}, 5, 4}, {{1, 2, 3, 4, 5}, 6, std::nullopt},\n      {{}, 3, std::nullopt},\n  };\n\n  for (const auto& test : tests) {\n    auto it =\n        BinarySearchByKey(test.data.begin(), test.data.end(), test.value,\n                          [](int elem, int value) { return elem < value; });\n\n    if (test.expected_index.has_value()) {\n      EXPECT_EQ(std::distance(test.data.begin(), it),\n                test.expected_index.value());\n    } else {\n      EXPECT_EQ(it, test.data.end());\n    }\n  }\n}\n\nTEST(ContainerUtilTest, CreateArrayWithGenerator) {\n  EXPECT_THAT(CreateArray<5>(([]() { return 3; })),\n              testing::ContainerEq(std::array<int, 5>{3, 3, 3, 3, 3}));\n  EXPECT_THAT(CreateArray<5>(([](int idx) { return idx + 1; })),\n              testing::ContainerEq(std::array<int, 5>{1, 2, 3, 4, 5}));\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/containers/contains.h",
    "content": "// Copyright 2020 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_CONTAINERS_CONTAINS_H_\n#define TACHYON_BASE_CONTAINERS_CONTAINS_H_\n\n#include <type_traits>\n#include <utility>\n\n#include \"absl/meta/type_traits.h\"\n\n#include \"tachyon/base/ranges/algorithm.h\"\n#include \"tachyon/base/ranges/ranges.h\"\n\nnamespace tachyon::base {\nnamespace internal {\n\n// Small helper to detect whether a given type has a nested `key_type` typedef.\n// Used below to catch misuses of the API for associative containers.\ntemplate <typename T, typename SFINAE = void>\nstruct HasKeyType : std::false_type {};\n\ntemplate <typename T>\nstruct HasKeyType<T, absl::void_t<typename T::key_type>> : std::true_type {};\n\n// Probe whether a `contains` member function exists and return the result of\n// `container.contains(value)` if this is a valid expression. This is the\n// highest priority option.\ntemplate <typename Container, typename Value>\nconstexpr auto ContainsImpl(const Container& container, const Value& value,\n                            priority_tag<2>)\n    -> decltype(container.contains(value)) {\n  return container.contains(value);\n}\n\n// Probe whether a `find` member function exists and whether its return value\n// can be compared with `container.end()`. Intended for STL style maps and sets\n// that lack a `contains` member function.\ntemplate <typename Container, typename Value>\nconstexpr auto ContainsImpl(const Container& container, const Value& value,\n                            priority_tag<1>)\n    -> decltype(container.find(value) != container.end()) {\n  return container.find(value) != container.end();\n}\n\n// Probe whether a `find` member function exists and whether its return value\n// can be compared with `Container::npos`. Intended for STL style strings that\n// lack a `contains` member function.\ntemplate <typename Container, typename Value>\nconstexpr auto ContainsImpl(const Container& container, const Value& value,\n                            priority_tag<1>)\n    -> decltype(container.find(value) != Container::npos) {\n  return container.find(value) != Container::npos;\n}\n\n// Generic fallback option, using a linear search over `container` to find\n// `value`. Has the lowest priority. This will not compile for associative\n// containers, as this likely is a performance bug.\ntemplate <typename Container, typename Value>\nconstexpr bool ContainsImpl(const Container& container, const Value& value,\n                            priority_tag<0>) {\n  static_assert(\n      !HasKeyType<Container>::value,\n      \"Error: About to perform linear search on an associative container. \"\n      \"Either use a more generic comparator (e.g. std::less<>) or, if a linear \"\n      \"search is desired, provide an explicit projection parameter.\");\n  return ranges::find(container, value) != ranges::end(container);\n}\n\n}  // namespace internal\n\n// A general purpose utility to check whether `container` contains `value`. This\n// will probe whether a `contains` or `find` member function on `container`\n// exists, and fall back to a generic linear search over `container`.\ntemplate <typename Container, typename Value>\nconstexpr bool Contains(const Container& container, const Value& value) {\n  return internal::ContainsImpl(container, value, internal::priority_tag<2>());\n}\n\n// Overload that allows to provide an additional projection invocable. This\n// projection will be applied to every element in `container` before comparing\n// it with `value`. This will always perform a linear search.\ntemplate <typename Container, typename Value, typename Proj>\nconstexpr bool Contains(const Container& container, const Value& value,\n                        Proj proj) {\n  return ranges::find(container, value, std::move(proj)) !=\n         ranges::end(container);\n}\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_CONTAINERS_CONTAINS_H_\n"
  },
  {
    "path": "tachyon/base/containers/contains_unittest.cc",
    "content": "// Copyright 2020 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/containers/contains.h\"\n\n#include <set>\n#include <string>\n\n#include \"absl/container/flat_hash_set.h\"\n#include \"absl/strings/ascii.h\"\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/functional/identity.h\"\n\nnamespace tachyon::base {\n\nTEST(ContainsTest, GenericContains) {\n  constexpr char allowed_chars[] = {'a', 'b', 'c', 'd'};\n\n  static_assert(Contains(allowed_chars, 'a'), \"\");\n  static_assert(!Contains(allowed_chars, 'z'), \"\");\n  static_assert(!Contains(allowed_chars, 0), \"\");\n\n  constexpr char allowed_chars_including_nul[] = \"abcd\";\n  static_assert(Contains(allowed_chars_including_nul, 0), \"\");\n}\n\nTEST(ContainsTest, GenericContainsWithProjection) {\n  const char allowed_chars[] = {'A', 'B', 'C', 'D'};\n\n  EXPECT_TRUE(Contains(allowed_chars, 'a', &absl::ascii_tolower));\n  EXPECT_FALSE(Contains(allowed_chars, 'z', &absl::ascii_tolower));\n  EXPECT_FALSE(Contains(allowed_chars, 0, &absl::ascii_tolower));\n}\n\nTEST(ContainsTest, GenericSetContainsWithProjection) {\n  constexpr std::string_view kFoo = \"foo\";\n  std::set<std::string> set = {\"foo\", \"bar\", \"baz\"};\n\n  // Opt into a linear search by explicitly providing a projection:\n  EXPECT_TRUE(Contains(set, kFoo, identity{}));\n}\n\nTEST(ContainsTest, ContainsWithFindAndNpos) {\n  std::string str = \"abcd\";\n\n  EXPECT_TRUE(Contains(str, 'a'));\n  EXPECT_FALSE(Contains(str, 'z'));\n  EXPECT_FALSE(Contains(str, 0));\n}\n\nTEST(ContainsTest, ContainsWithFindAndEnd) {\n  std::set<int> set = {1, 2, 3, 4};\n\n  EXPECT_TRUE(Contains(set, 1));\n  EXPECT_FALSE(Contains(set, 5));\n  EXPECT_FALSE(Contains(set, 0));\n}\n\nTEST(ContainsTest, ContainsWithContains) {\n  absl::flat_hash_set<int> set = {1, 2, 3, 4};\n\n  EXPECT_TRUE(Contains(set, 1));\n  EXPECT_FALSE(Contains(set, 5));\n  EXPECT_FALSE(Contains(set, 0));\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/containers/cxx20_erase.h",
    "content": "// Copyright 2021 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_CONTAINERS_CXX20_ERASE_H_\n#define TACHYON_BASE_CONTAINERS_CXX20_ERASE_H_\n\n#include \"tachyon/base/containers/cxx20_erase_deque.h\"\n#include \"tachyon/base/containers/cxx20_erase_forward_list.h\"\n#include \"tachyon/base/containers/cxx20_erase_list.h\"\n#include \"tachyon/base/containers/cxx20_erase_map.h\"\n#include \"tachyon/base/containers/cxx20_erase_set.h\"\n#include \"tachyon/base/containers/cxx20_erase_string.h\"\n#include \"tachyon/base/containers/cxx20_erase_unordered_map.h\"\n#include \"tachyon/base/containers/cxx20_erase_unordered_set.h\"\n#include \"tachyon/base/containers/cxx20_erase_vector.h\"\n\n// Erase/EraseIf are based on C++20's uniform container erasure API:\n// - https://eel.is/c++draft/libraryindex#:erase\n// - https://eel.is/c++draft/libraryindex#:erase_if\n// They provide a generic way to erase elements from a container.\n// The functions here implement these for the standard containers until those\n// functions are available in the C++ standard.\n// Note: there is no std::erase for standard associative containers so we don't\n// have it either.\n\n// This header is provided for convenience, so callers to Erase/EraseIf can just\n// include this in their .cc file without thinking about which Erase/EraseIf\n// specialization header to include. For uncommon cases where Erase/EraseIf are\n// used in .h files, please include the specialization header to avoid bloating\n// the header.\n\n#endif  // TACHYON_BASE_CONTAINERS_CXX20_ERASE_H_\n"
  },
  {
    "path": "tachyon/base/containers/cxx20_erase_deque.h",
    "content": "// Copyright 2021 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_CONTAINERS_CXX20_ERASE_DEQUE_H_\n#define TACHYON_BASE_CONTAINERS_CXX20_ERASE_DEQUE_H_\n\n#include <algorithm>\n#include <deque>\n#include <iterator>\n\nnamespace tachyon::base {\n\n// Erase/EraseIf are based on C++20's uniform container erasure API:\n// - https://eel.is/c++draft/libraryindex#:erase\n// - https://eel.is/c++draft/libraryindex#:erase_if\n// They provide a generic way to erase elements from a container.\n// The functions here implement these for the standard containers until those\n// functions are available in the C++ standard.\n// Note: there is no std::erase for standard associative containers so we don't\n// have it either.\n\ntemplate <class T, class Allocator, class Value>\nsize_t Erase(std::deque<T, Allocator>& container, const Value& value) {\n  auto it = std::remove(container.begin(), container.end(), value);\n  size_t removed = std::distance(it, container.end());\n  container.erase(it, container.end());\n  return removed;\n}\n\ntemplate <class T, class Allocator, class Predicate>\nsize_t EraseIf(std::deque<T, Allocator>& container, Predicate pred) {\n  auto it = std::remove_if(container.begin(), container.end(), pred);\n  size_t removed = std::distance(it, container.end());\n  container.erase(it, container.end());\n  return removed;\n}\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_CONTAINERS_CXX20_ERASE_DEQUE_H_\n"
  },
  {
    "path": "tachyon/base/containers/cxx20_erase_forward_list.h",
    "content": "// Copyright 2021 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_CONTAINERS_CXX20_ERASE_FORWARD_LIST_H_\n#define TACHYON_BASE_CONTAINERS_CXX20_ERASE_FORWARD_LIST_H_\n\n#include <forward_list>\n#include <iterator>\n\nnamespace tachyon::base {\n\n// Erase/EraseIf are based on C++20's uniform container erasure API:\n// - https://eel.is/c++draft/libraryindex#:erase\n// - https://eel.is/c++draft/libraryindex#:erase_if\n// They provide a generic way to erase elements from a container.\n// The functions here implement these for the standard containers until those\n// functions are available in the C++ standard.\n// Note: there is no std::erase for standard associative containers so we don't\n// have it either.\n\ntemplate <class T, class Allocator, class Predicate>\nsize_t EraseIf(std::forward_list<T, Allocator>& container, Predicate pred) {\n  // Note: std::forward_list does not have a size() API, thus we need to use the\n  // O(n) std::distance work-around. However, given that EraseIf is O(n)\n  // already, this should not make a big difference.\n  size_t old_size = std::distance(container.begin(), container.end());\n  container.remove_if(pred);\n  return old_size - std::distance(container.begin(), container.end());\n}\n\ntemplate <class T, class Allocator, class Value>\nsize_t Erase(std::forward_list<T, Allocator>& container, const Value& value) {\n  // Unlike std::forward_list::remove, this function template accepts\n  // heterogeneous types and does not force a conversion to the container's\n  // value type before invoking the == operator.\n  return EraseIf(container, [&](const T& cur) { return cur == value; });\n}\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_CONTAINERS_CXX20_ERASE_FORWARD_LIST_H_\n"
  },
  {
    "path": "tachyon/base/containers/cxx20_erase_internal.h",
    "content": "// Copyright 2021 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_CONTAINERS_CXX20_ERASE_INTERNAL_H_\n#define TACHYON_BASE_CONTAINERS_CXX20_ERASE_INTERNAL_H_\n\n#include <stddef.h>\n\n// Internal portion of base/containers/cxx20_erase_*.h. Please include those\n// headers instead of including this directly.\n\nnamespace tachyon::base {\n\nnamespace internal {\n\n// Calls erase on iterators of matching elements and returns the number of\n// removed elements.\ntemplate <typename Container, typename Predicate>\nsize_t IterateAndEraseIf(Container& container, Predicate pred) {\n  size_t old_size = container.size();\n  for (auto it = container.begin(), last = container.end(); it != last;) {\n    if (pred(*it))\n      it = container.erase(it);\n    else\n      ++it;\n  }\n  return old_size - container.size();\n}\n\n}  // namespace internal\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_CONTAINERS_CXX20_ERASE_INTERNAL_H_\n"
  },
  {
    "path": "tachyon/base/containers/cxx20_erase_list.h",
    "content": "// Copyright 2021 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_CONTAINERS_CXX20_ERASE_LIST_H_\n#define TACHYON_BASE_CONTAINERS_CXX20_ERASE_LIST_H_\n\n#include <stddef.h>\n\n#include <list>\n\nnamespace tachyon::base {\n\n// Erase/EraseIf are based on C++20's uniform container erasure API:\n// - https://eel.is/c++draft/libraryindex#:erase\n// - https://eel.is/c++draft/libraryindex#:erase_if\n// They provide a generic way to erase elements from a container.\n// The functions here implement these for the standard containers until those\n// functions are available in the C++ standard.\n// Note: there is no std::erase for standard associative containers so we don't\n// have it either.\n\ntemplate <class T, class Allocator, class Predicate>\nsize_t EraseIf(std::list<T, Allocator>& container, Predicate pred) {\n  size_t old_size = container.size();\n  container.remove_if(pred);\n  return old_size - container.size();\n}\n\ntemplate <class T, class Allocator, class Value>\nsize_t Erase(std::list<T, Allocator>& container, const Value& value) {\n  // Unlike std::list::remove, this function template accepts heterogeneous\n  // types and does not force a conversion to the container's value type before\n  // invoking the == operator.\n  return EraseIf(container, [&](const T& cur) { return cur == value; });\n}\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_CONTAINERS_CXX20_ERASE_LIST_H_\n"
  },
  {
    "path": "tachyon/base/containers/cxx20_erase_map.h",
    "content": "// Copyright 2021 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_CONTAINERS_CXX20_ERASE_MAP_H_\n#define TACHYON_BASE_CONTAINERS_CXX20_ERASE_MAP_H_\n\n#include <map>\n\n#include \"tachyon/base/containers/cxx20_erase_internal.h\"\n\nnamespace tachyon::base {\n\n// EraseIf is based on C++20's uniform container erasure API:\n// - https://eel.is/c++draft/libraryindex#:erase\n// - https://eel.is/c++draft/libraryindex#:erase_if\n// They provide a generic way to erase elements from a container.\n// The functions here implement these for the standard containers until those\n// functions are available in the C++ standard.\n// Note: there is no std::erase for standard associative containers so we don't\n// have it either.\n\ntemplate <class Key, class T, class Compare, class Allocator, class Predicate>\nsize_t EraseIf(std::map<Key, T, Compare, Allocator>& container,\n               Predicate pred) {\n  return internal::IterateAndEraseIf(container, pred);\n}\n\ntemplate <class Key, class T, class Compare, class Allocator, class Predicate>\nsize_t EraseIf(std::multimap<Key, T, Compare, Allocator>& container,\n               Predicate pred) {\n  return internal::IterateAndEraseIf(container, pred);\n}\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_CONTAINERS_CXX20_ERASE_MAP_H_\n"
  },
  {
    "path": "tachyon/base/containers/cxx20_erase_set.h",
    "content": "// Copyright 2021 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_CONTAINERS_CXX20_ERASE_SET_H_\n#define TACHYON_BASE_CONTAINERS_CXX20_ERASE_SET_H_\n\n#include <set>\n\n#include \"tachyon/base/containers/cxx20_erase_internal.h\"\n\nnamespace tachyon::base {\n\n// EraseIf is based on C++20's uniform container erasure API:\n// - https://eel.is/c++draft/libraryindex#:erase\n// - https://eel.is/c++draft/libraryindex#:erase_if\n// They provide a generic way to erase elements from a container.\n// The functions here implement these for the standard containers until those\n// functions are available in the C++ standard.\n// Note: there is no std::erase for standard associative containers so we don't\n// have it either.\n\ntemplate <class Key, class Compare, class Allocator, class Predicate>\nsize_t EraseIf(std::set<Key, Compare, Allocator>& container, Predicate pred) {\n  return internal::IterateAndEraseIf(container, pred);\n}\n\ntemplate <class Key, class Compare, class Allocator, class Predicate>\nsize_t EraseIf(std::multiset<Key, Compare, Allocator>& container,\n               Predicate pred) {\n  return internal::IterateAndEraseIf(container, pred);\n}\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_CONTAINERS_CXX20_ERASE_SET_H_\n"
  },
  {
    "path": "tachyon/base/containers/cxx20_erase_string.h",
    "content": "// Copyright 2021 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_CONTAINERS_CXX20_ERASE_STRING_H_\n#define TACHYON_BASE_CONTAINERS_CXX20_ERASE_STRING_H_\n\n#include <algorithm>\n#include <iterator>\n#include <string>\n\nnamespace tachyon::base {\n\n// Erase/EraseIf are based on C++20's uniform container erasure API:\n// - https://eel.is/c++draft/libraryindex#:erase\n// - https://eel.is/c++draft/libraryindex#:erase_if\n// They provide a generic way to erase elements from a container.\n// The functions here implement these for the standard containers until those\n// functions are available in the C++ standard.\n// Note: there is no std::erase for standard associative containers so we don't\n// have it either.\n\ntemplate <typename CharT, typename Traits, typename Allocator, typename Value>\nsize_t Erase(std::basic_string<CharT, Traits, Allocator>& container,\n             const Value& value) {\n  auto it = std::remove(container.begin(), container.end(), value);\n  size_t removed = std::distance(it, container.end());\n  container.erase(it, container.end());\n  return removed;\n}\n\ntemplate <typename CharT, typename Traits, typename Allocator, class Predicate>\nsize_t EraseIf(std::basic_string<CharT, Traits, Allocator>& container,\n               Predicate pred) {\n  auto it = std::remove_if(container.begin(), container.end(), pred);\n  size_t removed = std::distance(it, container.end());\n  container.erase(it, container.end());\n  return removed;\n}\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_CONTAINERS_CXX20_ERASE_STRING_H_\n"
  },
  {
    "path": "tachyon/base/containers/cxx20_erase_unordered_map.h",
    "content": "// Copyright 2021 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_CONTAINERS_CXX20_ERASE_UNORDERED_MAP_H_\n#define TACHYON_BASE_CONTAINERS_CXX20_ERASE_UNORDERED_MAP_H_\n\n#include <unordered_map>\n\n#include \"tachyon/base/containers/cxx20_erase_internal.h\"\n\nnamespace tachyon::base {\n\n// EraseIf is based on C++20's uniform container erasure API:\n// - https://eel.is/c++draft/libraryindex#:erase\n// - https://eel.is/c++draft/libraryindex#:erase_if\n// They provide a generic way to erase elements from a container.\n// The functions here implement these for the standard containers until those\n// functions are available in the C++ standard.\n// Note: there is no std::erase for standard associative containers so we don't\n// have it either.\n\ntemplate <class Key, class T, class Hash, class KeyEqual, class Allocator,\n          class Predicate>\nsize_t EraseIf(std::unordered_map<Key, T, Hash, KeyEqual, Allocator>& container,\n               Predicate pred) {\n  return internal::IterateAndEraseIf(container, pred);\n}\n\ntemplate <class Key, class T, class Hash, class KeyEqual, class Allocator,\n          class Predicate>\nsize_t EraseIf(\n    std::unordered_multimap<Key, T, Hash, KeyEqual, Allocator>& container,\n    Predicate pred) {\n  return internal::IterateAndEraseIf(container, pred);\n}\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_CONTAINERS_CXX20_ERASE_UNORDERED_MAP_H_\n"
  },
  {
    "path": "tachyon/base/containers/cxx20_erase_unordered_set.h",
    "content": "// Copyright 2021 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_CONTAINERS_CXX20_ERASE_UNORDERED_SET_H_\n#define TACHYON_BASE_CONTAINERS_CXX20_ERASE_UNORDERED_SET_H_\n\n#include <unordered_set>\n\n#include \"tachyon/base/containers/cxx20_erase_internal.h\"\n\nnamespace tachyon::base {\n\n// EraseIf is based on C++20's uniform container erasure API:\n// - https://eel.is/c++draft/libraryindex#:erase\n// - https://eel.is/c++draft/libraryindex#:erase_if\n// They provide a generic way to erase elements from a container.\n// The functions here implement these for the standard containers until those\n// functions are available in the C++ standard.\n// Note: there is no std::erase for standard associative containers so we don't\n// have it either.\n\ntemplate <class Key, class Hash, class KeyEqual, class Allocator,\n          class Predicate>\nsize_t EraseIf(std::unordered_set<Key, Hash, KeyEqual, Allocator>& container,\n               Predicate pred) {\n  return internal::IterateAndEraseIf(container, pred);\n}\n\ntemplate <class Key, class Hash, class KeyEqual, class Allocator,\n          class Predicate>\nsize_t EraseIf(\n    std::unordered_multiset<Key, Hash, KeyEqual, Allocator>& container,\n    Predicate pred) {\n  return internal::IterateAndEraseIf(container, pred);\n}\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_CONTAINERS_CXX20_ERASE_UNORDERED_SET_H_\n"
  },
  {
    "path": "tachyon/base/containers/cxx20_erase_vector.h",
    "content": "// Copyright 2021 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_CONTAINERS_CXX20_ERASE_VECTOR_H_\n#define TACHYON_BASE_CONTAINERS_CXX20_ERASE_VECTOR_H_\n\n#include <algorithm>\n#include <iterator>\n#include <vector>\n\nnamespace tachyon::base {\n\n// Erase/EraseIf are based on C++20's uniform container erasure API:\n// - https://eel.is/c++draft/libraryindex#:erase\n// - https://eel.is/c++draft/libraryindex#:erase_if\n// They provide a generic way to erase elements from a container.\n// The functions here implement these for the standard containers until those\n// functions are available in the C++ standard.\n// Note: there is no std::erase for standard associative containers so we don't\n// have it either.\n\ntemplate <class T, class Allocator, class Value>\nsize_t Erase(std::vector<T, Allocator>& container, const Value& value) {\n  auto it = std::remove(container.begin(), container.end(), value);\n  size_t removed = static_cast<size_t>(std::distance(it, container.end()));\n  container.erase(it, container.end());\n  return removed;\n}\n\ntemplate <class T, class Allocator, class Predicate>\nsize_t EraseIf(std::vector<T, Allocator>& container, Predicate pred) {\n  auto it = std::remove_if(container.begin(), container.end(), pred);\n  size_t removed = static_cast<size_t>(std::distance(it, container.end()));\n  container.erase(it, container.end());\n  return removed;\n}\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_CONTAINERS_CXX20_ERASE_VECTOR_H_\n"
  },
  {
    "path": "tachyon/base/containers/stack.h",
    "content": "// Copyright 2017 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_CONTAINERS_STACK_H_\n#define TACHYON_BASE_CONTAINERS_STACK_H_\n\n#include <stack>\n\n#include \"tachyon/base/containers/circular_deque.h\"\n\nnamespace tachyon::base {\n\n// Provides a definition of base::stack that's like std::stack but uses a\n// base::circular_deque instead of std::deque. Since std::stack is just a\n// wrapper for an underlying type, we can just provide a typedef for it that\n// defaults to the base circular_deque.\ntemplate <class T, class Container = circular_deque<T>>\nusing stack = std::stack<T, Container>;\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_CONTAINERS_STACK_H_\n"
  },
  {
    "path": "tachyon/base/containers/util.h",
    "content": "// Copyright 2018 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n// clang-format off\n\n#ifndef TACHYON_BASE_CONTAINERS_UTIL_H_\n#define TACHYON_BASE_CONTAINERS_UTIL_H_\n\n#include <stdint.h>\n\nnamespace tachyon::base {\n\n// TODO(crbug.com/817982): What we really need is for checked_math.h to be\n// able to do checked arithmetic on pointers.\ntemplate <typename T>\ninline uintptr_t get_uintptr(const T* t) {\n  return reinterpret_cast<uintptr_t>(t);\n}\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_CONTAINERS_UTIL_H_\n\n// clang-format on\n"
  },
  {
    "path": "tachyon/base/containers/vector_buffer.h",
    "content": "// Copyright 2017 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n// clang-format off\n\n#ifndef TACHYON_BASE_CONTAINERS_VECTOR_BUFFER_H_\n#define TACHYON_BASE_CONTAINERS_VECTOR_BUFFER_H_\n\n#include <stdlib.h>\n#include <string.h>\n\n#include <type_traits>\n#include <utility>\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/containers/util.h\"\n// #include \"tachyon/base/memory/raw_ptr_exclusion.h\"\n#include \"tachyon/base/numerics/checked_math.h\"\n\nnamespace tachyon::base {\nnamespace internal {\n\n// Internal implementation detail of base/containers.\n//\n// Implements a vector-like buffer that holds a certain capacity of T. Unlike\n// std::vector, VectorBuffer never constructs or destructs its arguments, and\n// can't change sizes. But it does implement templates to assist in efficient\n// moving and destruction of those items manually.\n//\n// In particular, the destructor function does not iterate over the items if\n// there is no destructor. Moves should be implemented as a memcpy/memmove for\n// trivially copyable objects (POD) otherwise, it should be a std::move if\n// possible, and as a last resort it falls back to a copy. This behavior is\n// similar to std::vector.\n//\n// No special consideration is done for noexcept move constructors since\n// we compile without exceptions.\n//\n// The current API does not support moving overlapping ranges.\ntemplate <typename T>\nclass VectorBuffer {\n public:\n  constexpr VectorBuffer() = default;\n\n#if defined(__clang__) && !defined(__native_client__)\n  // This constructor converts an uninitialized void* to a T* which triggers\n  // clang Control Flow Integrity. Since this is as-designed, disable.\n  __attribute__((no_sanitize(\"cfi-unrelated-cast\", \"vptr\")))\n#endif\n  VectorBuffer(size_t count)\n      : buffer_(reinterpret_cast<T*>(\n            malloc(CheckMul(sizeof(T), count).ValueOrDie()))),\n        capacity_(count) {\n  }\n  VectorBuffer(VectorBuffer&& other) noexcept\n      : buffer_(other.buffer_), capacity_(other.capacity_) {\n    other.buffer_ = nullptr;\n    other.capacity_ = 0;\n  }\n\n  VectorBuffer(const VectorBuffer&) = delete;\n  VectorBuffer& operator=(const VectorBuffer&) = delete;\n\n  ~VectorBuffer() { free(buffer_); }\n\n  VectorBuffer& operator=(VectorBuffer&& other) {\n    free(buffer_);\n    buffer_ = other.buffer_;\n    capacity_ = other.capacity_;\n\n    other.buffer_ = nullptr;\n    other.capacity_ = 0;\n    return *this;\n  }\n\n  size_t capacity() const { return capacity_; }\n\n  T& operator[](size_t i) {\n    // TODO(crbug.com/817982): Some call sites (at least circular_deque.h) are\n    // calling this with `i == capacity_` as a way of getting `end()`. Therefore\n    // we have to allow this for now (`i <= capacity_`), until we fix those call\n    // sites to use real iterators. This comment applies here and to `const T&\n    // operator[]`, below.\n    CHECK_LE(i, capacity_);\n    return buffer_[i];\n  }\n\n  const T& operator[](size_t i) const {\n    CHECK_LE(i, capacity_);\n    return buffer_[i];\n  }\n\n  T* begin() { return buffer_; }\n  T* end() { return &buffer_[capacity_]; }\n\n  // DestructRange ------------------------------------------------------------\n\n  // Trivially destructible objects need not have their destructors called.\n  template <typename T2 = T,\n            typename std::enable_if<std::is_trivially_destructible<T2>::value,\n                                    int>::type = 0>\n  void DestructRange(T* begin, T* end) {}\n\n  // Non-trivially destructible objects must have their destructors called\n  // individually.\n  template <typename T2 = T,\n            typename std::enable_if<!std::is_trivially_destructible<T2>::value,\n                                    int>::type = 0>\n  void DestructRange(T* begin, T* end) {\n    CHECK_LE(begin, end);\n    while (begin != end) {\n      begin->~T();\n      begin++;\n    }\n  }\n\n  // MoveRange ----------------------------------------------------------------\n  //\n  // The destructor will be called (as necessary) for all moved types. The\n  // ranges must not overlap.\n  //\n  // The parameters and begin and end (one past the last) of the input buffer,\n  // and the address of the first element to copy to. There must be sufficient\n  // room in the destination for all items in the range [begin, end).\n\n  // Trivially copyable types can use memcpy. trivially copyable implies\n  // that there is a trivial destructor as we don't have to call it.\n  template <\n      typename T2 = T,\n      typename std::enable_if<std::is_trivially_copyable_v<T2>, int>::type = 0>\n  static void MoveRange(T* from_begin, T* from_end, T* to) {\n    CHECK(!RangesOverlap(from_begin, from_end, to));\n\n    memcpy(\n        to, from_begin,\n        CheckSub(get_uintptr(from_end), get_uintptr(from_begin)).ValueOrDie());\n  }\n\n  // Not trivially copyable, but movable: call the move constructor and\n  // destruct the original.\n  template <typename T2 = T,\n            typename std::enable_if<std::is_move_constructible<T2>::value &&\n                                        !std::is_trivially_copyable_v<T2>,\n                                    int>::type = 0>\n  static void MoveRange(T* from_begin, T* from_end, T* to) {\n    CHECK(!RangesOverlap(from_begin, from_end, to));\n    while (from_begin != from_end) {\n      new (to) T(std::move(*from_begin));\n      from_begin->~T();\n      from_begin++;\n      to++;\n    }\n  }\n\n  // Not movable, not trivially copyable: call the copy constructor and\n  // destruct the original.\n  template <typename T2 = T,\n            typename std::enable_if<!std::is_move_constructible<T2>::value &&\n                                        !std::is_trivially_copyable_v<T2>,\n                                    int>::type = 0>\n  static void MoveRange(T* from_begin, T* from_end, T* to) {\n    CHECK(!RangesOverlap(from_begin, from_end, to));\n    while (from_begin != from_end) {\n      new (to) T(*from_begin);\n      from_begin->~T();\n      from_begin++;\n      to++;\n    }\n  }\n\n private:\n  static bool RangesOverlap(const T* from_begin,\n                            const T* from_end,\n                            const T* to) {\n    const auto from_begin_uintptr = get_uintptr(from_begin);\n    const auto from_end_uintptr = get_uintptr(from_end);\n    const auto to_uintptr = get_uintptr(to);\n    return !(\n        to >= from_end ||\n        CheckAdd(to_uintptr, CheckSub(from_end_uintptr, from_begin_uintptr))\n                .ValueOrDie() <= from_begin_uintptr);\n  }\n\n  // `buffer_` is not a raw_ptr<...> for performance reasons (based on analysis\n  // of sampling profiler data and tab_search:top100:2020).\n  // TODO(chokobole):\n  // RAW_PTR_EXCLUSION T* buffer_ = nullptr;\n  T* buffer_ = nullptr;\n  size_t capacity_ = 0;\n};\n\n}  // namespace internal\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_CONTAINERS_VECTOR_BUFFER_H_\n\n// clang-format on\n"
  },
  {
    "path": "tachyon/base/containers/vector_buffer_unittest.cc",
    "content": "// Copyright 2017 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n// clang-format off\n\n#include \"tachyon/base/containers/vector_buffer.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/test/copy_only_int.h\"\n#include \"tachyon/base/test/move_only_int.h\"\n\nnamespace tachyon::base {\nnamespace internal {\n\nTEST(VectorBuffer, DeletePOD) {\n  constexpr int size = 10;\n  VectorBuffer<int> buffer(size);\n  for (int i = 0; i < size; i++)\n    buffer.begin()[i] = i + 1;\n\n  buffer.DestructRange(buffer.begin(), buffer.end());\n\n  // Delete should do nothing.\n  for (int i = 0; i < size; i++)\n    EXPECT_EQ(i + 1, buffer.begin()[i]);\n}\n\nTEST(VectorBuffer, DeleteMoveOnly) {\n  constexpr int size = 10;\n  VectorBuffer<MoveOnlyInt> buffer(size);\n  for (int i = 0; i < size; i++)\n    new (buffer.begin() + i) MoveOnlyInt(i + 1);\n\n  buffer.DestructRange(buffer.begin(), buffer.end());\n\n  // Delete should have reset all of the values to 0.\n  for (int i = 0; i < size; i++)\n    EXPECT_EQ(0, buffer.begin()[i].data());\n}\n\nTEST(VectorBuffer, PODMove) {\n  constexpr int size = 10;\n  VectorBuffer<int> dest(size);\n\n  VectorBuffer<int> original(size);\n  for (int i = 0; i < size; i++)\n    original.begin()[i] = i + 1;\n\n  original.MoveRange(original.begin(), original.end(), dest.begin());\n  for (int i = 0; i < size; i++)\n    EXPECT_EQ(i + 1, dest.begin()[i]);\n}\n\nTEST(VectorBuffer, MovableMove) {\n  constexpr int size = 10;\n  VectorBuffer<MoveOnlyInt> dest(size);\n\n  VectorBuffer<MoveOnlyInt> original(size);\n  for (int i = 0; i < size; i++)\n    new (original.begin() + i) MoveOnlyInt(i + 1);\n\n  original.MoveRange(original.begin(), original.end(), dest.begin());\n\n  // Moving from a MoveOnlyInt resets to 0.\n  for (int i = 0; i < size; i++) {\n    EXPECT_EQ(0, original.begin()[i].data());\n    EXPECT_EQ(i + 1, dest.begin()[i].data());\n  }\n}\n\nTEST(VectorBuffer, CopyToMove) {\n  constexpr int size = 10;\n  VectorBuffer<CopyOnlyInt> dest(size);\n\n  VectorBuffer<CopyOnlyInt> original(size);\n  for (int i = 0; i < size; i++)\n    new (original.begin() + i) CopyOnlyInt(i + 1);\n\n  original.MoveRange(original.begin(), original.end(), dest.begin());\n\n  // The original should have been destructed, which should reset the value to\n  // 0. Technically this dereferences the destructed object.\n  for (int i = 0; i < size; i++) {\n    EXPECT_EQ(0, original.begin()[i].data());\n    EXPECT_EQ(i + 1, dest.begin()[i].data());\n  }\n}\n\n}  // namespace internal\n}  // namespace tachyon::base\n\n// clang-format on\n"
  },
  {
    "path": "tachyon/base/containers/zipped_iterator.h",
    "content": "#ifndef TACHYON_BASE_CONTAINERS_ZIPPED_ITERATOR_H_\n#define TACHYON_BASE_CONTAINERS_ZIPPED_ITERATOR_H_\n\n#include <tuple>\n#include <type_traits>\n#include <utility>\n\n#include \"tachyon/base/template_util.h\"\n\nnamespace tachyon::base {\n\ntemplate <typename Iter, typename Iter2>\nclass ZippedIterator {\n public:\n  using underlying_value_type = iter_value_t<Iter>;\n  using underlying_value_type2 = iter_value_t<Iter2>;\n\n  using difference_type = iter_difference_t<Iter>;\n  using value_type = std::tuple<underlying_value_type, underlying_value_type2>;\n  using pointer = value_type*;\n  using reference = value_type&;\n  using iterator_category =\n      typename std::iterator_traits<Iter>::iterator_category;\n\n  ZippedIterator(Iter current, Iter2 current2)\n      : current_(std::move(current)), current2_(std::move(current2)) {}\n\n  ZippedIterator(const ZippedIterator& other) = default;\n  ZippedIterator& operator=(const ZippedIterator& other) = default;\n\n  bool operator==(const ZippedIterator& other) const {\n    return current_ == other.current_ && current2_ == other.current2_;\n  }\n  bool operator!=(const ZippedIterator& other) const {\n    return !(*this == other);\n  }\n\n  // TODO(chokobole): Add more operators to support iterator category other than\n  // forward_iterator.\n  ZippedIterator& operator++() {\n    ++current_;\n    ++current2_;\n    return *this;\n  }\n\n  ZippedIterator operator++(int) {\n    ZippedIterator iterator(*this);\n    ++(*this);\n    return iterator;\n  }\n\n  difference_type operator-(const ZippedIterator& other) const {\n    return current_ - other.current_;\n  }\n\n  // NOTE(chokobole): To suppress -Werror,-Wignored-reference-qualifiers on\n  // mac\n  const std::remove_const_t<pointer> operator->() const {\n    value_ = value_type(*current_, *current2_);\n    return &value_;\n  }\n  pointer operator->() {\n    return const_cast<pointer>(std::as_const(*this).operator->());\n  }\n\n  // NOTE(chokobole): To suppress -Werror,-Wignored-reference-qualifiers on\n  // mac\n  const std::remove_const_t<reference> operator*() const {\n    value_ = value_type(*current_, *current2_);\n    return value_;\n  }\n  reference operator*() {\n    return const_cast<reference>(std::as_const(*this).operator*());\n  }\n\n private:\n  Iter current_;\n  Iter2 current2_;\n  mutable value_type value_;\n};\n\ntemplate <typename T, typename U>\nauto ZippedBegin(T& t, U& u) {\n  using Iter = decltype(std::begin(std::declval<T&>()));\n  using Iter2 = decltype(std::begin(std::declval<U&>()));\n  return ZippedIterator<Iter, Iter2>(std::begin(t), std::begin(u));\n}\n\ntemplate <typename T, typename U>\nauto ZippedEnd(T& t, U& u) {\n  using Iter = decltype(std::end(std::declval<T&>()));\n  using Iter2 = decltype(std::end(std::declval<U&>()));\n  return ZippedIterator<Iter, Iter2>(std::end(t), std::end(u));\n}\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_CONTAINERS_ZIPPED_ITERATOR_H_\n"
  },
  {
    "path": "tachyon/base/cxx20_is_constant_evaluated.h",
    "content": "// Copyright 2022 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_CXX20_IS_CONSTANT_EVALUATED_H_\n#define TACHYON_BASE_CXX20_IS_CONSTANT_EVALUATED_H_\n\nnamespace tachyon::base {\n\n// Implementation of C++20's std::is_constant_evaluated.\n//\n// References:\n// - https://en.cppreference.com/w/cpp/types/is_constant_evaluated\n// - https://wg21.link/meta.const.eval\nconstexpr bool is_constant_evaluated() noexcept {\n  return __builtin_is_constant_evaluated();\n}\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_CXX20_IS_CONSTANT_EVALUATED_H_\n"
  },
  {
    "path": "tachyon/base/cxx20_is_constant_evaluated_unittest.cc",
    "content": "// Copyright 2022 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/cxx20_is_constant_evaluated.h\"\n\n#include \"gtest/gtest.h\"\n\nnamespace tachyon::base {\n\nTEST(Cxx20IsConstantEvaluated, Basic) {\n  static_assert(is_constant_evaluated(), \"\");\n  EXPECT_FALSE(is_constant_evaluated());\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/endian.cc",
    "content": "#include \"tachyon/base/endian.h\"\n\n#include \"tachyon/base/logging.h\"\n\nnamespace tachyon::base {\n\nstd::string_view EndianToString(Endian endian) {\n  switch (endian) {\n    case Endian::kNative:\n      return \"Native\";\n    case Endian::kBig:\n      return \"Big\";\n    case Endian::kLittle:\n      return \"Little\";\n  }\n  NOTREACHED();\n  return \"\";\n}\n\nstd::ostream& operator<<(std::ostream& os, Endian endian) {\n  return os << EndianToString(endian);\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/endian.h",
    "content": "#ifndef TACHYON_BASE_ENDIAN_H_\n#define TACHYON_BASE_ENDIAN_H_\n\n#include <ostream>\n\n#include \"tachyon/export.h\"\n\nnamespace tachyon::base {\n\nenum class Endian {\n  kNative,\n  kBig,\n  kLittle,\n};\n\nTACHYON_EXPORT std::string_view EndianToString(Endian endian);\n\nTACHYON_EXPORT std::ostream& operator<<(std::ostream& os, Endian endian);\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_ENDIAN_H_\n"
  },
  {
    "path": "tachyon/base/endian_utils.h",
    "content": "#ifndef TACHYON_BASE_ENDIAN_UTILS_H_\n#define TACHYON_BASE_ENDIAN_UTILS_H_\n\n#include \"tachyon/build/build_config.h\"\n\n#if ARCH_CPU_BIG_ENDIAN\n#define FOR_FROM_BIGGEST(idx, start, end) \\\n  for (size_t idx = start; idx < end; ++idx)\n#else  // ARCH_CPU_LITTLE_ENDIAN\n#define FOR_FROM_BIGGEST(idx, start, end) \\\n  for (size_t idx = end - 1; idx != static_cast<size_t>(start - 1); --idx)\n#endif\n\n#if ARCH_CPU_BIG_ENDIAN\n#define FOR_FROM_SMALLEST(idx, start, end) \\\n  for (size_t idx = end - 1; idx != static_cast<size_t>(start - 1); --idx)\n#else  // ARCH_CPU_LITTLE_ENDIAN\n#define FOR_FROM_SMALLEST(idx, start, end) \\\n  for (size_t idx = start; idx < end; ++idx)\n#endif\n\n#if ARCH_CPU_BIG_ENDIAN\n#define FOR_FROM_SECOND_SMALLEST(idx, start, end) \\\n  for (size_t idx = end - 2; idx != static_cast<size_t>(start - 1); --idx)\n#else  // ARCH_CPU_LITTLE_ENDIAN\n#define FOR_FROM_SECOND_SMALLEST(idx, start, end) \\\n  for (size_t idx = start + 1; idx < end; ++idx)\n#endif\n\n#if ARCH_CPU_BIG_ENDIAN\n#define FOR_BUT_SMALLEST(idx, end) for (size_t idx = 0; idx != end - 1; ++idx)\n#else  // ARCH_CPU_LITTLE_ENDIAN\n#define FOR_BUT_SMALLEST(idx, end) for (size_t idx = 1; idx < end; ++idx)\n#endif\n\n#if ARCH_CPU_BIG_ENDIAN\n#define SMALLEST_INDEX(end) end - 1\n#else  // ARCH_CPU_LITTLE_ENDIAN\n#define SMALLEST_INDEX(end) 0\n#endif\n\n#if ARCH_CPU_BIG_ENDIAN\n#define BIGGEST_INDEX(end) 0\n#else  // ARCH_CPU_LITTLE_ENDIAN\n#define BIGGEST_INDEX(end) end - 1\n#endif\n\n#endif  // TACHYON_BASE_ENDIAN_UTILS_H_\n"
  },
  {
    "path": "tachyon/base/endian_utils_unittest.cc",
    "content": "#include \"tachyon/base/endian_utils.h\"\n\n#include <vector>\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/containers/cxx20_erase_vector.h\"\n\nstruct Range {\n  size_t start;\n  size_t end;\n};\n\nTEST(EndianUtils, ForFromBiggest) {\n  struct {\n    size_t start;\n    size_t end;\n    std::vector<size_t> answers;\n  } tests[] = {\n      {0, 5, {4, 3, 2, 1, 0}},\n      {2, 4, {3, 2}},\n  };\n\n  for (const auto& test : tests) {\n    std::vector<size_t> idxs;\n    FOR_FROM_BIGGEST(i, test.start, test.end) { idxs.push_back(i); }\n\n    EXPECT_THAT(idxs, testing::ContainerEq(test.answers));\n  }\n}\n\nTEST(EndianUtils, ForFromSmallest) {\n  struct {\n    size_t start;\n    size_t end;\n    std::vector<size_t> answers;\n  } tests[] = {\n      {0, 5, {0, 1, 2, 3, 4}},\n      {2, 4, {2, 3}},\n  };\n\n  for (const auto& test : tests) {\n    std::vector<size_t> idxs;\n    FOR_FROM_SMALLEST(i, test.start, test.end) { idxs.push_back(i); }\n\n    EXPECT_THAT(idxs, testing::ContainerEq(test.answers));\n  }\n}\n\nTEST(EndianUtils, ForBugSmallest) {\n  std::vector<size_t> idxs;\n  FOR_BUT_SMALLEST(i, 5) { idxs.push_back(i); }\n\n  std::vector<size_t> answers({0, 1, 2, 3, 4});\n  tachyon::base::Erase(answers, SMALLEST_INDEX(5));\n  EXPECT_THAT(idxs, testing::ContainerEq(answers));\n}\n\nTEST(EndianUtils, SmallestIndex) {\n#if ARCH_CPU_BIG_ENDIAN\n  int answer = 4;\n#else  // ARCH_CPU_LITTLE_ENDIAN\n  int answer = 0;\n#endif\n  EXPECT_EQ(SMALLEST_INDEX(5), answer);\n}\n\nTEST(EndianUtils, BiggestIndex) {\n#if ARCH_CPU_BIG_ENDIAN\n  int answer = 0;\n#else  // ARCH_CPU_LITTLE_ENDIAN\n  int answer = 4;\n#endif\n  EXPECT_EQ(BIGGEST_INDEX(5), answer);\n}\n"
  },
  {
    "path": "tachyon/base/environment.h",
    "content": "#ifndef TACHYON_BASE_ENVIRONMENT_H_\n#define TACHYON_BASE_ENVIRONMENT_H_\n\n#include <map>\n#include <string>\n#include <string_view>\n\n#include \"tachyon/export.h\"\n\nnamespace tachyon::base {\n\n// This is a wrapper class to interact with environment variables.\n// NOTE: Do not call directly getenv() or setenv() inside tachyon project.\nclass TACHYON_EXPORT Environment {\n public:\n  Environment() = delete;\n\n  Environment(const Environment& other) = delete;\n  Environment& operator=(const Environment& other) = delete;\n\n  // Syntactic sugar for Get(variable_name, nullptr);\n  static bool Has(std::string_view variable_name);\n  // Returns true iff successful. This is not threadsafe.\n  static bool Get(std::string_view variable_name, std::string_view* value);\n\n  // Returns true iff successful. This is not threadsafe.\n  static bool Set(std::string_view variable_name, std::string_view value);\n  // Returns true iff successful. This is not threadsafe.\n  static bool Unset(std::string_view variable_name);\n};\n\nusing EnvironmentMap = std::map<std::string, std::string>;\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_ENVIRONMENT_H_\n"
  },
  {
    "path": "tachyon/base/environment_posix.cc",
    "content": "#include <stdlib.h>\n\n#include \"tachyon/base/environment.h\"\n\nnamespace tachyon::base {\n\n// static\nbool Environment::Has(std::string_view variable_name) {\n  return Get(variable_name, nullptr);\n}\n\n// static\nbool Environment::Get(std::string_view variable_name, std::string_view* value) {\n  const char* env_var = getenv(variable_name.data());\n  if (!env_var) return false;\n  if (value) *value = env_var;\n  return true;\n}\n\n// static\nbool Environment::Set(std::string_view variable_name, std::string_view value) {\n  return setenv(variable_name.data(), value.data(), /*overwrite=*/1) == 0;\n}\n\n// static\nbool Environment::Unset(std::string_view variable_name) {\n  return unsetenv(variable_name.data()) == 0;\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/environment_unittest.cc",
    "content": "#include \"tachyon/base/environment.h\"\n\n#include \"gtest/gtest.h\"\n\nnamespace tachyon::base {\n\nTEST(Environment, BasicTest) {\n  std::string_view value;\n  EXPECT_FALSE(Environment::Has(\"foo\"));\n  EXPECT_FALSE(Environment::Get(\"foo\", &value));\n\n  EXPECT_TRUE(Environment::Set(\"foo\", \"bar\"));\n  EXPECT_TRUE(Environment::Has(\"foo\"));\n  EXPECT_TRUE(Environment::Get(\"foo\", &value));\n  EXPECT_STREQ(value.data(), \"bar\");\n\n  EXPECT_TRUE(Environment::Unset(\"foo\"));\n  EXPECT_FALSE(Environment::Has(\"foo\"));\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/files/.clang-format",
    "content": "DisableFormat: true\nSortIncludes: true\n"
  },
  {
    "path": "tachyon/base/files/BUILD.bazel",
    "content": "load(\n    \"//bazel:tachyon.bzl\",\n    \"if_linux\",\n    \"if_macos\",\n    \"if_posix\",\n)\nload(\n    \"//bazel:tachyon_cc.bzl\",\n    \"tachyon_cc_library\",\n    \"tachyon_cc_unittest\",\n    \"tachyon_objc_library\",\n)\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"bin_file\",\n    srcs = [\"bin_file.cc\"],\n    hdrs = [\"bin_file.h\"],\n    deps = [\n        \":file_util\",\n        \":memory_mapped_file\",\n        \"//tachyon:export\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base/buffer:read_only_buffer\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"file\",\n    srcs = [\"file.cc\"] + if_posix([\"file_posix.cc\"]),\n    hdrs = [\"file.h\"],\n    deps = [\n        \":file_path\",\n        \":platform_file\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base/numerics:safe_conversions\",\n        \"//tachyon/base/posix:eintr_wrapper\",\n        \"//tachyon/base/time\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"file_path\",\n    srcs = [\"file_path.cc\"],\n    hdrs = [\"file_path.h\"],\n    deps = [\n        \"//tachyon/base/strings:string_util\",\n        \"//tachyon/build:build_config\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"file_path_flag\",\n    hdrs = [\"file_path_flag.h\"],\n    deps = [\n        \":file_path\",\n        \"//tachyon/base/flag\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"file_util_base\",\n    hdrs = [\n        \"file_enumerator.h\",\n        \"file_util.h\",\n    ],\n    deps = [\n        \":file\",\n        \"//tachyon/base/containers:stack\",\n        \"//tachyon/base/functional:callback\",\n        \"//tachyon/base/functional:function_ref\",\n        \"//tachyon/base/posix:eintr_wrapper\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"file_util\",\n    srcs = [\n        \"file_enumerator.cc\",\n        \"file_util.cc\",\n    ] + if_posix([\n        \"file_enumerator_posix.cc\",\n        \"file_util_posix.cc\",\n    ]),\n    deps = [\n        \":file_util_base\",\n        \"//tachyon/base:bit_cast\",\n        \"//tachyon/base:bits\",\n        \"//tachyon/base:environment\",\n        \"//tachyon/base/containers:adapters\",\n        \"//tachyon/base/containers:contains\",\n        \"//tachyon/base/strings:string_util\",\n        \"@com_google_absl//absl/strings:str_format\",\n    ] + if_macos([\n        \":file_util_mac\",\n    ]),\n)\n\ntachyon_objc_library(\n    name = \"file_util_mac\",\n    srcs = [\"file_util_mac.mm\"],\n    deps = [\n        \":file_util_base\",\n        \"//tachyon/base/mac:foundation_util\",\n        \"//tachyon/base/strings:string_util\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"memory_mapped_file\",\n    srcs = [\"memory_mapped_file.cc\"] + if_posix([\"memory_mapped_file_posix.cc\"]),\n    hdrs = [\"memory_mapped_file.h\"],\n    deps = [\n        \":file\",\n        \":file_util\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base/numerics:safe_math\",\n        \"//tachyon/base/system:sys_info\",\n        \"//tachyon/build:build_config\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"platform_file\",\n    hdrs = [\"platform_file.h\"],\n    deps = [\n        \":scoped_file\",\n        \"//tachyon/build:build_config\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"scoped_file\",\n    srcs = [\"scoped_file.cc\"] + if_linux([\n        \"scoped_file_linux.cc\",\n    ]),\n    hdrs = [\"scoped_file.h\"],\n    linkopts = if_linux([\"-ldl\"]),\n    deps = [\n        \"//tachyon:export\",\n        \"//tachyon/base:compiler_specific\",\n        \"//tachyon/base:immediate_crash\",\n        \"//tachyon/base:scoped_generic\",\n        \"//tachyon/base/posix:eintr_wrapper\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"scoped_temp_dir\",\n    srcs = [\"scoped_temp_dir.cc\"],\n    hdrs = [\"scoped_temp_dir.h\"],\n    deps = [\":file_util\"],\n)\n\ntachyon_cc_unittest(\n    name = \"files_unittests\",\n    srcs = [\n        \"file_enumerator_unittest.cc\",\n        \"file_path_unittest.cc\",\n        \"file_unittest.cc\",\n        \"scoped_temp_dir_unittest.cc\",\n    ] + if_linux([\n        \"scoped_file_linux_unittest.cc\",\n    ]),\n    deps = [\":scoped_temp_dir\"],\n)\n"
  },
  {
    "path": "tachyon/base/files/bin_file.cc",
    "content": "#include \"tachyon/base/files/bin_file.h\"\n\n#include <optional>\n#include <utility>\n\n#include \"tachyon/base/files/file_util.h\"\n#include \"tachyon/base/logging.h\"\n\nnamespace tachyon::base {\n\nbool BinFile::Load(const base::FilePath& path, bool use_mmap) {\n  if (use_mmap) {\n    base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ);\n    std::unique_ptr<base::MemoryMappedFile> map(new base::MemoryMappedFile);\n    if (!map->Initialize(std::move(file),\n                          base::MemoryMappedFile::Region::kWholeFile,\n                          base::MemoryMappedFile::Access::READ_WRITE_COPY))\n      return false;\n    map_ = std::move(map);\n    return true;\n  } else {\n    std::optional<std::vector<uint8_t>> vec = base::ReadFileToBytes(path);\n    if (!vec.has_value()) {\n      LOG(ERROR) << \"Failed to read file: \" << path.value();\n      return false;\n    }\n    vec_ = std::move(vec).value();\n    return true;\n  }\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/files/bin_file.h",
    "content": "#ifndef TACHYON_BASE_FILES_BIN_FILE_H_\n#define TACHYON_BASE_FILES_BIN_FILE_H_\n\n#include <memory>\n#include <vector>\n\n#include \"tachyon/base/buffer/read_only_buffer.h\"\n#include \"tachyon/base/files/memory_mapped_file.h\"\n#include \"tachyon/export.h\"\n\nnamespace tachyon::base {\n\nclass TACHYON_EXPORT BinFile {\n public:\n  BinFile() {}\n  BinFile(const BinFile& other) = delete;\n  BinFile& operator=(const BinFile& other) = delete;\n  BinFile(BinFile&& other) = default;\n  BinFile& operator=(BinFile&& other) = default;\n\n  const std::vector<uint8_t>& vec() const { return vec_; }\n  std::vector<uint8_t>& vec() { return vec_; }\n\n  const MemoryMappedFile* map() const { return map_.get(); }\n  MemoryMappedFile* map() { return map_.get(); }\n\n  const uint8_t* GetData() const {\n    if (map_) {\n      return map_->data();\n    } else {\n      return vec_.data();\n    }\n  }\n\n  uint8_t* GetData() {\n    if (map_) {\n      return map_->data();\n    } else {\n      return const_cast<uint8_t*>(vec_.data());\n    }\n  }\n\n  size_t GetDataLength() const {\n    if (map_) {\n      return map_->length();\n    } else {\n      return vec_.size();\n    }\n  }\n\n  ReadOnlyBuffer ToReadOnlyBuffer() const {\n    return {GetData(), GetDataLength()};\n  }\n\n  bool Load(const FilePath& path, bool use_mmap);\n\n private:\n  std::vector<uint8_t> vec_;\n  std::unique_ptr<MemoryMappedFile> map_;\n};\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_FILES_BIN_FILE_H_\n"
  },
  {
    "path": "tachyon/base/files/file.cc",
    "content": "// Copyright 2011 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/files/file.h\"\n\n#include <utility>\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/files/file_path.h\"\n// #include \"tachyon/base/files/file_tracing.h\"\n// #include \"tachyon/base/metrics/histogram.h\"\n#include \"tachyon/base/numerics/safe_conversions.h\"\n// #include \"tachyon/base/trace_event/base_tracing.h\"\n#include \"tachyon/build/build_config.h\"\n\n#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)\n#include <errno.h>\n#endif\n\nnamespace tachyon::base {\n\nFile::Info::Info() = default;\n\nFile::Info::~Info() = default;\n\nFile::File() = default;\n\n#if !BUILDFLAG(IS_NACL)\nFile::File(const FilePath& path, uint32_t flags) : error_details_(FILE_OK) {\n  Initialize(path, flags);\n}\n#endif\n\nFile::File(ScopedPlatformFile platform_file)\n    : File(std::move(platform_file), false) {}\n\nFile::File(PlatformFile platform_file) : File(platform_file, false) {}\n\nFile::File(ScopedPlatformFile platform_file, bool async)\n    : file_(std::move(platform_file)), error_details_(FILE_OK), async_(async) {\n#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)\n  DCHECK_GE(file_.get(), -1);\n#endif\n}\n\nFile::File(PlatformFile platform_file, bool async)\n    : file_(platform_file),\n      error_details_(FILE_OK),\n      async_(async) {\n#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)\n  DCHECK_GE(platform_file, -1);\n#endif\n}\n\nFile::File(Error error_details) : error_details_(error_details) {}\n\nFile::File(File&& other)\n    : file_(other.TakePlatformFile()),\n      tracing_path_(other.tracing_path_),\n      error_details_(other.error_details()),\n      created_(other.created()),\n      async_(other.async_) {}\n\nFile::~File() {\n  // Go through the AssertIOAllowed logic.\n  Close();\n}\n\nFile& File::operator=(File&& other) {\n  Close();\n  SetPlatformFile(other.TakePlatformFile());\n  tracing_path_ = other.tracing_path_;\n  error_details_ = other.error_details();\n  created_ = other.created();\n  async_ = other.async_;\n  return *this;\n}\n\n#if !BUILDFLAG(IS_NACL)\nvoid File::Initialize(const FilePath& path, uint32_t flags) {\n  if (path.ReferencesParent()) {\n#if BUILDFLAG(IS_WIN)\n    ::SetLastError(ERROR_ACCESS_DENIED);\n#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)\n    errno = EACCES;\n#else\n#error Unsupported platform\n#endif\n    error_details_ = FILE_ERROR_ACCESS_DENIED;\n    return;\n  }\n  /*\n  TODO(chokobole):\n  if (FileTracing::IsCategoryEnabled())\n    tracing_path_ = path;\n  SCOPED_FILE_TRACE(\"Initialize\");\n  */\n  DoInitialize(path, flags);\n}\n#endif\n\nbool File::ReadAndCheck(int64_t offset, absl::Span<uint8_t> data) {\n  int size = checked_cast<int>(data.size());\n  return Read(offset, reinterpret_cast<char*>(data.data()), size) == size;\n}\n\nbool File::ReadAtCurrentPosAndCheck(absl::Span<uint8_t> data) {\n  int size = checked_cast<int>(data.size());\n  return ReadAtCurrentPos(reinterpret_cast<char*>(data.data()), size) == size;\n}\n\nbool File::WriteAndCheck(int64_t offset, absl::Span<const uint8_t> data) {\n  int size = checked_cast<int>(data.size());\n  return Write(offset, reinterpret_cast<const char*>(data.data()), size) ==\n         size;\n}\n\nbool File::WriteAtCurrentPosAndCheck(absl::Span<const uint8_t> data) {\n  int size = checked_cast<int>(data.size());\n  return WriteAtCurrentPos(reinterpret_cast<const char*>(data.data()), size) ==\n         size;\n}\n\n// static\nstd::string File::ErrorToString(Error error) {\n  switch (error) {\n    case FILE_OK:\n      return \"FILE_OK\";\n    case FILE_ERROR_FAILED:\n      return \"FILE_ERROR_FAILED\";\n    case FILE_ERROR_IN_USE:\n      return \"FILE_ERROR_IN_USE\";\n    case FILE_ERROR_EXISTS:\n      return \"FILE_ERROR_EXISTS\";\n    case FILE_ERROR_NOT_FOUND:\n      return \"FILE_ERROR_NOT_FOUND\";\n    case FILE_ERROR_ACCESS_DENIED:\n      return \"FILE_ERROR_ACCESS_DENIED\";\n    case FILE_ERROR_TOO_MANY_OPENED:\n      return \"FILE_ERROR_TOO_MANY_OPENED\";\n    case FILE_ERROR_NO_MEMORY:\n      return \"FILE_ERROR_NO_MEMORY\";\n    case FILE_ERROR_NO_SPACE:\n      return \"FILE_ERROR_NO_SPACE\";\n    case FILE_ERROR_NOT_A_DIRECTORY:\n      return \"FILE_ERROR_NOT_A_DIRECTORY\";\n    case FILE_ERROR_INVALID_OPERATION:\n      return \"FILE_ERROR_INVALID_OPERATION\";\n    case FILE_ERROR_SECURITY:\n      return \"FILE_ERROR_SECURITY\";\n    case FILE_ERROR_ABORT:\n      return \"FILE_ERROR_ABORT\";\n    case FILE_ERROR_NOT_A_FILE:\n      return \"FILE_ERROR_NOT_A_FILE\";\n    case FILE_ERROR_NOT_EMPTY:\n      return \"FILE_ERROR_NOT_EMPTY\";\n    case FILE_ERROR_INVALID_URL:\n      return \"FILE_ERROR_INVALID_URL\";\n    case FILE_ERROR_IO:\n      return \"FILE_ERROR_IO\";\n    case FILE_ERROR_MAX:\n      break;\n  }\n\n  NOTREACHED();\n  return \"\";\n}\n\n/*\nTODO(chokobole):\nvoid File::WriteIntoTrace(perfetto::TracedValue context) const {\n  auto dict = std::move(context).WriteDictionary();\n  dict.Add(\"is_valid\", IsValid());\n  dict.Add(\"created\", created_);\n  dict.Add(\"async\", async_);\n  dict.Add(\"error_details\", ErrorToString(error_details_));\n}\n*/\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/files/file.h",
    "content": "// Copyright 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_FILES_FILE_H_\n#define TACHYON_BASE_FILES_FILE_H_\n\n#include <stdint.h>\n\n#include <string>\n\n#include \"absl/types/span.h\"\n\n#include \"tachyon/export.h\"\n#include \"tachyon/base/files/file_path.h\"\n// #include \"tachyon/base/files/file_tracing.h\"\n#include \"tachyon/base/files/platform_file.h\"\n#include \"tachyon/base/time/time.h\"\n// #include \"tachyon/base/trace_event/base_tracing_forward.h\"\n#include \"tachyon/build/build_config.h\"\n\nstruct stat;\n\nnamespace tachyon::base {\n\nusing stat_wrapper_t = struct stat;\n\n// Thin wrapper around an OS-level file.\n// Note that this class does not provide any support for asynchronous IO, other\n// than the ability to create asynchronous handles on Windows.\n//\n// Note about const: this class does not attempt to determine if the underlying\n// file system object is affected by a particular method in order to consider\n// that method const or not. Only methods that deal with member variables in an\n// obvious non-modifying way are marked as const. Any method that forward calls\n// to the OS is not considered const, even if there is no apparent change to\n// member variables.\n//\n// On POSIX, if the given file is a symbolic link, most of the methods apply to\n// the file that the symbolic link resolves to.\nclass TACHYON_EXPORT File {\n public:\n  // FLAG_(OPEN|CREATE).* are mutually exclusive. You should specify exactly one\n  // of the five (possibly combining with other flags) when opening or creating\n  // a file.\n  // FLAG_(WRITE|APPEND) are mutually exclusive. This is so that APPEND behavior\n  // will be consistent with O_APPEND on POSIX.\n  enum Flags : uint32_t {\n    FLAG_OPEN = 1 << 0,            // Opens a file, only if it exists.\n    FLAG_CREATE = 1 << 1,          // Creates a new file, only if it does not\n                                   // already exist.\n    FLAG_OPEN_ALWAYS = 1 << 2,     // May create a new file.\n    FLAG_CREATE_ALWAYS = 1 << 3,   // May overwrite an old file.\n    FLAG_OPEN_TRUNCATED = 1 << 4,  // Opens a file and truncates it, only if it\n                                   // exists.\n    FLAG_READ = 1 << 5,\n    FLAG_WRITE = 1 << 6,\n    FLAG_APPEND = 1 << 7,\n    FLAG_WIN_EXCLUSIVE_READ = 1 << 8,   // Windows only. Opposite of SHARE.\n    FLAG_WIN_EXCLUSIVE_WRITE = 1 << 9,  // Windows only. Opposite of SHARE.\n    FLAG_ASYNC = 1 << 10,\n    FLAG_WIN_TEMPORARY = 1 << 11,  // Windows only.\n    FLAG_WIN_HIDDEN = 1 << 12,     // Windows only.\n    FLAG_DELETE_ON_CLOSE = 1 << 13,\n    FLAG_WRITE_ATTRIBUTES = 1 << 14,  // File opened in a mode allowing writing\n                                      // attributes, such as with SetTimes().\n    FLAG_WIN_SHARE_DELETE = 1 << 15,  // Windows only.\n    FLAG_TERMINAL_DEVICE = 1 << 16,   // Serial port flags.\n    FLAG_WIN_BACKUP_SEMANTICS = 1 << 17,  // Windows only.\n    FLAG_WIN_EXECUTE = 1 << 18,           // Windows only.\n    FLAG_WIN_SEQUENTIAL_SCAN = 1 << 19,   // Windows only.\n    FLAG_CAN_DELETE_ON_CLOSE = 1 << 20,  // Requests permission to delete a file\n                                         // via DeleteOnClose() (Windows only).\n                                         // See DeleteOnClose() for details.\n    FLAG_WIN_NO_EXECUTE =\n        1 << 21,  // Windows only. Marks the file with a deny ACE that prevents\n                  // opening the file with EXECUTE access. Cannot be used with\n                  // FILE_WIN_EXECUTE flag. See also PreventExecuteMapping.\n  };\n\n  // This enum has been recorded in multiple histograms using PlatformFileError\n  // enum. If the order of the fields needs to change, please ensure that those\n  // histograms are obsolete or have been moved to a different enum.\n  //\n  // FILE_ERROR_ACCESS_DENIED is returned when a call fails because of a\n  // filesystem restriction. FILE_ERROR_SECURITY is returned when a browser\n  // policy doesn't allow the operation to be executed.\n  enum Error {\n    FILE_OK = 0,\n    FILE_ERROR_FAILED = -1,\n    FILE_ERROR_IN_USE = -2,\n    FILE_ERROR_EXISTS = -3,\n    FILE_ERROR_NOT_FOUND = -4,\n    FILE_ERROR_ACCESS_DENIED = -5,\n    FILE_ERROR_TOO_MANY_OPENED = -6,\n    FILE_ERROR_NO_MEMORY = -7,\n    FILE_ERROR_NO_SPACE = -8,\n    FILE_ERROR_NOT_A_DIRECTORY = -9,\n    FILE_ERROR_INVALID_OPERATION = -10,\n    FILE_ERROR_SECURITY = -11,\n    FILE_ERROR_ABORT = -12,\n    FILE_ERROR_NOT_A_FILE = -13,\n    FILE_ERROR_NOT_EMPTY = -14,\n    FILE_ERROR_INVALID_URL = -15,\n    FILE_ERROR_IO = -16,\n    // Put new entries here and increment FILE_ERROR_MAX.\n    FILE_ERROR_MAX = -17\n  };\n\n  // This explicit mapping matches both FILE_ on Windows and SEEK_ on Linux.\n  enum Whence {\n    FROM_BEGIN   = 0,\n    FROM_CURRENT = 1,\n    FROM_END     = 2\n  };\n\n  // Used to hold information about a given file.\n  // If you add more fields to this structure (platform-specific fields are OK),\n  // make sure to update all functions that use it in file_util_{win|posix}.cc,\n  // too, and the ParamTraits<base::File::Info> implementation in\n  // ipc/ipc_message_utils.cc.\n  struct TACHYON_EXPORT Info {\n    Info();\n    ~Info();\n#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)\n    // Fills this struct with values from |stat_info|.\n    void FromStat(const stat_wrapper_t& stat_info);\n#endif\n\n    // The size of the file in bytes.  Undefined when is_directory is true.\n    int64_t size = 0;\n\n    // True if the file corresponds to a directory.\n    bool is_directory = false;\n\n    // True if the file corresponds to a symbolic link.  For Windows currently\n    // not supported and thus always false.\n    bool is_symbolic_link = false;\n\n    // The last modified time of a file.\n    Time last_modified;\n\n    // The last accessed time of a file.\n    Time last_accessed;\n\n    // The creation time of a file.\n    Time creation_time;\n  };\n\n  File();\n\n  // Creates or opens the given file. This will fail with 'access denied' if the\n  // |path| contains path traversal ('..') components.\n  File(const FilePath& path, uint32_t flags);\n\n  // Takes ownership of |platform_file| and sets async to false.\n  explicit File(ScopedPlatformFile platform_file);\n  explicit File(PlatformFile platform_file);\n\n  // Takes ownership of |platform_file| and sets async to the given value.\n  // This constructor exists because on Windows you can't check if platform_file\n  // is async or not.\n  File(ScopedPlatformFile platform_file, bool async);\n  File(PlatformFile platform_file, bool async);\n\n  // Creates an object with a specific error_details code.\n  explicit File(Error error_details);\n\n  File(File&& other);\n\n  File(const File&) = delete;\n  File& operator=(const File&) = delete;\n\n  ~File();\n\n  File& operator=(File&& other);\n\n  // Creates or opens the given file.\n  void Initialize(const FilePath& path, uint32_t flags);\n\n  // Returns |true| if the handle / fd wrapped by this object is valid.  This\n  // method doesn't interact with the file system and is thus safe to be called\n  // from threads that disallow blocking.\n  bool IsValid() const;\n\n  // Returns true if a new file was created (or an old one truncated to zero\n  // length to simulate a new file, which can happen with\n  // FLAG_CREATE_ALWAYS), and false otherwise.\n  bool created() const { return created_; }\n\n  // Returns the OS result of opening this file. Note that the way to verify\n  // the success of the operation is to use IsValid(), not this method:\n  //   File file(path, flags);\n  //   if (!file.IsValid())\n  //     return;\n  Error error_details() const { return error_details_; }\n\n  PlatformFile GetPlatformFile() const;\n  PlatformFile TakePlatformFile();\n\n  // Destroying this object closes the file automatically.\n  void Close();\n\n  // Changes current position in the file to an |offset| relative to an origin\n  // defined by |whence|. Returns the resultant current position in the file\n  // (relative to the start) or -1 in case of error.\n  int64_t Seek(Whence whence, int64_t offset);\n\n  // Simplified versions of Read() and friends (see below) that check the int\n  // return value and just return a boolean. They return true if and only if\n  // the function read in / wrote out exactly |data.size()| bytes of data.\n  bool ReadAndCheck(int64_t offset, absl::Span<uint8_t> data);\n  bool ReadAtCurrentPosAndCheck(absl::Span<uint8_t> data);\n  bool WriteAndCheck(int64_t offset, absl::Span<const uint8_t> data);\n  bool WriteAtCurrentPosAndCheck(absl::Span<const uint8_t> data);\n\n  // Reads the given number of bytes (or until EOF is reached) starting with the\n  // given offset. Returns the number of bytes read, or -1 on error. Note that\n  // this function makes a best effort to read all data on all platforms, so it\n  // is not intended for stream oriented files but instead for cases when the\n  // normal expectation is that actually |size| bytes are read unless there is\n  // an error.\n  int Read(int64_t offset, char* data, int size);\n\n  // Same as above but without seek.\n  int ReadAtCurrentPos(char* data, int size);\n\n  // Reads the given number of bytes (or until EOF is reached) starting with the\n  // given offset, but does not make any effort to read all data on all\n  // platforms. Returns the number of bytes read, or -1 on error.\n  int ReadNoBestEffort(int64_t offset, char* data, int size);\n\n  // Same as above but without seek.\n  int ReadAtCurrentPosNoBestEffort(char* data, int size);\n\n  // Writes the given buffer into the file at the given offset, overwriting any\n  // data that was previously there. Returns the number of bytes written, or -1\n  // on error. Note that this function makes a best effort to write all data on\n  // all platforms. |data| can be nullptr when |size| is 0.\n  // Ignores the offset and writes to the end of the file if the file was opened\n  // with FLAG_APPEND.\n  int Write(int64_t offset, const char* data, int size);\n\n  // Save as above but without seek.\n  int WriteAtCurrentPos(const char* data, int size);\n\n  // Save as above but does not make any effort to write all data on all\n  // platforms. Returns the number of bytes written, or -1 on error.\n  int WriteAtCurrentPosNoBestEffort(const char* data, int size);\n\n  // Returns the current size of this file, or a negative number on failure.\n  int64_t GetLength();\n\n  // Truncates the file to the given length. If |length| is greater than the\n  // current size of the file, the file is extended with zeros. If the file\n  // doesn't exist, |false| is returned.\n  bool SetLength(int64_t length);\n\n  // Instructs the filesystem to flush the file to disk. (POSIX: fsync, Windows:\n  // FlushFileBuffers).\n  // Calling Flush() does not guarantee file integrity and thus is not a valid\n  // substitute for file integrity checks and recovery codepaths for malformed\n  // files. It can also be *really* slow, so avoid blocking on Flush(),\n  // especially please don't block shutdown on Flush().\n  // Latency percentiles of Flush() across all platforms as of July 2016:\n  // 50 %     > 5 ms\n  // 10 %     > 58 ms\n  //  1 %     > 357 ms\n  //  0.1 %   > 1.8 seconds\n  //  0.01 %  > 7.6 seconds\n  bool Flush();\n\n  // Updates the file times.\n  bool SetTimes(Time last_access_time, Time last_modified_time);\n\n  // Returns some basic information for the given file.\n  bool GetInfo(Info* info);\n\n#if !BUILDFLAG( \\\n    IS_FUCHSIA)  // Fuchsia's POSIX API does not support file locking.\n  enum class LockMode {\n    kShared,\n    kExclusive,\n  };\n\n  // Attempts to take an exclusive write lock on the file. Returns immediately\n  // (i.e. does not wait for another process to unlock the file). If the lock\n  // was obtained, the result will be FILE_OK. A lock only guarantees\n  // that other processes may not also take a lock on the same file with the\n  // same API - it may still be opened, renamed, unlinked, etc.\n  //\n  // Common semantics:\n  //  * Locks are held by processes, but not inherited by child processes.\n  //  * Locks are released by the OS on file close or process termination.\n  //  * Locks are reliable only on local filesystems.\n  //  * Duplicated file handles may also write to locked files.\n  // Windows-specific semantics:\n  //  * Locks are mandatory for read/write APIs, advisory for mapping APIs.\n  //  * Within a process, locking the same file (by the same or new handle)\n  //    will fail.\n  // POSIX-specific semantics:\n  //  * Locks are advisory only.\n  //  * Within a process, locking the same file (by the same or new handle)\n  //    will succeed. The new lock replaces the old lock.\n  //  * Closing any descriptor on a given file releases the lock.\n  Error Lock(LockMode mode);\n\n  // Unlock a file previously locked.\n  Error Unlock();\n\n#endif  // !BUILDFLAG(IS_FUCHSIA)\n\n  // Returns a new object referencing this file for use within the current\n  // process. Handling of FLAG_DELETE_ON_CLOSE varies by OS. On POSIX, the File\n  // object that was created or initialized with this flag will have unlinked\n  // the underlying file when it was created or opened. On Windows, the\n  // underlying file is deleted when the last handle to it is closed.\n  File Duplicate() const;\n\n  bool async() const { return async_; }\n\n  // TODO(chokobole):\n  // Serialise this object into a trace.\n  // void WriteIntoTrace(perfetto::TracedValue context) const;\n\n#if BUILDFLAG(IS_WIN)\n  // Sets or clears the DeleteFile disposition on the file. Returns true if\n  // the disposition was set or cleared, as indicated by |delete_on_close|.\n  //\n  // Microsoft Windows deletes a file only when the DeleteFile disposition is\n  // set on a file when the last handle to the last underlying kernel File\n  // object is closed. This disposition is be set by:\n  // - Calling the Win32 DeleteFile function with the path to a file.\n  // - Opening/creating a file with FLAG_DELETE_ON_CLOSE and then closing all\n  //   handles to that File object.\n  // - Opening/creating a file with FLAG_CAN_DELETE_ON_CLOSE and subsequently\n  //   calling DeleteOnClose(true).\n  //\n  // In all cases, all pre-existing handles to the file must have been opened\n  // with FLAG_WIN_SHARE_DELETE. Once the disposition has been set by any of the\n  // above means, no new File objects can be created for the file.\n  //\n  // So:\n  // - Use FLAG_WIN_SHARE_DELETE when creating/opening a file to allow another\n  //   entity on the system to cause it to be deleted when it is closed. (Note:\n  //   another entity can delete the file the moment after it is closed, so not\n  //   using this permission doesn't provide any protections.)\n  // - Use FLAG_DELETE_ON_CLOSE for any file that is to be deleted after use.\n  //   The OS will ensure it is deleted even in the face of process termination.\n  //   Note that it's possible for deletion to be cancelled via another File\n  //   object referencing the same file using DeleteOnClose(false) to clear the\n  //   DeleteFile disposition after the original File is closed.\n  // - Use FLAG_CAN_DELETE_ON_CLOSE in conjunction with DeleteOnClose() to alter\n  //   the DeleteFile disposition on an open handle. This fine-grained control\n  //   allows for marking a file for deletion during processing so that it is\n  //   deleted in the event of untimely process termination, and then clearing\n  //   this state once the file is suitable for persistence.\n  bool DeleteOnClose(bool delete_on_close);\n#endif\n\n#if BUILDFLAG(IS_WIN)\n  static Error OSErrorToFileError(DWORD last_error);\n#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)\n  static Error OSErrorToFileError(int saved_errno);\n#endif\n\n  // Gets the last global error (errno or GetLastError()) and converts it to the\n  // closest base::File::Error equivalent via OSErrorToFileError(). The returned\n  // value is only trustworthy immediately after another base::File method\n  // fails. base::File never resets the global error to zero.\n  static Error GetLastFileError();\n\n  // Converts an error value to a human-readable form. Used for logging.\n  static std::string ErrorToString(Error error);\n\n#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)\n  // Wrapper for stat().\n  static int Stat(const char* path, stat_wrapper_t* sb);\n  // Wrapper for fstat().\n  static int Fstat(int fd, stat_wrapper_t* sb);\n  // Wrapper for lstat().\n  static int Lstat(const char* path, stat_wrapper_t* sb);\n#endif\n\n  // This function can be used to augment `flags` with the correct flags\n  // required to create a File that can be safely passed to an untrusted\n  // process. It must be called if the File is intended to be transferred to an\n  // untrusted process, but can still be safely called even if the File is not\n  // intended to be transferred.\n  static constexpr uint32_t AddFlagsForPassingToUntrustedProcess(\n      uint32_t flags) {\n    if (flags & File::FLAG_WRITE || flags & File::FLAG_APPEND ||\n        flags & File::FLAG_WRITE_ATTRIBUTES) {\n      flags |= File::FLAG_WIN_NO_EXECUTE;\n    }\n    return flags;\n  }\n\n private:\n  // TODO(chokobole):\n  // friend class FileTracing::ScopedTrace;\n\n  // Creates or opens the given file. Only called if |path| has no\n  // traversal ('..') components.\n  void DoInitialize(const FilePath& path, uint32_t flags);\n\n  void SetPlatformFile(PlatformFile file);\n\n  ScopedPlatformFile file_;\n\n  // A path to use for tracing purposes. Set if file tracing is enabled during\n  // |Initialize()|.\n  FilePath tracing_path_;\n\n  // Object tied to the lifetime of |this| that enables/disables tracing.\n  // TODO(chokobole):\n  // FileTracing::ScopedEnabler trace_enabler_;\n\n  Error error_details_ = FILE_ERROR_FAILED;\n  bool created_ = false;\n  bool async_ = false;\n};\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_FILES_FILE_H_\n"
  },
  {
    "path": "tachyon/base/files/file_enumerator.cc",
    "content": "// Copyright 2013 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/files/file_enumerator.h\"\n\n#include \"tachyon/base/files/file_util.h\"\n#include \"tachyon/base/functional/function_ref.h\"\n\nnamespace tachyon::base {\n\nFileEnumerator::FileInfo::~FileInfo() = default;\n\nbool FileEnumerator::ShouldSkip(const FilePath& path) {\n  std::string basename = path.BaseName().value();\n  return basename == \".\" ||\n         (basename == \"..\" &&\n          !(INCLUDE_DOT_DOT & file_type_));\n}\n\nbool FileEnumerator::IsTypeMatched(bool is_dir) const {\n  return (file_type_ &\n          (is_dir ? FileEnumerator::DIRECTORIES : FileEnumerator::FILES)) != 0;\n}\n\nvoid FileEnumerator::ForEach(FunctionRef<void(const FilePath& path)> ref) {\n  for (FilePath name = Next(); !name.empty(); name = Next()) {\n    ref(name);\n  }\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/files/file_enumerator.h",
    "content": "// Copyright 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_FILES_FILE_ENUMERATOR_H_\n#define TACHYON_BASE_FILES_FILE_ENUMERATOR_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <vector>\n\n#include \"tachyon/base/functional/function_ref.h\"\n\n#include \"tachyon/export.h\"\n#include \"tachyon/base/containers/stack.h\"\n#include \"tachyon/base/files/file.h\"\n#include \"tachyon/base/files/file_path.h\"\n#include \"tachyon/base/time/time.h\"\n#include \"tachyon/build/build_config.h\"\n\n#if BUILDFLAG(IS_WIN)\n#include \"tachyon/base/win/windows_types.h\"\n#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)\n#include <sys/stat.h>\n#include <unistd.h>\n#include <unordered_set>\n#endif\n\nnamespace tachyon::base {\n\n// A class for enumerating the files in a provided path. The order of the\n// results is not guaranteed.\n//\n// This is blocking. Do not use on critical threads.\n//\n// Example:\n//\n//   base::FileEnumerator e(my_dir, false, base::FileEnumerator::FILES,\n//                          \"*.txt\");\n// Using `ForEach` with a lambda:\n//   e.ForEach([](const base::FilePath& item) {...});\n// Using a `for` loop:\n//   for (base::FilePath name = e.Next(); !name.empty(); name = e.Next())\n//     ...\nclass TACHYON_EXPORT FileEnumerator {\n public:\n  // Note: copy & assign supported.\n  class TACHYON_EXPORT FileInfo {\n   public:\n    FileInfo();\n    ~FileInfo();\n\n    bool IsDirectory() const;\n\n    // The name of the file. This will not include any path information. This\n    // is in constrast to the value returned by FileEnumerator.Next() which\n    // includes the |root_path| passed into the FileEnumerator constructor.\n    FilePath GetName() const;\n\n    int64_t GetSize() const;\n\n    // On POSIX systems, this is rounded down to the second.\n    Time GetLastModifiedTime() const;\n\n#if BUILDFLAG(IS_WIN)\n    // Note that the cAlternateFileName (used to hold the \"short\" 8.3 name)\n    // of the WIN32_FIND_DATA will be empty. Since we don't use short file\n    // names, we tell Windows to omit it which speeds up the query slightly.\n    const WIN32_FIND_DATA& find_data() const {\n      return *ChromeToWindowsType(&find_data_);\n    }\n#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)\n    const stat_wrapper_t& stat() const { return stat_; }\n#endif\n\n   private:\n    friend class FileEnumerator;\n\n#if BUILDFLAG(IS_WIN)\n    CHROME_WIN32_FIND_DATA find_data_;\n#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)\n    stat_wrapper_t stat_;\n    FilePath filename_;\n#endif\n  };\n\n  enum FileType {\n    FILES = 1 << 0,\n    DIRECTORIES = 1 << 1,\n    INCLUDE_DOT_DOT = 1 << 2,\n\n    // Report only the names of entries and not their type, size, or\n    // last-modified time. May only be used for non-recursive enumerations, and\n    // implicitly includes both files and directories (neither of which may be\n    // specified). When used, an enumerator's `GetInfo()` method must not be\n    // called.\n    NAMES_ONLY = 1 << 3,\n\n#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)\n    SHOW_SYM_LINKS = 1 << 4,\n#endif\n  };\n\n  // Search policy for intermediate folders.\n  enum class FolderSearchPolicy {\n    // Recursive search will pass through folders whose names match the\n    // pattern. Inside each one, all files will be returned. Folders with names\n    // that do not match the pattern will be ignored within their interior.\n    MATCH_ONLY,\n    // Recursive search will pass through every folder and perform pattern\n    // matching inside each one.\n    ALL,\n  };\n\n  // Determines how a FileEnumerator handles errors encountered during\n  // enumeration. When no ErrorPolicy is explicitly set, FileEnumerator defaults\n  // to IGNORE_ERRORS.\n  enum class ErrorPolicy {\n    // Errors are ignored if possible and FileEnumerator returns as many files\n    // as it is able to enumerate.\n    IGNORE_ERRORS,\n\n    // Any error encountered during enumeration will terminate the enumeration\n    // immediately. An error code indicating the nature of a failure can be\n    // retrieved from |GetError()|.\n    STOP_ENUMERATION,\n  };\n\n  // |root_path| is the starting directory to search for. It may or may not end\n  // in a slash.\n  //\n  // If |recursive| is true, this will enumerate all matches in any\n  // subdirectories matched as well. It does a breadth-first search, so all\n  // files in one directory will be returned before any files in a\n  // subdirectory.\n  //\n  // |file_type|, a bit mask of FileType, specifies whether the enumerator\n  // should match files, directories, or both.\n  //\n  // |pattern| is an optional pattern for which files to match. This\n  // works like shell globbing. For example, \"*.txt\" or \"Foo???.doc\".\n  // However, be careful in specifying patterns that aren't cross platform\n  // since the underlying code uses OS-specific matching routines.  In general,\n  // Windows matching is less featureful than others, so test there first.\n  // If unspecified, this will match all files.\n  //\n  // |folder_search_policy| optionally specifies a search behavior. Refer to\n  // |FolderSearchPolicy| for a list of folder search policies and the meaning\n  // of them. If |recursive| is false, this parameter has no effect.\n  //\n  // |error_policy| optionally specifies the behavior when an error occurs.\n  // Refer to |ErrorPolicy| for a list of error policies and the meaning of\n  // them.\n  FileEnumerator(const FilePath& root_path, bool recursive, int file_type);\n  FileEnumerator(const FilePath& root_path,\n                 bool recursive,\n                 int file_type,\n                 const std::string& pattern);\n  FileEnumerator(const FilePath& root_path,\n                 bool recursive,\n                 int file_type,\n                 const std::string& pattern,\n                 FolderSearchPolicy folder_search_policy);\n  FileEnumerator(const FilePath& root_path,\n                 bool recursive,\n                 int file_type,\n                 const std::string& pattern,\n                 FolderSearchPolicy folder_search_policy,\n                 ErrorPolicy error_policy);\n  FileEnumerator(const FileEnumerator&) = delete;\n  FileEnumerator& operator=(const FileEnumerator&) = delete;\n  ~FileEnumerator();\n\n  // Calls `ref` synchronously for each path found by the `FileEnumerator`. Each\n  // path will incorporate the `root_path` passed in the constructor:\n  // \"<root_path>/file_name.txt\". If the `root_path` is absolute, then so will\n  // be the paths provided in the `ref` invocations.\n  void ForEach(FunctionRef<void(const FilePath& path)> ref);\n\n  // Returns the next file or an empty string if there are no more results.\n  //\n  // The returned path will incorporate the |root_path| passed in the\n  // constructor: \"<root_path>/file_name.txt\". If the |root_path| is absolute,\n  // then so will be the result of Next().\n  FilePath Next();\n\n  // Returns info about the file last returned by Next(). Note that on Windows\n  // and Fuchsia, GetInfo() does not play well with INCLUDE_DOT_DOT. In\n  // particular, the GetLastModifiedTime() for the .. directory is 1601-01-01\n  // on Fuchsia (https://crbug.com/1106172) and is equal to the last modified\n  // time of the current directory on Windows (https://crbug.com/1119546).\n  // Must not be used with FileType::NAMES_ONLY.\n  FileInfo GetInfo() const;\n\n  // Once |Next()| returns an empty path, enumeration has been terminated. If\n  // termination was normal (i.e. no more results to enumerate) or ErrorPolicy\n  // is set to IGNORE_ERRORS, this returns FILE_OK. Otherwise it returns an\n  // error code reflecting why enumeration was stopped early.\n  File::Error GetError() const { return error_; }\n\n private:\n  // Returns true if the given path should be skipped in enumeration.\n  bool ShouldSkip(const FilePath& path);\n\n  bool IsTypeMatched(bool is_dir) const;\n\n  bool IsPatternMatched(const FilePath& src) const;\n\n#if BUILDFLAG(IS_WIN)\n  const WIN32_FIND_DATA& find_data() const {\n    return *ChromeToWindowsType(&find_data_);\n  }\n\n  // True when find_data_ is valid.\n  bool has_find_data_ = false;\n  CHROME_WIN32_FIND_DATA find_data_;\n  HANDLE find_handle_ = INVALID_HANDLE_VALUE;\n#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)\n  // The files in the current directory\n  std::vector<FileInfo> directory_entries_;\n\n  // Set of visited directories. Used to prevent infinite looping along\n  // circular symlinks.\n  // The Android NDK (r23) does not declare `st_ino` as an `ino_t`, hence the\n  // need for the ugly decltype.\n  std::unordered_set<decltype(stat_wrapper_t::st_ino)> visited_directories_;\n\n  // The next entry to use from the directory_entries_ vector\n  size_t current_directory_entry_;\n#endif\n  FilePath root_path_;\n  const bool recursive_;\n  int file_type_;\n  std::string pattern_;\n  const FolderSearchPolicy folder_search_policy_;\n  const ErrorPolicy error_policy_;\n  File::Error error_ = File::FILE_OK;\n\n  // A stack that keeps track of which subdirectories we still need to\n  // enumerate in the breadth-first search.\n  base::stack<FilePath> pending_paths_;\n};\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_FILES_FILE_ENUMERATOR_H_\n"
  },
  {
    "path": "tachyon/base/files/file_enumerator_posix.cc",
    "content": "// Copyright 2013 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/files/file_enumerator.h\"\n\n#include <dirent.h>\n#include <errno.h>\n#include <fnmatch.h>\n#include <stdint.h>\n#include <string.h>\n\n#include \"tachyon/base/logging.h\"\n// #include \"tachyon/base/threading/scoped_blocking_call.h\"\n#include \"tachyon/build/build_config.h\"\n\n#if BUILDFLAG(IS_FUCHSIA)\n#include <fuchsia/io/cpp/fidl.h>\n#include <lib/fdio/directory.h>\n#include <zircon/errors.h>\n\n#include \"tachyon/base/files/scoped_file.h\"\n#endif\n\nnamespace tachyon::base {\nnamespace {\n\nvoid GetStat(const FilePath& path, bool show_links, stat_wrapper_t* st) {\n  DCHECK(st);\n  const int res = show_links ? File::Lstat(path.value().c_str(), st)\n                             : File::Stat(path.value().c_str(), st);\n  if (res < 0) {\n    // Print the stat() error message unless it was ENOENT and we're following\n    // symlinks.\n    DPLOG_IF(ERROR, errno != ENOENT || show_links)\n        << \"Cannot stat '\" << path << \"'\";\n    memset(st, 0, sizeof(*st));\n  }\n}\n\n#if BUILDFLAG(IS_FUCHSIA)\nbool ShouldShowSymLinks(int file_type) {\n  return false;\n}\n#else\nbool ShouldShowSymLinks(int file_type) {\n  return file_type & FileEnumerator::SHOW_SYM_LINKS;\n}\n#endif  // BUILDFLAG(IS_FUCHSIA)\n\n#if BUILDFLAG(IS_FUCHSIA)\nbool ShouldTrackVisitedDirectories(int file_type) {\n  return false;\n}\n#else\nbool ShouldTrackVisitedDirectories(int file_type) {\n  return !(file_type & FileEnumerator::SHOW_SYM_LINKS);\n}\n#endif  // BUILDFLAG(IS_FUCHSIA)\n\n}  // namespace\n\n// FileEnumerator::FileInfo ----------------------------------------------------\n\nFileEnumerator::FileInfo::FileInfo() {\n  memset(&stat_, 0, sizeof(stat_));\n}\n\nbool FileEnumerator::FileInfo::IsDirectory() const {\n  return S_ISDIR(stat_.st_mode);\n}\n\nFilePath FileEnumerator::FileInfo::GetName() const {\n  return filename_;\n}\n\nint64_t FileEnumerator::FileInfo::GetSize() const {\n  return stat_.st_size;\n}\n\nbase::Time FileEnumerator::FileInfo::GetLastModifiedTime() const {\n  return base::Time::FromTimeT(stat_.st_mtime);\n}\n\n// FileEnumerator --------------------------------------------------------------\n\nFileEnumerator::FileEnumerator(const FilePath& root_path,\n                               bool recursive,\n                               int file_type)\n    : FileEnumerator(root_path,\n                     recursive,\n                     file_type,\n                     std::string(),\n                     FolderSearchPolicy::MATCH_ONLY) {}\n\nFileEnumerator::FileEnumerator(const FilePath& root_path,\n                               bool recursive,\n                               int file_type,\n                               const std::string& pattern)\n    : FileEnumerator(root_path,\n                     recursive,\n                     file_type,\n                     pattern,\n                     FolderSearchPolicy::MATCH_ONLY) {}\n\nFileEnumerator::FileEnumerator(const FilePath& root_path,\n                               bool recursive,\n                               int file_type,\n                               const std::string& pattern,\n                               FolderSearchPolicy folder_search_policy)\n    : FileEnumerator(root_path,\n                     recursive,\n                     file_type,\n                     pattern,\n                     folder_search_policy,\n                     ErrorPolicy::IGNORE_ERRORS) {}\n\nFileEnumerator::FileEnumerator(const FilePath& root_path,\n                               bool recursive,\n                               int file_type,\n                               const std::string& pattern,\n                               FolderSearchPolicy folder_search_policy,\n                               ErrorPolicy error_policy)\n    : current_directory_entry_(0),\n      root_path_(root_path),\n      recursive_(recursive),\n      file_type_(file_type),\n      pattern_(pattern),\n      folder_search_policy_(folder_search_policy),\n      error_policy_(error_policy) {\n  // INCLUDE_DOT_DOT must not be specified if recursive.\n  DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));\n\n  if (file_type_ & FileType::NAMES_ONLY) {\n    DCHECK(!recursive_);\n    DCHECK_EQ(file_type_ & ~(FileType::NAMES_ONLY | FileType::INCLUDE_DOT_DOT),\n              0);\n    file_type_ |= (FileType::FILES | FileType::DIRECTORIES);\n  }\n\n  if (recursive && ShouldTrackVisitedDirectories(file_type_)) {\n    stat_wrapper_t st;\n    GetStat(root_path, false, &st);\n    visited_directories_.insert(st.st_ino);\n  }\n\n  pending_paths_.push(root_path);\n}\n\nFileEnumerator::~FileEnumerator() = default;\n\nFilePath FileEnumerator::Next() {\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n\n  ++current_directory_entry_;\n\n  // While we've exhausted the entries in the current directory, do the next\n  while (current_directory_entry_ >= directory_entries_.size()) {\n    if (pending_paths_.empty())\n      return FilePath();\n\n    root_path_ = pending_paths_.top();\n    root_path_ = root_path_.StripTrailingSeparators();\n    pending_paths_.pop();\n\n#if BUILDFLAG(IS_FUCHSIA)\n    // Fuchsia directories can be enumerable without being readable; open\n    // without fuchsia.io/OpenFlags.RIGHT_READABLE to avoid spurious failures.\n    //\n    // TODO(https://crbug.com/1457942): Remove this workaround once opendir no\n    // longer requires READABLE.\n    ScopedFD fd;\n    if (zx_status_t status = fdio_open_fd(\n            root_path_.value().c_str(),\n            static_cast<uint32_t>(fuchsia::io::OpenFlags::DIRECTORY),\n            ScopedFD::Receiver(fd).get());\n        status != ZX_OK) {\n      if (error_policy_ == ErrorPolicy::IGNORE_ERRORS) {\n        continue;\n      }\n      auto status_to_file_error = [](zx_status_t status) {\n        switch (status) {\n          case ZX_ERR_NOT_FOUND:\n            return File::FILE_ERROR_NOT_FOUND;\n          case ZX_ERR_ACCESS_DENIED:\n            return File::FILE_ERROR_ACCESS_DENIED;\n          case ZX_ERR_NOT_DIR:\n            return File::FILE_ERROR_NOT_A_DIRECTORY;\n          default:\n            return File::FILE_ERROR_FAILED;\n        }\n      };\n      error_ = status_to_file_error(status);\n      return FilePath();\n    }\n    DIR* dir = fdopendir(fd.release());\n#else\n    DIR* dir = opendir(root_path_.value().c_str());\n#endif\n    if (!dir) {\n      if (errno == 0 || error_policy_ == ErrorPolicy::IGNORE_ERRORS)\n        continue;\n      error_ = File::OSErrorToFileError(errno);\n      return FilePath();\n    }\n\n    directory_entries_.clear();\n\n#if BUILDFLAG(IS_FUCHSIA)\n    // Fuchsia does not support .. on the file system server side, see\n    // https://fuchsia.googlesource.com/docs/+/master/dotdot.md and\n    // https://crbug.com/735540. However, for UI purposes, having the parent\n    // directory show up in directory listings makes sense, so we add it here to\n    // match the expectation on other operating systems. In cases where this\n    // is useful it should be resolvable locally.\n    FileInfo dotdot;\n    dotdot.stat_.st_mode = S_IFDIR;\n    dotdot.filename_ = FilePath(\"..\");\n    if (!ShouldSkip(dotdot.filename_)) {\n      directory_entries_.push_back(std::move(dotdot));\n    }\n#endif  // BUILDFLAG(IS_FUCHSIA)\n\n    current_directory_entry_ = 0;\n    struct dirent* dent;\n    // NOTE: Per the readdir() documentation, when the end of the directory is\n    // reached with no errors, null is returned and errno is not changed.\n    // Therefore we must reset errno to zero before calling readdir() if we\n    // wish to know whether a null result indicates an error condition.\n    while (errno = 0, dent = readdir(dir)) {\n      FileInfo info;\n      info.filename_ = FilePath(dent->d_name);\n\n      if (ShouldSkip(info.filename_))\n        continue;\n\n      const bool is_pattern_matched = IsPatternMatched(info.filename_);\n\n      // MATCH_ONLY policy enumerates files and directories which matching\n      // pattern only. So we can early skip further checks.\n      if (folder_search_policy_ == FolderSearchPolicy::MATCH_ONLY &&\n          !is_pattern_matched)\n        continue;\n\n      // Do not call OS stat/lstat if there is no sense to do it. If pattern is\n      // not matched (file will not appear in results) and search is not\n      // recursive (possible directory will not be added to pending paths) -\n      // there is no sense to obtain item below.\n      if (!recursive_ && !is_pattern_matched)\n        continue;\n\n      // If the caller only wants the names of files and directories, then\n      // continue without populating `info` further.\n      if (file_type_ & FileType::NAMES_ONLY) {\n        directory_entries_.push_back(std::move(info));\n        continue;\n      }\n\n      const FilePath full_path = root_path_.Append(info.filename_);\n      GetStat(full_path, ShouldShowSymLinks(file_type_), &info.stat_);\n\n      const bool is_dir = info.IsDirectory();\n\n      // Recursive mode: schedule traversal of a directory if either\n      // SHOW_SYM_LINKS is on or we haven't visited the directory yet.\n      if (recursive_ && is_dir &&\n          (!ShouldTrackVisitedDirectories(file_type_) ||\n           visited_directories_.insert(info.stat_.st_ino).second)) {\n        pending_paths_.push(full_path);\n      }\n\n      if (is_pattern_matched && IsTypeMatched(is_dir))\n        directory_entries_.push_back(std::move(info));\n    }\n    int readdir_errno = errno;\n    closedir(dir);\n    if (readdir_errno != 0 && error_policy_ != ErrorPolicy::IGNORE_ERRORS) {\n      error_ = File::OSErrorToFileError(readdir_errno);\n      return FilePath();\n    }\n\n    // MATCH_ONLY policy enumerates files in matched subfolders by \"*\" pattern.\n    // ALL policy enumerates files in all subfolders by origin pattern.\n    if (folder_search_policy_ == FolderSearchPolicy::MATCH_ONLY)\n      pattern_.clear();\n  }\n\n  return root_path_.Append(\n      directory_entries_[current_directory_entry_].filename_);\n}\n\nFileEnumerator::FileInfo FileEnumerator::GetInfo() const {\n  DCHECK(!(file_type_ & FileType::NAMES_ONLY));\n  return directory_entries_[current_directory_entry_];\n}\n\nbool FileEnumerator::IsPatternMatched(const FilePath& path) const {\n  return pattern_.empty() ||\n         !fnmatch(pattern_.c_str(), path.value().c_str(), FNM_NOESCAPE);\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/files/file_enumerator_unittest.cc",
    "content": "// Copyright 2017 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/files/file_enumerator.h\"\n\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/containers/circular_deque.h\"\n#include \"tachyon/base/files/file_path.h\"\n#include \"tachyon/base/files/file_util.h\"\n#include \"tachyon/base/files/scoped_temp_dir.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/build/build_config.h\"\n\nusing testing::ElementsAre;\nusing testing::IsEmpty;\nusing testing::UnorderedElementsAre;\n\nnamespace tachyon::base {\nnamespace {\n\nconst std::string kEmptyPattern;\n\nconst std::vector<FileEnumerator::FolderSearchPolicy> kFolderSearchPolicies{\n    FileEnumerator::FolderSearchPolicy::MATCH_ONLY,\n    FileEnumerator::FolderSearchPolicy::ALL};\n\nstruct TestFile {\n  TestFile(const char* file_name, const char* c)\n      : path(file_name), contents(c) {}\n\n  TestFile(const char* directory,\n           const char* file_name,\n           const char* c)\n      : path(FilePath(directory).Append(file_name)), contents(c) {}\n\n  const FilePath path;\n  const std::string contents;\n  File::Info info;\n  bool found = false;\n};\n\nstruct TestDirectory {\n  explicit TestDirectory(const char* n) : name(n) {}\n  const FilePath name;\n  File::Info info;\n  bool found = false;\n};\n\nvoid CheckModificationTime(const FileEnumerator::FileInfo& actual,\n                           Time expected_last_modified_time) {\n  /*\n  TODO(chokobole):\n#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)\n  // On POSIX, GetLastModifiedTime() rounds down to the second, but\n  // File::GetInfo() does not.\n  Time::Exploded exploded;\n  expected_last_modified_time.UTCExplode(&exploded);\n  exploded.millisecond = 0;\n  EXPECT_TRUE(Time::FromUTCExploded(exploded, &expected_last_modified_time));\n#endif\n  EXPECT_EQ(actual.GetLastModifiedTime(), expected_last_modified_time);\n  */\n}\n\nvoid CheckFileAgainstInfo(const FileEnumerator::FileInfo& actual,\n                          TestFile& expected) {\n  EXPECT_FALSE(expected.found)\n      << \"Got \" << expected.path.BaseName().value() << \" twice\";\n  expected.found = true;\n  EXPECT_EQ(actual.GetSize(), int64_t(expected.contents.size()));\n  CheckModificationTime(actual, expected.info.last_modified);\n}\n\nvoid CheckDirectoryAgainstInfo(const FileEnumerator::FileInfo& actual,\n                               TestDirectory& expected) {\n  EXPECT_FALSE(expected.found) << \"Got \" << expected.name.value() << \" twice\";\n  expected.found = true;\n  CheckModificationTime(actual, expected.info.last_modified);\n}\n\ncircular_deque<FilePath> RunEnumerator(\n    const FilePath& root_path,\n    bool recursive,\n    int file_type,\n    const std::string& pattern,\n    FileEnumerator::FolderSearchPolicy folder_search_policy) {\n  circular_deque<FilePath> rv;\n  FileEnumerator enumerator(root_path, recursive, file_type, pattern,\n                            folder_search_policy,\n                            FileEnumerator::ErrorPolicy::IGNORE_ERRORS);\n  for (auto file = enumerator.Next(); !file.empty(); file = enumerator.Next())\n    rv.emplace_back(std::move(file));\n  return rv;\n}\n\nbool CreateDummyFile(const FilePath& path) {\n  return WriteFile(path, \"42\", sizeof(\"42\")) == sizeof(\"42\");\n}\n\nbool GetFileInfo(const FilePath& file_path, File::Info& info) {\n  // FLAG_WIN_BACKUP_SEMANTICS: Needed to open directories on Windows.\n  File f(file_path,\n         File::FLAG_OPEN | File::FLAG_READ | File::FLAG_WIN_BACKUP_SEMANTICS);\n  if (!f.IsValid()) {\n    LOG(ERROR) << \"Could not open \" << file_path.value() << \": \"\n               << File::ErrorToString(f.error_details());\n    return false;\n  }\n  if (!f.GetInfo(&info)) {\n    std::string last_error = File::ErrorToString(File::GetLastFileError());\n    LOG(ERROR) << \"Could not get info about \" << file_path.value() << \": \"\n               << last_error;\n    return false;\n  }\n\n  return true;\n}\n\nvoid SetUpTestFiles(const ScopedTempDir& temp_dir,\n                    std::vector<TestFile>& files) {\n  for (TestFile& file : files) {\n    const FilePath file_path = temp_dir.GetPath().Append(file.path);\n    ASSERT_TRUE(WriteFile(file_path, file.contents));\n    ASSERT_TRUE(GetFileInfo(file_path, file.info));\n  }\n}\n\n}  // namespace\n\nTEST(FileEnumerator, NotExistingPath) {\n  const FilePath path = FilePath(\"some_not_existing_path\");\n  ASSERT_FALSE(PathExists(path));\n\n  for (auto policy : kFolderSearchPolicies) {\n    const auto files = RunEnumerator(\n        path, true, FileEnumerator::FILES | FileEnumerator::DIRECTORIES,\n        \"\", policy);\n    EXPECT_THAT(files, IsEmpty());\n  }\n}\n\nTEST(FileEnumerator, EmptyFolder) {\n  ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n\n  for (auto policy : kFolderSearchPolicies) {\n    const auto files =\n        RunEnumerator(temp_dir.GetPath(), true,\n                      FileEnumerator::FILES | FileEnumerator::DIRECTORIES,\n                      kEmptyPattern, policy);\n    EXPECT_THAT(files, IsEmpty());\n  }\n}\n\nTEST(FileEnumerator, SingleFileInFolderForFileSearch) {\n  ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n\n  const FilePath& path = temp_dir.GetPath();\n  const FilePath file = path.Append(\"test.txt\");\n  ASSERT_TRUE(CreateDummyFile(file));\n\n  for (auto policy : kFolderSearchPolicies) {\n    const auto files = RunEnumerator(\n        temp_dir.GetPath(), true, FileEnumerator::FILES, kEmptyPattern, policy);\n    EXPECT_THAT(files, ElementsAre(file));\n  }\n}\n\nTEST(FileEnumerator, SingleFileInFolderForDirSearch) {\n  ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n\n  const FilePath& path = temp_dir.GetPath();\n  ASSERT_TRUE(CreateDummyFile(path.Append(\"test.txt\")));\n\n  for (auto policy : kFolderSearchPolicies) {\n    const auto files = RunEnumerator(path, true, FileEnumerator::DIRECTORIES,\n                                     kEmptyPattern, policy);\n    EXPECT_THAT(files, IsEmpty());\n  }\n}\n\nTEST(FileEnumerator, SingleFileInFolderWithFiltering) {\n  ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n\n  const FilePath& path = temp_dir.GetPath();\n  const FilePath file = path.Append(\"test.txt\");\n  ASSERT_TRUE(CreateDummyFile(file));\n\n  for (auto policy : kFolderSearchPolicies) {\n    auto files = RunEnumerator(path, true, FileEnumerator::FILES,\n                               \"*.txt\", policy);\n    EXPECT_THAT(files, ElementsAre(file));\n\n    files = RunEnumerator(path, true, FileEnumerator::FILES,\n                          \"*.pdf\", policy);\n    EXPECT_THAT(files, IsEmpty());\n  }\n}\n\nTEST(FileEnumerator, TwoFilesInFolder) {\n  ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n\n  const FilePath& path = temp_dir.GetPath();\n  const FilePath foo_txt = path.Append(\"foo.txt\");\n  const FilePath bar_txt = path.Append(\"bar.txt\");\n  ASSERT_TRUE(CreateDummyFile(foo_txt));\n  ASSERT_TRUE(CreateDummyFile(bar_txt));\n\n  for (auto policy : kFolderSearchPolicies) {\n    auto files = RunEnumerator(path, true, FileEnumerator::FILES,\n                               \"*.txt\", policy);\n    EXPECT_THAT(files, UnorderedElementsAre(foo_txt, bar_txt));\n\n    files = RunEnumerator(path, true, FileEnumerator::FILES,\n                          \"foo*\", policy);\n    EXPECT_THAT(files, ElementsAre(foo_txt));\n\n    files = RunEnumerator(path, true, FileEnumerator::FILES,\n                          \"*.pdf\", policy);\n    EXPECT_THAT(files, IsEmpty());\n\n    files =\n        RunEnumerator(path, true, FileEnumerator::FILES, kEmptyPattern, policy);\n    EXPECT_THAT(files, UnorderedElementsAre(foo_txt, bar_txt));\n  }\n}\n\nTEST(FileEnumerator, SingleFolderInFolderForFileSearch) {\n  ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n\n  const FilePath& path = temp_dir.GetPath();\n\n  ScopedTempDir temp_subdir;\n  ASSERT_TRUE(temp_subdir.CreateUniqueTempDirUnderPath(path));\n\n  for (auto policy : kFolderSearchPolicies) {\n    const auto files =\n        RunEnumerator(path, true, FileEnumerator::FILES, kEmptyPattern, policy);\n    EXPECT_THAT(files, IsEmpty());\n  }\n}\n\nTEST(FileEnumerator, SingleFolderInFolderForDirSearch) {\n  ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n\n  const FilePath& path = temp_dir.GetPath();\n\n  ScopedTempDir temp_subdir;\n  ASSERT_TRUE(temp_subdir.CreateUniqueTempDirUnderPath(path));\n\n  for (auto policy : kFolderSearchPolicies) {\n    const auto files = RunEnumerator(path, true, FileEnumerator::DIRECTORIES,\n                                     kEmptyPattern, policy);\n    EXPECT_THAT(files, ElementsAre(temp_subdir.GetPath()));\n  }\n}\n\nTEST(FileEnumerator, TwoFoldersInFolder) {\n  ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n\n  const FilePath& path = temp_dir.GetPath();\n\n  const FilePath subdir_foo = path.Append(\"foo\");\n  const FilePath subdir_bar = path.Append(\"bar\");\n  ASSERT_TRUE(CreateDirectory(subdir_foo));\n  ASSERT_TRUE(CreateDirectory(subdir_bar));\n\n  for (auto policy : kFolderSearchPolicies) {\n    auto files = RunEnumerator(path, true, FileEnumerator::DIRECTORIES,\n                               kEmptyPattern, policy);\n    EXPECT_THAT(files, UnorderedElementsAre(subdir_foo, subdir_bar));\n\n    files = RunEnumerator(path, true, FileEnumerator::DIRECTORIES,\n                          \"foo\", policy);\n    EXPECT_THAT(files, ElementsAre(subdir_foo));\n  }\n}\n\nTEST(FileEnumerator, FolderAndFileInFolder) {\n  ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n\n  const FilePath& path = temp_dir.GetPath();\n\n  ScopedTempDir temp_subdir;\n  ASSERT_TRUE(temp_subdir.CreateUniqueTempDirUnderPath(path));\n  const FilePath file = path.Append(\"test.txt\");\n  ASSERT_TRUE(CreateDummyFile(file));\n\n  for (auto policy : kFolderSearchPolicies) {\n    auto files =\n        RunEnumerator(path, true, FileEnumerator::FILES, kEmptyPattern, policy);\n    EXPECT_THAT(files, ElementsAre(file));\n\n    files = RunEnumerator(path, true, FileEnumerator::DIRECTORIES,\n                          kEmptyPattern, policy);\n    EXPECT_THAT(files, ElementsAre(temp_subdir.GetPath()));\n\n    files = RunEnumerator(path, true,\n                          FileEnumerator::FILES | FileEnumerator::DIRECTORIES,\n                          kEmptyPattern, policy);\n    EXPECT_THAT(files, UnorderedElementsAre(file, temp_subdir.GetPath()));\n  }\n}\n\nTEST(FileEnumerator, FilesInParentFolderAlwaysFirst) {\n  ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n\n  const FilePath& path = temp_dir.GetPath();\n\n  ScopedTempDir temp_subdir;\n  ASSERT_TRUE(temp_subdir.CreateUniqueTempDirUnderPath(path));\n  const FilePath foo_txt = path.Append(\"foo.txt\");\n  const FilePath bar_txt = temp_subdir.GetPath().Append(\"bar.txt\");\n  ASSERT_TRUE(CreateDummyFile(foo_txt));\n  ASSERT_TRUE(CreateDummyFile(bar_txt));\n\n  for (auto policy : kFolderSearchPolicies) {\n    const auto files =\n        RunEnumerator(path, true, FileEnumerator::FILES, kEmptyPattern, policy);\n    EXPECT_THAT(files, ElementsAre(foo_txt, bar_txt));\n  }\n}\n\nTEST(FileEnumerator, FileInSubfolder) {\n  ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n\n  const FilePath subdir = temp_dir.GetPath().Append(\"subdir\");\n  ASSERT_TRUE(CreateDirectory(subdir));\n\n  const FilePath file = subdir.Append(\"test.txt\");\n  ASSERT_TRUE(CreateDummyFile(file));\n\n  for (auto policy : kFolderSearchPolicies) {\n    auto files = RunEnumerator(temp_dir.GetPath(), true, FileEnumerator::FILES,\n                               kEmptyPattern, policy);\n    EXPECT_THAT(files, ElementsAre(file));\n\n    files = RunEnumerator(temp_dir.GetPath(), false, FileEnumerator::FILES,\n                          kEmptyPattern, policy);\n    EXPECT_THAT(files, IsEmpty());\n  }\n}\n\nTEST(FileEnumerator, FilesInSubfoldersWithFiltering) {\n  ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n\n  const FilePath test_txt = temp_dir.GetPath().Append(\"test.txt\");\n  const FilePath subdir_foo = temp_dir.GetPath().Append(\"foo_subdir\");\n  const FilePath subdir_bar = temp_dir.GetPath().Append(\"bar_subdir\");\n  const FilePath foo_test = subdir_foo.Append(\"test.txt\");\n  const FilePath foo_foo = subdir_foo.Append(\"foo.txt\");\n  const FilePath foo_bar = subdir_foo.Append(\"bar.txt\");\n  const FilePath bar_test = subdir_bar.Append(\"test.txt\");\n  const FilePath bar_foo = subdir_bar.Append(\"foo.txt\");\n  const FilePath bar_bar = subdir_bar.Append(\"bar.txt\");\n  ASSERT_TRUE(CreateDummyFile(test_txt));\n  ASSERT_TRUE(CreateDirectory(subdir_foo));\n  ASSERT_TRUE(CreateDirectory(subdir_bar));\n  ASSERT_TRUE(CreateDummyFile(foo_test));\n  ASSERT_TRUE(CreateDummyFile(foo_foo));\n  ASSERT_TRUE(CreateDummyFile(foo_bar));\n  ASSERT_TRUE(CreateDummyFile(bar_test));\n  ASSERT_TRUE(CreateDummyFile(bar_foo));\n  ASSERT_TRUE(CreateDummyFile(bar_bar));\n\n  auto files =\n      RunEnumerator(temp_dir.GetPath(), true,\n                    FileEnumerator::FILES | FileEnumerator::DIRECTORIES,\n                    \"foo*\",\n                    FileEnumerator::FolderSearchPolicy::MATCH_ONLY);\n  EXPECT_THAT(files,\n              UnorderedElementsAre(subdir_foo, foo_test, foo_foo, foo_bar));\n\n  files = RunEnumerator(temp_dir.GetPath(), true,\n                        FileEnumerator::FILES | FileEnumerator::DIRECTORIES,\n                        \"foo*\",\n                        FileEnumerator::FolderSearchPolicy::ALL);\n  EXPECT_THAT(files, UnorderedElementsAre(subdir_foo, foo_foo, bar_foo));\n}\n\nTEST(FileEnumerator, InvalidDirectory) {\n  ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n\n  const FilePath test_file = temp_dir.GetPath().Append(\"test_file\");\n  ASSERT_TRUE(CreateDummyFile(test_file));\n\n  // Attempt to enumerate entries at a regular file path.\n  FileEnumerator enumerator(test_file, /*recursive=*/true,\n                            FileEnumerator::FILES, kEmptyPattern,\n                            FileEnumerator::FolderSearchPolicy::ALL,\n                            FileEnumerator::ErrorPolicy::STOP_ENUMERATION);\n  FilePath path = enumerator.Next();\n  EXPECT_TRUE(path.empty());\n\n  // Slightly different outcomes between Windows and POSIX.\n#if BUILDFLAG(IS_WIN)\n  EXPECT_EQ(File::Error::FILE_ERROR_FAILED, enumerator.GetError());\n#else\n  EXPECT_EQ(File::Error::FILE_ERROR_NOT_A_DIRECTORY, enumerator.GetError());\n#endif\n}\n\n#if BUILDFLAG(IS_POSIX)\nTEST(FileEnumerator, SymLinkLoops) {\n  ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n\n  const FilePath subdir = temp_dir.GetPath().Append(\"subdir\");\n  ASSERT_TRUE(CreateDirectory(subdir));\n\n  const FilePath file = subdir.Append(\"test.txt\");\n  ASSERT_TRUE(CreateDummyFile(file));\n\n  const FilePath link = subdir.Append(\"link\");\n  ASSERT_TRUE(CreateSymbolicLink(temp_dir.GetPath(), link));\n\n  auto files = RunEnumerator(\n      temp_dir.GetPath(), true,\n      FileEnumerator::FILES | FileEnumerator::DIRECTORIES, kEmptyPattern,\n      FileEnumerator::FolderSearchPolicy::MATCH_ONLY);\n\n  EXPECT_THAT(files, UnorderedElementsAre(subdir, link, file));\n\n  files = RunEnumerator(subdir, true,\n                        FileEnumerator::FILES | FileEnumerator::DIRECTORIES |\n                            FileEnumerator::SHOW_SYM_LINKS,\n                        kEmptyPattern,\n                        FileEnumerator::FolderSearchPolicy::MATCH_ONLY);\n\n  EXPECT_THAT(files, UnorderedElementsAre(link, file));\n}\n#endif\n\n// Test FileEnumerator::GetInfo() on some files and ensure all the returned\n// information is correct.\nTEST(FileEnumerator, GetInfo) {\n  ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n\n  std::vector<TestFile> files = {\n      TestFile(\"file1\", \"First\"),\n      TestFile(\"file2\", \"Second\"),\n      TestFile(\"file3\", \"Third-third-third\")};\n  SetUpTestFiles(temp_dir, files);\n\n  FileEnumerator file_enumerator(temp_dir.GetPath(), false,\n                                 FileEnumerator::FILES);\n  while (!file_enumerator.Next().empty()) {\n    auto info = file_enumerator.GetInfo();\n    bool found = false;\n    for (TestFile& file : files) {\n      if (info.GetName() == file.path.BaseName()) {\n        CheckFileAgainstInfo(info, file);\n        found = true;\n        break;\n      }\n    }\n\n    EXPECT_TRUE(found) << \"Got unexpected result \" << info.GetName().value();\n  }\n\n  for (const TestFile& file : files) {\n    EXPECT_TRUE(file.found)\n        << \"File \" << file.path.value() << \" was not returned\";\n  }\n}\n\n// Test that FileEnumerator::GetInfo() works when searching recursively. It also\n// tests that it returns the correct information about directories.\nTEST(FileEnumerator, GetInfoRecursive) {\n  ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n\n  TestDirectory directories[] = {TestDirectory(\"dir1\"),\n                                 TestDirectory(\"dir2\"),\n                                 TestDirectory(\"dir3\"),\n                                 TestDirectory(\"dirempty\")};\n\n  for (const TestDirectory& dir : directories) {\n    const FilePath dir_path = temp_dir.GetPath().Append(dir.name);\n    ASSERT_TRUE(CreateDirectory(dir_path));\n  }\n\n  std::vector<TestFile> files = {\n      TestFile(\"dir1\", \"file1\", \"First\"),\n      TestFile(\"dir1\", \"file2\", \"Second\"),\n      TestFile(\"dir2\", \"fileA\", \"Third-third-3\"),\n      TestFile(\"dir3\", \".file\", \"Dot\")};\n  SetUpTestFiles(temp_dir, files);\n\n  // Get last-modification times for directories. Must be done after we create\n  // all the files.\n  for (TestDirectory& dir : directories) {\n    const FilePath dir_path = temp_dir.GetPath().Append(dir.name);\n    ASSERT_TRUE(GetFileInfo(dir_path, dir.info));\n  }\n\n  FileEnumerator file_enumerator(\n      temp_dir.GetPath(), true,\n      FileEnumerator::FILES | FileEnumerator::DIRECTORIES);\n  while (!file_enumerator.Next().empty()) {\n    auto info = file_enumerator.GetInfo();\n    bool found = false;\n    if (info.IsDirectory()) {\n      for (TestDirectory& dir : directories) {\n        if (info.GetName() == dir.name) {\n          CheckDirectoryAgainstInfo(info, dir);\n          found = true;\n          break;\n        }\n      }\n    } else {\n      for (TestFile& file : files) {\n        if (info.GetName() == file.path.BaseName()) {\n          CheckFileAgainstInfo(info, file);\n          found = true;\n          break;\n        }\n      }\n    }\n\n    EXPECT_TRUE(found) << \"Got unexpected result \" << info.GetName().value();\n  }\n\n  for (const TestDirectory& dir : directories) {\n    EXPECT_TRUE(dir.found) << \"Directory \" << dir.name.value()\n                           << \" was not returned\";\n  }\n  for (const TestFile& file : files) {\n    EXPECT_TRUE(file.found)\n        << \"File \" << file.path.value() << \" was not returned\";\n  }\n}\n\n#if BUILDFLAG(IS_FUCHSIA)\n// FileEnumerator::GetInfo does not work correctly with INCLUDE_DOT_DOT.\n// https://crbug.com/1106172\n#elif BUILDFLAG(IS_WIN)\n// Windows has a bug in their handling of \"..\"; they always report the file\n// modification time of the current directory, not the parent directory. This is\n// a bug in Windows, not us -- you can see it with the \"dir\" command (notice\n// that the time of . and .. always match). Skip this test.\n// https://crbug.com/1119546\n#else\n// Tests that FileEnumerator::GetInfo() returns the correct info for the ..\n// directory.\nTEST(FileEnumerator, GetInfoDotDot) {\n  ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n\n  const char kSubdir[] = \"subdir\";\n  const FilePath subdir_path = temp_dir.GetPath().Append(kSubdir);\n  ASSERT_TRUE(CreateDirectory(subdir_path));\n\n  std::vector<TestFile> files = {\n      TestFile(kSubdir, \"file1\", \"First\"),\n      TestFile(kSubdir, \"file2\", \"Second\"),\n      TestFile(kSubdir, \"file3\", \"Third-third-third\")};\n  SetUpTestFiles(temp_dir, files);\n\n  TestDirectory dotdot(\"..\");\n  // test_dir/subdir/.. is just test_dir.\n  ASSERT_TRUE(GetFileInfo(temp_dir.GetPath(), dotdot.info));\n\n  FileEnumerator file_enumerator(subdir_path, false,\n                                 FileEnumerator::FILES |\n                                     FileEnumerator::DIRECTORIES |\n                                     FileEnumerator::INCLUDE_DOT_DOT);\n  while (!file_enumerator.Next().empty()) {\n    auto info = file_enumerator.GetInfo();\n    bool found = false;\n    if (info.IsDirectory()) {\n      EXPECT_EQ(info.GetName(), FilePath(\"..\"));\n      CheckDirectoryAgainstInfo(info, dotdot);\n      found = true;\n    } else {\n      for (TestFile& file : files) {\n        if (info.GetName() == file.path.BaseName()) {\n          CheckFileAgainstInfo(info, file);\n          found = true;\n          break;\n        }\n      }\n    }\n\n    EXPECT_TRUE(found) << \"Got unexpected result \" << info.GetName().value();\n  }\n\n  EXPECT_TRUE(dotdot.found) << \"Directory .. was not returned\";\n\n  for (const TestFile& file : files) {\n    EXPECT_TRUE(file.found)\n        << \"File \" << file.path.value() << \" was not returned\";\n  }\n}\n#endif  // !BUILDFLAG(IS_FUCHSIA) && !BUILDFLAG(IS_WIN)\n\nTEST(FileEnumerator, OnlyName) {\n  ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n\n  const FilePath& path = temp_dir.GetPath();\n\n  // Add a directory and a file.\n  ScopedTempDir temp_subdir;\n  ASSERT_TRUE(temp_subdir.CreateUniqueTempDirUnderPath(path));\n  const FilePath& subdir = temp_subdir.GetPath();\n  const FilePath dummy_file = path.Append(\"a_file.txt\");\n  ASSERT_TRUE(CreateDummyFile(dummy_file));\n\n  auto found_paths = RunEnumerator(\n      path, /*recursive=*/false, FileEnumerator::FileType::NAMES_ONLY,\n      std::string(), FileEnumerator::FolderSearchPolicy::MATCH_ONLY);\n  EXPECT_THAT(found_paths, UnorderedElementsAre(subdir, dummy_file));\n}\n\nTEST(FileEnumerator, ForEach) {\n  ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n  const FilePath mock_path(temp_dir.GetPath());\n\n  // Create a top-level directory, and 3 sub-directories, with 2 files within\n  // each directory.\n  for (const FilePath& path :\n       {mock_path, mock_path.Append(\"1.2.3.4\"),\n        mock_path.Append(\"Download\"),\n        mock_path.Append(\"Install\")}) {\n    ASSERT_TRUE(CreateDirectory(path));\n    for (std::string_view file_name : {\"mock.executable\", \"mock.text\"}) {\n      ASSERT_TRUE(\n          File(path.Append(file_name), File::FLAG_CREATE | File::FLAG_WRITE)\n              .IsValid());\n    }\n  }\n\n  const struct {\n    bool recursive;\n    int file_type;\n    const int expected_invocation_count;\n  } test_cases[] = {\n      {false, FileEnumerator::FILES, 2},\n      {true, FileEnumerator::FILES, 8},\n      {false, FileEnumerator::DIRECTORIES, 3},\n      {true, FileEnumerator::DIRECTORIES, 3},\n      {false, FileEnumerator::FILES | FileEnumerator::DIRECTORIES, 5},\n      {true, FileEnumerator::FILES | FileEnumerator::DIRECTORIES, 11},\n  };\n\n  for (const auto& test_case : test_cases) {\n    int invocation_count = 0;\n\n    FileEnumerator(mock_path, test_case.recursive, test_case.file_type)\n        .ForEach([&invocation_count, &test_case](const FilePath& item) {\n          ++invocation_count;\n          if (invocation_count > test_case.expected_invocation_count) {\n            ADD_FAILURE() << \"Unexpected file/directory found: \" << item << \": \"\n                          << invocation_count << \": \"\n                          << test_case.expected_invocation_count;\n          }\n        });\n\n    EXPECT_EQ(invocation_count, test_case.expected_invocation_count);\n  }\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/files/file_path.cc",
    "content": "// Copyright (c) 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/files/file_path.h\"\n\n#include \"absl/strings/str_cat.h\"\n#include \"absl/strings/str_join.h\"\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/strings/string_util.h\"\n#include \"tachyon/build/build_config.h\"\n\nnamespace tachyon::base {\nnamespace {\n\nconst char* const kCommonDoubleExtensionSuffixes[] = {\n    \"bz\", \"bz2\", \"gz\", \"lz\", \"lzma\", \"lzo\", \"xz\", \"z\", \"zst\"};\n\nbool AreAllSeparators(const std::string& input) {\n  for (char it : input) {\n    if (!FilePath::IsSeparator(it)) return false;\n  }\n\n  return true;\n}\n\n// Find the position of the '.' that separates the extension from the rest\n// of the file name. The position is relative to BaseName(), not value().\n// Returns npos if it can't find an extension.\nstd::string::size_type FinalExtensionSeparatorPosition(const std::string& path) {\n  // Special case \".\" and \"..\"\n  if (path == FilePath::kCurrentDirectory || path == FilePath::kParentDirectory)\n    return std::string::npos;\n\n  return path.rfind(FilePath::kExtensionSeparator);\n}\n\n// Same as above, but allow a second extension component of up to 4\n// characters when the rightmost extension component is a common double\n// extension (gz, bz2, Z).  For example, foo.tar.gz or foo.tar.Z would have\n// extension components of '.tar.gz' and '.tar.Z' respectively.\nstd::string::size_type ExtensionSeparatorPosition(const std::string& path) {\n  const std::string::size_type last_dot = FinalExtensionSeparatorPosition(path);\n\n  // No extension, or the extension is the whole filename.\n  if (last_dot == std::string::npos || last_dot == 0U)\n    return last_dot;\n\n  const std::string::size_type penultimate_dot =\n      path.rfind(FilePath::kExtensionSeparator, last_dot - 1);\n  const std::string::size_type last_separator =\n      path.find_last_of(FilePath::kSeparators, last_dot - 1,\n                        FilePath::kSeparatorsLength - 1);\n\n  if (penultimate_dot == std::string::npos ||\n      (last_separator != std::string::npos &&\n       penultimate_dot < last_separator)) {\n    return last_dot;\n  }\n\n  std::string extension(path, last_dot + 1);\n  for (auto* i : kCommonDoubleExtensionSuffixes) {\n    if (EqualsCaseInsensitiveASCII(extension, i)) {\n      if ((last_dot - penultimate_dot) <= 5U &&\n          (last_dot - penultimate_dot) > 1U) {\n        return penultimate_dot;\n      }\n    }\n  }\n\n  return last_dot;\n}\n\n// Returns true if path is \"\", \".\", or \"..\".\nbool IsEmptyOrSpecialCase(std::string_view path) {\n  // Special cases \"\", \".\", and \"..\"\n  if (path.empty() || path == FilePath::kCurrentDirectory ||\n      path == FilePath::kParentDirectory) {\n    return true;\n  }\n\n  return false;\n}\n\n}  // namespace\n\nFilePath::FilePath() = default;\n\nFilePath::FilePath(std::string_view path) : path_(std::string(path)) {}\n\nFilePath::FilePath(const FilePath& other) = default;\nFilePath& FilePath::operator=(const FilePath& other) = default;\n\nFilePath::FilePath(FilePath&& other) noexcept = default;\nFilePath& FilePath::operator=(FilePath&& other) noexcept = default;\n\nFilePath::~FilePath() = default;\n\nbool FilePath::IsRoot() const {\n  return path_.length() > 0 && AreAllSeparators(path_);\n}\n\n// static\nbool FilePath::IsSeparator(char character) {\n  for (size_t i = 0; i < kSeparatorsLength - 1; ++i) {\n    if (character == kSeparators[i]) {\n      return true;\n    }\n  }\n\n  return false;\n}\n\nstd::vector<std::string> FilePath::GetComponents() const {\n  if (value().empty()) return {};\n\n  std::vector<std::string> ret_val;\n  FilePath current = *this;\n  FilePath base;\n\n  // Capture path components.\n  while (current != current.DirName()) {\n    base = current.BaseName();\n    if (!AreAllSeparators(base.value())) ret_val.push_back(base.value());\n    current = current.DirName();\n  }\n\n  // Capture root, if any.\n  if (current.IsRoot()) ret_val.push_back(FilePath::kRootPath);\n\n  return {ret_val.rbegin(), ret_val.rend()};\n}\n\nbool FilePath::IsParent(const FilePath& child) const {\n  return AppendRelativePath(child, nullptr);\n}\n\nbool FilePath::AppendRelativePath(const FilePath& child, FilePath* path) const {\n  std::vector<std::string> parent_components = GetComponents();\n  std::vector<std::string> child_components = child.GetComponents();\n\n  if (parent_components.empty() ||\n      parent_components.size() >= child_components.size())\n    return false;\n\n  std::vector<std::string>::const_iterator parent_comp =\n      parent_components.begin();\n  std::vector<std::string>::const_iterator child_comp =\n      child_components.begin();\n\n  while (parent_comp != parent_components.end()) {\n    if (*parent_comp != *child_comp) return false;\n    ++parent_comp;\n    ++child_comp;\n  }\n\n  if (path) {\n    *path = path->Append(FilePath(\n        absl::StrJoin(child_comp, child_components.cend(), kSeparators)));\n  }\n  return true;\n}\n\nFilePath FilePath::DirName() const {\n  std::string_view p = path_;\n  size_t last_separator = p.find_last_of(kSeparators, std::string::npos);\n  if (last_separator == std::string::npos) {\n    return FilePath();\n  } else if (last_separator == 0) {\n    // root directory\n    return FilePath(kRootPath);\n  } else {\n    return FilePath(p.substr(0, last_separator));\n  }\n}\n\nFilePath FilePath::BaseName() const {\n  std::string_view p = path_;\n  size_t last_separator = p.find_last_of(kSeparators, std::string::npos);\n  if (last_separator == std::string::npos) {\n    return FilePath(path_);\n  }\n\n  return FilePath(\n      p.substr(last_separator + 1, p.length() - last_separator - 1));\n}\n\nstd::string FilePath::Extension() const {\n  FilePath base(BaseName());\n  std::string_view p = base.value();\n  size_t last_separator =\n      p.find_last_of(kExtensionSeparator, std::string::npos);\n  if (last_separator == std::string::npos) return EmptyString();\n  return std::string(p.substr(last_separator, p.length() - last_separator));\n}\n\nstd::string FilePath::FinalExtension() const {\n  FilePath base(BaseName());\n  const std::string::size_type dot = FinalExtensionSeparatorPosition(base.path_);\n  if (dot == std::string::npos)\n    return std::string();\n\n  return base.path_.substr(dot, std::string::npos);\n}\n\nFilePath FilePath::RemoveExtension() const {\n  if (Extension().empty())\n    return *this;\n\n  const std::string::size_type dot = ExtensionSeparatorPosition(path_);\n  if (dot == std::string::npos)\n    return *this;\n\n  return FilePath(path_.substr(0, dot));\n}\n\nFilePath FilePath::RemoveFinalExtension() const {\n  if (FinalExtension().empty())\n    return *this;\n\n  const std::string::size_type dot = FinalExtensionSeparatorPosition(path_);\n  if (dot == std::string::npos)\n    return *this;\n\n  return FilePath(path_.substr(0, dot));\n}\n\nFilePath FilePath::InsertBeforeExtension(std::string_view suffix) const {\n  if (suffix.empty())\n    return FilePath(path_);\n\n  if (IsEmptyOrSpecialCase(BaseName().value()))\n    return FilePath();\n\n  return FilePath(\n      absl::StrCat(RemoveExtension().value(), suffix, Extension()));\n}\n\nFilePath FilePath::Append(std::string_view component) const {\n  return Append(FilePath(component));\n}\n\nFilePath FilePath::Append(const FilePath& component) const {\n  if (component.IsAbsolute()) return FilePath(EmptyString());\n  if (path_.empty()) return component;\n  if (EndsWithSeparator()) {\n    return FilePath(absl::StrCat(path_, component.value()));\n  }\n  return FilePath(absl::StrJoin({path_, component.value()}, kSeparators));\n}\n\nFilePath FilePath::operator/(std::string_view component) const {\n  return Append(component);\n}\n\nFilePath FilePath::operator/(const FilePath& component) const {\n  return Append(component);\n}\n\nFilePath& FilePath::operator/=(std::string_view component) {\n  *this = Append(component);\n  return *this;\n}\n\nFilePath& FilePath::operator/=(const FilePath& component) {\n  *this = Append(component);\n  return *this;\n}\n\nbool FilePath::IsAbsolute() const {\n  return path_.length() > 0 && FilePath::IsSeparator(path_[0]);\n}\n\nbool FilePath::EndsWithSeparator() const {\n  if (path_.empty()) return false;\n  return IsSeparator(path_.back());\n}\n\nFilePath FilePath::AsEndingWithSeparator() const {\n  if (EndsWithSeparator() || path_.empty()) return *this;\n\n  std::string path_str;\n  path_str.reserve(path_.length() + 1);  // Only allocate string once.\n  path_str = path_;\n  path_str.push_back(kSeparators[0]);\n  return FilePath(path_str);\n}\n\nFilePath FilePath::StripTrailingSeparators() const {\n  FilePath new_path(path_);\n  new_path.StripTrailingSeparatorsInternal();\n\n  return new_path;\n}\n\nvoid FilePath::StripTrailingSeparatorsInternal() {\n  size_t pos = path_.find_last_not_of(kSeparators);\n  if (pos == std::string::npos) {\n    path_.resize(1);\n  } else {\n    path_.resize(pos + 1);\n  }\n}\n\nbool FilePath::ReferencesParent() const {\n  return path_.find(kParentDirectory) != std::string::npos;\n}\n\nstd::ostream& operator<<(std::ostream& out, const FilePath& file_path) {\n  out << file_path.value();\n  return out;\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/files/file_path.h",
    "content": "// Copyright (c) 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_FILES_FILE_PATH_H_\n#define TACHYON_BASE_FILES_FILE_PATH_H_\n\n#include <ostream>\n#include <string>\n#include <string_view>\n#include <vector>\n\n#include \"tachyon/export.h\"\n\nnamespace tachyon::base {\n\n// This is a class represents file path inside tachyon projects.\n// CAUTION: On linux, unlike your expectation, it's not utf-8 encoded, encoding\n// is dependent to locale.\n// NOTE: please do not use std::string directly!\nclass TACHYON_EXPORT FilePath {\n public:\n  // Null-terminated array of separators used to separate components in paths.\n  // Each character in this array is a valid separator, but kSeparators[0] is\n  // treated as the canonical separator and is used when composing pathnames.\n  static constexpr char kSeparators[] = \"/\";\n\n  // std::size(kSeparators), i.e., the number of separators in kSeparators plus\n  // one (the null terminator at the end of kSeparators).\n  static constexpr size_t kSeparatorsLength = std::size(kSeparators);\n\n  // The special path component meaning \"this directory.\"\n  static constexpr char kCurrentDirectory[] = \".\";\n\n  // The special path component meaning \"the parent directory.\"\n  static constexpr char kParentDirectory[] = \"..\";\n\n  // The character used to identify a file extension.\n  static constexpr char kExtensionSeparator = '.';\n\n  // The special path to identify a root.\n  static constexpr char kRootPath[] = \"/\";\n\n  FilePath();\n  explicit FilePath(std::string_view path);\n\n  FilePath(const FilePath& other);\n  FilePath& operator=(const FilePath& other);\n\n  FilePath(FilePath&& other) noexcept;\n  FilePath& operator=(FilePath&& other) noexcept;\n\n  ~FilePath();\n\n  bool operator==(const FilePath& other) const { return path_ == other.path_; }\n  bool operator!=(const FilePath& other) const { return path_ != other.path_; }\n\n  bool operator<(const FilePath& other) const { return path_ < other.path_; }\n\n  const std::string& value() const { return path_; }\n\n  bool empty() const { return path_.empty(); }\n\n  void clear() { path_.clear(); }\n\n  // Returns true if |character| is in kSeparators.\n  static bool IsSeparator(char character);\n\n  // Returns true if |path_| references to root. It also returns true |path_|\n  // consists of '/';\n  bool IsRoot() const;\n\n  // Returns a vector of all of the components of the provided path. It is\n  // equivalent to calling DirName().value() on the path's root component,\n  // and BaseName().value() on each child component.\n  //\n  // To make sure this is lossless so we can differentiate absolute and\n  // relative paths, the root slash will be included even though no other\n  // slashes will be. The precise behavior is:\n  //\n  // Posix:  \"/foo/bar\"  ->  [ \"/\", \"foo\", \"bar\" ]\n  // Windows:  \"C:\\foo\\bar\"  ->  [ \"C:\", \"\\\\\", \"foo\", \"bar\" ]\n  std::vector<std::string> GetComponents() const;\n\n  // Returns true if this FilePath is a parent or ancestor of the |child|.\n  // Absolute and relative paths are accepted i.e. /foo is a parent to /foo/bar,\n  // and foo is a parent to foo/bar. Any ancestor is considered a parent i.e. /a\n  // is a parent to both /a/b and /a/b/c.  Does not convert paths to absolute,\n  // follow symlinks or directory navigation (e.g. \"..\"). A path is *NOT* its\n  // own parent.\n  bool IsParent(const FilePath& child) const;\n\n  // If IsParent(child) holds, appends to path (if non-NULL) the\n  // relative path to child and returns true.  For example, if parent\n  // holds \"/Users/johndoe/Library/Application Support\", child holds\n  // \"/Users/johndoe/Library/Application Support/Google/Chrome/Default\", and\n  // *path holds \"/Users/johndoe/Library/Caches\", then after\n  // parent.AppendRelativePath(child, path) is called *path will hold\n  // \"/Users/johndoe/Library/Caches/Google/Chrome/Default\".  Otherwise,\n  // returns false.\n  bool AppendRelativePath(const FilePath& child, FilePath* path) const;\n\n  // Returns the directory name of this FilePath. If |path_| is root, it\n  // returns the same. For example, if |path_| is \"foo/bar\", it returns \"foo\".\n  [[nodiscard]] FilePath DirName() const;\n\n  // Returns the base name of this FilePath. If |path_| is root, it returns\n  // empty path. For example, if |path_| is \"foo/bar\", it returns \"bar\".\n  [[nodiscard]] FilePath BaseName() const;\n\n  // Returns the extension of this FilePath. If |path_| doesn't contain an\n  // extension, it returns empty string. For example, if |path_| is \"foo.jpg\",\n  // it returns \".jpg\".\n  [[nodiscard]] std::string Extension() const;\n\n  // Returns the final extension of a file path, or an empty string if the file\n  // path has no extension.  In most cases, the final extension of a file path\n  // refers to the part of the file path from the last dot to the end (including\n  // the dot itself).  For example, this method applied to \"/pics/jojo.jpg\"\n  // and \"/pics/jojo.\" returns \".jpg\" and \".\", respectively.  However, if the\n  // base name of the file path is either \".\" or \"..\", this method returns an\n  // empty string.\n  //\n  // TODO(davidben): Check all our extension-sensitive code to see if\n  // we can rename this to Extension() and the other to something like\n  // LongExtension(), defaulting to short extensions and leaving the\n  // long \"extensions\" to logic like base::GetUniquePathNumber().\n  [[nodiscard]] std::string FinalExtension() const;\n\n  // Returns \"/pics/jojo\" for path \"/pics/jojo.jpg\"\n  // NOTE: this is slightly different from the similar file_util implementation\n  // which returned simply 'jojo'.\n  [[nodiscard]] FilePath RemoveExtension() const;\n\n  // Removes the path's file extension, as in RemoveExtension(), but\n  // ignores double extensions.\n  [[nodiscard]] FilePath RemoveFinalExtension() const;\n\n  // Inserts |suffix| after the file name portion of |path| but before the\n  // extension.  Returns \"\" if BaseName() == \".\" or \"..\".\n  // Examples:\n  // path == \"jojo.jpg\"         suffix == \" (1)\", returns \"jojo (1).jpg\"\n  [[nodiscard]] FilePath InsertBeforeExtension(std::string_view suffix) const;\n\n  // Returns a FilePath by appending a separator and the supplied path\n  // component to this object's path. Append takes care to avoid adding\n  // excessive separators if this object's path already ends with a separator.\n  //\n  // NOTE: |component| must be a relative path, it is an error to pass an\n  // absolute path.\n  [[nodiscard]] FilePath Append(std::string_view component) const;\n  [[nodiscard]] FilePath Append(const FilePath& component) const;\n\n  // Convenient operators, which does the same above.\n  FilePath operator/(std::string_view component) const;\n  FilePath operator/(const FilePath& component) const;\n  FilePath& operator/=(std::string_view component);\n  FilePath& operator/=(const FilePath& component);\n\n  // Returns true if |path_| is absolute.\n  bool IsAbsolute() const;\n\n  // Returns true if |path_| ends with a path separator character.\n  [[nodiscard]] bool EndsWithSeparator() const;\n\n  // Returns a copy of this FilePath that ends with a trailing separator. If\n  // the input path is empty, an empty FilePath will be returned.\n  [[nodiscard]] FilePath AsEndingWithSeparator() const;\n\n  // Returns a copy of this FilePath that does not end with a trailing\n  // separator.\n  [[nodiscard]] FilePath StripTrailingSeparators() const;\n\n  // Returns true if this FilePath contains an attempt to reference a parent\n  // directory (e.g. has a path component that is \"..\").\n  bool ReferencesParent() const;\n\n private:\n  // Remove trailing separators from this object. If |path_| is absolute, it\n  // will never be stripped any more than to refer to the absolute root\n  // directory, so \"////\" will become \"/\", not \"\".\n  void StripTrailingSeparatorsInternal();\n\n  std::string path_;\n};\n\nTACHYON_EXPORT std::ostream& operator<<(std::ostream& out,\n                                        const FilePath& file_path);\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_FILES_FILE_PATH_H_\n"
  },
  {
    "path": "tachyon/base/files/file_path_flag.h",
    "content": "#ifndef TACHYON_BASE_FILES_FILE_PATH_FLAG_H_\n#define TACHYON_BASE_FILES_FILE_PATH_FLAG_H_\n\n#include <string>\n\n#include \"tachyon/base/files/file_path.h\"\n#include \"tachyon/base/flag/flag.h\"\n\nnamespace tachyon::base {\n\ntemplate <>\nclass FlagValueTraits<FilePath> {\n public:\n  static bool ParseValue(std::string_view input, FilePath* value,\n                         std::string* reason) {\n    *value = FilePath(input);\n    return true;\n  }\n};\n\ntypedef Flag<FilePath> FilePathFlag;\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_FILES_FILE_PATH_FLAG_H_\n"
  },
  {
    "path": "tachyon/base/files/file_path_unittest.cc",
    "content": "#include \"tachyon/base/files/file_path.h\"\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n\nnamespace tachyon::base {\n\nTEST(FilePathTest, GetComponents) {\n  std::vector<std::string> components = FilePath(\"foo/bar\").GetComponents();\n  EXPECT_THAT(components,\n              testing::ContainerEq(std::vector<std::string>{\"foo\", \"bar\"}));\n  components = FilePath(\"/foo/bar\").GetComponents();\n  EXPECT_THAT(components, testing::ContainerEq(\n                              std::vector<std::string>{\"/\", \"foo\", \"bar\"}));\n  components = FilePath(\"./foo/bar\").GetComponents();\n  EXPECT_THAT(components, testing::ContainerEq(\n                              std::vector<std::string>{\".\", \"foo\", \"bar\"}));\n}\n\nTEST(FilePathTest, IsParent) {\n  FilePath path(\"foo/bar\");\n  EXPECT_TRUE(path.IsParent(FilePath(\"foo/bar/baz.jpg\")));\n  EXPECT_FALSE(path.IsParent(FilePath(\"foo/bar\")));\n  EXPECT_FALSE(path.IsParent(FilePath(\"abc/foo/bar/baz.jpg\")));\n}\n\nTEST(FilePathTest, AppendRelativePath) {\n  FilePath path(\"foo/bar\");\n  FilePath path_appended;\n  EXPECT_TRUE(\n      path.AppendRelativePath(FilePath(\"foo/bar/baz.jpg\"), &path_appended));\n  EXPECT_EQ(FilePath(\"baz.jpg\"), path_appended);\n}\n\nTEST(FilePathTest, DirName) {\n  FilePath path(\"foo/bar\");\n  EXPECT_EQ(path.DirName(), FilePath(\"foo\"));\n  EXPECT_EQ(path.DirName().DirName(), FilePath(\"\"));\n  EXPECT_EQ(FilePath(\"/\").DirName(), FilePath(\"/\"));\n}\n\nTEST(FilePathTest, BaseName) {\n  FilePath path(\"foo/bar\");\n  EXPECT_EQ(path.BaseName(), FilePath(\"bar\"));\n  EXPECT_EQ(path.DirName().BaseName(), FilePath(\"foo\"));\n  EXPECT_EQ(FilePath(\"\"), FilePath(\"\"));\n  EXPECT_EQ(FilePath(\"/\").BaseName(), FilePath(\"\"));\n}\n\nTEST(FilePathTest, Extension) {\n  EXPECT_EQ(FilePath(\"foo/bar.jpg\").Extension(), \".jpg\");\n}\n\nTEST(FilePathTest, IsAbsolute) {\n  EXPECT_FALSE(FilePath(\"foo/bar\").IsAbsolute());\n  EXPECT_TRUE(FilePath(\"/foo/bar\").IsAbsolute());\n}\n\nTEST(FilePathTest, EndsWithSeparator) {\n  EXPECT_TRUE(FilePath(\"foo/\").EndsWithSeparator());\n  EXPECT_FALSE(FilePath(\"foo\").EndsWithSeparator());\n}\n\nTEST(FilePathTest, AsEndingWithSeparator) {\n  EXPECT_EQ(FilePath(\"foo/\").AsEndingWithSeparator(), FilePath(\"foo/\"));\n  EXPECT_EQ(FilePath(\"foo\").AsEndingWithSeparator(), FilePath(\"foo/\"));\n}\n\nTEST(FilePathTest, StripTrailingSeparators) {\n  EXPECT_EQ(FilePath(\"foo/\").StripTrailingSeparators(), FilePath(\"foo\"));\n  EXPECT_EQ(FilePath(\"foo//\").StripTrailingSeparators(), FilePath(\"foo\"));\n  EXPECT_EQ(FilePath(\"foo\").StripTrailingSeparators(), FilePath(\"foo\"));\n  EXPECT_EQ(FilePath(\"/\").StripTrailingSeparators(), FilePath(\"/\"));\n}\n\nTEST(FilePathTest, AppendComponent) {\n  {\n    FilePath path(\"foo/bar\");\n    EXPECT_EQ(path.Append(\"baz\"), FilePath(\"foo/bar/baz\"));\n  }\n  {\n    FilePath path;\n    EXPECT_EQ(path.Append(\"foo\"), FilePath(\"foo\"));\n  }\n  {\n    FilePath path(\".\");\n    EXPECT_EQ(path.Append(\"foo\"), FilePath(\"./foo\"));\n  }\n}\n\nTEST(FilePathTest, ReferencesParent) {\n  EXPECT_TRUE(FilePath(\"..\").ReferencesParent());\n  EXPECT_TRUE(FilePath(\"../foo\").ReferencesParent());\n  EXPECT_TRUE(FilePath(\"foo/../bar\").ReferencesParent());\n  EXPECT_FALSE(FilePath(\".\").ReferencesParent());\n  EXPECT_FALSE(FilePath(\"foo/bar\").ReferencesParent());\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/files/file_posix.cc",
    "content": "// Copyright 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/files/file.h\"\n\n// The only 32-bit platform that uses this file is Android. On Android APIs\n// >= 21, this standard define is the right way to express that you want a\n// 64-bit offset in struct stat, and the stat64 struct and functions aren't\n// useful.\n#define _FILE_OFFSET_BITS 64\n\n#include <errno.h>\n#include <fcntl.h>\n#include <stdint.h>\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include <optional>\n\nstatic_assert(sizeof(tachyon::base::stat_wrapper_t::st_size) >= 8);\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/numerics/safe_conversions.h\"\n#include \"tachyon/base/posix/eintr_wrapper.h\"\n// #include \"tachyon/base/strings/utf_string_conversions.h\"\n// #include \"tachyon/base/threading/scoped_blocking_call.h\"\n#include \"tachyon/build/build_config.h\"\n\n#if BUILDFLAG(IS_ANDROID)\n#include \"base/os_compat_android.h\"\n#endif\n\nnamespace tachyon::base {\n\n// Make sure our Whence mappings match the system headers.\nstatic_assert(File::FROM_BEGIN == SEEK_SET && File::FROM_CURRENT == SEEK_CUR &&\n                  File::FROM_END == SEEK_END,\n              \"whence mapping must match the system headers\");\n\nnamespace {\n\n// NaCl doesn't provide the following system calls, so either simulate them or\n// wrap them in order to minimize the number of #ifdef's in this file.\n#if !BUILDFLAG(IS_NACL) && !BUILDFLAG(IS_AIX)\nbool IsOpenAppend(PlatformFile file) {\n  return (fcntl(file, F_GETFL) & O_APPEND) != 0;\n}\n\nint CallFtruncate(PlatformFile file, int64_t length) {\n#if BUILDFLAG(IS_BSD) || BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_FUCHSIA)\n  static_assert(sizeof(off_t) >= sizeof(int64_t),\n                \"off_t is not a 64-bit integer\");\n  return HANDLE_EINTR(ftruncate(file, length));\n#else\n  return HANDLE_EINTR(ftruncate64(file, length));\n#endif\n}\n\nint CallFutimes(PlatformFile file, const struct timeval times[2]) {\n#ifdef __USE_XOPEN2K8\n  // futimens should be available, but futimes might not be\n  // http://pubs.opengroup.org/onlinepubs/9699919799/\n\n  timespec ts_times[2];\n  ts_times[0].tv_sec  = times[0].tv_sec;\n  ts_times[0].tv_nsec = times[0].tv_usec * 1000;\n  ts_times[1].tv_sec  = times[1].tv_sec;\n  ts_times[1].tv_nsec = times[1].tv_usec * 1000;\n\n  return futimens(file, ts_times);\n#else\n  return futimes(file, times);\n#endif\n}\n\n#if !BUILDFLAG(IS_FUCHSIA)\nshort FcntlFlockType(std::optional<File::LockMode> mode) {\n  if (!mode.has_value())\n    return F_UNLCK;\n  switch (mode.value()) {\n    case File::LockMode::kShared:\n      return F_RDLCK;\n    case File::LockMode::kExclusive:\n      return F_WRLCK;\n  }\n  NOTREACHED();\n}\n\nFile::Error CallFcntlFlock(PlatformFile file,\n                           std::optional<File::LockMode> mode) {\n  struct flock lock;\n  lock.l_type = FcntlFlockType(std::move(mode));\n  lock.l_whence = SEEK_SET;\n  lock.l_start = 0;\n  lock.l_len = 0;  // Lock entire file.\n  if (HANDLE_EINTR(fcntl(file, F_SETLK, &lock)) == -1)\n    return File::GetLastFileError();\n  return File::FILE_OK;\n}\n#endif\n\n#else   // BUILDFLAG(IS_NACL) && !BUILDFLAG(IS_AIX)\n\nbool IsOpenAppend(PlatformFile file) {\n  // NaCl doesn't implement fcntl. Since NaCl's write conforms to the POSIX\n  // standard and always appends if the file is opened with O_APPEND, just\n  // return false here.\n  return false;\n}\n\nint CallFtruncate(PlatformFile file, int64_t length) {\n  NOTIMPLEMENTED();  // NaCl doesn't implement ftruncate.\n  return 0;\n}\n\nint CallFutimes(PlatformFile file, const struct timeval times[2]) {\n  NOTIMPLEMENTED();  // NaCl doesn't implement futimes.\n  return 0;\n}\n\nFile::Error CallFcntlFlock(PlatformFile file,\n                           std::optional<File::LockMode> mode) {\n  NOTIMPLEMENTED();  // NaCl doesn't implement flock struct.\n  return File::FILE_ERROR_INVALID_OPERATION;\n}\n#endif  // BUILDFLAG(IS_NACL)\n\n}  // namespace\n\nvoid File::Info::FromStat(const stat_wrapper_t& stat_info) {\n  is_directory = S_ISDIR(stat_info.st_mode);\n  is_symbolic_link = S_ISLNK(stat_info.st_mode);\n  size = stat_info.st_size;\n\n  // Get last modification time, last access time, and creation time from\n  // |stat_info|.\n  // Note: st_ctime is actually last status change time when the inode was last\n  // updated, which happens on any metadata change. It is not the file's\n  // creation time. However, other than on Mac & iOS where the actual file\n  // creation time is included as st_birthtime, the rest of POSIX platforms have\n  // no portable way to get the creation time.\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_FUCHSIA)\n  time_t last_modified_sec = stat_info.st_mtim.tv_sec;\n  int64_t last_modified_nsec = stat_info.st_mtim.tv_nsec;\n  time_t last_accessed_sec = stat_info.st_atim.tv_sec;\n  int64_t last_accessed_nsec = stat_info.st_atim.tv_nsec;\n  time_t creation_time_sec = stat_info.st_ctim.tv_sec;\n  int64_t creation_time_nsec = stat_info.st_ctim.tv_nsec;\n#elif BUILDFLAG(IS_ANDROID)\n  time_t last_modified_sec = stat_info.st_mtime;\n  int64_t last_modified_nsec = stat_info.st_mtime_nsec;\n  time_t last_accessed_sec = stat_info.st_atime;\n  int64_t last_accessed_nsec = stat_info.st_atime_nsec;\n  time_t creation_time_sec = stat_info.st_ctime;\n  int64_t creation_time_nsec = stat_info.st_ctime_nsec;\n#elif BUILDFLAG(IS_APPLE)\n  time_t last_modified_sec = stat_info.st_mtimespec.tv_sec;\n  int64_t last_modified_nsec = stat_info.st_mtimespec.tv_nsec;\n  time_t last_accessed_sec = stat_info.st_atimespec.tv_sec;\n  int64_t last_accessed_nsec = stat_info.st_atimespec.tv_nsec;\n  time_t creation_time_sec = stat_info.st_birthtimespec.tv_sec;\n  int64_t creation_time_nsec = stat_info.st_birthtimespec.tv_nsec;\n#elif BUILDFLAG(IS_BSD)\n  time_t last_modified_sec = stat_info.st_mtimespec.tv_sec;\n  int64_t last_modified_nsec = stat_info.st_mtimespec.tv_nsec;\n  time_t last_accessed_sec = stat_info.st_atimespec.tv_sec;\n  int64_t last_accessed_nsec = stat_info.st_atimespec.tv_nsec;\n  time_t creation_time_sec = stat_info.st_ctimespec.tv_sec;\n  int64_t creation_time_nsec = stat_info.st_ctimespec.tv_nsec;\n#else\n  time_t last_modified_sec = stat_info.st_mtime;\n  int64_t last_modified_nsec = 0;\n  time_t last_accessed_sec = stat_info.st_atime;\n  int64_t last_accessed_nsec = 0;\n  time_t creation_time_sec = stat_info.st_ctime;\n  int64_t creation_time_nsec = 0;\n#endif\n\n  last_modified =\n      Time::FromTimeT(last_modified_sec) +\n      Microseconds(last_modified_nsec / Time::kNanosecondsPerMicrosecond);\n\n  last_accessed =\n      Time::FromTimeT(last_accessed_sec) +\n      Microseconds(last_accessed_nsec / Time::kNanosecondsPerMicrosecond);\n\n  creation_time =\n      Time::FromTimeT(creation_time_sec) +\n      Microseconds(creation_time_nsec / Time::kNanosecondsPerMicrosecond);\n}\n\nbool File::IsValid() const {\n  return file_.is_valid();\n}\n\nPlatformFile File::GetPlatformFile() const {\n  return file_.get();\n}\n\nPlatformFile File::TakePlatformFile() {\n  return file_.release();\n}\n\nvoid File::Close() {\n  if (!IsValid())\n    return;\n\n  /*\n  TODO(chokobole):\n  SCOPED_FILE_TRACE(\"Close\");\n  ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n  */\n  file_.reset();\n}\n\nint64_t File::Seek(Whence whence, int64_t offset) {\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n  DCHECK(IsValid());\n\n  // TODO(chokobole):\n  // SCOPED_FILE_TRACE_WITH_SIZE(\"Seek\", offset);\n\n#if BUILDFLAG(IS_ANDROID)\n  static_assert(sizeof(int64_t) == sizeof(off64_t), \"off64_t must be 64 bits\");\n  return lseek64(file_.get(), static_cast<off64_t>(offset),\n                 static_cast<int>(whence));\n#else\n  static_assert(sizeof(int64_t) == sizeof(off_t), \"off_t must be 64 bits\");\n  return lseek(file_.get(), static_cast<off_t>(offset),\n               static_cast<int>(whence));\n#endif\n}\n\nint File::Read(int64_t offset, char* data, int size) {\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n  DCHECK(IsValid());\n  if (size < 0 || !IsValueInRangeForNumericType<off_t>(offset + size - 1))\n    return -1;\n\n  // TODO(chokobole):\n  // SCOPED_FILE_TRACE_WITH_SIZE(\"Read\", size);\n\n  int bytes_read = 0;\n  long rv;\n  do {\n    rv = HANDLE_EINTR(pread(file_.get(), data + bytes_read,\n                            static_cast<size_t>(size - bytes_read),\n                            static_cast<off_t>(offset + bytes_read)));\n    if (rv <= 0)\n      break;\n\n    bytes_read += rv;\n  } while (bytes_read < size);\n\n  return bytes_read ? bytes_read : checked_cast<int>(rv);\n}\n\nint File::ReadAtCurrentPos(char* data, int size) {\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n  DCHECK(IsValid());\n  if (size < 0)\n    return -1;\n\n  // TODO(chokobole):\n  // SCOPED_FILE_TRACE_WITH_SIZE(\"ReadAtCurrentPos\", size);\n\n  int bytes_read = 0;\n  long rv;\n  do {\n    rv = HANDLE_EINTR(read(file_.get(), data + bytes_read,\n                           static_cast<size_t>(size - bytes_read)));\n    if (rv <= 0)\n      break;\n\n    bytes_read += rv;\n  } while (bytes_read < size);\n\n  return bytes_read ? bytes_read : checked_cast<int>(rv);\n}\n\nint File::ReadNoBestEffort(int64_t offset, char* data, int size) {\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n  DCHECK(IsValid());\n  if (size < 0 || !IsValueInRangeForNumericType<off_t>(offset))\n    return -1;\n\n  // TODO(chokobole):\n  // SCOPED_FILE_TRACE_WITH_SIZE(\"ReadNoBestEffort\", size);\n  return checked_cast<int>(\n      HANDLE_EINTR(pread(file_.get(), data, static_cast<size_t>(size),\n                         static_cast<off_t>(offset))));\n}\n\nint File::ReadAtCurrentPosNoBestEffort(char* data, int size) {\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n  DCHECK(IsValid());\n  if (size < 0)\n    return -1;\n\n  // TODO(chokobole):\n  // SCOPED_FILE_TRACE_WITH_SIZE(\"ReadAtCurrentPosNoBestEffort\", size);\n  return checked_cast<int>(\n      HANDLE_EINTR(read(file_.get(), data, static_cast<size_t>(size))));\n}\n\nint File::Write(int64_t offset, const char* data, int size) {\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n\n  if (IsOpenAppend(file_.get()))\n    return WriteAtCurrentPos(data, size);\n\n  DCHECK(IsValid());\n  if (size < 0)\n    return -1;\n\n  // TODO(chokobole):\n  // SCOPED_FILE_TRACE_WITH_SIZE(\"Write\", size);\n\n  int bytes_written = 0;\n  long rv;\n  do {\n#if BUILDFLAG(IS_ANDROID)\n    // In case __USE_FILE_OFFSET64 is not used, we need to call pwrite64()\n    // instead of pwrite().\n    static_assert(sizeof(int64_t) == sizeof(off64_t),\n                  \"off64_t must be 64 bits\");\n    rv = HANDLE_EINTR(pwrite64(file_.get(), data + bytes_written,\n                               static_cast<size_t>(size - bytes_written),\n                               offset + bytes_written));\n#else\n    rv = HANDLE_EINTR(pwrite(file_.get(), data + bytes_written,\n                             static_cast<size_t>(size - bytes_written),\n                             offset + bytes_written));\n#endif\n    if (rv <= 0)\n      break;\n\n    bytes_written += rv;\n  } while (bytes_written < size);\n\n  return bytes_written ? bytes_written : checked_cast<int>(rv);\n}\n\nint File::WriteAtCurrentPos(const char* data, int size) {\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n  DCHECK(IsValid());\n  if (size < 0)\n    return -1;\n\n  // TODO(chokobole):\n  // SCOPED_FILE_TRACE_WITH_SIZE(\"WriteAtCurrentPos\", size);\n\n  int bytes_written = 0;\n  long rv;\n  do {\n    rv = HANDLE_EINTR(write(file_.get(), data + bytes_written,\n                            static_cast<size_t>(size - bytes_written)));\n    if (rv <= 0)\n      break;\n\n    bytes_written += rv;\n  } while (bytes_written < size);\n\n  return bytes_written ? bytes_written : checked_cast<int>(rv);\n}\n\nint File::WriteAtCurrentPosNoBestEffort(const char* data, int size) {\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n  DCHECK(IsValid());\n  if (size < 0)\n    return -1;\n\n  // TODO(chokobole):\n  // SCOPED_FILE_TRACE_WITH_SIZE(\"WriteAtCurrentPosNoBestEffort\", size);\n  return checked_cast<int>(\n      HANDLE_EINTR(write(file_.get(), data, static_cast<size_t>(size))));\n}\n\nint64_t File::GetLength() {\n  DCHECK(IsValid());\n\n  // TODO(chokobole):\n  // SCOPED_FILE_TRACE(\"GetLength\");\n\n  stat_wrapper_t file_info;\n  if (Fstat(file_.get(), &file_info))\n    return -1;\n\n  return file_info.st_size;\n}\n\nbool File::SetLength(int64_t length) {\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n  DCHECK(IsValid());\n\n  // TODO(chokobole):\n  // SCOPED_FILE_TRACE_WITH_SIZE(\"SetLength\", length);\n  return !CallFtruncate(file_.get(), length);\n}\n\nbool File::SetTimes(Time last_access_time, Time last_modified_time) {\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n  DCHECK(IsValid());\n\n  // TODO(chokobole):\n  // SCOPED_FILE_TRACE(\"SetTimes\");\n\n  timeval times[2];\n  times[0] = last_access_time.ToTimeVal();\n  times[1] = last_modified_time.ToTimeVal();\n\n  return !CallFutimes(file_.get(), times);\n}\n\nbool File::GetInfo(Info* info) {\n  DCHECK(IsValid());\n\n  // TODO(chokobole):\n  // SCOPED_FILE_TRACE(\"GetInfo\");\n\n  stat_wrapper_t file_info;\n  if (Fstat(file_.get(), &file_info))\n    return false;\n\n  info->FromStat(file_info);\n  return true;\n}\n\n#if !BUILDFLAG(IS_FUCHSIA)\nFile::Error File::Lock(File::LockMode mode) {\n  // TODO(chokobole):\n  // SCOPED_FILE_TRACE(\"Lock\");\n  return CallFcntlFlock(file_.get(), mode);\n}\n\nFile::Error File::Unlock() {\n  // TODO(chokobole):\n  // SCOPED_FILE_TRACE(\"Unlock\");\n  return CallFcntlFlock(file_.get(), std::optional<File::LockMode>());\n}\n#endif\n\nFile File::Duplicate() const {\n  if (!IsValid())\n    return File();\n\n  // TODO(chokobole):\n  // SCOPED_FILE_TRACE(\"Duplicate\");\n\n  ScopedPlatformFile other_fd(HANDLE_EINTR(dup(GetPlatformFile())));\n  if (!other_fd.is_valid())\n    return File(File::GetLastFileError());\n\n  return File(std::move(other_fd), async());\n}\n\n// Static.\nFile::Error File::OSErrorToFileError(int saved_errno) {\n  switch (saved_errno) {\n    case EACCES:\n    case EISDIR:\n    case EROFS:\n    case EPERM:\n      return FILE_ERROR_ACCESS_DENIED;\n    case EBUSY:\n#if !BUILDFLAG(IS_NACL)  // ETXTBSY not defined by NaCl.\n    case ETXTBSY:\n#endif\n      return FILE_ERROR_IN_USE;\n    case EEXIST:\n      return FILE_ERROR_EXISTS;\n    case EIO:\n      return FILE_ERROR_IO;\n    case ENOENT:\n      return FILE_ERROR_NOT_FOUND;\n    case ENFILE:  // fallthrough\n    case EMFILE:\n      return FILE_ERROR_TOO_MANY_OPENED;\n    case ENOMEM:\n      return FILE_ERROR_NO_MEMORY;\n    case ENOSPC:\n      return FILE_ERROR_NO_SPACE;\n    case ENOTDIR:\n      return FILE_ERROR_NOT_A_DIRECTORY;\n    default:\n      // This function should only be called for errors.\n      DCHECK_NE(0, saved_errno);\n      return FILE_ERROR_FAILED;\n  }\n}\n\n// NaCl doesn't implement system calls to open files directly.\n#if !BUILDFLAG(IS_NACL)\n// TODO(erikkay): does it make sense to support FLAG_EXCLUSIVE_* here?\nvoid File::DoInitialize(const FilePath& path, uint32_t flags) {\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n  DCHECK(!IsValid());\n\n  int open_flags = 0;\n  if (flags & FLAG_CREATE)\n    open_flags = O_CREAT | O_EXCL;\n\n  created_ = false;\n\n  if (flags & FLAG_CREATE_ALWAYS) {\n    DCHECK(!open_flags);\n    DCHECK(flags & FLAG_WRITE);\n    open_flags = O_CREAT | O_TRUNC;\n  }\n\n  if (flags & FLAG_OPEN_TRUNCATED) {\n    DCHECK(!open_flags);\n    DCHECK(flags & FLAG_WRITE);\n    open_flags = O_TRUNC;\n  }\n\n  if (!open_flags && !(flags & FLAG_OPEN) && !(flags & FLAG_OPEN_ALWAYS)) {\n    NOTREACHED();\n    errno = EOPNOTSUPP;\n    error_details_ = FILE_ERROR_FAILED;\n    return;\n  }\n\n  if (flags & FLAG_WRITE && flags & FLAG_READ) {\n    open_flags |= O_RDWR;\n  } else if (flags & FLAG_WRITE) {\n    open_flags |= O_WRONLY;\n  } else if (!(flags & FLAG_READ) && !(flags & FLAG_WRITE_ATTRIBUTES) &&\n             !(flags & FLAG_APPEND) && !(flags & FLAG_OPEN_ALWAYS)) {\n    // Note: For FLAG_WRITE_ATTRIBUTES and no other read/write flags, we'll\n    // open the file in O_RDONLY mode (== 0, see static_assert below), so that\n    // we get a fd that can be used for SetTimes().\n    NOTREACHED();\n  }\n\n  if (flags & FLAG_TERMINAL_DEVICE)\n    open_flags |= O_NOCTTY | O_NDELAY;\n\n  if (flags & FLAG_APPEND && flags & FLAG_READ)\n    open_flags |= O_APPEND | O_RDWR;\n  else if (flags & FLAG_APPEND)\n    open_flags |= O_APPEND | O_WRONLY;\n\n  static_assert(O_RDONLY == 0, \"O_RDONLY must equal zero\");\n\n  mode_t mode = S_IRUSR | S_IWUSR;\n#if BUILDFLAG(IS_CHROMEOS)\n  mode |= S_IRGRP | S_IROTH;\n#endif\n\n  int descriptor = HANDLE_EINTR(open(path.value().c_str(), open_flags, mode));\n\n  if (flags & FLAG_OPEN_ALWAYS) {\n    if (descriptor < 0) {\n      open_flags |= O_CREAT;\n      descriptor = HANDLE_EINTR(open(path.value().c_str(), open_flags, mode));\n      if (descriptor >= 0)\n        created_ = true;\n    }\n  }\n\n  if (descriptor < 0) {\n    error_details_ = File::GetLastFileError();\n    return;\n  }\n\n  if (flags & (FLAG_CREATE_ALWAYS | FLAG_CREATE))\n    created_ = true;\n\n  if (flags & FLAG_DELETE_ON_CLOSE)\n    unlink(path.value().c_str());\n\n  async_ = ((flags & FLAG_ASYNC) == FLAG_ASYNC);\n  error_details_ = FILE_OK;\n  file_.reset(descriptor);\n}\n#endif  // !BUILDFLAG(IS_NACL)\n\nbool File::Flush() {\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n  DCHECK(IsValid());\n  // TODO(chokobole):\n  // SCOPED_FILE_TRACE(\"Flush\");\n\n#if BUILDFLAG(IS_NACL)\n  NOTIMPLEMENTED();  // NaCl doesn't implement fsync.\n  return true;\n#elif BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS) || \\\n    BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_LINUX)\n  return !HANDLE_EINTR(fdatasync(file_.get()));\n#elif BUILDFLAG(IS_APPLE)\n  // On macOS and iOS, fsync() is guaranteed to send the file's data to the\n  // underlying storage device, but may return before the device actually writes\n  // the data to the medium. When used by database systems, this may result in\n  // unexpected data loss.\n  // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fsync.2.html\n  if (!HANDLE_EINTR(fcntl(file_.get(), F_FULLFSYNC)))\n    return true;\n\n  // Some filesystms do not support fcntl(F_FULLFSYNC). We handle these cases by\n  // falling back to fsync(). Unfortunately, lack of F_FULLFSYNC support results\n  // in various error codes, so we cannot use the error code as a definitive\n  // indicator that F_FULLFSYNC was not supported. So, if fcntl() errors out for\n  // any reason, we may end up making an unnecessary system call.\n  //\n  // See the CL description at https://crrev.com/c/1400159 for details.\n  return !HANDLE_EINTR(fsync(file_.get()));\n#else\n  return !HANDLE_EINTR(fsync(file_.get()));\n#endif\n}\n\nvoid File::SetPlatformFile(PlatformFile file) {\n  DCHECK(!file_.is_valid());\n  file_.reset(file);\n}\n\n// static\nFile::Error File::GetLastFileError() {\n  return base::File::OSErrorToFileError(errno);\n}\n\nint File::Stat(const char* path, stat_wrapper_t* sb) {\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n  return stat(path, sb);\n}\nint File::Fstat(int fd, stat_wrapper_t* sb) {\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n  return fstat(fd, sb);\n}\nint File::Lstat(const char* path, stat_wrapper_t* sb) {\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n  return lstat(path, sb);\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/files/file_unittest.cc",
    "content": "// Copyright 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/files/file.h\"\n\n#include <stdint.h>\n\n#include <utility>\n\n#include \"tachyon/base/files/file_util.h\"\n// #include \"tachyon/base/files/memory_mapped_file.h\"\n#include \"tachyon/base/files/scoped_temp_dir.h\"\n#include \"tachyon/base/strings/string_util.h\"\n#include \"tachyon/base/time/time.h\"\n#include \"tachyon/build/build_config.h\"\n#include \"tachyon/build/buildflag.h\"\n#include \"gtest/gtest.h\"\n\n// TODO(chokobole):\n// #if BUILDFLAG(ENABLE_BASE_TRACING)\n// #include \"third_party/perfetto/include/perfetto/test/traced_value_test_support.h\"  // no-presubmit-check nogncheck\n// #endif  // BUILDFLAG(ENABLE_BASE_TRACING)\n\n#if BUILDFLAG(IS_WIN)\n#include <windows.h>\n\n#include \"tachyon/base/environment.h\"\n#include \"tachyon/base/strings/utf_string_conversions.h\"\n#include \"tachyon/base/test/gtest_util.h\"\n#endif\n\nnamespace tachyon {\n\nusing base::File;\nusing base::FilePath;\n\nTEST(FileTest, Create) {\n  base::ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n  FilePath file_path = temp_dir.GetPath().Append(\"create_file_1\");\n\n  {\n    // Don't create a File at all.\n    File file;\n    EXPECT_FALSE(file.IsValid());\n    EXPECT_EQ(base::File::FILE_ERROR_FAILED, file.error_details());\n\n    File file2(base::File::FILE_ERROR_TOO_MANY_OPENED);\n    EXPECT_FALSE(file2.IsValid());\n    EXPECT_EQ(base::File::FILE_ERROR_TOO_MANY_OPENED, file2.error_details());\n  }\n\n  {\n    // Open a file that doesn't exist.\n    File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ);\n    EXPECT_FALSE(file.IsValid());\n    EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, file.error_details());\n    EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, base::File::GetLastFileError());\n  }\n\n  {\n    // Open or create a file.\n    File file(file_path, base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_READ);\n    EXPECT_TRUE(file.IsValid());\n    EXPECT_TRUE(file.created());\n    EXPECT_EQ(base::File::FILE_OK, file.error_details());\n  }\n\n  {\n    // Open an existing file.\n    File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ);\n    EXPECT_TRUE(file.IsValid());\n    EXPECT_FALSE(file.created());\n    EXPECT_EQ(base::File::FILE_OK, file.error_details());\n\n    // This time verify closing the file.\n    file.Close();\n    EXPECT_FALSE(file.IsValid());\n  }\n\n  {\n    // Open an existing file through Initialize\n    File file;\n    file.Initialize(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ);\n    EXPECT_TRUE(file.IsValid());\n    EXPECT_FALSE(file.created());\n    EXPECT_EQ(base::File::FILE_OK, file.error_details());\n\n    // This time verify closing the file.\n    file.Close();\n    EXPECT_FALSE(file.IsValid());\n  }\n\n  {\n    // Create a file that exists.\n    File file(file_path, base::File::FLAG_CREATE | base::File::FLAG_READ);\n    EXPECT_FALSE(file.IsValid());\n    EXPECT_FALSE(file.created());\n    EXPECT_EQ(base::File::FILE_ERROR_EXISTS, file.error_details());\n    EXPECT_EQ(base::File::FILE_ERROR_EXISTS, base::File::GetLastFileError());\n  }\n\n  {\n    // Create or overwrite a file.\n    File file(file_path,\n              base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);\n    EXPECT_TRUE(file.IsValid());\n    EXPECT_TRUE(file.created());\n    EXPECT_EQ(base::File::FILE_OK, file.error_details());\n  }\n\n  {\n    // Create a delete-on-close file.\n    file_path = temp_dir.GetPath().Append(\"create_file_2\");\n    File file(file_path,\n              base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_READ |\n                  base::File::FLAG_DELETE_ON_CLOSE);\n    EXPECT_TRUE(file.IsValid());\n    EXPECT_TRUE(file.created());\n    EXPECT_EQ(base::File::FILE_OK, file.error_details());\n  }\n\n  EXPECT_FALSE(base::PathExists(file_path));\n}\n\nTEST(FileTest, SelfSwap) {\n  base::ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n  FilePath file_path = temp_dir.GetPath().Append(\"create_file_1\");\n  File file(file_path,\n            base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_DELETE_ON_CLOSE);\n  std::swap(file, file);\n  EXPECT_TRUE(file.IsValid());\n}\n\nTEST(FileTest, Async) {\n  base::ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n  FilePath file_path = temp_dir.GetPath().Append(\"create_file\");\n\n  {\n    File file(file_path, base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_ASYNC);\n    EXPECT_TRUE(file.IsValid());\n    EXPECT_TRUE(file.async());\n  }\n\n  {\n    File file(file_path, base::File::FLAG_OPEN_ALWAYS);\n    EXPECT_TRUE(file.IsValid());\n    EXPECT_FALSE(file.async());\n  }\n}\n\nTEST(FileTest, DeleteOpenFile) {\n  base::ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n  FilePath file_path = temp_dir.GetPath().Append(\"create_file_1\");\n\n  // Create a file.\n  File file(file_path, base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_READ |\n                           base::File::FLAG_WIN_SHARE_DELETE);\n  EXPECT_TRUE(file.IsValid());\n  EXPECT_TRUE(file.created());\n  EXPECT_EQ(base::File::FILE_OK, file.error_details());\n\n  // Open an existing file and mark it as delete on close.\n  File same_file(file_path,\n                 base::File::FLAG_OPEN | base::File::FLAG_DELETE_ON_CLOSE |\n                     base::File::FLAG_READ);\n  EXPECT_TRUE(file.IsValid());\n  EXPECT_FALSE(same_file.created());\n  EXPECT_EQ(base::File::FILE_OK, same_file.error_details());\n\n  // Close both handles and check that the file is gone.\n  file.Close();\n  same_file.Close();\n  EXPECT_FALSE(base::PathExists(file_path));\n}\n\nTEST(FileTest, ReadWrite) {\n  base::ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n  FilePath file_path = temp_dir.GetPath().Append(\"read_write_file\");\n  File file(file_path,\n            base::File::FLAG_CREATE | base::File::FLAG_READ |\n                base::File::FLAG_WRITE);\n  ASSERT_TRUE(file.IsValid());\n\n  char data_to_write[] = \"test\";\n  const int kTestDataSize = 4;\n\n  // Write 0 bytes to the file.\n  int bytes_written = file.Write(0, data_to_write, 0);\n  EXPECT_EQ(0, bytes_written);\n\n  // Write 0 bytes, with buf=nullptr.\n  bytes_written = file.Write(0, nullptr, 0);\n  EXPECT_EQ(0, bytes_written);\n\n  // Write \"test\" to the file.\n  bytes_written = file.Write(0, data_to_write, kTestDataSize);\n  EXPECT_EQ(kTestDataSize, bytes_written);\n\n  // Read from EOF.\n  char data_read_1[32];\n  int bytes_read = file.Read(kTestDataSize, data_read_1, kTestDataSize);\n  EXPECT_EQ(0, bytes_read);\n\n  // Read from somewhere in the middle of the file.\n  const int kPartialReadOffset = 1;\n  bytes_read = file.Read(kPartialReadOffset, data_read_1, kTestDataSize);\n  EXPECT_EQ(kTestDataSize - kPartialReadOffset, bytes_read);\n  for (int i = 0; i < bytes_read; i++)\n    EXPECT_EQ(data_to_write[i + kPartialReadOffset], data_read_1[i]);\n\n  // Read 0 bytes.\n  bytes_read = file.Read(0, data_read_1, 0);\n  EXPECT_EQ(0, bytes_read);\n\n  // Read the entire file.\n  bytes_read = file.Read(0, data_read_1, kTestDataSize);\n  EXPECT_EQ(kTestDataSize, bytes_read);\n  for (int i = 0; i < bytes_read; i++)\n    EXPECT_EQ(data_to_write[i], data_read_1[i]);\n\n  // Read again, but using the trivial native wrapper.\n  bytes_read = file.ReadNoBestEffort(0, data_read_1, kTestDataSize);\n  EXPECT_LE(bytes_read, kTestDataSize);\n  for (int i = 0; i < bytes_read; i++)\n    EXPECT_EQ(data_to_write[i], data_read_1[i]);\n\n  // Write past the end of the file.\n  const int kOffsetBeyondEndOfFile = 10;\n  const int kPartialWriteLength = 2;\n  bytes_written = file.Write(kOffsetBeyondEndOfFile,\n                             data_to_write, kPartialWriteLength);\n  EXPECT_EQ(kPartialWriteLength, bytes_written);\n\n  // Make sure the file was extended.\n  int64_t file_size = 0;\n  EXPECT_TRUE(GetFileSize(file_path, &file_size));\n  EXPECT_EQ(kOffsetBeyondEndOfFile + kPartialWriteLength, file_size);\n\n  // Make sure the file was zero-padded.\n  char data_read_2[32];\n  bytes_read = file.Read(0, data_read_2, static_cast<int>(file_size));\n  EXPECT_EQ(file_size, bytes_read);\n  for (int i = 0; i < kTestDataSize; i++)\n    EXPECT_EQ(data_to_write[i], data_read_2[i]);\n  for (int i = kTestDataSize; i < kOffsetBeyondEndOfFile; i++)\n    EXPECT_EQ(0, data_read_2[i]);\n  for (int i = kOffsetBeyondEndOfFile; i < file_size; i++)\n    EXPECT_EQ(data_to_write[i - kOffsetBeyondEndOfFile], data_read_2[i]);\n}\n\nTEST(FileTest, GetLastFileError) {\n#if BUILDFLAG(IS_WIN)\n  ::SetLastError(ERROR_ACCESS_DENIED);\n#else\n  errno = EACCES;\n#endif\n  EXPECT_EQ(File::FILE_ERROR_ACCESS_DENIED, File::GetLastFileError());\n\n  base::ScopedTempDir temp_dir;\n  EXPECT_TRUE(temp_dir.CreateUniqueTempDir());\n\n  FilePath nonexistent_path(temp_dir.GetPath().Append(\"nonexistent\"));\n  File file(nonexistent_path, File::FLAG_OPEN | File::FLAG_READ);\n  File::Error last_error = File::GetLastFileError();\n  EXPECT_FALSE(file.IsValid());\n  EXPECT_EQ(File::FILE_ERROR_NOT_FOUND, file.error_details());\n  EXPECT_EQ(File::FILE_ERROR_NOT_FOUND, last_error);\n}\n\nTEST(FileTest, Append) {\n  base::ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n  FilePath file_path = temp_dir.GetPath().Append(\"append_file\");\n  File file(file_path, base::File::FLAG_CREATE | base::File::FLAG_APPEND);\n  ASSERT_TRUE(file.IsValid());\n\n  char data_to_write[] = \"test\";\n  const int kTestDataSize = 4;\n\n  // Write 0 bytes to the file.\n  int bytes_written = file.Write(0, data_to_write, 0);\n  EXPECT_EQ(0, bytes_written);\n\n  // Write 0 bytes, with buf=nullptr.\n  bytes_written = file.Write(0, nullptr, 0);\n  EXPECT_EQ(0, bytes_written);\n\n  // Write \"test\" to the file.\n  bytes_written = file.Write(0, data_to_write, kTestDataSize);\n  EXPECT_EQ(kTestDataSize, bytes_written);\n\n  file.Close();\n  File file2(file_path,\n             base::File::FLAG_OPEN | base::File::FLAG_READ |\n                 base::File::FLAG_APPEND);\n  ASSERT_TRUE(file2.IsValid());\n\n  // Test passing the file around.\n  file = std::move(file2);\n  EXPECT_FALSE(file2.IsValid());\n  ASSERT_TRUE(file.IsValid());\n\n  char append_data_to_write[] = \"78\";\n  const int kAppendDataSize = 2;\n\n  // Append \"78\" to the file.\n  bytes_written = file.Write(0, append_data_to_write, kAppendDataSize);\n  EXPECT_EQ(kAppendDataSize, bytes_written);\n\n  // Read the entire file.\n  char data_read_1[32];\n  int bytes_read = file.Read(0, data_read_1,\n                             kTestDataSize + kAppendDataSize);\n  EXPECT_EQ(kTestDataSize + kAppendDataSize, bytes_read);\n  for (int i = 0; i < kTestDataSize; i++)\n    EXPECT_EQ(data_to_write[i], data_read_1[i]);\n  for (int i = 0; i < kAppendDataSize; i++)\n    EXPECT_EQ(append_data_to_write[i], data_read_1[kTestDataSize + i]);\n}\n\n\nTEST(FileTest, Length) {\n  base::ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n  FilePath file_path = temp_dir.GetPath().Append(\"truncate_file\");\n  File file(file_path,\n            base::File::FLAG_CREATE | base::File::FLAG_READ |\n                base::File::FLAG_WRITE);\n  ASSERT_TRUE(file.IsValid());\n  EXPECT_EQ(0, file.GetLength());\n\n  // Write \"test\" to the file.\n  char data_to_write[] = \"test\";\n  int kTestDataSize = 4;\n  int bytes_written = file.Write(0, data_to_write, kTestDataSize);\n  EXPECT_EQ(kTestDataSize, bytes_written);\n\n  // Extend the file.\n  const int kExtendedFileLength = 10;\n  int64_t file_size = 0;\n  EXPECT_TRUE(file.SetLength(kExtendedFileLength));\n  EXPECT_EQ(kExtendedFileLength, file.GetLength());\n  EXPECT_TRUE(GetFileSize(file_path, &file_size));\n  EXPECT_EQ(kExtendedFileLength, file_size);\n\n  // Make sure the file was zero-padded.\n  char data_read[32];\n  int bytes_read = file.Read(0, data_read, static_cast<int>(file_size));\n  EXPECT_EQ(file_size, bytes_read);\n  for (int i = 0; i < kTestDataSize; i++)\n    EXPECT_EQ(data_to_write[i], data_read[i]);\n  for (int i = kTestDataSize; i < file_size; i++)\n    EXPECT_EQ(0, data_read[i]);\n\n  // Truncate the file.\n  const int kTruncatedFileLength = 2;\n  EXPECT_TRUE(file.SetLength(kTruncatedFileLength));\n  EXPECT_EQ(kTruncatedFileLength, file.GetLength());\n  EXPECT_TRUE(GetFileSize(file_path, &file_size));\n  EXPECT_EQ(kTruncatedFileLength, file_size);\n\n  // Make sure the file was truncated.\n  bytes_read = file.Read(0, data_read, kTestDataSize);\n  EXPECT_EQ(file_size, bytes_read);\n  for (int i = 0; i < file_size; i++)\n    EXPECT_EQ(data_to_write[i], data_read[i]);\n\n#if !BUILDFLAG(IS_FUCHSIA)  // Fuchsia doesn't seem to support big files.\n  // Expand the file past the 4 GB limit.\n  const int64_t kBigFileLength = 5'000'000'000;\n  EXPECT_TRUE(file.SetLength(kBigFileLength));\n  EXPECT_EQ(kBigFileLength, file.GetLength());\n  EXPECT_TRUE(GetFileSize(file_path, &file_size));\n  EXPECT_EQ(kBigFileLength, file_size);\n#endif\n\n  // Close the file and reopen with base::File::FLAG_CREATE_ALWAYS, and make\n  // sure the file is empty (old file was overridden).\n  file.Close();\n  file.Initialize(file_path,\n                  base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);\n  EXPECT_EQ(0, file.GetLength());\n}\n\n// Flakily fails: http://crbug.com/86494\n#if BUILDFLAG(IS_ANDROID)\nTEST(FileTest, TouchGetInfo) {\n#else\nTEST(FileTest, DISABLED_TouchGetInfo) {\n#endif\n  base::ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n  File file(temp_dir.GetPath().Append(\"touch_get_info_file\"),\n            base::File::FLAG_CREATE | base::File::FLAG_WRITE |\n                base::File::FLAG_WRITE_ATTRIBUTES);\n  ASSERT_TRUE(file.IsValid());\n\n  // Get info for a newly created file.\n  base::File::Info info;\n  EXPECT_TRUE(file.GetInfo(&info));\n\n  // Add 2 seconds to account for possible rounding errors on\n  // filesystems that use a 1s or 2s timestamp granularity.\n  base::Time now = base::Time::Now() + base::Seconds(2);\n  EXPECT_EQ(0, info.size);\n  EXPECT_FALSE(info.is_directory);\n  EXPECT_FALSE(info.is_symbolic_link);\n  EXPECT_LE(info.last_accessed.ToDeltaSinceUnixEpoch(), now.ToDeltaSinceUnixEpoch());\n  EXPECT_LE(info.last_modified.ToDeltaSinceUnixEpoch(), now.ToDeltaSinceUnixEpoch());\n  EXPECT_LE(info.creation_time.ToDeltaSinceUnixEpoch(), now.ToDeltaSinceUnixEpoch());\n  base::Time creation_time = info.creation_time;\n\n  // Write \"test\" to the file.\n  char data[] = \"test\";\n  const int kTestDataSize = 4;\n  int bytes_written = file.Write(0, data, kTestDataSize);\n  EXPECT_EQ(kTestDataSize, bytes_written);\n\n  // Change the last_accessed and last_modified dates.\n  // It's best to add values that are multiples of 2 (in seconds)\n  // to the current last_accessed and last_modified times, because\n  // FATxx uses a 2s timestamp granularity.\n  base::Time new_last_accessed = info.last_accessed + base::Seconds(234);\n  base::Time new_last_modified = info.last_modified + base::Minutes(567);\n\n  EXPECT_TRUE(file.SetTimes(new_last_accessed, new_last_modified));\n\n  // Make sure the file info was updated accordingly.\n  EXPECT_TRUE(file.GetInfo(&info));\n  EXPECT_EQ(info.size, kTestDataSize);\n  EXPECT_FALSE(info.is_directory);\n  EXPECT_FALSE(info.is_symbolic_link);\n\n  // ext2/ext3 and HPS/HPS+ seem to have a timestamp granularity of 1s.\n#if BUILDFLAG(IS_POSIX)\n  EXPECT_EQ(info.last_accessed.ToTimeVal().tv_sec,\n            new_last_accessed.ToTimeVal().tv_sec);\n  EXPECT_EQ(info.last_modified.ToTimeVal().tv_sec,\n            new_last_modified.ToTimeVal().tv_sec);\n#else\n  EXPECT_EQ(info.last_accessed.ToDeltaSinceUnixEpoch(),\n            new_last_accessed.ToDeltaSinceUnixEpoch());\n  EXPECT_EQ(info.last_modified.ToDeltaSinceUnixEpoch(),\n            new_last_modified.ToDeltaSinceUnixEpoch());\n#endif\n\n  EXPECT_EQ(info.creation_time.ToDeltaSinceUnixEpoch(),\n            creation_time.ToDeltaSinceUnixEpoch());\n}\n\n// Test we can retrieve the file's creation time through File::GetInfo().\nTEST(FileTest, GetInfoForCreationTime) {\n  int64_t before_creation_time_s =\n      base::Time::Now().ToDeltaSinceUnixEpoch().InSeconds();\n\n  base::ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n  FilePath file_path = temp_dir.GetPath().Append(\"test_file\");\n  File file(file_path, base::File::FLAG_CREATE | base::File::FLAG_READ |\n                           base::File::FLAG_WRITE);\n  EXPECT_TRUE(file.IsValid());\n\n  int64_t after_creation_time_s =\n      base::Time::Now().ToDeltaSinceUnixEpoch().InSeconds();\n\n  base::File::Info info;\n  EXPECT_TRUE(file.GetInfo(&info));\n  EXPECT_GE(info.creation_time.ToDeltaSinceUnixEpoch().InSeconds(),\n            before_creation_time_s);\n  EXPECT_LE(info.creation_time.ToDeltaSinceUnixEpoch().InSeconds(),\n            after_creation_time_s);\n}\n\nTEST(FileTest, ReadAtCurrentPosition) {\n  base::ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n  FilePath file_path =\n      temp_dir.GetPath().Append(\"read_at_current_position\");\n  File file(file_path,\n            base::File::FLAG_CREATE | base::File::FLAG_READ |\n                base::File::FLAG_WRITE);\n  EXPECT_TRUE(file.IsValid());\n\n  const char kData[] = \"test\";\n  const int kDataSize = sizeof(kData) - 1;\n  EXPECT_EQ(kDataSize, file.Write(0, kData, kDataSize));\n\n  EXPECT_EQ(0, file.Seek(base::File::FROM_BEGIN, 0));\n\n  char buffer[kDataSize];\n  int first_chunk_size = kDataSize / 2;\n  EXPECT_EQ(first_chunk_size, file.ReadAtCurrentPos(buffer, first_chunk_size));\n  EXPECT_EQ(kDataSize - first_chunk_size,\n            file.ReadAtCurrentPos(buffer + first_chunk_size,\n                                  kDataSize - first_chunk_size));\n  EXPECT_EQ(std::string(buffer, buffer + kDataSize), std::string(kData));\n}\n\nTEST(FileTest, WriteAtCurrentPosition) {\n  base::ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n  FilePath file_path =\n      temp_dir.GetPath().Append(\"write_at_current_position\");\n  File file(file_path,\n            base::File::FLAG_CREATE | base::File::FLAG_READ |\n                base::File::FLAG_WRITE);\n  EXPECT_TRUE(file.IsValid());\n\n  const char kData[] = \"test\";\n  const int kDataSize = sizeof(kData) - 1;\n\n  int first_chunk_size = kDataSize / 2;\n  EXPECT_EQ(first_chunk_size, file.WriteAtCurrentPos(kData, first_chunk_size));\n  EXPECT_EQ(kDataSize - first_chunk_size,\n            file.WriteAtCurrentPos(kData + first_chunk_size,\n                                   kDataSize - first_chunk_size));\n\n  char buffer[kDataSize];\n  EXPECT_EQ(kDataSize, file.Read(0, buffer, kDataSize));\n  EXPECT_EQ(std::string(buffer, buffer + kDataSize), std::string(kData));\n}\n\nTEST(FileTest, Seek) {\n  base::ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n  FilePath file_path = temp_dir.GetPath().Append(\"seek_file\");\n  File file(file_path,\n            base::File::FLAG_CREATE | base::File::FLAG_READ |\n                base::File::FLAG_WRITE);\n  ASSERT_TRUE(file.IsValid());\n\n  const int64_t kOffset = 10;\n  EXPECT_EQ(kOffset, file.Seek(base::File::FROM_BEGIN, kOffset));\n  EXPECT_EQ(2 * kOffset, file.Seek(base::File::FROM_CURRENT, kOffset));\n  EXPECT_EQ(kOffset, file.Seek(base::File::FROM_CURRENT, -kOffset));\n  EXPECT_TRUE(file.SetLength(kOffset * 2));\n  EXPECT_EQ(kOffset, file.Seek(base::File::FROM_END, -kOffset));\n}\n\nTEST(FileTest, Duplicate) {\n  base::ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n  FilePath file_path = temp_dir.GetPath().Append(\"file\");\n  File file(file_path, (base::File::FLAG_CREATE |\n                        base::File::FLAG_READ |\n                        base::File::FLAG_WRITE));\n  ASSERT_TRUE(file.IsValid());\n\n  File file2(file.Duplicate());\n  ASSERT_TRUE(file2.IsValid());\n\n  // Write through one handle, close it, read through the other.\n  static const char kData[] = \"now is a good time.\";\n  static const int kDataLen = sizeof(kData) - 1;\n\n  ASSERT_EQ(0, file.Seek(base::File::FROM_CURRENT, 0));\n  ASSERT_EQ(0, file2.Seek(base::File::FROM_CURRENT, 0));\n  ASSERT_EQ(kDataLen, file.WriteAtCurrentPos(kData, kDataLen));\n  ASSERT_EQ(kDataLen, file.Seek(base::File::FROM_CURRENT, 0));\n  ASSERT_EQ(kDataLen, file2.Seek(base::File::FROM_CURRENT, 0));\n  file.Close();\n  char buf[kDataLen];\n  ASSERT_EQ(kDataLen, file2.Read(0, &buf[0], kDataLen));\n  ASSERT_EQ(std::string(kData, kDataLen), std::string(&buf[0], kDataLen));\n}\n\nTEST(FileTest, DuplicateDeleteOnClose) {\n  base::ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n  FilePath file_path = temp_dir.GetPath().Append(\"file\");\n  File file(file_path, (base::File::FLAG_CREATE |\n                        base::File::FLAG_READ |\n                        base::File::FLAG_WRITE |\n                        base::File::FLAG_DELETE_ON_CLOSE));\n  ASSERT_TRUE(file.IsValid());\n  File file2(file.Duplicate());\n  ASSERT_TRUE(file2.IsValid());\n  file.Close();\n  file2.Close();\n  ASSERT_FALSE(base::PathExists(file_path));\n}\n\n// TODO(chokobole):\n// #if BUILDFLAG(ENABLE_BASE_TRACING)\n// TEST(FileTest, TracedValueSupport) {\n//   base::ScopedTempDir temp_dir;\n//   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n//   FilePath file_path = temp_dir.GetPath().Append(\"file\");\n\n//   File file(file_path,\n//             (base::File::FLAG_CREATE | base::File::FLAG_READ |\n//              base::File::FLAG_WRITE | base::File::FLAG_DELETE_ON_CLOSE));\n//   ASSERT_TRUE(file.IsValid());\n\n//   EXPECT_EQ(perfetto::TracedValueToString(file),\n//             \"{is_valid:true,created:true,async:false,error_details:FILE_OK}\");\n// }\n// #endif  // BUILDFLAG(ENABLE_BASE_TRACING)\n\n#if BUILDFLAG(IS_WIN)\n// Flakily times out on Windows, see http://crbug.com/846276.\n#define MAYBE_WriteDataToLargeOffset DISABLED_WriteDataToLargeOffset\n#else\n#define MAYBE_WriteDataToLargeOffset WriteDataToLargeOffset\n#endif\nTEST(FileTest, MAYBE_WriteDataToLargeOffset) {\n  base::ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n  FilePath file_path = temp_dir.GetPath().Append(\"file\");\n  File file(file_path,\n            (base::File::FLAG_CREATE | base::File::FLAG_READ |\n             base::File::FLAG_WRITE | base::File::FLAG_DELETE_ON_CLOSE));\n  ASSERT_TRUE(file.IsValid());\n\n  const char kData[] = \"this file is sparse.\";\n  const int kDataLen = sizeof(kData) - 1;\n  const int64_t kLargeFileOffset = (1LL << 31);\n\n  // If the file fails to write, it is probably we are running out of disk space\n  // and the file system doesn't support sparse file.\n  if (file.Write(kLargeFileOffset - kDataLen - 1, kData, kDataLen) < 0)\n    return;\n\n  ASSERT_EQ(kDataLen, file.Write(kLargeFileOffset + 1, kData, kDataLen));\n}\n\nTEST(FileTest, AddFlagsForPassingToUntrustedProcess) {\n  {\n    uint32_t flags = base::File::FLAG_OPEN | base::File::FLAG_READ;\n    flags = base::File::AddFlagsForPassingToUntrustedProcess(flags);\n    EXPECT_EQ(flags, base::File::FLAG_OPEN | base::File::FLAG_READ);\n  }\n  {\n    uint32_t flags = base::File::FLAG_OPEN | base::File::FLAG_WRITE;\n    flags = base::File::AddFlagsForPassingToUntrustedProcess(flags);\n    EXPECT_EQ(flags, base::File::FLAG_OPEN | base::File::FLAG_WRITE |\n                         base::File::FLAG_WIN_NO_EXECUTE);\n  }\n}\n\n#if BUILDFLAG(IS_WIN)\nTEST(FileTest, GetInfoForDirectory) {\n  base::ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n  FilePath empty_dir =\n      temp_dir.GetPath().Append(FILE_PATH_LITERAL(\"gpfi_test\"));\n  ASSERT_TRUE(CreateDirectory(empty_dir));\n\n  base::File dir(\n      ::CreateFile(empty_dir.value().c_str(), GENERIC_READ | GENERIC_WRITE,\n                   FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,\n                   OPEN_EXISTING,\n                   FILE_FLAG_BACKUP_SEMANTICS,  // Needed to open a directory.\n                   NULL));\n  ASSERT_TRUE(dir.IsValid());\n\n  base::File::Info info;\n  EXPECT_TRUE(dir.GetInfo(&info));\n  EXPECT_TRUE(info.is_directory);\n  EXPECT_FALSE(info.is_symbolic_link);\n  EXPECT_EQ(0, info.size);\n}\n\nTEST(FileTest, DeleteNoop) {\n  base::ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n  FilePath file_path = temp_dir.GetPath().Append(\"file\");\n\n  // Creating and closing a file with DELETE perms should do nothing special.\n  File file(file_path,\n            (base::File::FLAG_CREATE | base::File::FLAG_READ |\n             base::File::FLAG_WRITE | base::File::FLAG_CAN_DELETE_ON_CLOSE));\n  ASSERT_TRUE(file.IsValid());\n  file.Close();\n  ASSERT_TRUE(base::PathExists(file_path));\n}\n\nTEST(FileTest, Delete) {\n  base::ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n  FilePath file_path = temp_dir.GetPath().Append(\"file\");\n\n  // Creating a file with DELETE and then marking for delete on close should\n  // delete it.\n  File file(file_path,\n            (base::File::FLAG_CREATE | base::File::FLAG_READ |\n             base::File::FLAG_WRITE | base::File::FLAG_CAN_DELETE_ON_CLOSE));\n  ASSERT_TRUE(file.IsValid());\n  ASSERT_TRUE(file.DeleteOnClose(true));\n  file.Close();\n  ASSERT_FALSE(base::PathExists(file_path));\n}\n\nTEST(FileTest, DeleteThenRevoke) {\n  base::ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n  FilePath file_path = temp_dir.GetPath().Append(\"file\");\n\n  // Creating a file with DELETE, marking it for delete, then clearing delete on\n  // close should not delete it.\n  File file(file_path,\n            (base::File::FLAG_CREATE | base::File::FLAG_READ |\n             base::File::FLAG_WRITE | base::File::FLAG_CAN_DELETE_ON_CLOSE));\n  ASSERT_TRUE(file.IsValid());\n  ASSERT_TRUE(file.DeleteOnClose(true));\n  ASSERT_TRUE(file.DeleteOnClose(false));\n  file.Close();\n  ASSERT_TRUE(base::PathExists(file_path));\n}\n\nTEST(FileTest, IrrevokableDeleteOnClose) {\n  base::ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n  FilePath file_path = temp_dir.GetPath().Append(\"file\");\n\n  // DELETE_ON_CLOSE cannot be revoked by this opener.\n  File file(file_path,\n            (base::File::FLAG_CREATE | base::File::FLAG_READ |\n             base::File::FLAG_WRITE | base::File::FLAG_DELETE_ON_CLOSE |\n             base::File::FLAG_WIN_SHARE_DELETE |\n             base::File::FLAG_CAN_DELETE_ON_CLOSE));\n  ASSERT_TRUE(file.IsValid());\n  // https://msdn.microsoft.com/library/windows/desktop/aa364221.aspx says that\n  // setting the dispositon has no effect if the handle was opened with\n  // FLAG_DELETE_ON_CLOSE. Do not make the test's success dependent on whether\n  // or not SetFileInformationByHandle indicates success or failure. (It happens\n  // to indicate success on Windows 10.)\n  file.DeleteOnClose(false);\n  file.Close();\n  ASSERT_FALSE(base::PathExists(file_path));\n}\n\nTEST(FileTest, IrrevokableDeleteOnCloseOther) {\n  base::ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n  FilePath file_path = temp_dir.GetPath().Append(\"file\");\n\n  // DELETE_ON_CLOSE cannot be revoked by another opener.\n  File file(file_path,\n            (base::File::FLAG_CREATE | base::File::FLAG_READ |\n             base::File::FLAG_WRITE | base::File::FLAG_DELETE_ON_CLOSE |\n             base::File::FLAG_WIN_SHARE_DELETE |\n             base::File::FLAG_CAN_DELETE_ON_CLOSE));\n  ASSERT_TRUE(file.IsValid());\n\n  File file2(file_path,\n             (base::File::FLAG_OPEN | base::File::FLAG_READ |\n              base::File::FLAG_WRITE | base::File::FLAG_WIN_SHARE_DELETE |\n              base::File::FLAG_CAN_DELETE_ON_CLOSE));\n  ASSERT_TRUE(file2.IsValid());\n\n  file2.DeleteOnClose(false);\n  file2.Close();\n  ASSERT_TRUE(base::PathExists(file_path));\n  file.Close();\n  ASSERT_FALSE(base::PathExists(file_path));\n}\n\nTEST(FileTest, DeleteWithoutPermission) {\n  base::ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n  FilePath file_path = temp_dir.GetPath().Append(\"file\");\n\n  // It should not be possible to mark a file for deletion when it was not\n  // created/opened with DELETE.\n  File file(file_path, (base::File::FLAG_CREATE | base::File::FLAG_READ |\n                        base::File::FLAG_WRITE));\n  ASSERT_TRUE(file.IsValid());\n  ASSERT_FALSE(file.DeleteOnClose(true));\n  file.Close();\n  ASSERT_TRUE(base::PathExists(file_path));\n}\n\nTEST(FileTest, UnsharedDeleteOnClose) {\n  base::ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n  FilePath file_path = temp_dir.GetPath().Append(\"file\");\n\n  // Opening with DELETE_ON_CLOSE when a previous opener hasn't enabled sharing\n  // will fail.\n  File file(file_path, (base::File::FLAG_CREATE | base::File::FLAG_READ |\n                        base::File::FLAG_WRITE));\n  ASSERT_TRUE(file.IsValid());\n  File file2(\n      file_path,\n      (base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE |\n       base::File::FLAG_DELETE_ON_CLOSE | base::File::FLAG_WIN_SHARE_DELETE));\n  ASSERT_FALSE(file2.IsValid());\n\n  file.Close();\n  ASSERT_TRUE(base::PathExists(file_path));\n}\n\nTEST(FileTest, NoDeleteOnCloseWithMappedFile) {\n  base::ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n  FilePath file_path = temp_dir.GetPath().Append(\"file\");\n\n  // Mapping a file into memory blocks DeleteOnClose.\n  File file(file_path,\n            (base::File::FLAG_CREATE | base::File::FLAG_READ |\n             base::File::FLAG_WRITE | base::File::FLAG_CAN_DELETE_ON_CLOSE));\n  ASSERT_TRUE(file.IsValid());\n  ASSERT_EQ(5, file.WriteAtCurrentPos(\"12345\", 5));\n\n  {\n    base::MemoryMappedFile mapping;\n    ASSERT_TRUE(mapping.Initialize(file.Duplicate()));\n    ASSERT_EQ(5U, mapping.length());\n\n    EXPECT_FALSE(file.DeleteOnClose(true));\n  }\n\n  file.Close();\n  ASSERT_TRUE(base::PathExists(file_path));\n}\n\n// Check that we handle the async bit being set incorrectly in a sane way.\nTEST(FileTest, UseSyncApiWithAsyncFile) {\n  base::ScopedTempDir temp_dir;\n  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());\n  FilePath file_path = temp_dir.GetPath().Append(\"file\");\n\n  File file(file_path, base::File::FLAG_CREATE | base::File::FLAG_WRITE |\n                           base::File::FLAG_ASYNC);\n  File lying_file(file.TakePlatformFile(), false /* async */);\n  ASSERT_TRUE(lying_file.IsValid());\n\n  ASSERT_EQ(lying_file.WriteAtCurrentPos(\"12345\", 5), -1);\n}\n\nTEST(FileDeathTest, InvalidFlags) {\n  EXPECT_CHECK_DEATH_WITH(\n      {\n        // When this test is running as Admin, TMP gets ignored and temporary\n        // files/folders are created in %ProgramFiles%. This means that the\n        // temporary folder created by the death test never gets deleted, as it\n        // crashes before the `base::ScopedTempDir` goes out of scope and also\n        // does not get automatically cleaned by by the test runner.\n        //\n        // To avoid this from happening, this death test explicitly creates the\n        // temporary folder in TMP, which is set by the test runner parent\n        // process to a temporary folder for the test. This means that the\n        // folder created here is always deleted during test runner cleanup.\n        std::string tmp_folder;\n        ASSERT_TRUE(base::Environment::Create()->GetVar(\"TMP\", &tmp_folder));\n        base::ScopedTempDir temp_dir;\n        ASSERT_TRUE(temp_dir.CreateUniqueTempDirUnderPath(\n            base::FilePath(base::UTF8ToWide(tmp_folder))));\n        FilePath file_path = temp_dir.GetPath().Append(\"file\");\n\n        File file(file_path,\n                  base::File::FLAG_CREATE | base::File::FLAG_WIN_EXECUTE |\n                      base::File::FLAG_READ | base::File::FLAG_WIN_NO_EXECUTE);\n      },\n      \"FLAG_WIN_NO_EXECUTE\");\n}\n#endif  // BUILDFLAG(IS_WIN)\n\n}  // namespace tachyon\n"
  },
  {
    "path": "tachyon/base/files/file_util.cc",
    "content": "// Copyright 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/files/file_util.h\"\n\n#include \"tachyon/build/build_config.h\"\n\n#if BUILDFLAG(IS_WIN)\n#include <io.h>\n#endif\n#include <stdio.h>\n\n#include <fstream>\n#include <limits>\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"absl/types/span.h\"\n#include \"absl/strings/str_format.h\"\n\n#include \"tachyon/base/bit_cast.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/files/file_enumerator.h\"\n#include \"tachyon/base/files/file_path.h\"\n#include \"tachyon/base/functional/function_ref.h\"\n#include \"tachyon/base/posix/eintr_wrapper.h\"\n#include \"tachyon/base/strings/string_util.h\"\n// #include \"tachyon/base/strings/utf_string_conversions.h\"\n// #include \"tachyon/base/task/bind_post_task.h\"\n// #include \"tachyon/base/task/sequenced_task_runner.h\"\n// #include \"tachyon/base/threading/scoped_blocking_call.h\"\n\n#if BUILDFLAG(IS_WIN)\n#include <windows.h>\n#endif\n\nnamespace tachyon::base {\n\nnamespace {\n\n#if !BUILDFLAG(IS_WIN)\n\n/*\nTODO(chokobole):\nvoid RunAndReply(OnceCallback<bool()> action_callback,\n                 OnceCallback<void(bool)> reply_callback) {\n  bool result = std::move(action_callback).Run();\n  if (!reply_callback.is_null())\n    std::move(reply_callback).Run(result);\n}\n*/\n\n#endif  // !BUILDFLAG(IS_WIN)\n\nbool ReadStreamToSpanWithMaxSize(\n    FILE* stream,\n    size_t max_size,\n    FunctionRef<absl::Span<uint8_t>(size_t)> resize_span) {\n  if (!stream) {\n    return false;\n  }\n\n  // Seeking to the beginning is best-effort -- it is expected to fail for\n  // certain non-file stream (e.g., pipes).\n  HANDLE_EINTR(fseek(stream, 0, SEEK_SET));\n\n  // Many files have incorrect size (proc files etc). Hence, the file is read\n  // sequentially as opposed to a one-shot read, using file size as a hint for\n  // chunk size if available.\n  constexpr size_t kDefaultChunkSize = 1 << 16;\n  size_t chunk_size = kDefaultChunkSize - 1;\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n#if BUILDFLAG(IS_WIN)\n  BY_HANDLE_FILE_INFORMATION file_info = {};\n  if (::GetFileInformationByHandle(\n          reinterpret_cast<HANDLE>(_get_osfhandle(_fileno(stream))),\n          &file_info)) {\n    LARGE_INTEGER size;\n    size.HighPart = static_cast<LONG>(file_info.nFileSizeHigh);\n    size.LowPart = file_info.nFileSizeLow;\n    if (size.QuadPart > 0)\n      chunk_size = static_cast<size_t>(size.QuadPart);\n  }\n#else   // BUILDFLAG(IS_WIN)\n  // In cases where the reported file size is 0, use a smaller chunk size to\n  // minimize memory allocated and cost of string::resize() in case the read\n  // size is small (i.e. proc files). If the file is larger than this, the read\n  // loop will reset |chunk_size| to kDefaultChunkSize.\n  constexpr size_t kSmallChunkSize = 4096;\n  chunk_size = kSmallChunkSize - 1;\n  stat_wrapper_t file_info = {};\n  if (!File::Fstat(fileno(stream), &file_info) && file_info.st_size > 0)\n    chunk_size = static_cast<size_t>(file_info.st_size);\n#endif  // BUILDFLAG(IS_WIN)\n\n  // We need to attempt to read at EOF for feof flag to be set so here we use\n  // |chunk_size| + 1.\n  chunk_size = std::min(chunk_size, max_size) + 1;\n  size_t bytes_read_this_pass;\n  size_t bytes_read_so_far = 0;\n  bool read_status = true;\n  absl::Span<uint8_t> bytes_span = resize_span(chunk_size);\n  DCHECK_EQ(bytes_span.size(), chunk_size);\n\n  while ((bytes_read_this_pass = fread(bytes_span.data() + bytes_read_so_far, 1,\n                                       chunk_size, stream)) > 0) {\n    if ((max_size - bytes_read_so_far) < bytes_read_this_pass) {\n      // Read more than max_size bytes, bail out.\n      bytes_read_so_far = max_size;\n      read_status = false;\n      break;\n    }\n    // In case EOF was not reached, iterate again but revert to the default\n    // chunk size.\n    if (bytes_read_so_far == 0)\n      chunk_size = kDefaultChunkSize;\n\n    bytes_read_so_far += bytes_read_this_pass;\n    // Last fread syscall (after EOF) can be avoided via feof, which is just a\n    // flag check.\n    if (feof(stream))\n      break;\n    bytes_span = resize_span(bytes_read_so_far + chunk_size);\n    DCHECK_EQ(bytes_span.size(), bytes_read_so_far + chunk_size);\n  }\n  read_status = read_status && !ferror(stream);\n\n  // Trim the container down to the number of bytes that were actually read.\n  bytes_span = resize_span(bytes_read_so_far);\n  DCHECK_EQ(bytes_span.size(), bytes_read_so_far);\n\n  return read_status;\n}\n\n}  // namespace\n\n#if !BUILDFLAG(IS_WIN)\n\n/*\nTODO(chokobole):\nOnceClosure GetDeleteFileCallback(const FilePath& path,\n                                  OnceCallback<void(bool)> reply_callback) {\n  return BindOnce(&RunAndReply, BindOnce(&DeleteFile, path),\n                  reply_callback.is_null()\n                      ? std::move(reply_callback)\n                      : BindPostTask(SequencedTaskRunner::GetCurrentDefault(),\n                                     std::move(reply_callback)));\n}\n\nOnceClosure GetDeletePathRecursivelyCallback(\n    const FilePath& path,\n    OnceCallback<void(bool)> reply_callback) {\n  return BindOnce(&RunAndReply, BindOnce(&DeletePathRecursively, path),\n                  reply_callback.is_null()\n                      ? std::move(reply_callback)\n                      : BindPostTask(SequencedTaskRunner::GetCurrentDefault(),\n                                     std::move(reply_callback)));\n}\n*/\n\n#endif  // !BUILDFLAG(IS_WIN)\n\nint64_t ComputeDirectorySize(const FilePath& root_path) {\n  int64_t running_size = 0;\n  FileEnumerator file_iter(root_path, true, FileEnumerator::FILES);\n  while (!file_iter.Next().empty())\n    running_size += file_iter.GetInfo().GetSize();\n  return running_size;\n}\n\nbool Move(const FilePath& from_path, const FilePath& to_path) {\n  if (from_path.ReferencesParent() || to_path.ReferencesParent())\n    return false;\n  return internal::MoveUnsafe(from_path, to_path);\n}\n\nbool CopyFileContents(File& infile, File& outfile) {\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n  bool retry_slow = false;\n  bool res =\n      internal::CopyFileContentsWithSendfile(infile, outfile, retry_slow);\n  if (res || !retry_slow) {\n    return res;\n  }\n  // Any failures which allow retrying using read/write will not have modified\n  // either file offset or size.\n#endif\n\n  static constexpr size_t kBufferSize = 32768;\n  std::vector<char> buffer(kBufferSize);\n\n  for (;;) {\n    int bytes_read =\n        infile.ReadAtCurrentPos(buffer.data(), static_cast<int>(buffer.size()));\n    if (bytes_read < 0) {\n      return false;\n    }\n    if (bytes_read == 0) {\n      return true;\n    }\n    // Allow for partial writes\n    int bytes_written_per_read = 0;\n    do {\n      int bytes_written_partial = outfile.WriteAtCurrentPos(\n          &buffer[static_cast<size_t>(bytes_written_per_read)],\n          bytes_read - bytes_written_per_read);\n      if (bytes_written_partial < 0) {\n        return false;\n      }\n\n      bytes_written_per_read += bytes_written_partial;\n    } while (bytes_written_per_read < bytes_read);\n  }\n\n  NOTREACHED();\n  return false;\n}\n\nbool ContentsEqual(const FilePath& filename1, const FilePath& filename2) {\n  // We open the file in binary format even if they are text files because\n  // we are just comparing that bytes are exactly same in both files and not\n  // doing anything smart with text formatting.\n#if BUILDFLAG(IS_WIN)\n  std::ifstream file1(filename1.value().c_str(),\n                      std::ios::in | std::ios::binary);\n  std::ifstream file2(filename2.value().c_str(),\n                      std::ios::in | std::ios::binary);\n#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)\n  std::ifstream file1(filename1.value(), std::ios::in | std::ios::binary);\n  std::ifstream file2(filename2.value(), std::ios::in | std::ios::binary);\n#endif  // BUILDFLAG(IS_WIN)\n\n  // Even if both files aren't openable (and thus, in some sense, \"equal\"),\n  // any unusable file yields a result of \"false\".\n  if (!file1.is_open() || !file2.is_open())\n    return false;\n\n  const int BUFFER_SIZE = 2056;\n  char buffer1[BUFFER_SIZE], buffer2[BUFFER_SIZE];\n  do {\n    file1.read(buffer1, BUFFER_SIZE);\n    file2.read(buffer2, BUFFER_SIZE);\n\n    if ((file1.eof() != file2.eof()) ||\n        (file1.gcount() != file2.gcount()) ||\n        (memcmp(buffer1, buffer2, static_cast<size_t>(file1.gcount())))) {\n      file1.close();\n      file2.close();\n      return false;\n    }\n  } while (!file1.eof() || !file2.eof());\n\n  file1.close();\n  file2.close();\n  return true;\n}\n\nbool TextContentsEqual(const FilePath& filename1, const FilePath& filename2) {\n#if BUILDFLAG(IS_WIN)\n  std::ifstream file1(filename1.value().c_str(), std::ios::in);\n  std::ifstream file2(filename2.value().c_str(), std::ios::in);\n#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)\n  std::ifstream file1(filename1.value(), std::ios::in);\n  std::ifstream file2(filename2.value(), std::ios::in);\n#endif  // BUILDFLAG(IS_WIN)\n\n  // Even if both files aren't openable (and thus, in some sense, \"equal\"),\n  // any unusable file yields a result of \"false\".\n  if (!file1.is_open() || !file2.is_open())\n    return false;\n\n  do {\n    std::string line1, line2;\n    getline(file1, line1);\n    getline(file2, line2);\n\n    // Check for mismatched EOF states, or any error state.\n    if ((file1.eof() != file2.eof()) ||\n        file1.bad() || file2.bad()) {\n      return false;\n    }\n\n    // Trim all '\\r' and '\\n' characters from the end of the line.\n    std::string::size_type end1 = line1.find_last_not_of(\"\\r\\n\");\n    if (end1 == std::string::npos)\n      line1.clear();\n    else if (end1 + 1 < line1.length())\n      line1.erase(end1 + 1);\n\n    std::string::size_type end2 = line2.find_last_not_of(\"\\r\\n\");\n    if (end2 == std::string::npos)\n      line2.clear();\n    else if (end2 + 1 < line2.length())\n      line2.erase(end2 + 1);\n\n    if (line1 != line2)\n      return false;\n  } while (!file1.eof() || !file2.eof());\n\n  return true;\n}\n\nbool ReadStreamToString(FILE* stream, std::string* contents) {\n  return ReadStreamToStringWithMaxSize(\n      stream, std::numeric_limits<size_t>::max(), contents);\n}\n\nbool ReadStreamToStringWithMaxSize(FILE* stream,\n                                   size_t max_size,\n                                   std::string* contents) {\n  if (contents) {\n    contents->clear();\n  }\n\n  std::string content_string;\n  bool read_successs = ReadStreamToSpanWithMaxSize(\n      stream, max_size, [&content_string](size_t size) {\n        content_string.resize(size);\n        return absl::Span<uint8_t>(reinterpret_cast<uint8_t*>(content_string.data()), content_string.length());\n      });\n\n  if (contents) {\n    contents->swap(content_string);\n  }\n  return read_successs;\n}\n\nstd::optional<std::vector<uint8_t>> ReadFileToBytes(const FilePath& path) {\n  if (path.ReferencesParent()) {\n    return std::nullopt;\n  }\n\n  ScopedFILE file_stream(OpenFile(path, \"rb\"));\n  if (!file_stream) {\n    return std::nullopt;\n  }\n\n  std::vector<uint8_t> bytes;\n  if (!ReadStreamToSpanWithMaxSize(file_stream.get(),\n                                   std::numeric_limits<size_t>::max(),\n                                   [&bytes](size_t size) {\n                                     bytes.resize(size);\n                                     return absl::MakeSpan(bytes);\n                                   })) {\n    return std::nullopt;\n  }\n  return bytes;\n}\n\nbool ReadFileToString(const FilePath& path, std::string* contents) {\n  return ReadFileToStringWithMaxSize(path, contents,\n                                     std::numeric_limits<size_t>::max());\n}\n\nbool ReadFileToStringWithMaxSize(const FilePath& path,\n                                 std::string* contents,\n                                 size_t max_size) {\n  if (contents)\n    contents->clear();\n  if (path.ReferencesParent())\n    return false;\n  ScopedFILE file_stream(OpenFile(path, \"rb\"));\n  if (!file_stream)\n    return false;\n  return ReadStreamToStringWithMaxSize(file_stream.get(), max_size, contents);\n}\n\nbool IsDirectoryEmpty(const FilePath& dir_path) {\n  FileEnumerator files(dir_path, false,\n      FileEnumerator::FILES | FileEnumerator::DIRECTORIES);\n  if (files.Next().empty())\n    return true;\n  return false;\n}\n\nbool CreateTemporaryFile(FilePath* path) {\n  FilePath temp_dir;\n  return GetTempDir(&temp_dir) && CreateTemporaryFileInDir(temp_dir, path);\n}\n\nScopedFILE CreateAndOpenTemporaryStream(FilePath* path) {\n  FilePath directory;\n  if (!GetTempDir(&directory))\n    return nullptr;\n\n  return CreateAndOpenTemporaryStreamInDir(directory, path);\n}\n\nbool CreateDirectory(const FilePath& full_path) {\n  return CreateDirectoryAndGetError(full_path, nullptr);\n}\n\nbool GetFileSize(const FilePath& file_path, int64_t* file_size) {\n  File::Info info;\n  if (!GetFileInfo(file_path, &info))\n    return false;\n  *file_size = info.size;\n  return true;\n}\n\nbool TouchFile(const FilePath& path,\n               const Time& last_accessed,\n               const Time& last_modified) {\n  uint32_t flags = File::FLAG_OPEN | File::FLAG_WRITE_ATTRIBUTES;\n\n#if BUILDFLAG(IS_WIN)\n  // On Windows, FILE_FLAG_BACKUP_SEMANTICS is needed to open a directory.\n  if (DirectoryExists(path))\n    flags |= File::FLAG_WIN_BACKUP_SEMANTICS;\n#elif BUILDFLAG(IS_FUCHSIA)\n  // On Fuchsia, we need O_RDONLY for directories, or O_WRONLY for files.\n  // TODO(https://crbug.com/947802): Find a cleaner workaround for this.\n  flags |= (DirectoryExists(path) ? File::FLAG_READ : File::FLAG_WRITE);\n#endif\n\n  File file(path, flags);\n  if (!file.IsValid())\n    return false;\n\n  return file.SetTimes(last_accessed, last_modified);\n}\n\nbool CloseFile(FILE* file) {\n  if (file == nullptr)\n    return true;\n  return fclose(file) == 0;\n}\n\nbool TruncateFile(FILE* file) {\n  if (file == nullptr)\n    return false;\n  long current_offset = ftell(file);\n  if (current_offset == -1)\n    return false;\n#if BUILDFLAG(IS_WIN)\n  int fd = _fileno(file);\n  if (_chsize(fd, current_offset) != 0)\n    return false;\n#else\n  int fd = fileno(file);\n  if (ftruncate(fd, current_offset) != 0)\n    return false;\n#endif\n  return true;\n}\n\nbool WriteFile(const FilePath& filename, absl::Span<const uint8_t> data) {\n  int size = checked_cast<int>(data.size());\n  return WriteFile(filename, reinterpret_cast<const char*>(data.data()),\n                   size) == size;\n}\n\nbool WriteFile(const FilePath& filename, std::string_view data) {\n  int size = checked_cast<int>(data.size());\n  return WriteFile(filename, data.data(), size) == size;\n}\n\nbool WriteLargeFile(const FilePath& filename, absl::Span<const uint8_t> data) {\n  return WriteLargeFile(filename, reinterpret_cast<const char*>(data.data()),\n                   data.size());\n}\n\nbool WriteLargeFile(const FilePath& filename, std::string_view data) {\nreturn WriteLargeFile(filename, data.data(),\n                   data.size());\n}\n\nint GetUniquePathNumber(const FilePath& path) {\n  DCHECK(!path.empty());\n  if (!PathExists(path))\n    return 0;\n\n  std::string number;\n  for (int count = 1; count <= kMaxUniqueFiles; ++count) {\n    absl::StrAppendFormat(&number, \" (%d)\", count);\n    if (!PathExists(path.InsertBeforeExtension(number)))\n      return count;\n    number.clear();\n  }\n\n  return -1;\n}\n\nFilePath GetUniquePath(const FilePath& path) {\n  DCHECK(!path.empty());\n  const int uniquifier = GetUniquePathNumber(path);\n  if (uniquifier > 0)\n    return path.InsertBeforeExtension(absl::StrFormat(\" (%d)\", uniquifier));\n  return uniquifier == 0 ? path : FilePath();\n}\n\nnamespace internal {\n\nbool PreReadFileSlow(const FilePath& file_path, int64_t max_bytes) {\n  DCHECK_GE(max_bytes, 0);\n\n  File file(file_path, File::FLAG_OPEN | File::FLAG_READ |\n                           File::FLAG_WIN_SEQUENTIAL_SCAN |\n                           File::FLAG_WIN_SHARE_DELETE);\n  if (!file.IsValid())\n    return false;\n\n  constexpr int kBufferSize = 1024 * 1024;\n  // Ensures the buffer is deallocated at function exit.\n  std::unique_ptr<char[]> buffer_deleter(new char[kBufferSize]);\n  char* const buffer = buffer_deleter.get();\n\n  while (max_bytes > 0) {\n    // The static_cast<int> is safe because kBufferSize is int, and both values\n    // are non-negative. So, the minimum is guaranteed to fit in int.\n    const int read_size =\n        static_cast<int>(std::min<int64_t>(max_bytes, kBufferSize));\n    DCHECK_GE(read_size, 0);\n    DCHECK_LE(read_size, kBufferSize);\n\n    const int read_bytes = file.ReadAtCurrentPos(buffer, read_size);\n    if (read_bytes < 0)\n      return false;\n    if (read_bytes == 0)\n      break;\n\n    max_bytes -= read_bytes;\n  }\n\n  return true;\n}\n\n}  // namespace internal\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/files/file_util.h",
    "content": "// Copyright 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n// This file contains utility functions for dealing with the local\n// filesystem.\n\n#ifndef TACHYON_BASE_FILES_FILE_UTIL_H_\n#define TACHYON_BASE_FILES_FILE_UTIL_H_\n\n#include <stddef.h>\n#include <stdint.h>\n#include <stdio.h>\n\n#include <limits>\n#include <set>\n#include <string>\n#include <optional>\n\n#include \"absl/types/span.h\"\n\n#include \"tachyon/export.h\"\n#include \"tachyon/base/files/file.h\"\n#include \"tachyon/base/files/file_path.h\"\n#include \"tachyon/base/files/scoped_file.h\"\n#include \"tachyon/base/functional/callback.h\"\n#include \"tachyon/build/build_config.h\"\n\n#if BUILDFLAG(IS_WIN)\n#include \"tachyon/base/win/windows_types.h\"\n#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)\n#include <sys/stat.h>\n#include <unistd.h>\n#include \"tachyon/base/posix/eintr_wrapper.h\"\n#endif\n\nnamespace tachyon::base {\n\nclass Environment;\nclass Time;\n\n//-----------------------------------------------------------------------------\n// Functions that involve filesystem access or modification:\n\n// Returns an absolute version of a relative path. Returns an empty path on\n// error. This function can result in I/O so it can be slow.\n//\n// On POSIX, this function calls realpath(), so:\n// 1) it fails if the path does not exist.\n// 2) it expands all symlink components of the path.\n// 3) it removes \".\" and \"..\" directory components.\nTACHYON_EXPORT FilePath MakeAbsoluteFilePath(const FilePath& input);\n\n#if BUILDFLAG(IS_POSIX)\n// Prepends the current working directory if `input` is not already absolute,\n// and removes \"/./\" and \"/../\" This is similar to MakeAbsoluteFilePath(), but\n// MakeAbsoluteFilePath() expands all symlinks in the path and this does not.\n//\n// This may block if `input` is a relative path, when calling\n// GetCurrentDirectory().\n//\n// This doesn't return absl::nullopt unless (1) `input` is empty, or (2)\n// `input` is a relative path and GetCurrentDirectory() fails.\n[[nodiscard]] TACHYON_EXPORT std::optional<FilePath>\nMakeAbsoluteFilePathNoResolveSymbolicLinks(const FilePath& input);\n#endif\n\n// Returns the total number of bytes used by all the files under |root_path|.\n// If the path does not exist the function returns 0.\n//\n// This function is implemented using the FileEnumerator class so it is not\n// particularly speedy on any platform.\nTACHYON_EXPORT int64_t ComputeDirectorySize(const FilePath& root_path);\n\n// Deletes the given path, whether it's a file or a directory.\n// If it's a directory, it's perfectly happy to delete all of the directory's\n// contents, but it will not recursively delete subdirectories and their\n// contents.\n// Returns true if successful, false otherwise. It is considered successful to\n// attempt to delete a file that does not exist.\n//\n// In POSIX environment and if |path| is a symbolic link, this deletes only\n// the symlink. (even if the symlink points to a non-existent file)\nTACHYON_EXPORT bool DeleteFile(const FilePath& path);\n\n// Deletes the given path, whether it's a file or a directory.\n// If it's a directory, it's perfectly happy to delete all of the\n// directory's contents, including subdirectories and their contents.\n// Returns true if successful, false otherwise. It is considered successful\n// to attempt to delete a file that does not exist.\n//\n// In POSIX environment and if |path| is a symbolic link, this deletes only\n// the symlink. (even if the symlink points to a non-existent file)\n//\n// WARNING: USING THIS EQUIVALENT TO \"rm -rf\", SO USE WITH CAUTION.\nTACHYON_EXPORT bool DeletePathRecursively(const FilePath& path);\n\n/*\nTODO(chokobole):\n// Returns a closure that, when run on any sequence that allows blocking calls,\n// will kick off a potentially asynchronous operation to delete `path`, whose\n// behavior is similar to `DeleteFile()` and `DeletePathRecursively()`\n// respectively.\n//\n// In contrast to `DeleteFile()` and `DeletePathRecursively()`, the thread pool\n// may be used in case retries are needed. On Windows, in particular, retries\n// will be attempted for some time to allow other programs (e.g., anti-virus\n// scanners or malware) to close any open handles to `path` or its contents. If\n// `reply_callback` is not null, it will be posted to the caller's sequence with\n// true if `path` was fully deleted or false otherwise.\n//\n// WARNING: It is NOT safe to use `path` until `reply_callback` is run, as the\n// retry task may still be actively trying to delete it.\nTACHYON_EXPORT OnceClosure\nGetDeleteFileCallback(const FilePath& path,\n                      OnceCallback<void(bool)> reply_callback = {});\nTACHYON_EXPORT OnceClosure\nGetDeletePathRecursivelyCallback(const FilePath& path,\n                                 OnceCallback<void(bool)> reply_callback = {});\n*/\n\n#if BUILDFLAG(IS_WIN)\n// Schedules to delete the given path, whether it's a file or a directory, until\n// the operating system is restarted.\n// Note:\n// 1) The file/directory to be deleted should exist in a temp folder.\n// 2) The directory to be deleted must be empty.\nTACHYON_EXPORT bool DeleteFileAfterReboot(const FilePath& path);\n\n// Prevents opening the file at `path` with EXECUTE access by adding a deny ACE\n// on the filesystem. This allows the file handle to be safely passed to an\n// untrusted process. See also `File::FLAG_WIN_NO_EXECUTE`.\nTACHYON_EXPORT bool PreventExecuteMapping(const FilePath& path);\n\n// Set `path_key` to the second of two valid paths that support safely marking a\n// file as non-execute. The first allowed path is always PATH_TEMP. This is\n// needed to avoid layering violations, as the user data dir is an embedder\n// concept and only known later at runtime.\nTACHYON_EXPORT void SetExtraNoExecuteAllowedPath(int path_key);\n#endif  // BUILDFLAG(IS_WIN)\n\n// Moves the given path, whether it's a file or a directory.\n// If a simple rename is not possible, such as in the case where the paths are\n// on different volumes, this will attempt to copy and delete. Returns\n// true for success.\n// This function fails if either path contains traversal components ('..').\nTACHYON_EXPORT bool Move(const FilePath& from_path, const FilePath& to_path);\n\n// Renames file |from_path| to |to_path|. Both paths must be on the same\n// volume, or the function will fail. Destination file will be created\n// if it doesn't exist. Prefer this function over Move when dealing with\n// temporary files. On Windows it preserves attributes of the target file.\n// Returns true on success, leaving *error unchanged.\n// Returns false on failure and sets *error appropriately, if it is non-NULL.\nTACHYON_EXPORT bool ReplaceFile(const FilePath& from_path,\n                                const FilePath& to_path,\n                                File::Error* error);\n\n// Copies a single file. Use CopyDirectory() to copy directories.\n// This function fails if either path contains traversal components ('..').\n// This function also fails if |to_path| is a directory.\n//\n// On POSIX, if |to_path| is a symlink, CopyFile() will follow the symlink. This\n// may have security implications. Use with care.\n//\n// If |to_path| already exists and is a regular file, it will be overwritten,\n// though its permissions will stay the same.\n//\n// If |to_path| does not exist, it will be created. The new file's permissions\n// varies per platform:\n//\n// - This function keeps the metadata on Windows. The read only bit is not kept.\n// - On Mac and iOS, |to_path| retains |from_path|'s permissions, except user\n//   read/write permissions are always set.\n// - On Linux and Android, |to_path| has user read/write permissions only. i.e.\n//   Always 0600.\n// - On ChromeOS, |to_path| has user read/write permissions and group/others\n//   read permissions. i.e. Always 0644.\nTACHYON_EXPORT bool CopyFile(const FilePath& from_path, const FilePath& to_path);\n\n// Copies the contents of one file into another.\n// The files are taken as is: the copy is done starting from the current offset\n// of |infile| until the end of |infile| is reached, into the current offset of\n// |outfile|.\nTACHYON_EXPORT bool CopyFileContents(File& infile, File& outfile);\n\n// Copies the given path, and optionally all subdirectories and their contents\n// as well.\n//\n// If there are files existing under to_path, always overwrite. Returns true\n// if successful, false otherwise. Wildcards on the names are not supported.\n//\n// This function has the same metadata behavior as CopyFile().\n//\n// If you only need to copy a file use CopyFile, it's faster.\nTACHYON_EXPORT bool CopyDirectory(const FilePath& from_path,\n                                  const FilePath& to_path,\n                                  bool recursive);\n\n// Like CopyDirectory() except trying to overwrite an existing file will not\n// work and will return false.\nTACHYON_EXPORT bool CopyDirectoryExcl(const FilePath& from_path,\n                                      const FilePath& to_path,\n                                      bool recursive);\n\n// Returns true if the given path exists on the local filesystem,\n// false otherwise.\nTACHYON_EXPORT bool PathExists(const FilePath& path);\n\n// Returns true if the given path is readable by the user, false otherwise.\nTACHYON_EXPORT bool PathIsReadable(const FilePath& path);\n\n// Returns true if the given path is writable by the user, false otherwise.\nTACHYON_EXPORT bool PathIsWritable(const FilePath& path);\n\n// Returns true if the given path exists and is a directory, false otherwise.\nTACHYON_EXPORT bool DirectoryExists(const FilePath& path);\n\n// Returns true if the contents of the two files given are equal, false\n// otherwise.  If either file can't be read, returns false.\nTACHYON_EXPORT bool ContentsEqual(const FilePath& filename1,\n                                  const FilePath& filename2);\n\n// Returns true if the contents of the two text files given are equal, false\n// otherwise.  This routine treats \"\\r\\n\" and \"\\n\" as equivalent.\nTACHYON_EXPORT bool TextContentsEqual(const FilePath& filename1,\n                                      const FilePath& filename2);\n\n// Reads the file at |path| and returns a vector of bytes on success, and\n// nullopt on error. For security reasons, a |path| containing path traversal\n// components ('..') is treated as a read error, returning nullopt.\nTACHYON_EXPORT std::optional<std::vector<uint8_t>> ReadFileToBytes(\n    const FilePath& path);\n\n// Reads the file at |path| into |contents| and returns true on success and\n// false on error.  For security reasons, a |path| containing path traversal\n// components ('..') is treated as a read error and |contents| is set to empty.\n// In case of I/O error, |contents| holds the data that could be read from the\n// file before the error occurred.\n// |contents| may be NULL, in which case this function is useful for its side\n// effect of priming the disk cache (could be used for unit tests).\nTACHYON_EXPORT bool ReadFileToString(const FilePath& path, std::string* contents);\n\n// Reads the file at |path| into |contents| and returns true on success and\n// false on error.  For security reasons, a |path| containing path traversal\n// components ('..') is treated as a read error and |contents| is set to empty.\n// In case of I/O error, |contents| holds the data that could be read from the\n// file before the error occurred.  When the file size exceeds |max_size|, the\n// function returns false with |contents| holding the file truncated to\n// |max_size|.\n// |contents| may be NULL, in which case this function is useful for its side\n// effect of priming the disk cache (could be used for unit tests).\nTACHYON_EXPORT bool ReadFileToStringWithMaxSize(const FilePath& path,\n                                                std::string* contents,\n                                                size_t max_size);\n\n// As ReadFileToString, but reading from an open stream after seeking to its\n// start (if supported by the stream). This can also be used to read the whole\n// file from a file descriptor by converting the file descriptor into a stream\n// by using base::FileToFILE() before calling this function.\nTACHYON_EXPORT bool ReadStreamToString(FILE* stream, std::string* contents);\n\n// As ReadFileToStringWithMaxSize, but reading from an open stream after seeking\n// to its start (if supported by the stream).\nTACHYON_EXPORT bool ReadStreamToStringWithMaxSize(FILE* stream,\n                                                  size_t max_size,\n                                                  std::string* contents);\n\n#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)\n\n// Read exactly |bytes| bytes from file descriptor |fd|, storing the result\n// in |buffer|. This function is protected against EINTR and partial reads.\n// Returns true iff |bytes| bytes have been successfully read from |fd|.\nTACHYON_EXPORT bool ReadFromFD(int fd, char* buffer, size_t bytes);\n\n// Performs the same function as CreateAndOpenTemporaryStreamInDir(), but\n// returns the file-descriptor wrapped in a ScopedFD, rather than the stream\n// wrapped in a ScopedFILE.\n// The caller is responsible for deleting the file `path` points to, if\n// appropriate.\nTACHYON_EXPORT ScopedFD CreateAndOpenFdForTemporaryFileInDir(const FilePath& dir,\n                                                             FilePath* path);\n\n#endif  // BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)\n\n#if BUILDFLAG(IS_POSIX)\n\n// ReadFileToStringNonBlocking is identical to ReadFileToString except it\n// guarantees that it will not block. This guarantee is provided on POSIX by\n// opening the file as O_NONBLOCK. This variant should only be used on files\n// which are guaranteed not to block (such as kernel files). Or in situations\n// where a partial read would be acceptable because the backing store returned\n// EWOULDBLOCK.\nTACHYON_EXPORT bool ReadFileToStringNonBlocking(const base::FilePath& file,\n                                                std::string* ret);\n\n// Creates a symbolic link at |symlink| pointing to |target|.  Returns\n// false on failure.\nTACHYON_EXPORT bool CreateSymbolicLink(const FilePath& target,\n                                       const FilePath& symlink);\n\n// Reads the given |symlink| and returns the raw string in |target|.\n// Returns false upon failure.\n// IMPORTANT NOTE: if the string stored in the symlink is a relative file path,\n// it should be interpreted relative to the symlink's directory, NOT the current\n// working directory. ReadSymbolicLinkAbsolute() may be the better choice.\nTACHYON_EXPORT bool ReadSymbolicLink(const FilePath& symlink, FilePath* target);\n\n// Same as ReadSymbolicLink(), but properly converts it into an absolute path if\n// the link is relative.\n// Can fail if readlink() fails, or if\n// MakeAbsoluteFilePathNoResolveSymbolicLinks() fails on the resulting absolute\n// path.\nTACHYON_EXPORT std::optional<FilePath> ReadSymbolicLinkAbsolute(\n    const FilePath& symlink);\n\n// Bits and masks of the file permission.\nenum FilePermissionBits {\n  FILE_PERMISSION_MASK              = S_IRWXU | S_IRWXG | S_IRWXO,\n  FILE_PERMISSION_USER_MASK         = S_IRWXU,\n  FILE_PERMISSION_GROUP_MASK        = S_IRWXG,\n  FILE_PERMISSION_OTHERS_MASK       = S_IRWXO,\n\n  FILE_PERMISSION_READ_BY_USER      = S_IRUSR,\n  FILE_PERMISSION_WRITE_BY_USER     = S_IWUSR,\n  FILE_PERMISSION_EXECUTE_BY_USER   = S_IXUSR,\n  FILE_PERMISSION_READ_BY_GROUP     = S_IRGRP,\n  FILE_PERMISSION_WRITE_BY_GROUP    = S_IWGRP,\n  FILE_PERMISSION_EXECUTE_BY_GROUP  = S_IXGRP,\n  FILE_PERMISSION_READ_BY_OTHERS    = S_IROTH,\n  FILE_PERMISSION_WRITE_BY_OTHERS   = S_IWOTH,\n  FILE_PERMISSION_EXECUTE_BY_OTHERS = S_IXOTH,\n};\n\n// Reads the permission of the given |path|, storing the file permission\n// bits in |mode|. If |path| is symbolic link, |mode| is the permission of\n// a file which the symlink points to.\nTACHYON_EXPORT bool GetPosixFilePermissions(const FilePath& path, int* mode);\n// Sets the permission of the given |path|. If |path| is symbolic link, sets\n// the permission of a file which the symlink points to.\nTACHYON_EXPORT bool SetPosixFilePermissions(const FilePath& path, int mode);\n\n// Returns true iff |executable| can be found in any directory specified by the\n// environment variable in |env|.\n// TODO(chokobole):\n// TACHYON_EXPORT bool ExecutableExistsInPath(Environment* env,\n//                                            const std::string& executable);\n\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_AIX)\n// Determine if files under a given |path| can be mapped and then mprotect'd\n// PROT_EXEC. This depends on the mount options used for |path|, which vary\n// among different Linux distributions and possibly local configuration. It also\n// depends on details of kernel--ChromeOS uses the noexec option for /dev/shm\n// but its kernel allows mprotect with PROT_EXEC anyway.\nTACHYON_EXPORT bool IsPathExecutable(const FilePath& path);\n#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_AIX)\n\n#endif  // BUILDFLAG(IS_POSIX)\n\n// Returns true if the given directory is empty\nTACHYON_EXPORT bool IsDirectoryEmpty(const FilePath& dir_path);\n\n// Get the temporary directory provided by the system.\n//\n// WARNING: In general, you should use CreateTemporaryFile variants below\n// instead of this function. Those variants will ensure that the proper\n// permissions are set so that other users on the system can't edit them while\n// they're open (which can lead to security issues).\nTACHYON_EXPORT bool GetTempDir(FilePath* path);\n\n// Get the home directory. This is more complicated than just getenv(\"HOME\")\n// as it knows to fall back on getpwent() etc.\n//\n// You should not generally call this directly. Instead use DIR_HOME with the\n// path service which will use this function but cache the value.\n// Path service may also override DIR_HOME.\nTACHYON_EXPORT FilePath GetHomeDir();\n\n// Returns a new temporary file in |dir| with a unique name. The file is opened\n// for exclusive read, write, and delete access.\n// On success, |temp_file| is populated with the full path to the created file.\n//\n// NOTE: Exclusivity is unique to Windows. On Windows, the returned file\n// supports File::DeleteOnClose. On other platforms, the caller is responsible\n// for deleting the file `temp_file` points to, if appropriate.\nTACHYON_EXPORT File CreateAndOpenTemporaryFileInDir(const FilePath& dir,\n                                                    FilePath* temp_file);\n\n// Creates a temporary file. The full path is placed in `path`, and the\n// function returns true if was successful in creating the file. The file will\n// be empty and all handles closed after this function returns.\n// The caller is responsible for deleting the file `path` points to, if\n// appropriate.\nTACHYON_EXPORT bool CreateTemporaryFile(FilePath* path);\n\n// Same as CreateTemporaryFile() but the file is created in `dir`.\n// The caller is responsible for deleting the file `temp_file` points to, if\n// appropriate.\nTACHYON_EXPORT bool CreateTemporaryFileInDir(const FilePath& dir,\n                                             FilePath* temp_file);\n\n// Returns the file name for a temporary file by using a platform-specific\n// naming scheme that incorporates |identifier|.\nTACHYON_EXPORT FilePath FormatTemporaryFileName(std::string_view identifier);\n\n// Create and open a temporary file stream for exclusive read, write, and delete\n// access. The full path is placed in `path`. Returns the opened file stream, or\n// null in case of error.\n// NOTE: Exclusivity is unique to Windows. On Windows, the returned file\n// supports File::DeleteOnClose. On other platforms, the caller is responsible\n// for deleting the file `path` points to, if appropriate.\nTACHYON_EXPORT ScopedFILE CreateAndOpenTemporaryStream(FilePath* path);\n\n// Similar to CreateAndOpenTemporaryStream(), but the file is created in `dir`.\nTACHYON_EXPORT ScopedFILE CreateAndOpenTemporaryStreamInDir(const FilePath& dir,\n                                                            FilePath* path);\n\n#if BUILDFLAG(IS_WIN)\n// Retrieves the path `%systemroot%\\SystemTemp`, if available, else retrieves\n// `%programfiles%`.\n// Returns the path in `temp` and `true` if the path is writable by the caller,\n// which is usually only when the caller is running as admin or system.\n// Returns `false` otherwise.\n// Both paths are only accessible to admin and system processes, and are\n// therefore secure.\nTACHYON_EXPORT bool GetSecureSystemTemp(FilePath* temp);\n#endif  // BUILDFLAG(IS_WIN)\n\n// Do NOT USE in new code. Use ScopedTempDir instead.\n// TODO(crbug.com/561597) Remove existing usage and make this an implementation\n// detail inside ScopedTempDir.\n//\n// Create a new directory. If prefix is provided, the new directory name is in\n// the format of prefixyyyy.\n// NOTE: prefix is ignored in the POSIX implementation.\n// If success, return true and output the full path of the directory created.\n//\n// For Windows, this directory is usually created in a secure location if the\n// caller is admin. This is because the default %TEMP% folder for Windows is\n// insecure, since low privilege users can get the path of folders under %TEMP%\n// after creation and are able to create subfolders and files within these\n// folders which can lead to privilege escalation.\nTACHYON_EXPORT bool CreateNewTempDirectory(const std::string& prefix,\n                                           FilePath* new_temp_path);\n\n// Create a directory within another directory.\n// Extra characters will be appended to |prefix| to ensure that the\n// new directory does not have the same name as an existing directory.\nTACHYON_EXPORT bool CreateTemporaryDirInDir(const FilePath& base_dir,\n                                            const std::string& prefix,\n                                            FilePath* new_dir);\n\n// Creates a directory, as well as creating any parent directories, if they\n// don't exist. Returns 'true' on successful creation, or if the directory\n// already exists.  The directory is only readable by the current user.\n// Returns true on success, leaving *error unchanged.\n// Returns false on failure and sets *error appropriately, if it is non-NULL.\nTACHYON_EXPORT bool CreateDirectoryAndGetError(const FilePath& full_path,\n                                               File::Error* error);\n\n// Backward-compatible convenience method for the above.\nTACHYON_EXPORT bool CreateDirectory(const FilePath& full_path);\n\n// Returns the file size. Returns true on success.\nTACHYON_EXPORT bool GetFileSize(const FilePath& file_path, int64_t* file_size);\n\n// Sets |real_path| to |path| with symbolic links and junctions expanded.\n// On windows, make sure the path starts with a lettered drive.\n// |path| must reference a file.  Function will fail if |path| points to\n// a directory or to a nonexistent path.  On windows, this function will\n// fail if |real_path| would be longer than MAX_PATH characters.\nTACHYON_EXPORT bool NormalizeFilePath(const FilePath& path, FilePath* real_path);\n\n#if BUILDFLAG(IS_WIN)\n\n// Given a path in NT native form (\"\\Device\\HarddiskVolumeXX\\...\"),\n// return in |drive_letter_path| the equivalent path that starts with\n// a drive letter (\"C:\\...\").  Return false if no such path exists.\nTACHYON_EXPORT bool DevicePathToDriveLetterPath(const FilePath& device_path,\n                                                FilePath* drive_letter_path);\n\n// Method that wraps the win32 GetLongPathName API, normalizing the specified\n// path to its long form. An example where this is needed is when comparing\n// temp file paths. If a username isn't a valid 8.3 short file name (even just a\n// lengthy name like \"user with long name\"), Windows will set the TMP and TEMP\n// environment variables to be 8.3 paths. ::GetTempPath (called in\n// base::GetTempDir) just uses the value specified by TMP or TEMP, and so can\n// return a short path. Returns an empty path on error.\nTACHYON_EXPORT FilePath MakeLongFilePath(const FilePath& input);\n\n// Creates a hard link named |to_file| to the file |from_file|. Both paths\n// must be on the same volume, and |from_file| may not name a directory.\n// Returns true if the hard link is created, false if it fails.\nTACHYON_EXPORT bool CreateWinHardLink(const FilePath& to_file,\n                                      const FilePath& from_file);\n#endif\n\n// This function will return if the given file is a symlink or not.\nTACHYON_EXPORT bool IsLink(const FilePath& file_path);\n\n// Returns information about the given file path. Also see |File::GetInfo|.\nTACHYON_EXPORT bool GetFileInfo(const FilePath& file_path, File::Info* info);\n\n// Sets the time of the last access and the time of the last modification.\nTACHYON_EXPORT bool TouchFile(const FilePath& path,\n                              const Time& last_accessed,\n                              const Time& last_modified);\n\n// Wrapper for fopen-like calls. Returns non-NULL FILE* on success. The\n// underlying file descriptor (POSIX) or handle (Windows) is unconditionally\n// configured to not be propagated to child processes.\nTACHYON_EXPORT FILE* OpenFile(const FilePath& filename, const char* mode);\n\n// Closes file opened by OpenFile. Returns true on success.\nTACHYON_EXPORT bool CloseFile(FILE* file);\n\n// Associates a standard FILE stream with an existing File. Note that this\n// functions take ownership of the existing File.\nTACHYON_EXPORT FILE* FileToFILE(File file, const char* mode);\n\n// Returns a new handle to the file underlying |file_stream|.\nTACHYON_EXPORT File FILEToFile(FILE* file_stream);\n\n// Truncates an open file to end at the location of the current file pointer.\n// This is a cross-platform analog to Windows' SetEndOfFile() function.\nTACHYON_EXPORT bool TruncateFile(FILE* file);\n\n// Reads at most the given number of bytes from the file into the buffer.\n// Returns the number of read bytes, or -1 on error.\nTACHYON_EXPORT int ReadFile(const FilePath& filename, char* data, int max_size);\n\n// Writes the given buffer into the file, overwriting any data that was\n// previously there.  Returns the number of bytes written, or -1 on error.\n// If file doesn't exist, it gets created with read/write permissions for all.\n// Note that the other variants of WriteFile() below may be easier to use.\n// Note that it restricts the |size| to |INT_MAX|.\nTACHYON_EXPORT int WriteFile(const FilePath& filename, const char* data,\n                             int size);\n\n// Writes |data| into the file, overwriting any data that was previously there.\n// Returns true if and only if all of |data| was written. If the file does not\n// exist, it gets created with read/write permissions for all.\n// Note that it restricts the length of the |data| to |INT_MAX|.\nTACHYON_EXPORT bool WriteFile(const FilePath& filename, absl::Span<const uint8_t> data);\n\n// Another WriteFile() variant that takes a std::string_view so callers don't have to\n// do manual conversions from a char span to a uint8_t span.\n// Note that it restricts the length of the |data| to |INT_MAX|.\nTACHYON_EXPORT bool WriteFile(const FilePath& filename, std::string_view data);\n\n// Writes the given buffer into the file, overwriting any data that was\n// previously there.  Returns true on success, false on failure.\n// If file doesn't exist, it gets created with read/write permissions for all.\n// Note that the other variants of WriteFile() below may be easier to use.\nTACHYON_EXPORT bool WriteLargeFile(const FilePath& filename, const char* data, size_t size);\n\n// Writes |data| into the file, overwriting any data that was previously there.\n// Returns true if and only if all of |data| was written. If the file does not\n// exist, it gets created with read/write permissions for all.\nTACHYON_EXPORT bool WriteLargeFile(const FilePath& filename, absl::Span<const uint8_t> data);\n\n// Another WriteFile() variant that takes a std::string_view so callers don't have to\n// do manual conversions from a char span to a uint8_t span.\nTACHYON_EXPORT bool WriteLargeFile(const FilePath& filename, std::string_view data);\n\n#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)\n// Appends |data| to |fd|. Does not close |fd| when done.  Returns true iff all\n// of |data| were written to |fd|.\nTACHYON_EXPORT bool WriteFileDescriptor(int fd, absl::Span<const uint8_t> data);\n\n// WriteFileDescriptor() variant that takes a std::string_view so callers don't have\n// to do manual conversions from a char span to a uint8_t span.\nTACHYON_EXPORT bool WriteFileDescriptor(int fd, std::string_view data);\n\n// Allocates disk space for the file referred to by |fd| for the byte range\n// starting at |offset| and continuing for |size| bytes. The file size will be\n// changed if |offset|+|len| is greater than the file size. Zeros will fill the\n// new space.\n// After a successful call, subsequent writes into the specified range are\n// guaranteed not to fail because of lack of disk space.\nTACHYON_EXPORT bool AllocateFileRegion(File* file, int64_t offset, size_t size);\n#endif\n\n// Appends |data| to |filename|.  Returns true iff |data| were written to\n// |filename|.\nTACHYON_EXPORT bool AppendToFile(const FilePath& filename,\n                                 absl::Span<const uint8_t> data);\n\n// AppendToFile() variant that takes a std::string_view so callers don't have to do\n// manual conversions from a char span to a uint8_t span.\nTACHYON_EXPORT bool AppendToFile(const FilePath& filename, std::string_view data);\n\n// Gets the current working directory for the process.\nTACHYON_EXPORT bool GetCurrentDirectory(FilePath* path);\n\n// Sets the current working directory for the process.\nTACHYON_EXPORT bool SetCurrentDirectory(const FilePath& path);\n\n// The largest value attempted by GetUniquePath{Number,}.\nenum { kMaxUniqueFiles = 100 };\n\n// Returns the number N that makes |path| unique when formatted as \" (N)\" in a\n// suffix to its basename before any file extension, where N is a number between\n// 1 and 100 (inclusive). Returns 0 if |path| does not exist (meaning that it is\n// unique as-is), or -1 if no such number can be found.\nTACHYON_EXPORT int GetUniquePathNumber(const FilePath& path);\n\n// Returns |path| if it does not exist. Otherwise, returns |path| with the\n// suffix \" (N)\" appended to its basename before any file extension, where N is\n// a number between 1 and 100 (inclusive). Returns an empty path if no such\n// number can be found.\nTACHYON_EXPORT FilePath GetUniquePath(const FilePath& path);\n\n// Sets the given |fd| to non-blocking mode.\n// Returns true if it was able to set it in the non-blocking mode, otherwise\n// false.\nTACHYON_EXPORT bool SetNonBlocking(int fd);\n\n// Hints the OS to prefetch the first |max_bytes| of |file_path| into its cache.\n//\n// If called at the appropriate time, this can reduce the latency incurred by\n// feature code that needs to read the file.\n//\n// |max_bytes| specifies how many bytes should be pre-fetched. It may exceed the\n// file's size. Passing in std::numeric_limits<int64_t>::max() is a convenient\n// way to get the entire file pre-fetched.\n//\n// |is_executable| specifies whether the file is to be prefetched as\n// executable code or as data. Windows treats the file backed pages in RAM\n// differently, and specifying the wrong value results in two copies in RAM.\n//\n// Returns true if at least part of the requested range was successfully\n// prefetched.\n//\n// Calling this before using ::LoadLibrary() on Windows is more efficient memory\n// wise, but we must be sure no other threads try to LoadLibrary() the file\n// while we are doing the mapping and prefetching, or the process will get a\n// private copy of the DLL via COW.\nTACHYON_EXPORT bool PreReadFile(\n    const FilePath& file_path,\n    bool is_executable,\n    int64_t max_bytes = std::numeric_limits<int64_t>::max());\n\n#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)\n\n// Creates a pipe. Returns true on success, otherwise false.\n// On success, |read_fd| will be set to the fd of the read side, and\n// |write_fd| will be set to the one of write side. If |non_blocking|\n// is set the pipe will be created with O_NONBLOCK|O_CLOEXEC flags set\n// otherwise flag is set to zero (default).\nTACHYON_EXPORT bool CreatePipe(ScopedFD* read_fd,\n                               ScopedFD* write_fd,\n                               bool non_blocking = false);\n\n// Creates a non-blocking, close-on-exec pipe.\n// This creates a non-blocking pipe that is not intended to be shared with any\n// child process. This will be done atomically if the operating system supports\n// it. Returns true if it was able to create the pipe, otherwise false.\nTACHYON_EXPORT bool CreateLocalNonBlockingPipe(int fds[2]);\n\n// Sets the given |fd| to close-on-exec mode.\n// Returns true if it was able to set it in the close-on-exec mode, otherwise\n// false.\nTACHYON_EXPORT bool SetCloseOnExec(int fd);\n#endif  // BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)\n\n#if BUILDFLAG(IS_MAC)\n// Test that |path| can only be changed by a given user and members of\n// a given set of groups.\n// Specifically, test that all parts of |path| under (and including) |base|:\n// * Exist.\n// * Are owned by a specific user.\n// * Are not writable by all users.\n// * Are owned by a member of a given set of groups, or are not writable by\n//   their group.\n// * Are not symbolic links.\n// This is useful for checking that a config file is administrator-controlled.\n// |base| must contain |path|.\nTACHYON_EXPORT bool VerifyPathControlledByUser(const base::FilePath& base,\n                                               const base::FilePath& path,\n                                               uid_t owner_uid,\n                                               const std::set<gid_t>& group_gids);\n\n// Is |path| writable only by a user with administrator privileges?\n// This function uses Mac OS conventions.  The super user is assumed to have\n// uid 0, and the administrator group is assumed to be named \"admin\".\n// Testing that |path|, and every parent directory including the root of\n// the filesystem, are owned by the superuser, controlled by the group\n// \"admin\", are not writable by all users, and contain no symbolic links.\n// Will return false if |path| does not exist.\nTACHYON_EXPORT bool VerifyPathControlledByAdmin(const base::FilePath& path);\n#endif  // BUILDFLAG(IS_MAC)\n\n// Returns the maximum length of path component on the volume containing\n// the directory |path|, in the number of FilePath::CharType, or -1 on failure.\nTACHYON_EXPORT int GetMaximumPathComponentLength(const base::FilePath& path);\n\n#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)\n// Get a temporary directory for shared memory files. The directory may depend\n// on whether the destination is intended for executable files, which in turn\n// depends on how /dev/shmem was mounted. As a result, you must supply whether\n// you intend to create executable shmem segments so this function can find\n// an appropriate location.\nTACHYON_EXPORT bool GetShmemTempDir(bool executable, FilePath* path);\n#endif\n\n// Internal --------------------------------------------------------------------\n\nnamespace internal {\n\n// Same as Move but allows paths with traversal components.\n// Use only with extreme care.\nTACHYON_EXPORT bool MoveUnsafe(const FilePath& from_path,\n                               const FilePath& to_path);\n\n#if BUILDFLAG(IS_WIN)\n// Copy from_path to to_path recursively and then delete from_path recursively.\n// Returns true if all operations succeed.\n// This function simulates Move(), but unlike Move() it works across volumes.\n// This function is not transactional.\nTACHYON_EXPORT bool CopyAndDeleteDirectory(const FilePath& from_path,\n                                           const FilePath& to_path);\n#endif  // BUILDFLAG(IS_WIN)\n\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n// CopyFileContentsWithSendfile will use the sendfile(2) syscall to perform a\n// file copy without moving the data between kernel and userspace. This is much\n// more efficient than sequences of read(2)/write(2) calls. The |retry_slow|\n// parameter instructs the caller that it should try to fall back to a normal\n// sequences of read(2)/write(2) syscalls.\n//\n// The input file |infile| must be opened for reading and the output file\n// |outfile| must be opened for writing.\nTACHYON_EXPORT bool CopyFileContentsWithSendfile(File& infile,\n                                                 File& outfile,\n                                                 bool& retry_slow);\n#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) ||\n        // BUILDFLAG(IS_ANDROID)\n\n// Used by PreReadFile() when no kernel support for prefetching is available.\nbool PreReadFileSlow(const FilePath& file_path, int64_t max_bytes);\n\n}  // namespace internal\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_FILES_FILE_UTIL_H_\n"
  },
  {
    "path": "tachyon/base/files/file_util_mac.mm",
    "content": "// Copyright 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/files/file_util.h\"\n\n#import <Foundation/Foundation.h>\n#include <copyfile.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/files/file_path.h\"\n#include \"tachyon/base/mac/foundation_util.h\"\n#include \"tachyon/base/strings/string_util.h\"\n// #include \"tachyon/base/threading/scoped_blocking_call.h\"\n\n#if !defined(__has_feature) || !__has_feature(objc_arc)\n#error \"This file requires ARC support.\"\n#endif\n\nnamespace tachyon::base {\n\nbool CopyFile(const FilePath& from_path, const FilePath& to_path) {\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n  if (from_path.ReferencesParent() || to_path.ReferencesParent())\n    return false;\n  return (copyfile(from_path.value().c_str(), to_path.value().c_str(),\n                   /*state=*/nullptr, COPYFILE_DATA) == 0);\n}\n\nbool GetTempDir(base::FilePath* path) {\n  // In order to facilitate hermetic runs on macOS, first check\n  // MAC_CHROMIUM_TMPDIR. This is used instead of TMPDIR for historical reasons.\n  // This was originally done for https://crbug.com/698759 (TMPDIR too long for\n  // process singleton socket path), but is hopefully obsolete as of\n  // https://crbug.com/1266817 (allows a longer process singleton socket path).\n  // Continue tracking MAC_CHROMIUM_TMPDIR as that's what build infrastructure\n  // sets on macOS.\n  // const char* env_tmpdir = getenv(\"MAC_CHROMIUM_TMPDIR\");\n  const char* env_tmpdir = getenv(\"MAC_TACHYON_TMPDIR\");\n  if (env_tmpdir) {\n    *path = base::FilePath(env_tmpdir);\n    return true;\n  }\n\n  // If we didn't find it, fall back to the native function.\n  NSString* tmp = NSTemporaryDirectory();\n  if (tmp == nil)\n    return false;\n  *path = base::mac::NSStringToFilePath(tmp);\n  return true;\n}\n\nFilePath GetHomeDir() {\n  NSString* tmp = NSHomeDirectory();\n  if (tmp != nil) {\n    FilePath mac_home_dir = base::mac::NSStringToFilePath(tmp);\n    if (!mac_home_dir.empty())\n      return mac_home_dir;\n  }\n\n  // Fall back on temp dir if no home directory is defined.\n  FilePath rv;\n  if (GetTempDir(&rv))\n    return rv;\n\n  // Last resort.\n  return FilePath(\"/tmp\");\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/files/file_util_posix.cc",
    "content": "// Copyright 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n\n#include \"tachyon/base/files/file_util.h\"\n\n#include <dirent.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <libgen.h>\n#include <limits.h>\n#include <stddef.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/mman.h>\n#include <sys/param.h>\n#include <sys/time.h>\n#include <sys/types.h>\n#include <time.h>\n#include <unistd.h>\n\n#include \"absl/strings/str_cat.h\"\n\n#include \"tachyon/export.h\"\n#include \"tachyon/base/bits.h\"\n#include \"tachyon/base/containers/adapters.h\"\n#include \"tachyon/base/containers/contains.h\"\n#include \"tachyon/base/containers/stack.h\"\n#include \"tachyon/base/environment.h\"\n#include \"tachyon/base/files/file_enumerator.h\"\n#include \"tachyon/base/files/file_path.h\"\n#include \"tachyon/base/files/scoped_file.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/no_destructor.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/numerics/safe_conversions.h\"\n// #include \"tachyon/base/path_service.h\"\n#include \"tachyon/base/posix/eintr_wrapper.h\"\n#include \"tachyon/base/strings/string_util.h\"\n// #include \"tachyon/base/strings/sys_string_conversions.h\"\n// #include \"tachyon/base/strings/utf_string_conversions.h\"\n// #include \"tachyon/base/system/sys_info.h\"\n// #include \"tachyon/base/threading/scoped_blocking_call.h\"\n#include \"tachyon/base/time/time.h\"\n#include \"tachyon/build/build_config.h\"\n\n#if BUILDFLAG(IS_APPLE)\n#include <AvailabilityMacros.h>\n#include \"tachyon/base/mac/foundation_util.h\"\n#endif\n\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n#include <sys/sendfile.h>\n#endif\n\n#if BUILDFLAG(IS_ANDROID)\n#include \"tachyon/base/android/content_uri_utils.h\"\n#include \"tachyon/base/os_compat_android.h\"\n#endif\n\n#if !BUILDFLAG(IS_IOS)\n#include <grp.h>\n#endif\n\n// We need to do this on AIX due to some inconsistencies in how AIX\n// handles XOPEN_SOURCE and ALL_SOURCE.\n#if BUILDFLAG(IS_AIX)\nextern \"C\" char* mkdtemp(char* path);\n#endif\n\nnamespace tachyon::base {\n\nnamespace {\n\n#if BUILDFLAG(IS_MAC)\n// Helper for VerifyPathControlledByUser.\nbool VerifySpecificPathControlledByUser(const FilePath& path,\n                                        uid_t owner_uid,\n                                        const std::set<gid_t>& group_gids) {\n  stat_wrapper_t stat_info;\n  if (File::Lstat(path.value().c_str(), &stat_info) != 0) {\n    DPLOG(ERROR) << \"Failed to get information on path \"\n                 << path.value();\n    return false;\n  }\n\n  if (S_ISLNK(stat_info.st_mode)) {\n    DLOG(ERROR) << \"Path \" << path.value() << \" is a symbolic link.\";\n    return false;\n  }\n\n  if (stat_info.st_uid != owner_uid) {\n    DLOG(ERROR) << \"Path \" << path.value() << \" is owned by the wrong user.\";\n    return false;\n  }\n\n  if ((stat_info.st_mode & S_IWGRP) &&\n      !Contains(group_gids, stat_info.st_gid)) {\n    DLOG(ERROR) << \"Path \" << path.value()\n                << \" is writable by an unprivileged group.\";\n    return false;\n  }\n\n  if (stat_info.st_mode & S_IWOTH) {\n    DLOG(ERROR) << \"Path \" << path.value() << \" is writable by any user.\";\n    return false;\n  }\n\n  return true;\n}\n#endif\n\nbase::FilePath GetTempTemplate() {\n  return FormatTemporaryFileName(\"XXXXXX\");\n}\n\nbool AdvanceEnumeratorWithStat(FileEnumerator* traversal,\n                               FilePath* out_next_path,\n                               stat_wrapper_t* out_next_stat) {\n  DCHECK(out_next_path);\n  DCHECK(out_next_stat);\n  *out_next_path = traversal->Next();\n  if (out_next_path->empty())\n    return false;\n\n  *out_next_stat = traversal->GetInfo().stat();\n  return true;\n}\n\nbool DoCopyDirectory(const FilePath& from_path,\n                     const FilePath& to_path,\n                     bool recursive,\n                     bool open_exclusive) {\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n  // Some old callers of CopyDirectory want it to support wildcards.\n  // After some discussion, we decided to fix those callers.\n  // Break loudly here if anyone tries to do this.\n  DCHECK(to_path.value().find('*') == std::string::npos);\n  DCHECK(from_path.value().find('*') == std::string::npos);\n\n  if (from_path.value().size() >= PATH_MAX) {\n    return false;\n  }\n\n  // This function does not properly handle destinations within the source\n  FilePath real_to_path = to_path;\n  if (PathExists(real_to_path))\n    real_to_path = MakeAbsoluteFilePath(real_to_path);\n  else\n    real_to_path = MakeAbsoluteFilePath(real_to_path.DirName());\n  if (real_to_path.empty())\n    return false;\n\n  FilePath real_from_path = MakeAbsoluteFilePath(from_path);\n  if (real_from_path.empty())\n    return false;\n  if (real_to_path == real_from_path || real_from_path.IsParent(real_to_path))\n    return false;\n\n  int traverse_type = FileEnumerator::FILES | FileEnumerator::SHOW_SYM_LINKS;\n  if (recursive)\n    traverse_type |= FileEnumerator::DIRECTORIES;\n  FileEnumerator traversal(from_path, recursive, traverse_type);\n\n  // We have to mimic windows behavior here. |to_path| may not exist yet,\n  // start the loop with |to_path|.\n  stat_wrapper_t from_stat;\n  FilePath current = from_path;\n  if (File::Stat(from_path.value().c_str(), &from_stat) < 0) {\n    DPLOG(ERROR) << \"CopyDirectory() couldn't stat source directory: \"\n                 << from_path.value();\n    return false;\n  }\n  FilePath from_path_base = from_path;\n  if (recursive && DirectoryExists(to_path)) {\n    // If the destination already exists and is a directory, then the\n    // top level of source needs to be copied.\n    from_path_base = from_path.DirName();\n  }\n\n  // The Windows version of this function assumes that non-recursive calls\n  // will always have a directory for from_path.\n  // TODO(maruel): This is not necessary anymore.\n  DCHECK(recursive || S_ISDIR(from_stat.st_mode));\n\n  do {\n    // current is the source path, including from_path, so append\n    // the suffix after from_path to to_path to create the target_path.\n    FilePath target_path(to_path);\n    if (from_path_base != current &&\n        !from_path_base.AppendRelativePath(current, &target_path)) {\n      return false;\n    }\n\n    if (S_ISDIR(from_stat.st_mode)) {\n      mode_t mode = (from_stat.st_mode & 01777) | S_IRUSR | S_IXUSR | S_IWUSR;\n      if (mkdir(target_path.value().c_str(), mode) == 0)\n        continue;\n      if (errno == EEXIST && !open_exclusive)\n        continue;\n\n      DPLOG(ERROR) << \"CopyDirectory() couldn't create directory: \"\n                   << target_path.value();\n      return false;\n    }\n\n    if (!S_ISREG(from_stat.st_mode)) {\n      DLOG(WARNING) << \"CopyDirectory() skipping non-regular file: \"\n                    << current.value();\n      continue;\n    }\n\n    // Add O_NONBLOCK so we can't block opening a pipe.\n    File infile(open(current.value().c_str(), O_RDONLY | O_NONBLOCK));\n    if (!infile.IsValid()) {\n      DPLOG(ERROR) << \"CopyDirectory() couldn't open file: \" << current.value();\n      return false;\n    }\n\n    stat_wrapper_t stat_at_use;\n    if (File::Fstat(infile.GetPlatformFile(), &stat_at_use) < 0) {\n      DPLOG(ERROR) << \"CopyDirectory() couldn't stat file: \" << current.value();\n      return false;\n    }\n\n    if (!S_ISREG(stat_at_use.st_mode)) {\n      DLOG(WARNING) << \"CopyDirectory() skipping non-regular file: \"\n                    << current.value();\n      continue;\n    }\n\n    int open_flags = O_WRONLY | O_CREAT;\n    // If |open_exclusive| is set then we should always create the destination\n    // file, so O_NONBLOCK is not necessary to ensure we don't block on the\n    // open call for the target file below, and since the destination will\n    // always be a regular file it wouldn't affect the behavior of the\n    // subsequent write calls anyway.\n    if (open_exclusive)\n      open_flags |= O_EXCL;\n    else\n      open_flags |= O_TRUNC | O_NONBLOCK;\n    // Each platform has different default file opening modes for CopyFile which\n    // we want to replicate here. On OS X, we use copyfile(3) which takes the\n    // source file's permissions into account. On the other platforms, we just\n    // use the base::File constructor. On Chrome OS, base::File uses a different\n    // set of permissions than it does on other POSIX platforms.\n#if BUILDFLAG(IS_APPLE)\n    mode_t mode = 0600 | (stat_at_use.st_mode & 0177);\n#elif BUILDFLAG(IS_CHROMEOS)\n    mode_t mode = 0644;\n#else\n    mode_t mode = 0600;\n#endif\n    File outfile(open(target_path.value().c_str(), open_flags, mode));\n    if (!outfile.IsValid()) {\n      DPLOG(ERROR) << \"CopyDirectory() couldn't create file: \"\n                   << target_path.value();\n      return false;\n    }\n\n    if (!CopyFileContents(infile, outfile)) {\n      DLOG(ERROR) << \"CopyDirectory() couldn't copy file: \" << current.value();\n      return false;\n    }\n  } while (AdvanceEnumeratorWithStat(&traversal, &current, &from_stat));\n\n  return true;\n}\n\n// TODO(erikkay): The Windows version of this accepts paths like \"foo/bar/*\"\n// which works both with and without the recursive flag.  I'm not sure we need\n// that functionality. If not, remove from file_util_win.cc, otherwise add it\n// here.\nbool DoDeleteFile(const FilePath& path, bool recursive) {\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n\n#if BUILDFLAG(IS_ANDROID)\n  if (path.IsContentUri())\n    return DeleteContentUri(path);\n#endif  // BUILDFLAG(IS_ANDROID)\n\n  const char* path_str = path.value().c_str();\n  stat_wrapper_t file_info;\n  if (File::Lstat(path_str, &file_info) != 0) {\n    // The Windows version defines this condition as success.\n    return (errno == ENOENT);\n  }\n  if (!S_ISDIR(file_info.st_mode))\n    return (unlink(path_str) == 0) || (errno == ENOENT);\n  if (!recursive)\n    return (rmdir(path_str) == 0) || (errno == ENOENT);\n\n  bool success = true;\n  stack<std::string> directories;\n  directories.push(path.value());\n  FileEnumerator traversal(path, true,\n      FileEnumerator::FILES | FileEnumerator::DIRECTORIES |\n      FileEnumerator::SHOW_SYM_LINKS);\n  for (FilePath current = traversal.Next(); !current.empty();\n       current = traversal.Next()) {\n    if (traversal.GetInfo().IsDirectory())\n      directories.push(current.value());\n    else\n      success &= (unlink(current.value().c_str()) == 0) || (errno == ENOENT);\n  }\n\n  while (!directories.empty()) {\n    FilePath dir = FilePath(directories.top());\n    directories.pop();\n    success &= (rmdir(dir.value().c_str()) == 0) || (errno == ENOENT);\n  }\n  return success;\n}\n\n#if !BUILDFLAG(IS_APPLE)\n// Appends |mode_char| to |mode| before the optional character set encoding; see\n// https://www.gnu.org/software/libc/manual/html_node/Opening-Streams.html for\n// details.\nstd::string AppendModeCharacter(std::string_view mode, char mode_char) {\n  std::string result(mode);\n  size_t comma_pos = result.find(',');\n  result.insert(comma_pos == std::string::npos ? result.length() : comma_pos, 1,\n                mode_char);\n  return result;\n}\n#endif\n\n}  // namespace\n\nFilePath MakeAbsoluteFilePath(const FilePath& input) {\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n  char full_path[PATH_MAX];\n  if (realpath(input.value().c_str(), full_path) == nullptr)\n    return FilePath();\n  return FilePath(full_path);\n}\n\nstd::optional<FilePath> MakeAbsoluteFilePathNoResolveSymbolicLinks(\n    const FilePath& input) {\n  if (input.empty()) {\n    return std::nullopt;\n  }\n\n  FilePath collapsed_path;\n  std::vector<std::string> components = input.GetComponents();\n  absl::Span<std::string> components_span(components);\n  // Start with root for absolute |input| and the current working directory for\n  // a relative |input|.\n  if (input.IsAbsolute()) {\n    collapsed_path = FilePath(components_span[0]);\n    components_span = components_span.subspan(1);\n  } else {\n    if (!GetCurrentDirectory(&collapsed_path)) {\n      return std::nullopt;\n    }\n  }\n\n  for (const auto& component : components_span) {\n    if (component == FilePath::kCurrentDirectory) {\n      continue;\n    }\n\n    if (component == FilePath::kParentDirectory) {\n      // Pop the most recent component off the FilePath. Works correctly when\n      // the FilePath is root.\n      collapsed_path = collapsed_path.DirName();\n      continue;\n    }\n\n    // This is just a regular component. Append it.\n    collapsed_path = collapsed_path.Append(component);\n  }\n\n  return collapsed_path;\n}\n\nbool DeleteFile(const FilePath& path) {\n  return DoDeleteFile(path, /*recursive=*/false);\n}\n\nbool DeletePathRecursively(const FilePath& path) {\n  return DoDeleteFile(path, /*recursive=*/true);\n}\n\nbool ReplaceFile(const FilePath& from_path,\n                 const FilePath& to_path,\n                 File::Error* error) {\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n  if (rename(from_path.value().c_str(), to_path.value().c_str()) == 0)\n    return true;\n  if (error)\n    *error = File::GetLastFileError();\n  return false;\n}\n\nbool CopyDirectory(const FilePath& from_path,\n                   const FilePath& to_path,\n                   bool recursive) {\n  return DoCopyDirectory(from_path, to_path, recursive, false);\n}\n\nbool CopyDirectoryExcl(const FilePath& from_path,\n                       const FilePath& to_path,\n                       bool recursive) {\n  return DoCopyDirectory(from_path, to_path, recursive, true);\n}\n\nbool CreatePipe(ScopedFD* read_fd, ScopedFD* write_fd, bool non_blocking) {\n  int fds[2];\n  bool created =\n      non_blocking ? CreateLocalNonBlockingPipe(fds) : (0 == pipe(fds));\n  if (!created)\n    return false;\n  read_fd->reset(fds[0]);\n  write_fd->reset(fds[1]);\n  return true;\n}\n\nbool CreateLocalNonBlockingPipe(int fds[2]) {\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n  return pipe2(fds, O_CLOEXEC | O_NONBLOCK) == 0;\n#else\n  int raw_fds[2];\n  if (pipe(raw_fds) != 0)\n    return false;\n  ScopedFD fd_out(raw_fds[0]);\n  ScopedFD fd_in(raw_fds[1]);\n  if (!SetCloseOnExec(fd_out.get()))\n    return false;\n  if (!SetCloseOnExec(fd_in.get()))\n    return false;\n  if (!SetNonBlocking(fd_out.get()))\n    return false;\n  if (!SetNonBlocking(fd_in.get()))\n    return false;\n  fds[0] = fd_out.release();\n  fds[1] = fd_in.release();\n  return true;\n#endif\n}\n\nbool SetNonBlocking(int fd) {\n  const int flags = fcntl(fd, F_GETFL);\n  if (flags == -1)\n    return false;\n  if (flags & O_NONBLOCK)\n    return true;\n  if (HANDLE_EINTR(fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1)\n    return false;\n  return true;\n}\n\nbool SetCloseOnExec(int fd) {\n  const int flags = fcntl(fd, F_GETFD);\n  if (flags == -1)\n    return false;\n  if (flags & FD_CLOEXEC)\n    return true;\n  if (HANDLE_EINTR(fcntl(fd, F_SETFD, flags | FD_CLOEXEC)) == -1)\n    return false;\n  return true;\n}\n\nbool PathExists(const FilePath& path) {\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n#if BUILDFLAG(IS_ANDROID)\n  if (path.IsContentUri()) {\n    return ContentUriExists(path);\n  }\n#endif\n  return access(path.value().c_str(), F_OK) == 0;\n}\n\nbool PathIsReadable(const FilePath& path) {\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n  return access(path.value().c_str(), R_OK) == 0;\n}\n\nbool PathIsWritable(const FilePath& path) {\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n  return access(path.value().c_str(), W_OK) == 0;\n}\n\nbool DirectoryExists(const FilePath& path) {\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n  stat_wrapper_t file_info;\n  if (File::Stat(path.value().c_str(), &file_info) != 0)\n    return false;\n  return S_ISDIR(file_info.st_mode);\n}\n\nbool ReadFromFD(int fd, char* buffer, size_t bytes) {\n  size_t total_read = 0;\n  while (total_read < bytes) {\n    ssize_t bytes_read =\n        HANDLE_EINTR(read(fd, buffer + total_read, bytes - total_read));\n    if (bytes_read <= 0)\n      break;\n    total_read += static_cast<size_t>(bytes_read);\n  }\n  return total_read == bytes;\n}\n\nScopedFD CreateAndOpenFdForTemporaryFileInDir(const FilePath& directory,\n                                              FilePath* path) {\n  /*\n  TODO(chokobole):\n  ScopedBlockingCall scoped_blocking_call(\n      FROM_HERE,\n      BlockingType::MAY_BLOCK);  // For call to mkstemp().\n  */\n  *path = directory.Append(GetTempTemplate());\n  const std::string& tmpdir_string = path->value();\n  // this should be OK since mkstemp just replaces characters in place\n  char* buffer = const_cast<char*>(tmpdir_string.c_str());\n\n  return ScopedFD(HANDLE_EINTR(mkstemp(buffer)));\n}\n\n#if !BUILDFLAG(IS_FUCHSIA)\nbool CreateSymbolicLink(const FilePath& target_path,\n                        const FilePath& symlink_path) {\n  DCHECK(!symlink_path.empty());\n  DCHECK(!target_path.empty());\n  return ::symlink(target_path.value().c_str(),\n                   symlink_path.value().c_str()) != -1;\n}\n\nbool ReadSymbolicLink(const FilePath& symlink_path, FilePath* target_path) {\n  DCHECK(!symlink_path.empty());\n  DCHECK(target_path);\n  char buf[PATH_MAX];\n  ssize_t count = ::readlink(symlink_path.value().c_str(), buf, std::size(buf));\n\n#if BUILDFLAG(IS_ANDROID) && defined(__LP64__)\n  // A few 64-bit Android L/M devices return INT_MAX instead of -1 here for\n  // errors; this is related to bionic's (incorrect) definition of ssize_t as\n  // being long int instead of int. Cast it so the compiler generates the\n  // comparison we want here. https://crbug.com/1101940\n  bool error = static_cast<int32_t>(count) <= 0;\n#else\n  bool error = count <= 0;\n#endif\n\n  if (error) {\n    target_path->clear();\n    return false;\n  }\n\n  *target_path =\n      FilePath(std::string(buf, static_cast<size_t>(count)));\n  return true;\n}\n\nstd::optional<FilePath> ReadSymbolicLinkAbsolute(\n    const FilePath& symlink_path) {\n  FilePath target_path;\n  if (!ReadSymbolicLink(symlink_path, &target_path)) {\n    return std::nullopt;\n  }\n\n  // Relative symbolic links are relative to the symlink's directory.\n  if (!target_path.IsAbsolute()) {\n    target_path = symlink_path.DirName().Append(target_path);\n  }\n\n  // Remove \"/./\" and \"/../\" to make this more friendly to path-allowlist-based\n  // sandboxes.\n  return MakeAbsoluteFilePathNoResolveSymbolicLinks(target_path);\n}\n\nbool GetPosixFilePermissions(const FilePath& path, int* mode) {\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n  DCHECK(mode);\n\n  stat_wrapper_t file_info;\n  // Uses stat(), because on symbolic link, lstat() does not return valid\n  // permission bits in st_mode\n  if (File::Stat(path.value().c_str(), &file_info) != 0)\n    return false;\n\n  *mode = file_info.st_mode & FILE_PERMISSION_MASK;\n  return true;\n}\n\nbool SetPosixFilePermissions(const FilePath& path,\n                             int mode) {\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n  DCHECK_EQ(mode & ~FILE_PERMISSION_MASK, 0);\n\n  // Calls stat() so that we can preserve the higher bits like S_ISGID.\n  stat_wrapper_t stat_buf;\n  if (File::Stat(path.value().c_str(), &stat_buf) != 0)\n    return false;\n\n  // Clears the existing permission bits, and adds the new ones.\n  // The casting here is because the Android NDK does not declare `st_mode` as a\n  // `mode_t`.\n  mode_t updated_mode_bits = static_cast<mode_t>(stat_buf.st_mode);\n  updated_mode_bits &= static_cast<mode_t>(~FILE_PERMISSION_MASK);\n  updated_mode_bits |= mode & FILE_PERMISSION_MASK;\n\n  if (HANDLE_EINTR(chmod(path.value().c_str(), updated_mode_bits)) != 0)\n    return false;\n\n  return true;\n}\n\n/*\nTODO(chokobole):\nbool ExecutableExistsInPath(const std::string& executable) {\n  std::string_view path;\n  if (!Environment::Get(\"PATH\", &path)) {\n    LOG(ERROR) << \"No $PATH variable. Assuming no \" << executable << \".\";\n    return false;\n  }\n\n  for (const std::string_view& cur_path :\n       SplitStringView(path, \":\", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY)) {\n    FilePath file(cur_path);\n    int permissions;\n    if (GetPosixFilePermissions(file.Append(executable), &permissions) &&\n        (permissions & FILE_PERMISSION_EXECUTE_BY_USER))\n      return true;\n  }\n  return false;\n}\n*/\n\n#endif  // !BUILDFLAG(IS_FUCHSIA)\n\n#if !BUILDFLAG(IS_APPLE)\n// This is implemented in file_util_mac.mm for Mac.\nbool GetTempDir(FilePath* path) {\n  const char* tmp = getenv(\"TMPDIR\");\n  if (tmp) {\n    *path = FilePath(tmp);\n    return true;\n  }\n\n#if BUILDFLAG(IS_ANDROID)\n  return PathService::Get(DIR_CACHE, path);\n#else\n  *path = FilePath(\"/tmp\");\n  return true;\n#endif\n}\n#endif  // !BUILDFLAG(IS_APPLE)\n\n#if !BUILDFLAG(IS_APPLE)  // Mac implementation is in file_util_mac.mm.\nFilePath GetHomeDir() {\n#if BUILDFLAG(IS_CHROMEOS)\n  if (SysInfo::IsRunningOnChromeOS()) {\n    // On Chrome OS chrome::DIR_USER_DATA is overridden with a primary user\n    // homedir once it becomes available. Return / as the safe option.\n    return FilePath(\"/\");\n  }\n#endif\n\n  const char* home_dir = getenv(\"HOME\");\n  if (home_dir && home_dir[0])\n    return FilePath(home_dir);\n\n#if BUILDFLAG(IS_ANDROID)\n  DLOG(WARNING) << \"OS_ANDROID: Home directory lookup not yet implemented.\";\n#endif\n\n  FilePath rv;\n  if (GetTempDir(&rv))\n    return rv;\n\n  // Last resort.\n  return FilePath(\"/tmp\");\n}\n#endif  // !BUILDFLAG(IS_APPLE)\n\nFile CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* temp_file) {\n  // For call to close() inside ScopedFD.\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n  ScopedFD fd = CreateAndOpenFdForTemporaryFileInDir(dir, temp_file);\n  return fd.is_valid() ? File(std::move(fd)) : File(File::GetLastFileError());\n}\n\nbool CreateTemporaryFileInDir(const FilePath& dir, FilePath* temp_file) {\n  // For call to close() inside ScopedFD.\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n  ScopedFD fd = CreateAndOpenFdForTemporaryFileInDir(dir, temp_file);\n  return fd.is_valid();\n}\n\nFilePath FormatTemporaryFileName(std::string_view identifier) {\n#if BUILDFLAG(IS_APPLE)\n  std::string_view prefix = base::mac::BaseBundleID();\n#else\n  std::string_view prefix = \"org.kroma.Tachyon\";\n#endif\n  return FilePath(absl::StrCat(\".\", prefix, \".\", identifier));\n}\n\nScopedFILE CreateAndOpenTemporaryStreamInDir(const FilePath& dir,\n                                             FilePath* path) {\n  ScopedFD scoped_fd = CreateAndOpenFdForTemporaryFileInDir(dir, path);\n  if (!scoped_fd.is_valid())\n    return nullptr;\n\n  int fd = scoped_fd.release();\n  FILE* file = fdopen(fd, \"a+\");\n  if (!file)\n    close(fd);\n  return ScopedFILE(file);\n}\n\nstatic bool CreateTemporaryDirInDirImpl(const FilePath& base_dir,\n                                        const FilePath& name_tmpl,\n                                        FilePath* new_dir) {\n  /*\n  TODO(chokobole):\n  ScopedBlockingCall scoped_blocking_call(\n      FROM_HERE, BlockingType::MAY_BLOCK);  // For call to mkdtemp().\n  */\n  DCHECK(EndsWith(name_tmpl.value(), \"XXXXXX\"))\n      << \"Directory name template must end with \\\"XXXXXX\\\".\";\n\n  FilePath sub_dir = base_dir.Append(name_tmpl);\n  std::string sub_dir_string = sub_dir.value();\n\n  // this should be OK since mkdtemp just replaces characters in place\n  char* buffer = const_cast<char*>(sub_dir_string.c_str());\n  char* dtemp = mkdtemp(buffer);\n  if (!dtemp) {\n    DPLOG(ERROR) << \"mkdtemp\";\n    return false;\n  }\n  *new_dir = FilePath(dtemp);\n  return true;\n}\n\nbool CreateTemporaryDirInDir(const FilePath& base_dir,\n                             const std::string& prefix,\n                             FilePath* new_dir) {\n  std::string mkdtemp_template = prefix;\n  mkdtemp_template.append(\"XXXXXX\");\n  return CreateTemporaryDirInDirImpl(base_dir, FilePath(mkdtemp_template),\n                                     new_dir);\n}\n\nbool CreateNewTempDirectory(const std::string& prefix,\n                            FilePath* new_temp_path) {\n  FilePath tmpdir;\n  if (!GetTempDir(&tmpdir))\n    return false;\n\n  return CreateTemporaryDirInDirImpl(tmpdir, GetTempTemplate(), new_temp_path);\n}\n\nbool CreateDirectoryAndGetError(const FilePath& full_path,\n                                File::Error* error) {\n  /*\n  TODO(chokobole):\n  ScopedBlockingCall scoped_blocking_call(\n      FROM_HERE, BlockingType::MAY_BLOCK);  // For call to mkdir().\n  */\n  std::vector<FilePath> subpaths;\n\n  // Collect a list of all parent directories.\n  FilePath last_path = full_path;\n  subpaths.push_back(full_path);\n  for (FilePath path = full_path.DirName();\n       path.value() != last_path.value(); path = path.DirName()) {\n    subpaths.push_back(path);\n    last_path = path;\n  }\n\n  // Iterate through the parents and create the missing ones.\n  for (const FilePath& subpath : base::Reversed(subpaths)) {\n    if (DirectoryExists(subpath))\n      continue;\n    if (mkdir(subpath.value().c_str(), 0700) == 0)\n      continue;\n    // Mkdir failed, but it might have failed with EEXIST, or some other error\n    // due to the directory appearing out of thin air. This can occur if\n    // two processes are trying to create the same file system tree at the same\n    // time. Check to see if it exists and make sure it is a directory.\n    int saved_errno = errno;\n    if (!DirectoryExists(subpath)) {\n      if (error)\n        *error = File::OSErrorToFileError(saved_errno);\n      return false;\n    }\n  }\n  return true;\n}\n\n// ReadFileToStringNonBlockingNonBlocking will read a file to a string. This\n// method should only be used on files which are known to be non-blocking such\n// as procfs or sysfs nodes. Additionally, the file is opened as O_NONBLOCK so\n// it WILL NOT block even if opened on a blocking file. It will return true if\n// the file read until EOF and it will return false otherwise, errno will remain\n// set on error conditions. |ret| will be populated with the contents of the\n// file.\nbool ReadFileToStringNonBlocking(const base::FilePath& file, std::string* ret) {\n  DCHECK(ret);\n  ret->clear();\n\n  base::ScopedFD fd(HANDLE_EINTR(\n      open(file.value().c_str(), O_CLOEXEC | O_NONBLOCK | O_RDONLY)));\n  if (!fd.is_valid()) {\n    return false;\n  }\n\n  ssize_t bytes_read = 0;\n  do {\n    char buf[4096];\n    bytes_read = HANDLE_EINTR(read(fd.get(), buf, sizeof(buf)));\n    if (bytes_read < 0)\n      return false;\n    if (bytes_read > 0)\n      ret->append(buf, static_cast<size_t>(bytes_read));\n  } while (bytes_read > 0);\n\n  return true;\n}\n\nbool NormalizeFilePath(const FilePath& path, FilePath* normalized_path) {\n  FilePath real_path_result = MakeAbsoluteFilePath(path);\n  if (real_path_result.empty())\n    return false;\n\n  // To be consistant with windows, fail if |real_path_result| is a\n  // directory.\n  if (DirectoryExists(real_path_result))\n    return false;\n\n  *normalized_path = real_path_result;\n  return true;\n}\n\n// TODO(rkc): Refactor GetFileInfo and FileEnumerator to handle symlinks\n// correctly. http://code.google.com/p/chromium-os/issues/detail?id=15948\nbool IsLink(const FilePath& file_path) {\n  stat_wrapper_t st;\n  // If we can't lstat the file, it's safe to assume that the file won't at\n  // least be a 'followable' link.\n  if (File::Lstat(file_path.value().c_str(), &st) != 0)\n    return false;\n  return S_ISLNK(st.st_mode);\n}\n\nbool GetFileInfo(const FilePath& file_path, File::Info* results) {\n  stat_wrapper_t file_info;\n#if BUILDFLAG(IS_ANDROID)\n  if (file_path.IsContentUri()) {\n    File file = OpenContentUriForRead(file_path);\n    if (!file.IsValid())\n      return false;\n    return file.GetInfo(results);\n  } else {\n#endif  // BUILDFLAG(IS_ANDROID)\n    if (File::Stat(file_path.value().c_str(), &file_info) != 0)\n      return false;\n#if BUILDFLAG(IS_ANDROID)\n  }\n#endif  // BUILDFLAG(IS_ANDROID)\n\n  results->FromStat(file_info);\n  return true;\n}\n\nFILE* OpenFile(const FilePath& filename, const char* mode) {\n  // 'e' is unconditionally added below, so be sure there is not one already\n  // present before a comma in |mode|.\n  DCHECK(\n      strchr(mode, 'e') == nullptr ||\n      (strchr(mode, ',') != nullptr && strchr(mode, 'e') > strchr(mode, ',')));\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n  FILE* result = nullptr;\n#if BUILDFLAG(IS_APPLE)\n  // macOS does not provide a mode character to set O_CLOEXEC; see\n  // https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man3/fopen.3.html.\n  const char* the_mode = mode;\n#else\n  std::string mode_with_e(AppendModeCharacter(mode, 'e'));\n  const char* the_mode = mode_with_e.c_str();\n#endif\n  do {\n    result = fopen(filename.value().c_str(), the_mode);\n  } while (!result && errno == EINTR);\n#if BUILDFLAG(IS_APPLE)\n  // Mark the descriptor as close-on-exec.\n  if (result)\n    SetCloseOnExec(fileno(result));\n#endif\n  return result;\n}\n\n// NaCl doesn't implement system calls to open files directly.\n#if !BUILDFLAG(IS_NACL)\nFILE* FileToFILE(File file, const char* mode) {\n  PlatformFile unowned = file.GetPlatformFile();\n  FILE* stream = fdopen(file.TakePlatformFile(), mode);\n  if (!stream)\n    ScopedFD to_be_closed(unowned);\n  return stream;\n}\n\nFile FILEToFile(FILE* file_stream) {\n  if (!file_stream)\n    return File();\n\n  PlatformFile fd = fileno(file_stream);\n  DCHECK_NE(fd, -1);\n  ScopedPlatformFile other_fd(HANDLE_EINTR(dup(fd)));\n  if (!other_fd.is_valid())\n    return File(File::GetLastFileError());\n  return File(std::move(other_fd));\n}\n#endif  // !BUILDFLAG(IS_NACL)\n\nint ReadFile(const FilePath& filename, char* data, int max_size) {\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n  if (max_size < 0)\n    return -1;\n  int fd = HANDLE_EINTR(open(filename.value().c_str(), O_RDONLY));\n  if (fd < 0)\n    return -1;\n\n  long bytes_read = HANDLE_EINTR(read(fd, data, static_cast<size_t>(max_size)));\n  if (IGNORE_EINTR(close(fd)) < 0)\n    return -1;\n  return checked_cast<int>(bytes_read);\n}\n\nint WriteFile(const FilePath& filename, const char* data, int size) {\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n  if (size < 0)\n    return -1;\n  int fd = HANDLE_EINTR(creat(filename.value().c_str(), 0666));\n  if (fd < 0)\n    return -1;\n\n  int bytes_written =\n      WriteFileDescriptor(fd, std::string_view(data, static_cast<size_t>(size)))\n          ? size\n          : -1;\n  if (IGNORE_EINTR(close(fd)) < 0)\n    return -1;\n  return bytes_written;\n}\n\nbool WriteLargeFile(const FilePath& filename, const char* data, size_t size) {\n  int fd = HANDLE_EINTR(creat(filename.value().c_str(), 0666));\n  if (fd < 0)\n    return false;\n\n  size_t offset = 0;\n  size_t to_write = size;\n  int bytes_written = 0;\n\n  while (to_write > 0) {\n    int chunk_size = std::min(to_write, size_t{INT_MAX});\n    bytes_written =\n        WriteFileDescriptor(fd, std::string_view(data + offset, chunk_size))\n            ? chunk_size\n            : -1;\n    if (bytes_written == -1) break;\n    offset += chunk_size;\n    to_write -= chunk_size;\n  }\n\n  if (IGNORE_EINTR(close(fd)) < 0)\n    return false;\n  return bytes_written != -1;\n\n}\n\nbool WriteFileDescriptor(int fd, absl::Span<const uint8_t> data) {\n  // Allow for partial writes.\n  ssize_t bytes_written_total = 0;\n  ssize_t size = checked_cast<ssize_t>(data.size());\n  for (ssize_t bytes_written_partial = 0; bytes_written_total < size;\n       bytes_written_total += bytes_written_partial) {\n    bytes_written_partial =\n        HANDLE_EINTR(write(fd, data.data() + bytes_written_total,\n                           static_cast<size_t>(size - bytes_written_total)));\n    if (bytes_written_partial < 0)\n      return false;\n  }\n\n  return true;\n}\n\nbool WriteFileDescriptor(int fd, std::string_view data) {\n  return WriteFileDescriptor(fd, absl::Span<const uint8_t>(\n    reinterpret_cast<const uint8_t*>(data.data()), data.length()));\n}\n\nbool AllocateFileRegion(File* file, int64_t offset, size_t size) {\n  DCHECK(file);\n\n  // Explicitly extend |file| to the maximum size. Zeros will fill the new\n  // space. It is assumed that the existing file is fully realized as\n  // otherwise the entire file would have to be read and possibly written.\n  const int64_t original_file_len = file->GetLength();\n  if (original_file_len < 0) {\n    DPLOG(ERROR) << \"fstat \" << file->GetPlatformFile();\n    return false;\n  }\n\n  // Increase the actual length of the file, if necessary. This can fail if\n  // the disk is full and the OS doesn't support sparse files.\n  const int64_t new_file_len = offset + static_cast<int64_t>(size);\n  // If the first condition fails, the cast on the previous line was invalid\n  // (though not UB).\n  if (!IsValueInRangeForNumericType<int64_t>(size) ||\n      !IsValueInRangeForNumericType<off_t>(size) ||\n      !IsValueInRangeForNumericType<off_t>(new_file_len) ||\n      !file->SetLength(std::max(original_file_len, new_file_len))) {\n    DPLOG(ERROR) << \"ftruncate \" << file->GetPlatformFile();\n    return false;\n  }\n\n  // Realize the extent of the file so that it can't fail (and crash) later\n  // when trying to write to a memory page that can't be created. This can\n  // fail if the disk is full and the file is sparse.\n\n  // First try the more effective platform-specific way of allocating the disk\n  // space. It can fail because the filesystem doesn't support it. In that case,\n  // use the manual method below.\n\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n  if (HANDLE_EINTR(fallocate(file->GetPlatformFile(), 0, offset,\n                             static_cast<off_t>(size))) != -1)\n    return true;\n  DPLOG(ERROR) << \"fallocate\";\n#elif BUILDFLAG(IS_APPLE)\n  // MacOS doesn't support fallocate even though their new APFS filesystem\n  // does support sparse files. It does, however, have the functionality\n  // available via fcntl.\n  // See also: https://openradar.appspot.com/32720223\n  fstore_t params = {F_ALLOCATEALL, F_PEOFPOSMODE, offset,\n                     static_cast<off_t>(size), 0};\n  if (fcntl(file->GetPlatformFile(), F_PREALLOCATE, &params) != -1)\n    return true;\n  DPLOG(ERROR) << \"F_PREALLOCATE\";\n#endif\n\n  // Manually realize the extended file by writing bytes to it at intervals.\n  blksize_t block_size = 512;  // Start with something safe.\n  stat_wrapper_t statbuf;\n  if (File::Fstat(file->GetPlatformFile(), &statbuf) == 0 &&\n      statbuf.st_blksize > 0 && base::bits::IsPowerOfTwo(statbuf.st_blksize)) {\n    block_size = static_cast<blksize_t>(statbuf.st_blksize);\n  }\n\n  // Write starting at the next block boundary after the old file length.\n  const int64_t extension_start = checked_cast<int64_t>(base::bits::AlignUp(\n      static_cast<size_t>(original_file_len), static_cast<size_t>(block_size)));\n  for (int64_t i = extension_start; i < new_file_len; i += block_size) {\n    char existing_byte;\n    if (HANDLE_EINTR(pread(file->GetPlatformFile(), &existing_byte, 1,\n                           static_cast<off_t>(i))) != 1) {\n      return false;  // Can't read? Not viable.\n    }\n    if (existing_byte != 0) {\n      continue;  // Block has data so must already exist.\n    }\n    if (HANDLE_EINTR(pwrite(file->GetPlatformFile(), &existing_byte, 1,\n                            static_cast<off_t>(i))) != 1) {\n      return false;  // Can't write? Not viable.\n    }\n  }\n\n  return true;\n}\n\nbool AppendToFile(const FilePath& filename, absl::Span<const uint8_t> data) {\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n  bool ret = true;\n  int fd = HANDLE_EINTR(open(filename.value().c_str(), O_WRONLY | O_APPEND));\n  if (fd < 0) {\n    VPLOG(1) << \"Unable to create file \" << filename.value();\n    return false;\n  }\n\n  // This call will either write all of the data or return false.\n  if (!WriteFileDescriptor(fd, data)) {\n    VPLOG(1) << \"Error while writing to file \" << filename.value();\n    ret = false;\n  }\n\n  if (IGNORE_EINTR(close(fd)) < 0) {\n    VPLOG(1) << \"Error while closing file \" << filename.value();\n    return false;\n  }\n\n  return ret;\n}\n\nbool AppendToFile(const FilePath& filename, std::string_view data) {\n  return AppendToFile(filename, absl::Span<const uint8_t>(\n    reinterpret_cast<const uint8_t*>(data.data()), data.length()));\n}\n\nbool GetCurrentDirectory(FilePath* dir) {\n  // getcwd can return ENOENT, which implies it checks against the disk.\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n\n  char system_buffer[PATH_MAX] = \"\";\n  if (!getcwd(system_buffer, sizeof(system_buffer))) {\n    return false;\n  }\n  *dir = FilePath(system_buffer);\n  return true;\n}\n\nbool SetCurrentDirectory(const FilePath& path) {\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n  return chdir(path.value().c_str()) == 0;\n}\n\n#if BUILDFLAG(IS_MAC)\nbool VerifyPathControlledByUser(const FilePath& base,\n                                const FilePath& path,\n                                uid_t owner_uid,\n                                const std::set<gid_t>& group_gids) {\n  if (base != path && !base.IsParent(path)) {\n     DLOG(ERROR) << \"|base| must be a subdirectory of |path|.  base = \\\"\"\n                 << base.value() << \"\\\", path = \\\"\" << path.value() << \"\\\"\";\n     return false;\n  }\n\n  std::vector<std::string> base_components = base.GetComponents();\n  std::vector<std::string> path_components = path.GetComponents();\n  std::vector<std::string>::const_iterator ib, ip;\n  for (ib = base_components.begin(), ip = path_components.begin();\n       ib != base_components.end(); ++ib, ++ip) {\n    // |base| must be a subpath of |path|, so all components should match.\n    // If these CHECKs fail, look at the test that base is a parent of\n    // path at the top of this function.\n    DCHECK(ip != path_components.end());\n    DCHECK(*ip == *ib);\n  }\n\n  FilePath current_path = base;\n  if (!VerifySpecificPathControlledByUser(current_path, owner_uid, group_gids))\n    return false;\n\n  for (; ip != path_components.end(); ++ip) {\n    current_path = current_path.Append(*ip);\n    if (!VerifySpecificPathControlledByUser(\n            current_path, owner_uid, group_gids))\n      return false;\n  }\n  return true;\n}\n\nbool VerifyPathControlledByAdmin(const FilePath& path) {\n  const unsigned kRootUid = 0;\n  const FilePath kFileSystemRoot(\"/\");\n\n  // The name of the administrator group on mac os.\n  const char* const kAdminGroupNames[] = {\n    \"admin\",\n    \"wheel\"\n  };\n\n  // Reading the groups database may touch the file system.\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n\n  std::set<gid_t> allowed_group_ids;\n  for (int i = 0, ie = std::size(kAdminGroupNames); i < ie; ++i) {\n    struct group *group_record = getgrnam(kAdminGroupNames[i]);\n    if (!group_record) {\n      DPLOG(ERROR) << \"Could not get the group ID of group \\\"\"\n                   << kAdminGroupNames[i] << \"\\\".\";\n      continue;\n    }\n\n    allowed_group_ids.insert(group_record->gr_gid);\n  }\n\n  return VerifyPathControlledByUser(\n      kFileSystemRoot, path, kRootUid, allowed_group_ids);\n}\n#endif  // BUILDFLAG(IS_MAC)\n\nint GetMaximumPathComponentLength(const FilePath& path) {\n#if BUILDFLAG(IS_FUCHSIA)\n  // Return a value we do not expect anyone ever to reach, but which is small\n  // enough to guard against e.g. bugs causing multi-megabyte paths.\n  return 1024;\n#else\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n  return saturated_cast<int>(pathconf(path.value().c_str(), _PC_NAME_MAX));\n#endif\n}\n\n#if !BUILDFLAG(IS_ANDROID)\n// This is implemented in file_util_android.cc for that platform.\nbool GetShmemTempDir(bool executable, FilePath* path) {\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_AIX)\n  bool disable_dev_shm = false;\n// #if !BUILDFLAG(IS_CHROMEOS)\n//   disable_dev_shm = CommandLine::ForCurrentProcess()->HasSwitch(\n//       switches::kDisableDevShmUsage);\n// #endif\n  bool use_dev_shm = true;\n  if (executable) {\n    static const bool s_dev_shm_executable =\n        IsPathExecutable(FilePath(\"/dev/shm\"));\n    use_dev_shm = s_dev_shm_executable;\n  }\n  if (use_dev_shm && !disable_dev_shm) {\n    *path = FilePath(\"/dev/shm\");\n    return true;\n  }\n#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_AIX)\n  return GetTempDir(path);\n}\n#endif  // !BUILDFLAG(IS_ANDROID)\n\n#if !BUILDFLAG(IS_APPLE)\n// Mac has its own implementation, this is for all other Posix systems.\nbool CopyFile(const FilePath& from_path, const FilePath& to_path) {\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n  File infile;\n#if BUILDFLAG(IS_ANDROID)\n  if (from_path.IsContentUri()) {\n    infile = OpenContentUriForRead(from_path);\n  } else {\n    infile = File(from_path, File::FLAG_OPEN | File::FLAG_READ);\n  }\n#else\n  infile = File(from_path, File::FLAG_OPEN | File::FLAG_READ);\n#endif\n  if (!infile.IsValid())\n    return false;\n\n  File outfile(to_path, File::FLAG_WRITE | File::FLAG_CREATE_ALWAYS);\n  if (!outfile.IsValid())\n    return false;\n\n  return CopyFileContents(infile, outfile);\n}\n#endif  // !BUILDFLAG(IS_APPLE)\n\nbool PreReadFile(const FilePath& file_path,\n                 bool is_executable,\n                 int64_t max_bytes) {\n  DCHECK_GE(max_bytes, 0);\n\n  // posix_fadvise() is only available in the Android NDK in API 21+. Older\n  // versions may have the required kernel support, but don't have enough usage\n  // to justify backporting.\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \\\n    (BUILDFLAG(IS_ANDROID) && __ANDROID_API__ >= 21)\n  File file(file_path, File::FLAG_OPEN | File::FLAG_READ);\n  if (!file.IsValid())\n    return false;\n\n  if (max_bytes == 0) {\n    // fadvise() pre-fetches the entire file when given a zero length.\n    return true;\n  }\n\n  const PlatformFile fd = file.GetPlatformFile();\n  const ::off_t len = base::saturated_cast<::off_t>(max_bytes);\n  return posix_fadvise(fd, /*offset=*/0, len, POSIX_FADV_WILLNEED) == 0;\n#elif BUILDFLAG(IS_APPLE)\n  File file(file_path, File::FLAG_OPEN | File::FLAG_READ);\n  if (!file.IsValid())\n    return false;\n\n  if (max_bytes == 0) {\n    // fcntl(F_RDADVISE) fails when given a zero length.\n    return true;\n  }\n\n  const PlatformFile fd = file.GetPlatformFile();\n  ::radvisory read_advise_data = {\n      .ra_offset = 0, .ra_count = base::saturated_cast<int>(max_bytes)};\n  return fcntl(fd, F_RDADVISE, &read_advise_data) != -1;\n#else\n  return internal::PreReadFileSlow(file_path, max_bytes);\n#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) ||\n        // (BUILDFLAG(IS_ANDROID) &&\n        // __ANDROID_API__ >= 21)\n}\n\n// -----------------------------------------------------------------------------\n\nnamespace internal {\n\nbool MoveUnsafe(const FilePath& from_path, const FilePath& to_path) {\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n  // Windows compatibility: if |to_path| exists, |from_path| and |to_path|\n  // must be the same type, either both files, or both directories.\n  stat_wrapper_t to_file_info;\n  if (File::Stat(to_path.value().c_str(), &to_file_info) == 0) {\n    stat_wrapper_t from_file_info;\n    if (File::Stat(from_path.value().c_str(), &from_file_info) != 0)\n      return false;\n    if (S_ISDIR(to_file_info.st_mode) != S_ISDIR(from_file_info.st_mode))\n      return false;\n  }\n\n  if (rename(from_path.value().c_str(), to_path.value().c_str()) == 0)\n    return true;\n\n  if (!CopyDirectory(from_path, to_path, true))\n    return false;\n\n  DeletePathRecursively(from_path);\n  return true;\n}\n\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\nbool CopyFileContentsWithSendfile(File& infile,\n                                  File& outfile,\n                                  bool& retry_slow) {\n  DCHECK(infile.IsValid());\n  stat_wrapper_t in_file_info;\n  retry_slow = false;\n\n  if (base::File::Fstat(infile.GetPlatformFile(), &in_file_info)) {\n    return false;\n  }\n\n  int64_t file_size = in_file_info.st_size;\n  if (file_size < 0)\n    return false;\n  if (file_size == 0) {\n    // Non-regular files can return a file size of 0, things such as pipes,\n    // sockets, etc. Additionally, kernel seq_files(most procfs files) will also\n    // return 0 while still reporting as a regular file. Unfortunately, in some\n    // of these situations there are easy ways to detect them, in others there\n    // are not. No extra syscalls are needed if it's not a regular file.\n    //\n    // Because any attempt to detect it would likely require another syscall,\n    // let's just fall back to a slow copy which will invoke a single read(2) to\n    // determine if the file has contents or if it's really a zero length file.\n    retry_slow = true;\n    return false;\n  }\n\n  size_t copied = 0;\n  ssize_t res = 0;\n  do {\n    // Don't specify an offset and the kernel will begin reading/writing to the\n    // current file offsets.\n    res = HANDLE_EINTR(sendfile(\n        outfile.GetPlatformFile(), infile.GetPlatformFile(), /*offset=*/nullptr,\n        /*length=*/static_cast<size_t>(file_size) - copied));\n    if (res <= 0) {\n      break;\n    }\n\n    copied += static_cast<size_t>(res);\n  } while (copied < static_cast<size_t>(file_size));\n\n  // Fallback on non-fatal error cases. None of these errors can happen after\n  // data has started copying, a check is included for good measure. As a result\n  // file sizes and file offsets will not have changed. A slow fallback and\n  // proceed without issues.\n  retry_slow = (copied == 0 && res < 0 &&\n                (errno == EINVAL || errno == ENOSYS || errno == EPERM));\n\n  return res >= 0;\n}\n#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) ||\n        // BUILDFLAG(IS_ANDROID)\n\n}  // namespace internal\n\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_AIX)\nTACHYON_EXPORT bool IsPathExecutable(const FilePath& path) {\n  bool result = false;\n  FilePath tmp_file_path;\n\n  ScopedFD fd = CreateAndOpenFdForTemporaryFileInDir(path, &tmp_file_path);\n  if (fd.is_valid()) {\n    DeleteFile(tmp_file_path);\n    long sysconf_result = sysconf(_SC_PAGESIZE);\n    CHECK_GE(sysconf_result, 0);\n    size_t pagesize = static_cast<size_t>(sysconf_result);\n    CHECK_GE(sizeof(pagesize), sizeof(sysconf_result));\n    void* mapping = mmap(nullptr, pagesize, PROT_READ, MAP_SHARED, fd.get(), 0);\n    if (mapping != MAP_FAILED) {\n      if (HANDLE_EINTR(mprotect(mapping, pagesize, PROT_READ | PROT_EXEC)) == 0)\n        result = true;\n      munmap(mapping, pagesize);\n    }\n  }\n  return result;\n}\n#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_AIX)\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/files/memory_mapped_file.cc",
    "content": "// Copyright 2013 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/files/memory_mapped_file.h\"\n\n#include <utility>\n\n#include \"tachyon/base/files/file_path.h\"\n#include \"tachyon/base/logging.h\"\n// #include \"tachyon/base/notreached.h\"\n#include \"tachyon/base/numerics/safe_math.h\"\n#include \"tachyon/base/system/sys_info.h\"\n#include \"tachyon/build/build_config.h\"\n\nnamespace tachyon::base {\n\nconst MemoryMappedFile::Region MemoryMappedFile::Region::kWholeFile = {0, 0};\n\nMemoryMappedFile::~MemoryMappedFile() {\n  CloseHandles();\n}\n\n#if !BUILDFLAG(IS_NACL)\nbool MemoryMappedFile::Initialize(const FilePath& file_name, Access access) {\n  if (IsValid())\n    return false;\n\n  uint32_t flags = 0;\n  switch (access) {\n    case READ_ONLY:\n      flags = File::FLAG_OPEN | File::FLAG_READ;\n      break;\n    case READ_WRITE_COPY:\n      flags = File::FLAG_OPEN | File::FLAG_READ;\n#if BUILDFLAG(IS_FUCHSIA)\n      // Fuchsia's mmap() implementation does not allow us to create a\n      // copy-on-write mapping of a file opened as read-only.\n      flags |= File::FLAG_WRITE;\n#endif\n      break;\n    case READ_WRITE:\n      flags = File::FLAG_OPEN | File::FLAG_READ | File::FLAG_WRITE;\n      break;\n    case READ_WRITE_EXTEND:\n      // Can't open with \"extend\" because no maximum size is known.\n      NOTREACHED();\n      break;\n#if BUILDFLAG(IS_WIN)\n    case READ_CODE_IMAGE:\n      flags |= File::FLAG_OPEN | File::FLAG_READ |\n               File::FLAG_WIN_EXCLUSIVE_WRITE | File::FLAG_WIN_EXECUTE;\n      break;\n#endif\n  }\n  file_.Initialize(file_name, flags);\n\n  if (!file_.IsValid()) {\n    DLOG(ERROR) << \"Couldn't open \" << file_name;\n    return false;\n  }\n\n  if (!MapFileRegionToMemory(Region::kWholeFile, access)) {\n    CloseHandles();\n    return false;\n  }\n\n  return true;\n}\n\nbool MemoryMappedFile::Initialize(File file, Access access) {\n  DCHECK_NE(READ_WRITE_EXTEND, access);\n  return Initialize(std::move(file), Region::kWholeFile, access);\n}\n\nbool MemoryMappedFile::Initialize(File file,\n                                  const Region& region,\n                                  Access access) {\n  switch (access) {\n    case READ_WRITE_EXTEND:\n      DCHECK(Region::kWholeFile != region);\n      {\n        CheckedNumeric<int64_t> region_end(region.offset);\n        region_end += region.size;\n        if (!region_end.IsValid()) {\n          DLOG(ERROR) << \"Region bounds exceed maximum for base::File.\";\n          return false;\n        }\n      }\n      [[fallthrough]];\n    case READ_ONLY:\n    case READ_WRITE:\n    case READ_WRITE_COPY:\n      // Ensure that the region values are valid.\n      if (region.offset < 0) {\n        DLOG(ERROR) << \"Region bounds are not valid.\";\n        return false;\n      }\n      break;\n#if BUILDFLAG(IS_WIN)\n    case READ_CODE_IMAGE:\n      // Can't open with \"READ_CODE_IMAGE\", not supported outside Windows\n      // or with a |region|.\n      NOTREACHED_IN_MIGRATION();\n      break;\n#endif\n  }\n\n  if (IsValid())\n    return false;\n\n  if (region != Region::kWholeFile)\n    DCHECK_GE(region.offset, 0);\n\n  file_ = std::move(file);\n\n  if (!MapFileRegionToMemory(region, access)) {\n    CloseHandles();\n    return false;\n  }\n\n  return true;\n}\n\nbool MemoryMappedFile::IsValid() const {\n  return !bytes_.empty();\n}\n\n// static\nvoid MemoryMappedFile::CalculateVMAlignedBoundaries(int64_t start,\n                                                    size_t size,\n                                                    int64_t* aligned_start,\n                                                    size_t* aligned_size,\n                                                    int32_t* offset) {\n  // Sadly, on Windows, the mmap alignment is not just equal to the page size.\n  uint64_t mask = SysInfo::VMAllocationGranularity() - 1;\n  CHECK(IsValueInRangeForNumericType<int32_t>(mask));\n  *offset = static_cast<int32_t>(static_cast<uint64_t>(start) & mask);\n  *aligned_start = static_cast<int64_t>(static_cast<uint64_t>(start) & ~mask);\n  // The CHECK above means bit 31 is not set in `mask`, which in turn means\n  // *offset is positive.  Therefore casting it to a size_t is safe.\n  *aligned_size =\n      (size + static_cast<size_t>(*offset) + static_cast<size_t>(mask)) & ~mask;\n}\n#endif  // !BUILDFLAG(IS_NACL)\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/files/memory_mapped_file.h",
    "content": "// Copyright 2013 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_FILES_MEMORY_MAPPED_FILE_H_\n#define TACHYON_BASE_FILES_MEMORY_MAPPED_FILE_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <utility>\n\n#include \"absl/types/span.h\"\n\n#include \"tachyon/export.h\"\n#include \"tachyon/base/files/file.h\"\n// #include \"tachyon/base/memory/raw_ptr_exclusion.h\"\n#include \"tachyon/build/build_config.h\"\n\n#if BUILDFLAG(IS_WIN)\n#include \"base/win/scoped_handle.h\"\n#endif\n\nnamespace tachyon::base {\n\nclass FilePath;\n\nclass TACHYON_EXPORT MemoryMappedFile {\n public:\n  enum Access {\n    // Mapping a file into memory effectively allows for file I/O on any thread.\n    // The accessing thread could be paused while data from the file is paged\n    // into memory. Worse, a corrupted filesystem could cause a SEGV within the\n    // program instead of just an I/O error.\n    READ_ONLY,\n\n    // This provides read/write access to a file and must be used with care of\n    // the additional subtleties involved in doing so. Though the OS will do\n    // the writing of data on its own time, too many dirty pages can cause\n    // the OS to pause the thread while it writes them out. The pause can\n    // be as much as 1s on some systems.\n    READ_WRITE,\n\n    // This provides read/write access to the mapped file contents as above, but\n    // applies a copy-on-write policy such that no writes are carried through to\n    // the underlying file.\n    READ_WRITE_COPY,\n\n    // This provides read/write access but with the ability to write beyond\n    // the end of the existing file up to a maximum size specified as the\n    // \"region\". Depending on the OS, the file may or may not be immediately\n    // extended to the maximum size though it won't be loaded in RAM until\n    // needed. Note, however, that the maximum size will still be reserved\n    // in the process address space.\n    READ_WRITE_EXTEND,\n\n#if BUILDFLAG(IS_WIN)\n    // This provides read access, but as executable code used for prefetching\n    // DLLs into RAM to avoid inefficient hard fault patterns such as during\n    // process startup. The accessing thread could be paused while data from\n    // the file is read into memory (if needed).\n    READ_CODE_IMAGE,\n#endif\n  };\n\n  // The default constructor sets all members to invalid/null values.\n  MemoryMappedFile();\n  MemoryMappedFile(const MemoryMappedFile&) = delete;\n  MemoryMappedFile& operator=(const MemoryMappedFile&) = delete;\n  ~MemoryMappedFile();\n\n  // Used to hold information about a region [offset + size] of a file.\n  struct TACHYON_EXPORT Region {\n    static const Region kWholeFile;\n\n    bool operator==(const Region& other) const {\n      return offset == other.offset && size == other.size;\n    }\n    bool operator!=(const Region& other) const {\n      return !operator==(other);\n    }\n\n    // Start of the region (measured in bytes from the beginning of the file).\n    int64_t offset;\n\n    // Length of the region in bytes.\n    size_t size;\n  };\n\n  // Opens an existing file and maps it into memory. |access| can be read-only\n  // or read/write but not read/write+extend. If this object already points\n  // to a valid memory mapped file then this method will fail and return\n  // false. If it cannot open the file, the file does not exist, or the\n  // memory mapping fails, it will return false.\n  [[nodiscard]] bool Initialize(const FilePath& file_name, Access access);\n  [[nodiscard]] bool Initialize(const FilePath& file_name) {\n    return Initialize(file_name, READ_ONLY);\n  }\n\n  // As above, but works with an already-opened file. |access| can be read-only\n  // or read/write but not read/write+extend. MemoryMappedFile takes ownership\n  // of |file| and closes it when done. |file| must have been opened with\n  // permissions suitable for |access|. If the memory mapping fails, it will\n  // return false.\n  [[nodiscard]] bool Initialize(File file, Access access);\n  [[nodiscard]] bool Initialize(File file) {\n    return Initialize(std::move(file), READ_ONLY);\n  }\n\n  // As above, but works with a region of an already-opened file. |access|\n  // must not be READ_CODE_IMAGE. If READ_WRITE_EXTEND is specified then\n  // |region| provides the maximum size of the file. If the memory mapping\n  // fails, it return false.\n  [[nodiscard]] bool Initialize(File file, const Region& region, Access access);\n  [[nodiscard]] bool Initialize(File file, const Region& region) {\n    return Initialize(std::move(file), region, READ_ONLY);\n  }\n\n  const uint8_t* data() const { return bytes_.data(); }\n  uint8_t* data() { return bytes_.data(); }\n  size_t length() const { return bytes_.size(); }\n\n  absl::Span<const uint8_t> bytes() const { return bytes_; }\n  absl::Span<uint8_t> mutable_bytes() { return bytes_; }\n\n  // Is file_ a valid file handle that points to an open, memory mapped file?\n  bool IsValid() const;\n\n private:\n  // Given the arbitrarily aligned memory region [start, size], returns the\n  // boundaries of the region aligned to the granularity specified by the OS,\n  // (a page on Linux, ~32k on Windows) as follows:\n  // - |aligned_start| is page aligned and <= |start|.\n  // - |aligned_size| is a multiple of the VM granularity and >= |size|.\n  // - |offset| is the displacement of |start| w.r.t |aligned_start|.\n  static void CalculateVMAlignedBoundaries(int64_t start,\n                                           size_t size,\n                                           int64_t* aligned_start,\n                                           size_t* aligned_size,\n                                           int32_t* offset);\n\n#if BUILDFLAG(IS_WIN)\n  // Maps the executable file to memory, point `bytes_` to the memory range.\n  // Return true on success.\n  bool MapImageToMemory(Access access);\n#endif\n\n  // Map the file to memory, point `bytes_` to that memory address. Return true\n  // on success, false on any kind of failure. This is a helper for\n  // Initialize().\n  bool MapFileRegionToMemory(const Region& region, Access access);\n\n  // Closes all open handles.\n  void CloseHandles();\n\n  File file_;\n\n  // RAW_PTR_EXCLUSION: Never allocated by PartitionAlloc (always mmap'ed), so\n  // there is no benefit to using a raw_span, only cost.\n  // RAW_PTR_EXCLUSION span<uint8_t> bytes_;\n  absl::Span<uint8_t> bytes_;\n\n#if BUILDFLAG(IS_WIN)\n  win::ScopedHandle file_mapping_;\n#endif\n};\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_FILES_MEMORY_MAPPED_FILE_H_\n"
  },
  {
    "path": "tachyon/base/files/memory_mapped_file_posix.cc",
    "content": "// Copyright 2013 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/files/memory_mapped_file.h\"\n\n#include <fcntl.h>\n#include <stddef.h>\n#include <stdint.h>\n#include <sys/mman.h>\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include \"tachyon/base/files/file_util.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/numerics/safe_conversions.h\"\n// #include \"tachyon/base/threading/scoped_blocking_call.h\"\n#include \"tachyon/build/build_config.h\"\n\nnamespace tachyon::base {\n\nMemoryMappedFile::MemoryMappedFile() = default;\n\n#if !BUILDFLAG(IS_NACL)\nbool MemoryMappedFile::MapFileRegionToMemory(\n    const MemoryMappedFile::Region& region,\n    Access access) {\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n\n  off_t map_start = 0;\n  size_t map_size = 0u;\n  int32_t data_offset = 0;\n  size_t byte_size = 0u;\n\n  if (region == MemoryMappedFile::Region::kWholeFile) {\n    int64_t file_len = file_.GetLength();\n    if (file_len < 0) {\n      DPLOG(ERROR) << \"fstat \" << file_.GetPlatformFile();\n      return false;\n    }\n    if (!IsValueInRangeForNumericType<size_t>(file_len))\n      return false;\n    map_size = base::checked_cast<size_t>(file_len);\n    byte_size = map_size;\n  } else {\n    // The region can be arbitrarily aligned. mmap, instead, requires both the\n    // start and size to be page-aligned. Hence, we map here the page-aligned\n    // outer region [|aligned_start|, |aligned_start| + |size|] which contains\n    // |region| and then add up the |data_offset| displacement.\n    int64_t aligned_start = 0;\n    size_t aligned_size = 0u;\n    CalculateVMAlignedBoundaries(region.offset,\n                                 region.size,\n                                 &aligned_start,\n                                 &aligned_size,\n                                 &data_offset);\n\n    // Ensure that the casts in the mmap call below are sane.\n    if (aligned_start < 0 ||\n        !IsValueInRangeForNumericType<off_t>(aligned_start)) {\n      DLOG(ERROR) << \"Region bounds are not valid for mmap\";\n      return false;\n    }\n\n    map_start = base::checked_cast<off_t>(aligned_start);\n    map_size = aligned_size;\n    byte_size = region.size;\n  }\n\n  if (map_size == 0u) {\n    // mmap() requires `map_size > 0`, and this ensures an empty span indicates\n    // invalid.\n    return false;\n  }\n\n  int prot = 0;\n  int flags = MAP_SHARED;\n  switch (access) {\n    case READ_ONLY:\n      prot |= PROT_READ;\n      break;\n\n    case READ_WRITE:\n      prot |= PROT_READ | PROT_WRITE;\n      break;\n\n    case READ_WRITE_COPY:\n      prot |= PROT_READ | PROT_WRITE;\n      flags = MAP_PRIVATE;\n      break;\n\n    case READ_WRITE_EXTEND:\n      prot |= PROT_READ | PROT_WRITE;\n\n      if (!AllocateFileRegion(&file_, region.offset, region.size))\n        return false;\n\n      break;\n  }\n\n  auto* ptr = static_cast<uint8_t*>(\n      mmap(nullptr, map_size, prot, flags, file_.GetPlatformFile(), map_start));\n  if (ptr == MAP_FAILED) {\n    DPLOG(ERROR) << \"mmap \" << file_.GetPlatformFile();\n    return false;\n  }\n\n  // SAFETY: For the span construction to be valid, `ptr` needs to point to at\n  // least `map_size` many bytes, which is the guarantee of mmap() when it\n  // returns a valid pointer, and that `data_offset + byte_size <= map_size`.\n  //\n  // If the mapping is of the whole file, `map_size == byte_size`\n  // and `data_offset == 0`, so `data_offset + byte_size <= map_size` is\n  // trivially satisfied.\n  //\n  // If the mapping is a sub-range of the file:\n  // - `aligned_start` is page aligned and <= `start`.\n  // - `map_size` is a multiple of the VM granularity and >=\n  //   `byte_size`.\n  // - `data_offset` is the displacement of `start` w.r.t `aligned_start`.\n  // |..................|xxxxxxxxxxxxxxxxxx|.................|\n  // ^ aligned start    ^ start            |                 |\n  // ^------------------^ data_offset      |                 |\n  //                    ^------------------^ byte_size       |\n  // ^-------------------------------------------------------^ map_size\n  //\n  // The `data_offset` undoes the alignment of start. The `map_size` contains\n  // the padding before and after the mapped region to satisfy alignment. So\n  // the `data_offset + byte_size <= map_size`.\n  bytes_ = UNSAFE_BUFFERS(absl::Span(ptr + data_offset, byte_size));\n  return true;\n}\n#endif\n\nvoid MemoryMappedFile::CloseHandles() {\n  // TODO(chokobole):\n  // ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);\n\n  if (!bytes_.empty()) {\n    munmap(bytes_.data(), bytes_.size());\n  }\n  file_.Close();\n  bytes_ = absl::Span<uint8_t>();\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/files/platform_file.h",
    "content": "// Copyright 2017 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_FILES_PLATFORM_FILE_H_\n#define TACHYON_BASE_FILES_PLATFORM_FILE_H_\n\n#include \"tachyon/base/files/scoped_file.h\"\n#include \"tachyon/build/build_config.h\"\n\n#if BUILDFLAG(IS_WIN)\n#include \"tachyon/base/win/scoped_handle.h\"\n#include \"tachyon/base/win/windows_types.h\"\n#endif\n\n// This file defines platform-independent types for dealing with\n// platform-dependent files. If possible, use the higher-level base::File class\n// rather than these primitives.\n\nnamespace tachyon::base {\n\n#if BUILDFLAG(IS_WIN)\n\nusing PlatformFile = HANDLE;\nusing ScopedPlatformFile = ::tachyon::base::win::ScopedHandle;\n\n// It would be nice to make this constexpr but INVALID_HANDLE_VALUE is a\n// ((void*)(-1)) which Clang rejects since reinterpret_cast is technically\n// disallowed in constexpr. Visual Studio accepts this, however.\nconst PlatformFile kInvalidPlatformFile = INVALID_HANDLE_VALUE;\n\n#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)\n\nusing PlatformFile = int;\nusing ScopedPlatformFile = ::tachyon::base::ScopedFD;\n\nconstexpr PlatformFile kInvalidPlatformFile = -1;\n\n#endif\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_FILES_PLATFORM_FILE_H_\n"
  },
  {
    "path": "tachyon/base/files/scoped_file.cc",
    "content": "// Copyright 2014 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/files/scoped_file.h\"\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/build/build_config.h\"\n\n#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)\n#include <errno.h>\n#include <unistd.h>\n\n#include \"tachyon/base/posix/eintr_wrapper.h\"\n#endif\n\nnamespace tachyon::base {\nnamespace internal {\n\n#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)\n\n// static\nvoid ScopedFDCloseTraits::Free(int fd) {\n  // It's important to crash here.\n  // There are security implications to not closing a file descriptor\n  // properly. As file descriptors are \"capabilities\", keeping them open\n  // would make the current process keep access to a resource. Much of\n  // Chrome relies on being able to \"drop\" such access.\n  // It's especially problematic on Linux with the setuid sandbox, where\n  // a single open directory would bypass the entire security model.\n  int ret = IGNORE_EINTR(close(fd));\n\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_APPLE) || \\\n    BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_ANDROID)\n  // NB: Some file descriptors can return errors from close() e.g. network\n  // filesystems such as NFS and Linux input devices. On Linux, macOS, and\n  // Fuchsia's POSIX layer, errors from close other than EBADF do not indicate\n  // failure to actually close the fd.\n  if (ret != 0 && errno != EBADF)\n    ret = 0;\n#endif\n\n  PCHECK(0 == ret);\n}\n\n#endif  // BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)\n\n}  // namespace internal\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/files/scoped_file.h",
    "content": "// Copyright 2014 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_FILES_SCOPED_FILE_H_\n#define TACHYON_BASE_FILES_SCOPED_FILE_H_\n\n#include <stdio.h>\n\n#include <memory>\n\n#include \"tachyon/export.h\"\n#include \"tachyon/base/scoped_generic.h\"\n#include \"tachyon/build/build_config.h\"\n\nnamespace tachyon::base {\n\nnamespace internal {\n\n#if BUILDFLAG(IS_ANDROID)\n// Use fdsan on android.\nstruct TACHYON_EXPORT ScopedFDCloseTraits : public ScopedGenericOwnershipTracking {\n  static int InvalidValue() { return -1; }\n  static void Free(int);\n  static void Acquire(const ScopedGeneric<int, ScopedFDCloseTraits>&, int);\n  static void Release(const ScopedGeneric<int, ScopedFDCloseTraits>&, int);\n};\n#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)\n#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)\n// On ChromeOS and Linux we guard FD lifetime with a global table and hook into\n// libc close() to perform checks.\nstruct TACHYON_EXPORT ScopedFDCloseTraits : public ScopedGenericOwnershipTracking {\n#else\nstruct TACHYON_EXPORT ScopedFDCloseTraits {\n#endif\n  static int InvalidValue() {\n    return -1;\n  }\n  static void Free(int fd);\n#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)\n  static void Acquire(const ScopedGeneric<int, ScopedFDCloseTraits>&, int);\n  static void Release(const ScopedGeneric<int, ScopedFDCloseTraits>&, int);\n#endif\n};\n#endif\n\n// Functor for |ScopedFILE| (below).\nstruct ScopedFILECloser {\n  inline void operator()(FILE* x) const {\n    if (x)\n      fclose(x);\n  }\n};\n\n}  // namespace internal\n\n#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)\nnamespace subtle {\n\n// Enables or disables enforcement of FD ownership as tracked by ScopedFD\n// objects. Enforcement is disabled by default since it proves unwieldy in some\n// test environments, but tracking is always done. It's best to enable this as\n// early as possible in a process's lifetime.\nvoid TACHYON_EXPORT EnableFDOwnershipEnforcement(bool enabled);\n\n// Resets ownership state of all FDs. The only permissible use of this API is\n// in a forked child process between the fork() and a subsequent exec() call.\n//\n// For one issue, it is common to mass-close most open FDs before calling\n// exec(), to avoid leaking FDs into the new executable's environment. For\n// processes which have enabled FD ownership enforcement, this reset operation\n// is necessary before performing such closures.\n//\n// Furthermore, fork()+exec() may be used in a multithreaded context, and\n// because fork() is not atomic, the FD ownership state in the child process may\n// be inconsistent with the actual set of opened file descriptors once fork()\n// returns in the child process.\n//\n// It is therefore especially important to call this ASAP after fork() in the\n// child process if any FD manipulation will be done prior to the subsequent\n// exec call.\nvoid TACHYON_EXPORT ResetFDOwnership();\n\n}  // namespace subtle\n#endif\n\n// -----------------------------------------------------------------------------\n\n#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)\n// A low-level Posix file descriptor closer class. Use this when writing\n// platform-specific code, especially that does non-file-like things with the\n// FD (like sockets).\n//\n// If you're writing low-level Windows code, see base/win/scoped_handle.h\n// which provides some additional functionality.\n//\n// If you're writing cross-platform code that deals with actual files, you\n// should generally use base::File instead which can be constructed with a\n// handle, and in addition to handling ownership, has convenient cross-platform\n// file manipulation functions on it.\ntypedef ScopedGeneric<int, internal::ScopedFDCloseTraits> ScopedFD;\n#endif\n\n// Automatically closes |FILE*|s.\ntypedef std::unique_ptr<FILE, internal::ScopedFILECloser> ScopedFILE;\n\n#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)\n// Queries the ownership status of an FD, i.e. whether it is currently owned by\n// a ScopedFD in the calling process.\nbool TACHYON_EXPORT IsFDOwned(int fd);\n#endif  // BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_FILES_SCOPED_FILE_H_\n"
  },
  {
    "path": "tachyon/base/files/scoped_file_linux.cc",
    "content": "// Copyright 2021 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/files/scoped_file.h\"\n\n#include <dlfcn.h>\n\n#include <algorithm>\n#include <array>\n#include <atomic>\n#include <string_view>\n\n#include \"tachyon/base/compiler_specific.h\"\n// TODO(chokobole):\n// #include \"tachyon/base/debug/stack_trace.h\"\n#include \"tachyon/base/immediate_crash.h\"\n#include \"tachyon/base/logging.h\"\n\nnamespace {\n\n// We want to avoid any kind of allocations in our close() implementation, so we\n// use a fixed-size table. Given our common FD limits and the preference for new\n// FD allocations to use the lowest available descriptor, this should be\n// sufficient to guard most FD lifetimes. The worst case scenario if someone\n// attempts to own a higher FD is that we don't track it.\nconst int kMaxTrackedFds = 4096;\n\nstd::atomic_bool g_is_ownership_enforced{false};\nstd::array<std::atomic_bool, kMaxTrackedFds> g_is_fd_owned;\n\nNOINLINE void CrashOnFdOwnershipViolation() {\n  RAW_LOG(ERROR, \"Crashing due to FD ownership violation:\\n\");\n  // TODO(chokobole):\n  // tachyon::base::debug::StackTrace().Print();\n  tachyon::base::ImmediateCrash();\n}\n\nbool CanTrack(int fd) {\n  return fd >= 0 && fd < kMaxTrackedFds;\n}\n\nvoid UpdateAndCheckFdOwnership(int fd, bool owned) {\n  if (CanTrack(fd) &&\n      g_is_fd_owned[static_cast<size_t>(fd)].exchange(owned) == owned &&\n      g_is_ownership_enforced) {\n    CrashOnFdOwnershipViolation();\n  }\n}\n\n}  // namespace\n\nnamespace tachyon::base {\nnamespace internal {\n\n// static\nvoid ScopedFDCloseTraits::Acquire(const ScopedFD& owner, int fd) {\n  UpdateAndCheckFdOwnership(fd, /*owned=*/true);\n}\n\n// static\nvoid ScopedFDCloseTraits::Release(const ScopedFD& owner, int fd) {\n  UpdateAndCheckFdOwnership(fd, /*owned=*/false);\n}\n\n}  // namespace internal\n\nnamespace subtle {\n\nvoid EnableFDOwnershipEnforcement(bool enabled) {\n  g_is_ownership_enforced = enabled;\n}\n\nvoid ResetFDOwnership() {\n  std::fill(g_is_fd_owned.begin(), g_is_fd_owned.end(), false);\n}\n\n}  // namespace subtle\n\nbool IsFDOwned(int fd) {\n  return CanTrack(fd) && g_is_fd_owned[static_cast<size_t>(fd)];\n}\n\n}  // namespace tachyon::base\n\nusing LibcCloseFuncPtr = int (*)(int);\n\n// Load the libc close symbol to forward to from the close wrapper.\nLibcCloseFuncPtr LoadCloseSymbol() {\n#if defined(THREAD_SANITIZER)\n  // If TSAN is enabled use __interceptor___close first to make sure the TSAN\n  // wrapper gets called.\n  return reinterpret_cast<LibcCloseFuncPtr>(\n      dlsym(RTLD_DEFAULT, \"__interceptor___close\"));\n#else\n  return reinterpret_cast<LibcCloseFuncPtr>(dlsym(RTLD_NEXT, \"close\"));\n#endif\n}\n\nextern \"C\" {\n\n// TODO(chokobole):\n// NO_SANITIZE(\"cfi-icall\")\n__attribute__((visibility(\"default\"), noinline)) int close(int fd) {\n  static LibcCloseFuncPtr libc_close = LoadCloseSymbol();\n  if (tachyon::base::IsFDOwned(fd) && g_is_ownership_enforced)\n    CrashOnFdOwnershipViolation();\n  if (libc_close == nullptr) {\n    RAW_LOG(ERROR, \"close symbol missing\\n\");\n    tachyon::base::ImmediateCrash();\n  }\n  return libc_close(fd);\n}\n\n}  // extern \"C\"\n"
  },
  {
    "path": "tachyon/base/files/scoped_file_linux_unittest.cc",
    "content": "// Copyright 2021 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/files/scoped_file.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/files/file_util.h\"\n#include \"tachyon/base/files/scoped_temp_dir.h\"\n\nnamespace tachyon::base {\nnamespace {\n\nclass ScopedFDOwnershipTrackingTest : public testing::Test {\n public:\n  void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); }\n  void TearDown() override { ASSERT_TRUE(temp_dir_.Delete()); }\n\n  ScopedFD OpenFD() {\n    FilePath dont_care;\n    return CreateAndOpenFdForTemporaryFileInDir(temp_dir_.GetPath(),\n                                                &dont_care);\n  }\n\n private:\n  ScopedTempDir temp_dir_;\n};\n\nTEST_F(ScopedFDOwnershipTrackingTest, BasicTracking) {\n  ScopedFD fd = OpenFD();\n  EXPECT_TRUE(IsFDOwned(fd.get()));\n  int fd_value = fd.get();\n  fd.reset();\n  EXPECT_FALSE(IsFDOwned(fd_value));\n}\n\n#if defined(GTEST_HAS_DEATH_TEST)\n\nTEST_F(ScopedFDOwnershipTrackingTest, NoDoubleOwnership) {\n  ScopedFD fd = OpenFD();\n  subtle::EnableFDOwnershipEnforcement(true);\n  EXPECT_DEATH(ScopedFD(fd.get()), \"\");\n}\n\nTEST_F(ScopedFDOwnershipTrackingTest, CrashOnUnownedClose) {\n  ScopedFD fd = OpenFD();\n  subtle::EnableFDOwnershipEnforcement(true);\n  EXPECT_DEATH(close(fd.get()), \"\");\n}\n\n#endif  // defined(GTEST_HAS_DEATH_TEST)\n\n}  // namespace\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/files/scoped_temp_dir.cc",
    "content": "// Copyright 2011 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/files/scoped_temp_dir.h\"\n\n#include \"tachyon/base/files/file_util.h\"\n#include \"tachyon/base/logging.h\"\n\nnamespace tachyon::base {\n\nnamespace {\n\nconstexpr char kScopedDirPrefix[] = \"scoped_dir\";\n\n}  // namespace\n\nScopedTempDir::ScopedTempDir() = default;\n\nScopedTempDir::ScopedTempDir(ScopedTempDir&& other) noexcept\n    : path_(other.Take()) {}\n\nScopedTempDir& ScopedTempDir::operator=(ScopedTempDir&& other) {\n  if (!path_.empty() && !Delete())\n    DLOG(WARNING) << \"Could not delete temp dir in operator=().\";\n  path_ = other.Take();\n  return *this;\n}\n\nScopedTempDir::~ScopedTempDir() {\n  if (!path_.empty() && !Delete())\n    DLOG(WARNING) << \"Could not delete temp dir in dtor.\";\n}\n\nbool ScopedTempDir::CreateUniqueTempDir() {\n  if (!path_.empty())\n    return false;\n\n  // This \"scoped_dir\" prefix is only used on Windows and serves as a template\n  // for the unique name.\n  if (!CreateNewTempDirectory(kScopedDirPrefix, &path_))\n    return false;\n\n  return true;\n}\n\nbool ScopedTempDir::CreateUniqueTempDirUnderPath(const FilePath& base_path) {\n  if (!path_.empty())\n    return false;\n\n  // If |base_path| does not exist, create it.\n  if (!CreateDirectory(base_path))\n    return false;\n\n  // Create a new, uniquely named directory under |base_path|.\n  if (!CreateTemporaryDirInDir(base_path, kScopedDirPrefix, &path_))\n    return false;\n\n  return true;\n}\n\nbool ScopedTempDir::Set(const FilePath& path) {\n  if (!path_.empty())\n    return false;\n\n  if (!DirectoryExists(path) && !CreateDirectory(path))\n    return false;\n\n  path_ = path;\n  return true;\n}\n\nbool ScopedTempDir::Delete() {\n  if (path_.empty())\n    return false;\n\n  bool ret = DeletePathRecursively(path_);\n  if (ret) {\n    // We only clear the path if deleted the directory.\n    path_.clear();\n  }\n\n  return ret;\n}\n\nFilePath ScopedTempDir::Take() {\n  return std::exchange(path_, FilePath());\n}\n\nconst FilePath& ScopedTempDir::GetPath() const {\n  DCHECK(!path_.empty()) << \"Did you call CreateUniqueTempDir* before?\";\n  return path_;\n}\n\nbool ScopedTempDir::IsValid() const {\n  return !path_.empty() && DirectoryExists(path_);\n}\n\n// static\nconst char* ScopedTempDir::GetTempDirPrefix() {\n  return kScopedDirPrefix;\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/files/scoped_temp_dir.h",
    "content": "// Copyright 2011 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_FILES_SCOPED_TEMP_DIR_H_\n#define TACHYON_BASE_FILES_SCOPED_TEMP_DIR_H_\n\n// An object representing a temporary / scratch directory that should be\n// cleaned up (recursively) when this object goes out of scope.  Since deletion\n// occurs during the destructor, no further error handling is possible if the\n// directory fails to be deleted.  As a result, deletion is not guaranteed by\n// this class.  (However note that, whenever possible, by default\n// CreateUniqueTempDir creates the directory in a location that is\n// automatically cleaned up on reboot, or at other appropriate times.)\n//\n// Multiple calls to the methods which establish a temporary directory\n// (CreateUniqueTempDir, CreateUniqueTempDirUnderPath, and Set) must have\n// intervening calls to Delete or Take, or the calls will fail.\n\n#include \"tachyon/export.h\"\n#include \"tachyon/base/files/file_path.h\"\n\nnamespace tachyon::base {\n\nclass TACHYON_EXPORT ScopedTempDir {\n public:\n  // No directory is owned/created initially.\n  ScopedTempDir();\n\n  ScopedTempDir(ScopedTempDir&&) noexcept;\n  ScopedTempDir& operator=(ScopedTempDir&&);\n\n  // Recursively delete path.\n  ~ScopedTempDir();\n\n  // Creates a unique directory in TempPath, and takes ownership of it.\n  // See file_util::CreateNewTemporaryDirectory.\n  [[nodiscard]] bool CreateUniqueTempDir();\n\n  // Creates a unique directory under a given path, and takes ownership of it.\n  [[nodiscard]] bool CreateUniqueTempDirUnderPath(const FilePath& path);\n\n  // Takes ownership of directory at |path|, creating it if necessary.\n  // Don't call multiple times unless Take() has been called first.\n  [[nodiscard]] bool Set(const FilePath& path);\n\n  // Deletes the temporary directory wrapped by this object.\n  [[nodiscard]] bool Delete();\n\n  // Caller takes ownership of the temporary directory so it won't be destroyed\n  // when this object goes out of scope.\n  FilePath Take();\n\n  // Returns the path to the created directory. Call one of the\n  // CreateUniqueTempDir* methods before getting the path.\n  const FilePath& GetPath() const;\n\n  // Returns true if path_ is non-empty and exists.\n  bool IsValid() const;\n\n  // Returns the prefix used for temp directory names generated by\n  // ScopedTempDirs.\n  static const char* GetTempDirPrefix();\n\n private:\n  FilePath path_;\n};\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_FILES_SCOPED_TEMP_DIR_H_\n"
  },
  {
    "path": "tachyon/base/files/scoped_temp_dir_unittest.cc",
    "content": "// Copyright 2011 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include <string>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/files/file.h\"\n#include \"tachyon/base/files/file_util.h\"\n#include \"tachyon/base/files/scoped_temp_dir.h\"\n// #include \"tachyon/base/path_service.h\"\n#include \"tachyon/build/build_config.h\"\n\n#if BUILDFLAG(IS_WIN)\n#include <shlobj.h>\n#endif\n\nnamespace tachyon::base {\n\nTEST(ScopedTempDir, FullPath) {\n  FilePath test_path;\n  CreateNewTempDirectory(\"scoped_temp_dir\", &test_path);\n\n  // Against an existing dir, it should get destroyed when leaving scope.\n  EXPECT_TRUE(DirectoryExists(test_path));\n  {\n    ScopedTempDir dir;\n    EXPECT_TRUE(dir.Set(test_path));\n    EXPECT_TRUE(dir.IsValid());\n  }\n  EXPECT_FALSE(DirectoryExists(test_path));\n\n  {\n    ScopedTempDir dir;\n    EXPECT_TRUE(dir.Set(test_path));\n    // Now the dir doesn't exist, so ensure that it gets created.\n    EXPECT_TRUE(DirectoryExists(test_path));\n    // When we call Release(), it shouldn't get destroyed when leaving scope.\n    FilePath path = dir.Take();\n    EXPECT_EQ(path.value(), test_path.value());\n    EXPECT_FALSE(dir.IsValid());\n  }\n  EXPECT_TRUE(DirectoryExists(test_path));\n\n  // Clean up.\n  {\n    ScopedTempDir dir;\n    EXPECT_TRUE(dir.Set(test_path));\n  }\n  EXPECT_FALSE(DirectoryExists(test_path));\n}\n\nTEST(ScopedTempDir, TempDir) {\n  // In this case, just verify that a directory was created and that it's a\n  // child of TempDir.\n  FilePath test_path;\n  {\n    ScopedTempDir dir;\n    EXPECT_TRUE(dir.CreateUniqueTempDir());\n    test_path = dir.GetPath();\n    EXPECT_TRUE(DirectoryExists(test_path));\n\n#if BUILDFLAG(IS_WIN)\n    FilePath expected_parent_dir;\n    if (!GetSecureSystemTemp(&expected_parent_dir)) {\n      EXPECT_TRUE(PathService::Get(DIR_TEMP, &expected_parent_dir));\n    }\n    EXPECT_TRUE(expected_parent_dir.IsParent(test_path));\n#else   // BUILDFLAG(IS_WIN)\n    FilePath tmp_dir;\n    EXPECT_TRUE(GetTempDir(&tmp_dir));\n    EXPECT_TRUE(test_path.value().find(tmp_dir.value()) != std::string::npos);\n#endif  // BUILDFLAG(IS_WIN)\n  }\n  EXPECT_FALSE(DirectoryExists(test_path));\n}\n\nTEST(ScopedTempDir, UniqueTempDirUnderPath) {\n  // Create a path which will contain a unique temp path.\n  FilePath base_path;\n  ASSERT_TRUE(\n      CreateNewTempDirectory(\"base_dir\", &base_path));\n\n  FilePath test_path;\n  {\n    ScopedTempDir dir;\n    EXPECT_TRUE(dir.CreateUniqueTempDirUnderPath(base_path));\n    test_path = dir.GetPath();\n    EXPECT_TRUE(DirectoryExists(test_path));\n    EXPECT_TRUE(base_path.IsParent(test_path));\n    EXPECT_TRUE(test_path.value().find(base_path.value()) != std::string::npos);\n  }\n  EXPECT_FALSE(DirectoryExists(test_path));\n  DeletePathRecursively(base_path);\n}\n\nTEST(ScopedTempDir, MultipleInvocations) {\n  ScopedTempDir dir;\n  EXPECT_TRUE(dir.CreateUniqueTempDir());\n  EXPECT_FALSE(dir.CreateUniqueTempDir());\n  EXPECT_TRUE(dir.Delete());\n  EXPECT_TRUE(dir.CreateUniqueTempDir());\n  EXPECT_FALSE(dir.CreateUniqueTempDir());\n  ScopedTempDir other_dir;\n  EXPECT_TRUE(other_dir.Set(dir.Take()));\n  EXPECT_TRUE(dir.CreateUniqueTempDir());\n  EXPECT_FALSE(dir.CreateUniqueTempDir());\n  EXPECT_FALSE(other_dir.CreateUniqueTempDir());\n}\n\nTEST(ScopedTempDir, Move) {\n  ScopedTempDir dir;\n  EXPECT_TRUE(dir.CreateUniqueTempDir());\n  FilePath dir_path = dir.GetPath();\n  EXPECT_TRUE(DirectoryExists(dir_path));\n  {\n    ScopedTempDir other_dir(std::move(dir));\n    EXPECT_EQ(dir_path, other_dir.GetPath());\n    EXPECT_TRUE(DirectoryExists(dir_path));\n    EXPECT_FALSE(dir.IsValid());\n  }\n  EXPECT_FALSE(DirectoryExists(dir_path));\n}\n\n#if BUILDFLAG(IS_WIN)\nTEST(ScopedTempDir, LockedTempDir) {\n  ScopedTempDir dir;\n  EXPECT_TRUE(dir.CreateUniqueTempDir());\n  File file(dir.GetPath().Append(\"temp\"),\n            File::FLAG_CREATE_ALWAYS | File::FLAG_WRITE);\n  EXPECT_TRUE(file.IsValid());\n  EXPECT_EQ(File::FILE_OK, file.error_details());\n  EXPECT_FALSE(dir.Delete());  // We should not be able to delete.\n  EXPECT_FALSE(dir.GetPath().empty());  // We should still have a valid path.\n  file.Close();\n  // Now, we should be able to delete.\n  EXPECT_TRUE(dir.Delete());\n}\n#endif  // BUILDFLAG(IS_WIN)\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/flag/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"flag\",\n    srcs = [\"flag.cc\"],\n    hdrs = [\n        \"flag.h\",\n        \"flag_forward.h\",\n        \"flag_value_traits.h\",\n        \"numeric_flags.h\",\n    ],\n    deps = [\n        \"//tachyon/base:environment\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base/containers:contains\",\n        \"//tachyon/base/files:file_path\",\n        \"//tachyon/base/strings:string_number_conversions\",\n        \"//tachyon/base/strings:string_util\",\n        \"@com_google_googletest//:gtest_prod\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"flag_parser\",\n    srcs = [\"flag_parser.cc\"],\n    hdrs = [\"flag_parser.h\"],\n    deps = [\n        \":flag\",\n        \"//tachyon/base:compiler_specific\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"flag_unittests\",\n    srcs = [\n        \"flag_parser_unittest.cc\",\n        \"flag_unittest.cc\",\n    ],\n    deps = [\n        \":flag_parser\",\n        \"//tachyon/base/test:scoped_environment\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/base/flag/flag.cc",
    "content": "// Copyright (c) 2020 The Console Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/flag/flag.h\"\n\n#include <algorithm>\n\nnamespace tachyon::base {\nnamespace {\n\nsize_t Append(std::ostream& ss, std::string_view text) {\n  ss << text;\n  return text.length();\n}\n\nstd::string AlignAtIndexAndAppend(std::stringstream& ss, std::string_view text,\n                                  int index, int index2) {\n  int final_index = index;\n  if (final_index <= 0) {\n    ss << std::endl;\n    final_index = index2;\n  }\n  ss << std::string(final_index, ' ') << text;\n  return ss.str();\n}\n\n}  // namespace\n\nbool IsValidFlagName(std::string_view text) {\n  if (text.length() == 0) return false;\n  if (!absl::ascii_isalpha(text[0])) return false;\n  text.remove_prefix(1);\n  return std::all_of(text.begin(), text.end(), [](char c) {\n    return absl::ascii_isalpha(c) || absl::ascii_isdigit(c) || c == '_';\n  });\n}\n\nFlagBase::FlagBase() = default;\n\nFlagBase::~FlagBase() = default;\n\nconst std::string& FlagBase::display_name() const {\n  if (!name_.empty()) return name_;\n  if (!long_name_.empty()) return long_name_;\n  return short_name_;\n}\n\nstd::string FlagBase::display_help(int help_start) const {\n  int remain_len = help_start;\n  std::stringstream ss;\n  if (is_positional()) {\n    remain_len -= Append(ss, name_);\n  } else {\n    if (!short_name_.empty()) {\n      remain_len -= Append(ss, short_name_);\n    }\n\n    if (!long_name_.empty()) {\n      if (!short_name_.empty()) {\n        remain_len -= Append(ss, \", \");\n      }\n      remain_len -= Append(ss, long_name_);\n    }\n  }\n\n  return AlignAtIndexAndAppend(ss, help_, remain_len, help_start);\n}\n\nbool FlagBase::ConsumeNamePrefix(FlagParserBase& parser,\n                                 std::string_view* arg) const {\n  std::string_view input = *arg;\n  if (!long_name_.empty()) {\n    if (ConsumePrefix(&input, long_name_)) {\n      if (input.empty() || input[0] == '=') {\n        *arg = input;\n        return true;\n      }\n    }\n  }\n  if (!short_name_.empty()) {\n    if (ConsumePrefix(&input, short_name_)) {\n      if (input.empty() || input[0] == '=') {\n        *arg = input;\n        return true;\n      }\n    }\n  }\n  return false;\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/flag/flag.h",
    "content": "// Copyright (c) 2020 The Console Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_FLAG_FLAG_H_\n#define TACHYON_BASE_FLAG_FLAG_H_\n\n#include <string>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"absl/strings/substitute.h\"\n#include \"gtest/gtest_prod.h\"\n\n#include \"tachyon/base/containers/contains.h\"\n#include \"tachyon/base/environment.h\"\n#include \"tachyon/base/flag/flag_forward.h\"\n#include \"tachyon/base/flag/flag_value_traits.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/strings/string_util.h\"\n#include \"tachyon/export.h\"\n\nnamespace tachyon::base {\n\nTACHYON_EXPORT bool IsValidFlagName(std::string_view text);\n\nclass FlagParserBase;\nclass FlagParser;\nclass SubParser;\n\ntemplate <typename T>\nclass FlagBaseBuilder {\n public:\n  T& set_short_name(std::string_view short_name) {\n    T* impl = static_cast<T*>(this);\n    std::string_view text = short_name;\n    if (!ConsumePrefix(&text, \"-\")) return *impl;\n    if (!(text.length() == 1 && absl::ascii_isalpha(text[0]))) return *impl;\n\n    impl->short_name_ = std::string(short_name);\n    return *impl;\n  }\n\n  T& set_long_name(std::string_view long_name) {\n    T* impl = static_cast<T*>(this);\n    std::string_view text = long_name;\n    if (!ConsumePrefix(&text, \"--\")) return *impl;\n    if (!IsValidFlagName(text)) return *impl;\n\n    impl->long_name_ = std::string(long_name);\n    return *impl;\n  }\n\n  T& set_name(std::string_view name) {\n    T* impl = static_cast<T*>(this);\n    std::string_view text = name;\n    if (!IsValidFlagName(text)) return *impl;\n\n    impl->name_ = std::string(name);\n    return *impl;\n  }\n\n  T& set_help(std::string_view help) {\n    T* impl = static_cast<T*>(this);\n    impl->help_ = std::string(help);\n    return *impl;\n  }\n\n  T& set_required() {\n    T* impl = static_cast<T*>(this);\n    impl->is_required_ = true;\n    return *impl;\n  }\n};\n\n// FlagBase must have |short_name_|, |long_name_| or |name_|.\n// |short_name_| should be a alphabet with a prefix \"-\".\n// |long_name_| should contain alphabet, digit or underscore with a prefix \"--\".\n// |name_| should contain alphabet, digit or underscore without any prefix.\n// |name_| and |long_name_| should start with alphabet.\n// e.g, --3a or --_ab are not allowed.\n// |long_name_| and |short_name_| can be set together, but |name_| shouldn't.\nclass TACHYON_EXPORT FlagBase {\n public:\n  FlagBase();\n  FlagBase(const FlagBase& other) = delete;\n  FlagBase& operator=(const FlagBase& other) = delete;\n  virtual ~FlagBase();\n\n  const std::string& short_name() const { return short_name_; }\n  const std::string& long_name() const { return long_name_; }\n  const std::string& name() const { return name_; }\n  const std::string& help() const { return help_; }\n\n  // Returns true if the flag was marked with required.\n  bool is_required() const { return is_required_; }\n  // Returns true |name_| was set.\n  bool is_positional() const { return !name_.empty(); }\n  // Returns true |short_name_| or |long_name_| was set.\n  bool is_optional() const {\n    return !short_name_.empty() || !long_name_.empty();\n  }\n  // Returns true if a value was set.\n  bool is_set() const { return is_set_; }\n  // Returns true if a flag is an instance of SubParser\n  virtual bool IsSubParser() const { return false; }\n\n  SubParser* ToSubParser() {\n    DCHECK(IsSubParser());\n    return reinterpret_cast<SubParser*>(this);\n  }\n\n  // Returns |name_| if it is positional.\n  // Otherwise, it returns |long_name_| if it is not empty.\n  // Returns |short_name_| if |long_name_| is empty.\n  const std::string& display_name() const;\n  std::string display_help(int help_start = 0) const;\n\n protected:\n  template <typename T>\n  friend class FlagBaseBuilder;\n  friend class FlagParserBase;\n  FRIEND_TEST(FlagParserTest, SubParserTest);\n\n  bool ConsumeNamePrefix(FlagParserBase& parser, std::string_view* arg) const;\n\n  // Returns true if underlying type of Flag<T>, in other words, T is bool.\n  virtual bool NeedsValue() const = 0;\n  virtual bool ParseValue(std::string_view arg, std::string* reason) = 0;\n  virtual bool ParseValueFromEnvironment(std::string* reason) { return true; }\n\n  void reset() { is_set_ = false; }\n\n  std::string short_name_;\n  std::string long_name_;\n  std::string name_;\n  std::string help_;\n  bool is_required_ = false;\n  bool is_set_ = false;\n};\n\ntemplate <typename T, typename value_type>\nclass FlagBuilder : public FlagBaseBuilder<T> {\n public:\n  T& set_default_value(const value_type& value) {\n    T* impl = static_cast<T*>(this);\n    *(impl->value_) = value;\n    return *impl;\n  }\n\n  T& set_env_name(std::string_view env_name) {\n    T* impl = static_cast<T*>(this);\n    impl->env_name_ = std::string(env_name);\n    return *impl;\n  }\n};\n\ntemplate <typename T>\nclass Flag : public FlagBase, public FlagBuilder<Flag<T>, T> {\n public:\n  using value_type = T;\n  using ParseValueCallback =\n      std::function<bool(std::string_view, std::string*)>;\n\n  explicit Flag(T* value) : value_(value) {}\n  explicit Flag(ParseValueCallback parse_value_callback)\n      : parse_value_callback_(parse_value_callback) {}\n  Flag(const Flag& other) = delete;\n  Flag& operator=(const Flag& other) = delete;\n\n private:\n  friend class FlagBuilder<Flag<T>, T>;\n  FRIEND_TEST(FlagTest, ParseValue);\n  FRIEND_TEST(TimeDeltaFlagTest, ParseValue);\n\n  void set_value(const T& value) {\n    is_set_ = true;\n    *value_ = value;\n  }\n\n  const T* value() const { return value_; }\n\n  bool NeedsValue() const override { return !std::is_same<T, bool>::value; }\n  bool ParseValue(std::string_view arg, std::string* reason) override;\n  bool ParseValueFromEnvironment(std::string* reason) override;\n\n  T* value_ = nullptr;\n  ParseValueCallback parse_value_callback_;\n  std::string env_name_;\n};\n\ntemplate <typename T>\nbool Flag<T>::ParseValue(std::string_view arg, std::string* reason) {\n  if (parse_value_callback_) {\n    bool ret = parse_value_callback_(arg, reason);\n    if (ret) {\n      is_set_ = true;\n    }\n    return ret;\n  }\n\n  if (FlagValueTraits<T>::ParseValue(arg, value_, reason)) {\n    is_set_ = true;\n    return true;\n  }\n  return false;\n}\n\ntemplate <typename T>\nbool Flag<T>::ParseValueFromEnvironment(std::string* reason) {\n  if (!env_name_.empty()) {\n    std::string_view value;\n    if (Environment::Get(env_name_, &value)) {\n      return ParseValue(value, reason);\n    }\n  }\n  return true;\n}\n\ntemplate <typename T, typename value_type>\nclass ChoicesFlagBuilder : public FlagBaseBuilder<T> {\n public:\n  T& set_default_value(const value_type& value) {\n    T* impl = static_cast<T*>(this);\n    DCHECK(impl->Contains(value));\n    *(impl->value_) = value;\n    return *impl;\n  }\n\n  T& set_env_name(std::string_view env_name) {\n    T* impl = static_cast<T*>(this);\n    impl->env_name_ = std::string(env_name);\n    return *impl;\n  }\n};\n\ntemplate <typename T>\nclass ChoicesFlag : public FlagBase,\n                    public ChoicesFlagBuilder<ChoicesFlag<T>, T> {\n public:\n  using value_type = T;\n\n  ChoicesFlag(T* value, const std::vector<T>& choices)\n      : value_(value), choices_(choices) {}\n  ChoicesFlag(T* value, std::vector<T>&& choices)\n      : value_(value), choices_(std::move(choices)) {}\n  ChoicesFlag(const ChoicesFlag& other) = delete;\n  ChoicesFlag& operator=(const ChoicesFlag& other) = delete;\n\n private:\n  friend class ChoicesFlagBuilder<ChoicesFlag<T>, T>;\n  FRIEND_TEST(FlagTest, ParseValue);\n\n  void set_value(const T& value) {\n    is_set_ = true;\n    *value_ = value;\n  }\n\n  const T* value() const { return value_; }\n\n  bool Contains(const T& value) { return base::Contains(choices_, value); }\n\n  bool NeedsValue() const override { return !std::is_same<T, bool>::value; }\n  bool ParseValue(std::string_view arg, std::string* reason) override;\n  bool ParseValueFromEnvironment(std::string* reason) override;\n\n  T* value_ = nullptr;\n  std::vector<T> choices_;\n  std::string env_name_;\n};\n\ntemplate <typename T>\nbool ChoicesFlag<T>::ParseValue(std::string_view arg, std::string* reason) {\n  T value;\n  if (FlagValueTraits<T>::ParseValue(arg, &value, reason)) {\n    if (Contains(value)) {\n      *value_ = std::move(value);\n      is_set_ = true;\n      return true;\n    } else {\n      *reason = absl::Substitute(\"$0 is not in choices\", arg);\n    }\n  }\n  return false;\n}\n\ntemplate <typename T>\nbool ChoicesFlag<T>::ParseValueFromEnvironment(std::string* reason) {\n  if (!env_name_.empty()) {\n    std::string_view value;\n    if (Environment::Get(env_name_, &value)) {\n      return ParseValue(value, reason);\n    }\n  }\n  return true;\n}\n\ntemplate <typename T, typename value_type>\nclass RangeFlagBuilder : public FlagBaseBuilder<T> {\n public:\n  T& set_default_value(const value_type& value) {\n    T* impl = static_cast<T*>(this);\n    DCHECK(impl->Contains(value));\n    *(impl->value_) = value;\n    return *impl;\n  }\n\n  T& set_env_name(std::string_view env_name) {\n    T* impl = static_cast<T*>(this);\n    impl->env_name_ = std::string(env_name);\n    return *impl;\n  }\n\n  T& set_greater_than_or_equal_to(bool greater_than_or_equal_to) {\n    T* impl = static_cast<T*>(this);\n    impl->greater_than_or_equal_to_ = greater_than_or_equal_to;\n    return *impl;\n  }\n\n  T& set_less_than_or_equal_to(bool less_than_or_equal_to) {\n    T* impl = static_cast<T*>(this);\n    impl->less_than_or_equal_to_ = less_than_or_equal_to;\n    return *impl;\n  }\n};\n\ntemplate <typename T>\nclass RangeFlag : public FlagBase, public RangeFlagBuilder<RangeFlag<T>, T> {\n public:\n  using value_type = T;\n\n  RangeFlag(T* value, const T& start, const T& end)\n      : value_(value), start_(start), end_(end) {\n    DCHECK_GE(end, start);\n  }\n  RangeFlag(const RangeFlag& other) = delete;\n  RangeFlag& operator=(const RangeFlag& other) = delete;\n\n private:\n  friend class RangeFlagBuilder<RangeFlag<T>, T>;\n  FRIEND_TEST(FlagTest, ParseValue);\n\n  void set_value(const T& value) {\n    is_set_ = true;\n    *value_ = value;\n  }\n\n  const T* value() const { return value_; }\n\n  bool Contains(const T& value) {\n    if (greater_than_or_equal_to_) {\n      if (value < start_) return false;\n    } else {\n      if (value <= start_) return false;\n    }\n    if (less_than_or_equal_to_) {\n      return value <= end_;\n    } else {\n      return value < end_;\n    }\n  }\n\n  bool NeedsValue() const override { return !std::is_same<T, bool>::value; }\n  bool ParseValue(std::string_view arg, std::string* reason) override;\n  bool ParseValueFromEnvironment(std::string* reason) override;\n\n  T* value_ = nullptr;\n  T start_;\n  T end_;\n  bool greater_than_or_equal_to_ = false;\n  bool less_than_or_equal_to_ = false;\n  std::string env_name_;\n};\n\ntemplate <typename T>\nbool RangeFlag<T>::ParseValue(std::string_view arg, std::string* reason) {\n  T value;\n  if (FlagValueTraits<T>::ParseValue(arg, &value, reason)) {\n    if (Contains(value)) {\n      *value_ = std::move(value);\n      is_set_ = true;\n      return true;\n    } else {\n      *reason = absl::Substitute(\"$0 is not in range\", arg);\n    }\n  }\n  return false;\n}\n\ntemplate <typename T>\nbool RangeFlag<T>::ParseValueFromEnvironment(std::string* reason) {\n  if (!env_name_.empty()) {\n    std::string_view value;\n    if (Environment::Get(env_name_, &value)) {\n      return ParseValue(value, reason);\n    }\n  }\n  return true;\n}\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_FLAG_FLAG_H_\n"
  },
  {
    "path": "tachyon/base/flag/flag_forward.h",
    "content": "// Copyright (c) 2020 The Console Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_FLAG_FLAG_FORWARD_H_\n#define TACHYON_BASE_FLAG_FLAG_FORWARD_H_\n\n#include <stdint.h>\n\n#include <string>\n\nnamespace tachyon::base {\n\ntemplate <typename T>\nclass Flag;\n\nusing BoolFlag = Flag<bool>;\nusing IntFlag = Flag<int>;\nusing Uint8Flag = Flag<uint8_t>;\nusing Int8Flag = Flag<int8_t>;\nusing Uint16Flag = Flag<uint16_t>;\nusing Int16Flag = Flag<int16_t>;\nusing Uint32Flag = Flag<uint32_t>;\nusing Int32Flag = Flag<int32_t>;\nusing Uint64Flag = Flag<uint64_t>;\nusing Int64Flag = Flag<int64_t>;\nusing FloatFlag = Flag<float>;\nusing DoubleFlag = Flag<double>;\nusing StringFlag = Flag<std::string>;\n\ntemplate <typename T>\nclass ChoicesFlag;\n\nusing StringChoicesFlag = ChoicesFlag<std::string>;\n\ntemplate <typename T>\nclass RangeFlag;\n\nusing Uint8RangeFlag = RangeFlag<uint8_t>;\nusing Int8RangeFlag = RangeFlag<int8_t>;\nusing Uint16RangeFlag = RangeFlag<uint16_t>;\nusing Int16RangeFlag = RangeFlag<int16_t>;\nusing Uint32RangeFlag = RangeFlag<uint32_t>;\nusing Int32RangeFlag = RangeFlag<int32_t>;\nusing Uint64RangeFlag = RangeFlag<uint64_t>;\nusing Int64RangeFlag = RangeFlag<int64_t>;\nusing FloatRangeFlag = RangeFlag<float>;\nusing DoubleRangeFlag = RangeFlag<double>;\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_FLAG_FLAG_FORWARD_H_\n"
  },
  {
    "path": "tachyon/base/flag/flag_parser.cc",
    "content": "#include \"tachyon/base/flag/flag_parser.h\"\n\n#include <iostream>\n#include <limits>\n#include <string>\n#include <tuple>\n#include <utility>\n\nnamespace tachyon::base {\n\nnamespace {\n\nconstexpr int kDefaultHelpStart = 20;\n\nbool ContainsOnlyAlpha(std::string_view text) {\n  return std::all_of(text.begin(), text.end(),\n                     [](char c) { return absl::ascii_isalpha(c); });\n}\n\nvoid AppendActiveSubParser(\n    std::ostream& ss, FlagParserBase& parser,\n    const std::vector<std::unique_ptr<FlagBase>>** flags) {\n  bool has_subparser = false;\n\n  if (parser.flags()->size() > 0) {\n    has_subparser = (*parser.flags())[0]->IsSubParser();\n  }\n\n  if (has_subparser) {\n    for (const auto& flag : *parser.flags()) {\n      if (flag->IsSubParser()) {\n        if (flag->is_set()) {\n          ss << \" \" << flag->name();\n          AppendActiveSubParser(ss, *flag->ToSubParser(), flags);\n          return;\n        }\n      } else {\n        break;\n      }\n    }\n  }\n  *flags = parser.flags();\n}\n\nstruct Costs {\n  explicit Costs(size_t size) : size(size) {\n    if (size > 256) {\n      costs = new size_t[size];\n    } else {\n      costs = costs_on_stack;\n    }\n    Init();\n  }\n\n  void Init() {\n    for (size_t i = 0; i < size; ++i) {\n      costs[i] = i;\n    }\n  }\n\n  ~Costs() {\n    if (size > 256) {\n      delete[] costs;\n    }\n  }\n\n  size_t& operator[](size_t i) { return costs[i]; }\n\n  size_t size;\n  size_t* costs = nullptr;\n  size_t costs_on_stack[256];\n};\n\n// This was taken and modified from\n// https://rosettacode.org/wiki/Levenshtein_distance#C.2B.2B\nsize_t GetLevenshteinDistance(std::string_view s1, std::string_view s2) {\n  size_t m = s1.size();\n  size_t n = s2.size();\n\n  if (m == 0) return n;\n  if (n == 0) return m;\n\n  Costs costs(n + 1);\n\n  size_t i = 0;\n  for (; i < m; ++i) {\n    costs[0] = i + 1;\n    size_t corner = i;\n    size_t j = 0;\n    char c = s1[i];\n    for (; j < n; ++j) {\n      size_t upper = costs[j + 1];\n      if (c == s2[j]) {\n        costs[j + 1] = corner;\n      } else {\n        size_t t = upper < corner ? upper : corner;\n        costs[j + 1] = (costs[j] < t ? costs[j] : t) + 1;\n      }\n\n      corner = upper;\n    }\n  }\n\n  return costs[n];\n}\n\n}  // namespace\n\nFlagParserBase::Context::Context(FlagParser* parser, int current_idx, int argc,\n                                 char** argv)\n    : parser(parser), current_idx(current_idx), argc(argc), argv(argv) {}\n\nFlagParserBase::Context::~Context() = default;\n\nstd::string_view FlagParserBase::Context::current() const {\n  return argv[current_idx];\n}\n\nbool FlagParserBase::Context::ConsumeEqualOrProceed(std::string_view* arg) {\n  if (ConsumePrefix(arg, \"=\")) return true;\n  if (!arg->empty()) return false;\n  Proceed();\n  *arg = current();\n  return true;\n}\n\nvoid FlagParserBase::Context::Proceed() { current_idx++; }\n\nbool FlagParserBase::Context::HasArg() const { return current_idx < argc; }\n\nvoid FlagParserBase::Context::FillUnknownArgs() const {\n  if (!unknown_argc) return;\n  size_t new_argc = 1;\n  for (char* unknown_arg : unknown_argv) {\n    argv[new_argc++] = unknown_arg;\n  }\n  *unknown_argc = new_argc;\n}\n\nFlagParserBase::FlagParserBase() = default;\n\nFlagParserBase::~FlagParserBase() = default;\n\nFlagBase& FlagParserBase::AddFlag(std::unique_ptr<FlagBase> flag_base) {\n  flags_.push_back(std::move(flag_base));\n  return *flags_.back().get();\n}\n\nSubParser& FlagParserBase::AddSubParser() {\n  std::unique_ptr<SubParser> sub_parser(new SubParser());\n  flags_.push_back(std::move(sub_parser));\n  return *flags_.back()->ToSubParser();\n}\n\nbool FlagParserBase::ValidateInternally(std::string* error) const {\n  bool is_positional = true;\n  bool has_subparser = false;\n  for (auto& flag : flags_) {\n    if (!(flag->is_positional() || flag->is_optional())) {\n      *error = \"Flag should be positional or optional.\";\n      return false;\n    }\n\n    if (flag->is_positional() && flag->is_optional()) {\n      *error = absl::Substitute(\n          \"\\\"$0\\\" is positional and optional, please choose either one of \"\n          \"them.\",\n          flag->name());\n      return false;\n    }\n\n    if (flag->IsSubParser()) {\n      if (flag->is_optional()) {\n        *error = absl::Substitute(\"Subparser \\\"$0\\\" should be positional.\",\n                                  flag->display_name());\n        return false;\n      }\n\n      has_subparser = true;\n      if (!flag->ToSubParser()->ValidateInternally(error)) {\n        return false;\n      }\n    } else {\n      if (flag->is_positional()) {\n        if (!is_positional) {\n          *error = absl::Substitute(\n              \"\\\"$0\\\" should be before any optional arguments.\",\n              flag->display_name());\n          return false;\n        }\n      } else {\n        is_positional = false;\n      }\n    }\n\n    if (!flag->NeedsValue()) {\n      if (flag->is_positional()) {\n        *error = absl::Substitute(\n            \"\\\"$0\\\" can't parse a value, how about considering using \"\n            \"set_short_name() or set_long_name()?\",\n            flag->name());\n        return false;\n      }\n    }\n  }\n\n  bool should_not_be_subparser = false;\n  if (has_subparser) {\n    for (auto& flag : flags_) {\n      if (flag->is_positional()) {\n        if (!flag->IsSubParser()) {\n          *error = absl::Substitute(\n              \"\\\"$0\\\" can't be positional if the parser has \"\n              \"subparser, how about considering using \"\n              \"set_short_name() or set_long_name()?\",\n              flag->name());\n          return false;\n        } else if (should_not_be_subparser) {\n          *error = \"SubParser should be at the very front.\";\n          return false;\n        }\n      } else {\n        should_not_be_subparser = true;\n      }\n    }\n  }\n\n  return true;\n}\n\nbool FlagParserBase::Parse(Context& ctx, std::string* error) {\n  int positional_parsed = 0;\n  int positional_argument = std::count_if(\n      flags_.begin(), flags_.end(), [](const std::unique_ptr<FlagBase>& flag) {\n        return flag->is_positional();\n      });\n  bool has_subparser = false;\n  if (positional_argument > 0) {\n    has_subparser = flags_[0]->IsSubParser();\n  }\n\n  while (ctx.HasArg()) {\n    std::string_view arg = ctx.current();\n    if (arg == \"--\") {\n      if (ctx.forward_argv) {\n        ctx.forward_argv->reserve(ctx.argc - ctx.current_idx - 1);\n        for (int i = ctx.current_idx + 1; i < ctx.argc; ++i) {\n          ctx.forward_argv->emplace_back(ctx.argv[i]);\n        }\n      }\n      return true;\n    }\n\n    if (arg == \"--help\" || arg == \"-h\") {\n      std::cerr << ctx.parser->help_message() << std::endl;\n      *error = absl::Substitute(\"Got \\\"$0\\\".\", arg);\n      return false;\n    }\n\n    FlagBase* target_flag = nullptr;\n    if (has_subparser) {\n      for (int i = 0; i < positional_argument; ++i) {\n        if (flags_[i]->name() == arg) {\n          target_flag = flags_[i].get();\n          break;\n        }\n      }\n    }\n\n    if (!has_subparser && positional_parsed < positional_argument) {\n      int positional_idx = 0;\n      for (auto& flag : flags_) {\n        if (flag->is_optional()) {\n          continue;\n        } else {\n          if (positional_idx == positional_parsed) {\n            target_flag = flag.get();\n            break;\n          }\n\n          positional_idx++;\n        }\n      }\n    } else {\n      std::string_view new_arg = arg;\n      if (ConsumePrefix(&new_arg, \"-\")) {\n        if ((ContainsOnlyAlpha(new_arg))) {\n          // Flags can be passed like '-ab' if '-a' and '-b' are valid short\n          // names and don't need values. For this special case, we have to\n          // treat differently.\n          bool all_parsed =\n              std::all_of(new_arg.begin(), new_arg.end(), [this](char c) {\n                for (auto& flag : flags_) {\n                  if (flag->is_positional()) continue;\n                  if (flag->NeedsValue()) continue;\n                  if (flag->short_name().length() == 2 &&\n                      c == flag->short_name()[1]) {\n                    bool parsed = flag->ParseValue(\"\", nullptr);\n                    std::ignore = parsed;\n                    DCHECK(parsed);\n                    return true;\n                  }\n                }\n\n                return false;\n              });\n          if (all_parsed) {\n            ctx.Proceed();\n            continue;\n          }\n        }\n      }\n\n      for (auto& flag : flags_) {\n        if (flag->is_positional()) continue;\n\n        if (!flag->ConsumeNamePrefix(*this, &arg)) continue;\n        target_flag = flag.get();\n        break;\n      }\n    }\n\n    bool parsed = false;\n    std::string reason;\n    if (target_flag) {\n      if (target_flag->IsSubParser()) {\n        target_flag->is_set_ = true;\n        SubParser* sub_parser = target_flag->ToSubParser();\n        if (sub_parser->is_set_) *sub_parser->is_set_ = true;\n        ctx.Proceed();\n        return target_flag->ToSubParser()->Parse(ctx, error);\n      } else if (target_flag->is_positional()) {\n        parsed = target_flag->ParseValue(arg, &reason);\n        positional_parsed++;\n      } else {\n        if (target_flag->NeedsValue() && !ctx.ConsumeEqualOrProceed(&arg)) {\n          *error = absl::Substitute(\n              \"\\\"$0\\\" is failed to parse: (reason: empty value).\",\n              target_flag->display_name());\n          return false;\n        }\n        parsed = target_flag->ParseValue(arg, &reason);\n      }\n    } else {\n      if (ctx.unknown_argc) {\n        ctx.unknown_argv.push_back(ctx.argv[ctx.current_idx]);\n        ctx.Proceed();\n        if (ctx.HasArg() && !StartsWith(ctx.current(), \"-\")) {\n          ctx.unknown_argv.push_back(ctx.argv[ctx.current_idx]);\n          ctx.Proceed();\n        }\n        continue;\n      }\n    }\n\n    if (!parsed) {\n      if (target_flag) {\n        if (reason.empty()) {\n          *error =\n              absl::Substitute(\"\\\"$0\\\" is failed to parse: (reason: unknown).\",\n                               target_flag->display_name());\n        } else {\n          *error = absl::Substitute(\"\\\"$0\\\" is failed to parse: (reason: $1).\",\n                                    target_flag->display_name(), reason);\n        }\n      } else {\n        std::string_view candidate_arg;\n        bool found = FindTheMostSimilarFlag(arg, &candidate_arg);\n        if (found) {\n          *error = absl::Substitute(\n              \"met unknown argument: \\\"$0\\\", maybe you mean \\\"$1\\\"?\", arg,\n              candidate_arg);\n        } else {\n          *error = absl::Substitute(\"met unknown argument: \\\"$0\\\".\", arg);\n        }\n      }\n      return false;\n    }\n    ctx.Proceed();\n  }\n\n  if (!has_subparser && positional_parsed < positional_argument) {\n    for (auto& flag : flags_) {\n      if (flag->is_optional()) continue;\n      if (!flag->is_set()) {\n        *error = absl::Substitute(\"\\\"$0\\\" is positional, but not set.\",\n                                  flag->name());\n        return false;\n      }\n    }\n  }\n\n  for (auto& flag : flags_) {\n    if (!flag->is_set()) {\n      if (flag->is_required()) {\n        *error = absl::Substitute(\"\\\"$0\\\" is required, but not set.\",\n                                  flag->display_name());\n        return false;\n      } else {\n        if (!flag->ParseValueFromEnvironment(error)) return false;\n      }\n    }\n  }\n\n  return true;\n}\n\nbool FlagParserBase::FindTheMostSimilarFlag(std::string_view input,\n                                            std::string_view* output) {\n  size_t threshold = (input.length() + 1) / 2;\n  size_t min = std::numeric_limits<size_t>::max();\n\n  for (auto& flag : flags_) {\n    if (!flag->short_name().empty()) {\n      size_t dist = GetLevenshteinDistance(input, flag->short_name());\n      if (dist <= threshold && dist < min) {\n        *output = flag->short_name();\n        min = dist;\n      }\n    }\n\n    if (!flag->long_name().empty()) {\n      size_t dist = GetLevenshteinDistance(input, flag->long_name());\n      if (dist <= threshold && dist < min) {\n        *output = flag->long_name();\n        min = dist;\n      }\n    }\n\n    if (flag->IsSubParser()) {\n      size_t dist = GetLevenshteinDistance(input, flag->name());\n      if (dist <= threshold && dist < min) {\n        *output = flag->name();\n        min = dist;\n      }\n    }\n  }\n\n  return min < std::numeric_limits<size_t>::max();\n}\n\nFlagParser::FlagParser() = default;\n\nFlagParser::~FlagParser() = default;\n\nbool FlagParser::Parse(int argc, char** argv, std::string* error) {\n  return ParseWithForward(argc, argv, nullptr, error);\n}\n\nbool FlagParser::ParseKnown(int* argc, char** argv, std::string* error) {\n  return ParseKnownWithForward(argc, argv, nullptr, error);\n}\n\nbool FlagParser::ParseWithForward(int argc, char** argv,\n                                  std::vector<std::string>* forward,\n                                  std::string* error) {\n  if (!PreParse(argc, argv, error)) return false;\n\n  Context ctx(this, 1, argc, argv);\n  ctx.forward_argv = forward;\n  return FlagParserBase::Parse(ctx, error) && Validate(error);\n}\n\nbool FlagParser::ParseKnownWithForward(int* argc, char** argv,\n                                       std::vector<std::string>* forward,\n                                       std::string* error) {\n  if (!PreParse(*argc, argv, error)) return false;\n\n  Context ctx(this, 1, *argc, argv);\n  ctx.unknown_argc = argc;\n  ctx.forward_argv = forward;\n  if (!FlagParserBase::Parse(ctx, error) && Validate(error)) return false;\n  ctx.FillUnknownArgs();\n  return true;\n}\n\nbool FlagParser::PreParse(int argc, char** argv, std::string* error) {\n  if (!ValidateInternally(error)) return false;\n\n  if (program_path_.empty()) {\n    DCHECK_GT(strlen(argv[0]), 0U);\n    program_path_ = FilePath(argv[0]);\n  }\n\n  return true;\n}\n\nstd::string FlagParser::help_message() {\n  std::stringstream ss;\n  ss << \"Usage: \" << std::endl << std::endl;\n  ss << program_path_;\n  const std::vector<std::unique_ptr<FlagBase>>* flags = nullptr;\n  AppendActiveSubParser(ss, *this, &flags);\n\n  // |flags| might be different from |flags_|.\n  bool has_optional_flag = std::any_of(\n      flags->begin(), flags->end(), [](const std::unique_ptr<FlagBase>& flag) {\n        return flag->is_optional();\n      });\n  bool has_sub_parser = false;\n  if (flags->size() > 0) {\n    has_sub_parser = (*flags)[0]->IsSubParser();\n  }\n  if (has_sub_parser) {\n    if (has_optional_flag) {\n      ss << \" [OPTIONS]\";\n    }\n    ss << \" COMMAND\";\n  }\n\n  if (has_sub_parser) {\n    ss << std::endl << std::endl;\n    ss << \"Commands:\" << std::endl << std::endl;\n    for (auto& flag : *flags) {\n      if (flag->IsSubParser()) {\n        ss << flag->display_help(kDefaultHelpStart) << std::endl;\n      }\n    }\n  } else {\n    bool has_positional_flag =\n        std::any_of(flags->begin(), flags->end(),\n                    [](const std::unique_ptr<FlagBase>& flag) {\n                      return flag->is_positional();\n                    });\n    if (has_positional_flag) {\n      for (auto& flag : *flags) {\n        if (flag->is_positional()) {\n          ss << \" \" << flag->name();\n        }\n      }\n    }\n    if (has_optional_flag) {\n      ss << \" [OPTIONS]\";\n    }\n    ss << std::endl;\n\n    if (has_positional_flag) {\n      ss << std::endl << \"Positional arguments:\" << std::endl << std::endl;\n      for (auto& flag : *flags) {\n        if (flag->is_positional()) {\n          ss << flag->display_help(kDefaultHelpStart) << std::endl;\n        }\n      }\n    }\n  }\n\n  if (has_optional_flag) {\n    ss << std::endl << \"Optional arguments:\" << std::endl << std::endl;\n    for (auto& flag : *flags) {\n      if (flag->is_optional()) {\n        ss << flag->display_help(kDefaultHelpStart) << std::endl;\n      }\n    }\n  }\n\n  return ss.str();\n}\n\nSubParser::SubParser() = default;\n\nSubParser::~SubParser() = default;\n\nbool SubParser::IsSubParser() const { return true; }\n\nbool SubParser::NeedsValue() const { return true; }\n\nbool SubParser::ParseValue(std::string_view arg, std::string* reason) {\n  NOTREACHED();\n  return false;\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/flag/flag_parser.h",
    "content": "// Copyright (c) 2020 The Console Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_FLAG_FLAG_PARSER_H_\n#define TACHYON_BASE_FLAG_FLAG_PARSER_H_\n\n#include <memory>\n#include <string>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/files/file_path.h\"\n#include \"tachyon/base/flag/flag.h\"\n#include \"tachyon/export.h\"\n\nnamespace tachyon::base {\n\nclass TACHYON_EXPORT FlagParserBase {\n public:\n  FlagParserBase();\n  FlagParserBase(const FlagParserBase& other) = delete;\n  FlagParserBase& operator=(const FlagParserBase& other) = delete;\n  virtual ~FlagParserBase();\n\n  const std::vector<std::unique_ptr<FlagBase>>* flags() const {\n    return &flags_;\n  }\n\n  template <typename T, typename value_type = typename Flag<T>::value_type>\n  T& AddFlag(value_type* value) {\n    std::unique_ptr<FlagBase> flag(new T(value));\n    flags_.push_back(std::move(flag));\n    return *reinterpret_cast<T*>(flags_.back().get());\n  }\n\n  template <typename T,\n            typename ParseValueCallback = typename Flag<T>::ParseValueCallback>\n  T& AddFlag(ParseValueCallback parse_value_callback) {\n    std::unique_ptr<FlagBase> flag(new T(parse_value_callback));\n    flags_.push_back(std::move(flag));\n    return *reinterpret_cast<T*>(flags_.back().get());\n  }\n\n  template <typename T,\n            typename value_type = typename ChoicesFlag<T>::value_type>\n  T& AddFlag(value_type* value, const std::vector<value_type>& choices) {\n    std::unique_ptr<FlagBase> flag(new T(value, choices));\n    flags_.push_back(std::move(flag));\n    return *reinterpret_cast<T*>(flags_.back().get());\n  }\n\n  template <typename T,\n            typename value_type = typename ChoicesFlag<T>::value_type>\n  T& AddFlag(value_type* value, std::vector<value_type>&& choices) {\n    std::unique_ptr<FlagBase> flag(new T(value, std::move(choices)));\n    flags_.push_back(std::move(flag));\n    return *reinterpret_cast<T*>(flags_.back().get());\n  }\n\n  template <typename T, typename value_type = typename RangeFlag<T>::value_type>\n  T& AddFlag(value_type* value, const value_type& start,\n             const value_type& end) {\n    std::unique_ptr<FlagBase> flag(new T(value, start, end));\n    flags_.push_back(std::move(flag));\n    return *reinterpret_cast<T*>(flags_.back().get());\n  }\n\n  FlagBase& AddFlag(std::unique_ptr<FlagBase> flag_base);\n\n  SubParser& AddSubParser();\n\n protected:\n  FRIEND_TEST(FlagParserTest, ValidateInternally);\n\n  struct Context {\n    FlagParser* parser;\n    int current_idx;\n    int argc;\n    char** argv;\n    int* unknown_argc = nullptr;\n    std::vector<char*> unknown_argv;\n    std::vector<std::string>* forward_argv = nullptr;\n\n    Context(FlagParser* parser, int current_idx, int argc, char** argv);\n    ~Context();\n\n    std::string_view current() const;\n    bool ConsumeEqualOrProceed(std::string_view* arg);\n    void Proceed();\n    bool HasArg() const;\n    void FillUnknownArgs() const;\n  };\n\n  bool Parse(Context& ctx, std::string* error);\n\n  bool ValidateInternally(std::string* error) const;\n\n  // Internally it measures Levenshtein distance among arguments.\n  bool FindTheMostSimilarFlag(std::string_view input, std::string_view* output);\n\n protected:\n  std::vector<std::unique_ptr<FlagBase>> flags_;\n};\n\nclass TACHYON_EXPORT FlagParser : public FlagParserBase {\n public:\n  FlagParser();\n  FlagParser(const FlagParser& other) = delete;\n  FlagParser& operator=(const FlagParser& other) = delete;\n  ~FlagParser();\n\n  void set_program_path(const FilePath& program_path) {\n    program_path_ = program_path;\n  }\n  const FilePath& program_path() const { return program_path_; }\n\n  // Sames as ParseWithForward(argc, argv, nullptr, error).\n  bool Parse(int argc, char** argv, std::string* error);\n\n  // Sames as ParseKnownWithForward(argc, argv, nullptr, error).\n  // It only parses known arguments. After parsing, |argc| and |argv| are\n  // updated to match the unknown arguments.\n  bool ParseKnown(int* argc, char** argv, std::string* error);\n\n  bool ParseWithForward(int argc, char** argv,\n                        std::vector<std::string>* forward, std::string* error);\n\n  bool ParseKnownWithForward(int* argc, char** argv,\n                             std::vector<std::string>* forward,\n                             std::string* error);\n\n  // It is marked virtual so that users can make custom help messages.\n  virtual std::string help_message();\n\n  virtual bool Validate(std::string* error) { return true; }\n\n private:\n  bool PreParse(int argc, char** argv, std::string* error);\n\n  FilePath program_path_;\n};\n\nclass TACHYON_EXPORT SubParser : public FlagBase,\n                                 public FlagParserBase,\n                                 public FlagBaseBuilder<SubParser> {\n public:\n  SubParser();\n  ~SubParser();\n  SubParser(const SubParser& other) = delete;\n  SubParser& operator=(const SubParser& other) = delete;\n\n  // FlagBase methods\n  bool IsSubParser() const override;\n  bool NeedsValue() const override;\n  bool ParseValue(std::string_view arg, std::string* reason) override;\n\n  SubParser& set_is_set(bool* is_set) {\n    is_set_ = is_set;\n    return *this;\n  }\n\n private:\n  friend class FlagParserBase;\n\n  bool* is_set_ = nullptr;\n};\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_FLAG_FLAG_PARSER_H_\n"
  },
  {
    "path": "tachyon/base/flag/flag_parser_unittest.cc",
    "content": "// Copyright (c) 2020 The Console Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/flag/flag_parser.h\"\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/test/scoped_environment.h\"\n\nnamespace tachyon::base {\n\n#define EXPECT_PARSE_TRUE(...)        \\\n  const char* argv[] = {__VA_ARGS__}; \\\n  std::string error;                  \\\n  EXPECT_TRUE(parser.Parse(std::size(argv), const_cast<char**>(argv), &error))\n\n#define EXPECT_PARSE_FALSE(...)       \\\n  const char* argv[] = {__VA_ARGS__}; \\\n  std::string error;                  \\\n  EXPECT_FALSE(parser.Parse(std::size(argv), const_cast<char**>(argv), &error))\n\nTEST(FlagParserTest, ValidateInternally) {\n  {\n    FlagParser parser;\n    uint16_t value;\n    parser.AddFlag<Uint16Flag>(&value);\n    std::string error;\n    EXPECT_FALSE(parser.ValidateInternally(&error));\n    EXPECT_EQ(error, \"Flag should be positional or optional.\");\n  }\n  {\n    FlagParser parser;\n    uint16_t value;\n    parser.AddFlag<Uint16Flag>(&value).set_name(\"value\").set_short_name(\"-v\");\n    std::string error;\n    EXPECT_FALSE(parser.ValidateInternally(&error));\n    EXPECT_EQ(error,\n              \"\\\"value\\\" is positional and optional, please choose either one \"\n              \"of them.\");\n  }\n  {\n    FlagParser parser;\n    parser.AddSubParser().set_short_name(\"-a\");\n    std::string error;\n    EXPECT_FALSE(parser.ValidateInternally(&error));\n    EXPECT_EQ(error, \"Subparser \\\"-a\\\" should be positional.\");\n  }\n  {\n    FlagParser parser;\n    uint16_t value;\n    parser.AddFlag<Uint16Flag>(&value).set_short_name(\"-v\");\n    parser.AddFlag<Uint16Flag>(&value).set_name(\"value\");\n    std::string error;\n    EXPECT_FALSE(parser.ValidateInternally(&error));\n    EXPECT_EQ(error, \"\\\"value\\\" should be before any optional arguments.\");\n  }\n  {\n    FlagParser parser;\n    bool value;\n    parser.AddFlag<BoolFlag>(&value).set_name(\"value\");\n    std::string error;\n    EXPECT_FALSE(parser.ValidateInternally(&error));\n    EXPECT_EQ(error,\n              \"\\\"value\\\" can't parse a value, how about considering using \"\n              \"set_short_name() or set_long_name()?\");\n  }\n  {\n    FlagParser parser;\n    uint16_t value;\n    parser.AddSubParser().set_name(\"a\");\n    parser.AddFlag<Uint16Flag>(&value).set_name(\"value\");\n    std::string error;\n    EXPECT_FALSE(parser.ValidateInternally(&error));\n    EXPECT_EQ(error,\n              \"\\\"value\\\" can't be positional if the parser has \"\n              \"subparser, how about considering using \"\n              \"set_short_name() or set_long_name()?\");\n  }\n  {\n    FlagParser parser;\n    uint16_t value;\n    parser.AddFlag<Uint16Flag>(&value).set_short_name(\"-a\");\n    SubParser& sub_parser = parser.AddSubParser();\n    sub_parser.set_name(\"test\");\n    sub_parser.AddFlag<Uint16Flag>(&value).set_name(\"name\");\n    std::string error;\n    EXPECT_FALSE(parser.ValidateInternally(&error));\n    EXPECT_EQ(error, \"SubParser should be at the very front.\");\n  }\n}\n\nTEST(FlagParserTest, UndefinedArgument) {\n  FlagParser parser;\n  uint16_t value;\n  parser.AddFlag<Uint16Flag>(&value).set_long_name(\"--value\");\n  {\n    EXPECT_PARSE_FALSE(\"program\", \"--v\", \"16\");\n    EXPECT_EQ(error, \"met unknown argument: \\\"--v\\\".\");\n  }\n  {\n    EXPECT_PARSE_FALSE(\"program\", \"--val\", \"16\");\n    EXPECT_EQ(error,\n              \"met unknown argument: \\\"--val\\\", maybe you mean \\\"--value\\\"?\");\n  }\n}\n\nTEST(FlagParserTest, DefaultValue) {\n  FlagParser parser;\n  uint16_t value;\n  parser.AddFlag<Uint16Flag>(&value)\n      .set_default_value(static_cast<uint16_t>(12))\n      .set_short_name(\"-v\");\n  {\n    EXPECT_PARSE_TRUE(\"program\");\n    EXPECT_EQ(value, 12);\n  }\n}\n\nTEST(FlagParserTest, PositionalArgumtens) {\n  FlagParser parser;\n  uint16_t value;\n  uint16_t value2;\n  parser.AddFlag<Uint16Flag>(&value).set_name(\"flag\");\n  parser.AddFlag<Uint16Flag>(&value2).set_name(\"flag2\");\n  { EXPECT_PARSE_FALSE(\"program\", \"12\"); }\n  { EXPECT_PARSE_TRUE(\"program\", \"12\", \"34\"); }\n  EXPECT_EQ(value, 12);\n  EXPECT_EQ(value2, 34);\n}\n\nTEST(FlagParserTest, RequiredOptionalArguments) {\n  FlagParser parser;\n  uint16_t value;\n  uint16_t value2;\n  parser.AddFlag<Uint16Flag>(&value).set_short_name(\"-a\");\n  parser.AddFlag<Uint16Flag>(&value2).set_short_name(\"-b\").set_required();\n  {\n    EXPECT_PARSE_FALSE(\"program\", \"-a\", \"12\");\n    EXPECT_EQ(error, \"\\\"-b\\\" is required, but not set.\");\n  }\n  { EXPECT_PARSE_TRUE(\"program\", \"-b\", \"34\"); }\n  EXPECT_EQ(value2, 34);\n  { EXPECT_PARSE_TRUE(\"program\", \"-a\", \"56\", \"-b\", \"78\"); }\n  EXPECT_EQ(value, 56);\n  EXPECT_EQ(value2, 78);\n}\n\nTEST(FlagParserTest, ConcatenatedOptionalFlags) {\n  FlagParser parser;\n  bool value = false;\n  bool value2 = false;\n  int32_t value3;\n  parser.AddFlag<BoolFlag>(&value).set_short_name(\"-a\");\n  parser.AddFlag<BoolFlag>(&value2).set_short_name(\"-b\");\n  parser.AddFlag<Int32Flag>(&value3).set_short_name(\"-c\");\n  { EXPECT_PARSE_TRUE(\"program\", \"-ab\"); }\n  EXPECT_TRUE(value);\n  EXPECT_TRUE(value2);\n  {\n    EXPECT_PARSE_FALSE(\"program\", \"-ac\");\n    EXPECT_EQ(error, \"met unknown argument: \\\"-ac\\\", maybe you mean \\\"-a\\\"?\");\n  }\n}\n\nTEST(FlagParserTest, VectorFlag) {\n  FlagParser parser;\n  std::vector<int> numbers;\n  parser.AddFlag<Flag<std::vector<int>>>(&numbers).set_short_name(\"-a\");\n  {\n    EXPECT_PARSE_TRUE(\"program\", \"-a\", \"1\", \"-a\", \"2\", \"-a\", \"3\");\n    EXPECT_THAT(numbers, testing::ElementsAre(1, 2, 3));\n  }\n}\n\nTEST(FlagParserTest, CustomParseValueCallback) {\n  FlagParser parser;\n  std::string value;\n  parser\n      .AddFlag<StringFlag>([&value](std::string_view arg, std::string* reason) {\n        if (arg == \"cat\" || arg == \"dog\") {\n          value = std::string(arg);\n          return true;\n        }\n        *reason = absl::Substitute(\"$0 is not either cat or dog\", arg);\n        return false;\n      })\n      .set_short_name(\"-a\");\n  {\n    EXPECT_PARSE_FALSE(\"program\", \"-a\", \"pig\");\n    EXPECT_EQ(\n        error,\n        \"\\\"-a\\\" is failed to parse: (reason: pig is not either cat or dog).\");\n  }\n  {\n    EXPECT_PARSE_TRUE(\"program\", \"-a\", \"cat\");\n    EXPECT_EQ(value, \"cat\");\n  }\n}\n\nTEST(FlagParserTest, ParseValueFromEnvironment) {\n  FlagParser parser;\n  std::string value;\n  parser.AddFlag<StringFlag>(&value).set_env_name(\"VALUE\").set_short_name(\"-v\");\n  {\n    ScopedEnvironment env(\"VALUE\", \"value\");\n    EXPECT_PARSE_TRUE(\"program\");\n    EXPECT_EQ(value, \"value\");\n  }\n}\n\nTEST(FlagParserTest, ParseKnown) {\n  FlagParser parser;\n  int value;\n  parser.AddFlag<IntFlag>(&value).set_short_name(\"-a\");\n  bool value2;\n  parser.AddFlag<BoolFlag>(&value2).set_short_name(\"-b\");\n  {\n    value = 0;\n    value2 = false;\n    const char* argv[] = {\"program\", \"-a\", \"1\", \"--unknown\", \"-b\"};\n    int argc = std::size(argv);\n    std::string error;\n    EXPECT_TRUE(parser.ParseKnown(&argc, const_cast<char**>(argv), &error));\n    EXPECT_EQ(value, 1);\n    EXPECT_EQ(value2, true);\n    EXPECT_EQ(argc, 2);\n    EXPECT_STREQ(argv[0], \"program\");\n    EXPECT_STREQ(argv[1], \"--unknown\");\n  }\n\n  {\n    value = 0;\n    value2 = false;\n    const char* argv[] = {\"program\", \"-a\", \"1\", \"-b\", \"--unknown\", \"2\"};\n    int argc = std::size(argv);\n    std::string error;\n    EXPECT_TRUE(parser.ParseKnown(&argc, const_cast<char**>(argv), &error));\n    EXPECT_EQ(value, 1);\n    EXPECT_EQ(value2, true);\n    EXPECT_EQ(argc, 3);\n    EXPECT_STREQ(argv[0], \"program\");\n    EXPECT_STREQ(argv[1], \"--unknown\");\n    EXPECT_STREQ(argv[2], \"2\");\n  }\n}\n\nTEST(FlagParserTest, ParseWithForward) {\n  FlagParser parser;\n  int value;\n  parser.AddFlag<IntFlag>(&value).set_short_name(\"-a\");\n  bool value2;\n  parser.AddFlag<BoolFlag>(&value2).set_short_name(\"-b\");\n  {\n    value = 0;\n    value2 = false;\n    const char* argv[] = {\"program\", \"-a\", \"1\", \"--\", \"-b\"};\n    int argc = std::size(argv);\n    std::vector<std::string> forward;\n    std::string error;\n    EXPECT_TRUE(parser.ParseWithForward(argc, const_cast<char**>(argv),\n                                        &forward, &error));\n    EXPECT_EQ(value, 1);\n    EXPECT_EQ(value2, false);\n    EXPECT_THAT(forward, testing::ElementsAre(\"-b\"));\n  }\n}\n\nTEST(FlagParserTest, SubParserTest) {\n  FlagParser parser;\n  int a;\n  int b;\n  bool verbose = false;\n  SubParser& add_parser = parser.AddSubParser().set_name(\"add\");\n  add_parser.AddFlag<Int32Flag>(&a).set_name(\"a\");\n  add_parser.AddFlag<Int32Flag>(&b).set_name(\"b\");\n  SubParser& sub_parser = parser.AddSubParser().set_name(\"sub\");\n  sub_parser.AddFlag<Int32Flag>(&a).set_name(\"a\");\n  sub_parser.AddFlag<Int32Flag>(&b).set_name(\"b\");\n  parser.AddFlag<BoolFlag>(&verbose).set_short_name(\"-v\");\n  {\n    EXPECT_PARSE_TRUE(\"program\", \"add\", \"1\", \"2\");\n    EXPECT_TRUE(add_parser.is_set());\n    EXPECT_FALSE(sub_parser.is_set());\n    EXPECT_EQ(1, a);\n    EXPECT_EQ(2, b);\n    EXPECT_FALSE(verbose);\n  }\n\n  add_parser.reset();\n  {\n    EXPECT_PARSE_TRUE(\"program\", \"-v\", \"add\", \"1\", \"2\");\n    EXPECT_TRUE(add_parser.is_set());\n    EXPECT_FALSE(sub_parser.is_set());\n    EXPECT_EQ(1, a);\n    EXPECT_EQ(2, b);\n    EXPECT_TRUE(verbose);\n  }\n}\n\n#undef EXPECT_PARSE_TRUE\n#undef EXPECT_PARSE_FALSE\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/flag/flag_unittest.cc",
    "content": "// Copyright (c) 2020 The Console Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/flag/flag.h\"\n\n#include \"gtest/gtest.h\"\n\nnamespace tachyon::base {\n\nTEST(FlagTest, ShortName) {\n  bool value;\n  BoolFlag flag(&value);\n  flag.set_short_name(\"-a\");\n  EXPECT_EQ(flag.short_name(), \"-a\");\n  flag.set_short_name(\"--a\");\n  EXPECT_EQ(flag.short_name(), \"-a\");\n  flag.set_short_name(\"a\");\n  EXPECT_EQ(flag.short_name(), \"-a\");\n  flag.set_short_name(\"-ab\");\n  EXPECT_EQ(flag.short_name(), \"-a\");\n  flag.set_short_name(\"-1\");\n  EXPECT_EQ(flag.short_name(), \"-a\");\n  flag.set_short_name(\"-_\");\n  EXPECT_EQ(flag.short_name(), \"-a\");\n  flag.set_short_name(\"-b\");\n  EXPECT_EQ(flag.short_name(), \"-b\");\n}\n\nTEST(FlagTest, LongName) {\n  bool value;\n  BoolFlag flag(&value);\n  flag.set_long_name(\"--a\");\n  EXPECT_EQ(flag.long_name(), \"--a\");\n  flag.set_long_name(\"-a\");\n  EXPECT_EQ(flag.long_name(), \"--a\");\n  flag.set_long_name(\"a\");\n  EXPECT_EQ(flag.long_name(), \"--a\");\n  flag.set_long_name(\"--1\");\n  EXPECT_EQ(flag.long_name(), \"--a\");\n  flag.set_long_name(\"--_\");\n  EXPECT_EQ(flag.long_name(), \"--a\");\n  flag.set_long_name(\"--a_\");\n  EXPECT_EQ(flag.long_name(), \"--a_\");\n}\n\nTEST(FlagTest, Name) {\n  bool value;\n  BoolFlag flag(&value);\n  flag.set_name(\"a\");\n  EXPECT_EQ(flag.name(), \"a\");\n  flag.set_name(\"-a\");\n  EXPECT_EQ(flag.name(), \"a\");\n  flag.set_name(\"--a\");\n  EXPECT_EQ(flag.name(), \"a\");\n  flag.set_name(\"1\");\n  EXPECT_EQ(flag.name(), \"a\");\n  flag.set_name(\"_\");\n  EXPECT_EQ(flag.name(), \"a\");\n  flag.set_name(\"a_\");\n  EXPECT_EQ(flag.name(), \"a_\");\n}\n\nTEST(FlagTest, ParseValue) {\n  bool bool_value = false;\n  std::string reason;\n  BoolFlag bool_flag(&bool_value);\n  EXPECT_TRUE(bool_flag.ParseValue(\"\", &reason));\n  EXPECT_EQ(reason, \"\");\n  EXPECT_TRUE(bool_value);\n\n  reason.clear();\n  int16_t int16_value;\n  Int16Flag int16_flag(&int16_value);\n  EXPECT_TRUE(int16_flag.ParseValue(\"123\", &reason));\n  EXPECT_EQ(reason, \"\");\n  EXPECT_EQ(int16_value, 123);\n  EXPECT_FALSE(int16_flag.ParseValue(\"a\", &reason));\n  EXPECT_EQ(reason, \"failed to convert int (\\\"a\\\")\");\n  EXPECT_FALSE(int16_flag.ParseValue(\"40000\", &reason));\n  EXPECT_EQ(reason, \"40000 is out of its range\");\n  EXPECT_EQ(int16_value, 123);\n\n  reason.clear();\n  std::string string_value;\n  StringFlag string_flag(&string_value);\n  EXPECT_TRUE(string_flag.ParseValue(\"abc\", &reason));\n  EXPECT_EQ(reason, \"\");\n  EXPECT_EQ(string_value, \"abc\");\n  EXPECT_FALSE(string_flag.ParseValue(\"\", &reason));\n  EXPECT_EQ(reason, \"input is empty\");\n  EXPECT_EQ(string_value, \"abc\");\n\n  reason.clear();\n  std::string choice_value;\n  StringChoicesFlag choices_flag(\n      &choice_value, std::vector<std::string>{\"cat\", \"dog\", \"duck\"});\n  EXPECT_TRUE(choices_flag.ParseValue(\"cat\", &reason));\n  EXPECT_EQ(reason, \"\");\n  EXPECT_TRUE(choices_flag.ParseValue(\"dog\", &reason));\n  EXPECT_EQ(reason, \"\");\n  EXPECT_TRUE(choices_flag.ParseValue(\"duck\", &reason));\n  EXPECT_EQ(reason, \"\");\n  EXPECT_FALSE(choices_flag.ParseValue(\"bird\", &reason));\n  EXPECT_EQ(reason, \"bird is not in choices\");\n\n  reason.clear();\n  int32_t int32_value;\n  Int32RangeFlag int32_range_flag(&int32_value, 1, 5);\n  int32_range_flag.set_less_than_or_equal_to(true);\n  EXPECT_TRUE(int32_range_flag.ParseValue(\"2\", &reason));\n  EXPECT_EQ(reason, \"\");\n  EXPECT_TRUE(int32_range_flag.ParseValue(\"5\", &reason));\n  EXPECT_EQ(reason, \"\");\n  EXPECT_FALSE(int32_range_flag.ParseValue(\"1\", &reason));\n  EXPECT_EQ(reason, \"1 is not in range\");\n  EXPECT_FALSE(int32_range_flag.ParseValue(\"6\", &reason));\n  EXPECT_EQ(reason, \"6 is not in range\");\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/flag/flag_value_traits.h",
    "content": "// Copyright (c) 2020 The Console Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_FLAG_FLAG_VALUE_TRAITS_H_\n#define TACHYON_BASE_FLAG_FLAG_VALUE_TRAITS_H_\n\n#include <stdint.h>\n\n#include <limits>\n#include <numeric>\n#include <set>\n#include <string>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/base/strings/string_number_conversions.h\"\n\nnamespace tachyon::base {\n\ntemplate <typename T, typename SFINAE = void>\nclass FlagValueTraits;\n\ntemplate <typename T>\nclass FlagValueTraits<\n    T, std::enable_if_t<std::is_integral<T>::value &&\n                        std::is_signed<T>::value && (sizeof(T) <= 32)>> {\n public:\n  static bool ParseValue(std::string_view input, T* value,\n                         std::string* reason) {\n    int value_tmp;\n    if (!StringToInt(input, &value_tmp)) {\n      *reason = absl::Substitute(\"failed to convert int (\\\"$0\\\")\", input);\n      return false;\n    }\n    if (value_tmp <= std::numeric_limits<T>::max() &&\n        value_tmp >= std::numeric_limits<T>::min()) {\n      *value = static_cast<T>(value_tmp);\n      return true;\n    }\n    *reason = absl::Substitute(\"$0 is out of its range\", input);\n    return false;\n  }\n};\n\ntemplate <typename T>\nclass FlagValueTraits<\n    T,\n    std::enable_if_t<std::is_integral<T>::value && !std::is_signed<T>::value &&\n                     !std::is_same<T, bool>::value && (sizeof(T) <= 32)>> {\n public:\n  static bool ParseValue(std::string_view input, T* value,\n                         std::string* reason) {\n    unsigned value_tmp;\n    if (!StringToUint(input, &value_tmp)) {\n      *reason =\n          absl::Substitute(\"failed to convert unsigned int (\\\"$0\\\")\", input);\n      return false;\n    }\n    if (value_tmp <= std::numeric_limits<T>::max()) {\n      *value = static_cast<T>(value_tmp);\n      return true;\n    }\n    *reason = absl::Substitute(\"$0 is out of its range\", input);\n    return false;\n  }\n};\n\ntemplate <>\nclass FlagValueTraits<float> {\n public:\n  static bool ParseValue(std::string_view input, float* value,\n                         std::string* reason) {\n    if (!StringToFloat(input, value)) {\n      *reason = absl::Substitute(\"failed to convert to float (\\\"$0\\\")\", input);\n      return false;\n    }\n    return true;\n  }\n};\n\ntemplate <>\nclass FlagValueTraits<double> {\n public:\n  static bool ParseValue(std::string_view input, double* value,\n                         std::string* reason) {\n    if (!StringToDouble(input, value)) {\n      *reason = absl::Substitute(\"failed to convert to double (\\\"$0\\\")\", input);\n      return false;\n    }\n    return true;\n  }\n};\n\ntemplate <>\nclass FlagValueTraits<int64_t> {\n public:\n  static bool ParseValue(std::string_view input, int64_t* value,\n                         std::string* reason) {\n    if (!StringToInt64(input, value)) {\n      *reason =\n          absl::Substitute(\"failed to convert to int64_t (\\\"$0\\\")\", input);\n      return false;\n    }\n    return true;\n  }\n};\n\ntemplate <>\nclass FlagValueTraits<uint64_t> {\n public:\n  static bool ParseValue(std::string_view input, uint64_t* value,\n                         std::string* reason) {\n    if (!StringToUint64(input, value)) {\n      *reason =\n          absl::Substitute(\"failed to convert to uint64_t (\\\"$0\\\")\", input);\n      return false;\n    }\n    return true;\n  }\n};\n\ntemplate <>\nclass FlagValueTraits<bool> {\n public:\n  static bool ParseValue(std::string_view input, bool* value,\n                         std::string* reason) {\n    *value = true;\n    return true;\n  }\n};\n\ntemplate <>\nclass FlagValueTraits<std::string> {\n public:\n  static bool ParseValue(std::string_view input, std::string* value,\n                         std::string* reason) {\n    if (input.length() == 0) {\n      *reason = \"input is empty\";\n      return false;\n    }\n    *value = std::string(input);\n    return true;\n  }\n};\n\ntemplate <typename T>\nclass FlagValueTraits<std::vector<T>> {\n public:\n  static bool ParseValue(std::string_view input, std::vector<T>* value,\n                         std::string* reason) {\n    T element;\n    if (FlagValueTraits<T>::ParseValue(input, &element, reason)) {\n      value->push_back(std::move(element));\n      return true;\n    }\n    return false;\n  }\n};\n\ntemplate <typename T>\nclass FlagValueTraits<std::set<T>> {\n public:\n  static bool ParseValue(std::string_view input, std::set<T>* value,\n                         std::string* reason) {\n    T element;\n    if (FlagValueTraits<T>::ParseValue(input, &element, reason)) {\n      value->insert(std::move(element));\n      return true;\n    }\n    return false;\n  }\n};\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_FLAG_FLAG_VALUE_TRAITS_H_\n"
  },
  {
    "path": "tachyon/base/flag/numeric_flags.h",
    "content": "#ifndef TACHYON_BASE_FLAG_NUMERIC_FLAGS_H_\n#define TACHYON_BASE_FLAG_NUMERIC_FLAGS_H_\n\n#include <string>\n\n#include \"tachyon/base/flag/flag.h\"\n\nnamespace tachyon::base {\nnamespace flag_internal {\n\ntemplate <typename T>\nstruct PositiveNumber {\n  T value;\n};\n\nusing PositiveInt = flag_internal::PositiveNumber<int>;\nusing PositiveInt8 = flag_internal::PositiveNumber<int8_t>;\nusing PositiveInt16 = flag_internal::PositiveNumber<int16_t>;\nusing PositiveInt32 = flag_internal::PositiveNumber<int32_t>;\nusing PositiveInt64 = flag_internal::PositiveNumber<int64_t>;\nusing PositiveFloat = flag_internal::PositiveNumber<float>;\nusing PositiveDouble = flag_internal::PositiveNumber<double>;\n\n}  // namespace flag_internal\n\ntemplate <typename T>\nclass FlagValueTraits<flag_internal::PositiveNumber<T>> {\n public:\n  static bool ParseValue(std::string_view input,\n                         flag_internal::PositiveNumber<T>* value,\n                         std::string* reason) {\n    T n;\n    if (!FlagValueTraits<T>::ParseValue(input, &n, reason)) {\n      return false;\n    }\n    if (n > 0) {\n      value->value = n;\n      return true;\n    } else {\n      *reason = \"value should be positive\";\n      return false;\n    }\n    return true;\n  }\n};\n\nusing PositiveIntFlag = Flag<flag_internal::PositiveInt>;\nusing PositiveInt8Flag = Flag<flag_internal::PositiveInt8>;\nusing PositiveInt16Flag = Flag<flag_internal::PositiveInt16>;\nusing PositiveInt32Flag = Flag<flag_internal::PositiveInt32>;\nusing PositiveInt64Flag = Flag<flag_internal::PositiveInt64>;\nusing PositiveFloatFlag = Flag<flag_internal::PositiveFloat>;\nusing PositiveDoubleFlag = Flag<flag_internal::PositiveDouble>;\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_FLAG_NUMERIC_FLAGS_H_\n"
  },
  {
    "path": "tachyon/base/functional/.clang-format",
    "content": "DisableFormat: true\nSortIncludes: true\n"
  },
  {
    "path": "tachyon/base/functional/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"callback_forward\",\n    hdrs = [\"callback_forward.h\"],\n)\n\ntachyon_cc_library(\n    name = \"callback\",\n    hdrs = [\"callback.h\"],\n    deps = [\n        \":callback_forward\",\n        \"@com_google_absl//absl/functional:function_ref\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"function_ref\",\n    hdrs = [\"function_ref.h\"],\n    deps = [\n        \":functor_traits\",\n        \"@com_google_absl//absl/base:core_headers\",\n        \"@com_google_absl//absl/functional:function_ref\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"functor_traits\",\n    hdrs = [\"functor_traits.h\"],\n    deps = [\n        \"//tachyon/base:type_list\",\n        \"@com_google_absl//absl/meta:type_traits\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"identity\",\n    hdrs = [\"identity.h\"],\n)\n\ntachyon_cc_library(\n    name = \"invoke\",\n    hdrs = [\"invoke.h\"],\n)\n\ntachyon_cc_unittest(\n    name = \"functional_unittests\",\n    srcs = [\n        \"function_ref_unittest.cc\",\n        \"identity_unittest.cc\",\n        \"invoke_unittest.cc\",\n    ],\n    deps = [\n        \":function_ref\",\n        \":identity\",\n        \":invoke\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/base/functional/callback.h",
    "content": "// Copyright 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_FUNCTIONAL_CALLBACK_H_\n#define TACHYON_BASE_FUNCTIONAL_CALLBACK_H_\n\n#include <functional>\n#include <utility>\n#include <type_traits>\n\n#include \"absl/functional/function_ref.h\"\n\n#include \"tachyon/base/functional/callback_forward.h\"\n\nnamespace tachyon::base {\n\ntemplate <typename R, typename... Args>\nclass OnceCallback<R(Args...)> {\n public:\n  typedef std::function<R(Args...)> CallbackTy;\n\n  constexpr OnceCallback() = default;\n  OnceCallback(std::nullptr_t) = delete;\n  OnceCallback(absl::FunctionRef<R(Args...)> callback) : callback_(callback) {}\n  template <typename T,\n            std::enable_if_t<std::is_convertible_v<T, CallbackTy>>* = nullptr>\n  OnceCallback(T&& callback) : callback_(callback) {}\n  OnceCallback(const OnceCallback& other) = delete;\n  OnceCallback& operator=(const OnceCallback& other) = delete;\n  OnceCallback(OnceCallback&& other) noexcept = default;\n  OnceCallback& operator=(OnceCallback&& other) noexcept = default;\n\n  OnceCallback(const RepeatingCallback<R(Args...)>& other) noexcept\n      : callback_(other.callback_) {}\n  OnceCallback& operator=(const RepeatingCallback<R(Args...)>& other) noexcept {\n    callback_ = other.callback_;\n    return *this;\n  }\n\n  R Run(Args... args) && {\n    CallbackTy callback = callback_;\n    callback_ = nullptr;\n    return callback(std::forward<Args>(args)...);\n  }\n\n  operator bool() const { return static_cast<bool>(callback_); }\n\n  bool is_null() const { return !static_cast<bool>(callback_); }\n\n  void Reset() { callback_ = nullptr; }\n\n private:\n  CallbackTy callback_;\n};\n\ntemplate <typename R, typename... Args>\nclass RepeatingCallback<R(Args...)> {\n public:\n  typedef std::function<R(Args...)> CallbackTy;\n\n  constexpr RepeatingCallback() = default;\n  RepeatingCallback(std::nullptr_t) = delete;\n  RepeatingCallback(absl::FunctionRef<R(Args...)> callback)\n      : callback_(callback) {}\n  template <typename T,\n            std::enable_if_t<std::is_convertible_v<T, CallbackTy>>* = nullptr>\n  RepeatingCallback(T&& callback) : callback_(callback) {}\n  RepeatingCallback(const RepeatingCallback& other) = default;\n  RepeatingCallback& operator=(const RepeatingCallback& other) = default;\n\n  R Run(Args... args) const& { return callback_(std::forward<Args>(args)...); }\n\n  R Run(Args... args) && {\n    CallbackTy callback = callback_;\n    callback_ = nullptr;\n    return callback(std::forward<Args>(args)...);\n  }\n\n  operator bool() const { return static_cast<bool>(callback_); }\n\n  bool is_null() const { return !static_cast<bool>(callback_); }\n\n  void Reset() { callback_ = nullptr; }\n\n private:\n  friend class OnceCallback<R(Args...)>;\n  CallbackTy callback_;\n};\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_FUNCTIONAL_CALLBACK_H_\n"
  },
  {
    "path": "tachyon/base/functional/callback_forward.h",
    "content": "// Copyright 2011 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_FUNCTIONAL_CALLBACK_FORWARD_H_\n#define TACHYON_BASE_FUNCTIONAL_CALLBACK_FORWARD_H_\n\nnamespace tachyon::base {\n\ntemplate <typename Signature>\nclass OnceCallback;\n\ntemplate <typename Signature>\nclass RepeatingCallback;\n\n// Syntactic sugar to make OnceClosure<void()> and RepeatingClosure<void()>\n// easier to declare since they will be used in a lot of APIs with delayed\n// execution.\nusing OnceClosure = OnceCallback<void()>;\nusing RepeatingClosure = RepeatingCallback<void()>;\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_FUNCTIONAL_CALLBACK_FORWARD_H_\n"
  },
  {
    "path": "tachyon/base/functional/function_ref.h",
    "content": "// Copyright 2022 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_FUNCTIONAL_FUNCTION_REF_H_\n#define TACHYON_BASE_FUNCTIONAL_FUNCTION_REF_H_\n\n#include <type_traits>\n#include <utility>\n\n#include \"absl/base/attributes.h\"\n#include \"absl/functional/function_ref.h\"\n\n#include \"tachyon/base/functional/functor_traits.h\"\n\nnamespace tachyon::base {\n\ntemplate <typename Signature>\nclass FunctionRef;\n\n// A non-owning reference to any invocable object (e.g. function pointer, method\n// pointer, functor, lambda, et cetera) suitable for use as a type-erased\n// argument to ForEach-style functions or other visitor patterns that:\n//\n// - do not need to copy or take ownership of the argument\n// - synchronously call the invocable that was passed as an argument\n//\n// `base::FunctionRef` makes no heap allocations: it is trivially copyable and\n// should be passed by value.\n//\n// `base::FunctionRef` has no null/empty state: a `base::FunctionRef` is always\n// valid to invoke.\n//\n// The usual lifetime precautions for other non-owning references types (e.g.\n// `std::string_view`, `absl::Span`) also apply to `base::FunctionRef`.\n// `base::FunctionRef` should typically be used as an argument; returning a\n// `base::FunctionRef` or storing a `base::FunctionRef` as a field is dangerous\n// and likely to result in lifetime bugs.\n//\n// `base::RepeatingCallback` and `base::BindRepeating()` is another common way\n// to represent type-erased invocable objects. In contrast, it requires a heap\n// allocation and is not trivially copyable. It should be used when there are\n// ownership requirements (e.g. partial application of arguments to a function\n// stored for asynchronous execution).\n//\n// Note: `base::FunctionRef` is similar to `absl::FunctionRef<R(Args...)>`, but\n// with stricter conversions between function types. Return type conversions are\n// allowed (e.g. `int` -> `bool`, `Derived*` -> `Base*`); other than that,\n// function parameter types must match exactly, and return values may not be\n// silently discarded, e.g. `absl::FunctionRef` allows the following:\n//\n//   // Silently discards `42`.\n//   [] (absl::FunctionRef<void()> r) {\n//     r();\n//   }([] { return 42; });\n//\n// But with `base::FunctionRef`:\n//\n//   // Does not compile!\n//   [] (base::FunctionRef<void()> r) {\n//     r();\n//   }([] { return 42; });\ntemplate <typename R, typename... Args>\nclass FunctionRef<R(Args...)> {\n private:\n  template <typename Functor,\n            typename FunctorReturnType =\n                typename internal::BindTypeHelper<Functor>::ReturnType,\n            typename FunctorArgsAsTypeList =\n                typename internal::BindTypeHelper<Functor>::RunParamsList>\n  using EnableIfCompatible = std::enable_if_t<\n      std::is_convertible_v<FunctorReturnType, R> &&\n      std::is_same_v<FunctorArgsAsTypeList, internal::TypeList<Args...>>>;\n\n public:\n  // `ABSL_ATTRIBUTE_LIFETIME_BOUND` is important since `FunctionRef` retains\n  // only a reference to `functor`, `functor` must outlive `this`.\n  template <typename Functor, typename = EnableIfCompatible<Functor>>\n  // NOLINTNEXTLINE(google-explicit-constructor)\n  FunctionRef(const Functor& functor ABSL_ATTRIBUTE_LIFETIME_BOUND)\n      : wrapped_func_ref_(functor) {}\n\n  // Null FunctionRefs are not allowed.\n  FunctionRef() = delete;\n\n  FunctionRef(const FunctionRef&) = default;\n  // Reduce the likelihood of lifetime bugs by disallowing assignment.\n  FunctionRef& operator=(const FunctionRef&) = delete;\n\n  R operator()(Args... args) const {\n    return wrapped_func_ref_(std::forward<Args>(args)...);\n  }\n\n  absl::FunctionRef<R(Args...)> ToAbsl() const { return wrapped_func_ref_; }\n\n  // In Chrome, converting to `absl::FunctionRef` should be explicitly done\n  // through `ToAbsl()`.\n  template <typename Signature>\n  operator absl::FunctionRef<Signature>() = delete;\n\n private:\n  absl::FunctionRef<R(Args...)> wrapped_func_ref_;\n};\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_FUNCTIONAL_FUNCTION_REF_H_\n"
  },
  {
    "path": "tachyon/base/functional/function_ref_unittest.cc",
    "content": "// Copyright 2022 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/functional/function_ref.h\"\n\n#include \"gtest/gtest.h\"\n\nnamespace tachyon::base {\n\nnamespace {\n\nchar Moo(float) {\n  return 'a';\n}\n\nstruct C {\n  long Method() { return value; }\n  long value;\n};\n\n}  // namespace\n\nTEST(FunctionRefTest, FreeFunction) {\n  [](FunctionRef<char(float)> ref) { EXPECT_EQ('a', ref(1.0)); }(&Moo);\n}\n\nTEST(FunctionRefTest, Method) {\n  [](FunctionRef<long(C*)> ref) {\n    C c = {.value = 25L};\n    EXPECT_EQ(25L, ref(&c));\n  }(&C::Method);\n}\n\nTEST(FunctionRefTest, Lambda) {\n  int x = 3;\n  auto lambda = [&x]() { return x; };\n  [](FunctionRef<int()> ref) { EXPECT_EQ(3, ref()); }(lambda);\n}\n\n// Tests for passing a `base::FunctionRef` as an `absl::FunctionRef`.\nTEST(FunctionRefTest, AbslConversion) {\n  // Matching signatures should work.\n  {\n    bool called = false;\n    auto lambda = [&called](float) {\n      called = true;\n      return 'a';\n    };\n    FunctionRef<char(float)> ref(lambda);\n    [](absl::FunctionRef<char(float)> absl_ref) {\n      absl_ref(1.0);\n    }(ref.ToAbsl());\n    EXPECT_TRUE(called);\n  }\n\n  // `absl::FunctionRef` should be able to adapt \"similar enough\" signatures.\n  {\n    bool called = false;\n    auto lambda = [&called](float) {\n      called = true;\n      return 'a';\n    };\n    FunctionRef<char(float)> ref(lambda);\n    [](absl::FunctionRef<void(float)> absl_ref) {\n      absl_ref(1.0);\n    }(ref.ToAbsl());\n    EXPECT_TRUE(called);\n  }\n}\n\n// base::FunctionRef allows functors with convertible return types to be\n// adapted.\nTEST(FunctionRefTest, ConvertibleReturnTypes) {\n  // Hopefully this never results in a postmorterm-worthy bug...\n  {\n    auto lambda = []() -> bool { return true; };\n    [](FunctionRef<int()> ref) { EXPECT_EQ(1, ref()); }(lambda);\n  }\n\n  {\n    class Base {};\n    class Derived : public Base {};\n\n    auto lambda = []() -> Derived* { return nullptr; };\n    [](FunctionRef<Base*()> ref) { EXPECT_EQ(nullptr, ref()); }(lambda);\n  }\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/functional/functor_traits.h",
    "content": "// Copyright (c) 2011 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n// Derived from chromium/base/bind_internal.h\n\n#ifndef TACHYON_BASE_FUNCTIONAL_FUNCTOR_TRAITS_H_\n#define TACHYON_BASE_FUNCTIONAL_FUNCTOR_TRAITS_H_\n\n#include <functional>\n#include <utility>\n\n#include \"absl/meta/type_traits.h\"\n\n#include \"tachyon/base/type_list.h\"\n\nnamespace tachyon::base::internal {\n\n// Used for MakeFunctionType implementation.\ntemplate <typename R, typename ArgList>\nstruct MakeFunctionTypeImpl;\n\ntemplate <typename R, typename... Args>\nstruct MakeFunctionTypeImpl<R, TypeList<Args...>> {\n  // MSVC 2013 doesn't support Type Alias of function types.\n  // Revisit this after we update it to newer version.\n  typedef R Type(Args...);\n};\n\n// A type-level function that constructs a function type that has |R| as its\n// return type and has TypeLists items as its arguments.\ntemplate <typename R, typename ArgList>\nusing MakeFunctionType = typename MakeFunctionTypeImpl<R, ArgList>::Type;\n\n// Used for ExtractArgs and ExtractReturnType.\ntemplate <typename Signature>\nstruct ExtractArgsImpl;\n\ntemplate <typename R, typename... Args>\nstruct ExtractArgsImpl<R(Args...)> {\n  using ReturnType = R;\n  using ArgsList = TypeList<Args...>;\n};\n\n// A type-level function that extracts function arguments into a TypeList.\n// E.g. ExtractArgs<R(A, B, C)> is evaluated to TypeList<A, B, C>.\ntemplate <typename Signature>\nusing ExtractArgs = typename ExtractArgsImpl<Signature>::ArgsList;\n\n// A type-level function that extracts the return type of a function.\n// E.g. ExtractReturnType<R(A, B, C)> is evaluated to R.\ntemplate <typename Signature>\nusing ExtractReturnType = typename ExtractArgsImpl<Signature>::ReturnType;\n\ntemplate <typename Callable,\n          typename Signature = decltype(&Callable::operator())>\nstruct ExtractCallableRunTypeImpl;\n\ntemplate <typename Callable, typename R, typename... Args>\nstruct ExtractCallableRunTypeImpl<Callable, R (Callable::*)(Args...)> {\n  using Type = R(Args...);\n};\n\ntemplate <typename Callable, typename R, typename... Args>\nstruct ExtractCallableRunTypeImpl<Callable, R (Callable::*)(Args...) const> {\n  using Type = R(Args...);\n};\n\n// Evaluated to RunType of the given callable type.\n// Example:\n//   auto f = [](int, char*) { return 0.1; };\n//   ExtractCallableRunType<decltype(f)>\n//   is evaluated to\n//   double(int, char*);\ntemplate <typename Callable>\nusing ExtractCallableRunType =\n    typename ExtractCallableRunTypeImpl<Callable>::Type;\n\n// IsCallableObject<Functor> is std::true_type if |Functor| has operator().\n// Otherwise, it's std::false_type.\n// Example:\n//   IsCallableObject<void(*)()>::value is false.\n//\n//   struct Foo {};\n//   IsCallableObject<void(Foo::*)()>::value is false.\n//\n//   int i = 0;\n//   auto f = [i]() {};\n//   IsCallableObject<decltype(f)>::value is false.\ntemplate <typename Functor, typename SFINAE = void>\nstruct IsCallableObject : std::false_type {};\n\ntemplate <typename Callable>\nstruct IsCallableObject<Callable, absl::void_t<decltype(&Callable::operator())>>\n    : std::true_type {};\n\n// FunctorTraits<>\n//\n// See description at top of file.\ntemplate <typename Functor, typename SFINAE = void>\nstruct FunctorTraits;\n\n// For callable types.\n// This specialization handles lambdas (captureless and capturing) and functors\n// with a call operator. Capturing lambdas and stateful functors are explicitly\n// disallowed by BindImpl().\n//\n// Example:\n//\n//   // Captureless lambdas are allowed.\n//   []() {return 42;};\n//\n//   // Capturing lambdas are *not* allowed.\n//   int x;\n//   [x]() {return x;};\n//\n//   // Any empty class with operator() is allowed.\n//   struct Foo {\n//     void operator()() const {}\n//     // No non-static member variable and no virtual functions.\n//   };\ntemplate <typename Functor>\nstruct FunctorTraits<Functor,\n                     std::enable_if_t<IsCallableObject<Functor>::value>> {\n  using RunType = ExtractCallableRunType<Functor>;\n  using ReturnType = ExtractReturnType<RunType>;\n\n  static constexpr bool is_method = false;\n  static constexpr bool is_nullable = false;\n  static constexpr bool is_callback = false;\n  static constexpr bool is_stateless = std::is_empty_v<Functor>;\n\n  template <typename RunFunctor, typename... RunArgs>\n  static ExtractReturnType<RunType> Invoke(RunFunctor&& functor,\n                                           RunArgs&&... args) {\n    return std::forward<RunFunctor>(functor)(std::forward<RunArgs>(args)...);\n  }\n};\n\n// For functions.\ntemplate <typename R, typename... Args>\nstruct FunctorTraits<R (*)(Args...)> {\n  using RunType = R(Args...);\n  using ReturnType = R;\n\n  static constexpr bool is_method = false;\n  static constexpr bool is_nullable = true;\n  static constexpr bool is_callback = false;\n  static constexpr bool is_stateless = true;\n\n  template <typename Function, typename... RunArgs>\n  static R Invoke(Function&& function, RunArgs&&... args) {\n    return std::forward<Function>(function)(std::forward<RunArgs>(args)...);\n  }\n};\n\n// For methods.\ntemplate <typename R, typename Receiver, typename... Args>\nstruct FunctorTraits<R (Receiver::*)(Args...)> {\n  using RunType = R(Receiver*, Args...);\n  using ReturnType = R;\n\n  static constexpr bool is_method = true;\n  static constexpr bool is_nullable = true;\n  static constexpr bool is_callback = false;\n  static constexpr bool is_stateless = true;\n\n  template <typename Method, typename ReceiverPtr, typename... RunArgs>\n  static R Invoke(Method method, ReceiverPtr&& receiver_ptr,\n                  RunArgs&&... args) {\n    return ((*receiver_ptr).*method)(std::forward<RunArgs>(args)...);\n  }\n};\n\n// For const methods.\ntemplate <typename R, typename Receiver, typename... Args>\nstruct FunctorTraits<R (Receiver::*)(Args...) const> {\n  using RunType = R(const Receiver*, Args...);\n  using ReturnType = R;\n\n  static constexpr bool is_method = true;\n  static constexpr bool is_nullable = true;\n  static constexpr bool is_callback = false;\n  static constexpr bool is_stateless = true;\n\n  template <typename Method, typename ReceiverPtr, typename... RunArgs>\n  static R Invoke(Method method, ReceiverPtr&& receiver_ptr,\n                  RunArgs&&... args) {\n    return ((*receiver_ptr).*method)(std::forward<RunArgs>(args)...);\n  }\n};\n\n#ifdef __cpp_noexcept_function_type\n// noexcept makes a distinct function type in C++17.\n// I.e. `void(*)()` and `void(*)() noexcept` are same in pre-C++17, and\n// different in C++17.\ntemplate <typename R, typename... Args>\nstruct FunctorTraits<R (*)(Args...) noexcept> : FunctorTraits<R (*)(Args...)> {\n};\n\ntemplate <typename R, typename Receiver, typename... Args>\nstruct FunctorTraits<R (Receiver::*)(Args...) noexcept>\n    : FunctorTraits<R (Receiver::*)(Args...)> {};\n\ntemplate <typename R, typename Receiver, typename... Args>\nstruct FunctorTraits<R (Receiver::*)(Args...) const noexcept>\n    : FunctorTraits<R (Receiver::*)(Args...) const> {};\n#endif\n\ntemplate <typename Functor>\nusing MakeFunctorTraits = FunctorTraits<std::decay_t<Functor>>;\n\n// Extracts necessary type info from Functor and BoundArgs.\n// Used to implement MakeUnboundRunType, BindOnce and BindRepeating.\ntemplate <typename Functor, typename... BoundArgs>\nstruct BindTypeHelper {\n  static constexpr size_t num_bounds = sizeof...(BoundArgs);\n  using FunctorTraits = MakeFunctorTraits<Functor>;\n\n  // Example:\n  //   When Functor is `double (Foo::*)(int, const std::string&)`, and BoundArgs\n  //   is a template pack of `Foo*` and `int16_t`:\n  //    - RunType is `double(Foo*, int, const std::string&)`,\n  //    - ReturnType is `double`,\n  //    - RunParamsList is `TypeList<Foo*, int, const std::string&>`,\n  //    - BoundParamsList is `TypeList<Foo*, int>`,\n  //    - UnboundParamsList is `TypeList<const std::string&>`,\n  //    - BoundArgsList is `TypeList<Foo*, int16_t>`,\n  //    - UnboundRunType is `double(const std::string&)`.\n  using RunType = typename FunctorTraits::RunType;\n  using ReturnType = ExtractReturnType<RunType>;\n\n  using RunParamsList = ExtractArgs<RunType>;\n  using BoundParamsList = TakeTypeListItem<num_bounds, RunParamsList>;\n  using UnboundParamsList = DropTypeListItem<num_bounds, RunParamsList>;\n\n  using BoundArgsList = TypeList<BoundArgs...>;\n\n  using UnboundRunType = MakeFunctionType<ReturnType, UnboundParamsList>;\n};\n\n}  // namespace tachyon::base::internal\n\n#endif  // TACHYON_BASE_FUNCTIONAL_FUNCTOR_TRAITS_H_\n"
  },
  {
    "path": "tachyon/base/functional/identity.h",
    "content": "// Copyright 2020 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_FUNCTIONAL_IDENTITY_H_\n#define TACHYON_BASE_FUNCTIONAL_IDENTITY_H_\n\n#include <utility>\n\nnamespace tachyon::base {\n\n// Implementation of C++20's std::identity.\n//\n// Reference:\n// - https://en.cppreference.com/w/cpp/utility/functional/identity\n// - https://wg21.link/func.identity\nstruct identity {\n  template <typename T>\n  constexpr T&& operator()(T&& t) const noexcept {\n    return std::forward<T>(t);\n  }\n\n  using is_transparent = void;\n};\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_FUNCTIONAL_IDENTITY_H_\n"
  },
  {
    "path": "tachyon/base/functional/identity_unittest.cc",
    "content": "// Copyright 2020 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/functional/identity.h\"\n\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\nnamespace tachyon::base {\n\nTEST(FunctionalTest, Identity) {\n  static constexpr identity id;\n\n  std::vector<int> v;\n  EXPECT_EQ(&v, &id(v));\n\n  constexpr int arr = {0};\n  static_assert(arr == id(arr), \"\");\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/functional/invoke.h",
    "content": "// Copyright 2020 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_FUNCTIONAL_INVOKE_H_\n#define TACHYON_BASE_FUNCTIONAL_INVOKE_H_\n\n#include <type_traits>\n#include <utility>\n\nnamespace tachyon::base {\n\nnamespace internal {\n\n// Helper struct and alias to deduce the class type from a member function\n// pointer or member object pointer.\ntemplate <typename DecayedF>\nstruct member_pointer_class {};\n\ntemplate <typename ReturnT, typename ClassT>\nstruct member_pointer_class<ReturnT ClassT::*> {\n  using type = ClassT;\n};\n\ntemplate <typename DecayedF>\nusing member_pointer_class_t = typename member_pointer_class<DecayedF>::type;\n\n// Utility struct to detect specializations of std::reference_wrapper.\ntemplate <typename T>\nstruct is_reference_wrapper : std::false_type {};\n\ntemplate <typename T>\nstruct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};\n\n// Small helpers used below in internal::invoke to make the SFINAE more concise.\ntemplate <typename F>\nconst bool& IsMemFunPtr =\n    std::is_member_function_pointer<std::decay_t<F>>::value;\n\ntemplate <typename F>\nconst bool& IsMemObjPtr = std::is_member_object_pointer<std::decay_t<F>>::value;\n\ntemplate <typename F,\n          typename T,\n          typename MemPtrClass = member_pointer_class_t<std::decay_t<F>>>\nconst bool& IsMemPtrToBaseOf =\n    std::is_base_of<MemPtrClass, std::decay_t<T>>::value;\n\ntemplate <typename T>\nconst bool& IsRefWrapper = is_reference_wrapper<std::decay_t<T>>::value;\n\ntemplate <bool B>\nusing EnableIf = std::enable_if_t<B, bool>;\n\n// Invokes a member function pointer on a reference to an object of a suitable\n// type. Covers bullet 1 of the INVOKE definition.\n//\n// Reference: https://wg21.link/func.require#1.1\ntemplate <typename F,\n          typename T1,\n          typename... Args,\n          EnableIf<IsMemFunPtr<F> && IsMemPtrToBaseOf<F, T1>> = true>\nconstexpr decltype(auto) InvokeImpl(F&& f, T1&& t1, Args&&... args) {\n  return (std::forward<T1>(t1).*f)(std::forward<Args>(args)...);\n}\n\n// Invokes a member function pointer on a std::reference_wrapper to an object of\n// a suitable type. Covers bullet 2 of the INVOKE definition.\n//\n// Reference: https://wg21.link/func.require#1.2\ntemplate <typename F,\n          typename T1,\n          typename... Args,\n          EnableIf<IsMemFunPtr<F> && IsRefWrapper<T1>> = true>\nconstexpr decltype(auto) InvokeImpl(F&& f, T1&& t1, Args&&... args) {\n  return (t1.get().*f)(std::forward<Args>(args)...);\n}\n\n// Invokes a member function pointer on a pointer-like type to an object of a\n// suitable type. Covers bullet 3 of the INVOKE definition.\n//\n// Reference: https://wg21.link/func.require#1.3\ntemplate <typename F,\n          typename T1,\n          typename... Args,\n          EnableIf<IsMemFunPtr<F> && !IsMemPtrToBaseOf<F, T1> &&\n                   !IsRefWrapper<T1>> = true>\nconstexpr decltype(auto) InvokeImpl(F&& f, T1&& t1, Args&&... args) {\n  return ((*std::forward<T1>(t1)).*f)(std::forward<Args>(args)...);\n}\n\n// Invokes a member object pointer on a reference to an object of a suitable\n// type. Covers bullet 4 of the INVOKE definition.\n//\n// Reference: https://wg21.link/func.require#1.4\ntemplate <typename F,\n          typename T1,\n          EnableIf<IsMemObjPtr<F> && IsMemPtrToBaseOf<F, T1>> = true>\nconstexpr decltype(auto) InvokeImpl(F&& f, T1&& t1) {\n  return std::forward<T1>(t1).*f;\n}\n\n// Invokes a member object pointer on a std::reference_wrapper to an object of\n// a suitable type. Covers bullet 5 of the INVOKE definition.\n//\n// Reference: https://wg21.link/func.require#1.5\ntemplate <typename F,\n          typename T1,\n          EnableIf<IsMemObjPtr<F> && IsRefWrapper<T1>> = true>\nconstexpr decltype(auto) InvokeImpl(F&& f, T1&& t1) {\n  return t1.get().*f;\n}\n\n// Invokes a member object pointer on a pointer-like type to an object of a\n// suitable type. Covers bullet 6 of the INVOKE definition.\n//\n// Reference: https://wg21.link/func.require#1.6\ntemplate <typename F,\n          typename T1,\n          EnableIf<IsMemObjPtr<F> && !IsMemPtrToBaseOf<F, T1> &&\n                   !IsRefWrapper<T1>> = true>\nconstexpr decltype(auto) InvokeImpl(F&& f, T1&& t1) {\n  return (*std::forward<T1>(t1)).*f;\n}\n\n// Invokes a regular function or function object. Covers bullet 7 of the INVOKE\n// definition.\n//\n// Reference: https://wg21.link/func.require#1.7\ntemplate <typename F, typename... Args>\nconstexpr decltype(auto) InvokeImpl(F&& f, Args&&... args) {\n  return std::forward<F>(f)(std::forward<Args>(args)...);\n}\n\n}  // namespace internal\n\n// Implementation of C++17's std::invoke. This is not based on implementation\n// referenced in original std::invoke proposal, but rather a manual\n// implementation, so that it can be constexpr.\n//\n// References:\n// - https://wg21.link/n4169#implementability\n// - https://en.cppreference.com/w/cpp/utility/functional/invoke\n// - https://wg21.link/func.invoke\ntemplate <typename F, typename... Args>\nconstexpr decltype(auto) invoke(F&& f, Args&&... args) {\n  return internal::InvokeImpl(std::forward<F>(f), std::forward<Args>(args)...);\n}\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_FUNCTIONAL_INVOKE_H_\n"
  },
  {
    "path": "tachyon/base/functional/invoke_unittest.cc",
    "content": "// Copyright 2020 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/functional/invoke.h\"\n\n#include <functional>\n\n#include \"gtest/gtest.h\"\n\nnamespace tachyon::base {\n\nTEST(FunctionalTest, Invoke) {\n  struct S {\n    int i;\n    constexpr int add(int x) const { return i + x; }\n  };\n\n  constexpr S s = {1};\n\n  // Note: The tests involving a std::reference_wrapper are not static_asserts,\n  // since std::reference_wrapper is not constexpr prior to C++20.\n  static_assert(base::invoke(&S::add, s, 2) == 3, \"\");\n  EXPECT_EQ(base::invoke(&S::add, std::ref(s), 2), 3);\n  static_assert(base::invoke(&S::add, &s, 3) == 4, \"\");\n\n  static_assert(base::invoke(&S::i, s) == 1, \"\");\n  EXPECT_EQ(base::invoke(&S::i, std::ref(s)), 1);\n  static_assert(base::invoke(&S::i, &s) == 1, \"\");\n\n  static_assert(base::invoke(std::plus<>(), 1, 2) == 3, \"\");\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/immediate_crash.h",
    "content": "// Copyright 2019 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n// clang-format off\n\n#ifndef TACHYON_BASE_IMMEDIATE_CRASH_H_\n#define TACHYON_BASE_IMMEDIATE_CRASH_H_\n\n#include \"tachyon/build/build_config.h\"\n\n// Crashes in the fastest possible way with no attempt at logging.\n// There are several constraints; see http://crbug.com/664209 for more context.\n//\n// - TRAP_SEQUENCE_() must be fatal. It should not be possible to ignore the\n//   resulting exception or simply hit 'continue' to skip over it in a debugger.\n// - Different instances of TRAP_SEQUENCE_() must not be folded together, to\n//   ensure crash reports are debuggable. Unlike __builtin_trap(), asm volatile\n//   blocks will not be folded together.\n//   Note: TRAP_SEQUENCE_() previously required an instruction with a unique\n//   nonce since unlike clang, GCC folds together identical asm volatile\n//   blocks.\n// - TRAP_SEQUENCE_() must produce a signal that is distinct from an invalid\n//   memory access.\n// - TRAP_SEQUENCE_() must be treated as a set of noreturn instructions.\n//   __builtin_unreachable() is used to provide that hint here. clang also uses\n//   this as a heuristic to pack the instructions in the function epilogue to\n//   improve code density.\n// - base::ImmediateCrash() is used in allocation hooks. To prevent recursions,\n//   TRAP_SEQUENCE_() must not allocate.\n//\n// Additional properties that are nice to have:\n// - TRAP_SEQUENCE_() should be as compact as possible.\n// - The first instruction of TRAP_SEQUENCE_() should not change, to avoid\n//   shifting crash reporting clusters. As a consequence of this, explicit\n//   assembly is preferred over intrinsics.\n//   Note: this last bullet point may no longer be true, and may be removed in\n//   the future.\n\n// Note: TRAP_SEQUENCE Is currently split into two macro helpers due to the fact\n// that clang emits an actual instruction for __builtin_unreachable() on certain\n// platforms (see https://crbug.com/958675). In addition, the int3/bkpt/brk will\n// be removed in followups, so splitting it up like this now makes it easy to\n// land the followups.\n\n#if defined(COMPILER_GCC)\n\n#if BUILDFLAG(IS_NACL)\n\n// Crash report accuracy is not guaranteed on NaCl.\n#define TRAP_SEQUENCE1_() __builtin_trap()\n#define TRAP_SEQUENCE2_() asm volatile(\"\")\n\n#elif defined(ARCH_CPU_X86_FAMILY)\n\n// TODO(https://crbug.com/958675): In theory, it should be possible to use just\n// int3. However, there are a number of crashes with SIGILL as the exception\n// code, so it seems likely that there's a signal handler that allows execution\n// to continue after SIGTRAP.\n#define TRAP_SEQUENCE1_() asm volatile(\"int3\")\n\n#if BUILDFLAG(IS_APPLE)\n// Intentionally empty: __builtin_unreachable() is always part of the sequence\n// (see IMMEDIATE_CRASH below) and already emits a ud2 on Mac.\n#define TRAP_SEQUENCE2_() asm volatile(\"\")\n#else\n#define TRAP_SEQUENCE2_() asm volatile(\"ud2\")\n#endif  // BUILDFLAG(IS_APPLE)\n\n#elif defined(ARCH_CPU_ARMEL)\n\n// bkpt will generate a SIGBUS when running on armv7 and a SIGTRAP when running\n// as a 32 bit userspace app on arm64. There doesn't seem to be any way to\n// cause a SIGTRAP from userspace without using a syscall (which would be a\n// problem for sandboxing).\n// TODO(https://crbug.com/958675): Remove bkpt from this sequence.\n#define TRAP_SEQUENCE1_() asm volatile(\"bkpt #0\")\n#define TRAP_SEQUENCE2_() asm volatile(\"udf #0\")\n\n#elif defined(ARCH_CPU_ARM64)\n\n// This will always generate a SIGTRAP on arm64.\n// TODO(https://crbug.com/958675): Remove brk from this sequence.\n#define TRAP_SEQUENCE1_() asm volatile(\"brk #0\")\n#define TRAP_SEQUENCE2_() asm volatile(\"hlt #0\")\n\n#else\n\n// Crash report accuracy will not be guaranteed on other architectures, but at\n// least this will crash as expected.\n#define TRAP_SEQUENCE1_() __builtin_trap()\n#define TRAP_SEQUENCE2_() asm volatile(\"\")\n\n#endif  // ARCH_CPU_*\n\n#elif defined(COMPILER_MSVC)\n\n#if !defined(__clang__)\n\n// MSVC x64 doesn't support inline asm, so use the MSVC intrinsic.\n#define TRAP_SEQUENCE1_() __debugbreak()\n#define TRAP_SEQUENCE2_()\n\n#elif defined(ARCH_CPU_ARM64)\n\n// Windows ARM64 uses \"BRK #F000\" as its breakpoint instruction, and\n// __debugbreak() generates that in both VC++ and clang.\n#define TRAP_SEQUENCE1_() __debugbreak()\n// Intentionally empty: __builtin_unreachable() is always part of the sequence\n// (see IMMEDIATE_CRASH below) and already emits a ud2 on Win64,\n// https://crbug.com/958373\n#define TRAP_SEQUENCE2_() __asm volatile(\"\")\n\n#else\n\n#define TRAP_SEQUENCE1_() asm volatile(\"int3\")\n#define TRAP_SEQUENCE2_() asm volatile(\"ud2\")\n\n#endif  // __clang__\n\n#else\n\n#error No supported trap sequence!\n\n#endif  // COMPILER_GCC\n\n#define TRAP_SEQUENCE_() \\\n  do {                   \\\n    TRAP_SEQUENCE1_();   \\\n    TRAP_SEQUENCE2_();   \\\n  } while (false)\n\n// This version of ALWAYS_INLINE inlines even in is_debug=true.\n// TODO(pbos): See if NDEBUG can be dropped from ALWAYS_INLINE as well, and if\n// so merge. Otherwise document why it cannot inline in debug in\n// base/compiler_specific.h.\n#if defined(COMPILER_GCC)\n#define IMMEDIATE_CRASH_ALWAYS_INLINE inline __attribute__((__always_inline__))\n#elif defined(COMPILER_MSVC)\n#define IMMEDIATE_CRASH_ALWAYS_INLINE __forceinline\n#else\n#define IMMEDIATE_CRASH_ALWAYS_INLINE inline\n#endif\n\nnamespace tachyon::base {\n\n[[noreturn]] IMMEDIATE_CRASH_ALWAYS_INLINE void ImmediateCrash() {\n  TRAP_SEQUENCE_();\n#if defined(__clang__) || defined(COMPILER_GCC)\n  __builtin_unreachable();\n#endif  // defined(__clang__) || defined(COMPILER_GCC)\n}\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_IMMEDIATE_CRASH_H_\n\n// clang-format on\n"
  },
  {
    "path": "tachyon/base/json/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"json\",\n    hdrs = [\"json.h\"],\n    deps = [\n        \":rapidjson_util\",\n        \"//tachyon/base/files:file_util\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"rapidjson_util\",\n    srcs = [\"rapidjson_util.cc\"],\n    hdrs = [\"rapidjson_util.h\"],\n    deps = [\n        \"//tachyon:export\",\n        \"//tachyon/base:bit_cast\",\n        \"//tachyon/base/numerics:safe_conversions\",\n        \"@com_github_tencent_rapidjson//:rapidjson\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"json_unittests\",\n    srcs = [\"json_unittest.cc\"],\n    data = [\"test/simple_data.json\"],\n    deps = [\":json\"],\n)\n"
  },
  {
    "path": "tachyon/base/json/json.h",
    "content": "#ifndef TACHYON_BASE_JSON_JSON_H_\n#define TACHYON_BASE_JSON_JSON_H_\n\n#include <string>\n\n#include \"rapidjson/document.h\"\n#include \"rapidjson/error/en.h\"\n#include \"rapidjson/stringbuffer.h\"\n#include \"rapidjson/writer.h\"\n\n#include \"tachyon/base/files/file_util.h\"\n#include \"tachyon/base/json/rapidjson_util.h\"\n\nnamespace tachyon::base {\n\n// Parse json string and populate |value| on success.\ntemplate <typename T>\nbool ParseJson(std::string_view content, T* value, std::string* error) {\n  rapidjson::Document document;\n  document.Parse(content.data(), content.length());\n  if (document.HasParseError()) {\n    *error =\n        absl::Substitute(\"Failed to parse with error \\\"$0\\\" at offset $1\",\n                         rapidjson::GetParseError_En(document.GetParseError()),\n                         document.GetErrorOffset());\n    return false;\n  }\n  return RapidJsonValueConverter<T>::To(document.GetObject(), \"\", value, error);\n}\n\n// Load from file and parse json string and populate |value| on success.\ntemplate <typename T>\nbool LoadAndParseJson(const FilePath& path, T* value, std::string* error) {\n  std::string content;\n  if (!ReadFileToString(path, &content)) {\n    *error = absl::Substitute(\"Failed to read file: $0\", path.value());\n    return false;\n  }\n  return ParseJson(content, value, error);\n}\n\n// Write |value| to json string.\ntemplate <typename T>\nstd::string WriteToJson(const T& value) {\n  rapidjson::Document document;\n  rapidjson::Value json_value =\n      RapidJsonValueConverter<T>::From(value, document.GetAllocator());\n  rapidjson::StringBuffer buffer;\n  rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);\n  json_value.Accept(writer);\n  return buffer.GetString();\n}\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_JSON_JSON_H_\n"
  },
  {
    "path": "tachyon/base/json/json_unittest.cc",
    "content": "#include \"tachyon/base/json/json.h\"\n\n#include <utility>\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\nnamespace tachyon::base {\n\nnamespace {\n\nstruct SimpleData {\n  std::string message;\n  int index = 0;\n  bool flag = false;\n  std::vector<unsigned int> data;\n\n  bool operator==(const SimpleData& other) const {\n    return message == other.message && index == other.index &&\n           flag == other.flag && data == other.data;\n  }\n  bool operator!=(const SimpleData& other) const { return !operator==(other); }\n};\n\nclass JsonTest : public testing::Test {\n public:\n  void SetUp() override {\n    expected_simple_data_.message = \"hello world\";\n    expected_simple_data_.index = 1;\n    expected_simple_data_.flag = true;\n    expected_simple_data_.data = std::vector<unsigned int>{0, 2, 4};\n  }\n\n protected:\n  SimpleData expected_simple_data_;\n};\n\n}  // namespace\n\ntemplate <>\nclass RapidJsonValueConverter<SimpleData> {\n public:\n  template <typename Allocator>\n  static rapidjson::Value From(const SimpleData& value, Allocator& allocator) {\n    rapidjson::Value object(rapidjson::kObjectType);\n    // NOTE: avoid unnecessary copy.\n    std::string_view message = value.message;\n    AddJsonElement(object, \"message\", message, allocator);\n    AddJsonElement(object, \"index\", value.index, allocator);\n    AddJsonElement(object, \"flag\", value.flag, allocator);\n    AddJsonElement(object, \"data\", value.data, allocator);\n    return object;\n  }\n\n  static bool To(const rapidjson::Value& json_value, std::string_view key,\n                 SimpleData* value, std::string* error) {\n    std::string message;\n    int index;\n    bool flag;\n    std::vector<unsigned int> data;\n    if (!ParseJsonElement(json_value, \"message\", &message, error)) return false;\n    if (!ParseJsonElement(json_value, \"index\", &index, error)) return false;\n    if (!ParseJsonElement(json_value, \"flag\", &flag, error)) return false;\n    if (!ParseJsonElement(json_value, \"data\", &data, error)) return false;\n\n    value->message = std::move(message);\n    value->index = index;\n    value->flag = flag;\n    value->data = std::move(data);\n    return true;\n  }\n};\n\nTEST_F(JsonTest, LoadAndParseJson) {\n  SimpleData simple_data;\n  std::string error;\n  ASSERT_TRUE(\n      LoadAndParseJson(FilePath(\"tachyon/base/json/test/simple_data.json\"),\n                       &simple_data, &error));\n  EXPECT_TRUE(error.empty());\n\n  EXPECT_EQ(simple_data, expected_simple_data_);\n}\n\nTEST_F(JsonTest, ParseInvalidJson) {\n  // missing key\n  std::string json = R\"({})\";\n  SimpleData simple_data;\n  std::string error;\n  ASSERT_FALSE(ParseJson(json, &simple_data, &error));\n  EXPECT_EQ(error, \"\\\"message\\\" key is not found\");\n\n  // invalid value\n  json = R\"({\"message\":3})\";\n  ASSERT_FALSE(ParseJson(json, &simple_data, &error));\n  EXPECT_EQ(error,\n            \"\\\"message\\\" expects type \\\"string\\\" but type \\\"number\\\" comes\");\n}\n\nTEST_F(JsonTest, WriteToJson) {\n  std::string json = WriteToJson(expected_simple_data_);\n  EXPECT_EQ(\n      json,\n      R\"({\"message\":\"hello world\",\"index\":1,\"flag\":true,\"data\":[0,2,4]})\");\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/json/rapidjson_util.cc",
    "content": "#include \"tachyon/base/json/rapidjson_util.h\"\n\n#include \"absl/strings/substitute.h\"\n\nnamespace tachyon::base {\n\nnamespace {\n\nconstexpr const char* kRapidJsonTypeNames[] = {\n    \"null\", \"false\", \"true\", \"object\", \"array\", \"string\", \"number\"};\n\n}  // namespace\n\nstd::string RapidJsonMismatchedTypeError(std::string_view key,\n                                         std::string_view type,\n                                         const rapidjson::Value& value) {\n  return absl::Substitute(\"\\\"$0\\\" expects type \\\"$1\\\" but type \\\"$2\\\" comes\",\n                          key, type, kRapidJsonTypeNames[value.GetType()]);\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/json/rapidjson_util.h",
    "content": "#ifndef TACHYON_BASE_JSON_RAPIDJSON_UTIL_H_\n#define TACHYON_BASE_JSON_RAPIDJSON_UTIL_H_\n\n#include <array>\n#include <optional>\n#include <string>\n#include <string_view>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"absl/strings/substitute.h\"\n#include \"rapidjson/document.h\"\n#include \"rapidjson/writer.h\"\n\n#include \"tachyon/base/bit_cast.h\"\n#include \"tachyon/base/numerics/safe_conversions.h\"\n#include \"tachyon/export.h\"\n\nnamespace tachyon::base {\n\nTACHYON_EXPORT std::string RapidJsonMismatchedTypeError(\n    std::string_view key, std::string_view type, const rapidjson::Value& value);\n\ntemplate <typename T>\nstd::string RapidJsonOutOfRangeError(std::string_view key, T value) {\n  return absl::Substitute(\"value($0) of \\\"$1\\\" is out of range\", value, key);\n}\n\ntemplate <typename T, typename SFINAE = void>\nclass RapidJsonValueConverter;\n\ntemplate <>\nclass RapidJsonValueConverter<bool> {\n public:\n  template <typename Allocator>\n  static rapidjson::Value From(bool value, Allocator& allocator) {\n    return rapidjson::Value(value);\n  }\n\n  static bool To(const rapidjson::Value& json_value, std::string_view key,\n                 bool* value, std::string* error) {\n    if (!json_value.IsBool()) {\n      *error = RapidJsonMismatchedTypeError(key, \"bool\", json_value);\n      return false;\n    }\n    *value = json_value.GetBool();\n    return true;\n  }\n};\n\ntemplate <typename T>\nclass RapidJsonValueConverter<\n    T, std::enable_if_t<std::is_integral<T>::value &&\n                        std::is_signed<T>::value && sizeof(T) == 8>> {\n public:\n  template <typename Allocator>\n  static rapidjson::Value From(T value, Allocator& allocator) {\n    int64_t i64_value = base::bit_cast<int64_t>(value);\n    return rapidjson::Value(i64_value);\n  }\n\n  static bool To(const rapidjson::Value& json_value, std::string_view key,\n                 T* value, std::string* error) {\n    if (!json_value.IsInt64() && !json_value.IsInt()) {\n      *error = RapidJsonMismatchedTypeError(key, \"int64\", json_value);\n      return false;\n    }\n    if (json_value.IsInt()) {\n      *value = base::bit_cast<T>(static_cast<int64_t>(json_value.GetInt()));\n    } else {\n      *value = base::bit_cast<T>(json_value.GetInt64());\n    }\n    return true;\n  }\n};\n\ntemplate <typename T>\nclass RapidJsonValueConverter<\n    T, std::enable_if_t<std::is_integral<T>::value &&\n                        !std::is_signed<T>::value && sizeof(T) == 8>> {\n public:\n  template <typename Allocator>\n  static rapidjson::Value From(T value, Allocator& allocator) {\n    uint64_t u64_value = base::bit_cast<uint64_t>(value);\n    return rapidjson::Value(u64_value);\n  }\n\n  static bool To(const rapidjson::Value& json_value, std::string_view key,\n                 T* value, std::string* error) {\n    if (!json_value.IsUint64() && !json_value.IsUint()) {\n      *error = RapidJsonMismatchedTypeError(key, \"uint64\", json_value);\n      return false;\n    }\n    if (json_value.IsUint()) {\n      *value = base::bit_cast<T>(static_cast<uint64_t>(json_value.GetUint()));\n    } else {\n      *value = base::bit_cast<T>(json_value.GetUint64());\n    }\n    return true;\n  }\n};\n\ntemplate <typename T>\nclass RapidJsonValueConverter<\n    T, std::enable_if_t<std::is_integral<T>::value &&\n                        std::is_signed<T>::value && sizeof(T) < 8>> {\n public:\n  template <typename Allocator>\n  static rapidjson::Value From(T value, Allocator& allocator) {\n    static_assert(sizeof(T) <= sizeof(int));\n    return rapidjson::Value(static_cast<int>(value));\n  }\n\n  static bool To(const rapidjson::Value& json_value, std::string_view key,\n                 T* value, std::string* error) {\n    if (!json_value.IsInt()) {\n      *error = RapidJsonMismatchedTypeError(key, \"int\", json_value);\n      return false;\n    }\n    int value_tmp = json_value.GetInt();\n    if (!IsValueInRangeForNumericType<T>(value_tmp)) {\n      *error = RapidJsonOutOfRangeError(key, value_tmp);\n      return false;\n    }\n    *value = static_cast<T>(value_tmp);\n    return true;\n  }\n};\n\ntemplate <typename T>\nclass RapidJsonValueConverter<\n    T, std::enable_if_t<std::is_integral<T>::value &&\n                        !std::is_signed<T>::value && sizeof(T) < 8>> {\n public:\n  template <typename Allocator>\n  static rapidjson::Value From(T value, Allocator& allocator) {\n    static_assert(sizeof(T) <= sizeof(unsigned));\n    return rapidjson::Value(static_cast<unsigned>(value));\n  }\n\n  static bool To(const rapidjson::Value& json_value, std::string_view key,\n                 T* value, std::string* error) {\n    if (!json_value.IsUint()) {\n      *error = RapidJsonMismatchedTypeError(key, \"uint\", json_value);\n      return false;\n    }\n    unsigned value_tmp = json_value.GetUint();\n    if (!IsValueInRangeForNumericType<T>(value_tmp)) {\n      *error = RapidJsonOutOfRangeError(key, value_tmp);\n      return false;\n    }\n    *value = static_cast<T>(value_tmp);\n    return true;\n  }\n};\n\ntemplate <typename T>\nclass RapidJsonValueConverter<\n    T, std::enable_if_t<std::is_floating_point<T>::value>> {\n public:\n  template <typename Allocator>\n  static rapidjson::Value From(T value, Allocator& allocator) {\n    return rapidjson::Value(value);\n  }\n\n  static bool To(const rapidjson::Value& json_value, std::string_view key,\n                 T* value, std::string* error) {\n    if (!json_value.IsDouble()) {\n      *error = RapidJsonMismatchedTypeError(key, \"double\", json_value);\n      return false;\n    }\n    double value_tmp = json_value.GetDouble();\n    if (!IsValueInRangeForNumericType<T>(value_tmp)) {\n      *error = RapidJsonOutOfRangeError(key, value_tmp);\n      return false;\n    }\n    *value = static_cast<T>(value_tmp);\n    return true;\n  }\n};\n\ntemplate <>\nclass RapidJsonValueConverter<std::string> {\n public:\n  template <typename Allocator>\n  static rapidjson::Value From(const std::string& value, Allocator& allocator) {\n    return rapidjson::Value(value.c_str(), value.length(), allocator);\n  }\n\n  static bool To(const rapidjson::Value& json_value, std::string_view key,\n                 std::string* value, std::string* error) {\n    if (!json_value.IsString()) {\n      *error = RapidJsonMismatchedTypeError(key, \"string\", json_value);\n      return false;\n    }\n    *value = json_value.GetString();\n    return true;\n  }\n};\n\ntemplate <>\nclass RapidJsonValueConverter<std::string_view> {\n public:\n  template <typename Allocator>\n  static rapidjson::Value From(std::string_view value, Allocator& allocator) {\n    return rapidjson::Value(value.data(), value.length());\n  }\n\n  static bool To(const rapidjson::Value& json_value, std::string_view key,\n                 std::string_view* value, std::string* error) {\n    if (!json_value.IsString()) {\n      *error = RapidJsonMismatchedTypeError(key, \"string_view\", json_value);\n      return false;\n    }\n    *value = json_value.GetString();\n    return true;\n  }\n};\n\ntemplate <typename T>\nclass RapidJsonValueConverter<T, std::enable_if_t<std::is_enum<T>::value>> {\n public:\n  using U = std::underlying_type_t<T>;\n\n  template <typename Allocator>\n  static rapidjson::Value From(T value, Allocator& allocator) {\n    return rapidjson::Value(static_cast<U>(value));\n  }\n\n  static bool To(const rapidjson::Value& json_value, std::string_view key,\n                 T* value, std::string* error) {\n    U value_tmp;\n    if (!RapidJsonValueConverter<U>::To(json_value, key, &value_tmp, error))\n      return false;\n    *value = static_cast<T>(value_tmp);\n    return true;\n  }\n};\n\ntemplate <typename T, size_t N>\nclass RapidJsonValueConverter<std::array<T, N>> {\n public:\n  template <typename Allocator>\n  static rapidjson::Value From(const std::array<T, N>& value,\n                               Allocator& allocator) {\n    rapidjson::Value array(rapidjson::kArrayType);\n    for (size_t i = 0; i < N; ++i) {\n      array.PushBack(RapidJsonValueConverter<T>::From(value[i], allocator),\n                     allocator);\n    }\n    return array;\n  }\n\n  static bool To(const rapidjson::Value& json_value, std::string_view key,\n                 std::array<T, N>* value, std::string* error) {\n    if (!json_value.IsArray()) {\n      *error = RapidJsonMismatchedTypeError(key, \"array\", json_value);\n      return false;\n    }\n    if (N != json_value.Size()) {\n      *error = absl::Substitute(\"The length of json($0) is not $1\",\n                                json_value.Size(), N);\n      return false;\n    }\n    std::array<T, N> value_tmp;\n    for (size_t i = 0; i < N; ++i) {\n      T v;\n      if (!RapidJsonValueConverter<T>::To(json_value[i], key, &v, error))\n        return false;\n      value_tmp[i] = std::move(v);\n    }\n    *value = std::move(value_tmp);\n    return true;\n  }\n};\n\ntemplate <typename T>\nclass RapidJsonValueConverter<std::vector<T>> {\n public:\n  template <typename Allocator>\n  static rapidjson::Value From(const std::vector<T>& value,\n                               Allocator& allocator) {\n    rapidjson::Value array(rapidjson::kArrayType);\n    for (size_t i = 0; i < value.size(); ++i) {\n      array.PushBack(RapidJsonValueConverter<T>::From(value[i], allocator),\n                     allocator);\n    }\n    return array;\n  }\n\n  static bool To(const rapidjson::Value& json_value, std::string_view key,\n                 std::vector<T>* value, std::string* error) {\n    if (!json_value.IsArray()) {\n      *error = RapidJsonMismatchedTypeError(key, \"array\", json_value);\n      return false;\n    }\n    std::vector<T> value_tmp;\n    for (auto it = json_value.Begin(); it != json_value.End(); ++it) {\n      T v;\n      if (!RapidJsonValueConverter<T>::To(*it, key, &v, error)) return false;\n      value_tmp.push_back(std::move(v));\n    }\n    *value = std::move(value_tmp);\n    return true;\n  }\n};\n\ntemplate <typename T>\nclass RapidJsonValueConverter<std::optional<T>> {\n public:\n  template <typename Allocator>\n  static rapidjson::Value From(const std::optional<T>& value,\n                               Allocator& allocator) {\n    if (value.has_value()) {\n      return RapidJsonValueConverter<T>::From(value.value(), allocator);\n    } else {\n      return rapidjson::Value();\n    }\n  }\n\n  static bool To(const rapidjson::Value& json_value, std::string_view key,\n                 std::optional<T>* value, std::string* error) {\n    if (json_value.IsNull()) {\n      *value = std::optional<T>();\n    } else {\n      T value_tmp;\n      if (!RapidJsonValueConverter<T>::To(json_value, key, &value_tmp, error))\n        return false;\n      *value = std::move(value_tmp);\n    }\n    return true;\n  }\n};\n\ntemplate <typename T, typename Allocator>\nvoid AddJsonElement(rapidjson::Value& json_value, std::string_view key,\n                    const T& value, Allocator& allocator) {\n  json_value.AddMember(rapidjson::StringRef(key.data(), key.length()),\n                       RapidJsonValueConverter<T>::From(value, allocator),\n                       allocator);\n}\n\ntemplate <typename T>\nbool ParseJsonElement(const rapidjson::Value& json_value, std::string_view key,\n                      T* value, std::string* error) {\n  auto it = json_value.FindMember(key.data());\n  if (it == json_value.MemberEnd()) {\n    *error = absl::Substitute(\"\\\"$0\\\" key is not found\", key);\n    return false;\n  }\n  return RapidJsonValueConverter<T>::To(it->value, key, value, error);\n}\n\n}  // namespace tachyon::base\n\nnamespace rapidjson {\n\ntemplate <typename OutputStream, typename SourceEncoding,\n          typename TargetEncoding, typename StackAllocator>\nWriter<OutputStream, SourceEncoding, TargetEncoding, StackAllocator>&\noperator<<(Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator>&\n               writer,\n           bool value) {\n  writer.Bool(value);\n  return writer;\n}\n\ntemplate <typename OutputStream, typename SourceEncoding,\n          typename TargetEncoding, typename StackAllocator>\nWriter<OutputStream, SourceEncoding, TargetEncoding, StackAllocator>&\noperator<<(Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator>&\n               writer,\n           int64_t value) {\n  writer.Int64(value);\n  return writer;\n}\n\ntemplate <typename OutputStream, typename SourceEncoding,\n          typename TargetEncoding, typename StackAllocator>\nWriter<OutputStream, SourceEncoding, TargetEncoding, StackAllocator>&\noperator<<(Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator>&\n               writer,\n           uint64_t value) {\n  writer.Uint64(value);\n  return writer;\n}\n\ntemplate <\n    typename OutputStream, typename SourceEncoding, typename TargetEncoding,\n    typename StackAllocator, typename T,\n    std::enable_if_t<std::is_integral<T>::value && std::is_signed<T>::value &&\n                     !std::is_same<T, int64_t>::value>* = nullptr>\nWriter<OutputStream, SourceEncoding, TargetEncoding, StackAllocator>&\noperator<<(Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator>&\n               writer,\n           T value) {\n  writer.Int(value);\n  return writer;\n}\n\ntemplate <\n    typename OutputStream, typename SourceEncoding, typename TargetEncoding,\n    typename StackAllocator, typename T,\n    std::enable_if_t<std::is_integral<T>::value && !std::is_signed<T>::value &&\n                     !std::is_same<T, uint64_t>::value &&\n                     !std::is_same<T, bool>::value>* = nullptr>\nWriter<OutputStream, SourceEncoding, TargetEncoding, StackAllocator>&\noperator<<(Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator>&\n               writer,\n           T value) {\n  writer.Uint(value);\n  return writer;\n}\n\ntemplate <typename OutputStream, typename SourceEncoding,\n          typename TargetEncoding, typename StackAllocator, typename T,\n          std::enable_if_t<std::is_floating_point<T>::value>* = nullptr>\nWriter<OutputStream, SourceEncoding, TargetEncoding, StackAllocator>&\noperator<<(Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator>&\n               writer,\n           T value) {\n  writer.Double(value);\n  return writer;\n}\n\ntemplate <typename OutputStream, typename SourceEncoding,\n          typename TargetEncoding, typename StackAllocator>\nWriter<OutputStream, SourceEncoding, TargetEncoding, StackAllocator>&\noperator<<(Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator>&\n               writer,\n           std::string_view value) {\n  writer.String(value.data());\n  return writer;\n}\n\n}  // namespace rapidjson\n\n#endif  // TACHYON_BASE_JSON_RAPIDJSON_UTIL_H_\n"
  },
  {
    "path": "tachyon/base/json/test/simple_data.json",
    "content": "{\n  \"message\": \"hello world\",\n  \"index\": 1,\n  \"flag\": true,\n  \"data\": [\n    0,\n    2,\n    4\n  ]\n}\n"
  },
  {
    "path": "tachyon/base/logging.cc",
    "content": "#include \"tachyon/base/logging.h\"\n\nnamespace tachyon::base {\n\n// This is never instantiated, it's just used for EAT_STREAM_PARAMETERS to have\n// an object of the correct type on the LHS of the unused part of the ternary\n// operator.\nstd::ostream* g_swallow_stream;\n\nbool ShouldCreateLogMessage(int severity) {\n  // TODO(chokobole): enable this.\n  return true;\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/logging.h",
    "content": "#ifndef TACHYON_BASE_LOGGING_H_\n#define TACHYON_BASE_LOGGING_H_\n\n#include <ostream>\n\n#include \"tachyon/export.h\"\n\n// These are codes to minimize gaps between glog and chromium logging.\n// Followings are modified and taken from chromium/base/logging.\n// - LAZY_STREAM\n// - EAT_STREAM_PARAMETERS\n// - ANALYZER_*\n\n#define GLOG_NO_ABBREVIATED_SEVERITIES\n#include \"glog/logging.h\"\n#include \"glog/raw_logging.h\"\n\nnamespace tachyon::base {\n\nTACHYON_EXPORT extern std::ostream* g_swallow_stream;\n\n// Used by LOG_IS_ON to lazy-evaluate stream arguments.\nTACHYON_EXPORT bool ShouldCreateLogMessage(int severity);\n\n}  // namespace tachyon::base\n\n// Helper macro which avoids evaluating the arguments to a stream if\n// the condition doesn't hold. Condition is evaluated once and only once.\n#define LAZY_STREAM(stream, condition) \\\n  !(condition) ? (void)0 : ::google::LogMessageVoidify() & (stream)\n\n// As special cases, we can assume that LOG_IS_ON(FATAL) always holds. Also,\n// LOG_IS_ON(DFATAL) always holds in debug mode. In particular, CHECK()s will\n// always fire if they fail.\n#define LOG_IS_ON(severity) \\\n  (::tachyon::base::ShouldCreateLogMessage(::google::GLOG_##severity))\n\n// A few definitions of macros that don't generate much code. These are used\n// by LOG() and LOG_IF, etc. Since these are used all over our code, it's\n// better to have compact code for these operations.\n#define COMPACT_GOOGLE_LOG_EX_INFO(ClassName, ...) \\\n  ::google::ClassName(__FILE__, __LINE__, ::google::GLOG_INFO, ##__VA_ARGS__)\n#define COMPACT_GOOGLE_LOG_EX_WARNING(ClassName, ...) \\\n  ::google::ClassName(__FILE__, __LINE__, ::google::GLOG_WARNING, ##__VA_ARGS__)\n#define COMPACT_GOOGLE_LOG_EX_ERROR(ClassName, ...) \\\n  ::google::ClassName(__FILE__, __LINE__, ::google::GLOG_ERROR, ##__VA_ARGS__)\n#define COMPACT_GOOGLE_LOG_EX_FATAL(ClassName, ...) \\\n  ::google::ClassName(__FILE__, __LINE__, ::google::GLOG_FATAL, ##__VA_ARGS__)\n#define COMPACT_GOOGLE_LOG_EX_DFATAL(ClassName, ...) \\\n  ::google::ClassName(__FILE__, __LINE__, ::google::GLOG_DFATAL, ##__VA_ARGS__)\n#define COMPACT_GOOGLE_LOG_EX_DCHECK(ClassName, ...) \\\n  ::google::ClassName(__FILE__, __LINE__, ::google::GLOG_DCHECK, ##__VA_ARGS__)\n\n// Note that g_swallow_stream is used instead of an arbitrary LOG() stream to\n// avoid the creation of an object with a non-trivial destructor (LogMessage).\n// On MSVC x86 (checked on 2015 Update 3), this causes a few additional\n// pointless instructions to be emitted even at full optimization level, even\n// though the : arm of the ternary operator is clearly never executed. Using a\n// simpler object to be &'d with Voidify() avoids these extra instructions.\n// Using a simpler POD object with a templated operator<< also works to avoid\n// these instructions. However, this causes warnings on statically defined\n// implementations of operator<<(std::ostream, ...) in some .cc files, because\n// they become defined-but-unreferenced functions. A reinterpret_cast of 0 to an\n// ostream* also is not suitable, because some compilers warn of undefined\n// behavior.\n#define EAT_STREAM_PARAMETERS \\\n  true ? (void)0              \\\n       : ::google::LogMessageVoidify() & (*::tachyon::base::g_swallow_stream)\n\n#define NOTREACHED() CHECK(false)\n\n#define VPLOG(verboselevel) PLOG_IF(INFO, VLOG_IS_ON(verboselevel))\n\n#define VPLOG_IF(verboselevel, condition) \\\n  PLOG_IF(INFO, VLOG_IS_ON(verboselevel) && (condition))\n\n#define DPLOG(severity) LAZY_STREAM(PLOG(severity), DCHECK_IS_ON())\n#define DVPLOG(verboselevel) \\\n  LAZY_STREAM(PLOG(INFO), DCHECK_IS_ON() && VLOG_IS_ON(verboselevel))\n\n#if DCHECK_IS_ON()\n\n#define DLOG_IS_ON(severity) LOG_IS_ON(severity)\n#define DPLOG_IF(severity, condition) PLOG_IF(severity, condition)\n#define DVLOG_IF(verboselevel, condition) VLOG_IF(verboselevel, condition)\n#define DVPLOG_IF(verboselevel, condition) VPLOG_IF(verboselevel, condition)\n\n#define DPCHECK(condition) PCHECK(condition)\n\n#else  // !DCHECK_IS_ON()\n\n// If !DCHECK_IS_ON(), we want to avoid emitting any references to |condition|\n// (which may reference a variable defined only if DCHECK_IS_ON()).\n// Contrast this with DCHECK et al., which has different behavior.\n\n#define DLOG_IS_ON(severity) false\n#define DPLOG_IF(severity, condition) EAT_STREAM_PARAMETERS\n#define DVLOG_IF(verboselevel, condition) EAT_STREAM_PARAMETERS\n#define DVPLOG_IF(verboselevel, condition) EAT_STREAM_PARAMETERS\n\n#define DPCHECK(condition) EAT_STREAM_PARAMETERS\n\n#endif\n\n#if defined(COMPILER_GCC)\n// On Linux, with GCC, we can use __PRETTY_FUNCTION__ to get the demangled name\n// of the current function in the NOTIMPLEMENTED message.\n#define NOTIMPLEMENTED_MSG \"Not implemented reached in \" << __PRETTY_FUNCTION__\n#else\n#define NOTIMPLEMENTED_MSG \"NOT IMPLEMENTED\"\n#endif\n\n#define NOTIMPLEMENTED() DLOG(ERROR) << NOTIMPLEMENTED_MSG\n#define NOTIMPLEMENTED_LOG_ONCE()                      \\\n  do {                                                 \\\n    static bool logged_once = false;                   \\\n    LOG_IF(ERROR, !logged_once) << NOTIMPLEMENTED_MSG; \\\n    logged_once = true;                                \\\n  } while (0);                                         \\\n  EAT_STREAM_PARAMETERS\n\n#if TACHYON_CUDA\n#define LOG_IF_NOT_GPU(severity) EAT_STREAM_PARAMETERS\n#else\n#define LOG_IF_NOT_GPU(severity) LOG(severity)\n#endif\n\n#endif  // TACHYON_BASE_LOGGING_H_\n"
  },
  {
    "path": "tachyon/base/mac/.clang-format",
    "content": "DisableFormat: true\nSortIncludes: true\n"
  },
  {
    "path": "tachyon/base/mac/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_objc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_objc_library(\n    name = \"foundation_util\",\n    srcs = [\"foundation_util.mm\"],\n    hdrs = [\"foundation_util.h\"],\n    deps = [\n        \":mac_logging\",\n        \":scoped_cftyperef\",\n        \"//tachyon/base/apple:bundle_locations\",\n        \"//tachyon/base/containers:adapters\",\n        \"//tachyon/base/numerics:checked_math\",\n        \"//tachyon/base/numerics:safe_conversions\",\n        \"//tachyon/base/strings:string_util\",\n        \"//tachyon/base/strings:sys_string_conversions\",\n    ],\n)\n\ntachyon_objc_library(\n    name = \"mac_logging\",\n    srcs = [\"mac_logging.mm\"],\n    hdrs = [\"mac_logging.h\"],\n    deps = [\n        \"//tachyon/base:logging\",\n        \"//tachyon/build:build_config\",\n    ],\n)\n\ntachyon_objc_library(\n    name = \"mach_logging\",\n    srcs = [\"mach_logging.cc\"],\n    hdrs = [\"mach_logging.h\"],\n    deps = [\n        \"//tachyon/base:logging\",\n        \"//tachyon/build:build_config\",\n        \"@com_google_absl//absl/strings:str_format\",\n    ],\n)\n\ntachyon_objc_library(\n    name = \"scoped_cftyperef\",\n    hdrs = [\"scoped_cftyperef.h\"],\n    deps = [\":scoped_typeref\"],\n)\n\ntachyon_objc_library(\n    name = \"scoped_mach_port\",\n    srcs = [\"scoped_mach_port.cc\"],\n    hdrs = [\"scoped_mach_port.h\"],\n    deps = [\n        \":mach_logging\",\n        \"//tachyon/base:scoped_generic\",\n    ],\n)\n\ntachyon_objc_library(\n    name = \"scoped_typeref\",\n    hdrs = [\"scoped_typeref.h\"],\n    deps = [\n        \"//tachyon/base:logging\",\n        \"//tachyon/base/memory:scoped_policy\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/base/mac/foundation_util.h",
    "content": "// Copyright 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_MAC_FOUNDATION_UTIL_H_\n#define TACHYON_BASE_MAC_FOUNDATION_UTIL_H_\n\n#include <AvailabilityMacros.h>\n#include <CoreFoundation/CoreFoundation.h>\n#include <CoreText/CoreText.h>\n#include <Security/Security.h>\n\n#include <string>\n\n#include \"tachyon/export.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/mac/scoped_cftyperef.h\"\n#include \"tachyon/build/build_config.h\"\n\n#if defined(__OBJC__)\n#import <Foundation/Foundation.h>\n@class NSFont;\n@class UIFont;\n#endif  // __OBJC__\n\nnamespace tachyon::base {\nclass FilePath;\n}\n\nnamespace tachyon::base::mac {\n\n// Returns true if the application is running from a bundle\nTACHYON_EXPORT bool AmIBundled();\nTACHYON_EXPORT void SetOverrideAmIBundled(bool value);\n\n#if defined(UNIT_TEST)\n// This is required because instantiating some tests requires checking the\n// directory structure, which sets the AmIBundled cache state. Individual tests\n// may or may not be bundled, and this would trip them up if the cache weren't\n// cleared. This should not be called from individual tests, just from test\n// instantiation code that gets a path from PathService.\nTACHYON_EXPORT void ClearAmIBundledCache();\n#endif\n\n// Returns true if this process is marked as a \"Background only process\".\nTACHYON_EXPORT bool IsBackgroundOnlyProcess();\n\n// Returns the path to a resource within the framework bundle.\nTACHYON_EXPORT FilePath PathForFrameworkBundleResource(const char* resource_name);\n\n// Returns the creator code associated with the CFBundleRef at bundle.\nOSType CreatorCodeForCFBundleRef(CFBundleRef bundle);\n\n// Returns the creator code associated with this application, by calling\n// CreatorCodeForCFBundleRef for the application's main bundle.  If this\n// information cannot be determined, returns kUnknownType ('????').  This\n// does not respect the override app bundle because it's based on CFBundle\n// instead of NSBundle, and because callers probably don't want the override\n// app bundle's creator code anyway.\nTACHYON_EXPORT OSType CreatorCodeForApplication();\n\n#if defined(__OBJC__)\n\n// Searches for directories for the given key in only the given |domain_mask|.\n// If found, fills result (which must always be non-NULL) with the\n// first found directory and returns true.  Otherwise, returns false.\nTACHYON_EXPORT bool GetSearchPathDirectory(NSSearchPathDirectory directory,\n                                        NSSearchPathDomainMask domain_mask,\n                                        FilePath* result);\n\n// Searches for directories for the given key in only the local domain.\n// If found, fills result (which must always be non-NULL) with the\n// first found directory and returns true.  Otherwise, returns false.\nTACHYON_EXPORT bool GetLocalDirectory(NSSearchPathDirectory directory,\n                                   FilePath* result);\n\n// Searches for directories for the given key in only the user domain.\n// If found, fills result (which must always be non-NULL) with the\n// first found directory and returns true.  Otherwise, returns false.\nTACHYON_EXPORT bool GetUserDirectory(NSSearchPathDirectory directory,\n                                  FilePath* result);\n\n#endif  // __OBJC__\n\n// Returns the ~/Library directory.\nTACHYON_EXPORT FilePath GetUserLibraryPath();\n\n// Returns the ~/Documents directory.\nTACHYON_EXPORT FilePath GetUserDocumentPath();\n\n// Takes a path to an (executable) binary and tries to provide the path to an\n// application bundle containing it. It takes the outermost bundle that it can\n// find (so for \"/Foo/Bar.app/.../Baz.app/...\" it produces \"/Foo/Bar.app\").\n//   |exec_name| - path to the binary\n//   returns - path to the application bundle, or empty on error\nTACHYON_EXPORT FilePath GetAppBundlePath(const FilePath& exec_name);\n\n// Takes a path to an (executable) binary and tries to provide the path to an\n// application bundle containing it. It takes the innermost bundle that it can\n// find (so for \"/Foo/Bar.app/.../Baz.app/...\" it produces\n// \"/Foo/Bar.app/.../Baz.app\").\n//   |exec_name| - path to the binary\n//   returns - path to the application bundle, or empty on error\nTACHYON_EXPORT FilePath GetInnermostAppBundlePath(const FilePath& exec_name);\n\n#define TYPE_NAME_FOR_CF_TYPE_DECL(TypeCF) \\\n  TACHYON_EXPORT std::string TypeNameForCFType(TypeCF##Ref)\n\nTYPE_NAME_FOR_CF_TYPE_DECL(CFArray);\nTYPE_NAME_FOR_CF_TYPE_DECL(CFBag);\nTYPE_NAME_FOR_CF_TYPE_DECL(CFBoolean);\nTYPE_NAME_FOR_CF_TYPE_DECL(CFData);\nTYPE_NAME_FOR_CF_TYPE_DECL(CFDate);\nTYPE_NAME_FOR_CF_TYPE_DECL(CFDictionary);\nTYPE_NAME_FOR_CF_TYPE_DECL(CFNull);\nTYPE_NAME_FOR_CF_TYPE_DECL(CFNumber);\nTYPE_NAME_FOR_CF_TYPE_DECL(CFSet);\nTYPE_NAME_FOR_CF_TYPE_DECL(CFString);\nTYPE_NAME_FOR_CF_TYPE_DECL(CFURL);\nTYPE_NAME_FOR_CF_TYPE_DECL(CFUUID);\n\n/*\nTODO(chokobole):\nTYPE_NAME_FOR_CF_TYPE_DECL(CGColor);\n\nTYPE_NAME_FOR_CF_TYPE_DECL(CTFont);\nTYPE_NAME_FOR_CF_TYPE_DECL(CTRun);\n\nTYPE_NAME_FOR_CF_TYPE_DECL(SecAccessControl);\nTYPE_NAME_FOR_CF_TYPE_DECL(SecCertificate);\nTYPE_NAME_FOR_CF_TYPE_DECL(SecKey);\nTYPE_NAME_FOR_CF_TYPE_DECL(SecPolicy);\n*/\n#undef TYPE_NAME_FOR_CF_TYPE_DECL\n\n// Returns the base bundle ID, which can be set by SetBaseBundleID but\n// defaults to a reasonable string. This never returns NULL. BaseBundleID\n// returns a pointer to static storage that must not be freed.\nTACHYON_EXPORT const char* BaseBundleID();\n\n// Sets the base bundle ID to override the default. The implementation will\n// make its own copy of new_base_bundle_id.\nTACHYON_EXPORT void SetBaseBundleID(const char* new_base_bundle_id);\n\n// CFCast<>() and CFCastStrict<>() cast a basic CFTypeRef to a more\n// specific CoreFoundation type. The compatibility of the passed\n// object is found by comparing its opaque type against the\n// requested type identifier. If the supplied object is not\n// compatible with the requested return type, CFCast<>() returns\n// NULL and CFCastStrict<>() will DCHECK. Providing a NULL pointer\n// to either variant results in NULL being returned without\n// triggering any DCHECK.\n//\n// Example usage:\n// CFNumberRef some_number = base::mac::CFCast<CFNumberRef>(\n//     CFArrayGetValueAtIndex(array, index));\n//\n// CFTypeRef hello = CFSTR(\"hello world\");\n// CFStringRef some_string = base::mac::CFCastStrict<CFStringRef>(hello);\n\ntemplate<typename T>\nT CFCast(const CFTypeRef& cf_val);\n\ntemplate<typename T>\nT CFCastStrict(const CFTypeRef& cf_val);\n\n#define CF_CAST_DECL(TypeCF)                                            \\\n  template <>                                                           \\\n  TACHYON_EXPORT TypeCF##Ref CFCast<TypeCF##Ref>(const CFTypeRef& cf_val); \\\n                                                                        \\\n  template <>                                                           \\\n  TACHYON_EXPORT TypeCF##Ref CFCastStrict<TypeCF##Ref>(const CFTypeRef& cf_val)\n\nCF_CAST_DECL(CFArray);\nCF_CAST_DECL(CFBag);\nCF_CAST_DECL(CFBoolean);\nCF_CAST_DECL(CFData);\nCF_CAST_DECL(CFDate);\nCF_CAST_DECL(CFDictionary);\nCF_CAST_DECL(CFNull);\nCF_CAST_DECL(CFNumber);\nCF_CAST_DECL(CFSet);\nCF_CAST_DECL(CFString);\nCF_CAST_DECL(CFURL);\nCF_CAST_DECL(CFUUID);\n\n/*\nTODO(chokobole):\nCF_CAST_DECL(CGColor);\n\nCF_CAST_DECL(CTFont);\nCF_CAST_DECL(CTFontDescriptor);\nCF_CAST_DECL(CTRun);\n\nCF_CAST_DECL(SecAccessControl);\nCF_CAST_DECL(SecCertificate);\nCF_CAST_DECL(SecKey);\nCF_CAST_DECL(SecPolicy);\n*/\n#undef CF_CAST_DECL\n\n#if defined(__OBJC__)\n\n// ObjCCast<>() and ObjCCastStrict<>() cast a basic id to a more\n// specific (NSObject-derived) type. The compatibility of the passed\n// object is found by checking if it's a kind of the requested type\n// identifier. If the supplied object is not compatible with the\n// requested return type, ObjCCast<>() returns nil and\n// ObjCCastStrict<>() will DCHECK. Providing a nil pointer to either\n// variant results in nil being returned without triggering any DCHECK.\n//\n// The strict variant is useful when retrieving a value from a\n// collection which only has values of a specific type, e.g. an\n// NSArray of NSStrings. The non-strict variant is useful when\n// retrieving values from data that you can't fully control. For\n// example, a plist read from disk may be beyond your exclusive\n// control, so you'd only want to check that the values you retrieve\n// from it are of the expected types, but not crash if they're not.\n//\n// Example usage:\n// NSString* version = base::mac::ObjCCast<NSString>(\n//     [bundle objectForInfoDictionaryKey:@\"CFBundleShortVersionString\"]);\n//\n// NSString* str = base::mac::ObjCCastStrict<NSString>(\n//     [ns_arr_of_ns_strs objectAtIndex:0]);\ntemplate<typename T>\nT* ObjCCast(id objc_val) {\n  if ([objc_val isKindOfClass:[T class]]) {\n    return reinterpret_cast<T*>(objc_val);\n  }\n  return nil;\n}\n\ntemplate<typename T>\nT* ObjCCastStrict(id objc_val) {\n  T* rv = ObjCCast<T>(objc_val);\n  DCHECK(objc_val == nil || rv);\n  return rv;\n}\n\n#endif  // defined(__OBJC__)\n\n// Helper function for GetValueFromDictionary to create the error message\n// that appears when a type mismatch is encountered.\nTACHYON_EXPORT std::string GetValueFromDictionaryErrorMessage(\n    CFStringRef key, const std::string& expected_type, CFTypeRef value);\n\n// Utility function to pull out a value from a dictionary, check its type, and\n// return it. Returns NULL if the key is not present or of the wrong type.\ntemplate<typename T>\nT GetValueFromDictionary(CFDictionaryRef dict, CFStringRef key) {\n  CFTypeRef value = CFDictionaryGetValue(dict, key);\n  T value_specific = CFCast<T>(value);\n\n  if (value && !value_specific) {\n    std::string expected_type = TypeNameForCFType(value_specific);\n    DLOG(WARNING) << GetValueFromDictionaryErrorMessage(key,\n                                                        expected_type,\n                                                        value);\n  }\n\n  return value_specific;\n}\n\n#if defined(__OBJC__)\n\n// Converts |path| to an autoreleased NSURL. Returns nil if |path| is empty.\nTACHYON_EXPORT NSURL* FilePathToNSURL(const FilePath& path);\n\n// Converts |path| to an autoreleased NSString. Returns nil if |path| is empty.\nTACHYON_EXPORT NSString* FilePathToNSString(const FilePath& path);\n\n// Converts |str| to a FilePath. Returns an empty path if |str| is nil.\nTACHYON_EXPORT FilePath NSStringToFilePath(NSString* str);\n\n// Converts |url| to a FilePath. Returns an empty path if |url| is nil or if\n// |url| is not of scheme \"file\".\nTACHYON_EXPORT FilePath NSURLToFilePath(NSURL* url);\n\n#endif  // __OBJC__\n\n// Converts a non-null |path| to a CFURLRef. |path| must not be empty.\n//\n// This function only uses manually-owned resources, so it does not depend on an\n// NSAutoreleasePool being set up on the current thread.\nTACHYON_EXPORT ScopedCFTypeRef<CFURLRef> FilePathToCFURL(const FilePath& path);\n\n#if defined(__OBJC__)\n// Converts |range| to an NSRange, returning the new range in |range_out|.\n// Returns true if conversion was successful, false if the values of |range|\n// could not be converted to NSUIntegers.\n[[nodiscard]] TACHYON_EXPORT bool CFRangeToNSRange(CFRange range,\n                                                   NSRange* range_out);\n#endif  // defined(__OBJC__)\n\n}  // namespace tachyon::base::mac\n\n// Stream operations for CFTypes. They can be used with Objective-C types as\n// well by using the casting methods in base/apple/bridging.h.\n//\n// For example: LOG(INFO) << base::apple::NSToCFPtrCast(@\"foo\");\n//\n// operator<<() can not be overloaded for Objective-C types as the compiler\n// cannot distinguish between overloads for id with overloads for void*.\nTACHYON_EXPORT extern std::ostream& operator<<(std::ostream& o,\n                                               const CFErrorRef err);\nTACHYON_EXPORT extern std::ostream& operator<<(std::ostream& o,\n                                               const CFStringRef str);\nTACHYON_EXPORT extern std::ostream& operator<<(std::ostream& o, CFRange);\n\n#if defined(__OBJC__)\nTACHYON_EXPORT extern std::ostream& operator<<(std::ostream& o, id);\nTACHYON_EXPORT extern std::ostream& operator<<(std::ostream& o, NSRange);\nTACHYON_EXPORT extern std::ostream& operator<<(std::ostream& o, SEL);\n\n#if BUILDFLAG(IS_MAC)\nTACHYON_EXPORT extern std::ostream& operator<<(std::ostream& o, NSPoint);\nTACHYON_EXPORT extern std::ostream& operator<<(std::ostream& o, NSRect);\nTACHYON_EXPORT extern std::ostream& operator<<(std::ostream& o, NSSize);\n#endif  // IS_MAC\n\n#endif  // __OBJC__\n\n#endif  // TACHYON_BASE_MAC_FOUNDATION_UTIL_H_\n"
  },
  {
    "path": "tachyon/base/mac/foundation_util.mm",
    "content": "// Copyright 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/mac/foundation_util.h\"\n\n#include <stddef.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include <vector>\n\n#include \"tachyon/base/apple/bundle_locations.h\"\n#include \"tachyon/base/containers/adapters.h\"\n#include \"tachyon/base/files/file_path.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/mac/mac_logging.h\"\n#include \"tachyon/base/numerics/checked_math.h\"\n#include \"tachyon/base/numerics/safe_conversions.h\"\n#include \"tachyon/base/ranges/algorithm.h\"\n#include \"tachyon/base/strings/string_util.h\"\n#include \"tachyon/base/strings/sys_string_conversions.h\"\n// #include \"tachyon/build/branding_buildflags.h\"\n#include \"tachyon/build/build_config.h\"\n\n#if !BUILDFLAG(IS_IOS)\n#import <AppKit/AppKit.h>\n#endif\n\n#if !defined(__has_feature) || !__has_feature(objc_arc)\n#error \"This file requires ARC support.\"\n#endif\n\nextern \"C\" {\nCFTypeID SecKeyGetTypeID();\n}  // extern \"C\"\n\nnamespace tachyon::base::apple {\n\nnamespace {\n\nNSBundle* g_override_framework_bundle = nil;\nNSBundle* g_override_outer_bundle = nil;\n\nNSBundle* BundleFromPath(const FilePath& file_path) {\n  if (file_path.empty()) {\n    return nil;\n  }\n\n  NSBundle* bundle = [NSBundle bundleWithURL:mac::FilePathToNSURL(file_path)];\n  CHECK(bundle) << \"Failed to load the bundle at \" << file_path.value();\n\n  return bundle;\n}\n\n}  // namespace\n\nFilePath MainBundlePath() {\n  return mac::NSStringToFilePath(MainBundle().bundlePath);\n}\n\nNSBundle* OuterBundle() {\n  if (g_override_outer_bundle) {\n    return g_override_outer_bundle;\n  }\n  return NSBundle.mainBundle;\n}\n\nFilePath OuterBundlePath() {\n  return mac::NSStringToFilePath(OuterBundle().bundlePath);\n}\n\nNSBundle* FrameworkBundle() {\n  if (g_override_framework_bundle) {\n    return g_override_framework_bundle;\n  }\n  return NSBundle.mainBundle;\n}\n\nFilePath FrameworkBundlePath() {\n  return mac::NSStringToFilePath(FrameworkBundle().bundlePath);\n}\n\nvoid SetOverrideOuterBundle(NSBundle* bundle) {\n  g_override_outer_bundle = bundle;\n}\n\nvoid SetOverrideFrameworkBundle(NSBundle* bundle) {\n  g_override_framework_bundle = bundle;\n}\n\nvoid SetOverrideOuterBundlePath(const FilePath& file_path) {\n  NSBundle* bundle = BundleFromPath(file_path);\n  g_override_outer_bundle = bundle;\n}\n\nvoid SetOverrideFrameworkBundlePath(const FilePath& file_path) {\n  NSBundle* bundle = BundleFromPath(file_path);\n  g_override_framework_bundle = bundle;\n}\n\n}\n\nnamespace tachyon::base::mac {\n\nnamespace {\n\nbool g_cached_am_i_bundled_called = false;\nbool g_cached_am_i_bundled_value = false;\nbool g_override_am_i_bundled = false;\nbool g_override_am_i_bundled_value = false;\n\nbool UncachedAmIBundled() {\n#if BUILDFLAG(IS_IOS)\n  // All apps are bundled on iOS.\n  return true;\n#else\n  if (g_override_am_i_bundled)\n    return g_override_am_i_bundled_value;\n\n  // Yes, this is cheap.\n  return [apple::OuterBundle().bundlePath hasSuffix:@\".app\"];\n#endif\n}\n\n}  // namespace\n\nbool AmIBundled() {\n  // If the return value is not cached, this function will return different\n  // values depending on when it's called. This confuses some client code, see\n  // http://crbug.com/63183 .\n  if (!g_cached_am_i_bundled_called) {\n    g_cached_am_i_bundled_called = true;\n    g_cached_am_i_bundled_value = UncachedAmIBundled();\n  }\n  DCHECK_EQ(g_cached_am_i_bundled_value, UncachedAmIBundled())\n      << \"The return value of AmIBundled() changed. This will confuse tests. \"\n      << \"Call SetAmIBundled() override manually if your test binary \"\n      << \"delay-loads the framework.\";\n  return g_cached_am_i_bundled_value;\n}\n\nvoid SetOverrideAmIBundled(bool value) {\n#if BUILDFLAG(IS_IOS)\n  // It doesn't make sense not to be bundled on iOS.\n  if (!value)\n    NOTREACHED();\n#endif\n  g_override_am_i_bundled = true;\n  g_override_am_i_bundled_value = value;\n}\n\nvoid ClearAmIBundledCache() {\n  g_cached_am_i_bundled_called = false;\n}\n\nbool IsBackgroundOnlyProcess() {\n  // This function really does want to examine NSBundle's idea of the main\n  // bundle dictionary.  It needs to look at the actual running .app's\n  // Info.plist to access its LSUIElement property.\n  @autoreleasepool {\n    NSDictionary* info_dictionary = [apple::MainBundle() infoDictionary];\n    return [info_dictionary[@\"LSUIElement\"] boolValue] != NO;\n  }\n}\n\nFilePath PathForFrameworkBundleResource(const char* resource_name) {\n  NSBundle* bundle = apple::FrameworkBundle();\n  NSURL* resource_url = [bundle URLForResource:@(resource_name)\n                                 withExtension:nil];\n  return NSURLToFilePath(resource_url);\n}\n\nOSType CreatorCodeForCFBundleRef(CFBundleRef bundle) {\n  OSType creator = kUnknownType;\n  CFBundleGetPackageInfo(bundle, /*packageType=*/nullptr, &creator);\n  return creator;\n}\n\nOSType CreatorCodeForApplication() {\n  CFBundleRef bundle = CFBundleGetMainBundle();\n  if (!bundle)\n    return kUnknownType;\n\n  return CreatorCodeForCFBundleRef(bundle);\n}\n\nbool GetSearchPathDirectory(NSSearchPathDirectory directory,\n                            NSSearchPathDomainMask domain_mask,\n                            FilePath* result) {\n  DCHECK(result);\n  NSArray<NSString*>* dirs =\n      NSSearchPathForDirectoriesInDomains(directory, domain_mask, YES);\n  if (dirs.count < 1) {\n    return false;\n  }\n  *result = NSStringToFilePath(dirs[0]);\n  return true;\n}\n\nbool GetLocalDirectory(NSSearchPathDirectory directory, FilePath* result) {\n  return GetSearchPathDirectory(directory, NSLocalDomainMask, result);\n}\n\nbool GetUserDirectory(NSSearchPathDirectory directory, FilePath* result) {\n  return GetSearchPathDirectory(directory, NSUserDomainMask, result);\n}\n\nFilePath GetUserLibraryPath() {\n  FilePath user_library_path;\n  if (!GetUserDirectory(NSLibraryDirectory, &user_library_path)) {\n    DLOG(WARNING) << \"Could not get user library path\";\n  }\n  return user_library_path;\n}\n\nFilePath GetUserDocumentPath() {\n  FilePath user_document_path;\n  if (!GetUserDirectory(NSDocumentDirectory, &user_document_path)) {\n    DLOG(WARNING) << \"Could not get user document path\";\n  }\n  return user_document_path;\n}\n\n// Takes a path to an (executable) binary and tries to provide the path to an\n// application bundle containing it. It takes the outermost bundle that it can\n// find (so for \"/Foo/Bar.app/.../Baz.app/...\" it produces \"/Foo/Bar.app\").\n//   |exec_name| - path to the binary\n//   returns - path to the application bundle, or empty on error\nFilePath GetAppBundlePath(const FilePath& exec_name) {\n  const char kExt[] = \".app\";\n  const size_t kExtLength = std::size(kExt) - 1;\n\n  // Split the path into components.\n  std::vector<std::string> components = exec_name.GetComponents();\n\n  // It's an error if we don't get any components.\n  if (components.empty())\n    return FilePath();\n\n  // Don't prepend '/' to the first component.\n  std::vector<std::string>::const_iterator it = components.begin();\n  std::string bundle_name = *it;\n  DCHECK_GT(it->length(), 0U);\n  // If the first component ends in \".app\", we're already done.\n  if (it->length() > kExtLength &&\n      !it->compare(it->length() - kExtLength, kExtLength, kExt, kExtLength))\n    return FilePath(bundle_name);\n\n  // The first component may be \"/\" or \"//\", etc. Only append '/' if it doesn't\n  // already end in '/'.\n  if (bundle_name.back() != '/')\n    bundle_name += '/';\n\n  // Go through the remaining components.\n  for (++it; it != components.end(); ++it) {\n    DCHECK_GT(it->length(), 0U);\n\n    bundle_name += *it;\n\n    // If the current component ends in \".app\", we're done.\n    if (it->length() > kExtLength &&\n        !it->compare(it->length() - kExtLength, kExtLength, kExt, kExtLength))\n      return FilePath(bundle_name);\n\n    // Separate this component from the next one.\n    bundle_name += '/';\n  }\n\n  return FilePath();\n}\n\n// Takes a path to an (executable) binary and tries to provide the path to an\n// application bundle containing it. It takes the innermost bundle that it can\n// find (so for \"/Foo/Bar.app/.../Baz.app/...\" it produces\n// \"/Foo/Bar.app/.../Baz.app\").\n//   |exec_name| - path to the binary\n//   returns - path to the application bundle, or empty on error\nFilePath GetInnermostAppBundlePath(const FilePath& exec_name) {\n  static constexpr char kExt[] = \".app\";\n  static constexpr size_t kExtLength = std::size(kExt) - 1;\n\n  // Split the path into components.\n  std::vector<std::string> components = exec_name.GetComponents();\n\n  // It's an error if we don't get any components.\n  if (components.empty()) {\n    return FilePath();\n  }\n\n  auto app = ranges::find_if(\n      Reversed(components), [](const std::string& component) -> bool {\n        return component.size() > kExtLength && EndsWith(component, kExt);\n      });\n\n  if (app == components.rend()) {\n    return FilePath();\n  }\n\n  // Remove all path components after the final \".app\" extension.\n  components.erase(app.base(), components.end());\n\n  std::string bundle_path;\n  for (const std::string& component : components) {\n    // Don't prepend a slash if this is the first component or if the\n    // previous component ended with a slash, which can happen when dealing\n    // with an absolute path.\n    if (!bundle_path.empty() && bundle_path.back() != '/') {\n      bundle_path += '/';\n    }\n\n    bundle_path += component;\n  }\n\n  return FilePath(bundle_path);\n}\n\n#define TYPE_NAME_FOR_CF_TYPE_DEFN(TypeCF) \\\nstd::string TypeNameForCFType(TypeCF##Ref) { \\\n  return #TypeCF; \\\n}\n\nTYPE_NAME_FOR_CF_TYPE_DEFN(CFArray)\nTYPE_NAME_FOR_CF_TYPE_DEFN(CFBag)\nTYPE_NAME_FOR_CF_TYPE_DEFN(CFBoolean)\nTYPE_NAME_FOR_CF_TYPE_DEFN(CFData)\nTYPE_NAME_FOR_CF_TYPE_DEFN(CFDate)\nTYPE_NAME_FOR_CF_TYPE_DEFN(CFDictionary)\nTYPE_NAME_FOR_CF_TYPE_DEFN(CFNull)\nTYPE_NAME_FOR_CF_TYPE_DEFN(CFNumber)\nTYPE_NAME_FOR_CF_TYPE_DEFN(CFSet)\nTYPE_NAME_FOR_CF_TYPE_DEFN(CFString)\nTYPE_NAME_FOR_CF_TYPE_DEFN(CFURL)\nTYPE_NAME_FOR_CF_TYPE_DEFN(CFUUID)\n\n/*\nTODO(chokobole):\nTYPE_NAME_FOR_CF_TYPE_DEFN(CGColor)\n\nTYPE_NAME_FOR_CF_TYPE_DEFN(CTFont)\nTYPE_NAME_FOR_CF_TYPE_DEFN(CTRun)\n\n#if !BUILDFLAG(IS_IOS)\nTYPE_NAME_FOR_CF_TYPE_DEFN(SecAccessControl)\nTYPE_NAME_FOR_CF_TYPE_DEFN(SecCertificate)\nTYPE_NAME_FOR_CF_TYPE_DEFN(SecKey)\nTYPE_NAME_FOR_CF_TYPE_DEFN(SecPolicy)\n#endif\n*/\n#undef TYPE_NAME_FOR_CF_TYPE_DEFN\n\nstatic const char* base_bundle_id;\n\nconst char* BaseBundleID() {\n  if (base_bundle_id) {\n    return base_bundle_id;\n  }\n\n// #if BUILDFLAG(GOOGLE_CHROME_BRANDING)\n  // return \"com.google.Chrome\";\n// #else\n  return \"org.kroma.Tachyon\";\n// #endif\n}\n\nvoid SetBaseBundleID(const char* new_base_bundle_id) {\n  if (new_base_bundle_id != base_bundle_id) {\n    free((void*)base_bundle_id);\n    base_bundle_id = new_base_bundle_id ? strdup(new_base_bundle_id) : nullptr;\n  }\n}\n\n#define CF_CAST_DEFN(TypeCF) \\\ntemplate<> TypeCF##Ref \\\nCFCast<TypeCF##Ref>(const CFTypeRef& cf_val) { \\\n  if (cf_val == NULL) { \\\n    return NULL; \\\n  } \\\n  if (CFGetTypeID(cf_val) == TypeCF##GetTypeID()) { \\\n    return (TypeCF##Ref)(cf_val); \\\n  } \\\n  return NULL; \\\n} \\\n\\\ntemplate<> TypeCF##Ref \\\nCFCastStrict<TypeCF##Ref>(const CFTypeRef& cf_val) { \\\n  TypeCF##Ref rv = CFCast<TypeCF##Ref>(cf_val); \\\n  DCHECK(cf_val == NULL || rv); \\\n  return rv; \\\n}\n\nCF_CAST_DEFN(CFArray)\nCF_CAST_DEFN(CFBag)\nCF_CAST_DEFN(CFBoolean)\nCF_CAST_DEFN(CFData)\nCF_CAST_DEFN(CFDate)\nCF_CAST_DEFN(CFDictionary)\nCF_CAST_DEFN(CFNull)\nCF_CAST_DEFN(CFNumber)\nCF_CAST_DEFN(CFSet)\nCF_CAST_DEFN(CFString)\nCF_CAST_DEFN(CFURL)\nCF_CAST_DEFN(CFUUID)\n\n/*\nTODO(chokobole):\nCF_CAST_DEFN(CGColor)\n\nCF_CAST_DEFN(CTFont)\nCF_CAST_DEFN(CTFontDescriptor)\nCF_CAST_DEFN(CTRun)\n\nCF_CAST_DEFN(SecCertificate)\n\n#if !BUILDFLAG(IS_IOS)\nCF_CAST_DEFN(SecAccessControl)\nCF_CAST_DEFN(SecKey)\nCF_CAST_DEFN(SecPolicy)\n#endif\n*/\n\n#undef CF_CAST_DEFN\n\nstd::string GetValueFromDictionaryErrorMessage(\n    CFStringRef key, const std::string& expected_type, CFTypeRef value) {\n  ScopedCFTypeRef<CFStringRef> actual_type_ref(\n      CFCopyTypeIDDescription(CFGetTypeID(value)));\n  return \"Expected value for key \" + SysCFStringRefToUTF8(key) + \" to be \" +\n         expected_type + \" but it was \" +\n         SysCFStringRefToUTF8(actual_type_ref) + \" instead\";\n}\n\nNSURL* FilePathToNSURL(const FilePath& path) {\n  if (NSString* path_string = FilePathToNSString(path))\n    return [NSURL fileURLWithPath:path_string];\n  return nil;\n}\n\nNSString* FilePathToNSString(const FilePath& path) {\n  if (path.empty())\n    return nil;\n  return @(path.value().c_str());  // @() does UTF8 conversion.\n}\n\nFilePath NSStringToFilePath(NSString* str) {\n  if (!str.length) {\n    return FilePath();\n  }\n  return FilePath(str.fileSystemRepresentation);\n}\n\nFilePath NSURLToFilePath(NSURL* url) {\n  if (!url.fileURL) {\n    return FilePath();\n  }\n  return NSStringToFilePath(url.path);\n}\n\nScopedCFTypeRef<CFURLRef> FilePathToCFURL(const FilePath& path) {\n  DCHECK(!path.empty());\n\n  // The function's docs promise that it does not require an NSAutoreleasePool.\n  // A straightforward way to accomplish this is to use *Create* functions,\n  // combined with ScopedCFTypeRef.\n  const std::string& path_string = path.value();\n  ScopedCFTypeRef<CFStringRef> path_cfstring(CFStringCreateWithBytes(\n      kCFAllocatorDefault, reinterpret_cast<const UInt8*>(path_string.data()),\n      checked_cast<CFIndex>(path_string.length()), kCFStringEncodingUTF8,\n      /*isExternalRepresentation=*/FALSE));\n  if (!path_cfstring)\n    return ScopedCFTypeRef<CFURLRef>();\n\n  return ScopedCFTypeRef<CFURLRef>(CFURLCreateWithFileSystemPath(\n      kCFAllocatorDefault, path_cfstring, kCFURLPOSIXPathStyle,\n      /*isDirectory=*/FALSE));\n}\n\nbool CFRangeToNSRange(CFRange range, NSRange* range_out) {\n  NSUInteger end;\n  if (IsValueInRangeForNumericType<NSUInteger>(range.location) &&\n      IsValueInRangeForNumericType<NSUInteger>(range.length) &&\n      CheckAdd(range.location, range.length).AssignIfValid(&end) &&\n      IsValueInRangeForNumericType<NSUInteger>(end)) {\n    *range_out = NSMakeRange(static_cast<NSUInteger>(range.location),\n                             static_cast<NSUInteger>(range.length));\n    return true;\n  }\n  return false;\n}\n\n}  // namespace tachyon::base::mac\n\nstd::ostream& operator<<(std::ostream& o, const CFStringRef string) {\n  return o << tachyon::base::SysCFStringRefToUTF8(string);\n}\n\nstd::ostream& operator<<(std::ostream& o, const CFErrorRef err) {\n  tachyon::base::ScopedCFTypeRef<CFStringRef> desc(CFErrorCopyDescription(err));\n  tachyon::base::ScopedCFTypeRef<CFDictionaryRef> user_info(CFErrorCopyUserInfo(err));\n  CFStringRef errorDesc = nullptr;\n  if (user_info.get()) {\n    errorDesc = reinterpret_cast<CFStringRef>(\n        CFDictionaryGetValue(user_info.get(), kCFErrorDescriptionKey));\n  }\n  o << \"Code: \" << CFErrorGetCode(err)\n    << \" Domain: \" << CFErrorGetDomain(err)\n    << \" Desc: \" << desc.get();\n  if(errorDesc) {\n    o << \"(\" << errorDesc << \")\";\n  }\n  return o;\n}\n\nstd::ostream& operator<<(std::ostream& o, CFRange range) {\n  return o << NSStringFromRange(\n             NSMakeRange(static_cast<NSUInteger>(range.location),\n                         static_cast<NSUInteger>(range.length)));\n}\n\nstd::ostream& operator<<(std::ostream& o, id obj) {\n  return obj ? o << [obj description].UTF8String : o << \"(nil)\";\n}\n\nstd::ostream& operator<<(std::ostream& o, NSRange range) {\n  return o << NSStringFromRange(range);\n}\n\nstd::ostream& operator<<(std::ostream& o, SEL selector) {\n  return o << NSStringFromSelector(selector);\n}\n\n#if !BUILDFLAG(IS_IOS)\nstd::ostream& operator<<(std::ostream& o, NSPoint point) {\n  return o << NSStringFromPoint(point);\n}\nstd::ostream& operator<<(std::ostream& o, NSRect rect) {\n  return o << NSStringFromRect(rect);\n}\nstd::ostream& operator<<(std::ostream& o, NSSize size) {\n  return o << NSStringFromSize(size);\n}\n#endif\n"
  },
  {
    "path": "tachyon/base/mac/mac_logging.h",
    "content": "// Copyright 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_MAC_MAC_LOGGING_H_\n#define TACHYON_BASE_MAC_MAC_LOGGING_H_\n\n#include <string>\n\n#include \"tachyon/export.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/build/build_config.h\"\n\n#if BUILDFLAG(IS_IOS)\n#include <MacTypes.h>\n#else\n#include <libkern/OSTypes.h>\n#endif\n\n// Use the OSSTATUS_LOG family to log messages related to errors in Mac OS X\n// system routines that report status via an OSStatus or OSErr value. It is\n// similar to the PLOG family which operates on errno, but because there is no\n// global (or thread-local) OSStatus or OSErr value, the specific error must\n// be supplied as an argument to the OSSTATUS_LOG macro. The message logged\n// will contain the symbolic constant name corresponding to the status value,\n// along with the value itself.\n//\n// OSErr is just an older 16-bit form of the newer 32-bit OSStatus. Despite\n// the name, OSSTATUS_LOG can be used equally well for OSStatus and OSErr.\n\nnamespace google {\n\n// Returns a UTF8 description from an OS X Status error.\nTACHYON_EXPORT std::string DescriptionFromOSStatus(OSStatus err);\n\nclass TACHYON_EXPORT OSStatusLogMessage : public google::LogMessage {\n public:\n  OSStatusLogMessage(const char* file_path,\n                     int line,\n                     LogSeverity severity,\n                     OSStatus status);\n\n  OSStatusLogMessage(const OSStatusLogMessage&) = delete;\n  OSStatusLogMessage& operator=(const OSStatusLogMessage&) = delete;\n\n  ~OSStatusLogMessage();\n\n private:\n  OSStatus status_;\n};\n\n}  // namespace google\n\n#if DCHECK_IS_ON()\n#define MAC_DVLOG_IS_ON(verbose_level) VLOG_IS_ON(verbose_level)\n#else\n#define MAC_DVLOG_IS_ON(verbose_level) 0\n#endif\n\n#define OSSTATUS_LOG_STREAM(severity, status) \\\n    COMPACT_GOOGLE_LOG_EX_ ## severity(OSStatusLogMessage, status).stream()\n#define OSSTATUS_VLOG_STREAM(verbose_level, status) \\\n    logging::OSStatusLogMessage(__FILE__, __LINE__, \\\n                                -verbose_level, status).stream()\n\n#define OSSTATUS_LOG(severity, status) \\\n    LAZY_STREAM(OSSTATUS_LOG_STREAM(severity, status), LOG_IS_ON(severity))\n#define OSSTATUS_LOG_IF(severity, condition, status) \\\n    LAZY_STREAM(OSSTATUS_LOG_STREAM(severity, status), \\\n                LOG_IS_ON(severity) && (condition))\n\n#define OSSTATUS_VLOG(verbose_level, status) \\\n    LAZY_STREAM(OSSTATUS_VLOG_STREAM(verbose_level, status), \\\n                VLOG_IS_ON(verbose_level))\n#define OSSTATUS_VLOG_IF(verbose_level, condition, status) \\\n    LAZY_STREAM(OSSTATUS_VLOG_STREAM(verbose_level, status), \\\n                VLOG_IS_ON(verbose_level) && (condition))\n\n#define OSSTATUS_CHECK(condition, status) \\\n    LAZY_STREAM(OSSTATUS_LOG_STREAM(FATAL, status), !(condition)) \\\n    << \"Check failed: \" # condition << \". \"\n\n#define OSSTATUS_DLOG(severity, status) \\\n    LAZY_STREAM(OSSTATUS_LOG_STREAM(severity, status), DLOG_IS_ON(severity))\n#define OSSTATUS_DLOG_IF(severity, condition, status) \\\n    LAZY_STREAM(OSSTATUS_LOG_STREAM(severity, status), \\\n                DLOG_IS_ON(severity) && (condition))\n\n#define OSSTATUS_DVLOG(verbose_level, status) \\\n    LAZY_STREAM(OSSTATUS_VLOG_STREAM(verbose_level, status), \\\n                MAC_DVLOG_IS_ON(verbose_level))\n#define OSSTATUS_DVLOG_IF(verbose_level, condition, status) \\\n    LAZY_STREAM(OSSTATUS_VLOG_STREAM(verbose_level, status), \\\n                MAC_DVLOG_IS_ON(verbose_level) && (condition))\n\n#define OSSTATUS_DCHECK(condition, status)        \\\n  LAZY_STREAM(OSSTATUS_LOG_STREAM(FATAL, status), \\\n              DCHECK_IS_ON() && !(condition))     \\\n      << \"Check failed: \" #condition << \". \"\n\n#endif  // TACHYON_BASE_MAC_MAC_LOGGING_H_\n"
  },
  {
    "path": "tachyon/base/mac/mac_logging.mm",
    "content": "// Copyright 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/mac/mac_logging.h\"\n\n#import <Foundation/Foundation.h>\n\n#include <iomanip>\n\n#include \"tachyon/build/build_config.h\"\n\n#if !BUILDFLAG(IS_IOS)\n#include <CoreServices/CoreServices.h>\n#endif\n\n#if !defined(__has_feature) || !__has_feature(objc_arc)\n#error \"This file requires ARC support.\"\n#endif\n\nnamespace google {\n\nstd::string DescriptionFromOSStatus(OSStatus err) {\n  NSError* error =\n      [NSError errorWithDomain:NSOSStatusErrorDomain code:err userInfo:nil];\n  return error.description.UTF8String;\n}\n\nOSStatusLogMessage::OSStatusLogMessage(const char* file_path,\n                                       int line,\n                                       LogSeverity severity,\n                                       OSStatus status)\n    : LogMessage(file_path, line, severity),\n      status_(status) {\n}\n\nOSStatusLogMessage::~OSStatusLogMessage() {\n#if BUILDFLAG(IS_IOS)\n  // TODO(crbug.com/546375): Consider using NSError with NSOSStatusErrorDomain\n  // to try to get a description of the failure.\n  stream() << \": \" << status_;\n#else\n  stream() << \": \"\n           << DescriptionFromOSStatus(status_)\n           << \" (\"\n           << status_\n           << \")\";\n#endif\n}\n\n}  // namespace google\n"
  },
  {
    "path": "tachyon/base/mac/mach_logging.cc",
    "content": "// Copyright 2014 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/mac/mach_logging.h\"\n\n#include <iomanip>\n#include <string>\n\n#include \"absl/strings/str_format.h\"\n#include \"tachyon/build/build_config.h\"\n\nnamespace {\n\nstd::string FormatMachErrorNumber(mach_error_t mach_err) {\n  // For the os/kern subsystem, give the error number in decimal as in\n  // <mach/kern_return.h>. Otherwise, give it in hexadecimal to make it easier\n  // to visualize the various bits. See <mach/error.h>.\n  if (mach_err >= 0 && mach_err < KERN_RETURN_MAX) {\n    return absl::StrFormat(\" (%d)\", mach_err);\n  }\n  return absl::StrFormat(\" (0x%08x)\", mach_err);\n}\n\n}  // namespace\n\nnamespace google {\n\nMachLogMessage::MachLogMessage(const char* file_path,\n                               int line,\n                               LogSeverity severity,\n                               mach_error_t mach_err)\n    : LogMessage(file_path, line, severity),\n      mach_err_(mach_err) {\n}\n\nMachLogMessage::~MachLogMessage() {\n  stream() << \": \"\n           << mach_error_string(mach_err_)\n           << FormatMachErrorNumber(mach_err_);\n}\n\n}  // namespace google\n"
  },
  {
    "path": "tachyon/base/mac/mach_logging.h",
    "content": "// Copyright 2014 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_MAC_MACH_LOGGING_H_\n#define TACHYON_BASE_MAC_MACH_LOGGING_H_\n\n#include <mach/mach.h>\n\n#include \"tachyon/export.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/build/build_config.h\"\n\n// Use the MACH_LOG family of macros along with a mach_error_t (kern_return_t)\n// containing a Mach error. The error value will be decoded so that logged\n// messages explain the error.\n//\n// Use the BOOTSTRAP_LOG family of macros specifically for errors that occur\n// while interoperating with the bootstrap subsystem. These errors will first\n// be looked up as bootstrap error messages. If no match is found, they will\n// be treated as generic Mach errors, as in MACH_LOG.\n//\n// Examples:\n//\n//   kern_return_t kr = mach_timebase_info(&info);\n//   if (kr != KERN_SUCCESS) {\n//     MACH_LOG(ERROR, kr) << \"mach_timebase_info\";\n//   }\n//\n//   kr = vm_deallocate(task, address, size);\n//   MACH_DCHECK(kr == KERN_SUCCESS, kr) << \"vm_deallocate\";\n\nnamespace google {\n\nclass TACHYON_EXPORT MachLogMessage : public LogMessage {\n public:\n  MachLogMessage(const char* file_path,\n                 int line,\n                 LogSeverity severity,\n                 mach_error_t mach_err);\n\n  MachLogMessage(const MachLogMessage&) = delete;\n  MachLogMessage& operator=(const MachLogMessage&) = delete;\n\n  ~MachLogMessage();\n\n private:\n  mach_error_t mach_err_;\n};\n\n}  // namespace google\n\n#if DCHECK_IS_ON()\n#define MACH_DVLOG_IS_ON(verbose_level) VLOG_IS_ON(verbose_level)\n#else\n#define MACH_DVLOG_IS_ON(verbose_level) 0\n#endif\n\n#define MACH_LOG_STREAM(severity, mach_err) \\\n    COMPACT_GOOGLE_LOG_EX_ ## severity(MachLogMessage, mach_err).stream()\n#define MACH_VLOG_STREAM(verbose_level, mach_err) \\\n    ::google::MachLogMessage(__FILE__, __LINE__, \\\n                            -verbose_level, mach_err).stream()\n\n#define MACH_LOG(severity, mach_err) \\\n    LAZY_STREAM(MACH_LOG_STREAM(severity, mach_err), LOG_IS_ON(severity))\n#define MACH_LOG_IF(severity, condition, mach_err) \\\n    LAZY_STREAM(MACH_LOG_STREAM(severity, mach_err), \\\n                LOG_IS_ON(severity) && (condition))\n\n#define MACH_VLOG(verbose_level, mach_err) \\\n    LAZY_STREAM(MACH_VLOG_STREAM(verbose_level, mach_err), \\\n                VLOG_IS_ON(verbose_level))\n#define MACH_VLOG_IF(verbose_level, condition, mach_err) \\\n    LAZY_STREAM(MACH_VLOG_STREAM(verbose_level, mach_err), \\\n                VLOG_IS_ON(verbose_level) && (condition))\n\n#define MACH_CHECK(condition, mach_err) \\\n    LAZY_STREAM(MACH_LOG_STREAM(FATAL, mach_err), !(condition)) \\\n    << \"Check failed: \" # condition << \". \"\n\n#define MACH_DLOG(severity, mach_err) \\\n    LAZY_STREAM(MACH_LOG_STREAM(severity, mach_err), DLOG_IS_ON(severity))\n#define MACH_DLOG_IF(severity, condition, mach_err) \\\n    LAZY_STREAM(MACH_LOG_STREAM(severity, mach_err), \\\n                DLOG_IS_ON(severity) && (condition))\n\n#define MACH_DVLOG(verbose_level, mach_err) \\\n    LAZY_STREAM(MACH_VLOG_STREAM(verbose_level, mach_err), \\\n                MACH_DVLOG_IS_ON(verbose_level))\n#define MACH_DVLOG_IF(verbose_level, condition, mach_err) \\\n    LAZY_STREAM(MACH_VLOG_STREAM(verbose_level, mach_err), \\\n                MACH_DVLOG_IS_ON(verbose_level) && (condition))\n\n#define MACH_DCHECK(condition, mach_err)        \\\n  LAZY_STREAM(MACH_LOG_STREAM(FATAL, mach_err), \\\n              DCHECK_IS_ON() && !(condition))   \\\n      << \"Check failed: \" #condition << \". \"\n\n#endif  // TACHYON_BASE_MAC_MACH_LOGGING_H_\n"
  },
  {
    "path": "tachyon/base/mac/scoped_cftyperef.h",
    "content": "// Copyright 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_MAC_SCOPED_CFTYPEREF_H_\n#define TACHYON_BASE_MAC_SCOPED_CFTYPEREF_H_\n\n#include <CoreFoundation/CoreFoundation.h>\n\n#include \"tachyon/base/mac/scoped_typeref.h\"\n\nnamespace tachyon::base {\n\n// ScopedCFTypeRef<> is patterned after std::unique_ptr<>, but maintains\n// ownership of a CoreFoundation object: any object that can be represented\n// as a CFTypeRef.  Style deviations here are solely for compatibility with\n// std::unique_ptr<>'s interface, with which everyone is already familiar.\n//\n// By default, ScopedCFTypeRef<> takes ownership of an object (in the\n// constructor or in reset()) by taking over the caller's existing ownership\n// claim.  The caller must own the object it gives to ScopedCFTypeRef<>, and\n// relinquishes an ownership claim to that object.  ScopedCFTypeRef<> does not\n// call CFRetain(). This behavior is parameterized by the |OwnershipPolicy|\n// enum. If the value |RETAIN| is passed (in the constructor or in reset()),\n// then ScopedCFTypeRef<> will call CFRetain() on the object, and the initial\n// ownership is not changed.\n\nnamespace internal {\n\ntemplate<typename CFT>\nstruct ScopedCFTypeRefTraits {\n  static CFT InvalidValue() { return nullptr; }\n  static CFT Retain(CFT object) {\n    CFRetain(object);\n    return object;\n  }\n  static void Release(CFT object) {\n    CFRelease(object);\n  }\n};\n\n}  // namespace internal\n\ntemplate<typename CFT>\nusing ScopedCFTypeRef =\n    ScopedTypeRef<CFT, internal::ScopedCFTypeRefTraits<CFT>>;\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_MAC_SCOPED_CFTYPEREF_H_\n"
  },
  {
    "path": "tachyon/base/mac/scoped_mach_port.cc",
    "content": "// Copyright 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/mac/scoped_mach_port.h\"\n\n#include \"tachyon/base/mac/mach_logging.h\"\n\nnamespace tachyon::base::mac {\nnamespace internal {\n\n// static\nvoid SendRightTraits::Free(mach_port_t port) {\n  kern_return_t kr = mach_port_deallocate(mach_task_self(), port);\n  MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr)\n      << \"ScopedMachSendRight mach_port_deallocate\";\n}\n\n// static\nvoid ReceiveRightTraits::Free(mach_port_t port) {\n  kern_return_t kr =\n      mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1);\n  MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr)\n      << \"ScopedMachReceiveRight mach_port_mod_refs\";\n}\n\n// static\nvoid PortSetTraits::Free(mach_port_t port) {\n  kern_return_t kr =\n      mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_PORT_SET, -1);\n  MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr)\n      << \"ScopedMachPortSet mach_port_mod_refs\";\n}\n\n}  // namespace internal\n\nbool CreateMachPort(ScopedMachReceiveRight* receive,\n                    ScopedMachSendRight* send,\n                    std::optional<mach_port_msgcount_t> queue_limit) {\n  mach_port_options_t options{};\n  options.flags = (send != nullptr ? MPO_INSERT_SEND_RIGHT : 0);\n\n  if (queue_limit.has_value()) {\n    options.flags |= MPO_QLIMIT;\n    options.mpl.mpl_qlimit = *queue_limit;\n  }\n\n  kern_return_t kr =\n      mach_port_construct(mach_task_self(), &options, 0,\n                          ScopedMachReceiveRight::Receiver(*receive).get());\n  if (kr != KERN_SUCCESS) {\n    MACH_LOG(ERROR, kr) << \"mach_port_construct\";\n    return false;\n  }\n\n  // Multiple rights are coalesced to the same name in a task, so assign the\n  // send rights to the same name.\n  if (send) {\n    send->reset(receive->get());\n  }\n\n  return true;\n}\n\nScopedMachSendRight RetainMachSendRight(mach_port_t port) {\n  kern_return_t kr =\n      mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_SEND, 1);\n  if (kr == KERN_SUCCESS)\n    return ScopedMachSendRight(port);\n  MACH_DLOG(ERROR, kr) << \"mach_port_mod_refs +1\";\n  return {};\n}\n\n}  // namespace tachyon::base::mac\n"
  },
  {
    "path": "tachyon/base/mac/scoped_mach_port.h",
    "content": "// Copyright 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_MAC_SCOPED_MACH_PORT_H_\n#define TACHYON_BASE_MAC_SCOPED_MACH_PORT_H_\n\n#include <mach/mach.h>\n\n#include <optional>\n\n#include \"tachyon/export.h\"\n#include \"tachyon/base/scoped_generic.h\"\n\nnamespace tachyon::base::mac {\n\nnamespace internal {\n\nstruct TACHYON_EXPORT SendRightTraits {\n  static mach_port_t InvalidValue() {\n    return MACH_PORT_NULL;\n  }\n\n  TACHYON_EXPORT static void Free(mach_port_t port);\n};\n\nstruct TACHYON_EXPORT ReceiveRightTraits {\n  static mach_port_t InvalidValue() {\n    return MACH_PORT_NULL;\n  }\n\n  TACHYON_EXPORT static void Free(mach_port_t port);\n};\n\nstruct PortSetTraits {\n  static mach_port_t InvalidValue() {\n    return MACH_PORT_NULL;\n  }\n\n  TACHYON_EXPORT static void Free(mach_port_t port);\n};\n\n}  // namespace internal\n\n// A scoper for handling a Mach port that names a send right. Send rights are\n// reference counted, and this takes ownership of the right on construction\n// and then removes a reference to the right on destruction. If the reference\n// is the last one on the right, the right is deallocated.\nusing ScopedMachSendRight =\n    ScopedGeneric<mach_port_t, internal::SendRightTraits>;\n\n// A scoper for handling a Mach port's receive right. There is only one\n// receive right per port. This takes ownership of the receive right on\n// construction and then destroys the right on destruction, turning all\n// outstanding send rights into dead names.\nusing ScopedMachReceiveRight =\n    ScopedGeneric<mach_port_t, internal::ReceiveRightTraits>;\n\n// A scoper for handling a Mach port set. A port set can have only one\n// reference. This takes ownership of that single reference on construction and\n// destroys the port set on destruction. Destroying a port set does not destroy\n// the receive rights that are members of the port set.\nusing ScopedMachPortSet = ScopedGeneric<mach_port_t, internal::PortSetTraits>;\n\n// Constructs a Mach port receive right and places the result in |receive|.\n// If |send| is non-null, a send right will be created as well and stored\n// there. If |queue_limit| is specified, the receive right will be constructed\n// with the specified mpo_qlmit. Returns true on success and false on failure.\nTACHYON_EXPORT bool CreateMachPort(\n    ScopedMachReceiveRight* receive,\n    ScopedMachSendRight* send,\n    std::optional<mach_port_msgcount_t> queue_limit = std::nullopt);\n\n// Increases the user reference count for MACH_PORT_RIGHT_SEND by 1 and returns\n// a new scoper to manage the additional right.\nTACHYON_EXPORT ScopedMachSendRight RetainMachSendRight(mach_port_t port);\n\n}  // namespace tachyon::base::mac\n\n#endif  // TACHYPN_BASE_MAC_SCOPED_MACH_PORT_H_\n"
  },
  {
    "path": "tachyon/base/mac/scoped_typeref.h",
    "content": "// Copyright 2014 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_MAC_SCOPED_TYPEREF_H_\n#define TACHYON_BASE_MAC_SCOPED_TYPEREF_H_\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/memory/scoped_policy.h\"\n\nnamespace tachyon::base {\n\n// ScopedTypeRef<> is patterned after std::unique_ptr<>, but maintains ownership\n// of a reference to any type that is maintained by Retain and Release methods.\n//\n// The Traits structure must provide the Retain and Release methods for type T.\n// A default ScopedTypeRefTraits is used but not defined, and should be defined\n// for each type to use this interface. For example, an appropriate definition\n// of ScopedTypeRefTraits for CGLContextObj would be:\n//\n//   template<>\n//   struct ScopedTypeRefTraits<CGLContextObj> {\n//     static CGLContextObj InvalidValue() { return nullptr; }\n//     static CGLContextObj Retain(CGLContextObj object) {\n//       CGLContextRetain(object);\n//       return object;\n//     }\n//     static void Release(CGLContextObj object) { CGLContextRelease(object); }\n//   };\n//\n// For the many types that have pass-by-pointer create functions, the function\n// InitializeInto() is provided to allow direct initialization and assumption\n// of ownership of the object. For example, continuing to use the above\n// CGLContextObj specialization:\n//\n//   base::ScopedTypeRef<CGLContextObj> context;\n//   CGLCreateContext(pixel_format, share_group, context.InitializeInto());\n//\n// For initialization with an existing object, the caller may specify whether\n// the ScopedTypeRef<> being initialized is assuming the caller's existing\n// ownership of the object (and should not call Retain in initialization) or if\n// it should not assume this ownership and must create its own (by calling\n// Retain in initialization). This behavior is based on the |policy| parameter,\n// with |ASSUME| for the former and |RETAIN| for the latter. The default policy\n// is to |ASSUME|.\n\ntemplate<typename T>\nstruct ScopedTypeRefTraits;\n\ntemplate<typename T, typename Traits = ScopedTypeRefTraits<T>>\nclass ScopedTypeRef {\n public:\n  using element_type = T;\n\n  explicit constexpr ScopedTypeRef(\n      element_type object = Traits::InvalidValue(),\n      base::scoped_policy::OwnershipPolicy policy = base::scoped_policy::ASSUME)\n      : object_(object) {\n    if (object_ && policy == base::scoped_policy::RETAIN)\n      object_ = Traits::Retain(object_);\n  }\n\n  ScopedTypeRef(const ScopedTypeRef<T, Traits>& that)\n      : object_(that.object_) {\n    if (object_)\n      object_ = Traits::Retain(object_);\n  }\n\n  // This allows passing an object to a function that takes its superclass.\n  template <typename R, typename RTraits>\n  explicit ScopedTypeRef(const ScopedTypeRef<R, RTraits>& that_as_subclass)\n      : object_(that_as_subclass.get()) {\n    if (object_)\n      object_ = Traits::Retain(object_);\n  }\n\n  ScopedTypeRef(ScopedTypeRef<T, Traits>&& that) : object_(that.object_) {\n    that.object_ = Traits::InvalidValue();\n  }\n\n  ~ScopedTypeRef() {\n    if (object_)\n      Traits::Release(object_);\n  }\n\n  ScopedTypeRef& operator=(const ScopedTypeRef<T, Traits>& that) {\n    reset(that.get(), base::scoped_policy::RETAIN);\n    return *this;\n  }\n\n  // This is to be used only to take ownership of objects that are created\n  // by pass-by-pointer create functions. To enforce this, require that the\n  // object be reset to NULL before this may be used.\n  [[nodiscard]] element_type* InitializeInto() {\n    DCHECK(!object_);\n    return &object_;\n  }\n\n  void reset(const ScopedTypeRef<T, Traits>& that) {\n    reset(that.get(), base::scoped_policy::RETAIN);\n  }\n\n  void reset(element_type object = Traits::InvalidValue(),\n             base::scoped_policy::OwnershipPolicy policy =\n                 base::scoped_policy::ASSUME) {\n    if (object && policy == base::scoped_policy::RETAIN)\n      object = Traits::Retain(object);\n    if (object_)\n      Traits::Release(object_);\n    object_ = object;\n  }\n\n  bool operator==(const ScopedTypeRef& that) const {\n    return object_ == that.object_;\n  }\n\n  bool operator!=(const ScopedTypeRef& that) const {\n    return object_ != that.object_;\n  }\n\n  operator element_type() const { return object_; }\n\n  element_type get() const { return object_; }\n\n  void swap(ScopedTypeRef& that) {\n    element_type temp = that.object_;\n    that.object_ = object_;\n    object_ = temp;\n  }\n\n  // ScopedTypeRef<>::release() is like std::unique_ptr<>::release.  It is NOT\n  // a wrapper for Release().  To force a ScopedTypeRef<> object to call\n  // Release(), use ScopedTypeRef<>::reset().\n  [[nodiscard]] element_type release() {\n    element_type temp = object_;\n    object_ = Traits::InvalidValue();\n    return temp;\n  }\n\n private:\n  element_type object_;\n};\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_MAC_SCOPED_TYPEREF_H_\n"
  },
  {
    "path": "tachyon/base/maybe_owned.h",
    "content": "#ifndef TACHYON_BASE_MAYBE_OWNED_H_\n#define TACHYON_BASE_MAYBE_OWNED_H_\n\n#include <utility>\n\n#include \"tachyon/base/maybe_owned_traits.h\"\n\nnamespace tachyon::base {\n\ntemplate <typename T>\nclass MaybeOwned {\n public:\n  using Ref = typename MaybeOwnedTraits<T>::Ref;\n  using ConstRef = typename MaybeOwnedTraits<T>::ConstRef;\n  using Ptr = typename MaybeOwnedTraits<T>::Ptr;\n  using ConstPtr = typename MaybeOwnedTraits<T>::ConstPtr;\n\n  constexpr MaybeOwned() : ptr_(MaybeOwnedTraits<T>::Default()) {}\n  constexpr MaybeOwned(const MaybeOwned& other)\n      : value_(other.value_), owned_(other.owned_) {\n    UpdatePtr(other);\n  }\n  constexpr MaybeOwned& operator=(const MaybeOwned& other) {\n    value_ = other.value_;\n    owned_ = other.owned_;\n    UpdatePtr(other);\n    return *this;\n  }\n  constexpr MaybeOwned(MaybeOwned&& other)\n      : value_(std::move(other.value_)),\n        owned_(std::exchange(other.owned_, false)) {\n    UpdatePtr(other);\n    MaybeOwnedTraits<T>::ResetPtr(&other.ptr_);\n  }\n  constexpr MaybeOwned& operator=(MaybeOwned&& other) {\n    value_ = std::move(other.value_);\n    owned_ = std::exchange(other.owned_, false);\n    UpdatePtr(other);\n    MaybeOwnedTraits<T>::ResetPtr(&other.ptr_);\n    return *this;\n  }\n  // NOTE(chokobole): |explicit| keyword is explicitly removed for convenience.\n  //\n  // class Storage {\n  //  public:\n  //   Storage(const std::string& data): data_(data) {}\n  //   Storage(std::string&& data): data_(std::move(data)) {}\n  //  private:\n  //   MaybeOwned<std::string> data_;\n  // };\n  // NOLINTNEXTLINE(runtime/explicit)\n  constexpr MaybeOwned(const T& value) : value_(value), owned_(true) {\n    MaybeOwnedTraits<T>::UpdatePtr(&ptr_, value_);\n  }\n  // NOLINTNEXTLINE(runtime/explicit)\n  constexpr MaybeOwned(T&& value) : value_(std::move(value)), owned_(true) {\n    MaybeOwnedTraits<T>::UpdatePtr(&ptr_, value_);\n  }\n  // NOLINTNEXTLINE(runtime/explicit)\n  constexpr MaybeOwned(Ptr ptr) : ptr_(ptr) {}\n  constexpr MaybeOwned& operator=(const T& value) {\n    value_ = value;\n    owned_ = true;\n    MaybeOwnedTraits<T>::UpdatePtr(&ptr_, value_);\n    return *this;\n  }\n  constexpr MaybeOwned& operator=(T&& value) {\n    value_ = std::move(value);\n    owned_ = true;\n    MaybeOwnedTraits<T>::UpdatePtr(&ptr_, value_);\n    return *this;\n  }\n  constexpr MaybeOwned& operator=(Ptr ptr) {\n    MaybeOwnedTraits<T>::Release(value_);\n    ptr_ = ptr;\n    owned_ = false;\n    return *this;\n  }\n\n  Ref operator*() { return MaybeOwnedTraits<T>::ToRef(ptr_); }\n  ConstRef operator*() const { return MaybeOwnedTraits<T>::ToConstRef(ptr_); }\n\n  Ptr operator->() { return ptr(); }\n  ConstPtr operator->() const { return ptr(); }\n\n  Ptr ptr() { return ptr_; }\n  ConstPtr ptr() const { return ptr_; }\n\n private:\n  void UpdatePtr(const MaybeOwned& other) {\n    if (owned_) {\n      MaybeOwnedTraits<T>::UpdatePtr(&ptr_, value_);\n    } else {\n      ptr_ = other.ptr_;\n    }\n  }\n\n  T value_;\n  Ptr ptr_;\n  bool owned_ = false;\n};\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_MAYBE_OWNED_H_\n"
  },
  {
    "path": "tachyon/base/maybe_owned_traits.h",
    "content": "#ifndef TACHYON_BASE_MAYBE_OWNED_TRAITS_H_\n#define TACHYON_BASE_MAYBE_OWNED_TRAITS_H_\n\n#include <utility>\n#include <vector>\n\n#include \"absl/types/span.h\"\n\nnamespace tachyon::base {\n\ntemplate <typename T>\nstruct MaybeOwnedTraits {\n  using Ref = T&;\n  using ConstRef = const T&;\n  using Ptr = T*;\n  using ConstPtr = const T*;\n\n  static T* Default() { return nullptr; }\n\n  static void UpdatePtr(T** ptr, T& value) { *ptr = &value; }\n\n  static void ResetPtr(T** ptr) { *ptr = nullptr; }\n\n  static T& ToRef(T* ptr) { return *ptr; }\n\n  static const T& ToConstRef(const T* ptr) { return *ptr; }\n\n  static void Release(T& value) {}\n};\n\ntemplate <typename T>\nstruct MaybeOwnedTraits<std::vector<T>> {\n  using Ref = absl::Span<T>;\n  using ConstRef = absl::Span<const T>;\n  using Ptr = absl::Span<T>;\n  using ConstPtr = absl::Span<const T>;\n\n  static absl::Span<T> Default() { return {}; }\n\n  static void UpdatePtr(absl::Span<T>* ptr, std::vector<T>& value) {\n    *ptr = absl::MakeSpan(value);\n  }\n\n  static void ResetPtr(absl::Span<T>* ptr) { *ptr = absl::Span<T>(); }\n\n  static absl::Span<T> ToRef(absl::Span<T> ptr) { return ptr; }\n\n  static absl::Span<const T> ToConstRef(absl::Span<const T> ptr) { return ptr; }\n\n  static void Release(std::vector<T>& value) { value.clear(); }\n};\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_MAYBE_OWNED_TRAITS_H_\n"
  },
  {
    "path": "tachyon/base/memory/.clang-format",
    "content": "DisableFormat: true\nSortIncludes: true\n"
  },
  {
    "path": "tachyon/base/memory/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"aligned_memory\",\n    srcs = [\"aligned_memory.cc\"],\n    hdrs = [\"aligned_memory.h\"],\n    deps = [\"//tachyon/base:bits\"],\n)\n\ntachyon_cc_library(\n    name = \"scoped_policy\",\n    hdrs = [\"scoped_policy.h\"],\n)\n\ntachyon_cc_unittest(\n    name = \"memory_unittests\",\n    srcs = [\"aligned_memory_unittest.cc\"],\n    deps = [\":aligned_memory\"],\n)\n"
  },
  {
    "path": "tachyon/base/memory/aligned_memory.cc",
    "content": "// Copyright 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/memory/aligned_memory.h\"\n\n#if BUILDFLAG(IS_ANDROID)\n#include <malloc.h>\n#endif\n\nnamespace tachyon::base {\n\nvoid* AlignedAlloc(size_t size, size_t alignment) {\n  DCHECK_GT(size, 0U);\n  DCHECK(bits::IsPowerOfTwo(alignment));\n  DCHECK_EQ(alignment % sizeof(void*), 0U);\n  void* ptr = nullptr;\n#if defined(COMPILER_MSVC)\n  ptr = _aligned_malloc(size, alignment);\n#elif BUILDFLAG(IS_ANDROID)\n  // Android technically supports posix_memalign(), but does not expose it in\n  // the current version of the library headers used by Chromium.  Luckily,\n  // memalign() on Android returns pointers which can safely be used with\n  // free(), so we can use it instead.  Issue filed to document this:\n  // http://code.google.com/p/android/issues/detail?id=35391\n  ptr = memalign(alignment, size);\n#else\n  int ret = posix_memalign(&ptr, alignment, size);\n  if (ret != 0) {\n    DLOG(ERROR) << \"posix_memalign() returned with error \" << ret;\n    ptr = nullptr;\n  }\n#endif\n\n  // Since aligned allocations may fail for non-memory related reasons, force a\n  // crash if we encounter a failed allocation; maintaining consistent behavior\n  // with a normal allocation failure in Chrome.\n  if (!ptr) {\n    DLOG(ERROR) << \"If you crashed here, your aligned allocation is incorrect: \"\n                << \"size=\" << size << \", alignment=\" << alignment;\n    CHECK(false);\n  }\n  // Sanity check alignment just to be safe.\n  DCHECK(IsAligned(ptr, alignment));\n  return ptr;\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/memory/aligned_memory.h",
    "content": "// Copyright 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_MEMORY_ALIGNED_MEMORY_H_\n#define TACHYON_BASE_MEMORY_ALIGNED_MEMORY_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <ostream>\n\n#include \"tachyon/export.h\"\n#include \"tachyon/base/bits.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/build/build_config.h\"\n\n#if defined(COMPILER_MSVC)\n#include <malloc.h>\n#else\n#include <stdlib.h>\n#endif\n\n// A runtime sized aligned allocation can be created:\n//\n//   float* my_array = static_cast<float*>(AlignedAlloc(size, alignment));\n//\n//   // ... later, to release the memory:\n//   AlignedFree(my_array);\n//\n// Or using unique_ptr:\n//\n//   std::unique_ptr<float, AlignedFreeDeleter> my_array(\n//       static_cast<float*>(AlignedAlloc(size, alignment)));\n\nnamespace tachyon::base {\n\n// This can be replaced with std::aligned_alloc when we have C++17.\n// Caveat: std::aligned_alloc requires the size parameter be an integral\n// multiple of alignment.\nTACHYON_EXPORT void* AlignedAlloc(size_t size, size_t alignment);\n\ninline void AlignedFree(void* ptr) {\n#if defined(COMPILER_MSVC)\n  _aligned_free(ptr);\n#else\n  free(ptr);\n#endif\n}\n\n// Deleter for use with unique_ptr. E.g., use as\n//   std::unique_ptr<Foo, base::AlignedFreeDeleter> foo;\nstruct AlignedFreeDeleter {\n  inline void operator()(void* ptr) const {\n    AlignedFree(ptr);\n  }\n};\n\n#ifdef __has_builtin\n#define SUPPORTS_BUILTIN_IS_ALIGNED (__has_builtin(__builtin_is_aligned))\n#else\n#define SUPPORTS_BUILTIN_IS_ALIGNED 0\n#endif\n\ninline bool IsAligned(uintptr_t val, size_t alignment) {\n  // If the compiler supports builtin alignment checks prefer them.\n#if SUPPORTS_BUILTIN_IS_ALIGNED\n  return __builtin_is_aligned(val, alignment);\n#else\n  DCHECK(bits::IsPowerOfTwo(alignment)) << alignment << \" is not a power of 2\";\n  return (val & (alignment - 1)) == 0;\n#endif\n}\n\n#undef SUPPORTS_BUILTIN_IS_ALIGNED\n\ninline bool IsAligned(const void* val, size_t alignment) {\n  return IsAligned(reinterpret_cast<uintptr_t>(val), alignment);\n}\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_MEMORY_ALIGNED_MEMORY_H_\n"
  },
  {
    "path": "tachyon/base/memory/aligned_memory_unittest.cc",
    "content": "// Copyright 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/memory/aligned_memory.h\"\n\n#include <memory>\n\n#include \"gtest/gtest.h\"\n\nnamespace tachyon::base {\n\nTEST(AlignedMemoryTest, DynamicAllocation) {\n  void* p = AlignedAlloc(8, 8);\n  EXPECT_TRUE(p);\n  EXPECT_TRUE(IsAligned(p, 8));\n  AlignedFree(p);\n\n  p = AlignedAlloc(8, 16);\n  EXPECT_TRUE(p);\n  EXPECT_TRUE(IsAligned(p, 16));\n  AlignedFree(p);\n\n  p = AlignedAlloc(8, 256);\n  EXPECT_TRUE(p);\n  EXPECT_TRUE(IsAligned(p, 256));\n  AlignedFree(p);\n\n  p = AlignedAlloc(8, 4096);\n  EXPECT_TRUE(p);\n  EXPECT_TRUE(IsAligned(p, 4096));\n  AlignedFree(p);\n}\n\nTEST(AlignedMemoryTest, ScopedDynamicAllocation) {\n  std::unique_ptr<float, AlignedFreeDeleter> p(\n      static_cast<float*>(AlignedAlloc(8, 8)));\n  EXPECT_TRUE(p.get());\n  EXPECT_TRUE(IsAligned(p.get(), 8));\n\n  // Make sure IsAligned() can check const pointers as well.\n  const float* const_p = p.get();\n  EXPECT_TRUE(IsAligned(const_p, 8));\n}\n\nTEST(AlignedMemoryTest, IsAligned) {\n  // Check alignment around powers of two.\n  for (int i = 0; i < 64; ++i) {\n    const uint64_t n = static_cast<uint64_t>(1) << i;\n\n    // Walk back down all lower powers of two checking alignment.\n    for (int j = i - 1; j >= 0; --j) {\n      // n is aligned on all powers of two less than or equal to 2ⁱ.\n      EXPECT_TRUE(IsAligned(n, n >> j))\n          << \"Expected \" << n << \" to be \" << (n >> j) << \" aligned\";\n\n      // Also, n - 1 should not be aligned on ANY lower power of two except 1\n      // (but since we're starting from i - 1 we don't test that case here.\n      EXPECT_FALSE(IsAligned(n - 1, n >> j))\n          << \"Expected \" << (n - 1) << \" to NOT be \" << (n >> j) << \" aligned\";\n    }\n  }\n\n  // And a few hard coded smoke tests for completeness:\n  EXPECT_TRUE(IsAligned(4, 2));\n  EXPECT_TRUE(IsAligned(8, 4));\n  EXPECT_TRUE(IsAligned(8, 2));\n  EXPECT_TRUE(IsAligned(0x1000, 4 << 10));\n  EXPECT_TRUE(IsAligned(0x2000, 8 << 10));\n  EXPECT_TRUE(IsAligned(1, 1));\n  EXPECT_TRUE(IsAligned(7, 1));\n  EXPECT_TRUE(IsAligned(reinterpret_cast<void*>(0x1000), 4 << 10));\n  EXPECT_TRUE(IsAligned(reinterpret_cast<int*>(0x1000), 4 << 10));\n\n  EXPECT_FALSE(IsAligned(3, 2));\n  EXPECT_FALSE(IsAligned(7, 4));\n  EXPECT_FALSE(IsAligned(7, 2));\n  EXPECT_FALSE(IsAligned(0x1001, 4 << 10));\n  EXPECT_FALSE(IsAligned(0x999, 8 << 10));\n  EXPECT_FALSE(IsAligned(7, 8));\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/memory/scoped_policy.h",
    "content": "// Copyright 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_MEMORY_SCOPED_POLICY_H_\n#define TACHYON_BASE_MEMORY_SCOPED_POLICY_H_\n\nnamespace tachyon::base {\nnamespace scoped_policy {\n\n// Defines the ownership policy for a scoped object.\nenum OwnershipPolicy {\n  // The scoped object takes ownership of an object by taking over an existing\n  // ownership claim.\n  ASSUME,\n\n  // The scoped object will retain the object and any initial ownership is\n  // not changed.\n  RETAIN\n};\n\n}  // namespace scoped_policy\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_MEMORY_SCOPED_POLICY_H_\n"
  },
  {
    "path": "tachyon/base/message_loop/.clang-format",
    "content": "DisableFormat: true\nSortIncludes: false"
  },
  {
    "path": "tachyon/base/message_loop/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"message_pump_type\",\n    hdrs = [\"message_pump_type.h\"],\n    deps = [\"//tachyon/build:build_config\"],\n)\n"
  },
  {
    "path": "tachyon/base/message_loop/message_pump_type.h",
    "content": "// Copyright 2019 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_MESSAGE_LOOP_MESSAGE_PUMP_TYPE_H_\n#define TACHYON_BASE_MESSAGE_LOOP_MESSAGE_PUMP_TYPE_H_\n\n#include \"tachyon/build/build_config.h\"\n\nnamespace tachyon::base {\n\n// A MessagePump has a particular type, which indicates the set of\n// asynchronous events it may process in addition to tasks and timers.\n\nenum class MessagePumpType {\n  // This type of pump only supports tasks and timers.\n  DEFAULT,\n\n  // This type of pump also supports native UI events (e.g., Windows\n  // messages).\n  UI,\n\n  // User provided implementation of MessagePump interface\n  CUSTOM,\n\n  // This type of pump also supports asynchronous IO.\n  IO,\n\n#if BUILDFLAG(IS_ANDROID)\n  // This type of pump is backed by a Java message handler which is\n  // responsible for running the tasks added to the ML. This is only for use\n  // on Android. TYPE_JAVA behaves in essence like TYPE_UI, except during\n  // construction where it does not use the main thread specific pump factory.\n  JAVA,\n#endif  // BUILDFLAG(IS_ANDROID)\n\n#if BUILDFLAG(IS_APPLE)\n  // This type of pump is backed by a NSRunLoop. This is only for use on\n  // OSX and IOS.\n  NS_RUNLOOP,\n#endif  // BUILDFLAG(IS_APPLE)\n};\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_MESSAGE_LOOP_MESSAGE_PUMP_TYPE_H_\n"
  },
  {
    "path": "tachyon/base/no_destructor.h",
    "content": "// Copyright 2018 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_NO_DESTRUCTOR_H_\n#define TACHYON_BASE_NO_DESTRUCTOR_H_\n\n#include <new>\n#include <type_traits>\n#include <utility>\n\nnamespace tachyon::base {\n\n// Helper type to create a function-local static variable of type `T` when `T`\n// has a non-trivial destructor. Storing a `T` in a\n// `tachyon::base::NoDestructor<T>` will prevent `~T()` from running, even when\n// the variable goes out of scope.\n//\n// Useful when a variable has static storage duration but its type has a\n// non-trivial destructor. Tachyon bans global constructors and destructors:\n// using a function-local static variable prevents the former, while using\n// `tachyon::base::NoDestructor<T>` prevents the latter.\n//\n// ## Caveats\n//\n// - Must only be used as a function-local static variable. Declaring a global\n//   variable of type `tachyon::base::NoDestructor<T>` will still generate a\n//   global constructor; declaring a local or member variable will lead to\n//   memory leaks or other surprising and undesirable behaviour.\n//\n// - If the data is rarely used, consider creating it on demand rather than\n//   caching it for the lifetime of the program. Though\n//   `tachyon::base::NoDestructor<T>` does not heap allocate, the compiler still\n//   reserves space in bss for storing `T`, which costs memory at runtime.\n//\n// - If `T` is trivially destructible, do not use\n// `tachyon::base::NoDestructor<T>`:\n//\n//     const uint64_t GetUnstableSessionSeed() {\n//       // No need to use `tachyon::base::NoDestructor<T>` as `uint64_t` is\n//       trivially\n//       // destructible and does not require a global destructor.\n//       static const uint64_t kSessionSeed = tachyon::RandUint64();\n//       return kSessionSeed;\n//     }\n//\n// ## Example Usage\n//\n// const std::string& GetDefaultText() {\n//   // Required since `static const std::string` requires a global destructor.\n//   static const tachyon::base::NoDestructor<std::string> s(\"Hello world!\");\n//   return *s;\n// }\n//\n// More complex initialization using a lambda:\n//\n// const std::string& GetRandomNonce() {\n//   // `nonce` is initialized with random data the first time this function is\n//   // called, but its value is fixed thereafter.\n//   static const tachyon::base::NoDestructor<std::string> nonce([] {\n//     std::string s(16);\n//     crypto::RandString(s.data(), s.size());\n//     return s;\n//   }());\n//   return *nonce;\n// }\n//\n// ## Thread safety\n//\n// Initialisation of function-local static variables is thread-safe since C++11.\n// The standard guarantees that:\n//\n// - function-local static variables will be initialised the first time\n//   execution passes through the declaration.\n//\n// - if another thread's execution concurrently passes through the declaration\n//   in the middle of initialisation, that thread will wait for the in-progress\n//   initialisation to complete.\ntemplate <typename T>\nclass NoDestructor {\n public:\n  static_assert(\n      !std::is_trivially_destructible_v<T>,\n      \"T is trivially destructible; please use a function-local static \"\n      \"of type T directly instead\");\n\n  // Not constexpr; just write static constexpr T x = ...; if the value should\n  // be a constexpr.\n  template <typename... Args>\n  explicit NoDestructor(Args&&... args) {\n    new (storage_) T(std::forward<Args>(args)...);\n  }\n\n  // Allows copy and move construction of the contained type, to allow\n  // construction from an initializer list, e.g. for std::vector.\n  explicit NoDestructor(const T& x) { new (storage_) T(x); }\n  explicit NoDestructor(T&& x) { new (storage_) T(std::move(x)); }\n\n  NoDestructor(const NoDestructor&) = delete;\n  NoDestructor& operator=(const NoDestructor&) = delete;\n\n  ~NoDestructor() = default;\n\n  const T& operator*() const { return *get(); }\n  T& operator*() { return *get(); }\n\n  const T* operator->() const { return get(); }\n  T* operator->() { return get(); }\n\n  const T* get() const { return reinterpret_cast<const T*>(storage_); }\n  T* get() { return reinterpret_cast<T*>(storage_); }\n\n private:\n  alignas(T) char storage_[sizeof(T)];\n};\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_NO_DESTRUCTOR_H_\n"
  },
  {
    "path": "tachyon/base/numerics/.clang-format",
    "content": "DisableFormat: true\nSortIncludes: false"
  },
  {
    "path": "tachyon/base/numerics/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"checked_math\",\n    hdrs = [\n        \"checked_math.h\",\n        \"checked_math_impl.h\",\n    ],\n    deps = [\":safe_math_impl\"],\n)\n\ntachyon_cc_library(\n    name = \"clamped_math\",\n    hdrs = [\n        \"clamped_math.h\",\n        \"clamped_math_impl.h\",\n    ],\n    deps = [\":checked_math\"],\n)\n\ntachyon_cc_library(\n    name = \"math_constants\",\n    hdrs = [\"math_constants.h\"],\n)\n\ntachyon_cc_library(\n    name = \"safe_conversions\",\n    hdrs = [\n        \"safe_conversions.h\",\n        \"safe_conversions_arm_impl.h\",\n        \"safe_conversions_impl.h\",\n    ],\n    deps = [\"//tachyon/build:build_config\"],\n)\n\ntachyon_cc_library(\n    name = \"safe_math_impl\",\n    hdrs = [\n        \"safe_math_arm_impl.h\",\n        \"safe_math_clang_gcc_impl.h\",\n        \"safe_math_shared_impl.h\",\n    ],\n    deps = [\":safe_conversions\"],\n)\n\ntachyon_cc_library(\n    name = \"safe_math\",\n    hdrs = [\"safe_math.h\"],\n    deps = [\n        \":checked_math\",\n        \":clamped_math\",\n        \":safe_conversions\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/base/numerics/README.md",
    "content": "# `tachyon/base/numerics`\n\nThis directory contains a dependency-free, header-only library of templates\nproviding well-defined semantics for safely and performantly handling a variety\nof numeric operations, including most common arithmetic operations and\nconversions.\n\nThe public API is broken out into the following header files:\n\n*   `checked_math.h` contains the `CheckedNumeric` template class and helper\n    functions for performing arithmetic and conversion operations that detect\n    errors and boundary conditions (e.g. overflow, truncation, etc.).\n*   `clamped_math.h` contains the `ClampedNumeric` template class and\n    helper functions for performing fast, clamped (i.e. [non-sticky](#notsticky)\n    saturating) arithmetic operations and conversions.\n*   `safe_conversions.h` contains the `StrictNumeric` template class and\n    a collection of custom casting templates and helper functions for safely\n    converting between a range of numeric types.\n*   `safe_math.h` includes all of the previously mentioned headers.\n\n*** aside\n**Note:** The `Numeric` template types implicitly convert from C numeric types\nand `Numeric` templates that are convertible to an underlying C numeric type.\nThe conversion priority for `Numeric` type coercions is:\n\n*   `StrictNumeric` coerces to `ClampedNumeric` and `CheckedNumeric`\n*   `ClampedNumeric` coerces to `CheckedNumeric`\n***\n\n[TOC]\n\n## Common patterns and use-cases\n\nThe following covers the preferred style for the most common uses of this\nlibrary. Please don't cargo-cult from anywhere else. 😉\n\n### Performing checked arithmetic type conversions\n\nThe `checked_cast` template converts between arbitrary arithmetic types, and is\nused for cases where a conversion failure should result in program termination:\n\n```cpp\n// Crash if signed_value is out of range for buff_size.\nsize_t buff_size = checked_cast<size_t>(signed_value);\n```\n\n### Performing saturated (clamped) arithmetic type conversions\n\nThe `saturated_cast` template converts between arbitrary arithmetic types, and\nis used in cases where an out-of-bounds source value should be saturated to the\ncorresponding maximum or minimum of the destination type:\n\n```cpp\n// Cast to a smaller type, saturating as needed.\nint8_t eight_bit_value = saturated_cast<int8_t>(int_value);\n\n// Convert from float with saturation to INT_MAX, INT_MIN, or 0 for NaN.\nint int_value = saturated_cast<int>(floating_point_value);\n```\n\n`ClampCeil`, `ClampFloor`, and `ClampRound` provide similar functionality to the\nversions in `std::`, but saturate and return an integral type.  An optional\ntemplate parameter specifies the desired destination type (`int` if\nunspecified).  These should be used for most floating-to-integral conversions.\n\n```cpp\n// Basically saturated_cast<int>(std::round(floating_point_value)).\nint int_value = ClampRound(floating_point_value);\n\n// A destination type can be explicitly specified.\nuint8_t byte_value = ClampFloor<uint8_t>(floating_point_value);\n```\n\n### Enforcing arithmetic type conversions at compile-time\n\nThe `strict_cast` emits code that is identical to `static_cast`. However,\nprovides static checks that will cause a compilation failure if the\ndestination type cannot represent the full range of the source type:\n\n```cpp\n// Throw a compiler error if byte_value is changed to an out-of-range-type.\nint int_value = strict_cast<int>(byte_value);\n```\n\nYou can also enforce these compile-time restrictions on function parameters by\nusing the `StrictNumeric` template:\n\n```cpp\n// Throw a compiler error if the size argument cannot be represented by a\n// size_t (e.g. passing an int will fail to compile).\nbool AllocateBuffer(void** buffer, StrictCast<size_t> size);\n```\n\n### Comparing values between arbitrary arithmetic types\n\nBoth the `StrictNumeric` and `ClampedNumeric` types provide well defined\ncomparisons between arbitrary arithmetic types. This allows you to perform\ncomparisons that are not legal or would trigger compiler warnings or errors\nunder the normal arithmetic promotion rules:\n\n```cpp\nbool foo(unsigned value, int upper_bound) {\n  // Converting to StrictNumeric allows this comparison to work correctly.\n  if (MakeStrictNum(value) >= upper_bound)\n    return false;\n```\n\n*** note\n**Warning:** Do not perform manual conversions using the comparison operators.\nInstead, use the cast templates described in the previous sections, or the\nconstexpr template functions `IsValueInRangeForNumericType` and\n`IsTypeInRangeForNumericType`, as these templates properly handle the full range\nof corner cases and employ various optimizations.\n***\n\n### Calculating a buffer size (checked arithmetic)\n\nWhen making exact calculations—such as for buffer lengths—it's often necessary\nto know when those calculations trigger an overflow, undefined behavior, or\nother boundary conditions. The `CheckedNumeric` template does this by storing\na bit determining whether or not some arithmetic operation has occurred that\nwould put the variable in an \"invalid\" state. Attempting to extract the value\nfrom a variable in an invalid state will trigger a check/trap condition, that\nby default will result in process termination.\n\nHere's an example of a buffer calculation using a `CheckedNumeric` type (note:\nthe AssignIfValid method will trigger a compile error if the result is ignored).\n\n```cpp\n// Calculate the buffer size and detect if an overflow occurs.\nsize_t size;\nif (!CheckAdd(kHeaderSize, CheckMul(count, kItemSize)).AssignIfValid(&size)) {\n  // Handle an overflow error...\n}\n```\n\n### Calculating clamped coordinates (non-sticky saturating arithmetic)\n\nCertain classes of calculations—such as coordinate calculations—require\nwell-defined semantics that always produce a valid result on boundary\nconditions. The `ClampedNumeric` template addresses this by providing\nperformant, non-sticky saturating arithmetic operations.\n\nHere's an example of using a `ClampedNumeric` to calculate an operation\ninsetting a rectangle.\n\n```cpp\n// Use clamped arithmetic since inset calculations might overflow.\nvoid Rect::Inset(int left, int top, int right, int bottom) {\n  origin_ += Vector2d(left, top);\n  set_width(ClampSub(width(), ClampAdd(left, right)));\n  set_height(ClampSub(height(), ClampAdd(top, bottom)));\n}\n```\n\n*** note\n<a name=\"notsticky\"></a>\nThe `ClampedNumeric` type is not \"sticky\", which means the saturation is not\nretained across individual operations. As such, one arithmetic operation may\nresult in a saturated value, while the next operation may then \"desaturate\"\nthe value. Here's an example:\n\n```cpp\nClampedNumeric<int> value = INT_MAX;\n++value;  // value is still INT_MAX, due to saturation.\n--value;  // value is now (INT_MAX - 1), because saturation is not sticky.\n```\n\n***\n\n## Conversion functions and StrictNumeric<> in safe_conversions.h\n\nThis header includes a collection of helper `constexpr` templates for safely\nperforming a range of conversions, assignments, and tests.\n\n### Safe casting templates\n\n*   `as_signed()` - Returns the supplied integral value as a signed type of\n    the same width.\n*   `as_unsigned()` - Returns the supplied integral value as an unsigned type\n    of the same width.\n*   `checked_cast<>()` - Analogous to `static_cast<>` for numeric types, except\n    that by default it will trigger a crash on an out-of-bounds conversion (e.g.\n    overflow, underflow, NaN to integral) or a compile error if the conversion\n    error can be detected at compile time. The crash handler can be overridden\n    to perform a behavior other than crashing.\n*   `saturated_cast<>()` - Analogous to `static_cast` for numeric types, except\n    that it returns a saturated result when the specified numeric conversion\n    would otherwise overflow or underflow. An NaN source returns 0 by\n    default, but can be overridden to return a different result.\n*   `strict_cast<>()` - Analogous to `static_cast` for numeric types, except\n    this causes a compile failure if the destination type is not large\n    enough to contain any value in the source type. It performs no runtime\n    checking and thus introduces no runtime overhead.\n\n### Other helper and conversion functions\n\n*   `ClampCeil<>()` - A convenience function that computes the ceil of its floating-\n    point arg, then saturates to the destination type (template parameter,\n    defaults to `int`).\n*   `ClampFloor<>()` - A convenience function that computes the floor of its\n    floating-point arg, then saturates to the destination type (template\n    parameter, defaults to `int`).\n*   `IsTypeInRangeForNumericType<>()` - A convenience function that evaluates\n    entirely at compile-time and returns true if the destination type (first\n    template parameter) can represent the full range of the source type\n    (second template parameter).\n*   `IsValueInRangeForNumericType<>()` - A convenience function that returns\n    true if the type supplied as the template parameter can represent the value\n    passed as an argument to the function.\n*   `IsValueNegative()` - A convenience function that will accept any\n    arithmetic type as an argument and will return whether the value is less\n    than zero. Unsigned types always return false.\n*   `ClampRound<>()` - A convenience function that rounds its floating-point arg,\n    then saturates to the destination type (template parameter, defaults to\n    `int`).\n*   `SafeUnsignedAbs()` - Returns the absolute value of the supplied integer\n    parameter as an unsigned result (thus avoiding an overflow if the value\n    is the signed, two's complement minimum).\n\n### StrictNumeric<>\n\n`StrictNumeric<>` is a wrapper type that performs assignments and copies via\nthe `strict_cast` template, and can perform valid arithmetic comparisons\nacross any range of arithmetic types. `StrictNumeric` is the return type for\nvalues extracted from a `CheckedNumeric` class instance. The raw numeric value\nis extracted via `static_cast` to the underlying type or any type with\nsufficient range to represent the underlying type.\n\n*   `MakeStrictNum()` - Creates a new `StrictNumeric` from the underlying type\n    of the supplied arithmetic or StrictNumeric type.\n*   `SizeT` - Alias for `StrictNumeric<size_t>`.\n\n## CheckedNumeric<> in checked_math.h\n\n`CheckedNumeric<>` implements all the logic and operators for detecting integer\nboundary conditions such as overflow, underflow, and invalid conversions.\nThe `CheckedNumeric` type implicitly converts from floating point and integer\ndata types, and contains overloads for basic arithmetic operations (i.e.: `+`,\n`-`, `*`, `/` for all types and `%`, `<<`, `>>`, `&`, `|`, `^` for integers).\nHowever, *the [variadic template functions\n](#CheckedNumeric_in-checked_math_h-Non_member-helper-functions)\nare the preferred API,* as they remove type ambiguities and help prevent a number\nof common errors. The variadic functions can also be more performant, as they\neliminate redundant expressions that are unavoidable with the with the operator\noverloads. (Ideally the compiler should optimize those away, but better to avoid\nthem in the first place.)\n\nType promotions are a slightly modified version of the [standard C/C++ numeric\npromotions](http://en.cppreference.com/w/cpp/language/implicit_conversion#Numeric_promotions)\nwith the two differences being that *there is no default promotion to int*\nand *bitwise logical operations always return an unsigned of the wider type.*\n\n### Example\n\n```\n#include \"tachyon/base/numerics/checked_math.h\"\n...\nCheckedNumeric<uint32_t> variable = 0;\nvariable++;\nvariable--;\nif (variable.ValueOrDie() == 0)\n  // Fine, |variable| still within valid range.\n\nvariable--;\nvariable++;\nif (variable.ValueOrDie() == 0)  // Breakpoint or configured CheckHandler\n  // Does not happen as variable underflowed.\n```\n\n### Members\n\nThe unary negation, increment, and decrement operators are supported, along\nwith the following unary arithmetic methods, which return a new\n`CheckedNumeric` as a result of the operation:\n\n*   `Abs()` - Absolute value.\n*   `UnsignedAbs()` - Absolute value as an equal-width unsigned underlying type\n    (valid for only integral types).\n*   `Max()` - Returns whichever is greater of the current instance or argument.\n    The underlying return type is whichever has the greatest magnitude.\n*   `Min()` - Returns whichever is lowest of the current instance or argument.\n    The underlying return type is whichever has can represent the lowest\n    number in the smallest width (e.g. int8_t over unsigned, int over\n    int8_t, and float over int).\n\nThe following are for converting `CheckedNumeric` instances:\n\n*   `type` - The underlying numeric type.\n*   `AssignIfValid()` - Assigns the underlying value to the supplied\n    destination pointer if the value is currently valid and within the\n    range supported by the destination type. Returns true on success.\n*   `Cast<>()` - Instance method returning a `CheckedNumeric` derived from\n    casting the current instance to a `CheckedNumeric` of the supplied\n    destination type.\n\n*** aside\nThe following member functions return a `StrictNumeric`, which is valid for\ncomparison and assignment operations, but will trigger a compile failure on\nattempts to assign to a type of insufficient range. The underlying value can\nbe extracted by an explicit `static_cast` to the underlying type or any type\nwith sufficient range to represent the underlying type.\n***\n\n*   `IsValid()` - Returns true if the underlying numeric value is valid (i.e.\n    has not wrapped or saturated and is not the result of an invalid\n    conversion).\n*   `ValueOrDie()` - Returns the underlying value. If the state is not valid\n    this call will trigger a crash by default (but may be overridden by\n    supplying an alternate handler to the template).\n*   `ValueOrDefault()` - Returns the current value, or the supplied default if\n    the state is not valid (but will not crash).\n\n**Comparison operators are explicitly not provided** for `CheckedNumeric`\ntypes because they could result in a crash if the type is not in a valid state.\nPatterns like the following should be used instead:\n\n```cpp\n// Either input or padding (or both) may be arbitrary sizes.\nsize_t buff_size;\nif (!CheckAdd(input, padding, kHeaderLength).AssignIfValid(&buff_size) ||\n     buff_size >= kMaxBuffer) {\n  // Handle an error...\n} else {\n  // Do stuff on success...\n}\n```\n\n### Non-member helper functions\n\nThe following variadic convenience functions, which accept standard arithmetic\nor `CheckedNumeric` types, perform arithmetic operations, and return a\n`CheckedNumeric` result. The supported functions are:\n\n*   `CheckAdd()` - Addition.\n*   `CheckSub()` - Subtraction.\n*   `CheckMul()` - Multiplication.\n*   `CheckDiv()` - Division.\n*   `CheckMod()` - Modulus (integer only).\n*   `CheckLsh()` - Left integer shift (integer only).\n*   `CheckRsh()` - Right integer shift (integer only).\n*   `CheckAnd()` - Bitwise AND (integer only with unsigned result).\n*   `CheckOr()`  - Bitwise OR (integer only with unsigned result).\n*   `CheckXor()` - Bitwise XOR (integer only with unsigned result).\n*   `CheckMax()` - Maximum of supplied arguments.\n*   `CheckMin()` - Minimum of supplied arguments.\n\nThe following wrapper functions can be used to avoid the template\ndisambiguator syntax when converting a destination type.\n\n*   `IsValidForType<>()` in place of: `a.template IsValid<>()`\n*   `ValueOrDieForType<>()` in place of: `a.template ValueOrDie<>()`\n*   `ValueOrDefaultForType<>()` in place of: `a.template ValueOrDefault<>()`\n\nThe following general utility methods are useful for converting from\narithmetic types to `CheckedNumeric` types:\n\n*   `MakeCheckedNum()` - Creates a new `CheckedNumeric` from the underlying type\n    of the supplied arithmetic or directly convertible type.\n\n## ClampedNumeric<> in clamped_math.h\n\n`ClampedNumeric<>` implements all the logic and operators for clamped\n(non-sticky saturating) arithmetic operations and conversions. The\n`ClampedNumeric` type implicitly converts back and forth between floating point\nand integer data types, saturating on assignment as appropriate. It contains\noverloads for basic arithmetic operations (i.e.: `+`, `-`, `*`, `/` for\nall types and `%`, `<<`, `>>`, `&`, `|`, `^` for integers) along with comparison\noperators for arithmetic types of any size. However, *the [variadic template\nfunctions\n](#ClampedNumeric_in-clamped_math_h-Non_member-helper-functions)\nare the preferred API,* as they remove type ambiguities and help prevent\na number of common errors. The variadic functions can also be more performant,\nas they eliminate redundant expressions that are unavoidable with the operator\noverloads. (Ideally, the compiler should optimize those away, but better to avoid\nthem in the first place.)\n\nType promotions are a slightly modified version of the [standard C/C++ numeric\npromotions](http://en.cppreference.com/w/cpp/language/implicit_conversion#Numeric_promotions) with the two differences being that *there is no default promotion to int*\nand *bitwise logical operations always return an unsigned of the wider type.*\n\n*** aside\nMost arithmetic operations saturate normally, to the numeric limit in the\ndirection of the sign. The potentially unusual cases are:\n\n*   **Division:** Division by zero returns the saturated limit in the direction\n    of sign of the dividend (first argument). The one exception is 0/0, which\n\treturns zero (although logically is NaN).\n*   **Modulus:** Division by zero returns the dividend (first argument).\n*   **Left shift:** Non-zero values saturate in the direction of the signed\n    limit (max/min), even for shifts larger than the bit width. 0 shifted any\n    amount results in 0.\n*   **Right shift:** Negative values saturate to -1. Positive or 0 saturates\n    to 0. (Effectively just an unbounded arithmetic-right-shift.)\n*   **Bitwise operations:** No saturation; bit pattern is identical to\n    non-saturated bitwise operations.\n***\n\n### Members\n\nThe unary negation, increment, and decrement operators are supported, along\nwith the following unary arithmetic methods, which return a new\n`ClampedNumeric` as a result of the operation:\n\n*   `Abs()` - Absolute value.\n*   `UnsignedAbs()` - Absolute value as an equal-width unsigned underlying type\n    (valid for only integral types).\n*   `Max()` - Returns whichever is greater of the current instance or argument.\n    The underlying return type is whichever has the greatest magnitude.\n*   `Min()` - Returns whichever is lowest of the current instance or argument.\n    The underlying return type is whichever has can represent the lowest\n    number in the smallest width (e.g. int8_t over unsigned, int over\n    int8_t, and float over int).\n\nThe following are for converting `ClampedNumeric` instances:\n\n*   `type` - The underlying numeric type.\n*   `RawValue()` - Returns the raw value as the underlying arithmetic type. This\n    is useful when e.g. assigning to an auto type or passing as a deduced\n    template parameter.\n*   `Cast<>()` - Instance method returning a `ClampedNumeric` derived from\n    casting the current instance to a `ClampedNumeric` of the supplied\n    destination type.\n\n### Non-member helper functions\n\nThe following variadic convenience functions, which accept standard arithmetic\nor `ClampedNumeric` types, perform arithmetic operations, and return a\n`ClampedNumeric` result. The supported functions are:\n\n*   `ClampAdd()` - Addition.\n*   `ClampSub()` - Subtraction.\n*   `ClampMul()` - Multiplication.\n*   `ClampDiv()` - Division.\n*   `ClampMod()` - Modulus (integer only).\n*   `ClampLsh()` - Left integer shift (integer only).\n*   `ClampRsh()` - Right integer shift (integer only).\n*   `ClampAnd()` - Bitwise AND (integer only with unsigned result).\n*   `ClampOr()`  - Bitwise OR (integer only with unsigned result).\n*   `ClampXor()` - Bitwise XOR (integer only with unsigned result).\n*   `ClampMax()` - Maximum of supplied arguments.\n*   `ClampMin()` - Minimum of supplied arguments.\n\nThe following is a general utility method that is useful for converting\nto a `ClampedNumeric` type:\n\n*   `MakeClampedNum()` - Creates a new `ClampedNumeric` from the underlying type\n    of the supplied arithmetic or directly convertible type.\n"
  },
  {
    "path": "tachyon/base/numerics/checked_math.h",
    "content": "// Copyright 2017 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_NUMERICS_CHECKED_MATH_H_\n#define TACHYON_BASE_NUMERICS_CHECKED_MATH_H_\n\n#include <stddef.h>\n\n#include <limits>\n#include <type_traits>\n\n#include \"tachyon/base/numerics/checked_math_impl.h\"\n\nnamespace tachyon::base {\nnamespace internal {\n\ntemplate <typename T>\nclass CheckedNumeric {\n  static_assert(std::is_arithmetic<T>::value,\n                \"CheckedNumeric<T>: T must be a numeric type.\");\n\n public:\n  template <typename Src>\n  friend class CheckedNumeric;\n\n  using type = T;\n\n  constexpr CheckedNumeric() = default;\n\n  // Copy constructor.\n  template <typename Src>\n  constexpr CheckedNumeric(const CheckedNumeric<Src>& rhs)\n      : state_(rhs.state_.value(), rhs.IsValid()) {}\n\n  // Strictly speaking, this is not necessary, but declaring this allows class\n  // template argument deduction to be used so that it is possible to simply\n  // write `CheckedNumeric(777)` instead of `CheckedNumeric<int>(777)`.\n  // NOLINTNEXTLINE(google-explicit-constructor)\n  constexpr CheckedNumeric(T value) : state_(value) {}\n\n  // This is not an explicit constructor because we implicitly upgrade regular\n  // numerics to CheckedNumerics to make them easier to use.\n  template <typename Src,\n            typename = std::enable_if_t<std::is_arithmetic<Src>::value>>\n  // NOLINTNEXTLINE(google-explicit-constructor)\n  constexpr CheckedNumeric(Src value) : state_(value) {}\n\n  // This is not an explicit constructor because we want a seamless conversion\n  // from StrictNumeric types.\n  template <typename Src>\n  // NOLINTNEXTLINE(google-explicit-constructor)\n  constexpr CheckedNumeric(StrictNumeric<Src> value)\n      : state_(static_cast<Src>(value)) {}\n\n  // IsValid() - The public API to test if a CheckedNumeric is currently valid.\n  // A range checked destination type can be supplied using the Dst template\n  // parameter.\n  template <typename Dst = T>\n  constexpr bool IsValid() const {\n    return state_.is_valid() &&\n           IsValueInRangeForNumericType<Dst>(state_.value());\n  }\n\n  // AssignIfValid(Dst) - Assigns the underlying value if it is currently valid\n  // and is within the range supported by the destination type. Returns true if\n  // successful and false otherwise.\n  template <typename Dst>\n#if defined(__clang__) || defined(__GNUC__)\n  __attribute__((warn_unused_result))\n#elif defined(_MSC_VER)\n  _Check_return_\n#endif\n  constexpr bool\n  AssignIfValid(Dst* result) const {\n    return BASE_NUMERICS_LIKELY(IsValid<Dst>())\n               ? ((*result = static_cast<Dst>(state_.value())), true)\n               : false;\n  }\n\n  // ValueOrDie() - The primary accessor for the underlying value. If the\n  // current state is not valid it will CHECK and crash.\n  // A range checked destination type can be supplied using the Dst template\n  // parameter, which will trigger a CHECK if the value is not in bounds for\n  // the destination.\n  // The CHECK behavior can be overridden by supplying a handler as a\n  // template parameter, for test code, etc. However, the handler cannot access\n  // the underlying value, and it is not available through other means.\n  template <typename Dst = T, class CheckHandler = CheckOnFailure>\n  constexpr StrictNumeric<Dst> ValueOrDie() const {\n    return BASE_NUMERICS_LIKELY(IsValid<Dst>())\n               ? static_cast<Dst>(state_.value())\n               : CheckHandler::template HandleFailure<Dst>();\n  }\n\n  // ValueOrDefault(T default_value) - A convenience method that returns the\n  // current value if the state is valid, and the supplied default_value for\n  // any other state.\n  // A range checked destination type can be supplied using the Dst template\n  // parameter. WARNING: This function may fail to compile or CHECK at runtime\n  // if the supplied default_value is not within range of the destination type.\n  template <typename Dst = T, typename Src>\n  constexpr StrictNumeric<Dst> ValueOrDefault(const Src default_value) const {\n    return BASE_NUMERICS_LIKELY(IsValid<Dst>())\n               ? static_cast<Dst>(state_.value())\n               : checked_cast<Dst>(default_value);\n  }\n\n  // Returns a checked numeric of the specified type, cast from the current\n  // CheckedNumeric. If the current state is invalid or the destination cannot\n  // represent the result then the returned CheckedNumeric will be invalid.\n  template <typename Dst>\n  constexpr CheckedNumeric<typename UnderlyingType<Dst>::type> Cast() const {\n    return *this;\n  }\n\n  // This friend method is available solely for providing more detailed logging\n  // in the tests. Do not implement it in production code, because the\n  // underlying values may change at any time.\n  template <typename U>\n  friend U GetNumericValueForTest(const CheckedNumeric<U>& src);\n\n  // Prototypes for the supported arithmetic operator overloads.\n  template <typename Src>\n  constexpr CheckedNumeric& operator+=(const Src rhs);\n  template <typename Src>\n  constexpr CheckedNumeric& operator-=(const Src rhs);\n  template <typename Src>\n  constexpr CheckedNumeric& operator*=(const Src rhs);\n  template <typename Src>\n  constexpr CheckedNumeric& operator/=(const Src rhs);\n  template <typename Src>\n  constexpr CheckedNumeric& operator%=(const Src rhs);\n  template <typename Src>\n  constexpr CheckedNumeric& operator<<=(const Src rhs);\n  template <typename Src>\n  constexpr CheckedNumeric& operator>>=(const Src rhs);\n  template <typename Src>\n  constexpr CheckedNumeric& operator&=(const Src rhs);\n  template <typename Src>\n  constexpr CheckedNumeric& operator|=(const Src rhs);\n  template <typename Src>\n  constexpr CheckedNumeric& operator^=(const Src rhs);\n\n  constexpr CheckedNumeric operator-() const {\n    // Use an optimized code path for a known run-time variable.\n    if (!IsConstantEvaluated() && std::is_signed<T>::value &&\n        std::is_floating_point<T>::value) {\n      return FastRuntimeNegate();\n    }\n    // The negation of two's complement int min is int min.\n    const bool is_valid =\n        IsValid() &&\n        (!std::is_signed<T>::value || std::is_floating_point<T>::value ||\n         NegateWrapper(state_.value()) != std::numeric_limits<T>::lowest());\n    return CheckedNumeric<T>(NegateWrapper(state_.value()), is_valid);\n  }\n\n  constexpr CheckedNumeric operator~() const {\n    return CheckedNumeric<decltype(InvertWrapper(T()))>(\n        InvertWrapper(state_.value()), IsValid());\n  }\n\n  constexpr CheckedNumeric Abs() const {\n    return !IsValueNegative(state_.value()) ? *this : -*this;\n  }\n\n  template <typename U>\n  constexpr CheckedNumeric<typename MathWrapper<CheckedMaxOp, T, U>::type> Max(\n      const U rhs) const {\n    return CheckMax(*this, rhs);\n  }\n\n  template <typename U>\n  constexpr CheckedNumeric<typename MathWrapper<CheckedMinOp, T, U>::type> Min(\n      const U rhs) const {\n    return CheckMin(*this, rhs);\n  }\n\n  // This function is available only for integral types. It returns an unsigned\n  // integer of the same width as the source type, containing the absolute value\n  // of the source, and properly handling signed min.\n  constexpr CheckedNumeric<typename UnsignedOrFloatForSize<T>::type>\n  UnsignedAbs() const {\n    return CheckedNumeric<typename UnsignedOrFloatForSize<T>::type>(\n        SafeUnsignedAbs(state_.value()), state_.is_valid());\n  }\n\n  constexpr CheckedNumeric& operator++() {\n    *this += 1;\n    return *this;\n  }\n\n  constexpr CheckedNumeric operator++(int) {\n    CheckedNumeric value = *this;\n    *this += 1;\n    return value;\n  }\n\n  constexpr CheckedNumeric& operator--() {\n    *this -= 1;\n    return *this;\n  }\n\n  constexpr CheckedNumeric operator--(int) {\n    // TODO(pkasting): Consider std::exchange() once it's constexpr in C++20.\n    const CheckedNumeric value = *this;\n    *this -= 1;\n    return value;\n  }\n\n  // These perform the actual math operations on the CheckedNumerics.\n  // Binary arithmetic operations.\n  template <template <typename, typename, typename> class M,\n            typename L,\n            typename R>\n  static constexpr CheckedNumeric MathOp(const L lhs, const R rhs) {\n    using Math = typename MathWrapper<M, L, R>::math;\n    T result = 0;\n    const bool is_valid =\n        Wrapper<L>::is_valid(lhs) && Wrapper<R>::is_valid(rhs) &&\n        Math::Do(Wrapper<L>::value(lhs), Wrapper<R>::value(rhs), &result);\n    return CheckedNumeric<T>(result, is_valid);\n  }\n\n  // Assignment arithmetic operations.\n  template <template <typename, typename, typename> class M, typename R>\n  constexpr CheckedNumeric& MathOp(const R rhs) {\n    using Math = typename MathWrapper<M, T, R>::math;\n    T result = 0;  // Using T as the destination saves a range check.\n    const bool is_valid =\n        state_.is_valid() && Wrapper<R>::is_valid(rhs) &&\n        Math::Do(state_.value(), Wrapper<R>::value(rhs), &result);\n    *this = CheckedNumeric<T>(result, is_valid);\n    return *this;\n  }\n\n private:\n  CheckedNumericState<T> state_;\n\n  CheckedNumeric FastRuntimeNegate() const {\n    T result;\n    const bool success = CheckedSubOp<T, T>::Do(T(0), state_.value(), &result);\n    return CheckedNumeric<T>(result, IsValid() && success);\n  }\n\n  template <typename Src>\n  constexpr CheckedNumeric(Src value, bool is_valid)\n      : state_(value, is_valid) {}\n\n  // These wrappers allow us to handle state the same way for both\n  // CheckedNumeric and POD arithmetic types.\n  template <typename Src>\n  struct Wrapper {\n    static constexpr bool is_valid(Src) { return true; }\n    static constexpr Src value(Src value) { return value; }\n  };\n\n  template <typename Src>\n  struct Wrapper<CheckedNumeric<Src>> {\n    static constexpr bool is_valid(const CheckedNumeric<Src> v) {\n      return v.IsValid();\n    }\n    static constexpr Src value(const CheckedNumeric<Src> v) {\n      return v.state_.value();\n    }\n  };\n\n  template <typename Src>\n  struct Wrapper<StrictNumeric<Src>> {\n    static constexpr bool is_valid(const StrictNumeric<Src>) { return true; }\n    static constexpr Src value(const StrictNumeric<Src> v) {\n      return static_cast<Src>(v);\n    }\n  };\n};\n\n// Convenience functions to avoid the ugly template disambiguator syntax.\ntemplate <typename Dst, typename Src>\nconstexpr bool IsValidForType(const CheckedNumeric<Src> value) {\n  return value.template IsValid<Dst>();\n}\n\ntemplate <typename Dst, typename Src>\nconstexpr StrictNumeric<Dst> ValueOrDieForType(\n    const CheckedNumeric<Src> value) {\n  return value.template ValueOrDie<Dst>();\n}\n\ntemplate <typename Dst, typename Src, typename Default>\nconstexpr StrictNumeric<Dst> ValueOrDefaultForType(\n    const CheckedNumeric<Src> value,\n    const Default default_value) {\n  return value.template ValueOrDefault<Dst>(default_value);\n}\n\n// Convenience wrapper to return a new CheckedNumeric from the provided\n// arithmetic or CheckedNumericType.\ntemplate <typename T>\nconstexpr CheckedNumeric<typename UnderlyingType<T>::type> MakeCheckedNum(\n    const T value) {\n  return value;\n}\n\n// These implement the variadic wrapper for the math operations.\ntemplate <template <typename, typename, typename> class M,\n          typename L,\n          typename R>\nconstexpr CheckedNumeric<typename MathWrapper<M, L, R>::type> CheckMathOp(\n    const L lhs,\n    const R rhs) {\n  using Math = typename MathWrapper<M, L, R>::math;\n  return CheckedNumeric<typename Math::result_type>::template MathOp<M>(lhs,\n                                                                        rhs);\n}\n\n// General purpose wrapper template for arithmetic operations.\ntemplate <template <typename, typename, typename> class M,\n          typename L,\n          typename R,\n          typename... Args>\nconstexpr auto CheckMathOp(const L lhs, const R rhs, const Args... args) {\n  return CheckMathOp<M>(CheckMathOp<M>(lhs, rhs), args...);\n}\n\nBASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Add, +, +=)\nBASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Sub, -, -=)\nBASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Mul, *, *=)\nBASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Div, /, /=)\nBASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Mod, %, %=)\nBASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Lsh, <<, <<=)\nBASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Rsh, >>, >>=)\nBASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, And, &, &=)\nBASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Or, |, |=)\nBASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Xor, ^, ^=)\nBASE_NUMERIC_ARITHMETIC_VARIADIC(Checked, Check, Max)\nBASE_NUMERIC_ARITHMETIC_VARIADIC(Checked, Check, Min)\n\n// These are some extra StrictNumeric operators to support simple pointer\n// arithmetic with our result types. Since wrapping on a pointer is always\n// bad, we trigger the CHECK condition here.\ntemplate <typename L, typename R>\nL* operator+(L* lhs, const StrictNumeric<R> rhs) {\n  const uintptr_t result = CheckAdd(reinterpret_cast<uintptr_t>(lhs),\n                                    CheckMul(sizeof(L), static_cast<R>(rhs)))\n                               .template ValueOrDie<uintptr_t>();\n  return reinterpret_cast<L*>(result);\n}\n\ntemplate <typename L, typename R>\nL* operator-(L* lhs, const StrictNumeric<R> rhs) {\n  const uintptr_t result = CheckSub(reinterpret_cast<uintptr_t>(lhs),\n                                    CheckMul(sizeof(L), static_cast<R>(rhs)))\n                               .template ValueOrDie<uintptr_t>();\n  return reinterpret_cast<L*>(result);\n}\n\n}  // namespace internal\n\nusing internal::CheckAdd;\nusing internal::CheckAnd;\nusing internal::CheckDiv;\nusing internal::CheckedNumeric;\nusing internal::CheckLsh;\nusing internal::CheckMax;\nusing internal::CheckMin;\nusing internal::CheckMod;\nusing internal::CheckMul;\nusing internal::CheckOr;\nusing internal::CheckRsh;\nusing internal::CheckSub;\nusing internal::CheckXor;\nusing internal::IsValidForType;\nusing internal::MakeCheckedNum;\nusing internal::ValueOrDefaultForType;\nusing internal::ValueOrDieForType;\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_NUMERICS_CHECKED_MATH_H_\n"
  },
  {
    "path": "tachyon/base/numerics/checked_math_impl.h",
    "content": "// Copyright 2017 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_NUMERICS_CHECKED_MATH_IMPL_H_\n#define TACHYON_BASE_NUMERICS_CHECKED_MATH_IMPL_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <climits>\n#include <cmath>\n#include <cstdlib>\n#include <limits>\n#include <type_traits>\n\n#include \"tachyon/base/numerics/safe_conversions.h\"\n#include \"tachyon/base/numerics/safe_math_shared_impl.h\"\n\nnamespace tachyon::base {\nnamespace internal {\n\ntemplate <typename T>\nconstexpr bool CheckedAddImpl(T x, T y, T* result) {\n  static_assert(std::is_integral<T>::value, \"Type must be integral\");\n  // Since the value of x+y is undefined if we have a signed type, we compute\n  // it using the unsigned type of the same size.\n  using UnsignedDst = typename std::make_unsigned<T>::type;\n  using SignedDst = typename std::make_signed<T>::type;\n  const UnsignedDst ux = static_cast<UnsignedDst>(x);\n  const UnsignedDst uy = static_cast<UnsignedDst>(y);\n  const UnsignedDst uresult = static_cast<UnsignedDst>(ux + uy);\n  // Addition is valid if the sign of (x + y) is equal to either that of x or\n  // that of y.\n  if (std::is_signed<T>::value\n          ? static_cast<SignedDst>((uresult ^ ux) & (uresult ^ uy)) < 0\n          : uresult < uy)  // Unsigned is either valid or underflow.\n    return false;\n  *result = static_cast<T>(uresult);\n  return true;\n}\n\ntemplate <typename T, typename U, class Enable = void>\nstruct CheckedAddOp {};\n\ntemplate <typename T, typename U>\nstruct CheckedAddOp<T,\n                    U,\n                    typename std::enable_if<std::is_integral<T>::value &&\n                                            std::is_integral<U>::value>::type> {\n  using result_type = typename MaxExponentPromotion<T, U>::type;\n  template <typename V>\n  static constexpr bool Do(T x, U y, V* result) {\n    if constexpr (CheckedAddFastOp<T, U>::is_supported)\n      return CheckedAddFastOp<T, U>::Do(x, y, result);\n\n    // Double the underlying type up to a full machine word.\n    using FastPromotion = typename FastIntegerArithmeticPromotion<T, U>::type;\n    // NOTE(chokobole): To make it build with nvcc.\n    constexpr bool cond = IntegerBitsPlusSign<FastPromotion>::value > IntegerBitsPlusSign<intptr_t>::value;\n    using Promotion =\n        typename std::conditional<cond,\n                                  typename BigEnoughPromotion<T, U>::type,\n                                  FastPromotion>::type;\n    // Fail if either operand is out of range for the promoted type.\n    // TODO(jschuh): This could be made to work for a broader range of values.\n    if (BASE_NUMERICS_UNLIKELY(!IsValueInRangeForNumericType<Promotion>(x) ||\n                               !IsValueInRangeForNumericType<Promotion>(y))) {\n      return false;\n    }\n\n    Promotion presult = {};\n    bool is_valid = true;\n    if (IsIntegerArithmeticSafe<Promotion, T, U>::value) {\n      presult = static_cast<Promotion>(x) + static_cast<Promotion>(y);\n    } else {\n      is_valid = CheckedAddImpl(static_cast<Promotion>(x),\n                                static_cast<Promotion>(y), &presult);\n    }\n    if (!is_valid || !IsValueInRangeForNumericType<V>(presult))\n      return false;\n    *result = static_cast<V>(presult);\n    return true;\n  }\n};\n\ntemplate <typename T>\nconstexpr bool CheckedSubImpl(T x, T y, T* result) {\n  static_assert(std::is_integral<T>::value, \"Type must be integral\");\n  // Since the value of x+y is undefined if we have a signed type, we compute\n  // it using the unsigned type of the same size.\n  using UnsignedDst = typename std::make_unsigned<T>::type;\n  using SignedDst = typename std::make_signed<T>::type;\n  const UnsignedDst ux = static_cast<UnsignedDst>(x);\n  const UnsignedDst uy = static_cast<UnsignedDst>(y);\n  const UnsignedDst uresult = static_cast<UnsignedDst>(ux - uy);\n  // Subtraction is valid if either x and y have same sign, or (x-y) and x have\n  // the same sign.\n  if (std::is_signed<T>::value\n          ? static_cast<SignedDst>((uresult ^ ux) & (ux ^ uy)) < 0\n          : x < y)\n    return false;\n  *result = static_cast<T>(uresult);\n  return true;\n}\n\ntemplate <typename T, typename U, class Enable = void>\nstruct CheckedSubOp {};\n\ntemplate <typename T, typename U>\nstruct CheckedSubOp<T,\n                    U,\n                    typename std::enable_if<std::is_integral<T>::value &&\n                                            std::is_integral<U>::value>::type> {\n  using result_type = typename MaxExponentPromotion<T, U>::type;\n  template <typename V>\n  static constexpr bool Do(T x, U y, V* result) {\n    if constexpr (CheckedSubFastOp<T, U>::is_supported)\n      return CheckedSubFastOp<T, U>::Do(x, y, result);\n\n    // Double the underlying type up to a full machine word.\n    using FastPromotion = typename FastIntegerArithmeticPromotion<T, U>::type;\n    // NOTE(chokobole): To make it build with nvcc.\n    constexpr bool cond = IntegerBitsPlusSign<FastPromotion>::value > IntegerBitsPlusSign<intptr_t>::value;\n    using Promotion =\n        typename std::conditional<cond,\n                                  typename BigEnoughPromotion<T, U>::type,\n                                  FastPromotion>::type;\n    // Fail if either operand is out of range for the promoted type.\n    // TODO(jschuh): This could be made to work for a broader range of values.\n    if (BASE_NUMERICS_UNLIKELY(!IsValueInRangeForNumericType<Promotion>(x) ||\n                               !IsValueInRangeForNumericType<Promotion>(y))) {\n      return false;\n    }\n\n    Promotion presult = {};\n    bool is_valid = true;\n    if (IsIntegerArithmeticSafe<Promotion, T, U>::value) {\n      presult = static_cast<Promotion>(x) - static_cast<Promotion>(y);\n    } else {\n      is_valid = CheckedSubImpl(static_cast<Promotion>(x),\n                                static_cast<Promotion>(y), &presult);\n    }\n    if (!is_valid || !IsValueInRangeForNumericType<V>(presult))\n      return false;\n    *result = static_cast<V>(presult);\n    return true;\n  }\n};\n\ntemplate <typename T>\nconstexpr bool CheckedMulImpl(T x, T y, T* result) {\n  static_assert(std::is_integral<T>::value, \"Type must be integral\");\n  // Since the value of x*y is potentially undefined if we have a signed type,\n  // we compute it using the unsigned type of the same size.\n  using UnsignedDst = typename std::make_unsigned<T>::type;\n  using SignedDst = typename std::make_signed<T>::type;\n  const UnsignedDst ux = SafeUnsignedAbs(x);\n  const UnsignedDst uy = SafeUnsignedAbs(y);\n  const UnsignedDst uresult = static_cast<UnsignedDst>(ux * uy);\n  const bool is_negative =\n      std::is_signed<T>::value && static_cast<SignedDst>(x ^ y) < 0;\n  // We have a fast out for unsigned identity or zero on the second operand.\n  // After that it's an unsigned overflow check on the absolute value, with\n  // a +1 bound for a negative result.\n  if (uy > UnsignedDst(!std::is_signed<T>::value || is_negative) &&\n      ux > (std::numeric_limits<T>::max() + UnsignedDst(is_negative)) / uy)\n    return false;\n  *result = static_cast<T>(is_negative ? 0 - uresult : uresult);\n  return true;\n}\n\ntemplate <typename T, typename U, class Enable = void>\nstruct CheckedMulOp {};\n\ntemplate <typename T, typename U>\nstruct CheckedMulOp<T,\n                    U,\n                    typename std::enable_if<std::is_integral<T>::value &&\n                                            std::is_integral<U>::value>::type> {\n  using result_type = typename MaxExponentPromotion<T, U>::type;\n  template <typename V>\n  static constexpr bool Do(T x, U y, V* result) {\n    if constexpr (CheckedMulFastOp<T, U>::is_supported)\n      return CheckedMulFastOp<T, U>::Do(x, y, result);\n\n    using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type;\n    // Verify the destination type can hold the result (always true for 0).\n    if (BASE_NUMERICS_UNLIKELY((!IsValueInRangeForNumericType<Promotion>(x) ||\n                                !IsValueInRangeForNumericType<Promotion>(y)) &&\n                               x && y)) {\n      return false;\n    }\n\n    Promotion presult = {};\n    bool is_valid = true;\n    if (CheckedMulFastOp<Promotion, Promotion>::is_supported) {\n      // The fast op may be available with the promoted type.\n      // The casts here are safe because of the \"value in range\" conditional\n      // above.\n      is_valid = CheckedMulFastOp<Promotion, Promotion>::Do(\n          static_cast<Promotion>(x), static_cast<Promotion>(y), &presult);\n    } else if (IsIntegerArithmeticSafe<Promotion, T, U>::value) {\n      presult = static_cast<Promotion>(x) * static_cast<Promotion>(y);\n    } else {\n      is_valid = CheckedMulImpl(static_cast<Promotion>(x),\n                                static_cast<Promotion>(y), &presult);\n    }\n    if (!is_valid || !IsValueInRangeForNumericType<V>(presult))\n      return false;\n    *result = static_cast<V>(presult);\n    return true;\n  }\n};\n\n// Division just requires a check for a zero denominator or an invalid negation\n// on signed min/-1.\ntemplate <typename T, typename U, class Enable = void>\nstruct CheckedDivOp {};\n\ntemplate <typename T, typename U>\nstruct CheckedDivOp<T,\n                    U,\n                    typename std::enable_if<std::is_integral<T>::value &&\n                                            std::is_integral<U>::value>::type> {\n  using result_type = typename MaxExponentPromotion<T, U>::type;\n  template <typename V>\n  static constexpr bool Do(T x, U y, V* result) {\n    if (BASE_NUMERICS_UNLIKELY(!y))\n      return false;\n\n    // The overflow check can be compiled away if we don't have the exact\n    // combination of types needed to trigger this case.\n    using Promotion = typename BigEnoughPromotion<T, U>::type;\n    if (BASE_NUMERICS_UNLIKELY(\n            (std::is_signed<T>::value && std::is_signed<U>::value &&\n             IsTypeInRangeForNumericType<T, Promotion>::value &&\n             static_cast<Promotion>(x) ==\n                 std::numeric_limits<Promotion>::lowest() &&\n             y == static_cast<U>(-1)))) {\n      return false;\n    }\n\n    // This branch always compiles away if the above branch wasn't removed.\n    if (BASE_NUMERICS_UNLIKELY((!IsValueInRangeForNumericType<Promotion>(x) ||\n                                !IsValueInRangeForNumericType<Promotion>(y)) &&\n                               x)) {\n      return false;\n    }\n\n    const Promotion presult = Promotion(x) / Promotion(y);\n    if (!IsValueInRangeForNumericType<V>(presult))\n      return false;\n    *result = static_cast<V>(presult);\n    return true;\n  }\n};\n\ntemplate <typename T, typename U, class Enable = void>\nstruct CheckedModOp {};\n\ntemplate <typename T, typename U>\nstruct CheckedModOp<T,\n                    U,\n                    typename std::enable_if<std::is_integral<T>::value &&\n                                            std::is_integral<U>::value>::type> {\n  using result_type = typename MaxExponentPromotion<T, U>::type;\n  template <typename V>\n  static constexpr bool Do(T x, U y, V* result) {\n    if (BASE_NUMERICS_UNLIKELY(!y))\n      return false;\n\n    using Promotion = typename BigEnoughPromotion<T, U>::type;\n    if (BASE_NUMERICS_UNLIKELY(\n            (std::is_signed<T>::value && std::is_signed<U>::value &&\n             IsTypeInRangeForNumericType<T, Promotion>::value &&\n             static_cast<Promotion>(x) ==\n                 std::numeric_limits<Promotion>::lowest() &&\n             y == static_cast<U>(-1)))) {\n      *result = 0;\n      return true;\n    }\n\n    const Promotion presult =\n        static_cast<Promotion>(x) % static_cast<Promotion>(y);\n    if (!IsValueInRangeForNumericType<V>(presult))\n      return false;\n    *result = static_cast<Promotion>(presult);\n    return true;\n  }\n};\n\ntemplate <typename T, typename U, class Enable = void>\nstruct CheckedLshOp {};\n\n// Left shift. Shifts less than 0 or greater than or equal to the number\n// of bits in the promoted type are undefined. Shifts of negative values\n// are undefined. Otherwise it is defined when the result fits.\ntemplate <typename T, typename U>\nstruct CheckedLshOp<T,\n                    U,\n                    typename std::enable_if<std::is_integral<T>::value &&\n                                            std::is_integral<U>::value>::type> {\n  using result_type = T;\n  template <typename V>\n  static constexpr bool Do(T x, U shift, V* result) {\n    // Disallow negative numbers and verify the shift is in bounds.\n    if (BASE_NUMERICS_LIKELY(!IsValueNegative(x) &&\n                             as_unsigned(shift) <\n                                 as_unsigned(std::numeric_limits<T>::digits))) {\n      // Shift as unsigned to avoid undefined behavior.\n      *result = static_cast<V>(as_unsigned(x) << shift);\n      // If the shift can be reversed, we know it was valid.\n      return *result >> shift == x;\n    }\n\n    // Handle the legal corner-case of a full-width signed shift of zero.\n    if (!std::is_signed<T>::value || x ||\n        as_unsigned(shift) != as_unsigned(std::numeric_limits<T>::digits))\n      return false;\n    *result = 0;\n    return true;\n  }\n};\n\ntemplate <typename T, typename U, class Enable = void>\nstruct CheckedRshOp {};\n\n// Right shift. Shifts less than 0 or greater than or equal to the number\n// of bits in the promoted type are undefined. Otherwise, it is always defined,\n// but a right shift of a negative value is implementation-dependent.\ntemplate <typename T, typename U>\nstruct CheckedRshOp<T,\n                    U,\n                    typename std::enable_if<std::is_integral<T>::value &&\n                                            std::is_integral<U>::value>::type> {\n  using result_type = T;\n  template <typename V>\n  static constexpr bool Do(T x, U shift, V* result) {\n    // Use sign conversion to push negative values out of range.\n    if (BASE_NUMERICS_UNLIKELY(as_unsigned(shift) >=\n                               IntegerBitsPlusSign<T>::value)) {\n      return false;\n    }\n\n    const T tmp = x >> shift;\n    if (!IsValueInRangeForNumericType<V>(tmp))\n      return false;\n    *result = static_cast<V>(tmp);\n    return true;\n  }\n};\n\ntemplate <typename T, typename U, class Enable = void>\nstruct CheckedAndOp {};\n\n// For simplicity we support only unsigned integer results.\ntemplate <typename T, typename U>\nstruct CheckedAndOp<T,\n                    U,\n                    typename std::enable_if<std::is_integral<T>::value &&\n                                            std::is_integral<U>::value>::type> {\n  using result_type = typename std::make_unsigned<\n      typename MaxExponentPromotion<T, U>::type>::type;\n  template <typename V>\n  static constexpr bool Do(T x, U y, V* result) {\n    const result_type tmp =\n        static_cast<result_type>(x) & static_cast<result_type>(y);\n    if (!IsValueInRangeForNumericType<V>(tmp))\n      return false;\n    *result = static_cast<V>(tmp);\n    return true;\n  }\n};\n\ntemplate <typename T, typename U, class Enable = void>\nstruct CheckedOrOp {};\n\n// For simplicity we support only unsigned integers.\ntemplate <typename T, typename U>\nstruct CheckedOrOp<T,\n                   U,\n                   typename std::enable_if<std::is_integral<T>::value &&\n                                           std::is_integral<U>::value>::type> {\n  using result_type = typename std::make_unsigned<\n      typename MaxExponentPromotion<T, U>::type>::type;\n  template <typename V>\n  static constexpr bool Do(T x, U y, V* result) {\n    const result_type tmp =\n        static_cast<result_type>(x) | static_cast<result_type>(y);\n    if (!IsValueInRangeForNumericType<V>(tmp))\n      return false;\n    *result = static_cast<V>(tmp);\n    return true;\n  }\n};\n\ntemplate <typename T, typename U, class Enable = void>\nstruct CheckedXorOp {};\n\n// For simplicity we support only unsigned integers.\ntemplate <typename T, typename U>\nstruct CheckedXorOp<T,\n                    U,\n                    typename std::enable_if<std::is_integral<T>::value &&\n                                            std::is_integral<U>::value>::type> {\n  using result_type = typename std::make_unsigned<\n      typename MaxExponentPromotion<T, U>::type>::type;\n  template <typename V>\n  static constexpr bool Do(T x, U y, V* result) {\n    const result_type tmp =\n        static_cast<result_type>(x) ^ static_cast<result_type>(y);\n    if (!IsValueInRangeForNumericType<V>(tmp))\n      return false;\n    *result = static_cast<V>(tmp);\n    return true;\n  }\n};\n\n// Max doesn't really need to be implemented this way because it can't fail,\n// but it makes the code much cleaner to use the MathOp wrappers.\ntemplate <typename T, typename U, class Enable = void>\nstruct CheckedMaxOp {};\n\ntemplate <typename T, typename U>\nstruct CheckedMaxOp<\n    T,\n    U,\n    typename std::enable_if<std::is_arithmetic<T>::value &&\n                            std::is_arithmetic<U>::value>::type> {\n  using result_type = typename MaxExponentPromotion<T, U>::type;\n  template <typename V>\n  static constexpr bool Do(T x, U y, V* result) {\n    const result_type tmp = IsGreater<T, U>::Test(x, y)\n                                ? static_cast<result_type>(x)\n                                : static_cast<result_type>(y);\n    if (!IsValueInRangeForNumericType<V>(tmp))\n      return false;\n    *result = static_cast<V>(tmp);\n    return true;\n  }\n};\n\n// Min doesn't really need to be implemented this way because it can't fail,\n// but it makes the code much cleaner to use the MathOp wrappers.\ntemplate <typename T, typename U, class Enable = void>\nstruct CheckedMinOp {};\n\ntemplate <typename T, typename U>\nstruct CheckedMinOp<\n    T,\n    U,\n    typename std::enable_if<std::is_arithmetic<T>::value &&\n                            std::is_arithmetic<U>::value>::type> {\n  using result_type = typename LowestValuePromotion<T, U>::type;\n  template <typename V>\n  static constexpr bool Do(T x, U y, V* result) {\n    const result_type tmp = IsLess<T, U>::Test(x, y)\n                                ? static_cast<result_type>(x)\n                                : static_cast<result_type>(y);\n    if (!IsValueInRangeForNumericType<V>(tmp))\n      return false;\n    *result = static_cast<V>(tmp);\n    return true;\n  }\n};\n\n// This is just boilerplate that wraps the standard floating point arithmetic.\n// A macro isn't the nicest solution, but it beats rewriting these repeatedly.\n#define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP)                              \\\n  template <typename T, typename U>                                      \\\n  struct Checked##NAME##Op<                                              \\\n      T, U,                                                              \\\n      typename std::enable_if<std::is_floating_point<T>::value ||        \\\n                              std::is_floating_point<U>::value>::type> { \\\n    using result_type = typename MaxExponentPromotion<T, U>::type;       \\\n    template <typename V>                                                \\\n    static constexpr bool Do(T x, U y, V* result) {                      \\\n      using Promotion = typename MaxExponentPromotion<T, U>::type;       \\\n      const Promotion presult = x OP y;                                  \\\n      if (!IsValueInRangeForNumericType<V>(presult))                     \\\n        return false;                                                    \\\n      *result = static_cast<V>(presult);                                 \\\n      return true;                                                       \\\n    }                                                                    \\\n  };\n\nBASE_FLOAT_ARITHMETIC_OPS(Add, +)\nBASE_FLOAT_ARITHMETIC_OPS(Sub, -)\nBASE_FLOAT_ARITHMETIC_OPS(Mul, *)\nBASE_FLOAT_ARITHMETIC_OPS(Div, /)\n\n#undef BASE_FLOAT_ARITHMETIC_OPS\n\n// Floats carry around their validity state with them, but integers do not. So,\n// we wrap the underlying value in a specialization in order to hide that detail\n// and expose an interface via accessors.\nenum NumericRepresentation {\n  NUMERIC_INTEGER,\n  NUMERIC_FLOATING,\n  NUMERIC_UNKNOWN\n};\n\ntemplate <typename NumericType>\nstruct GetNumericRepresentation {\n  static const NumericRepresentation value =\n      std::is_integral<NumericType>::value\n          ? NUMERIC_INTEGER\n          : (std::is_floating_point<NumericType>::value ? NUMERIC_FLOATING\n                                                        : NUMERIC_UNKNOWN);\n};\n\ntemplate <typename T,\n          NumericRepresentation type = GetNumericRepresentation<T>::value>\nclass CheckedNumericState {};\n\n// Integrals require quite a bit of additional housekeeping to manage state.\ntemplate <typename T>\nclass CheckedNumericState<T, NUMERIC_INTEGER> {\n public:\n  template <typename Src = int>\n  constexpr explicit CheckedNumericState(Src value = 0, bool is_valid = true)\n      : is_valid_(is_valid && IsValueInRangeForNumericType<T>(value)),\n        value_(WellDefinedConversionOrZero(value, is_valid_)) {\n    static_assert(std::is_arithmetic<Src>::value, \"Argument must be numeric.\");\n  }\n\n  template <typename Src>\n  constexpr CheckedNumericState(const CheckedNumericState<Src>& rhs)\n      : CheckedNumericState(rhs.value(), rhs.is_valid()) {}\n\n  constexpr bool is_valid() const { return is_valid_; }\n\n  constexpr T value() const { return value_; }\n\n private:\n  // Ensures that a type conversion does not trigger undefined behavior.\n  template <typename Src>\n  static constexpr T WellDefinedConversionOrZero(Src value, bool is_valid) {\n    using SrcType = typename internal::UnderlyingType<Src>::type;\n    return (std::is_integral<SrcType>::value || is_valid)\n               ? static_cast<T>(value)\n               : 0;\n  }\n\n  // is_valid_ precedes value_ because member initializers in the constructors\n  // are evaluated in field order, and is_valid_ must be read when initializing\n  // value_.\n  bool is_valid_;\n  T value_;\n};\n\n// Floating points maintain their own validity, but need translation wrappers.\ntemplate <typename T>\nclass CheckedNumericState<T, NUMERIC_FLOATING> {\n public:\n  template <typename Src = double>\n  constexpr explicit CheckedNumericState(Src value = 0.0, bool is_valid = true)\n      : value_(WellDefinedConversionOrNaN(\n            value,\n            is_valid && IsValueInRangeForNumericType<T>(value))) {}\n\n  template <typename Src>\n  constexpr CheckedNumericState(const CheckedNumericState<Src>& rhs)\n      : CheckedNumericState(rhs.value(), rhs.is_valid()) {}\n\n  constexpr bool is_valid() const {\n    // Written this way because std::isfinite is not reliably constexpr.\n    return IsConstantEvaluated()\n               ? value_ <= std::numeric_limits<T>::max() &&\n                     value_ >= std::numeric_limits<T>::lowest()\n               : std::isfinite(value_);\n  }\n\n  constexpr T value() const { return value_; }\n\n private:\n  // Ensures that a type conversion does not trigger undefined behavior.\n  template <typename Src>\n  static constexpr T WellDefinedConversionOrNaN(Src value, bool is_valid) {\n    using SrcType = typename internal::UnderlyingType<Src>::type;\n    return (StaticDstRangeRelationToSrcRange<T, SrcType>::value ==\n                NUMERIC_RANGE_CONTAINED ||\n            is_valid)\n               ? static_cast<T>(value)\n               : std::numeric_limits<T>::quiet_NaN();\n  }\n\n  T value_;\n};\n\n}  // namespace internal\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_NUMERICS_CHECKED_MATH_IMPL_H_\n"
  },
  {
    "path": "tachyon/base/numerics/clamped_math.h",
    "content": "// Copyright 2017 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_NUMERICS_CLAMPED_MATH_H_\n#define TACHYON_BASE_NUMERICS_CLAMPED_MATH_H_\n\n#include <stddef.h>\n\n#include <limits>\n#include <type_traits>\n\n#include \"tachyon/base/numerics/clamped_math_impl.h\"\n\nnamespace tachyon::base {\nnamespace internal {\n\ntemplate <typename T>\nclass ClampedNumeric {\n  static_assert(std::is_arithmetic<T>::value,\n                \"ClampedNumeric<T>: T must be a numeric type.\");\n\n public:\n  using type = T;\n\n  constexpr ClampedNumeric() : value_(0) {}\n\n  // Copy constructor.\n  template <typename Src>\n  constexpr ClampedNumeric(const ClampedNumeric<Src>& rhs)\n      : value_(saturated_cast<T>(rhs.value_)) {}\n\n  template <typename Src>\n  friend class ClampedNumeric;\n\n  // Strictly speaking, this is not necessary, but declaring this allows class\n  // template argument deduction to be used so that it is possible to simply\n  // write `ClampedNumeric(777)` instead of `ClampedNumeric<int>(777)`.\n  // NOLINTNEXTLINE(google-explicit-constructor)\n  constexpr ClampedNumeric(T value) : value_(value) {}\n\n  // This is not an explicit constructor because we implicitly upgrade regular\n  // numerics to ClampedNumerics to make them easier to use.\n  template <typename Src>\n  // NOLINTNEXTLINE(google-explicit-constructor)\n  constexpr ClampedNumeric(Src value) : value_(saturated_cast<T>(value)) {\n    static_assert(UnderlyingType<Src>::is_numeric, \"Argument must be numeric.\");\n  }\n\n  // This is not an explicit constructor because we want a seamless conversion\n  // from StrictNumeric types.\n  template <typename Src>\n  // NOLINTNEXTLINE(google-explicit-constructor)\n  constexpr ClampedNumeric(StrictNumeric<Src> value)\n      : value_(saturated_cast<T>(static_cast<Src>(value))) {}\n\n  // Returns a ClampedNumeric of the specified type, cast from the current\n  // ClampedNumeric, and saturated to the destination type.\n  template <typename Dst>\n  constexpr ClampedNumeric<typename UnderlyingType<Dst>::type> Cast() const {\n    return *this;\n  }\n\n  // Prototypes for the supported arithmetic operator overloads.\n  template <typename Src>\n  constexpr ClampedNumeric& operator+=(const Src rhs);\n  template <typename Src>\n  constexpr ClampedNumeric& operator-=(const Src rhs);\n  template <typename Src>\n  constexpr ClampedNumeric& operator*=(const Src rhs);\n  template <typename Src>\n  constexpr ClampedNumeric& operator/=(const Src rhs);\n  template <typename Src>\n  constexpr ClampedNumeric& operator%=(const Src rhs);\n  template <typename Src>\n  constexpr ClampedNumeric& operator<<=(const Src rhs);\n  template <typename Src>\n  constexpr ClampedNumeric& operator>>=(const Src rhs);\n  template <typename Src>\n  constexpr ClampedNumeric& operator&=(const Src rhs);\n  template <typename Src>\n  constexpr ClampedNumeric& operator|=(const Src rhs);\n  template <typename Src>\n  constexpr ClampedNumeric& operator^=(const Src rhs);\n\n  constexpr ClampedNumeric operator-() const {\n    // The negation of two's complement int min is int min, so that's the\n    // only overflow case where we will saturate.\n    return ClampedNumeric<T>(SaturatedNegWrapper(value_));\n  }\n\n  constexpr ClampedNumeric operator~() const {\n    return ClampedNumeric<decltype(InvertWrapper(T()))>(InvertWrapper(value_));\n  }\n\n  constexpr ClampedNumeric Abs() const {\n    // The negation of two's complement int min is int min, so that's the\n    // only overflow case where we will saturate.\n    return ClampedNumeric<T>(SaturatedAbsWrapper(value_));\n  }\n\n  template <typename U>\n  constexpr ClampedNumeric<typename MathWrapper<ClampedMaxOp, T, U>::type> Max(\n      const U rhs) const {\n    using result_type = typename MathWrapper<ClampedMaxOp, T, U>::type;\n    return ClampedNumeric<result_type>(\n        ClampedMaxOp<T, U>::Do(value_, Wrapper<U>::value(rhs)));\n  }\n\n  template <typename U>\n  constexpr ClampedNumeric<typename MathWrapper<ClampedMinOp, T, U>::type> Min(\n      const U rhs) const {\n    using result_type = typename MathWrapper<ClampedMinOp, T, U>::type;\n    return ClampedNumeric<result_type>(\n        ClampedMinOp<T, U>::Do(value_, Wrapper<U>::value(rhs)));\n  }\n\n  // This function is available only for integral types. It returns an unsigned\n  // integer of the same width as the source type, containing the absolute value\n  // of the source, and properly handling signed min.\n  constexpr ClampedNumeric<typename UnsignedOrFloatForSize<T>::type>\n  UnsignedAbs() const {\n    return ClampedNumeric<typename UnsignedOrFloatForSize<T>::type>(\n        SafeUnsignedAbs(value_));\n  }\n\n  constexpr ClampedNumeric& operator++() {\n    *this += 1;\n    return *this;\n  }\n\n  constexpr ClampedNumeric operator++(int) {\n    ClampedNumeric value = *this;\n    *this += 1;\n    return value;\n  }\n\n  constexpr ClampedNumeric& operator--() {\n    *this -= 1;\n    return *this;\n  }\n\n  constexpr ClampedNumeric operator--(int) {\n    ClampedNumeric value = *this;\n    *this -= 1;\n    return value;\n  }\n\n  // These perform the actual math operations on the ClampedNumerics.\n  // Binary arithmetic operations.\n  template <template <typename, typename, typename> class M,\n            typename L,\n            typename R>\n  static constexpr ClampedNumeric MathOp(const L lhs, const R rhs) {\n    using Math = typename MathWrapper<M, L, R>::math;\n    return ClampedNumeric<T>(\n        Math::template Do<T>(Wrapper<L>::value(lhs), Wrapper<R>::value(rhs)));\n  }\n\n  // Assignment arithmetic operations.\n  template <template <typename, typename, typename> class M, typename R>\n  constexpr ClampedNumeric& MathOp(const R rhs) {\n    using Math = typename MathWrapper<M, T, R>::math;\n    *this =\n        ClampedNumeric<T>(Math::template Do<T>(value_, Wrapper<R>::value(rhs)));\n    return *this;\n  }\n\n  template <typename Dst>\n  constexpr operator Dst() const {\n    return saturated_cast<typename ArithmeticOrUnderlyingEnum<Dst>::type>(\n        value_);\n  }\n\n  // This method extracts the raw integer value without saturating it to the\n  // destination type as the conversion operator does. This is useful when\n  // e.g. assigning to an auto type or passing as a deduced template parameter.\n  constexpr T RawValue() const { return value_; }\n\n private:\n  T value_;\n\n  // These wrappers allow us to handle state the same way for both\n  // ClampedNumeric and POD arithmetic types.\n  template <typename Src>\n  struct Wrapper {\n    static constexpr typename UnderlyingType<Src>::type value(Src value) {\n      return value;\n    }\n  };\n};\n\n// Convenience wrapper to return a new ClampedNumeric from the provided\n// arithmetic or ClampedNumericType.\ntemplate <typename T>\nconstexpr ClampedNumeric<typename UnderlyingType<T>::type> MakeClampedNum(\n    const T value) {\n  return value;\n}\n\n// These implement the variadic wrapper for the math operations.\ntemplate <template <typename, typename, typename> class M,\n          typename L,\n          typename R>\nconstexpr ClampedNumeric<typename MathWrapper<M, L, R>::type> ClampMathOp(\n    const L lhs,\n    const R rhs) {\n  using Math = typename MathWrapper<M, L, R>::math;\n  return ClampedNumeric<typename Math::result_type>::template MathOp<M>(lhs,\n                                                                        rhs);\n}\n\n// General purpose wrapper template for arithmetic operations.\ntemplate <template <typename, typename, typename> class M,\n          typename L,\n          typename R,\n          typename... Args>\nconstexpr auto ClampMathOp(const L lhs, const R rhs, const Args... args) {\n  return ClampMathOp<M>(ClampMathOp<M>(lhs, rhs), args...);\n}\n\nBASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Add, +, +=)\nBASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Sub, -, -=)\nBASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Mul, *, *=)\nBASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Div, /, /=)\nBASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Mod, %, %=)\nBASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Lsh, <<, <<=)\nBASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Rsh, >>, >>=)\nBASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, And, &, &=)\nBASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Or, |, |=)\nBASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Xor, ^, ^=)\nBASE_NUMERIC_ARITHMETIC_VARIADIC(Clamped, Clamp, Max)\nBASE_NUMERIC_ARITHMETIC_VARIADIC(Clamped, Clamp, Min)\nBASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsLess, <)\nBASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsLessOrEqual, <=)\nBASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsGreater, >)\nBASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsGreaterOrEqual, >=)\nBASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsEqual, ==)\nBASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsNotEqual, !=)\n\n}  // namespace internal\n\nusing internal::ClampAdd;\nusing internal::ClampAnd;\nusing internal::ClampDiv;\nusing internal::ClampedNumeric;\nusing internal::ClampLsh;\nusing internal::ClampMax;\nusing internal::ClampMin;\nusing internal::ClampMod;\nusing internal::ClampMul;\nusing internal::ClampOr;\nusing internal::ClampRsh;\nusing internal::ClampSub;\nusing internal::ClampXor;\nusing internal::MakeClampedNum;\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_NUMERICS_CLAMPED_MATH_H_\n"
  },
  {
    "path": "tachyon/base/numerics/clamped_math_impl.h",
    "content": "// Copyright 2017 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_NUMERICS_CLAMPED_MATH_IMPL_H_\n#define TACHYON_BASE_NUMERICS_CLAMPED_MATH_IMPL_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <climits>\n#include <cmath>\n#include <cstdlib>\n#include <limits>\n#include <type_traits>\n\n#include \"tachyon/base/numerics/checked_math.h\"\n#include \"tachyon/base/numerics/safe_conversions.h\"\n#include \"tachyon/base/numerics/safe_math_shared_impl.h\"\n\nnamespace tachyon::base {\nnamespace internal {\n\ntemplate <typename T,\n          typename std::enable_if<std::is_integral<T>::value &&\n                                  std::is_signed<T>::value>::type* = nullptr>\nconstexpr T SaturatedNegWrapper(T value) {\n  return IsConstantEvaluated() || !ClampedNegFastOp<T>::is_supported\n             ? (NegateWrapper(value) != std::numeric_limits<T>::lowest()\n                    ? NegateWrapper(value)\n                    : std::numeric_limits<T>::max())\n             : ClampedNegFastOp<T>::Do(value);\n}\n\ntemplate <typename T,\n          typename std::enable_if<std::is_integral<T>::value &&\n                                  !std::is_signed<T>::value>::type* = nullptr>\nconstexpr T SaturatedNegWrapper(T value) {\n  return T(0);\n}\n\ntemplate <\n    typename T,\n    typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>\nconstexpr T SaturatedNegWrapper(T value) {\n  return -value;\n}\n\ntemplate <typename T,\n          typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>\nconstexpr T SaturatedAbsWrapper(T value) {\n  // The calculation below is a static identity for unsigned types, but for\n  // signed integer types it provides a non-branching, saturated absolute value.\n  // This works because SafeUnsignedAbs() returns an unsigned type, which can\n  // represent the absolute value of all negative numbers of an equal-width\n  // integer type. The call to IsValueNegative() then detects overflow in the\n  // special case of numeric_limits<T>::min(), by evaluating the bit pattern as\n  // a signed integer value. If it is the overflow case, we end up subtracting\n  // one from the unsigned result, thus saturating to numeric_limits<T>::max().\n  return static_cast<T>(\n      SafeUnsignedAbs(value) -\n      IsValueNegative<T>(static_cast<T>(SafeUnsignedAbs(value))));\n}\n\ntemplate <\n    typename T,\n    typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>\nconstexpr T SaturatedAbsWrapper(T value) {\n  return value < 0 ? -value : value;\n}\n\ntemplate <typename T, typename U, class Enable = void>\nstruct ClampedAddOp {};\n\ntemplate <typename T, typename U>\nstruct ClampedAddOp<T,\n                    U,\n                    typename std::enable_if<std::is_integral<T>::value &&\n                                            std::is_integral<U>::value>::type> {\n  using result_type = typename MaxExponentPromotion<T, U>::type;\n  template <typename V = result_type>\n  static constexpr V Do(T x, U y) {\n    if (!IsConstantEvaluated() && ClampedAddFastOp<T, U>::is_supported)\n      return ClampedAddFastOp<T, U>::template Do<V>(x, y);\n\n    static_assert(std::is_same<V, result_type>::value ||\n                      IsTypeInRangeForNumericType<U, V>::value,\n                  \"The saturation result cannot be determined from the \"\n                  \"provided types.\");\n    const V saturated = CommonMaxOrMin<V>(IsValueNegative(y));\n    V result = {};\n    return BASE_NUMERICS_LIKELY((CheckedAddOp<T, U>::Do(x, y, &result)))\n               ? result\n               : saturated;\n  }\n};\n\ntemplate <typename T, typename U, class Enable = void>\nstruct ClampedSubOp {};\n\ntemplate <typename T, typename U>\nstruct ClampedSubOp<T,\n                    U,\n                    typename std::enable_if<std::is_integral<T>::value &&\n                                            std::is_integral<U>::value>::type> {\n  using result_type = typename MaxExponentPromotion<T, U>::type;\n  template <typename V = result_type>\n  static constexpr V Do(T x, U y) {\n    if (!IsConstantEvaluated() && ClampedSubFastOp<T, U>::is_supported)\n      return ClampedSubFastOp<T, U>::template Do<V>(x, y);\n\n    static_assert(std::is_same<V, result_type>::value ||\n                      IsTypeInRangeForNumericType<U, V>::value,\n                  \"The saturation result cannot be determined from the \"\n                  \"provided types.\");\n    const V saturated = CommonMaxOrMin<V>(!IsValueNegative(y));\n    V result = {};\n    return BASE_NUMERICS_LIKELY((CheckedSubOp<T, U>::Do(x, y, &result)))\n               ? result\n               : saturated;\n  }\n};\n\ntemplate <typename T, typename U, class Enable = void>\nstruct ClampedMulOp {};\n\ntemplate <typename T, typename U>\nstruct ClampedMulOp<T,\n                    U,\n                    typename std::enable_if<std::is_integral<T>::value &&\n                                            std::is_integral<U>::value>::type> {\n  using result_type = typename MaxExponentPromotion<T, U>::type;\n  template <typename V = result_type>\n  static constexpr V Do(T x, U y) {\n    if (!IsConstantEvaluated() && ClampedMulFastOp<T, U>::is_supported)\n      return ClampedMulFastOp<T, U>::template Do<V>(x, y);\n\n    V result = {};\n    const V saturated =\n        CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y));\n    return BASE_NUMERICS_LIKELY((CheckedMulOp<T, U>::Do(x, y, &result)))\n               ? result\n               : saturated;\n  }\n};\n\ntemplate <typename T, typename U, class Enable = void>\nstruct ClampedDivOp {};\n\ntemplate <typename T, typename U>\nstruct ClampedDivOp<T,\n                    U,\n                    typename std::enable_if<std::is_integral<T>::value &&\n                                            std::is_integral<U>::value>::type> {\n  using result_type = typename MaxExponentPromotion<T, U>::type;\n  template <typename V = result_type>\n  static constexpr V Do(T x, U y) {\n    V result = {};\n    if (BASE_NUMERICS_LIKELY((CheckedDivOp<T, U>::Do(x, y, &result))))\n      return result;\n    // Saturation goes to max, min, or NaN (if x is zero).\n    return x ? CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y))\n             : SaturationDefaultLimits<V>::NaN();\n  }\n};\n\ntemplate <typename T, typename U, class Enable = void>\nstruct ClampedModOp {};\n\ntemplate <typename T, typename U>\nstruct ClampedModOp<T,\n                    U,\n                    typename std::enable_if<std::is_integral<T>::value &&\n                                            std::is_integral<U>::value>::type> {\n  using result_type = typename MaxExponentPromotion<T, U>::type;\n  template <typename V = result_type>\n  static constexpr V Do(T x, U y) {\n    V result = {};\n    return BASE_NUMERICS_LIKELY((CheckedModOp<T, U>::Do(x, y, &result)))\n               ? result\n               : x;\n  }\n};\n\ntemplate <typename T, typename U, class Enable = void>\nstruct ClampedLshOp {};\n\n// Left shift. Non-zero values saturate in the direction of the sign. A zero\n// shifted by any value always results in zero.\ntemplate <typename T, typename U>\nstruct ClampedLshOp<T,\n                    U,\n                    typename std::enable_if<std::is_integral<T>::value &&\n                                            std::is_integral<U>::value>::type> {\n  using result_type = T;\n  template <typename V = result_type>\n  static constexpr V Do(T x, U shift) {\n    static_assert(!std::is_signed<U>::value, \"Shift value must be unsigned.\");\n    if (BASE_NUMERICS_LIKELY(shift < std::numeric_limits<T>::digits)) {\n      // Shift as unsigned to avoid undefined behavior.\n      V result = static_cast<V>(as_unsigned(x) << shift);\n      // If the shift can be reversed, we know it was valid.\n      if (BASE_NUMERICS_LIKELY(result >> shift == x))\n        return result;\n    }\n    return x ? CommonMaxOrMin<V>(IsValueNegative(x)) : 0;\n  }\n};\n\ntemplate <typename T, typename U, class Enable = void>\nstruct ClampedRshOp {};\n\n// Right shift. Negative values saturate to -1. Positive or 0 saturates to 0.\ntemplate <typename T, typename U>\nstruct ClampedRshOp<T,\n                    U,\n                    typename std::enable_if<std::is_integral<T>::value &&\n                                            std::is_integral<U>::value>::type> {\n  using result_type = T;\n  template <typename V = result_type>\n  static constexpr V Do(T x, U shift) {\n    static_assert(!std::is_signed<U>::value, \"Shift value must be unsigned.\");\n    // Signed right shift is odd, because it saturates to -1 or 0.\n    const V saturated = as_unsigned(V(0)) - IsValueNegative(x);\n    return BASE_NUMERICS_LIKELY(shift < IntegerBitsPlusSign<T>::value)\n               ? saturated_cast<V>(x >> shift)\n               : saturated;\n  }\n};\n\ntemplate <typename T, typename U, class Enable = void>\nstruct ClampedAndOp {};\n\ntemplate <typename T, typename U>\nstruct ClampedAndOp<T,\n                    U,\n                    typename std::enable_if<std::is_integral<T>::value &&\n                                            std::is_integral<U>::value>::type> {\n  using result_type = typename std::make_unsigned<\n      typename MaxExponentPromotion<T, U>::type>::type;\n  template <typename V>\n  static constexpr V Do(T x, U y) {\n    return static_cast<result_type>(x) & static_cast<result_type>(y);\n  }\n};\n\ntemplate <typename T, typename U, class Enable = void>\nstruct ClampedOrOp {};\n\n// For simplicity we promote to unsigned integers.\ntemplate <typename T, typename U>\nstruct ClampedOrOp<T,\n                   U,\n                   typename std::enable_if<std::is_integral<T>::value &&\n                                           std::is_integral<U>::value>::type> {\n  using result_type = typename std::make_unsigned<\n      typename MaxExponentPromotion<T, U>::type>::type;\n  template <typename V>\n  static constexpr V Do(T x, U y) {\n    return static_cast<result_type>(x) | static_cast<result_type>(y);\n  }\n};\n\ntemplate <typename T, typename U, class Enable = void>\nstruct ClampedXorOp {};\n\n// For simplicity we support only unsigned integers.\ntemplate <typename T, typename U>\nstruct ClampedXorOp<T,\n                    U,\n                    typename std::enable_if<std::is_integral<T>::value &&\n                                            std::is_integral<U>::value>::type> {\n  using result_type = typename std::make_unsigned<\n      typename MaxExponentPromotion<T, U>::type>::type;\n  template <typename V>\n  static constexpr V Do(T x, U y) {\n    return static_cast<result_type>(x) ^ static_cast<result_type>(y);\n  }\n};\n\ntemplate <typename T, typename U, class Enable = void>\nstruct ClampedMaxOp {};\n\ntemplate <typename T, typename U>\nstruct ClampedMaxOp<\n    T,\n    U,\n    typename std::enable_if<std::is_arithmetic<T>::value &&\n                            std::is_arithmetic<U>::value>::type> {\n  using result_type = typename MaxExponentPromotion<T, U>::type;\n  template <typename V = result_type>\n  static constexpr V Do(T x, U y) {\n    return IsGreater<T, U>::Test(x, y) ? saturated_cast<V>(x)\n                                       : saturated_cast<V>(y);\n  }\n};\n\ntemplate <typename T, typename U, class Enable = void>\nstruct ClampedMinOp {};\n\ntemplate <typename T, typename U>\nstruct ClampedMinOp<\n    T,\n    U,\n    typename std::enable_if<std::is_arithmetic<T>::value &&\n                            std::is_arithmetic<U>::value>::type> {\n  using result_type = typename LowestValuePromotion<T, U>::type;\n  template <typename V = result_type>\n  static constexpr V Do(T x, U y) {\n    return IsLess<T, U>::Test(x, y) ? saturated_cast<V>(x)\n                                    : saturated_cast<V>(y);\n  }\n};\n\n// This is just boilerplate that wraps the standard floating point arithmetic.\n// A macro isn't the nicest solution, but it beats rewriting these repeatedly.\n#define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP)                              \\\n  template <typename T, typename U>                                      \\\n  struct Clamped##NAME##Op<                                              \\\n      T, U,                                                              \\\n      typename std::enable_if<std::is_floating_point<T>::value ||        \\\n                              std::is_floating_point<U>::value>::type> { \\\n    using result_type = typename MaxExponentPromotion<T, U>::type;       \\\n    template <typename V = result_type>                                  \\\n    static constexpr V Do(T x, U y) {                                    \\\n      return saturated_cast<V>(x OP y);                                  \\\n    }                                                                    \\\n  };\n\nBASE_FLOAT_ARITHMETIC_OPS(Add, +)\nBASE_FLOAT_ARITHMETIC_OPS(Sub, -)\nBASE_FLOAT_ARITHMETIC_OPS(Mul, *)\nBASE_FLOAT_ARITHMETIC_OPS(Div, /)\n\n#undef BASE_FLOAT_ARITHMETIC_OPS\n\n}  // namespace internal\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_NUMERICS_CLAMPED_MATH_IMPL_H_\n"
  },
  {
    "path": "tachyon/base/numerics/math_constants.h",
    "content": "// Copyright 2017 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_NUMERICS_MATH_CONSTANTS_H_\n#define TACHYON_BASE_NUMERICS_MATH_CONSTANTS_H_\n\nnamespace tachyon::base {\n\nconstexpr double kPiDouble = 3.14159265358979323846;\nconstexpr float kPiFloat = 3.14159265358979323846f;\n\n// pi/180 and 180/pi. These are correctly rounded from the true\n// mathematical value, unlike what you'd get from e.g.\n// 180.0f / kPiFloat.\nconstexpr double kDegToRadDouble = 0.017453292519943295769;\nconstexpr float kDegToRadFloat = 0.017453292519943295769f;\nconstexpr double kRadToDegDouble = 57.295779513082320876798;\nconstexpr float kRadToDegFloat = 57.295779513082320876798f;\n\n// sqrt(1/2) = 1/sqrt(2).\nconstexpr double kSqrtHalfDouble = 0.70710678118654752440;\nconstexpr float kSqrtHalfFloat = 0.70710678118654752440f;\n\n// The mean acceleration due to gravity on Earth in m/s².\nconstexpr double kMeanGravityDouble = 9.80665;\nconstexpr float kMeanGravityFloat = 9.80665f;\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_NUMERICS_MATH_CONSTANTS_H_\n"
  },
  {
    "path": "tachyon/base/numerics/ostream_operators.h",
    "content": "// Copyright 2021 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_NUMERICS_OSTREAM_OPERATORS_H_\n#define TACHYON_BASE_NUMERICS_OSTREAM_OPERATORS_H_\n\n#include <ostream>\n\nnamespace tachyon::base {\nnamespace internal {\n\ntemplate <typename T>\nclass ClampedNumeric;\ntemplate <typename T>\nclass StrictNumeric;\n\n// Overload the ostream output operator to make logging work nicely.\ntemplate <typename T>\nstd::ostream& operator<<(std::ostream& os, const StrictNumeric<T>& value) {\n  os << static_cast<T>(value);\n  return os;\n}\n\n// Overload the ostream output operator to make logging work nicely.\ntemplate <typename T>\nstd::ostream& operator<<(std::ostream& os, const ClampedNumeric<T>& value) {\n  os << static_cast<T>(value);\n  return os;\n}\n\n}  // namespace internal\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_NUMERICS_OSTREAM_OPERATORS_H_\n"
  },
  {
    "path": "tachyon/base/numerics/ranges.h",
    "content": "// Copyright 2017 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_NUMERICS_RANGES_H_\n#define TACHYON_BASE_NUMERICS_RANGES_H_\n\n#include <cmath>\n#include <type_traits>\n\nnamespace tachyon::base {\n\ntemplate <typename T>\nconstexpr bool IsApproximatelyEqual(T lhs, T rhs, T tolerance) {\n  static_assert(std::is_arithmetic<T>::value, \"Argument must be arithmetic\");\n  return std::abs(rhs - lhs) <= tolerance;\n}\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_NUMERICS_RANGES_H_\n"
  },
  {
    "path": "tachyon/base/numerics/safe_conversions.h",
    "content": "// Copyright 2014 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_NUMERICS_SAFE_CONVERSIONS_H_\n#define TACHYON_BASE_NUMERICS_SAFE_CONVERSIONS_H_\n\n#include <stddef.h>\n\n#include <cmath>\n#include <limits>\n#include <type_traits>\n\n#include \"tachyon/base/numerics/safe_conversions_impl.h\"\n\n#if defined(__ARMEL__) && !defined(__native_client__)\n#include \"tachyon/base/numerics/safe_conversions_arm_impl.h\"\n#define BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS (1)\n#else\n#define BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS (0)\n#endif\n\nnamespace tachyon::base {\nnamespace internal {\n\n#if !BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS\ntemplate <typename Dst, typename Src>\nstruct SaturateFastAsmOp {\n  static constexpr bool is_supported = false;\n  static constexpr Dst Do(Src) {\n    // Force a compile failure if instantiated.\n    return CheckOnFailure::template HandleFailure<Dst>();\n  }\n};\n#endif  // BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS\n#undef BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS\n\n// The following special case a few specific integer conversions where we can\n// eke out better performance than range checking.\ntemplate <typename Dst, typename Src, typename Enable = void>\nstruct IsValueInRangeFastOp {\n  static constexpr bool is_supported = false;\n  static constexpr bool Do(Src value) {\n    // Force a compile failure if instantiated.\n    return CheckOnFailure::template HandleFailure<bool>();\n  }\n};\n\n// Signed to signed range comparison.\ntemplate <typename Dst, typename Src>\nstruct IsValueInRangeFastOp<\n    Dst,\n    Src,\n    typename std::enable_if<\n        std::is_integral<Dst>::value && std::is_integral<Src>::value &&\n        std::is_signed<Dst>::value && std::is_signed<Src>::value &&\n        !IsTypeInRangeForNumericType<Dst, Src>::value>::type> {\n  static constexpr bool is_supported = true;\n\n  static constexpr bool Do(Src value) {\n    // Just downcast to the smaller type, sign extend it back to the original\n    // type, and then see if it matches the original value.\n    return value == static_cast<Dst>(value);\n  }\n};\n\n// Signed to unsigned range comparison.\ntemplate <typename Dst, typename Src>\nstruct IsValueInRangeFastOp<\n    Dst,\n    Src,\n    typename std::enable_if<\n        std::is_integral<Dst>::value && std::is_integral<Src>::value &&\n        !std::is_signed<Dst>::value && std::is_signed<Src>::value &&\n        !IsTypeInRangeForNumericType<Dst, Src>::value>::type> {\n  static constexpr bool is_supported = true;\n\n  static constexpr bool Do(Src value) {\n    // We cast a signed as unsigned to overflow negative values to the top,\n    // then compare against whichever maximum is smaller, as our upper bound.\n    return as_unsigned(value) <= as_unsigned(CommonMax<Src, Dst>());\n  }\n};\n\n// Convenience function that returns true if the supplied value is in range\n// for the destination type.\ntemplate <typename Dst, typename Src>\nconstexpr bool IsValueInRangeForNumericType(Src value) {\n  using SrcType = typename internal::UnderlyingType<Src>::type;\n  return internal::IsValueInRangeFastOp<Dst, SrcType>::is_supported\n             ? internal::IsValueInRangeFastOp<Dst, SrcType>::Do(\n                   static_cast<SrcType>(value))\n             : internal::DstRangeRelationToSrcRange<Dst>(\n                   static_cast<SrcType>(value))\n                   .IsValid();\n}\n\n// checked_cast<> is analogous to static_cast<> for numeric types,\n// except that it CHECKs that the specified numeric conversion will not\n// overflow or underflow. NaN source will always trigger a CHECK.\ntemplate <typename Dst,\n          class CheckHandler = internal::CheckOnFailure,\n          typename Src>\nconstexpr Dst checked_cast(Src value) {\n  // This throws a compile-time error on evaluating the constexpr if it can be\n  // determined at compile-time as failing, otherwise it will CHECK at runtime.\n  using SrcType = typename internal::UnderlyingType<Src>::type;\n  return BASE_NUMERICS_LIKELY((IsValueInRangeForNumericType<Dst>(value)))\n             ? static_cast<Dst>(static_cast<SrcType>(value))\n             : CheckHandler::template HandleFailure<Dst>();\n}\n\n// Default boundaries for integral/float: max/infinity, lowest/-infinity, 0/NaN.\n// You may provide your own limits (e.g. to saturated_cast) so long as you\n// implement all of the static constexpr member functions in the class below.\ntemplate <typename T>\nstruct SaturationDefaultLimits : public std::numeric_limits<T> {\n  static constexpr T NaN() {\n    if constexpr (std::numeric_limits<T>::has_quiet_NaN) {\n      return std::numeric_limits<T>::quiet_NaN();\n    } else {\n      return T();\n    }\n  }\n  using std::numeric_limits<T>::max;\n  static constexpr T Overflow() {\n    if constexpr (std::numeric_limits<T>::has_infinity) {\n      return std::numeric_limits<T>::infinity();\n    } else {\n      return std::numeric_limits<T>::max();\n    }\n  }\n  using std::numeric_limits<T>::lowest;\n  static constexpr T Underflow() {\n    if constexpr (std::numeric_limits<T>::has_infinity) {\n      return std::numeric_limits<T>::infinity() * -1;\n    } else {\n      return std::numeric_limits<T>::lowest();\n    }\n  }\n};\n\ntemplate <typename Dst, template <typename> class S, typename Src>\nconstexpr Dst saturated_cast_impl(Src value, RangeCheck constraint) {\n  // For some reason clang generates much better code when the branch is\n  // structured exactly this way, rather than a sequence of checks.\n  return !constraint.IsOverflowFlagSet()\n             ? (!constraint.IsUnderflowFlagSet() ? static_cast<Dst>(value)\n                                                 : S<Dst>::Underflow())\n             // Skip this check for integral Src, which cannot be NaN.\n             : (std::is_integral<Src>::value || !constraint.IsUnderflowFlagSet()\n                    ? S<Dst>::Overflow()\n                    : S<Dst>::NaN());\n}\n\n// We can reduce the number of conditions and get slightly better performance\n// for normal signed and unsigned integer ranges. And in the specific case of\n// Arm, we can use the optimized saturation instructions.\ntemplate <typename Dst, typename Src, typename Enable = void>\nstruct SaturateFastOp {\n  static constexpr bool is_supported = false;\n  static constexpr Dst Do(Src value) {\n    // Force a compile failure if instantiated.\n    return CheckOnFailure::template HandleFailure<Dst>();\n  }\n};\n\ntemplate <typename Dst, typename Src>\nstruct SaturateFastOp<\n    Dst,\n    Src,\n    typename std::enable_if<std::is_integral<Src>::value &&\n                            std::is_integral<Dst>::value &&\n                            SaturateFastAsmOp<Dst, Src>::is_supported>::type> {\n  static constexpr bool is_supported = true;\n  static constexpr Dst Do(Src value) {\n    return SaturateFastAsmOp<Dst, Src>::Do(value);\n  }\n};\n\ntemplate <typename Dst, typename Src>\nstruct SaturateFastOp<\n    Dst,\n    Src,\n    typename std::enable_if<std::is_integral<Src>::value &&\n                            std::is_integral<Dst>::value &&\n                            !SaturateFastAsmOp<Dst, Src>::is_supported>::type> {\n  static constexpr bool is_supported = true;\n  static constexpr Dst Do(Src value) {\n    // The exact order of the following is structured to hit the correct\n    // optimization heuristics across compilers. Do not change without\n    // checking the emitted code.\n    const Dst saturated = CommonMaxOrMin<Dst, Src>(\n        IsMaxInRangeForNumericType<Dst, Src>() ||\n        (!IsMinInRangeForNumericType<Dst, Src>() && IsValueNegative(value)));\n    return BASE_NUMERICS_LIKELY(IsValueInRangeForNumericType<Dst>(value))\n               ? static_cast<Dst>(value)\n               : saturated;\n  }\n};\n\n// saturated_cast<> is analogous to static_cast<> for numeric types, except\n// that the specified numeric conversion will saturate by default rather than\n// overflow or underflow, and NaN assignment to an integral will return 0.\n// All boundary condition behaviors can be overridden with a custom handler.\ntemplate <typename Dst,\n          template <typename> class SaturationHandler = SaturationDefaultLimits,\n          typename Src>\nconstexpr Dst saturated_cast(Src value) {\n  using SrcType = typename UnderlyingType<Src>::type;\n  return !IsConstantEvaluated() && SaturateFastOp<Dst, SrcType>::is_supported &&\n                 std::is_same<SaturationHandler<Dst>,\n                              SaturationDefaultLimits<Dst>>::value\n             ? SaturateFastOp<Dst, SrcType>::Do(static_cast<SrcType>(value))\n             : saturated_cast_impl<Dst, SaturationHandler, SrcType>(\n                   static_cast<SrcType>(value),\n                   DstRangeRelationToSrcRange<Dst, SaturationHandler, SrcType>(\n                       static_cast<SrcType>(value)));\n}\n\n// strict_cast<> is analogous to static_cast<> for numeric types, except that\n// it will cause a compile failure if the destination type is not large enough\n// to contain any value in the source type. It performs no runtime checking.\ntemplate <typename Dst, typename Src>\nconstexpr Dst strict_cast(Src value) {\n  using SrcType = typename UnderlyingType<Src>::type;\n  static_assert(UnderlyingType<Src>::is_numeric, \"Argument must be numeric.\");\n  static_assert(std::is_arithmetic<Dst>::value, \"Result must be numeric.\");\n\n  // If you got here from a compiler error, it's because you tried to assign\n  // from a source type to a destination type that has insufficient range.\n  // The solution may be to change the destination type you're assigning to,\n  // and use one large enough to represent the source.\n  // Alternatively, you may be better served with the checked_cast<> or\n  // saturated_cast<> template functions for your particular use case.\n  static_assert(StaticDstRangeRelationToSrcRange<Dst, SrcType>::value ==\n                    NUMERIC_RANGE_CONTAINED,\n                \"The source type is out of range for the destination type. \"\n                \"Please see strict_cast<> comments for more information.\");\n\n  return static_cast<Dst>(static_cast<SrcType>(value));\n}\n\n// Some wrappers to statically check that a type is in range.\ntemplate <typename Dst, typename Src, class Enable = void>\nstruct IsNumericRangeContained {\n  static constexpr bool value = false;\n};\n\ntemplate <typename Dst, typename Src>\nstruct IsNumericRangeContained<\n    Dst,\n    Src,\n    typename std::enable_if<ArithmeticOrUnderlyingEnum<Dst>::value &&\n                            ArithmeticOrUnderlyingEnum<Src>::value>::type> {\n  static constexpr bool value =\n      StaticDstRangeRelationToSrcRange<Dst, Src>::value ==\n      NUMERIC_RANGE_CONTAINED;\n};\n\n// StrictNumeric implements compile time range checking between numeric types by\n// wrapping assignment operations in a strict_cast. This class is intended to be\n// used for function arguments and return types, to ensure the destination type\n// can always contain the source type. This is essentially the same as enforcing\n// -Wconversion in gcc and C4302 warnings on MSVC, but it can be applied\n// incrementally at API boundaries, making it easier to convert code so that it\n// compiles cleanly with truncation warnings enabled.\n// This template should introduce no runtime overhead, but it also provides no\n// runtime checking of any of the associated mathematical operations. Use\n// CheckedNumeric for runtime range checks of the actual value being assigned.\ntemplate <typename T>\nclass StrictNumeric {\n public:\n  using type = T;\n\n  constexpr StrictNumeric() : value_(0) {}\n\n  // Copy constructor.\n  template <typename Src>\n  constexpr StrictNumeric(const StrictNumeric<Src>& rhs)\n      : value_(strict_cast<T>(rhs.value_)) {}\n\n  // Strictly speaking, this is not necessary, but declaring this allows class\n  // template argument deduction to be used so that it is possible to simply\n  // write `StrictNumeric(777)` instead of `StrictNumeric<int>(777)`.\n  // NOLINTNEXTLINE(google-explicit-constructor)\n  constexpr StrictNumeric(T value) : value_(value) {}\n\n  // This is not an explicit constructor because we implicitly upgrade regular\n  // numerics to StrictNumerics to make them easier to use.\n  template <typename Src>\n  // NOLINTNEXTLINE(google-explicit-constructor)\n  constexpr StrictNumeric(Src value) : value_(strict_cast<T>(value)) {}\n\n  // If you got here from a compiler error, it's because you tried to assign\n  // from a source type to a destination type that has insufficient range.\n  // The solution may be to change the destination type you're assigning to,\n  // and use one large enough to represent the source.\n  // If you're assigning from a CheckedNumeric<> class, you may be able to use\n  // the AssignIfValid() member function, specify a narrower destination type to\n  // the member value functions (e.g. val.template ValueOrDie<Dst>()), use one\n  // of the value helper functions (e.g. ValueOrDieForType<Dst>(val)).\n  // If you've encountered an _ambiguous overload_ you can use a static_cast<>\n  // to explicitly cast the result to the destination type.\n  // If none of that works, you may be better served with the checked_cast<> or\n  // saturated_cast<> template functions for your particular use case.\n  template <typename Dst,\n            typename std::enable_if<\n                IsNumericRangeContained<Dst, T>::value>::type* = nullptr>\n  constexpr operator Dst() const {\n    return static_cast<typename ArithmeticOrUnderlyingEnum<Dst>::type>(value_);\n  }\n\n private:\n  const T value_;\n};\n\n// Convenience wrapper returns a StrictNumeric from the provided arithmetic\n// type.\ntemplate <typename T>\nconstexpr StrictNumeric<typename UnderlyingType<T>::type> MakeStrictNum(\n    const T value) {\n  return value;\n}\n\n#define BASE_NUMERIC_COMPARISON_OPERATORS(CLASS, NAME, OP)              \\\n  template <typename L, typename R,                                     \\\n            typename std::enable_if<                                    \\\n                internal::Is##CLASS##Op<L, R>::value>::type* = nullptr> \\\n  constexpr bool operator OP(const L lhs, const R rhs) {                \\\n    return SafeCompare<NAME, typename UnderlyingType<L>::type,          \\\n                       typename UnderlyingType<R>::type>(lhs, rhs);     \\\n  }\n\nBASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsLess, <)\nBASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsLessOrEqual, <=)\nBASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsGreater, >)\nBASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsGreaterOrEqual, >=)\nBASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsEqual, ==)\nBASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsNotEqual, !=)\n\n}  // namespace internal\n\nusing internal::as_signed;\nusing internal::as_unsigned;\nusing internal::checked_cast;\nusing internal::IsTypeInRangeForNumericType;\nusing internal::IsValueInRangeForNumericType;\nusing internal::IsValueNegative;\nusing internal::MakeStrictNum;\nusing internal::SafeUnsignedAbs;\nusing internal::saturated_cast;\nusing internal::strict_cast;\nusing internal::StrictNumeric;\n\n// Explicitly make a shorter size_t alias for convenience.\nusing SizeT = StrictNumeric<size_t>;\n\n// floating -> integral conversions that saturate and thus can actually return\n// an integral type.  In most cases, these should be preferred over the std::\n// versions.\ntemplate <typename Dst = int,\n          typename Src,\n          typename = std::enable_if_t<std::is_integral<Dst>::value &&\n                                      std::is_floating_point<Src>::value>>\nDst ClampFloor(Src value) {\n  return saturated_cast<Dst>(std::floor(value));\n}\ntemplate <typename Dst = int,\n          typename Src,\n          typename = std::enable_if_t<std::is_integral<Dst>::value &&\n                                      std::is_floating_point<Src>::value>>\nDst ClampCeil(Src value) {\n  return saturated_cast<Dst>(std::ceil(value));\n}\ntemplate <typename Dst = int,\n          typename Src,\n          typename = std::enable_if_t<std::is_integral<Dst>::value &&\n                                      std::is_floating_point<Src>::value>>\nDst ClampRound(Src value) {\n  const Src rounded =\n      (value >= 0.0f) ? std::floor(value + 0.5f) : std::ceil(value - 0.5f);\n  return saturated_cast<Dst>(rounded);\n}\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_NUMERICS_SAFE_CONVERSIONS_H_\n"
  },
  {
    "path": "tachyon/base/numerics/safe_conversions_arm_impl.h",
    "content": "// Copyright 2017 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_NUMERICS_SAFE_CONVERSIONS_ARM_IMPL_H_\n#define TACHYON_BASE_NUMERICS_SAFE_CONVERSIONS_ARM_IMPL_H_\n\n#include <cassert>\n#include <limits>\n#include <type_traits>\n\n#include \"tachyon/base/numerics/safe_conversions_impl.h\"\n\nnamespace tachyon::base {\nnamespace internal {\n\n// Fast saturation to a destination type.\ntemplate <typename Dst, typename Src>\nstruct SaturateFastAsmOp {\n  static constexpr bool is_supported =\n      kEnableAsmCode && std::is_signed<Src>::value &&\n      std::is_integral<Dst>::value && std::is_integral<Src>::value &&\n      IntegerBitsPlusSign<Src>::value <= IntegerBitsPlusSign<int32_t>::value &&\n      IntegerBitsPlusSign<Dst>::value <= IntegerBitsPlusSign<int32_t>::value &&\n      !IsTypeInRangeForNumericType<Dst, Src>::value;\n\n  __attribute__((always_inline)) static Dst Do(Src value) {\n    int32_t src = value;\n    typename std::conditional<std::is_signed<Dst>::value, int32_t,\n                              uint32_t>::type result;\n    if (std::is_signed<Dst>::value) {\n      asm(\"ssat %[dst], %[shift], %[src]\"\n          : [dst] \"=r\"(result)\n          : [src] \"r\"(src), [shift] \"n\"(IntegerBitsPlusSign<Dst>::value <= 32\n                                            ? IntegerBitsPlusSign<Dst>::value\n                                            : 32));\n    } else {\n      asm(\"usat %[dst], %[shift], %[src]\"\n          : [dst] \"=r\"(result)\n          : [src] \"r\"(src), [shift] \"n\"(IntegerBitsPlusSign<Dst>::value < 32\n                                            ? IntegerBitsPlusSign<Dst>::value\n                                            : 31));\n    }\n    return static_cast<Dst>(result);\n  }\n};\n\n}  // namespace internal\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_NUMERICS_SAFE_CONVERSIONS_ARM_IMPL_H_\n"
  },
  {
    "path": "tachyon/base/numerics/safe_conversions_impl.h",
    "content": "// Copyright 2014 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_\n#define TACHYON_BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_\n\n#include <stdint.h>\n\n#include <limits>\n#include <type_traits>\n\n#if defined(__GNUC__) || defined(__clang__)\n#define BASE_NUMERICS_LIKELY(x) __builtin_expect(!!(x), 1)\n#define BASE_NUMERICS_UNLIKELY(x) __builtin_expect(!!(x), 0)\n#else\n#define BASE_NUMERICS_LIKELY(x) (x)\n#define BASE_NUMERICS_UNLIKELY(x) (x)\n#endif\n\nnamespace tachyon::base {\nnamespace internal {\n\n// The std library doesn't provide a binary max_exponent for integers, however\n// we can compute an analog using std::numeric_limits<>::digits.\ntemplate <typename NumericType>\nstruct MaxExponent {\n  static const int value = std::is_floating_point<NumericType>::value\n                               ? std::numeric_limits<NumericType>::max_exponent\n                               : std::numeric_limits<NumericType>::digits + 1;\n};\n\n// The number of bits (including the sign) in an integer. Eliminates sizeof\n// hacks.\ntemplate <typename NumericType>\nstruct IntegerBitsPlusSign {\n  static const int value = std::numeric_limits<NumericType>::digits +\n                           std::is_signed<NumericType>::value;\n};\n\n// Helper templates for integer manipulations.\n\ntemplate <typename Integer>\nstruct PositionOfSignBit {\n  static const size_t value = IntegerBitsPlusSign<Integer>::value - 1;\n};\n\n// Determines if a numeric value is negative without throwing compiler\n// warnings on: unsigned(value) < 0.\ntemplate <typename T,\n          typename std::enable_if<std::is_signed<T>::value>::type* = nullptr>\nconstexpr bool IsValueNegative(T value) {\n  static_assert(std::is_arithmetic<T>::value, \"Argument must be numeric.\");\n  return value < 0;\n}\n\ntemplate <typename T,\n          typename std::enable_if<!std::is_signed<T>::value>::type* = nullptr>\nconstexpr bool IsValueNegative(T) {\n  static_assert(std::is_arithmetic<T>::value, \"Argument must be numeric.\");\n  return false;\n}\n\n// This performs a fast negation, returning a signed value. It works on unsigned\n// arguments, but probably doesn't do what you want for any unsigned value\n// larger than max / 2 + 1 (i.e. signed min cast to unsigned).\ntemplate <typename T>\nconstexpr typename std::make_signed<T>::type ConditionalNegate(\n    T x,\n    bool is_negative) {\n  static_assert(std::is_integral<T>::value, \"Type must be integral\");\n  using SignedT = typename std::make_signed<T>::type;\n  using UnsignedT = typename std::make_unsigned<T>::type;\n  return static_cast<SignedT>((static_cast<UnsignedT>(x) ^\n                               static_cast<UnsignedT>(-SignedT(is_negative))) +\n                              is_negative);\n}\n\n// This performs a safe, absolute value via unsigned overflow.\ntemplate <typename T>\nconstexpr typename std::make_unsigned<T>::type SafeUnsignedAbs(T value) {\n  static_assert(std::is_integral<T>::value, \"Type must be integral\");\n  using UnsignedT = typename std::make_unsigned<T>::type;\n  return IsValueNegative(value)\n             ? static_cast<UnsignedT>(0u - static_cast<UnsignedT>(value))\n             : static_cast<UnsignedT>(value);\n}\n\n// TODO(jschuh): Switch to std::is_constant_evaluated() once C++20 is supported.\n// Alternately, the usage could be restructured for \"consteval if\" in C++23.\n#define IsConstantEvaluated() (__builtin_is_constant_evaluated())\n\n// TODO(jschuh): Debug builds don't reliably propagate constants, so we restrict\n// some accelerated runtime paths to release builds until this can be forced\n// with consteval support in C++20 or C++23.\n#if defined(NDEBUG)\nconstexpr bool kEnableAsmCode = true;\n#else\nconstexpr bool kEnableAsmCode = false;\n#endif\n\n// Forces a crash, like a CHECK(false). Used for numeric boundary errors.\n// Also used in a constexpr template to trigger a compilation failure on\n// an error condition.\nstruct CheckOnFailure {\n  template <typename T>\n  static T HandleFailure() {\n#if defined(_MSC_VER)\n    __debugbreak();\n#elif defined(__GNUC__) || defined(__clang__)\n    __builtin_trap();\n#else\n    ((void)(*(volatile char*)0 = 0));\n#endif\n    return T();\n  }\n};\n\nenum IntegerRepresentation {\n  INTEGER_REPRESENTATION_UNSIGNED,\n  INTEGER_REPRESENTATION_SIGNED\n};\n\n// A range for a given nunmeric Src type is contained for a given numeric Dst\n// type if both numeric_limits<Src>::max() <= numeric_limits<Dst>::max() and\n// numeric_limits<Src>::lowest() >= numeric_limits<Dst>::lowest() are true.\n// We implement this as template specializations rather than simple static\n// comparisons to ensure type correctness in our comparisons.\nenum NumericRangeRepresentation {\n  NUMERIC_RANGE_NOT_CONTAINED,\n  NUMERIC_RANGE_CONTAINED\n};\n\n// Helper templates to statically determine if our destination type can contain\n// maximum and minimum values represented by the source type.\n\ntemplate <typename Dst,\n          typename Src,\n          IntegerRepresentation DstSign = std::is_signed<Dst>::value\n                                              ? INTEGER_REPRESENTATION_SIGNED\n                                              : INTEGER_REPRESENTATION_UNSIGNED,\n          IntegerRepresentation SrcSign = std::is_signed<Src>::value\n                                              ? INTEGER_REPRESENTATION_SIGNED\n                                              : INTEGER_REPRESENTATION_UNSIGNED>\nstruct StaticDstRangeRelationToSrcRange;\n\n// Same sign: Dst is guaranteed to contain Src only if its range is equal or\n// larger.\ntemplate <typename Dst, typename Src, IntegerRepresentation Sign>\nstruct StaticDstRangeRelationToSrcRange<Dst, Src, Sign, Sign> {\n  static const NumericRangeRepresentation value =\n      MaxExponent<Dst>::value >= MaxExponent<Src>::value\n          ? NUMERIC_RANGE_CONTAINED\n          : NUMERIC_RANGE_NOT_CONTAINED;\n};\n\n// Unsigned to signed: Dst is guaranteed to contain source only if its range is\n// larger.\ntemplate <typename Dst, typename Src>\nstruct StaticDstRangeRelationToSrcRange<Dst,\n                                        Src,\n                                        INTEGER_REPRESENTATION_SIGNED,\n                                        INTEGER_REPRESENTATION_UNSIGNED> {\n  static const NumericRangeRepresentation value =\n      MaxExponent<Dst>::value > MaxExponent<Src>::value\n          ? NUMERIC_RANGE_CONTAINED\n          : NUMERIC_RANGE_NOT_CONTAINED;\n};\n\n// Signed to unsigned: Dst cannot be statically determined to contain Src.\ntemplate <typename Dst, typename Src>\nstruct StaticDstRangeRelationToSrcRange<Dst,\n                                        Src,\n                                        INTEGER_REPRESENTATION_UNSIGNED,\n                                        INTEGER_REPRESENTATION_SIGNED> {\n  static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED;\n};\n\n// This class wraps the range constraints as separate booleans so the compiler\n// can identify constants and eliminate unused code paths.\nclass RangeCheck {\n public:\n  constexpr RangeCheck(bool is_in_lower_bound, bool is_in_upper_bound)\n      : is_underflow_(!is_in_lower_bound), is_overflow_(!is_in_upper_bound) {}\n  constexpr RangeCheck() : is_underflow_(false), is_overflow_(false) {}\n  constexpr bool IsValid() const { return !is_overflow_ && !is_underflow_; }\n  constexpr bool IsInvalid() const { return is_overflow_ && is_underflow_; }\n  constexpr bool IsOverflow() const { return is_overflow_ && !is_underflow_; }\n  constexpr bool IsUnderflow() const { return !is_overflow_ && is_underflow_; }\n  constexpr bool IsOverflowFlagSet() const { return is_overflow_; }\n  constexpr bool IsUnderflowFlagSet() const { return is_underflow_; }\n  constexpr bool operator==(const RangeCheck rhs) const {\n    return is_underflow_ == rhs.is_underflow_ &&\n           is_overflow_ == rhs.is_overflow_;\n  }\n  constexpr bool operator!=(const RangeCheck rhs) const {\n    return !(*this == rhs);\n  }\n\n private:\n  // Do not change the order of these member variables. The integral conversion\n  // optimization depends on this exact order.\n  const bool is_underflow_;\n  const bool is_overflow_;\n};\n\n// The following helper template addresses a corner case in range checks for\n// conversion from a floating-point type to an integral type of smaller range\n// but larger precision (e.g. float -> unsigned). The problem is as follows:\n//   1. Integral maximum is always one less than a power of two, so it must be\n//      truncated to fit the mantissa of the floating point. The direction of\n//      rounding is implementation defined, but by default it's always IEEE\n//      floats, which round to nearest and thus result in a value of larger\n//      magnitude than the integral value.\n//      Example: float f = UINT_MAX; // f is 4294967296f but UINT_MAX\n//                                   // is 4294967295u.\n//   2. If the floating point value is equal to the promoted integral maximum\n//      value, a range check will erroneously pass.\n//      Example: (4294967296f <= 4294967295u) // This is true due to a precision\n//                                            // loss in rounding up to float.\n//   3. When the floating point value is then converted to an integral, the\n//      resulting value is out of range for the target integral type and\n//      thus is implementation defined.\n//      Example: unsigned u = (float)INT_MAX; // u will typically overflow to 0.\n// To fix this bug we manually truncate the maximum value when the destination\n// type is an integral of larger precision than the source floating-point type,\n// such that the resulting maximum is represented exactly as a floating point.\ntemplate <typename Dst, typename Src, template <typename> class Bounds>\nstruct NarrowingRange {\n  using SrcLimits = std::numeric_limits<Src>;\n  using DstLimits = typename std::numeric_limits<Dst>;\n\n  // Computes the mask required to make an accurate comparison between types.\n  static const int kShift =\n      (MaxExponent<Src>::value > MaxExponent<Dst>::value &&\n       SrcLimits::digits < DstLimits::digits)\n          ? (DstLimits::digits - SrcLimits::digits)\n          : 0;\n  template <\n      typename T,\n      typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>\n\n  // Masks out the integer bits that are beyond the precision of the\n  // intermediate type used for comparison.\n  static constexpr T Adjust(T value) {\n    static_assert(std::is_same<T, Dst>::value, \"\");\n    static_assert(kShift < DstLimits::digits, \"\");\n    using UnsignedDst = typename std::make_unsigned_t<T>;\n    return static_cast<T>(ConditionalNegate(\n        SafeUnsignedAbs(value) & ~((UnsignedDst{1} << kShift) - UnsignedDst{1}),\n        IsValueNegative(value)));\n  }\n\n  template <typename T,\n            typename std::enable_if<std::is_floating_point<T>::value>::type* =\n                nullptr>\n  static constexpr T Adjust(T value) {\n    static_assert(std::is_same<T, Dst>::value, \"\");\n    static_assert(kShift == 0, \"\");\n    return value;\n  }\n\n  static constexpr Dst max() { return Adjust(Bounds<Dst>::max()); }\n  static constexpr Dst lowest() { return Adjust(Bounds<Dst>::lowest()); }\n};\n\ntemplate <typename Dst,\n          typename Src,\n          template <typename>\n          class Bounds,\n          IntegerRepresentation DstSign = std::is_signed<Dst>::value\n                                              ? INTEGER_REPRESENTATION_SIGNED\n                                              : INTEGER_REPRESENTATION_UNSIGNED,\n          IntegerRepresentation SrcSign = std::is_signed<Src>::value\n                                              ? INTEGER_REPRESENTATION_SIGNED\n                                              : INTEGER_REPRESENTATION_UNSIGNED,\n          NumericRangeRepresentation DstRange =\n              StaticDstRangeRelationToSrcRange<Dst, Src>::value>\nstruct DstRangeRelationToSrcRangeImpl;\n\n// The following templates are for ranges that must be verified at runtime. We\n// split it into checks based on signedness to avoid confusing casts and\n// compiler warnings on signed an unsigned comparisons.\n\n// Same sign narrowing: The range is contained for normal limits.\ntemplate <typename Dst,\n          typename Src,\n          template <typename>\n          class Bounds,\n          IntegerRepresentation DstSign,\n          IntegerRepresentation SrcSign>\nstruct DstRangeRelationToSrcRangeImpl<Dst,\n                                      Src,\n                                      Bounds,\n                                      DstSign,\n                                      SrcSign,\n                                      NUMERIC_RANGE_CONTAINED> {\n  static constexpr RangeCheck Check(Src value) {\n    using SrcLimits = std::numeric_limits<Src>;\n    using DstLimits = NarrowingRange<Dst, Src, Bounds>;\n    return RangeCheck(\n        static_cast<Dst>(SrcLimits::lowest()) >= DstLimits::lowest() ||\n            static_cast<Dst>(value) >= DstLimits::lowest(),\n        static_cast<Dst>(SrcLimits::max()) <= DstLimits::max() ||\n            static_cast<Dst>(value) <= DstLimits::max());\n  }\n};\n\n// Signed to signed narrowing: Both the upper and lower boundaries may be\n// exceeded for standard limits.\ntemplate <typename Dst, typename Src, template <typename> class Bounds>\nstruct DstRangeRelationToSrcRangeImpl<Dst,\n                                      Src,\n                                      Bounds,\n                                      INTEGER_REPRESENTATION_SIGNED,\n                                      INTEGER_REPRESENTATION_SIGNED,\n                                      NUMERIC_RANGE_NOT_CONTAINED> {\n  static constexpr RangeCheck Check(Src value) {\n    using DstLimits = NarrowingRange<Dst, Src, Bounds>;\n    return RangeCheck(value >= DstLimits::lowest(), value <= DstLimits::max());\n  }\n};\n\n// Unsigned to unsigned narrowing: Only the upper bound can be exceeded for\n// standard limits.\ntemplate <typename Dst, typename Src, template <typename> class Bounds>\nstruct DstRangeRelationToSrcRangeImpl<Dst,\n                                      Src,\n                                      Bounds,\n                                      INTEGER_REPRESENTATION_UNSIGNED,\n                                      INTEGER_REPRESENTATION_UNSIGNED,\n                                      NUMERIC_RANGE_NOT_CONTAINED> {\n  static constexpr RangeCheck Check(Src value) {\n    using DstLimits = NarrowingRange<Dst, Src, Bounds>;\n    return RangeCheck(\n        DstLimits::lowest() == Dst(0) || value >= DstLimits::lowest(),\n        value <= DstLimits::max());\n  }\n};\n\n// Unsigned to signed: Only the upper bound can be exceeded for standard limits.\ntemplate <typename Dst, typename Src, template <typename> class Bounds>\nstruct DstRangeRelationToSrcRangeImpl<Dst,\n                                      Src,\n                                      Bounds,\n                                      INTEGER_REPRESENTATION_SIGNED,\n                                      INTEGER_REPRESENTATION_UNSIGNED,\n                                      NUMERIC_RANGE_NOT_CONTAINED> {\n  static constexpr RangeCheck Check(Src value) {\n    using DstLimits = NarrowingRange<Dst, Src, Bounds>;\n    using Promotion = decltype(Src() + Dst());\n    return RangeCheck(DstLimits::lowest() <= Dst(0) ||\n                          static_cast<Promotion>(value) >=\n                              static_cast<Promotion>(DstLimits::lowest()),\n                      static_cast<Promotion>(value) <=\n                          static_cast<Promotion>(DstLimits::max()));\n  }\n};\n\n// Signed to unsigned: The upper boundary may be exceeded for a narrower Dst,\n// and any negative value exceeds the lower boundary for standard limits.\ntemplate <typename Dst, typename Src, template <typename> class Bounds>\nstruct DstRangeRelationToSrcRangeImpl<Dst,\n                                      Src,\n                                      Bounds,\n                                      INTEGER_REPRESENTATION_UNSIGNED,\n                                      INTEGER_REPRESENTATION_SIGNED,\n                                      NUMERIC_RANGE_NOT_CONTAINED> {\n  static constexpr RangeCheck Check(Src value) {\n    using SrcLimits = std::numeric_limits<Src>;\n    using DstLimits = NarrowingRange<Dst, Src, Bounds>;\n    using Promotion = decltype(Src() + Dst());\n    bool ge_zero = false;\n    // Converting floating-point to integer will discard fractional part, so\n    // values in (-1.0, -0.0) will truncate to 0 and fit in Dst.\n    if (std::is_floating_point<Src>::value) {\n      ge_zero = value > Src(-1);\n    } else {\n      ge_zero = value >= Src(0);\n    }\n    return RangeCheck(\n        ge_zero && (DstLimits::lowest() == 0 ||\n                    static_cast<Dst>(value) >= DstLimits::lowest()),\n        static_cast<Promotion>(SrcLimits::max()) <=\n                static_cast<Promotion>(DstLimits::max()) ||\n            static_cast<Promotion>(value) <=\n                static_cast<Promotion>(DstLimits::max()));\n  }\n};\n\n// Simple wrapper for statically checking if a type's range is contained.\ntemplate <typename Dst, typename Src>\nstruct IsTypeInRangeForNumericType {\n  static const bool value = StaticDstRangeRelationToSrcRange<Dst, Src>::value ==\n                            NUMERIC_RANGE_CONTAINED;\n};\n\ntemplate <typename Dst,\n          template <typename> class Bounds = std::numeric_limits,\n          typename Src>\nconstexpr RangeCheck DstRangeRelationToSrcRange(Src value) {\n  static_assert(std::is_arithmetic<Src>::value, \"Argument must be numeric.\");\n  static_assert(std::is_arithmetic<Dst>::value, \"Result must be numeric.\");\n  static_assert(Bounds<Dst>::lowest() < Bounds<Dst>::max(), \"\");\n  return DstRangeRelationToSrcRangeImpl<Dst, Src, Bounds>::Check(value);\n}\n\n// Integer promotion templates used by the portable checked integer arithmetic.\ntemplate <size_t Size, bool IsSigned>\nstruct IntegerForDigitsAndSign;\n\n#define INTEGER_FOR_DIGITS_AND_SIGN(I)                          \\\n  template <>                                                   \\\n  struct IntegerForDigitsAndSign<IntegerBitsPlusSign<I>::value, \\\n                                 std::is_signed<I>::value> {    \\\n    using type = I;                                             \\\n  }\n\nINTEGER_FOR_DIGITS_AND_SIGN(int8_t);\nINTEGER_FOR_DIGITS_AND_SIGN(uint8_t);\nINTEGER_FOR_DIGITS_AND_SIGN(int16_t);\nINTEGER_FOR_DIGITS_AND_SIGN(uint16_t);\nINTEGER_FOR_DIGITS_AND_SIGN(int32_t);\nINTEGER_FOR_DIGITS_AND_SIGN(uint32_t);\nINTEGER_FOR_DIGITS_AND_SIGN(int64_t);\nINTEGER_FOR_DIGITS_AND_SIGN(uint64_t);\n#undef INTEGER_FOR_DIGITS_AND_SIGN\n\n// WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to\n// support 128-bit math, then the ArithmeticPromotion template below will need\n// to be updated (or more likely replaced with a decltype expression).\nstatic_assert(IntegerBitsPlusSign<intmax_t>::value == 64,\n              \"Max integer size not supported for this toolchain.\");\n\ntemplate <typename Integer, bool IsSigned = std::is_signed<Integer>::value>\nstruct TwiceWiderInteger {\n  using type =\n      typename IntegerForDigitsAndSign<IntegerBitsPlusSign<Integer>::value * 2,\n                                       IsSigned>::type;\n};\n\nenum ArithmeticPromotionCategory {\n  LEFT_PROMOTION,  // Use the type of the left-hand argument.\n  RIGHT_PROMOTION  // Use the type of the right-hand argument.\n};\n\n// Determines the type that can represent the largest positive value.\ntemplate <typename Lhs,\n          typename Rhs,\n          ArithmeticPromotionCategory Promotion =\n              (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value)\n                  ? LEFT_PROMOTION\n                  : RIGHT_PROMOTION>\nstruct MaxExponentPromotion;\n\ntemplate <typename Lhs, typename Rhs>\nstruct MaxExponentPromotion<Lhs, Rhs, LEFT_PROMOTION> {\n  using type = Lhs;\n};\n\ntemplate <typename Lhs, typename Rhs>\nstruct MaxExponentPromotion<Lhs, Rhs, RIGHT_PROMOTION> {\n  using type = Rhs;\n};\n\n// Determines the type that can represent the lowest arithmetic value.\ntemplate <typename Lhs,\n          typename Rhs,\n          ArithmeticPromotionCategory Promotion =\n              std::is_signed<Lhs>::value\n                  ? (std::is_signed<Rhs>::value\n                         ? (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value\n                                ? LEFT_PROMOTION\n                                : RIGHT_PROMOTION)\n                         : LEFT_PROMOTION)\n                  : (std::is_signed<Rhs>::value\n                         ? RIGHT_PROMOTION\n                         : (MaxExponent<Lhs>::value < MaxExponent<Rhs>::value\n                                ? LEFT_PROMOTION\n                                : RIGHT_PROMOTION))>\nstruct LowestValuePromotion;\n\ntemplate <typename Lhs, typename Rhs>\nstruct LowestValuePromotion<Lhs, Rhs, LEFT_PROMOTION> {\n  using type = Lhs;\n};\n\ntemplate <typename Lhs, typename Rhs>\nstruct LowestValuePromotion<Lhs, Rhs, RIGHT_PROMOTION> {\n  using type = Rhs;\n};\n\n// Determines the type that is best able to represent an arithmetic result.\ntemplate <\n    typename Lhs,\n    typename Rhs = Lhs,\n    bool is_intmax_type =\n        std::is_integral<typename MaxExponentPromotion<Lhs, Rhs>::type>::value&&\n            IntegerBitsPlusSign<typename MaxExponentPromotion<Lhs, Rhs>::type>::\n                value == IntegerBitsPlusSign<intmax_t>::value,\n    bool is_max_exponent =\n        StaticDstRangeRelationToSrcRange<\n            typename MaxExponentPromotion<Lhs, Rhs>::type,\n            Lhs>::value ==\n        NUMERIC_RANGE_CONTAINED&& StaticDstRangeRelationToSrcRange<\n            typename MaxExponentPromotion<Lhs, Rhs>::type,\n            Rhs>::value == NUMERIC_RANGE_CONTAINED>\nstruct BigEnoughPromotion;\n\n// The side with the max exponent is big enough.\ntemplate <typename Lhs, typename Rhs, bool is_intmax_type>\nstruct BigEnoughPromotion<Lhs, Rhs, is_intmax_type, true> {\n  using type = typename MaxExponentPromotion<Lhs, Rhs>::type;\n  static const bool is_contained = true;\n};\n\n// We can use a twice wider type to fit.\ntemplate <typename Lhs, typename Rhs>\nstruct BigEnoughPromotion<Lhs, Rhs, false, false> {\n  using type =\n      typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type,\n                                 std::is_signed<Lhs>::value ||\n                                     std::is_signed<Rhs>::value>::type;\n  static const bool is_contained = true;\n};\n\n// No type is large enough.\ntemplate <typename Lhs, typename Rhs>\nstruct BigEnoughPromotion<Lhs, Rhs, true, false> {\n  using type = typename MaxExponentPromotion<Lhs, Rhs>::type;\n  static const bool is_contained = false;\n};\n\n// We can statically check if operations on the provided types can wrap, so we\n// can skip the checked operations if they're not needed. So, for an integer we\n// care if the destination type preserves the sign and is twice the width of\n// the source.\ntemplate <typename T, typename Lhs, typename Rhs = Lhs>\nstruct IsIntegerArithmeticSafe {\n  static const bool value =\n      !std::is_floating_point<T>::value &&\n      !std::is_floating_point<Lhs>::value &&\n      !std::is_floating_point<Rhs>::value &&\n      std::is_signed<T>::value >= std::is_signed<Lhs>::value &&\n      IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Lhs>::value) &&\n      std::is_signed<T>::value >= std::is_signed<Rhs>::value &&\n      IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Rhs>::value);\n};\n\n// Promotes to a type that can represent any possible result of a binary\n// arithmetic operation with the source types.\ntemplate <typename Lhs,\n          typename Rhs,\n          bool is_promotion_possible = IsIntegerArithmeticSafe<\n              typename std::conditional<std::is_signed<Lhs>::value ||\n                                            std::is_signed<Rhs>::value,\n                                        intmax_t,\n                                        uintmax_t>::type,\n              typename MaxExponentPromotion<Lhs, Rhs>::type>::value>\nstruct FastIntegerArithmeticPromotion;\n\ntemplate <typename Lhs, typename Rhs>\nstruct FastIntegerArithmeticPromotion<Lhs, Rhs, true> {\n  using type =\n      typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type,\n                                 std::is_signed<Lhs>::value ||\n                                     std::is_signed<Rhs>::value>::type;\n  static_assert(IsIntegerArithmeticSafe<type, Lhs, Rhs>::value, \"\");\n  static const bool is_contained = true;\n};\n\ntemplate <typename Lhs, typename Rhs>\nstruct FastIntegerArithmeticPromotion<Lhs, Rhs, false> {\n  using type = typename BigEnoughPromotion<Lhs, Rhs>::type;\n  static const bool is_contained = false;\n};\n\n// Extracts the underlying type from an enum.\ntemplate <typename T, bool is_enum = std::is_enum<T>::value>\nstruct ArithmeticOrUnderlyingEnum;\n\ntemplate <typename T>\nstruct ArithmeticOrUnderlyingEnum<T, true> {\n  using type = typename std::underlying_type<T>::type;\n  static const bool value = std::is_arithmetic<type>::value;\n};\n\ntemplate <typename T>\nstruct ArithmeticOrUnderlyingEnum<T, false> {\n  using type = T;\n  static const bool value = std::is_arithmetic<type>::value;\n};\n\n// The following are helper templates used in the CheckedNumeric class.\ntemplate <typename T>\nclass CheckedNumeric;\n\ntemplate <typename T>\nclass ClampedNumeric;\n\ntemplate <typename T>\nclass StrictNumeric;\n\n// Used to treat CheckedNumeric and arithmetic underlying types the same.\ntemplate <typename T>\nstruct UnderlyingType {\n  using type = typename ArithmeticOrUnderlyingEnum<T>::type;\n  static const bool is_numeric = std::is_arithmetic<type>::value;\n  static const bool is_checked = false;\n  static const bool is_clamped = false;\n  static const bool is_strict = false;\n};\n\ntemplate <typename T>\nstruct UnderlyingType<CheckedNumeric<T>> {\n  using type = T;\n  static const bool is_numeric = true;\n  static const bool is_checked = true;\n  static const bool is_clamped = false;\n  static const bool is_strict = false;\n};\n\ntemplate <typename T>\nstruct UnderlyingType<ClampedNumeric<T>> {\n  using type = T;\n  static const bool is_numeric = true;\n  static const bool is_checked = false;\n  static const bool is_clamped = true;\n  static const bool is_strict = false;\n};\n\ntemplate <typename T>\nstruct UnderlyingType<StrictNumeric<T>> {\n  using type = T;\n  static const bool is_numeric = true;\n  static const bool is_checked = false;\n  static const bool is_clamped = false;\n  static const bool is_strict = true;\n};\n\ntemplate <typename L, typename R>\nstruct IsCheckedOp {\n  static const bool value =\n      UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&\n      (UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked);\n};\n\ntemplate <typename L, typename R>\nstruct IsClampedOp {\n  static const bool value =\n      UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&\n      (UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped) &&\n      !(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked);\n};\n\ntemplate <typename L, typename R>\nstruct IsStrictOp {\n  static const bool value =\n      UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&\n      (UnderlyingType<L>::is_strict || UnderlyingType<R>::is_strict) &&\n      !(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked) &&\n      !(UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped);\n};\n\n// as_signed<> returns the supplied integral value (or integral castable\n// Numeric template) cast as a signed integral of equivalent precision.\n// I.e. it's mostly an alias for: static_cast<std::make_signed<T>::type>(t)\ntemplate <typename Src>\nconstexpr typename std::make_signed<\n    typename base::internal::UnderlyingType<Src>::type>::type\nas_signed(const Src value) {\n  static_assert(std::is_integral<decltype(as_signed(value))>::value,\n                \"Argument must be a signed or unsigned integer type.\");\n  return static_cast<decltype(as_signed(value))>(value);\n}\n\n// as_unsigned<> returns the supplied integral value (or integral castable\n// Numeric template) cast as an unsigned integral of equivalent precision.\n// I.e. it's mostly an alias for: static_cast<std::make_unsigned<T>::type>(t)\ntemplate <typename Src>\nconstexpr typename std::make_unsigned<\n    typename base::internal::UnderlyingType<Src>::type>::type\nas_unsigned(const Src value) {\n  static_assert(std::is_integral<decltype(as_unsigned(value))>::value,\n                \"Argument must be a signed or unsigned integer type.\");\n  return static_cast<decltype(as_unsigned(value))>(value);\n}\n\ntemplate <typename L, typename R>\nconstexpr bool IsLessImpl(const L lhs,\n                          const R rhs,\n                          const RangeCheck l_range,\n                          const RangeCheck r_range) {\n  return l_range.IsUnderflow() || r_range.IsOverflow() ||\n         (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) <\n                                    static_cast<decltype(lhs + rhs)>(rhs));\n}\n\ntemplate <typename L, typename R>\nstruct IsLess {\n  static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,\n                \"Types must be numeric.\");\n  static constexpr bool Test(const L lhs, const R rhs) {\n    return IsLessImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),\n                      DstRangeRelationToSrcRange<L>(rhs));\n  }\n};\n\ntemplate <typename L, typename R>\nconstexpr bool IsLessOrEqualImpl(const L lhs,\n                                 const R rhs,\n                                 const RangeCheck l_range,\n                                 const RangeCheck r_range) {\n  return l_range.IsUnderflow() || r_range.IsOverflow() ||\n         (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) <=\n                                    static_cast<decltype(lhs + rhs)>(rhs));\n}\n\ntemplate <typename L, typename R>\nstruct IsLessOrEqual {\n  static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,\n                \"Types must be numeric.\");\n  static constexpr bool Test(const L lhs, const R rhs) {\n    return IsLessOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),\n                             DstRangeRelationToSrcRange<L>(rhs));\n  }\n};\n\ntemplate <typename L, typename R>\nconstexpr bool IsGreaterImpl(const L lhs,\n                             const R rhs,\n                             const RangeCheck l_range,\n                             const RangeCheck r_range) {\n  return l_range.IsOverflow() || r_range.IsUnderflow() ||\n         (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) >\n                                    static_cast<decltype(lhs + rhs)>(rhs));\n}\n\ntemplate <typename L, typename R>\nstruct IsGreater {\n  static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,\n                \"Types must be numeric.\");\n  static constexpr bool Test(const L lhs, const R rhs) {\n    return IsGreaterImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),\n                         DstRangeRelationToSrcRange<L>(rhs));\n  }\n};\n\ntemplate <typename L, typename R>\nconstexpr bool IsGreaterOrEqualImpl(const L lhs,\n                                    const R rhs,\n                                    const RangeCheck l_range,\n                                    const RangeCheck r_range) {\n  return l_range.IsOverflow() || r_range.IsUnderflow() ||\n         (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) >=\n                                    static_cast<decltype(lhs + rhs)>(rhs));\n}\n\ntemplate <typename L, typename R>\nstruct IsGreaterOrEqual {\n  static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,\n                \"Types must be numeric.\");\n  static constexpr bool Test(const L lhs, const R rhs) {\n    return IsGreaterOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),\n                                DstRangeRelationToSrcRange<L>(rhs));\n  }\n};\n\ntemplate <typename L, typename R>\nstruct IsEqual {\n  static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,\n                \"Types must be numeric.\");\n  static constexpr bool Test(const L lhs, const R rhs) {\n    return DstRangeRelationToSrcRange<R>(lhs) ==\n               DstRangeRelationToSrcRange<L>(rhs) &&\n           static_cast<decltype(lhs + rhs)>(lhs) ==\n               static_cast<decltype(lhs + rhs)>(rhs);\n  }\n};\n\ntemplate <typename L, typename R>\nstruct IsNotEqual {\n  static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,\n                \"Types must be numeric.\");\n  static constexpr bool Test(const L lhs, const R rhs) {\n    return DstRangeRelationToSrcRange<R>(lhs) !=\n               DstRangeRelationToSrcRange<L>(rhs) ||\n           static_cast<decltype(lhs + rhs)>(lhs) !=\n               static_cast<decltype(lhs + rhs)>(rhs);\n  }\n};\n\n// These perform the actual math operations on the CheckedNumerics.\n// Binary arithmetic operations.\ntemplate <template <typename, typename> class C, typename L, typename R>\nconstexpr bool SafeCompare(const L lhs, const R rhs) {\n  static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,\n                \"Types must be numeric.\");\n  using Promotion = BigEnoughPromotion<L, R>;\n  using BigType = typename Promotion::type;\n  return Promotion::is_contained\n             // Force to a larger type for speed if both are contained.\n             ? C<BigType, BigType>::Test(\n                   static_cast<BigType>(static_cast<L>(lhs)),\n                   static_cast<BigType>(static_cast<R>(rhs)))\n             // Let the template functions figure it out for mixed types.\n             : C<L, R>::Test(lhs, rhs);\n}\n\ntemplate <typename Dst, typename Src>\nconstexpr bool IsMaxInRangeForNumericType() {\n  return IsGreaterOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::max(),\n                                          std::numeric_limits<Src>::max());\n}\n\ntemplate <typename Dst, typename Src>\nconstexpr bool IsMinInRangeForNumericType() {\n  return IsLessOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::lowest(),\n                                       std::numeric_limits<Src>::lowest());\n}\n\ntemplate <typename Dst, typename Src>\nconstexpr Dst CommonMax() {\n  return !IsMaxInRangeForNumericType<Dst, Src>()\n             ? Dst(std::numeric_limits<Dst>::max())\n             : Dst(std::numeric_limits<Src>::max());\n}\n\ntemplate <typename Dst, typename Src>\nconstexpr Dst CommonMin() {\n  return !IsMinInRangeForNumericType<Dst, Src>()\n             ? Dst(std::numeric_limits<Dst>::lowest())\n             : Dst(std::numeric_limits<Src>::lowest());\n}\n\n// This is a wrapper to generate return the max or min for a supplied type.\n// If the argument is false, the returned value is the maximum. If true the\n// returned value is the minimum.\ntemplate <typename Dst, typename Src = Dst>\nconstexpr Dst CommonMaxOrMin(bool is_min) {\n  return is_min ? CommonMin<Dst, Src>() : CommonMax<Dst, Src>();\n}\n\n}  // namespace internal\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_\n"
  },
  {
    "path": "tachyon/base/numerics/safe_math.h",
    "content": "// Copyright 2017 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_NUMERICS_SAFE_MATH_H_\n#define TACHYON_BASE_NUMERICS_SAFE_MATH_H_\n\n#include \"tachyon/base/numerics/checked_math.h\"\n#include \"tachyon/base/numerics/clamped_math.h\"\n#include \"tachyon/base/numerics/safe_conversions.h\"\n\n#endif  // TACHYON_BASE_NUMERICS_SAFE_MATH_H_\n"
  },
  {
    "path": "tachyon/base/numerics/safe_math_arm_impl.h",
    "content": "// Copyright 2017 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_NUMERICS_SAFE_MATH_ARM_IMPL_H_\n#define TACHYON_BASE_NUMERICS_SAFE_MATH_ARM_IMPL_H_\n\n#include <cassert>\n#include <type_traits>\n\n#include \"tachyon/base/numerics/safe_conversions.h\"\n\nnamespace tachyon::base {\nnamespace internal {\n\ntemplate <typename T, typename U>\nstruct CheckedMulFastAsmOp {\n  static const bool is_supported =\n      kEnableAsmCode && FastIntegerArithmeticPromotion<T, U>::is_contained;\n\n  // The following is not an assembler routine and is thus constexpr safe, it\n  // just emits much more efficient code than the Clang and GCC builtins for\n  // performing overflow-checked multiplication when a twice wider type is\n  // available. The below compiles down to 2-3 instructions, depending on the\n  // width of the types in use.\n  // As an example, an int32_t multiply compiles to:\n  //    smull   r0, r1, r0, r1\n  //    cmp     r1, r1, asr #31\n  // And an int16_t multiply compiles to:\n  //    smulbb  r1, r1, r0\n  //    asr     r2, r1, #16\n  //    cmp     r2, r1, asr #15\n  template <typename V>\n  static constexpr bool Do(T x, U y, V* result) {\n    using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type;\n    Promotion presult;\n\n    presult = static_cast<Promotion>(x) * static_cast<Promotion>(y);\n    if (!IsValueInRangeForNumericType<V>(presult))\n      return false;\n    *result = static_cast<V>(presult);\n    return true;\n  }\n};\n\ntemplate <typename T, typename U>\nstruct ClampedAddFastAsmOp {\n  static const bool is_supported =\n      kEnableAsmCode && BigEnoughPromotion<T, U>::is_contained &&\n      IsTypeInRangeForNumericType<\n          int32_t,\n          typename BigEnoughPromotion<T, U>::type>::value;\n\n  template <typename V>\n  __attribute__((always_inline)) static V Do(T x, U y) {\n    // This will get promoted to an int, so let the compiler do whatever is\n    // clever and rely on the saturated cast to bounds check.\n    if (IsIntegerArithmeticSafe<int, T, U>::value)\n      return saturated_cast<V>(static_cast<int>(x) + static_cast<int>(y));\n\n    int32_t result;\n    int32_t x_i32 = checked_cast<int32_t>(x);\n    int32_t y_i32 = checked_cast<int32_t>(y);\n\n    asm(\"qadd %[result], %[first], %[second]\"\n        : [result] \"=r\"(result)\n        : [first] \"r\"(x_i32), [second] \"r\"(y_i32));\n    return saturated_cast<V>(result);\n  }\n};\n\ntemplate <typename T, typename U>\nstruct ClampedSubFastAsmOp {\n  static const bool is_supported =\n      kEnableAsmCode && BigEnoughPromotion<T, U>::is_contained &&\n      IsTypeInRangeForNumericType<\n          int32_t,\n          typename BigEnoughPromotion<T, U>::type>::value;\n\n  template <typename V>\n  __attribute__((always_inline)) static V Do(T x, U y) {\n    // This will get promoted to an int, so let the compiler do whatever is\n    // clever and rely on the saturated cast to bounds check.\n    if (IsIntegerArithmeticSafe<int, T, U>::value)\n      return saturated_cast<V>(static_cast<int>(x) - static_cast<int>(y));\n\n    int32_t result;\n    int32_t x_i32 = checked_cast<int32_t>(x);\n    int32_t y_i32 = checked_cast<int32_t>(y);\n\n    asm(\"qsub %[result], %[first], %[second]\"\n        : [result] \"=r\"(result)\n        : [first] \"r\"(x_i32), [second] \"r\"(y_i32));\n    return saturated_cast<V>(result);\n  }\n};\n\ntemplate <typename T, typename U>\nstruct ClampedMulFastAsmOp {\n  static const bool is_supported =\n      kEnableAsmCode && CheckedMulFastAsmOp<T, U>::is_supported;\n\n  template <typename V>\n  __attribute__((always_inline)) static V Do(T x, U y) {\n    // Use the CheckedMulFastAsmOp for full-width 32-bit values, because\n    // it's fewer instructions than promoting and then saturating.\n    if (!IsIntegerArithmeticSafe<int32_t, T, U>::value &&\n        !IsIntegerArithmeticSafe<uint32_t, T, U>::value) {\n      V result;\n      return CheckedMulFastAsmOp<T, U>::Do(x, y, &result)\n                 ? result\n                 : CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y));\n    }\n\n    assert((FastIntegerArithmeticPromotion<T, U>::is_contained));\n    using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type;\n    return saturated_cast<V>(static_cast<Promotion>(x) *\n                             static_cast<Promotion>(y));\n  }\n};\n\n}  // namespace internal\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_NUMERICS_SAFE_MATH_ARM_IMPL_H_\n"
  },
  {
    "path": "tachyon/base/numerics/safe_math_clang_gcc_impl.h",
    "content": "// Copyright 2017 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_NUMERICS_SAFE_MATH_CLANG_GCC_IMPL_H_\n#define TACHYON_BASE_NUMERICS_SAFE_MATH_CLANG_GCC_IMPL_H_\n\n#include <cassert>\n#include <limits>\n#include <type_traits>\n\n#include \"tachyon/base/numerics/safe_conversions.h\"\n\n#if !defined(__native_client__) && (defined(__ARMEL__) || defined(__arch64__))\n#include \"tachyon/base/numerics/safe_math_arm_impl.h\"\n#define BASE_HAS_ASSEMBLER_SAFE_MATH (1)\n#else\n#define BASE_HAS_ASSEMBLER_SAFE_MATH (0)\n#endif\n\nnamespace tachyon::base {\nnamespace internal {\n\n// These are the non-functioning boilerplate implementations of the optimized\n// safe math routines.\n#if !BASE_HAS_ASSEMBLER_SAFE_MATH\ntemplate <typename T, typename U>\nstruct CheckedMulFastAsmOp {\n  static const bool is_supported = false;\n  template <typename V>\n  static constexpr bool Do(T, U, V*) {\n    // Force a compile failure if instantiated.\n    return CheckOnFailure::template HandleFailure<bool>();\n  }\n};\n\ntemplate <typename T, typename U>\nstruct ClampedAddFastAsmOp {\n  static const bool is_supported = false;\n  template <typename V>\n  static constexpr V Do(T, U) {\n    // Force a compile failure if instantiated.\n    return CheckOnFailure::template HandleFailure<V>();\n  }\n};\n\ntemplate <typename T, typename U>\nstruct ClampedSubFastAsmOp {\n  static const bool is_supported = false;\n  template <typename V>\n  static constexpr V Do(T, U) {\n    // Force a compile failure if instantiated.\n    return CheckOnFailure::template HandleFailure<V>();\n  }\n};\n\ntemplate <typename T, typename U>\nstruct ClampedMulFastAsmOp {\n  static const bool is_supported = false;\n  template <typename V>\n  static constexpr V Do(T, U) {\n    // Force a compile failure if instantiated.\n    return CheckOnFailure::template HandleFailure<V>();\n  }\n};\n#endif  // BASE_HAS_ASSEMBLER_SAFE_MATH\n#undef BASE_HAS_ASSEMBLER_SAFE_MATH\n\ntemplate <typename T, typename U>\nstruct CheckedAddFastOp {\n  static const bool is_supported = true;\n  template <typename V>\n  __attribute__((always_inline)) static constexpr bool Do(T x, U y, V* result) {\n    return !__builtin_add_overflow(x, y, result);\n  }\n};\n\ntemplate <typename T, typename U>\nstruct CheckedSubFastOp {\n  static const bool is_supported = true;\n  template <typename V>\n  __attribute__((always_inline)) static constexpr bool Do(T x, U y, V* result) {\n    return !__builtin_sub_overflow(x, y, result);\n  }\n};\n\ntemplate <typename T, typename U>\nstruct CheckedMulFastOp {\n#if defined(__clang__)\n  // TODO(jschuh): Get the Clang runtime library issues sorted out so we can\n  // support full-width, mixed-sign multiply builtins.\n  // https://crbug.com/613003\n  // We can support intptr_t, uintptr_t, or a smaller common type.\n  static const bool is_supported =\n      (IsTypeInRangeForNumericType<intptr_t, T>::value &&\n       IsTypeInRangeForNumericType<intptr_t, U>::value) ||\n      (IsTypeInRangeForNumericType<uintptr_t, T>::value &&\n       IsTypeInRangeForNumericType<uintptr_t, U>::value);\n#else\n  static const bool is_supported = true;\n#endif\n  template <typename V>\n  __attribute__((always_inline)) static constexpr bool Do(T x, U y, V* result) {\n    return CheckedMulFastAsmOp<T, U>::is_supported\n               ? CheckedMulFastAsmOp<T, U>::Do(x, y, result)\n               : !__builtin_mul_overflow(x, y, result);\n  }\n};\n\ntemplate <typename T, typename U>\nstruct ClampedAddFastOp {\n  static const bool is_supported = ClampedAddFastAsmOp<T, U>::is_supported;\n  template <typename V>\n  __attribute__((always_inline)) static V Do(T x, U y) {\n    return ClampedAddFastAsmOp<T, U>::template Do<V>(x, y);\n  }\n};\n\ntemplate <typename T, typename U>\nstruct ClampedSubFastOp {\n  static const bool is_supported = ClampedSubFastAsmOp<T, U>::is_supported;\n  template <typename V>\n  __attribute__((always_inline)) static V Do(T x, U y) {\n    return ClampedSubFastAsmOp<T, U>::template Do<V>(x, y);\n  }\n};\n\ntemplate <typename T, typename U>\nstruct ClampedMulFastOp {\n  static const bool is_supported = ClampedMulFastAsmOp<T, U>::is_supported;\n  template <typename V>\n  __attribute__((always_inline)) static V Do(T x, U y) {\n    return ClampedMulFastAsmOp<T, U>::template Do<V>(x, y);\n  }\n};\n\ntemplate <typename T>\nstruct ClampedNegFastOp {\n  static const bool is_supported = std::is_signed<T>::value;\n  __attribute__((always_inline)) static T Do(T value) {\n    // Use this when there is no assembler path available.\n    if (!ClampedSubFastAsmOp<T, T>::is_supported) {\n      T result;\n      return !__builtin_sub_overflow(T(0), value, &result)\n                 ? result\n                 : std::numeric_limits<T>::max();\n    }\n\n    // Fallback to the normal subtraction path.\n    return ClampedSubFastOp<T, T>::template Do<T>(T(0), value);\n  }\n};\n\n}  // namespace internal\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_NUMERICS_SAFE_MATH_CLANG_GCC_IMPL_H_\n"
  },
  {
    "path": "tachyon/base/numerics/safe_math_shared_impl.h",
    "content": "// Copyright 2017 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_\n#define TACHYON_BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <cassert>\n#include <climits>\n#include <cmath>\n#include <cstdlib>\n#include <limits>\n#include <type_traits>\n\n#include \"tachyon/base/numerics/safe_conversions.h\"\n#include \"tachyon/build/build_config.h\"\n\n#if BUILDFLAG(IS_ASMJS)\n// Optimized safe math instructions are incompatible with asmjs.\n#define BASE_HAS_OPTIMIZED_SAFE_MATH (0)\n// Where available use builtin math overflow support on Clang and GCC.\n#elif !defined(__native_client__) &&                       \\\n    ((defined(__clang__) &&                                \\\n      ((__clang_major__ > 3) ||                            \\\n       (__clang_major__ == 3 && __clang_minor__ >= 4))) || \\\n     (defined(__GNUC__) && __GNUC__ >= 5))\n#include \"tachyon/base/numerics/safe_math_clang_gcc_impl.h\"\n#define BASE_HAS_OPTIMIZED_SAFE_MATH (1)\n#else\n#define BASE_HAS_OPTIMIZED_SAFE_MATH (0)\n#endif\n\nnamespace tachyon::base {\nnamespace internal {\n\n// These are the non-functioning boilerplate implementations of the optimized\n// safe math routines.\n#if !BASE_HAS_OPTIMIZED_SAFE_MATH\ntemplate <typename T, typename U>\nstruct CheckedAddFastOp {\n  static const bool is_supported = false;\n  template <typename V>\n  static constexpr bool Do(T, U, V*) {\n    // Force a compile failure if instantiated.\n    return CheckOnFailure::template HandleFailure<bool>();\n  }\n};\n\ntemplate <typename T, typename U>\nstruct CheckedSubFastOp {\n  static const bool is_supported = false;\n  template <typename V>\n  static constexpr bool Do(T, U, V*) {\n    // Force a compile failure if instantiated.\n    return CheckOnFailure::template HandleFailure<bool>();\n  }\n};\n\ntemplate <typename T, typename U>\nstruct CheckedMulFastOp {\n  static const bool is_supported = false;\n  template <typename V>\n  static constexpr bool Do(T, U, V*) {\n    // Force a compile failure if instantiated.\n    return CheckOnFailure::template HandleFailure<bool>();\n  }\n};\n\ntemplate <typename T, typename U>\nstruct ClampedAddFastOp {\n  static const bool is_supported = false;\n  template <typename V>\n  static constexpr V Do(T, U) {\n    // Force a compile failure if instantiated.\n    return CheckOnFailure::template HandleFailure<V>();\n  }\n};\n\ntemplate <typename T, typename U>\nstruct ClampedSubFastOp {\n  static const bool is_supported = false;\n  template <typename V>\n  static constexpr V Do(T, U) {\n    // Force a compile failure if instantiated.\n    return CheckOnFailure::template HandleFailure<V>();\n  }\n};\n\ntemplate <typename T, typename U>\nstruct ClampedMulFastOp {\n  static const bool is_supported = false;\n  template <typename V>\n  static constexpr V Do(T, U) {\n    // Force a compile failure if instantiated.\n    return CheckOnFailure::template HandleFailure<V>();\n  }\n};\n\ntemplate <typename T>\nstruct ClampedNegFastOp {\n  static const bool is_supported = false;\n  static constexpr T Do(T) {\n    // Force a compile failure if instantiated.\n    return CheckOnFailure::template HandleFailure<T>();\n  }\n};\n#endif  // BASE_HAS_OPTIMIZED_SAFE_MATH\n#undef BASE_HAS_OPTIMIZED_SAFE_MATH\n\n// This is used for UnsignedAbs, where we need to support floating-point\n// template instantiations even though we don't actually support the operations.\n// However, there is no corresponding implementation of e.g. SafeUnsignedAbs,\n// so the float versions will not compile.\ntemplate <typename Numeric,\n          bool IsInteger = std::is_integral<Numeric>::value,\n          bool IsFloat = std::is_floating_point<Numeric>::value>\nstruct UnsignedOrFloatForSize;\n\ntemplate <typename Numeric>\nstruct UnsignedOrFloatForSize<Numeric, true, false> {\n  using type = typename std::make_unsigned<Numeric>::type;\n};\n\ntemplate <typename Numeric>\nstruct UnsignedOrFloatForSize<Numeric, false, true> {\n  using type = Numeric;\n};\n\n// Wrap the unary operations to allow SFINAE when instantiating integrals versus\n// floating points. These don't perform any overflow checking. Rather, they\n// exhibit well-defined overflow semantics and rely on the caller to detect\n// if an overflow occurred.\n\ntemplate <typename T,\n          typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>\nconstexpr T NegateWrapper(T value) {\n  using UnsignedT = typename std::make_unsigned<T>::type;\n  // This will compile to a NEG on Intel, and is normal negation on ARM.\n  return static_cast<T>(UnsignedT(0) - static_cast<UnsignedT>(value));\n}\n\ntemplate <\n    typename T,\n    typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>\nconstexpr T NegateWrapper(T value) {\n  return -value;\n}\n\ntemplate <typename T,\n          typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>\nconstexpr typename std::make_unsigned<T>::type InvertWrapper(T value) {\n  return ~value;\n}\n\ntemplate <typename T,\n          typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>\nconstexpr T AbsWrapper(T value) {\n  return static_cast<T>(SafeUnsignedAbs(value));\n}\n\ntemplate <\n    typename T,\n    typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>\nconstexpr T AbsWrapper(T value) {\n  return value < 0 ? -value : value;\n}\n\ntemplate <template <typename, typename, typename> class M,\n          typename L,\n          typename R>\nstruct MathWrapper {\n  using math = M<typename UnderlyingType<L>::type,\n                 typename UnderlyingType<R>::type,\n                 void>;\n  using type = typename math::result_type;\n};\n\n// The following macros are just boilerplate for the standard arithmetic\n// operator overloads and variadic function templates. A macro isn't the nicest\n// solution, but it beats rewriting these over and over again.\n#define BASE_NUMERIC_ARITHMETIC_VARIADIC(CLASS, CL_ABBR, OP_NAME)       \\\n  template <typename L, typename R, typename... Args>                   \\\n  constexpr auto CL_ABBR##OP_NAME(const L lhs, const R rhs,             \\\n                                  const Args... args) {                 \\\n    return CL_ABBR##MathOp<CLASS##OP_NAME##Op, L, R, Args...>(lhs, rhs, \\\n                                                              args...); \\\n  }\n\n#define BASE_NUMERIC_ARITHMETIC_OPERATORS(CLASS, CL_ABBR, OP_NAME, OP, CMP_OP) \\\n  /* Binary arithmetic operator for all CLASS##Numeric operations. */          \\\n  template <typename L, typename R,                                            \\\n            typename std::enable_if<Is##CLASS##Op<L, R>::value>::type* =       \\\n                nullptr>                                                       \\\n  constexpr CLASS##Numeric<                                                    \\\n      typename MathWrapper<CLASS##OP_NAME##Op, L, R>::type>                    \\\n  operator OP(const L lhs, const R rhs) {                                      \\\n    return decltype(lhs OP rhs)::template MathOp<CLASS##OP_NAME##Op>(lhs,      \\\n                                                                     rhs);     \\\n  }                                                                            \\\n  /* Assignment arithmetic operator implementation from CLASS##Numeric. */     \\\n  template <typename L>                                                        \\\n  template <typename R>                                                        \\\n  constexpr CLASS##Numeric<L>& CLASS##Numeric<L>::operator CMP_OP(             \\\n      const R rhs) {                                                           \\\n    return MathOp<CLASS##OP_NAME##Op>(rhs);                                    \\\n  }                                                                            \\\n  /* Variadic arithmetic functions that return CLASS##Numeric. */              \\\n  BASE_NUMERIC_ARITHMETIC_VARIADIC(CLASS, CL_ABBR, OP_NAME)\n\n}  // namespace internal\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_\n"
  },
  {
    "path": "tachyon/base/openmp_util.h",
    "content": "#ifndef TACHYON_BASE_OPENMP_UTIL_H_\n#define TACHYON_BASE_OPENMP_UTIL_H_\n\n#include <algorithm>\n#include <optional>\n// NOTE(chokobole): There's no specific header for |std::size()|, but it causes\n// an compiler error on g++-12. See\n// https://en.cppreference.com/w/cpp/iterator/size.\n#include <vector>\n\n#if defined(TACHYON_HAS_OPENMP)\n#include <omp.h>\n#endif  // defined(TACHYON_HAS_OPENMP)\n\n#if defined(TACHYON_HAS_OPENMP)\n#define CONSTEXPR_IF_NOT_OPENMP\n#define OMP_FOR(expr) _Pragma(\"omp for\") for (expr)\n#define OMP_FOR_NOWAIT(expr) _Pragma(\"omp for nowait\") for (expr)\n#define OMP_NESTED_FOR(expr) _Pragma(\"omp for collapse(2)\") for (expr)\n#define OMP_PARALLEL _Pragma(\"omp parallel\")\n#define OMP_PARALLEL_FOR(expr) _Pragma(\"omp parallel for\") for (expr)\n#define OMP_PARALLEL_NESTED_FOR(expr) \\\n  _Pragma(\"omp parallel for collapse(2)\") for (expr)\n#define OMP_PARALLEL_DYNAMIC_FOR(expr) \\\n  _Pragma(\"omp parallel for schedule(dynamic)\") for (expr)\n#else\n#define CONSTEXPR_IF_NOT_OPENMP constexpr\n#define OMP_FOR(expr) for (expr)\n#define OMP_FOR_NOWAIT(expr) for (expr)\n#define OMP_NESTED_FOR(expr) for (expr)\n#define OMP_PARALLEL\n#define OMP_PARALLEL_FOR(expr) for (expr)\n#define OMP_PARALLEL_NESTED_FOR(expr) for (expr)\n#define OMP_PARALLEL_DYNAMIC_FOR(expr) for (expr)\n#endif  // defined(TACHYON_HAS_OPENMP)\n\nnamespace tachyon::base {\n\n// NOTE(chokobole): This function might return 0. You should handle this case\n// carefully. See other examples where it is used.\ninline size_t GetSizePerThread(size_t total_size,\n                               std::optional<size_t> threshold = std::nullopt) {\n#if defined(TACHYON_HAS_OPENMP)\n  size_t thread_nums = static_cast<size_t>(omp_get_max_threads());\n#else\n  size_t thread_nums = 1;\n#endif\n  return (!threshold.has_value() || total_size > threshold.value())\n             ? (total_size + thread_nums - 1) / thread_nums\n             : total_size;\n}\n\n// NOTE(chokobole): This function might return 0. You should handle this case\n// carefully. See other examples where it is used.\ntemplate <typename Container>\nsize_t GetNumElementsPerThread(const Container& container,\n                               std::optional<size_t> threshold = std::nullopt) {\n  return GetSizePerThread(std::size(container));\n}\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_OPENMP_UTIL_H_\n"
  },
  {
    "path": "tachyon/base/optional.h",
    "content": "#ifndef TACHYON_BASE_OPTIONAL_H_\n#define TACHYON_BASE_OPTIONAL_H_\n\n#include <optional>\n#include <utility>\n\n#include \"tachyon/base/logging.h\"\n\ntemplate <typename T>\nT&& unwrap(std::optional<T>&& optional_value) {\n  CHECK(optional_value);\n  return std::move(*optional_value);\n}\n\n#endif  // TACHYON_BASE_OPTIONAL_H_\n"
  },
  {
    "path": "tachyon/base/parallelize.h",
    "content": "#ifndef TACHYON_BASE_PARALLELIZE_H_\n#define TACHYON_BASE_PARALLELIZE_H_\n\n#include <optional>\n#include <utility>\n#include <vector>\n\n#include \"absl/types/span.h\"\n\n#include \"tachyon/base/functional/functor_traits.h\"\n#include \"tachyon/base/openmp_util.h\"\n\nnamespace tachyon::base {\nnamespace internal {\n\ntemplate <typename Container, typename Callable,\n          typename FunctorTraits = internal::MakeFunctorTraits<Callable>,\n          typename RunType = typename FunctorTraits::RunType,\n          typename ArgList = internal::ExtractArgs<RunType>,\n          typename SpanTy = internal::GetType<0, ArgList>,\n          size_t ArgNum = internal::GetSize<ArgList>>\nvoid InvokeParallelizeCallback(Container& container, size_t i,\n                               size_t num_chunks, size_t chunk_size,\n                               Callable callback) {\n  size_t len =\n      i == num_chunks - 1 ? std::size(container) - i * chunk_size : chunk_size;\n  SpanTy chunk(std::data(container) + i * chunk_size, len);\n  if constexpr (ArgNum == 1) {\n    callback(chunk);\n  } else if constexpr (ArgNum == 2) {\n    callback(chunk, i);\n  } else {\n    static_assert(ArgNum == 3);\n    callback(chunk, i, chunk_size);\n  }\n}\n\ntemplate <typename Callable,\n          typename FunctorTraits = internal::MakeFunctorTraits<Callable>,\n          typename RunType = typename FunctorTraits::RunType,\n          typename ArgList = internal::ExtractArgs<RunType>,\n          size_t ArgNum = internal::GetSize<ArgList>>\nvoid InvokeParallelizeCallback(size_t size, size_t i, size_t num_chunks,\n                               size_t chunk_size, Callable callback) {\n  size_t len = i == num_chunks - 1 ? size - i * chunk_size : chunk_size;\n  if constexpr (ArgNum == 1) {\n    callback(len);\n  } else if constexpr (ArgNum == 2) {\n    callback(len, i);\n  } else {\n    static_assert(ArgNum == 3);\n    callback(len, i, chunk_size);\n  }\n}\n\ntemplate <typename Container, typename Callable,\n          typename FunctorTraits = internal::MakeFunctorTraits<Callable>,\n          typename RunType = typename FunctorTraits::RunType,\n          typename ReturnType = typename FunctorTraits::ReturnType,\n          typename ArgList = internal::ExtractArgs<RunType>,\n          typename SpanTy = internal::GetType<0, ArgList>,\n          size_t ArgNum = internal::GetSize<ArgList>>\nvoid InvokeParallelizeCallback(Container& container, size_t i,\n                               size_t num_chunks, size_t chunk_size,\n                               Callable callback,\n                               std::vector<ReturnType>& values) {\n  size_t len =\n      i == num_chunks - 1 ? std::size(container) - i * chunk_size : chunk_size;\n  SpanTy chunk(std::data(container) + i * chunk_size, len);\n  if constexpr (ArgNum == 1) {\n    values[i] = callback(chunk);\n  } else if constexpr (ArgNum == 2) {\n    values[i] = callback(chunk, i);\n  } else {\n    static_assert(ArgNum == 3);\n    values[i] = callback(chunk, i, chunk_size);\n  }\n}\n\ntemplate <typename Callable,\n          typename FunctorTraits = internal::MakeFunctorTraits<Callable>,\n          typename RunType = typename FunctorTraits::RunType,\n          typename ReturnType = typename FunctorTraits::ReturnType,\n          typename ArgList = internal::ExtractArgs<RunType>,\n          size_t ArgNum = internal::GetSize<ArgList>>\nvoid InvokeParallelizeCallback(size_t size, size_t i, size_t num_chunks,\n                               size_t chunk_size, Callable callback,\n                               std::vector<ReturnType>& values) {\n  size_t len = i == num_chunks - 1 ? size - i * chunk_size : chunk_size;\n  if constexpr (ArgNum == 1) {\n    values[i] = callback(len);\n  } else if constexpr (ArgNum == 2) {\n    values[i] = callback(len, i);\n  } else {\n    static_assert(ArgNum == 3);\n    values[i] = callback(len, i, chunk_size);\n  }\n}\n}  // namespace internal\n\ntemplate <typename T>\nusing ParallelizeCallback1 = std::function<void(absl::Span<T>)>;\ntemplate <typename T>\nusing ParallelizeCallback2 = std::function<void(absl::Span<T>, size_t)>;\ntemplate <typename T>\nusing ParallelizeCallback3 = std::function<void(absl::Span<T>, size_t, size_t)>;\n\n// Splits the |container| by |chunk_size| and executes |callback| in parallel.\n// See parallelize_unittest.cc for more details.\ntemplate <typename Container, typename Callable>\nvoid ParallelizeByChunkSize(Container& container, size_t chunk_size,\n                            Callable callback) {\n  if (chunk_size == 0) return;\n  size_t num_chunks = (std::size(container) + chunk_size - 1) / chunk_size;\n  if (num_chunks == 1) {\n    internal::InvokeParallelizeCallback(container, 0, num_chunks, chunk_size,\n                                        callback);\n    return;\n  }\n  OMP_PARALLEL_FOR(size_t i = 0; i < num_chunks; ++i) {\n    internal::InvokeParallelizeCallback(container, i, num_chunks, chunk_size,\n                                        callback);\n  }\n}\n\n// Splits the |size| by |chunk_size| and executes |callback| in parallel.\ntemplate <typename Callable>\nvoid ParallelizeByChunkSize(size_t size, size_t chunk_size, Callable callback) {\n  if (chunk_size == 0) return;\n  size_t num_chunks = (size + chunk_size - 1) / chunk_size;\n  if (num_chunks == 1) {\n    internal::InvokeParallelizeCallback(size, 0, num_chunks, chunk_size,\n                                        callback);\n    return;\n  }\n  OMP_PARALLEL_FOR(size_t i = 0; i < num_chunks; ++i) {\n    internal::InvokeParallelizeCallback(size, i, num_chunks, chunk_size,\n                                        callback);\n  }\n}\n\n// Splits the |container| by |chunk_size| and executes |callback| in parallel.\n// Dynamically schedules tasks to ensure most efficient use of threads.\ntemplate <typename Container, typename Callable>\nvoid DynamicParallelizeByChunkSize(Container& container, size_t chunk_size,\n                                   Callable callback) {\n  if (chunk_size == 0) return;\n  size_t num_chunks = (std::size(container) + chunk_size - 1) / chunk_size;\n  if (num_chunks == 1) {\n    internal::InvokeParallelizeCallback(container, 0, num_chunks, chunk_size,\n                                        callback);\n    return;\n  }\n  OMP_PARALLEL_DYNAMIC_FOR(size_t i = 0; i < num_chunks; ++i) {\n    internal::InvokeParallelizeCallback(container, i, num_chunks, chunk_size,\n                                        callback);\n  }\n}\n\n// Splits the |size| by |chunk_size| and executes |callback| in parallel.\n// Dynamically schedules tasks to ensure most efficient use of threads.\ntemplate <typename Callable>\nvoid DynamicParallelizeByChunkSize(size_t size, size_t chunk_size,\n                                   Callable callback) {\n  if (chunk_size == 0) return;\n  size_t num_chunks = (size + chunk_size - 1) / chunk_size;\n  if (num_chunks == 1) {\n    internal::InvokeParallelizeCallback(size, 0, num_chunks, chunk_size,\n                                        callback);\n    return;\n  }\n  OMP_PARALLEL_DYNAMIC_FOR(size_t i = 0; i < num_chunks; ++i) {\n    internal::InvokeParallelizeCallback(size, i, num_chunks, chunk_size,\n                                        callback);\n  }\n}\n\n// Splits the |container| into threads and executes |callback| in parallel.\n// See parallelize_unittest.cc for more details.\ntemplate <typename Container, typename Callable>\nvoid Parallelize(Container& container, Callable callback,\n                 std::optional<size_t> threshold = std::nullopt) {\n  size_t num_elements_per_thread =\n      GetNumElementsPerThread(container, threshold);\n  ParallelizeByChunkSize(container, num_elements_per_thread,\n                         std::move(callback));\n}\n\n// Splits the |size| into threads and executes |callback| in parallel.\ntemplate <typename Callable>\nvoid Parallelize(size_t size, Callable callback,\n                 std::optional<size_t> threshold = std::nullopt) {\n  size_t size_per_thread = GetSizePerThread(size, threshold);\n  ParallelizeByChunkSize(size, size_per_thread, std::move(callback));\n}\n\n// Splits the |container| by |chunk_size| and maps each chunk using the provided\n// |callback| in parallel. Each callback's return value is collected into a\n// vector which is then returned.\n// See parallelize_unittest.cc for more details.\ntemplate <typename Container, typename Callable,\n          typename FunctorTraits = internal::MakeFunctorTraits<Callable>,\n          typename RunType = typename FunctorTraits::RunType,\n          typename ReturnType = typename FunctorTraits::ReturnType>\nstd::vector<ReturnType> ParallelizeMapByChunkSize(Container& container,\n                                                  size_t chunk_size,\n                                                  Callable callback) {\n  if (chunk_size == 0) return {};\n  size_t num_chunks = (std::size(container) + chunk_size - 1) / chunk_size;\n  std::vector<ReturnType> values(num_chunks);\n  if (num_chunks == 1) {\n    internal::InvokeParallelizeCallback(container, 0, num_chunks, chunk_size,\n                                        callback, values);\n    return values;\n  }\n  OMP_PARALLEL_FOR(size_t i = 0; i < num_chunks; ++i) {\n    internal::InvokeParallelizeCallback(container, i, num_chunks, chunk_size,\n                                        callback, values);\n  }\n  return values;\n}\n\n// Splits the |size| by |chunk_size| and maps each chunk using the provided\n// |callback| in parallel. Each callback's return value is collected into a\n// vector which is then returned.\ntemplate <typename Callable,\n          typename FunctorTraits = internal::MakeFunctorTraits<Callable>,\n          typename RunType = typename FunctorTraits::RunType,\n          typename ReturnType = typename FunctorTraits::ReturnType>\nstd::vector<ReturnType> ParallelizeMapByChunkSize(size_t size,\n                                                  size_t chunk_size,\n                                                  Callable callback) {\n  if (chunk_size == 0) return {};\n  size_t num_chunks = (size + chunk_size - 1) / chunk_size;\n  std::vector<ReturnType> values(num_chunks);\n  if (num_chunks == 1) {\n    internal::InvokeParallelizeCallback(size, 0, num_chunks, chunk_size,\n                                        callback, values);\n    return values;\n  }\n  OMP_PARALLEL_FOR(size_t i = 0; i < num_chunks; ++i) {\n    internal::InvokeParallelizeCallback(size, i, num_chunks, chunk_size,\n                                        callback, values);\n  }\n  return values;\n}\n\n// Splits the |container| into threads and maps each chunk using the provided\n// |callback| in parallel. The results from each callback are collected into a\n// vector and returned.\n// See parallelize_unittest.cc for more details.\ntemplate <typename Container, typename Callable>\nauto ParallelizeMap(Container& container, Callable callback,\n                    std::optional<size_t> threshold = std::nullopt) {\n  size_t num_elements_per_thread =\n      GetNumElementsPerThread(container, threshold);\n  return ParallelizeMapByChunkSize(container, num_elements_per_thread,\n                                   std::move(callback));\n}\n\n// Splits the |size| into threads and maps each chunk using the provided\n// |callback| in parallel. The results from each callback are collected into a\n// vector and returned.\ntemplate <typename Callable>\nauto ParallelizeMap(size_t size, Callable callback,\n                    std::optional<size_t> threshold = std::nullopt) {\n  size_t num_elements_per_thread = GetSizePerThread(size, threshold);\n  return ParallelizeMapByChunkSize(size, num_elements_per_thread,\n                                   std::move(callback));\n}\n\ntemplate <typename Container>\nvoid ParallelizeFill(Container& container, typename Container::value_type value,\n                     std::optional<size_t> threshold = std::nullopt) {\n  Parallelize(\n      container,\n      [&value](absl::Span<typename Container::value_type> chunk) {\n        for (auto& v : chunk) {\n          v = value;\n        }\n      },\n      threshold);\n}\n\ntemplate <typename Container>\nvoid ParallelizeResize(Container& container, size_t size,\n                       std::optional<size_t> threshold = std::nullopt) {\n  if (container.capacity() > size) {\n    container.resize(size);\n  } else {\n    std::vector<typename Container::value_type> new_container(size);\n    auto copy_span = absl::MakeSpan(new_container).first(container.size());\n    Parallelize(\n        copy_span,\n        [&container](absl::Span<typename Container::value_type> chunk,\n                     size_t chunk_offset, size_t chunk_size) {\n          size_t start = chunk_offset * chunk_size;\n          for (size_t i = 0; i < chunk.size(); ++i) {\n            chunk[i] = std::move(container[start + i]);\n          }\n        },\n        threshold);\n    container = std::move(new_container);\n  }\n}\n\ntemplate <typename Container>\nvoid ParallelizeResize(Container& container, size_t size,\n                       typename Container::value_type value,\n                       std::optional<size_t> threshold = std::nullopt) {\n  if (container.capacity() > size) {\n    size_t old_size = container.size();\n    container.resize(size);\n    auto init_span = absl::MakeSpan(container).last(size - old_size);\n    Parallelize(\n        init_span,\n        [&value](absl::Span<typename Container::value_type> chunk) {\n          std::fill(chunk.begin(), chunk.end(), value);\n        },\n        threshold);\n  } else {\n    std::vector<typename Container::value_type> new_container(size);\n    Parallelize(\n        new_container,\n        [&container, &value](absl::Span<typename Container::value_type> chunk,\n                             size_t chunk_offset, size_t chunk_size) {\n          size_t start = chunk_offset * chunk_size;\n          for (size_t i = 0; i < chunk.size(); ++i) {\n            chunk[i] = (start + i) < container.size()\n                           ? std::move(container[start + i])\n                           : value;\n          }\n        },\n        threshold);\n    container = std::move(new_container);\n  }\n}\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_PARALLELIZE_H_\n"
  },
  {
    "path": "tachyon/base/parallelize_unittest.cc",
    "content": "#include \"tachyon/base/parallelize.h\"\n\n#include <functional>\n#include <numeric>\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n\nnamespace tachyon::base {\n\nnamespace {\n\nvoid IncrementEachElement(absl::Span<int> chunk) {\n  for (int& v : chunk) {\n    v += 1;\n  }\n}\n\nint SumElements(absl::Span<int> chunk) {\n  int ret = 0;\n  for (int v : chunk) {\n    ret += v;\n  }\n  return ret;\n}\n\n}  // namespace\n\nTEST(ParallelizeTest, ParallelizeByChunks) {\n  std::vector<int> test_in = {0, 1, 2, 3, 4, 5};\n  std::vector<int> expected = {1, 2, 3, 4, 5, 6};\n  size_t chunk_size = 2;\n\n  ParallelizeByChunkSize(test_in, chunk_size,\n                         [chunk_size](absl::Span<int> chunk) {\n                           EXPECT_EQ(chunk.size(), chunk_size);\n                           IncrementEachElement(chunk);\n                         });\n  EXPECT_EQ(expected, test_in);\n\n  expected = {2, 3, 4, 5, 6, 7};\n  std::vector<size_t> chunk_indices(test_in.size() / chunk_size, size_t{0});\n  ParallelizeByChunkSize(\n      test_in, chunk_size,\n      [chunk_size, &chunk_indices](absl::Span<int> chunk, size_t chunk_offset) {\n        EXPECT_EQ(chunk.size(), chunk_size);\n        chunk_indices[chunk_offset] = chunk_offset;\n        IncrementEachElement(chunk);\n      });\n  EXPECT_EQ(expected, test_in);\n  EXPECT_THAT(chunk_indices, testing::UnorderedElementsAreArray({0, 1, 2}));\n\n  expected = {3, 4, 5, 6, 7, 8};\n  chunk_indices = std::vector<size_t>(test_in.size() / chunk_size, size_t{0});\n  std::vector<size_t> chunk_sizes(test_in.size() / chunk_size, size_t{0});\n  ParallelizeByChunkSize(\n      test_in, chunk_size,\n      [chunk_size, &chunk_indices, &chunk_sizes](\n          absl::Span<int> chunk, size_t chunk_index, size_t chunk_size_in) {\n        EXPECT_EQ(chunk.size(), chunk_size);\n        chunk_indices[chunk_index] = chunk_index;\n        chunk_sizes[chunk_index] = chunk_size_in;\n        IncrementEachElement(chunk);\n      });\n  EXPECT_EQ(expected, test_in);\n  EXPECT_THAT(chunk_indices, testing::UnorderedElementsAreArray({0, 1, 2}));\n  EXPECT_THAT(chunk_sizes, testing::UnorderedElementsAreArray({2, 2, 2}));\n}\n\nTEST(ParallelizeTest, Parallelize) {\n  std::vector<int> test_in = {0, 1, 2, 3, 4, 5};\n  std::vector<int> expected = {1, 2, 3, 4, 5, 6};\n\n  Parallelize(test_in, &IncrementEachElement);\n  EXPECT_EQ(expected, test_in);\n\n  expected = {2, 3, 4, 5, 6, 7};\n  Parallelize(test_in, [](absl::Span<int> chunk, size_t chunk_idx) {\n    IncrementEachElement(chunk);\n  });\n  EXPECT_EQ(expected, test_in);\n\n  expected = {3, 4, 5, 6, 7, 8};\n  Parallelize(test_in, [](absl::Span<int> chunk, size_t chunk_idx,\n                          size_t chunk_size) { IncrementEachElement(chunk); });\n  EXPECT_EQ(expected, test_in);\n}\n\nTEST(ParallelizeTest, ParallelizeMapByChunkSize) {\n  std::vector<int> test_in = {0, 1, 2, 3, 4, 5};\n\n  std::vector<int> values = ParallelizeMapByChunkSize(test_in, 2, &SumElements);\n  EXPECT_THAT(values, testing::ElementsAreArray({1, 5, 9}));\n}\n\nTEST(ParallelizeTest, ParallelizeMap) {\n  std::vector<int> test_in = {0, 1, 2, 3, 4, 5};\n\n  std::vector<int> values = ParallelizeMap(test_in, &SumElements);\n  EXPECT_EQ(std::accumulate(values.begin(), values.end(), 0, std::plus<int>()),\n            15);\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/posix/.clang-format",
    "content": "DisableFormat: true\nSortIncludes: false"
  },
  {
    "path": "tachyon/base/posix/BUILD.bazel",
    "content": "load(\"//bazel:tachyon.bzl\", \"if_linux\")\nload(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"can_lower_nice_to\",\n    srcs = if_linux([\"can_lower_nice_to.cc\"]),\n    hdrs = [\"can_lower_nice_to.h\"],\n    deps = [\"//tachyon/build:build_config\"],\n)\n\ntachyon_cc_library(\n    name = \"eintr_wrapper\",\n    hdrs = [\"eintr_wrapper.h\"],\n    deps = [\"//tachyon/build:build_config\"],\n)\n"
  },
  {
    "path": "tachyon/base/posix/can_lower_nice_to.cc",
    "content": "// Copyright 2018 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/posix/can_lower_nice_to.h\"\n\n#include <limits.h>\n#include <sys/resource.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include \"tachyon/build/build_config.h\"\n\n// Not defined on AIX by default.\n#if BUILDFLAG(IS_AIX)\n#if defined(RLIMIT_NICE)\n#error Assumption about OS_AIX is incorrect\n#endif\n#define RLIMIT_NICE 20\n#endif\n\nnamespace tachyon::base::internal {\n\nbool CanLowerNiceTo(int nice_value) {\n  // On a POSIX system, the nice value of a thread can be lowered 1. by the root\n  // user, 2. by a user with the CAP_SYS_NICE permission or 3. by any user if\n  // the target value is within the range allowed by RLIMIT_NICE.\n\n  // 1. Check for root user.\n  if (geteuid() == 0)\n    return true;\n\n  // 2. Skip checking the CAP_SYS_NICE permission because it would require\n  // libcap.so.\n\n  // 3. Check whether the target value is within the range allowed by\n  // RLIMIT_NICE.\n  //\n  // NZERO should be defined in <limits.h> per POSIX, and should be at least 20.\n  // (NZERO-1) is the highest possible niceness value (i.e. lowest priority).\n  // Most platforms use NZERO=20.\n  //\n  // RLIMIT_NICE tells us how much we can reduce niceness (increase priority) if\n  // we start at NZERO. For example, if NZERO is 20 and the rlimit is 30, we can\n  // lower niceness anywhere within the [-10, 19] range (20 - 30 = -10).\n  //\n  // So, we are allowed to reduce niceness to a minimum of NZERO - rlimit:\n  struct rlimit rlim;\n  if (getrlimit(RLIMIT_NICE, &rlim) != 0)\n    return false;\n  const int lowest_nice_allowed = NZERO - static_cast<int>(rlim.rlim_cur);\n\n  // And lowering niceness to |nice_value| is allowed if it is greater than or\n  // equal to the limit:\n  return nice_value >= lowest_nice_allowed;\n}\n\n}  // namespace tachyon::base::internal\n"
  },
  {
    "path": "tachyon/base/posix/can_lower_nice_to.h",
    "content": "// Copyright 2018 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_POSIX_CAN_LOWER_NICE_TO_H_\n#define TACHYON_BASE_POSIX_CAN_LOWER_NICE_TO_H_\n\nnamespace tachyon::base::internal {\n\n// Returns true if lowering the nice value of a process or thread to\n// |nice_value| using setpriority() or nice() should succeed. Note: A lower nice\n// value means a higher priority.\nbool CanLowerNiceTo(int nice_value);\n\n}  // namespace tachyon::base::internal\n\n#endif  // TACHYON_BASE_POSIX_CAN_LOWER_NICE_TO_H_\n"
  },
  {
    "path": "tachyon/base/posix/eintr_wrapper.h",
    "content": "// Copyright 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n// This provides a wrapper around system calls which may be interrupted by a\n// signal and return EINTR. See man 7 signal.\n// To prevent long-lasting loops (which would likely be a bug, such as a signal\n// that should be masked) to go unnoticed, there is a limit after which the\n// caller will nonetheless see an EINTR in Debug builds.\n//\n// On Windows and Fuchsia, this wrapper macro does nothing because there are no\n// signals.\n//\n// Don't wrap close calls in HANDLE_EINTR. Use IGNORE_EINTR if the return\n// value of close is significant. See http://crbug.com/269623.\n\n#ifndef TACHYON_BASE_POSIX_EINTR_WRAPPER_H_\n#define TACHYON_BASE_POSIX_EINTR_WRAPPER_H_\n\n#include \"tachyon/build/build_config.h\"\n\n#if BUILDFLAG(IS_POSIX)\n\n#include <errno.h>\n\n#if defined(NDEBUG)\n\n#define HANDLE_EINTR(x) ({ \\\n  decltype(x) eintr_wrapper_result; \\\n  do { \\\n    eintr_wrapper_result = (x); \\\n  } while (eintr_wrapper_result == -1 && errno == EINTR); \\\n  eintr_wrapper_result; \\\n})\n\n#else\n\n#define HANDLE_EINTR(x) ({ \\\n  int eintr_wrapper_counter = 0; \\\n  decltype(x) eintr_wrapper_result; \\\n  do { \\\n    eintr_wrapper_result = (x); \\\n  } while (eintr_wrapper_result == -1 && errno == EINTR && \\\n           eintr_wrapper_counter++ < 100); \\\n  eintr_wrapper_result; \\\n})\n\n#endif  // NDEBUG\n\n#define IGNORE_EINTR(x) ({ \\\n  decltype(x) eintr_wrapper_result; \\\n  do { \\\n    eintr_wrapper_result = (x); \\\n    if (eintr_wrapper_result == -1 && errno == EINTR) { \\\n      eintr_wrapper_result = 0; \\\n    } \\\n  } while (0); \\\n  eintr_wrapper_result; \\\n})\n\n#else  // !BUILDFLAG(IS_POSIX)\n\n#define HANDLE_EINTR(x) (x)\n#define IGNORE_EINTR(x) (x)\n\n#endif  // !BUILDFLAG(IS_POSIX)\n\n#endif  // TACHYON_BASE_POSIX_EINTR_WRAPPER_H_\n"
  },
  {
    "path": "tachyon/base/process/.clang-format",
    "content": "DisableFormat: true\nSortIncludes: false"
  },
  {
    "path": "tachyon/base/process/BUILD.bazel",
    "content": "load(\"//bazel:tachyon.bzl\", \"if_posix\")\nload(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"process_handle\",\n    srcs = [\"process_handle.cc\"] + if_posix([\n        \"process_handle_posix.cc\",\n    ]),\n    hdrs = [\"process_handle.h\"],\n    deps = [\n        \"//tachyon/base:logging\",\n        \"//tachyon/build:build_config\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/base/process/process_handle.cc",
    "content": "// Copyright 2015 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/process/process_handle.h\"\n\n#include <stdint.h>\n\n#include <ostream>\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/build/build_config.h\"\n\nnamespace tachyon::base {\n\nnamespace {\nProcessId g_pid_outside_of_namespace = kNullProcessId;\n}  // namespace\n\nstd::ostream& operator<<(std::ostream& os, const UniqueProcId& obj) {\n  os << obj.GetUnsafeValue();\n  return os;\n}\n\nUniqueProcId GetUniqueIdForProcess() {\n  // Used for logging. Must not use LogMessage or any of the macros that call\n  // into it.\n  return (g_pid_outside_of_namespace != kNullProcessId)\n             ? UniqueProcId(g_pid_outside_of_namespace)\n             : UniqueProcId(GetCurrentProcId());\n}\n\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_AIX)\n\nvoid InitUniqueIdForProcessInPidNamespace(ProcessId pid_outside_of_namespace) {\n  DCHECK(pid_outside_of_namespace != kNullProcessId);\n  g_pid_outside_of_namespace = pid_outside_of_namespace;\n}\n\n#endif\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/process/process_handle.h",
    "content": "// Copyright 2013 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_PROCESS_PROCESS_HANDLE_H_\n#define TACHYON_BASE_PROCESS_PROCESS_HANDLE_H_\n\n#include <stdint.h>\n#include <sys/types.h>\n\n#include <iosfwd>\n\n#include \"tachyon/export.h\"\n#include \"tachyon/build/build_config.h\"\n\n#if BUILDFLAG(IS_WIN)\n#include \"tachyon/base/win/windows_types.h\"\n#endif\n\n#if BUILDFLAG(IS_FUCHSIA)\n#include <zircon/types.h>\n#endif\n\nnamespace tachyon::base {\n\nclass FilePath;\n\n// ProcessHandle is a platform specific type which represents the underlying OS\n// handle to a process.\n// ProcessId is a number which identifies the process in the OS.\n#if BUILDFLAG(IS_WIN)\ntypedef HANDLE ProcessHandle;\ntypedef DWORD ProcessId;\ntypedef HANDLE UserTokenHandle;\nconst ProcessHandle kNullProcessHandle = NULL;\nconst ProcessId kNullProcessId = 0;\n#elif BUILDFLAG(IS_FUCHSIA)\ntypedef zx_handle_t ProcessHandle;\ntypedef zx_koid_t ProcessId;\nconst ProcessHandle kNullProcessHandle = ZX_HANDLE_INVALID;\nconst ProcessId kNullProcessId = ZX_KOID_INVALID;\n#elif BUILDFLAG(IS_POSIX)\n// On POSIX, our ProcessHandle will just be the PID.\ntypedef pid_t ProcessHandle;\ntypedef pid_t ProcessId;\nconst ProcessHandle kNullProcessHandle = 0;\nconst ProcessId kNullProcessId = 0;\n#endif  // BUILDFLAG(IS_WIN)\n\n// To print ProcessIds portably use CrPRIdPid (based on PRIuS and friends from\n// C99 and format_macros.h) like this:\n// absl::StrFormat(\"PID is %\" CrPRIdPid \".\\n\", pid);\n#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_FUCHSIA)\n#define CrPRIdPid \"ld\"\n#else\n#define CrPRIdPid \"d\"\n#endif\n\nclass UniqueProcId {\n public:\n  explicit UniqueProcId(ProcessId value) : value_(value) {}\n  UniqueProcId(const UniqueProcId& other) = default;\n  UniqueProcId& operator=(const UniqueProcId& other) = default;\n\n  // Returns the process PID. WARNING: On some platforms, the pid may not be\n  // valid within the current process sandbox.\n  ProcessId GetUnsafeValue() const { return value_; }\n\n  bool operator==(const UniqueProcId& other) const {\n    return value_ == other.value_;\n  }\n\n  bool operator!=(const UniqueProcId& other) const {\n    return value_ != other.value_;\n  }\n\n  bool operator<(const UniqueProcId& other) const {\n    return value_ < other.value_;\n  }\n\n  bool operator<=(const UniqueProcId& other) const {\n    return value_ <= other.value_;\n  }\n\n  bool operator>(const UniqueProcId& other) const {\n    return value_ > other.value_;\n  }\n\n  bool operator>=(const UniqueProcId& other) const {\n    return value_ >= other.value_;\n  }\n\n private:\n  ProcessId value_;\n};\n\nstd::ostream& operator<<(std::ostream& os, const UniqueProcId& obj);\n\n// Returns the id of the current process.\n// Note that on some platforms, this is not guaranteed to be unique across\n// processes (use GetUniqueIdForProcess if uniqueness is required).\nTACHYON_EXPORT ProcessId GetCurrentProcId();\n\n// Returns a unique ID for the current process. The ID will be unique across all\n// currently running processes within the chrome session, but IDs of terminated\n// processes may be reused.\nTACHYON_EXPORT UniqueProcId GetUniqueIdForProcess();\n\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n// When a process is started in a different PID namespace from the browser\n// process, this function must be called with the process's PID in the browser's\n// PID namespace in order to initialize its unique ID. Not thread safe.\n// WARNING: To avoid inconsistent results from GetUniqueIdForProcess, this\n// should only be called very early after process startup - ideally as soon\n// after process creation as possible.\nTACHYON_EXPORT void InitUniqueIdForProcessInPidNamespace(\n    ProcessId pid_outside_of_namespace);\n#endif\n\n// Returns the ProcessHandle of the current process.\nTACHYON_EXPORT ProcessHandle GetCurrentProcessHandle();\n\n// Returns the process ID for the specified process. This is functionally the\n// same as Windows' GetProcessId(), but works on versions of Windows before Win\n// XP SP1 as well.\n// DEPRECATED. New code should be using Process::Pid() instead.\n// Note that on some platforms, this is not guaranteed to be unique across\n// processes.\nTACHYON_EXPORT ProcessId GetProcId(ProcessHandle process);\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_PROCESS_PROCESS_HANDLE_H_\n"
  },
  {
    "path": "tachyon/base/process/process_handle_posix.cc",
    "content": "// Copyright 2013 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/process/process_handle.h\"\n\n#include <unistd.h>\n\nnamespace tachyon::base {\n\nProcessId GetCurrentProcId() {\n  return getpid();\n}\n\nProcessHandle GetCurrentProcessHandle() {\n  return GetCurrentProcId();\n}\n\nProcessId GetProcId(ProcessHandle process) {\n  return process;\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/profiler.cc",
    "content": "#include \"tachyon/base/profiler.h\"\n\nPERFETTO_TRACK_EVENT_STATIC_STORAGE();\n\nnamespace tachyon::base {\n\nProfiler::Profiler() : Profiler(Options{}) {}\n\nProfiler::Profiler(const Options& options)\n    : trace_filepath_(options.output_path),\n      trace_file_(trace_filepath_,\n                  base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE),\n      max_size_kb_(options.max_size_kb) {}\n\nProfiler::~Profiler() { Stop(); }\n\nvoid Profiler::Init() {\n  perfetto::TracingInitArgs args;\n  args.backends |= perfetto::kInProcessBackend;\n  perfetto::Tracing::Initialize(args);\n  perfetto::TrackEvent::Register();\n}\n\nvoid Profiler::DisableCategories(std::string_view category) {\n  track_event_cfg_.add_disabled_categories(std::string(category));\n}\n\nvoid Profiler::EnableCategories(std::string_view category) {\n  track_event_cfg_.add_enabled_categories(std::string(category));\n}\n\nvoid Profiler::Start() {\n  perfetto::TraceConfig cfg;\n  cfg.add_buffers()->set_size_kb(max_size_kb_);\n\n  auto* ds_cfg = cfg.add_data_sources()->mutable_config();\n  ds_cfg->set_name(\"track_event\");\n  ds_cfg->set_track_event_config_raw(track_event_cfg_.SerializeAsString());\n\n  tracing_session_ = perfetto::Tracing::NewTrace();\n  tracing_session_->Setup(cfg, trace_file_.GetPlatformFile());\n  tracing_session_->StartBlocking();\n}\n\nvoid Profiler::Stop() { tracing_session_->StopBlocking(); }\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/profiler.h",
    "content": "#ifndef TACHYON_BASE_PROFILER_H_\n#define TACHYON_BASE_PROFILER_H_\n\n#include <memory>\n#include <string>\n\n#include \"third_party/perfetto/perfetto.h\"\n\n#include \"tachyon/base/files/file.h\"\n#include \"tachyon/export.h\"\n\nPERFETTO_DEFINE_CATEGORIES(\n    perfetto::Category(\"Utils\").SetDescription(\"Base utility functions\"),\n    perfetto::Category(\"Subtask\").SetDescription(\n        \"Subtask within a bigger task\"),\n    perfetto::Category(\"MSM\").SetDescription(\n        \"Multi Scalar Multiplication operations\"),\n    perfetto::Category(\"ProofGeneration\")\n        .SetDescription(\"The proof generation process\"),\n    perfetto::Category(\"ProofVerification\")\n        .SetDescription(\"The proof verification process\"),\n    perfetto::Category(\"EvaluationDomain\")\n        .SetDescription(\"Evaluation Domain operations\"));\n\nnamespace tachyon::base {\n\nclass TACHYON_EXPORT Profiler {\n public:\n  struct Options {\n    constexpr static size_t kDefaultMaxSizeKB = 1e6;\n\n    base::FilePath output_path = base::FilePath(\"/tmp/tachyon.perfetto-trace\");\n    size_t max_size_kb = kDefaultMaxSizeKB;\n  };\n\n  Profiler();\n  explicit Profiler(const Options& options);\n  ~Profiler();\n\n  void Init();\n  void DisableCategories(std::string_view category);\n  void EnableCategories(std::string_view category);\n  void Start();\n  void Stop();\n\n private:\n  perfetto::protos::gen::TrackEventConfig track_event_cfg_;\n  std::unique_ptr<perfetto::TracingSession> tracing_session_;\n  FilePath trace_filepath_;\n  File trace_file_;\n  size_t max_size_kb_;\n};\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_PROFILER_H_\n"
  },
  {
    "path": "tachyon/base/random.cc",
    "content": "#include \"tachyon/base/random.h\"\n\nnamespace tachyon::base {\n\nabsl::BitGen& GetAbslBitGen() {\n  static absl::BitGen bitgen;\n  return bitgen;\n}\n\nbool Bernoulli(double probability) {\n  return absl::Bernoulli(GetAbslBitGen(), probability);\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/random.h",
    "content": "#ifndef TACHYON_BASE_RANDOM_H_\n#define TACHYON_BASE_RANDOM_H_\n\n#include \"absl/random/random.h\"\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/range.h\"\n#include \"tachyon/export.h\"\n\nnamespace tachyon::base {\n\nTACHYON_EXPORT absl::BitGen& GetAbslBitGen();\n\ntemplate <typename T, bool IsStartInclusive, bool IsEndInclusive>\nT Uniform(const Range<T, IsStartInclusive, IsEndInclusive>& range) {\n  CHECK(!range.IsEmpty());\n  if constexpr (IsStartInclusive && IsEndInclusive) {\n    return absl::Uniform(absl::IntervalClosedClosed, GetAbslBitGen(),\n                         range.from, range.to);\n  } else if constexpr (IsStartInclusive && !IsEndInclusive) {\n    return absl::Uniform(absl::IntervalClosedOpen, GetAbslBitGen(), range.from,\n                         range.to);\n  } else if constexpr (!IsStartInclusive && IsEndInclusive) {\n    return absl::Uniform(absl::IntervalOpenClosed, GetAbslBitGen(), range.from,\n                         range.to);\n  } else {\n    return absl::Uniform(absl::IntervalOpenOpen, GetAbslBitGen(), range.from,\n                         range.to);\n  }\n}\n\ntemplate <typename Container,\n          typename R = decltype(std::declval<Container>()[0])>\nR UniformElement(Container&& container) {\n  return container[Uniform(Range<size_t>::Until(std::size(container)))];\n}\n\nTACHYON_EXPORT bool Bernoulli(double probability);\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_RANDOM_H_\n"
  },
  {
    "path": "tachyon/base/random_unittest.cc",
    "content": "\n#include \"tachyon/base/random.h\"\n\n#include \"gtest/gtest.h\"\n\nnamespace tachyon::base {\n\nconstexpr int kMin = 0;\nconstexpr int kMax = 1000;\nconstexpr size_t kCount = 1000;\n\ntemplate <typename RangeType>\nclass RandomWithRangeTest : public testing::Test {};\n\nusing RangeTypes =\n    testing::Types<Range<int, false, false>, Range<int, false, true>,\n                   Range<int, true, false>, Range<int, true, true>>;\nTYPED_TEST_SUITE(RandomWithRangeTest, RangeTypes);\n\nTYPED_TEST(RandomWithRangeTest, SampleDifferentValue) {\n  using RangeType = TypeParam;\n\n  int r = Uniform(RangeType(kMin, kMax));\n  for (size_t i = 0; i < kCount; ++i) {\n    if (r != Uniform(RangeType(kMin, kMax))) {\n      SUCCEED();\n      return;\n    }\n  }\n  FAIL() << \"random seems not working\";\n}\n\nTYPED_TEST(RandomWithRangeTest, SampleWithinRange) {\n  using RangeType = TypeParam;\n\n  RangeType range(kMin, kMax);\n  int value = Uniform(range);\n  EXPECT_TRUE(range.Contains(value));\n}\n\nTEST(RandomTest, UniformElementWithArray) {\n  int arr[] = {1, 2, 3};\n  int r = UniformElement(arr);\n  EXPECT_GE(r, 1);\n  EXPECT_LE(r, 3);\n}\n\nTEST(RandomTest, UniformElementWithVector) {\n  std::vector<int> vec = {1, 2, 3};\n  int& r = UniformElement(vec);\n  EXPECT_GE(r, 1);\n  EXPECT_LE(r, 3);\n  r = 2;\n}\n\nTEST(RandomTest, UniformElementWithConstVector) {\n  const std::vector<int> vec = {1, 2, 3};\n  const int& r = UniformElement(vec);\n  EXPECT_GE(r, 1);\n  EXPECT_LE(r, 3);\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/range.h",
    "content": "#ifndef TACHYON_BASE_RANGE_H_\n#define TACHYON_BASE_RANGE_H_\n\n#include <algorithm>\n#include <limits>\n#include <string>\n#include <type_traits>\n\n#include \"absl/numeric/int128.h\"\n#include \"absl/strings/substitute.h\"\n\nnamespace tachyon::base {\n\ntemplate <typename T, bool IsStartInclusive = true, bool IsEndInclusive = false,\n          typename SFINAE = void>\nstruct Range;\n\ntemplate <typename T, bool IsStartInclusive, bool IsEndInclusive>\nstruct Range<T, IsStartInclusive, IsEndInclusive,\n             std::enable_if_t<std::is_arithmetic_v<T> ||\n                              std::is_same_v<T, absl::uint128> ||\n                              std::is_same_v<T, absl::int128>>> {\n  constexpr static bool kIsStartInclusive = IsStartInclusive;\n  constexpr static bool kIsEndInclusive = IsEndInclusive;\n\n  class Iterator {\n   public:\n    static_assert(std::is_integral_v<T>);\n\n    using difference_type = std::ptrdiff_t;\n    using value_type = T;\n    using pointer = value_type*;\n    using reference = value_type&;\n    using iterator_category = std::random_access_iterator_tag;\n\n    constexpr Iterator() = default;\n    constexpr explicit Iterator(T cur) : cur_(cur) {}\n\n    constexpr bool operator==(Iterator other) const {\n      return cur_ == other.cur_;\n    }\n\n    constexpr bool operator!=(Iterator other) const {\n      return cur_ != other.cur_;\n    }\n\n    constexpr bool operator<(Iterator other) const { return cur_ < other.cur_; }\n\n    constexpr bool operator<=(Iterator other) const {\n      return cur_ <= other.cur_;\n    }\n\n    constexpr bool operator>(Iterator other) const { return cur_ > other.cur_; }\n\n    constexpr bool operator>=(Iterator other) const {\n      return cur_ >= other.cur_;\n    }\n\n    constexpr Iterator& operator++() {\n      ++cur_;\n      return *this;\n    }\n\n    constexpr Iterator operator++(int) const {\n      Iterator it(*this);\n      ++(*this);\n      return it;\n    }\n\n    constexpr Iterator& operator--() {\n      --cur_;\n      return *this;\n    }\n\n    constexpr Iterator operator--(int) const {\n      Iterator it(*this);\n      --(*this);\n      return it;\n    }\n\n    constexpr Iterator operator+(difference_type n) const {\n      return Iterator(cur_ + n);\n    }\n\n    constexpr Iterator& operator+=(difference_type n) {\n      cur_ += n;\n      return *this;\n    }\n\n    constexpr Iterator operator-(difference_type n) const {\n      return Iterator(cur_ - n);\n    }\n\n    constexpr Iterator& operator-=(difference_type n) {\n      cur_ -= n;\n      return *this;\n    }\n\n    constexpr difference_type operator-(Iterator other) const {\n      return cur_ - other.cur_;\n    }\n\n    constexpr reference operator*() { return cur_; }\n\n    constexpr pointer operator->() { return &cur_; }\n\n   private:\n    T cur_ = 0;\n  };\n\n  // Returns the lowest finite value representable by the numeric type T, that\n  // is, a finite value x such that there is no other finite value y where\n  // y < x. This is different from std::numeric_limits<T>::min() for\n  // floating-point types. Only meaningful for bounded types.\n  // See https://en.cppreference.com/w/cpp/types/numeric_limits/lowest\n  // NOTE(chokobole): We used |lowest()| over |min()| for the reason above.\n  T from = std::numeric_limits<T>::lowest();\n  T to = std::numeric_limits<T>::max();\n\n  constexpr Range() = default;\n  constexpr Range(T from, T to) : from(from), to(to) {}\n\n  constexpr static Range All() { return Range(); }\n\n  constexpr static Range From(T from) {\n    Range range;\n    range.from = from;\n    return range;\n  }\n\n  constexpr static Range Until(T to) {\n    Range range;\n    range.to = to;\n    return range;\n  }\n\n  Iterator begin() const {\n    if constexpr (IsStartInclusive) {\n      return Iterator(from);\n    } else {\n      return Iterator(from + 1);\n    }\n  }\n\n  Iterator end() const {\n    if constexpr (IsEndInclusive) {\n      return Iterator(to + 1);\n    } else {\n      return Iterator(to);\n    }\n  }\n\n  constexpr Range Intersect(Range other) const {\n    return Range(std::max(from, other.from), std::min(to, other.to));\n  }\n\n  // Returns the number of integral elements within the range.\n  template <typename U = T, std::enable_if_t<std::is_integral_v<U>>* = nullptr>\n  constexpr size_t GetSize() const {\n    if (IsEmpty()) return 0;\n    if constexpr (IsStartInclusive && IsEndInclusive) {\n      return to - from + 1;\n    } else if constexpr (IsStartInclusive && !IsEndInclusive) {\n      return to - from;\n    } else if constexpr (!IsStartInclusive && IsEndInclusive) {\n      return to - from;\n    } else {\n      return to - from - 1;\n    }\n  }\n\n  // Returns true if the range doesn't contain any value. |Contains()| always\n  // gives you false in this case.\n  constexpr bool IsEmpty() const {\n    if constexpr (IsStartInclusive && IsEndInclusive) {\n      return from > to;\n    } else {\n      return from >= to;\n    }\n  }\n\n  // Returns true if the range contains |value|.\n  constexpr bool Contains(T value) const {\n    if constexpr (IsStartInclusive && IsEndInclusive) {\n      return from <= value && value <= to;\n    } else if constexpr (IsStartInclusive && !IsEndInclusive) {\n      return from <= value && value < to;\n    } else if constexpr (!IsStartInclusive && IsEndInclusive) {\n      return from < value && value <= to;\n    } else {\n      return from < value && value < to;\n    }\n  }\n\n  constexpr bool operator==(Range other) const {\n    return from == other.from && to == other.to;\n  }\n  constexpr bool operator!=(Range other) const { return !operator==(other); }\n\n  std::string ToString() const {\n    if constexpr (IsStartInclusive && IsEndInclusive) {\n      return absl::Substitute(\"$0 ≤ x ≤ $1\", from, to);\n    } else if constexpr (IsStartInclusive && !IsEndInclusive) {\n      return absl::Substitute(\"$0 ≤ x < $1\", from, to);\n    } else if constexpr (!IsStartInclusive && IsEndInclusive) {\n      return absl::Substitute(\"$0 < x ≤ $1\", from, to);\n    } else {\n      return absl::Substitute(\"$0 < x < $1\", from, to);\n    }\n  }\n};\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_RANGE_H_\n"
  },
  {
    "path": "tachyon/base/range_unittest.cc",
    "content": "#include \"tachyon/base/range.h\"\n\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/containers/container_util.h\"\n\nnamespace tachyon::base {\n\ntemplate <typename RangeType>\nclass RangeTest : public testing::Test {};\n\nusing RangeTypes =\n    testing::Types<Range<int, false, false>, Range<int, false, true>,\n                   Range<int, true, false>, Range<int, true, true>>;\nTYPED_TEST_SUITE(RangeTest, RangeTypes);\n\nTYPED_TEST(RangeTest, Iterator) {\n  using RangeType = TypeParam;\n\n  RangeType range(1, 5);\n  std::vector<int> elements =\n      base::Map(range.begin(), range.end(), [](int v) { return v; });\n  std::vector<int> expected;\n  if constexpr (RangeType::kIsStartInclusive) {\n    expected.push_back(1);\n  }\n  for (int i = 2; i < 5; ++i) {\n    expected.push_back(i);\n  }\n  if constexpr (RangeType::kIsEndInclusive) {\n    expected.push_back(5);\n  }\n  EXPECT_EQ(elements, expected);\n}\n\nTYPED_TEST(RangeTest, IsEmpty) {\n  using RangeType = TypeParam;\n\n  RangeType range(3, 3);\n  if constexpr (RangeType::kIsStartInclusive && RangeType::kIsEndInclusive) {\n    EXPECT_FALSE(range.IsEmpty());\n  } else {\n    EXPECT_TRUE(range.IsEmpty());\n  }\n\n  range = RangeType(3, 4);\n  EXPECT_FALSE(range.IsEmpty());\n}\n\nTYPED_TEST(RangeTest, Contains) {\n  using RangeType = TypeParam;\n\n  RangeType range(3, 4);\n  EXPECT_FALSE(range.Contains(2));\n  if constexpr (RangeType::kIsStartInclusive) {\n    EXPECT_TRUE(range.Contains(3));\n  } else {\n    EXPECT_FALSE(range.Contains(3));\n  }\n  if constexpr (RangeType::kIsEndInclusive) {\n    EXPECT_TRUE(range.Contains(4));\n  } else {\n    EXPECT_FALSE(range.Contains(4));\n  }\n  EXPECT_FALSE(range.Contains(5));\n}\n\nTYPED_TEST(RangeTest, Intersect) {\n  using RangeType = TypeParam;\n\n  RangeType range(1, 7);\n  RangeType range2(5, 10);\n  RangeType expected(5, 7);\n  EXPECT_EQ(range.Intersect(range2), expected);\n  EXPECT_EQ(range2.Intersect(range), expected);\n}\n\nTYPED_TEST(RangeTest, GetSize) {\n  using RangeType = TypeParam;\n\n  RangeType range(3, 4);\n  if constexpr (RangeType::kIsStartInclusive) {\n    if constexpr (RangeType::kIsEndInclusive) {\n      EXPECT_EQ(range.GetSize(), 2);\n    } else {\n      EXPECT_EQ(range.GetSize(), 1);\n    }\n  } else {\n    if constexpr (RangeType::kIsEndInclusive) {\n      EXPECT_EQ(range.GetSize(), 1);\n    } else {\n      EXPECT_EQ(range.GetSize(), 0);\n    }\n  }\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/ranges/.clang-format",
    "content": "DisableFormat: true\nSortIncludes: true\n"
  },
  {
    "path": "tachyon/base/ranges/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"algorithm\",\n    hdrs = [\"algorithm.h\"],\n    deps = [\n        \":functional\",\n        \":ranges\",\n        \"//tachyon/base:compiler_specific\",\n        \"//tachyon/base:cxx20_is_constant_evaluated\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base/functional:identity\",\n        \"//tachyon/base/functional:invoke\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"functional\",\n    hdrs = [\"functional.h\"],\n)\n\ntachyon_cc_library(\n    name = \"ranges\",\n    hdrs = [\"ranges.h\"],\n    deps = [\"//tachyon/base:template_util\"],\n)\n\ntachyon_cc_unittest(\n    name = \"ranges_unittests\",\n    srcs = [\n        \"algorithm_unittest.cc\",\n        \"functional_unittest.cc\",\n        \"ranges_unittest.cc\",\n    ],\n    deps = [\n        \":algorithm\",\n        \":functional\",\n        \":ranges\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/base/ranges/README.md",
    "content": "# `base::ranges`\n\nThis directory aims to implement a C++14 version of the new `std::ranges`\nalgorithms that were introduced in C++20. These implementations are added to the\n`base::ranges` namespace, and callers can access them by including\n[`base/ranges/algorithm.h`](https://source.chromium.org/chromium/chromium/src/+/main:base/ranges/algorithm.h).\n\n## Similarities with C++20\n\n### Automatically deducing `begin()` and `end()`\n\nAs probably one of the most important changes for readability and usability, all\nalgorithms in `base::ranges` have overloads for ranges of elements, which allow\ncallers to no longer specify `begin()` and `end()` iterators themselves.\n\nBefore:\n\n```c++\nbool HasEvens(const std::vector<int>& vec) {\n  return std::any_of(vec.begin(), vec.end(), [](int i) { return i % 2 == 0; });\n}\n```\n\nAfter:\n\n```c++\nbool HasEvens(const std::vector<int>& vec) {\n  return base::ranges::any_of(vec, [](int i) { return i % 2 == 0; });\n}\n```\n\nFurthermore, these overloads also support binding to temporaries, so that\napplying algorithms to return values is easier:\n\n```c++\nstd::vector<int> GetNums();\n```\n\nBefore:\n\n```c++\nbool HasEvens() {\n  std::vector<int> nums = GetNums();\n  return std::any_of(nums.begin(), nums.end(),\n                     [](int i) { return i % 2 == 0; });\n}\n```\n\nAfter:\n\n```c++\nbool HasEvens() {\n  return base::ranges::any_of(GetNums(), [](int i) { return i % 2 == 0; });\n}\n```\n\n### Support for Projections\n\nIn addition to supporting automatically deducing the `begin()` and `end()`\niterator for ranges, the `base::ranges::` algorithms also support projections,\nthat can be applied to arguments prior to passing it to supplied transformations\nor predicates. This is especially useful when ordering a collection of classes\nby a specific data member of the class. Example:\n\nBefore:\n\n```cpp\nstd::sort(suggestions->begin(), suggestions->end(),\n          [](const autofill::Suggestion& a, const autofill::Suggestion& b) {\n            return a.match < b.match;\n          });\n```\n\nAfter:\n\n```cpp\nbase::ranges::sort(*suggestions, /*comp=*/{}, &autofill::Suggestion::match);\n```\n\nAnything that is callable can be used as a projection. This includes\n`FunctionObjects` like function pointers or functors, but also pointers to\nmember function and pointers to data members, as shown above. When not specified\na projection defaults to `base::ranges::identity`, which simply perfectly\nforwards its argument.\n\nProjections are supported in both range and iterator-pair overloads of the\n`base::ranges::` algorithms, for example `base::ranges::all_of` has the\nfollowing signatures:\n\n```cpp\ntemplate <typename InputIterator, typename Pred, typename Proj = identity>\nbool all_of(InputIterator first, InputIterator last, Pred pred, Proj proj = {});\n\ntemplate <typename Range, typename Pred, typename Proj = identity>\nbool all_of(Range&& range, Pred pred, Proj proj = {});\n```\n\n## Differences from C++20\n\nTo simplify the implementation of the `base::ranges::` algorithms, they dispatch\nto the `std::` algorithms found in C++14. This leads to the following list of\ndifferences from C++20. Since most of these differences are differences in the\nlibrary and not in the language, they could be addressed in the future by adding\ncorresponding implementations.\n\n### Lack of Constraints\n\nDue to the lack of support for concepts in the language, the algorithms in\n`base::ranges` do not have the constraints that are present on the algorithms in\n`std::ranges`. Instead, they support any type, much like C++14's `std::`\nalgorithms. In the future this might be addressed by adding corresponding\nconstraints via SFINAE, should the need arise.\n\n### Lack of Range Primitives\n\nDue to C++14's lack of `std::ranges` concepts like sentinels and other range\nprimitives, algorithms taking a `[first, last)` pair rather than a complete\nrange, do not support different types for `first` and `last`. Since they rely on\nC++14's implementation, the type must be the same. This could be addressed in\nthe future by implementing support for sentinel types ourselves.\n\n### Lack of `constexpr`\n\nThe `base::ranges` algorithms can only be used in a `constexpr` context when\nthey call underlying `std::` algorithms that are themselves `constexpr`.  Before\nC++20, only `std::min`, `std::max` and `std::minmax` are annotated\nappropriately, so code like `constexpr bool foo = base::ranges::any_of(...);`\nwill fail because the compiler will not find a `constexpr std::any_of`.  This\ncould be addressed by either upgrading Chromium's STL to C++20, or implementing\n`constexpr` versions of some of these algorithms ourselves.\n\n### Lack of post C++14 algorithms\n\nSince most algorithms in `base::ranges` dispatch to their C++14 equivalent, some\n`std::` algorithms that are not present in C++14 have no implementation in\n`base::ranges`. This list of algorithms includes the following:\n\n- [`std::sample`](https://en.cppreference.com/w/cpp/algorithm/sample) (added in C++17)\n\n### Return Types\n\nSome of the algorithms in `std::ranges::` have different return types than their\nequivalent in `std::`. For example, while `std::for_each` returns the passed-in\n`Function`, `std::ranges::for_each` returns a `std::ranges::for_each_result`,\nconsisting of the `last` iterator and the function.\n\nIn the cases where the return type differs, `base::ranges::` algorithms will\ncontinue to return the old return type.\n\n### No blocking of ADL\n\nThe algorithms defined in `std::ranges` are not found by ADL, and inhibit ADL\nwhen found by [unqualified name lookup][1]. This is done to be able to enforce\nthe constraints specified by those algorithms and commonly implemented by using\nfunction objects instead of regular functions. Since we don't support\nconstrained algorithms yet, we don't implement the blocking of ADL either.\n\n[1]: https://wg21.link/algorithms.requirements#2\n"
  },
  {
    "path": "tachyon/base/ranges/algorithm.h",
    "content": "// Copyright 2020 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_RANGES_ALGORITHM_H_\n#define TACHYON_BASE_RANGES_ALGORITHM_H_\n\n#include <algorithm>\n#include <initializer_list>\n#include <iterator>\n#include <type_traits>\n#include <utility>\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/compiler_specific.h\"\n#include \"tachyon/base/cxx20_is_constant_evaluated.h\"\n#include \"tachyon/base/functional/identity.h\"\n#include \"tachyon/base/functional/invoke.h\"\n#include \"tachyon/base/ranges/functional.h\"\n#include \"tachyon/base/ranges/ranges.h\"\n\nnamespace tachyon::base {\n\nnamespace internal {\n\n// Returns a transformed version of the unary predicate `pred` applying `proj`\n// to its argument before invoking `pred` on it.\n// Ensures that the return type of `invoke(pred, ...)` is convertible to bool.\ntemplate <typename Pred, typename Proj>\nconstexpr auto ProjectedUnaryPredicate(Pred& pred, Proj& proj) noexcept {\n  return [&pred, &proj](auto&& arg) -> bool {\n    return base::invoke(pred,\n                        base::invoke(proj, std::forward<decltype(arg)>(arg)));\n  };\n}\n\n// Returns a transformed version of the binary predicate `pred` applying `proj1`\n// and `proj2` to its arguments before invoking `pred` on them.\n//\n// Provides an opt-in to considers all four permutations of projections and\n// argument types. This is sometimes necessary to allow usage with legacy\n// non-ranges std:: algorithms that don't support projections.\n//\n// These permutations are assigned different priorities to break ambiguities in\n// case several permutations are possible, e.g. when Proj1 and Proj2 are the\n// same type.\n//\n// Note that even when opting in to using all permutations of projections,\n// calling code should still ensure that the canonical mapping of {Proj1, Proj2}\n// to {LHS, RHS} compiles for all members of the range. This can be done by\n// adding the following constraint:\n//\n//   typename = indirect_result_t<Pred&,\n//                                projected<iterator_t<Range1>, Proj1>,\n//                                projected<iterator_t<Range2>, Proj2>>\n//\n// Ensures that the return type of `invoke(pred, ...)` is convertible to bool.\ntemplate <typename Pred, typename Proj1, typename Proj2, bool kPermute = false>\nclass BinaryPredicateProjector {\n public:\n  constexpr BinaryPredicateProjector(Pred& pred, Proj1& proj1, Proj2& proj2)\n      : pred_(pred), proj1_(proj1), proj2_(proj2) {}\n\n private:\n  template <typename ProjT, typename ProjU, typename T, typename U>\n  using InvokeResult = std::invoke_result_t<Pred&,\n                                            std::invoke_result_t<ProjT&, T&&>,\n                                            std::invoke_result_t<ProjU&, U&&>>;\n\n  template <typename T, typename U, typename = InvokeResult<Proj1, Proj2, T, U>>\n  constexpr std::pair<Proj1&, Proj2&> GetProjs(priority_tag<3>) const {\n    return {proj1_, proj2_};\n  }\n\n  template <typename T,\n            typename U,\n            bool LazyPermute = kPermute,\n            typename = std::enable_if_t<LazyPermute>,\n            typename = InvokeResult<Proj2, Proj1, T, U>>\n  constexpr std::pair<Proj2&, Proj1&> GetProjs(priority_tag<2>) const {\n    return {proj2_, proj1_};\n  }\n\n  template <typename T,\n            typename U,\n            bool LazyPermute = kPermute,\n            typename = std::enable_if_t<LazyPermute>,\n            typename = InvokeResult<Proj1, Proj1, T, U>>\n  constexpr std::pair<Proj1&, Proj1&> GetProjs(priority_tag<1>) const {\n    return {proj1_, proj1_};\n  }\n\n  template <typename T,\n            typename U,\n            bool LazyPermute = kPermute,\n            typename = std::enable_if_t<LazyPermute>,\n            typename = InvokeResult<Proj2, Proj2, T, U>>\n  constexpr std::pair<Proj2&, Proj2&> GetProjs(priority_tag<0>) const {\n    return {proj2_, proj2_};\n  }\n\n public:\n  template <typename T, typename U>\n  constexpr bool operator()(T&& lhs, U&& rhs) const {\n    auto projs = GetProjs<T, U>(priority_tag<3>());\n    return base::invoke(pred_, base::invoke(projs.first, std::forward<T>(lhs)),\n                        base::invoke(projs.second, std::forward<U>(rhs)));\n  }\n\n private:\n  // This field is not a raw_ref<> because it was filtered by the rewriter for:\n  // #constexpr-ctor-field-initializer\n  // NOTE(chokobole): removed RAW_PTR_EXCLUSION\n  Pred& pred_;\n  // This field is not a raw_ref<> because it was filtered by the rewriter for:\n  // #constexpr-ctor-field-initializer\n  // NOTE(chokobole): removed RAW_PTR_EXCLUSION\n  Proj1& proj1_;\n  // This field is not a raw_ref<> because it was filtered by the rewriter for:\n  // #constexpr-ctor-field-initializer\n  // NOTE(chokobole): removed RAW_PTR_EXCLUSION\n  Proj2& proj2_;\n};\n\n// Small wrappers around BinaryPredicateProjector to make the calling side more\n// readable.\ntemplate <typename Pred, typename Proj1, typename Proj2>\nconstexpr auto ProjectedBinaryPredicate(Pred& pred,\n                                        Proj1& proj1,\n                                        Proj2& proj2) noexcept {\n  return BinaryPredicateProjector<Pred, Proj1, Proj2>(pred, proj1, proj2);\n}\n\ntemplate <typename Pred, typename Proj1, typename Proj2>\nconstexpr auto PermutedProjectedBinaryPredicate(Pred& pred,\n                                                Proj1& proj1,\n                                                Proj2& proj2) noexcept {\n  return BinaryPredicateProjector<Pred, Proj1, Proj2, true>(pred, proj1, proj2);\n}\n\n// This alias is used below to restrict iterator based APIs to types for which\n// `iterator_category` and the pre-increment and post-increment operators are\n// defined. This is required in situations where otherwise an undesired overload\n// would be chosen, e.g. copy_if. In spirit this is similar to C++20's\n// std::input_or_output_iterator, a concept that each iterator should satisfy.\ntemplate <typename Iter,\n          typename = decltype(++std::declval<Iter&>()),\n          typename = decltype(std::declval<Iter&>()++)>\nusing iterator_category_t =\n    typename std::iterator_traits<Iter>::iterator_category;\n\n// This alias is used below to restrict range based APIs to types for which\n// `iterator_category_t` is defined for the underlying iterator. This is\n// required in situations where otherwise an undesired overload would be chosen,\n// e.g. transform. In spirit this is similar to C++20's std::ranges::range, a\n// concept that each range should satisfy.\ntemplate <typename Range>\nusing range_category_t = iterator_category_t<ranges::iterator_t<Range>>;\n\n}  // namespace internal\n\nnamespace ranges {\n\n// C++14 implementation of std::ranges::in_fun_result.\n//\n// Reference: https://wg21.link/algorithms.results#:~:text=in_fun_result\ntemplate <typename I, typename F>\nstruct in_fun_result {\n  NO_UNIQUE_ADDRESS I in;\n  NO_UNIQUE_ADDRESS F fun;\n\n  template <typename I2,\n            typename F2,\n            std::enable_if_t<std::is_convertible<const I&, I2>{} &&\n                             std::is_convertible<const F&, F2>{}>>\n  constexpr operator in_fun_result<I2, F2>() const& {\n    return {in, fun};\n  }\n\n  template <typename I2,\n            typename F2,\n            std::enable_if_t<std::is_convertible<I, I2>{} &&\n                             std::is_convertible<F, F2>{}>>\n  constexpr operator in_fun_result<I2, F2>() && {\n    return {std::move(in), std::move(fun)};\n  }\n};\n\n// TODO(crbug.com/1071094): Implement the other result types.\n\n// [alg.nonmodifying] Non-modifying sequence operations\n// Reference: https://wg21.link/alg.nonmodifying\n\n// [alg.all.of] All of\n// Reference: https://wg21.link/alg.all.of\n\n// Let `E(i)` be `invoke(pred, invoke(proj, *i))`.\n//\n// Returns: `false` if `E(i)` is `false` for some iterator `i` in the range\n// `[first, last)`, and `true` otherwise.\n//\n// Complexity: At most `last - first` applications of the predicate and any\n// projection.\n//\n// Reference: https://wg21.link/alg.all.of#:~:text=ranges::all_of(I\ntemplate <typename InputIterator,\n          typename Pred,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<InputIterator>>\nconstexpr bool all_of(InputIterator first,\n                      InputIterator last,\n                      Pred pred,\n                      Proj proj = {}) {\n  for (; first != last; ++first) {\n    if (!base::invoke(pred, base::invoke(proj, *first)))\n      return false;\n  }\n\n  return true;\n}\n\n// Let `E(i)` be `invoke(pred, invoke(proj, *i))`.\n//\n// Returns: `false` if `E(i)` is `false` for some iterator `i` in `range`, and\n// `true` otherwise.\n//\n// Complexity: At most `size(range)` applications of the predicate and any\n// projection.\n//\n// Reference: https://wg21.link/alg.all.of#:~:text=ranges::all_of(R\ntemplate <typename Range,\n          typename Pred,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>>\nconstexpr bool all_of(Range&& range, Pred pred, Proj proj = {}) {\n  return ranges::all_of(ranges::begin(range), ranges::end(range),\n                        std::move(pred), std::move(proj));\n}\n\n// [alg.any.of] Any of\n// Reference: https://wg21.link/alg.any.of\n\n// Let `E(i)` be `invoke(pred, invoke(proj, *i))`.\n//\n// Returns: `true` if `E(i)` is `true` for some iterator `i` in the range\n// `[first, last)`, and `false` otherwise.\n//\n// Complexity: At most `last - first` applications of the predicate and any\n// projection.\n//\n// Reference: https://wg21.link/alg.any.of#:~:text=ranges::any_of(I\ntemplate <typename InputIterator,\n          typename Pred,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<InputIterator>>\nconstexpr bool any_of(InputIterator first,\n                      InputIterator last,\n                      Pred pred,\n                      Proj proj = {}) {\n  for (; first != last; ++first) {\n    if (base::invoke(pred, base::invoke(proj, *first)))\n      return true;\n  }\n\n  return false;\n}\n\n// Let `E(i)` be `invoke(pred, invoke(proj, *i))`.\n//\n// Returns: `true` if `E(i)` is `true` for some iterator `i` in `range`, and\n// `false` otherwise.\n//\n// Complexity: At most `size(range)` applications of the predicate and any\n// projection.\n//\n// Reference: https://wg21.link/alg.any.of#:~:text=ranges::any_of(R\ntemplate <typename Range,\n          typename Pred,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>>\nconstexpr bool any_of(Range&& range, Pred pred, Proj proj = {}) {\n  return ranges::any_of(ranges::begin(range), ranges::end(range),\n                        std::move(pred), std::move(proj));\n}\n\n// [alg.none.of] None of\n// Reference: https://wg21.link/alg.none.of\n\n// Let `E(i)` be `invoke(pred, invoke(proj, *i))`.\n//\n// Returns: `false` if `E(i)` is `true` for some iterator `i` in the range\n// `[first, last)`, and `true` otherwise.\n//\n// Complexity: At most `last - first` applications of the predicate and any\n// projection.\n//\n// Reference: https://wg21.link/alg.none.of#:~:text=ranges::none_of(I\ntemplate <typename InputIterator,\n          typename Pred,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<InputIterator>>\nconstexpr bool none_of(InputIterator first,\n                       InputIterator last,\n                       Pred pred,\n                       Proj proj = {}) {\n  for (; first != last; ++first) {\n    if (base::invoke(pred, base::invoke(proj, *first)))\n      return false;\n  }\n\n  return true;\n}\n\n// Let `E(i)` be `invoke(pred, invoke(proj, *i))`.\n//\n// Returns: `false` if `E(i)` is `true` for some iterator `i` in `range`, and\n// `true` otherwise.\n//\n// Complexity: At most `size(range)` applications of the predicate and any\n// projection.\n//\n// Reference: https://wg21.link/alg.none.of#:~:text=ranges::none_of(R\ntemplate <typename Range,\n          typename Pred,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>>\nconstexpr bool none_of(Range&& range, Pred pred, Proj proj = {}) {\n  return ranges::none_of(ranges::begin(range), ranges::end(range),\n                         std::move(pred), std::move(proj));\n}\n\n// [alg.foreach] For each\n// Reference: https://wg21.link/alg.foreach\n\n// Reference: https://wg21.link/algorithm.syn#:~:text=for_each_result\ntemplate <typename I, typename F>\nusing for_each_result = in_fun_result<I, F>;\n\n// Effects: Calls `invoke(f, invoke(proj, *i))` for every iterator `i` in the\n// range `[first, last)`, starting from `first` and proceeding to `last - 1`.\n//\n// Returns: `{last, std::move(f)}`.\n//\n// Complexity: Applies `f` and `proj` exactly `last - first` times.\n//\n// Remarks: If `f` returns a result, the result is ignored.\n//\n// Reference: https://wg21.link/alg.foreach#:~:text=ranges::for_each(I\ntemplate <typename InputIterator,\n          typename Fun,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<InputIterator>>\nconstexpr auto for_each(InputIterator first,\n                        InputIterator last,\n                        Fun f,\n                        Proj proj = {}) {\n  for (; first != last; ++first)\n    base::invoke(f, base::invoke(proj, *first));\n  return for_each_result<InputIterator, Fun>{first, std::move(f)};\n}\n\n// Effects: Calls `invoke(f, invoke(proj, *i))` for every iterator `i` in the\n// range `range`, starting from `begin(range)` and proceeding to `end(range) -\n// 1`.\n//\n// Returns: `{last, std::move(f)}`.\n//\n// Complexity: Applies `f` and `proj` exactly `size(range)` times.\n//\n// Remarks: If `f` returns a result, the result is ignored.\n//\n// Reference: https://wg21.link/alg.foreach#:~:text=ranges::for_each(R\ntemplate <typename Range,\n          typename Fun,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>>\nconstexpr auto for_each(Range&& range, Fun f, Proj proj = {}) {\n  return ranges::for_each(ranges::begin(range), ranges::end(range),\n                          std::move(f), std::move(proj));\n}\n\n// Reference: https://wg21.link/algorithm.syn#:~:text=for_each_n_result\ntemplate <typename I, typename F>\nusing for_each_n_result = in_fun_result<I, F>;\n\n// Preconditions: `n >= 0` is `true`.\n//\n// Effects: Calls `invoke(f, invoke(proj, *i))` for every iterator `i` in the\n// range `[first, first + n)` in order.\n//\n// Returns: `{first + n, std::move(f)}`.\n//\n// Remarks: If `f` returns a result, the result is ignored.\n//\n// Reference: https://wg21.link/alg.foreach#:~:text=ranges::for_each_n\ntemplate <typename InputIterator,\n          typename Size,\n          typename Fun,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<InputIterator>>\nconstexpr auto for_each_n(InputIterator first, Size n, Fun f, Proj proj = {}) {\n  while (n > 0) {\n    base::invoke(f, base::invoke(proj, *first));\n    ++first;\n    --n;\n  }\n\n  return for_each_n_result<InputIterator, Fun>{first, std::move(f)};\n}\n\n// [alg.find] Find\n// Reference: https://wg21.link/alg.find\n\n// Let `E(i)` be `bool(invoke(proj, *i) == value)`.\n//\n// Returns: The first iterator `i` in the range `[first, last)` for which `E(i)`\n// is `true`. Returns `last` if no such iterator is found.\n//\n// Complexity: At most `last - first` applications of the corresponding\n// predicate and any projection.\n//\n// Reference: https://wg21.link/alg.find#:~:text=ranges::find(I\ntemplate <typename InputIterator,\n          typename T,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<InputIterator>>\nconstexpr auto find(InputIterator first,\n                    InputIterator last,\n                    const T& value,\n                    Proj proj = {}) {\n  for (; first != last; ++first) {\n    if (base::invoke(proj, *first) == value)\n      break;\n  }\n\n  return first;\n}\n\n// Let `E(i)` be `bool(invoke(proj, *i) == value)`.\n//\n// Returns: The first iterator `i` in `range` for which `E(i)` is `true`.\n// Returns `end(range)` if no such iterator is found.\n//\n// Complexity: At most `size(range)` applications of the corresponding predicate\n// and any projection.\n//\n// Reference: https://wg21.link/alg.find#:~:text=ranges::find(R\ntemplate <typename Range,\n          typename T,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>>\nconstexpr auto find(Range&& range, const T& value, Proj proj = {}) {\n  return ranges::find(ranges::begin(range), ranges::end(range), value,\n                      std::move(proj));\n}\n\n// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i)))`.\n//\n// Returns: The first iterator `i` in the range `[first, last)` for which `E(i)`\n// is `true`. Returns `last` if no such iterator is found.\n//\n// Complexity: At most `last - first` applications of the corresponding\n// predicate and any projection.\n//\n// Reference: https://wg21.link/alg.find#:~:text=ranges::find_if(I\ntemplate <typename InputIterator,\n          typename Pred,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<InputIterator>>\nconstexpr auto find_if(InputIterator first,\n                       InputIterator last,\n                       Pred pred,\n                       Proj proj = {}) {\n  return std::find_if(first, last,\n                      internal::ProjectedUnaryPredicate(pred, proj));\n}\n\n// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i)))`.\n//\n// Returns: The first iterator `i` in `range` for which `E(i)` is `true`.\n// Returns `end(range)` if no such iterator is found.\n//\n// Complexity: At most `size(range)` applications of the corresponding predicate\n// and any projection.\n//\n// Reference: https://wg21.link/alg.find#:~:text=ranges::find_if(R\ntemplate <typename Range,\n          typename Pred,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>>\nconstexpr auto find_if(Range&& range, Pred pred, Proj proj = {}) {\n  return ranges::find_if(ranges::begin(range), ranges::end(range),\n                         std::move(pred), std::move(proj));\n}\n\n// Let `E(i)` be `bool(!invoke(pred, invoke(proj, *i)))`.\n//\n// Returns: The first iterator `i` in the range `[first, last)` for which `E(i)`\n// is `true`. Returns `last` if no such iterator is found.\n//\n// Complexity: At most `last - first` applications of the corresponding\n// predicate and any projection.\n//\n// Reference: https://wg21.link/alg.find#:~:text=ranges::find_if_not(I\ntemplate <typename InputIterator,\n          typename Pred,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<InputIterator>>\nconstexpr auto find_if_not(InputIterator first,\n                           InputIterator last,\n                           Pred pred,\n                           Proj proj = {}) {\n  return std::find_if_not(first, last,\n                          internal::ProjectedUnaryPredicate(pred, proj));\n}\n\n// Let `E(i)` be `bool(!invoke(pred, invoke(proj, *i)))`.\n//\n// Returns: The first iterator `i` in `range` for which `E(i)` is `true`.\n// Returns `end(range)` if no such iterator is found.\n//\n// Complexity: At most `size(range)` applications of the corresponding predicate\n// and any projection.\n//\n// Reference: https://wg21.link/alg.find#:~:text=ranges::find_if_not(R\ntemplate <typename Range,\n          typename Pred,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>>\nconstexpr auto find_if_not(Range&& range, Pred pred, Proj proj = {}) {\n  return ranges::find_if_not(ranges::begin(range), ranges::end(range),\n                             std::move(pred), std::move(proj));\n}\n\n// [alg.find.end] Find end\n// Reference: https://wg21.link/alg.find.end\n\n// Let:\n// - `E(i,n)` be `invoke(pred, invoke(proj1, *(i + n)),\n//                             invoke(proj2, *(first2 + n)))`\n//\n// - `i` be `last1` if `[first2, last2)` is empty, or if\n//   `(last2 - first2) > (last1 - first1)` is `true`, or if there is no iterator\n//   in the range `[first1, last1 - (last2 - first2))` such that for every\n//   non-negative integer `n < (last2 - first2)`, `E(i,n)` is `true`. Otherwise\n//   `i` is the last such iterator in `[first1, last1 - (last2 - first2))`.\n//\n// Returns: `i`\n// Note: std::ranges::find_end(I1 first1,...) returns a range, rather than an\n// iterator. For simplicity we match std::find_end's return type instead.\n//\n// Complexity:\n// At most `(last2 - first2) * (last1 - first1 - (last2 - first2) + 1)`\n// applications of the corresponding predicate and any projections.\n//\n// Reference: https://wg21.link/alg.find.end#:~:text=ranges::find_end(I1\ntemplate <typename ForwardIterator1,\n          typename ForwardIterator2,\n          typename Pred = ranges::equal_to,\n          typename Proj1 = identity,\n          typename Proj2 = identity,\n          typename = internal::iterator_category_t<ForwardIterator1>,\n          typename = internal::iterator_category_t<ForwardIterator2>,\n          typename = indirect_result_t<Pred&,\n                                       projected<ForwardIterator1, Proj1>,\n                                       projected<ForwardIterator2, Proj2>>>\nconstexpr auto find_end(ForwardIterator1 first1,\n                        ForwardIterator1 last1,\n                        ForwardIterator2 first2,\n                        ForwardIterator2 last2,\n                        Pred pred = {},\n                        Proj1 proj1 = {},\n                        Proj2 proj2 = {}) {\n  return std::find_end(first1, last1, first2, last2,\n                       internal::ProjectedBinaryPredicate(pred, proj1, proj2));\n}\n\n// Let:\n// - `E(i,n)` be `invoke(pred, invoke(proj1, *(i + n)),\n//                             invoke(proj2, *(first2 + n)))`\n//\n// - `i` be `end(range1)` if `range2` is empty, or if\n//   `size(range2) > size(range1)` is `true`, or if there is no iterator in the\n//   range `[begin(range1), end(range1) - size(range2))` such that for every\n//   non-negative integer `n < size(range2)`, `E(i,n)` is `true`. Otherwise `i`\n//   is the last such iterator in `[begin(range1), end(range1) - size(range2))`.\n//\n// Returns: `i`\n// Note: std::ranges::find_end(R1&& r1,...) returns a range, rather than an\n// iterator. For simplicity we match std::find_end's return type instead.\n//\n// Complexity: At most `size(range2) * (size(range1) - size(range2) + 1)`\n// applications of the corresponding predicate and any projections.\n//\n// Reference: https://wg21.link/alg.find.end#:~:text=ranges::find_end(R1\ntemplate <typename Range1,\n          typename Range2,\n          typename Pred = ranges::equal_to,\n          typename Proj1 = identity,\n          typename Proj2 = identity,\n          typename = internal::range_category_t<Range1>,\n          typename = internal::range_category_t<Range2>,\n          typename = indirect_result_t<Pred&,\n                                       projected<iterator_t<Range1>, Proj1>,\n                                       projected<iterator_t<Range2>, Proj2>>>\nconstexpr auto find_end(Range1&& range1,\n                        Range2&& range2,\n                        Pred pred = {},\n                        Proj1 proj1 = {},\n                        Proj2 proj2 = {}) {\n  return ranges::find_end(ranges::begin(range1), ranges::end(range1),\n                          ranges::begin(range2), ranges::end(range2),\n                          std::move(pred), std::move(proj1), std::move(proj2));\n}\n\n// [alg.find.first.of] Find first\n// Reference: https://wg21.link/alg.find.first.of\n\n// Let `E(i,j)` be `bool(invoke(pred, invoke(proj1, *i), invoke(proj2, *j)))`.\n//\n// Effects: Finds an element that matches one of a set of values.\n//\n// Returns: The first iterator `i` in the range `[first1, last1)` such that for\n// some iterator `j` in the range `[first2, last2)` `E(i,j)` holds. Returns\n// `last1` if `[first2, last2)` is empty or if no such iterator is found.\n//\n// Complexity: At most `(last1 - first1) * (last2 - first2)` applications of the\n// corresponding predicate and any projections.\n//\n// Reference:\n// https://wg21.link/alg.find.first.of#:~:text=ranges::find_first_of(I1\ntemplate <typename ForwardIterator1,\n          typename ForwardIterator2,\n          typename Pred = ranges::equal_to,\n          typename Proj1 = identity,\n          typename Proj2 = identity,\n          typename = internal::iterator_category_t<ForwardIterator1>,\n          typename = internal::iterator_category_t<ForwardIterator2>,\n          typename = indirect_result_t<Pred&,\n                                       projected<ForwardIterator1, Proj1>,\n                                       projected<ForwardIterator2, Proj2>>>\nconstexpr auto find_first_of(ForwardIterator1 first1,\n                             ForwardIterator1 last1,\n                             ForwardIterator2 first2,\n                             ForwardIterator2 last2,\n                             Pred pred = {},\n                             Proj1 proj1 = {},\n                             Proj2 proj2 = {}) {\n  return std::find_first_of(\n      first1, last1, first2, last2,\n      internal::ProjectedBinaryPredicate(pred, proj1, proj2));\n}\n\n// Let `E(i,j)` be `bool(invoke(pred, invoke(proj1, *i), invoke(proj2, *j)))`.\n//\n// Effects: Finds an element that matches one of a set of values.\n//\n// Returns: The first iterator `i` in `range1` such that for some iterator `j`\n// in `range2` `E(i,j)` holds. Returns `end(range1)` if `range2` is empty or if\n// no such iterator is found.\n//\n// Complexity: At most `size(range1) * size(range2)` applications of the\n// corresponding predicate and any projections.\n//\n// Reference:\n// https://wg21.link/alg.find.first.of#:~:text=ranges::find_first_of(R1\ntemplate <typename Range1,\n          typename Range2,\n          typename Pred = ranges::equal_to,\n          typename Proj1 = identity,\n          typename Proj2 = identity,\n          typename = internal::range_category_t<Range1>,\n          typename = internal::range_category_t<Range2>,\n          typename = indirect_result_t<Pred&,\n                                       projected<iterator_t<Range1>, Proj1>,\n                                       projected<iterator_t<Range2>, Proj2>>>\nconstexpr auto find_first_of(Range1&& range1,\n                             Range2&& range2,\n                             Pred pred = {},\n                             Proj1 proj1 = {},\n                             Proj2 proj2 = {}) {\n  return ranges::find_first_of(\n      ranges::begin(range1), ranges::end(range1), ranges::begin(range2),\n      ranges::end(range2), std::move(pred), std::move(proj1), std::move(proj2));\n}\n\n// [alg.adjacent.find] Adjacent find\n// Reference: https://wg21.link/alg.adjacent.find\n\n// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i), invoke(proj, *(i + 1))))`.\n//\n// Returns: The first iterator `i` such that both `i` and `i + 1` are in the\n// range `[first, last)` for which `E(i)` holds. Returns `last` if no such\n// iterator is found.\n//\n// Complexity: Exactly `min((i - first) + 1, (last - first) - 1)` applications\n// of the corresponding predicate, where `i` is `adjacent_find`'s return value.\n//\n// Reference:\n// https://wg21.link/alg.adjacent.find#:~:text=ranges::adjacent_find(I\ntemplate <typename ForwardIterator,\n          typename Pred = ranges::equal_to,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<ForwardIterator>>\nconstexpr auto adjacent_find(ForwardIterator first,\n                             ForwardIterator last,\n                             Pred pred = {},\n                             Proj proj = {}) {\n  // Implementation inspired by cppreference.com:\n  // https://en.cppreference.com/w/cpp/algorithm/adjacent_find\n  //\n  // A reimplementation is required, because std::adjacent_find is not constexpr\n  // prior to C++20. Once we have C++20, we should switch to standard library\n  // implementation.\n  if (first == last)\n    return last;\n\n  for (ForwardIterator next = first; ++next != last; ++first) {\n    if (base::invoke(pred, base::invoke(proj, *first),\n                     base::invoke(proj, *next))) {\n      return first;\n    }\n  }\n\n  return last;\n}\n\n// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i), invoke(proj, *(i + 1))))`.\n//\n// Returns: The first iterator `i` such that both `i` and `i + 1` are in the\n// range `range` for which `E(i)` holds. Returns `end(range)` if no such\n// iterator is found.\n//\n// Complexity: Exactly `min((i - begin(range)) + 1, size(range) - 1)`\n// applications of the corresponding predicate, where `i` is `adjacent_find`'s\n// return value.\n//\n// Reference:\n// https://wg21.link/alg.adjacent.find#:~:text=ranges::adjacent_find(R\ntemplate <typename Range,\n          typename Pred = ranges::equal_to,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>>\nconstexpr auto adjacent_find(Range&& range, Pred pred = {}, Proj proj = {}) {\n  return ranges::adjacent_find(ranges::begin(range), ranges::end(range),\n                               std::move(pred), std::move(proj));\n}\n\n// [alg.count] Count\n// Reference: https://wg21.link/alg.count\n\n// Let `E(i)` be `invoke(proj, *i) == value`.\n//\n// Effects: Returns the number of iterators `i` in the range `[first, last)` for\n// which `E(i)` holds.\n//\n// Complexity: Exactly `last - first` applications of the corresponding\n// predicate and any projection.\n//\n// Reference: https://wg21.link/alg.count#:~:text=ranges::count(I\ntemplate <typename InputIterator,\n          typename T,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<InputIterator>>\nconstexpr auto count(InputIterator first,\n                     InputIterator last,\n                     const T& value,\n                     Proj proj = {}) {\n  // Note: In order to be able to apply `proj` to each element in [first, last)\n  // we are dispatching to std::count_if instead of std::count.\n  return std::count_if(first, last, [&proj, &value](auto&& lhs) {\n    return base::invoke(proj, std::forward<decltype(lhs)>(lhs)) == value;\n  });\n}\n\n// Let `E(i)` be `invoke(proj, *i) == value`.\n//\n// Effects: Returns the number of iterators `i` in `range` for which `E(i)`\n// holds.\n//\n// Complexity: Exactly `size(range)` applications of the corresponding predicate\n// and any projection.\n//\n// Reference: https://wg21.link/alg.count#:~:text=ranges::count(R\ntemplate <typename Range,\n          typename T,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>>\nconstexpr auto count(Range&& range, const T& value, Proj proj = {}) {\n  return ranges::count(ranges::begin(range), ranges::end(range), value,\n                       std::move(proj));\n}\n\n// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i)))`.\n//\n// Effects: Returns the number of iterators `i` in the range `[first, last)` for\n// which `E(i)` holds.\n//\n// Complexity: Exactly `last - first` applications of the corresponding\n// predicate and any projection.\n//\n// Reference: https://wg21.link/alg.count#:~:text=ranges::count_if(I\ntemplate <typename InputIterator,\n          typename Pred,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<InputIterator>>\nconstexpr auto count_if(InputIterator first,\n                        InputIterator last,\n                        Pred pred,\n                        Proj proj = {}) {\n  return std::count_if(first, last,\n                       internal::ProjectedUnaryPredicate(pred, proj));\n}\n\n// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i)))`.\n//\n// Effects: Returns the number of iterators `i` in `range` for which `E(i)`\n// holds.\n//\n// Complexity: Exactly `size(range)` applications of the corresponding predicate\n// and any projection.\n//\n// Reference: https://wg21.link/alg.count#:~:text=ranges::count_if(R\ntemplate <typename Range,\n          typename Pred,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>>\nconstexpr auto count_if(Range&& range, Pred pred, Proj proj = {}) {\n  return ranges::count_if(ranges::begin(range), ranges::end(range),\n                          std::move(pred), std::move(proj));\n}\n\n// [mismatch] Mismatch\n// Reference: https://wg21.link/mismatch\n\n// Let `E(n)` be `!invoke(pred, invoke(proj1, *(first1 + n)),\n//                              invoke(proj2, *(first2 + n)))`.\n//\n// Let `N` be `min(last1 - first1, last2 - first2)`.\n//\n// Returns: `{ first1 + n, first2 + n }`, where `n` is the smallest integer in\n// `[0, N)` such that `E(n)` holds, or `N` if no such integer exists.\n//\n// Complexity: At most `N` applications of the corresponding predicate and any\n// projections.\n//\n// Reference: https://wg21.link/mismatch#:~:text=ranges::mismatch(I1\ntemplate <typename ForwardIterator1,\n          typename ForwardIterator2,\n          typename Pred = ranges::equal_to,\n          typename Proj1 = identity,\n          typename Proj2 = identity,\n          typename = internal::iterator_category_t<ForwardIterator1>,\n          typename = internal::iterator_category_t<ForwardIterator2>,\n          typename = indirect_result_t<Pred&,\n                                       projected<ForwardIterator1, Proj1>,\n                                       projected<ForwardIterator2, Proj2>>>\nconstexpr auto mismatch(ForwardIterator1 first1,\n                        ForwardIterator1 last1,\n                        ForwardIterator2 first2,\n                        ForwardIterator2 last2,\n                        Pred pred = {},\n                        Proj1 proj1 = {},\n                        Proj2 proj2 = {}) {\n  return std::mismatch(first1, last1, first2, last2,\n                       internal::ProjectedBinaryPredicate(pred, proj1, proj2));\n}\n\n// Let `E(n)` be `!invoke(pred, invoke(proj1, *(begin(range1) + n)),\n//                              invoke(proj2, *(begin(range2) + n)))`.\n//\n// Let `N` be `min(size(range1), size(range2))`.\n//\n// Returns: `{ begin(range1) + n, begin(range2) + n }`, where `n` is the\n// smallest integer in `[0, N)` such that `E(n)` holds, or `N` if no such\n// integer exists.\n//\n// Complexity: At most `N` applications of the corresponding predicate and any\n// projections.\n//\n// Reference: https://wg21.link/mismatch#:~:text=ranges::mismatch(R1\ntemplate <typename Range1,\n          typename Range2,\n          typename Pred = ranges::equal_to,\n          typename Proj1 = identity,\n          typename Proj2 = identity,\n          typename = internal::range_category_t<Range1>,\n          typename = internal::range_category_t<Range2>,\n          typename = indirect_result_t<Pred&,\n                                       projected<iterator_t<Range1>, Proj1>,\n                                       projected<iterator_t<Range2>, Proj2>>>\nconstexpr auto mismatch(Range1&& range1,\n                        Range2&& range2,\n                        Pred pred = {},\n                        Proj1 proj1 = {},\n                        Proj2 proj2 = {}) {\n  return ranges::mismatch(ranges::begin(range1), ranges::end(range1),\n                          ranges::begin(range2), ranges::end(range2),\n                          std::move(pred), std::move(proj1), std::move(proj2));\n}\n\n// [alg.equal] Equal\n// Reference: https://wg21.link/alg.equal\n\n// Let `E(i)` be\n//   `invoke(pred, invoke(proj1, *i), invoke(proj2, *(first2 + (i - first1))))`.\n//\n// Returns: If `last1 - first1 != last2 - first2`, return `false.` Otherwise\n// return `true` if `E(i)` holds for every iterator `i` in the range `[first1,\n// last1)`. Otherwise, returns `false`.\n//\n// Complexity: If the types of `first1`, `last1`, `first2`, and `last2` meet the\n// `RandomAccessIterator` requirements and `last1 - first1 != last2 - first2`,\n// then no applications of the corresponding predicate and each projection;\n// otherwise, at most `min(last1 - first1, last2 - first2)` applications of the\n// corresponding predicate and any projections.\n//\n// Reference: https://wg21.link/alg.equal#:~:text=ranges::equal(I1\ntemplate <typename ForwardIterator1,\n          typename ForwardIterator2,\n          typename Pred = ranges::equal_to,\n          typename Proj1 = identity,\n          typename Proj2 = identity,\n          typename = internal::iterator_category_t<ForwardIterator1>,\n          typename = internal::iterator_category_t<ForwardIterator2>,\n          typename = indirect_result_t<Pred&,\n                                       projected<ForwardIterator1, Proj1>,\n                                       projected<ForwardIterator2, Proj2>>>\nconstexpr bool equal(ForwardIterator1 first1,\n                     ForwardIterator1 last1,\n                     ForwardIterator2 first2,\n                     ForwardIterator2 last2,\n                     Pred pred = {},\n                     Proj1 proj1 = {},\n                     Proj2 proj2 = {}) {\n  if (base::is_constant_evaluated()) {\n    for (; first1 != last1 && first2 != last2; ++first1, ++first2) {\n      if (!base::invoke(pred, base::invoke(proj1, *first1),\n                        base::invoke(proj2, *first2))) {\n        return false;\n      }\n    }\n\n    return first1 == last1 && first2 == last2;\n  }\n\n  return std::equal(first1, last1, first2, last2,\n                    internal::ProjectedBinaryPredicate(pred, proj1, proj2));\n}\n\n// Let `E(i)` be\n//   `invoke(pred, invoke(proj1, *i),\n//                 invoke(proj2, *(begin(range2) + (i - begin(range1)))))`.\n//\n// Returns: If `size(range1) != size(range2)`, return `false.` Otherwise return\n// `true` if `E(i)` holds for every iterator `i` in `range1`. Otherwise, returns\n// `false`.\n//\n// Complexity: If the types of `begin(range1)`, `end(range1)`, `begin(range2)`,\n// and `end(range2)` meet the `RandomAccessIterator` requirements and\n// `size(range1) != size(range2)`, then no applications of the corresponding\n// predicate and each projection;\n// otherwise, at most `min(size(range1), size(range2))` applications of the\n// corresponding predicate and any projections.\n//\n// Reference: https://wg21.link/alg.equal#:~:text=ranges::equal(R1\ntemplate <typename Range1,\n          typename Range2,\n          typename Pred = ranges::equal_to,\n          typename Proj1 = identity,\n          typename Proj2 = identity,\n          typename = internal::range_category_t<Range1>,\n          typename = internal::range_category_t<Range2>,\n          typename = indirect_result_t<Pred&,\n                                       projected<iterator_t<Range1>, Proj1>,\n                                       projected<iterator_t<Range2>, Proj2>>>\nconstexpr bool equal(Range1&& range1,\n                     Range2&& range2,\n                     Pred pred = {},\n                     Proj1 proj1 = {},\n                     Proj2 proj2 = {}) {\n  return ranges::equal(ranges::begin(range1), ranges::end(range1),\n                       ranges::begin(range2), ranges::end(range2),\n                       std::move(pred), std::move(proj1), std::move(proj2));\n}\n\n// [alg.is.permutation] Is permutation\n// Reference: https://wg21.link/alg.is.permutation\n\n// Returns: If `last1 - first1 != last2 - first2`, return `false`. Otherwise\n// return `true` if there exists a permutation of the elements in the range\n// `[first2, last2)`, bounded by `[pfirst, plast)`, such that\n// `ranges::equal(first1, last1, pfirst, plast, pred, proj, proj)` returns\n// `true`; otherwise, returns `false`.\n//\n// Complexity: No applications of the corresponding predicate if\n// ForwardIterator1 and ForwardIterator2 meet the requirements of random access\n// iterators and `last1 - first1 != last2 - first2`. Otherwise, exactly\n// `last1 - first1` applications of the corresponding predicate and projections\n// if `ranges::equal(first1, last1, first2, last2, pred, proj, proj)` would\n// return true;\n// otherwise, at worst `O(N²)`, where `N` has the value `last1 - first1`.\n//\n// Reference:\n// https://wg21.link/alg.is.permutation#:~:text=ranges::is_permutation(I1\ntemplate <typename ForwardIterator1,\n          typename ForwardIterator2,\n          typename Pred = ranges::equal_to,\n          typename Proj1 = identity,\n          typename Proj2 = identity,\n          typename = internal::iterator_category_t<ForwardIterator1>,\n          typename = internal::iterator_category_t<ForwardIterator2>,\n          typename = indirect_result_t<Pred&,\n                                       projected<ForwardIterator1, Proj1>,\n                                       projected<ForwardIterator2, Proj2>>>\nconstexpr bool is_permutation(ForwardIterator1 first1,\n                              ForwardIterator1 last1,\n                              ForwardIterator2 first2,\n                              ForwardIterator2 last2,\n                              Pred pred = {},\n                              Proj1 proj1 = {},\n                              Proj2 proj2 = {}) {\n  // Needs to opt-in to all permutations, since std::is_permutation expects\n  // pred(proj1(lhs), proj1(rhs)) to compile.\n  return std::is_permutation(\n      first1, last1, first2, last2,\n      internal::PermutedProjectedBinaryPredicate(pred, proj1, proj2));\n}\n\n// Returns: If `size(range1) != size(range2)`, return `false`. Otherwise return\n// `true` if there exists a permutation of the elements in `range2`, bounded by\n// `[pbegin, pend)`, such that\n// `ranges::equal(range1, [pbegin, pend), pred, proj, proj)` returns `true`;\n// otherwise, returns `false`.\n//\n// Complexity: No applications of the corresponding predicate if Range1 and\n// Range2 meet the requirements of random access ranges and\n// `size(range1) != size(range2)`. Otherwise, exactly `size(range1)`\n// applications of the corresponding predicate and projections if\n// `ranges::equal(range1, range2, pred, proj, proj)` would return true;\n// otherwise, at worst `O(N²)`, where `N` has the value `size(range1)`.\n//\n// Reference:\n// https://wg21.link/alg.is.permutation#:~:text=ranges::is_permutation(R1\ntemplate <typename Range1,\n          typename Range2,\n          typename Pred = ranges::equal_to,\n          typename Proj1 = identity,\n          typename Proj2 = identity,\n          typename = internal::range_category_t<Range1>,\n          typename = internal::range_category_t<Range2>,\n          typename = indirect_result_t<Pred&,\n                                       projected<iterator_t<Range1>, Proj1>,\n                                       projected<iterator_t<Range2>, Proj2>>>\nconstexpr bool is_permutation(Range1&& range1,\n                              Range2&& range2,\n                              Pred pred = {},\n                              Proj1 proj1 = {},\n                              Proj2 proj2 = {}) {\n  return ranges::is_permutation(\n      ranges::begin(range1), ranges::end(range1), ranges::begin(range2),\n      ranges::end(range2), std::move(pred), std::move(proj1), std::move(proj2));\n}\n\n// [alg.search] Search\n// Reference: https://wg21.link/alg.search\n\n// Returns: `i`, where `i` is the first iterator in the range\n// `[first1, last1 - (last2 - first2))` such that for every non-negative integer\n// `n` less than `last2 - first2` the condition\n// `bool(invoke(pred, invoke(proj1, *(i + n)), invoke(proj2, *(first2 + n))))`\n// is `true`.\n// Returns `last1` if no such iterator exists.\n// Note: std::ranges::search(I1 first1,...) returns a range, rather than an\n// iterator. For simplicity we match std::search's return type instead.\n//\n// Complexity: At most `(last1 - first1) * (last2 - first2)` applications of the\n// corresponding predicate and projections.\n//\n// Reference: https://wg21.link/alg.search#:~:text=ranges::search(I1\ntemplate <typename ForwardIterator1,\n          typename ForwardIterator2,\n          typename Pred = ranges::equal_to,\n          typename Proj1 = identity,\n          typename Proj2 = identity,\n          typename = internal::iterator_category_t<ForwardIterator1>,\n          typename = internal::iterator_category_t<ForwardIterator2>,\n          typename = indirect_result_t<Pred&,\n                                       projected<ForwardIterator1, Proj1>,\n                                       projected<ForwardIterator2, Proj2>>>\nconstexpr auto search(ForwardIterator1 first1,\n                      ForwardIterator1 last1,\n                      ForwardIterator2 first2,\n                      ForwardIterator2 last2,\n                      Pred pred = {},\n                      Proj1 proj1 = {},\n                      Proj2 proj2 = {}) {\n  return std::search(first1, last1, first2, last2,\n                     internal::ProjectedBinaryPredicate(pred, proj1, proj2));\n}\n\n// Returns: `i`, where `i` is the first iterator in the range\n// `[begin(range1), end(range1) - size(range2))` such that for every\n// non-negative integer `n` less than `size(range2)` the condition\n// `bool(invoke(pred, invoke(proj1, *(i + n)),\n//                    invoke(proj2, *(begin(range2) + n))))` is `true`.\n// Returns `end(range1)` if no such iterator exists.\n// Note: std::ranges::search(R1&& r1,...) returns a range, rather than an\n// iterator. For simplicity we match std::search's return type instead.\n//\n// Complexity: At most `size(range1) * size(range2)` applications of the\n// corresponding predicate and projections.\n//\n// Reference: https://wg21.link/alg.search#:~:text=ranges::search(R1\ntemplate <typename Range1,\n          typename Range2,\n          typename Pred = ranges::equal_to,\n          typename Proj1 = identity,\n          typename Proj2 = identity,\n          typename = internal::range_category_t<Range1>,\n          typename = internal::range_category_t<Range2>,\n          typename = indirect_result_t<Pred&,\n                                       projected<iterator_t<Range1>, Proj1>,\n                                       projected<iterator_t<Range2>, Proj2>>>\nconstexpr auto search(Range1&& range1,\n                      Range2&& range2,\n                      Pred pred = {},\n                      Proj1 proj1 = {},\n                      Proj2 proj2 = {}) {\n  return ranges::search(ranges::begin(range1), ranges::end(range1),\n                        ranges::begin(range2), ranges::end(range2),\n                        std::move(pred), std::move(proj1), std::move(proj2));\n}\n\n// Mandates: The type `Size` is convertible to an integral type.\n//\n// Returns: `i` where `i` is the first iterator in the range\n// `[first, last - count)` such that for every non-negative integer `n` less\n// than `count`, the following condition holds:\n// `invoke(pred, invoke(proj, *(i + n)), value)`.\n// Returns `last` if no such iterator is found.\n// Note: std::ranges::search_n(I1 first1,...) returns a range, rather than an\n// iterator. For simplicity we match std::search_n's return type instead.\n//\n// Complexity: At most `last - first` applications of the corresponding\n// predicate and projection.\n//\n// Reference: https://wg21.link/alg.search#:~:text=ranges::search_n(I\ntemplate <typename ForwardIterator,\n          typename Size,\n          typename T,\n          typename Pred = ranges::equal_to,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<ForwardIterator>>\nconstexpr auto search_n(ForwardIterator first,\n                        ForwardIterator last,\n                        Size count,\n                        const T& value,\n                        Pred pred = {},\n                        Proj proj = {}) {\n  // The second arg is guaranteed to be `value`, so we'll simply apply the\n  // identity projection.\n  identity value_proj;\n  return std::search_n(\n      first, last, count, value,\n      internal::ProjectedBinaryPredicate(pred, proj, value_proj));\n}\n\n// Mandates: The type `Size` is convertible to an integral type.\n//\n// Returns: `i` where `i` is the first iterator in the range\n// `[begin(range), end(range) - count)` such that for every non-negative integer\n// `n` less than `count`, the following condition holds:\n// `invoke(pred, invoke(proj, *(i + n)), value)`.\n// Returns `end(arnge)` if no such iterator is found.\n// Note: std::ranges::search_n(R1&& r1,...) returns a range, rather than an\n// iterator. For simplicity we match std::search_n's return type instead.\n//\n// Complexity: At most `size(range)` applications of the corresponding predicate\n// and projection.\n//\n// Reference: https://wg21.link/alg.search#:~:text=ranges::search_n(R\ntemplate <typename Range,\n          typename Size,\n          typename T,\n          typename Pred = ranges::equal_to,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>>\nconstexpr auto search_n(Range&& range,\n                        Size count,\n                        const T& value,\n                        Pred pred = {},\n                        Proj proj = {}) {\n  return ranges::search_n(ranges::begin(range), ranges::end(range), count,\n                          value, std::move(pred), std::move(proj));\n}\n\n// [alg.modifying.operations] Mutating sequence operations\n// Reference: https://wg21.link/alg.modifying.operations\n\n// [alg.copy] Copy\n// Reference: https://wg21.link/alg.copy\n\n// Let N be `last - first`.\n//\n// Preconditions: `result` is not in the range `[first, last)`.\n//\n// Effects: Copies elements in the range `[first, last)` into the range\n// `[result, result + N)` starting from `first` and proceeding to `last`. For\n// each non-negative integer `n < N` , performs `*(result + n) = *(first + n)`.\n//\n// Returns: `result + N`\n//\n// Complexity: Exactly `N` assignments.\n//\n// Reference: https://wg21.link/alg.copy#:~:text=ranges::copy(I\ntemplate <typename InputIterator,\n          typename OutputIterator,\n          typename = internal::iterator_category_t<InputIterator>,\n          typename = internal::iterator_category_t<OutputIterator>>\nconstexpr auto copy(InputIterator first,\n                    InputIterator last,\n                    OutputIterator result) {\n  return std::copy(first, last, result);\n}\n\n// Let N be `size(range)`.\n//\n// Preconditions: `result` is not in `range`.\n//\n// Effects: Copies elements in `range` into the range `[result, result + N)`\n// starting from `begin(range)` and proceeding to `end(range)`. For each\n// non-negative integer `n < N` , performs\n// *(result + n) = *(begin(range) + n)`.\n//\n// Returns: `result + N`\n//\n// Complexity: Exactly `N` assignments.\n//\n// Reference: https://wg21.link/alg.copy#:~:text=ranges::copy(R\ntemplate <typename Range,\n          typename OutputIterator,\n          typename = internal::range_category_t<Range>,\n          typename = internal::iterator_category_t<OutputIterator>>\nconstexpr auto copy(Range&& range, OutputIterator result) {\n  return ranges::copy(ranges::begin(range), ranges::end(range), result);\n}\n\n// Let `N` be `max(0, n)`.\n//\n// Mandates: The type `Size` is convertible to an integral type.\n//\n// Effects: For each non-negative integer `i < N`, performs\n// `*(result + i) = *(first + i)`.\n//\n// Returns: `result + N`\n//\n// Complexity: Exactly `N` assignments.\n//\n// Reference: https://wg21.link/alg.copy#:~:text=ranges::copy_n\ntemplate <typename InputIterator,\n          typename Size,\n          typename OutputIterator,\n          typename = internal::iterator_category_t<InputIterator>,\n          typename = internal::iterator_category_t<OutputIterator>>\nconstexpr auto copy_n(InputIterator first, Size n, OutputIterator result) {\n  return std::copy_n(first, n, result);\n}\n\n// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i)))`, and `N` be the number\n// of iterators `i` in the range `[first, last)` for which the condition `E(i)`\n// holds.\n//\n// Preconditions: The ranges `[first, last)` and\n// `[result, result + (last - first))` do not overlap.\n//\n// Effects: Copies all of the elements referred to by the iterator `i` in the\n// range `[first, last)` for which `E(i)` is true.\n//\n// Returns: `result + N`\n//\n// Complexity: Exactly `last - first` applications of the corresponding\n// predicate and any projection.\n//\n// Remarks: Stable.\n//\n// Reference: https://wg21.link/alg.copy#:~:text=ranges::copy_if(I\ntemplate <typename InputIterator,\n          typename OutputIterator,\n          typename Pred,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<InputIterator>,\n          typename = internal::iterator_category_t<OutputIterator>>\nconstexpr auto copy_if(InputIterator first,\n                       InputIterator last,\n                       OutputIterator result,\n                       Pred pred,\n                       Proj proj = {}) {\n  return std::copy_if(first, last, result,\n                      internal::ProjectedUnaryPredicate(pred, proj));\n}\n\n// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i)))`, and `N` be the number\n// of iterators `i` in `range` for which the condition `E(i)` holds.\n//\n// Preconditions: `range`  and `[result, result + size(range))` do not overlap.\n//\n// Effects: Copies all of the elements referred to by the iterator `i` in\n// `range` for which `E(i)` is true.\n//\n// Returns: `result + N`\n//\n// Complexity: Exactly `size(range)` applications of the corresponding predicate\n// and any projection.\n//\n// Remarks: Stable.\n//\n// Reference: https://wg21.link/alg.copy#:~:text=ranges::copy_if(R\ntemplate <typename Range,\n          typename OutputIterator,\n          typename Pred,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>,\n          typename = internal::iterator_category_t<OutputIterator>>\nconstexpr auto copy_if(Range&& range,\n                       OutputIterator result,\n                       Pred pred,\n                       Proj proj = {}) {\n  return ranges::copy_if(ranges::begin(range), ranges::end(range), result,\n                         std::move(pred), std::move(proj));\n}\n\n// Let `N` be `last - first`.\n//\n// Preconditions: `result` is not in the range `(first, last]`.\n//\n// Effects: Copies elements in the range `[first, last)` into the range\n// `[result - N, result)` starting from `last - 1` and proceeding to `first`.\n// For each positive integer `n ≤ N`, performs `*(result - n) = *(last - n)`.\n//\n// Returns: `result - N`\n//\n// Complexity: Exactly `N` assignments.\n//\n// Reference: https://wg21.link/alg.copy#:~:text=ranges::copy_backward(I1\ntemplate <typename BidirectionalIterator1,\n          typename BidirectionalIterator2,\n          typename = internal::iterator_category_t<BidirectionalIterator1>,\n          typename = internal::iterator_category_t<BidirectionalIterator2>>\nconstexpr auto copy_backward(BidirectionalIterator1 first,\n                             BidirectionalIterator1 last,\n                             BidirectionalIterator2 result) {\n  return std::copy_backward(first, last, result);\n}\n\n// Let `N` be `size(range)`.\n//\n// Preconditions: `result` is not in the range `(begin(range), end(range)]`.\n//\n// Effects: Copies elements in `range` into the range `[result - N, result)`\n// starting from `end(range) - 1` and proceeding to `begin(range)`. For each\n// positive integer `n ≤ N`, performs `*(result - n) = *(end(range) - n)`.\n//\n// Returns: `result - N`\n//\n// Complexity: Exactly `N` assignments.\n//\n// Reference: https://wg21.link/alg.copy#:~:text=ranges::copy_backward(R\ntemplate <typename Range,\n          typename BidirectionalIterator,\n          typename = internal::range_category_t<Range>,\n          typename = internal::iterator_category_t<BidirectionalIterator>>\nconstexpr auto copy_backward(Range&& range, BidirectionalIterator result) {\n  return ranges::copy_backward(ranges::begin(range), ranges::end(range),\n                               result);\n}\n\n// [alg.move] Move\n// Reference: https://wg21.link/alg.move\n\n// Let `E(n)` be `std::move(*(first + n))`.\n//\n// Let `N` be `last - first`.\n//\n// Preconditions: `result` is not in the range `[first, last)`.\n//\n// Effects: Moves elements in the range `[first, last)` into the range `[result,\n// result + N)` starting from `first` and proceeding to `last`. For each\n// non-negative integer `n < N`, performs `*(result + n) = E(n)`.\n//\n// Returns: `result + N`\n//\n// Complexity: Exactly `N` assignments.\n//\n// Reference: https://wg21.link/alg.move#:~:text=ranges::move(I\ntemplate <typename InputIterator,\n          typename OutputIterator,\n          typename = internal::iterator_category_t<InputIterator>,\n          typename = internal::iterator_category_t<OutputIterator>>\nconstexpr auto move(InputIterator first,\n                    InputIterator last,\n                    OutputIterator result) {\n  return std::move(first, last, result);\n}\n\n// Let `E(n)` be `std::move(*(begin(range) + n))`.\n//\n// Let `N` be `size(range)`.\n//\n// Preconditions: `result` is not in `range`.\n//\n// Effects: Moves elements in `range` into the range `[result, result + N)`\n// starting from `begin(range)` and proceeding to `end(range)`. For each\n// non-negative integer `n < N`, performs `*(result + n) = E(n)`.\n//\n// Returns: `result + N`\n//\n// Complexity: Exactly `N` assignments.\n//\n// Reference: https://wg21.link/alg.move#:~:text=ranges::move(R\ntemplate <typename Range,\n          typename OutputIterator,\n          typename = internal::range_category_t<Range>,\n          typename = internal::iterator_category_t<OutputIterator>>\nconstexpr auto move(Range&& range, OutputIterator result) {\n  return ranges::move(ranges::begin(range), ranges::end(range), result);\n}\n\n// Let `E(n)` be `std::move(*(last - n))`.\n//\n// Let `N` be `last - first`.\n//\n// Preconditions: `result` is not in the range `(first, last]`.\n//\n// Effects: Moves elements in the range `[first, last)` into the range\n// `[result - N, result)` starting from `last - 1` and proceeding to `first`.\n// For each positive integer `n ≤ N`, performs `*(result - n) = E(n)`.\n//\n// Returns: `result - N`\n//\n// Complexity: Exactly `N` assignments.\n//\n// Reference: https://wg21.link/alg.move#:~:text=ranges::move_backward(I1\ntemplate <typename BidirectionalIterator1,\n          typename BidirectionalIterator2,\n          typename = internal::iterator_category_t<BidirectionalIterator1>,\n          typename = internal::iterator_category_t<BidirectionalIterator2>>\nconstexpr auto move_backward(BidirectionalIterator1 first,\n                             BidirectionalIterator1 last,\n                             BidirectionalIterator2 result) {\n  return std::move_backward(first, last, result);\n}\n\n// Let `E(n)` be `std::move(*(end(range) - n))`.\n//\n// Let `N` be `size(range)`.\n//\n// Preconditions: `result` is not in the range `(begin(range), end(range)]`.\n//\n// Effects: Moves elements in `range` into the range `[result - N, result)`\n// starting from `end(range) - 1` and proceeding to `begin(range)`. For each\n// positive integer `n ≤ N`, performs `*(result - n) = E(n)`.\n//\n// Returns: `result - N`\n//\n// Complexity: Exactly `N` assignments.\n//\n// Reference: https://wg21.link/alg.move#:~:text=ranges::move_backward(R\ntemplate <typename Range,\n          typename BidirectionalIterator,\n          typename = internal::range_category_t<Range>,\n          typename = internal::iterator_category_t<BidirectionalIterator>>\nconstexpr auto move_backward(Range&& range, BidirectionalIterator result) {\n  return ranges::move_backward(ranges::begin(range), ranges::end(range),\n                               result);\n}\n\n// [alg.swap] Swap\n// Reference: https://wg21.link/alg.swap\n\n// Let `M` be `min(last1 - first1, last2 - first2)`.\n//\n// Preconditions: The two ranges `[first1, last1)` and `[first2, last2)` do not\n// overlap. `*(first1 + n)` is swappable with `*(first2 + n)`.\n//\n// Effects: For each non-negative integer `n < M` performs\n// `swap(*(first1 + n), *(first2 + n))`\n//\n// Returns: `first2 + M`\n//\n// Complexity: Exactly `M` swaps.\n//\n// Reference: https://wg21.link/alg.swap#:~:text=ranges::swap_ranges(I1\ntemplate <typename ForwardIterator1,\n          typename ForwardIterator2,\n          typename = internal::iterator_category_t<ForwardIterator1>,\n          typename = internal::iterator_category_t<ForwardIterator2>>\nconstexpr auto swap_ranges(ForwardIterator1 first1,\n                           ForwardIterator1 last1,\n                           ForwardIterator2 first2,\n                           ForwardIterator2 last2) {\n  // std::swap_ranges does not have a `last2` overload. Thus we need to\n  // adjust `last1` to ensure to not read past `last2`.\n  last1 = std::next(first1, std::min(std::distance(first1, last1),\n                                     std::distance(first2, last2)));\n  return std::swap_ranges(first1, last1, first2);\n}\n\n// Let `M` be `min(size(range1), size(range2))`.\n//\n// Preconditions: The two ranges `range1` and `range2` do not overlap.\n// `*(begin(range1) + n)` is swappable with `*(begin(range2) + n)`.\n//\n// Effects: For each non-negative integer `n < M` performs\n// `swap(*(begin(range1) + n), *(begin(range2) + n))`\n//\n// Returns: `begin(range2) + M`\n//\n// Complexity: Exactly `M` swaps.\n//\n// Reference: https://wg21.link/alg.swap#:~:text=ranges::swap_ranges(R1\ntemplate <typename Range1,\n          typename Range2,\n          typename = internal::range_category_t<Range1>,\n          typename = internal::range_category_t<Range2>>\nconstexpr auto swap_ranges(Range1&& range1, Range2&& range2) {\n  return ranges::swap_ranges(ranges::begin(range1), ranges::end(range1),\n                             ranges::begin(range2), ranges::end(range2));\n}\n\n// [alg.transform] Transform\n// Reference: https://wg21.link/alg.transform\n\n// Let `N` be `last1 - first1`,\n// `E(i)` be `invoke(op, invoke(proj, *(first1 + (i - result))))`.\n//\n// Preconditions: `op` does not invalidate iterators or subranges, nor modify\n// elements in the ranges `[first1, first1 + N]`, and `[result, result + N]`.\n//\n// Effects: Assigns through every iterator `i` in the range\n// `[result, result + N)` a new corresponding value equal to `E(i)`.\n//\n// Returns: `result + N`\n//\n// Complexity: Exactly `N` applications of `op` and any projections.\n//\n// Remarks: result may be equal to `first1`.\n//\n// Reference: https://wg21.link/alg.transform#:~:text=ranges::transform(I\ntemplate <typename InputIterator,\n          typename OutputIterator,\n          typename UnaryOperation,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<InputIterator>,\n          typename = internal::iterator_category_t<OutputIterator>,\n          typename = indirect_result_t<UnaryOperation&,\n                                       projected<InputIterator, Proj>>>\nconstexpr auto transform(InputIterator first1,\n                         InputIterator last1,\n                         OutputIterator result,\n                         UnaryOperation op,\n                         Proj proj = {}) {\n  return std::transform(first1, last1, result, [&op, &proj](auto&& arg) {\n    return base::invoke(op,\n                        base::invoke(proj, std::forward<decltype(arg)>(arg)));\n  });\n}\n\n// Let `N` be `size(range)`,\n// `E(i)` be `invoke(op, invoke(proj, *(begin(range) + (i - result))))`.\n//\n// Preconditions: `op` does not invalidate iterators or subranges, nor modify\n// elements in the ranges `[begin(range), end(range)]`, and\n// `[result, result + N]`.\n//\n// Effects: Assigns through every iterator `i` in the range\n// `[result, result + N)` a new corresponding value equal to `E(i)`.\n//\n// Returns: `result + N`\n//\n// Complexity: Exactly `N` applications of `op` and any projections.\n//\n// Remarks: result may be equal to `begin(range)`.\n//\n// Reference: https://wg21.link/alg.transform#:~:text=ranges::transform(R\ntemplate <typename Range,\n          typename OutputIterator,\n          typename UnaryOperation,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>,\n          typename = internal::iterator_category_t<OutputIterator>,\n          typename = indirect_result_t<UnaryOperation&,\n                                       projected<iterator_t<Range>, Proj>>>\nconstexpr auto transform(Range&& range,\n                         OutputIterator result,\n                         UnaryOperation op,\n                         Proj proj = {}) {\n  return ranges::transform(ranges::begin(range), ranges::end(range), result,\n                           std::move(op), std::move(proj));\n}\n\n// Let:\n// `N` be `min(last1 - first1, last2 - first2)`,\n// `E(i)` be `invoke(binary_op, invoke(proj1, *(first1 + (i - result))),\n//                              invoke(proj2, *(first2 + (i - result))))`.\n//\n// Preconditions: `binary_op` does not invalidate iterators or subranges, nor\n// modify elements in the ranges `[first1, first1 + N]`, `[first2, first2 + N]`,\n// and `[result, result + N]`.\n//\n// Effects: Assigns through every iterator `i` in the range\n// `[result, result + N)` a new corresponding value equal to `E(i)`.\n//\n// Returns: `result + N`\n//\n// Complexity: Exactly `N` applications of `binary_op`, and any projections.\n//\n// Remarks: `result` may be equal to `first1` or `first2`.\n//\n// Reference: https://wg21.link/alg.transform#:~:text=ranges::transform(I1\ntemplate <typename ForwardIterator1,\n          typename ForwardIterator2,\n          typename OutputIterator,\n          typename BinaryOperation,\n          typename Proj1 = identity,\n          typename Proj2 = identity,\n          typename = internal::iterator_category_t<ForwardIterator1>,\n          typename = internal::iterator_category_t<ForwardIterator2>,\n          typename = internal::iterator_category_t<OutputIterator>,\n          typename = indirect_result_t<BinaryOperation&,\n                                       projected<ForwardIterator1, Proj1>,\n                                       projected<ForwardIterator2, Proj2>>>\nconstexpr auto transform(ForwardIterator1 first1,\n                         ForwardIterator1 last1,\n                         ForwardIterator2 first2,\n                         ForwardIterator2 last2,\n                         OutputIterator result,\n                         BinaryOperation binary_op,\n                         Proj1 proj1 = {},\n                         Proj2 proj2 = {}) {\n  // std::transform does not have a `last2` overload. Thus we need to adjust\n  // `last1` to ensure to not read past `last2`.\n  last1 = std::next(first1, std::min(std::distance(first1, last1),\n                                     std::distance(first2, last2)));\n  return std::transform(\n      first1, last1, first2, result,\n      [&binary_op, &proj1, &proj2](auto&& lhs, auto&& rhs) {\n        return base::invoke(\n            binary_op, base::invoke(proj1, std::forward<decltype(lhs)>(lhs)),\n            base::invoke(proj2, std::forward<decltype(rhs)>(rhs)));\n      });\n}\n\n// Let:\n// `N` be `min(size(range1), size(range2)`,\n// `E(i)` be `invoke(binary_op, invoke(proj1, *(begin(range1) + (i - result))),\n//                              invoke(proj2, *(begin(range2) + (i - result))))`\n//\n// Preconditions: `binary_op` does not invalidate iterators or subranges, nor\n// modify elements in the ranges `[begin(range1), end(range1)]`,\n// `[begin(range2), end(range2)]`, and `[result, result + N]`.\n//\n// Effects: Assigns through every iterator `i` in the range\n// `[result, result + N)` a new corresponding value equal to `E(i)`.\n//\n// Returns: `result + N`\n//\n// Complexity: Exactly `N` applications of `binary_op`, and any projections.\n//\n// Remarks: `result` may be equal to `begin(range1)` or `begin(range2)`.\n//\n// Reference: https://wg21.link/alg.transform#:~:text=ranges::transform(R1\ntemplate <typename Range1,\n          typename Range2,\n          typename OutputIterator,\n          typename BinaryOperation,\n          typename Proj1 = identity,\n          typename Proj2 = identity,\n          typename = internal::range_category_t<Range1>,\n          typename = internal::range_category_t<Range2>,\n          typename = internal::iterator_category_t<OutputIterator>,\n          typename = indirect_result_t<BinaryOperation&,\n                                       projected<iterator_t<Range1>, Proj1>,\n                                       projected<iterator_t<Range2>, Proj2>>>\nconstexpr auto transform(Range1&& range1,\n                         Range2&& range2,\n                         OutputIterator result,\n                         BinaryOperation binary_op,\n                         Proj1 proj1 = {},\n                         Proj2 proj2 = {}) {\n  return ranges::transform(ranges::begin(range1), ranges::end(range1),\n                           ranges::begin(range2), ranges::end(range2), result,\n                           std::move(binary_op), std::move(proj1),\n                           std::move(proj2));\n}\n\n// [alg.replace] Replace\n// Reference: https://wg21.link/alg.replace\n\n// Let `E(i)` be `bool(invoke(proj, *i) == old_value)`.\n//\n// Mandates: `new_value` is writable  to `first`.\n//\n// Effects: Substitutes elements referred by the iterator `i` in the range\n// `[first, last)` with `new_value`, when `E(i)` is true.\n//\n// Returns: `last`\n//\n// Complexity: Exactly `last - first` applications of the corresponding\n// predicate and any projection.\n//\n// Reference: https://wg21.link/alg.replace#:~:text=ranges::replace(I\ntemplate <typename ForwardIterator,\n          typename T,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<ForwardIterator>>\nconstexpr auto replace(ForwardIterator first,\n                       ForwardIterator last,\n                       const T& old_value,\n                       const T& new_value,\n                       Proj proj = {}) {\n  // Note: In order to be able to apply `proj` to each element in [first, last)\n  // we are dispatching to std::replace_if instead of std::replace.\n  std::replace_if(\n      first, last,\n      [&proj, &old_value](auto&& lhs) {\n        return base::invoke(proj, std::forward<decltype(lhs)>(lhs)) ==\n               old_value;\n      },\n      new_value);\n  return last;\n}\n\n// Let `E(i)` be `bool(invoke(proj, *i) == old_value)`.\n//\n// Mandates: `new_value` is writable  to `begin(range)`.\n//\n// Effects: Substitutes elements referred by the iterator `i` in `range` with\n// `new_value`, when `E(i)` is true.\n//\n// Returns: `end(range)`\n//\n// Complexity: Exactly `size(range)` applications of the corresponding predicate\n// and any projection.\n//\n// Reference: https://wg21.link/alg.replace#:~:text=ranges::replace(R\ntemplate <typename Range,\n          typename T,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>>\nconstexpr auto replace(Range&& range,\n                       const T& old_value,\n                       const T& new_value,\n                       Proj proj = {}) {\n  return ranges::replace(ranges::begin(range), ranges::end(range), old_value,\n                         new_value, std::move(proj));\n}\n\n// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i)))`.\n//\n// Mandates: `new_value` is writable  to `first`.\n//\n// Effects: Substitutes elements referred by the iterator `i` in the range\n// `[first, last)` with `new_value`, when `E(i)` is true.\n//\n// Returns: `last`\n//\n// Complexity: Exactly `last - first` applications of the corresponding\n// predicate and any projection.\n//\n// Reference: https://wg21.link/alg.replace#:~:text=ranges::replace_if(I\ntemplate <typename ForwardIterator,\n          typename Predicate,\n          typename T,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<ForwardIterator>>\nconstexpr auto replace_if(ForwardIterator first,\n                          ForwardIterator last,\n                          Predicate pred,\n                          const T& new_value,\n                          Proj proj = {}) {\n  std::replace_if(first, last, internal::ProjectedUnaryPredicate(pred, proj),\n                  new_value);\n  return last;\n}\n\n// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i)))`.\n//\n// Mandates: `new_value` is writable  to `begin(range)`.\n//\n// Effects: Substitutes elements referred by the iterator `i` in `range` with\n// `new_value`, when `E(i)` is true.\n//\n// Returns: `end(range)`\n//\n// Complexity: Exactly `size(range)` applications of the corresponding predicate\n// and any projection.\n//\n// Reference: https://wg21.link/alg.replace#:~:text=ranges::replace_if(R\ntemplate <typename Range,\n          typename Predicate,\n          typename T,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>>\nconstexpr auto replace_if(Range&& range,\n                          Predicate pred,\n                          const T& new_value,\n                          Proj proj = {}) {\n  return ranges::replace_if(ranges::begin(range), ranges::end(range),\n                            std::move(pred), new_value, std::move(proj));\n}\n\n// Let `E(i)` be `bool(invoke(proj, *(first + (i - result))) == old_value)`.\n//\n// Mandates: The results of the expressions `*first` and `new_value` are\n// writable  to `result`.\n//\n// Preconditions: The ranges `[first, last)` and `[result, result + (last -\n// first))` do not overlap.\n//\n// Effects: Assigns through every iterator `i` in the range `[result, result +\n// (last - first))` a new corresponding value, `new_value` if `E(i)` is true, or\n// `*(first + (i - result))` otherwise.\n//\n// Returns: `result + (last - first)`.\n//\n// Complexity: Exactly `last - first` applications of the corresponding\n// predicate and any projection.\n//\n// Reference: https://wg21.link/alg.replace#:~:text=ranges::replace_copy(I\ntemplate <typename InputIterator,\n          typename OutputIterator,\n          typename T,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<InputIterator>,\n          typename = internal::iterator_category_t<OutputIterator>>\nconstexpr auto replace_copy(InputIterator first,\n                            InputIterator last,\n                            OutputIterator result,\n                            const T& old_value,\n                            const T& new_value,\n                            Proj proj = {}) {\n  // Note: In order to be able to apply `proj` to each element in [first, last)\n  // we are dispatching to std::replace_copy_if instead of std::replace_copy.\n  std::replace_copy_if(\n      first, last, result,\n      [&proj, &old_value](auto&& lhs) {\n        return base::invoke(proj, std::forward<decltype(lhs)>(lhs)) ==\n               old_value;\n      },\n      new_value);\n  return last;\n}\n\n// Let `E(i)` be\n// `bool(invoke(proj, *(begin(range) + (i - result))) == old_value)`.\n//\n// Mandates: The results of the expressions `*begin(range)` and `new_value` are\n// writable  to `result`.\n//\n// Preconditions: The ranges `range` and `[result, result + size(range))` do not\n// overlap.\n//\n// Effects: Assigns through every iterator `i` in the range `[result, result +\n// size(range))` a new corresponding value, `new_value` if `E(i)` is true, or\n// `*(begin(range) + (i - result))` otherwise.\n//\n// Returns: `result + size(range)`.\n//\n// Complexity: Exactly `size(range)` applications of the corresponding\n// predicate and any projection.\n//\n// Reference: https://wg21.link/alg.replace#:~:text=ranges::replace_copy(R\ntemplate <typename Range,\n          typename OutputIterator,\n          typename T,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>,\n          typename = internal::iterator_category_t<OutputIterator>>\nconstexpr auto replace_copy(Range&& range,\n                            OutputIterator result,\n                            const T& old_value,\n                            const T& new_value,\n                            Proj proj = {}) {\n  return ranges::replace_copy(ranges::begin(range), ranges::end(range), result,\n                              old_value, new_value, std::move(proj));\n}\n\n// Let `E(i)` be `bool(invoke(pred, invoke(proj, *(first + (i - result)))))`.\n//\n// Mandates: The results of the expressions `*first` and `new_value` are\n// writable  to `result`.\n//\n// Preconditions: The ranges `[first, last)` and `[result, result + (last -\n// first))` do not overlap.\n//\n// Effects: Assigns through every iterator `i` in the range `[result, result +\n// (last - first))` a new corresponding value, `new_value` if `E(i)` is true, or\n// `*(first + (i - result))` otherwise.\n//\n// Returns: `result + (last - first)`.\n//\n// Complexity: Exactly `last - first` applications of the corresponding\n// predicate and any projection.\n//\n// Reference: https://wg21.link/alg.replace#:~:text=ranges::replace_copy_if(I\ntemplate <typename InputIterator,\n          typename OutputIterator,\n          typename Predicate,\n          typename T,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<InputIterator>,\n          typename = internal::iterator_category_t<OutputIterator>>\nconstexpr auto replace_copy_if(InputIterator first,\n                               InputIterator last,\n                               OutputIterator result,\n                               Predicate pred,\n                               const T& new_value,\n                               Proj proj = {}) {\n  return std::replace_copy_if(first, last, result,\n                              internal::ProjectedUnaryPredicate(pred, proj),\n                              new_value);\n}\n\n// Let `E(i)` be\n// `bool(invoke(pred, invoke(proj, *(begin(range) + (i - result)))))`.\n//\n// Mandates: The results of the expressions `*begin(range)` and `new_value` are\n// writable  to `result`.\n//\n// Preconditions: The ranges `range` and `[result, result + size(range))` do not\n// overlap.\n//\n// Effects: Assigns through every iterator `i` in the range `[result, result +\n// size(range))` a new corresponding value, `new_value` if `E(i)` is true, or\n// `*(begin(range) + (i - result))` otherwise.\n//\n// Returns: `result + size(range)`.\n//\n// Complexity: Exactly `size(range)` applications of the corresponding\n// predicate and any projection.\n//\n// Reference: https://wg21.link/alg.replace#:~:text=ranges::replace_copy_if(R\ntemplate <typename Range,\n          typename OutputIterator,\n          typename Predicate,\n          typename T,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>,\n          typename = internal::iterator_category_t<OutputIterator>>\nconstexpr auto replace_copy_if(Range&& range,\n                               OutputIterator result,\n                               Predicate pred,\n                               const T& new_value,\n                               Proj proj = {}) {\n  return ranges::replace_copy_if(ranges::begin(range), ranges::end(range),\n                                 result, pred, new_value, std::move(proj));\n}\n\n// [alg.fill] Fill\n// Reference: https://wg21.link/alg.fill\n\n// Let `N` be `last - first`.\n//\n// Mandates: The expression `value` is writable to the output iterator.\n//\n// Effects: Assigns `value` through all the iterators in the range\n// `[first, last)`.\n//\n// Returns: `last`.\n//\n// Complexity: Exactly `N` assignments.\n//\n// Reference: https://wg21.link/alg.fill#:~:text=ranges::fill(O\ntemplate <typename OutputIterator,\n          typename T,\n          typename = internal::iterator_category_t<OutputIterator>>\nconstexpr auto fill(OutputIterator first, OutputIterator last, const T& value) {\n  std::fill(first, last, value);\n  return last;\n}\n\n// Let `N` be `size(range)`.\n//\n// Mandates: The expression `value` is writable to the output iterator.\n//\n// Effects: Assigns `value` through all the iterators in `range`.\n//\n// Returns: `end(range)`.\n//\n// Complexity: Exactly `N` assignments.\n//\n// Reference: https://wg21.link/alg.fill#:~:text=ranges::fill(R\ntemplate <typename Range,\n          typename T,\n          typename = internal::range_category_t<Range>>\nconstexpr auto fill(Range&& range, const T& value) {\n  return ranges::fill(ranges::begin(range), ranges::end(range), value);\n}\n\n// Let `N` be `max(0, n)`.\n//\n// Mandates: The expression `value` is writable to the output iterator.\n// The type `Size` is convertible to an integral type.\n//\n// Effects: Assigns `value` through all the iterators in `[first, first + N)`.\n//\n// Returns: `first + N`.\n//\n// Complexity: Exactly `N` assignments.\n//\n// Reference: https://wg21.link/alg.fill#:~:text=ranges::fill_n(O\ntemplate <typename OutputIterator,\n          typename Size,\n          typename T,\n          typename = internal::iterator_category_t<OutputIterator>>\nconstexpr auto fill_n(OutputIterator first, Size n, const T& value) {\n  return std::fill_n(first, n, value);\n}\n\n// [alg.generate] Generate\n// Reference: https://wg21.link/alg.generate\n\n// Let `N` be `last - first`.\n//\n// Effects: Assigns the result of successive evaluations of gen() through each\n// iterator in the range `[first, last)`.\n//\n// Returns: `last`.\n//\n// Complexity: Exactly `N` evaluations of `gen()` and assignments.\n//\n// Reference: https://wg21.link/alg.generate#:~:text=ranges::generate(O\ntemplate <typename OutputIterator,\n          typename Generator,\n          typename = internal::iterator_category_t<OutputIterator>>\nconstexpr auto generate(OutputIterator first,\n                        OutputIterator last,\n                        Generator gen) {\n  std::generate(first, last, std::move(gen));\n  return last;\n}\n\n// Let `N` be `size(range)`.\n//\n// Effects: Assigns the result of successive evaluations of gen() through each\n// iterator in `range`.\n//\n// Returns: `end(range)`.\n//\n// Complexity: Exactly `N` evaluations of `gen()` and assignments.\n//\n// Reference: https://wg21.link/alg.generate#:~:text=ranges::generate(R\ntemplate <typename Range,\n          typename Generator,\n          typename = internal::range_category_t<Range>>\nconstexpr auto generate(Range&& range, Generator gen) {\n  return ranges::generate(ranges::begin(range), ranges::end(range),\n                          std::move(gen));\n}\n\n// Let `N` be `max(0, n)`.\n//\n// Mandates: `Size` is convertible to an integral type.\n//\n// Effects: Assigns the result of successive evaluations of gen() through each\n// iterator in the range `[first, first + N)`.\n//\n// Returns: `first + N`.\n//\n// Complexity: Exactly `N` evaluations of `gen()` and assignments.\n//\n// Reference: https://wg21.link/alg.generate#:~:text=ranges::generate_n(O\ntemplate <typename OutputIterator,\n          typename Size,\n          typename Generator,\n          typename = internal::iterator_category_t<OutputIterator>>\nconstexpr auto generate_n(OutputIterator first, Size n, Generator gen) {\n  return std::generate_n(first, n, std::move(gen));\n}\n\n// [alg.remove] Remove\n// Reference: https://wg21.link/alg.remove\n\n// Let `E(i)` be `bool(invoke(proj, *i) == value)`.\n//\n// Effects: Eliminates all the elements referred to by iterator `i` in the range\n// `[first, last)` for which `E(i)` holds.\n//\n// Returns: The end of the resulting range.\n//\n// Remarks: Stable.\n//\n// Complexity: Exactly `last - first` applications of the corresponding\n// predicate and any projection.\n//\n// Reference: https://wg21.link/alg.remove#:~:text=ranges::remove(I\ntemplate <typename ForwardIterator,\n          typename T,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<ForwardIterator>>\nconstexpr auto remove(ForwardIterator first,\n                      ForwardIterator last,\n                      const T& value,\n                      Proj proj = {}) {\n  // Note: In order to be able to apply `proj` to each element in [first, last)\n  // we are dispatching to std::remove_if instead of std::remove.\n  return std::remove_if(first, last, [&proj, &value](auto&& lhs) {\n    return base::invoke(proj, std::forward<decltype(lhs)>(lhs)) == value;\n  });\n}\n\n// Let `E(i)` be `bool(invoke(proj, *i) == value)`.\n//\n// Effects: Eliminates all the elements referred to by iterator `i` in `range`\n// for which `E(i)` holds.\n//\n// Returns: The end of the resulting range.\n//\n// Remarks: Stable.\n//\n// Complexity: Exactly `size(range)` applications of the corresponding predicate\n// and any projection.\n//\n// Reference: https://wg21.link/alg.remove#:~:text=ranges::remove(R\ntemplate <typename Range,\n          typename T,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>>\nconstexpr auto remove(Range&& range, const T& value, Proj proj = {}) {\n  return ranges::remove(ranges::begin(range), ranges::end(range), value,\n                        std::move(proj));\n}\n\n// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i)))`.\n//\n// Effects: Eliminates all the elements referred to by iterator `i` in the range\n// `[first, last)` for which `E(i)` holds.\n//\n// Returns: The end of the resulting range.\n//\n// Remarks: Stable.\n//\n// Complexity: Exactly `last - first` applications of the corresponding\n// predicate and any projection.\n//\n// Reference: https://wg21.link/alg.remove#:~:text=ranges::remove_if(I\ntemplate <typename ForwardIterator,\n          typename Predicate,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<ForwardIterator>>\nconstexpr auto remove_if(ForwardIterator first,\n                         ForwardIterator last,\n                         Predicate pred,\n                         Proj proj = {}) {\n  return std::remove_if(first, last,\n                        internal::ProjectedUnaryPredicate(pred, proj));\n}\n\n// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i)))`.\n//\n// Effects: Eliminates all the elements referred to by iterator `i` in `range`.\n//\n// Returns: The end of the resulting range.\n//\n// Remarks: Stable.\n//\n// Complexity: Exactly `size(range)` applications of the corresponding predicate\n// and any projection.\n//\n// Reference: https://wg21.link/alg.remove#:~:text=ranges::remove_if(R\ntemplate <typename Range,\n          typename Predicate,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>>\nconstexpr auto remove_if(Range&& range, Predicate pred, Proj proj = {}) {\n  return ranges::remove_if(ranges::begin(range), ranges::end(range),\n                           std::move(pred), std::move(proj));\n}\n\n// Let `E(i)` be `bool(invoke(proj, *i) == value)`.\n//\n// Let `N` be the number of elements in `[first, last)` for which `E(i)` is\n// false.\n//\n// Mandates: `*first` is writable to `result`.\n//\n// Preconditions: The ranges `[first, last)` and `[result, result + (last -\n// first))` do not overlap.\n//\n// Effects: Copies all the elements referred to by the iterator `i` in the range\n// `[first, last)` for which `E(i)` is false.\n//\n// Returns: `result + N`.\n//\n// Complexity: Exactly `last - first` applications of the corresponding\n// predicate and any projection.\n//\n// Remarks: Stable.\n//\n// Reference: https://wg21.link/alg.remove#:~:text=ranges::remove_copy(I\ntemplate <typename InputIterator,\n          typename OutputIterator,\n          typename T,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<InputIterator>,\n          typename = internal::iterator_category_t<OutputIterator>>\nconstexpr auto remove_copy(InputIterator first,\n                           InputIterator last,\n                           OutputIterator result,\n                           const T& value,\n                           Proj proj = {}) {\n  // Note: In order to be able to apply `proj` to each element in [first, last)\n  // we are dispatching to std::remove_copy_if instead of std::remove_copy.\n  return std::remove_copy_if(first, last, result, [&proj, &value](auto&& lhs) {\n    return base::invoke(proj, std::forward<decltype(lhs)>(lhs)) == value;\n  });\n}\n\n// Let `E(i)` be `bool(invoke(proj, *i) == value)`.\n//\n// Let `N` be the number of elements in `range` for which `E(i)` is false.\n//\n// Mandates: `*begin(range)` is writable to `result`.\n//\n// Preconditions: The ranges `range` and `[result, result + size(range))` do not\n// overlap.\n//\n// Effects: Copies all the elements referred to by the iterator `i` in `range`\n//  for which `E(i)` is false.\n//\n// Returns: `result + N`.\n//\n// Complexity: Exactly `size(range)` applications of the corresponding\n// predicate and any projection.\n//\n// Remarks: Stable.\n//\n// Reference: https://wg21.link/alg.remove#:~:text=ranges::remove_copy(R\ntemplate <typename Range,\n          typename OutputIterator,\n          typename T,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>,\n          typename = internal::iterator_category_t<OutputIterator>>\nconstexpr auto remove_copy(Range&& range,\n                           OutputIterator result,\n                           const T& value,\n                           Proj proj = {}) {\n  return ranges::remove_copy(ranges::begin(range), ranges::end(range), result,\n                             value, std::move(proj));\n}\n\n// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i)))`.\n//\n// Let `N` be the number of elements in `[first, last)` for which `E(i)` is\n// false.\n//\n// Mandates: `*first` is writable to `result`.\n//\n// Preconditions: The ranges `[first, last)` and `[result, result + (last -\n// first))` do not overlap.\n//\n// Effects: Copies all the elements referred to by the iterator `i` in the range\n// `[first, last)` for which `E(i)` is false.\n//\n// Returns: `result + N`.\n//\n// Complexity: Exactly `last - first` applications of the corresponding\n// predicate and any projection.\n//\n// Remarks: Stable.\n//\n// Reference: https://wg21.link/alg.remove#:~:text=ranges::remove_copy_if(I\ntemplate <typename InputIterator,\n          typename OutputIterator,\n          typename Pred,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<InputIterator>,\n          typename = internal::iterator_category_t<OutputIterator>>\nconstexpr auto remove_copy_if(InputIterator first,\n                              InputIterator last,\n                              OutputIterator result,\n                              Pred pred,\n                              Proj proj = {}) {\n  return std::remove_copy_if(first, last, result,\n                             internal::ProjectedUnaryPredicate(pred, proj));\n}\n\n// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i)))`.\n//\n// Let `N` be the number of elements in `range` for which `E(i)` is false.\n//\n// Mandates: `*begin(range)` is writable to `result`.\n//\n// Preconditions: The ranges `range` and `[result, result + size(range))` do not\n// overlap.\n//\n// Effects: Copies all the elements referred to by the iterator `i` in `range`\n//  for which `E(i)` is false.\n//\n// Returns: `result + N`.\n//\n// Complexity: Exactly `size(range)` applications of the corresponding\n// predicate and any projection.\n//\n// Remarks: Stable.\n//\n// Reference: https://wg21.link/alg.remove#:~:text=ranges::remove_copy(R\ntemplate <typename Range,\n          typename OutputIterator,\n          typename Pred,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>,\n          typename = internal::iterator_category_t<OutputIterator>>\nconstexpr auto remove_copy_if(Range&& range,\n                              OutputIterator result,\n                              Pred pred,\n                              Proj proj = {}) {\n  return ranges::remove_copy_if(ranges::begin(range), ranges::end(range),\n                                result, std::move(pred), std::move(proj));\n}\n\n// [alg.unique] Unique\n// Reference: https://wg21.link/alg.unique\n\n// Let `E(i)` be `bool(invoke(comp, invoke(proj, *(i - 1)), invoke(proj, *i)))`.\n//\n// Effects: For a nonempty range, eliminates all but the first element from\n// every consecutive group of equivalent elements referred to by the iterator\n// `i` in the range `[first + 1, last)` for which `E(i)` is true.\n//\n// Returns: The end of the resulting range.\n//\n// Complexity: For nonempty ranges, exactly `(last - first) - 1` applications of\n// the corresponding predicate and no more than twice as many applications of\n// any projection.\n//\n// Reference: https://wg21.link/alg.unique#:~:text=ranges::unique(I\ntemplate <typename ForwardIterator,\n          typename Comp = ranges::equal_to,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<ForwardIterator>,\n          typename = indirect_result_t<Comp&,\n                                       projected<ForwardIterator, Proj>,\n                                       projected<ForwardIterator, Proj>>>\nconstexpr auto unique(ForwardIterator first,\n                      ForwardIterator last,\n                      Comp comp = {},\n                      Proj proj = {}) {\n  return std::unique(first, last,\n                     internal::ProjectedBinaryPredicate(comp, proj, proj));\n}\n\n// Let `E(i)` be `bool(invoke(comp, invoke(proj, *(i - 1)), invoke(proj, *i)))`.\n//\n// Effects: For a nonempty range, eliminates all but the first element from\n// every consecutive group of equivalent elements referred to by the iterator\n// `i` in the range `[begin(range) + 1, end(range))` for which `E(i)` is true.\n//\n// Returns: The end of the resulting range.\n//\n// Complexity: For nonempty ranges, exactly `size(range) - 1` applications of\n// the corresponding predicate and no more than twice as many applications of\n// any projection.\n//\n// Reference: https://wg21.link/alg.unique#:~:text=ranges::unique(R\ntemplate <typename Range,\n          typename Comp = ranges::equal_to,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>,\n          typename = indirect_result_t<Comp&,\n                                       projected<iterator_t<Range>, Proj>,\n                                       projected<iterator_t<Range>, Proj>>>\nconstexpr auto unique(Range&& range, Comp comp = {}, Proj proj = {}) {\n  return ranges::unique(ranges::begin(range), ranges::end(range),\n                        std::move(comp), std::move(proj));\n}\n\n// Let `E(i)` be `bool(invoke(comp, invoke(proj, *i), invoke(proj, *(i - 1))))`.\n//\n// Mandates: `*first` is writable to `result`.\n//\n// Preconditions: The ranges `[first, last)` and\n// `[result, result + (last - first))` do not overlap.\n//\n// Effects: Copies only the first element from every consecutive group of equal\n// elements referred to by the iterator `i` in the range `[first, last)` for\n// which `E(i)` holds.\n//\n// Returns: `result + N`.\n//\n// Complexity: Exactly `last - first - 1` applications of the corresponding\n// predicate and no more than twice as many applications of any projection.\n//\n// Reference: https://wg21.link/alg.unique#:~:text=ranges::unique_copy(I\ntemplate <typename ForwardIterator,\n          typename OutputIterator,\n          typename Comp = ranges::equal_to,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<ForwardIterator>,\n          typename = internal::iterator_category_t<OutputIterator>>\nconstexpr auto unique_copy(ForwardIterator first,\n                           ForwardIterator last,\n                           OutputIterator result,\n                           Comp comp = {},\n                           Proj proj = {}) {\n  return std::unique_copy(first, last, result,\n                          internal::ProjectedBinaryPredicate(comp, proj, proj));\n}\n\n// Let `E(i)` be `bool(invoke(comp, invoke(proj, *i), invoke(proj, *(i - 1))))`.\n//\n// Mandates: `*begin(range)` is writable to `result`.\n//\n// Preconditions: The ranges `range` and `[result, result + size(range))` do not\n// overlap.\n//\n// Effects: Copies only the first element from every consecutive group of equal\n// elements referred to by the iterator `i` in `range` for which `E(i)` holds.\n//\n// Returns: `result + N`.\n//\n// Complexity: Exactly `size(range) - 1` applications of the corresponding\n// predicate and no more than twice as many applications of any projection.\n//\n// Reference: https://wg21.link/alg.unique#:~:text=ranges::unique_copy(R\ntemplate <typename Range,\n          typename OutputIterator,\n          typename Comp = ranges::equal_to,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>,\n          typename = internal::iterator_category_t<OutputIterator>>\nconstexpr auto unique_copy(Range&& range,\n                           OutputIterator result,\n                           Comp comp = {},\n                           Proj proj = {}) {\n  return ranges::unique_copy(ranges::begin(range), ranges::end(range), result,\n                             std::move(comp), std::move(proj));\n}\n\n// [alg.reverse] Reverse\n// Reference: https://wg21.link/alg.reverse\n\n// Effects: For each non-negative integer `i < (last - first) / 2`, applies\n// `std::iter_swap` to all pairs of iterators `first + i, (last - i) - 1`.\n//\n// Returns: `last`.\n//\n// Complexity: Exactly `(last - first)/2` swaps.\n//\n// Reference: https://wg21.link/alg.reverse#:~:text=ranges::reverse(I\ntemplate <typename BidirectionalIterator,\n          typename = internal::iterator_category_t<BidirectionalIterator>>\nconstexpr auto reverse(BidirectionalIterator first,\n                       BidirectionalIterator last) {\n  std::reverse(first, last);\n  return last;\n}\n\n// Effects: For each non-negative integer `i < size(range) / 2`, applies\n// `std::iter_swap` to all pairs of iterators\n// `begin(range) + i, (end(range) - i) - 1`.\n//\n// Returns: `end(range)`.\n//\n// Complexity: Exactly `size(range)/2` swaps.\n//\n// Reference: https://wg21.link/alg.reverse#:~:text=ranges::reverse(R\ntemplate <typename Range, typename = internal::range_category_t<Range>>\nconstexpr auto reverse(Range&& range) {\n  return ranges::reverse(ranges::begin(range), ranges::end(range));\n}\n\n// Let `N` be `last - first`.\n//\n// Preconditions: The ranges `[first, last)` and `[result, result + N)` do not\n// overlap.\n//\n// Effects: Copies the range `[first, last)` to the range `[result, result + N)`\n// such that for every non-negative integer `i < N` the following assignment\n// takes place: `*(result + N - 1 - i) = *(first + i)`.\n//\n// Returns: `result + N`.\n//\n// Complexity: Exactly `N` assignments.\n//\n// Reference: https://wg21.link/alg.reverse#:~:text=ranges::reverse_copy(I\ntemplate <typename BidirectionalIterator,\n          typename OutputIterator,\n          typename = internal::iterator_category_t<BidirectionalIterator>,\n          typename = internal::iterator_category_t<OutputIterator>>\nconstexpr auto reverse_copy(BidirectionalIterator first,\n                            BidirectionalIterator last,\n                            OutputIterator result) {\n  return std::reverse_copy(first, last, result);\n}\n\n// Let `N` be `size(range)`.\n//\n// Preconditions: The ranges `range` and `[result, result + N)` do not\n// overlap.\n//\n// Effects: Copies `range` to the range `[result, result + N)` such that for\n// every non-negative integer `i < N` the following assignment takes place:\n// `*(result + N - 1 - i) = *(begin(range) + i)`.\n//\n// Returns: `result + N`.\n//\n// Complexity: Exactly `N` assignments.\n//\n// Reference: https://wg21.link/alg.reverse#:~:text=ranges::reverse_copy(R\ntemplate <typename Range,\n          typename OutputIterator,\n          typename = internal::range_category_t<Range>,\n          typename = internal::iterator_category_t<OutputIterator>>\nconstexpr auto reverse_copy(Range&& range, OutputIterator result) {\n  return ranges::reverse_copy(ranges::begin(range), ranges::end(range), result);\n}\n\n// [alg.rotate] Rotate\n// Reference: https://wg21.link/alg.rotate\n\n// Preconditions: `[first, middle)` and `[middle, last)` are valid ranges.\n//\n// Effects: For each non-negative integer `i < (last - first)`, places the\n// element from the position `first + i` into position\n// `first + (i + (last - middle)) % (last - first)`.\n//\n// Returns: `first + (last - middle)`.\n//\n// Complexity: At most `last - first` swaps.\n//\n// Reference: https://wg21.link/alg.rotate#:~:text=ranges::rotate(I\ntemplate <typename ForwardIterator,\n          typename = internal::iterator_category_t<ForwardIterator>>\nconstexpr auto rotate(ForwardIterator first,\n                      ForwardIterator middle,\n                      ForwardIterator last) {\n  return std::rotate(first, middle, last);\n}\n\n// Preconditions: `[begin(range), middle)` and `[middle, end(range))` are valid\n// ranges.\n//\n// Effects: For each non-negative integer `i < size(range)`, places the element\n// from the position `begin(range) + i` into position\n// `begin(range) + (i + (end(range) - middle)) % size(range)`.\n//\n// Returns: `begin(range) + (end(range) - middle)`.\n//\n// Complexity: At most `size(range)` swaps.\n//\n// Reference: https://wg21.link/alg.rotate#:~:text=ranges::rotate(R\ntemplate <typename Range, typename = internal::range_category_t<Range>>\nconstexpr auto rotate(Range&& range, iterator_t<Range> middle) {\n  return ranges::rotate(ranges::begin(range), middle, ranges::end(range));\n}\n\n// Let `N` be `last - first`.\n//\n// Preconditions: `[first, middle)` and `[middle, last)` are valid ranges. The\n// ranges `[first, last)` and `[result, result + N)` do not overlap.\n//\n// Effects: Copies the range `[first, last)` to the range `[result, result + N)`\n// such that for each non-negative integer `i < N` the following assignment\n// takes place: `*(result + i) = *(first + (i + (middle - first)) % N)`.\n//\n// Returns: `result + N`.\n//\n// Complexity: Exactly `N` assignments.\n//\n// Reference: https://wg21.link/alg.rotate#:~:text=ranges::rotate_copy(I\ntemplate <typename ForwardIterator,\n          typename OutputIterator,\n          typename = internal::iterator_category_t<ForwardIterator>,\n          typename = internal::iterator_category_t<OutputIterator>>\nconstexpr auto rotate_copy(ForwardIterator first,\n                           ForwardIterator middle,\n                           ForwardIterator last,\n                           OutputIterator result) {\n  return std::rotate_copy(first, middle, last, result);\n}\n\n// Let `N` be `size(range)`.\n//\n// Preconditions: `[begin(range), middle)` and `[middle, end(range))` are valid\n// ranges. The ranges `range` and `[result, result + N)` do not overlap.\n//\n// Effects: Copies `range` to the range `[result, result + N)` such that for\n// each non-negative integer `i < N` the following assignment takes place:\n// `*(result + i) = *(begin(range) + (i + (middle - begin(range))) % N)`.\n//\n// Returns: `result + N`.\n//\n// Complexity: Exactly `N` assignments.\n//\n// Reference: https://wg21.link/alg.rotate#:~:text=ranges::rotate_copy(R\ntemplate <typename Range,\n          typename OutputIterator,\n          typename = internal::range_category_t<Range>,\n          typename = internal::iterator_category_t<OutputIterator>>\nconstexpr auto rotate_copy(Range&& range,\n                           iterator_t<Range> middle,\n                           OutputIterator result) {\n  return ranges::rotate_copy(ranges::begin(range), middle, ranges::end(range),\n                             result);\n}\n\n// [alg.random.sample] Sample\n// Reference: https://wg21.link/alg.random.sample\n\n// Currently not implemented due to lack of std::sample in C++14.\n// TODO(crbug.com/1071094): Consider implementing a hand-rolled version.\n\n// [alg.random.shuffle] Shuffle\n// Reference: https://wg21.link/alg.random.shuffle\n\n// Preconditions: The type `std::remove_reference_t<UniformRandomBitGenerator>`\n// meets the uniform random bit generator requirements.\n//\n// Effects: Permutes the elements in the range `[first, last)` such that each\n// possible permutation of those elements has equal probability of appearance.\n//\n// Returns: `last`.\n//\n// Complexity: Exactly `(last - first) - 1` swaps.\n//\n// Remarks: To the extent that the implementation of this function makes use of\n// random numbers, the object referenced by g shall serve as the\n// implementation's source of randomness.\n//\n// Reference: https://wg21.link/alg.random.shuffle#:~:text=ranges::shuffle(I\ntemplate <typename RandomAccessIterator,\n          typename UniformRandomBitGenerator,\n          typename = internal::iterator_category_t<RandomAccessIterator>>\nconstexpr auto shuffle(RandomAccessIterator first,\n                       RandomAccessIterator last,\n                       UniformRandomBitGenerator&& g) {\n  std::shuffle(first, last, std::forward<UniformRandomBitGenerator>(g));\n  return last;\n}\n\n// Preconditions: The type `std::remove_reference_t<UniformRandomBitGenerator>`\n// meets the uniform random bit generator requirements.\n//\n// Effects: Permutes the elements in `range` such that each possible permutation\n// of those elements has equal probability of appearance.\n//\n// Returns: `end(range)`.\n//\n// Complexity: Exactly `size(range) - 1` swaps.\n//\n// Remarks: To the extent that the implementation of this function makes use of\n// random numbers, the object referenced by g shall serve as the\n// implementation's source of randomness.\n//\n// Reference: https://wg21.link/alg.random.shuffle#:~:text=ranges::shuffle(R\ntemplate <typename Range,\n          typename UniformRandomBitGenerator,\n          typename = internal::range_category_t<Range>>\nconstexpr auto shuffle(Range&& range, UniformRandomBitGenerator&& g) {\n  return ranges::shuffle(ranges::begin(range), ranges::end(range),\n                         std::forward<UniformRandomBitGenerator>(g));\n}\n\n// [alg.nonmodifying] Sorting and related operations\n// Reference: https://wg21.link/alg.sorting\n\n// [alg.sort] Sorting\n// Reference: https://wg21.link/alg.sort\n\n// [sort] sort\n// Reference: https://wg21.link/sort\n\n// Effects: Sorts the elements in the range `[first, last)` with respect to\n// `comp` and `proj`.\n//\n// Returns: `last`.\n//\n// Complexity: Let `N` be `last - first`. `O(N log N)` comparisons and\n// projections.\n//\n// Reference: https://wg21.link/sort#:~:text=ranges::sort(I\ntemplate <typename RandomAccessIterator,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<RandomAccessIterator>,\n          typename = indirect_result_t<Comp&,\n                                       projected<RandomAccessIterator, Proj>,\n                                       projected<RandomAccessIterator, Proj>>>\nconstexpr auto sort(RandomAccessIterator first,\n                    RandomAccessIterator last,\n                    Comp comp = {},\n                    Proj proj = {}) {\n  std::sort(first, last, internal::ProjectedBinaryPredicate(comp, proj, proj));\n  return last;\n}\n\n// Effects: Sorts the elements in `range` with respect to `comp` and `proj`.\n//\n// Returns: `end(range)`.\n//\n// Complexity: Let `N` be `size(range)`. `O(N log N)` comparisons and\n// projections.\n//\n// Reference: https://wg21.link/sort#:~:text=ranges::sort(R\ntemplate <typename Range,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>,\n          typename = indirect_result_t<Comp&,\n                                       projected<iterator_t<Range>, Proj>,\n                                       projected<iterator_t<Range>, Proj>>>\nconstexpr auto sort(Range&& range, Comp comp = {}, Proj proj = {}) {\n  return ranges::sort(ranges::begin(range), ranges::end(range), std::move(comp),\n                      std::move(proj));\n}\n\n// [stable.sort] stable_sort\n// Reference: https://wg21.link/stable.sort\n\n// Effects: Sorts the elements in the range `[first, last)` with respect to\n// `comp` and `proj`.\n//\n// Returns: `last`.\n//\n// Complexity: Let `N` be `last - first`. If enough extra memory is available,\n// `N log (N)` comparisons. Otherwise, at most `N log² (N)` comparisons. In\n// either case, twice as many projections as the number of comparisons.\n//\n// Remarks: Stable.\n//\n// Reference: https://wg21.link/stable.sort#:~:text=ranges::stable_sort(I\ntemplate <typename RandomAccessIterator,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<RandomAccessIterator>,\n          typename = indirect_result_t<Comp&,\n                                       projected<RandomAccessIterator, Proj>,\n                                       projected<RandomAccessIterator, Proj>>>\nconstexpr auto stable_sort(RandomAccessIterator first,\n                           RandomAccessIterator last,\n                           Comp comp = {},\n                           Proj proj = {}) {\n  std::stable_sort(first, last,\n                   internal::ProjectedBinaryPredicate(comp, proj, proj));\n  return last;\n}\n\n// Effects: Sorts the elements in `range` with respect to `comp` and `proj`.\n//\n// Returns: `end(rang)`.\n//\n// Complexity: Let `N` be `size(range)`. If enough extra memory is available,\n// `N log (N)` comparisons. Otherwise, at most `N log² (N)` comparisons. In\n// either case, twice as many projections as the number of comparisons.\n//\n// Remarks: Stable.\n//\n// Reference: https://wg21.link/stable.sort#:~:text=ranges::stable_sort(R\ntemplate <typename Range,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>,\n          typename = indirect_result_t<Comp&,\n                                       projected<iterator_t<Range>, Proj>,\n                                       projected<iterator_t<Range>, Proj>>>\nconstexpr auto stable_sort(Range&& range, Comp comp = {}, Proj proj = {}) {\n  return ranges::stable_sort(ranges::begin(range), ranges::end(range),\n                             std::move(comp), std::move(proj));\n}\n\n// [partial.sort] partial_sort\n// Reference: https://wg21.link/partial.sort\n\n// Preconditions: `[first, middle)` and `[middle, last)` are valid ranges.\n//\n// Effects: Places the first `middle - first` elements from the range\n// `[first, last)` as sorted with respect to `comp` and `proj` into the range\n// `[first, middle)`. The rest of the elements in the range `[middle, last)` are\n// placed in an unspecified order.\n//\n// Returns: `last`.\n//\n// Complexity: Approximately `(last - first) * log(middle - first)` comparisons,\n// and twice as many projections.\n//\n// Reference: https://wg21.link/partial.sort#:~:text=ranges::partial_sort(I\ntemplate <typename RandomAccessIterator,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<RandomAccessIterator>,\n          typename = indirect_result_t<Comp&,\n                                       projected<RandomAccessIterator, Proj>,\n                                       projected<RandomAccessIterator, Proj>>>\nconstexpr auto partial_sort(RandomAccessIterator first,\n                            RandomAccessIterator middle,\n                            RandomAccessIterator last,\n                            Comp comp = {},\n                            Proj proj = {}) {\n  std::partial_sort(first, middle, last,\n                    internal::ProjectedBinaryPredicate(comp, proj, proj));\n  return last;\n}\n\n// Preconditions: `[begin(range), middle)` and `[middle, end(range))` are valid\n// ranges.\n//\n// Effects: Places the first `middle - begin(range)` elements from `range` as\n// sorted with respect to `comp` and `proj` into the range\n// `[begin(range), middle)`. The rest of the elements in the range\n// `[middle, end(range))` are placed in an unspecified order.\n//\n// Returns: `end(range)`.\n//\n// Complexity: Approximately `size(range) * log(middle - begin(range))`\n// comparisons, and twice as many projections.\n//\n// Reference: https://wg21.link/partial.sort#:~:text=ranges::partial_sort(R\ntemplate <typename Range,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>,\n          typename = indirect_result_t<Comp&,\n                                       projected<iterator_t<Range>, Proj>,\n                                       projected<iterator_t<Range>, Proj>>>\nconstexpr auto partial_sort(Range&& range,\n                            iterator_t<Range> middle,\n                            Comp comp = {},\n                            Proj proj = {}) {\n  return ranges::partial_sort(ranges::begin(range), middle, ranges::end(range),\n                              std::move(comp), std::move(proj));\n}\n\n// [partial.sort.copy] partial_sort_copy\n// Reference: https://wg21.link/partial.sort.copy\n\n// Let `N` be `min(last - first, result_last - result_first)`.\n//\n// Preconditions: For iterators `a1` and `b1` in `[first, last)`, and iterators\n// `x2` and `y2` in `[result_first, result_last)`, after evaluating the\n// assignment `*y2 = *b1`, let `E` be the value of `bool(invoke(comp,\n// invoke(proj1, *a1), invoke(proj2, *y2)))`. Then, after evaluating the\n// assignment `*x2 = *a1`, `E` is equal to `bool(invoke(comp, invoke(proj2,\n// *x2), invoke(proj2, *y2)))`.\n//\n// Effects: Places the first `N` elements as sorted with respect to `comp` and\n// `proj2` into the range `[result_first, result_first + N)`.\n//\n// Returns: `result_first + N`.\n//\n// Complexity: Approximately `(last - first) * log N` comparisons, and twice as\n// many projections.\n//\n// Reference:\n// https://wg21.link/partial.sort.copy#:~:text=ranges::partial_sort_copy(I1\ntemplate <typename InputIterator,\n          typename RandomAccessIterator,\n          typename Comp = ranges::less,\n          typename Proj1 = identity,\n          typename Proj2 = identity,\n          typename = internal::iterator_category_t<InputIterator>,\n          typename = internal::iterator_category_t<RandomAccessIterator>,\n          typename = indirect_result_t<Comp&,\n                                       projected<InputIterator, Proj1>,\n                                       projected<RandomAccessIterator, Proj2>>,\n          typename = indirect_result_t<Comp&,\n                                       projected<RandomAccessIterator, Proj2>,\n                                       projected<InputIterator, Proj1>>>\nconstexpr auto partial_sort_copy(InputIterator first,\n                                 InputIterator last,\n                                 RandomAccessIterator result_first,\n                                 RandomAccessIterator result_last,\n                                 Comp comp = {},\n                                 Proj1 proj1 = {},\n                                 Proj2 proj2 = {}) {\n  // Needs to opt-in to all permutations, since std::partial_sort_copy expects\n  // comp(proj2(lhs), proj1(rhs)) to compile.\n  return std::partial_sort_copy(\n      first, last, result_first, result_last,\n      internal::PermutedProjectedBinaryPredicate(comp, proj1, proj2));\n}\n\n// Let `N` be `min(size(range), size(result_range))`.\n//\n// Preconditions: For iterators `a1` and `b1` in `range`, and iterators\n// `x2` and `y2` in `result_range`, after evaluating the assignment\n// `*y2 = *b1`, let `E` be the value of\n// `bool(invoke(comp, invoke(proj1, *a1), invoke(proj2, *y2)))`. Then, after\n// evaluating the assignment `*x2 = *a1`, `E` is equal to\n// `bool(invoke(comp, invoke(proj2, *x2), invoke(proj2, *y2)))`.\n//\n// Effects: Places the first `N` elements as sorted with respect to `comp` and\n// `proj2` into the range `[begin(result_range), begin(result_range) + N)`.\n//\n// Returns: `begin(result_range) + N`.\n//\n// Complexity: Approximately `size(range) * log N` comparisons, and twice as\n// many projections.\n//\n// Reference:\n// https://wg21.link/partial.sort.copy#:~:text=ranges::partial_sort_copy(R1\ntemplate <typename Range1,\n          typename Range2,\n          typename Comp = ranges::less,\n          typename Proj1 = identity,\n          typename Proj2 = identity,\n          typename = internal::range_category_t<Range1>,\n          typename = internal::range_category_t<Range2>,\n          typename = indirect_result_t<Comp&,\n                                       projected<iterator_t<Range1>, Proj1>,\n                                       projected<iterator_t<Range2>, Proj2>>,\n          typename = indirect_result_t<Comp&,\n                                       projected<iterator_t<Range2>, Proj2>,\n                                       projected<iterator_t<Range1>, Proj1>>>\nconstexpr auto partial_sort_copy(Range1&& range,\n                                 Range2&& result_range,\n                                 Comp comp = {},\n                                 Proj1 proj1 = {},\n                                 Proj2 proj2 = {}) {\n  return ranges::partial_sort_copy(ranges::begin(range), ranges::end(range),\n                                   ranges::begin(result_range),\n                                   ranges::end(result_range), std::move(comp),\n                                   std::move(proj1), std::move(proj2));\n}\n\n// [is.sorted] is_sorted\n// Reference: https://wg21.link/is.sorted\n\n// Returns: The last iterator `i` in `[first, last]` for which the range\n// `[first, i)` is sorted with respect to `comp` and `proj`.\n//\n// Complexity: Linear.\n//\n// Reference: https://wg21.link/is.sorted#:~:text=ranges::is_sorted_until(I\ntemplate <typename ForwardIterator,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<ForwardIterator>,\n          typename = indirect_result_t<Comp&,\n                                       projected<ForwardIterator, Proj>,\n                                       projected<ForwardIterator, Proj>>>\nconstexpr auto is_sorted_until(ForwardIterator first,\n                               ForwardIterator last,\n                               Comp comp = {},\n                               Proj proj = {}) {\n  // Implementation inspired by cppreference.com:\n  // https://en.cppreference.com/w/cpp/algorithm/is_sorted_until\n  //\n  // A reimplementation is required, because std::is_sorted_until is not\n  // constexpr prior to C++20. Once we have C++20, we should switch to standard\n  // library implementation.\n  if (first == last)\n    return last;\n\n  for (ForwardIterator next = first; ++next != last; ++first) {\n    if (base::invoke(comp, base::invoke(proj, *next),\n                     base::invoke(proj, *first))) {\n      return next;\n    }\n  }\n\n  return last;\n}\n\n// Returns: The last iterator `i` in `[begin(range), end(range)]` for which the\n// range `[begin(range), i)` is sorted with respect to `comp` and `proj`.\n//\n// Complexity: Linear.\n//\n// Reference: https://wg21.link/is.sorted#:~:text=ranges::is_sorted_until(R\ntemplate <typename Range,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>,\n          typename = indirect_result_t<Comp&,\n                                       projected<iterator_t<Range>, Proj>,\n                                       projected<iterator_t<Range>, Proj>>>\nconstexpr auto is_sorted_until(Range&& range, Comp comp = {}, Proj proj = {}) {\n  return ranges::is_sorted_until(ranges::begin(range), ranges::end(range),\n                                 std::move(comp), std::move(proj));\n}\n\n// Returns: Whether the range `[first, last)` is sorted with respect to `comp`\n// and `proj`.\n//\n// Complexity: Linear.\n//\n// Reference: https://wg21.link/is.sorted#:~:text=ranges::is_sorted(I\ntemplate <typename ForwardIterator,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<ForwardIterator>,\n          typename = indirect_result_t<Comp&,\n                                       projected<ForwardIterator, Proj>,\n                                       projected<ForwardIterator, Proj>>>\nconstexpr auto is_sorted(ForwardIterator first,\n                         ForwardIterator last,\n                         Comp comp = {},\n                         Proj proj = {}) {\n  return ranges::is_sorted_until(first, last, std::move(comp),\n                                 std::move(proj)) == last;\n}\n\n// Returns: Whether `range` is sorted with respect to `comp` and `proj`.\n//\n// Complexity: Linear.\n//\n// Reference: https://wg21.link/is.sorted#:~:text=ranges::is_sorted(R\ntemplate <typename Range,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>,\n          typename = indirect_result_t<Comp&,\n                                       projected<iterator_t<Range>, Proj>,\n                                       projected<iterator_t<Range>, Proj>>>\nconstexpr auto is_sorted(Range&& range, Comp comp = {}, Proj proj = {}) {\n  return ranges::is_sorted(ranges::begin(range), ranges::end(range),\n                           std::move(comp), std::move(proj));\n}\n\n// [alg.nth.element] Nth element\n// Reference: https://wg21.link/alg.nth.element\n\n// Preconditions: `[first, nth)` and `[nth, last)` are valid ranges.\n//\n// Effects: After `nth_element` the element in the position pointed to by `nth`\n// is the element that would be in that position if the whole range were sorted\n// with respect to `comp` and `proj`, unless `nth == last`. Also for every\n// iterator `i` in the range `[first, nth)` and every iterator `j` in the range\n// `[nth, last)` it holds that:\n// `bool(invoke(comp, invoke(proj, *j), invoke(proj, *i)))` is false.\n//\n// Returns: `last`.\n//\n// Complexity: Linear on average.\n//\n// Reference: https://wg21.link/alg.nth.element#:~:text=ranges::nth_element(I\ntemplate <typename RandomAccessIterator,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<RandomAccessIterator>,\n          typename = indirect_result_t<Comp&,\n                                       projected<RandomAccessIterator, Proj>,\n                                       projected<RandomAccessIterator, Proj>>>\nconstexpr auto nth_element(RandomAccessIterator first,\n                           RandomAccessIterator nth,\n                           RandomAccessIterator last,\n                           Comp comp = {},\n                           Proj proj = {}) {\n  std::nth_element(first, nth, last,\n                   internal::ProjectedBinaryPredicate(comp, proj, proj));\n  return last;\n}\n\n// Preconditions: `[begin(range), nth)` and `[nth, end(range))` are valid\n// ranges.\n//\n// Effects: After `nth_element` the element in the position pointed to by `nth`\n// is the element that would be in that position if the whole range were sorted\n// with respect to `comp` and `proj`, unless `nth == end(range)`. Also for every\n// iterator `i` in the range `[begin(range), nth)` and every iterator `j` in the\n// range `[nth, end(range))` it holds that:\n// `bool(invoke(comp, invoke(proj, *j), invoke(proj, *i)))` is false.\n//\n// Returns: `end(range)`.\n//\n// Complexity: Linear on average.\n//\n// Reference: https://wg21.link/alg.nth.element#:~:text=ranges::nth_element(R\ntemplate <typename Range,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>,\n          typename = indirect_result_t<Comp&,\n                                       projected<iterator_t<Range>, Proj>,\n                                       projected<iterator_t<Range>, Proj>>>\nconstexpr auto nth_element(Range&& range,\n                           iterator_t<Range> nth,\n                           Comp comp = {},\n                           Proj proj = {}) {\n  return ranges::nth_element(ranges::begin(range), nth, ranges::end(range),\n                             std::move(comp), std::move(proj));\n}\n\n// [alg.binary.search] Binary search\n// Reference: https://wg21.link/alg.binary.search\n\n// [lower.bound] lower_bound\n// Reference: https://wg21.link/lower.bound\n\n// Preconditions: The elements `e` of `[first, last)` are partitioned with\n// respect to the expression `bool(invoke(comp, invoke(proj, e), value))`.\n//\n// Returns: The furthermost iterator `i` in the range `[first, last]` such that\n// for every iterator `j` in the range `[first, i)`,\n// `bool(invoke(comp, invoke(proj, *j), value))` is true.\n//\n// Complexity: At most `log₂(last - first) + O(1)` comparisons and projections.\n//\n// Reference: https://wg21.link/lower.bound#:~:text=ranges::lower_bound(I\ntemplate <typename ForwardIterator,\n          typename T,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<ForwardIterator>>\nconstexpr auto lower_bound(ForwardIterator first,\n                           ForwardIterator last,\n                           const T& value,\n                           Comp comp = {},\n                           Proj proj = {}) {\n  // The second arg is guaranteed to be `value`, so we'll simply apply the\n  // identity projection.\n  identity value_proj;\n  return std::lower_bound(\n      first, last, value,\n      internal::ProjectedBinaryPredicate(comp, proj, value_proj));\n}\n\n// Preconditions: The elements `e` of `range` are partitioned with respect to\n// the expression `bool(invoke(comp, invoke(proj, e), value))`.\n//\n// Returns: The furthermost iterator `i` in the range\n// `[begin(range), end(range)]` such that for every iterator `j` in the range\n// `[begin(range), i)`, `bool(invoke(comp, invoke(proj, *j), value))` is true.\n//\n// Complexity: At most `log₂(size(range)) + O(1)` comparisons and projections.\n//\n// Reference: https://wg21.link/lower.bound#:~:text=ranges::lower_bound(R\ntemplate <typename Range,\n          typename T,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>>\nconstexpr auto lower_bound(Range&& range,\n                           const T& value,\n                           Comp comp = {},\n                           Proj proj = {}) {\n  return ranges::lower_bound(ranges::begin(range), ranges::end(range), value,\n                             std::move(comp), std::move(proj));\n}\n\n// [upper.bound] upper_bound\n// Reference: https://wg21.link/upper.bound\n\n// Preconditions: The elements `e` of `[first, last)` are partitioned with\n// respect to the expression `!bool(invoke(comp, value, invoke(proj, e)))`.\n//\n// Returns: The furthermost iterator `i` in the range `[first, last]` such that\n// for every iterator `j` in the range `[first, i)`,\n// `!bool(invoke(comp, value, invoke(proj, *j)))` is true.\n//\n// Complexity: At most `log₂(last - first) + O(1)` comparisons and projections.\n//\n// Reference: https://wg21.link/upper.bound#:~:text=ranges::upper_bound(I\ntemplate <typename ForwardIterator,\n          typename T,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<ForwardIterator>>\nconstexpr auto upper_bound(ForwardIterator first,\n                           ForwardIterator last,\n                           const T& value,\n                           Comp comp = {},\n                           Proj proj = {}) {\n  // The first arg is guaranteed to be `value`, so we'll simply apply the\n  // identity projection.\n  identity value_proj;\n  return std::upper_bound(\n      first, last, value,\n      internal::ProjectedBinaryPredicate(comp, value_proj, proj));\n}\n\n// Preconditions: The elements `e` of `range` are partitioned with\n// respect to the expression `!bool(invoke(comp, value, invoke(proj, e)))`.\n//\n// Returns: The furthermost iterator `i` in the range\n// `[begin(range), end(range)]` such that for every iterator `j` in the range\n// `[begin(range), i)`, `!bool(invoke(comp, value, invoke(proj, *j)))` is true.\n//\n// Complexity: At most `log₂(size(range)) + O(1)` comparisons and projections.\n//\n// Reference: https://wg21.link/upper.bound#:~:text=ranges::upper_bound(R\ntemplate <typename Range,\n          typename T,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>>\nconstexpr auto upper_bound(Range&& range,\n                           const T& value,\n                           Comp comp = {},\n                           Proj proj = {}) {\n  return ranges::upper_bound(ranges::begin(range), ranges::end(range), value,\n                             std::move(comp), std::move(proj));\n}\n\n// [equal.range] equal_range\n// Reference: https://wg21.link/equal.range\n\n// Preconditions: The elements `e` of `[first, last)` are partitioned with\n// respect to the expressions `bool(invoke(comp, invoke(proj, e), value))` and\n// `!bool(invoke(comp, value, invoke(proj, e)))`.\n//\n// Returns: `{ranges::lower_bound(first, last, value, comp, proj),\n//            ranges::upper_bound(first, last, value, comp, proj)}`.\n//\n// Complexity: At most 2 ∗ log₂(last - first) + O(1) comparisons and\n// projections.\n//\n// Reference: https://wg21.link/equal.range#:~:text=ranges::equal_range(I\ntemplate <typename ForwardIterator,\n          typename T,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<ForwardIterator>>\nconstexpr auto equal_range(ForwardIterator first,\n                           ForwardIterator last,\n                           const T& value,\n                           Comp comp = {},\n                           Proj proj = {}) {\n  // Note: This does not dispatch to std::equal_range, as otherwise it would not\n  // be possible to prevent applying `proj` to `value`, which can result in\n  // unintended behavior.\n  return std::make_pair(ranges::lower_bound(first, last, value, comp, proj),\n                        ranges::upper_bound(first, last, value, comp, proj));\n}\n\n// Preconditions: The elements `e` of `range` are partitioned with\n// respect to the expressions `bool(invoke(comp, invoke(proj, e), value))` and\n// `!bool(invoke(comp, value, invoke(proj, e)))`.\n//\n// Returns: `{ranges::lower_bound(range, value, comp, proj),\n//            ranges::upper_bound(range, value, comp, proj)}`.\n//\n// Complexity: At most 2 ∗ log₂(size(range)) + O(1) comparisons and\n// projections.\n//\n// Reference: https://wg21.link/equal.range#:~:text=ranges::equal_range(R\ntemplate <typename Range,\n          typename T,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>>\nconstexpr auto equal_range(Range&& range,\n                           const T& value,\n                           Comp comp = {},\n                           Proj proj = {}) {\n  return ranges::equal_range(ranges::begin(range), ranges::end(range), value,\n                             std::move(comp), std::move(proj));\n}\n\n// [binary.search] binary_search\n// Reference: https://wg21.link/binary.search\n\n// Preconditions: The elements `e` of `[first, last)` are partitioned with\n// respect to the expressions `bool(invoke(comp, invoke(proj, e), value))` and\n// `!bool(invoke(comp, value, invoke(proj, e)))`.\n//\n// Returns: `true` if and only if for some iterator `i` in the range\n// `[first, last)`, `!bool(invoke(comp, invoke(proj, *i), value)) &&\n//                   !bool(invoke(comp, value, invoke(proj, *i)))` is true.\n//\n// Complexity: At most `log₂(last - first) + O(1)` comparisons and projections.\n//\n// Reference: https://wg21.link/binary.search#:~:text=ranges::binary_search(I\ntemplate <typename ForwardIterator,\n          typename T,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<ForwardIterator>>\nconstexpr auto binary_search(ForwardIterator first,\n                             ForwardIterator last,\n                             const T& value,\n                             Comp comp = {},\n                             Proj proj = {}) {\n  first = ranges::lower_bound(first, last, value, comp, proj);\n  return first != last &&\n         !base::invoke(comp, value, base::invoke(proj, *first));\n}\n\n// Preconditions: The elements `e` of `range` are partitioned with\n// respect to the expressions `bool(invoke(comp, invoke(proj, e), value))` and\n// `!bool(invoke(comp, value, invoke(proj, e)))`.\n//\n// Returns: `true` if and only if for some iterator `i` in `range`\n// `!bool(invoke(comp, invoke(proj, *i), value)) &&\n//  !bool(invoke(comp, value, invoke(proj, *i)))` is true.\n//\n// Complexity: At most `log₂(size(range)) + O(1)` comparisons and projections.\n//\n// Reference: https://wg21.link/binary.search#:~:text=ranges::binary_search(R\ntemplate <typename Range,\n          typename T,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>>\nconstexpr auto binary_search(Range&& range,\n                             const T& value,\n                             Comp comp = {},\n                             Proj proj = {}) {\n  return ranges::binary_search(ranges::begin(range), ranges::end(range), value,\n                               std::move(comp), std::move(proj));\n}\n\n// [alg.partitions] Partitions\n// Reference: https://wg21.link/alg.partitions\n\n// Returns: `true` if and only if the elements `e` of `[first, last)` are\n// partitioned with respect to the expression\n// `bool(invoke(pred, invoke(proj, e)))`.\n//\n// Complexity: Linear. At most `last - first` applications of `pred` and `proj`.\n//\n// Reference: https://wg21.link/alg.partitions#:~:text=ranges::is_partitioned(I\ntemplate <typename ForwardIterator,\n          typename Pred,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<ForwardIterator>>\nconstexpr auto is_partitioned(ForwardIterator first,\n                              ForwardIterator last,\n                              Pred pred,\n                              Proj proj = {}) {\n  return std::is_partitioned(first, last,\n                             internal::ProjectedUnaryPredicate(pred, proj));\n}\n\n// Returns: `true` if and only if the elements `e` of `range` are partitioned\n// with respect to the expression `bool(invoke(pred, invoke(proj, e)))`.\n//\n// Complexity: Linear. At most `size(range)` applications of `pred` and `proj`.\n//\n// Reference: https://wg21.link/alg.partitions#:~:text=ranges::is_partitioned(R\ntemplate <typename Range,\n          typename Pred,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>>\nconstexpr auto is_partitioned(Range&& range, Pred pred, Proj proj = {}) {\n  return ranges::is_partitioned(ranges::begin(range), ranges::end(range),\n                                std::move(pred), std::move(proj));\n}\n\n// Let `E(x)` be `bool(invoke(pred, invoke(proj, x)))`.\n//\n// Effects: Places all the elements `e` in `[first, last)` that satisfy `E(e)`\n// before all the elements that do not.\n//\n// Returns: Let `i` be an iterator such that `E(*j)` is `true` for every\n// iterator `j` in `[first, i)` and `false` for every iterator `j` in\n// `[i, last)`. Returns: i.\n//\n// Complexity: Let `N = last - first`:\n// Exactly `N` applications of the predicate and projection. At most `N / 2`\n// swaps if the type of `first` models `bidirectional_iterator`, and at most `N`\n// swaps otherwise.\n//\n// Reference: https://wg21.link/alg.partitions#:~:text=ranges::partition(I\ntemplate <typename ForwardIterator,\n          typename Pred,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<ForwardIterator>>\nconstexpr auto partition(ForwardIterator first,\n                         ForwardIterator last,\n                         Pred pred,\n                         Proj proj = {}) {\n  return std::partition(first, last,\n                        internal::ProjectedUnaryPredicate(pred, proj));\n}\n\n// Let `E(x)` be `bool(invoke(pred, invoke(proj, x)))`.\n//\n// Effects: Places all the elements `e` in `range` that satisfy `E(e)` before\n// all the elements that do not.\n//\n// Returns: Let `i` be an iterator such that `E(*j)` is `true` for every\n// iterator `j` in `[begin(range), i)` and `false` for every iterator `j` in\n// `[i, last)`. Returns: i.\n//\n// Complexity: Let `N = size(range)`:\n// Exactly `N` applications of the predicate and projection. At most `N / 2`\n// swaps if the type of `first` models `bidirectional_iterator`, and at most `N`\n// swaps otherwise.\n//\n// Reference: https://wg21.link/alg.partitions#:~:text=ranges::partition(R\ntemplate <typename Range,\n          typename Pred,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>>\nconstexpr auto partition(Range&& range, Pred pred, Proj proj = {}) {\n  return ranges::partition(ranges::begin(range), ranges::end(range),\n                           std::move(pred), std::move(proj));\n}\n\n// Let `E(x)` be `bool(invoke(pred, invoke(proj, x)))`.\n//\n// Effects: Places all the elements `e` in `[first, last)` that satisfy `E(e)`\n// before all the elements that do not. The relative order of the elements in\n// both groups is preserved.\n//\n// Returns: Let `i` be an iterator such that for every iterator `j` in\n// `[first, i)`, `E(*j)` is `true`, and for every iterator `j` in the range\n// `[i, last)`, `E(*j)` is `false`. Returns: `i`.\n//\n// Complexity: Let `N = last - first`:\n// At most `N log N` swaps, but only `O(N)` swaps if there is enough extra\n// memory. Exactly `N` applications of the predicate and projection.\n//\n// Reference:\n// https://wg21.link/alg.partitions#:~:text=ranges::stable_partition(I\ntemplate <typename BidirectionalIterator,\n          typename Pred,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<BidirectionalIterator>>\nconstexpr auto stable_partition(BidirectionalIterator first,\n                                BidirectionalIterator last,\n                                Pred pred,\n                                Proj proj = {}) {\n  return std::stable_partition(first, last,\n                               internal::ProjectedUnaryPredicate(pred, proj));\n}\n\n// Let `E(x)` be `bool(invoke(pred, invoke(proj, x)))`.\n//\n// Effects: Places all the elements `e` in `range` that satisfy `E(e)` before\n// all the elements that do not. The relative order of the elements in both\n// groups is preserved.\n//\n// Returns: Let `i` be an iterator such that for every iterator `j` in\n// `[begin(range), i)`, `E(*j)` is `true`, and for every iterator `j` in the\n// range `[i, end(range))`, `E(*j)` is `false`. Returns: `i`.\n//\n// Complexity: Let `N = size(range)`:\n// At most `N log N` swaps, but only `O(N)` swaps if there is enough extra\n// memory. Exactly `N` applications of the predicate and projection.\n//\n// Reference:\n// https://wg21.link/alg.partitions#:~:text=ranges::stable_partition(R\ntemplate <typename Range,\n          typename Pred,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>>\nconstexpr auto stable_partition(Range&& range, Pred pred, Proj proj = {}) {\n  return ranges::stable_partition(ranges::begin(range), ranges::end(range),\n                                  std::move(pred), std::move(proj));\n}\n\n// Let `E(x)` be `bool(invoke(pred, invoke(proj, x)))`.\n//\n// Mandates: The expression `*first` is writable to `out_true` and `out_false`.\n//\n// Preconditions: The input range and output ranges do not overlap.\n//\n// Effects: For each iterator `i` in `[first, last)`, copies `*i` to the output\n// range beginning with `out_true` if `E(*i)` is `true`, or to the output range\n// beginning with `out_false` otherwise.\n//\n// Returns: Let `o1` be the end of the output range beginning at `out_true`, and\n// `o2` the end of the output range beginning at `out_false`.\n// Returns `{o1, o2}`.\n//\n// Complexity: Exactly `last - first` applications of `pred` and `proj`.\n//\n// Reference: https://wg21.link/alg.partitions#:~:text=ranges::partition_copy(I\ntemplate <typename InputIterator,\n          typename OutputIterator1,\n          typename OutputIterator2,\n          typename Pred,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<InputIterator>,\n          typename = internal::iterator_category_t<OutputIterator1>,\n          typename = internal::iterator_category_t<OutputIterator2>>\nconstexpr auto partition_copy(InputIterator first,\n                              InputIterator last,\n                              OutputIterator1 out_true,\n                              OutputIterator2 out_false,\n                              Pred pred,\n                              Proj proj = {}) {\n  return std::partition_copy(first, last, out_true, out_false,\n                             internal::ProjectedUnaryPredicate(pred, proj));\n}\n\n// Let `E(x)` be `bool(invoke(pred, invoke(proj, x)))`.\n//\n// Mandates: The expression `*begin(range)` is writable to `out_true` and\n// `out_false`.\n//\n// Preconditions: The input range and output ranges do not overlap.\n//\n// Effects: For each iterator `i` in `range`, copies `*i` to the output range\n// beginning with `out_true` if `E(*i)` is `true`, or to the output range\n// beginning with `out_false` otherwise.\n//\n// Returns: Let `o1` be the end of the output range beginning at `out_true`, and\n// `o2` the end of the output range beginning at `out_false`.\n// Returns `{o1, o2}`.\n//\n// Complexity: Exactly `size(range)` applications of `pred` and `proj`.\n//\n// Reference: https://wg21.link/alg.partitions#:~:text=ranges::partition_copy(R\ntemplate <typename Range,\n          typename OutputIterator1,\n          typename OutputIterator2,\n          typename Pred,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>,\n          typename = internal::iterator_category_t<OutputIterator1>,\n          typename = internal::iterator_category_t<OutputIterator2>>\nconstexpr auto partition_copy(Range&& range,\n                              OutputIterator1 out_true,\n                              OutputIterator2 out_false,\n                              Pred pred,\n                              Proj proj = {}) {\n  return ranges::partition_copy(ranges::begin(range), ranges::end(range),\n                                out_true, out_false, std::move(pred),\n                                std::move(proj));\n}\n\n// let `E(x)` be `bool(invoke(pred, invoke(proj, x)))`.\n//\n// Preconditions: The elements `e` of `[first, last)` are partitioned with\n// respect to `E(e)`.\n//\n// Returns: An iterator `mid` such that `E(*i)` is `true` for all iterators `i`\n// in `[first, mid)`, and `false` for all iterators `i` in `[mid, last)`.\n//\n// Complexity: `O(log(last - first))` applications of `pred` and `proj`.\n//\n// Reference: https://wg21.link/alg.partitions#:~:text=ranges::partition_point(I\ntemplate <typename ForwardIterator,\n          typename Pred,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<ForwardIterator>>\nconstexpr auto partition_point(ForwardIterator first,\n                               ForwardIterator last,\n                               Pred pred,\n                               Proj proj = {}) {\n  return std::partition_point(first, last,\n                              internal::ProjectedUnaryPredicate(pred, proj));\n}\n\n// let `E(x)` be `bool(invoke(pred, invoke(proj, x)))`.\n//\n// Preconditions: The elements `e` of `range` are partitioned with respect to\n// `E(e)`.\n//\n// Returns: An iterator `mid` such that `E(*i)` is `true` for all iterators `i`\n// in `[begin(range), mid)`, and `false` for all iterators `i` in\n// `[mid, end(range))`.\n//\n// Complexity: `O(log(size(range)))` applications of `pred` and `proj`.\n//\n// Reference: https://wg21.link/alg.partitions#:~:text=ranges::partition_point(R\ntemplate <typename Range,\n          typename Pred,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>>\nconstexpr auto partition_point(Range&& range, Pred pred, Proj proj = {}) {\n  return ranges::partition_point(ranges::begin(range), ranges::end(range),\n                                 std::move(pred), std::move(proj));\n}\n\n// [alg.merge] Merge\n// Reference: https://wg21.link/alg.merge\n\n// Let `N` be `(last1 - first1) + (last2 - first2)`.\n//\n// Preconditions: The ranges `[first1, last1)` and `[first2, last2)` are sorted\n// with respect to `comp` and `proj1` or `proj2`, respectively. The resulting\n// range does not overlap with either of the original ranges.\n//\n// Effects: Copies all the elements of the two ranges `[first1, last1)` and\n// `[first2, last2)` into the range `[result, result_last)`, where `result_last`\n// is `result + N`. If an element `a` precedes `b` in an input range, `a` is\n// copied into the output range before `b`. If `e1` is an element of\n// `[first1, last1)` and `e2` of `[first2, last2)`, `e2` is copied into the\n// output range before `e1` if and only if\n// `bool(invoke(comp, invoke(proj2, e2), invoke(proj1, e1)))` is `true`.\n//\n// Returns: `result_last`.\n//\n// Complexity: At most `N - 1` comparisons and applications of each projection.\n//\n// Remarks: Stable.\n//\n// Reference: https://wg21.link/alg.merge#:~:text=ranges::merge(I1\ntemplate <typename InputIterator1,\n          typename InputIterator2,\n          typename OutputIterator,\n          typename Comp = ranges::less,\n          typename Proj1 = identity,\n          typename Proj2 = identity,\n          typename = internal::iterator_category_t<InputIterator1>,\n          typename = internal::iterator_category_t<InputIterator2>,\n          typename = internal::iterator_category_t<OutputIterator>,\n          typename = indirect_result_t<Comp&,\n                                       projected<InputIterator1, Proj1>,\n                                       projected<InputIterator2, Proj2>>,\n          typename = indirect_result_t<Comp&,\n                                       projected<InputIterator2, Proj2>,\n                                       projected<InputIterator1, Proj1>>>\nconstexpr auto merge(InputIterator1 first1,\n                     InputIterator1 last1,\n                     InputIterator2 first2,\n                     InputIterator2 last2,\n                     OutputIterator result,\n                     Comp comp = {},\n                     Proj1 proj1 = {},\n                     Proj2 proj2 = {}) {\n  // Needs to opt-in to all permutations, since std::merge expects\n  // comp(proj2(lhs), proj1(rhs)) to compile.\n  return std::merge(\n      first1, last1, first2, last2, result,\n      internal::PermutedProjectedBinaryPredicate(comp, proj1, proj2));\n}\n\n// Let `N` be `size(range1) + size(range2)`.\n//\n// Preconditions: The ranges `range1` and `range2` are sorted with respect to\n// `comp` and `proj1` or `proj2`, respectively. The resulting range does not\n// overlap with either of the original ranges.\n//\n// Effects: Copies all the elements of the two ranges `range1` and `range2` into\n// the range `[result, result_last)`, where `result_last` is `result + N`. If an\n// element `a` precedes `b` in an input range, `a` is copied into the output\n// range before `b`. If `e1` is an element of `range1` and `e2` of `range2`,\n// `e2` is copied into the output range before `e1` if and only if\n// `bool(invoke(comp, invoke(proj2, e2), invoke(proj1, e1)))` is `true`.\n//\n// Returns: `result_last`.\n//\n// Complexity: At most `N - 1` comparisons and applications of each projection.\n//\n// Remarks: Stable.\n//\n// Reference: https://wg21.link/alg.merge#:~:text=ranges::merge(R1\ntemplate <typename Range1,\n          typename Range2,\n          typename OutputIterator,\n          typename Comp = ranges::less,\n          typename Proj1 = identity,\n          typename Proj2 = identity,\n          typename = internal::range_category_t<Range1>,\n          typename = internal::range_category_t<Range2>,\n          typename = internal::iterator_category_t<OutputIterator>,\n          typename = indirect_result_t<Comp&,\n                                       projected<iterator_t<Range1>, Proj1>,\n                                       projected<iterator_t<Range2>, Proj2>>,\n          typename = indirect_result_t<Comp&,\n                                       projected<iterator_t<Range2>, Proj2>,\n                                       projected<iterator_t<Range1>, Proj1>>>\nconstexpr auto merge(Range1&& range1,\n                     Range2&& range2,\n                     OutputIterator result,\n                     Comp comp = {},\n                     Proj1 proj1 = {},\n                     Proj2 proj2 = {}) {\n  return ranges::merge(ranges::begin(range1), ranges::end(range1),\n                       ranges::begin(range2), ranges::end(range2), result,\n                       std::move(comp), std::move(proj1), std::move(proj2));\n}\n\n// Preconditions: `[first, middle)` and `[middle, last)` are valid ranges sorted\n// with respect to `comp` and `proj`.\n//\n// Effects: Merges two sorted consecutive ranges `[first, middle)` and\n// `[middle, last)`, putting the result of the merge into the range\n// `[first, last)`. The resulting range is sorted with respect to `comp` and\n// `proj`.\n//\n// Returns: `last`.\n//\n// Complexity: Let `N = last - first`: If enough additional memory is available,\n// exactly `N - 1` comparisons. Otherwise, `O(N log N)` comparisons. In either\n// case, twice as many projections as comparisons.\n//\n// Remarks: Stable.\n//\n// Reference: https://wg21.link/alg.merge#:~:text=ranges::inplace_merge(I\ntemplate <typename BidirectionalIterator,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<BidirectionalIterator>>\nconstexpr auto inplace_merge(BidirectionalIterator first,\n                             BidirectionalIterator middle,\n                             BidirectionalIterator last,\n                             Comp comp = {},\n                             Proj proj = {}) {\n  std::inplace_merge(first, middle, last,\n                     internal::ProjectedBinaryPredicate(comp, proj, proj));\n  return last;\n}\n\n// Preconditions: `[begin(range), middle)` and `[middle, end(range))` are valid\n// ranges sorted with respect to `comp` and `proj`.\n//\n// Effects: Merges two sorted consecutive ranges `[begin(range), middle)` and\n// `[middle, end(range))`, putting the result of the merge into `range`. The\n// resulting range is sorted with respect to `comp` and `proj`.\n//\n// Returns: `end(range)`.\n//\n// Complexity: Let `N = size(range)`: If enough additional memory is available,\n// exactly `N - 1` comparisons. Otherwise, `O(N log N)` comparisons. In either\n// case, twice as many projections as comparisons.\n//\n// Remarks: Stable.\n//\n// Reference: https://wg21.link/alg.merge#:~:text=ranges::inplace_merge(R\ntemplate <typename Range,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>>\nconstexpr auto inplace_merge(Range&& range,\n                             iterator_t<Range> middle,\n                             Comp comp = {},\n                             Proj proj = {}) {\n  return ranges::inplace_merge(ranges::begin(range), middle, ranges::end(range),\n                               std::move(comp), std::move(proj));\n}\n\n// [alg.set.operations] Set operations on sorted structures\n// Reference: https://wg21.link/alg.set.operations\n\n// [includes] includes\n// Reference: https://wg21.link/includes\n\n// Preconditions: The ranges `[first1, last1)` and `[first2, last2)` are sorted\n// with respect to `comp` and `proj1` or `proj2`, respectively.\n//\n// Returns: `true` if and only if `[first2, last2)` is a subsequence of\n// `[first1, last1)`.\n//\n// Complexity: At most `2 * ((last1 - first1) + (last2 - first2)) - 1`\n// comparisons and applications of each projection.\n//\n// Reference: https://wg21.link/includes#:~:text=ranges::includes(I1\ntemplate <typename InputIterator1,\n          typename InputIterator2,\n          typename Comp = ranges::less,\n          typename Proj1 = identity,\n          typename Proj2 = identity,\n          typename = internal::iterator_category_t<InputIterator1>,\n          typename = internal::iterator_category_t<InputIterator2>,\n          typename = indirect_result_t<Comp&,\n                                       projected<InputIterator1, Proj1>,\n                                       projected<InputIterator2, Proj2>>,\n          typename = indirect_result_t<Comp&,\n                                       projected<InputIterator2, Proj2>,\n                                       projected<InputIterator1, Proj1>>>\nconstexpr auto includes(InputIterator1 first1,\n                        InputIterator1 last1,\n                        InputIterator2 first2,\n                        InputIterator2 last2,\n                        Comp comp = {},\n                        Proj1 proj1 = {},\n                        Proj2 proj2 = {}) {\n  DCHECK(ranges::is_sorted(first1, last1, comp, proj1));\n  DCHECK(ranges::is_sorted(first2, last2, comp, proj2));\n  // Needs to opt-in to all permutations, since std::includes expects\n  // comp(proj1(lhs), proj2(rhs)) and comp(proj2(lhs), proj1(rhs)) to compile.\n  return std::includes(\n      first1, last1, first2, last2,\n      internal::PermutedProjectedBinaryPredicate(comp, proj1, proj2));\n}\n\n// Preconditions: The ranges `range1` and `range2` are sorted with respect to\n// `comp` and `proj1` or `proj2`, respectively.\n//\n// Returns: `true` if and only if `range2` is a subsequence of `range1`.\n//\n// Complexity: At most `2 * (size(range1) + size(range2)) - 1` comparisons and\n// applications of each projection.\n//\n// Reference: https://wg21.link/includes#:~:text=ranges::includes(R1\ntemplate <typename Range1,\n          typename Range2,\n          typename Comp = ranges::less,\n          typename Proj1 = identity,\n          typename Proj2 = identity,\n          typename = internal::range_category_t<Range1>,\n          typename = internal::range_category_t<Range2>,\n          typename = indirect_result_t<Comp&,\n                                       projected<iterator_t<Range1>, Proj1>,\n                                       projected<iterator_t<Range2>, Proj2>>,\n          typename = indirect_result_t<Comp&,\n                                       projected<iterator_t<Range2>, Proj2>,\n                                       projected<iterator_t<Range1>, Proj1>>>\nconstexpr auto includes(Range1&& range1,\n                        Range2&& range2,\n                        Comp comp = {},\n                        Proj1 proj1 = {},\n                        Proj2 proj2 = {}) {\n  return ranges::includes(ranges::begin(range1), ranges::end(range1),\n                          ranges::begin(range2), ranges::end(range2),\n                          std::move(comp), std::move(proj1), std::move(proj2));\n}\n\n// [set.union] set_union\n// Reference: https://wg21.link/set.union\n\n// Preconditions: The ranges `[first1, last1)` and `[first2, last2)` are sorted\n// with respect to `comp` and `proj1` or `proj2`, respectively. The resulting\n// range does not overlap with either of the original ranges.\n//\n// Effects: Constructs a sorted union of the elements from the two ranges; that\n// is, the set of elements that are present in one or both of the ranges.\n//\n// Returns: The end of the constructed range.\n//\n// Complexity: At most `2 * ((last1 - first1) + (last2 - first2)) - 1`\n// comparisons and applications of each projection.\n//\n// Remarks: Stable. If `[first1, last1)` contains `m` elements that are\n// equivalent to each other and `[first2, last2)` contains `n` elements that are\n// equivalent to them, then all `m` elements from the first range are copied to\n// the output range, in order, and then the final `max(n - m , 0)` elements from\n// the second range are copied to the output range, in order.\n//\n// Reference: https://wg21.link/set.union#:~:text=ranges::set_union(I1\ntemplate <typename InputIterator1,\n          typename InputIterator2,\n          typename OutputIterator,\n          typename Comp = ranges::less,\n          typename Proj1 = identity,\n          typename Proj2 = identity,\n          typename = internal::iterator_category_t<InputIterator1>,\n          typename = internal::iterator_category_t<InputIterator2>,\n          typename = internal::iterator_category_t<OutputIterator>,\n          typename = indirect_result_t<Comp&,\n                                       projected<InputIterator1, Proj1>,\n                                       projected<InputIterator2, Proj2>>,\n          typename = indirect_result_t<Comp&,\n                                       projected<InputIterator2, Proj2>,\n                                       projected<InputIterator1, Proj1>>>\nconstexpr auto set_union(InputIterator1 first1,\n                         InputIterator1 last1,\n                         InputIterator2 first2,\n                         InputIterator2 last2,\n                         OutputIterator result,\n                         Comp comp = {},\n                         Proj1 proj1 = {},\n                         Proj2 proj2 = {}) {\n  // Needs to opt-in to all permutations, since std::set_union expects\n  // comp(proj1(lhs), proj2(rhs)) and comp(proj2(lhs), proj1(rhs)) to compile.\n  return std::set_union(\n      first1, last1, first2, last2, result,\n      internal::PermutedProjectedBinaryPredicate(comp, proj1, proj2));\n}\n\n// Preconditions: The ranges `range1` and `range2` are sorted with respect to\n// `comp` and `proj1` or `proj2`, respectively. The resulting range does not\n// overlap with either of the original ranges.\n//\n// Effects: Constructs a sorted union of the elements from the two ranges; that\n// is, the set of elements that are present in one or both of the ranges.\n//\n// Returns: The end of the constructed range.\n//\n// Complexity: At most `2 * (size(range1) + size(range2)) - 1` comparisons and\n// applications of each projection.\n//\n// Remarks: Stable. If `range1` contains `m` elements that are equivalent to\n// each other and `range2` contains `n` elements that are equivalent to them,\n// then all `m` elements from the first range are copied to the output range, in\n// order, and then the final `max(n - m , 0)` elements from the second range are\n// copied to the output range, in order.\n//\n// Reference: https://wg21.link/set.union#:~:text=ranges::set_union(R1\ntemplate <typename Range1,\n          typename Range2,\n          typename OutputIterator,\n          typename Comp = ranges::less,\n          typename Proj1 = identity,\n          typename Proj2 = identity,\n          typename = internal::range_category_t<Range1>,\n          typename = internal::range_category_t<Range2>,\n          typename = internal::iterator_category_t<OutputIterator>,\n          typename = indirect_result_t<Comp&,\n                                       projected<iterator_t<Range1>, Proj1>,\n                                       projected<iterator_t<Range2>, Proj2>>,\n          typename = indirect_result_t<Comp&,\n                                       projected<iterator_t<Range2>, Proj2>,\n                                       projected<iterator_t<Range1>, Proj1>>>\nconstexpr auto set_union(Range1&& range1,\n                         Range2&& range2,\n                         OutputIterator result,\n                         Comp comp = {},\n                         Proj1 proj1 = {},\n                         Proj2 proj2 = {}) {\n  return ranges::set_union(ranges::begin(range1), ranges::end(range1),\n                           ranges::begin(range2), ranges::end(range2), result,\n                           std::move(comp), std::move(proj1), std::move(proj2));\n}\n\n// [set.intersection] set_intersection\n// Reference: https://wg21.link/set.intersection\n\n// Preconditions: The ranges `[first1, last1)` and `[first2, last2)` are sorted\n// with respect to `comp` and `proj1` or `proj2`, respectively. The resulting\n// range does not overlap with either of the original ranges.\n//\n// Effects: Constructs a sorted intersection of the elements from the two\n// ranges; that is, the set of elements that are present in both of the ranges.\n//\n// Returns: The end of the constructed range.\n//\n// Complexity: At most `2 * ((last1 - first1) + (last2 - first2)) - 1`\n// comparisons and applications of each projection.\n//\n// Remarks: Stable. If `[first1, last1)` contains `m` elements that are\n// equivalent to each other and `[first2, last2)` contains `n` elements that are\n// equivalent to them, the first `min(m, n)` elements are copied from the first\n// range to the output range, in order.\n//\n// Reference:\n// https://wg21.link/set.intersection#:~:text=ranges::set_intersection(I1\ntemplate <typename InputIterator1,\n          typename InputIterator2,\n          typename OutputIterator,\n          typename Comp = ranges::less,\n          typename Proj1 = identity,\n          typename Proj2 = identity,\n          typename = internal::iterator_category_t<InputIterator1>,\n          typename = internal::iterator_category_t<InputIterator2>,\n          typename = internal::iterator_category_t<OutputIterator>,\n          typename = indirect_result_t<Comp&,\n                                       projected<InputIterator1, Proj1>,\n                                       projected<InputIterator2, Proj2>>,\n          typename = indirect_result_t<Comp&,\n                                       projected<InputIterator2, Proj2>,\n                                       projected<InputIterator1, Proj1>>>\nconstexpr auto set_intersection(InputIterator1 first1,\n                                InputIterator1 last1,\n                                InputIterator2 first2,\n                                InputIterator2 last2,\n                                OutputIterator result,\n                                Comp comp = {},\n                                Proj1 proj1 = {},\n                                Proj2 proj2 = {}) {\n  // Needs to opt-in to all permutations, since std::set_intersection expects\n  // comp(proj1(lhs), proj2(rhs)) and comp(proj2(lhs), proj1(rhs)) to compile.\n  return std::set_intersection(\n      first1, last1, first2, last2, result,\n      internal::PermutedProjectedBinaryPredicate(comp, proj1, proj2));\n}\n\n// Preconditions: The ranges `range1` and `range2` are sorted with respect to\n// `comp` and `proj1` or `proj2`, respectively. The resulting range does not\n// overlap with either of the original ranges.\n//\n// Effects: Constructs a sorted intersection of the elements from the two\n// ranges; that is, the set of elements that are present in both of the ranges.\n//\n// Returns: The end of the constructed range.\n//\n// Complexity: At most `2 * (size(range1) + size(range2)) - 1` comparisons and\n// applications of each projection.\n//\n// Remarks: Stable. If `range1` contains `m` elements that are equivalent to\n// each other and `range2` contains `n` elements that are equivalent to them,\n// the first `min(m, n)` elements are copied from the first range to the output\n// range, in order.\n//\n// Reference:\n// https://wg21.link/set.intersection#:~:text=ranges::set_intersection(R1\ntemplate <typename Range1,\n          typename Range2,\n          typename OutputIterator,\n          typename Comp = ranges::less,\n          typename Proj1 = identity,\n          typename Proj2 = identity,\n          typename = internal::range_category_t<Range1>,\n          typename = internal::range_category_t<Range2>,\n          typename = internal::iterator_category_t<OutputIterator>,\n          typename = indirect_result_t<Comp&,\n                                       projected<iterator_t<Range1>, Proj1>,\n                                       projected<iterator_t<Range2>, Proj2>>,\n          typename = indirect_result_t<Comp&,\n                                       projected<iterator_t<Range2>, Proj2>,\n                                       projected<iterator_t<Range1>, Proj1>>>\nconstexpr auto set_intersection(Range1&& range1,\n                                Range2&& range2,\n                                OutputIterator result,\n                                Comp comp = {},\n                                Proj1 proj1 = {},\n                                Proj2 proj2 = {}) {\n  return ranges::set_intersection(ranges::begin(range1), ranges::end(range1),\n                                  ranges::begin(range2), ranges::end(range2),\n                                  result, std::move(comp), std::move(proj1),\n                                  std::move(proj2));\n}\n\n// [set.difference] set_difference\n// Reference: https://wg21.link/set.difference\n\n// Preconditions: The ranges `[first1, last1)` and `[first2, last2)` are sorted\n// with respect to `comp` and `proj1` or `proj2`, respectively. The resulting\n// range does not overlap with either of the original ranges.\n//\n// Effects: Copies the elements of the range `[first1, last1)` which are not\n// present in the range `[first2, last2)` to the range beginning at `result`.\n// The elements in the constructed range are sorted.\n//\n// Returns: The end of the constructed range.\n//\n// Complexity: At most `2 * ((last1 - first1) + (last2 - first2)) - 1`\n// comparisons and applications of each projection.\n//\n// Remarks: If `[first1, last1)` contains `m` elements that are equivalent to\n// each other and `[first2, last2)` contains `n` elements that are equivalent to\n// them, the last `max(m - n, 0)` elements from `[first1, last1)` are copied to\n// the output range, in order.\n//\n// Reference:\n// https://wg21.link/set.difference#:~:text=ranges::set_difference(I1\ntemplate <typename InputIterator1,\n          typename InputIterator2,\n          typename OutputIterator,\n          typename Comp = ranges::less,\n          typename Proj1 = identity,\n          typename Proj2 = identity,\n          typename = internal::iterator_category_t<InputIterator1>,\n          typename = internal::iterator_category_t<InputIterator2>,\n          typename = internal::iterator_category_t<OutputIterator>,\n          typename = indirect_result_t<Comp&,\n                                       projected<InputIterator1, Proj1>,\n                                       projected<InputIterator2, Proj2>>,\n          typename = indirect_result_t<Comp&,\n                                       projected<InputIterator2, Proj2>,\n                                       projected<InputIterator1, Proj1>>>\nconstexpr auto set_difference(InputIterator1 first1,\n                              InputIterator1 last1,\n                              InputIterator2 first2,\n                              InputIterator2 last2,\n                              OutputIterator result,\n                              Comp comp = {},\n                              Proj1 proj1 = {},\n                              Proj2 proj2 = {}) {\n  // Needs to opt-in to all permutations, since std::set_difference expects\n  // comp(proj1(lhs), proj2(rhs)) and comp(proj2(lhs), proj1(rhs)) to compile.\n  return std::set_difference(\n      first1, last1, first2, last2, result,\n      internal::PermutedProjectedBinaryPredicate(comp, proj1, proj2));\n}\n\n// Preconditions: The ranges `range1` and `range2` are sorted with respect to\n// `comp` and `proj1` or `proj2`, respectively. The resulting range does not\n// overlap with either of the original ranges.\n//\n// Effects: Copies the elements of `range1` which are not present in `range2`\n// to the range beginning at `result`. The elements in the constructed range are\n// sorted.\n//\n// Returns: The end of the constructed range.\n//\n// Complexity: At most `2 * (size(range1) + size(range2)) - 1` comparisons and\n// applications of each projection.\n//\n// Remarks: Stable. If `range1` contains `m` elements that are equivalent to\n// each other and `range2` contains `n` elements that are equivalent to them,\n// the last `max(m - n, 0)` elements from `range1` are copied to the output\n// range, in order.\n//\n// Reference:\n// https://wg21.link/set.difference#:~:text=ranges::set_difference(R1\ntemplate <typename Range1,\n          typename Range2,\n          typename OutputIterator,\n          typename Comp = ranges::less,\n          typename Proj1 = identity,\n          typename Proj2 = identity,\n          typename = internal::range_category_t<Range1>,\n          typename = internal::range_category_t<Range2>,\n          typename = internal::iterator_category_t<OutputIterator>,\n          typename = indirect_result_t<Comp&,\n                                       projected<iterator_t<Range1>, Proj1>,\n                                       projected<iterator_t<Range2>, Proj2>>,\n          typename = indirect_result_t<Comp&,\n                                       projected<iterator_t<Range2>, Proj2>,\n                                       projected<iterator_t<Range1>, Proj1>>>\nconstexpr auto set_difference(Range1&& range1,\n                              Range2&& range2,\n                              OutputIterator result,\n                              Comp comp = {},\n                              Proj1 proj1 = {},\n                              Proj2 proj2 = {}) {\n  return ranges::set_difference(ranges::begin(range1), ranges::end(range1),\n                                ranges::begin(range2), ranges::end(range2),\n                                result, std::move(comp), std::move(proj1),\n                                std::move(proj2));\n}\n\n// [set.symmetric.difference] set_symmetric_difference\n// Reference: https://wg21.link/set.symmetric.difference\n\n// Preconditions: The ranges `[first1, last1)` and `[first2, last2)` are sorted\n// with respect to `comp` and `proj1` or `proj2`, respectively. The resulting\n// range does not overlap with either of the original ranges.\n//\n// Effects: Copies the elements of the range `[first1, last1)` that are not\n// present in the range `[first2, last2)`, and the elements of the range\n// `[first2, last2)` that are not present in the range `[first1, last1)` to the\n// range beginning at `result`. The elements in the constructed range are\n// sorted.\n//\n// Returns: The end of the constructed range.\n//\n// Complexity: At most `2 * ((last1 - first1) + (last2 - first2)) - 1`\n// comparisons and applications of each projection.\n//\n// Remarks: Stable. If `[first1, last1)` contains `m` elements that are\n// equivalent to each other and `[first2, last2)` contains `n` elements that are\n// equivalent to them, then `|m - n|` of those elements shall be copied to the\n// output range: the last `m - n` of these elements from `[first1, last1)` if\n// `m > n`, and the last `n - m` of these elements from `[first2, last2)` if\n// `m < n`. In either case, the elements are copied in order.\n//\n// Reference:\n// https://wg21.link/set.symmetric.difference#:~:text=set_symmetric_difference(I1\ntemplate <typename InputIterator1,\n          typename InputIterator2,\n          typename OutputIterator,\n          typename Comp = ranges::less,\n          typename Proj1 = identity,\n          typename Proj2 = identity,\n          typename = internal::iterator_category_t<InputIterator1>,\n          typename = internal::iterator_category_t<InputIterator2>,\n          typename = internal::iterator_category_t<OutputIterator>,\n          typename = indirect_result_t<Comp&,\n                                       projected<InputIterator1, Proj1>,\n                                       projected<InputIterator2, Proj2>>,\n          typename = indirect_result_t<Comp&,\n                                       projected<InputIterator2, Proj2>,\n                                       projected<InputIterator1, Proj1>>>\nconstexpr auto set_symmetric_difference(InputIterator1 first1,\n                                        InputIterator1 last1,\n                                        InputIterator2 first2,\n                                        InputIterator2 last2,\n                                        OutputIterator result,\n                                        Comp comp = {},\n                                        Proj1 proj1 = {},\n                                        Proj2 proj2 = {}) {\n  // Needs to opt-in to all permutations, since std::set_symmetric_difference\n  // expects comp(proj1(lhs), proj2(rhs)) and comp(proj2(lhs), proj1(rhs)) to\n  // compile.\n  return std::set_symmetric_difference(\n      first1, last1, first2, last2, result,\n      internal::PermutedProjectedBinaryPredicate(comp, proj1, proj2));\n}\n\n// Preconditions: The ranges `range1` and `range2` are sorted with respect to\n// `comp` and `proj1` or `proj2`, respectively. The resulting range does not\n// overlap with either of the original ranges.\n//\n// Effects: Copies the elements of `range1` that are not present in `range2`,\n// and the elements of `range2` that are not present in `range1` to the range\n// beginning at `result`. The elements in the constructed range are sorted.\n//\n// Returns: The end of the constructed range.\n//\n// Complexity: At most `2 * (size(range1) + size(range2)) - 1` comparisons and\n// applications of each projection.\n//\n// Remarks: Stable. If `range1` contains `m` elements that are equivalent to\n// each other and `range2` contains `n` elements that are equivalent to them,\n// then `|m - n|` of those elements shall be copied to the output range: the\n// last `m - n` of these elements from `range1` if `m > n`, and the last `n - m`\n// of these elements from `range2` if `m < n`. In either case, the elements are\n// copied in order.\n//\n// Reference:\n// https://wg21.link/set.symmetric.difference#:~:text=set_symmetric_difference(R1\ntemplate <typename Range1,\n          typename Range2,\n          typename OutputIterator,\n          typename Comp = ranges::less,\n          typename Proj1 = identity,\n          typename Proj2 = identity,\n          typename = internal::range_category_t<Range1>,\n          typename = internal::range_category_t<Range2>,\n          typename = internal::iterator_category_t<OutputIterator>,\n          typename = indirect_result_t<Comp&,\n                                       projected<iterator_t<Range1>, Proj1>,\n                                       projected<iterator_t<Range2>, Proj2>>,\n          typename = indirect_result_t<Comp&,\n                                       projected<iterator_t<Range2>, Proj2>,\n                                       projected<iterator_t<Range1>, Proj1>>>\nconstexpr auto set_symmetric_difference(Range1&& range1,\n                                        Range2&& range2,\n                                        OutputIterator result,\n                                        Comp comp = {},\n                                        Proj1 proj1 = {},\n                                        Proj2 proj2 = {}) {\n  return ranges::set_symmetric_difference(\n      ranges::begin(range1), ranges::end(range1), ranges::begin(range2),\n      ranges::end(range2), result, std::move(comp), std::move(proj1),\n      std::move(proj2));\n}\n\n// [alg.heap.operations] Heap operations\n// Reference: https://wg21.link/alg.heap.operations\n\n// [push.heap] push_heap\n// Reference: https://wg21.link/push.heap\n\n// Preconditions: The range `[first, last - 1)` is a valid heap with respect to\n// `comp` and `proj`.\n//\n// Effects: Places the value in the location `last - 1` into the resulting heap\n// `[first, last)`.\n//\n// Returns: `last`.\n//\n// Complexity: At most `log(last - first)` comparisons and twice as many\n// projections.\n//\n// Reference: https://wg21.link/push.heap#:~:text=ranges::push_heap(I\ntemplate <typename RandomAccessIterator,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<RandomAccessIterator>,\n          typename = indirect_result_t<Comp&,\n                                       projected<RandomAccessIterator, Proj>,\n                                       projected<RandomAccessIterator, Proj>>>\nconstexpr auto push_heap(RandomAccessIterator first,\n                         RandomAccessIterator last,\n                         Comp comp = {},\n                         Proj proj = {}) {\n  std::push_heap(first, last,\n                 internal::ProjectedBinaryPredicate(comp, proj, proj));\n  return last;\n}\n\n// Preconditions: The range `[begin(range), end(range) - 1)` is a valid heap\n// with respect to `comp` and `proj`.\n//\n// Effects: Places the value in the location `end(range) - 1` into the resulting\n// heap `range`.\n//\n// Returns: `end(range)`.\n//\n// Complexity: At most `log(size(range))` comparisons and twice as many\n// projections.\n//\n// Reference: https://wg21.link/push.heap#:~:text=ranges::push_heap(R\ntemplate <typename Range,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>,\n          typename = indirect_result_t<Comp&,\n                                       projected<iterator_t<Range>, Proj>,\n                                       projected<iterator_t<Range>, Proj>>>\nconstexpr auto push_heap(Range&& range, Comp comp = {}, Proj proj = {}) {\n  return ranges::push_heap(ranges::begin(range), ranges::end(range),\n                           std::move(comp), std::move(proj));\n}\n\n// [pop.heap] pop_heap\n// Reference: https://wg21.link/pop.heap\n\n// Preconditions: The range `[first, last)` is a valid non-empty heap with\n// respect to `comp` and `proj`.\n//\n// Effects: Swaps the value in the location `first` with the value in the\n// location `last - 1` and makes `[first, last - 1)` into a heap with respect to\n// `comp` and `proj`.\n//\n// Returns: `last`.\n//\n// Complexity: At most `2 log(last - first)` comparisons and twice as many\n// projections.\n//\n// Reference: https://wg21.link/pop.heap#:~:text=ranges::pop_heap(I\ntemplate <typename RandomAccessIterator,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<RandomAccessIterator>,\n          typename = indirect_result_t<Comp&,\n                                       projected<RandomAccessIterator, Proj>,\n                                       projected<RandomAccessIterator, Proj>>>\nconstexpr auto pop_heap(RandomAccessIterator first,\n                        RandomAccessIterator last,\n                        Comp comp = {},\n                        Proj proj = {}) {\n  std::pop_heap(first, last,\n                internal::ProjectedBinaryPredicate(comp, proj, proj));\n  return last;\n}\n\n// Preconditions: `range` is a valid non-empty heap with respect to `comp` and\n// `proj`.\n//\n// Effects: Swaps the value in the location `begin(range)` with the value in the\n// location `end(range) - 1` and makes `[begin(range), end(range) - 1)` into a\n// heap with respect to `comp` and `proj`.\n//\n// Returns: `end(range)`.\n//\n// Complexity: At most `2 log(size(range))` comparisons and twice as many\n// projections.\n//\n// Reference: https://wg21.link/pop.heap#:~:text=ranges::pop_heap(R\ntemplate <typename Range,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>,\n          typename = indirect_result_t<Comp&,\n                                       projected<iterator_t<Range>, Proj>,\n                                       projected<iterator_t<Range>, Proj>>>\nconstexpr auto pop_heap(Range&& range, Comp comp = {}, Proj proj = {}) {\n  return ranges::pop_heap(ranges::begin(range), ranges::end(range),\n                          std::move(comp), std::move(proj));\n}\n\n// [make.heap] make_heap\n// Reference: https://wg21.link/make.heap\n\n// Effects: Constructs a heap with respect to `comp` and `proj` out of the range\n// `[first, last)`.\n//\n// Returns: `last`.\n//\n// Complexity: At most `3 * (last - first)` comparisons and twice as many\n// projections.\n//\n// Reference: https://wg21.link/make.heap#:~:text=ranges::make_heap(I\ntemplate <typename RandomAccessIterator,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<RandomAccessIterator>,\n          typename = indirect_result_t<Comp&,\n                                       projected<RandomAccessIterator, Proj>,\n                                       projected<RandomAccessIterator, Proj>>>\nconstexpr auto make_heap(RandomAccessIterator first,\n                         RandomAccessIterator last,\n                         Comp comp = {},\n                         Proj proj = {}) {\n  std::make_heap(first, last,\n                 internal::ProjectedBinaryPredicate(comp, proj, proj));\n  return last;\n}\n\n// Effects: Constructs a heap with respect to `comp` and `proj` out of `range`.\n//\n// Returns: `end(range)`.\n//\n// Complexity: At most `3 * size(range)` comparisons and twice as many\n// projections.\n//\n// Reference: https://wg21.link/make.heap#:~:text=ranges::make_heap(R\ntemplate <typename Range,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>,\n          typename = indirect_result_t<Comp&,\n                                       projected<iterator_t<Range>, Proj>,\n                                       projected<iterator_t<Range>, Proj>>>\nconstexpr auto make_heap(Range&& range, Comp comp = {}, Proj proj = {}) {\n  return ranges::make_heap(ranges::begin(range), ranges::end(range),\n                           std::move(comp), std::move(proj));\n}\n\n// [sort.heap] sort_heap\n// Reference: https://wg21.link/sort.heap\n\n// Preconditions: The range `[first, last)` is a valid heap with respect to\n// `comp` and `proj`.\n//\n// Effects: Sorts elements in the heap `[first, last)` with respect to `comp`\n// and `proj`.\n//\n// Returns: `last`.\n//\n// Complexity: At most `2 N log N` comparisons, where `N = last - first`, and\n// twice as many projections.\n//\n// Reference: https://wg21.link/sort.heap#:~:text=ranges::sort_heap(I\ntemplate <typename RandomAccessIterator,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<RandomAccessIterator>,\n          typename = indirect_result_t<Comp&,\n                                       projected<RandomAccessIterator, Proj>,\n                                       projected<RandomAccessIterator, Proj>>>\nconstexpr auto sort_heap(RandomAccessIterator first,\n                         RandomAccessIterator last,\n                         Comp comp = {},\n                         Proj proj = {}) {\n  std::sort_heap(first, last,\n                 internal::ProjectedBinaryPredicate(comp, proj, proj));\n  return last;\n}\n\n// Preconditions: `range` is a valid heap with respect to `comp` and `proj`.\n//\n// Effects: Sorts elements in the heap `range` with respect to `comp` and\n// `proj`.\n//\n// Returns: `end(range)`.\n//\n// Complexity: At most `2 N log N` comparisons, where `N = size(range)`, and\n// twice as many projections.\n//\n// Reference: https://wg21.link/sort.heap#:~:text=ranges::sort_heap(R\ntemplate <typename Range,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>,\n          typename = indirect_result_t<Comp&,\n                                       projected<iterator_t<Range>, Proj>,\n                                       projected<iterator_t<Range>, Proj>>>\nconstexpr auto sort_heap(Range&& range, Comp comp = {}, Proj proj = {}) {\n  return ranges::sort_heap(ranges::begin(range), ranges::end(range),\n                           std::move(comp), std::move(proj));\n}\n\n// [is.heap] is_heap\n// Reference: https://wg21.link/is.heap\n\n// Returns: Whether the range `[first, last)` is a heap with respect to `comp`\n// and `proj`.\n//\n// Complexity: Linear.\n//\n// Reference: https://wg21.link/is.heap#:~:text=ranges::is_heap(I\ntemplate <typename RandomAccessIterator,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<RandomAccessIterator>,\n          typename = indirect_result_t<Comp&,\n                                       projected<RandomAccessIterator, Proj>,\n                                       projected<RandomAccessIterator, Proj>>>\nconstexpr auto is_heap(RandomAccessIterator first,\n                       RandomAccessIterator last,\n                       Comp comp = {},\n                       Proj proj = {}) {\n  return std::is_heap(first, last,\n                      internal::ProjectedBinaryPredicate(comp, proj, proj));\n}\n\n// Returns: Whether `range` is a heap with respect to `comp` and `proj`.\n//\n// Complexity: Linear.\n//\n// Reference: https://wg21.link/is.heap#:~:text=ranges::is_heap(R\ntemplate <typename Range,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>,\n          typename = indirect_result_t<Comp&,\n                                       projected<iterator_t<Range>, Proj>,\n                                       projected<iterator_t<Range>, Proj>>>\nconstexpr auto is_heap(Range&& range, Comp comp = {}, Proj proj = {}) {\n  return ranges::is_heap(ranges::begin(range), ranges::end(range),\n                         std::move(comp), std::move(proj));\n}\n\n// Returns: The last iterator `i` in `[first, last]` for which the range\n// `[first, i)` is a heap with respect to `comp` and `proj`.\n//\n// Complexity: Linear.\n//\n// Reference: https://wg21.link/is.heap#:~:text=ranges::is_heap_until(I\ntemplate <typename RandomAccessIterator,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<RandomAccessIterator>,\n          typename = indirect_result_t<Comp&,\n                                       projected<RandomAccessIterator, Proj>,\n                                       projected<RandomAccessIterator, Proj>>>\nconstexpr auto is_heap_until(RandomAccessIterator first,\n                             RandomAccessIterator last,\n                             Comp comp = {},\n                             Proj proj = {}) {\n  return std::is_heap_until(\n      first, last, internal::ProjectedBinaryPredicate(comp, proj, proj));\n}\n\n// Returns: The last iterator `i` in `[begin(range), end(range)]` for which the\n// range `[begin(range), i)` is a heap with respect to `comp` and `proj`.\n//\n// Complexity: Linear.\n//\n// Reference: https://wg21.link/is.heap#:~:text=ranges::is_heap_until(R\ntemplate <typename Range,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>,\n          typename = indirect_result_t<Comp&,\n                                       projected<iterator_t<Range>, Proj>,\n                                       projected<iterator_t<Range>, Proj>>>\nconstexpr auto is_heap_until(Range&& range, Comp comp = {}, Proj proj = {}) {\n  return ranges::is_heap_until(ranges::begin(range), ranges::end(range),\n                               std::move(comp), std::move(proj));\n}\n\n// [alg.min.max] Minimum and maximum\n// Reference: https://wg21.link/alg.min.max\n\n// Returns: The smaller value. Returns the first argument when the arguments are\n// equivalent.\n//\n// Complexity: Exactly one comparison and two applications of the projection, if\n// any.\n//\n// Reference: https://wg21.link/alg.min.max#:~:text=ranges::min\ntemplate <typename T, typename Comp = ranges::less, typename Proj = identity>\nconstexpr const T& min(const T& a, const T& b, Comp comp = {}, Proj proj = {}) {\n  return base::invoke(comp, base::invoke(proj, b), base::invoke(proj, a)) ? b\n                                                                          : a;\n}\n\n// Preconditions: `!empty(ilist)`.\n//\n// Returns: The smallest value in the input range. Returns a copy of the\n// leftmost element when several elements are equivalent to the smallest.\n//\n// Complexity: Exactly `size(ilist) - 1` comparisons and twice as many\n// applications of the projection, if any.\n//\n// Reference: https://wg21.link/alg.min.max#:~:text=ranges::min(initializer_list\ntemplate <typename T, typename Comp = ranges::less, typename Proj = identity>\nconstexpr T min(std::initializer_list<T> ilist,\n                Comp comp = {},\n                Proj proj = {}) {\n  return *std::min_element(\n      ilist.begin(), ilist.end(),\n      internal::ProjectedBinaryPredicate(comp, proj, proj));\n}\n\n// Preconditions: `!empty(range)`.\n//\n// Returns: The smallest value in the input range. Returns a copy of the\n// leftmost element when several elements are equivalent to the smallest.\n//\n// Complexity: Exactly `size(range) - 1` comparisons and twice as many\n// applications of the projection, if any.\n//\n// Reference: https://wg21.link/alg.min.max#:~:text=ranges::min(R\ntemplate <typename Range,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>>\nconstexpr auto min(Range&& range, Comp comp = {}, Proj proj = {}) {\n  return *std::min_element(\n      ranges::begin(range), ranges::end(range),\n      internal::ProjectedBinaryPredicate(comp, proj, proj));\n}\n\n// Returns: The larger value. Returns the first argument when the arguments are\n// equivalent.\n//\n// Complexity: Exactly one comparison and two applications of the projection, if\n// any.\n//\n// Reference: https://wg21.link/alg.min.max#:~:text=ranges::max\ntemplate <typename T, typename Comp = ranges::less, typename Proj = identity>\nconstexpr const T& max(const T& a, const T& b, Comp comp = {}, Proj proj = {}) {\n  return base::invoke(comp, base::invoke(proj, a), base::invoke(proj, b)) ? b\n                                                                          : a;\n}\n\n// Preconditions: `!empty(ilist)`.\n//\n// Returns: The largest value in the input range. Returns a copy of the leftmost\n// element when several elements are equivalent to the largest.\n//\n// Complexity: Exactly `size(ilist) - 1` comparisons and twice as many\n// applications of the projection, if any.\n//\n// Reference: https://wg21.link/alg.min.max#:~:text=ranges::max(initializer_list\ntemplate <typename T, typename Comp = ranges::less, typename Proj = identity>\nconstexpr T max(std::initializer_list<T> ilist,\n                Comp comp = {},\n                Proj proj = {}) {\n  return *std::max_element(\n      ilist.begin(), ilist.end(),\n      internal::ProjectedBinaryPredicate(comp, proj, proj));\n}\n\n// Preconditions: `!empty(range)`.\n//\n// Returns: The largest value in the input range. Returns a copy of the leftmost\n// element when several elements are equivalent to the smallest.\n//\n// Complexity: Exactly `size(range) - 1` comparisons and twice as many\n// applications of the projection, if any.\n//\n// Reference: https://wg21.link/alg.min.max#:~:text=ranges::max(R\ntemplate <typename Range,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>>\nconstexpr auto max(Range&& range, Comp comp = {}, Proj proj = {}) {\n  return *std::max_element(\n      ranges::begin(range), ranges::end(range),\n      internal::ProjectedBinaryPredicate(comp, proj, proj));\n}\n\n// Returns: `{b, a}` if `b` is smaller than `a`, and `{a, b}` otherwise.\n//\n// Complexity: Exactly one comparison and two applications of the projection, if\n// any.\n//\n// Reference: https://wg21.link/alg.min.max#:~:text=ranges::minmax\ntemplate <typename T, typename Comp = ranges::less, typename Proj = identity>\nconstexpr auto minmax(const T& a, const T& b, Comp comp = {}, Proj proj = {}) {\n  return std::minmax(a, b,\n                     internal::ProjectedBinaryPredicate(comp, proj, proj));\n}\n\n// Preconditions: `!empty(ilist)`.\n//\n// Returns: Let `X` be the return type. Returns `X{x, y}`, where `x` is a copy\n// of the leftmost element with the smallest value and `y` a copy of the\n// rightmost element with the largest value in the input range.\n//\n// Complexity: At most `(3/2) size(ilist)` applications of the corresponding\n// predicate and twice as many applications of the projection, if any.\n//\n// Reference:\n// https://wg21.link/alg.min.max#:~:text=ranges::minmax(initializer_list\ntemplate <typename T, typename Comp = ranges::less, typename Proj = identity>\nconstexpr auto minmax(std::initializer_list<T> ilist,\n                      Comp comp = {},\n                      Proj proj = {}) {\n  auto it =\n      std::minmax_element(ranges::begin(ilist), ranges::end(ilist),\n                          internal::ProjectedBinaryPredicate(comp, proj, proj));\n  return std::pair<T, T>{*it.first, *it.second};\n}\n\n// Preconditions: `!empty(range)`.\n//\n// Returns: Let `X` be the return type. Returns `X{x, y}`, where `x` is a copy\n// of the leftmost element with the smallest value and `y` a copy of the\n// rightmost element with the largest value in the input range.\n//\n// Complexity: At most `(3/2) size(range)` applications of the corresponding\n// predicate and twice as many applications of the projection, if any.\n//\n// Reference: https://wg21.link/alg.min.max#:~:text=ranges::minmax(R\ntemplate <typename Range,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>>\nconstexpr auto minmax(Range&& range, Comp comp = {}, Proj proj = {}) {\n  using T = range_value_t<Range>;\n  auto it =\n      std::minmax_element(ranges::begin(range), ranges::end(range),\n                          internal::ProjectedBinaryPredicate(comp, proj, proj));\n  return std::pair<T, T>{*it.first, *it.second};\n}\n\n// Returns: The first iterator i in the range `[first, last)` such that for\n// every iterator `j` in the range `[first, last)`,\n// `bool(invoke(comp, invoke(proj, *j), invoke(proj, *i)))` is `false`. Returns\n// `last` if `first == last`.\n//\n// Complexity: Exactly `max(last - first - 1, 0)` comparisons and twice as\n// many projections.\n//\n// Reference: https://wg21.link/alg.min.max#:~:text=ranges::min_element(I\ntemplate <typename ForwardIterator,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<ForwardIterator>,\n          typename = indirect_result_t<Comp&,\n                                       projected<ForwardIterator, Proj>,\n                                       projected<ForwardIterator, Proj>>>\nconstexpr auto min_element(ForwardIterator first,\n                           ForwardIterator last,\n                           Comp comp = {},\n                           Proj proj = {}) {\n  return std::min_element(first, last,\n                          internal::ProjectedBinaryPredicate(comp, proj, proj));\n}\n\n// Returns: The first iterator i in `range` such that for every iterator `j` in\n// `range`, `bool(invoke(comp, invoke(proj, *j), invoke(proj, *i)))` is `false`.\n// Returns `end(range)` if `empty(range)`.\n//\n// Complexity: Exactly `max(size(range) - 1, 0)` comparisons and twice as many\n// projections.\n//\n// Reference: https://wg21.link/alg.min.max#:~:text=ranges::min_element(R\ntemplate <typename Range,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>,\n          typename = indirect_result_t<Comp&,\n                                       projected<iterator_t<Range>, Proj>,\n                                       projected<iterator_t<Range>, Proj>>>\nconstexpr auto min_element(Range&& range, Comp comp = {}, Proj proj = {}) {\n  return ranges::min_element(ranges::begin(range), ranges::end(range),\n                             std::move(comp), std::move(proj));\n}\n\n// Returns: The first iterator i in the range `[first, last)` such that for\n// every iterator `j` in the range `[first, last)`,\n// `bool(invoke(comp, invoke(proj, *i), invoke(proj, *j)))` is `false`.\n// Returns `last` if `first == last`.\n//\n// Complexity: Exactly `max(last - first - 1, 0)` comparisons and twice as\n// many projections.\n//\n// Reference: https://wg21.link/alg.min.max#:~:text=ranges::max_element(I\ntemplate <typename ForwardIterator,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<ForwardIterator>,\n          typename = indirect_result_t<Comp&,\n                                       projected<ForwardIterator, Proj>,\n                                       projected<ForwardIterator, Proj>>>\nconstexpr auto max_element(ForwardIterator first,\n                           ForwardIterator last,\n                           Comp comp = {},\n                           Proj proj = {}) {\n  return std::max_element(first, last,\n                          internal::ProjectedBinaryPredicate(comp, proj, proj));\n}\n\n// Returns: The first iterator i in `range` such that for every iterator `j`\n// in `range`, `bool(invoke(comp, invoke(proj, *j), invoke(proj, *j)))` is\n// `false`. Returns `end(range)` if `empty(range)`.\n//\n// Complexity: Exactly `max(size(range) - 1, 0)` comparisons and twice as many\n// projections.\n//\n// Reference: https://wg21.link/alg.min.max#:~:text=ranges::max_element(R\ntemplate <typename Range,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>,\n          typename = indirect_result_t<Comp&,\n                                       projected<iterator_t<Range>, Proj>,\n                                       projected<iterator_t<Range>, Proj>>>\nconstexpr auto max_element(Range&& range, Comp comp = {}, Proj proj = {}) {\n  return ranges::max_element(ranges::begin(range), ranges::end(range),\n                             std::move(comp), std::move(proj));\n}\n\n// Returns: `{first, first}` if `[first, last)` is empty, otherwise `{m, M}`,\n// where `m` is the first iterator in `[first, last)` such that no iterator in\n// the range refers to a smaller element, and where `M` is the last iterator\n// in\n// `[first, last)` such that no iterator in the range refers to a larger\n// element.\n//\n// Complexity: Let `N` be `last - first`. At most `max(3/2 (N − 1), 0)`\n// comparisons and twice as many applications of the projection, if any.\n//\n// Reference: https://wg21.link/alg.min.max#:~:text=ranges::minmax_element(I\ntemplate <typename ForwardIterator,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<ForwardIterator>,\n          typename = indirect_result_t<Comp&,\n                                       projected<ForwardIterator, Proj>,\n                                       projected<ForwardIterator, Proj>>>\nconstexpr auto minmax_element(ForwardIterator first,\n                              ForwardIterator last,\n                              Comp comp = {},\n                              Proj proj = {}) {\n  return std::minmax_element(\n      first, last, internal::ProjectedBinaryPredicate(comp, proj, proj));\n}\n\n// Returns: `{begin(range), begin(range)}` if `range` is empty, otherwise\n// `{m, M}`, where `m` is the first iterator in `range` such that no iterator\n// in the range refers to a smaller element, and where `M` is the last\n// iterator in `range` such that no iterator in the range refers to a larger\n// element.\n//\n// Complexity: Let `N` be `size(range)`. At most `max(3/2 (N − 1), 0)`\n// comparisons and twice as many applications of the projection, if any.\n//\n// Reference: https://wg21.link/alg.min.max#:~:text=ranges::minmax_element(R\ntemplate <typename Range,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>,\n          typename = indirect_result_t<Comp&,\n                                       projected<iterator_t<Range>, Proj>,\n                                       projected<iterator_t<Range>, Proj>>>\nconstexpr auto minmax_element(Range&& range, Comp comp = {}, Proj proj = {}) {\n  return ranges::minmax_element(ranges::begin(range), ranges::end(range),\n                                std::move(comp), std::move(proj));\n}\n\n// [alg.clamp] Bounded value\n// Reference: https://wg21.link/alg.clamp\n\n// Preconditions: `bool(invoke(comp, invoke(proj, hi), invoke(proj, lo)))` is\n// `false`.\n//\n// Returns: `lo` if `bool(invoke(comp, invoke(proj, v), invoke(proj, lo)))` is\n// `true`, `hi` if `bool(invoke(comp, invoke(proj, hi), invoke(proj, v)))` is\n// `true`, otherwise `v`.\n//\n// Complexity: At most two comparisons and three applications of the\n// projection.\n//\n// Reference: https://wg21.link/alg.clamp#:~:text=ranges::clamp\ntemplate <typename T, typename Comp = ranges::less, typename Proj = identity>\nconstexpr const T& clamp(const T& v,\n                         const T& lo,\n                         const T& hi,\n                         Comp comp = {},\n                         Proj proj = {}) {\n  auto&& projected_v = base::invoke(proj, v);\n  if (base::invoke(comp, projected_v, base::invoke(proj, lo)))\n    return lo;\n\n  return base::invoke(comp, base::invoke(proj, hi), projected_v) ? hi : v;\n}\n\n// [alg.lex.comparison] Lexicographical comparison\n// Reference: https://wg21.link/alg.lex.comparison\n\n// Returns: `true` if and only if the sequence of elements defined by the range\n// `[first1, last1)` is lexicographically less than the sequence of elements\n// defined by the range `[first2, last2)`.\n//\n// Complexity: At most `2 min(last1 - first1, last2 - first2)` applications of\n// the corresponding comparison and each projection, if any.\n//\n// Remarks: If two sequences have the same number of elements and their\n// corresponding elements (if any) are equivalent, then neither sequence is\n// lexicographically less than the other. If one sequence is a proper prefix of\n// the other, then the shorter sequence is lexicographically less than the\n// longer sequence. Otherwise, the lexicographical comparison of the sequences\n// yields the same result as the comparison of the first corresponding pair of\n// elements that are not equivalent.\n//\n// Reference:\n// https://wg21.link/alg.lex.comparison#:~:text=lexicographical_compare(I1\ntemplate <typename ForwardIterator1,\n          typename ForwardIterator2,\n          typename Comp = ranges::less,\n          typename Proj1 = identity,\n          typename Proj2 = identity,\n          typename = internal::iterator_category_t<ForwardIterator1>,\n          typename = internal::iterator_category_t<ForwardIterator2>,\n          typename = indirect_result_t<Comp&,\n                                       projected<ForwardIterator1, Proj1>,\n                                       projected<ForwardIterator2, Proj2>>,\n          typename = indirect_result_t<Comp&,\n                                       projected<ForwardIterator2, Proj2>,\n                                       projected<ForwardIterator1, Proj1>>>\nconstexpr bool lexicographical_compare(ForwardIterator1 first1,\n                                       ForwardIterator1 last1,\n                                       ForwardIterator2 first2,\n                                       ForwardIterator2 last2,\n                                       Comp comp = {},\n                                       Proj1 proj1 = {},\n                                       Proj2 proj2 = {}) {\n  for (; first1 != last1 && first2 != last2; ++first1, ++first2) {\n    auto&& projected_first1 = base::invoke(proj1, *first1);\n    auto&& projected_first2 = base::invoke(proj2, *first2);\n    if (base::invoke(comp, projected_first1, projected_first2))\n      return true;\n    if (base::invoke(comp, projected_first2, projected_first1))\n      return false;\n  }\n\n  // `first2 != last2` is equivalent to `first1 == last1 && first2 != last2`\n  // here, since we broke out of the loop above.\n  return first2 != last2;\n}\n\n// Returns: `true` if and only if the sequence of elements defined by `range1`\n//  is lexicographically less than the sequence of elements defined by `range2`.\n//\n// Complexity: At most `2 min(size(range1), size(range2))` applications of the\n// corresponding comparison and each projection, if any.\n//\n// Remarks: If two sequences have the same number of elements and their\n// corresponding elements (if any) are equivalent, then neither sequence is\n// lexicographically less than the other. If one sequence is a proper prefix of\n// the other, then the shorter sequence is lexicographically less than the\n// longer sequence. Otherwise, the lexicographical comparison of the sequences\n// yields the same result as the comparison of the first corresponding pair of\n// elements that are not equivalent.\n//\n// Reference:\n// https://wg21.link/alg.lex.comparison#:~:text=lexicographical_compare(R1\ntemplate <typename Range1,\n          typename Range2,\n          typename Comp = ranges::less,\n          typename Proj1 = identity,\n          typename Proj2 = identity,\n          typename = internal::range_category_t<Range1>,\n          typename = internal::range_category_t<Range2>,\n          typename = indirect_result_t<Comp&,\n                                       projected<iterator_t<Range1>, Proj1>,\n                                       projected<iterator_t<Range2>, Proj2>>,\n          typename = indirect_result_t<Comp&,\n                                       projected<iterator_t<Range2>, Proj2>,\n                                       projected<iterator_t<Range1>, Proj1>>>\nconstexpr bool lexicographical_compare(Range1&& range1,\n                                       Range2&& range2,\n                                       Comp comp = {},\n                                       Proj1 proj1 = {},\n                                       Proj2 proj2 = {}) {\n  return ranges::lexicographical_compare(\n      ranges::begin(range1), ranges::end(range1), ranges::begin(range2),\n      ranges::end(range2), std::move(comp), std::move(proj1), std::move(proj2));\n}\n\n// [alg.permutation.generators] Permutation generators\n// Reference: https://wg21.link/alg.permutation.generators\n\n// Effects: Takes a sequence defined by the range `[first, last)` and transforms\n// it into the next permutation. The next permutation is found by assuming that\n// the set of all permutations is lexicographically sorted with respect to\n// `comp` and `proj`. If no such permutation exists, transforms the sequence\n// into the first permutation; that is, the ascendingly-sorted one.\n//\n// Returns: `true` if a next permutation was found and otherwise `false`.\n//\n// Complexity: At most `(last - first) / 2` swaps.\n//\n// Reference:\n// https://wg21.link/alg.permutation.generators#:~:text=next_permutation(I\ntemplate <typename BidirectionalIterator,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<BidirectionalIterator>,\n          typename = indirect_result_t<Comp&,\n                                       projected<BidirectionalIterator, Proj>,\n                                       projected<BidirectionalIterator, Proj>>>\nconstexpr auto next_permutation(BidirectionalIterator first,\n                                BidirectionalIterator last,\n                                Comp comp = {},\n                                Proj proj = {}) {\n  return std::next_permutation(\n      first, last, internal::ProjectedBinaryPredicate(comp, proj, proj));\n}\n\n// Effects: Takes a sequence defined by `range` and transforms it into the next\n// permutation. The next permutation is found by assuming that the set of all\n// permutations is lexicographically sorted with respect to `comp` and `proj`.\n// If no such permutation exists, transforms the sequence into the first\n// permutation; that is, the ascendingly-sorted one.\n//\n// Returns: `true` if a next permutation was found and otherwise `false`.\n//\n// Complexity: At most `size(range) / 2` swaps.\n//\n// Reference:\n// https://wg21.link/alg.permutation.generators#:~:text=next_permutation(R\ntemplate <typename Range,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>,\n          typename = indirect_result_t<Comp&,\n                                       projected<iterator_t<Range>, Proj>,\n                                       projected<iterator_t<Range>, Proj>>>\nconstexpr auto next_permutation(Range&& range, Comp comp = {}, Proj proj = {}) {\n  return ranges::next_permutation(ranges::begin(range), ranges::end(range),\n                                  std::move(comp), std::move(proj));\n}\n\n// Effects: Takes a sequence defined by the range `[first, last)` and transforms\n// it into the previous permutation. The previous permutation is found by\n// assuming that the set of all permutations is lexicographically sorted with\n// respect to `comp` and `proj`. If no such permutation exists, transforms the\n// sequence into the last permutation; that is, the decreasingly-sorted one.\n//\n// Returns: `true` if a next permutation was found and otherwise `false`.\n//\n// Complexity: At most `(last - first) / 2` swaps.\n//\n// Reference:\n// https://wg21.link/alg.permutation.generators#:~:text=prev_permutation(I\ntemplate <typename BidirectionalIterator,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::iterator_category_t<BidirectionalIterator>,\n          typename = indirect_result_t<Comp&,\n                                       projected<BidirectionalIterator, Proj>,\n                                       projected<BidirectionalIterator, Proj>>>\nconstexpr auto prev_permutation(BidirectionalIterator first,\n                                BidirectionalIterator last,\n                                Comp comp = {},\n                                Proj proj = {}) {\n  return std::prev_permutation(\n      first, last, internal::ProjectedBinaryPredicate(comp, proj, proj));\n}\n\n// Effects: Takes a sequence defined by `range` and transforms it into the\n// previous permutation. The previous permutation is found by assuming that the\n// set of all permutations is lexicographically sorted with respect to `comp`\n// and `proj`. If no such permutation exists, transforms the sequence into the\n// last permutation; that is, the decreasingly-sorted one.\n//\n// Returns: `true` if a previous permutation was found and otherwise `false`.\n//\n// Complexity: At most `size(range) / 2` swaps.\n//\n// Reference:\n// https://wg21.link/alg.permutation.generators#:~:text=prev_permutation(R\ntemplate <typename Range,\n          typename Comp = ranges::less,\n          typename Proj = identity,\n          typename = internal::range_category_t<Range>,\n          typename = indirect_result_t<Comp&,\n                                       projected<iterator_t<Range>, Proj>,\n                                       projected<iterator_t<Range>, Proj>>>\nconstexpr auto prev_permutation(Range&& range, Comp comp = {}, Proj proj = {}) {\n  return ranges::prev_permutation(ranges::begin(range), ranges::end(range),\n                                  std::move(comp), std::move(proj));\n}\n\n}  // namespace ranges\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_RANGES_ALGORITHM_H_\n"
  },
  {
    "path": "tachyon/base/ranges/algorithm_unittest.cc",
    "content": "// Copyright 2020 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/ranges/algorithm.h\"\n\n#include <algorithm>\n#include <functional>\n#include <initializer_list>\n#include <iterator>\n#include <random>\n#include <utility>\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/ranges/functional.h\"\n\nusing testing::ElementsAre;\nusing testing::Field;\nusing testing::Ge;\nusing testing::Gt;\nusing testing::Le;\nusing testing::Lt;\nusing testing::Pair;\n\nnamespace tachyon::base {\n\nnamespace {\n\n// A macro to work around the fact that lambdas are not constexpr in C++14.\n// This will define an unnamed struct with a constexpr call operator, similarly\n// to how lambdas behave in C++17+.\n// Note that this does not support capture groups, so all lambdas defined like\n// this must be stateless.\n// Example Usage: `CONSTEXPR_LAMBDA((int i, int j) { return i + j; }) lambda;`\n// TODO(crbug.com/752720): Remove once we have constexpr lambdas for real.\n#define CONSTEXPR_LAMBDA(fun)     \\\n  constexpr struct {              \\\n    constexpr bool operator() fun \\\n  }\n\nstruct Int {\n  constexpr Int() = default;\n  constexpr Int(int value) : value(value) {}\n\n  int value = 0;\n};\n\nconstexpr bool operator==(Int lhs, Int rhs) { return lhs.value == rhs.value; }\n\nconstexpr bool operator<(Int lhs, Int rhs) { return lhs.value < rhs.value; }\n\nconstexpr bool operator>(Int lhs, Int rhs) { return lhs.value > rhs.value; }\n\nconstexpr bool operator<=(Int lhs, Int rhs) { return lhs.value <= rhs.value; }\n\nconstexpr bool operator>=(Int lhs, Int rhs) { return lhs.value >= rhs.value; }\n\n// Move-only int that clears `value` when moving out.\nstruct MoveOnlyInt {\n  MoveOnlyInt(int value) : value(value) {}\n  MoveOnlyInt(MoveOnlyInt&& other) : value(std::exchange(other.value, 0)) {}\n\n  MoveOnlyInt& operator=(MoveOnlyInt&& other) {\n    value = std::exchange(other.value, 0);\n    return *this;\n  }\n\n  int value = 0;\n};\n\nconstexpr bool is_even(int i) { return i % 2 == 0; }\n\nbool is_odd(int i) { return i % 2 == 1; }\n\ntemplate <typename Iter>\nauto make_vector(Iter begin, Iter end) {\n  using T = typename std::iterator_traits<Iter>::value_type;\n  return std::vector<T>(begin, end);\n}\n\n}  // namespace\n\nTEST(RangesTest, AllOf) {\n  // Note: Lambdas don't have a constexpr call operator prior to C++17, thus we\n  // are providing our own anonyomous struct here.\n  constexpr struct {\n    constexpr bool operator()(int i) { return i != 0; }\n  } is_non_zero;\n\n  constexpr int array[] = {0, 1, 2, 3, 4, 5};\n\n  static_assert(ranges::all_of(array + 1, array + 6, is_non_zero), \"\");\n  static_assert(!ranges::all_of(array, is_non_zero), \"\");\n\n  constexpr Int values[] = {0, 2, 4, 5};\n  static_assert(\n      ranges::all_of(values + 1, ranges::end(values), is_non_zero, &Int::value),\n      \"\");\n  static_assert(!ranges::all_of(values, is_non_zero, &Int::value), \"\");\n}\n\nTEST(RangesTest, AnyOf) {\n  constexpr int array[] = {0, 1, 2, 3, 4, 5};\n\n  static_assert(!ranges::any_of(array + 5, array + 6, is_even), \"\");\n  static_assert(ranges::any_of(array, is_even), \"\");\n\n  constexpr Int values[] = {{0}, {2}, {4}, {5}};\n  static_assert(\n      !ranges::any_of(values + 3, ranges::end(values), is_even, &Int::value),\n      \"\");\n  static_assert(ranges::any_of(values, is_even, &Int::value), \"\");\n}\n\nTEST(RangesTest, NoneOf) {\n  // Note: Lambdas don't have a constexpr call operator prior to C++17, thus we\n  // are providing our own anonyomous struct here.\n  constexpr struct {\n    constexpr bool operator()(int i) { return i == 0; }\n  } is_zero;\n  constexpr int array[] = {0, 1, 2, 3, 4, 5};\n\n  static_assert(ranges::none_of(array + 1, array + 6, is_zero), \"\");\n  static_assert(!ranges::none_of(array, is_zero), \"\");\n\n  constexpr Int values[] = {{0}, {2}, {4}, {5}};\n  static_assert(\n      ranges::none_of(values + 1, ranges::end(values), is_zero, &Int::value),\n      \"\");\n  static_assert(!ranges::none_of(values, is_zero, &Int::value), \"\");\n}\n\nTEST(RangesTest, ForEach) {\n  auto times_two = [](int& i) { i *= 2; };\n  int array[] = {0, 1, 2, 3, 4, 5};\n\n  auto result = ranges::for_each(array, array + 3, times_two);\n  EXPECT_EQ(result.in, array + 3);\n  // TODO(https://crbug.com/1191256): Fix googletest and switch this back to\n  // EXPECT_EQ.\n  EXPECT_TRUE(result.fun == times_two);\n  EXPECT_THAT(array, ElementsAre(0, 2, 4, 3, 4, 5));\n\n  ranges::for_each(array + 3, array + 6, times_two);\n  EXPECT_EQ(result.in, array + 3);\n  // TODO(https://crbug.com/1191256): Fix googletest and switch this back to\n  // EXPECT_EQ.\n  EXPECT_TRUE(result.fun == times_two);\n  EXPECT_THAT(array, ElementsAre(0, 2, 4, 6, 8, 10));\n\n  // TODO(https://crbug.com/1191256): Fix googletest and switch this back to\n  // EXPECT_EQ.\n  EXPECT_TRUE(times_two == ranges::for_each(array, times_two).fun);\n  EXPECT_THAT(array, ElementsAre(0, 4, 8, 12, 16, 20));\n\n  Int values[] = {0, 2, 4, 5};\n  // TODO(https://crbug.com/1191256): Fix googletest and switch this back to\n  // EXPECT_EQ.\n  EXPECT_TRUE(times_two ==\n              ranges::for_each(values, times_two, &Int::value).fun);\n  EXPECT_THAT(values,\n              ElementsAre(Field(&Int::value, 0), Field(&Int::value, 4),\n                          Field(&Int::value, 8), Field(&Int::value, 10)));\n}\n\nTEST(RangesTest, ForEachN) {\n  auto times_two = [](int& i) { i *= 2; };\n  int array[] = {0, 1, 2, 3, 4, 5};\n\n  auto result = ranges::for_each_n(array, 3, times_two);\n  EXPECT_EQ(result.in, array + 3);\n  // TODO(https://crbug.com/1191256): Fix googletest and switch this back to\n  // EXPECT_EQ.\n  EXPECT_TRUE(result.fun == times_two);\n  EXPECT_THAT(array, ElementsAre(0, 2, 4, 3, 4, 5));\n\n  Int values[] = {0, 2, 4, 5};\n  // TODO(https://crbug.com/1191256): Fix googletest and switch this back to\n  // EXPECT_EQ.\n  EXPECT_TRUE(times_two ==\n              ranges::for_each_n(values, 4, times_two, &Int::value).fun);\n  EXPECT_THAT(values,\n              ElementsAre(Field(&Int::value, 0), Field(&Int::value, 4),\n                          Field(&Int::value, 8), Field(&Int::value, 10)));\n}\n\nTEST(RangesTest, Find) {\n  constexpr int array[] = {0, 1, 2, 3, 4, 5};\n\n  static_assert(array + 6 == ranges::find(array + 1, array + 6, 0), \"\");\n  static_assert(array == ranges::find(array, 0), \"\");\n\n  constexpr Int values[] = {{0}, {2}, {4}, {5}};\n  static_assert(values == ranges::find(values, values, 0, &Int::value), \"\");\n  static_assert(ranges::end(values) == ranges::find(values, 3, &Int::value),\n                \"\");\n}\n\nTEST(RangesTest, FindIf) {\n  auto is_at_least_5 = [](int i) { return i >= 5; };\n  int array[] = {0, 1, 2, 3, 4, 5};\n\n  EXPECT_EQ(array + 5, ranges::find_if(array, array + 5, is_at_least_5));\n  EXPECT_EQ(array + 5, ranges::find_if(array, is_at_least_5));\n\n  Int values[] = {{0}, {2}, {4}, {5}};\n  EXPECT_EQ(values + 3,\n            ranges::find_if(values, values + 3, is_odd, &Int::value));\n  EXPECT_EQ(values + 3, ranges::find_if(values, is_odd, &Int::value));\n}\n\nTEST(RangesTest, FindIfNot) {\n  auto is_less_than_5 = [](int i) { return i < 5; };\n  int array[] = {0, 1, 2, 3, 4, 5};\n\n  EXPECT_EQ(array + 5, ranges::find_if_not(array, array + 5, is_less_than_5));\n  EXPECT_EQ(array + 5, ranges::find_if_not(array, is_less_than_5));\n\n  Int values[] = {{0}, {2}, {4}, {5}};\n  EXPECT_EQ(values + 3,\n            ranges::find_if_not(values, values + 3, is_even, &Int::value));\n  EXPECT_EQ(values + 3, ranges::find_if_not(values, is_even, &Int::value));\n}\n\nTEST(RangesTest, FindEnd) {\n  int array1[] = {0, 1, 2};\n  int array2[] = {4, 5, 6};\n  int array3[] = {0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4,\n                  0, 1, 2, 3, 0, 1, 2, 0, 1, 0};\n\n  EXPECT_EQ(array3 + 15, ranges::find_end(array3, ranges::end(array3), array1,\n                                          ranges::end(array1)));\n  EXPECT_EQ(ranges::end(array3), ranges::find_end(array3, ranges::end(array3),\n                                                  array2, ranges::end(array2)));\n  EXPECT_EQ(array3 + 4,\n            ranges::find_end(array3, ranges::end(array3), array2, array2 + 2));\n\n  Int ints1[] = {{0}, {1}, {2}};\n  Int ints2[] = {{4}, {5}, {6}};\n\n  EXPECT_EQ(array3 + 15, ranges::find_end(array3, ints1, ranges::equal_to{},\n                                          identity{}, &Int::value));\n  EXPECT_EQ(ranges::end(array3),\n            ranges::find_end(array3, ints2, ranges::equal_to{}, identity{},\n                             &Int::value));\n}\n\nTEST(RangesTest, FindFirstOf) {\n  int array1[] = {1, 2, 3};\n  int array2[] = {7, 8, 9};\n  int array3[] = {0, 1, 2, 3, 4, 5, 0, 1, 2, 3};\n\n  EXPECT_EQ(array3 + 1, ranges::find_first_of(array3, ranges::end(array3),\n                                              array1, ranges::end(array1)));\n  EXPECT_EQ(ranges::end(array3),\n            ranges::find_first_of(array3, ranges::end(array3), array2,\n                                  ranges::end(array2)));\n  Int ints1[] = {{1}, {2}, {3}};\n  Int ints2[] = {{7}, {8}, {9}};\n\n  EXPECT_EQ(array3 + 1, ranges::find_first_of(array3, ints1, ranges::equal_to{},\n                                              identity{}, &Int::value));\n  EXPECT_EQ(ranges::end(array3),\n            ranges::find_first_of(array3, ints2, ranges::equal_to{}, identity{},\n                                  &Int::value));\n}\n\nTEST(RangesTest, AdjacentFind) {\n  constexpr int array[] = {1, 2, 3, 3};\n  static_assert(array + 2 == ranges::adjacent_find(array, ranges::end(array)),\n                \"\");\n  static_assert(\n      array == ranges::adjacent_find(array, ranges::end(array), ranges::less{}),\n      \"\");\n\n  constexpr Int ints[] = {{6}, {6}, {5}, {4}};\n  static_assert(\n      ints == ranges::adjacent_find(ints, ranges::equal_to{}, &Int::value), \"\");\n  static_assert(ranges::end(ints) ==\n                    ranges::adjacent_find(ints, ranges::less{}, &Int::value),\n                \"\");\n}\n\nTEST(RangesTest, Count) {\n  int array[] = {1, 2, 3, 3};\n  EXPECT_EQ(1, ranges::count(array, array + 4, 1));\n  EXPECT_EQ(1, ranges::count(array, array + 4, 2));\n  EXPECT_EQ(1, ranges::count(array, array + 3, 3));\n  EXPECT_EQ(2, ranges::count(array, array + 4, 3));\n\n  Int ints[] = {{1}, {2}, {3}, {3}};\n  EXPECT_EQ(1, ranges::count(ints, 1, &Int::value));\n  EXPECT_EQ(1, ranges::count(ints, 2, &Int::value));\n  EXPECT_EQ(2, ranges::count(ints, 3, &Int::value));\n}\n\nTEST(RangesTest, CountIf) {\n  int array[] = {1, 2, 3, 3};\n  EXPECT_EQ(0, ranges::count_if(array, array + 1, is_even));\n  EXPECT_EQ(1, ranges::count_if(array, array + 2, is_even));\n  EXPECT_EQ(1, ranges::count_if(array, array + 3, is_even));\n  EXPECT_EQ(1, ranges::count_if(array, array + 4, is_even));\n\n  Int ints[] = {{1}, {2}, {3}, {3}};\n  EXPECT_EQ(1, ranges::count_if(ints, is_even, &Int::value));\n  EXPECT_EQ(3, ranges::count_if(ints, is_odd, &Int::value));\n}\n\nTEST(RangesTest, Mismatch) {\n  int array1[] = {1, 3, 6, 7};\n  int array2[] = {1, 3};\n  int array3[] = {1, 3, 5, 7};\n  EXPECT_EQ(std::make_pair(array1 + 2, array2 + 2),\n            ranges::mismatch(array1, array1 + 4, array2, array2 + 2));\n  EXPECT_EQ(std::make_pair(array1 + 2, array3 + 2),\n            ranges::mismatch(array1, array1 + 4, array3, array3 + 4));\n\n  EXPECT_EQ(std::make_pair(array1 + 2, array2 + 2),\n            ranges::mismatch(array1, array2));\n  EXPECT_EQ(std::make_pair(array1 + 2, array3 + 2),\n            ranges::mismatch(array1, array3));\n}\n\nTEST(RangesTest, Equal) {\n  static constexpr int array1[] = {1, 3, 6, 7};\n  static constexpr int array2[] = {1, 3, 5, 7};\n\n  static_assert(ranges::equal(array1, array1 + 2, array2, array2 + 2), \"\");\n  EXPECT_TRUE(ranges::equal(array1, array1 + 2, array2, array2 + 2));\n\n  static_assert(!ranges::equal(array1, array1 + 4, array2, array2 + 4), \"\");\n  EXPECT_FALSE(ranges::equal(array1, array1 + 4, array2, array2 + 4));\n\n  static_assert(!ranges::equal(array1, array1 + 2, array2, array2 + 3), \"\");\n  EXPECT_FALSE(ranges::equal(array1, array1 + 2, array2, array2 + 3));\n\n  static constexpr Int ints[] = {{1}, {3}, {5}, {7}};\n\n  CONSTEXPR_LAMBDA((Int lhs, int rhs) { return lhs.value == rhs; }) lambda;\n  static_assert(ranges::equal(ints, array2, lambda), \"\");\n  EXPECT_TRUE(ranges::equal(ints, array2, lambda));\n\n  static_assert(\n      ranges::equal(array2, ints, ranges::equal_to{}, identity{}, &Int::value),\n      \"\");\n  EXPECT_TRUE(\n      ranges::equal(array2, ints, ranges::equal_to{}, identity{}, &Int::value));\n}\n\nTEST(RangesTest, IsPermutation) {\n  int array1[] = {1, 3, 6, 7};\n  int array2[] = {7, 3, 1, 6};\n  int array3[] = {1, 3, 5, 7};\n\n  EXPECT_TRUE(ranges::is_permutation(array1, array1 + 4, array2, array2 + 4));\n  EXPECT_FALSE(ranges::is_permutation(array1, array1 + 4, array3, array3 + 4));\n\n  EXPECT_TRUE(ranges::is_permutation(array1, array2));\n  EXPECT_FALSE(ranges::is_permutation(array1, array3));\n\n  Int ints1[] = {{1}, {3}, {5}, {7}};\n  Int ints2[] = {{1}, {5}, {3}, {7}};\n  EXPECT_TRUE(ranges::is_permutation(\n      ints1, ints2, [](Int lhs, Int rhs) { return lhs.value == rhs.value; }));\n\n  EXPECT_TRUE(\n      ranges::is_permutation(ints1, ints2, ranges::equal_to{}, &Int::value));\n\n  EXPECT_FALSE(ranges::is_permutation(array1, ints2, ranges::equal_to{}, {},\n                                      &Int::value));\n  EXPECT_TRUE(ranges::is_permutation(array3, ints2, ranges::equal_to{}, {},\n                                     &Int::value));\n}\n\nTEST(RangesTest, Search) {\n  int array1[] = {0, 1, 2, 3};\n  int array2[] = {0, 1, 5, 3};\n  int array3[] = {0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4};\n\n  EXPECT_EQ(array3 + 3,\n            ranges::search(array3, array3 + 12, array1, array1 + 4));\n  EXPECT_EQ(array3 + 12,\n            ranges::search(array3, array3 + 12, array2, array2 + 4));\n\n  EXPECT_EQ(array3 + 3, ranges::search(array3, array1));\n  EXPECT_EQ(array3 + 12, ranges::search(array3, array2));\n\n  Int ints1[] = {{0}, {1}, {2}, {3}};\n  Int ints2[] = {{0}, {1}, {5}, {3}};\n\n  EXPECT_EQ(ints1 + 4, ranges::search(ints1, ints2, ranges::equal_to{},\n                                      &Int::value, &Int::value));\n\n  EXPECT_EQ(array3 + 3, ranges::search(array3, ints1, {}, {}, &Int::value));\n  EXPECT_EQ(array3 + 12, ranges::search(array3, ints2, {}, {}, &Int::value));\n}\n\nTEST(RangesTest, SearchN) {\n  int array[] = {0, 0, 1, 1, 2, 2};\n\n  EXPECT_EQ(array, ranges::search_n(array, array + 6, 1, 0));\n  EXPECT_EQ(array + 2, ranges::search_n(array, array + 6, 1, 1));\n  EXPECT_EQ(array + 4, ranges::search_n(array, array + 6, 1, 2));\n  EXPECT_EQ(array + 6, ranges::search_n(array, array + 6, 1, 3));\n\n  EXPECT_EQ(array, ranges::search_n(array, array + 6, 2, 0));\n  EXPECT_EQ(array + 2, ranges::search_n(array, array + 6, 2, 1));\n  EXPECT_EQ(array + 4, ranges::search_n(array, array + 6, 2, 2));\n  EXPECT_EQ(array + 6, ranges::search_n(array, array + 6, 2, 3));\n\n  EXPECT_EQ(array + 6, ranges::search_n(array, array + 6, 3, 0));\n  EXPECT_EQ(array + 6, ranges::search_n(array, array + 6, 3, 1));\n  EXPECT_EQ(array + 6, ranges::search_n(array, array + 6, 3, 2));\n  EXPECT_EQ(array + 6, ranges::search_n(array, array + 6, 3, 3));\n\n  Int ints[] = {{0}, {0}, {1}, {1}, {2}, {2}};\n  EXPECT_EQ(ints, ranges::search_n(ints, 1, 0, {}, &Int::value));\n  EXPECT_EQ(ints + 2, ranges::search_n(ints, 1, 1, {}, &Int::value));\n  EXPECT_EQ(ints + 4, ranges::search_n(ints, 1, 2, {}, &Int::value));\n  EXPECT_EQ(ints + 6, ranges::search_n(ints, 1, 3, {}, &Int::value));\n\n  EXPECT_EQ(ints, ranges::search_n(ints, 2, 0, {}, &Int::value));\n  EXPECT_EQ(ints + 2, ranges::search_n(ints, 2, 1, {}, &Int::value));\n  EXPECT_EQ(ints + 4, ranges::search_n(ints, 2, 2, {}, &Int::value));\n  EXPECT_EQ(ints + 6, ranges::search_n(ints, 2, 3, {}, &Int::value));\n\n  EXPECT_EQ(ints + 6, ranges::search_n(ints, 3, 0, {}, &Int::value));\n  EXPECT_EQ(ints + 6, ranges::search_n(ints, 3, 1, {}, &Int::value));\n  EXPECT_EQ(ints + 6, ranges::search_n(ints, 3, 2, {}, &Int::value));\n  EXPECT_EQ(ints + 6, ranges::search_n(ints, 3, 3, {}, &Int::value));\n}\n\nTEST(RangesTest, Copy) {\n  int input[] = {1, 2, 3, 4, 5};\n  int output[] = {6, 6, 6, 6, 6, 6, 6};\n  auto equals_six = [](int i) { return i == 6; };\n\n  EXPECT_EQ(output + 3, ranges::copy(input, input + 3, output));\n  EXPECT_TRUE(std::equal(input, input + 3, output, output + 3));\n  EXPECT_TRUE(std::all_of(output + 3, output + 7, equals_six));\n\n  EXPECT_EQ(output + 5, ranges::copy(input, output));\n  EXPECT_TRUE(std::equal(input, input + 5, output, output + 5));\n  EXPECT_TRUE(std::all_of(output + 5, output + 7, equals_six));\n}\n\nTEST(RangesTest, CopyN) {\n  int input[] = {1, 2, 3, 4, 5};\n  int output[] = {6, 6, 6, 6, 6, 6, 6};\n  auto equals_six = [](int i) { return i == 6; };\n\n  EXPECT_EQ(output + 4, ranges::copy_n(input, 4, output));\n  EXPECT_TRUE(std::equal(input, input + 4, output, output + 4));\n  EXPECT_TRUE(std::all_of(output + 4, output + 7, equals_six));\n}\n\nTEST(RangesTest, CopyIf) {\n  int input[] = {2, 4, 6, 8, 6};\n  int output[] = {0, 0, 0, 0, 0, 0};\n  auto equals_six = [](int i) { return i == 6; };\n  auto equals_zero = [](int i) { return i == 0; };\n\n  EXPECT_EQ(output + 1, ranges::copy_if(input, input + 4, output, equals_six));\n  EXPECT_TRUE(std::all_of(output, output + 1, equals_six));\n  EXPECT_TRUE(std::all_of(output + 1, output + 6, equals_zero));\n\n  Int ints_in[] = {{2}, {4}, {6}, {8}, {6}};\n  Int ints_out[] = {{0}, {0}, {0}, {0}, {0}, {0}};\n  EXPECT_EQ(ints_out + 2,\n            ranges::copy_if(ints_in, ints_out, equals_six, &Int::value));\n\n  EXPECT_TRUE(ranges::all_of(ints_out, ints_out + 2, equals_six, &Int::value));\n  EXPECT_TRUE(\n      ranges::all_of(ints_out + 2, ints_out + 6, equals_zero, &Int::value));\n}\n\nTEST(RangesTest, CopyBackward) {\n  int input[] = {2, 4, 6, 8, 6};\n  int output[] = {0, 0, 0, 0, 0, 0};\n\n  EXPECT_EQ(output + 1, ranges::copy_backward(input, input + 5, output + 6));\n  EXPECT_THAT(output, ElementsAre(0, 2, 4, 6, 8, 6));\n\n  Int ints_in[] = {{2}, {4}, {6}, {8}, {6}};\n  Int ints_out[] = {{0}, {0}, {0}, {0}, {0}, {0}};\n\n  EXPECT_EQ(ints_out, ranges::copy_backward(ints_in, ints_out + 5));\n  EXPECT_TRUE(std::equal(ints_in, ints_in + 5, ints_out, ints_out + 5,\n                         [](Int i, Int j) { return i.value == j.value; }));\n}\n\nTEST(RangesTest, Move) {\n  MoveOnlyInt input[] = {6, 6, 6, 6, 6};\n  MoveOnlyInt output[] = {0, 0, 0, 0, 0};\n  auto equals_zero = [](const auto& i) { return i.value == 0; };\n  auto equals_six = [](const auto& i) { return i.value == 6; };\n\n  EXPECT_EQ(output + 3, ranges::move(input, input + 3, output));\n  EXPECT_TRUE(std::all_of(input, input + 3, equals_zero));\n  EXPECT_TRUE(std::all_of(input + 3, input + 5, equals_six));\n  EXPECT_TRUE(std::all_of(output, output + 3, equals_six));\n  EXPECT_TRUE(std::all_of(output + 3, output + 5, equals_zero));\n\n  for (auto& in : input) in = 6;\n\n  EXPECT_EQ(output + 5, ranges::move(input, output));\n  EXPECT_TRUE(ranges::all_of(input, equals_zero));\n  EXPECT_TRUE(ranges::all_of(output, equals_six));\n}\n\nTEST(RangesTest, MoveBackward) {\n  MoveOnlyInt input[] = {6, 6, 6, 6, 6};\n  MoveOnlyInt output[] = {0, 0, 0, 0, 0};\n  auto equals_zero = [](const auto& i) { return i.value == 0; };\n  auto equals_six = [](const auto& i) { return i.value == 6; };\n\n  EXPECT_EQ(output + 2, ranges::move_backward(input, input + 3, output + 5));\n  EXPECT_TRUE(std::all_of(input, input + 3, equals_zero));\n  EXPECT_TRUE(std::all_of(input + 3, input + 5, equals_six));\n  EXPECT_TRUE(std::all_of(output, output + 2, equals_zero));\n  EXPECT_TRUE(std::all_of(output + 2, output + 5, equals_six));\n\n  for (auto& in : input) in = 6;\n\n  EXPECT_EQ(output, ranges::move_backward(input, output + 5));\n  EXPECT_TRUE(ranges::all_of(input, equals_zero));\n  EXPECT_TRUE(ranges::all_of(output, equals_six));\n}\n\nTEST(RangesTest, SwapRanges) {\n  int ints1[] = {0, 0, 0, 0, 0};\n  int ints2[] = {6, 6, 6, 6, 6};\n\n  // Test that swap_ranges does not exceed `last2`.\n  EXPECT_EQ(ints2 + 3, ranges::swap_ranges(ints1, ints1 + 5, ints2, ints2 + 3));\n  EXPECT_THAT(ints1, ElementsAre(6, 6, 6, 0, 0));\n  EXPECT_THAT(ints2, ElementsAre(0, 0, 0, 6, 6));\n\n  // Test that swap_ranges does not exceed `last1`.\n  EXPECT_EQ(ints2 + 3, ranges::swap_ranges(ints1, ints1 + 3, ints2, ints2 + 5));\n  EXPECT_THAT(ints1, ElementsAre(0, 0, 0, 0, 0));\n  EXPECT_THAT(ints2, ElementsAre(6, 6, 6, 6, 6));\n\n  EXPECT_EQ(ints2 + 5,\n            ranges::swap_ranges(ints1 + 3, ints1 + 5, ints2 + 3, ints2 + 5));\n  EXPECT_THAT(ints1, ElementsAre(0, 0, 0, 6, 6));\n  EXPECT_THAT(ints2, ElementsAre(6, 6, 6, 0, 0));\n\n  EXPECT_EQ(ints2 + 5, ranges::swap_ranges(ints1, ints2));\n  EXPECT_THAT(ints1, ElementsAre(6, 6, 6, 0, 0));\n  EXPECT_THAT(ints2, ElementsAre(0, 0, 0, 6, 6));\n}\n\nTEST(RangesTest, UnaryTransform) {\n  int input[] = {1, 2, 3, 4, 5};\n  auto plus_1 = [](int i) { return i + 1; };\n  auto times_2 = [](int i) { return i * 2; };\n\n  EXPECT_EQ(input + 4,\n            ranges::transform(input + 1, input + 4, input + 1, plus_1));\n  EXPECT_THAT(input, ElementsAre(1, 3, 4, 5, 5));\n\n  int output[] = {0, 0, 0, 0, 0};\n  EXPECT_EQ(output + 3,\n            ranges::transform(input + 1, input + 4, output, times_2));\n  EXPECT_THAT(output, ElementsAre(6, 8, 10, 0, 0));\n\n  Int values[] = {{0}, {2}, {4}, {5}};\n  EXPECT_EQ(values + 4,\n            ranges::transform(values, values, times_2, &Int::value));\n  EXPECT_THAT(values, ElementsAre(Int{0}, Int{4}, Int{8}, Int{10}));\n}\n\nTEST(RangesTest, BinaryTransform) {\n  int input[] = {1, 2, 3, 4, 5};\n  int output[] = {0, 0, 0, 0, 0};\n\n  EXPECT_EQ(output + 2, ranges::transform(input, input + 2, input + 3,\n                                          input + 5, output, std::plus<>{}));\n  EXPECT_THAT(output, ElementsAre(5, 7, 0, 0, 0));\n\n  EXPECT_EQ(output + 5,\n            ranges::transform(input, input, output, std::multiplies<>{}));\n  EXPECT_THAT(output, ElementsAre(1, 4, 9, 16, 25));\n\n  Int values[] = {{0}, {2}, {4}, {5}};\n  EXPECT_EQ(values + 4,\n            ranges::transform(values, values, values, std::minus<>{},\n                              &Int::value, &Int::value));\n  EXPECT_THAT(values, ElementsAre(Int{0}, Int{0}, Int{0}, Int{0}));\n}\n\nTEST(RangesTest, Replace) {\n  int input[] = {0, 0, 0, 0, 0};\n\n  EXPECT_EQ(input + 2, ranges::replace(input, input + 2, 0, 2));\n  EXPECT_THAT(input, ElementsAre(2, 2, 0, 0, 0));\n\n  EXPECT_EQ(input + 5, ranges::replace(input, 0, 3));\n  EXPECT_THAT(input, ElementsAre(2, 2, 3, 3, 3));\n}\n\nTEST(RangesTest, ReplaceIf) {\n  int input[] = {0, 1, 2, 3, 4};\n\n  EXPECT_EQ(input + 3, ranges::replace_if(input, input + 3, is_even, 9));\n  EXPECT_THAT(input, ElementsAre(9, 1, 9, 3, 4));\n\n  EXPECT_EQ(input + 5, ranges::replace_if(input, is_odd, 0));\n  EXPECT_THAT(input, ElementsAre(0, 0, 0, 0, 4));\n\n  Int ints[] = {0, 0, 1, 1, 0};\n  EXPECT_EQ(ints + 5, ranges::replace_if(ints, is_odd, 3, &Int::value));\n  EXPECT_THAT(ints, ElementsAre(0, 0, 3, 3, 0));\n}\n\nTEST(RangesTest, ReplaceCopy) {\n  int input[] = {0, 0, 0, 0, 0};\n  int output[] = {1, 1, 1, 1, 1};\n\n  EXPECT_EQ(input + 2, ranges::replace_copy(input, input + 2, output, 0, 2));\n  EXPECT_THAT(input, ElementsAre(0, 0, 0, 0, 0));\n  EXPECT_THAT(output, ElementsAre(2, 2, 1, 1, 1));\n\n  EXPECT_EQ(input + 5, ranges::replace_copy(input, output, 0, 3));\n  EXPECT_THAT(input, ElementsAre(0, 0, 0, 0, 0));\n  EXPECT_THAT(output, ElementsAre(3, 3, 3, 3, 3));\n}\n\nTEST(RangesTest, ReplaceCopyIf) {\n  Int input[] = {0, 1, 2, 3, 4};\n  Int output[] = {0, 0, 0, 0, 0};\n\n  EXPECT_EQ(output + 3, ranges::replace_copy_if(input, input + 3, output,\n                                                is_even, 9, &Int::value));\n  EXPECT_THAT(input, ElementsAre(0, 1, 2, 3, 4));\n  EXPECT_THAT(output, ElementsAre(9, 1, 9, 0, 0));\n\n  EXPECT_EQ(output + 5,\n            ranges::replace_copy_if(input, output, is_odd, 0, &Int::value));\n  EXPECT_THAT(output, ElementsAre(0, 0, 2, 0, 4));\n}\n\nTEST(RangesTest, Fill) {\n  int input[] = {1, 2, 3, 4, 5};\n\n  EXPECT_EQ(input + 3, ranges::fill(input, input + 3, 0));\n  EXPECT_THAT(input, ElementsAre(0, 0, 0, 4, 5));\n\n  EXPECT_EQ(input + 5, ranges::fill(input, 1));\n  EXPECT_THAT(input, ElementsAre(1, 1, 1, 1, 1));\n}\n\nTEST(RangesTest, FillN) {\n  int input[] = {0, 0, 0, 0, 0};\n\n  EXPECT_EQ(input + 5, ranges::fill_n(input, 5, 5));\n  EXPECT_THAT(input, ElementsAre(5, 5, 5, 5, 5));\n\n  EXPECT_EQ(input + 3, ranges::fill_n(input, 3, 3));\n  EXPECT_THAT(input, ElementsAre(3, 3, 3, 5, 5));\n}\n\nTEST(RangesTest, Generate) {\n  int input[] = {0, 0, 0, 0, 0};\n\n  auto gen = [count = 0]() mutable { return ++count; };\n  EXPECT_EQ(input + 3, ranges::generate(input, input + 3, gen));\n  EXPECT_THAT(input, ElementsAre(1, 2, 3, 0, 0));\n\n  EXPECT_EQ(input + 5, ranges::generate(input, gen));\n  EXPECT_THAT(input, ElementsAre(1, 2, 3, 4, 5));\n}\n\nTEST(RangesTest, GenerateN) {\n  int input[] = {0, 0, 0, 0, 0};\n\n  auto gen = [count = 0]() mutable { return ++count; };\n  EXPECT_EQ(input + 4, ranges::generate_n(input, 4, gen));\n  EXPECT_THAT(input, ElementsAre(1, 2, 3, 4, 0));\n}\n\nTEST(RangesTest, Remove) {\n  int input[] = {1, 0, 1, 1, 0};\n\n  EXPECT_EQ(input + 3, ranges::remove(input + 1, input + 5, 1));\n  EXPECT_EQ(input[0], 1);\n  EXPECT_EQ(input[1], 0);\n  EXPECT_EQ(input[2], 0);\n\n  Int ints[] = {2, 2, 1, 1, 2, 2};\n  EXPECT_EQ(ints + 2, ranges::remove(ints, 2, &Int::value));\n  EXPECT_EQ(ints[0], 1);\n  EXPECT_EQ(ints[1], 1);\n}\n\nTEST(RangesTest, RemoveIf) {\n  int input[] = {0, 1, 2, 3, 4};\n\n  EXPECT_EQ(input + 2, ranges::remove_if(input, input + 4, is_even));\n  EXPECT_EQ(input[0], 1);\n  EXPECT_EQ(input[1], 3);\n  EXPECT_EQ(input[4], 4);\n\n  Int ints[] = {2, 2, 1, 1, 2, 2};\n  EXPECT_EQ(ints + 2, ranges::remove_if(ints, is_even, &Int::value));\n  EXPECT_EQ(ints[0], 1);\n  EXPECT_EQ(ints[1], 1);\n}\n\nTEST(RangesTest, RemoveCopy) {\n  int input[] = {0, 1, 2, 3, 4};\n  int output[] = {0, 0, 0, 0, 0};\n\n  EXPECT_EQ(output + 1, ranges::remove_copy(input, input + 2, output, 0));\n  EXPECT_THAT(input, ElementsAre(0, 1, 2, 3, 4));\n  EXPECT_THAT(output, ElementsAre(1, 0, 0, 0, 0));\n\n  EXPECT_EQ(output + 4, ranges::remove_copy(input, output, 4));\n  EXPECT_THAT(input, ElementsAre(0, 1, 2, 3, 4));\n  EXPECT_THAT(output, ElementsAre(0, 1, 2, 3, 0));\n}\n\nTEST(RangesTest, RemovCopyIf) {\n  Int input[] = {0, 1, 2, 3, 4};\n  Int output[] = {0, 0, 0, 0, 0};\n\n  EXPECT_EQ(output + 2, ranges::remove_copy_if(input, input + 4, output,\n                                               is_even, &Int::value));\n  EXPECT_THAT(input, ElementsAre(0, 1, 2, 3, 4));\n  EXPECT_THAT(output, ElementsAre(1, 3, 0, 0, 0));\n\n  EXPECT_EQ(output + 3,\n            ranges::remove_copy_if(input, output, is_odd, &Int::value));\n  EXPECT_THAT(input, ElementsAre(0, 1, 2, 3, 4));\n  EXPECT_THAT(output, ElementsAre(0, 2, 4, 0, 0));\n}\n\nTEST(RangesTest, Unique) {\n  int input[] = {0, 0, 1, 1, 2};\n\n  EXPECT_EQ(input + 2, ranges::unique(input, input + 3));\n  EXPECT_EQ(input[0], 0);\n  EXPECT_EQ(input[1], 1);\n  EXPECT_EQ(input[3], 1);\n  EXPECT_EQ(input[4], 2);\n\n  Int ints[] = {2, 2, 1, 1, 2, 2};\n  EXPECT_EQ(ints + 3, ranges::unique(ints, {}, &Int::value));\n  EXPECT_EQ(ints[0], 2);\n  EXPECT_EQ(ints[1], 1);\n  EXPECT_EQ(ints[2], 2);\n}\n\nTEST(RangesTest, UniqueCopy) {\n  Int input[] = {0, 0, 1, 2, 2};\n  Int output[] = {0, 0, 0, 0, 0};\n\n  EXPECT_EQ(output + 3,\n            ranges::unique_copy(input, input + 4, output, {}, &Int::value));\n  EXPECT_THAT(input, ElementsAre(0, 0, 1, 2, 2));\n  EXPECT_THAT(output, ElementsAre(0, 1, 2, 0, 0));\n\n  EXPECT_EQ(output + 3, ranges::unique_copy(input, output, {}, &Int::value));\n  EXPECT_THAT(input, ElementsAre(0, 0, 1, 2, 2));\n  EXPECT_THAT(output, ElementsAre(0, 1, 2, 0, 0));\n}\n\nTEST(RangesTest, Reverse) {\n  int input[] = {0, 1, 2, 3, 4};\n\n  EXPECT_EQ(input + 4, ranges::reverse(input + 2, input + 4));\n  EXPECT_THAT(input, ElementsAre(0, 1, 3, 2, 4));\n\n  EXPECT_EQ(input + 5, ranges::reverse(input));\n  EXPECT_THAT(input, ElementsAre(4, 2, 3, 1, 0));\n}\n\nTEST(RangesTest, ReverseCopy) {\n  int input[] = {0, 1, 2, 3, 4};\n  int output[] = {0, 0, 0, 0, 0};\n\n  EXPECT_EQ(output + 2, ranges::reverse_copy(input + 2, input + 4, output));\n  EXPECT_THAT(input, ElementsAre(0, 1, 2, 3, 4));\n  EXPECT_THAT(output, ElementsAre(3, 2, 0, 0, 0));\n\n  EXPECT_EQ(output + 5, ranges::reverse_copy(input, output));\n  EXPECT_THAT(input, ElementsAre(0, 1, 2, 3, 4));\n  EXPECT_THAT(output, ElementsAre(4, 3, 2, 1, 0));\n}\n\nTEST(RangesTest, Rotate) {\n  int input[] = {0, 1, 2, 3, 4};\n\n  EXPECT_EQ(input + 3, ranges::rotate(input + 2, input + 3, input + 4));\n  EXPECT_THAT(input, ElementsAre(0, 1, 3, 2, 4));\n\n  EXPECT_EQ(input + 3, ranges::rotate(input, input + 2));\n  EXPECT_THAT(input, ElementsAre(3, 2, 4, 0, 1));\n}\n\nTEST(RangesTest, RotateCopy) {\n  int input[] = {0, 1, 2, 3, 4};\n  int output[] = {0, 0, 0, 0, 0};\n\n  EXPECT_EQ(output + 2,\n            ranges::rotate_copy(input + 2, input + 3, input + 4, output));\n  EXPECT_THAT(input, ElementsAre(0, 1, 2, 3, 4));\n  EXPECT_THAT(output, ElementsAre(3, 2, 0, 0, 0));\n\n  EXPECT_EQ(output + 5, ranges::rotate_copy(input, input + 3, output));\n  EXPECT_THAT(input, ElementsAre(0, 1, 2, 3, 4));\n  EXPECT_THAT(output, ElementsAre(3, 4, 0, 1, 2));\n}\n\nTEST(RangesTest, Shuffle) {\n  int input[] = {0, 1, 2, 3, 4};\n\n  // Shuffles input[2] and input[3], thus we can't be certain about their\n  // positions.\n  EXPECT_EQ(input + 4, ranges::shuffle(input + 2, input + 4,\n                                       std::default_random_engine()));\n  EXPECT_EQ(input[0], 0);\n  EXPECT_EQ(input[1], 1);\n  EXPECT_EQ(input[4], 4);\n  EXPECT_THAT(input, testing::UnorderedElementsAre(0, 1, 2, 3, 4));\n\n  EXPECT_EQ(input + 5, ranges::shuffle(input, std::default_random_engine()));\n  EXPECT_THAT(input, testing::UnorderedElementsAre(0, 1, 2, 3, 4));\n}\n\nTEST(RangesTest, Sort) {\n  int input[] = {3, 1, 2, 0, 4};\n  EXPECT_EQ(input + 4, ranges::sort(input, input + 4));\n  EXPECT_THAT(input, ElementsAre(0, 1, 2, 3, 4));\n\n  EXPECT_EQ(input + 5, ranges::sort(input, input + 5, ranges::greater()));\n  EXPECT_THAT(input, ElementsAre(4, 3, 2, 1, 0));\n\n  Int ints[] = {6, 7, 9, 8, 5};\n  EXPECT_EQ(ints + 5, ranges::sort(ints, {}, &Int::value));\n  EXPECT_THAT(ints, ElementsAre(5, 6, 7, 8, 9));\n\n  EXPECT_EQ(ints + 5, ranges::sort(ints, ranges::greater(), &Int::value));\n  EXPECT_THAT(ints, ElementsAre(9, 8, 7, 6, 5));\n}\n\nTEST(RangesTest, StableSort) {\n  // Integer divide each element by 2 to check stability of elements that\n  // compare equal.\n  auto idiv2 = [](int i) { return i / 2; };\n\n  int input[] = {3, 1, 2, 0, 4};\n  EXPECT_EQ(input + 4, ranges::stable_sort(input, input + 4, {}, idiv2));\n  EXPECT_THAT(input, ElementsAre(1, 0, 3, 2, 4));\n\n  EXPECT_EQ(input + 5,\n            ranges::stable_sort(input, input + 5, ranges::greater()));\n  EXPECT_THAT(input, ElementsAre(4, 3, 2, 1, 0));\n\n  auto Idiv2 = [](Int i) { return i.value / 2; };\n  Int ints[] = {6, 7, 9, 8, 5};\n  EXPECT_EQ(ints + 5, ranges::stable_sort(ints, {}, Idiv2));\n  EXPECT_THAT(ints, ElementsAre(5, 6, 7, 9, 8));\n\n  EXPECT_EQ(ints + 5, ranges::stable_sort(ints, ranges::greater(), Idiv2));\n  EXPECT_THAT(ints, ElementsAre(9, 8, 6, 7, 5));\n}\n\nTEST(RangesTest, PartialSort) {\n  int input[] = {3, 1, 2, 0, 4};\n  EXPECT_EQ(input + 4, ranges::partial_sort(input, input + 2, input + 4));\n  EXPECT_EQ(input[0], 0);\n  EXPECT_EQ(input[1], 1);\n\n  EXPECT_EQ(input + 5, ranges::partial_sort(input, input + 3, input + 5,\n                                            ranges::greater()));\n  EXPECT_EQ(input[0], 4);\n  EXPECT_EQ(input[1], 3);\n  EXPECT_EQ(input[2], 2);\n\n  Int ints[] = {6, 7, 9, 8, 5};\n  EXPECT_EQ(ints + 5, ranges::partial_sort(ints, ints + 4, {}, &Int::value));\n  EXPECT_EQ(ints[0], 5);\n  EXPECT_EQ(ints[1], 6);\n  EXPECT_EQ(ints[2], 7);\n  EXPECT_EQ(ints[3], 8);\n\n  EXPECT_EQ(ints + 5, ranges::partial_sort(ints, ints + 3, ranges::greater(),\n                                           &Int::value));\n  EXPECT_EQ(ints[0], 9);\n  EXPECT_EQ(ints[1], 8);\n  EXPECT_EQ(ints[2], 7);\n}\n\nTEST(RangesTest, PartialSortCopy) {\n  int input[] = {3, 1, 2, 0, 4};\n  int output[] = {0, 0, 0, 0, 0};\n  EXPECT_EQ(output + 2,\n            ranges::partial_sort_copy(input, input + 2, output, output + 4));\n  EXPECT_THAT(input, ElementsAre(3, 1, 2, 0, 4));\n  EXPECT_THAT(output, ElementsAre(1, 3, 0, 0, 0));\n\n  EXPECT_EQ(output + 5,\n            ranges::partial_sort_copy(input, input + 3, output + 3, output + 5,\n                                      ranges::greater()));\n  EXPECT_THAT(input, ElementsAre(3, 1, 2, 0, 4));\n  EXPECT_THAT(output, ElementsAre(1, 3, 0, 3, 2));\n\n  Int ints[] = {3, 1, 2, 0, 4};\n  Int outs[] = {0, 0, 0};\n  EXPECT_EQ(outs + 3, ranges::partial_sort_copy(ints, outs, {}, &Int::value,\n                                                &Int::value));\n  EXPECT_THAT(ints, ElementsAre(3, 1, 2, 0, 4));\n  EXPECT_THAT(outs, ElementsAre(0, 1, 2));\n\n  EXPECT_EQ(outs + 3, ranges::partial_sort_copy(ints, outs, ranges::greater(),\n                                                &Int::value, &Int::value));\n  EXPECT_THAT(ints, ElementsAre(3, 1, 2, 0, 4));\n  EXPECT_THAT(outs, ElementsAre(4, 3, 2));\n\n  EXPECT_EQ(outs + 3,\n            ranges::partial_sort_copy(input, outs, {}, {}, &Int::value));\n}\n\nTEST(RangesTest, IsSorted) {\n  constexpr int input[] = {3, 1, 2, 0, 4};\n  static_assert(ranges::is_sorted(input + 1, input + 3), \"\");\n  static_assert(!ranges::is_sorted(input + 1, input + 4), \"\");\n  static_assert(ranges::is_sorted(input, input + 2, ranges::greater()), \"\");\n\n  constexpr Int ints[] = {0, 1, 2, 3, 4};\n  static_assert(ranges::is_sorted(ints, {}, &Int::value), \"\");\n  static_assert(!ranges::is_sorted(ints, ranges::greater(), &Int::value), \"\");\n}\n\nTEST(RangesTest, IsSortedUntil) {\n  constexpr int input[] = {3, 1, 2, 0, 4};\n  static_assert(input + 3 == ranges::is_sorted_until(input + 1, input + 3), \"\");\n  static_assert(input + 3 == ranges::is_sorted_until(input + 1, input + 4), \"\");\n  static_assert(\n      input + 2 == ranges::is_sorted_until(input, input + 2, ranges::greater()),\n      \"\");\n\n  constexpr Int ints[] = {0, 1, 2, 3, 4};\n  static_assert(ints + 5 == ranges::is_sorted_until(ints, {}, &Int::value), \"\");\n  static_assert(\n      ints + 1 == ranges::is_sorted_until(ints, ranges::greater(), &Int::value),\n      \"\");\n}\n\nTEST(RangesTest, NthElement) {\n  int input[] = {3, 1, 2, 0, 4};\n  EXPECT_EQ(input + 5, ranges::nth_element(input, input + 2, input + 5));\n  EXPECT_THAT(input, ElementsAre(Lt(2), Lt(2), 2, Gt(2), Gt(2)));\n\n  Int ints[] = {0, 1, 2, 3, 4};\n  EXPECT_EQ(ints + 5, ranges::nth_element(ints, ints + 2, ranges::greater(),\n                                          &Int::value));\n  EXPECT_THAT(ints, ElementsAre(Gt(2), Gt(2), 2, Lt(2), Lt(2)));\n}\n\nTEST(RangesTest, LowerBound) {\n  int array[] = {0, 0, 1, 1, 2, 2};\n\n  EXPECT_EQ(array, ranges::lower_bound(array, array + 6, -1));\n  EXPECT_EQ(array, ranges::lower_bound(array, array + 6, 0));\n  EXPECT_EQ(array + 2, ranges::lower_bound(array, array + 6, 1));\n  EXPECT_EQ(array + 4, ranges::lower_bound(array, array + 6, 2));\n  EXPECT_EQ(array + 6, ranges::lower_bound(array, array + 6, 3));\n\n  Int ints[] = {0, 0, 1, 1, 2, 2};\n\n  EXPECT_EQ(ints, ranges::lower_bound(ints, -1, {}, &Int::value));\n  EXPECT_EQ(ints, ranges::lower_bound(ints, 0, {}, &Int::value));\n  EXPECT_EQ(ints + 2, ranges::lower_bound(ints, 1, {}, &Int::value));\n  EXPECT_EQ(ints + 4, ranges::lower_bound(ints, 2, {}, &Int::value));\n  EXPECT_EQ(ints + 6, ranges::lower_bound(ints, 3, {}, &Int::value));\n\n  const auto proj = [](const Int& i) { return 2 - i.value; };\n  EXPECT_EQ(ints, ranges::lower_bound(ints, 3, ranges::greater{}, proj));\n  EXPECT_EQ(ints, ranges::lower_bound(ints, 2, ranges::greater{}, proj));\n  EXPECT_EQ(ints + 2, ranges::lower_bound(ints, 1, ranges::greater{}, proj));\n  EXPECT_EQ(ints + 4, ranges::lower_bound(ints, 0, ranges::greater{}, proj));\n  EXPECT_EQ(ints + 6, ranges::lower_bound(ints, -1, ranges::greater{}, proj));\n}\n\nTEST(RangesTest, UpperBound) {\n  int array[] = {0, 0, 1, 1, 2, 2};\n\n  EXPECT_EQ(array, ranges::upper_bound(array, array + 6, -1));\n  EXPECT_EQ(array + 2, ranges::upper_bound(array, array + 6, 0));\n  EXPECT_EQ(array + 4, ranges::upper_bound(array, array + 6, 1));\n  EXPECT_EQ(array + 6, ranges::upper_bound(array, array + 6, 2));\n  EXPECT_EQ(array + 6, ranges::upper_bound(array, array + 6, 3));\n\n  Int ints[] = {0, 0, 1, 1, 2, 2};\n\n  EXPECT_EQ(ints, ranges::upper_bound(ints, -1, {}, &Int::value));\n  EXPECT_EQ(ints + 2, ranges::upper_bound(ints, 0, {}, &Int::value));\n  EXPECT_EQ(ints + 4, ranges::upper_bound(ints, 1, {}, &Int::value));\n  EXPECT_EQ(ints + 6, ranges::upper_bound(ints, 2, {}, &Int::value));\n  EXPECT_EQ(ints + 6, ranges::upper_bound(ints, 3, {}, &Int::value));\n\n  const auto proj = [](const Int& i) { return 2 - i.value; };\n  EXPECT_EQ(ints, ranges::upper_bound(ints, 3, ranges::greater{}, proj));\n  EXPECT_EQ(ints + 2, ranges::upper_bound(ints, 2, ranges::greater{}, proj));\n  EXPECT_EQ(ints + 4, ranges::upper_bound(ints, 1, ranges::greater{}, proj));\n  EXPECT_EQ(ints + 6, ranges::upper_bound(ints, 0, ranges::greater{}, proj));\n  EXPECT_EQ(ints + 6, ranges::upper_bound(ints, -1, ranges::greater{}, proj));\n}\n\nTEST(RangesTest, EqualRange) {\n  int array[] = {0, 0, 1, 1, 2, 2};\n\n  EXPECT_THAT(ranges::equal_range(array, array + 6, -1), Pair(array, array));\n  EXPECT_THAT(ranges::equal_range(array, array + 6, 0), Pair(array, array + 2));\n  EXPECT_THAT(ranges::equal_range(array, array + 6, 1),\n              Pair(array + 2, array + 4));\n  EXPECT_THAT(ranges::equal_range(array, array + 6, 2),\n              Pair(array + 4, array + 6));\n  EXPECT_THAT(ranges::equal_range(array, array + 6, 3),\n              Pair(array + 6, array + 6));\n\n  Int ints[] = {0, 0, 1, 1, 2, 2};\n\n  EXPECT_THAT(ranges::equal_range(ints, -1, {}, &Int::value), Pair(ints, ints));\n  EXPECT_THAT(ranges::equal_range(ints, 0, {}, &Int::value),\n              Pair(ints, ints + 2));\n  EXPECT_THAT(ranges::equal_range(ints, 1, {}, &Int::value),\n              Pair(ints + 2, ints + 4));\n  EXPECT_THAT(ranges::equal_range(ints, 2, {}, &Int::value),\n              Pair(ints + 4, ints + 6));\n  EXPECT_THAT(ranges::equal_range(ints, 3, {}, &Int::value),\n              Pair(ints + 6, ints + 6));\n\n  const auto proj = [](const Int& i) { return 2 - i.value; };\n  EXPECT_THAT(ranges::equal_range(ints, 3, ranges::greater{}, proj),\n              Pair(ints, ints));\n  EXPECT_THAT(ranges::equal_range(ints, 2, ranges::greater{}, proj),\n              Pair(ints, ints + 2));\n  EXPECT_THAT(ranges::equal_range(ints, 1, ranges::greater{}, proj),\n              Pair(ints + 2, ints + 4));\n  EXPECT_THAT(ranges::equal_range(ints, 0, ranges::greater{}, proj),\n              Pair(ints + 4, ints + 6));\n  EXPECT_THAT(ranges::equal_range(ints, -1, ranges::greater{}, proj),\n              Pair(ints + 6, ints + 6));\n}\n\nTEST(RangesTest, BinarySearch) {\n  int array[] = {0, 0, 1, 1, 2, 2};\n\n  EXPECT_FALSE(ranges::binary_search(array, array + 6, -1));\n  EXPECT_TRUE(ranges::binary_search(array, array + 6, 0));\n  EXPECT_TRUE(ranges::binary_search(array, array + 6, 1));\n  EXPECT_TRUE(ranges::binary_search(array, array + 6, 2));\n  EXPECT_FALSE(ranges::binary_search(array, array + 6, 3));\n\n  Int ints[] = {0, 0, 1, 1, 2, 2};\n\n  EXPECT_FALSE(ranges::binary_search(ints, -1, {}, &Int::value));\n  EXPECT_TRUE(ranges::binary_search(ints, 0, {}, &Int::value));\n  EXPECT_TRUE(ranges::binary_search(ints, 1, {}, &Int::value));\n  EXPECT_TRUE(ranges::binary_search(ints, 2, {}, &Int::value));\n  EXPECT_FALSE(ranges::binary_search(ints, 3, {}, &Int::value));\n\n  const auto proj = [](const Int& i) { return 2 - i.value; };\n  EXPECT_FALSE(ranges::binary_search(ints, 3, ranges::greater{}, proj));\n  EXPECT_TRUE(ranges::binary_search(ints, 2, ranges::greater{}, proj));\n  EXPECT_TRUE(ranges::binary_search(ints, 1, ranges::greater{}, proj));\n  EXPECT_TRUE(ranges::binary_search(ints, 0, ranges::greater{}, proj));\n  EXPECT_FALSE(ranges::binary_search(ints, -1, ranges::greater{}, proj));\n}\n\nTEST(RangesTest, IsPartitioned) {\n  int input[] = {1, 3, 5, 0, 4, 2};\n  EXPECT_TRUE(ranges::is_partitioned(input, input, is_odd));\n  EXPECT_TRUE(ranges::is_partitioned(input, input + 6, is_odd));\n  EXPECT_TRUE(ranges::is_partitioned(input, input, is_even));\n  EXPECT_FALSE(ranges::is_partitioned(input, input + 6, is_even));\n\n  Int ints[] = {1, 0, 4, 3, 2};\n  auto lt_2 = [](const Int& i) { return i.value < 2; };\n  EXPECT_TRUE(ranges::is_partitioned(ints, lt_2, &Int::value));\n}\n\nTEST(RangesTest, Partition) {\n  int input[] = {3, 1, 2, 0, 4};\n  EXPECT_EQ(input + 3, ranges::partition(input, input + 5, is_even));\n  EXPECT_TRUE(is_even(input[0]));\n  EXPECT_TRUE(is_even(input[1]));\n  EXPECT_TRUE(is_even(input[2]));\n  EXPECT_TRUE(is_odd(input[3]));\n  EXPECT_TRUE(is_odd(input[4]));\n\n  Int ints[] = {6, 7, 9, 8, 5};\n  EXPECT_EQ(ints + 3, ranges::partition(ints, is_odd, &Int::value));\n  EXPECT_TRUE(is_odd(ints[0].value));\n  EXPECT_TRUE(is_odd(ints[1].value));\n  EXPECT_TRUE(is_odd(ints[2].value));\n  EXPECT_TRUE(is_even(ints[3].value));\n  EXPECT_TRUE(is_even(ints[4].value));\n}\n\nTEST(RangesTest, StablePartition) {\n  int input[] = {3, 1, 2, 0, 4};\n  EXPECT_EQ(input + 3, ranges::stable_partition(input, input + 5, is_even));\n  EXPECT_THAT(input, ElementsAre(2, 0, 4, 3, 1));\n\n  Int ints[] = {6, 7, 9, 8, 5};\n  EXPECT_EQ(ints + 3, ranges::stable_partition(ints, is_odd, &Int::value));\n  EXPECT_THAT(ints, ElementsAre(7, 9, 5, 6, 8));\n}\n\nTEST(RangesTest, PartitionCopy) {\n  int input[] = {3, 1, 2, 0, 4};\n  int evens[5] = {};\n  int odds[5] = {};\n  EXPECT_THAT(ranges::partition_copy(input, input + 5, evens, odds, is_even),\n              Pair(evens + 3, odds + 2));\n  EXPECT_THAT(input, ElementsAre(3, 1, 2, 0, 4));\n  EXPECT_THAT(evens, ElementsAre(2, 0, 4, 0, 0));\n  EXPECT_THAT(odds, ElementsAre(3, 1, 0, 0, 0));\n\n  Int ints[] = {6, 7, 9, 8, 5};\n  Int odd_ints[5] = {};\n  Int even_ints[5] = {};\n  EXPECT_THAT(\n      ranges::partition_copy(ints, odd_ints, even_ints, is_odd, &Int::value),\n      Pair(odd_ints + 3, even_ints + 2));\n  EXPECT_THAT(ints, ElementsAre(6, 7, 9, 8, 5));\n  EXPECT_THAT(odd_ints, ElementsAre(7, 9, 5, 0, 0));\n  EXPECT_THAT(even_ints, ElementsAre(6, 8, 0, 0, 0));\n}\n\nTEST(RangesTest, PartitionPoint) {\n  int input[] = {1, 3, 5, 0, 4, 2};\n  EXPECT_EQ(input, ranges::partition_point(input, input, is_odd));\n  EXPECT_EQ(input + 3, ranges::partition_point(input, input + 6, is_odd));\n  EXPECT_EQ(input, ranges::partition_point(input, input, is_even));\n\n  Int ints[] = {1, 0, 4, 3, 2};\n  auto lt_2 = [](const Int& i) { return i.value < 2; };\n  EXPECT_EQ(ints + 2, ranges::partition_point(ints, lt_2, &Int::value));\n}\n\nTEST(RangesTest, Merge) {\n  int input1[] = {0, 2, 4, 6, 8};\n  int input2[] = {1, 3, 5, 7, 9};\n  int output[10];\n  EXPECT_EQ(output + 10,\n            ranges::merge(input1, input1 + 5, input2, input2 + 5, output));\n  EXPECT_THAT(output, ElementsAre(0, 1, 2, 3, 4, 5, 6, 7, 8, 9));\n\n  Int ints1[] = {0, 2, 4, 6, 8};\n  Int ints2[] = {1, 3, 5, 7, 9};\n  Int outs[10];\n  EXPECT_EQ(outs + 10,\n            ranges::merge(ints1, ints2, outs, {}, &Int::value, &Int::value));\n  EXPECT_THAT(outs, ElementsAre(0, 1, 2, 3, 4, 5, 6, 7, 8, 9));\n\n  EXPECT_EQ(outs + 10, ranges::merge(input1, ints1, outs, {}, {}, &Int::value));\n  EXPECT_THAT(outs, ElementsAre(0, 0, 2, 2, 4, 4, 6, 6, 8, 8));\n\n  EXPECT_EQ(outs + 10, ranges::merge(ints2, input2, outs, {}, &Int::value, {}));\n  EXPECT_THAT(outs, ElementsAre(1, 1, 3, 3, 5, 5, 7, 7, 9, 9));\n}\n\nTEST(RangesTest, InplaceMerge) {\n  int input[] = {0, 2, 4, 6, 8, 1, 3, 5, 7, 9};\n  EXPECT_EQ(input + 10, ranges::inplace_merge(input, input + 5, input + 10));\n  EXPECT_THAT(input, ElementsAre(0, 1, 2, 3, 4, 5, 6, 7, 8, 9));\n\n  Int ints[] = {8, 6, 4, 2, 0, 9, 7, 5, 3, 1};\n  EXPECT_EQ(ints + 10, ranges::inplace_merge(ints, ints + 5, ranges::greater(),\n                                             &Int::value));\n  EXPECT_THAT(ints, ElementsAre(9, 8, 7, 6, 5, 4, 3, 2, 1, 0));\n}\n\nTEST(RangesTest, Includes) {\n  int evens[] = {0, 2, 4, 6, 8};\n  int odds[] = {1, 3, 5, 7, 9};\n  int fours[] = {0, 4, 8};\n\n  EXPECT_TRUE(ranges::includes(evens, evens + 5, fours, fours + 3));\n  EXPECT_FALSE(ranges::includes(fours, fours + 3, evens, evens + 5));\n  EXPECT_FALSE(ranges::includes(evens, evens + 5, odds, odds + 5));\n  EXPECT_FALSE(ranges::includes(odds, odds + 5, evens, evens + 5));\n\n  Int even_ints[] = {0, 2, 4, 6, 8};\n  Int odd_ints[] = {1, 3, 5, 7, 9};\n\n  EXPECT_TRUE(ranges::includes(even_ints, fours, {}, &Int::value));\n  EXPECT_FALSE(ranges::includes(fours, even_ints, {}, {}, &Int::value));\n  EXPECT_FALSE(\n      ranges::includes(even_ints, odd_ints, {}, &Int::value, &Int::value));\n  EXPECT_FALSE(\n      ranges::includes(odd_ints, even_ints, {}, &Int::value, &Int::value));\n}\n\nTEST(RangesTest, SetUnion) {\n  int evens[] = {0, 2, 4, 6, 8};\n  int odds[] = {1, 3, 5, 7, 9};\n  int fours[] = {0, 4, 8};\n  int result[10];\n\n  EXPECT_EQ(result + 10,\n            ranges::set_union(evens, evens + 5, odds, odds + 5, result));\n  EXPECT_THAT(result, ElementsAre(0, 1, 2, 3, 4, 5, 6, 7, 8, 9));\n\n  EXPECT_EQ(result + 5,\n            ranges::set_union(evens, evens + 5, fours, fours + 3, result));\n  EXPECT_THAT(make_vector(result, result + 5), ElementsAre(0, 2, 4, 6, 8));\n\n  Int even_ints[] = {0, 2, 4, 6, 8};\n  Int odd_ints[] = {1, 3, 5, 7, 9};\n  Int result_ints[10];\n\n  EXPECT_EQ(result_ints + 10,\n            ranges::set_union(even_ints, odd_ints, result_ints, {}, &Int::value,\n                              &Int::value));\n  EXPECT_THAT(result_ints, ElementsAre(0, 1, 2, 3, 4, 5, 6, 7, 8, 9));\n\n  EXPECT_EQ(result_ints + 5,\n            ranges::set_union(even_ints, fours, result_ints, {}, &Int::value));\n  EXPECT_THAT(make_vector(result_ints, result_ints + 5),\n              ElementsAre(0, 2, 4, 6, 8));\n\n  EXPECT_EQ(result_ints + 8, ranges::set_union(fours, odd_ints, result_ints, {},\n                                               {}, &Int::value));\n  EXPECT_THAT(make_vector(result_ints, result_ints + 8),\n              ElementsAre(0, 1, 3, 4, 5, 7, 8, 9));\n}\n\nTEST(RangesTest, SetIntersection) {\n  int evens[] = {0, 2, 4, 6, 8};\n  int odds[] = {1, 3, 5, 7, 9};\n  int fours[] = {0, 4, 8};\n  int result[10];\n\n  EXPECT_EQ(result,\n            ranges::set_intersection(evens, evens + 5, odds, odds + 5, result));\n\n  EXPECT_EQ(result + 3, ranges::set_intersection(evens, evens + 5, fours,\n                                                 fours + 3, result));\n  EXPECT_THAT(make_vector(result, result + 3), ElementsAre(0, 4, 8));\n\n  Int even_ints[] = {0, 2, 4, 6, 8};\n  Int odd_ints[] = {1, 3, 5, 7, 9};\n  Int result_ints[10];\n\n  EXPECT_EQ(result_ints,\n            ranges::set_intersection(even_ints, odd_ints, result_ints, {},\n                                     &Int::value, &Int::value));\n\n  EXPECT_EQ(\n      result_ints + 3,\n      ranges::set_intersection(even_ints, fours, result_ints, {}, &Int::value));\n  EXPECT_THAT(make_vector(result_ints, result_ints + 3), ElementsAre(0, 4, 8));\n\n  EXPECT_EQ(result_ints, ranges::set_intersection(fours, odd_ints, result_ints,\n                                                  {}, {}, &Int::value));\n}\n\nTEST(RangesTest, SetDifference) {\n  int evens[] = {0, 2, 4, 6, 8};\n  int odds[] = {1, 3, 5, 7, 9};\n  int fours[] = {0, 4, 8};\n  int result[5];\n\n  EXPECT_EQ(result + 5,\n            ranges::set_difference(evens, evens + 5, odds, odds + 5, result));\n  EXPECT_THAT(result, ElementsAre(0, 2, 4, 6, 8));\n\n  EXPECT_EQ(result + 2,\n            ranges::set_difference(evens, evens + 5, fours, fours + 3, result));\n  EXPECT_THAT(make_vector(result, result + 2), ElementsAre(2, 6));\n\n  Int even_ints[] = {0, 2, 4, 6, 8};\n  Int odd_ints[] = {1, 3, 5, 7, 9};\n  Int result_ints[5];\n\n  EXPECT_EQ(result_ints + 5,\n            ranges::set_difference(even_ints, odd_ints, result_ints, {},\n                                   &Int::value, &Int::value));\n  EXPECT_THAT(result_ints, ElementsAre(0, 2, 4, 6, 8));\n\n  EXPECT_EQ(\n      result_ints + 2,\n      ranges::set_difference(even_ints, fours, result_ints, {}, &Int::value));\n  EXPECT_THAT(make_vector(result_ints, result_ints + 2), ElementsAre(2, 6));\n\n  EXPECT_EQ(result_ints + 3,\n            ranges::set_difference(fours, odd_ints, result_ints, {}, {},\n                                   &Int::value));\n  EXPECT_THAT(make_vector(result_ints, result_ints + 3), ElementsAre(0, 4, 8));\n}\n\nTEST(RangesTest, SetSymmetricDifference) {\n  int evens[] = {0, 2, 4, 6, 8};\n  int odds[] = {1, 3, 5, 7, 9};\n  int fours[] = {0, 4, 8};\n  int result[10];\n\n  EXPECT_EQ(result + 10, ranges::set_symmetric_difference(\n                             evens, evens + 5, odds, odds + 5, result));\n  EXPECT_THAT(result, ElementsAre(0, 1, 2, 3, 4, 5, 6, 7, 8, 9));\n\n  EXPECT_EQ(result + 2, ranges::set_symmetric_difference(\n                            evens, evens + 5, fours, fours + 3, result));\n  EXPECT_THAT(make_vector(result, result + 2), ElementsAre(2, 6));\n\n  Int even_ints[] = {0, 2, 4, 6, 8};\n  Int odd_ints[] = {1, 3, 5, 7, 9};\n  Int result_ints[10];\n\n  EXPECT_EQ(result_ints + 10,\n            ranges::set_symmetric_difference(even_ints, odd_ints, result_ints,\n                                             {}, &Int::value, &Int::value));\n  EXPECT_THAT(result_ints, ElementsAre(0, 1, 2, 3, 4, 5, 6, 7, 8, 9));\n\n  EXPECT_EQ(result_ints + 2,\n            ranges::set_symmetric_difference(even_ints, fours, result_ints, {},\n                                             &Int::value));\n  EXPECT_THAT(make_vector(result_ints, result_ints + 2), ElementsAre(2, 6));\n\n  EXPECT_EQ(result_ints + 8,\n            ranges::set_symmetric_difference(fours, odd_ints, result_ints, {},\n                                             {}, &Int::value));\n  EXPECT_THAT(make_vector(result_ints, result_ints + 8),\n              ElementsAre(0, 1, 3, 4, 5, 7, 8, 9));\n}\n\nTEST(RangesTest, PushHeap) {\n  int heap[] = {6, 4, 3, 2, 1, 0, 5};\n  EXPECT_EQ(heap + 7, ranges::push_heap(heap, heap + 7));\n  EXPECT_THAT(heap, ElementsAre(6, Ge(4), Ge(4), Le(3), Le(3), Le(3), Le(3)));\n\n  Int heap_int[] = {1, 2, 3, 4, 5, 6, 0};\n  EXPECT_EQ(heap_int + 7,\n            ranges::push_heap(heap_int, ranges::greater(), &Int::value));\n  EXPECT_THAT(heap_int, ElementsAre(0, 2, 1, 4, 5, 6, 3));\n  EXPECT_THAT(heap_int,\n              ElementsAre(0, Le(2), Le(2), Ge(3), Ge(3), Ge(3), Ge(3)));\n}\n\nTEST(RangesTest, PopHeap) {\n  int heap[] = {6, 5, 4, 3, 2, 1, 0};\n  EXPECT_EQ(heap + 7, ranges::pop_heap(heap, heap + 7));\n  EXPECT_THAT(heap, ElementsAre(5, Ge(3), Ge(3), Le(2), Le(2), Le(2), 6));\n\n  Int heap_int[] = {0, 1, 2, 3, 4, 5, 6};\n  EXPECT_EQ(heap_int + 7,\n            ranges::pop_heap(heap_int, ranges::greater(), &Int::value));\n  EXPECT_THAT(heap_int, ElementsAre(1, Le(3), Le(3), Ge(4), Ge(4), Ge(4), 0));\n}\n\nTEST(RangesTest, MakeHeap) {\n  int heap[] = {0, 1, 2, 3, 4, 5, 6};\n  EXPECT_EQ(heap + 7, ranges::make_heap(heap, heap + 7));\n  EXPECT_THAT(heap, ElementsAre(6, Ge(4), Ge(4), Le(3), Le(3), Le(3), Le(3)));\n\n  Int heap_int[] = {6, 5, 4, 3, 2, 1, 0};\n  EXPECT_EQ(heap_int + 7,\n            ranges::make_heap(heap_int, ranges::greater(), &Int::value));\n  EXPECT_THAT(heap_int,\n              ElementsAre(0, Le(2), Le(2), Ge(3), Ge(3), Ge(3), Ge(3)));\n}\n\nTEST(RangesTest, SortHeap) {\n  int heap[] = {6, 4, 5, 0, 1, 2, 3};\n  EXPECT_EQ(heap + 7, ranges::sort_heap(heap, heap + 7));\n  EXPECT_THAT(heap, ElementsAre(0, 1, 2, 3, 4, 5, 6));\n\n  Int heap_int[] = {0, 2, 1, 4, 3, 6, 5};\n  EXPECT_EQ(heap_int + 7,\n            ranges::sort_heap(heap_int, ranges::greater(), &Int::value));\n  EXPECT_THAT(heap_int, ElementsAre(6, 5, 4, 3, 2, 1, 0));\n}\n\nTEST(RangesTest, IsHeap) {\n  int heap[] = {6, 4, 5, 0, 1, 2, 3};\n  EXPECT_TRUE(ranges::is_heap(heap, heap + 7));\n  EXPECT_FALSE(ranges::is_heap(heap, heap + 7, ranges::greater()));\n\n  Int heap_int[] = {0, 2, 1, 4, 3, 6, 5};\n  EXPECT_TRUE(ranges::is_heap(heap_int, ranges::greater(), &Int::value));\n  EXPECT_FALSE(ranges::is_heap(heap_int, {}, &Int::value));\n}\n\nTEST(RangesTest, IsHeapUntil) {\n  int heap[] = {6, 4, 5, 0, 1, 2, 3};\n  EXPECT_EQ(heap + 7, ranges::is_heap_until(heap, heap + 7));\n  EXPECT_EQ(heap + 1, ranges::is_heap_until(heap, heap + 7, ranges::greater()));\n\n  Int heap_int[] = {0, 2, 1, 4, 3, 6, 5};\n  EXPECT_EQ(heap_int + 7,\n            ranges::is_heap_until(heap_int, ranges::greater(), &Int::value));\n  EXPECT_EQ(heap_int + 1, ranges::is_heap_until(heap_int, {}, &Int::value));\n}\n\nTEST(RangesTest, Min) {\n  constexpr int k1 = 1;\n  constexpr int k2 = 2;\n  static_assert(&ranges::min(k1, k1) == &k1, \"\");\n  static_assert(&ranges::min(k1, k2) == &k1, \"\");\n  static_assert(&ranges::min(k2, k1) == &k1, \"\");\n  static_assert(&ranges::min(k2, k2) == &k2, \"\");\n\n  constexpr Int k3 = 3;\n  constexpr Int k4 = 4;\n  static_assert(&ranges::min(k3, k3, ranges::greater(), &Int::value) == &k3,\n                \"\");\n  static_assert(&ranges::min(k3, k4, ranges::greater(), &Int::value) == &k4,\n                \"\");\n  static_assert(&ranges::min(k4, k3, ranges::greater(), &Int::value) == &k4,\n                \"\");\n  static_assert(&ranges::min(k4, k4, ranges::greater(), &Int::value) == &k4,\n                \"\");\n\n  constexpr Int array[] = {2, 6, 4, 3, 5, 1};\n  static_assert(ranges::min({5, 3, 4, 2, 1, 6}) == 1, \"\");\n  static_assert(ranges::min(array, ranges::greater(), &Int::value) == 6, \"\");\n}\n\nTEST(RangesTest, Max) {\n  constexpr int k1 = 1;\n  constexpr int k2 = 2;\n  static_assert(&ranges::max(k1, k2) == &k2, \"\");\n  static_assert(&ranges::max(k1, k2) == &k2, \"\");\n  static_assert(&ranges::max(k2, k1) == &k2, \"\");\n  static_assert(&ranges::max(k2, k1) == &k2, \"\");\n\n  constexpr Int k3 = 3;\n  constexpr Int k4 = 4;\n  static_assert(&ranges::max(k3, k3, ranges::greater(), &Int::value) == &k3,\n                \"\");\n  static_assert(&ranges::max(k3, k4, ranges::greater(), &Int::value) == &k3,\n                \"\");\n  static_assert(&ranges::max(k4, k3, ranges::greater(), &Int::value) == &k3,\n                \"\");\n  static_assert(&ranges::max(k4, k4, ranges::greater(), &Int::value) == &k4,\n                \"\");\n\n  constexpr Int array[] = {2, 6, 4, 3, 5, 1};\n  static_assert(ranges::max({5, 3, 4, 2, 1, 6}) == 6, \"\");\n  static_assert(ranges::max(array, ranges::greater(), &Int::value) == 1, \"\");\n}\n\nTEST(RangesTest, Minmax) {\n  constexpr int k1 = 1;\n  constexpr int k2 = 2;\n  static_assert(&ranges::minmax(k1, k1).first == &k1, \"\");\n  static_assert(&ranges::minmax(k1, k1).second == &k1, \"\");\n  static_assert(&ranges::minmax(k1, k2).first == &k1, \"\");\n  static_assert(&ranges::minmax(k1, k2).second == &k2, \"\");\n  static_assert(&ranges::minmax(k2, k1).first == &k1, \"\");\n  static_assert(&ranges::minmax(k2, k1).second == &k2, \"\");\n  static_assert(&ranges::minmax(k2, k2).first == &k2, \"\");\n  static_assert(&ranges::minmax(k2, k2).second == &k2, \"\");\n\n  static constexpr Int k3 = 3;\n  static constexpr Int k4 = 4;\n  {\n    constexpr auto kResult =\n        ranges::minmax(k3, k3, ranges::greater(), &Int::value);\n    static_assert(&kResult.first == &k3, \"\");\n    static_assert(&kResult.second == &k3, \"\");\n  }\n  {\n    constexpr auto kResult =\n        ranges::minmax(k3, k4, ranges::greater(), &Int::value);\n    static_assert(&kResult.first == &k4, \"\");\n    static_assert(&kResult.second == &k3, \"\");\n  }\n  {\n    constexpr auto kResult =\n        ranges::minmax(k4, k3, ranges::greater(), &Int::value);\n    static_assert(&kResult.first == &k4, \"\");\n    static_assert(&kResult.second == &k3, \"\");\n  }\n  {\n    constexpr auto kResult =\n        ranges::minmax(k4, k4, ranges::greater(), &Int::value);\n    static_assert(&kResult.first == &k4, \"\");\n    static_assert(&kResult.second == &k4, \"\");\n  }\n\n  static_assert(ranges::minmax({5, 3, 4, 2, 1, 6}).first == 1, \"\");\n  static_assert(ranges::minmax({5, 3, 4, 2, 1, 6}).second == 6, \"\");\n\n  constexpr Int array[] = {2, 6, 4, 3, 5, 1};\n  static_assert(\n      ranges::minmax(array, ranges::greater(), &Int::value).first == 6, \"\");\n  static_assert(\n      ranges::minmax(array, ranges::greater(), &Int::value).second == 1, \"\");\n}\n\nTEST(RangesTest, MinElement) {\n  constexpr int array[] = {2, 6, 4, 3, 5, 1};\n  constexpr Int ints[] = {2, 6, 4, 3, 5, 1};\n  static_assert(*ranges::min_element(array, array + 6) == 1, \"\");\n  static_assert(*ranges::min_element(ints, ranges::greater(), &Int::value) == 6,\n                \"\");\n}\n\nTEST(RangesTest, MaxElement) {\n  constexpr int array[] = {2, 6, 4, 3, 5, 1};\n  constexpr Int ints[] = {2, 6, 4, 3, 5, 1};\n  static_assert(*ranges::max_element(array, array + 6) == 6, \"\");\n  static_assert(*ranges::max_element(ints, ranges::greater(), &Int::value) == 1,\n                \"\");\n}\n\nTEST(RangesTest, MinmaxElement) {\n  constexpr int array[] = {2, 6, 4, 3, 5, 1};\n  static_assert(*ranges::minmax_element(array, array + 6).first == 1, \"\");\n  static_assert(*ranges::minmax_element(array, array + 6).second == 6, \"\");\n\n  constexpr Int ints[] = {2, 6, 4, 3, 5, 1};\n  static_assert(\n      *ranges::minmax_element(ints, ranges::greater(), &Int::value).first == 6,\n      \"\");\n  static_assert(\n      *ranges::minmax_element(ints, ranges::greater(), &Int::value).second == 1,\n      \"\");\n}\n\nTEST(RangesTest, Clamp) {\n  constexpr int k1 = 1;\n  constexpr int k2 = 2;\n  constexpr int k3 = 3;\n\n  static_assert(&ranges::clamp(k1, k1, k1) == &k1, \"\");\n  static_assert(&ranges::clamp(k1, k1, k2) == &k1, \"\");\n  static_assert(&ranges::clamp(k1, k1, k3) == &k1, \"\");\n  static_assert(&ranges::clamp(k1, k2, k2) == &k2, \"\");\n  static_assert(&ranges::clamp(k1, k2, k3) == &k2, \"\");\n  static_assert(&ranges::clamp(k1, k3, k3) == &k3, \"\");\n\n  static_assert(&ranges::clamp(k2, k1, k1) == &k1, \"\");\n  static_assert(&ranges::clamp(k2, k1, k2) == &k2, \"\");\n  static_assert(&ranges::clamp(k2, k1, k3) == &k2, \"\");\n  static_assert(&ranges::clamp(k2, k2, k2) == &k2, \"\");\n  static_assert(&ranges::clamp(k2, k2, k3) == &k2, \"\");\n  static_assert(&ranges::clamp(k2, k3, k3) == &k3, \"\");\n\n  static_assert(&ranges::clamp(k3, k1, k1) == &k1, \"\");\n  static_assert(&ranges::clamp(k3, k1, k2) == &k2, \"\");\n  static_assert(&ranges::clamp(k3, k1, k3) == &k3, \"\");\n  static_assert(&ranges::clamp(k3, k2, k2) == &k2, \"\");\n  static_assert(&ranges::clamp(k3, k2, k3) == &k3, \"\");\n  static_assert(&ranges::clamp(k3, k3, k3) == &k3, \"\");\n\n  constexpr Int k4 = 4;\n  constexpr Int k5 = 5;\n  constexpr Int k6 = 6;\n\n  static_assert(\n      &ranges::clamp(k6, k6, k6, ranges::greater(), &Int::value) == &k6, \"\");\n  static_assert(\n      &ranges::clamp(k6, k6, k5, ranges::greater(), &Int::value) == &k6, \"\");\n  static_assert(\n      &ranges::clamp(k6, k6, k4, ranges::greater(), &Int::value) == &k6, \"\");\n  static_assert(\n      &ranges::clamp(k6, k5, k5, ranges::greater(), &Int::value) == &k5, \"\");\n  static_assert(\n      &ranges::clamp(k6, k5, k4, ranges::greater(), &Int::value) == &k5, \"\");\n  static_assert(\n      &ranges::clamp(k6, k4, k4, ranges::greater(), &Int::value) == &k4, \"\");\n\n  static_assert(\n      &ranges::clamp(k5, k6, k6, ranges::greater(), &Int::value) == &k6, \"\");\n  static_assert(\n      &ranges::clamp(k5, k6, k5, ranges::greater(), &Int::value) == &k5, \"\");\n  static_assert(\n      &ranges::clamp(k5, k6, k4, ranges::greater(), &Int::value) == &k5, \"\");\n  static_assert(\n      &ranges::clamp(k5, k5, k5, ranges::greater(), &Int::value) == &k5, \"\");\n  static_assert(\n      &ranges::clamp(k5, k5, k4, ranges::greater(), &Int::value) == &k5, \"\");\n  static_assert(\n      &ranges::clamp(k5, k4, k4, ranges::greater(), &Int::value) == &k4, \"\");\n\n  static_assert(\n      &ranges::clamp(k4, k6, k6, ranges::greater(), &Int::value) == &k6, \"\");\n  static_assert(\n      &ranges::clamp(k4, k6, k5, ranges::greater(), &Int::value) == &k5, \"\");\n  static_assert(\n      &ranges::clamp(k4, k6, k4, ranges::greater(), &Int::value) == &k4, \"\");\n  static_assert(\n      &ranges::clamp(k4, k5, k5, ranges::greater(), &Int::value) == &k5, \"\");\n  static_assert(\n      &ranges::clamp(k4, k5, k4, ranges::greater(), &Int::value) == &k4, \"\");\n  static_assert(\n      &ranges::clamp(k4, k4, k4, ranges::greater(), &Int::value) == &k4, \"\");\n}\n\nTEST(RangesTest, LexicographicalCompare) {\n  constexpr int inputs1[] = {0, 1, 2, 3, 4, 5};\n  constexpr int inputs2[] = {0, 1, 2, 3, 5, 4};\n  static_assert(!ranges::lexicographical_compare(inputs1, inputs1 + 6, inputs1,\n                                                 inputs1 + 6),\n                \"\");\n  static_assert(ranges::lexicographical_compare(inputs1, inputs1 + 6, inputs2,\n                                                inputs2 + 6),\n                \"\");\n  static_assert(!ranges::lexicographical_compare(inputs2, inputs2 + 6, inputs1,\n                                                 inputs1 + 6),\n                \"\");\n  static_assert(!ranges::lexicographical_compare(inputs2, inputs2 + 6, inputs2,\n                                                 inputs2 + 6),\n                \"\");\n\n  constexpr Int ints1[] = {0, 1, 2, 3, 4, 5};\n  constexpr Int ints2[] = {5, 4, 3, 2, 1, 0};\n  static_assert(\n      !ranges::lexicographical_compare(inputs1, ints1, {}, {}, &Int::value),\n      \"\");\n  static_assert(\n      !ranges::lexicographical_compare(ints1, inputs1, {}, &Int::value), \"\");\n\n  static_assert(\n      !ranges::lexicographical_compare(inputs2, ints1, {}, {}, &Int::value),\n      \"\");\n  static_assert(\n      ranges::lexicographical_compare(ints1, inputs2, {}, &Int::value), \"\");\n\n  static_assert(ranges::lexicographical_compare(ints1, ints2, {}, &Int::value,\n                                                &Int::value),\n                \"\");\n  static_assert(!ranges::lexicographical_compare(ints2, ints1, {}, &Int::value,\n                                                 &Int::value),\n                \"\");\n\n  static_assert(!ranges::lexicographical_compare(\n                    ints1, ints2, ranges::greater(), &Int::value, &Int::value),\n                \"\");\n  static_assert(ranges::lexicographical_compare(ints2, ints1, ranges::greater(),\n                                                &Int::value, &Int::value),\n                \"\");\n\n  using List = std::initializer_list<int>;\n  static_assert(\n      ranges::lexicographical_compare(List{0, 1, 2}, List{0, 1, 2, 3}), \"\");\n  static_assert(\n      !ranges::lexicographical_compare(List{0, 1, 2, 3}, List{0, 1, 2}), \"\");\n  static_assert(\n      ranges::lexicographical_compare(List{0, 1, 2, 3}, List{0, 1, 2, 4}), \"\");\n  static_assert(\n      !ranges::lexicographical_compare(List{0, 1, 2, 4}, List{0, 1, 2, 3}), \"\");\n}\n\nTEST(RangesTest, NextPermutation) {\n  int input[] = {5, 4, 3, 2, 0, 1};\n  EXPECT_TRUE(ranges::next_permutation(input, input + 6));\n  EXPECT_THAT(input, ElementsAre(5, 4, 3, 2, 1, 0));\n\n  EXPECT_FALSE(ranges::next_permutation(input, input + 6));\n  EXPECT_THAT(input, ElementsAre(0, 1, 2, 3, 4, 5));\n\n  Int ints[] = {0, 1, 2, 3, 5, 4};\n  EXPECT_TRUE(ranges::next_permutation(ints, ranges::greater(), &Int::value));\n  EXPECT_THAT(ints, ElementsAre(0, 1, 2, 3, 4, 5));\n\n  EXPECT_FALSE(ranges::next_permutation(ints, ranges::greater(), &Int::value));\n  EXPECT_THAT(ints, ElementsAre(5, 4, 3, 2, 1, 0));\n\n  int bits[] = {0, 0, 1, 0, 0};\n  EXPECT_TRUE(ranges::next_permutation(bits));\n  EXPECT_THAT(bits, ElementsAre(0, 1, 0, 0, 0));\n}\n\nTEST(RangesTest, PrevPermutation) {\n  int input[] = {0, 1, 2, 3, 5, 4};\n  EXPECT_TRUE(ranges::prev_permutation(input, input + 6));\n  EXPECT_THAT(input, ElementsAre(0, 1, 2, 3, 4, 5));\n\n  EXPECT_FALSE(ranges::prev_permutation(input, input + 6));\n  EXPECT_THAT(input, ElementsAre(5, 4, 3, 2, 1, 0));\n\n  Int ints[] = {5, 4, 3, 2, 0, 1};\n  EXPECT_TRUE(ranges::prev_permutation(ints, ranges::greater(), &Int::value));\n  EXPECT_THAT(ints, ElementsAre(5, 4, 3, 2, 1, 0));\n\n  EXPECT_FALSE(ranges::prev_permutation(ints, ranges::greater(), &Int::value));\n  EXPECT_THAT(ints, ElementsAre(0, 1, 2, 3, 4, 5));\n\n  int bits[] = {0, 0, 1, 0, 0};\n  EXPECT_TRUE(ranges::prev_permutation(bits));\n  EXPECT_THAT(bits, ElementsAre(0, 0, 0, 1, 0));\n}\n\nnamespace internal {\nconst auto predicate = [](int value) { return value; };\nstruct TestPair {\n  int a;\n  int b;\n};\n}  // namespace internal\n\n// This is a compilation test that checks that using predicates and projections\n// from the base::internal namespace in range algorithms doesn't result in\n// ambiguous calls to base::invoke.\nTEST(RangesTest, DontClashWithPredicateFromInternalInvoke) {\n  {\n    int input[] = {0, 1, 2};\n    ranges::any_of(input, internal::predicate);\n  }\n  {\n    internal::TestPair input[] = {{1, 2}, {3, 4}};\n    ranges::any_of(input, base::identity{}, &internal::TestPair::a);\n  }\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/ranges/functional.h",
    "content": "// Copyright 2020 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_RANGES_FUNCTIONAL_H_\n#define TACHYON_BASE_RANGES_FUNCTIONAL_H_\n\n#include <functional>\n#include <type_traits>\n#include <utility>\n\nnamespace tachyon::base {\n\nnamespace ranges {\n\n// Simplified implementations of C++20's std::ranges comparison function\n// objects. As opposed to the std::ranges implementation, these versions do not\n// constrain the passed-in types.\n//\n// Reference: https://wg21.link/range.cmp\nusing equal_to = std::equal_to<>;\nusing not_equal_to = std::not_equal_to<>;\nusing greater = std::greater<>;\nusing less = std::less<>;\nusing greater_equal = std::greater_equal<>;\nusing less_equal = std::less_equal<>;\n\n}  // namespace ranges\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_RANGES_FUNCTIONAL_H_\n"
  },
  {
    "path": "tachyon/base/ranges/functional_unittest.cc",
    "content": "// Copyright 2020 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/ranges/functional.h\"\n\n#include \"gtest/gtest.h\"\n\nnamespace tachyon::base {\n\nTEST(RangesTest, EqualTo) {\n  ranges::equal_to eq;\n  EXPECT_TRUE(eq(0, 0));\n  EXPECT_FALSE(eq(0, 1));\n  EXPECT_FALSE(eq(1, 0));\n}\n\nTEST(RangesTest, Less) {\n  ranges::less lt;\n  EXPECT_FALSE(lt(0, 0));\n  EXPECT_TRUE(lt(0, 1));\n  EXPECT_FALSE(lt(1, 0));\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/ranges/ranges.h",
    "content": "// Copyright 2020 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_RANGES_RANGES_H_\n#define TACHYON_BASE_RANGES_RANGES_H_\n\n#include <array>\n#include <iterator>\n#include <type_traits>\n#include <utility>\n\n#include \"tachyon/base/template_util.h\"\n\nnamespace tachyon::base {\n\nnamespace internal {\n\n// Overload for C array.\ntemplate <typename T, size_t N>\nconstexpr T* begin(T (&array)[N], priority_tag<2>) {\n  return array;\n}\n\n// Overload for mutable std::array. Required since std::array::begin is not\n// constexpr prior to C++17. Needs to dispatch to the const overload since only\n// const operator[] is constexpr in C++14.\ntemplate <typename T, size_t N>\nconstexpr T* begin(std::array<T, N>& array, priority_tag<2> tag) {\n  return const_cast<T*>(begin(const_cast<const std::array<T, N>&>(array), tag));\n}\n\n// Overload for const std::array. Required since std::array::begin is not\n// constexpr prior to C++17.\ntemplate <typename T, size_t N>\nconstexpr const T* begin(const std::array<T, N>& array, priority_tag<2>) {\n  return N != 0 ? &array[0] : nullptr;\n}\n\n// Generic container overload.\ntemplate <typename Range>\nconstexpr auto begin(Range&& range, priority_tag<1>)\n    -> decltype(std::forward<Range>(range).begin()) {\n  return std::forward<Range>(range).begin();\n}\n\n// Overload for free begin() function.\ntemplate <typename Range>\nconstexpr auto begin(Range&& range, priority_tag<0>)\n    -> decltype(begin(std::forward<Range>(range))) {\n  return begin(std::forward<Range>(range));\n}\n\n// Overload for C array.\ntemplate <typename T, size_t N>\nconstexpr T* end(T (&array)[N], priority_tag<2>) {\n  return array + N;\n}\n\n// Overload for mutable std::array. Required since std::array::end is not\n// constexpr prior to C++17. Needs to dispatch to the const overload since only\n// const operator[] is constexpr in C++14.\ntemplate <typename T, size_t N>\nconstexpr T* end(std::array<T, N>& array, priority_tag<2> tag) {\n  return const_cast<T*>(end(const_cast<const std::array<T, N>&>(array), tag));\n}\n\n// Overload for const std::array. Required since std::array::end is not\n// constexpr prior to C++17.\ntemplate <typename T, size_t N>\nconstexpr const T* end(const std::array<T, N>& array, priority_tag<2>) {\n  return N != 0 ? (&array[0]) + N : nullptr;\n}\n\n// Generic container overload.\ntemplate <typename Range>\nconstexpr auto end(Range&& range, priority_tag<1>)\n    -> decltype(std::forward<Range>(range).end()) {\n  return std::forward<Range>(range).end();\n}\n\n// Overload for free end() function.\ntemplate <typename Range>\nconstexpr auto end(Range&& range, priority_tag<0>)\n    -> decltype(end(std::forward<Range>(range))) {\n  return end(std::forward<Range>(range));\n}\n\n}  // namespace internal\n\nnamespace ranges {\n\n// Simplified implementation of C++20's std::ranges::begin.\n// As opposed to std::ranges::begin, this implementation does does not check\n// whether begin() returns an iterator and does not inhibit ADL.\n//\n// The trailing return type and dispatch to the internal implementation is\n// necessary to be SFINAE friendly.\n//\n// Reference: https://wg21.link/range.access.begin\ntemplate <typename Range>\nconstexpr auto begin(Range&& range) noexcept\n    -> decltype(internal::begin(std::forward<Range>(range),\n                                internal::priority_tag<2>())) {\n  return internal::begin(std::forward<Range>(range),\n                         internal::priority_tag<2>());\n}\n\n// Simplified implementation of C++20's std::ranges::end.\n// As opposed to std::ranges::end, this implementation does does not check\n// whether end() returns an iterator and does not inhibit ADL.\n//\n// The trailing return type and dispatch to the internal implementation is\n// necessary to be SFINAE friendly.\n//\n// Reference: - https://wg21.link/range.access.end\ntemplate <typename Range>\nconstexpr auto end(Range&& range) noexcept\n    -> decltype(internal::end(std::forward<Range>(range),\n                              internal::priority_tag<2>())) {\n  return internal::end(std::forward<Range>(range), internal::priority_tag<2>());\n}\n\n// Implementation of C++20's std::ranges::iterator_t.\n//\n// Reference: https://wg21.link/ranges.syn#:~:text=iterator_t\ntemplate <typename Range>\nusing iterator_t = decltype(ranges::begin(std::declval<Range&>()));\n\n// Implementation of C++20's std::ranges::range_value_t.\n//\n// Reference: https://wg21.link/ranges.syn#:~:text=range_value_t\ntemplate <typename Range>\nusing range_value_t = iter_value_t<iterator_t<Range>>;\n\n}  // namespace ranges\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_RANGES_RANGES_H_\n"
  },
  {
    "path": "tachyon/base/ranges/ranges_unittest.cc",
    "content": "// Copyright 2020 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/ranges/ranges.h\"\n\n#include <array>\n#include <initializer_list>\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\nnamespace tachyon::base {\n\nnamespace {\n\n// Test struct with free function overloads for begin and end. Tests whether the\n// free functions are found.\nstruct S {\n  std::vector<int> v;\n};\n\nauto begin(const S& s) { return s.v.begin(); }\n\nauto end(const S& s) { return s.v.end(); }\n\n// Test struct with both member and free function overloads for begin and end.\n// Tests whether the member function is preferred.\nstruct T {\n  constexpr int begin() const { return 1; }\n  constexpr int end() const { return 1; }\n};\n\n// constexpr utility to generate a std::array. Ensures that a mutable array can\n// be used in a constexpr context.\ntemplate <size_t N>\nconstexpr std::array<int, N> GenerateArray() {\n  std::array<int, N> arr{};\n  int i = 0;\n  for (auto* it = ranges::begin(arr); it != ranges::end(arr); ++it) {\n    *it = i++;\n  }\n\n  return arr;\n}\n\n}  // namespace\n\nTEST(RangesTest, BeginPrefersMember) {\n  constexpr T t;\n  static_assert(ranges::begin(t) == 1, \"\");\n}\n\nTEST(RangesTest, BeginConstexprContainers) {\n  int arr[1]{};\n  static_assert(arr == ranges::begin(arr), \"\");\n\n  static constexpr std::initializer_list<int> il = {1, 2, 3};\n  static_assert(il.begin() == ranges::begin(il), \"\");\n\n  static constexpr std::array<int, 3> array = {1, 2, 3};\n  static_assert(&array[0] == ranges::begin(array), \"\");\n}\n\nTEST(RangesTest, BeginRegularContainers) {\n  std::vector<int> vec;\n  S s;\n\n  EXPECT_EQ(vec.begin(), ranges::begin(vec));\n  EXPECT_EQ(s.v.begin(), ranges::begin(s));\n}\n\nTEST(RangesTest, EndPrefersMember) {\n  constexpr T t;\n  static_assert(ranges::end(t) == 1, \"\");\n}\n\nTEST(RangesTest, EndConstexprContainers) {\n  int arr[1]{};\n  static_assert(arr + 1 == ranges::end(arr), \"\");\n\n  static constexpr std::initializer_list<int> il = {1, 2, 3};\n  static_assert(il.end() == ranges::end(il), \"\");\n\n  static constexpr std::array<int, 3> array = {1, 2, 3};\n  static_assert(&array[0] + 3 == ranges::end(array), \"\");\n}\n\nTEST(RangesTest, EndRegularContainers) {\n  std::vector<int> vec;\n  S s;\n\n  EXPECT_EQ(vec.end(), ranges::end(vec));\n  EXPECT_EQ(s.v.end(), ranges::end(s));\n}\n\nTEST(RangesTest, BeginEndStdArray) {\n  static constexpr std::array<int, 0> null_array = GenerateArray<0>();\n  static_assert(ranges::begin(null_array) == ranges::end(null_array), \"\");\n\n  static constexpr std::array<int, 3> array = GenerateArray<3>();\n  static_assert(array[0] == 0, \"\");\n  static_assert(array[1] == 1, \"\");\n  static_assert(array[2] == 2, \"\");\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/ref.h",
    "content": "#ifndef TACHYON_BASE_REF_H_\n#define TACHYON_BASE_REF_H_\n\n#include <utility>\n\n#include \"absl/hash/hash.h\"\n\nnamespace tachyon::base {\n\ntemplate <typename T, bool SHALLOW = true>\nclass Ref {\n public:\n  Ref() = default;\n  explicit Ref(T* ref) : ref_(ref) {}\n\n  const T& operator*() const { return *get(); }\n  T& operator*() { return *get(); }\n\n  const T* operator->() const { return get(); }\n  T* operator->() { return get(); }\n\n  operator bool() const { return !!ref_; }\n\n  const T* get() const { return ref_; }\n  T* get() { return ref_; }\n\n  bool operator==(const Ref& other) const {\n    if constexpr (SHALLOW) {\n      return ref_ == other.ref_;\n    } else {\n      return *ref_ == *other.ref_;\n    }\n  }\n  bool operator!=(const Ref& other) const {\n    if constexpr (SHALLOW) {\n      return ref_ != other.ref_;\n    } else {\n      return *ref_ != *other.ref_;\n    }\n  }\n  bool operator<(const Ref& other) const {\n    if constexpr (SHALLOW) {\n      return ref_ < other.ref_;\n    } else {\n      return *ref_ < *other.ref_;\n    }\n  }\n  bool operator<=(const Ref& other) const {\n    if constexpr (SHALLOW) {\n      return ref_ <= other.ref_;\n    } else {\n      return *ref_ <= *other.ref_;\n    }\n  }\n  bool operator>(const Ref& other) const {\n    if constexpr (SHALLOW) {\n      return ref_ > other.ref_;\n    } else {\n      return *ref_ > *other.ref_;\n    }\n  }\n  bool operator>=(const Ref& other) const {\n    if constexpr (SHALLOW) {\n      return ref_ >= other.ref_;\n    } else {\n      return *ref_ >= *other.ref_;\n    }\n  }\n\n private:\n  // not owned\n  T* ref_ = nullptr;\n};\n\ntemplate <typename T>\nusing ShallowRef = Ref<T, true>;\n\ntemplate <typename T>\nusing DeepRef = Ref<T, false>;\n\ntemplate <typename H, typename T, bool SHALLOW>\nH AbslHashValue(H h, const Ref<T, SHALLOW>& ref) {\n  if constexpr (SHALLOW) {\n    return H::combine(std::move(h), ref.get());\n  } else {\n    return H::combine(std::move(h), *ref);\n  }\n}\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_REF_H_\n"
  },
  {
    "path": "tachyon/base/ref_unittest.cc",
    "content": "#include \"tachyon/base/ref.h\"\n\n#include \"absl/hash/hash_testing.h\"\n#include \"gtest/gtest.h\"\n\nnamespace tachyon::base {\n\nTEST(RefTest, BoolCasting) {\n  Ref<int> ref;\n  EXPECT_FALSE(ref);\n  int value = 1;\n  ref = Ref<int>(&value);\n  EXPECT_TRUE(ref);\n}\n\nTEST(RefTest, MutableRef) {\n  int value = 1;\n  Ref<int> ref(&value);\n  EXPECT_EQ(*ref, 1);\n  EXPECT_EQ(ref.get(), &value);\n  *ref = 2;\n  EXPECT_EQ(*ref, 2);\n  EXPECT_EQ(ref.get(), &value);\n}\n\nTEST(RefTest, ConstRef) {\n  int value = 1;\n  Ref<const int> ref(&value);\n  EXPECT_EQ(*ref, 1);\n  EXPECT_EQ(ref.get(), &value);\n}\n\nTEST(RefTest, Shallow) {\n  int value = 1;\n  ShallowRef<int> ref(&value);\n  int value2 = 1;\n  ShallowRef<int> ref2(&value2);\n  int value3 = 2;\n  ShallowRef<int> ref3(&value3);\n  EXPECT_TRUE(ref != ref2);\n  EXPECT_FALSE(ref == ref2);\n  EXPECT_TRUE(ref != ref3);\n  EXPECT_FALSE(ref == ref3);\n\n  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(\n      std::make_tuple(ref, ref2, ref3)));\n}\n\nTEST(RefTest, Deep) {\n  int value = 1;\n  DeepRef<int> ref(&value);\n  int value2 = 1;\n  DeepRef<int> ref2(&value2);\n  int value3 = 2;\n  DeepRef<int> ref3(&value3);\n  EXPECT_TRUE(ref == ref2);\n  EXPECT_FALSE(ref != ref2);\n  EXPECT_FALSE(ref == ref3);\n  EXPECT_TRUE(ref != ref3);\n\n  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(\n      std::make_tuple(ref, ref2, ref3)));\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/scoped_generic.h",
    "content": "// Copyright 2014 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n// clang-format off\n\n#ifndef TACHYON_BASE_SCOPED_GENERIC_H_\n#define TACHYON_BASE_SCOPED_GENERIC_H_\n\n#include <stdlib.h>\n\n#include <type_traits>\n\n#include \"tachyon/base/logging.h\"\n// #include \"base/memory/raw_ptr.h\"\n\nnamespace tachyon::base {\n\n// This class acts like unique_ptr with a custom deleter (although is slightly\n// less fancy in some of the more escoteric respects) except that it keeps a\n// copy of the object rather than a pointer, and we require that the contained\n// object has some kind of \"invalid\" value.\n//\n// Defining a scoper based on this class allows you to get a scoper for\n// non-pointer types without having to write custom code for set, reset, and\n// move, etc. and get almost identical semantics that people are used to from\n// unique_ptr.\n//\n// It is intended that you will typedef this class with an appropriate deleter\n// to implement clean up tasks for objects that act like pointers from a\n// resource management standpoint but aren't, such as file descriptors and\n// various types of operating system handles. Using unique_ptr for these\n// things requires that you keep a pointer to the handle valid for the lifetime\n// of the scoper (which is easy to mess up).\n//\n// For an object to be able to be put into a ScopedGeneric, it must support\n// standard copyable semantics and have a specific \"invalid\" value. The traits\n// must define a free function and also the invalid value to assign for\n// default-constructed and released objects.\n//\n//   struct FooScopedTraits {\n//     // It's assumed that this is a fast inline function with little-to-no\n//     // penalty for duplicate calls. This must be a static function even\n//     // for stateful traits.\n//     static int InvalidValue() {\n//       return 0;\n//     }\n//\n//     // This free function will not be called if f == InvalidValue()!\n//     static void Free(int f) {\n//       ::FreeFoo(f);\n//     }\n//   };\n//\n//   using ScopedFoo = ScopedGeneric<int, FooScopedTraits>;\n//\n// A Traits type may choose to track ownership of objects in parallel with\n// ScopedGeneric. To do so, it must implement the Acquire and Release methods,\n// which will be called by ScopedGeneric during ownership transfers and extend\n// the ScopedGenericOwnershipTracking tag type.\n//\n//   struct BarScopedTraits : public ScopedGenericOwnershipTracking {\n//     using ScopedGenericType = ScopedGeneric<int, BarScopedTraits>;\n//     static int InvalidValue() {\n//       return 0;\n//     }\n//\n//     static void Free(int b) {\n//       ::FreeBar(b);\n//     }\n//\n//     static void Acquire(const ScopedGenericType& owner, int b) {\n//       ::TrackAcquisition(b, owner);\n//     }\n//\n//     static void Release(const ScopedGenericType& owner, int b) {\n//       ::TrackRelease(b, owner);\n//     }\n//   };\n//\n//   using ScopedBar = ScopedGeneric<int, BarScopedTraits>;\nstruct ScopedGenericOwnershipTracking {};\n\ntemplate<typename T, typename Traits>\nclass ScopedGeneric {\n private:\n  // This must be first since it's used inline below.\n  //\n  // Use the empty base class optimization to allow us to have a D\n  // member, while avoiding any space overhead for it when D is an\n  // empty class.  See e.g. http://www.cantrip.org/emptyopt.html for a good\n  // discussion of this technique.\n  struct Data : public Traits {\n    explicit Data(const T& in) : generic(in) {}\n    Data(const T& in, const Traits& other) : Traits(other), generic(in) {}\n    T generic;\n  };\n\n public:\n  typedef T element_type;\n  typedef Traits traits_type;\n\n  ScopedGeneric() : data_(traits_type::InvalidValue()) {}\n\n  // Constructor. Takes responsibility for freeing the resource associated with\n  // the object T.\n  explicit ScopedGeneric(const element_type& value) : data_(value) {\n    TrackAcquire(data_.generic);\n  }\n\n  // Constructor. Allows initialization of a stateful traits object.\n  ScopedGeneric(const element_type& value, const traits_type& traits)\n      : data_(value, traits) {\n    TrackAcquire(data_.generic);\n  }\n\n  // Move constructor. Allows initialization from a ScopedGeneric rvalue.\n  ScopedGeneric(ScopedGeneric<T, Traits>&& rvalue)\n      : data_(rvalue.release(), rvalue.get_traits()) {\n    TrackAcquire(data_.generic);\n  }\n  ScopedGeneric(const ScopedGeneric&) = delete;\n  ScopedGeneric& operator=(const ScopedGeneric&) = delete;\n\n  virtual ~ScopedGeneric() {\n    CHECK(!receiving_);  // ScopedGeneric destroyed with active receiver.\n    FreeIfNecessary();\n  }\n\n  // operator=. Allows assignment from a ScopedGeneric rvalue.\n  ScopedGeneric& operator=(ScopedGeneric<T, Traits>&& rvalue) {\n    reset(rvalue.release());\n    return *this;\n  }\n\n  // Frees the currently owned object, if any. Then takes ownership of a new\n  // object, if given. Self-resets are not allowed as on unique_ptr. See\n  // http://crbug.com/162971\n  void reset(const element_type& value = traits_type::InvalidValue()) {\n    if (data_.generic != traits_type::InvalidValue() && data_.generic == value)\n      abort();\n    FreeIfNecessary();\n    data_.generic = value;\n    TrackAcquire(value);\n  }\n\n  // Release the object. The return value is the current object held by this\n  // object. After this operation, this object will hold a null value, and\n  // will not own the object any more.\n  [[nodiscard]] element_type release() {\n    element_type old_generic = data_.generic;\n    data_.generic = traits_type::InvalidValue();\n    TrackRelease(old_generic);\n    return old_generic;\n  }\n\n  // A helper class that provides a T* that can be used to take ownership of\n  // a value returned from a function via out-parameter. When the Receiver is\n  // destructed (which should usually be at the end of the statement in which\n  // receive is called), ScopedGeneric::reset() will be called with the\n  // Receiver's value.\n  //\n  // In the simple case of a function that assigns the value before it returns,\n  // C++'s lifetime extension can be used as follows:\n  //\n  //    ScopedFoo foo;\n  //    bool result = GetFoo(ScopedFoo::Receiver(foo).get());\n  //\n  // Note that the lifetime of the Receiver is extended until the semicolon,\n  // and ScopedGeneric is assigned the value upon destruction of the Receiver,\n  // so the following code would not work:\n  //\n  //    // BROKEN!\n  //    ScopedFoo foo;\n  //    UseFoo(&foo, GetFoo(ScopedFoo::Receiver(foo).get()));\n  //\n  // In more complicated scenarios, you may need to provide an explicit scope\n  // for the Receiver, as in the following:\n  //\n  //    std::vector<ScopedFoo> foos(64);\n  //\n  //    {\n  //      std::vector<ScopedFoo::Receiver> foo_receivers;\n  //      for (auto foo : foos) {\n  //        foo_receivers_.emplace_back(foo);\n  //      }\n  //      for (auto receiver : foo_receivers) {\n  //        SubmitGetFooRequest(receiver.get());\n  //      }\n  //      WaitForFooRequests();\n  //    }\n  //    UseFoos(foos);\n  class Receiver {\n   public:\n    explicit Receiver(ScopedGeneric& parent) : scoped_generic_(&parent) {\n      // Check if we attempted to construct a Receiver for ScopedGeneric with an\n      // existing Receiver.\n      CHECK(!scoped_generic_->receiving_);\n      scoped_generic_->receiving_ = true;\n    }\n    Receiver(const Receiver&) = delete;\n    Receiver& operator=(const Receiver&) = delete;\n    Receiver(Receiver&& move) {\n      CHECK(!used_);       // Moving into already-used Receiver.\n      CHECK(!move.used_);  // Moving from already-used Receiver.\n      scoped_generic_ = move.scoped_generic_;\n      move.scoped_generic_ = nullptr;\n    }\n\n    Receiver& operator=(Receiver&& move) {\n      CHECK(!used_);       // Moving into already-used Receiver.\n      CHECK(!move.used_);  // Moving from already-used Receiver.\n      scoped_generic_ = move.scoped_generic_;\n      move.scoped_generic_ = nullptr;\n    }\n    ~Receiver() {\n      if (scoped_generic_) {\n        CHECK(scoped_generic_->receiving_);\n        scoped_generic_->reset(value_);\n        scoped_generic_->receiving_ = false;\n      }\n    }\n    // We hand out a pointer to a field in Receiver instead of directly to\n    // ScopedGeneric's internal storage in order to make it so that users can't\n    // accidentally silently break ScopedGeneric's invariants. This way, an\n    // incorrect use-after-scope-exit is more detectable by ASan or static\n    // analysis tools, as the pointer is only valid for the lifetime of the\n    // Receiver, not the ScopedGeneric.\n    T* get() {\n      used_ = true;\n      return &value_;\n    }\n\n   private:\n    T value_ = Traits::InvalidValue();\n    // raw_ptr<ScopedGeneric<T, Traits>> scoped_generic_;\n    ScopedGeneric<T, Traits>* scoped_generic_;\n    bool used_ = false;\n  };\n\n  const element_type& get() const { return data_.generic; }\n\n  // Returns true if this object doesn't hold the special null value for the\n  // associated data type.\n  bool is_valid() const { return data_.generic != traits_type::InvalidValue(); }\n\n  bool operator==(const element_type& value) const {\n    return data_.generic == value;\n  }\n  bool operator!=(const element_type& value) const {\n    return data_.generic != value;\n  }\n\n  Traits& get_traits() { return data_; }\n  const Traits& get_traits() const { return data_; }\n\n private:\n  void FreeIfNecessary() {\n    if (data_.generic != traits_type::InvalidValue()) {\n      TrackRelease(data_.generic);\n      data_.Free(data_.generic);\n      data_.generic = traits_type::InvalidValue();\n    }\n  }\n\n  template <typename Void = void>\n  typename std::enable_if_t<\n      std::is_base_of<ScopedGenericOwnershipTracking, Traits>::value,\n      Void>\n  TrackAcquire(const T& value) {\n    if (value != traits_type::InvalidValue()) {\n      data_.Acquire(static_cast<const ScopedGeneric&>(*this), value);\n    }\n  }\n\n  template <typename Void = void>\n  typename std::enable_if_t<\n      !std::is_base_of<ScopedGenericOwnershipTracking, Traits>::value,\n      Void>\n  TrackAcquire(const T& value) {}\n\n  template <typename Void = void>\n  typename std::enable_if_t<\n      std::is_base_of<ScopedGenericOwnershipTracking, Traits>::value,\n      Void>\n  TrackRelease(const T& value) {\n    if (value != traits_type::InvalidValue()) {\n      data_.Release(static_cast<const ScopedGeneric&>(*this), value);\n    }\n  }\n\n  template <typename Void = void>\n  typename std::enable_if_t<\n      !std::is_base_of<ScopedGenericOwnershipTracking, Traits>::value,\n      Void>\n  TrackRelease(const T& value) {}\n\n  // Forbid comparison. If U != T, it totally doesn't make sense, and if U ==\n  // T, it still doesn't make sense because you should never have the same\n  // object owned by two different ScopedGenerics.\n  template <typename T2, typename Traits2> bool operator==(\n      const ScopedGeneric<T2, Traits2>& p2) const;\n  template <typename T2, typename Traits2> bool operator!=(\n      const ScopedGeneric<T2, Traits2>& p2) const;\n\n  Data data_;\n  bool receiving_ = false;\n};\n\ntemplate<class T, class Traits>\nvoid swap(const ScopedGeneric<T, Traits>& a,\n          const ScopedGeneric<T, Traits>& b) {\n  a.swap(b);\n}\n\ntemplate<class T, class Traits>\nbool operator==(const T& value, const ScopedGeneric<T, Traits>& scoped) {\n  return value == scoped.get();\n}\n\ntemplate<class T, class Traits>\nbool operator!=(const T& value, const ScopedGeneric<T, Traits>& scoped) {\n  return value != scoped.get();\n}\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_SCOPED_GENERIC_H_\n\n// clang-format on\n"
  },
  {
    "path": "tachyon/base/scoped_generic_unittest.cc",
    "content": "// Copyright 2014 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n// clang-format off\n\n#include \"tachyon/base/scoped_generic.h\"\n\n#include <memory>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/containers/contains.h\"\n// #include \"tachyon/base/memory/raw_ptr.h\"\n#include \"tachyon/build/build_config.h\"\n\nnamespace tachyon::base {\n\nnamespace {\n\nstruct IntTraits {\n  IntTraits(std::vector<int>* freed) : freed_ints(freed) {}\n\n  static int InvalidValue() {\n    return -1;\n  }\n  void Free(int value) {\n    freed_ints->push_back(value);\n  }\n\n  // raw_ptr<std::vector<int>> freed_ints;\n  std::vector<int>* freed_ints;\n};\n\nusing ScopedInt = ScopedGeneric<int, IntTraits>;\n\n}  // namespace\n\nTEST(ScopedGenericTest, ScopedGeneric) {\n  std::vector<int> values_freed;\n  IntTraits traits(&values_freed);\n\n  // Invalid case, delete should not be called.\n  {\n    ScopedInt a(IntTraits::InvalidValue(), traits);\n  }\n  EXPECT_TRUE(values_freed.empty());\n\n  // Simple deleting case.\n  static const int kFirst = 0;\n  {\n    ScopedInt a(kFirst, traits);\n  }\n  ASSERT_EQ(1u, values_freed.size());\n  ASSERT_EQ(kFirst, values_freed[0]);\n  values_freed.clear();\n\n  // Release should return the right value and leave the object empty.\n  {\n    ScopedInt a(kFirst, traits);\n    EXPECT_EQ(kFirst, a.release());\n\n    ScopedInt b(IntTraits::InvalidValue(), traits);\n    EXPECT_EQ(IntTraits::InvalidValue(), b.release());\n  }\n  ASSERT_TRUE(values_freed.empty());\n\n  // Reset should free the old value, then the new one should go away when\n  // it goes out of scope.\n  static const int kSecond = 1;\n  {\n    ScopedInt b(kFirst, traits);\n    b.reset(kSecond);\n    ASSERT_EQ(1u, values_freed.size());\n    ASSERT_EQ(kFirst, values_freed[0]);\n  }\n  ASSERT_EQ(2u, values_freed.size());\n  ASSERT_EQ(kSecond, values_freed[1]);\n  values_freed.clear();\n\n  // Move constructor.\n  {\n    ScopedInt a(kFirst, traits);\n    ScopedInt b(std::move(a));\n    EXPECT_TRUE(values_freed.empty());  // Nothing should be freed.\n    ASSERT_EQ(IntTraits::InvalidValue(), a.get());\n    ASSERT_EQ(kFirst, b.get());\n  }\n\n  ASSERT_EQ(1u, values_freed.size());\n  ASSERT_EQ(kFirst, values_freed[0]);\n  values_freed.clear();\n\n  // Move assign.\n  {\n    ScopedInt a(kFirst, traits);\n    ScopedInt b(kSecond, traits);\n    b = std::move(a);\n    ASSERT_EQ(1u, values_freed.size());\n    EXPECT_EQ(kSecond, values_freed[0]);\n    ASSERT_EQ(IntTraits::InvalidValue(), a.get());\n    ASSERT_EQ(kFirst, b.get());\n  }\n\n  ASSERT_EQ(2u, values_freed.size());\n  EXPECT_EQ(kFirst, values_freed[1]);\n  values_freed.clear();\n}\n\nTEST(ScopedGenericTest, Operators) {\n  std::vector<int> values_freed;\n  IntTraits traits(&values_freed);\n\n  static const int kFirst = 0;\n  static const int kSecond = 1;\n  {\n    ScopedInt a(kFirst, traits);\n    EXPECT_TRUE(a == kFirst);\n    EXPECT_FALSE(a != kFirst);\n    EXPECT_FALSE(a == kSecond);\n    EXPECT_TRUE(a != kSecond);\n\n    EXPECT_TRUE(kFirst == a);\n    EXPECT_FALSE(kFirst != a);\n    EXPECT_FALSE(kSecond == a);\n    EXPECT_TRUE(kSecond != a);\n  }\n\n  // is_valid().\n  {\n    ScopedInt a(kFirst, traits);\n    EXPECT_TRUE(a.is_valid());\n    a.reset();\n    EXPECT_FALSE(a.is_valid());\n  }\n}\n\nTEST(ScopedGenericTest, Receive) {\n  std::vector<int> values_freed;\n  IntTraits traits(&values_freed);\n  auto a = std::make_unique<ScopedInt>(123, traits);\n\n  EXPECT_EQ(123, a->get());\n\n  {\n    ScopedInt::Receiver r(*a);\n    EXPECT_EQ(123, a->get());\n    *r.get() = 456;\n    EXPECT_EQ(123, a->get());\n  }\n\n  EXPECT_EQ(456, a->get());\n\n  {\n    ScopedInt::Receiver r(*a);\n    EXPECT_DEATH_IF_SUPPORTED(a.reset(), \"\");\n    EXPECT_DEATH_IF_SUPPORTED(ScopedInt::Receiver(*a).get(), \"\");\n  }\n}\n\nnamespace {\n\nstruct TrackedIntTraits : public ScopedGenericOwnershipTracking {\n  using OwnerMap =\n      std::unordered_map<int, const ScopedGeneric<int, TrackedIntTraits>*>;\n  TrackedIntTraits(std::unordered_set<int>* freed, OwnerMap* owners)\n      : freed(freed), owners(owners) {}\n\n  static int InvalidValue() { return -1; }\n\n  void Free(int value) {\n    auto it = owners->find(value);\n    ASSERT_EQ(owners->end(), it);\n\n    ASSERT_EQ(0U, freed->count(value));\n    freed->insert(value);\n  }\n\n  void Acquire(const ScopedGeneric<int, TrackedIntTraits>& owner, int value) {\n    auto it = owners->find(value);\n    ASSERT_EQ(owners->end(), it);\n    (*owners)[value] = &owner;\n  }\n\n  void Release(const ScopedGeneric<int, TrackedIntTraits>& owner, int value) {\n    auto it = owners->find(value);\n    ASSERT_NE(owners->end(), it);\n    owners->erase(it);\n  }\n\n  // raw_ptr<std::unordered_set<int>> freed;\n  // raw_ptr<OwnerMap> owners;\n  std::unordered_set<int>* freed;\n  OwnerMap* owners;\n};\n\nusing ScopedTrackedInt = ScopedGeneric<int, TrackedIntTraits>;\n\n}  // namespace\n\nTEST(ScopedGenericTest, OwnershipTracking) {\n  TrackedIntTraits::OwnerMap owners;\n  std::unordered_set<int> freed;\n  TrackedIntTraits traits(&freed, &owners);\n\n#define ASSERT_OWNED(value, owner)            \\\n  ASSERT_TRUE(base::Contains(owners, value)); \\\n  ASSERT_EQ(&owner, owners[value]);           \\\n  ASSERT_FALSE(base::Contains(freed, value))\n\n#define ASSERT_UNOWNED(value)                  \\\n  ASSERT_FALSE(base::Contains(owners, value)); \\\n  ASSERT_FALSE(base::Contains(freed, value))\n\n#define ASSERT_FREED(value)                    \\\n  ASSERT_FALSE(base::Contains(owners, value)); \\\n  ASSERT_TRUE(base::Contains(freed, value))\n\n  // Constructor.\n  {\n    {\n      ScopedTrackedInt a(0, traits);\n      ASSERT_OWNED(0, a);\n    }\n    ASSERT_FREED(0);\n  }\n\n  owners.clear();\n  freed.clear();\n\n  // Reset.\n  {\n    ScopedTrackedInt a(0, traits);\n    ASSERT_OWNED(0, a);\n    a.reset(1);\n    ASSERT_FREED(0);\n    ASSERT_OWNED(1, a);\n    a.reset();\n    ASSERT_FREED(0);\n    ASSERT_FREED(1);\n  }\n\n  owners.clear();\n  freed.clear();\n\n  // Release.\n  {\n    {\n      ScopedTrackedInt a(0, traits);\n      ASSERT_OWNED(0, a);\n      int released = a.release();\n      ASSERT_EQ(0, released);\n      ASSERT_UNOWNED(0);\n    }\n    ASSERT_UNOWNED(0);\n  }\n\n  owners.clear();\n  freed.clear();\n\n  // Move constructor.\n  {\n    ScopedTrackedInt a(0, traits);\n    ASSERT_OWNED(0, a);\n    {\n      ScopedTrackedInt b(std::move(a));\n      ASSERT_OWNED(0, b);\n    }\n    ASSERT_FREED(0);\n  }\n\n  owners.clear();\n  freed.clear();\n\n  // Move assignment.\n  {\n    {\n      ScopedTrackedInt a(0, traits);\n      ScopedTrackedInt b(1, traits);\n      ASSERT_OWNED(0, a);\n      ASSERT_OWNED(1, b);\n      a = std::move(b);\n      ASSERT_OWNED(1, a);\n      ASSERT_FREED(0);\n    }\n    ASSERT_FREED(1);\n  }\n\n  owners.clear();\n  freed.clear();\n\n#undef ASSERT_OWNED\n#undef ASSERT_UNOWNED\n#undef ASSERT_FREED\n}\n\n// Cheesy manual \"no compile\" test for manually validating changes.\n#if 0\nTEST(ScopedGenericTest, NoCompile) {\n  // Assignment shouldn't work.\n  /*{\n    ScopedInt a(kFirst, traits);\n    ScopedInt b(a);\n  }*/\n\n  // Comparison shouldn't work.\n  /*{\n    ScopedInt a(kFirst, traits);\n    ScopedInt b(kFirst, traits);\n    if (a == b) {\n    }\n  }*/\n\n  // Implicit conversion to bool shouldn't work.\n  /*{\n    ScopedInt a(kFirst, traits);\n    bool result = a;\n  }*/\n}\n#endif\n\n}  // namespace tachyon::base\n\n// clang-format on\n"
  },
  {
    "path": "tachyon/base/sort.h",
    "content": "#ifndef TACHYON_BASE_SORT_H_\n#define TACHYON_BASE_SORT_H_\n\n#include \"third_party/pdqsort/include/pdqsort.h\"\n#include \"third_party/powersort/include/sorts/powersort.h\"\n\nnamespace tachyon::base {\n\ntemplate <typename Iter>\nvoid UnstableSort(Iter begin, Iter end) {\n  return pdqsort(begin, end);\n}\n\ntemplate <typename Iter, typename Compare>\nvoid UnstableSort(Iter begin, Iter end, Compare compare) {\n  return pdqsort(begin, end, compare);\n}\n\n// TODO(chokobole): Add StableSort() with compare version.\ntemplate <typename Iter>\nvoid StableSort(Iter begin, Iter end) {\n  algorithms::powersort<Iter> sort;\n  sort.sort(begin, end);\n}\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_SORT_H_\n"
  },
  {
    "path": "tachyon/base/sort_benchmark.cc",
    "content": "#include \"benchmark/benchmark.h\"\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/random.h\"\n#include \"tachyon/base/sort.h\"\n\nnamespace tachyon::math {\n\nenum class SortMethod {\n  kPdq,\n  kPowersort,\n  kStdStableSort,\n  kStdSort,\n};\n\nstd::vector<uint64_t> GetData(size_t size) {\n  static std::map<size_t, std::vector<uint64_t>>* s_data_map = nullptr;\n  if (s_data_map == nullptr) {\n    s_data_map = new std::map<size_t, std::vector<uint64_t>>();\n  }\n  std::vector<uint64_t>& data = (*s_data_map)[size];\n  if (data.empty()) {\n    data = base::CreateVector(size, [](size_t i) {\n      return base::Uniform(base::Range<uint64_t>::All());\n    });\n  }\n  return data;\n}\n\nstd::vector<uint64_t> GetPartiallySortedData(size_t size) {\n  static std::map<size_t, std::vector<uint64_t>>* s_data_map = nullptr;\n  if (s_data_map == nullptr) {\n    s_data_map = new std::map<size_t, std::vector<uint64_t>>();\n  }\n  std::vector<uint64_t>& data = (*s_data_map)[size];\n  if (data.empty()) {\n    data = base::CreateVector(size, [](size_t i) { return uint64_t{i}; });\n    size_t shuffle_count = size / 8;\n    for (size_t i = 0; i < shuffle_count; ++i) {\n      size_t idx = base::Uniform(base::Range<size_t>::Until(shuffle_count));\n      size_t idx2 = base::Uniform(base::Range<size_t>::Until(shuffle_count));\n      std::swap(data[idx], data[idx2]);\n    }\n  }\n  return data;\n}\n\ntemplate <SortMethod kSortMethod>\nvoid BM_SortRandomData(benchmark::State& state) {\n  std::vector<uint64_t> data = GetData(state.range(0));\n  std::vector<uint64_t> data2 = data;\n  for (auto _ : state) {\n    if constexpr (kSortMethod == SortMethod::kPdq) {\n      base::UnstableSort(data2.begin(), data2.end());\n    } else if constexpr (kSortMethod == SortMethod::kPowersort) {\n      base::StableSort(data2.begin(), data2.end());\n    } else if constexpr (kSortMethod == SortMethod::kStdStableSort) {\n      std::stable_sort(data2.begin(), data2.end());\n    } else if constexpr (kSortMethod == SortMethod::kStdSort) {\n      std::sort(data2.begin(), data2.end());\n    }\n    data2 = data;\n  }\n  benchmark::DoNotOptimize(data);\n  benchmark::DoNotOptimize(data2);\n}\n\ntemplate <SortMethod kSortMethod>\nvoid BM_SortPartiallySortedData(benchmark::State& state) {\n  std::vector<uint64_t> data = GetPartiallySortedData(state.range(0));\n  std::vector<uint64_t> data2 = data;\n  for (auto _ : state) {\n    if constexpr (kSortMethod == SortMethod::kPdq) {\n      base::UnstableSort(data2.begin(), data2.end());\n    } else if constexpr (kSortMethod == SortMethod::kPowersort) {\n      base::StableSort(data2.begin(), data2.end());\n    } else if constexpr (kSortMethod == SortMethod::kStdStableSort) {\n      std::stable_sort(data2.begin(), data2.end());\n    } else if constexpr (kSortMethod == SortMethod::kStdSort) {\n      std::sort(data2.begin(), data2.end());\n    }\n    data2 = data;\n  }\n  benchmark::DoNotOptimize(data);\n  benchmark::DoNotOptimize(data2);\n}\n\nBENCHMARK_TEMPLATE(BM_SortRandomData, SortMethod::kPdq)\n    ->RangeMultiplier(2)\n    ->Range(1 << 5, 1 << 20);\nBENCHMARK_TEMPLATE(BM_SortRandomData, SortMethod::kStdSort)\n    ->RangeMultiplier(2)\n    ->Range(1 << 5, 1 << 20);\nBENCHMARK_TEMPLATE(BM_SortRandomData, SortMethod::kPowersort)\n    ->RangeMultiplier(2)\n    ->Range(1 << 5, 1 << 20);\nBENCHMARK_TEMPLATE(BM_SortRandomData, SortMethod::kStdStableSort)\n    ->RangeMultiplier(2)\n    ->Range(1 << 5, 1 << 20);\n\nBENCHMARK_TEMPLATE(BM_SortPartiallySortedData, SortMethod::kPdq)\n    ->RangeMultiplier(2)\n    ->Range(1 << 5, 1 << 20);\nBENCHMARK_TEMPLATE(BM_SortPartiallySortedData, SortMethod::kStdSort)\n    ->RangeMultiplier(2)\n    ->Range(1 << 5, 1 << 20);\nBENCHMARK_TEMPLATE(BM_SortPartiallySortedData, SortMethod::kPowersort)\n    ->RangeMultiplier(2)\n    ->Range(1 << 5, 1 << 20);\nBENCHMARK_TEMPLATE(BM_SortPartiallySortedData, SortMethod::kStdStableSort)\n    ->RangeMultiplier(2)\n    ->Range(1 << 5, 1 << 20);\n\n}  // namespace tachyon::math\n\n// clang-format off\n// Executing tests from //tachyon/base:sort_benchmark\n// -----------------------------------------------------------------------------\n// 2024-08-05T09:26:02+00:00\n// Running /home/chokobole/.cache/bazel/_bazel_chokobole/234690e3562329d13f7f07caac03dae4/execroot/kroma_network_tachyon/bazel-out/k8-opt/bin/tachyon/base/sort_benchmark.runfiles/kroma_network_tachyon/tachyon/base/sort_benchmark\n// Run on (32 X 5499.96 MHz CPU s)\n// CPU Caches:\n//   L1 Data 48 KiB (x16)\n//   L1 Instruction 32 KiB (x16)\n//   L2 Unified 2048 KiB (x16)\n//   L3 Unified 36864 KiB (x1)\n// Load Average: 2.58, 2.64, 2.46\n// ---------------------------------------------------------------------------------------------------------\n// Benchmark                                                               Time             CPU   Iterations\n// ---------------------------------------------------------------------------------------------------------\n// BM_SortRandomData<SortMethod::kPdq>/32                               64.5 ns         64.5 ns     10927050\n// BM_SortRandomData<SortMethod::kPdq>/64                                173 ns          173 ns      4082994\n// BM_SortRandomData<SortMethod::kPdq>/128                               458 ns          458 ns      1526526\n// BM_SortRandomData<SortMethod::kPdq>/256                               917 ns          917 ns       767867\n// BM_SortRandomData<SortMethod::kPdq>/512                              1994 ns         1994 ns       349757\n// BM_SortRandomData<SortMethod::kPdq>/1024                             4694 ns         4694 ns       148456\n// BM_SortRandomData<SortMethod::kPdq>/2048                             9822 ns         9822 ns        71312\n// BM_SortRandomData<SortMethod::kPdq>/4096                            29664 ns        29660 ns        23724\n// BM_SortRandomData<SortMethod::kPdq>/8192                            87432 ns        87430 ns         8014\n// BM_SortRandomData<SortMethod::kPdq>/16384                          206323 ns       206317 ns         3389\n// BM_SortRandomData<SortMethod::kPdq>/32768                          449141 ns       449055 ns         1560\n// BM_SortRandomData<SortMethod::kPdq>/65536                          951382 ns       951308 ns          736\n// BM_SortRandomData<SortMethod::kPdq>/131072                        1971393 ns      1971368 ns          355\n// BM_SortRandomData<SortMethod::kPdq>/262144                        4258559 ns      4258174 ns          166\n// BM_SortRandomData<SortMethod::kPdq>/524288                        8783038 ns      8781649 ns           71\n// BM_SortRandomData<SortMethod::kPdq>/1048576                      18092094 ns     18091328 ns           39\n// BM_SortRandomData<SortMethod::kStdSort>/32                           65.7 ns         65.7 ns     10562672\n// BM_SortRandomData<SortMethod::kStdSort>/64                            135 ns          135 ns      5318656\n// BM_SortRandomData<SortMethod::kStdSort>/128                           369 ns          369 ns      1917457\n// BM_SortRandomData<SortMethod::kStdSort>/256                           780 ns          780 ns       906629\n// BM_SortRandomData<SortMethod::kStdSort>/512                          1720 ns         1720 ns       402973\n// BM_SortRandomData<SortMethod::kStdSort>/1024                         3937 ns         3937 ns       178163\n// BM_SortRandomData<SortMethod::kStdSort>/2048                        22613 ns        22612 ns        30836\n// BM_SortRandomData<SortMethod::kStdSort>/4096                        84293 ns        84293 ns         8256\n// BM_SortRandomData<SortMethod::kStdSort>/8192                       212055 ns       212048 ns         3296\n// BM_SortRandomData<SortMethod::kStdSort>/16384                      463883 ns       463879 ns         1505\n// BM_SortRandomData<SortMethod::kStdSort>/32768                     1011653 ns      1011613 ns          692\n// BM_SortRandomData<SortMethod::kStdSort>/65536                     2219845 ns      2219834 ns          315\n// BM_SortRandomData<SortMethod::kStdSort>/131072                    4715038 ns      4714815 ns          149\n// BM_SortRandomData<SortMethod::kStdSort>/262144                   10003182 ns     10002959 ns           70\n// BM_SortRandomData<SortMethod::kStdSort>/524288                   20982786 ns     20982067 ns           33\n// BM_SortRandomData<SortMethod::kStdSort>/1048576                  43885082 ns     43884019 ns           16\n// BM_SortRandomData<SortMethod::kPowersort>/32                         84.1 ns         84.1 ns      8342074\n// BM_SortRandomData<SortMethod::kPowersort>/64                          190 ns          190 ns      3655272\n// BM_SortRandomData<SortMethod::kPowersort>/128                         446 ns          446 ns      1580095\n// BM_SortRandomData<SortMethod::kPowersort>/256                         984 ns          984 ns       711206\n// BM_SortRandomData<SortMethod::kPowersort>/512                        2340 ns         2340 ns       302783\n// BM_SortRandomData<SortMethod::kPowersort>/1024                       5165 ns         5164 ns       130697\n// BM_SortRandomData<SortMethod::kPowersort>/2048                      33551 ns        33551 ns        21216\n// BM_SortRandomData<SortMethod::kPowersort>/4096                     100123 ns       100120 ns         6908\n// BM_SortRandomData<SortMethod::kPowersort>/8192                     246248 ns       246236 ns         2893\n// BM_SortRandomData<SortMethod::kPowersort>/16384                    544985 ns       544968 ns         1285\n// BM_SortRandomData<SortMethod::kPowersort>/32768                   1185154 ns      1185143 ns          590\n// BM_SortRandomData<SortMethod::kPowersort>/65536                   2544378 ns      2544265 ns          275\n// BM_SortRandomData<SortMethod::kPowersort>/131072                  5465731 ns      5465444 ns          128\n// BM_SortRandomData<SortMethod::kPowersort>/262144                 11856806 ns     11856582 ns           59\n// BM_SortRandomData<SortMethod::kPowersort>/524288                 25558533 ns     25557610 ns           27\n// BM_SortRandomData<SortMethod::kPowersort>/1048576                53946952 ns     53946318 ns           12\n// BM_SortRandomData<SortMethod::kStdStableSort>/32                      106 ns          106 ns      6572790\n// BM_SortRandomData<SortMethod::kStdStableSort>/64                      233 ns          233 ns      2997692\n// BM_SortRandomData<SortMethod::kStdStableSort>/128                     497 ns          497 ns      1416719\n// BM_SortRandomData<SortMethod::kStdStableSort>/256                    1133 ns         1133 ns       614965\n// BM_SortRandomData<SortMethod::kStdStableSort>/512                    2474 ns         2473 ns       281798\n// BM_SortRandomData<SortMethod::kStdStableSort>/1024                   5541 ns         5541 ns       126485\n// BM_SortRandomData<SortMethod::kStdStableSort>/2048                  39425 ns        39424 ns        17762\n// BM_SortRandomData<SortMethod::kStdStableSort>/4096                  99459 ns        99456 ns         7006\n// BM_SortRandomData<SortMethod::kStdStableSort>/8192                 238091 ns       238082 ns         2953\n// BM_SortRandomData<SortMethod::kStdStableSort>/16384                539281 ns       539263 ns         1300\n// BM_SortRandomData<SortMethod::kStdStableSort>/32768               1168490 ns      1168483 ns          597\n// BM_SortRandomData<SortMethod::kStdStableSort>/65536               2514056 ns      2513922 ns          279\n// BM_SortRandomData<SortMethod::kStdStableSort>/131072              5325962 ns      5325863 ns          131\n// BM_SortRandomData<SortMethod::kStdStableSort>/262144             11590090 ns     11589633 ns           61\n// BM_SortRandomData<SortMethod::kStdStableSort>/524288             24997070 ns     24993431 ns           29\n// BM_SortRandomData<SortMethod::kStdStableSort>/1048576            52364991 ns     52364081 ns           13\n// BM_SortPartiallySortedData<SortMethod::kPdq>/32                      26.1 ns         26.1 ns     27818789\n// BM_SortPartiallySortedData<SortMethod::kPdq>/64                      67.9 ns         67.9 ns     10076970\n// BM_SortPartiallySortedData<SortMethod::kPdq>/128                      159 ns          159 ns      4460125\n// BM_SortPartiallySortedData<SortMethod::kPdq>/256                      326 ns          326 ns      2155077\n// BM_SortPartiallySortedData<SortMethod::kPdq>/512                      612 ns          612 ns      1158772\n// BM_SortPartiallySortedData<SortMethod::kPdq>/1024                    1205 ns         1205 ns       587540\n// BM_SortPartiallySortedData<SortMethod::kPdq>/2048                    2252 ns         2252 ns       309958\n// BM_SortPartiallySortedData<SortMethod::kPdq>/4096                    4450 ns         4450 ns       157626\n// BM_SortPartiallySortedData<SortMethod::kPdq>/8192                    9237 ns         9237 ns        75456\n// BM_SortPartiallySortedData<SortMethod::kPdq>/16384                  19177 ns        19176 ns        36507\n// BM_SortPartiallySortedData<SortMethod::kPdq>/32768                  48304 ns        48302 ns        14462\n// BM_SortPartiallySortedData<SortMethod::kPdq>/65536                 124313 ns       124310 ns         5590\n// BM_SortPartiallySortedData<SortMethod::kPdq>/131072                282868 ns       282862 ns         2471\n// BM_SortPartiallySortedData<SortMethod::kPdq>/262144                748262 ns       748224 ns          930\n// BM_SortPartiallySortedData<SortMethod::kPdq>/524288               1587168 ns      1587159 ns          441\n// BM_SortPartiallySortedData<SortMethod::kPdq>/1048576              3434923 ns      3434856 ns          204\n// BM_SortPartiallySortedData<SortMethod::kStdSort>/32                  37.8 ns         37.8 ns     17865125\n// BM_SortPartiallySortedData<SortMethod::kStdSort>/64                  81.7 ns         81.7 ns      8572684\n// BM_SortPartiallySortedData<SortMethod::kStdSort>/128                  201 ns          201 ns      3502366\n// BM_SortPartiallySortedData<SortMethod::kStdSort>/256                  459 ns          459 ns      1513415\n// BM_SortPartiallySortedData<SortMethod::kStdSort>/512                 1124 ns         1124 ns       622606\n// BM_SortPartiallySortedData<SortMethod::kStdSort>/1024                2566 ns         2566 ns       274741\n// BM_SortPartiallySortedData<SortMethod::kStdSort>/2048                5570 ns         5570 ns       125864\n// BM_SortPartiallySortedData<SortMethod::kStdSort>/4096               12436 ns        12435 ns        56418\n// BM_SortPartiallySortedData<SortMethod::kStdSort>/8192               26709 ns        26708 ns        26237\n// BM_SortPartiallySortedData<SortMethod::kStdSort>/16384              69598 ns        69597 ns        10023\n// BM_SortPartiallySortedData<SortMethod::kStdSort>/32768             187689 ns       187657 ns         3598\n// BM_SortPartiallySortedData<SortMethod::kStdSort>/65536             422855 ns       422759 ns         1658\n// BM_SortPartiallySortedData<SortMethod::kStdSort>/131072            926691 ns       926666 ns          755\n// BM_SortPartiallySortedData<SortMethod::kStdSort>/262144           2141686 ns      2141672 ns          327\n// BM_SortPartiallySortedData<SortMethod::kStdSort>/524288           4528703 ns      4528676 ns          153\n// BM_SortPartiallySortedData<SortMethod::kStdSort>/1048576          9690864 ns      9690796 ns           72\n// BM_SortPartiallySortedData<SortMethod::kPowersort>/32                47.6 ns         47.6 ns     14566847\n// BM_SortPartiallySortedData<SortMethod::kPowersort>/64                69.7 ns         69.7 ns     10073432\n// BM_SortPartiallySortedData<SortMethod::kPowersort>/128                119 ns          119 ns      5879687\n// BM_SortPartiallySortedData<SortMethod::kPowersort>/256                234 ns          234 ns      2997032\n// BM_SortPartiallySortedData<SortMethod::kPowersort>/512                472 ns          472 ns      1489784\n// BM_SortPartiallySortedData<SortMethod::kPowersort>/1024               959 ns          958 ns       729569\n// BM_SortPartiallySortedData<SortMethod::kPowersort>/2048              1963 ns         1963 ns       358399\n// BM_SortPartiallySortedData<SortMethod::kPowersort>/4096              4909 ns         4908 ns       142478\n// BM_SortPartiallySortedData<SortMethod::kPowersort>/8192             10384 ns        10381 ns        67528\n// BM_SortPartiallySortedData<SortMethod::kPowersort>/16384            43864 ns        43863 ns        15994\n// BM_SortPartiallySortedData<SortMethod::kPowersort>/32768           118543 ns       118541 ns         5922\n// BM_SortPartiallySortedData<SortMethod::kPowersort>/65536           282220 ns       282215 ns         2485\n// BM_SortPartiallySortedData<SortMethod::kPowersort>/131072          639465 ns       639359 ns         1093\n// BM_SortPartiallySortedData<SortMethod::kPowersort>/262144         1601955 ns      1601811 ns          435\n// BM_SortPartiallySortedData<SortMethod::kPowersort>/524288         3426178 ns      3425631 ns          206\n// BM_SortPartiallySortedData<SortMethod::kPowersort>/1048576        7438610 ns      7438426 ns           91\n// BM_SortPartiallySortedData<SortMethod::kStdStableSort>/32            70.3 ns         70.3 ns     10067148\n// BM_SortPartiallySortedData<SortMethod::kStdStableSort>/64             131 ns          131 ns      5360170\n// BM_SortPartiallySortedData<SortMethod::kStdStableSort>/128            266 ns          266 ns      2622105\n// BM_SortPartiallySortedData<SortMethod::kStdStableSort>/256            631 ns          631 ns      1074449\n// BM_SortPartiallySortedData<SortMethod::kStdStableSort>/512           1243 ns         1243 ns       566539\n// BM_SortPartiallySortedData<SortMethod::kStdStableSort>/1024          2805 ns         2805 ns       249929\n// BM_SortPartiallySortedData<SortMethod::kStdStableSort>/2048          5767 ns         5767 ns       121282\n// BM_SortPartiallySortedData<SortMethod::kStdStableSort>/4096         12814 ns        12813 ns        55051\n// BM_SortPartiallySortedData<SortMethod::kStdStableSort>/8192         34371 ns        34370 ns        20305\n// BM_SortPartiallySortedData<SortMethod::kStdStableSort>/16384        95992 ns        95990 ns         7274\n// BM_SortPartiallySortedData<SortMethod::kStdStableSort>/32768       201706 ns       201701 ns         3472\n// BM_SortPartiallySortedData<SortMethod::kStdStableSort>/65536       453366 ns       453346 ns         1543\n// BM_SortPartiallySortedData<SortMethod::kStdStableSort>/131072      977696 ns       977652 ns          715\n// BM_SortPartiallySortedData<SortMethod::kStdStableSort>/262144     2291897 ns      2291858 ns          305\n// BM_SortPartiallySortedData<SortMethod::kStdStableSort>/524288     5089614 ns      5089385 ns          137\n// BM_SortPartiallySortedData<SortMethod::kStdStableSort>/1048576   11416225 ns     11415938 ns           60\n// clang-format on\n"
  },
  {
    "path": "tachyon/base/static_storage.h",
    "content": "#ifndef TACHYON_BASE_STATIC_STORAGE_H_\n#define TACHYON_BASE_STATIC_STORAGE_H_\n\n#include <type_traits>\n\n#define DEFINE_STATIC_STORAGE_TEMPLATE_METHOD(type, method_name)               \\\n  template <typename T = type,                                                 \\\n            std::enable_if_t<std::is_trivially_destructible_v<T>>* = nullptr>  \\\n  static T& method_name() {                                                    \\\n    static T storage;                                                          \\\n    return storage;                                                            \\\n  }                                                                            \\\n                                                                               \\\n  template <typename T = type,                                                 \\\n            std::enable_if_t<!std::is_trivially_destructible_v<T>>* = nullptr> \\\n  static T& method_name() {                                                    \\\n    static ::tachyon::base::NoDestructor<T> storage;                           \\\n    return *storage;                                                           \\\n  }\n\n#endif  // TACHYON_BASE_STATIC_STORAGE_H_\n"
  },
  {
    "path": "tachyon/base/strings/BUILD.bazel",
    "content": "load(\"//bazel:tachyon.bzl\", \"if_macos\")\nload(\n    \"//bazel:tachyon_cc.bzl\",\n    \"tachyon_cc_library\",\n    \"tachyon_cc_unittest\",\n    \"tachyon_objc_library\",\n)\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"rust_stringifier\",\n    hdrs = [\"rust_stringifier.h\"],\n    deps = [\n        \"//tachyon:export\",\n        \"@com_google_absl//absl/container:btree\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"string_number_conversions\",\n    srcs = [\n        \"string_number_conversions.cc\",\n        \"string_number_conversions_internal.h\",\n    ],\n    hdrs = [\"string_number_conversions.h\"],\n    deps = [\n        \"//tachyon/base:logging\",\n        \"@com_google_absl//absl/strings\",\n        \"@com_google_absl//absl/types:optional\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"string_util\",\n    srcs = [\n        \"string_util.cc\",\n        \"string_util_impl_helpers.h\",\n    ],\n    hdrs = [\n        \"string_util.h\",\n        \"string_util_internal.h\",\n    ],\n    deps = [\n        \"//tachyon/base:compiler_specific\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base:no_destructor\",\n        \"//tachyon/base/ranges:algorithm\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"sys_string_conversions_base\",\n    hdrs = [\"sys_string_conversions.h\"],\n    deps = [\n        \"//tachyon:export\",\n        \"//tachyon/build:build_config\",\n    ] + if_macos([\n        \"//tachyon/base/mac:scoped_cftyperef\",\n    ]),\n)\n\ntachyon_cc_library(\n    name = \"sys_string_conversions\",\n    srcs = select({\n        \"@platforms//os:macos\": [],\n        \"@platforms//os:windows\": [],\n        \"//conditions:default\": [\"sys_string_conversions_posix.cc\"],\n    }),\n    deps = [\n        \":sys_string_conversions_base\",\n        \"//tachyon/base:logging\",\n    ] + if_macos([\n        \":sys_string_conversions_mac\",\n    ]),\n)\n\ntachyon_objc_library(\n    name = \"sys_string_conversions_mac\",\n    srcs = [\"sys_string_conversions_mac.mm\"],\n    deps = [\n        \":sys_string_conversions_base\",\n        \"//tachyon/base/apple:bridging\",\n        \"//tachyon/base/numerics:safe_conversions\",\n    ],\n)\n\ntachyon_objc_library(\n    name = \"strings_mac_unittests\",\n    testonly = True,\n    srcs = [\"sys_string_conversions_mac_unittest.mm\"],\n    deps = [\n        \":sys_string_conversions_mac\",\n        \"@com_google_googletest//:gtest\",\n    ],\n    alwayslink = True,\n)\n\ntachyon_cc_unittest(\n    name = \"strings_unittests\",\n    srcs = [\n        \"string_number_conversions_unittest.cc\",\n        \"string_util_unittest.cc\",\n    ],\n    deps = [\n        \":string_number_conversions\",\n        \":string_util\",\n    ] + if_macos([\n        \":strings_mac_unittests\",\n    ]),\n)\n"
  },
  {
    "path": "tachyon/base/strings/rust_stringifier.h",
    "content": "#ifndef TACHYON_BASE_STRINGS_RUST_STRINGIFIER_H_\n#define TACHYON_BASE_STRINGS_RUST_STRINGIFIER_H_\n\n#include <optional>\n#include <sstream>\n#include <string>\n#include <type_traits>\n#include <vector>\n\n#include \"absl/container/btree_map.h\"\n\n#include \"tachyon/export.h\"\n\nnamespace tachyon::base {\n\nclass RustFormatter;\n\nnamespace internal {\n\ntemplate <typename T, typename SFINAE = void>\nclass RustDebugStringifier;\n\nclass TACHYON_EXPORT DebugStruct {\n public:\n  DebugStruct(RustFormatter& fmt, std::string_view name) : fmt_(fmt) {\n    ss_ << name << \" { \";\n  }\n\n  template <typename T>\n  DebugStruct& Field(std::string_view name, const T& value) {\n    if (has_field_) {\n      ss_ << \", \";\n    }\n    ss_ << name << \": \";\n    RustDebugStringifier<T>::AppendToStream(ss_, fmt_, value);\n    has_field_ = true;\n    return *this;\n  }\n\n  std::string Finish() {\n    ss_ << \" }\";\n    return ss_.str();\n  }\n\n private:\n  RustFormatter& fmt_;\n  std::stringstream ss_;\n  bool has_field_ = false;\n};\n\nclass TACHYON_EXPORT DebugTuple {\n public:\n  DebugTuple(RustFormatter& fmt, std::string_view name) : fmt_(fmt) {\n    ss_ << name << \"(\";\n  }\n\n  template <typename T>\n  DebugTuple& Field(const T& value) {\n    if (has_field_) {\n      ss_ << \", \";\n    }\n    RustDebugStringifier<T>::AppendToStream(ss_, fmt_, value);\n    has_field_ = true;\n    return *this;\n  }\n\n  std::string Finish() {\n    ss_ << \")\";\n    return ss_.str();\n  }\n\n private:\n  RustFormatter& fmt_;\n  std::stringstream ss_;\n  bool has_field_ = false;\n};\n\nclass TACHYON_EXPORT DebugList {\n public:\n  explicit DebugList(RustFormatter& fmt) : fmt_(fmt) { ss_ << \"[\"; }\n\n  template <typename T>\n  DebugList& Entry(const T& value) {\n    if (has_entry_) {\n      ss_ << \", \";\n    }\n    RustDebugStringifier<T>::AppendToStream(ss_, fmt_, value);\n    has_entry_ = true;\n    return *this;\n  }\n\n  std::string Finish() {\n    ss_ << \"]\";\n    return ss_.str();\n  }\n\n private:\n  RustFormatter& fmt_;\n  std::stringstream ss_;\n  bool has_entry_ = false;\n};\n\nclass TACHYON_EXPORT DebugMap {\n public:\n  explicit DebugMap(RustFormatter& fmt) : fmt_(fmt) { ss_ << \"{\"; }\n\n  template <typename T, typename U>\n  DebugMap& Pair(const T& key, const U& value) {\n    if (has_entry_) {\n      ss_ << \", \";\n    }\n    RustDebugStringifier<T>::AppendToStream(ss_, fmt_, key);\n    ss_ << \": \";\n    RustDebugStringifier<U>::AppendToStream(ss_, fmt_, value);\n    has_entry_ = true;\n    return *this;\n  }\n\n  std::string Finish() {\n    ss_ << \"}\";\n    return ss_.str();\n  }\n\n private:\n  RustFormatter& fmt_;\n  std::stringstream ss_;\n  bool has_entry_ = false;\n};\n\n}  // namespace internal\n\nclass TACHYON_EXPORT RustFormatter {\n public:\n  RustFormatter() = default;\n\n  internal::DebugStruct DebugStruct(std::string_view name) {\n    return internal::DebugStruct(*this, name);\n  }\n\n  internal::DebugTuple DebugTuple(std::string_view name) {\n    return internal::DebugTuple(*this, name);\n  }\n\n  internal::DebugList DebugList() { return internal::DebugList(*this); }\n\n  internal::DebugMap DebugMap() { return internal::DebugMap(*this); }\n};\n\nnamespace internal {\n\ntemplate <>\nclass RustDebugStringifier<bool> {\n public:\n  static std::ostream& AppendToStream(std::ostream& os, RustFormatter& fmt,\n                                      bool value) {\n    if (value) {\n      return os << \"true\";\n    } else {\n      return os << \"false\";\n    }\n  }\n};\n\ntemplate <typename T>\nclass RustDebugStringifier<T, std::enable_if_t<std::is_arithmetic_v<T>>> {\n public:\n  static std::ostream& AppendToStream(std::ostream& os, RustFormatter& fmt,\n                                      T value) {\n    return os << value;\n  }\n};\n\ntemplate <typename CharTy>\nclass RustDebugStringifier<std::basic_string_view<CharTy>> {\n public:\n  static std::ostream& AppendToStream(std::ostream& os, RustFormatter& fmt,\n                                      std::basic_string_view<CharTy> value) {\n    return os << \"\\\"\" << value << \"\\\"\";\n  }\n};\n\ntemplate <typename CharTy>\nclass RustDebugStringifier<std::basic_string<CharTy>> {\n public:\n  static std::ostream& AppendToStream(std::ostream& os, RustFormatter& fmt,\n                                      const std::basic_string<CharTy>& value) {\n    return os << \"\\\"\" << value << \"\\\"\";\n  }\n};\n\ntemplate <typename CharTy>\nclass RustDebugStringifier<\n    const CharTy*,\n    std::enable_if_t<\n        std::is_same_v<CharTy, char> || std::is_same_v<CharTy, wchar_t> ||\n        std::is_same_v<CharTy, char16_t> || std::is_same_v<CharTy, char32_t>>> {\n public:\n  static std::ostream& AppendToStream(std::ostream& os, RustFormatter& fmt,\n                                      const CharTy* value) {\n    return os << \"\\\"\" << value << \"\\\"\";\n  }\n};\n\ntemplate <typename T>\nclass RustDebugStringifier<std::optional<T>> {\n public:\n  static std::ostream& AppendToStream(std::ostream& os, RustFormatter& fmt,\n                                      const std::optional<T>& value) {\n    if (value.has_value()) {\n      os << \"Some(\";\n      RustDebugStringifier<T>::AppendToStream(os, fmt, value.value());\n      return os << \")\";\n    } else {\n      return os << \"None\";\n    }\n  }\n};\n\ntemplate <typename T>\nclass RustDebugStringifier<std::vector<T>> {\n public:\n  static std::ostream& AppendToStream(std::ostream& os, RustFormatter& fmt,\n                                      const std::vector<T>& values) {\n    DebugList debug_list = fmt.DebugList();\n    for (const T& value : values) {\n      debug_list.Entry(value);\n    }\n    return os << debug_list.Finish();\n  }\n};\n\ntemplate <typename K, typename V>\nclass RustDebugStringifier<absl::btree_map<K, V>> {\n public:\n  static std::ostream& AppendToStream(std::ostream& os, RustFormatter& fmt,\n                                      const absl::btree_map<K, V>& btree_map) {\n    DebugMap debug_map = fmt.DebugMap();\n    for (const auto& [key, value] : btree_map) {\n      debug_map.Pair(key, value);\n    }\n    return os << debug_map.Finish();\n  }\n};\n\n}  // namespace internal\n\ntemplate <typename T>\nstd::string ToRustDebugString(RustFormatter& fmt, const T& value) {\n  std::stringstream ss;\n  internal::RustDebugStringifier<T>::AppendToStream(ss, fmt, value);\n  return ss.str();\n}\n\ntemplate <typename T>\nstd::string ToRustDebugString(const T& value) {\n  RustFormatter fmt;\n  return ToRustDebugString(fmt, value);\n}\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_STRINGS_RUST_STRINGIFIER_H_\n"
  },
  {
    "path": "tachyon/base/strings/string_number_conversions.cc",
    "content": "#include \"tachyon/base/strings/string_number_conversions.h\"\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/strings/string_number_conversions_internal.h\"\n\nnamespace tachyon::base {\n\nbool StringToInt(std::string_view input, int* output) {\n  return absl::SimpleAtoi(input, output);\n}\n\nbool StringToUint(std::string_view input, unsigned* output) {\n  return absl::SimpleAtoi(input, output);\n}\n\nbool StringToInt64(std::string_view input, int64_t* output) {\n  return absl::SimpleAtoi(input, output);\n}\n\nbool StringToUint64(std::string_view input, uint64_t* output) {\n  return absl::SimpleAtoi(input, output);\n}\n\nbool StringToSizeT(std::string_view input, size_t* output) {\n  return absl::SimpleAtoi(input, output);\n}\n\nbool StringToFloat(std::string_view input, float* output) {\n  return absl::SimpleAtof(input, output);\n}\n\nbool StringToDouble(std::string_view input, double* output) {\n  return absl::SimpleAtod(input, output);\n}\n\nstd::string HexEncode(const void* bytes, size_t size, bool use_lower_case) {\n  static const char kUpperHexChars[] = \"0123456789ABCDEF\";\n  static const char kLowerHexChars[] = \"0123456789abcdef\";\n\n  const char* hex_chars = nullptr;\n  if (use_lower_case) {\n    hex_chars = kLowerHexChars;\n  } else {\n    hex_chars = kUpperHexChars;\n  }\n\n  // Each input byte creates two output hex characters.\n  std::string ret(size * 2, '\\0');\n\n  for (size_t i = 0; i < size; ++i) {\n    char b = reinterpret_cast<const char*>(bytes)[i];\n    ret[(i * 2)] = hex_chars[(b >> 4) & 0xf];\n    ret[(i * 2) + 1] = hex_chars[b & 0xf];\n  }\n  return ret;\n}\n\nstd::string HexEncode(absl::Span<const uint8_t> bytes, bool use_lower_case) {\n  return HexEncode(bytes.data(), bytes.size(), use_lower_case);\n}\n\nbool HexStringToInt(std::string_view input, int* output) {\n  return absl::SimpleHexAtoi(input, output);\n}\n\nbool HexStringToUint(std::string_view input, unsigned* output) {\n  return absl::SimpleHexAtoi(input, output);\n}\n\nbool HexStringToInt64(std::string_view input, int64_t* output) {\n  return absl::SimpleHexAtoi(input, output);\n}\n\nbool HexStringToUint64(std::string_view input, uint64_t* output) {\n  return absl::SimpleHexAtoi(input, output);\n}\n\nbool HexStringToBytes(std::string_view input, std::vector<uint8_t>* output) {\n  DCHECK(output->empty());\n  return internal::HexStringToByteContainer(input, std::back_inserter(*output));\n}\n\nbool HexStringToString(std::string_view input, std::string* output) {\n  DCHECK(output->empty());\n  return internal::HexStringToByteContainer(input, std::back_inserter(*output));\n}\n\nbool HexStringToSpan(std::string_view input, absl::Span<uint8_t> output) {\n  if (input.size() / 2 != output.size()) return false;\n\n  return internal::HexStringToByteContainer(input, output.begin());\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/strings/string_number_conversions.h",
    "content": "#ifndef TACHYON_BASE_STRINGS_STRING_NUMBER_CONVERSIONS_H_\n#define TACHYON_BASE_STRINGS_STRING_NUMBER_CONVERSIONS_H_\n\n#include <string>\n#include <string_view>\n#include <type_traits>\n#include <vector>\n\n#include \"absl/strings/numbers.h\"\n#include \"absl/strings/str_cat.h\"\n#include \"absl/types/span.h\"\n\n#include \"tachyon/export.h\"\n\nnamespace tachyon::base {\n\ntemplate <typename T, std::enable_if_t<std::is_arithmetic<T>::value>* = nullptr>\nstd::string NumberToString(T value) {\n  return absl::StrCat(value);\n}\n\nTACHYON_EXPORT bool StringToInt(std::string_view input, int* output);\nTACHYON_EXPORT bool StringToUint(std::string_view input, unsigned* output);\nTACHYON_EXPORT bool StringToInt64(std::string_view input, int64_t* output);\nTACHYON_EXPORT bool StringToUint64(std::string_view input, uint64_t* output);\nTACHYON_EXPORT bool StringToSizeT(std::string_view input, size_t* output);\nTACHYON_EXPORT bool StringToFloat(std::string_view input, float* output);\nTACHYON_EXPORT bool StringToDouble(std::string_view input, double* output);\n\ntemplate <typename T, std::enable_if_t<std::is_arithmetic<T>::value>* = nullptr>\nstd::string HexToString(T value) {\n  return absl::StrCat(absl::Hex(value));\n}\n\nTACHYON_EXPORT std::string HexEncode(const void* bytes, size_t size,\n                                     bool use_lower_case);\nTACHYON_EXPORT std::string HexEncode(absl::Span<const uint8_t> bytes,\n                                     bool use_lower_case);\n\nTACHYON_EXPORT bool HexStringToInt(std::string_view input, int* output);\nTACHYON_EXPORT bool HexStringToUint(std::string_view input, unsigned* output);\nTACHYON_EXPORT bool HexStringToInt64(std::string_view input, int64_t* output);\nTACHYON_EXPORT bool HexStringToUint64(std::string_view input, uint64_t* output);\n\n// Similar to the previous functions, except that output is a vector of bytes.\n// |*output| will contain as many bytes as were successfully parsed prior to the\n// error.  There is no overflow, but input.size() must be evenly divisible by 2.\n// Leading 0x or +/- are not allowed.\nTACHYON_EXPORT bool HexStringToBytes(std::string_view input,\n                                     std::vector<uint8_t>* output);\n\n// Same as HexStringToBytes, but for an std::string.\nTACHYON_EXPORT bool HexStringToString(std::string_view input,\n                                      std::string* output);\n\n// Decodes the hex string |input| into a presized |output|. The output buffer\n// must be sized exactly to |input.size() / 2| or decoding will fail and no\n// bytes will be written to |output|. Decoding an empty input is also\n// considered a failure. When decoding fails due to encountering invalid input\n// characters, |output| will have been filled with the decoded bytes up until\n// the failure.\nTACHYON_EXPORT bool HexStringToSpan(std::string_view input,\n                                    absl::Span<uint8_t> output);\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_STRINGS_STRING_NUMBER_CONVERSIONS_H_\n"
  },
  {
    "path": "tachyon/base/strings/string_number_conversions_internal.h",
    "content": "// Copyright 2020 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_STRINGS_STRING_NUMBER_CONVERSIONS_INTERNAL_H_\n#define TACHYON_BASE_STRINGS_STRING_NUMBER_CONVERSIONS_INTERNAL_H_\n\n#include <stdint.h>\n\n#include <algorithm>\n#include <limits>\n#include <optional>\n#include <string_view>\n\nnamespace tachyon::base {\nnamespace internal {\n\n// Utility to convert a character to a digit in a given base\ntemplate <int BASE, typename CHAR>\nstd::optional<uint8_t> CharToDigit(CHAR c) {\n  static_assert(1 <= BASE && BASE <= 36, \"BASE needs to be in [1, 36]\");\n  if (c >= '0' && c < '0' + std::min(BASE, 10)) return c - '0';\n\n  if (c >= 'a' && c < 'a' + BASE - 10) return c - 'a' + 10;\n\n  if (c >= 'A' && c < 'A' + BASE - 10) return c - 'A' + 10;\n\n  return std::nullopt;\n}\n\ntemplate <typename OutIter>\nstatic bool HexStringToByteContainer(std::string_view input, OutIter output) {\n  size_t count = input.size();\n  if (count == 0 || (count % 2) != 0) return false;\n  for (uintptr_t i = 0; i < count / 2; ++i) {\n    // most significant 4 bits\n    std::optional<uint8_t> msb = CharToDigit<16>(input[i * 2]);\n    // least significant 4 bits\n    std::optional<uint8_t> lsb = CharToDigit<16>(input[i * 2 + 1]);\n    if (!msb || !lsb) {\n      return false;\n    }\n    *(output++) = (*msb << 4) | *lsb;\n  }\n  return true;\n}\n\n}  // namespace internal\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_STRINGS_STRING_NUMBER_CONVERSIONS_INTERNAL_H_\n"
  },
  {
    "path": "tachyon/base/strings/string_number_conversions_unittest.cc",
    "content": "#include \"tachyon/base/strings/string_number_conversions.h\"\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n\nnamespace tachyon::base {\n\nTEST(StringNumberConversionTest, NumberToString) {\n  EXPECT_EQ(NumberToString(3), \"3\");\n  EXPECT_EQ(NumberToString(10), \"10\");\n}\n\n// We don't test another StringToXXX variants since they are just wrappers of\n// absl::SimpleAtoi, SimpleAtof and SimpleAtod.\nTEST(StringNumberConversionTest, StringToInt) {\n  int output;\n  EXPECT_TRUE(StringToInt(\"3\", &output));\n  EXPECT_EQ(output, 3);\n\n  EXPECT_FALSE(StringToInt(\"a\", &output));\n}\n\nTEST(StringNumberConversionTest, HexToString) {\n  EXPECT_EQ(HexToString(3), \"3\");\n  EXPECT_EQ(HexToString(10), \"a\");\n}\n\nTEST(StringNumberConversionTest, HexEncode) {\n  char data[] = {1, 10, 15, 2};\n\n  struct {\n    bool use_lower;\n    const char* answer;\n  } tests[] = {\n      {true, \"010a0f02\"},\n      {false, \"010A0F02\"},\n  };\n\n  for (const auto& test : tests) {\n    EXPECT_EQ(HexEncode(data, 4, test.use_lower), test.answer);\n    EXPECT_EQ(HexEncode(absl::MakeConstSpan(reinterpret_cast<uint8_t*>(data),\n                                            std::size(data)),\n                        test.use_lower),\n              test.answer);\n  }\n}\n\n// We don't test another HexStringToXXX variants since they are just wrappers of\n// absl::numbers_internal::safe_strtoi_base\nTEST(StringNumberConversionTest, HexStringToInt) {\n  int output;\n  EXPECT_TRUE(HexStringToInt(\"0x10\", &output));\n  EXPECT_EQ(output, 16);\n\n  int output2;\n  EXPECT_TRUE(HexStringToInt(\"10\", &output2));\n  EXPECT_EQ(output2, 16);\n}\n\nTEST(StringNumberConversionTest, HexStringToSomethingElse) {\n  struct {\n    const char* hex_input;\n    std::vector<uint8_t> vec_answer;\n    std::string str_answer;\n    bool ret;\n  } tests[] = {\n      {\"010a0f02\", {0x1, 0xa, 0xf, 0x2}, \"\\x1\\n\\xF\\x2\", true},\n      {\"010A0F02\", {0x1, 0xa, 0xf, 0x2}, \"\\x1\\n\\xF\\x2\", true},\n      {\"010x0f02\", {}, \"\", false},\n  };\n\n  for (const auto& test : tests) {\n    {\n      std::vector<uint8_t> output;\n      EXPECT_EQ(HexStringToBytes(test.hex_input, &output), test.ret);\n      if (test.ret) {\n        EXPECT_THAT(output, testing::ContainerEq(test.vec_answer));\n      }\n    }\n    {\n      std::string output;\n      EXPECT_EQ(HexStringToString(test.hex_input, &output), test.ret);\n      if (test.ret) {\n        EXPECT_EQ(output, test.str_answer);\n      }\n    }\n    {\n      std::vector<uint8_t> output;\n      output.resize(4);\n      EXPECT_EQ(HexStringToSpan(test.hex_input, absl::MakeSpan(output)),\n                test.ret);\n      if (test.ret) {\n        EXPECT_THAT(output, testing::ElementsAreArray(test.vec_answer.begin(),\n                                                      test.vec_answer.end()));\n      }\n    }\n  }\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/strings/string_util.cc",
    "content": "#include \"tachyon/base/strings/string_util.h\"\n\n#include <utility>\n\n#include \"absl/strings/ascii.h\"\n#include \"absl/strings/str_cat.h\"\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/no_destructor.h\"\n#include \"tachyon/base/strings/string_util_impl_helpers.h\"\n\nnamespace tachyon::base {\nnamespace {\n\nconstexpr const char* k0x = \"0x\";\n\n// Functor for case-insensitive ASCII comparisons for STL algorithms like\n// std::search.\nstruct CaseInsensitiveCompareASCII {\n public:\n  bool operator()(char x, char y) const {\n    return absl::ascii_tolower(x) == absl::ascii_tolower(y);\n  }\n};\n\ntemplate <typename T>\nstd::string DoMaybePrepend0x(T&& str) {\n  if (StartsWith(str, k0x)) {\n    return std::string(std::forward<T>(str));\n  }\n  return absl::StrCat(k0x, str);\n}\n\n}  // namespace\n\nstd::string ToLowerASCII(std::string_view str) {\n  return internal::ToLowerASCIIImpl(str);\n}\n\nstd::u16string ToLowerASCII(std::u16string_view str) {\n  return internal::ToLowerASCIIImpl(str);\n}\n\nstd::string ToUpperASCII(std::string_view str) {\n  return internal::ToUpperASCIIImpl(str);\n}\n\nstd::u16string ToUpperASCII(std::u16string_view str) {\n  return internal::ToUpperASCIIImpl(str);\n}\n\nstd::string CapitalizeASCII(std::string_view str) {\n  std::string ret = std::string(str);\n  if (!str.empty()) ret[0] = ToUpperASCII(str[0]);\n  return ret;\n}\n\nstd::u16string CapitalizeASCII(std::u16string_view str) {\n  std::u16string ret = std::u16string(str);\n  if (!str.empty()) ret[0] = ToUpperASCII(str[0]);\n  return ret;\n}\n\nconst std::string& EmptyString() {\n  static const NoDestructor<std::string> s;\n  return *s;\n}\n\nbool StartsWith(std::string_view str, std::string_view search_for,\n                CompareCase case_sensitivity) {\n  if (search_for.size() > str.size()) return false;\n\n  std::string_view source = str.substr(0, search_for.size());\n\n  switch (case_sensitivity) {\n    case CompareCase::SENSITIVE:\n      return source == search_for;\n\n    case CompareCase::INSENSITIVE_ASCII:\n      return std::equal(search_for.begin(), search_for.end(), source.begin(),\n                        CaseInsensitiveCompareASCII());\n\n    default:\n      NOTREACHED();\n      return false;\n  }\n}\n\nbool EndsWith(std::string_view str, std::string_view search_for,\n              CompareCase case_sensitivity) {\n  if (search_for.size() > str.size()) return false;\n\n  std::basic_string_view source =\n      str.substr(str.size() - search_for.size(), search_for.size());\n\n  switch (case_sensitivity) {\n    case CompareCase::SENSITIVE:\n      return source == search_for;\n\n    case CompareCase::INSENSITIVE_ASCII:\n      return std::equal(source.begin(), source.end(), search_for.begin(),\n                        CaseInsensitiveCompareASCII());\n\n    default:\n      NOTREACHED();\n      return false;\n  }\n}\n\nbool ConsumePrefix(std::string_view* str, std::string_view search_for,\n                   CompareCase case_sensitivity) {\n  if (StartsWith(*str, search_for, case_sensitivity)) {\n    str->remove_prefix(search_for.size());\n    return true;\n  }\n  return false;\n}\n\nbool ConsumeSuffix(std::string_view* str, std::string_view search_for,\n                   CompareCase case_sensitivity) {\n  if (EndsWith(*str, search_for, case_sensitivity)) {\n    str->remove_suffix(search_for.size());\n    return true;\n  }\n  return false;\n}\n\nbool ConsumePrefix0x(std::string_view* str) { return ConsumePrefix(str, k0x); }\n\nstd::string MaybePrepend0x(std::string_view str) {\n  return DoMaybePrepend0x(str);\n}\n\nstd::string MaybePrepend0x(const std::string& str) {\n  return DoMaybePrepend0x(str);\n}\n\nstd::string MaybePrepend0x(std::string&& str) {\n  return DoMaybePrepend0x(std::move(str));\n}\n\nstd::string ToHexStringWithLeadingZero(const std::string& str, size_t num) {\n  CHECK_GE(num, str.size());\n  std::string_view sv = str;\n  if (ConsumePrefix0x(&sv)) {\n    return absl::StrCat(k0x, std::string(num - sv.size(), '0'), sv);\n  } else {\n    return absl::StrCat(std::string(num - sv.size(), '0'), sv);\n  }\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/strings/string_util.h",
    "content": "#ifndef TACHYON_BASE_STRINGS_STRING_UTIL_H_\n#define TACHYON_BASE_STRINGS_STRING_UTIL_H_\n\n#include <optional>\n#include <sstream>\n#include <string>\n#include <string_view>\n#include <vector>\n\n#include \"tachyon/base/compiler_specific.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/strings/string_util_internal.h\"\n#include \"tachyon/export.h\"\n\nnamespace tachyon::base {\n\n// ASCII-specific tolower.  The standard library's tolower is locale sensitive,\n// so we don't want to use it here.\ntemplate <typename CharT,\n          typename = std::enable_if_t<std::is_integral<CharT>::value>>\nconstexpr CharT ToLowerASCII(CharT c) {\n  return internal::ToLowerASCII(c);\n}\n\n// ASCII-specific toupper.  The standard library's toupper is locale sensitive,\n// so we don't want to use it here.\ntemplate <typename CharT,\n          typename = std::enable_if_t<std::is_integral<CharT>::value>>\nCharT ToUpperASCII(CharT c) {\n  return (c >= 'a' && c <= 'z') ? static_cast<CharT>(c + 'A' - 'a') : c;\n}\n\n// Converts the given string to its ASCII-lowercase equivalent. Non-ASCII\n// bytes (or UTF-16 code units in `std::u16string_view`) are permitted but will\n// be unmodified.\nTACHYON_EXPORT std::string ToLowerASCII(std::string_view str);\nTACHYON_EXPORT std::u16string ToLowerASCII(std::u16string_view str);\n\n// Converts the given string to its ASCII-uppercase equivalent. Non-ASCII\n// bytes (or UTF-16 code units in `std::u16string_view`) are permitted but will\n// be unmodified.\nTACHYON_EXPORT std::string ToUpperASCII(std::string_view str);\nTACHYON_EXPORT std::u16string ToUpperASCII(std::u16string_view str);\n\n// Capitalize the given string. Non-ASCII bytes (or UTF-16 code units in\n// `std::u16string_view`) are permitted but will be unmodified.\nTACHYON_EXPORT std::string CapitalizeASCII(std::string_view str);\nTACHYON_EXPORT std::u16string CapitalizeASCII(std::u16string_view str);\n\n// Like strcasecmp for ASCII case-insensitive comparisons only. Returns:\n//   -1  (a < b)\n//    0  (a == b)\n//    1  (a > b)\n// (unlike strcasecmp which can return values greater or less than 1/-1). To\n// compare all Unicode code points case-insensitively, use base::i18n::ToLower\n// or base::i18n::FoldCase and then just call the normal string operators on the\n// result.\n//\n// Non-ASCII bytes (or UTF-16 code units in `std::u16string_view`) are permitted\n// but will be compared unmodified.\nTACHYON_EXPORT constexpr int CompareCaseInsensitiveASCII(std::string_view a,\n                                                         std::string_view b) {\n  return internal::CompareCaseInsensitiveASCIIT(a, b);\n}\n\n// Equality for ASCII case-insensitive comparisons. Non-ASCII bytes (or UTF-16\n// code units in `std::u16string_view`) are permitted but will be compared\n// unmodified.\ninline bool EqualsCaseInsensitiveASCII(std::string_view a, std::string_view b) {\n  return internal::EqualsCaseInsensitiveASCIIT(a, b);\n}\n\n// These threadsafe functions return references to globally unique empty\n// strings.\n//\n// It is likely faster to construct a new empty string object (just a few\n// instructions to set the length to 0) than to get the empty string instance\n// returned by these functions (which requires threadsafe static access).\n//\n// Therefore, DO NOT USE THESE AS A GENERAL-PURPOSE SUBSTITUTE FOR DEFAULT\n// CONSTRUCTORS. There is only one case where you should use these: functions\n// which need to return a string by reference (e.g. as a class member\n// accessor), and don't have an empty string to use (e.g. in an error case).\n// These should not be used as initializers, function arguments, or return\n// values for functions which return by value or outparam.\nTACHYON_EXPORT const std::string& EmptyString();\n\n// Indicates case sensitivity of comparisons. Only ASCII case insensitivity\n// is supported. Full Unicode case-insensitive conversions would need ICU\n// and it's not supported yet.\nenum class CompareCase {\n  SENSITIVE,\n  INSENSITIVE_ASCII,\n};\n\nTACHYON_EXPORT bool StartsWith(\n    std::string_view str, std::string_view search_for,\n    CompareCase case_sensitivity = CompareCase::SENSITIVE);\n\nTACHYON_EXPORT bool EndsWith(\n    std::string_view str, std::string_view search_for,\n    CompareCase case_sensitivity = CompareCase::SENSITIVE);\n\nTACHYON_EXPORT bool ConsumePrefix(\n    std::string_view* str, std::string_view search_for,\n    CompareCase case_sensitivity = CompareCase::SENSITIVE);\n\nTACHYON_EXPORT bool ConsumeSuffix(\n    std::string_view* str, std::string_view search_for,\n    CompareCase case_sensitivity = CompareCase::SENSITIVE);\n\nTACHYON_EXPORT bool ConsumePrefix0x(std::string_view* str);\nTACHYON_EXPORT std::string MaybePrepend0x(std::string_view str);\nTACHYON_EXPORT std::string MaybePrepend0x(std::string&& str);\nTACHYON_EXPORT std::string MaybePrepend0x(const std::string& str);\n\n// Return a string that left padded with zero so that size of a hex number\n// is a given |num|. For example,\n//\n//   - |str| = \"0x1234\" and |num| = 6 => \"0x001234\"\n//   - |str| = \"1234\" and |num| = 6 => \"001234\"\nTACHYON_EXPORT std::string ToHexStringWithLeadingZero(const std::string& str,\n                                                      size_t num);\n\nALWAYS_INLINE const char* BoolToString(bool b) { return b ? \"true\" : \"false\"; }\n\ntemplate <typename T>\nstd::string OptionalToString(const std::optional<T>& value) {\n  if (!value.has_value()) return \"None\";\n  std::stringstream ss;\n  ss << \"Some(\";\n  // NOTE(chokobole): This is a trick to call |operator<<()| or |ToString()|.\n  google::MakeCheckOpValueString(&ss, value.value());\n  ss << \")\";\n  return ss.str();\n}\n\ntemplate <typename Container>\nstd::string ContainerToString(const Container& data) {\n  size_t size = std::size(data);\n\n  if (size == 0) return \"[]\";\n\n  std::stringstream ss;\n  ss << \"[\";\n  for (size_t i = 0; i < size - 1; ++i) {\n    // NOTE(chokobole): This is a trick to call |operator<<()| or |ToString()|.\n    google::MakeCheckOpValueString(&ss, data[i]);\n    ss << \", \";\n  }\n  google::MakeCheckOpValueString(&ss, data[size - 1]);\n  ss << \"]\";\n  return ss.str();\n}\n\ntemplate <typename Container>\nstd::string Container2DToString(const Container& data) {\n  size_t size = std::size(data);\n\n  if (size == 0) return \"[]\";\n\n  std::stringstream ss;\n  ss << \"[\";\n  for (size_t i = 0; i < size - 1; ++i) {\n    ss << ContainerToString(data[i]) << \", \";\n  }\n  ss << ContainerToString(data[size - 1]) << \"]\";\n  return ss.str();\n}\n\ntemplate <typename Container>\nstd::string Container3DToString(const Container& data) {\n  size_t size = std::size(data);\n\n  if (size == 0) return \"[]\";\n\n  std::stringstream ss;\n  ss << \"[\";\n  for (size_t i = 0; i < size - 1; ++i) {\n    ss << Container2DToString(data[i]) << \", \";\n  }\n  ss << Container2DToString(data[size - 1]) << \"]\";\n  return ss.str();\n}\n\ntemplate <typename Container>\nstd::string Container4DToString(const Container& data) {\n  size_t size = std::size(data);\n\n  if (size == 0) return \"[]\";\n\n  std::stringstream ss;\n  ss << \"[\";\n  for (size_t i = 0; i < size - 1; ++i) {\n    ss << Container3DToString(data[i]) << \", \";\n  }\n  ss << Container3DToString(data[size - 1]) << \"]\";\n  return ss.str();\n}\n\ntemplate <typename Container>\nstd::string ContainerToHexString(const Container& data, bool pad_zero = false) {\n  size_t size = std::size(data);\n\n  if (size == 0) return \"[]\";\n\n  std::stringstream ss;\n  ss << \"[\";\n  for (size_t i = 0; i < size - 1; ++i) {\n    ss << data[i].ToHexString(pad_zero) << \", \";\n  }\n  ss << data[size - 1].ToHexString(pad_zero) << \"]\";\n  return ss.str();\n}\n\ntemplate <typename Container>\nstd::string Container2DToHexString(const Container& data,\n                                   bool pad_zero = false) {\n  size_t size = std::size(data);\n\n  if (size == 0) return \"[]\";\n\n  std::stringstream ss;\n  ss << \"[\";\n  for (size_t i = 0; i < size - 1; ++i) {\n    ss << ContainerToHexString(data[i], pad_zero) << \", \";\n  }\n  ss << ContainerToHexString(data[size - 1], pad_zero) << \"]\";\n  return ss.str();\n}\n\ntemplate <typename Container>\nstd::string Container3DToHexString(const Container& data,\n                                   bool pad_zero = false) {\n  size_t size = std::size(data);\n\n  if (size == 0) return \"[]\";\n\n  std::stringstream ss;\n  ss << \"[\";\n  for (size_t i = 0; i < size - 1; ++i) {\n    ss << Container2DToHexString(data[i], pad_zero) << \", \";\n  }\n  ss << Container2DToHexString(data[size - 1], pad_zero) << \"]\";\n  return ss.str();\n}\n\ntemplate <typename Container>\nstd::string Container4DToHexString(const Container& data,\n                                   bool pad_zero = false) {\n  size_t size = std::size(data);\n\n  if (size == 0) return \"[]\";\n\n  std::stringstream ss;\n  ss << \"[\";\n  for (size_t i = 0; i < size - 1; ++i) {\n    ss << Container3DToHexString(data[i], pad_zero) << \", \";\n  }\n  ss << Container3DToHexString(data[size - 1], pad_zero) << \"]\";\n  return ss.str();\n}\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_STRINGS_STRING_UTIL_H_\n"
  },
  {
    "path": "tachyon/base/strings/string_util_impl_helpers.h",
    "content": "// Copyright 2020 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n// clang-format off\n\n#ifndef TACHYON_BASE_STRINGS_STRING_UTIL_IMPL_HELPERS_H_\n#define TACHYON_BASE_STRINGS_STRING_UTIL_IMPL_HELPERS_H_\n\n#include <string>\n\nnamespace tachyon::base::internal {\n\ntemplate <typename T, typename CharT = typename T::value_type>\nstd::basic_string<CharT> ToLowerASCIIImpl(T str) {\n  std::basic_string<CharT> ret;\n  ret.reserve(str.size());\n  for (size_t i = 0; i < str.size(); i++)\n    ret.push_back(ToLowerASCII(str[i]));\n  return ret;\n}\n\ntemplate <typename T, typename CharT = typename T::value_type>\nstd::basic_string<CharT> ToUpperASCIIImpl(T str) {\n  std::basic_string<CharT> ret;\n  ret.reserve(str.size());\n  for (size_t i = 0; i < str.size(); i++)\n    ret.push_back(ToUpperASCII(str[i]));\n  return ret;\n}\n\n}  // namespace tachyon::base::internal\n\n#endif  // TACHYON_BASE_STRINGS_STRING_UTIL_IMPL_HELPERS_H_\n\n// clang-format on\n"
  },
  {
    "path": "tachyon/base/strings/string_util_internal.h",
    "content": "// Copyright 2020 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n// clang-format off\n\n#ifndef TACHYON_BASE_STRINGS_STRING_UTIL_INTERNAL_H_\n#define TACHYON_BASE_STRINGS_STRING_UTIL_INTERNAL_H_\n\n#include <type_traits>\n#include <string_view>\n\n#include \"tachyon/base/ranges/algorithm.h\"\n\nnamespace tachyon::base::internal {\n\n// ASCII-specific tolower.  The standard library's tolower is locale sensitive,\n// so we don't want to use it here.\ntemplate <typename CharT,\n          typename = std::enable_if_t<std::is_integral<CharT>::value>>\nconstexpr CharT ToLowerASCII(CharT c) {\n  return (c >= 'A' && c <= 'Z') ? (c + ('a' - 'A')) : c;\n}\n\ntemplate <typename T>\nconstexpr int CompareCaseInsensitiveASCIIT(T a, T b) {\n  // Find the first characters that aren't equal and compare them.  If the end\n  // of one of the strings is found before a nonequal character, the lengths\n  // of the strings are compared. Compare using the unsigned type so the sort\n  // order is independent of the signedness of `char`.\n  static_assert(std::is_integral_v<typename T::value_type>);\n  using UCharT = std::make_unsigned_t<typename T::value_type>;\n  size_t i = 0;\n  while (i < a.length() && i < b.length()) {\n    UCharT lower_a = static_cast<UCharT>(ToLowerASCII(a[i]));\n    UCharT lower_b = static_cast<UCharT>(ToLowerASCII(b[i]));\n    if (lower_a < lower_b)\n      return -1;\n    if (lower_a > lower_b)\n      return 1;\n    i++;\n  }\n\n  // End of one string hit before finding a different character. Expect the\n  // common case to be \"strings equal\" at this point so check that first.\n  if (a.length() == b.length())\n    return 0;\n\n  if (a.length() < b.length())\n    return -1;\n  return 1;\n}\n\ntemplate <typename CharT, typename CharU>\ninline bool EqualsCaseInsensitiveASCIIT(std::basic_string_view<CharT> a,\n                                        std::basic_string_view<CharU> b) {\n  return ranges::equal(a, b, [](auto lhs, auto rhs) {\n    return ToLowerASCII(lhs) == ToLowerASCII(rhs);\n  });\n}\n\n}  // namespace tachyon::base::internal\n\n#endif  // TACHYON_BASE_STRINGS_STRING_UTIL_INTERNAL_H_\n\n// clang-format on\n"
  },
  {
    "path": "tachyon/base/strings/string_util_unittest.cc",
    "content": "#include \"tachyon/base/strings/string_util.h\"\n\n#include \"gtest/gtest.h\"\n\nnamespace tachyon::base {\n\nTEST(StringUtilTest, EmptyString) { EXPECT_EQ(EmptyString(), \"\"); }\n\nTEST(StringUtilTest, StartsWith) {\n  std::string_view sv = \"Hello World\";\n  EXPECT_FALSE(StartsWith(sv, \"World\"));\n  EXPECT_TRUE(StartsWith(sv, \"Hello\"));\n  EXPECT_FALSE(StartsWith(sv, \"Hello World!\"));\n  EXPECT_TRUE(StartsWith(sv, \"hello\", CompareCase::INSENSITIVE_ASCII));\n}\n\nTEST(StringUtilTest, EndsWith) {\n  std::string_view sv = \"Hello World\";\n  EXPECT_FALSE(EndsWith(sv, \"Hello\"));\n  EXPECT_TRUE(EndsWith(sv, \"World\"));\n  EXPECT_FALSE(EndsWith(sv, \"!Hello World\"));\n  EXPECT_TRUE(EndsWith(sv, \"world\", CompareCase::INSENSITIVE_ASCII));\n}\n\nTEST(StringUtilTest, ConsumePrefix) {\n  std::string_view sv = \"Hello World\";\n  EXPECT_FALSE(ConsumePrefix(&sv, \"World\"));\n  EXPECT_TRUE(ConsumePrefix(&sv, \"Hello\"));\n  EXPECT_EQ(sv, \" World\");\n}\n\nTEST(StringUtilTest, ConsumeSuffix) {\n  std::string_view sv = \"Hello World\";\n  EXPECT_FALSE(ConsumeSuffix(&sv, \"Hello\"));\n  EXPECT_TRUE(ConsumeSuffix(&sv, \"World\"));\n  EXPECT_EQ(sv, \"Hello \");\n}\n\nTEST(StringUtilTest, ConsumePrefix0x) {\n  std::string_view sv = \"0x1234\";\n  EXPECT_TRUE(ConsumePrefix0x(&sv));\n  EXPECT_EQ(sv, \"1234\");\n  sv = \"3456\";\n  EXPECT_FALSE(ConsumePrefix0x(&sv));\n  EXPECT_EQ(sv, \"3456\");\n}\n\nTEST(StringUtilTest, MaybePrepend0x) {\n  std::string_view sv = \"0x1234\";\n  EXPECT_EQ(MaybePrepend0x(sv), \"0x1234\");\n  sv = \"3456\";\n  EXPECT_EQ(MaybePrepend0x(sv), \"0x3456\");\n}\n\nTEST(StringUtilTest, ToHexStringWithLeadingZero) {\n  std::string str = \"0x1234\";\n  EXPECT_EQ(ToHexStringWithLeadingZero(str, 6), \"0x001234\");\n  str = \"3456\";\n  EXPECT_EQ(ToHexStringWithLeadingZero(str, 6), \"003456\");\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/strings/sys_string_conversions.h",
    "content": "// Copyright 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n// clang-format off\n\n#ifndef TACHYON_BASE_STRINGS_SYS_STRING_CONVERSIONS_H_\n#define TACHYON_BASE_STRINGS_SYS_STRING_CONVERSIONS_H_\n\n// Provides system-dependent string type conversions for cases where it's\n// necessary to not use ICU. Generally, you should not need this in Chrome,\n// but it is used in some shared code. Dependencies should be minimal.\n\n#include <stdint.h>\n\n#include <string>\n#include <string_view>\n\n#include \"tachyon/export.h\"\n#include \"tachyon/build/build_config.h\"\n\n#if BUILDFLAG(IS_APPLE)\n#include <CoreFoundation/CoreFoundation.h>\n\n#include \"tachyon/base/mac/scoped_cftyperef.h\"\n\n#ifdef __OBJC__\n@class NSString;\n#endif\n#endif  // BUILDFLAG(IS_APPLE)\n\nnamespace tachyon::base {\n\n// Converts between wide and UTF-8 representations of a string. On error, the\n// result is system-dependent.\n[[nodiscard]] TACHYON_EXPORT std::string SysWideToUTF8(const std::wstring& wide);\n[[nodiscard]] TACHYON_EXPORT std::wstring SysUTF8ToWide(std::string_view utf8);\n\n// Converts between wide and the system multi-byte representations of a string.\n// DANGER: This will lose information and can change (on Windows, this can\n// change between reboots).\n[[nodiscard]] TACHYON_EXPORT std::string SysWideToNativeMB(\n    const std::wstring& wide);\n[[nodiscard]] TACHYON_EXPORT std::wstring SysNativeMBToWide(std::string_view native_mb);\n\n// Windows-specific ------------------------------------------------------------\n\n#if BUILDFLAG(IS_WIN)\n\n// Converts between 8-bit and wide strings, using the given code page. The\n// code page identifier is one accepted by the Windows function\n// MultiByteToWideChar().\n[[nodiscard]] TACHYON_EXPORT std::wstring SysMultiByteToWide(std::string_view mb,\n                                                             uint32_t code_page);\n[[nodiscard]] TACHYON_EXPORT std::string SysWideToMultiByte(\n    const std::wstring& wide,\n    uint32_t code_page);\n\n#endif  // BUILDFLAG(IS_WIN)\n\n// Mac-specific ----------------------------------------------------------------\n\n#if BUILDFLAG(IS_APPLE)\n\n// Converts between strings and CFStringRefs/NSStrings.\n\n// Converts a string to a CFStringRef. Returns null on failure.\n[[nodiscard]] TACHYON_EXPORT ScopedCFTypeRef<CFStringRef> SysUTF8ToCFStringRef(\n    std::string_view utf8);\n[[nodiscard]] TACHYON_EXPORT ScopedCFTypeRef<CFStringRef> SysUTF16ToCFStringRef(\n    std::u16string_view utf16);\n\n// Converts a CFStringRef to a string. Returns an empty string on failure. It is\n// not valid to call these with a null `ref`.\n[[nodiscard]] TACHYON_EXPORT std::string SysCFStringRefToUTF8(CFStringRef ref);\n[[nodiscard]] TACHYON_EXPORT std::u16string SysCFStringRefToUTF16(CFStringRef ref);\n\n#ifdef __OBJC__\n\n// Converts a string to an autoreleased NSString. Returns nil on failure.\n[[nodiscard]] TACHYON_EXPORT NSString* SysUTF8ToNSString(std::string_view utf8);\n[[nodiscard]] TACHYON_EXPORT NSString* SysUTF16ToNSString(std::u16string_view utf16);\n\n// Converts an NSString to a string. Returns an empty string on failure or if\n// `ref` is nil.\n[[nodiscard]] TACHYON_EXPORT std::string SysNSStringToUTF8(NSString* ref);\n[[nodiscard]] TACHYON_EXPORT std::u16string SysNSStringToUTF16(NSString* ref);\n\n#endif  // __OBJC__\n\n#endif  // BUILDFLAG(IS_APPLE)\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_STRINGS_SYS_STRING_CONVERSIONS_H_\n"
  },
  {
    "path": "tachyon/base/strings/sys_string_conversions_mac.mm",
    "content": "// Copyright 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n// clang-format off\n\n#include \"tachyon/base/strings/sys_string_conversions.h\"\n\n#import <Foundation/Foundation.h>\n#include <stddef.h>\n\n#include <string_view>\n#include <vector>\n\n#include \"tachyon/base/apple/bridging.h\"\n#include \"tachyon/base/mac/scoped_cftyperef.h\"\n#include \"tachyon/base/numerics/safe_conversions.h\"\n\n#if !defined(__has_feature) || !__has_feature(objc_arc)\n#error \"This file requires ARC support.\"\n#endif\n\nnamespace tachyon::base {\n\nnamespace {\n\n// Converts the supplied CFString into the specified encoding, and returns it as\n// a C++ library string of the template type. Returns an empty string on\n// failure.\n//\n// Do not assert in this function since it is used by the assertion code!\ntemplate <typename StringType>\nStringType CFStringToStringWithEncodingT(CFStringRef cfstring,\n                                         CFStringEncoding encoding) {\n  CFIndex length = CFStringGetLength(cfstring);\n  if (length == 0)\n    return StringType();\n\n  CFRange whole_string = CFRangeMake(0, length);\n  CFIndex out_size;\n  CFIndex converted = CFStringGetBytes(cfstring, whole_string, encoding,\n                                       /*lossByte=*/0,\n                                       /*isExternalRepresentation=*/false,\n                                       /*buffer=*/nullptr,\n                                       /*maxBufLen=*/0, &out_size);\n  if (converted == 0 || out_size <= 0)\n    return StringType();\n\n  // `out_size` is the number of UInt8-sized units needed in the destination.\n  // A buffer allocated as UInt8 units might not be properly aligned to\n  // contain elements of StringType::value_type.  Use a container for the\n  // proper value_type, and convert `out_size` by figuring the number of\n  // value_type elements per UInt8.  Leave room for a NUL terminator.\n  size_t elements = static_cast<size_t>(out_size) * sizeof(UInt8) /\n                        sizeof(typename StringType::value_type) +\n                    1;\n\n  std::vector<typename StringType::value_type> out_buffer(elements);\n  converted =\n      CFStringGetBytes(cfstring, whole_string, encoding,\n                       /*lossByte=*/0,\n                       /*isExternalRepresentation=*/false,\n                       reinterpret_cast<UInt8*>(&out_buffer[0]), out_size,\n                       /*usedBufLen=*/nullptr);\n  if (converted == 0)\n    return StringType();\n\n  out_buffer[elements - 1] = '\\0';\n  return StringType(&out_buffer[0], elements - 1);\n}\n\n// Given a C++ library string `in` with an encoding specified by `in_encoding`,\n// converts it to `out_encoding` and returns it as a C++ library string of the\n// `OutStringType` template type. Returns an empty string on failure.\n//\n// Do not assert in this function since it is used by the assertion code!\ntemplate <typename InStringType, typename OutStringType>\nOutStringType StringToStringWithEncodingsT(const InStringType& in,\n                                           CFStringEncoding in_encoding,\n                                           CFStringEncoding out_encoding) {\n  typename InStringType::size_type in_length = in.length();\n  if (in_length == 0)\n    return OutStringType();\n\n  base::ScopedCFTypeRef<CFStringRef> cfstring(CFStringCreateWithBytesNoCopy(\n      kCFAllocatorDefault, reinterpret_cast<const UInt8*>(in.data()),\n      checked_cast<CFIndex>(in_length *\n                            sizeof(typename InStringType::value_type)),\n      in_encoding,\n      /*isExternalRepresentation=*/false, kCFAllocatorNull));\n  if (!cfstring)\n    return OutStringType();\n\n  return CFStringToStringWithEncodingT<OutStringType>(cfstring, out_encoding);\n}\n\n// Given a std::string_view `in` with an encoding specified by `in_encoding`, returns\n// it as a CFStringRef. Returns null on failure.\ntemplate <typename CharT>\nScopedCFTypeRef<CFStringRef> StringPieceToCFStringWithEncodingsT(\n    std::basic_string_view<CharT> in,\n    CFStringEncoding in_encoding) {\n  const auto in_length = in.length();\n  if (in_length == 0)\n    return ScopedCFTypeRef<CFStringRef>(CFSTR(\"\"), base::scoped_policy::RETAIN);\n\n  return ScopedCFTypeRef<CFStringRef>(CFStringCreateWithBytes(\n      kCFAllocatorDefault, reinterpret_cast<const UInt8*>(in.data()),\n      checked_cast<CFIndex>(in_length * sizeof(CharT)), in_encoding, false));\n}\n\n}  // namespace\n\n// The CFStringEncodings used below specify the byte ordering explicitly,\n// otherwise CFString will be confused when strings don't carry BOMs, as they\n// typically won't.\n\n// Do not assert in this function since it is used by the assertion code!\nstd::string SysWideToUTF8(const std::wstring& wide) {\n  return StringToStringWithEncodingsT<std::wstring, std::string>(\n      wide, kCFStringEncodingUTF32LE, kCFStringEncodingUTF8);\n}\n\n// Do not assert in this function since it is used by the assertion code!\nstd::wstring SysUTF8ToWide(std::string_view utf8) {\n  return StringToStringWithEncodingsT<std::string_view, std::wstring>(\n      utf8, kCFStringEncodingUTF8, kCFStringEncodingUTF32LE);\n}\n\nstd::string SysWideToNativeMB(const std::wstring& wide) {\n  return SysWideToUTF8(wide);\n}\n\nstd::wstring SysNativeMBToWide(std::string_view native_mb) {\n  return SysUTF8ToWide(native_mb);\n}\n\nScopedCFTypeRef<CFStringRef> SysUTF8ToCFStringRef(std::string_view utf8) {\n  return StringPieceToCFStringWithEncodingsT(utf8, kCFStringEncodingUTF8);\n}\n\nScopedCFTypeRef<CFStringRef> SysUTF16ToCFStringRef(std::u16string_view utf16) {\n  return StringPieceToCFStringWithEncodingsT(utf16, kCFStringEncodingUTF16LE);\n}\n\nNSString* SysUTF8ToNSString(std::string_view utf8) {\n  return base::apple::CFToNSOwnershipCast(SysUTF8ToCFStringRef(utf8).release());\n}\n\nNSString* SysUTF16ToNSString(std::u16string_view utf16) {\n  return base::apple::CFToNSOwnershipCast(\n      SysUTF16ToCFStringRef(utf16).release());\n}\n\nstd::string SysCFStringRefToUTF8(CFStringRef ref) {\n  return CFStringToStringWithEncodingT<std::string>(ref, kCFStringEncodingUTF8);\n}\n\nstd::u16string SysCFStringRefToUTF16(CFStringRef ref) {\n  return CFStringToStringWithEncodingT<std::u16string>(\n      ref, kCFStringEncodingUTF16LE);\n}\n\nstd::string SysNSStringToUTF8(NSString* nsstring) {\n  if (!nsstring)\n    return std::string();\n  return SysCFStringRefToUTF8(apple::NSToCFPtrCast(nsstring));\n}\n\nstd::u16string SysNSStringToUTF16(NSString* nsstring) {\n  if (!nsstring)\n    return std::u16string();\n  return SysCFStringRefToUTF16(apple::NSToCFPtrCast(nsstring));\n}\n\n}  // namespace tachyon::base\n\n// clang-format on\n"
  },
  {
    "path": "tachyon/base/strings/sys_string_conversions_mac_unittest.mm",
    "content": "// Copyright 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n// clang-format off\n\n#import <Foundation/Foundation.h>\n\n#include <string>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/strings/sys_string_conversions.h\"\n// #include \"tachyon/base/strings/utf_string_conversions.h\"\n\n#if !defined(__has_feature) || !__has_feature(objc_arc)\n#error \"This file requires ARC support.\"\n#endif\n\nnamespace tachyon::base {\n\nTEST(SysStrings, ConversionsFromNSString) {\n  EXPECT_STREQ(\"Hello, world!\", SysNSStringToUTF8(@\"Hello, world!\").c_str());\n\n  // Conversions should be able to handle a NULL value without crashing.\n  EXPECT_STREQ(\"\", SysNSStringToUTF8(nil).c_str());\n  EXPECT_EQ(std::u16string(), SysNSStringToUTF16(nil));\n}\n\nstd::vector<std::string> GetRoundTripStrings() {\n  return {\n      \"Hello, World!\",  // ASCII / ISO8859 string (also valid UTF-8)\n      \"a\\0b\",           // UTF-8 with embedded NUL byte\n      \"λf\",             // lowercase lambda + 'f'\n      \"χρώμιο\",         // \"chromium\" in greek\n      \"כרום\",           // \"chromium\" in hebrew\n      \"クロム\",         // \"chromium\" in japanese\n\n      // Tarot card symbol \"the morning\", which does not fit in one UTF-16\n      // character.\n      \"🃦\",\n  };\n}\n\nTEST(SysStrings, RoundTripsFromUTF8) {\n  for (const auto& string8 : GetRoundTripStrings()) {\n    NSString* nsstring8 = SysUTF8ToNSString(string8);\n    std::string back8 = SysNSStringToUTF8(nsstring8);\n    EXPECT_EQ(string8, back8);\n  }\n}\n\n/*\nTODO(chokobole):\nTEST(SysStrings, RoundTripsFromUTF16) {\n  for (const auto& string8 : GetRoundTripStrings()) {\n    std::u16string string16 = base::UTF8ToUTF16(string8);\n    NSString* nsstring16 = SysUTF16ToNSString(string16);\n    std::u16string back16 = SysNSStringToUTF16(nsstring16);\n    EXPECT_EQ(string16, back16);\n  }\n}\n*/\n\n}  // namespace tachyon::base\n\n// clang-format on"
  },
  {
    "path": "tachyon/base/strings/sys_string_conversions_posix.cc",
    "content": "// Copyright 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n// clang-format off\n\n#include \"tachyon/base/strings/sys_string_conversions.h\"\n\n#include <stddef.h>\n#include <string.h>\n#include <wchar.h>\n\n#include <string_view>\n\n// #include \"tachyon/base/strings/utf_string_conversions.h\"\n#include \"tachyon/build/build_config.h\"\n#include \"tachyon/base/logging.h\"\n\nnamespace tachyon::base {\n\nstd::string SysWideToUTF8(const std::wstring& wide) {\n  // In theory this should be using the system-provided conversion rather\n  // than our ICU, but this will do for now.\n  // TODO(chokobole):\n  // return WideToUTF8(wide);\n  NOTIMPLEMENTED();\n  return std::string();\n}\nstd::wstring SysUTF8ToWide(std::string_view utf8) {\n  // In theory this should be using the system-provided conversion rather\n  // than our ICU, but this will do for now.\n  // TODO(chokobole):\n  // std::wstring out;\n  // UTF8ToWide(utf8.data(), utf8.size(), &out);\n  // return out;\n  NOTIMPLEMENTED();\n  return std::wstring();\n}\n\n#if defined(SYSTEM_NATIVE_UTF8) || BUILDFLAG(IS_ANDROID)\n// TODO(port): Consider reverting the OS_ANDROID when we have wcrtomb()\n// support and a better understanding of what calls these routines.\n\nstd::string SysWideToNativeMB(const std::wstring& wide) {\n  return WideToUTF8(wide);\n}\n\nstd::wstring SysNativeMBToWide(std::string_view native_mb) {\n  return SysUTF8ToWide(native_mb);\n}\n\n#else\n\nstd::string SysWideToNativeMB(const std::wstring& wide) {\n  mbstate_t ps;\n\n  // Calculate the number of multi-byte characters.  We walk through the string\n  // without writing the output, counting the number of multi-byte characters.\n  size_t num_out_chars = 0;\n  memset(&ps, 0, sizeof(ps));\n  for (auto src : wide) {\n    // Use a temp buffer since calling wcrtomb with an output of NULL does not\n    // calculate the output length.\n    char buf[16];\n    // Skip NULLs to avoid wcrtomb's special handling of them.\n    size_t res = src ? wcrtomb(buf, src, &ps) : 0;\n    switch (res) {\n      // Handle any errors and return an empty string.\n      case static_cast<size_t>(-1):\n        return std::string();\n      case 0:\n        // We hit an embedded null byte, keep going.\n        ++num_out_chars;\n        break;\n      default:\n        num_out_chars += res;\n        break;\n    }\n  }\n\n  if (num_out_chars == 0)\n    return std::string();\n\n  std::string out;\n  out.resize(num_out_chars);\n\n  // We walk the input string again, with |i| tracking the index of the\n  // wide input, and |j| tracking the multi-byte output.\n  memset(&ps, 0, sizeof(ps));\n  for (size_t i = 0, j = 0; i < wide.size(); ++i) {\n    const wchar_t src = wide[i];\n    // We don't want wcrtomb to do its funkiness for embedded NULLs.\n    size_t res = src ? wcrtomb(&out[j], src, &ps) : 0;\n    switch (res) {\n      // Handle any errors and return an empty string.\n      case static_cast<size_t>(-1):\n        return std::string();\n      case 0:\n        // We hit an embedded null byte, keep going.\n        ++j;  // Output is already zeroed.\n        break;\n      default:\n        j += res;\n        break;\n    }\n  }\n\n  return out;\n}\n\nstd::wstring SysNativeMBToWide(std::string_view native_mb) {\n  mbstate_t ps;\n\n  // Calculate the number of wide characters.  We walk through the string\n  // without writing the output, counting the number of wide characters.\n  size_t num_out_chars = 0;\n  memset(&ps, 0, sizeof(ps));\n  for (size_t i = 0; i < native_mb.size(); ) {\n    const char* src = native_mb.data() + i;\n    size_t res = mbrtowc(nullptr, src, native_mb.size() - i, &ps);\n    switch (res) {\n      // Handle any errors and return an empty string.\n      case static_cast<size_t>(-2):\n      case static_cast<size_t>(-1):\n        return std::wstring();\n      case 0:\n        // We hit an embedded null byte, keep going.\n        i += 1;\n        [[fallthrough]];\n      default:\n        i += res;\n        ++num_out_chars;\n        break;\n    }\n  }\n\n  if (num_out_chars == 0)\n    return std::wstring();\n\n  std::wstring out;\n  out.resize(num_out_chars);\n\n  memset(&ps, 0, sizeof(ps));  // Clear the shift state.\n  // We walk the input string again, with |i| tracking the index of the\n  // multi-byte input, and |j| tracking the wide output.\n  for (size_t i = 0, j = 0; i < native_mb.size(); ++j) {\n    const char* src = native_mb.data() + i;\n    wchar_t* dst = &out[j];\n    size_t res = mbrtowc(dst, src, native_mb.size() - i, &ps);\n    switch (res) {\n      // Handle any errors and return an empty string.\n      case static_cast<size_t>(-2):\n      case static_cast<size_t>(-1):\n        return std::wstring();\n      case 0:\n        i += 1;  // Skip null byte.\n        break;\n      default:\n        i += res;\n        break;\n    }\n  }\n\n  return out;\n}\n\n#endif  // defined(SYSTEM_NATIVE_UTF8) || BUILDFLAG(IS_ANDROID)\n\n}  // namespace tachyon::base\n\n// clang-format on\n"
  },
  {
    "path": "tachyon/base/system/BUILD.bazel",
    "content": "load(\"//bazel:tachyon.bzl\", \"if_posix\")\nload(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"sys_info\",\n    srcs = if_posix([\"sys_info_posix.cc\"]),\n    hdrs = [\"sys_info.h\"],\n    deps = [\n        \"//tachyon:export\",\n        \"//tachyon/base/numerics:checked_math\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/base/system/sys_info.h",
    "content": "// Copyright 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_SYSTEM_SYS_INFO_H_\n#define TACHYON_BASE_SYSTEM_SYS_INFO_H_\n\n#include <stddef.h>\n\n#include \"tachyon/export.h\"\n\nnamespace tachyon::base {\n\nclass TACHYON_EXPORT SysInfo {\n public:\n  // Return the smallest amount of memory (in bytes) which the VM system will\n  // allocate.\n  static size_t VMAllocationGranularity();\n};\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_SYSTEM_SYS_INFO_H_\n"
  },
  {
    "path": "tachyon/base/system/sys_info_posix.cc",
    "content": "// Copyright 2011 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include <unistd.h>\n\n#include \"tachyon/base/numerics/checked_math.h\"\n#include \"tachyon/base/system/sys_info.h\"\n\nnamespace tachyon::base {\n\n// static\nsize_t SysInfo::VMAllocationGranularity() {\n  return checked_cast<size_t>(getpagesize());\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/template_util.h",
    "content": "// Copyright (c) 2011 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n// clang-format off\n\n#ifndef TACHYON_BASE_TEMPLATE_UTIL_H_\n#define TACHYON_BASE_TEMPLATE_UTIL_H_\n\n#include <stddef.h>\n\n#include <iterator>\n#include <type_traits>\n\nnamespace tachyon::base {\nnamespace internal {\n\n// Used to detect whether the given type is an iterator.  This is normally used\n// with std::enable_if to provide disambiguation for functions that take\n// templated iterators as input.\ntemplate <typename T, typename = void>\nstruct is_iterator : std::false_type {};\n\ntemplate <typename T>\nstruct is_iterator<\n    T,\n    std::void_t<typename std::iterator_traits<T>::iterator_category>>\n    : std::true_type {};\n\n// Helper to express preferences in an overload set. If more than one overload\n// are available for a given set of parameters the overload with the higher\n// priority will be chosen.\ntemplate <size_t I>\nstruct priority_tag : priority_tag<I - 1> {};\n\ntemplate <>\nstruct priority_tag<0> {};\n\n}  // namespace internal\n\n// Implementation of C++20's std::remove_cvref.\n//\n// References:\n// - https://en.cppreference.com/w/cpp/types/remove_cvref\n// - https://wg21.link/meta.trans.other#lib:remove_cvref\ntemplate <typename T>\nstruct remove_cvref {\n  using type = std::remove_cv_t<std::remove_reference_t<T>>;\n};\n\n// Implementation of C++20's std::remove_cvref_t.\n//\n// References:\n// - https://en.cppreference.com/w/cpp/types/remove_cvref\n// - https://wg21.link/meta.type.synop#lib:remove_cvref_t\ntemplate <typename T>\nusing remove_cvref_t = typename remove_cvref<T>::type;\n\n// Simplified implementation of C++20's std::iter_value_t.\n// As opposed to std::iter_value_t, this implementation does not restrict\n// the type of `Iter` and does not consider specializations of\n// `indirectly_readable_traits`.\n//\n// Reference: https://wg21.link/readable.traits#2\ntemplate <typename Iter>\nusing iter_value_t =\n    typename std::iterator_traits<remove_cvref_t<Iter>>::value_type;\n\n// Simplified implementation of C++20's std::iter_difference_t.\n// As opposed to std::iter_difference_t, this implementation does not restrict\n// the type of `Iter` and does not consider specializations of\n// `indirectly_readable_traits`.\n//\n// Reference: https://wg21.link/readable.traits#2\ntemplate <typename Iter>\nusing iter_difference_t =\n    typename std::iterator_traits<remove_cvref_t<Iter>>::difference_type;\n\n// Simplified implementation of C++20's std::iter_reference_t.\n// As opposed to std::iter_reference_t, this implementation does not restrict\n// the type of `Iter`.\n//\n// Reference: https://wg21.link/iterator.synopsis#:~:text=iter_reference_t\ntemplate <typename Iter>\nusing iter_reference_t = decltype(*std::declval<Iter&>());\n\n// Simplified implementation of C++20's std::indirect_result_t. As opposed to\n// std::indirect_result_t, this implementation does not restrict the type of\n// `Func` and `Iters`.\n//\n// Reference: https://wg21.link/iterator.synopsis#:~:text=indirect_result_t\ntemplate <typename Func, typename... Iters>\nusing indirect_result_t =\n    std::invoke_result_t<Func, iter_reference_t<Iters>...>;\n\ntemplate<typename Iter>\nstruct is_const_iterator {\n    using pointer = typename std::iterator_traits<Iter>::pointer;\n    constexpr static bool value = std::is_const_v<std::remove_pointer_t<pointer>>;\n};\n\ntemplate <typename Iter>\nconstexpr bool is_const_iterator_v = is_const_iterator<Iter>::value;\n\n// Simplified implementation of C++20's std::projected. As opposed to\n// std::projected, this implementation does not explicitly restrict the type of\n// `Iter` and `Proj`, but rather does so implicitly by requiring\n// `indirect_result_t<Proj, Iter>` is a valid type. This is required for SFINAE\n// friendliness.\n//\n// Reference: https://wg21.link/projected\ntemplate <typename Iter, typename Proj,\n          typename IndirectResultT = indirect_result_t<Proj, Iter>>\nstruct projected {\n  using value_type = remove_cvref_t<IndirectResultT>;\n\n  IndirectResultT operator*() const;  // not defined\n};\n\n// Taken from\n// https://github.com/pybind/pybind11/blob/master/include/pybind11/detail/common.h\n// -------------------------------------------------------------------------------\n/// Like is_base_of, but requires a strict base (i.e. `is_strict_base_of<T,\n/// T>::value == false`, unlike `std::is_base_of`)\ntemplate <typename Base, typename Derived>\nusing is_strict_base_of = std::bool_constant<std::is_base_of<Base, Derived>::value &&\n                                        !std::is_same<Base, Derived>::value>;\n\n/// Compile-time all/any/none of that check the boolean value of all template\n/// types\n#if defined(__cpp_fold_expressions) && !(defined(_MSC_VER) && (_MSC_VER < 1916))\ntemplate <class... Ts>\nusing all_of = std::bool_constant<(Ts::value && ...)>;\ntemplate <class... Ts>\nusing any_of = std::bool_constant<(Ts::value || ...)>;\n#elif !defined(_MSC_VER)\ntemplate <bool...>\nstruct bools {};\ntemplate <class... Ts>\nusing all_of =\n    std::is_same<bools<Ts::value..., true>, bools<true, Ts::value...>>;\ntemplate <class... Ts>\nusing any_of = std::negation<all_of<std::negation<Ts>...>>;\n#else\n// MSVC has trouble with the above, but supports std::conjunction, which we can\n// use instead (albeit at a slight loss of compilation efficiency).\ntemplate <class... Ts>\nusing all_of = std::conjunction<Ts...>;\ntemplate <class... Ts>\nusing any_of = std::disjunction<Ts...>;\n#endif\ntemplate <class... Ts>\nusing none_of = std::negation<any_of<Ts...>>;\n\n/// Compile-time integer sum\n#ifdef __cpp_fold_expressions\ntemplate <typename... Ts>\nconstexpr size_t constexpr_sum(Ts... ns) {\n  return (0 + ... + size_t{ns});\n}\n#else\nconstexpr size_t constexpr_sum() { return 0; }\ntemplate <typename T, typename... Ts>\nconstexpr size_t constexpr_sum(T n, Ts... ns) {\n  return size_t{n} + constexpr_sum(ns...);\n}\n#endif\n\n/// Implementation details for constexpr functions\nconstexpr int first(int i) { return i; }\ntemplate <typename T, typename... Ts>\nconstexpr int first(int i, T v, Ts... vs) {\n  return v ? i : first(i + 1, vs...);\n}\n\nconstexpr int last(int /*i*/, int result) { return result; }\ntemplate <typename T, typename... Ts>\nconstexpr int last(int i, int result, T v, Ts... vs) {\n  return last(i + 1, v ? i : result, vs...);\n}\n\n/// Return the index of the first type in Ts which satisfies Predicate<T>.\n/// Returns sizeof...(Ts) if none match.\ntemplate <template <typename> class Predicate, typename... Ts>\nconstexpr int constexpr_first() {\n  return first(0, Predicate<Ts>::value...);\n}\n\n/// Return the index of the last type in Ts which satisfies Predicate<T>, or -1\n/// if none match.\ntemplate <template <typename> class Predicate, typename... Ts>\nconstexpr int constexpr_last() {\n  return last(0, -1, Predicate<Ts>::value...);\n}\n\n/// Return the Nth element from the parameter pack\ntemplate <size_t N, typename T, typename... Ts>\nstruct pack_element {\n  using type = typename pack_element<N - 1, Ts...>::type;\n};\ntemplate <typename T, typename... Ts>\nstruct pack_element<0, T, Ts...> {\n  using type = T;\n};\n\n/// Return the one and only type which matches the predicate, or Default if none\n/// match. If more than one type matches the predicate, fail at compile-time.\ntemplate <template <typename> class Predicate, typename Default, typename... Ts>\nstruct exactly_one {\n  static constexpr auto found = constexpr_sum(Predicate<Ts>::value...);\n  static_assert(found <= 1, \"Found more than one type matching the predicate\");\n\n  static constexpr auto index = found ? constexpr_first<Predicate, Ts...>() : 0;\n  using type =\n      std::conditional_t<found, typename pack_element<index, Ts...>::type,\n                         Default>;\n};\ntemplate <template <typename> class P, typename Default>\nstruct exactly_one<P, Default> {\n  using type = Default;\n};\n\ntemplate <template <typename> class Predicate, typename Default, typename... Ts>\nusing exactly_one_t = typename exactly_one<Predicate, Default, Ts...>::type;\n\n/// Apply a function over each element of a parameter pack\n#ifdef __cpp_fold_expressions\n#define TACHYON_EXPAND_SIDE_EFFECTS(PATTERN) (((PATTERN), void()), ...)\n#else\nusing expand_side_effects = bool[];\n#define TACHYON_EXPAND_SIDE_EFFECTS(PATTERN)       \\\n  (void)::tachyon::base::expand_side_effects { \\\n    ((PATTERN), void(), false)..., false         \\\n  }\n#endif\n\n// -------------------------------------------------------------------------------\n\ntemplate <typename T>\nstruct reference_to_pointer {\n  using Type = T;\n};\n\ntemplate <typename T>\nstruct reference_to_pointer<T&> {\n  using Type = std::remove_reference_t<T>*;\n};\n\ntemplate <typename T>\nusing reference_to_pointer_t = typename reference_to_pointer<T>::Type;\n\ntemplate <typename T>\nstruct container_value {\n  using type = typename T::value_type;\n};\n\ntemplate <typename T, size_t N>\nstruct container_value<T[N]> {\n  using type = T;\n};\n\ntemplate <typename T>\nusing container_value_t = typename container_value<T>::type;\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_TEMPLATE_UTIL_H_\n\n// clang-format on\n"
  },
  {
    "path": "tachyon/base/test/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"copy_only_int\",\n    testonly = True,\n    srcs = [\"copy_only_int.cc\"],\n    hdrs = [\"copy_only_int.h\"],\n)\n\ntachyon_cc_library(\n    name = \"move_only_int\",\n    testonly = True,\n    hdrs = [\"move_only_int.h\"],\n)\n\ntachyon_cc_library(\n    name = \"scoped_environment\",\n    testonly = True,\n    srcs = [\"scoped_environment.cc\"],\n    hdrs = [\"scoped_environment.h\"],\n    deps = [\"//tachyon/base:environment\"],\n)\n"
  },
  {
    "path": "tachyon/base/test/copy_only_int.cc",
    "content": "// Copyright 2019 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/test/copy_only_int.h\"\n\nnamespace tachyon::base {\n\n// static\nint CopyOnlyInt::num_copies_ = 0;\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/test/copy_only_int.h",
    "content": "// Copyright 2017 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_TEST_COPY_ONLY_INT_H_\n#define TACHYON_BASE_TEST_COPY_ONLY_INT_H_\n\nnamespace tachyon::base {\n\n// A copy-only (not moveable) class that holds an integer. This is designed for\n// testing containers. See also MoveOnlyInt.\nclass CopyOnlyInt {\n public:\n  explicit CopyOnlyInt(int data = 1) : data_(data) {}\n  CopyOnlyInt(const CopyOnlyInt& other) : data_(other.data_) { ++num_copies_; }\n  ~CopyOnlyInt() { data_ = 0; }\n\n  friend bool operator==(const CopyOnlyInt& lhs, const CopyOnlyInt& rhs) {\n    return lhs.data_ == rhs.data_;\n  }\n\n  friend bool operator!=(const CopyOnlyInt& lhs, const CopyOnlyInt& rhs) {\n    return !operator==(lhs, rhs);\n  }\n\n  friend bool operator<(const CopyOnlyInt& lhs, const CopyOnlyInt& rhs) {\n    return lhs.data_ < rhs.data_;\n  }\n\n  friend bool operator>(const CopyOnlyInt& lhs, const CopyOnlyInt& rhs) {\n    return rhs < lhs;\n  }\n\n  friend bool operator<=(const CopyOnlyInt& lhs, const CopyOnlyInt& rhs) {\n    return !(rhs < lhs);\n  }\n\n  friend bool operator>=(const CopyOnlyInt& lhs, const CopyOnlyInt& rhs) {\n    return !(lhs < rhs);\n  }\n\n  int data() const { return data_; }\n\n  static void reset_num_copies() { num_copies_ = 0; }\n\n  static int num_copies() { return num_copies_; }\n\n private:\n  volatile int data_;\n\n  static int num_copies_;\n\n  CopyOnlyInt(CopyOnlyInt&&) = delete;\n  CopyOnlyInt& operator=(CopyOnlyInt&) = delete;\n};\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_TEST_COPY_ONLY_INT_H_\n"
  },
  {
    "path": "tachyon/base/test/move_only_int.h",
    "content": "// Copyright 2017 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_TEST_MOVE_ONLY_INT_H_\n#define TACHYON_BASE_TEST_MOVE_ONLY_INT_H_\n\nnamespace tachyon::base {\n\n// A move-only class that holds an integer. This is designed for testing\n// containers. See also CopyOnlyInt.\nclass MoveOnlyInt {\n public:\n  explicit MoveOnlyInt(int data = 1) : data_(data) {}\n  MoveOnlyInt(MoveOnlyInt&& other) : data_(other.data_) { other.data_ = 0; }\n\n  MoveOnlyInt(const MoveOnlyInt&) = delete;\n  MoveOnlyInt& operator=(const MoveOnlyInt&) = delete;\n\n  ~MoveOnlyInt() { data_ = 0; }\n\n  MoveOnlyInt& operator=(MoveOnlyInt&& other) {\n    data_ = other.data_;\n    other.data_ = 0;\n    return *this;\n  }\n\n  friend bool operator==(const MoveOnlyInt& lhs, const MoveOnlyInt& rhs) {\n    return lhs.data_ == rhs.data_;\n  }\n\n  friend bool operator!=(const MoveOnlyInt& lhs, const MoveOnlyInt& rhs) {\n    return !operator==(lhs, rhs);\n  }\n\n  friend bool operator<(const MoveOnlyInt& lhs, int rhs) {\n    return lhs.data_ < rhs;\n  }\n\n  friend bool operator<(int lhs, const MoveOnlyInt& rhs) {\n    return lhs < rhs.data_;\n  }\n\n  friend bool operator<(const MoveOnlyInt& lhs, const MoveOnlyInt& rhs) {\n    return lhs.data_ < rhs.data_;\n  }\n\n  friend bool operator>(const MoveOnlyInt& lhs, const MoveOnlyInt& rhs) {\n    return rhs < lhs;\n  }\n\n  friend bool operator<=(const MoveOnlyInt& lhs, const MoveOnlyInt& rhs) {\n    return !(rhs < lhs);\n  }\n\n  friend bool operator>=(const MoveOnlyInt& lhs, const MoveOnlyInt& rhs) {\n    return !(lhs < rhs);\n  }\n\n  int data() const { return data_; }\n\n private:\n  volatile int data_;\n};\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_TEST_MOVE_ONLY_INT_H_\n"
  },
  {
    "path": "tachyon/base/test/scoped_environment.cc",
    "content": "#include \"tachyon/base/test/scoped_environment.h\"\n\n#include \"tachyon/base/environment.h\"\n\nnamespace tachyon::base {\n\nScopedEnvironment::ScopedEnvironment(std::string_view env_name,\n                                     std::string_view value)\n    : env_name_(std::string(env_name)) {\n  Environment::Set(env_name_, value);\n}\n\nScopedEnvironment::~ScopedEnvironment() { Environment::Unset(env_name_); }\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/test/scoped_environment.h",
    "content": "#ifndef TACHYON_BASE_TEST_SCOPED_ENVIRONMENT_H_\n#define TACHYON_BASE_TEST_SCOPED_ENVIRONMENT_H_\n\n#include <string>\n#include <string_view>\n\nnamespace tachyon::base {\n\nclass ScopedEnvironment {\n public:\n  explicit ScopedEnvironment(std::string_view env_name, std::string_view value);\n  ~ScopedEnvironment();\n\n private:\n  std::string env_name_;\n};\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_TEST_SCOPED_ENVIRONMENT_H_\n"
  },
  {
    "path": "tachyon/base/threading/.clang-format",
    "content": "DisableFormat: true\nSortIncludes: false"
  },
  {
    "path": "tachyon/base/threading/BUILD.bazel",
    "content": "load(\"//bazel:tachyon.bzl\", \"if_linux\", \"if_macos\", \"if_posix\")\nload(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_objc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"platform_thread_base\",\n    hdrs = [\"platform_thread.h\"],\n    visibility = [\"//visibility:private\"],\n    deps = [\n        \":platform_thread_ref\",\n        \"//tachyon/base/message_loop:message_pump_type\",\n        \"//tachyon/base/time\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"platform_thread\",\n    srcs = [\n        \"platform_thread.cc\",\n    ] + if_posix([\n        \"platform_thread_posix.cc\",\n    ]) + if_linux([\n        \"platform_thread_linux.cc\",\n        \"platform_thread_internal_posix.cc\",\n        \"platform_thread_internal_posix.h\",\n    ]),\n    deps = [\n        \":platform_thread_base\",\n        \"//tachyon/base:no_destructor\",\n        \"//tachyon/base/containers:adapters\",\n        \"//tachyon/base/containers:contains\",\n        \"//tachyon/base/containers:cxx20_erase\",\n        \"//tachyon/base/files:file_path\",\n        \"//tachyon/base/posix:can_lower_nice_to\",\n        \"//tachyon/base/process:process_handle\",\n        \"//tachyon/base/strings:string_number_conversions\",\n    ] + if_macos([\n        \":platform_thread_mac\",\n    ]),\n)\n\ntachyon_objc_library(\n    name = \"platform_thread_mac\",\n    srcs = [\"platform_thread_mac.mm\"],\n    deps = [\":platform_thread_base\"],\n)\n\ntachyon_cc_library(\n    name = \"platform_thread_ref\",\n    srcs = [\"platform_thread_ref.cc\"],\n    hdrs = [\"platform_thread_ref.h\"],\n    deps = [\n        \"//tachyon:export\",\n        \"//tachyon/build:build_config\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/base/threading/platform_thread.cc",
    "content": "// Copyright 2018 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/threading/platform_thread.h\"\n\n// #include \"tachyon/base/task/current_thread.h\"\n#include \"absl/base/attributes.h\"\n\n#if BUILDFLAG(IS_FUCHSIA)\n#include \"tachyon/base/fuchsia/scheduler.h\"\n#endif\n\nnamespace tachyon::base {\n\nnamespace {\n\nABSL_CONST_INIT thread_local ThreadType current_thread_type =\n    ThreadType::kDefault;\n\n}  // namespace\n\nstd::string ThreadTypeToString(ThreadType thread_type) {\n  switch (thread_type) {\n    case ThreadType::kBackground: return \"Background\";\n    case ThreadType::kUtility: return \"Utility\";\n    case ThreadType::kResourceEfficient: return \"ResourceEfficient\";\n    case ThreadType::kDefault: return \"Default\";\n    case ThreadType::kCompositing: return \"Compositing\";\n    case ThreadType::kDisplayCritical: return \"DisplayCritical\";\n    case ThreadType::kRealtimeAudio: return \"RealtimeAudio\";\n  }\n  NOTREACHED();\n  return \"\";\n}\n\nstd::ostream& operator<<(std::ostream& os, ThreadType thread_type) {\n  return os << ThreadTypeToString(thread_type);\n}\n\n// static\nvoid PlatformThreadBase::SetCurrentThreadType(ThreadType thread_type) {\n  MessagePumpType message_pump_type = MessagePumpType::DEFAULT;\n  // TODO(chokobole):\n  //   if (CurrentIOThread::IsSet()) {\n  //     message_pump_type = MessagePumpType::IO;\n  //   }\n  // #if !BUILDFLAG(IS_NACL)\n  //   else if (CurrentUIThread::IsSet()) {\n  //     message_pump_type = MessagePumpType::UI;\n  //   }\n  // #endif\n  internal::SetCurrentThreadType(thread_type, message_pump_type);\n}\n\n// static\nThreadType PlatformThreadBase::GetCurrentThreadType() {\n  return current_thread_type;\n}\n\n// static\nstd::optional<TimeDelta> PlatformThreadBase::GetThreadLeewayOverride() {\n#if BUILDFLAG(IS_FUCHSIA)\n  // On Fuchsia, all audio threads run with the CPU scheduling profile that uses\n  // an interval of |kAudioSchedulingPeriod|. Using the default leeway may lead\n  // to some tasks posted to audio threads to be executed too late (see\n  // http://crbug.com/1368858).\n  if (GetCurrentThreadType() == ThreadType::kRealtimeAudio)\n    return kAudioSchedulingPeriod;\n#endif\n  return std::nullopt;\n}\n\n// static\nvoid PlatformThreadBase::SetNameCommon(const std::string& name) {\n  // TODO(chokobole):\n  // ThreadIdNameManager::GetInstance()->SetName(name);\n}\n\nnamespace internal {\n\nvoid SetCurrentThreadType(ThreadType thread_type,\n                          MessagePumpType pump_type_hint) {\n  CHECK_LE(thread_type, ThreadType::kMaxValue);\n  SetCurrentThreadTypeImpl(thread_type, pump_type_hint);\n  current_thread_type = thread_type;\n}\n\n}  // namespace internal\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/threading/platform_thread.h",
    "content": "// Copyright 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n// WARNING: You should *NOT* be using this class directly.  PlatformThread is\n// the low-level platform-specific abstraction to the OS's threading interface.\n// You should instead be using a message-loop driven Thread, see thread.h.\n\n#ifndef TACHYON_BASE_THREADING_PLATFORM_THREAD_H_\n#define TACHYON_BASE_THREADING_PLATFORM_THREAD_H_\n\n#include <stddef.h>\n\n#include <iosfwd>\n#include <type_traits>\n#include <optional>\n\n#include \"tachyon/export.h\"\n#include \"tachyon/base/message_loop/message_pump_type.h\"\n#include \"tachyon/base/threading/platform_thread_ref.h\"\n#include \"tachyon/base/time/time.h\"\n#include \"tachyon/build/build_config.h\"\n\n#if BUILDFLAG(IS_WIN)\n#include \"tachyon/base/win/windows_types.h\"\n#elif BUILDFLAG(IS_FUCHSIA)\n#include <zircon/types.h>\n#elif BUILDFLAG(IS_APPLE)\n#include <mach/mach_types.h>\n#elif BUILDFLAG(IS_POSIX)\n#include <pthread.h>\n#include <unistd.h>\n#endif\n\nnamespace tachyon::base {\n\n// Used for logging. Always an integer value.\n#if BUILDFLAG(IS_WIN)\ntypedef DWORD PlatformThreadId;\n#elif BUILDFLAG(IS_FUCHSIA)\ntypedef zx_handle_t PlatformThreadId;\n#elif BUILDFLAG(IS_APPLE)\ntypedef mach_port_t PlatformThreadId;\n#elif BUILDFLAG(IS_POSIX)\ntypedef pid_t PlatformThreadId;\n#endif\nstatic_assert(std::is_integral_v<PlatformThreadId>, \"Always an integer value.\");\n\n// Used to operate on threads.\nclass PlatformThreadHandle {\n public:\n#if BUILDFLAG(IS_WIN)\n  typedef void* Handle;\n#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)\n  typedef pthread_t Handle;\n#endif\n\n  constexpr PlatformThreadHandle() : handle_(0) {}\n\n  explicit constexpr PlatformThreadHandle(Handle handle) : handle_(handle) {}\n\n  bool is_equal(const PlatformThreadHandle& other) const {\n    return handle_ == other.handle_;\n  }\n\n  bool is_null() const {\n    return !handle_;\n  }\n\n  Handle platform_handle() const {\n    return handle_;\n  }\n\n private:\n  Handle handle_;\n};\n\nconst PlatformThreadId kInvalidThreadId(0);\n\n// Valid values for `thread_type` of Thread::Options, SimpleThread::Options,\n// and SetCurrentThreadType(), listed in increasing order of importance.\n//\n// It is up to each platform-specific implementation what these translate to.\n// Callers should avoid setting different ThreadTypes on different platforms\n// (ifdefs) at all cost, instead the platform differences should be encoded in\n// the platform-specific implementations. Some implementations may treat\n// adjacent ThreadTypes in this enum as equivalent.\n//\n// Reach out to //base/task/OWNERS (scheduler-dev@chromium.org) before changing\n// thread type assignments in your component, as such decisions affect the whole\n// of Chrome.\n//\n// Refer to PlatformThreadTest.SetCurrentThreadTypeTest in\n// platform_thread_unittest.cc for the most up-to-date state of each platform's\n// handling of ThreadType.\nenum class ThreadType : int {\n  // Suitable for threads that have the least urgency and lowest priority, and\n  // can be interrupted or delayed by other types.\n  kBackground,\n  // Suitable for threads that are less important than normal type, and can be\n  // interrupted or delayed by threads with kDefault type.\n  kUtility,\n  // Suitable for threads that produce user-visible artifacts but aren't\n  // latency sensitive. The underlying platform will try to be economic\n  // in its usage of resources for this thread, if possible.\n  kResourceEfficient,\n  // Default type. The thread priority or quality of service will be set to\n  // platform default. In Chrome, this is suitable for handling user\n  // interactions (input), only display and audio can get a higher priority.\n  kDefault,\n  // Suitable for threads which are critical to compositing the foreground\n  // content.\n  kCompositing,\n  // Suitable for display critical threads.\n  kDisplayCritical,\n  // Suitable for low-latency, glitch-resistant audio.\n  kRealtimeAudio,\n  kMaxValue = kRealtimeAudio,\n};\n\n// Cross-platform mapping of physical thread priorities. Used by tests to verify\n// the underlying effects of SetCurrentThreadType.\nenum class ThreadPriorityForTest : int {\n  kBackground,\n  kUtility,\n  kNormal,\n  // The priority obtained via ThreadType::kDisplayCritical (and potentially\n  // other ThreadTypes).\n  kDisplay,\n  kRealtimeAudio,\n  kMaxValue = kRealtimeAudio,\n};\n\n// A namespace for low-level thread functions.\nclass TACHYON_EXPORT PlatformThreadBase {\n public:\n  // Implement this interface to run code on a background thread.  Your\n  // ThreadMain method will be called on the newly created thread.\n  class TACHYON_EXPORT Delegate {\n   public:\n    virtual void ThreadMain() = 0;\n\n#if BUILDFLAG(IS_APPLE)\n    // TODO: Move this to the PlatformThreadApple class.\n    // The interval at which the thread expects to have work to do. Zero if\n    // unknown. (Example: audio buffer duration for real-time audio.) Is used to\n    // optimize the thread real-time behavior. Is called on the newly created\n    // thread before ThreadMain().\n    virtual TimeDelta GetRealtimePeriod();\n#endif\n\n   protected:\n    virtual ~Delegate() = default;\n  };\n\n  PlatformThreadBase() = delete;\n  PlatformThreadBase(const PlatformThreadBase&) = delete;\n  PlatformThreadBase& operator=(const PlatformThreadBase&) = delete;\n\n  // Gets the current thread id, which may be useful for logging purposes.\n  static PlatformThreadId CurrentId();\n\n  // Gets the current thread reference, which can be used to check if\n  // we're on the right thread quickly.\n  static PlatformThreadRef CurrentRef();\n\n  // Get the handle representing the current thread. On Windows, this is a\n  // pseudo handle constant which will always represent the thread using it and\n  // hence should not be shared with other threads nor be used to differentiate\n  // the current thread from another.\n  static PlatformThreadHandle CurrentHandle();\n\n  // Yield the current thread so another thread can be scheduled.\n  //\n  // Note: this is likely not the right call to make in most situations. If this\n  // is part of a spin loop, consider base::Lock, which likely has better tail\n  // latency. Yielding the thread has different effects depending on the\n  // platform, system load, etc., and can result in yielding the CPU for less\n  // than 1us, or many tens of ms.\n  static void YieldCurrentThread();\n\n  // Sleeps for the specified duration (real-time; ignores time overrides).\n  // Note: The sleep duration may be in base::Time or base::TimeTicks, depending\n  // on platform. If you're looking to use this in unit tests testing delayed\n  // tasks, this will be unreliable - instead, use\n  // base::test::TaskEnvironment with MOCK_TIME mode.\n  static void Sleep(base::TimeDelta duration);\n\n  // Sets the thread name visible to debuggers/tools. This will try to\n  // initialize the context for current thread unless it's a WorkerThread.\n  static void SetName(const std::string& name);\n\n  // Gets the thread name, if previously set by SetName.\n  static const char* GetName();\n\n  // Creates a new thread.  The `stack_size` parameter can be 0 to indicate\n  // that the default stack size should be used.  Upon success,\n  // `*thread_handle` will be assigned a handle to the newly created thread,\n  // and `delegate`'s ThreadMain method will be executed on the newly created\n  // thread.\n  // NOTE: When you are done with the thread handle, you must call Join to\n  // release system resources associated with the thread.  You must ensure that\n  // the Delegate object outlives the thread.\n  static bool Create(size_t stack_size,\n                     Delegate* delegate,\n                     PlatformThreadHandle* thread_handle) {\n    return CreateWithType(stack_size, delegate, thread_handle,\n                          ThreadType::kDefault);\n  }\n\n  // CreateWithType() does the same thing as Create() except the priority and\n  // possibly the QoS of the thread is set based on `thread_type`.\n  // `pump_type_hint` must be provided if the thread will be using a\n  // MessagePumpForUI or MessagePumpForIO as this affects the application of\n  // `thread_type`.\n  static bool CreateWithType(\n      size_t stack_size,\n      Delegate* delegate,\n      PlatformThreadHandle* thread_handle,\n      ThreadType thread_type,\n      MessagePumpType pump_type_hint = MessagePumpType::DEFAULT);\n\n  // CreateNonJoinable() does the same thing as Create() except the thread\n  // cannot be Join()'d.  Therefore, it also does not output a\n  // PlatformThreadHandle.\n  static bool CreateNonJoinable(size_t stack_size, Delegate* delegate);\n\n  // CreateNonJoinableWithType() does the same thing as CreateNonJoinable()\n  // except the type of the thread is set based on `type`. `pump_type_hint` must\n  // be provided if the thread will be using a MessagePumpForUI or\n  // MessagePumpForIO as this affects the application of `thread_type`.\n  static bool CreateNonJoinableWithType(\n      size_t stack_size,\n      Delegate* delegate,\n      ThreadType thread_type,\n      MessagePumpType pump_type_hint = MessagePumpType::DEFAULT);\n\n  // Joins with a thread created via the Create function.  This function blocks\n  // the caller until the designated thread exits.  This will invalidate\n  // `thread_handle`.\n  static void Join(PlatformThreadHandle thread_handle);\n\n  // Detaches and releases the thread handle. The thread is no longer joinable\n  // and `thread_handle` is invalidated after this call.\n  static void Detach(PlatformThreadHandle thread_handle);\n\n  // Returns true if SetCurrentThreadType() should be able to change the type\n  // of a thread in current process from `from` to `to`.\n  static bool CanChangeThreadType(ThreadType from, ThreadType to);\n\n  // Declares the type of work running on the current thread. This will affect\n  // things like thread priority and thread QoS (Quality of Service) to the best\n  // of the current platform's abilities.\n  static void SetCurrentThreadType(ThreadType thread_type);\n\n  // Get the last `thread_type` set by SetCurrentThreadType, no matter if the\n  // underlying priority successfully changed or not.\n  static ThreadType GetCurrentThreadType();\n\n  // Returns a realtime period provided by `delegate`.\n  static TimeDelta GetRealtimePeriod(Delegate* delegate);\n\n  // Returns the override of task leeway if any.\n  static std::optional<TimeDelta> GetThreadLeewayOverride();\n\n  // Returns the default thread stack size set by chrome. If we do not\n  // explicitly set default size then returns 0.\n  static size_t GetDefaultThreadStackSize();\n\n  static ThreadPriorityForTest GetCurrentThreadPriorityForTest();\n\n  protected:\n  static void SetNameCommon(const std::string& name);\n};\n\n#if BUILDFLAG(IS_APPLE)\nclass TACHYON_EXPORT PlatformThreadApple : public PlatformThreadBase {\n public:\n  // Stores the period value in TLS.\n  static void SetCurrentThreadRealtimePeriodValue(TimeDelta realtime_period);\n\n  // Signals that the feature list has been initialized which allows to check\n  // the feature's value now and initialize state. This prevents race\n  // conditions where the feature is being checked while it is being\n  // initialized, which can cause a crash.\n  static void InitFeaturesPostFieldTrial();\n};\n#endif  // BUILDFLAG(IS_APPLE)\n\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\nclass ThreadTypeDelegate;\n\nclass TACHYON_EXPORT PlatformThreadLinux : public PlatformThreadBase {\n public:\n  static constexpr struct sched_param kRealTimePrio = {8};\n\n  // Sets a delegate which handles thread type changes for this process. This\n  // must be externally synchronized with any call to SetCurrentThreadType.\n  static void SetThreadTypeDelegate(ThreadTypeDelegate* delegate);\n\n  // Toggles a specific thread's type at runtime. This can be used to\n  // change the priority of a thread in a different process and will fail\n  // if the calling process does not have proper permissions. The\n  // SetCurrentThreadType() function above is preferred in favor of\n  // security but on platforms where sandboxed processes are not allowed to\n  // change priority this function exists to allow a non-sandboxed process\n  // to change the priority of sandboxed threads for improved performance.\n  // Warning: Don't use this for a main thread because that will change the\n  // whole thread group's (i.e. process) priority.\n  static void SetThreadType(PlatformThreadId process_id,\n                            PlatformThreadId thread_id,\n                            ThreadType thread_type);\n\n  // For a given thread id and thread type, setup the cpuset and schedtune\n  // CGroups for the thread.\n  static void SetThreadCgroupsForThreadType(PlatformThreadId thread_id,\n                                            ThreadType thread_type);\n};\n#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n\n#if BUILDFLAG(IS_CHROMEOS)\nclass TACHYON_EXPORT PlatformThreadChromeOS : public PlatformThreadLinux {\n public:\n  // Signals that the feature list has been initialized. Used for preventing\n  // race conditions and crashes, see comments in PlatformThreadApple.\n  static void InitFeaturesPostFieldTrial();\n\n  // Toggles a specific thread's type at runtime. This is the ChromeOS-specific\n  // version and includes Linux's functionality but does slightly more. See\n  // PlatformThreadLinux's SetThreadType() header comment for Linux details.\n  static void SetThreadType(PlatformThreadId process_id,\n                            PlatformThreadId thread_id,\n                            ThreadType thread_type);\n};\n#endif  // BUILDFLAG(IS_CHROMEOS)\n\n// Alias to the correct platform-specific class based on preprocessor directives\n#if BUILDFLAG(IS_APPLE)\nusing PlatformThread = PlatformThreadApple;\n#elif BUILDFLAG(IS_CHROMEOS)\nusing PlatformThread = PlatformThreadChromeOS;\n#elif BUILDFLAG(IS_LINUX)\nusing PlatformThread = PlatformThreadLinux;\n#else\nusing PlatformThread = PlatformThreadBase;\n#endif\n\nnamespace internal {\n\nvoid SetCurrentThreadType(ThreadType thread_type,\n                          MessagePumpType pump_type_hint);\n\nvoid SetCurrentThreadTypeImpl(ThreadType thread_type,\n                              MessagePumpType pump_type_hint);\n\n}  // namespace internal\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_THREADING_PLATFORM_THREAD_H_\n"
  },
  {
    "path": "tachyon/base/threading/platform_thread_internal_posix.cc",
    "content": "// Copyright 2015 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/threading/platform_thread_internal_posix.h\"\n\n#include <errno.h>\n#include <sys/resource.h>\n\n#include <ostream>\n\n#include \"tachyon/base/containers/adapters.h\"\n#include \"tachyon/base/logging.h\"\n\nnamespace tachyon::base {\n\nnamespace internal {\n\nint ThreadTypeToNiceValue(ThreadType thread_type) {\n  for (const auto& pair : kThreadTypeToNiceValueMap) {\n    if (pair.thread_type == thread_type)\n      return pair.nice_value;\n  }\n  NOTREACHED() << \"Unknown ThreadType\";\n  return 0;\n}\n\nThreadPriorityForTest NiceValueToThreadPriorityForTest(int nice_value) {\n  // Try to find a priority that best describes |nice_value|. If there isn't\n  // an exact match, this method returns the closest priority whose nice value\n  // is higher (lower priority) than |nice_value|.\n  for (const auto& pair : kThreadPriorityToNiceValueMapForTest) {\n    if (pair.nice_value >= nice_value)\n      return pair.priority;\n  }\n\n  // Reaching here means |nice_value| is more than any of the defined\n  // priorities. The lowest priority is suitable in this case.\n  return ThreadPriorityForTest::kBackground;\n}\n\nint GetCurrentThreadNiceValue() {\n#if BUILDFLAG(IS_NACL)\n  NOTIMPLEMENTED();\n  return 0;\n#else\n\n  // Need to clear errno before calling getpriority():\n  // http://man7.org/linux/man-pages/man2/getpriority.2.html\n  errno = 0;\n  int nice_value = getpriority(PRIO_PROCESS, 0);\n  if (errno != 0) {\n    DVPLOG(1) << \"Failed to get nice value of thread (\"\n              << PlatformThread::CurrentId() << \")\";\n    return 0;\n  }\n\n  return nice_value;\n#endif\n}\n\n}  // namespace internal\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/threading/platform_thread_internal_posix.h",
    "content": "// Copyright 2015 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_THREADING_PLATFORM_THREAD_INTERNAL_POSIX_H_\n#define TACHYON_BASE_THREADING_PLATFORM_THREAD_INTERNAL_POSIX_H_\n\n#include <optional>\n\n#include \"tachyon/export.h\"\n#include \"tachyon/base/threading/platform_thread.h\"\n#include \"tachyon/build/build_config.h\"\n\nnamespace tachyon::base {\n\nnamespace internal {\n\nstruct ThreadTypeToNiceValuePair {\n  ThreadType thread_type;\n  int nice_value;\n};\n\nstruct ThreadPriorityToNiceValuePairForTest {\n  ThreadPriorityForTest priority;\n  int nice_value;\n};\n\n// The elements must be listed in the order of increasing priority (lowest\n// priority first), that is, in the order of decreasing nice values (highest\n// nice value first).\nextern const ThreadTypeToNiceValuePair kThreadTypeToNiceValueMap[7];\n\n// The elements must be listed in the order of decreasing priority (highest\n// priority first), that is, in the order of increasing nice values (lowest nice\n// value first).\nextern const ThreadPriorityToNiceValuePairForTest\n    kThreadPriorityToNiceValueMapForTest[5];\n\n// Returns the nice value matching |priority| based on the platform-specific\n// implementation of kThreadTypeToNiceValueMap.\nint ThreadTypeToNiceValue(ThreadType thread_type);\n\n// Returns whether SetCurrentThreadTypeForPlatform can set a thread as\n// kRealtimeAudio.\nbool CanSetThreadTypeToRealtimeAudio();\n\n// Allows platform specific tweaks to the generic POSIX solution for\n// SetCurrentThreadType(). Returns true if the platform-specific\n// implementation handled this |thread_type| change, false if the generic\n// implementation should instead proceed.\nbool SetCurrentThreadTypeForPlatform(ThreadType thread_type,\n                                     MessagePumpType pump_type_hint);\n\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n// Current thread id is cached in thread local storage for performance reasons.\n// In some rare cases it's important to invalidate that cache explicitly (e.g.\n// after going through clone() syscall which does not call pthread_atfork()\n// handlers).\n// This can only be called when the process is single-threaded.\nTACHYON_EXPORT void InvalidateTidCache();\n#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n\n// Returns the ThreadPriorityForTest matching |nice_value| based on the\n// platform-specific implementation of kThreadPriorityToNiceValueMapForTest.\nThreadPriorityForTest NiceValueToThreadPriorityForTest(int nice_value);\n\nstd::optional<ThreadPriorityForTest>\nGetCurrentThreadPriorityForPlatformForTest();\n\nint GetCurrentThreadNiceValue();\n\n}  // namespace internal\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_THREADING_PLATFORM_THREAD_INTERNAL_POSIX_H_\n"
  },
  {
    "path": "tachyon/base/threading/platform_thread_linux.cc",
    "content": "// Copyright 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/threading/platform_thread.h\"\n\n#include <errno.h>\n#include <sched.h>\n#include <stddef.h>\n#include <cstdint>\n#include <atomic>\n#include <optional>\n\n#include \"tachyon/base/compiler_specific.h\"\n#include \"tachyon/base/files/file_path.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/process/process_handle.h\"\n// #include \"tachyon/base/process/internal_linux.h\"\n#include \"tachyon/base/strings/string_number_conversions.h\"\n#include \"tachyon/base/threading/platform_thread_internal_posix.h\"\n// #include \"tachyon/base/threading/thread_id_name_manager.h\"\n// #include \"tachyon/base/threading/thread_type_delegate.h\"\n#include \"tachyon/build/build_config.h\"\n\n#if !BUILDFLAG(IS_NACL) && !BUILDFLAG(IS_AIX)\n#include <pthread.h>\n#include <sys/prctl.h>\n#include <sys/resource.h>\n#include <sys/time.h>\n#include <sys/types.h>\n#include <unistd.h>\n#endif\n\nnamespace tachyon::base {\n\n#if BUILDFLAG(IS_CHROMEOS)\nBASE_FEATURE(kSchedUtilHints,\n             \"SchedUtilHints\",\n             base::FEATURE_ENABLED_BY_DEFAULT);\n#endif\n\nnamespace {\n\n#if !BUILDFLAG(IS_NACL)\nThreadTypeDelegate* g_thread_type_delegate = nullptr;\n#endif\n\n#if BUILDFLAG(IS_CHROMEOS)\nstd::atomic<bool> g_use_sched_util(true);\nstd::atomic<bool> g_scheduler_hints_adjusted(false);\n\n// When a device doesn't specify uclamp values via chrome switches,\n// default boosting for urgent tasks is hardcoded here as 20%.\n// Higher values can lead to higher power consumption thus this value\n// is chosen conservatively where it does not show noticeable\n// power usage increased from several perf/power tests.\nconst int kSchedulerBoostDef = 20;\nconst int kSchedulerLimitDef = 100;\nconst bool kSchedulerUseLatencyTuneDef = true;\n\nint g_scheduler_boost_adj;\nint g_scheduler_limit_adj;\nbool g_scheduler_use_latency_tune_adj;\n\n#if !BUILDFLAG(IS_NACL) && !BUILDFLAG(IS_AIX)\n\n// Defined by linux uclamp ABI of sched_setattr().\nconst uint32_t kSchedulerUclampMin = 0;\nconst uint32_t kSchedulerUclampMax = 1024;\n\n// sched_attr is used to set scheduler attributes for Linux. It is not a POSIX\n// struct and glibc does not expose it.\nstruct sched_attr {\n  uint32_t size;\n\n  uint32_t sched_policy;\n  uint64_t sched_flags;\n\n  /* SCHED_NORMAL, SCHED_BATCH */\n  __s32 sched_nice;\n\n  /* SCHED_FIFO, SCHED_RR */\n  uint32_t sched_priority;\n\n  /* SCHED_DEADLINE */\n  uint64_t sched_runtime;\n  uint64_t sched_deadline;\n  uint64_t sched_period;\n\n  /* Utilization hints */\n  uint32_t sched_util_min;\n  uint32_t sched_util_max;\n};\n\n#if !defined(__NR_sched_setattr)\n#if defined(__x86_64__)\n#define __NR_sched_setattr 314\n#define __NR_sched_getattr 315\n#elif defined(__i386__)\n#define __NR_sched_setattr 351\n#define __NR_sched_getattr 352\n#elif defined(__arm__)\n#define __NR_sched_setattr 380\n#define __NR_sched_getattr 381\n#elif defined(__aarch64__)\n#define __NR_sched_setattr 274\n#define __NR_sched_getattr 275\n#else\n#error \"We don't have an __NR_sched_setattr for this architecture.\"\n#endif\n#endif\n\n#if !defined(SCHED_FLAG_UTIL_CLAMP_MIN)\n#define SCHED_FLAG_UTIL_CLAMP_MIN 0x20\n#endif\n\n#if !defined(SCHED_FLAG_UTIL_CLAMP_MAX)\n#define SCHED_FLAG_UTIL_CLAMP_MAX 0x40\n#endif\n\nlong sched_getattr(pid_t pid,\n                   const struct sched_attr* attr,\n                   unsigned int size,\n                   unsigned int flags) {\n  return syscall(__NR_sched_getattr, pid, attr, size, flags);\n}\n\nlong sched_setattr(pid_t pid,\n                   const struct sched_attr* attr,\n                   unsigned int flags) {\n  return syscall(__NR_sched_setattr, pid, attr, flags);\n}\n#endif  // !BUILDFLAG(IS_NACL) && !BUILDFLAG(IS_AIX)\n#endif  // BUILDFLAG(IS_CHROMEOS)\n\n#if !BUILDFLAG(IS_NACL)\n/*\nTODO(chokobole):\nconst char kCgroupDirectory[] =\n    \"/sys/fs/cgroup\";\n\nFilePath ThreadTypeToCgroupDirectory(const FilePath& cgroup_filepath,\n                                     ThreadType thread_type) {\n  switch (thread_type) {\n    case ThreadType::kBackground:\n    case ThreadType::kUtility:\n    case ThreadType::kResourceEfficient:\n      return cgroup_filepath.Append(\"non-urgent\");\n    case ThreadType::kDefault:\n      return cgroup_filepath;\n    case ThreadType::kCompositing:\n#if BUILDFLAG(IS_CHROMEOS)\n      // On ChromeOS, kCompositing is also considered urgent.\n      return cgroup_filepath.Append(\"urgent\");\n#else\n      // TODO(1329208): Experiment with bringing IS_LINUX inline with\n      // IS_CHROMEOS.\n      return cgroup_filepath;\n#endif\n    case ThreadType::kDisplayCritical:\n    case ThreadType::kRealtimeAudio:\n      return cgroup_filepath.Append(\"urgent\");\n  }\n  NOTREACHED();\n  return FilePath();\n}\n\nvoid SetThreadCgroup(PlatformThreadId thread_id,\n                     const FilePath& cgroup_directory) {\n\n  FilePath tasks_filepath = cgroup_directory.Append(\"tasks\");\n  std::string tid = NumberToString(thread_id);\n  // TODO(crbug.com/1333521): Remove cast.\n  const int size = static_cast<int>(tid.size());\n  int bytes_written = WriteFile(tasks_filepath, tid.data(), size);\n  if (bytes_written != size) {\n    DVLOG(1) << \"Failed to add \" << tid << \" to \" << tasks_filepath.value();\n  }\n}\n\nvoid SetThreadCgroupForThreadType(PlatformThreadId thread_id,\n                                  const FilePath& cgroup_filepath,\n                                  ThreadType thread_type) {\n  // Append \"tachyon\" suffix.\n  FilePath cgroup_directory = ThreadTypeToCgroupDirectory(\n      cgroup_filepath.Append(\"tachyon\"), thread_type);\n\n  // Silently ignore request if cgroup directory doesn't exist.\n  if (!DirectoryExists(cgroup_directory))\n    return;\n\n  SetThreadCgroup(thread_id, cgroup_directory);\n}\n\n#if BUILDFLAG(IS_CHROMEOS)\n// thread_id should always be the value in the root PID namespace (see\n// FindThreadID).\nvoid SetThreadLatencySensitivity(ProcessId process_id,\n                                 PlatformThreadId thread_id,\n                                 ThreadType thread_type) {\n  struct sched_attr attr;\n  bool is_urgent = false;\n  int boost_percent, limit_percent;\n  int latency_sensitive_urgent;\n\n  // Scheduler boost defaults to true unless disabled.\n  if (!g_use_sched_util.load())\n    return;\n\n  // FieldTrial API can be called only once features were parsed.\n  if (g_scheduler_hints_adjusted.load()) {\n    boost_percent = g_scheduler_boost_adj;\n    limit_percent = g_scheduler_limit_adj;\n    latency_sensitive_urgent = g_scheduler_use_latency_tune_adj;\n  } else {\n    boost_percent = kSchedulerBoostDef;\n    limit_percent = kSchedulerLimitDef;\n    latency_sensitive_urgent = kSchedulerUseLatencyTuneDef;\n  }\n\n  // The thread_id passed in here is either 0 (in which case we ste for current\n  // thread), or is a tid that is not the NS tid but the global one. The\n  // conversion from NS tid to global tid is done by the callers using\n  // FindThreadID().\n  std::string thread_dir;\n  if (thread_id)\n    thread_dir = absl::StrFormat(\"/proc/%d/task/%d/\", process_id, thread_id);\n  else\n    thread_dir = \"/proc/thread-self/\";\n\n  // Silently ignore request if thread directory doesn't exist.\n  if (!DirectoryExists(FilePath(thread_dir)))\n    return;\n\n  FilePath latency_sensitive_file = FilePath(thread_dir + \"latency_sensitive\");\n\n  if (!PathExists(latency_sensitive_file))\n    return;\n\n  // Silently ignore if getattr fails due to sandboxing.\n  if (sched_getattr(thread_id, &attr, sizeof(attr), 0) == -1 ||\n      attr.size != sizeof(attr))\n    return;\n\n  switch (thread_type) {\n    case ThreadType::kBackground:\n    case ThreadType::kUtility:\n    case ThreadType::kResourceEfficient:\n    case ThreadType::kDefault:\n      break;\n    case ThreadType::kCompositing:\n    case ThreadType::kDisplayCritical:\n      // Compositing and display critical threads need a boost for consistent 60\n      // fps.\n      [[fallthrough]];\n    case ThreadType::kRealtimeAudio:\n      is_urgent = true;\n      break;\n  }\n\n  if (is_urgent && latency_sensitive_urgent) {\n    PLOG_IF(ERROR, !WriteFile(latency_sensitive_file, \"1\", 1))\n        << \"Failed to write latency file.\\n\";\n  } else {\n    PLOG_IF(ERROR, !WriteFile(latency_sensitive_file, \"0\", 1))\n        << \"Failed to write latency file.\\n\";\n  }\n\n  attr.sched_flags |= SCHED_FLAG_UTIL_CLAMP_MIN;\n  attr.sched_flags |= SCHED_FLAG_UTIL_CLAMP_MAX;\n\n  if (is_urgent) {\n    attr.sched_util_min =\n        (saturated_cast<uint32_t>(boost_percent) * kSchedulerUclampMax + 50) /\n        100;\n    attr.sched_util_max = kSchedulerUclampMax;\n  } else {\n    attr.sched_util_min = kSchedulerUclampMin;\n    attr.sched_util_max =\n        (saturated_cast<uint32_t>(limit_percent) * kSchedulerUclampMax + 50) /\n        100;\n  }\n\n  DCHECK_GE(attr.sched_util_min, kSchedulerUclampMin);\n  DCHECK_LE(attr.sched_util_max, kSchedulerUclampMax);\n\n  attr.size = sizeof(struct sched_attr);\n  if (sched_setattr(thread_id, &attr, 0) == -1) {\n    // We log it as an error because, if the PathExists above succeeded, we\n    // expect this syscall to also work since the kernel is new'ish.\n    PLOG_IF(ERROR, errno != E2BIG)\n        << \"Failed to set sched_util_min, performance may be effected.\\n\";\n  }\n}\n#endif\n\nvoid SetThreadCgroupsForThreadType(PlatformThreadId thread_id,\n                                   ThreadType thread_type) {\n  FilePath cgroup_filepath(kCgroupDirectory);\n  SetThreadCgroupForThreadType(\n      thread_id, cgroup_filepath.Append(\"cpuset\"),\n      thread_type);\n  SetThreadCgroupForThreadType(\n      thread_id, cgroup_filepath.Append(\"schedtune\"),\n      thread_type);\n}\n\n*/\n#endif\n\n}  // namespace\n\nnamespace internal {\n\nnamespace {\n#if !BUILDFLAG(IS_NACL)\nconst struct sched_param kRealTimePrio = {8};\n#endif\n}  // namespace\n\nconst ThreadPriorityToNiceValuePairForTest\n    kThreadPriorityToNiceValueMapForTest[5] = {\n        {ThreadPriorityForTest::kRealtimeAudio, -10},\n        {ThreadPriorityForTest::kDisplay, -8},\n        {ThreadPriorityForTest::kNormal, 0},\n        {ThreadPriorityForTest::kUtility, 1},\n        {ThreadPriorityForTest::kBackground, 10},\n};\n\nconst ThreadTypeToNiceValuePair kThreadTypeToNiceValueMap[7] = {\n    {ThreadType::kBackground, 10},       {ThreadType::kUtility, 1},\n    {ThreadType::kResourceEfficient, 0}, {ThreadType::kDefault, 0},\n#if BUILDFLAG(IS_CHROMEOS)\n    {ThreadType::kCompositing, -8},\n#else\n    // TODO(1329208): Experiment with bringing IS_LINUX inline with IS_CHROMEOS.\n    {ThreadType::kCompositing, 0},\n#endif\n    {ThreadType::kDisplayCritical, -8},  {ThreadType::kRealtimeAudio, -10},\n};\n\nbool CanSetThreadTypeToRealtimeAudio() {\n#if !BUILDFLAG(IS_NACL)\n  // A non-zero soft-limit on RLIMIT_RTPRIO is required to be allowed to invoke\n  // pthread_setschedparam in SetCurrentThreadTypeForPlatform().\n  struct rlimit rlim;\n  return getrlimit(RLIMIT_RTPRIO, &rlim) != 0 && rlim.rlim_cur != 0;\n#else\n  return false;\n#endif\n}\n\nbool SetCurrentThreadTypeForPlatform(ThreadType thread_type,\n                                     MessagePumpType pump_type_hint) {\n#if !BUILDFLAG(IS_NACL)\n  /*\n  TODO(chokobole):\n  const PlatformThreadId tid = PlatformThread::CurrentId();\n\n  if (g_thread_type_delegate &&\n      g_thread_type_delegate->HandleThreadTypeChange(tid, thread_type)) {\n    return true;\n  }\n\n  // For legacy schedtune interface\n  SetThreadCgroupsForThreadType(tid, thread_type);\n  */\n\n#if BUILDFLAG(IS_CHROMEOS)\n  // For upstream uclamp interface. We try both legacy (schedtune, as done\n  // earlier) and upstream (uclamp) interfaces, and whichever succeeds wins.\n  SetThreadLatencySensitivity(0 /* ignore */, 0 /* thread-self */, thread_type);\n#endif\n\n  return thread_type == ThreadType::kRealtimeAudio &&\n         pthread_setschedparam(pthread_self(), SCHED_RR, &kRealTimePrio) == 0;\n#else\n  return false;\n#endif\n}\n\nstd::optional<ThreadPriorityForTest>\nGetCurrentThreadPriorityForPlatformForTest() {\n#if !BUILDFLAG(IS_NACL)\n  int maybe_sched_rr = 0;\n  struct sched_param maybe_realtime_prio = {0};\n  if (pthread_getschedparam(pthread_self(), &maybe_sched_rr,\n                            &maybe_realtime_prio) == 0 &&\n      maybe_sched_rr == SCHED_RR &&\n      maybe_realtime_prio.sched_priority == kRealTimePrio.sched_priority) {\n    return std::make_optional(ThreadPriorityForTest::kRealtimeAudio);\n  }\n#endif\n  return std::nullopt;\n}\n\n}  // namespace internal\n\n// static\nvoid PlatformThreadBase::SetName(const std::string& name) {\n  // TODO(chokobole):\n  // ThreadIdNameManager::GetInstance()->SetName(name);\n\n#if !BUILDFLAG(IS_NACL) && !BUILDFLAG(IS_AIX)\n  // On linux we can get the thread names to show up in the debugger by setting\n  // the process name for the LWP.  We don't want to do this for the main\n  // thread because that would rename the process, causing tools like killall\n  // to stop working.\n  if (PlatformThread::CurrentId() == getpid())\n    return;\n\n  // http://0pointer.de/blog/projects/name-your-threads.html\n  // Set the name for the LWP (which gets truncated to 15 characters).\n  // Note that glibc also has a 'pthread_setname_np' api, but it may not be\n  // available everywhere and it's only benefit over using prctl directly is\n  // that it can set the name of threads other than the current thread.\n  int err = prctl(PR_SET_NAME, name.c_str());\n  // We expect EPERM failures in sandboxed processes, just ignore those.\n  if (err < 0 && errno != EPERM)\n    DPLOG(ERROR) << \"prctl(PR_SET_NAME)\";\n#endif  //  !BUILDFLAG(IS_NACL) && !BUILDFLAG(IS_AIX)\n}\n\n#if !BUILDFLAG(IS_NACL)\n// static\nvoid PlatformThread::SetThreadTypeDelegate(ThreadTypeDelegate* delegate) {\n  // A component cannot override a delegate set by another component, thus\n  // disallow setting a delegate when one already exists.\n  DCHECK(!g_thread_type_delegate || !delegate);\n\n  g_thread_type_delegate = delegate;\n}\n#endif\n\n#if !BUILDFLAG(IS_NACL) && !BUILDFLAG(IS_AIX)\n// static\nvoid PlatformThread::SetThreadType(ProcessId process_id,\n                                   PlatformThreadId thread_id,\n                                   ThreadType thread_type) {\n  // TODO(chokobole):\n  // For legacy schedtune interface\n  // SetThreadCgroupsForThreadType(thread_id, thread_type);\n\n#if BUILDFLAG(IS_CHROMEOS)\n  // For upstream uclamp interface. We try both legacy (schedtune, as done\n  // earlier) and upstream (uclamp) interfaces, and whichever succeeds wins.\n  SetThreadLatencySensitivity(process_id, thread_id, thread_type);\n#endif\n\n  const int nice_setting = internal::ThreadTypeToNiceValue(thread_type);\n  if (setpriority(PRIO_PROCESS, static_cast<id_t>(thread_id), nice_setting)) {\n    DVPLOG(1) << \"Failed to set nice value of thread (\" << thread_id << \") to \"\n              << nice_setting;\n  }\n}\n#endif  //  !BUILDFLAG(IS_NACL) && !BUILDFLAG(IS_AIX)\n\n#if BUILDFLAG(IS_CHROMEOS)\nvoid PlatformThread::InitFeaturesPostFieldTrial() {\n  DCHECK(FeatureList::GetInstance());\n  if (!FeatureList::IsEnabled(kSchedUtilHints)) {\n    g_use_sched_util.store(false);\n    return;\n  }\n\n  int boost_def = kSchedulerBoostDef;\n\n  if (CommandLine::ForCurrentProcess()->HasSwitch(\n          switches::kSchedulerBoostUrgent)) {\n    std::string boost_switch_str =\n        CommandLine::ForCurrentProcess()->GetSwitchValueASCII(\n            switches::kSchedulerBoostUrgent);\n\n    int boost_switch_val;\n    if (!StringToInt(boost_switch_str, &boost_switch_val) ||\n        boost_switch_val < 0 || boost_switch_val > 100) {\n      DVPLOG(1) << \"Invalid input for \" << switches::kSchedulerBoostUrgent;\n    } else {\n      boost_def = boost_switch_val;\n    }\n  }\n\n  g_scheduler_boost_adj = GetFieldTrialParamByFeatureAsInt(\n      kSchedUtilHints, \"BoostUrgent\", boost_def);\n  g_scheduler_limit_adj = GetFieldTrialParamByFeatureAsInt(\n      kSchedUtilHints, \"LimitNonUrgent\", kSchedulerLimitDef);\n  g_scheduler_use_latency_tune_adj = GetFieldTrialParamByFeatureAsBool(\n      kSchedUtilHints, \"LatencyTune\", kSchedulerUseLatencyTuneDef);\n\n  g_scheduler_hints_adjusted.store(true);\n}\n#endif\n\nvoid InitThreading() {}\n\nvoid TerminateOnThread() {}\n\nsize_t GetDefaultThreadStackSize(const pthread_attr_t& attributes) {\n#if !defined(THREAD_SANITIZER) && defined(__GLIBC__)\n  // Generally glibc sets ample default stack sizes, so use the default there.\n  return 0;\n#elif !defined(THREAD_SANITIZER)\n  // Other libcs (uclibc, musl, etc) tend to use smaller stacks, often too small\n  // for chromium. Make sure we have enough space to work with here. Note that\n  // for comparison glibc stacks are generally around 8MB.\n  return 2 * (1 << 20);\n#else\n  // ThreadSanitizer bloats the stack heavily. Evidence has been that the\n  // default stack size isn't enough for some browser tests.\n  return 2 * (1 << 23);  // 2 times 8192K (the default stack size on Linux).\n#endif\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/threading/platform_thread_mac.mm",
    "content": "// Copyright 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/threading/platform_thread.h\"\n\n#import <Foundation/Foundation.h>\n#include <mach/mach.h>\n#include <mach/mach_time.h>\n#include <mach/thread_policy.h>\n#include <mach/thread_switch.h>\n#include <stddef.h>\n#include <sys/resource.h>\n\n#include <algorithm>\n#include <atomic>\n\n#include \"tachyon/base/logging.h\"\n// #include \"tachyon/base/mac/foundation_util.h\"\n// #include \"tachyon/base/mac/mac_util.h\"\n// #include \"tachyon/base/mac/mach_logging.h\"\n// #include \"tachyon/base/metrics/histogram_functions.h\"\n// #include \"tachyon/base/threading/thread_id_name_manager.h\"\n// #include \"tachyon/base/threading/threading_features.h\"\n#include \"tachyon/build/build_config.h\"\n\n#if !defined(__has_feature) || !__has_feature(objc_arc)\n#error \"This file requires ARC support.\"\n#endif\n\nnamespace tachyon::base {\n\nnamespace {\nNSString* const kThreadPriorityForTestKey = @\"CrThreadPriorityForTestKey\";\nNSString* const kRealtimePeriodNsKey = @\"CrRealtimePeriodNsKey\";\n}  // namespace\n\n// If Cocoa is to be used on more than one thread, it must know that the\n// application is multithreaded.  Since it's possible to enter Cocoa code\n// from threads created by pthread_thread_create, Cocoa won't necessarily\n// be aware that the application is multithreaded.  Spawning an NSThread is\n// enough to get Cocoa to set up for multithreaded operation, so this is done\n// if necessary before pthread_thread_create spawns any threads.\n//\n// http://developer.apple.com/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/chapter_4_section_4.html\nvoid InitThreading() {\n  static BOOL multithreaded = [NSThread isMultiThreaded];\n  if (!multithreaded) {\n    // +[NSObject class] is idempotent.\n    @autoreleasepool {\n      [NSThread detachNewThreadSelector:@selector(class)\n                               toTarget:[NSObject class]\n                             withObject:nil];\n      multithreaded = YES;\n\n      DCHECK([NSThread isMultiThreaded]);\n    }\n  }\n}\n\nTimeDelta PlatformThreadBase::Delegate::GetRealtimePeriod() {\n  return TimeDelta();\n}\n\n// static\nvoid PlatformThreadBase::YieldCurrentThread() {\n  // Don't use sched_yield(), as it can lead to 10ms delays.\n  //\n  // This only depresses the thread priority for 1ms, which is more in line\n  // with what calling code likely wants. See this bug in webkit for context:\n  // https://bugs.webkit.org/show_bug.cgi?id=204871\n  mach_msg_timeout_t timeout_ms = 1;\n  thread_switch(MACH_PORT_NULL, SWITCH_OPTION_DEPRESS, timeout_ms);\n}\n\n// static\nvoid PlatformThreadBase::SetName(const std::string& name) {\n  SetNameCommon(name);\n\n  // macOS does not expose the length limit of the name, so hardcode it.\n  const int kMaxNameLength = 63;\n  std::string shortened_name = name.substr(0, kMaxNameLength);\n  // pthread_setname() fails (harmlessly) in the sandbox, ignore when it does.\n  // See http://crbug.com/47058\n  pthread_setname_np(shortened_name.c_str());\n}\n\n/*\nTODO(chokobole):\n// Whether optimized realt-time thread config should be used for audio.\nBASE_FEATURE(kOptimizedRealtimeThreadingMac,\n             \"OptimizedRealtimeThreadingMac\",\n#if BUILDFLAG(IS_MAC)\n             FEATURE_ENABLED_BY_DEFAULT\n#else\n             FEATURE_DISABLED_BY_DEFAULT\n#endif\n);\n\nconst Feature kUserInteractiveCompositingMac{\"UserInteractiveCompositingMac\",\n                                             FEATURE_DISABLED_BY_DEFAULT};\n\nnamespace {\n\nbool IsOptimizedRealtimeThreadingMacEnabled() {\n  return FeatureList::IsEnabled(kOptimizedRealtimeThreadingMac);\n}\n\n}  // namespace\n\n// Fine-tuning optimized real-time thread config:\n// Whether or not the thread should be preemptible.\nconst FeatureParam<bool> kOptimizedRealtimeThreadingMacPreemptible{\n    &kOptimizedRealtimeThreadingMac, \"preemptible\", true};\n// Portion of the time quantum the thread is expected to be busy, (0, 1].\nconst FeatureParam<double> kOptimizedRealtimeThreadingMacBusy{\n    &kOptimizedRealtimeThreadingMac, \"busy\", 0.5};\n// Maximum portion of the time quantum the thread is expected to be busy,\n// (kOptimizedRealtimeThreadingMacBusy, 1].\nconst FeatureParam<double> kOptimizedRealtimeThreadingMacBusyLimit{\n    &kOptimizedRealtimeThreadingMac, \"busy_limit\", 1.0};\nstd::atomic<bool> g_user_interactive_compositing(\n    kUserInteractiveCompositingMac.default_state == FEATURE_ENABLED_BY_DEFAULT);\n\nnamespace {\n\nstruct TimeConstraints {\n  bool preemptible{kOptimizedRealtimeThreadingMacPreemptible.default_value};\n  double busy{kOptimizedRealtimeThreadingMacBusy.default_value};\n  double busy_limit{kOptimizedRealtimeThreadingMacBusyLimit.default_value};\n\n  static TimeConstraints ReadFromFeatureParams() {\n    double busy_limit = kOptimizedRealtimeThreadingMacBusyLimit.Get();\n    return TimeConstraints{\n        kOptimizedRealtimeThreadingMacPreemptible.Get(),\n        std::min(busy_limit, kOptimizedRealtimeThreadingMacBusy.Get()),\n        busy_limit};\n  }\n};\n\n// Use atomics to access FeatureList values when setting up a thread, since\n// there are cases when FeatureList initialization is not synchronized with\n// PlatformThread creation.\nstd::atomic<bool> g_use_optimized_realtime_threading(\n    kOptimizedRealtimeThreadingMac.default_state == FEATURE_ENABLED_BY_DEFAULT);\nstd::atomic<TimeConstraints> g_time_constraints;\n\n}  // namespace\n*/\n\n// static\nvoid PlatformThreadApple::InitFeaturesPostFieldTrial() {\n  /*\n  TODO(chokobole):\n  // A DCHECK is triggered on FeatureList initialization if the state of a\n  // feature has been checked before. To avoid triggering this DCHECK in unit\n  // tests that call this before initializing the FeatureList, only check the\n  // state of the feature if the FeatureList is initialized.\n  if (FeatureList::GetInstance()) {\n    g_time_constraints.store(TimeConstraints::ReadFromFeatureParams());\n    g_use_optimized_realtime_threading.store(\n        IsOptimizedRealtimeThreadingMacEnabled());\n    g_user_interactive_compositing.store(\n        FeatureList::IsEnabled(kUserInteractiveCompositingMac));\n  }\n  */\n}\n\n// static\nvoid PlatformThreadApple::SetCurrentThreadRealtimePeriodValue(\n    TimeDelta realtime_period) {\n  /*\n  TODO(chokobole):\n  if (g_use_optimized_realtime_threading.load()) {\n    NSThread.currentThread.threadDictionary[kRealtimePeriodNsKey] =\n        @(realtime_period.InNanoseconds());\n  }\n  */\n}\n\nnamespace {\n/*\nTODO(chokobole):\nTimeDelta GetCurrentThreadRealtimePeriod() {\n  NSNumber* period = mac::ObjCCast<NSNumber>(\n      NSThread.currentThread.threadDictionary[kRealtimePeriodNsKey]);\n\n  return period ? Nanoseconds(period.longLongValue) : TimeDelta();\n}\n\n// Calculates time constraints for THREAD_TIME_CONSTRAINT_POLICY.\n// |realtime_period| is used as a base if it's non-zero.\n// Otherwise we fall back to empirical values.\nthread_time_constraint_policy_data_t GetTimeConstraints(\n    TimeDelta realtime_period) {\n  thread_time_constraint_policy_data_t time_constraints;\n  mach_timebase_info_data_t tb_info;\n  mach_timebase_info(&tb_info);\n\n  if (!realtime_period.is_zero()) {\n    // Limit the lowest value to 2.9 ms we used to have historically. The lower\n    // the period, the more CPU frequency may go up, and we don't want to risk\n    // worsening the thermal situation.\n    uint32_t abs_realtime_period = saturated_cast<uint32_t>(\n        std::max(realtime_period.InNanoseconds(), 2900000LL) *\n        (double(tb_info.denom) / tb_info.numer));\n    TimeConstraints config = g_time_constraints.load();\n    time_constraints.period = abs_realtime_period;\n    time_constraints.constraint = std::min(\n        abs_realtime_period, uint32_t(abs_realtime_period * config.busy_limit));\n    time_constraints.computation =\n        std::min(time_constraints.constraint,\n                 uint32_t(abs_realtime_period * config.busy));\n    time_constraints.preemptible = config.preemptible ? YES : NO;\n    return time_constraints;\n  }\n\n  // Empirical configuration.\n\n  // Define the guaranteed and max fraction of time for the audio thread.\n  // These \"duty cycle\" values can range from 0 to 1.  A value of 0.5\n  // means the scheduler would give half the time to the thread.\n  // These values have empirically been found to yield good behavior.\n  // Good means that audio performance is high and other threads won't starve.\n  const double kGuaranteedAudioDutyCycle = 0.75;\n  const double kMaxAudioDutyCycle = 0.85;\n\n  // Define constants determining how much time the audio thread can\n  // use in a given time quantum.  All times are in milliseconds.\n\n  // About 128 frames @44.1KHz\n  const double kTimeQuantum = 2.9;\n\n  // Time guaranteed each quantum.\n  const double kAudioTimeNeeded = kGuaranteedAudioDutyCycle * kTimeQuantum;\n\n  // Maximum time each quantum.\n  const double kMaxTimeAllowed = kMaxAudioDutyCycle * kTimeQuantum;\n\n  // Get the conversion factor from milliseconds to absolute time\n  // which is what the time-constraints call needs.\n  double ms_to_abs_time = double(tb_info.denom) / tb_info.numer * 1000000;\n\n  time_constraints.period = kTimeQuantum * ms_to_abs_time;\n  time_constraints.computation = kAudioTimeNeeded * ms_to_abs_time;\n  time_constraints.constraint = kMaxTimeAllowed * ms_to_abs_time;\n  time_constraints.preemptible = 0;\n  return time_constraints;\n}\n\n// Enables time-constraint policy and priority suitable for low-latency,\n// glitch-resistant audio.\nvoid SetPriorityRealtimeAudio(TimeDelta realtime_period) {\n  // Increase thread priority to real-time.\n\n  // Please note that the thread_policy_set() calls may fail in\n  // rare cases if the kernel decides the system is under heavy load\n  // and is unable to handle boosting the thread priority.\n  // In these cases we just return early and go on with life.\n\n  mach_port_t mach_thread_id =\n      pthread_mach_thread_np(PlatformThread::CurrentHandle().platform_handle());\n\n  // Make thread fixed priority.\n  thread_extended_policy_data_t policy;\n  policy.timeshare = 0;  // Set to 1 for a non-fixed thread.\n  kern_return_t result = thread_policy_set(\n      mach_thread_id, THREAD_EXTENDED_POLICY,\n      reinterpret_cast<thread_policy_t>(&policy), THREAD_EXTENDED_POLICY_COUNT);\n  if (result != KERN_SUCCESS) {\n    MACH_DVLOG(1, result) << \"thread_policy_set\";\n    return;\n  }\n\n  // Set to relatively high priority.\n  thread_precedence_policy_data_t precedence;\n  precedence.importance = 63;\n  result = thread_policy_set(mach_thread_id, THREAD_PRECEDENCE_POLICY,\n                             reinterpret_cast<thread_policy_t>(&precedence),\n                             THREAD_PRECEDENCE_POLICY_COUNT);\n  if (result != KERN_SUCCESS) {\n    MACH_DVLOG(1, result) << \"thread_policy_set\";\n    return;\n  }\n\n  // Most important, set real-time constraints.\n\n  thread_time_constraint_policy_data_t time_constraints =\n      GetTimeConstraints(realtime_period);\n\n  result =\n      thread_policy_set(mach_thread_id, THREAD_TIME_CONSTRAINT_POLICY,\n                        reinterpret_cast<thread_policy_t>(&time_constraints),\n                        THREAD_TIME_CONSTRAINT_POLICY_COUNT);\n  MACH_DVLOG_IF(1, result != KERN_SUCCESS, result) << \"thread_policy_set\";\n  return;\n}\n*/\n\n}  // anonymous namespace\n\n// static\nbool PlatformThreadBase::CanChangeThreadType(ThreadType from, ThreadType to) {\n  return true;\n}\n\nnamespace internal {\n\nvoid SetCurrentThreadTypeImpl(ThreadType thread_type,\n                              MessagePumpType pump_type_hint) {\n  // Changing the priority of the main thread causes performance\n  // regressions. https://crbug.com/601270\n  // TODO(1280764): Remove this check. kCompositing is the default on Mac, so\n  // this check is counter intuitive.\n  if ([[NSThread currentThread] isMainThread] &&\n      thread_type >= ThreadType::kCompositing) {\n    DCHECK(thread_type == ThreadType::kDefault ||\n           thread_type == ThreadType::kCompositing);\n    return;\n  }\n\n  ThreadPriorityForTest priority = ThreadPriorityForTest::kNormal;\n  switch (thread_type) {\n    case ThreadType::kBackground:\n      priority = ThreadPriorityForTest::kBackground;\n      pthread_set_qos_class_self_np(QOS_CLASS_BACKGROUND, 0);\n      break;\n    case ThreadType::kUtility:\n      priority = ThreadPriorityForTest::kUtility;\n      pthread_set_qos_class_self_np(QOS_CLASS_UTILITY, 0);\n      break;\n    case ThreadType::kResourceEfficient:\n      priority = ThreadPriorityForTest::kUtility;\n      pthread_set_qos_class_self_np(QOS_CLASS_UTILITY, 0);\n      break;\n    case ThreadType::kDefault:\n      priority = ThreadPriorityForTest::kNormal;\n      pthread_set_qos_class_self_np(QOS_CLASS_USER_INITIATED, 0);\n      break;\n    case ThreadType::kCompositing:\n      /*\n      TODO(chokobole):\n      if (g_user_interactive_compositing.load(std::memory_order_relaxed)) {\n        priority = ThreadPriorityForTest::kDisplay;\n        pthread_set_qos_class_self_np(QOS_CLASS_USER_INTERACTIVE, 0);\n      } else {\n        priority = ThreadPriorityForTest::kNormal;\n        pthread_set_qos_class_self_np(QOS_CLASS_USER_INITIATED, 0);\n      }\n      */\n      break;\n    case ThreadType::kDisplayCritical: {\n      priority = ThreadPriorityForTest::kDisplay;\n      pthread_set_qos_class_self_np(QOS_CLASS_USER_INTERACTIVE, 0);\n      break;\n    }\n    case ThreadType::kRealtimeAudio:\n      priority = ThreadPriorityForTest::kRealtimeAudio;\n      // TODO(chokobole):\n      // SetPriorityRealtimeAudio(GetCurrentThreadRealtimePeriod());\n      DCHECK_EQ([NSThread.currentThread threadPriority], 1.0);\n      break;\n  }\n\n  NSThread.currentThread.threadDictionary[kThreadPriorityForTestKey] =\n      @(static_cast<int>(priority));\n}\n\n}  // namespace internal\n\n/*\nTODO(chokobole):\n// static\nThreadPriorityForTest PlatformThreadBase::GetCurrentThreadPriorityForTest() {\n  NSNumber* priority = base::mac::ObjCCast<NSNumber>(\n      NSThread.currentThread.threadDictionary[kThreadPriorityForTestKey]);\n\n  if (!priority)\n    return ThreadPriorityForTest::kNormal;\n\n  ThreadPriorityForTest thread_priority =\n      static_cast<ThreadPriorityForTest>(priority.intValue);\n  DCHECK_GE(thread_priority, ThreadPriorityForTest::kBackground);\n  DCHECK_LE(thread_priority, ThreadPriorityForTest::kMaxValue);\n  return thread_priority;\n}\n*/\n\nsize_t GetDefaultThreadStackSize(const pthread_attr_t& attributes) {\n#if BUILDFLAG(IS_IOS)\n#if BUILDFLAG(USE_BLINK)\n  // For iOS 512kB (the default) isn't sufficient, but using the code\n  // for macOS below will return 8MB. So just be a little more conservative\n  // and return 1MB for now.\n  return 1024 * 1024;\n#else\n  return 0;\n#endif\n#else\n  // The macOS default for a pthread stack size is 512kB.\n  // Libc-594.1.4/pthreads/pthread.c's pthread_attr_init uses\n  // DEFAULT_STACK_SIZE for this purpose.\n  //\n  // 512kB isn't quite generous enough for some deeply recursive threads that\n  // otherwise request the default stack size by specifying 0. Here, adopt\n  // glibc's behavior as on Linux, which is to use the current stack size\n  // limit (ulimit -s) as the default stack size. See\n  // glibc-2.11.1/nptl/nptl-init.c's __pthread_initialize_minimal_internal. To\n  // avoid setting the limit below the macOS default or the minimum usable\n  // stack size, these values are also considered. If any of these values\n  // can't be determined, or if stack size is unlimited (ulimit -s unlimited),\n  // stack_size is left at 0 to get the system default.\n  //\n  // macOS normally only applies ulimit -s to the main thread stack. On\n  // contemporary macOS and Linux systems alike, this value is generally 8MB\n  // or in that neighborhood.\n  size_t default_stack_size = 0;\n  struct rlimit stack_rlimit;\n  if (pthread_attr_getstacksize(&attributes, &default_stack_size) == 0 &&\n      getrlimit(RLIMIT_STACK, &stack_rlimit) == 0 &&\n      stack_rlimit.rlim_cur != RLIM_INFINITY) {\n    default_stack_size =\n        std::max(std::max(default_stack_size,\n                          static_cast<size_t>(PTHREAD_STACK_MIN)),\n                 static_cast<size_t>(stack_rlimit.rlim_cur));\n  }\n  return default_stack_size;\n#endif\n}\n\nvoid TerminateOnThread() {\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/threading/platform_thread_posix.cc",
    "content": "// Copyright 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/threading/platform_thread.h\"\n\n#include <errno.h>\n#include <pthread.h>\n#include <sched.h>\n#include <stddef.h>\n#include <stdint.h>\n#include <sys/time.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <memory>\n#include <tuple>\n\n#include \"tachyon/base/compiler_specific.h\"\n#include \"tachyon/base/logging.h\"\n// #include \"tachyon/base/threading/scoped_blocking_call.h\"\n// #include \"tachyon/base/threading/thread_id_name_manager.h\"\n// #include \"tachyon/base/threading/thread_restrictions.h\"\n#include \"tachyon/build/build_config.h\"\n\n#if !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_FUCHSIA) && !BUILDFLAG(IS_NACL)\n#include \"tachyon/base/posix/can_lower_nice_to.h\"\n#include \"tachyon/base/threading/platform_thread_internal_posix.h\"\n#endif\n\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n#include <sys/syscall.h>\n#include <atomic>\n#endif\n\n#if BUILDFLAG(IS_FUCHSIA)\n#include <zircon/process.h>\n#else\n#include <sys/resource.h>\n#endif\n\nnamespace tachyon::base {\n\nvoid InitThreading();\nvoid TerminateOnThread();\nsize_t GetDefaultThreadStackSize(const pthread_attr_t& attributes);\n\nnamespace {\n\nstruct ThreadParams {\n  ThreadParams() = default;\n\n  // raw_ptr<PlatformThreadBase::Delegate> delegate = nullptr;\n  PlatformThreadBase::Delegate* delegate = nullptr;\n  bool joinable = false;\n  ThreadType thread_type = ThreadType::kDefault;\n  MessagePumpType message_pump_type = MessagePumpType::DEFAULT;\n};\n\nvoid* ThreadFunc(void* params) {\n  PlatformThreadBase::Delegate* delegate = nullptr;\n\n  {\n    std::unique_ptr<ThreadParams> thread_params(\n        static_cast<ThreadParams*>(params));\n\n    delegate = thread_params->delegate;\n    // TODO(chokobole):\n    // if (!thread_params->joinable)\n    //   base::DisallowSingleton();\n\n#if !BUILDFLAG(IS_NACL)\n#if BUILDFLAG(IS_APPLE)\n    PlatformThread::SetCurrentThreadRealtimePeriodValue(\n        delegate->GetRealtimePeriod());\n#endif\n\n    // Threads on linux/android may inherit their priority from the thread\n    // where they were created. This explicitly sets the priority of all new\n    // threads.\n    PlatformThreadBase::SetCurrentThreadType(thread_params->thread_type);\n#endif  //  !BUILDFLAG(IS_NACL)\n  }\n\n  // TODO(chokobole):\n  // ThreadIdNameManager::GetInstance()->RegisterThread(\n  //     PlatformThreadBase::CurrentHandle().platform_handle(),\n  //     PlatformThreadBase::CurrentId());\n\n  delegate->ThreadMain();\n\n  // TODO(chokobole):\n  // ThreadIdNameManager::GetInstance()->RemoveName(\n  //     PlatformThreadBase::CurrentHandle().platform_handle(),\n  //     PlatformThreadBase::CurrentId());\n\n  base::TerminateOnThread();\n  return nullptr;\n}\n\nbool CreateThread(size_t stack_size,\n                  bool joinable,\n                  PlatformThreadBase::Delegate* delegate,\n                  PlatformThreadHandle* thread_handle,\n                  ThreadType thread_type,\n                  MessagePumpType message_pump_type) {\n  DCHECK(thread_handle);\n  base::InitThreading();\n\n  pthread_attr_t attributes;\n  pthread_attr_init(&attributes);\n\n  // Pthreads are joinable by default, so only specify the detached\n  // attribute if the thread should be non-joinable.\n  if (!joinable)\n    pthread_attr_setdetachstate(&attributes, PTHREAD_CREATE_DETACHED);\n\n  // Get a better default if available.\n  if (stack_size == 0)\n    stack_size = base::GetDefaultThreadStackSize(attributes);\n\n  if (stack_size > 0)\n    pthread_attr_setstacksize(&attributes, stack_size);\n\n  std::unique_ptr<ThreadParams> params(new ThreadParams);\n  params->delegate = delegate;\n  params->joinable = joinable;\n  params->thread_type = thread_type;\n  params->message_pump_type = message_pump_type;\n\n  pthread_t handle;\n  int err = pthread_create(&handle, &attributes, ThreadFunc, params.get());\n  bool success = !err;\n  if (success) {\n    // ThreadParams should be deleted on the created thread after used.\n    std::ignore = params.release();\n  } else {\n    // Value of |handle| is undefined if pthread_create fails.\n    handle = 0;\n    errno = err;\n    PLOG(ERROR) << \"pthread_create\";\n  }\n  *thread_handle = PlatformThreadHandle(handle);\n\n  pthread_attr_destroy(&attributes);\n\n  return success;\n}\n\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n\n// Store the thread ids in local storage since calling the SWI can be\n// expensive and PlatformThreadBase::CurrentId is used liberally.\nthread_local pid_t g_thread_id = -1;\n\n// A boolean value that indicates that the value stored in |g_thread_id| on the\n// main thread is invalid, because it hasn't been updated since the process\n// forked.\n//\n// This used to work by setting |g_thread_id| to -1 in a pthread_atfork handler.\n// However, when a multithreaded process forks, it is only allowed to call\n// async-signal-safe functions until it calls an exec() syscall. However,\n// accessing TLS may allocate (see crbug.com/1275748), which is not\n// async-signal-safe and therefore causes deadlocks, corruption, and crashes.\n//\n// It's Atomic to placate TSAN.\nstd::atomic<bool> g_main_thread_tid_cache_valid = false;\n\n// Tracks whether the current thread is the main thread, and therefore whether\n// |g_main_thread_tid_cache_valid| is relevant for the current thread. This is\n// also updated by PlatformThreadBase::CurrentId().\nthread_local bool g_is_main_thread = true;\n\nclass InitAtFork {\n public:\n  InitAtFork() {\n    pthread_atfork(nullptr, nullptr, internal::InvalidateTidCache);\n  }\n};\n\n#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n\n}  // namespace\n\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n\nnamespace internal {\n\nvoid InvalidateTidCache() {\n  g_main_thread_tid_cache_valid.store(false, std::memory_order_relaxed);\n}\n\n}  // namespace internal\n\n#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n\n// static\nPlatformThreadId PlatformThreadBase::CurrentId() {\n  // Pthreads doesn't have the concept of a thread ID, so we have to reach down\n  // into the kernel.\n#if BUILDFLAG(IS_APPLE)\n  return pthread_mach_thread_np(pthread_self());\n#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n  // Workaround false-positive MSAN use-of-uninitialized-value on\n  // thread_local storage for loaded libraries:\n  // https://github.com/google/sanitizers/issues/1265\n  MSAN_UNPOISON(&g_thread_id, sizeof(pid_t));\n  MSAN_UNPOISON(&g_is_main_thread, sizeof(bool));\n  static InitAtFork init_at_fork;\n  if (g_thread_id == -1 ||\n      (g_is_main_thread &&\n       !g_main_thread_tid_cache_valid.load(std::memory_order_relaxed))) {\n    // Update the cached tid.\n    g_thread_id = static_cast<pid_t>(syscall(__NR_gettid));\n    // If this is the main thread, we can mark the tid_cache as valid.\n    // Otherwise, stop the current thread from always entering this slow path.\n    if (g_thread_id == getpid()) {\n      g_main_thread_tid_cache_valid.store(true, std::memory_order_relaxed);\n    } else {\n      g_is_main_thread = false;\n    }\n  } else {\n#if DCHECK_IS_ON()\n    if (g_thread_id != syscall(__NR_gettid)) {\n      RAW_LOG(\n          FATAL,\n          \"Thread id stored in TLS is different from thread id returned by \"\n          \"the system. It is likely that the process was forked without going \"\n          \"through fork().\");\n    }\n#endif\n  }\n  return g_thread_id;\n#elif BUILDFLAG(IS_ANDROID)\n  // Note: do not cache the return value inside a thread_local variable on\n  // Android (as above). The reasons are:\n  // - thread_local is slow on Android (goes through emutls)\n  // - gettid() is fast, since its return value is cached in pthread (in the\n  //   thread control block of pthread). See gettid.c in bionic.\n  return gettid();\n#elif BUILDFLAG(IS_FUCHSIA)\n  return zx_thread_self();\n#elif BUILDFLAG(IS_SOLARIS) || BUILDFLAG(IS_QNX)\n  return pthread_self();\n#elif BUILDFLAG(IS_NACL) && defined(__GLIBC__)\n  return pthread_self();\n#elif BUILDFLAG(IS_NACL) && !defined(__GLIBC__)\n  // Pointers are 32-bits in NaCl.\n  return reinterpret_cast<int32_t>(pthread_self());\n#elif BUILDFLAG(IS_POSIX) && BUILDFLAG(IS_AIX)\n  return pthread_self();\n#elif BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_AIX)\n  return reinterpret_cast<int64_t>(pthread_self());\n#endif\n}\n\n// static\nPlatformThreadRef PlatformThreadBase::CurrentRef() {\n  return PlatformThreadRef(pthread_self());\n}\n\n// static\nPlatformThreadHandle PlatformThreadBase::CurrentHandle() {\n  return PlatformThreadHandle(pthread_self());\n}\n\n#if !BUILDFLAG(IS_APPLE)\n// static\nvoid PlatformThreadBase::YieldCurrentThread() {\n  sched_yield();\n}\n#endif  // !BUILDFLAG(IS_APPLE)\n\n// static\nvoid PlatformThreadBase::Sleep(TimeDelta duration) {\n  struct timespec sleep_time, remaining;\n\n  // Break the duration into seconds and nanoseconds.\n  // NOTE: TimeDelta's microseconds are int64s while timespec's\n  // nanoseconds are longs, so this unpacking must prevent overflow.\n  sleep_time.tv_sec = static_cast<time_t>(duration.InSeconds());\n  duration -= Seconds(sleep_time.tv_sec);\n  sleep_time.tv_nsec = static_cast<long>(duration.InMicroseconds() * 1000);\n\n  while (nanosleep(&sleep_time, &remaining) == -1 && errno == EINTR)\n    sleep_time = remaining;\n}\n\n// static\nconst char* PlatformThreadBase::GetName() {\n  return \"\";\n  // TODO(chokobole):\n  // return ThreadIdNameManager::GetInstance()->GetName(CurrentId());\n}\n\n// static\nbool PlatformThreadBase::CreateWithType(size_t stack_size,\n                                    Delegate* delegate,\n                                    PlatformThreadHandle* thread_handle,\n                                    ThreadType thread_type,\n                                    MessagePumpType pump_type_hint) {\n  return CreateThread(stack_size, true /* joinable thread */, delegate,\n                      thread_handle, thread_type, pump_type_hint);\n}\n\n// static\nbool PlatformThreadBase::CreateNonJoinable(size_t stack_size, Delegate* delegate) {\n  return CreateNonJoinableWithType(stack_size, delegate, ThreadType::kDefault);\n}\n\n// static\nbool PlatformThreadBase::CreateNonJoinableWithType(size_t stack_size,\n                                                   Delegate* delegate,\n                                                   ThreadType thread_type,\n                                                   MessagePumpType pump_type_hint) {\n  PlatformThreadHandle unused;\n\n  bool result = CreateThread(stack_size, false /* non-joinable thread */,\n                             delegate, &unused, thread_type , pump_type_hint);\n  return result;\n}\n\n// static\nvoid PlatformThreadBase::Join(PlatformThreadHandle thread_handle) {\n  // Joining another thread may block the current thread for a long time, since\n  // the thread referred to by |thread_handle| may still be running long-lived /\n  // blocking tasks.\n  // TODO(chokobole):\n  // base::internal::ScopedBlockingCallWithBaseSyncPrimitives scoped_blocking_call(\n  //     FROM_HERE, base::BlockingType::MAY_BLOCK);\n  CHECK_EQ(0, pthread_join(thread_handle.platform_handle(), nullptr));\n}\n\n// static\nvoid PlatformThreadBase::Detach(PlatformThreadHandle thread_handle) {\n  CHECK_EQ(0, pthread_detach(thread_handle.platform_handle()));\n}\n\n// Mac and Fuchsia have their own SetCurrentThreadType() and\n// GetCurrentThreadPriorityForTest() implementations.\n#if !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_FUCHSIA)\n\n// static\nbool PlatformThreadBase::CanChangeThreadType(ThreadType from, ThreadType to) {\n#if BUILDFLAG(IS_NACL)\n  return false;\n#else\n  if (from >= to) {\n    // Decreasing thread priority on POSIX is always allowed.\n    return true;\n  }\n  if (to == ThreadType::kRealtimeAudio) {\n    return internal::CanSetThreadTypeToRealtimeAudio();\n  }\n\n  return internal::CanLowerNiceTo(internal::ThreadTypeToNiceValue(to));\n#endif  // BUILDFLAG(IS_NACL)\n}\n\nnamespace internal {\n\nvoid SetCurrentThreadTypeImpl(ThreadType thread_type,\n                              MessagePumpType pump_type_hint) {\n#if BUILDFLAG(IS_NACL)\n  NOTIMPLEMENTED();\n#else\n  if (internal::SetCurrentThreadTypeForPlatform(thread_type, pump_type_hint))\n    return;\n\n  // setpriority(2) should change the whole thread group's (i.e. process)\n  // priority. However, as stated in the bugs section of\n  // http://man7.org/linux/man-pages/man2/getpriority.2.html: \"under the current\n  // Linux/NPTL implementation of POSIX threads, the nice value is a per-thread\n  // attribute\". Also, 0 is preferred to the current thread id since it is\n  // equivalent but makes sandboxing easier (https://crbug.com/399473).\n  const int nice_setting = internal::ThreadTypeToNiceValue(thread_type);\n  if (setpriority(PRIO_PROCESS, 0, nice_setting)) {\n    DVPLOG(1) << \"Failed to set nice value of thread (\"\n              << PlatformThreadBase::CurrentId() << \") to \" << nice_setting;\n  }\n#endif  // BUILDFLAG(IS_NACL)\n}\n\n}  // namespace internal\n\n// static\nThreadPriorityForTest PlatformThreadBase::GetCurrentThreadPriorityForTest() {\n#if BUILDFLAG(IS_NACL)\n  NOTIMPLEMENTED();\n  return ThreadPriorityForTest::kNormal;\n#else\n  // Mirrors SetCurrentThreadPriority()'s implementation.\n  auto platform_specific_priority =\n      internal::GetCurrentThreadPriorityForPlatformForTest();  // IN-TEST\n  if (platform_specific_priority)\n    return platform_specific_priority.value();\n\n  int nice_value = internal::GetCurrentThreadNiceValue();\n\n  return internal::NiceValueToThreadPriorityForTest(nice_value);  // IN-TEST\n#endif  // !BUILDFLAG(IS_NACL)\n}\n\n#endif  // !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_FUCHSIA)\n\n// static\nsize_t PlatformThreadBase::GetDefaultThreadStackSize() {\n  pthread_attr_t attributes;\n  pthread_attr_init(&attributes);\n  return base::GetDefaultThreadStackSize(attributes);\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/threading/platform_thread_ref.cc",
    "content": "// Copyright 2021 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/threading/platform_thread_ref.h\"\n\n#include <ostream>\n\nnamespace tachyon::base {\n\nstd::ostream& operator<<(std::ostream& os, const PlatformThreadRef& ref) {\n  os << ref.id_;\n  return os;\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/threading/platform_thread_ref.h",
    "content": "// Copyright 2021 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n// WARNING: *DO NOT* use this class directly. base::PlatformThreadRef is a\n// low-level platform-specific abstraction to the OS's threading interface.\n// Instead, consider using a message-loop driven base::Thread, see\n// base/threading/thread.h.\n\n#ifndef TACHYON_BASE_THREADING_PLATFORM_THREAD_REF_H_\n#define TACHYON_BASE_THREADING_PLATFORM_THREAD_REF_H_\n\n#include <iosfwd>\n\n#include \"tachyon/export.h\"\n#include \"tachyon/build/build_config.h\"\n\n#if BUILDFLAG(IS_WIN)\n#include \"tachyon/base/win/windows_types.h\"\n#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)\n#include <pthread.h>\n#endif\n\nnamespace tachyon::base {\n\n// Used for thread checking and debugging.\n// Meant to be as fast as possible.\n// These are produced by PlatformThread::CurrentRef(), and used to later\n// check if we are on the same thread or not by using ==. These are safe\n// to copy between threads, but can't be copied to another process as they\n// have no meaning there. Also, the internal identifier can be re-used\n// after a thread dies, so a PlatformThreadRef cannot be reliably used\n// to distinguish a new thread from an old, dead thread.\nclass PlatformThreadRef {\n public:\n#if BUILDFLAG(IS_WIN)\n  using RefType = DWORD;\n#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)\n  using RefType = pthread_t;\n#endif\n\n  constexpr PlatformThreadRef() = default;\n  explicit constexpr PlatformThreadRef(RefType id) : id_(id) {}\n\n  bool operator==(PlatformThreadRef other) const { return id_ == other.id_; }\n  bool operator!=(PlatformThreadRef other) const { return id_ != other.id_; }\n\n  bool is_null() const { return id_ == 0; }\n\n private:\n  friend TACHYON_EXPORT std::ostream& operator<<(std::ostream& os,\n                                                 const PlatformThreadRef& ref);\n\n  RefType id_ = 0;\n};\n\nTACHYON_EXPORT std::ostream& operator<<(std::ostream& os,\n                                        const PlatformThreadRef& ref);\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_THREADING_PLATFORM_THREAD_REF_H_\n"
  },
  {
    "path": "tachyon/base/time/BUILD.bazel",
    "content": "load(\"@bazel_skylib//rules:common_settings.bzl\", \"bool_flag\")\nload(\"//bazel:tachyon.bzl\", \"if_macos\", \"if_posix\")\nload(\n    \"//bazel:tachyon_cc.bzl\",\n    \"tachyon_cc_library\",\n    \"tachyon_cc_unittest\",\n    \"tachyon_objc_library\",\n)\nload(\":time_buildflags.bzl\", \"ENABLE_MACH_ABSOLUTE_TIME_TICKS\", \"time_buildflag_header\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nbool_flag(\n    name = ENABLE_MACH_ABSOLUTE_TIME_TICKS,\n    build_setting_default = True,\n)\n\ntime_buildflag_header(\n    name = \"time_buildflags\",\n    enable_mach_absolute_time_ticks = \":\" + ENABLE_MACH_ABSOLUTE_TIME_TICKS,\n)\n\ntachyon_cc_library(\n    name = \"time_base\",\n    hdrs = [\"time.h\"],\n    visibility = [\"//visibility:private\"],\n    deps = [\n        \":time_buildflags\",\n        \"//tachyon/base:compiler_specific\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base/numerics:clamped_math\",\n        \"@com_google_absl//absl/time\",\n        \"@com_google_googletest//:gtest_prod\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"time\",\n    srcs = [\"time.cc\"] + select({\n        \"@platforms//os:macos\": [],\n        \"@platforms//os:windows\": [],\n        \"//conditions:default\": [\n            \"time_now_posix.cc\",\n        ],\n    }) + if_posix([\n        \"time_conversion_posix.cc\",\n    ]),\n    deps = [\n        \":time_base\",\n        \"//tachyon/base/numerics:checked_math\",\n    ] + if_macos([\n        \":time_mac\",\n    ]),\n)\n\ntachyon_objc_library(\n    name = \"time_mac\",\n    srcs = [\"time_mac.mm\"],\n    deps = [\n        \":time_base\",\n        \"//tachyon/base/mac:scoped_cftyperef\",\n        \"//tachyon/base/mac:scoped_mach_port\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"time_delta_flag\",\n    srcs = [\"time_delta_flag.h\"],\n    deps = [\n        \":time\",\n        \"//tachyon/base/flag\",\n        \"//tachyon/base/numerics:safe_conversions\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"time_interval\",\n    srcs = [\"time_interval.cc\"],\n    hdrs = [\"time_interval.h\"],\n    deps = [\":time\"],\n)\n\ntachyon_cc_library(\n    name = \"time_stamp\",\n    srcs = [\"time_stamp.cc\"],\n    hdrs = [\"time_stamp.h\"],\n    deps = [\":time\"],\n)\n\ntachyon_objc_library(\n    name = \"time_mac_unittests\",\n    testonly = True,\n    srcs = [\"time_mac_unittest.mm\"],\n    deps = [\n        \":time\",\n        \"@com_google_googletest//:gtest\",\n    ],\n    alwayslink = True,\n)\n\ntachyon_cc_unittest(\n    name = \"time_unittests\",\n    srcs = [\n        \"time_delta_flag_unittest.cc\",\n        \"time_interval_unittest.cc\",\n        \"time_stamp_unittest.cc\",\n        \"time_unittest.cc\",\n    ],\n    deps = [\n        \":time_delta_flag\",\n        \":time_interval\",\n        \":time_stamp\",\n        \"//tachyon/base/threading:platform_thread\",\n    ] + if_macos([\n        \":time_mac_unittests\",\n    ]),\n)\n"
  },
  {
    "path": "tachyon/base/time/time.cc",
    "content": "// Copyright 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/time/time.h\"\n\n#include \"absl/time/clock.h\"\n\nnamespace tachyon::base {\n\n// TimeDelta ------------------------------------------------------------------\n\n// static\nTimeDelta TimeDelta::FromAbsl(absl::Duration d) {\n  return TimeDelta(absl::ToInt64Microseconds(d));\n}\n\nstd::chrono::hours TimeDelta::ToChronoHours() const {\n  return std::chrono::hours(InHours());\n}\n\nstd::chrono::minutes TimeDelta::ToChronoMinutes() const {\n  return std::chrono::minutes(InMinutes());\n}\n\nstd::chrono::seconds TimeDelta::ToChronoSeconds() const {\n  return std::chrono::seconds(InSeconds());\n}\n\nstd::chrono::milliseconds TimeDelta::ToChronoMilliseconds() const {\n  return std::chrono::milliseconds(InMilliseconds());\n}\n\nstd::chrono::nanoseconds TimeDelta::ToChronoNanoseconds() const {\n  return std::chrono::nanoseconds(InNanoseconds());\n}\n\nint64_t TimeDelta::InSecondsFloored() const {\n  if (!is_inf()) {\n    const int64_t result = delta_ / Time::kMicrosecondsPerSecond;\n    // Convert |result| from truncating to flooring.\n    return (result * Time::kMicrosecondsPerSecond > delta_) ? (result - 1)\n                                                            : result;\n  }\n  return delta_;\n}\n\nint TimeDelta::InDays() const {\n  if (!is_inf()) return static_cast<int>(delta_ / Time::kMicrosecondsPerDay);\n  return (delta_ < 0) ? std::numeric_limits<int>::min()\n                      : std::numeric_limits<int>::max();\n}\n\nint TimeDelta::InDaysFloored() const {\n  if (!is_inf()) {\n    const int result = delta_ / Time::kMicrosecondsPerDay;\n    // Convert |result| from truncating to flooring.\n    return (result * Time::kMicrosecondsPerDay > delta_) ? (result - 1)\n                                                         : result;\n  }\n  return (delta_ < 0) ? std::numeric_limits<int>::min()\n                      : std::numeric_limits<int>::max();\n}\n\ndouble TimeDelta::InMillisecondsF() const {\n  if (!is_inf())\n    return static_cast<double>(delta_) / Time::kMicrosecondsPerMillisecond;\n  return (delta_ < 0) ? -std::numeric_limits<double>::infinity()\n                      : std::numeric_limits<double>::infinity();\n}\n\nint64_t TimeDelta::InMilliseconds() const {\n  if (!is_inf()) return delta_ / Time::kMicrosecondsPerMillisecond;\n  return (delta_ < 0) ? std::numeric_limits<int64_t>::min()\n                      : std::numeric_limits<int64_t>::max();\n}\n\nint64_t TimeDelta::InMillisecondsRoundedUp() const {\n  if (!is_inf()) {\n    const int64_t result = delta_ / Time::kMicrosecondsPerMillisecond;\n    // Convert |result| from truncating to ceiling.\n    return (delta_ > result * Time::kMicrosecondsPerMillisecond) ? (result + 1)\n                                                                 : result;\n  }\n  return delta_;\n}\n\ndouble TimeDelta::InMicrosecondsF() const {\n  if (!is_inf()) return static_cast<double>(delta_);\n  return (delta_ < 0) ? -std::numeric_limits<double>::infinity()\n                      : std::numeric_limits<double>::infinity();\n}\n\nTimeDelta TimeDelta::CeilToMultiple(TimeDelta interval) const {\n  if (is_inf() || interval.is_zero()) return *this;\n  const TimeDelta remainder = *this % interval;\n  if (delta_ < 0) return *this - remainder;\n  return remainder.is_zero() ? *this\n                             : (*this - remainder + interval.magnitude());\n}\n\nTimeDelta TimeDelta::FloorToMultiple(TimeDelta interval) const {\n  if (is_inf() || interval.is_zero()) return *this;\n  const TimeDelta remainder = *this % interval;\n  if (delta_ < 0) {\n    return remainder.is_zero() ? *this\n                               : (*this - remainder - interval.magnitude());\n  }\n  return *this - remainder;\n}\n\nTimeDelta TimeDelta::RoundToMultiple(TimeDelta interval) const {\n  if (is_inf() || interval.is_zero()) return *this;\n  if (interval.is_inf()) return TimeDelta();\n  const TimeDelta half = interval.magnitude() / 2;\n  return (delta_ < 0) ? (*this - half).CeilToMultiple(interval)\n                      : (*this + half).FloorToMultiple(interval);\n}\n\nstd::ostream& operator<<(std::ostream& os, TimeDelta time_delta) {\n  return os << time_delta.InSecondsF() << \" s\";\n}\n\n// Time -----------------------------------------------------------------------\n\n// static\nTime Time::Now() {\n  absl::Time now = absl::Now();\n  return Time(absl::ToUnixMicros(now));\n}\n\ntime_t Time::ToTimeT() const {\n  if (is_null()) return 0;  // Preserve 0 so we can tell it doesn't exist.\n  if (!is_inf()) {\n    return saturated_cast<time_t>((*this - Time()).InSecondsFloored());\n  }\n  return (us_ < 0) ? std::numeric_limits<time_t>::min()\n                   : std::numeric_limits<time_t>::max();\n}\n\n// static\nTime Time::FromDoubleT(double dt) {\n  // Preserve 0 so we can tell it doesn't exist.\n  return (dt == 0 || std::isnan(dt)) ? Time() : (Time() + Seconds(dt));\n}\n\ndouble Time::ToDoubleT() const {\n  if (is_null()) return 0;  // Preserve 0 so we can tell it doesn't exist.\n  if (!is_inf()) return (*this - Time()).InSecondsF();\n  return (us_ < 0) ? -std::numeric_limits<double>::infinity()\n                   : std::numeric_limits<double>::infinity();\n}\n\n#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)\n// static\nTime Time::FromTimeSpec(const timespec& ts) {\n  return FromDoubleT(ts.tv_sec +\n                     static_cast<double>(ts.tv_nsec) / kNanosecondsPerSecond);\n}\n#endif\n\n// static\nTime Time::FromAbsl(absl::Time time) { return Time(absl::ToUnixMicros(time)); }\n\nabsl::Time Time::ToAbsl() const { return absl::FromUnixMicros(us_); }\n\n// static\nTime Time::FromChrono(std::chrono::system_clock::time_point tp) {\n  return Time::FromDeltaSinceUnixEpoch(\n      TimeDelta::FromChrono(tp.time_since_epoch()));\n}\n\nstd::chrono::system_clock::time_point Time::ToChrono() const {\n  using D = std::chrono::system_clock::duration;\n  return std::chrono::system_clock::time_point{} +\n         ToDeltaSinceUnixEpoch().ToChrono<D>();\n}\n\nstd::ostream& operator<<(std::ostream& os, Time time) {\n  const TimeDelta time_delta = time - Time();\n  absl::Time absl_time = absl::FromUnixMicros(time_delta.InMicroseconds());\n  return os << absl_time;\n}\n\n// static\nTimeTicks TimeTicks::FromChrono(std::chrono::steady_clock::time_point tp) {\n  return TimeTicks(\n      TimeDelta::FromChrono(tp.time_since_epoch()).InMicroseconds());\n}\n\nstd::chrono::steady_clock::time_point TimeTicks::ToChrono() const {\n  using D = std::chrono::steady_clock::duration;\n  return std::chrono::steady_clock::time_point{} +\n         Microseconds(us_).ToChrono<D>();\n}\n\nstd::ostream& operator<<(std::ostream& os, TimeTicks time_ticks) {\n  // This function formats a TimeTicks object as \"bogo-microseconds\".\n  // The origin and granularity of the count are platform-specific, and may very\n  // from run to run. Although bogo-microseconds usually roughly correspond to\n  // real microseconds, the only real guarantee is that the number never goes\n  // down during a single run.\n  const TimeDelta as_time_delta = time_ticks - TimeTicks();\n  return os << as_time_delta.InMicroseconds() << \" bogo-microseconds\";\n}\n\n// ThreadTicks ----------------------------------------------------------------\n\nstd::ostream& operator<<(std::ostream& os, ThreadTicks thread_ticks) {\n  const TimeDelta as_time_delta = thread_ticks - ThreadTicks();\n  return os << as_time_delta.InMicroseconds() << \" bogo-thread-microseconds\";\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/time/time.h",
    "content": "// Copyright 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n// Followings are modified from chromium/base/time/time.h\n// * Unix epoch is being used to represent Time class instead of Windows epoch.\n// * Time is implemented using absl::Time inside.\n\n// `Time` represents an absolute point in coordinated universal time (UTC),\n// internally represented as microseconds (s/1,000,000) since the Unix epoch\n// (1970-01-01 00:00:00 UTC). System-dependent clock interface routines are\n// defined in time_PLATFORM.cc. Note that values for `Time` may skew and jump\n// around as the operating system makes adjustments to synchronize (e.g., with\n// NTP servers). Thus, client code that uses the `Time` class must account for\n// this.\n//\n// `TimeDelta` represents a duration of time, internally represented in\n// microseconds.\n//\n// `TimeTicks` and `ThreadTicks` represent an abstract time that is most of the\n// time incrementing, for use in measuring time durations. Internally, they are\n// represented in microseconds. They cannot be converted to a human-readable\n// time, but are guaranteed not to decrease (unlike the `Time` class). Note\n// that `TimeTicks` may \"stand still\" (e.g., if the computer is suspended), and\n// `ThreadTicks` will \"stand still\" whenever the thread has been de-scheduled\n// by the operating system.\n//\n// All time classes are copyable, assignable, and occupy 64 bits per instance.\n// Prefer to pass them by value, e.g.:\n//\n//   void MyFunction(TimeDelta arg);\n//\n// All time classes support `operator<<` with logging streams, e.g. `LOG(INFO)`.\n//\n// Example use cases for different time classes:\n//\n//   Time:        Interpreting the wall-clock time provided by a remote system.\n//                Detecting whether cached resources have expired. Providing the\n//                user with a display of the current date and time. Determining\n//                the amount of time between events across re-boots of the\n//                machine.\n//\n//   TimeTicks:   Tracking the amount of time a task runs. Executing delayed\n//                tasks at the right time. Computing presentation timestamps.\n//                Synchronizing audio and video using TimeTicks as a common\n//                reference clock (lip-sync). Measuring network round-trip\n//                latency.\n//\n//   ThreadTicks: Benchmarking how long the current thread has been doing actual\n//                work.\n//\n// Serialization:\n//\n// - Time: use `FromDeltaSinceUnixEpoch()`/`ToDeltaSinceUnixEpoch()`.\n// - TimeDelta: use `Microseconds()`/`InMicroseconds()`.\n//\n// `TimeTicks` and `ThreadTicks` do not have a stable origin; serialization for\n// the purpose of persistence is not supported.\n\n#ifndef TACHYON_BASE_TIME_TIME_H_\n#define TACHYON_BASE_TIME_TIME_H_\n\n#include <stdint.h>\n#include <time.h>\n\n#include <chrono>\n#include <iosfwd>\n#include <limits>\n#include <ostream>\n\n#include \"absl/time/time.h\"\n#include \"gtest/gtest_prod.h\"\n\n#include \"tachyon/base/compiler_specific.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/numerics/clamped_math.h\"\n#include \"tachyon/build/build_config.h\"\n#include \"tachyon/export.h\"\n\n#if BUILDFLAG(IS_APPLE)\n#include <CoreFoundation/CoreFoundation.h>\n#include <mach/mach_time.h>\n\n#include \"tachyon/base/time/time_buildflags.h\"\n// Avoid Mac system header macro leak.\n#undef TYPE_BOOL\n#endif\n\n#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)\n#include <sys/time.h>\n#include <unistd.h>\n#endif\n\nnamespace tachyon::base {\n\nclass TimeDelta;\n\nnamespace time_internal {\n\ntemplate <typename Ratio>\nint64_t ToInt64(TimeDelta d, Ratio);\ninline int64_t ToInt64(TimeDelta d, std::nano);\ninline int64_t ToInt64(TimeDelta d, std::micro);\ninline int64_t ToInt64(TimeDelta d, std::milli);\ninline int64_t ToInt64(TimeDelta d, std::ratio<1>);\ninline int64_t ToInt64(TimeDelta d, std::ratio<60>);\ninline int64_t ToInt64(TimeDelta d, std::ratio<3600>);\n\ntemplate <std::intmax_t N>\nconstexpr TimeDelta FromInt64(int64_t v, std::ratio<1, N>);\nconstexpr TimeDelta FromInt64(int64_t v, std::ratio<60>);\nconstexpr TimeDelta FromInt64(int64_t v, std::ratio<3600>);\n\n}  // namespace time_internal\n\ntemplate <typename T>\nconstexpr TimeDelta Microseconds(T n);\n\n// TimeDelta ------------------------------------------------------------------\n\nclass TACHYON_EXPORT TimeDelta {\n public:\n  constexpr TimeDelta() = default;\n\n#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)\n  static TimeDelta FromTimeSpec(const timespec& ts);\n#endif\n#if BUILDFLAG(IS_APPLE)\n#if BUILDFLAG(ENABLE_MACH_ABSOLUTE_TIME_TICKS)\n  static TimeDelta FromMachTime(uint64_t mach_time);\n#endif  // BUILDFLAG(ENABLE_MACH_ABSOLUTE_TIME_TICKS)\n#endif  // BUILDFLAG(IS_APPLE)\n\n  template <typename T>\n  static TimeDelta FromChrono(T d);\n  static constexpr TimeDelta FromChronoHours(std::chrono::hours d);\n  static constexpr TimeDelta FromChronoMinutes(std::chrono::minutes d);\n  static constexpr TimeDelta FromChronoSeconds(std::chrono::seconds d);\n  static constexpr TimeDelta FromChronoMilliseconds(\n      std::chrono::milliseconds d);\n  static constexpr TimeDelta FromChronoMicroseconds(\n      std::chrono::microseconds d);\n  static constexpr TimeDelta FromChronoNanoseconds(std::chrono::nanoseconds d);\n\n  static TimeDelta FromAbsl(absl::Duration d);\n\n  // Converts an integer value representing TimeDelta to a class. This is used\n  // when deserializing a |TimeDelta| structure, using a value known to be\n  // compatible. It is not provided as a constructor because the integer type\n  // may be unclear from the perspective of a caller.\n  //\n  // DEPRECATED - Do not use in new code. http://crbug.com/634507\n  static constexpr TimeDelta FromInternalValue(int64_t delta) {\n    return TimeDelta(delta);\n  }\n\n  // Returns the maximum time delta, which should be greater than any reasonable\n  // time delta we might compare it to. If converted to double with ToDouble()\n  // it becomes an IEEE double infinity. Use FiniteMax() if you want a very\n  // large number that doesn't do this. TimeDelta math saturates at the end\n  // points so adding to TimeDelta::Max() leaves the value unchanged.\n  // Subtracting should leave the value unchanged but currently changes it\n  // TODO(https://crbug.com/869387).\n  static constexpr TimeDelta Max();\n\n  // Returns the minimum time delta, which should be less than than any\n  // reasonable time delta we might compare it to. For more details see the\n  // comments for Max().\n  static constexpr TimeDelta Min();\n\n  // Returns the maximum time delta which is not equivalent to infinity. Only\n  // subtracting a finite time delta from this time delta has a defined result.\n  static constexpr TimeDelta FiniteMax();\n\n  // Returns the minimum time delta which is not equivalent to -infinity. Only\n  // adding a finite time delta to this time delta has a defined result.\n  static constexpr TimeDelta FiniteMin();\n\n  // Returns the magnitude (absolute value) of this TimeDelta.\n  constexpr TimeDelta magnitude() const { return TimeDelta(delta_.Abs()); }\n\n  // Returns true if the time delta is a zero, positive or negative time delta.\n  constexpr bool is_zero() const { return delta_ == 0; }\n  constexpr bool is_positive() const { return delta_ > 0; }\n  constexpr bool is_negative() const { return delta_ < 0; }\n\n  // Returns true if the time delta is the maximum/minimum time delta.\n  constexpr bool is_max() const { return *this == Max(); }\n  constexpr bool is_min() const { return *this == Min(); }\n  constexpr bool is_inf() const { return is_min() || is_max(); }\n\n#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)\n  struct timespec ToTimeSpec() const;\n#endif\n  template <typename T>\n  T ToChrono() const;\n  std::chrono::hours ToChronoHours() const;\n  std::chrono::minutes ToChronoMinutes() const;\n  std::chrono::seconds ToChronoSeconds() const;\n  std::chrono::milliseconds ToChronoMilliseconds() const;\n  constexpr std::chrono::microseconds ToChronoMicroseconds() const {\n    return std::chrono::microseconds(delta_);\n  }\n  std::chrono::nanoseconds ToChronoNanoseconds() const;\n\n  constexpr absl::Duration ToAbsl() const {\n    return absl::Microseconds(static_cast<int64_t>(delta_));\n  }\n\n  // Returns the frequency in Hertz (cycles per second) that has a period of\n  // *this.\n  constexpr double ToHz() const;\n\n  // Returns the time delta in some unit. Minimum argument values return as\n  // -inf for doubles and min type values otherwise. Maximum ones are treated as\n  // +inf for doubles and max type values otherwise. Their results will produce\n  // an is_min() or is_max() TimeDelta. The InXYZF versions return a floating\n  // point value. The InXYZ versions return a truncated value (aka rounded\n  // towards zero, std::trunc() behavior). The InXYZFloored() versions round to\n  // lesser integers (std::floor() behavior). The XYZRoundedUp() versions round\n  // up to greater integers (std::ceil() behavior). WARNING: Floating point\n  // arithmetic is such that XXX(t.InXXXF()) may not precisely equal |t|.\n  // Hence, floating point values should not be used for storage.\n  int InDays() const;\n  int InDaysFloored() const;\n  constexpr int InHours() const;\n  constexpr int InMinutes() const;\n  constexpr double InSecondsF() const;\n  constexpr int64_t InSeconds() const;\n  int64_t InSecondsFloored() const;\n  double InMillisecondsF() const;\n  int64_t InMilliseconds() const;\n  int64_t InMillisecondsRoundedUp() const;\n  constexpr int64_t InMicroseconds() const { return delta_; }\n  double InMicrosecondsF() const;\n  constexpr int64_t InNanoseconds() const;\n\n  // Computations with other deltas.\n  constexpr TimeDelta operator+(TimeDelta other) const;\n  constexpr TimeDelta operator-(TimeDelta other) const;\n\n  constexpr TimeDelta& operator+=(TimeDelta other) {\n    return *this = (*this + other);\n  }\n  constexpr TimeDelta& operator-=(TimeDelta other) {\n    return *this = (*this - other);\n  }\n  constexpr TimeDelta operator-() const {\n    if (!is_inf()) return TimeDelta(-delta_);\n    return (delta_ < 0) ? Max() : Min();\n  }\n\n  // Computations with numeric types.\n  template <typename T>\n  constexpr TimeDelta operator*(T a) const {\n    return TimeDelta(int64_t{delta_ * a});\n  }\n  template <typename T>\n  constexpr TimeDelta operator/(T a) const {\n    return TimeDelta(int64_t{delta_ / a});\n  }\n  template <typename T>\n  constexpr TimeDelta& operator*=(T a) {\n    return *this = (*this * a);\n  }\n  template <typename T>\n  constexpr TimeDelta& operator/=(T a) {\n    return *this = (*this / a);\n  }\n\n  // This does floating-point division. For an integer result, either call\n  // IntDiv(), or (possibly clearer) use this operator with\n  // tachyon::Clamp{Ceil,Floor,Round}() or tachyon::saturated_cast() (for\n  // truncation). Note that converting to double here drops precision to 53\n  // bits.\n  constexpr double operator/(TimeDelta a) const {\n    // 0/0 and inf/inf (any combination of positive and negative) are invalid\n    // (they are almost certainly not intentional, and result in NaN, which\n    // turns into 0 if clamped to an integer; this makes introducing subtle bugs\n    // too easy).\n    CHECK(!is_zero() || !a.is_zero());\n    CHECK(!is_inf() || !a.is_inf());\n\n    return ToDouble() / a.ToDouble();\n  }\n  constexpr int64_t IntDiv(TimeDelta a) const {\n    if (!is_inf() && !a.is_zero()) return int64_t{delta_ / a.delta_};\n\n    // For consistency, use the same edge case CHECKs and behavior as the code\n    // above.\n    CHECK(!is_zero() || !a.is_zero());\n    CHECK(!is_inf() || !a.is_inf());\n    return ((delta_ < 0) == (a.delta_ < 0))\n               ? std::numeric_limits<int64_t>::max()\n               : std::numeric_limits<int64_t>::min();\n  }\n\n  constexpr TimeDelta operator%(TimeDelta a) const {\n    return TimeDelta(\n        (is_inf() || a.is_zero() || a.is_inf()) ? delta_ : (delta_ % a.delta_));\n  }\n  constexpr TimeDelta& operator%=(TimeDelta other) {\n    return *this = (*this % other);\n  }\n\n  // Comparison operators.\n  constexpr bool operator==(TimeDelta other) const {\n    return delta_ == other.delta_;\n  }\n  constexpr bool operator!=(TimeDelta other) const {\n    return delta_ != other.delta_;\n  }\n  constexpr bool operator<(TimeDelta other) const {\n    return delta_ < other.delta_;\n  }\n  constexpr bool operator<=(TimeDelta other) const {\n    return delta_ <= other.delta_;\n  }\n  constexpr bool operator>(TimeDelta other) const {\n    return delta_ > other.delta_;\n  }\n  constexpr bool operator>=(TimeDelta other) const {\n    return delta_ >= other.delta_;\n  }\n\n  // Returns this delta, ceiled/floored/rounded-away-from-zero to the nearest\n  // multiple of |interval|.\n  TimeDelta CeilToMultiple(TimeDelta interval) const;\n  TimeDelta FloorToMultiple(TimeDelta interval) const;\n  TimeDelta RoundToMultiple(TimeDelta interval) const;\n\n private:\n  // Constructs a delta given the duration in microseconds. This is private\n  // to avoid confusion by callers with an integer constructor. Use\n  // tachyon::base::Seconds, tachyon::base::Milliseconds, etc. instead.\n  constexpr explicit TimeDelta(int64_t delta_us) : delta_(delta_us) {}\n  constexpr explicit TimeDelta(ClampedNumeric<int64_t> delta_us)\n      : delta_(delta_us) {}\n\n  // Returns a double representation of this TimeDelta's tick count.  In\n  // particular, Max()/Min() are converted to +/-infinity.\n  constexpr double ToDouble() const {\n    if (!is_inf()) return static_cast<double>(delta_);\n    return (delta_ < 0) ? -std::numeric_limits<double>::infinity()\n                        : std::numeric_limits<double>::infinity();\n  }\n\n  template <typename Ratio>\n  friend int64_t time_internal::ToInt64(TimeDelta d, Ratio);\n  template <std::intmax_t N>\n  friend constexpr TimeDelta time_internal::FromInt64(int64_t v,\n                                                      std::ratio<1, N>);\n  friend constexpr TimeDelta time_internal::FromInt64(int64_t v,\n                                                      std::ratio<60>);\n  friend constexpr TimeDelta time_internal::FromInt64(int64_t v,\n                                                      std::ratio<3600>);\n\n  // Delta in microseconds.\n  ClampedNumeric<int64_t> delta_ = 0;\n};\n\nconstexpr TimeDelta TimeDelta::operator+(TimeDelta other) const {\n  if (!other.is_inf()) return TimeDelta(delta_ + other.delta_);\n\n  // Additions involving two infinities are only valid if signs match.\n  CHECK(!is_inf() || (delta_ == other.delta_));\n  return other;\n}\n\nconstexpr TimeDelta TimeDelta::operator-(TimeDelta other) const {\n  if (!other.is_inf()) return TimeDelta(delta_ - other.delta_);\n\n  // Subtractions involving two infinities are only valid if signs differ.\n  CHECK_NE(int64_t{delta_}, int64_t{other.delta_});\n  return (other.delta_ < 0) ? Max() : Min();\n}\n\ntemplate <typename T>\nconstexpr TimeDelta operator*(T a, TimeDelta td) {\n  return td * a;\n}\n\n// For logging use only.\nTACHYON_EXPORT std::ostream& operator<<(std::ostream& os, TimeDelta time_delta);\n\nnamespace time_internal {\n\ntemplate <typename Ratio>\nint64_t ToInt64(TimeDelta d, Ratio) {\n  // Note: This may be used on MSVC, which may have a system_clock period of\n  // std::ratio<1, 10 * 1000 * 1000>\n  return (d * static_cast<double>(Ratio::den) / Ratio::num).InSeconds();\n}\n// Fastpath implementations for the 6 common duration units.\nint64_t ToInt64(TimeDelta d, std::nano) { return d.InNanoseconds(); }\nint64_t ToInt64(TimeDelta d, std::micro) { return d.InMicroseconds(); }\nint64_t ToInt64(TimeDelta d, std::milli) { return d.InMilliseconds(); }\nint64_t ToInt64(TimeDelta d, std::ratio<1>) { return d.InSeconds(); }\nint64_t ToInt64(TimeDelta d, std::ratio<60>) { return d.InMinutes(); }\nint64_t ToInt64(TimeDelta d, std::ratio<3600>) { return d.InHours(); }\n\n// TimeBase--------------------------------------------------------------------\n\n// Do not reference the time_internal::TimeBase template class directly.  Please\n// use one of the time subclasses instead, and only reference the public\n// TimeBase members via those classes.\n\n// Provides value storage and comparison/math operations common to all time\n// classes. Each subclass provides for strong type-checking to ensure\n// semantically meaningful comparison/math of time values from the same clock\n// source or timeline.\ntemplate <class TimeClass>\nclass TimeBase {\n public:\n  static constexpr int64_t kHoursPerDay = 24;\n  static constexpr int64_t kSecondsPerMinute = 60;\n  static constexpr int64_t kMinutesPerHour = 60;\n  static constexpr int64_t kSecondsPerHour =\n      kSecondsPerMinute * kMinutesPerHour;\n  static constexpr int64_t kMillisecondsPerSecond = 1000;\n  static constexpr int64_t kMillisecondsPerDay =\n      kMillisecondsPerSecond * kSecondsPerHour * kHoursPerDay;\n  static constexpr int64_t kMicrosecondsPerMillisecond = 1000;\n  static constexpr int64_t kMicrosecondsPerSecond =\n      kMicrosecondsPerMillisecond * kMillisecondsPerSecond;\n  static constexpr int64_t kMicrosecondsPerMinute =\n      kMicrosecondsPerSecond * kSecondsPerMinute;\n  static constexpr int64_t kMicrosecondsPerHour =\n      kMicrosecondsPerMinute * kMinutesPerHour;\n  static constexpr int64_t kMicrosecondsPerDay =\n      kMicrosecondsPerHour * kHoursPerDay;\n  static constexpr int64_t kMicrosecondsPerWeek = kMicrosecondsPerDay * 7;\n  static constexpr int64_t kNanosecondsPerMicrosecond = 1000;\n  static constexpr int64_t kNanosecondsPerSecond =\n      kNanosecondsPerMicrosecond * kMicrosecondsPerSecond;\n\n  // TODO(https://crbug.com/1392437): Remove concept of \"null\" from base::Time.\n  //\n  // Warning: Be careful when writing code that performs math on time values,\n  // since it's possible to produce a valid \"zero\" result that should not be\n  // interpreted as a \"null\" value. If you find yourself using this method or\n  // the zero-arg default constructor, please consider using an optional to\n  // express the null state.\n  //\n  // Returns true if this object has not been initialized (probably).\n  constexpr bool is_null() const { return us_ == 0; }\n\n  // Returns true if this object represents the maximum/minimum time.\n  constexpr bool is_max() const { return *this == Max(); }\n  constexpr bool is_min() const { return *this == Min(); }\n  constexpr bool is_inf() const { return is_min() || is_max(); }\n\n  // Returns the maximum/minimum times, which should be greater/less than than\n  // any reasonable time with which we might compare it.\n  static constexpr TimeClass Max() {\n    return TimeClass(std::numeric_limits<int64_t>::max());\n  }\n\n  static constexpr TimeClass Min() {\n    return TimeClass(std::numeric_limits<int64_t>::min());\n  }\n\n  // The amount of time since the origin (or \"zero\") point. This is a syntactic\n  // convenience to aid in code readability, mainly for debugging/testing use\n  // cases.\n  //\n  // Warning: While the Time subclass has a fixed origin point, the origin for\n  // the other subclasses can vary each time the application is restarted.\n  constexpr TimeDelta since_origin() const;\n\n  // Compute the difference between two times.\n#if !defined(__aarch64__) && BUILDFLAG(IS_ANDROID)\n  NOINLINE  // https://crbug.com/1369775\n#endif\n      constexpr TimeDelta\n      operator-(const TimeBase<TimeClass>& other) const;\n\n  // Return a new time modified by some delta.\n  constexpr TimeClass operator+(TimeDelta delta) const;\n  constexpr TimeClass operator-(TimeDelta delta) const;\n\n  // Modify by some time delta.\n  constexpr TimeClass& operator+=(TimeDelta delta) {\n    return static_cast<TimeClass&>(*this = (*this + delta));\n  }\n  constexpr TimeClass& operator-=(TimeDelta delta) {\n    return static_cast<TimeClass&>(*this = (*this - delta));\n  }\n\n  // Comparison operators\n  constexpr bool operator==(const TimeBase<TimeClass>& other) const {\n    return us_ == other.us_;\n  }\n  constexpr bool operator!=(const TimeBase<TimeClass>& other) const {\n    return us_ != other.us_;\n  }\n  constexpr bool operator<(const TimeBase<TimeClass>& other) const {\n    return us_ < other.us_;\n  }\n  constexpr bool operator<=(const TimeBase<TimeClass>& other) const {\n    return us_ <= other.us_;\n  }\n  constexpr bool operator>(const TimeBase<TimeClass>& other) const {\n    return us_ > other.us_;\n  }\n  constexpr bool operator>=(const TimeBase<TimeClass>& other) const {\n    return us_ >= other.us_;\n  }\n\n protected:\n  constexpr explicit TimeBase(int64_t us) : us_(us) {}\n\n  // Time value in a microsecond timebase.\n  ClampedNumeric<int64_t> us_;\n};\n\n}  // namespace time_internal\n\ntemplate <class TimeClass>\ninline constexpr TimeClass operator+(TimeDelta delta, TimeClass t) {\n  return t + delta;\n}\n\n// Time -----------------------------------------------------------------------\n\n// Represents a wall clock time in UTC. Values are not guaranteed to be\n// monotonically non-decreasing and are subject to large amounts of skew.\n\n// If you measure time with <chrono> then it looks like below.\n//\n// #include <chrono>\n// #include <ratio>\n//\n// std::chrono::system_clock::time_point t1 = std::chrono::system_clock::now();\n// std::chrono::system_clock::time_point t2 = std::chrono::system_clock::now();\n// std::chrono::duration<double, std::micro> delta = t2 - t1;\n// double us = delta.count();\n//\n// Using Time looks like below which is more simple.\n//\n// #include \"tachyon/base/time/time.h\"\n//\n// tachyon::base::Time t1 = tachyon::base::Time::Now();\n// tachyon::base::Time t2 = tachyon::base::Time::Now();\n// tachyon::base::TimeDelta delta = t2 -t1;\n// double us = delta.InMicrosecondsF();\nclass TACHYON_EXPORT Time : public time_internal::TimeBase<Time> {\n public:\n  constexpr Time() : TimeBase(0) {}\n\n  // Returns the current time. Watch out, the system might adjust its clock\n  // in which case time will actually go backwards. We don't guarantee that\n  // times are increasing, or that two calls to Now() won't be the same.\n  // You can think of it as std::chrono::system_clock::now(). It delegates\n  // to call absl::Now().\n  static Time Now();\n\n  // Converts to/from TimeDeltas relative to the Unix epoch (1970-01-01\n  // 00:00:00 UTC).\n  //\n  //   // Serialization:\n  //   tachyon::base::Time last_updated = ...;\n  //   SaveToDatabase(last_updated.ToDeltaSinceUnixEpoch().InMicroseconds());\n  //\n  //   // Deserialization:\n  //   tachyon::base::Time last_updated =\n  //   tachyon::base::Time::FromDeltaSinceUnixEpoch(\n  //       tachyon::base::Microseconds(LoadFromDatabase()));\n  static constexpr Time FromDeltaSinceUnixEpoch(TimeDelta delta) {\n    return Time(delta.InMicroseconds());\n  }\n  constexpr TimeDelta ToDeltaSinceUnixEpoch() const;\n\n  // Converts to/from time_t in UTC and a Time class.\n  static constexpr Time FromTimeT(time_t tt);\n  time_t ToTimeT() const;\n\n  // Converts time to/from a double which is the number of seconds since epoch\n  // (Jan 1, 1970).  Webkit uses this format to represent time.\n  // Because WebKit initializes double time value to 0 to indicate \"not\n  // initialized\", we map it to empty Time object that also means \"not\n  // initialized\".\n  static Time FromDoubleT(double dt);\n  double ToDoubleT() const;\n\n#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)\n  // Converts the timespec structure to time. MacOS X 10.8.3 (and tentatively,\n  // earlier versions) will have the |ts|'s tv_nsec component zeroed out,\n  // having a 1 second resolution, which agrees with\n  // https://developer.apple.com/legacy/library/#technotes/tn/tn1150.html#HFSPlusDates.\n  static Time FromTimeSpec(const timespec& ts);\n\n  static Time FromTimeVal(struct timeval t);\n  struct timeval ToTimeVal() const;\n#endif\n\n#if BUILDFLAG(IS_APPLE)\n  static Time FromCFAbsoluteTime(CFAbsoluteTime t);\n  CFAbsoluteTime ToCFAbsoluteTime() const;\n#if defined(__OBJC__)\n  static Time FromNSDate(NSDate* date);\n  NSDate* ToNSDate() const;\n#endif\n#endif\n\n  static Time FromAbsl(absl::Time time);\n  absl::Time ToAbsl() const;\n\n  static Time FromChrono(std::chrono::system_clock::time_point tp);\n  std::chrono::system_clock::time_point ToChrono() const;\n\n private:\n  friend class time_internal::TimeBase<Time>;\n\n  // NOTE: chromium's time is calculated from windows FILETIME epoch\n  // (1601-01-01 00:00:00 UTC). But tachyon currently runs on POSIX,\n  // so we calculate from UNIX epoch (1970-01-01 00:00:00 UTC).\n  constexpr explicit Time(int64_t microseconds_since_unix_epoch)\n      : TimeBase(microseconds_since_unix_epoch) {}\n};\n\n// Factory methods that return a TimeDelta of the given unit.\n// WARNING: Floating point arithmetic is such that XXX(t.InXXXF()) may not\n// precisely equal |t|. Hence, floating point values should not be used for\n// storage.\n\ntemplate <typename T>\nconstexpr TimeDelta Days(T n) {\n  return TimeDelta::FromInternalValue(MakeClampedNum(n) *\n                                      Time::kMicrosecondsPerDay);\n}\ntemplate <typename T>\nconstexpr TimeDelta Hours(T n) {\n  return TimeDelta::FromInternalValue(MakeClampedNum(n) *\n                                      Time::kMicrosecondsPerHour);\n}\ntemplate <typename T>\nconstexpr TimeDelta Minutes(T n) {\n  return TimeDelta::FromInternalValue(MakeClampedNum(n) *\n                                      Time::kMicrosecondsPerMinute);\n}\ntemplate <typename T>\nconstexpr TimeDelta Seconds(T n) {\n  return TimeDelta::FromInternalValue(MakeClampedNum(n) *\n                                      Time::kMicrosecondsPerSecond);\n}\ntemplate <typename T>\nconstexpr TimeDelta Milliseconds(T n) {\n  return TimeDelta::FromInternalValue(MakeClampedNum(n) *\n                                      Time::kMicrosecondsPerMillisecond);\n}\ntemplate <typename T>\nconstexpr TimeDelta Microseconds(T n) {\n  return TimeDelta::FromInternalValue(MakeClampedNum(n));\n}\ntemplate <typename T>\nconstexpr TimeDelta Nanoseconds(T n) {\n  return TimeDelta::FromInternalValue(MakeClampedNum(n) /\n                                      Time::kNanosecondsPerMicrosecond);\n}\ntemplate <typename T>\nconstexpr TimeDelta Hertz(T n) {\n  return n ? TimeDelta::FromInternalValue(Time::kMicrosecondsPerSecond /\n                                          MakeClampedNum(n))\n           : TimeDelta::Max();\n}\n\n// TimeDelta functions that must appear below the declarations of Time/TimeDelta\n\nconstexpr double TimeDelta::ToHz() const { return Seconds(1) / *this; }\n\nnamespace time_internal {\n\ntemplate <std::intmax_t N>\nconstexpr TimeDelta FromInt64(int64_t v, std::ratio<1, N>) {\n  static_assert(0 < N && N <= 1000 * 1000 * 1000, \"Unsupported ratio\");\n  return TimeDelta::FromInternalValue(saturated_cast<int64_t>(\n      static_cast<double>(v) / N * Time::kMicrosecondsPerSecond));\n}\n\nconstexpr TimeDelta FromInt64(int64_t v, std::ratio<60>) {\n  return TimeDelta::FromInternalValue(\n      int64_t{ClampMul(v, Time::kMicrosecondsPerMinute)});\n}\n\nconstexpr TimeDelta FromInt64(int64_t v, std::ratio<3600>) {\n  return TimeDelta::FromInternalValue(\n      int64_t{ClampMul(v, Time::kMicrosecondsPerHour)});\n}\n\n}  // namespace time_internal\n\n// taken and modified from absl::FromChrono().\n// static\ntemplate <typename T>\nTimeDelta TimeDelta::FromChrono(T d) {\n  using Rep = typename T::rep;\n  using Period = typename T::period;\n  static_assert(absl::time_internal::IsValidRep64<Rep>(0),\n                \"duration::rep is invalid\");\n  return time_internal::FromInt64(int64_t{d.count()}, Period{});\n}\n\n// static\nconstexpr TimeDelta TimeDelta::FromChronoHours(std::chrono::hours d) {\n  return Hours(d.count());\n}\n\n// static\nconstexpr TimeDelta TimeDelta::FromChronoMinutes(std::chrono::minutes d) {\n  return Minutes(d.count());\n}\n\n// static\nconstexpr TimeDelta TimeDelta::FromChronoSeconds(std::chrono::seconds d) {\n  return Seconds(d.count());\n}\n\n// static\nconstexpr TimeDelta TimeDelta::FromChronoMilliseconds(\n    std::chrono::milliseconds d) {\n  return Milliseconds(d.count());\n}\n\n// static\nconstexpr TimeDelta TimeDelta::FromChronoMicroseconds(\n    std::chrono::microseconds d) {\n  return Microseconds(d.count());\n}\n\n// static\nconstexpr TimeDelta TimeDelta::FromChronoNanoseconds(\n    std::chrono::nanoseconds d) {\n  return Nanoseconds(d.count());\n}\n\n// taken and modified from absl::time_internal::ToChronoDuration().\ntemplate <typename T>\nT TimeDelta::ToChrono() const {\n  using Rep = typename T::rep;\n  using Period = typename T::period;\n  static_assert(absl::time_internal::IsValidRep64<Rep>(0),\n                \"duration::rep is invalid\");\n  const auto v = time_internal::ToInt64(*this, Period{});\n  if (v > (std::numeric_limits<Rep>::max)()) return (T::max)();\n  if (v < (std::numeric_limits<Rep>::min)()) return (T::min)();\n  return T{v};\n}\n\nconstexpr int TimeDelta::InHours() const {\n  // saturated_cast<> is necessary since very large (but still less than\n  // min/max) deltas would result in overflow.\n  return saturated_cast<int>(delta_ / Time::kMicrosecondsPerHour);\n}\n\nconstexpr int TimeDelta::InMinutes() const {\n  // saturated_cast<> is necessary since very large (but still less than\n  // min/max) deltas would result in overflow.\n  return saturated_cast<int>(delta_ / Time::kMicrosecondsPerMinute);\n}\n\nconstexpr double TimeDelta::InSecondsF() const {\n  if (!is_inf())\n    return static_cast<double>(delta_) / Time::kMicrosecondsPerSecond;\n  return (delta_ < 0) ? -std::numeric_limits<double>::infinity()\n                      : std::numeric_limits<double>::infinity();\n}\n\nconstexpr int64_t TimeDelta::InSeconds() const {\n  return is_inf() ? delta_ : (delta_ / Time::kMicrosecondsPerSecond);\n}\n\nconstexpr int64_t TimeDelta::InNanoseconds() const {\n  return ClampMul(delta_, Time::kNanosecondsPerMicrosecond);\n}\n\n// static\nconstexpr TimeDelta TimeDelta::Max() {\n  return TimeDelta(std::numeric_limits<int64_t>::max());\n}\n\n// static\nconstexpr TimeDelta TimeDelta::Min() {\n  return TimeDelta(std::numeric_limits<int64_t>::min());\n}\n\n// static\nconstexpr TimeDelta TimeDelta::FiniteMax() {\n  return TimeDelta(std::numeric_limits<int64_t>::max() - 1);\n}\n\n// static\nconstexpr TimeDelta TimeDelta::FiniteMin() {\n  return TimeDelta(std::numeric_limits<int64_t>::min() + 1);\n}\n\n// TimeBase functions that must appear below the declarations of Time/TimeDelta\nnamespace time_internal {\n\ntemplate <class TimeClass>\nconstexpr TimeDelta TimeBase<TimeClass>::since_origin() const {\n  return Microseconds(us_);\n}\n\ntemplate <class TimeClass>\nconstexpr TimeDelta TimeBase<TimeClass>::operator-(\n    const TimeBase<TimeClass>& other) const {\n  return Microseconds(us_ - other.us_);\n}\n\ntemplate <class TimeClass>\nconstexpr TimeClass TimeBase<TimeClass>::operator+(TimeDelta delta) const {\n  return TimeClass((Microseconds(us_) + delta).InMicroseconds());\n}\n\ntemplate <class TimeClass>\nconstexpr TimeClass TimeBase<TimeClass>::operator-(TimeDelta delta) const {\n  return TimeClass((Microseconds(us_) - delta).InMicroseconds());\n}\n\n}  // namespace time_internal\n\n// Time functions that must appear below the declarations of Time/TimeDelta\n\nconstexpr TimeDelta Time::ToDeltaSinceUnixEpoch() const {\n  return Microseconds(us_);\n}\n\n// static\nconstexpr Time Time::FromTimeT(time_t tt) {\n  if (tt == 0) return Time();  // Preserve 0 so we can tell it doesn't exist.\n  return (tt == std::numeric_limits<time_t>::max())\n             ? Max()\n             : Time::FromDeltaSinceUnixEpoch(Seconds(tt));\n}\n\n// For logging use only.\nTACHYON_EXPORT std::ostream& operator<<(std::ostream& os, Time time);\n\n// TimeTicks ------------------------------------------------------------------\n\n// Represents monotonically non-decreasing clock time.\n//\n// If you measure time with <chrono> then it looks like below.\n//\n// #include <chrono>\n// #include <ratio>\n//\n// std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now();\n// std::chrono::steady_clock::time_point t2 = std::chrono::steady_clock::now();\n// std::chrono::duration<double, std::micro> delta = t2 - t1;\n// double us = delta.count();\n//\n// Using Time looks like below which is more simple.\n//\n// #include \"tachyon/base/time/time.h\"\n//\n// tachyon::base::TimeTicks t1 = tachyon::base::TimeTicks::Now();\n// tachyon::base::TimeTicks t2 = tachyon::base::TimeTicks::Now();\n// tachyon::base::TimeDelta delta = t2 -t1;\n// double us = delta.InMicrosecondsF();\nclass TACHYON_EXPORT TimeTicks : public time_internal::TimeBase<TimeTicks> {\n public:\n  constexpr TimeTicks() : TimeBase(0) {}\n\n  // Platform-dependent tick count representing \"right now.\" When\n  // IsHighResolution() returns false, the resolution of the clock could be\n  // as coarse as ~15.6ms. Otherwise, the resolution should be no worse than one\n  // microsecond.\n  static TimeTicks Now();\n\n  // Returns true if the high resolution clock is working on this system and\n  // Now() will return high resolution values. Note that, on systems where the\n  // high resolution clock works but is deemed inefficient, the low resolution\n  // clock will be used instead.\n  [[nodiscard]] static bool IsHighResolution();\n\n  // Returns true if TimeTicks is consistent across processes, meaning that\n  // timestamps taken on different processes can be safely compared with one\n  // another. (Note that, even on platforms where this returns true, time values\n  // from different threads that are within one tick of each other must be\n  // considered to have an ambiguous ordering.)\n  [[nodiscard]] static bool IsConsistentAcrossProcesses();\n\n#if BUILDFLAG(IS_APPLE)\n#if BUILDFLAG(ENABLE_MACH_ABSOLUTE_TIME_TICKS)\n  static TimeTicks FromMachAbsoluteTime(uint64_t mach_absolute_time);\n\n  // Sets the current Mach timebase to `timebase`. Returns the old timebase.\n  static mach_timebase_info_data_t SetMachTimebaseInfoForTesting(\n      mach_timebase_info_data_t timebase);\n\n#endif  // BUILDFLAG(ENABLE_MACH_ABSOLUTE_TIME_TICKS)\n#endif  // BUILDFLAG(IS_APPLE)\n\n  static TimeTicks FromChrono(std::chrono::steady_clock::time_point tp);\n  std::chrono::steady_clock::time_point ToChrono() const;\n\n private:\n  friend class time_internal::TimeBase<TimeTicks>;\n\n  // Please use Now() to create a new object. This is for internal use\n  // and testing.\n  constexpr explicit TimeTicks(int64_t us) : TimeBase(us) {}\n};\n\n// For logging use only.\nTACHYON_EXPORT std::ostream& operator<<(std::ostream& os, TimeTicks time_ticks);\n\n// ThreadTicks ----------------------------------------------------------------\n\n// Represents a clock, specific to a particular thread, than runs only while the\n// thread is running.\n//\n// There's no way of using thread ticks by <chrono>.\n// Using Time looks like below which is more simple.\n//\n// #include \"tachyon/base/time/time.h\"\n//\n// tachyon::base::ThreadTicks t1 = tachyon::base::ThreadTicks::Now();\n// tachyon::base::ThreadTicks t2 = tachyon::base::ThreadTicks::Now();\n// tachyon::base::TimeDelta delta = t2 -t1;\n// double us = delta.InMicrosecondsF();\nclass TACHYON_EXPORT ThreadTicks : public time_internal::TimeBase<ThreadTicks> {\n public:\n  constexpr ThreadTicks() : TimeBase(0) {}\n\n  // Returns true if ThreadTicks::Now() is supported on this system.\n  [[nodiscard]] static bool IsSupported() {\n#if (defined(_POSIX_THREAD_CPUTIME) && (_POSIX_THREAD_CPUTIME >= 0)) || \\\n    BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_FUCHSIA)\n    return true;\n#elif BUILDFLAG(IS_WIN)\n    return IsSupportedWin();\n#else\n    return false;\n#endif\n  }\n\n  // Waits until the initialization is completed. Needs to be guarded with a\n  // call to IsSupported().\n  static void WaitUntilInitialized() {\n#if BUILDFLAG(IS_WIN)\n    WaitUntilInitializedWin();\n#endif\n  }\n\n  // Returns thread-specific CPU-time on systems that support this feature.\n  // Needs to be guarded with a call to IsSupported(). Use this timer\n  // to (approximately) measure how much time the calling thread spent doing\n  // actual work vs. being de-scheduled. May return bogus results if the thread\n  // migrates to another CPU between two calls. Returns an empty ThreadTicks\n  // object until the initialization is completed. If a clock reading is\n  // absolutely needed, call WaitUntilInitialized() before this method.\n  static ThreadTicks Now();\n\n private:\n  friend class time_internal::TimeBase<ThreadTicks>;\n\n  // Please use Now() to create a new object. This is for internal use\n  // and testing.\n  constexpr explicit ThreadTicks(int64_t us) : TimeBase(us) {}\n};\n\n// For logging use only.\nTACHYON_EXPORT std::ostream& operator<<(std::ostream& os,\n                                        ThreadTicks thread_ticks);\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_TIME_TIME_H_\n"
  },
  {
    "path": "tachyon/base/time/time_buildflags.bzl",
    "content": "load(\"@bazel_skylib//rules:common_settings.bzl\", \"BuildSettingInfo\")\nload(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\nload(\"//tachyon/build:buildflag.bzl\", \"attrs\", \"gen_buildflag_header_helper\")\n\nENABLE_MACH_ABSOLUTE_TIME_TICKS = \"enable_mach_absolute_time_ticks\"\n\ndef _gen_time_buildflag_header_impl(ctx):\n    names = [ENABLE_MACH_ABSOLUTE_TIME_TICKS]\n    values = [ctx.attr.enable_mach_absolute_time_ticks]\n\n    return gen_buildflag_header_helper(ctx, [\n        \"%s=%s\" % (pair[0].upper(), pair[1][BuildSettingInfo].value)\n        for pair in zip(names, values)\n    ])\n\n_gen_time_buildflag_header = rule(\n    implementation = _gen_time_buildflag_header_impl,\n    output_to_genfiles = True,\n    attrs = attrs() | {\n        ENABLE_MACH_ABSOLUTE_TIME_TICKS: attr.label(),\n    },\n)\n\ndef time_buildflag_header(name, enable_mach_absolute_time_ticks):\n    _gen_time_buildflag_header(\n        enable_mach_absolute_time_ticks = enable_mach_absolute_time_ticks,\n        name = \"gen_\" + name,\n        out = name + \".h\",\n    )\n\n    tachyon_cc_library(\n        name = name,\n        hdrs = [name + \".h\"],\n        visibility = [\"//visibility:public\"],\n        deps = [\"//tachyon/build:buildflag\"],\n    )\n"
  },
  {
    "path": "tachyon/base/time/time_conversion_posix.cc",
    "content": "// Copyright (c) 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include <time.h>\n\n#include \"tachyon/base/numerics/checked_math.h\"\n#include \"tachyon/base/time/time.h\"\n#include \"tachyon/build/build_config.h\"\n\nnamespace tachyon::base {\n\n// static\nTimeDelta TimeDelta::FromTimeSpec(const timespec& ts) {\n  return TimeDelta(ts.tv_sec * Time::kMicrosecondsPerSecond +\n                   ts.tv_nsec / Time::kNanosecondsPerMicrosecond);\n}\n\nstruct timespec TimeDelta::ToTimeSpec() const {\n  int64_t microseconds = InMicroseconds();\n  time_t seconds = 0;\n  if (microseconds >= Time::kMicrosecondsPerSecond) {\n    seconds = static_cast<time_t>(InSeconds());\n    microseconds -= seconds * Time::kMicrosecondsPerSecond;\n  }\n  struct timespec result = {\n      seconds,\n      static_cast<long>(microseconds * Time::kNanosecondsPerMicrosecond)};\n  return result;\n}\n\n// static\nTime Time::FromTimeVal(struct timeval t) {\n  DCHECK_LT(t.tv_usec, static_cast<int>(Time::kMicrosecondsPerSecond));\n  DCHECK_GE(t.tv_usec, 0);\n  if (t.tv_usec == 0 && t.tv_sec == 0) return Time();\n  if (t.tv_usec == static_cast<suseconds_t>(Time::kMicrosecondsPerSecond) - 1 &&\n      t.tv_sec == std::numeric_limits<time_t>::max())\n    return Max();\n  return Time((static_cast<int64_t>(t.tv_sec) * Time::kMicrosecondsPerSecond) +\n              t.tv_usec);\n}\n\nstruct timeval Time::ToTimeVal() const {\n  struct timeval result;\n  if (is_null()) {\n    result.tv_sec = 0;\n    result.tv_usec = 0;\n    return result;\n  }\n  if (is_max()) {\n    result.tv_sec = std::numeric_limits<time_t>::max();\n    result.tv_usec = static_cast<suseconds_t>(Time::kMicrosecondsPerSecond) - 1;\n    return result;\n  }\n  result.tv_sec = us_ / Time::kMicrosecondsPerSecond;\n  result.tv_usec = us_ % Time::kMicrosecondsPerSecond;\n  return result;\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/time/time_delta_flag.h",
    "content": "#ifndef TACHYON_BASE_TIME_TIME_DELTA_FLAG_H_\n#define TACHYON_BASE_TIME_TIME_DELTA_FLAG_H_\n\n#include <functional>\n#include <string>\n\n#include \"tachyon/base/flag/flag.h\"\n#include \"tachyon/base/numerics/safe_conversions.h\"\n#include \"tachyon/base/strings/string_number_conversions.h\"\n#include \"tachyon/base/strings/string_util.h\"\n#include \"tachyon/base/time/time.h\"\n\nnamespace tachyon::base {\n\ntemplate <>\nclass FlagValueTraits<TimeDelta> {\n public:\n  static bool ParseValue(std::string_view input, TimeDelta* value,\n                         std::string* reason) {\n    std::function<TimeDelta(int)> func_from_int;\n    std::function<TimeDelta(int64_t)> func_from_int64;\n    std::function<TimeDelta(double)> func_from_double;\n    if (ConsumeSuffix(&input, \"d\")) {\n      func_from_int = &Days<int>;\n    } else if (ConsumeSuffix(&input, \"h\")) {\n      func_from_int = &Hours<int>;\n    } else if (ConsumeSuffix(&input, \"m\")) {\n      func_from_int = &Minutes<int>;\n    } else if (ConsumeSuffix(&input, \"ms\")) {\n      func_from_int64 = &Milliseconds<int64_t>;\n      func_from_double = &Milliseconds<double>;\n    } else if (ConsumeSuffix(&input, \"us\")) {\n      func_from_int64 = &Microseconds<int64_t>;\n      func_from_double = &Microseconds<double>;\n    } else if (ConsumeSuffix(&input, \"ns\")) {\n      func_from_int64 = &Nanoseconds<int64_t>;\n      func_from_double = &Nanoseconds<double>;\n    } else if (ConsumeSuffix(&input, \"s\")) {\n      func_from_int64 = &Seconds<int64_t>;\n      func_from_double = &Seconds<double>;\n    } else {\n      *reason = \"no suffix!, please add suffix d, h, m, s, ms, us or ns\";\n      return false;\n    }\n\n    int64_t int64_value;\n    bool success_to_convert_int64 = StringToInt64(input, &int64_value);\n    if (func_from_int) {\n      if (success_to_convert_int64 &&\n          IsValueInRangeForNumericType<int>(int64_value)) {\n        *value = func_from_int(static_cast<int>(int64_value));\n        return true;\n      }\n      *reason = \"failed to convert to int\";\n      return false;\n    }\n    if (success_to_convert_int64) {\n      *value = func_from_int64(int64_value);\n      return true;\n    }\n    double double_value;\n    if (!StringToDouble(input, &double_value)) {\n      *reason = \"failed to convert to double\";\n      return false;\n    }\n    *value = func_from_double(double_value);\n    return true;\n  }\n};\n\ntypedef Flag<TimeDelta> TimeDeltaFlag;\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_TIME_TIME_DELTA_FLAG_H_\n"
  },
  {
    "path": "tachyon/base/time/time_delta_flag_unittest.cc",
    "content": "#include \"tachyon/base/time/time_delta_flag.h\"\n\n#include <limits>\n\n#include \"absl/strings/substitute.h\"\n#include \"gtest/gtest.h\"\n\nnamespace tachyon::base {\n\nTEST(TimeDeltaFlagTest, ParseValue) {\n  TimeDelta time_delta;\n  std::string reason;\n  TimeDeltaFlag time_delta_flag(&time_delta);\n  EXPECT_FALSE(time_delta_flag.ParseValue(\"\", &reason));\n  EXPECT_EQ(reason, \"no suffix!, please add suffix d, h, m, s, ms, us or ns\");\n\n  reason.clear();\n  EXPECT_FALSE(time_delta_flag.ParseValue(\"1yr\", &reason));\n  EXPECT_EQ(reason, \"no suffix!, please add suffix d, h, m, s, ms, us or ns\");\n\n  reason.clear();\n  EXPECT_FALSE(time_delta_flag.ParseValue(\"0.5d\", &reason));\n  EXPECT_EQ(reason, \"failed to convert to int\");\n\n  reason.clear();\n  EXPECT_FALSE(time_delta_flag.ParseValue(\n      absl::Substitute(\n          \"$0d\", static_cast<int64_t>(std::numeric_limits<int>::max()) + 1),\n      &reason));\n  EXPECT_EQ(reason, \"failed to convert to int\");\n\n  reason.clear();\n  EXPECT_FALSE(time_delta_flag.ParseValue(\"ams\", &reason));\n  EXPECT_EQ(reason, \"failed to convert to double\");\n\n  EXPECT_TRUE(time_delta_flag.ParseValue(\"1d\", &reason));\n  EXPECT_EQ(time_delta, Days(1));\n\n  EXPECT_TRUE(time_delta_flag.ParseValue(\"1h\", &reason));\n  EXPECT_EQ(time_delta, Hours(1));\n\n  EXPECT_TRUE(time_delta_flag.ParseValue(\"1m\", &reason));\n  EXPECT_EQ(time_delta, Minutes(1));\n\n  EXPECT_TRUE(time_delta_flag.ParseValue(\"1s\", &reason));\n  EXPECT_EQ(time_delta, Seconds(1));\n\n  EXPECT_TRUE(time_delta_flag.ParseValue(\"1.2s\", &reason));\n  EXPECT_EQ(time_delta, Seconds(1.2));\n\n  EXPECT_TRUE(time_delta_flag.ParseValue(\"1ms\", &reason));\n  EXPECT_EQ(time_delta, Milliseconds(1));\n\n  EXPECT_TRUE(time_delta_flag.ParseValue(\"1.2ms\", &reason));\n  EXPECT_EQ(time_delta, Milliseconds(1.2));\n\n  EXPECT_TRUE(time_delta_flag.ParseValue(\"1us\", &reason));\n  EXPECT_EQ(time_delta, Microseconds(1));\n\n  EXPECT_TRUE(time_delta_flag.ParseValue(\"1.2us\", &reason));\n  EXPECT_EQ(time_delta, Microseconds(1.2));\n\n  EXPECT_TRUE(time_delta_flag.ParseValue(\"1ns\", &reason));\n  EXPECT_EQ(time_delta, Nanoseconds(1));\n\n  EXPECT_TRUE(time_delta_flag.ParseValue(\"1.2ns\", &reason));\n  EXPECT_EQ(time_delta, Nanoseconds(1.2));\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/time/time_interval.cc",
    "content": "#include \"tachyon/base/time/time_interval.h\"\n\nnamespace tachyon::base {\n\nvoid TimeInterval::Reset() { last_time_ = TimeTicks::Now(); }\n\nTimeDelta TimeInterval::GetTimeDelta(bool update) {\n  TimeTicks now = TimeTicks::Now();\n  TimeDelta dt;\n  if (!last_time_.is_null()) {\n    dt = now - last_time_;\n  }\n  if (update) {\n    last_time_ = now;\n  }\n  return dt;\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/time/time_interval.h",
    "content": "#ifndef TACHYON_BASE_TIME_TIME_INTERVAL_H_\n#define TACHYON_BASE_TIME_TIME_INTERVAL_H_\n\n#include \"gtest/gtest_prod.h\"\n\n#include \"tachyon/base/time/time.h\"\n#include \"tachyon/export.h\"\n\nnamespace tachyon::base {\n\n// This is a convenience class for getting tachyon::base::TimeDelta between\n// events. A typical usecase is getting time delta between robot movements.\n// NOTE: This class doesn't guarantee thread safety.\n//\n// Example:\n//\n//   tachyon::base::TimeInterval ti(TimeTicks::Now());\n//   // or you can do like below.\n//   // tachyon::base::TimeInterval ti;\n//   // ti.Reset();\n//   while (true) {\n//     // heavy calculation\n//     tachyon::base::TimeDelta dt = ti.GetTimeDelta();\n//   }\n//\n//   // This is same with below.\n//   tachyon::base::TimeTicks last_time = TimeTicks::Now();\n//   while (true) {\n//     // heavy calculation\n//     tachyon::base::TimeTicks now = tachyon::base::TimeTicks::Now();\n//     tachyon::base::TimeDelta dt = now - last_time;\n//     last_time = now;\n//   }\nclass TACHYON_EXPORT TimeInterval {\n public:\n  constexpr TimeInterval() {}\n  explicit constexpr TimeInterval(TimeTicks last_time)\n      : last_time_(last_time) {}\n\n  void Reset();\n\n  // Returns TimeDelta from |last_time_|. For the first call, it might return a\n  // bogus value if |last_time_| is not given to this class. If |update| is not\n  // set, |last_time_| will not be updated.\n  TimeDelta GetTimeDelta(bool update = true);\n\n private:\n  FRIEND_TEST(TimeIntervalTest, GetTimeDelta);\n\n  TimeTicks last_time_;\n};\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_TIME_TIME_INTERVAL_H_\n"
  },
  {
    "path": "tachyon/base/time/time_interval_unittest.cc",
    "content": "#include \"tachyon/base/time/time_interval.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/threading/platform_thread.h\"\n\nnamespace tachyon::base {\n\nTEST(TimeIntervalTest, GetTimeDelta) {\n  TimeInterval ti;\n  EXPECT_EQ(ti.GetTimeDelta(), TimeDelta());\n  TimeTicks last_time = ti.last_time_;\n  PlatformThread::Sleep(Milliseconds(10));\n  EXPECT_GT(ti.GetTimeDelta(), TimeDelta());\n  EXPECT_GT(ti.last_time_, last_time);\n  last_time = ti.last_time_;\n  TimeDelta dt;\n  for (int i = 0; i < 3; ++i) {\n    PlatformThread::Sleep(Milliseconds(10));\n    TimeDelta dt_temp = ti.GetTimeDelta(false);\n    EXPECT_GT(dt_temp, dt);\n    EXPECT_EQ(ti.last_time_, last_time);\n    dt = dt_temp;\n  }\n\n  TimeInterval ti2(TimeTicks::Now());\n  last_time = ti2.last_time_;\n  PlatformThread::Sleep(Milliseconds(10));\n  EXPECT_GT(ti2.GetTimeDelta(), TimeDelta());\n  EXPECT_GT(ti2.last_time_, last_time);\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/time/time_mac.mm",
    "content": "// Copyright 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include <stddef.h>\n#include <stdint.h>\n#include <sys/time.h>\n#include <sys/types.h>\n#include <time.h>\n\n#import <Foundation/Foundation.h>\n#include <mach/mach.h>\n#include <mach/mach_time.h>\n#include <sys/sysctl.h>\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/mac/mach_logging.h\"\n#include \"tachyon/base/mac/scoped_cftyperef.h\"\n#include \"tachyon/base/mac/scoped_mach_port.h\"\n#include \"tachyon/base/numerics/safe_conversions.h\"\n#include \"tachyon/base/time/time.h\"\n// #include \"tachyon/base/time/time_override.h\"\n#include \"tachyon/build/build_config.h\"\n\n#if !BUILDFLAG(ENABLE_MACH_ABSOLUTE_TIME_TICKS)\n#include <errno.h>\n#include <time.h>\n\n#include \"tachyon/base/ios/ios_util.h\"\n#endif  // !BUILDFLAG(ENABLE_MACH_ABSOLUTE_TIME_TICKS)\n\n#if !defined(__has_feature) || !__has_feature(objc_arc)\n#error \"This file requires ARC support.\"\n#endif\n\nnamespace {\n\n#if BUILDFLAG(ENABLE_MACH_ABSOLUTE_TIME_TICKS)\n// Returns a pointer to the initialized Mach timebase info struct.\nmach_timebase_info_data_t* MachTimebaseInfo() {\n  static mach_timebase_info_data_t timebase_info = []() {\n    mach_timebase_info_data_t info;\n    kern_return_t kr = mach_timebase_info(&info);\n    MACH_DCHECK(kr == KERN_SUCCESS, kr) << \"mach_timebase_info\";\n    DCHECK(info.numer);\n    DCHECK(info.denom);\n    return info;\n  }();\n  return &timebase_info;\n}\n\nint64_t MachTimeToMicroseconds(uint64_t mach_time) {\n  // timebase_info gives us the conversion factor between absolute time tick\n  // units and nanoseconds.\n  mach_timebase_info_data_t* timebase_info = MachTimebaseInfo();\n\n  // Take the fast path when the conversion is 1:1. The result will for sure fit\n  // into an int_64 because we're going from nanoseconds to microseconds.\n  if (timebase_info->numer == timebase_info->denom) {\n    return static_cast<int64_t>(mach_time / tachyon::base::Time::kNanosecondsPerMicrosecond);\n  }\n\n  uint64_t microseconds = 0;\n  const uint64_t divisor = timebase_info->denom * tachyon::base::Time::kNanosecondsPerMicrosecond;\n\n  // Microseconds is mach_time * timebase.numer /\n  // (timebase.denom * kNanosecondsPerMicrosecond). Divide first to reduce\n  // the chance of overflow. Also stash the remainder right now, a likely\n  // byproduct of the division.\n  microseconds = mach_time / divisor;\n  const uint64_t mach_time_remainder = mach_time % divisor;\n\n  // Now multiply, keeping an eye out for overflow.\n  CHECK(!__builtin_umulll_overflow(microseconds, timebase_info->numer, &microseconds));\n\n  // By dividing first we lose precision. Regain it by adding back the\n  // microseconds from the remainder, with an eye out for overflow.\n  uint64_t least_significant_microseconds = (mach_time_remainder * timebase_info->numer) / divisor;\n  CHECK(!__builtin_uaddll_overflow(microseconds, least_significant_microseconds, &microseconds));\n\n  // Don't bother with the rollover handling that the Windows version does.\n  // The returned time in microseconds is enough for 292,277 years (starting\n  // from 2^63 because the returned int64_t is signed,\n  // 9223372036854775807 / (1e6 * 60 * 60 * 24 * 365.2425) = 292,277).\n  return tachyon::base::checked_cast<int64_t>(microseconds);\n}\n#endif  // BUILDFLAG(ENABLE_MACH_ABSOLUTE_TIME_TICKS)\n\n// Returns monotonically growing number of ticks in microseconds since some\n// unspecified starting point.\nint64_t ComputeCurrentTicks() {\n#if !BUILDFLAG(ENABLE_MACH_ABSOLUTE_TIME_TICKS)\n  struct timespec tp;\n  // clock_gettime() returns 0 on success and -1 on failure. Failure can only\n  // happen because of bad arguments (unsupported clock type or timespec pointer\n  // out of accessible address space). Here it is known that neither can happen\n  // since the timespec parameter is stack allocated right above and\n  // `CLOCK_MONOTONIC` is supported on all versions of iOS that Chrome is\n  // supported on.\n  int res = clock_gettime(CLOCK_MONOTONIC, &tp);\n  DCHECK_EQ(res, 0) << \"Failed clock_gettime, errno: \" << errno;\n\n  return (int64_t)tp.tv_sec * 1000000 + tp.tv_nsec / 1000;\n#else\n  // mach_absolute_time is it when it comes to ticks on the Mac.  Other calls\n  // with less precision (such as TickCount) just call through to\n  // mach_absolute_time.\n  return MachTimeToMicroseconds(mach_absolute_time());\n#endif  // !BUILDFLAG(ENABLE_MACH_ABSOLUTE_TIME_TICKS)\n}\n\nint64_t ComputeThreadTicks() {\n  // The pthreads library keeps a cached reference to the thread port, which\n  // does not have to be released like mach_thread_self() does.\n  mach_port_t thread_port = pthread_mach_thread_np(pthread_self());\n  if (thread_port == MACH_PORT_NULL) {\n    DLOG(ERROR) << \"Failed to get pthread_mach_thread_np()\";\n    return 0;\n  }\n\n  mach_msg_type_number_t thread_info_count = THREAD_BASIC_INFO_COUNT;\n  thread_basic_info_data_t thread_info_data;\n\n  kern_return_t kr =\n      thread_info(thread_port, THREAD_BASIC_INFO,\n                  reinterpret_cast<thread_info_t>(&thread_info_data), &thread_info_count);\n  MACH_DCHECK(kr == KERN_SUCCESS, kr) << \"thread_info\";\n\n  tachyon::base::CheckedNumeric<int64_t> absolute_micros(thread_info_data.user_time.seconds +\n                                                         thread_info_data.system_time.seconds);\n  absolute_micros *= tachyon::base::Time::kMicrosecondsPerSecond;\n  absolute_micros +=\n      (thread_info_data.user_time.microseconds + thread_info_data.system_time.microseconds);\n  return absolute_micros.ValueOrDie();\n}\n\n}  // namespace\n\nnamespace tachyon::base {\n\n// The Time routines in this file use Mach and CoreFoundation APIs, since the\n// POSIX definition of time_t in macOS wraps around after 2038--and\n// there are already cookie expiration dates, etc., past that time out in\n// the field.  Using CFDate prevents that problem, and using mach_absolute_time\n// for TimeTicks gives us nice high-resolution interval timing.\n\n// Time -----------------------------------------------------------------------\n\nnamespace subtle {\nTime TimeNowIgnoringOverride() { return Time::FromCFAbsoluteTime(CFAbsoluteTimeGetCurrent()); }\n\nTime TimeNowFromSystemTimeIgnoringOverride() {\n  // Just use TimeNowIgnoringOverride() because it returns the system time.\n  return TimeNowIgnoringOverride();\n}\n}  // namespace subtle\n\n// static\nTime Time::FromCFAbsoluteTime(CFAbsoluteTime t) {\n  static_assert(std::numeric_limits<CFAbsoluteTime>::has_infinity,\n                \"CFAbsoluteTime must have an infinity value\");\n  if (t == 0) return Time();  // Consider 0 as a null Time.\n  return (t == std::numeric_limits<CFAbsoluteTime>::infinity())\n             ? Max()\n             : (Time() + Seconds(double{t + kCFAbsoluteTimeIntervalSince1970}));\n}\n\nCFAbsoluteTime Time::ToCFAbsoluteTime() const {\n  static_assert(std::numeric_limits<CFAbsoluteTime>::has_infinity,\n                \"CFAbsoluteTime must have an infinity value\");\n  if (is_null()) return 0;  // Consider 0 as a null Time.\n  return is_max()\n             ? std::numeric_limits<CFAbsoluteTime>::infinity()\n             : (CFAbsoluteTime{(*this - Time()).InSecondsF()} - kCFAbsoluteTimeIntervalSince1970);\n}\n\n// static\nTime Time::FromNSDate(NSDate* date) {\n  DCHECK(date);\n  return FromCFAbsoluteTime(date.timeIntervalSinceReferenceDate);\n}\n\nNSDate* Time::ToNSDate() const {\n  return [NSDate dateWithTimeIntervalSinceReferenceDate:ToCFAbsoluteTime()];\n}\n\n// TimeDelta ------------------------------------------------------------------\n\n#if BUILDFLAG(ENABLE_MACH_ABSOLUTE_TIME_TICKS)\n// static\nTimeDelta TimeDelta::FromMachTime(uint64_t mach_time) {\n  return Microseconds(MachTimeToMicroseconds(mach_time));\n}\n#endif  // BUILDFLAG(ENABLE_MACH_ABSOLUTE_TIME_TICKS)\n\n// TimeTicks ------------------------------------------------------------------\n\n// static\nTimeTicks TimeTicks::Now() { return TimeTicks() + Microseconds(ComputeCurrentTicks()); }\n\n// static\nbool TimeTicks::IsHighResolution() { return true; }\n\n// static\nbool TimeTicks::IsConsistentAcrossProcesses() { return true; }\n\n#if BUILDFLAG(ENABLE_MACH_ABSOLUTE_TIME_TICKS)\n// static\nTimeTicks TimeTicks::FromMachAbsoluteTime(uint64_t mach_absolute_time) {\n  return TimeTicks(MachTimeToMicroseconds(mach_absolute_time));\n}\n\n// static\nmach_timebase_info_data_t TimeTicks::SetMachTimebaseInfoForTesting(\n    mach_timebase_info_data_t timebase) {\n  mach_timebase_info_data_t orig_timebase = *MachTimebaseInfo();\n\n  *MachTimebaseInfo() = timebase;\n\n  return orig_timebase;\n}\n\n#endif  // BUILDFLAG(ENABLE_MACH_ABSOLUTE_TIME_TICKS)\n\n// ThreadTicks ----------------------------------------------------------------\n\n// static\nThreadTicks ThreadTicks::Now() { return ThreadTicks() + Microseconds(ComputeThreadTicks()); }\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/time/time_mac_unittest.mm",
    "content": "// Copyright 2021 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n// clang-format off\n\n// #include \"tachyon/base/test/gtest_util.h\"\n#include \"tachyon/base/time/time.h\"\n\n#include \"gtest/gtest.h\"\n\n#if !defined(__has_feature) || !__has_feature(objc_arc)\n#error \"This file requires ARC support.\"\n#endif\n\nnamespace {\n\nclass ScopedTimebase {\n public:\n  ScopedTimebase(mach_timebase_info_data_t timebase) {\n    orig_timebase_ = tachyon::base::TimeTicks::SetMachTimebaseInfoForTesting(timebase);\n  }\n\n  ScopedTimebase(const ScopedTimebase&) = delete;\n\n  ScopedTimebase& operator=(const ScopedTimebase&) = delete;\n\n  ~ScopedTimebase() {\n    tachyon::base::TimeTicks::SetMachTimebaseInfoForTesting(orig_timebase_);\n  }\n\n private:\n  mach_timebase_info_data_t orig_timebase_;\n};\n\nmach_timebase_info_data_t kIntelTimebase = {1, 1};\n\n// A sample (not definitive) timebase for M1.\nmach_timebase_info_data_t kM1Timebase = {125, 3};\n\n}  // namespace\n\nnamespace tachyon::base {\nnamespace {\n\nbase::Time NoonOnDate(int year, int month, int day) {\n  /*\n  TODO(chokobole):\n  base::Time::Exploded exploded;\n  exploded.year = year;\n  exploded.month = month;\n  exploded.day_of_week = 0;  // Not correct, but FromExploded permits it\n  exploded.day_of_month = day;\n  exploded.hour = 12;\n  exploded.minute = 0;\n  exploded.second = 0;\n  exploded.millisecond = 0;\n  base::Time imploded;\n  CHECK(base::Time::FromUTCExploded(exploded, &imploded));\n  return imploded;\n  */\n  return base::Time();\n}\n\nvoid CheckRoundTrip(int y, int m, int d) {\n  base::Time original = NoonOnDate(y, m, d);\n  base::Time roundtrip = Time::FromNSDate(original.ToNSDate());\n  EXPECT_EQ(original, roundtrip);\n}\n\nTEST(TimeMacTest, RoundTripNSDate) {\n  CheckRoundTrip(1911, 12, 14);\n  CheckRoundTrip(1924, 9, 28);\n  CheckRoundTrip(1926, 5, 12);\n  CheckRoundTrip(1969, 7, 24);\n}\n\nTEST(TimeMacTest, MachTimeToMicrosecondsIntelTimebase) {\n  ScopedTimebase timebase(kIntelTimebase);\n\n  // Perform the conversion.\n  uint64_t kArbitraryTicks = 59090101000;\n  TimeDelta result = TimeDelta::FromMachTime(kArbitraryTicks);\n\n  // With Intel the output should be the input.\n  EXPECT_EQ(Nanoseconds(kArbitraryTicks), result);\n}\n\nTEST(TimeMacTest, MachTimeToMicrosecondsM1Timebase) {\n  ScopedTimebase timebase(kM1Timebase);\n\n  // Use a tick count that's divisible by 3.\n  const uint64_t kArbitraryTicks = 92738127000;\n  TimeDelta result = TimeDelta::FromMachTime(kArbitraryTicks);\n\n  const uint64_t kExpectedResult =\n      kArbitraryTicks * kM1Timebase.numer / kM1Timebase.denom;\n  EXPECT_EQ(Nanoseconds(kExpectedResult), result);\n}\n\n// Tests MachTimeToMicroseconds when\n// mach_timebase_info_data_t.numer and mach_timebase_info_data_t.denom\n// are equal.\nTEST(TimeMacTest, MachTimeToMicrosecondsEqualTimebaseMembers) {\n  // These members would produce overflow but don't because\n  // MachTimeToMicroseconds should skip the timebase conversion\n  // when they're equal.\n  ScopedTimebase timebase({UINT_MAX, UINT_MAX});\n\n  uint64_t kArbitraryTicks = 175920053729;\n  TimeDelta result = TimeDelta::FromMachTime(kArbitraryTicks);\n\n  // With a unity timebase the output should be the input.\n  EXPECT_EQ(Nanoseconds(kArbitraryTicks), result);\n}\n\nTEST(TimeMacTest, MachTimeToMicrosecondsOverflowDetection) {\n  const uint32_t kArbitraryNumer = 1234567;\n  ScopedTimebase timebase({kArbitraryNumer, 1});\n\n  /*\n  TODO(chokobole):\n  // Expect an overflow.\n  EXPECT_CHECK_DEATH(\n      TimeDelta::FromMachTime(std::numeric_limits<uint64_t>::max()));\n  */\n}\n\n// Tests that there's no overflow in MachTimeToMicroseconds even with\n// std::numeric_limits<uint64_t>::max() ticks on Intel.\nTEST(TimeMacTest, MachTimeToMicrosecondsNoOverflowIntel) {\n  ScopedTimebase timebase(kIntelTimebase);\n\n  // The incoming Mach time ticks are on the order of nanoseconds while the\n  // return result is microseconds. Even though we're passing in the largest\n  // tick count the result should be orders of magnitude smaller. On Intel the\n  // mapping from ticks to nanoseconds is 1:1 so we wouldn't ever expect an\n  // overflow when applying the timebase conversion.\n  TimeDelta::FromMachTime(std::numeric_limits<uint64_t>::max());\n}\n\n// Tests that there's no overflow in MachTimeToMicroseconds even with\n// std::numeric_limits<uint64_t>::max() ticks on M1.\nTEST(TimeMacTest, MachTimeToMicrosecondsNoOverflowM1) {\n  ScopedTimebase timebase(kM1Timebase);\n\n  // The incoming Mach time ticks are on the order of nanoseconds while the\n  // return result is microseconds. Even though we're passing in the largest\n  // tick count the result should be orders of magnitude smaller. Expect that\n  // FromMachTime(), when applying the timebase conversion, is smart enough to\n  // not multiply first and generate an overflow.\n  TimeDelta::FromMachTime(std::numeric_limits<uint64_t>::max());\n}\n\n// Tests that there's no underflow in MachTimeToMicroseconds on Intel.\nTEST(TimeMacTest, MachTimeToMicrosecondsNoUnderflowIntel) {\n  ScopedTimebase timebase(kIntelTimebase);\n\n  // On Intel the timebase conversion is 1:1, so min ticks is one microsecond\n  // worth of nanoseconds.\n  const uint64_t kMinimumTicks = base::Time::kNanosecondsPerMicrosecond;\n  const uint64_t kOneMicrosecond = 1;\n  EXPECT_EQ(kOneMicrosecond,\n            TimeDelta::FromMachTime(kMinimumTicks).InMicroseconds() * 1UL);\n\n  // If we have even one fewer tick (i.e. not enough ticks to constitute a full\n  // microsecond) the integer rounding should result in 0 microseconds.\n  const uint64_t kZeroMicroseconds = 0;\n  EXPECT_EQ(kZeroMicroseconds,\n            TimeDelta::FromMachTime(kMinimumTicks - 1).InMicroseconds() * 1UL);\n}\n\n// Tests that there's no underflow in MachTimeToMicroseconds for M1.\nTEST(TimeMacTest, MachTimeToMicrosecondsNoUnderflowM1) {\n  ScopedTimebase timebase(kM1Timebase);\n\n  // Microseconds is mach_time multiplied by kM1Timebase.numer /\n  // (kM1Timebase.denom * base::Time::kNanosecondsPerMicrosecond). Inverting\n  // that should be the minimum number of ticks to get a single microsecond in\n  // return. If we get zero it means an underflow in the conversion. For example\n  // if FromMachTime() first divides mach_time by kM1Timebase.denom *\n  // base::Time::kNanosecondsPerMicrosecond we'll get zero back.\n  const uint64_t kMinimumTicks =\n      (kM1Timebase.denom * base::Time::kNanosecondsPerMicrosecond) /\n      kM1Timebase.numer;\n  const uint64_t kOneMicrosecond = 1;\n  EXPECT_EQ(kOneMicrosecond,\n            TimeDelta::FromMachTime(kMinimumTicks).InMicroseconds() * 1UL);\n\n  // If we have even one fewer tick (i.e. not enough ticks to constitute a full\n  // microsecond) the integer rounding should result in 0 microseconds.\n  const uint64_t kZeroMicroseconds = 0;\n  EXPECT_EQ(kZeroMicroseconds,\n            TimeDelta::FromMachTime(kMinimumTicks - 1).InMicroseconds() * 1UL);\n}\n\n}  // namespace\n}  // namespace tachyon::base\n\n// clang-format on"
  },
  {
    "path": "tachyon/base/time/time_now_posix.cc",
    "content": "// Copyright (c) 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include <time.h>\n\n#include \"tachyon/base/numerics/checked_math.h\"\n#include \"tachyon/base/time/time.h\"\n#include \"tachyon/build/build_config.h\"\n\nnamespace tachyon::base {\n\nnamespace {\n\nint64_t ConvertTimespecToMicros(const struct timespec& ts) {\n  // On 32-bit systems, the calculation cannot overflow int64_t.\n  // 2**32 * 1000000 + 2**64 / 1000 < 2**63\n  if (sizeof(ts.tv_sec) <= 4 && sizeof(ts.tv_nsec) <= 8) {\n    int64_t result = ts.tv_sec;\n    result *= Time::kMicrosecondsPerSecond;\n    result += (ts.tv_nsec / Time::kNanosecondsPerMicrosecond);\n    return result;\n  }\n  CheckedNumeric<int64_t> result(ts.tv_sec);\n  result *= Time::kMicrosecondsPerSecond;\n  result += (ts.tv_nsec / Time::kNanosecondsPerMicrosecond);\n  return result.ValueOrDie();\n}\n\n// Helper function to get results from clock_gettime() and convert to a\n// microsecond timebase. Minimum requirement is MONOTONIC_CLOCK to be supported\n// on the system. FreeBSD 6 has CLOCK_MONOTONIC but defines\n// _POSIX_MONOTONIC_CLOCK to -1.\n#if (BUILDFLAG(IS_POSIX) && defined(_POSIX_MONOTONIC_CLOCK) && \\\n     _POSIX_MONOTONIC_CLOCK >= 0) ||                           \\\n    BUILDFLAG(IS_BSD) || BUILDFLAG(IS_ANDROID)\nint64_t ClockNow(clockid_t clk_id) {\n  struct timespec ts;\n  CHECK(clock_gettime(clk_id, &ts) == 0);\n  return ConvertTimespecToMicros(ts);\n}\n#else  // _POSIX_MONOTONIC_CLOCK\n#error No usable tick clock function on this platform.\n#endif  // _POSIX_MONOTONIC_CLOCK\n\n}  // namespace\n\n// static\nTimeTicks TimeTicks::Now() { return TimeTicks(ClockNow(CLOCK_MONOTONIC)); }\n\n// static\nThreadTicks ThreadTicks::Now() {\n  return ThreadTicks(ClockNow(CLOCK_THREAD_CPUTIME_ID));\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/time/time_stamp.cc",
    "content": "#include \"tachyon/base/time/time_stamp.h\"\n\nnamespace tachyon::base {\n\nTimeDelta TimeStamp::GetTimeDelta(bool update) {\n  TimeTicks now = TimeTicks::Now();\n  if (update && base_time_.is_null()) {\n    base_time_ = now;\n  }\n\n  return now - base_time_;\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/time/time_stamp.h",
    "content": "#ifndef TACHYON_BASE_TIME_TIME_STAMP_H_\n#define TACHYON_BASE_TIME_TIME_STAMP_H_\n\n#include \"gtest/gtest_prod.h\"\n\n#include \"tachyon/base/time/time.h\"\n#include \"tachyon/export.h\"\n\nnamespace tachyon::base {\n\n// This is a convenience class for getting tachyon::base::TimeDelta from a base\n// event. A typical usecase is getting timestamp for video frame. In this case,\n// Timestamp of Initial frame will be 0 and get increased as video get streamed.\n// Another typical usecase is getting timestamp to measure message delivery\n// latency between sender and receiver. In this case, you should keep in mind\n// that you need to call `GetTimeDelta(/*update=*/false)` so that it doesn't\n// update its internal state. NOTE: This class doesn't guarantee thread safety.\n//\n// Example:\n//\n//   tachyon::base::TimeStamp ts;\n//   while (true) {\n//     // heavy calculation\n//     tachyon::base::TimeDelta dt = ts.GetTimeDelta();\n//   }\n//\n//   // This is same with below.\n//   tachyon::base::TimeTicks base_time;\n//   while (true) {\n//     // heavy calculation\n//     tachyon::base::TimeDelta dt;\n//     if (base_time.is_null()) {\n//       // First dt is evaluated to zero on purpose.\n//       base_time = tachyon::base::TimeTicks::Now();\n//     } else {\n//       dt = tachyon::base::TimeTicks::Now() - base_time;\n//     }\n//   }\nclass TACHYON_EXPORT TimeStamp {\n public:\n  constexpr TimeStamp() {}\n\n  // Returns TimeDelta from |base_time_|. For the first call, it returns 0 if\n  // you set |update| is true. Otherwise it returns TimeDelta from\n  // TimeTicks::Now() regardless of |base_time_|.\n  TimeDelta GetTimeDelta(bool update = true);\n\n private:\n  FRIEND_TEST(TimeStampTest, GetTimeDelta);\n\n  TimeTicks base_time_;\n};\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_TIME_TIME_STAMP_H_\n"
  },
  {
    "path": "tachyon/base/time/time_stamp_unittest.cc",
    "content": "#include \"tachyon/base/time/time_stamp.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/threading/platform_thread.h\"\n\nnamespace tachyon::base {\n\nTEST(TimeStampTest, GetTimeDelta) {\n  TimeStamp ts;\n  TimeDelta dt = ts.GetTimeDelta();\n  EXPECT_EQ(dt, TimeDelta());\n  TimeTicks base_time = ts.base_time_;\n  PlatformThread::Sleep(Milliseconds(10));\n  EXPECT_GT(ts.GetTimeDelta(), dt);\n  EXPECT_EQ(ts.base_time_, base_time);\n\n  TimeStamp ts2;\n  dt = ts2.GetTimeDelta(false);\n  EXPECT_GT(dt, TimeDelta());\n  PlatformThread::Sleep(Milliseconds(10));\n  EXPECT_GT(ts2.GetTimeDelta(false), dt);\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/time/time_unittest.cc",
    "content": "// Copyright (c) 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/base/time/time.h\"\n\n#include \"gtest/gtest-death-test.h\"\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/threading/platform_thread.h\"\n\nnamespace tachyon::base {\n\nTEST(TimeTest, Max) {\n  constexpr Time kMax = Time::Max();\n  static_assert(kMax.is_max());\n  static_assert(kMax == Time::Max());\n  EXPECT_GT(kMax, Time::Now());\n  static_assert(kMax > Time());\n  EXPECT_TRUE((Time::Now() - kMax).is_negative());\n  EXPECT_TRUE((kMax - Time::Now()).is_positive());\n}\n\nTEST(TimeTest, MaxConversions) {\n  Time t = Time::FromDoubleT(std::numeric_limits<double>::infinity());\n  EXPECT_TRUE(t.is_max());\n  EXPECT_EQ(std::numeric_limits<double>::infinity(), t.ToDoubleT());\n\n  t = Time::FromTimeT(std::numeric_limits<time_t>::max());\n  EXPECT_TRUE(t.is_max());\n  EXPECT_EQ(std::numeric_limits<time_t>::max(), t.ToTimeT());\n\n#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)\n  struct timeval tval;\n  tval.tv_sec = std::numeric_limits<time_t>::max();\n  tval.tv_usec = static_cast<suseconds_t>(Time::kMicrosecondsPerSecond) - 1;\n  t = Time::FromTimeVal(tval);\n  EXPECT_TRUE(t.is_max());\n  tval = t.ToTimeVal();\n  EXPECT_EQ(std::numeric_limits<time_t>::max(), tval.tv_sec);\n  EXPECT_EQ(static_cast<suseconds_t>(Time::kMicrosecondsPerSecond) - 1,\n            tval.tv_usec);\n#endif\n\n#if BUILDFLAG(IS_APPLE)\n  t = Time::FromCFAbsoluteTime(std::numeric_limits<CFAbsoluteTime>::infinity());\n  EXPECT_TRUE(t.is_max());\n  EXPECT_EQ(std::numeric_limits<CFAbsoluteTime>::infinity(),\n            t.ToCFAbsoluteTime());\n#endif\n\n#if BUILDFLAG(IS_WIN)\n  FILETIME ftime;\n  ftime.dwHighDateTime = std::numeric_limits<DWORD>::max();\n  ftime.dwLowDateTime = std::numeric_limits<DWORD>::max();\n  t = Time::FromFileTime(ftime);\n  EXPECT_TRUE(t.is_max());\n  ftime = t.ToFileTime();\n  EXPECT_EQ(std::numeric_limits<DWORD>::max(), ftime.dwHighDateTime);\n  EXPECT_EQ(std::numeric_limits<DWORD>::max(), ftime.dwLowDateTime);\n#endif\n}\n\nTEST(TimeTest, Min) {\n  constexpr Time kMin = Time::Min();\n  static_assert(kMin.is_min());\n  static_assert(kMin == Time::Min());\n  EXPECT_LT(kMin, Time::Now());\n  static_assert(kMin < Time());\n  EXPECT_TRUE((Time::Now() - kMin).is_positive());\n  EXPECT_TRUE((kMin - Time::Now()).is_negative());\n}\n\nTEST(TimeTicks, Deltas) {\n  for (int index = 0; index < 50; index++) {\n    TimeTicks ticks_start = TimeTicks::Now();\n    PlatformThread::Sleep(Milliseconds(10));\n    TimeTicks ticks_stop = TimeTicks::Now();\n    TimeDelta delta = ticks_stop - ticks_start;\n    // Note:  Although we asked for a 10ms sleep, if the\n    // time clock has a finer granularity than the Sleep()\n    // clock, it is quite possible to wakeup early.  Here\n    // is how that works:\n    //      Time(ms timer)      Time(us timer)\n    //          5                   5010\n    //          6                   6010\n    //          7                   7010\n    //          8                   8010\n    //          9                   9000\n    // Elapsed  4ms                 3990us\n    //\n    // Unfortunately, our InMilliseconds() function truncates\n    // rather than rounds.  We should consider fixing this\n    // so that our averages come out better.\n    EXPECT_GE(delta.InMilliseconds(), 9);\n    EXPECT_GE(delta.InMicroseconds(), 9000);\n    EXPECT_EQ(delta.InSeconds(), 0);\n  }\n}\n\nTEST(ThreadTicks, ThreadNow) {\n  if (ThreadTicks::IsSupported()) {\n    TimeTicks begin = TimeTicks::Now();\n    ThreadTicks begin_thread = ThreadTicks::Now();\n    // Make sure that ThreadNow value is non-zero.\n    EXPECT_GT(begin_thread, ThreadTicks());\n    // Sleep for 10 milliseconds to get the thread de-scheduled.\n    PlatformThread::Sleep(Milliseconds(10));\n    ThreadTicks end_thread = ThreadTicks::Now();\n    TimeTicks end = TimeTicks::Now();\n    TimeDelta delta = end - begin;\n    TimeDelta delta_thread = end_thread - begin_thread;\n    // Make sure that some thread time have elapsed.\n    EXPECT_GE(delta_thread.InMicroseconds(), 0);\n    // But the thread time is at least 9ms less than clock time.\n    TimeDelta difference = delta - delta_thread;\n    EXPECT_GE(difference.InMicroseconds(), 9000);\n  }\n}\n\nTEST(TimeDelta, FromAndIn) {\n  // static_assert also checks that the contained expression is a constant\n  // expression, meaning all its components are suitable for initializing global\n  // variables.\n  static_assert(Days(2) == Hours(48));\n  static_assert(Hours(3) == Minutes(180));\n  static_assert(Minutes(2) == Seconds(120));\n  static_assert(Seconds(2) == Milliseconds(2000));\n  static_assert(Milliseconds(2) == Microseconds(2000));\n  static_assert(Seconds(2.3) == Milliseconds(2300));\n  static_assert(Milliseconds(2.5) == Microseconds(2500));\n  EXPECT_EQ(Days(13).InDays(), 13);\n  static_assert(Hours(13).InHours() == 13);\n  static_assert(Minutes(13).InMinutes() == 13);\n  static_assert(Seconds(13).InSeconds() == 13);\n  static_assert(Seconds(13).InSecondsF() == 13.0);\n  EXPECT_EQ(Milliseconds(13).InMilliseconds(), 13);\n  EXPECT_EQ(Milliseconds(13).InMillisecondsF(), 13.0);\n  static_assert(Seconds(13.1).InSeconds() == 13);\n  static_assert(Seconds(13.1).InSecondsF() == 13.1);\n  EXPECT_EQ(Milliseconds(13.3).InMilliseconds(), 13);\n  EXPECT_EQ(Milliseconds(13.3).InMillisecondsF(), 13.3);\n  static_assert(Microseconds(13).InMicroseconds() == 13);\n  static_assert(Microseconds(13.3).InMicroseconds() == 13);\n  EXPECT_EQ(Milliseconds(3.45678).InMillisecondsF(), 3.456);\n  static_assert(Nanoseconds(12345).InNanoseconds() == 12000);\n  static_assert(Nanoseconds(12345.678).InNanoseconds() == 12000);\n}\n\nTEST(TimeDelta, InRoundsTowardsZero) {\n  EXPECT_EQ(Hours(23).InDays(), 0);\n  EXPECT_EQ(Hours(-23).InDays(), 0);\n  static_assert(Minutes(59).InHours() == 0);\n  static_assert(Minutes(-59).InHours() == 0);\n  static_assert(Seconds(59).InMinutes() == 0);\n  static_assert(Seconds(-59).InMinutes() == 0);\n  static_assert(Milliseconds(999).InSeconds() == 0);\n  static_assert(Milliseconds(-999).InSeconds() == 0);\n  EXPECT_EQ(Microseconds(999).InMilliseconds(), 0);\n  EXPECT_EQ(Microseconds(-999).InMilliseconds(), 0);\n}\n\nTEST(TimeDelta, InDaysFloored) {\n  EXPECT_EQ(Hours(-25).InDaysFloored(), -2);\n  EXPECT_EQ(Hours(-24).InDaysFloored(), -1);\n  EXPECT_EQ(Hours(-23).InDaysFloored(), -1);\n\n  EXPECT_EQ(Hours(-1).InDaysFloored(), -1);\n  EXPECT_EQ(Hours(0).InDaysFloored(), 0);\n  EXPECT_EQ(Hours(1).InDaysFloored(), 0);\n\n  EXPECT_EQ(Hours(23).InDaysFloored(), 0);\n  EXPECT_EQ(Hours(24).InDaysFloored(), 1);\n  EXPECT_EQ(Hours(25).InDaysFloored(), 1);\n}\n\nTEST(TimeDelta, InSecondsFloored) {\n  EXPECT_EQ(Seconds(13.1).InSecondsFloored(), 13);\n  EXPECT_EQ(Seconds(13.9).InSecondsFloored(), 13);\n  EXPECT_EQ(Seconds(13).InSecondsFloored(), 13);\n\n  EXPECT_EQ(Milliseconds(1001).InSecondsFloored(), 1);\n  EXPECT_EQ(Milliseconds(1000).InSecondsFloored(), 1);\n  EXPECT_EQ(Milliseconds(999).InSecondsFloored(), 0);\n  EXPECT_EQ(Milliseconds(1).InSecondsFloored(), 0);\n  EXPECT_EQ(Milliseconds(0).InSecondsFloored(), 0);\n  EXPECT_EQ(Milliseconds(-1).InSecondsFloored(), -1);\n  EXPECT_EQ(Milliseconds(-1000).InSecondsFloored(), -1);\n  EXPECT_EQ(Milliseconds(-1001).InSecondsFloored(), -2);\n}\n\nTEST(TimeDelta, InMillisecondsRoundedUp) {\n  EXPECT_EQ(Microseconds(-1001).InMillisecondsRoundedUp(), -1);\n  EXPECT_EQ(Microseconds(-1000).InMillisecondsRoundedUp(), -1);\n  EXPECT_EQ(Microseconds(-999).InMillisecondsRoundedUp(), 0);\n\n  EXPECT_EQ(Microseconds(-1).InMillisecondsRoundedUp(), 0);\n  EXPECT_EQ(Microseconds(0).InMillisecondsRoundedUp(), 0);\n  EXPECT_EQ(Microseconds(1).InMillisecondsRoundedUp(), 1);\n\n  EXPECT_EQ(Microseconds(999).InMillisecondsRoundedUp(), 1);\n  EXPECT_EQ(Microseconds(1000).InMillisecondsRoundedUp(), 1);\n  EXPECT_EQ(Microseconds(1001).InMillisecondsRoundedUp(), 2);\n}\n\n// Check that near-min/max values saturate rather than overflow when converted\n// lossily with InXXX() functions.  Only integral hour, minute, and nanosecond\n// conversions are checked, since those are the only cases where the return type\n// is small enough for saturation or overflow to occur.\nTEST(TimeDelta, InXXXOverflow) {\n  constexpr TimeDelta kLargeDelta =\n      Microseconds(std::numeric_limits<int64_t>::max() - 1);\n  static_assert(!kLargeDelta.is_max());\n  static_assert(std::numeric_limits<int>::max() == kLargeDelta.InHours());\n  static_assert(std::numeric_limits<int>::max() == kLargeDelta.InMinutes());\n  static_assert(std::numeric_limits<int64_t>::max() ==\n                kLargeDelta.InNanoseconds());\n\n  constexpr TimeDelta kLargeNegative =\n      Microseconds(std::numeric_limits<int64_t>::min() + 1);\n  static_assert(!kLargeNegative.is_min());\n  static_assert(std::numeric_limits<int>::min() == kLargeNegative.InHours());\n  static_assert(std::numeric_limits<int>::min() == kLargeNegative.InMinutes());\n  static_assert(std::numeric_limits<int64_t>::min() ==\n                kLargeNegative.InNanoseconds());\n}\n\n#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)\nTEST(TimeDelta, TimeSpecConversion) {\n  TimeDelta delta = Seconds(0);\n  struct timespec result = delta.ToTimeSpec();\n  EXPECT_EQ(result.tv_sec, 0);\n  EXPECT_EQ(result.tv_nsec, 0);\n  EXPECT_EQ(delta, TimeDelta::FromTimeSpec(result));\n\n  delta = Seconds(1);\n  result = delta.ToTimeSpec();\n  EXPECT_EQ(result.tv_sec, 1);\n  EXPECT_EQ(result.tv_nsec, 0);\n  EXPECT_EQ(delta, TimeDelta::FromTimeSpec(result));\n\n  delta = Microseconds(1);\n  result = delta.ToTimeSpec();\n  EXPECT_EQ(result.tv_sec, 0);\n  EXPECT_EQ(result.tv_nsec, 1000);\n  EXPECT_EQ(delta, TimeDelta::FromTimeSpec(result));\n\n  delta = Microseconds(Time::kMicrosecondsPerSecond + 1);\n  result = delta.ToTimeSpec();\n  EXPECT_EQ(result.tv_sec, 1);\n  EXPECT_EQ(result.tv_nsec, 1000);\n  EXPECT_EQ(delta, TimeDelta::FromTimeSpec(result));\n}\n#endif  // BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)\n\nTEST(TimeDelta, Hz) {\n  static_assert(Hertz(1) == Seconds(1));\n  EXPECT_EQ(Hertz(0), TimeDelta::Max());\n  static_assert(Hertz(-1) == Seconds(-1));\n  static_assert(Hertz(1000) == Milliseconds(1));\n  static_assert(Hertz(0.5) == Seconds(2));\n  static_assert(Hertz(std::numeric_limits<double>::infinity()) == TimeDelta());\n\n  static_assert(Seconds(1).ToHz() == 1);\n  static_assert(TimeDelta::Max().ToHz() == 0);\n  static_assert(Seconds(-1).ToHz() == -1);\n  static_assert(Milliseconds(1).ToHz() == 1000);\n  static_assert(Seconds(2).ToHz() == 0.5);\n  EXPECT_EQ(TimeDelta().ToHz(), std::numeric_limits<double>::infinity());\n\n  // 60 Hz can't be represented exactly.\n  static_assert(Hertz(60) * 60 != Seconds(1));\n  static_assert(Hertz(60).ToHz() != 60);\n  EXPECT_EQ(ClampRound(Hertz(60).ToHz()), 60);\n}\n\n// We could define this separately for Time, TimeTicks and TimeDelta but the\n// definitions would be identical anyway.\ntemplate <class Any>\nstd::string AnyToString(Any any) {\n  std::ostringstream oss;\n  oss << any;\n  return oss.str();\n}\n\nTEST(TimeDelta, Magnitude) {\n  constexpr int64_t zero = 0;\n  static_assert(Microseconds(zero) == Microseconds(zero).magnitude());\n\n  constexpr int64_t one = 1;\n  constexpr int64_t negative_one = -1;\n  static_assert(Microseconds(one) == Microseconds(one).magnitude());\n  static_assert(Microseconds(one) == Microseconds(negative_one).magnitude());\n\n  constexpr int64_t max_int64_minus_one =\n      std::numeric_limits<int64_t>::max() - 1;\n  constexpr int64_t min_int64_plus_two =\n      std::numeric_limits<int64_t>::min() + 2;\n  static_assert(Microseconds(max_int64_minus_one) ==\n                Microseconds(max_int64_minus_one).magnitude());\n  static_assert(Microseconds(max_int64_minus_one) ==\n                Microseconds(min_int64_plus_two).magnitude());\n\n  static_assert(TimeDelta::Max() == TimeDelta::Min().magnitude());\n}\n\nTEST(TimeDelta, ZeroMinMax) {\n  constexpr TimeDelta kZero;\n  static_assert(kZero.is_zero());\n\n  constexpr TimeDelta kMax = TimeDelta::Max();\n  static_assert(kMax.is_max());\n  static_assert(kMax == TimeDelta::Max());\n  static_assert(kMax > Days(100 * 365));\n  static_assert(kMax > kZero);\n\n  constexpr TimeDelta kMin = TimeDelta::Min();\n  static_assert(kMin.is_min());\n  static_assert(kMin == TimeDelta::Min());\n  static_assert(kMin < Days(-100 * 365));\n  static_assert(kMin < kZero);\n}\n\nTEST(TimeDelta, MaxConversions) {\n  // static_assert also confirms constexpr works as intended.\n  constexpr TimeDelta kMax = TimeDelta::Max();\n  EXPECT_EQ(kMax.InDays(), std::numeric_limits<int>::max());\n  static_assert(kMax.InHours() == std::numeric_limits<int>::max());\n  static_assert(kMax.InMinutes() == std::numeric_limits<int>::max());\n  static_assert(kMax.InSecondsF() == std::numeric_limits<double>::infinity());\n  static_assert(kMax.InSeconds() == std::numeric_limits<int64_t>::max());\n  EXPECT_EQ(kMax.InMillisecondsF(), std::numeric_limits<double>::infinity());\n  EXPECT_EQ(kMax.InMilliseconds(), std::numeric_limits<int64_t>::max());\n  EXPECT_EQ(kMax.InMillisecondsRoundedUp(),\n            std::numeric_limits<int64_t>::max());\n\n  static_assert(Days(std::numeric_limits<int64_t>::max()).is_max());\n\n  static_assert(Hours(std::numeric_limits<int64_t>::max()).is_max());\n\n  static_assert(Minutes(std::numeric_limits<int64_t>::max()).is_max());\n\n  constexpr int64_t max_int = std::numeric_limits<int64_t>::max();\n  constexpr int64_t min_int = std::numeric_limits<int64_t>::min();\n\n  static_assert(Seconds(max_int / Time::kMicrosecondsPerSecond + 1).is_max());\n\n  static_assert(\n      Milliseconds(max_int / Time::kMillisecondsPerSecond + 1).is_max());\n\n  static_assert(Microseconds(max_int).is_max());\n\n  static_assert(Seconds(min_int / Time::kMicrosecondsPerSecond - 1).is_min());\n\n  static_assert(\n      Milliseconds(min_int / Time::kMillisecondsPerSecond - 1).is_min());\n\n  static_assert(Microseconds(min_int).is_min());\n\n  static_assert(Microseconds(std::numeric_limits<int64_t>::min()).is_min());\n\n  static_assert(Seconds(std::numeric_limits<double>::infinity()).is_max());\n\n  // Note that max_int/min_int will be rounded when converted to doubles - they\n  // can't be exactly represented.\n  constexpr double max_d = static_cast<double>(max_int);\n  constexpr double min_d = static_cast<double>(min_int);\n\n  static_assert(Seconds(max_d / Time::kMicrosecondsPerSecond + 1).is_max());\n\n  static_assert(\n      Microseconds(max_d).is_max(),\n      \"Make sure that 2⁶³ correctly gets clamped to `max` (crbug.com/612601)\");\n\n  static_assert(Milliseconds(std::numeric_limits<double>::infinity()).is_max());\n\n  static_assert(\n      Milliseconds(max_d / Time::kMillisecondsPerSecond * 2).is_max());\n\n  static_assert(Seconds(min_d / Time::kMicrosecondsPerSecond - 1).is_min());\n\n  static_assert(\n      Milliseconds(min_d / Time::kMillisecondsPerSecond * 2).is_min());\n}\n\nTEST(TimeDelta, MinConversions) {\n  constexpr TimeDelta kMin = TimeDelta::Min();\n\n  EXPECT_EQ(kMin.InDays(), std::numeric_limits<int>::min());\n  static_assert(kMin.InHours() == std::numeric_limits<int>::min());\n  static_assert(kMin.InMinutes() == std::numeric_limits<int>::min());\n  static_assert(kMin.InSecondsF() == -std::numeric_limits<double>::infinity());\n  static_assert(kMin.InSeconds() == std::numeric_limits<int64_t>::min());\n  EXPECT_EQ(kMin.InMillisecondsF(), -std::numeric_limits<double>::infinity());\n  EXPECT_EQ(kMin.InMilliseconds(), std::numeric_limits<int64_t>::min());\n  EXPECT_EQ(kMin.InMillisecondsRoundedUp(),\n            std::numeric_limits<int64_t>::min());\n}\n\nTEST(TimeDelta, FiniteMaxMin) {\n  constexpr TimeDelta kFiniteMax = TimeDelta::FiniteMax();\n  constexpr TimeDelta kUnit = Microseconds(1);\n  static_assert(kFiniteMax + kUnit == TimeDelta::Max());\n  static_assert(kFiniteMax - kUnit < kFiniteMax);\n\n  constexpr TimeDelta kFiniteMin = TimeDelta::FiniteMin();\n  static_assert(kFiniteMin - kUnit == TimeDelta::Min());\n  static_assert(kFiniteMin + kUnit > kFiniteMin);\n}\n\nTEST(TimeDelta, NumericOperators) {\n  constexpr double d = 0.5;\n  static_assert(Milliseconds(500) == Milliseconds(1000) * d);\n  static_assert(Milliseconds(2000) == (Milliseconds(1000) / d));\n  static_assert(Milliseconds(500) == (Milliseconds(1000) *= d));\n  static_assert(Milliseconds(2000) == (Milliseconds(1000) /= d));\n  static_assert(Milliseconds(500) == d * Milliseconds(1000));\n\n  constexpr float f = 0.5;\n  static_assert(Milliseconds(500) == Milliseconds(1000) * f);\n  static_assert(Milliseconds(2000) == (Milliseconds(1000) / f));\n  static_assert(Milliseconds(500) == (Milliseconds(1000) *= f));\n  static_assert(Milliseconds(2000) == (Milliseconds(1000) /= f));\n  static_assert(Milliseconds(500) == f * Milliseconds(1000));\n\n  constexpr int i = 2;\n  static_assert(Milliseconds(2000) == Milliseconds(1000) * i);\n  static_assert(Milliseconds(500) == (Milliseconds(1000) / i));\n  static_assert(Milliseconds(2000) == (Milliseconds(1000) *= i));\n  static_assert(Milliseconds(500) == (Milliseconds(1000) /= i));\n  static_assert(Milliseconds(2000) == i * Milliseconds(1000));\n\n  constexpr int64_t i64 = 2;\n  static_assert(Milliseconds(2000) == Milliseconds(1000) * i64);\n  static_assert(Milliseconds(500) == (Milliseconds(1000) / i64));\n  static_assert(Milliseconds(2000) == (Milliseconds(1000) *= i64));\n  static_assert(Milliseconds(500) == (Milliseconds(1000) /= i64));\n  static_assert(Milliseconds(2000) == i64 * Milliseconds(1000));\n\n  static_assert(Milliseconds(500) == Milliseconds(1000) * 0.5);\n  static_assert(Milliseconds(2000) == (Milliseconds(1000) / 0.5));\n  static_assert(Milliseconds(500) == (Milliseconds(1000) *= 0.5));\n  static_assert(Milliseconds(2000) == (Milliseconds(1000) /= 0.5));\n  static_assert(Milliseconds(500) == 0.5 * Milliseconds(1000));\n\n  static_assert(Milliseconds(2000) == Milliseconds(1000) * 2);\n  static_assert(Milliseconds(500) == (Milliseconds(1000) / 2));\n  static_assert(Milliseconds(2000) == (Milliseconds(1000) *= 2));\n  static_assert(Milliseconds(500) == (Milliseconds(1000) /= 2));\n  static_assert(Milliseconds(2000) == 2 * Milliseconds(1000));\n}\n\n// Basic test of operators between TimeDeltas (without overflow -- next test\n// handles overflow).\nTEST(TimeDelta, TimeDeltaOperators) {\n  constexpr TimeDelta kElevenSeconds = Seconds(11);\n  constexpr TimeDelta kThreeSeconds = Seconds(3);\n\n  static_assert(Seconds(14) == kElevenSeconds + kThreeSeconds);\n  static_assert(Seconds(14) == kThreeSeconds + kElevenSeconds);\n  static_assert(Seconds(8) == kElevenSeconds - kThreeSeconds);\n  static_assert(Seconds(-8) == kThreeSeconds - kElevenSeconds);\n  static_assert(11.0 / 3.0 == kElevenSeconds / kThreeSeconds);\n  static_assert(3.0 / 11.0 == kThreeSeconds / kElevenSeconds);\n  static_assert(3 == kElevenSeconds.IntDiv(kThreeSeconds));\n  static_assert(0 == kThreeSeconds.IntDiv(kElevenSeconds));\n  static_assert(Seconds(2) == kElevenSeconds % kThreeSeconds);\n}\n\nTEST(TimeDelta, Overflows) {\n  // Some sanity checks. static_asserts used where possible to verify constexpr\n  // evaluation at the same time.\n  static_assert(TimeDelta::Max().is_max());\n  static_assert(-TimeDelta::Max() < TimeDelta());\n  static_assert(-TimeDelta::Max() == TimeDelta::Min());\n  static_assert(TimeDelta() > -TimeDelta::Max());\n\n  static_assert(TimeDelta::Min().is_min());\n  static_assert(-TimeDelta::Min() > TimeDelta());\n  static_assert(-TimeDelta::Min() == TimeDelta::Max());\n  static_assert(TimeDelta() < -TimeDelta::Min());\n\n  constexpr TimeDelta kLargeDelta = TimeDelta::Max() - Milliseconds(1);\n  constexpr TimeDelta kLargeNegative = -kLargeDelta;\n  static_assert(TimeDelta() > kLargeNegative);\n  static_assert(!kLargeDelta.is_max());\n  static_assert(!(-kLargeNegative).is_min());\n\n  // Test +, -, * and / operators.\n  constexpr TimeDelta kOneSecond = Seconds(1);\n  static_assert((kLargeDelta + kOneSecond).is_max());\n  static_assert((kLargeNegative + (-kOneSecond)).is_min());\n  static_assert((kLargeNegative - kOneSecond).is_min());\n  static_assert((kLargeDelta - (-kOneSecond)).is_max());\n  static_assert((kLargeDelta * 2).is_max());\n  static_assert((kLargeDelta * -2).is_min());\n  static_assert((kLargeDelta / 0.5).is_max());\n  static_assert((kLargeDelta / -0.5).is_min());\n\n  // Test math operators on Max() and Min() values\n  // Calculations that would overflow are saturated.\n  static_assert(TimeDelta::Max() + kOneSecond == TimeDelta::Max());\n  static_assert(TimeDelta::Max() * 7 == TimeDelta::Max());\n  static_assert(TimeDelta::FiniteMax() + kOneSecond == TimeDelta::Max());\n  static_assert(TimeDelta::Min() - kOneSecond == TimeDelta::Min());\n  static_assert(TimeDelta::Min() * 7 == TimeDelta::Min());\n  static_assert(TimeDelta::FiniteMin() - kOneSecond == TimeDelta::Min());\n\n  // Division is done by converting to double with Max()/Min() converted to\n  // +/- infinities.\n  static_assert(TimeDelta::Max() / kOneSecond ==\n                std::numeric_limits<double>::infinity());\n  static_assert(TimeDelta::Max() / -kOneSecond ==\n                -std::numeric_limits<double>::infinity());\n  static_assert(TimeDelta::Min() / kOneSecond ==\n                -std::numeric_limits<double>::infinity());\n  static_assert(TimeDelta::Min() / -kOneSecond ==\n                std::numeric_limits<double>::infinity());\n  static_assert(TimeDelta::Max().IntDiv(kOneSecond) ==\n                std::numeric_limits<int64_t>::max());\n  static_assert(TimeDelta::Max().IntDiv(-kOneSecond) ==\n                std::numeric_limits<int64_t>::min());\n  static_assert(TimeDelta::Min().IntDiv(kOneSecond) ==\n                std::numeric_limits<int64_t>::min());\n  static_assert(TimeDelta::Min().IntDiv(-kOneSecond) ==\n                std::numeric_limits<int64_t>::max());\n  static_assert(TimeDelta::Max() % kOneSecond == TimeDelta::Max());\n  static_assert(TimeDelta::Max() % -kOneSecond == TimeDelta::Max());\n  static_assert(TimeDelta::Min() % kOneSecond == TimeDelta::Min());\n  static_assert(TimeDelta::Min() % -kOneSecond == TimeDelta::Min());\n\n  // Division by zero.\n  static_assert((kOneSecond / 0).is_max());\n  static_assert((-kOneSecond / 0).is_min());\n  static_assert((TimeDelta::Max() / 0).is_max());\n  static_assert((TimeDelta::Min() / 0).is_min());\n  EXPECT_EQ(std::numeric_limits<double>::infinity(), kOneSecond / TimeDelta());\n  EXPECT_EQ(-std::numeric_limits<double>::infinity(),\n            -kOneSecond / TimeDelta());\n  EXPECT_EQ(std::numeric_limits<double>::infinity(),\n            TimeDelta::Max() / TimeDelta());\n  EXPECT_EQ(-std::numeric_limits<double>::infinity(),\n            TimeDelta::Min() / TimeDelta());\n  static_assert(kOneSecond.IntDiv(TimeDelta()) ==\n                std::numeric_limits<int64_t>::max());\n  static_assert((-kOneSecond).IntDiv(TimeDelta()) ==\n                std::numeric_limits<int64_t>::min());\n  static_assert(TimeDelta::Max().IntDiv(TimeDelta()) ==\n                std::numeric_limits<int64_t>::max());\n  static_assert(TimeDelta::Min().IntDiv(TimeDelta()) ==\n                std::numeric_limits<int64_t>::min());\n  static_assert(kOneSecond % TimeDelta() == kOneSecond);\n  static_assert(-kOneSecond % TimeDelta() == -kOneSecond);\n  static_assert(TimeDelta::Max() % TimeDelta() == TimeDelta::Max());\n  static_assert(TimeDelta::Min() % TimeDelta() == TimeDelta::Min());\n\n  // Division by infinity.\n  static_assert(kLargeDelta / TimeDelta::Min() == 0);\n  static_assert(kLargeDelta / TimeDelta::Max() == 0);\n  static_assert(kLargeNegative / TimeDelta::Min() == 0);\n  static_assert(kLargeNegative / TimeDelta::Max() == 0);\n  static_assert(kLargeDelta.IntDiv(TimeDelta::Min()) == 0);\n  static_assert(kLargeDelta.IntDiv(TimeDelta::Max()) == 0);\n  static_assert(kLargeNegative.IntDiv(TimeDelta::Min()) == 0);\n  static_assert(kLargeNegative.IntDiv(TimeDelta::Max()) == 0);\n  static_assert(kOneSecond % TimeDelta::Min() == kOneSecond);\n  static_assert(kOneSecond % TimeDelta::Max() == kOneSecond);\n\n  // Test that double conversions overflow to infinity.\n  static_assert((kLargeDelta + kOneSecond).InSecondsF() ==\n                std::numeric_limits<double>::infinity());\n  EXPECT_EQ((kLargeDelta + kOneSecond).InMillisecondsF(),\n            std::numeric_limits<double>::infinity());\n  EXPECT_EQ((kLargeDelta + kOneSecond).InMicrosecondsF(),\n            std::numeric_limits<double>::infinity());\n\n  // Test op=.\n  static_assert((TimeDelta::FiniteMax() += kOneSecond).is_max());\n  static_assert((TimeDelta::FiniteMin() += -kOneSecond).is_min());\n\n  static_assert((TimeDelta::FiniteMin() -= kOneSecond).is_min());\n  static_assert((TimeDelta::FiniteMax() -= -kOneSecond).is_max());\n\n  static_assert((TimeDelta::FiniteMax() *= 2).is_max());\n  static_assert((TimeDelta::FiniteMin() *= 1.5).is_min());\n\n  static_assert((TimeDelta::FiniteMax() /= 0.5).is_max());\n  static_assert((TimeDelta::FiniteMin() /= 0.5).is_min());\n\n  static_assert((Seconds(1) %= TimeDelta::Max()) == Seconds(1));\n  static_assert((Seconds(1) %= TimeDelta()) == Seconds(1));\n\n  // Test operations with Time and TimeTicks.\n  EXPECT_TRUE((kLargeDelta + Time::Now()).is_max());\n  EXPECT_TRUE((kLargeDelta + TimeTicks::Now()).is_max());\n  EXPECT_TRUE((Time::Now() + kLargeDelta).is_max());\n  EXPECT_TRUE((TimeTicks::Now() + kLargeDelta).is_max());\n\n  Time time_now = Time::Now();\n  EXPECT_EQ(kOneSecond, (time_now + kOneSecond) - time_now);\n  EXPECT_EQ(-kOneSecond, (time_now - kOneSecond) - time_now);\n\n  TimeTicks ticks_now = TimeTicks::Now();\n  EXPECT_EQ(-kOneSecond, (ticks_now - kOneSecond) - ticks_now);\n  EXPECT_EQ(kOneSecond, (ticks_now + kOneSecond) - ticks_now);\n}\n\nTEST(TimeDelta, CeilToMultiple) {\n  for (const auto interval : {Seconds(10), Seconds(-10)}) {\n    SCOPED_TRACE(interval);\n    EXPECT_EQ(TimeDelta().CeilToMultiple(interval), TimeDelta());\n    EXPECT_EQ(Seconds(1).CeilToMultiple(interval), Seconds(10));\n    EXPECT_EQ(Seconds(9).CeilToMultiple(interval), Seconds(10));\n    EXPECT_EQ(Seconds(10).CeilToMultiple(interval), Seconds(10));\n    EXPECT_EQ(Seconds(15).CeilToMultiple(interval), Seconds(20));\n    EXPECT_EQ(Seconds(20).CeilToMultiple(interval), Seconds(20));\n    EXPECT_EQ(TimeDelta::Max().CeilToMultiple(interval), TimeDelta::Max());\n    EXPECT_EQ(Seconds(-1).CeilToMultiple(interval), TimeDelta());\n    EXPECT_EQ(Seconds(-9).CeilToMultiple(interval), TimeDelta());\n    EXPECT_EQ(Seconds(-10).CeilToMultiple(interval), Seconds(-10));\n    EXPECT_EQ(Seconds(-15).CeilToMultiple(interval), Seconds(-10));\n    EXPECT_EQ(Seconds(-20).CeilToMultiple(interval), Seconds(-20));\n    EXPECT_EQ(TimeDelta::Min().CeilToMultiple(interval), TimeDelta::Min());\n  }\n\n  for (const auto interval : {TimeDelta::Max(), TimeDelta::Min()}) {\n    SCOPED_TRACE(interval);\n    EXPECT_EQ(TimeDelta().CeilToMultiple(interval), TimeDelta());\n    EXPECT_EQ(Seconds(1).CeilToMultiple(interval), TimeDelta::Max());\n    EXPECT_EQ(Seconds(9).CeilToMultiple(interval), TimeDelta::Max());\n    EXPECT_EQ(Seconds(10).CeilToMultiple(interval), TimeDelta::Max());\n    EXPECT_EQ(Seconds(15).CeilToMultiple(interval), TimeDelta::Max());\n    EXPECT_EQ(Seconds(20).CeilToMultiple(interval), TimeDelta::Max());\n    EXPECT_EQ(TimeDelta::Max().CeilToMultiple(interval), TimeDelta::Max());\n    EXPECT_EQ(Seconds(-1).CeilToMultiple(interval), TimeDelta());\n    EXPECT_EQ(Seconds(-9).CeilToMultiple(interval), TimeDelta());\n    EXPECT_EQ(Seconds(-10).CeilToMultiple(interval), TimeDelta());\n    EXPECT_EQ(Seconds(-15).CeilToMultiple(interval), TimeDelta());\n    EXPECT_EQ(Seconds(-20).CeilToMultiple(interval), TimeDelta());\n    EXPECT_EQ(TimeDelta::Min().CeilToMultiple(interval), TimeDelta::Min());\n  }\n}\n\nTEST(TimeDelta, FloorToMultiple) {\n  for (const auto interval : {Seconds(10), Seconds(-10)}) {\n    SCOPED_TRACE(interval);\n    EXPECT_EQ(TimeDelta().FloorToMultiple(interval), TimeDelta());\n    EXPECT_EQ(Seconds(1).FloorToMultiple(interval), TimeDelta());\n    EXPECT_EQ(Seconds(9).FloorToMultiple(interval), TimeDelta());\n    EXPECT_EQ(Seconds(10).FloorToMultiple(interval), Seconds(10));\n    EXPECT_EQ(Seconds(15).FloorToMultiple(interval), Seconds(10));\n    EXPECT_EQ(Seconds(20).FloorToMultiple(interval), Seconds(20));\n    EXPECT_EQ(TimeDelta::Max().FloorToMultiple(interval), TimeDelta::Max());\n    EXPECT_EQ(Seconds(-1).FloorToMultiple(interval), Seconds(-10));\n    EXPECT_EQ(Seconds(-9).FloorToMultiple(interval), Seconds(-10));\n    EXPECT_EQ(Seconds(-10).FloorToMultiple(interval), Seconds(-10));\n    EXPECT_EQ(Seconds(-15).FloorToMultiple(interval), Seconds(-20));\n    EXPECT_EQ(Seconds(-20).FloorToMultiple(interval), Seconds(-20));\n    EXPECT_EQ(TimeDelta::Min().FloorToMultiple(interval), TimeDelta::Min());\n  }\n\n  for (const auto interval : {TimeDelta::Max(), TimeDelta::Min()}) {\n    SCOPED_TRACE(interval);\n    EXPECT_EQ(TimeDelta().FloorToMultiple(interval), TimeDelta());\n    EXPECT_EQ(Seconds(1).FloorToMultiple(interval), TimeDelta());\n    EXPECT_EQ(Seconds(9).FloorToMultiple(interval), TimeDelta());\n    EXPECT_EQ(Seconds(10).FloorToMultiple(interval), TimeDelta());\n    EXPECT_EQ(Seconds(15).FloorToMultiple(interval), TimeDelta());\n    EXPECT_EQ(Seconds(20).FloorToMultiple(interval), TimeDelta());\n    EXPECT_EQ(TimeDelta::Max().FloorToMultiple(interval), TimeDelta::Max());\n    EXPECT_EQ(Seconds(-1).FloorToMultiple(interval), TimeDelta::Min());\n    EXPECT_EQ(Seconds(-9).FloorToMultiple(interval), TimeDelta::Min());\n    EXPECT_EQ(Seconds(-10).FloorToMultiple(interval), TimeDelta::Min());\n    EXPECT_EQ(Seconds(-15).FloorToMultiple(interval), TimeDelta::Min());\n    EXPECT_EQ(Seconds(-20).FloorToMultiple(interval), TimeDelta::Min());\n    EXPECT_EQ(TimeDelta::Min().FloorToMultiple(interval), TimeDelta::Min());\n  }\n}\n\nTEST(TimeDelta, RoundToMultiple) {\n  for (const auto interval : {Seconds(10), Seconds(-10)}) {\n    SCOPED_TRACE(interval);\n    EXPECT_EQ(TimeDelta().RoundToMultiple(interval), TimeDelta());\n    EXPECT_EQ(Seconds(1).RoundToMultiple(interval), TimeDelta());\n    EXPECT_EQ(Seconds(9).RoundToMultiple(interval), Seconds(10));\n    EXPECT_EQ(Seconds(10).RoundToMultiple(interval), Seconds(10));\n    EXPECT_EQ(Seconds(15).RoundToMultiple(interval), Seconds(20));\n    EXPECT_EQ(Seconds(20).RoundToMultiple(interval), Seconds(20));\n    EXPECT_EQ(TimeDelta::Max().RoundToMultiple(interval), TimeDelta::Max());\n    EXPECT_EQ(Seconds(-1).RoundToMultiple(interval), TimeDelta());\n    EXPECT_EQ(Seconds(-9).RoundToMultiple(interval), Seconds(-10));\n    EXPECT_EQ(Seconds(-10).RoundToMultiple(interval), Seconds(-10));\n    EXPECT_EQ(Seconds(-15).RoundToMultiple(interval), Seconds(-20));\n    EXPECT_EQ(Seconds(-20).RoundToMultiple(interval), Seconds(-20));\n    EXPECT_EQ(TimeDelta::Min().RoundToMultiple(interval), TimeDelta::Min());\n  }\n\n  for (const auto interval : {TimeDelta::Max(), TimeDelta::Min()}) {\n    SCOPED_TRACE(interval);\n    EXPECT_EQ(TimeDelta().RoundToMultiple(interval), TimeDelta());\n    EXPECT_EQ(Seconds(1).RoundToMultiple(interval), TimeDelta());\n    EXPECT_EQ(Seconds(9).RoundToMultiple(interval), TimeDelta());\n    EXPECT_EQ(Seconds(10).RoundToMultiple(interval), TimeDelta());\n    EXPECT_EQ(Seconds(15).RoundToMultiple(interval), TimeDelta());\n    EXPECT_EQ(Seconds(20).RoundToMultiple(interval), TimeDelta());\n    EXPECT_EQ(TimeDelta::Max().RoundToMultiple(interval), TimeDelta::Max());\n    EXPECT_EQ(Seconds(-1).RoundToMultiple(interval), TimeDelta());\n    EXPECT_EQ(Seconds(-9).RoundToMultiple(interval), TimeDelta());\n    EXPECT_EQ(Seconds(-10).RoundToMultiple(interval), TimeDelta());\n    EXPECT_EQ(Seconds(-15).RoundToMultiple(interval), TimeDelta());\n    EXPECT_EQ(Seconds(-20).RoundToMultiple(interval), TimeDelta());\n    EXPECT_EQ(TimeDelta::Min().RoundToMultiple(interval), TimeDelta::Min());\n  }\n}\n\nTEST(TimeBase, AddSubDeltaSaturates) {\n  constexpr TimeTicks kLargeTimeTicks =\n      TimeTicks() + Microseconds(std::numeric_limits<int64_t>::max() - 1);\n\n  constexpr TimeTicks kLargeNegativeTimeTicks =\n      TimeTicks() + Microseconds(std::numeric_limits<int64_t>::min() + 1);\n\n  static_assert((kLargeTimeTicks + TimeDelta::Max()).is_max());\n  static_assert((kLargeNegativeTimeTicks + TimeDelta::Max()).is_max());\n  static_assert((kLargeTimeTicks - TimeDelta::Max()).is_min());\n  static_assert((kLargeNegativeTimeTicks - TimeDelta::Max()).is_min());\n  static_assert((TimeTicks() + TimeDelta::Max()).is_max());\n  static_assert((TimeTicks() - TimeDelta::Max()).is_min());\n  EXPECT_TRUE((TimeTicks::Now() + TimeDelta::Max()).is_max())\n      << (TimeTicks::Now() + TimeDelta::Max());\n  EXPECT_TRUE((TimeTicks::Now() - TimeDelta::Max()).is_min())\n      << (TimeTicks::Now() - TimeDelta::Max());\n\n  static_assert((kLargeTimeTicks + TimeDelta::Min()).is_min());\n  static_assert((kLargeNegativeTimeTicks + TimeDelta::Min()).is_min());\n  static_assert((kLargeTimeTicks - TimeDelta::Min()).is_max());\n  static_assert((kLargeNegativeTimeTicks - TimeDelta::Min()).is_max());\n  static_assert((TimeTicks() + TimeDelta::Min()).is_min());\n  static_assert((TimeTicks() - TimeDelta::Min()).is_max());\n  EXPECT_TRUE((TimeTicks::Now() + TimeDelta::Min()).is_min())\n      << (TimeTicks::Now() + TimeDelta::Min());\n  EXPECT_TRUE((TimeTicks::Now() - TimeDelta::Min()).is_max())\n      << (TimeTicks::Now() - TimeDelta::Min());\n}\n\nTEST(TimeBase, AddSubInfinities) {\n  /*\n  TODO(chokobole):\n  // CHECK when adding opposite signs or subtracting same sign.\n  EXPECT_CHECK_DEATH({ TimeTicks::Min() + TimeDelta::Max(); });\n  EXPECT_CHECK_DEATH({ TimeTicks::Max() + TimeDelta::Min(); });\n  EXPECT_CHECK_DEATH({ TimeTicks::Min() - TimeDelta::Min(); });\n  EXPECT_CHECK_DEATH({ TimeTicks::Max() - TimeDelta::Max(); });\n  */\n\n  // Saturates when adding same sign or subtracting opposite signs.\n  static_assert((TimeTicks::Max() + TimeDelta::Max()).is_max());\n  static_assert((TimeTicks::Min() + TimeDelta::Min()).is_min());\n  static_assert((TimeTicks::Max() - TimeDelta::Min()).is_max());\n  static_assert((TimeTicks::Min() - TimeDelta::Max()).is_min());\n}\n\nconstexpr TimeDelta TestTimeDeltaConstexprCopyAssignment() {\n  TimeDelta a = Seconds(1);\n  TimeDelta b;\n  b = a;\n  return b;\n}\n\nTEST(TimeDelta, ConstexprAndTriviallyCopiable) {\n  // \"Trivially copyable\" is necessary for use in std::atomic<TimeDelta>.\n  static_assert(std::is_trivially_copyable<TimeDelta>());\n\n  // Copy ctor.\n  constexpr TimeDelta a = Seconds(1);\n  constexpr TimeDelta b{a};\n  static_assert(a == b);\n\n  // Copy assignment.\n  static_assert(a == TestTimeDeltaConstexprCopyAssignment());\n}\n\nTEST(TimeDeltaLogging, DCheckEqCompiles) {\n  DCHECK_EQ(TimeDelta(), TimeDelta());\n}\n\nTEST(TimeDeltaLogging, EmptyIsZero) {\n  constexpr TimeDelta kZero;\n  EXPECT_EQ(\"0 s\", AnyToString(kZero));\n}\n\nTEST(TimeDeltaLogging, FiveHundredMs) {\n  constexpr TimeDelta kFiveHundredMs = Milliseconds(500);\n  EXPECT_EQ(\"0.5 s\", AnyToString(kFiveHundredMs));\n}\n\nTEST(TimeDeltaLogging, MinusTenSeconds) {\n  constexpr TimeDelta kMinusTenSeconds = Seconds(-10);\n  EXPECT_EQ(\"-10 s\", AnyToString(kMinusTenSeconds));\n}\n\nTEST(TimeDeltaLogging, DoesNotMessUpFormattingFlags) {\n  std::ostringstream oss;\n  std::ios_base::fmtflags flags_before = oss.flags();\n  oss << TimeDelta();\n  EXPECT_EQ(flags_before, oss.flags());\n}\n\nTEST(TimeDeltaLogging, DoesNotMakeStreamBad) {\n  std::ostringstream oss;\n  oss << TimeDelta();\n  EXPECT_TRUE(oss.good());\n}\n\nTEST(TimeLogging, DCheckEqCompiles) { DCHECK_EQ(Time(), Time()); }\n\nTEST(TimeLogging, DoesNotMessUpFormattingFlags) {\n  std::ostringstream oss;\n  std::ios_base::fmtflags flags_before = oss.flags();\n  oss << Time();\n  EXPECT_EQ(flags_before, oss.flags());\n}\n\nTEST(TimeLogging, DoesNotMakeStreamBad) {\n  std::ostringstream oss;\n  oss << Time();\n  EXPECT_TRUE(oss.good());\n}\n\nTEST(TimeTicksLogging, DCheckEqCompiles) {\n  DCHECK_EQ(TimeTicks(), TimeTicks());\n}\n\nTEST(TimeTicksLogging, ZeroTime) {\n  TimeTicks zero;\n  EXPECT_EQ(\"0 bogo-microseconds\", AnyToString(zero));\n}\n\nTEST(TimeTicksLogging, FortyYearsLater) {\n  TimeTicks forty_years_later = TimeTicks() + Days(365.25 * 40);\n  EXPECT_EQ(\"1262304000000000 bogo-microseconds\",\n            AnyToString(forty_years_later));\n}\n\nTEST(TimeTicksLogging, DoesNotMessUpFormattingFlags) {\n  std::ostringstream oss;\n  std::ios_base::fmtflags flags_before = oss.flags();\n  oss << TimeTicks();\n  EXPECT_EQ(flags_before, oss.flags());\n}\n\nTEST(TimeTicksLogging, DoesNotMakeStreamBad) {\n  std::ostringstream oss;\n  oss << TimeTicks();\n  EXPECT_TRUE(oss.good());\n}\n\nTEST(TimeDelta, ChronoConversion) {\n  TimeDelta delta = Seconds(1);\n  std::chrono::microseconds microseconds = delta.ToChronoMicroseconds();\n  EXPECT_EQ(microseconds.count(), delta.InMicroseconds());\n  EXPECT_EQ(TimeDelta::FromChrono(microseconds), delta);\n\n  delta = Hours(1);\n  std::chrono::minutes minutes = delta.ToChronoMinutes();\n  EXPECT_EQ(minutes.count(), delta.InMinutes());\n  EXPECT_EQ(TimeDelta::FromChrono(minutes), delta);\n}\n\n// Test conversion to/from TimeDeltas elapsed since the Windows epoch.\n// Conversions should be idempotent and non-lossy.\nTEST(Time, DeltaSinceUnixEpoch) {\n  const TimeDelta delta = Microseconds(123);\n  EXPECT_EQ(delta,\n            Time::FromDeltaSinceUnixEpoch(delta).ToDeltaSinceUnixEpoch());\n\n  const Time now = Time::Now();\n  const Time actual =\n      Time::FromDeltaSinceUnixEpoch(now.ToDeltaSinceUnixEpoch());\n  EXPECT_EQ(now, actual);\n\n  // Null times should remain null after a round-trip conversion. This is an\n  // important invariant for the common use case of serialization +\n  // deserialization.\n  const Time should_be_null =\n      Time::FromDeltaSinceUnixEpoch(Time().ToDeltaSinceUnixEpoch());\n  EXPECT_TRUE(should_be_null.is_null());\n}\n\n// Test conversion to/from time_t.\nTEST(TimeTest, TimeT) {\n  EXPECT_EQ(10, Time().FromTimeT(10).ToTimeT());\n  EXPECT_EQ(10.0, Time().FromTimeT(10).ToDoubleT());\n\n  // Conversions of 0 should stay 0.\n  EXPECT_EQ(0, Time().ToTimeT());\n}\n\n#if BUILDFLAG(IS_POSIX)\nTEST(Time, FromTimeVal) {\n  Time now = Time::Now();\n  Time also_now = Time::FromTimeVal(now.ToTimeVal());\n  EXPECT_EQ(now, also_now);\n}\n#endif  // BUILDFLAG(IS_POSIX)\n\nTEST(Time, ChronoConversion) {\n  Time now = Time::Now();\n  std::chrono::system_clock::time_point tp = now.ToChrono();\n  EXPECT_EQ(tp.time_since_epoch(),\n            now.ToDeltaSinceUnixEpoch()\n                .ToChrono<std::chrono::system_clock::duration>());\n  EXPECT_NEAR((now - Time()).InMicroseconds(),\n              (Time::FromChrono(tp) - Time()).InMicroseconds(), 1);\n}\n\nTEST(TimeTicks, ChronoConversion) {\n  TimeTicks now = TimeTicks::Now();\n  std::chrono::steady_clock::time_point tp = now.ToChrono();\n  EXPECT_EQ(\n      tp.time_since_epoch(),\n      (now - TimeTicks{}).ToChrono<std::chrono::steady_clock::duration>());\n  EXPECT_NEAR((now - TimeTicks()).InMicroseconds(),\n              (TimeTicks::FromChrono(tp) - TimeTicks()).InMicroseconds(), 1);\n}\n\n}  // namespace tachyon::base\n"
  },
  {
    "path": "tachyon/base/type_list.h",
    "content": "// Copyright (c) 2011 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n// Derived from chromium/base/bind_internal.h\n\n#ifndef TACHYON_BASE_TYPE_LIST_H_\n#define TACHYON_BASE_TYPE_LIST_H_\n\n#include <stddef.h>\n\n#include <tuple>\n#include <type_traits>\n\nnamespace tachyon::base::internal {\n\n// Packs a list of types to hold them in a single type.\ntemplate <typename... Types>\nstruct TypeList {};\n\n// Used for DropTypeListItem implementation.\ntemplate <size_t n, typename List>\nstruct DropTypeListItemImpl;\n\n// Do not use enable_if and SFINAE here to avoid MSVC2013 compile failure.\ntemplate <size_t n, typename T, typename... List>\nstruct DropTypeListItemImpl<n, TypeList<T, List...>>\n    : DropTypeListItemImpl<n - 1, TypeList<List...>> {};\n\ntemplate <typename T, typename... List>\nstruct DropTypeListItemImpl<0, TypeList<T, List...>> {\n  using Type = TypeList<T, List...>;\n};\n\ntemplate <>\nstruct DropTypeListItemImpl<0, TypeList<>> {\n  using Type = TypeList<>;\n};\n\n// A type-level function that drops |n| list item from given TypeList.\ntemplate <size_t n, typename List>\nusing DropTypeListItem = typename DropTypeListItemImpl<n, List>::Type;\n\n// Used for TakeTypeListItem implementation.\ntemplate <size_t n, typename List, typename... Accum>\nstruct TakeTypeListItemImpl;\n\n// Do not use enable_if and SFINAE here to avoid MSVC2013 compile failure.\ntemplate <size_t n, typename T, typename... List, typename... Accum>\nstruct TakeTypeListItemImpl<n, TypeList<T, List...>, Accum...>\n    : TakeTypeListItemImpl<n - 1, TypeList<List...>, Accum..., T> {};\n\ntemplate <typename T, typename... List, typename... Accum>\nstruct TakeTypeListItemImpl<0, TypeList<T, List...>, Accum...> {\n  using Type = TypeList<Accum...>;\n};\n\ntemplate <typename... Accum>\nstruct TakeTypeListItemImpl<0, TypeList<>, Accum...> {\n  using Type = TypeList<Accum...>;\n};\n\n// A type-level function that takes first |n| list item from given TypeList.\n// E.g. TakeTypeListItem<3, TypeList<A, B, C, D>> is evaluated to\n// TypeList<A, B, C>.\ntemplate <size_t n, typename List>\nusing TakeTypeListItem = typename TakeTypeListItemImpl<n, List>::Type;\n\n// Used for ConcatTypeLists implementation.\ntemplate <typename List1, typename List2>\nstruct ConcatTypeListsImpl;\n\ntemplate <typename... Types1, typename... Types2>\nstruct ConcatTypeListsImpl<TypeList<Types1...>, TypeList<Types2...>> {\n  using Type = TypeList<Types1..., Types2...>;\n};\n\n// A type-level function that concats two TypeLists.\ntemplate <typename List1, typename List2>\nusing ConcatTypeLists = typename ConcatTypeListsImpl<List1, List2>::Type;\n\n// Used for ConvertTypeListToTuple implementation.\ntemplate <typename... List>\nstruct ConvertTypeListToTupleImpl;\n\ntemplate <typename... List>\nstruct ConvertTypeListToTupleImpl<TypeList<List...>> {\n  using Type = std::tuple<List...>;\n};\n\n// A type-level function that converts TypeList to std::tuple.\ntemplate <typename List>\nusing ConvertTypeListToTuple = typename ConvertTypeListToTupleImpl<List>::Type;\n\n// Used for GetType implementation.\ntemplate <size_t idx, typename... List>\nstruct GetTypeImpl;\n\ntemplate <size_t idx, typename T, typename... List>\nstruct GetTypeImpl<idx, TypeList<T, List...>>\n    : GetTypeImpl<idx - 1, TypeList<List...>> {};\n\ntemplate <typename T, typename... List>\nstruct GetTypeImpl<0, TypeList<T, List...>> {\n  using Type = T;\n};\n\n// A type-level function that converts TypeList to std::tuple.\ntemplate <size_t idx, typename List>\nusing GetType = typename GetTypeImpl<idx, List>::Type;\n\ntemplate <typename... Types>\nstruct GetSizeImpl {};\n\ntemplate <typename... Types>\nstruct GetSizeImpl<TypeList<Types...>> {\n  static constexpr size_t value = sizeof...(Types);\n};\n\ntemplate <typename List>\ninline constexpr size_t GetSize = GetSizeImpl<List>::value;\n\n}  // namespace tachyon::base::internal\n\n#endif  //  TACHYON_BASE_TYPE_LIST_H_\n"
  },
  {
    "path": "tachyon/base/types/.clang-format",
    "content": "DisableFormat: true\nSortIncludes: false"
  },
  {
    "path": "tachyon/base/types/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"always_false\",\n    hdrs = [\"always_false.h\"],\n)\n\ntachyon_cc_library(\n    name = \"cxx20_is_bounded_array\",\n    hdrs = [\"cxx20_is_bounded_array.h\"],\n)\n"
  },
  {
    "path": "tachyon/base/types/always_false.h",
    "content": "// Copyright 2022 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BASE_TYPES_ALWAYS_FALSE_H_\n#define TACHYON_BASE_TYPES_ALWAYS_FALSE_H_\n\nnamespace tachyon::base {\n\n// A helper that can be used with a static_assert() that must always fail (e.g.\n// for an undesirable template instantiation). Such a static_assert() cannot\n// simply be written as static_assert(false, ...) because that would always fail\n// to compile, even if the template was never instantiated. Instead, a common\n// idiom is to force the static_assert() to depend on a template parameter so\n// that it is only evaluated when the template is instantiated:\n//\n// template <typename U = T>\n// void SomeDangerousMethodThatShouldNeverCompile() {\n//   static_assert(base::AlwaysFalse<U>, \"explanatory message here\");\n// }\n//\n//\n// The issue of not being able to use static_assert(false, ...) in a\n// non-instantiated template was fixed in C++23. When Chromium switches to\n// building with C++23, remove this file and use false directly, and search\n// across the Chromium codebase for \"AlwaysFalse\", as there are other\n// implementations in places that cannot depend on this file.\n//\n// References:\n// - https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2593r0.html\n// - https://github.com/cplusplus/papers/issues/1251\n\nnamespace internal {\n\ntemplate <typename... Args>\nstruct AlwaysFalseHelper {\n  static constexpr bool kValue = false;\n};\n\n}  // namespace internal\n\ntemplate <typename... Args>\ninline constexpr bool AlwaysFalse =\n    internal::AlwaysFalseHelper<Args...>::kValue;\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_TYPES_ALWAYS_FALSE_H_\n"
  },
  {
    "path": "tachyon/base/types/cxx20_is_bounded_array.h",
    "content": "#ifndef TACHYON_BASE_TYPES_CXX20_IS_BOUNDED_ARRAY_H_\n#define TACHYON_BASE_TYPES_CXX20_IS_BOUNDED_ARRAY_H_\n\n#include <stddef.h>\n\n#include <type_traits>\n\nnamespace tachyon::base {\n\n// Implementation of C++20's std::is_bounded_array.\n//\n// References:\n// - https://en.cppreference.com/w/cpp/types/is_bounded_array\ntemplate <typename T>\nstruct is_bounded_array : std::false_type {};\n\ntemplate <typename T, size_t N>\nstruct is_bounded_array<T[N]> : std::true_type {};\n\ntemplate <typename T>\ninline constexpr bool is_bounded_array_v = is_bounded_array<T>::value;\n\n}  // namespace tachyon::base\n\n#endif  // TACHYON_BASE_TYPES_CXX20_IS_BOUNDED_ARRAY_H_\n"
  },
  {
    "path": "tachyon/build/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\nload(\":lastchange.bzl\", \"lastchange\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\n    \"lastchange.py\",\n    \"write_buildflag_header.py\",\n    \"write_version_header.py\",\n])\n\nexports_files([\n    \"write_version_header.py\",\n])\n\nlastchange(\n    name = \"lastchange\",\n)\n\ntachyon_cc_library(\n    name = \"build_config\",\n    hdrs = [\"build_config.h\"],\n    deps = [\":buildflag\"],\n)\n\ntachyon_cc_library(\n    name = \"buildflag\",\n    hdrs = [\"buildflag.h\"],\n)\n\ntachyon_cc_library(\n    name = \"cc_writer\",\n    srcs = [\"cc_writer.cc\"],\n    hdrs = [\"cc_writer.h\"],\n    deps = [\n        \":writer\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/base/files:file_path\",\n        \"//tachyon/base/strings:string_util\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"writer\",\n    srcs = [\"writer.cc\"],\n    hdrs = [\"writer.h\"],\n    deps = [\"//tachyon/base/files:file_util\"],\n)\n"
  },
  {
    "path": "tachyon/build/build_config.h",
    "content": "// Copyright (c) 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n// This file adds build flags about the OS we're currently building on. They\n// are defined using the corresponding OS defines\n// (e.g. OS_WIN) which are also defined in this file (except for OS_CHROMEOS,\n// which is set by the build system). These defines are deprecated and should\n// NOT be used directly. For example:\n//    Please Use: #if BUILDFLAG(IS_WIN)\n//    Deprecated: #if defined(OS_WIN)\n//\n//  Operating System:\n//    IS_AIX / IS_ANDROID / IS_ASMJS / IS_CHROMEOS / IS_FREEBSD / IS_FUCHSIA /\n//    IS_IOS / IS_IOS_MACCATALYST / IS_LINUX / IS_MAC / IS_NACL / IS_NETBSD /\n//    IS_OPENBSD / IS_QNX / IS_SOLARIS / IS_WIN\n//  Operating System family:\n//    IS_APPLE: IOS or MAC or IOS_MACCATALYST\n//    IS_BSD: FREEBSD or NETBSD or OPENBSD\n//    IS_POSIX: AIX or ANDROID or ASMJS or CHROMEOS or FREEBSD or IOS or LINUX\n//              or MAC or NACL or NETBSD or OPENBSD or QNX or SOLARIS\n\n// This file also adds defines specific to the platform, architecture etc.\n//\n//  Platform:\n//    IS_OZONE\n//\n//  Compiler:\n//    COMPILER_MSVC / COMPILER_GCC\n//\n//  Processor:\n//    ARCH_CPU_ARM64 / ARCH_CPU_ARMEL / ARCH_CPU_LOONGARCH32 /\n//    ARCH_CPU_LOONGARCH64 / ARCH_CPU_MIPS / ARCH_CPU_MIPS64 /\n//    ARCH_CPU_MIPS64EL / ARCH_CPU_MIPSEL / ARCH_CPU_PPC64 / ARCH_CPU_S390 /\n//    ARCH_CPU_S390X / ARCH_CPU_X86 / ARCH_CPU_X86_64 / ARCH_CPU_RISCV64\n//  Processor family:\n//    ARCH_CPU_ARM_FAMILY: ARMEL or ARM64\n//    ARCH_CPU_LOONGARCH_FAMILY: LOONGARCH32 or LOONGARCH64\n//    ARCH_CPU_MIPS_FAMILY: MIPS64EL or MIPSEL or MIPS64 or MIPS\n//    ARCH_CPU_PPC64_FAMILY: PPC64\n//    ARCH_CPU_S390_FAMILY: S390 or S390X\n//    ARCH_CPU_X86_FAMILY: X86 or X86_64\n//    ARCH_CPU_RISCV_FAMILY: Riscv64\n//  Processor features:\n//    ARCH_CPU_31_BITS / ARCH_CPU_32_BITS / ARCH_CPU_64_BITS\n//    ARCH_CPU_BIG_ENDIAN / ARCH_CPU_LITTLE_ENDIAN\n\n#ifndef TACHYON_BUILD_BUILD_CONFIG_H_\n#define TACHYON_BUILD_BUILD_CONFIG_H_\n\n#include \"tachyon/build/buildflag.h\"\n\n// A set of macros to use for platform detection.\n#if defined(__native_client__)\n// __native_client__ must be first, so that other OS_ defines are not set.\n#define OS_NACL 1\n#elif defined(ANDROID)\n#define OS_ANDROID 1\n#elif defined(__APPLE__)\n// Only include TargetConditionals after testing ANDROID as some Android builds\n// on the Mac have this header available and it's not needed unless the target\n// is really an Apple platform.\n#include <TargetConditionals.h>\n#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE\n#define OS_IOS 1\n// Catalyst is the technology that allows running iOS apps on macOS. These\n// builds are both OS_IOS and OS_IOS_MACCATALYST.\n#if defined(TARGET_OS_MACCATALYST) && TARGET_OS_MACCATALYST\n#define OS_IOS_MACCATALYST\n#endif  // defined(TARGET_OS_MACCATALYST) && TARGET_OS_MACCATALYST\n#else\n#define OS_MAC 1\n#endif  // defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE\n#elif defined(__linux__)\n#if !defined(OS_CHROMEOS)\n// Do not define OS_LINUX on Chrome OS build.\n// The OS_CHROMEOS macro is defined in GN.\n#define OS_LINUX 1\n#endif  // !defined(OS_CHROMEOS)\n// Include a system header to pull in features.h for glibc/uclibc macros.\n#include <assert.h>\n#if defined(__GLIBC__) && !defined(__UCLIBC__)\n// We really are using glibc, not uClibc pretending to be glibc.\n#define LIBC_GLIBC 1\n#endif\n#elif defined(_WIN32)\n#define OS_WIN 1\n#elif defined(__Fuchsia__)\n#define OS_FUCHSIA 1\n#elif defined(__FreeBSD__)\n#define OS_FREEBSD 1\n#elif defined(__NetBSD__)\n#define OS_NETBSD 1\n#elif defined(__OpenBSD__)\n#define OS_OPENBSD 1\n#elif defined(__sun)\n#define OS_SOLARIS 1\n#elif defined(__QNXNTO__)\n#define OS_QNX 1\n#elif defined(_AIX)\n#define OS_AIX 1\n#elif defined(__asmjs__) || defined(__wasm__)\n#define OS_ASMJS 1\n#elif defined(__MVS__)\n#define OS_ZOS 1\n#else\n#error Please add support for your platform in build/build_config.h\n#endif\n\n#if defined(OS_MAC) || defined(OS_IOS)\n#define OS_APPLE 1\n#endif\n\n// For access to standard BSD features, use OS_BSD instead of a\n// more specific macro.\n#if defined(OS_FREEBSD) || defined(OS_NETBSD) || defined(OS_OPENBSD)\n#define OS_BSD 1\n#endif\n\n// For access to standard POSIXish features, use OS_POSIX instead of a\n// more specific macro.\n#if defined(OS_AIX) || defined(OS_ANDROID) || defined(OS_ASMJS) ||  \\\n    defined(OS_FREEBSD) || defined(OS_IOS) || defined(OS_LINUX) ||  \\\n    defined(OS_CHROMEOS) || defined(OS_MAC) || defined(OS_NACL) ||  \\\n    defined(OS_NETBSD) || defined(OS_OPENBSD) || defined(OS_QNX) || \\\n    defined(OS_SOLARIS) || defined(OS_ZOS)\n#define OS_POSIX 1\n#endif\n\n// OS build flags\n#if defined(OS_AIX)\n#define BUILDFLAG_INTERNAL_IS_AIX() (1)\n#else\n#define BUILDFLAG_INTERNAL_IS_AIX() (0)\n#endif\n\n#if defined(OS_ANDROID)\n#define BUILDFLAG_INTERNAL_IS_ANDROID() (1)\n#else\n#define BUILDFLAG_INTERNAL_IS_ANDROID() (0)\n#endif\n\n#if defined(OS_APPLE)\n#define BUILDFLAG_INTERNAL_IS_APPLE() (1)\n#else\n#define BUILDFLAG_INTERNAL_IS_APPLE() (0)\n#endif\n\n#if defined(OS_ASMJS)\n#define BUILDFLAG_INTERNAL_IS_ASMJS() (1)\n#else\n#define BUILDFLAG_INTERNAL_IS_ASMJS() (0)\n#endif\n\n#if defined(OS_BSD)\n#define BUILDFLAG_INTERNAL_IS_BSD() (1)\n#else\n#define BUILDFLAG_INTERNAL_IS_BSD() (0)\n#endif\n\n#if defined(OS_CHROMEOS)\n#define BUILDFLAG_INTERNAL_IS_CHROMEOS() (1)\n#else\n#define BUILDFLAG_INTERNAL_IS_CHROMEOS() (0)\n#endif\n\n#if defined(OS_FREEBSD)\n#define BUILDFLAG_INTERNAL_IS_FREEBSD() (1)\n#else\n#define BUILDFLAG_INTERNAL_IS_FREEBSD() (0)\n#endif\n\n#if defined(OS_FUCHSIA)\n#define BUILDFLAG_INTERNAL_IS_FUCHSIA() (1)\n#else\n#define BUILDFLAG_INTERNAL_IS_FUCHSIA() (0)\n#endif\n\n#if defined(OS_IOS)\n#define BUILDFLAG_INTERNAL_IS_IOS() (1)\n#else\n#define BUILDFLAG_INTERNAL_IS_IOS() (0)\n#endif\n\n#if defined(OS_IOS_MACCATALYST)\n#define BUILDFLAG_INTERNAL_IS_IOS_MACCATALYST() (1)\n#else\n#define BUILDFLAG_INTERNAL_IS_IOS_MACCATALYST() (0)\n#endif\n\n#if defined(OS_LINUX)\n#define BUILDFLAG_INTERNAL_IS_LINUX() (1)\n#else\n#define BUILDFLAG_INTERNAL_IS_LINUX() (0)\n#endif\n\n#if defined(OS_MAC)\n#define BUILDFLAG_INTERNAL_IS_MAC() (1)\n#else\n#define BUILDFLAG_INTERNAL_IS_MAC() (0)\n#endif\n\n#if defined(OS_NACL)\n#define BUILDFLAG_INTERNAL_IS_NACL() (1)\n#else\n#define BUILDFLAG_INTERNAL_IS_NACL() (0)\n#endif\n\n#if defined(OS_NETBSD)\n#define BUILDFLAG_INTERNAL_IS_NETBSD() (1)\n#else\n#define BUILDFLAG_INTERNAL_IS_NETBSD() (0)\n#endif\n\n#if defined(OS_OPENBSD)\n#define BUILDFLAG_INTERNAL_IS_OPENBSD() (1)\n#else\n#define BUILDFLAG_INTERNAL_IS_OPENBSD() (0)\n#endif\n\n#if defined(OS_POSIX)\n#define BUILDFLAG_INTERNAL_IS_POSIX() (1)\n#else\n#define BUILDFLAG_INTERNAL_IS_POSIX() (0)\n#endif\n\n#if defined(OS_QNX)\n#define BUILDFLAG_INTERNAL_IS_QNX() (1)\n#else\n#define BUILDFLAG_INTERNAL_IS_QNX() (0)\n#endif\n\n#if defined(OS_SOLARIS)\n#define BUILDFLAG_INTERNAL_IS_SOLARIS() (1)\n#else\n#define BUILDFLAG_INTERNAL_IS_SOLARIS() (0)\n#endif\n\n#if defined(OS_WIN)\n#define BUILDFLAG_INTERNAL_IS_WIN() (1)\n#else\n#define BUILDFLAG_INTERNAL_IS_WIN() (0)\n#endif\n\n#if defined(USE_OZONE)\n#define BUILDFLAG_INTERNAL_IS_OZONE() (1)\n#else\n#define BUILDFLAG_INTERNAL_IS_OZONE() (0)\n#endif\n\n// Compiler detection. Note: clang masquerades as GCC on POSIX and as MSVC on\n// Windows.\n#if defined(__GNUC__)\n#define COMPILER_GCC 1\n#elif defined(_MSC_VER)\n#define COMPILER_MSVC 1\n#else\n#error Please add support for your compiler in build/build_config.h\n#endif\n\n// Processor architecture detection.  For more info on what's defined, see:\n//   http://msdn.microsoft.com/en-us/library/b0084kay.aspx\n//   http://www.agner.org/optimize/calling_conventions.pdf\n//   or with gcc, run: \"echo | gcc -E -dM -\"\n#if defined(_M_X64) || defined(__x86_64__)\n#define ARCH_CPU_X86_FAMILY 1\n#define ARCH_CPU_X86_64 1\n#define ARCH_CPU_64_BITS 1\n#define ARCH_CPU_LITTLE_ENDIAN 1\n#elif defined(_M_IX86) || defined(__i386__)\n#define ARCH_CPU_X86_FAMILY 1\n#define ARCH_CPU_X86 1\n#define ARCH_CPU_32_BITS 1\n#define ARCH_CPU_LITTLE_ENDIAN 1\n#elif defined(__s390x__)\n#define ARCH_CPU_S390_FAMILY 1\n#define ARCH_CPU_S390X 1\n#define ARCH_CPU_64_BITS 1\n#define ARCH_CPU_BIG_ENDIAN 1\n#elif defined(__s390__)\n#define ARCH_CPU_S390_FAMILY 1\n#define ARCH_CPU_S390 1\n#define ARCH_CPU_31_BITS 1\n#define ARCH_CPU_BIG_ENDIAN 1\n#elif (defined(__PPC64__) || defined(__PPC__)) && defined(__BIG_ENDIAN__)\n#define ARCH_CPU_PPC64_FAMILY 1\n#define ARCH_CPU_PPC64 1\n#define ARCH_CPU_64_BITS 1\n#define ARCH_CPU_BIG_ENDIAN 1\n#elif defined(__PPC64__)\n#define ARCH_CPU_PPC64_FAMILY 1\n#define ARCH_CPU_PPC64 1\n#define ARCH_CPU_64_BITS 1\n#define ARCH_CPU_LITTLE_ENDIAN 1\n#elif defined(__ARMEL__)\n#define ARCH_CPU_ARM_FAMILY 1\n#define ARCH_CPU_ARMEL 1\n#define ARCH_CPU_32_BITS 1\n#define ARCH_CPU_LITTLE_ENDIAN 1\n#elif defined(__aarch64__) || defined(_M_ARM64)\n#define ARCH_CPU_ARM_FAMILY 1\n#define ARCH_CPU_ARM64 1\n#define ARCH_CPU_64_BITS 1\n#define ARCH_CPU_LITTLE_ENDIAN 1\n#elif defined(__pnacl__) || defined(__asmjs__) || defined(__wasm__)\n#define ARCH_CPU_32_BITS 1\n#define ARCH_CPU_LITTLE_ENDIAN 1\n#elif defined(__MIPSEL__)\n#if defined(__LP64__)\n#define ARCH_CPU_MIPS_FAMILY 1\n#define ARCH_CPU_MIPS64EL 1\n#define ARCH_CPU_64_BITS 1\n#define ARCH_CPU_LITTLE_ENDIAN 1\n#else\n#define ARCH_CPU_MIPS_FAMILY 1\n#define ARCH_CPU_MIPSEL 1\n#define ARCH_CPU_32_BITS 1\n#define ARCH_CPU_LITTLE_ENDIAN 1\n#endif\n#elif defined(__MIPSEB__)\n#if defined(__LP64__)\n#define ARCH_CPU_MIPS_FAMILY 1\n#define ARCH_CPU_MIPS64 1\n#define ARCH_CPU_64_BITS 1\n#define ARCH_CPU_BIG_ENDIAN 1\n#else\n#define ARCH_CPU_MIPS_FAMILY 1\n#define ARCH_CPU_MIPS 1\n#define ARCH_CPU_32_BITS 1\n#define ARCH_CPU_BIG_ENDIAN 1\n#endif\n#elif defined(__loongarch__)\n#define ARCH_CPU_LOONGARCH_FAMILY 1\n#define ARCH_CPU_LITTLE_ENDIAN 1\n#if __loongarch_grlen == 64\n#define ARCH_CPU_LOONGARCH64 1\n#define ARCH_CPU_64_BITS 1\n#else\n#define ARCH_CPU_LOONGARCH32 1\n#define ARCH_CPU_32_BITS 1\n#endif\n#elif defined(__riscv) && (__riscv_xlen == 64)\n#define ARCH_CPU_RISCV_FAMILY 1\n#define ARCH_CPU_RISCV64 1\n#define ARCH_CPU_64_BITS 1\n#define ARCH_CPU_LITTLE_ENDIAN 1\n#else\n#error Please add support for your architecture in build/build_config.h\n#endif\n\n// Type detection for wchar_t.\n#if defined(OS_WIN)\n#define WCHAR_T_IS_UTF16\n#elif defined(OS_FUCHSIA)\n#define WCHAR_T_IS_UTF32\n#elif defined(OS_POSIX) && defined(COMPILER_GCC) && defined(__WCHAR_MAX__) && \\\n    (__WCHAR_MAX__ == 0x7fffffff || __WCHAR_MAX__ == 0xffffffff)\n#define WCHAR_T_IS_UTF32\n#elif defined(OS_POSIX) && defined(COMPILER_GCC) && defined(__WCHAR_MAX__) && \\\n    (__WCHAR_MAX__ == 0x7fff || __WCHAR_MAX__ == 0xffff)\n// On Posix, we'll detect short wchar_t, but projects aren't guaranteed to\n// compile in this mode (in particular, Chrome doesn't). This is intended for\n// other projects using base who manage their own dependencies and make sure\n// short wchar works for them.\n#define WCHAR_T_IS_UTF16\n#else\n#error Please add support for your compiler in build/build_config.h\n#endif\n\n#endif  // TACHYON_BUILD_BUILD_CONFIG_H_\n"
  },
  {
    "path": "tachyon/build/buildflag.bzl",
    "content": "def attrs():\n    return {\n        \"out\": attr.output(mandatory = True),\n        \"_tool\": attr.label(\n            cfg = \"exec\",\n            executable = True,\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/build:write_buildflag_header.py\"),\n        ),\n    }\n\ndef gen_buildflag_header_helper(ctx, flags):\n    content = \"--flags \" + \" \".join(flags)\n    definition = \"%s.definition\" % (ctx.attr.name)\n    out = ctx.outputs.out\n    header_guard = out.path[len(ctx.genfiles_dir.path) + 1:]\n\n    ctx.actions.run_shell(\n        tools = [ctx.executable._tool],\n        outputs = [out],\n        progress_message = \"Generating buildflag %s\" % (out.short_path),\n        command = \"echo '%s' > %s &&  %s --header-guard %s --rulename %s --definition %s --output %s --gen-dir %s && rm %s\" % (\n            content,\n            definition,\n            ctx.executable._tool.path,\n            header_guard,  # --header-guard\n            ctx.build_file_path,  # --rulename\n            definition,  # --defintion\n            out.basename,  # --output\n            out.dirname,  #  --gen-dir\n            definition,\n        ),\n    )\n\n    return [DefaultInfo(files = depset([out]))]\n"
  },
  {
    "path": "tachyon/build/buildflag.h",
    "content": "// Copyright 2015 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_BUILD_BUILDFLAG_H_\n#define TACHYON_BUILD_BUILDFLAG_H_\n\n// These macros un-mangle the names of the build flags in a way that looks\n// natural, and gives errors if the flag is not defined. Normally in the\n// preprocessor it's easy to make mistakes that interpret \"you haven't done\n// the setup to know what the flag is\" as \"flag is off\". Normally you would\n// include the generated header rather than include this file directly.\n//\n// This is for use with generated headers. See build/buildflag.bzl.\n\n// This dance of two macros does a concatenation of two preprocessor args using\n// ## doubly indirectly because using ## directly prevents macros in that\n// parameter from being expanded.\n#define BUILDFLAG_CAT_INDIRECT(a, b) a##b\n#define BUILDFLAG_CAT(a, b) BUILDFLAG_CAT_INDIRECT(a, b)\n\n// Accessor for build flags.\n//\n// To test for a value, if the build file specifies:\n//\n//   ENABLE_FOO=true\n//\n// Then you would check at build-time in source code with:\n//\n//   #include \"foo_flags.h\"  // The header the build file specified.\n//\n//   #if BUILDFLAG(ENABLE_FOO)\n//     ...\n//   #endif\n//\n// There will no #define called ENABLE_FOO so if you accidentally test for\n// whether that is defined, it will always be negative. You can also use\n// the value in expressions:\n//\n//   const char kSpamServerName[] = BUILDFLAG(SPAM_SERVER_NAME);\n//\n// Because the flag is accessed as a preprocessor macro with (), an error\n// will be thrown if the proper header defining the internal flag value has\n// not been included.\n#define BUILDFLAG(flag) (BUILDFLAG_CAT(BUILDFLAG_INTERNAL_, flag)())\n\n#endif  // TACHYON_BUILD_BUILDFLAG_H_\n"
  },
  {
    "path": "tachyon/build/cc_writer.cc",
    "content": "#include \"tachyon/build/cc_writer.h\"\n\n#include <string>\n#include <vector>\n\n#include \"absl/strings/str_join.h\"\n#include \"absl/strings/str_replace.h\"\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/strings/string_util.h\"\n\nnamespace tachyon::build {\n\nnamespace {\n\n// clang-format off\n// Case 1. If your build target tries to generate //tachyon/math/elliptic_curves/bn/bn254/g1.h,\n// then |components| will be [\"bazel-out\", \"k8-fastbuild\", \"bin\", \"tachyon\", \"math\", \"elliptic_curves\", \"bn\", \"bn254\", \"g1.h\"]\n// Case 2. If your build target tries to generate @kroma_network_tachyon//tachyon/math/elliptic_curves/bn/bn254/g1.h,\n// then |components| will be [\"bazel-out\", \"k8-fastbuild\", \"bin\", \"external\", \"kroma_network_tachyon\", \"tachyon\", \"math\", \"elliptic_curves\", \"bn\", \"bn254\", \"g1.h\"]\n// This functions returns 3 for Case 1 and 5 for Case 5.\n// clang-format on\nsize_t CountBazelParts(const std::vector<std::string>& components) {\n  CHECK_EQ(components[0], \"bazel-out\");\n  CHECK_EQ(components[2], \"bin\");\n  if (components[3] == \"external\") {\n    return 5;\n  }\n  return 3;\n}\n\nbase::FilePath BazelOutToHdrPath(const base::FilePath& out) {\n  std::vector<std::string> components = out.GetComponents();\n  size_t non_bazel_part = CountBazelParts(components);\n  base::FilePath header_path(absl::StrJoin(components.begin() + non_bazel_part,\n                                           components.end() - 1,\n                                           base::FilePath::kSeparators));\n  header_path = header_path.Append(\n      absl::StrCat(out.BaseName().RemoveExtension().value(), \".h\"));\n  return header_path;\n}\n\nstd::string BazelOutToHdrGuardMacro(const base::FilePath& out) {\n  std::vector<std::string> components = out.GetComponents();\n  size_t non_bazel_part = CountBazelParts(components);\n  base::FilePath header_path(absl::StrJoin(components.begin() + non_bazel_part,\n                                           components.end() - 1,\n                                           base::FilePath::kSeparators));\n  // In case of .cu.h, it removes extension twice.\n  base::FilePath basename = out.BaseName().RemoveExtension().RemoveExtension();\n  return base::ToUpperASCII(\n      absl::StrCat(absl::StrJoin(components.begin() + non_bazel_part,\n                                 components.end() - 1, \"_\"),\n                   absl::Substitute(\"_$0_H_\", basename.value())));\n}\n\n}  // namespace\n\nbase::FilePath CcWriter::GetHdrPath() const { return BazelOutToHdrPath(out); }\n\nint CcWriter::WriteHdr(const std::string& content, bool c_api) const {\n  std::string_view tpl[] = {\n      \"// This is generated by %{generator}\",\n      \"#ifndef %{header_guard_macro}\",\n      \"#define %{header_guard_macro}\",\n      \"\",\n      \"%{content}\",\n      \"%{extern_c_back}\",\n      \"#endif  // %{header_guard_macro}\",\n      \"\",\n  };\n  std::string tpl_content = absl::StrJoin(tpl, \"\\n\");\n\n  std::string_view extern_c_front =\n      \"#ifdef __cplusplus\\n\"\n      \"extern \\\"C\\\" {\\n\"\n      \"#endif\";\n\n  std::string_view extern_c_back =\n      \"\\n\"\n      \"#ifdef __cplusplus\\n\"\n      \"}  // extern \\\"C\\\"\\n\"\n      \"#endif\\n\";\n\n  tpl_content = absl::StrReplaceAll(tpl_content, {\n                                                     {\"%{content}\", content},\n                                                 });\n\n  std::string text = absl::StrReplaceAll(\n      tpl_content,\n      {\n          {\"%{generator}\", generator},\n          {\"%{header_guard_macro}\", BazelOutToHdrGuardMacro(out)},\n          {\"%{extern_c_front}\", c_api ? std::string(extern_c_front) : \"\"},\n          {\"%{extern_c_back}\", c_api ? std::string(extern_c_back) : \"\"},\n      });\n  return Write(text);\n}\n\nint CcWriter::WriteSrc(const std::string& content) const {\n  std::vector<std::string_view> tpl = {\n      \"// This is generated by %{generator}\",\n      \"#include \\\"%{header_path}\\\"\",\n      \"\",\n      \"%{content}\"\n      \"\",\n  };\n  std::string tpl_content = absl::StrJoin(tpl, \"\\n\");\n\n  std::string text = absl::StrReplaceAll(\n      tpl_content, {\n                       {\"%{generator}\", generator},\n                       {\"%{header_path}\", GetHdrPath().value()},\n                       {\"%{content}\", content},\n                   });\n  return Write(text);\n}\n\n// static\nbool CcWriter::DoRemoveOptionalLines(std::vector<std::string>& tpl_lines,\n                                     std::string_view start_line,\n                                     std::string_view end_line,\n                                     bool select_tag_block) {\n  auto result = base::FindIndex(tpl_lines, start_line);\n  if (!result.has_value()) return false;\n  size_t start_idx = result.value();\n\n  result = base::FindIndex(tpl_lines, end_line);\n  CHECK(result.has_value()) << \"The tag starts but not ends\";\n  size_t end_idx = result.value();\n\n  if (select_tag_block) {\n    // Remove the first |start_line| and |end_line|.\n    tpl_lines.erase(tpl_lines.begin() + start_idx);\n    tpl_lines.erase(tpl_lines.begin() + end_idx - 1);\n\n  } else {\n    // Remove lines between |start_line| and |end_line|.\n    tpl_lines.erase(tpl_lines.begin() + start_idx,\n                    tpl_lines.begin() + end_idx + 1);\n  }\n  return true;\n}\n\n// static\nvoid CcWriter::RemoveOptionalLines(std::vector<std::string>& tpl_lines,\n                                   std::string_view tag,\n                                   bool select_tag_block) {\n  std::string start_line = absl::Substitute(\"%{if $0}\", tag);\n  std::string end_line = absl::Substitute(\"%{endif $0}\", tag);\n  bool result = true;\n  while (result) {\n    result = DoRemoveOptionalLines(tpl_lines, start_line, end_line,\n                                   select_tag_block);\n  }\n}\n\n}  // namespace tachyon::build\n"
  },
  {
    "path": "tachyon/build/cc_writer.h",
    "content": "#ifndef TACHYON_BUILD_CC_WRITER_H_\n#define TACHYON_BUILD_CC_WRITER_H_\n\n#include <string>\n#include <vector>\n\n#include \"tachyon/build/writer.h\"\n\nnamespace tachyon::build {\n\nstruct CcWriter : public Writer {\n  base::FilePath GetHdrPath() const;\n\n  // NOTE: You should mark %{extern_c_front} after header inclusion when |c_api|\n  // is true.\n  int WriteHdr(const std::string& content, bool c_api) const;\n  int WriteSrc(const std::string& content) const;\n\n  // Remove all lines equals to \"%{if |tag|}\" or \"%{endif |tag|}\"\n  // if |select_tag_block| is true. Otherwise, remove all lines that are\n  // enclosed between \"%{if |tag|}\" and \"%{endif |tag|}\".\n  static void RemoveOptionalLines(std::vector<std::string>& tpl_lines,\n                                  std::string_view tag, bool select_tag_block);\n\n private:\n  // Remove each first line that equals to |start_line| and |end_line| if\n  // |select_tag_block| is true. Otherwise, remove lines between the lines\n  // (including boundary lines).\n  static bool DoRemoveOptionalLines(std::vector<std::string>& tpl_lines,\n                                    std::string_view start_line,\n                                    std::string_view end_line,\n                                    bool select_tag_block);\n};\n\n}  // namespace tachyon::build\n\n#endif  // TACHYON_BUILD_CC_WRITER_H_\n"
  },
  {
    "path": "tachyon/build/lastchange.bzl",
    "content": "load(\"@local_config_env//:env.bzl\", \"PROJECT_ROOT\")\n\nLastChangeInfo = provider(\n    \"Info needed to extract last revision and commit time.\",\n    fields = [\"lastchange\"],\n)\n\ndef _lastchange_impl(ctx):\n    outputs = [ctx.actions.declare_file(\"LASTCHANGE\"), ctx.actions.declare_file(\"LASTCHANGE.committime\")]\n    ctx.actions.run_shell(\n        tools = [ctx.executable._tool],\n        outputs = outputs,\n        progress_message = \"Generating LASTCHANGE\",\n        command = \"%s --source-dir %s --output LASTCHANGE && mv LASTCHANGE LASTCHANGE.committime %s\" % (\n            ctx.executable._tool.path,\n            PROJECT_ROOT,\n            outputs[0].dirname,\n        ),\n    )\n\n    return [\n        DefaultInfo(files = depset(outputs)),\n        LastChangeInfo(lastchange = depset(outputs)),\n    ]\n\nlastchange = rule(\n    implementation = _lastchange_impl,\n    attrs = {\n        \"_tool\": attr.label(\n            cfg = \"exec\",\n            executable = True,\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/build:lastchange.py\"),\n        ),\n    },\n)\n"
  },
  {
    "path": "tachyon/build/lastchange.py",
    "content": "#!/usr/bin/env python3\n# Copyright (c) 2012 The Chromium Authors\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n\"\"\"\nlastchange.py -- Chromium revision fetching utility.\n\"\"\"\nfrom __future__ import print_function\n\nimport argparse\nimport collections\nimport logging\nimport os\nimport subprocess\nimport sys\n\nVersionInfo = collections.namedtuple(\"VersionInfo\",\n                                     (\"revision_id\", \"revision\", \"timestamp\"))\n\nclass GitError(Exception):\n  pass\n\n# This function exists for compatibility with logic outside this\n# repository that uses this file as a library.\n# TODO(eliribble) remove this function after it has been ported into\n# the repositories that depend on it\ndef RunGitCommand(directory, command):\n  \"\"\"\n  Launches git subcommand.\n  Errors are swallowed.\n  Returns:\n    A process object or None.\n  \"\"\"\n  command = ['git'] + command\n  # Force shell usage under cygwin. This is a workaround for\n  # mysterious loss of cwd while invoking cygwin's git.\n  # We can't just pass shell=True to Popen, as under win32 this will\n  # cause CMD to be used, while we explicitly want a cygwin shell.\n  if sys.platform == 'cygwin':\n    command = ['sh', '-c', ' '.join(command)]\n  try:\n    proc = subprocess.Popen(command,\n                            stdout=subprocess.PIPE,\n                            stderr=subprocess.PIPE,\n                            cwd=directory,\n                            shell=(sys.platform=='win32'))\n    return proc\n  except OSError as e:\n    logging.error('Command %r failed: %s' % (' '.join(command), e))\n    return None\n\n\ndef _RunGitCommand(directory, command):\n  \"\"\"Launches git subcommand.\n  Returns:\n    The stripped stdout of the git command.\n  Raises:\n    GitError on failure, including a nonzero return code.\n  \"\"\"\n  command = ['git'] + command\n  # Force shell usage under cygwin. This is a workaround for\n  # mysterious loss of cwd while invoking cygwin's git.\n  # We can't just pass shell=True to Popen, as under win32 this will\n  # cause CMD to be used, while we explicitly want a cygwin shell.\n  if sys.platform == 'cygwin':\n    command = ['sh', '-c', ' '.join(command)]\n  try:\n    logging.info(\"Executing '%s' in %s\", ' '.join(command), directory)\n    proc = subprocess.Popen(command,\n                            stdout=subprocess.PIPE,\n                            stderr=subprocess.PIPE,\n                            cwd=directory,\n                            shell=(sys.platform=='win32'))\n    stdout, stderr = tuple(x.decode(encoding='utf_8')\n                           for x in proc.communicate())\n    stdout = stdout.strip()\n    logging.debug(\"returncode: %d\", proc.returncode)\n    logging.debug(\"stdout: %s\", stdout)\n    logging.debug(\"stderr: %s\", stderr)\n    if proc.returncode != 0 or not stdout:\n      raise GitError((\n          \"Git command '{}' in {} failed: \"\n          \"rc={}, stdout='{}' stderr='{}'\").format(\n          \" \".join(command), directory, proc.returncode, stdout, stderr))\n    return stdout\n  except OSError as e:\n    raise GitError(\"Git command 'git {}' in {} failed: {}\".format(\n        \" \".join(command), directory, e))\n\n\ndef GetMergeBase(directory, ref):\n  \"\"\"\n  Return the merge-base of HEAD and ref.\n  Args:\n    directory: The directory containing the .git directory.\n    ref: The ref to use to find the merge base.\n  Returns:\n    The git commit SHA of the merge-base as a string.\n  \"\"\"\n  logging.debug(\"Calculating merge base between HEAD and %s in %s\",\n                ref, directory)\n  command = ['merge-base', 'HEAD', ref]\n  return _RunGitCommand(directory, command)\n\n\ndef FetchGitRevision(directory, commit_filter, start_commit=\"HEAD\"):\n  \"\"\"\n  Fetch the Git hash (and Cr-Commit-Position if any) for a given directory.\n  Args:\n    directory: The directory containing the .git directory.\n    commit_filter: A filter to supply to grep to filter commits\n    start_commit: A commit identifier. The result of this function\n      will be limited to only consider commits before the provided\n      commit.\n  Returns:\n    A VersionInfo object. On error all values will be 0.\n  \"\"\"\n  hash_ = ''\n\n  git_args = ['log', '-1', '--format=%H %ct']\n  if commit_filter is not None:\n    git_args.append('--grep=' + commit_filter)\n\n  git_args.append(start_commit)\n\n  output = _RunGitCommand(directory, git_args)\n  hash_, commit_timestamp = output.split()\n  if not hash_:\n    return VersionInfo('0', '0', 0)\n\n  revision = hash_\n  output = _RunGitCommand(directory, ['cat-file', 'commit', hash_])\n  for line in reversed(output.splitlines()):\n    if line.startswith('Cr-Commit-Position:'):\n      pos = line.rsplit()[-1].strip()\n      logging.debug(\"Found Cr-Commit-Position '%s'\", pos)\n      revision = \"{}-{}\".format(hash_, pos)\n      break\n  return VersionInfo(hash_, revision, int(commit_timestamp))\n\n\ndef GetHeaderGuard(path):\n  \"\"\"\n  Returns the header #define guard for the given file path.\n  This treats everything after the last instance of \"src/\" as being a\n  relevant part of the guard. If there is no \"src/\", then the entire path\n  is used.\n  \"\"\"\n  src_index = path.rfind('src/')\n  if src_index != -1:\n    guard = path[src_index + 4:]\n  else:\n    guard = path\n  guard = guard.upper()\n  return guard.replace('/', '_').replace('.', '_').replace('\\\\', '_') + '_'\n\n\ndef GetHeaderContents(path, define, version):\n  \"\"\"\n  Returns what the contents of the header file should be that indicate the given\n  revision.\n  \"\"\"\n  header_guard = GetHeaderGuard(path)\n\n  header_contents = \"\"\"/* Generated by lastchange.py, do not edit.*/\n#ifndef %(header_guard)s\n#define %(header_guard)s\n#define %(define)s \"%(version)s\"\n#endif  // %(header_guard)s\n\"\"\"\n  header_contents = header_contents % { 'header_guard': header_guard,\n                                        'define': define,\n                                        'version': version }\n  return header_contents\n\n\ndef GetGitTopDirectory(source_dir):\n  \"\"\"Get the top git directory - the directory that contains the .git directory.\n  Args:\n    source_dir: The directory to search.\n  Returns:\n    The output of \"git rev-parse --show-toplevel\" as a string\n  \"\"\"\n  return _RunGitCommand(source_dir, ['rev-parse', '--show-toplevel'])\n\n\ndef WriteIfChanged(file_name, contents):\n  \"\"\"\n  Writes the specified contents to the specified file_name\n  iff the contents are different than the current contents.\n  Returns if new data was written.\n  \"\"\"\n  try:\n    old_contents = open(file_name, 'r').read()\n  except EnvironmentError:\n    pass\n  else:\n    if contents == old_contents:\n      return False\n    os.unlink(file_name)\n  open(file_name, 'w').write(contents)\n  return True\n\n\ndef main(argv=None):\n  if argv is None:\n    argv = sys.argv\n\n  parser = argparse.ArgumentParser(usage=\"lastchange.py [options]\")\n  parser.add_argument(\"-m\", \"--version-macro\",\n                    help=(\"Name of C #define when using --header. Defaults to \"\n                          \"LAST_CHANGE.\"))\n  parser.add_argument(\"-o\", \"--output\", metavar=\"FILE\",\n                    help=(\"Write last change to FILE. \"\n                          \"Can be combined with --header to write both files.\"))\n  parser.add_argument(\"--header\", metavar=\"FILE\",\n                    help=(\"Write last change to FILE as a C/C++ header. \"\n                          \"Can be combined with --output to write both files.\"))\n  parser.add_argument(\"--merge-base-ref\",\n                    default=None,\n                    help=(\"Only consider changes since the merge \"\n                          \"base between HEAD and the provided ref\"))\n  parser.add_argument(\"--revision-id-only\", action='store_true',\n                    help=(\"Output the revision as a VCS revision ID only (in \"\n                          \"Git, a 40-character commit hash, excluding the \"\n                          \"Cr-Commit-Position).\"))\n  parser.add_argument(\"--print-only\", action=\"store_true\",\n                    help=(\"Just print the revision string. Overrides any \"\n                          \"file-output-related options.\"))\n  parser.add_argument(\"-s\", \"--source-dir\", metavar=\"DIR\",\n                    help=\"Use repository in the given directory.\")\n  # NOTE(chokobole): Original default value was %Change-Id: to suppress local commits.\n  parser.add_argument(\"--filter\", metavar=\"REGEX\",\n                    help=(\"Only use log entries where the commit message \"\n                          \"matches the supplied filter regex.\"))\n\n  args, extras = parser.parse_known_args(argv[1:])\n\n  logging.basicConfig(level=logging.WARNING)\n\n  out_file = args.output\n  header = args.header\n  commit_filter=args.filter\n\n  while len(extras) and out_file is None:\n    if out_file is None:\n      out_file = extras.pop(0)\n  if extras:\n    sys.stderr.write('Unexpected arguments: %r\\n\\n' % extras)\n    parser.print_help()\n    sys.exit(2)\n\n  source_dir = args.source_dir or os.path.dirname(os.path.abspath(__file__))\n  try:\n    git_top_dir = GetGitTopDirectory(source_dir)\n  except GitError as e:\n    logging.error(\"Failed to get git top directory from '%s': %s\",\n                  source_dir, e)\n    return 2\n\n  if args.merge_base_ref:\n    try:\n      merge_base_sha = GetMergeBase(git_top_dir, args.merge_base_ref)\n    except GitError as e:\n      logging.error(\"You requested a --merge-base-ref value of '%s' but no \"\n                    \"merge base could be found between it and HEAD. Git \"\n                    \"reports: %s\", args.merge_base_ref, e)\n      return 3\n  else:\n    merge_base_sha = 'HEAD'\n\n  try:\n    version_info = FetchGitRevision(git_top_dir, commit_filter, merge_base_sha)\n  except GitError as e:\n    logging.error(\"Failed to get version info: %s\", e)\n    logging.info((\"Falling back to a version of 0.0.0 to allow script to \"\n        \"finish. This is normal if you are bootstrapping a new environment \"\n        \"or do not have a git repository for any other reason. If not, this \"\n        \"could represent a serious error.\"))\n    version_info = VersionInfo('0', '0', 0)\n\n  revision_string = version_info.revision\n  if args.revision_id_only:\n    revision_string = version_info.revision_id\n\n  if args.print_only:\n    print(revision_string)\n  else:\n    contents = \"LASTCHANGE=%s\\n\" % revision_string\n    if not out_file and not args.header:\n      sys.stdout.write(contents)\n    else:\n      if out_file:\n        committime_file = out_file + '.committime'\n        out_changed = WriteIfChanged(out_file, contents)\n        if out_changed or not os.path.exists(committime_file):\n          with open(committime_file, 'w') as timefile:\n            timefile.write(str(version_info.timestamp))\n      if header:\n        WriteIfChanged(header,\n                       GetHeaderContents(header, args.version_macro,\n                                         revision_string))\n\n  return 0\n\n\nif __name__ == '__main__':\n  sys.exit(main())\n"
  },
  {
    "path": "tachyon/build/version.bzl",
    "content": "load(\"//tachyon/build:lastchange.bzl\", \"LastChangeInfo\")\n\ndef _write_version_header_impl(ctx):\n    output = ctx.actions.declare_file(ctx.attr.output)\n\n    arguments = [\n        output.path,\n        ctx.attr.project,\n        str(ctx.attr.major),\n        str(ctx.attr.minor),\n        str(ctx.attr.patch),\n    ]\n    if len(ctx.attr.prerelease) > 0:\n        arguments.append(\"--prerelease=%s\" % (ctx.attr.prerelease))\n    if ctx.attr.lastchange != None:\n        lastchanges = ctx.attr.lastchange[LastChangeInfo].lastchange.to_list()\n        arguments.append(\"--lastchange=%s\" % (lastchanges[0].path))\n    ctx.actions.run(\n        inputs = ctx.files.lastchange,\n        tools = [ctx.executable._tool],\n        executable = ctx.executable._tool,\n        outputs = [output],\n        progress_message = \"Generating %s\" % (output.short_path),\n        arguments = arguments,\n    )\n\n    return [DefaultInfo(files = depset([output]))]\n\nwrite_version_header = rule(\n    implementation = _write_version_header_impl,\n    attrs = {\n        \"_tool\": attr.label(\n            cfg = \"exec\",\n            allow_single_file = True,\n            executable = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/build:write_version_header.py\"),\n        ),\n        \"output\": attr.string(mandatory = True),\n        \"project\": attr.string(mandatory = True),\n        \"major\": attr.int(mandatory = True),\n        \"minor\": attr.int(mandatory = True),\n        \"patch\": attr.int(mandatory = True),\n        \"prerelease\": attr.string(default = \"\"),\n        \"lastchange\": attr.label(providers = [LastChangeInfo]),\n    },\n)\n"
  },
  {
    "path": "tachyon/build/write_buildflag_header.py",
    "content": "#!/usr/bin/env python3\n# Copyright 2015 The Chromium Authors\n# Use of this source code is governed by a BSD-style license that can be\n# found in the LICENSE file.\n\n# This writes headers for build flags. See buildflag_header.gni for usage of\n# this system as a whole.\n#\n# The parameters are passed in a response file so we don't have to worry\n# about command line lengths. The name of the response file is passed on the\n# command line.\n#\n# The format of the response file is:\n#    [--flags <list of one or more flag values>]\n\nimport optparse\nimport os\nimport shlex\n\n\nclass Options:\n  def __init__(self, output, rulename, header_guard, flags):\n    self.output = output\n    self.rulename = rulename\n    self.header_guard = header_guard\n    self.flags = flags\n\n\ndef GetOptions():\n  parser = optparse.OptionParser()\n  parser.add_option('--header-guard', help=\"Header guard.\")\n  parser.add_option('--output', help=\"Output header name inside --gen-dir.\")\n  parser.add_option('--rulename',\n                    help=\"Helpful name of build rule for including in the \" +\n                         \"comment at the top of the file.\")\n  parser.add_option('--gen-dir',\n                    help=\"Path to root of generated file directory tree.\")\n  parser.add_option('--definitions',\n                    help=\"Name of the response file containing the flags.\")\n  cmdline_options, cmdline_flags = parser.parse_args()\n\n  # Compute header guard by replacing some chars with _ and upper-casing.\n  header_guard = cmdline_options.header_guard.upper()\n  header_guard = \\\n      header_guard.replace('/', '_').replace('\\\\', '_').replace('.', '_')\n  header_guard += '_'\n\n  # The actual output file is inside the gen dir.\n  output = os.path.join(cmdline_options.gen_dir, cmdline_options.output)\n\n  # Definition file in GYP is newline separated, in GN they are shell formatted.\n  # shlex can parse both of these.\n  with open(cmdline_options.definitions, 'r') as def_file:\n    defs = shlex.split(def_file.read())\n  flags_index = defs.index('--flags')\n\n  # Everything after --flags are flags. true/false are remapped to 1/0,\n  # everything else is passed through.\n  flags = []\n  for flag in defs[flags_index + 1 :]:\n    equals_index = flag.index('=')\n    key = flag[:equals_index]\n    value = flag[equals_index + 1:]\n\n    # Canonicalize and validate the value.\n    if value == 'true' or value == 'True':\n      value = '1'\n    elif value == 'false' or value == 'False':\n      value = '0'\n    flags.append((key, str(value)))\n\n  return Options(output=output,\n                 rulename=cmdline_options.rulename,\n                 header_guard=header_guard,\n                 flags=flags)\n\n\ndef WriteHeader(options):\n  with open(options.output, 'w') as output_file:\n    output_file.write(\"// Generated by tachyon/build/write_buildflag_header.py\\n\")\n    if options.rulename:\n      output_file.write('// From \"' + options.rulename + '\"\\n')\n\n    output_file.write('\\n#ifndef %s\\n' % options.header_guard)\n    output_file.write('#define %s\\n\\n' % options.header_guard)\n    output_file.write('#include \"tachyon/build/buildflag.h\"\\n\\n')\n\n    for pair in options.flags:\n      output_file.write('#define BUILDFLAG_INTERNAL_%s() (%s)\\n' % pair)\n\n    output_file.write('\\n#endif  // %s\\n' % options.header_guard)\n\n\noptions = GetOptions()\nWriteHeader(options)\n"
  },
  {
    "path": "tachyon/build/write_version_header.py",
    "content": "#!/usr/bin/env python3\n\"\"\"Takes a commit sha and version and writes it in as readable text to a .h file.\"\"\"\n\nimport argparse\nimport os\nimport sys\n\n\ndef write_output(major, minor, patch, prerelease, lastchange, project):  # pylint: disable=too-many-arguments\n    output = [\n        '// Generated by //tachyon/build/version.py',\n        '#ifndef {project}_VERSION_GENERATED_H_',\n        '#define {project}_VERSION_GENERATED_H_\\n',\n        '#define {project}_VERSION_MAJOR {major}',\n        '#define {project}_VERSION_MINOR {minor}',\n        '#define {project}_VERSION_PATCH {patch}',\n    ]\n\n    if prerelease is not None:\n        output.extend(['#define {project}_PRERELEASE {prerelease}'])\n\n    if lastchange is not None:\n        output.extend(['#define {project}_LASTCHANGE {lastchange}'])\n\n    # pylint: disable=line-too-long\n    output.extend([\n        '\\n#define _{project}_VERSION_STRINGIFY(version) _{project}_VERSION_STRINGIFY2(version)',\n        '#define _{project}_VERSION_STRINGIFY2(version) #version\\n',\n        '#define {project}_VERSION {project}_VERSION_MAJOR * 10000 + {project}_VERSION_MINOR * 100 + {project}_VERSION_PATCH',\n        '#define {project}_VERSION_STR _{project}_VERSION_STRINGIFY({project}_VERSION_MAJOR.{project}_VERSION_MINOR.{project}_VERSION_PATCH)',\n    ])\n\n    full_str = '#define {project}_VERSION_FULL_STR _{project}_VERSION_STRINGIFY({project}_VERSION_MAJOR.{project}_VERSION_MINOR.{project}_VERSION_PATCH'\n    if prerelease is not None and lastchange is not None:\n        full_str += '-{project}_PRERELEASE'\n    if lastchange is not None:\n        full_str += '+{project}_LASTCHANGE'\n    full_str += ')'\n    output.append(full_str)\n\n    output.extend(['\\n#endif // {project}_VERSION_GENERATED_H_'])\n    return '\\n'.join(output).format(major=major,\n                                    minor=minor,\n                                    patch=patch,\n                                    prerelease=prerelease,\n                                    lastchange=lastchange,\n                                    project=project)\n\n\ndef main():\n    argument_parser = argparse.ArgumentParser()\n    argument_parser.add_argument('output_file', help='The file to write to')\n    argument_parser.add_argument('project')\n    argument_parser.add_argument('major')\n    argument_parser.add_argument('minor')\n    argument_parser.add_argument('patch')\n    argument_parser.add_argument('--prerelease')\n    argument_parser.add_argument('--lastchange')\n    args = argument_parser.parse_args()\n\n    lastchange = None\n    if args.lastchange is not None:\n        with open(args.lastchange) as file:\n            lastchange = file.read()[len('LASTCHANGE='):].rstrip()\n\n    output = write_output(args.major, args.minor, args.patch, args.prerelease,\n                          lastchange, args.project)\n    current_contents = ''\n    if os.path.isfile(args.output_file):\n        with open(args.output_file, 'r') as current_file:\n            current_contents = current_file.read()\n\n    if current_contents != output:\n        with open(args.output_file, 'w') as output_file:\n            output_file.write(output)\n    return 0\n\n\nif __name__ == '__main__':\n    sys.exit(main())\n"
  },
  {
    "path": "tachyon/build/writer.cc",
    "content": "#include \"tachyon/build/writer.h\"\n\n#include \"tachyon/base/files/file_util.h\"\n\nnamespace tachyon::build {\n\nint Writer::Write(const std::string& content) const {\n  if (!base::WriteFile(out, content)) return 1;\n  return 0;\n}\n\n}  // namespace tachyon::build\n"
  },
  {
    "path": "tachyon/build/writer.h",
    "content": "#ifndef TACHYON_BUILD_WRITER_H_\n#define TACHYON_BUILD_WRITER_H_\n\n#include <string>\n\n#include \"tachyon/base/files/file_path.h\"\n\nnamespace tachyon::build {\n\nstruct Writer {\n  int Write(const std::string& content) const;\n\n  std::string generator;\n  base::FilePath out;\n};\n\n}  // namespace tachyon::build\n\n#endif  // TACHYON_BUILD_WRITER_H_\n"
  },
  {
    "path": "tachyon/c/BUILD.bazel",
    "content": "load(\"@local_config_cuda//cuda:build_defs.bzl\", \"if_cuda\")\nload(\"//bazel:tachyon.bzl\", \"if_c_shared_object\")\nload(\n    \"//bazel:tachyon_cc.bzl\",\n    \"collect_hdrs\",\n    \"tachyon_cc_library\",\n    \"tachyon_cc_shared_library\",\n    \"tachyon_cc_unittest\",\n)\nload(\"//tachyon/build:version.bzl\", \"write_version_header\")\nload(\n    \":version.bzl\",\n    \"VERSION\",\n    \"VERSION_MAJOR\",\n    \"VERSION_MINOR\",\n    \"VERSION_PATCH\",\n    \"VERSION_PRERELEASE\",\n)\n\npackage(default_visibility = [\"//visibility:public\"])\n\nwrite_version_header(\n    name = \"version_generated\",\n    lastchange = \"//tachyon/build:lastchange\",\n    major = VERSION_MAJOR,\n    minor = VERSION_MINOR,\n    output = \"version_generated.h\",\n    patch = VERSION_PATCH,\n    prerelease = VERSION_PRERELEASE,\n    project = \"TACHYON_C\",\n)\n\ntachyon_cc_library(\n    name = \"export\",\n    hdrs = [\"export.h\"],\n)\n\ntachyon_cc_library(\n    name = \"version\",\n    srcs = [\"version.cc\"],\n    hdrs = [\n        \"version.h\",\n        \":version_generated\",\n    ],\n    deps = [\":export\"],\n)\n\ntachyon_cc_shared_library(\n    name = \"tachyon\",\n    linkstatic = True,\n    soversion = VERSION,\n    tags = [\"manual\"],\n    deps = if_c_shared_object([\n        \":version\",\n        \"//tachyon/c/base\",\n        \"//tachyon/c/crypto\",\n        \"//tachyon/c/math\",\n        \"//tachyon/c/zk\",\n    ]) + if_cuda([\n        \"@local_config_cuda//cuda:cudart_static\",\n    ]),\n)\n\ncollect_hdrs(\n    name = \"tachyon_hdrs\",\n    hdrs = [\n        \"api.h\",\n        \"export.h\",\n        \"version.h\",\n        \":version_generated\",\n    ],\n    deps = [\n        \"//tachyon/c/base:base_hdrs\",\n        \"//tachyon/c/crypto:crypto_hdrs\",\n        \"//tachyon/c/math:math_hdrs\",\n        \"//tachyon/c/zk:zk_hdrs\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"c_unittests\",\n    srcs = [\n        \"version_unittest.cc\",\n    ],\n    deps = [\n        \":version\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/c/api.h",
    "content": "#ifndef TACHYON_C_API_H_\n#define TACHYON_C_API_H_\n\n#include \"tachyon/c/math/elliptic_curves/bls12/bls12_381/fq.h\"\n#include \"tachyon/c/math/elliptic_curves/bls12/bls12_381/fr.h\"\n#include \"tachyon/c/math/elliptic_curves/bls12/bls12_381/g1.h\"\n#include \"tachyon/c/math/elliptic_curves/bls12/bls12_381/msm.h\"\n#include \"tachyon/c/math/elliptic_curves/bls12/bls12_381/msm_gpu.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/fq.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/fr.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/msm.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/msm_gpu.h\"\n#include \"tachyon/c/version.h\"\n\n#endif  // TACHYON_C_API_H_\n"
  },
  {
    "path": "tachyon/c/base/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n    name = \"base_hdrs\",\n    srcs = [\"profiler.h\"],\n)\n\ntachyon_cc_library(\n    name = \"base\",\n    deps = [\":profiler\"],\n)\n\ntachyon_cc_library(\n    name = \"type_traits_forward\",\n    hdrs = [\"type_traits_forward.h\"],\n)\n\ntachyon_cc_library(\n    name = \"profiler\",\n    srcs = [\"profiler.cc\"],\n    hdrs = [\n        \"profiler.h\",\n        \"profiler_type_traits.h\",\n    ],\n    deps = [\n        \":type_traits_forward\",\n        \"//tachyon/base:profiler\",\n        \"//tachyon/c:export\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/c/base/profiler.cc",
    "content": "#include \"tachyon/c/base/profiler.h\"\n\n#include \"tachyon/c/base/profiler_type_traits.h\"\n\nusing namespace tachyon;\n\ntachyon_profiler* tachyon_profiler_create(const char* path, size_t path_len) {\n  return c::base::c_cast(\n      new base::Profiler({base::FilePath(std::string_view(path, path_len))}));\n}\n\nvoid tachyon_profiler_destroy(tachyon_profiler* profiler) {\n  delete c::base::native_cast(profiler);\n}\n\nvoid tachyon_profiler_init(tachyon_profiler* profiler) {\n  c::base::native_cast(profiler)->Init();\n}\n\nvoid tachyon_profiler_start(tachyon_profiler* profiler) {\n  c::base::native_cast(profiler)->Start();\n}\n\nvoid tachyon_profiler_stop(tachyon_profiler* profiler) {\n  c::base::native_cast(profiler)->Stop();\n}\n"
  },
  {
    "path": "tachyon/c/base/profiler.h",
    "content": "/**\n * @file profiler.h\n * @brief Profiler interface.\n *\n * This header file provides an interface for profiler.\n *\n * @example profiler.cc\n */\n#ifndef TACHYON_C_BASE_PROFILER_H_\n#define TACHYON_C_BASE_PROFILER_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include \"tachyon/c/export.h\"\n\n/**\n * @struct tachyon_profiler\n * @brief Represents a profiler.\n */\nstruct tachyon_profiler {};\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Creates a profiler.\n * @param path The path to the file to record profiling result.\n * @param path_len The length of the path.\n * @return Pointer to the newly created profiler.\n */\nTACHYON_C_EXPORT tachyon_profiler* tachyon_profiler_create(const char* path,\n                                                           size_t path_len);\n\n/**\n * @brief Destroys a profiler.\n *\n * Frees the resources associated with the profiler. After\n * calling this function, the profiler pointer should not be used anymore.\n *\n * @param profiler A pointer to the profiler to destroy.\n */\nTACHYON_C_EXPORT void tachyon_profiler_destroy(tachyon_profiler* profiler);\n\n/**\n * @brief Initializes a profiler.\n * @param profiler A pointer to the profiler.\n */\nTACHYON_C_EXPORT void tachyon_profiler_init(tachyon_profiler* profiler);\n\n/**\n * @brief Starts a profiler.\n * @param profiler A pointer to the profiler.\n */\nTACHYON_C_EXPORT void tachyon_profiler_start(tachyon_profiler* profiler);\n\n/**\n * @brief Stops a profiler.\n * @param profiler A pointer to the profiler.\n */\nTACHYON_C_EXPORT void tachyon_profiler_stop(tachyon_profiler* profiler);\n\n#ifdef __cplusplus\n}  // extern \"C\"\n#endif\n\n#endif  // TACHYON_C_BASE_PROFILER_H_\n"
  },
  {
    "path": "tachyon/c/base/profiler_type_traits.h",
    "content": "#ifndef TACHYON_C_BASE_PROFILER_TYPE_TRAITS_H_\n#define TACHYON_C_BASE_PROFILER_TYPE_TRAITS_H_\n\n#include \"tachyon/base/profiler.h\"\n#include \"tachyon/c/base/profiler.h\"\n#include \"tachyon/c/base/type_traits_forward.h\"\n\nnamespace tachyon::c::base {\n\ntemplate <>\nstruct TypeTraits<tachyon::base::Profiler> {\n  using CType = tachyon_profiler;\n};\n\ntemplate <>\nstruct TypeTraits<tachyon_profiler> {\n  using NativeType = tachyon::base::Profiler;\n};\n\n}  // namespace tachyon::c::base\n\n#endif  // TACHYON_C_BASE_PROFILER_TYPE_TRAITS_H_\n"
  },
  {
    "path": "tachyon/c/base/type_traits_forward.h",
    "content": "#ifndef TACHYON_C_BASE_TYPE_TRAITS_FORWARD_H_\n#define TACHYON_C_BASE_TYPE_TRAITS_FORWARD_H_\n\nnamespace tachyon::c::base {\n\ntemplate <typename T>\nstruct TypeTraits;\n\ntemplate <typename CType,\n          typename NativeType = typename TypeTraits<CType>::NativeType>\nconst NativeType& native_cast(const CType& c_type) {\n  return reinterpret_cast<const NativeType&>(c_type);\n}\n\ntemplate <typename CType,\n          typename NativeType = typename TypeTraits<CType>::NativeType>\nNativeType& native_cast(CType& c_type) {\n  return reinterpret_cast<NativeType&>(c_type);\n}\n\ntemplate <typename CType,\n          typename NativeType = typename TypeTraits<CType>::NativeType>\nNativeType&& native_cast(CType&& c_type) {\n  return reinterpret_cast<NativeType&&>(c_type);\n}\n\ntemplate <typename CType,\n          typename NativeType = typename TypeTraits<CType>::NativeType>\nconst NativeType* native_cast(const CType* c_type) {\n  return reinterpret_cast<const NativeType*>(c_type);\n}\n\ntemplate <typename CType,\n          typename NativeType = typename TypeTraits<CType>::NativeType>\nNativeType* native_cast(CType* c_type) {\n  return reinterpret_cast<NativeType*>(c_type);\n}\n\ntemplate <typename NativeType,\n          typename CType = typename TypeTraits<NativeType>::CType>\nconst CType& c_cast(const NativeType& native_type) {\n  return reinterpret_cast<const CType&>(native_type);\n}\n\ntemplate <typename NativeType,\n          typename CType = typename TypeTraits<NativeType>::CType>\nCType& c_cast(NativeType& native_type) {\n  return reinterpret_cast<CType&>(native_type);\n}\n\ntemplate <typename NativeType,\n          typename CType = typename TypeTraits<NativeType>::CType>\nCType&& c_cast(NativeType&& native_type) {\n  return reinterpret_cast<CType&&>(native_type);\n}\n\ntemplate <typename NativeType,\n          typename CType = typename TypeTraits<NativeType>::CType>\nconst CType* c_cast(const NativeType* native_type) {\n  return reinterpret_cast<const CType*>(native_type);\n}\n\ntemplate <typename NativeType,\n          typename CType = typename TypeTraits<NativeType>::CType>\nCType* c_cast(NativeType* native_type) {\n  return reinterpret_cast<CType*>(native_type);\n}\n\n}  // namespace tachyon::c::base\n\n#endif  // TACHYON_C_BASE_TYPE_TRAITS_FORWARD_H_\n"
  },
  {
    "path": "tachyon/c/crypto/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n    name = \"crypto_hdrs\",\n    srcs = [\"//tachyon/c/crypto/random:random_hdrs\"],\n)\n\ntachyon_cc_library(\n    name = \"crypto\",\n    deps = [\"//tachyon/c/crypto/random\"],\n)\n"
  },
  {
    "path": "tachyon/c/crypto/commitments/fri/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"two_adic_fri_impl\",\n    hdrs = [\"two_adic_fri_impl.h\"],\n    deps = [\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/crypto/commitments/fri:two_adic_fri\",\n        \"@com_google_absl//absl/debugging:leak_check\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/c/crypto/commitments/fri/two_adic_fri_impl.h",
    "content": "#ifndef TACHYON_C_CRYPTO_COMMITMENTS_FRI_TWO_ADIC_FRI_IMPL_H_\n#define TACHYON_C_CRYPTO_COMMITMENTS_FRI_TWO_ADIC_FRI_IMPL_H_\n\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"absl/debugging/leak_check.h\"\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/crypto/commitments/fri/two_adic_fri.h\"\n\nnamespace tachyon::c::crypto {\n\ntemplate <typename ExtF, typename InputMMCS, typename ChallengeMMCS,\n          typename Challenger>\nclass TwoAdicFRIImpl\n    : public tachyon::crypto::TwoAdicFRI<ExtF, InputMMCS, ChallengeMMCS,\n                                         Challenger> {\n public:\n  using Base =\n      tachyon::crypto::TwoAdicFRI<ExtF, InputMMCS, ChallengeMMCS, Challenger>;\n  using F = typename Base::F;\n  using Commitment = typename Base::Commitment;\n  using ProverData = typename Base::ProverData;\n  using Domain = typename Base::Domain;\n  using FRIProof = typename Base::FRIProof;\n  using OpeningPoints = typename Base::OpeningPoints;\n  using OpenedValues = typename Base::OpenedValues;\n\n  using Base::Base;\n\n  void CosetLDEBatch(Eigen::Map<tachyon::math::RowMajorMatrix<F>>&& matrix,\n                     F shift,\n                     Eigen::Map<tachyon::math::RowMajorMatrix<F>>& lde) {\n    Domain coset = this->GetNaturalDomainForDegree(matrix.rows());\n    coset.domain()->CosetLDEBatch(std::move(matrix), this->config_.log_blowup,\n                                  shift, lde,\n                                  /*reverse_at_last=*/false);\n  }\n\n  using Base::Commit;\n\n  void Commit(\n      std::vector<Eigen::Map<const tachyon::math::RowMajorMatrix<F>>>&& ldes,\n      Commitment* commitment, ProverData** prover_data_out) {\n    // NOTE(chokobole): The caller is responsible for deallocating the memory.\n    // |TwoAdicFri| in Plonky3 is stateless, allowing it to be used in\n    // multithreaded contexts. As a result, the newly created |ProverData|\n    // object cannot be owned by the |TwoAdicFri| instance itself.\n    *prover_data_out = absl::IgnoreLeak(new ProverData());\n    CHECK(this->mmcs_.Commit(std::move(ldes), commitment, *prover_data_out));\n  }\n\n  void CreateOpeningProof(\n      const std::vector<const ProverData*>& prover_data_by_round,\n      const OpeningPoints& points_by_round, Challenger& challenger,\n      OpenedValues* opened_values_out, FRIProof* proof) const {\n    std::vector<std::unique_ptr<ProverData>> prover_data_by_round_tmp =\n        tachyon::base::Map(prover_data_by_round, [](const ProverData* data) {\n          return std::unique_ptr<ProverData>(const_cast<ProverData*>(data));\n        });\n    CHECK(Base::CreateOpeningProof(prover_data_by_round_tmp, points_by_round,\n                                   challenger, opened_values_out, proof));\n    for (std::unique_ptr<ProverData>& prover_data : prover_data_by_round_tmp) {\n      // NOTE(chokobole): The caller is responsible for deallocating the memory.\n      // See the comment in |Commit()|.\n      absl::IgnoreLeak(prover_data.release());\n    }\n  }\n};\n\n}  // namespace tachyon::c::crypto\n\n#endif  // TACHYON_C_CRYPTO_COMMITMENTS_FRI_TWO_ADIC_FRI_IMPL_H_\n"
  },
  {
    "path": "tachyon/c/crypto/random/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n    name = \"random_hdrs\",\n    srcs = [\"rng.h\"],\n)\n\ntachyon_cc_library(\n    name = \"random\",\n    deps = [\":rng\"],\n)\n\ntachyon_cc_library(\n    name = \"rng\",\n    srcs = [\"rng.cc\"],\n    hdrs = [\"rng.h\"],\n    deps = [\n        \"//tachyon/c:export\",\n        \"//tachyon/crypto/random:rng_type\",\n        \"//tachyon/crypto/random/cha_cha20:cha_cha20_rng\",\n        \"//tachyon/crypto/random/xor_shift:xor_shift_rng\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"random_unittests\",\n    srcs = [\"rng_unittest.cc\"],\n    deps = [\n        \":rng\",\n        \"//tachyon/base:random\",\n        \"//tachyon/base/containers:container_util\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/c/crypto/random/rng.cc",
    "content": "#include \"tachyon/c/crypto/random/rng.h\"\n\n#include \"absl/types/span.h\"\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/crypto/random/cha_cha20/cha_cha20_rng.h\"\n#include \"tachyon/crypto/random/rng_type.h\"\n#include \"tachyon/crypto/random/xor_shift/xor_shift_rng.h\"\n\nusing namespace tachyon;\n\nnamespace {\n\nvoid CheckKnownType(uint8_t type) {\n  CHECK(type == TACHYON_RNG_XOR_SHIFT || type == TACHYON_RNG_CHA_CHA20);\n}\n\ntachyon_rng* CreateFromType(uint8_t type) {\n  tachyon_rng* rng = new tachyon_rng;\n  rng->type = type;\n  switch (static_cast<crypto::RNGType>(type)) {\n    case crypto::RNGType::kXORShift:\n      rng->extra = new crypto::XORShiftRNG;\n      return rng;\n    case crypto::RNGType::kChaCha20:\n      rng->extra = new crypto::ChaCha20RNG;\n      return rng;\n  }\n  NOTREACHED();\n  return rng;\n}\n\nsize_t GetStateLen(uint8_t type) {\n  switch (static_cast<crypto::RNGType>(type)) {\n    case crypto::RNGType::kXORShift:\n      return crypto::XORShiftRNG::kStateSize;\n    case crypto::RNGType::kChaCha20:\n      return crypto::ChaCha20RNG::kStateSize;\n  }\n  NOTREACHED();\n  return 0;\n}\n\n}  // namespace\n\ntachyon_rng* tachyon_rng_create_from_seed(uint8_t type, const uint8_t* seed,\n                                          size_t seed_len) {\n  tachyon_rng* rng = CreateFromType(type);\n  CHECK(reinterpret_cast<crypto::RNG*>(rng->extra)\n            ->SetSeed(absl::Span<const uint8_t>(seed, seed_len)));\n  return rng;\n}\n\ntachyon_rng* tachyon_rng_create_from_state(uint8_t type, const uint8_t* state,\n                                           size_t state_len) {\n  tachyon_rng* rng = CreateFromType(type);\n  base::ReadOnlyBuffer buffer(state, state_len);\n  CHECK(reinterpret_cast<crypto::RNG*>(rng->extra)->ReadFromBuffer(buffer));\n  return rng;\n}\n\nvoid tachyon_rng_destroy(tachyon_rng* rng) {\n  CheckKnownType(rng->type);\n  delete reinterpret_cast<crypto::RNG*>(rng->extra);\n  delete rng;\n}\n\nuint32_t tachyon_rng_get_next_u32(tachyon_rng* rng) {\n  CheckKnownType(rng->type);\n  return reinterpret_cast<crypto::RNG*>(rng->extra)->NextUint32();\n}\n\nuint64_t tachyon_rng_get_next_u64(tachyon_rng* rng) {\n  CheckKnownType(rng->type);\n  return reinterpret_cast<crypto::RNG*>(rng->extra)->NextUint64();\n}\n\nvoid tachyon_rng_get_state(const tachyon_rng* rng, uint8_t* state,\n                           size_t* state_len) {\n  *state_len = GetStateLen(rng->type);\n  if (state == nullptr) return;\n  base::Buffer buffer(state, *state_len);\n  CHECK(reinterpret_cast<crypto::RNG*>(rng->extra)->WriteToBuffer(buffer));\n}\n"
  },
  {
    "path": "tachyon/c/crypto/random/rng.h",
    "content": "/**\n * @file rng.h\n * @brief Random Number Generator (RNG) interface.\n *\n * This header file provides an interface for RNG operations, including creating\n * RNG objects from seeds or state, getting random numbers, and managing RNG\n * state.\n *\n * @example rng.cc\n */\n#ifndef TACHYON_C_CRYPTO_RANDOM_RNG_H_\n#define TACHYON_C_CRYPTO_RANDOM_RNG_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include \"tachyon/c/export.h\"\n\n#define TACHYON_RNG_XOR_SHIFT 0\n#define TACHYON_RNG_CHA_CHA20 1\n\n/**\n * @struct tachyon_rng\n * @brief Represents a random number generator.\n */\nstruct tachyon_rng {\n  uint8_t type;\n  void* extra;\n};\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Creates a random number generator from the given seed.\n * @param type The type of random number generator.\n * @param seed Seed data in array of bytes.\n * @param seed_len The length of the seed.\n * @return Pointer to the newly created random number generator.\n */\nTACHYON_C_EXPORT tachyon_rng* tachyon_rng_create_from_seed(uint8_t type,\n                                                           const uint8_t* seed,\n                                                           size_t seed_len);\n\n/**\n * @brief Creates a random number generator from the given state.\n *\n * This function allows the creation of a random number generator (RNG) with a\n * predefined internal state, enabling reproducible RNG sequences.\n *\n * @param type The random number generator type.\n * @param state The array representing the RNG's internal state.\n * @param state_len The length of the state array.\n * @return Pointer to the newly created random number generator.\n */\nTACHYON_C_EXPORT tachyon_rng* tachyon_rng_create_from_state(\n    uint8_t type, const uint8_t* state, size_t state_len);\n\n/**\n * @brief Destroys a random number generator.\n *\n * Frees the resources associated with the random number generator. After\n * calling this function, the rng pointer should not be used anymore.\n *\n * @param rng A pointer to the random number generator to destroy.\n */\nTACHYON_C_EXPORT void tachyon_rng_destroy(tachyon_rng* rng);\n\n/**\n * @brief Generates a 32-bit unsigned integer from the random number generator.\n *\n * This function returns a randomly generated 32-bit unsigned integer using the\n * specified RNG. The randomness and distribution of the number depend on the\n * RNG's implementation.\n *\n * @param rng A pointer to the random number generator.\n * @return A 32-bit unsigned integer generated by the RNG.\n */\nTACHYON_C_EXPORT uint32_t tachyon_rng_get_next_u32(tachyon_rng* rng);\n\n/**\n * @brief Generates a 64-bit unsigned integer from the random number generator.\n *\n * This function returns a randomly generated 64-bit unsigned integer using the\n * specified RNG. The randomness and distribution of the number depend on the\n * RNG's implementation.\n *\n * @param rng A pointer to the random number generator.\n * @return A 64-bit unsigned integer generated by the RNG.\n */\nTACHYON_C_EXPORT uint64_t tachyon_rng_get_next_u64(tachyon_rng* rng);\n\n/**\n * @brief Retrieves the current state of the random number generator.\n *\n * This function is used to obtain the current internal state of the RNG. This\n * state can be used to create a new RNG instance that continues generating\n * random numbers from the same point.\n *\n * If the @p state parameter is NULL, the function populates @p state_len with\n * the necessary length to hold the state. This can be used to allocate enough\n * storage before calling the function again with a non-NULL @p state.\n *\n * @param rng A const pointer to the random number generator.\n * @param state A pointer to the buffer where the RNG's state will be stored. If\n * NULL, the function only populates @p state_len.\n * @param state_len A pointer to a variable where the length of the state will\n * be stored. If @p state is not NULL, it should be initialized to the size of\n * the buffer pointed to by @p state.\n */\nTACHYON_C_EXPORT void tachyon_rng_get_state(const tachyon_rng* rng,\n                                            uint8_t* state, size_t* state_len);\n\n#ifdef __cplusplus\n}  // extern \"C\"\n#endif\n\n#endif  // TACHYON_C_CRYPTO_RANDOM_RNG_H_\n"
  },
  {
    "path": "tachyon/c/crypto/random/rng_unittest.cc",
    "content": "#include \"tachyon/c/crypto/random/rng.h\"\n\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/random.h\"\n#include \"tachyon/crypto/random/cha_cha20/cha_cha20_rng.h\"\n#include \"tachyon/crypto/random/xor_shift/xor_shift_rng.h\"\n\nnamespace tachyon::crypto {\n\ntemplate <typename Rng>\nclass RngTest : public testing::Test {\n public:\n  void TearDown() override {\n    tachyon_rng_destroy(rng_);\n    tachyon_rng_destroy(rng_clone_);\n  }\n\n protected:\n  tachyon_rng* rng_ = nullptr;\n  tachyon_rng* rng_clone_ = nullptr;\n};\n\nusing RngTypes = testing::Types<XORShiftRNG, ChaCha20RNG>;\nTYPED_TEST_SUITE(RngTest, RngTypes);\n\nTYPED_TEST(RngTest, APIs) {\n  using Rng = TypeParam;\n\n  std::vector<uint8_t> seed = base::CreateVector(\n      Rng::kSeedSize, []() { return base::Uniform(base::Range<uint8_t>()); });\n  Rng cpp_rng;\n  ASSERT_TRUE(cpp_rng.SetSeed(seed));\n\n  uint8_t type;\n  if constexpr (std::is_same_v<Rng, XORShiftRNG>) {\n    type = TACHYON_RNG_XOR_SHIFT;\n  } else if constexpr (std::is_same_v<Rng, ChaCha20RNG>) {\n    type = TACHYON_RNG_CHA_CHA20;\n  }\n\n  this->rng_ = tachyon_rng_create_from_seed(type, seed.data(), Rng::kSeedSize);\n\n  EXPECT_EQ(cpp_rng.NextUint32(), tachyon_rng_get_next_u32(this->rng_));\n  EXPECT_EQ(cpp_rng.NextUint64(), tachyon_rng_get_next_u64(this->rng_));\n\n  size_t state_len;\n  tachyon_rng_get_state(this->rng_, nullptr, &state_len);\n  ASSERT_EQ(state_len, Rng::kStateSize);\n\n  uint8_t state[Rng::kStateSize];\n  tachyon_rng_get_state(this->rng_, state, &state_len);\n  ASSERT_EQ(state_len, Rng::kStateSize);\n\n  this->rng_clone_ = tachyon_rng_create_from_state(type, state, state_len);\n\n  EXPECT_EQ(tachyon_rng_get_next_u32(this->rng_),\n            tachyon_rng_get_next_u32(this->rng_clone_));\n}\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/c/examples/BUILD.bazel",
    "content": "filegroup(\n    name = \"examples\",\n    srcs = [\n        \"affine_point.cc\",\n        \"extension_field.cc\",\n        \"jacobian_point.cc\",\n        \"msm.cc\",\n        \"point_xyzz.cc\",\n        \"prime_field.cc\",\n        \"projective_point.cc\",\n        \"rng.cc\",\n        \"version.cc\",\n    ],\n    visibility = [\"//visibility:public\"],\n)\n\ncc_binary(\n    name = \"affine_point\",\n    srcs = [\"affine_point.cc\"],\n    deps = [\n        \"//tachyon/c/math/elliptic_curves/bn/bn254:g1\",\n        \"//tachyon/c/math/elliptic_curves/bn/bn254:g2\",\n    ],\n)\n\ncc_binary(\n    name = \"extension_field\",\n    srcs = [\"extension_field.cc\"],\n    deps = [\n        \"//tachyon/c/math/elliptic_curves/bn/bn254:fq12\",\n        \"//tachyon/c/math/elliptic_curves/bn/bn254:fq2\",\n        \"//tachyon/c/math/elliptic_curves/bn/bn254:fq6\",\n    ],\n)\n\ncc_binary(\n    name = \"jacobian_point\",\n    srcs = [\"jacobian_point.cc\"],\n    deps = [\n        \"//tachyon/c/math/elliptic_curves/bn/bn254:g1\",\n        \"//tachyon/c/math/elliptic_curves/bn/bn254:g2\",\n    ],\n)\n\ncc_binary(\n    name = \"msm\",\n    srcs = [\"msm.cc\"],\n    deps = [\n        \"//tachyon/c/math/elliptic_curves/bn/bn254:fr\",\n        \"//tachyon/c/math/elliptic_curves/bn/bn254:g1\",\n        \"//tachyon/c/math/elliptic_curves/bn/bn254:msm\",\n    ],\n)\n\ncc_binary(\n    name = \"point_xyzz\",\n    srcs = [\"point_xyzz.cc\"],\n    deps = [\n        \"//tachyon/c/math/elliptic_curves/bn/bn254:g1\",\n        \"//tachyon/c/math/elliptic_curves/bn/bn254:g2\",\n    ],\n)\n\ncc_binary(\n    name = \"prime_field\",\n    srcs = [\"prime_field.cc\"],\n    deps = [\"//tachyon/c/math/elliptic_curves/bn/bn254:fr\"],\n)\n\ncc_binary(\n    name = \"projective_point\",\n    srcs = [\"projective_point.cc\"],\n    deps = [\n        \"//tachyon/c/math/elliptic_curves/bn/bn254:g1\",\n        \"//tachyon/c/math/elliptic_curves/bn/bn254:g2\",\n    ],\n)\n\ncc_binary(\n    name = \"rng\",\n    srcs = [\"rng.cc\"],\n    deps = [\"//tachyon/c/crypto/random:rng\"],\n)\n\ncc_binary(\n    name = \"version\",\n    srcs = [\"version.cc\"],\n    deps = [\"//tachyon/c:version\"],\n)\n"
  },
  {
    "path": "tachyon/c/examples/affine_point.cc",
    "content": "#include <stdio.h>\n\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g2.h\"\n\nint main() {\n  // Initialize the G1 and G2 curves\n  tachyon_bn254_g1_init();\n  tachyon_bn254_g2_init();\n\n  // Generate random points on G1 and G2 curves\n  tachyon_bn254_g1_affine g1_random_point = tachyon_bn254_g1_affine_random();\n  tachyon_bn254_g2_affine g2_random_point = tachyon_bn254_g2_affine_random();\n\n  // Generate the generator points of G1 and G2 curves\n  tachyon_bn254_g1_affine g1_generator_point =\n      tachyon_bn254_g1_affine_generator();\n  tachyon_bn254_g2_affine g2_generator_point =\n      tachyon_bn254_g2_affine_generator();\n\n  // Perform addition on G1 and G2 curves\n  [[maybe_unused]] tachyon_bn254_g1_jacobian g1_sum =\n      tachyon_bn254_g1_affine_add(&g1_random_point, &g1_generator_point);\n  [[maybe_unused]] tachyon_bn254_g2_jacobian g2_sum =\n      tachyon_bn254_g2_affine_add(&g2_random_point, &g2_generator_point);\n\n  // Perform negation on G1 and G2 curves\n  [[maybe_unused]] tachyon_bn254_g1_affine g1_neg =\n      tachyon_bn254_g1_affine_neg(&g1_random_point);\n  [[maybe_unused]] tachyon_bn254_g2_affine g2_neg =\n      tachyon_bn254_g2_affine_neg(&g2_random_point);\n\n  // Double the points on G1 and G2 curves\n  [[maybe_unused]] tachyon_bn254_g1_jacobian g1_doubled =\n      tachyon_bn254_g1_affine_dbl(&g1_random_point);\n  [[maybe_unused]] tachyon_bn254_g2_jacobian g2_doubled =\n      tachyon_bn254_g2_affine_dbl(&g2_random_point);\n\n  printf(\"Affine point operations on G1 and G2 curves completed.\\n\");\n\n  return 0;\n}\n"
  },
  {
    "path": "tachyon/c/examples/extension_field.cc",
    "content": "#include <stdio.h>\n\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/fq12.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/fq2.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/fq6.h\"\n\nint main() {\n  // Initialize extension field elements\n  tachyon_bn254_fq2 fq2_element = tachyon_bn254_fq2_random();\n  tachyon_bn254_fq6 fq6_element = tachyon_bn254_fq6_random();\n  tachyon_bn254_fq12 fq12_element = tachyon_bn254_fq12_random();\n\n  printf(\"Random Fq2, Fq6, and Fq12 elements have been generated.\\n\");\n\n  // Perform operations on Fq2 element\n  [[maybe_unused]] tachyon_bn254_fq2 fq2_negated =\n      tachyon_bn254_fq2_neg(&fq2_element);\n  [[maybe_unused]] tachyon_bn254_fq2 fq2_doubled =\n      tachyon_bn254_fq2_dbl(&fq2_element);\n  [[maybe_unused]] tachyon_bn254_fq2 fq2_squared =\n      tachyon_bn254_fq2_sqr(&fq2_element);\n  [[maybe_unused]] tachyon_bn254_fq2 fq2_inverted =\n      tachyon_bn254_fq2_inv(&fq2_element);\n\n  printf(\"Operations on Fq2 element completed.\\n\");\n\n  // Perform operations on Fq6 element\n  [[maybe_unused]] tachyon_bn254_fq6 fq6_negated =\n      tachyon_bn254_fq6_neg(&fq6_element);\n  [[maybe_unused]] tachyon_bn254_fq6 fq6_doubled =\n      tachyon_bn254_fq6_dbl(&fq6_element);\n  [[maybe_unused]] tachyon_bn254_fq6 fq6_squared =\n      tachyon_bn254_fq6_sqr(&fq6_element);\n  [[maybe_unused]] tachyon_bn254_fq6 fq6_inverted =\n      tachyon_bn254_fq6_inv(&fq6_element);\n\n  printf(\"Operations on Fq6 element completed.\\n\");\n\n  // Perform operations on Fq12 element\n  [[maybe_unused]] tachyon_bn254_fq12 fq12_negated =\n      tachyon_bn254_fq12_neg(&fq12_element);\n  [[maybe_unused]] tachyon_bn254_fq12 fq12_doubled =\n      tachyon_bn254_fq12_dbl(&fq12_element);\n  [[maybe_unused]] tachyon_bn254_fq12 fq12_squared =\n      tachyon_bn254_fq12_sqr(&fq12_element);\n  [[maybe_unused]] tachyon_bn254_fq12 fq12_inverted =\n      tachyon_bn254_fq12_inv(&fq12_element);\n\n  printf(\"Operations on Fq12 element completed.\\n\");\n\n  return 0;\n}\n"
  },
  {
    "path": "tachyon/c/examples/jacobian_point.cc",
    "content": "#include <stdio.h>\n\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g2.h\"\n\nint main() {\n  // Initialize the G1 and G2 curves\n  tachyon_bn254_g1_init();\n  tachyon_bn254_g2_init();\n\n  // Generate random Jacobian points on G1 and G2 curves\n  tachyon_bn254_g1_jacobian g1_random_point =\n      tachyon_bn254_g1_jacobian_random();\n  tachyon_bn254_g2_jacobian g2_random_point =\n      tachyon_bn254_g2_jacobian_random();\n\n  // Generate the generator Jacobian points of G1 and G2 curves\n  tachyon_bn254_g1_jacobian g1_generator_point =\n      tachyon_bn254_g1_jacobian_generator();\n  tachyon_bn254_g2_jacobian g2_generator_point =\n      tachyon_bn254_g2_jacobian_generator();\n\n  // Perform addition on G1 and G2 curves\n  [[maybe_unused]] tachyon_bn254_g1_jacobian g1_sum =\n      tachyon_bn254_g1_jacobian_add(&g1_random_point, &g1_generator_point);\n  [[maybe_unused]] tachyon_bn254_g2_jacobian g2_sum =\n      tachyon_bn254_g2_jacobian_add(&g2_random_point, &g2_generator_point);\n\n  // Perform negation on G1 and G2 curves\n  [[maybe_unused]] tachyon_bn254_g1_jacobian g1_neg =\n      tachyon_bn254_g1_jacobian_neg(&g1_random_point);\n  [[maybe_unused]] tachyon_bn254_g2_jacobian g2_neg =\n      tachyon_bn254_g2_jacobian_neg(&g2_random_point);\n\n  // Double the points on G1 and G2 curves\n  [[maybe_unused]] tachyon_bn254_g1_jacobian g1_doubled =\n      tachyon_bn254_g1_jacobian_dbl(&g1_random_point);\n  [[maybe_unused]] tachyon_bn254_g2_jacobian g2_doubled =\n      tachyon_bn254_g2_jacobian_dbl(&g2_random_point);\n\n  printf(\"Jacobian point operations on G1 and G2 curves completed.\\n\");\n\n  return 0;\n}\n"
  },
  {
    "path": "tachyon/c/examples/msm.cc",
    "content": "#include \"tachyon/c/math/elliptic_curves/bn/bn254/msm.h\"\n\n#include <stdio.h>\n#include <stdlib.h>\n\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/fr.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1.h\"\n\nint main() {\n  // Initialize curve and MSM context\n  tachyon_bn254_g1_init();\n\n  static tachyon_bn254_g1_msm_ptr msm = tachyon_bn254_g1_create_msm(32);\n\n  const size_t kMSMSize = 32;\n\n  // Example: Scalar values to multiply with G1 points\n  tachyon_bn254_fr scalars[kMSMSize];\n  for (size_t i = 0; i < kMSMSize; i++) {\n    scalars[i] = tachyon_bn254_fr_random();\n  }\n\n  // Example: G1 points\n  tachyon_bn254_g1_affine points[kMSMSize];\n  for (size_t i = 0; i < kMSMSize; i++) {\n    points[i] = tachyon_bn254_g1_affine_random();\n  }\n\n  // Perform the MSM operation\n  [[maybe_unused]] tachyon_bn254_g1_jacobian* result =\n      tachyon_bn254_g1_affine_msm(msm, points, scalars, 32);\n\n  return 0;\n}\n"
  },
  {
    "path": "tachyon/c/examples/point_xyzz.cc",
    "content": "#include <stdio.h>\n\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g2.h\"\n\nint main() {\n  // Initialize the curves\n  tachyon_bn254_g1_init();\n  tachyon_bn254_g2_init();\n\n  // Generate random `PointXYZZ` points on G1 and G2\n  tachyon_bn254_g1_xyzz g1_random_point = tachyon_bn254_g1_xyzz_random();\n  tachyon_bn254_g2_xyzz g2_random_point = tachyon_bn254_g2_xyzz_random();\n\n  // Demonstrate addition with a generated point\n  [[maybe_unused]] tachyon_bn254_g1_xyzz g1_point_sum =\n      tachyon_bn254_g1_xyzz_add(\n          &g1_random_point,\n          &g1_random_point);  // Adding to itself as an example\n  [[maybe_unused]] tachyon_bn254_g2_xyzz g2_point_sum =\n      tachyon_bn254_g2_xyzz_add(&g2_random_point, &g2_random_point);\n\n  // Demonstrate doubling\n  [[maybe_unused]] tachyon_bn254_g1_xyzz g1_doubled =\n      tachyon_bn254_g1_xyzz_dbl(&g1_random_point);\n  [[maybe_unused]] tachyon_bn254_g2_xyzz g2_doubled =\n      tachyon_bn254_g2_xyzz_dbl(&g2_random_point);\n\n  // Demonstrate negation\n  tachyon_bn254_g1_xyzz g1_negated =\n      tachyon_bn254_g1_xyzz_neg(&g1_random_point);\n  tachyon_bn254_g2_xyzz g2_negated =\n      tachyon_bn254_g2_xyzz_neg(&g2_random_point);\n\n  // Demonstrate subtraction (using negation + addition for demonstration)\n  [[maybe_unused]] tachyon_bn254_g1_xyzz g1_sub_result =\n      tachyon_bn254_g1_xyzz_add(&g1_random_point, &g1_negated);\n  [[maybe_unused]] tachyon_bn254_g2_xyzz g2_sub_result =\n      tachyon_bn254_g2_xyzz_add(&g2_random_point, &g2_negated);\n\n  printf(\"Point XYZZ operations on G1 and G2 curves completed.\\n\");\n\n  return 0;\n}\n"
  },
  {
    "path": "tachyon/c/examples/prime_field.cc",
    "content": "#include <stdio.h>\n\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/fr.h\"\n\nint main() {\n  // Initialize prime field elements\n  tachyon_bn254_fr a = tachyon_bn254_fr_random();\n  tachyon_bn254_fr b = tachyon_bn254_fr_random();\n  printf(\"Random prime field elements a and b have been generated.\\n\");\n\n  // Addition\n  [[maybe_unused]] tachyon_bn254_fr sum = tachyon_bn254_fr_add(&a, &b);\n  printf(\"a + b computed.\\n\");\n\n  // Subtraction\n  [[maybe_unused]] tachyon_bn254_fr difference = tachyon_bn254_fr_sub(&a, &b);\n  printf(\"a - b computed.\\n\");\n\n  // Multiplication\n  [[maybe_unused]] tachyon_bn254_fr product = tachyon_bn254_fr_mul(&a, &b);\n  printf(\"a * b computed.\\n\");\n\n  // Division\n  [[maybe_unused]] tachyon_bn254_fr quotient = tachyon_bn254_fr_div(&a, &b);\n  printf(\"a / b computed.\\n\");\n\n  // Negation\n  [[maybe_unused]] tachyon_bn254_fr negation = tachyon_bn254_fr_neg(&a);\n  printf(\"-a computed.\\n\");\n\n  // Doubling\n  [[maybe_unused]] tachyon_bn254_fr doubled = tachyon_bn254_fr_dbl(&a);\n  printf(\"2a computed.\\n\");\n\n  // Squaring\n  [[maybe_unused]] tachyon_bn254_fr squared = tachyon_bn254_fr_sqr(&a);\n  printf(\"a^2 computed.\\n\");\n\n  // Inversion\n  [[maybe_unused]] tachyon_bn254_fr inverse = tachyon_bn254_fr_inv(&a);\n  printf(\"a^-1 computed.\\n\");\n\n  // Equality check\n  int isEqual = tachyon_bn254_fr_eq(&a, &b);\n  printf(\"Checking if a == b: %s\\n\", isEqual ? \"true\" : \"false\");\n\n  // Inequality check\n  int isNotEqual = tachyon_bn254_fr_ne(&a, &b);\n  printf(\"Checking if a != b: %s\\n\", isNotEqual ? \"true\" : \"false\");\n\n  return 0;\n}\n"
  },
  {
    "path": "tachyon/c/examples/projective_point.cc",
    "content": "#include <stdio.h>\n\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g2.h\"\n\nint main() {\n  // Initialize G1 and G2 curves\n  tachyon_bn254_g1_init();\n  tachyon_bn254_g2_init();\n\n  // Generate random projective points on G1 and G2 curves\n  tachyon_bn254_g1_projective g1_random_point =\n      tachyon_bn254_g1_projective_random();\n  tachyon_bn254_g2_projective g2_random_point =\n      tachyon_bn254_g2_projective_random();\n\n  // Generate generator projective points of G1 and G2 curves\n  tachyon_bn254_g1_projective g1_generator_point =\n      tachyon_bn254_g1_projective_generator();\n  tachyon_bn254_g2_projective g2_generator_point =\n      tachyon_bn254_g2_projective_generator();\n\n  // Perform projective point addition on G1 and G2 curves\n  [[maybe_unused]] tachyon_bn254_g1_projective g1_sum =\n      tachyon_bn254_g1_projective_add(&g1_random_point, &g1_generator_point);\n  [[maybe_unused]] tachyon_bn254_g2_projective g2_sum =\n      tachyon_bn254_g2_projective_add(&g2_random_point, &g2_generator_point);\n\n  // Double the projective points on G1 and G2 curves\n  [[maybe_unused]] tachyon_bn254_g1_projective g1_doubled =\n      tachyon_bn254_g1_projective_dbl(&g1_random_point);\n  [[maybe_unused]] tachyon_bn254_g2_projective g2_doubled =\n      tachyon_bn254_g2_projective_dbl(&g2_random_point);\n\n  printf(\"Projective point operations on G1 and G2 curves completed.\\n\");\n\n  return 0;\n}\n"
  },
  {
    "path": "tachyon/c/examples/rng.cc",
    "content": "#include \"tachyon/c/crypto/random/rng.h\"\n\n#include <stdio.h>\n\nint main() {\n  uint8_t seed[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,\n                    0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef};\n  size_t seed_size = sizeof(seed) / sizeof(seed[0]);\n\n  tachyon_rng* rng =\n      tachyon_rng_create_from_seed(TACHYON_RNG_XOR_SHIFT, seed, seed_size);\n\n  if (!rng) {\n    printf(\"Failed to create RNG.\\n\");\n    return 1;\n  }\n\n  uint32_t rand_u32 = tachyon_rng_get_next_u32(rng);\n  uint64_t rand_u64 = tachyon_rng_get_next_u64(rng);\n  printf(\"Next uint32: %u\\n\", rand_u32);\n  printf(\"Next uint64: %lu\\n\", rand_u64);\n\n  tachyon_rng_destroy(rng);\n  return 0;\n}\n"
  },
  {
    "path": "tachyon/c/examples/version.cc",
    "content": "#include \"tachyon/c/version.h\"\n\n#include <stdio.h>\n\nint main() {\n  printf(\"Runtime Version: %u\\n\", tachyon_get_runtime_version());\n  printf(\"Runtime Version String: %s\\n\", tachyon_get_runtime_version_str());\n  printf(\"Runtime Full Version String: %s\\n\",\n         tachyon_get_runtime_full_version_str());\n  return 0;\n}\n"
  },
  {
    "path": "tachyon/c/export.h",
    "content": "#ifndef TACHYON_C_EXPORT_H_\n#define TACHYON_C_EXPORT_H_\n\n#if defined(TACHYON_C_SHARED_LIB_BUILD)\n\n#if defined(_WIN32)\n#ifdef TACHYON_COMPILE_LIBRARY\n#define TACHYON_C_EXPORT __declspec(dllexport)\n#else\n#define TACHYON_C_EXPORT __declspec(dllimport)\n#endif  // defined(TACHYON_COMPILE_LIBRARY)\n\n#else\n#ifdef TACHYON_COMPILE_LIBRARY\n#define TACHYON_C_EXPORT __attribute__((visibility(\"default\")))\n#else\n#define TACHYON_C_EXPORT\n#endif  // defined(TACHYON_COMPILE_LIBRARY)\n#endif  // defined(_WIN32)\n\n#else\n#define TACHYON_C_EXPORT\n#endif  // defined(TACHYON_C_SHARED_LIB_BUILD)\n\n#endif  // TACHYON_C_EXPORT_H_\n"
  },
  {
    "path": "tachyon/c/math/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n    name = \"math_hdrs\",\n    srcs = [\n        \"//tachyon/c/math/elliptic_curves:elliptic_curves_hdrs\",\n        \"//tachyon/c/math/finite_fields:finite_fields_hdrs\",\n        \"//tachyon/c/math/matrix:matrix_hdrs\",\n        \"//tachyon/c/math/polynomials:polynomials_hdrs\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"math\",\n    deps = [\n        \"//tachyon/c/math/elliptic_curves\",\n        \"//tachyon/c/math/finite_fields\",\n        \"//tachyon/c/math/matrix\",\n        \"//tachyon/c/math/polynomials\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/c/math/elliptic_curves/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n    name = \"elliptic_curves_hdrs\",\n    srcs = [\n        \"//tachyon/c/math/elliptic_curves/bls12/bls12_381:bls12_381_hdrs\",\n        \"//tachyon/c/math/elliptic_curves/bn/bn254:bn254_hdrs\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"elliptic_curves\",\n    deps = [\n        \"//tachyon/c/math/elliptic_curves/bls12/bls12_381\",\n        \"//tachyon/c/math/elliptic_curves/bn/bn254\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"point_conversions\",\n    hdrs = [\"point_conversions.h\"],\n    deps = [\n        \":point_traits_forward\",\n        \"//tachyon/c/base:type_traits_forward\",\n        \"//tachyon/math/geometry:point2\",\n        \"//tachyon/math/geometry:point3\",\n        \"//tachyon/math/geometry:point4\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"point_traits_forward\",\n    hdrs = [\"point_traits_forward.h\"],\n)\n"
  },
  {
    "path": "tachyon/c/math/elliptic_curves/bls12/bls12_381/BUILD.bazel",
    "content": "load(\"//tachyon/c/math/elliptic_curves/generator:build_defs.bzl\", \"generate_ec_points\")\nload(\"//tachyon/c/math/finite_fields/generator/ext_field_generator:build_defs.bzl\", \"generate_ext_fields\")\nload(\"//tachyon/c/math/finite_fields/generator/prime_field_generator:build_defs.bzl\", \"generate_ec_prime_fields\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n    name = \"bls12_381_hdrs\",\n    srcs = [\n        \"fq.h\",\n        \"fq12.h\",\n        \"fq2.h\",\n        \"fq6.h\",\n        \"fr.h\",\n        \"g1.h\",\n        \"g2.h\",\n        \"msm.h\",\n        \"msm_gpu.h\",\n    ],\n)\n\ngenerate_ec_prime_fields(\n    name = \"fq\",\n    class_name = \"bls12_381_fq\",\n    curve = \"bls12_381\",\n    display_name = \"Fq\",\n    limb_nums = 6,\n    native_deps = [\"//tachyon/math/elliptic_curves/bls12/bls12_381:fq\"],\n    native_hdr = \"tachyon/math/elliptic_curves/bls12/bls12_381/fq.h\",\n    native_type = \"tachyon::math::bls12_381::Fq\",\n)\n\ngenerate_ec_prime_fields(\n    name = \"fr\",\n    class_name = \"bls12_381_fr\",\n    curve = \"bls12_381\",\n    display_name = \"Fr\",\n    limb_nums = 4,\n    native_deps = [\"//tachyon/math/elliptic_curves/bls12/bls12_381:fr\"],\n    native_hdr = \"tachyon/math/elliptic_curves/bls12/bls12_381/fr.h\",\n    native_type = \"tachyon::math::bls12_381::Fr\",\n)\n\ngenerate_ext_fields(\n    name = \"fq2\",\n    base_field_class_name = \"bls12_381_fq\",\n    base_field_display_name = \"Fq\",\n    c_base_field_deps = [\":fq\"],\n    c_base_field_hdr = \"tachyon/c/math/elliptic_curves/bls12/bls12_381/fq.h\",\n    class_name = \"bls12_381_fq2\",\n    degree_over_base_field = 2,\n    display_name = \"Fq2\",\n    native_deps = [\"//tachyon/math/elliptic_curves/bls12/bls12_381:fq2\"],\n    native_hdr = \"tachyon/math/elliptic_curves/bls12/bls12_381/fq2.h\",\n    native_type = \"tachyon::math::bls12_381::Fq2\",\n)\n\ngenerate_ext_fields(\n    name = \"fq6\",\n    base_field_class_name = \"bls12_381_fq2\",\n    base_field_display_name = \"Fq2\",\n    c_base_field_deps = [\":fq2\"],\n    c_base_field_hdr = \"tachyon/c/math/elliptic_curves/bls12/bls12_381/fq2.h\",\n    class_name = \"bls12_381_fq6\",\n    degree_over_base_field = 3,\n    display_name = \"Fq6\",\n    native_deps = [\"//tachyon/math/elliptic_curves/bls12/bls12_381:fq6\"],\n    native_hdr = \"tachyon/math/elliptic_curves/bls12/bls12_381/fq6.h\",\n    native_type = \"tachyon::math::bls12_381::Fq6\",\n)\n\ngenerate_ext_fields(\n    name = \"fq12\",\n    base_field_class_name = \"bls12_381_fq6\",\n    base_field_display_name = \"Fq6\",\n    c_base_field_deps = [\":fq6\"],\n    c_base_field_hdr = \"tachyon/c/math/elliptic_curves/bls12/bls12_381/fq6.h\",\n    class_name = \"bls12_381_fq12\",\n    degree_over_base_field = 2,\n    display_name = \"Fq12\",\n    native_deps = [\"//tachyon/math/elliptic_curves/bls12/bls12_381:fq12\"],\n    native_hdr = \"tachyon/math/elliptic_curves/bls12/bls12_381/fq12.h\",\n    native_type = \"tachyon::math::bls12_381::Fq12\",\n)\n\ngenerate_ec_points(\n    name = \"bls12_381\",\n    g1_deps = [\n        \":fq\",\n        \":fr\",\n        \"//tachyon/math/elliptic_curves/bls12/bls12_381:g1\",\n    ],\n    g1_gpu_deps = [\"//tachyon/math/elliptic_curves/bls12/bls12_381:g1_gpu\"],\n    g2_deps = [\n        \":fq2\",\n        \":fr\",\n        \"//tachyon/math/elliptic_curves/bls12/bls12_381:g2\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/c/math/elliptic_curves/bn/bn254/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\nload(\"//tachyon/c/math/elliptic_curves/generator:build_defs.bzl\", \"generate_ec_points\")\nload(\"//tachyon/c/math/finite_fields/generator/ext_field_generator:build_defs.bzl\", \"generate_ext_fields\")\nload(\"//tachyon/c/math/finite_fields/generator/prime_field_generator:build_defs.bzl\", \"generate_ec_prime_fields\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n    name = \"bn254_hdrs\",\n    srcs = [\n        \"fq.h\",\n        \"fq12.h\",\n        \"fq2.h\",\n        \"fq6.h\",\n        \"fr.h\",\n        \"g1.h\",\n        \"g2.h\",\n        \"msm.h\",\n        \"msm_gpu.h\",\n    ],\n)\n\ngenerate_ec_prime_fields(\n    name = \"fq\",\n    class_name = \"bn254_fq\",\n    curve = \"bn254\",\n    display_name = \"Fq\",\n    limb_nums = 4,\n    native_deps = [\"//tachyon/math/elliptic_curves/bn/bn254:fq\"],\n    native_hdr = \"tachyon/math/elliptic_curves/bn/bn254/fq.h\",\n    native_type = \"tachyon::math::bn254::Fq\",\n)\n\ngenerate_ec_prime_fields(\n    name = \"fr\",\n    class_name = \"bn254_fr\",\n    curve = \"bn254\",\n    display_name = \"Fr\",\n    limb_nums = 4,\n    native_deps = [\"//tachyon/math/elliptic_curves/bn/bn254:fr\"],\n    native_hdr = \"tachyon/math/elliptic_curves/bn/bn254/fr.h\",\n    native_type = \"tachyon::math::bn254::Fr\",\n)\n\ngenerate_ext_fields(\n    name = \"fq2\",\n    base_field_class_name = \"bn254_fq\",\n    base_field_display_name = \"Fq\",\n    c_base_field_deps = [\":fq\"],\n    c_base_field_hdr = \"tachyon/c/math/elliptic_curves/bn/bn254/fq.h\",\n    class_name = \"bn254_fq2\",\n    degree_over_base_field = 2,\n    display_name = \"Fq2\",\n    native_deps = [\"//tachyon/math/elliptic_curves/bn/bn254:fq2\"],\n    native_hdr = \"tachyon/math/elliptic_curves/bn/bn254/fq2.h\",\n    native_type = \"tachyon::math::bn254::Fq2\",\n)\n\ngenerate_ext_fields(\n    name = \"fq6\",\n    base_field_class_name = \"bn254_fq2\",\n    base_field_display_name = \"Fq2\",\n    c_base_field_deps = [\":fq2\"],\n    c_base_field_hdr = \"tachyon/c/math/elliptic_curves/bn/bn254/fq2.h\",\n    class_name = \"bn254_fq6\",\n    degree_over_base_field = 3,\n    display_name = \"Fq6\",\n    native_deps = [\"//tachyon/math/elliptic_curves/bn/bn254:fq6\"],\n    native_hdr = \"tachyon/math/elliptic_curves/bn/bn254/fq6.h\",\n    native_type = \"tachyon::math::bn254::Fq6\",\n)\n\ngenerate_ext_fields(\n    name = \"fq12\",\n    base_field_class_name = \"bn254_fq6\",\n    base_field_display_name = \"Fq6\",\n    c_base_field_deps = [\":fq6\"],\n    c_base_field_hdr = \"tachyon/c/math/elliptic_curves/bn/bn254/fq6.h\",\n    class_name = \"bn254_fq12\",\n    degree_over_base_field = 2,\n    display_name = \"Fq12\",\n    native_deps = [\"//tachyon/math/elliptic_curves/bn/bn254:fq12\"],\n    native_hdr = \"tachyon/math/elliptic_curves/bn/bn254/fq12.h\",\n    native_type = \"tachyon::math::bn254::Fq12\",\n)\n\ngenerate_ec_points(\n    name = \"bn254\",\n    g1_deps = [\n        \":fq\",\n        \":fr\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:g1\",\n    ],\n    g1_gpu_deps = [\"//tachyon/math/elliptic_curves/bn/bn254:g1_gpu\"],\n    g2_deps = [\n        \":fq2\",\n        \":fr\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:g2\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"g1_test\",\n    testonly = True,\n    hdrs = [\"g1_test.h\"],\n    deps = [\n        \":g1\",\n        \"@com_google_googletest//:gtest\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/c/math/elliptic_curves/bn/bn254/g1_test.h",
    "content": "#ifndef TACHYON_C_MATH_ELLIPTIC_CURVES_BN_BN254_G1_TEST_H_\n#define TACHYON_C_MATH_ELLIPTIC_CURVES_BN_BN254_G1_TEST_H_\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1.h\"\n\nnamespace tachyon::c::math::bn254 {\n\nclass G1Test : public testing::Test {\n public:\n  static void SetUpTestSuite() { tachyon_bn254_g1_init(); }\n};\n\n}  // namespace tachyon::c::math::bn254\n\n#endif  // TACHYON_C_MATH_ELLIPTIC_CURVES_BN_BN254_G1_TEST_H_\n"
  },
  {
    "path": "tachyon/c/math/elliptic_curves/generator/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_binary\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"generator_util\",\n    srcs = [\"generator_util.cc\"],\n    hdrs = [\"generator_util.h\"],\n    deps = [\"//tachyon/base:logging\"],\n)\n\ntachyon_cc_binary(\n    name = \"generator\",\n    srcs = [\"generator.cc\"],\n    data = [\n        \"msm.cc.tpl\",\n        \"msm.h.tpl\",\n        \"msm_gpu.cc.tpl\",\n        \"msm_gpu.h.tpl\",\n        \"point.cc.tpl\",\n        \"point.h.tpl\",\n        \"point_traits.h.tpl\",\n        \"point_type_traits.h.tpl\",\n    ],\n    deps = [\n        \":generator_util\",\n        \"//tachyon/base/console\",\n        \"//tachyon/base/files:file_path_flag\",\n        \"//tachyon/base/flag:flag_parser\",\n        \"//tachyon/base/strings:string_number_conversions\",\n        \"//tachyon/build:cc_writer\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/c/math/elliptic_curves/generator/build_defs.bzl",
    "content": "load(\"//bazel:tachyon.bzl\", \"if_gpu_is_configured\")\nload(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cuda_library\")\n\ndef _generate_ec_point_impl(ctx):\n    point_hdr_tpl_path = ctx.expand_location(\"$(location @kroma_network_tachyon//tachyon/c/math/elliptic_curves/generator:point.h.tpl)\", [ctx.attr.point_hdr_tpl_path])\n    point_src_tpl_path = ctx.expand_location(\"$(location @kroma_network_tachyon//tachyon/c/math/elliptic_curves/generator:point.cc.tpl)\", [ctx.attr.point_src_tpl_path])\n    point_traits_hdr_tpl_path = ctx.expand_location(\"$(location @kroma_network_tachyon//tachyon/c/math/elliptic_curves/generator:point_traits.h.tpl)\", [ctx.attr.point_traits_hdr_tpl_path])\n    point_type_traits_hdr_tpl_path = ctx.expand_location(\"$(location @kroma_network_tachyon//tachyon/c/math/elliptic_curves/generator:point_type_traits.h.tpl)\", [ctx.attr.point_type_traits_hdr_tpl_path])\n    msm_hdr_tpl_path = ctx.expand_location(\"$(location @kroma_network_tachyon//tachyon/c/math/elliptic_curves/generator:msm.h.tpl)\", [ctx.attr.msm_hdr_tpl_path])\n    msm_src_tpl_path = ctx.expand_location(\"$(location @kroma_network_tachyon//tachyon/c/math/elliptic_curves/generator:msm.cc.tpl)\", [ctx.attr.msm_src_tpl_path])\n    msm_gpu_hdr_tpl_path = ctx.expand_location(\"$(location @kroma_network_tachyon//tachyon/c/math/elliptic_curves/generator:msm_gpu.h.tpl)\", [ctx.attr.msm_gpu_hdr_tpl_path])\n    msm_gpu_src_tpl_path = ctx.expand_location(\"$(location @kroma_network_tachyon//tachyon/c/math/elliptic_curves/generator:msm_gpu.cc.tpl)\", [ctx.attr.msm_gpu_src_tpl_path])\n\n    arguments = [\n        \"--out=%s\" % (ctx.outputs.out.path),\n        \"--type=%s\" % (ctx.attr.type),\n        \"--point_hdr_tpl_path=%s\" % (point_hdr_tpl_path),\n        \"--point_src_tpl_path=%s\" % (point_src_tpl_path),\n        \"--point_traits_hdr_tpl_path=%s\" % (point_traits_hdr_tpl_path),\n        \"--point_type_traits_hdr_tpl_path=%s\" % (point_type_traits_hdr_tpl_path),\n        \"--msm_hdr_tpl_path=%s\" % (msm_hdr_tpl_path),\n        \"--msm_src_tpl_path=%s\" % (msm_src_tpl_path),\n        \"--msm_gpu_hdr_tpl_path=%s\" % (msm_gpu_hdr_tpl_path),\n        \"--msm_gpu_src_tpl_path=%s\" % (msm_gpu_src_tpl_path),\n    ]\n\n    ctx.actions.run(\n        inputs = [\n            ctx.files.point_hdr_tpl_path[0],\n            ctx.files.point_src_tpl_path[0],\n            ctx.files.point_traits_hdr_tpl_path[0],\n            ctx.files.point_type_traits_hdr_tpl_path[0],\n            ctx.files.msm_hdr_tpl_path[0],\n            ctx.files.msm_src_tpl_path[0],\n            ctx.files.msm_gpu_hdr_tpl_path[0],\n            ctx.files.msm_gpu_src_tpl_path[0],\n        ],\n        tools = [ctx.executable._tool],\n        executable = ctx.executable._tool,\n        outputs = [ctx.outputs.out],\n        arguments = arguments,\n    )\n\n    return [DefaultInfo(files = depset([ctx.outputs.out]))]\n\ngenerate_ec_point = rule(\n    implementation = _generate_ec_point_impl,\n    attrs = {\n        \"out\": attr.output(mandatory = True),\n        \"type\": attr.string(mandatory = True),\n        \"point_hdr_tpl_path\": attr.label(\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/c/math/elliptic_curves/generator:point.h.tpl\"),\n        ),\n        \"point_src_tpl_path\": attr.label(\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/c/math/elliptic_curves/generator:point.cc.tpl\"),\n        ),\n        \"point_traits_hdr_tpl_path\": attr.label(\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/c/math/elliptic_curves/generator:point_traits.h.tpl\"),\n        ),\n        \"point_type_traits_hdr_tpl_path\": attr.label(\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/c/math/elliptic_curves/generator:point_type_traits.h.tpl\"),\n        ),\n        \"msm_hdr_tpl_path\": attr.label(\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/c/math/elliptic_curves/generator:msm.h.tpl\"),\n        ),\n        \"msm_src_tpl_path\": attr.label(\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/c/math/elliptic_curves/generator:msm.cc.tpl\"),\n        ),\n        \"msm_gpu_hdr_tpl_path\": attr.label(\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/c/math/elliptic_curves/generator:msm_gpu.h.tpl\"),\n        ),\n        \"msm_gpu_src_tpl_path\": attr.label(\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/c/math/elliptic_curves/generator:msm_gpu.cc.tpl\"),\n        ),\n        \"_tool\": attr.label(\n            # TODO(chokobole): Change to \"exec\", so we can build on macos.\n            cfg = \"target\",\n            executable = True,\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/c/math/elliptic_curves/generator\"),\n        ),\n    },\n)\n\ndef generate_ec_points(\n        name,\n        g1_deps,\n        g2_deps,\n        g1_gpu_deps):\n    for n in [\n        (\"gen_g1_hdr\", \"g1.h\"),\n        (\"gen_g1_src\", \"g1.cc\"),\n        (\"gen_g2_hdr\", \"g2.h\"),\n        (\"gen_g2_src\", \"g2.cc\"),\n        (\"gen_g1_point_traits\", \"g1_point_traits.h\"),\n        (\"gen_g2_point_traits\", \"g2_point_traits.h\"),\n        (\"gen_g1_point_type_traits\", \"g1_point_type_traits.h\"),\n        (\"gen_g2_point_type_traits\", \"g2_point_type_traits.h\"),\n        (\"gen_msm_hdr\", \"msm.h\"),\n        (\"gen_msm_src\", \"msm.cc\"),\n        (\"gen_msm_gpu_hdr\", \"msm_gpu.h\"),\n        (\"gen_msm_gpu_src\", \"msm_gpu.cc\"),\n    ]:\n        generate_ec_point(\n            type = name,\n            name = n[0],\n            out = n[1],\n        )\n\n    tachyon_cc_library(\n        name = \"g1\",\n        hdrs = [\n            \"g1.h\",\n            \"g1_point_traits.h\",\n            \"g1_point_type_traits.h\",\n        ],\n        srcs = [\"g1.cc\"],\n        deps = g1_deps + [\n            \"//tachyon/c/math/elliptic_curves:point_traits_forward\",\n        ],\n    )\n\n    tachyon_cc_library(\n        name = \"g2\",\n        hdrs = [\n            \"g2.h\",\n            \"g2_point_traits.h\",\n            \"g2_point_type_traits.h\",\n        ],\n        srcs = [\"g2.cc\"],\n        deps = g2_deps + [\n            \"//tachyon/c/math/elliptic_curves:point_traits_forward\",\n        ],\n    )\n\n    tachyon_cc_library(\n        name = \"msm\",\n        hdrs = [\"msm.h\"],\n        srcs = [\"msm.cc\"],\n        deps = [\n            \":g1\",\n            \"//tachyon/c/math/elliptic_curves/msm\",\n        ],\n    )\n\n    tachyon_cuda_library(\n        name = \"msm_gpu\",\n        hdrs = [\"msm_gpu.h\"],\n        srcs = if_gpu_is_configured([\"msm_gpu.cc\"]),\n        deps = g1_gpu_deps + [\n            \":g1\",\n            \"//tachyon/c/math/elliptic_curves/msm:msm_gpu\",\n        ],\n    )\n\n    tachyon_cc_library(\n        name = name,\n        deps = [\n            \":msm\",\n            \":msm_gpu\",\n        ],\n    )\n"
  },
  {
    "path": "tachyon/c/math/elliptic_curves/generator/generator.cc",
    "content": "#include \"absl/strings/str_replace.h\"\n\n#include \"tachyon/base/console/iostream.h\"\n#include \"tachyon/base/files/file_path_flag.h\"\n#include \"tachyon/base/files/file_util.h\"\n#include \"tachyon/base/flag/flag_parser.h\"\n#include \"tachyon/base/strings/string_number_conversions.h\"\n#include \"tachyon/base/strings/string_util.h\"\n#include \"tachyon/build/cc_writer.h\"\n#include \"tachyon/c/math/elliptic_curves/generator/generator_util.h\"\n\nnamespace tachyon {\n\nstruct GenerationConfig : public build::CcWriter {\n  base::FilePath point_hdr_tpl_path;\n  base::FilePath point_src_tpl_path;\n  base::FilePath point_traits_hdr_tpl_path;\n  base::FilePath point_type_traits_hdr_tpl_path;\n  base::FilePath msm_hdr_tpl_path;\n  base::FilePath msm_src_tpl_path;\n  base::FilePath msm_gpu_hdr_tpl_path;\n  base::FilePath msm_gpu_src_tpl_path;\n\n  std::string type;\n\n  int GeneratePointHdr(std::string_view g1_or_g2,\n                       std::string_view fq_or_fq2) const;\n  int GeneratePointSrc(std::string_view g1_or_g2,\n                       std::string_view fq_or_fq2) const;\n  int GeneratePointTraitsHdr(std::string_view g1_or_g2,\n                             std::string_view fq_or_fq2) const;\n  int GeneratePointTypeTraitsHdr(std::string_view g1_or_g2,\n                                 std::string_view fq_or_fq2) const;\n  int GenerateG1Hdr() const;\n  int GenerateG1Src() const;\n  int GenerateG1TraitsHdr() const;\n  int GenerateG1TypeTraitsHdr() const;\n  int GenerateG2Hdr() const;\n  int GenerateG2Src() const;\n  int GenerateG2TraitsHdr() const;\n  int GenerateG2TypeTraitsHdr() const;\n  int GenerateMSMHdr() const;\n  int GenerateMSMSrc() const;\n  int GenerateMSMGpuHdr() const;\n  int GenerateMSMGpuSrc() const;\n};\n\nint GenerationConfig::GeneratePointHdr(std::string_view g1_or_g2,\n                                       std::string_view fq_or_fq2) const {\n  std::string tpl_content;\n  CHECK(base::ReadFileToString(point_hdr_tpl_path, &tpl_content));\n\n  std::string content = absl::StrReplaceAll(\n      tpl_content, {{\"%{header_dir_name}\", c::math::GetLocation(type)},\n                    {\"%{type}\", type},\n                    {\"%{g1_or_g2}\", g1_or_g2},\n                    {\"%{fq_or_fq2}\", fq_or_fq2}});\n  return WriteHdr(content, true);\n}\n\nint GenerationConfig::GenerateG1Hdr() const {\n  return GeneratePointHdr(\"g1\", \"fq\");\n}\n\nint GenerationConfig::GenerateG2Hdr() const {\n  return GeneratePointHdr(\"g2\", \"fq2\");\n}\n\nint GenerationConfig::GeneratePointSrc(std::string_view g1_or_g2,\n                                       std::string_view fq_or_fq2) const {\n  std::string tpl_content;\n  CHECK(base::ReadFileToString(point_src_tpl_path, &tpl_content));\n\n  std::string content = absl::StrReplaceAll(\n      tpl_content, {{\"%{header_dir_name}\", c::math::GetLocation(type)},\n                    {\"%{type}\", type},\n                    {\"%{g1_or_g2}\", g1_or_g2},\n                    {\"%{G1_or_G2}\", base::CapitalizeASCII(g1_or_g2)},\n                    {\"%{fq_or_fq2}\", fq_or_fq2}});\n  return WriteSrc(content);\n}\n\nint GenerationConfig::GenerateG1Src() const {\n  return GeneratePointSrc(\"g1\", \"fq\");\n}\n\nint GenerationConfig::GenerateG2Src() const {\n  return GeneratePointSrc(\"g2\", \"fq2\");\n}\n\nint GenerationConfig::GeneratePointTraitsHdr(std::string_view g1_or_g2,\n                                             std::string_view fq_or_fq2) const {\n  std::string tpl_content;\n  CHECK(base::ReadFileToString(point_traits_hdr_tpl_path, &tpl_content));\n\n  std::string content = absl::StrReplaceAll(\n      tpl_content, {\n                       {\"%{header_dir_name}\", c::math::GetLocation(type)},\n                       {\"%{type}\", type},\n                       {\"%{g1_or_g2}\", g1_or_g2},\n                       {\"%{G1_or_G2}\", base::CapitalizeASCII(g1_or_g2)},\n                       {\"%{fq_or_fq2}\", fq_or_fq2},\n                       {\"%{Fq_or_Fq2}\", base::CapitalizeASCII(fq_or_fq2)},\n                   });\n  return WriteHdr(content, false);\n}\n\nint GenerationConfig::GenerateG1TraitsHdr() const {\n  return GeneratePointTraitsHdr(\"g1\", \"fq\");\n}\n\nint GenerationConfig::GenerateG2TraitsHdr() const {\n  return GeneratePointTraitsHdr(\"g2\", \"fq2\");\n}\n\nint GenerationConfig::GeneratePointTypeTraitsHdr(\n    std::string_view g1_or_g2, std::string_view fq_or_fq2) const {\n  std::string tpl_content;\n  CHECK(base::ReadFileToString(point_type_traits_hdr_tpl_path, &tpl_content));\n\n  std::string content = absl::StrReplaceAll(\n      tpl_content, {\n                       {\"%{header_dir_name}\", c::math::GetLocation(type)},\n                       {\"%{type}\", type},\n                       {\"%{g1_or_g2}\", g1_or_g2},\n                       {\"%{G1_or_G2}\", base::CapitalizeASCII(g1_or_g2)},\n                       {\"%{Fq_or_Fq2}\", base::CapitalizeASCII(fq_or_fq2)},\n                   });\n  return WriteHdr(content, false);\n}\n\nint GenerationConfig::GenerateG1TypeTraitsHdr() const {\n  return GeneratePointTypeTraitsHdr(\"g1\", \"fq\");\n}\n\nint GenerationConfig::GenerateG2TypeTraitsHdr() const {\n  return GeneratePointTypeTraitsHdr(\"g2\", \"fq2\");\n}\n\nint GenerationConfig::GenerateMSMHdr() const {\n  std::string tpl_content;\n  CHECK(base::ReadFileToString(msm_hdr_tpl_path, &tpl_content));\n\n  std::string content = absl::StrReplaceAll(\n      tpl_content,\n      {{\"%{header_dir_name}\", c::math::GetLocation(type)}, {\"%{type}\", type}});\n  return WriteHdr(content, true);\n}\n\nint GenerationConfig::GenerateMSMSrc() const {\n  std::string tpl_content;\n  CHECK(base::ReadFileToString(msm_src_tpl_path, &tpl_content));\n\n  std::string content = absl::StrReplaceAll(\n      tpl_content,\n      {{\"%{header_dir_name}\", c::math::GetLocation(type)}, {\"%{type}\", type}});\n  return WriteSrc(content);\n}\n\nint GenerationConfig::GenerateMSMGpuHdr() const {\n  std::string tpl_content;\n  CHECK(base::ReadFileToString(msm_gpu_hdr_tpl_path, &tpl_content));\n\n  std::string content = absl::StrReplaceAll(\n      tpl_content, {\n                       {\"%{header_dir_name}\", c::math::GetLocation(type)},\n                       {\"%{type}\", type},\n                   });\n  return WriteHdr(content, true);\n}\n\nint GenerationConfig::GenerateMSMGpuSrc() const {\n  std::string tpl_content;\n  CHECK(base::ReadFileToString(msm_gpu_src_tpl_path, &tpl_content));\n\n  std::string content = absl::StrReplaceAll(\n      tpl_content, {\n                       {\"%{header_dir_name}\", c::math::GetLocation(type)},\n                       {\"%{type}\", type},\n                   });\n  return WriteSrc(content);\n}\n\nint RealMain(int argc, char** argv) {\n  GenerationConfig config;\n  config.generator = \"//tachyon/c/math/elliptic_curves/generator\";\n\n  base::FlagParser parser;\n  parser.AddFlag<base::FilePathFlag>(&config.out)\n      .set_long_name(\"--out\")\n      .set_help(\"path to output\");\n  parser.AddFlag<base::StringFlag>(&config.type)\n      .set_long_name(\"--type\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.point_hdr_tpl_path)\n      .set_long_name(\"--point_hdr_tpl_path\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.point_src_tpl_path)\n      .set_long_name(\"--point_src_tpl_path\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.point_traits_hdr_tpl_path)\n      .set_long_name(\"--point_traits_hdr_tpl_path\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.point_type_traits_hdr_tpl_path)\n      .set_long_name(\"--point_type_traits_hdr_tpl_path\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.msm_hdr_tpl_path)\n      .set_long_name(\"--msm_hdr_tpl_path\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.msm_src_tpl_path)\n      .set_long_name(\"--msm_src_tpl_path\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.msm_gpu_hdr_tpl_path)\n      .set_long_name(\"--msm_gpu_hdr_tpl_path\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.msm_gpu_src_tpl_path)\n      .set_long_name(\"--msm_gpu_src_tpl_path\")\n      .set_required();\n\n  std::string error;\n  if (!parser.Parse(argc, argv, &error)) {\n    tachyon_cerr << error << std::endl;\n    return 1;\n  }\n\n  if (base::EndsWith(config.out.value(), \"g1.h\")) {\n    return config.GenerateG1Hdr();\n  } else if (base::EndsWith(config.out.value(), \"g1.cc\")) {\n    return config.GenerateG1Src();\n  } else if (base::EndsWith(config.out.value(), \"g1_point_traits.h\")) {\n    return config.GenerateG1TraitsHdr();\n  } else if (base::EndsWith(config.out.value(), \"g1_point_type_traits.h\")) {\n    return config.GenerateG1TypeTraitsHdr();\n  } else if (base::EndsWith(config.out.value(), \"g2.h\")) {\n    return config.GenerateG2Hdr();\n  } else if (base::EndsWith(config.out.value(), \"g2.cc\")) {\n    return config.GenerateG2Src();\n  } else if (base::EndsWith(config.out.value(), \"g2_point_traits.h\")) {\n    return config.GenerateG2TraitsHdr();\n  } else if (base::EndsWith(config.out.value(), \"g2_point_type_traits.h\")) {\n    return config.GenerateG2TypeTraitsHdr();\n  } else if (base::EndsWith(config.out.value(), \"msm.h\")) {\n    return config.GenerateMSMHdr();\n  } else if (base::EndsWith(config.out.value(), \"msm.cc\")) {\n    return config.GenerateMSMSrc();\n  } else if (base::EndsWith(config.out.value(), \"msm_gpu.h\")) {\n    return config.GenerateMSMGpuHdr();\n  } else if (base::EndsWith(config.out.value(), \"msm_gpu.cc\")) {\n    return config.GenerateMSMGpuSrc();\n  } else {\n    tachyon_cerr << \"suffix not supported:\" << config.out << std::endl;\n    return 1;\n  }\n}\n\n}  // namespace tachyon\n\nint main(int argc, char** argv) { return tachyon::RealMain(argc, argv); }\n"
  },
  {
    "path": "tachyon/c/math/elliptic_curves/generator/generator_util.cc",
    "content": "#include \"tachyon/c/math/elliptic_curves/generator/generator_util.h\"\n\n#include \"tachyon/base/logging.h\"\n\nnamespace tachyon::c::math {\n\nstd::string GetLocation(std::string_view type) {\n  if (type == \"bn254\") {\n    return \"bn/bn254\";\n  } else if (type == \"bls12_381\") {\n    return \"bls12/bls12_381\";\n  }\n  NOTREACHED() << \"Unsupported type: \" << type;\n  return \"\";\n}\n\n}  // namespace tachyon::c::math\n"
  },
  {
    "path": "tachyon/c/math/elliptic_curves/generator/generator_util.h",
    "content": "#ifndef TACHYON_C_MATH_ELLIPTIC_CURVES_GENERATOR_GENERATOR_UTIL_H_\n#define TACHYON_C_MATH_ELLIPTIC_CURVES_GENERATOR_GENERATOR_UTIL_H_\n\n#include <string>\n\nnamespace tachyon::c::math {\n\nstd::string GetLocation(std::string_view type);\n\n}  // namespace tachyon::c::math\n\n#endif  // TACHYON_C_MATH_ELLIPTIC_CURVES_GENERATOR_GENERATOR_UTIL_H_\n"
  },
  {
    "path": "tachyon/c/math/elliptic_curves/generator/msm.cc.tpl",
    "content": "// clang-format off\n#include \"tachyon/c/math/elliptic_curves/%{header_dir_name}/g1_point_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/%{header_dir_name}/g1_point_type_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/%{header_dir_name}/fr_type_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/msm/msm.h\"\n\nstruct tachyon_%{type}_g1_msm : public tachyon::c::math::MSMApi<tachyon::math::%{type}::G1AffinePoint> {\n  using tachyon::c::math::MSMApi<tachyon::math::%{type}::G1AffinePoint>::MSMApi;\n};\n\ntachyon_%{type}_g1_msm_ptr tachyon_%{type}_g1_create_msm(uint8_t degree) {\n  return new tachyon_%{type}_g1_msm(degree);\n}\n\nvoid tachyon_%{type}_g1_destroy_msm(tachyon_%{type}_g1_msm_ptr ptr) {\n  delete ptr;\n}\n\ntachyon_%{type}_g1_jacobian* tachyon_%{type}_g1_point2_msm(\n    tachyon_%{type}_g1_msm_ptr ptr, const tachyon_%{type}_g1_point2* bases,\n    const tachyon_%{type}_fr* scalars, size_t size) {\n  return tachyon::c::math::DoMSM<tachyon::math::%{type}::G1JacobianPoint>(\n      *ptr, bases, scalars, size);\n}\n\ntachyon_%{type}_g1_jacobian* tachyon_%{type}_g1_affine_msm(\n    tachyon_%{type}_g1_msm_ptr ptr, const tachyon_%{type}_g1_affine* bases,\n    const tachyon_%{type}_fr* scalars, size_t size) {\n  return tachyon::c::math::DoMSM<tachyon::math::%{type}::G1JacobianPoint>(\n      *ptr, bases, scalars, size);\n}\n// clang-format on\n"
  },
  {
    "path": "tachyon/c/math/elliptic_curves/generator/msm.h.tpl",
    "content": "// clang-format off\n/**\n * @file %{type}_msm.h\n * @brief Multi-scalar multiplication (MSM) operations for %{type} curve points in a G1 group.\n *\n * This header file defines the interface for performing multi-scalar multiplication\n * operations on points of the G1 group of the %{type} curve. MSM is a crucial operation\n * in cryptographic schemes, enabling efficient computation of the sum of scalar\n * multiplications of points.\n *\n * @example msm.cc\n */\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include \"tachyon/c/export.h\"\n#include \"tachyon/c/math/elliptic_curves/%{header_dir_name}/fr.h\"\n#include \"tachyon/c/math/elliptic_curves/%{header_dir_name}/g1.h\"\n\ntypedef struct tachyon_%{type}_g1_msm* tachyon_%{type}_g1_msm_ptr;\n\n%{extern_c_front}\n\n/**\n * @brief Creates a new MSM context for the G1 group with a specified polynomial degree.\n * @param degree The degree of the polynomial for the MSM operation. Currently not used.\n * @return A pointer to the newly created MSM context.\n */\nTACHYON_C_EXPORT tachyon_%{type}_g1_msm_ptr tachyon_%{type}_g1_create_msm(uint8_t degree);\n\n/**\n * @brief Destroys an MSM context, freeing its resources.\n * @param ptr The pointer to the MSM context to destroy.\n */\nTACHYON_C_EXPORT void tachyon_%{type}_g1_destroy_msm(tachyon_%{type}_g1_msm_ptr ptr);\n\n/**\n * @brief Computes MSM using projective bases and scalars.\n * @param ptr The MSM context.\n * @param bases Array of projective points to be multiplied by corresponding scalars.\n * @param scalars Array of scalars for the multiplication.\n * @param size The number of points and scalars (must be the same).\n * @return A pointer to the result of the MSM operation in Jacobian coordinates.\n */\nTACHYON_C_EXPORT tachyon_%{type}_g1_jacobian* tachyon_%{type}_g1_point2_msm(\n    tachyon_%{type}_g1_msm_ptr ptr, const tachyon_%{type}_g1_point2* bases,\n    const tachyon_%{type}_fr* scalars, size_t size);\n\n/**\n * @brief Computes MSM using affine bases and scalars.\n * @param ptr The MSM context.\n * @param bases Array of affine points to be multiplied by corresponding scalars.\n * @param scalars Array of scalars for the multiplication.\n * @param size The number of points and scalars (must be the same).\n * @return A pointer to the result of the MSM operation in Jacobian coordinates.\n */\nTACHYON_C_EXPORT tachyon_%{type}_g1_jacobian* tachyon_%{type}_g1_affine_msm(\n    tachyon_%{type}_g1_msm_ptr ptr, const tachyon_%{type}_g1_affine* bases,\n    const tachyon_%{type}_fr* scalars, size_t size);\n// clang-format on\n"
  },
  {
    "path": "tachyon/c/math/elliptic_curves/generator/msm_gpu.cc.tpl",
    "content": "// clang-format off\n#include <tuple>\n\n#include \"tachyon/c/math/elliptic_curves/%{header_dir_name}/g1_point_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/%{header_dir_name}/g1_point_type_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/msm/msm_gpu.h\"\n#include \"tachyon/math/elliptic_curves/%{header_dir_name}/g1.h\"\n\nstruct tachyon_%{type}_g1_msm_gpu : public tachyon::c::math::MSMGpuApi<tachyon::math::%{type}::G1AffinePoint> {\n  using tachyon::c::math::MSMGpuApi<tachyon::math::%{type}::G1AffinePoint>::MSMGpuApi;\n};\n\ntachyon_%{type}_g1_msm_gpu_ptr tachyon_%{type}_g1_create_msm_gpu(uint8_t degree) {\n  return new tachyon_%{type}_g1_msm_gpu(degree);\n}\n\nvoid tachyon_%{type}_g1_destroy_msm_gpu(tachyon_%{type}_g1_msm_gpu_ptr ptr) {\n  delete ptr;\n}\n\ntachyon_%{type}_g1_jacobian* tachyon_%{type}_g1_point2_msm_gpu(\n    tachyon_%{type}_g1_msm_gpu_ptr ptr, const tachyon_%{type}_g1_point2* bases,\n    const tachyon_%{type}_fr* scalars, size_t size) {\n  return tachyon::c::math::DoMSMGpu<tachyon::math::%{type}::G1JacobianPoint>(\n      *ptr, bases, scalars, size);\n}\n\ntachyon_%{type}_g1_jacobian* tachyon_%{type}_g1_affine_msm_gpu(\n    tachyon_%{type}_g1_msm_gpu_ptr ptr, const tachyon_%{type}_g1_affine* bases,\n    const tachyon_%{type}_fr* scalars, size_t size) {\n  return tachyon::c::math::DoMSMGpu<tachyon::math::%{type}::G1JacobianPoint>(\n      *ptr, bases, scalars, size);\n}\n// clang-format on\n"
  },
  {
    "path": "tachyon/c/math/elliptic_curves/generator/msm_gpu.h.tpl",
    "content": "// clang-format off\n/**\n * @file %{type}_msm_gpu.h\n * @brief GPU-accelerated multi-scalar multiplication (MSM) operations for %{type} curve points in a G1 group.\n *\n * This header file defines the interface for performing GPU-accelerated multi-scalar multiplication\n * operations on points of the G1 group of the %{type} curve.\n */\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include \"tachyon/c/export.h\"\n#include \"tachyon/c/math/elliptic_curves/%{header_dir_name}/fr.h\"\n#include \"tachyon/c/math/elliptic_curves/%{header_dir_name}/g1.h\"\n\ntypedef struct tachyon_%{type}_g1_msm_gpu* tachyon_%{type}_g1_msm_gpu_ptr;\n\n%{extern_c_front}\n\n/**\n * @brief Creates a new GPU-accelerated MSM context for the G1 group with a specified polynomial degree.\n * @param degree The degree of the polynomial for the MSM operation. This parameter may influence the allocation and optimization strategy.\n * @return A pointer to the newly created GPU-accelerated MSM context.\n */\nTACHYON_C_EXPORT tachyon_%{type}_g1_msm_gpu_ptr tachyon_%{type}_g1_create_msm_gpu(uint8_t degree);\n\n/**\n * @brief Destroys a GPU-accelerated MSM context, freeing its resources.\n * @param ptr The pointer to the MSM context to destroy.\n */\nTACHYON_C_EXPORT void tachyon_%{type}_g1_destroy_msm_gpu(tachyon_%{type}_g1_msm_gpu_ptr ptr);\n\n/**\n * @brief Computes MSM using projective bases and scalars on the GPU.\n * @param ptr The MSM context.\n * @param bases Array of projective points to be multiplied by corresponding scalars.\n * @param scalars Array of scalars for the multiplication.\n * @param size The number of points and scalars (must be the same).\n * @return A pointer to the result of the MSM operation in Jacobian coordinates.\n */\nTACHYON_C_EXPORT tachyon_%{type}_g1_jacobian* tachyon_%{type}_g1_point2_msm_gpu(\n    tachyon_%{type}_g1_msm_gpu_ptr ptr, const tachyon_%{type}_g1_point2* bases,\n    const tachyon_%{type}_fr* scalars, size_t size);\n\n/**\n * @brief Computes MSM using affine bases and scalars on the GPU.\n * @param ptr The MSM context.\n * @param bases Array of affine points to be multiplied by corresponding scalars.\n * @param scalars Array of scalars for the multiplication.\n * @param size The number of points and scalars (must be the same).\n * @return A pointer to the result of the MSM operation in Jacobian coordinates.\n */\nTACHYON_C_EXPORT tachyon_%{type}_g1_jacobian* tachyon_%{type}_g1_affine_msm_gpu(\n    tachyon_%{type}_g1_msm_gpu_ptr ptr, const tachyon_%{type}_g1_affine* bases,\n    const tachyon_%{type}_fr* scalars, size_t size);\n// clang-format on\n"
  },
  {
    "path": "tachyon/c/math/elliptic_curves/generator/point.cc.tpl",
    "content": "// clang-format off\n#include \"tachyon/c/math/elliptic_curves/%{header_dir_name}/%{fq_or_fq2}_type_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/%{header_dir_name}/%{g1_or_g2}_point_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/%{header_dir_name}/%{g1_or_g2}_point_type_traits.h\"\n#include \"tachyon/math/elliptic_curves/%{header_dir_name}/%{g1_or_g2}.h\"\n\nvoid tachyon_%{type}_%{g1_or_g2}_init() {\n  tachyon::math::%{type}::%{G1_or_G2}Curve::Init();\n}\n\ntachyon_%{type}_%{g1_or_g2}_affine tachyon_%{type}_%{g1_or_g2}_affine_zero() {\n  using namespace tachyon::c;\n  using CurvePoint = typename math::PointTraits<tachyon_%{type}_%{g1_or_g2}_affine>::CurvePoint;\n  return base::c_cast(CurvePoint::Zero());\n}\n\ntachyon_%{type}_%{g1_or_g2}_projective tachyon_%{type}_%{g1_or_g2}_projective_zero() {\n  using namespace tachyon::c;\n  using CurvePoint = typename math::PointTraits<tachyon_%{type}_%{g1_or_g2}_projective>::CurvePoint;\n  return base::c_cast(CurvePoint::Zero());\n}\n\ntachyon_%{type}_%{g1_or_g2}_jacobian tachyon_%{type}_%{g1_or_g2}_jacobian_zero() {\n  using namespace tachyon::c;\n  using CurvePoint = typename math::PointTraits<tachyon_%{type}_%{g1_or_g2}_jacobian>::CurvePoint;\n  return base::c_cast(CurvePoint::Zero());\n}\n\ntachyon_%{type}_%{g1_or_g2}_xyzz tachyon_%{type}_%{g1_or_g2}_xyzz_zero() {\n  using namespace tachyon::c;\n  using CurvePoint = typename math::PointTraits<tachyon_%{type}_%{g1_or_g2}_xyzz>::CurvePoint;\n  return base::c_cast(CurvePoint::Zero());\n}\n\ntachyon_%{type}_%{g1_or_g2}_affine tachyon_%{type}_%{g1_or_g2}_affine_generator() {\n  using namespace tachyon::c;\n  using CurvePoint = typename math::PointTraits<tachyon_%{type}_%{g1_or_g2}_affine>::CurvePoint;\n  return base::c_cast(CurvePoint::Generator());\n}\n\ntachyon_%{type}_%{g1_or_g2}_projective tachyon_%{type}_%{g1_or_g2}_projective_generator() {\n  using namespace tachyon::c;\n  using CurvePoint = typename math::PointTraits<tachyon_%{type}_%{g1_or_g2}_projective>::CurvePoint;\n  return base::c_cast(CurvePoint::Generator());\n}\n\ntachyon_%{type}_%{g1_or_g2}_jacobian tachyon_%{type}_%{g1_or_g2}_jacobian_generator() {\n  using namespace tachyon::c;\n  using CurvePoint = typename math::PointTraits<tachyon_%{type}_%{g1_or_g2}_jacobian>::CurvePoint;\n  return base::c_cast(CurvePoint::Generator());\n}\n\ntachyon_%{type}_%{g1_or_g2}_xyzz tachyon_%{type}_%{g1_or_g2}_xyzz_generator() {\n  using namespace tachyon::c;\n  using CurvePoint = typename math::PointTraits<tachyon_%{type}_%{g1_or_g2}_xyzz>::CurvePoint;\n  return base::c_cast(CurvePoint::Generator());\n}\n\ntachyon_%{type}_%{g1_or_g2}_affine tachyon_%{type}_%{g1_or_g2}_affine_random() {\n  using namespace tachyon::c;\n  using CurvePoint = typename math::PointTraits<tachyon_%{type}_%{g1_or_g2}_affine>::CurvePoint;\n  return base::c_cast(CurvePoint::Random());\n}\n\ntachyon_%{type}_%{g1_or_g2}_projective tachyon_%{type}_%{g1_or_g2}_projective_random() {\n  using namespace tachyon::c;\n  using CurvePoint = typename math::PointTraits<tachyon_%{type}_%{g1_or_g2}_projective>::CurvePoint;\n  return base::c_cast(CurvePoint::Random());\n}\n\ntachyon_%{type}_%{g1_or_g2}_jacobian tachyon_%{type}_%{g1_or_g2}_jacobian_random() {\n  using namespace tachyon::c;\n  using CurvePoint = typename math::PointTraits<tachyon_%{type}_%{g1_or_g2}_jacobian>::CurvePoint;\n  return base::c_cast(CurvePoint::Random());\n}\n\ntachyon_%{type}_%{g1_or_g2}_xyzz tachyon_%{type}_%{g1_or_g2}_xyzz_random() {\n  using namespace tachyon::c;\n  using CurvePoint = typename math::PointTraits<tachyon_%{type}_%{g1_or_g2}_xyzz>::CurvePoint;\n  return base::c_cast(CurvePoint::Random());\n}\n\ntachyon_%{type}_%{g1_or_g2}_jacobian tachyon_%{type}_%{g1_or_g2}_affine_add(const tachyon_%{type}_%{g1_or_g2}_affine* a, const tachyon_%{type}_%{g1_or_g2}_affine* b) {\n  using namespace tachyon::c::base;\n  return c_cast(native_cast(*a).Add(native_cast(*b)));\n}\n\ntachyon_%{type}_%{g1_or_g2}_projective tachyon_%{type}_%{g1_or_g2}_projective_add(const tachyon_%{type}_%{g1_or_g2}_projective* a, const tachyon_%{type}_%{g1_or_g2}_projective* b) {\n  using namespace tachyon::c::base;\n  return c_cast(native_cast(*a).Add(native_cast(*b)));\n}\n\ntachyon_%{type}_%{g1_or_g2}_projective tachyon_%{type}_%{g1_or_g2}_projective_add_mixed(const tachyon_%{type}_%{g1_or_g2}_projective* a, const tachyon_%{type}_%{g1_or_g2}_affine* b) {\n  using namespace tachyon::c::base;\n  return c_cast(native_cast(*a).Add(native_cast(*b)));\n}\n\ntachyon_%{type}_%{g1_or_g2}_jacobian tachyon_%{type}_%{g1_or_g2}_jacobian_add(const tachyon_%{type}_%{g1_or_g2}_jacobian* a, const tachyon_%{type}_%{g1_or_g2}_jacobian* b) {\n  using namespace tachyon::c::base;\n  return c_cast(native_cast(*a).Add(native_cast(*b)));\n}\n\ntachyon_%{type}_%{g1_or_g2}_jacobian tachyon_%{type}_%{g1_or_g2}_jacobian_add_mixed(const tachyon_%{type}_%{g1_or_g2}_jacobian* a, const tachyon_%{type}_%{g1_or_g2}_affine* b) {\n  using namespace tachyon::c::base;\n  return c_cast(native_cast(*a).Add(native_cast(*b)));\n}\n\ntachyon_%{type}_%{g1_or_g2}_xyzz tachyon_%{type}_%{g1_or_g2}_xyzz_add(const tachyon_%{type}_%{g1_or_g2}_xyzz* a, const tachyon_%{type}_%{g1_or_g2}_xyzz* b) {\n  using namespace tachyon::c::base;\n  return c_cast(native_cast(*a).Add(native_cast(*b)));\n}\n\ntachyon_%{type}_%{g1_or_g2}_xyzz tachyon_%{type}_%{g1_or_g2}_xyzz_add_mixed(const tachyon_%{type}_%{g1_or_g2}_xyzz* a, const tachyon_%{type}_%{g1_or_g2}_affine* b) {\n  using namespace tachyon::c::base;\n  return c_cast(native_cast(*a).Add(native_cast(*b)));\n}\n\ntachyon_%{type}_%{g1_or_g2}_jacobian tachyon_%{type}_%{g1_or_g2}_affine_sub(const tachyon_%{type}_%{g1_or_g2}_affine* a, const tachyon_%{type}_%{g1_or_g2}_affine* b) {\n  using namespace tachyon::c::base;\n  return c_cast(native_cast(*a).Add(-native_cast(*b)));\n}\n\ntachyon_%{type}_%{g1_or_g2}_projective tachyon_%{type}_%{g1_or_g2}_projective_sub(const tachyon_%{type}_%{g1_or_g2}_projective* a, const tachyon_%{type}_%{g1_or_g2}_projective* b) {\n  using namespace tachyon::c::base;\n  return c_cast(native_cast(*a).Add(-native_cast(*b)));\n}\n\ntachyon_%{type}_%{g1_or_g2}_projective tachyon_%{type}_%{g1_or_g2}_projective_sub_mixed(const tachyon_%{type}_%{g1_or_g2}_projective* a, const tachyon_%{type}_%{g1_or_g2}_affine* b) {\n  using namespace tachyon::c::base;\n  return c_cast(native_cast(*a).Add(-native_cast(*b)));\n}\n\ntachyon_%{type}_%{g1_or_g2}_jacobian tachyon_%{type}_%{g1_or_g2}_jacobian_sub(const tachyon_%{type}_%{g1_or_g2}_jacobian* a, const tachyon_%{type}_%{g1_or_g2}_jacobian* b) {\n  using namespace tachyon::c::base;\n  return c_cast(native_cast(*a).Add(-native_cast(*b)));\n}\n\ntachyon_%{type}_%{g1_or_g2}_jacobian tachyon_%{type}_%{g1_or_g2}_jacobian_sub_mixed(const tachyon_%{type}_%{g1_or_g2}_jacobian* a, const tachyon_%{type}_%{g1_or_g2}_affine* b) {\n  using namespace tachyon::c::base;\n  return c_cast(native_cast(*a).Add(-native_cast(*b)));\n}\n\ntachyon_%{type}_%{g1_or_g2}_xyzz tachyon_%{type}_%{g1_or_g2}_xyzz_sub(const tachyon_%{type}_%{g1_or_g2}_xyzz* a, const tachyon_%{type}_%{g1_or_g2}_xyzz* b) {\n  using namespace tachyon::c::base;\n  return c_cast(native_cast(*a).Add(-native_cast(*b)));\n}\n\ntachyon_%{type}_%{g1_or_g2}_xyzz tachyon_%{type}_%{g1_or_g2}_xyzz_sub_mixed(const tachyon_%{type}_%{g1_or_g2}_xyzz* a, const tachyon_%{type}_%{g1_or_g2}_affine* b) {\n  using namespace tachyon::c::base;\n  return c_cast(native_cast(*a).Add(-native_cast(*b)));\n}\n\ntachyon_%{type}_%{g1_or_g2}_affine tachyon_%{type}_%{g1_or_g2}_affine_neg(const tachyon_%{type}_%{g1_or_g2}_affine* a) {\n  using namespace tachyon::c::base;\n  return c_cast(native_cast(*a).Negate());\n}\n\ntachyon_%{type}_%{g1_or_g2}_projective tachyon_%{type}_%{g1_or_g2}_projective_neg(const tachyon_%{type}_%{g1_or_g2}_projective* a) {\n  using namespace tachyon::c::base;\n  return c_cast(native_cast(*a).Negate());\n}\n\ntachyon_%{type}_%{g1_or_g2}_jacobian tachyon_%{type}_%{g1_or_g2}_jacobian_neg(const tachyon_%{type}_%{g1_or_g2}_jacobian* a) {\n  using namespace tachyon::c::base;\n  return c_cast(native_cast(*a).Negate());\n}\n\ntachyon_%{type}_%{g1_or_g2}_xyzz tachyon_%{type}_%{g1_or_g2}_xyzz_neg(const tachyon_%{type}_%{g1_or_g2}_xyzz* a) {\n  using namespace tachyon::c::base;\n  return c_cast(native_cast(*a).Negate());\n}\n\ntachyon_%{type}_%{g1_or_g2}_jacobian tachyon_%{type}_%{g1_or_g2}_affine_dbl(const tachyon_%{type}_%{g1_or_g2}_affine* a) {\n  using namespace tachyon::c::base;\n  return c_cast(native_cast(*a).Double());\n}\n\ntachyon_%{type}_%{g1_or_g2}_projective tachyon_%{type}_%{g1_or_g2}_projective_dbl(const tachyon_%{type}_%{g1_or_g2}_projective* a) {\n  using namespace tachyon::c::base;\n  return c_cast(native_cast(*a).Double());\n}\n\ntachyon_%{type}_%{g1_or_g2}_jacobian tachyon_%{type}_%{g1_or_g2}_jacobian_dbl(const tachyon_%{type}_%{g1_or_g2}_jacobian* a) {\n  using namespace tachyon::c::base;\n  return c_cast(native_cast(*a).Double());\n}\n\ntachyon_%{type}_%{g1_or_g2}_xyzz tachyon_%{type}_%{g1_or_g2}_xyzz_dbl(const tachyon_%{type}_%{g1_or_g2}_xyzz* a) {\n  using namespace tachyon::c::base;\n  return c_cast(native_cast(*a).Double());\n}\n\nbool tachyon_%{type}_%{g1_or_g2}_affine_eq(const tachyon_%{type}_%{g1_or_g2}_affine* a, const tachyon_%{type}_%{g1_or_g2}_affine* b) {\n  using namespace tachyon::c::base;\n  return native_cast(*a) == native_cast(*b);\n}\n\nbool tachyon_%{type}_%{g1_or_g2}_projective_eq(const tachyon_%{type}_%{g1_or_g2}_projective* a, const tachyon_%{type}_%{g1_or_g2}_projective* b) {\n  using namespace tachyon::c::base;\n  return native_cast(*a) == native_cast(*b);\n}\n\nbool tachyon_%{type}_%{g1_or_g2}_jacobian_eq(const tachyon_%{type}_%{g1_or_g2}_jacobian* a, const tachyon_%{type}_%{g1_or_g2}_jacobian* b) {\n  using namespace tachyon::c::base;\n  return native_cast(*a) == native_cast(*b);\n}\n\nbool tachyon_%{type}_%{g1_or_g2}_xyzz_eq(const tachyon_%{type}_%{g1_or_g2}_xyzz* a, const tachyon_%{type}_%{g1_or_g2}_xyzz* b) {\n  using namespace tachyon::c::base;\n  return native_cast(*a) == native_cast(*b);\n}\n\nbool tachyon_%{type}_%{g1_or_g2}_affine_ne(const tachyon_%{type}_%{g1_or_g2}_affine* a, const tachyon_%{type}_%{g1_or_g2}_affine* b) {\n  using namespace tachyon::c::base;\n  return native_cast(*a) != native_cast(*b);\n}\n\nbool tachyon_%{type}_%{g1_or_g2}_projective_ne(const tachyon_%{type}_%{g1_or_g2}_projective* a, const tachyon_%{type}_%{g1_or_g2}_projective* b) {\n  using namespace tachyon::c::base;\n  return native_cast(*a) != native_cast(*b);\n}\n\nbool tachyon_%{type}_%{g1_or_g2}_jacobian_ne(const tachyon_%{type}_%{g1_or_g2}_jacobian* a, const tachyon_%{type}_%{g1_or_g2}_jacobian* b) {\n  using namespace tachyon::c::base;\n  return native_cast(*a) != native_cast(*b);\n}\n\nbool tachyon_%{type}_%{g1_or_g2}_xyzz_ne(const tachyon_%{type}_%{g1_or_g2}_xyzz* a, const tachyon_%{type}_%{g1_or_g2}_xyzz* b) {\n  using namespace tachyon::c::base;\n  return native_cast(*a) != native_cast(*b);\n}\n// clang-format on\n"
  },
  {
    "path": "tachyon/c/math/elliptic_curves/generator/point.h.tpl",
    "content": "// clang-format off\n/**\n * @file %{type}_%{g1_or_g2}.h\n * @brief Defines structures and operations for elliptic curve points in %{g1_or_g2} group using %{type} curve.\n *\n * This header file provides definitions for different representations of elliptic curve points (affine, projective, Jacobian, and XYZZ coordinates)\n * on the %{g1_or_g2} group of the %{type} curve, along with functions for point operations such as addition, subtraction, negation, and comparison.\n * The operations are optimized for efficiency and are crucial for cryptographic algorithms implemented using elliptic curves.\n */\n\n#include \"tachyon/c/export.h\"\n#include \"tachyon/c/math/elliptic_curves/%{header_dir_name}/%{fq_or_fq2}.h\"\n\n/**\n * @struct tachyon_%{type}_%{g1_or_g2}_affine\n * @brief Represents an affine point on the %{type} %{g1_or_g2} curve.\n *\n * This structure models an affine point on the %{type} curve with x and y coordinates.\n *\n * @example affine_point.cc\n */\nstruct tachyon_%{type}_%{g1_or_g2}_affine {\n  tachyon_%{type}_%{fq_or_fq2} x;\n  tachyon_%{type}_%{fq_or_fq2} y;\n};\n\n/**\n * @struct tachyon_%{type}_%{g1_or_g2}_projective\n * @brief Represents a point on the %{type} %{g1_or_g2} curve in projective coordinates.\n *\n * Projective coordinates are useful for elliptic curve operations as they\n * allow for operations without division, improving efficiency.\n *\n * @example projective_point.cc\n */\nstruct tachyon_%{type}_%{g1_or_g2}_projective {\n  tachyon_%{type}_%{fq_or_fq2} x;\n  tachyon_%{type}_%{fq_or_fq2} y;\n  tachyon_%{type}_%{fq_or_fq2} z;\n};\n\n/**\n * @struct tachyon_%{type}_%{g1_or_g2}_jacobian\n * @brief Represents a point on the %{type} %{g1_or_g2} curve in jacobian coordinates.\n *\n * Jacobian coordinates are a type of projective coordinates that are often\n * used for internal calculations to speed up the arithmetic on elliptic curves.\n * @example jacobian_point.cc\n */\nstruct tachyon_%{type}_%{g1_or_g2}_jacobian {\n  tachyon_%{type}_%{fq_or_fq2} x;\n  tachyon_%{type}_%{fq_or_fq2} y;\n  tachyon_%{type}_%{fq_or_fq2} z;\n};\n\n/**\n * @struct tachyon_%{type}_%{g1_or_g2}_xyzz\n * @brief Represents an extended point on the %{type} %{g1_or_g2} curve with precomputed z squared and cubed values.\n *\n * XYZZ coordinates are used for optimizing certain elliptic curve operations by precomputing\n * and storing z^2 and z^3 along with the standard coordinates.\n * @example point_xyzz.cc\n */\nstruct tachyon_%{type}_%{g1_or_g2}_xyzz {\n  tachyon_%{type}_%{fq_or_fq2} x;\n  tachyon_%{type}_%{fq_or_fq2} y;\n  tachyon_%{type}_%{fq_or_fq2} zz;\n  tachyon_%{type}_%{fq_or_fq2} zzz;\n};\n\n/**\n * @struct tachyon_%{type}_%{g1_or_g2}_point2\n * @brief Represents a simple 2D point on the %{type} %{g1_or_g2} curve in affine coordinates.\n *\n * This structure can be used for operations where only x and y coordinates are required,\n * and the point is known not to be at infinity.\n */\nstruct tachyon_%{type}_%{g1_or_g2}_point2 {\n  tachyon_%{type}_%{fq_or_fq2} x;\n  tachyon_%{type}_%{fq_or_fq2} y;\n};\n\n/**\n * @struct tachyon_%{type}_%{g1_or_g2}_point3\n * @brief Represents a 3D point on the %{type} %{g1_or_g2} curve, potentially in projective or jacobian coordinates.\n *\n * This structure is a general representation that can be used in various contexts where\n * a third coordinate is necessary for the calculations.\n */\nstruct tachyon_%{type}_%{g1_or_g2}_point3 {\n  tachyon_%{type}_%{fq_or_fq2} x;\n  tachyon_%{type}_%{fq_or_fq2} y;\n  tachyon_%{type}_%{fq_or_fq2} z;\n};\n\n/**\n * @struct tachyon_%{type}_%{g1_or_g2}_point4\n * @brief Represents a point on the %{type} %{g1_or_g2} curve with an additional auxiliary coordinate.\n *\n * This structure is designed for specific algorithms that require an extra coordinate for\n * efficient computation, such as certain multi-scalar multiplication techniques.\n */\nstruct tachyon_%{type}_%{g1_or_g2}_point4 {\n  tachyon_%{type}_%{fq_or_fq2} x;\n  tachyon_%{type}_%{fq_or_fq2} y;\n  tachyon_%{type}_%{fq_or_fq2} z;\n  tachyon_%{type}_%{fq_or_fq2} w;\n};\n\n%{extern_c_front}\n\n/**\n * @brief Initializes the %{type} %{g1_or_g2} curve.\n *\n * This function performs any necessary initializations for the %{type} %{g1_or_g2} curve operations.\n */\nTACHYON_C_EXPORT void tachyon_%{type}_%{g1_or_g2}_init();\n\n/**\n * @brief Returns the zero point in affine coordinates on the %{type} %{g1_or_g2} curve.\n * @return The zero point in affine coordinates.\n */\nTACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_affine tachyon_%{type}_%{g1_or_g2}_affine_zero();\n\n/**\n * @brief Returns the zero point in projective coordinates on the %{type} %{g1_or_g2} curve.\n * @return The zero point in projective coordinates.\n */\nTACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_projective tachyon_%{type}_%{g1_or_g2}_projective_zero();\n\n/**\n * @brief Returns the zero point in jacobian coordinates on the %{type} %{g1_or_g2} curve.\n * @return The zero point in jacobian coordinates.\n */\nTACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_jacobian tachyon_%{type}_%{g1_or_g2}_jacobian_zero();\n\n/**\n * @brief Returns the zero point in xyzz coordinates on the %{type} %{g1_or_g2} curve.\n * @return The zero point in xyzz coordinates.\n */\nTACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_xyzz tachyon_%{type}_%{g1_or_g2}_xyzz_zero();\n\n/**\n * @brief Returns the generator of the affine group %{g1_or_g2}.\n *\n * This function returns the predefined generator point of the affine group\n * %{g1_or_g2}, which is a fundamental element for elliptic curve operations.\n *\n * @return The generator point of the affine group %{g1_or_g2}.\n */\nTACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_affine tachyon_%{type}_%{g1_or_g2}_affine_generator();\n\n/**\n * @brief Returns the generator of the projective group %{g1_or_g2}.\n *\n * This function returns the predefined generator point of the projective group\n * %{g1_or_g2}, which is used in elliptic curve cryptographic algorithms where\n * projective coordinates provide efficiency improvements.\n *\n * @return The generator point of the projective group %{g1_or_g2}.\n */\nTACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_projective tachyon_%{type}_%{g1_or_g2}_projective_generator();\n\n/**\n * @brief Returns the generator of the jacobian group %{g1_or_g2}.\n *\n * This function returns the predefined generator point of the jacobian group\n * %{g1_or_g2}. jacobian coordinates are used in elliptic curve cryptography to\n * optimize point addition and doubling operations.\n *\n * @return The generator point of the jacobian group %{g1_or_g2}.\n */\nTACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_jacobian tachyon_%{type}_%{g1_or_g2}_jacobian_generator();\n\n/**\n * @brief Returns the generator of the XYZZ group %{g1_or_g2}.\n *\n * This function returns the predefined generator point of the XYZZ group\n * %{g1_or_g2}, where the ZZ and ZZZ values are precomputed to optimize certain\n * elliptic curve operations. It's an advanced representation for specific algorithms.\n *\n * @return The generator point of the XYZZ group %{g1_or_g2}.\n */\nTACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_xyzz tachyon_%{type}_%{g1_or_g2}_xyzz_generator();\n\n/**\n * @brief Generates a random affine point on the %{type} %{g1_or_g2} curve.\n * @return A random affine point on the %{g1_or_g2} curve.\n */\nTACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_affine tachyon_%{type}_%{g1_or_g2}_affine_random();\n\n/**\n * @brief Generates a random projective point on the %{type} %{g1_or_g2} curve.\n * @return A random projective point on the %{g1_or_g2} curve.\n */\nTACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_projective tachyon_%{type}_%{g1_or_g2}_projective_random();\n\n/**\n * @brief Generates a random jacobian point on the %{type} %{g1_or_g2} curve.\n * @return A random jacobian point on the %{g1_or_g2} curve.\n */\nTACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_jacobian tachyon_%{type}_%{g1_or_g2}_jacobian_random();\n\n/**\n * @brief Generates a random xyzz point on the %{type} %{g1_or_g2} curve.\n * @return A random xyzz point on the %{g1_or_g2} curve.\n */\nTACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_xyzz tachyon_%{type}_%{g1_or_g2}_xyzz_random();\n\n/**\n * @brief Adds two affine points on the %{type} %{g1_or_g2} curve. The return type for the addition of affine points is jacobian.\n * @param a Pointer to the first affine point.\n * @param b Pointer to the second affine point.\n * @return The result of adding points a and b, represented in jacobian coordinates.\n */\nTACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_jacobian tachyon_%{type}_%{g1_or_g2}_affine_add(const tachyon_%{type}_%{g1_or_g2}_affine* a, const tachyon_%{type}_%{g1_or_g2}_affine* b);\n\n/**\n * @brief Adds two projective points on the %{type} %{g1_or_g2} curve.\n * @param a Pointer to the first projective point.\n * @param b Pointer to the second projective point.\n * @return The result of the addition, as a projective point.\n */\nTACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_projective tachyon_%{type}_%{g1_or_g2}_projective_add(const tachyon_%{type}_%{g1_or_g2}_projective* a, const tachyon_%{type}_%{g1_or_g2}_projective* b);\n\n/**\n * @brief Computes the addition of a projective point and an affine point on the %{type} %{g1_or_g2} curve.\n * @param a Pointer to the projective point.\n * @param b Pointer to the affine point.\n * @return The result of the addition, as a projective point.\n */\nTACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_projective tachyon_%{type}_%{g1_or_g2}_projective_add_mixed(const tachyon_%{type}_%{g1_or_g2}_projective* a, const tachyon_%{type}_%{g1_or_g2}_affine* b);\n\n/**\n * @brief Adds two jacobian points on the %{type} %{g1_or_g2} curve.\n * @param a Pointer to the first jacobian point.\n * @param b Pointer to the second jacobian point.\n * @return The result of the addition, as a jacobian point.\n */\nTACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_jacobian tachyon_%{type}_%{g1_or_g2}_jacobian_add(const tachyon_%{type}_%{g1_or_g2}_jacobian* a, const tachyon_%{type}_%{g1_or_g2}_jacobian* b);\n\n/**\n * @brief Computes the addition of a jacobian point and an affine point on the %{type} %{g1_or_g2} curve.\n * @param a Pointer to the jacobian point.\n * @param b Pointer to the affine point.\n * @return The result of the addition, as a jacobian point.\n */\nTACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_jacobian tachyon_%{type}_%{g1_or_g2}_jacobian_add_mixed(const tachyon_%{type}_%{g1_or_g2}_jacobian* a, const tachyon_%{type}_%{g1_or_g2}_affine* b);\n\n/**\n * @brief Adds two xyzz points on the %{type} %{g1_or_g2} curve.\n * @param a Pointer to the first xyzz point.\n * @param b Pointer to the second xyzz point.\n * @return The result of the addition, as a xyzz point.\n */\nTACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_xyzz tachyon_%{type}_%{g1_or_g2}_xyzz_add(const tachyon_%{type}_%{g1_or_g2}_xyzz* a, const tachyon_%{type}_%{g1_or_g2}_xyzz* b);\n\n/**\n * @brief Computes the addition of a xyzz point and an affine point on the %{type} %{g1_or_g2} curve.\n * @param a Pointer to the xyzz point.\n * @param b Pointer to the affine point.\n * @return The result of the addition, as a xyzz point.\n */\nTACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_xyzz tachyon_%{type}_%{g1_or_g2}_xyzz_add_mixed(const tachyon_%{type}_%{g1_or_g2}_xyzz* a, const tachyon_%{type}_%{g1_or_g2}_affine* b);\n\n/**\n * @brief Computes the subtraction of two affine points on the %{type} %{g1_or_g2} curve. The return type for the addition of affine points is jacobian.\n * @param a Pointer to the affine point from which to subtract.\n * @param b Pointer to the affine point to subtract.\n * @return The result of the subtraction, as a affine point.\n */\nTACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_jacobian tachyon_%{type}_%{g1_or_g2}_affine_sub(const tachyon_%{type}_%{g1_or_g2}_affine* a, const tachyon_%{type}_%{g1_or_g2}_affine* b);\n\n/**\n * @brief Computes the subtraction of two projective points on the %{type} %{g1_or_g2} curve.\n * @param a Pointer to the projective point from which to subtract.\n * @param b Pointer to the projective point to subtract.\n * @return The result of the subtraction, as a projective point.\n */\nTACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_projective tachyon_%{type}_%{g1_or_g2}_projective_sub(const tachyon_%{type}_%{g1_or_g2}_projective* a, const tachyon_%{type}_%{g1_or_g2}_projective* b);\n\n/**\n * @brief Computes the subtraction of a projective point and an affine point on the %{type} %{g1_or_g2} curve.\n * @param a Pointer to the projective point from which to subtract.\n * @param b Pointer to the affine point to subtract.\n * @return The result of the subtraction, as a projective point.\n */\nTACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_projective tachyon_%{type}_%{g1_or_g2}_projective_sub_mixed(const tachyon_%{type}_%{g1_or_g2}_projective* a, const tachyon_%{type}_%{g1_or_g2}_affine* b);\n\n/**\n * @brief Computes the subtraction of two jacobian points on the %{type} %{g1_or_g2} curve.\n * @param a Pointer to the jacobian point from which to subtract.\n * @param b Pointer to the jacobian point to subtract.\n * @return The result of the subtraction, as a jacobian point.\n */\nTACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_jacobian tachyon_%{type}_%{g1_or_g2}_jacobian_sub(const tachyon_%{type}_%{g1_or_g2}_jacobian* a, const tachyon_%{type}_%{g1_or_g2}_jacobian* b);\n\n/**\n * @brief Computes the subtraction of a jacobian point and an affine point on the %{type} %{g1_or_g2} curve.\n * @param a Pointer to the jacobian point from which to subtract.\n * @param b Pointer to the affine point to subtract.\n * @return The result of the subtraction, as a jacobian point.\n */\nTACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_jacobian tachyon_%{type}_%{g1_or_g2}_jacobian_sub_mixed(const tachyon_%{type}_%{g1_or_g2}_jacobian* a, const tachyon_%{type}_%{g1_or_g2}_affine* b);\n\n/**\n * @brief Computes the subtraction of two xyzz points on the %{type} %{g1_or_g2} curve.\n * @param a Pointer to the xyzz point from which to subtract.\n * @param b Pointer to the xyzz point to subtract.\n * @return The result of the subtraction, as a xyzz point.\n */\nTACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_xyzz tachyon_%{type}_%{g1_or_g2}_xyzz_sub(const tachyon_%{type}_%{g1_or_g2}_xyzz* a, const tachyon_%{type}_%{g1_or_g2}_xyzz* b);\n\n/**\n * @brief Computes the subtraction of a xyzz point and an affine point on the %{type} %{g1_or_g2} curve.\n * @param a Pointer to the xyzz point from which to subtract.\n * @param b Pointer to the affine point to subtract.\n * @return The result of the subtraction, as a xyzz point.\n */\nTACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_xyzz tachyon_%{type}_%{g1_or_g2}_xyzz_sub_mixed(const tachyon_%{type}_%{g1_or_g2}_xyzz* a, const tachyon_%{type}_%{g1_or_g2}_affine* b);\n\n/**\n * @brief Computes the negation of a affine point on the %{type} %{g1_or_g2} curve.\n * @param a Pointer to the affine point to negate.\n * @return The negation of the point, as a affine point.\n */\nTACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_affine tachyon_%{type}_%{g1_or_g2}_affine_neg(const tachyon_%{type}_%{g1_or_g2}_affine* a);\n\n/**\n * @brief Computes the negation of a projective point on the %{type} %{g1_or_g2} curve.\n * @param a Pointer to the projective point to negate.\n * @return The negation of the point, as a projective point.\n */\nTACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_projective tachyon_%{type}_%{g1_or_g2}_projective_neg(const tachyon_%{type}_%{g1_or_g2}_projective* a);\n\n/**\n * @brief Computes the negation of a jacobian point on the %{type} %{g1_or_g2} curve.\n * @param a Pointer to the jacobian point to negate.\n * @return The negation of the point, as a jacobian point.\n */\nTACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_jacobian tachyon_%{type}_%{g1_or_g2}_jacobian_neg(const tachyon_%{type}_%{g1_or_g2}_jacobian* a);\n\n/**\n * @brief Computes the negation of a xyzz point on the %{type} %{g1_or_g2} curve.\n * @param a Pointer to the xyzz point to negate.\n * @return The negation of the point, as a xyzz point.\n */\nTACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_xyzz tachyon_%{type}_%{g1_or_g2}_xyzz_neg(const tachyon_%{type}_%{g1_or_g2}_xyzz* a);\n\n/**\n * @brief Doubles a affine point on the %{type} %{g1_or_g2} curve.\n * @param a Pointer to the affine point to double.\n * @return The result of doubling the point, as a affine point.\n */\nTACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_jacobian tachyon_%{type}_%{g1_or_g2}_affine_dbl(const tachyon_%{type}_%{g1_or_g2}_affine* a);\n\n/**\n * @brief Doubles a projective point on the %{type} %{g1_or_g2} curve.\n * @param a Pointer to the projective point to double.\n * @return The result of doubling the point, as a projective point.\n */\nTACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_projective tachyon_%{type}_%{g1_or_g2}_projective_dbl(const tachyon_%{type}_%{g1_or_g2}_projective* a);\n\n/**\n * @brief Doubles a jacobian point on the %{type} %{g1_or_g2} curve.\n * @param a Pointer to the jacobian point to double.\n * @return The result of doubling the point, as a jacobian point.\n */\nTACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_jacobian tachyon_%{type}_%{g1_or_g2}_jacobian_dbl(const tachyon_%{type}_%{g1_or_g2}_jacobian* a);\n\n/**\n * @brief Doubles a xyzz point on the %{type} %{g1_or_g2} curve.\n * @param a Pointer to the xyzz point to double.\n * @return The result of doubling the point, as a xyzz point.\n */\nTACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_xyzz tachyon_%{type}_%{g1_or_g2}_xyzz_dbl(const tachyon_%{type}_%{g1_or_g2}_xyzz* a);\n\n/**\n * @brief Checks if two affine points on the %{type} %{g1_or_g2} curve are equal.\n * @param a Pointer to the first affine point.\n * @param b Pointer to the second affine point.\n * @return True if the points are equal, false otherwise.\n */\nbool TACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_affine_eq(const tachyon_%{type}_%{g1_or_g2}_affine* a, const tachyon_%{type}_%{g1_or_g2}_affine* b);\n\n/**\n * @brief Checks if two projective points on the %{type} %{g1_or_g2} curve are equal.\n * @param a Pointer to the first projective point.\n * @param b Pointer to the second projective point.\n * @return True if the points are equal, false otherwise.\n */\nbool TACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_projective_eq(const tachyon_%{type}_%{g1_or_g2}_projective* a, const tachyon_%{type}_%{g1_or_g2}_projective* b);\n\n/**\n * @brief Checks if two jacobian points on the %{type} %{g1_or_g2} curve are equal.\n * @param a Pointer to the first jacobian point.\n * @param b Pointer to the second jacobian point.\n * @return True if the points are equal, false otherwise.\n */\nbool TACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_jacobian_eq(const tachyon_%{type}_%{g1_or_g2}_jacobian* a, const tachyon_%{type}_%{g1_or_g2}_jacobian* b);\n\n/**\n * @brief Checks if two xyzz points on the %{type} %{g1_or_g2} curve are equal.\n * @param a Pointer to the first xyzz point.\n * @param b Pointer to the second xyzz point.\n * @return True if the points are equal, false otherwise.\n */\nbool TACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_xyzz_eq(const tachyon_%{type}_%{g1_or_g2}_xyzz* a, const tachyon_%{type}_%{g1_or_g2}_xyzz* b);\n\n/**\n * @brief Checks if two affine points on the %{type} %{g1_or_g2} curve are not equal.\n * @param a Pointer to the first affine point.\n * @param b Pointer to the second affine point.\n * @return True if the points are not equal, false otherwise.\n */\nbool TACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_affine_ne(const tachyon_%{type}_%{g1_or_g2}_affine* a, const tachyon_%{type}_%{g1_or_g2}_affine* b);\n\n/**\n * @brief Checks if two projective points on the %{type} %{g1_or_g2} curve are not equal.\n * @param a Pointer to the first projective point.\n * @param b Pointer to the second projective point.\n * @return True if the points are not equal, false otherwise.\n */\nbool TACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_projective_ne(const tachyon_%{type}_%{g1_or_g2}_projective* a, const tachyon_%{type}_%{g1_or_g2}_projective* b);\n\n/**\n * @brief Checks if two jacobian points on the %{type} %{g1_or_g2} curve are not equal.\n * @param a Pointer to the first jacobian point.\n * @param b Pointer to the second jacobian point.\n * @return True if the points are not equal, false otherwise.\n */\nbool TACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_jacobian_ne(const tachyon_%{type}_%{g1_or_g2}_jacobian* a, const tachyon_%{type}_%{g1_or_g2}_jacobian* b);\n\n/**\n * @brief Checks if two xyzz points on the %{type} %{g1_or_g2} curve are not equal.\n * @param a Pointer to the first xyzz point.\n * @param b Pointer to the second xyzz point.\n * @return True if the points are not equal, false otherwise.\n */\nbool TACHYON_C_EXPORT tachyon_%{type}_%{g1_or_g2}_xyzz_ne(const tachyon_%{type}_%{g1_or_g2}_xyzz* a, const tachyon_%{type}_%{g1_or_g2}_xyzz* b);\n// clang-format on\n"
  },
  {
    "path": "tachyon/c/math/elliptic_curves/generator/point_traits.h.tpl",
    "content": "// clang-format off\n#include \"tachyon/c/math/elliptic_curves/%{header_dir_name}/%{fq_or_fq2}_type_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/%{header_dir_name}/fr.h\"\n#include \"tachyon/c/math/elliptic_curves/%{header_dir_name}/fr_type_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/%{header_dir_name}/%{g1_or_g2}.h\"\n#include \"tachyon/c/math/elliptic_curves/point_traits_forward.h\"\n#include \"tachyon/math/elliptic_curves/%{header_dir_name}/%{g1_or_g2}.h\"\n\nnamespace tachyon::c::math {\n\ntemplate <>\nstruct PointTraits<tachyon_%{type}_%{g1_or_g2}_affine> {\n  using Point = tachyon::math::Point2<tachyon::math::%{type}::%{Fq_or_Fq2}>;\n  using CurvePoint = tachyon::math::%{type}::%{G1_or_G2}AffinePoint;\n};\n\ntemplate <>\nstruct PointTraits<tachyon::math::%{type}::%{G1_or_G2}AffinePoint> {\n  using CPoint = tachyon_%{type}_%{g1_or_g2}_point2;\n  using CCurvePoint = tachyon_%{type}_%{g1_or_g2}_affine;\n  using CScalarField = tachyon_%{type}_fr;\n};\n\ntemplate <>\nstruct PointTraits<tachyon_%{type}_%{g1_or_g2}_projective> {\n  using Point = tachyon::math::Point3<tachyon::math::%{type}::%{Fq_or_Fq2}>;\n  using CurvePoint = tachyon::math::%{type}::%{G1_or_G2}ProjectivePoint;\n};\n\ntemplate <>\nstruct PointTraits<tachyon::math::%{type}::%{G1_or_G2}ProjectivePoint> {\n  using CPoint = tachyon_%{type}_%{g1_or_g2}_point3;\n  using CCurvePoint = tachyon_%{type}_%{g1_or_g2}_projective;\n  using CScalarField = tachyon_%{type}_fr;\n};\n\ntemplate <>\nstruct PointTraits<tachyon_%{type}_%{g1_or_g2}_jacobian> {\n  using Point = tachyon::math::Point3<tachyon::math::%{type}::%{Fq_or_Fq2}>;\n  using CurvePoint = tachyon::math::%{type}::%{G1_or_G2}JacobianPoint;\n};\n\ntemplate <>\nstruct PointTraits<tachyon::math::%{type}::%{G1_or_G2}JacobianPoint> {\n  using CPoint = tachyon_%{type}_%{g1_or_g2}_point3;\n  using CCurvePoint = tachyon_%{type}_%{g1_or_g2}_jacobian;\n  using CScalarField = tachyon_%{type}_fr;\n};\n\ntemplate <>\nstruct PointTraits<tachyon_%{type}_%{g1_or_g2}_xyzz> {\n  using Point = tachyon::math::Point4<tachyon::math::%{type}::%{Fq_or_Fq2}>;\n  using CurvePoint = tachyon::math::%{type}::%{G1_or_G2}PointXYZZ;\n};\n\ntemplate <>\nstruct PointTraits<tachyon::math::%{type}::%{G1_or_G2}PointXYZZ> {\n  using CPoint = tachyon_%{type}_%{g1_or_g2}_point4;\n  using CCurvePoint = tachyon_%{type}_%{g1_or_g2}_xyzz;\n  using CScalarField = tachyon_%{type}_fr;\n};\n\ntemplate <>\nstruct PointTraits<tachyon_%{type}_%{g1_or_g2}_point2> {\n  using Point = tachyon::math::Point2<tachyon::math::%{type}::%{Fq_or_Fq2}>;\n};\n\ntemplate <>\nstruct PointTraits<tachyon_%{type}_%{g1_or_g2}_point3> {\n  using Point = tachyon::math::Point3<tachyon::math::%{type}::%{Fq_or_Fq2}>;\n};\n\ntemplate <>\nstruct PointTraits<tachyon_%{type}_%{g1_or_g2}_point4> {\n  using Point = tachyon::math::Point4<tachyon::math::%{type}::%{Fq_or_Fq2}>;\n};\n\n}  // namespace tachyon::c::math\n// clang-format on\n"
  },
  {
    "path": "tachyon/c/math/elliptic_curves/generator/point_type_traits.h.tpl",
    "content": "// clang-format off\n#include \"tachyon/c/base/type_traits_forward.h\"\n#include \"tachyon/c/math/elliptic_curves/%{header_dir_name}/%{g1_or_g2}.h\"\n#include \"tachyon/math/elliptic_curves/%{header_dir_name}/%{g1_or_g2}.h\"\n\nnamespace tachyon::c::base {\n\ntemplate <>\nstruct TypeTraits<tachyon_%{type}_%{g1_or_g2}_affine> {\n  using NativeType = tachyon::math::%{type}::%{G1_or_G2}AffinePoint;\n};\n\ntemplate <>\nstruct TypeTraits<tachyon::math::%{type}::%{G1_or_G2}AffinePoint> {\n  using CType = tachyon_%{type}_%{g1_or_g2}_affine;\n};\n\ntemplate <>\nstruct TypeTraits<tachyon_%{type}_%{g1_or_g2}_projective> {\n  using NativeType = tachyon::math::%{type}::%{G1_or_G2}ProjectivePoint;\n};\n\ntemplate <>\nstruct TypeTraits<tachyon::math::%{type}::%{G1_or_G2}ProjectivePoint> {\n  using CType = tachyon_%{type}_%{g1_or_g2}_projective;\n};\n\ntemplate <>\nstruct TypeTraits<tachyon_%{type}_%{g1_or_g2}_jacobian> {\n  using NativeType = tachyon::math::%{type}::%{G1_or_G2}JacobianPoint;\n};\n\ntemplate <>\nstruct TypeTraits<tachyon::math::%{type}::%{G1_or_G2}JacobianPoint> {\n  using CType = tachyon_%{type}_%{g1_or_g2}_jacobian;\n};\n\ntemplate <>\nstruct TypeTraits<tachyon_%{type}_%{g1_or_g2}_xyzz> {\n  using NativeType = tachyon::math::%{type}::%{G1_or_G2}PointXYZZ;\n};\n\ntemplate <>\nstruct TypeTraits<tachyon::math::%{type}::%{G1_or_G2}PointXYZZ> {\n  using CType = tachyon_%{type}_%{g1_or_g2}_xyzz;\n};\n\ntemplate <>\nstruct TypeTraits<tachyon_%{type}_%{g1_or_g2}_point2> {\n  using NativeType = tachyon::math::Point2<tachyon::math::%{type}::%{Fq_or_Fq2}>;\n};\n\ntemplate <>\nstruct TypeTraits<tachyon::math::Point2<tachyon::math::%{type}::%{Fq_or_Fq2}>> {\n  using CType = tachyon_%{type}_%{g1_or_g2}_point2;\n};\n\ntemplate <>\nstruct TypeTraits<tachyon_%{type}_%{g1_or_g2}_point3> {\n  using NativeType = tachyon::math::Point3<tachyon::math::%{type}::%{Fq_or_Fq2}>;\n};\n\ntemplate <>\nstruct TypeTraits<tachyon::math::Point3<tachyon::math::%{type}::%{Fq_or_Fq2}>> {\n  using CType = tachyon_%{type}_%{g1_or_g2}_point3;\n};\n\ntemplate <>\nstruct TypeTraits<tachyon_%{type}_%{g1_or_g2}_point4> {\n  using NativeType = tachyon::math::Point4<tachyon::math::%{type}::%{Fq_or_Fq2}>;\n};\n\ntemplate <>\nstruct TypeTraits<tachyon::math::Point4<tachyon::math::%{type}::%{Fq_or_Fq2}>> {\n  using CType = tachyon_%{type}_%{g1_or_g2}_point4;\n};\n\n}  // namespace tachyon::c::base\n// clang-format on\n"
  },
  {
    "path": "tachyon/c/math/elliptic_curves/msm/BUILD.bazel",
    "content": "load(\"//bazel:tachyon.bzl\", \"if_gpu_is_configured\")\nload(\n    \"//bazel:tachyon_cc.bzl\",\n    \"tachyon_cc_library\",\n    \"tachyon_cc_unittest\",\n    \"tachyon_cuda_binary\",\n)\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"msm_input_provider\",\n    hdrs = [\"msm_input_provider.h\"],\n    deps = [\n        \"//tachyon/c/math/elliptic_curves:point_traits_forward\",\n        \"//tachyon/math/geometry:point2\",\n        \"@com_google_absl//absl/numeric:bits\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"msm\",\n    hdrs = [\"msm.h\"],\n    deps = [\n        \":msm_input_provider\",\n        \"//tachyon/base/console\",\n        \"//tachyon/c/base:type_traits_forward\",\n        \"//tachyon/math/elliptic_curves/msm:variable_base_msm\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"msm_gpu\",\n    hdrs = [\"msm_gpu.h\"],\n    deps = [\n        \":msm_input_provider\",\n        \"//tachyon/base/buffer:vector_buffer\",\n        \"//tachyon/base/console\",\n        \"//tachyon/base/files:file_util\",\n        \"//tachyon/c/base:type_traits_forward\",\n        \"//tachyon/device/gpu:gpu_memory\",\n        \"//tachyon/device/gpu:scoped_mem_pool\",\n        \"//tachyon/device/gpu:scoped_stream\",\n        \"//tachyon/math/elliptic_curves/msm:variable_base_msm_gpu\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"msm_unittests\",\n    srcs = [\"msm_unittest.cc\"],\n    deps = [\n        \"//tachyon/base:bits\",\n        \"//tachyon/c/math/elliptic_curves/bn/bn254:g1_test\",\n        \"//tachyon/c/math/elliptic_curves/bn/bn254:msm\",\n        \"//tachyon/math/elliptic_curves/msm/test:variable_base_msm_test_set\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"msm_gpu_unittests\",\n    srcs = if_gpu_is_configured([\"msm_gpu_unittest.cc\"]),\n    deps = [\n        \"//tachyon/base:bits\",\n        \"//tachyon/c/math/elliptic_curves/bn/bn254:msm_gpu\",\n        \"//tachyon/math/elliptic_curves/msm/test:variable_base_msm_test_set\",\n    ],\n)\n\ntachyon_cuda_binary(\n    name = \"msm_gpu_replay\",\n    srcs = [\"msm_gpu_replay.cc\"],\n    deps = [\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/base/files:file_path_flag\",\n        \"//tachyon/base/flag:flag_parser\",\n        \"//tachyon/c/math/elliptic_curves/bn/bn254:msm_gpu\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/c/math/elliptic_curves/msm/msm.h",
    "content": "#ifndef TACHYON_C_MATH_ELLIPTIC_CURVES_MSM_MSM_H_\n#define TACHYON_C_MATH_ELLIPTIC_CURVES_MSM_MSM_H_\n\n#include <tuple>\n\n#include \"tachyon/base/console/console_stream.h\"\n#include \"tachyon/c/base/type_traits_forward.h\"\n#include \"tachyon/c/math/elliptic_curves/msm/msm_input_provider.h\"\n#include \"tachyon/math/elliptic_curves/msm/variable_base_msm.h\"\n\nnamespace tachyon::c::math {\n\ntemplate <typename Point>\nstruct MSMApi {\n  MSMInputProvider<Point> provider;\n  tachyon::math::VariableBaseMSM<Point> msm;\n\n  explicit MSMApi(uint8_t degree) {\n    // NOTE(chokobole): This constructor accepts |degree| for compatibility with\n    // a constructor of MSMGpuApi. We should consider whether it accepts an\n    // argument for algorithm selection even though it only supports pippenger\n    // in a same manner.\n    std::ignore = degree;\n    {\n      // NOTE(chokobole): This should be replaced with VLOG().\n      // Currently, there's no way to delegate VLOG flags from rust side.\n      tachyon::base::ConsoleStream cs;\n      cs.Green();\n      std::cout << \"CreateMSMApi()\" << std::endl;\n    }\n  }\n};\n\ntemplate <\n    typename RetPoint, typename Point, typename CPoint, typename CScalarField,\n    typename CRetPoint = typename PointTraits<RetPoint>::CCurvePoint,\n    typename Bucket = typename tachyon::math::VariableBaseMSM<Point>::Bucket>\nCRetPoint* DoMSM(MSMApi<Point>& msm_api, const CPoint* bases,\n                 const CScalarField* scalars, size_t size) {\n  msm_api.provider.Inject(bases, scalars, size);\n  Bucket bucket;\n  CHECK(msm_api.msm.Run(msm_api.provider.bases(), msm_api.provider.scalars(),\n                        &bucket));\n  auto ret = tachyon::math::ConvertPoint<RetPoint>(bucket);\n  CRetPoint* cret = new CRetPoint();\n  *cret = c::base::c_cast(ret);\n  return cret;\n}\n\n}  // namespace tachyon::c::math\n\n#endif  // TACHYON_C_MATH_ELLIPTIC_CURVES_MSM_MSM_H_\n"
  },
  {
    "path": "tachyon/c/math/elliptic_curves/msm/msm_gpu.h",
    "content": "#ifndef TACHYON_C_MATH_ELLIPTIC_CURVES_MSM_MSM_GPU_H_\n#define TACHYON_C_MATH_ELLIPTIC_CURVES_MSM_MSM_GPU_H_\n\n#include <limits>\n#include <memory>\n#include <string>\n\n#include \"tachyon/base/buffer/vector_buffer.h\"\n#include \"tachyon/base/console/console_stream.h\"\n#include \"tachyon/base/environment.h\"\n#include \"tachyon/base/files/file_util.h\"\n#include \"tachyon/c/base/type_traits_forward.h\"\n#include \"tachyon/c/math/elliptic_curves/msm/msm_input_provider.h\"\n#include \"tachyon/device/gpu/scoped_mem_pool.h\"\n#include \"tachyon/device/gpu/scoped_stream.h\"\n#include \"tachyon/math/elliptic_curves/msm/variable_base_msm_gpu.h\"\n#include \"tachyon/math/geometry/affine_point.h\"\n#include \"tachyon/math/geometry/point_conversions.h\"\n\nnamespace tachyon::c::math {\n\ntemplate <typename Point>\nstruct MSMGpuApi {\n  using Curve = typename Point::Curve;\n\n  tachyon::device::gpu::ScopedMemPool mem_pool;\n  tachyon::device::gpu::ScopedStream stream;\n  MSMInputProvider<Point> provider;\n  std::unique_ptr<tachyon::math::VariableBaseMSMGpu<Point>> msm;\n\n  std::string msm_gpu_input_dir;\n  bool log_msm = false;\n  size_t idx = 0;\n\n  explicit MSMGpuApi(uint8_t degree) {\n    {\n      // NOTE(chokobole): This should be replaced with VLOG().\n      // Currently, there's no way to delegate VLOG flags from rust side.\n      tachyon::base::ConsoleStream cs;\n      cs.Green();\n      std::cout << \"CreateMSMGpuApi()\" << std::endl;\n    }\n\n    std::string_view msm_gpu_input_dir_str;\n    if (tachyon::base::Environment::Get(\"TACHYON_MSM_GPU_INPUT_DIR\",\n                                        &msm_gpu_input_dir_str)) {\n      msm_gpu_input_dir = std::string(msm_gpu_input_dir_str);\n    }\n    std::string_view log_msm_str;\n    if (tachyon::base::Environment::Get(\"TACHYON_LOG_MSM\", &log_msm_str)) {\n      if (log_msm_str == \"1\") log_msm = true;\n    }\n\n    gpuMemPoolProps props = {gpuMemAllocationTypePinned,\n                             gpuMemHandleTypeNone,\n                             {gpuMemLocationTypeDevice, 0}};\n    mem_pool = tachyon::device::gpu::CreateMemPool(&props);\n    uint64_t mem_pool_threshold = std::numeric_limits<uint64_t>::max();\n    GPU_MUST_SUCCEED(\n        gpuMemPoolSetAttribute(mem_pool.get(), gpuMemPoolAttrReleaseThreshold,\n                               &mem_pool_threshold),\n        \"Failed gpuMemPoolSetAttribute()\");\n\n    stream = tachyon::device::gpu::CreateStream();\n    msm.reset(new tachyon::math::VariableBaseMSMGpu<Point>(mem_pool.get(),\n                                                           stream.get()));\n  }\n};\n\ntemplate <typename RetPoint, typename Point, typename CPoint,\n          typename CScalarField,\n          typename CRetPoint = typename PointTraits<RetPoint>::CCurvePoint,\n          typename Curve = typename Point::Curve>\nCRetPoint* DoMSMGpu(MSMGpuApi<Point>& msm_api, const CPoint* bases,\n                    const CScalarField* scalars, size_t size) {\n  msm_api.provider.Inject(bases, scalars, size);\n\n  tachyon::math::ProjectivePoint<Curve> ret;\n  CHECK(msm_api.msm->Run(msm_api.provider.bases(), msm_api.provider.scalars(),\n                         &ret));\n  CRetPoint* cret = new CRetPoint();\n  if constexpr (std::is_same_v<RetPoint,\n                               tachyon::math::ProjectivePoint<Curve>>) {\n    *cret = c::base::c_cast(ret);\n  } else {\n    RetPoint ret_tmp = tachyon::math::ConvertPoint<RetPoint>(ret);\n    *cret = c::base::c_cast(ret_tmp);\n  }\n\n  if (msm_api.log_msm) {\n    // NOTE(chokobole): This should be replaced with VLOG().\n    // Currently, there's no way to delegate VLOG flags from rust side.\n    tachyon::base::ConsoleStream cs;\n    cs.Yellow();\n    std::cout << \"DoMSMGpu()\" << msm_api.idx++ << std::endl;\n    std::cout << ret.ToHexString() << std::endl;\n  }\n\n  if (!msm_api.msm_gpu_input_dir.empty()) {\n    tachyon::base::Uint8VectorBuffer buffer;\n    {\n      CHECK(buffer.Grow(tachyon::base::EstimateSize(msm_api.provider.bases())));\n      CHECK(buffer.Write(msm_api.provider.bases()));\n      tachyon::base::WriteFile(\n          tachyon::base::FilePath(absl::Substitute(\n              \"$0/bases$1.txt\", msm_api.msm_gpu_input_dir, msm_api.idx - 1)),\n          buffer.owned_buffer());\n    }\n    {\n      buffer.set_buffer_offset(0);\n      CHECK(\n          buffer.Grow(tachyon::base::EstimateSize(msm_api.provider.scalars())));\n      CHECK(buffer.Write(msm_api.provider.scalars()));\n      tachyon::base::WriteFile(\n          tachyon::base::FilePath(absl::Substitute(\n              \"$0/scalars$1.txt\", msm_api.msm_gpu_input_dir, msm_api.idx - 1)),\n          buffer.owned_buffer());\n    }\n  }\n\n  return cret;\n}\n\n}  // namespace tachyon::c::math\n\n#endif  // TACHYON_C_MATH_ELLIPTIC_CURVES_MSM_MSM_GPU_H_\n"
  },
  {
    "path": "tachyon/c/math/elliptic_curves/msm/msm_gpu_replay.cc",
    "content": "#if TACHYON_CUDA\n#include <iostream>\n\n#include \"tachyon/base/console/iostream.h\"\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/files/file_path_flag.h\"\n#include \"tachyon/base/files/file_util.h\"\n#include \"tachyon/base/flag/flag_parser.h\"\n#include \"tachyon/base/time/time.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/fq_type_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1_point_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1_point_type_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/msm_gpu.h\"\n\nnamespace tachyon {\n\nusing namespace math;\n\nstd::vector<bn254::G1AffinePoint> ReadAffinePoints(const base::FilePath& path) {\n  std::string bases;\n  CHECK(base::ReadFileToString(path, &bases));\n  base::Buffer buffer(reinterpret_cast<char*>(bases.data()), bases.size());\n  std::vector<bn254::G1AffinePoint> ret;\n  CHECK(buffer.Read(&ret));\n  CHECK(buffer.Done());\n  return ret;\n}\n\nstd::vector<bn254::Fr> ReadScalarFields(const base::FilePath& path) {\n  std::string scalars;\n  CHECK(base::ReadFileToString(path, &scalars));\n  base::Buffer buffer(reinterpret_cast<char*>(scalars.data()), scalars.size());\n  std::vector<bn254::Fr> ret;\n  CHECK(buffer.Read(&ret));\n  CHECK(buffer.Done());\n  return ret;\n}\n\nint RealMain(int argc, char** argv) {\n  if (base::Environment::Has(\"TACHYON_MSM_GPU_INPUT_DIR\")) {\n    tachyon_cerr << \"If this is set, the log is overwritten\" << std::endl;\n    return 1;\n  }\n\n  base::FlagParser parser;\n  std::vector<int> idxes;\n  int degree;\n  base::FilePath input_dir;\n  parser.AddFlag<base::Flag<std::vector<int>>>(&idxes)\n      .set_long_name(\"--idx\")\n      .set_required();\n  parser.AddFlag<base::IntFlag>(&degree)\n      .set_long_name(\"--degree\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&input_dir)\n      .set_long_name(\"--input_dir\")\n      .set_required();\n  {\n    std::string error;\n    if (!parser.Parse(argc, argv, &error)) {\n      tachyon_cerr << error << std::endl;\n      return 1;\n    }\n  }\n\n  tachyon_bn254_g1_init();\n  tachyon_bn254_g1_msm_gpu_ptr msm = tachyon_bn254_g1_create_msm_gpu(degree);\n\n  for (int idx : idxes) {\n    base::FilePath bases_txt(\n        absl::Substitute(\"$0/bases$1.txt\", input_dir.value(), idx));\n    base::FilePath scalars_txt(\n        absl::Substitute(\"$0/scalars$1.txt\", input_dir.value(), idx));\n    auto bases = ReadAffinePoints(bases_txt);\n    auto scalars = ReadScalarFields(scalars_txt);\n    CHECK_EQ(bases.size(), scalars.size());\n\n    base::TimeTicks now = base::TimeTicks::Now();\n    std::unique_ptr<tachyon_bn254_g1_jacobian> ret(\n        tachyon_bn254_g1_affine_msm_gpu(msm, c::base::c_cast(bases.data()),\n                                        c::base::c_cast(scalars.data()),\n                                        scalars.size()));\n    std::cout << (base::TimeTicks::Now() - now) << std::endl;\n    std::cout << c::base::native_cast(*ret).ToAffine().ToHexString()\n              << std::endl;\n  }\n\n  tachyon_bn254_g1_destroy_msm_gpu(msm);\n\n  return 0;\n}\n\n}  // namespace tachyon\n\nint main(int argc, char** argv) { return tachyon::RealMain(argc, argv); }\n#else\n#include \"tachyon/base/console/iostream.h\"\n\nint main(int argc, char **argv) {\n  tachyon_cerr << \"please build with --config cuda\" << std::endl;\n  return 1;\n}\n#endif  // TACHYON_CUDA\n"
  },
  {
    "path": "tachyon/c/math/elliptic_curves/msm/msm_gpu_unittest.cc",
    "content": "#include \"tachyon/c/math/elliptic_curves/bn/bn254/msm_gpu.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/bits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/fq_type_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1_point_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1_point_type_traits.h\"\n#include \"tachyon/math/elliptic_curves/msm/test/variable_base_msm_test_set.h\"\n\nnamespace tachyon::math {\n\nconstexpr size_t kNums[] = {32, 2, 5};\n\nclass MSMGpuTest : public testing::Test {\n public:\n  static void SetUpTestSuite() {\n    tachyon_bn254_g1_init();\n\n    for (size_t n : kNums) {\n      test_sets_.push_back(VariableBaseMSMTestSet<bn254::G1AffinePoint>::Random(\n          n, VariableBaseMSMMethod::kNaive));\n    }\n  }\n\n protected:\n  static std::vector<VariableBaseMSMTestSet<bn254::G1AffinePoint>> test_sets_;\n};\n\nstd::vector<VariableBaseMSMTestSet<bn254::G1AffinePoint>>\n    MSMGpuTest::test_sets_;\n\nTEST_F(MSMGpuTest, MSMPoint2) {\n  size_t max_num = *std::max_element(std::begin(kNums), std::end(kNums));\n  tachyon_bn254_g1_msm_gpu_ptr msm =\n      tachyon_bn254_g1_create_msm_gpu(base::bits::Log2Ceiling(max_num));\n\n  for (const VariableBaseMSMTestSet<bn254::G1AffinePoint>& t :\n       this->test_sets_) {\n    std::unique_ptr<tachyon_bn254_g1_jacobian> ret;\n    std::vector<Point2<bn254::Fq>> bases =\n        base::CreateVector(t.bases.size(), [&t](size_t i) {\n          return Point2<bn254::Fq>(t.bases[i].x(), t.bases[i].y());\n        });\n    ret.reset(tachyon_bn254_g1_point2_msm_gpu(\n        msm, c::base::c_cast(bases.data()), c::base::c_cast(t.scalars.data()),\n        t.scalars.size()));\n    EXPECT_EQ(c::base::native_cast(*ret), t.answer.ToJacobian());\n  }\n  tachyon_bn254_g1_destroy_msm_gpu(msm);\n}\n\nTEST_F(MSMGpuTest, MSMG1Affine) {\n  size_t max_num = *std::max_element(std::begin(kNums), std::end(kNums));\n  tachyon_bn254_g1_msm_gpu_ptr msm =\n      tachyon_bn254_g1_create_msm_gpu(base::bits::Log2Ceiling(max_num));\n\n  for (const VariableBaseMSMTestSet<bn254::G1AffinePoint>& t :\n       this->test_sets_) {\n    std::unique_ptr<tachyon_bn254_g1_jacobian> ret;\n    ret.reset(tachyon_bn254_g1_affine_msm_gpu(\n        msm, c::base::c_cast(t.bases.data()), c::base::c_cast(t.scalars.data()),\n        t.scalars.size()));\n    EXPECT_EQ(c::base::native_cast(*ret), t.answer.ToJacobian());\n  }\n  tachyon_bn254_g1_destroy_msm_gpu(msm);\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/c/math/elliptic_curves/msm/msm_input_provider.h",
    "content": "#ifndef TACHYON_C_MATH_ELLIPTIC_CURVES_MSM_MSM_INPUT_PROVIDER_H_\n#define TACHYON_C_MATH_ELLIPTIC_CURVES_MSM_MSM_INPUT_PROVIDER_H_\n\n#include <vector>\n\n#include \"absl/numeric/bits.h\"\n#include \"absl/types/span.h\"\n\n#include \"tachyon/c/math/elliptic_curves/point_traits_forward.h\"\n#include \"tachyon/math/geometry/point2.h\"\n\nnamespace tachyon::c::math {\n\ntemplate <typename AffinePoint>\nclass MSMInputProvider {\n public:\n  using ScalarField = typename AffinePoint::ScalarField;\n  using CScalarField = typename PointTraits<AffinePoint>::CScalarField;\n\n  absl::Span<const AffinePoint> bases() const { return bases_; }\n  absl::Span<const ScalarField> scalars() const { return scalars_; }\n\n  template <typename T>\n  void Inject(const T* bases_in, const CScalarField* scalars_in, size_t size) {\n    bases_ = absl::MakeConstSpan(reinterpret_cast<const AffinePoint*>(bases_in),\n                                 size);\n    scalars_ = absl::MakeConstSpan(\n        reinterpret_cast<const ScalarField*>(scalars_in), size);\n  }\n\n private:\n  absl::Span<const AffinePoint> bases_;\n  absl::Span<const ScalarField> scalars_;\n};\n\n}  // namespace tachyon::c::math\n\n#endif  // TACHYON_C_MATH_ELLIPTIC_CURVES_MSM_MSM_INPUT_PROVIDER_H_\n"
  },
  {
    "path": "tachyon/c/math/elliptic_curves/msm/msm_unittest.cc",
    "content": "#include \"tachyon/c/math/elliptic_curves/bn/bn254/msm.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/bits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/fq_type_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1_point_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1_point_type_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1_test.h\"\n#include \"tachyon/math/elliptic_curves/msm/test/variable_base_msm_test_set.h\"\n\nnamespace tachyon::math {\n\nconstexpr size_t kNums[] = {32, 2, 5};\n\nclass MSMTest : public c::math::bn254::G1Test {\n public:\n  static void SetUpTestSuite() {\n    c::math::bn254::G1Test::SetUpTestSuite();\n\n    size_t max_num = *std::max_element(std::begin(kNums), std::end(kNums));\n    msm_ = tachyon_bn254_g1_create_msm(base::bits::Log2Ceiling(max_num));\n    for (size_t n : kNums) {\n      test_sets_.push_back(VariableBaseMSMTestSet<bn254::G1AffinePoint>::Random(\n          n, VariableBaseMSMMethod::kNaive));\n    }\n  }\n\n  static void TearDownTestSuite() { tachyon_bn254_g1_destroy_msm(msm_); }\n\n protected:\n  static tachyon_bn254_g1_msm_ptr msm_;\n  static std::vector<VariableBaseMSMTestSet<bn254::G1AffinePoint>> test_sets_;\n};\n\ntachyon_bn254_g1_msm_ptr MSMTest::msm_;\nstd::vector<VariableBaseMSMTestSet<bn254::G1AffinePoint>> MSMTest::test_sets_;\n\nTEST_F(MSMTest, MSMPoint2) {\n  for (const VariableBaseMSMTestSet<bn254::G1AffinePoint>& t : test_sets_) {\n    std::unique_ptr<tachyon_bn254_g1_jacobian> ret;\n    std::vector<Point2<bn254::Fq>> bases =\n        base::CreateVector(t.bases.size(), [&t](size_t i) {\n          return Point2<bn254::Fq>(t.bases[i].x(), t.bases[i].y());\n        });\n    ret.reset(tachyon_bn254_g1_point2_msm(msm_, c::base::c_cast(bases.data()),\n                                          c::base::c_cast(t.scalars.data()),\n                                          t.scalars.size()));\n    EXPECT_EQ(c::base::native_cast(*ret), t.answer.ToJacobian());\n  }\n}\n\nTEST_F(MSMTest, MSMG1Affine) {\n  for (const VariableBaseMSMTestSet<bn254::G1AffinePoint>& t : test_sets_) {\n    std::unique_ptr<tachyon_bn254_g1_jacobian> ret;\n    ret.reset(tachyon_bn254_g1_affine_msm(msm_, c::base::c_cast(t.bases.data()),\n                                          c::base::c_cast(t.scalars.data()),\n                                          t.scalars.size()));\n    EXPECT_EQ(c::base::native_cast(*ret), t.answer.ToJacobian());\n  }\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/c/math/elliptic_curves/point_traits_forward.h",
    "content": "#ifndef TACHYON_C_MATH_ELLIPTIC_CURVES_POINT_TRAITS_FORWARD_H_\n#define TACHYON_C_MATH_ELLIPTIC_CURVES_POINT_TRAITS_FORWARD_H_\n\nnamespace tachyon::c::math {\n\ntemplate <typename Point>\nstruct PointTraits;\n\n}  // namespace tachyon::c::math\n\n#endif  // TACHYON_C_MATH_ELLIPTIC_CURVES_POINT_TRAITS_FORWARD_H_\n"
  },
  {
    "path": "tachyon/c/math/elliptic_curves/short_weierstrass/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_unittest\")\n\ntachyon_cc_unittest(\n    name = \"short_weierstrass_unittests\",\n    srcs = [\n        \"affine_point_unittest.cc\",\n        \"jacobian_point_unittest.cc\",\n        \"point_xyzz_unittest.cc\",\n        \"projective_point_unittest.cc\",\n    ],\n    deps = [\n        \"//tachyon/c/math/elliptic_curves/bn/bn254:g1\",\n        \"//tachyon/c/math/elliptic_curves/bn/bn254:g1_test\",\n        \"//tachyon/c/math/elliptic_curves/bn/bn254:g2\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:g1\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:g2\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/c/math/elliptic_curves/short_weierstrass/affine_point_unittest.cc",
    "content": "#include \"gtest/gtest.h\"\n\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1_point_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1_point_type_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g2_point_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g2_point_type_traits.h\"\n\nnamespace tachyon {\n\nnamespace {\n\nclass AffinePointTest : public testing::Test {\n public:\n  void SetUp() override {\n    tachyon_bn254_g1_init();\n    tachyon_bn254_g2_init();\n\n    g1_a_ = math::bn254::G1AffinePoint::Random();\n    g1_b_ = math::bn254::G1AffinePoint::Random();\n    g1_c_a_ = c::base::c_cast(g1_a_);\n    g1_c_b_ = c::base::c_cast(g1_b_);\n\n    g2_a_ = math::bn254::G2AffinePoint::Random();\n    g2_b_ = math::bn254::G2AffinePoint::Random();\n    g2_c_a_ = c::base::c_cast(g2_a_);\n    g2_c_b_ = c::base::c_cast(g2_b_);\n  }\n\n protected:\n  math::bn254::G1AffinePoint g1_a_;\n  math::bn254::G1AffinePoint g1_b_;\n  tachyon_bn254_g1_affine g1_c_a_;\n  tachyon_bn254_g1_affine g1_c_b_;\n\n  math::bn254::G2AffinePoint g2_a_;\n  math::bn254::G2AffinePoint g2_b_;\n  tachyon_bn254_g2_affine g2_c_a_;\n  tachyon_bn254_g2_affine g2_c_b_;\n};\n\n}  // namespace\n\nTEST_F(AffinePointTest, Zero) {\n  tachyon_bn254_g1_affine c_ret = tachyon_bn254_g1_affine_zero();\n  EXPECT_TRUE(c::base::native_cast(c_ret).IsZero());\n\n  tachyon_bn254_g2_affine c_ret2 = tachyon_bn254_g2_affine_zero();\n  EXPECT_TRUE(c::base::native_cast(c_ret2).IsZero());\n}\n\nTEST_F(AffinePointTest, Generator) {\n  tachyon_bn254_g1_affine c_ret = tachyon_bn254_g1_affine_generator();\n  EXPECT_EQ(c::base::native_cast(c_ret),\n            math::bn254::G1AffinePoint::Generator());\n\n  tachyon_bn254_g2_affine c_ret2 = tachyon_bn254_g2_affine_generator();\n  EXPECT_EQ(c::base::native_cast(c_ret2),\n            math::bn254::G2AffinePoint::Generator());\n}\n\nTEST_F(AffinePointTest, Random) {\n  tachyon_bn254_g1_affine c_ret = tachyon_bn254_g1_affine_random();\n  EXPECT_NE(c::base::native_cast(c_ret), g1_a_);\n\n  tachyon_bn254_g2_affine c_ret2 = tachyon_bn254_g2_affine_random();\n  EXPECT_NE(c::base::native_cast(c_ret2), g2_a_);\n}\n\nTEST_F(AffinePointTest, Eq) {\n  EXPECT_EQ(tachyon_bn254_g1_affine_eq(&g1_c_a_, &g1_c_b_), g1_a_ == g1_b_);\n  EXPECT_EQ(tachyon_bn254_g2_affine_eq(&g2_c_a_, &g2_c_b_), g2_a_ == g2_b_);\n}\n\nTEST_F(AffinePointTest, Ne) {\n  EXPECT_EQ(tachyon_bn254_g1_affine_ne(&g1_c_a_, &g1_c_b_), g1_a_ != g1_b_);\n  EXPECT_EQ(tachyon_bn254_g2_affine_ne(&g2_c_a_, &g2_c_b_), g2_a_ != g2_b_);\n}\n\nTEST_F(AffinePointTest, Add) {\n  tachyon_bn254_g1_jacobian c_ret =\n      tachyon_bn254_g1_affine_add(&g1_c_a_, &g1_c_b_);\n  EXPECT_EQ(c::base::native_cast(c_ret), g1_a_ + g1_b_);\n\n  tachyon_bn254_g2_jacobian c_ret2 =\n      tachyon_bn254_g2_affine_add(&g2_c_a_, &g2_c_b_);\n  EXPECT_EQ(c::base::native_cast(c_ret2), g2_a_ + g2_b_);\n}\n\nTEST_F(AffinePointTest, Sub) {\n  tachyon_bn254_g1_jacobian c_ret =\n      tachyon_bn254_g1_affine_sub(&g1_c_a_, &g1_c_b_);\n  EXPECT_EQ(c::base::native_cast(c_ret), g1_a_ - g1_b_);\n\n  tachyon_bn254_g2_jacobian c_ret2 =\n      tachyon_bn254_g2_affine_sub(&g2_c_a_, &g2_c_b_);\n  EXPECT_EQ(c::base::native_cast(c_ret2), g2_a_ - g2_b_);\n}\n\nTEST_F(AffinePointTest, Neg) {\n  tachyon_bn254_g1_affine c_ret = tachyon_bn254_g1_affine_neg(&g1_c_a_);\n  EXPECT_EQ(c::base::native_cast(c_ret), -g1_a_);\n\n  tachyon_bn254_g2_affine c_ret2 = tachyon_bn254_g2_affine_neg(&g2_c_a_);\n  EXPECT_EQ(c::base::native_cast(c_ret2), -g2_a_);\n}\n\nTEST_F(AffinePointTest, Dbl) {\n  tachyon_bn254_g1_jacobian c_ret = tachyon_bn254_g1_affine_dbl(&g1_c_a_);\n  EXPECT_EQ(c::base::native_cast(c_ret), g1_a_.Double());\n\n  tachyon_bn254_g2_jacobian c_ret2 = tachyon_bn254_g2_affine_dbl(&g2_c_a_);\n  EXPECT_EQ(c::base::native_cast(c_ret2), g2_a_.Double());\n}\n\n}  // namespace tachyon\n"
  },
  {
    "path": "tachyon/c/math/elliptic_curves/short_weierstrass/jacobian_point_unittest.cc",
    "content": "#include \"gtest/gtest.h\"\n\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1_point_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1_point_type_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g2_point_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g2_point_type_traits.h\"\n\nnamespace tachyon {\n\nnamespace {\n\nclass JacobianPointTest : public testing::Test {\n public:\n  void SetUp() override {\n    tachyon_bn254_g1_init();\n    tachyon_bn254_g2_init();\n\n    g1_a_ = math::bn254::G1JacobianPoint::Random();\n    g1_b_ = math::bn254::G1JacobianPoint::Random();\n    g1_c_a_ = c::base::c_cast(g1_a_);\n    g1_c_b_ = c::base::c_cast(g1_b_);\n\n    g2_a_ = math::bn254::G2JacobianPoint::Random();\n    g2_b_ = math::bn254::G2JacobianPoint::Random();\n    g2_c_a_ = c::base::c_cast(g2_a_);\n    g2_c_b_ = c::base::c_cast(g2_b_);\n  }\n\n protected:\n  math::bn254::G1JacobianPoint g1_a_;\n  math::bn254::G1JacobianPoint g1_b_;\n  tachyon_bn254_g1_jacobian g1_c_a_;\n  tachyon_bn254_g1_jacobian g1_c_b_;\n\n  math::bn254::G2JacobianPoint g2_a_;\n  math::bn254::G2JacobianPoint g2_b_;\n  tachyon_bn254_g2_jacobian g2_c_a_;\n  tachyon_bn254_g2_jacobian g2_c_b_;\n};\n\n}  // namespace\n\nTEST_F(JacobianPointTest, Zero) {\n  tachyon_bn254_g1_jacobian c_ret = tachyon_bn254_g1_jacobian_zero();\n  EXPECT_TRUE(c::base::native_cast(c_ret).IsZero());\n\n  tachyon_bn254_g2_jacobian c_ret2 = tachyon_bn254_g2_jacobian_zero();\n  EXPECT_TRUE(c::base::native_cast(c_ret2).IsZero());\n}\n\nTEST_F(JacobianPointTest, Generator) {\n  tachyon_bn254_g1_jacobian c_ret = tachyon_bn254_g1_jacobian_generator();\n  EXPECT_EQ(c::base::native_cast(c_ret),\n            math::bn254::G1JacobianPoint::Generator());\n\n  tachyon_bn254_g2_jacobian c_ret2 = tachyon_bn254_g2_jacobian_generator();\n  EXPECT_EQ(c::base::native_cast(c_ret2),\n            math::bn254::G2JacobianPoint::Generator());\n}\n\nTEST_F(JacobianPointTest, Random) {\n  tachyon_bn254_g1_jacobian c_ret = tachyon_bn254_g1_jacobian_random();\n  EXPECT_NE(c::base::native_cast(c_ret), g1_a_);\n\n  tachyon_bn254_g2_jacobian c_ret2 = tachyon_bn254_g2_jacobian_random();\n  EXPECT_NE(c::base::native_cast(c_ret2), g2_a_);\n}\n\nTEST_F(JacobianPointTest, Eq) {\n  EXPECT_EQ(tachyon_bn254_g1_jacobian_eq(&g1_c_a_, &g1_c_b_), g1_a_ == g1_b_);\n  EXPECT_EQ(tachyon_bn254_g2_jacobian_eq(&g2_c_a_, &g2_c_b_), g2_a_ == g2_b_);\n}\n\nTEST_F(JacobianPointTest, Ne) {\n  EXPECT_EQ(tachyon_bn254_g1_jacobian_ne(&g1_c_a_, &g1_c_b_), g1_a_ != g1_b_);\n  EXPECT_EQ(tachyon_bn254_g2_jacobian_ne(&g2_c_a_, &g2_c_b_), g2_a_ != g2_b_);\n}\n\nTEST_F(JacobianPointTest, Add) {\n  tachyon_bn254_g1_jacobian c_ret =\n      tachyon_bn254_g1_jacobian_add(&g1_c_a_, &g1_c_b_);\n  EXPECT_EQ(c::base::native_cast(c_ret), g1_a_ + g1_b_);\n\n  math::bn254::G1AffinePoint d = math::bn254::G1AffinePoint::Random();\n  tachyon_bn254_g1_affine c_d = c::base::c_cast(d);\n  c_ret = tachyon_bn254_g1_jacobian_add_mixed(&g1_c_a_, &c_d);\n  EXPECT_EQ(c::base::native_cast(c_ret), g1_a_ + d);\n\n  tachyon_bn254_g2_jacobian c_ret2 =\n      tachyon_bn254_g2_jacobian_add(&g2_c_a_, &g2_c_b_);\n  EXPECT_EQ(c::base::native_cast(c_ret2), g2_a_ + g2_b_);\n\n  math::bn254::G2AffinePoint d2 = math::bn254::G2AffinePoint::Random();\n  tachyon_bn254_g2_affine c_d2 = c::base::c_cast(d2);\n  c_ret2 = tachyon_bn254_g2_jacobian_add_mixed(&g2_c_a_, &c_d2);\n  EXPECT_EQ(c::base::native_cast(c_ret2), g2_a_ + d2);\n}\n\nTEST_F(JacobianPointTest, Sub) {\n  tachyon_bn254_g1_jacobian c_ret =\n      tachyon_bn254_g1_jacobian_sub(&g1_c_a_, &g1_c_b_);\n  EXPECT_EQ(c::base::native_cast(c_ret), g1_a_ - g1_b_);\n\n  math::bn254::G1AffinePoint d = math::bn254::G1AffinePoint::Random();\n  tachyon_bn254_g1_affine c_d = c::base::c_cast(d);\n  c_ret = tachyon_bn254_g1_jacobian_sub_mixed(&g1_c_a_, &c_d);\n  EXPECT_EQ(c::base::native_cast(c_ret), g1_a_ - d);\n\n  tachyon_bn254_g2_jacobian c_ret2 =\n      tachyon_bn254_g2_jacobian_sub(&g2_c_a_, &g2_c_b_);\n  EXPECT_EQ(c::base::native_cast(c_ret2), g2_a_ - g2_b_);\n\n  math::bn254::G2AffinePoint d2 = math::bn254::G2AffinePoint::Random();\n  tachyon_bn254_g2_affine c_d2 = c::base::c_cast(d2);\n  c_ret2 = tachyon_bn254_g2_jacobian_sub_mixed(&g2_c_a_, &c_d2);\n  EXPECT_EQ(c::base::native_cast(c_ret2), g2_a_ - d2);\n}\n\nTEST_F(JacobianPointTest, Neg) {\n  tachyon_bn254_g1_jacobian c_ret = tachyon_bn254_g1_jacobian_neg(&g1_c_a_);\n  EXPECT_EQ(c::base::native_cast(c_ret), -g1_a_);\n\n  tachyon_bn254_g2_jacobian c_ret2 = tachyon_bn254_g2_jacobian_neg(&g2_c_a_);\n  EXPECT_EQ(c::base::native_cast(c_ret2), -g2_a_);\n}\n\nTEST_F(JacobianPointTest, Dbl) {\n  tachyon_bn254_g1_jacobian c_ret = tachyon_bn254_g1_jacobian_dbl(&g1_c_a_);\n  EXPECT_EQ(c::base::native_cast(c_ret), g1_a_.Double());\n\n  tachyon_bn254_g2_jacobian c_ret2 = tachyon_bn254_g2_jacobian_dbl(&g2_c_a_);\n  EXPECT_EQ(c::base::native_cast(c_ret2), g2_a_.Double());\n}\n\n}  // namespace tachyon\n"
  },
  {
    "path": "tachyon/c/math/elliptic_curves/short_weierstrass/point_xyzz_unittest.cc",
    "content": "#include \"gtest/gtest.h\"\n\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1_point_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1_point_type_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g2_point_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g2_point_type_traits.h\"\n\nnamespace tachyon {\n\nnamespace {\n\nclass PointXYZZTest : public testing::Test {\n public:\n  void SetUp() override {\n    tachyon_bn254_g1_init();\n    tachyon_bn254_g2_init();\n\n    g1_a_ = math::bn254::G1PointXYZZ::Random();\n    g1_b_ = math::bn254::G1PointXYZZ::Random();\n    g1_c_a_ = c::base::c_cast(g1_a_);\n    g1_c_b_ = c::base::c_cast(g1_b_);\n\n    g2_a_ = math::bn254::G2PointXYZZ::Random();\n    g2_b_ = math::bn254::G2PointXYZZ::Random();\n    g2_c_a_ = c::base::c_cast(g2_a_);\n    g2_c_b_ = c::base::c_cast(g2_b_);\n  }\n\n protected:\n  math::bn254::G1PointXYZZ g1_a_;\n  math::bn254::G1PointXYZZ g1_b_;\n  tachyon_bn254_g1_xyzz g1_c_a_;\n  tachyon_bn254_g1_xyzz g1_c_b_;\n\n  math::bn254::G2PointXYZZ g2_a_;\n  math::bn254::G2PointXYZZ g2_b_;\n  tachyon_bn254_g2_xyzz g2_c_a_;\n  tachyon_bn254_g2_xyzz g2_c_b_;\n};\n\n}  // namespace\n\nTEST_F(PointXYZZTest, Zero) {\n  tachyon_bn254_g1_xyzz c_ret = tachyon_bn254_g1_xyzz_zero();\n  EXPECT_TRUE(c::base::native_cast(c_ret).IsZero());\n\n  tachyon_bn254_g2_xyzz c_ret2 = tachyon_bn254_g2_xyzz_zero();\n  EXPECT_TRUE(c::base::native_cast(c_ret2).IsZero());\n}\n\nTEST_F(PointXYZZTest, Generator) {\n  tachyon_bn254_g1_xyzz c_ret = tachyon_bn254_g1_xyzz_generator();\n  EXPECT_EQ(c::base::native_cast(c_ret), math::bn254::G1PointXYZZ::Generator());\n\n  tachyon_bn254_g2_xyzz c_ret2 = tachyon_bn254_g2_xyzz_generator();\n  EXPECT_EQ(c::base::native_cast(c_ret2),\n            math::bn254::G2PointXYZZ::Generator());\n}\n\nTEST_F(PointXYZZTest, Random) {\n  tachyon_bn254_g1_xyzz c_ret = tachyon_bn254_g1_xyzz_random();\n  EXPECT_NE(c::base::native_cast(c_ret), g1_a_);\n\n  tachyon_bn254_g2_xyzz c_ret2 = tachyon_bn254_g2_xyzz_random();\n  EXPECT_NE(c::base::native_cast(c_ret2), g2_a_);\n}\n\nTEST_F(PointXYZZTest, Eq) {\n  EXPECT_EQ(tachyon_bn254_g1_xyzz_eq(&g1_c_a_, &g1_c_b_), g1_a_ == g1_b_);\n  EXPECT_EQ(tachyon_bn254_g2_xyzz_eq(&g2_c_a_, &g2_c_b_), g2_a_ == g2_b_);\n}\n\nTEST_F(PointXYZZTest, Ne) {\n  EXPECT_EQ(tachyon_bn254_g1_xyzz_ne(&g1_c_a_, &g1_c_b_), g1_a_ != g1_b_);\n  EXPECT_EQ(tachyon_bn254_g2_xyzz_ne(&g2_c_a_, &g2_c_b_), g2_a_ != g2_b_);\n}\n\nTEST_F(PointXYZZTest, Add) {\n  tachyon_bn254_g1_xyzz c_ret = tachyon_bn254_g1_xyzz_add(&g1_c_a_, &g1_c_b_);\n  EXPECT_EQ(c::base::native_cast(c_ret), g1_a_ + g1_b_);\n\n  math::bn254::G1AffinePoint d = math::bn254::G1AffinePoint::Random();\n  tachyon_bn254_g1_affine c_d = c::base::c_cast(d);\n  c_ret = tachyon_bn254_g1_xyzz_add_mixed(&g1_c_a_, &c_d);\n  EXPECT_EQ(c::base::native_cast(c_ret), g1_a_ + d);\n\n  tachyon_bn254_g2_xyzz c_ret2 = tachyon_bn254_g2_xyzz_add(&g2_c_a_, &g2_c_b_);\n  EXPECT_EQ(c::base::native_cast(c_ret2), g2_a_ + g2_b_);\n\n  math::bn254::G2AffinePoint d2 = math::bn254::G2AffinePoint::Random();\n  tachyon_bn254_g2_affine c_d2 = c::base::c_cast(d2);\n  c_ret2 = tachyon_bn254_g2_xyzz_add_mixed(&g2_c_a_, &c_d2);\n  EXPECT_EQ(c::base::native_cast(c_ret2), g2_a_ + d2);\n}\n\nTEST_F(PointXYZZTest, Sub) {\n  tachyon_bn254_g1_xyzz c_ret = tachyon_bn254_g1_xyzz_sub(&g1_c_a_, &g1_c_b_);\n  EXPECT_EQ(c::base::native_cast(c_ret), g1_a_ - g1_b_);\n\n  math::bn254::G1AffinePoint d = math::bn254::G1AffinePoint::Random();\n  tachyon_bn254_g1_affine c_d = c::base::c_cast(d);\n  c_ret = tachyon_bn254_g1_xyzz_sub_mixed(&g1_c_a_, &c_d);\n  EXPECT_EQ(c::base::native_cast(c_ret), g1_a_ - d);\n\n  tachyon_bn254_g2_xyzz c_ret2 = tachyon_bn254_g2_xyzz_sub(&g2_c_a_, &g2_c_b_);\n  EXPECT_EQ(c::base::native_cast(c_ret2), g2_a_ - g2_b_);\n\n  math::bn254::G2AffinePoint d2 = math::bn254::G2AffinePoint::Random();\n  tachyon_bn254_g2_affine c_d2 = c::base::c_cast(d2);\n  c_ret2 = tachyon_bn254_g2_xyzz_sub_mixed(&g2_c_a_, &c_d2);\n  EXPECT_EQ(c::base::native_cast(c_ret2), g2_a_ - d2);\n}\n\nTEST_F(PointXYZZTest, Neg) {\n  tachyon_bn254_g1_xyzz c_ret = tachyon_bn254_g1_xyzz_neg(&g1_c_a_);\n  EXPECT_EQ(c::base::native_cast(c_ret), -g1_a_);\n\n  tachyon_bn254_g2_xyzz c_ret2 = tachyon_bn254_g2_xyzz_neg(&g2_c_a_);\n  EXPECT_EQ(c::base::native_cast(c_ret2), -g2_a_);\n}\n\nTEST_F(PointXYZZTest, Dbl) {\n  tachyon_bn254_g2_xyzz c_ret = tachyon_bn254_g2_xyzz_dbl(&g2_c_a_);\n  EXPECT_EQ(c::base::native_cast(c_ret), g2_a_.Double());\n\n  tachyon_bn254_g2_xyzz c_ret2 = tachyon_bn254_g2_xyzz_dbl(&g2_c_a_);\n  EXPECT_EQ(c::base::native_cast(c_ret2), g2_a_.Double());\n}\n\n}  // namespace tachyon\n"
  },
  {
    "path": "tachyon/c/math/elliptic_curves/short_weierstrass/projective_point_unittest.cc",
    "content": "#include \"gtest/gtest.h\"\n\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1_point_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1_point_type_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g2_point_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g2_point_type_traits.h\"\n\nnamespace tachyon {\n\nnamespace {\n\nclass ProjectivePointTest : public testing::Test {\n public:\n  void SetUp() override {\n    tachyon_bn254_g1_init();\n    tachyon_bn254_g2_init();\n\n    g1_a_ = math::bn254::G1ProjectivePoint::Random();\n    g1_b_ = math::bn254::G1ProjectivePoint::Random();\n    g1_c_a_ = c::base::c_cast(g1_a_);\n    g1_c_b_ = c::base::c_cast(g1_b_);\n\n    g2_a_ = math::bn254::G2ProjectivePoint::Random();\n    g2_b_ = math::bn254::G2ProjectivePoint::Random();\n    g2_c_a_ = c::base::c_cast(g2_a_);\n    g2_c_b_ = c::base::c_cast(g2_b_);\n  }\n\n protected:\n  math::bn254::G1ProjectivePoint g1_a_;\n  math::bn254::G1ProjectivePoint g1_b_;\n  tachyon_bn254_g1_projective g1_c_a_;\n  tachyon_bn254_g1_projective g1_c_b_;\n\n  math::bn254::G2ProjectivePoint g2_a_;\n  math::bn254::G2ProjectivePoint g2_b_;\n  tachyon_bn254_g2_projective g2_c_a_;\n  tachyon_bn254_g2_projective g2_c_b_;\n};\n\n}  // namespace\n\nTEST_F(ProjectivePointTest, Zero) {\n  tachyon_bn254_g1_projective c_ret = tachyon_bn254_g1_projective_zero();\n  EXPECT_TRUE(c::base::native_cast(c_ret).IsZero());\n\n  tachyon_bn254_g2_projective c_ret2 = tachyon_bn254_g2_projective_zero();\n  EXPECT_TRUE(c::base::native_cast(c_ret2).IsZero());\n}\n\nTEST_F(ProjectivePointTest, Generator) {\n  tachyon_bn254_g1_projective c_ret = tachyon_bn254_g1_projective_generator();\n  EXPECT_EQ(c::base::native_cast(c_ret),\n            math::bn254::G1ProjectivePoint::Generator());\n\n  tachyon_bn254_g2_projective c_ret2 = tachyon_bn254_g2_projective_generator();\n  EXPECT_EQ(c::base::native_cast(c_ret2),\n            math::bn254::G2ProjectivePoint::Generator());\n}\n\nTEST_F(ProjectivePointTest, Random) {\n  tachyon_bn254_g1_projective c_ret = tachyon_bn254_g1_projective_random();\n  EXPECT_NE(c::base::native_cast(c_ret), g1_a_);\n\n  tachyon_bn254_g2_projective c_ret2 = tachyon_bn254_g2_projective_random();\n  EXPECT_NE(c::base::native_cast(c_ret2), g2_a_);\n}\n\nTEST_F(ProjectivePointTest, Eq) {\n  EXPECT_EQ(tachyon_bn254_g1_projective_eq(&g1_c_a_, &g1_c_b_), g1_a_ == g1_b_);\n  EXPECT_EQ(tachyon_bn254_g2_projective_eq(&g2_c_a_, &g2_c_b_), g2_a_ == g2_b_);\n}\n\nTEST_F(ProjectivePointTest, Ne) {\n  EXPECT_EQ(tachyon_bn254_g1_projective_ne(&g1_c_a_, &g1_c_b_), g1_a_ != g1_b_);\n  EXPECT_EQ(tachyon_bn254_g2_projective_ne(&g2_c_a_, &g2_c_b_), g2_a_ != g2_b_);\n}\n\nTEST_F(ProjectivePointTest, Add) {\n  tachyon_bn254_g1_projective c_ret =\n      tachyon_bn254_g1_projective_add(&g1_c_a_, &g1_c_b_);\n  EXPECT_EQ(c::base::native_cast(c_ret), g1_a_ + g1_b_);\n\n  math::bn254::G1AffinePoint d = math::bn254::G1AffinePoint::Random();\n  tachyon_bn254_g1_affine c_d = c::base::c_cast(d);\n  c_ret = tachyon_bn254_g1_projective_add_mixed(&g1_c_a_, &c_d);\n  EXPECT_EQ(c::base::native_cast(c_ret), g1_a_ + d);\n\n  tachyon_bn254_g2_projective c_ret2 =\n      tachyon_bn254_g2_projective_add(&g2_c_a_, &g2_c_b_);\n  EXPECT_EQ(c::base::native_cast(c_ret2), g2_a_ + g2_b_);\n\n  math::bn254::G2AffinePoint d2 = math::bn254::G2AffinePoint::Random();\n  tachyon_bn254_g2_affine c_d2 = c::base::c_cast(d2);\n  c_ret2 = tachyon_bn254_g2_projective_add_mixed(&g2_c_a_, &c_d2);\n  EXPECT_EQ(c::base::native_cast(c_ret2), g2_a_ + d2);\n}\n\nTEST_F(ProjectivePointTest, Sub) {\n  tachyon_bn254_g1_projective c_ret =\n      tachyon_bn254_g1_projective_sub(&g1_c_a_, &g1_c_b_);\n  EXPECT_EQ(c::base::native_cast(c_ret), g1_a_ - g1_b_);\n\n  math::bn254::G1AffinePoint d = math::bn254::G1AffinePoint::Random();\n  tachyon_bn254_g1_affine c_d = c::base::c_cast(d);\n  c_ret = tachyon_bn254_g1_projective_sub_mixed(&g1_c_a_, &c_d);\n  EXPECT_EQ(c::base::native_cast(c_ret), g1_a_ - d);\n\n  tachyon_bn254_g2_projective c_ret2 =\n      tachyon_bn254_g2_projective_sub(&g2_c_a_, &g2_c_b_);\n  EXPECT_EQ(c::base::native_cast(c_ret2), g2_a_ - g2_b_);\n\n  math::bn254::G2AffinePoint d2 = math::bn254::G2AffinePoint::Random();\n  tachyon_bn254_g2_affine c_d2 = c::base::c_cast(d2);\n  c_ret2 = tachyon_bn254_g2_projective_sub_mixed(&g2_c_a_, &c_d2);\n  EXPECT_EQ(c::base::native_cast(c_ret2), g2_a_ - d2);\n}\n\nTEST_F(ProjectivePointTest, Neg) {\n  tachyon_bn254_g1_projective c_ret = tachyon_bn254_g1_projective_neg(&g1_c_a_);\n  EXPECT_EQ(c::base::native_cast(c_ret), -g1_a_);\n\n  tachyon_bn254_g2_projective c_ret2 =\n      tachyon_bn254_g2_projective_neg(&g2_c_a_);\n  EXPECT_EQ(c::base::native_cast(c_ret2), -g2_a_);\n}\n\nTEST_F(ProjectivePointTest, Dbl) {\n  tachyon_bn254_g1_projective c_ret = tachyon_bn254_g1_projective_dbl(&g1_c_a_);\n  EXPECT_EQ(c::base::native_cast(c_ret), g1_a_.Double());\n\n  tachyon_bn254_g2_projective c_ret2 =\n      tachyon_bn254_g2_projective_dbl(&g2_c_a_);\n  EXPECT_EQ(c::base::native_cast(c_ret2), g2_a_.Double());\n}\n\n}  // namespace tachyon\n"
  },
  {
    "path": "tachyon/c/math/finite_fields/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n    name = \"finite_fields_hdrs\",\n    srcs = [\"//tachyon/c/math/finite_fields/baby_bear:baby_bear_hdrs\"],\n)\n\ntachyon_cc_library(\n    name = \"finite_fields\",\n    deps = [\n        \"//tachyon/c/math/finite_fields/baby_bear\",\n        \"//tachyon/c/math/finite_fields/baby_bear:baby_bear4\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"extension_field_unittests\",\n    srcs = [\"extension_field_unittest.cc\"],\n    deps = [\n        \"//tachyon/c/math/elliptic_curves/bn/bn254:fq12\",\n        \"//tachyon/c/math/elliptic_curves/bn/bn254:fq2\",\n        \"//tachyon/c/math/elliptic_curves/bn/bn254:fq6\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"finite_fields_unittests\",\n    srcs = [\"prime_field_unittest.cc\"],\n    deps = [\n        \"//tachyon/c/math/elliptic_curves/bn/bn254:fr\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/c/math/finite_fields/baby_bear/BUILD.bazel",
    "content": "load(\"//tachyon/c/math/finite_fields/generator/ext_field_generator:build_defs.bzl\", \"generate_ext_fields\")\nload(\"//tachyon/c/math/finite_fields/generator/prime_field_generator:build_defs.bzl\", \"generate_small_prime_fields\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n    name = \"baby_bear_hdrs\",\n    srcs = [\n        \"baby_bear.h\",\n        \"baby_bear4.h\",\n    ],\n)\n\ngenerate_small_prime_fields(\n    name = \"baby_bear\",\n    class_name = \"baby_bear\",\n    display_name = \"BabyBear\",\n    native_deps = [\"//tachyon/math/finite_fields/baby_bear\"],\n    native_hdr = \"tachyon/math/finite_fields/baby_bear/baby_bear.h\",\n    native_type = \"tachyon::math::BabyBear\",\n)\n\ngenerate_ext_fields(\n    name = \"baby_bear4\",\n    base_field_class_name = \"baby_bear\",\n    base_field_display_name = \"BabyBear\",\n    c_base_field_deps = [\":baby_bear\"],\n    c_base_field_hdr = \"tachyon/c/math/finite_fields/baby_bear/baby_bear.h\",\n    class_name = \"baby_bear4\",\n    degree_over_base_field = 4,\n    display_name = \"BabyBear4\",\n    native_deps = [\"//tachyon/math/finite_fields/baby_bear:baby_bear4\"],\n    native_hdr = \"tachyon/math/finite_fields/baby_bear/baby_bear4.h\",\n    native_type = \"tachyon::math::BabyBear4\",\n)\n"
  },
  {
    "path": "tachyon/c/math/finite_fields/extension_field_unittest.cc",
    "content": "#include \"gtest/gtest.h\"\n\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/fq12_type_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/fq2_type_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/fq6_type_traits.h\"\n\nnamespace tachyon {\nnamespace {\n\nclass ExtensionFieldTest : public testing::Test {\n public:\n  void SetUp() override {\n    native_fq2_a_ = math::bn254::Fq2::Random();\n    native_fq2_b_ = math::bn254::Fq2::Random();\n    native_fq6_a_ = math::bn254::Fq6::Random();\n    native_fq6_b_ = math::bn254::Fq6::Random();\n    native_fq12_a_ = math::bn254::Fq12::Random();\n    native_fq12_b_ = math::bn254::Fq12::Random();\n\n    c_fq2_a = c::base::c_cast(native_fq2_a_);\n    c_fq2_b_ = c::base::c_cast(native_fq2_b_);\n    c_fq6_a = c::base::c_cast(native_fq6_a_);\n    c_fq6_b_ = c::base::c_cast(native_fq6_b_);\n    c_fq12_a = c::base::c_cast(native_fq12_a_);\n    c_fq12_b2_ = c::base::c_cast(native_fq12_b_);\n  }\n\n protected:\n  math::bn254::Fq2 native_fq2_a_;\n  math::bn254::Fq2 native_fq2_b_;\n  math::bn254::Fq6 native_fq6_a_;\n  math::bn254::Fq6 native_fq6_b_;\n  math::bn254::Fq12 native_fq12_a_;\n  math::bn254::Fq12 native_fq12_b_;\n\n  tachyon_bn254_fq2 c_fq2_a;\n  tachyon_bn254_fq2 c_fq2_b_;\n  tachyon_bn254_fq6 c_fq6_a;\n  tachyon_bn254_fq6 c_fq6_b_;\n  tachyon_bn254_fq12 c_fq12_a;\n  tachyon_bn254_fq12 c_fq12_b2_;\n};\n\n}  // namespace\n\nTEST_F(ExtensionFieldTest, Zero) {\n  tachyon_bn254_fq2 c_ret2 = tachyon_bn254_fq2_zero();\n  EXPECT_TRUE(c::base::native_cast(c_ret2).IsZero());\n\n  tachyon_bn254_fq6 c_ret6 = tachyon_bn254_fq6_zero();\n  EXPECT_TRUE(c::base::native_cast(c_ret6).IsZero());\n\n  tachyon_bn254_fq12 c_ret12 = tachyon_bn254_fq12_zero();\n  EXPECT_TRUE(c::base::native_cast(c_ret12).IsZero());\n}\n\nTEST_F(ExtensionFieldTest, One) {\n  tachyon_bn254_fq2 c_ret2 = tachyon_bn254_fq2_one();\n  EXPECT_TRUE(c::base::native_cast(c_ret2).IsOne());\n\n  tachyon_bn254_fq6 c_ret6 = tachyon_bn254_fq6_one();\n  EXPECT_TRUE(c::base::native_cast(c_ret6).IsOne());\n\n  tachyon_bn254_fq12 c_ret12 = tachyon_bn254_fq12_one();\n  EXPECT_TRUE(c::base::native_cast(c_ret12).IsOne());\n}\n\nTEST_F(ExtensionFieldTest, MinusOne) {\n  tachyon_bn254_fq2 c_ret2 = tachyon_bn254_fq2_minus_one();\n  EXPECT_TRUE(c::base::native_cast(c_ret2).IsMinusOne());\n\n  tachyon_bn254_fq6 c_ret6 = tachyon_bn254_fq6_minus_one();\n  EXPECT_TRUE(c::base::native_cast(c_ret6).IsMinusOne());\n\n  tachyon_bn254_fq12 c_ret12 = tachyon_bn254_fq12_minus_one();\n  EXPECT_TRUE(c::base::native_cast(c_ret12).IsMinusOne());\n}\n\nTEST_F(ExtensionFieldTest, Random) {\n  tachyon_bn254_fq2 c_ret2 = tachyon_bn254_fq2_random();\n  EXPECT_NE(c::base::native_cast(c_ret2), native_fq2_a_);\n\n  tachyon_bn254_fq6 c_ret6 = tachyon_bn254_fq6_random();\n  EXPECT_NE(c::base::native_cast(c_ret6), native_fq6_a_);\n\n  tachyon_bn254_fq12 c_ret12 = tachyon_bn254_fq12_random();\n  EXPECT_NE(c::base::native_cast(c_ret12), native_fq12_a_);\n}\n\nTEST_F(ExtensionFieldTest, Dbl) {\n  tachyon_bn254_fq2 c_ret2 = tachyon_bn254_fq2_dbl(&c_fq2_a);\n  EXPECT_EQ(c::base::native_cast(c_ret2), native_fq2_a_.Double());\n\n  tachyon_bn254_fq6 c_ret6 = tachyon_bn254_fq6_dbl(&c_fq6_a);\n  EXPECT_EQ(c::base::native_cast(c_ret6), native_fq6_a_.Double());\n\n  tachyon_bn254_fq12 c_ret12 = tachyon_bn254_fq12_dbl(&c_fq12_a);\n  EXPECT_EQ(c::base::native_cast(c_ret12), native_fq12_a_.Double());\n}\n\nTEST_F(ExtensionFieldTest, Neg) {\n  tachyon_bn254_fq2 c_ret2 = tachyon_bn254_fq2_neg(&c_fq2_a);\n  EXPECT_EQ(c::base::native_cast(c_ret2), -native_fq2_a_);\n\n  tachyon_bn254_fq6 c_ret6 = tachyon_bn254_fq6_neg(&c_fq6_a);\n  EXPECT_EQ(c::base::native_cast(c_ret6), -native_fq6_a_);\n\n  tachyon_bn254_fq12 c_ret12 = tachyon_bn254_fq12_neg(&c_fq12_a);\n  EXPECT_EQ(c::base::native_cast(c_ret12), -native_fq12_a_);\n}\n\nTEST_F(ExtensionFieldTest, Sqr) {\n  tachyon_bn254_fq2 c_ret2 = tachyon_bn254_fq2_sqr(&c_fq2_a);\n  EXPECT_EQ(c::base::native_cast(c_ret2), native_fq2_a_.Square());\n\n  tachyon_bn254_fq6 c_ret6 = tachyon_bn254_fq6_sqr(&c_fq6_a);\n  EXPECT_EQ(c::base::native_cast(c_ret6), native_fq6_a_.Square());\n\n  tachyon_bn254_fq12 c_ret12 = tachyon_bn254_fq12_sqr(&c_fq12_a);\n  EXPECT_EQ(c::base::native_cast(c_ret12), native_fq12_a_.Square());\n}\n\nTEST_F(ExtensionFieldTest, Inv) {\n  tachyon_bn254_fq2 c_ret2 = tachyon_bn254_fq2_inv(&c_fq2_a);\n  EXPECT_EQ(c::base::native_cast(c_ret2), native_fq2_a_.Inverse());\n\n  tachyon_bn254_fq6 c_ret6 = tachyon_bn254_fq6_inv(&c_fq6_a);\n  EXPECT_EQ(c::base::native_cast(c_ret6), native_fq6_a_.Inverse());\n\n  tachyon_bn254_fq12 c_ret12 = tachyon_bn254_fq12_inv(&c_fq12_a);\n  EXPECT_EQ(c::base::native_cast(c_ret12), native_fq12_a_.Inverse());\n}\n\nTEST_F(ExtensionFieldTest, Add) {\n  tachyon_bn254_fq2 c_ret2 = tachyon_bn254_fq2_add(&c_fq2_a, &c_fq2_b_);\n  EXPECT_EQ(c::base::native_cast(c_ret2), native_fq2_a_ + native_fq2_b_);\n\n  tachyon_bn254_fq6 c_ret6 = tachyon_bn254_fq6_add(&c_fq6_a, &c_fq6_b_);\n  EXPECT_EQ(c::base::native_cast(c_ret6), native_fq6_a_ + native_fq6_b_);\n\n  tachyon_bn254_fq12 c_ret12 = tachyon_bn254_fq12_add(&c_fq12_a, &c_fq12_b2_);\n  EXPECT_EQ(c::base::native_cast(c_ret12), native_fq12_a_ + native_fq12_b_);\n}\n\nTEST_F(ExtensionFieldTest, Sub) {\n  tachyon_bn254_fq2 c_ret2 = tachyon_bn254_fq2_sub(&c_fq2_a, &c_fq2_b_);\n  EXPECT_EQ(c::base::native_cast(c_ret2), native_fq2_a_ - native_fq2_b_);\n\n  tachyon_bn254_fq6 c_ret6 = tachyon_bn254_fq6_sub(&c_fq6_a, &c_fq6_b_);\n  EXPECT_EQ(c::base::native_cast(c_ret6), native_fq6_a_ - native_fq6_b_);\n\n  tachyon_bn254_fq12 c_ret12 = tachyon_bn254_fq12_sub(&c_fq12_a, &c_fq12_b2_);\n  EXPECT_EQ(c::base::native_cast(c_ret12), native_fq12_a_ - native_fq12_b_);\n}\n\nTEST_F(ExtensionFieldTest, Mul) {\n  tachyon_bn254_fq2 c_ret2 = tachyon_bn254_fq2_mul(&c_fq2_a, &c_fq2_b_);\n  EXPECT_EQ(c::base::native_cast(c_ret2), native_fq2_a_ * native_fq2_b_);\n\n  tachyon_bn254_fq6 c_ret6 = tachyon_bn254_fq6_mul(&c_fq6_a, &c_fq6_b_);\n  EXPECT_EQ(c::base::native_cast(c_ret6), native_fq6_a_ * native_fq6_b_);\n\n  tachyon_bn254_fq12 c_ret12 = tachyon_bn254_fq12_mul(&c_fq12_a, &c_fq12_b2_);\n  EXPECT_EQ(c::base::native_cast(c_ret12), native_fq12_a_ * native_fq12_b_);\n}\n\nTEST_F(ExtensionFieldTest, Div) {\n  tachyon_bn254_fq2 c_ret2 = tachyon_bn254_fq2_div(&c_fq2_a, &c_fq2_b_);\n  EXPECT_EQ(c::base::native_cast(c_ret2), native_fq2_a_ / native_fq2_b_);\n\n  tachyon_bn254_fq6 c_ret6 = tachyon_bn254_fq6_div(&c_fq6_a, &c_fq6_b_);\n  EXPECT_EQ(c::base::native_cast(c_ret6), native_fq6_a_ / native_fq6_b_);\n\n  tachyon_bn254_fq12 c_ret12 = tachyon_bn254_fq12_div(&c_fq12_a, &c_fq12_b2_);\n  EXPECT_EQ(c::base::native_cast(c_ret12), native_fq12_a_ / native_fq12_b_);\n}\n\nTEST_F(ExtensionFieldTest, Eq) {\n  EXPECT_EQ(tachyon_bn254_fq2_eq(&c_fq2_a, &c_fq2_b_),\n            native_fq2_a_ == native_fq2_b_);\n  EXPECT_EQ(tachyon_bn254_fq6_eq(&c_fq6_a, &c_fq6_b_),\n            native_fq6_a_ == native_fq6_b_);\n  EXPECT_EQ(tachyon_bn254_fq12_eq(&c_fq12_a, &c_fq12_b2_),\n            native_fq12_a_ == native_fq12_b_);\n}\n\nTEST_F(ExtensionFieldTest, Ne) {\n  EXPECT_EQ(tachyon_bn254_fq2_ne(&c_fq2_a, &c_fq2_b_),\n            native_fq2_a_ != native_fq2_b_);\n  EXPECT_EQ(tachyon_bn254_fq6_ne(&c_fq6_a, &c_fq6_b_),\n            native_fq6_a_ != native_fq6_b_);\n  EXPECT_EQ(tachyon_bn254_fq12_ne(&c_fq12_a, &c_fq12_b2_),\n            native_fq12_a_ != native_fq12_b_);\n}\n\nTEST_F(ExtensionFieldTest, Gt) {\n  EXPECT_EQ(tachyon_bn254_fq2_gt(&c_fq2_a, &c_fq2_b_),\n            native_fq2_a_ > native_fq2_b_);\n  EXPECT_EQ(tachyon_bn254_fq6_gt(&c_fq6_a, &c_fq6_b_),\n            native_fq6_a_ > native_fq6_b_);\n  EXPECT_EQ(tachyon_bn254_fq12_gt(&c_fq12_a, &c_fq12_b2_),\n            native_fq12_a_ > native_fq12_b_);\n}\n\nTEST_F(ExtensionFieldTest, Ge) {\n  EXPECT_EQ(tachyon_bn254_fq2_ge(&c_fq2_a, &c_fq2_b_),\n            native_fq2_a_ >= native_fq2_b_);\n  EXPECT_EQ(tachyon_bn254_fq6_ge(&c_fq6_a, &c_fq6_b_),\n            native_fq6_a_ >= native_fq6_b_);\n  EXPECT_EQ(tachyon_bn254_fq12_ge(&c_fq12_a, &c_fq12_b2_),\n            native_fq12_a_ >= native_fq12_b_);\n}\n\nTEST_F(ExtensionFieldTest, Lt) {\n  EXPECT_EQ(tachyon_bn254_fq2_lt(&c_fq2_a, &c_fq2_b_),\n            native_fq2_a_ < native_fq2_b_);\n  EXPECT_EQ(tachyon_bn254_fq6_lt(&c_fq6_a, &c_fq6_b_),\n            native_fq6_a_ < native_fq6_b_);\n  EXPECT_EQ(tachyon_bn254_fq12_lt(&c_fq12_a, &c_fq12_b2_),\n            native_fq12_a_ < native_fq12_b_);\n}\n\nTEST_F(ExtensionFieldTest, Le) {\n  EXPECT_EQ(tachyon_bn254_fq2_le(&c_fq2_a, &c_fq2_b_),\n            native_fq2_a_ <= native_fq2_b_);\n  EXPECT_EQ(tachyon_bn254_fq6_le(&c_fq6_a, &c_fq6_b_),\n            native_fq6_a_ <= native_fq6_b_);\n  EXPECT_EQ(tachyon_bn254_fq12_le(&c_fq12_a, &c_fq12_b2_),\n            native_fq12_a_ <= native_fq12_b_);\n}\n\n}  // namespace tachyon\n"
  },
  {
    "path": "tachyon/c/math/finite_fields/generator/ext_field_generator/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_binary\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_binary(\n    name = \"ext_field_generator\",\n    srcs = [\"ext_field_generator.cc\"],\n    data = [\n        \"ext_field.cc.tpl\",\n        \"ext_field.h.tpl\",\n        \"ext_field_type_traits.h.tpl\",\n    ],\n    deps = [\n        \"//tachyon/base/console\",\n        \"//tachyon/base/files:file_path_flag\",\n        \"//tachyon/base/files:file_util\",\n        \"//tachyon/base/flag:flag_parser\",\n        \"//tachyon/base/strings:string_number_conversions\",\n        \"//tachyon/build:cc_writer\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/c/math/finite_fields/generator/ext_field_generator/build_defs.bzl",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\ndef _generate_ext_field_impl(ctx):\n    hdr_tpl_path = ctx.expand_location(\"$(location @kroma_network_tachyon//tachyon/c/math/finite_fields/generator/ext_field_generator:ext_field.h.tpl)\", [ctx.attr.hdr_tpl_path])\n    src_tpl_path = ctx.expand_location(\"$(location @kroma_network_tachyon//tachyon/c/math/finite_fields/generator/ext_field_generator:ext_field.cc.tpl)\", [ctx.attr.src_tpl_path])\n    type_traits_hdr_tpl_path = ctx.expand_location(\"$(location @kroma_network_tachyon//tachyon/c/math/finite_fields/generator/ext_field_generator:ext_field_type_traits.h.tpl)\", [ctx.attr.type_traits_hdr_tpl_path])\n\n    arguments = [\n        \"--out=%s\" % (ctx.outputs.out.path),\n        \"--class_name=%s\" % (ctx.attr.class_name),\n        \"--base_field_class_name=%s\" % (ctx.attr.base_field_class_name),\n        \"--display_name=%s\" % (ctx.attr.display_name),\n        \"--base_field_display_name=%s\" % (ctx.attr.base_field_display_name),\n        \"--c_base_field_hdr=%s\" % (ctx.attr.c_base_field_hdr),\n        \"--native_type=%s\" % (ctx.attr.native_type),\n        \"--native_hdr=%s\" % (ctx.attr.native_hdr),\n        \"--degree_over_base_field=%s\" % (ctx.attr.degree_over_base_field),\n        \"--hdr_tpl_path=%s\" % (hdr_tpl_path),\n        \"--src_tpl_path=%s\" % (src_tpl_path),\n        \"--type_traits_hdr_tpl_path=%s\" % (type_traits_hdr_tpl_path),\n    ]\n\n    ctx.actions.run(\n        inputs = [\n            ctx.files.hdr_tpl_path[0],\n            ctx.files.src_tpl_path[0],\n            ctx.files.type_traits_hdr_tpl_path[0],\n        ],\n        tools = [ctx.executable._tool],\n        executable = ctx.executable._tool,\n        outputs = [ctx.outputs.out],\n        arguments = arguments,\n    )\n\n    return [DefaultInfo(files = depset([ctx.outputs.out]))]\n\ngenerate_ext_field = rule(\n    implementation = _generate_ext_field_impl,\n    attrs = {\n        \"out\": attr.output(mandatory = True),\n        \"class_name\": attr.string(mandatory = True),\n        \"base_field_class_name\": attr.string(mandatory = True),\n        \"display_name\": attr.string(mandatory = True),\n        \"base_field_display_name\": attr.string(mandatory = True),\n        \"c_base_field_hdr\": attr.string(mandatory = True),\n        \"native_type\": attr.string(mandatory = True),\n        \"native_hdr\": attr.string(mandatory = True),\n        \"degree_over_base_field\": attr.int(mandatory = True),\n        \"hdr_tpl_path\": attr.label(\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/c/math/finite_fields/generator/ext_field_generator:ext_field.h.tpl\"),\n        ),\n        \"src_tpl_path\": attr.label(\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/c/math/finite_fields/generator/ext_field_generator:ext_field.cc.tpl\"),\n        ),\n        \"type_traits_hdr_tpl_path\": attr.label(\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/c/math/finite_fields/generator/ext_field_generator:ext_field_type_traits.h.tpl\"),\n        ),\n        \"_tool\": attr.label(\n            # TODO(chokobole): Change to \"exec\", so we can build on macos.\n            cfg = \"target\",\n            executable = True,\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/c/math/finite_fields/generator/ext_field_generator\"),\n        ),\n    },\n)\n\ndef generate_ext_fields(\n        name,\n        degree_over_base_field,\n        class_name,\n        base_field_class_name,\n        display_name,\n        base_field_display_name,\n        c_base_field_hdr,\n        c_base_field_deps,\n        native_type,\n        native_hdr,\n        native_deps):\n    for n in [\n        (\"{}_gen_type_traits_hdr\".format(name), \"{}_type_traits.h\".format(name)),\n        (\"{}_gen_src\".format(name), \"{}.cc\".format(name)),\n        (\"{}_gen_hdr\".format(name), \"{}.h\".format(name)),\n    ]:\n        generate_ext_field(\n            degree_over_base_field = degree_over_base_field,\n            class_name = class_name,\n            base_field_class_name = base_field_class_name,\n            display_name = display_name,\n            base_field_display_name = base_field_display_name,\n            c_base_field_hdr = c_base_field_hdr,\n            native_type = native_type,\n            native_hdr = native_hdr,\n            name = n[0],\n            out = n[1],\n        )\n\n    tachyon_cc_library(\n        name = name,\n        hdrs = [\n            \":{}_gen_type_traits_hdr\".format(name),\n            \":{}_gen_hdr\".format(name),\n        ],\n        srcs = [\":{}_gen_src\".format(name)],\n        deps = c_base_field_deps + native_deps + [\n            \"//tachyon/c:export\",\n            \"//tachyon/c/base:type_traits_forward\",\n        ],\n    )\n"
  },
  {
    "path": "tachyon/c/math/finite_fields/generator/ext_field_generator/ext_field.cc.tpl",
    "content": "// clang-format off\n#include \"tachyon/c/base/type_traits_forward.h\"\n#include \"%{c_type_traits_hdr}\"\n\ntachyon_%{class_name} tachyon_%{class_name}_zero() {\n  using namespace tachyon::c::base;\n  using NativeType =\n      typename TypeTraits<tachyon_%{class_name}>::NativeType;\n  return c_cast(NativeType::Zero());\n}\n\ntachyon_%{class_name} tachyon_%{class_name}_one() {\n  using namespace tachyon::c::base;\n  using NativeType =\n      typename TypeTraits<tachyon_%{class_name}>::NativeType;\n  return c_cast(NativeType::One());\n}\n\ntachyon_%{class_name} tachyon_%{class_name}_minus_one() {\n  using namespace tachyon::c::base;\n  using NativeType =\n      typename TypeTraits<tachyon_%{class_name}>::NativeType;\n  return c_cast(NativeType::MinusOne());\n}\n\ntachyon_%{class_name} tachyon_%{class_name}_random() {\n  using namespace tachyon::c::base;\n  using NativeType =\n      typename TypeTraits<tachyon_%{class_name}>::NativeType;\n  return c_cast(NativeType::Random());\n}\n\ntachyon_%{class_name} tachyon_%{class_name}_dbl(const tachyon_%{class_name}* a) {\n  using namespace tachyon::c::base;\n  using NativeType =\n      typename TypeTraits<tachyon_%{class_name}>::NativeType;\n  NativeType native_a = native_cast(*a);\n  return c_cast(native_a.DoubleInPlace());\n}\n\ntachyon_%{class_name} tachyon_%{class_name}_neg(const tachyon_%{class_name}* a) {\n  using namespace tachyon::c::base;\n  using NativeType =\n      typename TypeTraits<tachyon_%{class_name}>::NativeType;\n  NativeType native_a = native_cast(*a);\n  return c_cast(native_a.NegateInPlace());\n}\n\ntachyon_%{class_name} tachyon_%{class_name}_sqr(const tachyon_%{class_name}* a) {\n  using namespace tachyon::c::base;\n  using NativeType =\n      typename TypeTraits<tachyon_%{class_name}>::NativeType;\n  NativeType native_a = native_cast(*a);\n  return c_cast(native_a.SquareInPlace());\n}\n\ntachyon_%{class_name} tachyon_%{class_name}_inv(const tachyon_%{class_name}* a) {\n  using namespace tachyon::c::base;\n  using NativeType =\n      typename TypeTraits<tachyon_%{class_name}>::NativeType;\n  NativeType native_a = native_cast(*a);\n  CHECK(native_a.InverseInPlace());\n  return c_cast(native_a);\n}\n\ntachyon_%{class_name} tachyon_%{class_name}_add(const tachyon_%{class_name}* a, const tachyon_%{class_name}* b) {\n  using namespace tachyon::c::base;\n  using NativeType =\n      typename TypeTraits<tachyon_%{class_name}>::NativeType;\n  NativeType native_a = native_cast(*a);\n  return c_cast(native_a += native_cast(*b));\n}\n\ntachyon_%{class_name} tachyon_%{class_name}_sub(const tachyon_%{class_name}* a, const tachyon_%{class_name}* b) {\n  using namespace tachyon::c::base;\n  using NativeType =\n      typename TypeTraits<tachyon_%{class_name}>::NativeType;\n  NativeType native_a = native_cast(*a);\n  return c_cast(native_a -= native_cast(*b));\n}\n\ntachyon_%{class_name} tachyon_%{class_name}_mul(const tachyon_%{class_name}* a, const tachyon_%{class_name}* b) {\n  using namespace tachyon::c::base;\n  using NativeType =\n      typename TypeTraits<tachyon_%{class_name}>::NativeType;\n  NativeType native_a = native_cast(*a);\n  return c_cast(native_a *= native_cast(*b));\n}\n\ntachyon_%{class_name} tachyon_%{class_name}_div(const tachyon_%{class_name}* a, const tachyon_%{class_name}* b) {\n  using namespace tachyon::c::base;\n  using NativeType =\n      typename TypeTraits<tachyon_%{class_name}>::NativeType;\n  NativeType native_a = native_cast(*a);\n  CHECK(native_a /= native_cast(*b));\n  return c_cast(native_a);\n}\n\nbool tachyon_%{class_name}_eq(const tachyon_%{class_name}* a, const tachyon_%{class_name}* b) {\n  using namespace tachyon::c::base;\n  return native_cast(*a) == native_cast(*b);\n}\n\nbool tachyon_%{class_name}_ne(const tachyon_%{class_name}* a, const tachyon_%{class_name}* b) {\n  using namespace tachyon::c::base;\n  return native_cast(*a) != native_cast(*b);\n}\n\nbool tachyon_%{class_name}_gt(const tachyon_%{class_name}* a, const tachyon_%{class_name}* b) {\n  using namespace tachyon::c::base;\n  return native_cast(*a) > native_cast(*b);\n}\n\nbool tachyon_%{class_name}_ge(const tachyon_%{class_name}* a, const tachyon_%{class_name}* b) {\n  using namespace tachyon::c::base;\n  return native_cast(*a) >= native_cast(*b);\n}\n\nbool tachyon_%{class_name}_lt(const tachyon_%{class_name}* a, const tachyon_%{class_name}* b) {\n  using namespace tachyon::c::base;\n  return native_cast(*a) < native_cast(*b);\n}\n\nbool tachyon_%{class_name}_le(const tachyon_%{class_name}* a, const tachyon_%{class_name}* b) {\n  using namespace tachyon::c::base;\n  return native_cast(*a) <= native_cast(*b);\n}\n// clang-format on\n"
  },
  {
    "path": "tachyon/c/math/finite_fields/generator/ext_field_generator/ext_field.h.tpl",
    "content": "// clang-format off\n/**\n * @file %{class_name}.h\n * @brief Defines operations over the finite field extension %{display_name}.\n *\n * This header file specifies the structure and operations for elements of the extended finite field %{display_name},\n * which is built over the base field %{base_field_display_name} as part of the %{curve} elliptic curve cryptographic operations.\n * It includes basic arithmetic operations such as addition, subtraction, multiplication, and inversion,\n * as well as comparison operations.\n * @example extension_field.cc\n */\n\n#include <stdint.h>\n\n#include \"tachyon/c/export.h\"\n#include \"%{c_base_field_hdr}\"\n\n/**\n * @struct tachyon_%{class_name}\n * @brief Represents an element in the finite field extension %{display_name}.\n *\n * This structure models an element in %{display_name}, which is constructed over the base field %{base_field_display_name}.\n * It is used for cryptographic operations that require arithmetic in an extended finite field.\n */\nstruct tachyon_%{class_name} {\n  tachyon_%{base_field_class_name} c0;\n  tachyon_%{base_field_class_name} c1;\n%{if IsCubicExtension}\n  tachyon_%{base_field_class_name} c2;\n%{endif IsCubicExtension}\n%{if IsQuarticExtension}\n  tachyon_%{base_field_class_name} c2;\n  tachyon_%{base_field_class_name} c3;\n%{endif IsQuarticExtension}\n};\n\n%{extern_c_front}\n\n/**\n * @brief Returns an %{display_name} element with the value zero.\n *\n * @return An %{display_name} element representing zero.\n */\nTACHYON_C_EXPORT tachyon_%{class_name} tachyon_%{class_name}_zero();\n\n/**\n * @brief Returns an %{display_name} element with the value one.\n *\n * @return An %{display_name} element representing one.\n */\nTACHYON_C_EXPORT tachyon_%{class_name} tachyon_%{class_name}_one();\n\n/**\n * @brief Returns an %{display_name} element with the value minus one.\n *\n * @return An %{display_name} element representing minus one.\n */\nTACHYON_C_EXPORT tachyon_%{class_name} tachyon_%{class_name}_minus_one();\n\n/**\n * @brief Generates a random %{display_name} element.\n *\n * @return A randomly generated %{display_name} element.\n */\nTACHYON_C_EXPORT tachyon_%{class_name} tachyon_%{class_name}_random();\n\n/**\n * @brief Doubles an %{display_name} element.\n *\n * @param a Pointer to the %{display_name} element to double.\n * @return The doubled %{display_name} element.\n */\nTACHYON_C_EXPORT tachyon_%{class_name} tachyon_%{class_name}_dbl(const tachyon_%{class_name}* a);\n\n/**\n * @brief Negates an %{display_name} element.\n *\n * @param a Pointer to the %{display_name} element to negate.\n * @return The negated %{display_name} element.\n */\nTACHYON_C_EXPORT tachyon_%{class_name} tachyon_%{class_name}_neg(const tachyon_%{class_name}* a);\n\n/**\n * @brief Squares an %{display_name} element.\n *\n * @param a Pointer to the %{display_name} element to square.\n * @return The squared %{display_name} element.\n */\nTACHYON_C_EXPORT tachyon_%{class_name} tachyon_%{class_name}_sqr(const tachyon_%{class_name}* a);\n\n/**\n * @brief Inverts an %{display_name} element.\n *\n * @param a Pointer to the %{display_name} element to invert.\n * @return The inverted %{display_name} element. If the input is zero, the program will be killed.\n */\nTACHYON_C_EXPORT tachyon_%{class_name} tachyon_%{class_name}_inv(const tachyon_%{class_name}* a);\n\n/**\n * @brief Adds two %{display_name} elements.\n *\n * @param a Pointer to the first %{display_name} element.\n * @param b Pointer to the second %{display_name} element.\n * @return The sum a + b.\n */\nTACHYON_C_EXPORT tachyon_%{class_name} tachyon_%{class_name}_add(const tachyon_%{class_name}* a, const tachyon_%{class_name}* b);\n\n/**\n * @brief Subtracts one %{display_name} element from another.\n *\n * @param a Pointer to the %{display_name} element from which to subtract.\n * @param b Pointer to the %{display_name} element to subtract.\n * @return The difference a - b.\n */\nTACHYON_C_EXPORT tachyon_%{class_name} tachyon_%{class_name}_sub(const tachyon_%{class_name}* a, const tachyon_%{class_name}* b);\n\n/**\n * @brief Multiplies two %{display_name} elements.\n *\n * @param a Pointer to the first %{display_name} element.\n * @param b Pointer to the second %{display_name} element.\n * @return The product a * b.\n */\nTACHYON_C_EXPORT tachyon_%{class_name} tachyon_%{class_name}_mul(const tachyon_%{class_name}* a, const tachyon_%{class_name}* b);\n\n/**\n * @brief Divides one %{display_name} element by another.\n *\n * @param a Pointer to the %{display_name} element to divide.\n * @param b Pointer to the %{display_name} element to divide by.\n * @return The quotient a / b. If b is zero, the program will be killed.\n */\nTACHYON_C_EXPORT tachyon_%{class_name} tachyon_%{class_name}_div(const tachyon_%{class_name}* a, const tachyon_%{class_name}* b);\n\n/**\n * @brief Checks if two %{display_name} elements are equal.\n *\n * @param a Pointer to the first %{display_name} element.\n * @param b Pointer to the second %{display_name} element.\n * @return True if a and b are equal, false otherwise.\n */\nTACHYON_C_EXPORT bool tachyon_%{class_name}_eq(const tachyon_%{class_name}* a, const tachyon_%{class_name}* b);\n\n/**\n * @brief Checks if two %{display_name} elements are not equal.\n *\n * @param a Pointer to the first %{display_name} element.\n * @param b Pointer to the second %{display_name} element.\n * @return True if a and b are not equal, false otherwise.\n */\nTACHYON_C_EXPORT bool tachyon_%{class_name}_ne(const tachyon_%{class_name}* a, const tachyon_%{class_name}* b);\n\n/**\n * @brief Checks if one %{display_name} element is greater than another.\n *\n * @param a Pointer to the first %{display_name} element.\n * @param b Pointer to the second %{display_name} element.\n * @return True if a is greater than b, false otherwise.\n */\nTACHYON_C_EXPORT bool tachyon_%{class_name}_gt(const tachyon_%{class_name}* a, const tachyon_%{class_name}* b);\n\n/**\n * @brief Checks if one %{display_name} element is greater than or equal to another.\n *\n * @param a Pointer to the first %{display_name} element.\n * @param b Pointer to the second %{display_name} element.\n * @return True if a is greater than or equal to b, false otherwise.\n */\nTACHYON_C_EXPORT bool tachyon_%{class_name}_ge(const tachyon_%{class_name}* a, const tachyon_%{class_name}* b);\n\n/**\n * @brief Checks if one %{display_name} element is less than another.\n *\n * @param a Pointer to the first %{display_name} element.\n * @param b Pointer to the second %{display_name} element.\n * @return True if a is less than b, false otherwise.\n */\nTACHYON_C_EXPORT bool tachyon_%{class_name}_lt(const tachyon_%{class_name}* a, const tachyon_%{class_name}* b);\n\n/**\n * @brief Checks if one %{display_name} element is less than or equal to another.\n *\n * @param a Pointer to the first %{display_name} element.\n * @param b Pointer to the second %{display_name} element.\n * @return True if a is less than or equal to b, false otherwise.\n */\nTACHYON_C_EXPORT bool tachyon_%{class_name}_le(const tachyon_%{class_name}* a, const tachyon_%{class_name}* b);\n// clang-format on\n"
  },
  {
    "path": "tachyon/c/math/finite_fields/generator/ext_field_generator/ext_field_generator.cc",
    "content": "#include \"absl/strings/str_replace.h\"\n\n#include \"tachyon/base/console/iostream.h\"\n#include \"tachyon/base/files/file_path_flag.h\"\n#include \"tachyon/base/files/file_util.h\"\n#include \"tachyon/base/flag/flag_parser.h\"\n#include \"tachyon/base/strings/string_number_conversions.h\"\n#include \"tachyon/build/cc_writer.h\"\n\nnamespace tachyon {\n\nstruct GenerationConfig : public build::CcWriter {\n  base::FilePath hdr_tpl_path;\n  base::FilePath src_tpl_path;\n  base::FilePath type_traits_hdr_tpl_path;\n\n  std::string class_name;\n  std::string base_field_class_name;\n  std::string display_name;\n  std::string base_field_display_name;\n  std::string c_base_field_hdr;\n  std::string native_type;\n  std::string native_hdr;\n  int degree_over_base_field;\n\n  int GenerateHdr() const;\n  int GenerateSrc() const;\n  int GenerateTypeTraitsHdr() const;\n};\n\nint GenerationConfig::GenerateHdr() const {\n  std::string tpl_content;\n  CHECK(base::ReadFileToString(hdr_tpl_path, &tpl_content));\n\n  std::vector<std::string> tpl_lines = absl::StrSplit(tpl_content, '\\n');\n  RemoveOptionalLines(tpl_lines, \"IsCubicExtension\",\n                      degree_over_base_field == 3);\n  RemoveOptionalLines(tpl_lines, \"IsQuarticExtension\",\n                      degree_over_base_field == 4);\n  tpl_content = absl::StrJoin(tpl_lines, \"\\n\");\n\n  std::string content = absl::StrReplaceAll(\n      tpl_content, {\n                       {\"%{class_name}\", class_name},\n                       {\"%{base_field_class_name}\", base_field_class_name},\n                       {\"%{display_name}\", display_name},\n                       {\"%{base_field_display_name}\", base_field_display_name},\n                       {\"%{c_base_field_hdr}\", c_base_field_hdr},\n                   });\n  return WriteHdr(content, true);\n}\n\nint GenerationConfig::GenerateSrc() const {\n  std::string tpl_content;\n  CHECK(base::ReadFileToString(src_tpl_path, &tpl_content));\n\n  const base::FilePath& hdr_path = GetHdrPath();\n  std::string basename = GetHdrPath().BaseName().value();\n  basename = basename.substr(0, basename.find(\".h\"));\n  base::FilePath c_type_traits_hdr =\n      GetHdrPath().DirName().Append(basename + \"_type_traits.h\");\n  std::string content = absl::StrReplaceAll(\n      tpl_content, {\n                       {\"%{class_name}\", class_name},\n                       {\"%{c_type_traits_hdr}\", c_type_traits_hdr.value()},\n                   });\n  return WriteSrc(content);\n}\n\nint GenerationConfig::GenerateTypeTraitsHdr() const {\n  std::string tpl_content;\n  CHECK(base::ReadFileToString(type_traits_hdr_tpl_path, &tpl_content));\n\n  const base::FilePath& hdr_path = GetHdrPath();\n  std::string basename = GetHdrPath().BaseName().value();\n  basename = basename.substr(0, basename.find(\"_type_traits\"));\n  base::FilePath c_hdr = GetHdrPath().DirName().Append(basename + \".h\");\n  std::string content =\n      absl::StrReplaceAll(tpl_content, {\n                                           {\"%{class_name}\", class_name},\n                                           {\"%{c_hdr}\", c_hdr.value()},\n                                           {\"%{native_hdr}\", native_hdr},\n                                           {\"%{native_type}\", native_type},\n                                       });\n  return WriteHdr(content, false);\n}\n\nint RealMain(int argc, char** argv) {\n  GenerationConfig config;\n  config.generator = \"//tachyon/c/math/finite_fields/ext_field_generator\";\n\n  base::FlagParser parser;\n  parser.AddFlag<base::FilePathFlag>(&config.out)\n      .set_long_name(\"--out\")\n      .set_help(\"path to output\");\n  parser.AddFlag<base::StringFlag>(&config.class_name)\n      .set_long_name(\"--class_name\")\n      .set_required();\n  parser.AddFlag<base::StringFlag>(&config.base_field_class_name)\n      .set_long_name(\"--base_field_class_name\")\n      .set_required();\n  parser.AddFlag<base::StringFlag>(&config.display_name)\n      .set_long_name(\"--display_name\")\n      .set_required();\n  parser.AddFlag<base::StringFlag>(&config.base_field_display_name)\n      .set_long_name(\"--base_field_display_name\")\n      .set_required();\n  parser.AddFlag<base::StringFlag>(&config.c_base_field_hdr)\n      .set_long_name(\"--c_base_field_hdr\")\n      .set_required();\n  parser.AddFlag<base::StringFlag>(&config.native_type)\n      .set_long_name(\"--native_type\")\n      .set_required();\n  parser.AddFlag<base::StringFlag>(&config.native_hdr)\n      .set_long_name(\"--native_hdr\")\n      .set_required();\n  parser.AddFlag<base::IntFlag>(&config.degree_over_base_field)\n      .set_long_name(\"--degree_over_base_field\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.hdr_tpl_path)\n      .set_long_name(\"--hdr_tpl_path\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.src_tpl_path)\n      .set_long_name(\"--src_tpl_path\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.type_traits_hdr_tpl_path)\n      .set_long_name(\"--type_traits_hdr_tpl_path\")\n      .set_required();\n\n  std::string error;\n  if (!parser.Parse(argc, argv, &error)) {\n    tachyon_cerr << error << std::endl;\n    return 1;\n  }\n\n  if (base::EndsWith(config.out.value(), \"_type_traits.h\")) {\n    return config.GenerateTypeTraitsHdr();\n  } else if (base::EndsWith(config.out.value(), \".h\")) {\n    return config.GenerateHdr();\n  } else if (base::EndsWith(config.out.value(), \".cc\")) {\n    return config.GenerateSrc();\n  } else {\n    tachyon_cerr << \"suffix not supported:\" << config.out << std::endl;\n    return 1;\n  }\n}\n\n}  // namespace tachyon\n\nint main(int argc, char** argv) { return tachyon::RealMain(argc, argv); }\n"
  },
  {
    "path": "tachyon/c/math/finite_fields/generator/ext_field_generator/ext_field_type_traits.h.tpl",
    "content": "// clang-format off\n#include \"tachyon/c/base/type_traits_forward.h\"\n#include \"%{c_hdr}\"\n#include \"%{native_hdr}\"\n\nnamespace tachyon::c::base {\n\ntemplate <>\nstruct TypeTraits<%{native_type}> {\n  using CType = tachyon_%{class_name};\n};\n\ntemplate <>\nstruct TypeTraits<tachyon_%{class_name}> {\n  using NativeType = %{native_type};\n};\n\n}  // namespace tachyon::c::base\n// clang-format on\n"
  },
  {
    "path": "tachyon/c/math/finite_fields/generator/prime_field_generator/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_binary\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_binary(\n    name = \"prime_field_generator\",\n    srcs = [\"prime_field_generator.cc\"],\n    data = [\n        \"prime_field.cc.tpl\",\n        \"prime_field.h.tpl\",\n        \"prime_field_type_traits.h.tpl\",\n    ],\n    deps = [\n        \"//tachyon/base/console\",\n        \"//tachyon/base/files:file_path_flag\",\n        \"//tachyon/base/files:file_util\",\n        \"//tachyon/base/flag:flag_parser\",\n        \"//tachyon/base/strings:string_number_conversions\",\n        \"//tachyon/build:cc_writer\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/c/math/finite_fields/generator/prime_field_generator/build_defs.bzl",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\ndef _generate_prime_field_impl(ctx):\n    hdr_tpl_path = ctx.expand_location(\"$(location @kroma_network_tachyon//tachyon/c/math/finite_fields/generator/prime_field_generator:prime_field.h.tpl)\", [ctx.attr.hdr_tpl_path])\n    src_tpl_path = ctx.expand_location(\"$(location @kroma_network_tachyon//tachyon/c/math/finite_fields/generator/prime_field_generator:prime_field.cc.tpl)\", [ctx.attr.src_tpl_path])\n    type_traits_hdr_tpl_path = ctx.expand_location(\"$(location @kroma_network_tachyon//tachyon/c/math/finite_fields/generator/prime_field_generator:prime_field_type_traits.h.tpl)\", [ctx.attr.type_traits_hdr_tpl_path])\n\n    arguments = [\n        \"--out=%s\" % (ctx.outputs.out.path),\n        \"--class_name=%s\" % (ctx.attr.class_name),\n        \"--display_name=%s\" % (ctx.attr.display_name),\n        \"--native_type=%s\" % (ctx.attr.native_type),\n        \"--native_hdr=%s\" % (ctx.attr.native_hdr),\n        \"--limb_nums=%s\" % (ctx.attr.limb_nums),\n        \"--hdr_tpl_path=%s\" % (hdr_tpl_path),\n        \"--src_tpl_path=%s\" % (src_tpl_path),\n        \"--type_traits_hdr_tpl_path=%s\" % (type_traits_hdr_tpl_path),\n    ]\n\n    if len(ctx.attr.curve) > 0:\n        arguments.append(\"--curve=%s\" % (ctx.attr.curve))\n\n    ctx.actions.run(\n        inputs = [\n            ctx.files.hdr_tpl_path[0],\n            ctx.files.src_tpl_path[0],\n            ctx.files.type_traits_hdr_tpl_path[0],\n        ],\n        tools = [ctx.executable._tool],\n        executable = ctx.executable._tool,\n        outputs = [ctx.outputs.out],\n        arguments = arguments,\n    )\n\n    return [DefaultInfo(files = depset([ctx.outputs.out]))]\n\ngenerate_prime_field = rule(\n    implementation = _generate_prime_field_impl,\n    attrs = {\n        \"out\": attr.output(mandatory = True),\n        \"class_name\": attr.string(mandatory = True),\n        \"display_name\": attr.string(mandatory = True),\n        \"curve\": attr.string(),\n        \"native_type\": attr.string(mandatory = True),\n        \"native_hdr\": attr.string(mandatory = True),\n        \"limb_nums\": attr.int(default = 0),\n        \"hdr_tpl_path\": attr.label(\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/c/math/finite_fields/generator/prime_field_generator:prime_field.h.tpl\"),\n        ),\n        \"src_tpl_path\": attr.label(\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/c/math/finite_fields/generator/prime_field_generator:prime_field.cc.tpl\"),\n        ),\n        \"type_traits_hdr_tpl_path\": attr.label(\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/c/math/finite_fields/generator/prime_field_generator:prime_field_type_traits.h.tpl\"),\n        ),\n        \"_tool\": attr.label(\n            # TODO(chokobole): Change to \"exec\", so we can build on macos.\n            cfg = \"target\",\n            executable = True,\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/c/math/finite_fields/generator/prime_field_generator\"),\n        ),\n    },\n)\n\ndef generate_ec_prime_fields(\n        name,\n        curve,\n        class_name,\n        display_name,\n        limb_nums,\n        native_type,\n        native_hdr,\n        native_deps):\n    for n in [\n        (\"{}_gen_type_traits_hdr\".format(name), \"{}_type_traits.h\".format(name)),\n        (\"{}_gen_src\".format(name), \"{}.cc\".format(name)),\n        (\"{}_gen_hdr\".format(name), \"{}.h\".format(name)),\n    ]:\n        generate_prime_field(\n            curve = curve,\n            class_name = class_name,\n            display_name = display_name,\n            limb_nums = limb_nums,\n            native_type = native_type,\n            native_hdr = native_hdr,\n            name = n[0],\n            out = n[1],\n        )\n\n    tachyon_cc_library(\n        name = name,\n        hdrs = [\n            \":{}_gen_type_traits_hdr\".format(name),\n            \":{}_gen_hdr\".format(name),\n        ],\n        srcs = [\":{}_gen_src\".format(name)],\n        deps = native_deps + [\n            \"//tachyon/c:export\",\n            \"//tachyon/c/base:type_traits_forward\",\n        ],\n    )\n\ndef generate_small_prime_fields(\n        name,\n        class_name,\n        display_name,\n        native_type,\n        native_hdr,\n        native_deps):\n    for n in [\n        (\"{}_gen_type_traits_hdr\".format(name), \"{}_type_traits.h\".format(name)),\n        (\"{}_gen_src\".format(name), \"{}.cc\".format(name)),\n        (\"{}_gen_hdr\".format(name), \"{}.h\".format(name)),\n    ]:\n        generate_prime_field(\n            class_name = class_name,\n            display_name = display_name,\n            native_type = native_type,\n            native_hdr = native_hdr,\n            name = n[0],\n            out = n[1],\n        )\n\n    tachyon_cc_library(\n        name = name,\n        hdrs = [\n            \":{}_gen_type_traits_hdr\".format(name),\n            \":{}_gen_hdr\".format(name),\n        ],\n        srcs = [\":{}_gen_src\".format(name)],\n        deps = native_deps + [\n            \"//tachyon/c:export\",\n            \"//tachyon/c/base:type_traits_forward\",\n        ],\n    )\n"
  },
  {
    "path": "tachyon/c/math/finite_fields/generator/prime_field_generator/prime_field.cc.tpl",
    "content": "// clang-format off\n#include \"tachyon/c/base/type_traits_forward.h\"\n#include \"%{c_type_traits_hdr}\"\n\ntachyon_%{class_name} tachyon_%{class_name}_zero() {\n  using namespace tachyon::c::base;\n  using NativeType = typename TypeTraits<tachyon_%{class_name}>::NativeType;\n  return c_cast(NativeType::Zero());\n}\n\ntachyon_%{class_name} tachyon_%{class_name}_one() {\n  using namespace tachyon::c::base;\n  using NativeType = typename TypeTraits<tachyon_%{class_name}>::NativeType;\n  return c_cast(NativeType::One());\n}\n\ntachyon_%{class_name} tachyon_%{class_name}_minus_one() {\n  using namespace tachyon::c::base;\n  using NativeType = typename TypeTraits<tachyon_%{class_name}>::NativeType;\n  return c_cast(NativeType::MinusOne());\n}\n\ntachyon_%{class_name} tachyon_%{class_name}_random() {\n  using namespace tachyon::c::base;\n  using NativeType = typename TypeTraits<tachyon_%{class_name}>::NativeType;\n  return c_cast(NativeType::Random());\n}\n\ntachyon_%{class_name} tachyon_%{class_name}_add(const tachyon_%{class_name}* a, const tachyon_%{class_name}* b) {\n  using namespace tachyon::c::base;\n  using NativeType = typename TypeTraits<tachyon_%{class_name}>::NativeType;\n  NativeType native_a = native_cast(*a);\n  return c_cast(native_a += native_cast(*b));\n}\n\ntachyon_%{class_name} tachyon_%{class_name}_sub(const tachyon_%{class_name}* a, const tachyon_%{class_name}* b) {\n  using namespace tachyon::c::base;\n  using NativeType = typename TypeTraits<tachyon_%{class_name}>::NativeType;\n  NativeType native_a = native_cast(*a);\n  return c_cast(native_a -= native_cast(*b));\n}\n\ntachyon_%{class_name} tachyon_%{class_name}_mul(const tachyon_%{class_name}* a, const tachyon_%{class_name}* b) {\n  using namespace tachyon::c::base;\n  using NativeType = typename TypeTraits<tachyon_%{class_name}>::NativeType;\n  NativeType native_a = native_cast(*a);\n  return c_cast(native_a *= native_cast(*b));\n}\n\ntachyon_%{class_name} tachyon_%{class_name}_div(const tachyon_%{class_name}* a, const tachyon_%{class_name}* b) {\n  using namespace tachyon::c::base;\n  using NativeType = typename TypeTraits<tachyon_%{class_name}>::NativeType;\n  NativeType native_a = native_cast(*a);\n  CHECK(native_a /= native_cast(*b));\n  return c_cast(native_a);\n}\n\ntachyon_%{class_name} tachyon_%{class_name}_neg(const tachyon_%{class_name}* a) {\n  using namespace tachyon::c::base;\n  using NativeType = typename TypeTraits<tachyon_%{class_name}>::NativeType;\n  NativeType native_a = native_cast(*a);\n  return c_cast(native_a.NegateInPlace());\n}\n\ntachyon_%{class_name} tachyon_%{class_name}_dbl(const tachyon_%{class_name}* a) {\n  using namespace tachyon::c::base;\n  using NativeType = typename TypeTraits<tachyon_%{class_name}>::NativeType;\n  NativeType native_a = native_cast(*a);\n  return c_cast(native_a.DoubleInPlace());\n}\n\ntachyon_%{class_name} tachyon_%{class_name}_sqr(const tachyon_%{class_name}* a) {\n  using namespace tachyon::c::base;\n  using NativeType = typename TypeTraits<tachyon_%{class_name}>::NativeType;\n  NativeType native_a = native_cast(*a);\n  return c_cast(native_a.SquareInPlace());\n}\n\ntachyon_%{class_name} tachyon_%{class_name}_inv(const tachyon_%{class_name}* a) {\n  using namespace tachyon::c::base;\n  using NativeType = typename TypeTraits<tachyon_%{class_name}>::NativeType;\n  NativeType native_a = native_cast(*a);\n  CHECK(native_a.InverseInPlace());\n  return c_cast(native_a);\n}\n\nbool tachyon_%{class_name}_eq(const tachyon_%{class_name}* a, const tachyon_%{class_name}* b) {\n    using namespace tachyon::c::base;\n    return native_cast(*a) == native_cast(*b);\n}\n\nbool tachyon_%{class_name}_ne(const tachyon_%{class_name}* a, const tachyon_%{class_name}* b) {\n    using namespace tachyon::c::base;\n    return native_cast(*a) != native_cast(*b);\n}\n\nbool tachyon_%{class_name}_gt(const tachyon_%{class_name}* a, const tachyon_%{class_name}* b) {\n    using namespace tachyon::c::base;\n    return native_cast(*a) > native_cast(*b);\n}\n\nbool tachyon_%{class_name}_ge(const tachyon_%{class_name}* a, const tachyon_%{class_name}* b) {\n    using namespace tachyon::c::base;\n    return native_cast(*a) >= native_cast(*b);\n}\n\nbool tachyon_%{class_name}_lt(const tachyon_%{class_name}* a, const tachyon_%{class_name}* b) {\n    using namespace tachyon::c::base;\n    return native_cast(*a) < native_cast(*b);\n}\n\nbool tachyon_%{class_name}_le(const tachyon_%{class_name}* a, const tachyon_%{class_name}* b) {\n    using namespace tachyon::c::base;\n    return native_cast(*a) <= native_cast(*b);\n}\n\n// clang-format on\n"
  },
  {
    "path": "tachyon/c/math/finite_fields/generator/prime_field_generator/prime_field.h.tpl",
    "content": "// clang-format off\n%{if IsECPrimeField}\n/**\n * @file %{class_name}.h\n * @brief Finite field operations for the %{curve} curve.\n *\n * This header file defines operations and structures for manipulating elements of the finite field %{display_name} associated with the %{curve} elliptic curve.\n * It provides fundamental arithmetic operations necessary for elliptic curve cryptography on this curve such as addition, subtraction,\n * and multiplication of field elements.\n * @example prime_field.cc\n */\n%{endif IsECPrimeField}\n%{if IsSmallPrimeField}\n/**\n * @file %{class_name}.h\n * @brief Finite field operations for the %{display_name}\n *\n * This header file defines operations and structures for manipulating elements of the finite field %{display_name}.\n * It provides fundamental arithmetic operations necessary such as addition, subtraction and multiplication of field elements.\n * @example prime_field.cc\n */\n%{endif IsSmallPrimeField}\n\n#include <stdint.h>\n\n#include \"tachyon/c/export.h\"\n\n%{if IsECPrimeField}\n/**\n * @struct tachyon_%{class_name}\n * @brief Represents an element in the finite field %{display_name} for the %{curve} curve.\n *\n * This structure is used to represent an element in the finite field %{display_name},\n * of the %{curve} curve. It stores the element as an array of 64-bit limbs.\n */\nstruct tachyon_%{class_name} {\n  uint64_t limbs[%{limb_nums}];\n};\n%{endif IsECPrimeField}\n%{if IsSmallPrimeField}\n/**\n * @struct tachyon_%{class_name}\n * @brief Represents an element in the finite field %{display_name}.\n *\n * This structure is used to represent an element in the finite field %{display_name},\n * It stores the element as a 32-bit value.\n */\nstruct tachyon_%{class_name} {\n  uint32_t value;\n};\n%{endif IsSmallPrimeField}\n\n%{extern_c_front}\n\n/**\n * @brief Returns the zero element of the finite field %{display_name}.\n * @return The zero element in %{display_name}.\n */\nTACHYON_C_EXPORT tachyon_%{class_name} tachyon_%{class_name}_zero();\n\n/**\n * @brief Returns the one element of the finite field %{display_name}.\n * @return The one element in %{display_name}.\n */\nTACHYON_C_EXPORT tachyon_%{class_name} tachyon_%{class_name}_one();\n\n/**\n * @brief Returns the minus one element of the finite field %{display_name}.\n * @return The one element in %{display_name}.\n */\nTACHYON_C_EXPORT tachyon_%{class_name} tachyon_%{class_name}_minus_one();\n\n/**\n * @brief Generates a random element in the finite field %{display_name}.\n * @return A random element in %{display_name}.\n */\nTACHYON_C_EXPORT tachyon_%{class_name} tachyon_%{class_name}_random();\n\n/**\n * @brief Adds two elements in the finite field %{display_name}.\n * @param a Pointer to the first operand.\n * @param b Pointer to the second operand.\n * @return The result of the addition a + b in %{display_name}.\n */\nTACHYON_C_EXPORT tachyon_%{class_name} tachyon_%{class_name}_add(const tachyon_%{class_name}* a, const tachyon_%{class_name}* b);\n\n/**\n * @brief Subtracts one element from another in the finite field %{display_name}.\n * @param a Pointer to the minuend.\n * @param b Pointer to the subtrahend.\n * @return The result of the subtraction a - b in %{display_name}.\n */\nTACHYON_C_EXPORT tachyon_%{class_name} tachyon_%{class_name}_sub(const tachyon_%{class_name}* a, const tachyon_%{class_name}* b);\n\n/**\n * @brief Multiplies two elements in the finite field %{display_name}.\n * @param a Pointer to the first factor.\n * @param b Pointer to the second factor.\n * @return The result of the multiplication a * b in %{display_name}.\n */\nTACHYON_C_EXPORT tachyon_%{class_name} tachyon_%{class_name}_mul(const tachyon_%{class_name}* a, const tachyon_%{class_name}* b);\n\n/**\n * @brief Divides one element by another in the finite field %{display_name}.\n * @param a Pointer to the dividend.\n * @param b Pointer to the divisor.\n * @return The result of the division a / b in %{display_name}.\n */\nTACHYON_C_EXPORT tachyon_%{class_name} tachyon_%{class_name}_div(const tachyon_%{class_name}* a, const tachyon_%{class_name}* b);\n\n/**\n * @brief Negates an element in the finite field %{display_name}.\n * @param a Pointer to the element to negate.\n * @return The negation of a in %{display_name}.\n */\nTACHYON_C_EXPORT tachyon_%{class_name} tachyon_%{class_name}_neg(const tachyon_%{class_name}* a);\n\n/**\n * @brief Doubles an element in the finite field %{display_name}.\n * @param a Pointer to the element to double.\n * @return The result of doubling a in %{display_name}.\n */\nTACHYON_C_EXPORT tachyon_%{class_name} tachyon_%{class_name}_dbl(const tachyon_%{class_name}* a);\n\n/**\n * @brief Squares an element in the finite field %{display_name}.\n * @param a Pointer to the element to square.\n * @return The square of a in %{display_name}.\n */\nTACHYON_C_EXPORT tachyon_%{class_name} tachyon_%{class_name}_sqr(const tachyon_%{class_name}* a);\n\n/**\n * @brief Calculates the multiplicative inverse of an element in the finite field %{display_name}.\n * @param a Pointer to the element to invert.\n * @return The multiplicative inverse of a in %{display_name}.\n */\nTACHYON_C_EXPORT tachyon_%{class_name} tachyon_%{class_name}_inv(const tachyon_%{class_name}* a);\n\n/**\n * @brief Checks if two elements in the finite field %{display_name} are equal.\n * @param a Pointer to the first element.\n * @param b Pointer to the second element.\n * @return True if a and b are equal, false otherwise.\n */\nTACHYON_C_EXPORT bool tachyon_%{class_name}_eq(const tachyon_%{class_name}* a, const tachyon_%{class_name}* b);\n\n/**\n * @brief Checks if two elements in the finite field %{display_name} are not equal.\n * @param a Pointer to the first element.\n * @param b Pointer to the second element.\n * @return True if a and b are not equal, false otherwise.\n */\nTACHYON_C_EXPORT bool tachyon_%{class_name}_ne(const tachyon_%{class_name}* a, const tachyon_%{class_name}* b);\n\n/**\n * @brief Checks if one element in the finite field %{display_name} is greater than another.\n * @param a Pointer to the first element.\n * @param b Pointer to the second element.\n * @return True if a is greater than b, false otherwise.\n */\nTACHYON_C_EXPORT bool tachyon_%{class_name}_gt(const tachyon_%{class_name}* a, const tachyon_%{class_name}* b);\n\n/**\n * @brief Checks if one element in the finite field %{display_name} is greater than or equal to another.\n * @param a Pointer to the first element.\n * @param b Pointer to the second element.\n * @return True if a is greater than or equal to b, false otherwise.\n */\nTACHYON_C_EXPORT bool tachyon_%{class_name}_ge(const tachyon_%{class_name}* a, const tachyon_%{class_name}* b);\n\n/**\n * @brief Checks if one element in the finite field %{display_name} is less than another.\n * This function compares two elements `a` and `b` in the finite field %{display_name} to determine if `a` is strictly less than `b`.\n * @param a Pointer to the first element for comparison.\n * @param b Pointer to the second element for comparison.\n * @return True if `a` is strictly less than `b`, false otherwise.\n */\nTACHYON_C_EXPORT bool tachyon_%{class_name}_lt(const tachyon_%{class_name}* a, const tachyon_%{class_name}* b);\n\n/**\n * @brief Checks if one element in the finite field %{display_name} is less than or equal to another.\n * This function compares two elements `a` and `b` in the finite field %{display_name} to determine if `a` is less than or equal to `b`. It provides an essential comparison operation for cryptographic algorithms involving finite fields.\n * @param a Pointer to the first element for comparison.\n * @param b Pointer to the second element for comparison.\n * @return True if `a` is less than or equal to `b`, false otherwise.\n */\nTACHYON_C_EXPORT bool tachyon_%{class_name}_le(const tachyon_%{class_name}* a, const tachyon_%{class_name}* b);\n\n// clang-format on\n"
  },
  {
    "path": "tachyon/c/math/finite_fields/generator/prime_field_generator/prime_field_generator.cc",
    "content": "#include \"absl/strings/str_replace.h\"\n\n#include \"tachyon/base/console/iostream.h\"\n#include \"tachyon/base/files/file_path_flag.h\"\n#include \"tachyon/base/files/file_util.h\"\n#include \"tachyon/base/flag/flag_parser.h\"\n#include \"tachyon/base/strings/string_number_conversions.h\"\n#include \"tachyon/build/cc_writer.h\"\n\nnamespace tachyon {\n\nstruct GenerationConfig : public build::CcWriter {\n  base::FilePath hdr_tpl_path;\n  base::FilePath src_tpl_path;\n  base::FilePath type_traits_hdr_tpl_path;\n\n  std::string class_name;\n  std::string display_name;\n  std::string curve;\n  std::string native_type;\n  std::string native_hdr;\n  int limb_nums;\n\n  int GenerateHdr() const;\n  int GenerateSrc() const;\n  int GenerateTypeTraitsHdr() const;\n};\n\nint GenerationConfig::GenerateHdr() const {\n  std::string tpl_content;\n  CHECK(base::ReadFileToString(hdr_tpl_path, &tpl_content));\n\n  std::vector<std::string> tpl_lines = absl::StrSplit(tpl_content, '\\n');\n  RemoveOptionalLines(tpl_lines, \"IsECPrimeField\", limb_nums != 0);\n  RemoveOptionalLines(tpl_lines, \"IsSmallPrimeField\", limb_nums == 0);\n  tpl_content = absl::StrJoin(tpl_lines, \"\\n\");\n\n  std::string content = absl::StrReplaceAll(\n      tpl_content, {\n                       {\"%{class_name}\", class_name},\n                       {\"%{display_name}\", display_name},\n                       {\"%{curve}\", curve},\n                       {\"%{limb_nums}\", base::NumberToString(limb_nums)},\n                   });\n  return WriteHdr(content, true);\n}\n\nint GenerationConfig::GenerateSrc() const {\n  std::string tpl_content;\n  CHECK(base::ReadFileToString(src_tpl_path, &tpl_content));\n\n  const base::FilePath& hdr_path = GetHdrPath();\n  std::string basename = GetHdrPath().BaseName().value();\n  basename = basename.substr(0, basename.find(\".h\"));\n  base::FilePath c_type_traits_hdr =\n      GetHdrPath().DirName().Append(basename + \"_type_traits.h\");\n  std::string content = absl::StrReplaceAll(\n      tpl_content, {\n                       {\"%{class_name}\", class_name},\n                       {\"%{c_type_traits_hdr}\", c_type_traits_hdr.value()},\n                   });\n  return WriteSrc(content);\n}\n\nint GenerationConfig::GenerateTypeTraitsHdr() const {\n  std::string tpl_content;\n  CHECK(base::ReadFileToString(type_traits_hdr_tpl_path, &tpl_content));\n\n  const base::FilePath& hdr_path = GetHdrPath();\n  std::string basename = GetHdrPath().BaseName().value();\n  basename = basename.substr(0, basename.find(\"_type_traits\"));\n  base::FilePath c_hdr = GetHdrPath().DirName().Append(basename + \".h\");\n  std::string content =\n      absl::StrReplaceAll(tpl_content, {\n                                           {\"%{class_name}\", class_name},\n                                           {\"%{c_hdr}\", c_hdr.value()},\n                                           {\"%{native_hdr}\", native_hdr},\n                                           {\"%{native_type}\", native_type},\n                                       });\n  return WriteHdr(content, false);\n}\n\nint RealMain(int argc, char** argv) {\n  GenerationConfig config;\n  config.generator = \"//tachyon/c/math/finite_fields/prime_field_generator\";\n\n  base::FlagParser parser;\n  parser.AddFlag<base::FilePathFlag>(&config.out)\n      .set_long_name(\"--out\")\n      .set_help(\"path to output\");\n  parser.AddFlag<base::StringFlag>(&config.class_name)\n      .set_long_name(\"--class_name\")\n      .set_required();\n  parser.AddFlag<base::StringFlag>(&config.display_name)\n      .set_long_name(\"--display_name\")\n      .set_required();\n  parser.AddFlag<base::StringFlag>(&config.curve).set_long_name(\"--curve\");\n  parser.AddFlag<base::StringFlag>(&config.native_type)\n      .set_long_name(\"--native_type\")\n      .set_required();\n  parser.AddFlag<base::StringFlag>(&config.native_hdr)\n      .set_long_name(\"--native_hdr\")\n      .set_required();\n  parser.AddFlag<base::IntFlag>(&config.limb_nums)\n      .set_long_name(\"--limb_nums\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.hdr_tpl_path)\n      .set_long_name(\"--hdr_tpl_path\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.src_tpl_path)\n      .set_long_name(\"--src_tpl_path\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.type_traits_hdr_tpl_path)\n      .set_long_name(\"--type_traits_hdr_tpl_path\")\n      .set_required();\n\n  std::string error;\n  if (!parser.Parse(argc, argv, &error)) {\n    tachyon_cerr << error << std::endl;\n    return 1;\n  }\n\n  if (base::EndsWith(config.out.value(), \"_type_traits.h\")) {\n    return config.GenerateTypeTraitsHdr();\n  } else if (base::EndsWith(config.out.value(), \".h\")) {\n    return config.GenerateHdr();\n  } else if (base::EndsWith(config.out.value(), \".cc\")) {\n    return config.GenerateSrc();\n  } else {\n    tachyon_cerr << \"suffix not supported:\" << config.out << std::endl;\n    return 1;\n  }\n}\n\n}  // namespace tachyon\n\nint main(int argc, char** argv) { return tachyon::RealMain(argc, argv); }\n"
  },
  {
    "path": "tachyon/c/math/finite_fields/generator/prime_field_generator/prime_field_type_traits.h.tpl",
    "content": "// clang-format off\n#include \"tachyon/c/base/type_traits_forward.h\"\n#include \"%{c_hdr}\"\n#include \"%{native_hdr}\"\n\nnamespace tachyon::c::base {\n\ntemplate <>\nstruct TypeTraits<%{native_type}> {\n  using CType = tachyon_%{class_name};\n};\n\ntemplate <>\nstruct TypeTraits<tachyon_%{class_name}> {\n  using NativeType = %{native_type};\n};\n\n}  // namespace tachyon::c::base\n// clang-format on\n"
  },
  {
    "path": "tachyon/c/math/finite_fields/prime_field_unittest.cc",
    "content": "#include \"gtest/gtest.h\"\n\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/fr_type_traits.h\"\n\nnamespace tachyon {\n\nnamespace {\n\nclass PrimeFieldTest : public testing::Test {\n public:\n  void SetUp() override {\n    a_ = math::bn254::Fr::Random();\n    b_ = math::bn254::Fr::Random();\n\n    c_a_ = c::base::c_cast(a_);\n    c_b_ = c::base::c_cast(b_);\n  }\n\n protected:\n  math::bn254::Fr a_;\n  math::bn254::Fr b_;\n  tachyon_bn254_fr c_a_;\n  tachyon_bn254_fr c_b_;\n};\n\n}  // namespace\n\nTEST_F(PrimeFieldTest, Zero) {\n  tachyon_bn254_fr c_ret = tachyon_bn254_fr_zero();\n  EXPECT_TRUE(c::base::native_cast(c_ret).IsZero());\n}\n\nTEST_F(PrimeFieldTest, One) {\n  tachyon_bn254_fr c_ret = tachyon_bn254_fr_one();\n  EXPECT_TRUE(c::base::native_cast(c_ret).IsOne());\n}\n\nTEST_F(PrimeFieldTest, MinusOne) {\n  tachyon_bn254_fr c_ret = tachyon_bn254_fr_minus_one();\n  EXPECT_TRUE(c::base::native_cast(c_ret).IsMinusOne());\n}\n\nTEST_F(PrimeFieldTest, Random) {\n  tachyon_bn254_fr c_ret = tachyon_bn254_fr_random();\n  EXPECT_NE(c::base::native_cast(c_ret), a_);\n}\n\nTEST_F(PrimeFieldTest, Add) {\n  tachyon_bn254_fr c_ret = tachyon_bn254_fr_add(&c_a_, &c_b_);\n  EXPECT_EQ(c::base::native_cast(c_ret), a_ + b_);\n}\n\nTEST_F(PrimeFieldTest, Sub) {\n  tachyon_bn254_fr c_ret = tachyon_bn254_fr_sub(&c_a_, &c_b_);\n  EXPECT_EQ(c::base::native_cast(c_ret), a_ - b_);\n}\n\nTEST_F(PrimeFieldTest, Mul) {\n  tachyon_bn254_fr c_ret = tachyon_bn254_fr_mul(&c_a_, &c_b_);\n  EXPECT_EQ(c::base::native_cast(c_ret), a_ * b_);\n}\n\nTEST_F(PrimeFieldTest, Div) {\n  tachyon_bn254_fr c_ret = tachyon_bn254_fr_div(&c_a_, &c_b_);\n  EXPECT_EQ(c::base::native_cast(c_ret), a_ / b_);\n}\n\nTEST_F(PrimeFieldTest, Neg) {\n  tachyon_bn254_fr c_ret = tachyon_bn254_fr_neg(&c_a_);\n  EXPECT_EQ(c::base::native_cast(c_ret), -a_);\n}\n\nTEST_F(PrimeFieldTest, Dbl) {\n  tachyon_bn254_fr c_ret = tachyon_bn254_fr_dbl(&c_a_);\n  EXPECT_EQ(c::base::native_cast(c_ret), a_.Double());\n}\n\nTEST_F(PrimeFieldTest, Sqr) {\n  tachyon_bn254_fr c_ret = tachyon_bn254_fr_sqr(&c_a_);\n  EXPECT_EQ(c::base::native_cast(c_ret), a_.Square());\n}\n\nTEST_F(PrimeFieldTest, Inv) {\n  tachyon_bn254_fr c_ret = tachyon_bn254_fr_inv(&c_a_);\n  EXPECT_EQ(c::base::native_cast(c_ret), a_.Inverse());\n}\n\nTEST_F(PrimeFieldTest, Eq) {\n  EXPECT_EQ(tachyon_bn254_fr_eq(&c_a_, &c_b_), a_ == b_);\n}\n\nTEST_F(PrimeFieldTest, Ne) {\n  EXPECT_EQ(tachyon_bn254_fr_ne(&c_a_, &c_b_), a_ != b_);\n}\n\nTEST_F(PrimeFieldTest, Gt) {\n  EXPECT_EQ(tachyon_bn254_fr_gt(&c_a_, &c_b_), a_ > b_);\n}\n\nTEST_F(PrimeFieldTest, Ge) {\n  EXPECT_EQ(tachyon_bn254_fr_ge(&c_a_, &c_b_), a_ >= b_);\n}\n\nTEST_F(PrimeFieldTest, Lt) {\n  EXPECT_EQ(tachyon_bn254_fr_lt(&c_a_, &c_b_), a_ < b_);\n}\n\nTEST_F(PrimeFieldTest, Le) {\n  EXPECT_EQ(tachyon_bn254_fr_le(&c_a_, &c_b_), a_ <= b_);\n}\n\n}  // namespace tachyon\n"
  },
  {
    "path": "tachyon/c/math/matrix/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n    name = \"matrix_hdrs\",\n    srcs = [\"baby_bear_row_major_matrix.h\"],\n)\n\ntachyon_cc_library(\n    name = \"baby_bear_row_major_matrix\",\n    srcs = [\"baby_bear_row_major_matrix.cc\"],\n    hdrs = [\n        \"baby_bear_row_major_matrix.h\",\n        \"baby_bear_row_major_matrix_type_traits.h\",\n    ],\n    deps = [\n        \"//tachyon/c/math/finite_fields/baby_bear\",\n        \"//tachyon/math/matrix:matrix_types\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"matrix\",\n    deps = [\":baby_bear_row_major_matrix\"],\n)\n\ntachyon_cc_unittest(\n    name = \"matrix_unittests\",\n    srcs = [\"baby_bear_row_major_matrix_unittest.cc\"],\n    deps = [\n        \":baby_bear_row_major_matrix\",\n        \"//tachyon/math/finite_fields/test:finite_field_test\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/c/math/matrix/baby_bear_row_major_matrix.cc",
    "content": "#include \"tachyon/c/math/matrix/baby_bear_row_major_matrix.h\"\n\n#include \"tachyon/c/math/finite_fields/baby_bear/baby_bear_type_traits.h\"\n#include \"tachyon/c/math/matrix/baby_bear_row_major_matrix_type_traits.h\"\n\nusing namespace tachyon;\n\nusing RowMajorMatrix = math::RowMajorMatrix<math::BabyBear>;\n\ntachyon_baby_bear_row_major_matrix* tachyon_baby_bear_row_major_matrix_create(\n    tachyon_baby_bear* ptr, size_t rows, size_t cols) {\n  RowMajorMatrix* matrix = new RowMajorMatrix(\n      Eigen::Map<RowMajorMatrix>(c::base::native_cast(ptr), rows, cols));\n  return c::base::c_cast(matrix);\n}\n\ntachyon_baby_bear_row_major_matrix* tachyon_baby_bear_row_major_matrix_clone(\n    const tachyon_baby_bear_row_major_matrix* matrix) {\n  RowMajorMatrix* cloned = new RowMajorMatrix(c::base::native_cast(*matrix));\n  return c::base::c_cast(cloned);\n}\n\nvoid tachyon_baby_bear_row_major_matrix_destroy(\n    tachyon_baby_bear_row_major_matrix* matrix) {\n  delete c::base::native_cast(matrix);\n}\n\nsize_t tachyon_baby_bear_row_major_matrix_get_rows(\n    const tachyon_baby_bear_row_major_matrix* matrix) {\n  return static_cast<size_t>(c::base::native_cast(*matrix).rows());\n}\n\nsize_t tachyon_baby_bear_row_major_matrix_get_cols(\n    const tachyon_baby_bear_row_major_matrix* matrix) {\n  return static_cast<size_t>(c::base::native_cast(*matrix).cols());\n}\n\ntachyon_baby_bear tachyon_baby_bear_row_major_matrix_get_element(\n    const tachyon_baby_bear_row_major_matrix* matrix, size_t row, size_t col) {\n  return c::base::c_cast(c::base::native_cast(*matrix)(row, col));\n}\n\nconst tachyon_baby_bear* tachyon_baby_bear_row_major_matrix_get_const_data_ptr(\n    const tachyon_baby_bear_row_major_matrix* matrix) {\n  return c::base::c_cast(c::base::native_cast(*matrix).data());\n}\n\ntachyon_baby_bear* tachyon_baby_bear_row_major_matrix_get_data_ptr(\n    tachyon_baby_bear_row_major_matrix* matrix) {\n  return c::base::c_cast(c::base::native_cast(*matrix).data());\n}\n"
  },
  {
    "path": "tachyon/c/math/matrix/baby_bear_row_major_matrix.h",
    "content": "/**\n * @file baby_bear_row_major_matrix.h\n * @brief Row major matrix operations for baby bear.\n *\n * This header file defines the structure and API for row major matrix for\n * baby bear. This includes creation, cloning, and destruction of row major\n * matrix structures, which are fundamental to various cryptographic protocols\n * and algorithms implemented on this field.\n */\n\n#ifndef TACHYON_C_MATH_MATRIX_BABY_BEAR_ROW_MAJOR_MATRIX_H_\n#define TACHYON_C_MATH_MATRIX_BABY_BEAR_ROW_MAJOR_MATRIX_H_\n\n#include <stddef.h>\n\n#include \"tachyon/c/export.h\"\n#include \"tachyon/c/math/finite_fields/baby_bear/baby_bear.h\"\n\n/**\n * @struct tachyon_baby_bear_row_major_matrix\n * @brief Represents a row major matrix for baby bear.\n *\n * This structure encapsulates a row major matrix, offering efficient\n * storage and manipulation for cryptographic computations.\n */\nstruct tachyon_baby_bear_row_major_matrix {};\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Creates a new instance of a row major matrix.\n *\n * Allocates and initializes a new row major matrix structure for use\n * in cryptographic algorithms.\n *\n * @param ptr A pointer to the matrix data.\n * @param rows The number of rows.\n * @param cols The number of columns.\n * @return A pointer to the newly created matrix structure.\n */\nTACHYON_C_EXPORT tachyon_baby_bear_row_major_matrix*\ntachyon_baby_bear_row_major_matrix_create(tachyon_baby_bear* ptr, size_t rows,\n                                          size_t cols);\n\n/**\n * @brief Clones a row major matrix.\n *\n * Creates a deep copy of the given row major matrix.\n *\n * @param matrix A const pointer to the matrix to be cloned.\n * @return A pointer to the cloned matrix structure.\n */\nTACHYON_C_EXPORT tachyon_baby_bear_row_major_matrix*\ntachyon_baby_bear_row_major_matrix_clone(\n    const tachyon_baby_bear_row_major_matrix* matrix);\n\n/**\n * @brief Destroys a row major matrix.\n *\n * Frees the memory allocated for a row major matrix structure,\n * effectively destroying it.\n *\n * @param matrix A pointer to the matrix to be destroyed.\n */\nTACHYON_C_EXPORT void tachyon_baby_bear_row_major_matrix_destroy(\n    tachyon_baby_bear_row_major_matrix* matrix);\n\n/**\n * @brief Returns the number of rows in the row major matrix.\n *\n * @param matrix A const pointer to the matrix.\n * @return The number of rows in the row major matrix.\n */\nTACHYON_C_EXPORT size_t tachyon_baby_bear_row_major_matrix_get_rows(\n    const tachyon_baby_bear_row_major_matrix* matrix);\n\n/**\n * @brief Returns the number of columns in the row major matrix.\n *\n * @param matrix A const pointer to the matrix.\n * @return The number of columns in the row major matrix.\n */\nTACHYON_C_EXPORT size_t tachyon_baby_bear_row_major_matrix_get_cols(\n    const tachyon_baby_bear_row_major_matrix* matrix);\n\n/**\n * @brief Returns the matrix element at a given row and column index.\n *\n * @param matrix A const pointer to the matrix.\n * @param row The row index.\n * @param col The column index.\n * @return The element of the row major matrix at a given row and column index.\n */\nTACHYON_C_EXPORT tachyon_baby_bear\ntachyon_baby_bear_row_major_matrix_get_element(\n    const tachyon_baby_bear_row_major_matrix* matrix, size_t row, size_t col);\n\n/**\n * @brief Returns a const pointer to the matrix data.\n *\n * @param matrix A const pointer to the matrix.\n * @return A const pointer to the matrix data.\n */\nTACHYON_C_EXPORT const tachyon_baby_bear*\ntachyon_baby_bear_row_major_matrix_get_const_data_ptr(\n    const tachyon_baby_bear_row_major_matrix* matrix);\n\n/**\n * @brief Returns a pointer to the matrix data.\n *\n * @param matrix A pointer to the matrix.\n * @return A pointer to the matrix data.\n */\nTACHYON_C_EXPORT tachyon_baby_bear*\ntachyon_baby_bear_row_major_matrix_get_data_ptr(\n    tachyon_baby_bear_row_major_matrix* matrix);\n\n#ifdef __cplusplus\n}  // extern \"C\"\n#endif\n\n#endif  // TACHYON_C_MATH_MATRIX_BABY_BEAR_ROW_MAJOR_MATRIX_H_\n"
  },
  {
    "path": "tachyon/c/math/matrix/baby_bear_row_major_matrix_type_traits.h",
    "content": "#ifndef TACHYON_C_MATH_MATRIX_BABY_BEAR_ROW_MAJOR_MATRIX_TYPE_TRAITS_H_\n#define TACHYON_C_MATH_MATRIX_BABY_BEAR_ROW_MAJOR_MATRIX_TYPE_TRAITS_H_\n\n#include \"tachyon/c/base/type_traits_forward.h\"\n#include \"tachyon/c/math/matrix/baby_bear_row_major_matrix.h\"\n#include \"tachyon/math/finite_fields/baby_bear/baby_bear.h\"\n#include \"tachyon/math/matrix/matrix_types.h\"\n\nnamespace tachyon::c::base {\n\ntemplate <>\nstruct TypeTraits<tachyon::math::RowMajorMatrix<tachyon::math::BabyBear>> {\n  using CType = tachyon_baby_bear_row_major_matrix;\n};\n\ntemplate <>\nstruct TypeTraits<tachyon_baby_bear_row_major_matrix> {\n  using NativeType = tachyon::math::RowMajorMatrix<tachyon::math::BabyBear>;\n};\n\n}  // namespace tachyon::c::base\n\n#endif  // TACHYON_C_MATH_MATRIX_BABY_BEAR_ROW_MAJOR_MATRIX_TYPE_TRAITS_H_\n"
  },
  {
    "path": "tachyon/c/math/matrix/baby_bear_row_major_matrix_unittest.cc",
    "content": "#include \"tachyon/c/math/matrix/baby_bear_row_major_matrix.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/c/math/finite_fields/baby_bear/baby_bear_type_traits.h\"\n#include \"tachyon/c/math/matrix/baby_bear_row_major_matrix_type_traits.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\nusing F = BabyBear;\n\nclass RowMajorMatrixTest : public FiniteFieldTest<F> {\n public:\n  void SetUp() override {\n    cpp_matrix_ = RowMajorMatrix<F>::Random(5, 5);\n    matrix_ = c::base::c_cast(&cpp_matrix_);\n  }\n\n protected:\n  RowMajorMatrix<F> cpp_matrix_;\n  tachyon_baby_bear_row_major_matrix* matrix_;\n};\n\n}  // namespace\n\nTEST_F(RowMajorMatrixTest, APIs) {\n  EXPECT_EQ(cpp_matrix_,\n            c::base::native_cast(*tachyon_baby_bear_row_major_matrix_create(\n                c::base::c_cast(cpp_matrix_.data()), 5, 5)));\n\n  tachyon_baby_bear_row_major_matrix* cloned =\n      tachyon_baby_bear_row_major_matrix_clone(matrix_);\n  tachyon_baby_bear_row_major_matrix_destroy(cloned);\n\n  EXPECT_EQ(5, tachyon_baby_bear_row_major_matrix_get_rows(matrix_));\n  EXPECT_EQ(5, tachyon_baby_bear_row_major_matrix_get_cols(matrix_));\n\n  EXPECT_EQ(cpp_matrix_(2, 3),\n            c::base::native_cast(\n                tachyon_baby_bear_row_major_matrix_get_element(matrix_, 2, 3)));\n\n  EXPECT_EQ(\n      cpp_matrix_.data(),\n      c::base::native_cast(\n          tachyon_baby_bear_row_major_matrix_get_const_data_ptr(matrix_)));\n  EXPECT_EQ(cpp_matrix_.data(),\n            c::base::native_cast(\n                tachyon_baby_bear_row_major_matrix_get_data_ptr(matrix_)));\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/c/math/polynomials/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n    name = \"polynomials_hdrs\",\n    srcs = [\n        \"constants.h\",\n        \"//tachyon/c/math/polynomials/univariate:univariate_hdrs\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"polynomials\",\n    deps = [\"//tachyon/c/math/polynomials/univariate\"],\n)\n\ntachyon_cc_library(\n    name = \"constants\",\n    hdrs = [\"constants.h\"],\n)\n"
  },
  {
    "path": "tachyon/c/math/polynomials/constants.h",
    "content": "#ifndef TACHYON_C_MATH_POLYNOMIALS_CONSTANTS_H_\n#define TACHYON_C_MATH_POLYNOMIALS_CONSTANTS_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\nnamespace tachyon::c::math {\n\n// NOTE(chokobole): We set |kMaxDegree| to |SIZE_MAX| - 1 on purpose to avoid\n// creating variant apis corresponding to the set of each degree.\nconstexpr size_t kMaxDegree = SIZE_MAX - 1;\n\n}  // namespace tachyon::c::math\n\n#endif  // TACHYON_C_MATH_POLYNOMIALS_CONSTANTS_H_\n"
  },
  {
    "path": "tachyon/c/math/polynomials/univariate/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n    name = \"univariate_hdrs\",\n    srcs = [\n        \"bn254_univariate_dense_polynomial.h\",\n        \"bn254_univariate_evaluation_domain.h\",\n        \"bn254_univariate_evaluations.h\",\n        \"bn254_univariate_rational_evaluations.h\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"univariate\",\n    deps = [\":bn254_univariate_evaluation_domain\"],\n)\n\ntachyon_cc_library(\n    name = \"bn254_univariate_dense_polynomial\",\n    srcs = [\"bn254_univariate_dense_polynomial.cc\"],\n    hdrs = [\n        \"bn254_univariate_dense_polynomial.h\",\n        \"bn254_univariate_dense_polynomial_type_traits.h\",\n    ],\n    deps = [\n        \"//tachyon/c:export\",\n        \"//tachyon/c/base:type_traits_forward\",\n        \"//tachyon/c/math/polynomials:constants\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:fr\",\n        \"//tachyon/math/polynomials/univariate:univariate_polynomial\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"bn254_univariate_evaluation_domain\",\n    srcs = [\"bn254_univariate_evaluation_domain.cc\"],\n    hdrs = [\n        \"bn254_univariate_evaluation_domain.h\",\n        \"bn254_univariate_evaluation_domain_type_traits.h\",\n    ],\n    deps = [\n        \":bn254_univariate_dense_polynomial\",\n        \":bn254_univariate_evaluations\",\n        \":bn254_univariate_rational_evaluations\",\n        \"//tachyon/c/base:type_traits_forward\",\n        \"//tachyon/math/polynomials/univariate:univariate_evaluation_domain\",\n        \"//tachyon/math/polynomials/univariate:univariate_evaluation_domain_factory\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"bn254_univariate_evaluations\",\n    srcs = [\"bn254_univariate_evaluations.cc\"],\n    hdrs = [\n        \"bn254_univariate_evaluations.h\",\n        \"bn254_univariate_evaluations_type_traits.h\",\n    ],\n    deps = [\n        \"//tachyon/c/base:type_traits_forward\",\n        \"//tachyon/c/math/elliptic_curves/bn/bn254:fr\",\n        \"//tachyon/c/math/polynomials:constants\",\n        \"//tachyon/math/polynomials/univariate:univariate_evaluations\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"bn254_univariate_rational_evaluations\",\n    srcs = [\"bn254_univariate_rational_evaluations.cc\"],\n    hdrs = [\n        \"bn254_univariate_rational_evaluations.h\",\n        \"bn254_univariate_rational_evaluations_type_traits.h\",\n    ],\n    deps = [\n        \":bn254_univariate_evaluations\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/c/base:type_traits_forward\",\n        \"//tachyon/math/base:rational_field\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"univariate_unittests\",\n    srcs = [\n        \"bn254_univariate_dense_polynomial_unittest.cc\",\n        \"bn254_univariate_evaluation_domain_unittest.cc\",\n        \"bn254_univariate_evaluations_unittest.cc\",\n        \"bn254_univariate_rational_evaluations_unittest.cc\",\n    ],\n    deps = [\n        \":bn254_univariate_evaluation_domain\",\n        \":bn254_univariate_rational_evaluations\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/cc/math/elliptic_curves/bn/bn254:fr\",\n        \"//tachyon/math/finite_fields/test:finite_field_test\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/c/math/polynomials/univariate/bn254_univariate_dense_polynomial.cc",
    "content": "#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_dense_polynomial.h\"\n\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_dense_polynomial_type_traits.h\"\n\nusing namespace tachyon;\n\nusing Poly =\n    math::UnivariateDensePolynomial<math::bn254::Fr, c::math::kMaxDegree>;\n\ntachyon_bn254_univariate_dense_polynomial*\ntachyon_bn254_univariate_dense_polynomial_create() {\n  return c::base::c_cast(new Poly);\n}\n\ntachyon_bn254_univariate_dense_polynomial*\ntachyon_bn254_univariate_dense_polynomial_clone(\n    const tachyon_bn254_univariate_dense_polynomial* poly) {\n  Poly* cloned_poly = new Poly(*c::base::native_cast(poly));\n  return c::base::c_cast(cloned_poly);\n}\n\nvoid tachyon_bn254_univariate_dense_polynomial_destroy(\n    tachyon_bn254_univariate_dense_polynomial* poly) {\n  delete c::base::native_cast(poly);\n}\n"
  },
  {
    "path": "tachyon/c/math/polynomials/univariate/bn254_univariate_dense_polynomial.h",
    "content": "/**\n * @file bn254_univariate_dense_polynomial.h\n * @brief Univariate dense polynomial operations for the BN254 curve.\n *\n * This header file defines the structure and API for univariate dense\n * polynomials over the BN254 curve. This includes creation, cloning, and\n * destruction of dense polynomial structures, which are fundamental to various\n * cryptographic protocols and algorithms implemented on this curve.\n */\n\n#ifndef TACHYON_C_MATH_POLYNOMIALS_UNIVARIATE_BN254_UNIVARIATE_DENSE_POLYNOMIAL_H_\n#define TACHYON_C_MATH_POLYNOMIALS_UNIVARIATE_BN254_UNIVARIATE_DENSE_POLYNOMIAL_H_\n\n#include \"tachyon/c/export.h\"\n\n/**\n * @struct tachyon_bn254_univariate_dense_polynomial\n * @brief Represents a univariate dense polynomial over the BN254 curve.\n *\n * This structure encapsulates a univariate dense polynomial, offering efficient\n * storage and manipulation for cryptographic computations.\n */\nstruct tachyon_bn254_univariate_dense_polynomial {};\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Creates a new instance of a univariate dense polynomial.\n *\n * Allocates and initializes a new univariate dense polynomial structure for use\n * in cryptographic algorithms.\n *\n * @return A pointer to the newly created polynomial structure.\n */\nTACHYON_C_EXPORT tachyon_bn254_univariate_dense_polynomial*\ntachyon_bn254_univariate_dense_polynomial_create();\n\n/**\n * @brief Clones a univariate dense polynomial.\n *\n * Creates a deep copy of the given univariate dense polynomial.\n *\n * @param poly A pointer to the polynomial to be cloned.\n * @return A pointer to the cloned polynomial structure.\n */\nTACHYON_C_EXPORT tachyon_bn254_univariate_dense_polynomial*\ntachyon_bn254_univariate_dense_polynomial_clone(\n    const tachyon_bn254_univariate_dense_polynomial* poly);\n\n/**\n * @brief Destroys a univariate dense polynomial.\n *\n * Frees the memory allocated for a univariate dense polynomial structure,\n * effectively destroying it.\n *\n * @param poly A pointer to the polynomial to be destroyed.\n */\nTACHYON_C_EXPORT void tachyon_bn254_univariate_dense_polynomial_destroy(\n    tachyon_bn254_univariate_dense_polynomial* poly);\n\n#ifdef __cplusplus\n}  // extern \"C\"\n#endif\n\n#endif  // TACHYON_C_MATH_POLYNOMIALS_UNIVARIATE_BN254_UNIVARIATE_DENSE_POLYNOMIAL_H_\n"
  },
  {
    "path": "tachyon/c/math/polynomials/univariate/bn254_univariate_dense_polynomial_type_traits.h",
    "content": "#ifndef TACHYON_C_MATH_POLYNOMIALS_UNIVARIATE_BN254_UNIVARIATE_DENSE_POLYNOMIAL_TYPE_TRAITS_H_\n#define TACHYON_C_MATH_POLYNOMIALS_UNIVARIATE_BN254_UNIVARIATE_DENSE_POLYNOMIAL_TYPE_TRAITS_H_\n\n#include \"tachyon/c/base/type_traits_forward.h\"\n#include \"tachyon/c/math/polynomials/constants.h\"\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_dense_polynomial.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/fr.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_polynomial.h\"\n\nnamespace tachyon::c::base {\n\ntemplate <>\nstruct TypeTraits<tachyon::math::UnivariateDensePolynomial<\n    tachyon::math::bn254::Fr, math::kMaxDegree>> {\n  using CType = tachyon_bn254_univariate_dense_polynomial;\n};\n\ntemplate <>\nstruct TypeTraits<tachyon_bn254_univariate_dense_polynomial> {\n  using NativeType =\n      tachyon::math::UnivariateDensePolynomial<tachyon::math::bn254::Fr,\n                                               math::kMaxDegree>;\n};\n\n}  // namespace tachyon::c::base\n\n#endif  // TACHYON_C_MATH_POLYNOMIALS_UNIVARIATE_BN254_UNIVARIATE_DENSE_POLYNOMIAL_TYPE_TRAITS_H_\n"
  },
  {
    "path": "tachyon/c/math/polynomials/univariate/bn254_univariate_dense_polynomial_unittest.cc",
    "content": "#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_dense_polynomial.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_dense_polynomial_type_traits.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\nconstexpr size_t kDegree = 5;\n\nclass UnivariateDensePolynomialTest : public FiniteFieldTest<bn254::Fr> {\n public:\n  using Poly = UnivariateDensePolynomial<bn254::Fr, c::math::kMaxDegree>;\n\n  void SetUp() override {\n    Poly* cpp_poly = new Poly(Poly::Random(kDegree));\n    poly_ = c::base::c_cast(cpp_poly);\n  }\n\n  void TearDown() override {\n    tachyon_bn254_univariate_dense_polynomial_destroy(poly_);\n  }\n\n protected:\n  tachyon_bn254_univariate_dense_polynomial* poly_;\n};\n\n}  // namespace\n\nTEST_F(UnivariateDensePolynomialTest, Clone) {\n  if (c::base::native_cast(*poly_).NumElements() > 0) {\n    tachyon_bn254_univariate_dense_polynomial* poly_clone =\n        tachyon_bn254_univariate_dense_polynomial_clone(poly_);\n    // NOTE(chokobole): It's safe to access since we checked |NumElements()| is\n    // greater than 0.\n    c::base::native_cast(*poly_).at(0) += bn254::Fr::One();\n    EXPECT_NE((c::base::native_cast(*poly_))[0],\n              (c::base::native_cast(*poly_clone))[0]);\n    tachyon_bn254_univariate_dense_polynomial_destroy(poly_clone);\n  } else {\n    GTEST_SKIP() << \"This test assumes that the coefficient for the 0th degree \"\n                    \"is not empty\";\n  }\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/c/math/polynomials/univariate/bn254_univariate_evaluation_domain.cc",
    "content": "#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_evaluation_domain.h\"\n\n#include <utility>\n\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_dense_polynomial_type_traits.h\"\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_evaluation_domain_type_traits.h\"\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_evaluations_type_traits.h\"\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_rational_evaluations_type_traits.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluation_domain_factory.h\"\n\nusing namespace tachyon;\n\nusing Domain =\n    math::UnivariateEvaluationDomain<math::bn254::Fr, c::math::kMaxDegree>;\nusing RationalEvals =\n    math::UnivariateEvaluations<math::RationalField<math::bn254::Fr>,\n                                c::math::kMaxDegree>;\n\ntachyon_bn254_univariate_evaluation_domain*\ntachyon_bn254_univariate_evaluation_domain_create(size_t num_coeffs) {\n  return c::base::c_cast(Domain::Create(num_coeffs).release());\n}\n\nvoid tachyon_bn254_univariate_evaluation_domain_destroy(\n    tachyon_bn254_univariate_evaluation_domain* domain) {\n  delete c::base::native_cast(domain);\n}\n\ntachyon_bn254_univariate_evaluations*\ntachyon_bn254_univariate_evaluation_domain_empty_evals(\n    const tachyon_bn254_univariate_evaluation_domain* domain) {\n  return c::base::c_cast(\n      new Domain::Evals(c::base::native_cast(domain)->Zero<Domain::Evals>()));\n}\n\ntachyon_bn254_univariate_dense_polynomial*\ntachyon_bn254_univariate_evaluation_domain_empty_poly(\n    const tachyon_bn254_univariate_evaluation_domain* domain) {\n  return c::base::c_cast(new Domain::DensePoly(\n      c::base::native_cast(domain)->Zero<Domain::DensePoly>()));\n}\n\ntachyon_bn254_univariate_rational_evaluations*\ntachyon_bn254_univariate_evaluation_domain_empty_rational_evals(\n    const tachyon_bn254_univariate_evaluation_domain* domain) {\n  return c::base::c_cast(\n      new RationalEvals(c::base::native_cast(domain)->Zero<RationalEvals>()));\n}\n\ntachyon_bn254_univariate_evaluations*\ntachyon_bn254_univariate_evaluation_domain_fft(\n    const tachyon_bn254_univariate_evaluation_domain* domain,\n    const tachyon_bn254_univariate_dense_polynomial* poly) {\n  Domain::Evals* evals = new Domain::Evals(\n      c::base::native_cast(domain)->FFT(c::base::native_cast(*poly)));\n  return c::base::c_cast(evals);\n}\n\ntachyon_bn254_univariate_evaluations*\ntachyon_bn254_univariate_evaluation_domain_fft_inplace(\n    const tachyon_bn254_univariate_evaluation_domain* domain,\n    tachyon_bn254_univariate_dense_polynomial* poly) {\n  Domain::Evals* evals = new Domain::Evals(c::base::native_cast(domain)->FFT(\n      c::base::native_cast(std::move(*poly))));\n  return c::base::c_cast(evals);\n}\n\ntachyon_bn254_univariate_dense_polynomial*\ntachyon_bn254_univariate_evaluation_domain_ifft(\n    const tachyon_bn254_univariate_evaluation_domain* domain,\n    const tachyon_bn254_univariate_evaluations* evals) {\n  Domain::DensePoly* poly = new Domain::DensePoly(\n      c::base::native_cast(domain)->IFFT(c::base::native_cast(*evals)));\n  return c::base::c_cast(poly);\n}\n\ntachyon_bn254_univariate_dense_polynomial*\ntachyon_bn254_univariate_evaluation_domain_ifft_inplace(\n    const tachyon_bn254_univariate_evaluation_domain* domain,\n    tachyon_bn254_univariate_evaluations* evals) {\n  Domain::DensePoly* poly =\n      new Domain::DensePoly(c::base::native_cast(domain)->IFFT(\n          c::base::native_cast(std::move(*evals))));\n  return c::base::c_cast(poly);\n}\n"
  },
  {
    "path": "tachyon/c/math/polynomials/univariate/bn254_univariate_evaluation_domain.h",
    "content": "/**\n * @file bn254_univariate_evaluation_domain.h\n * @brief Univariate Evaluation Domain for BN254 Curve.\n *\n * This header file defines the univariate evaluation domain structure and\n * related operations for the BN254 curve. It includes functions for creating\n * and destroying evaluation domains, generating empty polynomial and evaluation\n * structures, and performing Fast Fourier Transforms (FFT) and inverse FFT\n * (IFFT) on polynomials.\n */\n#ifndef TACHYON_C_MATH_POLYNOMIALS_UNIVARIATE_BN254_UNIVARIATE_EVALUATION_DOMAIN_H_\n#define TACHYON_C_MATH_POLYNOMIALS_UNIVARIATE_BN254_UNIVARIATE_EVALUATION_DOMAIN_H_\n\n#include <stddef.h>\n\n#include \"tachyon/c/export.h\"\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_dense_polynomial.h\"\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_evaluations.h\"\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_rational_evaluations.h\"\n\n/**\n * @struct tachyon_bn254_univariate_evaluation_domain\n * @brief Represents an evaluation domain for univariate polynomials over the\n * BN254 curve.\n */\nstruct tachyon_bn254_univariate_evaluation_domain {};\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Creates a new evaluation domain for a given number of coefficients.\n *\n * @param num_coeffs The number of coefficients in the domain.\n * @return A pointer to the newly created evaluation domain.\n */\nTACHYON_C_EXPORT tachyon_bn254_univariate_evaluation_domain*\ntachyon_bn254_univariate_evaluation_domain_create(size_t num_coeffs);\n\n/**\n * @brief Destroys an evaluation domain, freeing its allocated resources.\n *\n * @param domain A pointer to the evaluation domain to destroy.\n */\nTACHYON_C_EXPORT void tachyon_bn254_univariate_evaluation_domain_destroy(\n    tachyon_bn254_univariate_evaluation_domain* domain);\n\n/**\n * @brief Creates an empty evaluations structure associated with the evaluation\n * domain.\n *\n * @param domain A const pointer to the evaluation domain.\n * @return A pointer to the empty evaluations structure.\n */\nTACHYON_C_EXPORT tachyon_bn254_univariate_evaluations*\ntachyon_bn254_univariate_evaluation_domain_empty_evals(\n    const tachyon_bn254_univariate_evaluation_domain* domain);\n\n/**\n * @brief Creates an empty dense polynomial associated with the evaluation\n * domain.\n *\n * @param domain A const pointer to the evaluation domain.\n * @return A pointer to the empty dense polynomial.\n */\nTACHYON_C_EXPORT tachyon_bn254_univariate_dense_polynomial*\ntachyon_bn254_univariate_evaluation_domain_empty_poly(\n    const tachyon_bn254_univariate_evaluation_domain* domain);\n\n/**\n * @brief Creates an empty rational evaluations structure associated with the\n * evaluation domain.\n *\n * @param domain A const pointer to the evaluation domain.\n * @return A pointer to the empty rational evaluations structure.\n */\nTACHYON_C_EXPORT tachyon_bn254_univariate_rational_evaluations*\ntachyon_bn254_univariate_evaluation_domain_empty_rational_evals(\n    const tachyon_bn254_univariate_evaluation_domain* domain);\n\n/**\n * @brief Performs the Fast Fourier Transform (FFT) on a given polynomial within\n * the domain.\n *\n * @param domain A const pointer to the evaluation domain.\n * @param poly A const pointer to the polynomial to transform.\n * @return A pointer to the evaluations resulting from the FFT.\n */\nTACHYON_C_EXPORT tachyon_bn254_univariate_evaluations*\ntachyon_bn254_univariate_evaluation_domain_fft(\n    const tachyon_bn254_univariate_evaluation_domain* domain,\n    const tachyon_bn254_univariate_dense_polynomial* poly);\n\n/**\n * @brief Performs the in-place Fast Fourier Transform (FFT) on a given\n * polynomial within the domain. Note that memory space in poly is altered\n * after this call.\n *\n * @param domain A const pointer to the evaluation domain.\n * @param poly A pointer to the polynomial to transform.\n * @return A pointer to the evaluations resulting from the FFT.\n */\nTACHYON_C_EXPORT tachyon_bn254_univariate_evaluations*\ntachyon_bn254_univariate_evaluation_domain_fft_inplace(\n    const tachyon_bn254_univariate_evaluation_domain* domain,\n    tachyon_bn254_univariate_dense_polynomial* poly);\n\n/**\n * @brief Performs the inverse Fast Fourier Transform (IFFT) on given\n * evaluations within the domain.\n *\n * @param domain A const pointer to the evaluation domain.\n * @param evals A const pointer to the evaluations to transform back into a\n * polynomial.\n * @return A pointer to the dense polynomial resulting from the IFFT.\n */\nTACHYON_C_EXPORT tachyon_bn254_univariate_dense_polynomial*\ntachyon_bn254_univariate_evaluation_domain_ifft(\n    const tachyon_bn254_univariate_evaluation_domain* domain,\n    const tachyon_bn254_univariate_evaluations* evals);\n\n/**\n * @brief Performs the in-place inverse Fast Fourier Transform (IFFT) on given\n * evaluations within the domain. Note that memory space in evals is altered\n * after this call.\n *\n * @param domain A const pointer to the evaluation domain.\n * @param evals A pointer to the evaluations to transform back into a\n * polynomial.\n * @return A pointer to the dense polynomial resulting from the IFFT.\n */\nTACHYON_C_EXPORT tachyon_bn254_univariate_dense_polynomial*\ntachyon_bn254_univariate_evaluation_domain_ifft_inplace(\n    const tachyon_bn254_univariate_evaluation_domain* domain,\n    tachyon_bn254_univariate_evaluations* evals);\n\n#ifdef __cplusplus\n}  // extern \"C\"\n#endif\n\n#endif  // TACHYON_C_MATH_POLYNOMIALS_UNIVARIATE_BN254_UNIVARIATE_EVALUATION_DOMAIN_H_\n"
  },
  {
    "path": "tachyon/c/math/polynomials/univariate/bn254_univariate_evaluation_domain_type_traits.h",
    "content": "#ifndef TACHYON_C_MATH_POLYNOMIALS_UNIVARIATE_BN254_UNIVARIATE_EVALUATION_DOMAIN_TYPE_TRAITS_H_\n#define TACHYON_C_MATH_POLYNOMIALS_UNIVARIATE_BN254_UNIVARIATE_EVALUATION_DOMAIN_TYPE_TRAITS_H_\n\n#include \"tachyon/c/base/type_traits_forward.h\"\n#include \"tachyon/c/math/polynomials/constants.h\"\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_evaluation_domain.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/fr.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluation_domain.h\"\n\nnamespace tachyon::c::base {\n\ntemplate <>\nstruct TypeTraits<tachyon::math::UnivariateEvaluationDomain<\n    tachyon::math::bn254::Fr, math::kMaxDegree>> {\n  using CType = tachyon_bn254_univariate_evaluation_domain;\n};\n\ntemplate <>\nstruct TypeTraits<tachyon_bn254_univariate_evaluation_domain> {\n  using NativeType =\n      tachyon::math::UnivariateEvaluationDomain<tachyon::math::bn254::Fr,\n                                                math::kMaxDegree>;\n};\n\n}  // namespace tachyon::c::base\n\n#endif  // TACHYON_C_MATH_POLYNOMIALS_UNIVARIATE_BN254_UNIVARIATE_EVALUATION_DOMAIN_TYPE_TRAITS_H_\n"
  },
  {
    "path": "tachyon/c/math/polynomials/univariate/bn254_univariate_evaluation_domain_unittest.cc",
    "content": "#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_evaluation_domain.h\"\n\n#include <utility>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_dense_polynomial_type_traits.h\"\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_evaluation_domain_type_traits.h\"\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_evaluations_type_traits.h\"\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_rational_evaluations_type_traits.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluation_domain_factory.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\nconstexpr size_t kDegree = 5;\n\nclass UnivariateEvaluationDomainTest : public FiniteFieldTest<bn254::Fr> {\n public:\n  using Domain = UnivariateEvaluationDomain<bn254::Fr, c::math::kMaxDegree>;\n  using RationalEvals =\n      UnivariateEvaluations<RationalField<bn254::Fr>, c::math::kMaxDegree>;\n\n  void SetUp() override {\n    domain_ = tachyon_bn254_univariate_evaluation_domain_create(kDegree);\n  }\n\n  void TearDown() override {\n    tachyon_bn254_univariate_evaluation_domain_destroy(domain_);\n  }\n\n protected:\n  tachyon_bn254_univariate_evaluation_domain* domain_;\n};\n\n}  // namespace\n\nTEST_F(UnivariateEvaluationDomainTest, EmptyEvals) {\n  Domain::Evals cpp_evals =\n      c::base::native_cast(domain_)->Zero<Domain::Evals>();\n  tachyon_bn254_univariate_evaluations* evals =\n      tachyon_bn254_univariate_evaluation_domain_empty_evals(domain_);\n  EXPECT_EQ(cpp_evals, c::base::native_cast(*evals));\n  tachyon_bn254_univariate_evaluations_destroy(evals);\n}\n\nTEST_F(UnivariateEvaluationDomainTest, EmptyPoly) {\n  Domain::DensePoly cpp_poly =\n      c::base::native_cast(domain_)->Zero<Domain::DensePoly>();\n  tachyon_bn254_univariate_dense_polynomial* poly =\n      tachyon_bn254_univariate_evaluation_domain_empty_poly(domain_);\n  EXPECT_EQ(cpp_poly, c::base::native_cast(*poly));\n  tachyon_bn254_univariate_dense_polynomial_destroy(poly);\n}\n\nTEST_F(UnivariateEvaluationDomainTest, EmptyRationalEvals) {\n  RationalEvals cpp_evals =\n      c::base::native_cast(domain_)->Zero<RationalEvals>();\n  tachyon_bn254_univariate_rational_evaluations* evals =\n      tachyon_bn254_univariate_evaluation_domain_empty_rational_evals(domain_);\n  EXPECT_EQ(cpp_evals, c::base::native_cast(*evals));\n  tachyon_bn254_univariate_rational_evaluations_destroy(evals);\n}\n\nTEST_F(UnivariateEvaluationDomainTest, FFT) {\n  Domain::DensePoly poly = Domain::DensePoly::Random(kDegree);\n  tachyon_bn254_univariate_evaluations* evals =\n      tachyon_bn254_univariate_evaluation_domain_fft(domain_,\n                                                     c::base::c_cast(&poly));\n  EXPECT_EQ(Domain::Create(kDegree + 1)->FFT(std::move(poly)),\n            c::base::native_cast(*evals));\n  tachyon_bn254_univariate_evaluations_destroy(evals);\n}\n\nTEST_F(UnivariateEvaluationDomainTest, IFFT) {\n  Domain::Evals evals = Domain::Evals::Random(kDegree);\n  tachyon_bn254_univariate_dense_polynomial* poly =\n      tachyon_bn254_univariate_evaluation_domain_ifft(domain_,\n                                                      c::base::c_cast(&evals));\n  EXPECT_EQ(Domain::Create(kDegree + 1)->IFFT(std::move(evals)),\n            c::base::native_cast(*poly));\n  tachyon_bn254_univariate_dense_polynomial_destroy(poly);\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/c/math/polynomials/univariate/bn254_univariate_evaluations.cc",
    "content": "#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_evaluations.h\"\n\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/fr_type_traits.h\"\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_evaluations_type_traits.h\"\n\nusing namespace tachyon;\n\nusing Evals = math::UnivariateEvaluations<math::bn254::Fr, c::math::kMaxDegree>;\n\ntachyon_bn254_univariate_evaluations*\ntachyon_bn254_univariate_evaluations_create() {\n  return c::base::c_cast(new Evals);\n}\n\ntachyon_bn254_univariate_evaluations*\ntachyon_bn254_univariate_evaluations_clone(\n    const tachyon_bn254_univariate_evaluations* evals) {\n  Evals* cloned_evals = new Evals(*c::base::native_cast(evals));\n  return c::base::c_cast(cloned_evals);\n}\n\nvoid tachyon_bn254_univariate_evaluations_destroy(\n    tachyon_bn254_univariate_evaluations* evals) {\n  delete c::base::native_cast(evals);\n}\n\nsize_t tachyon_bn254_univariate_evaluations_len(\n    const tachyon_bn254_univariate_evaluations* evals) {\n  return c::base::native_cast(evals)->NumElements();\n}\n\nvoid tachyon_bn254_univariate_evaluations_set_value(\n    tachyon_bn254_univariate_evaluations* evals, size_t i,\n    const tachyon_bn254_fr* value) {\n  // NOTE(chokobole): Boundary check is the responsibility of API callers.\n  c::base::native_cast(*evals).at(i) = c::base::native_cast(*value);\n}\n"
  },
  {
    "path": "tachyon/c/math/polynomials/univariate/bn254_univariate_evaluations.h",
    "content": "/**\n * @file bn254_univariate_evaluations.h\n * @brief Univariate Evaluations for BN254 Curve.\n *\n * This header file defines the structure and API for managing univariate\n * evaluations over the BN254 curve. It includes functions for creating,\n * cloning, destroying, and manipulating evaluations, as well as setting and\n * retrieving values within these structures.\n */\n#ifndef TACHYON_C_MATH_POLYNOMIALS_UNIVARIATE_BN254_UNIVARIATE_EVALUATIONS_H_\n#define TACHYON_C_MATH_POLYNOMIALS_UNIVARIATE_BN254_UNIVARIATE_EVALUATIONS_H_\n\n#include <stddef.h>\n\n#include \"tachyon/c/export.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/fr.h\"\n\n/**\n * @struct tachyon_bn254_univariate_evaluations\n * @brief Represents a collection of univariate polynomial evaluations over the\n * BN254 curve.\n */\nstruct tachyon_bn254_univariate_evaluations {};\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Creates a new univariate evaluations structure.\n *\n * Allocates and initializes a new structure for storing polynomial evaluations.\n *\n * @return A pointer to the newly created evaluations structure.\n */\nTACHYON_C_EXPORT tachyon_bn254_univariate_evaluations*\ntachyon_bn254_univariate_evaluations_create();\n\n/**\n * @brief Clones an existing univariate evaluations structure.\n *\n * Creates a deep copy of the given evaluations structure.\n *\n * @param evals A const pointer to the evaluations structure to clone.\n * @return A pointer to the cloned evaluations structure.\n */\nTACHYON_C_EXPORT tachyon_bn254_univariate_evaluations*\ntachyon_bn254_univariate_evaluations_clone(\n    const tachyon_bn254_univariate_evaluations* evals);\n\n/**\n * @brief Destroys a univariate evaluations structure.\n *\n * Frees the memory allocated for an evaluations structure.\n *\n * @param evals A pointer to the evaluations structure to destroy.\n */\nTACHYON_C_EXPORT void tachyon_bn254_univariate_evaluations_destroy(\n    tachyon_bn254_univariate_evaluations* evals);\n\n/**\n * @brief Retrieves the length of the univariate evaluations structure.\n *\n * @param evals A const pointer to the evaluations structure.\n * @return The number of evaluations stored in the structure.\n */\nTACHYON_C_EXPORT size_t tachyon_bn254_univariate_evaluations_len(\n    const tachyon_bn254_univariate_evaluations* evals);\n\n/**\n * @brief Sets a value in the univariate evaluations structure.\n *\n * @param evals A pointer to the evaluations structure.\n * @param i Index at which to set the value.\n * @param value A const pointer to the value to set at index i.\n */\nTACHYON_C_EXPORT void tachyon_bn254_univariate_evaluations_set_value(\n    tachyon_bn254_univariate_evaluations* evals, size_t i,\n    const tachyon_bn254_fr* value);\n\n#ifdef __cplusplus\n}  // extern \"C\"\n#endif\n\n#endif  // TACHYON_C_MATH_POLYNOMIALS_UNIVARIATE_BN254_UNIVARIATE_EVALUATIONS_H_\n"
  },
  {
    "path": "tachyon/c/math/polynomials/univariate/bn254_univariate_evaluations_type_traits.h",
    "content": "#ifndef TACHYON_C_MATH_POLYNOMIALS_UNIVARIATE_BN254_UNIVARIATE_EVALUATIONS_TYPE_TRAITS_H_\n#define TACHYON_C_MATH_POLYNOMIALS_UNIVARIATE_BN254_UNIVARIATE_EVALUATIONS_TYPE_TRAITS_H_\n\n#include \"tachyon/c/base/type_traits_forward.h\"\n#include \"tachyon/c/math/polynomials/constants.h\"\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_evaluations.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/fr.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluations.h\"\n\nnamespace tachyon::c::base {\n\ntemplate <>\nstruct TypeTraits<tachyon::math::UnivariateEvaluations<tachyon::math::bn254::Fr,\n                                                       math::kMaxDegree>> {\n  using CType = tachyon_bn254_univariate_evaluations;\n};\n\ntemplate <>\nstruct TypeTraits<tachyon_bn254_univariate_evaluations> {\n  using NativeType =\n      tachyon::math::UnivariateEvaluations<tachyon::math::bn254::Fr,\n                                           math::kMaxDegree>;\n};\n\n}  // namespace tachyon::c::base\n\n#endif  // TACHYON_C_MATH_POLYNOMIALS_UNIVARIATE_BN254_UNIVARIATE_EVALUATIONS_TYPE_TRAITS_H_\n"
  },
  {
    "path": "tachyon/c/math/polynomials/univariate/bn254_univariate_evaluations_unittest.cc",
    "content": "#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_evaluations.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/fr_type_traits.h\"\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_evaluations_type_traits.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\nconstexpr size_t kDegree = 5;\n\nclass UnivariateEvaluationsTest : public FiniteFieldTest<bn254::Fr> {\n public:\n  using Evals = UnivariateEvaluations<bn254::Fr, c::math::kMaxDegree>;\n\n  void SetUp() override {\n    Evals* cpp_evals = new Evals(Evals::Random(kDegree));\n    evals_ = c::base::c_cast(cpp_evals);\n  }\n\n  void TearDown() override {\n    tachyon_bn254_univariate_evaluations_destroy(evals_);\n  }\n\n protected:\n  tachyon_bn254_univariate_evaluations* evals_;\n};\n\n}  // namespace\n\nTEST_F(UnivariateEvaluationsTest, Clone) {\n  tachyon_bn254_univariate_evaluations* evals_clone =\n      tachyon_bn254_univariate_evaluations_clone(evals_);\n  // NOTE(chokobole): It's safe to access since we created |kDegree| |evals_|.\n  c::base::native_cast(*evals_).at(0) += bn254::Fr::One();\n  EXPECT_NE((c::base::native_cast(*evals_))[0],\n            (c::base::native_cast(*evals_clone))[0]);\n  tachyon_bn254_univariate_evaluations_destroy(evals_clone);\n}\n\nTEST_F(UnivariateEvaluationsTest, Len) {\n  EXPECT_EQ(tachyon_bn254_univariate_evaluations_len(evals_), kDegree + 1);\n}\n\nTEST_F(UnivariateEvaluationsTest, SetValue) {\n  bn254::Fr cpp_value = bn254::Fr::Random();\n  const tachyon_bn254_fr& value = c::base::c_cast(cpp_value);\n  tachyon_bn254_univariate_evaluations_set_value(evals_, 0, &value);\n  // NOTE(chokobole): It's safe to access since we created |kDegree| |evals_|.\n  EXPECT_EQ(c::base::native_cast(*evals_)[0], cpp_value);\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/c/math/polynomials/univariate/bn254_univariate_rational_evaluations.cc",
    "content": "#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_rational_evaluations.h\"\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/fr_type_traits.h\"\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_evaluations_type_traits.h\"\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_rational_evaluations_type_traits.h\"\n\nusing namespace tachyon;\n\nusing Evals = math::UnivariateEvaluations<math::bn254::Fr, c::math::kMaxDegree>;\nusing RationalEvals =\n    math::UnivariateEvaluations<math::RationalField<math::bn254::Fr>,\n                                c::math::kMaxDegree>;\n\ntachyon_bn254_univariate_rational_evaluations*\ntachyon_bn254_univariate_rational_evaluations_create() {\n  return c::base::c_cast(new RationalEvals);\n}\n\ntachyon_bn254_univariate_rational_evaluations*\ntachyon_bn254_univariate_rational_evaluations_clone(\n    const tachyon_bn254_univariate_rational_evaluations* evals) {\n  RationalEvals* cloned_evals = new RationalEvals(*c::base::native_cast(evals));\n  return c::base::c_cast(cloned_evals);\n}\n\nvoid tachyon_bn254_univariate_rational_evaluations_destroy(\n    tachyon_bn254_univariate_rational_evaluations* evals) {\n  delete c::base::native_cast(evals);\n}\n\nsize_t tachyon_bn254_univariate_rational_evaluations_len(\n    const tachyon_bn254_univariate_rational_evaluations* evals) {\n  return c::base::native_cast(evals)->NumElements();\n}\n\nvoid tachyon_bn254_univariate_rational_evaluations_set_zero(\n    tachyon_bn254_univariate_rational_evaluations* evals, size_t i) {\n  // NOTE(chokobole): Boundary check is the responsibility of API callers.\n  c::base::native_cast(*evals).at(i) =\n      math::RationalField<math::bn254::Fr>::Zero();\n}\n\nvoid tachyon_bn254_univariate_rational_evaluations_set_trivial(\n    tachyon_bn254_univariate_rational_evaluations* evals, size_t i,\n    const tachyon_bn254_fr* numerator) {\n  // NOTE(chokobole): Boundary check is the responsibility of API callers.\n  c::base::native_cast(*evals).at(i) =\n      math::RationalField<math::bn254::Fr>(c::base::native_cast(*numerator));\n}\n\nvoid tachyon_bn254_univariate_rational_evaluations_set_rational(\n    tachyon_bn254_univariate_rational_evaluations* evals, size_t i,\n    const tachyon_bn254_fr* numerator, const tachyon_bn254_fr* denominator) {\n  // NOTE(chokobole): Boundary check is the responsibility of API callers.\n  c::base::native_cast(*evals).at(i) = {\n      c::base::native_cast(*numerator),\n      c::base::native_cast(*denominator),\n  };\n}\n\nvoid tachyon_bn254_univariate_rational_evaluations_evaluate(\n    const tachyon_bn254_univariate_rational_evaluations* rational_evals,\n    size_t i, tachyon_bn254_fr* value) {\n  const RationalEvals& cpp_rational_eval =\n      c::base::native_cast(*rational_evals);\n  *value = c::base::c_cast(cpp_rational_eval[i].Evaluate());\n}\n\ntachyon_bn254_univariate_evaluations*\ntachyon_bn254_univariate_rational_evaluations_batch_evaluate(\n    const tachyon_bn254_univariate_rational_evaluations* rational_evals) {\n  const RationalEvals& cpp_rational_eval =\n      c::base::native_cast(*rational_evals);\n  std::vector<math::bn254::Fr> cpp_values(cpp_rational_eval.NumElements());\n  CHECK(math::RationalField<math::bn254::Fr>::BatchEvaluate(\n      cpp_rational_eval.evaluations(), &cpp_values));\n  Evals* cpp_evals = new Evals(Evals(std::move(cpp_values)));\n  return c::base::c_cast(cpp_evals);\n}\n"
  },
  {
    "path": "tachyon/c/math/polynomials/univariate/bn254_univariate_rational_evaluations.h",
    "content": "/**\n * @file bn254_univariate_rational_evaluations.h\n * @brief Univariate Rational Evaluations for BN254 Curve.\n *\n * This header file defines the structures and API for univariate rational\n * evaluations over the BN254 curve. It includes functionalities to create,\n * clone, and destroy rational evaluations structures, as well as setting values\n * within these structures and performing batch evaluations.\n */\n#ifndef TACHYON_C_MATH_POLYNOMIALS_UNIVARIATE_BN254_UNIVARIATE_RATIONAL_EVALUATIONS_H_\n#define TACHYON_C_MATH_POLYNOMIALS_UNIVARIATE_BN254_UNIVARIATE_RATIONAL_EVALUATIONS_H_\n\n#include <stddef.h>\n\n#include \"tachyon/c/export.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/fr.h\"\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_evaluations.h\"\n\n/**\n * @struct tachyon_bn254_univariate_rational_evaluations\n * @brief Represents a collection of univariate rational polynomial evaluations\n * over the BN254 curve.\n */\nstruct tachyon_bn254_univariate_rational_evaluations {};\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Creates a new univariate rational evaluations structure.\n *\n * Allocates and initializes a new structure for storing rational polynomial\n * evaluations.\n *\n * @return A pointer to the newly created rational evaluations structure.\n */\n\nTACHYON_C_EXPORT tachyon_bn254_univariate_rational_evaluations*\ntachyon_bn254_univariate_rational_evaluations_create();\n\n/**\n * @brief Clones an existing univariate rational evaluations structure.\n *\n * Creates a deep copy of the given rational evaluations structure.\n *\n * @param evals A const pointer to the rational evaluations structure to clone.\n * @return A pointer to the cloned rational evaluations structure.\n */\nTACHYON_C_EXPORT tachyon_bn254_univariate_rational_evaluations*\ntachyon_bn254_univariate_rational_evaluations_clone(\n    const tachyon_bn254_univariate_rational_evaluations* evals);\n\n/**\n * @brief Destroys a univariate rational evaluations structure.\n *\n * Frees the memory allocated for a rational evaluations structure.\n *\n * @param evals A pointer to the rational evaluations structure to destroy.\n */\nTACHYON_C_EXPORT void tachyon_bn254_univariate_rational_evaluations_destroy(\n    tachyon_bn254_univariate_rational_evaluations* evals);\n\n/**\n * @brief Retrieves the length of the univariate rational evaluations structure.\n *\n * @param evals A const pointer to the rational evaluations structure.\n * @return The number of rational evaluations stored in the structure.\n */\nTACHYON_C_EXPORT size_t tachyon_bn254_univariate_rational_evaluations_len(\n    const tachyon_bn254_univariate_rational_evaluations* evals);\n\n/**\n * @brief Sets a specific index in the rational evaluations structure to zero.\n *\n * @param evals A pointer to the rational evaluations structure.\n * @param i Index at which to set the value to zero.\n */\nTACHYON_C_EXPORT void tachyon_bn254_univariate_rational_evaluations_set_zero(\n    tachyon_bn254_univariate_rational_evaluations* evals, size_t i);\n\n/**\n * @brief Sets a specific index in the rational evaluations structure to a\n * trivial value (numerator only).\n *\n * @param evals A pointer to the rational evaluations structure.\n * @param i Index at which to set the value.\n * @param numerator A const pointer to the numerator value.\n */\nTACHYON_C_EXPORT void tachyon_bn254_univariate_rational_evaluations_set_trivial(\n    tachyon_bn254_univariate_rational_evaluations* evals, size_t i,\n    const tachyon_bn254_fr* numerator);\n\n/**\n * @brief Sets a specific index in the rational evaluations structure to a\n * rational value (numerator and denominator).\n *\n * @param evals A pointer to the rational evaluations structure.\n * @param i Index at which to set the rational value.\n * @param numerator A const pointer to the numerator value.\n * @param denominator A const pointer to the denominator value.\n */\nTACHYON_C_EXPORT void\ntachyon_bn254_univariate_rational_evaluations_set_rational(\n    tachyon_bn254_univariate_rational_evaluations* evals, size_t i,\n    const tachyon_bn254_fr* numerator, const tachyon_bn254_fr* denominator);\n\n/**\n * @brief Evaluates the rational evaluations structure at the given index.\n *\n * @param evals A const pointer to the rational evaluations structure to\n * evaluate.\n * @param i Index at which to perform the evaluation.\n * @param value A pointer to the value resulting from the evaluation.\n */\nTACHYON_C_EXPORT void tachyon_bn254_univariate_rational_evaluations_evaluate(\n    const tachyon_bn254_univariate_rational_evaluations* evals, size_t i,\n    tachyon_bn254_fr* value);\n\n/**\n * @brief Performs a batch evaluation on the rational evaluations structure.\n *\n * @param evals A const pointer to the rational evaluations structure to\n * evaluate.\n * @return A pointer to the univariate evaluations resulting from the batch\n * evaluation.\n */\nTACHYON_C_EXPORT tachyon_bn254_univariate_evaluations*\ntachyon_bn254_univariate_rational_evaluations_batch_evaluate(\n    const tachyon_bn254_univariate_rational_evaluations* evals);\n\n#ifdef __cplusplus\n}  // extern \"C\"\n#endif\n\n#endif  // TACHYON_C_MATH_POLYNOMIALS_UNIVARIATE_BN254_UNIVARIATE_RATIONAL_EVALUATIONS_H_\n"
  },
  {
    "path": "tachyon/c/math/polynomials/univariate/bn254_univariate_rational_evaluations_type_traits.h",
    "content": "#ifndef TACHYON_C_MATH_POLYNOMIALS_UNIVARIATE_BN254_UNIVARIATE_RATIONAL_EVALUATIONS_TYPE_TRAITS_H_\n#define TACHYON_C_MATH_POLYNOMIALS_UNIVARIATE_BN254_UNIVARIATE_RATIONAL_EVALUATIONS_TYPE_TRAITS_H_\n\n#include \"tachyon/c/base/type_traits_forward.h\"\n#include \"tachyon/c/math/polynomials/constants.h\"\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_rational_evaluations.h\"\n#include \"tachyon/math/base/rational_field.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/fr.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluations.h\"\n\nnamespace tachyon::c::base {\n\ntemplate <>\nstruct TypeTraits<tachyon::math::UnivariateEvaluations<\n    tachyon::math::RationalField<tachyon::math::bn254::Fr>, math::kMaxDegree>> {\n  using CType = tachyon_bn254_univariate_rational_evaluations;\n};\n\ntemplate <>\nstruct TypeTraits<tachyon_bn254_univariate_rational_evaluations> {\n  using NativeType = tachyon::math::UnivariateEvaluations<\n      tachyon::math::RationalField<tachyon::math::bn254::Fr>, math::kMaxDegree>;\n};\n\n}  // namespace tachyon::c::base\n\n#endif  // TACHYON_C_MATH_POLYNOMIALS_UNIVARIATE_BN254_UNIVARIATE_RATIONAL_EVALUATIONS_TYPE_TRAITS_H_\n"
  },
  {
    "path": "tachyon/c/math/polynomials/univariate/bn254_univariate_rational_evaluations_unittest.cc",
    "content": "#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_rational_evaluations.h\"\n\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/fr_type_traits.h\"\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_evaluations_type_traits.h\"\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_rational_evaluations_type_traits.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\nconstexpr size_t kDegree = 5;\n\nclass UnivariateRationalEvaluationsTest : public FiniteFieldTest<bn254::Fr> {\n public:\n  using Evals = UnivariateEvaluations<bn254::Fr, c::math::kMaxDegree>;\n  using RationalEvals =\n      UnivariateEvaluations<RationalField<bn254::Fr>, c::math::kMaxDegree>;\n\n  void SetUp() override {\n    RationalEvals* cpp_evals =\n        new RationalEvals(RationalEvals::Random(kDegree));\n    evals_ = c::base::c_cast(cpp_evals);\n  }\n\n  void TearDown() override {\n    tachyon_bn254_univariate_rational_evaluations_destroy(evals_);\n  }\n\n protected:\n  tachyon_bn254_univariate_rational_evaluations* evals_;\n};\n\n}  // namespace\n\nTEST_F(UnivariateRationalEvaluationsTest, Clone) {\n  tachyon_bn254_univariate_rational_evaluations* evals_clone =\n      tachyon_bn254_univariate_rational_evaluations_clone(evals_);\n  // NOTE(chokobole): It's safe to access since we created |kDegree| |evals_|.\n  c::base::native_cast(*evals_).at(0) += RationalField<bn254::Fr>::One();\n  EXPECT_NE((c::base::native_cast(*evals_))[0],\n            (c::base::native_cast(*evals_clone))[0]);\n  tachyon_bn254_univariate_rational_evaluations_destroy(evals_clone);\n}\n\nTEST_F(UnivariateRationalEvaluationsTest, Len) {\n  EXPECT_EQ(tachyon_bn254_univariate_rational_evaluations_len(evals_),\n            kDegree + 1);\n}\n\nTEST_F(UnivariateRationalEvaluationsTest, SetZero) {\n  tachyon_bn254_univariate_rational_evaluations_set_zero(evals_, 0);\n  EXPECT_TRUE(c::base::native_cast(*evals_)[0].IsZero());\n}\n\nTEST_F(UnivariateRationalEvaluationsTest, SetTrivial) {\n  RationalField<bn254::Fr> expected =\n      RationalField<bn254::Fr>(bn254::Fr::Random());\n  const tachyon_bn254_fr& numerator = c::base::c_cast(expected.numerator());\n  tachyon_bn254_univariate_rational_evaluations_set_trivial(evals_, 0,\n                                                            &numerator);\n  EXPECT_EQ(c::base::native_cast(*evals_)[0], expected);\n}\n\nTEST_F(UnivariateRationalEvaluationsTest, SetRational) {\n  RationalField<bn254::Fr> expected = RationalField<bn254::Fr>::Random();\n  const tachyon_bn254_fr& numerator = c::base::c_cast(expected.numerator());\n  const tachyon_bn254_fr& denominator = c::base::c_cast(expected.denominator());\n  tachyon_bn254_univariate_rational_evaluations_set_rational(\n      evals_, 0, &numerator, &denominator);\n  EXPECT_EQ(c::base::native_cast(*evals_)[0], expected);\n}\n\nTEST_F(UnivariateRationalEvaluationsTest, BatchEvaluate) {\n  std::vector<RationalField<bn254::Fr>> rational_values = base::CreateVector(\n      kDegree + 1, []() { return RationalField<bn254::Fr>::Random(); });\n  for (size_t i = 0; i < rational_values.size(); ++i) {\n    const tachyon_bn254_fr& numerator =\n        c::base::c_cast(rational_values[i].numerator());\n    const tachyon_bn254_fr& denominator =\n        c::base::c_cast(rational_values[i].denominator());\n    tachyon_bn254_univariate_rational_evaluations_set_rational(\n        evals_, i, &numerator, &denominator);\n  }\n  tachyon_bn254_univariate_evaluations* evaluated =\n      tachyon_bn254_univariate_rational_evaluations_batch_evaluate(evals_);\n  std::vector<bn254::Fr> values;\n  values.resize(rational_values.size());\n  CHECK(RationalField<bn254::Fr>::BatchEvaluate(rational_values, &values));\n  EXPECT_EQ(c::base::native_cast(*evaluated).evaluations(), values);\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/c/version.bzl",
    "content": "# See https://semver.org/\nVERSION_MAJOR = 0\nVERSION_MINOR = 4\nVERSION_PATCH = 0\nVERSION_PRERELEASE = \"\"\n\nVERSION = \".\".join([\n    str(VERSION_MAJOR),\n    str(VERSION_MINOR),\n    str(VERSION_PATCH),\n])\n"
  },
  {
    "path": "tachyon/c/version.cc",
    "content": "#include \"tachyon/c/version.h\"\n\nuint32_t tachyon_get_runtime_version() { return TACHYON_C_VERSION; }\n\nconst char* tachyon_get_runtime_version_str() { return TACHYON_C_VERSION_STR; }\n\nconst char* tachyon_get_runtime_full_version_str() {\n  return TACHYON_C_VERSION_FULL_STR;\n}\n"
  },
  {
    "path": "tachyon/c/version.h",
    "content": "#ifndef TACHYON_C_VERSION_H_\n#define TACHYON_C_VERSION_H_\n\n#include <stdint.h>\n\n#include \"tachyon/c/export.h\"\n#include \"tachyon/c/version_generated.h\"\n\n/**\n * @file version.h\n * @brief Version information for tachyon.\n *\n * This header file contains functions to get the runtime version information of\n * tachyon.\n *\n * @example version.cc\n */\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Returns the runtime version number of tachyon.\n * @return Runtime version number as a uint32_t. e.g., 10203.\n */\nTACHYON_C_EXPORT uint32_t tachyon_get_runtime_version();\n\n/**\n * @brief Returns the runtime version string of tachyon.\n * @return Runtime version string as a std::string_view. e.g., \"1.2.3\".\n */\nTACHYON_C_EXPORT const char* tachyon_get_runtime_version_str();\n\n/**\n * @brief Returns the runtime full version string of tachyon.\n * @return Runtime full version string as a std::string_view. e.g.,\n * \"1.2.3-<commit sha256>\".\n */\nTACHYON_C_EXPORT const char* tachyon_get_runtime_full_version_str();\n\n#ifdef __cplusplus\n}  // extern \"C\"\n#endif\n\n#endif  // TACHYON_C_VERSION_H_\n"
  },
  {
    "path": "tachyon/c/version_unittest.cc",
    "content": "#include \"tachyon/c/version.h\"\n\n#include \"absl/strings/substitute.h\"\n#include \"gtest/gtest.h\"\n\nTEST(VersionTest, CompileTimeVersionTest) {\n  EXPECT_EQ(absl::Substitute(\"$0.$1.$2\", TACHYON_C_VERSION_MAJOR,\n                             TACHYON_C_VERSION_MINOR, TACHYON_C_VERSION_PATCH),\n            TACHYON_C_VERSION_STR);\n  EXPECT_EQ(TACHYON_C_VERSION_MAJOR * 10000 + TACHYON_C_VERSION_MINOR * 100 +\n                TACHYON_C_VERSION_PATCH,\n            TACHYON_C_VERSION);\n}\n\nTEST(VersionTest, RunTimeVersionTest) {\n  EXPECT_EQ(TACHYON_C_VERSION, tachyon_get_runtime_version());\n  EXPECT_EQ(TACHYON_C_VERSION_STR,\n            std::string_view(tachyon_get_runtime_version_str()));\n  EXPECT_EQ(TACHYON_C_VERSION_FULL_STR,\n            std::string_view(tachyon_get_runtime_full_version_str()));\n}\n"
  },
  {
    "path": "tachyon/c/zk/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n    name = \"zk_hdrs\",\n    srcs = [\n        \"//tachyon/c/zk/air:air_hdrs\",\n        \"//tachyon/c/zk/base:base_hdrs\",\n        \"//tachyon/c/zk/plonk:plonk_hdrs\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"zk\",\n    deps = [\n        \"//tachyon/c/zk/air\",\n        \"//tachyon/c/zk/base\",\n        \"//tachyon/c/zk/plonk\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/c/zk/air/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n    name = \"air_hdrs\",\n    srcs = [\"//tachyon/c/zk/air/sp1:sp1_hdrs\"],\n)\n\ntachyon_cc_library(\n    name = \"air\",\n    deps = [\"//tachyon/c/zk/air/sp1\"],\n)\n"
  },
  {
    "path": "tachyon/c/zk/air/sp1/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n    name = \"sp1_hdrs\",\n    srcs = [\n        \"baby_bear_poseidon2_commitment_vec.h\",\n        \"baby_bear_poseidon2_constants.h\",\n        \"baby_bear_poseidon2_domains.h\",\n        \"baby_bear_poseidon2_duplex_challenger.h\",\n        \"baby_bear_poseidon2_field_merkle_tree.h\",\n        \"baby_bear_poseidon2_field_merkle_tree_vec.h\",\n        \"baby_bear_poseidon2_fri_proof.h\",\n        \"baby_bear_poseidon2_lde_vec.h\",\n        \"baby_bear_poseidon2_opened_values.h\",\n        \"baby_bear_poseidon2_opening_points.h\",\n        \"baby_bear_poseidon2_two_adic_fri.h\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"baby_bear_poseidon2_constants\",\n    hdrs = [\"baby_bear_poseidon2_constants.h\"],\n)\n\ntachyon_cc_library(\n    name = \"baby_bear_poseidon2_commitment_vec\",\n    srcs = [\"baby_bear_poseidon2_commitment_vec.cc\"],\n    hdrs = [\n        \"baby_bear_poseidon2_commitment_vec.h\",\n        \"baby_bear_poseidon2_commitment_vec_type_traits.h\",\n    ],\n    deps = [\n        \":baby_bear_poseidon2_constants\",\n        \"//tachyon/c:export\",\n        \"//tachyon/c/base:type_traits_forward\",\n        \"//tachyon/c/math/finite_fields/baby_bear\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"baby_bear_poseidon2_domains\",\n    srcs = [\"baby_bear_poseidon2_domains.cc\"],\n    hdrs = [\n        \"baby_bear_poseidon2_domains.h\",\n        \"baby_bear_poseidon2_domains_type_traits.h\",\n    ],\n    deps = [\n        \"//tachyon/c:export\",\n        \"//tachyon/c/base:type_traits_forward\",\n        \"//tachyon/c/math/finite_fields/baby_bear\",\n        \"//tachyon/crypto/commitments/fri:two_adic_multiplicative_coset\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"baby_bear_poseidon2_duplex_challenger\",\n    srcs = [\"baby_bear_poseidon2_duplex_challenger.cc\"],\n    hdrs = [\n        \"baby_bear_poseidon2_duplex_challenger.h\",\n        \"baby_bear_poseidon2_duplex_challenger_type_traits.h\",\n    ],\n    deps = [\n        \":baby_bear_poseidon2_constants\",\n        \":baby_bear_poseidon2_hintable\",\n        \"//tachyon/base:auto_reset\",\n        \"//tachyon/base/buffer\",\n        \"//tachyon/c:export\",\n        \"//tachyon/c/base:type_traits_forward\",\n        \"//tachyon/c/math/finite_fields/baby_bear\",\n        \"//tachyon/crypto/challenger:duplex_challenger\",\n        \"//tachyon/crypto/hashes/sponge/poseidon2\",\n        \"//tachyon/crypto/hashes/sponge/poseidon2:poseidon2_params\",\n        \"//tachyon/crypto/hashes/sponge/poseidon2/param_traits:poseidon2_baby_bear\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"baby_bear_poseidon2_fields\",\n    hdrs = [\"baby_bear_poseidon2_fields.h\"],\n    deps = [\n        \"//tachyon/math/finite_fields/baby_bear:baby_bear4\",\n        \"//tachyon/math/finite_fields/baby_bear:packed_baby_bear4\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"baby_bear_poseidon2_field_merkle_tree\",\n    srcs = [\"baby_bear_poseidon2_field_merkle_tree.cc\"],\n    hdrs = [\n        \"baby_bear_poseidon2_field_merkle_tree.h\",\n        \"baby_bear_poseidon2_field_merkle_tree_type_traits.h\",\n    ],\n    deps = [\n        \":baby_bear_poseidon2_constants\",\n        \"//tachyon/base:auto_reset\",\n        \"//tachyon/base/buffer\",\n        \"//tachyon/c:export\",\n        \"//tachyon/c/base:type_traits_forward\",\n        \"//tachyon/crypto/commitments/merkle_tree/field_merkle_tree\",\n        \"//tachyon/math/finite_fields/baby_bear\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"baby_bear_poseidon2_field_merkle_tree_vec\",\n    srcs = [\"baby_bear_poseidon2_field_merkle_tree_vec.cc\"],\n    hdrs = [\n        \"baby_bear_poseidon2_field_merkle_tree_vec.h\",\n        \"baby_bear_poseidon2_field_merkle_tree_vec_type_traits.h\",\n    ],\n    deps = [\n        \":baby_bear_poseidon2_constants\",\n        \":baby_bear_poseidon2_field_merkle_tree\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/c:export\",\n        \"//tachyon/c/base:type_traits_forward\",\n        \"//tachyon/crypto/commitments/merkle_tree/field_merkle_tree\",\n        \"//tachyon/math/finite_fields/baby_bear\",\n        \"@com_google_absl//absl/debugging:leak_check\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"baby_bear_poseidon2_hintable\",\n    hdrs = [\"baby_bear_poseidon2_hintable.h\"],\n    deps = [\n        \":baby_bear_poseidon2_fields\",\n        \":block\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/crypto/challenger:duplex_challenger\",\n        \"//tachyon/crypto/commitments/fri:fri_proof\",\n        \"//tachyon/math/finite_fields/baby_bear\",\n        \"//tachyon/math/matrix:matrix_types\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"baby_bear_poseidon2_lde_vec\",\n    srcs = [\"baby_bear_poseidon2_lde_vec.cc\"],\n    hdrs = [\n        \"baby_bear_poseidon2_lde_vec.h\",\n        \"baby_bear_poseidon2_lde_vec_type_traits.h\",\n    ],\n    deps = [\n        \"//tachyon/c:export\",\n        \"//tachyon/c/base:type_traits_forward\",\n        \"//tachyon/c/math/finite_fields/baby_bear\",\n        \"//tachyon/math/matrix:matrix_types\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"baby_bear_poseidon2_opened_values\",\n    srcs = [\"baby_bear_poseidon2_opened_values.cc\"],\n    hdrs = [\n        \"baby_bear_poseidon2_opened_values.h\",\n        \"baby_bear_poseidon2_opened_values_type_traits.h\",\n    ],\n    deps = [\n        \"//tachyon/base:auto_reset\",\n        \"//tachyon/base/buffer\",\n        \"//tachyon/c:export\",\n        \"//tachyon/c/base:type_traits_forward\",\n        \"//tachyon/c/math/finite_fields/baby_bear:baby_bear4\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"baby_bear_poseidon2_opening_points\",\n    srcs = [\"baby_bear_poseidon2_opening_points.cc\"],\n    hdrs = [\n        \"baby_bear_poseidon2_opening_points.h\",\n        \"baby_bear_poseidon2_opening_points_type_traits.h\",\n    ],\n    deps = [\n        \"//tachyon/c:export\",\n        \"//tachyon/c/base:type_traits_forward\",\n        \"//tachyon/c/math/finite_fields/baby_bear:baby_bear4\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"baby_bear_poseidon2_two_adic_fri\",\n    srcs = [\n        \"baby_bear_poseidon2_fri_proof.cc\",\n        \"baby_bear_poseidon2_two_adic_fri.cc\",\n    ],\n    hdrs = [\n        \"baby_bear_poseidon2_fri_proof.h\",\n        \"baby_bear_poseidon2_fri_proof_type_traits.h\",\n        \"baby_bear_poseidon2_two_adic_fri.h\",\n        \"baby_bear_poseidon2_two_adic_fri_type_traits.h\",\n    ],\n    deps = [\n        \":baby_bear_poseidon2_commitment_vec\",\n        \":baby_bear_poseidon2_constants\",\n        \":baby_bear_poseidon2_domains\",\n        \":baby_bear_poseidon2_duplex_challenger\",\n        \":baby_bear_poseidon2_field_merkle_tree\",\n        \":baby_bear_poseidon2_field_merkle_tree_vec\",\n        \":baby_bear_poseidon2_fields\",\n        \":baby_bear_poseidon2_hintable\",\n        \":baby_bear_poseidon2_lde_vec\",\n        \":baby_bear_poseidon2_opened_values\",\n        \":baby_bear_poseidon2_opening_points\",\n        \"//tachyon/base:auto_reset\",\n        \"//tachyon/base/buffer\",\n        \"//tachyon/c:export\",\n        \"//tachyon/c/base:type_traits_forward\",\n        \"//tachyon/c/crypto/commitments/fri:two_adic_fri_impl\",\n        \"//tachyon/c/math/finite_fields/baby_bear\",\n        \"//tachyon/c/math/finite_fields/baby_bear:baby_bear4\",\n        \"//tachyon/c/math/matrix:baby_bear_row_major_matrix\",\n        \"//tachyon/crypto/challenger:duplex_challenger\",\n        \"//tachyon/crypto/commitments/fri:two_adic_fri\",\n        \"//tachyon/crypto/commitments/fri:two_adic_multiplicative_coset\",\n        \"//tachyon/crypto/commitments/merkle_tree/field_merkle_tree:extension_field_merkle_tree_mmcs\",\n        \"//tachyon/crypto/commitments/merkle_tree/field_merkle_tree:field_merkle_tree_mmcs\",\n        \"//tachyon/crypto/hashes/sponge:padding_free_sponge\",\n        \"//tachyon/crypto/hashes/sponge:truncated_permutation\",\n        \"//tachyon/crypto/hashes/sponge/poseidon2\",\n        \"//tachyon/crypto/hashes/sponge/poseidon2:poseidon2_params\",\n        \"//tachyon/crypto/hashes/sponge/poseidon2/param_traits:poseidon2_baby_bear\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"block\",\n    hdrs = [\"block.h\"],\n    deps = [\n        \"//tachyon/base/buffer:copyable\",\n        \"//tachyon/base/strings:string_util\",\n        \"//tachyon/base/types:always_false\",\n        \"//tachyon/math/finite_fields:finite_field_traits\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"sp1\",\n    deps = [\":baby_bear_poseidon2_two_adic_fri\"],\n)\n\ntachyon_cc_unittest(\n    name = \"sp1_unittests\",\n    srcs = [\n        \"baby_bear_poseidon2_duplex_challenger_unittest.cc\",\n        \"baby_bear_poseidon2_hintable_unittest.cc\",\n        \"baby_bear_poseidon2_opened_values_unittest.cc\",\n        \"baby_bear_poseidon2_two_adic_fri_unittest.cc\",\n        \"block_unittest.cc\",\n    ],\n    deps = [\n        \":baby_bear_poseidon2_hintable\",\n        \":sp1\",\n        \"//tachyon/base/buffer:vector_buffer\",\n        \"//tachyon/math/finite_fields/test:finite_field_test\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/c/zk/air/sp1/baby_bear_poseidon2_commitment_vec.cc",
    "content": "#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_commitment_vec.h\"\n\n#include <array>\n#include <vector>\n\n#include \"tachyon/c/math/finite_fields/baby_bear/baby_bear_type_traits.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_commitment_vec_type_traits.h\"\n\nusing namespace tachyon;\n\nusing CommitmentVec = std::vector<\n    std::array<math::BabyBear, TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_CHUNK>>;\n\ntachyon_sp1_baby_bear_poseidon2_commitment_vec*\ntachyon_sp1_baby_bear_poseidon2_commitment_vec_create(size_t rounds) {\n  return c::base::c_cast(new CommitmentVec(rounds));\n}\n\nvoid tachyon_sp1_baby_bear_poseidon2_commitment_vec_destroy(\n    tachyon_sp1_baby_bear_poseidon2_commitment_vec* commitment_vec) {\n  delete c::base::native_cast(commitment_vec);\n}\n\nvoid tachyon_sp1_baby_bear_poseidon2_commitment_vec_set(\n    tachyon_sp1_baby_bear_poseidon2_commitment_vec* commitment_vec,\n    size_t round, const tachyon_baby_bear* commitment) {\n  std::array<math::BabyBear, TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_CHUNK>&\n      native_commitment = c::base::native_cast(*commitment_vec)[round];\n  for (size_t i = 0; i < TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_CHUNK; ++i) {\n    native_commitment[i] = c::base::native_cast(commitment[i]);\n  }\n}\n"
  },
  {
    "path": "tachyon/c/zk/air/sp1/baby_bear_poseidon2_commitment_vec.h",
    "content": "/**\n * @file baby_bear_poseidon2_commitment_vec.h\n * @brief Defines the interface for the commitment vector used within the\n * SP1(BabyBear + Poseidon2) proof system.\n */\n\n#ifndef TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_COMMITMENT_VEC_H_\n#define TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_COMMITMENT_VEC_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include \"tachyon/c/export.h\"\n#include \"tachyon/c/math/finite_fields/baby_bear/baby_bear.h\"\n\nstruct tachyon_sp1_baby_bear_poseidon2_commitment_vec {};\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Creates a new commitment vector.\n *\n * @param rounds The number of rounds.\n * @return A pointer to the newly created commitment vector.\n */\nTACHYON_C_EXPORT\ntachyon_sp1_baby_bear_poseidon2_commitment_vec*\ntachyon_sp1_baby_bear_poseidon2_commitment_vec_create(size_t rounds);\n\n/**\n * @brief Destroys a commitment vector, freeing its resources.\n *\n * @param commitment_vec A pointer to the commitment vector to destroy.\n */\nTACHYON_C_EXPORT void tachyon_sp1_baby_bear_poseidon2_commitment_vec_destroy(\n    tachyon_sp1_baby_bear_poseidon2_commitment_vec* commitment_vec);\n\n/**\n * @brief Sets commitment.\n *\n * @param commitment_vec A pointer to the commitment vector.\n * @param round The round index of the point.\n * @param commitment A const pointer to the commitment.\n */\nTACHYON_C_EXPORT void tachyon_sp1_baby_bear_poseidon2_commitment_vec_set(\n    tachyon_sp1_baby_bear_poseidon2_commitment_vec* commitment_vec,\n    size_t round, const tachyon_baby_bear* commitment);\n\n#ifdef __cplusplus\n}  // extern \"C\"\n#endif\n\n#endif  // TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_COMMITMENT_VEC_H_\n"
  },
  {
    "path": "tachyon/c/zk/air/sp1/baby_bear_poseidon2_commitment_vec_type_traits.h",
    "content": "#ifndef TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_COMMITMENT_VEC_TYPE_TRAITS_H_\n#define TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_COMMITMENT_VEC_TYPE_TRAITS_H_\n\n#include <array>\n#include <vector>\n\n#include \"tachyon/c/base/type_traits_forward.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_commitment_vec.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_constants.h\"\n#include \"tachyon/math/finite_fields/baby_bear/baby_bear.h\"\n\nnamespace tachyon::c::base {\n\ntemplate <>\nstruct TypeTraits<std::vector<std::array<\n    tachyon::math::BabyBear, TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_CHUNK>>> {\n  using CType = tachyon_sp1_baby_bear_poseidon2_commitment_vec;\n};\n\ntemplate <>\nstruct TypeTraits<tachyon_sp1_baby_bear_poseidon2_commitment_vec> {\n  using NativeType =\n      std::vector<std::array<tachyon::math::BabyBear,\n                             TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_CHUNK>>;\n};\n\n}  // namespace tachyon::c::base\n\n#endif  // TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_COMMITMENT_VEC_TYPE_TRAITS_H_\n"
  },
  {
    "path": "tachyon/c/zk/air/sp1/baby_bear_poseidon2_constants.h",
    "content": "#ifndef TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_CONSTANTS_H_\n#define TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_CONSTANTS_H_\n\n#include <stdint.h>\n\n// Poseidon2 constants\n#define TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_WIDTH 16\n#define TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_ALPHA 7\n#define TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_FULL_ROUNDS 8\n#define TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_PARTIAL_ROUNDS 13\n\n#define TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_RATE 8\n#define TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_CHUNK 8\n#define TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_N 2\n\nconst uint32_t kRoundConstants[30][16] = {\n    {\n        UINT32_C(2110014213),\n        UINT32_C(3964964605),\n        UINT32_C(2190662774),\n        UINT32_C(2732996483),\n        UINT32_C(640767983),\n        UINT32_C(3403899136),\n        UINT32_C(1716033721),\n        UINT32_C(1606702601),\n        UINT32_C(3759873288),\n        UINT32_C(1466015491),\n        UINT32_C(1498308946),\n        UINT32_C(2844375094),\n        UINT32_C(3042463841),\n        UINT32_C(1969905919),\n        UINT32_C(4109944726),\n        UINT32_C(3925048366),\n    },\n    {\n        UINT32_C(3706859504),\n        UINT32_C(759122502),\n        UINT32_C(3167665446),\n        UINT32_C(1131812921),\n        UINT32_C(1080754908),\n        UINT32_C(4080114493),\n        UINT32_C(893583089),\n        UINT32_C(2019677373),\n        UINT32_C(3128604556),\n        UINT32_C(580640471),\n        UINT32_C(3277620260),\n        UINT32_C(842931656),\n        UINT32_C(548879852),\n        UINT32_C(3608554714),\n        UINT32_C(3575647916),\n        UINT32_C(81826002),\n    },\n    {\n        UINT32_C(4289086263),\n        UINT32_C(1563933798),\n        UINT32_C(1440025885),\n        UINT32_C(184445025),\n        UINT32_C(2598651360),\n        UINT32_C(1396647410),\n        UINT32_C(1575877922),\n        UINT32_C(3303853401),\n        UINT32_C(137125468),\n        UINT32_C(765010148),\n        UINT32_C(633675867),\n        UINT32_C(2037803363),\n        UINT32_C(2573389828),\n        UINT32_C(1895729703),\n        UINT32_C(541515871),\n        UINT32_C(1783382863),\n    },\n    {\n        UINT32_C(2641856484),\n        UINT32_C(3035743342),\n        UINT32_C(3672796326),\n        UINT32_C(245668751),\n        UINT32_C(2025460432),\n        UINT32_C(201609705),\n        UINT32_C(286217151),\n        UINT32_C(4093475563),\n        UINT32_C(2519572182),\n        UINT32_C(3080699870),\n        UINT32_C(2762001832),\n        UINT32_C(1244250808),\n        UINT32_C(606038199),\n        UINT32_C(3182740831),\n        UINT32_C(73007766),\n        UINT32_C(2572204153),\n    },\n    {\n        UINT32_C(1196780786),\n        UINT32_C(3447394443),\n        UINT32_C(747167305),\n        UINT32_C(2968073607),\n        UINT32_C(1053214930),\n        UINT32_C(1074411832),\n        UINT32_C(4016794508),\n        UINT32_C(1570312929),\n        UINT32_C(113576933),\n        UINT32_C(4042581186),\n        UINT32_C(3634515733),\n        UINT32_C(1032701597),\n        UINT32_C(2364839308),\n        UINT32_C(3840286918),\n        UINT32_C(888378655),\n        UINT32_C(2520191583),\n    },\n    {\n        UINT32_C(36046858),\n        UINT32_C(2927525953),\n        UINT32_C(3912129105),\n        UINT32_C(4004832531),\n        UINT32_C(193772436),\n        UINT32_C(1590247392),\n        UINT32_C(4125818172),\n        UINT32_C(2516251696),\n        UINT32_C(4050945750),\n        UINT32_C(269498914),\n        UINT32_C(1973292656),\n        UINT32_C(891403491),\n        UINT32_C(1845429189),\n        UINT32_C(2611996363),\n        UINT32_C(2310542653),\n        UINT32_C(4071195740),\n    },\n    {\n        UINT32_C(3505307391),\n        UINT32_C(786445290),\n        UINT32_C(3815313971),\n        UINT32_C(1111591756),\n        UINT32_C(4233279834),\n        UINT32_C(2775453034),\n        UINT32_C(1991257625),\n        UINT32_C(2940505809),\n        UINT32_C(2751316206),\n        UINT32_C(1028870679),\n        UINT32_C(1282466273),\n        UINT32_C(1059053371),\n        UINT32_C(834521354),\n        UINT32_C(138721483),\n        UINT32_C(3100410803),\n        UINT32_C(3843128331),\n    },\n    {\n        UINT32_C(3878220780),\n        UINT32_C(4058162439),\n        UINT32_C(1478942487),\n        UINT32_C(799012923),\n        UINT32_C(496734827),\n        UINT32_C(3521261236),\n        UINT32_C(755421082),\n        UINT32_C(1361409515),\n        UINT32_C(392099473),\n        UINT32_C(3178453393),\n        UINT32_C(4068463721),\n        UINT32_C(7935614),\n        UINT32_C(4140885645),\n        UINT32_C(2150748066),\n        UINT32_C(1685210312),\n        UINT32_C(3852983224),\n    },\n    {\n        UINT32_C(2896943075),\n        UINT32_C(3087590927),\n        UINT32_C(992175959),\n        UINT32_C(970216228),\n        UINT32_C(3473630090),\n        UINT32_C(3899670400),\n        UINT32_C(3603388822),\n        UINT32_C(2633488197),\n        UINT32_C(2479406964),\n        UINT32_C(2420952999),\n        UINT32_C(1852516800),\n        UINT32_C(4253075697),\n        UINT32_C(979699862),\n        UINT32_C(1163403191),\n        UINT32_C(1608599874),\n        UINT32_C(3056104448),\n    },\n    {\n        UINT32_C(3779109343),\n        UINT32_C(536205958),\n        UINT32_C(4183458361),\n        UINT32_C(1649720295),\n        UINT32_C(1444912244),\n        UINT32_C(3122230878),\n        UINT32_C(384301396),\n        UINT32_C(4228198516),\n        UINT32_C(1662916865),\n        UINT32_C(4082161114),\n        UINT32_C(2121897314),\n        UINT32_C(1706239958),\n        UINT32_C(4166959388),\n        UINT32_C(1626054781),\n        UINT32_C(3005858978),\n        UINT32_C(1431907253),\n    },\n    {\n        UINT32_C(1418914503),\n        UINT32_C(1365856753),\n        UINT32_C(3942715745),\n        UINT32_C(1429155552),\n        UINT32_C(3545642795),\n        UINT32_C(3772474257),\n        UINT32_C(1621094396),\n        UINT32_C(2154399145),\n        UINT32_C(826697382),\n        UINT32_C(1700781391),\n        UINT32_C(3539164324),\n        UINT32_C(652815039),\n        UINT32_C(442484755),\n        UINT32_C(2055299391),\n        UINT32_C(1064289978),\n        UINT32_C(1152335780),\n    },\n    {\n        UINT32_C(3417648695),\n        UINT32_C(186040114),\n        UINT32_C(3475580573),\n        UINT32_C(2113941250),\n        UINT32_C(1779573826),\n        UINT32_C(1573808590),\n        UINT32_C(3235694804),\n        UINT32_C(2922195281),\n        UINT32_C(1119462702),\n        UINT32_C(3688305521),\n        UINT32_C(1849567013),\n        UINT32_C(667446787),\n        UINT32_C(753897224),\n        UINT32_C(1896396780),\n        UINT32_C(3143026334),\n        UINT32_C(3829603876),\n    },\n    {\n        UINT32_C(859661334),\n        UINT32_C(3898844357),\n        UINT32_C(180258337),\n        UINT32_C(2321867017),\n        UINT32_C(3599002504),\n        UINT32_C(2886782421),\n        UINT32_C(3038299378),\n        UINT32_C(1035366250),\n        UINT32_C(2038912197),\n        UINT32_C(2920174523),\n        UINT32_C(1277696101),\n        UINT32_C(2785700290),\n        UINT32_C(3806504335),\n        UINT32_C(3518858933),\n        UINT32_C(654843672),\n        UINT32_C(2127120275),\n    },\n    {\n        UINT32_C(1548195514),\n        UINT32_C(2378056027),\n        UINT32_C(390914568),\n        UINT32_C(1472049779),\n        UINT32_C(1552596765),\n        UINT32_C(1905886441),\n        UINT32_C(1611959354),\n        UINT32_C(3653263304),\n        UINT32_C(3423946386),\n        UINT32_C(340857935),\n        UINT32_C(2208879480),\n        UINT32_C(139364268),\n        UINT32_C(3447281773),\n        UINT32_C(3777813707),\n        UINT32_C(55640413),\n        UINT32_C(4101901741),\n    },\n    {UINT32_C(104929687), UINT32_C(1459980974), UINT32_C(1831234737),\n     UINT32_C(457139004), UINT32_C(2581487628), UINT32_C(2112044563),\n     UINT32_C(3567013861), UINT32_C(2792004347), UINT32_C(576325418),\n     UINT32_C(41126132), UINT32_C(2713562324), UINT32_C(151213722),\n     UINT32_C(2891185935), UINT32_C(546846420), UINT32_C(2939794919),\n     UINT32_C(2543469905)},\n    {\n        UINT32_C(2191909784),\n        UINT32_C(3315138460),\n        UINT32_C(530414574),\n        UINT32_C(1242280418),\n        UINT32_C(1211740715),\n        UINT32_C(3993672165),\n        UINT32_C(2505083323),\n        UINT32_C(3845798801),\n        UINT32_C(538768466),\n        UINT32_C(2063567560),\n        UINT32_C(3366148274),\n        UINT32_C(1449831887),\n        UINT32_C(2408012466),\n        UINT32_C(294726285),\n        UINT32_C(3943435493),\n        UINT32_C(924016661),\n    },\n    {\n        UINT32_C(3633138367),\n        UINT32_C(3222789372),\n        UINT32_C(809116305),\n        UINT32_C(30100013),\n        UINT32_C(2655172876),\n        UINT32_C(2564247117),\n        UINT32_C(2478649732),\n        UINT32_C(4113689151),\n        UINT32_C(4120146082),\n        UINT32_C(2512308515),\n        UINT32_C(650406041),\n        UINT32_C(4240012393),\n        UINT32_C(2683508708),\n        UINT32_C(951073977),\n        UINT32_C(3460081988),\n        UINT32_C(339124269),\n    },\n    {\n        UINT32_C(130182653),\n        UINT32_C(2755946749),\n        UINT32_C(542600513),\n        UINT32_C(2816103022),\n        UINT32_C(1931786340),\n        UINT32_C(2044470840),\n        UINT32_C(1709908013),\n        UINT32_C(2938369043),\n        UINT32_C(3640399693),\n        UINT32_C(1374470239),\n        UINT32_C(2191149676),\n        UINT32_C(2637495682),\n        UINT32_C(4236394040),\n        UINT32_C(2289358846),\n        UINT32_C(3833368530),\n        UINT32_C(974546524),\n    },\n    {\n        UINT32_C(3306659113),\n        UINT32_C(2234814261),\n        UINT32_C(1188782305),\n        UINT32_C(223782844),\n        UINT32_C(2248980567),\n        UINT32_C(2309786141),\n        UINT32_C(2023401627),\n        UINT32_C(3278877413),\n        UINT32_C(2022138149),\n        UINT32_C(575851471),\n        UINT32_C(1612560780),\n        UINT32_C(3926656936),\n        UINT32_C(3318548977),\n        UINT32_C(2591863678),\n        UINT32_C(188109355),\n        UINT32_C(4217723909),\n    },\n    {\n        UINT32_C(1564209905),\n        UINT32_C(2154197895),\n        UINT32_C(2459687029),\n        UINT32_C(2870634489),\n        UINT32_C(1375012945),\n        UINT32_C(1529454825),\n        UINT32_C(306140690),\n        UINT32_C(2855578299),\n        UINT32_C(1246997295),\n        UINT32_C(3024298763),\n        UINT32_C(1915270363),\n        UINT32_C(1218245412),\n        UINT32_C(2479314020),\n        UINT32_C(2989827755),\n        UINT32_C(814378556),\n        UINT32_C(4039775921),\n    },\n    {\n        UINT32_C(1165280628),\n        UINT32_C(1203983801),\n        UINT32_C(3814740033),\n        UINT32_C(1919627044),\n        UINT32_C(600240215),\n        UINT32_C(773269071),\n        UINT32_C(486685186),\n        UINT32_C(4254048810),\n        UINT32_C(1415023565),\n        UINT32_C(502840102),\n        UINT32_C(4225648358),\n        UINT32_C(510217063),\n        UINT32_C(166444818),\n        UINT32_C(1430745893),\n        UINT32_C(1376516190),\n        UINT32_C(1775891321),\n    },\n    {\n        UINT32_C(1170945922),\n        UINT32_C(1105391877),\n        UINT32_C(261536467),\n        UINT32_C(1401687994),\n        UINT32_C(1022529847),\n        UINT32_C(2476446456),\n        UINT32_C(2603844878),\n        UINT32_C(3706336043),\n        UINT32_C(3463053714),\n        UINT32_C(1509644517),\n        UINT32_C(588552318),\n        UINT32_C(65252581),\n        UINT32_C(3696502656),\n        UINT32_C(2183330763),\n        UINT32_C(3664021233),\n        UINT32_C(1643809916),\n    },\n    {\n        UINT32_C(2922875898),\n        UINT32_C(3740690643),\n        UINT32_C(3932461140),\n        UINT32_C(161156271),\n        UINT32_C(2619943483),\n        UINT32_C(4077039509),\n        UINT32_C(2921201703),\n        UINT32_C(2085619718),\n        UINT32_C(2065264646),\n        UINT32_C(2615693812),\n        UINT32_C(3116555433),\n        UINT32_C(246100007),\n        UINT32_C(4281387154),\n        UINT32_C(4046141001),\n        UINT32_C(4027749321),\n        UINT32_C(111611860),\n    },\n    {\n        UINT32_C(2066954820),\n        UINT32_C(2502099969),\n        UINT32_C(2915053115),\n        UINT32_C(2362518586),\n        UINT32_C(366091708),\n        UINT32_C(2083204932),\n        UINT32_C(4138385632),\n        UINT32_C(3195157567),\n        UINT32_C(1318086382),\n        UINT32_C(521723799),\n        UINT32_C(702443405),\n        UINT32_C(2507670985),\n        UINT32_C(1760347557),\n        UINT32_C(2631999893),\n        UINT32_C(1672737554),\n        UINT32_C(1060867760),\n    },\n    {\n        UINT32_C(2359801781),\n        UINT32_C(2800231467),\n        UINT32_C(3010357035),\n        UINT32_C(1035997899),\n        UINT32_C(1210110952),\n        UINT32_C(1018506770),\n        UINT32_C(2799468177),\n        UINT32_C(1479380761),\n        UINT32_C(1536021911),\n        UINT32_C(358993854),\n        UINT32_C(579904113),\n        UINT32_C(3432144800),\n        UINT32_C(3625515809),\n        UINT32_C(199241497),\n        UINT32_C(4058304109),\n        UINT32_C(2590164234),\n    },\n    {\n        UINT32_C(1688530738),\n        UINT32_C(1580733335),\n        UINT32_C(2443981517),\n        UINT32_C(2206270565),\n        UINT32_C(2780074229),\n        UINT32_C(2628739677),\n        UINT32_C(2940123659),\n        UINT32_C(4145206827),\n        UINT32_C(3572278009),\n        UINT32_C(2779607509),\n        UINT32_C(1098718697),\n        UINT32_C(1424913749),\n        UINT32_C(2224415875),\n        UINT32_C(1108922178),\n        UINT32_C(3646272562),\n        UINT32_C(3935186184),\n    },\n    {\n        UINT32_C(820046587),\n        UINT32_C(1393386250),\n        UINT32_C(2665818575),\n        UINT32_C(2231782019),\n        UINT32_C(672377010),\n        UINT32_C(1920315467),\n        UINT32_C(1913164407),\n        UINT32_C(2029526876),\n        UINT32_C(2629271820),\n        UINT32_C(384320012),\n        UINT32_C(4112320585),\n        UINT32_C(3131824773),\n        UINT32_C(2347818197),\n        UINT32_C(2220997386),\n        UINT32_C(1772368609),\n        UINT32_C(2579960095),\n    },\n    {\n        UINT32_C(3544930873),\n        UINT32_C(225847443),\n        UINT32_C(3070082278),\n        UINT32_C(95643305),\n        UINT32_C(3438572042),\n        UINT32_C(3312856509),\n        UINT32_C(615850007),\n        UINT32_C(1863868773),\n        UINT32_C(803582265),\n        UINT32_C(3461976859),\n        UINT32_C(2903025799),\n        UINT32_C(1482092434),\n        UINT32_C(3902972499),\n        UINT32_C(3872341868),\n        UINT32_C(1530411808),\n        UINT32_C(2214923584),\n    },\n    {\n        UINT32_C(3118792481),\n        UINT32_C(2241076515),\n        UINT32_C(3983669831),\n        UINT32_C(3180915147),\n        UINT32_C(3838626501),\n        UINT32_C(1921630011),\n        UINT32_C(3415351771),\n        UINT32_C(2249953859),\n        UINT32_C(3755081630),\n        UINT32_C(486327260),\n        UINT32_C(1227575720),\n        UINT32_C(3643869379),\n        UINT32_C(2982026073),\n        UINT32_C(2466043731),\n        UINT32_C(1982634375),\n        UINT32_C(3769609014),\n    },\n    {\n        UINT32_C(2195455495),\n        UINT32_C(2596863283),\n        UINT32_C(4244994973),\n        UINT32_C(1983609348),\n        UINT32_C(4019674395),\n        UINT32_C(3469982031),\n        UINT32_C(1458697570),\n        UINT32_C(1593516217),\n        UINT32_C(1963896497),\n        UINT32_C(3115309118),\n        UINT32_C(1659132465),\n        UINT32_C(2536770756),\n        UINT32_C(3059294171),\n        UINT32_C(2618031334),\n        UINT32_C(2040903247),\n        UINT32_C(3799795076),\n    },\n};\n\n#endif  // TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_CONSTANTS_H_\n"
  },
  {
    "path": "tachyon/c/zk/air/sp1/baby_bear_poseidon2_domains.cc",
    "content": "#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_domains.h\"\n\n#include <vector>\n\n#include \"tachyon/c/math/finite_fields/baby_bear/baby_bear_type_traits.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_domains_type_traits.h\"\n\nusing namespace tachyon;\n\nusing Domains = std::vector<\n    std::vector<crypto::TwoAdicMultiplicativeCoset<math::BabyBear>>>;\n\ntachyon_sp1_baby_bear_poseidon2_domains*\ntachyon_sp1_baby_bear_poseidon2_domains_create(size_t rounds) {\n  return c::base::c_cast(new Domains(rounds));\n}\n\nvoid tachyon_sp1_baby_bear_poseidon2_domains_destroy(\n    tachyon_sp1_baby_bear_poseidon2_domains* domains) {\n  delete c::base::native_cast(domains);\n}\n\nvoid tachyon_sp1_baby_bear_poseidon2_domains_allocate(\n    tachyon_sp1_baby_bear_poseidon2_domains* domains, size_t round,\n    size_t size) {\n  c::base::native_cast(*domains)[round].resize(size);\n}\n\nvoid tachyon_sp1_baby_bear_poseidon2_domains_set(\n    tachyon_sp1_baby_bear_poseidon2_domains* domains, size_t round, size_t idx,\n    uint32_t log_n, const tachyon_baby_bear* shift) {\n  c::base::native_cast(*domains)[round][idx] =\n      crypto::TwoAdicMultiplicativeCoset(log_n, c::base::native_cast(*shift));\n}\n"
  },
  {
    "path": "tachyon/c/zk/air/sp1/baby_bear_poseidon2_domains.h",
    "content": "/**\n * @file baby_bear_poseidon2_domains.h\n * @brief Defines the interface for the set of domains used within the\n * SP1(BabyBear + Poseidon2) proof system.\n */\n\n#ifndef TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_DOMAINS_H_\n#define TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_DOMAINS_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include \"tachyon/c/export.h\"\n#include \"tachyon/c/math/finite_fields/baby_bear/baby_bear.h\"\n\nstruct tachyon_sp1_baby_bear_poseidon2_domains {};\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Creates a new set of domains.\n *\n * @param rounds The number of rounds.\n * @return A pointer to the newly created domains.\n */\nTACHYON_C_EXPORT\ntachyon_sp1_baby_bear_poseidon2_domains*\ntachyon_sp1_baby_bear_poseidon2_domains_create(size_t rounds);\n\n/**\n * @brief Destroys a set of domains, freeing its resources.\n *\n * @param domains A pointer to the domains to destroy.\n */\nTACHYON_C_EXPORT void tachyon_sp1_baby_bear_poseidon2_domains_destroy(\n    tachyon_sp1_baby_bear_poseidon2_domains* domains);\n\n/**\n * @brief Allocates memory for the set of domains.\n *\n * @param domains A pointer to the set of domains.\n * @param round The round.\n * @param size The size of the domains.\n */\nTACHYON_C_EXPORT void tachyon_sp1_baby_bear_poseidon2_domains_allocate(\n    tachyon_sp1_baby_bear_poseidon2_domains* domains, size_t round,\n    size_t size);\n\n/**\n * @brief Sets up a domain.\n *\n * @param domains A pointer to the domains.\n * @param round The round index of the point.\n * @param idx The index of the domain.\n * @param log_n The logarithmic of domain size.\n * @param shift The shift value.\n */\nTACHYON_C_EXPORT void tachyon_sp1_baby_bear_poseidon2_domains_set(\n    tachyon_sp1_baby_bear_poseidon2_domains* domains, size_t round, size_t idx,\n    uint32_t log_n, const tachyon_baby_bear* shift);\n\n#ifdef __cplusplus\n}  // extern \"C\"\n#endif\n\n#endif  // TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_DOMAINS_H_\n"
  },
  {
    "path": "tachyon/c/zk/air/sp1/baby_bear_poseidon2_domains_type_traits.h",
    "content": "#ifndef TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_DOMAINS_TYPE_TRAITS_H_\n#define TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_DOMAINS_TYPE_TRAITS_H_\n\n#include <vector>\n\n#include \"tachyon/c/base/type_traits_forward.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_domains.h\"\n#include \"tachyon/crypto/commitments/fri/two_adic_multiplicative_coset.h\"\n\nnamespace tachyon::c::base {\n\ntemplate <>\nstruct TypeTraits<std::vector<std::vector<\n    tachyon::crypto::TwoAdicMultiplicativeCoset<tachyon::math::BabyBear>>>> {\n  using CType = tachyon_sp1_baby_bear_poseidon2_domains;\n};\n\ntemplate <>\nstruct TypeTraits<tachyon_sp1_baby_bear_poseidon2_domains> {\n  using NativeType = std::vector<std::vector<\n      tachyon::crypto::TwoAdicMultiplicativeCoset<tachyon::math::BabyBear>>>;\n};\n\n}  // namespace tachyon::c::base\n\n#endif  // TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_DOMAINS_TYPE_TRAITS_H_\n"
  },
  {
    "path": "tachyon/c/zk/air/sp1/baby_bear_poseidon2_duplex_challenger.cc",
    "content": "#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_duplex_challenger.h\"\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/auto_reset.h\"\n#include \"tachyon/base/buffer/buffer.h\"\n#include \"tachyon/c/math/finite_fields/baby_bear/baby_bear_type_traits.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_duplex_challenger_type_traits.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_hintable.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/param_traits/poseidon2_baby_bear.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_params.h\"\n\nusing namespace tachyon;\n\nusing F = math::BabyBear;\nusing Params =\n    crypto::Poseidon2Params<crypto::Poseidon2Vendor::kPlonky3,\n                            crypto::Poseidon2Vendor::kPlonky3, F,\n                            TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_WIDTH - 1,\n                            TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_ALPHA>;\nusing Poseidon2 = crypto::Poseidon2Sponge<Params>;\n\ntachyon_sp1_baby_bear_poseidon2_duplex_challenger*\ntachyon_sp1_baby_bear_poseidon2_duplex_challenger_create() {\n  math::Matrix<F> ark(TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_FULL_ROUNDS +\n                          TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_PARTIAL_ROUNDS,\n                      TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_WIDTH);\n  for (Eigen::Index r = 0; r < ark.rows(); ++r) {\n    for (Eigen::Index c = 0; c < ark.cols(); ++c) {\n      ark(r, c) = F(kRoundConstants[r][c] % F::Config::kModulus);\n    }\n  }\n\n  auto config = crypto::Poseidon2Config<Params>::CreateDefault(std::move(ark));\n  Poseidon2 sponge(std::move(config));\n  return c::base::c_cast(\n      new crypto::DuplexChallenger<Poseidon2,\n                                   TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_RATE>(\n          std::move(sponge)));\n}\n\ntachyon_sp1_baby_bear_poseidon2_duplex_challenger*\ntachyon_sp1_baby_bear_poseidon2_duplex_challenger_clone(\n    const tachyon_sp1_baby_bear_poseidon2_duplex_challenger* challenger) {\n  return c::base::c_cast(\n      new crypto::DuplexChallenger<Poseidon2,\n                                   TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_RATE>(\n          *c::base::native_cast(challenger)));\n}\n\nvoid tachyon_sp1_baby_bear_poseidon2_duplex_challenger_destroy(\n    tachyon_sp1_baby_bear_poseidon2_duplex_challenger* challenger) {\n  delete c::base::native_cast(challenger);\n}\n\nvoid tachyon_sp1_baby_bear_poseidon2_duplex_challenger_observe(\n    tachyon_sp1_baby_bear_poseidon2_duplex_challenger* challenger,\n    const tachyon_baby_bear* value) {\n  c::base::native_cast(challenger)->Observe(c::base::native_cast(*value));\n}\n\ntachyon_baby_bear tachyon_sp1_baby_bear_poseidon2_duplex_challenger_sample(\n    tachyon_sp1_baby_bear_poseidon2_duplex_challenger* challenger) {\n  return c::base::c_cast(c::base::native_cast(challenger)->Sample());\n}\n\nvoid tachyon_sp1_baby_bear_poseidon2_duplex_challenger_write_hint(\n    const tachyon_sp1_baby_bear_poseidon2_duplex_challenger* challenger,\n    uint8_t* data, size_t* data_len) {\n  std::vector<std::vector<c::zk::air::sp1::Block<F>>> hints =\n      c::zk::air::sp1::baby_bear::WriteHint(c::base::native_cast(*challenger));\n  *data_len = base::EstimateSize(hints);\n  if (data == nullptr) return;\n\n  base::AutoReset<bool> auto_reset(&base::Copyable<F>::s_is_in_montgomery,\n                                   true);\n  base::Buffer buffer(data, *data_len);\n  CHECK(buffer.Write(hints));\n  CHECK(buffer.Done());\n}\n"
  },
  {
    "path": "tachyon/c/zk/air/sp1/baby_bear_poseidon2_duplex_challenger.h",
    "content": "/**\n * @file baby_bear_poseidon2_duplex_challenger.h\n * @brief Defines the interface for the duplex challenger used within the\n * SP1(BabyBear + Poseidon2) proof system.\n */\n\n#ifndef TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_DUPLEX_CHALLENGER_H_\n#define TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_DUPLEX_CHALLENGER_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include \"tachyon/c/export.h\"\n#include \"tachyon/c/math/finite_fields/baby_bear/baby_bear.h\"\n\nstruct tachyon_sp1_baby_bear_poseidon2_duplex_challenger {};\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Creates a new duplex challenger.\n *\n * @return A pointer to the newly created duplex challenger.\n */\nTACHYON_C_EXPORT tachyon_sp1_baby_bear_poseidon2_duplex_challenger*\ntachyon_sp1_baby_bear_poseidon2_duplex_challenger_create();\n\n/**\n * @brief Clones an existing duplex challenger structure.\n *\n * Creates a deep copy of the given duplex challenger structure.\n *\n * @param challenger A const pointer to the duplex challenger structure to\n * clone.\n * @return A pointer to the cloned duplex challenger structure.\n */\nTACHYON_C_EXPORT tachyon_sp1_baby_bear_poseidon2_duplex_challenger*\ntachyon_sp1_baby_bear_poseidon2_duplex_challenger_clone(\n    const tachyon_sp1_baby_bear_poseidon2_duplex_challenger* challenger);\n\n/**\n * @brief Destroys a duplex challenger, freeing its resources.\n *\n * @param challenger A pointer to the duplex challenger to destroy.\n */\nTACHYON_C_EXPORT void tachyon_sp1_baby_bear_poseidon2_duplex_challenger_destroy(\n    tachyon_sp1_baby_bear_poseidon2_duplex_challenger* challenger);\n\n/**\n * @brief Observes a new value.\n *\n * @param challenger A pointer to the duplex challenger.\n * @param value A const pointer to the value.\n */\nTACHYON_C_EXPORT void tachyon_sp1_baby_bear_poseidon2_duplex_challenger_observe(\n    tachyon_sp1_baby_bear_poseidon2_duplex_challenger* challenger,\n    const tachyon_baby_bear* value);\n\n/**\n * @brief Samples a field element from the challenger.\n *\n * @param challenger A pointer to the duplex challenger.\n */\nTACHYON_C_EXPORT tachyon_baby_bear\ntachyon_sp1_baby_bear_poseidon2_duplex_challenger_sample(\n    tachyon_sp1_baby_bear_poseidon2_duplex_challenger* challenger);\n\n/**\n * @brief Write a hint of the duplex challenger to the byte array.\n * See\n * https://github.com/succinctlabs/sp1/blob/6f67afd/crates/recursion/program/src/hints.rs#L371-L402.\n *\n * @param challenger A const pointer to the duplex challenger.\n * @param data A pointer to the byte array.\n * @param data_len A pointer to store the length of the byte array.\n */\nTACHYON_C_EXPORT void\ntachyon_sp1_baby_bear_poseidon2_duplex_challenger_write_hint(\n    const tachyon_sp1_baby_bear_poseidon2_duplex_challenger* challenger,\n    uint8_t* data, size_t* data_len);\n\n#ifdef __cplusplus\n}  // extern \"C\"\n#endif\n\n#endif  // TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_DUPLEX_CHALLENGER_H_\n"
  },
  {
    "path": "tachyon/c/zk/air/sp1/baby_bear_poseidon2_duplex_challenger_type_traits.h",
    "content": "#ifndef TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_DUPLEX_CHALLENGER_TYPE_TRAITS_H_\n#define TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_DUPLEX_CHALLENGER_TYPE_TRAITS_H_\n\n#include \"tachyon/c/base/type_traits_forward.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_constants.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_duplex_challenger.h\"\n#include \"tachyon/crypto/challenger/duplex_challenger.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_params.h\"\n#include \"tachyon/math/finite_fields/baby_bear/baby_bear.h\"\n\nnamespace tachyon::c::base {\n\nnamespace {\n\nusing Params = tachyon::crypto::Poseidon2Params<\n    tachyon::crypto::Poseidon2Vendor::kPlonky3,\n    tachyon::crypto::Poseidon2Vendor::kPlonky3, tachyon::math::BabyBear,\n    TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_WIDTH - 1,\n    TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_ALPHA>;\n\n}  // namespace\n\ntemplate <>\nstruct TypeTraits<tachyon::crypto::DuplexChallenger<\n    tachyon::crypto::Poseidon2Sponge<Params>,\n    TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_RATE>> {\n  using CType = tachyon_sp1_baby_bear_poseidon2_duplex_challenger;\n};\n\ntemplate <>\nstruct TypeTraits<tachyon_sp1_baby_bear_poseidon2_duplex_challenger> {\n  using NativeType = tachyon::crypto::DuplexChallenger<\n      tachyon::crypto::Poseidon2Sponge<Params>,\n      TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_RATE>;\n};\n\n}  // namespace tachyon::c::base\n\n#endif  // TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_DUPLEX_CHALLENGER_TYPE_TRAITS_H_\n"
  },
  {
    "path": "tachyon/c/zk/air/sp1/baby_bear_poseidon2_duplex_challenger_unittest.cc",
    "content": "#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_duplex_challenger.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/c/math/finite_fields/baby_bear/baby_bear_type_traits.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_constants.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_duplex_challenger_type_traits.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_params.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n\nnamespace tachyon::crypto {\n\nusing F = math::BabyBear;\nusing Params =\n    Poseidon2Params<Poseidon2Vendor::kPlonky3, Poseidon2Vendor::kPlonky3, F,\n                    TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_WIDTH - 1,\n                    TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_ALPHA>;\nusing Poseidon2 = Poseidon2Sponge<Params>;\n\nclass DuplexChallengerTest : public math::FiniteFieldTest<F> {\n public:\n  void SetUp() override {\n    challenger_ = tachyon_sp1_baby_bear_poseidon2_duplex_challenger_create();\n  }\n\n  void TearDown() override {\n    tachyon_sp1_baby_bear_poseidon2_duplex_challenger_destroy(challenger_);\n  }\n\n protected:\n  tachyon_sp1_baby_bear_poseidon2_duplex_challenger* challenger_ = nullptr;\n};\n\nTEST_F(DuplexChallengerTest, APIs) {\n  DuplexChallenger<Poseidon2, TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_RATE>\n      challenger = c::base::native_cast(*challenger_);\n  for (size_t i = 0; i < 20; ++i) {\n    F value(i);\n    challenger.Observe(value);\n\n    tachyon_sp1_baby_bear_poseidon2_duplex_challenger_observe(\n        challenger_, c::base::c_cast(&value));\n  }\n  for (size_t i = 0; i < 10; ++i) {\n    tachyon_baby_bear value =\n        tachyon_sp1_baby_bear_poseidon2_duplex_challenger_sample(challenger_);\n    EXPECT_EQ(c::base::native_cast(value), challenger.Sample());\n  }\n}\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/c/zk/air/sp1/baby_bear_poseidon2_field_merkle_tree.cc",
    "content": "#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_field_merkle_tree.h\"\n\n#include \"tachyon/base/auto_reset.h\"\n#include \"tachyon/base/buffer/buffer.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_field_merkle_tree_type_traits.h\"\n\nusing namespace tachyon;\n\nusing F = math::BabyBear;\nusing Tree = c::zk::air::sp1::baby_bear::Tree;\n\ntachyon_sp1_baby_bear_poseidon2_field_merkle_tree*\ntachyon_sp1_baby_bear_poseidon2_field_merkle_tree_clone(\n    const tachyon_sp1_baby_bear_poseidon2_field_merkle_tree* tree) {\n  return c::base::c_cast(new Tree(c::base::native_cast(*tree)));\n}\n\nvoid tachyon_sp1_baby_bear_poseidon2_field_merkle_tree_destroy(\n    tachyon_sp1_baby_bear_poseidon2_field_merkle_tree* tree) {\n  delete c::base::native_cast(tree);\n}\n\nvoid tachyon_sp1_baby_bear_poseidon2_field_merkle_tree_serialize(\n    const tachyon_sp1_baby_bear_poseidon2_field_merkle_tree* tree,\n    uint8_t* data, size_t* data_len) {\n  *data_len = base::EstimateSize(c::base::native_cast(*tree));\n  if (data == nullptr) return;\n\n  base::AutoReset<bool> auto_reset(&base::Copyable<F>::s_is_in_montgomery,\n                                   true);\n  base::Buffer buffer(data, *data_len);\n  CHECK(buffer.Write(c::base::native_cast(*tree)));\n  CHECK(buffer.Done());\n}\n\ntachyon_sp1_baby_bear_poseidon2_field_merkle_tree*\ntachyon_sp1_baby_bear_poseidon2_field_merkle_tree_deserialize(\n    const uint8_t* data, size_t data_len) {\n  base::AutoReset<bool> auto_reset(&base::Copyable<F>::s_is_in_montgomery,\n                                   true);\n  Tree* tree = new Tree();\n  base::ReadOnlyBuffer buffer(data, data_len);\n  CHECK(buffer.Read(tree));\n  return c::base::c_cast(tree);\n}\n"
  },
  {
    "path": "tachyon/c/zk/air/sp1/baby_bear_poseidon2_field_merkle_tree.h",
    "content": "/**\n * @file baby_bear_poseidon2_field_merkle_tree.h\n * @brief Defines the interface for the field merkle tree used within the\n * SP1(BabyBear + Poseidon2) proof system.\n */\n\n#ifndef TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_FIELD_MERKLE_TREE_H_\n#define TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_FIELD_MERKLE_TREE_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include \"tachyon/c/export.h\"\n\nstruct tachyon_sp1_baby_bear_poseidon2_field_merkle_tree {};\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Clones an existing field merkle tree structure.\n *\n * Creates a deep copy of the given field merkle tree structure.\n *\n * @param tree A const pointer to the field merkle tree structure\n * to clone.\n * @return A pointer to the cloned field merkle tree structure.\n */\nTACHYON_C_EXPORT tachyon_sp1_baby_bear_poseidon2_field_merkle_tree*\ntachyon_sp1_baby_bear_poseidon2_field_merkle_tree_clone(\n    const tachyon_sp1_baby_bear_poseidon2_field_merkle_tree* tree);\n\n/**\n * @brief Destroys a field merkle tree, freeing its resources.\n *\n * @param tree A pointer to the field merkle tree to destroy.\n */\nTACHYON_C_EXPORT void tachyon_sp1_baby_bear_poseidon2_field_merkle_tree_destroy(\n    tachyon_sp1_baby_bear_poseidon2_field_merkle_tree* tree);\n\n/**\n * @brief Serializes a field merkle tree to the byte array.\n *\n * @param tree A const pointer to the set of field merkle tree.\n * @param data A pointer to the byte array.\n * @param data_len A pointer to store the length of the byte array.\n */\nTACHYON_C_EXPORT void\ntachyon_sp1_baby_bear_poseidon2_field_merkle_tree_serialize(\n    const tachyon_sp1_baby_bear_poseidon2_field_merkle_tree* tree,\n    uint8_t* data, size_t* data_len);\n\n/**\n * @brief Deserializes a field merkle tree from the byte array.\n *\n * @param data A const pointer to the byte array.\n * @param data_len The length of the byte array.\n * @return A pointer to the deserialized field merkle tree.\n */\nTACHYON_C_EXPORT tachyon_sp1_baby_bear_poseidon2_field_merkle_tree*\ntachyon_sp1_baby_bear_poseidon2_field_merkle_tree_deserialize(\n    const uint8_t* data, size_t data_len);\n\n#ifdef __cplusplus\n}  // extern \"C\"\n#endif\n\n#endif  // TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_FIELD_MERKLE_TREE_H_\n"
  },
  {
    "path": "tachyon/c/zk/air/sp1/baby_bear_poseidon2_field_merkle_tree_type_traits.h",
    "content": "#ifndef TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_FIELD_MERKLE_TREE_TYPE_TRAITS_H_\n#define TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_FIELD_MERKLE_TREE_TYPE_TRAITS_H_\n\n#include \"tachyon/c/base/type_traits_forward.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_constants.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_field_merkle_tree.h\"\n#include \"tachyon/crypto/commitments/merkle_tree/field_merkle_tree/field_merkle_tree.h\"\n#include \"tachyon/math/finite_fields/baby_bear/baby_bear.h\"\n\nnamespace tachyon::c {\nnamespace zk::air::sp1::baby_bear {\n\nusing Tree =\n    tachyon::crypto::FieldMerkleTree<tachyon::math::BabyBear,\n                                     TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_CHUNK>;\n\n}  // namespace zk::air::sp1::baby_bear\n\nnamespace base {\n\ntemplate <>\nstruct TypeTraits<zk::air::sp1::baby_bear::Tree> {\n  using CType = tachyon_sp1_baby_bear_poseidon2_field_merkle_tree;\n};\n\ntemplate <>\nstruct TypeTraits<tachyon_sp1_baby_bear_poseidon2_field_merkle_tree> {\n  using NativeType = zk::air::sp1::baby_bear::Tree;\n};\n\n}  // namespace base\n}  // namespace tachyon::c\n\n#endif  // TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_FIELD_MERKLE_TREE_TYPE_TRAITS_H_\n"
  },
  {
    "path": "tachyon/c/zk/air/sp1/baby_bear_poseidon2_field_merkle_tree_vec.cc",
    "content": "#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_field_merkle_tree_vec.h\"\n\n#include <vector>\n\n#include \"absl/debugging/leak_check.h\"\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_field_merkle_tree_type_traits.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_field_merkle_tree_vec_type_traits.h\"\n\nusing namespace tachyon;\n\nusing Tree = c::zk::air::sp1::baby_bear::Tree;\nusing TreeVec = std::vector<const Tree*>;\n\ntachyon_sp1_baby_bear_poseidon2_field_merkle_tree_vec*\ntachyon_sp1_baby_bear_poseidon2_field_merkle_tree_vec_create(size_t rounds) {\n  return c::base::c_cast(new TreeVec(rounds));\n}\n\ntachyon_sp1_baby_bear_poseidon2_field_merkle_tree_vec*\ntachyon_sp1_baby_bear_poseidon2_field_merkle_tree_vec_clone(\n    const tachyon_sp1_baby_bear_poseidon2_field_merkle_tree_vec* tree_vec) {\n  const TreeVec& native_tree_vec = c::base::native_cast(*tree_vec);\n  TreeVec* cloned = new TreeVec();\n  *cloned = base::Map(native_tree_vec, [](const Tree* tree) {\n    // NOTE(chokobole): The caller is responsible for deallocating the memory.\n    // Specifically, in SP1 proof generation, the deallocation is handled by\n    // calling |tachyon_sp1_baby_bear_poseidon2_field_merkle_tree_destroy()|.\n    // This function will be invoked by the Rust struct that owns the Field\n    // Merkle tree.\n    return absl::IgnoreLeak(new const Tree(*tree));\n  });\n  return c::base::c_cast(cloned);\n}\n\nvoid tachyon_sp1_baby_bear_poseidon2_field_merkle_tree_vec_destroy(\n    tachyon_sp1_baby_bear_poseidon2_field_merkle_tree_vec* tree_vec) {\n  delete c::base::native_cast(tree_vec);\n}\n\nvoid tachyon_sp1_baby_bear_poseidon2_field_merkle_tree_vec_set(\n    tachyon_sp1_baby_bear_poseidon2_field_merkle_tree_vec* tree_vec,\n    size_t round,\n    const tachyon_sp1_baby_bear_poseidon2_field_merkle_tree* tree) {\n  c::base::native_cast(*tree_vec)[round] = c::base::native_cast(tree);\n}\n"
  },
  {
    "path": "tachyon/c/zk/air/sp1/baby_bear_poseidon2_field_merkle_tree_vec.h",
    "content": "/**\n * @file baby_bear_poseidon2_field_merkle_tree_vec.h\n * @brief Defines the interface for the field merkle tree used within the\n * SP1(BabyBear + Poseidon2) proof system.\n */\n\n#ifndef TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_FIELD_MERKLE_TREE_VEC_H_\n#define TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_FIELD_MERKLE_TREE_VEC_H_\n\n#include <stddef.h>\n\n#include \"tachyon/c/export.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_field_merkle_tree.h\"\n\nstruct tachyon_sp1_baby_bear_poseidon2_field_merkle_tree_vec {};\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Creates a new field merkle tree vector.\n *\n * @param rounds The number of rounds.\n * @return A pointer to the newly created field merkle tree vector.\n */\nTACHYON_C_EXPORT tachyon_sp1_baby_bear_poseidon2_field_merkle_tree_vec*\ntachyon_sp1_baby_bear_poseidon2_field_merkle_tree_vec_create(size_t rounds);\n\n/**\n * @brief Clones an existing field merkle tree vector structure.\n *\n * Creates a deep copy of the given field merkle tree vector structure.\n *\n * @param tree_vec A const pointer to the field merkle tree vector structure\n * to clone.\n * @return A pointer to the cloned field merkle tree vector structure.\n */\nTACHYON_C_EXPORT tachyon_sp1_baby_bear_poseidon2_field_merkle_tree_vec*\ntachyon_sp1_baby_bear_poseidon2_field_merkle_tree_vec_clone(\n    const tachyon_sp1_baby_bear_poseidon2_field_merkle_tree_vec* tree_vec);\n\n/**\n * @brief Destroys a field merkle tree vector, freeing its resources.\n *\n * @param pcs A pointer to the field merkle tree vector to destroy.\n */\nTACHYON_C_EXPORT void\ntachyon_sp1_baby_bear_poseidon2_field_merkle_tree_vec_destroy(\n    tachyon_sp1_baby_bear_poseidon2_field_merkle_tree_vec* tree_vec);\n\n/**\n * @brief Sets field merkle tree.\n *\n * @param tree_vec A pointer to the field merkle tree vector.\n * @param round The round index of the point.\n * @param tree The const pointer to the field merkle tree.\n */\nTACHYON_C_EXPORT void tachyon_sp1_baby_bear_poseidon2_field_merkle_tree_vec_set(\n    tachyon_sp1_baby_bear_poseidon2_field_merkle_tree_vec* tree_vec,\n    size_t round,\n    const tachyon_sp1_baby_bear_poseidon2_field_merkle_tree* tree);\n\n#ifdef __cplusplus\n}  // extern \"C\"\n#endif\n\n#endif  // TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_FIELD_MERKLE_TREE_VEC_H_\n"
  },
  {
    "path": "tachyon/c/zk/air/sp1/baby_bear_poseidon2_field_merkle_tree_vec_type_traits.h",
    "content": "#ifndef TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_FIELD_MERKLE_TREE_VEC_TYPE_TRAITS_H_\n#define TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_FIELD_MERKLE_TREE_VEC_TYPE_TRAITS_H_\n\n#include <vector>\n\n#include \"tachyon/c/base/type_traits_forward.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_constants.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_field_merkle_tree_vec.h\"\n#include \"tachyon/crypto/commitments/merkle_tree/field_merkle_tree/field_merkle_tree.h\"\n#include \"tachyon/math/finite_fields/baby_bear/baby_bear.h\"\n\nnamespace tachyon::c {\nnamespace zk::air::sp1::baby_bear {\n\nusing Tree =\n    tachyon::crypto::FieldMerkleTree<tachyon::math::BabyBear,\n                                     TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_CHUNK>;\n\n}  // namespace zk::air::sp1::baby_bear\n\nnamespace base {\n\ntemplate <>\nstruct TypeTraits<std::vector<const zk::air::sp1::baby_bear::Tree*>> {\n  using CType = tachyon_sp1_baby_bear_poseidon2_field_merkle_tree_vec;\n};\n\ntemplate <>\nstruct TypeTraits<tachyon_sp1_baby_bear_poseidon2_field_merkle_tree_vec> {\n  using NativeType = std::vector<const zk::air::sp1::baby_bear::Tree*>;\n};\n\n}  // namespace base\n}  // namespace tachyon::c\n\n#endif  // TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_FIELD_MERKLE_TREE_VEC_TYPE_TRAITS_H_\n"
  },
  {
    "path": "tachyon/c/zk/air/sp1/baby_bear_poseidon2_fields.h",
    "content": "#ifndef TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_FIELDS_H_\n#define TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_FIELDS_H_\n\n#include \"tachyon/math/finite_fields/baby_bear/baby_bear4.h\"\n#include \"tachyon/math/finite_fields/baby_bear/packed_baby_bear4.h\"\n\nnamespace tachyon::c::zk::air::sp1::baby_bear {\n\nusing F = tachyon::math::BabyBear;\nusing PackedF = tachyon::math::PackedBabyBear;\nusing ExtF = tachyon::math::BabyBear4;\nusing ExtPackedF = tachyon::math::PackedBabyBear4;\n\n}  // namespace tachyon::c::zk::air::sp1::baby_bear\n\n#endif  // TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_FIELDS_H_\n"
  },
  {
    "path": "tachyon/c/zk/air/sp1/baby_bear_poseidon2_fri_proof.cc",
    "content": "#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_fri_proof.h\"\n\n#include <vector>\n\n#include \"tachyon/base/auto_reset.h\"\n#include \"tachyon/base/buffer/buffer.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_fri_proof_type_traits.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_hintable.h\"\n\nusing namespace tachyon;\n\nusing F = math::BabyBear;\nusing Proof = crypto::FRIProof<c::zk::air::sp1::baby_bear::PCS::Base>;\n\ntachyon_sp1_baby_bear_poseidon2_fri_proof*\ntachyon_sp1_baby_bear_poseidon2_fri_proof_create() {\n  return c::base::c_cast(new Proof());\n}\n\ntachyon_sp1_baby_bear_poseidon2_fri_proof*\ntachyon_sp1_baby_bear_poseidon2_fri_proof_clone(\n    const tachyon_sp1_baby_bear_poseidon2_fri_proof* fri_proof) {\n  return c::base::c_cast(new Proof(c::base::native_cast(*fri_proof)));\n}\n\nvoid tachyon_sp1_baby_bear_poseidon2_fri_proof_destroy(\n    tachyon_sp1_baby_bear_poseidon2_fri_proof* fri_proof) {\n  delete c::base::native_cast(fri_proof);\n}\n\nvoid tachyon_sp1_baby_bear_poseidon2_fri_proof_write_hint(\n    const tachyon_sp1_baby_bear_poseidon2_fri_proof* fri_proof, uint8_t* data,\n    size_t* data_len) {\n  std::vector<std::vector<c::zk::air::sp1::Block<F>>> hints =\n      c::zk::air::sp1::baby_bear::WriteHint(c::base::native_cast(*fri_proof));\n  *data_len = base::EstimateSize(hints);\n  if (data == nullptr) return;\n\n  base::AutoReset<bool> auto_reset(&base::Copyable<F>::s_is_in_montgomery,\n                                   true);\n  base::Buffer buffer(data, *data_len);\n  CHECK(buffer.Write(hints));\n  CHECK(buffer.Done());\n}\n\nvoid tachyon_sp1_baby_bear_poseidon2_fri_proof_serialize(\n    const tachyon_sp1_baby_bear_poseidon2_fri_proof* fri_proof, uint8_t* data,\n    size_t* data_len) {\n  *data_len = base::EstimateSize(c::base::native_cast(*fri_proof));\n  if (data == nullptr) return;\n\n  base::AutoReset<bool> auto_reset(&base::Copyable<F>::s_is_in_montgomery,\n                                   true);\n  base::Buffer buffer(data, *data_len);\n  CHECK(buffer.Write(c::base::native_cast(*fri_proof)));\n  CHECK(buffer.Done());\n}\n\ntachyon_sp1_baby_bear_poseidon2_fri_proof*\ntachyon_sp1_baby_bear_poseidon2_fri_proof_deserialize(const uint8_t* data,\n                                                      size_t data_len) {\n  base::AutoReset<bool> auto_reset(&base::Copyable<F>::s_is_in_montgomery,\n                                   true);\n  Proof* fri_proof = new Proof();\n  base::ReadOnlyBuffer buffer(data, data_len);\n  CHECK(buffer.Read(fri_proof));\n  return c::base::c_cast(fri_proof);\n}\n"
  },
  {
    "path": "tachyon/c/zk/air/sp1/baby_bear_poseidon2_fri_proof.h",
    "content": "/**\n * @file baby_bear_poseidon2_fri_proof.h\n * @brief Defines the interface for the fri proof used within the\n * SP1(BabyBear + Poseidon2) proof system.\n */\n\n#ifndef TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_FRI_PROOF_H_\n#define TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_FRI_PROOF_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include \"tachyon/c/export.h\"\n\nstruct tachyon_sp1_baby_bear_poseidon2_fri_proof {};\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Creates a new fri proof.\n *\n * @return A pointer to the newly created fri proof.\n */\nTACHYON_C_EXPORT tachyon_sp1_baby_bear_poseidon2_fri_proof*\ntachyon_sp1_baby_bear_poseidon2_fri_proof_create();\n\n/**\n * @brief Clones an existing fri proof.\n *\n * Creates a deep copy of the given fri proof.\n *\n * @param fri_proof A const pointer to the fri proof to\n * clone.\n * @return A pointer to the cloned fri proof.\n */\nTACHYON_C_EXPORT tachyon_sp1_baby_bear_poseidon2_fri_proof*\ntachyon_sp1_baby_bear_poseidon2_fri_proof_clone(\n    const tachyon_sp1_baby_bear_poseidon2_fri_proof* fri_proof);\n\n/**\n * @brief Destroys a fri proof, freeing its resources.\n *\n * @param fri_proof A pointer to the fri proof to destroy.\n */\nTACHYON_C_EXPORT void tachyon_sp1_baby_bear_poseidon2_fri_proof_destroy(\n    tachyon_sp1_baby_bear_poseidon2_fri_proof* fri_proof);\n\n/**\n * @brief Writes a hint of the fri proof to the byte array.\n * See\n * https://github.com/succinctlabs/sp1/blob/6f67afd/crates/recursion/program/src/fri/hints.rs.\n *\n * @param fri_proof A const pointer to the set of fri proof.\n * @param data A pointer to the byte array.\n * @param data_len A pointer to store the length of the byte array.\n */\nTACHYON_C_EXPORT void tachyon_sp1_baby_bear_poseidon2_fri_proof_write_hint(\n    const tachyon_sp1_baby_bear_poseidon2_fri_proof* fri_proof, uint8_t* data,\n    size_t* data_len);\n\n/**\n * @brief Serializes a fri proof to the byte array.\n *\n * @param fri_proof A const pointer to the fri proof.\n * @param data A pointer to the byte array.\n * @param data_len A pointer to store the length of the byte array.\n */\nTACHYON_C_EXPORT void tachyon_sp1_baby_bear_poseidon2_fri_proof_serialize(\n    const tachyon_sp1_baby_bear_poseidon2_fri_proof* fri_proof, uint8_t* data,\n    size_t* data_len);\n\n/**\n * @brief Deserializes a fri proof from the byte array.\n *\n * @param data A const pointer to the byte array.\n * @param data_len The length of the byte array.\n * @return A pointer to the deserialized fri proof.\n */\nTACHYON_C_EXPORT tachyon_sp1_baby_bear_poseidon2_fri_proof*\ntachyon_sp1_baby_bear_poseidon2_fri_proof_deserialize(const uint8_t* data,\n                                                      size_t data_len);\n\n#ifdef __cplusplus\n}  // extern \"C\"\n#endif\n\n#endif  // TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_FRI_PROOF_H_\n"
  },
  {
    "path": "tachyon/c/zk/air/sp1/baby_bear_poseidon2_fri_proof_type_traits.h",
    "content": "#ifndef TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_FRI_PROOF_TYPE_TRAITS_H_\n#define TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_FRI_PROOF_TYPE_TRAITS_H_\n\n#include \"tachyon/c/base/type_traits_forward.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_fri_proof.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_two_adic_fri_type_traits.h\"\n#include \"tachyon/crypto/commitments/fri/fri_proof.h\"\n\nnamespace tachyon::c::base {\n\ntemplate <>\nstruct TypeTraits<\n    tachyon::crypto::FRIProof<zk::air::sp1::baby_bear::PCS::Base>> {\n  using CType = tachyon_sp1_baby_bear_poseidon2_fri_proof;\n};\n\ntemplate <>\nstruct TypeTraits<tachyon_sp1_baby_bear_poseidon2_fri_proof> {\n  using NativeType =\n      tachyon::crypto::FRIProof<zk::air::sp1::baby_bear::PCS::Base>;\n};\n\n}  // namespace tachyon::c::base\n\n#endif  // TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_FRI_PROOF_TYPE_TRAITS_H_\n"
  },
  {
    "path": "tachyon/c/zk/air/sp1/baby_bear_poseidon2_hintable.h",
    "content": "#ifndef TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_HINTABLE_H_\n#define TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_HINTABLE_H_\n\n#include <stddef.h>\n\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"absl/container/inlined_vector.h\"\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_fields.h\"\n#include \"tachyon/c/zk/air/sp1/block.h\"\n#include \"tachyon/crypto/challenger/duplex_challenger.h\"\n#include \"tachyon/crypto/commitments/fri/fri_proof.h\"\n#include \"tachyon/math/finite_fields/finite_field_traits.h\"\n#include \"tachyon/math/matrix/matrix_types.h\"\n\nnamespace tachyon::c::zk::air::sp1::baby_bear {\n\ntemplate <typename T, typename SFINAE = void>\nclass Hintable;\n\ntemplate <typename T>\nauto WriteHint(T&& value) {\n  return Hintable<std::decay_t<T>>::Write(std::forward<T>(value));\n}\n\ntemplate <typename... Args>\nsize_t EstimateSize(const Args&... args) {\n  return (... + Hintable<Args>::EstimateSize(args));\n}\n\ntemplate <>\nclass Hintable<size_t> {\n public:\n  static std::vector<std::vector<Block<F>>> Write(size_t value) {\n    return {{Block<F>::From(F(static_cast<uint32_t>(value)))}};\n  }\n\n  constexpr static size_t EstimateSize(size_t) { return 1; }\n};\n\ntemplate <>\nclass Hintable<Eigen::Index> {\n public:\n  static std::vector<std::vector<Block<F>>> Write(Eigen::Index value) {\n    return {{Block<F>::From(F(static_cast<uint32_t>(value)))}};\n  }\n\n  constexpr static size_t EstimateSize(size_t) { return 1; }\n};\n\ntemplate <size_t N>\nclass Hintable<std::array<F, N>> {\n public:\n  static std::vector<std::vector<Block<F>>> Write(\n      const std::array<F, N>& values) {\n    return {tachyon::base::Map(values,\n                               [](F value) { return Block<F>::From(value); })};\n  }\n\n  constexpr static size_t EstimateSize(const std::array<F, N>& values) {\n    return 1;\n  }\n};\n\ntemplate <>\nclass Hintable<std::vector<F>> {\n public:\n  static std::vector<std::vector<Block<F>>> Write(\n      const std::vector<F>& values) {\n    return {tachyon::base::Map(values,\n                               [](F value) { return Block<F>::From(value); })};\n  }\n\n  constexpr static size_t EstimateSize(const std::vector<F>& values) {\n    return 1;\n  }\n};\n\ntemplate <>\nclass Hintable<math::Vector<F>> {\n public:\n  static std::vector<std::vector<Block<F>>> Write(\n      const math::Vector<F>& values) {\n    return {tachyon::base::Map(values,\n                               [](F value) { return Block<F>::From(value); })};\n  }\n\n  constexpr static size_t EstimateSize(const math::Vector<F>& values) {\n    return 1;\n  }\n};\n\ntemplate <size_t N>\nclass Hintable<absl::InlinedVector<F, N>> {\n public:\n  static std::vector<std::vector<Block<F>>> Write(\n      const absl::InlinedVector<F, N>& values) {\n    return {tachyon::base::Map(values,\n                               [](F value) { return Block<F>::From(value); })};\n  }\n\n  constexpr static size_t EstimateSize(\n      const absl::InlinedVector<F, N>& values) {\n    return 1;\n  }\n};\n\ntemplate <typename T>\nclass Hintable<std::vector<T>> {\n public:\n  static std::vector<std::vector<Block<F>>> Write(\n      const std::vector<T>& values) {\n    std::vector<std::vector<Block<F>>> ret;\n    ret.reserve(EstimateSize(values));\n    ret.push_back({Block<F>::From(F(values.size()))});\n    for (size_t i = 0; i < values.size(); ++i) {\n      tachyon::base::Extend(ret, WriteHint(values[i]));\n    }\n    return ret;\n  }\n\n  constexpr static size_t EstimateSize(const std::vector<T>& values) {\n    return std::accumulate(values.begin(), values.end(), 1,\n                           [](size_t total, const T& value) {\n                             return total + baby_bear::EstimateSize(value);\n                           });\n  }\n};\n\ntemplate <typename Params>\nclass Hintable<tachyon::crypto::SpongeState<Params>> {\n public:\n  static std::vector<std::vector<Block<F>>> Write(\n      const tachyon::crypto::SpongeState<Params>& value) {\n    return WriteHint(value.elements);\n  }\n\n  constexpr static size_t EstimateSize(\n      const tachyon::crypto::SpongeState<Params>& value) {\n    return baby_bear::EstimateSize(value.elements);\n  }\n};\n\ntemplate <typename Permutation, size_t R>\nclass Hintable<tachyon::crypto::DuplexChallenger<Permutation, R>> {\n public:\n  using Params = typename Permutation::Params;\n\n  static std::vector<std::vector<Block<F>>> Write(\n      const tachyon::crypto::DuplexChallenger<Permutation, R>& value) {\n    std::vector<std::vector<Block<F>>> ret;\n    ret.reserve(EstimateSize(value));\n    tachyon::base::Extend(ret, WriteHint(value.state()));\n    tachyon::base::Extend(ret, WriteHint(value.input_buffer().size()));\n    absl::InlinedVector<F, R> input_buffer_padded = value.input_buffer();\n    input_buffer_padded.resize(Params::kWidth, F::Zero());\n    tachyon::base::Extend(ret, WriteHint(input_buffer_padded));\n    tachyon::base::Extend(ret, WriteHint(value.output_buffer().size()));\n    absl::InlinedVector<F, Params::kWidth> output_buffer_padded =\n        value.output_buffer();\n    output_buffer_padded.resize(Params::kWidth, F::Zero());\n    tachyon::base::Extend(ret, WriteHint(output_buffer_padded));\n    return ret;\n  }\n\n  constexpr static size_t EstimateSize(\n      const tachyon::crypto::DuplexChallenger<Permutation, R>& value) {\n    return baby_bear::EstimateSize(\n        value.state(), value.input_buffer().size(), value.input_buffer(),\n        value.output_buffer().size(), value.output_buffer());\n  }\n};\n\ntemplate <typename PCS>\nclass Hintable<tachyon::crypto::BatchOpening<PCS>> {\n public:\n  static std::vector<std::vector<Block<F>>> Write(\n      const tachyon::crypto::BatchOpening<PCS>& value) {\n    std::vector<std::vector<Block<F>>> ret;\n    ret.reserve(EstimateSize(value));\n    tachyon::base::Extend(ret, WriteHint(value.opened_values));\n    tachyon::base::Extend(ret, WriteHint(value.opening_proof));\n    return ret;\n  }\n\n  constexpr static size_t EstimateSize(\n      const tachyon::crypto::BatchOpening<PCS>& value) {\n    return baby_bear::EstimateSize(value.opened_values, value.opening_proof);\n  }\n};\n\ntemplate <typename PCS>\nclass Hintable<tachyon::crypto::CommitPhaseProofStep<PCS>> {\n public:\n  static std::vector<std::vector<Block<F>>> Write(\n      const tachyon::crypto::CommitPhaseProofStep<PCS>& value) {\n    std::vector<std::vector<Block<F>>> ret;\n    ret.reserve(EstimateSize(value));\n    ret.push_back({Block<F>::From(value.sibling_value)});\n    tachyon::base::Extend(ret, WriteHint(value.opening_proof));\n    return ret;\n  }\n\n  constexpr static size_t EstimateSize(\n      const tachyon::crypto::CommitPhaseProofStep<PCS>& value) {\n    return 1 + baby_bear::EstimateSize(value.opening_proof);\n  }\n};\n\ntemplate <typename PCS>\nclass Hintable<tachyon::crypto::QueryProof<PCS>> {\n public:\n  static std::vector<std::vector<Block<F>>> Write(\n      const tachyon::crypto::QueryProof<PCS>& value) {\n    std::vector<std::vector<Block<F>>> ret;\n    ret.reserve(EstimateSize(value));\n    // NOTE(chokobole): |input_proof| shouldn't be included here.\n    // See\n    // https://github.com/succinctlabs/sp1/blob/6f67afd/crates/recursion/program/src/fri/hints.rs#L123-L129.\n    tachyon::base::Extend(ret, WriteHint(value.commit_phase_openings));\n    return ret;\n  }\n\n  constexpr static size_t EstimateSize(\n      const tachyon::crypto::QueryProof<PCS>& value) {\n    return baby_bear::EstimateSize(value.commit_phase_openings);\n  }\n};\n\ntemplate <typename PCS>\nclass Hintable<tachyon::crypto::FRIProof<PCS>> {\n public:\n  static std::vector<std::vector<Block<F>>> Write(\n      const tachyon::crypto::FRIProof<PCS>& value) {\n    std::vector<std::vector<Block<F>>> ret;\n    size_t ret_size = EstimateSize(value);\n    ret.reserve(ret_size);\n    tachyon::base::Extend(ret, WriteHint(value.commit_phase_commits));\n    tachyon::base::Extend(ret, WriteHint(value.query_proofs));\n    ret.push_back({Block<F>::From(value.final_eval)});\n    ret.push_back({Block<F>::From(value.pow_witness)});\n    // NOTE(chokobole): |query_proofs[i].input_proof| should be included here.\n    // See\n    // https://github.com/succinctlabs/sp1/blob/6f67afd/crates/recursion/program/src/fri/hints.rs#L279.\n    std::vector<std::vector<Block<F>>> query_openings;\n    size_t query_openings_size = std::accumulate(\n        value.query_proofs.begin(), value.query_proofs.end(), 1,\n        [](size_t total, const tachyon::crypto::QueryProof<PCS>& proof) {\n          return total + baby_bear::EstimateSize(proof.input_proof);\n        });\n    query_openings.reserve(query_openings_size);\n    query_openings.push_back({Block<F>::From(F(value.query_proofs.size()))});\n    for (size_t i = 0; i < value.query_proofs.size(); ++i) {\n      tachyon::base::Extend(query_openings,\n                            WriteHint(value.query_proofs[i].input_proof));\n    }\n    CHECK_EQ(query_openings.size(), query_openings_size);\n    tachyon::base::Extend(ret, std::move(query_openings));\n    CHECK_EQ(ret.size(), ret_size);\n    return ret;\n  }\n\n  constexpr static size_t EstimateSize(\n      const tachyon::crypto::FRIProof<PCS>& value) {\n    size_t query_openings_size = std::accumulate(\n        value.query_proofs.begin(), value.query_proofs.end(), 1,\n        [](size_t total, const tachyon::crypto::QueryProof<PCS>& proof) {\n          return total + baby_bear::EstimateSize(proof.input_proof);\n        });\n    return baby_bear::EstimateSize(value.commit_phase_commits,\n                                   value.query_proofs) +\n           2 + query_openings_size;\n  }\n};\n\n}  // namespace tachyon::c::zk::air::sp1::baby_bear\n\n#endif  // TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_HINTABLE_H_\n"
  },
  {
    "path": "tachyon/c/zk/air/sp1/baby_bear_poseidon2_hintable_unittest.cc",
    "content": "#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_hintable.h\"\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_two_adic_fri_type_traits.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n\nnamespace tachyon {\n\nusing F = c::zk::air::sp1::baby_bear::F;\nusing Params = c::zk::air::sp1::baby_bear::Params;\nusing Poseidon2 = c::zk::air::sp1::baby_bear::Poseidon2;\nusing Block = c::zk::air::sp1::Block<F>;\n\nconstexpr size_t kRate = 4;\n\nclass HintableTest : public math::FiniteFieldTest<F> {};\n\nTEST_F(HintableTest, DuplexChallenger) {\n  crypto::DuplexChallenger<Poseidon2, kRate> challenger;\n  for (size_t i = 0; i < Params::kWidth; ++i) {\n    challenger.state_[i] = F(i);\n  }\n  for (size_t i = 0; i < kRate / 2; ++i) {\n    challenger.input_buffer_.push_back(F(i));\n  }\n  for (size_t i = 0; i < Params::kWidth / 2; ++i) {\n    challenger.output_buffer_.push_back(F(i));\n  }\n\n  std::vector<std::vector<Block>> actual =\n      c::zk::air::sp1::baby_bear::WriteHint(challenger);\n\n  std::vector<std::vector<Block>> expected = {\n      {base::CreateVector(Params::kWidth,\n                          [](size_t i) { return Block::From(F(i)); })},\n      {Block::From(F(kRate / 2))},\n      {base::CreateVector(Params::kWidth,\n                          [](size_t i) {\n                            if (i < kRate / 2) {\n                              return Block::From(F(i));\n                            } else {\n                              return Block();\n                            }\n                          })},\n      {Block::From(F(Params::kWidth / 2))},\n      {base::CreateVector(Params::kWidth,\n                          [](size_t i) {\n                            if (i < Params::kWidth / 2) {\n                              return Block::From(F(i));\n                            } else {\n                              return Block();\n                            }\n                          })},\n  };\n\n  EXPECT_EQ(actual.size(),\n            c::zk::air::sp1::baby_bear::EstimateSize(challenger));\n  EXPECT_EQ(actual, expected);\n}\n\n}  // namespace tachyon\n"
  },
  {
    "path": "tachyon/c/zk/air/sp1/baby_bear_poseidon2_lde_vec.cc",
    "content": "\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_lde_vec.h\"\n\n#include <vector>\n\n#include \"tachyon/c/math/finite_fields/baby_bear/baby_bear_type_traits.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_lde_vec_type_traits.h\"\n\nusing namespace tachyon;\n\nusing LDEVec =\n    std::vector<Eigen::Map<const math::RowMajorMatrix<math::BabyBear>>>;\n\ntachyon_sp1_baby_bear_poseidon2_lde_vec*\ntachyon_sp1_baby_bear_poseidon2_lde_vec_create() {\n  return c::base::c_cast(new LDEVec());\n}\n\nvoid tachyon_sp1_baby_bear_poseidon2_lde_vec_destroy(\n    tachyon_sp1_baby_bear_poseidon2_lde_vec* lde_vec) {\n  delete c::base::native_cast(lde_vec);\n}\n\nvoid tachyon_sp1_baby_bear_poseidon2_lde_vec_add(\n    tachyon_sp1_baby_bear_poseidon2_lde_vec* lde_vec,\n    const tachyon_baby_bear* lde, size_t rows, size_t cols) {\n  c::base::native_cast(*lde_vec).push_back(\n      Eigen::Map<const math::RowMajorMatrix<math::BabyBear>>(\n          c::base::native_cast(lde), rows, cols));\n}\n"
  },
  {
    "path": "tachyon/c/zk/air/sp1/baby_bear_poseidon2_lde_vec.h",
    "content": "/**\n * @file baby_bear_poseidon2_lde_vec.h\n * @brief Defines the interface for the lde vector used within the\n * SP1(BabyBear + Poseidon2) proof system.\n */\n\n#ifndef TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_LDE_VEC_H_\n#define TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_LDE_VEC_H_\n\n#include <stddef.h>\n\n#include \"tachyon/c/export.h\"\n#include \"tachyon/c/math/finite_fields/baby_bear/baby_bear.h\"\n\nstruct tachyon_sp1_baby_bear_poseidon2_lde_vec {};\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Creates a new lde vector.\n *\n * @return A pointer to the newly created lde vector.\n */\nTACHYON_C_EXPORT\ntachyon_sp1_baby_bear_poseidon2_lde_vec*\ntachyon_sp1_baby_bear_poseidon2_lde_vec_create();\n\n/**\n * @brief Destroys an lde vector, freeing its resources.\n *\n * @param lde_vec A pointer to the lde vector to destroy.\n */\nTACHYON_C_EXPORT void tachyon_sp1_baby_bear_poseidon2_lde_vec_destroy(\n    tachyon_sp1_baby_bear_poseidon2_lde_vec* lde_vec);\n\n/**\n * @brief Adds lde.\n *\n * @param lde_vec A pointer to the lde vector.\n * @param lde A const pointer to the lde.\n * @param rows The number of the rows.\n * @param cols The number of the cols.\n */\nTACHYON_C_EXPORT void tachyon_sp1_baby_bear_poseidon2_lde_vec_add(\n    tachyon_sp1_baby_bear_poseidon2_lde_vec* lde_vec,\n    const tachyon_baby_bear* lde, size_t rows, size_t cols);\n\n#ifdef __cplusplus\n}  // extern \"C\"\n#endif\n\n#endif  // TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_LDE_VEC_H_\n"
  },
  {
    "path": "tachyon/c/zk/air/sp1/baby_bear_poseidon2_lde_vec_type_traits.h",
    "content": "#ifndef TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_LDE_VEC_TYPE_TRAITS_H_\n#define TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_LDE_VEC_TYPE_TRAITS_H_\n\n#include <vector>\n\n#include \"tachyon/c/base/type_traits_forward.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_lde_vec.h\"\n#include \"tachyon/math/finite_fields/baby_bear/baby_bear.h\"\n#include \"tachyon/math/matrix/matrix_types.h\"\n\nnamespace tachyon::c::base {\n\ntemplate <>\nstruct TypeTraits<std::vector<\n    Eigen::Map<const tachyon::math::RowMajorMatrix<tachyon::math::BabyBear>>>> {\n  using CType = tachyon_sp1_baby_bear_poseidon2_lde_vec;\n};\n\ntemplate <>\nstruct TypeTraits<tachyon_sp1_baby_bear_poseidon2_lde_vec> {\n  using NativeType =\n      std::vector<Eigen::Map<const math::RowMajorMatrix<math::BabyBear>>>;\n};\n\n}  // namespace tachyon::c::base\n\n#endif  // TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_LDE_VEC_TYPE_TRAITS_H_\n"
  },
  {
    "path": "tachyon/c/zk/air/sp1/baby_bear_poseidon2_opened_values.cc",
    "content": "#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_opened_values.h\"\n\n#include <vector>\n\n#include \"tachyon/base/auto_reset.h\"\n#include \"tachyon/base/buffer/buffer.h\"\n#include \"tachyon/c/math/finite_fields/baby_bear/baby_bear4_type_traits.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_opened_values_type_traits.h\"\n\nusing namespace tachyon;\n\nusing OpenedValues =\n    std::vector<std::vector<std::vector<std::vector<math::BabyBear4>>>>;\n\ntachyon_sp1_baby_bear_poseidon2_opened_values*\ntachyon_sp1_baby_bear_poseidon2_opened_values_create(size_t rounds) {\n  return c::base::c_cast(new OpenedValues(rounds));\n}\n\ntachyon_sp1_baby_bear_poseidon2_opened_values*\ntachyon_sp1_baby_bear_poseidon2_opened_values_clone(\n    const tachyon_sp1_baby_bear_poseidon2_opened_values* opened_values) {\n  return c::base::c_cast(\n      new OpenedValues(c::base::native_cast(*opened_values)));\n}\n\nvoid tachyon_sp1_baby_bear_poseidon2_opened_values_destroy(\n    tachyon_sp1_baby_bear_poseidon2_opened_values* opened_values) {\n  delete c::base::native_cast(opened_values);\n}\n\nvoid tachyon_sp1_baby_bear_poseidon2_opened_values_serialize(\n    const tachyon_sp1_baby_bear_poseidon2_opened_values* opened_values,\n    uint8_t* data, size_t* data_len) {\n  *data_len = base::EstimateSize(c::base::native_cast(*opened_values));\n  if (data == nullptr) return;\n  base::AutoReset<bool> auto_reset(\n      &base::Copyable<math::BabyBear>::s_is_in_montgomery, true);\n  base::Buffer buffer(data, *data_len);\n  CHECK(buffer.Write(c::base::native_cast(*opened_values)));\n}\n\nvoid tachyon_sp1_baby_bear_poseidon2_opened_values_allocate_outer(\n    tachyon_sp1_baby_bear_poseidon2_opened_values* opened_values, size_t round,\n    size_t rows, size_t cols) {\n  std::vector<std::vector<std::vector<math::BabyBear4>>>& mat =\n      c::base::native_cast(*opened_values)[round];\n  mat.resize(rows);\n  for (size_t r = 0; r < rows; ++r) {\n    mat[r].resize(cols);\n  }\n}\n\nvoid tachyon_sp1_baby_bear_poseidon2_opened_values_allocate_inner(\n    tachyon_sp1_baby_bear_poseidon2_opened_values* opened_values, size_t round,\n    size_t r, size_t cols, size_t size) {\n  std::vector<std::vector<std::vector<math::BabyBear4>>>& mat =\n      c::base::native_cast(*opened_values)[round];\n  std::vector<std::vector<math::BabyBear4>>& row = mat[r];\n  for (size_t c = 0; c < cols; ++c) {\n    row[c].resize(size);\n  }\n}\n\nvoid tachyon_sp1_baby_bear_poseidon2_opened_values_set(\n    tachyon_sp1_baby_bear_poseidon2_opened_values* opened_values, size_t round,\n    size_t row, size_t col, size_t idx, const tachyon_baby_bear4* value) {\n  c::base::native_cast(*opened_values)[round][row][col][idx] =\n      c::base::native_cast(*value);\n}\n"
  },
  {
    "path": "tachyon/c/zk/air/sp1/baby_bear_poseidon2_opened_values.h",
    "content": "/**\n * @file baby_bear_poseidon2_opened_values.h\n * @brief Defines the interface for the set of opened values used within the\n * SP1(BabyBear + Poseidon2) proof system.\n */\n\n#ifndef TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_OPENED_VALUES_H_\n#define TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_OPENED_VALUES_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include \"tachyon/c/export.h\"\n#include \"tachyon/c/math/finite_fields/baby_bear/baby_bear4.h\"\n\nstruct tachyon_sp1_baby_bear_poseidon2_opened_values {};\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Creates a new set of opened values.\n *\n * @param rounds The number of rounds.\n * @return A pointer to the newly created set of opened values.\n */\nTACHYON_C_EXPORT tachyon_sp1_baby_bear_poseidon2_opened_values*\ntachyon_sp1_baby_bear_poseidon2_opened_values_create(size_t rounds);\n\n/**\n * @brief Clones an existing set of opened values.\n *\n * Creates a deep copy of the given set of opened values.\n *\n * @param opened_values A const pointer to the set of opened values to\n * clone.\n * @return A pointer to the cloned set of opened values.\n */\nTACHYON_C_EXPORT tachyon_sp1_baby_bear_poseidon2_opened_values*\ntachyon_sp1_baby_bear_poseidon2_opened_values_clone(\n    const tachyon_sp1_baby_bear_poseidon2_opened_values* opened_values);\n\n/**\n * @brief Destroys a set of opened values, freeing its resources.\n *\n * @param opened_values A pointer to the set of opened values to destroy.\n */\nTACHYON_C_EXPORT void tachyon_sp1_baby_bear_poseidon2_opened_values_destroy(\n    tachyon_sp1_baby_bear_poseidon2_opened_values* opened_values);\n\n/**\n * @brief Serializes a set of opened values to the byte array.\n *\n * @param opened_values A const pointer to the set of opened values.\n * @param data A pointer to the byte array.\n * @param data_len A pointer to store the length of the byte array.\n */\nTACHYON_C_EXPORT void tachyon_sp1_baby_bear_poseidon2_opened_values_serialize(\n    const tachyon_sp1_baby_bear_poseidon2_opened_values* opened_values,\n    uint8_t* data, size_t* data_len);\n\n/**\n * @brief Allocates outer memory for the set of opened values.\n *\n * @param opened_values A pointer to the set of opened values.\n * @param round The round index of the point.\n * @param rows The number of rows.\n * @param cols The number of columns.\n */\nTACHYON_C_EXPORT void\ntachyon_sp1_baby_bear_poseidon2_opened_values_allocate_outer(\n    tachyon_sp1_baby_bear_poseidon2_opened_values* opened_values, size_t round,\n    size_t rows, size_t cols);\n\n/**\n * @brief Allocates inner memory for the set of opened values.\n *\n * @param opened_values A pointer to the set of opened values.\n * @param round The round index of the point.\n * @param row The row index of the value.\n * @param cols The number of columns.\n * @param size The size of the values.\n */\nTACHYON_C_EXPORT void\ntachyon_sp1_baby_bear_poseidon2_opened_values_allocate_inner(\n    tachyon_sp1_baby_bear_poseidon2_opened_values* opened_values, size_t round,\n    size_t row, size_t cols, size_t size);\n\n/**\n * @brief Sets value.\n *\n * @param opened_values A pointer to the set of opened values.\n * @param round The round index of the point.\n * @param row The row index of the value.\n * @param col The column index of the value.\n * @param idx The index of the value.\n * @param value A const pointer to the value.\n */\nTACHYON_C_EXPORT void tachyon_sp1_baby_bear_poseidon2_opened_values_set(\n    tachyon_sp1_baby_bear_poseidon2_opened_values* opened_values, size_t round,\n    size_t row, size_t col, size_t idx, const tachyon_baby_bear4* value);\n\n#ifdef __cplusplus\n}  // extern \"C\"\n#endif\n\n#endif  // TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_OPENED_VALUES_H_\n"
  },
  {
    "path": "tachyon/c/zk/air/sp1/baby_bear_poseidon2_opened_values_type_traits.h",
    "content": "#ifndef TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_OPENED_VALUES_TYPE_TRAITS_H_\n#define TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_OPENED_VALUES_TYPE_TRAITS_H_\n\n#include <vector>\n\n#include \"tachyon/c/base/type_traits_forward.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_opened_values.h\"\n#include \"tachyon/math/finite_fields/baby_bear/baby_bear4.h\"\n\nnamespace tachyon::c::base {\n\ntemplate <>\nstruct TypeTraits<std::vector<\n    std::vector<std::vector<std::vector<tachyon::math::BabyBear4>>>>> {\n  using CType = tachyon_sp1_baby_bear_poseidon2_opened_values;\n};\n\ntemplate <>\nstruct TypeTraits<tachyon_sp1_baby_bear_poseidon2_opened_values> {\n  using NativeType = std::vector<\n      std::vector<std::vector<std::vector<tachyon::math::BabyBear4>>>>;\n};\n\n}  // namespace tachyon::c::base\n\n#endif  // TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_OPENED_VALUES_TYPE_TRAITS_H_\n"
  },
  {
    "path": "tachyon/c/zk/air/sp1/baby_bear_poseidon2_opened_values_unittest.cc",
    "content": "#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_opened_values.h\"\n\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/auto_reset.h\"\n#include \"tachyon/base/buffer/read_only_buffer.h\"\n#include \"tachyon/base/random.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_opened_values_type_traits.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\nusing ExtF = BabyBear4;\nusing OpenedValues = std::vector<std::vector<std::vector<std::vector<ExtF>>>>;\n\nclass OpenedValuesTest : public FiniteFieldTest<ExtF> {};\n\nsize_t GetRandom() { return base::Uniform(base::Range<size_t>(1, 3)); }\n\n}  // namespace\n\nTEST_F(OpenedValuesTest, Serialize) {\n  OpenedValues opened_values = base::CreateVector(GetRandom(), []() {\n    return base::CreateVector(GetRandom(), []() {\n      return base::CreateVector(GetRandom(), []() {\n        return base::CreateVector(GetRandom(), []() { return ExtF::Random(); });\n      });\n    });\n  });\n  size_t data_len;\n  tachyon_sp1_baby_bear_poseidon2_opened_values_serialize(\n      c::base::c_cast(&opened_values), nullptr, &data_len);\n  ASSERT_NE(data_len, size_t{0});\n\n  std::vector<uint8_t> data(data_len);\n  tachyon_sp1_baby_bear_poseidon2_opened_values_serialize(\n      c::base::c_cast(&opened_values), const_cast<uint8_t*>(data.data()),\n      &data_len);\n  ASSERT_EQ(data.size(), data_len);\n\n  base::ReadOnlyBuffer buffer(data.data(), data_len);\n  OpenedValues opened_values_deser;\n  base::AutoReset<bool> auto_reset(\n      &base::Copyable<BabyBear>::s_is_in_montgomery, true);\n  ASSERT_TRUE(buffer.Read(&opened_values_deser));\n  EXPECT_EQ(opened_values, opened_values_deser);\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/c/zk/air/sp1/baby_bear_poseidon2_opening_points.cc",
    "content": "#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_opening_points.h\"\n\n#include <vector>\n\n#include \"tachyon/c/math/finite_fields/baby_bear/baby_bear4_type_traits.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_opening_points_type_traits.h\"\n\nusing namespace tachyon;\n\nusing OpeningPoints = std::vector<std::vector<std::vector<math::BabyBear4>>>;\n\ntachyon_sp1_baby_bear_poseidon2_opening_points*\ntachyon_sp1_baby_bear_poseidon2_opening_points_create(size_t rounds) {\n  return c::base::c_cast(new OpeningPoints(rounds));\n}\n\ntachyon_sp1_baby_bear_poseidon2_opening_points*\ntachyon_sp1_baby_bear_poseidon2_opening_points_clone(\n    const tachyon_sp1_baby_bear_poseidon2_opening_points* opening_points) {\n  return c::base::c_cast(\n      new OpeningPoints(c::base::native_cast(*opening_points)));\n}\n\nvoid tachyon_sp1_baby_bear_poseidon2_opening_points_destroy(\n    tachyon_sp1_baby_bear_poseidon2_opening_points* opening_points) {\n  delete c::base::native_cast(opening_points);\n}\n\nvoid tachyon_sp1_baby_bear_poseidon2_opening_points_allocate(\n    tachyon_sp1_baby_bear_poseidon2_opening_points* opening_points,\n    size_t round, size_t rows, size_t cols) {\n  std::vector<std::vector<math::BabyBear4>>& mat =\n      c::base::native_cast(*opening_points)[round];\n  mat.resize(rows);\n  for (size_t r = 0; r < rows; ++r) {\n    mat[r].resize(cols);\n  }\n}\n\nvoid tachyon_sp1_baby_bear_poseidon2_opening_points_set(\n    tachyon_sp1_baby_bear_poseidon2_opening_points* opening_points,\n    size_t round, size_t row, size_t col, const tachyon_baby_bear4* point) {\n  c::base::native_cast(*opening_points)[round][row][col] =\n      c::base::native_cast(*point);\n}\n"
  },
  {
    "path": "tachyon/c/zk/air/sp1/baby_bear_poseidon2_opening_points.h",
    "content": "/**\n * @file baby_bear_poseidon2_opening_points.h\n * @brief Defines the interface for the set of opening points used within\n * the SP1(BabyBear + Poseidon2) proof system.\n */\n\n#ifndef TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_OPENING_POINTS_H_\n#define TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_OPENING_POINTS_H_\n\n#include <stddef.h>\n\n#include \"tachyon/c/export.h\"\n#include \"tachyon/c/math/finite_fields/baby_bear/baby_bear4.h\"\n\nstruct tachyon_sp1_baby_bear_poseidon2_opening_points {};\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Creates a new set of opening points.\n *\n * @param rounds The number of rounds.\n * @return A pointer to the newly created set of opening points.\n */\nTACHYON_C_EXPORT tachyon_sp1_baby_bear_poseidon2_opening_points*\ntachyon_sp1_baby_bear_poseidon2_opening_points_create(size_t rounds);\n\n/**\n * @brief Clones an existing set of opening points.\n *\n * Creates a deep copy of the given set of opening points.\n *\n * @param opening_points A const pointer to the set of opening points to\n * clone.\n * @return A pointer to the cloned set of opening points.\n */\nTACHYON_C_EXPORT tachyon_sp1_baby_bear_poseidon2_opening_points*\ntachyon_sp1_baby_bear_poseidon2_opening_points_clone(\n    const tachyon_sp1_baby_bear_poseidon2_opening_points* opening_points);\n\n/**\n * @brief Destroys a set of opening points, freeing its resources.\n *\n * @param opening_points A pointer to the set of opening points to destroy.\n */\nTACHYON_C_EXPORT void tachyon_sp1_baby_bear_poseidon2_opening_points_destroy(\n    tachyon_sp1_baby_bear_poseidon2_opening_points* opening_points);\n\n/**\n * @brief Allocates memory for the set of opening points.\n *\n * @param opening_points A pointer to the set of opening points.\n * @param round The round index of the point.\n * @param rows The number of rows.\n * @param cols The number of columns.\n */\nTACHYON_C_EXPORT void tachyon_sp1_baby_bear_poseidon2_opening_points_allocate(\n    tachyon_sp1_baby_bear_poseidon2_opening_points* opening_points,\n    size_t round, size_t rows, size_t cols);\n\n/**\n * @brief Sets point.\n *\n * @param opening_points A pointer to the set of opening points.\n * @param round The round index of the point.\n * @param row The row index of the point.\n * @param col The column index of the point.\n * @param col A const pointer to the point.\n */\nTACHYON_C_EXPORT void tachyon_sp1_baby_bear_poseidon2_opening_points_set(\n    tachyon_sp1_baby_bear_poseidon2_opening_points* opening_points,\n    size_t round, size_t row, size_t col, const tachyon_baby_bear4* point);\n\n#ifdef __cplusplus\n}  // extern \"C\"\n#endif\n\n#endif  // TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_OPENING_POINTS_H_\n"
  },
  {
    "path": "tachyon/c/zk/air/sp1/baby_bear_poseidon2_opening_points_type_traits.h",
    "content": "#ifndef TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_OPENING_POINTS_TYPE_TRAITS_H_\n#define TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_OPENING_POINTS_TYPE_TRAITS_H_\n\n#include <vector>\n\n#include \"tachyon/c/base/type_traits_forward.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_opening_points.h\"\n#include \"tachyon/math/finite_fields/baby_bear/baby_bear4.h\"\n\nnamespace tachyon::c::base {\n\ntemplate <>\nstruct TypeTraits<\n    std::vector<std::vector<std::vector<tachyon::math::BabyBear4>>>> {\n  using CType = tachyon_sp1_baby_bear_poseidon2_opening_points;\n};\n\ntemplate <>\nstruct TypeTraits<tachyon_sp1_baby_bear_poseidon2_opening_points> {\n  using NativeType =\n      std::vector<std::vector<std::vector<tachyon::math::BabyBear4>>>;\n};\n\n}  // namespace tachyon::c::base\n\n#endif  // TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_OPENING_POINTS_TYPE_TRAITS_H_\n"
  },
  {
    "path": "tachyon/c/zk/air/sp1/baby_bear_poseidon2_two_adic_fri.cc",
    "content": "#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_two_adic_fri.h\"\n\n#include <utility>\n\n#include \"tachyon/c/math/finite_fields/baby_bear/baby_bear4_type_traits.h\"\n#include \"tachyon/c/math/finite_fields/baby_bear/baby_bear_type_traits.h\"\n#include \"tachyon/c/math/matrix/baby_bear_row_major_matrix_type_traits.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_commitment_vec_type_traits.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_domains_type_traits.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_duplex_challenger_type_traits.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_field_merkle_tree_type_traits.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_field_merkle_tree_vec_type_traits.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_fri_proof_type_traits.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_lde_vec_type_traits.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_opened_values_type_traits.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_opening_points_type_traits.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_two_adic_fri_type_traits.h\"\n\nusing namespace tachyon;\n\nusing F = math::BabyBear;\nusing PackedF = math::PackedBabyBear;\nusing ExtF = math::BabyBear4;\nusing ExtPackedF = math::PackedBabyBear4;\nusing Poseidon2 = c::zk::air::sp1::baby_bear::Poseidon2;\nusing PackedPoseidon2 = c::zk::air::sp1::baby_bear::PackedPoseidon2;\nusing Hasher = c::zk::air::sp1::baby_bear::Hasher;\nusing PackedHasher = c::zk::air::sp1::baby_bear::PackedHasher;\nusing Compressor = c::zk::air::sp1::baby_bear::Compressor;\nusing PackedCompressor = c::zk::air::sp1::baby_bear::PackedCompressor;\nusing Tree = c::zk::air::sp1::baby_bear::Tree;\nusing MMCS = c::zk::air::sp1::baby_bear::MMCS;\nusing ExtMMCS = c::zk::air::sp1::baby_bear::ExtMMCS;\nusing ChallengeMMCS = c::zk::air::sp1::baby_bear::ChallengeMMCS;\nusing PCS = c::zk::air::sp1::baby_bear::PCS;\nusing Params =\n    crypto::Poseidon2Params<crypto::Poseidon2Vendor::kPlonky3,\n                            crypto::Poseidon2Vendor::kPlonky3, F,\n                            TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_WIDTH - 1,\n                            TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_ALPHA>;\nusing PackedParams =\n    crypto::Poseidon2Params<crypto::Poseidon2Vendor::kPlonky3,\n                            crypto::Poseidon2Vendor::kPlonky3, PackedF,\n                            TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_WIDTH - 1,\n                            TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_ALPHA>;\n\ntachyon_sp1_baby_bear_poseidon2_two_adic_fri*\ntachyon_sp1_baby_bear_poseidon2_two_adic_fri_create(uint32_t log_blowup,\n                                                    size_t num_queries,\n                                                    size_t proof_of_work_bits) {\n  ExtF::Init();\n  ExtPackedF::Init();\n\n  math::Matrix<F> ark(TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_FULL_ROUNDS +\n                          TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_PARTIAL_ROUNDS,\n                      TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_WIDTH);\n  for (Eigen::Index r = 0; r < ark.rows(); ++r) {\n    for (Eigen::Index c = 0; c < ark.cols(); ++c) {\n      ark(r, c) = F(kRoundConstants[r][c] % F::Config::kModulus);\n    }\n  }\n\n  math::Matrix<PackedF> packed_ark(\n      TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_FULL_ROUNDS +\n          TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_PARTIAL_ROUNDS,\n      TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_WIDTH);\n  for (Eigen::Index r = 0; r < ark.rows(); ++r) {\n    for (Eigen::Index c = 0; c < ark.cols(); ++c) {\n      packed_ark(r, c) = PackedF::Broadcast(ark(r, c));\n    }\n  }\n\n  auto config = crypto::Poseidon2Config<Params>::CreateDefault(std::move(ark));\n  Poseidon2 sponge(std::move(config));\n  Hasher hasher(sponge);\n  Compressor compressor(std::move(sponge));\n\n  auto packed_config = crypto::Poseidon2Config<PackedParams>::CreateDefault(\n      std::move(packed_ark));\n  PackedPoseidon2 packed_sponge(std::move(packed_config));\n  PackedHasher packed_hasher(packed_sponge);\n  PackedCompressor packed_compressor(std::move(packed_sponge));\n  MMCS mmcs(hasher, packed_hasher, compressor, packed_compressor);\n\n  ChallengeMMCS challenge_mmcs(\n      ExtMMCS(std::move(hasher), std::move(packed_hasher),\n              std::move(compressor), std::move(packed_compressor)));\n\n  crypto::FRIConfig<ChallengeMMCS> fri_config{\n      log_blowup, num_queries, proof_of_work_bits, std::move(challenge_mmcs)};\n\n  return c::base::c_cast(new PCS(std::move(mmcs), std::move(fri_config)));\n}\n\nvoid tachyon_sp1_baby_bear_poseidon2_two_adic_fri_destroy(\n    tachyon_sp1_baby_bear_poseidon2_two_adic_fri* pcs) {\n  delete c::base::native_cast(pcs);\n}\n\nvoid tachyon_sp1_baby_bear_poseidon2_two_adic_fri_coset_lde_batch(\n    tachyon_sp1_baby_bear_poseidon2_two_adic_fri* pcs,\n    tachyon_baby_bear* values, size_t rows, size_t cols,\n    tachyon_baby_bear* extended_values, tachyon_baby_bear shift) {\n  PCS* native_pcs = c::base::native_cast(pcs);\n  Eigen::Map<math::RowMajorMatrix<F>> matrix(c::base::native_cast(values),\n                                             static_cast<Eigen::Index>(rows),\n                                             static_cast<Eigen::Index>(cols));\n  Eigen::Map<math::RowMajorMatrix<F>> extended_matrix(\n      c::base::native_cast(extended_values),\n      static_cast<Eigen::Index>(rows) << (native_pcs->config().log_blowup),\n      static_cast<Eigen::Index>(cols));\n  native_pcs->CosetLDEBatch(std::move(matrix), c::base::native_cast(shift),\n                            extended_matrix);\n}\n\nvoid tachyon_sp1_baby_bear_poseidon2_two_adic_fri_commit(\n    tachyon_sp1_baby_bear_poseidon2_two_adic_fri* pcs,\n    tachyon_sp1_baby_bear_poseidon2_lde_vec* lde_vec,\n    tachyon_baby_bear* commitment,\n    tachyon_sp1_baby_bear_poseidon2_field_merkle_tree** prover_data) {\n  using Commitment = MMCS::Commitment;\n  c::base::native_cast(pcs)->Commit(std::move(c::base::native_cast(*lde_vec)),\n                                    reinterpret_cast<Commitment*>(commitment),\n                                    reinterpret_cast<Tree**>(prover_data));\n}\n\nvoid tachyon_sp1_baby_bear_poseidon2_two_adic_fri_open(\n    const tachyon_sp1_baby_bear_poseidon2_two_adic_fri* pcs,\n    const tachyon_sp1_baby_bear_poseidon2_field_merkle_tree_vec*\n        prover_data_by_round,\n    const tachyon_sp1_baby_bear_poseidon2_opening_points* points_by_round,\n    tachyon_sp1_baby_bear_poseidon2_duplex_challenger* challenger,\n    tachyon_sp1_baby_bear_poseidon2_opened_values** opened_values,\n    tachyon_sp1_baby_bear_poseidon2_fri_proof** proof) {\n  c::base::native_cast(pcs)->CreateOpeningProof(\n      c::base::native_cast(*prover_data_by_round),\n      c::base::native_cast(*points_by_round), c::base::native_cast(*challenger),\n      c::base::native_cast(\n          reinterpret_cast<tachyon_sp1_baby_bear_poseidon2_opened_values*>(\n              *opened_values)),\n      c::base::native_cast(\n          reinterpret_cast<tachyon_sp1_baby_bear_poseidon2_fri_proof*>(\n              *proof)));\n}\n\nbool tachyon_sp1_baby_bear_poseidon2_two_adic_fri_verify(\n    const tachyon_sp1_baby_bear_poseidon2_two_adic_fri* pcs,\n    const tachyon_sp1_baby_bear_poseidon2_commitment_vec* commitments_by_round,\n    const tachyon_sp1_baby_bear_poseidon2_domains* domains_by_round,\n    const tachyon_sp1_baby_bear_poseidon2_opening_points* points_by_round,\n    const tachyon_sp1_baby_bear_poseidon2_opened_values* opened_values_by_round,\n    const tachyon_sp1_baby_bear_poseidon2_fri_proof* proof,\n    tachyon_sp1_baby_bear_poseidon2_duplex_challenger* challenger) {\n  return c::base::native_cast(pcs)->VerifyOpeningProof(\n      c::base::native_cast(*commitments_by_round),\n      c::base::native_cast(*domains_by_round),\n      c::base::native_cast(*points_by_round),\n      c::base::native_cast(*opened_values_by_round),\n      c::base::native_cast(*proof), c::base::native_cast(*challenger));\n}\n"
  },
  {
    "path": "tachyon/c/zk/air/sp1/baby_bear_poseidon2_two_adic_fri.h",
    "content": "/**\n * @file baby_bear_poseidon2_two_adic_fri.h\n * @brief Defines the interface for the two adic fri used within the\n * SP1(BabyBear + Poseidon2) proof system.\n */\n\n#ifndef TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_TWO_ADIC_FRI_H_\n#define TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_TWO_ADIC_FRI_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include \"tachyon/c/export.h\"\n#include \"tachyon/c/math/finite_fields/baby_bear/baby_bear4.h\"\n#include \"tachyon/c/math/matrix/baby_bear_row_major_matrix.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_commitment_vec.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_constants.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_domains.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_duplex_challenger.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_field_merkle_tree.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_field_merkle_tree_vec.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_fri_proof.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_lde_vec.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_opened_values.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_opening_points.h\"\n\nstruct tachyon_sp1_baby_bear_poseidon2_two_adic_fri {};\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Creates a new two adic fri.\n *\n * @param log_blowup The logarithmic blowup factor.\n * @param num_queries The number of queries.\n * @param proof_of_work_bits The proof of work bits.\n * @return A pointer to the newly created two adic fri.\n */\nTACHYON_C_EXPORT tachyon_sp1_baby_bear_poseidon2_two_adic_fri*\ntachyon_sp1_baby_bear_poseidon2_two_adic_fri_create(uint32_t log_blowup,\n                                                    size_t num_queries,\n                                                    size_t proof_of_work_bits);\n\n/**\n * @brief Destroys a two adic fri, freeing its resources.\n *\n * @param pcs A pointer to the two adic fri to destroy.\n */\nTACHYON_C_EXPORT void tachyon_sp1_baby_bear_poseidon2_two_adic_fri_destroy(\n    tachyon_sp1_baby_bear_poseidon2_two_adic_fri* pcs);\n\n/**\n * @brief Compute the low-degree extension of each column of the matrix onto a\n * coset of a larger subgroup.\n *\n * @param pcs A pointer to the two adic fri.\n * @param values A pointer to the data of the baby bear row major matrix.\n * @param rows The number of rows.\n * @param cols The number of columns.\n * @param extended_values A pointer to the data of the extended baby bear row\n * major matrix.\n * @param shift The shift value.\n */\nTACHYON_C_EXPORT void\ntachyon_sp1_baby_bear_poseidon2_two_adic_fri_coset_lde_batch(\n    tachyon_sp1_baby_bear_poseidon2_two_adic_fri* pcs,\n    tachyon_baby_bear* values, size_t rows, size_t cols,\n    tachyon_baby_bear* extended_values, tachyon_baby_bear shift);\n\n/**\n * @brief Commits to the lde vector.\n *\n * @param pcs A pointer to the two adic fri.\n * @param lde_vec A pointer to the lde vector.\n * @param commitment A pointer to store the commitment.\n * @param prover_data A pointer to store the field merkle tree.\n */\nTACHYON_C_EXPORT void tachyon_sp1_baby_bear_poseidon2_two_adic_fri_commit(\n    tachyon_sp1_baby_bear_poseidon2_two_adic_fri* pcs,\n    tachyon_sp1_baby_bear_poseidon2_lde_vec* lde_vec,\n    tachyon_baby_bear* commitment,\n    tachyon_sp1_baby_bear_poseidon2_field_merkle_tree** prover_data);\n\n/**\n * @brief Creates an opening proof with prover data and points.\n *\n * @param pcs A const pointer to the two adic fri pcs.\n * @param prover_data_by_round A const pointer to the field merkle tree vector.\n * @param points_by_round A const pointer to the opening points.\n * @param challenger A pointer to the duplex challenger.\n * @param opened_values A pointer to store the opened values.\n * @param proof A pointer to store the fri proof.\n */\nTACHYON_C_EXPORT void tachyon_sp1_baby_bear_poseidon2_two_adic_fri_open(\n    const tachyon_sp1_baby_bear_poseidon2_two_adic_fri* pcs,\n    const tachyon_sp1_baby_bear_poseidon2_field_merkle_tree_vec*\n        prover_data_by_round,\n    const tachyon_sp1_baby_bear_poseidon2_opening_points* points_by_round,\n    tachyon_sp1_baby_bear_poseidon2_duplex_challenger* challenger,\n    tachyon_sp1_baby_bear_poseidon2_opened_values** opened_values,\n    tachyon_sp1_baby_bear_poseidon2_fri_proof** proof);\n\n/**\n * @brief Verifies an opening proof with commitments.\n *\n * @param pcs A const pointer to the two adic fri pcs.\n * @param commitments_by_round A const pointer to the commitment vector.\n * @param domains_by_round A const pointer to the domains.\n * @param prover_data_by_round A const pointer to the field merkle tree vector.\n * @param points_by_round A const pointer to the opening points.\n * @param opened_values_by_round A const pointer to the opened values.\n * @param proof A const pointer to the fri proof.\n * @param challenger A pointer to the duplex challenger.\n */\nTACHYON_C_EXPORT bool tachyon_sp1_baby_bear_poseidon2_two_adic_fri_verify(\n    const tachyon_sp1_baby_bear_poseidon2_two_adic_fri* pcs,\n    const tachyon_sp1_baby_bear_poseidon2_commitment_vec* commitments_by_round,\n    const tachyon_sp1_baby_bear_poseidon2_domains* domains_by_round,\n    const tachyon_sp1_baby_bear_poseidon2_opening_points* points_by_round,\n    const tachyon_sp1_baby_bear_poseidon2_opened_values* opened_values_by_round,\n    const tachyon_sp1_baby_bear_poseidon2_fri_proof* proof,\n    tachyon_sp1_baby_bear_poseidon2_duplex_challenger* challenger);\n\n#ifdef __cplusplus\n}  // extern \"C\"\n#endif\n\n#endif  // TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_TWO_ADIC_FRI_H_\n"
  },
  {
    "path": "tachyon/c/zk/air/sp1/baby_bear_poseidon2_two_adic_fri_type_traits.h",
    "content": "#ifndef TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_TWO_ADIC_FRI_TYPE_TRAITS_H_\n#define TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_TWO_ADIC_FRI_TYPE_TRAITS_H_\n\n#include \"tachyon/c/base/type_traits_forward.h\"\n#include \"tachyon/c/crypto/commitments/fri/two_adic_fri_impl.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_constants.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_fields.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_two_adic_fri.h\"\n#include \"tachyon/crypto/challenger/duplex_challenger.h\"\n#include \"tachyon/crypto/commitments/fri/two_adic_multiplicative_coset.h\"\n#include \"tachyon/crypto/commitments/merkle_tree/field_merkle_tree/extension_field_merkle_tree_mmcs.h\"\n#include \"tachyon/crypto/commitments/merkle_tree/field_merkle_tree/field_merkle_tree_mmcs.h\"\n#include \"tachyon/crypto/hashes/sponge/padding_free_sponge.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/param_traits/poseidon2_baby_bear.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_params.h\"\n#include \"tachyon/crypto/hashes/sponge/truncated_permutation.h\"\n\nnamespace tachyon::c {\nnamespace zk::air::sp1::baby_bear {\n\nusing Params = tachyon::crypto::Poseidon2Params<\n    tachyon::crypto::Poseidon2Vendor::kPlonky3,\n    tachyon::crypto::Poseidon2Vendor::kPlonky3, F,\n    TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_WIDTH - 1,\n    TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_ALPHA>;\n\nusing PackedParams = tachyon::crypto::Poseidon2Params<\n    tachyon::crypto::Poseidon2Vendor::kPlonky3,\n    tachyon::crypto::Poseidon2Vendor::kPlonky3, PackedF,\n    TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_WIDTH - 1,\n    TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_ALPHA>;\n\nusing Poseidon2 = tachyon::crypto::Poseidon2Sponge<Params>;\n\nusing PackedPoseidon2 = tachyon::crypto::Poseidon2Sponge<PackedParams>;\n\nusing Hasher = tachyon::crypto::PaddingFreeSponge<\n    Poseidon2, TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_RATE,\n    TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_CHUNK>;\n\nusing PackedHasher = tachyon::crypto::PaddingFreeSponge<\n    PackedPoseidon2, TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_RATE,\n    TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_CHUNK>;\n\nusing Compressor = tachyon::crypto::TruncatedPermutation<\n    Poseidon2, TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_CHUNK,\n    TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_N>;\n\nusing PackedCompressor = tachyon::crypto::TruncatedPermutation<\n    PackedPoseidon2, TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_CHUNK,\n    TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_N>;\n\nusing Tree =\n    tachyon::crypto::FieldMerkleTree<F,\n                                     TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_CHUNK>;\n\nusing MMCS = tachyon::crypto::FieldMerkleTreeMMCS<\n    F, Hasher, PackedHasher, Compressor, PackedCompressor,\n    TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_CHUNK>;\n\nusing ExtMMCS = tachyon::crypto::FieldMerkleTreeMMCS<\n    ExtF, Hasher, PackedHasher, Compressor, PackedCompressor,\n    TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_CHUNK>;\n\nusing ExtMMCS = tachyon::crypto::FieldMerkleTreeMMCS<\n    ExtF, Hasher, PackedHasher, Compressor, PackedCompressor,\n    TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_CHUNK>;\n\nusing ChallengeMMCS =\n    tachyon::crypto::ExtensionFieldMerkleTreeMMCS<ExtF, ExtMMCS>;\n\nusing Challenger =\n    tachyon::crypto::DuplexChallenger<Poseidon2,\n                                      TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_RATE>;\n\nusing Coset = tachyon::crypto::TwoAdicMultiplicativeCoset<F>;\n\nusing PCS = crypto::TwoAdicFRIImpl<ExtF, MMCS, ChallengeMMCS, Challenger>;\n\n}  // namespace zk::air::sp1::baby_bear\n\nnamespace base {\n\ntemplate <>\nstruct TypeTraits<zk::air::sp1::baby_bear::PCS> {\n  using CType = tachyon_sp1_baby_bear_poseidon2_two_adic_fri;\n};\n\ntemplate <>\nstruct TypeTraits<tachyon_sp1_baby_bear_poseidon2_two_adic_fri> {\n  using NativeType = zk::air::sp1::baby_bear::PCS;\n};\n\n}  // namespace base\n}  // namespace tachyon::c\n\n#endif  // TACHYON_C_ZK_AIR_SP1_BABY_BEAR_POSEIDON2_TWO_ADIC_FRI_TYPE_TRAITS_H_\n"
  },
  {
    "path": "tachyon/c/zk/air/sp1/baby_bear_poseidon2_two_adic_fri_unittest.cc",
    "content": "#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_two_adic_fri.h\"\n\n#include <utility>\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/bits.h\"\n#include \"tachyon/c/math/finite_fields/baby_bear/baby_bear4_type_traits.h\"\n#include \"tachyon/c/math/finite_fields/baby_bear/baby_bear_type_traits.h\"\n#include \"tachyon/c/math/matrix/baby_bear_row_major_matrix_type_traits.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_commitment_vec_type_traits.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_domains_type_traits.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_duplex_challenger_type_traits.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_fri_proof_type_traits.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_opened_values_type_traits.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_opening_points_type_traits.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_two_adic_fri_type_traits.h\"\n\nnamespace tachyon {\n\nnamespace {\n\nusing F = math::BabyBear;\nusing ExtF = math::BabyBear4;\nusing MMCS = c::zk::air::sp1::baby_bear::MMCS;\nusing Coset = c::zk::air::sp1::baby_bear::Coset;\nusing PCS = c::zk::air::sp1::baby_bear::PCS;\n\nconstexpr uint32_t kLogBlowup = 1;\nconstexpr size_t kRounds = 1;\n\nclass TwoAdicFRITest : public testing::Test {\n public:\n  void SetUp() override {\n    pcs_ =\n        tachyon_sp1_baby_bear_poseidon2_two_adic_fri_create(kLogBlowup, 10, 8);\n    lde_vec_ = tachyon_sp1_baby_bear_poseidon2_lde_vec_create();\n    prover_data_vec_ =\n        tachyon_sp1_baby_bear_poseidon2_field_merkle_tree_vec_create(kRounds);\n    opening_points_ =\n        tachyon_sp1_baby_bear_poseidon2_opening_points_create(kRounds);\n    challenger_ = tachyon_sp1_baby_bear_poseidon2_duplex_challenger_create();\n    opened_values_ = tachyon_sp1_baby_bear_poseidon2_opened_values_create(0);\n    proof_ = tachyon_sp1_baby_bear_poseidon2_fri_proof_create();\n    commitment_vec_ =\n        tachyon_sp1_baby_bear_poseidon2_commitment_vec_create(kRounds);\n    domains_ = tachyon_sp1_baby_bear_poseidon2_domains_create(kRounds);\n  }\n\n  void TearDown() override {\n    tachyon_sp1_baby_bear_poseidon2_two_adic_fri_destroy(pcs_);\n    tachyon_sp1_baby_bear_poseidon2_lde_vec_destroy(lde_vec_);\n    tachyon_sp1_baby_bear_poseidon2_field_merkle_tree_vec_destroy(\n        prover_data_vec_);\n    tachyon_sp1_baby_bear_poseidon2_opening_points_destroy(opening_points_);\n    tachyon_sp1_baby_bear_poseidon2_duplex_challenger_destroy(challenger_);\n    tachyon_sp1_baby_bear_poseidon2_opened_values_destroy(opened_values_);\n    tachyon_sp1_baby_bear_poseidon2_fri_proof_destroy(proof_);\n    tachyon_sp1_baby_bear_poseidon2_commitment_vec_destroy(commitment_vec_);\n    tachyon_sp1_baby_bear_poseidon2_domains_destroy(domains_);\n  }\n\n protected:\n  tachyon_sp1_baby_bear_poseidon2_two_adic_fri* pcs_ = nullptr;\n  tachyon_sp1_baby_bear_poseidon2_lde_vec* lde_vec_ = nullptr;\n  tachyon_sp1_baby_bear_poseidon2_field_merkle_tree_vec* prover_data_vec_ =\n      nullptr;\n  tachyon_sp1_baby_bear_poseidon2_opening_points* opening_points_ = nullptr;\n  tachyon_sp1_baby_bear_poseidon2_duplex_challenger* challenger_ = nullptr;\n  tachyon_sp1_baby_bear_poseidon2_opened_values* opened_values_ = nullptr;\n  tachyon_sp1_baby_bear_poseidon2_fri_proof* proof_ = nullptr;\n  tachyon_sp1_baby_bear_poseidon2_commitment_vec* commitment_vec_ = nullptr;\n  tachyon_sp1_baby_bear_poseidon2_domains* domains_ = nullptr;\n};\n\n}  // namespace\n\nTEST_F(TwoAdicFRITest, APIs) {\n  constexpr size_t kRowsForInput = 32;\n  constexpr size_t kColsForInput = 5;\n  constexpr size_t kExtendedRowsForInput = kRowsForInput << kLogBlowup;\n  constexpr size_t kRowsForOpening = 2;\n  constexpr size_t kColsForOpening = 2;\n\n  using Commitment = typename MMCS::Commitment;\n  using ProverData = typename MMCS::ProverData;\n\n  tachyon_sp1_baby_bear_poseidon2_two_adic_fri* another_pcs =\n      tachyon_sp1_baby_bear_poseidon2_two_adic_fri_create(1, 10, 8);\n  PCS* native_pcs = c::base::native_cast(another_pcs);\n\n  Coset coset = native_pcs->GetNaturalDomainForDegree(kRowsForInput);\n\n  std::vector<F> matrix_data = base::CreateVector(kRowsForInput * kColsForInput,\n                                                  []() { return F::Random(); });\n  std::vector<F> matrix_data_clone = matrix_data;\n\n  std::vector<F> extended_matrix_data(kExtendedRowsForInput * kColsForInput);\n  F shift = F::FromMontgomery(F::Config::kSubgroupGenerator) *\n            coset.domain()->offset_inv();\n  tachyon_sp1_baby_bear_poseidon2_two_adic_fri_coset_lde_batch(\n      pcs_, c::base::c_cast(const_cast<F*>(matrix_data.data())), kRowsForInput,\n      kColsForInput,\n      c::base::c_cast(const_cast<F*>(extended_matrix_data.data())),\n      c::base::c_cast(shift));\n  tachyon_sp1_baby_bear_poseidon2_lde_vec_add(\n      lde_vec_, c::base::c_cast(const_cast<F*>(extended_matrix_data.data())),\n      kExtendedRowsForInput, kColsForInput);\n  tachyon_baby_bear commitment[TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_CHUNK];\n  tachyon_sp1_baby_bear_poseidon2_field_merkle_tree* prover_data = nullptr;\n  tachyon_sp1_baby_bear_poseidon2_two_adic_fri_commit(pcs_, lde_vec_,\n                                                      commitment, &prover_data);\n  tachyon_sp1_baby_bear_poseidon2_field_merkle_tree_vec_set(prover_data_vec_, 0,\n                                                            prover_data);\n\n  Commitment native_commitment;\n  ProverData native_prover_data;\n  std::vector<Coset> cosets;\n  cosets.push_back(std::move(coset));\n  std::vector<math::RowMajorMatrix<F>> matrices;\n  matrices.push_back(Eigen::Map<const math::RowMajorMatrix<F>>(\n      matrix_data_clone.data(), kRowsForInput, kColsForInput));\n  ASSERT_TRUE(native_pcs->Commit(cosets, matrices, &native_commitment,\n                                 &native_prover_data));\n\n  for (size_t i = 0; i < TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_CHUNK; ++i) {\n    EXPECT_EQ(c::base::native_cast(commitment[i]), native_commitment[i]);\n  }\n\n  math::RowMajorMatrix<F> leaf = Eigen::Map<math::RowMajorMatrix<F>>(\n      extended_matrix_data.data(), kExtendedRowsForInput, kColsForInput);\n  EXPECT_EQ(leaf, native_prover_data.leaves()[0]);\n\n  ExtF point = ExtF::Random();\n\n  tachyon_sp1_baby_bear_poseidon2_opening_points_allocate(\n      opening_points_, 0, kRowsForOpening, kColsForOpening);\n  for (size_t r = 0; r < kRowsForOpening; ++r) {\n    for (size_t c = 0; c < kColsForOpening; ++c) {\n      tachyon_sp1_baby_bear_poseidon2_opening_points_set(\n          opening_points_, 0, r, c, c::base::c_cast(&point));\n    }\n  }\n\n  tachyon_sp1_baby_bear_poseidon2_duplex_challenger* another_challenger =\n      tachyon_sp1_baby_bear_poseidon2_duplex_challenger_clone(challenger_);\n  tachyon_sp1_baby_bear_poseidon2_two_adic_fri_open(\n      pcs_, prover_data_vec_, opening_points_, challenger_, &opened_values_,\n      &proof_);\n\n  tachyon_sp1_baby_bear_poseidon2_commitment_vec_set(commitment_vec_, 0,\n                                                     commitment);\n  tachyon_sp1_baby_bear_poseidon2_domains_allocate(domains_, 0, 1);\n  tachyon_sp1_baby_bear_poseidon2_domains_set(\n      domains_, 0, 0, base::bits::CheckedLog2(kRowsForInput),\n      c::base::c_cast(&shift));\n\n  ASSERT_TRUE(tachyon_sp1_baby_bear_poseidon2_two_adic_fri_verify(\n      pcs_, commitment_vec_, domains_, opening_points_, opened_values_, proof_,\n      another_challenger));\n\n  tachyon_sp1_baby_bear_poseidon2_two_adic_fri_destroy(another_pcs);\n  tachyon_sp1_baby_bear_poseidon2_duplex_challenger_destroy(another_challenger);\n}\n\n}  // namespace tachyon\n"
  },
  {
    "path": "tachyon/c/zk/air/sp1/block.h",
    "content": "#ifndef TACHYON_C_ZK_AIR_SP1_BLOCK_H_\n#define TACHYON_C_ZK_AIR_SP1_BLOCK_H_\n\n#include <array>\n#include <string>\n#include <type_traits>\n#include <utility>\n\n#include \"tachyon/base/buffer/copyable.h\"\n#include \"tachyon/base/strings/string_util.h\"\n#include \"tachyon/base/types/always_false.h\"\n#include \"tachyon/math/finite_fields/finite_field_traits.h\"\n\nnamespace tachyon {\nnamespace c::zk::air::sp1 {\n\ntemplate <typename T>\nclass Block {\n public:\n  constexpr Block() = default;\n  constexpr explicit Block(const std::array<T, 4>& value) : value_(value) {}\n  constexpr explicit Block(std::array<T, 4>&& value)\n      : value_(std::move(value)) {}\n\n  const std::array<T, 4>& value() const { return value_; }\n\n  template <typename F = T, std::enable_if_t<tachyon::math::FiniteFieldTraits<\n                                F>::kIsFiniteField>* = nullptr>\n  constexpr static Block From(const F& value) {\n    if constexpr (tachyon::math::FiniteFieldTraits<F>::kIsPrimeField) {\n      return Block({value, F::Zero(), F::Zero(), F::Zero()});\n      // NOLINTNEXTLINE(readability/braces)\n    } else if constexpr (tachyon::math::FiniteFieldTraits<\n                             F>::kIsExtensionField) {\n      return Block(value.ToBaseFields());\n    } else {\n      static_assert(tachyon::base::AlwaysFalse<F>);\n    }\n  }\n\n  constexpr T& operator[](size_t idx) { return value_[idx]; }\n  constexpr const T& operator[](size_t idx) const { return value_[idx]; }\n\n  constexpr bool operator==(const Block& other) const {\n    return value_ == other.value_;\n  }\n  constexpr bool operator!=(const Block& other) const {\n    return value_ != other.value_;\n  }\n\n  std::string ToString() const {\n    return tachyon::base::ContainerToString(value_);\n  }\n\n private:\n  std::array<T, 4> value_;\n};\n\n}  // namespace c::zk::air::sp1\n\nnamespace base {\n\ntemplate <typename T>\nclass Copyable<c::zk::air::sp1::Block<T>> {\n public:\n  static bool WriteTo(const c::zk::air::sp1::Block<T>& block, Buffer* buffer) {\n    return buffer->Write(block.value());\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer,\n                       c::zk::air::sp1::Block<T>* block) {\n    std::array<T, 4> value;\n    if (!buffer.Read(&value)) return false;\n    *block = c::zk::air::sp1::Block<T>(std::move(value));\n    return true;\n  }\n\n  static size_t EstimateSize(const c::zk::air::sp1::Block<T>& block) {\n    return base::EstimateSize(block.value());\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_C_ZK_AIR_SP1_BLOCK_H_\n"
  },
  {
    "path": "tachyon/c/zk/air/sp1/block_unittest.cc",
    "content": "#include \"tachyon/c/zk/air/sp1/block.h\"\n\n#include \"tachyon/base/buffer/vector_buffer.h\"\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/math/finite_fields/baby_bear/baby_bear.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n\nnamespace tachyon {\n\nnamespace {\n\nusing F = math::BabyBear;\n\nclass BlockTest : public math::FiniteFieldTest<F> {};\n\n}  // namespace\n\nTEST(BlockTest, Copyable) {\n  c::zk::air::sp1::Block<F> expected(\n      base::CreateArray<4>([]() { return F::Random(); }));\n\n  base::Uint8VectorBuffer write_buf;\n  ASSERT_TRUE(write_buf.Grow(base::EstimateSize(expected)));\n  ASSERT_TRUE(write_buf.Write(expected));\n  ASSERT_TRUE(write_buf.Done());\n\n  write_buf.set_buffer_offset(0);\n\n  c::zk::air::sp1::Block<F> value;\n  ASSERT_TRUE(write_buf.Read(&value));\n\n  EXPECT_EQ(value, expected);\n}\n\n}  // namespace tachyon\n"
  },
  {
    "path": "tachyon/c/zk/base/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n    name = \"base_hdrs\",\n    srcs = [\"bn254_blinder.h\"],\n)\n\ntachyon_cc_library(\n    name = \"base\",\n    deps = [\":bn254_blinder\"],\n)\n\ntachyon_cc_library(\n    name = \"bn254_blinder\",\n    srcs = [\"bn254_blinder.cc\"],\n    hdrs = [\n        \"bn254_blinder.h\",\n        \"bn254_blinder_type_traits.h\",\n    ],\n    deps = [\n        \"//tachyon/c:export\",\n        \"//tachyon/c/base:type_traits_forward\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:fr\",\n        \"//tachyon/zk/base:blinder\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/c/zk/base/bn254_blinder.cc",
    "content": "#include \"tachyon/c/zk/base/bn254_blinder.h\"\n\n#include \"tachyon/c/zk/base/bn254_blinder_type_traits.h\"\n\nusing namespace tachyon;\n\nvoid tachyon_halo2_bn254_blinder_set_blinding_factors(\n    tachyon_bn254_blinder* blinder, uint32_t blinding_factors) {\n  c::base::native_cast(blinder)->set_blinding_factors(blinding_factors);\n}\n\nuint32_t tachyon_halo2_bn254_blinder_get_blinding_factors(\n    const tachyon_bn254_blinder* blinder) {\n  return c::base::native_cast(blinder)->blinding_factors();\n}\n"
  },
  {
    "path": "tachyon/c/zk/base/bn254_blinder.h",
    "content": "/**\n * @file bn254_blinder.h\n * @brief Blinder for the BN254 Curve.\n *\n * This header file defines the structure and API for managing blinding factors\n * for the BN254 curve.\n */\n#ifndef TACHYON_C_ZK_BASE_BN254_BLINDER_H_\n#define TACHYON_C_ZK_BASE_BN254_BLINDER_H_\n\n#include <stdint.h>\n\n#include \"tachyon/c/export.h\"\n\n/**\n * @struct tachyon_bn254_blinder\n * @brief Represents a blinder for BN254 curve.\n *\n * It allows setting and retrieving the blinding factors used\n * during the proof generation process.\n */\nstruct tachyon_bn254_blinder {};\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Sets the blinding factors for a BN254 blinder.\n *\n * Configures the blinder with a specific number of blinding factors to be used\n * in zero-knowledge proof generation or verification.\n *\n * @param blinder Pointer to the BN254 blinder structure.\n * @param blinding_factors The number of blinding factors to be set.\n */\nTACHYON_C_EXPORT void tachyon_halo2_bn254_blinder_set_blinding_factors(\n    tachyon_bn254_blinder* blinder, uint32_t blinding_factors);\n\n/**\n * @brief Gets the blinding factors from a BN254 blinder.\n *\n * Retrieves the number of blinding factors configured in the blinder, which are\n * used in the zero-knowledge proof processes.\n *\n * @param blinder Pointer to the BN254 blinder structure.\n * @return The number of blinding factors set in the blinder.\n */\nTACHYON_C_EXPORT uint32_t tachyon_halo2_bn254_blinder_get_blinding_factors(\n    const tachyon_bn254_blinder* blinder);\n\n#ifdef __cplusplus\n}  // extern \"C\"\n#endif\n\n#endif  // TACHYON_C_ZK_BASE_BN254_BLINDER_H_\n"
  },
  {
    "path": "tachyon/c/zk/base/bn254_blinder_type_traits.h",
    "content": "#ifndef TACHYON_C_ZK_BASE_BN254_BLINDER_TYPE_TRAITS_H_\n#define TACHYON_C_ZK_BASE_BN254_BLINDER_TYPE_TRAITS_H_\n\n#include \"tachyon/c/base/type_traits_forward.h\"\n#include \"tachyon/c/zk/base/bn254_blinder.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/fr.h\"\n#include \"tachyon/zk/base/blinder.h\"\n\nnamespace tachyon::c::base {\n\ntemplate <>\nstruct TypeTraits<tachyon::zk::Blinder<tachyon::math::bn254::Fr>> {\n  using CType = tachyon_bn254_blinder;\n};\n\ntemplate <>\nstruct TypeTraits<tachyon_bn254_blinder> {\n  using NativeType = tachyon::zk::Blinder<tachyon::math::bn254::Fr>;\n};\n\n}  // namespace tachyon::c::base\n\n#endif  // TACHYON_C_ZK_BASE_BN254_BLINDER_TYPE_TRAITS_H_\n"
  },
  {
    "path": "tachyon/c/zk/plonk/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n    name = \"plonk_hdrs\",\n    srcs = [\n        \"//tachyon/c/zk/plonk/constraint_system:constraint_system_hdrs\",\n        \"//tachyon/c/zk/plonk/halo2:halo2_hdrs\",\n        \"//tachyon/c/zk/plonk/keys:keys_hdrs\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"plonk\",\n    deps = [\n        \"//tachyon/c/zk/plonk/constraint_system\",\n        \"//tachyon/c/zk/plonk/halo2\",\n        \"//tachyon/c/zk/plonk/keys\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/c/zk/plonk/constraint_system/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n    name = \"constraint_system_hdrs\",\n    srcs = [\n        \"bn254_constraint_system.h\",\n        \"column_key.h\",\n        \"column_type.h\",\n        \"phase.h\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"bn254_constraint_system\",\n    srcs = [\"bn254_constraint_system.cc\"],\n    hdrs = [\n        \"bn254_constraint_system.h\",\n        \"bn254_constraint_system_type_traits.h\",\n    ],\n    deps = [\n        \":column_key\",\n        \"//tachyon/c:export\",\n        \"//tachyon/c/base:type_traits_forward\",\n        \"//tachyon/zk/plonk/constraint_system\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"column_key\",\n    hdrs = [\"column_key.h\"],\n    deps = [\n        \":column_type\",\n        \":phase\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"column_type\",\n    hdrs = [\"column_type.h\"],\n)\n\ntachyon_cc_library(\n    name = \"constraint_system\",\n    deps = [\":bn254_constraint_system\"],\n)\n\ntachyon_cc_library(\n    name = \"phase\",\n    hdrs = [\"phase.h\"],\n)\n\ntachyon_cc_unittest(\n    name = \"constraint_system_unittests\",\n    srcs = [\"bn254_constraint_system_unittest.cc\"],\n    deps = [\n        \":bn254_constraint_system\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:fr\",\n        \"//tachyon/math/finite_fields/test:finite_field_test\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/c/zk/plonk/constraint_system/bn254_constraint_system.cc",
    "content": "#include \"tachyon/c/zk/plonk/constraint_system/bn254_constraint_system.h\"\n\n#include <vector>\n\n#include \"tachyon/c/zk/plonk/constraint_system/bn254_constraint_system_type_traits.h\"\n\nusing namespace tachyon;\n\nuint32_t tachyon_bn254_plonk_constraint_system_compute_blinding_factors(\n    const tachyon_bn254_plonk_constraint_system* cs) {\n  return c::base::native_cast(cs)->ComputeBlindingFactors();\n}\n\nvoid tachyon_bn254_plonk_constraint_system_get_advice_column_phases(\n    const tachyon_bn254_plonk_constraint_system* cs, tachyon_phase* phases,\n    size_t* phases_len) {\n  const std::vector<zk::plonk::Phase>& cpp_phases =\n      c::base::native_cast(cs)->advice_column_phases();\n  *phases_len = cpp_phases.size();\n  if (phases == nullptr) return;\n  for (size_t i = 0; i < cpp_phases.size(); ++i) {\n    phases[i].value = cpp_phases[i].value();\n  }\n}\n\nvoid tachyon_bn254_plonk_constraint_system_get_challenge_phases(\n    const tachyon_bn254_plonk_constraint_system* cs, tachyon_phase* phases,\n    size_t* phases_len) {\n  const std::vector<zk::plonk::Phase>& cpp_phases =\n      c::base::native_cast(cs)->challenge_phases();\n  *phases_len = cpp_phases.size();\n  if (phases == nullptr) return;\n  for (size_t i = 0; i < cpp_phases.size(); ++i) {\n    phases[i].value = cpp_phases[i].value();\n  }\n}\n\nvoid tachyon_bn254_plonk_constraint_system_get_phases(\n    const tachyon_bn254_plonk_constraint_system* cs, tachyon_phase* phases,\n    size_t* phases_len) {\n  std::vector<zk::plonk::Phase> cpp_phases =\n      c::base::native_cast(cs)->GetPhases();\n  *phases_len = cpp_phases.size();\n  if (phases == nullptr) return;\n  for (size_t i = 0; i < cpp_phases.size(); ++i) {\n    phases[i].value = cpp_phases[i].value();\n  }\n}\n\nsize_t tachyon_bn254_plonk_constraint_system_get_num_fixed_columns(\n    const tachyon_bn254_plonk_constraint_system* cs) {\n  return c::base::native_cast(cs)->num_fixed_columns();\n}\n\nsize_t tachyon_bn254_plonk_constraint_system_get_num_instance_columns(\n    const tachyon_bn254_plonk_constraint_system* cs) {\n  return c::base::native_cast(cs)->num_instance_columns();\n}\n\nsize_t tachyon_bn254_plonk_constraint_system_get_num_advice_columns(\n    const tachyon_bn254_plonk_constraint_system* cs) {\n  return c::base::native_cast(cs)->num_advice_columns();\n}\n\nsize_t tachyon_bn254_plonk_constraint_system_get_num_challenges(\n    const tachyon_bn254_plonk_constraint_system* cs) {\n  return c::base::native_cast(cs)->num_challenges();\n}\n\nvoid tachyon_bn254_plonk_constraint_system_get_constants(\n    const tachyon_bn254_plonk_constraint_system* cs,\n    tachyon_fixed_column_key* constants, size_t* constants_len) {\n  const std::vector<zk::plonk::FixedColumnKey>& cpp_constants =\n      c::base::native_cast(cs)->constants();\n  *constants_len = cpp_constants.size();\n  if (constants == nullptr) return;\n  for (size_t i = 0; i < cpp_constants.size(); ++i) {\n    constants[i].index = cpp_constants[i].index();\n  }\n}\n"
  },
  {
    "path": "tachyon/c/zk/plonk/constraint_system/bn254_constraint_system.h",
    "content": "/**\n * @file bn254_constraint_system.h\n * @brief PLONK Constraint System for BN254 Curve.\n *\n * This header file defines the API for the PLONK constraint system specific to\n * the BN254 curve, offering functionalities to compute blinding factors,\n * retrieve column phases, and access counts of fixed, instance, and advice\n * columns as well as challenges and constants within the constraint system.\n */\n#ifndef TACHYON_C_ZK_PLONK_CONSTRAINT_SYSTEM_BN254_CONSTRAINT_SYSTEM_H_\n#define TACHYON_C_ZK_PLONK_CONSTRAINT_SYSTEM_BN254_CONSTRAINT_SYSTEM_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include \"tachyon/c/export.h\"\n#include \"tachyon/c/zk/plonk/constraint_system/column_key.h\"\n#include \"tachyon/c/zk/plonk/constraint_system/phase.h\"\n\n/**\n * @struct tachyon_bn254_plonk_constraint_system\n * @brief Represents the PLONK constraint system structure for the BN254 curve.\n */\nstruct tachyon_bn254_plonk_constraint_system {};\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Computes the blinding factors necessary for the constraint system.\n *\n * @param cs Pointer to the constraint system structure.\n * @return The number of blinding factors computed for the constraint system.\n */\nTACHYON_C_EXPORT uint32_t\ntachyon_bn254_plonk_constraint_system_compute_blinding_factors(\n    const tachyon_bn254_plonk_constraint_system* cs);\n\n/**\n * @brief Retrieves the phases for advice columns within the constraint system.\n *\n * @param cs Pointer to the constraint system structure.\n * @param phases Output array to store the phases. Can be NULL to query required\n * length.\n * @param phases_len Pointer to store the length of phases array or required\n * length if phases is NULL.\n */\nTACHYON_C_EXPORT void\ntachyon_bn254_plonk_constraint_system_get_advice_column_phases(\n    const tachyon_bn254_plonk_constraint_system* cs, tachyon_phase* phases,\n    size_t* phases_len);\n\n/**\n * @brief Retrieves the phases for challenge columns within the constraint\n * system.\n *\n * @param cs Pointer to the constraint system structure.\n * @param phases Output array to store the phases. Can be NULL to query required\n * length.\n * @param phases_len Pointer to store the length of phases array or required\n * length if phases is NULL.\n */\nTACHYON_C_EXPORT void\ntachyon_bn254_plonk_constraint_system_get_challenge_phases(\n    const tachyon_bn254_plonk_constraint_system* cs, tachyon_phase* phases,\n    size_t* phases_len);\n\n/**\n * @brief Retrieves all phases within the constraint system.\n *\n * @param cs Pointer to the constraint system structure.\n * @param phases Output array to store the phases. Can be NULL to query required\n * length.\n * @param phases_len Pointer to store the length of phases array or required\n * length if phases is NULL.\n */\nTACHYON_C_EXPORT void tachyon_bn254_plonk_constraint_system_get_phases(\n    const tachyon_bn254_plonk_constraint_system* cs, tachyon_phase* phases,\n    size_t* phases_len);\n\n/**\n * @brief Retrieves the number of fixed columns in the constraint system.\n *\n * @param cs Pointer to the constraint system structure.\n * @return The number of fixed columns.\n */\nTACHYON_C_EXPORT size_t\ntachyon_bn254_plonk_constraint_system_get_num_fixed_columns(\n    const tachyon_bn254_plonk_constraint_system* cs);\n\n/**\n * @brief Retrieves the number of instance columns in the constraint system.\n *\n * @param cs Pointer to the constraint system structure.\n * @return The number of instance columns.\n */\nTACHYON_C_EXPORT size_t\ntachyon_bn254_plonk_constraint_system_get_num_instance_columns(\n    const tachyon_bn254_plonk_constraint_system* cs);\n\n/**\n * @brief Retrieves the number of advice columns in the constraint system.\n *\n * @param cs Pointer to the constraint system structure.\n * @return The number of advice columns.\n */\nTACHYON_C_EXPORT size_t\ntachyon_bn254_plonk_constraint_system_get_num_advice_columns(\n    const tachyon_bn254_plonk_constraint_system* cs);\n\n/**\n * @brief Retrieves the number of challenges in the constraint system.\n *\n * @param cs Pointer to the constraint system structure.\n * @return The number of challenges.\n */\nTACHYON_C_EXPORT size_t\ntachyon_bn254_plonk_constraint_system_get_num_challenges(\n    const tachyon_bn254_plonk_constraint_system* cs);\n\n/**\n * @brief Retrieves the constants used in the constraint system. This function\n * can be used in two modes: querying the required length of the constants array\n * or populating the constants array.\n *\n * If the `constants` parameter is NULL, this function will populate\n * `constants_len` with the required length for the constants array. This mode\n * allows the caller to determine the size of the array needed to hold all\n * constants.\n *\n * If the `constants` parameter is not NULL, the function assumes that\n * `constants` points to an array of sufficient size to hold all constants. It\n * will then populate this array with the constants from the constraint system.\n *\n * @param cs Pointer to the constraint system structure from which to retrieve\n * constants.\n * @param constants Pointer to the array where the constants will be stored. If\n * NULL, the function will only populate `constants_len` with the required array\n * size.\n * @param constants_len Pointer to a size_t variable where the function will\n * store the length of the constants array. If `constants` is NULL, it stores\n * the required length; otherwise, it stores the number of constants actually\n * populated.\n */\nTACHYON_C_EXPORT void tachyon_bn254_plonk_constraint_system_get_constants(\n    const tachyon_bn254_plonk_constraint_system* cs,\n    tachyon_fixed_column_key* constants, size_t* constants_len);\n\n#ifdef __cplusplus\n}  // extern \"C\"\n#endif\n\n#endif  // TACHYON_C_ZK_PLONK_CONSTRAINT_SYSTEM_BN254_CONSTRAINT_SYSTEM_H_\n"
  },
  {
    "path": "tachyon/c/zk/plonk/constraint_system/bn254_constraint_system_type_traits.h",
    "content": "#ifndef TACHYON_C_ZK_PLONK_CONSTRAINT_SYSTEM_BN254_CONSTRAINT_SYSTEM_TYPE_TRAITS_H_\n#define TACHYON_C_ZK_PLONK_CONSTRAINT_SYSTEM_BN254_CONSTRAINT_SYSTEM_TYPE_TRAITS_H_\n\n#include \"tachyon/c/base/type_traits_forward.h\"\n#include \"tachyon/c/zk/plonk/constraint_system/bn254_constraint_system.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/fr.h\"\n#include \"tachyon/zk/plonk/constraint_system/constraint_system.h\"\n\nnamespace tachyon::c::base {\n\ntemplate <>\nstruct TypeTraits<\n    tachyon::zk::plonk::ConstraintSystem<tachyon::math::bn254::Fr>> {\n  using CType = tachyon_bn254_plonk_constraint_system;\n};\n\ntemplate <>\nstruct TypeTraits<tachyon_bn254_plonk_constraint_system> {\n  using NativeType =\n      tachyon::zk::plonk::ConstraintSystem<tachyon::math::bn254::Fr>;\n};\n\n}  // namespace tachyon::c::base\n\n#endif  // TACHYON_C_ZK_PLONK_CONSTRAINT_SYSTEM_BN254_CONSTRAINT_SYSTEM_TYPE_TRAITS_H_\n"
  },
  {
    "path": "tachyon/c/zk/plonk/constraint_system/bn254_constraint_system_unittest.cc",
    "content": "#include \"tachyon/c/zk/plonk/constraint_system/bn254_constraint_system.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/c/zk/plonk/constraint_system/bn254_constraint_system_type_traits.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n\nnamespace tachyon::zk::plonk {\n\nnamespace {\n\nclass ConstraintSystemTest : public math::FiniteFieldTest<math::bn254::Fr> {\n public:\n  void SetUp() override { cs_ = c::base::c_cast(&cpp_cs_); }\n\n protected:\n  ConstraintSystem<math::bn254::Fr> cpp_cs_;\n  tachyon_bn254_plonk_constraint_system* cs_;\n};\n\n}  // namespace\n\nTEST_F(ConstraintSystemTest, ComputeBlindingFactors) {\n  EXPECT_EQ(tachyon_bn254_plonk_constraint_system_compute_blinding_factors(cs_),\n            5);\n}\n\nTEST_F(ConstraintSystemTest, GetAdviceColumnPhases) {\n  size_t phases_len;\n  for (uint8_t i = 0; i < 3; ++i) {\n    tachyon_bn254_plonk_constraint_system_get_advice_column_phases(cs_, nullptr,\n                                                                   &phases_len);\n    ASSERT_EQ(phases_len, i);\n\n    cpp_cs_.CreateAdviceColumn(Phase(i));\n  }\n\n  tachyon_phase phases[3];\n  tachyon_bn254_plonk_constraint_system_get_advice_column_phases(cs_, phases,\n                                                                 &phases_len);\n  ASSERT_EQ(phases_len, 3);\n  for (uint8_t i = 0; i < 3; ++i) {\n    EXPECT_EQ(phases[i].value, i);\n  }\n}\n\nTEST_F(ConstraintSystemTest, GetChallengePhases) {\n  size_t phases_len;\n  for (uint8_t i = 0; i < 3; ++i) {\n    tachyon_bn254_plonk_constraint_system_get_challenge_phases(cs_, nullptr,\n                                                               &phases_len);\n    ASSERT_EQ(phases_len, i);\n\n    cpp_cs_.CreateAdviceColumn(Phase(i));\n    cpp_cs_.CreateChallengeUsableAfter(Phase(i));\n  }\n\n  tachyon_phase phases[3];\n  tachyon_bn254_plonk_constraint_system_get_challenge_phases(cs_, phases,\n                                                             &phases_len);\n  ASSERT_EQ(phases_len, 3);\n  for (uint8_t i = 0; i < 3; ++i) {\n    EXPECT_EQ(phases[i].value, i);\n  }\n}\n\nTEST_F(ConstraintSystemTest, GetPhases) {\n  size_t phases_len;\n  for (uint8_t i = 0; i < 3; ++i) {\n    tachyon_bn254_plonk_constraint_system_get_phases(cs_, nullptr, &phases_len);\n    if (i == 0) {\n      ASSERT_EQ(phases_len, 1);\n    } else {\n      ASSERT_EQ(phases_len, i);\n    }\n\n    cpp_cs_.CreateAdviceColumn(Phase(i));\n  }\n\n  tachyon_phase phases[3];\n  tachyon_bn254_plonk_constraint_system_get_phases(cs_, phases, &phases_len);\n  ASSERT_EQ(phases_len, 3);\n  for (uint8_t i = 0; i < 3; ++i) {\n    EXPECT_EQ(phases[i].value, i);\n  }\n}\n\nTEST_F(ConstraintSystemTest, GetNumFixedColumns) {\n  for (uint8_t i = 0; i < 3; ++i) {\n    EXPECT_EQ(tachyon_bn254_plonk_constraint_system_get_num_fixed_columns(cs_),\n              i);\n    cpp_cs_.CreateFixedColumn();\n  }\n}\n\nTEST_F(ConstraintSystemTest, GetNumInstanceColumns) {\n  for (uint8_t i = 0; i < 3; ++i) {\n    EXPECT_EQ(\n        tachyon_bn254_plonk_constraint_system_get_num_instance_columns(cs_), i);\n    cpp_cs_.CreateInstanceColumn();\n  }\n  EXPECT_EQ(tachyon_bn254_plonk_constraint_system_get_num_instance_columns(cs_),\n            3);\n}\n\nTEST_F(ConstraintSystemTest, GetNumAdviceColumns) {\n  for (uint8_t i = 0; i < 3; ++i) {\n    EXPECT_EQ(tachyon_bn254_plonk_constraint_system_get_num_advice_columns(cs_),\n              i);\n    cpp_cs_.CreateAdviceColumn();\n  }\n  EXPECT_EQ(tachyon_bn254_plonk_constraint_system_get_num_advice_columns(cs_),\n            3);\n}\n\nTEST_F(ConstraintSystemTest, GetNumChallenges) {\n  for (uint8_t i = 0; i < 3; ++i) {\n    EXPECT_EQ(tachyon_bn254_plonk_constraint_system_get_num_challenges(cs_), i);\n\n    cpp_cs_.CreateAdviceColumn(Phase(i));\n    cpp_cs_.CreateChallengeUsableAfter(Phase(i));\n  }\n  EXPECT_EQ(tachyon_bn254_plonk_constraint_system_get_num_challenges(cs_), 3);\n}\n\nTEST_F(ConstraintSystemTest, GetConstants) {\n  size_t constants_len;\n  for (uint8_t i = 0; i < 3; ++i) {\n    tachyon_bn254_plonk_constraint_system_get_constants(cs_, nullptr,\n                                                        &constants_len);\n    ASSERT_EQ(constants_len, i);\n\n    FixedColumnKey column = cpp_cs_.CreateFixedColumn();\n    cpp_cs_.EnableConstant(column);\n  }\n\n  tachyon_fixed_column_key constants[3];\n  tachyon_bn254_plonk_constraint_system_get_constants(cs_, constants,\n                                                      &constants_len);\n  ASSERT_EQ(constants_len, 3);\n  for (uint8_t i = 0; i < 3; ++i) {\n    EXPECT_EQ(constants[i].index, i);\n  }\n}\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/c/zk/plonk/constraint_system/column_key.h",
    "content": "/**\n * @file column_key.h\n * @brief Column Keys for PLONK Constraint System.\n *\n * This header file defines structures for various types of column keys used in\n * the PLONK constraint system. Column keys uniquely identify columns within the\n * system and may include additional information such as phases for advice\n * columns.\n */\n#ifndef TACHYON_C_ZK_PLONK_CONSTRAINT_SYSTEM_COLUMN_KEY_H_\n#define TACHYON_C_ZK_PLONK_CONSTRAINT_SYSTEM_COLUMN_KEY_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include \"tachyon/c/zk/plonk/constraint_system/phase.h\"\n\n/**\n * @struct tachyon_fixed_column_key\n * @brief Key for identifying a fixed column in the PLONK constraint system.\n *\n * This structure represents a key that uniquely identifies a fixed column\n * within a PLONK constraint system. Fixed columns contain constants or other\n * values that do not change between different executions of the protocol.\n */\nstruct tachyon_fixed_column_key {\n  size_t index;\n};\n\n/**\n * @struct tachyon_instance_column_key\n * @brief Key for identifying an instance column in the PLONK constraint system.\n *\n * This structure represents a key that uniquely identifies an instance column\n * within a PLONK constraint system. Instance columns contain public inputs to\n * the zero-knowledge proof.\n */\nstruct tachyon_instance_column_key {\n  size_t index;\n};\n\n/**\n * @struct tachyon_advice_column_key\n * @brief Key for identifying an advice column in the PLONK constraint system.\n *\n * This structure represents a key that uniquely identifies an advice column\n * within a PLONK constraint system. Advice columns contain auxiliary witness\n * information generated during the proof construction. Each advice column is\n * associated with a phase, indicating when its values are to be used during the\n * proof verification process.\n */\nstruct tachyon_advice_column_key {\n  size_t index;\n  struct tachyon_phase phase;\n};\n\n#endif  // TACHYON_C_ZK_PLONK_CONSTRAINT_SYSTEM_COLUMN_KEY_H_\n"
  },
  {
    "path": "tachyon/c/zk/plonk/constraint_system/column_type.h",
    "content": "#ifndef TACHYON_C_ZK_PLONK_CONSTRAINT_SYSTEM_COLUMN_TYPE_H_\n#define TACHYON_C_ZK_PLONK_CONSTRAINT_SYSTEM_COLUMN_TYPE_H_\n\n#define TACHYON_PLONK_COLUMN_TYPE_ANY 0\n#define TACHYON_PLONK_COLUMN_TYPE_INSTANCE 1\n#define TACHYON_PLONK_COLUMN_TYPE_ADVICE 2\n#define TACHYON_PLONK_COLUMN_TYPE_FIXED 3\n\n#endif  // TACHYON_C_ZK_PLONK_CONSTRAINT_SYSTEM_COLUMN_TYPE_H_\n"
  },
  {
    "path": "tachyon/c/zk/plonk/constraint_system/phase.h",
    "content": "/**\n * @file phase.h\n * @brief Phase Structure for PLONK Constraint System.\n *\n * This header file defines the tachyon_phase structure, which represents the\n * phase of an operation or element within the PLONK constraint system. Phases\n * are used to organize and control the execution order of various operations in\n * PLONK proofs, ensuring the correct application of constraints and\n * evaluations.\n */\n#ifndef TACHYON_C_ZK_PLONK_CONSTRAINT_SYSTEM_PHASE_H_\n#define TACHYON_C_ZK_PLONK_CONSTRAINT_SYSTEM_PHASE_H_\n\n#include <stdint.h>\n\n/**\n * @struct tachyon_phase\n * @brief Represents a phase in the PLONK constraint system.\n *\n * A phase is a conceptual tool used in the PLONK constraint system to denote\n * the timing or sequencing of operations. It helps in organizing the constraint\n * system and ensuring that operations are performed in the intended order. Each\n * phase is associated with certain columns or operations within the PLONK\n * setup, enabling fine-grained control over the proof generation and\n * verification processes.\n */\nstruct tachyon_phase {\n  uint8_t value;\n};\n\n#endif  // TACHYON_C_ZK_PLONK_CONSTRAINT_SYSTEM_PHASE_H_\n"
  },
  {
    "path": "tachyon/c/zk/plonk/halo2/BUILD.bazel",
    "content": "load(\n    \"//bazel:tachyon_cc.bzl\",\n    \"tachyon_cc_binary\",\n    \"tachyon_cc_library\",\n    \"tachyon_cc_unittest\",\n    \"tachyon_openmp_num_threads_env\",\n)\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n    name = \"halo2_hdrs\",\n    srcs = [\n        \"bn254_argument_data.h\",\n        \"bn254_instance_columns_vec.h\",\n        \"bn254_prover.h\",\n        \"bn254_transcript.h\",\n        \"bn254_verifier.h\",\n        \"constants.h\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"bn254_argument_data\",\n    srcs = [\"bn254_argument_data.cc\"],\n    hdrs = [\n        \"bn254_argument_data.h\",\n        \"bn254_argument_data_type_traits.h\",\n    ],\n    deps = [\n        \"//tachyon/c/base:type_traits_forward\",\n        \"//tachyon/c/math/polynomials:constants\",\n        \"//tachyon/c/math/polynomials/univariate:bn254_univariate_dense_polynomial\",\n        \"//tachyon/c/math/polynomials/univariate:bn254_univariate_evaluations\",\n        \"//tachyon/zk/plonk/halo2:argument_data\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"bn254_halo2\",\n    deps = [\n        \":bn254_prover\",\n        \":bn254_verifier\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"bn254_instance_columns_vec\",\n    srcs = [\"bn254_instance_columns_vec.cc\"],\n    hdrs = [\n        \"bn254_instance_columns_vec.h\",\n        \"bn254_instance_columns_vec_type_traits.h\",\n    ],\n    deps = [\n        \"//tachyon/c/base:type_traits_forward\",\n        \"//tachyon/c/math/elliptic_curves/bn/bn254:fr\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"bn254_prover\",\n    srcs = [\"bn254_prover.cc\"],\n    hdrs = [\"bn254_prover.h\"],\n    deps = [\n        \":bn254_argument_data\",\n        \":bn254_ps\",\n        \":bn254_transcript\",\n        \":kzg_family_prover_impl\",\n        \"//tachyon/c/base:type_traits_forward\",\n        \"//tachyon/c/math/elliptic_curves/bn/bn254:g1\",\n        \"//tachyon/c/math/elliptic_curves/bn/bn254:g2\",\n        \"//tachyon/c/math/polynomials/univariate:bn254_univariate_evaluation_domain\",\n        \"//tachyon/c/zk/base:bn254_blinder\",\n        \"//tachyon/c/zk/plonk/keys:bn254_plonk_proving_key\",\n        \"//tachyon/crypto/random:rng_type\",\n        \"//tachyon/crypto/random/cha_cha20:cha_cha20_rng\",\n        \"//tachyon/crypto/random/xor_shift:xor_shift_rng\",\n        \"//tachyon/math/elliptic_curves/bn/bn254\",\n        \"//tachyon/math/elliptic_curves/bn/bn254/halo2:bn254\",\n        \"//tachyon/zk/base/commitments:gwc_extension\",\n        \"//tachyon/zk/base/commitments:shplonk_extension\",\n        \"//tachyon/zk/plonk/halo2:pcs_type\",\n        \"//tachyon/zk/plonk/halo2:prover\",\n        \"//tachyon/zk/plonk/halo2:vendor\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"bn254_ps\",\n    hdrs = [\"bn254_ps.h\"],\n    deps = [\n        \"//tachyon/c/math/polynomials:constants\",\n        \"//tachyon/math/elliptic_curves/bn/bn254\",\n        \"//tachyon/zk/base/commitments:gwc_extension\",\n        \"//tachyon/zk/base/commitments:shplonk_extension\",\n        \"//tachyon/zk/plonk/halo2:proving_scheme\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"bn254_transcript\",\n    srcs = [\"bn254_transcript.cc\"],\n    hdrs = [\"bn254_transcript.h\"],\n    deps = [\n        \":constants\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/c:export\",\n        \"//tachyon/c/math/elliptic_curves/bn/bn254:fr\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:g1\",\n        \"//tachyon/zk/plonk/halo2:blake2b_transcript\",\n        \"//tachyon/zk/plonk/halo2:poseidon_transcript\",\n        \"//tachyon/zk/plonk/halo2:sha256_transcript\",\n        \"//tachyon/zk/plonk/halo2:snark_verifier_poseidon_transcript\",\n        \"//tachyon/zk/plonk/halo2:transcript_type\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"bn254_verifier\",\n    srcs = [\"bn254_verifier.cc\"],\n    hdrs = [\"bn254_verifier.h\"],\n    deps = [\n        \":bn254_instance_columns_vec\",\n        \":bn254_ps\",\n        \":bn254_transcript\",\n        \":verifier_impl\",\n        \"//tachyon/c/base:type_traits_forward\",\n        \"//tachyon/c/zk/plonk/keys:bn254_plonk_verifying_key\",\n        \"//tachyon/math/elliptic_curves/bn/bn254/halo2:bn254\",\n        \"//tachyon/math/polynomials/univariate:univariate_evaluation_domain_factory\",\n        \"//tachyon/zk/plonk/halo2:pcs_type\",\n        \"//tachyon/zk/plonk/halo2:vendor\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"buffer_reader\",\n    hdrs = [\"buffer_reader.h\"],\n    deps = [\n        \":bn254_ps\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base/buffer:endian_auto_reset\",\n        \"//tachyon/base/buffer:read_only_buffer\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/math/finite_fields:cubic_extension_field\",\n        \"//tachyon/math/finite_fields:prime_field_base\",\n        \"//tachyon/math/finite_fields:quadratic_extension_field\",\n        \"//tachyon/math/geometry:affine_point\",\n        \"//tachyon/math/polynomials/univariate:univariate_evaluations\",\n        \"//tachyon/math/polynomials/univariate:univariate_polynomial\",\n        \"//tachyon/zk/expressions:expression_factory\",\n        \"//tachyon/zk/lookup:argument\",\n        \"//tachyon/zk/plonk/base:column_key\",\n        \"//tachyon/zk/plonk/base:phase\",\n        \"//tachyon/zk/plonk/constraint_system:challenge\",\n        \"//tachyon/zk/plonk/constraint_system:gate\",\n        \"//tachyon/zk/plonk/constraint_system:lookup_tracker\",\n        \"//tachyon/zk/plonk/constraint_system:query\",\n        \"//tachyon/zk/plonk/expressions:expression_factory\",\n        \"//tachyon/zk/plonk/permutation:permutation_argument\",\n        \"//tachyon/zk/shuffle:argument\",\n        \"@com_google_absl//absl/container:btree\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"constants\",\n    hdrs = [\"constants.h\"],\n)\n\ntachyon_cc_library(\n    name = \"halo2\",\n    deps = [\":bn254_halo2\"],\n)\n\ntachyon_cc_library(\n    name = \"kzg_family_prover_impl\",\n    hdrs = [\"kzg_family_prover_impl.h\"],\n    deps = [\n        \":prover_impl_base\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/c/base:type_traits_forward\",\n        \"//tachyon/c/math/elliptic_curves:point_traits_forward\",\n        \"//tachyon/math/elliptic_curves/msm:variable_base_msm\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"prover_impl_base\",\n    hdrs = [\"prover_impl_base.h\"],\n    deps = [\n        \"//tachyon/base:environment\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base/files:file_util\",\n        \"//tachyon/base/functional:callback\",\n        \"//tachyon/zk/plonk/halo2:proof_serializer\",\n        \"//tachyon/zk/plonk/halo2:prover\",\n    ],\n)\n\ntachyon_cc_binary(\n    name = \"prover_replay\",\n    srcs = [\"prover_replay.cc\"],\n    deps = [\n        \":bn254_prover\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base/console\",\n        \"//tachyon/base/files:file_path_flag\",\n        \"//tachyon/base/files:file_util\",\n        \"//tachyon/base/flag:flag_parser\",\n        \"//tachyon/c/math/elliptic_curves/bn/bn254:fr\",\n        \"//tachyon/c/zk/plonk/keys:proving_key_impl\",\n        \"//tachyon/crypto/random:rng_type\",\n        \"//tachyon/crypto/random/cha_cha20:cha_cha20_rng\",\n        \"//tachyon/crypto/random/xor_shift:xor_shift_rng\",\n        \"//tachyon/zk/plonk/halo2:constants\",\n        \"//tachyon/zk/plonk/halo2:pcs_type\",\n        \"//tachyon/zk/plonk/halo2:transcript_type\",\n        \"//tachyon/zk/plonk/halo2:vendor\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"prover_traits_forward\",\n    hdrs = [\"prover_traits_forward.h\"],\n)\n\ntachyon_cc_library(\n    name = \"verifier_impl\",\n    hdrs = [\"verifier_impl.h\"],\n    deps = [\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/base/functional:callback\",\n        \"//tachyon/zk/plonk/halo2:verifier\",\n    ],\n)\n\ntachyon_cc_binary(\n    name = \"verifier_replay\",\n    srcs = [\"verifier_replay.cc\"],\n    deps = [\n        \":bn254_verifier\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base/console\",\n        \"//tachyon/base/files:file_path_flag\",\n        \"//tachyon/base/files:file_util\",\n        \"//tachyon/base/flag:flag_parser\",\n        \"//tachyon/c/math/elliptic_curves/bn/bn254:fr\",\n        \"//tachyon/c/zk/plonk/keys:proving_key_impl\",\n        \"//tachyon/zk/plonk/halo2:transcript_type\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"halo2_unittests\",\n    # NOTE(chokobole): Timeout time increased due to CI.\n    timeout = \"moderate\",\n    srcs = [\n        \"bn254_prover_unittest.cc\",\n        \"bn254_transcript_unittest.cc\",\n    ],\n    env = tachyon_openmp_num_threads_env(4),\n    deps = [\n        \":bn254_prover\",\n        \"//tachyon/c/crypto/random:rng\",\n        \"//tachyon/c/zk/plonk/halo2/test:bn254_halo2_params_data\",\n        \"//tachyon/cc/math/elliptic_curves/bn/bn254:fr\",\n        \"//tachyon/math/finite_fields/test:finite_field_test\",\n        \"//tachyon/zk/lookup/halo2:scheme\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/c/zk/plonk/halo2/bn254_argument_data.cc",
    "content": "#include \"tachyon/c/zk/plonk/halo2/bn254_argument_data.h\"\n\n#include <utility>\n\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/fr_type_traits.h\"\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_dense_polynomial_type_traits.h\"\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_evaluations_type_traits.h\"\n#include \"tachyon/c/zk/plonk/halo2/bn254_argument_data_type_traits.h\"\n\nusing namespace tachyon;\n\nusing Poly =\n    math::UnivariateDensePolynomial<math::bn254::Fr, c::math::kMaxDegree>;\nusing Evals = math::UnivariateEvaluations<math::bn254::Fr, c::math::kMaxDegree>;\nusing Data = zk::plonk::halo2::ArgumentData<Poly, Evals>;\n\ntachyon_halo2_bn254_argument_data* tachyon_halo2_bn254_argument_data_create(\n    size_t num_circuits) {\n  return c::base::c_cast(new Data(num_circuits));\n}\n\nvoid tachyon_halo2_bn254_argument_data_destroy(\n    tachyon_halo2_bn254_argument_data* data) {\n  delete c::base::native_cast(data);\n}\n\nvoid tachyon_halo2_bn254_argument_data_reserve_advice_columns(\n    tachyon_halo2_bn254_argument_data* data, size_t circuit_idx,\n    size_t num_columns) {\n  c::base::native_cast(data)->advice_columns_vec()[circuit_idx].reserve(\n      num_columns);\n}\n\nvoid tachyon_halo2_bn254_argument_data_add_advice_column(\n    tachyon_halo2_bn254_argument_data* data, size_t circuit_idx,\n    tachyon_bn254_univariate_evaluations* column) {\n  c::base::native_cast(data)->advice_columns_vec()[circuit_idx].push_back(\n      std::move(c::base::native_cast(*column)));\n  tachyon_bn254_univariate_evaluations_destroy(column);\n}\n\nvoid tachyon_halo2_bn254_argument_data_reserve_advice_blinds(\n    tachyon_halo2_bn254_argument_data* data, size_t circuit_idx,\n    size_t num_blinds) {\n  c::base::native_cast(data)->advice_blinds_vec()[circuit_idx].reserve(\n      num_blinds);\n}\n\nvoid tachyon_halo2_bn254_argument_data_add_advice_blind(\n    tachyon_halo2_bn254_argument_data* data, size_t circuit_idx,\n    const tachyon_bn254_fr* value) {\n  c::base::native_cast(data)->advice_blinds_vec()[circuit_idx].push_back(\n      c::base::native_cast(*value));\n}\n\nvoid tachyon_halo2_bn254_argument_data_reserve_instance_columns(\n    tachyon_halo2_bn254_argument_data* data, size_t circuit_idx,\n    size_t num_columns) {\n  c::base::native_cast(data)->instance_columns_vec()[circuit_idx].reserve(\n      num_columns);\n}\n\nvoid tachyon_halo2_bn254_argument_data_add_instance_column(\n    tachyon_halo2_bn254_argument_data* data, size_t circuit_idx,\n    tachyon_bn254_univariate_evaluations* column) {\n  c::base::native_cast(data)->instance_columns_vec()[circuit_idx].push_back(\n      std::move(c::base::native_cast(*column)));\n  tachyon_bn254_univariate_evaluations_destroy(column);\n}\n\nvoid tachyon_halo2_bn254_argument_data_reserve_instance_polys(\n    tachyon_halo2_bn254_argument_data* data, size_t circuit_idx,\n    size_t num_polys) {\n  c::base::native_cast(data)->instance_polys_vec()[circuit_idx].reserve(\n      num_polys);\n}\n\nvoid tachyon_halo2_bn254_argument_data_add_instance_poly(\n    tachyon_halo2_bn254_argument_data* data, size_t circuit_idx,\n    tachyon_bn254_univariate_dense_polynomial* poly) {\n  c::base::native_cast(data)->instance_polys_vec()[circuit_idx].push_back(\n      std::move(c::base::native_cast(*poly)));\n  tachyon_bn254_univariate_dense_polynomial_destroy(poly);\n}\n\nvoid tachyon_halo2_bn254_argument_data_reserve_challenges(\n    tachyon_halo2_bn254_argument_data* data, size_t num_challenges) {\n  c::base::native_cast(data)->challenges().reserve(num_challenges);\n}\n\nvoid tachyon_halo2_bn254_argument_data_add_challenge(\n    tachyon_halo2_bn254_argument_data* data, const tachyon_bn254_fr* value) {\n  c::base::native_cast(data)->challenges().push_back(\n      tachyon::c::base::native_cast(*value));\n}\n"
  },
  {
    "path": "tachyon/c/zk/plonk/halo2/bn254_argument_data.h",
    "content": "/**\n * @file bn254_argument_data.h\n * @brief Halo2 Argument Data for BN254 Curve.\n *\n * This header file defines the structure and API for managing argument data in\n * Halo2 proofs over the BN254 curve. It includes functions for creating and\n * destroying argument data structures, reserving and adding advice columns,\n * instance columns, and challenges, as well as managing polynomial and blind\n * values associated with these columns.\n */\n#ifndef TACHYON_C_ZK_PLONK_HALO2_BN254_ARGUMENT_DATA_H_\n#define TACHYON_C_ZK_PLONK_HALO2_BN254_ARGUMENT_DATA_H_\n\n#include <stddef.h>\n\n#include \"tachyon/c/export.h\"\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_dense_polynomial.h\"\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_evaluations.h\"\n\n/**\n * @struct tachyon_halo2_bn254_argument_data\n * @brief Represents argument data for a Halo2 proof on the BN254 curve.\n *\n * This structure encapsulates the data necessary for constructing and verifying\n * Halo2 proofs, including advice and instance columns, and challenges. It\n * allows for the efficient management and manipulation of this data as part of\n * the proof generation and verification process.\n */\nstruct tachyon_halo2_bn254_argument_data {};\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Creates a new Halo2 argument data structure.\n *\n * Allocates and initializes a new structure for storing Halo2 argument data\n * for a given number of circuits.\n *\n * @param num_circuits The number of circuits for which to create argument data.\n * @return A pointer to the newly created argument data structure.\n */\nTACHYON_C_EXPORT tachyon_halo2_bn254_argument_data*\ntachyon_halo2_bn254_argument_data_create(size_t num_circuits);\n\n/**\n * @brief Destroys a Halo2 argument data structure.\n *\n * Frees the memory allocated for an argument data structure.\n *\n * @param data A pointer to the argument data structure to destroy.\n */\nTACHYON_C_EXPORT void tachyon_halo2_bn254_argument_data_destroy(\n    tachyon_halo2_bn254_argument_data* data);\n\n/**\n * @brief Reserves space for a specified number of advice columns in a given\n * circuit.\n *\n * @param data A pointer to the argument data structure.\n * @param circuit_idx The index of the circuit for which to reserve advice\n * columns.\n * @param num_columns The number of advice columns to reserve.\n */\nTACHYON_C_EXPORT void tachyon_halo2_bn254_argument_data_reserve_advice_columns(\n    tachyon_halo2_bn254_argument_data* data, size_t circuit_idx,\n    size_t num_columns);\n\n/**\n * @brief Adds an advice column to the argument data for a given circuit.\n * Note: The column object is consumed by this call and should not be used\n * afterwards.\n *\n * @param data A pointer to the argument data structure.\n * @param circuit_idx The index of the circuit to which the advice column is\n * added.\n * @param column A pointer to the advice column evaluations to add.\n */\nTACHYON_C_EXPORT void tachyon_halo2_bn254_argument_data_add_advice_column(\n    tachyon_halo2_bn254_argument_data* data, size_t circuit_idx,\n    tachyon_bn254_univariate_evaluations* column);\n\n/**\n * @brief Reserves space for a specified number of advice blinds in a given\n * circuit.\n *\n * @param data A pointer to the argument data structure.\n * @param circuit_idx The index of the circuit for which to reserve advice\n * blinds.\n * @param num_blinds The number of advice blinds to reserve.\n */\nTACHYON_C_EXPORT void tachyon_halo2_bn254_argument_data_reserve_advice_blinds(\n    tachyon_halo2_bn254_argument_data* data, size_t circuit_idx,\n    size_t num_blinds);\n\n/**\n * @brief Adds an advice blind value to the argument data for a given circuit.\n *\n * @param data A pointer to the argument data structure.\n * @param circuit_idx The index of the circuit to which the advice blind is\n * added.\n * @param value A const pointer to the scalar field element representing the\n * blind value.\n */\nTACHYON_C_EXPORT void tachyon_halo2_bn254_argument_data_add_advice_blind(\n    tachyon_halo2_bn254_argument_data* data, size_t circuit_idx,\n    const tachyon_bn254_fr* value);\n\n/**\n * @brief Reserves space for a specified number of instance columns in a given\n * circuit.\n *\n * @param data A pointer to the argument data structure.\n * @param circuit_idx The index of the circuit for which to reserve instance\n * columns.\n * @param num_columns The number of instance columns to reserve.\n */\nTACHYON_C_EXPORT void\ntachyon_halo2_bn254_argument_data_reserve_instance_columns(\n    tachyon_halo2_bn254_argument_data* data, size_t circuit_idx,\n    size_t num_columns);\n\n/**\n * @brief Adds an instance column to the argument data for a given circuit.\n * Note: The column object is consumed by this call and should not be used\n * afterwards.\n *\n * @param data A pointer to the argument data structure.\n * @param circuit_idx The index of the circuit to which the instance column is\n * added.\n * @param column A pointer to the instance column evaluations to add.\n */\nTACHYON_C_EXPORT void tachyon_halo2_bn254_argument_data_add_instance_column(\n    tachyon_halo2_bn254_argument_data* data, size_t circuit_idx,\n    tachyon_bn254_univariate_evaluations* column);\n\n/**\n * @brief Reserves space for a specified number of instance polynomials in a\n * given circuit.\n *\n * @param data A pointer to the argument data structure.\n * @param circuit_idx The index of the circuit for which to reserve instance\n * polynomials.\n * @param num_polys The number of instance polynomials to reserve.\n */\nTACHYON_C_EXPORT void tachyon_halo2_bn254_argument_data_reserve_instance_polys(\n    tachyon_halo2_bn254_argument_data* data, size_t circuit_idx,\n    size_t num_polys);\n\n/**\n * @brief Adds an instance polynomial to the argument data for a given circuit.\n * Note: The poly object is consumed by this call and should not be used\n * afterwards.\n *\n * @param data A pointer to the argument data structure.\n * @param circuit_idx The index of the circuit to which the instance polynomial\n * is added.\n * @param poly A pointer to the instance polynomial to add.\n */\nTACHYON_C_EXPORT void tachyon_halo2_bn254_argument_data_add_instance_poly(\n    tachyon_halo2_bn254_argument_data* data, size_t circuit_idx,\n    tachyon_bn254_univariate_dense_polynomial* poly);\n\n/**\n * @brief Reserves space for a specified number of challenges in the argument\n * data.\n *\n * This function allows for the reservation of space for challenge values that\n * will be used in the proof construction or verification. Preallocating space\n * can improve efficiency and manage memory usage effectively.\n *\n * @param data A pointer to the argument data structure.\n * @param num_challenges The number of challenges to reserve space for.\n */\nTACHYON_C_EXPORT void tachyon_halo2_bn254_argument_data_reserve_challenges(\n    tachyon_halo2_bn254_argument_data* data, size_t num_challenges);\n\n/**\n * @brief Adds a challenge value to the argument data.\n *\n * This function appends a single challenge value to the argument data.\n * Challenges are essential elements in constructing and verifying Halo2 proofs,\n * providing randomness and contributing to the security of the protocol.\n *\n * @param data A pointer to the argument data structure.\n * @param value A const pointer to the scalar field element representing the\n * challenge value.\n */\nTACHYON_C_EXPORT void tachyon_halo2_bn254_argument_data_add_challenge(\n    tachyon_halo2_bn254_argument_data* data, const tachyon_bn254_fr* value);\n\n#ifdef __cplusplus\n}  // extern \"C\"\n#endif\n\n#endif  // TACHYON_C_ZK_PLONK_HALO2_BN254_ARGUMENT_DATA_H_\n"
  },
  {
    "path": "tachyon/c/zk/plonk/halo2/bn254_argument_data_type_traits.h",
    "content": "#ifndef TACHYON_C_ZK_PLONK_HALO2_BN254_ARGUMENT_DATA_TYPE_TRAITS_H_\n#define TACHYON_C_ZK_PLONK_HALO2_BN254_ARGUMENT_DATA_TYPE_TRAITS_H_\n\n#include \"tachyon/c/base/type_traits_forward.h\"\n#include \"tachyon/c/math/polynomials/constants.h\"\n#include \"tachyon/c/zk/plonk/halo2/bn254_argument_data.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/fr.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluations.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_polynomial.h\"\n#include \"tachyon/zk/plonk/halo2/argument_data.h\"\n\nnamespace tachyon::c::base {\n\ntemplate <>\nstruct TypeTraits<tachyon::zk::plonk::halo2::ArgumentData<\n    tachyon::math::UnivariateDensePolynomial<tachyon::math::bn254::Fr,\n                                             c::math::kMaxDegree>,\n    tachyon::math::UnivariateEvaluations<tachyon::math::bn254::Fr,\n                                         c::math::kMaxDegree>>> {\n  using CType = tachyon_halo2_bn254_argument_data;\n};\n\ntemplate <>\nstruct TypeTraits<tachyon_halo2_bn254_argument_data> {\n  using NativeType = tachyon::zk::plonk::halo2::ArgumentData<\n      tachyon::math::UnivariateDensePolynomial<tachyon::math::bn254::Fr,\n                                               c::math::kMaxDegree>,\n      tachyon::math::UnivariateEvaluations<tachyon::math::bn254::Fr,\n                                           c::math::kMaxDegree>>;\n};\n\n}  // namespace tachyon::c::base\n\n#endif  // TACHYON_C_ZK_PLONK_HALO2_BN254_ARGUMENT_DATA_TYPE_TRAITS_H_\n"
  },
  {
    "path": "tachyon/c/zk/plonk/halo2/bn254_instance_columns_vec.cc",
    "content": "#include \"tachyon/c/zk/plonk/halo2/bn254_instance_columns_vec.h\"\n\n#include <vector>\n\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/fr_type_traits.h\"\n#include \"tachyon/c/zk/plonk/halo2/bn254_instance_columns_vec_type_traits.h\"\n\nusing namespace tachyon;\n\nusing ColumnsVec = std::vector<std::vector<std::vector<math::bn254::Fr>>>;\n\ntachyon_halo2_bn254_instance_columns_vec*\ntachyon_halo2_bn254_instance_columns_vec_create(size_t num_circuits) {\n  return c::base::c_cast(new ColumnsVec(num_circuits));\n}\n\nvoid tachyon_halo2_bn254_instance_columns_vec_destroy(\n    tachyon_halo2_bn254_instance_columns_vec* data) {\n  delete c::base::native_cast(data);\n}\n\nvoid tachyon_halo2_bn254_instance_columns_vec_resize_columns(\n    tachyon_halo2_bn254_instance_columns_vec* data, size_t circuit_idx,\n    size_t num_columns) {\n  c::base::native_cast(*data)[circuit_idx].resize(num_columns);\n}\n\nvoid tachyon_halo2_bn254_instance_columns_vec_reserve_values(\n    tachyon_halo2_bn254_instance_columns_vec* data, size_t circuit_idx,\n    size_t column_idx, size_t num_values) {\n  c::base::native_cast(*data)[circuit_idx][column_idx].reserve(num_values);\n}\n\nvoid tachyon_halo2_bn254_instance_columns_vec_add_values(\n    tachyon_halo2_bn254_instance_columns_vec* data, size_t circuit_idx,\n    size_t column_idx, const tachyon_bn254_fr* value) {\n  c::base::native_cast(*data)[circuit_idx][column_idx].push_back(\n      c::base::native_cast(*value));\n}\n"
  },
  {
    "path": "tachyon/c/zk/plonk/halo2/bn254_instance_columns_vec.h",
    "content": "/**\n * @file bn254_instance_columns_vec.h\n * @brief Defines the vector of instance columns for Halo2 proofs on the BN254\n * curve.\n *\n * This structure is used to manage instance columns for circuits, providing\n * functionality for creating, resizing, and managing the values within each\n * column of the instance data.\n */\n#ifndef TACHYON_C_ZK_PLONK_HALO2_BN254_INSTANCE_COLUMNS_VEC_H_\n#define TACHYON_C_ZK_PLONK_HALO2_BN254_INSTANCE_COLUMNS_VEC_H_\n\n#include <stddef.h>\n\n#include \"tachyon/c/export.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/fr.h\"\n\n/**\n * @struct tachyon_halo2_bn254_instance_columns_vec\n * @brief Represents a vector of instance columns for the Halo2 protocol.\n *\n * Encapsulates the instance data for one or more circuits, with functionality\n * to manipulate the size and content of the instance columns required for proof\n * verification.\n */\nstruct tachyon_halo2_bn254_instance_columns_vec {};\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Creates a new instance columns vector for a specified number of\n * circuits.\n *\n * @param num_circuits The number of circuits for which to create instance\n * columns.\n * @return A pointer to the newly created instance columns vector.\n */\nTACHYON_C_EXPORT tachyon_halo2_bn254_instance_columns_vec*\ntachyon_halo2_bn254_instance_columns_vec_create(size_t num_circuits);\n\n/**\n * @brief Destroys an instance columns vector, freeing its resources.\n *\n * @param data A pointer to the instance columns vector to destroy.\n */\nTACHYON_C_EXPORT void tachyon_halo2_bn254_instance_columns_vec_destroy(\n    tachyon_halo2_bn254_instance_columns_vec* data);\n\n/**\n * @brief Resizes the number of columns for a specific circuit within the\n * vector.\n *\n * @param data A pointer to the instance columns vector.\n * @param circuit_idx Index of the circuit to resize.\n * @param num_columns The new number of columns for the specified circuit.\n */\n\nTACHYON_C_EXPORT void tachyon_halo2_bn254_instance_columns_vec_resize_columns(\n    tachyon_halo2_bn254_instance_columns_vec* data, size_t circuit_idx,\n    size_t num_columns);\n\n/**\n * @brief Reserves space for values in a specific column of a circuit.\n *\n * @param data A pointer to the instance columns vector.\n * @param circuit_idx Index of the circuit containing the column.\n * @param column_idx Index of the column to reserve space in.\n * @param num_values The number of values to reserve space for.\n */\nTACHYON_C_EXPORT void tachyon_halo2_bn254_instance_columns_vec_reserve_values(\n    tachyon_halo2_bn254_instance_columns_vec* data, size_t circuit_idx,\n    size_t column_idx, size_t num_values);\n\n/**\n * @brief Adds values to a specific column of a circuit.\n *\n * @param data A pointer to the instance columns vector.\n * @param circuit_idx Index of the circuit containing the column.\n * @param column_idx Index of the column to add values to.\n * @param value A const pointer to the values to add.\n */\nTACHYON_C_EXPORT void tachyon_halo2_bn254_instance_columns_vec_add_values(\n    tachyon_halo2_bn254_instance_columns_vec* data, size_t circuit_idx,\n    size_t column_idx, const tachyon_bn254_fr* value);\n\n#ifdef __cplusplus\n}  // extern \"C\"\n#endif\n\n#endif  // TACHYON_C_ZK_PLONK_HALO2_BN254_INSTANCE_COLUMNS_VEC_H_\n"
  },
  {
    "path": "tachyon/c/zk/plonk/halo2/bn254_instance_columns_vec_type_traits.h",
    "content": "#ifndef TACHYON_C_ZK_PLONK_HALO2_BN254_INSTANCE_COLUMNS_VEC_TYPE_TRAITS_H_\n#define TACHYON_C_ZK_PLONK_HALO2_BN254_INSTANCE_COLUMNS_VEC_TYPE_TRAITS_H_\n\n#include <vector>\n\n#include \"tachyon/c/base/type_traits_forward.h\"\n#include \"tachyon/c/zk/plonk/halo2/bn254_instance_columns_vec.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/fr.h\"\n\nnamespace tachyon::c::base {\n\ntemplate <>\nstruct TypeTraits<\n    std::vector<std::vector<std::vector<tachyon::math::bn254::Fr>>>> {\n  using CType = tachyon_halo2_bn254_instance_columns_vec;\n};\n\ntemplate <>\nstruct TypeTraits<tachyon_halo2_bn254_instance_columns_vec> {\n  using NativeType =\n      std::vector<std::vector<std::vector<tachyon::math::bn254::Fr>>>;\n};\n\n}  // namespace tachyon::c::base\n\n#endif  // TACHYON_C_ZK_PLONK_HALO2_BN254_INSTANCE_COLUMNS_VEC_TYPE_TRAITS_H_\n"
  },
  {
    "path": "tachyon/c/zk/plonk/halo2/bn254_prover.cc",
    "content": "#include \"tachyon/c/zk/plonk/halo2/bn254_prover.h\"\n\n#include <string.h>\n\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1_point_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1_point_type_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g2_point_type_traits.h\"\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_dense_polynomial_type_traits.h\"\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_evaluation_domain_type_traits.h\"\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_evaluations_type_traits.h\"\n#include \"tachyon/c/zk/base/bn254_blinder_type_traits.h\"\n#include \"tachyon/c/zk/plonk/constraint_system/bn254_constraint_system_type_traits.h\"\n#include \"tachyon/c/zk/plonk/halo2/bn254_argument_data_type_traits.h\"\n#include \"tachyon/c/zk/plonk/halo2/bn254_ps.h\"\n#include \"tachyon/c/zk/plonk/halo2/bn254_transcript.h\"\n#include \"tachyon/c/zk/plonk/halo2/kzg_family_prover_impl.h\"\n#include \"tachyon/c/zk/plonk/keys/proving_key_impl.h\"\n#include \"tachyon/crypto/random/cha_cha20/cha_cha20_rng.h\"\n#include \"tachyon/crypto/random/rng_type.h\"\n#include \"tachyon/crypto/random/xor_shift/xor_shift_rng.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/halo2/bn254.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluation_domain_factory.h\"\n#include \"tachyon/zk/plonk/halo2/blake2b_transcript.h\"\n#include \"tachyon/zk/plonk/halo2/pcs_type.h\"\n#include \"tachyon/zk/plonk/halo2/poseidon_transcript.h\"\n#include \"tachyon/zk/plonk/halo2/prover.h\"\n#include \"tachyon/zk/plonk/halo2/sha256_transcript.h\"\n#include \"tachyon/zk/plonk/halo2/snark_verifier_poseidon_transcript.h\"\n#include \"tachyon/zk/plonk/halo2/transcript_type.h\"\n#include \"tachyon/zk/plonk/halo2/vendor.h\"\n\nusing namespace tachyon;\n\nusing GWCPCS = c::zk::plonk::halo2::bn254::GWCPCS;\nusing SHPlonkPCS = c::zk::plonk::halo2::bn254::SHPlonkPCS;\nusing PSEGWC = c::zk::plonk::halo2::bn254::PSEGWC;\nusing PSESHPlonk = c::zk::plonk::halo2::bn254::PSESHPlonk;\nusing ScrollGWC = c::zk::plonk::halo2::bn254::ScrollGWC;\nusing ScrollSHPlonk = c::zk::plonk::halo2::bn254::ScrollSHPlonk;\nusing XORShiftRNG = crypto::XORShiftRNG;\nusing ChaCha20RNG = crypto::ChaCha20RNG;\n\ntemplate <typename PS>\nusing ProverImpl = c::zk::plonk::halo2::KZGFamilyProverImpl<PS>;\ntemplate <typename PS>\nusing ProvingKey = c::zk::plonk::ProvingKeyImpl<PS>;\n\nnamespace {\n\ntemplate <typename PS>\nzk::plonk::halo2::Prover<PS> CreateProver(uint8_t transcript_type, uint32_t k,\n                                          const tachyon_bn254_fr* s) {\n  using PCS = typename PS::PCS;\n\n  PCS pcs;\n  size_t n = size_t{1} << k;\n  math::bn254::Fr::BigIntTy bigint;\n  memcpy(bigint.limbs, reinterpret_cast<const uint8_t*>(s->limbs),\n         sizeof(uint64_t) * math::bn254::Fr::kLimbNums);\n  CHECK(pcs.UnsafeSetup(n, math::bn254::Fr::FromMontgomery(bigint)));\n  base::Uint8VectorBuffer write_buf;\n  std::unique_ptr<crypto::TranscriptWriter<math::bn254::G1AffinePoint>> writer;\n  switch (static_cast<zk::plonk::halo2::TranscriptType>(transcript_type)) {\n    case zk::plonk::halo2::TranscriptType::kBlake2b: {\n      writer = std::make_unique<\n          zk::plonk::halo2::Blake2bWriter<math::bn254::G1AffinePoint>>(\n          std::move(write_buf));\n      break;\n    }\n    case zk::plonk::halo2::TranscriptType::kPoseidon: {\n      writer = std::make_unique<\n          zk::plonk::halo2::PoseidonWriter<math::bn254::G1AffinePoint>>(\n          std::move(write_buf));\n      break;\n    }\n    case zk::plonk::halo2::TranscriptType::kSha256: {\n      writer = std::make_unique<\n          zk::plonk::halo2::Sha256Writer<math::bn254::G1AffinePoint>>(\n          std::move(write_buf));\n      break;\n    }\n    case zk::plonk::halo2::TranscriptType::kSnarkVerifierPoseidon: {\n      writer = std::make_unique<zk::plonk::halo2::SnarkVerifierPoseidonWriter<\n          math::bn254::G1AffinePoint>>(std::move(write_buf));\n      break;\n    }\n  }\n  CHECK(writer);\n  zk::plonk::halo2::Prover<PS> prover =\n      zk::plonk::halo2::Prover<PS>::Create(std::move(pcs), std::move(writer),\n                                           /*rng=*/nullptr,\n                                           /*blinding_factors=*/0);\n  prover.set_domain(PCS::Domain::Create(n));\n  return prover;\n}\n\ntemplate <typename PS>\nzk::plonk::halo2::Prover<PS> CreateProverFromParams(uint8_t transcript_type,\n                                                    uint32_t k,\n                                                    const uint8_t* params,\n                                                    size_t params_len) {\n  using PCS = typename PS::PCS;\n\n  PCS pcs;\n  size_t n = size_t{1} << k;\n  base::ReadOnlyBuffer read_buf(params, params_len);\n  c::zk::plonk::ReadBuffer(read_buf, pcs);\n\n  base::Uint8VectorBuffer write_buf;\n  std::unique_ptr<crypto::TranscriptWriter<math::bn254::G1AffinePoint>> writer;\n  switch (static_cast<zk::plonk::halo2::TranscriptType>(transcript_type)) {\n    case zk::plonk::halo2::TranscriptType::kBlake2b: {\n      writer = std::make_unique<\n          zk::plonk::halo2::Blake2bWriter<math::bn254::G1AffinePoint>>(\n          std::move(write_buf));\n      break;\n    }\n    case zk::plonk::halo2::TranscriptType::kPoseidon: {\n      writer = std::make_unique<\n          zk::plonk::halo2::PoseidonWriter<math::bn254::G1AffinePoint>>(\n          std::move(write_buf));\n      break;\n    }\n    case zk::plonk::halo2::TranscriptType::kSha256: {\n      writer = std::make_unique<\n          zk::plonk::halo2::Sha256Writer<math::bn254::G1AffinePoint>>(\n          std::move(write_buf));\n      break;\n    }\n    case zk::plonk::halo2::TranscriptType::kSnarkVerifierPoseidon: {\n      writer = std::make_unique<zk::plonk::halo2::SnarkVerifierPoseidonWriter<\n          math::bn254::G1AffinePoint>>(std::move(write_buf));\n      break;\n    }\n  }\n  CHECK(writer);\n  zk::plonk::halo2::Prover<PS> prover =\n      zk::plonk::halo2::Prover<PS>::Create(std::move(pcs), std::move(writer),\n                                           /*rng=*/nullptr,\n                                           /*blinding_factors=*/0);\n  prover.set_domain(PCS::Domain::Create(n));\n  return prover;\n}\n\ntemplate <typename NativeProver>\nvoid Destroy(NativeProver* prover) {\n  delete prover;\n}\n\ntemplate <typename NativeEntity>\nuint32_t GetK(NativeEntity* entity) {\n  return entity->pcs().K();\n}\n\ntemplate <typename NativeEntity>\nsize_t GetN(NativeEntity* entity) {\n  return entity->pcs().N();\n}\n\ntemplate <typename NativeEntity>\nconst tachyon_bn254_g2_affine* GetSG2(NativeEntity* entity) {\n  return &c::base::c_cast(entity->pcs().SG2());\n}\n\ntemplate <typename NativeProver>\ntachyon_bn254_blinder* GetBlinder(NativeProver* prover) {\n  return &c::base::c_cast(prover->blinder());\n}\n\ntemplate <typename NativeProver>\nconst tachyon_bn254_univariate_evaluation_domain* GetDomain(\n    NativeProver* prover) {\n  return c::base::c_cast(prover->domain());\n}\n\ntemplate <typename NativeProver>\ntachyon_bn254_g1_projective* Commit(\n    NativeProver* prover,\n    const tachyon_bn254_univariate_dense_polynomial* poly) {\n  const std::vector<math::bn254::Fr>& scalars =\n      c::base::native_cast(*poly).coefficients().coefficients();\n  return prover->CommitRaw(scalars);\n}\n\ntemplate <typename NativeProver>\ntachyon_bn254_g1_projective* CommitLagrange(\n    NativeProver* prover, const tachyon_bn254_univariate_evaluations* evals) {\n  const std::vector<math::bn254::Fr>& scalars =\n      c::base::native_cast(*evals).evaluations();\n  return prover->CommitLagrangeRaw(scalars);\n}\n\ntemplate <typename NativeProver>\nvoid BatchStart(NativeProver* prover, size_t len) {\n  using PS = typename NativeProver::PS;\n  using PCS = typename PS::PCS;\n\n  if constexpr (PCS::kSupportsBatchMode) {\n    prover->pcs().SetBatchMode(len);\n  } else {\n    NOTREACHED() << \"PCS doesn't support batch commitment\";\n  }\n}\n\ntemplate <typename NativeProver>\nvoid BatchCommit(NativeProver* prover,\n                 const tachyon_bn254_univariate_dense_polynomial* poly,\n                 size_t idx) {\n  using PS = typename NativeProver::PS;\n  using PCS = typename PS::PCS;\n\n  if constexpr (PCS::kSupportsBatchMode) {\n    prover->BatchCommitAt(c::base::native_cast(*poly), idx);\n  } else {\n    NOTREACHED() << \"PCS doesn't support batch commitment\";\n  }\n}\n\ntemplate <typename NativeProver>\nvoid BatchCommitLagrange(NativeProver* prover,\n                         const tachyon_bn254_univariate_evaluations* evals,\n                         size_t idx) {\n  using PS = typename NativeProver::PS;\n  using PCS = typename PS::PCS;\n\n  if constexpr (PCS::kSupportsBatchMode) {\n    prover->BatchCommitAt(c::base::native_cast(*evals), idx);\n  } else {\n    NOTREACHED() << \"PCS doesn't support batch commitment\";\n  }\n}\n\ntemplate <typename NativeProver>\nvoid BatchEnd(NativeProver* prover, tachyon_bn254_g1_affine* points,\n              size_t len) {\n  using PS = typename NativeProver::PS;\n  using PCS = typename PS::PCS;\n  using Commitment = typename PCS::Commitment;\n\n  if constexpr (PCS::kSupportsBatchMode) {\n    std::vector<Commitment> commitments = prover->pcs().GetBatchCommitments();\n    CHECK_EQ(commitments.size(), len);\n    // TODO(chokobole): Remove this |memcpy()| by modifying\n    // |GetBatchCommitments()| to take the out parameters |points|.\n    memcpy(points, commitments.data(), len * sizeof(Commitment));\n  } else {\n    NOTREACHED() << \"PCS doesn't support batch commitment\";\n  }\n}\n\ntemplate <typename NativeProver>\nvoid SetRngState(NativeProver* prover, uint8_t rng_type, const uint8_t* state,\n                 size_t state_len) {\n  std::unique_ptr<crypto::RNG> rng;\n  switch (static_cast<crypto::RNGType>(rng_type)) {\n    case crypto::RNGType::kXORShift:\n      rng = std::make_unique<crypto::XORShiftRNG>();\n      break;\n    case crypto::RNGType::kChaCha20:\n      rng = std::make_unique<crypto::ChaCha20RNG>();\n      break;\n  }\n  CHECK(rng);\n  base::ReadOnlyBuffer buffer(state, state_len);\n  CHECK(rng->ReadFromBuffer(buffer));\n  prover->SetRng(std::move(rng));\n}\n\ntemplate <typename NativeProver>\nvoid SetTranscriptState(NativeProver* prover, const uint8_t* state,\n                        size_t state_len) {\n  uint8_t transcript_type = prover->transcript_type();\n  base::Uint8VectorBuffer write_buf;\n  switch (static_cast<zk::plonk::halo2::TranscriptType>(transcript_type)) {\n    case zk::plonk::halo2::TranscriptType::kBlake2b: {\n      auto writer = std::make_unique<\n          zk::plonk::halo2::Blake2bWriter<math::bn254::G1AffinePoint>>(\n          std::move(write_buf));\n      absl::Span<const uint8_t> state_span(state, state_len);\n      writer->SetState(state_span);\n      prover->SetTranscript(state_span, std::move(writer));\n      return;\n    }\n    case zk::plonk::halo2::TranscriptType::kPoseidon: {\n      auto writer = std::make_unique<\n          zk::plonk::halo2::PoseidonWriter<math::bn254::G1AffinePoint>>(\n          std::move(write_buf));\n      absl::Span<const uint8_t> state_span(state, state_len);\n      writer->SetState(state_span);\n      prover->SetTranscript(state_span, std::move(writer));\n      return;\n    }\n    case zk::plonk::halo2::TranscriptType::kSha256: {\n      auto writer = std::make_unique<\n          zk::plonk::halo2::Sha256Writer<math::bn254::G1AffinePoint>>(\n          std::move(write_buf));\n      absl::Span<const uint8_t> state_span(state, state_len);\n      writer->SetState(state_span);\n      prover->SetTranscript(state_span, std::move(writer));\n      return;\n    }\n    case zk::plonk::halo2::TranscriptType::kSnarkVerifierPoseidon: {\n      auto writer =\n          std::make_unique<zk::plonk::halo2::SnarkVerifierPoseidonWriter<\n              math::bn254::G1AffinePoint>>(std::move(write_buf));\n      absl::Span<const uint8_t> state_span(state, state_len);\n      writer->SetState(state_span);\n      prover->SetTranscript(state_span, std::move(writer));\n      return;\n    }\n  }\n  NOTREACHED();\n}\n\ntemplate <typename NativeProver>\nvoid SetExtendedDomain(NativeProver* prover,\n                       const tachyon_bn254_plonk_proving_key* pk) {\n  using PS = typename NativeProver::PS;\n  using PCS = typename PS::PCS;\n\n  const tachyon_bn254_plonk_verifying_key* vk =\n      tachyon_bn254_plonk_proving_key_get_verifying_key(pk);\n  const tachyon_bn254_plonk_constraint_system* cs =\n      tachyon_bn254_plonk_verifying_key_get_constraint_system(vk);\n\n  uint32_t extended_k =\n      c::base::native_cast(cs)->ComputeExtendedK(prover->pcs().K());\n  prover->set_extended_domain(\n      PCS::ExtendedDomain::Create(size_t{1} << extended_k));\n#if TACHYON_CUDA\n  prover->EnableIcicleNTT();\n#endif\n}\n\ntemplate <typename NativeProver, typename NativeProvingKey>\nvoid CreateProof(NativeProver* prover, NativeProvingKey* pk,\n                 tachyon_halo2_bn254_argument_data* data) {\n  prover->CreateProof(*pk, c::base::native_cast(data));\n}\n\ntemplate <typename NativeProver>\nvoid GetProof(NativeProver* prover, uint8_t* proof, size_t* proof_len) {\n  const crypto::TranscriptWriter<math::bn254::G1AffinePoint>* transcript =\n      prover->GetWriter();\n  const std::vector<uint8_t>& buffer = transcript->buffer().owned_buffer();\n  *proof_len = buffer.size();\n  if (proof == nullptr) return;\n  memcpy(proof, buffer.data(), buffer.size());\n}\n\n}  // namespace\n\n#define INVOKE_PROVER(Method, ...)                                            \\\n  switch (static_cast<zk::plonk::halo2::Vendor>(prover->vendor)) {            \\\n    case zk::plonk::halo2::Vendor::kPSE: {                                    \\\n      switch (static_cast<zk::plonk::halo2::PCSType>(prover->pcs_type)) {     \\\n        case zk::plonk::halo2::PCSType::kGWC: {                               \\\n          return Method(reinterpret_cast<ProverImpl<PSEGWC>*>(prover->extra), \\\n                        ##__VA_ARGS__);                                       \\\n        }                                                                     \\\n        case zk::plonk::halo2::PCSType::kSHPlonk: {                           \\\n          return Method(                                                      \\\n              reinterpret_cast<ProverImpl<PSESHPlonk>*>(prover->extra),       \\\n              ##__VA_ARGS__);                                                 \\\n        }                                                                     \\\n      }                                                                       \\\n      break;                                                                  \\\n    }                                                                         \\\n    case zk::plonk::halo2::Vendor::kScroll: {                                 \\\n      switch (static_cast<zk::plonk::halo2::PCSType>(prover->pcs_type)) {     \\\n        case zk::plonk::halo2::PCSType::kGWC: {                               \\\n          return Method(                                                      \\\n              reinterpret_cast<ProverImpl<ScrollGWC>*>(prover->extra),        \\\n              ##__VA_ARGS__);                                                 \\\n        }                                                                     \\\n        case zk::plonk::halo2::PCSType::kSHPlonk: {                           \\\n          return Method(                                                      \\\n              reinterpret_cast<ProverImpl<ScrollSHPlonk>*>(prover->extra),    \\\n              ##__VA_ARGS__);                                                 \\\n        }                                                                     \\\n      }                                                                       \\\n      break;                                                                  \\\n    }                                                                         \\\n  }                                                                           \\\n  NOTREACHED()\n\n#define INVOKE_ENTITY(Method, ...)                                            \\\n  switch (static_cast<zk::plonk::halo2::PCSType>(prover->pcs_type)) {         \\\n    case zk::plonk::halo2::PCSType::kGWC: {                                   \\\n      return Method(reinterpret_cast<zk::Entity<GWCPCS>*>(prover->extra),     \\\n                    ##__VA_ARGS__);                                           \\\n    }                                                                         \\\n    case zk::plonk::halo2::PCSType::kSHPlonk: {                               \\\n      return Method(reinterpret_cast<zk::Entity<SHPlonkPCS>*>(prover->extra), \\\n                    ##__VA_ARGS__);                                           \\\n    }                                                                         \\\n  }                                                                           \\\n  NOTREACHED()\n\n#define INVOKE_PROVER_BASE(Method, ...)                                       \\\n  switch (static_cast<zk::plonk::halo2::PCSType>(prover->pcs_type)) {         \\\n    case zk::plonk::halo2::PCSType::kGWC: {                                   \\\n      return Method(reinterpret_cast<zk::ProverBase<GWCPCS>*>(prover->extra), \\\n                    ##__VA_ARGS__);                                           \\\n    }                                                                         \\\n    case zk::plonk::halo2::PCSType::kSHPlonk: {                               \\\n      return Method(                                                          \\\n          reinterpret_cast<zk::ProverBase<SHPlonkPCS>*>(prover->extra),       \\\n          ##__VA_ARGS__);                                                     \\\n    }                                                                         \\\n  }                                                                           \\\n  NOTREACHED()\n\ntachyon_halo2_bn254_prover* tachyon_halo2_bn254_prover_create_from_unsafe_setup(\n    uint8_t vendor, uint8_t pcs_type, uint8_t transcript_type, uint32_t k,\n    const tachyon_bn254_fr* s) {\n  tachyon_halo2_bn254_prover* prover = new tachyon_halo2_bn254_prover;\n  prover->vendor = vendor;\n  prover->pcs_type = pcs_type;\n  math::bn254::BN254Curve::Init();\n  math::halo2::OverrideSubgroupGenerator();\n\n  switch (static_cast<zk::plonk::halo2::Vendor>(vendor)) {\n    case zk::plonk::halo2::Vendor::kPSE: {\n      switch (static_cast<zk::plonk::halo2::PCSType>(pcs_type)) {\n        case zk::plonk::halo2::PCSType::kGWC: {\n          prover->extra = new ProverImpl<PSEGWC>(\n              CreateProver<PSEGWC>(transcript_type, k, s), transcript_type);\n          return prover;\n        }\n        case zk::plonk::halo2::PCSType::kSHPlonk: {\n          prover->extra = new ProverImpl<PSESHPlonk>(\n              CreateProver<PSESHPlonk>(transcript_type, k, s), transcript_type);\n          return prover;\n        }\n      }\n      break;\n    }\n    case zk::plonk::halo2::Vendor::kScroll: {\n      switch (static_cast<zk::plonk::halo2::PCSType>(pcs_type)) {\n        case zk::plonk::halo2::PCSType::kGWC: {\n          prover->extra = new ProverImpl<ScrollGWC>(\n              CreateProver<ScrollGWC>(transcript_type, k, s), transcript_type);\n          return prover;\n        }\n        case zk::plonk::halo2::PCSType::kSHPlonk: {\n          prover->extra = new ProverImpl<ScrollSHPlonk>(\n              CreateProver<ScrollSHPlonk>(transcript_type, k, s),\n              transcript_type);\n          return prover;\n        }\n      }\n      break;\n    }\n  }\n  NOTREACHED();\n  return nullptr;\n}\n\ntachyon_halo2_bn254_prover* tachyon_halo2_bn254_prover_create_from_params(\n    uint8_t vendor, uint8_t pcs_type, uint8_t transcript_type, uint32_t k,\n    const uint8_t* params, size_t params_len) {\n  tachyon_halo2_bn254_prover* prover = new tachyon_halo2_bn254_prover;\n  prover->vendor = vendor;\n  prover->pcs_type = pcs_type;\n  math::bn254::BN254Curve::Init();\n  math::halo2::OverrideSubgroupGenerator();\n\n  switch (static_cast<zk::plonk::halo2::Vendor>(vendor)) {\n    case zk::plonk::halo2::Vendor::kPSE: {\n      switch (static_cast<zk::plonk::halo2::PCSType>(pcs_type)) {\n        case zk::plonk::halo2::PCSType::kGWC: {\n          prover->extra = new ProverImpl<PSEGWC>(\n              CreateProverFromParams<PSEGWC>(transcript_type, k, params,\n                                             params_len),\n              transcript_type);\n          return prover;\n        }\n        case zk::plonk::halo2::PCSType::kSHPlonk: {\n          prover->extra = new ProverImpl<PSESHPlonk>(\n              CreateProverFromParams<PSESHPlonk>(transcript_type, k, params,\n                                                 params_len),\n              transcript_type);\n          return prover;\n        }\n      }\n      break;\n    }\n    case zk::plonk::halo2::Vendor::kScroll: {\n      switch (static_cast<zk::plonk::halo2::PCSType>(pcs_type)) {\n        case zk::plonk::halo2::PCSType::kGWC: {\n          prover->extra = new ProverImpl<ScrollGWC>(\n              CreateProverFromParams<ScrollGWC>(transcript_type, k, params,\n                                                params_len),\n              transcript_type);\n          return prover;\n        }\n        case zk::plonk::halo2::PCSType::kSHPlonk: {\n          prover->extra = new ProverImpl<ScrollSHPlonk>(\n              CreateProverFromParams<ScrollSHPlonk>(transcript_type, k, params,\n                                                    params_len),\n              transcript_type);\n          return prover;\n        }\n      }\n      break;\n    }\n  }\n  NOTREACHED();\n  return nullptr;\n}\n\nvoid tachyon_halo2_bn254_prover_destroy(tachyon_halo2_bn254_prover* prover) {\n  INVOKE_PROVER(Destroy);\n}\n\nuint32_t tachyon_halo2_bn254_prover_get_k(\n    const tachyon_halo2_bn254_prover* prover) {\n  INVOKE_ENTITY(GetK);\n  return 0;\n}\n\nsize_t tachyon_halo2_bn254_prover_get_n(\n    const tachyon_halo2_bn254_prover* prover) {\n  INVOKE_ENTITY(GetN);\n  return 0;\n}\n\nconst tachyon_bn254_g2_affine* tachyon_halo2_bn254_prover_get_s_g2(\n    const tachyon_halo2_bn254_prover* prover) {\n  INVOKE_ENTITY(GetSG2);\n  return nullptr;\n}\n\ntachyon_bn254_blinder* tachyon_halo2_bn254_prover_get_blinder(\n    tachyon_halo2_bn254_prover* prover) {\n  INVOKE_PROVER_BASE(GetBlinder);\n  return nullptr;\n}\n\nconst tachyon_bn254_univariate_evaluation_domain*\ntachyon_halo2_bn254_prover_get_domain(\n    const tachyon_halo2_bn254_prover* prover) {\n  INVOKE_ENTITY(GetDomain);\n  return nullptr;\n}\n\ntachyon_bn254_g1_projective* tachyon_halo2_bn254_prover_commit(\n    const tachyon_halo2_bn254_prover* prover,\n    const tachyon_bn254_univariate_dense_polynomial* poly) {\n  INVOKE_PROVER(Commit, poly);\n  return nullptr;\n}\n\ntachyon_bn254_g1_projective* tachyon_halo2_bn254_prover_commit_lagrange(\n    const tachyon_halo2_bn254_prover* prover,\n    const tachyon_bn254_univariate_evaluations* evals) {\n  INVOKE_PROVER(CommitLagrange, evals);\n  return nullptr;\n}\n\nvoid tachyon_halo2_bn254_prover_batch_start(\n    const tachyon_halo2_bn254_prover* prover, size_t len) {\n  INVOKE_PROVER(BatchStart, len);\n}\n\nvoid tachyon_halo2_bn254_prover_batch_commit(\n    const tachyon_halo2_bn254_prover* prover,\n    const tachyon_bn254_univariate_dense_polynomial* poly, size_t idx) {\n  INVOKE_PROVER(BatchCommit, poly, idx);\n}\n\nvoid tachyon_halo2_bn254_prover_batch_commit_lagrange(\n    const tachyon_halo2_bn254_prover* prover,\n    const tachyon_bn254_univariate_evaluations* evals, size_t idx) {\n  INVOKE_PROVER(BatchCommitLagrange, evals, idx);\n}\n\nvoid tachyon_halo2_bn254_prover_batch_end(\n    const tachyon_halo2_bn254_prover* prover, tachyon_bn254_g1_affine* points,\n    size_t len) {\n  INVOKE_PROVER(BatchEnd, points, len);\n}\n\nvoid tachyon_halo2_bn254_prover_set_rng_state(\n    tachyon_halo2_bn254_prover* prover, uint8_t rng_type, const uint8_t* state,\n    size_t state_len) {\n  INVOKE_PROVER(SetRngState, rng_type, state, state_len);\n}\n\nvoid tachyon_halo2_bn254_prover_set_transcript_state(\n    tachyon_halo2_bn254_prover* prover, const uint8_t* state,\n    size_t state_len) {\n  INVOKE_PROVER(SetTranscriptState, state, state_len);\n}\n\nvoid tachyon_halo2_bn254_prover_set_extended_domain(\n    tachyon_halo2_bn254_prover* prover,\n    const tachyon_bn254_plonk_proving_key* pk) {\n  INVOKE_PROVER(SetExtendedDomain, pk);\n}\n\nvoid tachyon_halo2_bn254_prover_create_proof(\n    tachyon_halo2_bn254_prover* prover, tachyon_bn254_plonk_proving_key* pk,\n    tachyon_halo2_bn254_argument_data* data) {\n  switch (static_cast<zk::plonk::halo2::Vendor>(prover->vendor)) {\n    case zk::plonk::halo2::Vendor::kPSE: {\n      switch (static_cast<zk::plonk::halo2::PCSType>(prover->pcs_type)) {\n        case zk::plonk::halo2::PCSType::kGWC: {\n          CreateProof(reinterpret_cast<ProverImpl<PSEGWC>*>(prover->extra),\n                      reinterpret_cast<ProvingKey<PSEGWC>*>(pk->extra), data);\n          return;\n        }\n        case zk::plonk::halo2::PCSType::kSHPlonk: {\n          CreateProof(reinterpret_cast<ProverImpl<PSESHPlonk>*>(prover->extra),\n                      reinterpret_cast<ProvingKey<PSESHPlonk>*>(pk->extra),\n                      data);\n          return;\n        }\n      }\n      break;\n    }\n    case zk::plonk::halo2::Vendor::kScroll: {\n      switch (static_cast<zk::plonk::halo2::PCSType>(prover->pcs_type)) {\n        case zk::plonk::halo2::PCSType::kGWC: {\n          CreateProof(reinterpret_cast<ProverImpl<ScrollGWC>*>(prover->extra),\n                      reinterpret_cast<ProvingKey<ScrollGWC>*>(pk->extra),\n                      data);\n          return;\n        }\n        case zk::plonk::halo2::PCSType::kSHPlonk: {\n          CreateProof(\n              reinterpret_cast<ProverImpl<ScrollSHPlonk>*>(prover->extra),\n              reinterpret_cast<ProvingKey<ScrollSHPlonk>*>(pk->extra), data);\n          return;\n        }\n      }\n      break;\n    }\n  }\n  NOTREACHED();\n}\n\nvoid tachyon_halo2_bn254_prover_get_proof(\n    const tachyon_halo2_bn254_prover* prover, uint8_t* proof,\n    size_t* proof_len) {\n  INVOKE_PROVER_BASE(GetProof, proof, proof_len);\n}\n\nvoid tachyon_halo2_bn254_prover_set_transcript_repr(\n    const tachyon_halo2_bn254_prover* prover,\n    tachyon_bn254_plonk_proving_key* pk) {\n  switch (static_cast<zk::plonk::halo2::Vendor>(prover->vendor)) {\n    case zk::plonk::halo2::Vendor::kPSE: {\n      switch (static_cast<zk::plonk::halo2::PCSType>(prover->pcs_type)) {\n        case zk::plonk::halo2::PCSType::kGWC: {\n          reinterpret_cast<ProvingKey<PSEGWC>*>(pk->extra)->SetTranscriptRepr(\n              *reinterpret_cast<const zk::Entity<GWCPCS>*>(prover->extra));\n          return;\n        }\n        case zk::plonk::halo2::PCSType::kSHPlonk: {\n          reinterpret_cast<ProvingKey<PSESHPlonk>*>(pk->extra)\n              ->SetTranscriptRepr(\n                  *reinterpret_cast<const zk::Entity<SHPlonkPCS>*>(\n                      prover->extra));\n          return;\n        }\n      }\n      break;\n    }\n    case zk::plonk::halo2::Vendor::kScroll: {\n      switch (static_cast<zk::plonk::halo2::PCSType>(prover->pcs_type)) {\n        case zk::plonk::halo2::PCSType::kGWC: {\n          reinterpret_cast<ProvingKey<ScrollGWC>*>(pk->extra)\n              ->SetTranscriptRepr(\n                  *reinterpret_cast<const zk::Entity<GWCPCS>*>(prover->extra));\n          return;\n        }\n        case zk::plonk::halo2::PCSType::kSHPlonk: {\n          reinterpret_cast<ProvingKey<ScrollSHPlonk>*>(pk->extra)\n              ->SetTranscriptRepr(\n                  *reinterpret_cast<const zk::Entity<SHPlonkPCS>*>(\n                      prover->extra));\n          return;\n        }\n      }\n      break;\n    }\n  }\n  NOTREACHED();\n}\n"
  },
  {
    "path": "tachyon/c/zk/plonk/halo2/bn254_prover.h",
    "content": "/**\n * @file bn254_prover.h\n * @brief Interface for the Halo2 BN254 Prover.\n *\n * This header file defines the structure and API for the prover specific to\n * the Halo2 proof system on the BN254 curve. This includes functions for\n * creating provers, committing to polynomials or evaluations, and generating\n * proofs.\n */\n#ifndef TACHYON_C_ZK_PLONK_HALO2_BN254_PROVER_H_\n#define TACHYON_C_ZK_PLONK_HALO2_BN254_PROVER_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include \"tachyon/c/export.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/fr.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g2.h\"\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_evaluation_domain.h\"\n#include \"tachyon/c/zk/base/bn254_blinder.h\"\n#include \"tachyon/c/zk/plonk/halo2/bn254_argument_data.h\"\n#include \"tachyon/c/zk/plonk/keys/bn254_plonk_proving_key.h\"\n\n/**\n * @struct tachyon_halo2_bn254_prover\n * @brief Structure representing a prover for the Halo2 protocol over BN254.\n *\n * Encapsulates the state and functionality required for constructing Halo2\n * proofs using the construction on the BN254 curve. This includes managing\n * commitments, handling randomness, and generating the cryptographic proof.\n */\nstruct tachyon_halo2_bn254_prover {\n  uint8_t vendor;\n  uint8_t pcs_type;\n  void* extra;\n};\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Creates a prover from unsafe setup parameters.\n *\n * @param vendor Identifier for the vendor.\n * @param pcs_type Identifier for the pcs type.\n * @param transcript_type Identifier for the transcript type.\n * @param k Security parameter.\n * @param s The setup parameter s as a scalar field element.\n * @return A const pointer to the newly created prover instance.\n */\nTACHYON_C_EXPORT tachyon_halo2_bn254_prover*\ntachyon_halo2_bn254_prover_create_from_unsafe_setup(uint8_t vendor,\n                                                    uint8_t pcs_type,\n                                                    uint8_t transcript_type,\n                                                    uint32_t k,\n                                                    const tachyon_bn254_fr* s);\n\n/**\n * @brief Creates a prover from given parameters.\n *\n * Initializes a prover instance for the Halo2 protocol using the specified\n * parameters, facilitating the proof generation process for given circuits.\n *\n * @param vendor Identifier for the vendor.\n * @param pcs_type Identifier for the pcs type.\n * @param transcript_type The type of transcript to be used.\n * @param k The circuit size parameter.\n * @param params A const pointer to the parameters used for prover creation.\n * @param params_len The length of the parameters array.\n * @return A pointer to the newly created prover instance.\n */\nTACHYON_C_EXPORT tachyon_halo2_bn254_prover*\ntachyon_halo2_bn254_prover_create_from_params(uint8_t vendor, uint8_t pcs_type,\n                                              uint8_t transcript_type,\n                                              uint32_t k, const uint8_t* params,\n                                              size_t params_len);\n\n/**\n * @brief Destroys a prover instance, freeing its resources.\n *\n * @param prover A pointer to the prover to destroy.\n */\nTACHYON_C_EXPORT void tachyon_halo2_bn254_prover_destroy(\n    tachyon_halo2_bn254_prover* prover);\n\n/**\n * @brief Retrieves the 'k' parameter of the prover.\n *\n * This function returns the circuit size parameter 'k', which is crucial for\n * the setup and execution of the Halo2 protocol.\n *\n * @param prover A const pointer to the prover.\n * @return The 'k' parameter of the prover.\n */\nTACHYON_C_EXPORT uint32_t\ntachyon_halo2_bn254_prover_get_k(const tachyon_halo2_bn254_prover* prover);\n\n/**\n * @brief Retrieves the number of circuits handled by the prover.\n *\n * @param prover A const pointer to the prover.\n * @return The number of circuits that the prover is configured to handle.\n */\nTACHYON_C_EXPORT size_t\ntachyon_halo2_bn254_prover_get_n(const tachyon_halo2_bn254_prover* prover);\n\n/**\n * @brief Retrieves the G2 affine representation of the setup parameter s.\n *\n * @param prover A const pointer to the prover.\n * @return A pointer to the G2 affine representation of s.\n */\nTACHYON_C_EXPORT const tachyon_bn254_g2_affine*\ntachyon_halo2_bn254_prover_get_s_g2(const tachyon_halo2_bn254_prover* prover);\n\n/**\n * @brief Retrieves the blinder instance associated with the prover.\n *\n * @param prover A pointer to the prover.\n * @return A pointer to the blinder used by the prover.\n */\nTACHYON_C_EXPORT tachyon_bn254_blinder* tachyon_halo2_bn254_prover_get_blinder(\n    tachyon_halo2_bn254_prover* prover);\n\n/**\n * @brief Retrieves the evaluation domain used by the prover.\n *\n * @param prover A const pointer to the prover.\n * @return A pointer to the evaluation domain used by the prover.\n */\nTACHYON_C_EXPORT const tachyon_bn254_univariate_evaluation_domain*\ntachyon_halo2_bn254_prover_get_domain(const tachyon_halo2_bn254_prover* prover);\n\n/**\n * @brief Commits to a polynomial using the prover.\n *\n * This function generates a commitment to a given polynomial as part of the\n * proof generation process.\n *\n * @param prover A const pointer to the prover.\n * @param poly A const pointer to the polynomial to commit.\n * @return A pointer to the commitment in the form of a G1 projective point.\n */\nTACHYON_C_EXPORT tachyon_bn254_g1_projective* tachyon_halo2_bn254_prover_commit(\n    const tachyon_halo2_bn254_prover* prover,\n    const tachyon_bn254_univariate_dense_polynomial* poly);\n\n/**\n * @brief Commits to polynomial evaluations (Lagrange form) using the prover.\n *\n * This function generates a commitment to the evaluations of a polynomial,\n * allowing for efficient handling of the polynomial in its evaluated form.\n *\n * @param prover A const pointer to the prover.\n * @param evals A const pointer to the evaluations to commit.\n * @return A pointer to the commitment in the form of a G1 projective point.\n */\nTACHYON_C_EXPORT tachyon_bn254_g1_projective*\ntachyon_halo2_bn254_prover_commit_lagrange(\n    const tachyon_halo2_bn254_prover* prover,\n    const tachyon_bn254_univariate_evaluations* evals);\n\n/**\n * @brief Marks the prover to prepare for batch commitment.\n *\n * @param prover A const pointer to the prover.\n * @param len The number of commitments.\n */\nTACHYON_C_EXPORT void tachyon_halo2_bn254_prover_batch_start(\n    const tachyon_halo2_bn254_prover* prover, size_t len);\n\n/**\n * @brief Commits to a polynomial using the prover.\n *\n * Unlike \\ref tachyon_halo2_bn254_prover_commit(), this function doesn't\n * generate a commitment immediately to avoid expensive inverse operations.\n *\n * @param prover A const pointer to the prover.\n * @param poly A const pointer to the polynomial to commit.\n * @param idx The index of the commitment.\n */\nTACHYON_C_EXPORT void tachyon_halo2_bn254_prover_batch_commit(\n    const tachyon_halo2_bn254_prover* prover,\n    const tachyon_bn254_univariate_dense_polynomial* poly, size_t idx);\n\n/**\n * @brief Commits to polynomial evaluations (Lagrange form) using the prover.\n *\n * Unlike \\ref tachyon_halo2_bn254_prover_commit_lagrange(),\n * this function doesn't generate a commitment immediately to avoid expensive\n * inverse operation.\n *\n * @param prover A const pointer to the prover.\n * @param evals A const pointer to the evaluations to commit.\n * @param idx The index of the commitment.\n */\nTACHYON_C_EXPORT void tachyon_halo2_bn254_prover_batch_commit_lagrange(\n    const tachyon_halo2_bn254_prover* prover,\n    const tachyon_bn254_univariate_evaluations* evals, size_t idx);\n\n/**\n * @brief Retrieves the resulting commitment from the prover.\n *\n * @param prover A const pointer to the prover.\n * @param points A pointer to the affine points.\n * @param len The number of commitments.\n */\nTACHYON_C_EXPORT void tachyon_halo2_bn254_prover_batch_end(\n    const tachyon_halo2_bn254_prover* prover, tachyon_bn254_g1_affine* points,\n    size_t len);\n\n/**\n * @brief Sets the random number generator state for the prover.\n *\n * Configures the internal RNG state, ensuring the reproducibility and security\n * of the random numbers used in the proof generation process.\n *\n * @param prover A pointer to the prover.\n * @param rng_type Identifier for the rng type.\n * @param state A const pointer to the RNG state.\n * @param state_len Length of the RNG state array.\n */\nTACHYON_C_EXPORT void tachyon_halo2_bn254_prover_set_rng_state(\n    tachyon_halo2_bn254_prover* prover, uint8_t rng_type, const uint8_t* state,\n    size_t state_len);\n\n/**\n * @brief Sets the state of the transcript to a specific value.\n *\n * This is crucial for ensuring consistency and security across the entire proof\n * generation and verification process.\n *\n * @param prover A pointer to the prover.\n * @param state A const pointer to the transcript state.\n * @param state_len Length of the transcript state array.\n */\nTACHYON_C_EXPORT void tachyon_halo2_bn254_prover_set_transcript_state(\n    tachyon_halo2_bn254_prover* prover, const uint8_t* state, size_t state_len);\n\n/**\n * @brief Sets the extended domain for the scroll versioned prover based on the\n * proving key.\n *\n * This function allows the prover to operate over an extended domain, which is\n * necessary for certain types of proofs that require a larger evaluation\n * domain.\n *\n * @param prover A pointer to the prover.\n * @param pk A const pointer to the proving key that contains the extended\n * domain.\n */\nTACHYON_C_EXPORT void tachyon_halo2_bn254_prover_set_extended_domain(\n    tachyon_halo2_bn254_prover* prover,\n    const tachyon_bn254_plonk_proving_key* pk);\n\n/**\n * @brief Initiates the proof creation process using the scroll versioned\n * prover, proving key, and argument data.\n *\n * @param prover A pointer to the prover.\n * @param pk A pointer to the proving key used for generating the proof.\n * @param data A pointer to the argument data required for the proof.\n */\nTACHYON_C_EXPORT void tachyon_halo2_bn254_prover_create_proof(\n    tachyon_halo2_bn254_prover* prover, tachyon_bn254_plonk_proving_key* pk,\n    tachyon_halo2_bn254_argument_data* data);\n\n/**\n * @brief Retrieves the generated proof.\n *\n * If the proof parameter is NULL, the function will provide the necessary\n * length for the proof via proof_len. If the proof parameter is not NULL, the\n * function will fill it with the generated proof data.\n *\n * @param prover A const pointer to the prover that generated the proof.\n * @param proof A pointer to the buffer where the proof will be stored.\n * @param proof_len A pointer to a variable where the length of the proof will\n * be stored.\n */\nTACHYON_C_EXPORT void tachyon_halo2_bn254_prover_get_proof(\n    const tachyon_halo2_bn254_prover* prover, uint8_t* proof,\n    size_t* proof_len);\n\n/**\n * @brief Sets the representation of the transcript for the scroll versioned\n * prover based on the proving key.\n *\n * This function configures how the prover should interpret the transcript,\n * impacting the format and structure of the generated proof.\n *\n * @param prover A const pointer to the prover.\n * @param pk A pointer to the proving key that dictates the transcript\n * representation.\n */\nTACHYON_C_EXPORT void tachyon_halo2_bn254_prover_set_transcript_repr(\n    const tachyon_halo2_bn254_prover* prover,\n    tachyon_bn254_plonk_proving_key* pk);\n\n#ifdef __cplusplus\n}  // extern \"C\"\n#endif\n\n#endif  // TACHYON_C_ZK_PLONK_HALO2_BN254_PROVER_H_\n"
  },
  {
    "path": "tachyon/c/zk/plonk/halo2/bn254_prover_unittest.cc",
    "content": "#include \"tachyon/c/zk/plonk/halo2/bn254_prover.h\"\n\n#include <memory>\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/c/crypto/random/rng.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/fq_type_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/fr_type_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1_point_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1_point_type_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g2_point_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g2_point_type_traits.h\"\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_dense_polynomial_type_traits.h\"\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_evaluation_domain_type_traits.h\"\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_evaluations_type_traits.h\"\n#include \"tachyon/c/zk/base/bn254_blinder_type_traits.h\"\n#include \"tachyon/c/zk/plonk/halo2/bn254_ps.h\"\n#include \"tachyon/c/zk/plonk/halo2/bn254_transcript.h\"\n#include \"tachyon/c/zk/plonk/halo2/kzg_family_prover_impl.h\"\n#include \"tachyon/c/zk/plonk/halo2/test/bn254_halo2_params_data.h\"\n#include \"tachyon/crypto/random/cha_cha20/cha_cha20_rng.h\"\n#include \"tachyon/crypto/random/rng_type.h\"\n#include \"tachyon/crypto/random/xor_shift/xor_shift_rng.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/bn254.h\"\n#include \"tachyon/zk/plonk/halo2/blake2b_transcript.h\"\n#include \"tachyon/zk/plonk/halo2/pcs_type.h\"\n#include \"tachyon/zk/plonk/halo2/poseidon_transcript.h\"\n#include \"tachyon/zk/plonk/halo2/sha256_transcript.h\"\n#include \"tachyon/zk/plonk/halo2/snark_verifier_poseidon_transcript.h\"\n#include \"tachyon/zk/plonk/halo2/transcript_type.h\"\n#include \"tachyon/zk/plonk/halo2/vendor.h\"\n\nnamespace tachyon::zk::plonk::halo2::bn254 {\n\nnamespace {\n\nusing GWCPCS = c::zk::plonk::halo2::bn254::GWCPCS;\nusing SHPlonkPCS = c::zk::plonk::halo2::bn254::SHPlonkPCS;\nusing PSEGWC = c::zk::plonk::halo2::bn254::PSEGWC;\nusing PSESHPlonk = c::zk::plonk::halo2::bn254::PSESHPlonk;\nusing ScrollGWC = c::zk::plonk::halo2::bn254::ScrollGWC;\nusing ScrollSHPlonk = c::zk::plonk::halo2::bn254::ScrollSHPlonk;\n\ntemplate <typename PS>\nusing ProverImpl = c::zk::plonk::halo2::KZGFamilyProverImpl<PS>;\n\nstruct Param {\n  uint8_t vendor;\n  uint8_t pcs_type;\n  uint8_t transcript_type;\n\n  Param(uint8_t vendor, uint8_t pcs_type, uint8_t transcript_type)\n      : vendor(vendor), pcs_type(pcs_type), transcript_type(transcript_type) {}\n};\n\nclass ProverTest : public testing::TestWithParam<Param> {\n public:\n  void SetUp() override {\n    Param param = GetParam();\n\n    k_ = 5;\n    s_ = math::bn254::Fr(2);\n    const tachyon_bn254_fr& c_s = c::base::c_cast(s_);\n    prover_ = tachyon_halo2_bn254_prover_create_from_unsafe_setup(\n        param.vendor, param.pcs_type, param.transcript_type, k_, &c_s);\n  }\n\n  void TearDown() override { tachyon_halo2_bn254_prover_destroy(prover_); }\n\n protected:\n  tachyon_halo2_bn254_prover* prover_ = nullptr;\n  uint32_t k_;\n  math::bn254::Fr s_;\n};\n\n}  // namespace\n\nINSTANTIATE_TEST_SUITE_P(\n    ProverTest, ProverTest,\n    testing::Values(\n        Param(TACHYON_HALO2_PSE_VENDOR, TACHYON_HALO2_GWC_PCS,\n              TACHYON_HALO2_BLAKE2B_TRANSCRIPT),\n        Param(TACHYON_HALO2_PSE_VENDOR, TACHYON_HALO2_GWC_PCS,\n              TACHYON_HALO2_POSEIDON_TRANSCRIPT),\n        Param(TACHYON_HALO2_PSE_VENDOR, TACHYON_HALO2_GWC_PCS,\n              TACHYON_HALO2_SHA256_TRANSCRIPT),\n        Param(TACHYON_HALO2_PSE_VENDOR, TACHYON_HALO2_GWC_PCS,\n              TACHYON_HALO2_SNARK_VERIFIER_POSEIDON_TRANSCRIPT),\n        Param(TACHYON_HALO2_SCROLL_VENDOR, TACHYON_HALO2_GWC_PCS,\n              TACHYON_HALO2_BLAKE2B_TRANSCRIPT),\n        Param(TACHYON_HALO2_SCROLL_VENDOR, TACHYON_HALO2_GWC_PCS,\n              TACHYON_HALO2_POSEIDON_TRANSCRIPT),\n        Param(TACHYON_HALO2_SCROLL_VENDOR, TACHYON_HALO2_GWC_PCS,\n              TACHYON_HALO2_SHA256_TRANSCRIPT),\n        Param(TACHYON_HALO2_SCROLL_VENDOR, TACHYON_HALO2_GWC_PCS,\n              TACHYON_HALO2_SNARK_VERIFIER_POSEIDON_TRANSCRIPT),\n        Param(TACHYON_HALO2_PSE_VENDOR, TACHYON_HALO2_SHPLONK_PCS,\n              TACHYON_HALO2_BLAKE2B_TRANSCRIPT),\n        Param(TACHYON_HALO2_PSE_VENDOR, TACHYON_HALO2_SHPLONK_PCS,\n              TACHYON_HALO2_POSEIDON_TRANSCRIPT),\n        Param(TACHYON_HALO2_PSE_VENDOR, TACHYON_HALO2_SHPLONK_PCS,\n              TACHYON_HALO2_SHA256_TRANSCRIPT),\n        Param(TACHYON_HALO2_PSE_VENDOR, TACHYON_HALO2_SHPLONK_PCS,\n              TACHYON_HALO2_SNARK_VERIFIER_POSEIDON_TRANSCRIPT),\n        Param(TACHYON_HALO2_SCROLL_VENDOR, TACHYON_HALO2_SHPLONK_PCS,\n              TACHYON_HALO2_BLAKE2B_TRANSCRIPT),\n        Param(TACHYON_HALO2_SCROLL_VENDOR, TACHYON_HALO2_SHPLONK_PCS,\n              TACHYON_HALO2_POSEIDON_TRANSCRIPT),\n        Param(TACHYON_HALO2_SCROLL_VENDOR, TACHYON_HALO2_SHPLONK_PCS,\n              TACHYON_HALO2_SHA256_TRANSCRIPT),\n        Param(TACHYON_HALO2_SCROLL_VENDOR, TACHYON_HALO2_SHPLONK_PCS,\n              TACHYON_HALO2_SNARK_VERIFIER_POSEIDON_TRANSCRIPT)));\n\nTEST_P(ProverTest, Constructor) {\n  Param param = GetParam();\n\n  tachyon_halo2_bn254_prover* prover_from_halo2_parmas =\n      tachyon_halo2_bn254_prover_create_from_params(\n          param.vendor, param.pcs_type, param.transcript_type, k_,\n          reinterpret_cast<const uint8_t*>(\n              c::zk::plonk::halo2::bn254::kExpectedHalo2Params),\n          std::size(c::zk::plonk::halo2::bn254::kExpectedHalo2Params));\n\n  const tachyon_bn254_g2_affine* expected_s_g2 =\n      tachyon_halo2_bn254_prover_get_s_g2(prover_);\n  const tachyon_bn254_g2_affine* s_g2 =\n      tachyon_halo2_bn254_prover_get_s_g2(prover_from_halo2_parmas);\n  ASSERT_TRUE(tachyon_bn254_g2_affine_eq(expected_s_g2, s_g2));\n  tachyon_halo2_bn254_prover_destroy(prover_from_halo2_parmas);\n}\n\nTEST_P(ProverTest, Getters) {\n  Param param = GetParam();\n  if (param.vendor != TACHYON_HALO2_PSE_VENDOR ||\n      param.transcript_type != TACHYON_HALO2_BLAKE2B_TRANSCRIPT) {\n    GTEST_SKIP() << \"Getters test is not related to vendor and transcript_type\";\n  }\n\n  EXPECT_EQ(tachyon_halo2_bn254_prover_get_k(prover_), k_);\n  EXPECT_EQ(tachyon_halo2_bn254_prover_get_n(prover_), size_t{1} << k_);\n  tachyon_bn254_blinder* blinder =\n      tachyon_halo2_bn254_prover_get_blinder(prover_);\n  const tachyon_bn254_univariate_evaluation_domain* domain =\n      tachyon_halo2_bn254_prover_get_domain(prover_);\n  switch (static_cast<PCSType>(prover_->pcs_type)) {\n    case PCSType::kGWC: {\n      EXPECT_EQ(blinder,\n                &c::base::c_cast(\n                    reinterpret_cast<zk::ProverBase<GWCPCS>*>(prover_->extra)\n                        ->blinder()));\n      EXPECT_EQ(\n          domain,\n          c::base::c_cast(\n              reinterpret_cast<zk::Entity<GWCPCS>*>(prover_->extra)->domain()));\n      break;\n    }\n    case PCSType::kSHPlonk: {\n      EXPECT_EQ(blinder,\n                &c::base::c_cast(reinterpret_cast<zk::ProverBase<SHPlonkPCS>*>(\n                                     prover_->extra)\n                                     ->blinder()));\n      EXPECT_EQ(domain,\n                c::base::c_cast(\n                    reinterpret_cast<zk::Entity<SHPlonkPCS>*>(prover_->extra)\n                        ->domain()));\n      break;\n    }\n  }\n\n  // NOTE(dongchangYoo): |expected_s_g2| can be generated by doubling\n  // g2-generator since |s| equals to 2.\n  tachyon_bn254_g2_affine expected_gen = tachyon_bn254_g2_affine_generator();\n  tachyon_bn254_g2_jacobian expected_s_g2_jacob =\n      tachyon_bn254_g2_affine_dbl(&expected_gen);\n  const tachyon_bn254_g2_affine* s_g2_affine =\n      tachyon_halo2_bn254_prover_get_s_g2(prover_);\n  EXPECT_EQ(c::base::native_cast(*s_g2_affine),\n            c::base::native_cast(expected_s_g2_jacob).ToAffine());\n}\n\nTEST_P(ProverTest, Commit) {\n  Param param = GetParam();\n  if (param.transcript_type != TACHYON_HALO2_BLAKE2B_TRANSCRIPT) {\n    GTEST_SKIP() << \"Commit test is not related to transcript type\";\n  }\n\n  using Poly =\n      math::UnivariateDensePolynomial<math::bn254::Fr, c::math::kMaxDegree>;\n  Poly poly = Poly::Random(5);\n\n  tachyon_bn254_g1_projective* point =\n      tachyon_halo2_bn254_prover_commit(prover_, c::base::c_cast(&poly));\n\n  math::bn254::G1ProjectivePoint expected;\n  switch (static_cast<Vendor>(prover_->vendor)) {\n    case Vendor::kPSE: {\n      switch (static_cast<PCSType>(prover_->pcs_type)) {\n        case PCSType::kGWC: {\n          expected = reinterpret_cast<ProverImpl<PSEGWC>*>(prover_->extra)\n                         ->Commit(poly)\n                         .ToProjective();\n          break;\n        }\n        case PCSType::kSHPlonk: {\n          expected = reinterpret_cast<ProverImpl<PSESHPlonk>*>(prover_->extra)\n                         ->Commit(poly)\n                         .ToProjective();\n          break;\n        }\n      }\n      break;\n    }\n    case Vendor::kScroll: {\n      switch (static_cast<PCSType>(prover_->pcs_type)) {\n        case PCSType::kGWC: {\n          expected = reinterpret_cast<ProverImpl<ScrollGWC>*>(prover_->extra)\n                         ->Commit(poly)\n                         .ToProjective();\n          break;\n        }\n        case PCSType::kSHPlonk: {\n          expected =\n              reinterpret_cast<ProverImpl<ScrollSHPlonk>*>(prover_->extra)\n                  ->Commit(poly)\n                  .ToProjective();\n          break;\n        }\n      }\n      break;\n    }\n  }\n\n  EXPECT_EQ(c::base::native_cast(*point), expected);\n}\n\nTEST_P(ProverTest, CommitLagrange) {\n  Param param = GetParam();\n  if (param.transcript_type != TACHYON_HALO2_BLAKE2B_TRANSCRIPT) {\n    GTEST_SKIP() << \"CommitLagrange test is not related to transcript type\";\n  }\n\n  using Evals =\n      math::UnivariateEvaluations<math::bn254::Fr, c::math::kMaxDegree>;\n  Evals evals = Evals::Random(5);\n\n  tachyon_bn254_g1_projective* point =\n      tachyon_halo2_bn254_prover_commit_lagrange(prover_,\n                                                 c::base::c_cast(&evals));\n\n  math::bn254::G1ProjectivePoint expected;\n  switch (static_cast<Vendor>(prover_->vendor)) {\n    case Vendor::kPSE: {\n      switch (static_cast<PCSType>(prover_->pcs_type)) {\n        case PCSType::kGWC: {\n          expected = reinterpret_cast<ProverImpl<PSEGWC>*>(prover_->extra)\n                         ->Commit(evals)\n                         .ToProjective();\n          break;\n        }\n        case PCSType::kSHPlonk: {\n          expected = reinterpret_cast<ProverImpl<PSESHPlonk>*>(prover_->extra)\n                         ->Commit(evals)\n                         .ToProjective();\n          break;\n        }\n      }\n      break;\n    }\n    case Vendor::kScroll: {\n      switch (static_cast<PCSType>(prover_->pcs_type)) {\n        case PCSType::kGWC: {\n          expected = reinterpret_cast<ProverImpl<ScrollGWC>*>(prover_->extra)\n                         ->Commit(evals)\n                         .ToProjective();\n          break;\n        }\n        case PCSType::kSHPlonk: {\n          expected =\n              reinterpret_cast<ProverImpl<ScrollSHPlonk>*>(prover_->extra)\n                  ->Commit(evals)\n                  .ToProjective();\n          break;\n        }\n      }\n      break;\n    }\n  }\n\n  EXPECT_EQ(c::base::native_cast(*point), expected);\n}\n\nTEST_P(ProverTest, BatchCommit) {\n  Param param = GetParam();\n  if (param.transcript_type != TACHYON_HALO2_BLAKE2B_TRANSCRIPT) {\n    GTEST_SKIP() << \"BatchCommit test is not related to transcript type\";\n  }\n\n  using Poly =\n      math::UnivariateDensePolynomial<math::bn254::Fr, c::math::kMaxDegree>;\n  std::vector<Poly> polys =\n      base::CreateVector(4, []() { return Poly::Random(5); });\n\n  tachyon_halo2_bn254_prover_batch_start(prover_, polys.size());\n  for (size_t i = 0; i < polys.size(); ++i) {\n    tachyon_halo2_bn254_prover_batch_commit(prover_, c::base::c_cast(&polys[i]),\n                                            i);\n  }\n\n  std::vector<math::bn254::G1AffinePoint> points(polys.size());\n  tachyon_halo2_bn254_prover_batch_end(\n      prover_,\n      const_cast<tachyon_bn254_g1_affine*>(c::base::c_cast(points.data())),\n      points.size());\n\n  for (size_t i = 0; i < points.size(); ++i) {\n    const Poly& poly = polys[i];\n    math::bn254::G1AffinePoint expected;\n    switch (static_cast<Vendor>(prover_->vendor)) {\n      case Vendor::kPSE: {\n        switch (static_cast<PCSType>(prover_->pcs_type)) {\n          case PCSType::kGWC: {\n            expected = reinterpret_cast<ProverImpl<PSEGWC>*>(prover_->extra)\n                           ->Commit(poly);\n            break;\n          }\n          case PCSType::kSHPlonk: {\n            expected = reinterpret_cast<ProverImpl<PSESHPlonk>*>(prover_->extra)\n                           ->Commit(poly);\n            break;\n          }\n        }\n        break;\n      }\n      case Vendor::kScroll: {\n        switch (static_cast<PCSType>(prover_->pcs_type)) {\n          case PCSType::kGWC: {\n            expected = reinterpret_cast<ProverImpl<ScrollGWC>*>(prover_->extra)\n                           ->Commit(poly);\n            break;\n          }\n          case PCSType::kSHPlonk: {\n            expected =\n                reinterpret_cast<ProverImpl<ScrollSHPlonk>*>(prover_->extra)\n                    ->Commit(poly);\n            break;\n          }\n        }\n        break;\n      }\n    }\n    EXPECT_EQ(points[i], expected);\n  }\n}\n\nTEST_P(ProverTest, BatchCommitLagrange) {\n  Param param = GetParam();\n  if (param.transcript_type != TACHYON_HALO2_BLAKE2B_TRANSCRIPT) {\n    GTEST_SKIP()\n        << \"BatchCommitLagrange test is not related to transcript type\";\n  }\n\n  using Evals =\n      math::UnivariateEvaluations<math::bn254::Fr, c::math::kMaxDegree>;\n  std::vector<Evals> evals_vec =\n      base::CreateVector(4, []() { return Evals::Random(5); });\n\n  tachyon_halo2_bn254_prover_batch_start(prover_, evals_vec.size());\n  for (size_t i = 0; i < evals_vec.size(); ++i) {\n    tachyon_halo2_bn254_prover_batch_commit_lagrange(\n        prover_, c::base::c_cast(&evals_vec[i]), i);\n  }\n\n  std::vector<math::bn254::G1AffinePoint> points(evals_vec.size());\n  tachyon_halo2_bn254_prover_batch_end(\n      prover_,\n      const_cast<tachyon_bn254_g1_affine*>(c::base::c_cast(points.data())),\n      points.size());\n\n  for (size_t i = 0; i < points.size(); ++i) {\n    const Evals& evals = evals_vec[i];\n    math::bn254::G1AffinePoint expected;\n    switch (static_cast<Vendor>(prover_->vendor)) {\n      case Vendor::kPSE: {\n        switch (static_cast<PCSType>(prover_->pcs_type)) {\n          case PCSType::kGWC: {\n            expected = reinterpret_cast<ProverImpl<PSEGWC>*>(prover_->extra)\n                           ->Commit(evals);\n            break;\n          }\n          case PCSType::kSHPlonk: {\n            expected = reinterpret_cast<ProverImpl<PSESHPlonk>*>(prover_->extra)\n                           ->Commit(evals);\n            break;\n          }\n        }\n        break;\n      }\n      case Vendor::kScroll: {\n        switch (static_cast<PCSType>(prover_->pcs_type)) {\n          case PCSType::kGWC: {\n            expected = reinterpret_cast<ProverImpl<ScrollGWC>*>(prover_->extra)\n                           ->Commit(evals);\n            break;\n          }\n          case PCSType::kSHPlonk: {\n            expected =\n                reinterpret_cast<ProverImpl<ScrollSHPlonk>*>(prover_->extra)\n                    ->Commit(evals);\n            break;\n          }\n        }\n        break;\n      }\n    }\n    EXPECT_EQ(points[i], expected);\n  }\n}\n\ntemplate <typename RNG>\nvoid SetRngTestImpl(tachyon_halo2_bn254_prover* prover, uint8_t rng_type) {\n  std::vector<uint8_t> seed = base::CreateVector(\n      RNG::kSeedSize, []() { return base::Uniform(base::Range<uint8_t>()); });\n  tachyon_rng* rng =\n      tachyon_rng_create_from_seed(rng_type, seed.data(), seed.size());\n  uint8_t state[RNG::kStateSize];\n  size_t state_len;\n  tachyon_rng_get_state(rng, state, &state_len);\n  tachyon_halo2_bn254_prover_set_rng_state(prover, rng_type, state, state_len);\n\n  auto cpp_rng = std::make_unique<RNG>();\n  ASSERT_TRUE(cpp_rng->SetSeed(seed));\n  auto cpp_generator =\n      std::make_unique<RandomFieldGenerator<math::bn254::Fr>>(cpp_rng.get());\n\n  math::bn254::Fr expected;\n  switch (static_cast<PCSType>(prover->pcs_type)) {\n    case PCSType::kGWC: {\n      expected = reinterpret_cast<zk::ProverBase<GWCPCS>*>(prover->extra)\n                     ->blinder()\n                     .Generate();\n      break;\n    }\n    case PCSType::kSHPlonk: {\n      expected = reinterpret_cast<zk::ProverBase<SHPlonkPCS>*>(prover->extra)\n                     ->blinder()\n                     .Generate();\n      break;\n    }\n  }\n\n  EXPECT_EQ(cpp_generator->Generate(), expected);\n\n  tachyon_rng_destroy(rng);\n}\n\nTEST_P(ProverTest, SetRng) {\n  Param param = GetParam();\n  if (param.vendor != TACHYON_HALO2_PSE_VENDOR ||\n      param.transcript_type != TACHYON_HALO2_BLAKE2B_TRANSCRIPT) {\n    GTEST_SKIP() << \"SetRng test is not related to vendor and transcript_type\";\n  }\n\n  SetRngTestImpl<crypto::XORShiftRNG>(prover_, TACHYON_RNG_XOR_SHIFT);\n  SetRngTestImpl<crypto::ChaCha20RNG>(prover_, TACHYON_RNG_CHA_CHA20);\n}\n\nTEST_P(ProverTest, SetTranscript) {\n  Param param = GetParam();\n  if (param.vendor != TACHYON_HALO2_PSE_VENDOR) {\n    GTEST_SKIP() << \"SetRng test is not related to vendor\";\n  }\n\n  uint8_t transcript_type = param.transcript_type;\n\n  tachyon_halo2_bn254_transcript_writer* transcript =\n      tachyon_halo2_bn254_transcript_writer_create(transcript_type);\n\n  size_t digest_len = 0;\n  size_t state_len = 0;\n  switch (static_cast<TranscriptType>(transcript_type)) {\n    case TranscriptType::kBlake2b: {\n      Blake2bWriter<math::bn254::G1AffinePoint>* blake2b =\n          reinterpret_cast<Blake2bWriter<math::bn254::G1AffinePoint>*>(\n              transcript->extra);\n      digest_len = blake2b->GetDigestLen();\n      state_len = blake2b->GetStateLen();\n      break;\n    }\n    case TranscriptType::kPoseidon: {\n      PoseidonWriter<math::bn254::G1AffinePoint>* poseidon =\n          reinterpret_cast<PoseidonWriter<math::bn254::G1AffinePoint>*>(\n              transcript->extra);\n      digest_len = poseidon->GetDigestLen();\n      // NOTE(chokobole): In case of Poseidon transcript,\n      // |tachyon_halo2_bn254_transcript_writer_update()| touches an internal\n      // member |absorbing_|, so |state_len| has to be updated after this\n      break;\n    }\n    case TranscriptType::kSha256: {\n      Sha256Writer<math::bn254::G1AffinePoint>* sha256 =\n          reinterpret_cast<Sha256Writer<math::bn254::G1AffinePoint>*>(\n              transcript->extra);\n      digest_len = sha256->GetDigestLen();\n      state_len = sha256->GetStateLen();\n      break;\n    }\n    case TranscriptType::kSnarkVerifierPoseidon: {\n      SnarkVerifierPoseidonWriter<math::bn254::G1AffinePoint>* sv_poseidon =\n          reinterpret_cast<\n              SnarkVerifierPoseidonWriter<math::bn254::G1AffinePoint>*>(\n              transcript->extra);\n      digest_len = sv_poseidon->GetDigestLen();\n      // NOTE(chokobole): In case of SnarkVerifierPoseidon transcript,\n      // |tachyon_halo2_bn254_transcript_writer_update()| touches an internal\n      // member |buf_|, so |state_len| has to be updated after this\n      break;\n    }\n  }\n\n  std::vector<uint8_t> data = base::CreateVector(\n      digest_len, []() { return base::Uniform(base::Range<uint8_t>()); });\n  tachyon_halo2_bn254_transcript_writer_update(transcript, data.data(),\n                                               data.size());\n\n  switch (static_cast<TranscriptType>(transcript_type)) {\n    case TranscriptType::kPoseidon: {\n      PoseidonWriter<math::bn254::G1AffinePoint>* poseidon =\n          reinterpret_cast<PoseidonWriter<math::bn254::G1AffinePoint>*>(\n              transcript->extra);\n      state_len = poseidon->GetStateLen();\n      break;\n    }\n    case TranscriptType::kSnarkVerifierPoseidon: {\n      SnarkVerifierPoseidonWriter<math::bn254::G1AffinePoint>* sv_poseidon =\n          reinterpret_cast<\n              SnarkVerifierPoseidonWriter<math::bn254::G1AffinePoint>*>(\n              transcript->extra);\n      state_len = sv_poseidon->GetStateLen();\n      break;\n    }\n    case TranscriptType::kBlake2b:\n    case TranscriptType::kSha256:\n      break;\n  }\n\n  std::vector<uint8_t> state(state_len);\n  tachyon_halo2_bn254_transcript_writer_get_state(transcript, state.data(),\n                                                  &state_len);\n  tachyon_halo2_bn254_prover_set_transcript_state(prover_, state.data(),\n                                                  state_len);\n\n  math::bn254::Fr expected;\n  switch (static_cast<PCSType>(prover_->pcs_type)) {\n    case PCSType::kGWC: {\n      expected = reinterpret_cast<zk::Entity<GWCPCS>*>(prover_->extra)\n                     ->transcript()\n                     ->SqueezeChallenge();\n      break;\n    }\n    case PCSType::kSHPlonk: {\n      expected = reinterpret_cast<zk::Entity<SHPlonkPCS>*>(prover_->extra)\n                     ->transcript()\n                     ->SqueezeChallenge();\n      break;\n    }\n  }\n\n  EXPECT_EQ(\n      reinterpret_cast<crypto::TranscriptWriter<math::bn254::G1AffinePoint>*>(\n          transcript->extra)\n          ->SqueezeChallenge(),\n      expected);\n\n  tachyon_halo2_bn254_transcript_writer_destroy(transcript);\n}\n\n}  // namespace tachyon::zk::plonk::halo2::bn254\n"
  },
  {
    "path": "tachyon/c/zk/plonk/halo2/bn254_ps.h",
    "content": "#ifndef TACHYON_C_ZK_PLONK_HALO2_BN254_PS_H_\n#define TACHYON_C_ZK_PLONK_HALO2_BN254_PS_H_\n\n#include \"tachyon/c/math/polynomials/constants.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/bn254.h\"\n#include \"tachyon/zk/base/commitments/gwc_extension.h\"\n#include \"tachyon/zk/base/commitments/shplonk_extension.h\"\n#include \"tachyon/zk/plonk/halo2/proving_scheme.h\"\n\nnamespace tachyon::c::zk::plonk::halo2::bn254 {\n\nusing GWCPCS = tachyon::zk::GWCExtension<tachyon::math::bn254::BN254Curve,\n                                         math::kMaxDegree, math::kMaxDegree,\n                                         tachyon::math::bn254::G1AffinePoint>;\n\nusing SHPlonkPCS =\n    tachyon::zk::SHPlonkExtension<tachyon::math::bn254::BN254Curve,\n                                  math::kMaxDegree, math::kMaxDegree,\n                                  tachyon::math::bn254::G1AffinePoint>;\n\nusing PSEGWC = tachyon::zk::plonk::halo2::ProvingScheme<\n    tachyon::zk::plonk::halo2::Vendor::kPSE, tachyon::zk::lookup::Type::kHalo2,\n    GWCPCS>;\n\nusing PSESHPlonk = tachyon::zk::plonk::halo2::ProvingScheme<\n    tachyon::zk::plonk::halo2::Vendor::kPSE, tachyon::zk::lookup::Type::kHalo2,\n    SHPlonkPCS>;\n\nusing ScrollGWC = tachyon::zk::plonk::halo2::ProvingScheme<\n    tachyon::zk::plonk::halo2::Vendor::kScroll,\n    tachyon::zk::lookup::Type::kLogDerivativeHalo2, GWCPCS>;\n\nusing ScrollSHPlonk = tachyon::zk::plonk::halo2::ProvingScheme<\n    tachyon::zk::plonk::halo2::Vendor::kScroll,\n    tachyon::zk::lookup::Type::kLogDerivativeHalo2, SHPlonkPCS>;\n\n}  // namespace tachyon::c::zk::plonk::halo2::bn254\n\n#endif  // TACHYON_C_ZK_PLONK_HALO2_BN254_PS_H_\n"
  },
  {
    "path": "tachyon/c/zk/plonk/halo2/bn254_transcript.cc",
    "content": "#include \"tachyon/c/zk/plonk/halo2/bn254_transcript.h\"\n\n#include <string.h>\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/g1.h\"\n#include \"tachyon/zk/plonk/halo2/blake2b_transcript.h\"\n#include \"tachyon/zk/plonk/halo2/poseidon_transcript.h\"\n#include \"tachyon/zk/plonk/halo2/sha256_transcript.h\"\n#include \"tachyon/zk/plonk/halo2/snark_verifier_poseidon_transcript.h\"\n#include \"tachyon/zk/plonk/halo2/transcript_type.h\"\n\nusing namespace tachyon;\n\nusing Blake2bWriter =\n    zk::plonk::halo2::Blake2bWriter<math::bn254::G1AffinePoint>;\nusing PoseidonWriter =\n    zk::plonk::halo2::PoseidonWriter<math::bn254::G1AffinePoint>;\nusing Sha256Writer = zk::plonk::halo2::Sha256Writer<math::bn254::G1AffinePoint>;\nusing SnarkVerifierPoseidonWriter =\n    zk::plonk::halo2::SnarkVerifierPoseidonWriter<math::bn254::G1AffinePoint>;\n\ntachyon_halo2_bn254_transcript_writer*\ntachyon_halo2_bn254_transcript_writer_create(uint8_t type) {\n  tachyon_halo2_bn254_transcript_writer* writer =\n      new tachyon_halo2_bn254_transcript_writer;\n  writer->type = type;\n  switch (static_cast<zk::plonk::halo2::TranscriptType>(type)) {\n    case zk::plonk::halo2::TranscriptType::kBlake2b: {\n      writer->extra = new Blake2bWriter(base::Uint8VectorBuffer());\n      return writer;\n    }\n    case zk::plonk::halo2::TranscriptType::kPoseidon: {\n      writer->extra = new PoseidonWriter(base::Uint8VectorBuffer());\n      return writer;\n    }\n    case zk::plonk::halo2::TranscriptType::kSha256: {\n      writer->extra = new Sha256Writer(base::Uint8VectorBuffer());\n      return writer;\n    }\n    case zk::plonk::halo2::TranscriptType::kSnarkVerifierPoseidon: {\n      writer->extra =\n          new SnarkVerifierPoseidonWriter(base::Uint8VectorBuffer());\n      return writer;\n    }\n  }\n  NOTREACHED();\n  return nullptr;\n}\n\ntachyon_halo2_bn254_transcript_writer*\ntachyon_halo2_bn254_transcript_writer_create_from_state(uint8_t type,\n                                                        const uint8_t* state,\n                                                        size_t state_len) {\n  tachyon_halo2_bn254_transcript_writer* writer =\n      new tachyon_halo2_bn254_transcript_writer;\n  writer->type = type;\n  switch (static_cast<zk::plonk::halo2::TranscriptType>(type)) {\n    case zk::plonk::halo2::TranscriptType::kBlake2b: {\n      Blake2bWriter* blake2b = new Blake2bWriter(base::Uint8VectorBuffer());\n      blake2b->SetState(absl::Span<const uint8_t>(state, state_len));\n      writer->extra = blake2b;\n      return writer;\n    }\n    case zk::plonk::halo2::TranscriptType::kPoseidon: {\n      PoseidonWriter* poseidon = new PoseidonWriter(base::Uint8VectorBuffer());\n      poseidon->SetState(absl::Span<const uint8_t>(state, state_len));\n      writer->extra = poseidon;\n      return writer;\n    }\n    case zk::plonk::halo2::TranscriptType::kSha256: {\n      Sha256Writer* sha256 = new Sha256Writer(base::Uint8VectorBuffer());\n      sha256->SetState(absl::Span<const uint8_t>(state, state_len));\n      writer->extra = sha256;\n      return writer;\n    }\n    case zk::plonk::halo2::TranscriptType::kSnarkVerifierPoseidon: {\n      SnarkVerifierPoseidonWriter* sv_poseidon =\n          new SnarkVerifierPoseidonWriter(base::Uint8VectorBuffer());\n      sv_poseidon->SetState(absl::Span<const uint8_t>(state, state_len));\n      writer->extra = sv_poseidon;\n      return writer;\n    }\n  }\n  NOTREACHED();\n  return nullptr;\n}\n\nvoid tachyon_halo2_bn254_transcript_writer_destroy(\n    tachyon_halo2_bn254_transcript_writer* writer) {\n  switch (static_cast<zk::plonk::halo2::TranscriptType>(writer->type)) {\n    case zk::plonk::halo2::TranscriptType::kBlake2b: {\n      delete reinterpret_cast<Blake2bWriter*>(writer->extra);\n      delete writer;\n      return;\n    }\n    case zk::plonk::halo2::TranscriptType::kPoseidon: {\n      delete reinterpret_cast<PoseidonWriter*>(writer->extra);\n      delete writer;\n      return;\n    }\n    case zk::plonk::halo2::TranscriptType::kSha256: {\n      delete reinterpret_cast<Sha256Writer*>(writer->extra);\n      delete writer;\n      return;\n    }\n    case zk::plonk::halo2::TranscriptType::kSnarkVerifierPoseidon: {\n      delete reinterpret_cast<SnarkVerifierPoseidonWriter*>(writer->extra);\n      delete writer;\n      return;\n    }\n  }\n  NOTREACHED();\n}\n\nvoid tachyon_halo2_bn254_transcript_writer_update(\n    tachyon_halo2_bn254_transcript_writer* writer, const uint8_t* data,\n    size_t data_len) {\n  switch (static_cast<zk::plonk::halo2::TranscriptType>(writer->type)) {\n    case zk::plonk::halo2::TranscriptType::kBlake2b: {\n      reinterpret_cast<Blake2bWriter*>(writer->extra)->Update(data, data_len);\n      return;\n    }\n    case zk::plonk::halo2::TranscriptType::kPoseidon: {\n      reinterpret_cast<PoseidonWriter*>(writer->extra)\n          ->Update(reinterpret_cast<const PoseidonWriter::ScalarField*>(data),\n                   data_len / PoseidonWriter::ScalarBigInt::kByteNums);\n      return;\n    }\n    case zk::plonk::halo2::TranscriptType::kSha256: {\n      reinterpret_cast<Sha256Writer*>(writer->extra)->Update(data, data_len);\n      return;\n    }\n    case zk::plonk::halo2::TranscriptType::kSnarkVerifierPoseidon: {\n      reinterpret_cast<SnarkVerifierPoseidonWriter*>(writer->extra)\n          ->Update(\n              reinterpret_cast<const SnarkVerifierPoseidonWriter::ScalarField*>(\n                  data),\n              data_len / SnarkVerifierPoseidonWriter::ScalarBigInt::kByteNums);\n      return;\n    }\n  }\n  NOTREACHED();\n}\n\nvoid tachyon_halo2_bn254_transcript_writer_finalize(\n    tachyon_halo2_bn254_transcript_writer* writer, uint8_t* data,\n    size_t* data_len) {\n  switch (static_cast<zk::plonk::halo2::TranscriptType>(writer->type)) {\n    case zk::plonk::halo2::TranscriptType::kBlake2b: {\n      *data_len = BLAKE2B512_DIGEST_LENGTH;\n      if (data == nullptr) return;\n      uint8_t data_tmp[BLAKE2B512_DIGEST_LENGTH];\n      reinterpret_cast<Blake2bWriter*>(writer->extra)->Finalize(data_tmp);\n      memcpy(data, data_tmp, BLAKE2B512_DIGEST_LENGTH);\n      return;\n    }\n    case zk::plonk::halo2::TranscriptType::kPoseidon:\n    case zk::plonk::halo2::TranscriptType::kSnarkVerifierPoseidon:\n      break;\n    case zk::plonk::halo2::TranscriptType::kSha256: {\n      *data_len = SHA256_DIGEST_LENGTH;\n      if (data == nullptr) return;\n      uint8_t data_tmp[SHA256_DIGEST_LENGTH];\n      reinterpret_cast<Sha256Writer*>(writer->extra)->Finalize(data_tmp);\n      memcpy(data, data_tmp, SHA256_DIGEST_LENGTH);\n      return;\n    }\n  }\n  NOTREACHED();\n}\n\ntachyon_bn254_fr tachyon_halo2_bn254_transcript_writer_squeeze(\n    tachyon_halo2_bn254_transcript_writer* writer) {\n  tachyon_bn254_fr ret;\n  math::bn254::Fr challenge;\n  switch (static_cast<zk::plonk::halo2::TranscriptType>(writer->type)) {\n    case zk::plonk::halo2::TranscriptType::kPoseidon: {\n      challenge = reinterpret_cast<PoseidonWriter*>(writer->extra)->Squeeze();\n      memcpy(ret.limbs, challenge.value().limbs,\n             math::bn254::Fr::BigIntTy::kByteNums);\n      return ret;\n    }\n    case zk::plonk::halo2::TranscriptType::kSnarkVerifierPoseidon: {\n      challenge = reinterpret_cast<SnarkVerifierPoseidonWriter*>(writer->extra)\n                      ->Squeeze();\n      memcpy(ret.limbs, challenge.value().limbs,\n             math::bn254::Fr::BigIntTy::kByteNums);\n      return ret;\n    }\n    case zk::plonk::halo2::TranscriptType::kBlake2b:\n    case zk::plonk::halo2::TranscriptType::kSha256:\n      break;\n  }\n  NOTREACHED();\n  return ret;\n}\n\nvoid tachyon_halo2_bn254_transcript_writer_get_state(\n    const tachyon_halo2_bn254_transcript_writer* writer, uint8_t* state,\n    size_t* state_len) {\n  switch (static_cast<zk::plonk::halo2::TranscriptType>(writer->type)) {\n    case zk::plonk::halo2::TranscriptType::kBlake2b: {\n      Blake2bWriter* blake2b = reinterpret_cast<Blake2bWriter*>(writer->extra);\n      *state_len = blake2b->GetStateLen();\n      if (state == nullptr) return;\n      std::vector<uint8_t> state_tmp = blake2b->GetState();\n      memcpy(state, state_tmp.data(), *state_len);\n      return;\n    }\n    case zk::plonk::halo2::TranscriptType::kPoseidon: {\n      PoseidonWriter* poseidon =\n          reinterpret_cast<PoseidonWriter*>(writer->extra);\n      *state_len = poseidon->GetStateLen();\n      if (state == nullptr) return;\n      std::vector<uint8_t> state_tmp = poseidon->GetState();\n      memcpy(state, state_tmp.data(), *state_len);\n      return;\n    }\n    case zk::plonk::halo2::TranscriptType::kSha256: {\n      Sha256Writer* sha256 = reinterpret_cast<Sha256Writer*>(writer->extra);\n      *state_len = sha256->GetStateLen();\n      if (state == nullptr) return;\n      std::vector<uint8_t> state_tmp = sha256->GetState();\n      memcpy(state, state_tmp.data(), *state_len);\n      return;\n    }\n    case zk::plonk::halo2::TranscriptType::kSnarkVerifierPoseidon: {\n      SnarkVerifierPoseidonWriter* sv_poseidon =\n          reinterpret_cast<SnarkVerifierPoseidonWriter*>(writer->extra);\n      *state_len = sv_poseidon->GetStateLen();\n      if (state == nullptr) return;\n      std::vector<uint8_t> state_tmp = sv_poseidon->GetState();\n      memcpy(state, state_tmp.data(), *state_len);\n      return;\n    }\n  }\n  NOTREACHED();\n}\n"
  },
  {
    "path": "tachyon/c/zk/plonk/halo2/bn254_transcript.h",
    "content": "/**\n * @file bn254_transcript.h\n * @brief Defines the interface for the transcript writer used within the Halo2\n * bn254 proof system.\n */\n\n#ifndef TACHYON_C_ZK_PLONK_HALO2_BN254_TRANSCRIPT_H_\n#define TACHYON_C_ZK_PLONK_HALO2_BN254_TRANSCRIPT_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include \"tachyon/c/export.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/fr.h\"\n#include \"tachyon/c/zk/plonk/halo2/constants.h\"\n\nstruct tachyon_halo2_bn254_transcript_writer {\n  uint8_t type;\n  void* extra;\n};\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Creates a new transcript writer of the specified type.\n *\n * @param type The type of transcript writer to create.\n * @return A pointer to the newly created transcript writer.\n */\nTACHYON_C_EXPORT tachyon_halo2_bn254_transcript_writer*\ntachyon_halo2_bn254_transcript_writer_create(uint8_t type);\n\n/**\n * @brief Creates a new transcript writer of the specified type, initializing\n * its state from the given buffer.\n *\n * @param type The type of transcript writer to create.\n * @param state A buffer containing the initial state.\n * @param state_len The length of the state buffer.\n * @return A pointer to the newly created transcript writer with its state\n * initialized.\n */\nTACHYON_C_EXPORT tachyon_halo2_bn254_transcript_writer*\ntachyon_halo2_bn254_transcript_writer_create_from_state(uint8_t type,\n                                                        const uint8_t* state,\n                                                        size_t state_len);\n\n/**\n * @brief Destroys a transcript writer, freeing its resources.\n *\n * @param writer The transcript writer to destroy.\n */\nTACHYON_C_EXPORT void tachyon_halo2_bn254_transcript_writer_destroy(\n    tachyon_halo2_bn254_transcript_writer* writer);\n\n/**\n * @brief Updates the transcript with new data.\n *\n * @param writer The transcript writer to update.\n * @param data The data to add to the transcript.\n * @param data_len The length of the data.\n */\nTACHYON_C_EXPORT void tachyon_halo2_bn254_transcript_writer_update(\n    tachyon_halo2_bn254_transcript_writer* writer, const uint8_t* data,\n    size_t data_len);\n\n/**\n * @brief Finalizes the transcript, producing a hash.\n * For Poseidon transcript types, use\n * tachyon_halo2_bn254_transcript_writer_squeeze instead.\n *\n * If |data| is NULL, then it populates |data_len| with length to be used.\n * If |data| is not NULL, then it populates |data| with the hash.\n * |tachyon_halo2_bn254_transcript_writer_squeeze| instead. Otherwise, it\n * terminates the program.\n *\n * @param writer The transcript writer to finalize.\n * @param data Buffer to store the resulting hash.\n * @param data_len A pointer to store the length of the hash or the required\n * buffer size.\n */\nTACHYON_C_EXPORT void tachyon_halo2_bn254_transcript_writer_finalize(\n    tachyon_halo2_bn254_transcript_writer* writer, uint8_t* data,\n    size_t* data_len);\n\n/**\n * @brief Squeezes a field element from the transcript. Only valid for Poseidon\n * transcripts.\n *\n * If the type of the transcript is not poseidon, it terminates the program.\n *\n * @param writer The transcript writer.\n * @return A field element squeezed from the transcript.\n */\n\nTACHYON_C_EXPORT tachyon_bn254_fr tachyon_halo2_bn254_transcript_writer_squeeze(\n    tachyon_halo2_bn254_transcript_writer* writer);\n\n/**\n * @brief Retrieves the internal state of the transcript writer.\n *\n * If |state| is NULL, then it populates |state_len| with length to be used.\n * If |state| is not NULL, then it populates |state| with its internal state.\n *\n * @param writer The transcript writer.\n * @param state Buffer to store the internal state.\n * @param state_len A pointer to store the length of the internal state or the\n * required buffer size.\n */\nTACHYON_C_EXPORT void tachyon_halo2_bn254_transcript_writer_get_state(\n    const tachyon_halo2_bn254_transcript_writer* writer, uint8_t* state,\n    size_t* state_len);\n\n#ifdef __cplusplus\n}  // extern \"C\"\n#endif\n\n#endif  // TACHYON_C_ZK_PLONK_HALO2_BN254_TRANSCRIPT_H_\n"
  },
  {
    "path": "tachyon/c/zk/plonk/halo2/bn254_transcript_unittest.cc",
    "content": "#include \"tachyon/c/zk/plonk/halo2/bn254_transcript.h\"\n\n#include <utility>\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/elliptic_curves/bn/bn254/g1.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/zk/plonk/halo2/blake2b_transcript.h\"\n#include \"tachyon/zk/plonk/halo2/poseidon_transcript.h\"\n#include \"tachyon/zk/plonk/halo2/sha256_transcript.h\"\n#include \"tachyon/zk/plonk/halo2/snark_verifier_poseidon_transcript.h\"\n\nnamespace tachyon::zk::plonk::halo2 {\n\ntemplate <typename Transcript>\nclass TranscriptWriterTest : public math::FiniteFieldTest<math::bn254::Fr> {\n public:\n  void TearDown() override {\n    tachyon_halo2_bn254_transcript_writer_destroy(writer_);\n    tachyon_halo2_bn254_transcript_writer_destroy(writer_clone_);\n  }\n\n protected:\n  tachyon_halo2_bn254_transcript_writer* writer_ = nullptr;\n  tachyon_halo2_bn254_transcript_writer* writer_clone_ = nullptr;\n};\n\nusing TranscriptTypes =\n    testing::Types<Blake2bWriter<math::bn254::G1AffinePoint>,\n                   PoseidonWriter<math::bn254::G1AffinePoint>,\n                   Sha256Writer<math::bn254::G1AffinePoint>,\n                   SnarkVerifierPoseidonWriter<math::bn254::G1AffinePoint>>;\nTYPED_TEST_SUITE(TranscriptWriterTest, TranscriptTypes);\n\nTYPED_TEST(TranscriptWriterTest, APIs) {\n  using TranscriptWriter = TypeParam;\n\n  base::Uint8VectorBuffer buffer;\n  TranscriptWriter cpp_writer(std::move(buffer));\n\n  uint8_t type;\n  if constexpr (std::is_same_v<TranscriptWriter,\n                               Blake2bWriter<math::bn254::G1AffinePoint>>) {\n    type = TACHYON_HALO2_BLAKE2B_TRANSCRIPT;\n    // NOLINTNEXTLINE(readability/braces)\n  } else if constexpr (std::is_same_v<\n                           TranscriptWriter,\n                           PoseidonWriter<math::bn254::G1AffinePoint>>) {\n    type = TACHYON_HALO2_POSEIDON_TRANSCRIPT;\n    // NOLINTNEXTLINE(readability/braces)\n  } else if constexpr (std::is_same_v<\n                           TranscriptWriter,\n                           Sha256Writer<math::bn254::G1AffinePoint>>) {\n    type = TACHYON_HALO2_SHA256_TRANSCRIPT;\n    // NOLINTNEXTLINE(readability/braces)\n  } else if constexpr (std::is_same_v<TranscriptWriter,\n                                      SnarkVerifierPoseidonWriter<\n                                          math::bn254::G1AffinePoint>>) {\n    type = TACHYON_HALO2_SNARK_VERIFIER_POSEIDON_TRANSCRIPT;\n  }\n\n  this->writer_ = tachyon_halo2_bn254_transcript_writer_create(type);\n\n  EXPECT_EQ(cpp_writer.SqueezeChallenge(),\n            reinterpret_cast<TranscriptWriter*>(this->writer_->extra)\n                ->SqueezeChallenge());\n\n  size_t expected_state_len =\n      reinterpret_cast<TranscriptWriter*>(this->writer_->extra)->GetStateLen();\n\n  size_t state_len;\n  tachyon_halo2_bn254_transcript_writer_get_state(this->writer_, nullptr,\n                                                  &state_len);\n  ASSERT_EQ(state_len, expected_state_len);\n\n  std::vector<uint8_t> state(state_len);\n  tachyon_halo2_bn254_transcript_writer_get_state(this->writer_, state.data(),\n                                                  &state_len);\n  ASSERT_EQ(state_len, expected_state_len);\n\n  this->writer_clone_ = tachyon_halo2_bn254_transcript_writer_create_from_state(\n      type, state.data(), state_len);\n\n  EXPECT_EQ(reinterpret_cast<TranscriptWriter*>(this->writer_->extra)\n                ->SqueezeChallenge(),\n            reinterpret_cast<TranscriptWriter*>(this->writer_clone_->extra)\n                ->SqueezeChallenge());\n}\n\n}  // namespace tachyon::zk::plonk::halo2\n"
  },
  {
    "path": "tachyon/c/zk/plonk/halo2/bn254_verifier.cc",
    "content": "#include \"tachyon/c/zk/plonk/halo2/bn254_verifier.h\"\n\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/c/zk/plonk/halo2/bn254_instance_columns_vec_type_traits.h\"\n#include \"tachyon/c/zk/plonk/halo2/bn254_ps.h\"\n#include \"tachyon/c/zk/plonk/halo2/bn254_transcript.h\"\n#include \"tachyon/c/zk/plonk/halo2/verifier_impl.h\"\n#include \"tachyon/c/zk/plonk/keys/bn254_plonk_verifying_key_type_traits.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/halo2/bn254.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluation_domain_factory.h\"\n#include \"tachyon/zk/plonk/halo2/blake2b_transcript.h\"\n#include \"tachyon/zk/plonk/halo2/pcs_type.h\"\n#include \"tachyon/zk/plonk/halo2/poseidon_transcript.h\"\n#include \"tachyon/zk/plonk/halo2/sha256_transcript.h\"\n#include \"tachyon/zk/plonk/halo2/snark_verifier_poseidon_transcript.h\"\n#include \"tachyon/zk/plonk/halo2/transcript_type.h\"\n#include \"tachyon/zk/plonk/halo2/vendor.h\"\n\nusing namespace tachyon;\n\nusing PSEGWC = c::zk::plonk::halo2::bn254::PSEGWC;\nusing PSESHPlonk = c::zk::plonk::halo2::bn254::PSESHPlonk;\nusing ScrollGWC = c::zk::plonk::halo2::bn254::ScrollGWC;\nusing ScrollSHPlonk = c::zk::plonk::halo2::bn254::ScrollSHPlonk;\n\ntemplate <typename PS>\nusing Verifier = c::zk::plonk::halo2::VerifierImpl<PS>;\n\nnamespace {\n\ntemplate <typename PS>\nVerifier<PS>* CreateVerifierFromParams(uint8_t transcript_type, uint32_t k,\n                                       const uint8_t* params, size_t params_len,\n                                       const uint8_t* proof, size_t proof_len) {\n  return new Verifier<PS>(\n      [transcript_type, k, params, params_len, proof, proof_len]() {\n        using PCS = typename PS::PCS;\n\n        PCS pcs;\n        base::ReadOnlyBuffer read_buf(params, params_len);\n        CHECK(read_buf.Read(&pcs));\n\n        read_buf = base::ReadOnlyBuffer(proof, proof_len);\n        std::unique_ptr<crypto::TranscriptReader<math::bn254::G1AffinePoint>>\n            reader;\n        switch (\n            static_cast<zk::plonk::halo2::TranscriptType>(transcript_type)) {\n          case zk::plonk::halo2::TranscriptType::kBlake2b: {\n            reader = std::make_unique<\n                zk::plonk::halo2::Blake2bReader<math::bn254::G1AffinePoint>>(\n                std::move(read_buf));\n            break;\n          }\n          case zk::plonk::halo2::TranscriptType::kPoseidon: {\n            reader = std::make_unique<\n                zk::plonk::halo2::PoseidonReader<math::bn254::G1AffinePoint>>(\n                std::move(read_buf));\n            break;\n          }\n          case zk::plonk::halo2::TranscriptType::kSha256: {\n            reader = std::make_unique<\n                zk::plonk::halo2::Sha256Reader<math::bn254::G1AffinePoint>>(\n                std::move(read_buf));\n            break;\n          }\n          case zk::plonk::halo2::TranscriptType::kSnarkVerifierPoseidon: {\n            reader =\n                std::make_unique<zk::plonk::halo2::SnarkVerifierPoseidonReader<\n                    math::bn254::G1AffinePoint>>(std::move(read_buf));\n            break;\n          }\n        }\n        CHECK(reader);\n        zk::plonk::halo2::Verifier<PS> verifier(std::move(pcs),\n                                                std::move(reader));\n        verifier.set_domain(PCS::Domain::Create(size_t{1} << k));\n        return verifier;\n      },\n      transcript_type);\n}\n\ntemplate <typename NativeVerifier>\nvoid Destroy(NativeVerifier* verifier) {\n  delete verifier;\n}\n\ntemplate <typename NativeVerifier>\nbool VerifyProof(\n    NativeVerifier* verifier, const tachyon_bn254_plonk_verifying_key* vkey,\n    tachyon_halo2_bn254_instance_columns_vec* instance_columns_vec) {\n  bool ret = verifier->VerifyProof(c::base::native_cast(*vkey),\n                                   c::base::native_cast(*instance_columns_vec));\n  tachyon_halo2_bn254_instance_columns_vec_destroy(instance_columns_vec);\n  return ret;\n}\n\n}  // namespace\n\n#define INVOKE_VERIFIER(Method, ...)                                          \\\n  switch (static_cast<zk::plonk::halo2::Vendor>(verifier->vendor)) {          \\\n    case zk::plonk::halo2::Vendor::kPSE: {                                    \\\n      switch (static_cast<zk::plonk::halo2::PCSType>(verifier->pcs_type)) {   \\\n        case zk::plonk::halo2::PCSType::kGWC: {                               \\\n          return Method(reinterpret_cast<Verifier<PSEGWC>*>(verifier->extra), \\\n                        ##__VA_ARGS__);                                       \\\n        }                                                                     \\\n        case zk::plonk::halo2::PCSType::kSHPlonk: {                           \\\n          return Method(                                                      \\\n              reinterpret_cast<Verifier<PSESHPlonk>*>(verifier->extra),       \\\n              ##__VA_ARGS__);                                                 \\\n        }                                                                     \\\n      }                                                                       \\\n      break;                                                                  \\\n    }                                                                         \\\n    case zk::plonk::halo2::Vendor::kScroll: {                                 \\\n      switch (static_cast<zk::plonk::halo2::PCSType>(verifier->pcs_type)) {   \\\n        case zk::plonk::halo2::PCSType::kGWC: {                               \\\n          return Method(                                                      \\\n              reinterpret_cast<Verifier<ScrollGWC>*>(verifier->extra),        \\\n              ##__VA_ARGS__);                                                 \\\n        }                                                                     \\\n        case zk::plonk::halo2::PCSType::kSHPlonk: {                           \\\n          return Method(                                                      \\\n              reinterpret_cast<Verifier<ScrollSHPlonk>*>(verifier->extra),    \\\n              ##__VA_ARGS__);                                                 \\\n        }                                                                     \\\n      }                                                                       \\\n      break;                                                                  \\\n    }                                                                         \\\n  }                                                                           \\\n  NOTREACHED()\n\ntachyon_halo2_bn254_verifier* tachyon_halo2_bn254_verifier_create_from_params(\n    uint8_t vendor, uint8_t pcs_type, uint8_t transcript_type, uint32_t k,\n    const uint8_t* params, size_t params_len, const uint8_t* proof,\n    size_t proof_len) {\n  tachyon_halo2_bn254_verifier* verifier = new tachyon_halo2_bn254_verifier;\n  verifier->vendor = vendor;\n  verifier->pcs_type = pcs_type;\n  math::bn254::BN254Curve::Init();\n  math::halo2::OverrideSubgroupGenerator();\n\n  switch (static_cast<zk::plonk::halo2::Vendor>(vendor)) {\n    case zk::plonk::halo2::Vendor::kPSE: {\n      switch (static_cast<zk::plonk::halo2::PCSType>(pcs_type)) {\n        case zk::plonk::halo2::PCSType::kGWC: {\n          verifier->extra = CreateVerifierFromParams<PSEGWC>(\n              transcript_type, k, params, params_len, proof, proof_len);\n          return verifier;\n        }\n        case zk::plonk::halo2::PCSType::kSHPlonk: {\n          verifier->extra = CreateVerifierFromParams<PSESHPlonk>(\n              transcript_type, k, params, params_len, proof, proof_len);\n          return verifier;\n        }\n      }\n      break;\n    }\n    case zk::plonk::halo2::Vendor::kScroll: {\n      switch (static_cast<zk::plonk::halo2::PCSType>(pcs_type)) {\n        case zk::plonk::halo2::PCSType::kGWC: {\n          verifier->extra = CreateVerifierFromParams<ScrollGWC>(\n              transcript_type, k, params, params_len, proof, proof_len);\n          return verifier;\n        }\n        case zk::plonk::halo2::PCSType::kSHPlonk: {\n          verifier->extra = CreateVerifierFromParams<ScrollSHPlonk>(\n              transcript_type, k, params, params_len, proof, proof_len);\n          return verifier;\n        }\n      }\n      break;\n    }\n  }\n  NOTREACHED();\n  return nullptr;\n}\n\nvoid tachyon_halo2_bn254_verifier_destroy(\n    tachyon_halo2_bn254_verifier* verifier) {\n  INVOKE_VERIFIER(Destroy);\n}\n\nbool tachyon_halo2_bn254_verifier_verify_proof(\n    tachyon_halo2_bn254_verifier* verifier,\n    const tachyon_bn254_plonk_verifying_key* vkey,\n    tachyon_halo2_bn254_instance_columns_vec* instance_columns_vec) {\n  INVOKE_VERIFIER(VerifyProof, vkey, instance_columns_vec);\n  return false;\n}\n"
  },
  {
    "path": "tachyon/c/zk/plonk/halo2/bn254_verifier.h",
    "content": "/**\n * @file bn254_verifier.h\n * @brief Interface for the Halo2 BN254 Verifier.\n *\n * This header file defines the structure and API for the verifier specific\n * to the Halo2 proof system on the BN254 curve. It includes functionality for\n * creating verifiers from parameters, destroying verifiers, and verifying\n * proofs.\n */\n#ifndef TACHYON_C_ZK_PLONK_HALO2_BN254_VERIFIER_H_\n#define TACHYON_C_ZK_PLONK_HALO2_BN254_VERIFIER_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include \"tachyon/c/export.h\"\n#include \"tachyon/c/zk/plonk/halo2/bn254_instance_columns_vec.h\"\n#include \"tachyon/c/zk/plonk/keys/bn254_plonk_verifying_key.h\"\n\n/**\n * @struct tachyon_halo2_bn254_verifier\n * @brief Structure representing a verifier for the Halo2 protocol on the BN254\n * curve.\n *\n * Encapsulates the functionality required for verifying Halo2 proofs. It is\n * responsible for checking the validity of proofs against a given verifying key\n * and instance data.\n */\nstruct tachyon_halo2_bn254_verifier {\n  uint8_t vendor;\n  uint8_t pcs_type;\n  void* extra;\n};\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Creates a verifier from provided parameters and a proof.\n *\n * Initializes a verifier instance for the Halo2 protocol using the specified\n * parameters and proof, facilitating the proof verification process.\n *\n * @param vendor The type of vendor used in the proof.\n * @param pcs_type The type of pcs used in the proof.\n * @param transcript_type The type of transcript used in the proof.\n * @param k The circuit size parameter.\n * @param params A const pointer to the verifier parameters.\n * @param params_len The length of the parameters array.\n * @param proof A const pointer to the proof to be verified.\n * @param proof_len The length of the proof.\n * @return A pointer to the newly created verifier instance.\n */\nTACHYON_C_EXPORT tachyon_halo2_bn254_verifier*\ntachyon_halo2_bn254_verifier_create_from_params(\n    uint8_t vendor, uint8_t pcs_type, uint8_t transcript_type, uint32_t k,\n    const uint8_t* params, size_t params_len, const uint8_t* proof,\n    size_t proof_len);\n\n/**\n * @brief Destroys a verifier instance, freeing its resources.\n *\n * @param verifier A pointer to the verifier to destroy.\n */\nTACHYON_C_EXPORT void tachyon_halo2_bn254_verifier_destroy(\n    tachyon_halo2_bn254_verifier* verifier);\n\n/**\n * @brief Verifies a proof against a given verifying key and instance columns.\n *\n * This function checks the validity of a proof with respect to the provided\n * verifying key and instance columns vector. Note that the instance columns\n * vector is destroyed after the call.\n *\n * @param verifier A pointer to the verifier.\n * @param vkey A const pointer to the verifying key against which the proof will\n * be checked.\n * @param instance_columns_vec A pointer to the vector of instance columns\n * related to the proof.\n * @return True if the proof is valid, false otherwise.\n */\nTACHYON_C_EXPORT bool tachyon_halo2_bn254_verifier_verify_proof(\n    tachyon_halo2_bn254_verifier* verifier,\n    const tachyon_bn254_plonk_verifying_key* vkey,\n    tachyon_halo2_bn254_instance_columns_vec* instance_columns_vec);\n\n#ifdef __cplusplus\n}  // extern \"C\"\n#endif\n\n#endif  // TACHYON_C_ZK_PLONK_HALO2_BN254_VERIFIER_H_\n"
  },
  {
    "path": "tachyon/c/zk/plonk/halo2/buffer_reader.h",
    "content": "#ifndef TACHYON_C_ZK_PLONK_HALO2_BUFFER_READER_H_\n#define TACHYON_C_ZK_PLONK_HALO2_BUFFER_READER_H_\n\n#include <memory>\n#include <string>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"absl/container/btree_map.h\"\n\n#include \"tachyon/base/buffer/endian_auto_reset.h\"\n#include \"tachyon/base/buffer/read_only_buffer.h\"\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/c/zk/plonk/halo2/bn254_ps.h\"\n#include \"tachyon/math/finite_fields/cubic_extension_field.h\"\n#include \"tachyon/math/finite_fields/prime_field_base.h\"\n#include \"tachyon/math/finite_fields/quadratic_extension_field.h\"\n#include \"tachyon/math/geometry/affine_point.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluations.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_polynomial.h\"\n#include \"tachyon/zk/expressions/expression_factory.h\"\n#include \"tachyon/zk/lookup/argument.h\"\n#include \"tachyon/zk/plonk/base/column_key.h\"\n#include \"tachyon/zk/plonk/base/phase.h\"\n#include \"tachyon/zk/plonk/constraint_system/challenge.h\"\n#include \"tachyon/zk/plonk/constraint_system/gate.h\"\n#include \"tachyon/zk/plonk/constraint_system/lookup_tracker.h\"\n#include \"tachyon/zk/plonk/constraint_system/query.h\"\n#include \"tachyon/zk/plonk/expressions/expression_factory.h\"\n#include \"tachyon/zk/plonk/permutation/permutation_argument.h\"\n#include \"tachyon/zk/shuffle/argument.h\"\n\nnamespace tachyon::c::zk::plonk {\n\ntemplate <typename T, typename SFINAE = void>\nclass BufferReader;\n\ntemplate <typename T>\nvoid ReadBuffer(const tachyon::base::ReadOnlyBuffer& buffer, T& value) {\n  value = BufferReader<T>::Read(buffer);\n}\n\ntemplate <typename T>\nclass BufferReader<T, std::enable_if_t<std::is_integral_v<T>>> {\n public:\n  static T Read(const tachyon::base::ReadOnlyBuffer& buffer) {\n    tachyon::base::EndianAutoReset resetter(buffer,\n                                            tachyon::base::Endian::kBig);\n    T v;\n    CHECK(buffer.Read(&v));\n    return v;\n  }\n};\n\ninline bool ReadU8AsBool(const tachyon::base::ReadOnlyBuffer& buffer) {\n  return BufferReader<uint8_t>::Read(buffer) != 0;\n}\n\ninline size_t ReadU32AsSizeT(const tachyon::base::ReadOnlyBuffer& buffer) {\n  return size_t{BufferReader<uint32_t>::Read(buffer)};\n}\n\ntemplate <>\nclass BufferReader<std::string> {\n public:\n  static std::string Read(const tachyon::base::ReadOnlyBuffer& buffer) {\n    size_t size = ReadU32AsSizeT(buffer);\n    std::string ret;\n    ret.resize(size);\n    CHECK(buffer.Read(reinterpret_cast<uint8_t*>(const_cast<char*>(ret.data())),\n                      size));\n    return ret;\n  }\n};\n\ntemplate <typename T>\nclass BufferReader<std::optional<T>> {\n public:\n  static std::optional<T> Read(const tachyon::base::ReadOnlyBuffer& buffer) {\n    bool has_value = ReadU8AsBool(buffer);\n    if (has_value) {\n      return BufferReader<T>::Read(buffer);\n    } else {\n      return std::nullopt;\n    }\n  }\n};\n\ntemplate <>\nclass BufferReader<std::optional<size_t>> {\n public:\n  static std::optional<size_t> Read(\n      const tachyon::base::ReadOnlyBuffer& buffer) {\n    bool has_value = ReadU8AsBool(buffer);\n    if (has_value) {\n      return ReadU32AsSizeT(buffer);\n    } else {\n      return std::nullopt;\n    }\n  }\n};\n\ntemplate <typename T>\nclass BufferReader<std::vector<T>> {\n public:\n  static std::vector<T> Read(const tachyon::base::ReadOnlyBuffer& buffer) {\n    return tachyon::base::CreateVector(ReadU32AsSizeT(buffer), [&buffer]() {\n      return BufferReader<T>::Read(buffer);\n    });\n  }\n};\n\ntemplate <typename K, typename V>\nclass BufferReader<absl::btree_map<K, V>> {\n public:\n  static absl::btree_map<K, V> Read(\n      const tachyon::base::ReadOnlyBuffer& buffer) {\n    size_t size = ReadU32AsSizeT(buffer);\n    absl::btree_map<K, V> ret;\n    for (size_t i = 0; i < size; ++i) {\n      K key = BufferReader<K>::Read(buffer);\n      V value = BufferReader<V>::Read(buffer);\n      ret[std::move(key)] = std::move(value);\n    }\n    return ret;\n  }\n};\n\ntemplate <typename Curve>\nclass BufferReader<tachyon::math::AffinePoint<Curve>> {\n public:\n  using BaseField = typename tachyon::math::AffinePoint<Curve>::BaseField;\n\n  static tachyon::math::AffinePoint<Curve> Read(\n      const tachyon::base::ReadOnlyBuffer& buffer) {\n    return {BufferReader<BaseField>::Read(buffer),\n            BufferReader<BaseField>::Read(buffer)};\n  }\n};\n\ntemplate <typename T>\nclass BufferReader<\n    T,\n    std::enable_if_t<std::is_base_of_v<tachyon::math::PrimeFieldBase<T>, T>>> {\n public:\n  using BigInt = typename T::BigIntTy;\n\n  static T Read(const tachyon::base::ReadOnlyBuffer& buffer) {\n    tachyon::base::EndianAutoReset resetter(buffer,\n                                            tachyon::base::Endian::kLittle);\n    BigInt montgomery;\n    CHECK(buffer.Read(montgomery.limbs));\n    return T::FromMontgomery(montgomery);\n  }\n};\n\ntemplate <typename T>\nclass BufferReader<T, std::enable_if_t<std::is_base_of_v<\n                          tachyon::math::QuadraticExtensionField<T>, T>>> {\n public:\n  using BaseField = typename T::BaseField;\n\n  static T Read(const tachyon::base::ReadOnlyBuffer& buffer) {\n    tachyon::base::EndianAutoReset resetter(buffer,\n                                            tachyon::base::Endian::kLittle);\n    BaseField c0 = BufferReader<BaseField>::Read(buffer);\n    BaseField c1 = BufferReader<BaseField>::Read(buffer);\n    return {std::move(c0), std::move(c1)};\n  }\n};\n\ntemplate <typename T>\nclass BufferReader<T, std::enable_if_t<std::is_base_of_v<\n                          tachyon::math::CubicExtensionField<T>, T>>> {\n public:\n  using BaseField = typename T::BaseField;\n\n  static T Read(const tachyon::base::ReadOnlyBuffer& buffer) {\n    tachyon::base::EndianAutoReset resetter(buffer,\n                                            tachyon::base::Endian::kLittle);\n    BaseField c0 = BufferReader<BaseField>::Read(buffer);\n    BaseField c1 = BufferReader<BaseField>::Read(buffer);\n    BaseField c2 = BufferReader<BaseField>::Read(buffer);\n    return {std::move(c0), std::move(c1), std::move(c1)};\n  }\n};\n\ntemplate <typename F, size_t MaxDegree>\nclass BufferReader<tachyon::math::UnivariateDensePolynomial<F, MaxDegree>> {\n public:\n  static tachyon::math::UnivariateDensePolynomial<F, MaxDegree> Read(\n      const tachyon::base::ReadOnlyBuffer& buffer) {\n    std::vector<F> coeffs;\n    ReadBuffer(buffer, coeffs);\n    return tachyon::math::UnivariateDensePolynomial<F, MaxDegree>(\n        tachyon::math::UnivariateDenseCoefficients<F, MaxDegree>(\n            std::move(coeffs), true));\n  }\n};  // namespace tachyon::c::zk\n\ntemplate <typename F, size_t MaxDegree>\nclass BufferReader<tachyon::math::UnivariateEvaluations<F, MaxDegree>> {\n public:\n  static tachyon::math::UnivariateEvaluations<F, MaxDegree> Read(\n      const tachyon::base::ReadOnlyBuffer& buffer) {\n    std::vector<F> evals;\n    ReadBuffer(buffer, evals);\n    return tachyon::math::UnivariateEvaluations<F, MaxDegree>(std::move(evals));\n  }\n};  // namespace tachyon::c::zk\n\ntemplate <>\nclass BufferReader<tachyon::zk::plonk::Phase> {\n public:\n  static tachyon::zk::plonk::Phase Read(\n      const tachyon::base::ReadOnlyBuffer& buffer) {\n    return tachyon::zk::plonk::Phase(BufferReader<uint8_t>::Read(buffer));\n  }\n};\n\ntemplate <>\nclass BufferReader<tachyon::zk::plonk::Challenge> {\n public:\n  static tachyon::zk::plonk::Challenge Read(\n      const tachyon::base::ReadOnlyBuffer& buffer) {\n    size_t index = ReadU32AsSizeT(buffer);\n    tachyon::zk::plonk::Phase phase =\n        BufferReader<tachyon::zk::plonk::Phase>::Read(buffer);\n    return {index, phase};\n  }\n};\n\ntemplate <>\nclass BufferReader<tachyon::zk::Rotation> {\n public:\n  static tachyon::zk::Rotation Read(\n      const tachyon::base::ReadOnlyBuffer& buffer) {\n    return tachyon::zk::Rotation(BufferReader<int32_t>::Read(buffer));\n  }\n};\n\ntemplate <>\nclass BufferReader<tachyon::zk::plonk::Selector> {\n public:\n  static tachyon::zk::plonk::Selector Read(\n      const tachyon::base::ReadOnlyBuffer& buffer) {\n    size_t index = ReadU32AsSizeT(buffer);\n    bool is_simple = ReadU8AsBool(buffer);\n    return is_simple ? tachyon::zk::plonk::Selector::Simple(index)\n                     : tachyon::zk::plonk::Selector::Complex(index);\n  }\n};\n\ntemplate <tachyon::zk::plonk::ColumnType C>\nclass BufferReader<tachyon::zk::plonk::ColumnKey<C>> {\n public:\n  static tachyon::zk::plonk::ColumnKey<C> Read(\n      const tachyon::base::ReadOnlyBuffer& buffer) {\n    size_t index = ReadU32AsSizeT(buffer);\n    uint8_t kind = BufferReader<uint8_t>::Read(buffer);\n    if constexpr (C == tachyon::zk::plonk::ColumnType::kAdvice) {\n      CHECK_EQ(kind, static_cast<int8_t>(C));\n      return tachyon::zk::plonk::ColumnKey<C>(\n          index, BufferReader<tachyon::zk::plonk::Phase>::Read(buffer));\n    } else {\n      if constexpr (C == tachyon::zk::plonk::ColumnType::kInstance ||\n                    C == tachyon::zk::plonk::ColumnType::kFixed) {\n        CHECK_EQ(kind, static_cast<int8_t>(C));\n        return tachyon::zk::plonk::ColumnKey<C>(index);\n      } else {\n        tachyon::zk::plonk::Phase phase =\n            BufferReader<tachyon::zk::plonk::Phase>::Read(buffer);\n        switch (static_cast<tachyon::zk::plonk::ColumnType>(kind)) {\n          case tachyon::zk::plonk::ColumnType::kAdvice:\n            return tachyon::zk::plonk::AdviceColumnKey(index, phase);\n          case tachyon::zk::plonk::ColumnType::kInstance:\n            return tachyon::zk::plonk::InstanceColumnKey(index);\n          case tachyon::zk::plonk::ColumnType::kFixed:\n            return tachyon::zk::plonk::FixedColumnKey(index);\n          case tachyon::zk::plonk::ColumnType::kAny:\n            break;\n        }\n        NOTREACHED();\n        return tachyon::zk::plonk::AnyColumnKey();\n      }\n    }\n  }\n};\n\ntemplate <>\nclass BufferReader<tachyon::zk::plonk::VirtualCell> {\n public:\n  static tachyon::zk::plonk::VirtualCell Read(\n      const tachyon::base::ReadOnlyBuffer& buffer) {\n    tachyon::zk::plonk::AnyColumnKey column =\n        BufferReader<tachyon::zk::plonk::AnyColumnKey>::Read(buffer);\n    tachyon::zk::Rotation rotation =\n        BufferReader<tachyon::zk::Rotation>::Read(buffer);\n    return {column, rotation};\n  }\n};\n\ntemplate <tachyon::zk::plonk::ColumnType C>\nclass BufferReader<tachyon::zk::plonk::QueryData<C>> {\n public:\n  static tachyon::zk::plonk::QueryData<C> Read(\n      const tachyon::base::ReadOnlyBuffer& buffer) {\n    if constexpr (C == tachyon::zk::plonk::ColumnType::kAny) {\n      NOTREACHED();\n    } else {\n      tachyon::zk::plonk::ColumnKey<C> column =\n          BufferReader<tachyon::zk::plonk::ColumnKey<C>>::Read(buffer);\n      tachyon::zk::Rotation rotation =\n          BufferReader<tachyon::zk::Rotation>::Read(buffer);\n      return {rotation, column};\n    }\n  }\n};\n\ntemplate <tachyon::zk::plonk::ColumnType C>\nclass BufferReader<tachyon::zk::plonk::Query<C>> {\n public:\n  static tachyon::zk::plonk::Query<C> Read(\n      const tachyon::base::ReadOnlyBuffer& buffer) {\n    bool has_index = ReadU8AsBool(buffer);\n    size_t index = has_index ? ReadU32AsSizeT(buffer) : 0;\n    size_t column_index = ReadU32AsSizeT(buffer);\n    tachyon::zk::Rotation rotation =\n        BufferReader<tachyon::zk::Rotation>::Read(buffer);\n    if constexpr (C == tachyon::zk::plonk::ColumnType::kAdvice) {\n      tachyon::zk::plonk::Phase phase =\n          BufferReader<tachyon::zk::plonk::Phase>::Read(buffer);\n      return {\n          index,\n          rotation,\n          tachyon::zk::plonk::ColumnKey<C>(column_index, phase),\n      };\n    } else {\n      if constexpr (C == tachyon::zk::plonk::ColumnType::kInstance ||\n                    C == tachyon::zk::plonk::ColumnType::kFixed) {\n        return {\n            index,\n            rotation,\n            tachyon::zk::plonk::ColumnKey<C>(column_index),\n        };\n      } else {\n        NOTREACHED();\n      }\n    }\n  }\n};\n\ntemplate <typename F>\nclass BufferReader<std::unique_ptr<tachyon::zk::Expression<F>>> {\n public:\n  static std::unique_ptr<tachyon::zk::Expression<F>> Read(\n      const tachyon::base::ReadOnlyBuffer& buffer) {\n    uint8_t kind = BufferReader<uint8_t>::Read(buffer);\n    // NOTE(batzor): this switch statement is hardcoded to be compliant with\n    // halo2 rust implementation.\n    // https://github.com/kroma-network/halo2/blob/4ad135/halo2_proofs/src/plonk/circuit.rs#L993\n    switch (kind) {\n      case 0:\n        return tachyon::zk::ExpressionFactory<F>::Constant(\n            BufferReader<F>::Read(buffer));\n      case 1:\n        return tachyon::zk::plonk::ExpressionFactory<F>::Selector(\n            BufferReader<tachyon::zk::plonk::Selector>::Read(buffer));\n      case 2:\n        return tachyon::zk::plonk::ExpressionFactory<F>::Fixed(\n            BufferReader<tachyon::zk::plonk::FixedQuery>::Read(buffer));\n      case 3:\n        return tachyon::zk::plonk::ExpressionFactory<F>::Advice(\n            BufferReader<tachyon::zk::plonk::AdviceQuery>::Read(buffer));\n      case 4:\n        return tachyon::zk::plonk::ExpressionFactory<F>::Instance(\n            BufferReader<tachyon::zk::plonk::InstanceQuery>::Read(buffer));\n      case 5:\n        return tachyon::zk::plonk::ExpressionFactory<F>::Challenge(\n            BufferReader<tachyon::zk::plonk::Challenge>::Read(buffer));\n      case 6:\n        return tachyon::zk::ExpressionFactory<F>::Negated(\n            BufferReader<std::unique_ptr<tachyon::zk::Expression<F>>>::Read(\n                buffer));\n      case 7: {\n        std::unique_ptr<tachyon::zk::Expression<F>> left;\n        ReadBuffer(buffer, left);\n        std::unique_ptr<tachyon::zk::Expression<F>> right;\n        ReadBuffer(buffer, right);\n        return tachyon::zk::ExpressionFactory<F>::Sum(std::move(left),\n                                                      std::move(right));\n      }\n      case 8: {\n        std::unique_ptr<tachyon::zk::Expression<F>> left;\n        ReadBuffer(buffer, left);\n        std::unique_ptr<tachyon::zk::Expression<F>> right;\n        ReadBuffer(buffer, right);\n        return tachyon::zk::ExpressionFactory<F>::Product(std::move(left),\n                                                          std::move(right));\n      }\n      case 9: {\n        std::unique_ptr<tachyon::zk::Expression<F>> expr;\n        ReadBuffer(buffer, expr);\n        F scale = BufferReader<F>::Read(buffer);\n        return tachyon::zk::ExpressionFactory<F>::Scaled(std::move(expr),\n                                                         std::move(scale));\n      }\n    }\n    NOTREACHED();\n    return nullptr;\n  }\n};\n\ntemplate <typename F>\nclass BufferReader<tachyon::zk::plonk::Gate<F>> {\n public:\n  static tachyon::zk::plonk::Gate<F> Read(\n      const tachyon::base::ReadOnlyBuffer& buffer) {\n    std::vector<std::unique_ptr<tachyon::zk::Expression<F>>> polys;\n    ReadBuffer(buffer, polys);\n    std::vector<tachyon::zk::plonk::Selector> queried_selectors;\n    ReadBuffer(buffer, queried_selectors);\n    std::vector<tachyon::zk::plonk::VirtualCell> queried_cells;\n    ReadBuffer(buffer, queried_cells);\n    return tachyon::zk::plonk::Gate<F>(\"\", {}, std::move(polys),\n                                       std::move(queried_selectors),\n                                       std::move(queried_cells));\n  }\n};\n\ntemplate <>\nclass BufferReader<tachyon::zk::plonk::PermutationArgument> {\n public:\n  static tachyon::zk::plonk::PermutationArgument Read(\n      const tachyon::base::ReadOnlyBuffer& buffer) {\n    std::vector<tachyon::zk::plonk::AnyColumnKey> column_keys;\n    ReadBuffer(buffer, column_keys);\n    return tachyon::zk::plonk::PermutationArgument(std::move(column_keys));\n  }\n};\n\ntemplate <typename F>\nclass BufferReader<tachyon::zk::lookup::Argument<F>> {\n public:\n  static tachyon::zk::lookup::Argument<F> Read(\n      const tachyon::base::ReadOnlyBuffer& buffer) {\n    std::vector<std::vector<std::unique_ptr<tachyon::zk::Expression<F>>>>\n        inputs_expressions;\n    ReadBuffer(buffer, inputs_expressions);\n    std::vector<std::unique_ptr<tachyon::zk::Expression<F>>> table_expressions;\n    ReadBuffer(buffer, table_expressions);\n    return tachyon::zk::lookup::Argument<F>(\"\", std::move(inputs_expressions),\n                                            std::move(table_expressions));\n  }\n};\n\ntemplate <typename F>\nclass BufferReader<tachyon::zk::plonk::LookupTracker<F>> {\n public:\n  static tachyon::zk::plonk::LookupTracker<F> Read(\n      const tachyon::base::ReadOnlyBuffer& buffer) {\n    std::vector<std::unique_ptr<tachyon::zk::Expression<F>>> table;\n    ReadBuffer(buffer, table);\n    std::vector<std::vector<std::unique_ptr<tachyon::zk::Expression<F>>>>\n        inputs;\n    ReadBuffer(buffer, inputs);\n    return tachyon::zk::plonk::LookupTracker<F>(\"\", std::move(table),\n                                                std::move(inputs));\n  }\n};\n\ntemplate <typename F>\nclass BufferReader<tachyon::zk::shuffle::Argument<F>> {\n public:\n  static tachyon::zk::shuffle::Argument<F> Read(\n      const tachyon::base::ReadOnlyBuffer& buffer) {\n    std::vector<std::unique_ptr<tachyon::zk::Expression<F>>> input_expressions;\n    ReadBuffer(buffer, input_expressions);\n    std::vector<std::unique_ptr<tachyon::zk::Expression<F>>>\n        shuffle_expressions;\n    ReadBuffer(buffer, shuffle_expressions);\n    return tachyon::zk::shuffle::Argument<F>(\"\", std::move(input_expressions),\n                                             std::move(shuffle_expressions));\n  }\n};\n\ntemplate <typename T>\nclass BufferReader<\n    T, std::enable_if_t<\n           std::is_same_v<T, c::zk::plonk::halo2::bn254::GWCPCS> ||\n           std::is_same_v<T, c::zk::plonk::halo2::bn254::SHPlonkPCS>>> {\n public:\n  static T Read(const tachyon::base::ReadOnlyBuffer& buffer) {\n    using G1Point = typename T::G1Point;\n    using G2Point = typename T::G2Point;\n\n    uint32_t k;\n    CHECK(buffer.Read(&k));\n    size_t n = size_t{1} << k;\n    std::vector<G1Point> g1_powers_of_tau =\n        tachyon::base::CreateVector(n, [&buffer]() {\n          G1Point point;\n          ReadBuffer(buffer, point);\n          return point;\n        });\n    std::vector<G1Point> g1_powers_of_tau_lagrange =\n        tachyon::base::CreateVector(n, [&buffer]() {\n          G1Point point;\n          ReadBuffer(buffer, point);\n          return point;\n        });\n\n    // NOTE(dongchangYoo): read |g2| but do not use it.\n    G2Point g2;\n    ReadBuffer(buffer, g2);\n\n    G2Point s_g2;\n    ReadBuffer(buffer, s_g2);\n    return {std::move(g1_powers_of_tau), std::move(g1_powers_of_tau_lagrange),\n            std::move(s_g2)};\n  }\n};\n\n}  // namespace tachyon::c::zk::plonk\n\n#endif  // TACHYON_C_ZK_PLONK_HALO2_BUFFER_READER_H_\n"
  },
  {
    "path": "tachyon/c/zk/plonk/halo2/constants.h",
    "content": "#ifndef TACHYON_C_ZK_PLONK_HALO2_CONSTANTS_H_\n#define TACHYON_C_ZK_PLONK_HALO2_CONSTANTS_H_\n\n#define TACHYON_HALO2_PSE_VENDOR 0\n#define TACHYON_HALO2_SCROLL_VENDOR 1\n\n#define TACHYON_HALO2_GWC_PCS 0\n#define TACHYON_HALO2_SHPLONK_PCS 1\n\n#define TACHYON_HALO2_BLAKE2B_TRANSCRIPT 0\n#define TACHYON_HALO2_POSEIDON_TRANSCRIPT 1\n#define TACHYON_HALO2_SHA256_TRANSCRIPT 2\n#define TACHYON_HALO2_SNARK_VERIFIER_POSEIDON_TRANSCRIPT 3\n\n#endif  // TACHYON_C_ZK_PLONK_HALO2_CONSTANTS_H_\n"
  },
  {
    "path": "tachyon/c/zk/plonk/halo2/kzg_family_prover_impl.h",
    "content": "#ifndef TACHYON_C_ZK_PLONK_HALO2_KZG_FAMILY_PROVER_IMPL_H_\n#define TACHYON_C_ZK_PLONK_HALO2_KZG_FAMILY_PROVER_IMPL_H_\n\n#include <algorithm>\n#include <utility>\n#include <vector>\n\n#include \"absl/types/span.h\"\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/c/base/type_traits_forward.h\"\n#include \"tachyon/c/math/elliptic_curves/point_traits_forward.h\"\n#include \"tachyon/c/zk/plonk/halo2/prover_impl_base.h\"\n#include \"tachyon/math/elliptic_curves/msm/variable_base_msm.h\"\n\nnamespace tachyon::c::zk::plonk::halo2 {\n\ntemplate <typename PS>\nclass KZGFamilyProverImpl : public ProverImplBase<PS> {\n public:\n  using Callback = typename ProverImplBase<PS>::Callback;\n  using PCS = typename PS::PCS;\n  using AffinePoint = typename PCS::Commitment;\n  using ProjectivePoint =\n      tachyon::math::ProjectivePoint<typename AffinePoint::Curve>;\n  using ScalarField = typename AffinePoint::ScalarField;\n  using CAffinePoint = typename math::PointTraits<AffinePoint>::CCurvePoint;\n  using CProjectivePoint =\n      typename math::PointTraits<ProjectivePoint>::CCurvePoint;\n  using CScalarField = typename math::PointTraits<AffinePoint>::CScalarField;\n\n  using ProverImplBase<PS>::ProverImplBase;\n\n  CProjectivePoint* CommitRaw(const std::vector<ScalarField>& scalars) const {\n#if TACHYON_CUDA\n    if (this->pcs_.UsesGPU()) {\n      return DoMSM(this->pcs_.GetDeviceG1PowersOfTau(), scalars);\n    }\n#endif\n    return DoMSM(this->pcs_.GetG1PowersOfTau(), scalars);\n  }\n\n  CProjectivePoint* CommitLagrangeRaw(\n      const std::vector<ScalarField>& scalars) const {\n#if TACHYON_CUDA\n    if (this->pcs_.UsesGPU()) {\n      return DoMSM(this->pcs_.GetDeviceG1PowersOfTauLagrange(), scalars);\n    }\n#endif\n    return DoMSM(this->pcs_.GetG1PowersOfTauLagrange(), scalars);\n  }\n\n private:\n  template <typename BaseContainer>\n  CProjectivePoint* DoMSM(const BaseContainer& bases,\n                          const std::vector<ScalarField>& scalars) const {\n    ProjectivePoint* ret = new ProjectivePoint();\n    CHECK(this->pcs_.DoMSM(bases, scalars, ret));\n\n    return c::base::c_cast(ret);\n  }\n};\n\n}  // namespace tachyon::c::zk::plonk::halo2\n\n#endif  // TACHYON_C_ZK_PLONK_HALO2_KZG_FAMILY_PROVER_IMPL_H_\n"
  },
  {
    "path": "tachyon/c/zk/plonk/halo2/prover_impl_base.h",
    "content": "#ifndef TACHYON_C_ZK_PLONK_HALO2_PROVER_IMPL_BASE_H_\n#define TACHYON_C_ZK_PLONK_HALO2_PROVER_IMPL_BASE_H_\n\n#include <stdint.h>\n\n#include <memory>\n#include <utility>\n\n#include \"tachyon/base/environment.h\"\n#include \"tachyon/base/files/file_util.h\"\n#include \"tachyon/base/functional/callback.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/zk/plonk/halo2/proof_serializer.h\"\n#include \"tachyon/zk/plonk/halo2/prover.h\"\n\nnamespace tachyon::c::zk::plonk::halo2 {\n\ntemplate <typename PS>\nclass ProverImplBase : public tachyon::zk::plonk::halo2::Prover<PS> {\n public:\n  using Base = tachyon::zk::plonk::halo2::Prover<PS>;\n  using PCS = typename Base::PCS;\n  using Poly = typename PCS::Poly;\n  using Evals = typename PCS::Evals;\n  using Commitment = typename PCS::Commitment;\n  using Callback = tachyon::base::OnceCallback<Base()>;\n\n  ProverImplBase(Base&& base, uint8_t transcript_type)\n      : Base(std::move(base)), transcript_type_(transcript_type) {\n    tachyon::zk::plonk::halo2::ProofSerializer<\n        Commitment>::s_use_legacy_serialization = false;\n  }\n\n  ProverImplBase(Callback callback, uint8_t transcript_type)\n      : Base(std::move(callback).Run()), transcript_type_(transcript_type) {\n    tachyon::zk::plonk::halo2::ProofSerializer<\n        Commitment>::s_use_legacy_serialization = false;\n\n    std::string_view pcs_params_str;\n    if (tachyon::base::Environment::Get(\"TACHYON_PCS_PARAMS_LOG_PATH\",\n                                        &pcs_params_str)) {\n      VLOG(1) << \"Save pcs params to: \" << pcs_params_str;\n      tachyon::base::Uint8VectorBuffer buffer;\n      CHECK(buffer.Grow(tachyon::base::EstimateSize(this->pcs_)));\n      CHECK(buffer.Write(this->pcs_));\n      CHECK(tachyon::base::WriteFile(tachyon::base::FilePath(pcs_params_str),\n                                     buffer.owned_buffer()));\n    }\n  }\n\n  uint8_t transcript_type() const { return transcript_type_; }\n\n  void SetRng(std::unique_ptr<crypto::RNG> rng) {\n    Base::SetRng(std::move(rng));\n  }\n\n  void SetTranscript(\n      absl::Span<const uint8_t> state,\n      std::unique_ptr<crypto::TranscriptWriter<Commitment>> writer) {\n    std::string_view state_str;\n    if (tachyon::base::Environment::Get(\"TACHYON_TRANSCRIPT_STATE_LOG_PATH\",\n                                        &state_str)) {\n      VLOG(1) << \"Save transcript state to: \" << state_str;\n      CHECK(\n          tachyon::base::WriteFile(tachyon::base::FilePath(state_str), state));\n    }\n\n    this->transcript_ = std::move(writer);\n  }\n\n  void CreateProof(\n      tachyon::zk::plonk::ProvingKey<PS>& proving_key,\n      tachyon::zk::plonk::halo2::ArgumentData<Poly, Evals>* argument_data) {\n    std::string_view arg_data_str;\n    if (tachyon::base::Environment::Get(\"TACHYON_ARG_DATA_LOG_PATH\",\n                                        &arg_data_str)) {\n      VLOG(1) << \"Save argument data to: \" << arg_data_str;\n      tachyon::base::Uint8VectorBuffer buffer;\n      CHECK(buffer.Grow(tachyon::base::EstimateSize(*argument_data)));\n      CHECK(buffer.Write(*argument_data));\n      CHECK(tachyon::base::WriteLargeFile(tachyon::base::FilePath(arg_data_str),\n                                          buffer.owned_buffer()));\n    }\n\n    Base::CreateProof(proving_key, argument_data);\n  }\n\n protected:\n  uint8_t transcript_type_;\n};\n\n}  // namespace tachyon::c::zk::plonk::halo2\n\n#endif  // TACHYON_C_ZK_PLONK_HALO2_PROVER_IMPL_BASE_H_\n"
  },
  {
    "path": "tachyon/c/zk/plonk/halo2/prover_replay.cc",
    "content": "#include <string>\n\n#include \"tachyon/base/console/iostream.h\"\n#include \"tachyon/base/files/file_path_flag.h\"\n#include \"tachyon/base/files/file_util.h\"\n#include \"tachyon/base/flag/flag_parser.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/fr_type_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1_point_traits.h\"\n#include \"tachyon/c/zk/plonk/halo2/bn254_prover.h\"\n#include \"tachyon/c/zk/plonk/halo2/bn254_ps.h\"\n#include \"tachyon/c/zk/plonk/halo2/kzg_family_prover_impl.h\"\n#include \"tachyon/c/zk/plonk/keys/proving_key_impl.h\"\n#include \"tachyon/crypto/random/cha_cha20/cha_cha20_rng.h\"\n#include \"tachyon/crypto/random/rng_type.h\"\n#include \"tachyon/crypto/random/xor_shift/xor_shift_rng.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluation_domain_factory.h\"\n#include \"tachyon/zk/plonk/halo2/constants.h\"\n#include \"tachyon/zk/plonk/halo2/pcs_type.h\"\n#include \"tachyon/zk/plonk/halo2/transcript_type.h\"\n#include \"tachyon/zk/plonk/halo2/vendor.h\"\n\nnamespace tachyon {\nnamespace c::zk::plonk::halo2::bn254 {\n\nusing PSEGWC = c::zk::plonk::halo2::bn254::PSEGWC;\nusing PSESHPlonk = c::zk::plonk::halo2::bn254::PSESHPlonk;\nusing ScrollGWC = c::zk::plonk::halo2::bn254::ScrollGWC;\nusing ScrollSHPlonk = c::zk::plonk::halo2::bn254::ScrollSHPlonk;\ntemplate <typename PCS>\nusing ArgumentData =\n    tachyon::zk::plonk::halo2::ArgumentData<typename PCS::Poly,\n                                            typename PCS::Evals>;\n\ntemplate <typename PS>\nusing Prover = KZGFamilyProverImpl<PS>;\n\ntemplate <typename PS>\nusing ProvingKey = plonk::ProvingKeyImpl<PS>;\n\ntemplate <typename PCS>\nArgumentData<PCS> DeserializeArgumentData(\n    const std::vector<uint8_t>& arg_data_bytes) {\n  ArgumentData<PCS> arg_data;\n  tachyon::base::Buffer buffer(const_cast<uint8_t*>(arg_data_bytes.data()),\n                               arg_data_bytes.size());\n  CHECK(buffer.Read(&arg_data));\n  CHECK(buffer.Done());\n  return arg_data;\n}\n\ntemplate <typename PCS>\nvoid WriteParams(const PCS& pcs, const tachyon::base::FilePath& params_path) {\n  tachyon::base::Uint8VectorBuffer buffer;\n  CHECK(buffer.Grow(tachyon::base::EstimateSize(pcs)));\n  CHECK(buffer.Write(pcs));\n  CHECK(buffer.Done());\n  CHECK(tachyon::base::WriteLargeFile(params_path, buffer.owned_buffer()));\n}\n\nvoid WriteParams(tachyon_halo2_bn254_prover* c_prover,\n                 const tachyon::base::FilePath& params_path) {\n  switch (static_cast<tachyon::zk::plonk::halo2::PCSType>(c_prover->pcs_type)) {\n    case tachyon::zk::plonk::halo2::PCSType::kGWC: {\n      WriteParams(\n          reinterpret_cast<tachyon::zk::Entity<GWCPCS>*>(c_prover->extra)\n              ->pcs(),\n          params_path);\n      break;\n    }\n    case tachyon::zk::plonk::halo2::PCSType::kSHPlonk: {\n      WriteParams(\n          reinterpret_cast<tachyon::zk::Entity<SHPlonkPCS>*>(c_prover->extra)\n              ->pcs(),\n          params_path);\n      break;\n    }\n  }\n}\n\ntemplate <typename NativeProver>\nvoid CreateProof(NativeProver* prover, tachyon_halo2_bn254_prover* c_prover,\n                 crypto::RNGType rng_type, const std::vector<uint8_t>& pk_bytes,\n                 const std::vector<uint8_t>& arg_data_bytes,\n                 const std::vector<uint8_t>& transcript_state_bytes) {\n  using PS = typename NativeProver::PS;\n  using PCS = typename PS::PCS;\n\n  std::cout << \"deserializing proving key\" << std::endl;\n  ProvingKey<PS> pk(pk_bytes, /*read_only_vk=*/false);\n  std::cout << \"done deserializing proving key\" << std::endl;\n\n  uint32_t extended_k = pk.verifying_key().constraint_system().ComputeExtendedK(\n      prover->pcs().K());\n  prover->set_extended_domain(\n      PCS::ExtendedDomain::Create(size_t{1} << extended_k));\n#if TACHYON_CUDA\n  prover->EnableIcicleNTT();\n#endif\n\n  tachyon_halo2_bn254_prover_set_transcript_state(\n      c_prover, transcript_state_bytes.data(), transcript_state_bytes.size());\n\n  std::unique_ptr<tachyon::crypto::RNG> rng;\n  switch (rng_type) {\n    case crypto::RNGType::kXORShift:\n      rng = std::make_unique<tachyon::crypto::XORShiftRNG>();\n      CHECK(rng->SetSeed(tachyon::zk::plonk::halo2::kXORShiftSeed));\n      break;\n    case crypto::RNGType::kChaCha20:\n      rng = std::make_unique<tachyon::crypto::ChaCha20RNG>();\n      break;\n  }\n  CHECK(rng);\n  prover->SetRng(std::move(rng));\n\n  std::cout << \"deserializing argument data\" << std::endl;\n  ArgumentData<PCS> arg_data = DeserializeArgumentData<PCS>(arg_data_bytes);\n  std::cout << \"done deserializing argument data\" << std::endl;\n  for (size_t i = 0; i < arg_data.advice_blinds_vec().size(); ++i) {\n    const std::vector<tachyon::math::bn254::Fr>& advice_blinds =\n        arg_data.advice_blinds_vec()[i];\n    for (size_t j = 0; j < advice_blinds.size(); ++j) {\n      // Update Rng state\n      prover->blinder().Generate();\n    }\n  }\n\n  prover->blinder().set_blinding_factors(\n      pk.verifying_key().constraint_system().ComputeBlindingFactors());\n  prover->CreateProof(pk, &arg_data);\n}\n\nvoid CreateProof(tachyon_halo2_bn254_prover* c_prover, crypto::RNGType rng_type,\n                 const std::vector<uint8_t>& pk_bytes,\n                 const std::vector<uint8_t>& arg_data_bytes,\n                 const std::vector<uint8_t>& transcript_state_bytes) {\n  switch (static_cast<tachyon::zk::plonk::halo2::Vendor>(c_prover->vendor)) {\n    case tachyon::zk::plonk::halo2::Vendor::kPSE: {\n      switch (\n          static_cast<tachyon::zk::plonk::halo2::PCSType>(c_prover->pcs_type)) {\n        case tachyon::zk::plonk::halo2::PCSType::kGWC: {\n          CreateProof(reinterpret_cast<Prover<PSEGWC>*>(c_prover->extra),\n                      c_prover, rng_type, pk_bytes, arg_data_bytes,\n                      transcript_state_bytes);\n          break;\n        }\n        case tachyon::zk::plonk::halo2::PCSType::kSHPlonk: {\n          CreateProof(reinterpret_cast<Prover<PSESHPlonk>*>(c_prover->extra),\n                      c_prover, rng_type, pk_bytes, arg_data_bytes,\n                      transcript_state_bytes);\n          break;\n        }\n      }\n      break;\n    }\n    case tachyon::zk::plonk::halo2::Vendor::kScroll: {\n      switch (\n          static_cast<tachyon::zk::plonk::halo2::PCSType>(c_prover->pcs_type)) {\n        case tachyon::zk::plonk::halo2::PCSType::kGWC: {\n          CreateProof(reinterpret_cast<Prover<ScrollGWC>*>(c_prover->extra),\n                      c_prover, rng_type, pk_bytes, arg_data_bytes,\n                      transcript_state_bytes);\n          break;\n        }\n        case tachyon::zk::plonk::halo2::PCSType::kSHPlonk: {\n          CreateProof(reinterpret_cast<Prover<ScrollSHPlonk>*>(c_prover->extra),\n                      c_prover, rng_type, pk_bytes, arg_data_bytes,\n                      transcript_state_bytes);\n          break;\n        }\n      }\n      break;\n    }\n  }\n  NOTREACHED();\n}\n\n}  // namespace c::zk::plonk::halo2::bn254\n\nint RunMain(int argc, char** argv) {\n  if (tachyon::base::Environment::Has(\"TACHYON_PCS_PARAMS_PATH\")) {\n    tachyon_cerr << \"If this is set, the pcs params is overwritten\"\n                 << std::endl;\n    return 1;\n  }\n  if (tachyon::base::Environment::Has(\"TACHYON_PK_LOG_PATH\")) {\n    tachyon_cerr << \"If this is set, the pk log is overwritten\" << std::endl;\n    return 1;\n  }\n  if (tachyon::base::Environment::Has(\"TACHYON_ARG_DATA_LOG_PATH\")) {\n    tachyon_cerr << \"If this is set, the arg data log is overwritten\"\n                 << std::endl;\n    return 1;\n  }\n  if (tachyon::base::Environment::Has(\"TACHYON_TRANSCRIPT_STATE_LOG_PATH\")) {\n    tachyon_cerr << \"If this is set, the transcript state log is overwritten\"\n                 << std::endl;\n    return 1;\n  }\n\n  zk::plonk::halo2::Vendor vendor;\n  zk::plonk::halo2::PCSType pcs_type;\n  zk::plonk::halo2::TranscriptType transcript_type;\n  crypto::RNGType rng_type;\n  uint32_t k;\n  std::string s_hex;\n  tachyon::base::FilePath pcs_params_path;\n  tachyon::base::FilePath pk_path;\n  tachyon::base::FilePath arg_data_path;\n  tachyon::base::FilePath transcript_state_path;\n  tachyon::base::FlagParser parser;\n  parser.AddFlag<tachyon::base::Flag<zk::plonk::halo2::Vendor>>(&vendor)\n      .set_long_name(\"--vendor\")\n      .set_required()\n      .set_help(\"Vendor\");\n  parser.AddFlag<tachyon::base::Flag<zk::plonk::halo2::PCSType>>(&pcs_type)\n      .set_long_name(\"--pcs_type\")\n      .set_required()\n      .set_help(\"PCS(Polynomial Commitment Scheme) type\");\n  parser\n      .AddFlag<tachyon::base::Flag<zk::plonk::halo2::TranscriptType>>(\n          &transcript_type)\n      .set_long_name(\"--transcript_type\")\n      .set_required()\n      .set_help(\"Transcript type\");\n  parser.AddFlag<tachyon::base::Flag<crypto::RNGType>>(&rng_type)\n      .set_long_name(\"--rng_type\")\n      .set_required()\n      .set_help(\"Rng type\");\n  parser.AddFlag<tachyon::base::Uint32Flag>(&k)\n      .set_short_name(\"-k\")\n      .set_required()\n      .set_help(\"K\");\n  parser.AddFlag<tachyon::base::StringFlag>(&s_hex)\n      .set_short_name(\"-s\")\n      .set_help(\"s in hex\");\n  parser.AddFlag<tachyon::base::FilePathFlag>(&pcs_params_path)\n      .set_long_name(\"--pcs_params\")\n      .set_help(\"The path to pcs params\");\n  parser.AddFlag<tachyon::base::FilePathFlag>(&pk_path)\n      .set_long_name(\"--pk\")\n      .set_required()\n      .set_help(\"The path to proving key\");\n  parser.AddFlag<tachyon::base::FilePathFlag>(&arg_data_path)\n      .set_long_name(\"--arg_data\")\n      .set_required()\n      .set_help(\"The path to argument data\");\n  parser.AddFlag<tachyon::base::FilePathFlag>(&transcript_state_path)\n      .set_long_name(\"--transcript_state\")\n      .set_required()\n      .set_help(\"The path to transcript state\");\n  {\n    std::string error;\n    if (!parser.Parse(argc, argv, &error)) {\n      tachyon_cerr << error << std::endl;\n      return 1;\n    }\n  }\n\n  std::optional<std::vector<uint8_t>> pk_bytes =\n      tachyon::base::ReadFileToBytes(pk_path);\n  if (!pk_bytes.has_value()) {\n    tachyon_cerr << \"Failed to read file: \" << pk_path.value() << std::endl;\n    return 1;\n  }\n\n  std::optional<std::vector<uint8_t>> arg_data_bytes =\n      tachyon::base::ReadFileToBytes(arg_data_path);\n  if (!arg_data_bytes.has_value()) {\n    tachyon_cerr << \"Failed to read file: \" << arg_data_path.value()\n                 << std::endl;\n    return 1;\n  }\n\n  std::optional<std::vector<uint8_t>> transcript_state_bytes =\n      tachyon::base::ReadFileToBytes(transcript_state_path);\n  if (!transcript_state_bytes.has_value()) {\n    tachyon_cerr << \"Failed to read file: \" << transcript_state_path.value()\n                 << std::endl;\n    return 1;\n  }\n\n  tachyon_halo2_bn254_prover* prover;\n  std::optional<std::vector<uint8_t>> pcs_params_bytes;\n  if (!pcs_params_path.empty()) {\n    pcs_params_bytes = tachyon::base::ReadFileToBytes(pcs_params_path);\n  }\n\n  if (pcs_params_bytes.has_value()) {\n    std::cout << \"creating prover\" << std::endl;\n    prover = tachyon_halo2_bn254_prover_create_from_params(\n        static_cast<uint8_t>(vendor), static_cast<uint8_t>(pcs_type),\n        static_cast<uint8_t>(transcript_type), k, pcs_params_bytes->data(),\n        pcs_params_bytes->size());\n    std::cout << \"done creating prover\" << std::endl;\n  } else {\n    if (s_hex.empty()) {\n      tachyon_cerr << \"s_hex is empty\" << std::endl;\n      return 1;\n    }\n    math::bn254::Fr cpp_s = *math::bn254::Fr::FromHexString(s_hex);\n    const tachyon_bn254_fr& s = c::base::c_cast(cpp_s);\n\n    std::cout << \"creating prover\" << std::endl;\n    prover = tachyon_halo2_bn254_prover_create_from_unsafe_setup(\n        static_cast<uint8_t>(vendor), static_cast<uint8_t>(pcs_type),\n        static_cast<uint8_t>(transcript_type), k, &s);\n    std::cout << \"done creating prover\" << std::endl;\n    if (!pcs_params_path.empty()) {\n      c::zk::plonk::halo2::bn254::WriteParams(prover, pcs_params_path);\n    }\n  }\n\n  c::zk::plonk::halo2::bn254::CreateProof(prover, rng_type, pk_bytes.value(),\n                                          arg_data_bytes.value(),\n                                          transcript_state_bytes.value());\n\n  std::vector<uint8_t> proof;\n  size_t proof_size;\n  tachyon_halo2_bn254_prover_get_proof(prover, nullptr, &proof_size);\n  proof.resize(proof_size);\n  tachyon_halo2_bn254_prover_get_proof(prover, proof.data(), &proof_size);\n\n  std::cout << \"proof: [\";\n  for (size_t i = 0; i < proof_size; ++i) {\n    std::cout << uint32_t{proof[i]};\n    if (i != proof_size - 1) {\n      std::cout << \", \";\n    }\n  }\n  std::cout << \"]\" << std::endl;\n\n  tachyon_halo2_bn254_prover_destroy(prover);\n  return 0;\n}\n\n}  // namespace tachyon\n\nint main(int argc, char** argv) { return tachyon::RunMain(argc, argv); }\n"
  },
  {
    "path": "tachyon/c/zk/plonk/halo2/test/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"bn254_halo2_params_data\",\n    testonly = True,\n    hdrs = [\"bn254_halo2_params_data.h\"],\n)\n"
  },
  {
    "path": "tachyon/c/zk/plonk/halo2/test/bn254_halo2_params_data.h",
    "content": "#ifndef TACHYON_C_ZK_PLONK_HALO2_TEST_BN254_HALO2_PARAMS_DATA_H_\n#define TACHYON_C_ZK_PLONK_HALO2_TEST_BN254_HALO2_PARAMS_DATA_H_\n\n#include <stdint.h>\n\nnamespace tachyon::c::zk::plonk::halo2::bn254 {\n\nconstexpr uint8_t kExpectedHalo2Params[] = {\n    5,   0,   0,   0,   157, 13,  143, 197, 141, 67,  93,  211, 61,  11,  199,\n    245, 40,  235, 120, 10,  44,  70,  121, 120, 111, 163, 110, 102, 47,  223,\n    7,   154, 193, 119, 10,  14,  58,  27,  30,  139, 27,  135, 186, 166, 123,\n    22,  142, 235, 81,  214, 241, 20,  88,  140, 242, 240, 222, 70,  221, 204,\n    94,  190, 15,  52,  131, 239, 20,  28,  56,  234, 231, 195, 182, 96,  4,\n    225, 105, 84,  142, 67,  139, 84,  11,  188, 236, 194, 10,  12,  180, 45,\n    130, 194, 77,  1,   141, 9,   151, 115, 34,  19,  71,  253, 124, 216, 22,\n    140, 32,  60,  141, 202, 113, 104, 145, 106, 129, 151, 93,  88,  129, 129,\n    182, 69,  80,  184, 41,  160, 49,  225, 114, 78,  100, 4,   134, 77,  86,\n    113, 25,  238, 235, 95,  131, 14,  18,  72,  241, 172, 172, 88,  170, 116,\n    29,  234, 56,  12,  44,  202, 45,  255, 24,  120, 27,  123, 68,  38,  46,\n    216, 138, 210, 53,  95,  85,  220, 82,  169, 129, 43,  19,  101, 57,  230,\n    174, 175, 97,  145, 193, 124, 129, 104, 224, 228, 39,  102, 172, 126, 154,\n    42,  167, 118, 44,  9,   246, 62,  119, 249, 186, 211, 160, 230, 117, 2,\n    229, 151, 205, 177, 3,   17,  231, 11,  98,  201, 174, 17,  206, 56,  69,\n    175, 125, 41,  88,  253, 74,  200, 245, 45,  56,  47,  80,  205, 143, 22,\n    138, 200, 64,  84,  196, 187, 176, 222, 162, 68,  12,  250, 177, 12,  117,\n    70,  216, 130, 251, 19,  17,  112, 115, 70,  6,   225, 145, 202, 11,  202,\n    94,  79,  151, 210, 89,  185, 11,  198, 119, 43,  21,  130, 164, 116, 56,\n    164, 245, 118, 159, 48,  105, 4,   107, 234, 170, 225, 214, 178, 88,  89,\n    216, 204, 156, 205, 180, 36,  236, 51,  49,  79,  249, 201, 158, 116, 153,\n    146, 185, 226, 127, 133, 47,  211, 7,   10,  68,  140, 183, 243, 39,  125,\n    246, 138, 155, 244, 232, 13,  120, 188, 198, 205, 35,  227, 86,  23,  133,\n    248, 83,  102, 194, 200, 126, 249, 145, 235, 207, 36,  143, 106, 116, 195,\n    146, 43,  112, 229, 47,  208, 89,  177, 200, 31,  151, 198, 18,  226, 89,\n    66,  159, 161, 94,  121, 21,  5,   137, 68,  41,  14,  191, 4,   158, 164,\n    110, 142, 105, 202, 180, 42,  155, 138, 205, 85,  90,  146, 193, 253, 14,\n    200, 236, 43,  227, 50,  99,  158, 206, 21,  162, 59,  142, 16,  151, 5,\n    207, 23,  95,  126, 45,  223, 104, 156, 138, 96,  80,  181, 145, 208, 60,\n    132, 42,  105, 120, 113, 156, 224, 11,  41,  78,  231, 42,  2,   90,  240,\n    221, 11,  124, 65,  188, 172, 192, 85,  143, 84,  185, 116, 134, 158, 3,\n    192, 214, 55,  69,  147, 84,  89,  249, 68,  81,  217, 40,  172, 221, 206,\n    141, 242, 121, 29,  63,  64,  213, 20,  5,   133, 52,  23,  127, 70,  151,\n    186, 204, 56,  249, 227, 52,  156, 242, 98,  160, 3,   39,  52,  85,  135,\n    174, 80,  235, 81,  84,  21,  59,  116, 188, 170, 40,  138, 177, 162, 100,\n    83,  171, 196, 147, 33,  52,  216, 174, 117, 78,  44,  128, 30,  160, 100,\n    114, 141, 58,  145, 210, 4,   81,  12,  74,  32,  141, 222, 207, 119, 178,\n    4,   157, 6,   133, 226, 55,  217, 218, 234, 225, 217, 238, 173, 107, 37,\n    165, 49,  250, 192, 71,  54,  96,  166, 180, 33,  25,  30,  234, 104, 101,\n    75,  157, 155, 17,  241, 104, 160, 15,  82,  206, 192, 0,   223, 116, 4,\n    71,  222, 40,  56,  187, 66,  134, 113, 186, 229, 249, 44,  77,  141, 121,\n    68,  174, 102, 32,  77,  186, 13,  3,   58,  47,  160, 233, 106, 64,  185,\n    254, 172, 19,  190, 78,  2,   231, 125, 56,  58,  165, 240, 0,   35,  138,\n    215, 146, 242, 102, 199, 192, 215, 172, 201, 79,  168, 64,  122, 106, 226,\n    77,  154, 114, 209, 76,  16,  217, 255, 73,  144, 226, 109, 65,  12,  227,\n    7,   40,  115, 130, 148, 226, 164, 65,  234, 13,  236, 164, 20,  90,  236,\n    51,  144, 91,  155, 40,  246, 69,  70,  143, 177, 36,  56,  199, 95,  113,\n    144, 227, 12,  229, 184, 25,  121, 193, 213, 11,  13,  241, 108, 80,  233,\n    55,  238, 236, 190, 87,  25,  191, 14,  126, 198, 78,  183, 72,  189, 209,\n    121, 176, 56,  166, 7,   253, 225, 143, 101, 35,  146, 164, 158, 113, 241,\n    97,  214, 59,  3,   253, 76,  239, 27,  162, 246, 208, 60,  10,  112, 37,\n    110, 203, 20,  120, 204, 221, 29,  37,  164, 36,  234, 130, 14,  159, 114,\n    94,  52,  64,  50,  211, 227, 206, 242, 133, 244, 25,  59,  47,  181, 217,\n    27,  81,  143, 111, 129, 180, 219, 8,   27,  14,  53,  214, 152, 94,  130,\n    0,   182, 229, 44,  153, 76,  168, 157, 209, 203, 4,   21,  61,  137, 57,\n    254, 1,   141, 244, 237, 77,  197, 133, 167, 43,  30,  29,  56,  219, 84,\n    158, 48,  202, 4,   95,  188, 170, 18,  59,  29,  84,  58,  231, 65,  226,\n    168, 219, 41,  152, 215, 35,  214, 168, 134, 177, 72,  21,  25,  182, 21,\n    249, 178, 161, 164, 148, 235, 150, 163, 210, 109, 111, 142, 165, 66,  142,\n    103, 111, 237, 247, 59,  214, 83,  84,  98,  77,  73,  45,  225, 220, 17,\n    169, 137, 135, 240, 138, 56,  149, 205, 153, 60,  10,  167, 184, 206, 50,\n    176, 229, 242, 203, 90,  206, 99,  146, 116, 50,  208, 207, 223, 40,  78,\n    22,  40,  170, 139, 95,  18,  205, 114, 106, 238, 146, 107, 222, 47,  27,\n    191, 72,  29,  162, 97,  70,  51,  187, 201, 236, 155, 17,  195, 196, 203,\n    91,  32,  232, 40,  43,  86,  117, 118, 175, 207, 175, 51,  177, 18,  75,\n    215, 186, 205, 128, 205, 140, 174, 34,  227, 28,  216, 31,  167, 168, 212,\n    43,  144, 244, 183, 134, 25,  53,  17,  144, 188, 191, 36,  254, 102, 121,\n    235, 186, 34,  239, 202, 149, 71,  192, 34,  94,  106, 198, 51,  79,  80,\n    56,  137, 164, 42,  126, 81,  194, 32,  128, 100, 11,  43,  89,  27,  236,\n    223, 114, 228, 103, 83,  98,  100, 176, 157, 95,  92,  238, 192, 206, 42,\n    28,  205, 112, 10,  132, 150, 152, 166, 13,  15,  143, 206, 121, 187, 175,\n    123, 129, 28,  111, 145, 231, 132, 195, 231, 161, 179, 66,  172, 203, 36,\n    243, 178, 27,  248, 104, 141, 89,  10,  50,  77,  131, 32,  227, 104, 34,\n    53,  251, 248, 145, 235, 103, 254, 180, 134, 185, 199, 161, 145, 22,  27,\n    40,  210, 73,  49,  115, 79,  155, 170, 158, 97,  25,  138, 128, 10,  199,\n    109, 129, 93,  81,  219, 73,  113, 151, 67,  41,  131, 119, 210, 123, 245,\n    129, 103, 67,  140, 254, 238, 247, 164, 31,  122, 113, 196, 138, 35,  89,\n    24,  236, 162, 24,  88,  240, 129, 178, 203, 119, 50,  39,  69,  43,  232,\n    44,  127, 88,  175, 43,  233, 159, 191, 213, 155, 70,  30,  85,  21,  212,\n    47,  124, 7,   181, 82,  204, 188, 224, 94,  70,  17,  69,  131, 74,  70,\n    58,  112, 45,  184, 211, 85,  88,  161, 63,  96,  152, 99,  102, 204, 223,\n    26,  130, 190, 212, 35,  186, 246, 72,  241, 252, 210, 52,  2,   54,  4,\n    62,  164, 120, 221, 28,  106, 88,  45,  133, 9,   52,  127, 191, 83,  8,\n    164, 114, 141, 183, 169, 156, 5,   138, 222, 161, 155, 205, 199, 228, 253,\n    131, 95,  125, 136, 133, 144, 111, 26,  209, 230, 165, 129, 137, 66,  118,\n    208, 130, 192, 137, 18,  30,  63,  91,  1,   187, 56,  177, 222, 59,  251,\n    126, 139, 45,  165, 91,  56,  180, 37,  85,  222, 202, 89,  47,  61,  46,\n    63,  104, 24,  235, 205, 114, 38,  213, 82,  52,  47,  78,  194, 57,  244,\n    175, 138, 106, 162, 170, 59,  198, 187, 225, 67,  3,   227, 112, 114, 130,\n    190, 110, 231, 197, 109, 174, 13,  239, 112, 232, 73,  4,   2,   32,  160,\n    156, 3,   231, 203, 216, 103, 141, 67,  5,   168, 243, 191, 1,   40,  204,\n    173, 165, 195, 133, 6,   58,  204, 108, 90,  69,  198, 231, 177, 123, 32,\n    33,  2,   116, 244, 123, 19,  77,  33,  169, 158, 231, 62,  164, 216, 253,\n    156, 182, 3,   84,  82,  93,  30,  93,  162, 203, 69,  139, 30,  116, 152,\n    44,  24,  138, 212, 39,  255, 127, 12,  184, 87,  194, 54,  87,  43,  251,\n    159, 203, 213, 233, 48,  72,  173, 130, 15,  147, 250, 45,  176, 97,  14,\n    169, 191, 181, 9,   208, 111, 42,  103, 163, 62,  50,  245, 243, 111, 42,\n    43,  253, 132, 181, 173, 28,  252, 160, 184, 132, 23,  55,  129, 208, 229,\n    71,  89,  71,  159, 115, 26,  101, 15,  199, 117, 234, 68,  17,  193, 147,\n    104, 180, 182, 7,   24,  83,  197, 199, 227, 180, 121, 189, 28,  226, 74,\n    29,  53,  139, 40,  49,  192, 213, 21,  242, 227, 201, 151, 71,  22,  252,\n    55,  213, 249, 172, 10,  51,  44,  95,  204, 139, 6,   244, 129, 116, 212,\n    71,  59,  52,  227, 158, 62,  168, 104, 207, 8,   85,  10,  190, 171, 128,\n    222, 55,  51,  231, 84,  191, 84,  148, 74,  194, 157, 44,  132, 246, 37,\n    19,  218, 244, 210, 222, 163, 200, 254, 250, 240, 9,   22,  154, 171, 85,\n    173, 159, 148, 119, 130, 27,  167, 156, 128, 96,  201, 253, 121, 65,  150,\n    16,  193, 145, 160, 21,  61,  254, 171, 92,  130, 59,  188, 241, 9,   249,\n    183, 97,  89,  159, 253, 45,  69,  138, 158, 170, 110, 164, 51,  200, 56,\n    99,  64,  161, 215, 63,  109, 50,  128, 238, 7,   163, 167, 158, 149, 132,\n    16,  52,  161, 161, 37,  71,  135, 63,  78,  242, 123, 175, 76,  153, 55,\n    246, 155, 152, 44,  118, 222, 92,  180, 139, 113, 47,  217, 80,  143, 9,\n    4,   249, 1,   79,  193, 162, 131, 224, 181, 75,  251, 242, 41,  171, 212,\n    29,  86,  224, 252, 36,  65,  85,  125, 200, 81,  65,  102, 148, 154, 185,\n    70,  184, 58,  16,  26,  150, 168, 184, 100, 0,   135, 237, 109, 87,  96,\n    187, 182, 99,  58,  203, 157, 249, 218, 174, 138, 157, 1,   204, 144, 86,\n    100, 141, 247, 132, 28,  142, 13,  191, 139, 229, 171, 169, 234, 31,  97,\n    137, 16,  114, 8,   251, 161, 220, 61,  132, 67,  186, 5,   111, 189, 63,\n    40,  167, 67,  145, 50,  177, 157, 19,  26,  251, 51,  189, 245, 252, 151,\n    185, 11,  110, 20,  240, 109, 250, 135, 123, 252, 75,  144, 40,  243, 49,\n    76,  222, 127, 16,  107, 189, 136, 26,  184, 241, 26,  58,  189, 202, 54,\n    107, 125, 228, 46,  55,  153, 16,  24,  22,  103, 101, 204, 70,  185, 92,\n    206, 17,  145, 14,  45,  200, 94,  157, 94,  72,  210, 227, 1,   252, 205,\n    243, 157, 161, 141, 209, 196, 217, 9,   145, 186, 111, 229, 131, 33,  65,\n    129, 217, 56,  107, 31,  178, 7,   123, 215, 24,  3,   207, 126, 48,  47,\n    73,  232, 231, 216, 69,  201, 17,  219, 181, 51,  66,  66,  237, 177, 6,\n    80,  25,  82,  220, 86,  149, 99,  141, 232, 206, 52,  142, 252, 155, 236,\n    121, 39,  100, 168, 88,  72,  79,  142, 167, 166, 49,  190, 139, 195, 75,\n    159, 11,  26,  205, 92,  190, 84,  16,  35,  133, 5,   224, 83,  80,  147,\n    130, 148, 195, 39,  138, 26,  92,  144, 91,  253, 222, 174, 208, 146, 27,\n    9,   80,  248, 251, 253, 221, 90,  21,  254, 228, 236, 184, 2,   23,  102,\n    197, 103, 15,  48,  76,  42,  243, 118, 88,  19,  15,  109, 205, 194, 221,\n    147, 135, 38,  102, 152, 15,  73,  58,  186, 246, 9,   107, 55,  171, 195,\n    250, 189, 43,  30,  240, 23,  23,  33,  172, 115, 116, 93,  95,  72,  244,\n    65,  240, 82,  140, 251, 86,  196, 241, 87,  8,   219, 122, 128, 8,   92,\n    97,  46,  119, 216, 29,  56,  108, 147, 19,  37,  124, 126, 207, 129, 255,\n    106, 77,  255, 230, 217, 158, 113, 47,  222, 38,  83,  102, 64,  106, 86,\n    57,  18,  23,  220, 82,  103, 152, 120, 238, 70,  16,  3,   206, 44,  140,\n    151, 8,   236, 203, 79,  143, 176, 108, 115, 206, 243, 58,  247, 94,  89,\n    100, 219, 199, 19,  36,  172, 170, 127, 224, 231, 116, 204, 139, 2,   96,\n    124, 224, 192, 227, 46,  155, 179, 150, 139, 72,  120, 68,  167, 2,   1,\n    123, 37,  217, 178, 31,  15,  197, 78,  132, 56,  191, 174, 192, 110, 15,\n    15,  223, 122, 129, 16,  253, 151, 207, 8,   44,  33,  95,  115, 173, 87,\n    122, 102, 195, 22,  144, 250, 151, 72,  205, 37,  192, 133, 37,  231, 77,\n    4,   127, 18,  191, 108, 219, 38,  153, 165, 18,  17,  242, 136, 42,  12,\n    20,  99,  58,  222, 213, 2,   57,  95,  121, 165, 169, 112, 142, 99,  199,\n    242, 47,  73,  214, 37,  186, 97,  6,   239, 104, 160, 124, 66,  4,   58,\n    208, 55,  253, 230, 226, 101, 219, 241, 249, 159, 9,   132, 5,   1,   53,\n    148, 157, 217, 90,  120, 141, 12,  52,  174, 198, 121, 165, 215, 127, 16,\n    39,  243, 8,   229, 20,  198, 251, 144, 173, 121, 164, 54,  213, 223, 241,\n    238, 228, 79,  108, 196, 142, 97,  254, 29,  181, 243, 128, 97,  200, 16,\n    208, 226, 143, 156, 75,  60,  163, 60,  232, 213, 164, 215, 52,  89,  196,\n    215, 161, 14,  3,   235, 10,  122, 238, 86,  88,  23,  88,  209, 54,  250,\n    144, 56,  125, 6,   188, 161, 100, 30,  238, 52,  125, 62,  100, 82,  149,\n    133, 24,  150, 42,  137, 93,  221, 136, 197, 210, 179, 31,  6,   152, 142,\n    221, 22,  254, 51,  206, 49,  149, 189, 173, 47,  241, 85,  211, 255, 32,\n    234, 4,   126, 85,  164, 243, 128, 101, 181, 96,  108, 122, 81,  2,   10,\n    212, 135, 175, 121, 79,  138, 206, 40,  64,  155, 20,  235, 94,  86,  243,\n    147, 147, 203, 229, 215, 168, 71,  19,  58,  230, 234, 15,  250, 215, 169,\n    111, 11,  152, 5,   48,  106, 189, 191, 184, 199, 86,  170, 253, 166, 207,\n    173, 127, 127, 168, 111, 74,  156, 146, 51,  207, 214, 117, 207, 186, 177,\n    166, 31,  170, 5,   29,  147, 123, 83,  75,  149, 28,  218, 93,  122, 160,\n    139, 70,  236, 200, 136, 145, 127, 200, 13,  129, 147, 35,  193, 2,   136,\n    114, 98,  60,  172, 65,  21,  50,  246, 173, 98,  98,  158, 148, 212, 251,\n    209, 62,  58,  109, 242, 105, 131, 196, 204, 127, 48,  240, 198, 99,  158,\n    80,  214, 227, 20,  164, 31,  29,  20,  119, 2,   72,  100, 63,  234, 67,\n    187, 178, 186, 177, 125, 142, 195, 134, 19,  216, 207, 43,  138, 227, 227,\n    235, 157, 230, 210, 106, 251, 184, 158, 101, 1,   9,   120, 214, 189, 8,\n    80,  167, 80,  51,  77,  103, 205, 191, 185, 115, 122, 17,  215, 109, 20,\n    18,  201, 10,  116, 180, 166, 226, 94,  106, 147, 186, 38,  191, 0,   140,\n    196, 22,  57,  176, 123, 174, 231, 244, 166, 12,  14,  246, 114, 163, 176,\n    37,  226, 94,  202, 233, 186, 6,   27,  161, 3,   234, 76,  101, 7,   95,\n    221, 213, 237, 62,  125, 116, 57,  137, 21,  221, 50,  76,  16,  231, 164,\n    108, 248, 59,  105, 14,  150, 207, 132, 92,  121, 246, 186, 205, 69,  117,\n    27,  152, 232, 160, 124, 53,  4,   243, 205, 133, 62,  152, 128, 192, 30,\n    10,  24,  196, 242, 211, 72,  205, 216, 189, 30,  135, 10,  205, 124, 222,\n    99,  125, 30,  69,  50,  94,  223, 55,  80,  175, 155, 184, 36,  93,  159,\n    103, 109, 91,  63,  39,  118, 16,  15,  119, 215, 51,  244, 209, 124, 186,\n    226, 215, 160, 201, 33,  253, 4,   77,  121, 112, 102, 217, 160, 103, 158,\n    18,  206, 146, 205, 13,  84,  40,  104, 38,  33,  15,  169, 141, 133, 172,\n    216, 61,  222, 177, 215, 209, 28,  40,  70,  105, 165, 210, 129, 251, 85,\n    197, 33,  193, 233, 196, 26,  213, 49,  114, 252, 183, 32,  93,  127, 86,\n    205, 44,  124, 115, 181, 22,  34,  75,  24,  56,  207, 56,  31,  101, 226,\n    5,   85,  239, 99,  72,  158, 249, 201, 54,  120, 139, 10,  191, 192, 100,\n    75,  147, 232, 179, 34,  2,   136, 101, 40,  138, 29,  182, 97,  186, 197,\n    223, 218, 49,  75,  242, 224, 91,  30,  91,  23,  107, 218, 97,  34,  121,\n    138, 59,  223, 34,  32,  162, 183, 161, 41,  74,  206, 169, 40,  239, 18,\n    244, 114, 175, 136, 10,  99,  186, 141, 148, 180, 157, 180, 143, 147, 202,\n    137, 169, 22,  80,  225, 156, 8,   106, 26,  196, 128, 82,  12,  248, 47,\n    109, 14,  105, 226, 84,  214, 223, 223, 166, 34,  185, 148, 64,  144, 169,\n    246, 122, 86,  161, 143, 24,  115, 183, 176, 51,  162, 246, 173, 18,  14,\n    29,  42,  29,  181, 53,  112, 21,  106, 186, 126, 217, 174, 60,  153, 139,\n    179, 37,  234, 88,  91,  83,  169, 215, 167, 133, 89,  50,  3,   65,  21,\n    86,  52,  200, 4,   20,  83,  210, 22,  51,  218, 158, 10,  198, 64,  55,\n    98,  173, 173, 84,  9,   103, 197, 171, 191, 232, 112, 32,  116, 32,  49,\n    7,   90,  237, 68,  52,  44,  112, 101, 119, 99,  179, 24,  161, 204, 159,\n    232, 243, 233, 238, 121, 77,  184, 161, 151, 140, 118, 251, 220, 228, 73,\n    239, 97,  201, 129, 202, 11,  26,  44,  249, 168, 181, 15,  5,   21,  187,\n    184, 170, 20,  121, 112, 31,  122, 180, 254, 18,  3,   110, 208, 22,  80,\n    172, 1,   4,   126, 83,  65,  98,  182, 58,  36,  97,  222, 14,  75,  136,\n    63,  180, 219, 22,  99,  110, 11,  217, 115, 119, 197, 14,  125, 21,  70,\n    158, 195, 253, 86,  107, 3,   211, 189, 43,  88,  191, 31,  29,  190, 59,\n    60,  70,  128, 198, 121, 179, 130, 165, 22,  77,  152, 152, 30,  131, 125,\n    192, 222, 100, 193, 85,  232, 9,   70,  198, 169, 62,  80,  218, 15,  59,\n    245, 32,  154, 111, 113, 154, 70,  179, 194, 29,  147, 172, 161, 147, 11,\n    81,  191, 40,  195, 223, 236, 241, 229, 188, 155, 14,  241, 87,  187, 154,\n    32,  144, 214, 17,  94,  156, 31,  134, 128, 205, 95,  220, 107, 172, 210,\n    219, 121, 108, 36,  94,  13,  104, 135, 128, 255, 17,  116, 202, 153, 13,\n    52,  224, 32,  166, 130, 0,   167, 87,  210, 128, 160, 43,  70,  158, 168,\n    232, 241, 69,  139, 98,  66,  16,  173, 158, 112, 54,  246, 242, 146, 3,\n    48,  144, 128, 76,  37,  92,  31,  161, 186, 46,  209, 27,  61,  99,  212,\n    223, 219, 202, 51,  180, 0,   29,  201, 137, 85,  22,  220, 154, 205, 227,\n    91,  81,  153, 193, 162, 165, 29,  14,  253, 145, 26,  11,  212, 183, 247,\n    136, 232, 139, 14,  136, 51,  165, 147, 146, 79,  165, 75,  198, 73,  211,\n    139, 243, 60,  233, 83,  9,   214, 175, 11,  196, 76,  214, 33,  150, 83,\n    80,  58,  134, 93,  11,  242, 196, 21,  53,  206, 30,  107, 218, 42,  248,\n    121, 99,  237, 17,  109, 87,  161, 59,  184, 196, 23,  60,  183, 69,  152,\n    122, 152, 211, 239, 5,   6,   122, 75,  197, 105, 146, 165, 159, 34,  103,\n    214, 44,  102, 49,  157, 17,  22,  26,  66,  0,   73,  206, 22,  136, 238,\n    255, 44,  83,  86,  106, 50,  31,  128, 129, 102, 136, 182, 207, 111, 3,\n    66,  21,  173, 3,   13,  221, 22,  70,  24,  96,  136, 69,  179, 76,  33,\n    184, 21,  55,  168, 223, 238, 65,  36,  92,  180, 153, 64,  48,  119, 49,\n    204, 97,  229, 178, 225, 96,  209, 90,  248, 25,  146, 226, 193, 237, 133,\n    31,  6,   34,  43,  193, 57,  4,   0,   32,  68,  187, 250, 143, 123, 21,\n    41,  16,  17,  101, 156, 60,  54,  82,  61,  135, 17,  40,  254, 190, 56,\n    207, 24,  213, 22,  19,  4,   4,   109, 233, 150, 152, 216, 205, 184, 255,\n    148, 73,  75,  3,   192, 40,  248, 119, 232, 210, 84,  81,  64,  79,  206,\n    46,  244, 157, 145, 106, 14,  116, 47,  114, 154, 252, 142, 232, 214, 247,\n    45,  20,  228, 242, 145, 123, 37,  25,  104, 175, 80,  107, 199, 32,  164,\n    170, 190, 188, 90,  0,   98,  11,  17,  150, 145, 240, 92,  88,  249, 49,\n    107, 50,  9,   91,  237, 241, 33,  46,  109, 229, 95,  67,  216, 14,  26,\n    29,  241, 95,  230, 182, 250, 5,   46,  25,  6,   48,  221, 112, 222, 189,\n    245, 115, 50,  175, 170, 251, 139, 134, 238, 134, 107, 136, 46,  24,  233,\n    46,  190, 253, 236, 166, 255, 193, 136, 235, 190, 113, 47,  143, 177, 161,\n    4,   206, 176, 235, 219, 123, 93,  126, 14,  167, 80,  202, 20,  41,  28,\n    21,  27,  39,  3,   178, 135, 31,  184, 56,  164, 200, 158, 54,  25,  72,\n    81,  68,  147, 76,  210, 209, 193, 77,  36,  46,  121, 181, 107, 211, 198,\n    230, 180, 73,  65,  253, 22,  188, 225, 57,  157, 20,  114, 126, 240, 76,\n    11,  246, 116, 1,   176, 158, 179, 201, 102, 249, 113, 110, 34,  77,  172,\n    35,  99,  67,  55,  115, 121, 176, 83,  151, 219, 88,  102, 62,  140, 133,\n    134, 97,  7,   210, 8,   128, 162, 146, 186, 227, 91,  193, 172, 169, 196,\n    215, 113, 87,  81,  113, 113, 137, 40,  93,  102, 240, 112, 172, 40,  170,\n    61,  121, 98,  133, 5,   194, 57,  64,  41,  161, 217, 5,   165, 173, 213,\n    102, 71,  26,  209, 195, 172, 197, 252, 113, 155, 106, 34,  43,  7,   212,\n    212, 210, 79,  199, 200, 47,  36,  155, 67,  164, 92,  135, 70,  107, 7,\n    182, 198, 213, 226, 3,   218, 1,   209, 48,  66,  24,  136, 75,  255, 130,\n    48,  159, 141, 83,  14,  225, 57,  103, 11,  27,  179, 101, 209, 150, 45,\n    160, 221, 58,  22,  106, 63,  230, 116, 64,  51,  222, 207, 204, 154, 90,\n    89,  242, 58,  163, 17,  80,  221, 65,  111, 26,  15,  183, 200, 17,  194,\n    138, 236, 141, 104, 195, 154, 7,   39,  170, 198, 121, 192, 120, 180, 39,\n    144, 106, 234, 214, 121, 170, 57,  131, 93,  110, 53,  34,  39,  48,  48,\n    111, 114, 140, 36,  35,  58,  242, 21,  59,  147, 41,  65,  255, 233, 11,\n    231, 184, 108, 135, 189, 229, 213, 91,  251, 210, 96,  11,  77,  227, 37,\n    234, 1,   117, 219, 161, 125, 68,  7,   162, 92,  253, 97,  101, 95,  12,\n    221, 70,  206, 107, 199, 125, 5,   111, 191, 72,  182, 236, 119, 185, 20,\n    72,  23,  60,  236, 59,  172, 144, 174, 100, 126, 82,  190, 81,  225, 78,\n    39,  93,  52,  239, 200, 184, 104, 106, 134, 55,  0,   39,  82,  233, 71,\n    189, 79,  65,  17,  99,  20,  39,  82,  149, 246, 121, 63,  67,  196, 171,\n    203, 222, 192, 242, 0,   133, 238, 250, 119, 140, 156, 133, 115, 174, 140,\n    126, 4,   50,  207, 166, 34,  117, 98,  66,  149, 210, 17,  41,  148, 121,\n    11,  71,  227, 232, 123, 229, 138, 75,  173, 5,   201, 113, 195, 85,  39,\n    103, 122, 185, 137, 215, 78,  146, 37,  253, 72,  109, 143, 45,  250, 219,\n    104, 61,  240, 222, 168, 197, 216, 134, 59,  194, 187, 92,  9,   225, 172,\n    192, 243, 200, 242, 83,  17,  0,   14,  201, 19,  207, 135, 154, 142, 165,\n    233, 208, 149, 188, 254, 197, 211, 70,  204, 16,  131, 108, 111, 191, 182,\n    51,  51,  19,  165, 235, 253, 213, 95,  169, 93,  90,  22,  100, 29,  237,\n    110, 213, 93,  88,  199, 171, 230, 225, 98,  235, 80,  4,   100, 100, 153,\n    10,  218, 7,   176, 162, 29,  245, 110, 29,  35,  137, 184, 227, 4,   9,\n    193, 182, 81,  216, 7,   27,  5,   28,  125, 171, 176, 61,  215, 57,  35,\n    204, 73,  71,  85,  174, 120, 156, 147, 53,  189, 179, 40,  47,  174, 52,\n    21,  111, 215, 68,  40,  19,  103, 173, 52,  122, 107, 216, 8,   157, 235,\n    67,  108, 83,  17,  112, 211, 196, 211, 60,  216, 153, 29,  198, 79,  186,\n    66,  213, 38,  110, 6,   97,  101, 175, 250, 138, 94,  241, 223, 154, 15,\n    247, 60,  132, 102, 51,  169, 73,  63,  143, 132, 95,  233, 179, 103, 16,\n    36,  29,  45,  31,  4,   38,  32,  188, 2,   209, 181, 131, 142, 114, 1,\n    123, 73,  53,  25,  235, 220, 223, 26,  129, 151, 71,  38,  184, 251, 59,\n    80,  150, 175, 65,  56,  87,  25,  64,  97,  76,  168, 125, 115, 180, 175,\n    196, 216, 2,   88,  90,  221, 67,  96,  134, 47,  160, 82,  252, 80,  233,\n    9,   107, 123, 234, 58,  131, 240, 254, 20,  246, 233, 107, 136, 157, 250,\n    157, 97,  120, 155, 158, 245, 151, 210, 127, 254, 254, 125, 27,  35,  98,\n    26,  158, 255, 6,   66,  158, 174, 235, 126, 253, 40,  238, 86,  24,  199,\n    86,  91,  9,   100, 187, 60,  125, 50,  34,  249, 87,  220, 118, 16,  53,\n    51,  190, 53,  249, 85,  130, 100, 253, 147, 230, 160, 164, 13,  121, 165,\n    247, 42,  55,  174, 211, 66,  181, 147, 162, 192, 203, 9,   246, 210, 237,\n    156, 248, 102, 97,  199, 193, 87,  66,  204, 242, 30,  57,  88,  232, 46,\n    184, 114, 10,  146, 147, 224, 86,  168, 62,  63,  3,   109, 84,  106, 128,\n    99,  236, 61,  84,  85,  250, 238, 126, 30,  144, 10,  60,  116, 245, 160,\n    108, 5,   135, 37,  9,   92,  176, 164, 208, 188, 186, 246, 79,  173, 132,\n    246, 119, 89,  89,  106, 233, 120, 237, 28,  222, 79,  46,  216, 24,  207,\n    153, 15,  117, 2,   2,   91,  171, 63,  214, 115, 37,  51,  98,  240, 84,\n    190, 229, 41,  52,  168, 244, 17,  1,   210, 195, 118, 227, 174, 125, 239,\n    114, 146, 139, 59,  16,  14};\n\n}  // namespace tachyon::c::zk::plonk::halo2::bn254\n\n#endif  // TACHYON_C_ZK_PLONK_HALO2_TEST_BN254_HALO2_PARAMS_DATA_H_\n"
  },
  {
    "path": "tachyon/c/zk/plonk/halo2/verifier_impl.h",
    "content": "#ifndef TACHYON_C_ZK_PLONK_HALO2_VERIFIER_IMPL_H_\n#define TACHYON_C_ZK_PLONK_HALO2_VERIFIER_IMPL_H_\n\n#include <stdint.h>\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/functional/callback.h\"\n#include \"tachyon/zk/plonk/halo2/verifier.h\"\n\nnamespace tachyon::c::zk::plonk::halo2 {\n\ntemplate <typename PS>\nclass VerifierImpl : public tachyon::zk::plonk::halo2::Verifier<PS> {\n public:\n  using Base = tachyon::zk::plonk::halo2::Verifier<PS>;\n  using Callback = tachyon::base::OnceCallback<Base()>;\n  using PCS = typename PS::PCS;\n  using F = typename PCS::Field;\n  using Commitment = typename PCS::Commitment;\n  using Evals = typename PCS::Evals;\n\n  VerifierImpl(Callback callback, uint8_t transcript_type)\n      : Base(std::move(callback).Run()), transcript_type_(transcript_type) {}\n\n  uint8_t transcript_type() const { return transcript_type_; }\n\n  using Base::VerifyProof;\n\n  [[nodiscard]] bool VerifyProof(\n      const tachyon::zk::plonk::VerifyingKey<F, Commitment>& vkey,\n      std::vector<std::vector<std::vector<F>>>& instance_columns_vec) {\n    std::vector<std::vector<Evals>> cpp_instance_columns_vec =\n        tachyon::base::Map(instance_columns_vec,\n                           [](std::vector<std::vector<F>>& instance_columns) {\n                             return tachyon::base::Map(\n                                 instance_columns,\n                                 [](std::vector<F>& instance_column) {\n                                   return Evals(std::move(instance_column));\n                                 });\n                           });\n    return Base::VerifyProof(vkey, cpp_instance_columns_vec);\n  }\n\n protected:\n  uint8_t transcript_type_;\n};\n\n}  // namespace tachyon::c::zk::plonk::halo2\n\n#endif  // TACHYON_C_ZK_PLONK_HALO2_VERIFIER_IMPL_H_\n"
  },
  {
    "path": "tachyon/c/zk/plonk/halo2/verifier_replay.cc",
    "content": "#include <string>\n\n#include \"tachyon/base/console/iostream.h\"\n#include \"tachyon/base/files/file_path_flag.h\"\n#include \"tachyon/base/files/file_util.h\"\n#include \"tachyon/base/flag/flag_parser.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/c/zk/plonk/halo2/bn254_ps.h\"\n#include \"tachyon/c/zk/plonk/halo2/bn254_verifier.h\"\n#include \"tachyon/c/zk/plonk/keys/proving_key_impl.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluation_domain_factory.h\"\n#include \"tachyon/zk/plonk/halo2/pcs_type.h\"\n#include \"tachyon/zk/plonk/halo2/transcript_type.h\"\n#include \"tachyon/zk/plonk/halo2/vendor.h\"\n#include \"tachyon/zk/plonk/halo2/verifier.h\"\n\nconstexpr uint8_t kProof[] = {\n    // You need to fill here!\n    0,\n};\n\nnamespace tachyon {\nnamespace c::zk::plonk::halo2::bn254 {\n\ntemplate <typename PS>\nusing ProvingKey = plonk::ProvingKeyImpl<PS>;\n\ntemplate <typename PS>\nusing Verifier = tachyon::zk::plonk::halo2::Verifier<PS>;\n\ntemplate <typename PCS>\nstd::vector<std::vector<typename PCS::Evals>> GetInstanceColumnsVec() {\n  // You need to fill here!\n  return {{{}}};\n}\n\ntemplate <typename NativeVerifier>\nbool VerifyProof(NativeVerifier* verifier,\n                 const std::vector<uint8_t>& pk_bytes) {\n  using PS = typename NativeVerifier::PS;\n  using PCS = typename PS::PCS;\n\n  std::cout << \"deserializing proving key\" << std::endl;\n  ProvingKey<PS> pk(pk_bytes, /*read_only_vk=*/true);\n  std::cout << \"done deserializing proving key\" << std::endl;\n\n  uint32_t extended_k = pk.verifying_key().constraint_system().ComputeExtendedK(\n      verifier->pcs().K());\n  verifier->set_extended_domain(\n      PCS::ExtendedDomain::Create(size_t{1} << extended_k));\n#if TACHYON_CUDA\n  verifier->EnableIcicleNTT();\n#endif\n  pk.SetTranscriptRepr(*verifier);\n\n  return verifier->VerifyProof(pk.verifying_key(),\n                               GetInstanceColumnsVec<PCS>());\n}\n\nbool VerifyProof(tachyon_halo2_bn254_verifier* c_verifier,\n                 const std::vector<uint8_t>& pk_bytes) {\n  switch (static_cast<tachyon::zk::plonk::halo2::Vendor>(c_verifier->vendor)) {\n    case tachyon::zk::plonk::halo2::Vendor::kPSE: {\n      switch (static_cast<tachyon::zk::plonk::halo2::PCSType>(\n          c_verifier->pcs_type)) {\n        case tachyon::zk::plonk::halo2::PCSType::kGWC: {\n          return VerifyProof(\n              reinterpret_cast<Verifier<PSEGWC>*>(c_verifier->extra), pk_bytes);\n        }\n        case tachyon::zk::plonk::halo2::PCSType::kSHPlonk: {\n          return VerifyProof(\n              reinterpret_cast<Verifier<PSESHPlonk>*>(c_verifier->extra),\n              pk_bytes);\n        }\n      }\n      break;\n    }\n    case tachyon::zk::plonk::halo2::Vendor::kScroll: {\n      switch (static_cast<tachyon::zk::plonk::halo2::PCSType>(\n          c_verifier->pcs_type)) {\n        case tachyon::zk::plonk::halo2::PCSType::kGWC: {\n          return VerifyProof(\n              reinterpret_cast<Verifier<ScrollGWC>*>(c_verifier->extra),\n              pk_bytes);\n        }\n        case tachyon::zk::plonk::halo2::PCSType::kSHPlonk: {\n          return VerifyProof(\n              reinterpret_cast<Verifier<ScrollSHPlonk>*>(c_verifier->extra),\n              pk_bytes);\n        }\n      }\n      break;\n    }\n  }\n\n  NOTREACHED();\n  return false;\n}\n\n}  // namespace c::zk::plonk::halo2::bn254\n\nint RunMain(int argc, char** argv) {\n  if (base::Environment::Has(\"TACHYON_PCS_PARAMS_PATH\")) {\n    tachyon_cerr << \"If this is set, the pcs params is overwritten\"\n                 << std::endl;\n    return 1;\n  }\n  if (base::Environment::Has(\"TACHYON_PK_LOG_PATH\")) {\n    tachyon_cerr << \"If this is set, the pk log is overwritten\" << std::endl;\n    return 1;\n  }\n\n  zk::plonk::halo2::Vendor vendor;\n  zk::plonk::halo2::PCSType pcs_type;\n  zk::plonk::halo2::TranscriptType transcript_type;\n  uint32_t k;\n  std::string s_hex;\n  base::FilePath pcs_params_path;\n  base::FilePath pk_path;\n  base::FlagParser parser;\n  parser.AddFlag<tachyon::base::Flag<zk::plonk::halo2::Vendor>>(&vendor)\n      .set_long_name(\"--vendor\")\n      .set_required()\n      .set_help(\"Vendor\");\n  parser.AddFlag<tachyon::base::Flag<zk::plonk::halo2::PCSType>>(&pcs_type)\n      .set_long_name(\"--pcs_type\")\n      .set_required()\n      .set_help(\"PCS(Polynomial Commitment Scheme) type\");\n  parser.AddFlag<base::Flag<zk::plonk::halo2::TranscriptType>>(&transcript_type)\n      .set_long_name(\"--transcript_type\")\n      .set_required()\n      .set_help(\"Transcript type\");\n  parser.AddFlag<base::Uint32Flag>(&k)\n      .set_short_name(\"-k\")\n      .set_required()\n      .set_help(\"K\");\n  parser.AddFlag<base::FilePathFlag>(&pcs_params_path)\n      .set_long_name(\"--pcs_params\")\n      .set_required()\n      .set_help(\"The path to pcs params\");\n  parser.AddFlag<base::FilePathFlag>(&pk_path)\n      .set_long_name(\"--pk\")\n      .set_required()\n      .set_help(\"The path to proving key\");\n  {\n    std::string error;\n    if (!parser.Parse(argc, argv, &error)) {\n      tachyon_cerr << error << std::endl;\n      return 1;\n    }\n  }\n\n  std::optional<std::vector<uint8_t>> pk_bytes = base::ReadFileToBytes(pk_path);\n  if (!pk_bytes.has_value()) {\n    tachyon_cerr << \"Failed to read file: \" << pk_path.value() << std::endl;\n    return 1;\n  }\n\n  std::optional<std::vector<uint8_t>> pcs_params_bytes =\n      base::ReadFileToBytes(pcs_params_path);\n  if (!pcs_params_bytes.has_value()) {\n    tachyon_cerr << \"Failed to read file: \" << pcs_params_path.value()\n                 << std::endl;\n    return 1;\n  }\n\n  std::cout << \"creating verifier\" << std::endl;\n  tachyon_halo2_bn254_verifier* verifier =\n      tachyon_halo2_bn254_verifier_create_from_params(\n          static_cast<uint8_t>(vendor), static_cast<uint8_t>(pcs_type),\n          static_cast<uint8_t>(transcript_type), k, pcs_params_bytes->data(),\n          pcs_params_bytes->size(), std::data(kProof), std::size(kProof));\n  std::cout << \"done creating verifier\" << std::endl;\n\n  if (!c::zk::plonk::halo2::bn254::VerifyProof(verifier, pk_bytes.value())) {\n    tachyon_halo2_bn254_verifier_destroy(verifier);\n    tachyon_cerr << \"Failed to verify proof\" << std::endl;\n    return 1;\n  }\n\n  tachyon_halo2_bn254_verifier_destroy(verifier);\n  return 0;\n}\n\n}  // namespace tachyon\n\nint main(int argc, char** argv) { return tachyon::RunMain(argc, argv); }\n"
  },
  {
    "path": "tachyon/c/zk/plonk/keys/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n    name = \"keys_hdrs\",\n    srcs = [\n        \"bn254_plonk_proving_key.h\",\n        \"bn254_plonk_verifying_key.h\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"bn254_keys\",\n    deps = [\":bn254_plonk_proving_key\"],\n)\n\ntachyon_cc_library(\n    name = \"bn254_plonk_proving_key\",\n    srcs = [\"bn254_plonk_proving_key.cc\"],\n    hdrs = [\"bn254_plonk_proving_key.h\"],\n    deps = [\n        \":bn254_plonk_verifying_key\",\n        \":proving_key_impl\",\n        \"//tachyon/c/zk/plonk/halo2:bn254_ps\",\n        \"//tachyon/zk/plonk/halo2:pcs_type\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"bn254_plonk_verifying_key\",\n    srcs = [\"bn254_plonk_verifying_key.cc\"],\n    hdrs = [\n        \"bn254_plonk_verifying_key.h\",\n        \"bn254_plonk_verifying_key_type_traits.h\",\n    ],\n    deps = [\n        \"//tachyon/c/base:type_traits_forward\",\n        \"//tachyon/c/zk/plonk/constraint_system:bn254_constraint_system\",\n        \"//tachyon/cc/math/elliptic_curves/bn/bn254:fr\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:g1\",\n        \"//tachyon/zk/plonk/keys:verifying_key\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"keys\",\n    deps = [\":bn254_keys\"],\n)\n\ntachyon_cc_library(\n    name = \"proving_key_impl\",\n    hdrs = [\"proving_key_impl.h\"],\n    deps = [\n        \"//tachyon/base:environment\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base/buffer\",\n        \"//tachyon/base/files:file_util\",\n        \"//tachyon/c/zk/plonk/halo2:buffer_reader\",\n        \"//tachyon/zk/plonk/halo2:pinned_verifying_key\",\n        \"//tachyon/zk/plonk/halo2:vendor\",\n        \"//tachyon/zk/plonk/keys:proving_key\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"keys_unittests\",\n    srcs = [\n        \"bn254_plonk_proving_key_unittest.cc\",\n        \"bn254_plonk_verifying_key_unittest.cc\",\n    ],\n    deps = [\n        \":bn254_plonk_proving_key\",\n        \"//tachyon/c/zk/plonk/halo2:constants\",\n        \"//tachyon/math/finite_fields/test:finite_field_test\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/c/zk/plonk/keys/bn254_plonk_proving_key.cc",
    "content": "#include \"tachyon/c/zk/plonk/keys/bn254_plonk_proving_key.h\"\n\n#include \"tachyon/c/zk/plonk/halo2/bn254_ps.h\"\n#include \"tachyon/c/zk/plonk/keys/bn254_plonk_verifying_key_type_traits.h\"\n#include \"tachyon/c/zk/plonk/keys/proving_key_impl.h\"\n#include \"tachyon/zk/plonk/halo2/pcs_type.h\"\n\nusing namespace tachyon;\n\nnamespace {\n\nusing PSEGWC = c::zk::plonk::halo2::bn254::PSEGWC;\nusing PSESHPlonk = c::zk::plonk::halo2::bn254::PSESHPlonk;\nusing ScrollGWC = c::zk::plonk::halo2::bn254::ScrollGWC;\nusing ScrollSHPlonk = c::zk::plonk::halo2::bn254::ScrollSHPlonk;\n\ntemplate <typename PS>\nusing PKeyImpl = c::zk::plonk::ProvingKeyImpl<PS>;\n\ntemplate <typename NativePKey>\nvoid Destroy(const NativePKey* pk) {\n  delete pk;\n}\n\ntemplate <typename NativePKey>\nconst tachyon_bn254_plonk_verifying_key* GetVerifyingKey(const NativePKey* pk) {\n  return c::base::c_cast(&pk->verifying_key());\n}\n\n}  // namespace\n\n#define INVOKE(Method, ...)                                                    \\\n  switch (static_cast<zk::plonk::halo2::Vendor>(pk->vendor)) {                 \\\n    case zk::plonk::halo2::Vendor::kPSE: {                                     \\\n      switch (static_cast<zk::plonk::halo2::PCSType>(pk->pcs_type)) {          \\\n        case zk::plonk::halo2::PCSType::kGWC: {                                \\\n          return Method(reinterpret_cast<PKeyImpl<PSEGWC>*>(pk->extra),        \\\n                        ##__VA_ARGS__);                                        \\\n        }                                                                      \\\n        case zk::plonk::halo2::PCSType::kSHPlonk: {                            \\\n          return Method(reinterpret_cast<PKeyImpl<PSESHPlonk>*>(pk->extra),    \\\n                        ##__VA_ARGS__);                                        \\\n        }                                                                      \\\n      }                                                                        \\\n    }                                                                          \\\n    case zk::plonk::halo2::Vendor::kScroll: {                                  \\\n      switch (static_cast<zk::plonk::halo2::PCSType>(pk->pcs_type)) {          \\\n        case zk::plonk::halo2::PCSType::kGWC: {                                \\\n          return Method(reinterpret_cast<PKeyImpl<ScrollGWC>*>(pk->extra),     \\\n                        ##__VA_ARGS__);                                        \\\n        }                                                                      \\\n        case zk::plonk::halo2::PCSType::kSHPlonk: {                            \\\n          return Method(reinterpret_cast<PKeyImpl<ScrollSHPlonk>*>(pk->extra), \\\n                        ##__VA_ARGS__);                                        \\\n        }                                                                      \\\n      }                                                                        \\\n    }                                                                          \\\n  }                                                                            \\\n  NOTREACHED()\n\ntachyon_bn254_plonk_proving_key*\ntachyon_bn254_plonk_proving_key_create_from_state(uint8_t vendor,\n                                                  uint8_t pcs_type,\n                                                  const uint8_t* state,\n                                                  size_t state_len) {\n  tachyon_bn254_plonk_proving_key* pkey = new tachyon_bn254_plonk_proving_key;\n  pkey->vendor = vendor;\n  pkey->pcs_type = pcs_type;\n  switch (static_cast<zk::plonk::halo2::Vendor>(vendor)) {\n    case zk::plonk::halo2::Vendor::kPSE: {\n      switch (static_cast<zk::plonk::halo2::PCSType>(pcs_type)) {\n        case zk::plonk::halo2::PCSType::kGWC: {\n          pkey->extra =\n              new PKeyImpl<PSEGWC>(absl::Span<const uint8_t>(state, state_len),\n                                   /*read_only_vk=*/false);\n          return pkey;\n        }\n        case zk::plonk::halo2::PCSType::kSHPlonk: {\n          pkey->extra = new PKeyImpl<PSESHPlonk>(\n              absl::Span<const uint8_t>(state, state_len),\n              /*read_only_vk=*/false);\n          return pkey;\n        }\n      }\n    }\n    case zk::plonk::halo2::Vendor::kScroll: {\n      switch (static_cast<zk::plonk::halo2::PCSType>(pcs_type)) {\n        case zk::plonk::halo2::PCSType::kGWC: {\n          pkey->extra = new PKeyImpl<ScrollGWC>(\n              absl::Span<const uint8_t>(state, state_len),\n              /*read_only_vk=*/false);\n          return pkey;\n        }\n        case zk::plonk::halo2::PCSType::kSHPlonk: {\n          pkey->extra = new PKeyImpl<ScrollSHPlonk>(\n              absl::Span<const uint8_t>(state, state_len),\n              /*read_only_vk=*/false);\n          return pkey;\n        }\n      }\n    }\n  }\n  NOTREACHED();\n  return nullptr;\n}\n\nvoid tachyon_bn254_plonk_proving_key_destroy(\n    tachyon_bn254_plonk_proving_key* pk) {\n  INVOKE(Destroy);\n}\n\nconst tachyon_bn254_plonk_verifying_key*\ntachyon_bn254_plonk_proving_key_get_verifying_key(\n    const tachyon_bn254_plonk_proving_key* pk) {\n  INVOKE(GetVerifyingKey);\n}\n"
  },
  {
    "path": "tachyon/c/zk/plonk/keys/bn254_plonk_proving_key.h",
    "content": "/**\n * @file bn254_plonk_proving_key.h\n * @brief This header file defines the structure and functions for working with\n * the PLONK proving key for the BN254 curve.\n */\n#ifndef TACHYON_C_ZK_PLONK_KEYS_BN254_PLONK_PROVING_KEY_H_\n#define TACHYON_C_ZK_PLONK_KEYS_BN254_PLONK_PROVING_KEY_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include \"tachyon/c/export.h\"\n#include \"tachyon/c/zk/plonk/keys/bn254_plonk_verifying_key.h\"\n\n/**\n * @struct tachyon_bn254_plonk_proving_key\n * @brief Represents the proving key for a PLONK protocol on the BN254 curve.\n */\nstruct tachyon_bn254_plonk_proving_key {\n  uint8_t vendor;\n  uint8_t pcs_type;\n  void* extra;\n};\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Creates a PLONK scroll versioned proving key for the BN254 curve from\n * a given state.\n *\n * @param vendor Identifier for the vendor.\n * @param pcs_type Identifier for the pcs type.\n * @param state A const pointer to the buffer containing the serialized state of\n * the proving key.\n * @param state_len The length of the state buffer.\n * @return A pointer to the newly created PLONK proving key.\n */\nTACHYON_C_EXPORT tachyon_bn254_plonk_proving_key*\ntachyon_bn254_plonk_proving_key_create_from_state(uint8_t vendor,\n                                                  uint8_t pcs_type,\n                                                  const uint8_t* state,\n                                                  size_t state_len);\n\n/**\n * @brief Destroys a PLONK scroll versioned proving key for the BN254 curve,\n * freeing its resources.\n *\n * @param pk A pointer to the PLONK proving key to destroy.\n */\nTACHYON_C_EXPORT void tachyon_bn254_plonk_proving_key_destroy(\n    tachyon_bn254_plonk_proving_key* pk);\n\n/**\n * @brief Retrieves the corresponding verifying key for a given PLONK scroll\n * versioned proving key.\n *\n * @param pk A const pointer to the PLONK proving key.\n * @return A pointer to the corresponding PLONK verifying key.\n */\nTACHYON_C_EXPORT const tachyon_bn254_plonk_verifying_key*\ntachyon_bn254_plonk_proving_key_get_verifying_key(\n    const tachyon_bn254_plonk_proving_key* pk);\n\n#ifdef __cplusplus\n}  // extern \"C\"\n#endif\n\n#endif  // TACHYON_C_ZK_PLONK_KEYS_BN254_PLONK_PROVING_KEY_H_\n"
  },
  {
    "path": "tachyon/c/zk/plonk/keys/bn254_plonk_proving_key_unittest.cc",
    "content": "#include \"tachyon/c/zk/plonk/keys/bn254_plonk_proving_key.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/c/zk/plonk/halo2/bn254_ps.h\"\n#include \"tachyon/c/zk/plonk/halo2/constants.h\"\n#include \"tachyon/c/zk/plonk/keys/bn254_plonk_verifying_key_type_traits.h\"\n#include \"tachyon/c/zk/plonk/keys/proving_key_impl.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n\nnamespace tachyon::zk::plonk {\n\nnamespace {\n\nclass Bn254PlonkScrollProvingKeyTest\n    : public math::FiniteFieldTest<math::bn254::Fr> {};\n\n}  // namespace\n\nTEST_F(Bn254PlonkScrollProvingKeyTest, GetVerifyingKey) {\n  using PkeyImpl =\n      c::zk::plonk::ProvingKeyImpl<c::zk::plonk::halo2::bn254::PSEGWC>;\n  PkeyImpl cpp_pkey;\n\n  tachyon_bn254_plonk_proving_key pkey;\n  pkey.vendor = TACHYON_HALO2_PSE_VENDOR;\n  pkey.pcs_type = TACHYON_HALO2_GWC_PCS;\n  pkey.extra = &cpp_pkey;\n  EXPECT_EQ(tachyon_bn254_plonk_proving_key_get_verifying_key(&pkey),\n            c::base::c_cast(&cpp_pkey.verifying_key()));\n}\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/c/zk/plonk/keys/bn254_plonk_verifying_key.cc",
    "content": "#include \"tachyon/c/zk/plonk/keys/bn254_plonk_verifying_key.h\"\n\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/fr_type_traits.h\"\n#include \"tachyon/c/zk/plonk/constraint_system/bn254_constraint_system_type_traits.h\"\n#include \"tachyon/c/zk/plonk/keys/bn254_plonk_verifying_key_type_traits.h\"\n\nusing namespace tachyon;\n\nconst tachyon_bn254_plonk_constraint_system*\ntachyon_bn254_plonk_verifying_key_get_constraint_system(\n    const tachyon_bn254_plonk_verifying_key* vk) {\n  return c::base::c_cast(&c::base::native_cast(vk)->constraint_system());\n}\n\ntachyon_bn254_fr tachyon_bn254_plonk_verifying_key_get_transcript_repr(\n    const tachyon_bn254_plonk_verifying_key* vk) {\n  return c::base::c_cast(c::base::native_cast(vk)->transcript_repr());\n}\n"
  },
  {
    "path": "tachyon/c/zk/plonk/keys/bn254_plonk_verifying_key.h",
    "content": "/**\n * @file bn254_plonk_verifying_key.h\n * @brief This header file defines the verifying key structure for PLONK on the\n * BN254 curve.\n *\n * The verifying key is essential for verifying PLONK proofs.\n */\n#ifndef TACHYON_C_ZK_PLONK_KEYS_BN254_PLONK_VERIFYING_KEY_H_\n#define TACHYON_C_ZK_PLONK_KEYS_BN254_PLONK_VERIFYING_KEY_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include \"tachyon/c/export.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/fr.h\"\n#include \"tachyon/c/zk/plonk/constraint_system/bn254_constraint_system.h\"\n\n/**\n * @struct tachyon_bn254_plonk_verifying_key\n * @brief Represents the verifying key for PLONK protocol on the BN254 curve.\n *\n * Encapsulates data necessary for verifying PLONK proofs, including references\n * to the constraint system used in the proof and parameters for the evaluation\n * domain.\n */\nstruct tachyon_bn254_plonk_verifying_key {};\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Gets the constraint system associated with the verifying key.\n *\n * @param vk A const pointer to the verifying key.\n * @return A pointer to the constraint system used by the verifying key.\n */\nTACHYON_C_EXPORT const tachyon_bn254_plonk_constraint_system*\ntachyon_bn254_plonk_verifying_key_get_constraint_system(\n    const tachyon_bn254_plonk_verifying_key* vk);\n\n/**\n * @brief Gets the transcript representation used in the verifying key.\n *\n * @param vk A const pointer to the verifying key.\n * @return The transcript representation as a field element.\n */\nTACHYON_C_EXPORT tachyon_bn254_fr\ntachyon_bn254_plonk_verifying_key_get_transcript_repr(\n    const tachyon_bn254_plonk_verifying_key* vk);\n\n#ifdef __cplusplus\n}  // extern \"C\"\n#endif\n\n#endif  // TACHYON_C_ZK_PLONK_KEYS_BN254_PLONK_VERIFYING_KEY_H_\n"
  },
  {
    "path": "tachyon/c/zk/plonk/keys/bn254_plonk_verifying_key_type_traits.h",
    "content": "#ifndef TACHYON_C_ZK_PLONK_KEYS_BN254_PLONK_VERIFYING_KEY_TYPE_TRAITS_H_\n#define TACHYON_C_ZK_PLONK_KEYS_BN254_PLONK_VERIFYING_KEY_TYPE_TRAITS_H_\n\n#include \"tachyon/c/base/type_traits_forward.h\"\n#include \"tachyon/c/zk/plonk/keys/bn254_plonk_verifying_key.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/g1.h\"\n#include \"tachyon/zk/plonk/keys/verifying_key.h\"\n\nnamespace tachyon::c::base {\n\ntemplate <>\nstruct TypeTraits<tachyon::zk::plonk::VerifyingKey<\n    tachyon::math::bn254::Fr, tachyon::math::bn254::G1AffinePoint>> {\n  using CType = tachyon_bn254_plonk_verifying_key;\n};\n\ntemplate <>\nstruct TypeTraits<tachyon_bn254_plonk_verifying_key> {\n  using NativeType =\n      tachyon::zk::plonk::VerifyingKey<tachyon::math::bn254::Fr,\n                                       tachyon::math::bn254::G1AffinePoint>;\n};\n\n}  // namespace tachyon::c::base\n\n#endif  // TACHYON_C_ZK_PLONK_KEYS_BN254_PLONK_VERIFYING_KEY_TYPE_TRAITS_H_\n"
  },
  {
    "path": "tachyon/c/zk/plonk/keys/bn254_plonk_verifying_key_unittest.cc",
    "content": "#include \"tachyon/c/zk/plonk/keys/bn254_plonk_verifying_key.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/fr_type_traits.h\"\n#include \"tachyon/c/zk/plonk/keys/bn254_plonk_verifying_key_type_traits.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n\nnamespace tachyon::zk::plonk {\n\nnamespace {\n\nclass Bn254PlonkVerifyingKeyTest\n    : public math::FiniteFieldTest<math::bn254::Fr> {};\n\n}  // namespace\n\nTEST_F(Bn254PlonkVerifyingKeyTest, GetTranscriptRepr) {\n  VerifyingKey<math::bn254::Fr, math::bn254::G1AffinePoint> cpp_vkey;\n  math::bn254::Fr cpp_transcript_repr = math::bn254::Fr::Random();\n  cpp_vkey.SetTranscriptReprForTesting(cpp_transcript_repr);\n\n  tachyon_bn254_plonk_verifying_key* vkey = c::base::c_cast(&cpp_vkey);\n  tachyon_bn254_fr transcript_repr =\n      tachyon_bn254_plonk_verifying_key_get_transcript_repr(vkey);\n  EXPECT_EQ(c::base::native_cast(transcript_repr), cpp_transcript_repr);\n}\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/c/zk/plonk/keys/proving_key_impl.h",
    "content": "#ifndef TACHYON_C_ZK_PLONK_KEYS_PROVING_KEY_IMPL_H_\n#define TACHYON_C_ZK_PLONK_KEYS_PROVING_KEY_IMPL_H_\n\n#include <stdint.h>\n\n#include <utility>\n#include <vector>\n\n#include \"absl/types/span.h\"\n\n#include \"tachyon/base/buffer/read_only_buffer.h\"\n#include \"tachyon/base/environment.h\"\n#include \"tachyon/base/files/file_util.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/c/zk/plonk/halo2/buffer_reader.h\"\n#include \"tachyon/zk/plonk/halo2/pinned_verifying_key.h\"\n#include \"tachyon/zk/plonk/halo2/vendor.h\"\n#include \"tachyon/zk/plonk/keys/proving_key.h\"\n\nnamespace tachyon::c::zk::plonk {\n\ntemplate <typename PS>\nclass ProvingKeyImpl : public tachyon::zk::plonk::ProvingKey<PS> {\n public:\n  constexpr static tachyon::zk::plonk::halo2::Vendor kVendor = PS::kVendor;\n  constexpr static tachyon::zk::lookup::Type kLookupType = PS::kLookupType;\n\n  using PCS = typename PS::PCS;\n  using F = typename PCS::Field;\n  using C = typename PCS::Commitment;\n\n  ProvingKeyImpl() {\n    this->verifying_key_.constraint_system_.set_lookup_type(kLookupType);\n  }\n  ProvingKeyImpl(absl::Span<const uint8_t> state, bool read_only_vk)\n      : read_only_vk_(read_only_vk) {\n    this->verifying_key_.constraint_system_.set_lookup_type(kLookupType);\n\n    std::string_view pk_str;\n    if (tachyon::base::Environment::Get(\"TACHYON_PK_LOG_PATH\", &pk_str)) {\n      VLOG(1) << \"Save pk to: \" << pk_str;\n      CHECK(tachyon::base::WriteLargeFile(tachyon::base::FilePath(pk_str),\n                                          state));\n    }\n\n    tachyon::base::ReadOnlyBuffer buffer(state.data(), state.size());\n\n    ReadProvingKey(buffer);\n  }\n\n  const tachyon::zk::plonk::ConstraintSystem<F>& GetConstraintSystem() const {\n    return this->verifying_key_.constraint_system_;\n  }\n\n  void SetTranscriptRepr(const tachyon::zk::Entity<PCS>& entity) {\n    this->verifying_key_.SetTranscriptRepresentative(&entity);\n  }\n\n  const F& GetTranscriptRepr(const tachyon::zk::Entity<PCS>& entity) {\n    return this->verifying_key_.transcript_repr_;\n  }\n\n private:\n  void ReadProvingKey(const tachyon::base::ReadOnlyBuffer& buffer) {\n    ReadVerifyingKey(buffer, this->verifying_key_);\n    if (read_only_vk_) return;\n    ReadBuffer(buffer, this->l_first_);\n    ReadBuffer(buffer, this->l_last_);\n    ReadBuffer(buffer, this->l_active_row_);\n    ReadBuffer(buffer, this->fixed_columns_);\n    ReadBuffer(buffer, this->fixed_polys_);\n    ReadBuffer(buffer, this->permutation_proving_key_.permutations_);\n    ReadBuffer(buffer, this->permutation_proving_key_.polys_);\n    if constexpr (kVendor == tachyon::zk::plonk::halo2::Vendor::kPSE) {\n      ReadBuffer(buffer, this->permutation_proving_key_.cosets_);\n    }\n    this->vanishing_argument_ =\n        tachyon::zk::plonk::VanishingArgument<PS>::Create(\n            this->verifying_key_.constraint_system_);\n    CHECK(buffer.Done());\n  }\n\n  static void ReadVerifyingKey(const tachyon::base::ReadOnlyBuffer& buffer,\n                               tachyon::zk::plonk::VerifyingKey<F, C>& vkey) {\n    // NOTE(chokobole): For k\n    ReadU32AsSizeT(buffer);\n    ReadBuffer(buffer, vkey.fixed_commitments_);\n    ReadConstraintSystem(buffer, vkey.constraint_system_);\n    size_t num_commitments =\n        vkey.constraint_system_.permutation().columns().size();\n    std::vector<C> commitments;\n    commitments.resize(num_commitments);\n    for (size_t i = 0; i < num_commitments; ++i) {\n      ReadBuffer(buffer, commitments[i]);\n    }\n    vkey.permutation_verifying_key_ =\n        tachyon::zk::plonk::PermutationVerifyingKey<C>(std::move(commitments));\n  }\n\n  static void ReadConstraintSystem(\n      const tachyon::base::ReadOnlyBuffer& buffer,\n      tachyon::zk::plonk::ConstraintSystem<F>& cs) {\n    cs.num_fixed_columns_ = ReadU32AsSizeT(buffer);\n    cs.num_advice_columns_ = ReadU32AsSizeT(buffer);\n    cs.num_instance_columns_ = ReadU32AsSizeT(buffer);\n    cs.num_simple_selectors_ = ReadU32AsSizeT(buffer);\n    cs.num_complex_selectors_ =\n        ReadU32AsSizeT(buffer) - cs.num_simple_selectors_;\n    cs.num_challenges_ = ReadU32AsSizeT(buffer);\n    ReadBuffer(buffer, cs.advice_column_phases_);\n    ReadBuffer(buffer, cs.challenge_phases_);\n    ReadBuffer(buffer, cs.selector_map_);\n    ReadBuffer(buffer, cs.gates_);\n    ReadBuffer(buffer, cs.advice_queries_);\n    cs.num_advice_queries_ = tachyon::base::CreateVector(\n        ReadU32AsSizeT(buffer),\n        [&buffer]() { return BufferReader<uint32_t>::Read(buffer); });\n    ReadBuffer(buffer, cs.instance_queries_);\n    ReadBuffer(buffer, cs.fixed_queries_);\n    ReadBuffer(buffer, cs.permutation_);\n    ReadBuffer(buffer, cs.lookups_map_);\n    ReadBuffer(buffer, cs.lookups_);\n    ReadBuffer(buffer, cs.shuffles_);\n    ReadBuffer(buffer, cs.constants_);\n    ReadBuffer(buffer, cs.minimum_degree_);\n  }\n\n private:\n  bool read_only_vk_ = false;\n};\n\n}  // namespace tachyon::c::zk::plonk\n\n#endif  // TACHYON_C_ZK_PLONK_KEYS_PROVING_KEY_IMPL_H_\n"
  },
  {
    "path": "tachyon/cc/BUILD.bazel",
    "content": "load(\"//bazel:tachyon.bzl\", \"if_cc_shared_object\")\nload(\n    \"//bazel:tachyon_cc.bzl\",\n    \"collect_hdrs\",\n    \"tachyon_cc_library\",\n    \"tachyon_cc_shared_library\",\n    \"tachyon_cc_unittest\",\n)\nload(\"//tachyon/build:version.bzl\", \"write_version_header\")\nload(\n    \":version.bzl\",\n    \"VERSION\",\n    \"VERSION_MAJOR\",\n    \"VERSION_MINOR\",\n    \"VERSION_PATCH\",\n    \"VERSION_PRERELEASE\",\n)\n\npackage(default_visibility = [\"//visibility:public\"])\n\nwrite_version_header(\n    name = \"version_generated\",\n    lastchange = \"//tachyon/build:lastchange\",\n    major = VERSION_MAJOR,\n    minor = VERSION_MINOR,\n    output = \"version_generated.h\",\n    patch = VERSION_PATCH,\n    prerelease = VERSION_PRERELEASE,\n    project = \"TACHYON_CC\",\n)\n\ntachyon_cc_library(\n    name = \"export\",\n    hdrs = [\"export.h\"],\n)\n\ntachyon_cc_library(\n    name = \"version\",\n    srcs = [\"version.cc\"],\n    hdrs = [\n        \"version.h\",\n        \":version_generated\",\n    ],\n    deps = [\":export\"],\n)\n\ntachyon_cc_unittest(\n    name = \"cc_unittests\",\n    srcs = [\n        \"version_unittest.cc\",\n    ],\n    deps = [\n        \":version\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n\ntachyon_cc_shared_library(\n    name = \"tachyon\",\n    linkstatic = True,\n    soversion = VERSION,\n    tags = [\"manual\"],\n    deps = if_cc_shared_object([\n        \"//tachyon/cc/math/elliptic_curves/bls12/bls12_381:g1\",\n        \"//tachyon/cc/math/elliptic_curves/bn/bn254:g1\",\n    ]),\n)\n\ncollect_hdrs(\n    name = \"tachyon_hdrs\",\n    hdrs = [\n        \"api.h\",\n        \"export.h\",\n        \"version.h\",\n        \":version_generated\",\n    ],\n    deps = [\n        \"//tachyon/cc/math:math_hdrs\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/cc/api.h",
    "content": "#ifndef TACHYON_CC_API_H_\n#define TACHYON_CC_API_H_\n\n#include \"tachyon/cc/math/elliptic_curves/bls12/bls12_381/fq.h\"\n#include \"tachyon/cc/math/elliptic_curves/bls12/bls12_381/fr.h\"\n#include \"tachyon/cc/math/elliptic_curves/bls12/bls12_381/g1.h\"\n#include \"tachyon/cc/math/elliptic_curves/bn/bn254/fq.h\"\n#include \"tachyon/cc/math/elliptic_curves/bn/bn254/fr.h\"\n#include \"tachyon/cc/math/elliptic_curves/bn/bn254/g1.h\"\n\n#endif  // TACHYON_CC_API_H_\n"
  },
  {
    "path": "tachyon/cc/export.h",
    "content": "#ifndef TACHYON_CC_EXPORT_H_\n#define TACHYON_CC_EXPORT_H_\n\n#if defined(TACHYON_CC_SHARED_LIB_BUILD)\n\n#if defined(_WIN32)\n#ifdef TACHYON_COMPILE_LIBRARY\n#define TACHYON_CC_EXPORT __declspec(dllexport)\n#else\n#define TACHYON_CC_EXPORT __declspec(dllimport)\n#endif  // defined(TACHYON_COMPILE_LIBRARY)\n\n#else\n#ifdef TACHYON_COMPILE_LIBRARY\n#define TACHYON_CC_EXPORT __attribute__((visibility(\"default\")))\n#else\n#define TACHYON_CC_EXPORT\n#endif  // defined(TACHYON_COMPILE_LIBRARY)\n#endif  // defined(_WIN32)\n\n#else\n#define TACHYON_CC_EXPORT\n#endif  // defined(TACHYON_CC_SHARED_LIB_BUILD)\n\n#endif  // TACHYON_CC_EXPORT_H_\n"
  },
  {
    "path": "tachyon/cc/math/BUILD.bazel",
    "content": "package(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n    name = \"math_hdrs\",\n    srcs = [\n        \"//tachyon/cc/math/elliptic_curves/bls12/bls12_381:bls12_381_hdrs\",\n        \"//tachyon/cc/math/elliptic_curves/bn/bn254:bn254_hdrs\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/cc/math/elliptic_curves/bls12/bls12_381/BUILD.bazel",
    "content": "load(\"//tachyon/cc/math/elliptic_curves/generator:build_defs.bzl\", \"generate_ec_points\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n    name = \"bls12_381_hdrs\",\n    srcs = [\n        \"fq.h\",\n        \"fr.h\",\n        \"g1.h\",\n    ],\n)\n\ngenerate_ec_points(\n    name = \"bls12_381\",\n    fq_limb_nums = 6,\n    fr_limb_nums = 4,\n    g1_deps = [\n        \"//tachyon/c/math/elliptic_curves/bls12/bls12_381:g1\",\n        \"//tachyon/math/elliptic_curves/bls12/bls12_381:g1\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/cc/math/elliptic_curves/bn/bn254/BUILD.bazel",
    "content": "load(\"//tachyon/cc/math/elliptic_curves/generator:build_defs.bzl\", \"generate_ec_points\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n    name = \"bn254_hdrs\",\n    srcs = [\n        \"fq.h\",\n        \"fr.h\",\n        \"g1.h\",\n    ],\n)\n\ngenerate_ec_points(\n    name = \"bn254\",\n    fq_limb_nums = 4,\n    fr_limb_nums = 4,\n    g1_deps = [\n        \"//tachyon/c/math/elliptic_curves/bn/bn254:g1\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:g1\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/cc/math/elliptic_curves/generator/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_binary\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_binary(\n    name = \"generator\",\n    srcs = [\"generator.cc\"],\n    data = [\n        \"g1.cc.tpl\",\n        \"g1.h.tpl\",\n        \"prime_field.cc.tpl\",\n        \"prime_field.h.tpl\",\n    ],\n    deps = [\n        \"//tachyon/base/console\",\n        \"//tachyon/base/files:file_path_flag\",\n        \"//tachyon/base/flag:flag_parser\",\n        \"//tachyon/base/strings:string_number_conversions\",\n        \"//tachyon/build:cc_writer\",\n        \"//tachyon/c/math/elliptic_curves/generator:generator_util\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/cc/math/elliptic_curves/generator/build_defs.bzl",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\ndef _generate_ec_point_impl(ctx):\n    prime_field_hdr_tpl_path = ctx.expand_location(\"$(location @kroma_network_tachyon//tachyon/cc/math/elliptic_curves/generator:prime_field.h.tpl)\", [ctx.attr.prime_field_hdr_tpl_path])\n    prime_field_src_tpl_path = ctx.expand_location(\"$(location @kroma_network_tachyon//tachyon/cc/math/elliptic_curves/generator:prime_field.cc.tpl)\", [ctx.attr.prime_field_src_tpl_path])\n    g1_hdr_tpl_path = ctx.expand_location(\"$(location @kroma_network_tachyon//tachyon/cc/math/elliptic_curves/generator:g1.h.tpl)\", [ctx.attr.g1_hdr_tpl_path])\n    g1_src_tpl_path = ctx.expand_location(\"$(location @kroma_network_tachyon//tachyon/cc/math/elliptic_curves/generator:g1.cc.tpl)\", [ctx.attr.g1_src_tpl_path])\n\n    arguments = [\n        \"--out=%s\" % (ctx.outputs.out.path),\n        \"--type=%s\" % (ctx.attr.type),\n        \"--fq_limb_nums=%s\" % (ctx.attr.fq_limb_nums),\n        \"--fr_limb_nums=%s\" % (ctx.attr.fr_limb_nums),\n        \"--prime_field_hdr_tpl_path=%s\" % (prime_field_hdr_tpl_path),\n        \"--prime_field_src_tpl_path=%s\" % (prime_field_src_tpl_path),\n        \"--g1_hdr_tpl_path=%s\" % (g1_hdr_tpl_path),\n        \"--g1_src_tpl_path=%s\" % (g1_src_tpl_path),\n    ]\n\n    ctx.actions.run(\n        inputs = [\n            ctx.files.prime_field_hdr_tpl_path[0],\n            ctx.files.prime_field_src_tpl_path[0],\n            ctx.files.g1_hdr_tpl_path[0],\n            ctx.files.g1_src_tpl_path[0],\n        ],\n        tools = [ctx.executable._tool],\n        executable = ctx.executable._tool,\n        outputs = [ctx.outputs.out],\n        arguments = arguments,\n    )\n\n    return [DefaultInfo(files = depset([ctx.outputs.out]))]\n\ngenerate_ec_point = rule(\n    implementation = _generate_ec_point_impl,\n    attrs = {\n        \"out\": attr.output(mandatory = True),\n        \"type\": attr.string(mandatory = True),\n        \"fq_limb_nums\": attr.int(mandatory = True),\n        \"fr_limb_nums\": attr.int(mandatory = True),\n        \"prime_field_hdr_tpl_path\": attr.label(\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/cc/math/elliptic_curves/generator:prime_field.h.tpl\"),\n        ),\n        \"prime_field_src_tpl_path\": attr.label(\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/cc/math/elliptic_curves/generator:prime_field.cc.tpl\"),\n        ),\n        \"g1_hdr_tpl_path\": attr.label(\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/cc/math/elliptic_curves/generator:g1.h.tpl\"),\n        ),\n        \"g1_src_tpl_path\": attr.label(\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/cc/math/elliptic_curves/generator:g1.cc.tpl\"),\n        ),\n        \"_tool\": attr.label(\n            # TODO(chokobole): Change to \"exec\", so we can build on macos.\n            cfg = \"target\",\n            executable = True,\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/cc/math/elliptic_curves/generator\"),\n        ),\n    },\n)\n\ndef generate_ec_points(\n        name,\n        fq_limb_nums,\n        fr_limb_nums,\n        g1_deps):\n    for n in [\n        (\"gen_fq_hdr\", \"fq.h\"),\n        (\"gen_fq_src\", \"fq.cc\"),\n        (\"gen_fr_hdr\", \"fr.h\"),\n        (\"gen_fr_src\", \"fr.cc\"),\n        (\"gen_g1_hdr\", \"g1.h\"),\n        (\"gen_g1_src\", \"g1.cc\"),\n    ]:\n        generate_ec_point(\n            type = name,\n            fq_limb_nums = fq_limb_nums,\n            fr_limb_nums = fr_limb_nums,\n            name = n[0],\n            out = n[1],\n        )\n\n    tachyon_cc_library(\n        name = \"fq\",\n        hdrs = [\"fq.h\"],\n        srcs = [\"fq.cc\"],\n        deps = g1_deps + [\"//tachyon/cc:export\"],\n    )\n\n    tachyon_cc_library(\n        name = \"fr\",\n        hdrs = [\"fr.h\"],\n        srcs = [\"fr.cc\"],\n        deps = g1_deps + [\"//tachyon/cc:export\"],\n    )\n\n    tachyon_cc_library(\n        name = \"g1\",\n        hdrs = [\"g1.h\"],\n        srcs = [\"g1.cc\"],\n        deps = g1_deps + [\n            \":fq\",\n            \":fr\",\n        ],\n    )\n"
  },
  {
    "path": "tachyon/cc/math/elliptic_curves/generator/g1.cc.tpl",
    "content": "// clang-format off\n#include \"tachyon/c/math/elliptic_curves/%{header_dir_name}/fq_type_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/%{header_dir_name}/g1_point_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/%{header_dir_name}/g1_point_type_traits.h\"\n\nnamespace tachyon::cc::math::%{type} {\n\nstd::string G1AffinePoint::ToString() const {\n  return c::base::native_cast(ToCPoint()).ToString();\n}\n\nstd::ostream& operator<<(std::ostream& os, const G1AffinePoint& value) {\n  return os << value.ToString();\n}\n\nstd::string G1ProjectivePoint::ToString() const {\n  return c::base::native_cast(ToCPoint()).ToString();\n}\n\nstd::ostream& operator<<(std::ostream& os, const G1ProjectivePoint& value) {\n  return os << value.ToString();\n}\n\nstd::string G1JacobianPoint::ToString() const {\n  return c::base::native_cast(ToCPoint()).ToString();\n}\n\nstd::ostream& operator<<(std::ostream& os, const G1JacobianPoint& value) {\n  return os << value.ToString();\n}\n\nstd::string G1PointXYZZ::ToString() const {\n  return c::base::native_cast(ToCPoint()).ToString();\n}\n\nstd::ostream& operator<<(std::ostream& os, const G1PointXYZZ& value) {\n  return os << value.ToString();\n}\n\nstd::string G1Point2::ToString() const {\n  return c::base::native_cast(ToCPoint()).ToString();\n}\n\nstd::ostream& operator<<(std::ostream& os, const G1Point2& value) {\n  return os << value.ToString();\n}\n\nstd::string G1Point3::ToString() const {\n  return c::base::native_cast(ToCPoint()).ToString();\n}\n\nstd::ostream& operator<<(std::ostream& os, const G1Point3& value) {\n  return os << value.ToString();\n}\n\nstd::string G1Point4::ToString() const {\n  return c::base::native_cast(ToCPoint()).ToString();\n}\n\nstd::ostream& operator<<(std::ostream& os, const G1Point4& value) {\n  return os << value.ToString();\n}\n\n} // namespace tachyon::cc::math::%{type}\n// clang-format on\n"
  },
  {
    "path": "tachyon/cc/math/elliptic_curves/generator/g1.h.tpl",
    "content": "// clang-format off\n#include <string.h>\n\n#include <ostream>\n\n#include \"tachyon/c/math/elliptic_curves/%{header_dir_name}/g1.h\"\n#include \"tachyon/cc/export.h\"\n#include \"tachyon/cc/math/elliptic_curves/%{header_dir_name}/fq.h\"\n\nnamespace tachyon::cc::math::%{type} {\n\nclass TACHYON_CC_EXPORT G1ProjectivePoint {\n public:\n  G1ProjectivePoint() = default;\n  explicit G1ProjectivePoint(const tachyon_%{type}_g1_projective& point)\n      : G1ProjectivePoint(point.x, point.y, point.z) {}\n  G1ProjectivePoint(const tachyon_%{type}_fq& x, const tachyon_%{type}_fq& y, const tachyon_%{type}_fq& z) {\n    memcpy(x_.value().limbs, x.limbs, sizeof(uint64_t) * %{fq_limb_nums});\n    memcpy(y_.value().limbs, y.limbs, sizeof(uint64_t) * %{fq_limb_nums});\n    memcpy(z_.value().limbs, z.limbs, sizeof(uint64_t) * %{fq_limb_nums});\n  }\n  G1ProjectivePoint(const Fq& x, const Fq& y, const Fq& z)\n      : x_(x), y_(y), z_(z) {}\n\n  const Fq& x() const { return x_; }\n  Fq& x() { return x_; }\n\n  const Fq& y() const { return y_; }\n  Fq& y() { return y_; }\n\n  const Fq& z() const { return z_; }\n  Fq& z() { return z_; }\n\n  static G1ProjectivePoint Zero() {\n    return G1ProjectivePoint(tachyon_%{type}_g1_projective_zero());\n  }\n\n  static G1ProjectivePoint Generator() {\n    return G1ProjectivePoint(tachyon_%{type}_g1_projective_generator());\n  }\n\n  static G1ProjectivePoint Random() {\n    return G1ProjectivePoint(tachyon_%{type}_g1_projective_random());\n  }\n\n  G1ProjectivePoint operator+(const G1ProjectivePoint& other) const {\n    auto a = ToCPoint();\n    auto b = other.ToCPoint();\n    return G1ProjectivePoint(tachyon_%{type}_g1_projective_add(&a, &b));\n  }\n\n  G1ProjectivePoint& operator+=(const G1ProjectivePoint& other) {\n    auto a = ToCPoint();\n    auto b = other.ToCPoint();\n    *this = G1ProjectivePoint(tachyon_%{type}_g1_projective_add(&a, &b));\n    return *this;\n  }\n\n  G1ProjectivePoint operator-(const G1ProjectivePoint& other) const {\n    auto a = ToCPoint();\n    auto b = other.ToCPoint();\n    return G1ProjectivePoint(tachyon_%{type}_g1_projective_sub(&a, &b));\n  }\n\n  G1ProjectivePoint& operator-=(const G1ProjectivePoint& other) {\n    auto a = ToCPoint();\n    auto b = other.ToCPoint();\n    *this = G1ProjectivePoint(tachyon_%{type}_g1_projective_sub(&a, &b));\n    return *this;\n  }\n\n  G1ProjectivePoint operator-() const {\n    return {x_, -y_, z_};\n  }\n\n  G1ProjectivePoint Double() const {\n    auto a = ToCPoint();\n    return G1ProjectivePoint(tachyon_%{type}_g1_projective_dbl(&a));\n  }\n\n  bool operator==(const G1ProjectivePoint& other) const {\n    auto a = ToCPoint();\n    auto b = other.ToCPoint();\n    return tachyon_%{type}_g1_projective_eq(&a, &b);\n  }\n\n  bool operator!=(const G1ProjectivePoint& other) const {\n    auto a = ToCPoint();\n    auto b = other.ToCPoint();\n    return tachyon_%{type}_g1_projective_ne(&a, &b);\n  }\n\n  tachyon_%{type}_g1_projective ToCPoint() const {\n    tachyon_%{type}_g1_projective ret;\n    memcpy(ret.x.limbs, x_.value().limbs, sizeof(uint64_t) * %{fq_limb_nums});\n    memcpy(ret.y.limbs, y_.value().limbs, sizeof(uint64_t) * %{fq_limb_nums});\n    memcpy(ret.z.limbs, z_.value().limbs, sizeof(uint64_t) * %{fq_limb_nums});\n    return ret;\n  }\n\n  std::string ToString() const;\n\n private:\n  Fq x_;\n  Fq y_;\n  Fq z_;\n};\n\nTACHYON_CC_EXPORT std::ostream& operator<<(std::ostream& os, const G1ProjectivePoint& value);\n\nclass TACHYON_CC_EXPORT G1JacobianPoint {\n public:\n  G1JacobianPoint() = default;\n  explicit G1JacobianPoint(const tachyon_%{type}_g1_jacobian& point)\n      : G1JacobianPoint(point.x, point.y, point.z) {}\n  G1JacobianPoint(const tachyon_%{type}_fq& x, const tachyon_%{type}_fq& y, const tachyon_%{type}_fq& z) {\n    memcpy(x_.value().limbs, x.limbs, sizeof(uint64_t) * %{fq_limb_nums});\n    memcpy(y_.value().limbs, y.limbs, sizeof(uint64_t) * %{fq_limb_nums});\n    memcpy(z_.value().limbs, z.limbs, sizeof(uint64_t) * %{fq_limb_nums});\n  }\n  G1JacobianPoint(const Fq& x, const Fq& y, const Fq& z)\n      : x_(x), y_(y), z_(z) {}\n\n  const Fq& x() const { return x_; }\n  Fq& x() { return x_; }\n\n  const Fq& y() const { return y_; }\n  Fq& y() { return y_; }\n\n  const Fq& z() const { return z_; }\n  Fq& z() { return z_; }\n\n  static G1JacobianPoint Zero() {\n    return G1JacobianPoint(tachyon_%{type}_g1_jacobian_zero());\n  }\n\n  static G1JacobianPoint One() { return Generator(); }\n\n  static G1JacobianPoint Generator() {\n    return G1JacobianPoint(tachyon_%{type}_g1_jacobian_generator());\n  }\n\n  static G1JacobianPoint Random() {\n    return G1JacobianPoint(tachyon_%{type}_g1_jacobian_random());\n  }\n\n  G1JacobianPoint operator+(const G1JacobianPoint& other) const {\n    auto a = ToCPoint();\n    auto b = other.ToCPoint();\n    return G1JacobianPoint(tachyon_%{type}_g1_jacobian_add(&a, &b));\n  }\n\n  G1JacobianPoint& operator+=(const G1JacobianPoint& other) {\n    auto a = ToCPoint();\n    auto b = other.ToCPoint();\n    *this = G1JacobianPoint(tachyon_%{type}_g1_jacobian_add(&a, &b));\n    return *this;\n  }\n\n  G1JacobianPoint operator-(const G1JacobianPoint& other) const {\n    auto a = ToCPoint();\n    auto b = other.ToCPoint();\n    return G1JacobianPoint(tachyon_%{type}_g1_jacobian_sub(&a, &b));\n  }\n\n  G1JacobianPoint& operator-=(const G1JacobianPoint& other) {\n    auto a = ToCPoint();\n    auto b = other.ToCPoint();\n    *this = G1JacobianPoint(tachyon_%{type}_g1_jacobian_sub(&a, &b));\n    return *this;\n  }\n\n  G1JacobianPoint operator-() const {\n    return {x_, -y_, z_};\n  }\n\n  G1JacobianPoint Double() const {\n    auto a = ToCPoint();\n    return G1JacobianPoint(tachyon_%{type}_g1_jacobian_dbl(&a));\n  }\n\n  bool operator==(const G1JacobianPoint& other) const {\n    auto a = ToCPoint();\n    auto b = other.ToCPoint();\n    return tachyon_%{type}_g1_jacobian_eq(&a, &b);\n  }\n\n  bool operator!=(const G1JacobianPoint& other) const {\n    auto a = ToCPoint();\n    auto b = other.ToCPoint();\n    return tachyon_%{type}_g1_jacobian_ne(&a, &b);\n  }\n\n  tachyon_%{type}_g1_jacobian ToCPoint() const {\n    tachyon_%{type}_g1_jacobian ret;\n    memcpy(ret.x.limbs, x_.value().limbs, sizeof(uint64_t) * %{fq_limb_nums});\n    memcpy(ret.y.limbs, y_.value().limbs, sizeof(uint64_t) * %{fq_limb_nums});\n    memcpy(ret.z.limbs, z_.value().limbs, sizeof(uint64_t) * %{fq_limb_nums});\n    return ret;\n  }\n\n  std::string ToString() const;\n\n private:\n  Fq x_;\n  Fq y_;\n  Fq z_;\n};\n\nTACHYON_CC_EXPORT std::ostream& operator<<(std::ostream& os, const G1JacobianPoint& value);\n\nclass TACHYON_CC_EXPORT G1AffinePoint {\n public:\n  G1AffinePoint() = default;\n  explicit G1AffinePoint(const tachyon_%{type}_g1_affine& point)\n      : G1AffinePoint(point.x, point.y) {}\n  G1AffinePoint(const tachyon_%{type}_fq& x, const tachyon_%{type}_fq& y) {\n    memcpy(x_.value().limbs, x.limbs, sizeof(uint64_t) * %{fq_limb_nums});\n    memcpy(y_.value().limbs, y.limbs, sizeof(uint64_t) * %{fq_limb_nums});\n  }\n  G1AffinePoint(const Fq& x, const Fq& y)\n      : x_(x), y_(y) {}\n\n  const Fq& x() const { return x_; }\n  Fq& x() { return x_; }\n\n  const Fq& y() const { return y_; }\n  Fq& y() { return y_; }\n\n  static G1AffinePoint Zero() {\n    return G1AffinePoint(tachyon_%{type}_g1_affine_zero());\n  }\n\n  static G1AffinePoint Generator() {\n    return G1AffinePoint(tachyon_%{type}_g1_affine_generator());\n  }\n\n  static G1AffinePoint Random() {\n    return G1AffinePoint(tachyon_%{type}_g1_affine_random());\n  }\n\n  G1JacobianPoint operator+(const G1AffinePoint& other) const {\n    auto a = ToCPoint();\n    auto b = other.ToCPoint();\n    return G1JacobianPoint(tachyon_%{type}_g1_affine_add(&a, &b));\n  }\n\n  G1JacobianPoint operator-(const G1AffinePoint& other) const {\n    auto a = ToCPoint();\n    auto b = other.ToCPoint();\n    return G1JacobianPoint(tachyon_%{type}_g1_affine_sub(&a, &b));\n  }\n\n  G1AffinePoint operator-() const {\n    return {x_, -y_};\n  }\n\n  G1JacobianPoint Double() const {\n    auto a = ToCPoint();\n    return G1JacobianPoint(tachyon_%{type}_g1_affine_dbl(&a));\n  }\n\n  bool operator==(const G1AffinePoint& other) const {\n    auto a = ToCPoint();\n    auto b = other.ToCPoint();\n    return tachyon_%{type}_g1_affine_eq(&a, &b);\n  }\n\n  bool operator!=(const G1AffinePoint& other) const {\n    auto a = ToCPoint();\n    auto b = other.ToCPoint();\n    return tachyon_%{type}_g1_affine_ne(&a, &b);\n  }\n\n  tachyon_%{type}_g1_affine ToCPoint() const {\n    tachyon_%{type}_g1_affine ret;\n    memcpy(ret.x.limbs, x_.value().limbs, sizeof(uint64_t) * %{fq_limb_nums});\n    memcpy(ret.y.limbs, y_.value().limbs, sizeof(uint64_t) * %{fq_limb_nums});\n    return ret;\n  }\n\n  std::string ToString() const;\n\n private:\n  Fq x_;\n  Fq y_;\n};\n\nTACHYON_CC_EXPORT std::ostream& operator<<(std::ostream& os, const G1AffinePoint& value);\n\nclass TACHYON_CC_EXPORT G1PointXYZZ {\n public:\n  G1PointXYZZ() = default;\n  explicit G1PointXYZZ(const tachyon_%{type}_g1_xyzz& point)\n      : G1PointXYZZ(point.x, point.y, point.zz, point.zzz) {}\n  G1PointXYZZ(const tachyon_%{type}_fq& x, const tachyon_%{type}_fq& y, const tachyon_%{type}_fq& zz, const tachyon_%{type}_fq& zzz) {\n    memcpy(x_.value().limbs, x.limbs, sizeof(uint64_t) * %{fq_limb_nums});\n    memcpy(y_.value().limbs, y.limbs, sizeof(uint64_t) * %{fq_limb_nums});\n    memcpy(zz_.value().limbs, zz.limbs, sizeof(uint64_t) * %{fq_limb_nums});\n    memcpy(zzz_.value().limbs, zzz.limbs, sizeof(uint64_t) * %{fq_limb_nums});\n  }\n  G1PointXYZZ(const Fq& x, const Fq& y, const Fq& zz, const Fq& zzz)\n      : x_(x), y_(y), zz_(zz), zzz_(zzz) {}\n\n  const Fq& x() const { return x_; }\n  Fq& x() { return x_; }\n\n  const Fq& y() const { return y_; }\n  Fq& y() { return y_; }\n\n  const Fq& zz() const { return zz_; }\n  Fq& zz() { return zz_; }\n\n  const Fq& zzz() const { return zzz_; }\n  Fq& zzz() { return zzz_; }\n\n  static G1PointXYZZ Zero() {\n    return G1PointXYZZ(tachyon_%{type}_g1_xyzz_zero());\n  }\n\n  static G1PointXYZZ Generator() {\n    return G1PointXYZZ(tachyon_%{type}_g1_xyzz_generator());\n  }\n\n  static G1PointXYZZ Random() {\n    return G1PointXYZZ(tachyon_%{type}_g1_xyzz_random());\n  }\n\n  G1PointXYZZ operator+(const G1PointXYZZ& other) const {\n    auto a = ToCPoint();\n    auto b = other.ToCPoint();\n    return G1PointXYZZ(tachyon_%{type}_g1_xyzz_add(&a, &b));\n  }\n\n  G1PointXYZZ& operator+=(const G1PointXYZZ& other) {\n    auto a = ToCPoint();\n    auto b = other.ToCPoint();\n    *this = G1PointXYZZ(tachyon_%{type}_g1_xyzz_add(&a, &b));\n    return *this;\n  }\n\n  G1PointXYZZ operator-(const G1PointXYZZ& other) const {\n    auto a = ToCPoint();\n    auto b = other.ToCPoint();\n    return G1PointXYZZ(tachyon_%{type}_g1_xyzz_sub(&a, &b));\n  }\n\n  G1PointXYZZ& operator-=(const G1PointXYZZ& other) {\n    auto a = ToCPoint();\n    auto b = other.ToCPoint();\n    *this = G1PointXYZZ(tachyon_%{type}_g1_xyzz_sub(&a, &b));\n    return *this;\n  }\n\n  G1PointXYZZ operator-() const {\n    return {x_, -y_, zz_, zzz_};\n  }\n\n  G1PointXYZZ Double() const {\n    auto a = ToCPoint();\n    return G1PointXYZZ(tachyon_%{type}_g1_xyzz_dbl(&a));\n  }\n\n  bool operator==(const G1PointXYZZ& other) const {\n    auto a = ToCPoint();\n    auto b = other.ToCPoint();\n    return tachyon_%{type}_g1_xyzz_eq(&a, &b);\n  }\n\n  bool operator!=(const G1PointXYZZ& other) const {\n    auto a = ToCPoint();\n    auto b = other.ToCPoint();\n    return tachyon_%{type}_g1_xyzz_ne(&a, &b);\n  }\n\n  tachyon_%{type}_g1_xyzz ToCPoint() const {\n    tachyon_%{type}_g1_xyzz ret;\n    memcpy(ret.x.limbs, x_.value().limbs, sizeof(uint64_t) * %{fq_limb_nums});\n    memcpy(ret.y.limbs, y_.value().limbs, sizeof(uint64_t) * %{fq_limb_nums});\n    memcpy(ret.zz.limbs, zz_.value().limbs, sizeof(uint64_t) * %{fq_limb_nums});\n    memcpy(ret.zzz.limbs, zzz_.value().limbs, sizeof(uint64_t) * %{fq_limb_nums});\n    return ret;\n  }\n\n  std::string ToString() const;\n\n private:\n  Fq x_;\n  Fq y_;\n  Fq zz_;\n  Fq zzz_;\n};\n\nTACHYON_CC_EXPORT std::ostream& operator<<(std::ostream& os, const G1PointXYZZ& value);\n\nclass TACHYON_CC_EXPORT G1Point2 {\n public:\n  G1Point2() = default;\n  G1Point2(const tachyon_%{type}_g1_point2& point)\n      : G1Point2(point.x, point.y) {}\n  G1Point2(const tachyon_%{type}_fq& x, const tachyon_%{type}_fq& y) {\n    memcpy(x_.value().limbs, x.limbs, sizeof(uint64_t) * %{fq_limb_nums});\n    memcpy(y_.value().limbs, y.limbs, sizeof(uint64_t) * %{fq_limb_nums});\n  }\n  G1Point2(const Fq& x, const Fq& y)\n      : x_(x), y_(y) {}\n\n  const Fq& x() const { return x_; }\n  Fq& x() { return x_; }\n\n  const Fq& y() const { return y_; }\n  Fq& y() { return y_; }\n\n  tachyon_%{type}_g1_point2 ToCPoint() const {\n    tachyon_%{type}_g1_point2 ret;\n    memcpy(ret.x.limbs, x_.value().limbs, sizeof(uint64_t) * %{fq_limb_nums});\n    memcpy(ret.y.limbs, y_.value().limbs, sizeof(uint64_t) * %{fq_limb_nums});\n    return ret;\n  }\n\n  std::string ToString() const;\n\n private:\n  Fq x_;\n  Fq y_;\n};\n\nTACHYON_CC_EXPORT std::ostream& operator<<(std::ostream& os, const G1Point2& value);\n\nclass TACHYON_CC_EXPORT G1Point3 {\n public:\n  G1Point3() = default;\n  G1Point3(const tachyon_%{type}_g1_point3& point)\n      : G1Point3(point.x, point.y, point.z) {}\n  G1Point3(const tachyon_%{type}_fq& x, const tachyon_%{type}_fq& y, const tachyon_%{type}_fq& z) {\n    memcpy(x_.value().limbs, x.limbs, sizeof(uint64_t) * %{fq_limb_nums});\n    memcpy(y_.value().limbs, y.limbs, sizeof(uint64_t) * %{fq_limb_nums});\n    memcpy(z_.value().limbs, z.limbs, sizeof(uint64_t) * %{fq_limb_nums});\n  }\n  G1Point3(const Fq& x, const Fq& y, const Fq& z)\n      : x_(x), y_(y), z_(z) {}\n\n  const Fq& x() const { return x_; }\n  Fq& x() { return x_; }\n\n  const Fq& y() const { return y_; }\n  Fq& y() { return y_; }\n\n  const Fq& z() const { return z_; }\n  Fq& z() { return z_; }\n\n  tachyon_%{type}_g1_point3 ToCPoint() const {\n    tachyon_%{type}_g1_point3 ret;\n    memcpy(ret.x.limbs, x_.value().limbs, sizeof(uint64_t) * %{fq_limb_nums});\n    memcpy(ret.y.limbs, y_.value().limbs, sizeof(uint64_t) * %{fq_limb_nums});\n    memcpy(ret.z.limbs, z_.value().limbs, sizeof(uint64_t) * %{fq_limb_nums});\n    return ret;\n  }\n\n  std::string ToString() const;\n\n private:\n  Fq x_;\n  Fq y_;\n  Fq z_;\n};\n\nTACHYON_CC_EXPORT std::ostream& operator<<(std::ostream& os, const G1Point2& value);\n\nclass TACHYON_CC_EXPORT G1Point4 {\n public:\n  G1Point4() = default;\n  G1Point4(const tachyon_%{type}_g1_point4& point)\n      : G1Point4(point.x, point.y, point.z, point.w) {}\n  G1Point4(const tachyon_%{type}_fq& x, const tachyon_%{type}_fq& y,const tachyon_%{type}_fq& z, const tachyon_%{type}_fq& w) {\n    memcpy(x_.value().limbs, x.limbs, sizeof(uint64_t) * %{fq_limb_nums});\n    memcpy(y_.value().limbs, y.limbs, sizeof(uint64_t) * %{fq_limb_nums});\n    memcpy(z_.value().limbs, z.limbs, sizeof(uint64_t) * %{fq_limb_nums});\n    memcpy(w_.value().limbs, w.limbs, sizeof(uint64_t) * %{fq_limb_nums});\n  }\n  G1Point4(const Fq& x, const Fq& y, const Fq& z, const Fq& w)\n      : x_(x), y_(y), z_(z), w_(w) {}\n\n  const Fq& x() const { return x_; }\n  Fq& x() { return x_; }\n\n  const Fq& y() const { return y_; }\n  Fq& y() { return y_; }\n\n  const Fq& z() const { return z_; }\n  Fq& z() { return z_; }\n\n  const Fq& w() const { return w_; }\n  Fq& w() { return w_; }\n\n  tachyon_%{type}_g1_point4 ToCPoint() const {\n    tachyon_%{type}_g1_point4 ret;\n    memcpy(ret.x.limbs, x_.value().limbs, sizeof(uint64_t) * %{fq_limb_nums});\n    memcpy(ret.y.limbs, y_.value().limbs, sizeof(uint64_t) * %{fq_limb_nums});\n    memcpy(ret.z.limbs, z_.value().limbs, sizeof(uint64_t) * %{fq_limb_nums});\n    memcpy(ret.w.limbs, w_.value().limbs, sizeof(uint64_t) * %{fq_limb_nums});\n    return ret;\n  }\n\n  std::string ToString() const;\n\n private:\n  Fq x_;\n  Fq y_;\n  Fq z_;\n  Fq w_;\n};\n\nTACHYON_CC_EXPORT std::ostream& operator<<(std::ostream& os, const G1Point4& value);\n\n} // namespace tachyon::cc::math::%{type}\n// clang-format on\n"
  },
  {
    "path": "tachyon/cc/math/elliptic_curves/generator/generator.cc",
    "content": "#include \"absl/strings/str_replace.h\"\n\n#include \"tachyon/base/console/iostream.h\"\n#include \"tachyon/base/files/file_path_flag.h\"\n#include \"tachyon/base/files/file_util.h\"\n#include \"tachyon/base/flag/flag_parser.h\"\n#include \"tachyon/base/strings/string_number_conversions.h\"\n#include \"tachyon/base/strings/string_util.h\"\n#include \"tachyon/build/cc_writer.h\"\n#include \"tachyon/c/math/elliptic_curves/generator/generator_util.h\"\n\nnamespace tachyon {\n\nstruct GenerationConfig : public build::CcWriter {\n  base::FilePath prime_field_hdr_tpl_path;\n  base::FilePath prime_field_src_tpl_path;\n  base::FilePath g1_hdr_tpl_path;\n  base::FilePath g1_src_tpl_path;\n\n  std::string type;\n  int fq_limb_nums;\n  int fr_limb_nums;\n\n  int GeneratePrimeFieldHdr(std::string_view suffix) const;\n  int GenerateFqHdr() const;\n  int GenerateFrHdr() const;\n  int GeneratePrimeFieldSrc(std::string_view suffix) const;\n  int GenerateFqSrc() const;\n  int GenerateFrSrc() const;\n  int GenerateG1Hdr() const;\n  int GenerateG1Src() const;\n};\n\nint GenerationConfig::GeneratePrimeFieldHdr(std::string_view suffix) const {\n  std::string tpl_content;\n  base::ReadFileToString(prime_field_hdr_tpl_path, &tpl_content);\n\n  std::string content = absl::StrReplaceAll(\n      tpl_content,\n      {\n          {\"%{header_dir_name}\", c::math::GetLocation(type)},\n          {\"%{type}\", type},\n          {\"%{suffix}\", suffix},\n          {\"%{c_field}\", absl::Substitute(\"tachyon_$0_$1\", type, suffix)},\n          {\"%{cc_field}\", suffix == \"fq\" ? \"Fq\" : \"Fr\"},\n          {\"%{limb_nums}\",\n           base::NumberToString(suffix == \"fq\" ? fq_limb_nums : fr_limb_nums)},\n      });\n  return WriteHdr(content, false);\n}\n\nint GenerationConfig::GenerateFqHdr() const {\n  return GeneratePrimeFieldHdr(\"fq\");\n}\n\nint GenerationConfig::GenerateFrHdr() const {\n  return GeneratePrimeFieldHdr(\"fr\");\n}\n\nint GenerationConfig::GeneratePrimeFieldSrc(std::string_view suffix) const {\n  std::string tpl_content;\n  base::ReadFileToString(prime_field_src_tpl_path, &tpl_content);\n\n  std::string content = absl::StrReplaceAll(\n      tpl_content, {\n                       {\"%{header_dir_name}\", c::math::GetLocation(type)},\n                       {\"%{type}\", type},\n                       {\"%{suffix}\", suffix},\n                       {\"%{cc_field}\", suffix == \"fq\" ? \"Fq\" : \"Fr\"},\n                   });\n  return WriteSrc(content);\n}\n\nint GenerationConfig::GenerateFqSrc() const {\n  return GeneratePrimeFieldSrc(\"fq\");\n}\n\nint GenerationConfig::GenerateFrSrc() const {\n  return GeneratePrimeFieldSrc(\"fr\");\n}\n\nint GenerationConfig::GenerateG1Hdr() const {\n  std::string tpl_content;\n  base::ReadFileToString(g1_hdr_tpl_path, &tpl_content);\n\n  std::string content = absl::StrReplaceAll(\n      tpl_content, {\n                       {\"%{header_dir_name}\", c::math::GetLocation(type)},\n                       {\"%{type}\", type},\n                       {\"%{c_fq}\", absl::Substitute(\"tachyon_$0_fq\", type)},\n                       {\"%{c_g1}\", absl::Substitute(\"tachyon_$0_g1\", type)},\n                       {\"%{fq_limb_nums}\", base::NumberToString(fq_limb_nums)},\n                   });\n  return WriteHdr(content, false);\n}\n\nint GenerationConfig::GenerateG1Src() const {\n  std::string tpl_content;\n  base::ReadFileToString(g1_src_tpl_path, &tpl_content);\n\n  std::string content = absl::StrReplaceAll(\n      tpl_content, {\n                       {\"%{header_dir_name}\", c::math::GetLocation(type)},\n                       {\"%{type}\", type},\n                   });\n  return WriteSrc(content);\n}\n\nint RealMain(int argc, char** argv) {\n  GenerationConfig config;\n  config.generator = \"//tachyon/cc/math/elliptic_curves/generator\";\n\n  base::FlagParser parser;\n  parser.AddFlag<base::FilePathFlag>(&config.out)\n      .set_long_name(\"--out\")\n      .set_help(\"path to output\");\n  parser.AddFlag<base::StringFlag>(&config.type)\n      .set_long_name(\"--type\")\n      .set_required();\n  parser.AddFlag<base::IntFlag>(&config.fq_limb_nums)\n      .set_long_name(\"--fq_limb_nums\")\n      .set_required();\n  parser.AddFlag<base::IntFlag>(&config.fr_limb_nums)\n      .set_long_name(\"--fr_limb_nums\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.prime_field_hdr_tpl_path)\n      .set_long_name(\"--prime_field_hdr_tpl_path\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.prime_field_src_tpl_path)\n      .set_long_name(\"--prime_field_src_tpl_path\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.g1_hdr_tpl_path)\n      .set_long_name(\"--g1_hdr_tpl_path\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.g1_src_tpl_path)\n      .set_long_name(\"--g1_src_tpl_path\")\n      .set_required();\n\n  std::string error;\n  if (!parser.Parse(argc, argv, &error)) {\n    tachyon_cerr << error << std::endl;\n    return 1;\n  }\n\n  if (base::EndsWith(config.out.value(), \"fq.h\")) {\n    return config.GenerateFqHdr();\n  } else if (base::EndsWith(config.out.value(), \"fq.cc\")) {\n    return config.GenerateFqSrc();\n  } else if (base::EndsWith(config.out.value(), \"fr.h\")) {\n    return config.GenerateFrHdr();\n  } else if (base::EndsWith(config.out.value(), \"fr.cc\")) {\n    return config.GenerateFrSrc();\n  } else if (base::EndsWith(config.out.value(), \"g1.h\")) {\n    return config.GenerateG1Hdr();\n  } else if (base::EndsWith(config.out.value(), \"g1.cc\")) {\n    return config.GenerateG1Src();\n  } else {\n    tachyon_cerr << \"suffix not supported:\" << config.out << std::endl;\n    return 1;\n  }\n}\n\n}  // namespace tachyon\n\nint main(int argc, char** argv) { return tachyon::RealMain(argc, argv); }\n"
  },
  {
    "path": "tachyon/cc/math/elliptic_curves/generator/prime_field.cc.tpl",
    "content": "// clang-format off\n#include \"tachyon/c/math/elliptic_curves/%{header_dir_name}/%{suffix}_type_traits.h\"\n\nnamespace tachyon::cc::math::%{type} {\n\nstd::string %{cc_field}::ToString() const {\n  return c::base::native_cast(value_).ToString();\n}\n\nstd::ostream& operator<<(std::ostream& os, const %{cc_field}& value) {\n  return os << value.ToString();\n}\n\n} // namespace tachyon::cc::math::%{type}\n// clang-format on\n"
  },
  {
    "path": "tachyon/cc/math/elliptic_curves/generator/prime_field.h.tpl",
    "content": "// clang-format off\n#include <string.h>\n\n#include <ostream>\n\n#include \"tachyon/c/math/elliptic_curves/%{header_dir_name}/%{suffix}.h\"\n#include \"tachyon/cc/export.h\"\n\nnamespace tachyon::cc::math::%{type} {\n\nclass TACHYON_CC_EXPORT %{cc_field} {\n public:\n  %{cc_field}() = default;\n  explicit %{cc_field}(%{c_field} value) {\n    memcpy(value_.limbs, value.limbs, sizeof(uint64_t) * %{limb_nums});\n  }\n\n  const %{c_field}& value() const { return value_; }\n  %{c_field}& value() { return value_; }\n\n  const %{c_field}* value_ptr() const { return &value_; }\n  %{c_field}* value_ptr() { return &value_; }\n\n  static %{cc_field} Zero() {\n    return %{cc_field}(%{c_field}_zero());\n  }\n\n  static %{cc_field} One() {\n    return %{cc_field}(%{c_field}_one());\n  }\n\n  static %{cc_field} MinusOne() {\n    return %{cc_field}(%{c_field}_minus_one());\n  }\n\n  static %{cc_field} Random() {\n    return %{cc_field}(%{c_field}_random());\n  }\n\n  %{cc_field} operator+(const %{cc_field}& other) const {\n    return %{cc_field}(%{c_field}_add(&value_, &other.value_));\n  }\n\n  %{cc_field}& operator+=(const %{cc_field}& other) {\n    value_ = %{c_field}_add(&value_, &other.value_);\n    return *this;\n  }\n\n  %{cc_field} operator-(const %{cc_field}& other) const {\n    return %{cc_field}(%{c_field}_sub(&value_, &other.value_));\n  }\n\n  %{cc_field}& operator-=(const %{cc_field}& other) {\n    value_ = %{c_field}_sub(&value_, &other.value_);\n    return *this;\n  }\n\n  %{cc_field} operator*(const %{cc_field}& other) const {\n    return %{cc_field}(%{c_field}_mul(&value_, &other.value_));\n  }\n\n  %{cc_field}& operator*=(const %{cc_field}& other) {\n    value_ = %{c_field}_mul(&value_, &other.value_);\n    return *this;\n  }\n\n  %{cc_field} operator/(const %{cc_field}& other) const {\n    return %{cc_field}(%{c_field}_div(&value_, &other.value_));\n  }\n\n  %{cc_field}& operator/=(const %{cc_field}& other) {\n    value_ = %{c_field}_div(&value_, &other.value_);\n    return *this;\n  }\n\n  %{cc_field} operator-() const {\n    return %{cc_field}(%{c_field}_neg(&value_));\n  }\n\n  %{cc_field} Double() const {\n    return %{cc_field}(%{c_field}_dbl(&value_));\n  }\n\n  %{cc_field} Square() const {\n    return %{cc_field}(%{c_field}_sqr(&value_));\n  }\n\n  %{cc_field} Inverse() const {\n    return %{cc_field}(%{c_field}_inv(&value_));\n  }\n\n  bool operator==(const %{cc_field}& other) const {\n    return %{c_field}_eq(&value_, &other.value_);\n  }\n\n  bool operator!=(const %{cc_field}& other) const {\n    return %{c_field}_ne(&value_, &other.value_);\n  }\n\n  bool operator>(const %{cc_field}& other) const {\n    return %{c_field}_gt(&value_, &other.value_);\n  }\n\n  bool operator>=(const %{cc_field}& other) const {\n    return %{c_field}_ge(&value_, &other.value_);\n  }\n\n  bool operator<(const %{cc_field}& other) const {\n    return %{c_field}_lt(&value_, &other.value_);\n  }\n\n  bool operator<=(const %{cc_field}& other) const {\n    return %{c_field}_le(&value_, &other.value_);\n  }\n\n  std::string ToString() const;\n\n private:\n  %{c_field} value_;\n};\n\nTACHYON_CC_EXPORT std::ostream& operator<<(std::ostream& os, const %{cc_field}& value);\n\n} // namespace tachyon::cc::math::%{type}\n// clang-format on\n"
  },
  {
    "path": "tachyon/cc/math/elliptic_curves/short_weierstrass/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_unittest\")\n\ntachyon_cc_unittest(\n    name = \"short_weierstrass_unittests\",\n    srcs = [\n        \"affine_point_unittest.cc\",\n        \"jacobian_point_unittest.cc\",\n        \"point_xyzz_unittest.cc\",\n        \"projective_point_unittest.cc\",\n    ],\n    deps = [\n        \"//tachyon/cc/math/elliptic_curves/bn/bn254:g1\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/cc/math/elliptic_curves/short_weierstrass/affine_point_unittest.cc",
    "content": "#include \"gtest/gtest.h\"\n\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/fq_type_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1_point_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1_point_type_traits.h\"\n#include \"tachyon/cc/math/elliptic_curves/bn/bn254/g1.h\"\n\nnamespace tachyon::cc::math {\n\nnamespace {\n\nclass AffinePointTest : public testing::Test {\n public:\n  static void SetUpTestSuite() { tachyon::math::bn254::G1Curve::Init(); }\n\n  void SetUp() override {\n    a_ = tachyon::math::bn254::G1AffinePoint::Random();\n    b_ = tachyon::math::bn254::G1AffinePoint::Random();\n\n    cc_a_ = bn254::G1AffinePoint(c::base::c_cast(a_));\n    cc_b_ = bn254::G1AffinePoint(c::base::c_cast(b_));\n  }\n\n protected:\n  tachyon::math::bn254::G1AffinePoint a_;\n  tachyon::math::bn254::G1AffinePoint b_;\n  bn254::G1AffinePoint cc_a_;\n  bn254::G1AffinePoint cc_b_;\n};\n\n}  // namespace\n\nTEST_F(AffinePointTest, Zero) {\n  bn254::G1AffinePoint cc_ret = bn254::G1AffinePoint::Zero();\n  EXPECT_TRUE(c::base::native_cast(cc_ret.ToCPoint()).IsZero());\n}\n\nTEST_F(AffinePointTest, Generator) {\n  bn254::G1AffinePoint cc_ret = bn254::G1AffinePoint::Generator();\n  EXPECT_EQ(c::base::native_cast(cc_ret.ToCPoint()),\n            tachyon::math::bn254::G1AffinePoint::Generator());\n}\n\nTEST_F(AffinePointTest, Random) {\n  bn254::G1AffinePoint cc_ret = bn254::G1AffinePoint::Random();\n  EXPECT_NE(c::base::native_cast(cc_ret.ToCPoint()), a_);\n}\n\nTEST_F(AffinePointTest, Eq) { EXPECT_EQ(cc_a_ == cc_b_, a_ == b_); }\n\nTEST_F(AffinePointTest, Ne) { EXPECT_EQ(cc_a_ != cc_b_, a_ != b_); }\n\nTEST_F(AffinePointTest, Add) {\n  bn254::G1JacobianPoint cc_ret = cc_a_ + cc_b_;\n  EXPECT_EQ(c::base::native_cast(cc_ret.ToCPoint()), a_ + b_);\n}\n\nTEST_F(AffinePointTest, Sub) {\n  bn254::G1JacobianPoint cc_ret = cc_a_ - cc_b_;\n  EXPECT_EQ(c::base::native_cast(cc_ret.ToCPoint()), a_ - b_);\n}\n\nTEST_F(AffinePointTest, Neg) {\n  bn254::G1AffinePoint cc_ret = -cc_a_;\n  EXPECT_EQ(c::base::native_cast(cc_ret.ToCPoint()), -a_);\n}\n\nTEST_F(AffinePointTest, Dbl) {\n  bn254::G1JacobianPoint cc_ret = cc_a_.Double();\n  EXPECT_EQ(c::base::native_cast(cc_ret.ToCPoint()), a_.Double());\n}\n\n}  // namespace tachyon::cc::math\n"
  },
  {
    "path": "tachyon/cc/math/elliptic_curves/short_weierstrass/jacobian_point_unittest.cc",
    "content": "#include \"gtest/gtest.h\"\n\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/fq_type_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1_point_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1_point_type_traits.h\"\n#include \"tachyon/cc/math/elliptic_curves/bn/bn254/g1.h\"\n\nnamespace tachyon::cc::math {\n\nnamespace {\n\nclass JacobianPointTest : public testing::Test {\n public:\n  static void SetUpTestSuite() { tachyon::math::bn254::G1Curve::Init(); }\n\n  void SetUp() override {\n    a_ = tachyon::math::bn254::G1JacobianPoint::Random();\n    b_ = tachyon::math::bn254::G1JacobianPoint::Random();\n\n    cc_a_ = bn254::G1JacobianPoint(c::base::c_cast(a_));\n    cc_b_ = bn254::G1JacobianPoint(c::base::c_cast(b_));\n  }\n\n protected:\n  tachyon::math::bn254::G1JacobianPoint a_;\n  tachyon::math::bn254::G1JacobianPoint b_;\n  bn254::G1JacobianPoint cc_a_;\n  bn254::G1JacobianPoint cc_b_;\n};\n\n}  // namespace\n\nTEST_F(JacobianPointTest, Zero) {\n  bn254::G1JacobianPoint c_ret = bn254::G1JacobianPoint::Zero();\n  EXPECT_TRUE(c::base::native_cast(c_ret.ToCPoint()).IsZero());\n}\n\nTEST_F(JacobianPointTest, Generator) {\n  bn254::G1JacobianPoint c_ret = bn254::G1JacobianPoint::Generator();\n  EXPECT_EQ(c::base::native_cast(c_ret.ToCPoint()),\n            tachyon::math::bn254::G1JacobianPoint::Generator());\n}\n\nTEST_F(JacobianPointTest, Random) {\n  bn254::G1JacobianPoint c_ret = bn254::G1JacobianPoint::Random();\n  EXPECT_NE(c::base::native_cast(c_ret.ToCPoint()), a_);\n}\n\nTEST_F(JacobianPointTest, Eq) { EXPECT_EQ(cc_a_ == cc_b_, a_ == b_); }\n\nTEST_F(JacobianPointTest, Ne) { EXPECT_EQ(cc_a_ != cc_b_, a_ != b_); }\n\nTEST_F(JacobianPointTest, Add) {\n  bn254::G1JacobianPoint cc_ret = cc_a_ + cc_b_;\n  EXPECT_EQ(c::base::native_cast(cc_ret.ToCPoint()), a_ + b_);\n  EXPECT_EQ(c::base::native_cast((cc_a_ += cc_b_).ToCPoint()), a_ += b_);\n}\n\nTEST_F(JacobianPointTest, Sub) {\n  bn254::G1JacobianPoint cc_ret = cc_a_ - cc_b_;\n  EXPECT_EQ(c::base::native_cast(cc_ret.ToCPoint()), a_ - b_);\n  EXPECT_EQ(c::base::native_cast((cc_a_ -= cc_b_).ToCPoint()), a_ -= b_);\n}\n\nTEST_F(JacobianPointTest, Neg) {\n  bn254::G1JacobianPoint cc_ret = -cc_a_;\n  EXPECT_EQ(c::base::native_cast(cc_ret.ToCPoint()), -a_);\n}\n\nTEST_F(JacobianPointTest, Dbl) {\n  bn254::G1JacobianPoint cc_ret = cc_a_.Double();\n  EXPECT_EQ(c::base::native_cast(cc_ret.ToCPoint()), a_.Double());\n}\n\n}  // namespace tachyon::cc::math\n"
  },
  {
    "path": "tachyon/cc/math/elliptic_curves/short_weierstrass/point_xyzz_unittest.cc",
    "content": "#include \"gtest/gtest.h\"\n\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/fq_type_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1_point_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1_point_type_traits.h\"\n#include \"tachyon/cc/math/elliptic_curves/bn/bn254/g1.h\"\n\nnamespace tachyon::cc::math {\n\nnamespace {\n\nclass PointXYZZTest : public testing::Test {\n public:\n  static void SetUpTestSuite() { tachyon::math::bn254::G1Curve::Init(); }\n\n  void SetUp() override {\n    a_ = tachyon::math::bn254::G1PointXYZZ::Random();\n    b_ = tachyon::math::bn254::G1PointXYZZ::Random();\n\n    cc_a_ = bn254::G1PointXYZZ(c::base::c_cast(a_));\n    cc_b_ = bn254::G1PointXYZZ(c::base::c_cast(b_));\n  }\n\n protected:\n  tachyon::math::bn254::G1PointXYZZ a_;\n  tachyon::math::bn254::G1PointXYZZ b_;\n  bn254::G1PointXYZZ cc_a_;\n  bn254::G1PointXYZZ cc_b_;\n};\n\n}  // namespace\n\nTEST_F(PointXYZZTest, Zero) {\n  bn254::G1PointXYZZ c_ret = bn254::G1PointXYZZ::Zero();\n  EXPECT_TRUE(c::base::native_cast(c_ret.ToCPoint()).IsZero());\n}\n\nTEST_F(PointXYZZTest, Generator) {\n  bn254::G1PointXYZZ c_ret = bn254::G1PointXYZZ::Generator();\n  EXPECT_EQ(c::base::native_cast(c_ret.ToCPoint()),\n            tachyon::math::bn254::G1PointXYZZ::Generator());\n}\n\nTEST_F(PointXYZZTest, Random) {\n  bn254::G1PointXYZZ c_ret = bn254::G1PointXYZZ::Random();\n  EXPECT_NE(c::base::native_cast(c_ret.ToCPoint()), a_);\n}\n\nTEST_F(PointXYZZTest, Eq) { EXPECT_EQ(cc_a_ == cc_b_, a_ == b_); }\n\nTEST_F(PointXYZZTest, Ne) { EXPECT_EQ(cc_a_ != cc_b_, a_ != b_); }\n\nTEST_F(PointXYZZTest, Add) {\n  bn254::G1PointXYZZ cc_ret = cc_a_ + cc_b_;\n  EXPECT_EQ(c::base::native_cast(cc_ret.ToCPoint()), a_ + b_);\n  EXPECT_EQ(c::base::native_cast((cc_a_ += cc_b_).ToCPoint()), a_ += b_);\n}\n\nTEST_F(PointXYZZTest, Sub) {\n  bn254::G1PointXYZZ cc_ret = cc_a_ - cc_b_;\n  EXPECT_EQ(c::base::native_cast(cc_ret.ToCPoint()), a_ - b_);\n  EXPECT_EQ(c::base::native_cast((cc_a_ -= cc_b_).ToCPoint()), a_ -= b_);\n}\n\nTEST_F(PointXYZZTest, Neg) {\n  bn254::G1PointXYZZ cc_ret = -cc_a_;\n  EXPECT_EQ(c::base::native_cast(cc_ret.ToCPoint()), -a_);\n}\n\nTEST_F(PointXYZZTest, Dbl) {\n  bn254::G1PointXYZZ cc_ret = cc_a_.Double();\n  EXPECT_EQ(c::base::native_cast(cc_ret.ToCPoint()), a_.Double());\n}\n\n}  // namespace tachyon::cc::math\n"
  },
  {
    "path": "tachyon/cc/math/elliptic_curves/short_weierstrass/projective_point_unittest.cc",
    "content": "#include \"gtest/gtest.h\"\n\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/fq_type_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1_point_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1_point_type_traits.h\"\n#include \"tachyon/cc/math/elliptic_curves/bn/bn254/g1.h\"\n\nnamespace tachyon::cc::math {\n\nnamespace {\n\nclass ProjectivePointTest : public testing::Test {\n public:\n  static void SetUpTestSuite() { tachyon::math::bn254::G1Curve::Init(); }\n\n  void SetUp() override {\n    a_ = tachyon::math::bn254::G1ProjectivePoint::Random();\n    b_ = tachyon::math::bn254::G1ProjectivePoint::Random();\n\n    cc_a_ = bn254::G1ProjectivePoint(c::base::c_cast(a_));\n    cc_b_ = bn254::G1ProjectivePoint(c::base::c_cast(b_));\n  }\n\n protected:\n  tachyon::math::bn254::G1ProjectivePoint a_;\n  tachyon::math::bn254::G1ProjectivePoint b_;\n  bn254::G1ProjectivePoint cc_a_;\n  bn254::G1ProjectivePoint cc_b_;\n};\n\n}  // namespace\n\nTEST_F(ProjectivePointTest, Zero) {\n  bn254::G1ProjectivePoint c_ret = bn254::G1ProjectivePoint::Zero();\n  EXPECT_TRUE(c::base::native_cast(c_ret.ToCPoint()).IsZero());\n}\n\nTEST_F(ProjectivePointTest, Generator) {\n  bn254::G1ProjectivePoint c_ret = bn254::G1ProjectivePoint::Generator();\n  EXPECT_EQ(c::base::native_cast(c_ret.ToCPoint()),\n            tachyon::math::bn254::G1ProjectivePoint::Generator());\n}\n\nTEST_F(ProjectivePointTest, Random) {\n  bn254::G1ProjectivePoint c_ret = bn254::G1ProjectivePoint::Random();\n  EXPECT_NE(c::base::native_cast(c_ret.ToCPoint()), a_);\n}\n\nTEST_F(ProjectivePointTest, Eq) { EXPECT_EQ(cc_a_ == cc_b_, a_ == b_); }\n\nTEST_F(ProjectivePointTest, Ne) { EXPECT_EQ(cc_a_ != cc_b_, a_ != b_); }\n\nTEST_F(ProjectivePointTest, Add) {\n  bn254::G1ProjectivePoint cc_ret = cc_a_ + cc_b_;\n  EXPECT_EQ(c::base::native_cast(cc_ret.ToCPoint()), a_ + b_);\n  EXPECT_EQ(c::base::native_cast((cc_a_ += cc_b_).ToCPoint()), a_ += b_);\n}\n\nTEST_F(ProjectivePointTest, Sub) {\n  bn254::G1ProjectivePoint cc_ret = cc_a_ - cc_b_;\n  EXPECT_EQ(c::base::native_cast(cc_ret.ToCPoint()), a_ - b_);\n  EXPECT_EQ(c::base::native_cast((cc_a_ -= cc_b_).ToCPoint()), a_ -= b_);\n}\n\nTEST_F(ProjectivePointTest, Neg) {\n  bn254::G1ProjectivePoint cc_ret = -cc_a_;\n  EXPECT_EQ(c::base::native_cast(cc_ret.ToCPoint()), -a_);\n}\n\nTEST_F(ProjectivePointTest, Dbl) {\n  bn254::G1ProjectivePoint cc_ret = cc_a_.Double();\n  EXPECT_EQ(c::base::native_cast(cc_ret.ToCPoint()), a_.Double());\n}\n\n}  // namespace tachyon::cc::math\n"
  },
  {
    "path": "tachyon/cc/math/finite_fields/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_unittest(\n    name = \"finite_fields_unittests\",\n    srcs = [\"prime_field_unittest.cc\"],\n    deps = [\n        \"//tachyon/base:optional\",\n        \"//tachyon/cc/math/elliptic_curves/bn/bn254:fr\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/cc/math/finite_fields/prime_field_unittest.cc",
    "content": "#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/optional.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/fr_type_traits.h\"\n#include \"tachyon/cc/math/elliptic_curves/bn/bn254/fr.h\"\n\nnamespace tachyon::cc::math {\n\nnamespace {\n\nclass PrimeFieldTest : public testing::Test {\n public:\n  void SetUp() override {\n    a_ = tachyon::math::bn254::Fr::Random();\n    b_ = tachyon::math::bn254::Fr::Random();\n\n    cc_a_ = bn254::Fr(c::base::c_cast(a_));\n    cc_b_ = bn254::Fr(c::base::c_cast(b_));\n  }\n\n protected:\n  tachyon::math::bn254::Fr a_;\n  tachyon::math::bn254::Fr b_;\n  bn254::Fr cc_a_;\n  bn254::Fr cc_b_;\n};\n\n}  // namespace\n\nTEST_F(PrimeFieldTest, Zero) {\n  bn254::Fr zero = bn254::Fr::Zero();\n  EXPECT_TRUE(c::base::native_cast(zero.value()).IsZero());\n}\n\nTEST_F(PrimeFieldTest, One) {\n  bn254::Fr one = bn254::Fr::One();\n  EXPECT_TRUE(c::base::native_cast(one.value()).IsOne());\n}\n\nTEST_F(PrimeFieldTest, MinusOne) {\n  bn254::Fr minus_one = bn254::Fr::MinusOne();\n  EXPECT_TRUE(c::base::native_cast(minus_one.value()).IsMinusOne());\n}\n\nTEST_F(PrimeFieldTest, Random) {\n  bn254::Fr random = bn254::Fr::Random();\n  EXPECT_NE(c::base::native_cast(random.value()), a_);\n}\n\nTEST_F(PrimeFieldTest, Add) {\n  EXPECT_EQ(c::base::native_cast((cc_a_ + cc_b_).value()), a_ + b_);\n  EXPECT_EQ(c::base::native_cast((cc_a_ += cc_b_).value()), a_ += b_);\n}\n\nTEST_F(PrimeFieldTest, Sub) {\n  EXPECT_EQ(c::base::native_cast((cc_a_ - cc_b_).value()), a_ - b_);\n  EXPECT_EQ(c::base::native_cast((cc_a_ -= cc_b_).value()), a_ -= b_);\n}\n\nTEST_F(PrimeFieldTest, Mul) {\n  EXPECT_EQ(c::base::native_cast((cc_a_ * cc_b_).value()), a_ * b_);\n  EXPECT_EQ(c::base::native_cast((cc_a_ *= cc_b_).value()), a_ *= b_);\n}\n\nTEST_F(PrimeFieldTest, Div) {\n  if (b_.IsZero()) {\n    ASSERT_FALSE(a_ / b_);\n    ASSERT_FALSE(a_ /= b_);\n  } else {\n    bn254::Fr cc_div = unwrap<bn254::Fr>(cc_a_ / cc_b_);\n    tachyon::math::bn254::Fr div = unwrap(a_ / b_);\n    EXPECT_EQ(c::base::native_cast((cc_div).value()), div);\n    ASSERT_TRUE(a_ /= b_);\n    EXPECT_EQ(c::base::native_cast((cc_a_ /= cc_b_).value()), a_);\n  }\n}\n\nTEST_F(PrimeFieldTest, Negate) {\n  EXPECT_EQ(c::base::native_cast((-cc_a_).value()), -a_);\n}\n\nTEST_F(PrimeFieldTest, Double) {\n  EXPECT_EQ(c::base::native_cast(cc_a_.Double().value()), a_.Double());\n}\n\nTEST_F(PrimeFieldTest, Square) {\n  EXPECT_EQ(c::base::native_cast(cc_a_.Square().value()), a_.Square());\n}\n\nTEST_F(PrimeFieldTest, Inverse) {\n  if (a_.IsZero()) {\n    ASSERT_FALSE(a_.Inverse());\n  } else {\n    bn254::Fr cc_a_inv = unwrap<bn254::Fr>(cc_a_.Inverse());\n    tachyon::math::bn254::Fr a_inv = unwrap(a_.Inverse());\n    EXPECT_EQ(c::base::native_cast((cc_a_inv).value()), a_inv);\n  }\n}\n\nTEST_F(PrimeFieldTest, Eq) { EXPECT_EQ(cc_a_ == cc_b_, a_ == b_); }\n\nTEST_F(PrimeFieldTest, Ne) { EXPECT_EQ(cc_a_ != cc_b_, a_ != b_); }\n\nTEST_F(PrimeFieldTest, Gt) { EXPECT_EQ(cc_a_ > cc_b_, a_ > b_); }\n\nTEST_F(PrimeFieldTest, Ge) { EXPECT_EQ(cc_a_ >= cc_b_, a_ >= b_); }\n\nTEST_F(PrimeFieldTest, Lt) { EXPECT_EQ(cc_a_ < cc_b_, a_ < b_); }\n\nTEST_F(PrimeFieldTest, Le) { EXPECT_EQ(cc_a_ <= cc_b_, a_ <= b_); }\n\n}  // namespace tachyon::cc::math\n"
  },
  {
    "path": "tachyon/cc/version.bzl",
    "content": "# See https://semver.org/\nVERSION_MAJOR = 0\nVERSION_MINOR = 0\nVERSION_PATCH = 1\nVERSION_PRERELEASE = \"\"\n\nVERSION = \".\".join([\n    str(VERSION_MAJOR),\n    str(VERSION_MINOR),\n    str(VERSION_PATCH),\n])\n"
  },
  {
    "path": "tachyon/cc/version.cc",
    "content": "#include \"tachyon/cc/version.h\"\n\nnamespace tachyon::cc {\n\nuint32_t GetRuntimeVersion() { return TACHYON_CC_VERSION; }\n\nstd::string_view GetRuntimeVersionStr() { return TACHYON_CC_VERSION_STR; }\n\nstd::string_view GetRuntimeFullVersionStr() {\n  return TACHYON_CC_VERSION_FULL_STR;\n}\n\n}  // namespace tachyon::cc\n"
  },
  {
    "path": "tachyon/cc/version.h",
    "content": "#ifndef TACHYON_CC_VERSION_H_\n#define TACHYON_CC_VERSION_H_\n\n#include <stdint.h>\n\n#include <string_view>\n\n#include \"tachyon/cc/export.h\"\n#include \"tachyon/cc/version_generated.h\"\n\nnamespace tachyon::cc {\n\nTACHYON_CC_EXPORT uint32_t GetRuntimeVersion();\n\nTACHYON_CC_EXPORT std::string_view GetRuntimeVersionStr();\n\nTACHYON_CC_EXPORT std::string_view GetRuntimeFullVersionStr();\n\n}  // namespace tachyon::cc\n\n#endif  // TACHYON_CC_VERSION_H_\n"
  },
  {
    "path": "tachyon/cc/version_unittest.cc",
    "content": "#include \"tachyon/cc/version.h\"\n\n#include \"absl/strings/substitute.h\"\n#include \"gtest/gtest.h\"\n\nnamespace tachyon::cc {\n\nTEST(VersionTest, CompileTimeVersionTest) {\n  EXPECT_EQ(\n      absl::Substitute(\"$0.$1.$2\", TACHYON_CC_VERSION_MAJOR,\n                       TACHYON_CC_VERSION_MINOR, TACHYON_CC_VERSION_PATCH),\n      TACHYON_CC_VERSION_STR);\n  EXPECT_EQ(TACHYON_CC_VERSION_MAJOR * 10000 + TACHYON_CC_VERSION_MINOR * 100 +\n                TACHYON_CC_VERSION_PATCH,\n            TACHYON_CC_VERSION);\n}\n\nTEST(VersionTest, RunTimeVersionTest) {\n  EXPECT_EQ(TACHYON_CC_VERSION, GetRuntimeVersion());\n  EXPECT_EQ(TACHYON_CC_VERSION_STR, GetRuntimeVersionStr());\n  EXPECT_EQ(TACHYON_CC_VERSION_FULL_STR, GetRuntimeFullVersionStr());\n}\n\n}  // namespace tachyon::cc\n"
  },
  {
    "path": "tachyon/crypto/challenger/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"challenger\",\n    hdrs = [\"challenger.h\"],\n    deps = [\n        \":challenger_traits_forward\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base:parallelize\",\n        \"//tachyon/base:profiler\",\n        \"//tachyon/base:range\",\n        \"//tachyon/base/containers:container_util\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"challenger_traits_forward\",\n    hdrs = [\"challenger_traits_forward.h\"],\n)\n\ntachyon_cc_library(\n    name = \"duplex_challenger\",\n    hdrs = [\"duplex_challenger.h\"],\n    deps = [\n        \":challenger\",\n        \"//tachyon/crypto/hashes/sponge:sponge_state\",\n        \"@com_google_absl//absl/container:inlined_vector\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"hash_challenger\",\n    hdrs = [\"hash_challenger.h\"],\n    deps = [\n        \":challenger\",\n        \"//tachyon/base/containers:container_util\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"multi_field32_challenger\",\n    hdrs = [\"multi_field32_challenger.h\"],\n    deps = [\n        \":challenger\",\n        \":multi_field32_conversions\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/crypto/hashes/sponge:sponge_state\",\n        \"@com_google_absl//absl/container:inlined_vector\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"multi_field32_conversions\",\n    hdrs = [\"multi_field32_conversions.h\"],\n    deps = [\n        \"//tachyon/base/containers:adapters\",\n        \"//tachyon/build:build_config\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"challenger_unittests\",\n    srcs = [\n        \"duplex_challenger_unittest.cc\",\n        \"hash_challenger_unittest.cc\",\n        \"multi_field32_challenger_unittest.cc\",\n    ],\n    deps = [\n        \":duplex_challenger\",\n        \":hash_challenger\",\n        \":multi_field32_challenger\",\n        \"//tachyon/crypto/hashes/sponge:padding_free_sponge\",\n        \"//tachyon/crypto/hashes/sponge/poseidon2\",\n        \"//tachyon/crypto/hashes/sponge/poseidon2:poseidon2_params\",\n        \"//tachyon/crypto/hashes/sponge/poseidon2/param_traits:poseidon2_baby_bear\",\n        \"//tachyon/crypto/hashes/sponge/poseidon2/param_traits:poseidon2_bn254\",\n        \"//tachyon/math/finite_fields/test:finite_field_test\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/crypto/challenger/challenger.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_CRYPTO_CHALLENGER_CHALLENGER_H_\n#define TACHYON_CRYPTO_CHALLENGER_CHALLENGER_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <algorithm>\n#include <array>\n#include <atomic>\n#include <limits>\n#include <vector>\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/parallelize.h\"\n#include \"tachyon/base/profiler.h\"\n#include \"tachyon/base/range.h\"\n#include \"tachyon/crypto/challenger/challenger_traits_forward.h\"\n\nnamespace tachyon::crypto {\n\ntemplate <typename Derived>\nclass Challenger {\n public:\n  using Field = typename ChallengerTraits<Derived>::Field;\n\n  template <typename T>\n  void Observe(const T& value) {\n    Derived* derived = static_cast<Derived*>(this);\n    derived->DoObserve(value);\n  }\n\n  template <typename Container>\n  void ObserveContainer(const Container& container) {\n    Derived* derived = static_cast<Derived*>(this);\n    for (const auto& value : container) {\n      derived->DoObserve(value);\n    }\n  }\n\n  template <typename Container>\n  void ObserveContainer2D(const Container& container_2d) {\n    Derived* derived = static_cast<Derived*>(this);\n    for (const auto& container : container_2d) {\n      for (const auto& value : container) {\n        derived->DoObserve(value);\n      }\n    }\n  }\n\n  Field Sample() {\n    Derived* derived = static_cast<Derived*>(this);\n    return derived->DoSample();\n  }\n\n  template <size_t N>\n  std::array<Field, N> SampleArray() {\n    return base::CreateArray<N>([this]() { return Sample(); });\n  }\n\n  template <typename ExtField>\n  ExtField SampleExtElement() {\n    constexpr size_t N = ExtField::kDegreeOverBasePrimeField;\n    using F = typename ExtField::BasePrimeField;\n    static_assert(std::is_same_v<F, Field>);\n    std::array<F, N> prime_fields = SampleArray<N>();\n    return ExtField::FromBasePrimeFields(prime_fields);\n  }\n\n  uint32_t SampleBits(uint32_t bits) {\n    static_assert(Field::Config::kModulusBits <= 32);\n    DCHECK_LT(bits, sizeof(uint32_t) * 8);\n    DCHECK_LT(uint32_t{1} << bits, Field::Config::kModulus);\n    Field rand_f = Sample();\n    uint32_t rand_size;\n    if constexpr (Field::Config::kUseMontgomery) {\n      rand_size = Field::Config::FromMontgomery(rand_f.value());\n    } else {\n      rand_size = rand_f.value();\n    }\n    return rand_size & ((uint32_t{1} << bits) - 1);\n  }\n\n  Field Grind(uint32_t bits, std::optional<Field> pow_witness = std::nullopt) {\n    return Grind(bits, base::Range<uint32_t>::Until(Field::Config::kModulus),\n                 pow_witness);\n  }\n\n  Field Grind(uint32_t bits, base::Range<uint32_t> range,\n              std::optional<Field> pow_witness = std::nullopt) {\n    TRACE_EVENT(\"ProofGeneration\", \"Challenger::Grind\");\n    std::atomic<bool> found(false);\n    std::vector<uint32_t> ret = base::ParallelizeMap(\n        range.GetSize(),\n        [this, bits, range, &found](uint32_t len, uint32_t chunk_offset,\n                                    uint32_t chunk_size) {\n          uint32_t ret = std::numeric_limits<uint32_t>::max();\n          uint32_t start = range.from + chunk_offset * chunk_size;\n          uint32_t end = start + std::min(range.to - start, chunk_size);\n          Field f(start);\n          for (uint32_t i = start; i < end; ++i) {\n            if (found.load(std::memory_order_acquire)) break;\n            Derived derived = *static_cast<Derived*>(this);\n            if (derived.CheckWitness(bits, f)) {\n              found.store(true, std::memory_order_release);\n              ret = i;\n              break;\n            }\n            f += Field::One();\n          }\n          return ret;\n        });\n    auto it = std::find_if(ret.begin(), ret.end(), [](uint32_t v) {\n      return v != std::numeric_limits<uint32_t>::max();\n    });\n    CHECK(it != ret.end());\n    if (pow_witness) {\n      CHECK(CheckWitness(bits, *pow_witness));\n      return *pow_witness;\n    }\n    CHECK(CheckWitness(bits, Field(*it)));\n    return Field(*it);\n  }\n\n  [[nodiscard]] bool CheckWitness(uint32_t bits, const Field& witness) {\n    Observe(witness);\n    return SampleBits(bits) == 0;\n  }\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_CHALLENGER_CHALLENGER_H_\n"
  },
  {
    "path": "tachyon/crypto/challenger/challenger_traits_forward.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_CRYPTO_CHALLENGER_CHALLENGER_TRAITS_FORWARD_H_\n#define TACHYON_CRYPTO_CHALLENGER_CHALLENGER_TRAITS_FORWARD_H_\n\nnamespace tachyon::crypto {\n\ntemplate <typename T>\nstruct ChallengerTraits;\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_CHALLENGER_CHALLENGER_TRAITS_FORWARD_H_\n"
  },
  {
    "path": "tachyon/crypto/challenger/duplex_challenger.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_CRYPTO_CHALLENGER_DUPLEX_CHALLENGER_H_\n#define TACHYON_CRYPTO_CHALLENGER_DUPLEX_CHALLENGER_H_\n\n#include <utility>\n\n#include \"absl/container/inlined_vector.h\"\n\n#include \"tachyon/crypto/challenger/challenger.h\"\n#include \"tachyon/crypto/hashes/sponge/sponge_state.h\"\n\nnamespace tachyon {\n\nclass HintableTest_DuplexChallenger_Test;\n\nnamespace crypto {\n\ntemplate <typename Permutation, size_t R>\nclass DuplexChallenger final\n    : public Challenger<DuplexChallenger<Permutation, R>> {\n public:\n  using Params = typename Permutation::Params;\n  using F = typename Params::Field;\n\n  DuplexChallenger() = default;\n  explicit DuplexChallenger(Permutation&& permutation)\n      : permutation_(std::move(permutation)) {}\n\n  const SpongeState<Params>& state() const { return state_; }\n  const absl::InlinedVector<F, R>& input_buffer() const {\n    return input_buffer_;\n  }\n  const absl::InlinedVector<F, Params::kWidth>& output_buffer() const {\n    return output_buffer_;\n  }\n\n private:\n  friend class Challenger<DuplexChallenger<Permutation, R>>;\n  friend class tachyon::HintableTest_DuplexChallenger_Test;\n\n  // Challenger methods\n  void DoObserve(const F& value) {\n    output_buffer_.clear();\n\n    input_buffer_.push_back(value);\n\n    if (input_buffer_.size() == R) {\n      Duplex();\n    }\n  }\n\n  F DoSample() {\n    if (!input_buffer_.empty() || output_buffer_.empty()) {\n      Duplex();\n    }\n\n    F ret = std::move(output_buffer_.back());\n    output_buffer_.pop_back();\n    return ret;\n  }\n\n  void Duplex() {\n    for (size_t i = 0; i < input_buffer_.size(); ++i) {\n      state_[i] = std::move(input_buffer_[i]);\n    }\n    input_buffer_.clear();\n\n    permutation_.Permute(state_);\n\n    output_buffer_.clear();\n    for (size_t i = 0; i < Params::kWidth; ++i) {\n      output_buffer_.push_back(state_[i]);\n    }\n  }\n\n  SpongeState<Params> state_;\n  absl::InlinedVector<F, R> input_buffer_;\n  absl::InlinedVector<F, Params::kWidth> output_buffer_;\n  Permutation permutation_;\n};\n\ntemplate <typename Permutation, size_t R>\nstruct ChallengerTraits<DuplexChallenger<Permutation, R>> {\n  using Field = typename Permutation::F;\n};\n\n}  // namespace crypto\n}  // namespace tachyon\n\n#endif  // TACHYON_CRYPTO_CHALLENGER_DUPLEX_CHALLENGER_H_\n"
  },
  {
    "path": "tachyon/crypto/challenger/duplex_challenger_unittest.cc",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#include \"tachyon/crypto/challenger/duplex_challenger.h\"\n\n#include <utility>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/bits.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/param_traits/poseidon2_baby_bear.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_params.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n\nnamespace tachyon::crypto {\n\nusing F = math::BabyBear;\nusing Params = Poseidon2Params<Poseidon2Vendor::kPlonky3,\n                               Poseidon2Vendor::kPlonky3, F, 15, 7>;\nusing Poseidon2 = Poseidon2Sponge<Params>;\n\nnamespace {\n\nclass DuplexChallengerTest : public math::FiniteFieldTest<F> {\n public:\n  constexpr static size_t kRate = 4;\n\n  void SetUp() override {\n    Poseidon2 sponge;\n    challenger_ = DuplexChallenger<Poseidon2, kRate>(std::move(sponge));\n  }\n\n protected:\n  DuplexChallenger<Poseidon2, kRate> challenger_;\n};\n\n}  // namespace\n\nTEST_F(DuplexChallengerTest, Sample) {\n  for (uint32_t i = 0; i < 20; ++i) {\n    challenger_.Observe(F(i));\n  }\n\n  F answers[] = {\n      F(1091695522), F(747772208), F(1145639564), F(1789312616), F(567623980),\n      F(179016966),  F(125050365), F(1725901131), F(65962335),   F(1086560956),\n  };\n  for (size_t i = 0; i < std::size(answers); ++i) {\n    EXPECT_EQ(challenger_.Sample(), answers[i]);\n  }\n}\n\nTEST_F(DuplexChallengerTest, Grind) {\n  const uint32_t kBits = 3;\n  DuplexChallenger<Poseidon2, kRate> challenger_clone = challenger_;\n  F witness = challenger_.Grind(kBits, base::Range<uint32_t>(0, 100));\n  EXPECT_TRUE(challenger_clone.CheckWitness(kBits, witness));\n}\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/crypto/challenger/hash_challenger.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_CRYPTO_CHALLENGER_HASH_CHALLENGER_H_\n#define TACHYON_CRYPTO_CHALLENGER_HASH_CHALLENGER_H_\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/crypto/challenger/challenger.h\"\n\nnamespace tachyon::crypto {\n\ntemplate <typename Hasher>\nclass HashChallenger final : public Challenger<HashChallenger<Hasher>> {\n public:\n  using F = typename Hasher::F;\n\n  explicit HashChallenger(Hasher&& hasher) : hasher_(std::move(hasher)) {}\n  HashChallenger(const std::vector<F>& input_buffer, Hasher&& hasher)\n      : input_buffer_(input_buffer), hasher_(std::move(hasher)) {}\n  HashChallenger(std::vector<F>&& input_buffer, Hasher&& hasher)\n      : input_buffer_(std::move(input_buffer)), hasher_(std::move(hasher)) {}\n\n private:\n  friend class Challenger<HashChallenger<Hasher>>;\n\n  // Challenger methods\n  void DoObserve(const F& value) {\n    output_buffer_.clear();\n\n    input_buffer_.push_back(value);\n  }\n\n  F DoSample() {\n    if (output_buffer_.empty()) {\n      Flush();\n    }\n\n    F ret = std::move(output_buffer_.back());\n    output_buffer_.pop_back();\n    return ret;\n  }\n\n  void Flush() {\n    auto output = hasher_.Hash(input_buffer_);\n\n    output_buffer_ =\n        base::Map(output, [](F& value) { return std::move(value); });\n    input_buffer_ = output_buffer_;\n  }\n\n  std::vector<F> input_buffer_;\n  std::vector<F> output_buffer_;\n  Hasher hasher_;\n};\n\ntemplate <typename Hasher>\nstruct ChallengerTraits<HashChallenger<Hasher>> {\n  using Field = typename Hasher::F;\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_CHALLENGER_HASH_CHALLENGER_H_\n"
  },
  {
    "path": "tachyon/crypto/challenger/hash_challenger_unittest.cc",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#include \"tachyon/crypto/challenger/hash_challenger.h\"\n\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/crypto/hashes/sponge/padding_free_sponge.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/param_traits/poseidon2_baby_bear.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_params.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n\nnamespace tachyon::crypto {\n\nusing F = math::BabyBear;\nusing Params = Poseidon2Params<Poseidon2Vendor::kPlonky3,\n                               Poseidon2Vendor::kPlonky3, F, 15, 7>;\nusing Poseidon2 = Poseidon2Sponge<Params>;\nusing MyHasher = PaddingFreeSponge<Poseidon2, 8, 8>;\n\nnamespace {\n\nclass HashChallengerTest : public math::FiniteFieldTest<F> {\n public:\n  void SetUp() override {\n    MyHasher hasher;\n\n    std::vector<F> initial_state =\n        base::CreateVector(10, [](size_t i) { return F(i + 1); });\n    challenger_.reset(new HashChallenger<MyHasher>(std::move(initial_state),\n                                                   std::move(hasher)));\n  }\n\n protected:\n  std::unique_ptr<HashChallenger<MyHasher>> challenger_;\n};\n\n}  // namespace\n\nTEST_F(HashChallengerTest, Sample) {\n  for (uint32_t i = 0; i < 20; ++i) {\n    challenger_->Observe(F(i));\n  }\n\n  F answers[] = {\n      F(886174168),  F(1457271233), F(1952268252), F(1595005924), F(796215768),\n      F(1553987485), F(1108393593), F(1336137665), F(971109448),  F(1853357459),\n  };\n  for (size_t i = 0; i < std::size(answers); ++i) {\n    EXPECT_EQ(challenger_->Sample(), answers[i]);\n  }\n}\n\n// NOTE(chokobole): Grind is not tested since |HashChallenger| doesn't implement\n// |GrindingChallenger| traits.\n// See\n// https://github.com/Plonky3/Plonky3/blob/7bb6db5/challenger/src/grinding_challenger.rs.\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/crypto/challenger/multi_field32_challenger.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_CRYPTO_CHALLENGER_MULTI_FIELD32_CHALLENGER_H_\n#define TACHYON_CRYPTO_CHALLENGER_MULTI_FIELD32_CHALLENGER_H_\n\n#include <algorithm>\n#include <utility>\n\n#include \"absl/container/inlined_vector.h\"\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/crypto/challenger/challenger.h\"\n#include \"tachyon/crypto/challenger/multi_field32_conversions.h\"\n#include \"tachyon/crypto/hashes/sponge/sponge_state.h\"\n\nnamespace tachyon::crypto {\n\n// A challenger that operates natively on |BigF| but produces challenges of\n// |SmallF|.\n//\n// Used for optimizing the cost of recursive proof verification of STARKs in\n// SNARKs.\n//\n// SAFETY: There are some bias complications with using this challenger. In\n// particular, samples are actually random in [0, 2⁶⁴) and then reduced to be\n// in |SmallF|.\ntemplate <typename SmallF, typename Permutation>\nclass MultiField32Challenger final\n    : public Challenger<MultiField32Challenger<SmallF, Permutation>> {\n public:\n  using Params = typename Permutation::Params;\n  using BigF = typename Permutation::F;\n\n  static_assert(BigF::Config::kModulusBits > 64);\n  static_assert(SmallF::Config::kModulusBits <= 32);\n\n  constexpr static size_t kNumFElements = BigF::Config::kModulusBits / 64;\n  constexpr static size_t R = kNumFElements * Params::kWidth;\n\n  explicit MultiField32Challenger(Permutation&& permutation)\n      : permutation_(std::move(permutation)) {}\n\n private:\n  friend class Challenger<MultiField32Challenger<SmallF, Permutation>>;\n\n  // Challenger methods\n  void DoObserve(const SmallF& value) {\n    output_buffer_.clear();\n\n    input_buffer_.push_back(value);\n\n    if (input_buffer_.size() == R) {\n      Duplex();\n    }\n  }\n\n  SmallF DoSample() {\n    if (!input_buffer_.empty() || output_buffer_.empty()) {\n      Duplex();\n    }\n\n    SmallF ret = std::move(output_buffer_.back());\n    output_buffer_.pop_back();\n    return ret;\n  }\n\n  void Duplex() {\n    for (size_t i = 0;\n         i < (input_buffer_.size() + kNumFElements - 1) / kNumFElements; ++i) {\n      size_t start = i * kNumFElements;\n      size_t len = std::min(input_buffer_.size() - start, kNumFElements);\n      state_[i] =\n          Reduce<BigF>(absl::Span<const SmallF>(&input_buffer_[start], len));\n    }\n    input_buffer_.clear();\n\n    permutation_.Permute(state_);\n\n    output_buffer_.clear();\n    for (size_t i = 0; i < Params::kWidth; ++i) {\n      std::array<SmallF, kNumFElements> values = Split<SmallF>(state_[i]);\n      for (size_t j = 0; j < kNumFElements; ++j) {\n        output_buffer_.push_back(values[j]);\n      }\n    }\n  }\n\n  SpongeState<Params> state_;\n  absl::InlinedVector<SmallF, R> input_buffer_;\n  absl::InlinedVector<SmallF, Params::kWidth * kNumFElements> output_buffer_;\n  Permutation permutation_;\n};\n\ntemplate <typename SmallF, typename Permutation>\nstruct ChallengerTraits<MultiField32Challenger<SmallF, Permutation>> {\n  using Field = SmallF;\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_CHALLENGER_MULTI_FIELD32_CHALLENGER_H_\n"
  },
  {
    "path": "tachyon/crypto/challenger/multi_field32_challenger_unittest.cc",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#include \"tachyon/crypto/challenger/multi_field32_challenger.h\"\n\n#include <memory>\n#include <utility>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/crypto/hashes/sponge/poseidon2/param_traits/poseidon2_baby_bear.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/param_traits/poseidon2_bn254.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_params.h\"\n\nnamespace tachyon::crypto {\n\nusing Params =\n    Poseidon2Params<Poseidon2Vendor::kPlonky3, Poseidon2Vendor::kPlonky3,\n                    math::bn254::Fr, 2, 5>;\nusing Poseidon2 = Poseidon2Sponge<Params>;\n\nnamespace {\n\nclass MultiField32ChallengerTest : public testing::Test {\n public:\n  static void SetUpTestSuite() {\n    math::BabyBear::Init();\n    math::bn254::Fr::Init();\n  }\n\n  void SetUp() override {\n    auto config = Poseidon2Config<Params>::Create(\n        crypto::GetPoseidon2InternalDiagonalArray<Params>());\n    Poseidon2 sponge(std::move(config));\n    challenger_.reset(new MultiField32Challenger<math::BabyBear, Poseidon2>(\n        std::move(sponge)));\n  }\n\n protected:\n  std::unique_ptr<MultiField32Challenger<math::BabyBear, Poseidon2>>\n      challenger_;\n};\n\n}  // namespace\n\nTEST_F(MultiField32ChallengerTest, Sample) {\n  for (uint32_t i = 0; i < 20; ++i) {\n    challenger_->Observe(math::BabyBear(i));\n  }\n\n  math::BabyBear answers[] = {\n      math::BabyBear(72199253),   math::BabyBear(733473132),\n      math::BabyBear(442816494),  math::BabyBear(326641700),\n      math::BabyBear(1342573676), math::BabyBear(1242755868),\n      math::BabyBear(887300172),  math::BabyBear(1831922292),\n      math::BabyBear(1518709680),\n  };\n  for (size_t i = 0; i < std::size(answers); ++i) {\n    EXPECT_EQ(challenger_->Sample(), answers[i]);\n  }\n}\n\nTEST_F(MultiField32ChallengerTest, Grind) {\n  const uint32_t kBits = 3;\n  MultiField32Challenger<math::BabyBear, Poseidon2> challenger_clone =\n      *challenger_;\n  math::BabyBear witness =\n      challenger_->Grind(kBits, base::Range<uint32_t>(0, 100));\n  EXPECT_TRUE(challenger_clone.CheckWitness(kBits, witness));\n}\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/crypto/challenger/multi_field32_conversions.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_CRYPTO_CHALLENGER_MULTI_FIELD32_CONVERSIONS_H_\n#define TACHYON_CRYPTO_CHALLENGER_MULTI_FIELD32_CONVERSIONS_H_\n\n#include <stdint.h>\n\n#include <array>\n#include <limits>\n\n#include \"absl/types/span.h\"\n\n#include \"tachyon/base/containers/adapters.h\"\n#include \"tachyon/build/build_config.h\"\n\nnamespace tachyon::crypto {\n\ntemplate <typename BigF, typename SmallF>\nBigF Reduce(absl::Span<const SmallF> values) {\n  static_assert(SmallF::Config::kModulusBits <= 32);\n  static_assert(BigF::Config::kModulusBits > 64);\n\n  using BigInt = typename BigF::BigIntTy;\n  CHECK_LT(values.size(), BigInt::kLimbNums * 2);\n\n  BigInt ret(0);\n  for (size_t i = 0; i < values.size(); i += 2) {\n    uint32_t value = values[i].value();\n    if constexpr (SmallF::Config::kUseMontgomery) {\n      ret[i >> 1] = SmallF::Config::FromMontgomery(value);\n    } else {\n      ret[i >> 1] = value;\n    }\n    if (i < values.size() - 1) {\n      uint64_t value2 = values[i + 1].value();\n      if constexpr (SmallF::Config::kUseMontgomery) {\n        ret[i >> 1] += uint64_t{SmallF::Config::FromMontgomery(value2)} << 32;\n      } else {\n        ret[i >> 1] += value2 << 32;\n      }\n    }\n  }\n  return BigF(ret % BigF::Config::kModulus);\n}\n\ntemplate <typename SmallF, typename BigF,\n          size_t N = BigF::Config::kModulusBits / 64>\nstd::array<SmallF, N> Split(const BigF& value) {\n  static_assert(SmallF::Config::kModulusBits <= 32);\n  static_assert(BigF::Config::kModulusBits > 64);\n  static_assert(ARCH_CPU_LITTLE_ENDIAN);\n\n  using BigInt = typename BigF::BigIntTy;\n  std::array<SmallF, N> ret;\n  BigInt value_bigint = value.ToBigInt();\n  for (size_t i = 0; i < N; ++i) {\n    uint64_t digit = value_bigint[0] & std::numeric_limits<uint64_t>::max();\n    if constexpr (SmallF::Config::kUseMontgomery) {\n      ret[i] = SmallF::FromMontgomery(SmallF::Config::ToMontgomery(digit));\n    } else {\n      ret[i] = SmallF(digit);\n    }\n    value_bigint >>= 64;\n  }\n  return ret;\n}\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_CHALLENGER_MULTI_FIELD32_CONVERSIONS_H_\n"
  },
  {
    "path": "tachyon/crypto/commitments/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"batch_commitment_state\",\n    hdrs = [\"batch_commitment_state.h\"],\n    deps = [\"//tachyon:export\"],\n)\n\ntachyon_cc_library(\n    name = \"mixed_matrix_commitment_scheme\",\n    hdrs = [\"mixed_matrix_commitment_scheme.h\"],\n    deps = [\n        \":mixed_matrix_commitment_scheme_traits_forward\",\n        \"//tachyon/base:openmp_util\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/math/geometry:dimensions\",\n        \"//tachyon/math/matrix:matrix_types\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"mixed_matrix_commitment_scheme_traits_forward\",\n    hdrs = [\"mixed_matrix_commitment_scheme_traits_forward.h\"],\n)\n\ntachyon_cc_library(\n    name = \"polynomial_openings\",\n    hdrs = [\"polynomial_openings.h\"],\n    deps = [\n        \"//tachyon/base:logging\",\n        \"//tachyon/base:openmp_util\",\n        \"//tachyon/base:optional\",\n        \"//tachyon/base:ref\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/math/polynomials/univariate:lagrange_interpolation\",\n        \"//tachyon/math/polynomials/univariate:univariate_polynomial\",\n        \"@com_google_absl//absl/container:btree\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"univariate_polynomial_commitment_scheme\",\n    hdrs = [\"univariate_polynomial_commitment_scheme.h\"],\n    deps = [\n        \":vector_commitment_scheme\",\n        \"//tachyon/math/polynomials/univariate:univariate_evaluations\",\n        \"//tachyon/math/polynomials/univariate:univariate_polynomial\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"vector_commitment_scheme\",\n    hdrs = [\"vector_commitment_scheme.h\"],\n    deps = [\n        \":batch_commitment_state\",\n        \":vector_commitment_scheme_traits_forward\",\n        \"//tachyon/base:bits\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"vector_commitment_scheme_traits_forward\",\n    hdrs = [\"vector_commitment_scheme_traits_forward.h\"],\n)\n\ntachyon_cc_unittest(\n    name = \"commitments_unittests\",\n    srcs = [\"polynomial_openings_unittest.cc\"],\n    deps = [\n        \":polynomial_openings\",\n        \"//tachyon/base/containers:contains\",\n        \"//tachyon/math/finite_fields/test:finite_field_test\",\n        \"//tachyon/math/finite_fields/test:gf7\",\n        \"//tachyon/math/polynomials/univariate:univariate_polynomial\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/crypto/commitments/batch_commitment_state.h",
    "content": "#ifndef TACHYON_CRYPTO_COMMITMENTS_BATCH_COMMITMENT_STATE_H_\n#define TACHYON_CRYPTO_COMMITMENTS_BATCH_COMMITMENT_STATE_H_\n\n#include <stddef.h>\n\n#include \"tachyon/export.h\"\n\nnamespace tachyon::crypto {\n\nstruct TACHYON_EXPORT BatchCommitmentState {\n  bool batch_mode = false;\n  size_t batch_count = 0;\n\n  constexpr BatchCommitmentState() = default;\n  constexpr BatchCommitmentState(bool batch_mode, size_t batch_count)\n      : batch_mode(batch_mode), batch_count(batch_count) {}\n\n  void Reset() {\n    batch_mode = false;\n    batch_count = 0;\n  }\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_COMMITMENTS_BATCH_COMMITMENT_STATE_H_\n"
  },
  {
    "path": "tachyon/crypto/commitments/fri/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"fri_config\",\n    hdrs = [\"fri_config.h\"],\n    deps = [\n        \"//tachyon/base:optional\",\n        \"//tachyon/math/finite_fields:extension_field_traits_forward\",\n        \"//tachyon/math/polynomials/univariate:evaluations_utils\",\n        \"@eigen_archive//:eigen3\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"fri_proof\",\n    hdrs = [\"fri_proof.h\"],\n    deps = [\n        \"//tachyon/base/buffer:copyable\",\n        \"//tachyon/base/strings:string_util\",\n        \"//tachyon/crypto/commitments:mixed_matrix_commitment_scheme_traits_forward\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"lagrange_selectors\",\n    hdrs = [\"lagrange_selectors.h\"],\n)\n\ntachyon_cc_library(\n    name = \"prove\",\n    hdrs = [\"prove.h\"],\n    deps = [\n        \":fri_config\",\n        \":fri_proof\",\n        \"//tachyon/base:bits\",\n        \"//tachyon/crypto/challenger\",\n        \"//tachyon/math/finite_fields:extension_field_traits_forward\",\n        \"//tachyon/math/matrix:matrix_types\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"simple_fri\",\n    hdrs = [\"simple_fri.h\"],\n    deps = [\n        \":simple_fri_proof\",\n        \":simple_fri_storage\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base:optional\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/crypto/commitments:univariate_polynomial_commitment_scheme\",\n        \"//tachyon/crypto/commitments/merkle_tree/binary_merkle_tree\",\n        \"//tachyon/crypto/transcripts:transcript\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"simple_fri_proof\",\n    hdrs = [\"simple_fri_proof.h\"],\n    deps = [\"//tachyon/crypto/commitments/merkle_tree/binary_merkle_tree:binary_merkle_proof\"],\n)\n\ntachyon_cc_library(\n    name = \"simple_fri_storage\",\n    hdrs = [\"simple_fri_storage.h\"],\n    deps = [\"//tachyon/crypto/commitments/merkle_tree/binary_merkle_tree:binary_merkle_tree_storage\"],\n)\n\ntachyon_cc_library(\n    name = \"two_adic_fri\",\n    hdrs = [\"two_adic_fri.h\"],\n    deps = [\n        \":fri_proof\",\n        \":prove\",\n        \":two_adic_multiplicative_coset\",\n        \":verify\",\n        \"//tachyon/base:bits\",\n        \"//tachyon/base:parallelize\",\n        \"//tachyon/base:profiler\",\n        \"//tachyon/crypto/commitments:mixed_matrix_commitment_scheme_traits_forward\",\n        \"//tachyon/math/finite_fields:extension_field_traits_forward\",\n        \"//tachyon/math/geometry:dimensions\",\n        \"//tachyon/math/matrix:matrix_types\",\n        \"//tachyon/math/matrix:matrix_utils\",\n        \"//tachyon/math/polynomials/univariate:evaluations_utils\",\n        \"@com_google_absl//absl/container:btree\",\n        \"@com_google_absl//absl/container:flat_hash_map\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"two_adic_multiplicative_coset\",\n    hdrs = [\"two_adic_multiplicative_coset.h\"],\n    deps = [\n        \":lagrange_selectors\",\n        \"//tachyon/base:bits\",\n        \"//tachyon/base:optional\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/math/polynomials/univariate:radix2_evaluation_domain\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"verify\",\n    hdrs = [\"verify.h\"],\n    deps = [\n        \":fri_config\",\n        \":fri_proof\",\n        \"//tachyon/base:bits\",\n        \"//tachyon/base/ranges:algorithm\",\n        \"//tachyon/math/geometry:dimensions\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"fri_unittests\",\n    srcs = [\n        \"fri_config_unittest.cc\",\n        \"simple_fri_unittest.cc\",\n        \"two_adic_fri_unittest.cc\",\n        \"two_adic_multiplicative_coset_unittest.cc\",\n    ],\n    deps = [\n        \":fri_config\",\n        \":simple_fri\",\n        \":two_adic_fri\",\n        \":two_adic_multiplicative_coset\",\n        \"//tachyon/crypto/challenger:duplex_challenger\",\n        \"//tachyon/crypto/commitments/merkle_tree/binary_merkle_tree\",\n        \"//tachyon/crypto/commitments/merkle_tree/binary_merkle_tree:binary_merkle_hasher\",\n        \"//tachyon/crypto/commitments/merkle_tree/binary_merkle_tree:simple_binary_merkle_tree_storage\",\n        \"//tachyon/crypto/commitments/merkle_tree/field_merkle_tree:extension_field_merkle_tree_mmcs\",\n        \"//tachyon/crypto/commitments/merkle_tree/field_merkle_tree:field_merkle_tree_mmcs\",\n        \"//tachyon/crypto/hashes/sponge:padding_free_sponge\",\n        \"//tachyon/crypto/hashes/sponge:truncated_permutation\",\n        \"//tachyon/crypto/hashes/sponge/poseidon2\",\n        \"//tachyon/crypto/hashes/sponge/poseidon2:poseidon2_params\",\n        \"//tachyon/crypto/hashes/sponge/poseidon2/param_traits:poseidon2_baby_bear\",\n        \"//tachyon/crypto/transcripts:simple_transcript\",\n        \"//tachyon/math/finite_fields/baby_bear:baby_bear4\",\n        \"//tachyon/math/finite_fields/goldilocks\",\n        \"//tachyon/math/finite_fields/test:finite_field_test\",\n        \"//tachyon/math/matrix:matrix_types\",\n        \"//tachyon/math/polynomials/univariate:univariate_evaluation_domain_factory\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/crypto/commitments/fri/fri_config.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_CRYPTO_COMMITMENTS_FRI_FRI_CONFIG_H_\n#define TACHYON_CRYPTO_COMMITMENTS_FRI_FRI_CONFIG_H_\n\n#include <vector>\n\n#include \"third_party/eigen3/Eigen/Core\"\n\n#include \"tachyon/base/optional.h\"\n#include \"tachyon/math/finite_fields/extension_field_traits_forward.h\"\n#include \"tachyon/math/polynomials/univariate/evaluations_utils.h\"\n\nnamespace tachyon::crypto {\n\ntemplate <typename MMCS>\nstruct FRIConfig {\n  uint32_t log_blowup;\n  size_t num_queries;\n  size_t proof_of_work_bits;\n  MMCS mmcs;\n\n  size_t Blowup() const { return size_t{1} << log_blowup; }\n};\n\n// NOTE(ashjeong): |kLogArity| is subject to change in the future\ntemplate <typename ExtF, typename Derived>\nstd::vector<ExtF> FoldMatrix(const ExtF& beta,\n                             const Eigen::MatrixBase<Derived>& mat) {\n  using F = typename math::ExtensionFieldTraits<ExtF>::BaseField;\n  const size_t kLogArity = 1;\n  // We use the fact that\n  //     pₑ(x²) = (p(x) + p(-x)) / 2\n  //     pₒ(x²) = (p(x) - p(-x)) / (2x)\n  // that is,\n  //     pₑ(g²ⁱ) = (p(gⁱ) + p(gⁿᐟ²⁺ⁱ)) / 2\n  //     pₒ(g²ⁱ) = (p(gⁱ) - p(gⁿᐟ²⁺ⁱ)) / (2gⁱ)\n  // so\n  //     result(g²ⁱ) = pₑ(g²ⁱ) + βpₒ(g²ⁱ)\n  //                    = (1/2 + β/2gᵢₙᵥⁱ)p(gⁱ)\n  //                    + (1/2 - β/2gᵢₙᵥⁱ)p(gⁿᐟ²⁺ⁱ)\n  size_t rows = static_cast<size_t>(mat.rows());\n  F w;\n  CHECK(F::GetRootOfUnity(\n      size_t{1} << (base::bits::CheckedLog2(rows) + kLogArity), &w));\n  ExtF w_inv = ExtF(unwrap(w.Inverse()));\n  ExtF half_beta = beta * ExtF::TwoInv();\n\n  // β/2 times successive powers of gᵢₙᵥ\n  std::vector<ExtF> powers =\n      ExtF::GetBitRevIndexSuccessivePowers(rows, w_inv, half_beta);\n\n  std::vector<ExtF> ret(rows);\n  OMP_PARALLEL_FOR(size_t r = 0; r < rows; ++r) {\n    const ExtF& lo = mat(r, 0);\n    const ExtF& hi = mat(r, 1);\n    ret[r] =\n        (ExtF::TwoInv() + powers[r]) * lo + (ExtF::TwoInv() - powers[r]) * hi;\n  }\n  return ret;\n}\n\n// NOTE(ashjeong): |kLogArity| is subject to change in the future\ntemplate <typename ExtF>\nExtF FoldRow(size_t index, uint32_t log_num_rows, const ExtF& beta,\n             const std::vector<ExtF>& evals) {\n  using F = typename math::ExtensionFieldTraits<ExtF>::BaseField;\n  const size_t kLogArity = 1;\n\n  F w;\n  CHECK(F::GetRootOfUnity(size_t{1} << (log_num_rows + kLogArity), &w));\n  ExtF w_inv = ExtF(unwrap(w.Inverse()));\n  ExtF half_beta = beta * ExtF::TwoInv();\n  ExtF power =\n      ExtF(w_inv.Pow(base::bits::ReverseBitsLen(index, log_num_rows))) *\n      half_beta;\n\n  // result(g²ⁱ) = (1/2 + β/2gᵢₙᵥⁱ)p(gⁱ) + (1/2 - β/2gᵢₙᵥⁱ)p(gⁿᐟ²⁺ⁱ)\n  const ExtF& lo = evals[0];\n  const ExtF& hi = evals[1];\n  return (ExtF::TwoInv() + power) * lo + (ExtF::TwoInv() - power) * hi;\n}\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_COMMITMENTS_FRI_FRI_CONFIG_H_\n"
  },
  {
    "path": "tachyon/crypto/commitments/fri/fri_config_unittest.cc",
    "content": "#include \"tachyon/crypto/commitments/fri/fri_config.h\"\n\n#include \"tachyon/math/finite_fields/baby_bear/baby_bear4.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/matrix/matrix_types.h\"\n\nnamespace tachyon::crypto {\nnamespace {\n\nusing F = math::BabyBear;\nusing ExtF = math::BabyBear4;\n\nclass FRIConfigTest : public math::FiniteFieldTest<ExtF> {};\n\n}  // namespace\n\nTEST_F(FRIConfigTest, FoldMatrix) {\n  math::RowMajorMatrix<ExtF> inside(4, 2);\n  inside << ExtF(F(7)), ExtF(F(1)), ExtF(F(2)), ExtF(F(3)), ExtF(F(5)),\n      ExtF(F(3)), ExtF(F(2)), ExtF(F(5));\n  std::vector<ExtF> result = FoldMatrix(ExtF(F(28)), inside);\n  EXPECT_EQ(result[0], ExtF(F(88)));\n  EXPECT_EQ(result[1], ExtF(F(1045105093)));\n  EXPECT_EQ(result[2], ExtF(F(111548335)));\n  EXPECT_EQ(result[3], ExtF(F(1448238559)));\n}\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/crypto/commitments/fri/fri_proof.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_CRYPTO_COMMITMENTS_FRI_FRI_PROOF_H_\n#define TACHYON_CRYPTO_COMMITMENTS_FRI_FRI_PROOF_H_\n\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/base/buffer/copyable.h\"\n#include \"tachyon/base/strings/string_util.h\"\n#include \"tachyon/crypto/commitments/mixed_matrix_commitment_scheme_traits_forward.h\"\n\nnamespace tachyon {\nnamespace crypto {\n\ntemplate <typename PCS>\nstruct BatchOpening {\n  using InputMMCS = typename PCS::InputMMCS;\n  using Field = typename InputMMCS::Field;\n  using Proof = typename InputMMCS::Proof;\n\n  std::vector<std::vector<Field>> opened_values;\n  Proof opening_proof;\n\n  bool operator==(const BatchOpening& other) const {\n    return opened_values == other.opened_values &&\n           opening_proof == other.opening_proof;\n  }\n  bool operator!=(const BatchOpening& other) const {\n    return !operator==(other);\n  }\n\n  std::string ToString() const {\n    return absl::Substitute(\"{opened_values: $0, opening_proof: $1}\",\n                            base::Container2DToString(opened_values),\n                            base::Container2DToString(opening_proof));\n  }\n\n  std::string ToHexString(bool pad_zero = false) const {\n    return absl::Substitute(\n        \"{opened_values: $0, opening_proof: $1}\",\n        base::Container2DToHexString(opened_values, pad_zero),\n        base::Container2DToHexString(opening_proof, pad_zero));\n  }\n};\n\ntemplate <typename PCS>\nstruct CommitPhaseResult {\n  using ChallengeMMCS = typename PCS::ChallengeMMCS;\n  using Commitment = typename ChallengeMMCS::Commitment;\n  using ProverData = typename ChallengeMMCS::ProverData;\n  using Field = typename ChallengeMMCS::Field;\n\n  std::vector<Commitment> commits;\n  std::vector<ProverData> data;\n  Field final_eval;\n\n  bool operator==(const CommitPhaseResult& other) const {\n    return commits == other.commits && data == other.data &&\n           final_eval == other.final_eval;\n  }\n  bool operator!=(const CommitPhaseResult& other) const {\n    return !operator==(other);\n  }\n\n  std::string ToString() const {\n    return absl::Substitute(\"{commits: $0, data: $1, final_eval: $2},\",\n                            base::ContainerToString(commits),\n                            base::ContainerToString(data),\n                            final_eval.ToString());\n  }\n\n  std::string ToHexString(bool pad_zero = false) const {\n    return absl::Substitute(\"{commits: $0, data: $1, final_eval: $2},\",\n                            base::ContainerToHexString(commits, pad_zero),\n                            base::ContainerToHexString(data, pad_zero),\n                            final_eval.ToHexString(pad_zero));\n  }\n};\n\ntemplate <typename PCS>\nstruct CommitPhaseProofStep {\n  using ChallengeMMCS = typename PCS::ChallengeMMCS;\n  using Field = typename ChallengeMMCS::Field;\n  using Proof = typename ChallengeMMCS::Proof;\n\n  // The opening of the commit phase codeword at the sibling location.\n  Field sibling_value;\n  Proof opening_proof;\n\n  bool operator==(const CommitPhaseProofStep& other) const {\n    return sibling_value == other.sibling_value &&\n           opening_proof == other.opening_proof;\n  }\n  bool operator!=(const CommitPhaseProofStep& other) const {\n    return !operator==(other);\n  }\n\n  std::string ToString() const {\n    return absl::Substitute(\"{sibling_value: $0, opening_proof: $1}\",\n                            sibling_value.ToString(),\n                            base::Container2DToString(opening_proof));\n  }\n\n  std::string ToHexString(bool pad_zero = false) const {\n    return absl::Substitute(\n        \"{sibling_value: $0, opening_proof: $1}\",\n        sibling_value.ToHexString(pad_zero),\n        base::Container2DToHexString(opening_proof, pad_zero));\n  }\n};\n\n// Note(ashjeong): |InputProof| is usually a vector of |BatchOpening|\ntemplate <typename PCS>\nstruct QueryProof {\n  using InputProof = typename PCS::InputProof;\n\n  InputProof input_proof;\n  // For each commit phase commitment, this contains openings of a commit phase\n  // codeword at the queried location, along with an opening proof.\n  std::vector<CommitPhaseProofStep<PCS>> commit_phase_openings;\n\n  bool operator==(const QueryProof& other) const {\n    return input_proof == other.input_proof &&\n           commit_phase_openings == other.commit_phase_openings;\n  }\n  bool operator!=(const QueryProof& other) const { return !operator==(other); }\n\n  std::string ToString() const {\n    return absl::Substitute(\"{input_proof: $0, commit_phase_openings: $1}\",\n                            base::ContainerToString(input_proof),\n                            base::ContainerToString(commit_phase_openings));\n  }\n\n  std::string ToHexString(bool pad_zero = false) const {\n    return absl::Substitute(\"{input_proof: $0, commit_phase_openings: $1}\",\n                            base::ContainerToHexString(input_proof, pad_zero),\n                            base::ContainerToHexString(commit_phase_openings));\n  }\n};\n\ntemplate <typename PCS>\nstruct FRIProof {\n  using ChallengeMMCS = typename PCS::ChallengeMMCS;\n  using Commitment = typename ChallengeMMCS::Commitment;\n  using ExtField = typename ChallengeMMCS::Field;\n  using InputMMCS = typename PCS::InputMMCS;\n  using Field = typename InputMMCS::Field;\n\n  std::vector<Commitment> commit_phase_commits;\n  std::vector<QueryProof<PCS>> query_proofs;\n  ExtField final_eval;\n  Field pow_witness;\n\n  bool operator==(const FRIProof& other) const {\n    return commit_phase_commits == other.commit_phase_commits &&\n           query_proofs == other.query_proofs &&\n           final_eval == other.final_eval && pow_witness == other.pow_witness;\n  }\n  bool operator!=(const FRIProof& other) const { return !operator==(other); }\n\n  std::string ToString() const {\n    return absl::Substitute(\n        \"{commit_phase_commits: $0, query_proofs: $1, final_eval: $2, \"\n        \"pow_witness: $3}\",\n        base::Container2DToString(commit_phase_commits),\n        base::ContainerToString(query_proofs), final_eval.ToString(),\n        pow_witness.ToString());\n  }\n\n  std::string ToHexString(bool pad_zero = false) const {\n    return absl::Substitute(\n        \"{commit_phase_commits: $0, query_proofs: $1, final_eval: $2, \"\n        \"pow_witness: $3}\",\n        base::Container2DToHexString(commit_phase_commits, pad_zero),\n        base::ContainerToHexString(query_proofs, pad_zero),\n        final_eval.ToHexString(pad_zero), pow_witness.ToHexString(pad_zero));\n  }\n};\n\n}  // namespace crypto\n\nnamespace base {\n\ntemplate <typename PCS>\nclass Copyable<crypto::BatchOpening<PCS>> {\n  using InputMMCS = typename PCS::InputMMCS;\n  using Field = typename InputMMCS::Field;\n  using Proof = typename InputMMCS::Proof;\n\n public:\n  static bool WriteTo(const crypto::BatchOpening<PCS>& batch_opening,\n                      Buffer* buffer) {\n    return buffer->WriteMany(batch_opening.opened_values,\n                             batch_opening.opening_proof);\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer,\n                       crypto::BatchOpening<PCS>* batch_opening) {\n    std::vector<std::vector<Field>> opened_values;\n    Proof opening_proof;\n    if (!buffer.ReadMany(&opened_values, &opening_proof)) {\n      return false;\n    }\n\n    *batch_opening = {std::move(opened_values), std::move(opening_proof)};\n    return true;\n  }\n\n  static size_t EstimateSize(const crypto::BatchOpening<PCS>& batch_opening) {\n    return base::EstimateSize(batch_opening.opened_values,\n                              batch_opening.opening_proof);\n  }\n};\n\ntemplate <typename PCS>\nclass Copyable<crypto::CommitPhaseResult<PCS>> {\n  using ChallengeMMCS = typename PCS::ChallengeMMCS;\n  using Commitment = typename ChallengeMMCS::Commitment;\n  using ProverData = typename ChallengeMMCS::ProverData;\n  using Field = typename ChallengeMMCS::Field;\n\n public:\n  static bool WriteTo(const crypto::CommitPhaseResult<PCS>& commit_phase_result,\n                      Buffer* buffer) {\n    return buffer->WriteMany(commit_phase_result.commits,\n                             commit_phase_result.data,\n                             commit_phase_result.final_eval);\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer,\n                       crypto::CommitPhaseResult<PCS>* commit_phase_result) {\n    std::vector<Commitment> commits;\n    std::vector<ProverData> data;\n    Field final_eval;\n    if (!buffer.ReadMany(&commits, &data, &final_eval)) {\n      return false;\n    }\n\n    *commit_phase_result = {std::move(commits), std::move(data),\n                            std::move(final_eval)};\n    return true;\n  }\n\n  static size_t EstimateSize(\n      const crypto::CommitPhaseResult<PCS>& commit_phase_result) {\n    return base::EstimateSize(commit_phase_result.commits,\n                              commit_phase_result.data,\n                              commit_phase_result.final_eval);\n  }\n};\n\ntemplate <typename PCS>\nclass Copyable<crypto::CommitPhaseProofStep<PCS>> {\n  using ChallengeMMCS = typename PCS::ChallengeMMCS;\n  using Field = typename ChallengeMMCS::Field;\n  using Proof = typename ChallengeMMCS::Proof;\n\n public:\n  static bool WriteTo(\n      const crypto::CommitPhaseProofStep<PCS>& commit_phase_proof_step,\n      Buffer* buffer) {\n    return buffer->WriteMany(commit_phase_proof_step.sibling_value,\n                             commit_phase_proof_step.opening_proof);\n  }\n\n  static bool ReadFrom(\n      const ReadOnlyBuffer& buffer,\n      crypto::CommitPhaseProofStep<PCS>* commit_phase_proof_step) {\n    Field sibling_value;\n    Proof opening_proof;\n    if (!buffer.ReadMany(&sibling_value, &opening_proof)) {\n      return false;\n    }\n\n    *commit_phase_proof_step = {std::move(sibling_value),\n                                std::move(opening_proof)};\n    return true;\n  }\n\n  static size_t EstimateSize(\n      const crypto::CommitPhaseProofStep<PCS>& commit_phase_proof_step) {\n    return base::EstimateSize(commit_phase_proof_step.sibling_value,\n                              commit_phase_proof_step.opening_proof);\n  }\n};\n\ntemplate <typename PCS>\nclass Copyable<crypto::QueryProof<PCS>> {\n  using InputProof = typename PCS::InputProof;\n\n public:\n  static bool WriteTo(const crypto::QueryProof<PCS>& query_proof,\n                      Buffer* buffer) {\n    return buffer->WriteMany(query_proof.input_proof,\n                             query_proof.commit_phase_openings);\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer,\n                       crypto::QueryProof<PCS>* query_proof) {\n    InputProof input_proof;\n    std::vector<crypto::CommitPhaseProofStep<PCS>> commit_phase_openings;\n    if (!buffer.ReadMany(&input_proof, &commit_phase_openings)) {\n      return false;\n    }\n\n    *query_proof = {std::move(input_proof), std::move(commit_phase_openings)};\n    return true;\n  }\n\n  static size_t EstimateSize(const crypto::QueryProof<PCS>& query_proof) {\n    return base::EstimateSize(query_proof.input_proof,\n                              query_proof.commit_phase_openings);\n  }\n};\n\ntemplate <typename PCS>\nclass Copyable<crypto::FRIProof<PCS>> {\n  using ChallengeMMCS = typename PCS::ChallengeMMCS;\n  using Commitment = typename ChallengeMMCS::Commitment;\n  using ExtField = typename ChallengeMMCS::Field;\n  using InputMMCS = typename PCS::InputMMCS;\n  using Field = typename InputMMCS::Field;\n\n public:\n  static bool WriteTo(const crypto::FRIProof<PCS>& fri_proof, Buffer* buffer) {\n    return buffer->WriteMany(fri_proof.commit_phase_commits,\n                             fri_proof.query_proofs, fri_proof.final_eval,\n                             fri_proof.pow_witness);\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer,\n                       crypto::FRIProof<PCS>* fri_proof) {\n    std::vector<Commitment> commit_phase_commits;\n    std::vector<crypto::QueryProof<PCS>> query_proofs;\n    ExtField final_eval;\n    Field pow_witness;\n    if (!buffer.ReadMany(&commit_phase_commits, &query_proofs, &final_eval,\n                         &pow_witness)) {\n      return false;\n    }\n\n    *fri_proof = {std::move(commit_phase_commits), std::move(query_proofs),\n                  std::move(final_eval), std::move(pow_witness)};\n    return true;\n  }\n\n  static size_t EstimateSize(const crypto::FRIProof<PCS>& fri_proof) {\n    return base::EstimateSize(fri_proof.commit_phase_commits,\n                              fri_proof.query_proofs, fri_proof.final_eval,\n                              fri_proof.pow_witness);\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_CRYPTO_COMMITMENTS_FRI_FRI_PROOF_H_\n"
  },
  {
    "path": "tachyon/crypto/commitments/fri/lagrange_selectors.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_CRYPTO_COMMITMENTS_FRI_LAGRANGE_SELECTORS_H_\n#define TACHYON_CRYPTO_COMMITMENTS_FRI_LAGRANGE_SELECTORS_H_\n\nnamespace tachyon::crypto {\n\ntemplate <typename T>\nstruct LagrangeSelectors {\n  T first_row;\n  T last_row;\n  T transition;\n  T inv_zeroifier;\n\n  bool operator==(const LagrangeSelectors& other) const {\n    return first_row == other.first_row && last_row == other.last_row &&\n           transition == other.transition &&\n           inv_zeroifier == other.inv_zeroifier;\n  }\n  bool operator!=(const LagrangeSelectors& other) const {\n    return !operator==(other);\n  }\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_COMMITMENTS_FRI_LAGRANGE_SELECTORS_H_\n"
  },
  {
    "path": "tachyon/crypto/commitments/fri/prove.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_CRYPTO_COMMITMENTS_FRI_PROVE_H_\n#define TACHYON_CRYPTO_COMMITMENTS_FRI_PROVE_H_\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/bits.h\"\n#include \"tachyon/base/ranges/algorithm.h\"\n#include \"tachyon/crypto/challenger/challenger.h\"\n#include \"tachyon/crypto/commitments/fri/fri_config.h\"\n#include \"tachyon/crypto/commitments/fri/fri_proof.h\"\n#include \"tachyon/math/finite_fields/extension_field_traits_forward.h\"\n#include \"tachyon/math/matrix/matrix_types.h\"\n\nnamespace tachyon::crypto::fri {\n\ntemplate <typename PCS, typename ExtF, typename ChallengeMMCS,\n          typename Challenger>\nCommitPhaseResult<PCS> CommitPhase(const FRIConfig<ChallengeMMCS>& config,\n                                   std::vector<std::vector<ExtF>>&& inputs,\n                                   Challenger& challenger) {\n  // NOTE(ashjeong): This is empirically determined in case the size of the for\n  // loop is small enough to not need parallelization.\n  constexpr size_t kThreshold = 1024;\n\n  using Commitment = typename ChallengeMMCS::Commitment;\n  using ProverData = typename ChallengeMMCS::ProverData;\n\n  TRACE_EVENT(\"ProofGeneration\", \"fri::CommitPhase\");\n\n  // NOTE(ashjeong): Refer to the note below as to why this is safe.\n  std::vector<ExtF> folded = std::move(inputs[0]);\n  size_t total_folds = base::bits::CheckedLog2(folded.size()) -\n                       base::bits::Log2Floor(config.Blowup());\n  std::vector<Commitment> commits;\n  commits.reserve(total_folds);\n  std::vector<ProverData> data;\n  data.reserve(total_folds);\n\n  while (folded.size() > config.Blowup()) {\n    std::vector<math::RowMajorMatrix<ExtF>> leaves;\n    leaves.emplace_back((folded.size() + 1) / 2, 2);\n    base::Parallelize(\n        folded,\n        [&leaves](absl::Span<ExtF> chunk, size_t chunk_offset,\n                  size_t chunk_size) {\n          size_t chunk_start = chunk_offset * chunk_size;\n          for (size_t i = chunk_start; i < chunk_start + chunk.size(); ++i) {\n            leaves[0](i / 2, i % 2) = std::move(chunk[i - chunk_start]);\n          }\n        },\n        kThreshold);\n\n    Commitment commit;\n    ProverData prover_data;\n    CHECK(config.mmcs.CommitOwned(std::move(leaves), &commit, &prover_data));\n    commits.push_back(std::move(commit));\n    data.push_back(std::move(prover_data));\n\n    challenger.ObserveContainer(commits.back());\n    const ExtF beta = challenger.template SampleExtElement<ExtF>();\n    VLOG(2) << \"FRI(beta[\" << commits.size() - 1\n            << \"]): \" << beta.ToHexString(true);\n\n    folded = FoldMatrix(beta, config.mmcs.GetMatrices(data.back()).back());\n    // NOTE(ashjeong): |inputs| is sorted by largest to smallest size, and the\n    // size of |folded| is divided by two every loop. This means that if\n    // |folded| is initialized as |inputs[0]|, the size of the next iteration of\n    // |folded| will never be the size of |inputs[0]|.\n    for (size_t i = 1; i < inputs.size(); ++i) {\n      if (inputs[i].size() == folded.size()) {\n        OMP_PARALLEL_FOR(size_t j = 0; j < inputs[i].size(); ++j) {\n          folded[j] += inputs[i][j];\n        }\n      }\n    }\n  }\n  CHECK_EQ(folded.size(), config.Blowup());\n  ExtF& final_eval = folded[0];\n  VLOG(2) << \"FRI(final_eval): \" << final_eval.ToHexString(true);\n\n#if DCHECK_IS_ON()\n  OMP_PARALLEL_FOR(size_t i = 0; i < folded.size(); ++i) {\n    DCHECK_EQ(folded[i], final_eval);\n  }\n#endif\n  challenger.ObserveContainer(final_eval);\n\n  return {std::move(commits), std::move(data), std::move(final_eval)};\n}\n\ntemplate <typename PCS, typename ChallengeMMCS = typename PCS::ChallengeMMCS>\nstd::vector<CommitPhaseProofStep<PCS>> AnswerQuery(\n    size_t index, const FRIConfig<ChallengeMMCS>& config,\n    const std::vector<typename ChallengeMMCS::ProverData>&\n        commit_phase_commits) {\n  TRACE_EVENT(\"ProofGeneration\", \"fri::AnswerQuery\");\n  return base::CreateVector(\n      commit_phase_commits.size(),\n      [index, &config, &commit_phase_commits](size_t i) {\n        using F = typename ChallengeMMCS::Field;\n        using Proof = typename ChallengeMMCS::Proof;\n\n        size_t index_i = index >> i;\n        size_t index_i_sibling = index_i ^ 1;\n        size_t index_pair = index_i >> 1;\n        std::vector<std::vector<F>> opened_rows;\n        Proof opening_proof;\n        CHECK(config.mmcs.CreateOpeningProof(\n            index_pair, commit_phase_commits[i], &opened_rows, &opening_proof));\n        CHECK_EQ(opened_rows.size(), size_t{1});\n        CHECK_EQ(opened_rows[0].size(), size_t{2});\n        const F& sibling_value = opened_rows[0][index_i_sibling % 2];\n        return CommitPhaseProofStep<PCS>{sibling_value,\n                                         std::move(opening_proof)};\n      });\n}\n\ntemplate <typename PCS, typename ExtF, typename ChallengeMMCS,\n          typename Challenger, typename OpenInputCallback,\n          typename F = typename math::ExtensionFieldTraits<ExtF>::BaseField>\nFRIProof<PCS> Prove(const FRIConfig<ChallengeMMCS>& config,\n                    std::vector<std::vector<ExtF>>&& inputs,\n                    Challenger& challenger, OpenInputCallback open_input,\n                    std::optional<F> pow_witness_for_testing = std::nullopt) {\n  using QueryProof = QueryProof<PCS>;\n  TRACE_EVENT(\"ProofGeneration\", \"fri::Prove\");\n\n#if DCHECK_IS_ON()\n  // Ensure |inputs| is in order from largest to smallest\n  DCHECK(base::ranges::is_sorted(\n      inputs.begin(), inputs.end(),\n      [](const std::vector<ExtF>& l, const std::vector<ExtF>& r) {\n        return l.size() > r.size();\n      }));\n#endif\n\n  uint32_t log_max_num_rows = base::bits::CheckedLog2(inputs[0].size());\n  CommitPhaseResult<PCS> commit_phase_result =\n      CommitPhase<PCS>(config, std::move(inputs), challenger);\n  F pow_witness =\n      challenger.Grind(config.proof_of_work_bits, pow_witness_for_testing);\n  VLOG(2) << \"FRI(pow): \" << pow_witness.ToHexString(true);\n\n  std::vector<QueryProof> query_proofs = base::CreateVector(\n      config.num_queries, [log_max_num_rows, &challenger, &open_input, &config,\n                           &commit_phase_result](size_t query_idx) {\n        size_t index = challenger.SampleBits(log_max_num_rows);\n        VLOG(2) << \"FRI(index[\" << query_idx << \"]): \" << index;\n        std::vector<BatchOpening<PCS>> x = open_input(index);\n\n        std::vector<CommitPhaseProofStep<PCS>> answered_query =\n            AnswerQuery<PCS>(index, config, commit_phase_result.data);\n        return QueryProof{std::move(x), std::move(answered_query)};\n      });\n  return {std::move(commit_phase_result.commits), std::move(query_proofs),\n          std::move(commit_phase_result.final_eval), std::move(pow_witness)};\n}\n\n}  // namespace tachyon::crypto::fri\n\n#endif  // TACHYON_CRYPTO_COMMITMENTS_FRI_PROVE_H_\n"
  },
  {
    "path": "tachyon/crypto/commitments/fri/simple_fri.h",
    "content": "// Use of this source code is governed by a Apache-2.0 style license that\n// can be found in the LICENSE.lambdaworks.\n\n#ifndef TACHYON_CRYPTO_COMMITMENTS_FRI_SIMPLE_FRI_H_\n#define TACHYON_CRYPTO_COMMITMENTS_FRI_SIMPLE_FRI_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/optional.h\"\n#include \"tachyon/crypto/commitments/fri/simple_fri_proof.h\"\n#include \"tachyon/crypto/commitments/fri/simple_fri_storage.h\"\n#include \"tachyon/crypto/commitments/merkle_tree/binary_merkle_tree/binary_merkle_tree.h\"\n#include \"tachyon/crypto/commitments/univariate_polynomial_commitment_scheme.h\"\n#include \"tachyon/crypto/transcripts/transcript.h\"\n\nnamespace tachyon::crypto {\n\ntemplate <typename F, size_t MaxDegree>\nclass SimpleFRI final\n    : public UnivariatePolynomialCommitmentScheme<SimpleFRI<F, MaxDegree>> {\n public:\n  using Base = UnivariatePolynomialCommitmentScheme<SimpleFRI<F, MaxDegree>>;\n  using Poly = typename Base::Poly;\n  using Evals = typename Base::Evals;\n  using Domain = typename Base::Domain;\n\n  SimpleFRI() = default;\n  SimpleFRI(const Domain* domain, SimpleFRIStorage<F>* storage,\n            BinaryMerkleHasher<F, F>* hasher)\n      : domain_(domain), storage_(storage), hasher_(hasher) {\n    // This ensures last folding process.\n    CHECK_GE(domain->size(), size_t{2}) << \"Domain size must be at least 2\";\n    uint32_t k = domain->log_size_of_group();\n    if (k > 1) {\n      sub_domains_ = base::CreateVector(k - 1, [k](size_t i) {\n        return Domain::Create(size_t{1} << (k - i - 1));\n      });\n    }\n    storage_->Allocate(k);\n  }\n\n  // UnivariatePolynomialCommitmentScheme methods\n  const char* Name() const { return \"SimpleFRI\"; }\n\n  size_t N() const { return domain_->size(); }\n\n  [[nodiscard]] bool Commit(const Poly& poly, Transcript<F>* transcript) const {\n    uint32_t num_layers = domain_->log_size_of_group();\n    TranscriptWriter<F>* writer = transcript->ToWriter();\n    BinaryMerkleTree<F, F, MaxDegree + 1> tree(storage_->GetLayer(0), hasher_);\n    Evals evals = domain_->FFT(poly);\n    F root;\n    if (!tree.Commit(evals.evaluations(), &root)) return false;\n    if (!writer->WriteToProof(root)) return false;\n    const Poly* cur_poly = &poly;\n\n    F beta;\n    Poly folded_poly;\n    if (num_layers > 1) {\n      for (uint32_t i = 1; i < num_layers; ++i) {\n        // Pᵢ(X)   = Pᵢ_even(X²) + X * Pᵢ_odd(X²)\n        // Pᵢ₊₁(X) = Pᵢ_even(X) + β * Pᵢ_odd(X)\n        beta = writer->SqueezeChallenge();\n        VLOG(2) << \"SimpleFRI(beta[\" << i - 1\n                << \"]): \" << beta.ToHexString(true);\n        folded_poly = cur_poly->Fold(beta);\n        BinaryMerkleTree<F, F, MaxDegree + 1> tree(storage_->GetLayer(i),\n                                                   hasher_);\n        evals = sub_domains_[i - 1]->FFT(folded_poly);\n        if (!tree.Commit(evals.evaluations(), &root)) return false;\n        if (!writer->WriteToProof(root)) return false;\n        cur_poly = &folded_poly;\n      }\n    }\n\n    beta = writer->SqueezeChallenge();\n    VLOG(2) << \"SimpleFRI(beta[\" << num_layers - 1\n            << \"]): \" << beta.ToHexString(true);\n    folded_poly = cur_poly->Fold(beta);\n    return writer->WriteToProof(folded_poly[0]);\n  }\n\n  [[nodiscard]] bool DoCreateOpeningProof(size_t index,\n                                          SimpleFRIProof<F>* fri_proof) {\n    size_t domain_size = domain_->size();\n    uint32_t num_layers = domain_->log_size_of_group();\n    for (uint32_t i = 0; i < num_layers; ++i) {\n      size_t leaf_index = index % domain_size;\n      BinaryMerkleTreeStorage<F>* layer = storage_->GetLayer(i);\n      BinaryMerkleTree<F, F, MaxDegree + 1> tree(layer, hasher_);\n      BinaryMerkleProof<F> proof;\n      if (!tree.CreateOpeningProof(leaf_index, &proof)) return false;\n      // Merkle proof for Pᵢ(ωʲ) against Cᵢ\n      fri_proof->paths.push_back(std::move(proof));\n      // Pᵢ(ωʲ)\n      fri_proof->evaluations.push_back(\n          layer->GetHash(domain_size - 1 + leaf_index));\n\n      size_t half_domain_size = domain_size >> 1;\n      size_t leaf_index_sym = (index + half_domain_size) % domain_size;\n      BinaryMerkleProof<F> proof_sym;\n      if (!tree.CreateOpeningProof(leaf_index_sym, &proof_sym)) return false;\n      // Merkle proof for Pᵢ(-ωʲ) against Cᵢ\n      fri_proof->paths_sym.push_back(std::move(proof_sym));\n      // Pᵢ(-ωʲ)\n      fri_proof->evaluations_sym.push_back(\n          layer->GetHash(domain_size - 1 + leaf_index_sym));\n\n      domain_size = half_domain_size;\n    }\n    return true;\n  }\n\n  [[nodiscard]] bool DoVerifyOpeningProof(\n      Transcript<F>& transcript, size_t index,\n      const SimpleFRIProof<F>& proof) const {\n    TranscriptReader<F>* reader = transcript.ToReader();\n    size_t domain_size = domain_->size();\n    uint32_t num_layers = domain_->log_size_of_group();\n    F root;\n    F x;\n    F beta;\n    F evaluation;\n    F evaluation_sym;\n    for (uint32_t i = 0; i < num_layers; ++i) {\n      BinaryMerkleTreeStorage<F>* layer = storage_->GetLayer(i);\n      BinaryMerkleTree<F, F, MaxDegree + 1> tree(layer, hasher_);\n\n      if (!reader->ReadFromProof(&root)) return false;\n      if (!tree.VerifyOpeningProof(root, proof.evaluations[i], proof.paths[i]))\n        return false;\n\n      if (!tree.VerifyOpeningProof(root, proof.evaluations_sym[i],\n                                   proof.paths_sym[i]))\n        return false;\n\n      // Given equations:\n      // Pᵢ(X)  = Pᵢ_even(X²) + X * Pᵢ_odd(X²)\n      // Pᵢ(-X) = Pᵢ_even(X²) - X * Pᵢ_odd(X²)\n      //\n      // Using Gaussian elimination, we derive:\n      // Pᵢ_even(X²) = (Pᵢ(X) + Pᵢ(-X)) / 2\n      // Pᵢ_odd(X²)  = (Pᵢ(X) - Pᵢ(-X)) / (2 * X)\n      //\n      // Next layer equation:\n      // Pᵢ₊₁(X) = Pᵢ_even(X) + β * Pᵢ_odd(X)\n      //\n      // If the domain of Pᵢ(X) is Dᵢ = {ω⁰, ω¹, ..., ωⁿ⁻¹},\n      // then the domain of Pᵢ₊₁(X) is Dᵢ₊₁ = {ω⁰, ω², ..., ωⁿ⁻²}.\n      //\n      // As per the definition:\n      // Pᵢ₊₁(ω²ʲ) = Pᵢ_even(ω²ʲ) + β * Pᵢ_odd(ω²ʲ)\n      //\n      // Substituting Pᵢ_even and Pᵢ_odd:\n      // Pᵢ₊₁(ω²ʲ) = (Pᵢ(ωʲ) + Pᵢ(-ωʲ)) / 2 + β * (Pᵢ(ωʲ) - Pᵢ(-ωʲ)) / (2 * ωʲ)\n      //           = ((1 + β * ω⁻ʲ) * Pᵢ(ωʲ) + (1 - β * ω⁻ʲ) * Pᵢ(-ωʲ)) / 2\n      size_t leaf_index = index % domain_size;\n      if (i == 0) {\n        evaluation = proof.evaluations[i];\n        evaluation_sym = proof.evaluations_sym[i];\n        x = domain_->GetElement(leaf_index);\n      } else {\n        evaluation *= (F::One() + beta);\n        evaluation_sym *= (F::One() - beta);\n        evaluation += evaluation_sym;\n        evaluation *= F::TwoInv();\n\n        if (evaluation != proof.evaluations[i]) {\n          LOG(ERROR)\n              << \"Proof doesn't match with expected evaluation at layer [\" << i\n              << \"]\";\n          return false;\n        }\n        evaluation_sym = proof.evaluations_sym[i];\n        x = sub_domains_[i - 1]->GetElement(leaf_index);\n      }\n      beta = reader->SqueezeChallenge();\n      VLOG(2) << \"SimpleFRI(beta[\" << i << \"]): \" << beta.ToHexString(true);\n      beta *= unwrap(x.Inverse());\n      domain_size = domain_size >> 1;\n    }\n\n    evaluation *= (F::One() + beta);\n    evaluation_sym *= (F::One() - beta);\n    evaluation += evaluation_sym;\n    evaluation *= F::TwoInv();\n\n    if (!reader->ReadFromProof(&root)) return false;\n    if (root != evaluation) {\n      LOG(ERROR) << \"Root doesn't match with expected evaluation\";\n      return false;\n    }\n    return true;\n  }\n\n private:\n  // not owned\n  const Domain* domain_ = nullptr;\n  // not owned\n  mutable SimpleFRIStorage<F>* storage_ = nullptr;\n  // not owned\n  BinaryMerkleHasher<F, F>* hasher_ = nullptr;\n  // not owned\n  Transcript<F>* transcript_ = nullptr;\n  std::vector<std::unique_ptr<Domain>> sub_domains_;\n};\n\ntemplate <typename F, size_t MaxDegree>\nstruct VectorCommitmentSchemeTraits<SimpleFRI<F, MaxDegree>> {\n public:\n  constexpr static size_t kMaxSize = MaxDegree + 1;\n  constexpr static bool kIsTransparent = true;\n  constexpr static bool kSupportsBatchMode = false;\n\n  using Field = F;\n  using Commitment = Transcript<F>;\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_COMMITMENTS_FRI_SIMPLE_FRI_H_\n"
  },
  {
    "path": "tachyon/crypto/commitments/fri/simple_fri_proof.h",
    "content": "// Use of this source code is governed by a Apache-2.0 style license that\n// can be found in the LICENSE.lambdaworks.\n\n#ifndef TACHYON_CRYPTO_COMMITMENTS_FRI_SIMPLE_FRI_PROOF_H_\n#define TACHYON_CRYPTO_COMMITMENTS_FRI_SIMPLE_FRI_PROOF_H_\n\n#include <vector>\n\n#include \"tachyon/crypto/commitments/merkle_tree/binary_merkle_tree/binary_merkle_proof.h\"\n\nnamespace tachyon::crypto {\n\ntemplate <typename F>\nstruct SimpleFRIProof {\n  std::vector<BinaryMerkleProof<F>> paths;\n  std::vector<BinaryMerkleProof<F>> paths_sym;\n  std::vector<F> evaluations;\n  std::vector<F> evaluations_sym;\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_COMMITMENTS_FRI_SIMPLE_FRI_PROOF_H_\n"
  },
  {
    "path": "tachyon/crypto/commitments/fri/simple_fri_storage.h",
    "content": "// Use of this source code is governed by a Apache-2.0 style license that\n// can be found in the LICENSE.lambdaworks.\n\n#ifndef TACHYON_CRYPTO_COMMITMENTS_FRI_SIMPLE_FRI_STORAGE_H_\n#define TACHYON_CRYPTO_COMMITMENTS_FRI_SIMPLE_FRI_STORAGE_H_\n\n#include \"tachyon/crypto/commitments/merkle_tree/binary_merkle_tree/binary_merkle_tree_storage.h\"\n\nnamespace tachyon::crypto {\n\ntemplate <typename Hash>\nclass SimpleFRIStorage {\n public:\n  virtual ~SimpleFRIStorage() = default;\n\n  virtual void Allocate(size_t size) = 0;\n  virtual BinaryMerkleTreeStorage<Hash>* GetLayer(size_t index) = 0;\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_COMMITMENTS_FRI_SIMPLE_FRI_STORAGE_H_\n"
  },
  {
    "path": "tachyon/crypto/commitments/fri/simple_fri_unittest.cc",
    "content": "// Use of this source code is governed by a Apache-2.0 style license that\n// can be found in the LICENSE.lambdaworks.\n\n#include \"tachyon/crypto/commitments/fri/simple_fri.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/crypto/commitments/merkle_tree/binary_merkle_tree/simple_binary_merkle_tree_storage.h\"\n#include \"tachyon/crypto/transcripts/simple_transcript.h\"\n#include \"tachyon/math/finite_fields/goldilocks/goldilocks.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluation_domain_factory.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_polynomial.h\"\n\nnamespace tachyon::crypto {\n\nnamespace {\n\nclass SimpleHasher\n    : public BinaryMerkleHasher<math::Goldilocks, math::Goldilocks> {\n public:\n  // BinaryMerkleHasher<math::Goldilocks, math::Goldilocks> methods\n  math::Goldilocks ComputeLeafHash(\n      const math::Goldilocks& leaf) const override {\n    return leaf;\n  }\n  math::Goldilocks ComputeParentHash(\n      const math::Goldilocks& left,\n      const math::Goldilocks& right) const override {\n    return left + right.Double();\n  }\n};\n\nclass SimpleFRIStorageImpl : public SimpleFRIStorage<math::Goldilocks> {\n public:\n  const std::vector<SimpleBinaryMerkleTreeStorage<math::Goldilocks>>& layers()\n      const {\n    return layers_;\n  }\n\n  // FRIStorage<math::Goldilocks> methods\n  void Allocate(size_t size) override { layers_.resize(size); }\n  BinaryMerkleTreeStorage<math::Goldilocks>* GetLayer(size_t index) override {\n    return &layers_[index];\n  }\n\n private:\n  std::vector<SimpleBinaryMerkleTreeStorage<math::Goldilocks>> layers_;\n};\n\nclass FRITest : public math::FiniteFieldTest<math::Goldilocks> {\n public:\n  constexpr static size_t K = 3;\n  constexpr static size_t N = size_t{1} << K;\n  constexpr static size_t kMaxDegree = N - 1;\n\n  using PCS = SimpleFRI<math::Goldilocks, kMaxDegree>;\n  using F = PCS::Field;\n  using Poly = PCS::Poly;\n  using Commitment = PCS::Commitment;\n  using Domain = PCS::Domain;\n\n  void SetUp() override {\n    domain_ = Domain::Create(N);\n    pcs_ = PCS(domain_.get(), &storage_, &hasher_);\n  }\n\n protected:\n  std::unique_ptr<Domain> domain_;\n  SimpleFRIStorageImpl storage_;\n  SimpleHasher hasher_;\n  PCS pcs_;\n};\n\n}  // namespace\n\nTEST_F(FRITest, CommitAndVerify) {\n  Poly poly = Poly::Random(kMaxDegree);\n  base::Uint8VectorBuffer write_buffer;\n  SimpleTranscriptWriter<F> writer(std::move(write_buffer));\n  ASSERT_TRUE(pcs_.Commit(poly, &writer));\n\n  size_t index = base::Uniform(base::Range<size_t>::Until(kMaxDegree + 1));\n  SimpleFRIProof<math::Goldilocks> proof;\n  ASSERT_TRUE(pcs_.CreateOpeningProof(index, &proof));\n\n  SimpleTranscriptReader<F> reader(std::move(writer).TakeBuffer());\n  reader.buffer().set_buffer_offset(0);\n  ASSERT_TRUE(pcs_.VerifyOpeningProof(reader, index, proof));\n}\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/crypto/commitments/fri/two_adic_fri.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_CRYPTO_COMMITMENTS_FRI_TWO_ADIC_FRI_H_\n#define TACHYON_CRYPTO_COMMITMENTS_FRI_TWO_ADIC_FRI_H_\n\n#include <algorithm>\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"absl/container/btree_map.h\"\n#include \"absl/container/flat_hash_map.h\"\n\n#include \"tachyon/base/bits.h\"\n#include \"tachyon/base/parallelize.h\"\n#include \"tachyon/base/profiler.h\"\n#include \"tachyon/crypto/commitments/fri/fri_proof.h\"\n#include \"tachyon/crypto/commitments/fri/prove.h\"\n#include \"tachyon/crypto/commitments/fri/two_adic_multiplicative_coset.h\"\n#include \"tachyon/crypto/commitments/fri/verify.h\"\n#include \"tachyon/crypto/commitments/mixed_matrix_commitment_scheme.h\"\n#include \"tachyon/math/finite_fields/extension_field_traits_forward.h\"\n#include \"tachyon/math/geometry/dimensions.h\"\n#include \"tachyon/math/matrix/matrix_types.h\"\n#include \"tachyon/math/matrix/matrix_utils.h\"\n#include \"tachyon/math/polynomials/univariate/evaluations_utils.h\"\n\nnamespace tachyon {\nnamespace c::crypto {\n\ntemplate <typename ExtF, typename InputMMCS, typename ChallengeMMCS,\n          typename Challenger>\nclass TwoAdicFRIImpl;\n\n}  // namespace c::crypto\n\nnamespace crypto {\n\ntemplate <typename _ExtF, typename _InputMMCS, typename _ChallengeMMCS,\n          typename _Challenger>\nclass TwoAdicFRI {\n public:\n  using ExtF = _ExtF;\n  using InputMMCS = _InputMMCS;\n  using ChallengeMMCS = _ChallengeMMCS;\n  using Challenger = _Challenger;\n\n  using F = typename math::ExtensionFieldTraits<ExtF>::BaseField;\n  using Domain = TwoAdicMultiplicativeCoset<F>;\n  using Commitment = typename InputMMCS::Commitment;\n  using ProverData = typename InputMMCS::ProverData;\n  using Proof = typename InputMMCS::Proof;\n  using InputProof = std::vector<BatchOpening<TwoAdicFRI>>;\n  using FRIProof = crypto::FRIProof<TwoAdicFRI>;\n\n  using OpeningPointsForRound = std::vector<std::vector<ExtF>>;\n  using OpeningPoints = std::vector<OpeningPointsForRound>;\n\n  using OpenedValuesForRound = std::vector<std::vector<std::vector<ExtF>>>;\n  using OpenedValues = std::vector<OpenedValuesForRound>;\n\n  TwoAdicFRI() = default;\n  TwoAdicFRI(InputMMCS&& mmcs, FRIConfig<ChallengeMMCS>&& config)\n      : mmcs_(std::move(mmcs)), config_(std::move(config)) {}\n\n  const FRIConfig<ChallengeMMCS>& config() const { return config_; }\n\n  Domain GetNaturalDomainForDegree(size_t size) {\n    uint32_t log_n = base::bits::CheckedLog2(size);\n    return Domain(log_n, F::One());\n  }\n\n  [[nodiscard]] bool Commit(const std::vector<Domain>& cosets,\n                            std::vector<math::RowMajorMatrix<F>>& matrices,\n                            Commitment* commitment, ProverData* prover_data) {\n    TRACE_EVENT(\"ProofGeneration\", \"TwoAdicFRI::Commit\");\n    std::vector<math::RowMajorMatrix<F>> ldes =\n        base::Map(cosets, [this, &matrices](size_t i, const Domain& coset) {\n          math::RowMajorMatrix<F>& mat = matrices[i];\n          CHECK_EQ(coset.domain()->size(), static_cast<size_t>(mat.rows()));\n          math::RowMajorMatrix<F> lde(mat.rows() << config_.log_blowup,\n                                      mat.cols());\n          coset.domain()->CosetLDEBatch(\n              std::move(mat), config_.log_blowup,\n              F::FromMontgomery(F::Config::kSubgroupGenerator) *\n                  coset.domain()->offset_inv(),\n              lde, /*reverse_at_last=*/false);\n          return lde;\n        });\n    return mmcs_.CommitOwned(std::move(ldes), commitment, prover_data);\n  }\n\n  [[nodiscard]] bool CreateOpeningProof(\n      const std::vector<std::unique_ptr<ProverData>>& prover_data_by_round,\n      const OpeningPoints& points_by_round, Challenger& challenger,\n      OpenedValues* opened_values_out, FRIProof* proof,\n      std::optional<F> pow_witness_for_testing = std::nullopt) const {\n    TRACE_EVENT(\"ProofGeneration\", \"TwoAdicFRI::CreateOpeningProof\");\n    ExtF alpha = challenger.template SampleExtElement<ExtF>();\n    VLOG(2) << \"FRI(alpha): \" << alpha.ToHexString(true);\n    size_t num_rounds = prover_data_by_round.size();\n\n    size_t global_max_num_rows = 0;\n    std::vector<absl::Span<const Eigen::Map<const math::RowMajorMatrix<F>>>>\n        matrices_by_round = base::Map(\n            prover_data_by_round,\n            [this, &global_max_num_rows](\n                const std::unique_ptr<ProverData>& prover_data) {\n              global_max_num_rows = std::max(global_max_num_rows,\n                                             mmcs_.GetMaxRowSize(*prover_data));\n              return absl::MakeConstSpan(mmcs_.GetMatrices(*prover_data));\n            });\n    uint32_t log_global_max_num_rows =\n        base::bits::CheckedLog2(global_max_num_rows);\n\n    // For each unique opening point z, we will find the largest degree bound\n    // for that point, and precompute 1/(X - z) for the largest subgroup (in\n    // bitrev order).\n    absl::flat_hash_map<ExtF, std::vector<ExtF>> inv_denoms =\n        ComputeInverseDenominators(\n            matrices_by_round, points_by_round,\n            F::FromMontgomery(F::Config::kSubgroupGenerator));\n\n    std::array<std::vector<ExtF>, 32> reduced_openings;\n    std::array<size_t, 32> num_reduced = {};\n\n    OpenedValues opened_values(num_rounds);\n    for (size_t round = 0; round < num_rounds; ++round) {\n      absl::Span<const Eigen::Map<const math::RowMajorMatrix<F>>> matrices =\n          matrices_by_round[round];\n      const OpeningPointsForRound& points = points_by_round[round];\n      OpenedValuesForRound opened_values_for_round(matrices.size());\n      for (size_t matrix_idx = 0; matrix_idx < matrices.size(); ++matrix_idx) {\n        const Eigen::Map<const math::RowMajorMatrix<F>>& mat =\n            matrices[matrix_idx];\n        size_t num_rows = static_cast<size_t>(mat.rows());\n        size_t num_cols = static_cast<size_t>(mat.cols());\n        uint32_t log_num_rows = base::bits::CheckedLog2(num_rows);\n\n        if (reduced_openings[log_num_rows].empty()) {\n          reduced_openings[log_num_rows] =\n              std::vector<ExtF>(num_rows, ExtF::Zero());\n        }\n        std::vector<ExtF>& reduced_opening_for_log_num_rows =\n            reduced_openings[log_num_rows];\n        CHECK_EQ(reduced_opening_for_log_num_rows.size(), num_rows);\n\n        math::RowMajorMatrix<F> block;\n        block.resize(num_rows >> config_.log_blowup, mat.cols());\n        OMP_PARALLEL_FOR(size_t row = 0; row < num_rows >> config_.log_blowup;\n                         ++row) {\n          block.row(row) = mat.row(base::bits::ReverseBitsLen(\n              row, log_num_rows - config_.log_blowup));\n        }\n        std::vector<ExtF> reduced_rows = DotExtPowers(mat, alpha);\n\n        // TODO(ashjeong): Determine if using a matrix is a better fit.\n        opened_values_for_round[matrix_idx].reserve(points[matrix_idx].size());\n        for (size_t point_idx = 0; point_idx < points[matrix_idx].size();\n             ++point_idx) {\n          const ExtF& point = points[matrix_idx][point_idx];\n          std::vector<ExtF> ys = InterpolateCoset(\n              block, F::FromMontgomery(F::Config::kSubgroupGenerator), point);\n\n          ExtF alpha_pow = ExtF::One();\n          ExtF reduced_ys = ExtF::Zero();\n          for (size_t c = 0; c < num_cols - 1; ++c) {\n            reduced_ys += alpha_pow * ys[c];\n            alpha_pow *= alpha;\n          }\n          reduced_ys += alpha_pow * ys[num_cols - 1];\n\n          const ExtF alpha_pow_offset = alpha.Pow(num_reduced[log_num_rows]);\n          num_reduced[log_num_rows] += num_cols;\n\n          const std::vector<ExtF>& inv_denom = inv_denoms[point];\n          OMP_PARALLEL_FOR(size_t i = 0;\n                           i < reduced_opening_for_log_num_rows.size(); ++i) {\n            reduced_opening_for_log_num_rows[i] +=\n                alpha_pow_offset * (reduced_rows[i] - reduced_ys) *\n                inv_denom[i];\n          }\n          opened_values_for_round[matrix_idx].emplace_back(std::move(ys));\n        }\n      }\n      opened_values[round] = std::move(opened_values_for_round);\n    }\n    std::vector<std::vector<ExtF>> fri_input;\n    fri_input.reserve(reduced_openings.size() - 1);\n    for (size_t i = reduced_openings.size() - 1; i != SIZE_MAX; --i) {\n      if (!reduced_openings[i].empty()) {\n        fri_input.push_back(std::move(reduced_openings[i]));\n      }\n    }\n\n    *opened_values_out = std::move(opened_values);\n    *proof = fri::Prove<TwoAdicFRI>(\n        config_, std::move(fri_input), challenger,\n        [this, log_global_max_num_rows, &prover_data_by_round](size_t index) {\n          size_t num_rounds = prover_data_by_round.size();\n          return base::CreateVector(\n              num_rounds, [this, log_global_max_num_rows, index,\n                           &prover_data_by_round](size_t round) {\n                Proof proof;\n                std::vector<std::vector<F>> openings;\n                const ProverData& prover_data = *prover_data_by_round[round];\n                uint32_t log_max_num_rows =\n                    base::bits::CheckedLog2(mmcs_.GetMaxRowSize(prover_data));\n                uint32_t bits_reduced =\n                    log_global_max_num_rows - log_max_num_rows;\n                uint32_t reduced_index = index >> bits_reduced;\n                CHECK(mmcs_.CreateOpeningProof(reduced_index, prover_data,\n                                               &openings, &proof));\n                return BatchOpening<TwoAdicFRI>{std::move(openings),\n                                                std::move(proof)};\n              });\n        },\n        pow_witness_for_testing);\n    return true;\n  }\n\n  [[nodiscard]] bool VerifyOpeningProof(\n      const std::vector<Commitment>& commits_by_round,\n      const std::vector<std::vector<Domain>>& domains_by_round,\n      const OpeningPoints& points_by_round,\n      const OpenedValues& opened_values_by_round, const FRIProof& proof,\n      Challenger& challenger) const {\n    TRACE_EVENT(\"ProofVerification\", \"TwoAdicFRI::VerifyOpeningProof\");\n    // Batch combination challenge\n    const ExtF alpha = challenger.template SampleExtElement<ExtF>();\n    VLOG(2) << \"FRI(alpha): \" << alpha.ToHexString(true);\n    uint32_t log_global_max_num_rows =\n        proof.commit_phase_commits.size() + config_.log_blowup;\n    return fri::Verify(\n        config_, proof, challenger,\n        [this, alpha, log_global_max_num_rows, &commits_by_round,\n         &domains_by_round, &points_by_round, &opened_values_by_round](\n            size_t index, const InputProof& input_proof,\n            std::vector<size_t>& ro_num_rows, std::vector<ExtF>& ro_values) {\n          struct ReducedOpening {\n            ExtF value;\n            ExtF pow;\n\n            static ReducedOpening Default() {\n              return {\n                  ExtF::Zero(),\n                  ExtF::One(),\n              };\n            }\n          };\n\n          absl::btree_map<size_t, ReducedOpening> reduced_openings;\n          size_t num_rounds = commits_by_round.size();\n          for (size_t round = 0; round < num_rounds; ++round) {\n            const std::vector<Domain>& domains = domains_by_round[round];\n            const OpeningPointsForRound& points = points_by_round[round];\n            const OpenedValuesForRound& opened_values =\n                opened_values_by_round[round];\n            size_t vals_size = opened_values.size();\n            size_t batch_max_num_rows = 0;\n            std::vector<math::Dimensions> batch_dims = base::CreateVector(\n                vals_size,\n                [this, &batch_max_num_rows, &domains](size_t batch_idx) {\n                  const Domain& domain = domains[batch_idx];\n                  size_t num_rows = domain.domain()->size()\n                                    << config_.log_blowup;\n                  batch_max_num_rows = std::max(batch_max_num_rows, num_rows);\n                  return math::Dimensions{0, num_rows};\n                });\n            uint32_t bits_reduced = log_global_max_num_rows -\n                                    base::bits::CheckedLog2(batch_max_num_rows);\n            uint32_t reduced_index = index >> bits_reduced;\n            const std::vector<std::vector<F>>& opened_values_in =\n                input_proof[round].opened_values;\n\n            CHECK(mmcs_.VerifyOpeningProof(commits_by_round[round], batch_dims,\n                                           reduced_index, opened_values_in,\n                                           input_proof[round].opening_proof));\n\n            for (size_t batch_idx = 0; batch_idx < vals_size; ++batch_idx) {\n              const Domain& domain = domains[batch_idx];\n              const std::vector<ExtF>& cur_points = points[batch_idx];\n              const std::vector<std::vector<ExtF>>& cur_values =\n                  opened_values[batch_idx];\n              const std::vector<ExtF> cur_values_in = base::Map(\n                  opened_values_in[batch_idx], [](F f) { return ExtF(f); });\n              uint32_t log_num_rows =\n                  domain.domain()->log_size_of_group() + config_.log_blowup;\n              uint32_t bits_reduced = log_global_max_num_rows - log_num_rows;\n              uint32_t rev_reduced_index = base::bits::ReverseBitsLen(\n                  index >> bits_reduced, log_num_rows);\n              F w;\n              CHECK(F::GetRootOfUnity(size_t{1} << log_num_rows, &w));\n              ExtF x(F::FromMontgomery(F::Config::kSubgroupGenerator) *\n                     w.Pow(rev_reduced_index));\n\n              auto it = reduced_openings.try_emplace(log_num_rows,\n                                                     ReducedOpening::Default());\n              ReducedOpening& reduced_opening = it.first->second;\n              for (size_t i = 0; i < cur_points.size(); ++i) {\n                const ExtF& z = cur_points[i];\n                ExtF denom = unwrap((x - z).Inverse());\n                const std::vector<ExtF>& ps_at_z = cur_values[i];\n                CHECK_EQ(ps_at_z.size(), cur_values_in.size());\n                for (size_t j = 0; j < ps_at_z.size(); ++j) {\n                  ExtF quotient = (cur_values_in[j] - ps_at_z[j]) * denom;\n                  reduced_opening.value += reduced_opening.pow * quotient;\n                  reduced_opening.pow *= alpha;\n                }\n              }\n            }\n          }\n          ro_num_rows.reserve(reduced_openings.size());\n          ro_values.reserve(reduced_openings.size());\n          for (auto it = reduced_openings.rbegin();\n               it != reduced_openings.rend(); ++it) {\n            ro_num_rows.emplace_back(it->first);\n            ro_values.emplace_back(std::move(it->second.value));\n          }\n        });\n  }\n\n private:\n  friend class c::crypto::TwoAdicFRIImpl<ExtF, InputMMCS, ChallengeMMCS,\n                                         Challenger>;\n\n  static absl::flat_hash_map<ExtF, std::vector<ExtF>>\n  ComputeInverseDenominators(\n      const std::vector<absl::Span<\n          const Eigen::Map<const math::RowMajorMatrix<F>>>>& matrices_by_round,\n      const OpeningPoints& points_by_round, F coset_shift) {\n    TRACE_EVENT(\"Utils\", \"ComputeInverseDenominators\");\n    size_t num_rounds = matrices_by_round.size();\n\n    absl::flat_hash_map<ExtF, uint32_t> max_log_num_rows_for_point;\n    uint32_t max_log_num_rows = 0;\n    for (size_t round = 0; round < num_rounds; ++round) {\n      absl::Span<const Eigen::Map<const math::RowMajorMatrix<F>>> matrices =\n          matrices_by_round[round];\n      const OpeningPointsForRound& points_vec_for_round =\n          points_by_round[round];\n      for (size_t i = 0; i < matrices.size(); ++i) {\n        const Eigen::Map<const math::RowMajorMatrix<F>>& matrix = matrices[i];\n        const std::vector<ExtF>& points_for_mat = points_vec_for_round[i];\n        uint32_t log_num_rows =\n            base::bits::CheckedLog2(static_cast<uint32_t>(matrix.rows()));\n        max_log_num_rows = std::max(max_log_num_rows, log_num_rows);\n        for (const ExtF& point : points_for_mat) {\n          const auto [it, inserted] =\n              max_log_num_rows_for_point.try_emplace(point, log_num_rows);\n          if (!inserted) {\n            it->second = std::max(it->second, log_num_rows);\n          }\n        }\n      }\n    }\n\n    // Compute the largest subgroup we will use, in bitrev order.\n    F w;\n    CHECK(F::GetRootOfUnity(size_t{1} << max_log_num_rows, &w));\n    // TODO(chokobole): Change type of |subgroup| to |std::vector<ExtF>|.\n    std::vector<F> subgroup = F::GetBitRevIndexSuccessivePowers(\n        size_t{1} << max_log_num_rows, w, coset_shift);\n\n    absl::flat_hash_map<ExtF, std::vector<ExtF>> ret;\n    ret.reserve(max_log_num_rows_for_point.size());\n    for (auto it = max_log_num_rows_for_point.begin();\n         it != max_log_num_rows_for_point.end(); ++it) {\n      const ExtF& point = it->first;\n      uint32_t log_num_rows = it->second;\n      std::vector<ExtF> temp(size_t{1} << log_num_rows);\n      base::Parallelize(\n          temp, [&subgroup, &point](absl::Span<ExtF> chunk, size_t chunk_offset,\n                                    size_t chunk_size) {\n            size_t start = chunk_offset * chunk_size;\n            for (size_t i = start; i < start + chunk.size(); ++i) {\n              chunk[i - start] = ExtF(subgroup[i]) - point;\n            }\n            CHECK(ExtF::BatchInverseInPlace(chunk));\n          });\n      ret[point] = std::move(temp);\n    }\n    return ret;\n  }\n\n  // Slight variation of this approach:\n  // https://hackmd.io/@vbuterin/barycentric_evaluation\n  template <typename Derived>\n  static std::vector<ExtF> InterpolateCoset(\n      const Eigen::MatrixBase<Derived>& coset_evals, const F shift,\n      const ExtF& point) {\n    TRACE_EVENT(\"Utils\", \"InterpolateCoset\");\n    size_t num_rows = static_cast<size_t>(coset_evals.rows());\n    size_t num_cols = static_cast<size_t>(coset_evals.cols());\n    uint32_t log_num_rows = base::bits::CheckedLog2(num_rows);\n    F w;\n    CHECK(F::GetRootOfUnity(num_rows, &w));\n\n    std::vector<std::vector<ExtF>> sums = base::ParallelizeMap(\n        num_rows,\n        [num_cols, shift, w, &point, &coset_evals](\n            size_t chunk_actual_size, size_t chunk_offset, size_t chunk_size) {\n          size_t row_start = chunk_offset * chunk_size;\n          F pow = w.Pow(row_start);\n          std::vector<ExtF> sum_tracker(num_cols, ExtF::Zero());\n          ExtF shifted_pow(shift * pow);\n          std::vector<ExtF> diff_invs = base::CreateVector(\n              chunk_actual_size, [w, &point, &shifted_pow](size_t i) {\n                ExtF temp = point - shifted_pow;\n                shifted_pow *= w;\n                return temp;\n              });\n          CHECK(ExtF::BatchInverseInPlaceSerial(diff_invs));\n          for (size_t r = 0; r < chunk_actual_size; ++r) {\n            for (size_t c = 0; c < num_cols; ++c) {\n              sum_tracker[c] +=\n                  diff_invs[r] * pow * coset_evals(row_start + r, c);\n            }\n            pow *= w;\n          }\n          return sum_tracker;\n        });\n    const ExtF zeroifier =\n        point.ExpPowOfTwo(log_num_rows) - ExtF(shift).ExpPowOfTwo(log_num_rows);\n    const F denominator = F(num_rows) * shift.Pow(num_rows - 1);\n    const ExtF scale = zeroifier * ExtF(unwrap(denominator.Inverse()));\n\n    std::vector<ExtF> sum(num_cols, ExtF::Zero());\n    for (size_t chunk_offset = 0; chunk_offset < sums.size(); ++chunk_offset) {\n      for (size_t c = 0; c < num_cols; ++c) {\n        sum[c] += sums[chunk_offset][c];\n      }\n    }\n    for (size_t c = 0; c < num_cols; ++c) {\n      sum[c] *= scale;\n    }\n    return sum;\n  }\n\n  InputMMCS mmcs_;\n  FRIConfig<ChallengeMMCS> config_;\n};\n\n}  // namespace crypto\n}  // namespace tachyon\n\n#endif  // TACHYON_CRYPTO_COMMITMENTS_FRI_TWO_ADIC_FRI_H_\n"
  },
  {
    "path": "tachyon/crypto/commitments/fri/two_adic_fri_unittest.cc",
    "content": "#include \"tachyon/crypto/commitments/fri/two_adic_fri.h\"\n\n#include <tuple>\n#include <utility>\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/crypto/challenger/duplex_challenger.h\"\n#include \"tachyon/crypto/commitments/merkle_tree/field_merkle_tree/extension_field_merkle_tree_mmcs.h\"\n#include \"tachyon/crypto/commitments/merkle_tree/field_merkle_tree/field_merkle_tree_mmcs.h\"\n#include \"tachyon/crypto/hashes/sponge/padding_free_sponge.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/param_traits/poseidon2_baby_bear.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_params.h\"\n#include \"tachyon/crypto/hashes/sponge/truncated_permutation.h\"\n#include \"tachyon/math/finite_fields/baby_bear/baby_bear4.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n\nnamespace tachyon::crypto {\n\nnamespace {\n\nconstexpr size_t kRate = 8;\nconstexpr size_t kChunk = 8;\nconstexpr size_t kN = 2;\n\nusing F = math::BabyBear;\nusing ExtF = math::BabyBear4;\nusing PackedF = math::PackedBabyBear;\nusing ExtPackedF = math::PackedBabyBear4;\nusing Params = Poseidon2Params<Poseidon2Vendor::kPlonky3,\n                               Poseidon2Vendor::kPlonky3, F, 15, 7>;\nusing PackedParams = Poseidon2Params<Poseidon2Vendor::kPlonky3,\n                                     Poseidon2Vendor::kPlonky3, PackedF, 15, 7>;\nusing Domain = TwoAdicMultiplicativeCoset<F>;\nusing Poseidon2 = Poseidon2Sponge<Params>;\nusing PackedPoseidon2 = Poseidon2Sponge<PackedParams>;\nusing MyHasher = PaddingFreeSponge<Poseidon2, kRate, kChunk>;\nusing MyPackedHasher = PaddingFreeSponge<PackedPoseidon2, kRate, kChunk>;\nusing MyCompressor = TruncatedPermutation<Poseidon2, kChunk, kN>;\nusing MyPackedCompressor = TruncatedPermutation<PackedPoseidon2, kChunk, kN>;\nusing MMCS = FieldMerkleTreeMMCS<F, MyHasher, MyPackedHasher, MyCompressor,\n                                 MyPackedCompressor, kChunk>;\nusing ExtMMCS = FieldMerkleTreeMMCS<ExtF, MyHasher, MyPackedHasher,\n                                    MyCompressor, MyPackedCompressor, kChunk>;\nusing ChallengeMMCS = ExtensionFieldMerkleTreeMMCS<ExtF, ExtMMCS>;\nusing Challenger = DuplexChallenger<Poseidon2, kRate>;\nusing Coset = TwoAdicMultiplicativeCoset<F>;\nusing MyPCS = TwoAdicFRI<ExtF, MMCS, ChallengeMMCS, Challenger>;\n\nclass TwoAdicFRITest : public testing::Test {\n public:\n  TwoAdicFRITest() = default;\n\n  static void SetUpTestSuite() {\n    ExtF::Init();\n    ExtPackedF::Init();\n  }\n\n  void SetUp() override {\n    MMCS mmcs;\n    ChallengeMMCS challenge_mmcs;\n\n    // TODO(ashjeong): Include separate test for |log_blowup| = 2\n    FRIConfig<ChallengeMMCS> fri_config{1, 10, 8, std::move(challenge_mmcs)};\n\n    pcs_ = MyPCS(std::move(mmcs), std::move(fri_config));\n  }\n\n  void TestProtocol(std::vector<std::vector<uint32_t>> log_degrees_by_round) {\n    using Commitment = typename MMCS::Commitment;\n    using ProverData = typename MMCS::ProverData;\n    using OpenedValues =\n        std::vector<std::vector<std::vector<std::vector<ExtF>>>>;\n    using Proof = FRIProof<MyPCS>;\n\n    size_t num_rounds = log_degrees_by_round.size();\n    std::vector<std::vector<Domain>> domains_by_round(num_rounds);\n    std::vector<Commitment> commits_by_round(num_rounds);\n    std::vector<std::unique_ptr<ProverData>> data_by_round(num_rounds);\n    Challenger p_challenger = challenger_;\n    for (size_t round = 0; round < num_rounds; ++round) {\n      const std::vector<uint32_t>& log_degrees = log_degrees_by_round[round];\n      std::vector<Domain> inner_domains(log_degrees.size());\n      std::vector<math::RowMajorMatrix<F>> inner_polys(log_degrees.size());\n      for (size_t i = 0; i < log_degrees.size(); ++i) {\n        size_t rows = size_t{1} << log_degrees[i];\n        // TODO(ashjeong): make the latter number randomized from 0-10\n        size_t cols = 5;\n        inner_domains[i] = pcs_.GetNaturalDomainForDegree(rows);\n        inner_polys[i] = math::RowMajorMatrix<F>::Random(rows, cols);\n      }\n      data_by_round[round].reset(new ProverData());\n      ASSERT_TRUE(pcs_.Commit(inner_domains, inner_polys,\n                              &commits_by_round[round],\n                              data_by_round[round].get()));\n      domains_by_round[round] = std::move(inner_domains);\n    }\n    p_challenger.ObserveContainer2D(commits_by_round);\n    ExtF zeta = p_challenger.template SampleExtElement<ExtF>();\n\n    std::vector<std::vector<std::vector<ExtF>>> points_by_round(num_rounds);\n    for (size_t round = 0; round < num_rounds; ++round) {\n      points_by_round[round] = std::vector<std::vector<ExtF>>(\n          log_degrees_by_round[round].size(), {zeta});\n    }\n    OpenedValues opened_values_by_round;\n    Proof proof;\n    ASSERT_TRUE(pcs_.CreateOpeningProof(data_by_round, points_by_round,\n                                        p_challenger, &opened_values_by_round,\n                                        &proof));\n    ASSERT_EQ(opened_values_by_round.size(), num_rounds);\n\n    // Verify the proof\n    Challenger v_challenger = challenger_;\n    v_challenger.ObserveContainer2D(commits_by_round);\n    ExtF verifier_zeta = v_challenger.template SampleExtElement<ExtF>();\n    ASSERT_EQ(verifier_zeta, zeta);\n\n    ASSERT_TRUE(pcs_.VerifyOpeningProof(commits_by_round, domains_by_round,\n                                        points_by_round, opened_values_by_round,\n                                        proof, v_challenger));\n  }\n\n protected:\n  MyPCS pcs_;\n  Challenger challenger_;\n};\n\n}  // namespace\n\nTEST_F(TwoAdicFRITest, Single) {\n  for (uint32_t i = 3; i < 6; ++i) TestProtocol({{i}});\n}\n\nTEST_F(TwoAdicFRITest, ManyEqual) {\n  for (uint32_t i = 2; i < 5; ++i) TestProtocol({std::vector<uint32_t>(5, i)});\n}\n\nTEST_F(TwoAdicFRITest, ManyDifferent) {\n  for (uint32_t i = 2; i < 4; ++i) {\n    std::vector<uint32_t> input(i);\n    for (uint32_t j = 3; j < 3 + i; ++j) {\n      input[j - 3] = j;\n    }\n    TestProtocol({input});\n  }\n}\n\nTEST_F(TwoAdicFRITest, ManyDifferentRev) {\n  for (uint32_t i = 2; i < 4; ++i) {\n    std::vector<uint32_t> input(i);\n    for (uint32_t j = 3 + i - 1; j >= 3; --j) {\n      input[j - 3] = j;\n    }\n    TestProtocol({input});\n  }\n}\n\nTEST_F(TwoAdicFRITest, MultipleRounds) {\n  TestProtocol({{3}});\n  TestProtocol({{3}, {3}});\n  TestProtocol({{3}, {2}});\n  TestProtocol({{2}, {3}});\n  TestProtocol({{1, 2}});\n  TestProtocol({{4, 2}, {4, 2}});\n  TestProtocol({{2, 3}, {3, 3}});\n  TestProtocol({{3, 3}, {2, 2}});\n  TestProtocol({{2}, {3, 3}});\n}\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/crypto/commitments/fri/two_adic_multiplicative_coset.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_CRYPTO_COMMITMENTS_FRI_TWO_ADIC_MULTIPLICATIVE_COSET_H_\n#define TACHYON_CRYPTO_COMMITMENTS_FRI_TWO_ADIC_MULTIPLICATIVE_COSET_H_\n\n#include <memory>\n#include <optional>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/bits.h\"\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/optional.h\"\n#include \"tachyon/crypto/commitments/fri/lagrange_selectors.h\"\n#include \"tachyon/math/polynomials/univariate/radix2_evaluation_domain.h\"\n\nnamespace tachyon::crypto {\n\ntemplate <typename F>\nclass TwoAdicMultiplicativeCoset {\n  static_assert(F::Config::kModulusBits <= 32);\n\n public:\n  constexpr TwoAdicMultiplicativeCoset() = default;\n\n  TwoAdicMultiplicativeCoset(uint32_t log_n, F shift) {\n    domain_.reset(static_cast<math::Radix2EvaluationDomain<F>*>(\n        math::Radix2EvaluationDomain<F>::Create(size_t{1} << log_n)\n            ->GetCoset(shift)\n            .release()));\n  }\n\n  const math::Radix2EvaluationDomain<F>* domain() const {\n    return domain_.get();\n  }\n  math::Radix2EvaluationDomain<F>* domain() { return domain_.get(); }\n\n  template <typename ExtField>\n  ExtField GetNextPoint(const ExtField& x) const {\n    return x * domain_->group_gen();\n  }\n\n  TwoAdicMultiplicativeCoset CreateDisjointDomain(size_t min_size) const {\n    return {\n        base::bits::SafeLog2Ceiling(min_size),\n        domain_->offset() * F::FromMontgomery(F::Config::kSubgroupGenerator)};\n  }\n\n  template <typename ExtField>\n  ExtField GetZpAtPoint(const ExtField& point) const {\n    return (point * domain_->offset_inv())\n               .ExpPowOfTwo(domain_->log_size_of_group()) -\n           ExtField::One();\n  }\n\n  std::vector<TwoAdicMultiplicativeCoset> SplitDomains(\n      size_t num_chunks) const {\n    uint32_t log_chunks = base::bits::CheckedLog2(num_chunks);\n    F f = domain_->offset();\n    return base::CreateVector(num_chunks, [this, log_chunks, &f](size_t i) {\n      TwoAdicMultiplicativeCoset ret{domain_->log_size_of_group() - log_chunks,\n                                     f};\n      f *= domain_->group_gen();\n      return ret;\n    });\n  }\n\n  template <typename ExtField>\n  LagrangeSelectors<ExtField> GetSelectorsAtPoint(const ExtField& point) const {\n    ExtField unshifted_point = point * domain_->offset_inv();\n    ExtField z_h = unshifted_point.ExpPowOfTwo(domain_->log_size_of_group()) -\n                   ExtField::One();\n    ExtField first_row = unwrap(z_h / (unshifted_point - ExtField::One()));\n    ExtField transition = unshifted_point - ExtField(domain_->group_gen_inv());\n    ExtField last_row = unwrap(z_h / transition);\n    ExtField inv_zeroifier = unwrap(z_h.Inverse());\n    return {std::move(first_row), std::move(last_row), std::move(transition),\n            std::move(inv_zeroifier)};\n  }\n\n  LagrangeSelectors<std::vector<F>> GetSelectorsOnCoset(\n      const TwoAdicMultiplicativeCoset& coset) const {\n    F coset_shift = coset.domain()->offset();\n\n    CHECK_EQ(domain_->offset(), F::One());\n    CHECK_NE(coset_shift, F::One());\n    CHECK_GE(domain_->log_size_of_group(), coset.domain()->log_size_of_group());\n    uint32_t rate_bits =\n        coset.domain()->log_size_of_group() - domain_->log_size_of_group();\n    F s_pow_n = coset_shift.ExpPowOfTwo(domain_->log_size_of_group());\n\n    // Evals of Z_H(X) = Xⁿ - 1\n    size_t evals_size = size_t{1} << rate_bits;\n    std::vector<F> evals(evals_size);\n    std::vector<F> inv_denoms_inv_zeroifier(evals_size);\n    base::Parallelize(\n        evals_size, [this, &s_pow_n, &evals, &inv_denoms_inv_zeroifier](\n                        size_t len, size_t chunk_offset, size_t chunk_size) {\n          size_t start = chunk_offset * chunk_size;\n          F eval = s_pow_n * domain_->group_gen().Pow(start);\n          for (size_t i = start; i < start + len; ++i) {\n            evals[i] = eval - F::One();\n            inv_denoms_inv_zeroifier[i] = evals[i];\n          }\n          absl::Span<F> inv_denoms_inv_zeroifier_chunk =\n              absl::MakeSpan(&inv_denoms_inv_zeroifier[start], chunk_size);\n          CHECK(F::BatchInverseInPlaceSerial(inv_denoms_inv_zeroifier_chunk));\n        });\n\n    F coset_i = domain_->group_gen().ExpPowOfTwo(domain_->log_size_of_group()) *\n                domain_->group_gen_inv();\n\n    size_t sz = coset.domain()->size();\n    // NOTE(batzor): These vectors are initialized below in the parallel loop so\n    // it is safe to keep it uninitialized here.\n    std::vector<F> first_row(sz);\n    std::vector<F> last_row(sz);\n    std::vector<F> transition(sz);\n    std::vector<F> inv_zeroifier(sz);\n\n    base::Parallelize(sz, [this, &coset, &coset_shift, &evals, &coset_i,\n                           &inv_denoms_inv_zeroifier, &first_row, &last_row,\n                           &transition,\n                           &inv_zeroifier](size_t len, size_t chunk_offset,\n                                           size_t chunk_size) {\n      size_t start = chunk_offset * chunk_size;\n      F x = coset_shift * coset.domain()->group_gen().Pow(start);\n      for (size_t i = start; i < start + len; ++i) {\n        first_row[i] = x - F::One();\n        last_row[i] = x - coset_i;\n        transition[i] = x - domain_->group_gen_inv();\n        x *= coset.domain()->group_gen();\n      }\n      absl::Span<F> first_row_chunk = absl::MakeSpan(&first_row[start], len);\n      CHECK(F::BatchInverseInPlaceSerial(first_row_chunk));\n      absl::Span<F> last_row_chunk = absl::MakeSpan(&last_row[start], len);\n      CHECK(F::BatchInverseInPlaceSerial(last_row_chunk));\n\n      for (size_t i = start; i < start + len; ++i) {\n        size_t evals_i = i % evals.size();\n        first_row[i] *= evals[evals_i];\n        last_row[i] *= evals[evals_i];\n        inv_zeroifier[i] = inv_denoms_inv_zeroifier[evals_i];\n      }\n    });\n\n    return {std::move(first_row), std::move(last_row), std::move(transition),\n            std::move(inv_zeroifier)};\n  }\n\n private:\n  std::unique_ptr<math::Radix2EvaluationDomain<F>> domain_;\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_COMMITMENTS_FRI_TWO_ADIC_MULTIPLICATIVE_COSET_H_\n"
  },
  {
    "path": "tachyon/crypto/commitments/fri/two_adic_multiplicative_coset_unittest.cc",
    "content": "#include \"tachyon/crypto/commitments/fri/two_adic_multiplicative_coset.h\"\n\n#include \"tachyon/math/finite_fields/baby_bear/baby_bear.h\"\n#include \"tachyon/math/finite_fields/baby_bear/baby_bear4.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n\nnamespace tachyon::crypto {\nnamespace {\n\nusing F = math::BabyBear;\nusing ExtField = math::BabyBear4;\n\nclass TwoAdicMultiplicativeCosetTest : public math::FiniteFieldTest<ExtField> {\n public:\n  void SetUp() override {\n    p0_ = ExtField(F(11) * F(64));\n    mult_coset_ = TwoAdicMultiplicativeCoset<F>(5, F(11));\n  }\n\n protected:\n  ExtField p0_;\n  TwoAdicMultiplicativeCoset<F> mult_coset_;\n};\n\n}  // namespace\n\n// TODO(ashjeong): Generalize all tests.\nTEST_F(TwoAdicMultiplicativeCosetTest, GetNextPoint) {\n  ExtField res = mult_coset_.GetNextPoint(p0_);\n  EXPECT_EQ(res, ExtField(F(1528649335)));\n}\n\nTEST_F(TwoAdicMultiplicativeCosetTest, CreateDisjointDomain) {\n  TwoAdicMultiplicativeCoset<F> disjoint_domain =\n      mult_coset_.CreateDisjointDomain(3);\n  EXPECT_EQ(disjoint_domain.domain()->size(), 4);\n  EXPECT_EQ(disjoint_domain.domain()->offset(), F(341));\n}\n\nTEST_F(TwoAdicMultiplicativeCosetTest, GetZpAtPoint) {\n  EXPECT_EQ(mult_coset_.GetZpAtPoint(ExtField(mult_coset_.domain()->offset())),\n            ExtField::Zero());\n  EXPECT_EQ(mult_coset_.GetZpAtPoint(p0_), ExtField(F(193919811)));\n}\n\nTEST_F(TwoAdicMultiplicativeCosetTest, SplitDomains) {\n  std::vector<TwoAdicMultiplicativeCoset<F>> split_domains =\n      mult_coset_.SplitDomains(4);\n  std::vector<F> expected{F(11), F(307000666), F(147092939), F(1567650515)};\n  for (size_t i = 0; i < expected.size(); ++i) {\n    EXPECT_EQ(split_domains[i].domain()->size(), 8);\n    EXPECT_EQ(split_domains[i].domain()->offset(), expected[i]);\n  }\n}\n\nTEST_F(TwoAdicMultiplicativeCosetTest, GetSelectorsAtPoint) {\n  LagrangeSelectors<ExtField> expected{\n      ExtField(F(98947898)), ExtField(F(425615992)), ExtField(F(740045704)),\n      ExtField(F(1108440347))};\n  EXPECT_EQ(mult_coset_.GetSelectorsAtPoint(p0_), expected);\n}\n\nTEST_F(TwoAdicMultiplicativeCosetTest, GetSelectorsOnCoset) {\n  TwoAdicMultiplicativeCoset<F> other_coset =\n      TwoAdicMultiplicativeCoset<F>(2, F(7));\n  mult_coset_ = TwoAdicMultiplicativeCoset<F>(2, F::One());\n  LagrangeSelectors<std::vector<F>> result =\n      mult_coset_.GetSelectorsOnCoset(other_coset);\n  std::vector<F> expected{F(400), F(1089934753), F(2013265621), F(923331072)};\n  EXPECT_EQ(result.first_row, expected);\n  expected =\n      std::vector<F>{F(1593752394), F(901253718), F(1593751722), F(811594297)};\n  EXPECT_EQ(result.last_row, expected);\n  expected = std::vector<F>{F(1728404520), F(1747640578), F(1728404506),\n                            F(1709168448)};\n  EXPECT_EQ(result.transition, expected);\n  expected = std::vector<F>{F(1609773876), F(1609773876), F(1609773876),\n                            F(1609773876)};\n  EXPECT_EQ(result.inv_zeroifier, expected);\n}\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/crypto/commitments/fri/verify.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_CRYPTO_COMMITMENTS_FRI_VERIFY_H_\n#define TACHYON_CRYPTO_COMMITMENTS_FRI_VERIFY_H_\n\n#include <vector>\n\n#include \"tachyon/base/bits.h\"\n#include \"tachyon/base/ranges/algorithm.h\"\n#include \"tachyon/crypto/challenger/challenger.h\"\n#include \"tachyon/crypto/commitments/fri/fri_config.h\"\n#include \"tachyon/crypto/commitments/fri/fri_proof.h\"\n#include \"tachyon/math/geometry/dimensions.h\"\n\nnamespace tachyon::crypto::fri {\n\ntemplate <typename PCS>\nstruct CommitStep {\n  using ChallengeMMCS = typename PCS::ChallengeMMCS;\n  using F = typename ChallengeMMCS::Field;\n  using Commitment = typename ChallengeMMCS::Commitment;\n\n  F beta;\n  Commitment commit;\n  CommitPhaseProofStep<PCS> opening;\n};\n\ntemplate <typename PCS, typename MMCS, typename F>\nF VerifyQuery(uint32_t index, uint32_t log_max_num_rows,\n              const FRIConfig<MMCS>& config,\n              const std::vector<CommitStep<PCS>>& steps,\n              const std::vector<size_t>& ro_num_rows,\n              const std::vector<F>& ro_values) {\n  F folded_eval = F::Zero();\n  size_t ro_idx = 0;\n  size_t ro_size = ro_num_rows.size();\n\n  for (uint32_t step_idx = 0; step_idx < steps.size(); ++step_idx) {\n    uint32_t log_folded_num_rows = log_max_num_rows - step_idx - 1;\n    if (ro_idx != ro_size && ro_num_rows[ro_idx] == log_folded_num_rows + 1) {\n      folded_eval += ro_values[ro_idx++];\n    }\n    size_t index_sibling = index ^ 1;\n    size_t index_pair = index >> 1;\n    std::vector<std::vector<F>> evals = {{folded_eval, folded_eval}};\n    evals[0][index_sibling % 2] = steps[step_idx].opening.sibling_value;\n    CHECK(config.mmcs.VerifyOpeningProof(\n        steps[step_idx].commit,\n        {math::Dimensions(2, size_t{1} << log_folded_num_rows)}, index_pair,\n        evals, steps[step_idx].opening.opening_proof));\n    folded_eval = FoldRow(index_pair, log_folded_num_rows, steps[step_idx].beta,\n                          evals[0]);\n    index = index_pair;\n  }\n  CHECK_LT(index, config.Blowup()) << \"index was \" << index;\n  CHECK_EQ(ro_idx, ro_size);\n\n  return folded_eval;\n}\n\ntemplate <typename PCS, typename ChallengeMMCS, typename Challenger,\n          typename OpenInputCallback>\n[[nodiscard]] bool Verify(const FRIConfig<ChallengeMMCS>& config,\n                          const FRIProof<PCS>& proof, Challenger& challenger,\n                          OpenInputCallback open_input) {\n  using ExtF = typename ChallengeMMCS::Field;\n  using Commitment = typename ChallengeMMCS::Commitment;\n  size_t num_commits = proof.commit_phase_commits.size();\n  std::vector<ExtF> betas = base::Map(\n      proof.commit_phase_commits,\n      [&challenger](size_t i, const Commitment& commit) {\n        challenger.ObserveContainer(commit);\n        ExtF beta = challenger.template SampleExtElement<ExtF>();\n        VLOG(2) << \"FRI(beta[\" << i << \"]): \" << beta.ToHexString(true);\n        return beta;\n      });\n  challenger.ObserveContainer(proof.final_eval);\n  VLOG(2) << \"FRI(final_eval): \" << proof.final_eval.ToHexString(true);\n\n  if (proof.query_proofs.size() != config.num_queries) {\n    LOG(ERROR) << \"proof size doesn't match \" << proof.query_proofs.size()\n               << \" vs \" << config.num_queries;\n    return false;\n  }\n  // Check PoW.\n  VLOG(2) << \"FRI(pow): \" << proof.pow_witness.ToHexString(true);\n  if (!(challenger.CheckWitness(config.proof_of_work_bits,\n                                proof.pow_witness))) {\n    LOG(ERROR) << \"failed to check pow\";\n    return false;\n  }\n\n  uint32_t log_max_num_rows = num_commits + config.log_blowup;\n\n  for (size_t i = 0; i < proof.query_proofs.size(); ++i) {\n    std::vector<size_t> ro_num_rows;\n    std::vector<ExtF> ro_values;\n    size_t index = challenger.SampleBits(log_max_num_rows);\n    VLOG(2) << \"FRI(index[\" << i << \"]): \" << index;\n    open_input(index, proof.query_proofs[i].input_proof, ro_num_rows,\n               ro_values);\n\n#if DCHECK_IS_ON()\n    // Check reduced openings sorted by |num_rows| descending\n    DCHECK(base::ranges::is_sorted(ro_num_rows.begin(), ro_num_rows.end(),\n                                   base::ranges::greater()));\n#endif\n    std::vector<CommitStep<PCS>> steps =\n        base::CreateVector(num_commits, [&betas, &proof, i](size_t j) {\n          return CommitStep<PCS>{\n              betas[j], proof.commit_phase_commits[j],\n              proof.query_proofs[i].commit_phase_openings[j]};\n        });\n    ExtF folded_eval = VerifyQuery(index, log_max_num_rows, config, steps,\n                                   ro_num_rows, ro_values);\n    if (folded_eval != proof.final_eval) {\n      LOG(ERROR) << \"final_eval is not matched: \"\n                 << folded_eval.ToHexString(true);\n      return false;\n    }\n  }\n  return true;\n}\n\n}  // namespace tachyon::crypto::fri\n\n#endif  // TACHYON_CRYPTO_COMMITMENTS_FRI_VERIFY_H_\n"
  },
  {
    "path": "tachyon/crypto/commitments/kzg/BUILD.bazel",
    "content": "load(\n    \"//bazel:tachyon_cc.bzl\",\n    \"tachyon_cc_library\",\n    \"tachyon_cc_unittest\",\n)\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"gwc\",\n    hdrs = [\"gwc.h\"],\n    deps = [\n        \":kzg_family\",\n        \"//tachyon/crypto/commitments:polynomial_openings\",\n        \"//tachyon/crypto/commitments:univariate_polynomial_commitment_scheme\",\n        \"//tachyon/crypto/transcripts:transcript\",\n        \"//tachyon/math/elliptic_curves/pairing\",\n        \"@com_google_googletest//:gtest_prod\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"kzg\",\n    hdrs = [\"kzg.h\"],\n    deps = [\n        \"//tachyon/base:logging\",\n        \"//tachyon/base/buffer:copyable\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/crypto/commitments:batch_commitment_state\",\n        \"//tachyon/device/gpu:gpu_memory\",\n        \"//tachyon/device/gpu:scoped_mem_pool\",\n        \"//tachyon/device/gpu:scoped_stream\",\n        \"//tachyon/math/elliptic_curves/msm:variable_base_msm\",\n        \"//tachyon/math/elliptic_curves/msm:variable_base_msm_gpu\",\n        \"//tachyon/math/polynomials/univariate:univariate_evaluation_domain\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"kzg_family\",\n    hdrs = [\"kzg_family.h\"],\n    deps = [\":kzg\"],\n)\n\ntachyon_cc_library(\n    name = \"kzg_family_test\",\n    testonly = True,\n    hdrs = [\"kzg_family_test.h\"],\n    deps = [\n        \":kzg\",\n        \"//tachyon/base/buffer\",\n        \"//tachyon/base/json\",\n        \"//tachyon/crypto/commitments/test:bn254_kzg_polynomial_openings\",\n        \"//tachyon/crypto/transcripts:simple_transcript\",\n        \"//tachyon/math/elliptic_curves/bn/bn254\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:g1\",\n        \"//tachyon/math/polynomials/univariate:univariate_evaluation_domain_factory\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"shplonk\",\n    hdrs = [\"shplonk.h\"],\n    deps = [\n        \":kzg_family\",\n        \"//tachyon/base/containers:contains\",\n        \"//tachyon/crypto/commitments:polynomial_openings\",\n        \"//tachyon/crypto/commitments:univariate_polynomial_commitment_scheme\",\n        \"//tachyon/crypto/transcripts:transcript\",\n        \"//tachyon/math/elliptic_curves/pairing\",\n        \"@com_google_googletest//:gtest_prod\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"kzg_unittests\",\n    srcs = [\n        \"gwc_unittest.cc\",\n        \"kzg_unittest.cc\",\n        \"shplonk_unittest.cc\",\n    ],\n    deps = [\n        \":gwc\",\n        \":kzg_family_test\",\n        \":shplonk\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/crypto/commitments/kzg/gwc.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_CRYPTO_COMMITMENTS_KZG_GWC_H_\n#define TACHYON_CRYPTO_COMMITMENTS_KZG_GWC_H_\n\n#include <array>\n#include <utility>\n#include <vector>\n\n#include \"gtest/gtest_prod.h\"\n\n#include \"tachyon/crypto/commitments/kzg/kzg_family.h\"\n#include \"tachyon/crypto/commitments/polynomial_openings.h\"\n#include \"tachyon/crypto/commitments/univariate_polynomial_commitment_scheme.h\"\n#include \"tachyon/crypto/transcripts/transcript.h\"\n#include \"tachyon/math/elliptic_curves/pairing/pairing.h\"\n\nnamespace tachyon {\nnamespace zk {\n\ntemplate <typename Curve, size_t MaxDegree, size_t MaxExtensionDegree,\n          typename _Commitment = typename math::Pippenger<\n              typename Curve::G1Curve::AffinePoint>::Bucket>\nclass GWCExtension;\n\n}  // namespace zk\n\nnamespace crypto {\n\ntemplate <typename Curve, size_t MaxDegree,\n          typename Commitment = typename math::Pippenger<\n              typename Curve::G1Curve::AffinePoint>::Bucket>\nclass GWC final : public UnivariatePolynomialCommitmentScheme<\n                      GWC<Curve, MaxDegree, Commitment>>,\n                  public KZGFamily<typename Curve::G1Curve::AffinePoint,\n                                   MaxDegree, Commitment> {\n public:\n  using Base =\n      UnivariatePolynomialCommitmentScheme<GWC<Curve, MaxDegree, Commitment>>;\n  using G1Point = typename Curve::G1Curve::AffinePoint;\n  using G2Point = typename Curve::G2Curve::AffinePoint;\n  using G2Prepared = typename Curve::G2Prepared;\n  using Fp12 = typename Curve::Fp12;\n  using Field = typename Base::Field;\n  using Poly = typename Base::Poly;\n  using Point = typename Poly::Point;\n\n  GWC() = default;\n  explicit GWC(KZG<G1Point, MaxDegree, Commitment>&& kzg)\n      : KZGFamily<G1Point, MaxDegree, Commitment>(std::move(kzg)) {}\n  GWC(KZG<G1Point, MaxDegree, Commitment>&& kzg, G2Point&& s_g2)\n      : KZGFamily<G1Point, MaxDegree, Commitment>(std::move(kzg)),\n        s_g2_(std::move(s_g2)),\n        g2_arr_({G2Prepared::From(G2Point::Generator()),\n                 G2Prepared::From(-s_g2)}) {}\n\n  const G2Point& s_g2() const { return s_g2_; }\n\n  void ResizeBatchCommitments() {\n    this->kzg_.ResizeBatchCommitments(\n        this->batch_commitment_state_.batch_count);\n  }\n\n  std::vector<Commitment> GetBatchCommitments() {\n    return this->kzg_.GetBatchCommitments(this->batch_commitment_state_);\n  }\n\n private:\n  friend class VectorCommitmentScheme<GWC<Curve, MaxDegree, Commitment>>;\n  friend class UnivariatePolynomialCommitmentScheme<\n      GWC<Curve, MaxDegree, Commitment>>;\n  template <typename, size_t, size_t, typename>\n  friend class zk::GWCExtension;\n  FRIEND_TEST(GWCTest, Copyable);\n\n  const char* Name() const { return \"GWC\"; }\n\n  // UnivariatePolynomialCommitmentScheme methods\n  template <typename Container>\n  [[nodiscard]] bool DoCreateOpeningProof(\n      const Container& poly_openings, TranscriptWriter<Commitment>* writer) {\n    Field v = writer->SqueezeChallenge();\n    VLOG(2) << \"GWC(v): \" << v.ToHexString(true);\n\n    PolynomialOpeningGrouper<Poly> grouper;\n    grouper.GroupBySinglePoint(poly_openings);\n\n    // Group |poly_openings| to |grouped_poly_openings_vec|.\n    // {x₀, [P₀, P₁, P₂]}\n    // {x₁, [P₀, P₁, P₂]}\n    // {x₂, [P₀, P₁, P₂, P₃]}\n    // {x₃, [P₃]}\n    // {x₄, [P₄]}\n    const std::vector<GroupedPolynomialOpenings<Poly>>&\n        grouped_poly_openings_vec = grouper.grouped_poly_openings_vec();\n\n    this->SetBatchMode(grouped_poly_openings_vec.size());\n    for (size_t i = 0; i < grouped_poly_openings_vec.size(); ++i) {\n      // clang-format off\n      // W₀(X) = H₀(X) / (X - x₀) = (P₀(X) - P₀(x₀)) + v(P₁(X) - P₁(x₀)) + v²(P₂(X) - P₂(x₀)) / (X - x₀)\n      // W₁(X) = H₁(X) / (X - x₁) = (P₀(X) - P₀(x₁)) + v(P₁(X) - P₁(x₁)) + v²(P₂(X) - P₂(x₁)) / (X - x₁)\n      // W₂(X) = H₂(X) / (X - x₂) = (P₀(X) - P₀(x₂)) + v(P₁(X) - P₁(x₂)) + v²(P₂(X) - P₂(x₂)) + v³(P₃(X) - P₃(x₂)) / (X - x₂)\n      // W₃(X) = H₃(X) / (X - x₃) = (P₃(X) - P₃(x₃)) / (X - x₃)\n      // W₄(X) = H₄(X) / (X - x₄) = (P₄(X) - P₄(x₄)) / (X - x₄)\n      // clang-format on\n      std::vector<Poly> low_degree_extensions;\n      Poly w = grouped_poly_openings_vec[i].CreateCombinedLowDegreeExtensions(\n          v, low_degree_extensions);\n      if (!this->Commit(w, i)) return false;\n    }\n\n    // Commit all the Wᵢ(X).\n    std::vector<Commitment> commitments = this->GetBatchCommitments();\n    for (const Commitment& commitment : commitments) {\n      if (!writer->WriteToProof(commitment)) return false;\n    }\n    return true;\n  }\n\n  template <typename Container>\n  [[nodiscard]] bool DoVerifyOpeningProof(\n      const Container& poly_openings,\n      TranscriptReader<Commitment>* reader) const {\n    using G1JacobianPoint = math::JacobianPoint<typename G1Point::Curve>;\n\n    Field v = reader->SqueezeChallenge();\n    VLOG(2) << \"GWC(v): \" << v.ToHexString(true);\n\n    PolynomialOpeningGrouper<Poly, Commitment> grouper;\n    grouper.GroupBySinglePoint(poly_openings);\n\n    // Group |poly_openings| to |grouped_poly_openings_vec|.\n    // {x₀, [C₀, C₁, C₂]}\n    // {x₁, [C₀, C₁, C₂]}\n    // {x₂, [C₀, C₁, C₂, C₃]}\n    // {x₃, [C₃]}\n    // {x₄, [C₄]}\n    const std::vector<GroupedPolynomialOpenings<Poly, Commitment>>&\n        grouped_poly_openings_vec = grouper.grouped_poly_openings_vec();\n\n    // |commitments| = [[W₀(τ)]₁, [W₁(τ)]₁, [W₂(τ)]₁, [W₃(τ)]₁, [W₄(τ)]₁]\n    std::vector<Commitment> commitments;\n    commitments.reserve(grouped_poly_openings_vec.size());\n    for (size_t i = 0; i < grouped_poly_openings_vec.size(); ++i) {\n      Commitment commitment;\n      if (!reader->ReadFromProof(&commitment)) return false;\n      commitments.push_back(std::move(commitment));\n    }\n\n    Field u = reader->SqueezeChallenge();\n    VLOG(2) << \"GWC(u): \" << u.ToHexString(true);\n\n    Field opening_multi = Field::Zero();\n    G1JacobianPoint commitment_multi = G1JacobianPoint::Zero();\n    G1JacobianPoint witness_with_aux = G1JacobianPoint::Zero();\n    G1JacobianPoint witness = G1JacobianPoint::Zero();\n    Field power_of_u = Field::One();\n    for (size_t i = 0; i < grouped_poly_openings_vec.size(); ++i) {\n      Field opening_batch = Field::Zero();\n      G1JacobianPoint commitment_batch = G1JacobianPoint::Zero();\n\n      const std::vector<PolynomialOpenings<Poly, Commitment>>&\n          poly_openings_vec = grouped_poly_openings_vec[i].poly_openings_vec;\n      for (const PolynomialOpenings<Poly, Commitment>& poly_openings :\n           base::Reversed(poly_openings_vec)) {\n        // |opening_batch₀| = P₀(x₀) + vP₁(x₀) + v²P₂(x₀)\n        // |opening_batch₁| = P₀(x₁) + vP₁(x₁) + v²P₂(x₁)\n        // |opening_batch₂| = P₀(x₂) + vP₁(x₂) + v²P₂(x₂) + v³P₃(x₂)\n        // |opening_batch₃| = P₃(x₃)\n        // |opening_batch₄| = P₄(x₄)\n        opening_batch *= v;\n        opening_batch += poly_openings.openings[0];\n        // |commitment_batch₀| = C₀ + vC₁ + v²C₂\n        // |commitment_batch₁| = C₀ + vC₁ + v²C₂\n        // |commitment_batch₂| = C₀ + vC₁ + v²C₂ + v³C₃\n        // |commitment_batch₃| = C₃\n        // |commitment_batch₄| = C₄\n        commitment_batch *= v;\n        commitment_batch += *poly_openings.poly_oracle;\n      }\n\n      // |commitment_multi| = Cₘᵤₗₜ = C₀ + vC₁ + v²C₂ +\n      //                              u(C₀ + vC₁ + v²C₂) +\n      //                              u²(C₀ + vC₁ + v²C₂ + v³C₃) +\n      //                              u³C₃ +\n      //                              u⁴C₄\n      commitment_batch *= power_of_u;\n      commitment_multi += commitment_batch;\n      // |opening_multi| = Oₘᵤₗₜ = P₀(x₀) + vP₁(x₀) + v²P₂(x₀) +\n      //                           u(P₀(x₁) + vP₁(x₁) + v²P₂(x₁)) +\n      //                           u²(P₀(x₂) + vP₁(x₂) + v²P₂(x₂) + v³P₃(x₂)) +\n      //                           u³P₃(x₃) +\n      //                           u⁴P₄(x₄)\n      opening_batch *= power_of_u;\n      opening_multi += opening_batch;\n\n      // clang-format off\n      // |witness_with_aux| = Wₐᵤₓ = x₀[W₀(τ)]₁ + ux₁[W₁(τ)]₁ + u²x₂[W₂(τ)]₁ + u³x₃[W₃(τ)]₁ + u⁴x₄[W₄(τ)]₁\n      // clang-format on\n      witness_with_aux +=\n          power_of_u * grouped_poly_openings_vec[i].points[0] * commitments[i];\n      // clang-format off\n      // |witness| = W = [W₀(τ)]₁ + u[W₁(τ)]₁ + u²[W₂(τ)]₁ + u³[W₃(τ)]₁ + u⁴[W₄(τ)]₁\n      // clang-format on\n      witness += power_of_u * commitments[i];\n\n      power_of_u *= u;\n    }\n    // clang-format off\n    // e(W, [τ]₂) * e(Wₐᵤₓ + Cₘᵤₗₜ - [Oₘᵤₗₜ]₁, [-1]₂) ≟ gᴛ⁰\n    // τ(W₀(τ) + uW₁(τ) + u²W₂(τ) + u³W₃(τ) + u⁴W₄(τ)) - x₀W₀(τ) - ux₁W₁(τ) - u²x₂W₂(τ) - u³x₃W₃(τ) - u⁴x₄W₄(τ) -\n    // P₀(τ) - vP₁(τ) - v²P₂(τ) - u(P₀(τ) + vP₁(τ) + v²P₂(τ)) - u²(P₀(τ) + vP₁(τ) + v²P₂(τ) + v³P₃(τ)) -\n    // u³P₃(τ) - u⁴P₄(τ) + P₀(x₀) + vP₁(x₀) + v²P₂(x₀) + u(P₀(x₁) + vP₁(x₁) + v²P₂(x₁)) +\n    // u²(P₀(x₂) + vP₁(x₂) + v²P₂(x₂) + v³P₃(x₂)) + u³P₃(x₃) + u⁴P₄(x₄) ≟ 0\n    // (τ - x₀)W₀(τ) + u(τ - x₁)W₁(τ) + u²(τ - x₂)W₂(τ) + u³(τ - x₃)W₃(τ) + u⁴(τ - x₄)W₄(τ) -\n    // H₀(τ) - uH₁(τ) - u²H₂(τ) - u³H₃(τ) - u⁴H₄(τ) ≟ 0\n    // clang-format on\n    G1JacobianPoint g1_jacobian_arr[] = {\n        witness, (witness_with_aux + commitment_multi -\n                  opening_multi * G1JacobianPoint::Generator())};\n    G1Point g1_arr[2];\n    if (!G1JacobianPoint::BatchNormalize(g1_jacobian_arr, &g1_arr))\n      return false;\n    return math::Pairing<Curve>(g1_arr, g2_arr_).IsOne();\n  }\n\n  // KZGFamily methods\n  [[nodiscard]] bool DoUnsafeSetupWithTau(size_t size,\n                                          const Field& tau) override {\n    s_g2_ = (G2Point::Generator() * tau).ToAffine();\n    g2_arr_ = {\n        G2Prepared::From(s_g2_),\n        G2Prepared::From(-G2Point::Generator()),\n    };\n    return true;\n  }\n\n  G2Point s_g2_;\n  std::array<G2Prepared, 2> g2_arr_;\n};\n\ntemplate <typename Curve, size_t MaxDegree, typename _Commitment>\nstruct VectorCommitmentSchemeTraits<GWC<Curve, MaxDegree, _Commitment>> {\n public:\n  constexpr static size_t kMaxSize = MaxDegree + 1;\n  constexpr static bool kIsTransparent = false;\n  constexpr static bool kSupportsBatchMode = true;\n\n  using G1Point = typename Curve::G1Curve::AffinePoint;\n  using Field = typename G1Point::ScalarField;\n  using Commitment = _Commitment;\n};\n\n}  // namespace crypto\n\nnamespace base {\n\ntemplate <typename Curve, size_t MaxDegree, typename Commitment>\nclass Copyable<crypto::GWC<Curve, MaxDegree, Commitment>> {\n public:\n  using G1Point = typename Curve::G1Curve::AffinePoint;\n  using G2Point = typename Curve::G2Curve::AffinePoint;\n  using KZG = crypto::KZG<G1Point, MaxDegree, Commitment>;\n  using PCS = crypto::GWC<Curve, MaxDegree, Commitment>;\n\n  static bool WriteTo(const PCS& pcs, Buffer* buffer) {\n    return buffer->WriteMany(pcs.kzg(), pcs.s_g2());\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer, PCS* pcs) {\n    KZG kzg;\n    G2Point s_g2;\n    if (!buffer.ReadMany(&kzg, &s_g2)) {\n      return false;\n    }\n\n    *pcs = PCS(std::move(kzg), std::move(s_g2));\n    return true;\n  }\n\n  static size_t EstimateSize(const PCS& pcs) {\n    return base::EstimateSize(pcs.kzg()) + base::EstimateSize(pcs.s_g2());\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_CRYPTO_COMMITMENTS_KZG_GWC_H_\n"
  },
  {
    "path": "tachyon/crypto/commitments/kzg/gwc_unittest.cc",
    "content": "#include \"tachyon/crypto/commitments/kzg/gwc.h\"\n\n#include \"tachyon/crypto/commitments/kzg/kzg_family_test.h\"\n\nnamespace tachyon::crypto {\nnamespace {\n\nusing PCS =\n    GWC<math::bn254::BN254Curve, kMaxDegree, math::bn254::G1AffinePoint>;\n\nclass GWCTest : public KZGFamilyTest<PCS> {};\n\n}  // namespace\n\nTEST_F(GWCTest, CreateAndVerifyProof) { this->CreateAndVerifyProof(); }\n\nTEST_F(GWCTest, Copyable) { this->Copyable(); }\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/crypto/commitments/kzg/kzg.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_CRYPTO_COMMITMENTS_KZG_KZG_H_\n#define TACHYON_CRYPTO_COMMITMENTS_KZG_KZG_H_\n\n#include <stddef.h>\n\n#include <algorithm>\n#include <limits>\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/buffer/copyable.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/crypto/commitments/batch_commitment_state.h\"\n#include \"tachyon/math/elliptic_curves/msm/variable_base_msm.h\"\n#include \"tachyon/math/geometry/point_conversions.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluation_domain.h\"\n\n#if TACHYON_CUDA\n#include \"tachyon/device/gpu/gpu_memory.h\"\n#include \"tachyon/device/gpu/scoped_mem_pool.h\"\n#include \"tachyon/device/gpu/scoped_stream.h\"\n#include \"tachyon/math/elliptic_curves/msm/variable_base_msm_gpu.h\"\n#endif\n\nnamespace tachyon {\nnamespace zk {\n\ntemplate <typename Curve, size_t MaxDegree, size_t MaxExtendedDegree,\n          typename Commitment>\nclass GWCExtension;\n\ntemplate <typename Curve, size_t MaxDegree, size_t MaxExtendedDegree,\n          typename Commitment>\nclass SHPlonkExtension;\n\n}  // namespace zk\n\nnamespace crypto {\n\ntemplate <typename G1Point, size_t MaxDegree,\n          typename Commitment = typename math::Pippenger<G1Point>::Bucket>\nclass KZG {\n public:\n  using Field = typename G1Point::ScalarField;\n  using Bucket = typename math::Pippenger<G1Point>::Bucket;\n  using Curve = typename G1Point::Curve;\n\n  static constexpr size_t kMaxDegree = MaxDegree;\n\n  KZG() = default;\n\n  KZG(std::vector<G1Point>&& g1_powers_of_tau,\n      std::vector<G1Point>&& g1_powers_of_tau_lagrange)\n      : g1_powers_of_tau_(std::move(g1_powers_of_tau)),\n        g1_powers_of_tau_lagrange_(std::move(g1_powers_of_tau_lagrange)) {\n    CHECK_EQ(g1_powers_of_tau_.size(), g1_powers_of_tau_lagrange_.size());\n    CHECK_LE(g1_powers_of_tau_.size(), kMaxDegree + 1);\n#if TACHYON_CUDA\n    SetupForGpu();\n#endif\n  }\n\n  const std::vector<G1Point>& g1_powers_of_tau() const {\n    return g1_powers_of_tau_;\n  }\n\n  const std::vector<G1Point>& g1_powers_of_tau_lagrange() const {\n    return g1_powers_of_tau_lagrange_;\n  }\n\n#if TACHYON_CUDA\n  const device::gpu::GpuMemory<G1Point>& d_g1_powers_of_tau() const {\n    return d_g1_powers_of_tau_;\n  }\n\n  const device::gpu::GpuMemory<G1Point>& d_g1_powers_of_tau_lagrange() const {\n    return d_g1_powers_of_tau_lagrange_;\n  }\n\n  bool UsesGPU() const { return static_cast<bool>(msm_gpu_); }\n#endif\n\n#if TACHYON_CUDA\n  void SetupForGpu() {\n    if (msm_gpu_) return;\n\n    gpuMemPoolProps props = {gpuMemAllocationTypePinned,\n                             gpuMemHandleTypeNone,\n                             {gpuMemLocationTypeDevice, 0}};\n    mem_pool_ = device::gpu::CreateMemPool(&props);\n\n    uint64_t mem_pool_threshold = std::numeric_limits<uint64_t>::max();\n    gpuError_t error = gpuMemPoolSetAttribute(\n        mem_pool_.get(), gpuMemPoolAttrReleaseThreshold, &mem_pool_threshold);\n    CHECK_EQ(error, gpuSuccess);\n    stream_ = device::gpu::CreateStream();\n\n    size_t bases_size = g1_powers_of_tau_.size();\n    msm_gpu_.reset(\n        new math::VariableBaseMSMGpu<G1Point>(mem_pool_.get(), stream_.get()));\n    d_g1_powers_of_tau_ = device::gpu::GpuMemory<G1Point>::MallocFromPoolAsync(\n        bases_size, mem_pool_.get(), stream_.get());\n    d_g1_powers_of_tau_lagrange_ =\n        device::gpu::GpuMemory<G1Point>::MallocFromPoolAsync(\n            bases_size, mem_pool_.get(), stream_.get());\n  }\n#endif\n\n  void ResizeBatchCommitments(size_t size) {\n    // WARN(batzor): When resizing to a larger size, the last values will be\n    // garbage and should be filled with commitment results.\n#if TACHYON_CUDA\n    if (msm_gpu_) {\n      gpu_batch_commitments_.resize(size);\n      return;\n    }\n#endif\n    cpu_batch_commitments_.resize(size);\n  }\n\n  std::vector<Commitment> GetBatchCommitments(BatchCommitmentState& state) {\n    // NOTE(batzor): Resizing this vector without initialization is safe since\n    // |BatchNormalize| will overwrite them.\n    std::vector<Commitment> batch_commitments;\n#if TACHYON_CUDA\n    if (msm_gpu_) {\n      if constexpr (std::is_same_v<Commitment, math::ProjectivePoint<Curve>>) {\n        batch_commitments = std::move(gpu_batch_commitments_);\n        // NOLINTNEXTLINE(readability/braces)\n      } else if constexpr (std::is_same_v<Commitment,\n                                          math::AffinePoint<Curve>>) {\n        batch_commitments.resize(gpu_batch_commitments_.size());\n        CHECK(math::ProjectivePoint<Curve>::BatchNormalize(\n            gpu_batch_commitments_, &batch_commitments));\n        gpu_batch_commitments_.clear();\n      } else {\n        batch_commitments.resize(gpu_batch_commitments_.size());\n        CHECK(math::ConvertPoints(gpu_batch_commitments_, &batch_commitments));\n        gpu_batch_commitments_.clear();\n      }\n    } else {\n#endif\n      if constexpr (std::is_same_v<Commitment, Bucket>) {\n        batch_commitments = std::move(cpu_batch_commitments_);\n        // NOLINTNEXTLINE(readability/braces)\n      } else if constexpr (std::is_same_v<Commitment,\n                                          math::AffinePoint<Curve>>) {\n        batch_commitments.resize(cpu_batch_commitments_.size());\n        CHECK(\n            Bucket::BatchNormalize(cpu_batch_commitments_, &batch_commitments));\n        cpu_batch_commitments_.clear();\n      } else {\n        batch_commitments.resize(cpu_batch_commitments_.size());\n        CHECK(math::ConvertPoints(cpu_batch_commitments_, &batch_commitments));\n        cpu_batch_commitments_.clear();\n      }\n#if TACHYON_CUDA\n    }\n#endif\n    state.Reset();\n    return batch_commitments;\n  }\n\n  size_t N() const { return g1_powers_of_tau_.size(); }\n\n  [[nodiscard]] bool UnsafeSetup(size_t size) {\n    return UnsafeSetup(size, Field::Random());\n  }\n\n  [[nodiscard]] bool UnsafeSetup(size_t size, const Field& tau) {\n    using Domain = math::UnivariateEvaluationDomain<Field, kMaxDegree>;\n\n    // |g1_powers_of_tau_| = [τ⁰g₁, τ¹g₁, ... , τⁿ⁻¹g₁]\n    G1Point g1 = G1Point::Generator();\n    std::vector<Field> powers_of_tau = Field::GetSuccessivePowers(size, tau);\n\n    g1_powers_of_tau_.resize(size);\n    if (!G1Point::BatchMapScalarFieldToPoint(g1, powers_of_tau,\n                                             &g1_powers_of_tau_)) {\n      return false;\n    }\n\n    // Get |g1_powers_of_tau_lagrange_| from τ and g₁.\n    std::unique_ptr<Domain> domain = Domain::Create(size);\n    std::vector<Field> lagrange_coeffs =\n        domain->EvaluateAllLagrangeCoefficients(tau);\n\n    g1_powers_of_tau_lagrange_.resize(size);\n    if (!G1Point::BatchMapScalarFieldToPoint(g1, lagrange_coeffs,\n                                             &g1_powers_of_tau_lagrange_)) {\n      return false;\n    }\n\n#if TACHYON_CUDA\n    SetupForGpu();\n    CHECK(d_g1_powers_of_tau_.CopyFromAsync(\n        reinterpret_cast<const G1Point*>(g1_powers_of_tau_.data()),\n        device::gpu::GpuMemoryType::kHost, stream_.get(), 0, size));\n    CHECK(d_g1_powers_of_tau_lagrange_.CopyFromAsync(\n        reinterpret_cast<const G1Point*>(g1_powers_of_tau_lagrange_.data()),\n        device::gpu::GpuMemoryType::kHost, stream_.get(), 0, size));\n#endif\n    return true;\n  }\n\n  // Return false if |n| >= |N()|.\n  [[nodiscard]] bool Downsize(size_t n) {\n    if (n >= N()) return false;\n    g1_powers_of_tau_.resize(n);\n    g1_powers_of_tau_lagrange_.resize(n);\n    return true;\n  }\n\n  template <typename ScalarContainer>\n  [[nodiscard]] bool Commit(const ScalarContainer& v, Commitment* out) const {\n#if TACHYON_CUDA\n    if (msm_gpu_) {\n      return DoMSM(d_g1_powers_of_tau_, v, out);\n    }\n#endif\n    return DoMSM(g1_powers_of_tau_, v, out);\n  }\n\n  template <typename ScalarContainer>\n  [[nodiscard]] bool Commit(const ScalarContainer& v,\n                            BatchCommitmentState& state, size_t index) {\n#if TACHYON_CUDA\n    if (msm_gpu_) {\n      return DoMSM(d_g1_powers_of_tau_, v, state, index);\n    }\n#endif\n    return DoMSM(g1_powers_of_tau_, v, state, index);\n  }\n\n  template <typename ScalarContainer>\n  [[nodiscard]] bool CommitLagrange(const ScalarContainer& v,\n                                    Commitment* out) const {\n#if TACHYON_CUDA\n    if (msm_gpu_) {\n      return DoMSM(d_g1_powers_of_tau_lagrange_, v, out);\n    }\n#endif\n    return DoMSM(g1_powers_of_tau_lagrange_, v, out);\n  }\n\n  template <typename ScalarContainer>\n  [[nodiscard]] bool CommitLagrange(const ScalarContainer& v,\n                                    BatchCommitmentState& state, size_t index) {\n#if TACHYON_CUDA\n    if (msm_gpu_) {\n      return DoMSM(d_g1_powers_of_tau_lagrange_, v, state, index);\n    }\n#endif\n    return DoMSM(g1_powers_of_tau_lagrange_, v, state, index);\n  }\n\n private:\n  template <typename, size_t, size_t, typename>\n  friend class tachyon::zk::GWCExtension;\n\n  template <typename, size_t, size_t, typename>\n  friend class tachyon::zk::SHPlonkExtension;\n\n  template <typename BaseContainer, typename ScalarContainer,\n            typename OutCommitment>\n  bool DoMSM(const BaseContainer& bases, const ScalarContainer& scalars,\n             OutCommitment* out) const {\n#if TACHYON_CUDA\n    if (msm_gpu_) {\n      absl::Span<const G1Point> bases_span = absl::Span<const G1Point>(\n          bases.data(), std::min(bases.size(), scalars.size()));\n      if constexpr (std::is_same_v<OutCommitment,\n                                   math::ProjectivePoint<Curve>>) {\n        return msm_gpu_->Run(bases_span, scalars, out);\n      } else {\n        math::ProjectivePoint<Curve> result;\n        if (!msm_gpu_->Run(bases_span, scalars, &result)) return false;\n        *out = math::ConvertPoint<OutCommitment>(result);\n        return true;\n      }\n    }\n#endif\n    math::VariableBaseMSM<G1Point> msm;\n    absl::Span<const G1Point> bases_span = absl::Span<const G1Point>(\n        bases.data(), std::min(bases.size(), scalars.size()));\n    if constexpr (std::is_same_v<OutCommitment, Bucket>) {\n      return msm.Run(bases_span, scalars, out);\n    } else {\n      Bucket result;\n      if (!msm.Run(bases_span, scalars, &result)) return false;\n      *out = math::ConvertPoint<OutCommitment>(result);\n      return true;\n    }\n  }\n\n  template <typename BaseContainer, typename ScalarContainer>\n  bool DoMSM(const BaseContainer& bases, const ScalarContainer& scalars,\n             BatchCommitmentState& state, size_t index) {\n#if TACHYON_CUDA\n    if (msm_gpu_) {\n      absl::Span<const G1Point> bases_span = absl::Span<const G1Point>(\n          bases.data(), std::min(bases.size(), scalars.size()));\n      return msm_gpu_->Run(bases_span, scalars, &gpu_batch_commitments_[index]);\n    }\n#endif\n    math::VariableBaseMSM<G1Point> msm;\n    absl::Span<const G1Point> bases_span = absl::Span<const G1Point>(\n        bases.data(), std::min(bases.size(), scalars.size()));\n    return msm.Run(bases_span, scalars, &cpu_batch_commitments_[index]);\n  }\n\n  std::vector<G1Point> g1_powers_of_tau_;\n  std::vector<G1Point> g1_powers_of_tau_lagrange_;\n  std::vector<Bucket> cpu_batch_commitments_;\n#if TACHYON_CUDA\n  device::gpu::ScopedMemPool mem_pool_;\n  device::gpu::ScopedStream stream_;\n  std::unique_ptr<math::VariableBaseMSMGpu<G1Point>> msm_gpu_;\n  std::vector<math::ProjectivePoint<Curve>> gpu_batch_commitments_;\n  device::gpu::GpuMemory<G1Point> d_g1_powers_of_tau_;\n  device::gpu::GpuMemory<G1Point> d_g1_powers_of_tau_lagrange_;\n#endif\n};\n\n}  // namespace crypto\n\nnamespace base {\n\ntemplate <typename G1Point, size_t MaxDegree, typename Commitment>\nclass Copyable<crypto::KZG<G1Point, MaxDegree, Commitment>> {\n public:\n  using PCS = crypto::KZG<G1Point, MaxDegree, Commitment>;\n\n  static bool WriteTo(const PCS& pcs, Buffer* buffer) {\n    return buffer->WriteMany(pcs.g1_powers_of_tau(),\n                             pcs.g1_powers_of_tau_lagrange());\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer, PCS* pcs) {\n    std::vector<G1Point> g1_powers_of_tau;\n    std::vector<G1Point> g1_powers_of_tau_lagrange;\n    if (!buffer.ReadMany(&g1_powers_of_tau, &g1_powers_of_tau_lagrange)) {\n      return false;\n    }\n\n    *pcs =\n        PCS(std::move(g1_powers_of_tau), std::move(g1_powers_of_tau_lagrange));\n    return true;\n  }\n\n  static size_t EstimateSize(const PCS& pcs) {\n    return base::EstimateSize(pcs.g1_powers_of_tau(),\n                              pcs.g1_powers_of_tau_lagrange());\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_CRYPTO_COMMITMENTS_KZG_KZG_H_\n"
  },
  {
    "path": "tachyon/crypto/commitments/kzg/kzg_family.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_CRYPTO_COMMITMENTS_KZG_KZG_FAMILY_H_\n#define TACHYON_CRYPTO_COMMITMENTS_KZG_KZG_FAMILY_H_\n\n#include <stddef.h>\n\n#include <utility>\n\n#include \"tachyon/crypto/commitments/kzg/kzg.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluations.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_polynomial.h\"\n\nnamespace tachyon::crypto {\n\ntemplate <typename G1Point, size_t MaxDegree, typename Commitment>\nclass KZGFamily {\n public:\n  using F = typename G1Point::ScalarField;\n\n  KZGFamily() = default;\n  explicit KZGFamily(KZG<G1Point, MaxDegree, Commitment>&& kzg)\n      : kzg_(std::move(kzg)) {}\n\n  const KZG<G1Point, MaxDegree, Commitment>& kzg() const { return kzg_; }\n\n  size_t N() const { return kzg_.N(); }\n\n  [[nodiscard]] bool DoUnsafeSetup(size_t size) {\n    return DoUnsafeSetup(size, F::Random());\n  }\n\n  [[nodiscard]] bool DoUnsafeSetup(size_t size, const F& tau) {\n    return kzg_.UnsafeSetup(size, tau) && DoUnsafeSetupWithTau(size, tau);\n  }\n\n  template <typename ScalarContainer>\n  [[nodiscard]] bool DoCommit(const ScalarContainer& poly,\n                              Commitment* commitment) const {\n    return kzg_.Commit(poly, commitment);\n  }\n\n  template <typename ScalarContainer>\n  [[nodiscard]] bool DoCommit(const ScalarContainer& poly,\n                              BatchCommitmentState& state, size_t index) {\n    return kzg_.Commit(poly, state, index);\n  }\n\n  template <typename ScalarContainer>\n  [[nodiscard]] bool DoCommitLagrange(const ScalarContainer& poly,\n                                      Commitment* commitment) const {\n    return kzg_.CommitLagrange(poly, commitment);\n  }\n\n  template <typename ScalarContainer>\n  [[nodiscard]] bool DoCommitLagrange(const ScalarContainer& poly,\n                                      BatchCommitmentState& state,\n                                      size_t index) {\n    return kzg_.CommitLagrange(poly, state, index);\n  }\n\n  [[nodiscard]] bool DoCommit(\n      const math::UnivariateDensePolynomial<F, MaxDegree>& poly,\n      Commitment* commitment) const {\n    return kzg_.Commit(poly.coefficients().coefficients(), commitment);\n  }\n\n  [[nodiscard]] bool DoCommit(\n      const math::UnivariateDensePolynomial<F, MaxDegree>& poly,\n      BatchCommitmentState& state, size_t index) {\n    return kzg_.Commit(poly.coefficients().coefficients(), state, index);\n  }\n\n  [[nodiscard]] bool DoCommitLagrange(\n      const math::UnivariateEvaluations<F, MaxDegree>& evals,\n      Commitment* commitment) const {\n    return kzg_.CommitLagrange(evals.evaluations(), commitment);\n  }\n\n  [[nodiscard]] bool DoCommitLagrange(\n      const math::UnivariateEvaluations<F, MaxDegree>& evals,\n      BatchCommitmentState& state, size_t index) {\n    return kzg_.CommitLagrange(evals.evaluations(), state, index);\n  }\n\n protected:\n  [[nodiscard]] virtual bool DoUnsafeSetupWithTau(size_t size,\n                                                  const F& tau) = 0;\n\n  KZG<G1Point, MaxDegree, Commitment> kzg_;\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_COMMITMENTS_KZG_KZG_FAMILY_H_\n"
  },
  {
    "path": "tachyon/crypto/commitments/kzg/kzg_family_test.h",
    "content": "#ifndef TACHYON_CRYPTO_COMMITMENTS_KZG_KZG_FAMILY_TEST_H_\n#define TACHYON_CRYPTO_COMMITMENTS_KZG_KZG_FAMILY_TEST_H_\n\n#include <stddef.h>\n\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/crypto/commitments/kzg/kzg.h\"\n#include \"tachyon/crypto/commitments/test/bn254_kzg_polynomial_openings.h\"\n#include \"tachyon/crypto/transcripts/simple_transcript.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/bn254.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/g1.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluation_domain_factory.h\"\n\nnamespace tachyon::crypto {\n\nconstexpr static size_t K = 4;\nconstexpr static size_t N = size_t{1} << K;\nconstexpr static size_t kMaxDegree = N - 1;\n\ntemplate <typename PCS>\nclass KZGFamilyTest : public testing::Test {\n public:\n  using F = typename PCS::Field;\n  using Poly = typename PCS::Poly;\n  using Commitment = typename PCS::Commitment;\n  using Point = typename Poly::Point;\n\n  static void SetUpTestSuite() { math::bn254::BN254Curve::Init(); }\n\n  void SetUp() override {\n    KZG<math::bn254::G1AffinePoint, kMaxDegree, Commitment> kzg;\n    pcs_ = PCS(std::move(kzg));\n    ASSERT_TRUE(pcs_.UnsafeSetup(N, F(2)));\n  }\n\n  void CreateAndVerifyProof() {\n    OwnedPolynomialOpenings<Poly, Commitment> owned_openings;\n    std::string error;\n    ASSERT_TRUE(\n        LoadAndParseJson(base::FilePath(\"tachyon/crypto/commitments/test/\"\n                                        \"bn254_kzg_polynomial_openings.json\"),\n                         &owned_openings, &error));\n    ASSERT_TRUE(error.empty());\n\n    SimpleTranscriptWriter<Commitment> writer((base::Uint8VectorBuffer()));\n    std::vector<PolynomialOpening<Poly>> prover_openings =\n        owned_openings.CreateProverOpenings();\n    ASSERT_TRUE(pcs_.CreateOpeningProof(prover_openings, &writer));\n\n    base::Buffer read_buf(writer.buffer().buffer(),\n                          writer.buffer().buffer_len());\n    SimpleTranscriptReader<Commitment> reader(std::move(read_buf));\n    std::vector<PolynomialOpening<Poly, Commitment>> verifier_openings =\n        owned_openings.CreateVerifierOpenings();\n    EXPECT_TRUE((pcs_.VerifyOpeningProof(verifier_openings, &reader)));\n  }\n\n  void Copyable() {\n    base::Uint8VectorBuffer write_buf;\n    ASSERT_TRUE(write_buf.Grow(base::EstimateSize(pcs_)));\n    ASSERT_TRUE(write_buf.Write(pcs_));\n    ASSERT_TRUE(write_buf.Done());\n\n    write_buf.set_buffer_offset(0);\n\n    PCS value;\n    ASSERT_TRUE(write_buf.Read(&value));\n\n    EXPECT_EQ(pcs_.kzg().g1_powers_of_tau(), value.kzg().g1_powers_of_tau());\n    EXPECT_EQ(pcs_.kzg().g1_powers_of_tau_lagrange(),\n              value.kzg().g1_powers_of_tau_lagrange());\n    EXPECT_EQ(pcs_.s_g2(), value.s_g2());\n  }\n\n protected:\n  PCS pcs_;\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_COMMITMENTS_KZG_KZG_FAMILY_TEST_H_\n"
  },
  {
    "path": "tachyon/crypto/commitments/kzg/kzg_unittest.cc",
    "content": "#include \"tachyon/crypto/commitments/kzg/kzg.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/buffer/vector_buffer.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/g1.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluation_domain_factory.h\"\n\nnamespace tachyon::crypto {\n\nnamespace {\n\nconstexpr size_t K = 3;\nconstexpr size_t N = size_t{1} << K;\nconstexpr size_t kMaxDegree = N - 1;\n\nclass KZGTest : public testing::Test {\n public:\n  using PCS =\n      KZG<math::bn254::G1AffinePoint, kMaxDegree, math::bn254::G1AffinePoint>;\n  using Domain = math::UnivariateEvaluationDomain<math::bn254::Fr, kMaxDegree>;\n  using Poly = math::UnivariateDensePolynomial<math::bn254::Fr, kMaxDegree>;\n  using Evals = math::UnivariateEvaluations<math::bn254::Fr, kMaxDegree>;\n\n  static void SetUpTestSuite() { math::bn254::G1Curve::Init(); }\n};\n\n}  // namespace\n\nTEST_F(KZGTest, UnsafeSetup) {\n  PCS pcs;\n  ASSERT_TRUE(pcs.UnsafeSetup(N));\n\n  EXPECT_EQ(pcs.N(), N);\n  EXPECT_EQ(pcs.g1_powers_of_tau().size(), size_t{N});\n  EXPECT_EQ(pcs.g1_powers_of_tau_lagrange().size(), size_t{N});\n}\n\nTEST_F(KZGTest, CommitLagrange) {\n  PCS pcs;\n  ASSERT_TRUE(pcs.UnsafeSetup(N));\n\n  Poly poly = Poly::Random(N - 1);\n\n  math::bn254::G1AffinePoint cpu_commit;\n  ASSERT_TRUE(pcs.Commit(poly.coefficients().coefficients(), &cpu_commit));\n\n  std::unique_ptr<Domain> domain = Domain::Create(N);\n  Evals poly_evals = domain->FFT(poly);\n\n  math::bn254::G1AffinePoint cpu_commit_lagrange;\n  ASSERT_TRUE(\n      pcs.CommitLagrange(poly_evals.evaluations(), &cpu_commit_lagrange));\n\n  EXPECT_EQ(cpu_commit, cpu_commit_lagrange);\n\n#if TACHYON_CUDA\n  pcs.SetupForGpu();\n\n  math::bn254::G1AffinePoint gpu_commit;\n  ASSERT_TRUE(pcs.Commit(poly.coefficients().coefficients(), &gpu_commit));\n\n  EXPECT_EQ(gpu_commit, cpu_commit);\n\n  math::bn254::G1AffinePoint gpu_commit_lagrange;\n  ASSERT_TRUE(\n      pcs.CommitLagrange(poly_evals.evaluations(), &gpu_commit_lagrange));\n\n  EXPECT_EQ(gpu_commit_lagrange, cpu_commit_lagrange);\n#endif\n}\n\nTEST_F(KZGTest, BatchCommitLagrange) {\n  PCS pcs;\n  ASSERT_TRUE(pcs.UnsafeSetup(N));\n\n  size_t num_polys = 10;\n  std::vector<Poly> polys =\n      base::CreateVector(num_polys, []() { return Poly::Random(N - 1); });\n\n  BatchCommitmentState state(true, num_polys);\n  pcs.ResizeBatchCommitments(num_polys);\n  for (size_t i = 0; i < num_polys; ++i) {\n    ASSERT_TRUE(pcs.Commit(polys[i].coefficients().coefficients(), state, i));\n  }\n  std::vector<math::bn254::G1AffinePoint> cpu_batch_commitments =\n      pcs.GetBatchCommitments(state);\n  EXPECT_EQ(state.batch_mode, false);\n  EXPECT_EQ(state.batch_count, size_t{0});\n\n  std::unique_ptr<Domain> domain = Domain::Create(N);\n  std::vector<Evals> poly_evals =\n      base::Map(polys, [&domain](Poly& poly) { return domain->FFT(poly); });\n\n  state.batch_mode = true;\n  state.batch_count = num_polys;\n  pcs.ResizeBatchCommitments(num_polys);\n  for (size_t i = 0; i < num_polys; ++i) {\n    ASSERT_TRUE(pcs.CommitLagrange(poly_evals[i].evaluations(), state, i));\n  }\n  std::vector<math::bn254::G1AffinePoint> cpu_batch_commitments_lagrange =\n      pcs.GetBatchCommitments(state);\n  EXPECT_EQ(state.batch_mode, false);\n  EXPECT_EQ(state.batch_count, size_t{0});\n\n  EXPECT_EQ(cpu_batch_commitments, cpu_batch_commitments_lagrange);\n\n#if TACHYON_CUDA\n  pcs.SetupForGpu();\n\n  state.batch_mode = true;\n  state.batch_count = num_polys;\n  pcs.ResizeBatchCommitments(num_polys);\n  for (size_t i = 0; i < num_polys; ++i) {\n    ASSERT_TRUE(pcs.Commit(polys[i].coefficients().coefficients(), state, i));\n  }\n  std::vector<math::bn254::G1AffinePoint> gpu_batch_commitments =\n      pcs.GetBatchCommitments(state);\n  EXPECT_EQ(state.batch_mode, false);\n  EXPECT_EQ(state.batch_count, size_t{0});\n\n  EXPECT_EQ(gpu_batch_commitments, cpu_batch_commitments);\n\n  state.batch_mode = true;\n  state.batch_count = num_polys;\n  pcs.ResizeBatchCommitments(num_polys);\n  for (size_t i = 0; i < num_polys; ++i) {\n    ASSERT_TRUE(pcs.CommitLagrange(poly_evals[i].evaluations(), state, i));\n  }\n  std::vector<math::bn254::G1AffinePoint> gpu_batch_commitments_lagrange =\n      pcs.GetBatchCommitments(state);\n  EXPECT_EQ(state.batch_mode, false);\n  EXPECT_EQ(state.batch_count, size_t{0});\n\n  EXPECT_EQ(gpu_batch_commitments_lagrange, cpu_batch_commitments_lagrange);\n#endif\n}\n\nTEST_F(KZGTest, Downsize) {\n  PCS pcs;\n  ASSERT_TRUE(pcs.UnsafeSetup(N));\n  ASSERT_FALSE(pcs.Downsize(N));\n  ASSERT_TRUE(pcs.Downsize(N / 2));\n  EXPECT_EQ(pcs.N(), N / 2);\n}\n\nTEST_F(KZGTest, Copyable) {\n  PCS expected;\n  ASSERT_TRUE(expected.UnsafeSetup(N));\n\n  base::Uint8VectorBuffer write_buf;\n  ASSERT_TRUE(write_buf.Grow(base::EstimateSize(expected)));\n  ASSERT_TRUE(write_buf.Write(expected));\n  ASSERT_TRUE(write_buf.Done());\n\n  write_buf.set_buffer_offset(0);\n\n  PCS value;\n  ASSERT_TRUE(write_buf.Read(&value));\n\n  EXPECT_EQ(expected.g1_powers_of_tau(), value.g1_powers_of_tau());\n  EXPECT_EQ(expected.g1_powers_of_tau_lagrange(),\n            value.g1_powers_of_tau_lagrange());\n}\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/crypto/commitments/kzg/shplonk.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_CRYPTO_COMMITMENTS_KZG_SHPLONK_H_\n#define TACHYON_CRYPTO_COMMITMENTS_KZG_SHPLONK_H_\n\n#include <array>\n#include <utility>\n#include <vector>\n\n#include \"gtest/gtest_prod.h\"\n\n#include \"tachyon/base/containers/contains.h\"\n#include \"tachyon/crypto/commitments/kzg/kzg_family.h\"\n#include \"tachyon/crypto/commitments/polynomial_openings.h\"\n#include \"tachyon/crypto/commitments/univariate_polynomial_commitment_scheme.h\"\n#include \"tachyon/crypto/transcripts/transcript.h\"\n#include \"tachyon/math/elliptic_curves/pairing/pairing.h\"\n\nnamespace tachyon {\nnamespace zk {\n\ntemplate <typename Curve, size_t MaxDegree, size_t MaxExtensionDegree,\n          typename _Commitment = typename math::Pippenger<\n              typename Curve::G1Curve::AffinePoint>::Bucket>\nclass SHPlonkExtension;\n\n}  // namespace zk\n\nnamespace crypto {\n\ntemplate <typename Curve, size_t MaxDegree,\n          typename Commitment = typename math::Pippenger<\n              typename Curve::G1Curve::AffinePoint>::Bucket>\nclass SHPlonk final : public UnivariatePolynomialCommitmentScheme<\n                          SHPlonk<Curve, MaxDegree, Commitment>>,\n                      public KZGFamily<typename Curve::G1Curve::AffinePoint,\n                                       MaxDegree, Commitment> {\n public:\n  using Base = UnivariatePolynomialCommitmentScheme<\n      SHPlonk<Curve, MaxDegree, Commitment>>;\n  using G1Point = typename Curve::G1Curve::AffinePoint;\n  using G2Point = typename Curve::G2Curve::AffinePoint;\n  using G2Prepared = typename Curve::G2Prepared;\n  using Fp12 = typename Curve::Fp12;\n  using Field = typename Base::Field;\n  using Poly = typename Base::Poly;\n  using Point = typename Poly::Point;\n\n  SHPlonk() = default;\n  explicit SHPlonk(KZG<G1Point, MaxDegree, Commitment>&& kzg)\n      : KZGFamily<G1Point, MaxDegree, Commitment>(std::move(kzg)) {}\n  SHPlonk(KZG<G1Point, MaxDegree, Commitment>&& kzg, G2Point&& s_g2)\n      : KZGFamily<G1Point, MaxDegree, Commitment>(std::move(kzg)),\n        s_g2_(std::move(s_g2)),\n        g2_arr_({G2Prepared::From(G2Point::Generator()),\n                 G2Prepared::From(-s_g2)}) {}\n\n  const G2Point& s_g2() const { return s_g2_; }\n\n  void ResizeBatchCommitments() {\n    this->kzg_.ResizeBatchCommitments(\n        this->batch_commitment_state_.batch_count);\n  }\n\n  std::vector<Commitment> GetBatchCommitments() {\n    return this->kzg_.GetBatchCommitments(this->batch_commitment_state_);\n  }\n\n private:\n  friend class VectorCommitmentScheme<SHPlonk<Curve, MaxDegree, Commitment>>;\n  friend class UnivariatePolynomialCommitmentScheme<\n      SHPlonk<Curve, MaxDegree, Commitment>>;\n  template <typename, size_t, size_t, typename>\n  friend class zk::SHPlonkExtension;\n  FRIEND_TEST(SHPlonkTest, Copyable);\n\n  const char* Name() const { return \"SHPlonk\"; }\n\n  // UnivariatePolynomialCommitmentScheme methods\n  template <typename Container>\n  [[nodiscard]] bool DoCreateOpeningProof(\n      const Container& poly_openings, TranscriptWriter<Commitment>* writer) {\n    PolynomialOpeningGrouper<Poly> grouper;\n    grouper.GroupByPolyOracleAndPoints(poly_openings);\n\n    // Group |poly_openings| to |grouped_poly_openings_vec|.\n    // {[P₀, P₁, P₂], [x₀, x₁, x₂]}\n    // {[P₃], [x₂, x₃]}\n    // {[P₄], [x₄]}\n    const std::vector<GroupedPolynomialOpenings<Poly>>&\n        grouped_poly_openings_vec = grouper.grouped_poly_openings_vec();\n    const absl::btree_set<Point>& super_point_set = grouper.super_point_set();\n\n    Field y = writer->SqueezeChallenge();\n    VLOG(2) << \"SHPlonk(y): \" << y.ToHexString(true);\n\n    // Create [H₀(X), H₁(X), H₂(X)].\n    // clang-format off\n    // H₀(X) = ((P₀(X) - R₀(X)) + y(P₁(X) - R₁(X)) + y²(P₂(X) - R₂(X))) / (X - x₀)(X - x₁)(X - x₂)\n    // H₁(X) = ((P₃(X) - R₃(X)) / (X - x₂)(X - x₃)\n    // H₂(X) = ((P₄(X) - R₄(X)) / (X - x₄)\n    // clang-format on\n    std::vector<std::vector<Poly>> low_degree_extensions_vec;\n    low_degree_extensions_vec.resize(grouped_poly_openings_vec.size());\n    std::vector<Poly> h_polys = base::Map(\n        grouped_poly_openings_vec,\n        [&y, &low_degree_extensions_vec](\n            size_t i,\n            const GroupedPolynomialOpenings<Poly>& grouped_poly_openings) {\n          return grouped_poly_openings.CreateCombinedLowDegreeExtensions(\n              y, low_degree_extensions_vec[i]);\n        });\n\n    Field v = writer->SqueezeChallenge();\n    VLOG(2) << \"SHPlonk(v): \" << v.ToHexString(true);\n\n    // Create a linear combination of polynomials [H₀(X), H₁(X), H₂(X)] with\n    // with |v|.\n    // H(X) = H₀(X) + vH₁(X) + v²H₂(X)\n    Poly& h_poly =\n        Poly::template LinearCombinationInPlace</*forward=*/false>(h_polys, v);\n\n    // Commit H(X)\n    Commitment h;\n    if (!this->Commit(h_poly, &h)) return false;\n\n    if (!writer->WriteToProof(h)) return false;\n    Field u = writer->SqueezeChallenge();\n    VLOG(2) << \"SHPlonk(u): \" << u.ToHexString(true);\n\n    // Create [L₀(X), L₁(X), L₂(X)].\n    // clang-format off\n    // L₀(X) = Zᴛ\\₀(u) * ((P₀(X) - R₀(u)) + y(P₁(X) - R₁(u)) + y²(P₂(X) - R₂(u)))\n    // L₁(X) = Zᴛ\\₁(u) * (P₃(X) - R₃(u))\n    // L₂(X) = Zᴛ\\₂(u) * (P₄(X) - R₄(u))\n    // clang-format on\n    Field first_z_diff;\n    std::vector<Poly> l_polys = base::Map(\n        grouped_poly_openings_vec,\n        [&y, &u, &first_z_diff, &low_degree_extensions_vec, &super_point_set](\n            size_t i,\n            const GroupedPolynomialOpenings<Poly>& grouped_poly_openings) {\n          std::vector<Point> diffs;\n          diffs.reserve(super_point_set.size() -\n                        grouped_poly_openings.points.size());\n          for (const Point& point : super_point_set) {\n            if (!base::Contains(grouped_poly_openings.points, point)) {\n              diffs.push_back(point);\n            }\n          }\n          // calculate difference vanishing polynomial evaluation\n          // |z_diff₀| = Zᴛ\\₀(u) = (u - x₃)(u - x₄)\n          // |z_diff₁| = Zᴛ\\₁(u) = (u - x₀)(u - x₁)(u - x₄)\n          // |z_diff₂| = Zᴛ\\₂(u) = (u - x₀)(u - x₁)(u - x₂)(u - x₃)\n          Field z_diff = Poly::EvaluateVanishingPolyByRoots(diffs, u);\n          if (i == 0) {\n            first_z_diff = z_diff;\n          }\n\n          const std::vector<Poly>& low_degree_extensions =\n              low_degree_extensions_vec[i];\n          std::vector<Poly> polys = base::Map(\n              grouped_poly_openings.poly_openings_vec,\n              [&u, &low_degree_extensions](\n                  size_t i, const PolynomialOpenings<Poly>& poly_openings) {\n                using Coefficients = typename Poly::Coefficients;\n\n                Poly poly = *poly_openings.poly_oracle;\n                if (poly.NumElements() > 0) {\n                  // NOTE(chokobole): It's safe to access since we checked\n                  // |NumElements()| is greater than 0.\n                  poly.at(0) -= low_degree_extensions[i].Evaluate(u);\n                  return poly;\n                }\n\n                return Poly(Coefficients(\n                    {-low_degree_extensions[i].Evaluate(u)}, true));\n              });\n\n          // clang-format off\n          // L₀(X) = (P₀(X) - R₀(u)) + y(P₁(X) - R₁(u)) + y²(P₂(X) - R₂(u))) * Zᴛ\\₀(u)\n          // L₁(X) = (P₃(X) - R₃(u)) * Zᴛ\\₁(u)\n          // L₂(X) = (P₄(X) - R₄(u)) * Zᴛ\\₂(u)\n          // clang-format on\n          Poly& l = Poly::template LinearCombinationInPlace</*forward=*/false>(\n              polys, y);\n          return l * z_diff;\n        });\n\n    // Create a linear combination of polynomials [L₀(X), L₁(X), L₂(X)] with\n    // |v|.\n    // L(X) = L₀(X) + vL₁(X) + v²L₂(X)\n    Poly& l_poly =\n        Poly::template LinearCombinationInPlace</*forward=*/false>(l_polys, v);\n\n    // Zᴛ = [x₀, x₁, x₂, x₃, x₄]\n    std::vector<Field> z_t(super_point_set.begin(), super_point_set.end());\n    // Zᴛ(X) = (X - x₀)(X - x₁)(X - x₂)(X - x₃)(X - x₄)\n    // Zᴛ(u) = (u - x₀)(u - x₁)(u - x₂)(u - x₃)(u - x₄)\n    Field zt_eval = Poly::EvaluateVanishingPolyByRoots(z_t, u);\n\n    // L(X) = L₀(X) + vL₁(X) + v²L₂(X) - Zᴛ(u) * H(X)\n    h_poly *= zt_eval;\n    l_poly -= h_poly;\n\n    // L(X) should be zero in X = |u|\n    DCHECK(l_poly.Evaluate(u).IsZero());\n\n    // Q(X) = L(X) / (X - u)\n    Poly vanishing_poly = Poly::FromRoots(std::vector<Field>({u}));\n    CHECK(l_poly /= vanishing_poly);\n    Poly& q_poly = l_poly;\n\n    // Normalize\n    // Q(X) = L(X) / ((X - u) * Zᴛ\\₀(u))\n    CHECK(q_poly /= first_z_diff);\n\n    // Commit Q(X)\n    Commitment q;\n    if (!this->Commit(q_poly, &q)) return false;\n    return writer->WriteToProof(q);\n  }\n\n  template <typename Container>\n  [[nodiscard]] bool DoVerifyOpeningProof(\n      const Container& poly_openings,\n      TranscriptReader<Commitment>* reader) const {\n    using G1JacobianPoint = math::JacobianPoint<typename G1Point::Curve>;\n\n    Field y = reader->SqueezeChallenge();\n    VLOG(2) << \"SHPlonk(y): \" << y.ToHexString(true);\n    Field v = reader->SqueezeChallenge();\n    VLOG(2) << \"SHPlonk(v): \" << v.ToHexString(true);\n\n    Commitment h;\n    if (!reader->ReadFromProof(&h)) return false;\n\n    Field u = reader->SqueezeChallenge();\n    VLOG(2) << \"SHPlonk(u): \" << u.ToHexString(true);\n\n    Commitment q;\n    if (!reader->ReadFromProof(&q)) return false;\n\n    PolynomialOpeningGrouper<Poly, Commitment> grouper;\n    grouper.GroupByPolyOracleAndPoints(poly_openings);\n\n    // Group |poly_openings| to |grouped_poly_openings_vec|.\n    // {[C₀, C₁, C₂], [x₀, x₁, x₂]}\n    // {[C₃], [x₂, x₃]}\n    // {[C₄], [x₄]}\n    const std::vector<GroupedPolynomialOpenings<Poly, Commitment>>&\n        grouped_poly_openings_vec = grouper.grouped_poly_openings_vec();\n    const absl::btree_set<Point>& super_point_set = grouper.super_point_set();\n\n    Field first_z_diff_inverse = Field::Zero();\n    Field first_z = Field::Zero();\n\n    std::vector<G1JacobianPoint> normalized_l_commitments;\n    normalized_l_commitments.reserve(grouped_poly_openings_vec.size());\n    size_t i = 0;\n    for (const GroupedPolynomialOpenings<Poly, Commitment>&\n             grouped_poly_openings : grouped_poly_openings_vec) {\n      const std::vector<PolynomialOpenings<Poly, Commitment>>&\n          poly_openings_vec = grouped_poly_openings.poly_openings_vec;\n      const std::vector<Point>& points = grouped_poly_openings.points;\n      // |commitments₀| = [C₀, C₁, C₂]\n      // |commitments₁| = [C₃]\n      // |commitments₂| = [C₄]\n      std::vector<Commitment> commitments = base::Map(\n          poly_openings_vec,\n          [](const PolynomialOpenings<Poly, Commitment>& poly_openings) {\n            return *poly_openings.poly_oracle;\n          });\n      // |points₀| = [x₀, x₁, x₂]\n      // |points₁| = [x₂, x₃]\n      // |points₂| = [x₄]\n      // |diffs₀| = [x₃, x₄]\n      // |diffs₁| = [x₀, x₁, x₄]\n      // |diffs₂| = [x₀, x₁, x₂, x₃]\n      std::vector<Point> diffs;\n      diffs.reserve(super_point_set.size() - points.size());\n      for (const Point& point : super_point_set) {\n        if (!base::Contains(points, point)) {\n          diffs.push_back(point);\n        }\n      }\n\n      // clang-format off\n      // |normalized_z_diff₀| = Zᴛ\\₀(u) / Zᴛ\\₀(u) = 1\n      // |normalized_z_diff₁| = Zᴛ\\₁(u) / Zᴛ\\₀(u) = (u - x₀)(u - x₁)(u - x₄) / (u - x₃)(u - x₄)\n      // |normalized_z_diff₂| = Zᴛ\\₂(u) / Zᴛ\\₀(u) = (u - x₀)(u - x₁)(u - x₂)(u - x₃) / (u - x₃)(u - x₄)\n      // clang-format on\n      Point normalized_z_diff = Poly::EvaluateVanishingPolyByRoots(diffs, u);\n      if (i == 0) {\n        // Zᴛ = [x₀, x₁, x₂, x₃, x₄]\n        // |first_z| = Z₀(u) = Zᴛ(u) / Zᴛ\\₀(u) = (u - x₀)(u - x₁)(u - x₂)\n        first_z = Poly::EvaluateVanishingPolyByRoots(points, u);\n        // Z₀(u)⁻¹ = (u - x₃)(u - x₄)⁻¹\n        CHECK(normalized_z_diff.InverseInPlace());\n        first_z_diff_inverse = std::move(normalized_z_diff);\n        normalized_z_diff = Field::One();\n      } else {\n        normalized_z_diff *= first_z_diff_inverse;\n      }\n\n      // |r_commitments₀| = [[R₀(u)]₁, [R₁(u)]₁, [R₂(u)]₁]\n      // |r_commitments₁| = [[R₃(u)]₁]\n      // |r_commitments₂| = [[R₄(u)]₁]\n      std::vector<G1JacobianPoint> r_commitments = base::Map(\n          poly_openings_vec,\n          [&points,\n           &u](const PolynomialOpenings<Poly, Commitment>& poly_openings) {\n            Poly r;\n            CHECK(\n                math::LagrangeInterpolate(points, poly_openings.openings, &r));\n            return r.Evaluate(u) * G1Point::Generator();\n          });\n\n      // clang-format off\n      // |l_commitment₀| = (C₀ - [R₀(u)]₁) + y(C₁ - [R₁(u)]₁) + y²(C₂ - [R₂(u)]₁)\n      // |l_commitment₁| = C₁ - [R₁(u)]₁\n      // |l_commitment₂| = C₂ - [R₂(u)]₁\n      // clang-format on\n      G1JacobianPoint l_commitment = G1JacobianPoint::Zero();\n      for (size_t j = commitments.size() - 1; j != SIZE_MAX; --j) {\n        l_commitment *= y;\n        l_commitment += (commitments[j] - r_commitments[j]);\n      }\n\n      // clang-format off\n      // |normalized_l_commitments₀| = [L₀(τ)]₁ / Zᴛ\\₀(u) = (C₀ - [R₀(u)]₁) + y(C₁ - [R₁(u)]₁) + y²(C₂ - [R₂(u)]₁) * Zᴛ\\₀(u) / Zᴛ\\₀(u)\n      // |normalized_l_commitments₁| = [L₁(τ)]₁ / Zᴛ\\₀(u) = (C₁ - [R₁(u)]₁) * Zᴛ\\₁(u) / Zᴛ\\₀(u)\n      // |normalized_l_commitments₂| = [L₂(τ)]₁ / Zᴛ\\₀(u) = (C₂ - [R₂(u)]₁) * Zᴛ\\₂(u) / Zᴛ\\₀(u)\n      // clang-format on\n      l_commitment *= normalized_z_diff;\n      normalized_l_commitments.push_back(std::move(l_commitment));\n      ++i;\n    }\n\n    // clang-format off\n    // |p| = ([L₀(τ)]₁ + v[L₁(τ)]₁ + v²[L₂(τ)]₁) / Zᴛ\\₀(u) - Z₀(u)[H(τ)]₁ + u[Q(τ)]₁\n    // clang-format on\n    G1JacobianPoint& p =\n        G1JacobianPoint::template LinearCombinationInPlace</*forward=*/false>(\n            normalized_l_commitments, v);\n\n    p -= (first_z * h);\n    p += (u * q);\n\n    // clang-format off\n    // e([Q(τ)]₁, [τ]₂) * e(p, [-1]₂) ≟ gᴛ⁰\n    // τ * Q(τ) - (L₀(τ) + v * L₁(τ) + v² * L₂(τ)) / Zᴛ\\₀(u) + Z₀(u) * H(τ) - u * Q(τ) ≟ 0\n    // (τ - u) * Q(τ) ≟ (L₀(τ) + v * L₁(τ) + v² * L₂(τ)) / Zᴛ\\₀(u) - Z₀(u) * H(τ)\n    // (τ - u) * Q(τ) ≟ (L₀(τ) + v * L₁(τ) + v² * L₂(τ) - Zᴛ(u) * H(τ)) / Zᴛ\\₀(u)\n    // (τ - u) * Q(τ) * Zᴛ\\₀(u) ≟ L(τ)\n    // clang-format on\n    G1Point g1_arr[] = {std::move(q), p.ToAffine()};\n    return math::Pairing<Curve>(g1_arr, g2_arr_).IsOne();\n  }\n\n  // KZGFamily methods\n  [[nodiscard]] bool DoUnsafeSetupWithTau(size_t size,\n                                          const Field& tau) override {\n    s_g2_ = (G2Point::Generator() * tau).ToAffine();\n    g2_arr_ = {G2Prepared::From(s_g2_),\n               G2Prepared::From(-G2Point::Generator())};\n    return true;\n  }\n\n  G2Point s_g2_;\n  std::array<G2Prepared, 2> g2_arr_;\n};\n\ntemplate <typename Curve, size_t MaxDegree, typename _Commitment>\nstruct VectorCommitmentSchemeTraits<SHPlonk<Curve, MaxDegree, _Commitment>> {\n public:\n  constexpr static size_t kMaxSize = MaxDegree + 1;\n  constexpr static bool kIsTransparent = false;\n  constexpr static bool kSupportsBatchMode = true;\n\n  using G1Point = typename Curve::G1Curve::AffinePoint;\n  using Field = typename G1Point::ScalarField;\n  using Commitment = _Commitment;\n};\n\n}  // namespace crypto\n\nnamespace base {\n\ntemplate <typename Curve, size_t MaxDegree, typename Commitment>\nclass Copyable<crypto::SHPlonk<Curve, MaxDegree, Commitment>> {\n public:\n  using G1Point = typename Curve::G1Curve::AffinePoint;\n  using G2Point = typename Curve::G2Curve::AffinePoint;\n  using KZG = crypto::KZG<G1Point, MaxDegree, Commitment>;\n  using PCS = crypto::SHPlonk<Curve, MaxDegree, Commitment>;\n\n  static bool WriteTo(const PCS& pcs, Buffer* buffer) {\n    return buffer->WriteMany(pcs.kzg(), pcs.s_g2());\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer, PCS* pcs) {\n    KZG kzg;\n    G2Point s_g2;\n    if (!buffer.ReadMany(&kzg, &s_g2)) {\n      return false;\n    }\n\n    *pcs = PCS(std::move(kzg), std::move(s_g2));\n    return true;\n  }\n\n  static size_t EstimateSize(const PCS& pcs) {\n    return base::EstimateSize(pcs.kzg(), pcs.s_g2());\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_CRYPTO_COMMITMENTS_KZG_SHPLONK_H_\n"
  },
  {
    "path": "tachyon/crypto/commitments/kzg/shplonk_unittest.cc",
    "content": "#include \"tachyon/crypto/commitments/kzg/shplonk.h\"\n\n#include \"tachyon/crypto/commitments/kzg/kzg_family_test.h\"\n\nnamespace tachyon::crypto {\nnamespace {\n\nusing PCS =\n    SHPlonk<math::bn254::BN254Curve, kMaxDegree, math::bn254::G1AffinePoint>;\n\nclass SHPlonkTest : public KZGFamilyTest<PCS> {};\n\n}  // namespace\n\nTEST_F(SHPlonkTest, CreateAndVerifyProof) { this->CreateAndVerifyProof(); }\n\nTEST_F(SHPlonkTest, Copyable) { this->Copyable(); }\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/crypto/commitments/merkle_tree/binary_merkle_tree/BUILD.bazel",
    "content": "load(\n    \"//bazel:tachyon_cc.bzl\",\n    \"tachyon_cc_library\",\n    \"tachyon_cc_unittest\",\n)\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"binary_merkle_hasher\",\n    hdrs = [\"binary_merkle_hasher.h\"],\n)\n\ntachyon_cc_library(\n    name = \"binary_merkle_proof\",\n    hdrs = [\"binary_merkle_proof.h\"],\n)\n\ntachyon_cc_library(\n    name = \"binary_merkle_tree_storage\",\n    hdrs = [\"binary_merkle_tree_storage.h\"],\n)\n\ntachyon_cc_library(\n    name = \"binary_merkle_tree\",\n    hdrs = [\"binary_merkle_tree.h\"],\n    deps = [\n        \":binary_merkle_hasher\",\n        \":binary_merkle_proof\",\n        \":binary_merkle_tree_storage\",\n        \"//tachyon/base:bits\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base:openmp_util\",\n        \"//tachyon/base:range\",\n        \"//tachyon/base/numerics:checked_math\",\n        \"//tachyon/crypto/commitments:vector_commitment_scheme\",\n        \"@com_google_googletest//:gtest_prod\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"simple_binary_merkle_tree_storage\",\n    testonly = True,\n    hdrs = [\"simple_binary_merkle_tree_storage.h\"],\n    deps = [\":binary_merkle_tree_storage\"],\n)\n\ntachyon_cc_unittest(\n    name = \"binary_merkle_tree_unittests\",\n    srcs = [\"binary_merkle_tree_unittest.cc\"],\n    deps = [\n        \":binary_merkle_tree\",\n        \":simple_binary_merkle_tree_storage\",\n        \"//tachyon/base/containers:container_util\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/crypto/commitments/merkle_tree/binary_merkle_tree/binary_merkle_hasher.h",
    "content": "// Use of this source code is governed by a Apache-2.0 style license that\n// can be found in the LICENSE.lambdaworks.\n\n#ifndef TACHYON_CRYPTO_COMMITMENTS_MERKLE_TREE_BINARY_MERKLE_TREE_BINARY_MERKLE_HASHER_H_\n#define TACHYON_CRYPTO_COMMITMENTS_MERKLE_TREE_BINARY_MERKLE_TREE_BINARY_MERKLE_HASHER_H_\n\nnamespace tachyon::crypto {\n\ntemplate <typename Leaf, typename Hash>\nclass BinaryMerkleHasher {\n public:\n  virtual ~BinaryMerkleHasher() = default;\n\n  virtual Hash ComputeLeafHash(const Leaf& leaf) const = 0;\n\n  virtual Hash ComputeParentHash(const Hash& left, const Hash& right) const = 0;\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_COMMITMENTS_MERKLE_TREE_BINARY_MERKLE_TREE_BINARY_MERKLE_HASHER_H_\n"
  },
  {
    "path": "tachyon/crypto/commitments/merkle_tree/binary_merkle_tree/binary_merkle_proof.h",
    "content": "// Use of this source code is governed by a Apache-2.0 style license that\n// can be found in the LICENSE.lambdaworks.\n\n#ifndef TACHYON_CRYPTO_COMMITMENTS_MERKLE_TREE_BINARY_MERKLE_TREE_BINARY_MERKLE_PROOF_H_\n#define TACHYON_CRYPTO_COMMITMENTS_MERKLE_TREE_BINARY_MERKLE_TREE_BINARY_MERKLE_PROOF_H_\n\n#include <vector>\n\nnamespace tachyon::crypto {\n\ntemplate <typename Hash>\nstruct BinaryMerklePath {\n  bool left;\n  Hash hash;\n\n  bool operator==(const BinaryMerklePath& other) const {\n    return left == other.left && hash == other.hash;\n  }\n  bool operator!=(const BinaryMerklePath& other) const {\n    return !operator==(other);\n  }\n};\n\ntemplate <typename Hash>\nstruct BinaryMerkleProof {\n  std::vector<BinaryMerklePath<Hash>> paths;\n\n  bool operator==(const BinaryMerkleProof& other) const {\n    return paths == other.paths;\n  }\n  bool operator!=(const BinaryMerkleProof& other) const {\n    return paths != other.paths;\n  }\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_COMMITMENTS_MERKLE_TREE_BINARY_MERKLE_TREE_BINARY_MERKLE_PROOF_H_\n"
  },
  {
    "path": "tachyon/crypto/commitments/merkle_tree/binary_merkle_tree/binary_merkle_tree.h",
    "content": "// Use of this source code is governed by a Apache-2.0 style license that\n// can be found in the LICENSE.lambdaworks.\n\n#ifndef TACHYON_CRYPTO_COMMITMENTS_MERKLE_TREE_BINARY_MERKLE_TREE_BINARY_MERKLE_TREE_H_\n#define TACHYON_CRYPTO_COMMITMENTS_MERKLE_TREE_BINARY_MERKLE_TREE_BINARY_MERKLE_TREE_H_\n\n#include <algorithm>\n#include <utility>\n#include <vector>\n\n#include \"gtest/gtest_prod.h\"\n\n#include \"tachyon/base/bits.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/numerics/checked_math.h\"\n#include \"tachyon/base/openmp_util.h\"\n#include \"tachyon/base/range.h\"\n#include \"tachyon/crypto/commitments/merkle_tree/binary_merkle_tree/binary_merkle_hasher.h\"\n#include \"tachyon/crypto/commitments/merkle_tree/binary_merkle_tree/binary_merkle_proof.h\"\n#include \"tachyon/crypto/commitments/merkle_tree/binary_merkle_tree/binary_merkle_tree_storage.h\"\n#include \"tachyon/crypto/commitments/vector_commitment_scheme.h\"\n\nnamespace tachyon::crypto {\n\ntemplate <typename Leaf, typename Hash, size_t MaxSize>\nclass BinaryMerkleTree final\n    : public VectorCommitmentScheme<BinaryMerkleTree<Leaf, Hash, MaxSize>> {\n public:\n  constexpr static size_t kDefaultLeavesSizeForParallelization = 1024;\n\n  BinaryMerkleTree() = default;\n  BinaryMerkleTree(BinaryMerkleTreeStorage<Hash>* storage,\n                   BinaryMerkleHasher<Leaf, Hash>* hasher)\n      : storage_(storage), hasher_(hasher) {}\n\n  size_t leaves_size_for_parallelization() const {\n    return leaves_size_for_parallelization_;\n  }\n  void set_leaves_size_for_parallelization(\n      size_t leaves_size_for_parallelization) {\n    CHECK(base::bits::IsPowerOfTwo(leaves_size_for_parallelization));\n    leaves_size_for_parallelization_ = leaves_size_for_parallelization;\n  }\n\n private:\n  FRIEND_TEST(BinaryMerkleTreeTest, FillLeaves);\n  FRIEND_TEST(BinaryMerkleTreeTest, BuildTreeFromLeaves);\n\n  friend class VectorCommitmentScheme<BinaryMerkleTree<Leaf, Hash, MaxSize>>;\n\n  // VectorCommitmentScheme methods\n  const char* Name() const { return \"BinaryMerkleTree\"; }\n\n  size_t N() const { return MaxSize; }\n\n  template <typename Container>\n  [[nodiscard]] bool DoCommit(const Container& leaves, Hash* out) const {\n    if (!FillLeaves(leaves)) return false;\n\n    // For instance, if |leaves_size_for_parallelization_| equals 4, the\n    // subtrees with root indices 1 and 2 will be constructed.\n    //\n    //         0\n    //    1          2\n    //  3   4     5    6\n    // 7 8 9 10 11 12 13 14\n    //\n    // Finally, the remaining tree should be constructed from leaves 1 and 2.\n    size_t leaves_size = std::size(leaves);\n    if (leaves_size > leaves_size_for_parallelization_) {\n      OMP_PARALLEL_FOR(size_t i = 0; i < leaves_size;\n                       i += leaves_size_for_parallelization_) {\n        size_t from = leaves_size - 1 + i;\n        size_t to = from + leaves_size_for_parallelization_;\n        BuildTreeFromLeaves(base::Range<size_t>(from, to));\n      }\n      size_t i = base::bits::Log2Floor(leaves_size) -\n                 base::bits::Log2Floor(leaves_size_for_parallelization_);\n      BuildTreeFromLeaves(base::Range<size_t>((size_t{1} << i) - 1,\n                                              (size_t{1} << (i + 1)) - 1));\n    } else {\n      BuildTreeFromLeaves(\n          base::Range<size_t>(leaves_size - 1, (leaves_size << 1) - 1));\n    }\n    *out = storage_->GetHash(0);\n    return true;\n  }\n\n  [[nodiscard]] bool DoCreateOpeningProof(size_t index,\n                                          BinaryMerkleProof<Hash>* proof) {\n    size_t size = storage_->GetSize();\n    index = (size >> 1) + index;\n    proof->paths.resize(base::bits::Log2Floor(size));\n    size_t i = 0;\n    while (index > 0) {\n      BinaryMerklePath<Hash> path;\n      if (index % 2 == 0) {\n        path.left = true;\n        path.hash = storage_->GetHash(index - 1);\n      } else {\n        path.left = false;\n        path.hash = storage_->GetHash(index + 1);\n      }\n      proof->paths[i++] = std::move(path);\n\n      index = (index - 1) >> 1;\n    }\n    return true;\n  }\n\n  [[nodiscard]] bool DoVerifyOpeningProof(\n      const Hash& root, const Hash& leaf_hash,\n      const BinaryMerkleProof<Hash>& proof) const {\n    Hash hash = leaf_hash;\n    for (const BinaryMerklePath<Hash>& path : proof.paths) {\n      if (path.left) {\n        hash = hasher_->ComputeParentHash(path.hash, hash);\n      } else {\n        hash = hasher_->ComputeParentHash(hash, path.hash);\n      }\n    }\n    return hash == root;\n  }\n\n  template <typename Container>\n  bool FillLeaves(const Container& leaves) const {\n    size_t leaves_size = std::size(leaves);\n    if (!base::bits::IsPowerOfTwo(leaves_size)) {\n      LOG(ERROR) << leaves_size << \" is not a power of two\";\n      return false;\n    }\n    if (leaves_size > MaxSize) {\n      LOG(ERROR) << \"Too many leaves\";\n      return false;\n    }\n    base::CheckedNumeric<size_t> n = leaves_size;\n    storage_->Allocate(((n << 1) - 1).ValueOrDie());\n    OMP_PARALLEL_FOR(size_t i = 0; i < leaves_size; ++i) {\n      storage_->SetHash(leaves_size + i - 1,\n                        hasher_->ComputeLeafHash(leaves[i]));\n    }\n    return true;\n  }\n\n  void BuildTreeFromLeaves(base::Range<size_t> range) const {\n    while (range.GetSize() > 0) {\n      for (size_t i = range.from; i < range.to; i += 2) {\n        storage_->SetHash(i >> 1,\n                          hasher_->ComputeParentHash(storage_->GetHash(i),\n                                                     storage_->GetHash(i + 1)));\n      }\n      range = base::Range<size_t>(range.from >> 1, (range.to >> 1) - 1);\n    }\n  }\n\n  // not owned\n  mutable BinaryMerkleTreeStorage<Hash>* storage_ = nullptr;\n  // not owned\n  BinaryMerkleHasher<Leaf, Hash>* hasher_ = nullptr;\n  size_t leaves_size_for_parallelization_ =\n      kDefaultLeavesSizeForParallelization;\n};\n\ntemplate <typename Leaf, typename Hash, size_t MaxSize>\nstruct VectorCommitmentSchemeTraits<BinaryMerkleTree<Leaf, Hash, MaxSize>> {\n public:\n  constexpr static size_t kMaxSize = MaxSize;\n  constexpr static bool kIsTransparent = true;\n  constexpr static bool kSupportsBatchMode = false;\n\n  // TODO(chokobole): The result of Keccak256 is not a field. How can we handle\n  // this?\n  using Field = Hash;\n  using Commitment = Hash;\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_COMMITMENTS_MERKLE_TREE_BINARY_MERKLE_TREE_BINARY_MERKLE_TREE_H_\n"
  },
  {
    "path": "tachyon/crypto/commitments/merkle_tree/binary_merkle_tree/binary_merkle_tree_storage.h",
    "content": "// Use of this source code is governed by a Apache-2.0 style license that\n// can be found in the LICENSE.lambdaworks.\n\n#ifndef TACHYON_CRYPTO_COMMITMENTS_MERKLE_TREE_BINARY_MERKLE_TREE_BINARY_MERKLE_TREE_STORAGE_H_\n#define TACHYON_CRYPTO_COMMITMENTS_MERKLE_TREE_BINARY_MERKLE_TREE_BINARY_MERKLE_TREE_STORAGE_H_\n\n#include <stddef.h>\n\nnamespace tachyon::crypto {\n\ntemplate <typename Hash>\nclass BinaryMerkleTreeStorage {\n public:\n  virtual ~BinaryMerkleTreeStorage() = default;\n\n  virtual void Allocate(size_t size) = 0;\n  virtual size_t GetSize() const = 0;\n  virtual const Hash& GetHash(size_t i) const = 0;\n  virtual void SetHash(size_t i, const Hash& hash) = 0;\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_COMMITMENTS_MERKLE_TREE_BINARY_MERKLE_TREE_BINARY_MERKLE_TREE_STORAGE_H_\n"
  },
  {
    "path": "tachyon/crypto/commitments/merkle_tree/binary_merkle_tree/binary_merkle_tree_unittest.cc",
    "content": "// Use of this source code is governed by a Apache-2.0 style license that\n// can be found in the LICENSE.lambdaworks.\n\n#include \"tachyon/crypto/commitments/merkle_tree/binary_merkle_tree/binary_merkle_tree.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/crypto/commitments/merkle_tree/binary_merkle_tree/simple_binary_merkle_tree_storage.h\"\n\nnamespace tachyon::crypto {\n\nnamespace {\n\nclass SimpleHasher : public BinaryMerkleHasher<int, int> {\n public:\n  // BinaryMerkleHasher<int, int> methods\n  int ComputeLeafHash(const int& leaf) const override { return leaf; }\n  int ComputeParentHash(const int& left, const int& right) const override {\n    return left + 2 * right;\n  }\n};\n\nclass BinaryMerkleTreeTest : public testing::Test {\n public:\n  constexpr static size_t K = 3;\n  constexpr static size_t N = size_t{1} << K;\n\n  using VCS = BinaryMerkleTree<int, int, N>;\n\n  void SetUp() override {\n    vcs_ = VCS(&storage_, &hasher_);\n    vcs_.set_leaves_size_for_parallelization(N >> 1);\n  }\n\n  void CreateLeaves() { leaves_ = base::CreateRangedVector<int>(0, N); }\n\n protected:\n  SimpleBinaryMerkleTreeStorage<int> storage_;\n  SimpleHasher hasher_;\n  VCS vcs_;\n  std::vector<int> leaves_;\n};\n\n}  // namespace\n\nTEST_F(BinaryMerkleTreeTest, FillLeaves) {\n  std::vector<int> invalid_leaves = base::CreateRangedVector<int>(0, N + 1);\n  EXPECT_FALSE(vcs_.FillLeaves(invalid_leaves));\n  invalid_leaves = base::CreateRangedVector<int>(0, size_t{1} << (K + 1));\n  EXPECT_FALSE(vcs_.FillLeaves(invalid_leaves));\n}\n\nTEST_F(BinaryMerkleTreeTest, BuildTreeFromLeaves) {\n  CreateLeaves();\n  ASSERT_TRUE(vcs_.FillLeaves(leaves_));\n\n  vcs_.BuildTreeFromLeaves(base::Range<size_t>(7, 11));\n  // clang-format off\n  std::vector<int> expected_nodes = {\n    0,\n    18, 0,\n    2, 8, 0, 0,\n    0, 1, 2, 3, 4, 5, 6, 7,\n  };\n  // clang-format on\n  EXPECT_EQ(storage_.hashes(), expected_nodes);\n\n  vcs_.BuildTreeFromLeaves(base::Range<size_t>(11, 15));\n  // clang-format off\n  expected_nodes = {\n    0,\n    18, 54,\n    2, 8, 14, 20,\n    0, 1, 2, 3, 4, 5, 6, 7,\n  };\n  // clang-format on\n  EXPECT_EQ(storage_.hashes(), expected_nodes);\n\n  vcs_.BuildTreeFromLeaves(base::Range<size_t>(1, 3));\n  // clang-format off\n  expected_nodes = {\n    126,\n    18, 54,\n    2, 8, 14, 20,\n    0, 1, 2, 3, 4, 5, 6, 7,\n  };\n  // clang-format on\n  EXPECT_EQ(storage_.hashes(), expected_nodes);\n}\n\nTEST_F(BinaryMerkleTreeTest, CommitAndVerify) {\n  CreateLeaves();\n\n  int commitment;\n  ASSERT_TRUE(vcs_.Commit(leaves_, &commitment));\n  EXPECT_EQ(commitment, 126);\n\n  BinaryMerkleProof<int> proof;\n  ASSERT_TRUE(vcs_.CreateOpeningProof(1, &proof));\n\n  BinaryMerkleProof<int> expected_proof;\n  expected_proof.paths = std::vector<BinaryMerklePath<int>>{\n      {true, 0},\n      {false, 8},\n      {false, 54},\n  };\n  EXPECT_EQ(proof, expected_proof);\n\n  int leaf_hash = hasher_.ComputeLeafHash(1);\n  ASSERT_TRUE(vcs_.VerifyOpeningProof(commitment, leaf_hash, proof));\n}\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/crypto/commitments/merkle_tree/binary_merkle_tree/simple_binary_merkle_tree_storage.h",
    "content": "#ifndef TACHYON_CRYPTO_COMMITMENTS_MERKLE_TREE_BINARY_MERKLE_TREE_SIMPLE_BINARY_MERKLE_TREE_STORAGE_H_\n#define TACHYON_CRYPTO_COMMITMENTS_MERKLE_TREE_BINARY_MERKLE_TREE_SIMPLE_BINARY_MERKLE_TREE_STORAGE_H_\n\n#include <vector>\n\n#include \"tachyon/crypto/commitments/merkle_tree/binary_merkle_tree/binary_merkle_tree_storage.h\"\n\nnamespace tachyon::crypto {\n\ntemplate <typename T>\nclass SimpleBinaryMerkleTreeStorage : public BinaryMerkleTreeStorage<T> {\n public:\n  const std::vector<T>& hashes() const { return hashes_; }\n\n  // BinaryMerkleTreeStorage<T> methods\n  void Allocate(size_t size) override { hashes_.resize(size); }\n  size_t GetSize() const override { return hashes_.size(); }\n  const T& GetHash(size_t i) const override { return hashes_[i]; }\n  void SetHash(size_t i, const T& hash) override { hashes_[i] = hash; }\n\n private:\n  std::vector<T> hashes_;\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_COMMITMENTS_MERKLE_TREE_BINARY_MERKLE_TREE_SIMPLE_BINARY_MERKLE_TREE_STORAGE_H_\n"
  },
  {
    "path": "tachyon/crypto/commitments/merkle_tree/field_merkle_tree/BUILD.bazel",
    "content": "load(\"//bazel:tachyon.bzl\", \"if_gpu_is_configured\")\nload(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"extension_field_merkle_tree_mmcs\",\n    hdrs = [\"extension_field_merkle_tree_mmcs.h\"],\n    deps = [\"//tachyon/crypto/commitments:mixed_matrix_commitment_scheme\"],\n)\n\ntachyon_cc_library(\n    name = \"field_merkle_tree\",\n    hdrs = [\"field_merkle_tree.h\"],\n    deps = [\n        \"//tachyon/base:logging\",\n        \"//tachyon/base:parallelize\",\n        \"//tachyon/base:profiler\",\n        \"//tachyon/base:sort\",\n        \"//tachyon/base/buffer:copyable\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/crypto/commitments/merkle_tree/field_merkle_tree/icicle:icicle_mmcs_holder\",\n        \"//tachyon/math/finite_fields:extension_field_traits_forward\",\n        \"//tachyon/math/finite_fields:finite_field_traits\",\n        \"//tachyon/math/finite_fields:packed_field_traits_forward\",\n        \"//tachyon/math/matrix:matrix_types\",\n        \"//tachyon/math/matrix:matrix_utils\",\n        \"@com_google_absl//absl/numeric:bits\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"field_merkle_tree_mmcs\",\n    hdrs = [\"field_merkle_tree_mmcs.h\"],\n    deps = [\n        \":field_merkle_tree\",\n        \"//tachyon/base:bits\",\n        \"//tachyon/base:profiler\",\n        \"//tachyon/base:sort\",\n        \"//tachyon/crypto/commitments:mixed_matrix_commitment_scheme\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"field_merkle_tree_unittests\",\n    srcs = [\n        \"extension_field_merkle_tree_mmcs_unittest.cc\",\n        \"field_merkle_tree_mmcs_unittest.cc\",\n        \"field_merkle_tree_unittest.cc\",\n    ],\n    deps = [\n        \":extension_field_merkle_tree_mmcs\",\n        \":field_merkle_tree_mmcs\",\n        \"//tachyon/crypto/hashes/sponge:padding_free_sponge\",\n        \"//tachyon/crypto/hashes/sponge:truncated_permutation\",\n        \"//tachyon/crypto/hashes/sponge/poseidon2\",\n        \"//tachyon/crypto/hashes/sponge/poseidon2:poseidon2_params\",\n        \"//tachyon/crypto/hashes/sponge/poseidon2/param_traits:poseidon2_baby_bear\",\n        \"//tachyon/math/finite_fields/baby_bear:baby_bear4\",\n        \"//tachyon/math/finite_fields/test:finite_field_test\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"field_merkle_tree_gpu_unittests\",\n    srcs = if_gpu_is_configured([\"field_merkle_tree_gpu_unittest.cc\"]),\n    deps = [\n        \":field_merkle_tree_mmcs\",\n        \"//tachyon/crypto/hashes/sponge:padding_free_sponge\",\n        \"//tachyon/crypto/hashes/sponge:truncated_permutation\",\n        \"//tachyon/crypto/hashes/sponge/poseidon2\",\n        \"//tachyon/crypto/hashes/sponge/poseidon2:poseidon2_params\",\n        \"//tachyon/crypto/hashes/sponge/poseidon2/icicle:icicle_poseidon2_holder\",\n        \"//tachyon/crypto/hashes/sponge/poseidon2/param_traits:poseidon2_baby_bear\",\n        \"//tachyon/math/finite_fields/test:finite_field_test\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/crypto/commitments/merkle_tree/field_merkle_tree/extension_field_merkle_tree_mmcs.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_CRYPTO_COMMITMENTS_MERKLE_TREE_FIELD_MERKLE_TREE_EXTENSION_FIELD_MERKLE_TREE_MMCS_H_\n#define TACHYON_CRYPTO_COMMITMENTS_MERKLE_TREE_FIELD_MERKLE_TREE_EXTENSION_FIELD_MERKLE_TREE_MMCS_H_\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/crypto/commitments/mixed_matrix_commitment_scheme.h\"\n\nnamespace tachyon::crypto {\n\ntemplate <typename ExtF, typename InnerMMCS>\nclass ExtensionFieldMerkleTreeMMCS final\n    : public MixedMatrixCommitmentScheme<\n          ExtensionFieldMerkleTreeMMCS<ExtF, InnerMMCS>> {\n public:\n  using Commitment =\n      typename MixedMatrixCommitmentSchemeTraits<InnerMMCS>::Commitment;\n  using ProverData =\n      typename MixedMatrixCommitmentSchemeTraits<InnerMMCS>::ProverData;\n  using Proof = std::vector<Commitment>;\n\n  ExtensionFieldMerkleTreeMMCS() = default;\n  ExtensionFieldMerkleTreeMMCS(InnerMMCS&& inner) : inner_(std::move(inner)) {}\n\n  const InnerMMCS& inner() const { return inner_; }\n\n private:\n  friend class MixedMatrixCommitmentScheme<\n      ExtensionFieldMerkleTreeMMCS<ExtF, InnerMMCS>>;\n\n  [[nodiscard]] bool DoCommit(\n      std::vector<Eigen::Map<const math::RowMajorMatrix<ExtF>>>&& matrices,\n      Commitment* commitment, ProverData* prover_data) const {\n    return inner_.Commit(std::move(matrices), commitment, prover_data);\n  }\n\n  [[nodiscard]] bool DoCommitOwned(\n      std::vector<math::RowMajorMatrix<ExtF>>&& owned_matrices,\n      Commitment* commitment, ProverData* prover_data) const {\n    return inner_.CommitOwned(std::move(owned_matrices), commitment,\n                              prover_data);\n  }\n\n  const std::vector<Eigen::Map<const math::RowMajorMatrix<ExtF>>>&\n  DoGetMatrices(const ProverData& prover_data) const {\n    return prover_data.leaves();\n  }\n\n  [[nodiscard]] bool DoCreateOpeningProof(\n      size_t index, const ProverData& prover_data,\n      std::vector<std::vector<ExtF>>* openings, Proof* proof) const {\n    return inner_.CreateOpeningProof(index, prover_data, openings, proof);\n  }\n\n  [[nodiscard]] bool DoVerifyOpeningProof(\n      const Commitment& commitment,\n      absl::Span<const math::Dimensions> dimensions_list, size_t index,\n      absl::Span<const std::vector<ExtF>> opened_values,\n      const Proof& proof) const {\n    return inner_.VerifyOpeningProof(commitment, dimensions_list, index,\n                                     opened_values, proof);\n  }\n\n  InnerMMCS inner_;\n};\n\ntemplate <typename ExtF, typename InnerMMCS>\nstruct MixedMatrixCommitmentSchemeTraits<\n    ExtensionFieldMerkleTreeMMCS<ExtF, InnerMMCS>> {\n public:\n  using Field = ExtF;\n  using Commitment =\n      typename MixedMatrixCommitmentSchemeTraits<InnerMMCS>::Commitment;\n  using ProverData =\n      typename MixedMatrixCommitmentSchemeTraits<InnerMMCS>::ProverData;\n  using Proof = typename MixedMatrixCommitmentSchemeTraits<InnerMMCS>::Proof;\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_COMMITMENTS_MERKLE_TREE_FIELD_MERKLE_TREE_EXTENSION_FIELD_MERKLE_TREE_MMCS_H_\n"
  },
  {
    "path": "tachyon/crypto/commitments/merkle_tree/field_merkle_tree/extension_field_merkle_tree_mmcs_unittest.cc",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#include \"tachyon/crypto/commitments/merkle_tree/field_merkle_tree/extension_field_merkle_tree_mmcs.h\"\n\n#include <utility>\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/crypto/commitments/merkle_tree/field_merkle_tree/field_merkle_tree_mmcs.h\"\n#include \"tachyon/crypto/hashes/sponge/padding_free_sponge.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/param_traits/poseidon2_baby_bear.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_params.h\"\n#include \"tachyon/crypto/hashes/sponge/truncated_permutation.h\"\n#include \"tachyon/math/finite_fields/baby_bear/baby_bear4.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n\nnamespace tachyon::crypto {\n\nconstexpr size_t kRate = 8;\nconstexpr size_t kChunk = 8;\nconstexpr size_t kN = 2;\n\nusing F = math::BabyBear;\nusing ExtF = math::BabyBear4;\nusing PackedF = math::PackedBabyBear;\nusing Params = Poseidon2Params<Poseidon2Vendor::kPlonky3,\n                               Poseidon2Vendor::kPlonky3, F, 15, 7>;\nusing PackedParams = Poseidon2Params<Poseidon2Vendor::kPlonky3,\n                                     Poseidon2Vendor::kPlonky3, PackedF, 15, 7>;\nusing Poseidon2 = Poseidon2Sponge<Params>;\nusing PackedPoseidon2 = Poseidon2Sponge<PackedParams>;\nusing MyHasher = PaddingFreeSponge<Poseidon2, kRate, kChunk>;\nusing MyPackedHasher = PaddingFreeSponge<PackedPoseidon2, kRate, kChunk>;\nusing MyCompressor = TruncatedPermutation<Poseidon2, kChunk, kN>;\nusing MyPackedCompressor = TruncatedPermutation<PackedPoseidon2, kChunk, kN>;\nusing MMCS = FieldMerkleTreeMMCS<F, MyHasher, MyPackedHasher, MyCompressor,\n                                 MyPackedCompressor, kChunk>;\nusing InnerMMCS = FieldMerkleTreeMMCS<ExtF, MyHasher, MyPackedHasher,\n                                      MyCompressor, MyPackedCompressor, kChunk>;\nusing ExtMMCS = ExtensionFieldMerkleTreeMMCS<ExtF, InnerMMCS>;\nusing Tree = FieldMerkleTree<F, kChunk>;\nusing ExtTree = FieldMerkleTree<ExtF, kChunk>;\n\nnamespace {\n\nclass ExtensionFieldMerkleTreeMMCSTest : public math::FiniteFieldTest<PackedF> {\n public:\n  static void SetUpTestSuite() {\n    math::FiniteFieldTest<PackedF>::SetUpTestSuite();\n    ExtF::Init();\n  }\n\n protected:\n  ExtMMCS ext_mmcs_;\n};\n\n}  // namespace\n\nTEST_F(ExtensionFieldMerkleTreeMMCSTest, CommitAndVerify) {\n  constexpr size_t kRows = 16;\n  constexpr size_t kCols = 16;\n  math::RowMajorMatrix<ExtF> ext_matrix =\n      math::RowMajorMatrix<ExtF>::Random(kRows, kCols);\n\n  math::RowMajorMatrix<F> matrix(kRows,\n                                 kCols * ExtF::kDegreeOverBasePrimeField);\n  for (size_t i = 0; i < kRows; ++i) {\n    for (size_t j = 0; j < kCols * ExtF::kDegreeOverBasePrimeField; ++j) {\n      size_t col = j / ExtF::kDegreeOverBasePrimeField;\n      size_t idx = j % ExtF::kDegreeOverBasePrimeField;\n      matrix(i, j) = ext_matrix(i, col)[idx];\n    }\n  }\n\n  std::vector<math::RowMajorMatrix<ExtF>> ext_matrices = {\n      std::move(ext_matrix)};\n  std::array<F, kChunk> ext_commitment;\n  ExtTree ext_prover_data;\n  ASSERT_TRUE(ext_mmcs_.CommitOwned(std::move(ext_matrices), &ext_commitment,\n                                    &ext_prover_data));\n\n  const InnerMMCS& inner_mmcs = ext_mmcs_.inner();\n  MMCS mmcs(inner_mmcs.hasher(), inner_mmcs.packed_hasher(),\n            inner_mmcs.compressor(), inner_mmcs.packed_compressor());\n  std::vector<math::RowMajorMatrix<F>> matrices = {std::move(matrix)};\n  std::array<F, kChunk> commitment;\n  Tree prover_data;\n  ASSERT_TRUE(mmcs.CommitOwned(std::move(matrices), &commitment, &prover_data));\n\n  EXPECT_EQ(ext_commitment, commitment);\n\n  size_t index = 5;\n  std::vector<std::vector<ExtF>> openings;\n  std::vector<std::array<F, kChunk>> proof;\n  ASSERT_TRUE(\n      ext_mmcs_.CreateOpeningProof(index, ext_prover_data, &openings, &proof));\n  std::vector<math::Dimensions> dimensions_vec;\n  dimensions_vec.push_back({kCols, kRows});\n  ASSERT_TRUE(ext_mmcs_.VerifyOpeningProof(ext_commitment, dimensions_vec,\n                                           index, openings, proof));\n}\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/crypto/commitments/merkle_tree/field_merkle_tree/field_merkle_tree.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_CRYPTO_COMMITMENTS_MERKLE_TREE_FIELD_MERKLE_TREE_FIELD_MERKLE_TREE_H_\n#define TACHYON_CRYPTO_COMMITMENTS_MERKLE_TREE_FIELD_MERKLE_TREE_FIELD_MERKLE_TREE_H_\n\n#include <stddef.h>\n\n#include <array>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"absl/numeric/bits.h\"\n#include \"absl/types/span.h\"\n\n#include \"tachyon/base/buffer/copyable.h\"\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/parallelize.h\"\n#include \"tachyon/base/profiler.h\"\n#include \"tachyon/base/sort.h\"\n#include \"tachyon/math/finite_fields/extension_field_traits_forward.h\"\n#include \"tachyon/math/finite_fields/finite_field_traits.h\"\n#include \"tachyon/math/finite_fields/packed_field_traits_forward.h\"\n#include \"tachyon/math/matrix/matrix_types.h\"\n#include \"tachyon/math/matrix/matrix_utils.h\"\n\n#if TACHYON_CUDA\n#include \"tachyon/crypto/commitments/merkle_tree/field_merkle_tree/icicle/icicle_mmcs_holder.h\"\n#endif\n\nnamespace tachyon {\nnamespace crypto {\n\ntemplate <typename F, size_t N>\nclass FieldMerkleTree {\n public:\n  using PrimeField =\n      std::conditional_t<math::FiniteFieldTraits<F>::kIsExtensionField,\n                         typename math::ExtensionFieldTraits<F>::BasePrimeField,\n                         F>;\n  using PackedPrimeField =\n      typename math::PackedFieldTraits<PrimeField>::PackedField;\n  using Digest = std::array<PrimeField, N>;\n  using PackedDigest = std::array<PackedPrimeField, N>;\n\n  FieldMerkleTree() = default;\n\n  template <typename Hasher, typename PackedHasher, typename Compressor,\n            typename PackedCompressor>\n  static FieldMerkleTree BuildCpu(\n      const Hasher& hasher, const PackedHasher& packed_hasher,\n      const Compressor& compressor, const PackedCompressor& packed_compressor,\n      std::vector<Eigen::Map<const math::RowMajorMatrix<F>>>&& leaves) {\n    CHECK(!leaves.empty());\n    return DoBuildCpu(hasher, packed_hasher, compressor, packed_compressor,\n                      std::move(leaves));\n  }\n\n  template <typename Hasher, typename PackedHasher, typename Compressor,\n            typename PackedCompressor>\n  static FieldMerkleTree BuildOwnedCpu(\n      const Hasher& hasher, const PackedHasher& packed_hasher,\n      const Compressor& compressor, const PackedCompressor& packed_compressor,\n      std::vector<math::RowMajorMatrix<F>>&& owned_leaves) {\n    std::vector<Eigen::Map<const math::RowMajorMatrix<F>>> leaves =\n        base::Map(owned_leaves, [](const math::RowMajorMatrix<F>& owned_leaf) {\n          return math::Map(owned_leaf);\n        });\n    FieldMerkleTree ret = BuildCpu(hasher, packed_hasher, compressor,\n                                   packed_compressor, std::move(leaves));\n    ret.owned_leaves_ = std::move(owned_leaves);\n    return ret;\n  }\n\n#if TACHYON_CUDA\n  template <typename Hasher, typename PackedHasher, typename Compressor,\n            typename PackedCompressor>\n  static FieldMerkleTree MaybeBuildGpu(\n      const Hasher& hasher, const PackedHasher& packed_hasher,\n      const Compressor& compressor, const PackedCompressor& packed_compressor,\n      std::vector<Eigen::Map<const math::RowMajorMatrix<F>>>&& leaves,\n      IcicleMMCSHolder<F>* icicle) {\n    CHECK(!leaves.empty());\n\n#if TACHYON_CUDA\n    if constexpr (IsIcicleMMCSSupported<F>) {\n      if (icicle) {\n        TRACE_EVENT(\"Utils\", \"DoBuildGpu\");\n        std::vector<std::vector<std::vector<F>>> digest_layers_icicle;\n        CHECK((*icicle)->Commit(std::move(leaves), &digest_layers_icicle));\n        // TODO(GideokKim): Optimize this.\n        std::vector<std::vector<Digest>> digest_layers;\n        digest_layers.resize(digest_layers_icicle.size());\n        for (size_t i = 0; i < digest_layers_icicle.size(); ++i) {\n          digest_layers[i].resize(digest_layers_icicle[i].size());\n          for (size_t j = 0; j < digest_layers_icicle[i].size(); ++j) {\n            if (digest_layers_icicle[i][j].size() == N) {\n              std::array<PrimeField, N> arr;\n\n              std::move(digest_layers_icicle[i][j].begin(),\n                        digest_layers_icicle[i][j].end(), arr.begin());\n\n              digest_layers[i][j] = std::move(arr);\n            }\n          }\n        }\n        return {std::move(leaves), std::move(digest_layers)};\n      }\n    }\n#endif\n    return DoBuildCpu(hasher, packed_hasher, compressor, packed_compressor,\n                      std::move(leaves));\n  }\n\n  template <typename Hasher, typename PackedHasher, typename Compressor,\n            typename PackedCompressor>\n  static FieldMerkleTree MaybeBuildOwnedGpu(\n      const Hasher& hasher, const PackedHasher& packed_hasher,\n      const Compressor& compressor, const PackedCompressor& packed_compressor,\n      std::vector<math::RowMajorMatrix<F>>&& owned_leaves,\n      IcicleMMCSHolder<F>* icicle) {\n    std::vector<Eigen::Map<const math::RowMajorMatrix<F>>> leaves =\n        base::Map(owned_leaves, [](const math::RowMajorMatrix<F>& owned_leaf) {\n          return math::Map(owned_leaf);\n        });\n    FieldMerkleTree ret =\n        MaybeBuildGpu(hasher, packed_hasher, compressor, packed_compressor,\n                      std::move(leaves), icicle);\n    ret.owned_leaves_ = std::move(owned_leaves);\n    return ret;\n  }\n\n#endif\n\n  const std::vector<Eigen::Map<const math::RowMajorMatrix<F>>>& leaves() const {\n    return leaves_;\n  }\n  const std::vector<std::vector<Digest>>& digest_layers() const {\n    return digest_layers_;\n  }\n\n  bool operator==(const FieldMerkleTree& other) const {\n    return leaves_ == other.leaves_ && digest_layers_ == other.digest_layers_;\n  }\n  bool operator!=(const FieldMerkleTree& other) const {\n    return !operator==(other);\n  }\n\n  const Digest& GetRoot() const { return digest_layers_.back()[0]; }\n\n private:\n  friend class base::Copyable<FieldMerkleTree<F, N>>;\n\n  class RowMajorMatrixView {\n   public:\n    RowMajorMatrixView() = default;\n    explicit RowMajorMatrixView(Eigen::Map<const math::RowMajorMatrix<F>>* ptr)\n        : ptr_(ptr) {}\n\n    // TODO(chokobole): This comparison is intentionally reversed to sort in\n    // descending order, as powersort doesn't accept custom callbacks.\n    bool operator<(const RowMajorMatrixView& other) const {\n      return ptr_->rows() > other.ptr_->rows();\n    }\n    bool operator<=(const RowMajorMatrixView& other) const {\n      return ptr_->rows() >= other.ptr_->rows();\n    }\n    bool operator>(const RowMajorMatrixView& other) const {\n      return ptr_->rows() < other.ptr_->rows();\n    }\n\n    Eigen::Map<const math::RowMajorMatrix<F>>* operator->() const {\n      return ptr_;\n    }\n\n    Eigen::Map<const math::RowMajorMatrix<F>>& operator*() const {\n      return *ptr_;\n    }\n\n   private:\n    Eigen::Map<const math::RowMajorMatrix<F>>* ptr_ = nullptr;\n  };\n\n  FieldMerkleTree(\n      std::vector<Eigen::Map<const math::RowMajorMatrix<F>>>&& leaves,\n      std::vector<std::vector<Digest>>&& digest_layers)\n      : leaves_(std::move(leaves)), digest_layers_(std::move(digest_layers)) {}\n\n  template <typename Hasher, typename PackedHasher, typename Compressor,\n            typename PackedCompressor>\n  static FieldMerkleTree DoBuildCpu(\n      const Hasher& hasher, const PackedHasher& packed_hasher,\n      const Compressor& compressor, const PackedCompressor& packed_compressor,\n      std::vector<Eigen::Map<const math::RowMajorMatrix<F>>>&& leaves) {\n    TRACE_EVENT(\"Utils\", \"DoBuildCpu\");\n\n    std::vector<RowMajorMatrixView> sorted_leaves = base::Map(\n        leaves, [](Eigen::Map<const math::RowMajorMatrix<F>>& matrix) {\n          return RowMajorMatrixView(&matrix);\n        });\n    base::StableSort(sorted_leaves.begin(), sorted_leaves.end());\n\n#if DCHECK_IS_ON()\n    {\n      for (size_t i = 0; i < sorted_leaves.size() - 1; ++i) {\n        size_t a = static_cast<size_t>(sorted_leaves[i]->rows());\n        size_t b = static_cast<size_t>(sorted_leaves[i + 1]->rows());\n        CHECK(a == b || absl::bit_ceil(a) != absl::bit_ceil(b))\n            << \"matrix rows that round up to the same power of two must be \"\n               \"equal\";\n      }\n    }\n#endif  // DCHECK_IS_ON()\n\n    size_t first_layer_rows = sorted_leaves.front()->rows();\n    size_t first_layer_size = 1;\n    for (size_t i = 1; i < sorted_leaves.size(); ++i) {\n      if (first_layer_rows == static_cast<size_t>(sorted_leaves[i]->rows())) {\n        ++first_layer_size;\n      } else {\n        break;\n      }\n    }\n    absl::Span<RowMajorMatrixView> tallest_matrices =\n        absl::MakeSpan(sorted_leaves.data(), first_layer_size);\n    absl::Span<RowMajorMatrixView> remaining_leaves =\n        absl::MakeSpan(sorted_leaves.data() + first_layer_size,\n                       sorted_leaves.size() - first_layer_size);\n\n    std::vector<std::vector<Digest>> digest_layers;\n    digest_layers.emplace_back(\n        CreateFirstDigestLayer(hasher, packed_hasher, tallest_matrices));\n\n    while (true) {\n      const std::vector<Digest>& prev_layer = digest_layers.back();\n      if (prev_layer.size() == 1) break;\n      size_t next_layer_rows = prev_layer.size() / 2;\n      size_t next_layer_size = 0;\n      for (size_t i = 0; i < remaining_leaves.size(); ++i) {\n        if (absl::bit_ceil(static_cast<size_t>(remaining_leaves[i]->rows())) ==\n            next_layer_rows) {\n          ++next_layer_size;\n        } else {\n          break;\n        }\n      }\n      absl::Span<RowMajorMatrixView> matrices_to_inject;\n      if (next_layer_size > 0) {\n        matrices_to_inject = remaining_leaves.subspan(0, next_layer_size);\n        remaining_leaves.remove_prefix(next_layer_size);\n      }\n\n      digest_layers.push_back(\n          CompressAndInject(hasher, packed_hasher, compressor,\n                            packed_compressor, prev_layer, matrices_to_inject));\n    }\n    return {std::move(leaves), std::move(digest_layers)};\n  }\n\n  template <typename Hasher, typename PackedHasher>\n  static std::vector<Digest> CreateFirstDigestLayer(\n      const Hasher& hasher, const PackedHasher& packed_hasher,\n      absl::Span<RowMajorMatrixView> tallest_matrices) {\n    TRACE_EVENT(\"Utils\", \"CreateFirstDigestLayer\");\n    size_t max_rows = static_cast<size_t>(tallest_matrices[0]->rows());\n    size_t max_rows_padded = absl::bit_ceil(max_rows);\n\n    std::vector<Digest> ret(max_rows_padded);\n    absl::Span<Digest> sub_ret = absl::MakeSpan(ret).subspan(0, max_rows);\n    base::DynamicParallelizeByChunkSize(\n        sub_ret, PackedPrimeField::N,\n        [&hasher, &packed_hasher, tallest_matrices](\n            absl::Span<Digest> chunk, size_t chunk_offset, size_t chunk_size) {\n          size_t start = chunk_offset * chunk_size;\n          if (chunk.size() == chunk_size) {\n            std::vector<PackedPrimeField> packed_prime_fields =\n                base::FlatMap(tallest_matrices, [start](RowMajorMatrixView m) {\n                  return math::PackRowVertically<PackedPrimeField>(*m, start);\n                });\n            PackedDigest packed_digest =\n                packed_hasher.Hash(packed_prime_fields);\n            for (size_t i = 0; i < chunk.size(); ++i) {\n              for (size_t j = 0; j < N; ++j) {\n                chunk[i][j] = std::move(packed_digest[j][i]);\n              }\n            }\n          } else {\n            for (size_t i = 0; i < chunk.size(); ++i) {\n              chunk[i] = hasher.Hash(\n                  GetRowAsPrimeFieldVector(tallest_matrices, start + i));\n            }\n          }\n        });\n    return ret;\n  }\n\n  template <typename Hasher, typename PackedHasher, typename Compressor,\n            typename PackedCompressor>\n  static std::vector<Digest> CompressAndInject(\n      const Hasher& hasher, const PackedHasher& packed_hasher,\n      const Compressor& compressor, const PackedCompressor& packed_compressor,\n      const std::vector<Digest>& prev_layer,\n      absl::Span<RowMajorMatrixView> matrices_to_inject) {\n    TRACE_EVENT(\"Utils\", \"CompressAndInject\");\n    if (matrices_to_inject.empty())\n      return Compress(compressor, packed_compressor, prev_layer);\n\n    size_t next_rows = matrices_to_inject[0]->rows();\n    size_t next_rows_padded = absl::bit_ceil(next_rows);\n\n    std::vector<Digest> ret(next_rows_padded);\n    absl::Span<Digest> sub_ret = absl::MakeSpan(ret).subspan(0, next_rows);\n    base::DynamicParallelizeByChunkSize(\n        sub_ret, PackedPrimeField::N,\n        [&hasher, &packed_hasher, &compressor, &packed_compressor, &prev_layer,\n         matrices_to_inject](absl::Span<Digest> chunk, size_t chunk_offset,\n                             size_t chunk_size) {\n          size_t start = chunk_offset * chunk_size;\n          if (chunk.size() == chunk_size) {\n            PackedDigest inputs[] = {\n                base::CreateArray<N>([&prev_layer, start](size_t i) {\n                  return PackedPrimeField::From(\n                      [&prev_layer, start, i](size_t j) {\n                        return prev_layer[2 * (start + j)][i];\n                      });\n                }),\n                base::CreateArray<N>([&prev_layer, start](size_t i) {\n                  return PackedPrimeField::From(\n                      [&prev_layer, start, i](size_t j) {\n                        return prev_layer[2 * (start + j) + 1][i];\n                      });\n                }),\n            };\n            inputs[0] = packed_compressor.Compress(inputs);\n            std::vector<PackedPrimeField> packed_prime_fields = base::FlatMap(\n                matrices_to_inject, [start](RowMajorMatrixView m) {\n                  return math::PackRowVertically<PackedPrimeField>(*m, start);\n                });\n            inputs[1] = packed_hasher.Hash(packed_prime_fields);\n            PackedDigest packed_digest = packed_compressor.Compress(inputs);\n            for (size_t i = 0; i < chunk.size(); ++i) {\n              for (size_t j = 0; j < N; ++j) {\n                chunk[i][j] = std::move(packed_digest[j][i]);\n              }\n            }\n          } else {\n            for (size_t i = 0; i < chunk.size(); ++i) {\n              Digest inputs[] = {\n                  prev_layer[2 * (start + i)],\n                  prev_layer[2 * (start + i) + 1],\n              };\n              inputs[0] = compressor.Compress(inputs);\n              inputs[1] = hasher.Hash(\n                  GetRowAsPrimeFieldVector(matrices_to_inject, start + i));\n              chunk[i] = compressor.Compress(inputs);\n            }\n          }\n        });\n\n    Digest default_digest = {PrimeField::Zero()};\n    Digest inputs_with_default_digest[] = {\n        default_digest,\n        default_digest,\n    };\n    for (size_t i = next_rows; i < next_rows_padded; ++i) {\n      Digest inputs[] = {\n          prev_layer[2 * i],\n          prev_layer[2 * i + 1],\n      };\n      inputs_with_default_digest[0] = compressor.Compress(inputs);\n      ret[i] = compressor.Compress(inputs_with_default_digest);\n    }\n    return ret;\n  }\n\n  template <typename Compressor, typename PackedCompressor>\n  static std::vector<Digest> Compress(const Compressor& compressor,\n                                      const PackedCompressor& packed_compressor,\n                                      const std::vector<Digest>& prev_layer) {\n    TRACE_EVENT(\"Utils\", \"Compress\");\n    size_t next_rows = prev_layer.size() / 2;\n\n    std::vector<Digest> ret(next_rows);\n    base::DynamicParallelizeByChunkSize(\n        ret, PackedPrimeField::N,\n        [&compressor, &packed_compressor, &prev_layer](\n            absl::Span<Digest> chunk, size_t chunk_offset, size_t chunk_size) {\n          size_t start = chunk_offset * chunk_size;\n          if (chunk.size() == chunk_size) {\n            PackedDigest inputs[] = {\n                base::CreateArray<N>([&prev_layer, start](size_t i) {\n                  return PackedPrimeField::From(\n                      [&prev_layer, start, i](size_t j) {\n                        return prev_layer[2 * (start + j)][i];\n                      });\n                }),\n                base::CreateArray<N>([&prev_layer, start](size_t i) {\n                  return PackedPrimeField::From(\n                      [&prev_layer, start, i](size_t j) {\n                        return prev_layer[2 * (start + j) + 1][i];\n                      });\n                }),\n            };\n            PackedDigest packed_digest = packed_compressor.Compress(inputs);\n            for (size_t i = 0; i < chunk.size(); ++i) {\n              for (size_t j = 0; j < N; ++j) {\n                chunk[i][j] = std::move(packed_digest[j][i]);\n              }\n            }\n          } else {\n            for (size_t i = 0; i < chunk.size(); ++i) {\n              Digest inputs[] = {\n                  prev_layer[2 * (start + i)],\n                  prev_layer[2 * (start + i) + 1],\n              };\n              chunk[i] = compressor.Compress(inputs);\n            }\n          }\n        });\n    return ret;\n  }\n\n  static std::vector<PrimeField> GetRowAsPrimeFieldVector(\n      absl::Span<RowMajorMatrixView> matrices, size_t row) {\n    TRACE_EVENT(\"Utils\", \"GetRowAsPrimeFieldVector\");\n    return base::FlatMap(matrices, [row](RowMajorMatrixView m) {\n      if constexpr (math::FiniteFieldTraits<F>::kIsExtensionField) {\n        static_assert(\n            math::ExtensionFieldTraits<F>::kDegreeOverBasePrimeField ==\n            math::ExtensionFieldTraits<F>::kDegreeOverBaseField);\n        std::vector<PrimeField> ret;\n        ret.reserve(m->cols() *\n                    math::ExtensionFieldTraits<F>::kDegreeOverBasePrimeField);\n        for (Eigen::Index i = 0; i < m->cols(); ++i) {\n          const F& element = (*m)(row, i);\n          for (size_t j = 0;\n               j < math::ExtensionFieldTraits<F>::kDegreeOverBasePrimeField;\n               ++j) {\n            ret.push_back(element[j]);\n          }\n        }\n        return ret;\n      } else {\n        return base::CreateVector(m->cols(),\n                                  [m, row](size_t i) { return (*m)(row, i); });\n      }\n    });\n  }\n\n  std::vector<math::RowMajorMatrix<F>> owned_leaves_;\n  std::vector<Eigen::Map<const math::RowMajorMatrix<F>>> leaves_;\n  std::vector<std::vector<Digest>> digest_layers_;\n};\n\n}  // namespace crypto\n\nnamespace base {\n\ntemplate <typename F, size_t N>\nclass Copyable<crypto::FieldMerkleTree<F, N>> {\n  using Digest = typename crypto::FieldMerkleTree<F, N>::Digest;\n\n public:\n  static bool WriteTo(const crypto::FieldMerkleTree<F, N>& tree,\n                      Buffer* buffer) {\n    return buffer->WriteMany(tree.leaves_, tree.digest_layers_);\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer,\n                       crypto::FieldMerkleTree<F, N>* tree) {\n    std::vector<math::RowMajorMatrix<F>> owned_leaves;\n    std::vector<std::vector<Digest>> digest_layers;\n    if (!buffer.ReadMany(&owned_leaves, &digest_layers)) {\n      return false;\n    }\n\n    std::vector<Eigen::Map<const math::RowMajorMatrix<F>>> leaves =\n        base::Map(owned_leaves, [](const math::RowMajorMatrix<F>& owned_leaf) {\n          return math::Map(owned_leaf);\n        });\n\n    *tree = {std::move(leaves), std::move(digest_layers)};\n    tree->owned_leaves_ = std::move(owned_leaves);\n    return true;\n  }\n\n  static size_t EstimateSize(const crypto::FieldMerkleTree<F, N>& tree) {\n    return base::EstimateSize(tree.leaves_, tree.digest_layers_);\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_CRYPTO_COMMITMENTS_MERKLE_TREE_FIELD_MERKLE_TREE_FIELD_MERKLE_TREE_H_\n"
  },
  {
    "path": "tachyon/crypto/commitments/merkle_tree/field_merkle_tree/field_merkle_tree_gpu_unittest.cc",
    "content": "#include <utility>\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/crypto/commitments/merkle_tree/field_merkle_tree/field_merkle_tree.h\"\n#include \"tachyon/crypto/hashes/sponge/padding_free_sponge.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/param_traits/poseidon2_baby_bear.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_params.h\"\n#include \"tachyon/crypto/hashes/sponge/truncated_permutation.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/geometry/dimensions.h\"\n\n#if TACHYON_CUDA\n#include \"tachyon/crypto/hashes/sponge/poseidon2/icicle/icicle_poseidon2_holder.h\"\n#endif\n\nnamespace tachyon::crypto {\n\nconstexpr size_t kRate = 8;\nconstexpr size_t kChunk = 8;\nconstexpr size_t kN = 2;\n\nusing F = math::BabyBear;\nusing PackedF = math::PackedBabyBear;\nusing Params = Poseidon2Params<Poseidon2Vendor::kPlonky3,\n                               Poseidon2Vendor::kPlonky3, F, 15, 7>;\nusing PackedParams = Poseidon2Params<Poseidon2Vendor::kPlonky3,\n                                     Poseidon2Vendor::kPlonky3, PackedF, 15, 7>;\nusing Poseidon2 = Poseidon2Sponge<Params>;\nusing PackedPoseidon2 = Poseidon2Sponge<PackedParams>;\nusing MyHasher = PaddingFreeSponge<Poseidon2, kRate, kChunk>;\nusing MyPackedHasher = PaddingFreeSponge<PackedPoseidon2, kRate, kChunk>;\nusing MyCompressor = TruncatedPermutation<Poseidon2, kChunk, kN>;\nusing MyPackedCompressor = TruncatedPermutation<PackedPoseidon2, kChunk, kN>;\nusing Tree = FieldMerkleTree<F, kChunk>;\n\nnamespace {\n\nclass FieldMerkleTreeGpuTest : public math::FiniteFieldTest<PackedF> {\n protected:\n  MyHasher hasher_;\n  MyCompressor compressor_;\n  MyPackedHasher packed_hasher_;\n  MyPackedCompressor packed_compressor_;\n};\n\n}  // namespace\n\nTEST_F(FieldMerkleTreeGpuTest, CommitGpu) {\n  struct Config {\n    size_t num;\n    math::Dimensions dimensions;\n  };\n\n  struct {\n    std::vector<Config> configs;\n  } tests[] = {\n      // NOTE(chokobole): Icicle supports only rows that are powers of 2.\n      {{{4, {8, 1024}}, {5, {8, 128}}, {6, {8, 8}}}},\n      {\n          {{1, {1, 32}},\n           {1, {2, 32}},\n           {1, {3, 32}},\n           {1, {4, 32}},\n           {1, {5, 32}},\n           {1, {6, 32}},\n           {1, {7, 32}},\n           {1, {8, 32}},\n           {1, {9, 32}},\n           {1, {10, 32}}},\n      },\n  };\n\n  IciclePoseidon2Holder<F> poseidon2_holder =\n      IciclePoseidon2Holder<F>::Create<kRate>(hasher_.derived().config);\n  IcicleMMCSHolder<F> mmcs_holder = IcicleMMCSHolder<F>::Create<kRate>(\n      poseidon2_holder.get()->impl(), poseidon2_holder.get()->impl());\n\n  for (const auto& test : tests) {\n    std::vector<math::RowMajorMatrix<F>> matrices;\n    for (size_t i = 0; i < test.configs.size(); ++i) {\n      math::Dimensions dimensions = test.configs[i].dimensions;\n      for (size_t j = 0; j < test.configs[i].num; ++j) {\n        matrices.push_back(math::RowMajorMatrix<F>::Random(dimensions.height,\n                                                           dimensions.width));\n      }\n    }\n\n    std::vector<math::RowMajorMatrix<F>> matrices_tmp = matrices;\n    Tree tree_cpu = Tree::MaybeBuildOwnedGpu(hasher_, packed_hasher_,\n                                             compressor_, packed_compressor_,\n                                             std::move(matrices_tmp), nullptr);\n    Tree tree_gpu = Tree::MaybeBuildOwnedGpu(hasher_, packed_hasher_,\n                                             compressor_, packed_compressor_,\n                                             std::move(matrices), &mmcs_holder);\n    EXPECT_EQ(tree_cpu.GetRoot(), tree_gpu.GetRoot());\n    EXPECT_EQ(tree_cpu.leaves(), tree_gpu.leaves());\n    EXPECT_EQ(tree_cpu.digest_layers(), tree_gpu.digest_layers());\n  }\n}\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/crypto/commitments/merkle_tree/field_merkle_tree/field_merkle_tree_mmcs.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_CRYPTO_COMMITMENTS_MERKLE_TREE_FIELD_MERKLE_TREE_FIELD_MERKLE_TREE_MMCS_H_\n#define TACHYON_CRYPTO_COMMITMENTS_MERKLE_TREE_FIELD_MERKLE_TREE_FIELD_MERKLE_TREE_MMCS_H_\n\n#include <string>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"absl/numeric/bits.h\"\n\n#include \"tachyon/base/bits.h\"\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/profiler.h\"\n#include \"tachyon/crypto/commitments/merkle_tree/field_merkle_tree/field_merkle_tree.h\"\n#include \"tachyon/crypto/commitments/mixed_matrix_commitment_scheme.h\"\n#include \"tachyon/math/finite_fields/extension_field_traits_forward.h\"\n#include \"tachyon/math/finite_fields/finite_field_traits.h\"\n\nnamespace tachyon::crypto {\n\ntemplate <typename F, typename Hasher, typename PackedHasher,\n          typename Compressor, typename PackedCompressor, size_t N>\nclass FieldMerkleTreeMMCS final\n    : public MixedMatrixCommitmentScheme<FieldMerkleTreeMMCS<\n          F, Hasher, PackedHasher, Compressor, PackedCompressor, N>> {\n public:\n  using PrimeField =\n      std::conditional_t<math::FiniteFieldTraits<F>::kIsExtensionField,\n                         typename math::ExtensionFieldTraits<F>::BasePrimeField,\n                         F>;\n  using Commitment = std::array<PrimeField, N>;\n  using Digest = Commitment;\n  using ProverData = FieldMerkleTree<F, N>;\n  using Proof = std::vector<Digest>;\n\n  FieldMerkleTreeMMCS() = default;\n  FieldMerkleTreeMMCS(const Hasher& hasher, const PackedHasher& packed_hasher,\n                      const Compressor& compressor,\n                      const PackedCompressor& packed_compressor)\n      : hasher_(hasher),\n        packed_hasher_(packed_hasher),\n        compressor_(compressor),\n        packed_compressor_(packed_compressor) {}\n  FieldMerkleTreeMMCS(Hasher&& hasher, PackedHasher&& packed_hasher,\n                      Compressor&& compressor,\n                      PackedCompressor&& packed_compressor)\n      : hasher_(std::move(hasher)),\n        packed_hasher_(std::move(packed_hasher)),\n        compressor_(std::move(compressor)),\n        packed_compressor_(std::move(packed_compressor)) {}\n\n  const Hasher& hasher() const { return hasher_; }\n  const PackedHasher& packed_hasher() const { return packed_hasher_; }\n  const Compressor& compressor() const { return compressor_; }\n  const PackedCompressor& packed_compressor() const {\n    return packed_compressor_;\n  }\n\n#if TACHYON_CUDA\n  void set_icicle(IcicleMMCSHolder<F>* icicle) { icicle_ = icicle; }\n#endif\n\n private:\n  friend class MixedMatrixCommitmentScheme<FieldMerkleTreeMMCS<\n      F, Hasher, PackedHasher, Compressor, PackedCompressor, N>>;\n\n  struct IndexedDimensions {\n    size_t index;\n    math::Dimensions dimensions;\n\n    // TODO(chokobole): This comparison is intentionally reversed to sort in\n    // descending order, as powersort doesn't accept custom callbacks.\n    bool operator<(const IndexedDimensions& other) const {\n      return dimensions.height > other.dimensions.height;\n    }\n    bool operator<=(const IndexedDimensions& other) const {\n      return dimensions.height >= other.dimensions.height;\n    }\n    bool operator>(const IndexedDimensions& other) const {\n      return dimensions.height < other.dimensions.height;\n    }\n\n    std::string ToString() const {\n      return absl::Substitute(\"($0, $1)\", index, dimensions.ToString());\n    }\n  };\n\n  [[nodiscard]] bool DoCommit(\n      std::vector<Eigen::Map<const math::RowMajorMatrix<F>>>&& matrices,\n      Commitment* commitment, ProverData* prover_data) const {\n    TRACE_EVENT(\"ProofGeneration\", \"FieldMerkleTreeMMCS::DoCommit\");\n#if TACHYON_CUDA\n    *prover_data = FieldMerkleTree<F, N>::MaybeBuildGpu(\n        hasher_, packed_hasher_, compressor_, packed_compressor_,\n        std::move(matrices), icicle_);\n#else\n    *prover_data = FieldMerkleTree<F, N>::BuildCpu(\n        hasher_, packed_hasher_, compressor_, packed_compressor_,\n        std::move(matrices));\n#endif\n    *commitment = prover_data->GetRoot();\n\n    return true;\n  }\n\n  [[nodiscard]] bool DoCommitOwned(\n      std::vector<math::RowMajorMatrix<F>>&& owned_matrices,\n      Commitment* commitment, ProverData* prover_data) const {\n    TRACE_EVENT(\"ProofGeneration\", \"FieldMerkleTreeMMCS::DoCommitOwned\");\n#if TACHYON_CUDA\n    *prover_data = FieldMerkleTree<F, N>::MaybeBuildOwnedGpu(\n        hasher_, packed_hasher_, compressor_, packed_compressor_,\n        std::move(owned_matrices), icicle_);\n#else\n    *prover_data = FieldMerkleTree<F, N>::BuildOwnedCpu(\n        hasher_, packed_hasher_, compressor_, packed_compressor_,\n        std::move(owned_matrices));\n#endif\n    *commitment = prover_data->GetRoot();\n\n    return true;\n  }\n\n  const std::vector<Eigen::Map<const math::RowMajorMatrix<F>>>& DoGetMatrices(\n      const ProverData& prover_data) const {\n    return prover_data.leaves();\n  }\n\n  [[nodiscard]] bool DoCreateOpeningProof(size_t index,\n                                          const ProverData& prover_data,\n                                          std::vector<std::vector<F>>* openings,\n                                          Proof* proof) const {\n    TRACE_EVENT(\"ProofGeneration\", \"FieldMerkleTreeMMCS::DoCreateOpeningProof\");\n    size_t max_row_size = this->GetMaxRowSize(prover_data);\n    uint32_t log_max_row_size = base::bits::Log2Ceiling(max_row_size);\n\n    // TODO(chokobole): Is it able to be parallelized?\n    *openings = base::Map(\n        prover_data.leaves(),\n        [log_max_row_size,\n         index](const Eigen::Map<const math::RowMajorMatrix<F>>& matrix) {\n          uint32_t log_row_size =\n              base::bits::Log2Ceiling(static_cast<size_t>(matrix.rows()));\n          uint32_t bits_reduced = log_max_row_size - log_row_size;\n          size_t reduced_index = index >> bits_reduced;\n          return base::CreateVector(matrix.cols(),\n                                    [reduced_index, &matrix](size_t col) {\n                                      return matrix(reduced_index, col);\n                                    });\n        });\n\n    *proof =\n        base::CreateVector(log_max_row_size, [&prover_data, index](size_t i) {\n          // NOTE(chokobole): Let v be |index >> i|. If v is even, v ^ 1 is v\n          // + 1. Otherwise, v ^ 1 is v - 1.\n          return prover_data.digest_layers()[i][(index >> i) ^ 1];\n        });\n\n    return true;\n  }\n\n  [[nodiscard]] bool DoVerifyOpeningProof(\n      const Commitment& commitment,\n      absl::Span<const math::Dimensions> dimensions_list, size_t index,\n      absl::Span<const std::vector<F>> opened_values,\n      const Proof& proof) const {\n    TRACE_EVENT(\"ProofVerification\",\n                \"FieldMerkleTreeMMCS::DoVerifyOpeningProof\");\n    CHECK_EQ(dimensions_list.size(), opened_values.size());\n\n    std::vector<IndexedDimensions> sorted_dimensions_list = base::Map(\n        dimensions_list, [](size_t index, math::Dimensions dimensions) {\n          return IndexedDimensions{index, dimensions};\n        });\n\n    base::StableSort(sorted_dimensions_list.begin(),\n                     sorted_dimensions_list.end());\n\n    absl::Span<const IndexedDimensions> remaining_dimensions_list =\n        absl::MakeConstSpan(sorted_dimensions_list);\n\n    size_t next_layer =\n        absl::bit_ceil(remaining_dimensions_list.front().dimensions.height);\n    size_t next_layer_size = CountLayers(next_layer, remaining_dimensions_list);\n    Digest root = hasher_.Hash(GetOpenedValuesAsPrimeFieldVectors(\n        opened_values, remaining_dimensions_list.subspan(0, next_layer_size)));\n    remaining_dimensions_list.remove_prefix(next_layer_size);\n\n    for (const Digest& sibling : proof) {\n      Digest inputs[2];\n      inputs[0] = (index & 1) == 0 ? root : sibling;\n      inputs[1] = (index & 1) == 0 ? sibling : root;\n      root = compressor_.Compress(inputs);\n\n      index >>= 1;\n      next_layer >>= 1;\n      next_layer_size = CountLayers(next_layer, remaining_dimensions_list);\n      if (next_layer_size > 0) {\n        inputs[0] = std::move(root);\n        inputs[1] = hasher_.Hash(GetOpenedValuesAsPrimeFieldVectors(\n            opened_values,\n            remaining_dimensions_list.subspan(0, next_layer_size)));\n        remaining_dimensions_list.remove_prefix(next_layer_size);\n\n        root = compressor_.Compress(inputs);\n      }\n    }\n\n    return root == commitment;\n  }\n\n  constexpr static size_t CountLayers(\n      size_t target_height,\n      absl::Span<const IndexedDimensions> dimensions_list) {\n    TRACE_EVENT(\"Utils\", \"CountLayers\");\n    size_t ret = 0;\n    for (size_t i = 0; i < dimensions_list.size(); ++i) {\n      if (target_height == absl::bit_ceil(static_cast<size_t>(\n                               dimensions_list[i].dimensions.height))) {\n        ++ret;\n      } else {\n        break;\n      }\n    }\n    return ret;\n  }\n\n  static std::vector<PrimeField> GetOpenedValuesAsPrimeFieldVectors(\n      absl::Span<const std::vector<F>> opened_values,\n      absl::Span<const IndexedDimensions> dimensions_list) {\n    TRACE_EVENT(\"Utils\", \"GetOpenedValuesAsPrimeFieldVectors\");\n    if constexpr (math::FiniteFieldTraits<F>::kIsExtensionField) {\n      static_assert(math::ExtensionFieldTraits<F>::kDegreeOverBasePrimeField ==\n                    math::ExtensionFieldTraits<F>::kDegreeOverBaseField);\n      size_t size = std::accumulate(\n          dimensions_list.begin(), dimensions_list.end(), 0,\n          [&opened_values](size_t acc, const IndexedDimensions& dimensions) {\n            return acc + opened_values[dimensions.index].size();\n          });\n      std::vector<PrimeField> ret;\n      ret.reserve(size *\n                  math::ExtensionFieldTraits<F>::kDegreeOverBasePrimeField);\n      for (size_t i = 0; i < dimensions_list.size(); ++i) {\n        const std::vector<F>& elements =\n            opened_values[dimensions_list[i].index];\n        for (size_t j = 0; j < elements.size(); ++j) {\n          const F& element = elements[j];\n          for (size_t k = 0;\n               k < math::ExtensionFieldTraits<F>::kDegreeOverBasePrimeField;\n               ++k) {\n            ret.push_back(element[k]);\n          }\n        }\n      }\n      return ret;\n    } else {\n      return base::FlatMap(\n          dimensions_list,\n          [&opened_values](const IndexedDimensions& dimensions) {\n            return opened_values[dimensions.index];\n          });\n    }\n  }\n\n  Hasher hasher_;\n  PackedHasher packed_hasher_;\n  Compressor compressor_;\n  PackedCompressor packed_compressor_;\n#if TACHYON_CUDA\n  // not owned\n  IcicleMMCSHolder<F>* icicle_ = nullptr;\n#endif\n};\n\ntemplate <typename F, typename Hasher, typename PackedHasher,\n          typename Compressor, typename PackedCompressor, size_t N>\nstruct MixedMatrixCommitmentSchemeTraits<FieldMerkleTreeMMCS<\n    F, Hasher, PackedHasher, Compressor, PackedCompressor, N>> {\n public:\n  using Field = F;\n  using PrimeField =\n      std::conditional_t<math::FiniteFieldTraits<F>::kIsExtensionField,\n                         typename math::ExtensionFieldTraits<F>::BasePrimeField,\n                         F>;\n  using Commitment = std::array<PrimeField, N>;\n  using ProverData = FieldMerkleTree<F, N>;\n  using Proof = std::vector<std::array<PrimeField, N>>;\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_COMMITMENTS_MERKLE_TREE_FIELD_MERKLE_TREE_FIELD_MERKLE_TREE_MMCS_H_\n"
  },
  {
    "path": "tachyon/crypto/commitments/merkle_tree/field_merkle_tree/field_merkle_tree_mmcs_unittest.cc",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#include \"tachyon/crypto/commitments/merkle_tree/field_merkle_tree/field_merkle_tree_mmcs.h\"\n\n#include <utility>\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/crypto/hashes/sponge/padding_free_sponge.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/param_traits/poseidon2_baby_bear.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_params.h\"\n#include \"tachyon/crypto/hashes/sponge/truncated_permutation.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n\nnamespace tachyon::crypto {\n\nconstexpr size_t kRate = 8;\nconstexpr size_t kChunk = 8;\nconstexpr size_t kN = 2;\n\nusing F = math::BabyBear;\nusing PackedF = math::PackedBabyBear;\nusing Params = Poseidon2Params<Poseidon2Vendor::kPlonky3,\n                               Poseidon2Vendor::kPlonky3, F, 15, 7>;\nusing PackedParams = Poseidon2Params<Poseidon2Vendor::kPlonky3,\n                                     Poseidon2Vendor::kPlonky3, PackedF, 15, 7>;\nusing Poseidon2 = Poseidon2Sponge<Params>;\nusing PackedPoseidon2 = Poseidon2Sponge<PackedParams>;\nusing MyHasher = PaddingFreeSponge<Poseidon2, kRate, kChunk>;\nusing MyPackedHasher = PaddingFreeSponge<PackedPoseidon2, kRate, kChunk>;\nusing MyCompressor = TruncatedPermutation<Poseidon2, kChunk, kN>;\nusing MyPackedCompressor = TruncatedPermutation<PackedPoseidon2, kChunk, kN>;\nusing Tree = FieldMerkleTree<F, kChunk>;\nusing MMCS = FieldMerkleTreeMMCS<F, MyHasher, MyPackedHasher, MyCompressor,\n                                 MyPackedCompressor, kChunk>;\n\nnamespace {\n\nclass FieldMerkleTreeMMCSTest : public math::FiniteFieldTest<PackedF> {\n protected:\n  MMCS mmcs_;\n};\n\n}  // namespace\n\nTEST_F(FieldMerkleTreeMMCSTest, Commit) {\n  std::vector<F> vector{F(2), F(1), F(2), F(2), F(0), F(0), F(1), F(0)};\n  const math::RowMajorMatrix<F> owned_matrix{\n      {F(2)}, {F(1)}, {F(2)}, {F(2)}, {F(0)}, {F(0)}, {F(1)}, {F(0)},\n  };\n  Eigen::Map<const math::RowMajorMatrix<F>> matrix = math::Map(owned_matrix);\n  std::vector<Eigen::Map<const math::RowMajorMatrix<F>>> matrices = {matrix};\n\n  std::vector<Eigen::Map<const math::RowMajorMatrix<F>>> matrices_tmp =\n      matrices;\n  Tree tree =\n      Tree::BuildCpu(mmcs_.hasher(), mmcs_.packed_hasher(), mmcs_.compressor(),\n                     mmcs_.packed_compressor(), std::move(matrices_tmp));\n\n  {\n    std::array<F, kChunk> commitment;\n    Tree prover_data;\n    ASSERT_TRUE(mmcs_.Commit(vector, &commitment, &prover_data));\n    EXPECT_EQ(commitment, tree.GetRoot());\n  }\n  {\n    std::array<F, kChunk> commitment;\n    Tree prover_data;\n    ASSERT_TRUE(mmcs_.Commit(matrix, &commitment, &prover_data));\n    EXPECT_EQ(commitment, tree.GetRoot());\n  }\n  {\n    std::array<F, kChunk> commitment;\n    Tree prover_data;\n    ASSERT_TRUE(mmcs_.Commit(std::move(matrices), &commitment, &prover_data));\n    EXPECT_EQ(commitment, tree.GetRoot());\n  }\n}\n\nTEST_F(FieldMerkleTreeMMCSTest, CommitOwned) {\n  std::vector<F> vector{F(2), F(1), F(2), F(2), F(0), F(0), F(1), F(0)};\n  math::RowMajorMatrix<F> matrix{\n      {F(2)}, {F(1)}, {F(2)}, {F(2)}, {F(0)}, {F(0)}, {F(1)}, {F(0)},\n  };\n  std::vector<math::RowMajorMatrix<F>> matrices = {matrix};\n\n  std::vector<math::RowMajorMatrix<F>> matrices_tmp = matrices;\n  Tree tree = Tree::BuildOwnedCpu(mmcs_.hasher(), mmcs_.packed_hasher(),\n                                  mmcs_.compressor(), mmcs_.packed_compressor(),\n                                  std::move(matrices_tmp));\n\n  {\n    std::array<F, kChunk> commitment;\n    Tree prover_data;\n    ASSERT_TRUE(mmcs_.CommitOwned(vector, &commitment, &prover_data));\n    EXPECT_EQ(commitment, tree.GetRoot());\n  }\n  {\n    std::array<F, kChunk> commitment;\n    Tree prover_data;\n    ASSERT_TRUE(\n        mmcs_.CommitOwned(std::move(matrix), &commitment, &prover_data));\n    EXPECT_EQ(commitment, tree.GetRoot());\n  }\n  {\n    std::array<F, kChunk> commitment;\n    Tree prover_data;\n    ASSERT_TRUE(\n        mmcs_.CommitOwned(std::move(matrices), &commitment, &prover_data));\n    EXPECT_EQ(commitment, tree.GetRoot());\n  }\n}\n\nTEST_F(FieldMerkleTreeMMCSTest, CommitAndVerify) {\n  struct Config {\n    size_t num;\n    math::Dimensions dimensions;\n  };\n\n  struct {\n    std::vector<Config> configs;\n    size_t index;\n  } tests[] = {\n      {{{4, {8, 1000}}, {5, {8, 70}}, {6, {8, 8}}}, 6},\n      {{{1, {1, 32}},\n        {1, {2, 32}},\n        {1, {3, 32}},\n        {1, {4, 32}},\n        {1, {5, 32}},\n        {1, {6, 32}},\n        {1, {7, 32}},\n        {1, {8, 32}},\n        {1, {9, 32}},\n        {1, {10, 32}}},\n       17},\n  };\n\n  for (const auto& test : tests) {\n    std::vector<math::RowMajorMatrix<F>> matrices;\n    std::vector<math::Dimensions> dimensions_vec;\n    for (size_t i = 0; i < test.configs.size(); ++i) {\n      math::Dimensions dimensions = test.configs[i].dimensions;\n      for (size_t j = 0; j < test.configs[i].num; ++j) {\n        matrices.push_back(math::RowMajorMatrix<F>::Random(dimensions.height,\n                                                           dimensions.width));\n        dimensions_vec.push_back(dimensions);\n      }\n    }\n\n    std::array<F, kChunk> commitment;\n    Tree prover_data;\n    ASSERT_TRUE(\n        mmcs_.CommitOwned(std::move(matrices), &commitment, &prover_data));\n\n    std::vector<std::vector<F>> openings;\n    std::vector<std::array<F, kChunk>> proof;\n    ASSERT_TRUE(\n        mmcs_.CreateOpeningProof(test.index, prover_data, &openings, &proof));\n    ASSERT_TRUE(mmcs_.VerifyOpeningProof(commitment, dimensions_vec, test.index,\n                                         openings, proof));\n  }\n}\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/crypto/commitments/merkle_tree/field_merkle_tree/field_merkle_tree_unittest.cc",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#include \"tachyon/crypto/commitments/merkle_tree/field_merkle_tree/field_merkle_tree.h\"\n\n#include <utility>\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/crypto/hashes/sponge/padding_free_sponge.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/param_traits/poseidon2_baby_bear.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_params.h\"\n#include \"tachyon/crypto/hashes/sponge/truncated_permutation.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n\nnamespace tachyon::crypto {\n\nconstexpr size_t kRate = 8;\nconstexpr size_t kChunk = 8;\nconstexpr size_t kN = 2;\n\nusing F = math::BabyBear;\nusing PackedF = math::PackedBabyBear;\nusing Params = Poseidon2Params<Poseidon2Vendor::kPlonky3,\n                               Poseidon2Vendor::kPlonky3, F, 15, 7>;\nusing PackedParams = Poseidon2Params<Poseidon2Vendor::kPlonky3,\n                                     Poseidon2Vendor::kPlonky3, PackedF, 15, 7>;\nusing Poseidon2 = Poseidon2Sponge<Params>;\nusing PackedPoseidon2 = Poseidon2Sponge<PackedParams>;\nusing MyHasher = PaddingFreeSponge<Poseidon2, kRate, kChunk>;\nusing MyPackedHasher = PaddingFreeSponge<PackedPoseidon2, kRate, kChunk>;\nusing MyCompressor = TruncatedPermutation<Poseidon2, kChunk, kN>;\nusing MyPackedCompressor = TruncatedPermutation<PackedPoseidon2, kChunk, kN>;\nusing Tree = FieldMerkleTree<F, kChunk>;\n\nnamespace {\n\nclass FieldMerkleTreeTest : public math::FiniteFieldTest<PackedF> {\n protected:\n  MyHasher hasher_;\n  MyCompressor compressor_;\n  MyPackedHasher packed_hasher_;\n  MyPackedCompressor packed_compressor_;\n};\n\n}  // namespace\n\nTEST_F(FieldMerkleTreeTest, CommitSingle1x8) {\n  math::RowMajorMatrix<F> matrix{\n      {F(2)}, {F(1)}, {F(2)}, {F(2)}, {F(0)}, {F(0)}, {F(1)}, {F(0)},\n  };\n  std::vector<math::RowMajorMatrix<F>> matrices = {matrix};\n\n  Tree tree = Tree::BuildOwnedCpu(hasher_, packed_hasher_, compressor_,\n                                  packed_compressor_, std::move(matrices));\n\n  auto h0_1 = compressor_.Compress(std::vector<std::array<F, kChunk>>{\n      hasher_.Hash(std::vector<F>{matrix(0, 0)}),\n      hasher_.Hash(std::vector<F>{matrix(1, 0)})});\n  auto h2_3 = compressor_.Compress(std::vector<std::array<F, kChunk>>{\n      hasher_.Hash(std::vector<F>{matrix(2, 0)}),\n      hasher_.Hash(std::vector<F>{matrix(3, 0)})});\n  auto h4_5 = compressor_.Compress(std::vector<std::array<F, kChunk>>{\n      hasher_.Hash(std::vector<F>{matrix(4, 0)}),\n      hasher_.Hash(std::vector<F>{matrix(5, 0)})});\n  auto h6_7 = compressor_.Compress(std::vector<std::array<F, kChunk>>{\n      hasher_.Hash(std::vector<F>{matrix(6, 0)}),\n      hasher_.Hash(std::vector<F>{matrix(7, 0)})});\n  auto h0_3 =\n      compressor_.Compress(std::vector<std::array<F, kChunk>>{h0_1, h2_3});\n  auto h4_7 =\n      compressor_.Compress(std::vector<std::array<F, kChunk>>{h4_5, h6_7});\n  auto expected =\n      compressor_.Compress(std::vector<std::array<F, kChunk>>{h0_3, h4_7});\n  EXPECT_EQ(tree.GetRoot(), expected);\n}\n\nTEST_F(FieldMerkleTreeTest, CommitSingle2x2) {\n  math::RowMajorMatrix<F> matrix{\n      {F(0), F(1)},\n      {F(2), F(1)},\n  };\n  std::vector<math::RowMajorMatrix<F>> matrices = {matrix};\n\n  Tree tree = Tree::BuildOwnedCpu(hasher_, packed_hasher_, compressor_,\n                                  packed_compressor_, std::move(matrices));\n\n  auto expected = compressor_.Compress(std::vector<std::array<F, kChunk>>{\n      hasher_.Hash(std::vector<F>{matrix(0, 0), matrix(0, 1)}),\n      hasher_.Hash(std::vector<F>{matrix(1, 0), matrix(1, 1)})});\n  EXPECT_EQ(tree.GetRoot(), expected);\n}\n\nTEST_F(FieldMerkleTreeTest, CommitSingle2x3) {\n  math::RowMajorMatrix<F> matrix{\n      {F(0), F(1)},\n      {F(2), F(1)},\n      {F(2), F(2)},\n  };\n  std::vector<math::RowMajorMatrix<F>> matrices = {matrix};\n\n  Tree tree = Tree::BuildOwnedCpu(hasher_, packed_hasher_, compressor_,\n                                  packed_compressor_, std::move(matrices));\n  std::array<F, kChunk> default_digest =\n      base::CreateArray<kChunk>([]() { return F::Zero(); });\n  auto h0_3 = compressor_.Compress(std::vector<std::array<F, kChunk>>{\n      hasher_.Hash(std::vector<F>{matrix(0, 0), matrix(0, 1)}),\n      hasher_.Hash(std::vector<F>{matrix(1, 0), matrix(1, 1)})});\n  auto h4_5 = compressor_.Compress(std::vector<std::array<F, kChunk>>{\n      hasher_.Hash(std::vector<F>{matrix(2, 0), matrix(2, 1)}),\n      default_digest});\n  auto expected =\n      compressor_.Compress(std::vector<std::array<F, kChunk>>{h0_3, h4_5});\n  EXPECT_EQ(tree.GetRoot(), expected);\n}\n\nTEST_F(FieldMerkleTreeTest, CommitMixed) {\n  math::RowMajorMatrix<F> matrix{\n      {F(0), F(1)},\n      {F(2), F(1)},\n      {F(2), F(2)},\n  };\n  math::RowMajorMatrix<F> matrix2{\n      {F(1), F(2), F(1)},\n      {F(0), F(2), F(2)},\n  };\n  std::vector<math::RowMajorMatrix<F>> matrices = {matrix, matrix2};\n\n  Tree tree = Tree::BuildOwnedCpu(hasher_, packed_hasher_, compressor_,\n                                  packed_compressor_, std::move(matrices));\n  std::array<F, kChunk> default_digest =\n      base::CreateArray<kChunk>([]() { return F::Zero(); });\n  auto h0_3 = compressor_.Compress(std::vector<std::array<F, kChunk>>{\n      hasher_.Hash(std::vector<F>{matrix(0, 0), matrix(0, 1)}),\n      hasher_.Hash(std::vector<F>{matrix(1, 0), matrix(1, 1)})});\n  auto h4_6 =\n      hasher_.Hash(std::vector<F>{matrix2(0, 0), matrix2(0, 1), matrix2(0, 2)});\n  auto h7_8 = compressor_.Compress(std::vector<std::array<F, kChunk>>{\n      hasher_.Hash(std::vector<F>{matrix(2, 0), matrix(2, 1)}),\n      default_digest});\n  auto h9_11 =\n      hasher_.Hash(std::vector<F>{matrix2(1, 0), matrix2(1, 1), matrix2(1, 2)});\n  auto h0_6 =\n      compressor_.Compress(std::vector<std::array<F, kChunk>>{h0_3, h4_6});\n  auto h7_11 =\n      compressor_.Compress(std::vector<std::array<F, kChunk>>{h7_8, h9_11});\n  auto expected =\n      compressor_.Compress(std::vector<std::array<F, kChunk>>{h0_6, h7_11});\n  EXPECT_EQ(tree.GetRoot(), expected);\n}\n\nTEST_F(FieldMerkleTreeTest, CommitEitherOrder) {\n  math::RowMajorMatrix<F> matrix = math::RowMajorMatrix<F>::Random(3, 2);\n  math::RowMajorMatrix<F> matrix2 = math::RowMajorMatrix<F>::Random(2, 3);\n\n  std::vector<math::RowMajorMatrix<F>> matrices = {matrix, matrix2};\n  Tree tree = Tree::BuildOwnedCpu(hasher_, packed_hasher_, compressor_,\n                                  packed_compressor_, std::move(matrices));\n\n  std::vector<math::RowMajorMatrix<F>> matrices2 = {matrix2, matrix};\n  Tree tree2 = Tree::BuildOwnedCpu(hasher_, packed_hasher_, compressor_,\n                                   packed_compressor_, std::move(matrices2));\n  EXPECT_EQ(tree.GetRoot(), tree2.GetRoot());\n}\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/crypto/commitments/merkle_tree/field_merkle_tree/icicle/BUILD.bazel",
    "content": "load(\"@icicle//:build_defs.bzl\", \"BABY_BEAR\", \"BN254\", \"icicle_defines\")\nload(\"@local_config_cuda//cuda:build_defs.bzl\", \"if_cuda\")\nload(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cuda_library\")\n\ntachyon_cuda_library(\n    name = \"icicle_mmcs_baby_bear\",\n    srcs = if_cuda([\"icicle_mmcs_baby_bear.cc\"]),\n    hdrs = [\"icicle_mmcs_baby_bear.h\"],\n    local_defines = icicle_defines(BABY_BEAR),\n    deps = [\n        \":icicle_mmcs\",\n        \"//tachyon/base:bits\",\n        \"//tachyon/base:openmp_util\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/device/gpu:gpu_enums\",\n        \"//tachyon/device/gpu:gpu_logging\",\n        \"@com_google_absl//absl/numeric:bits\",\n        \"@icicle//:merkle_tree_baby_bear\",\n    ] + if_cuda([\n        \"@local_config_cuda//cuda:cudart_static\",\n    ]),\n)\n\ntachyon_cuda_library(\n    name = \"icicle_mmcs_bn254\",\n    srcs = if_cuda([\"icicle_mmcs_bn254.cc\"]),\n    hdrs = [\"icicle_mmcs_bn254.h\"],\n    local_defines = icicle_defines(BN254),\n    deps = [\n        \":icicle_mmcs\",\n        \"//tachyon/base:bits\",\n        \"//tachyon/base:openmp_util\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/device/gpu:gpu_enums\",\n        \"//tachyon/device/gpu:gpu_logging\",\n        \"@com_google_absl//absl/numeric:bits\",\n        \"@icicle//:merkle_tree_bn254\",\n    ] + if_cuda([\n        \"@local_config_cuda//cuda:cudart_static\",\n    ]),\n)\n\ntachyon_cc_library(\n    name = \"icicle_mmcs\",\n    hdrs = [\"icicle_mmcs.h\"],\n    deps = [\n        \"//tachyon:export\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/device/gpu:gpu_device_functions\",\n        \"//tachyon/math/elliptic_curves/bls12/bls12_381:fr\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:fr\",\n        \"//tachyon/math/finite_fields/baby_bear\",\n        \"//tachyon/math/matrix:matrix_types\",\n        \"@com_google_absl//absl/types:span\",\n        \"@icicle//:hdrs\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"icicle_mmcs_holder\",\n    hdrs = [\"icicle_mmcs_holder.h\"],\n    visibility = [\"//visibility:public\"],\n    deps = [\n        \":icicle_mmcs_baby_bear\",\n        \":icicle_mmcs_bn254\",\n        \"//tachyon/device/gpu:scoped_mem_pool\",\n        \"//tachyon/device/gpu:scoped_stream\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/crypto/commitments/merkle_tree/field_merkle_tree/icicle/icicle_mmcs.h",
    "content": "#ifndef TACHYON_CRYPTO_COMMITMENTS_MERKLE_TREE_FIELD_MERKLE_TREE_ICICLE_ICICLE_MMCS_H_\n#define TACHYON_CRYPTO_COMMITMENTS_MERKLE_TREE_FIELD_MERKLE_TREE_ICICLE_ICICLE_MMCS_H_\n\n#include <memory>\n#include <vector>\n\n#include \"absl/types/span.h\"\n#include \"third_party/icicle/include/merkle-tree/merkle_tree_config.h\"\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/device/gpu/gpu_device_functions.h\"\n#include \"tachyon/export.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/fr.h\"\n#include \"tachyon/math/finite_fields/baby_bear/baby_bear.h\"\n#include \"tachyon/math/matrix/matrix_types.h\"\n\nnamespace tachyon::crypto {\n\ntemplate <class F>\nstruct IsIcicleMMCSSupportedImpl {\n  constexpr static bool value = false;\n};\n\ntemplate <>\nstruct IsIcicleMMCSSupportedImpl<math::BabyBear> {\n  constexpr static bool value = true;\n};\n\ntemplate <>\nstruct IsIcicleMMCSSupportedImpl<math::bn254::Fr> {\n  constexpr static bool value = true;\n};\n\ntemplate <typename F>\nconstexpr bool IsIcicleMMCSSupported = IsIcicleMMCSSupportedImpl<F>::value;\n\nstruct TACHYON_EXPORT IcicleMMCSOptions {\n  unsigned int arity = 2;\n  unsigned int keep_rows = 0;\n  unsigned int digest_elements = 1;\n  bool are_inputs_on_device = false;\n  bool are_outputs_on_device = false;\n  bool is_async = false;\n};\n\ntemplate <typename F>\nclass IcicleMMCS {\n public:\n  IcicleMMCS(gpuMemPool_t mem_pool, gpuStream_t stream, const void* hasher,\n             const void* compressor, size_t rate,\n             const IcicleMMCSOptions& options = IcicleMMCSOptions())\n      : mem_pool_(mem_pool),\n        stream_(stream),\n        hasher_(hasher),\n        compressor_(compressor),\n        rate_(rate) {\n    ::device_context::DeviceContext ctx{stream_, /*device_id=*/0, mem_pool_};\n    config_.reset(new ::merkle_tree::TreeBuilderConfig{\n        ctx,\n        options.arity,\n        options.keep_rows,\n        options.digest_elements,\n        options.are_inputs_on_device,\n        options.are_outputs_on_device,\n        options.is_async,\n    });\n    VLOG(1) << \"IcicleMMCS is created\";\n  }\n  IcicleMMCS(const IcicleMMCS& other) = delete;\n  IcicleMMCS& operator=(const IcicleMMCS& other) = delete;\n\n  [[nodiscard]] bool Commit(\n      std::vector<Eigen::Map<const math::RowMajorMatrix<F>>>&& matrices,\n      std::vector<std::vector<std::vector<F>>>* outputs);\n\n private:\n  gpuMemPool_t mem_pool_ = nullptr;\n  gpuStream_t stream_ = nullptr;\n  // not owned\n  const void* hasher_ = nullptr;\n  // not owned\n  const void* compressor_ = nullptr;\n  size_t rate_ = 0;\n  std::unique_ptr<::merkle_tree::TreeBuilderConfig> config_;\n};\n\ntemplate <>\nTACHYON_EXPORT bool IcicleMMCS<math::BabyBear>::Commit(\n    std::vector<Eigen::Map<const math::RowMajorMatrix<math::BabyBear>>>&&\n        matrices,\n    std::vector<std::vector<std::vector<math::BabyBear>>>* outputs);\n\ntemplate <>\nTACHYON_EXPORT bool IcicleMMCS<math::bn254::Fr>::Commit(\n    std::vector<Eigen::Map<const math::RowMajorMatrix<math::bn254::Fr>>>&&\n        matrices,\n    std::vector<std::vector<std::vector<math::bn254::Fr>>>* outputs);\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_COMMITMENTS_MERKLE_TREE_FIELD_MERKLE_TREE_ICICLE_ICICLE_MMCS_H_\n"
  },
  {
    "path": "tachyon/crypto/commitments/merkle_tree/field_merkle_tree/icicle/icicle_mmcs_baby_bear.cc",
    "content": "#include \"tachyon/crypto/commitments/merkle_tree/field_merkle_tree/icicle/icicle_mmcs_baby_bear.h\"\n\n#include <algorithm>\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"absl/numeric/bits.h\"\n#include \"third_party/icicle/include/poseidon2/poseidon2.cu.h\"\n#include \"third_party/icicle/src/merkle-tree/merkle.cu.cc\"  // NOLINT(build/include)\n#include \"third_party/icicle/src/merkle-tree/mmcs.cu.cc\"  // NOLINT(build/include)\n\n#include \"tachyon/base/bits.h\"\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/openmp_util.h\"\n#include \"tachyon/crypto/commitments/merkle_tree/field_merkle_tree/icicle/icicle_mmcs.h\"\n#include \"tachyon/device/gpu/gpu_enums.h\"\n#include \"tachyon/device/gpu/gpu_logging.h\"\n\ngpuError_t tachyon_babybear_mmcs_commit_cuda(\n    const ::matrix::Matrix<::babybear::scalar_t>* leaves,\n    unsigned int number_of_inputs, ::babybear::scalar_t* digests,\n    const ::hash::Hasher<::babybear::scalar_t, ::babybear::scalar_t>* hasher,\n    const ::hash::Hasher<::babybear::scalar_t, ::babybear::scalar_t>*\n        compressor,\n    const ::merkle_tree::TreeBuilderConfig& tree_config) {\n  // NOTE(GideokKim): The internal logic of the icicle Merkle tree always\n  // assumes that the leaves exist in multiples of 2.\n  return ::merkle_tree::mmcs_commit<::babybear::scalar_t, ::babybear::scalar_t>(\n      leaves, number_of_inputs, digests, *hasher, *compressor, tree_config);\n}\n\nnamespace tachyon::crypto {\n\ntemplate <>\nbool IcicleMMCS<math::BabyBear>::Commit(\n    std::vector<Eigen::Map<const math::RowMajorMatrix<math::BabyBear>>>&&\n        matrices,\n    std::vector<std::vector<std::vector<math::BabyBear>>>* outputs) {\n#if FIELD_ID != BABY_BEAR\n#error Only BABY_BEAR is supported\n#endif\n  std::vector<std::vector<::babybear::scalar_t>> matrices_tmp(matrices.size());\n  for (size_t i = 0; i < matrices.size(); ++i) {\n    matrices_tmp[i].resize(matrices[i].rows() * matrices[i].cols());\n    OMP_PARALLEL_FOR(size_t j = 0; j < matrices[i].size(); ++j) {\n      matrices_tmp[i][j] = ::babybear::scalar_t::from_montgomery(\n          reinterpret_cast<const ::babybear::scalar_t*>(matrices[i].data())[j]);\n    }\n  }\n\n  std::vector<::matrix::Matrix<::babybear::scalar_t>> leaves =\n      base::CreateVector(matrices.size(), [&matrices_tmp, &matrices](size_t i) {\n        return ::matrix::Matrix<::babybear::scalar_t>{\n            matrices_tmp[i].data(),\n            static_cast<size_t>(matrices[i].cols()),\n            static_cast<size_t>(matrices[i].rows()),\n        };\n      });\n\n  size_t max_tree_height = 0;\n  for (const auto& matrix : matrices) {\n    size_t tree_height = base::bits::Log2Ceiling(\n        absl::bit_ceil(static_cast<size_t>(matrix.rows())));\n    max_tree_height = std::max(max_tree_height, tree_height);\n  }\n  config_->keep_rows = max_tree_height + 1;\n  config_->digest_elements = rate_;\n  size_t digests_len = ::merkle_tree::get_digests_len(\n      config_->keep_rows - 1, config_->arity, config_->digest_elements);\n\n  std::unique_ptr<::babybear::scalar_t[]> icicle_digest(\n      new ::babybear::scalar_t[digests_len]);\n\n  gpuError_t error = tachyon_babybear_mmcs_commit_cuda(\n      leaves.data(), leaves.size(), icicle_digest.get(),\n      reinterpret_cast<\n          const ::hash::Hasher<::babybear::scalar_t, ::babybear::scalar_t>*>(\n          hasher_),\n      reinterpret_cast<\n          const ::hash::Hasher<::babybear::scalar_t, ::babybear::scalar_t>*>(\n          compressor_),\n      *config_);\n  if (error != gpuSuccess) {\n    GPU_LOG(ERROR, error) << \"Failed tachyon_babybear_mmcs_commit_cuda()\";\n    return false;\n  }\n\n  // TODO(GideokKim): Optimize this.\n  outputs->reserve(config_->keep_rows);\n  size_t previous_number_of_element = 0;\n  for (size_t layer_idx = 0; layer_idx <= max_tree_height; ++layer_idx) {\n    std::vector<std::vector<math::BabyBear>> digest_layer;\n    size_t number_of_node = 1 << (max_tree_height - layer_idx);\n    digest_layer.reserve(number_of_node);\n\n    for (size_t node_idx = 0; node_idx < number_of_node; ++node_idx) {\n      std::vector<math::BabyBear> digest;\n      digest.reserve(config_->digest_elements);\n\n      for (size_t element_idx = 0; element_idx < config_->digest_elements;\n           ++element_idx) {\n        size_t idx = previous_number_of_element +\n                     config_->digest_elements * node_idx + element_idx;\n        icicle_digest[idx] =\n            ::babybear::scalar_t::to_montgomery(icicle_digest[idx]);\n        digest.emplace_back(\n            *reinterpret_cast<math::BabyBear*>(&icicle_digest[idx]));\n      }\n      digest_layer.emplace_back(std::move(digest));\n    }\n    outputs->emplace_back(std::move(digest_layer));\n    previous_number_of_element += number_of_node * config_->digest_elements;\n  }\n  return true;\n}\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/crypto/commitments/merkle_tree/field_merkle_tree/icicle/icicle_mmcs_baby_bear.h",
    "content": "#ifndef TACHYON_CRYPTO_COMMITMENTS_MERKLE_TREE_FIELD_MERKLE_TREE_ICICLE_ICICLE_MMCS_BABY_BEAR_H_\n#define TACHYON_CRYPTO_COMMITMENTS_MERKLE_TREE_FIELD_MERKLE_TREE_ICICLE_ICICLE_MMCS_BABY_BEAR_H_\n\n#include \"third_party/icicle/include/fields/stark_fields/babybear.cu.h\"\n#include \"third_party/icicle/include/hash/hash.cu.h\"\n#include \"third_party/icicle/include/matrix/matrix.cu.h\"\n#include \"third_party/icicle/include/merkle-tree/merkle_tree_config.h\"\n\n#include \"tachyon/device/gpu/gpu_device_functions.h\"\n\nextern \"C\" gpuError_t tachyon_babybear_mmcs_commit_cuda(\n    const ::matrix::Matrix<::babybear::scalar_t>* leaves,\n    unsigned int number_of_inputs, ::babybear::scalar_t* digests,\n    const ::hash::Hasher<::babybear::scalar_t, ::babybear::scalar_t>* hasher,\n    const ::hash::Hasher<::babybear::scalar_t, ::babybear::scalar_t>*\n        compressor,\n    const ::merkle_tree::TreeBuilderConfig& tree_config);\n\n#endif  // TACHYON_CRYPTO_COMMITMENTS_MERKLE_TREE_FIELD_MERKLE_TREE_ICICLE_ICICLE_MMCS_BABY_BEAR_H_\n"
  },
  {
    "path": "tachyon/crypto/commitments/merkle_tree/field_merkle_tree/icicle/icicle_mmcs_bn254.cc",
    "content": "#include \"tachyon/crypto/commitments/merkle_tree/field_merkle_tree/icicle/icicle_mmcs_bn254.h\"\n\n#include <algorithm>\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"absl/numeric/bits.h\"\n#include \"third_party/icicle/include/poseidon2/poseidon2.cu.h\"\n#include \"third_party/icicle/src/merkle-tree/merkle.cu.cc\"  // NOLINT(build/include)\n#include \"third_party/icicle/src/merkle-tree/mmcs.cu.cc\"  // NOLINT(build/include)\n\n#include \"tachyon/base/bits.h\"\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/openmp_util.h\"\n#include \"tachyon/crypto/commitments/merkle_tree/field_merkle_tree/icicle/icicle_mmcs.h\"\n#include \"tachyon/device/gpu/gpu_enums.h\"\n#include \"tachyon/device/gpu/gpu_logging.h\"\n\ngpuError_t tachyon_bn254_mmcs_commit_cuda(\n    const ::matrix::Matrix<::bn254::scalar_t>* leaves,\n    unsigned int number_of_inputs, ::bn254::scalar_t* digests,\n    const ::hash::Hasher<::bn254::scalar_t, ::bn254::scalar_t>* hasher,\n    const ::hash::Hasher<::bn254::scalar_t, ::bn254::scalar_t>* compressor,\n    const ::merkle_tree::TreeBuilderConfig& tree_config) {\n  // NOTE(GideokKim): The internal logic of the icicle Merkle tree always\n  // assumes that the leaves exist in multiples of 2.\n  return ::merkle_tree::mmcs_commit<::bn254::scalar_t, ::bn254::scalar_t>(\n      leaves, number_of_inputs, digests, *hasher, *compressor, tree_config);\n}\n\nnamespace tachyon::crypto {\n\ntemplate <>\nbool IcicleMMCS<math::bn254::Fr>::Commit(\n    std::vector<Eigen::Map<const math::RowMajorMatrix<math::bn254::Fr>>>&&\n        matrices,\n    std::vector<std::vector<std::vector<math::bn254::Fr>>>* outputs) {\n#if FIELD_ID != BN254\n#error Only BN254 is supported\n#endif\n  std::vector<std::vector<::bn254::scalar_t>> matrices_tmp(matrices.size());\n  for (size_t i = 0; i < matrices.size(); ++i) {\n    matrices_tmp[i].resize(matrices[i].rows() * matrices[i].cols());\n    OMP_PARALLEL_FOR(size_t j = 0; j < matrices[i].size(); ++j) {\n      matrices_tmp[i][j] = ::bn254::scalar_t::from_montgomery(\n          reinterpret_cast<const ::bn254::scalar_t*>(matrices[i].data())[j]);\n    }\n  }\n\n  std::vector<::matrix::Matrix<::bn254::scalar_t>> leaves =\n      base::CreateVector(matrices.size(), [&matrices_tmp, &matrices](size_t i) {\n        return ::matrix::Matrix<::bn254::scalar_t>{\n            matrices_tmp[i].data(),\n            static_cast<size_t>(matrices[i].cols()),\n            static_cast<size_t>(matrices[i].rows()),\n        };\n      });\n\n  size_t max_tree_height = 0;\n  for (const auto& matrix : matrices) {\n    size_t tree_height = base::bits::Log2Ceiling(\n        absl::bit_ceil(static_cast<size_t>(matrix.rows())));\n    max_tree_height = std::max(max_tree_height, tree_height);\n  }\n  config_->keep_rows = max_tree_height + 1;\n  config_->digest_elements = rate_;\n  size_t digests_len = ::merkle_tree::get_digests_len(\n      config_->keep_rows - 1, config_->arity, config_->digest_elements);\n\n  std::unique_ptr<::bn254::scalar_t[]> icicle_digest(\n      new ::bn254::scalar_t[digests_len]);\n\n  gpuError_t error = tachyon_bn254_mmcs_commit_cuda(\n      leaves.data(), leaves.size(), icicle_digest.get(),\n      reinterpret_cast<\n          const ::hash::Hasher<::bn254::scalar_t, ::bn254::scalar_t>*>(hasher_),\n      reinterpret_cast<\n          const ::hash::Hasher<::bn254::scalar_t, ::bn254::scalar_t>*>(\n          compressor_),\n      *config_);\n  if (error != gpuSuccess) {\n    GPU_LOG(ERROR, error) << \"Failed tachyon_bn254_mmcs_commit_cuda()\";\n    return false;\n  }\n\n  // TODO(GideokKim): Optimize this.\n  outputs->reserve(config_->keep_rows);\n  size_t previous_number_of_element = 0;\n  for (size_t layer_idx = 0; layer_idx <= max_tree_height; ++layer_idx) {\n    std::vector<std::vector<math::bn254::Fr>> digest_layer;\n    size_t number_of_node = 1 << (max_tree_height - layer_idx);\n    digest_layer.reserve(number_of_node);\n\n    for (size_t node_idx = 0; node_idx < number_of_node; ++node_idx) {\n      std::vector<math::bn254::Fr> digest;\n      digest.reserve(config_->digest_elements);\n\n      for (size_t element_idx = 0; element_idx < config_->digest_elements;\n           ++element_idx) {\n        size_t idx = previous_number_of_element +\n                     config_->digest_elements * node_idx + element_idx;\n        icicle_digest[idx] =\n            ::bn254::scalar_t::to_montgomery(icicle_digest[idx]);\n        digest.emplace_back(\n            *reinterpret_cast<math::bn254::Fr*>(&icicle_digest[idx]));\n      }\n      digest_layer.emplace_back(std::move(digest));\n    }\n    outputs->emplace_back(std::move(digest_layer));\n    previous_number_of_element += number_of_node * config_->digest_elements;\n  }\n  return true;\n}\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/crypto/commitments/merkle_tree/field_merkle_tree/icicle/icicle_mmcs_bn254.h",
    "content": "#ifndef TACHYON_CRYPTO_COMMITMENTS_MERKLE_TREE_FIELD_MERKLE_TREE_ICICLE_ICICLE_MMCS_BN254_H_\n#define TACHYON_CRYPTO_COMMITMENTS_MERKLE_TREE_FIELD_MERKLE_TREE_ICICLE_ICICLE_MMCS_BN254_H_\n\n#include \"third_party/icicle/include/curves/params/bn254.cu.h\"\n#include \"third_party/icicle/include/hash/hash.cu.h\"\n#include \"third_party/icicle/include/matrix/matrix.cu.h\"\n#include \"third_party/icicle/include/merkle-tree/merkle_tree_config.h\"\n\n#include \"tachyon/device/gpu/gpu_device_functions.h\"\n\nextern \"C\" gpuError_t tachyon_bn254_mmcs_commit_cuda(\n    const ::matrix::Matrix<::bn254::scalar_t>* leaves,\n    unsigned int number_of_inputs, ::bn254::scalar_t* digests,\n    const ::hash::Hasher<::bn254::scalar_t, ::bn254::scalar_t>* hasher,\n    const ::hash::Hasher<::bn254::scalar_t, ::bn254::scalar_t>* compressor,\n    const ::merkle_tree::TreeBuilderConfig& tree_config);\n\n#endif  // TACHYON_CRYPTO_COMMITMENTS_MERKLE_TREE_FIELD_MERKLE_TREE_ICICLE_ICICLE_MMCS_BN254_H_\n"
  },
  {
    "path": "tachyon/crypto/commitments/merkle_tree/field_merkle_tree/icicle/icicle_mmcs_holder.h",
    "content": "#ifndef TACHYON_CRYPTO_COMMITMENTS_MERKLE_TREE_FIELD_MERKLE_TREE_ICICLE_ICICLE_MMCS_HOLDER_H_\n#define TACHYON_CRYPTO_COMMITMENTS_MERKLE_TREE_FIELD_MERKLE_TREE_ICICLE_ICICLE_MMCS_HOLDER_H_\n\n#include <limits>\n#include <memory>\n#include <utility>\n\n#include \"tachyon/crypto/commitments/merkle_tree/field_merkle_tree/icicle/icicle_mmcs.h\"\n#include \"tachyon/device/gpu/scoped_mem_pool.h\"\n#include \"tachyon/device/gpu/scoped_stream.h\"\n\nnamespace tachyon::crypto {\n\ntemplate <typename F>\nclass IcicleMMCSHolder {\n public:\n  IcicleMMCSHolder() = default;\n\n  const IcicleMMCS<F>& operator*() const { return *get(); }\n  IcicleMMCS<F>& operator*() { return *get(); }\n\n  const IcicleMMCS<F>* operator->() const { return get(); }\n  IcicleMMCS<F>* operator->() { return get(); }\n\n  operator bool() const { return !!icicle_; }\n\n  const IcicleMMCS<F>* get() const { return icicle_.get(); }\n  IcicleMMCS<F>* get() { return icicle_.get(); }\n\n  template <size_t Rate>\n  static IcicleMMCSHolder Create(const void* hasher, const void* compressor) {\n    gpuMemPoolProps props = {gpuMemAllocationTypePinned,\n                             gpuMemHandleTypeNone,\n                             {gpuMemLocationTypeDevice, 0}};\n    device::gpu::ScopedMemPool mem_pool = device::gpu::CreateMemPool(&props);\n\n    uint64_t mem_pool_threshold = std::numeric_limits<uint64_t>::max();\n    gpuError_t error = gpuMemPoolSetAttribute(\n        mem_pool.get(), gpuMemPoolAttrReleaseThreshold, &mem_pool_threshold);\n    CHECK_EQ(error, gpuSuccess);\n    device::gpu::ScopedStream stream = device::gpu::CreateStream();\n\n    return {std::move(mem_pool), std::move(stream),\n            std::make_unique<IcicleMMCS<F>>(mem_pool.get(), stream.get(),\n                                            hasher, compressor, Rate)};\n  }\n\n  void Release() {\n    icicle_.reset();\n    stream_.reset();\n    mem_pool_.reset();\n  }\n\n private:\n  IcicleMMCSHolder(device::gpu::ScopedMemPool mem_pool,\n                   device::gpu::ScopedStream stream,\n                   std::unique_ptr<IcicleMMCS<F>> icicle)\n      : mem_pool_(std::move(mem_pool)),\n        stream_(std::move(stream)),\n        icicle_(std::move(icicle)) {}\n\n  device::gpu::ScopedMemPool mem_pool_;\n  device::gpu::ScopedStream stream_;\n  std::unique_ptr<IcicleMMCS<F>> icicle_;\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_COMMITMENTS_MERKLE_TREE_FIELD_MERKLE_TREE_ICICLE_ICICLE_MMCS_HOLDER_H_\n"
  },
  {
    "path": "tachyon/crypto/commitments/mixed_matrix_commitment_scheme.h",
    "content": "#ifndef TACHYON_CRYPTO_COMMITMENTS_MIXED_MATRIX_COMMITMENT_SCHEME_H_\n#define TACHYON_CRYPTO_COMMITMENTS_MIXED_MATRIX_COMMITMENT_SCHEME_H_\n\n#include <algorithm>\n#include <utility>\n#include <vector>\n\n#include \"absl/types/span.h\"\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/openmp_util.h\"\n#include \"tachyon/crypto/commitments/mixed_matrix_commitment_scheme_traits_forward.h\"\n#include \"tachyon/math/geometry/dimensions.h\"\n#include \"tachyon/math/matrix/matrix_types.h\"\n\nnamespace tachyon::crypto {\n\ntemplate <typename Derived>\nclass MixedMatrixCommitmentScheme {\n public:\n  using Field = typename MixedMatrixCommitmentSchemeTraits<Derived>::Field;\n  using Commitment =\n      typename MixedMatrixCommitmentSchemeTraits<Derived>::Commitment;\n  using ProverData =\n      typename MixedMatrixCommitmentSchemeTraits<Derived>::ProverData;\n  using Proof = typename MixedMatrixCommitmentSchemeTraits<Derived>::Proof;\n\n  // NOTE: |Commit()| doesn't own the input data unlike |CommitOwned()|.\n  // This is created based on our usecase. |get_evaluations_on_domain()| returns\n  // a borrowed value with the same lifetime as the prover_data in |ProverData|.\n  // However, if the actual data is managed on the C++ side, then both Rust and\n  // C++ attempt to deallocate the memory once it goes out of scope. An\n  // alternative is using |std::mem::forget()| in Plonky3, but this needs\n  // additional modifications.\n  // See\n  // https://github.com/Plonky3/Plonky3/blob/2df15fd/fri/src/two_adic_pcs.rs#L177-L188.\n  [[nodiscard]] bool Commit(const std::vector<Field>& vector,\n                            Commitment* commitment, ProverData* prover_data) {\n    return Commit(\n        std::vector<Eigen::Map<const math::RowMajorMatrix<Field>>>{\n            Eigen::Map<const math::RowMajorMatrix<Field>>(vector.data(),\n                                                          vector.size(), 1)},\n        commitment, prover_data);\n  }\n\n  [[nodiscard]] bool Commit(\n      const Eigen::Map<const math::RowMajorMatrix<Field>>& matrix,\n      Commitment* commitment, ProverData* prover_data) const {\n    return Commit(\n        std::vector<Eigen::Map<const math::RowMajorMatrix<Field>>>{matrix},\n        commitment, prover_data);\n  }\n\n  [[nodiscard]] bool Commit(\n      std::vector<Eigen::Map<const math::RowMajorMatrix<Field>>>&& matrices,\n      Commitment* commitment, ProverData* prover_data) const {\n    const Derived* derived = static_cast<const Derived*>(this);\n    return derived->DoCommit(std::move(matrices), commitment, prover_data);\n  }\n\n  // NOTE: |CommitOwned()| own the input data unlike |Commit()|.\n  [[nodiscard]] bool CommitOwned(const std::vector<Field>& vector,\n                                 Commitment* commitment,\n                                 ProverData* prover_data) {\n    math::RowMajorMatrix<Field> matrix(vector.size(), 1);\n    OMP_PARALLEL_FOR(size_t i = 0; i < vector.size(); ++i) {\n      matrix(i, 0) = vector[i];\n    }\n    return CommitOwned(std::move(matrix), commitment, prover_data);\n  }\n\n  [[nodiscard]] bool CommitOwned(math::RowMajorMatrix<Field>&& matrix,\n                                 Commitment* commitment,\n                                 ProverData* prover_data) const {\n    return CommitOwned(\n        std::vector<math::RowMajorMatrix<Field>>{std::move(matrix)}, commitment,\n        prover_data);\n  }\n\n  [[nodiscard]] bool CommitOwned(\n      std::vector<math::RowMajorMatrix<Field>>&& matrices,\n      Commitment* commitment, ProverData* prover_data) const {\n    const Derived* derived = static_cast<const Derived*>(this);\n    return derived->DoCommitOwned(std::move(matrices), commitment, prover_data);\n  }\n\n  [[nodiscard]] bool CreateOpeningProof(\n      size_t index, const ProverData& prover_data,\n      std::vector<std::vector<Field>>* openings, Proof* proof) const {\n    const Derived* derived = static_cast<const Derived*>(this);\n    return derived->DoCreateOpeningProof(index, prover_data, openings, proof);\n  }\n\n  const std::vector<Eigen::Map<const math::RowMajorMatrix<Field>>>& GetMatrices(\n      const ProverData& prover_data) const {\n    const Derived* derived = static_cast<const Derived*>(this);\n    return derived->DoGetMatrices(prover_data);\n  }\n\n  std::vector<size_t> GetRowSizes() const {\n    return base::Map(\n        GetMatrices(),\n        [](const Eigen::Map<const math::RowMajorMatrix<Field>>& matrix) {\n          return matrix.rows();\n        });\n  }\n\n  size_t GetMaxRowSize(const ProverData& prover_data) const {\n    const std::vector<Eigen::Map<const math::RowMajorMatrix<Field>>>& matrices =\n        GetMatrices(prover_data);\n    if (matrices.empty()) return 0;\n    return std::max_element(\n               matrices.begin(), matrices.end(),\n               [](const Eigen::Map<const math::RowMajorMatrix<Field>>& a,\n                  const Eigen::Map<const math::RowMajorMatrix<Field>>& b) {\n                 return a.rows() < b.rows();\n               })\n        ->rows();\n  }\n\n  [[nodiscard]] bool VerifyOpeningProof(\n      const Commitment& commitment,\n      absl::Span<const math::Dimensions> dimensions_list, size_t index,\n      absl::Span<const std::vector<Field>> opened_values,\n      const Proof& proof) const {\n    const Derived* derived = static_cast<const Derived*>(this);\n    return derived->DoVerifyOpeningProof(commitment, dimensions_list, index,\n                                         opened_values, proof);\n  }\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_COMMITMENTS_MIXED_MATRIX_COMMITMENT_SCHEME_H_\n"
  },
  {
    "path": "tachyon/crypto/commitments/mixed_matrix_commitment_scheme_traits_forward.h",
    "content": "#ifndef TACHYON_CRYPTO_COMMITMENTS_MIXED_MATRIX_COMMITMENT_SCHEME_TRAITS_FORWARD_H_\n#define TACHYON_CRYPTO_COMMITMENTS_MIXED_MATRIX_COMMITMENT_SCHEME_TRAITS_FORWARD_H_\n\nnamespace tachyon::crypto {\n\ntemplate <typename MMCS>\nstruct MixedMatrixCommitmentSchemeTraits;\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_COMMITMENTS_MIXED_MATRIX_COMMITMENT_SCHEME_TRAITS_FORWARD_H_\n"
  },
  {
    "path": "tachyon/crypto/commitments/pedersen/BUILD.bazel",
    "content": "load(\n    \"//bazel:tachyon_cc.bzl\",\n    \"tachyon_cc_library\",\n    \"tachyon_cc_unittest\",\n)\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"pedersen\",\n    hdrs = [\"pedersen.h\"],\n    deps = [\n        \"//tachyon/base/buffer:copyable\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/base/strings:string_util\",\n        \"//tachyon/crypto/commitments:vector_commitment_scheme\",\n        \"//tachyon/device/gpu:scoped_mem_pool\",\n        \"//tachyon/device/gpu:scoped_stream\",\n        \"//tachyon/math/elliptic_curves/msm:variable_base_msm\",\n        \"//tachyon/math/elliptic_curves/msm:variable_base_msm_gpu\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"pedersen_unittests\",\n    srcs = [\"pedersen_unittest.cc\"],\n    deps = [\n        \":pedersen\",\n        \"//tachyon/base/buffer:vector_buffer\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:g1\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/crypto/commitments/pedersen/pedersen.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_CRYPTO_COMMITMENTS_PEDERSEN_PEDERSEN_H_\n#define TACHYON_CRYPTO_COMMITMENTS_PEDERSEN_PEDERSEN_H_\n\n#include <stddef.h>\n\n#include <algorithm>\n#include <limits>\n#include <memory>\n#include <sstream>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/buffer/copyable.h\"\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/strings/string_util.h\"\n#include \"tachyon/crypto/commitments/vector_commitment_scheme.h\"\n#include \"tachyon/math/elliptic_curves/msm/variable_base_msm.h\"\n#include \"tachyon/math/geometry/point_conversions.h\"\n\n#if TACHYON_CUDA\n#include \"tachyon/device/gpu/scoped_mem_pool.h\"\n#include \"tachyon/device/gpu/scoped_stream.h\"\n#include \"tachyon/math/elliptic_curves/msm/variable_base_msm_gpu.h\"\n#endif\n\nnamespace tachyon {\nnamespace crypto {\n\n// A Pedersen commitment is a point on an elliptic curve that is\n// cryptographically binding to data but hides it.\ntemplate <typename Point, size_t MaxSize,\n          typename Commitment = typename math::Pippenger<Point>::Bucket>\nclass Pedersen final\n    : public VectorCommitmentScheme<Pedersen<Point, MaxSize, Commitment>> {\n public:\n  using Field = typename Point::ScalarField;\n  using Bucket = typename math::Pippenger<Point>::Bucket;\n  using Curve = typename Point::Curve;\n  using AddResult =\n      typename math::internal::AdditiveSemigroupTraits<Point>::ReturnTy;\n\n  Pedersen() = default;\n  Pedersen(const Point& h, const std::vector<Point>& generators)\n      : h_(h), generators_(generators) {\n    CHECK_LE(generators_.size(), MaxSize);\n#if TACHYON_CUDA\n    SetupForGpu();\n#endif\n  }\n  Pedersen(Point&& h, std::vector<Point>&& generators)\n      : h_(h), generators_(std::move(generators)) {\n    CHECK_LE(generators_.size(), MaxSize);\n#if TACHYON_CUDA\n    SetupForGpu();\n#endif\n  }\n\n  const Point& h() const { return h_; }\n  const std::vector<Point>& generators() const { return generators_; }\n\n#if TACHYON_CUDA\n  void SetupForGpu() {\n    if (msm_gpu_) return;\n\n    gpuMemPoolProps props = {gpuMemAllocationTypePinned,\n                             gpuMemHandleTypeNone,\n                             {gpuMemLocationTypeDevice, 0}};\n    mem_pool_ = device::gpu::CreateMemPool(&props);\n\n    uint64_t mem_pool_threshold = std::numeric_limits<uint64_t>::max();\n    gpuError_t error = gpuMemPoolSetAttribute(\n        mem_pool_.get(), gpuMemPoolAttrReleaseThreshold, &mem_pool_threshold);\n    CHECK_EQ(error, gpuSuccess);\n    stream_ = device::gpu::CreateStream();\n\n    msm_gpu_.reset(\n        new math::VariableBaseMSMGpu<Point>(mem_pool_.get(), stream_.get()));\n  }\n#endif\n\n  void ResizeBatchCommitments() {\n    // WARN(batzor): When resizing to a larger size, the last values will be\n    // garbage and should be filled with commitment results.\n    size_t size = this->batch_commitment_state_.batch_count;\n#if TACHYON_CUDA\n    if (msm_gpu_) {\n      gpu_batch_commitments_.resize(size);\n      return;\n    }\n#endif\n    cpu_batch_commitments_.resize(size);\n  }\n\n  std::vector<Commitment> GetBatchCommitments() {\n    // NOTE(batzor): Resizing this vector without initialization is safe since\n    // |BatchNormalize| will overwrite them.\n    std::vector<Commitment> batch_commitments;\n#if TACHYON_CUDA\n    if (msm_gpu_) {\n      if constexpr (std::is_same_v<Commitment, math::ProjectivePoint<Curve>>) {\n        batch_commitments = std::move(gpu_batch_commitments_);\n        // NOLINTNEXTLINE(readability/braces)\n      } else if constexpr (std::is_same_v<Commitment,\n                                          math::AffinePoint<Curve>>) {\n        batch_commitments.resize(gpu_batch_commitments_.size());\n        CHECK(math::ProjectivePoint<Curve>::BatchNormalize(\n            gpu_batch_commitments_, &batch_commitments));\n        gpu_batch_commitments_.clear();\n      } else {\n        batch_commitments.resize(gpu_batch_commitments_.size());\n        CHECK(math::ConvertPoints(gpu_batch_commitments_, &batch_commitments));\n        gpu_batch_commitments_.clear();\n      }\n    } else {\n#endif\n      if constexpr (std::is_same_v<Commitment, Bucket>) {\n        batch_commitments = std::move(cpu_batch_commitments_);\n      } else {\n        batch_commitments.resize(cpu_batch_commitments_.size());\n        CHECK(\n            Bucket::BatchNormalize(cpu_batch_commitments_, &batch_commitments));\n        cpu_batch_commitments_.clear();\n      }\n#if TACHYON_CUDA\n    }\n#endif\n    this->batch_commitment_state_.Reset();\n    return batch_commitments;\n  }\n\n  // VectorCommitmentScheme methods\n  const char* Name() const { return \"Pedersen\"; }\n\n  size_t N() const { return generators_.size(); }\n\n  std::string ToString() const {\n    std::stringstream ss;\n    ss << \"h: \" << h_ << \", generators: \" << ContainerToString(generators_);\n    return ss.str();\n  }\n\n private:\n  friend class VectorCommitmentScheme<Pedersen<Point, MaxSize, Commitment>>;\n\n  bool DoSetup(size_t size) {\n    // NOTE(leegwangwoon): For security, |Random| is used instead of\n    // |CreatePseudoRandomPoints|.\n    // See\n    // https://research.nccgroup.com/2023/03/22/breaking-pedersen-hashes-in-practice/\n\n    h_ = Point::Random();\n    generators_ = base::CreateVector(size, []() { return Point::Random(); });\n    return true;\n  }\n\n  // Pedersen Commitment:\n  // clang-format off\n  // |h|⋅|r| + <|g|, |v|> = |h|⋅|r| + |g₀|⋅|v₀| + |g₁|⋅|v₁| + ... + |gₙ₋₁|⋅|vₙ₋₁|\n  // - |h| is a randomly generated base point from Setup.\n  // - |r| is a random value called the blinding factor.\n  // - |g| denotes random |generators| in Setup params.\n  // - |v| is a vector of values to be committed.\n  // clang-format on\n  bool DoCommit(const std::vector<Field>& v, const Field& r,\n                Commitment* out) const {\n#if TACHYON_CUDA\n    if (msm_gpu_) {\n      math::ProjectivePoint<Curve> msm_result;\n      absl::Span<const Point> bases_span = absl::Span<const Point>(\n          generators_.data(), std::min(generators_.size(), v.size()));\n      if (!msm_gpu_->Run(bases_span, v, &msm_result)) return false;\n      *out = ComputeCommitment(r, msm_result);\n      return true;\n    }\n#endif\n\n    math::VariableBaseMSM<Point> msm;\n    Bucket msm_result;\n    if (!msm.Run(generators_, v, &msm_result)) return false;\n    *out = ComputeCommitment(r, msm_result);\n    return true;\n  }\n\n  bool DoCommit(const std::vector<Field>& v, const Field& r,\n                BatchCommitmentState& state, size_t index) {\n#if TACHYON_CUDA\n    if (msm_gpu_) {\n      absl::Span<const Point> bases_span = absl::Span<const Point>(\n          generators_.data(), std::min(generators_.size(), v.size()));\n      return msm_gpu_->Run(bases_span, v, &gpu_batch_commitments_[index]);\n    }\n#endif\n    math::VariableBaseMSM<Point> msm;\n    if (cpu_batch_commitments_.size() != state.batch_count)\n      cpu_batch_commitments_.resize(state.batch_count);\n    return msm.Run(generators_, v, &cpu_batch_commitments_[index]);\n  }\n\n  template <typename MSMResult>\n  Commitment ComputeCommitment(const Field& r,\n                               const MSMResult& msm_result) const {\n    AddResult rh = r * h_;\n    if constexpr (math::internal::SupportsAdd<MSMResult, AddResult>::value) {\n      if constexpr (std::is_same_v<MSMResult, Commitment>) {\n        return rh + msm_result;\n      } else {\n        return math::ConvertPoint<Commitment>(rh + msm_result);\n      }\n    } else {\n      MSMResult result = math::ConvertPoint<MSMResult>(rh) + msm_result;\n      if constexpr (std::is_same_v<MSMResult, Commitment>) {\n        return result;\n      } else {\n        return math::ConvertPoint<Commitment>(result);\n      }\n    }\n  }\n\n  Point h_;\n  std::vector<Point> generators_;\n  std::vector<Bucket> cpu_batch_commitments_;\n#if TACHYON_CUDA\n  device::gpu::ScopedMemPool mem_pool_;\n  device::gpu::ScopedStream stream_;\n  std::unique_ptr<math::VariableBaseMSMGpu<Point>> msm_gpu_;\n  std::vector<math::ProjectivePoint<Curve>> gpu_batch_commitments_;\n#endif\n};\n\ntemplate <typename Point, size_t MaxSize, typename _Commitment>\nstruct VectorCommitmentSchemeTraits<Pedersen<Point, MaxSize, _Commitment>> {\n public:\n  constexpr static size_t kMaxSize = MaxSize;\n  constexpr static bool kIsTransparent = true;\n  constexpr static bool kSupportsBatchMode = true;\n\n  using Field = typename Point::ScalarField;\n  using Commitment = _Commitment;\n};\n\n}  // namespace crypto\n\nnamespace base {\n\ntemplate <typename Point, size_t MaxSize, typename Commitment>\nclass Copyable<crypto::Pedersen<Point, MaxSize, Commitment>> {\n public:\n  using PCS = crypto::Pedersen<Point, MaxSize, Commitment>;\n\n  static bool WriteTo(const PCS& pcs, Buffer* buffer) {\n    return buffer->WriteMany(pcs.h(), pcs.generators());\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer, PCS* pcs) {\n    Point h;\n    std::vector<Point> generators;\n    if (!buffer.ReadMany(&h, &generators)) {\n      return false;\n    }\n\n    *pcs = PCS(std::move(h), std::move(generators));\n    return true;\n  }\n\n  static size_t EstimateSize(const PCS& pcs) {\n    return base::EstimateSize(pcs.h(), pcs.generators());\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_CRYPTO_COMMITMENTS_PEDERSEN_PEDERSEN_H_\n"
  },
  {
    "path": "tachyon/crypto/commitments/pedersen/pedersen_unittest.cc",
    "content": "#include \"tachyon/crypto/commitments/pedersen/pedersen.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/buffer/vector_buffer.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/g1.h\"\n\nnamespace tachyon::crypto {\n\nnamespace {\n\nclass PedersenTest : public testing::Test {\n public:\n  constexpr static size_t kMaxSize = 3;\n\n  using VCS = Pedersen<math::bn254::G1AffinePoint, kMaxSize,\n                       math::bn254::G1AffinePoint>;\n\n  static void SetUpTestSuite() { math::bn254::G1Curve::Init(); }\n};\n\n}  // namespace\n\nTEST_F(PedersenTest, CommitPedersen) {\n  VCS vcs;\n  ASSERT_TRUE(vcs.Setup());\n\n  std::vector<math::bn254::Fr> v =\n      base::CreateVector(kMaxSize, []() { return math::bn254::Fr::Random(); });\n\n  math::bn254::Fr r = math::bn254::Fr::Random();\n  math::bn254::G1AffinePoint cpu_commitment;\n  ASSERT_TRUE(vcs.Commit(v, r, &cpu_commitment));\n\n  math::VariableBaseMSM<math::bn254::G1AffinePoint> msm;\n  math::bn254::G1PointXYZZ msm_result_xyzz;\n  ASSERT_TRUE(msm.Run(vcs.generators(), v, &msm_result_xyzz));\n  math::bn254::G1AffinePoint msm_result_affine = msm_result_xyzz.ToAffine();\n\n  EXPECT_EQ(cpu_commitment, (msm_result_affine + r * vcs.h()).ToAffine());\n\n#if TACHYON_CUDA\n  vcs.SetupForGpu();\n\n  math::bn254::G1AffinePoint gpu_commitment;\n  ASSERT_TRUE(vcs.Commit(v, r, &gpu_commitment));\n\n  EXPECT_EQ(gpu_commitment, cpu_commitment);\n#endif\n}\n\nTEST_F(PedersenTest, BatchCommitPedersen) {\n  VCS vcs;\n  ASSERT_TRUE(vcs.Setup());\n\n  size_t num_vectors = 10;\n\n  std::vector<std::vector<math::bn254::Fr>> v_vec =\n      base::CreateVector(num_vectors, []() {\n        return base::CreateVector(kMaxSize,\n                                  []() { return math::bn254::Fr::Random(); });\n      });\n\n  std::vector<math::bn254::Fr> r_vec = base::CreateVector(\n      num_vectors, []() { return math::bn254::Fr::Random(); });\n\n  vcs.SetBatchMode(num_vectors);\n  for (size_t i = 0; i < num_vectors; ++i) {\n    ASSERT_TRUE(vcs.Commit(v_vec[i], r_vec[i], i));\n  }\n  std::vector<math::bn254::G1AffinePoint> cpu_batch_commitments =\n      vcs.GetBatchCommitments();\n  BatchCommitmentState& state = vcs.batch_commitment_state();\n  EXPECT_EQ(state.batch_mode, false);\n  EXPECT_EQ(state.batch_count, size_t{0});\n\n  math::VariableBaseMSM<math::bn254::G1AffinePoint> msm;\n  std::vector<math::bn254::G1PointXYZZ> cpu_msm_results_xyzz(num_vectors);\n  for (size_t i = 0; i < num_vectors; ++i) {\n    ASSERT_TRUE(msm.Run(vcs.generators(), v_vec[i], &cpu_msm_results_xyzz[i]));\n  }\n  std::vector<math::bn254::G1AffinePoint> cpu_msm_results_affine(num_vectors);\n  ASSERT_TRUE(math::bn254::G1PointXYZZ::BatchNormalize(\n      cpu_msm_results_xyzz, &cpu_msm_results_affine));\n\n  EXPECT_EQ(cpu_batch_commitments, cpu_msm_results_affine);\n\n#if TACHYON_CUDA\n  vcs.SetupForGpu();\n\n  state.batch_mode = true;\n  state.batch_count = num_vectors;\n  vcs.ResizeBatchCommitments();\n  for (size_t i = 0; i < num_vectors; ++i) {\n    ASSERT_TRUE(vcs.Commit(v_vec[i], r_vec[i], i));\n  }\n  std::vector<math::bn254::G1AffinePoint> gpu_batch_commitments =\n      vcs.GetBatchCommitments();\n  EXPECT_EQ(state.batch_mode, false);\n  EXPECT_EQ(state.batch_count, size_t{0});\n\n  EXPECT_EQ(gpu_batch_commitments, cpu_batch_commitments);\n\n#endif\n}\n\nTEST_F(PedersenTest, Copyable) {\n  VCS expected;\n  ASSERT_TRUE(expected.Setup());\n\n  base::Uint8VectorBuffer write_buf;\n  ASSERT_TRUE(write_buf.Grow(base::EstimateSize(expected)));\n  ASSERT_TRUE(write_buf.Write(expected));\n  ASSERT_TRUE(write_buf.Done());\n\n  write_buf.set_buffer_offset(0);\n\n  VCS value;\n  ASSERT_TRUE(write_buf.Read(&value));\n  EXPECT_EQ(expected.generators(), value.generators());\n}\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/crypto/commitments/polynomial_openings.h",
    "content": "#ifndef TACHYON_CRYPTO_COMMITMENTS_POLYNOMIAL_OPENINGS_H_\n#define TACHYON_CRYPTO_COMMITMENTS_POLYNOMIAL_OPENINGS_H_\n\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"absl/container/btree_set.h\"\n#include \"absl/strings/substitute.h\"\n#include \"gtest/gtest_prod.h\"\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/openmp_util.h\"\n#include \"tachyon/base/optional.h\"\n#include \"tachyon/base/ref.h\"\n#include \"tachyon/math/polynomials/univariate/lagrange_interpolation.h\"\n\nnamespace tachyon::crypto {\n\n// A single polynomial oracle with a single opening.\n// The type of polynomial oracle can be either |Poly| in |CreateOpeningProof()|\n// and |Commitment| in |VerifyOpeningProof()|.\ntemplate <typename Poly, typename PolyOracle = Poly>\nstruct PolynomialOpening {\n  using Field = typename Poly::Field;\n  using Point = typename Poly::Point;\n\n  // polynomial Pᵢ or commitment Cᵢ\n  base::Ref<const PolyOracle> poly_oracle;\n  // xᵢ\n  Point point;\n  // Pᵢ(xᵢ)\n  Field opening;\n\n  PolynomialOpening() = default;\n  PolynomialOpening(base::Ref<const PolyOracle> poly_oracle, const Point& point,\n                    const Field& opening)\n      : poly_oracle(poly_oracle), point(point), opening(opening) {}\n  PolynomialOpening(base::Ref<const PolyOracle> poly_oracle, Point&& point,\n                    Field&& opening)\n      : poly_oracle(poly_oracle),\n        point(std::move(point)),\n        opening(std::move(opening)) {}\n\n  bool operator==(const PolynomialOpening& other) const {\n    return poly_oracle == other.poly_oracle && point == other.point &&\n           opening == other.opening;\n  }\n  bool operator!=(const PolynomialOpening& other) const {\n    return !operator==(other);\n  }\n\n  std::string ToString() const {\n    if constexpr (std::is_same_v<Poly, PolyOracle>) {\n      return absl::Substitute(\"{poly: $0, point: $1, opening: $2}\",\n                              poly_oracle->ToString(), point->ToString(),\n                              opening.ToString());\n    } else {\n      return absl::Substitute(\"{commitment: $0, point: $1, opening: $2}\",\n                              poly_oracle->ToString(), point->ToString(),\n                              opening.ToString());\n    }\n  }\n\n  std::string ToHexString(bool pad_zero = false) const {\n    if constexpr (std::is_same_v<Poly, PolyOracle>) {\n      return absl::Substitute(\n          \"{poly: $0, point: $1, opening: $2}\", poly_oracle->ToString(),\n          point->ToHexString(pad_zero), opening.ToHexString(pad_zero));\n    } else {\n      return absl::Substitute(\"{commitment: $0, point: $1, opening: $2}\",\n                              poly_oracle->ToHexString(pad_zero),\n                              point->ToHexString(pad_zero),\n                              opening.ToHexString(pad_zero));\n    }\n  }\n};\n\n// A single polynomial oracle with multi openings.\ntemplate <typename Poly, typename PolyOracle = Poly>\nstruct PolynomialOpenings {\n  using Field = typename Poly::Field;\n\n  // polynomial Pᵢ or commitment Cᵢ\n  base::Ref<const PolyOracle> poly_oracle;\n  // [Pᵢ(x₀), Pᵢ(x₁), Pᵢ(x₂)]\n  std::vector<Field> openings;\n\n  PolynomialOpenings() = default;\n  PolynomialOpenings(base::Ref<const PolyOracle> poly_oracle,\n                     std::vector<Field>&& openings)\n      : poly_oracle(poly_oracle), openings(std::move(openings)) {}\n\n  bool operator==(const PolynomialOpenings& other) const {\n    return poly_oracle == other.poly_oracle && openings == other.openings;\n  }\n  bool operator!=(const PolynomialOpenings& other) const {\n    return !operator==(other);\n  }\n};\n\n// Multi polynomial oracles with multi openings grouped by shared points.\ntemplate <typename Poly, typename PolyOracle = Poly>\nstruct GroupedPolynomialOpenings {\n  using Field = typename Poly::Field;\n  using Point = typename Poly::Point;\n\n  // [{P₀, [P₀(x₀), P₀(x₁), P₀(x₂)]}, {P₁, [P₁(x₀), P₁(x₁), P₁(x₂)]}]\n  std::vector<PolynomialOpenings<Poly, PolyOracle>> poly_openings_vec;\n  // [x₀, x₁, x₂]\n  std::vector<Point> points;\n\n  GroupedPolynomialOpenings() = default;\n  GroupedPolynomialOpenings(\n      std::vector<PolynomialOpenings<Poly, PolyOracle>>&& poly_openings_vec,\n      std::vector<Point>&& points)\n      : poly_openings_vec(std::move(poly_openings_vec)),\n        points(std::move(points)) {}\n\n  // Create a low degree extension that is a linear combination with a set of\n  // low degree extensions based on every |poly_openings_vec.openings| and\n  // shared |points|.\n  Poly CreateCombinedLowDegreeExtensions(\n      const Field& r, std::vector<Poly>& low_degree_extensions) const {\n    low_degree_extensions = CreateLowDegreeExtensions();\n    return CombineLowDegreeExtensions(r, low_degree_extensions);\n  }\n\n  bool operator==(const GroupedPolynomialOpenings& other) const {\n    return poly_openings_vec == other.poly_openings_vec &&\n           points == other.points;\n  }\n  bool operator!=(const GroupedPolynomialOpenings& other) const {\n    return !operator==(other);\n  }\n\n private:\n  FRIEND_TEST(PolynomialOpeningsTest, CreateCombinedLowDegreeExtensions);\n\n  // Create a set of low degree extensions based on every\n  // |poly_openings.openings| and shared |points|.\n  std::vector<Poly> CreateLowDegreeExtensions() const {\n    return base::Map(\n        poly_openings_vec,\n        [this](const PolynomialOpenings<Poly>& poly_openings) {\n          Poly low_degree_extension;\n          CHECK(math::LagrangeInterpolate(points, poly_openings.openings,\n                                          &low_degree_extension));\n          return low_degree_extension;\n        });\n  }\n\n  Poly CombineLowDegreeExtensions(\n      const Field& r, const std::vector<Poly>& low_degree_extensions) const {\n    // numerators: [P₀(X) - R₀(X), P₁(X) - R₁(X), P₂(X) - R₂(X)]\n    std::vector<Poly> numerators(low_degree_extensions.size());\n    OMP_PARALLEL_FOR(size_t i = 0; i < low_degree_extensions.size(); ++i) {\n      numerators[i] =\n          *poly_openings_vec[i].poly_oracle - low_degree_extensions[i];\n    }\n\n    // Combine numerator polynomials with powers of |r|.\n    // N(X) = (P₀(X) - R₀(X)) + r(P₁(X) - R₁(X)) + r²(P₂(X) - R₂(X))\n    Poly& n = Poly::template LinearCombinationInPlace</*forward=*/false>(\n        numerators, r);\n\n    // Divide combined polynomial by vanishing polynomial of evaluation points.\n    // H(X) = N(X) / (X - x₀)(X - x₁)(X - x₂)\n    Poly vanishing_poly = Poly::FromRoots(points);\n    return unwrap(n / vanishing_poly);\n  }\n};\n\ntemplate <typename Poly, typename PolyOracle = Poly>\nclass PolynomialOpeningGrouper {\n public:\n  using Field = typename Poly::Field;\n  using Point = typename Poly::Point;\n  using PolyOracleRef = base::Ref<const PolyOracle>;\n\n  const std::vector<GroupedPolynomialOpenings<Poly, PolyOracle>>&\n  grouped_poly_openings_vec() const {\n    return grouped_poly_openings_vec_;\n  }\n  const absl::btree_set<Point>& super_point_set() const {\n    return super_point_set_;\n  }\n\n  // Used by GWC.\n  void GroupBySinglePoint(\n      const std::vector<PolynomialOpening<Poly, PolyOracle>>& poly_openings) {\n    for (const PolynomialOpening<Poly, PolyOracle>& poly_opening :\n         poly_openings) {\n      super_point_set_.insert(poly_opening.point);\n\n      auto it = std::find_if(\n          grouped_poly_openings_vec_.begin(), grouped_poly_openings_vec_.end(),\n          [&poly_opening](const GroupedPolynomialOpenings<Poly, PolyOracle>&\n                              grouped_poly_openings) {\n            return grouped_poly_openings.points[0] == poly_opening.point;\n          });\n\n      PolynomialOpenings<Poly, PolyOracle> poly_openings(\n          poly_opening.poly_oracle, {poly_opening.opening});\n      if (it != grouped_poly_openings_vec_.end()) {\n        it->poly_openings_vec.push_back(std::move(poly_openings));\n      } else {\n        grouped_poly_openings_vec_.push_back(\n            {{std::move(poly_openings)}, {poly_opening.point}});\n      }\n    }\n  }\n\n  // Used by SHPlonk.\n  void GroupByPolyOracleAndPoints(\n      const std::vector<PolynomialOpening<Poly, PolyOracle>>& poly_openings) {\n    // Group |poly_openings| by polynomial.\n    // grouped_poly_oracle_pairs[0]: {P₀, [x₀, x₁, x₂]}\n    // grouped_poly_oracle_pairs[1]: {P₁, [x₀, x₁, x₂]}\n    // grouped_poly_oracle_pairs[2]: {P₂, [x₀, x₁, x₂]}\n    // grouped_poly_oracle_pairs[3]: {P₃, [x₂, x₃]}\n    // grouped_poly_oracle_pairs[4]: {P₄, [x₄]}\n    std::vector<GroupedPolyOraclePair> grouped_poly_oracle_pairs =\n        GroupByPolyOracle(poly_openings);\n\n    // Group |grouped_poly_oracle_pairs| by points.\n    // grouped_point_pairs[0]: {[x₀, x₁, x₂], [P₀, P₁, P₂]}\n    // grouped_point_pairs[1]: {[x₂, x₃], [P₃]}\n    // grouped_point_pairs[2]: {[x₄], [P₄]}\n    std::vector<GroupedPointPair> grouped_point_pairs =\n        GroupByPoints(grouped_poly_oracle_pairs);\n\n    // Construct openings vectors from the |grouped_poly_oracle_pairs|.\n    // Each contains oracles and the corresponding evaluation points.\n    // grouped_poly_openings_vec_[0]: {[P₀, P₁, P₂], [x₀, x₁, x₂]}\n    // grouped_poly_openings_vec_[1]: {[P₃], [x₂, x₃]}\n    // grouped_poly_openings_vec_[2]: {[P₄], [x₄]}\n    CreateMultiPolynomialOpenings(poly_openings, grouped_point_pairs);\n  }\n\n private:\n  FRIEND_TEST(PolynomialOpeningsTest, GroupByPolyOracleAndPoints);\n\n  struct GroupedPolyOraclePair {\n    PolyOracleRef poly_oracle;\n    absl::btree_set<Point> points;\n  };\n\n  std::vector<GroupedPolyOraclePair> GroupByPolyOracle(\n      const std::vector<PolynomialOpening<Poly, PolyOracle>>& poly_openings) {\n    std::vector<GroupedPolyOraclePair> ret;\n    ret.reserve(poly_openings.size());\n    for (const PolynomialOpening<Poly, PolyOracle>& poly_opening :\n         poly_openings) {\n      super_point_set_.insert(poly_opening.point);\n\n      auto it = std::find_if(\n          ret.begin(), ret.end(),\n          [&poly_opening](\n              const GroupedPolyOraclePair& poly_oracle_grouped_pair) {\n            return poly_oracle_grouped_pair.poly_oracle ==\n                   poly_opening.poly_oracle;\n          });\n\n      if (it != ret.end()) {\n        it->points.insert(poly_opening.point);\n      } else {\n        GroupedPolyOraclePair new_pair;\n        new_pair.poly_oracle = poly_opening.poly_oracle;\n        new_pair.points.insert(poly_opening.point);\n        ret.push_back(std::move(new_pair));\n      }\n    }\n    return ret;\n  }\n\n  struct GroupedPointPair {\n    absl::btree_set<Point> points;\n    std::vector<PolyOracleRef> polys;\n  };\n\n  std::vector<GroupedPointPair> GroupByPoints(\n      const std::vector<GroupedPolyOraclePair>& grouped_poly_oracle_pairs) {\n    std::vector<GroupedPointPair> ret;\n    ret.reserve(grouped_poly_oracle_pairs.size());\n    for (const GroupedPolyOraclePair& grouped_poly_oracle_pair :\n         grouped_poly_oracle_pairs) {\n      auto it = std::find_if(ret.begin(), ret.end(),\n                             [&grouped_poly_oracle_pair](\n                                 const GroupedPointPair& point_grouped_pair) {\n                               return point_grouped_pair.points ==\n                                      grouped_poly_oracle_pair.points;\n                             });\n\n      if (it != ret.end()) {\n        it->polys.push_back(grouped_poly_oracle_pair.poly_oracle);\n      } else {\n        GroupedPointPair new_pair;\n        new_pair.points = grouped_poly_oracle_pair.points;\n        new_pair.polys.push_back(grouped_poly_oracle_pair.poly_oracle);\n        ret.push_back(std::move(new_pair));\n      }\n    }\n    return ret;\n  }\n\n  void CreateMultiPolynomialOpenings(\n      const std::vector<PolynomialOpening<Poly, PolyOracle>>& poly_openings,\n      const std::vector<GroupedPointPair>& grouped_point_pairs) {\n    grouped_poly_openings_vec_.reserve(grouped_point_pairs.size());\n\n    for (const GroupedPointPair& grouped_point_pair : grouped_point_pairs) {\n      const std::vector<PolyOracleRef>& polys = grouped_point_pair.polys;\n      std::vector<Point> points(grouped_point_pair.points.begin(),\n                                grouped_point_pair.points.end());\n      std::vector<PolynomialOpenings<Poly, PolyOracle>> poly_openings_vec =\n          base::Map(\n              polys, [&poly_openings, &points](PolyOracleRef poly_oracle) {\n                std::vector<Field> openings = base::Map(\n                    points, [poly_oracle, &poly_openings](const Point& point) {\n                      return GetOpeningFromPolyOpenings(poly_openings,\n                                                        poly_oracle, point);\n                    });\n                return PolynomialOpenings<Poly, PolyOracle>(\n                    poly_oracle, std::move(openings));\n              });\n\n      grouped_poly_openings_vec_.push_back(\n          GroupedPolynomialOpenings<Poly, PolyOracle>(\n              std::move(poly_openings_vec), std::move(points)));\n    }\n  }\n\n  static Field GetOpeningFromPolyOpenings(\n      const std::vector<PolynomialOpening<Poly, PolyOracle>>& poly_openings,\n      PolyOracleRef poly_oracle, const Point& point) {\n    auto it = std::find_if(\n        poly_openings.begin(), poly_openings.end(),\n        [poly_oracle,\n         point](const PolynomialOpening<Poly, PolyOracle>& poly_opening) {\n          return poly_opening.poly_oracle == poly_oracle &&\n                 poly_opening.point == point;\n        });\n    CHECK(it != poly_openings.end());\n    return it->opening;\n  }\n\n  // |grouped_poly_openings_vec_| is a list of\n  // |GroupedPolynomialOpenings|, which is the result of list of\n  // |PolynomialOpening| grouped by poly_oracle and points.\n  // {[P₀, P₁, P₂], [x₀, x₁, x₂]}\n  // {[P₃], [x₂, x₃]}\n  // {[P₄], [x₄]}\n  std::vector<GroupedPolynomialOpenings<Poly, PolyOracle>>\n      grouped_poly_openings_vec_;\n  // |super_point_set_| is all the points that appear in opening.\n  // [x₀, x₁, x₂, x₃, x₄]\n  absl::btree_set<Point> super_point_set_;\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_COMMITMENTS_POLYNOMIAL_OPENINGS_H_\n"
  },
  {
    "path": "tachyon/crypto/commitments/polynomial_openings_unittest.cc",
    "content": "#include \"tachyon/crypto/commitments/polynomial_openings.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/containers/contains.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_polynomial.h\"\n\nnamespace tachyon::crypto {\n\nusing namespace math;\n\nnamespace {\n\nconst size_t kMaxDegree = 4;\n\nusing Point = GF7;\nusing Poly = UnivariateDensePolynomial<GF7, kMaxDegree>;\nusing Coeffs = UnivariateDenseCoefficients<GF7, kMaxDegree>;\nusing PolyRef = base::Ref<const Poly>;\n\nclass PolynomialOpeningsTest : public FiniteFieldTest<GF7> {\n public:\n  void SetUp() override {\n    // NOTE(Insun35): For testing, I added the same polynomial to |polys_[2]|\n    // and |polys_[3]| on purpose. Even if they have the same value, they should\n    // be grouped separately because they are ShallowRef in\n    // |PolynomialOpenings|.\n    polys_.push_back(Poly(Coeffs({GF7(3), GF7(6), GF7(4), GF7(6), GF7(6)})));\n    polys_.push_back(Poly(Coeffs({GF7(3), GF7(4), GF7(5), GF7(0), GF7(2)})));\n    polys_.push_back(Poly(Coeffs({GF7(1), GF7(5), GF7(3), GF7(6), GF7(6)})));\n    polys_.push_back(Poly(Coeffs({GF7(1), GF7(5), GF7(3), GF7(6), GF7(6)})));\n    points_.push_back(GF7(1));\n    points_.push_back(GF7(2));\n    points_.push_back(GF7(3));\n  }\n\n protected:\n  std::vector<Poly> polys_;\n  std::vector<Point> points_;\n};\n\n}  // namespace\n\nTEST_F(PolynomialOpeningsTest, CreateCombinedLowDegreeExtensions) {\n  std::vector<PolynomialOpenings<Poly>> poly_openings_vec = {\n      {PolyRef(&polys_[0]),\n       {polys_[0].Evaluate(points_[0]), polys_[0].Evaluate(points_[1])}},\n      {PolyRef(&polys_[1]),\n       {polys_[1].Evaluate(points_[0]), polys_[1].Evaluate(points_[1])}},\n  };\n  std::vector<Point> points = {points_[0], points_[1]};\n  std::vector<Point> points_copy = points;\n  GroupedPolynomialOpenings<Poly> grouped_poly_opening(\n      std::move(poly_openings_vec), std::move(points_copy));\n\n  // NOTE(chokobole): Check whether the manually created low degree extensions\n  // are constructed correctly.\n  std::vector<Poly> low_degree_extensions =\n      grouped_poly_opening.CreateLowDegreeExtensions();\n  for (size_t i = 0; i < low_degree_extensions.size(); ++i) {\n    const Poly& low_degree_extension = low_degree_extensions[i];\n    const PolynomialOpenings<Poly>& poly_openings =\n        grouped_poly_opening.poly_openings_vec[i];\n    for (size_t j = 0; j < poly_openings.openings.size(); ++j) {\n      EXPECT_EQ(low_degree_extension.Evaluate(grouped_poly_opening.points[j]),\n                poly_openings.openings[j]);\n    }\n  }\n\n  // NOTE(chokobole): Check whether the manually combined low degree extension\n  // and returned ones are same.\n  GF7 r = GF7::Random();\n  Poly combined_low_degree_extension =\n      grouped_poly_opening.CombineLowDegreeExtensions(r, low_degree_extensions);\n  std::vector<Poly> actual_low_degree_extensions;\n  EXPECT_EQ(combined_low_degree_extension,\n            grouped_poly_opening.CreateCombinedLowDegreeExtensions(\n                r, actual_low_degree_extensions));\n  EXPECT_EQ(actual_low_degree_extensions, low_degree_extensions);\n\n  // NOTE(chokobole): Check whether the evaluations are same.\n  Point x = Point::Random();\n  while (base::Contains(points, x)) {\n    x = Point::Random();\n  }\n  GF7 actual_eval = combined_low_degree_extension.Evaluate(x);\n  GF7 expected_eval =\n      grouped_poly_opening.poly_openings_vec[0].poly_oracle->Evaluate(x) -\n      low_degree_extensions[0].Evaluate(x);\n  GF7 power = r;\n  for (size_t i = 1; i < low_degree_extensions.size(); ++i) {\n    expected_eval +=\n        (grouped_poly_opening.poly_openings_vec[i].poly_oracle->Evaluate(x) -\n         low_degree_extensions[i].Evaluate(x)) *\n        power;\n    power *= r;\n  }\n  for (const Point& point : points) {\n    if (UNLIKELY((x - point).IsZero())) {\n      ASSERT_FALSE(expected_eval /= (x - point));\n    } else {\n      ASSERT_TRUE(expected_eval /= (x - point));\n    }\n  }\n  EXPECT_EQ(actual_eval, expected_eval);\n}\n\n#define OPENING(poly, point) PolyRef(&poly), point, poly.Evaluate(point)\n\nTEST_F(PolynomialOpeningsTest, GroupBySinglePoint) {\n  std::vector<PolynomialOpening<Poly>> poly_openings;\n  poly_openings.emplace_back(OPENING(polys_[0], points_[0]));\n  poly_openings.emplace_back(OPENING(polys_[0], points_[1]));\n  poly_openings.emplace_back(OPENING(polys_[1], points_[0]));\n  poly_openings.emplace_back(OPENING(polys_[1], points_[1]));\n  poly_openings.emplace_back(OPENING(polys_[2], points_[2]));\n  poly_openings.emplace_back(OPENING(polys_[3], points_[2]));\n  PolynomialOpeningGrouper<Poly> grouper;\n  grouper.GroupBySinglePoint(poly_openings);\n\n  absl::btree_set<Point> expected_points = {\n      points_[0],\n      points_[1],\n      points_[2],\n  };\n  EXPECT_EQ(grouper.super_point_set(), expected_points);\n\n  const std::vector<GroupedPolynomialOpenings<Poly>>&\n      grouped_poly_openings_vec = grouper.grouped_poly_openings_vec();\n  ASSERT_EQ(grouped_poly_openings_vec.size(), 3);\n  std::vector<Point> expected_points_vec = {points_[0]};\n  std::vector<Point> expected_points_vec2 = {points_[1]};\n  std::vector<Point> expected_points_vec3 = {points_[2]};\n  std::vector<PolynomialOpenings<Poly>> expected_polys_vec = {\n      PolynomialOpenings<Poly>(PolyRef(&polys_[0]), {poly_openings[0].opening}),\n      PolynomialOpenings<Poly>(PolyRef(&polys_[1]), {poly_openings[2].opening}),\n  };\n  std::vector<PolynomialOpenings<Poly>> expected_polys_vec2 = {\n      PolynomialOpenings<Poly>(PolyRef(&polys_[0]), {poly_openings[1].opening}),\n      PolynomialOpenings<Poly>(PolyRef(&polys_[1]), {poly_openings[3].opening}),\n  };\n  std::vector<PolynomialOpenings<Poly>> expected_polys_vec3 = {\n      PolynomialOpenings<Poly>(PolyRef(&polys_[2]), {poly_openings[4].opening}),\n      PolynomialOpenings<Poly>(PolyRef(&polys_[3]), {poly_openings[5].opening}),\n  };\n\n  EXPECT_EQ(grouped_poly_openings_vec[0].points, expected_points_vec);\n  EXPECT_EQ(grouped_poly_openings_vec[1].points, expected_points_vec2);\n  EXPECT_EQ(grouped_poly_openings_vec[2].points, expected_points_vec3);\n  EXPECT_EQ(grouped_poly_openings_vec[0].poly_openings_vec, expected_polys_vec);\n  EXPECT_EQ(grouped_poly_openings_vec[1].poly_openings_vec,\n            expected_polys_vec2);\n  EXPECT_EQ(grouped_poly_openings_vec[2].poly_openings_vec,\n            expected_polys_vec3);\n}\n\nTEST_F(PolynomialOpeningsTest, GroupByPolyOracleAndPoints) {\n  using GroupedPolyOraclePair =\n      PolynomialOpeningGrouper<Poly>::GroupedPolyOraclePair;\n  using GroupedPointPair = PolynomialOpeningGrouper<Poly>::GroupedPointPair;\n\n  std::vector<PolynomialOpening<Poly>> poly_openings;\n  poly_openings.emplace_back(OPENING(polys_[0], points_[0]));\n  poly_openings.emplace_back(OPENING(polys_[0], points_[1]));\n  poly_openings.emplace_back(OPENING(polys_[1], points_[0]));\n  poly_openings.emplace_back(OPENING(polys_[1], points_[1]));\n  poly_openings.emplace_back(OPENING(polys_[2], points_[2]));\n  poly_openings.emplace_back(OPENING(polys_[3], points_[2]));\n  PolynomialOpeningGrouper<Poly> grouper;\n  std::vector<GroupedPolyOraclePair> poly_openings_grouped_by_poly =\n      grouper.GroupByPolyOracle(poly_openings);\n\n  // NOTE(Insun35): Although |polys_[2]| and |polys_[3]| have the same value,\n  // they should be grouped separately because they are ShallowRef in\n  // |PolynomialOpenings|.\n  EXPECT_EQ(poly_openings_grouped_by_poly.size(), 4);\n\n  for (const auto& poly_oracle_grouped_pair : poly_openings_grouped_by_poly) {\n    absl::btree_set<Point> expected_points;\n    if (poly_oracle_grouped_pair.poly_oracle == PolyRef(&polys_[0]) ||\n        poly_oracle_grouped_pair.poly_oracle == PolyRef(&polys_[1])) {\n      expected_points = {points_[0], points_[1]};\n    } else {\n      expected_points = {points_[2]};\n    }\n    EXPECT_EQ(poly_oracle_grouped_pair.points, expected_points);\n  }\n\n  absl::btree_set<Point> expected_points = {\n      points_[0],\n      points_[1],\n      points_[2],\n  };\n  EXPECT_EQ(grouper.super_point_set(), expected_points);\n\n  std::vector<GroupedPointPair> poly_openings_grouped_by_poly_and_points =\n      grouper.GroupByPoints(poly_openings_grouped_by_poly);\n  for (const auto& point_grouped_pair :\n       poly_openings_grouped_by_poly_and_points) {\n    std::vector<PolyRef> expected_polys;\n    if (point_grouped_pair.points ==\n        absl::btree_set<Point>{points_[0], points_[1]}) {\n      expected_polys = {\n          PolyRef(&polys_[0]),\n          PolyRef(&polys_[1]),\n      };\n    } else {\n      expected_polys = {\n          PolyRef(&polys_[2]),\n          PolyRef(&polys_[3]),\n      };\n    }\n    EXPECT_EQ(point_grouped_pair.polys, expected_polys);\n  }\n\n  grouper.CreateMultiPolynomialOpenings(\n      poly_openings, poly_openings_grouped_by_poly_and_points);\n  const std::vector<GroupedPolynomialOpenings<Poly>>&\n      grouped_poly_openings_vec = grouper.grouped_poly_openings_vec();\n  ASSERT_EQ(grouped_poly_openings_vec.size(), 2);\n  std::vector<Point> expected_points_vec = {points_[0], points_[1]};\n  std::vector<Point> expected_points_vec2 = {points_[2]};\n  std::vector<PolynomialOpenings<Poly>> expected_polys_vec = {\n      PolynomialOpenings<Poly>(PolyRef(&polys_[0]), {poly_openings[0].opening,\n                                                     poly_openings[1].opening}),\n      PolynomialOpenings<Poly>(PolyRef(&polys_[1]), {poly_openings[2].opening,\n                                                     poly_openings[3].opening}),\n  };\n  std::vector<PolynomialOpenings<Poly>> expected_polys_vec2 = {\n      PolynomialOpenings<Poly>(PolyRef(&polys_[2]), {poly_openings[4].opening}),\n      PolynomialOpenings<Poly>(PolyRef(&polys_[3]), {poly_openings[5].opening}),\n  };\n\n  EXPECT_EQ(grouped_poly_openings_vec[0].points, expected_points_vec);\n  EXPECT_EQ(grouped_poly_openings_vec[0].poly_openings_vec, expected_polys_vec);\n  EXPECT_EQ(grouped_poly_openings_vec[1].points, expected_points_vec2);\n  EXPECT_EQ(grouped_poly_openings_vec[1].poly_openings_vec,\n            expected_polys_vec2);\n}\n\n#undef OPENING\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/crypto/commitments/test/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"bn254_kzg_polynomial_openings\",\n    hdrs = [\"bn254_kzg_polynomial_openings.h\"],\n    data = [\"bn254_kzg_polynomial_openings.json\"],\n    deps = [\n        \"//tachyon/base:ref\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/base/json\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/crypto/commitments/test/bn254_kzg_polynomial_openings.h",
    "content": "#ifndef TACHYON_CRYPTO_COMMITMENTS_TEST_BN254_KZG_POLYNOMIAL_OPENINGS_H_\n#define TACHYON_CRYPTO_COMMITMENTS_TEST_BN254_KZG_POLYNOMIAL_OPENINGS_H_\n\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/json/json.h\"\n#include \"tachyon/base/ref.h\"\n\nnamespace tachyon {\nnamespace crypto {\n\ntemplate <typename Poly, typename Commitment>\nstruct OwnedPolynomialOpening {\n  using Point = typename Poly::Point;\n  using Field = typename Poly::Field;\n\n  Poly poly;\n  Commitment commitment;\n  Point point;\n  Field opening;\n};\n\ntemplate <typename Poly, typename Commitment>\nstruct OwnedPolynomialOpenings {\n  using Point = typename Poly::Point;\n\n  std::vector<OwnedPolynomialOpening<Poly, Commitment>> prover_openings;\n\n  template <typename PCS>\n  void Validate(const PCS& pcs) const {\n    for (const OwnedPolynomialOpening<Poly, Commitment>& owned_opening :\n         prover_openings) {\n      CHECK_EQ(owned_opening.poly.Evaluate(owned_opening.point),\n               owned_opening.opening);\n      Commitment commitment;\n      CHECK(pcs.Commit(owned_opening.poly, &commitment));\n      CHECK_EQ(commitment, owned_opening.commitment);\n    }\n  }\n\n  std::vector<PolynomialOpening<Poly>> CreateProverOpenings() const {\n    return base::Map(\n        prover_openings,\n        [](const OwnedPolynomialOpening<Poly, Commitment>& owned_opening) {\n          return PolynomialOpening<Poly>(\n              base::Ref<const Poly>(&owned_opening.poly), owned_opening.point,\n              owned_opening.opening);\n        });\n  }\n\n  std::vector<PolynomialOpening<Poly, Commitment>> CreateVerifierOpenings()\n      const {\n    return base::Map(\n        prover_openings,\n        [](const OwnedPolynomialOpening<Poly, Commitment>& owned_opening) {\n          return PolynomialOpening<Poly, Commitment>(\n              base::Ref<const Commitment>(&owned_opening.commitment),\n              owned_opening.point, owned_opening.opening);\n        });\n  }\n};\n\n}  // namespace crypto\n\nnamespace base {\n\ntemplate <typename Poly, typename Commitment>\nclass RapidJsonValueConverter<\n    crypto::OwnedPolynomialOpening<Poly, Commitment>> {\n public:\n  using Point = typename Poly::Point;\n  using Field = typename Poly::Field;\n\n  template <typename Allocator>\n  static rapidjson::Value From(\n      const crypto::OwnedPolynomialOpening<Poly, Commitment>& value,\n      Allocator& allocator) {\n    rapidjson::Value object(rapidjson::kObjectType);\n    AddJsonElement(object, \"poly\", value.poly, allocator);\n    AddJsonElement(object, \"point\", value.point, allocator);\n    AddJsonElement(object, \"opening\", value.opening, allocator);\n    AddJsonElement(object, \"commitment\", value.commitment, allocator);\n    return object;\n  }\n\n  static bool To(const rapidjson::Value& json_value, std::string_view key,\n                 crypto::OwnedPolynomialOpening<Poly, Commitment>* value,\n                 std::string* error) {\n    Poly poly;\n    Commitment commitment;\n    Point point;\n    Field opening;\n    if (!ParseJsonElement(json_value, \"poly\", &poly, error)) return false;\n    if (!ParseJsonElement(json_value, \"point\", &point, error)) return false;\n    if (!ParseJsonElement(json_value, \"opening\", &opening, error)) return false;\n    if (!ParseJsonElement(json_value, \"commitment\", &commitment, error))\n      return false;\n    value->poly = std::move(poly);\n    value->commitment = std::move(commitment);\n    value->point = std::move(point);\n    value->opening = std::move(opening);\n    return true;\n  }\n};\n\ntemplate <typename Poly, typename Commitment>\nclass RapidJsonValueConverter<\n    crypto::OwnedPolynomialOpenings<Poly, Commitment>> {\n public:\n  template <typename Allocator>\n  static rapidjson::Value From(\n      const crypto::OwnedPolynomialOpenings<Poly, Commitment>& value,\n      Allocator& allocator) {\n    rapidjson::Value object(rapidjson::kObjectType);\n    AddJsonElement(object, \"prover_openings\", value.prover_openings, allocator);\n    return object;\n  }\n\n  static bool To(const rapidjson::Value& json_value, std::string_view key,\n                 crypto::OwnedPolynomialOpenings<Poly, Commitment>* value,\n                 std::string* error) {\n    std::vector<crypto::OwnedPolynomialOpening<Poly, Commitment>>\n        prover_openings;\n    if (!ParseJsonElement(json_value, \"prover_openings\", &prover_openings,\n                          error))\n      return false;\n    value->prover_openings = std::move(prover_openings);\n    return true;\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_CRYPTO_COMMITMENTS_TEST_BN254_KZG_POLYNOMIAL_OPENINGS_H_\n"
  },
  {
    "path": "tachyon/crypto/commitments/test/bn254_kzg_polynomial_openings.json",
    "content": "{\n  \"prover_openings\": [\n    {\n      \"poly\": {\n        \"coefficients\": {\n          \"coefficients\": [\n            \"0x2a57c4a4850b6c2481463cffb1512d51832d6b3f6a82427f1b65b6e172000015\",\n            \"0x12d62582906462c130f4ea4b800a85e9eb4f7f466d5bfa89ea8bf20deb857e75\",\n            \"0x24d3621acf7ac08f3ff2b1536911a5f55c73faa6ac631dc11419dde5d02fa4c3\",\n            \"0x266f277a8898dcbac4b1619077fd15d7cbea2cab246f1f963405b98fcc267aa1\",\n            \"0x060c89ce5c2634052bcdbb3cfc1599961851e0d3f772cdc7d66f3fa047f70371\",\n            \"0x156c4fe519d5f5230fbb7446f480a0625386582f64a5a96b70ce67ebeda2e08a\",\n            \"0x223a74e2b7a94291a2b3184a19ef58b1df6171d7daec0bf0b1d81cc3b5729d8e\",\n            \"0x2ba1af62d8a5586c97e3b9a88ae7c1b5b62381468bc2a5b77ca221dab0353b7b\",\n            \"0x0c19139cb84c680a6e14116da06056174a0cfa121e6e5c2450f87d64fc000012\",\n            \"0x0f22af6b65f8136a0f5512232c8b93ec26b2d0fc013663a9943ea7d915d45ed0\",\n            \"0x05846289b590ab92b5a9fe123a3a92ba45ce0c915ba153e7705a0edb63c71fdd\",\n            \"0x0975b22f115b3aa88ffb637fcf46ae05a5851de94b98412a2332ad75e4aa12f1\",\n            \"0x244b3ad628e5381f557881c2b53b93bb6adb8a6b730f74b744f677412a08fcab\",\n            \"0x29637812b230d5062e425e23446999871bcdcd195aa3e07b4962d30a01daf0a5\",\n            \"0x1436635e85ae919fd850c3bd45c71f58cec45781108263895177e1a2f6969e12\",\n            \"0x054213d94fc9d0827668c9484e598821f7e65fbbbd3fd3b302b178926e228806\"\n          ]\n        }\n      },\n      \"point\": \"0x1878ab885c4eeb02752b8e5e37413b6ac4095ece8af445a9f105912aba6091a6\",\n      \"opening\": \"0x0fa13ccbca58eaacf3e0137cbaa3f09e0de199864589ed3b5d52513eb59012bb\",\n      \"commitment\": {\n        \"x\": \"0xd4c8829e6feb9ab9606917e970b944717cc4d9a74695dd4e7cc23b5888b6dce\",\n        \"y\": \"0x3a99ef4660a95515763e072043119fcbf6d3f3b709af6bf05b5c8b4d815a775\"\n      }\n    },\n    {\n      \"poly\": {\n        \"coefficients\": {\n          \"coefficients\": [\n            \"0x060c89ce5c263405370a08b6d0302b0ba5067d090f372e12287c3eb27e000003\",\n            \"0x3028c9344e30580ea8438e904895dd3aca99e1c6df3b1350af25b1d4ade4a57a\",\n            \"0x2a517e34acc241a2d74e670c1c87a3c89fb9647a6c280716ec20a7bd46cce6d5\",\n            \"0x1b368ad453cf5e33080841d7bae0a9d361d2e68b5064b035c0399f94e5b0c709\",\n            \"0x12259d6b14729c0e271bcff645092f863f16b80e0590c05793badbac4cce7331\",\n            \"0x0e2e8378d846e58ea5bf2f5d9d2b15ec6e089dbc279e7e73b8720ec69ca8daf3\",\n            \"0x2a45aa0d3f0d2460e04bcbf2a284939a713c0b7a196c7bf3cd85e41c023d22e8\",\n            \"0x29b017d8e7a9669ad6361e0a527678af4ba104241b73f98af9dce2a9f6f8d7eb\",\n            \"0x2a57c4a4850b6c2481463cffb1512d51832d6b3f6a82427f1b65b6e171fffffe\",\n            \"0x003b853e9301481b100cb72638eb7b225d9a06819a7e5d4094bc43bf421b5a87\",\n            \"0x0612d03e346f5e86e101deaa64f9b494887a83ce0d91697a57c14dd6a933192c\",\n            \"0x152dc39e8d6241f6b04803dec6a0ae89c66101bd2954c05b83a855ff0a4f38f8\",\n            \"0x1e3eb107ccbf041b913475c03c7828d6e91d303a7428b039b02719e7a3318cd0\",\n            \"0x2235cafa08eaba9b12911658e4564270ba2b4a8c521af21d8b6fe6cd5357250e\",\n            \"0x061ea465a2247bc8d80479c3defcc4c2b6f7dcce604cf49d765c1177edc2dd19\",\n            \"0x06b43699f988398ee21a27ac2f0adfaddc92e4245e4577064a0512e9f9072816\"\n          ]\n        }\n      },\n      \"point\": \"0x1878ab885c4eeb02752b8e5e37413b6ac4095ece8af445a9f105912aba6091a6\",\n      \"opening\": \"0x1ca0efb9353dd426b7c7a7d4e5147967371203bd0076145a34c71b9007d15fca\",\n      \"commitment\": {\n        \"x\": \"0xe80af0f2dac74d170c3995fd07ffd5577918b748fe4ba0843fd2386ce9ae384\",\n        \"y\": \"0x58b31b773e7a0e22f1ef9d6bbcc154b3dfaec09ff6c78084c1ae5c150f6624d\"\n      }\n    },\n    {\n      \"poly\": {\n        \"coefficients\": {\n          \"coefficients\": [\n            \"0x2a57c4a4850b6c2481463cffb1512d51832d6b3f6a82427f1b65b6e172000015\",\n            \"0x12d62582906462c130f4ea4b800a85e9eb4f7f466d5bfa89ea8bf20deb857e75\",\n            \"0x24d3621acf7ac08f3ff2b1536911a5f55c73faa6ac631dc11419dde5d02fa4c3\",\n            \"0x266f277a8898dcbac4b1619077fd15d7cbea2cab246f1f963405b98fcc267aa1\",\n            \"0x060c89ce5c2634052bcdbb3cfc1599961851e0d3f772cdc7d66f3fa047f70371\",\n            \"0x156c4fe519d5f5230fbb7446f480a0625386582f64a5a96b70ce67ebeda2e08a\",\n            \"0x223a74e2b7a94291a2b3184a19ef58b1df6171d7daec0bf0b1d81cc3b5729d8e\",\n            \"0x2ba1af62d8a5586c97e3b9a88ae7c1b5b62381468bc2a5b77ca221dab0353b7b\",\n            \"0x0c19139cb84c680a6e14116da06056174a0cfa121e6e5c2450f87d64fc000012\",\n            \"0x0f22af6b65f8136a0f5512232c8b93ec26b2d0fc013663a9943ea7d915d45ed0\",\n            \"0x05846289b590ab92b5a9fe123a3a92ba45ce0c915ba153e7705a0edb63c71fdd\",\n            \"0x0975b22f115b3aa88ffb637fcf46ae05a5851de94b98412a2332ad75e4aa12f1\",\n            \"0x244b3ad628e5381f557881c2b53b93bb6adb8a6b730f74b744f677412a08fcab\",\n            \"0x29637812b230d5062e425e23446999871bcdcd195aa3e07b4962d30a01daf0a5\",\n            \"0x1436635e85ae919fd850c3bd45c71f58cec45781108263895177e1a2f6969e12\",\n            \"0x054213d94fc9d0827668c9484e598821f7e65fbbbd3fd3b302b178926e228806\"\n          ]\n        }\n      },\n      \"point\": \"0x1feddb41613d5151f64a23db20a3f4479d3645f9d4e7ea92ff3b7798e63359da\",\n      \"opening\": \"0x01df83543b4be0f49ba822a8067a75444a113d2cda81e8e541109ba09c6d0214\",\n      \"commitment\": {\n        \"x\": \"0xd4c8829e6feb9ab9606917e970b944717cc4d9a74695dd4e7cc23b5888b6dce\",\n        \"y\": \"0x3a99ef4660a95515763e072043119fcbf6d3f3b709af6bf05b5c8b4d815a775\"\n      }\n    },\n    {\n      \"poly\": {\n        \"coefficients\": {\n          \"coefficients\": [\n            \"0x2c17c0f5f77b48b6f8bb69a7cceac8da32489a140bb6ae30550fea98dcca2729\",\n            \"0x2482e87fb833258b7d6faf7180dac78821d421cb82737c2ca9c85544014bce0d\",\n            \"0x1cf5deb215f48e7edd15d1d845fd18c91ba0dda114d82471b96db592c501a135\",\n            \"0x19423f3a7cc9227f9e58cc469e2f2955d3226b29b7d61f4c99defb0755db6f27\",\n            \"0x141aec4bfe36cc59490b8eb64802803edfbfae97926a2afaa496ae6b96324d77\",\n            \"0x1e72408aadda81ddcd62a0b8ea8520f3d4951ba76e54590e7e647282126dc4de\",\n            \"0x132a255ed346ac9ba404d2061800dcb85146a33455191698b249d2412656695e\",\n            \"0x2ccbc43312750ff857bb20a53bb64dcd6eeb14f53b65313ae1d0feee0675d890\",\n            \"0x1616c186407bb6a2d4ce1647f92b42852c0f917cc185d76777e3b40b5e5bbae3\",\n            \"0x05ba6117e26437f6619d1b925f6dfc54226ad706ad734bc779dbfe3f9bd44e58\",\n            \"0x190710278d14025ccee750f5bf641d17ee04e59f6b3953e727be90897dc3eeab\",\n            \"0x0637fc3a4e5dcfa006a90c08321c77605239cec29b20648153a62a3cd7a2d83a\",\n            \"0x2dd226c45e7f23db94e0297daba673c35d005a41ce53596382c763a811b7e18b\",\n            \"0x1d45324a309410209f1ce9db95cf0b06cce476aca282c08b8a8324d6e3f9a4de\",\n            \"0x0ee0a9cc83d1650822f0e3f02350d71273a3b713e12d684dbb7df68224fe2575\",\n            \"0x2528b264054f1dd1142113f6267b53de862afe9194195d4f23c9d38d375a2a37\"\n          ]\n        }\n      },\n      \"point\": \"0x1878ab885c4eeb02752b8e5e37413b6ac4095ece8af445a9f105912aba6091a6\",\n      \"opening\": \"0x1a1ba62baa824efaa4befc327a860d549b0f21345dee287d700942d3802f2777\",\n      \"commitment\": {\n        \"x\": \"0x16ad53c6cb565e23a435f06849c1fca80d7dd87de0c25d2a37012543b01bc77\",\n        \"y\": \"0xfdcfdd9560c81de5141f9b51b95d3639e2a9eabb093eda4ed5934e24c3775e4\"\n      }\n    },\n    {\n      \"poly\": {\n        \"coefficients\": {\n          \"coefficients\": [\n            \"0x2c17c0f5f77b48b6f8bb69a7cceac8da32489a140bb6ae30550fea98dcca2729\",\n            \"0x2482e87fb833258b7d6faf7180dac78821d421cb82737c2ca9c85544014bce0d\",\n            \"0x1cf5deb215f48e7edd15d1d845fd18c91ba0dda114d82471b96db592c501a135\",\n            \"0x19423f3a7cc9227f9e58cc469e2f2955d3226b29b7d61f4c99defb0755db6f27\",\n            \"0x141aec4bfe36cc59490b8eb64802803edfbfae97926a2afaa496ae6b96324d77\",\n            \"0x1e72408aadda81ddcd62a0b8ea8520f3d4951ba76e54590e7e647282126dc4de\",\n            \"0x132a255ed346ac9ba404d2061800dcb85146a33455191698b249d2412656695e\",\n            \"0x2ccbc43312750ff857bb20a53bb64dcd6eeb14f53b65313ae1d0feee0675d890\",\n            \"0x1616c186407bb6a2d4ce1647f92b42852c0f917cc185d76777e3b40b5e5bbae3\",\n            \"0x05ba6117e26437f6619d1b925f6dfc54226ad706ad734bc779dbfe3f9bd44e58\",\n            \"0x190710278d14025ccee750f5bf641d17ee04e59f6b3953e727be90897dc3eeab\",\n            \"0x0637fc3a4e5dcfa006a90c08321c77605239cec29b20648153a62a3cd7a2d83a\",\n            \"0x2dd226c45e7f23db94e0297daba673c35d005a41ce53596382c763a811b7e18b\",\n            \"0x1d45324a309410209f1ce9db95cf0b06cce476aca282c08b8a8324d6e3f9a4de\",\n            \"0x0ee0a9cc83d1650822f0e3f02350d71273a3b713e12d684dbb7df68224fe2575\",\n            \"0x2528b264054f1dd1142113f6267b53de862afe9194195d4f23c9d38d375a2a37\"\n          ]\n        }\n      },\n      \"point\": \"0x1feddb41613d5151f64a23db20a3f4479d3645f9d4e7ea92ff3b7798e63359da\",\n      \"opening\": \"0x190fbeaa37659963b02ba989785b4a104ad2fffa6acdc7fed55a04d374b4039b\",\n      \"commitment\": {\n        \"x\": \"0x16ad53c6cb565e23a435f06849c1fca80d7dd87de0c25d2a37012543b01bc77\",\n        \"y\": \"0xfdcfdd9560c81de5141f9b51b95d3639e2a9eabb093eda4ed5934e24c3775e4\"\n      }\n    },\n    {\n      \"poly\": {\n        \"coefficients\": {\n          \"coefficients\": [\n            \"0x1b26aab418970af02bb4af272fa0f9103e0cb0e470f9fadcd375129953e79cba\",\n            \"0x192ca548746879a2863b41d25a72cfc763927b9d3d583b68479c2288186f03bd\",\n            \"0x2717b21eb04ef5ca7374496840db2a3606fb643edcefa5cf63c6f2d58bf4fd8e\",\n            \"0x1f24d818f62ea18fb0b7c7c9fa8088f721f7f2d0707a94602a61aa93fcb4123d\",\n            \"0x12d5f438cf33400c2d5fda136e0d3401ae591d1c66c2af6adc95ed5db8f7f8da\",\n            \"0x05bd91f33bcfa11b1c99e42d0633c450726b52de7f0fff9e09ac8b58eb50d808\",\n            \"0x1ebe483c4a8bed2c78b7f8f2505672822ffb2b7895d73d582d37ceb1f3f2173e\",\n            \"0x007fc35853678a45f868ed4842c6a75edf1e65835eac914814c64548f361aa45\",\n            \"0x13a443a76de74dc5daea5da806164fdd8ba1a081559d3e59da45710bfc71c8b1\",\n            \"0x2ac155a1ca4ef56592ba2d7d627f54be553e944622e48164d26a15acecad32d2\",\n            \"0x016878006aa738f94a09b59a928ef7d9d3982ea95d4b51e46a22752e11c58a5c\",\n            \"0x184d567b0a6eb2dc34fa5c71e956a5153e579570f841cd31620c455e58477a62\",\n            \"0x2e7405b321d54798e75f654199090a08ef72689729eaae6eef48dd1a82e94392\",\n            \"0x13ce04db3756a33530548baab13824336b5a5415222f8b3547440a05fe76fbb9\",\n            \"0x2484a997019dcbbb597364e56bd57ff4576abe166671c10956dde4cfe0c1f4c8\",\n            \"0x169ed0d157e029e19579426f0c02fbca0ba4c370f83cca396ae61d28bc69e885\"\n          ]\n        }\n      },\n      \"point\": \"0x1878ab885c4eeb02752b8e5e37413b6ac4095ece8af445a9f105912aba6091a6\",\n      \"opening\": \"0x2af01d0655e6a2b4495ab0eb0901d601c410de4c6c43dc958a59c408655e5609\",\n      \"commitment\": {\n        \"x\": \"0x477698b841d60ab1dfa466a9ccde627778106ae7ccfcde75247b2a256f0b740\",\n        \"y\": \"0xa5a2dc0da5660baf57695de3da9c489f927b6ad08b63798bb596441dd794830\"\n      }\n    },\n    {\n      \"poly\": {\n        \"coefficients\": {\n          \"coefficients\": [\n            \"0x1b26aab418970af02bb4af272fa0f9103e0cb0e470f9fadcd375129953e79cba\",\n            \"0x192ca548746879a2863b41d25a72cfc763927b9d3d583b68479c2288186f03bd\",\n            \"0x2717b21eb04ef5ca7374496840db2a3606fb643edcefa5cf63c6f2d58bf4fd8e\",\n            \"0x1f24d818f62ea18fb0b7c7c9fa8088f721f7f2d0707a94602a61aa93fcb4123d\",\n            \"0x12d5f438cf33400c2d5fda136e0d3401ae591d1c66c2af6adc95ed5db8f7f8da\",\n            \"0x05bd91f33bcfa11b1c99e42d0633c450726b52de7f0fff9e09ac8b58eb50d808\",\n            \"0x1ebe483c4a8bed2c78b7f8f2505672822ffb2b7895d73d582d37ceb1f3f2173e\",\n            \"0x007fc35853678a45f868ed4842c6a75edf1e65835eac914814c64548f361aa45\",\n            \"0x13a443a76de74dc5daea5da806164fdd8ba1a081559d3e59da45710bfc71c8b1\",\n            \"0x2ac155a1ca4ef56592ba2d7d627f54be553e944622e48164d26a15acecad32d2\",\n            \"0x016878006aa738f94a09b59a928ef7d9d3982ea95d4b51e46a22752e11c58a5c\",\n            \"0x184d567b0a6eb2dc34fa5c71e956a5153e579570f841cd31620c455e58477a62\",\n            \"0x2e7405b321d54798e75f654199090a08ef72689729eaae6eef48dd1a82e94392\",\n            \"0x13ce04db3756a33530548baab13824336b5a5415222f8b3547440a05fe76fbb9\",\n            \"0x2484a997019dcbbb597364e56bd57ff4576abe166671c10956dde4cfe0c1f4c8\",\n            \"0x169ed0d157e029e19579426f0c02fbca0ba4c370f83cca396ae61d28bc69e885\"\n          ]\n        }\n      },\n      \"point\": \"0x1feddb41613d5151f64a23db20a3f4479d3645f9d4e7ea92ff3b7798e63359da\",\n      \"opening\": \"0x1850f63235f10a13262e3ba222bda97c8fde7afccbe0f6a5e9d962ea456a2149\",\n      \"commitment\": {\n        \"x\": \"0x477698b841d60ab1dfa466a9ccde627778106ae7ccfcde75247b2a256f0b740\",\n        \"y\": \"0xa5a2dc0da5660baf57695de3da9c489f927b6ad08b63798bb596441dd794830\"\n      }\n    },\n    {\n      \"poly\": {\n        \"coefficients\": {\n          \"coefficients\": [\n            \"0x01560814b583224327802eef8c1d73d84fa58fbf72903b46153e5b84462bccb4\",\n            \"0x0610cf7bec53d014f83ea169eac90120525c86dad94026151cb0b929dbfde72a\",\n            \"0x05b4516754b7b214bb4d655888459cb980c1fccb34aec28f0745f777dff6acb5\",\n            \"0x10de1cd9c0cef832dd63a8d651097ac7c2ec3ae1f96e68e698ad69831fc8db01\",\n            \"0x2e559d894d46842b037ceab67292482d588990d22fe8462318f899f50fbba7ac\",\n            \"0x0c32262afba90bf5411c761c595e7fffde2c72a5d1a69cb44302a2b9ae455570\",\n            \"0x2ae377c49a9b57aef48f4e8a220fc08bba34a989729e441949d874b3495d410d\",\n            \"0x2cb2e6a7815ab3ead9bb73355878010cb6c2b3865b0c7576f1ea44cc1bb5e169\",\n            \"0x2c2e45ea36cff436d89262869af053228777559c786232e9469cff017b4553dd\",\n            \"0x2203981d714298588c1db7ee545574b556cdd5518ff696267b977162f1f69e96\",\n            \"0x1c055e3f95c67aa5275524f8377cd756877d8e7b055f2ac0f95165c395094c08\",\n            \"0x2f41fc9f6ec4754443405f688630fec8952d8cb35b33571b077e56765bf9d5b7\",\n            \"0x06b0ef59aae4136f30c249b0d55cd5e4b4fd0f151c4108a0b01589a7d0cd5ee8\",\n            \"0x0d55a330d9fe6fb51e14257ef62ea5c94dd4b3db085cd95cf4edde8f5debebc1\",\n            \"0x29e1f7b3a7081867034e4c12b71d1cad98c4bd2672bb40a7a109d1a7c1854bd4\",\n            \"0x2b76f84395a5bd0aa75348656b2533970584b8c55106763300e523f5f7830641\"\n          ]\n        }\n      },\n      \"point\": \"0x1878ab885c4eeb02752b8e5e37413b6ac4095ece8af445a9f105912aba6091a6\",\n      \"opening\": \"0x021920c3c93e6a2e03fa1c53ffa1cade0bff5d83143357d01ca57d74d0d87ec9\",\n      \"commitment\": {\n        \"x\": \"0x27dd27bd64c2a6d5cd7e0a85c4c024711ee51e42665eaa9b36ee6a757faeacb7\",\n        \"y\": \"0xefceabfdcfdba04d109a9de2368ad6440ce70ed2e004d008f7ae9d12939e09d\"\n      }\n    },\n    {\n      \"poly\": {\n        \"coefficients\": {\n          \"coefficients\": [\n            \"0x01560814b583224327802eef8c1d73d84fa58fbf72903b46153e5b84462bccb4\",\n            \"0x0610cf7bec53d014f83ea169eac90120525c86dad94026151cb0b929dbfde72a\",\n            \"0x05b4516754b7b214bb4d655888459cb980c1fccb34aec28f0745f777dff6acb5\",\n            \"0x10de1cd9c0cef832dd63a8d651097ac7c2ec3ae1f96e68e698ad69831fc8db01\",\n            \"0x2e559d894d46842b037ceab67292482d588990d22fe8462318f899f50fbba7ac\",\n            \"0x0c32262afba90bf5411c761c595e7fffde2c72a5d1a69cb44302a2b9ae455570\",\n            \"0x2ae377c49a9b57aef48f4e8a220fc08bba34a989729e441949d874b3495d410d\",\n            \"0x2cb2e6a7815ab3ead9bb73355878010cb6c2b3865b0c7576f1ea44cc1bb5e169\",\n            \"0x2c2e45ea36cff436d89262869af053228777559c786232e9469cff017b4553dd\",\n            \"0x2203981d714298588c1db7ee545574b556cdd5518ff696267b977162f1f69e96\",\n            \"0x1c055e3f95c67aa5275524f8377cd756877d8e7b055f2ac0f95165c395094c08\",\n            \"0x2f41fc9f6ec4754443405f688630fec8952d8cb35b33571b077e56765bf9d5b7\",\n            \"0x06b0ef59aae4136f30c249b0d55cd5e4b4fd0f151c4108a0b01589a7d0cd5ee8\",\n            \"0x0d55a330d9fe6fb51e14257ef62ea5c94dd4b3db085cd95cf4edde8f5debebc1\",\n            \"0x29e1f7b3a7081867034e4c12b71d1cad98c4bd2672bb40a7a109d1a7c1854bd4\",\n            \"0x2b76f84395a5bd0aa75348656b2533970584b8c55106763300e523f5f7830641\"\n          ]\n        }\n      },\n      \"point\": \"0x1feddb41613d5151f64a23db20a3f4479d3645f9d4e7ea92ff3b7798e63359da\",\n      \"opening\": \"0x1850e36381c7815fe57548db8e5870b80591159a5d5fec87f987a55c40034302\",\n      \"commitment\": {\n        \"x\": \"0x27dd27bd64c2a6d5cd7e0a85c4c024711ee51e42665eaa9b36ee6a757faeacb7\",\n        \"y\": \"0xefceabfdcfdba04d109a9de2368ad6440ce70ed2e004d008f7ae9d12939e09d\"\n      }\n    },\n    {\n      \"poly\": {\n        \"coefficients\": {\n          \"coefficients\": [\n            \"0x23b6be73de9d4d13b1c910543033f2b710cda13085433997193e210b27d1f263\",\n            \"0x1a001b7d2c12d1ad390e46c75a2a9987c64d37d0c752667463d622c9de304695\",\n            \"0x159dd595aba9326ab79ee29e377b406d66beae9dbe7d85f766bb50599bbeb8b2\",\n            \"0x1f96c632dd3c844d8345188b9aa2e1ee637fe49aba0ed7975d968fd4f930eefa\",\n            \"0x1b36f04ec15725d6ecab1fcb25a6ea51acbf6f1de9a52706d0fb12b0701ab01b\",\n            \"0x020b267c4795e87212e1cccce20efb78a5a17d051d31d530f27d9e0ed520d5de\",\n            \"0x01166b11f6762098417a8b521c113d3a30ce3a5a6a80668cf22c67a9b24237b0\",\n            \"0x15b702e7696d35482428983f71ea99c64f8ebe5b282ea0e8f6b774b245eb3780\",\n            \"0x0d1b42be204a62ab857dd6bb9ab026260f741671beda5d97e0e172a10051de49\",\n            \"0x19321af2193aceae389d158591853bf82e7d2fca7ab86a54a8472ca2354a808d\",\n            \"0x13fabadbef9e4a7b10f55a03c835c4fef599c73d814c0a3952aa544888844903\",\n            \"0x011938f3c213d0f5a1473dd9535a7906feaa50737c6cc6ed358206ca1fe641d4\",\n            \"0x10ed356906e94396f26102b56c67c32a996ad53acaa4ab0fe6942374201d36b1\",\n            \"0x07aff53fd5f4425b02cb7a7f8a17e7a3913d838a0cad92366907bf839c101ecb\",\n            \"0x125d7019aa002bd0807ebdaf5e1b9e97f96f525e15868e0dfa4316126171beb4\",\n            \"0x26ae3e4d97508cc7d0d2fcf61f11fa400f9fa2faeda37673661133df0ca48b77\"\n          ]\n        }\n      },\n      \"point\": \"0x1878ab885c4eeb02752b8e5e37413b6ac4095ece8af445a9f105912aba6091a6\",\n      \"opening\": \"0x1c927a2ab0d78f73bd243a32266e1165d14ebe4f4950aa487c53d090104e5b09\",\n      \"commitment\": {\n        \"x\": \"0x1c30dbdea5181f79dadfdc2195bf45e077f5fcd47c1621058ae5c8a5684f004c\",\n        \"y\": \"0x28bbabd833d7b5d594236352611e7a6b5e6d03dd592b7d551573391cf8d79cf8\"\n      }\n    },\n    {\n      \"poly\": {\n        \"coefficients\": {\n          \"coefficients\": [\n            \"0x23b6be73de9d4d13b1c910543033f2b710cda13085433997193e210b27d1f263\",\n            \"0x1a001b7d2c12d1ad390e46c75a2a9987c64d37d0c752667463d622c9de304695\",\n            \"0x159dd595aba9326ab79ee29e377b406d66beae9dbe7d85f766bb50599bbeb8b2\",\n            \"0x1f96c632dd3c844d8345188b9aa2e1ee637fe49aba0ed7975d968fd4f930eefa\",\n            \"0x1b36f04ec15725d6ecab1fcb25a6ea51acbf6f1de9a52706d0fb12b0701ab01b\",\n            \"0x020b267c4795e87212e1cccce20efb78a5a17d051d31d530f27d9e0ed520d5de\",\n            \"0x01166b11f6762098417a8b521c113d3a30ce3a5a6a80668cf22c67a9b24237b0\",\n            \"0x15b702e7696d35482428983f71ea99c64f8ebe5b282ea0e8f6b774b245eb3780\",\n            \"0x0d1b42be204a62ab857dd6bb9ab026260f741671beda5d97e0e172a10051de49\",\n            \"0x19321af2193aceae389d158591853bf82e7d2fca7ab86a54a8472ca2354a808d\",\n            \"0x13fabadbef9e4a7b10f55a03c835c4fef599c73d814c0a3952aa544888844903\",\n            \"0x011938f3c213d0f5a1473dd9535a7906feaa50737c6cc6ed358206ca1fe641d4\",\n            \"0x10ed356906e94396f26102b56c67c32a996ad53acaa4ab0fe6942374201d36b1\",\n            \"0x07aff53fd5f4425b02cb7a7f8a17e7a3913d838a0cad92366907bf839c101ecb\",\n            \"0x125d7019aa002bd0807ebdaf5e1b9e97f96f525e15868e0dfa4316126171beb4\",\n            \"0x26ae3e4d97508cc7d0d2fcf61f11fa400f9fa2faeda37673661133df0ca48b77\"\n          ]\n        }\n      },\n      \"point\": \"0x1feddb41613d5151f64a23db20a3f4479d3645f9d4e7ea92ff3b7798e63359da\",\n      \"opening\": \"0x2a9025d9b99336a05ea2e946ddf222aeb1213f9eb9a4e500021abd259c9ce4a3\",\n      \"commitment\": {\n        \"x\": \"0x1c30dbdea5181f79dadfdc2195bf45e077f5fcd47c1621058ae5c8a5684f004c\",\n        \"y\": \"0x28bbabd833d7b5d594236352611e7a6b5e6d03dd592b7d551573391cf8d79cf8\"\n      }\n    },\n    {\n      \"poly\": {\n        \"coefficients\": {\n          \"coefficients\": [\n            \"0x01560814b583224327802eef8c1d73d84fa58fbf72903b46153e5b84462bccb4\",\n            \"0x0610cf7bec53d014f83ea169eac90120525c86dad94026151cb0b929dbfde72a\",\n            \"0x05b4516754b7b214bb4d655888459cb980c1fccb34aec28f0745f777dff6acb5\",\n            \"0x10de1cd9c0cef832dd63a8d651097ac7c2ec3ae1f96e68e698ad69831fc8db01\",\n            \"0x2e559d894d46842b037ceab67292482d588990d22fe8462318f899f50fbba7ac\",\n            \"0x0c32262afba90bf5411c761c595e7fffde2c72a5d1a69cb44302a2b9ae455570\",\n            \"0x2ae377c49a9b57aef48f4e8a220fc08bba34a989729e441949d874b3495d410d\",\n            \"0x2cb2e6a7815ab3ead9bb73355878010cb6c2b3865b0c7576f1ea44cc1bb5e169\",\n            \"0x2c2e45ea36cff436d89262869af053228777559c786232e9469cff017b4553dd\",\n            \"0x2203981d714298588c1db7ee545574b556cdd5518ff696267b977162f1f69e96\",\n            \"0x1c055e3f95c67aa5275524f8377cd756877d8e7b055f2ac0f95165c395094c08\",\n            \"0x2f41fc9f6ec4754443405f688630fec8952d8cb35b33571b077e56765bf9d5b7\",\n            \"0x06b0ef59aae4136f30c249b0d55cd5e4b4fd0f151c4108a0b01589a7d0cd5ee8\",\n            \"0x0d55a330d9fe6fb51e14257ef62ea5c94dd4b3db085cd95cf4edde8f5debebc1\",\n            \"0x29e1f7b3a7081867034e4c12b71d1cad98c4bd2672bb40a7a109d1a7c1854bd4\",\n            \"0x2b76f84395a5bd0aa75348656b2533970584b8c55106763300e523f5f7830641\"\n          ]\n        }\n      },\n      \"point\": \"0x25aca9a399b1284f8cbbdc0428e87d584e064bc8656d2e0a7388180efa910125\",\n      \"opening\": \"0x1772fb9a8e183147338a84957333e344f9ee953cc5b60c61e816ff09cf3c7a8e\",\n      \"commitment\": {\n        \"x\": \"0x27dd27bd64c2a6d5cd7e0a85c4c024711ee51e42665eaa9b36ee6a757faeacb7\",\n        \"y\": \"0xefceabfdcfdba04d109a9de2368ad6440ce70ed2e004d008f7ae9d12939e09d\"\n      }\n    },\n    {\n      \"poly\": {\n        \"coefficients\": {\n          \"coefficients\": [\n            \"0x1b26aab418970af02bb4af272fa0f9103e0cb0e470f9fadcd375129953e79cba\",\n            \"0x192ca548746879a2863b41d25a72cfc763927b9d3d583b68479c2288186f03bd\",\n            \"0x2717b21eb04ef5ca7374496840db2a3606fb643edcefa5cf63c6f2d58bf4fd8e\",\n            \"0x1f24d818f62ea18fb0b7c7c9fa8088f721f7f2d0707a94602a61aa93fcb4123d\",\n            \"0x12d5f438cf33400c2d5fda136e0d3401ae591d1c66c2af6adc95ed5db8f7f8da\",\n            \"0x05bd91f33bcfa11b1c99e42d0633c450726b52de7f0fff9e09ac8b58eb50d808\",\n            \"0x1ebe483c4a8bed2c78b7f8f2505672822ffb2b7895d73d582d37ceb1f3f2173e\",\n            \"0x007fc35853678a45f868ed4842c6a75edf1e65835eac914814c64548f361aa45\",\n            \"0x13a443a76de74dc5daea5da806164fdd8ba1a081559d3e59da45710bfc71c8b1\",\n            \"0x2ac155a1ca4ef56592ba2d7d627f54be553e944622e48164d26a15acecad32d2\",\n            \"0x016878006aa738f94a09b59a928ef7d9d3982ea95d4b51e46a22752e11c58a5c\",\n            \"0x184d567b0a6eb2dc34fa5c71e956a5153e579570f841cd31620c455e58477a62\",\n            \"0x2e7405b321d54798e75f654199090a08ef72689729eaae6eef48dd1a82e94392\",\n            \"0x13ce04db3756a33530548baab13824336b5a5415222f8b3547440a05fe76fbb9\",\n            \"0x2484a997019dcbbb597364e56bd57ff4576abe166671c10956dde4cfe0c1f4c8\",\n            \"0x169ed0d157e029e19579426f0c02fbca0ba4c370f83cca396ae61d28bc69e885\"\n          ]\n        }\n      },\n      \"point\": \"0x25aca9a399b1284f8cbbdc0428e87d584e064bc8656d2e0a7388180efa910125\",\n      \"opening\": \"0x2e8be5374a7e38c58fd45f0b7cd736114fda48180be4c8bac5d8d7826b1f1afd\",\n      \"commitment\": {\n        \"x\": \"0x477698b841d60ab1dfa466a9ccde627778106ae7ccfcde75247b2a256f0b740\",\n        \"y\": \"0xa5a2dc0da5660baf57695de3da9c489f927b6ad08b63798bb596441dd794830\"\n      }\n    },\n    {\n      \"poly\": {\n        \"coefficients\": {\n          \"coefficients\": [\n            \"0x2c17c0f5f77b48b6f8bb69a7cceac8da32489a140bb6ae30550fea98dcca2729\",\n            \"0x2482e87fb833258b7d6faf7180dac78821d421cb82737c2ca9c85544014bce0d\",\n            \"0x1cf5deb215f48e7edd15d1d845fd18c91ba0dda114d82471b96db592c501a135\",\n            \"0x19423f3a7cc9227f9e58cc469e2f2955d3226b29b7d61f4c99defb0755db6f27\",\n            \"0x141aec4bfe36cc59490b8eb64802803edfbfae97926a2afaa496ae6b96324d77\",\n            \"0x1e72408aadda81ddcd62a0b8ea8520f3d4951ba76e54590e7e647282126dc4de\",\n            \"0x132a255ed346ac9ba404d2061800dcb85146a33455191698b249d2412656695e\",\n            \"0x2ccbc43312750ff857bb20a53bb64dcd6eeb14f53b65313ae1d0feee0675d890\",\n            \"0x1616c186407bb6a2d4ce1647f92b42852c0f917cc185d76777e3b40b5e5bbae3\",\n            \"0x05ba6117e26437f6619d1b925f6dfc54226ad706ad734bc779dbfe3f9bd44e58\",\n            \"0x190710278d14025ccee750f5bf641d17ee04e59f6b3953e727be90897dc3eeab\",\n            \"0x0637fc3a4e5dcfa006a90c08321c77605239cec29b20648153a62a3cd7a2d83a\",\n            \"0x2dd226c45e7f23db94e0297daba673c35d005a41ce53596382c763a811b7e18b\",\n            \"0x1d45324a309410209f1ce9db95cf0b06cce476aca282c08b8a8324d6e3f9a4de\",\n            \"0x0ee0a9cc83d1650822f0e3f02350d71273a3b713e12d684dbb7df68224fe2575\",\n            \"0x2528b264054f1dd1142113f6267b53de862afe9194195d4f23c9d38d375a2a37\"\n          ]\n        }\n      },\n      \"point\": \"0x25aca9a399b1284f8cbbdc0428e87d584e064bc8656d2e0a7388180efa910125\",\n      \"opening\": \"0x23fc00efe984c5098fc8228923773907fe7da84caa1c1bbee1f3609d8b86b696\",\n      \"commitment\": {\n        \"x\": \"0x16ad53c6cb565e23a435f06849c1fca80d7dd87de0c25d2a37012543b01bc77\",\n        \"y\": \"0xfdcfdd9560c81de5141f9b51b95d3639e2a9eabb093eda4ed5934e24c3775e4\"\n      }\n    },\n    {\n      \"poly\": {\n        \"coefficients\": {\n          \"coefficients\": [\n            \"0x2a57c4a4850b6c2481463cffb1512d51832d6b3f6a82427f1b65b6e172000015\",\n            \"0x12d62582906462c130f4ea4b800a85e9eb4f7f466d5bfa89ea8bf20deb857e75\",\n            \"0x24d3621acf7ac08f3ff2b1536911a5f55c73faa6ac631dc11419dde5d02fa4c3\",\n            \"0x266f277a8898dcbac4b1619077fd15d7cbea2cab246f1f963405b98fcc267aa1\",\n            \"0x060c89ce5c2634052bcdbb3cfc1599961851e0d3f772cdc7d66f3fa047f70371\",\n            \"0x156c4fe519d5f5230fbb7446f480a0625386582f64a5a96b70ce67ebeda2e08a\",\n            \"0x223a74e2b7a94291a2b3184a19ef58b1df6171d7daec0bf0b1d81cc3b5729d8e\",\n            \"0x2ba1af62d8a5586c97e3b9a88ae7c1b5b62381468bc2a5b77ca221dab0353b7b\",\n            \"0x0c19139cb84c680a6e14116da06056174a0cfa121e6e5c2450f87d64fc000012\",\n            \"0x0f22af6b65f8136a0f5512232c8b93ec26b2d0fc013663a9943ea7d915d45ed0\",\n            \"0x05846289b590ab92b5a9fe123a3a92ba45ce0c915ba153e7705a0edb63c71fdd\",\n            \"0x0975b22f115b3aa88ffb637fcf46ae05a5851de94b98412a2332ad75e4aa12f1\",\n            \"0x244b3ad628e5381f557881c2b53b93bb6adb8a6b730f74b744f677412a08fcab\",\n            \"0x29637812b230d5062e425e23446999871bcdcd195aa3e07b4962d30a01daf0a5\",\n            \"0x1436635e85ae919fd850c3bd45c71f58cec45781108263895177e1a2f6969e12\",\n            \"0x054213d94fc9d0827668c9484e598821f7e65fbbbd3fd3b302b178926e228806\"\n          ]\n        }\n      },\n      \"point\": \"0x1878ab885c4eeb02752b8e5e37413b6ac4095ece8af445a9f105912aba6091a6\",\n      \"opening\": \"0x0fa13ccbca58eaacf3e0137cbaa3f09e0de199864589ed3b5d52513eb59012bb\",\n      \"commitment\": {\n        \"x\": \"0xd4c8829e6feb9ab9606917e970b944717cc4d9a74695dd4e7cc23b5888b6dce\",\n        \"y\": \"0x3a99ef4660a95515763e072043119fcbf6d3f3b709af6bf05b5c8b4d815a775\"\n      }\n    },\n    {\n      \"poly\": {\n        \"coefficients\": {\n          \"coefficients\": [\n            \"0x060c89ce5c263405370a08b6d0302b0ba5067d090f372e12287c3eb27e000003\",\n            \"0x3028c9344e30580ea8438e904895dd3aca99e1c6df3b1350af25b1d4ade4a57a\",\n            \"0x2a517e34acc241a2d74e670c1c87a3c89fb9647a6c280716ec20a7bd46cce6d5\",\n            \"0x1b368ad453cf5e33080841d7bae0a9d361d2e68b5064b035c0399f94e5b0c709\",\n            \"0x12259d6b14729c0e271bcff645092f863f16b80e0590c05793badbac4cce7331\",\n            \"0x0e2e8378d846e58ea5bf2f5d9d2b15ec6e089dbc279e7e73b8720ec69ca8daf3\",\n            \"0x2a45aa0d3f0d2460e04bcbf2a284939a713c0b7a196c7bf3cd85e41c023d22e8\",\n            \"0x29b017d8e7a9669ad6361e0a527678af4ba104241b73f98af9dce2a9f6f8d7eb\",\n            \"0x2a57c4a4850b6c2481463cffb1512d51832d6b3f6a82427f1b65b6e171fffffe\",\n            \"0x003b853e9301481b100cb72638eb7b225d9a06819a7e5d4094bc43bf421b5a87\",\n            \"0x0612d03e346f5e86e101deaa64f9b494887a83ce0d91697a57c14dd6a933192c\",\n            \"0x152dc39e8d6241f6b04803dec6a0ae89c66101bd2954c05b83a855ff0a4f38f8\",\n            \"0x1e3eb107ccbf041b913475c03c7828d6e91d303a7428b039b02719e7a3318cd0\",\n            \"0x2235cafa08eaba9b12911658e4564270ba2b4a8c521af21d8b6fe6cd5357250e\",\n            \"0x061ea465a2247bc8d80479c3defcc4c2b6f7dcce604cf49d765c1177edc2dd19\",\n            \"0x06b43699f988398ee21a27ac2f0adfaddc92e4245e4577064a0512e9f9072816\"\n          ]\n        }\n      },\n      \"point\": \"0x1878ab885c4eeb02752b8e5e37413b6ac4095ece8af445a9f105912aba6091a6\",\n      \"opening\": \"0x1ca0efb9353dd426b7c7a7d4e5147967371203bd0076145a34c71b9007d15fca\",\n      \"commitment\": {\n        \"x\": \"0xe80af0f2dac74d170c3995fd07ffd5577918b748fe4ba0843fd2386ce9ae384\",\n        \"y\": \"0x58b31b773e7a0e22f1ef9d6bbcc154b3dfaec09ff6c78084c1ae5c150f6624d\"\n      }\n    },\n    {\n      \"poly\": {\n        \"coefficients\": {\n          \"coefficients\": [\n            \"0x2a57c4a4850b6c2481463cffb1512d51832d6b3f6a82427f1b65b6e172000015\",\n            \"0x12d62582906462c130f4ea4b800a85e9eb4f7f466d5bfa89ea8bf20deb857e75\",\n            \"0x24d3621acf7ac08f3ff2b1536911a5f55c73faa6ac631dc11419dde5d02fa4c3\",\n            \"0x266f277a8898dcbac4b1619077fd15d7cbea2cab246f1f963405b98fcc267aa1\",\n            \"0x060c89ce5c2634052bcdbb3cfc1599961851e0d3f772cdc7d66f3fa047f70371\",\n            \"0x156c4fe519d5f5230fbb7446f480a0625386582f64a5a96b70ce67ebeda2e08a\",\n            \"0x223a74e2b7a94291a2b3184a19ef58b1df6171d7daec0bf0b1d81cc3b5729d8e\",\n            \"0x2ba1af62d8a5586c97e3b9a88ae7c1b5b62381468bc2a5b77ca221dab0353b7b\",\n            \"0x0c19139cb84c680a6e14116da06056174a0cfa121e6e5c2450f87d64fc000012\",\n            \"0x0f22af6b65f8136a0f5512232c8b93ec26b2d0fc013663a9943ea7d915d45ed0\",\n            \"0x05846289b590ab92b5a9fe123a3a92ba45ce0c915ba153e7705a0edb63c71fdd\",\n            \"0x0975b22f115b3aa88ffb637fcf46ae05a5851de94b98412a2332ad75e4aa12f1\",\n            \"0x244b3ad628e5381f557881c2b53b93bb6adb8a6b730f74b744f677412a08fcab\",\n            \"0x29637812b230d5062e425e23446999871bcdcd195aa3e07b4962d30a01daf0a5\",\n            \"0x1436635e85ae919fd850c3bd45c71f58cec45781108263895177e1a2f6969e12\",\n            \"0x054213d94fc9d0827668c9484e598821f7e65fbbbd3fd3b302b178926e228806\"\n          ]\n        }\n      },\n      \"point\": \"0x1feddb41613d5151f64a23db20a3f4479d3645f9d4e7ea92ff3b7798e63359da\",\n      \"opening\": \"0x01df83543b4be0f49ba822a8067a75444a113d2cda81e8e541109ba09c6d0214\",\n      \"commitment\": {\n        \"x\": \"0xd4c8829e6feb9ab9606917e970b944717cc4d9a74695dd4e7cc23b5888b6dce\",\n        \"y\": \"0x3a99ef4660a95515763e072043119fcbf6d3f3b709af6bf05b5c8b4d815a775\"\n      }\n    },\n    {\n      \"poly\": {\n        \"coefficients\": {\n          \"coefficients\": [\n            \"0x27bad031f6e5d63c6287744855e8ade0884815738c74c2cb0146042ba80f9dc1\",\n            \"0x0aedfd48d1d99352bddbae12766888014ce326c0184a49030f255bc402971c8e\",\n            \"0x1421f8d1f52f38eadad21dacba2c9c8ae2fa265be2b5cf56ecac1dc30e880614\",\n            \"0x2615ac9cfd366ea7c7b50c56fa3753c345533ddfaa1cfd23b7fdf6771010b3ae\",\n            \"0x055a53b13f4ef2982200bcee18cad82631591d7537e81f9f6d0d9af73919aca7\",\n            \"0x055992fb3e68a839041d15f16baee9aac780d3cc027c2650a8fa07d8c708ffa4\",\n            \"0x01288fca758908fd75be91359aae9947105255cb57e3737ba4cd2cb249b9568b\",\n            \"0x057b12a3c876f530ff3416b1f37b57888c294abfca1add3c80920e715ebd3dce\",\n            \"0x128bda5bfdc7fd9f0bcd7a4590e6367ac7d02e478e7fe7b4effcc682fee6ac4e\",\n            \"0x15fa52c718251448f4a7b5e5a51b8983626f542e76616ec2a97b51a9ca55acaa\",\n            \"0x2e1e139d30e7837e3a9c17b1cfabb68449abe4a71d09929789ba75a389477d5b\",\n            \"0x18d881eb1a3059a451819fc43526ad05f7144db79e00c502abb2999c9677e5eb\",\n            \"0x0497036ec879338b7800d9c8785fd34de9677ccba2b989725a258156fecaeddc\",\n            \"0x2a58721ef44e23f723b2f6f0c767346ee25de6a9ce3be5143c45bf02700f6067\",\n            \"0x241e67371586327ed2947f58dc70148a99a32ae01d5f67a82bba22edc0c67134\",\n            \"0x11fb89af7e2c3df6b15be924a02551ebb634e49577dd20c759a6da3a058acf9e\"\n          ]\n        }\n      },\n      \"point\": \"0x1878ab885c4eeb02752b8e5e37413b6ac4095ece8af445a9f105912aba6091a6\",\n      \"opening\": \"0x11e368174b8acccfbdfa3df69075bbfc0a755a0b1e8b75eed1af049a4454c091\",\n      \"commitment\": {\n        \"x\": \"0xd2b3853c2faafac946818a717533f3b12ab292173d52c1a9015cdf18ef87827\",\n        \"y\": \"0x21b7650147538e48bab652f15aece419695f7374c8e8bfae869451bb50f387ec\"\n      }\n    },\n    {\n      \"poly\": {\n        \"coefficients\": {\n          \"coefficients\": [\n            \"0x27bad031f6e5d63c6287744855e8ade0884815738c74c2cb0146042ba80f9dc1\",\n            \"0x0aedfd48d1d99352bddbae12766888014ce326c0184a49030f255bc402971c8e\",\n            \"0x1421f8d1f52f38eadad21dacba2c9c8ae2fa265be2b5cf56ecac1dc30e880614\",\n            \"0x2615ac9cfd366ea7c7b50c56fa3753c345533ddfaa1cfd23b7fdf6771010b3ae\",\n            \"0x055a53b13f4ef2982200bcee18cad82631591d7537e81f9f6d0d9af73919aca7\",\n            \"0x055992fb3e68a839041d15f16baee9aac780d3cc027c2650a8fa07d8c708ffa4\",\n            \"0x01288fca758908fd75be91359aae9947105255cb57e3737ba4cd2cb249b9568b\",\n            \"0x057b12a3c876f530ff3416b1f37b57888c294abfca1add3c80920e715ebd3dce\",\n            \"0x128bda5bfdc7fd9f0bcd7a4590e6367ac7d02e478e7fe7b4effcc682fee6ac4e\",\n            \"0x15fa52c718251448f4a7b5e5a51b8983626f542e76616ec2a97b51a9ca55acaa\",\n            \"0x2e1e139d30e7837e3a9c17b1cfabb68449abe4a71d09929789ba75a389477d5b\",\n            \"0x18d881eb1a3059a451819fc43526ad05f7144db79e00c502abb2999c9677e5eb\",\n            \"0x0497036ec879338b7800d9c8785fd34de9677ccba2b989725a258156fecaeddc\",\n            \"0x2a58721ef44e23f723b2f6f0c767346ee25de6a9ce3be5143c45bf02700f6067\",\n            \"0x241e67371586327ed2947f58dc70148a99a32ae01d5f67a82bba22edc0c67134\",\n            \"0x11fb89af7e2c3df6b15be924a02551ebb634e49577dd20c759a6da3a058acf9e\"\n          ]\n        }\n      },\n      \"point\": \"0x1feddb41613d5151f64a23db20a3f4479d3645f9d4e7ea92ff3b7798e63359da\",\n      \"opening\": \"0x0bf644932135dc0354b7894bcce618471838a3c81224399d962da14d3c5ce060\",\n      \"commitment\": {\n        \"x\": \"0xd2b3853c2faafac946818a717533f3b12ab292173d52c1a9015cdf18ef87827\",\n        \"y\": \"0x21b7650147538e48bab652f15aece419695f7374c8e8bfae869451bb50f387ec\"\n      }\n    },\n    {\n      \"poly\": {\n        \"coefficients\": {\n          \"coefficients\": [\n            \"0x25eb43faf087b073127c0c78c9bff217db519dc4e341fa67c3bff39521fafaa0\",\n            \"0x1d11001a2c335416fd402e6f2320a36637e9716224dbd24c644e8d992a9937d2\",\n            \"0x022ed7d9821857ae10b56d3e6653f9d2d66c5830f37fd8f8fe919499fd5cd141\",\n            \"0x067ba58be138be7551ae282a31bdbc382611eb1ec1598e903f8f2c5b54c2ae7b\",\n            \"0x119475b2c3264aa8805750846d070c5475b662e89d05727c0a1b07a12137eb11\",\n            \"0x1db303cde0cc6d069ed2018e7b373f2cdb104ecaa5eacd854ed95e0e8bcfbb5f\",\n            \"0x0643722d1811c56d8f664a29024b43d452870ca0da49a8c8079a4e8b3afe2441\",\n            \"0x08d7f5f9b6a126e4791f3ad415ab77dddc5c1fd75d9737fd6a3c4b476b020dcb\",\n            \"0x28abab2f4ebd7fb6fc77a8b5f743eaab4c1d5ad7781da8e4a0f020109ba8f7b3\",\n            \"0x10966a5a95d1fb71187025af34157068768839ebfd8a39cb25799d9003e8fe38\",\n            \"0x26ad4be30ca2f3f5a762f8d9e29731d8afab86e052de49089e26466d221b4217\",\n            \"0x02ce4196df38f8e4ba40d9638aacbcd8a769ad82b59a10127e0bff068df182ba\",\n            \"0x2ea4540330333650e1de53ca27a69edcc8e27e49368aa39367cc7c5d910fb194\",\n            \"0x2a950d6c990a49114ebb24fda0be841e688018802c341c41afdd4b9a45f595b7\",\n            \"0x1de8b570053b05d1f25f9f402b7ba40a8acc8e103e31090050aea86c34476f36\",\n            \"0x23f8fbaaaad3d80d512c7c0e61bd163645cf9cfaf8123834c6bad47ca5ad6399\"\n          ]\n        }\n      },\n      \"point\": \"0x1878ab885c4eeb02752b8e5e37413b6ac4095ece8af445a9f105912aba6091a6\",\n      \"opening\": \"0x2b269271b39772a678f407722dfd9945cb531223a3908edf4862dffdbe4e02ee\",\n      \"commitment\": {\n        \"x\": \"0x22c7193d81517510725d2b2485121827c5d6409b97eb3d23d4f6606217aa2b48\",\n        \"y\": \"0x275207c52e625cc123cb30e9ce18ec2963b9478f475f7d89f7b3ed1930415686\"\n      }\n    },\n    {\n      \"poly\": {\n        \"coefficients\": {\n          \"coefficients\": [\n            \"0x25eb43faf087b073127c0c78c9bff217db519dc4e341fa67c3bff39521fafaa0\",\n            \"0x1d11001a2c335416fd402e6f2320a36637e9716224dbd24c644e8d992a9937d2\",\n            \"0x022ed7d9821857ae10b56d3e6653f9d2d66c5830f37fd8f8fe919499fd5cd141\",\n            \"0x067ba58be138be7551ae282a31bdbc382611eb1ec1598e903f8f2c5b54c2ae7b\",\n            \"0x119475b2c3264aa8805750846d070c5475b662e89d05727c0a1b07a12137eb11\",\n            \"0x1db303cde0cc6d069ed2018e7b373f2cdb104ecaa5eacd854ed95e0e8bcfbb5f\",\n            \"0x0643722d1811c56d8f664a29024b43d452870ca0da49a8c8079a4e8b3afe2441\",\n            \"0x08d7f5f9b6a126e4791f3ad415ab77dddc5c1fd75d9737fd6a3c4b476b020dcb\",\n            \"0x28abab2f4ebd7fb6fc77a8b5f743eaab4c1d5ad7781da8e4a0f020109ba8f7b3\",\n            \"0x10966a5a95d1fb71187025af34157068768839ebfd8a39cb25799d9003e8fe38\",\n            \"0x26ad4be30ca2f3f5a762f8d9e29731d8afab86e052de49089e26466d221b4217\",\n            \"0x02ce4196df38f8e4ba40d9638aacbcd8a769ad82b59a10127e0bff068df182ba\",\n            \"0x2ea4540330333650e1de53ca27a69edcc8e27e49368aa39367cc7c5d910fb194\",\n            \"0x2a950d6c990a49114ebb24fda0be841e688018802c341c41afdd4b9a45f595b7\",\n            \"0x1de8b570053b05d1f25f9f402b7ba40a8acc8e103e31090050aea86c34476f36\",\n            \"0x23f8fbaaaad3d80d512c7c0e61bd163645cf9cfaf8123834c6bad47ca5ad6399\"\n          ]\n        }\n      },\n      \"point\": \"0x1feddb41613d5151f64a23db20a3f4479d3645f9d4e7ea92ff3b7798e63359da\",\n      \"opening\": \"0x08e5c7ee1b8a38cabb682b937b0401ea4b3727a6b69558f52a89cdf087ad1306\",\n      \"commitment\": {\n        \"x\": \"0x22c7193d81517510725d2b2485121827c5d6409b97eb3d23d4f6606217aa2b48\",\n        \"y\": \"0x275207c52e625cc123cb30e9ce18ec2963b9478f475f7d89f7b3ed1930415686\"\n      }\n    },\n    {\n      \"poly\": {\n        \"coefficients\": {\n          \"coefficients\": [\n            \"0x09281fd13b026e16f8fbc689941062b0d6bbd535fda626b269ef78699a7467c2\",\n            \"0x0301f0f426b900cdc3104ce36c21c916a21e1d2821480870a498a7a915dca364\",\n            \"0x2d772b9ee0a6f032ec7a08268ee673a34ebfbc9a4a5d947a570374d1f762e6f6\",\n            \"0x241681a32c9efd5cbf44aaef7c816e3c8a09eeee0fae2913a8988f0d3df36470\",\n            \"0x2fba9623fda6de12e3d46dc86655aae912fed1a6e4e864f9a39e854098aab964\",\n            \"0x1f3a4f4b754ada3ee38d4f190fe772dadc419bc7114d6a032ea4cfe53b681d2e\",\n            \"0x00001858f89385829b929ac872a97abee329490838b0b0f0d76e429e07c3726a\",\n            \"0x2d164b5f5702693c772eec4088d9c40ce8a5ffe3fb51cc30f1c587b8d235ab5d\",\n            \"0x19fafa156b2097beaef098f1f4b4ad68504d52703d90d21c16bb9ca17d7547f8\",\n            \"0x2cc87c08c38eb1927bb5123b318bb9c133ca82a1f53225527373db4f3ef3bf5d\",\n            \"0x0c704b7263c687c03ae32713ea3b260d9d8218539d92f2928ffc8cecfec6ac40\",\n            \"0x23ea21065c46c605a69023577da80f480938e6d60628f727d89ea8ea8ad36bb6\",\n            \"0x30166b4126d88e5af867493fe778e242aec5d2df28bf893c8c9ab21ff5cd18a4\",\n            \"0x227f3a698689b09d45c6d5513f1326ce2511cf900e68f0db752aeda9e11f5ef7\",\n            \"0x1c2e4f4b08fbdb2ac7ba0a44458d84a187a38d16ed031001a11090f07d8e2561\",\n            \"0x23af931194fef8d2f872c0733b59441dbe9bbe0d764ed97b783cc9ed4ccd04eb\"\n          ]\n        }\n      },\n      \"point\": \"0x1878ab885c4eeb02752b8e5e37413b6ac4095ece8af445a9f105912aba6091a6\",\n      \"opening\": \"0x271afc05aa78364bd47bc2ca0e22e0bc228759e9ccdd5b2c2999d87a9eb60729\",\n      \"commitment\": {\n        \"x\": \"0x234473361e5bb22a37a02da065c0ccbeaf6d44c3820afe7ec594b3791e179705\",\n        \"y\": \"0x72d829adbddb62d6af633e6a660f11f75dea4701990f9c5fc50972f6a54aa0c\"\n      }\n    },\n    {\n      \"poly\": {\n        \"coefficients\": {\n          \"coefficients\": [\n            \"0x09281fd13b026e16f8fbc689941062b0d6bbd535fda626b269ef78699a7467c2\",\n            \"0x0301f0f426b900cdc3104ce36c21c916a21e1d2821480870a498a7a915dca364\",\n            \"0x2d772b9ee0a6f032ec7a08268ee673a34ebfbc9a4a5d947a570374d1f762e6f6\",\n            \"0x241681a32c9efd5cbf44aaef7c816e3c8a09eeee0fae2913a8988f0d3df36470\",\n            \"0x2fba9623fda6de12e3d46dc86655aae912fed1a6e4e864f9a39e854098aab964\",\n            \"0x1f3a4f4b754ada3ee38d4f190fe772dadc419bc7114d6a032ea4cfe53b681d2e\",\n            \"0x00001858f89385829b929ac872a97abee329490838b0b0f0d76e429e07c3726a\",\n            \"0x2d164b5f5702693c772eec4088d9c40ce8a5ffe3fb51cc30f1c587b8d235ab5d\",\n            \"0x19fafa156b2097beaef098f1f4b4ad68504d52703d90d21c16bb9ca17d7547f8\",\n            \"0x2cc87c08c38eb1927bb5123b318bb9c133ca82a1f53225527373db4f3ef3bf5d\",\n            \"0x0c704b7263c687c03ae32713ea3b260d9d8218539d92f2928ffc8cecfec6ac40\",\n            \"0x23ea21065c46c605a69023577da80f480938e6d60628f727d89ea8ea8ad36bb6\",\n            \"0x30166b4126d88e5af867493fe778e242aec5d2df28bf893c8c9ab21ff5cd18a4\",\n            \"0x227f3a698689b09d45c6d5513f1326ce2511cf900e68f0db752aeda9e11f5ef7\",\n            \"0x1c2e4f4b08fbdb2ac7ba0a44458d84a187a38d16ed031001a11090f07d8e2561\",\n            \"0x23af931194fef8d2f872c0733b59441dbe9bbe0d764ed97b783cc9ed4ccd04eb\"\n          ]\n        }\n      },\n      \"point\": \"0x1feddb41613d5151f64a23db20a3f4479d3645f9d4e7ea92ff3b7798e63359da\",\n      \"opening\": \"0x04daca11b912394054c18cd4e83a625abffe0d9e2bb3c5c8126f1a4b83cd253f\",\n      \"commitment\": {\n        \"x\": \"0x234473361e5bb22a37a02da065c0ccbeaf6d44c3820afe7ec594b3791e179705\",\n        \"y\": \"0x72d829adbddb62d6af633e6a660f11f75dea4701990f9c5fc50972f6a54aa0c\"\n      }\n    },\n    {\n      \"poly\": {\n        \"coefficients\": {\n          \"coefficients\": [\n            \"0x06ccb417864f529990061d4e92dcaa0574b12ce900438688329abd9f2717ebe2\",\n            \"0x00f3d4c029499eb2128ed6d28c384536e11649e81c7f313ba2779a1ae87a764c\",\n            \"0x20dac075424ea2cad49a61fee47e720c9a7b63425bf88b6985995bf2b5f8d1b7\",\n            \"0x2b347eba498aecc00a9e2e9f047a35adced26a8d3dfa377b099f775235879c28\",\n            \"0x2a7280b93e6810a7cd6007f521e90d9c581f377a04474dc2e3ac4bdf326eafb0\",\n            \"0x09981f3516fd42812f82175ba7678ed4ddccfc4e7b45b53f96f13c7aa005be5b\",\n            \"0x00fa3e092c8e8fe96f4813437a17c110c186f85f66a1c8d27adef4957b30b267\",\n            \"0x089f7432f01c790c423deb22871e6315c1c9d26e6930ecce2824fc6c1b8db4c6\",\n            \"0x1fd70c1418949aab4c48142f21e703ed08b7068fe1a5d75015208dcac51be0f2\",\n            \"0x11a0af8f117fe0445f626ff2e3142b4b476df4f9f366b92f512c7c0b6cf0ae04\",\n            \"0x299f90885fff6bec0a490480c1b0fa715b5c781ccf39ac27768ce493b9ca31a5\",\n            \"0x1879f265c72f21029625740d85f4ae1d8b0ed2326f6d6fa89aef4c5e4bd4230f\",\n            \"0x1564e2c543fb895ee90380db7947dfceb5c31b9b5f24baf4efb0b1459838d368\",\n            \"0x1b6b86f9c2524d698ff49ef7d177a54d100382f7d8011beb272787fd3870658c\",\n            \"0x18772e48f3f35cb97853a0d8e8163708d28c8c8a5ac7f7af094864183e83a9e3\",\n            \"0x161c82b5edf64ccb8c77044bdd17bc12bb0231383e729e89d91351742587f35c\"\n          ]\n        }\n      },\n      \"point\": \"0x1878ab885c4eeb02752b8e5e37413b6ac4095ece8af445a9f105912aba6091a6\",\n      \"opening\": \"0x19eebedecda2ae4b32431888d642c5cc7847f560652c938c9760ac5449eeea41\",\n      \"commitment\": {\n        \"x\": \"0x6c289509103cb19067161c7eb3956dc9b7289ebc8ae47efa4eaac74cc95e568\",\n        \"y\": \"0x2071ad32b48002a3f62778fb57451c91c744ca9d0f837fa0fc96bd570898d8b2\"\n      }\n    },\n    {\n      \"poly\": {\n        \"coefficients\": {\n          \"coefficients\": [\n            \"0x06ccb417864f529990061d4e92dcaa0574b12ce900438688329abd9f2717ebe2\",\n            \"0x00f3d4c029499eb2128ed6d28c384536e11649e81c7f313ba2779a1ae87a764c\",\n            \"0x20dac075424ea2cad49a61fee47e720c9a7b63425bf88b6985995bf2b5f8d1b7\",\n            \"0x2b347eba498aecc00a9e2e9f047a35adced26a8d3dfa377b099f775235879c28\",\n            \"0x2a7280b93e6810a7cd6007f521e90d9c581f377a04474dc2e3ac4bdf326eafb0\",\n            \"0x09981f3516fd42812f82175ba7678ed4ddccfc4e7b45b53f96f13c7aa005be5b\",\n            \"0x00fa3e092c8e8fe96f4813437a17c110c186f85f66a1c8d27adef4957b30b267\",\n            \"0x089f7432f01c790c423deb22871e6315c1c9d26e6930ecce2824fc6c1b8db4c6\",\n            \"0x1fd70c1418949aab4c48142f21e703ed08b7068fe1a5d75015208dcac51be0f2\",\n            \"0x11a0af8f117fe0445f626ff2e3142b4b476df4f9f366b92f512c7c0b6cf0ae04\",\n            \"0x299f90885fff6bec0a490480c1b0fa715b5c781ccf39ac27768ce493b9ca31a5\",\n            \"0x1879f265c72f21029625740d85f4ae1d8b0ed2326f6d6fa89aef4c5e4bd4230f\",\n            \"0x1564e2c543fb895ee90380db7947dfceb5c31b9b5f24baf4efb0b1459838d368\",\n            \"0x1b6b86f9c2524d698ff49ef7d177a54d100382f7d8011beb272787fd3870658c\",\n            \"0x18772e48f3f35cb97853a0d8e8163708d28c8c8a5ac7f7af094864183e83a9e3\",\n            \"0x161c82b5edf64ccb8c77044bdd17bc12bb0231383e729e89d91351742587f35c\"\n          ]\n        }\n      },\n      \"point\": \"0x1feddb41613d5151f64a23db20a3f4479d3645f9d4e7ea92ff3b7798e63359da\",\n      \"opening\": \"0x01ad91ba86bac41e31b988163233331418b680ddb0258a95acf2fd32d22329fd\",\n      \"commitment\": {\n        \"x\": \"0x6c289509103cb19067161c7eb3956dc9b7289ebc8ae47efa4eaac74cc95e568\",\n        \"y\": \"0x2071ad32b48002a3f62778fb57451c91c744ca9d0f837fa0fc96bd570898d8b2\"\n      }\n    },\n    {\n      \"poly\": {\n        \"coefficients\": {\n          \"coefficients\": [\n            \"0x09281fd13b026e16f8fbc689941062b0d6bbd535fda626b269ef78699a7467c2\",\n            \"0x0301f0f426b900cdc3104ce36c21c916a21e1d2821480870a498a7a915dca364\",\n            \"0x2d772b9ee0a6f032ec7a08268ee673a34ebfbc9a4a5d947a570374d1f762e6f6\",\n            \"0x241681a32c9efd5cbf44aaef7c816e3c8a09eeee0fae2913a8988f0d3df36470\",\n            \"0x2fba9623fda6de12e3d46dc86655aae912fed1a6e4e864f9a39e854098aab964\",\n            \"0x1f3a4f4b754ada3ee38d4f190fe772dadc419bc7114d6a032ea4cfe53b681d2e\",\n            \"0x00001858f89385829b929ac872a97abee329490838b0b0f0d76e429e07c3726a\",\n            \"0x2d164b5f5702693c772eec4088d9c40ce8a5ffe3fb51cc30f1c587b8d235ab5d\",\n            \"0x19fafa156b2097beaef098f1f4b4ad68504d52703d90d21c16bb9ca17d7547f8\",\n            \"0x2cc87c08c38eb1927bb5123b318bb9c133ca82a1f53225527373db4f3ef3bf5d\",\n            \"0x0c704b7263c687c03ae32713ea3b260d9d8218539d92f2928ffc8cecfec6ac40\",\n            \"0x23ea21065c46c605a69023577da80f480938e6d60628f727d89ea8ea8ad36bb6\",\n            \"0x30166b4126d88e5af867493fe778e242aec5d2df28bf893c8c9ab21ff5cd18a4\",\n            \"0x227f3a698689b09d45c6d5513f1326ce2511cf900e68f0db752aeda9e11f5ef7\",\n            \"0x1c2e4f4b08fbdb2ac7ba0a44458d84a187a38d16ed031001a11090f07d8e2561\",\n            \"0x23af931194fef8d2f872c0733b59441dbe9bbe0d764ed97b783cc9ed4ccd04eb\"\n          ]\n        }\n      },\n      \"point\": \"0x25aca9a399b1284f8cbbdc0428e87d584e064bc8656d2e0a7388180efa910125\",\n      \"opening\": \"0x2912e54ff10738c9a5d153eeb805c3499374dc43c918e0bf50b650db5ff98733\",\n      \"commitment\": {\n        \"x\": \"0x234473361e5bb22a37a02da065c0ccbeaf6d44c3820afe7ec594b3791e179705\",\n        \"y\": \"0x72d829adbddb62d6af633e6a660f11f75dea4701990f9c5fc50972f6a54aa0c\"\n      }\n    },\n    {\n      \"poly\": {\n        \"coefficients\": {\n          \"coefficients\": [\n            \"0x25eb43faf087b073127c0c78c9bff217db519dc4e341fa67c3bff39521fafaa0\",\n            \"0x1d11001a2c335416fd402e6f2320a36637e9716224dbd24c644e8d992a9937d2\",\n            \"0x022ed7d9821857ae10b56d3e6653f9d2d66c5830f37fd8f8fe919499fd5cd141\",\n            \"0x067ba58be138be7551ae282a31bdbc382611eb1ec1598e903f8f2c5b54c2ae7b\",\n            \"0x119475b2c3264aa8805750846d070c5475b662e89d05727c0a1b07a12137eb11\",\n            \"0x1db303cde0cc6d069ed2018e7b373f2cdb104ecaa5eacd854ed95e0e8bcfbb5f\",\n            \"0x0643722d1811c56d8f664a29024b43d452870ca0da49a8c8079a4e8b3afe2441\",\n            \"0x08d7f5f9b6a126e4791f3ad415ab77dddc5c1fd75d9737fd6a3c4b476b020dcb\",\n            \"0x28abab2f4ebd7fb6fc77a8b5f743eaab4c1d5ad7781da8e4a0f020109ba8f7b3\",\n            \"0x10966a5a95d1fb71187025af34157068768839ebfd8a39cb25799d9003e8fe38\",\n            \"0x26ad4be30ca2f3f5a762f8d9e29731d8afab86e052de49089e26466d221b4217\",\n            \"0x02ce4196df38f8e4ba40d9638aacbcd8a769ad82b59a10127e0bff068df182ba\",\n            \"0x2ea4540330333650e1de53ca27a69edcc8e27e49368aa39367cc7c5d910fb194\",\n            \"0x2a950d6c990a49114ebb24fda0be841e688018802c341c41afdd4b9a45f595b7\",\n            \"0x1de8b570053b05d1f25f9f402b7ba40a8acc8e103e31090050aea86c34476f36\",\n            \"0x23f8fbaaaad3d80d512c7c0e61bd163645cf9cfaf8123834c6bad47ca5ad6399\"\n          ]\n        }\n      },\n      \"point\": \"0x25aca9a399b1284f8cbbdc0428e87d584e064bc8656d2e0a7388180efa910125\",\n      \"opening\": \"0x01fc0c271c7599cd7a0d4317363f2345ee8d1ea46faaf2092e4181c2b94d857b\",\n      \"commitment\": {\n        \"x\": \"0x22c7193d81517510725d2b2485121827c5d6409b97eb3d23d4f6606217aa2b48\",\n        \"y\": \"0x275207c52e625cc123cb30e9ce18ec2963b9478f475f7d89f7b3ed1930415686\"\n      }\n    },\n    {\n      \"poly\": {\n        \"coefficients\": {\n          \"coefficients\": [\n            \"0x27bad031f6e5d63c6287744855e8ade0884815738c74c2cb0146042ba80f9dc1\",\n            \"0x0aedfd48d1d99352bddbae12766888014ce326c0184a49030f255bc402971c8e\",\n            \"0x1421f8d1f52f38eadad21dacba2c9c8ae2fa265be2b5cf56ecac1dc30e880614\",\n            \"0x2615ac9cfd366ea7c7b50c56fa3753c345533ddfaa1cfd23b7fdf6771010b3ae\",\n            \"0x055a53b13f4ef2982200bcee18cad82631591d7537e81f9f6d0d9af73919aca7\",\n            \"0x055992fb3e68a839041d15f16baee9aac780d3cc027c2650a8fa07d8c708ffa4\",\n            \"0x01288fca758908fd75be91359aae9947105255cb57e3737ba4cd2cb249b9568b\",\n            \"0x057b12a3c876f530ff3416b1f37b57888c294abfca1add3c80920e715ebd3dce\",\n            \"0x128bda5bfdc7fd9f0bcd7a4590e6367ac7d02e478e7fe7b4effcc682fee6ac4e\",\n            \"0x15fa52c718251448f4a7b5e5a51b8983626f542e76616ec2a97b51a9ca55acaa\",\n            \"0x2e1e139d30e7837e3a9c17b1cfabb68449abe4a71d09929789ba75a389477d5b\",\n            \"0x18d881eb1a3059a451819fc43526ad05f7144db79e00c502abb2999c9677e5eb\",\n            \"0x0497036ec879338b7800d9c8785fd34de9677ccba2b989725a258156fecaeddc\",\n            \"0x2a58721ef44e23f723b2f6f0c767346ee25de6a9ce3be5143c45bf02700f6067\",\n            \"0x241e67371586327ed2947f58dc70148a99a32ae01d5f67a82bba22edc0c67134\",\n            \"0x11fb89af7e2c3df6b15be924a02551ebb634e49577dd20c759a6da3a058acf9e\"\n          ]\n        }\n      },\n      \"point\": \"0x25aca9a399b1284f8cbbdc0428e87d584e064bc8656d2e0a7388180efa910125\",\n      \"opening\": \"0x11bea2be37f4170fa49da99c65c6a139e07976c36cc5e35e2735ed5b11e8a86f\",\n      \"commitment\": {\n        \"x\": \"0xd2b3853c2faafac946818a717533f3b12ab292173d52c1a9015cdf18ef87827\",\n        \"y\": \"0x21b7650147538e48bab652f15aece419695f7374c8e8bfae869451bb50f387ec\"\n      }\n    },\n    {\n      \"poly\": {\n        \"coefficients\": {\n          \"coefficients\": [\n            \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n            \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n            \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n            \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n            \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n            \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n            \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n            \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n            \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n            \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n            \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n            \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n            \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n            \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n            \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n            \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\"\n          ]\n        }\n      },\n      \"point\": \"0x1878ab885c4eeb02752b8e5e37413b6ac4095ece8af445a9f105912aba6091a6\",\n      \"opening\": \"0x167e2486350b9d6096379aad44fdc662e020b922458f47bfeff7b825548ccc88\",\n      \"commitment\": {\n        \"x\": \"0xf9cc629d0010671d7f755267acccfc8d5854f47d4e437ec84bddcc27b9f19d1\",\n        \"y\": \"0x199b1dcc7aff518a4e49c6393bcf847cc3fde52ae69e326dea0d1d901424552a\"\n      }\n    },\n    {\n      \"poly\": {\n        \"coefficients\": {\n          \"coefficients\": [\n            \"0x27517fbd56f85221e5c138a4493917cbb0aa2cbae2e6ab760727978833000001\",\n            \"0x2e2956f8332a2abea8eae65e83211a8cfd4c9c38c6ed5c09186cafec19009edf\",\n            \"0x2014447de15a99b6df03833e95f96ae1299c9ec6ff990b6e75fa3b3b04846a57\",\n            \"0x130034a5a3705c18e67b698f576257c783c3403058f57e9a359495efd00ce8cf\",\n            \"0x2144f5eefad21e1ca37ae273a4ee5b4a7eef137cbbeb1d198c9e59c37ef70364\",\n            \"0x11ded077258dd59f58ab8525c92955ebcb2b1905e676b2fe47ca20d6919e5e16\",\n            \"0x02b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e8\",\n            \"0x152fae7ca9f4520815c46c7ae6996e0cd78f9e211ed4ffa8d8d48d83b3a445cd\",\n            \"0x0912ceb58a394e07d28f0d12384840917789bb8d96d2c51b3cba5e0bbd000000\",\n            \"0x023af77aae07756b0f655f57fe603dd02ae74c0fb2cc14882b7545a7d6ff6122\",\n            \"0x105009f4ffd70672d94cc277eb87ed7bfe9749817a206522cde7ba58eb7b95aa\",\n            \"0x1d6419cd3dc14410d1d4dc272a1f0095a470a81820c3f1f70e4d5fa41ff31732\",\n            \"0x0f1f5883e65f820d14d56342dc92fd12a944d4cbbdce5377b7439bd07108fc9d\",\n            \"0x1e857dfbbba3ca8a5fa4c090b85802715d08cf429342bd92fc17d4bd5e61a1eb\",\n            \"0x2db11694c4a58b3789868bd388165969c30dc1120a37fff09a991abf43e42a19\",\n            \"0x1b349ff6373d4e21a28bd93b9ae7ea5050a44a275ae470e86b0d68103c5bba34\"\n          ]\n        }\n      },\n      \"point\": \"0x1878ab885c4eeb02752b8e5e37413b6ac4095ece8af445a9f105912aba6091a6\",\n      \"opening\": \"0x2ce9cf1f952831ab654748c6bf472d5ac137b164c079a05eaa20485176e6d319\",\n      \"commitment\": {\n        \"x\": \"0x10472018b5bfdcc76f3925ea4f660dc7167ef12c96fb70f686d0c1cf7791cde5\",\n        \"y\": \"0x358f44f7cb29a8d129dbe6b61fc1d921a903f1e9209abc137c7a6446c3ae38f\"\n      }\n    },\n    {\n      \"poly\": {\n        \"coefficients\": {\n          \"coefficients\": [\n            \"0x231004c8da62398dea4f1e40d0e808b9bd12c67de122f895bf270053460efc8e\",\n            \"0x231004c8da62398dea4f1e40d0e808b9bd12c67de122f895bf270053460efc8f\",\n            \"0x231004c8da62398dea4f1e40d0e808b9bd12c67de122f895bf270053460efc8e\",\n            \"0x231004c8da62398dea4f1e40d0e808b9bd12c67de122f895bf270053460efc8e\",\n            \"0x231004c8da62398dea4f1e40d0e808b9bd12c67de122f895bf270053460efc8e\",\n            \"0x231004c8da62398dea4f1e40d0e808b9bd12c67de122f895bf270053460efc8e\",\n            \"0x231004c8da62398dea4f1e40d0e808b9bd12c67de122f895bf270053460efc8e\",\n            \"0x231004c8da62398dea4f1e40d0e808b9bd12c67de122f895bf270053460efc8e\",\n            \"0x231004c8da62398dea4f1e40d0e808b9bd12c67de122f895bf270053460efc8e\",\n            \"0x231004c8da62398dea4f1e40d0e808b9bd12c67de122f895bf270053460efc8e\",\n            \"0x231004c8da62398dea4f1e40d0e808b9bd12c67de122f895bf270053460efc8e\",\n            \"0x231004c8da62398dea4f1e40d0e808b9bd12c67de122f895bf270053460efc8e\",\n            \"0x231004c8da62398dea4f1e40d0e808b9bd12c67de122f895bf270053460efc8e\",\n            \"0x231004c8da62398dea4f1e40d0e808b9bd12c67de122f895bf270053460efc8e\",\n            \"0x231004c8da62398dea4f1e40d0e808b9bd12c67de122f895bf270053460efc8e\",\n            \"0x231004c8da62398dea4f1e40d0e808b9bd12c67de122f895bf270053460efc8e\"\n          ]\n        }\n      },\n      \"point\": \"0x1878ab885c4eeb02752b8e5e37413b6ac4095ece8af445a9f105912aba6091a6\",\n      \"opening\": \"0x272ba02794f3cb184151a79e4441c7aa7a50b8adf272f508e38796c23389a82c\",\n      \"commitment\": {\n        \"x\": \"0x365b8986f1c38476aa6479eea1b688244b4070413eb393efa5b06441ac2aeaa\",\n        \"y\": \"0x303ed0aaed99cb2848d844239ab4ab9a9191b86544edab860c42a2d4e504cf34\"\n      }\n    },\n    {\n      \"poly\": {\n        \"coefficients\": {\n          \"coefficients\": [\n            \"0x2d82db36686efc9851aae360b50a578473c5776bc63a0f783c153515690a52c4\",\n            \"0x0640f831aa044d38fe46c45508516d98a6f118b1ab16de0c7f41963d5e3e3c65\",\n            \"0x2d82db36686efc9851aae360b50a578473c5776bc63a0f783c153515690a52c4\",\n            \"0x2d82db36686efc9851aae360b50a578473c5776bc63a0f783c153515690a52c4\",\n            \"0x2d82db36686efc9851aae360b50a578473c5776bc63a0f783c153515690a52c4\",\n            \"0x2d82db36686efc9851aae360b50a578473c5776bc63a0f783c153515690a52c4\",\n            \"0x2d82db36686efc9851aae360b50a578473c5776bc63a0f783c153515690a52c4\",\n            \"0x2d82db36686efc9851aae360b50a578473c5776bc63a0f783c153515690a52c4\",\n            \"0x2d82db36686efc9851aae360b50a578473c5776bc63a0f783c153515690a52c4\",\n            \"0x2d82db36686efc9851aae360b50a578473c5776bc63a0f783c153515690a52c4\",\n            \"0x2d82db36686efc9851aae360b50a578473c5776bc63a0f783c153515690a52c4\",\n            \"0x2d82db36686efc9851aae360b50a578473c5776bc63a0f783c153515690a52c4\",\n            \"0x2d82db36686efc9851aae360b50a578473c5776bc63a0f783c153515690a52c4\",\n            \"0x2d82db36686efc9851aae360b50a578473c5776bc63a0f783c153515690a52c4\",\n            \"0x2d82db36686efc9851aae360b50a578473c5776bc63a0f783c153515690a52c4\",\n            \"0x2d82db36686efc9851aae360b50a578473c5776bc63a0f783c153515690a52c4\"\n          ]\n        }\n      },\n      \"point\": \"0x1878ab885c4eeb02752b8e5e37413b6ac4095ece8af445a9f105912aba6091a6\",\n      \"opening\": \"0x2216922012e3bd8e54f70dc0cb070cbc74e00067d3315ab08314e24b98a7c8f2\",\n      \"commitment\": {\n        \"x\": \"0x1af13f7dc79a97c1a690215e2ffd3d8386f682fe1a13bdb8588d2fbaa0161edc\",\n        \"y\": \"0x10f0c0ddf9db782e88deb3e49464292b1276b2b9ad29b5a25971c85fe0b8aa96\"\n      }\n    },\n    {\n      \"poly\": {\n        \"coefficients\": {\n          \"coefficients\": [\n            \"0x18038fb09dafe4561b7c81a4ddecb5a9fdec9905c914eb53873760ec477b6153\",\n            \"0x01330c7cbea5f36f17d3698ffdacd0e8d561fc0253004050c673cb4825a5d678\",\n            \"0x11bd06280c7c8a6daa71eb9d3b2c97f9f211a37a41798bc63f78d5af70135d20\",\n            \"0x021ebc7877f2b98917ec49b986e1a497743d0464e2e1cf787fcc843b013626bb\",\n            \"0x2965a7e65ce635bbab94a2fffae4c86626d67c0eb10d658380f782f08db31ef9\",\n            \"0x13d9914eeb3084263023150484b8da37654dc6d57f82afffca2739b48de541c3\",\n            \"0x150b22b022aca6d5eacd65a0435fe2b487242afc5df5b9a417a001d5a79554a0\",\n            \"0x1911d0efc0d670781ad228d1df69e12aa502675c2c2b0873421dc9f32f16364f\",\n            \"0x173bca2a5af56060d72e1cf66a4c15350c7a1d0985d14232e9c548bf83a8e270\",\n            \"0x1761ac780a2688e48980ee5f7bb6c6489891ad7207f1b53c9a9aecdf3ca68117\",\n            \"0x2af4468570b8b17c26c074b53131f4dab8f4abc05ac204dfee977cd8f2ec9c0e\",\n            \"0x29a949ffe203c1114c6cc31a6b99269932f7cd7340abbe797c0d6c99a7f802f1\",\n            \"0x2f93c69781f9dd1bcc42e4149107242b847da4d909a47893ebe85b9a1fe5700e\",\n            \"0x2a7dc0ef215246ec520423ffc5f6e6fa3d5c0144e341d3358dd64ce8e648afff\",\n            \"0x134ac81b3f8e47b083bd4a759f2e5be3ec6cdfc0d4b28794740f1a3096f24a6d\",\n            \"0x2115cec3e5a7af5c4f4829c80b9825c24f499de351535dc35cb607a47b85c1b7\"\n          ]\n        }\n      },\n      \"point\": \"0x1878ab885c4eeb02752b8e5e37413b6ac4095ece8af445a9f105912aba6091a6\",\n      \"opening\": \"0x0d4e76b700e0ccf22194dba6a70f03d24a1ced8e93a333d5b7e83535e4195c01\",\n      \"commitment\": {\n        \"x\": \"0x9837455c613e5b0e0edd2a1d47f1efdee21eeeda00548f3686c733301e23da4\",\n        \"y\": \"0x2e2b7776741eb2214916eb80ae0982c32b02e57e83c51e8907b5ffeab048bf2d\"\n      }\n    },\n    {\n      \"poly\": {\n        \"coefficients\": {\n          \"coefficients\": [\n            \"0x28967ba8c313c600d17a4ddd20a4f32f49d6e1e9fcba5e5249324a66d96b4f5e\",\n            \"0x00a354bfcc60f6a39aa68e9a9fa36f4f2bc9322db22d4c76b20738f12376fa40\",\n            \"0x2d0e02c7383ab7a8d1dc324434cbc0efa7c7da0a5c10f1008c072ad9284c951b\",\n            \"0x295fc9ac7c7023fa3374d3df0959578ede07bc8e8e32a7d46b5928ac0f6051c7\",\n            \"0x2be457106435d313da2cbe5c1304c9b5eac6cef2e824ab55be7e094f779a7680\",\n            \"0x11d0a6eb25ba5944c820e99c5b6bfeacbaed201b58985a3e47ac129d746acb2d\",\n            \"0x10f99e36b6e6090fddea8d5f4048b35d3bf6c5f9b4b84f83c3f8695c1fc00a84\",\n            \"0x1269a9b9c8dd3f9f671c12f107a14cb0be65a15412dc8d8b0fb139184bfbd050\",\n            \"0x07cdd2ca1e1dda28e6d5f7d960dc652dde5d065e7cff123efaafab2d1694b0a3\",\n            \"0x180c8a641d5c9c5ecc993345d7012385aa929f610a2038d81f81daaa87ba4412\",\n            \"0x03564baba8f6e880e67413724cb5976d806c0e3e1da87f90b7dacabac7b36ae6\",\n            \"0x070484c664c17c2f84db71d7782800ce4a2c2bb9eb86c8bcd888cce7e09fae3a\",\n            \"0x047ff7627cfbcd15de23875a6e7c8ea73d6d19559194c53b8563ec4478658981\",\n            \"0x1e93a787bb7746e4f02f5c1a261559b06d46c82d21211652fc35e2f67b9534d4\",\n            \"0x1f6ab03c2a4b9719da65b8574138a4ffec3d224ec501210d7fe98c37d03ff57d\",\n            \"0x1dfaa4b91854608a513432c579e00bac69ce46f466dce3063430bc7ba4042fb1\"\n          ]\n        }\n      },\n      \"point\": \"0x1878ab885c4eeb02752b8e5e37413b6ac4095ece8af445a9f105912aba6091a6\",\n      \"opening\": \"0x0f8442c9f3ec7b7470dcbce83ba923dc83fb1bb28e524fcfd4012abb99d2cab2\",\n      \"commitment\": {\n        \"x\": \"0x23e033260e2f2ed7dd27295a06d951fad7c1545a493f24e8d7f416ccc3c74670\",\n        \"y\": \"0x957c9d3b0c00ff783bc3357dd0b5cf891f49bc78f569126ba6e68bc394859ef\"\n      }\n    },\n    {\n      \"poly\": {\n        \"coefficients\": {\n          \"coefficients\": [\n            \"0x1f615c09076e36cec6b73da310b463828347b51b9e9974e8a81b062077548f09\",\n            \"0x01486375680eea6fc3a85823dfcd197786e698ced109f6b67e5e4b7215da08b5\",\n            \"0x037aa90da674af38268ed2f31dc3e710a15056f17e299751c5b04c08c70745bc\",\n            \"0x2f1bbf9df033d50aeb8c582ad2f78cc3f663caed5e7fd352e9139adf69631d3f\",\n            \"0x1f1024523b7e2ab8e444ab4342eb4783f2b36904cb7d4fc9cc89d9aebcb8a4c9\",\n            \"0x2ede108bc37f0bde8453e3259fdabb172f2852e292ddf42acfb7597f28ce7fd1\",\n            \"0x2ad79d128ed1b9078ffe409f5a8f0d5e03a60c35406c2e9dc875f100a8d66538\",\n            \"0x1c2185aa71f806854eb5f14ae1fb874e45eaf891c70cfd904c5e3e27572d647f\",\n            \"0x2b669394f456de8527a6733d5968b33832183cb941aafd5b821f4b146d29b5eb\",\n            \"0x1ef290e15b09f3f7b360569a3f5dfbf3099b5158a6b240c72bfae22a700a2583\",\n            \"0x2cd0e32fa58075c49f47f5d62f1ccf5a730c66107069547ec7adf8514f1d9961\",\n            \"0x02435c65a144dce7011af52c8bb7d13ba7df98d8b6a3dc749e2ad97047653ff4\",\n            \"0x15afcc6459a9173a3655635a693b5a8b90d355ea1c6f5bc303f64053458d3c88\",\n            \"0x234d4da6f81007d8fbbdc4ca041dfd7042b4d2c2b37703bb6ef1d2b1a64719bf\",\n            \"0x08576f2d1e32d8ce8bd2e71b5556b34147fef4f0b20f13379b3c1847244ab3dc\",\n            \"0x047503ee3fc5bcf4a401f7a5ef1f6b461a7304208882e61bac5913b563f5b42e\"\n          ]\n        }\n      },\n      \"point\": \"0x1878ab885c4eeb02752b8e5e37413b6ac4095ece8af445a9f105912aba6091a6\",\n      \"opening\": \"0x2f027ef35645ae4b15f229f4e6edf60cfe7166cc803fa1243fde06a9a0a67009\",\n      \"commitment\": {\n        \"x\": \"0x108c4b342af988a9a1da9594fa34a4744e79ccce4b8ac04d7bda5294569808a3\",\n        \"y\": \"0x1a420c577e9605038605167a60cf192a637fc575cb2057268122a003cfbfc61\"\n      }\n    },\n    {\n      \"poly\": {\n        \"coefficients\": {\n          \"coefficients\": [\n            \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n            \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n            \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n            \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n            \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n            \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n            \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n            \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n            \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n            \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n            \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n            \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n            \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n            \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n            \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n            \"0x0000000000000000000000000000000000000000000000000000000000000000\"\n          ]\n        }\n      },\n      \"point\": \"0x1878ab885c4eeb02752b8e5e37413b6ac4095ece8af445a9f105912aba6091a6\",\n      \"opening\": \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n      \"commitment\": {\n        \"x\": \"0x1\",\n        \"y\": \"0x2\"\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "tachyon/crypto/commitments/univariate_polynomial_commitment_scheme.h",
    "content": "#ifndef TACHYON_CRYPTO_COMMITMENTS_UNIVARIATE_POLYNOMIAL_COMMITMENT_SCHEME_H_\n#define TACHYON_CRYPTO_COMMITMENTS_UNIVARIATE_POLYNOMIAL_COMMITMENT_SCHEME_H_\n\n#include <stddef.h>\n\n#include \"tachyon/crypto/commitments/vector_commitment_scheme.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluations.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_polynomial.h\"\n\nnamespace tachyon::crypto {\n\ntemplate <typename Derived>\nclass UnivariatePolynomialCommitmentScheme\n    : public VectorCommitmentScheme<Derived> {\n public:\n  constexpr static size_t kMaxDegree =\n      VectorCommitmentScheme<Derived>::kMaxSize - 1;\n\n  using Field = typename VectorCommitmentScheme<Derived>::Field;\n  using Commitment = typename VectorCommitmentScheme<Derived>::Commitment;\n  using Poly = math::UnivariateDensePolynomial<Field, kMaxDegree>;\n  using Evals = math::UnivariateEvaluations<Field, kMaxDegree>;\n  using Domain = math::UnivariateEvaluationDomain<Field, kMaxDegree>;\n\n  size_t D() const {\n    const Derived* derived = static_cast<const Derived*>(this);\n    return derived->N() - 1;\n  }\n\n  // Commit to |poly| and populates |result| with the commitment.\n  // Return false if the degree of |poly| exceeds |kMaxDegree|.\n  [[nodiscard]] bool Commit(const Poly& poly, Commitment* result) const {\n    const Derived* derived = static_cast<const Derived*>(this);\n    return derived->DoCommit(poly, result);\n  }\n\n  // Commit to |poly| and stores the commitment in |batch_commitments_| at\n  // |index| if |batch_mode| is true. Return false if the degree of |poly|\n  // exceeds |kMaxDegree|. It terminates when |batch_mode| is false.\n  template <typename T = Derived, std::enable_if_t<VectorCommitmentSchemeTraits<\n                                      T>::kSupportsBatchMode>* = nullptr>\n  [[nodiscard]] bool Commit(const Poly& poly, size_t index) {\n    Derived* derived = static_cast<Derived*>(this);\n    CHECK(derived->GetBatchMode());\n    return derived->DoCommit(poly, derived->batch_commitment_state(), index);\n  }\n\n  // Commit to |evals| and populates |result| with the commitment.\n  // Return false if the degree of |evals| exceeds |kMaxDegree|.\n  [[nodiscard]] bool CommitLagrange(const Evals& evals,\n                                    Commitment* result) const {\n    const Derived* derived = static_cast<const Derived*>(this);\n    return derived->DoCommitLagrange(evals, result);\n  }\n\n  // Commit to |evals| and stores the commitment in |batch_commitments_| at\n  // |index| if |batch_mode| is true. Return false if the degree of |evals|\n  // exceeds |kMaxDegree|. It terminates when |batch_mode| is false.\n  template <typename T = Derived, std::enable_if_t<VectorCommitmentSchemeTraits<\n                                      T>::kSupportsBatchMode>* = nullptr>\n  [[nodiscard]] bool CommitLagrange(const Evals& evals, size_t index) {\n    Derived* derived = static_cast<Derived*>(this);\n    CHECK(derived->GetBatchMode());\n    return derived->DoCommitLagrange(evals, derived->batch_commitment_state(),\n                                     index);\n  }\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_COMMITMENTS_UNIVARIATE_POLYNOMIAL_COMMITMENT_SCHEME_H_\n"
  },
  {
    "path": "tachyon/crypto/commitments/vector_commitment_scheme.h",
    "content": "#ifndef TACHYON_CRYPTO_COMMITMENTS_VECTOR_COMMITMENT_SCHEME_H_\n#define TACHYON_CRYPTO_COMMITMENTS_VECTOR_COMMITMENT_SCHEME_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include \"tachyon/base/bits.h\"\n#include \"tachyon/crypto/commitments/batch_commitment_state.h\"\n#include \"tachyon/crypto/commitments/vector_commitment_scheme_traits_forward.h\"\n\nnamespace tachyon::crypto {\n\ntemplate <typename Derived>\nclass VectorCommitmentScheme {\n public:\n  constexpr static size_t kMaxSize =\n      VectorCommitmentSchemeTraits<Derived>::kMaxSize;\n  constexpr static bool kIsTransparent =\n      VectorCommitmentSchemeTraits<Derived>::kIsTransparent;\n  constexpr static bool kSupportsBatchMode =\n      VectorCommitmentSchemeTraits<Derived>::kSupportsBatchMode;\n\n  using Field = typename VectorCommitmentSchemeTraits<Derived>::Field;\n  using Commitment = typename VectorCommitmentSchemeTraits<Derived>::Commitment;\n\n  uint32_t K() const {\n    const Derived* derived = static_cast<const Derived*>(this);\n    return base::bits::SafeLog2Ceiling(derived->N());\n  }\n\n  BatchCommitmentState& batch_commitment_state() {\n    return batch_commitment_state_;\n  }\n  bool GetBatchMode() const { return batch_commitment_state_.batch_mode; }\n\n  template <typename T = Derived, std::enable_if_t<VectorCommitmentSchemeTraits<\n                                      T>::kSupportsBatchMode>* = nullptr>\n  void SetBatchMode(size_t batch_count) {\n    if (batch_count == 0) return;\n    CHECK_EQ(batch_commitment_state_.batch_count, size_t{0});\n    batch_commitment_state_ = BatchCommitmentState(true, batch_count);\n    Derived* derived = static_cast<Derived*>(this);\n    derived->ResizeBatchCommitments();\n  }\n\n  // Initialize parameters.\n  template <typename T = Derived,\n            std::enable_if_t<VectorCommitmentSchemeTraits<T>::kIsTransparent>* =\n                nullptr>\n  [[nodiscard]] bool Setup() {\n    return Setup(kMaxSize);\n  }\n\n  template <typename T = Derived,\n            std::enable_if_t<VectorCommitmentSchemeTraits<T>::kIsTransparent>* =\n                nullptr>\n  [[nodiscard]] bool Setup(size_t size) {\n    Derived* derived = static_cast<Derived*>(this);\n    return derived->DoSetup(size);\n  }\n\n  // Initialize parameters.\n  template <typename T = Derived,\n            std::enable_if_t<\n                !VectorCommitmentSchemeTraits<T>::kIsTransparent>* = nullptr>\n  [[nodiscard]] bool UnsafeSetup() {\n    return UnsafeSetup(kMaxSize);\n  }\n\n  template <typename T = Derived,\n            std::enable_if_t<\n                !VectorCommitmentSchemeTraits<T>::kIsTransparent>* = nullptr>\n  [[nodiscard]] bool UnsafeSetup(size_t size) {\n    Derived* derived = static_cast<Derived*>(this);\n    return derived->DoUnsafeSetup(size);\n  }\n\n  template <typename T = Derived, typename Params,\n            std::enable_if_t<\n                !VectorCommitmentSchemeTraits<T>::kIsTransparent>* = nullptr>\n  [[nodiscard]] bool UnsafeSetup(size_t size, const Params& params) {\n    Derived* derived = static_cast<Derived*>(this);\n    return derived->DoUnsafeSetup(size, params);\n  }\n\n  // Commit to |container| and populates |result| with the commitment.\n  // Return false if the size of |container| doesn't match with the size of\n  // parameters.\n  template <typename Container>\n  [[nodiscard]] bool Commit(const Container& container,\n                            Commitment* result) const {\n    const Derived* derived = static_cast<const Derived*>(this);\n    return derived->DoCommit(container, result);\n  }\n\n  // Commit to |container| and stores the commitment in |batch_commitments_| at\n  // |index| if |batch_mode| is true. Return false if the size of |container|\n  // doesn't match with the size of parameters. It terminates when |batch_mode|\n  // is false.\n  template <typename T = Derived, typename Container,\n            std::enable_if_t<\n                VectorCommitmentSchemeTraits<T>::kSupportsBatchMode>* = nullptr>\n  [[nodiscard]] bool Commit(const Container& container, size_t index) {\n    Derived* derived = static_cast<Derived*>(this);\n    CHECK(batch_commitment_state_.batch_mode);\n    return derived->DoCommit(container, batch_commitment_state_, index);\n  }\n\n  // Commit to |container| with a |random_value| and populates |result| with the\n  // commitment. Return false if the size of |container| doesn't match with the\n  // size of parameters.\n  template <typename Container>\n  [[nodiscard]] bool Commit(const Container& container,\n                            const Field& random_value,\n                            Commitment* result) const {\n    const Derived* derived = static_cast<const Derived*>(this);\n    return derived->DoCommit(container, random_value, result);\n  }\n\n  // Commit to |container| with a |random_value| and stores the commitment in\n  // |batch_commitments_| at |index| if |batch_mode| is true. Return false if\n  // the size of |container| doesn't match with the size of parameters. It\n  // terminates when |batch_mode| is false.\n  template <typename T = Derived, typename Container,\n            std::enable_if_t<\n                VectorCommitmentSchemeTraits<T>::kSupportsBatchMode>* = nullptr>\n  [[nodiscard]] bool Commit(const Container& container,\n                            const Field& random_value, size_t index) {\n    Derived* derived = static_cast<Derived*>(this);\n    CHECK(batch_commitment_state_.batch_mode);\n    return derived->DoCommit(container, random_value, batch_commitment_state_,\n                             index);\n  }\n\n  // Create an opening proof that proves that |members| belong to a\n  // commitment.\n  template <typename Container, typename Proof>\n  [[nodiscard]] bool CreateOpeningProof(const Container& members,\n                                        Proof* proof) {\n    Derived* derived = static_cast<Derived*>(this);\n    return derived->DoCreateOpeningProof(members, proof);\n  }\n\n  // Verify an opening |proof| that proves that |members| belong to a\n  // |commitment|.\n  // NOTE(chokobole): const was removed from |Commitment| since it can be a\n  // |Transcript|. At this moment, |WriteToTranscript()| is not a const method.\n  template <typename Container, typename Proof>\n  [[nodiscard]] bool VerifyOpeningProof(Commitment& commitment,\n                                        const Container& members,\n                                        const Proof& proof) const {\n    const Derived* derived = static_cast<const Derived*>(this);\n    return derived->DoVerifyOpeningProof(commitment, members, proof);\n  }\n\n  // Verify multi-openings |proof|.\n  template <typename Container, typename Proof>\n  [[nodiscard]] bool VerifyOpeningProof(const Container& members,\n                                        Proof* proof) const {\n    const Derived* derived = static_cast<const Derived*>(this);\n    return derived->DoVerifyOpeningProof(members, proof);\n  }\n\n protected:\n  BatchCommitmentState batch_commitment_state_;\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_COMMITMENTS_VECTOR_COMMITMENT_SCHEME_H_\n"
  },
  {
    "path": "tachyon/crypto/commitments/vector_commitment_scheme_traits_forward.h",
    "content": "#ifndef TACHYON_CRYPTO_COMMITMENTS_VECTOR_COMMITMENT_SCHEME_TRAITS_FORWARD_H_\n#define TACHYON_CRYPTO_COMMITMENTS_VECTOR_COMMITMENT_SCHEME_TRAITS_FORWARD_H_\n\nnamespace tachyon::crypto {\n\ntemplate <typename VCS>\nstruct VectorCommitmentSchemeTraits;\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_COMMITMENTS_VECTOR_COMMITMENT_SCHEME_TRAITS_FORWARD_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"compressor\",\n    hdrs = [\"compressor.h\"],\n)\n\ntachyon_cc_library(\n    name = \"hasher\",\n    hdrs = [\"hasher.h\"],\n)\n\ntachyon_cc_library(\n    name = \"prime_field_serializable\",\n    hdrs = [\"prime_field_serializable.h\"],\n    deps = [\n        \"//tachyon/base/buffer\",\n        \"//tachyon/math/base:big_int\",\n        \"//tachyon/math/finite_fields:finite_field_traits\",\n        \"@com_google_absl//absl/container:inlined_vector\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"serializable_unittests\",\n    srcs = [\n        \"prime_field_serializable_unittest.cc\",\n    ],\n    deps = [\n        \":prime_field_serializable\",\n        \"//tachyon/math/finite_fields/test:finite_field_test\",\n        \"//tachyon/math/finite_fields/test:gf7\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/crypto/hashes/compressor.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_CRYPTO_HASHES_COMPRESSOR_H_\n#define TACHYON_CRYPTO_HASHES_COMPRESSOR_H_\n\nnamespace tachyon::crypto {\n\ntemplate <typename _Derived>\nclass Compressor {\n public:\n  using Derived = _Derived;\n\n  // TODO(chokobole): Make this accept iterator as an argument.\n  template <typename T>\n  auto Compress(const T& input) const {\n    const Derived& derived = static_cast<const Derived&>(*this);\n    auto state = derived.CreateEmptyState();\n    return derived.DoCompress(state, input);\n  }\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_HASHES_COMPRESSOR_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/hasher.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_CRYPTO_HASHES_HASHER_H_\n#define TACHYON_CRYPTO_HASHES_HASHER_H_\n\nnamespace tachyon::crypto {\n\ntemplate <typename _Derived>\nclass Hasher {\n public:\n  using Derived = _Derived;\n\n  // TODO(chokobole): Make this accept iterator as an argument.\n  template <typename T>\n  auto Hash(const T& input) const {\n    const Derived& derived = static_cast<const Derived&>(*this);\n    auto state = derived.CreateEmptyState();\n    return derived.DoHash(state, input);\n  }\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_HASHES_HASHER_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/prime_field_serializable.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_CRYPTO_HASHES_PRIME_FIELD_SERIALIZABLE_H_\n#define TACHYON_CRYPTO_HASHES_PRIME_FIELD_SERIALIZABLE_H_\n\n#include <array>\n#include <type_traits>\n#include <vector>\n\n#include \"absl/container/inlined_vector.h\"\n#include \"absl/types/span.h\"\n\n#include \"tachyon/math/base/big_int.h\"\n#include \"tachyon/math/finite_fields/finite_field_traits.h\"\n\nnamespace tachyon::crypto {\n\ntemplate <typename T, typename SFINAE = void>\nclass PrimeFieldSerializable;\n\ntemplate <typename T>\nclass PrimeFieldSerializable<T, std::enable_if_t<std::is_integral_v<T>>> {\n public:\n  template <typename PrimeField>\n  constexpr static bool ToPrimeField(const T& value,\n                                     std::vector<PrimeField>* fields) {\n    if constexpr (PrimeField::Config::kModulusBits <= 32) {\n      if (value >= PrimeField::Config::kModulus) return false;\n    } else {\n      using BigInt = typename PrimeField::BigIntTy;\n      if (BigInt(value) >= PrimeField::Config::kModulus) return false;\n    }\n    fields->push_back(PrimeField(value));\n    return true;\n  }\n\n  template <typename PrimeField>\n  constexpr static bool BatchToPrimeField(absl::Span<const T> values,\n                                          std::vector<PrimeField>* fields) {\n    for (const T& value : values) {\n      if (!PrimeFieldSerializable<T>::ToPrimeField(value, fields)) return false;\n    }\n    return true;\n  }\n};\n\ntemplate <typename T>\nclass PrimeFieldSerializable<\n    T, std::enable_if_t<math::FiniteFieldTraits<T>::kIsPrimeField>> {\n public:\n  constexpr static bool ToPrimeField(const T& value, std::vector<T>* fields) {\n    fields->push_back(value);\n    return true;\n  }\n\n  constexpr static bool BatchToPrimeField(absl::Span<const T> values,\n                                          std::vector<T>* fields) {\n    for (const T& value : values) {\n      if (!PrimeFieldSerializable<T>::ToPrimeField(value, fields)) return false;\n    }\n    return true;\n  }\n};\n\ntemplate <typename T>\nclass PrimeFieldSerializable<std::vector<T>> {\n public:\n  template <typename PrimeField>\n  constexpr static bool ToPrimeField(const std::vector<T>& values,\n                                     std::vector<PrimeField>* fields) {\n    return PrimeFieldSerializable<T>::BatchToPrimeField(values, fields);\n  }\n};\n\ntemplate <typename T, size_t N>\nclass PrimeFieldSerializable<absl::InlinedVector<T, N>> {\n public:\n  template <typename PrimeField>\n  constexpr static bool ToPrimeField(const absl::InlinedVector<T, N>& values,\n                                     std::vector<PrimeField>* fields) {\n    return PrimeFieldSerializable<T>::BatchToPrimeField(values, fields);\n  }\n};\n\ntemplate <typename T>\nclass PrimeFieldSerializable<absl::Span<T>> {\n public:\n  template <typename PrimeField>\n  constexpr static bool ToPrimeField(absl::Span<T> values,\n                                     std::vector<PrimeField>* fields) {\n    return PrimeFieldSerializable<std::remove_const_t<T>>::BatchToPrimeField(\n        values, fields);\n  }\n};\n\ntemplate <typename T, size_t N>\nclass PrimeFieldSerializable<std::array<T, N>> {\n public:\n  template <typename PrimeField>\n  constexpr static bool ToPrimeField(const std::array<T, N>& values,\n                                     std::vector<PrimeField>* fields) {\n    return PrimeFieldSerializable<T>::BatchToPrimeField(values, fields);\n  }\n};\n\ntemplate <typename T, typename PrimeField>\nconstexpr bool SerializeToFieldElements(const T& value,\n                                        std::vector<PrimeField>* fields) {\n  return PrimeFieldSerializable<T>::ToPrimeField(value, fields);\n}\n\ntemplate <typename T, typename PrimeField>\nconstexpr bool SerializeBatchToFieldElements(absl::Span<const T> values,\n                                             std::vector<PrimeField>* fields) {\n  return PrimeFieldSerializable<T>::BatchToPrimeField(values, fields);\n}\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_HASHES_PRIME_FIELD_SERIALIZABLE_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/prime_field_serializable_unittest.cc",
    "content": "#include \"tachyon/crypto/hashes/prime_field_serializable.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n\nnamespace tachyon::crypto {\n\nnamespace {\n\nclass PrimeFieldSerializableTest : public math::FiniteFieldTest<math::GF7> {};\n\n}  // namespace\n\nTEST_F(PrimeFieldSerializableTest, SerializeSingleValueToField) {\n  constexpr int64_t kValue = 5;\n  std::vector<math::GF7> fields;\n  ASSERT_TRUE(SerializeToFieldElements(kValue, &fields));\n  std::vector<math::GF7> expected = {math::GF7(5)};\n  EXPECT_EQ(fields, expected);\n}\n\nTEST_F(PrimeFieldSerializableTest, SerializeVectorToField) {\n  std::vector<int64_t> values = {1, 2, 3, 4, 5};\n  std::vector<math::GF7> fields;\n  ASSERT_TRUE(SerializeToFieldElements(values, &fields));\n  std::vector<math::GF7> expected = {\n      math::GF7(1), math::GF7(2), math::GF7(3), math::GF7(4), math::GF7(5),\n  };\n  EXPECT_EQ(fields, expected);\n}\n\nTEST_F(PrimeFieldSerializableTest, SerializeArrayToField) {\n  std::array<int64_t, 5> values = {1, 2, 3, 4, 5};\n  std::vector<math::GF7> fields;\n  ASSERT_TRUE(SerializeToFieldElements(values, &fields));\n  std::vector<math::GF7> expected = {\n      math::GF7(1), math::GF7(2), math::GF7(3), math::GF7(4), math::GF7(5),\n  };\n  EXPECT_EQ(fields, expected);\n}\n\nTEST_F(PrimeFieldSerializableTest, SerializeInlinedVectorToField) {\n  absl::InlinedVector<int64_t, 5> values = {1, 2, 3, 4, 5};\n  std::vector<math::GF7> fields;\n  ASSERT_TRUE(SerializeToFieldElements(values, &fields));\n  std::vector<math::GF7> expected = {\n      math::GF7(1), math::GF7(2), math::GF7(3), math::GF7(4), math::GF7(5),\n  };\n  EXPECT_EQ(fields, expected);\n}\n\nTEST_F(PrimeFieldSerializableTest, SerializeBatchToField) {\n  std::vector<int64_t> values = {1, 2, 3, 4, 5};\n  std::vector<math::GF7> fields;\n  ASSERT_TRUE(\n      SerializeBatchToFieldElements(absl::MakeConstSpan(values), &fields));\n  std::vector<math::GF7> expected = {\n      math::GF7(1), math::GF7(2), math::GF7(3), math::GF7(4), math::GF7(5),\n  };\n  EXPECT_EQ(fields, expected);\n}\n\nTEST_F(PrimeFieldSerializableTest, SerializationFailureDueToModulus) {\n  std::vector<math::GF7> fields;\n  ASSERT_FALSE(SerializeToFieldElements(math::GF7::Config::kModulus, &fields));\n  EXPECT_TRUE(fields.empty());\n}\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"duplex_sponge_mode\",\n    hdrs = [\"duplex_sponge_mode.h\"],\n    deps = [\"//tachyon/base/buffer:copyable\"],\n)\n\ntachyon_cc_library(\n    name = \"padding_free_sponge\",\n    hdrs = [\"padding_free_sponge.h\"],\n    deps = [\n        \":sponge\",\n        \"//tachyon/crypto/hashes:hasher\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"sponge\",\n    hdrs = [\"sponge.h\"],\n    deps = [\n        \":duplex_sponge_mode\",\n        \":sponge_state\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base/buffer:copyable\",\n        \"//tachyon/math/finite_fields:finite_field_traits\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"sponge_state\",\n    hdrs = [\"sponge_state.h\"],\n    deps = [\n        \":duplex_sponge_mode\",\n        \"//tachyon/base/strings:string_util\",\n        \"//tachyon/math/matrix:matrix_types\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"truncated_permutation\",\n    hdrs = [\"truncated_permutation.h\"],\n    deps = [\n        \":sponge\",\n        \"//tachyon/crypto/hashes:compressor\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"sponge_unittests\",\n    srcs = [\n        \"duplex_sponge_mode_unittest.cc\",\n        \"padding_free_sponge_unittest.cc\",\n        \"truncated_permutation_unittest.cc\",\n    ],\n    deps = [\n        \":duplex_sponge_mode\",\n        \":padding_free_sponge\",\n        \":truncated_permutation\",\n        \"//tachyon/base/buffer:vector_buffer\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/crypto/hashes/sponge/poseidon2\",\n        \"//tachyon/crypto/hashes/sponge/poseidon2:poseidon2_params\",\n        \"//tachyon/crypto/hashes/sponge/poseidon2/param_traits:poseidon2_baby_bear\",\n        \"//tachyon/math/finite_fields/test:finite_field_test\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/duplex_sponge_mode.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_CRYPTO_HASHES_SPONGE_DUPLEX_SPONGE_MODE_H_\n#define TACHYON_CRYPTO_HASHES_SPONGE_DUPLEX_SPONGE_MODE_H_\n\n#include <stddef.h>\n\n#include \"tachyon/base/buffer/copyable.h\"\n\nnamespace tachyon {\nnamespace crypto {\n\n// The mode structure for duplex sponge.\nstruct TACHYON_EXPORT DuplexSpongeMode {\n  enum class Type {\n    // The sponge is currently absorbing data.\n    kAbsorbing,\n    // The sponge is currently squeezing data out.\n    kSqueezing,\n  };\n\n  constexpr DuplexSpongeMode() = default;\n\n  constexpr static DuplexSpongeMode Absorbing(size_t next_index = 0) {\n    return {Type::kAbsorbing, next_index};\n  }\n  constexpr static DuplexSpongeMode Squeezing(size_t next_index = 0) {\n    return {Type::kSqueezing, next_index};\n  }\n\n  Type type = Type::kAbsorbing;\n  // When |type| is |kAbsorbing|, it is interpreted as next position of the\n  // state to be XOR-ed when absorbing.\n  // When |type| is |kSqueezing|, it is interpreted as next position of the\n  // state to be outputted when squeezing.\n  size_t next_index = 0;\n\n  bool operator==(const DuplexSpongeMode& other) const {\n    return type == other.type && next_index == other.next_index;\n  }\n  bool operator!=(const DuplexSpongeMode& other) const {\n    return !operator==(other);\n  }\n\n private:\n  friend class base::Copyable<DuplexSpongeMode>;\n\n  constexpr DuplexSpongeMode(Type type, size_t next_index)\n      : type(type), next_index(next_index) {}\n};\n\n}  // namespace crypto\n\nnamespace base {\n\ntemplate <>\nclass Copyable<crypto::DuplexSpongeMode> {\n public:\n  static bool WriteTo(const crypto::DuplexSpongeMode& mode, Buffer* buffer) {\n    return buffer->WriteMany(mode.type, mode.next_index);\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer,\n                       crypto::DuplexSpongeMode* mode) {\n    crypto::DuplexSpongeMode::Type type;\n    size_t next_index;\n    if (!buffer.ReadMany(&type, &next_index)) {\n      return false;\n    }\n\n    *mode = {type, next_index};\n    return true;\n  }\n\n  static size_t EstimateSize(const crypto::DuplexSpongeMode& mode) {\n    return base::EstimateSize(mode.type, mode.next_index);\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_CRYPTO_HASHES_SPONGE_DUPLEX_SPONGE_MODE_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/duplex_sponge_mode_unittest.cc",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#include \"tachyon/crypto/hashes/sponge/duplex_sponge_mode.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/buffer/vector_buffer.h\"\n\nnamespace tachyon::crypto {\n\nTEST(DuplexSpongeModeTest, Copyable) {\n  DuplexSpongeMode expected = DuplexSpongeMode::Squeezing(3);\n\n  base::Uint8VectorBuffer write_buf;\n  ASSERT_TRUE(write_buf.Grow(base::EstimateSize(expected)));\n  ASSERT_TRUE(write_buf.Write(expected));\n  ASSERT_TRUE(write_buf.Done());\n\n  write_buf.set_buffer_offset(0);\n\n  DuplexSpongeMode value;\n  ASSERT_TRUE(write_buf.Read(&value));\n\n  EXPECT_EQ(value, expected);\n}\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/padding_free_sponge.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_CRYPTO_HASHES_SPONGE_PADDING_FREE_SPONGE_H_\n#define TACHYON_CRYPTO_HASHES_SPONGE_PADDING_FREE_SPONGE_H_\n\n#include <stddef.h>\n\n#include <array>\n#include <utility>\n\n#include \"tachyon/crypto/hashes/hasher.h\"\n#include \"tachyon/crypto/hashes/sponge/sponge.h\"\n\nnamespace tachyon::crypto {\n\ntemplate <typename Derived, size_t Rate, size_t Out>\nclass PaddingFreeSponge final\n    : public Hasher<PaddingFreeSponge<Derived, Rate, Out>> {\n public:\n  constexpr static size_t kRate = Rate;\n  constexpr static size_t kOut = Out;\n\n  using Params = typename Derived::Params;\n  using F = typename CryptographicSpongeTraits<Derived>::F;\n\n  PaddingFreeSponge() = default;\n  explicit PaddingFreeSponge(const Derived& derived) : derived_(derived) {}\n  explicit PaddingFreeSponge(Derived&& derived)\n      : derived_(std::move(derived)) {}\n\n  const Derived& derived() const { return derived_; }\n\n private:\n  friend class Hasher<PaddingFreeSponge<Derived, Rate, Out>>;\n\n  SpongeState<Params> CreateEmptyState() const { return SpongeState<Params>(); }\n\n  template <typename T>\n  std::array<F, Out> DoHash(SpongeState<Params>& state, const T& input) const {\n    for (size_t i = 0; i < std::size(input); i += Rate) {\n      for (size_t j = 0; j < Rate; ++j) {\n        if (i + j < std::size(input)) {\n          state[j] = input[i + j];\n        }\n      }\n      derived_.Permute(state);\n    }\n    std::array<F, Out> ret;\n    for (size_t i = 0; i < Out; ++i) {\n      ret[i] = std::move(state[i]);\n    }\n    return ret;\n  }\n\n  Derived derived_;\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_HASHES_SPONGE_PADDING_FREE_SPONGE_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/padding_free_sponge_unittest.cc",
    "content": "#include \"tachyon/crypto/hashes/sponge/padding_free_sponge.h\"\n\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/param_traits/poseidon2_baby_bear.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_params.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n\nnamespace tachyon::crypto {\n\nusing F = math::BabyBear;\n\nnamespace {\n\nclass PaddingFreeSpongeTest : public math::FiniteFieldTest<F> {};\n\n}  // namespace\n\nTEST_F(PaddingFreeSpongeTest, Hash) {\n  using Params = Poseidon2Params<Poseidon2Vendor::kHorizen,\n                                 Poseidon2Vendor::kPlonky3, F, 15, 7>;\n  using Poseidon2 = Poseidon2Sponge<Params>;\n  constexpr size_t kRate = 8;\n  constexpr size_t kOut = 8;\n\n  PaddingFreeSponge<Poseidon2, kRate, kOut> hasher;\n  std::vector<F> inputs =\n      base::CreateVector(100, [](uint32_t i) { return F(i); });\n  std::array<F, kOut> hash = hasher.Hash(inputs);\n  std::array<F, kOut> expected = {\n      F(1812148253), F(1620994441), F(1186045281), F(1486390083),\n      F(1521745237), F(1658565356), F(1836019216), F(3991760),\n  };\n  EXPECT_EQ(hash, expected);\n}\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"poseidon\",\n    hdrs = [\"poseidon.h\"],\n    deps = [\n        \":poseidon_config\",\n        \":poseidon_sponge_base\",\n        \"//tachyon/base/buffer:copyable\",\n        \"//tachyon/crypto/hashes/sponge:sponge_state\",\n        \"//tachyon/math/matrix:matrix_operations\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"poseidon_config\",\n    hdrs = [\"poseidon_config.h\"],\n    deps = [\n        \":poseidon_config_base\",\n        \":poseidon_config_entry\",\n        \":sparse_mds_matrix\",\n        \"//tachyon/base:optional\",\n        \"//tachyon/base/buffer:copyable\",\n        \"//tachyon/base/ranges:algorithm\",\n        \"//tachyon/math/finite_fields:finite_field_traits\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"poseidon_config_base\",\n    hdrs = [\"poseidon_config_base.h\"],\n    deps = [\n        \"//tachyon/base/buffer:copyable\",\n        \"//tachyon/math/finite_fields:finite_field_traits\",\n        \"//tachyon/math/matrix:matrix_types\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"poseidon_config_entry\",\n    hdrs = [\"poseidon_config_entry.h\"],\n    deps = [\":poseidon_config_entry_base\"],\n)\n\ntachyon_cc_library(\n    name = \"poseidon_config_entry_base\",\n    hdrs = [\"poseidon_config_entry_base.h\"],\n    deps = [\n        \":poseidon_grain_lfsr\",\n        \"//tachyon/math/finite_fields:finite_field_traits\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"poseidon_grain_lfsr\",\n    hdrs = [\"poseidon_grain_lfsr.h\"],\n    deps = [\n        \"//tachyon/math/base:big_int\",\n        \"//tachyon/math/finite_fields:finite_field_traits\",\n        \"//tachyon/math/matrix:matrix_types\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"poseidon_params\",\n    hdrs = [\"poseidon_params.h\"],\n    deps = [\"//tachyon/math/finite_fields/baby_bear/internal:baby_bear\"],\n)\n\ntachyon_cc_library(\n    name = \"poseidon_sponge_base\",\n    hdrs = [\"poseidon_sponge_base.h\"],\n    deps = [\n        \"//tachyon/base:logging\",\n        \"//tachyon/crypto/hashes:prime_field_serializable\",\n        \"//tachyon/crypto/hashes/sponge\",\n        \"@eigen_archive//:eigen3\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"sparse_mds_matrix\",\n    hdrs = [\"sparse_mds_matrix.h\"],\n    deps = [\n        \"//tachyon/base:logging\",\n        \"//tachyon/base/buffer:copyable\",\n        \"//tachyon/base/strings:string_util\",\n        \"//tachyon/math/matrix:matrix_types\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"poseidon_unittests\",\n    srcs = [\n        \"poseidon_config_unittest.cc\",\n        \"poseidon_grain_lfsr_unittest.cc\",\n        \"poseidon_unittest.cc\",\n        \"sparse_mds_matrix_unittest.cc\",\n    ],\n    deps = [\n        \":poseidon\",\n        \":poseidon_config\",\n        \":poseidon_params\",\n        \":sparse_mds_matrix\",\n        \"//tachyon/base/buffer:vector_buffer\",\n        \"//tachyon/math/elliptic_curves/bls12/bls12_381:fr\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:fr\",\n        \"//tachyon/math/finite_fields/baby_bear\",\n        \"//tachyon/math/finite_fields/test:finite_field_test\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon/poseidon.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n// Copyright 2022 Ethereum Foundation\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.EF and the LICENCE-APACHE.EF\n// file.\n\n#ifndef TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON_POSEIDON_H_\n#define TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON_POSEIDON_H_\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/buffer/copyable.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon/poseidon_config.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon/poseidon_sponge_base.h\"\n#include \"tachyon/crypto/hashes/sponge/sponge_state.h\"\n#include \"tachyon/math/matrix/matrix_operations.h\"\n\nnamespace tachyon {\nnamespace crypto {\n\n// Poseidon Sponge Hash: Absorb → Permute → Squeeze\n// Absorb: Absorb elements into the sponge.\n// Permute: Transform the |state| using a series of operations.\n//   1. Apply ARK (addition of round constants) to |state|.\n//   2. Apply S-Box (xᵅ) to |state|.\n//   3. Apply MDS matrix to |state|.\n// Squeeze: Squeeze elements out of the sponge.\n// This implementation of Poseidon is entirely Fractal's implementation in\n// [COS20][cos] with small syntax changes. See https://eprint.iacr.org/2019/1076\ntemplate <typename _Params>\nstruct PoseidonSponge final\n    : public PoseidonSpongeBase<PoseidonSponge<_Params>> {\n  using Params = _Params;\n  using F = typename Params::Field;\n  // Sponge Config\n  PoseidonConfig<Params> config;\n\n  PoseidonSponge() = default;\n  explicit PoseidonSponge(const PoseidonConfig<Params>& config)\n      : config(config) {}\n  explicit PoseidonSponge(PoseidonConfig<Params>&& config)\n      : config(std::move(config)) {}\n\n  // PoseidonSpongeBase methods\n  void Permute(SpongeState<Params>& state) const {\n    this->ApplyARKFull(state, 0);\n\n    size_t full_rounds_over_2 = Params::kFullRounds / 2;\n    for (size_t i = 1; i < full_rounds_over_2; ++i) {\n      this->ApplySBoxFull(state);\n      this->ApplyARKFull(state, i);\n      ApplyMixFull(state);\n    }\n    this->ApplySBoxFull(state);\n    this->ApplyARKFull(state, full_rounds_over_2);\n    ApplyMixEfficientFull(state, full_rounds_over_2);\n\n    for (size_t i = full_rounds_over_2 + 1;\n         i < full_rounds_over_2 + Params::kPartialRounds + 1; ++i) {\n      this->ApplySBoxPartial(state);\n      this->ApplyARKPartial(state, i);\n      ApplyMixEfficientPartial(state, i - (full_rounds_over_2 + 1));\n    }\n\n    for (size_t i = full_rounds_over_2 + Params::kPartialRounds + 1;\n         i < Params::kPartialRounds + Params::kFullRounds; ++i) {\n      this->ApplySBoxFull(state);\n      this->ApplyARKFull(state, i);\n      ApplyMixFull(state);\n    }\n    this->ApplySBoxFull(state);\n    ApplyMixFull(state);\n  }\n\n  bool operator==(const PoseidonSponge& other) const {\n    return config == other.config;\n  }\n  bool operator!=(const PoseidonSponge& other) const {\n    return !operator==(other);\n  }\n\n private:\n  void ApplyMixFull(SpongeState<Params>& state) const {\n    state.elements = math::MulMatVecSerial(config.mds, state.elements);\n  }\n\n  void ApplyMixEfficientFull(SpongeState<Params>& state,\n                             Eigen::Index index) const {\n    state.elements =\n        math::MulMatVecSerial(config.pre_sparse_mds, state.elements);\n  }\n\n  void ApplyMixEfficientPartial(SpongeState<Params>& state,\n                                Eigen::Index index) const {\n    config.sparse_mds_matrices[index].Apply(state.elements);\n  }\n};\n\ntemplate <typename _Params>\nstruct CryptographicSpongeTraits<PoseidonSponge<_Params>> {\n  using Params = _Params;\n  using F = typename Params::Field;\n};\n\n}  // namespace crypto\n\nnamespace base {\n\ntemplate <typename Params>\nclass Copyable<crypto::PoseidonSponge<Params>> {\n public:\n  static bool WriteTo(const crypto::PoseidonSponge<Params>& poseidon,\n                      Buffer* buffer) {\n    return buffer->WriteMany(poseidon.config);\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer,\n                       crypto::PoseidonSponge<Params>* poseidon) {\n    crypto::PoseidonConfig<Params> config;\n    if (!buffer.ReadMany(&config)) {\n      return false;\n    }\n\n    *poseidon = crypto::PoseidonSponge<Params>(std::move(config));\n    return true;\n  }\n\n  static size_t EstimateSize(const crypto::PoseidonSponge<Params>& poseidon) {\n    return base::EstimateSize(poseidon.config);\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON_POSEIDON_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon/poseidon_config.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n// Copyright 2022 Ethereum Foundation\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.EF and the LICENCE-APACHE.EF\n// file.\n\n#ifndef TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON_POSEIDON_CONFIG_H_\n#define TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON_POSEIDON_CONFIG_H_\n\n#include <algorithm>\n#include <utility>\n#include <vector>\n\n#include \"absl/types/span.h\"\n#include \"third_party/eigen3/Eigen/LU\"\n\n#include \"tachyon/base/buffer/copyable.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/optional.h\"\n#include \"tachyon/base/ranges/algorithm.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon/poseidon_config_base.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon/poseidon_config_entry.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon/sparse_mds_matrix.h\"\n#include \"tachyon/math/finite_fields/finite_field_traits.h\"\n\nnamespace tachyon {\nnamespace crypto {\n\n// ARK(AddRoundKey) is a matrix that contains an ARC(AddRoundConstant) array in\n// each row. Each constant is added to the |state| of each round of Poseidon.\n// MDS(Maximum Distance Separable) is a matrix that is applied to the |state|\n// for each round. It ensures that the sum of the vector's weight before and\n// after the MDS is at least |state| + 1.\ntemplate <typename F>\nvoid FindPoseidonARKAndMDS(const PoseidonGrainLFSRConfig& config,\n                           size_t skip_matrices, math::Matrix<F>& ark,\n                           math::Matrix<F>& mds) {\n  PoseidonGrainLFSR<F> lfsr(config);\n  ark = math::Matrix<F>(config.num_full_rounds + config.num_partial_rounds,\n                        config.state_len);\n  for (size_t i = 0; i < config.num_full_rounds + config.num_partial_rounds;\n       ++i) {\n    ark.row(i) = lfsr.GetFieldElementsRejectionSampling(config.state_len);\n  }\n\n  for (uint64_t i = 0; i < skip_matrices; ++i) {\n    lfsr.GetFieldElementsModP(2 * config.state_len);\n  }\n\n  // a qualifying matrix must satisfy the following requirements\n  // - there is no duplication among the elements in x or y\n  // - there is no i and j such that x[i] + y[j] = p\n  // - the resultant MDS passes all the three tests\n\n  math::Vector<F> xs = lfsr.GetFieldElementsModP(config.state_len);\n  math::Vector<F> ys = lfsr.GetFieldElementsModP(config.state_len);\n\n  mds = math::Matrix<F>(config.state_len, config.state_len);\n  for (Eigen::Index i = 0; i < mds.rows(); ++i) {\n    for (Eigen::Index j = 0; j < mds.cols(); ++j) {\n      mds(i, j) = unwrap((xs[i] + ys[j]).Inverse());\n    }\n  }\n}\n\ntemplate <typename Params>\nstruct PoseidonConfig : public PoseidonConfigBase<Params> {\n  using F = typename Params::Field;\n  // Maximally Distance Separating (MDS) Matrix.\n  math::Matrix<F> mds;\n\n  // See\n  // https://github.com/privacy-scaling-explorations/poseidon/blob/d29f35d/src/spec.rs#L122-L123\n  math::Matrix<F> pre_sparse_mds;\n  std::vector<SparseMDSMatrix<F>> sparse_mds_matrices;\n\n  PoseidonConfig() = default;\n  PoseidonConfig(const PoseidonConfigBase<Params>& base,\n                 const math::Matrix<F>& mds,\n                 const math::Matrix<F>& pre_sparse_mds,\n                 const std::vector<SparseMDSMatrix<F>>& sparse_mds_matrices)\n      : PoseidonConfigBase<Params>(base),\n        mds(mds),\n        pre_sparse_mds(pre_sparse_mds),\n        sparse_mds_matrices(sparse_mds_matrices) {}\n  PoseidonConfig(PoseidonConfigBase<Params>&& base, math::Matrix<F>&& mds,\n                 math::Matrix<F>&& pre_sparse_mds,\n                 std::vector<SparseMDSMatrix<F>>&& sparse_mds_matrices)\n      : PoseidonConfigBase<Params>(std::move(base)),\n        mds(std::move(mds)),\n        pre_sparse_mds(std::move(pre_sparse_mds)),\n        sparse_mds_matrices(std::move(sparse_mds_matrices)) {}\n\n  constexpr static PoseidonConfig Create(size_t skip_matrices) {\n    PoseidonConfigEntry config_entry(Params::kRate, Params::kAlpha,\n                                     Params::kFullRounds,\n                                     Params::kPartialRounds, skip_matrices);\n    PoseidonConfig ret = config_entry.ToPoseidonConfig<Params>();\n    FindPoseidonARKAndMDS<F>(config_entry.ToPoseidonGrainLFSRConfig<F>(),\n                             skip_matrices, ret.ark, ret.mds);\n    OptimizeARK<Params::kFullRounds, Params::kPartialRounds>(ret.mds, ret.ark);\n    ComputeSparseMatrices<Params::kPartialRounds>(ret.mds, ret.pre_sparse_mds,\n                                                  ret.sparse_mds_matrices);\n    return ret;\n  }\n\n  bool IsValid() const override {\n    bool ret =\n        PoseidonConfigBase<Params>::IsValid() &&\n        static_cast<size_t>(mds.rows()) == Params::kRate + Params::kCapacity &&\n        static_cast<size_t>(mds.cols()) == Params::kRate + Params::kCapacity &&\n        static_cast<size_t>(pre_sparse_mds.rows()) ==\n            Params::kRate + Params::kCapacity &&\n        static_cast<size_t>(pre_sparse_mds.cols()) ==\n            Params::kRate + Params::kCapacity &&\n        sparse_mds_matrices.size() == Params::kPartialRounds;\n    if (!ret) return false;\n\n    for (const SparseMDSMatrix<F>& sparse_mds_matrix : sparse_mds_matrices) {\n      if (sparse_mds_matrix.row().size() != mds.cols()) return false;\n      if (sparse_mds_matrix.col_hat().size() != mds.rows() - 1) return false;\n    }\n    return true;\n  }\n\n  bool operator==(const PoseidonConfig& other) const {\n    return PoseidonConfigBase<Params>::operator==(other) && mds == other.mds &&\n           pre_sparse_mds == other.pre_sparse_mds &&\n           sparse_mds_matrices == other.sparse_mds_matrices;\n  }\n  bool operator!=(const PoseidonConfig& other) const {\n    return !operator==(other);\n  }\n\n private:\n  template <size_t FullRounds, size_t PartialRounds>\n  constexpr static void OptimizeARK(const math::Matrix<F>& mds,\n                                    math::Matrix<F>& ark) {\n    math::Matrix<F> mds_inverse = mds.inverse();\n    math::Matrix<F> optimized_ark = {ark.rows(), ark.cols()};\n    optimized_ark.row(0) = ark.row(0);\n    size_t full_rounds_over_2 = FullRounds / 2;\n    for (size_t i = 1; i < full_rounds_over_2; ++i) {\n      optimized_ark.row(i) = mds_inverse * ark.row(i).transpose();\n    }\n\n    math::RowVector<F> acc = ark.row(full_rounds_over_2 + PartialRounds);\n    for (size_t i = full_rounds_over_2; i < full_rounds_over_2 + PartialRounds;\n         ++i) {\n      math::RowVector<F> tmp = mds_inverse * acc.transpose();\n      optimized_ark.row(FullRounds + PartialRounds - i).setZero();\n      optimized_ark.row(FullRounds + PartialRounds - i)[0] = tmp[0];\n\n      tmp[0] = F::Zero();\n      acc = tmp + ark.row(FullRounds + PartialRounds - i - 1);\n    }\n    optimized_ark.row(full_rounds_over_2) = mds_inverse * acc.transpose();\n\n    for (size_t i = full_rounds_over_2 + PartialRounds + 1;\n         i < PartialRounds + FullRounds; ++i) {\n      optimized_ark.row(i) = mds_inverse * ark.row(i).transpose();\n    }\n    ark = std::move(optimized_ark);\n  }\n\n  template <size_t PartialRounds>\n  constexpr static void ComputeSparseMatrices(\n      const math::Matrix<F>& mds, math::Matrix<F>& pre_sparse_mds,\n      std::vector<SparseMDSMatrix<F>>& sparse_matrices) {\n    math::Matrix<F> mds_transpose = mds.transpose();\n    math::Matrix<F> acc = mds_transpose;\n    sparse_matrices =\n        base::CreateVector(PartialRounds, [&mds_transpose, &acc]() {\n          math::Matrix<F> m_prime;\n          SparseMDSMatrix<F> m_prime_prime;\n          Factorize(acc, m_prime, m_prime_prime);\n          acc = mds_transpose * m_prime;\n          return m_prime_prime;\n        });\n    std::reverse(sparse_matrices.begin(), sparse_matrices.end());\n    pre_sparse_mds = acc.transpose();\n  }\n\n  // See Section B in Supplementary Material in\n  // https://eprint.iacr.org/2019/458.pdf.\n  // Factorizes an MDS matrix |M| into |M'| and |M''| where |M = M' * M''|.\n  // The |M'| matrix contributes to the accumulator of the process, and the\n  // resulting |M''| matrix is sparse.\n  constexpr static void Factorize(const math::Matrix<F>& mds,\n                                  math::Matrix<F>& m_prime,\n                                  SparseMDSMatrix<F>& m_prime_prime) {\n    // |(r - 1 * r - 1)| sized MDS matrix called |m_hat| constructs a matrix\n    // in the form |[[1 | 0], [0 | m_hat]]|, where r is rate.\n    Eigen::Block<const math::Matrix<F>> m_hat =\n        mds.block(1, 1, mds.rows() - 1, mds.cols() - 1);\n    m_prime = math::Matrix<F>::Identity(mds.rows(), mds.cols());\n    m_prime.block(1, 1, mds.rows() - 1, mds.cols() - 1) = m_hat;\n\n    // |(r - 1)| sized vector called |w_hat| is denoted as\n    // |[[m₀,₀ | m₀,ᵢ], [w_hat | identity]]|, where r is rate.\n    auto w_hat = m_hat.inverse() * mds.block(1, 0, mds.rows() - 1, 1);\n    math::Matrix<F> m_prime_prime_mds =\n        math::Matrix<F>::Identity(mds.rows(), mds.cols());\n    m_prime_prime_mds.row(0) = mds.row(0);\n    m_prime_prime_mds.block(1, 0, w_hat.rows(), 1) = w_hat;\n    m_prime_prime =\n        SparseMDSMatrix<F>::FromMDSMatrix(m_prime_prime_mds.transpose());\n  }\n};\n\ntemplate <typename Params>\nPoseidonConfig<Params> PoseidonConfigEntry::ToPoseidonConfig() const {\n  return PoseidonConfig<Params>();\n}\n\n}  // namespace crypto\n\nnamespace base {\ntemplate <typename Params>\nclass Copyable<crypto::PoseidonConfig<Params>> {\n public:\n  using F = typename Params::Field;\n  static bool WriteTo(const crypto::PoseidonConfig<Params>& config,\n                      Buffer* buffer) {\n    return Copyable<crypto::PoseidonConfigBase<Params>>::WriteTo(config,\n                                                                 buffer) &&\n           buffer->WriteMany(config.mds, config.pre_sparse_mds,\n                             config.sparse_mds_matrices);\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer,\n                       crypto::PoseidonConfig<Params>* config) {\n    crypto::PoseidonConfigBase<Params> base;\n    math::Matrix<F> mds;\n    math::Matrix<F> pre_sparse_mds;\n    std::vector<crypto::SparseMDSMatrix<F>> sparse_mds_matrices;\n    if (!buffer.ReadMany(&base, &mds, &pre_sparse_mds, &sparse_mds_matrices)) {\n      return false;\n    }\n\n    *config = {std::move(base), std::move(mds), std::move(pre_sparse_mds),\n               std::move(sparse_mds_matrices)};\n    return true;\n  }\n\n  static size_t EstimateSize(const crypto::PoseidonConfig<Params>& config) {\n    const crypto::PoseidonConfigBase<Params>& base =\n        static_cast<const crypto::PoseidonConfigBase<Params>&>(config);\n    return base::EstimateSize(base, config.mds, config.pre_sparse_mds,\n                              config.sparse_mds_matrices);\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON_POSEIDON_CONFIG_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon/poseidon_config_base.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON_POSEIDON_CONFIG_BASE_H_\n#define TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON_POSEIDON_CONFIG_BASE_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <utility>\n\n#include \"tachyon/base/buffer/copyable.h\"\n#include \"tachyon/math/matrix/matrix_types.h\"\n\nnamespace tachyon {\nnamespace crypto {\n\ntemplate <typename Params>\nstruct PoseidonConfigBase {\n  using F = typename Params::Field;\n\n  // Additive Round Keys added before each MDS matrix application to make it an\n  // affine shift. They are indexed by |ark[round_num][state_element_index]|.\n  math::Matrix<F> ark;\n\n  PoseidonConfigBase() = default;\n  explicit PoseidonConfigBase(const math::Matrix<F>& ark) : ark(ark) {}\n  explicit PoseidonConfigBase(math::Matrix<F>&& ark) : ark(std::move(ark)) {}\n\n  virtual ~PoseidonConfigBase() = default;\n\n  virtual bool IsValid() const {\n    return static_cast<size_t>(ark.rows()) ==\n               Params::kFullRounds + Params::kPartialRounds &&\n           static_cast<size_t>(ark.cols()) == Params::kRate + Params::kCapacity;\n  }\n\n  bool operator==(const PoseidonConfigBase& other) const {\n    return ark == other.ark;\n  }\n  bool operator!=(const PoseidonConfigBase& other) const {\n    return !operator==(other);\n  }\n};\n\n}  // namespace crypto\n\nnamespace base {\n\ntemplate <typename Params>\nclass Copyable<crypto::PoseidonConfigBase<Params>> {\n public:\n  static bool WriteTo(const crypto::PoseidonConfigBase<Params>& config,\n                      Buffer* buffer) {\n    return buffer->Write(config.ark);\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer,\n                       crypto::PoseidonConfigBase<Params>* config) {\n    math::Matrix<typename Params::Field> ark;\n    if (!buffer.Read(&ark)) {\n      return false;\n    }\n\n    config->ark = std::move(ark);\n    return true;\n  }\n\n  static size_t EstimateSize(const crypto::PoseidonConfigBase<Params>& config) {\n    return base::EstimateSize(config.ark);\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON_POSEIDON_CONFIG_BASE_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon/poseidon_config_entry.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON_POSEIDON_CONFIG_ENTRY_H_\n#define TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON_POSEIDON_CONFIG_ENTRY_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include \"tachyon/crypto/hashes/sponge/poseidon/poseidon_config_entry_base.h\"\n\nnamespace tachyon::crypto {\n\ntemplate <typename Params>\nstruct PoseidonConfig;\n\n// An entry in the Poseidon config\nstruct TACHYON_EXPORT PoseidonConfigEntry : public PoseidonConfigEntryBase {\n  // Number of matrices to skip when generating config using the Grain LFSR.\n  // The matrices being skipped are those that do not satisfy all the desired\n  // properties. See:\n  // https://extgit.iaik.tugraz.at/krypto/hadeshash/-/blob/master/code/generate_parameters_grain.sage\n  size_t skip_matrices;\n\n  constexpr PoseidonConfigEntry() : PoseidonConfigEntry(0, 0, 0, 0, 0) {}\n  constexpr PoseidonConfigEntry(size_t rate, uint64_t alpha, size_t full_rounds,\n                                size_t partial_rounds, size_t skip_matrices)\n      : PoseidonConfigEntryBase(rate, alpha, full_rounds, partial_rounds),\n        skip_matrices(skip_matrices) {}\n\n  template <typename Params>\n  PoseidonConfig<Params> ToPoseidonConfig() const;\n\n  bool operator==(const PoseidonConfigEntry& other) const {\n    return PoseidonConfigEntryBase::operator==(other) &&\n           skip_matrices == other.skip_matrices;\n  }\n  bool operator!=(const PoseidonConfigEntry& other) const {\n    return !operator==(other);\n  }\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON_POSEIDON_CONFIG_ENTRY_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon/poseidon_config_entry_base.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON_POSEIDON_CONFIG_ENTRY_BASE_H_\n#define TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON_POSEIDON_CONFIG_ENTRY_BASE_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include \"tachyon/crypto/hashes/sponge/poseidon/poseidon_grain_lfsr.h\"\n#include \"tachyon/math/finite_fields/finite_field_traits.h\"\n\nnamespace tachyon::crypto {\n\n// An entry in the Poseidon config\nstruct TACHYON_EXPORT PoseidonConfigEntryBase {\n  // The rate (in terms of number of field elements).\n  size_t rate;\n\n  // Exponent used in S-boxes.\n  uint64_t alpha;\n\n  // Number of rounds in a full-round operation.\n  size_t full_rounds;\n\n  // Number of rounds in a partial-round operation.\n  size_t partial_rounds;\n\n  constexpr PoseidonConfigEntryBase() : PoseidonConfigEntryBase(0, 0, 0, 0) {}\n  constexpr PoseidonConfigEntryBase(size_t rate, uint64_t alpha,\n                                    size_t full_rounds, size_t partial_rounds)\n      : rate(rate),\n        alpha(alpha),\n        full_rounds(full_rounds),\n        partial_rounds(partial_rounds) {}\n\n  template <typename F>\n  PoseidonGrainLFSRConfig ToPoseidonGrainLFSRConfig() const {\n    using PrimeField = math::MaybeUnpack<F>;\n\n    PoseidonGrainLFSRConfig config;\n    config.prime_num_bits = PrimeField::kModulusBits;\n    config.state_len = rate + 1;\n    config.num_full_rounds = full_rounds;\n    config.num_partial_rounds = partial_rounds;\n    return config;\n  }\n\n  bool operator==(const PoseidonConfigEntryBase& other) const {\n    return rate == other.rate && alpha == other.alpha &&\n           full_rounds == other.full_rounds &&\n           partial_rounds == other.partial_rounds;\n  }\n  bool operator!=(const PoseidonConfigEntryBase& other) const {\n    return !operator==(other);\n  }\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON_POSEIDON_CONFIG_ENTRY_BASE_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon/poseidon_config_unittest.cc",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#include \"tachyon/crypto/hashes/sponge/poseidon/poseidon_config.h\"\n\n#include <string>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/crypto/hashes/sponge/poseidon/poseidon_params.h\"\n#include \"tachyon/math/elliptic_curves/bls12/bls12_381/fr.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/fr.h\"\n\nnamespace tachyon::crypto {\n\ntemplate <typename F, size_t rate, uint64_t alpha, size_t full_rounds,\n          size_t partial_rounds>\nvoid RunTest(size_t skip_matrices, std::string ark00_str, std::string mds00_str,\n             std::string pre_sparse_mds11_str,\n             std::string sparse_matrices0_row0_str,\n             std::string sparse_matrices0_col_hat0_str) {\n  using Params = PoseidonParams<F, rate, alpha, full_rounds, partial_rounds>;\n  auto config = PoseidonConfig<Params>::Create(skip_matrices);\n  ASSERT_TRUE(config.IsValid());\n  EXPECT_EQ(config.ark(0, 0), *F::FromDecString(ark00_str));\n  EXPECT_EQ(config.mds(0, 0), *F::FromDecString(mds00_str));\n  EXPECT_EQ(config.pre_sparse_mds(1, 1),\n            *F::FromDecString(pre_sparse_mds11_str));\n  EXPECT_EQ(config.sparse_mds_matrices[0].row()[0],\n            *F::FromDecString(sparse_matrices0_row0_str));\n  EXPECT_EQ(config.sparse_mds_matrices[0].col_hat()[0],\n            *F::FromDecString(sparse_matrices0_col_hat0_str));\n}\n\nTEST(PoseidonConfigTest, Create) {\n  using F = math::bn254::Fr;\n  F::Init();\n\n  // clang-format off\n  RunTest<F, 2, 5, 8, 57>(0, \"6745197990210204598374042828761989596302876299545964402857411729872131034734\", \"7511745149465107256748700652201246547602992235352608707588321460060273774987\",  \"20498480049173041451757161739353136932402063966867101132544382489060457121690\", \"7511745149465107256748700652201246547602992235352608707588321460060273774987\", \"8364259238812534287689210722577399963878179320345509803468849104367466297989\");\n  RunTest<F, 3, 5, 8, 31>(0, \"13403165100170528731188983416904733323082063763924490907636477756087868446385\", \"4843064272860702558353681805605581092414485968533095609154162537440763859608\", \"18904002742018845139579665761306904915446670558152364574366010338917470570969\", \"4843064272860702558353681805605581092414485968533095609154162537440763859608\", \"17098920592029650201655138442655581967768635903769554042626458334412341558169\");\n  RunTest<F, 4, 5, 8, 57>(0, \"19113776568595304904649638772185171763805916665512167111307269381068492077003\", \"9390358363320792447057897590391227342305356869000968376798315031785873376651\", \"10430399957950918663081154404041014339007209286885623491793224536133959093342\", \"9390358363320792447057897590391227342305356869000968376798315031785873376651\", \"4258476155330118372762506614067358362596124649116737498715627157925163661\");\n  RunTest<F, 5, 5, 4, 31>(0, \"12911143707477888215006316149665736087938931277132002862716953775656297280505\", \"10942845101262402626166273431356787436787991939175819278065571096963239049995\", \"5243571363387738625655552142589814423245410823902064800321501427822383158829\", \"10942845101262402626166273431356787436787991939175819278065571096963239049995\", \"10233199471798935861979427851690416803067121439733943143275749690073539672323\");\n  RunTest<F, 6, 5, 2, 12>(0, \"6891333877554147985347986116215540686446242805416768740851383136400218469817\", \"9837954178279989965317992196165220609182866932765981962230951853077616895744\", \"2342265246701838568043436561372198242203879164391121705534106671605750623757\", \"9837954178279989965317992196165220609182866932765981962230951853077616895744\", \"9504868060103463837598804825403938487759722464496492662464197532903433072082\");\n  RunTest<F, 7, 5, 6, 57>(0, \"3674224614566270991667957870461358488780972465005715233452288530417333318480\", \"20096031343166107597256293287487285680099080393005092613845214101013675429510\", \"10312826920506585789288853057573225788967547772875440745847746432386053340298\", \"20096031343166107597256293287487285680099080393005092613845214101013675429510\", \"21032704002095809084962846202624190836816781835379410962327180744509481109422\");\n  RunTest<F, 8, 5, 8, 63>(0, \"14715728137766105031387583973733149375806784983272780095398485311648630967927\", \"708458300293891745856425423607721463509413916954480913172999113933455141974\", \"1739241146470184391733348241765630726223090738057028604336245542891367839190\", \"708458300293891745856425423607721463509413916954480913172999113933455141974\", \"9912718281831214028971669318551955931380391283853329273384463785744511549623\");\n  // clang-format on\n}\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon/poseidon_grain_lfsr.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON_POSEIDON_GRAIN_LFSR_H_\n#define TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON_POSEIDON_GRAIN_LFSR_H_\n\n#include <bitset>\n\n#include \"tachyon/export.h\"\n#include \"tachyon/math/base/big_int.h\"\n#include \"tachyon/math/finite_fields/finite_field_traits.h\"\n#include \"tachyon/math/matrix/matrix_types.h\"\n\nnamespace tachyon::crypto {\n\nstruct TACHYON_EXPORT PoseidonGrainLFSRConfig {\n  bool is_sbox_an_inverse = false;\n  uint64_t prime_num_bits = 0;\n  uint64_t state_len = 0;\n  uint64_t num_full_rounds = 0;\n  uint64_t num_partial_rounds = 0;\n};\n\n// GrainLFSR is a pseudo-random generator using a stream cipher.\n// It is used to generate ARK and MDS for Poseidon.\ntemplate <typename F>\nstruct PoseidonGrainLFSR {\n  uint64_t prime_num_bits = 0;\n  bool state[80] = {\n      false,\n  };\n  size_t head = 0;\n\n  explicit PoseidonGrainLFSR(const PoseidonGrainLFSRConfig& config);\n\n  auto GetBits(size_t num_bits);\n\n  math::Vector<F> GetFieldElementsRejectionSampling(size_t num_elems);\n\n  math::Vector<F> GetFieldElementsModP(size_t num_elems);\n\n private:\n  bool Update() {\n    bool new_bit = state[(head + 62) % 80] ^ state[(head + 51) % 80] ^\n                   state[(head + 38) % 80] ^ state[(head + 23) % 80] ^\n                   state[(head + 13) % 80] ^ state[head];\n\n    state[head] = new_bit;\n    head = (head + 1) % 80;\n\n    return new_bit;\n  }\n\n  void Init() {\n    for (size_t i = 0; i < 160; ++i) {\n      Update();\n    }\n  }\n};\n\ntemplate <typename F>\nPoseidonGrainLFSR<F>::PoseidonGrainLFSR(const PoseidonGrainLFSRConfig& config)\n    : prime_num_bits(config.prime_num_bits) {\n  std::fill(std::begin(state), std::end(state), false);\n\n  // b0, b1 describe the field\n  state[1] = true;\n\n  // b2, ..., b5 describe the S-BOX\n  state[5] = config.is_sbox_an_inverse;\n\n  auto fill_state = [&](uint64_t value, size_t start, size_t end) {\n    for (size_t i = end; i >= start && i <= end; --i) {\n      state[i] = value & 1;\n      value >>= 1;\n    }\n  };\n\n  // b6, ..., b17 are the binary representation of n (prime_num_bits)\n  fill_state(prime_num_bits, 6, 17);\n\n  // b18, ..., b29 are the binary representation of t\n  // state_len, rate + capacity\n  fill_state(config.state_len, 18, 29);\n\n  // b30, ..., b39 are the binary representation of R_F (num_full_rounds)\n  fill_state(config.num_full_rounds, 30, 39);\n\n  // b40, ..., b49 are the binary representation of R_P (num_partial_rounds)\n  fill_state(config.num_partial_rounds, 40, 49);\n\n  // b50, ..., b79 are set to 1\n  for (size_t i = 50; i < 80; ++i) {\n    state[i] = true;\n  }\n\n  head = 0;\n  Init();\n}\n\ntemplate <typename F>\nauto PoseidonGrainLFSR<F>::GetBits(size_t num_bits) {\n  using PrimeField = math::MaybeUnpack<F>;\n\n  std::bitset<PrimeField::kModulusBits> ret;\n  for (size_t i = 0; i < num_bits; ++i) {\n    // Obtain the first bit\n    bool new_bit = Update();\n\n    // Loop until the first bit is 1\n    while (!new_bit) {\n      // Discard the second bit\n      Update();\n      // Obtain another first bit\n      new_bit = Update();\n    }\n\n    // Obtain the second bit\n    ret.set(i, Update());\n  }\n  return ret;\n}\n\n// Rejects elements greater than the modulus and resamples.\ntemplate <typename F>\nmath::Vector<F> PoseidonGrainLFSR<F>::GetFieldElementsRejectionSampling(\n    size_t num_elems) {\n  using PrimeField = math::MaybeUnpack<F>;\n  using BigInt = typename PrimeField::BigIntTy;\n\n  CHECK_EQ(PrimeField::Config::kModulusBits, prime_num_bits);\n\n  math::Vector<F> ret(num_elems);\n\n  for (size_t i = 0; i < num_elems; ++i) {\n    // Perform rejection sampling\n    while (true) {\n      // Obtain n bits and make it most-significant-bit first\n      std::bitset<PrimeField::kModulusBits> bits = GetBits(prime_num_bits);\n      BigInt bigint = BigInt::FromBitsBE(bits);\n\n      if (bigint < BigInt(PrimeField::Config::kModulus)) {\n        if constexpr (math::FiniteFieldTraits<F>::kIsPackedPrimeField) {\n          ret[i] = F::Broadcast(PrimeField::FromBigInt(bigint));\n        } else {\n          ret[i] = PrimeField::FromBigInt(bigint);\n        }\n        break;\n      }\n    }\n  }\n\n  return ret;\n}\n\n// Samples n bits and computes the remainder modulo P.\ntemplate <typename F>\nmath::Vector<F> PoseidonGrainLFSR<F>::GetFieldElementsModP(size_t num_elems) {\n  using PrimeField = math::MaybeUnpack<F>;\n  using BigInt = typename PrimeField::BigIntTy;\n\n  CHECK_EQ(PrimeField::Config::kModulusBits, prime_num_bits);\n\n  math::Vector<F> ret(num_elems);\n\n  for (size_t i = 0; i < num_elems; ++i) {\n    // Obtain n bits and make it most-significant-bit first\n    std::bitset<PrimeField::kModulusBits> bits = GetBits(prime_num_bits);\n\n    BigInt bigint = BigInt::FromBitsBE(bits);\n    bigint %= BigInt(PrimeField::Config::kModulus);\n\n    if constexpr (math::FiniteFieldTraits<F>::kIsPackedPrimeField) {\n      ret[i] = F::Broadcast(PrimeField::FromBigInt(bigint));\n    } else {\n      ret[i] = PrimeField::FromBigInt(bigint);\n    }\n  }\n\n  return ret;\n}\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON_POSEIDON_GRAIN_LFSR_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon/poseidon_grain_lfsr_unittest.cc",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#include \"tachyon/crypto/hashes/sponge/poseidon/poseidon_grain_lfsr.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/elliptic_curves/bls12/bls12_381/fr.h\"\n#include \"tachyon/math/finite_fields/baby_bear/baby_bear.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n\nnamespace tachyon::crypto {\n\nnamespace {\n\nclass PoseidonGrainLFSRTest\n    : public math::FiniteFieldTest<math::bls12_381::Fr> {\n public:\n  void SetUp() override {\n    default_config_.is_sbox_an_inverse = false;\n    default_config_.prime_num_bits = 255;\n    default_config_.state_len = 3;\n    default_config_.num_full_rounds = 8;\n    default_config_.num_partial_rounds = 31;\n  }\n\n protected:\n  PoseidonGrainLFSRConfig default_config_;\n};\n\n}  // namespace\n\nTEST_F(PoseidonGrainLFSRTest, GetBits) {\n  PoseidonGrainLFSR<math::bls12_381::Fr> lfsr(default_config_);\n\n  std::bitset<255> bits = lfsr.GetBits(255);\n  ASSERT_EQ(bits.size(), 255);\n  EXPECT_EQ(\n      bits,\n      std::bitset<255>(\n          \"00001100110011010111010101110101111010101001010111110100001011100000\"\n          \"11000011001111011111101000111100111100100000111111100000101101011001\"\n          \"11001101001101111001110110111001110000110001000110011111010111000101\"\n          \"110111011110110010011001010101011011110011111101110\"));\n}\n\nTEST_F(PoseidonGrainLFSRTest, GetFieldElementsModP) {\n  PoseidonGrainLFSR<math::bls12_381::Fr> lfsr(default_config_);\n\n  // clang-format off\n  EXPECT_EQ(lfsr.GetFieldElementsModP(1)[0],\n            *math::bls12_381::Fr::FromDecString(\n                \"27117311055620256798560880810000042840428971800021819916023577129547249660720\"));\n  EXPECT_EQ(lfsr.GetFieldElementsModP(1)[0],\n            *math::bls12_381::Fr::FromDecString(\n                \"51641662388546346858987925410984003801092143452466182801674685248597955169158\"));\n  // clang-format on\n}\n\nTEST_F(PoseidonGrainLFSRTest, GetFieldElementsRejectionSampling) {\n  PoseidonGrainLFSR<math::bls12_381::Fr> lfsr(default_config_);\n\n  // clang-format off\n  EXPECT_EQ(lfsr.GetFieldElementsRejectionSampling(1)[0],\n            *math::bls12_381::Fr::FromDecString(\n                \"27117311055620256798560880810000042840428971800021819916023577129547249660720\"));\n  EXPECT_EQ(lfsr.GetFieldElementsRejectionSampling(1)[0],\n            *math::bls12_381::Fr::FromDecString(\n                \"51641662388546346858987925410984003801092143452466182801674685248597955169158\"));\n  // clang-format on\n}\n\nTEST_F(PoseidonGrainLFSRTest, GrainLFSRConsistency) {\n  PoseidonGrainLFSR<math::bls12_381::Fr> lfsr(default_config_);\n\n  // clang-format off\n  EXPECT_EQ(lfsr.GetFieldElementsRejectionSampling(1)[0],\n            *math::bls12_381::Fr::FromDecString(\n                \"27117311055620256798560880810000042840428971800021819916023577129547249660720\"));\n  EXPECT_EQ(lfsr.GetFieldElementsRejectionSampling(1)[0],\n            *math::bls12_381::Fr::FromDecString(\n                \"51641662388546346858987925410984003801092143452466182801674685248597955169158\"));\n  EXPECT_EQ(lfsr.GetFieldElementsModP(1)[0],\n            *math::bls12_381::Fr::FromDecString(\n                \"30468495022634911716522728179277518871747767531215914044579216845399211650580\"));\n  EXPECT_EQ(lfsr.GetFieldElementsModP(1)[0],\n            *math::bls12_381::Fr::FromDecString(\n                \"17250718238509906485015112994867732544602358855445377986727968022920517907825\"));\n  // clang-format on\n}\n\nnamespace {\n\nclass PackedPoseidonGrainLFSRTest\n    : public math::FiniteFieldTest<math::PackedBabyBear> {\n public:\n  void SetUp() override {\n    default_config_.is_sbox_an_inverse = false;\n    default_config_.prime_num_bits = 31;\n    default_config_.state_len = 3;\n    default_config_.num_full_rounds = 8;\n    default_config_.num_partial_rounds = 31;\n  }\n\n protected:\n  PoseidonGrainLFSRConfig default_config_;\n};\n\n}  // namespace\n\nTEST_F(PackedPoseidonGrainLFSRTest, GetBits) {\n  PoseidonGrainLFSR<math::PackedBabyBear> packed_lfsr(default_config_);\n\n  std::bitset<31> packed_bits = packed_lfsr.GetBits(31);\n  ASSERT_EQ(packed_bits.size(), 31);\n\n  PoseidonGrainLFSR<math::BabyBear> lfsr(default_config_);\n\n  std::bitset<31> bits = lfsr.GetBits(31);\n  ASSERT_EQ(bits.size(), 31);\n  EXPECT_EQ(packed_bits, bits);\n}\n\nTEST_F(PackedPoseidonGrainLFSRTest, GetFieldElementsModP) {\n  PoseidonGrainLFSR<math::PackedBabyBear> packed_lfsr(default_config_);\n  PoseidonGrainLFSR<math::BabyBear> lfsr(default_config_);\n\n  EXPECT_EQ(packed_lfsr.GetFieldElementsModP(1)[0],\n            math::PackedBabyBear::Broadcast(lfsr.GetFieldElementsModP(1)[0]));\n}\n\nTEST_F(PackedPoseidonGrainLFSRTest, GetFieldElementsRejectionSampling) {\n  PoseidonGrainLFSR<math::PackedBabyBear> packed_lfsr(default_config_);\n  PoseidonGrainLFSR<math::BabyBear> lfsr(default_config_);\n\n  EXPECT_EQ(packed_lfsr.GetFieldElementsRejectionSampling(1)[0],\n            math::PackedBabyBear::Broadcast(\n                lfsr.GetFieldElementsRejectionSampling(1)[0]));\n}\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon/poseidon_params.h",
    "content": "#ifndef TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON_POSEIDON_PARAMS_H_\n#define TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON_POSEIDON_PARAMS_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include \"tachyon/math/elliptic_curves/bn/bn254/fr.h\"\n#include \"tachyon/math/finite_fields/baby_bear/internal/baby_bear.h\"\n\nnamespace tachyon::crypto {\n\ntemplate <typename _Field, size_t Rate, uint32_t Alpha, size_t FullRounds,\n          size_t PartialRounds, size_t Capacity = 1>\nstruct PoseidonParams {\n  using Field = _Field;\n\n  // The rate (in terms of number of field elements).\n  // See https://iacr.org/archive/eurocrypt2008/49650180/49650180.pdf\n  constexpr static size_t kRate = Rate;\n  // The capacity (in terms of number of field elements).\n  constexpr static size_t kCapacity = Capacity;\n  constexpr static size_t kWidth = Rate + Capacity;\n  // NOTE(ashjeong): |Alpha| is also referred to as |D|\n  // Exponent used in S-boxes.\n  constexpr static uint32_t kAlpha = Alpha;\n  // Number of rounds in a full-round operation.\n  constexpr static size_t kFullRounds = FullRounds;\n  // Number of rounds in a partial-round operation.\n  constexpr static size_t kPartialRounds = PartialRounds;\n};\n\n// NOTE(ashjeong): The variables names' ending number refers to the |Width|;\n// however, note that the |PartialRounds| and |FullRounds| depend on both\n// |Width| and |Alpha|.\nusing BabyBearPoseidonParams16 = PoseidonParams<math::BabyBear, 15, 7, 8, 22>;\nusing BabyBearPoseidonParams24 = PoseidonParams<math::BabyBear, 23, 7, 8, 22>;\nusing BN254PoseidonParams9 = PoseidonParams<math::bn254::Fr, 8, 5, 8, 63>;\nusing BN254PoseidonParams5 = PoseidonParams<math::bn254::Fr, 4, 5, 8, 60>;\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON_POSEIDON_PARAMS_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon/poseidon_sponge_base.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON_POSEIDON_SPONGE_BASE_H_\n#define TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON_POSEIDON_SPONGE_BASE_H_\n\n#include <vector>\n\n#include \"third_party/eigen3/Eigen/Core\"\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/crypto/hashes/prime_field_serializable.h\"\n#include \"tachyon/crypto/hashes/sponge/sponge.h\"\n\nnamespace tachyon::crypto {\n\ntemplate <typename Derived>\nstruct PoseidonSpongeBase : public FieldBasedCryptographicSponge<Derived> {\n  using Params = typename CryptographicSpongeTraits<Derived>::Params;\n  using F = typename CryptographicSpongeTraits<Derived>::F;\n\n  // CryptographicSponge methods\n  template <typename T>\n  bool Absorb(SpongeState<Params>& state, const T& input) const {\n    if constexpr (std::is_constructible_v<absl::Span<const F>, T>) {\n      return Absorb(state, absl::Span<const F>(input));\n    }\n    std::vector<F> elements;\n    if (!SerializeToFieldElements(input, &elements)) return false;\n    return Absorb(state, absl::MakeConstSpan(elements));\n  }\n\n  bool Absorb(SpongeState<Params>& state, absl::Span<const F> input) const {\n    const Derived& derived = static_cast<const Derived&>(*this);\n\n    switch (state.mode.type) {\n      case DuplexSpongeMode::Type::kAbsorbing: {\n        size_t absorb_index = state.mode.next_index;\n        if (absorb_index == Params::kRate) {\n          derived.Permute(state);\n          absorb_index = 0;\n        }\n        AbsorbInternal(state, absorb_index, input);\n        return true;\n      }\n      case DuplexSpongeMode::Type::kSqueezing: {\n        derived.Permute(state);\n        AbsorbInternal(state, 0, input);\n        return true;\n      }\n    }\n    NOTREACHED();\n    return false;\n  }\n\n  std::vector<uint8_t> SqueezeBytes(SpongeState<Params>& state,\n                                    size_t num_bytes) const {\n    size_t usable_bytes = (F::kModulusBits - 1) / 8;\n\n    size_t num_elements = (num_bytes + usable_bytes - 1) / usable_bytes;\n    std::vector<F> src_elements =\n        SqueezeNativeFieldElements(state, num_elements);\n\n    std::vector<F> bytes;\n    bytes.reserve(usable_bytes * num_elements);\n    for (const F& elem : src_elements) {\n      auto elem_bytes = elem.ToBigInt().ToBytesLE();\n      bytes.insert(bytes.end(), elem_bytes.begin(), elem_bytes.end());\n    }\n\n    bytes.resize(num_bytes, F::Zero());\n    return bytes;\n  }\n\n  std::vector<bool> SqueezeBits(SpongeState<Params>& state,\n                                size_t num_bits) const {\n    size_t usable_bits = F::kModulusBits - 1;\n\n    size_t num_elements = (num_bits + usable_bits - 1) / usable_bits;\n    std::vector<F> src_elements =\n        SqueezeNativeFieldElements(state, num_elements);\n\n    std::vector<bool> bits;\n    for (const F& elem : src_elements) {\n      std::bitset<F::kModulusBits> elem_bits =\n          elem.ToBigInt().template ToBitsLE<F::kModulusBits>();\n      bits.insert(bits.end(), elem_bits.begin(), elem_bits.end());\n    }\n    bits.resize(num_bits);\n    return bits;\n  }\n\n  template <typename F2 = F>\n  std::vector<F2> SqueezeFieldElementsWithSizes(\n      SpongeState<Params>& state,\n      const std::vector<FieldElementSize>& sizes) const {\n    if constexpr (F::Characteristic() == F2::Characteristic()) {\n      // native case\n      return this->SqueezeNativeFieldElementsWithSizes(state, sizes);\n    }\n    return this->template SqueezeFieldElementsWithSizesDefaultImpl<F2>(state,\n                                                                       sizes);\n  }\n\n  template <typename F2 = F>\n  std::vector<F2> SqueezeFieldElements(SpongeState<Params>& state,\n                                       size_t num_elements) const {\n    if constexpr (std::is_same_v<F, F2>) {\n      return SqueezeNativeFieldElements(state, num_elements);\n    } else {\n      return SqueezeFieldElementsWithSizes<F2>(\n          state, std::vector<FieldElementSize>(num_elements,\n                                               FieldElementSize::Full()));\n    }\n  }\n\n  // FieldBasedCryptographicSponge methods\n  std::vector<F> SqueezeNativeFieldElements(SpongeState<Params>& state,\n                                            size_t num_elements) const {\n    const Derived& derived = static_cast<const Derived&>(*this);\n\n    // NOTE(batzor): |SqueezeInternal| will fill all the garbage values, so it\n    // is safe to have it uninitialized.\n    std::vector<F> ret(num_elements);\n    switch (state.mode.type) {\n      case DuplexSpongeMode::Type::kAbsorbing: {\n        derived.Permute(state);\n        SqueezeInternal(state, 0, &ret);\n        return ret;\n      }\n      case DuplexSpongeMode::Type::kSqueezing: {\n        size_t squeeze_index = state.mode.next_index;\n        if (squeeze_index == Params::kRate) {\n          derived.Permute(state);\n          squeeze_index = 0;\n        }\n        SqueezeInternal(state, squeeze_index, &ret);\n        return ret;\n      }\n    }\n    NOTREACHED();\n    return {};\n  }\n\n protected:\n  void ApplyARKFull(SpongeState<Params>& state,\n                    Eigen::Index round_number) const {\n    const Derived& derived = static_cast<const Derived&>(*this);\n    auto& config = derived.config;\n    state.elements += config.ark.row(round_number);\n  }\n\n  void ApplyARKPartial(SpongeState<Params>& state,\n                       Eigen::Index round_number) const {\n    const Derived& derived = static_cast<const Derived&>(*this);\n    auto& config = derived.config;\n    state.elements[0] += config.ark.row(round_number)[0];\n  }\n\n  void ApplySBoxFull(SpongeState<Params>& state) const {\n    // Full rounds apply the S-Box (xᵅ) to every element of |state|.\n    for (F& elem : state.elements) {\n      elem = elem.template ConstPow<Params::kAlpha>();\n    }\n  }\n\n  void ApplySBoxPartial(SpongeState<Params>& state) const {\n    // Partial rounds apply the S-Box (xᵅ) to just the first element of\n    // |state|.\n    state[0] = state[0].template ConstPow<Params::kAlpha>();\n  }\n\n private:\n  // Absorbs everything in |elements|, this does not end in an absorbing.\n  void AbsorbInternal(SpongeState<Params>& state, size_t rate_start_index,\n                      absl::Span<const F> elements) const {\n    const Derived& derived = static_cast<const Derived&>(*this);\n    size_t elements_idx = 0;\n    while (true) {\n      size_t remaining_size = elements.size() - elements_idx;\n      // if we can finish in this call\n      if (rate_start_index + remaining_size <= Params::kRate) {\n        for (size_t i = 0; i < remaining_size; ++i, ++elements_idx) {\n          state[Params::kCapacity + i + rate_start_index] +=\n              elements[elements_idx];\n        }\n        state.mode.type = DuplexSpongeMode::Type::kAbsorbing;\n        state.mode.next_index = rate_start_index + remaining_size;\n        break;\n      }\n      // otherwise absorb (|Params::kRate| - |rate_start_index|) elements\n      size_t num_elements_absorbed = Params::kRate - rate_start_index;\n      for (size_t i = 0; i < num_elements_absorbed; ++i, ++elements_idx) {\n        state[Params::kCapacity + i + rate_start_index] +=\n            elements[elements_idx];\n      }\n      derived.Permute(state);\n      rate_start_index = 0;\n    }\n  }\n\n  // Squeeze |output| many elements. This does not end in a squeezing.\n  void SqueezeInternal(SpongeState<Params>& state, size_t rate_start_index,\n                       std::vector<F>* output) const {\n    const Derived& derived = static_cast<const Derived&>(*this);\n    size_t output_size = output->size();\n    size_t output_idx = 0;\n    while (true) {\n      size_t output_remaining_size = output_size - output_idx;\n      // if we can finish in this call\n      if (rate_start_index + output_remaining_size <= Params::kRate) {\n        for (size_t i = 0; i < output_remaining_size; ++i) {\n          (*output)[output_idx + i] =\n              state[Params::kCapacity + rate_start_index + i];\n        }\n        state.mode.type = DuplexSpongeMode::Type::kSqueezing;\n        state.mode.next_index = rate_start_index + output_remaining_size;\n        return;\n      }\n\n      // otherwise squeeze (|Params::kRate| - |rate_start_index|) elements\n      size_t num_elements_squeezed = Params::kRate - rate_start_index;\n      for (size_t i = 0; i < num_elements_squeezed; ++i) {\n        (*output)[output_idx + i] =\n            state[Params::kCapacity + rate_start_index + i];\n      }\n\n      if (output_remaining_size != Params::kRate) {\n        derived.Permute(state);\n      }\n      output_idx += num_elements_squeezed;\n      rate_start_index = 0;\n    }\n  }\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON_POSEIDON_SPONGE_BASE_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon/poseidon_unittest.cc",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#include \"tachyon/crypto/hashes/sponge/poseidon/poseidon.h\"\n\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/buffer/vector_buffer.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon/poseidon_params.h\"\n#include \"tachyon/math/elliptic_curves/bls12/bls12_381/fr.h\"\n#include \"tachyon/math/finite_fields/baby_bear/baby_bear.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n\nnamespace tachyon::crypto {\n\nnamespace {\n\nclass PoseidonTest : public math::FiniteFieldTest<math::bls12_381::Fr> {};\n\n}  // namespace\n\nTEST_F(PoseidonTest, AbsorbSqueeze) {\n  using Fr = math::bls12_381::Fr;\n  using Params = PoseidonParams<Fr, 2, 17, 8, 31>;\n\n  auto config = PoseidonConfig<Params>::Create(0);\n  PoseidonSponge<Params> sponge(std::move(config));\n  SpongeState<Params> state;\n  std::vector<Fr> inputs = {Fr(0), Fr(1), Fr(2)};\n  ASSERT_TRUE(sponge.Absorb(state, inputs));\n  std::vector<Fr> result = sponge.SqueezeNativeFieldElements(state, 3);\n  std::vector<Fr> expected = {\n      *Fr::FromDecString(\n          \"404427934635713040283377530022421867103101638970489622\"\n          \"78675457993207843616876\"),\n      *Fr::FromDecString(\n          \"266437446169989800029115314522409928771122402171620296\"\n          \"0480903840045233645301\"),\n      *Fr::FromDecString(\n          \"501910788280669236620702282565306929518015040434228440\"\n          \"38937334196346054068797\"),\n  };\n  EXPECT_EQ(result, expected);\n}\n\nTEST_F(PoseidonTest, Copyable) {\n  using Fr = math::bls12_381::Fr;\n  using Params = PoseidonParams<Fr, 2, 17, 8, 31>;\n\n  auto config = PoseidonConfig<Params>::Create(0);\n  PoseidonSponge<Params> expected(std::move(config));\n\n  base::Uint8VectorBuffer write_buf;\n  ASSERT_TRUE(write_buf.Grow(base::EstimateSize(expected)));\n  ASSERT_TRUE(write_buf.Write(expected));\n  ASSERT_TRUE(write_buf.Done());\n\n  write_buf.set_buffer_offset(0);\n\n  PoseidonSponge<Params> value;\n  ASSERT_TRUE(write_buf.Read(&value));\n\n  EXPECT_EQ(value, expected);\n}\n\nnamespace {\n\nclass PackedPoseidonTest : public math::FiniteFieldTest<math::PackedBabyBear> {\n};\n\n}  // namespace\n\nTEST_F(PackedPoseidonTest, AbsorbSqueeze) {\n  using F = math::BabyBear;\n  using PackedF = math::PackedBabyBear;\n  using Params = PoseidonParams<F, 2, 17, 8, 31>;\n  using PackedParams = PoseidonParams<PackedF, 2, 17, 8, 31>;\n\n  auto packed_config = PoseidonConfig<PackedParams>::Create(0);\n  PoseidonSponge<PackedParams> packed_sponge(std::move(packed_config));\n  SpongeState<PackedParams> packed_state;\n  std::vector<PackedF> packed_inputs = {PackedF(0), PackedF(1), PackedF(2)};\n  ASSERT_TRUE(packed_sponge.Absorb(packed_state, packed_inputs));\n  std::vector<PackedF> packed_result =\n      packed_sponge.SqueezeNativeFieldElements(packed_state, 1);\n\n  auto config = PoseidonConfig<Params>::Create(0);\n  PoseidonSponge<Params> sponge(std::move(config));\n  SpongeState<Params> state;\n  std::vector<F> inputs = {F(0), F(1), F(2)};\n  ASSERT_TRUE(sponge.Absorb(state, inputs));\n  std::vector<F> result = sponge.SqueezeNativeFieldElements(state, 1);\n\n  EXPECT_EQ(packed_result[0], PackedF::Broadcast(result[0]));\n}\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon/sparse_mds_matrix.h",
    "content": "// Copyright 2022 Ethereum Foundation\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.EF and the LICENCE-APACHE.EF\n// file.\n\n#ifndef TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON_SPARSE_MDS_MATRIX_H_\n#define TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON_SPARSE_MDS_MATRIX_H_\n\n#include <string>\n#include <utility>\n\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/base/buffer/copyable.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/strings/string_util.h\"\n#include \"tachyon/math/matrix/matrix_types.h\"\n\nnamespace tachyon {\nnamespace crypto {\n\n// +------+------+-----+------+   +-----+\n// | M₀,₀ | M₀,₁ | ... | M₀,ₜ | * | v₀  |\n// +------+------+-----+------+   +-----+\n// | M₁,₀ |                   |   | v₁  |\n// +------+                   +   +-----+\n// | ...  |         I         |   | ... |\n// +------+                   +   +-----+\n// | Mₜ,₀ |                   |   | vₜ  |\n// +------+------+-----+------+   +-----+\n//\n// |v[0]| = M₀,₀ * v₀ + M₀,₁ * v₁ + ... + M₀,ₜ * vₜ\n// |v[i]| = Mᵢ,₀ * v₀ + vᵢ, where 0 < i ≤ t\ntemplate <typename F>\nclass SparseMDSMatrix {\n public:\n  SparseMDSMatrix() = default;\n  SparseMDSMatrix(const math::Vector<F>& row, const math::Vector<F>& col_hat)\n      : row_(row), col_hat_(col_hat) {\n    CHECK_EQ(row_.size(), col_hat_.size() + 1);\n  }\n  SparseMDSMatrix(math::Vector<F>&& row, math::Vector<F>&& col_hat)\n      : row_(std::move(row)), col_hat_(std::move(col_hat)) {\n    CHECK_EQ(row_.size(), col_hat_.size() + 1);\n  }\n\n  static SparseMDSMatrix FromMDSMatrix(const math::Matrix<F>& mds_matrix) {\n    CHECK_EQ(mds_matrix.rows(), mds_matrix.cols());\n    CHECK_GT(mds_matrix.rows(), 1);\n    CHECK_EQ(\n        mds_matrix.block(1, 1, mds_matrix.rows() - 1, mds_matrix.cols() - 1),\n        math::Matrix<F>::Identity(mds_matrix.rows() - 1,\n                                  mds_matrix.cols() - 1));\n    return {mds_matrix.row(0),\n            mds_matrix.block(1, 0, mds_matrix.rows() - 1, 1)};\n  }\n\n  const math::Vector<F>& row() const { return row_; }\n  const math::Vector<F>& col_hat() const { return col_hat_; }\n\n  bool operator==(const SparseMDSMatrix& other) const {\n    return row_ == other.row_ && col_hat_ == other.col_hat_;\n  }\n  bool operator!=(const SparseMDSMatrix& other) const {\n    return !operator==(other);\n  }\n\n  void Apply(math::Vector<F>& v) const {\n    F v_0 = F::Zero();\n    for (Eigen::Index i = 0; i < v.size(); ++i) {\n      v_0 += row_[i] * v[i];\n    }\n\n    for (Eigen::Index i = 1; i < v.size(); ++i) {\n      v[i] += col_hat_[i - 1] * v[0];\n    }\n\n    v[0] = std::move(v_0);\n  }\n\n  math::Matrix<F> Construct() const {\n    math::Matrix<F> ret = math::Matrix<F>::Identity(row_.size(), row_.size());\n    ret.row(0) = row_;\n    ret.block(1, 0, col_hat_.size(), 1) = col_hat_;\n    return ret;\n  }\n\n  std::string ToString() const {\n    return absl::Substitute(\"{row: $0, col_hat: $1}\",\n                            base::ContainerToString(row_),\n                            base::ContainerToString(col_hat_));\n  }\n\n  std::string ToHexString(bool pad_zero = false) const {\n    return absl::Substitute(\"{row: $0, col_hat: $1}\",\n                            base::ContainerToHexString(row_, pad_zero),\n                            base::ContainerToHexString(col_hat_, pad_zero));\n  }\n\n private:\n  // |row_| = {M₀,₀, ..., M₀,ₜ}\n  math::Vector<F> row_;\n  // |col_hat_| = {M₁,₀, ..., Mₜ,₀}\n  math::Vector<F> col_hat_;\n};\n\n}  // namespace crypto\n\nnamespace base {\n\ntemplate <typename F>\nclass Copyable<crypto::SparseMDSMatrix<F>> {\n public:\n  static bool WriteTo(const crypto::SparseMDSMatrix<F>& matrix,\n                      Buffer* buffer) {\n    return buffer->WriteMany(matrix.row(), matrix.col_hat());\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer,\n                       crypto::SparseMDSMatrix<F>* matrix) {\n    math::Vector<F> row;\n    math::Vector<F> col_hat;\n    if (!buffer.ReadMany(&row, &col_hat)) {\n      return false;\n    }\n\n    *matrix = {std::move(row), std::move(col_hat)};\n    return true;\n  }\n\n  static size_t EstimateSize(const crypto::SparseMDSMatrix<F>& matrix) {\n    return base::EstimateSize(matrix.row(), matrix.col_hat());\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON_SPARSE_MDS_MATRIX_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon/sparse_mds_matrix_unittest.cc",
    "content": "#include \"tachyon/crypto/hashes/sponge/poseidon/sparse_mds_matrix.h\"\n\n#include \"tachyon/base/buffer/vector_buffer.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/fr.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n\nnamespace tachyon::crypto {\n\nusing F = math::bn254::Fr;\n\nnamespace {\n\nclass SparseMDSMatrixTest : public math::FiniteFieldTest<F> {};\n\n}  // namespace\n\nTEST_F(SparseMDSMatrixTest, FromMDSMatrix) {\n  math::Matrix<F> matrix = math::Matrix<F>::Random(5, 5);\n  EXPECT_DEATH(SparseMDSMatrix<F>::FromMDSMatrix(matrix), \"\");\n  matrix.block(1, 1, 4, 4) = math::Matrix<F>::Identity(4, 4);\n  auto sparse_mds_matrix = SparseMDSMatrix<F>::FromMDSMatrix(matrix);\n  EXPECT_EQ(sparse_mds_matrix.row(), matrix.row(0).transpose());\n  EXPECT_EQ(sparse_mds_matrix.col_hat(), matrix.block(1, 0, 4, 1));\n}\n\nTEST_F(SparseMDSMatrixTest, Apply) {\n  SparseMDSMatrix<F> sparse_mds_matrix(math::Vector<F>::Random(5),\n                                       math::Vector<F>::Random(4));\n  math::Matrix<F> matrix = sparse_mds_matrix.Construct();\n  math::Vector<F> state = math::Vector<F>::Random(5);\n  math::Vector<F> state2 = state;\n  sparse_mds_matrix.Apply(state2);\n  EXPECT_EQ(matrix * state, state2);\n}\n\nTEST_F(SparseMDSMatrixTest, Copyable) {\n  SparseMDSMatrix<F> expected(math::Vector<F>::Random(5),\n                              math::Vector<F>::Random(4));\n\n  base::Uint8VectorBuffer write_buf;\n  ASSERT_TRUE(write_buf.Grow(base::EstimateSize(expected)));\n  ASSERT_TRUE(write_buf.Write(expected));\n  ASSERT_TRUE(write_buf.Done());\n\n  write_buf.set_buffer_offset(0);\n\n  SparseMDSMatrix<F> value;\n  ASSERT_TRUE(write_buf.Read(&value));\n\n  EXPECT_EQ(value, expected);\n}\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon2/BUILD.bazel",
    "content": "load(\"//bazel:tachyon.bzl\", \"if_gpu_is_configured\")\nload(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"poseidon2\",\n    hdrs = [\"poseidon2.h\"],\n    deps = [\n        \":poseidon2_config\",\n        \":poseidon2_horizen_external_matrix\",\n        \":poseidon2_horizen_internal_matrix\",\n        \":poseidon2_plonky3_external_matrix\",\n        \":poseidon2_plonky3_internal_matrix\",\n        \":poseidon2_vendor\",\n        \"//tachyon/base/buffer:copyable\",\n        \"//tachyon/crypto/hashes/sponge:sponge_state\",\n        \"//tachyon/crypto/hashes/sponge/poseidon:poseidon_sponge_base\",\n        \"//tachyon/math/finite_fields:finite_field_traits\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"poseidon2_config_entry\",\n    hdrs = [\"poseidon2_config_entry.h\"],\n    deps = [\"//tachyon/crypto/hashes/sponge/poseidon:poseidon_config_entry_base\"],\n)\n\ntachyon_cc_library(\n    name = \"poseidon2_config\",\n    hdrs = [\"poseidon2_config.h\"],\n    deps = [\n        \":poseidon2_config_entry\",\n        \"//tachyon/base/ranges:algorithm\",\n        \"//tachyon/crypto/hashes/sponge/poseidon:poseidon_config_base\",\n        \"//tachyon/math/finite_fields:finite_field_traits\",\n        \"//tachyon/math/matrix:matrix_utils\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"poseidon2_external_matrix\",\n    hdrs = [\"poseidon2_external_matrix.h\"],\n    deps = [\n        \":poseidon2_external_matrix_traits_forward\",\n        \"//tachyon/math/matrix:matrix_types\",\n        \"//tachyon/math/matrix:matrix_utils\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"poseidon2_external_matrix_traits_forward\",\n    hdrs = [\"poseidon2_external_matrix_traits_forward.h\"],\n)\n\ntachyon_cc_library(\n    name = \"poseidon2_horizen_external_matrix\",\n    hdrs = [\"poseidon2_horizen_external_matrix.h\"],\n    deps = [\":poseidon2_external_matrix\"],\n)\n\ntachyon_cc_library(\n    name = \"poseidon2_horizen_internal_matrix\",\n    hdrs = [\"poseidon2_horizen_internal_matrix.h\"],\n    deps = [\"//tachyon/math/matrix:matrix_types\"],\n)\n\ntachyon_cc_library(\n    name = \"poseidon2_param_traits_forward\",\n    hdrs = [\"poseidon2_param_traits_forward.h\"],\n)\n\ntachyon_cc_library(\n    name = \"poseidon2_params\",\n    hdrs = [\"poseidon2_params.h\"],\n    deps = [\n        \":poseidon2_vendor\",\n        \"//tachyon/base/types:always_false\",\n        \"//tachyon/math/finite_fields:finite_field_traits\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"poseidon2_plonky3_external_matrix\",\n    hdrs = [\"poseidon2_plonky3_external_matrix.h\"],\n    deps = [\":poseidon2_external_matrix\"],\n)\n\ntachyon_cc_library(\n    name = \"poseidon2_plonky3_internal_matrix\",\n    hdrs = [\"poseidon2_plonky3_internal_matrix.h\"],\n    deps = [\n        \":poseidon2_horizen_internal_matrix\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/math/finite_fields:finite_field_traits\",\n        \"//tachyon/math/matrix:matrix_types\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"poseidon2_vendor\",\n    hdrs = [\"poseidon2_vendor.h\"],\n    deps = [\n        \"//tachyon:export\",\n        \"//tachyon/base:logging\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"poseidon2_unittests\",\n    srcs = [\n        \"poseidon2_external_matrix_unittest.cc\",\n        \"poseidon2_internal_matrix_unittest.cc\",\n        \"poseidon2_unittest.cc\",\n    ],\n    deps = [\n        \":poseidon2\",\n        \":poseidon2_params\",\n        \"//tachyon/base/buffer:vector_buffer\",\n        \"//tachyon/crypto/hashes/sponge/poseidon2/param_traits:poseidon2_baby_bear\",\n        \"//tachyon/crypto/hashes/sponge/poseidon2/param_traits:poseidon2_goldilocks\",\n        \"//tachyon/math/finite_fields/mersenne31\",\n        \"//tachyon/math/finite_fields/test:finite_field_test\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"poseidon2_gpu_unittests\",\n    srcs = if_gpu_is_configured([\"poseidon2_gpu_unittest.cc\"]),\n    deps = [\n        \":poseidon2\",\n        \":poseidon2_params\",\n        \"//tachyon/crypto/hashes/sponge/poseidon2/icicle:icicle_poseidon2_holder\",\n        \"//tachyon/crypto/hashes/sponge/poseidon2/param_traits:poseidon2_baby_bear\",\n        \"//tachyon/crypto/hashes/sponge/poseidon2/param_traits:poseidon2_bn254\",\n        \"//tachyon/math/finite_fields/test:finite_field_test\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon2/icicle/BUILD.bazel",
    "content": "load(\"@icicle//:build_defs.bzl\", \"BABY_BEAR\", \"BN254\", \"icicle_defines\")\nload(\"@local_config_cuda//cuda:build_defs.bzl\", \"if_cuda\")\nload(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cuda_library\")\n\ntachyon_cuda_library(\n    name = \"icicle_poseidon2_baby_bear\",\n    srcs = if_cuda([\"icicle_poseidon2_baby_bear.cc\"]),\n    hdrs = [\"icicle_poseidon2_baby_bear.h\"],\n    force_exceptions = True,\n    local_defines = icicle_defines(BABY_BEAR),\n    deps = [\n        \":icicle_poseidon2\",\n        \"//tachyon/base:openmp_util\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/crypto/hashes/sponge/poseidon2:poseidon2_vendor\",\n        \"//tachyon/device/gpu:gpu_enums\",\n        \"//tachyon/device/gpu:gpu_logging\",\n        \"@icicle//:poseidon2_baby_bear\",\n    ] + if_cuda([\n        \"@local_config_cuda//cuda:cudart_static\",\n    ]),\n)\n\ntachyon_cuda_library(\n    name = \"icicle_poseidon2_bn254\",\n    srcs = if_cuda([\"icicle_poseidon2_bn254.cc\"]),\n    hdrs = [\"icicle_poseidon2_bn254.h\"],\n    force_exceptions = True,\n    local_defines = icicle_defines(BN254),\n    deps = [\n        \":icicle_poseidon2\",\n        \"//tachyon/base:openmp_util\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/crypto/hashes/sponge/poseidon2:poseidon2_vendor\",\n        \"//tachyon/device/gpu:gpu_enums\",\n        \"//tachyon/device/gpu:gpu_logging\",\n        \"@icicle//:poseidon2_bn254\",\n    ] + if_cuda([\n        \"@local_config_cuda//cuda:cudart_static\",\n    ]),\n)\n\ntachyon_cc_library(\n    name = \"icicle_poseidon2\",\n    hdrs = [\"icicle_poseidon2.h\"],\n    deps = [\n        \"//tachyon:export\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/device/gpu:gpu_device_functions\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:fr\",\n        \"//tachyon/math/finite_fields/baby_bear\",\n        \"@com_google_absl//absl/types:span\",\n        \"@icicle//:hdrs\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"icicle_poseidon2_holder\",\n    hdrs = [\"icicle_poseidon2_holder.h\"],\n    visibility = [\"//visibility:public\"],\n    deps = [\n        \":icicle_poseidon2_baby_bear\",\n        \":icicle_poseidon2_bn254\",\n        \"//tachyon/crypto/hashes/sponge/poseidon2:poseidon2_config\",\n        \"//tachyon/device/gpu:scoped_mem_pool\",\n        \"//tachyon/device/gpu:scoped_stream\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon2/icicle/icicle_poseidon2.h",
    "content": "#ifndef TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_ICICLE_ICICLE_POSEIDON2_H_\n#define TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_ICICLE_ICICLE_POSEIDON2_H_\n\n#include <memory>\n\n#include \"absl/types/span.h\"\n#include \"third_party/icicle/include/hash/hash_config.h\"\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_vendor.h\"\n#include \"tachyon/device/gpu/gpu_device_functions.h\"\n#include \"tachyon/export.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/fr.h\"\n#include \"tachyon/math/finite_fields/baby_bear/baby_bear.h\"\n\nnamespace tachyon::crypto {\n\ntemplate <class F>\nstruct IsIciclePoseidon2SupportedImpl {\n  constexpr static bool value = false;\n};\n\ntemplate <>\nstruct IsIciclePoseidon2SupportedImpl<math::BabyBear> {\n  constexpr static bool value = true;\n};\n\ntemplate <>\nstruct IsIciclePoseidon2SupportedImpl<math::bn254::Fr> {\n  constexpr static bool value = true;\n};\n\ntemplate <typename F>\nconstexpr bool IsIciclePoseidon2Supported =\n    IsIciclePoseidon2SupportedImpl<F>::value;\n\nstruct TACHYON_EXPORT IciclePoseidon2Options {\n  bool are_inputs_on_device = false;\n  bool are_outputs_on_device = false;\n  bool is_async = false;\n};\n\ntemplate <typename F>\nclass IciclePoseidon2 {\n public:\n  IciclePoseidon2(\n      gpuMemPool_t mem_pool, gpuStream_t stream,\n      const IciclePoseidon2Options& options = IciclePoseidon2Options())\n      : mem_pool_(mem_pool), stream_(stream) {\n    ::device_context::DeviceContext ctx{stream_, /*device_id=*/0, mem_pool_};\n    config_.reset(new ::hash::HashConfig{\n        ctx,\n        options.are_inputs_on_device,\n        options.are_outputs_on_device,\n        options.is_async,\n    });\n    VLOG(1) << \"IciclePoseidon2 is created\";\n  }\n  IciclePoseidon2(const IciclePoseidon2& other) = delete;\n  IciclePoseidon2& operator=(const IciclePoseidon2& other) = delete;\n  ~IciclePoseidon2() { CHECK(Delete()); }\n\n  void* impl() { return impl_; }\n  const void* impl() const { return impl_; }\n\n  [[nodiscard]] bool Create(unsigned int rate, unsigned int width,\n                            unsigned int alpha, unsigned int external_rounds,\n                            unsigned int internal_rounds,\n                            Poseidon2Vendor external_matrix_vendor,\n                            Poseidon2Vendor internal_matrix_vendor,\n                            absl::Span<const F> round_constants,\n                            absl::Span<const F> internal_matrix_diag);\n\n  [[nodiscard]] bool Load(unsigned int rate, unsigned int width,\n                          Poseidon2Vendor external_matrix_vendor,\n                          Poseidon2Vendor internal_matrix_vendor);\n\n  [[nodiscard]] bool Hash(unsigned int rate, absl::Span<const F> inputs,\n                          absl::Span<F> outputs);\n\n  [[nodiscard]] bool Delete();\n\n private:\n  gpuMemPool_t mem_pool_ = nullptr;\n  gpuStream_t stream_ = nullptr;\n  void* impl_ = nullptr;\n  std::unique_ptr<::hash::HashConfig> config_;\n};\n\ntemplate <>\nTACHYON_EXPORT bool IciclePoseidon2<math::BabyBear>::Create(\n    unsigned int rate, unsigned int width, unsigned int alpha,\n    unsigned int external_rounds, unsigned int internal_rounds,\n    Poseidon2Vendor external_matrix_vendor,\n    Poseidon2Vendor internal_matrix_vendor,\n    absl::Span<const math::BabyBear> round_constants,\n    absl::Span<const math::BabyBear> internal_matrix_diag);\n\ntemplate <>\nTACHYON_EXPORT bool IciclePoseidon2<math::BabyBear>::Load(\n    unsigned int rate, unsigned int width,\n    Poseidon2Vendor external_matrix_vendor,\n    Poseidon2Vendor internal_matrix_vendor);\n\ntemplate <>\nTACHYON_EXPORT bool IciclePoseidon2<math::BabyBear>::Hash(\n    unsigned int rate, absl::Span<const math::BabyBear> inputs,\n    absl::Span<math::BabyBear> outputs);\n\ntemplate <>\nTACHYON_EXPORT bool IciclePoseidon2<math::BabyBear>::Delete();\n\ntemplate <>\nTACHYON_EXPORT bool IciclePoseidon2<math::bn254::Fr>::Create(\n    unsigned int rate, unsigned int width, unsigned int alpha,\n    unsigned int external_rounds, unsigned int internal_rounds,\n    Poseidon2Vendor external_matrix_vendor,\n    Poseidon2Vendor internal_matrix_vendor,\n    absl::Span<const math::bn254::Fr> round_constants,\n    absl::Span<const math::bn254::Fr> internal_matrix_diag);\n\ntemplate <>\nTACHYON_EXPORT bool IciclePoseidon2<math::bn254::Fr>::Load(\n    unsigned int rate, unsigned int width,\n    Poseidon2Vendor external_matrix_vendor,\n    Poseidon2Vendor internal_matrix_vendor);\n\ntemplate <>\nTACHYON_EXPORT bool IciclePoseidon2<math::bn254::Fr>::Hash(\n    unsigned int rate, absl::Span<const math::bn254::Fr> inputs,\n    absl::Span<math::bn254::Fr> outputs);\n\ntemplate <>\nTACHYON_EXPORT bool IciclePoseidon2<math::bn254::Fr>::Delete();\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_ICICLE_ICICLE_POSEIDON2_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon2/icicle/icicle_poseidon2_baby_bear.cc",
    "content": "#include \"tachyon/crypto/hashes/sponge/poseidon2/icicle/icicle_poseidon2_baby_bear.h\"\n\n#include <memory>\n#include <vector>\n\n#include \"third_party/icicle/include/fields/id.h\"\n#include \"third_party/icicle/include/gpu-utils/error_handler.cu.h\"\n#include \"third_party/icicle/src/poseidon2/constants.cu.cc\"  // NOLINT(build/include)\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/openmp_util.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/icicle/icicle_poseidon2.h\"\n#include \"tachyon/device/gpu/gpu_enums.h\"\n#include \"tachyon/device/gpu/gpu_logging.h\"\n\ngpuError_t tachyon_babybear_poseidon2_create_cuda(\n    unsigned int width, unsigned int rate, unsigned int alpha,\n    unsigned int internal_rounds, unsigned int external_rounds,\n    const ::babybear::scalar_t* round_constants,\n    const ::babybear::scalar_t* internal_matrix_diag,\n    ::poseidon2::MdsType mds_type, ::poseidon2::DiffusionStrategy diffusion,\n    ::device_context::DeviceContext& ctx,\n    ::poseidon2::Poseidon2<::babybear::scalar_t>** poseidon) {\n  try {\n    *poseidon = new ::poseidon2::Poseidon2<::babybear::scalar_t>(\n        width, rate, alpha, internal_rounds, external_rounds, round_constants,\n        internal_matrix_diag, mds_type, diffusion, ctx);\n    return gpuSuccess;\n  } catch (const ::IcicleError& error) {\n    LOG(ERROR) << \"Failed tachyon_babybear_poseidon2_create_cuda(): \"\n               << error.what();\n    return cudaErrorUnknown;\n  }\n}\n\ngpuError_t tachyon_babybear_poseidon2_load_cuda(\n    unsigned int width, unsigned int rate, ::poseidon2::MdsType mds_type,\n    ::poseidon2::DiffusionStrategy diffusion,\n    ::device_context::DeviceContext& ctx,\n    ::poseidon2::Poseidon2<::babybear::scalar_t>** poseidon) {\n  try {\n    *poseidon = new ::poseidon2::Poseidon2<::babybear::scalar_t>(\n        width, rate, mds_type, diffusion, ctx);\n    return gpuSuccess;\n  } catch (const IcicleError& error) {\n    LOG(ERROR) << \"Failed tachyon_babybear_poseidon2_load_cuda(): \"\n               << error.what();\n    return cudaErrorUnknown;\n  }\n}\n\ngpuError_t tachyon_babybear_poseidon2_hash_many_cuda(\n    const ::poseidon2::Poseidon2<::babybear::scalar_t>* poseidon,\n    const ::babybear::scalar_t* inputs, ::babybear::scalar_t* output,\n    unsigned int number_of_states, unsigned int input_block_len,\n    unsigned int output_len, ::hash::HashConfig& cfg) {\n  return poseidon->hash_many(inputs, output, number_of_states, input_block_len,\n                             output_len, cfg);\n}\n\ngpuError_t tachyon_babybear_poseidon2_delete_cuda(\n    ::poseidon2::Poseidon2<::babybear::scalar_t>* poseidon) {\n  try {\n    delete poseidon;\n    return gpuSuccess;\n  } catch (const IcicleError& error) {\n    LOG(ERROR) << \"Failed tachyon_babybear_poseidon2_delete_cuda(): \"\n               << error.what();\n    return cudaErrorUnknown;\n  }\n}\n\nnamespace tachyon::crypto {\n\ntemplate <>\nbool IciclePoseidon2<math::BabyBear>::Create(\n    unsigned int rate, unsigned int width, unsigned int alpha,\n    unsigned int external_rounds, unsigned int internal_rounds,\n    Poseidon2Vendor external_matrix_vendor,\n    Poseidon2Vendor internal_matrix_vendor,\n    absl::Span<const math::BabyBear> round_constants,\n    absl::Span<const math::BabyBear> internal_matrix_diag) {\n#if FIELD_ID != BABY_BEAR\n#error Only BABY_BEAR is supported\n#endif\n  if (impl_ != nullptr) {\n    VLOG(1) << \"IciclePoseidon2 was already initialized\";\n    return true;\n  }\n\n  std::vector<::babybear::scalar_t> round_constants_tmp =\n      base::Map(round_constants, [](const math::BabyBear& f) {\n        return ::babybear::scalar_t::from_montgomery(\n            reinterpret_cast<const ::babybear::scalar_t&>(f));\n      });\n  std::vector<::babybear::scalar_t> internal_matrix_diag_tmp =\n      base::Map(internal_matrix_diag, [](const math::BabyBear& f) {\n        return ::babybear::scalar_t::from_montgomery(\n            reinterpret_cast<const ::babybear::scalar_t&>(f));\n      });\n  ::poseidon2::Poseidon2<::babybear::scalar_t>* icicle_poseidon = nullptr;\n  gpuError_t error = tachyon_babybear_poseidon2_create_cuda(\n      width, rate, alpha, internal_rounds, external_rounds,\n      round_constants_tmp.data(), internal_matrix_diag_tmp.data(),\n      external_matrix_vendor == Poseidon2Vendor::kHorizen\n          ? ::poseidon2::DEFAULT_MDS\n          : ::poseidon2::PLONKY,\n      internal_matrix_vendor == Poseidon2Vendor::kHorizen\n          ? ::poseidon2::DEFAULT_DIFFUSION\n          : ::poseidon2::MONTGOMERY,\n      config_->ctx, &icicle_poseidon);\n  if (error != gpuSuccess) return false;\n\n  impl_ = static_cast<void*>(icicle_poseidon);\n  return true;\n}\n\ntemplate <>\nbool IciclePoseidon2<math::BabyBear>::Load(\n    unsigned int rate, unsigned int width,\n    Poseidon2Vendor external_matrix_vendor,\n    Poseidon2Vendor internal_matrix_vendor) {\n#if FIELD_ID != BABY_BEAR\n#error Only BABY_BEAR is supported\n#endif\n  if (impl_ != nullptr) {\n    VLOG(1) << \"IciclePoseidon2 was already initialized\";\n    return true;\n  }\n\n  ::poseidon2::Poseidon2<::babybear::scalar_t>* icicle_poseidon = nullptr;\n  gpuError_t error = tachyon_babybear_poseidon2_load_cuda(\n      width, rate,\n      external_matrix_vendor == Poseidon2Vendor::kHorizen\n          ? ::poseidon2::DEFAULT_MDS\n          : ::poseidon2::PLONKY,\n      internal_matrix_vendor == Poseidon2Vendor::kHorizen\n          ? ::poseidon2::DEFAULT_DIFFUSION\n          : ::poseidon2::MONTGOMERY,\n      config_->ctx, &icicle_poseidon);\n  if (error != gpuSuccess) return false;\n\n  impl_ = static_cast<void*>(icicle_poseidon);\n  return true;\n}\n\ntemplate <>\nbool IciclePoseidon2<math::BabyBear>::Hash(\n    unsigned int rate, absl::Span<const math::BabyBear> inputs,\n    absl::Span<math::BabyBear> outputs) {\n#if FIELD_ID != BABY_BEAR\n#error Only BABY_BEAR is supported\n#endif\n  if (impl_ == nullptr) {\n    VLOG(1) << \"IciclePoseidon2 is not initialized\";\n    return false;\n  }\n\n  // TODO(chokobole): Change it to allocate just once across many |Hash()|\n  // calls.\n  std::vector<::babybear::scalar_t> inputs_tmp(inputs.size());\n  OMP_PARALLEL_FOR(size_t i = 0; i < inputs_tmp.size(); ++i) {\n    inputs_tmp[i] = ::babybear::scalar_t::from_montgomery(\n        reinterpret_cast<const ::babybear::scalar_t&>(inputs[i]));\n  }\n\n  size_t num_states = inputs.size() / rate;\n  gpuError_t error = tachyon_babybear_poseidon2_hash_many_cuda(\n      reinterpret_cast<::poseidon2::Poseidon2<::babybear::scalar_t>*>(impl_),\n      inputs_tmp.data(),\n      reinterpret_cast<::babybear::scalar_t*>(outputs.data()), num_states, rate,\n      outputs.size() / num_states, *config_);\n  if (error != gpuSuccess) {\n    GPU_LOG(ERROR, error)\n        << \"Failed tachyon_babybear_poseidon2_hash_many_cuda()\";\n    return false;\n  }\n\n  OMP_PARALLEL_FOR(size_t i = 0; i < outputs.size(); ++i) {\n    reinterpret_cast<::babybear::scalar_t&>(outputs[i]) =\n        ::babybear::scalar_t::to_montgomery(\n            reinterpret_cast<const ::babybear::scalar_t&>(outputs[i]));\n  }\n  return true;\n}\n\ntemplate <>\nbool IciclePoseidon2<math::BabyBear>::Delete() {\n#if FIELD_ID != BABY_BEAR\n#error Only BABY_BEAR is supported\n#endif\n  if (!impl_) {\n    VLOG(1) << \"IciclePoseidon2 is not initialized\";\n    return false;\n  }\n  gpuError_t error = tachyon_babybear_poseidon2_delete_cuda(\n      reinterpret_cast<::poseidon2::Poseidon2<::babybear::scalar_t>*>(\n          std::exchange(impl_, nullptr)));\n  return error == gpuSuccess;\n}\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon2/icicle/icicle_poseidon2_baby_bear.h",
    "content": "#ifndef TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_ICICLE_ICICLE_POSEIDON2_BABY_BEAR_H_\n#define TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_ICICLE_ICICLE_POSEIDON2_BABY_BEAR_H_\n\n#include \"third_party/icicle/include/fields/stark_fields/babybear.cu.h\"\n#include \"third_party/icicle/include/poseidon2/poseidon2.cu.h\"\n\n#include \"tachyon/device/gpu/gpu_device_functions.h\"\n\nextern \"C\" gpuError_t tachyon_babybear_poseidon2_create_cuda(\n    unsigned int width, unsigned int rate, unsigned int alpha,\n    unsigned int internal_rounds, unsigned int external_rounds,\n    const ::babybear::scalar_t* round_constants,\n    const ::babybear::scalar_t* internal_matrix_diag,\n    ::poseidon2::MdsType mds_type, ::poseidon2::DiffusionStrategy diffusion,\n    ::device_context::DeviceContext& ctx,\n    ::poseidon2::Poseidon2<babybear::scalar_t>** poseidon);\n\nextern \"C\" gpuError_t tachyon_babybear_poseidon2_load_cuda(\n    unsigned int width, unsigned int rate, ::poseidon2::MdsType mds_type,\n    ::poseidon2::DiffusionStrategy diffusion,\n    ::device_context::DeviceContext& ctx,\n    ::poseidon2::Poseidon2<babybear::scalar_t>** poseidon);\n\nextern \"C\" gpuError_t tachyon_babybear_poseidon2_hash_many_cuda(\n    const ::poseidon2::Poseidon2<::babybear::scalar_t>* poseidon,\n    const ::babybear::scalar_t* inputs, ::babybear::scalar_t* output,\n    unsigned int number_of_states, unsigned int input_block_len,\n    unsigned int output_len, ::hash::HashConfig& cfg);\n\nextern \"C\" gpuError_t tachyon_babybear_poseidon2_delete_cuda(\n    ::poseidon2::Poseidon2<::babybear::scalar_t>* poseidon);\n\n#endif  // TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_ICICLE_ICICLE_POSEIDON2_BABY_BEAR_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon2/icicle/icicle_poseidon2_bn254.cc",
    "content": "#include \"tachyon/crypto/hashes/sponge/poseidon2/icicle/icicle_poseidon2_bn254.h\"\n\n#include <memory>\n#include <vector>\n\n#include \"third_party/icicle/include/fields/id.h\"\n#include \"third_party/icicle/include/gpu-utils/error_handler.cu.h\"\n#include \"third_party/icicle/src/poseidon2/constants.cu.cc\"  // NOLINT(build/include)\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/openmp_util.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/icicle/icicle_poseidon2.h\"\n#include \"tachyon/device/gpu/gpu_enums.h\"\n#include \"tachyon/device/gpu/gpu_logging.h\"\n\ngpuError_t tachyon_bn254_poseidon2_create_cuda(\n    unsigned int width, unsigned int rate, unsigned int alpha,\n    unsigned int internal_rounds, unsigned int external_rounds,\n    const ::bn254::scalar_t* round_constants,\n    const ::bn254::scalar_t* internal_matrix_diag,\n    ::poseidon2::MdsType mds_type, ::poseidon2::DiffusionStrategy diffusion,\n    ::device_context::DeviceContext& ctx,\n    ::poseidon2::Poseidon2<::bn254::scalar_t>** poseidon) {\n  try {\n    *poseidon = new ::poseidon2::Poseidon2<::bn254::scalar_t>(\n        width, rate, alpha, internal_rounds, external_rounds, round_constants,\n        internal_matrix_diag, mds_type, diffusion, ctx);\n    return gpuSuccess;\n  } catch (const ::IcicleError& error) {\n    LOG(ERROR) << \"Failed tachyon_bn254_poseidon2_create_cuda(): \"\n               << error.what();\n    return cudaErrorUnknown;\n  }\n}\n\ngpuError_t tachyon_bn254_poseidon2_load_cuda(\n    unsigned int width, unsigned int rate, ::poseidon2::MdsType mds_type,\n    ::poseidon2::DiffusionStrategy diffusion,\n    ::device_context::DeviceContext& ctx,\n    ::poseidon2::Poseidon2<::bn254::scalar_t>** poseidon) {\n  try {\n    *poseidon = new ::poseidon2::Poseidon2<::bn254::scalar_t>(\n        width, rate, mds_type, diffusion, ctx);\n    return gpuSuccess;\n  } catch (const IcicleError& error) {\n    LOG(ERROR) << \"Failed tachyon_bn254_poseidon2_load_cuda(): \"\n               << error.what();\n    return cudaErrorUnknown;\n  }\n}\n\ngpuError_t tachyon_bn254_poseidon2_hash_many_cuda(\n    const ::poseidon2::Poseidon2<::bn254::scalar_t>* poseidon,\n    const ::bn254::scalar_t* inputs, ::bn254::scalar_t* output,\n    unsigned int number_of_states, unsigned int input_block_len,\n    unsigned int output_len, ::hash::HashConfig& cfg) {\n  return poseidon->hash_many(inputs, output, number_of_states, input_block_len,\n                             output_len, cfg);\n}\n\ngpuError_t tachyon_bn254_poseidon2_delete_cuda(\n    ::poseidon2::Poseidon2<::bn254::scalar_t>* poseidon) {\n  try {\n    delete poseidon;\n    return gpuSuccess;\n  } catch (const IcicleError& error) {\n    LOG(ERROR) << \"Failed tachyon_bn254_poseidon2_delete_cuda(): \"\n               << error.what();\n    return cudaErrorUnknown;\n  }\n}\n\nnamespace tachyon::crypto {\n\ntemplate <>\nbool IciclePoseidon2<math::bn254::Fr>::Create(\n    unsigned int rate, unsigned int width, unsigned int alpha,\n    unsigned int external_rounds, unsigned int internal_rounds,\n    Poseidon2Vendor external_matrix_vendor,\n    Poseidon2Vendor internal_matrix_vendor,\n    absl::Span<const math::bn254::Fr> round_constants,\n    absl::Span<const math::bn254::Fr> internal_matrix_diag) {\n#if FIELD_ID != BN254\n#error Only BN254 is supported\n#endif\n  if (impl_ != nullptr) {\n    VLOG(1) << \"IciclePoseidon2 was already initialized\";\n    return true;\n  }\n\n  std::vector<::bn254::scalar_t> round_constants_tmp =\n      base::Map(round_constants, [](const math::bn254::Fr& f) {\n        return ::bn254::scalar_t::from_montgomery(\n            reinterpret_cast<const ::bn254::scalar_t&>(f));\n      });\n  std::vector<::bn254::scalar_t> internal_matrix_diag_tmp =\n      base::Map(internal_matrix_diag, [](const math::bn254::Fr& f) {\n        return ::bn254::scalar_t::from_montgomery(\n            reinterpret_cast<const ::bn254::scalar_t&>(f));\n      });\n  ::poseidon2::Poseidon2<::bn254::scalar_t>* icicle_poseidon = nullptr;\n  gpuError_t error = tachyon_bn254_poseidon2_create_cuda(\n      width, rate, alpha, internal_rounds, external_rounds,\n      round_constants_tmp.data(), internal_matrix_diag_tmp.data(),\n      external_matrix_vendor == Poseidon2Vendor::kHorizen\n          ? ::poseidon2::DEFAULT_MDS\n          : ::poseidon2::PLONKY,\n      internal_matrix_vendor == Poseidon2Vendor::kHorizen\n          ? ::poseidon2::DEFAULT_DIFFUSION\n          : ::poseidon2::MONTGOMERY,\n      config_->ctx, &icicle_poseidon);\n  if (error != gpuSuccess) return false;\n\n  impl_ = static_cast<void*>(icicle_poseidon);\n  return true;\n}\n\ntemplate <>\nbool IciclePoseidon2<math::bn254::Fr>::Load(\n    unsigned int rate, unsigned int width,\n    Poseidon2Vendor external_matrix_vendor,\n    Poseidon2Vendor internal_matrix_vendor) {\n#if FIELD_ID != BN254\n#error Only BN254 is supported\n#endif\n  if (impl_ != nullptr) {\n    VLOG(1) << \"IciclePoseidon2 was already initialized\";\n    return true;\n  }\n\n  ::poseidon2::Poseidon2<::bn254::scalar_t>* icicle_poseidon = nullptr;\n  gpuError_t error = tachyon_bn254_poseidon2_load_cuda(\n      width, rate,\n      external_matrix_vendor == Poseidon2Vendor::kHorizen\n          ? ::poseidon2::DEFAULT_MDS\n          : ::poseidon2::PLONKY,\n      internal_matrix_vendor == Poseidon2Vendor::kHorizen\n          ? ::poseidon2::DEFAULT_DIFFUSION\n          : ::poseidon2::MONTGOMERY,\n      config_->ctx, &icicle_poseidon);\n  if (error != gpuSuccess) return false;\n\n  impl_ = static_cast<void*>(icicle_poseidon);\n  return true;\n}\n\ntemplate <>\nbool IciclePoseidon2<math::bn254::Fr>::Hash(\n    unsigned int rate, absl::Span<const math::bn254::Fr> inputs,\n    absl::Span<math::bn254::Fr> outputs) {\n#if FIELD_ID != BN254\n#error Only BN254 is supported\n#endif\n  if (impl_ == nullptr) {\n    VLOG(1) << \"IciclePoseidon2 is not initialized\";\n    return false;\n  }\n\n  // TODO(chokobole): Change it to allocate just once across many |Hash()|\n  // calls.\n  std::vector<::bn254::scalar_t> inputs_tmp(inputs.size());\n  OMP_PARALLEL_FOR(size_t i = 0; i < inputs_tmp.size(); ++i) {\n    inputs_tmp[i] = ::bn254::scalar_t::from_montgomery(\n        reinterpret_cast<const ::bn254::scalar_t&>(inputs[i]));\n  }\n\n  size_t num_states = inputs.size() / rate;\n  gpuError_t error = tachyon_bn254_poseidon2_hash_many_cuda(\n      reinterpret_cast<::poseidon2::Poseidon2<::bn254::scalar_t>*>(impl_),\n      inputs_tmp.data(), reinterpret_cast<::bn254::scalar_t*>(outputs.data()),\n      num_states, rate, outputs.size() / num_states, *config_);\n  if (error != gpuSuccess) {\n    GPU_LOG(ERROR, error) << \"Failed tachyon_bn254_poseidon2_hash_many_cuda()\";\n    return false;\n  }\n\n  OMP_PARALLEL_FOR(size_t i = 0; i < outputs.size(); ++i) {\n    reinterpret_cast<::bn254::scalar_t&>(outputs[i]) =\n        ::bn254::scalar_t::to_montgomery(\n            reinterpret_cast<const ::bn254::scalar_t&>(outputs[i]));\n  }\n  return true;\n}\n\ntemplate <>\nbool IciclePoseidon2<math::bn254::Fr>::Delete() {\n#if FIELD_ID != BN254\n#error Only BN254 is supported\n#endif\n  if (!impl_) {\n    VLOG(1) << \"IciclePoseidon2 is not initialized\";\n    return false;\n  }\n  gpuError_t error = tachyon_bn254_poseidon2_delete_cuda(\n      reinterpret_cast<::poseidon2::Poseidon2<::bn254::scalar_t>*>(\n          std::exchange(impl_, nullptr)));\n  return error == gpuSuccess;\n}\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon2/icicle/icicle_poseidon2_bn254.h",
    "content": "#ifndef TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_ICICLE_ICICLE_POSEIDON2_BN254_H_\n#define TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_ICICLE_ICICLE_POSEIDON2_BN254_H_\n\n#include \"third_party/icicle/include/curves/params/bn254.cu.h\"\n#include \"third_party/icicle/include/poseidon2/poseidon2.cu.h\"\n\n#include \"tachyon/device/gpu/gpu_device_functions.h\"\n\nextern \"C\" gpuError_t tachyon_bn254_poseidon2_create_cuda(\n    unsigned int width, unsigned int rate, unsigned int alpha,\n    unsigned int internal_rounds, unsigned int external_rounds,\n    const ::bn254::scalar_t* round_constants,\n    const ::bn254::scalar_t* internal_matrix_diag,\n    ::poseidon2::MdsType mds_type, ::poseidon2::DiffusionStrategy diffusion,\n    ::device_context::DeviceContext& ctx,\n    ::poseidon2::Poseidon2<bn254::scalar_t>** poseidon);\n\nextern \"C\" gpuError_t tachyon_bn254_poseidon2_load_cuda(\n    unsigned int width, unsigned int rate, ::poseidon2::MdsType mds_type,\n    ::poseidon2::DiffusionStrategy diffusion,\n    ::device_context::DeviceContext& ctx,\n    ::poseidon2::Poseidon2<bn254::scalar_t>** poseidon);\n\nextern \"C\" gpuError_t tachyon_bn254_poseidon2_hash_many_cuda(\n    const ::poseidon2::Poseidon2<::bn254::scalar_t>* poseidon,\n    const ::bn254::scalar_t* inputs, ::bn254::scalar_t* output,\n    unsigned int number_of_states, unsigned int input_block_len,\n    unsigned int output_len, ::hash::HashConfig& cfg);\n\nextern \"C\" gpuError_t tachyon_bn254_poseidon2_delete_cuda(\n    ::poseidon2::Poseidon2<::bn254::scalar_t>* poseidon);\n\n#endif  // TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_ICICLE_ICICLE_POSEIDON2_BN254_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon2/icicle/icicle_poseidon2_holder.h",
    "content": "#ifndef TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_ICICLE_ICICLE_POSEIDON2_HOLDER_H_\n#define TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_ICICLE_ICICLE_POSEIDON2_HOLDER_H_\n\n#include <limits>\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/crypto/hashes/sponge/poseidon2/icicle/icicle_poseidon2.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_config.h\"\n#include \"tachyon/device/gpu/scoped_mem_pool.h\"\n#include \"tachyon/device/gpu/scoped_stream.h\"\n\nnamespace tachyon::crypto {\n\ntemplate <typename F>\nclass IciclePoseidon2Holder {\n public:\n  IciclePoseidon2Holder() = default;\n\n  const IciclePoseidon2<F>& operator*() const { return *get(); }\n  IciclePoseidon2<F>& operator*() { return *get(); }\n\n  const IciclePoseidon2<F>* operator->() const { return get(); }\n  IciclePoseidon2<F>* operator->() { return get(); }\n\n  operator bool() const { return !!icicle_; }\n\n  const IciclePoseidon2<F>* get() const { return icicle_.get(); }\n  IciclePoseidon2<F>* get() { return icicle_.get(); }\n\n  template <size_t Rate, typename Params>\n  static IciclePoseidon2Holder Create(const Poseidon2Config<Params>& config) {\n    gpuMemPoolProps props = {gpuMemAllocationTypePinned,\n                             gpuMemHandleTypeNone,\n                             {gpuMemLocationTypeDevice, 0}};\n    device::gpu::ScopedMemPool mem_pool = device::gpu::CreateMemPool(&props);\n\n    uint64_t mem_pool_threshold = std::numeric_limits<uint64_t>::max();\n    gpuError_t error = gpuMemPoolSetAttribute(\n        mem_pool.get(), gpuMemPoolAttrReleaseThreshold, &mem_pool_threshold);\n    CHECK_EQ(error, gpuSuccess);\n    device::gpu::ScopedStream stream = device::gpu::CreateStream();\n\n    auto icicle =\n        std::make_unique<IciclePoseidon2<F>>(mem_pool.get(), stream.get());\n\n    if constexpr (Params::kInternalMatrixVendor == Poseidon2Vendor::kPlonky3) {\n      // NOTE(chokobole): It's impossible to determine if |config| was\n      // constructed from |Poseidon2Config::Create()|, which accepts optimized\n      // constants, so |internal_shifts| and |ark| are copied.\n      std::vector<F> internal_diagonal_minus_one(Params::kWidth);\n      internal_diagonal_minus_one[0] = F(F::Config::kModulus - 2);\n      for (size_t i = 1; i < internal_diagonal_minus_one.size(); ++i) {\n        internal_diagonal_minus_one[i] =\n            F(uint32_t{1} << config.internal_shifts[i - 1]);\n      }\n\n      std::vector<F> ark;\n      ark.reserve(Params::kFullRounds * Params::kWidth +\n                  Params::kPartialRounds);\n      Eigen::Index partial_rounds_start = Params::kFullRounds / 2;\n      Eigen::Index partial_rounds_end =\n          Params::kFullRounds / 2 + Params::kPartialRounds;\n      for (Eigen::Index i = 0; i < config.ark.rows(); ++i) {\n        if (i < partial_rounds_start || i >= partial_rounds_end) {\n          for (Eigen::Index j = 0; j < config.ark.cols(); ++j) {\n            ark.push_back(config.ark(i, j));\n          }\n        } else {\n          ark.push_back(config.ark(i, 0));\n        }\n      }\n      if (!icicle->Create(\n              Rate, Params::kWidth, Params::kAlpha, Params::kFullRounds,\n              Params::kPartialRounds, Params::kExternalMatrixVendor,\n              Params::kInternalMatrixVendor, ark, internal_diagonal_minus_one))\n        return {};\n    } else {\n      if (!icicle->Load(Rate, Params::kWidth, Params::kExternalMatrixVendor,\n                        Params::kInternalMatrixVendor))\n        return {};\n    }\n\n    return {std::move(mem_pool), std::move(stream), std::move(icicle)};\n  }\n\n  void Release() {\n    icicle_.reset();\n    stream_.reset();\n    mem_pool_.reset();\n  }\n\n private:\n  IciclePoseidon2Holder(device::gpu::ScopedMemPool mem_pool,\n                        device::gpu::ScopedStream stream,\n                        std::unique_ptr<IciclePoseidon2<F>> icicle)\n      : mem_pool_(std::move(mem_pool)),\n        stream_(std::move(stream)),\n        icicle_(std::move(icicle)) {}\n\n  device::gpu::ScopedMemPool mem_pool_;\n  device::gpu::ScopedStream stream_;\n  std::unique_ptr<IciclePoseidon2<F>> icicle_;\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_ICICLE_ICICLE_POSEIDON2_HOLDER_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon2/param_traits/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"poseidon2_baby_bear\",\n    hdrs = [\"poseidon2_baby_bear.h\"],\n    deps = [\n        \"//tachyon/crypto/hashes/sponge/poseidon2:poseidon2_param_traits_forward\",\n        \"//tachyon/math/finite_fields/baby_bear\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"poseidon2_bls12_381\",\n    hdrs = [\"poseidon2_bls12_381.h\"],\n    deps = [\n        \"//tachyon/crypto/hashes/sponge/poseidon2:poseidon2_param_traits_forward\",\n        \"//tachyon/math/elliptic_curves/bls12/bls12_381:fr\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"poseidon2_bn254\",\n    hdrs = [\"poseidon2_bn254.h\"],\n    deps = [\n        \"//tachyon/crypto/hashes/sponge/poseidon2:poseidon2_param_traits_forward\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:fr\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"poseidon2_goldilocks\",\n    hdrs = [\"poseidon2_goldilocks.h\"],\n    deps = [\n        \"//tachyon/crypto/hashes/sponge/poseidon2:poseidon2_param_traits_forward\",\n        \"//tachyon/math/finite_fields/goldilocks\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"poseidon2_koala_bear\",\n    hdrs = [\"poseidon2_koala_bear.h\"],\n    deps = [\n        \"//tachyon/crypto/hashes/sponge/poseidon2:poseidon2_param_traits_forward\",\n        \"//tachyon/math/finite_fields/koala_bear\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"poseidon2_mersenne31\",\n    hdrs = [\"poseidon2_mersenne31.h\"],\n    deps = [\n        \"//tachyon/crypto/hashes/sponge/poseidon2:poseidon2_param_traits_forward\",\n        \"//tachyon/math/finite_fields/mersenne31\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"poseidon2_pallas\",\n    hdrs = [\"poseidon2_pallas.h\"],\n    deps = [\n        \"//tachyon/crypto/hashes/sponge/poseidon2:poseidon2_param_traits_forward\",\n        \"//tachyon/math/elliptic_curves/pasta/pallas:fr\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"poseidon2_vesta\",\n    hdrs = [\"poseidon2_vesta.h\"],\n    deps = [\n        \"//tachyon/crypto/hashes/sponge/poseidon2:poseidon2_param_traits_forward\",\n        \"//tachyon/math/elliptic_curves/pasta/vesta:fr\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon2/param_traits/poseidon2_baby_bear.h",
    "content": "#ifndef TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_PARAM_TRAITS_POSEIDON2_BABY_BEAR_H_\n#define TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_PARAM_TRAITS_POSEIDON2_BABY_BEAR_H_\n\n#include <array>\n\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_param_traits_forward.h\"\n#include \"tachyon/math/finite_fields/baby_bear/baby_bear.h\"\n\nnamespace tachyon::crypto {\n\n// This is taken and modified from\n// https://github.com/HorizenLabs/poseidon2/blob/bb476b9/plain_implementations/src/poseidon2/poseidon2_instance_babybear.rs.\n// and\n// https://github.com/Plonky3/Plonky3/blob/fde81db/baby-bear/src/poseidon2.rs.\ntemplate <>\nstruct Poseidon2ParamsTraits<math::BabyBear, 15, 7> {\n  constexpr static std::array<math::BabyBear, 16>\n  GetPoseidon2InternalDiagonalArray() {\n    return {\n        math::BabyBear{0x0a632d94}, math::BabyBear{0x6db657b7},\n        math::BabyBear{0x56fbdc9e}, math::BabyBear{0x052b3d8a},\n        math::BabyBear{0x33745201}, math::BabyBear{0x5c03108c},\n        math::BabyBear{0x0beba37b}, math::BabyBear{0x258c2e8b},\n        math::BabyBear{0x12029f39}, math::BabyBear{0x694909ce},\n        math::BabyBear{0x6d231724}, math::BabyBear{0x21c3b222},\n        math::BabyBear{0x3c0904a5}, math::BabyBear{0x01d6acda},\n        math::BabyBear{0x27705c83}, math::BabyBear{0x5231c802},\n    };\n  }\n  constexpr static std::array<uint8_t, 15> GetPoseidon2InternalShiftArray() {\n    return {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15};\n  }\n};\n\ntemplate <>\nstruct Poseidon2ParamsTraits<math::BabyBear, 23, 7> {\n  constexpr static std::array<math::BabyBear, 24>\n  GetPoseidon2InternalDiagonalArray() {\n    return {\n        math::BabyBear{0x0a632d94}, math::BabyBear{0x6db657b7},\n        math::BabyBear{0x56fbdc9e}, math::BabyBear{0x052b3d8a},\n        math::BabyBear{0x33745201}, math::BabyBear{0x5c03108c},\n        math::BabyBear{0x0beba37b}, math::BabyBear{0x258c2e8b},\n        math::BabyBear{0x12029f39}, math::BabyBear{0x694909ce},\n        math::BabyBear{0x6d231724}, math::BabyBear{0x21c3b222},\n        math::BabyBear{0x3c0904a5}, math::BabyBear{0x01d6acda},\n        math::BabyBear{0x27705c83}, math::BabyBear{0x5231c802},\n    };\n  }\n  constexpr static std::array<uint8_t, 23> GetPoseidon2InternalShiftArray() {\n    return {\n        0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11,\n        12, 13, 14, 15, 16, 18, 19, 20, 21, 22, 23,\n    };\n  }\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_PARAM_TRAITS_POSEIDON2_BABY_BEAR_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon2/param_traits/poseidon2_bls12_381.h",
    "content": "#ifndef TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_PARAM_TRAITS_POSEIDON2_BLS12_381_H_\n#define TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_PARAM_TRAITS_POSEIDON2_BLS12_381_H_\n\n#include <array>\n\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_param_traits_forward.h\"\n#include \"tachyon/math/elliptic_curves/bls12/bls12_318/fr.h\"\n\nnamespace tachyon::crypto {\n\n// This is taken and modified from\n// https://github.com/HorizenLabs/poseidon2/blob/bb476b9/plain_implementations/src/poseidon2/poseidon2_instance_bls12.rs.\ntemplate <>\nstruct Poseidon2ParamsTraits<math::bls12_381::Fr, 1, 5> {\n  constexpr static std::array<math::bls12_381::Fr, 2>\n  GetPoseidon2InternalDiagonalArray() {\n    return {\n        math::bls12_381::Fr(1),\n        math::bls12_381::Fr(2),\n    };\n  }\n};\n\ntemplate <>\nstruct Poseidon2ParamsTraits<math::bls12_381::Fr, 2, 5> {\n  constexpr static std::array<math::bls12_381::Fr, 3>\n  GetPoseidon2InternalDiagonalArray() {\n    return {\n        math::bls12_381::Fr(1),\n        math::bls12_381::Fr(1),\n        math::bls12_381::Fr(2),\n    };\n  }\n};\n\ntemplate <>\nstruct Poseidon2ParamsTraits<math::bls12_381::Fr, 3, 5> {\n  constexpr static std::array<math::bls12_381::Fr, 4>\n  GetPoseidon2InternalDiagonalArray() {\n    return {\n        // clang-format off\n        math::bls12_381::Fr::FromHexString(\"0x07564ad691bf01c8601d68757a561d224f00f313ada673ab83e6255fb4fd5b3d\"),\n        math::bls12_381::Fr::FromHexString(\"0x6184e3be38549f7c0850cd069b32f6decbfde312dd4b8c18349b1b3776a6eaa4\"),\n        math::bls12_381::Fr::FromHexString(\"0x419289088178ad742be6f78425c0156b6546a18fd338f0169937dea46cfb64d2\"),\n        math::bls12_381::Fr::FromHexString(\"0x3244cdec173b71a4659e2529b499362dac10cb2fd17562860c8bb9d0fd45b787\"),\n        // clang-format on\n    };\n  }\n};\n\ntemplate <>\nstruct Poseidon2ParamsTraits<math::bls12_381::Fr, 4, 5> {\n  constexpr static std::array<math::bls12_381::Fr, 5>\n  GetPoseidon2InternalDiagonalArray() {\n    return {\n        // clang-format off\n        math::bls12_381::Fr::FromHexString(\"0x1118b610c2544efa26b70d9d60ca6ca362afcfff12436cf3b0f8a3ec5895d9ea\"),\n        math::bls12_381::Fr::FromHexString(\"0x5ba288c5197e71745a8fde16aca575e379dcc19f21042d8b9375e478f809325b\"),\n        math::bls12_381::Fr::FromHexString(\"0x079a987d87d7c80d5f4a3b4018517c50f5067ecb516f6bd14d79eabaa8349e62\"),\n        math::bls12_381::Fr::FromHexString(\"0x4c6497b0b99e1f1af4ec0322dc38869b2dfb79db3ab5fa68936cc8b6025aad1f\"),\n        math::bls12_381::Fr::FromHexString(\"0x483b5c5071e90c98bd353556453f04113442f29a1c4c236b4ca31890136bee4d\"),\n        math::bls12_381::Fr::FromHexString(\"0x3ef76c8bae0aa755dde594d8ec22b157f913323e5b29bbd0652e4b74973ac8f9\"),\n        math::bls12_381::Fr::FromHexString(\"0x091767b280c59a58a39f293bfc22ae944cb921c2efa240262b5b66312724f20b\"),\n        math::bls12_381::Fr::FromHexString(\"0x45ef82a5684137e5fc9613e0581cb65b5ad3d43470eacf0f060e1711c4c57623\"),\n        // clang-format on\n    };\n  }\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_PARAM_TRAITS_POSEIDON2_BLS12_381_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon2/param_traits/poseidon2_bn254.h",
    "content": "#ifndef TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_PARAM_TRAITS_POSEIDON2_BN254_H_\n#define TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_PARAM_TRAITS_POSEIDON2_BN254_H_\n\n#include <array>\n\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_param_traits_forward.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/fr.h\"\n\nnamespace tachyon::crypto {\n\n// This is taken and modified from\n// https://github.com/HorizenLabs/poseidon2/blob/bb476b9/plain_implementations/src/poseidon2/poseidon2_instance_bn256.rs.\ntemplate <>\nstruct Poseidon2ParamsTraits<math::bn254::Fr, 2, 5> {\n  constexpr static std::array<math::bn254::Fr, 3>\n  GetPoseidon2InternalDiagonalArray() {\n    return {\n        math::bn254::Fr(1),\n        math::bn254::Fr(1),\n        math::bn254::Fr(2),\n    };\n  }\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_PARAM_TRAITS_POSEIDON2_BN254_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon2/param_traits/poseidon2_goldilocks.h",
    "content": "#ifndef TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_PARAM_TRAITS_POSEIDON2_GOLDILOCKS_H_\n#define TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_PARAM_TRAITS_POSEIDON2_GOLDILOCKS_H_\n\n#include <array>\n\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_param_traits_forward.h\"\n#include \"tachyon/math/finite_fields/goldilocks/goldilocks.h\"\n\nnamespace tachyon::crypto {\n\n// This is taken and modified from\n// https://github.com/HorizenLabs/poseidon2/blob/bb476b9/plain_implementations/src/poseidon2/poseidon2_instance_goldilocks.rs.\ntemplate <>\nstruct Poseidon2ParamsTraits<math::Goldilocks, 7, 7> {\n  constexpr static std::array<math::Goldilocks, 8>\n  GetPoseidon2InternalDiagonalArray() {\n    return {\n        math::Goldilocks{UINT64_C(0xa98811a1fed4e3a5)},\n        math::Goldilocks{UINT64_C(0x1cc48b54f377e2a0)},\n        math::Goldilocks{UINT64_C(0xe40cd4f6c5609a26)},\n        math::Goldilocks{UINT64_C(0x11de79ebca97a4a3)},\n        math::Goldilocks{UINT64_C(0x9177c73d8b7e929c)},\n        math::Goldilocks{UINT64_C(0x2a6fe8085797e791)},\n        math::Goldilocks{UINT64_C(0x3de6e93329f8d5ad)},\n        math::Goldilocks{UINT64_C(0x3f7af9125da962fe)},\n    };\n  }\n};\n\ntemplate <>\nstruct Poseidon2ParamsTraits<math::Goldilocks, 11, 7> {\n  constexpr static std::array<math::Goldilocks, 12>\n  GetPoseidon2InternalDiagonalArray() {\n    return {\n        math::Goldilocks{UINT64_C(0xc3b6c08e23ba9300)},\n        math::Goldilocks{UINT64_C(0xd84b5de94a324fb6)},\n        math::Goldilocks{UINT64_C(0x0d0c371c5b35b84f)},\n        math::Goldilocks{UINT64_C(0x7964f570e7188037)},\n        math::Goldilocks{UINT64_C(0x5daf18bbd996604b)},\n        math::Goldilocks{UINT64_C(0x6743bc47b9595257)},\n        math::Goldilocks{UINT64_C(0x5528b9362c59bb70)},\n        math::Goldilocks{UINT64_C(0xac45e25b7127b68b)},\n        math::Goldilocks{UINT64_C(0xa2077d7dfbb606b5)},\n        math::Goldilocks{UINT64_C(0xf3faac6faee378ae)},\n        math::Goldilocks{UINT64_C(0x0c6388b51545e883)},\n        math::Goldilocks{UINT64_C(0xd27dbb6944917b60)},\n    };\n  }\n};\n\ntemplate <>\nstruct Poseidon2ParamsTraits<math::Goldilocks, 15, 7> {\n  constexpr static std::array<math::Goldilocks, 16>\n  GetPoseidon2InternalDiagonalArray() {\n    return {\n        math::Goldilocks{UINT64_C(0xde9b91a467d6afc0)},\n        math::Goldilocks{UINT64_C(0xc5f16b9c76a9be17)},\n        math::Goldilocks{UINT64_C(0x0ab0fef2d540ac55)},\n        math::Goldilocks{UINT64_C(0x3001d27009d05773)},\n        math::Goldilocks{UINT64_C(0xed23b1f906d3d9eb)},\n        math::Goldilocks{UINT64_C(0x5ce73743cba97054)},\n        math::Goldilocks{UINT64_C(0x1c3bab944af4ba24)},\n        math::Goldilocks{UINT64_C(0x2faa105854dbafae)},\n        math::Goldilocks{UINT64_C(0x53ffb3ae6d421a10)},\n        math::Goldilocks{UINT64_C(0xbcda9df8884ba396)},\n        math::Goldilocks{UINT64_C(0xfc1273e4a31807bb)},\n        math::Goldilocks{UINT64_C(0xc77952573d5142c0)},\n        math::Goldilocks{UINT64_C(0x56683339a819b85e)},\n        math::Goldilocks{UINT64_C(0x328fcbd8f0ddc8eb)},\n        math::Goldilocks{UINT64_C(0xb5101e303fce9cb7)},\n        math::Goldilocks{UINT64_C(0x774487b8c40089bb)},\n    };\n  }\n};\n\ntemplate <>\nstruct Poseidon2ParamsTraits<math::Goldilocks, 19, 7> {\n  constexpr static std::array<math::Goldilocks, 20>\n  GetPoseidon2InternalDiagonalArray() {\n    return {\n        math::Goldilocks{UINT64_C(0x95c381fda3b1fa57)},\n        math::Goldilocks{UINT64_C(0xf36fe9eb1288f42c)},\n        math::Goldilocks{UINT64_C(0x89f5dcdfef277944)},\n        math::Goldilocks{UINT64_C(0x106f22eadeb3e2d2)},\n        math::Goldilocks{UINT64_C(0x684e31a2530e5111)},\n        math::Goldilocks{UINT64_C(0x27435c5d89fd148e)},\n        math::Goldilocks{UINT64_C(0x3ebed31c414dbf17)},\n        math::Goldilocks{UINT64_C(0xfd45b0b2d294e3cc)},\n        math::Goldilocks{UINT64_C(0x48c904473a7f6dbf)},\n        math::Goldilocks{UINT64_C(0xe0d1b67809295b4d)},\n        math::Goldilocks{UINT64_C(0xddd1941e9d199dcb)},\n        math::Goldilocks{UINT64_C(0x8cfe534eeb742219)},\n        math::Goldilocks{UINT64_C(0xa6e5261d9e3b8524)},\n        math::Goldilocks{UINT64_C(0x6897ee5ed0f82c1b)},\n        math::Goldilocks{UINT64_C(0x0e7dcd0739ee5f78)},\n        math::Goldilocks{UINT64_C(0x493253f3d0d32363)},\n        math::Goldilocks{UINT64_C(0xbb2737f5845f05c0)},\n        math::Goldilocks{UINT64_C(0xa187e810b06ad903)},\n        math::Goldilocks{UINT64_C(0xb635b995936c4918)},\n        math::Goldilocks{UINT64_C(0x0b3694a940bd2394)},\n    };\n  }\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_PARAM_TRAITS_POSEIDON2_GOLDILOCKS_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon2/param_traits/poseidon2_koala_bear.h",
    "content": "#ifndef TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_PARAM_TRAITS_POSEIDON2_KOALA_BEAR_H_\n#define TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_PARAM_TRAITS_POSEIDON2_KOALA_BEAR_H_\n\n#include <array>\n\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_param_traits_forward.h\"\n#include \"tachyon/math/finite_fields/koala_bear/koala_bear.h\"\n\nnamespace tachyon::crypto {\n\n// This is taken and modified from\n// https://github.com/Plonky3/Plonky3/blob/fde81db/koala-bear/src/poseidon2.rs.\ntemplate <>\nstruct Poseidon2ParamsTraits<math::KoalaBear, 15, 7> {\n  constexpr static std::array<uint8_t, 15> GetPoseidon2InternalShiftArray() {\n    return {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15};\n  }\n};\n\ntemplate <>\nstruct Poseidon2ParamsTraits<math::KoalaBear, 23, 7> {\n  constexpr static std::array<uint8_t, 23> GetPoseidon2InternalShiftArray() {\n    return {\n        0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11,\n        12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 23,\n    };\n  }\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_PARAM_TRAITS_POSEIDON2_KOALA_BEAR_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon2/param_traits/poseidon2_mersenne31.h",
    "content": "#ifndef TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_PARAM_TRAITS_POSEIDON2_MERSENNE31_H_\n#define TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_PARAM_TRAITS_POSEIDON2_MERSENNE31_H_\n\n#include <array>\n\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_param_traits_forward.h\"\n#include \"tachyon/math/finite_fields/mersenne31/mersenne31.h\"\n\nnamespace tachyon::crypto {\n\n// This is taken and modified from\n// https://github.com/Plonky3/Plonky3/blob/fde81db/mersenne-31/src/poseidon2.rs.\ntemplate <>\nstruct Poseidon2ParamsTraits<math::Mersenne31, 15, 7> {\n  constexpr static std::array<uint8_t, 15> GetPoseidon2InternalShiftArray() {\n    return {0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 13, 14, 15, 16};\n  }\n};\n\ntemplate <>\nstruct Poseidon2ParamsTraits<math::Mersenne31, 23, 7> {\n  constexpr static std::array<uint8_t, 23> GetPoseidon2InternalShiftArray() {\n    return {\n        0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11,\n        12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,\n    };\n  }\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_PARAM_TRAITS_POSEIDON2_MERSENNE31_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon2/param_traits/poseidon2_pallas.h",
    "content": "#ifndef TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_PARAM_TRAITS_POSEIDON2_PALLAS_H_\n#define TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_PARAM_TRAITS_POSEIDON2_PALLAS_H_\n\n#include <array>\n\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_param_traits_forward.h\"\n#include \"tachyon/math/elliptic_curves/pasta/pallas/fr.h\"\n\nnamespace tachyon::crypto {\n\n// This is taken and modified from\n// https://github.com/HorizenLabs/poseidon2/blob/bb476b9/plain_implementations/src/poseidon2/poseidon2_instance_pallas.rs.\ntemplate <>\nstruct Poseidon2ParamsTraits<math::Pallas::Fr, 2, 5> {\n  constexpr static std::array<math::Pallas::Fr, 3>\n  GetPoseidon2InternalDiagonalArray() {\n    return {\n        math::Pallas::Fr(1),\n        math::Pallas::Fr(1),\n        math::Pallas::Fr(2),\n    };\n  }\n};\n\ntemplate <>\nstruct Poseidon2ParamsTraits<math::Pallas::Fr, 3, 5> {\n  constexpr static std::array<math::Pallas::Fr, 4>\n  GetPoseidon2InternalDiagonalArray() {\n    return {\n        // clang-format off\n        math::Pallas::Fr::FromHexString(\"0x0767b051e5b6358fd12f217aae53bb9dac9a72a9f6a16fdde8f36e715bb27f51\"),\n        math::Pallas::Fr::FromHexString(\"0x2a59f16a37626bdd5536c5546f046b608c777734990103996730611728cfef21\"),\n        math::Pallas::Fr::FromHexString(\"0x2388405f3a1e87a1fd3183bb12a89c71b37555b4db6a4306e1f05322217ee15c\"),\n        math::Pallas::Fr::FromHexString(\"0x0e7c7e19ad92352c35e4d302828f64de68750dac64cbd944f0eba6c0ed003757\"),\n        // clang-format on\n    };\n  }\n};\n\ntemplate <>\nstruct Poseidon2ParamsTraits<math::Pallas::Fr, 7, 5> {\n  constexpr static std::array<math::Pallas::Fr, 8>\n  GetPoseidon2InternalDiagonalArray() {\n    return {\n        // clang-format off\n        math::Pallas::Fr::FromHexString(\"0x2527e8a83e49ae6bf3c8e459d5220e34d84aa49ce14f2dc401273cebdec65067\"),\n        math::Pallas::Fr::FromHexString(\"0x0e4a24b206b7494d2437d3e0fd1deeae8a943ccd836e0f959aaeccebb3068859\"),\n        math::Pallas::Fr::FromHexString(\"0x03c9638e9b8ad067e7033ed3aef5e185fcfa3959f82283bbf00c1bf0ea40fe45\"),\n        math::Pallas::Fr::FromHexString(\"0x09f7633edc22a16de93a8676260b507aa44aa8c57565bc5d21543897be56c100\"),\n        math::Pallas::Fr::FromHexString(\"0x06dfc4a91b7acb8ef203a8bc6b850290b1e272a594512ac0d1c9d3a56c8a7921\"),\n        math::Pallas::Fr::FromHexString(\"0x020e0af80c2a8e2aab6dcf5d8e94e71d24156e3123a16fdf8fd80471776e3551\"),\n        math::Pallas::Fr::FromHexString(\"0x24dfd0278f203a55322e94b290ae4269bbe76aa5531921f5f87a8d2d736dbb9c\"),\n        math::Pallas::Fr::FromHexString(\"0x08aa91c42dea2206ff4e601a1f49c009d18acc891ccb78529856db2a49664b2d\"),\n        // clang-format on\n    };\n  }\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_PARAM_TRAITS_POSEIDON2_PALLAS_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon2/param_traits/poseidon2_vesta.h",
    "content": "#ifndef TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_PARAM_TRAITS_POSEIDON2_VESTA_H_\n#define TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_PARAM_TRAITS_POSEIDON2_VESTA_H_\n\n#include <array>\n\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_param_traits_forward.h\"\n#include \"tachyon/math/elliptic_curves/pasta/vesta/fr.h\"\n\nnamespace tachyon::crypto {\n\n// This is taken and modified from\n// https://github.com/HorizenLabs/poseidon2/blob/bb476b9/plain_implementations/src/poseidon2/poseidon2_instance_vesta.rs.\ntemplate <>\nstruct Poseidon2ParamsTraits<math::vesta::Fr, 2, 5> {\n  constexpr static std::array<math::vesta::Fr, 3>\n  GetPoseidon2InternalDiagonalArray() {\n    return {\n        math::vesta::Fr(1),\n        math::vesta::Fr(1),\n        math::vesta::Fr(2),\n    };\n  }\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_PARAM_TRAITS_POSEIDON2_VESTA_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon2/poseidon2.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_POSEIDON2_H_\n#define TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_POSEIDON2_H_\n\n#include <utility>\n\n#include \"tachyon/base/buffer/copyable.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon/poseidon_sponge_base.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_config.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_horizen_external_matrix.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_horizen_internal_matrix.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_plonky3_external_matrix.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_plonky3_internal_matrix.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_vendor.h\"\n#include \"tachyon/crypto/hashes/sponge/sponge_state.h\"\n#include \"tachyon/math/finite_fields/finite_field_traits.h\"\n\nnamespace tachyon {\nnamespace crypto {\n\n// Poseidon2 Sponge Hash: Absorb → Permute → Squeeze\n// Absorb: Absorb elements into the sponge.\n// Permute: Transform the |state| using a series of operations.\n//   1. Apply ARK (addition of round constants) to |state|.\n//   2. Apply S-Box (xᵅ) to |state|.\n//   3. Apply external and internal matrices to |state|.\n// Squeeze: Squeeze elements out of the sponge.\ntemplate <typename _Params>\nstruct Poseidon2Sponge final\n    : public PoseidonSpongeBase<Poseidon2Sponge<_Params>> {\n  using Params = _Params;\n  using F = typename Params::Field;\n\n  // Sponge Config\n  Poseidon2Config<Params> config;\n\n  Poseidon2Sponge() : config(Poseidon2Config<Params>::CreateDefault()) {}\n  explicit Poseidon2Sponge(const Poseidon2Config<Params>& config)\n      : config(config) {}\n  explicit Poseidon2Sponge(Poseidon2Config<Params>&& config)\n      : config(std::move(config)) {}\n\n  // PoseidonSpongeBase methods\n  void Permute(SpongeState<Params>& state) const {\n    ApplyMixFull(state);\n\n    size_t full_rounds_over_2 = Params::kFullRounds / 2;\n    for (size_t i = 0; i < full_rounds_over_2; ++i) {\n      this->ApplyARKFull(state, i);\n      this->ApplySBoxFull(state);\n      ApplyMixFull(state);\n    }\n    for (size_t i = full_rounds_over_2;\n         i < full_rounds_over_2 + Params::kPartialRounds; ++i) {\n      this->ApplyARKPartial(state, i);\n      this->ApplySBoxPartial(state);\n      ApplyMixPartial(state);\n    }\n    for (size_t i = full_rounds_over_2 + Params::kPartialRounds;\n         i < Params::kPartialRounds + Params::kFullRounds; ++i) {\n      this->ApplyARKFull(state, i);\n      this->ApplySBoxFull(state);\n      ApplyMixFull(state);\n    }\n  }\n\n  bool operator==(const Poseidon2Sponge& other) const {\n    return config == other.config;\n  }\n  bool operator!=(const Poseidon2Sponge& other) const {\n    return !operator==(other);\n  }\n\n private:\n  void ApplyMixFull(SpongeState<Params>& state) const {\n    if constexpr (Params::kExternalMatrixVendor == Poseidon2Vendor::kHorizen) {\n      Poseidon2ExternalMatrix<Poseidon2HorizenExternalMatrix<F>>::\n          template Apply<Params::kWidth>(state.elements);\n    } else {\n      Poseidon2ExternalMatrix<Poseidon2Plonky3ExternalMatrix<F>>::\n          template Apply<Params::kWidth>(state.elements);\n    }\n  }\n\n  void ApplyMixPartial(SpongeState<Params>& state) const {\n    using PrimeField = math::MaybeUnpack<F>;\n\n    if constexpr (PrimeField::Config::kModulusBits <= 32 &&\n                  Params::kInternalMatrixVendor == Poseidon2Vendor::kPlonky3) {\n      if constexpr (math::FiniteFieldTraits<F>::kIsPackedPrimeField) {\n        Poseidon2Plonky3InternalMatrix<F>::Apply(\n            state.elements, config.internal_diagonal_minus_one);\n      } else {\n        Poseidon2Plonky3InternalMatrix<F>::Apply(state.elements,\n                                                 config.internal_shifts);\n      }\n      return;\n    }\n    Poseidon2HorizenInternalMatrix<F>::Apply(\n        state.elements, config.internal_diagonal_minus_one);\n  }\n};\n\ntemplate <typename _Params>\nstruct CryptographicSpongeTraits<Poseidon2Sponge<_Params>> {\n  using Params = _Params;\n  using F = typename Params::Field;\n};\n\n}  // namespace crypto\n\nnamespace base {\ntemplate <typename Params>\nclass Copyable<crypto::Poseidon2Sponge<Params>> {\n public:\n  using F = typename Params::Field;\n\n  static bool WriteTo(const crypto::Poseidon2Sponge<Params>& poseidon,\n                      Buffer* buffer) {\n    return buffer->WriteMany(poseidon.config);\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer,\n                       crypto::Poseidon2Sponge<Params>* poseidon) {\n    crypto::Poseidon2Config<Params> config;\n    if (!buffer.ReadMany(&config)) {\n      return false;\n    }\n\n    *poseidon = crypto::Poseidon2Sponge<Params>(std::move(config));\n    return true;\n  }\n\n  static size_t EstimateSize(const crypto::Poseidon2Sponge<Params>& poseidon) {\n    return base::EstimateSize(poseidon.config);\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_POSEIDON2_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon2/poseidon2_config.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_POSEIDON2_CONFIG_H_\n#define TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_POSEIDON2_CONFIG_H_\n\n#include <array>\n#include <utility>\n\n#include \"absl/types/span.h\"\n\n#include \"tachyon/base/buffer/copyable.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/ranges/algorithm.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon/poseidon_config_base.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_config_entry.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_params.h\"\n#include \"tachyon/math/finite_fields/finite_field_traits.h\"\n#include \"tachyon/math/matrix/matrix_utils.h\"\n\nnamespace tachyon {\nnamespace crypto {\n\n// ARK(AddRoundKey) is a matrix that contains an ARC(AddRoundConstant) array in\n// each row. Each constant is added to the |state| of each round of Poseidon.\n// TODO(chokobole): add comment\ntemplate <typename F>\nvoid FindPoseidon2ARK(const PoseidonGrainLFSRConfig& config,\n                      math::Matrix<F>& ark) {\n  PoseidonGrainLFSR<F> lfsr(config);\n  ark = math::Matrix<F>(config.num_full_rounds + config.num_partial_rounds,\n                        config.state_len);\n  size_t partial_rounds_start = config.num_full_rounds / 2;\n  size_t partial_rounds_end =\n      config.num_full_rounds / 2 + config.num_partial_rounds;\n  for (size_t i = 0; i < config.num_full_rounds + config.num_partial_rounds;\n       ++i) {\n    if (i < partial_rounds_start || i >= partial_rounds_end) {\n      ark.row(i) = lfsr.GetFieldElementsRejectionSampling(config.state_len);\n    } else {\n      ark.row(i)[0] = lfsr.GetFieldElementsRejectionSampling(1)[0];\n    }\n  }\n  // TODO(chokobole): Enable generating |internal_diagonal_mins_one|.\n}\n\ntemplate <typename Params>\nstruct Poseidon2Config : public PoseidonConfigBase<Params> {\n  using F = typename Params::Field;\n  using PrimeField = math::MaybeUnpack<F>;\n\n  math::Vector<F> internal_diagonal_minus_one;\n  math::Vector<uint8_t> internal_shifts;\n\n  Poseidon2Config() = default;\n  Poseidon2Config(const PoseidonConfigBase<Params>& base,\n                  const math::Vector<F>& internal_diagonal_minus_one)\n      : PoseidonConfigBase<Params>(base),\n        internal_diagonal_minus_one(internal_diagonal_minus_one) {}\n  Poseidon2Config(PoseidonConfigBase<Params>&& base,\n                  math::Vector<F>&& internal_diagonal_minus_one)\n      : PoseidonConfigBase<Params>(std::move(base)),\n        internal_diagonal_minus_one(std::move(internal_diagonal_minus_one)) {}\n\n  constexpr static Poseidon2Config CreateDefault() {\n    // TODO(chokobole): Only |BabyBear| has both shift and diagonal arrays.\n    // We assume the Plonky3 team developed the concept of the shift array,\n    // and will use it in most cases, so we set it as the default.\n    // Other small prime fields have only a shift array, while larger prime\n    // fields have only a diagonal array. For more details, refer to\n    // tachyon/crypto/hashes/sponge/poseidon2/param_traits/poseidon2_xxx.h.\n    if constexpr (Params::kInternalMatrixVendor == Poseidon2Vendor::kPlonky3 &&\n                  PrimeField::Config::kModulusBits <= 32) {\n      return Create(GetPoseidon2InternalShiftArray<Params>());\n    } else {\n      return Create(GetPoseidon2InternalDiagonalArray<Params>());\n    }\n  }\n\n  constexpr static Poseidon2Config CreateDefault(math::Matrix<F>&& ark) {\n    static_assert(Params::kInternalMatrixVendor == Poseidon2Vendor::kPlonky3);\n    return Create(GetPoseidon2InternalShiftArray<Params>(), std::move(ark));\n  }\n\n  constexpr static Poseidon2Config Create(\n      const std::array<PrimeField, Params::kWidth>&\n          internal_diagonal_minus_one) {\n    constexpr Poseidon2ConfigEntry config_entry(Params::kRate, Params::kAlpha,\n                                                Params::kFullRounds,\n                                                Params::kPartialRounds);\n    Poseidon2Config ret = config_entry.ToPoseidon2Config<Params>();\n    ret.internal_diagonal_minus_one = math::Vector<F>(Params::kWidth);\n    for (size_t i = 0; i < Params::kWidth; ++i) {\n      if constexpr (math::FiniteFieldTraits<F>::kIsPackedPrimeField) {\n        ret.internal_diagonal_minus_one[i] =\n            F::Broadcast(internal_diagonal_minus_one[i]);\n      } else {\n        ret.internal_diagonal_minus_one[i] = internal_diagonal_minus_one[i];\n      }\n    }\n    FindPoseidon2ARK(config_entry.ToPoseidonGrainLFSRConfig<F>(), ret.ark);\n    return ret;\n  }\n\n  constexpr static Poseidon2Config Create(\n      const std::array<uint8_t, Params::kRate>& internal_shifts) {\n    Poseidon2ConfigEntry config_entry(Params::kRate, Params::kAlpha,\n                                      Params::kFullRounds,\n                                      Params::kPartialRounds);\n    math::Matrix<F> ark;\n    FindPoseidon2ARK(config_entry.ToPoseidonGrainLFSRConfig<F>(), ark);\n    return Create(config_entry, internal_shifts, std::move(ark));\n  }\n\n  // NOTE(chokobole): If another variant method that accepts ark is added,\n  // remember to update the code in icicle_poseidon2_holder.h as well.\n  constexpr static Poseidon2Config Create(\n      const std::array<uint8_t, Params::kRate>& internal_shifts,\n      math::Matrix<F>&& ark) {\n    Poseidon2ConfigEntry config_entry(Params::kRate, Params::kAlpha,\n                                      Params::kFullRounds,\n                                      Params::kPartialRounds);\n    return Create(config_entry, internal_shifts, std::move(ark));\n  }\n\n private:\n  constexpr static Poseidon2Config Create(\n      const Poseidon2ConfigEntry& config_entry,\n      const std::array<uint8_t, Params::kRate>& internal_shifts,\n      math::Matrix<F>&& ark) {\n    Poseidon2Config ret = config_entry.ToPoseidon2Config<Params>();\n    if constexpr (math::FiniteFieldTraits<F>::kIsPackedPrimeField) {\n      ret.internal_diagonal_minus_one = math::Vector<F>(Params::kWidth);\n      ret.internal_diagonal_minus_one[0] = F(PrimeField::Config::kModulus - 2);\n      for (size_t i = 1; i < Params::kWidth; ++i) {\n        ret.internal_diagonal_minus_one[i] =\n            F(uint32_t{1} << internal_shifts[i - 1]);\n      }\n    } else {\n      ret.internal_shifts = math::Vector<uint8_t>(Params::kRate);\n      for (size_t i = 0; i < Params::kRate; ++i) {\n        ret.internal_shifts[i] = internal_shifts[i];\n      }\n    }\n    ret.ark = std::move(ark);\n    return ret;\n  }\n};\n\ntemplate <typename Params>\nPoseidon2Config<Params> Poseidon2ConfigEntry::ToPoseidon2Config() const {\n  return Poseidon2Config<Params>();\n}\n\n}  // namespace crypto\n\nnamespace base {\n\ntemplate <typename Params>\nclass Copyable<crypto::Poseidon2Config<Params>> {\n public:\n  using F = typename Params::Field;\n  static bool WriteTo(const crypto::Poseidon2Config<Params>& config,\n                      Buffer* buffer) {\n    return Copyable<crypto::PoseidonConfigBase<Params>>::WriteTo(config,\n                                                                 buffer) &&\n           buffer->WriteMany(config.internal_diagonal_minus_one);\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer,\n                       crypto::Poseidon2Config<Params>* config) {\n    crypto::PoseidonConfigBase<Params> base;\n    math::Matrix<F> internal_diagonal_minus_one;\n    if (!buffer.ReadMany(&base, &internal_diagonal_minus_one)) {\n      return false;\n    }\n\n    *config = {std::move(base), std::move(internal_diagonal_minus_one)};\n    return true;\n  }\n\n  static size_t EstimateSize(const crypto::Poseidon2Config<Params>& config) {\n    const crypto::PoseidonConfigBase<Params>& base =\n        static_cast<const crypto::PoseidonConfigBase<Params>&>(config);\n    return base::EstimateSize(base, config.internal_diagonal_minus_one);\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_POSEIDON2_CONFIG_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon2/poseidon2_config_entry.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_POSEIDON2_CONFIG_ENTRY_H_\n#define TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_POSEIDON2_CONFIG_ENTRY_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include \"tachyon/crypto/hashes/sponge/poseidon/poseidon_config_entry_base.h\"\n\nnamespace tachyon::crypto {\n\ntemplate <typename Params>\nstruct Poseidon2Config;\n\n// An entry in the Poseidon config\nstruct TACHYON_EXPORT Poseidon2ConfigEntry : public PoseidonConfigEntryBase {\n  using PoseidonConfigEntryBase::PoseidonConfigEntryBase;\n\n  template <typename Params>\n  Poseidon2Config<Params> ToPoseidon2Config() const;\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_POSEIDON2_CONFIG_ENTRY_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon2/poseidon2_external_matrix.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_POSEIDON2_EXTERNAL_MATRIX_H_\n#define TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_POSEIDON2_EXTERNAL_MATRIX_H_\n\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_external_matrix_traits_forward.h\"\n#include \"tachyon/math/matrix/matrix_types.h\"\n#include \"tachyon/math/matrix/matrix_utils.h\"\n\nnamespace tachyon::crypto {\n\ntemplate <typename Derived>\nclass Poseidon2ExternalMatrix {\n public:\n  using Field = typename Poseidon2ExternalMatrixTraits<Derived>::Field;\n\n  template <size_t kWidth>\n  static void Apply(math::Vector<Field>& v) {\n    if constexpr (kWidth <= 1 || kWidth > 24) {\n      NOTREACHED() << \"Out of range\";\n    } else if constexpr (kWidth == 2) {\n      Field sum = v[0] + v[1];\n      v[0] += sum;\n      v[1] += sum;\n    } else if constexpr (kWidth == 3) {\n      Field sum = v[0] + v[1] + v[2];\n      v[0] += sum;\n      v[1] += sum;\n      v[2] += sum;\n    } else if constexpr (kWidth == 4) {\n      Derived::DoApply(v);\n    } else if constexpr (kWidth % 4 == 0) {\n      for (size_t i = 0; i < kWidth; i += 4) {\n        Eigen::Block<math::Vector<Field>> block = v.block(i, 0, 4, 1);\n        Derived::DoApply(block);\n      }\n\n      std::array<Field, 4> v_tmp = {Field::Zero(), Field::Zero(), Field::Zero(),\n                                    Field::Zero()};\n      for (size_t i = 0; i < 4; ++i) {\n        for (size_t j = 0; j < kWidth; j += 4) {\n          v_tmp[i] += v[i + j];\n        }\n      }\n\n      for (size_t i = 0; i < kWidth; ++i) {\n        v[i] += v_tmp[i % 4];\n      }\n    } else {\n      NOTREACHED() << \"Not a multiple of 4\";\n    }\n  }\n\n  template <size_t kWidth>\n  static math::Matrix<Field> Construct(size_t size) {\n    if constexpr (kWidth <= 1 || kWidth > 24) {\n      NOTREACHED() << \"Out of range\";\n    } else if constexpr (kWidth == 2) {\n      return math::MakeCirculant(math::Vector<Field>{{Field(2), Field(1)}});\n    } else if constexpr (kWidth == 3) {\n      return math::MakeCirculant(\n          math::Vector<Field>{{Field(2), Field(1), Field(1)}});\n    } else if constexpr (kWidth == 4) {\n      return Derived::DoConstruct();\n    } else if constexpr (kWidth % 4 == 0) {\n      math::Matrix<Field> small_matrix = Derived::DoConstruct();\n\n      math::Matrix<Field> ret(kWidth, kWidth);\n      for (size_t i = 0; i < kWidth / 4; ++i) {\n        for (size_t j = 0; j < kWidth / 4; ++j) {\n          ret.block(i * 4, j * 4, 4, 4) =\n              i == j ? Field(2) * small_matrix : small_matrix;\n        }\n      }\n      return ret;\n    } else {\n      NOTREACHED() << \"Not a multiple of 4\";\n    }\n  }\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_POSEIDON2_EXTERNAL_MATRIX_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon2/poseidon2_external_matrix_traits_forward.h",
    "content": "#ifndef TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_POSEIDON2_EXTERNAL_MATRIX_TRAITS_FORWARD_H_\n#define TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_POSEIDON2_EXTERNAL_MATRIX_TRAITS_FORWARD_H_\n\nnamespace tachyon::crypto {\n\ntemplate <typename T>\nstruct Poseidon2ExternalMatrixTraits;\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_POSEIDON2_EXTERNAL_MATRIX_TRAITS_FORWARD_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon2/poseidon2_external_matrix_unittest.cc",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_horizen_external_matrix.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_plonky3_external_matrix.h\"\n#include \"tachyon/math/finite_fields/mersenne31/mersenne31.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n\nnamespace tachyon::crypto {\n\nnamespace {\n\ntemplate <typename Matrix>\nclass Poseidon2ExternalMatrixTest\n    : public math::FiniteFieldTest<math::PackedMersenne31> {};\n\n}  // namespace\n\nusing MatrixTypes =\n    testing::Types<Poseidon2HorizenExternalMatrix<math::Mersenne31>,\n                   Poseidon2Plonky3ExternalMatrix<math::Mersenne31>,\n                   Poseidon2HorizenExternalMatrix<math::PackedMersenne31>,\n                   Poseidon2Plonky3ExternalMatrix<math::PackedMersenne31>>;\nTYPED_TEST_SUITE(Poseidon2ExternalMatrixTest, MatrixTypes);\n\nTYPED_TEST(Poseidon2ExternalMatrixTest, DoApply) {\n  using Matrix = TypeParam;\n  using F = typename Matrix::Field;\n\n  math::Vector<F> vector{{{F(1)}, {F(1)}, {F(1)}, {F(1)}}};\n  math::Vector<F> vector2 = vector;\n  Matrix::DoApply(vector2);\n  EXPECT_EQ(Matrix::DoConstruct() * vector, vector2);\n}\n\nTYPED_TEST(Poseidon2ExternalMatrixTest, Apply) {\n  using Matrix = TypeParam;\n  using F = typename Matrix::Field;\n\n  size_t sizes[] = {2, 3, 4, 8, 12, 16, 20, 24};\n\n  for (size_t size : sizes) {\n    math::Vector<F> vector(size);\n    for (size_t i = 0; i < size; ++i) {\n      vector(i, 0) = F::Random();\n    }\n    math::Vector<F> vector2 = vector;\n    switch (size) {\n      case 2:\n        Poseidon2ExternalMatrix<Matrix>::template Apply<2>(vector2);\n        EXPECT_EQ(Matrix::template Construct<2>(size) * vector, vector2);\n        break;\n      case 3:\n        Poseidon2ExternalMatrix<Matrix>::template Apply<3>(vector2);\n        EXPECT_EQ(Matrix::template Construct<3>(size) * vector, vector2);\n        break;\n      case 4:\n        Poseidon2ExternalMatrix<Matrix>::template Apply<4>(vector2);\n        EXPECT_EQ(Matrix::template Construct<4>(size) * vector, vector2);\n        break;\n      case 8:\n        Poseidon2ExternalMatrix<Matrix>::template Apply<8>(vector2);\n        EXPECT_EQ(Matrix::template Construct<8>(size) * vector, vector2);\n        break;\n      case 12:\n        Poseidon2ExternalMatrix<Matrix>::template Apply<12>(vector2);\n        EXPECT_EQ(Matrix::template Construct<12>(size) * vector, vector2);\n        break;\n      case 16:\n        Poseidon2ExternalMatrix<Matrix>::template Apply<16>(vector2);\n        EXPECT_EQ(Matrix::template Construct<16>(size) * vector, vector2);\n        break;\n      case 20:\n        Poseidon2ExternalMatrix<Matrix>::template Apply<20>(vector2);\n        EXPECT_EQ(Matrix::template Construct<20>(size) * vector, vector2);\n        break;\n      case 24:\n        Poseidon2ExternalMatrix<Matrix>::template Apply<24>(vector2);\n        EXPECT_EQ(Matrix::template Construct<24>(size) * vector, vector2);\n        break;\n      default:\n        FAIL();\n    }\n  }\n\n  size_t invalid_sizes[] = {0, 1, 5, 28};\n  for (size_t size : invalid_sizes) {\n    math::Vector<F> vector(size);\n    switch (size) {\n      case 0:\n        EXPECT_DEATH(Poseidon2ExternalMatrix<Matrix>::template Apply<0>(vector),\n                     \"\");\n        EXPECT_DEATH(Matrix::template Construct<0>(size), \"\");\n        break;\n      case 1:\n        EXPECT_DEATH(Poseidon2ExternalMatrix<Matrix>::template Apply<1>(vector),\n                     \"\");\n        EXPECT_DEATH(Matrix::template Construct<1>(size), \"\");\n        break;\n      case 5:\n        EXPECT_DEATH(Poseidon2ExternalMatrix<Matrix>::template Apply<5>(vector),\n                     \"\");\n        EXPECT_DEATH(Matrix::template Construct<5>(size), \"\");\n        break;\n      case 28:\n        EXPECT_DEATH(\n            Poseidon2ExternalMatrix<Matrix>::template Apply<28>(vector), \"\");\n        EXPECT_DEATH(Matrix::template Construct<28>(size), \"\");\n        break;\n      default:\n        FAIL();\n    }\n  }\n}\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon2/poseidon2_gpu_unittest.cc",
    "content": "#include <vector>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/crypto/hashes/sponge/poseidon2/icicle/icicle_poseidon2_holder.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/param_traits/poseidon2_baby_bear.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/param_traits/poseidon2_bn254.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_params.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n\nnamespace tachyon::crypto {\n\nnamespace {\n\ntemplate <typename Params>\nclass Poseidon2GpuTest : public math::FiniteFieldTest<typename Params::Field> {\n};\n\n}  // namespace\n\nusing ParamsTypes = testing::Types<\n    Poseidon2Params<Poseidon2Vendor::kPlonky3, Poseidon2Vendor::kPlonky3,\n                    math::BabyBear, 15, 7>,\n    Poseidon2Params<Poseidon2Vendor::kHorizen, Poseidon2Vendor::kHorizen,\n                    math::bn254::Fr, 2, 5>>;\nTYPED_TEST_SUITE(Poseidon2GpuTest, ParamsTypes);\n\nTYPED_TEST(Poseidon2GpuTest, Poseidon2Correctness) {\n  using Params = TypeParam;\n  using F = typename Params::Field;\n\n  constexpr size_t kLen = 16;\n  constexpr size_t kRate = std::is_same_v<F, math::BabyBear> ? 8 : 2;\n  constexpr size_t kOutput = std::is_same_v<F, math::BabyBear> ? 6 : 1;\n\n  Poseidon2Config<Params> config = Poseidon2Config<Params>::CreateDefault();\n  IciclePoseidon2Holder<F> holder =\n      IciclePoseidon2Holder<F>::template Create<Params::kWidth - 1>(config);\n\n  std::vector<F> inputs =\n      base::CreateVector(kLen * kRate, []() { return F::Random(); });\n  std::vector<F> expected;\n  expected.reserve(kLen * kOutput);\n\n  Poseidon2Sponge<Params> poseidon2_cpu(config);\n  for (size_t i = 0; i < kLen; ++i) {\n    SpongeState<Params> state;\n    for (size_t j = 0; j < kRate; ++j) {\n      state[j] = inputs[i * kRate + j];\n    }\n    poseidon2_cpu.Permute(state);\n    for (size_t j = 0; j < kOutput; ++j) {\n      expected.push_back(state.elements[j]);\n    }\n  }\n\n  std::vector<F> outputs(kLen * kOutput);\n  ASSERT_TRUE((*holder).Hash(kRate, inputs, absl::MakeSpan(outputs)));\n  EXPECT_EQ(expected, outputs);\n}\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon2/poseidon2_horizen_external_matrix.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_POSEIDON2_HORIZEN_EXTERNAL_MATRIX_H_\n#define TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_POSEIDON2_HORIZEN_EXTERNAL_MATRIX_H_\n\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_external_matrix.h\"\n\nnamespace tachyon::crypto {\n\ntemplate <typename F>\nclass Poseidon2HorizenExternalMatrix final\n    : public Poseidon2ExternalMatrix<Poseidon2HorizenExternalMatrix<F>> {\n public:\n  template <typename Derived>\n  static void DoApply(Eigen::MatrixBase<Derived>& v) {\n    F t0 = v(0, 0) + v(1, 0);\n    F t1 = v(2, 0) + v(3, 0);\n    F t2 = v(1, 0) + v(1, 0) + t1;\n    F t3 = v(3, 0) + v(3, 0) + t0;\n    v(3, 0) = t1.Double().Double() + t3;\n    v(1, 0) = t0.Double().Double() + t2;\n    v(0, 0) = t3 + v(1, 0);\n    v(2, 0) = t2 + v(3, 0);\n  }\n\n  static math::Matrix<F> DoConstruct() {\n    return math::Matrix<F>{\n        {F(5), F(7), F(1), F(3)},\n        {F(4), F(6), F(1), F(1)},\n        {F(1), F(3), F(5), F(7)},\n        {F(1), F(1), F(4), F(6)},\n    };\n  }\n};\n\ntemplate <typename Field_>\nstruct Poseidon2ExternalMatrixTraits<Poseidon2HorizenExternalMatrix<Field_>> {\n  using Field = Field_;\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_POSEIDON2_HORIZEN_EXTERNAL_MATRIX_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon2/poseidon2_horizen_internal_matrix.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_POSEIDON2_HORIZEN_INTERNAL_MATRIX_H_\n#define TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_POSEIDON2_HORIZEN_INTERNAL_MATRIX_H_\n\n#include <numeric>\n\n#include \"tachyon/math/matrix/matrix_types.h\"\n\nnamespace tachyon::crypto {\n\ntemplate <typename F>\nclass Poseidon2HorizenInternalMatrix {\n public:\n  static void Apply(math::Vector<F>& v,\n                    const math::Vector<F>& diagonal_minus_one) {\n    // +-----+-----+-----+-----+   +-----+-----+-----+-----+\n    // |  v₀ |  v₁ | ... | vₙ₋₁| * |  μ₀ |  1  | ... |  1  |\n    // +-----+-----+-----+-----+   +-----+-----+-----+-----+\n    //                             |  1  |  μ₁ | ... |  1  |\n    //                             +-----+-----+-----+-----+\n    //                             | ... | ... | ... | ... |\n    //                             +-----+-----+-----+-----+\n    //                             |  1  |  1  | ... | μₙ₋₁|\n    //                             +-----+-----+-----+-----+\n    // |v[i]| = v₀ + v₁ + ... + μᵢvᵢ + ... + vₙ₋₂ + vₙ₋₁\n    //        = (μᵢ - 1)vᵢ + v₀ + v₁ + ... + vₙ₋₂ + vₙ₋₁\n    //        = (μᵢ - 1)vᵢ + |sum|\n    F sum = std::accumulate(v.begin(), v.end(), F::Zero(),\n                            [](F acc, F value) { return acc += value; });\n    for (Eigen::Index i = 0; i < v.size(); ++i) {\n      v[i] *= diagonal_minus_one[i];\n      v[i] += sum;\n    }\n  }\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_POSEIDON2_HORIZEN_INTERNAL_MATRIX_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon2/poseidon2_internal_matrix_unittest.cc",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_horizen_internal_matrix.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_plonky3_internal_matrix.h\"\n#include \"tachyon/math/finite_fields/mersenne31/mersenne31.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n\nnamespace tachyon::crypto {\n\nnamespace {\n\ntemplate <typename F>\nclass Poseidon2InternalMatrixTest : public math::FiniteFieldTest<F> {};\n\n}  // namespace\n\nusing FieldTypes = testing::Types<math::Mersenne31, math::PackedMersenne31>;\nTYPED_TEST_SUITE(Poseidon2InternalMatrixTest, FieldTypes);\n\nTYPED_TEST(Poseidon2InternalMatrixTest, ApplyHorizen) {\n  using F = TypeParam;\n\n  math::Vector<F> diagonal_minus_one_vec =\n      math::Vector<F>::Random(3) - math::Vector<F>::Constant(3, F::One());\n\n  math::Matrix<F> matrix{\n      {diagonal_minus_one_vec[0] + F::One(), F::One(), F::One()},\n      {F::One(), diagonal_minus_one_vec[1] + F::One(), F::One()},\n      {F::One(), F::One(), diagonal_minus_one_vec[2] + F::One()},\n  };\n\n  math::Vector<F> state = math::Vector<F>::Random(3);\n  math::Vector<F> state2 = state;\n  Poseidon2HorizenInternalMatrix<F>::Apply(state2, diagonal_minus_one_vec);\n  EXPECT_EQ(matrix * state, state2);\n}\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon2/poseidon2_param_traits_forward.h",
    "content": "#ifndef TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_POSEIDON2_PARAM_TRAITS_FORWARD_H_\n#define TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_POSEIDON2_PARAM_TRAITS_FORWARD_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\nnamespace tachyon::crypto {\n\ntemplate <typename F, size_t Rate, uint32_t Alpha>\nstruct Poseidon2ParamsTraits;\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_POSEIDON2_PARAM_TRAITS_FORWARD_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon2/poseidon2_params.h",
    "content": "#ifndef TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_POSEIDON2_PARAMS_H_\n#define TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_POSEIDON2_PARAMS_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <type_traits>\n\n#include \"tachyon/base/types/always_false.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_vendor.h\"\n#include \"tachyon/math/finite_fields/finite_field_traits.h\"\n\nnamespace tachyon::crypto {\n\ntemplate <typename F, size_t Width, uint64_t Alpha>\nconstexpr size_t GetPoseidon2PartialRounds() {\n  using PrimeField = math::MaybeUnpack<F>;\n\n  constexpr auto SelectRounds = [](uint64_t alpha,\n                                   const std::array<size_t, 5>& rounds) {\n    switch (alpha) {\n      case 3:\n        return rounds[0];\n      case 5:\n        return rounds[1];\n      case 7:\n        return rounds[2];\n      case 9:\n        return rounds[3];\n      case 11:\n        return rounds[4];\n      default:\n        NOTREACHED();\n        return size_t{0};\n    }\n  };\n\n  if constexpr (PrimeField::Config::kModulusBits == 31) {\n    if constexpr (Width == 16) {\n      return SelectRounds(Alpha, std::array<size_t, 5>{20, 14, 13, 13, 13});\n    } else if constexpr (Width == 24) {\n      return SelectRounds(Alpha, std::array<size_t, 5>{23, 22, 21, 21, 21});\n    } else {\n      static_assert(base::AlwaysFalse<F>);\n    }\n  } else if constexpr (PrimeField::Config::kModulusBits == 64) {\n    if constexpr (Width == 8) {\n      return SelectRounds(Alpha, std::array<size_t, 5>{41, 27, 22, 19, 17});\n    } else if constexpr (Width == 16) {\n      return SelectRounds(Alpha, std::array<size_t, 5>{42, 27, 22, 20, 18});\n    } else if constexpr (Width == 24) {\n      return SelectRounds(Alpha, std::array<size_t, 5>{47, 27, 22, 20, 18});\n    } else {\n      static_assert(base::AlwaysFalse<F>);\n    }\n  }\n  return size_t{56};\n}\n\ntemplate <Poseidon2Vendor ExternalMatrixVendor,\n          Poseidon2Vendor InternalMatrixVendor, typename _Field, size_t Rate,\n          uint32_t Alpha, size_t Capacity = 1, size_t FullRounds = 8,\n          size_t PartialRounds =\n              (GetPoseidon2PartialRounds<_Field, Rate + Capacity, Alpha>())>\nstruct Poseidon2Params {\n  using Field = _Field;\n\n  constexpr static Poseidon2Vendor kExternalMatrixVendor = ExternalMatrixVendor;\n  constexpr static Poseidon2Vendor kInternalMatrixVendor = InternalMatrixVendor;\n\n  // The rate (in terms of number of field elements).\n  // See https://iacr.org/archive/eurocrypt2008/49650180/49650180.pdf\n  constexpr static size_t kRate = Rate;\n  // The capacity (in terms of number of field elements).\n  constexpr static size_t kCapacity = Capacity;\n  constexpr static size_t kWidth = Rate + Capacity;\n  // NOTE(ashjeong): |Alpha| is also referred to as |D|\n  // Exponent used in S-boxes.\n  constexpr static uint32_t kAlpha = Alpha;\n  // Number of rounds in a full-round operation.\n  constexpr static size_t kFullRounds = FullRounds;\n  // Number of rounds in a partial-round operation.\n  constexpr static size_t kPartialRounds = PartialRounds;\n};\n\ntemplate <typename F, size_t Rate, uint32_t Alpha>\nstruct Poseidon2ParamsTraits;\n\ntemplate <typename Params>\nauto GetPoseidon2InternalDiagonalArray() {\n  using PrimeField = math::MaybeUnpack<typename Params::Field>;\n  return Poseidon2ParamsTraits<PrimeField, Params::kRate, Params::kAlpha>::\n      GetPoseidon2InternalDiagonalArray();\n}\n\ntemplate <typename Params>\nauto GetPoseidon2InternalShiftArray() {\n  using PrimeField = math::MaybeUnpack<typename Params::Field>;\n  return Poseidon2ParamsTraits<PrimeField, Params::kRate, Params::kAlpha>::\n      GetPoseidon2InternalShiftArray();\n}\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_POSEIDON2_PARAMS_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon2/poseidon2_plonky3_external_matrix.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_POSEIDON2_PLONKY3_EXTERNAL_MATRIX_H_\n#define TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_POSEIDON2_PLONKY3_EXTERNAL_MATRIX_H_\n\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_external_matrix.h\"\n\nnamespace tachyon::crypto {\n\ntemplate <typename F>\nclass Poseidon2Plonky3ExternalMatrix final\n    : public Poseidon2ExternalMatrix<Poseidon2Plonky3ExternalMatrix<F>> {\n public:\n  template <typename Derived>\n  static void DoApply(Eigen::MatrixBase<Derived>& v) {\n    F t0 = v(0, 0) + v(1, 0);\n    F t1 = v(2, 0) + v(3, 0);\n    F t2 = t0 + t1;\n    F t3 = t2 + v(1, 0);\n    F t4 = t2 + v(3, 0);\n    v(3, 0) = t4 + v(0, 0).Double();\n    v(1, 0) = t3 + v(2, 0).Double();\n    v(0, 0) = t3 + t0;\n    v(2, 0) = t4 + t1;\n  }\n\n  static math::Matrix<F> DoConstruct() {\n    return math::Matrix<F>{\n        {F(2), F(3), F(1), F(1)},\n        {F(1), F(2), F(3), F(1)},\n        {F(1), F(1), F(2), F(3)},\n        {F(3), F(1), F(1), F(2)},\n    };\n  }\n};\n\ntemplate <typename Field_>\nstruct Poseidon2ExternalMatrixTraits<Poseidon2Plonky3ExternalMatrix<Field_>> {\n  using Field = Field_;\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_POSEIDON2_PLONKY3_EXTERNAL_MATRIX_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon2/poseidon2_plonky3_internal_matrix.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_POSEIDON2_PLONKY3_INTERNAL_MATRIX_H_\n#define TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_POSEIDON2_PLONKY3_INTERNAL_MATRIX_H_\n\n#include <numeric>\n#include <type_traits>\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_horizen_internal_matrix.h\"\n#include \"tachyon/math/finite_fields/finite_field_traits.h\"\n#include \"tachyon/math/matrix/matrix_types.h\"\n\nnamespace tachyon::crypto {\n\ntemplate <typename F>\nclass Poseidon2Plonky3InternalMatrix {\n public:\n  template <typename F2 = F, std::enable_if_t<math::FiniteFieldTraits<\n                                 F2>::kIsPackedPrimeField>* = nullptr>\n  static void Apply(math::Vector<F>& v,\n                    const math::Vector<F>& diagonal_minus_one) {\n    using PrimeField = typename math::FiniteFieldTraits<F>::PrimeField;\n\n    Poseidon2HorizenInternalMatrix<F>::Apply(v, diagonal_minus_one);\n    if constexpr (PrimeField::Config::kUseMontgomery) {\n      static_assert(PrimeField::Config::kModulusBits <= 32);\n      for (F& f : v) {\n        f *= F::RawOne();\n      }\n    }\n  }\n\n  template <typename F2 = F,\n            std::enable_if_t<\n                math::FiniteFieldTraits<F2>::kIsPrimeField &&\n                !math::FiniteFieldTraits<F2>::kIsPackedPrimeField>* = nullptr>\n  static void Apply(math::Vector<F>& v, const math::Vector<uint8_t>& shifts) {\n    // |partial_sum| =       v₁ + v₂ + ... + vₙ₋₂ + vₙ₋₁\n    // |full_sum|    =  v₀ + v₁ + v₂ + ... + vₙ₋₂ + vₙ₋₁\n    //       s₀      = -v₀ + v₁ + v₂ + ... + vₙ₋₂ + vₙ₋₁\n    //       sᵢ      = |full_sum| + (vᵢ << shiftsᵢ₋₁)\n    static_assert(F::Config::kModulusBits <= 32);\n    uint64_t partial_sum = std::accumulate(\n        v.begin() + 1, v.end(), uint64_t{0},\n        [](uint64_t acc, F value) { return acc += value.value(); });\n    uint64_t full_sum = partial_sum + v[0].value();\n    uint64_t s0 = partial_sum + (-v[0]).value();\n\n    if constexpr (F::Config::kUseMontgomery) {\n      v[0] = F::FromMontgomery(F::Config::FromMontgomery(s0));\n    } else {\n      v[0] = FromU62(s0);\n    }\n    for (Eigen::Index i = 1; i < v.size(); ++i) {\n      uint64_t si = full_sum + (uint64_t{v[i].value()} << shifts[i - 1]);\n      if constexpr (F::Config::kUseMontgomery) {\n        v[i] = F::FromMontgomery(F::Config::FromMontgomery(si));\n      } else {\n        v[i] = FromU62(si);\n      }\n    }\n  }\n\n private:\n  template <typename F2 = F,\n            std::enable_if_t<\n                math::FiniteFieldTraits<F2>::kIsPrimeField &&\n                !math::FiniteFieldTraits<F2>::kIsPackedPrimeField>* = nullptr>\n  constexpr static F FromU62(uint64_t input) {\n    DCHECK_LT(input, (uint64_t{1} << 62));\n    uint32_t lo = static_cast<uint32_t>(input & F::Config::kModulus);\n    uint32_t hi = static_cast<uint32_t>(input >> 31);\n    return F(lo) + F(hi);\n  }\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_POSEIDON2_PLONKY3_INTERNAL_MATRIX_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon2/poseidon2_unittest.cc",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2.h\"\n\n#include <utility>\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/buffer/vector_buffer.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/param_traits/poseidon2_baby_bear.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/param_traits/poseidon2_goldilocks.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_params.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n\nnamespace tachyon::crypto {\n\nnamespace {\n\nclass Poseidon2GoldilocksTest : public math::FiniteFieldTest<math::Goldilocks> {\n};\n\n}  // namespace\n\nTEST_F(Poseidon2GoldilocksTest, Permute) {\n  using F = math::Goldilocks;\n  using Params = Poseidon2Params<Poseidon2Vendor::kHorizen,\n                                 Poseidon2Vendor::kPlonky3, F, 7, 7>;\n\n  auto config = Poseidon2Config<Params>::CreateDefault();\n  Poseidon2Sponge<Params> sponge(std::move(config));\n  SpongeState<Params> state;\n  for (size_t i = 0; i < 8; ++i) {\n    state.elements[i] = F(i);\n  }\n  sponge.Permute(state);\n  math::Vector<F> expected{\n      {F(UINT64_C(14266028122062624699))}, {F(UINT64_C(5353147180106052723))},\n      {F(UINT64_C(15203350112844181434))}, {F(UINT64_C(17630919042639565165))},\n      {F(UINT64_C(16601551015858213987))}, {F(UINT64_C(10184091939013874068))},\n      {F(UINT64_C(16774100645754596496))}, {F(UINT64_C(12047415603622314780))},\n  };\n  EXPECT_EQ(state.elements, expected);\n}\n\nTEST_F(Poseidon2GoldilocksTest, Copyable) {\n  using F = math::Goldilocks;\n  using Params = Poseidon2Params<Poseidon2Vendor::kHorizen,\n                                 Poseidon2Vendor::kPlonky3, F, 7, 7>;\n\n  Poseidon2Sponge<Params> expected;\n\n  base::Uint8VectorBuffer write_buf;\n  ASSERT_TRUE(write_buf.Grow(base::EstimateSize(expected)));\n  ASSERT_TRUE(write_buf.Write(expected));\n  ASSERT_TRUE(write_buf.Done());\n\n  write_buf.set_buffer_offset(0);\n\n  Poseidon2Sponge<Params> value;\n  ASSERT_TRUE(write_buf.Read(&value));\n\n  EXPECT_EQ(value, expected);\n}\n\nnamespace {\n\nclass Poseidon2BabyBearTest\n    : public math::FiniteFieldTest<math::PackedBabyBear> {};\n\n}  // namespace\n\nTEST_F(Poseidon2BabyBearTest, Permute) {\n  using F = math::BabyBear;\n  using Params = Poseidon2Params<Poseidon2Vendor::kHorizen,\n                                 Poseidon2Vendor::kPlonky3, F, 15, 7>;\n\n  Poseidon2Sponge<Params> sponge;\n  SpongeState<Params> state;\n  for (size_t i = 0; i < 16; ++i) {\n    state.elements[i] = F(i);\n  }\n  sponge.Permute(state);\n  math::Vector<F> expected{\n      {F(1699737005)}, {F(296394369)},  {F(268410240)},  {F(828329642)},\n      {F(1491697358)}, {F(1128780676)}, {F(287184043)},  {F(1806152977)},\n      {F(1380147856)}, {F(345666717)},  {F(491196631)},  {F(1875224538)},\n      {F(697740550)},  {F(1854502887)}, {F(1201727753)}, {F(1802410886)},\n  };\n  EXPECT_EQ(state.elements, expected);\n}\n\nTEST_F(Poseidon2BabyBearTest, PermutePacked) {\n  using F = math::BabyBear;\n  using PackedF = math::PackedBabyBear;\n  using Params = Poseidon2Params<Poseidon2Vendor::kHorizen,\n                                 Poseidon2Vendor::kPlonky3, F, 15, 7>;\n  using PackedParams =\n      Poseidon2Params<Poseidon2Vendor::kHorizen, Poseidon2Vendor::kPlonky3,\n                      PackedF, 15, 7>;\n\n  Poseidon2Sponge<PackedParams> packed_sponge;\n  SpongeState<PackedParams> packed_state;\n  for (size_t i = 0; i < 16; ++i) {\n    packed_state.elements[i] = PackedF(i);\n  }\n  packed_sponge.Permute(packed_state);\n\n  Poseidon2Sponge<Params> sponge;\n  SpongeState<Params> state;\n  for (size_t i = 0; i < 16; ++i) {\n    state.elements[i] = F(i);\n  }\n  sponge.Permute(state);\n\n  for (size_t i = 0; i < 16; ++i) {\n    EXPECT_EQ(packed_state.elements[i], PackedF::Broadcast(state.elements[i]));\n  }\n}\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon2/poseidon2_vendor.cc",
    "content": "#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_vendor.h\"\n\n#include <string>\n\n#include \"tachyon/base/logging.h\"\n\nnamespace tachyon::crypto {\n\nstd::string Poseidon2VendorToString(Poseidon2Vendor vendor) {\n  switch (vendor) {\n    case Poseidon2Vendor::kHorizen:\n      return \"horizen\";\n    case Poseidon2Vendor::kPlonky3:\n      return \"plonky3\";\n  }\n  NOTREACHED();\n  return \"\";\n}\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/poseidon2/poseidon2_vendor.h",
    "content": "#ifndef TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_POSEIDON2_VENDOR_H_\n#define TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_POSEIDON2_VENDOR_H_\n\n#include <string>\n\n#include \"tachyon/export.h\"\n\nnamespace tachyon::crypto {\n\nenum class Poseidon2Vendor {\n  kHorizen,\n  kPlonky3,\n};\n\nTACHYON_EXPORT std::string Poseidon2VendorToString(Poseidon2Vendor vendor);\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON2_POSEIDON2_VENDOR_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/sponge.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_CRYPTO_HASHES_SPONGE_SPONGE_H_\n#define TACHYON_CRYPTO_HASHES_SPONGE_SPONGE_H_\n\n#include <bitset>\n#include <numeric>\n#include <vector>\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/crypto/hashes/sponge/duplex_sponge_mode.h\"\n#include \"tachyon/crypto/hashes/sponge/sponge_state.h\"\n#include \"tachyon/math/finite_fields/finite_field_traits.h\"\n\nnamespace tachyon::crypto {\n\n// Specifying the output field element size.\nclass TACHYON_EXPORT FieldElementSize {\n public:\n  static FieldElementSize Full() { return FieldElementSize(false); }\n  static FieldElementSize Truncated(size_t num_bits) {\n    return {true, num_bits};\n  }\n\n  template <typename Field>\n  size_t NumBits() {\n    static_assert(math::FiniteFieldTraits<Field>::kIsPrimeField,\n                  \"NumBits() is only supported for PrimeField\");\n    if (is_truncated_) {\n      CHECK_LE(num_bits_, Field::kModulusBits)\n          << \"num_bits is greater than the bit size of the field.\";\n      return num_bits_;\n    }\n    return Field::kModulusBits - 1;\n  }\n\n  // Calculate the sum of prime field element sizes in |elements|.\n  template <typename Field>\n  static size_t Sum(const std::vector<FieldElementSize>& elements) {\n    static_assert(math::FiniteFieldTraits<Field>::kIsPrimeField,\n                  \"Sum() is only supported for PrimeField\");\n    return (Field::kModulusBits - 1) * elements.size();\n  }\n\n  bool IsFull() const { return !is_truncated_; }\n  bool IsTruncated() const { return is_truncated_; }\n\n private:\n  explicit FieldElementSize(bool is_truncated)\n      : FieldElementSize(is_truncated, 0) {}\n  FieldElementSize(bool is_truncated, size_t num_bits)\n      : is_truncated_(is_truncated), num_bits_(num_bits) {}\n\n  // If |is_truncated_| is false, sample field elements from the entire field.\n  // If |is_truncated_| is true, sample field elements from a subset of the\n  // field, specified by the maximum number of bit.\n  bool is_truncated_;\n  size_t num_bits_ = 0;\n};\n\ntemplate <typename T>\nstruct CryptographicSpongeTraits;\n\n// The interface for a cryptographic sponge.\n// A sponge can |Absorb| and later |Squeeze| bytes of field elements.\n// The outputs are dependent on previous |Absorb| and |Squeeze| calls.\ntemplate <typename Derived>\nclass CryptographicSponge {\n public:\n  using F = typename CryptographicSpongeTraits<Derived>::F;\n\n  // Squeeze |num_elements| nonnative field elements from the sponge.\n  std::vector<F> SqueezeFieldElements(size_t num_elements) {\n    Derived* derived = static_cast<Derived*>(this);\n    return derived->SqueezeFieldElementsWithSizes(\n        std::vector<FieldElementSize>(num_elements, FieldElementSize::Full()));\n  }\n\n  // Creates a new sponge with applied domain separation.\n  Derived Fork(absl::Span<const uint8_t> domain) const {\n    const Derived* derived = static_cast<const Derived*>(this);\n    CHECK(derived->Absorb(domain));\n    return *derived;\n  }\n\n protected:\n  std::vector<F> SqueezeFieldElementsWithSizesDefaultImpl(\n      const std::vector<FieldElementSize>& sizes) {\n    if constexpr (math::FiniteFieldTraits<F>::kIsPrimeField) {\n      if (sizes.empty()) {\n        return {};\n      }\n\n      size_t total_num_bits = FieldElementSize::Sum<F>(sizes);\n\n      Derived derived = static_cast<Derived*>(this);\n      std::vector<bool> bits = derived->SqueezeBits(total_num_bits);\n      auto bits_window = bits.begin();\n\n      std::vector<F> output;\n      output.reserve(sizes.size());\n      for (const FieldElementSize& size : sizes) {\n        size_t num_bits = size.NumBits<F>();\n\n        std::bitset<F::kModulusBits> field_element_bits;\n        for (size_t i = 0; i < num_bits; ++i) {\n          field_element_bits[i] = *(bits_window + i);\n        }\n        bits_window += num_bits;\n\n        output.push_back(F::FromBitsLE(field_element_bits));\n      }\n      return output;\n    } else {\n      NOTIMPLEMENTED();\n      return {};\n    }\n  }\n};\n\n// The interface for field-based cryptographic sponge.\ntemplate <typename Derived>\nclass FieldBasedCryptographicSponge : public CryptographicSponge<Derived> {\n public:\n  using Params = typename CryptographicSpongeTraits<Derived>::Params;\n  using NativeField = typename CryptographicSpongeTraits<Derived>::F;\n\n  // Squeeze |sizes.size()| field elements from the sponge.\n  // where the |i|-th element of the output has |sizes[i]|.\n  std::vector<NativeField> SqueezeNativeFieldElementsWithSizes(\n      SpongeState<Params>& state, const std::vector<FieldElementSize>& sizes) {\n    bool all_full_size =\n        std::all_of(sizes.begin(), sizes.end(),\n                    [](const FieldElementSize& size) { return size.IsFull(); });\n    Derived* derived = static_cast<Derived*>(this);\n    if (all_full_size) {\n      return derived->SqueezeNativeFieldElements(state, sizes.size());\n    } else {\n      return derived->SqueezeFieldElementsWithSizesDefaultImpl(state, sizes);\n    }\n  }\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_HASHES_SPONGE_SPONGE_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/sponge_state.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_CRYPTO_HASHES_SPONGE_SPONGE_STATE_H_\n#define TACHYON_CRYPTO_HASHES_SPONGE_SPONGE_STATE_H_\n\n#include <sstream>\n#include <string>\n#include <utility>\n\n#include \"tachyon/base/strings/string_util.h\"\n#include \"tachyon/crypto/hashes/sponge/duplex_sponge_mode.h\"\n#include \"tachyon/math/matrix/matrix_types.h\"\n\nnamespace tachyon {\nnamespace crypto {\n\ntemplate <typename Params>\nstruct SpongeState {\n  using F = typename Params::Field;\n  // Current sponge's state (current elements in the permutation block)\n  math::Vector<F> elements;\n\n  // Current mode (whether its absorbing or squeezing)\n  DuplexSpongeMode mode = DuplexSpongeMode::Absorbing();\n\n  SpongeState() : elements(math::Vector<F>::Zero(Params::kWidth)) {}\n\n  size_t size() const { return elements.size(); }\n\n  F& operator[](size_t idx) { return elements[idx]; }\n  const F& operator[](size_t idx) const { return elements[idx]; }\n\n  bool operator==(const SpongeState& other) const {\n    return elements == other.elements && mode == other.mode;\n  }\n  bool operator!=(const SpongeState& other) const { return !operator==(other); }\n\n  std::string ToString() const { return base::ContainerToString(elements); }\n\n  std::string ToHexString(bool pad_zero = false) const {\n    return base::ContainerToHexString(elements, pad_zero);\n  }\n};\n\n}  // namespace crypto\n\nnamespace base {\n\ntemplate <typename Params>\nclass Copyable<crypto::SpongeState<Params>> {\n public:\n  using F = typename Params::Field;\n  static bool WriteTo(const crypto::SpongeState<Params>& state,\n                      Buffer* buffer) {\n    return buffer->WriteMany(state.elements, state.mode);\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer,\n                       crypto::SpongeState<Params>* state) {\n    math::Vector<F> elements;\n    crypto::DuplexSpongeMode mode;\n    if (!buffer.ReadMany(&elements, &mode)) {\n      return false;\n    }\n\n    state->elements = std::move(elements);\n    state->mode = mode;\n    return true;\n  }\n\n  static size_t EstimateSize(const crypto::SpongeState<Params>& state) {\n    return base::EstimateSize(state.elements, state.mode);\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_CRYPTO_HASHES_SPONGE_SPONGE_STATE_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/truncated_permutation.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_CRYPTO_HASHES_SPONGE_TRUNCATED_PERMUTATION_H_\n#define TACHYON_CRYPTO_HASHES_SPONGE_TRUNCATED_PERMUTATION_H_\n\n#include <stddef.h>\n\n#include <array>\n#include <utility>\n\n#include \"tachyon/crypto/hashes/compressor.h\"\n#include \"tachyon/crypto/hashes/sponge/sponge.h\"\n\nnamespace tachyon::crypto {\n\ntemplate <typename Derived, size_t Chunk, size_t N>\nclass TruncatedPermutation final\n    : public Compressor<TruncatedPermutation<Derived, Chunk, N>> {\n public:\n  constexpr static size_t kChunk = Chunk;\n  constexpr static size_t kN = N;\n\n  using Params = typename Derived::Params;\n  using F = typename CryptographicSpongeTraits<Derived>::F;\n\n  TruncatedPermutation() = default;\n  explicit TruncatedPermutation(const Derived& derived) : derived_(derived) {}\n  explicit TruncatedPermutation(Derived&& derived)\n      : derived_(std::move(derived)) {}\n\n  const Derived& derived() const { return derived_; }\n\n private:\n  friend class Compressor<TruncatedPermutation<Derived, Chunk, N>>;\n\n  SpongeState<Params> CreateEmptyState() const { return SpongeState<Params>(); }\n\n  template <typename T>\n  std::array<F, Chunk> DoCompress(SpongeState<Params>& state,\n                                  const T& input) const {\n    size_t idx = 0;\n    for (size_t i = 0; i < N; ++i) {\n      for (size_t j = 0; j < Chunk; ++j) {\n        state[idx++] = input[i][j];\n      }\n    }\n    derived_.Permute(state);\n    std::array<F, Chunk> ret;\n    for (size_t i = 0; i < Chunk; ++i) {\n      ret[i] = std::move(state[i]);\n    }\n    return ret;\n  }\n\n  Derived derived_;\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_HASHES_SPONGE_TRUNCATED_PERMUTATION_H_\n"
  },
  {
    "path": "tachyon/crypto/hashes/sponge/truncated_permutation_unittest.cc",
    "content": "#include \"tachyon/crypto/hashes/sponge/truncated_permutation.h\"\n\n#include <utility>\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/param_traits/poseidon2_baby_bear.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon2/poseidon2_params.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n\nnamespace tachyon::crypto {\n\nusing F = math::BabyBear;\nusing Params = Poseidon2Params<Poseidon2Vendor::kHorizen,\n                               Poseidon2Vendor::kPlonky3, F, 15, 7>;\nusing Poseidon2 = Poseidon2Sponge<Params>;\n\nnamespace {\n\nclass TruncatedPermutationTest : public math::FiniteFieldTest<F> {};\n\n}  // namespace\n\nTEST_F(TruncatedPermutationTest, Hash) {\n  constexpr size_t kChunk = 8;\n  constexpr size_t kN = 2;\n\n  Poseidon2 sponge;\n  TruncatedPermutation<Poseidon2, kChunk, kN> compressor(std::move(sponge));\n  std::vector<std::vector<F>> inputs = base::CreateVector(kN, [](uint32_t i) {\n    return base::CreateVector(kChunk,\n                              [i](uint32_t j) { return F(i * kChunk + j); });\n  });\n  std::array<F, kChunk> hash = compressor.Compress(inputs);\n  std::array<F, kChunk> expected = {F(1699737005), F(296394369),  F(268410240),\n                                    F(828329642),  F(1491697358), F(1128780676),\n                                    F(287184043),  F(1806152977)};\n  EXPECT_EQ(hash, expected);\n}\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/crypto/random/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"block_rng\",\n    hdrs = [\"block_rng.h\"],\n    deps = [\":rng\"],\n)\n\ntachyon_cc_library(\n    name = \"rng\",\n    hdrs = [\"rng.h\"],\n    deps = [\n        \"//tachyon:export\",\n        \"//tachyon/base/buffer\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"rng_type\",\n    hdrs = [\"rng_type.h\"],\n    deps = [\n        \"//tachyon/base/flag\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/crypto/random/block_rng.h",
    "content": "#ifndef TACHYON_CRYPTO_RANDOM_BLOCK_RNG_H_\n#define TACHYON_CRYPTO_RANDOM_BLOCK_RNG_H_\n\n#include <stdint.h>\n\n#include \"tachyon/crypto/random/rng.h\"\n\nnamespace tachyon::crypto {\n\ntemplate <typename Derived>\nclass BlockRNG : public RNG {\n public:\n  constexpr static size_t kMaxIndex = Derived::kOutputSize / sizeof(uint32_t);\n\n  BlockRNG() = default;\n  explicit BlockRNG(size_t index) : index_(index) {}\n\n  size_t index() const { return index_; }\n\n  // RNG methods\n  uint32_t NextUint32() override {\n    auto derived = static_cast<Derived*>(this);\n    if (index_ == kMaxIndex) {\n      derived->Update();\n      index_ = 0;\n    }\n    return derived->Get(index_++);\n  }\n\n protected:\n  size_t index_ = kMaxIndex;\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_RANDOM_BLOCK_RNG_H_\n"
  },
  {
    "path": "tachyon/crypto/random/cha_cha20/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"cha_cha20_rng\",\n    hdrs = [\"cha_cha20_rng.h\"],\n    deps = [\n        \"//tachyon:export\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base:random\",\n        \"//tachyon/base/buffer:copyable\",\n        \"//tachyon/base/buffer:endian_auto_reset\",\n        \"//tachyon/crypto/random:block_rng\",\n        \"@com_google_absl//absl/base:endian\",\n        \"@com_google_boringssl//:crypto\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"cha_cha20_unittests\",\n    srcs = [\"cha_cha20_rng_unittest.cc\"],\n    deps = [\":cha_cha20_rng\"],\n)\n"
  },
  {
    "path": "tachyon/crypto/random/cha_cha20/cha_cha20_rng.h",
    "content": "#ifndef TACHYON_CRYPTO_RANDOM_CHA_CHA20_CHA_CHA20_RNG_H_\n#define TACHYON_CRYPTO_RANDOM_CHA_CHA20_CHA_CHA20_RNG_H_\n\n#include <stddef.h>\n#include <stdint.h>\n#include <string.h>\n\n#include \"absl/base/internal/endian.h\"\n#include \"openssl/chacha.h\"\n\n#include \"tachyon/base/buffer/copyable.h\"\n#include \"tachyon/base/buffer/endian_auto_reset.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/random.h\"\n#include \"tachyon/crypto/random/block_rng.h\"\n#include \"tachyon/export.h\"\n\nnamespace tachyon::crypto {\n\n// ChaCha20RNG stands for \"ChaCha20 Random Number Generator\".\n// Please use |base::Uniform()| mostly for production. This is being used in\n// snark-verifier and we need a seed stability to ensure the same random number\n// to be generated from a given seed.\n//\n// See\n// [Seed Stability]\n// (https://abseil.io/docs/cpp/guides/random#classes-of-generator-stability),\n// [ChaCha20 and Poly1305 for IETF Protocols]\n// (https://tools.ietf.org/html/rfc8439) and\n// [rand_chacha](https://github.com/rust-random/rand/tree/master/rand_chacha).\nclass TACHYON_EXPORT ChaCha20RNG final : public BlockRNG<ChaCha20RNG> {\n public:\n  constexpr static size_t kSeedSize = 32;\n  constexpr static size_t kInputSize = 64;\n  constexpr static size_t kOutputSize = 64;\n  constexpr static size_t kStateSize =\n      sizeof(size_t) + kInputSize + kOutputSize;\n  constexpr static uint8_t kDefaultSeed[kSeedSize] = {\n      0,\n  };\n\n  ChaCha20RNG() : ChaCha20RNG(kDefaultSeed) {}\n\n  // RNG methods\n  void SetRandomSeed() override {\n    uint8_t seed[kSeedSize];\n    for (size_t i = 0; i < 4; ++i) {\n      *reinterpret_cast<uint64_t*>(&seed[i * 8]) =\n          base::Uniform(base::Range<uint64_t>::All());\n    }\n    CHECK(SetSeed(seed));\n  }\n\n  [[nodiscard]] bool SetSeed(absl::Span<const uint8_t> seed) override {\n    if (seed.size() != kSeedSize) {\n      LOG(ERROR) << \"Seed size must be \" << kSeedSize;\n      return false;\n    }\n    for (size_t i = 0; i < 8; ++i) {\n      input_[i + 4] = absl::little_endian::Load32(&seed[4 * i]);\n    }\n    return true;\n  }\n\n  [[nodiscard]] bool ReadFromBuffer(\n      const base::ReadOnlyBuffer& buffer) override {\n    size_t index;\n    uint8_t input[kInputSize];\n    uint8_t output[kOutputSize];\n    base::EndianAutoReset auto_reset(buffer, base::Endian::kLittle);\n    if (!buffer.ReadMany(&index, input, output)) return false;\n    index_ = index;\n    memcpy(input_, input, kInputSize);\n    memcpy(output_, output, kOutputSize);\n    return true;\n  }\n\n  [[nodiscard]] bool WriteToBuffer(base::Buffer& buffer) const override {\n    base::EndianAutoReset auto_reset(buffer, base::Endian::kLittle);\n    return buffer.WriteMany(index_, input_, output_);\n  }\n\n private:\n  friend class BlockRNG<ChaCha20RNG>;\n\n  explicit ChaCha20RNG(const uint8_t seed[kSeedSize]) {\n    // constants\n    input_[0] = 0x61707865;\n    input_[1] = 0x3320646E;\n    input_[2] = 0x79622D32;\n    input_[3] = 0x6B206574;\n\n    // seed\n    CHECK(SetSeed(absl::Span<const uint8_t>(seed, kSeedSize)));\n\n    // counter\n    input_[12] = 0;\n    input_[13] = 0;\n    input_[14] = 0;\n    input_[15] = 0;\n  }\n\n  ChaCha20RNG(size_t index, const uint8_t input[kInputSize],\n              const uint8_t output[kOutputSize])\n      : BlockRNG<ChaCha20RNG>(index) {\n    memcpy(input_, input, kInputSize);\n    memcpy(output_, output, kOutputSize);\n  }\n\n  // BlockRNG<ChaCha20RNG> methods\n  void Update() {\n    chacha_core(output_, input_);\n    if (++input_[12] == 0) {\n      ++input_[13];\n    }\n  }\n\n  uint32_t Get(size_t index) const {\n    return reinterpret_cast<const uint32_t*>(output_)[index];\n  }\n\n  uint32_t input_[kInputSize / sizeof(uint32_t)];\n  uint8_t output_[kOutputSize];\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_RANDOM_CHA_CHA20_CHA_CHA20_RNG_H_\n"
  },
  {
    "path": "tachyon/crypto/random/cha_cha20/cha_cha20_rng_unittest.cc",
    "content": "#include \"tachyon/crypto/random/cha_cha20/cha_cha20_rng.h\"\n\n#include \"gtest/gtest.h\"\n\nnamespace tachyon::crypto {\n\nTEST(ChaCha20RNGTest, NextUint64) {\n  uint64_t expected[100] = {\n      UINT64_C(10393729187455219830), UINT64_C(2935650227004792128),\n      UINT64_C(1940362735889535677),  UINT64_C(14343251830567286440),\n      UINT64_C(10180482965161198042), UINT64_C(3984235106219861111),\n      UINT64_C(2062956586891494250),  UINT64_C(9684409023775279043),\n      UINT64_C(8806878500039886751),  UINT64_C(939050496341555864),\n      UINT64_C(7594726247694405579),  UINT64_C(17112251633709073938),\n      UINT64_C(4850067408395810601),  UINT64_C(15364549599435125205),\n      UINT64_C(5042635551453211953),  UINT64_C(8020199874967036332),\n      UINT64_C(16243400166531402029), UINT64_C(8475888955442691758),\n      UINT64_C(14295220695038062990), UINT64_C(6751916914945475504),\n      UINT64_C(1107176458225657601),  UINT64_C(2214742857283673640),\n      UINT64_C(13876944814363366003), UINT64_C(17458382352914969439),\n      UINT64_C(7734567147541635091),  UINT64_C(774568827527241941),\n      UINT64_C(8297748152085231813),  UINT64_C(11858361222889432003),\n      UINT64_C(3318454703339852845),  UINT64_C(4588149941567271405),\n      UINT64_C(6203934359032441887),  UINT64_C(9085562760231545576),\n      UINT64_C(12521492590937810661), UINT64_C(12762635606041057787),\n      UINT64_C(3096410682545938149),  UINT64_C(5157591943273650122),\n      UINT64_C(14488165282383183438), UINT64_C(9577362568275248150),\n      UINT64_C(4079332552118504582),  UINT64_C(5587461883856785389),\n      UINT64_C(5063541989730226400),  UINT64_C(1524727576459009209),\n      UINT64_C(12085977716165202206), UINT64_C(15787106821018276889),\n      UINT64_C(1920590957300281692),  UINT64_C(5957300950003652231),\n      UINT64_C(2416862117611592572),  UINT64_C(5479067130089892010),\n      UINT64_C(912532022007996146),   UINT64_C(7171791822103543488),\n      UINT64_C(5461290933381004665),  UINT64_C(5182606474117124289),\n      UINT64_C(1179899407735948526),  UINT64_C(6860028705977903330),\n      UINT64_C(13960227638405752870), UINT64_C(3347080940407452016),\n      UINT64_C(13839352340982246125), UINT64_C(731528835182815829),\n      UINT64_C(16501257607314924491), UINT64_C(547481869190374323),\n      UINT64_C(10971329490697310343), UINT64_C(6916228695646037994),\n      UINT64_C(16280261910639226348), UINT64_C(15817793174876584019),\n      UINT64_C(9074420358402181148),  UINT64_C(357118251176964789),\n      UINT64_C(15272588994549399728), UINT64_C(13325403081000393433),\n      UINT64_C(877970774918498640),   UINT64_C(15732295103341332567),\n      UINT64_C(1940976211848945660),  UINT64_C(13386000513454104877),\n      UINT64_C(7711777664747450393),  UINT64_C(8874701560112629883),\n      UINT64_C(11994694005001377979), UINT64_C(14890135524144922226),\n      UINT64_C(4179774647536864771),  UINT64_C(1478747169106552816),\n      UINT64_C(7185786830989669347),  UINT64_C(13822778071138932147),\n      UINT64_C(467554094344374893),   UINT64_C(8477030970323946282),\n      UINT64_C(6235833977768581969),  UINT64_C(9405569865753553512),\n      UINT64_C(13456404989354226680), UINT64_C(9288529277979812446),\n      UINT64_C(13830455138970923676), UINT64_C(13836816604810330492),\n      UINT64_C(17971588049683313305), UINT64_C(13554147842590742486),\n      UINT64_C(15173774080656305358), UINT64_C(3196108095160856000),\n      UINT64_C(840335128193248279),   UINT64_C(6100810389749577965),\n      UINT64_C(14151766940868082726), UINT64_C(3296151717789255466),\n      UINT64_C(13897386303572935112), UINT64_C(9025696065642439663),\n      UINT64_C(7533880160997907807),  UINT64_C(3525968612637664997),\n  };\n\n  ChaCha20RNG rng;\n  for (size_t i = 0; i < 100; ++i) {\n    EXPECT_EQ(rng.NextUint64(), expected[i]);\n  }\n}\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/crypto/random/rng.h",
    "content": "#ifndef TACHYON_CRYPTO_RANDOM_RNG_H_\n#define TACHYON_CRYPTO_RANDOM_RNG_H_\n\n#include <stdint.h>\n\n#include \"absl/types/span.h\"\n\n#include \"tachyon/base/buffer/buffer.h\"\n#include \"tachyon/export.h\"\n\nnamespace tachyon::crypto {\n\nclass TACHYON_EXPORT RNG {\n public:\n  virtual ~RNG() = default;\n\n  uint64_t NextUint64() {\n    uint64_t lo = uint64_t{NextUint32()};\n    uint64_t hi = uint64_t{NextUint32()};\n    return (hi << 32 | lo);\n  }\n\n  virtual void SetRandomSeed() = 0;\n\n  // Return false if the length of the |seed| exceeds the expected seed size.\n  [[nodiscard]] virtual bool SetSeed(absl::Span<const uint8_t> seed) = 0;\n\n  virtual uint32_t NextUint32() = 0;\n\n  // Return false if it fails to read the state from the |buffer|.\n  [[nodiscard]] virtual bool ReadFromBuffer(\n      const base::ReadOnlyBuffer& buffer) = 0;\n\n  // Return false if it fails to write the state to the |buffer|.\n  [[nodiscard]] virtual bool WriteToBuffer(base::Buffer& buffer) const = 0;\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_RANDOM_RNG_H_\n"
  },
  {
    "path": "tachyon/crypto/random/rng_type.h",
    "content": "#ifndef TACHYON_CRYPTO_RANDOM_RNG_TYPE_H_\n#define TACHYON_CRYPTO_RANDOM_RNG_TYPE_H_\n\n#include <stdint.h>\n\n#include <string>\n\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/base/flag/flag_value_traits.h\"\n\nnamespace tachyon {\nnamespace crypto {\n\n// THE ORDER OF ELEMENTS ARE IMPORTANT!! DO NOT CHANGE!\n// This order matches with the constants of c APIs.\n// Pleas refer to tachyon/c/crypto/random.h for details.\nenum class RNGType : uint8_t {\n  kXORShift,\n  kChaCha20,\n};\n\n}  // namespace crypto\n\nnamespace base {\n\ntemplate <>\nclass FlagValueTraits<crypto::RNGType> {\n public:\n  static bool ParseValue(std::string_view input, crypto::RNGType* value,\n                         std::string* reason) {\n    if (input == \"xor_shift\") {\n      *value = crypto::RNGType::kXORShift;\n    } else if (input == \"cha_cha20\") {\n      *value = crypto::RNGType::kChaCha20;\n    } else {\n      *reason = absl::Substitute(\"Unknown rng type: $0\", input);\n      return false;\n    }\n    return true;\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_CRYPTO_RANDOM_RNG_TYPE_H_\n"
  },
  {
    "path": "tachyon/crypto/random/xor_shift/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"xor_shift_rng\",\n    hdrs = [\"xor_shift_rng.h\"],\n    deps = [\n        \"//tachyon:export\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base:random\",\n        \"//tachyon/base/buffer:endian_auto_reset\",\n        \"//tachyon/crypto/random:rng\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"xor_shift_unittests\",\n    srcs = [\"xor_shift_rng_unittest.cc\"],\n    deps = [\":xor_shift_rng\"],\n)\n"
  },
  {
    "path": "tachyon/crypto/random/xor_shift/xor_shift_rng.h",
    "content": "#ifndef TACHYON_CRYPTO_RANDOM_XOR_SHIFT_XOR_SHIFT_RNG_H_\n#define TACHYON_CRYPTO_RANDOM_XOR_SHIFT_XOR_SHIFT_RNG_H_\n\n#include <stddef.h>\n#include <stdint.h>\n#include <string.h>\n\n#include \"tachyon/base/buffer/endian_auto_reset.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/random.h\"\n#include \"tachyon/crypto/random/rng.h\"\n#include \"tachyon/export.h\"\n\nnamespace tachyon::crypto {\n\n// XORShiftRNG stands for \"XOR Shift Random Number Generator\".\n// Please use |base::Uniform()| mostly for production. This is being used in\n// Halo2 and we need a seed stability to ensure the same random number to be\n// generated from a given seed.\n//\n// See\n// [Seed Stability]\n// (https://abseil.io/docs/cpp/guides/random#classes-of-generator-stability)\n// and [Xorshift RNGs](https://www.jstatsoft.org/v08/i14/paper).\nclass TACHYON_EXPORT XORShiftRNG final : public RNG {\n public:\n  constexpr static size_t kSeedSize = 16;\n  constexpr static size_t kStateSize = 16;\n\n  XORShiftRNG() = default;\n\n  // RNG methods\n  void SetRandomSeed() override {\n    uint8_t seed[kSeedSize];\n    *reinterpret_cast<uint64_t*>(&seed[0]) =\n        base::Uniform(base::Range<uint64_t>::All());\n    *reinterpret_cast<uint64_t*>(&seed[8]) =\n        base::Uniform(base::Range<uint64_t>::All());\n    CHECK(SetSeed(seed));\n  }\n\n  [[nodiscard]] bool SetSeed(absl::Span<const uint8_t> seed) override {\n    if (seed.size() != kSeedSize) {\n      LOG(ERROR) << \"Seed size must be \" << kSeedSize;\n      return false;\n    }\n    memcpy(&x_, &seed[0], sizeof(uint32_t));\n    memcpy(&y_, &seed[4], sizeof(uint32_t));\n    memcpy(&z_, &seed[8], sizeof(uint32_t));\n    memcpy(&w_, &seed[12], sizeof(uint32_t));\n    return true;\n  }\n\n  uint32_t NextUint32() override {\n    uint32_t t = x_ ^ (x_ << 11);\n    x_ = y_;\n    y_ = z_;\n    z_ = w_;\n    w_ = w_ ^ (w_ >> 19) ^ (t ^ (t >> 8));\n    return w_;\n  }\n\n  [[nodiscard]] bool ReadFromBuffer(\n      const base::ReadOnlyBuffer& buffer) override {\n    uint32_t x, y, z, w;\n    base::EndianAutoReset auto_reset(buffer, base::Endian::kLittle);\n    if (!buffer.ReadMany(&x, &y, &z, &w)) return false;\n    x_ = x;\n    y_ = y;\n    z_ = z;\n    w_ = w;\n    return true;\n  }\n\n  [[nodiscard]] bool WriteToBuffer(base::Buffer& buffer) const override {\n    base::EndianAutoReset auto_reset(buffer, base::Endian::kLittle);\n    return buffer.WriteMany(x_, y_, z_, w_);\n  }\n\n private:\n  XORShiftRNG(uint32_t x, uint32_t y, uint32_t z, uint32_t w)\n      : x_(x), y_(y), z_(z), w_(w) {}\n\n  uint32_t x_ = 195911405;  // 0xBAD_5EED\n  uint32_t y_ = 195911405;  // 0xBAD_5EED\n  uint32_t z_ = 195911405;  // 0xBAD_5EED\n  uint32_t w_ = 195911405;  // 0xBAD_5EED\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_RANDOM_XOR_SHIFT_XOR_SHIFT_RNG_H_\n"
  },
  {
    "path": "tachyon/crypto/random/xor_shift/xor_shift_rng_unittest.cc",
    "content": "#include \"tachyon/crypto/random/xor_shift/xor_shift_rng.h\"\n\n#include \"gtest/gtest.h\"\n\nnamespace tachyon::crypto {\n\nTEST(XORShiftRngTest, NextUint64) {\n  uint8_t seed[16] = {\n      0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d,\n      0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5,\n  };\n  uint64_t expected[100] = {\n      UINT64_C(5719644765813611536),  UINT64_C(14305956443231340292),\n      UINT64_C(3482677072762842571),  UINT64_C(7784261772788248241),\n      UINT64_C(17253842704833461224), UINT64_C(14239653185454770815),\n      UINT64_C(6633452422753404160),  UINT64_C(13090433023660269913),\n      UINT64_C(18258063891970645770), UINT64_C(437291729752752195),\n      UINT64_C(9212368311569592005),  UINT64_C(1881318862022302859),\n      UINT64_C(8121433790281421193),  UINT64_C(9430684813719984921),\n      UINT64_C(3743710790114399315),  UINT64_C(7623403977589353428),\n      UINT64_C(10689784540225189536), UINT64_C(6314227608983747315),\n      UINT64_C(12023216175781001324), UINT64_C(1247429250317392482),\n      UINT64_C(15124404311311414102), UINT64_C(3536376116331771714),\n      UINT64_C(13372140391163231968), UINT64_C(4627552104030767553),\n      UINT64_C(5815810961114836999),  UINT64_C(15198555498458018071),\n      UINT64_C(14598705655181811041), UINT64_C(1974664556384156671),\n      UINT64_C(16453960414236511979), UINT64_C(14577587862365872550),\n      UINT64_C(5307326110650084888),  UINT64_C(17287926254572675193),\n      UINT64_C(5814330016395360862),  UINT64_C(908179402052645855),\n      UINT64_C(17008011703610211229), UINT64_C(16907694749369302173),\n      UINT64_C(9259733836259213201),  UINT64_C(6804200541314624068),\n      UINT64_C(15423702039459644860), UINT64_C(15538406174167214534),\n      UINT64_C(12642351015120266619), UINT64_C(5734927085045947509),\n      UINT64_C(338984259144816760),   UINT64_C(814837196519237940),\n      UINT64_C(12092377574622215765), UINT64_C(4929686523249137873),\n      UINT64_C(7578938277161758216),  UINT64_C(556715018348158161),\n      UINT64_C(18135826653751038349), UINT64_C(2096671650130071344),\n      UINT64_C(4878364342165081558),  UINT64_C(6383170428904977100),\n      UINT64_C(4034655074585306454),  UINT64_C(430637940813927304),\n      UINT64_C(2387870089825088464),  UINT64_C(3614062426512926158),\n      UINT64_C(11430717537338432583), UINT64_C(8428449033530022598),\n      UINT64_C(5396187671217797056),  UINT64_C(12664192531195519784),\n      UINT64_C(186633926847421261),   UINT64_C(5266300151260521672),\n      UINT64_C(10751902326511832777), UINT64_C(6123775252746815033),\n      UINT64_C(2725770165193166901),  UINT64_C(5410213407606773643),\n      UINT64_C(6976963969868509890),  UINT64_C(15795993068178353437),\n      UINT64_C(5289892779407555975),  UINT64_C(9038347105216106497),\n      UINT64_C(2450746375875507041),  UINT64_C(9878547588967777921),\n      UINT64_C(9821024908276872521),  UINT64_C(9756217541011904216),\n      UINT64_C(3492661374481453297),  UINT64_C(12987601896609314818),\n      UINT64_C(3135698627929260851),  UINT64_C(16051428247861855827),\n      UINT64_C(3907579066732945090),  UINT64_C(12261942348272980607),\n      UINT64_C(9259426699815884944),  UINT64_C(2078858535711350595),\n      UINT64_C(12020280222695588417), UINT64_C(8200866121682790835),\n      UINT64_C(1290391369830084283),  UINT64_C(4220890213472660002),\n      UINT64_C(14276759160287357654), UINT64_C(18358041331099204300),\n      UINT64_C(9110387079659320711),  UINT64_C(2726424275717210332),\n      UINT64_C(12193543865555892126), UINT64_C(13979317448342616273),\n      UINT64_C(15589337403047049790), UINT64_C(5042850533273272111),\n      UINT64_C(5598077207033268956),  UINT64_C(8557810207710613009),\n      UINT64_C(9318823210973686820),  UINT64_C(7879556961278080875),\n      UINT64_C(4115505420980006796),  UINT64_C(16660106575296596679),\n  };\n\n  XORShiftRNG rng;\n  ASSERT_TRUE(rng.SetSeed(seed));\n  for (size_t i = 0; i < 100; ++i) {\n    EXPECT_EQ(rng.NextUint64(), expected[i]);\n  }\n}\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/crypto/sumcheck/multilinear/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"multilinear_sumcheck\",\n    hdrs = [\"multilinear_sumcheck.h\"],\n    deps = [\n        \":sumcheck_prover\",\n        \":sumcheck_verifier\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"sumcheck_prover\",\n    hdrs = [\"sumcheck_prover.h\"],\n    deps = [\n        \":sumcheck_prover_msg\",\n        \":sumcheck_proving_key\",\n        \":sumcheck_verifier_msg\",\n        \"//tachyon/base:parallelize\",\n        \"//tachyon/math/polynomials/multivariate:linear_combination\",\n        \"//tachyon/math/polynomials/univariate:univariate_evaluations\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"sumcheck_prover_msg\",\n    hdrs = [\"sumcheck_prover_msg.h\"],\n    deps = [\n        \"//tachyon/base/buffer:copyable\",\n        \"//tachyon/math/polynomials/univariate:univariate_evaluations\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"sumcheck_proving_key\",\n    hdrs = [\"sumcheck_proving_key.h\"],\n    deps = [\n        \":sumcheck_verifying_key\",\n        \"//tachyon/base/containers:container_util\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"sumcheck_verifier\",\n    hdrs = [\"sumcheck_verifier.h\"],\n    deps = [\n        \":sumcheck_prover_msg\",\n        \":sumcheck_verifier_msg\",\n        \":sumcheck_verifying_key\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"sumcheck_verifier_msg\",\n    hdrs = [\"sumcheck_verifier_msg.h\"],\n    deps = [\"//tachyon/base/buffer:copyable\"],\n)\n\ntachyon_cc_library(\n    name = \"sumcheck_verifying_key\",\n    hdrs = [\"sumcheck_verifying_key.h\"],\n    deps = [\n        \"//tachyon/base:random\",\n        \"//tachyon/base/buffer:copyable\",\n        \"//tachyon/math/polynomials/multivariate:linear_combination\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"multilinear_unittests\",\n    srcs = [\n        \"multilinear_sumcheck_unittest.cc\",\n        \"sumcheck_prover_msg_unittest.cc\",\n        \"sumcheck_proving_key_unittest.cc\",\n        \"sumcheck_verifier_msg_unittest.cc\",\n        \"sumcheck_verifying_key_unittest.cc\",\n    ],\n    deps = [\n        \":multilinear_sumcheck\",\n        \":sumcheck_prover_msg\",\n        \":sumcheck_proving_key\",\n        \":sumcheck_verifier_msg\",\n        \"//tachyon/base:range\",\n        \"//tachyon/base/buffer:vector_buffer\",\n        \"//tachyon/math/finite_fields/test:finite_field_test\",\n        \"//tachyon/math/finite_fields/test:gf7\",\n        \"//tachyon/math/polynomials/multivariate:multilinear_extension\",\n        \"//tachyon/math/polynomials/univariate:univariate_evaluations\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/crypto/sumcheck/multilinear/multilinear_sumcheck.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_CRYPTO_SUMCHECK_MULTILINEAR_MULTILINEAR_SUMCHECK_H_\n#define TACHYON_CRYPTO_SUMCHECK_MULTILINEAR_MULTILINEAR_SUMCHECK_H_\n\n#include <utility>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/crypto/sumcheck/multilinear/sumcheck_prover.h\"\n#include \"tachyon/crypto/sumcheck/multilinear/sumcheck_verifier.h\"\n\nnamespace tachyon::crypto {\n\ntemplate <typename MLE>\nclass MultilinearSumcheck {\n public:\n  template <size_t MaxDegree>\n  [[nodiscard]] static bool RunInteractiveProtocol(\n      const math::LinearCombination<MLE>& linear_combination,\n      size_t num_variables) {\n    using F = typename MLE::Field;\n\n    const SumcheckProvingKey<MLE> proving_key =\n        SumcheckProvingKey<MLE>::Build(linear_combination);\n    const SumcheckVerifyingKey verifying_key =\n        SumcheckVerifyingKey::Build(linear_combination);\n    SumcheckProver<MLE, MaxDegree> prover(std::move(proving_key));\n    SumcheckVerifier<MLE> verifier(std::move(verifying_key));\n    SumcheckProverMsg<F, MaxDegree> prover_msg = prover.Round();\n    SumcheckVerifierMsg<F> verifier_msg = verifier.Round(std::move(prover_msg));\n\n    for (size_t i = 1; i < num_variables; ++i) {\n      prover_msg = prover.Round(std::move(verifier_msg));\n      verifier_msg = verifier.Round(std::move(prover_msg));\n    }\n    const F asserted_sum = linear_combination.Combine();\n    Subclaim<F> subclaim;\n    if (!verifier.CheckAndGenerateSubclaim(asserted_sum, &subclaim)) {\n      return false;\n    }\n    F expected_sum = linear_combination.Evaluate(subclaim.point);\n\n    return expected_sum == subclaim.expected_evaluation;\n  }\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_SUMCHECK_MULTILINEAR_MULTILINEAR_SUMCHECK_H_\n"
  },
  {
    "path": "tachyon/crypto/sumcheck/multilinear/multilinear_sumcheck_unittest.cc",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#include \"tachyon/crypto/sumcheck/multilinear/multilinear_sumcheck.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/range.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n#include \"tachyon/math/polynomials/multivariate/linear_combination.h\"\n#include \"tachyon/math/polynomials/multivariate/multilinear_dense_evaluations.h\"\n\nnamespace tachyon::crypto {\nnamespace {\n\nusing F = math::GF7;\n\nclass MultilinearSumcheckTest : public math::FiniteFieldTest<F> {};\n\n}  // namespace\n\nTEST_F(MultilinearSumcheckTest, TestInteractiveProtocolSmall) {\n  // |SumcheckVerifier| runs in-depth logic in |InterpolateUniPoly()|\n  constexpr size_t kNumVariables = 2;\n  const base::Range<size_t> kMaxEvaluationsInTermRange(2, 3);\n  constexpr size_t kMaxPossibleEvaluations = 3;\n  size_t kNumTerms = 2;\n  using MLE = math::MultilinearDenseEvaluations<F, kNumVariables>;\n  const math::LinearCombination<MLE> linear_combination =\n      math::LinearCombination<MLE>::Random(\n          kNumVariables, kMaxEvaluationsInTermRange, kNumTerms);\n\n  EXPECT_TRUE(\n      MultilinearSumcheck<MLE>::RunInteractiveProtocol<kMaxPossibleEvaluations>(\n          linear_combination, kNumVariables));\n}\n\nTEST_F(MultilinearSumcheckTest, TestInteractiveProtocolBig) {\n  // |SumcheckVerifier| returns early for |InterpolateUniPoly()|\n  constexpr size_t kNumVariables = 10;\n  const base::Range<size_t> kMaxEvaluationsInTermRange(6, 30);\n  constexpr size_t kMaxPossibleEvaluations = 30;\n  size_t kNumTerms = 20;\n  using MLE = math::MultilinearDenseEvaluations<F, kNumVariables>;\n  const math::LinearCombination<MLE> linear_combination =\n      math::LinearCombination<MLE>::Random(\n          kNumVariables, kMaxEvaluationsInTermRange, kNumTerms);\n\n  EXPECT_TRUE(\n      MultilinearSumcheck<MLE>::RunInteractiveProtocol<kMaxPossibleEvaluations>(\n          linear_combination, kNumVariables));\n}\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/crypto/sumcheck/multilinear/sumcheck_prover.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_CRYPTO_SUMCHECK_MULTILINEAR_SUMCHECK_PROVER_H_\n#define TACHYON_CRYPTO_SUMCHECK_MULTILINEAR_SUMCHECK_PROVER_H_\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/parallelize.h\"\n#include \"tachyon/crypto/sumcheck/multilinear/sumcheck_prover_msg.h\"\n#include \"tachyon/crypto/sumcheck/multilinear/sumcheck_proving_key.h\"\n#include \"tachyon/crypto/sumcheck/multilinear/sumcheck_verifier_msg.h\"\n#include \"tachyon/math/polynomials/multivariate/linear_combination.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluations.h\"\n\nnamespace tachyon::crypto {\n\n// The prover argues for the sum of the polynomial over {0,1}^|num_variables_|.\n//\n// The polynomial is represented by a list of terms each containing a\n// coefficient and a list of |MultilinearDenseEvaluations|. Refer to\n// \"tachyon/math/polynomials/multivariate/linear_combination.h\" for more info.\ntemplate <typename MLE, size_t MaxDegree>\nclass SumcheckProver {\n public:\n  constexpr static size_t kParallelFactor = 16;\n\n  using F = typename MLE::Field;\n  using Point = typename MLE::Point;\n  using Term = math::LinearCombinationTerm<F>;\n\n  SumcheckProver() = default;\n  explicit SumcheckProver(const SumcheckProvingKey<MLE>& key)\n      : num_variables_(key.verifying_key.num_variables),\n        max_evaluations_(key.verifying_key.max_evaluations),\n        terms_(key.terms),\n        flattened_ml_evaluations_(key.flattened_ml_evaluations) {\n    // Constants are not accepted.\n    CHECK_NE(num_variables_, size_t{0});\n    randomness_.reserve(num_variables_);\n  }\n  explicit SumcheckProver(SumcheckProvingKey<MLE>&& key)\n      : num_variables_(key.verifying_key.num_variables),\n        max_evaluations_(key.verifying_key.max_evaluations),\n        terms_(std::move(key.terms)),\n        flattened_ml_evaluations_(std::move(key.flattened_ml_evaluations)) {\n    // Constants are not accepted.\n    CHECK_NE(num_variables_, size_t{0});\n    randomness_.reserve(num_variables_);\n  }\n\n  // Generate prover message, and proceed to next round.\n  //\n  // Main algorithm used is from section 3.2 of\n  // [XZZPS19](https://eprint.iacr.org/2019/317.pdf#subsection.3.2).\n  //\n  // Referencing https://people.cs.georgetown.edu/jthaler/sumcheck.pdf\n  // v = the number of variables\n  // For the first round, the Prover creates and sends:\n  //   g₁(X₁) := ∑ g(X₁,x₂,...,xᵥ), (x₂,...,xᵥ)∈{0,1}ᵛ⁻¹\n  // For the consequent round i, the Prover creates and sends:\n  //   gᵢ(Xᵢ) = ∑ g(r₁,...,rᵢ₋₁,Xᵢ,xᵢ₊₁,...,xᵥ), (xᵢ₊₁ ,...,xᵥ)∈{0,1}ᵛ⁻¹\n  // More specifically, |Prover(VerifierMsg)| fixes the front variables with\n  // r₁,...,rᵢ₋₁, and |Prover()| creates the total gᵢ(Xᵢ).\n  SumcheckProverMsg<F, MaxDegree> Round() {\n    // Max number of prover rounds should be |num_variables_|.\n    CHECK_LE(++round_, num_variables_);\n\n    // Generate sum\n    // clang-format off\n    // g(x₁, x₂) = 1(1 - x₁)(1 - x₂) + 2x₁(1 - x₂) + 3(1 - x₁)x₂ + 4x₁x₂\n    //\n    // Creating the univariate polynomial:\n    // g₁(X₁) = ∑ 1(1 - X₁)(1 - x₂) + 2X₁(1 - x₂) + 3(1 - X₁)x₂ + 4X₁x₂, x₂∈{0,1}\n    //        = 1(1 - X₁) + 2X₁ + 3(1 - X₁) + 4X₁\n    //        = start₀ + step₀X₁ + start₁ + step₁X₁\n    //         (where start₀ = 1, step₀ = 1 = 2 - 1, start₁ = 3 and step₁ = 1 = 4 - 3)\n    //        = start₀ + start₁ + (step₀ + step₁)X₁\n    //\n    // g₁(0) = start₀ + start₁\n    // g₁(1) = start₀ + start₁ + step₀ + step₁\n    // g₁(2) = start₀ + start₁ + 2step₀ + 2step₁\n    // g₁(3) = start₀ + start₁ + 3step₀ + 3step₁\n    //         where product_sum₀ = start₀ + start₁ and product_sumᵢ = product_sum₀ + (i - 1) * (step₀ + step₁)\n    // clang-format on\n#if defined(TACHYON_HAS_OPENMP)\n    size_t thread_nums = static_cast<size_t>(omp_get_max_threads());\n#else\n    size_t thread_nums = 1;\n#endif\n    size_t size = size_t{1} << (num_variables_ - round_);\n    std::vector<std::vector<F>> evals_vec = base::ParallelizeMap(\n        size,\n        [this](size_t len, size_t chunk_offset, size_t chunk_size) {\n          size_t begin = chunk_offset * chunk_size;\n          std::vector<F> ret(max_evaluations_ + 1, F::Zero());\n          std::vector<F> tmp(max_evaluations_ + 1);\n          for (size_t i = begin; i < begin + len; ++i) {\n            for (const Term& term : terms_) {\n              std::fill(tmp.begin(), tmp.end(), term.coefficient);\n              EvaluateTermPerVariable(i, tmp, term);\n              for (size_t j = 0; j < ret.size(); ++j) {\n                ret[j] += tmp[j];\n              }\n            }\n          }\n          return ret;\n        },\n        kParallelFactor * thread_nums);\n    for (size_t i = 1; i < evals_vec.size(); ++i) {\n      for (size_t j = 0; j < max_evaluations_ + 1; ++j) {\n        evals_vec[0][j] += evals_vec[i][j];\n      }\n    }\n    return {math::UnivariateEvaluations<F, MaxDegree>(std::move(evals_vec[0]))};\n  }\n\n  // Receive message from verifier and run a prover round.\n  //\n  // Referencing https://people.cs.georgetown.edu/jthaler/sumcheck.pdf\n  // v = the number of variables\n  // For each round i, the Prover creates and sends:\n  //   gᵢ(Xᵢ) = ∑ g(r₁,...,rᵢ₋₁,Xᵢ,xᵢ₊₁,...,xᵥ), (xᵢ₊₁,...,xᵥ)∈{0,1}ᵛ⁻¹\n  //\n  // The first section of |Round(SumcheckVerifierMsg)| fixes r₁,...,rᵢ₋₁.\n  // |Round()| subsequently creates gᵢ(Xᵢ).\n  //\n  // Note that the Prover's last round v will create:\n  //   gᵥ(Xᵥ) = g(r₁,...,rᵥ₋₁,Xᵥ)\n  SumcheckProverMsg<F, MaxDegree> Round(SumcheckVerifierMsg<F>&& v_msg) {\n    // First round should be prover\n    CHECK_GT(round_, size_t{0});\n    randomness_.push_back(v_msg.random_value);\n    Point point({std::move(v_msg.random_value)});\n    for (MLE& evaluations : flattened_ml_evaluations_) {\n      evaluations.FixVariablesInPlace(point);\n    }\n    return Round();\n  }\n\n private:\n  void EvaluateTermPerVariable(size_t j, std::vector<F>& products,\n                               const Term& term) const {\n    for (size_t index : term.indexes) {\n      const MLE& table = flattened_ml_evaluations_[index];\n      F start = table[j << 1];\n      const F step = table[(j << 1) + 1] - start;\n      // start, (start + step), (start + 2 * step), (start + 3 * step),...\n      for (F& p : products) {\n        p *= start;\n        start += step;\n      }\n    }\n  }\n\n  // The current round number.\n  size_t round_ = 0;\n  // Number of variables.\n  size_t num_variables_ = 0;\n  // Max number of evaluations in a term.\n  size_t max_evaluations_ = 0;\n  // Sampled set of random values given by the verifier.\n  std::vector<F> randomness_;\n  // Stores the list of terms that is meant to be added together. Each\n  // evaluation is represented by the index in |flattened_ml_evaluations_|.\n  std::vector<Term> terms_;\n  // Stores a list of multilinear evaluations in which |terms_| points to.\n  std::vector<MLE> flattened_ml_evaluations_;\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_SUMCHECK_MULTILINEAR_SUMCHECK_PROVER_H_\n"
  },
  {
    "path": "tachyon/crypto/sumcheck/multilinear/sumcheck_prover_msg.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_CRYPTO_SUMCHECK_MULTILINEAR_SUMCHECK_PROVER_MSG_H_\n#define TACHYON_CRYPTO_SUMCHECK_MULTILINEAR_SUMCHECK_PROVER_MSG_H_\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/buffer/copyable.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluations.h\"\n\nnamespace tachyon {\nnamespace crypto {\n\ntemplate <typename F, size_t MaxDegree>\nstruct SumcheckProverMsg {\n  // Evaluations on P(0), P(1), P(2), ...\n  math::UnivariateEvaluations<F, MaxDegree> evaluations;\n\n  bool operator==(const SumcheckProverMsg& other) const {\n    return evaluations == other.evaluations;\n  }\n  bool operator!=(const SumcheckProverMsg& other) const {\n    return !operator==(other);\n  }\n};\n\n}  // namespace crypto\n\nnamespace base {\n\ntemplate <typename F, size_t MaxDegree>\nclass Copyable<crypto::SumcheckProverMsg<F, MaxDegree>> {\n public:\n  static bool WriteTo(const crypto::SumcheckProverMsg<F, MaxDegree>& msg,\n                      Buffer* buffer) {\n    return buffer->Write(msg.evaluations);\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer,\n                       crypto::SumcheckProverMsg<F, MaxDegree>* msg) {\n    math::UnivariateEvaluations<F, MaxDegree> evaluations;\n    if (!buffer.Read(&evaluations)) return false;\n    *msg = {std::move(evaluations)};\n    return true;\n  }\n\n  static size_t EstimateSize(\n      const crypto::SumcheckProverMsg<F, MaxDegree>& msg) {\n    return base::EstimateSize(msg.evaluations);\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_CRYPTO_SUMCHECK_MULTILINEAR_SUMCHECK_PROVER_MSG_H_\n"
  },
  {
    "path": "tachyon/crypto/sumcheck/multilinear/sumcheck_prover_msg_unittest.cc",
    "content": "#include \"tachyon/crypto/sumcheck/multilinear/sumcheck_prover_msg.h\"\n\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/buffer/vector_buffer.h\"\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluations.h\"\n\nnamespace tachyon::crypto {\nnamespace {\n\nusing F = math::GF7;\n\nclass SumcheckProverMsgTest : public math::FiniteFieldTest<math::GF7> {};\n\n}  // namespace\n\nTEST_F(SumcheckProverMsgTest, Copyable) {\n  SumcheckProverMsg<F, 5> expected = {\n      math::UnivariateEvaluations<F, 5>::Random(5)};\n\n  base::Uint8VectorBuffer write_buf;\n  ASSERT_TRUE(write_buf.Grow(base::EstimateSize(expected)));\n  ASSERT_TRUE(write_buf.Write(expected));\n  ASSERT_TRUE(write_buf.Done());\n\n  write_buf.set_buffer_offset(0);\n\n  SumcheckProverMsg<F, 5> value;\n  ASSERT_TRUE(write_buf.Read(&value));\n  EXPECT_EQ(expected, value);\n}\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/crypto/sumcheck/multilinear/sumcheck_proving_key.h",
    "content": "#ifndef TACHYON_CRYPTO_SUMCHECK_MULTILINEAR_SUMCHECK_PROVING_KEY_H_\n#define TACHYON_CRYPTO_SUMCHECK_MULTILINEAR_SUMCHECK_PROVING_KEY_H_\n\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/buffer/copyable.h\"\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/crypto/sumcheck/multilinear/sumcheck_verifying_key.h\"\n\nnamespace tachyon {\nnamespace crypto {\n\ntemplate <typename MLE>\nstruct SumcheckProvingKey {\n  SumcheckVerifyingKey verifying_key;\n  std::vector<math::LinearCombinationTerm<typename MLE::Field>> terms;\n  std::vector<MLE> flattened_ml_evaluations;\n\n  static SumcheckProvingKey Build(\n      const math::LinearCombination<MLE>& linear_combination) {\n    std::vector<MLE> flattened_ml_evaluations =\n        base::Map(linear_combination.flattened_ml_evaluations(),\n                  [](const std::shared_ptr<MLE> ptr) { return *ptr.get(); });\n    return {SumcheckVerifyingKey::Build(linear_combination),\n            linear_combination.terms(), std::move(flattened_ml_evaluations)};\n  }\n\n  bool operator==(const SumcheckProvingKey& other) const {\n    return verifying_key == other.verifying_key && terms == other.terms &&\n           flattened_ml_evaluations == flattened_ml_evaluations;\n  }\n  bool operator!=(const SumcheckProvingKey& other) const {\n    return !operator==(other);\n  }\n};\n\n}  // namespace crypto\n\nnamespace base {\n\ntemplate <typename MLE>\nclass Copyable<crypto::SumcheckProvingKey<MLE>> {\n public:\n  static bool WriteTo(const crypto::SumcheckProvingKey<MLE>& proving_key,\n                      Buffer* buffer) {\n    return buffer->WriteMany(proving_key.verifying_key, proving_key.terms,\n                             proving_key.flattened_ml_evaluations);\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer,\n                       crypto::SumcheckProvingKey<MLE>* proving_key) {\n    crypto::SumcheckVerifyingKey verifying_key;\n    std::vector<math::LinearCombinationTerm<typename MLE::Field>> terms;\n    std::vector<MLE> flattened_ml_evaluations;\n    if (!buffer.ReadMany(&verifying_key, &terms, &flattened_ml_evaluations))\n      return false;\n    *proving_key = {std::move(verifying_key), std::move(terms),\n                    std::move(flattened_ml_evaluations)};\n    return true;\n  }\n\n  static size_t EstimateSize(\n      const crypto::SumcheckProvingKey<MLE>& proving_key) {\n    return base::EstimateSize(proving_key.verifying_key, proving_key.terms,\n                              proving_key.flattened_ml_evaluations);\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_CRYPTO_SUMCHECK_MULTILINEAR_SUMCHECK_PROVING_KEY_H_\n"
  },
  {
    "path": "tachyon/crypto/sumcheck/multilinear/sumcheck_proving_key_unittest.cc",
    "content": "#include \"tachyon/crypto/sumcheck/multilinear/sumcheck_proving_key.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/buffer/vector_buffer.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n#include \"tachyon/math/polynomials/multivariate/multilinear_dense_evaluations.h\"\n\nnamespace tachyon::crypto {\n\nnamespace {\n\nconst size_t kMaxDegree = 4;\n\nusing Poly = math::MultilinearDenseEvaluations<math::GF7, kMaxDegree>;\n\nclass SumcheckProvingKeyTest : public math::FiniteFieldTest<math::GF7> {};\n\n}  // namespace\n\nTEST_F(SumcheckProvingKeyTest, Copyable) {\n  math::LinearCombination<Poly> random_linear_combination =\n      math::LinearCombination<Poly>::Random(kMaxDegree,\n                                            base::Range<size_t>(6, 12), 7);\n  SumcheckProvingKey<Poly> expected =\n      SumcheckProvingKey<Poly>::Build(random_linear_combination);\n\n  base::Uint8VectorBuffer write_buf;\n  ASSERT_TRUE(write_buf.Grow(base::EstimateSize(expected)));\n  ASSERT_TRUE(write_buf.Write(expected));\n  ASSERT_TRUE(write_buf.Done());\n\n  write_buf.set_buffer_offset(0);\n\n  SumcheckProvingKey<Poly> value;\n  ASSERT_TRUE(write_buf.Read(&value));\n  EXPECT_EQ(expected, value);\n}\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/crypto/sumcheck/multilinear/sumcheck_verifier.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_CRYPTO_SUMCHECK_MULTILINEAR_SUMCHECK_VERIFIER_H_\n#define TACHYON_CRYPTO_SUMCHECK_MULTILINEAR_SUMCHECK_VERIFIER_H_\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/crypto/sumcheck/multilinear/sumcheck_prover_msg.h\"\n#include \"tachyon/crypto/sumcheck/multilinear/sumcheck_verifier_msg.h\"\n#include \"tachyon/crypto/sumcheck/multilinear/sumcheck_verifying_key.h\"\n\nnamespace tachyon::crypto {\n\ntemplate <typename F>\nF InterpolateUniPoly(const std::vector<F>& poly, const F& evaluation_point);\n\n// Subclaim created when verifier is convinced.\ntemplate <typename F>\nstruct Subclaim {\n  // The multi-dimensional point this multilinear evaluation is evaluated at.\n  std::vector<F> point;\n  // The expected evaluation.\n  F expected_evaluation;\n};\n\ntemplate <typename MLE>\nclass SumcheckVerifier {\n public:\n  using F = typename MLE::Field;\n\n  SumcheckVerifier() = default;\n  explicit SumcheckVerifier(const SumcheckVerifyingKey& key)\n      : num_variables_(key.num_variables),\n        max_evaluations_(key.max_evaluations) {\n    randomness_.reserve(key.num_variables);\n    polynomials_received_.reserve(key.num_variables);\n  }\n  explicit SumcheckVerifier(SumcheckVerifyingKey&& key)\n      : num_variables_(key.num_variables),\n        max_evaluations_(key.max_evaluations) {\n    randomness_.reserve(key.num_variables);\n    polynomials_received_.reserve(key.num_variables);\n  }\n\n  // Runs a \"round\" given a |SumcheckProverMsg|, sampling and storing a random\n  // value.\n  //\n  // While a true verifier round should perform actual verification, |Round()|\n  // merely samples and stores random values. Verifications will be performed\n  // altogether in |CheckAndGenerateSubclaim| after all prover and verifier\n  // sub-rounds are subsequently finished.\n  //\n  // Referencing https://people.cs.georgetown.edu/jthaler/sumcheck.pdf\n  // |random_value| is the challenge that will given from the Verifier\n  // to the Prover aka rᵢ.\n  template <size_t MaxDegree>\n  SumcheckVerifierMsg<F> Round(SumcheckProverMsg<F, MaxDegree>&& prover_msg) {\n    CHECK_LT(polynomials_received_.size(), num_variables_);\n\n    const F random_value = F::Random();\n    randomness_.push_back(random_value);\n    polynomials_received_.push_back(\n        std::move(prover_msg.evaluations.evaluations()));\n\n    return {std::move(random_value)};\n  }\n\n  // Verifies the sumcheck phases' validity and generates a subclaim.\n  //\n  // If the asserted sum is correct, then the multilinear polynomial evaluated\n  // at |subclaim.point| is |subclaim.expected_evaluation|. Otherwise, it is\n  // highly unlikely that these two will be equal. A larger field size\n  // guarantees a smaller soundness error.\n  //\n  // Referencing https://people.cs.georgetown.edu/jthaler/sumcheck.pdf\n  // |CheckAndGenerateSubclaim()| verifies for each round i:\n  //   gᵢ(0) + gᵢ(1) =  gᵢ₋₁(rᵢ₋₁)\n  // Note: For the first round, gᵢ₋₁(rᵢ₋₁) = H = |LinearCombination.Combine()|\n  //   Meanwhile, for the last round, gᵢ₋₁(rᵢ₋₁) = the |LinearCombination|\n  //   evaluated on the multivariate point of Verifier's challenges.\n  bool CheckAndGenerateSubclaim(const F& asserted_sum, Subclaim<F>* subclaim) {\n    // Insufficient rounds.\n    CHECK_EQ(polynomials_received_.size(), num_variables_);\n    F expected = asserted_sum;\n\n    for (size_t i = 0; i < num_variables_; ++i) {\n      const std::vector<F>& evaluations = polynomials_received_[i];\n      // Incorrect number of evaluations.\n      CHECK_EQ(evaluations.size(), max_evaluations_ + 1);\n\n      const F& p0 = evaluations[0];\n      const F& p1 = evaluations[1];\n\n      F sum = p0 + p1;\n\n      // Check that prover message is consistent with the claim.\n      if (sum != expected) {\n        return false;\n      }\n      expected = InterpolateUniPoly(evaluations, randomness_[i]);\n    }\n    *subclaim = {std::move(randomness_), std::move(expected)};\n    return true;\n  }\n\n private:\n  size_t num_variables_ = 0;\n  size_t max_evaluations_ = 0;\n  // A list storing the randomness sampled by the verifier at each round.\n  std::vector<F> randomness_;\n  // A list storing the univariate polynomial in evaluation form sent by the\n  // prover at each round.\n  std::vector<std::vector<F>> polynomials_received_;\n};\n\n// Interpolates the unique univariate polynomial |poly| of degree at most\n// |poly.size()| - 1 = |poly_size| passing through the y-values in |poly| at\n// x = 0,..., |poly[i].size()| - 1 and evaluates this polynomial at\n// |evaluation_point|:\n//   ∑ poly[i] * ∏ⱼ≠ᵢ(|evaluation_point| - j)/(i - j), (i = 0,1,...,|poly_size|)\ntemplate <typename F>\nF InterpolateUniPoly(const std::vector<F>& poly, const F& evaluation_point) {\n  constexpr size_t kParallelFactor = 16;\n\n  using BigInt = typename F::BigIntTy;\n\n  // clang-format off\n  // Calculating iteration i on X = |evaluation_point| is done like so:\n  //\n  //                             (|product| / |evals[i]|)\n  //                                        |\n  //                          |‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾|\n  // iteration i =  poly[i] * ∏ⱼ≠ᵢ(|evaluation_point| - j)/(i - j), (i = 0,1,...,|poly_size|)\n  //                         |___|------------------------|______|\n  //                                                         |\n  //                                                     denom[i]\n  //\n  // More specifically...\n  //\n  //                                       |product|\n  //                                           |\n  //                         |‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾|\n  //                         (X - 0)(X - 1)(X - 2)...(X - poly_size)\n  // iteration i = poly[i] * ―――――――――――――――――――――――――――――――――――――――\n  //                                   (X - i) * denom[i]\n  //                                  |______|\n  //                                     |\n  //                                |evals[i]|\n  //\n  // Where...\n  //                                                  |denom_up|\n  //                                                      |\n  //             |‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾|\n  //            (|poly_size| - 1)! * ∏ -|offset_up|, (|offset_up| = 1,2,...,|poly_size| - i - 1)\n  // denom[i] = ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――\n  //                     ∏ |offset_down|, (|offset_down| = |poly_size| - 1,...,i + 1)\n  //                    |___________________________________________________________|\n  //                                                  |\n  //                                            |denom_down|\n  //\n  // More information on denom is given later.\n  // clang-format on\n\n  // Return early if 0 ≤ |evaluation_point| < |poly_size|, i.e. if the desired\n  // value is already calculated (in |poly|)\n  size_t poly_size = poly.size();\n  CHECK_NE(poly_size, size_t{0});\n\n  const BigInt test = evaluation_point.ToBigInt();\n  if (test < BigInt(poly_size)) {\n    return poly[test.smallest_limb()];\n  }\n\n  // |product| = ∏ⱼ(|evaluation_point| - j)\n#if defined(TACHYON_HAS_OPENMP)\n  size_t thread_nums = static_cast<size_t>(omp_get_max_threads());\n#else\n  size_t thread_nums = 1;\n#endif\n\n  struct Result {\n    F product;\n    F denom_up;\n    std::vector<F> evals;\n  };\n\n  F product;\n  F denom_up;\n  std::vector<F> evals;\n  {\n    std::vector<Result> results = base::ParallelizeMap(\n        poly_size,\n        [&evaluation_point](size_t len, size_t chunk_offset,\n                            size_t chunk_size) {\n          size_t begin = chunk_offset * chunk_size;\n          Result result;\n          result.product = F::One();\n          result.denom_up = F::One();\n          result.evals.reserve(len);\n          F check = F(begin);\n          for (size_t i = begin; i < begin + len; ++i) {\n            F difference = evaluation_point - check;\n            result.product *= difference;\n            result.evals.push_back(std::move(difference));\n            if (i > 1) {\n              result.denom_up *= check;\n            }\n            check += F::One();\n          }\n          return result;\n        },\n        kParallelFactor * thread_nums);\n    product = std::move(results[0].product);\n    denom_up = std::move(results[0].denom_up);\n    size_t size = std::accumulate(results.begin(), results.end(), 0,\n                                  [](size_t acc, const Result& result) {\n                                    return acc + result.evals.size();\n                                  });\n    evals = std::move(results[0].evals);\n    evals.reserve(size);\n    for (size_t i = 1; i < results.size(); ++i) {\n      evals.insert(evals.end(), results[i].evals.begin(),\n                   results[i].evals.end());\n      product *= results[i].product;\n      denom_up *= results[i].denom_up;\n    }\n  }\n\n  // Computing denom[i] = ∏ⱼ≠ᵢ(i - j) for a given i:\n  //\n  // clang-format off\n  // Start from the last step:\n  //  denom[poly_size - 1] = (poly_size - 1) * (poly_size - 2) *... * 2 * 1\n  //  aka:\n  //   denom[poly_size - 1] = (poly_size - 1)!\n  // The step before that is\n  //  denom[poly_size - 2] = (poly_size - 2) * (poly_size - 3) * ... * 2 * 1 * -1\n  // and the step before that is\n  //  denom[poly_size - 3] = (poly_size - 3) * (poly_size - 4) * ... * 2 * 1 * -1 *-2\n  // clang-format on\n  //\n  // i.e., for any i, the one before this will be derived from\n  //  denom[i - 1] = - denom[i] * (|poly_size| - i) / i\n  // aka:\n  //  denom[i] = (|poly_size| - 1)! * ∏ -|offset_up|, (|offset_up| =\n  //  1,2,...,|poly_size| - i - 1)\n  //\n  // denom is stored as a fraction number to reduce field divisions.\n  // TODO(chokobole): This should be parallelized depending on |poly_size| like\n  // above.\n  F res = F::Zero();\n  F offset_up = F::One();\n\n  for (size_t i = poly_size - 1; i < SIZE_MAX; --i) {\n    evals[i] *= denom_up;\n    if (i != 0) {\n      denom_up *= -offset_up;\n      offset_up += F::One();\n    }\n  }\n  CHECK(F::BatchInverseInPlace(evals));\n  F denom_down = F::One();\n  F offset_down = F(poly_size - 1);\n  for (size_t i = poly_size - 1; i < SIZE_MAX; --i) {\n    res += poly[i] * product * denom_down * evals[i];\n    if (i != 0) {\n      denom_down *= offset_down;\n      offset_down -= F::One();\n    }\n  }\n  return res;\n}\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_SUMCHECK_MULTILINEAR_SUMCHECK_VERIFIER_H_\n"
  },
  {
    "path": "tachyon/crypto/sumcheck/multilinear/sumcheck_verifier_msg.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_CRYPTO_SUMCHECK_MULTILINEAR_SUMCHECK_VERIFIER_MSG_H_\n#define TACHYON_CRYPTO_SUMCHECK_MULTILINEAR_SUMCHECK_VERIFIER_MSG_H_\n\n#include <utility>\n\n#include \"tachyon/base/buffer/copyable.h\"\n\nnamespace tachyon {\nnamespace crypto {\n\ntemplate <typename F>\nstruct SumcheckVerifierMsg {\n  // Random value sampled by verifier.\n  F random_value;\n\n  bool operator==(const SumcheckVerifierMsg& other) const {\n    return random_value == other.random_value;\n  }\n  bool operator!=(const SumcheckVerifierMsg& other) const {\n    return !operator==(other);\n  }\n};\n\n}  // namespace crypto\n\nnamespace base {\n\ntemplate <typename F>\nclass Copyable<crypto::SumcheckVerifierMsg<F>> {\n public:\n  static bool WriteTo(const crypto::SumcheckVerifierMsg<F>& msg,\n                      Buffer* buffer) {\n    return buffer->Write(msg.random_value);\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer,\n                       crypto::SumcheckVerifierMsg<F>* msg) {\n    F random_value;\n    if (!buffer.Read(&random_value)) return false;\n    *msg = {std::move(random_value)};\n    return true;\n  }\n\n  static size_t EstimateSize(const crypto::SumcheckVerifierMsg<F>& msg) {\n    return base::EstimateSize(msg.random_value);\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_CRYPTO_SUMCHECK_MULTILINEAR_SUMCHECK_VERIFIER_MSG_H_\n"
  },
  {
    "path": "tachyon/crypto/sumcheck/multilinear/sumcheck_verifier_msg_unittest.cc",
    "content": "#include \"tachyon/crypto/sumcheck/multilinear/sumcheck_verifier_msg.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/buffer/vector_buffer.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n\nnamespace tachyon::crypto {\nnamespace {\n\nusing F = math::GF7;\n\nclass SumcheckVerifierMsgTest : public math::FiniteFieldTest<math::GF7> {};\n\n}  // namespace\n\nTEST_F(SumcheckVerifierMsgTest, Copyable) {\n  SumcheckVerifierMsg<F> expected = {F::Random()};\n\n  base::Uint8VectorBuffer write_buf;\n  ASSERT_TRUE(write_buf.Grow(base::EstimateSize(expected)));\n  ASSERT_TRUE(write_buf.Write(expected));\n  ASSERT_TRUE(write_buf.Done());\n\n  write_buf.set_buffer_offset(0);\n\n  SumcheckVerifierMsg<F> value;\n  ASSERT_TRUE(write_buf.Read(&value));\n  EXPECT_EQ(expected, value);\n}\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/crypto/sumcheck/multilinear/sumcheck_verifying_key.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_CRYPTO_SUMCHECK_MULTILINEAR_SUMCHECK_VERIFYING_KEY_H_\n#define TACHYON_CRYPTO_SUMCHECK_MULTILINEAR_SUMCHECK_VERIFYING_KEY_H_\n\n#include \"tachyon/base/buffer/copyable.h\"\n#include \"tachyon/base/random.h\"\n#include \"tachyon/math/polynomials/multivariate/linear_combination.h\"\n\nnamespace tachyon {\nnamespace crypto {\n\nstruct TACHYON_EXPORT SumcheckVerifyingKey {\n  size_t max_evaluations;\n  size_t num_variables;\n\n  template <typename MLE>\n  static SumcheckVerifyingKey Build(\n      const math::LinearCombination<MLE>& linear_combination) {\n    return {linear_combination.max_evaluations(),\n            linear_combination.num_variables()};\n  }\n\n  bool operator==(const SumcheckVerifyingKey& other) const {\n    return max_evaluations == other.max_evaluations &&\n           num_variables == other.num_variables;\n  }\n  bool operator!=(const SumcheckVerifyingKey& other) const {\n    return !operator==(other);\n  }\n\n  static SumcheckVerifyingKey Random() {\n    return {base::Uniform(base::Range<size_t>()),\n            base::Uniform(base::Range<size_t>())};\n  }\n};\n\n}  // namespace crypto\n\nnamespace base {\n\ntemplate <>\nclass Copyable<crypto::SumcheckVerifyingKey> {\n public:\n  static bool WriteTo(const crypto::SumcheckVerifyingKey& verifying_key,\n                      Buffer* buffer) {\n    return buffer->WriteMany(verifying_key.max_evaluations,\n                             verifying_key.num_variables);\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer,\n                       crypto::SumcheckVerifyingKey* verifying_key) {\n    size_t max_evaluations;\n    size_t num_variables;\n    if (!buffer.ReadMany(&max_evaluations, &num_variables)) return false;\n    *verifying_key = {max_evaluations, num_variables};\n    return true;\n  }\n\n  static size_t EstimateSize(\n      const crypto::SumcheckVerifyingKey& verifying_key) {\n    return base::EstimateSize(verifying_key.max_evaluations,\n                              verifying_key.num_variables);\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_CRYPTO_SUMCHECK_MULTILINEAR_SUMCHECK_VERIFYING_KEY_H_\n"
  },
  {
    "path": "tachyon/crypto/sumcheck/multilinear/sumcheck_verifying_key_unittest.cc",
    "content": "#include \"tachyon/crypto/sumcheck/multilinear/sumcheck_verifying_key.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/buffer/vector_buffer.h\"\n\nnamespace tachyon::crypto {\n\nTEST(SumcheckVerifyingKeyTest, Copyable) {\n  SumcheckVerifyingKey expected = SumcheckVerifyingKey::Random();\n\n  base::Uint8VectorBuffer write_buf;\n  ASSERT_TRUE(write_buf.Grow(base::EstimateSize(expected)));\n  ASSERT_TRUE(write_buf.Write(expected));\n  ASSERT_TRUE(write_buf.Done());\n\n  write_buf.set_buffer_offset(0);\n\n  SumcheckVerifyingKey value;\n  ASSERT_TRUE(write_buf.Read(&value));\n  EXPECT_EQ(expected, value);\n}\n\n}  // namespace tachyon::crypto\n"
  },
  {
    "path": "tachyon/crypto/transcripts/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"simple_transcript\",\n    testonly = True,\n    hdrs = [\"simple_transcript.h\"],\n    deps = [\":transcript\"],\n)\n\ntachyon_cc_library(\n    name = \"transcript\",\n    hdrs = [\"transcript.h\"],\n    deps = [\n        \":transcript_traits\",\n        \"//tachyon/base/buffer:vector_buffer\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"transcript_traits\",\n    hdrs = [\"transcript_traits.h\"],\n    deps = [\n        \"//tachyon/math/finite_fields:prime_field_base\",\n        \"//tachyon/math/geometry:affine_point\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/crypto/transcripts/simple_transcript.h",
    "content": "#ifndef TACHYON_CRYPTO_TRANSCRIPTS_SIMPLE_TRANSCRIPT_H_\n#define TACHYON_CRYPTO_TRANSCRIPTS_SIMPLE_TRANSCRIPT_H_\n\n#include <utility>\n\n#include \"tachyon/crypto/transcripts/transcript.h\"\n\nnamespace tachyon::crypto {\nnamespace internal {\n\ntemplate <typename F, typename SFINAE = void>\nclass SimpleTranscript;\n\ntemplate <typename F>\nclass SimpleTranscript<\n    F, std::enable_if_t<std::is_base_of_v<math::PrimeFieldBase<F>, F>>> {\n protected:\n  F DoSqueezeChallenge() { return state_.DoubleInPlace(); }\n\n  bool DoWriteToTranscript(const F& value) {\n    state_ += value;\n    return true;\n  }\n\n private:\n  F state_ = F::Zero();\n};\n\ntemplate <typename Curve>\nclass SimpleTranscript<math::AffinePoint<Curve>> {\n protected:\n  using F = typename TranscriptTraits<math::AffinePoint<Curve>>::Field;\n  using CurveConfig = typename Curve::Config;\n\n  F DoSqueezeChallenge() { return state_.DoubleInPlace(); }\n\n  bool DoWriteToTranscript(const math::AffinePoint<Curve>& point) {\n    state_ += BaseToScalar(point.x().value());\n    state_ += BaseToScalar(point.y().value());\n    return true;\n  }\n\n  bool DoWriteToTranscript(const F& scalar) {\n    state_ += scalar;\n    return true;\n  }\n\n private:\n  template <typename BigInt>\n  F BaseToScalar(const BigInt& base) {\n    return F::FromMontgomery(base % F::Config::kModulus);\n  }\n\n  F state_ = F::Zero();\n};\n\n}  // namespace internal\n\ntemplate <typename F, typename SFINAE = void>\nclass SimpleTranscriptReader;\n\ntemplate <typename F>\nclass SimpleTranscriptReader<\n    F, std::enable_if_t<std::is_base_of_v<math::PrimeFieldBase<F>, F>>>\n    : public TranscriptReader<F>, protected internal::SimpleTranscript<F> {\n public:\n  explicit SimpleTranscriptReader(base::Buffer read_buf)\n      : TranscriptReader<F>(std::move(read_buf)) {}\n\n  // TranscriptReader methods\n  F SqueezeChallenge() override { return this->DoSqueezeChallenge(); }\n\n  bool WriteToTranscript(const F& value) override {\n    return this->DoWriteToTranscript(value);\n  }\n\n private:\n  bool DoReadFromProof(F* value) const override {\n    return this->buffer_.Read(value);\n  }\n};\n\ntemplate <typename Curve>\nclass SimpleTranscriptReader<math::AffinePoint<Curve>>\n    : public TranscriptReader<math::AffinePoint<Curve>>,\n      protected internal::SimpleTranscript<math::AffinePoint<Curve>> {\n public:\n  using F = typename TranscriptTraits<math::AffinePoint<Curve>>::Field;\n\n  explicit SimpleTranscriptReader(base::Buffer read_buf)\n      : TranscriptReader<math::AffinePoint<Curve>>(std::move(read_buf)) {}\n\n  // TranscriptReader methods\n  F SqueezeChallenge() override { return this->DoSqueezeChallenge(); }\n\n  bool WriteToTranscript(const math::AffinePoint<Curve>& point) override {\n    return this->DoWriteToTranscript(point);\n  }\n\n  bool WriteToTranscript(const F& scalar) override {\n    return this->DoWriteToTranscript(scalar);\n  }\n\n private:\n  bool DoReadFromProof(math::AffinePoint<Curve>* point) const override {\n    return this->buffer_.Read(point);\n  }\n\n  bool DoReadFromProof(F* scalar) const override {\n    return this->buffer_.Read(scalar);\n  }\n};\n\ntemplate <typename F, typename SFINAE = void>\nclass SimpleTranscriptWriter;\n\ntemplate <typename F>\nclass SimpleTranscriptWriter<\n    F, std::enable_if_t<std::is_base_of_v<math::PrimeFieldBase<F>, F>>>\n    : public TranscriptWriter<F>, protected internal::SimpleTranscript<F> {\n public:\n  explicit SimpleTranscriptWriter(base::Uint8VectorBuffer write_buf)\n      : TranscriptWriter<F>(std::move(write_buf)) {}\n\n  // TranscriptWriter methods\n  F SqueezeChallenge() override { return this->DoSqueezeChallenge(); }\n\n  bool WriteToTranscript(const F& value) override {\n    return this->DoWriteToTranscript(value);\n  }\n\n private:\n  bool DoWriteToProof(const F& value) override {\n    return this->buffer_.Write(value);\n  }\n};\n\ntemplate <typename Curve>\nclass SimpleTranscriptWriter<math::AffinePoint<Curve>>\n    : public TranscriptWriter<math::AffinePoint<Curve>>,\n      protected internal::SimpleTranscript<math::AffinePoint<Curve>> {\n public:\n  using F = typename TranscriptTraits<math::AffinePoint<Curve>>::Field;\n\n  explicit SimpleTranscriptWriter(base::Uint8VectorBuffer write_buf)\n      : TranscriptWriter<math::AffinePoint<Curve>>(std::move(write_buf)) {}\n\n  // TranscriptWriter methods\n  F SqueezeChallenge() override { return this->DoSqueezeChallenge(); }\n\n  bool WriteToTranscript(const math::AffinePoint<Curve>& point) override {\n    return this->DoWriteToTranscript(point);\n  }\n\n  bool WriteToTranscript(const F& scalar) override {\n    return this->DoWriteToTranscript(scalar);\n  }\n\n private:\n  bool DoWriteToProof(const math::AffinePoint<Curve>& point) override {\n    return this->buffer_.Write(point);\n  }\n\n  bool DoWriteToProof(const F& scalar) override {\n    return this->buffer_.Write(scalar);\n  }\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_TRANSCRIPTS_SIMPLE_TRANSCRIPT_H_\n"
  },
  {
    "path": "tachyon/crypto/transcripts/transcript.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_CRYPTO_TRANSCRIPTS_TRANSCRIPT_H_\n#define TACHYON_CRYPTO_TRANSCRIPTS_TRANSCRIPT_H_\n\n#include <utility>\n\n#include \"tachyon/base/buffer/vector_buffer.h\"\n#include \"tachyon/crypto/transcripts/transcript_traits.h\"\n\nnamespace tachyon::crypto {\n\ntemplate <typename Commitment, bool FieldAndCommitmentAreSameType>\nclass TranscriptReaderImpl;\n\ntemplate <typename Commitment, bool FieldAndCommitmentAreSameType>\nclass TranscriptWriterImpl;\n\ntemplate <typename _Commitment, bool FieldAndCommitmentAreSameType>\nclass TranscriptImpl;\n\n// Generic transcript view (from either the prover or verifier's perspective)\ntemplate <typename _Commitment>\nclass TranscriptImpl<_Commitment, false> {\n public:\n  using Commitment = _Commitment;\n  using Field = typename TranscriptTraits<Commitment>::Field;\n\n  virtual ~TranscriptImpl() = default;\n\n  // Squeeze an encoded verifier challenge from the transcript.\n  virtual Field SqueezeChallenge() = 0;\n\n  // Write a |commitment| to the transcript without writing it to the proof,\n  // treating it as a common input.\n  [[nodiscard]] virtual bool WriteToTranscript(\n      const Commitment& commitment) = 0;\n\n  // Write a |value| to the transcript without writing it to the proof,\n  // treating it as a common input.\n  [[nodiscard]] virtual bool WriteToTranscript(const Field& value) = 0;\n\n  TranscriptWriterImpl<Commitment, false>* ToWriter() {\n    return static_cast<TranscriptWriterImpl<Commitment, false>*>(this);\n  }\n\n  const TranscriptWriterImpl<Commitment, false>* ToWriter() const {\n    return static_cast<const TranscriptWriterImpl<Commitment, false>*>(this);\n  }\n\n  TranscriptReaderImpl<Commitment, false>* ToReader() {\n    return static_cast<TranscriptReaderImpl<Commitment, false>*>(this);\n  }\n\n  const TranscriptReaderImpl<Commitment, false>* ToReader() const {\n    return static_cast<const TranscriptReaderImpl<Commitment, false>*>(this);\n  }\n};\n\ntemplate <typename _Field>\nclass TranscriptImpl<_Field, true> {\n public:\n  using Field = _Field;\n\n  virtual ~TranscriptImpl() = default;\n\n  // Squeeze an encoded verifier challenge from the transcript.\n  virtual Field SqueezeChallenge() = 0;\n\n  // Write a |value| to the transcript without writing it to the proof,\n  // treating it as a common input.\n  [[nodiscard]] virtual bool WriteToTranscript(const Field& value) = 0;\n\n  TranscriptWriterImpl<Field, true>* ToWriter() {\n    return static_cast<TranscriptWriterImpl<Field, true>*>(this);\n  }\n\n  const TranscriptWriterImpl<Field, true>* ToWriter() const {\n    return static_cast<const TranscriptWriterImpl<Field, true>*>(this);\n  }\n\n  TranscriptReaderImpl<Field, true>* ToReader() {\n    return static_cast<TranscriptReaderImpl<Field, true>*>(this);\n  }\n\n  const TranscriptReaderImpl<Field, true>* ToReader() const {\n    return static_cast<const TranscriptReaderImpl<Field, true>*>(this);\n  }\n};\n\ntemplate <typename T>\nusing Transcript =\n    TranscriptImpl<T, TranscriptTraits<T>::kFieldAndCommitmentAreSameType>;\n\n// Transcript view from the perspective of a verifier that has access to an\n// input stream of data from the prover to the verifier.\ntemplate <typename Commitment>\nclass TranscriptReaderImpl<Commitment, false> : public Transcript<Commitment> {\n public:\n  using Field = typename TranscriptTraits<Commitment>::Field;\n\n  TranscriptReaderImpl() = default;\n  // Initialize a transcript given an input buffer.\n  explicit TranscriptReaderImpl(base::ReadOnlyBuffer read_buf)\n      : buffer_(std::move(read_buf)) {}\n\n  base::ReadOnlyBuffer& buffer() { return buffer_; }\n  const base::ReadOnlyBuffer& buffer() const { return buffer_; }\n\n  // Read a |commitment| from the proof. Note that it also writes the\n  // |commitment| to the transcript by calling |WriteToTranscript()| internally.\n  [[nodiscard]] bool ReadFromProof(Commitment* commitment) {\n    if (!DoReadFromProof(commitment)) return false;\n    VLOG(3) << \"Proof[\" << proof_idx_++\n            << \"]: \" << commitment->ToHexString(true);\n    return this->WriteToTranscript(*commitment);\n  }\n\n  // Read a |value| from the proof. Note that it also writes the\n  // |value| to the transcript by calling |WriteToTranscript()| internally.\n  [[nodiscard]] bool ReadFromProof(Field* value) {\n    if (!DoReadFromProof(value)) return false;\n    VLOG(3) << \"Proof[\" << proof_idx_++ << \"]: \" << value->ToHexString(true);\n    return this->WriteToTranscript(*value);\n  }\n\n protected:\n  //  Read a |commitment| from the proof.\n  [[nodiscard]] virtual bool DoReadFromProof(Commitment* commitment) const = 0;\n\n  //  Read a |value| from the proof.\n  [[nodiscard]] virtual bool DoReadFromProof(Field* value) const = 0;\n\n  base::ReadOnlyBuffer buffer_;\n  size_t proof_idx_ = 0;\n};\n\ntemplate <typename Field>\nclass TranscriptReaderImpl<Field, true> : public Transcript<Field> {\n public:\n  TranscriptReaderImpl() = default;\n  // Initialize a transcript given an input buffer.\n  explicit TranscriptReaderImpl(base::ReadOnlyBuffer read_buf)\n      : buffer_(std::move(read_buf)) {}\n\n  base::ReadOnlyBuffer& buffer() { return buffer_; }\n  const base::ReadOnlyBuffer& buffer() const { return buffer_; }\n\n  // Read a |value| from the proof. Note that it also writes the\n  // |value| to the transcript by calling |WriteToTranscript()| internally.\n  [[nodiscard]] bool ReadFromProof(Field* value) {\n    if (!DoReadFromProof(value)) return false;\n    VLOG(3) << \"Proof[\" << proof_idx_++ << \"]: \" << value->ToHexString(true);\n    return this->WriteToTranscript(*value);\n  }\n\n protected:\n  //  Read a |value| from the proof.\n  [[nodiscard]] virtual bool DoReadFromProof(Field* value) const = 0;\n\n  base::ReadOnlyBuffer buffer_;\n  size_t proof_idx_ = 0;\n};\n\ntemplate <typename T>\nusing TranscriptReader =\n    TranscriptReaderImpl<T,\n                         TranscriptTraits<T>::kFieldAndCommitmentAreSameType>;\n\n// Transcript view from the perspective of a prover that has access to an output\n// stream of messages from the prover to the verifier.\ntemplate <typename Commitment>\nclass TranscriptWriterImpl<Commitment, false> : public Transcript<Commitment> {\n public:\n  using Field = typename TranscriptTraits<Commitment>::Field;\n\n  TranscriptWriterImpl() = default;\n  // Initialize a transcript given an output buffer.\n  explicit TranscriptWriterImpl(base::Uint8VectorBuffer buf)\n      : buffer_(std::move(buf)) {}\n\n  base::Uint8VectorBuffer& buffer() { return buffer_; }\n  const base::Uint8VectorBuffer& buffer() const { return buffer_; }\n\n  base::Uint8VectorBuffer&& TakeBuffer() && { return std::move(buffer_); }\n\n  // Write a |commitment| to the proof. Note that it also writes the\n  // |commitment| to the transcript by calling |WriteToTranscript()| internally.\n  [[nodiscard]] bool WriteToProof(const Commitment& commitment) {\n    VLOG(3) << \"Proof[\" << proof_idx_++\n            << \"]: \" << commitment.ToHexString(true);\n    return this->WriteToTranscript(commitment) && DoWriteToProof(commitment);\n  }\n\n  // Write a |value| to the proof. Note that it also writes the\n  // |value| to the transcript by calling |WriteToTranscript()| internally.\n  [[nodiscard]] bool WriteToProof(const Field& value) {\n    VLOG(3) << \"Proof[\" << proof_idx_++ << \"]: \" << value.ToHexString(true);\n    return this->WriteToTranscript(value) && DoWriteToProof(value);\n  }\n\n protected:\n  //  Write a |commitment| to the proof.\n  [[nodiscard]] virtual bool DoWriteToProof(const Commitment& commitment) = 0;\n\n  //  Write a |value| to the proof.\n  [[nodiscard]] virtual bool DoWriteToProof(const Field& value) = 0;\n\n  base::Uint8VectorBuffer buffer_;\n  size_t proof_idx_ = 0;\n};\n\n// Transcript view from the perspective of a prover that has access to an output\n// stream of messages from the prover to the verifier.\ntemplate <typename Field>\nclass TranscriptWriterImpl<Field, true> : public Transcript<Field> {\n public:\n  TranscriptWriterImpl() = default;\n  // Initialize a transcript given an output buffer.\n  explicit TranscriptWriterImpl(base::Uint8VectorBuffer buf)\n      : buffer_(std::move(buf)) {}\n\n  base::Uint8VectorBuffer& buffer() { return buffer_; }\n  const base::Uint8VectorBuffer& buffer() const { return buffer_; }\n\n  base::Uint8VectorBuffer&& TakeBuffer() && { return std::move(buffer_); }\n\n  // Write a |value| to the proof. Note that it also writes the\n  // |value| to the transcript by calling |WriteToTranscript()| internally.\n  [[nodiscard]] bool WriteToProof(const Field& value) {\n    VLOG(3) << \"Proof[\" << proof_idx_++ << \"]: \" << value.ToHexString(true);\n    return this->WriteToTranscript(value) && DoWriteToProof(value);\n  }\n\n protected:\n  //  Write a |value| to the proof.\n  [[nodiscard]] virtual bool DoWriteToProof(const Field& value) = 0;\n\n  base::Uint8VectorBuffer buffer_;\n  size_t proof_idx_ = 0;\n};\n\ntemplate <typename T>\nusing TranscriptWriter =\n    TranscriptWriterImpl<T,\n                         TranscriptTraits<T>::kFieldAndCommitmentAreSameType>;\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_TRANSCRIPTS_TRANSCRIPT_H_\n"
  },
  {
    "path": "tachyon/crypto/transcripts/transcript_traits.h",
    "content": "#ifndef TACHYON_CRYPTO_TRANSCRIPTS_TRANSCRIPT_TRAITS_H_\n#define TACHYON_CRYPTO_TRANSCRIPTS_TRANSCRIPT_TRAITS_H_\n\n#include <type_traits>\n\n#include \"tachyon/math/finite_fields/prime_field_base.h\"\n#include \"tachyon/math/geometry/affine_point.h\"\n\nnamespace tachyon::crypto {\n\ntemplate <typename T, typename SFINAE = void>\nstruct TranscriptTraits;\n\ntemplate <typename Curve>\nstruct TranscriptTraits<math::AffinePoint<Curve>> {\n  constexpr static bool kFieldAndCommitmentAreSameType = false;\n\n  using Field = typename math::AffinePoint<Curve>::ScalarField;\n};\n\ntemplate <typename F>\nstruct TranscriptTraits<\n    F, std::enable_if_t<std::is_base_of_v<math::PrimeFieldBase<F>, F>>> {\n  constexpr static bool kFieldAndCommitmentAreSameType = true;\n\n  using Field = F;\n};\n\n}  // namespace tachyon::crypto\n\n#endif  // TACHYON_CRYPTO_TRANSCRIPTS_TRANSCRIPT_TRAITS_H_\n"
  },
  {
    "path": "tachyon/device/BUILD.bazel",
    "content": "load(\"//bazel:tachyon.bzl\", \"if_has_numa\")\nload(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"allocator\",\n    srcs = [\n        \"allocator.cc\",\n        \"allocator_registry.cc\",\n        \"tracking_allocator.cc\",\n    ],\n    hdrs = [\n        \"allocator.h\",\n        \"allocator_registry.h\",\n        \"tracking_allocator.h\",\n    ],\n    deps = [\n        \":numa\",\n        \"//tachyon/base:no_destructor\",\n        \"//tachyon/base/time\",\n        \"@com_google_absl//absl/base:core_headers\",\n        \"@com_google_absl//absl/container:flat_hash_map\",\n        \"@com_google_absl//absl/container:inlined_vector\",\n        \"@com_google_absl//absl/synchronization\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"numa\",\n    srcs = [\"numa.cc\"],\n    hdrs = [\"numa.h\"],\n    deps = [\n        \"//tachyon/base:logging\",\n        \"//tachyon/base/memory:aligned_memory\",\n        \"//tachyon/build:build_config\",\n    ] + if_has_numa([\"@hwloc\"]),\n)\n\ntachyon_cc_unittest(\n    name = \"device_unittests\",\n    srcs = [\"numa_unittest.cc\"],\n    deps = [\":numa\"],\n)\n"
  },
  {
    "path": "tachyon/device/allocator.cc",
    "content": "/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n#include \"tachyon/device/allocator.h\"\n\n#include <atomic>\n\n#include \"absl/strings/str_cat.h\"\n#include \"absl/strings/str_format.h\"\n\n#include \"tachyon/device/allocator_registry.h\"\n#include \"tachyon/device/tracking_allocator.h\"\n\nnamespace tachyon::device {\n\nstd::string AllocatorStats::DebugString() const {\n  return absl::StrFormat(\n      \"Limit:            %20lld\\n\"\n      \"InUse:            %20lld\\n\"\n      \"MaxInUse:         %20lld\\n\"\n      \"NumAllocs:        %20lld\\n\"\n      \"MaxAllocSize:     %20lld\\n\"\n      \"Reserved:         %20lld\\n\"\n      \"PeakReserved:     %20lld\\n\"\n      \"LargestFreeBlock: %20lld\\n\",\n      // NOLINTNEXTLINE(runtime/int)\n      static_cast<long long>(this->bytes_limit ? *this->bytes_limit : 0),\n      // NOLINTNEXTLINE(runtime/int)\n      static_cast<long long>(this->bytes_in_use),\n      // NOLINTNEXTLINE(runtime/int)\n      static_cast<long long>(this->peak_bytes_in_use),\n      // NOLINTNEXTLINE(runtime/int)\n      static_cast<long long>(this->num_allocs),\n      // NOLINTNEXTLINE(runtime/int)\n      static_cast<long long>(this->largest_alloc_size),\n      // NOLINTNEXTLINE(runtime/int)\n      static_cast<long long>(this->bytes_reserved),\n      // NOLINTNEXTLINE(runtime/int)\n      static_cast<long long>(this->peak_bytes_reserved),\n      // NOLINTNEXTLINE(runtime/int)\n      static_cast<long long>(this->largest_free_block_bytes));\n}\n\nconstexpr size_t Allocator::kAllocatorAlignment;\n\nAllocator::~Allocator() {}\n\n// If true, cpu allocator collects full stats.\nstatic bool cpu_allocator_collect_full_stats = false;\n\nvoid EnableCPUAllocatorFullStats() { cpu_allocator_collect_full_stats = true; }\nbool CPUAllocatorFullStatsEnabled() { return cpu_allocator_collect_full_stats; }\n\nstd::string AllocatorAttributes::DebugString() const {\n  return absl::StrCat(\"AllocatorAttributes(on_host=\", on_host(),\n                      \" nic_compatible=\", nic_compatible(),\n                      \" gpu_compatible=\", gpu_compatible(), \")\");\n}\n\nAllocator* cpu_allocator_base() {\n  static Allocator* cpu_alloc = AllocatorFactoryRegistry::Get().GetAllocator();\n  // TODO(tucker): This really seems wrong.  It's only going to be effective on\n  // the first call in a process (but the desired effect is associated with a\n  // session), and we probably ought to be tracking the highest level Allocator,\n  // not the lowest.  Revisit the advertised semantics of the triggering option.\n  if (cpu_allocator_collect_full_stats && !cpu_alloc->TracksAllocationSizes()) {\n    cpu_alloc = new TrackingAllocator(cpu_alloc, true);\n  }\n  return cpu_alloc;\n}\n\nAllocator* cpu_allocator(int numa_node) {\n  // Correctness relies on devices being created prior to the first call\n  // to cpu_allocator, if devices are ever to be created in the process.\n  // Device creation in turn triggers ProcessState creation and the availability\n  // of the correct access pointer via this function call.\n  static ProcessStateInterface* ps =\n      AllocatorFactoryRegistry::Get().process_state();\n  if (ps) {\n    return ps->GetCPUAllocator(numa_node);\n  } else {\n    return cpu_allocator_base();\n  }\n}\n\nSubAllocator::SubAllocator(const std::vector<Visitor>& alloc_visitors,\n                           const std::vector<Visitor>& free_visitors)\n    : alloc_visitors_(alloc_visitors), free_visitors_(free_visitors) {}\n\nvoid SubAllocator::VisitAlloc(void* ptr, int index, size_t num_bytes) {\n  for (const auto& v : alloc_visitors_) {\n    v(ptr, index, num_bytes);\n  }\n}\n\nvoid SubAllocator::VisitFree(void* ptr, int index, size_t num_bytes) {\n  // Although we don't guarantee any order of visitor application, strive\n  // to apply free visitors in reverse order of alloc visitors.\n  for (int i = free_visitors_.size() - 1; i >= 0; --i) {\n    free_visitors_[i](ptr, index, num_bytes);\n  }\n}\n}  // namespace tachyon::device\n"
  },
  {
    "path": "tachyon/device/allocator.h",
    "content": "/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n#ifndef TACHYON_DEVICE_ALLOCATOR_H_\n#define TACHYON_DEVICE_ALLOCATOR_H_\n\n#include <stddef.h>\n#include <stdint.h>\n#include <stdlib.h>\n\n#include <functional>\n#include <limits>\n#include <optional>\n#include <string>\n#include <string_view>\n#include <vector>\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/device/numa.h\"\n\nnamespace tachyon::device {\n\n// Attributes for a single allocation call. Different calls to the same\n// allocator could potentially have different allocation attributes.\nstruct AllocationAttributes {\n  AllocationAttributes() = default;\n  AllocationAttributes(const AllocationAttributes& other) = delete;\n  AllocationAttributes& operator=(const AllocationAttributes& other) = delete;\n  AllocationAttributes(bool retry_on_failure, bool allocation_will_be_logged,\n                       std::function<uint64_t()>* freed_by_func)\n      : retry_on_failure(retry_on_failure),\n        allocation_will_be_logged(allocation_will_be_logged),\n        freed_by_func(freed_by_func) {}\n\n  // If the first attempt to allocate the memory fails, the allocation should\n  // wait and retry (with a timeout).\n  //\n  // This is usually set to true, but we may set it to false in cases where a\n  // failure has only performance impact (e.g. optional scratch space\n  // allocation).\n  bool retry_on_failure = true;\n  // If a Tensor is allocated without the following set to true, then\n  // it is logged as an unknown allocation. During execution Tensors\n  // should be allocated through the OpKernelContext which records\n  // which Op is performing the allocation, and sets this flag to\n  // true.\n  bool allocation_will_be_logged = false;\n  // EXPERIMENTAL: If provided, then evaluates to a timing count such that only\n  // a memory chunk whose freed_at_count is at this value or earlier may be\n  // returned.\n  std::function<uint64_t()>* freed_by_func = nullptr;  // Not owned.\n};\n\n// Runtime statistics collected by an allocator. Exactly the same as\n// stream_executor::AllocatorStats, but independently defined to preserve the\n// mutual independence of StreamExecutor and TensorFlow.\nstruct AllocatorStats {\n  int64_t num_allocs;          // Number of allocations.\n  int64_t bytes_in_use;        // Number of bytes in use.\n  int64_t peak_bytes_in_use;   // The peak bytes in use.\n  int64_t largest_alloc_size;  // The largest single allocation seen.\n\n  // The upper limit of bytes of user allocatable device memory, if such a limit\n  // is known.\n  std::optional<int64_t> bytes_limit;\n\n  // Stats for reserved memory usage.\n  int64_t bytes_reserved;       // Number of bytes reserved.\n  int64_t peak_bytes_reserved;  // The peak number of bytes reserved.\n  // The upper limit on the number bytes of reservable memory,\n  // if such a limit is known.\n  std::optional<int64_t> bytes_reservable_limit;\n\n  int64_t largest_free_block_bytes;  // Largest free block's size in heap.\n\n  // Number of bytes of memory held by the allocator.  This may be higher than\n  // bytes_in_use if the allocator holds a pool of memory (e.g. BFCAllocator).\n  std::optional<int64_t> pool_bytes;\n  std::optional<int64_t> peak_pool_bytes;\n\n  AllocatorStats()\n      : num_allocs(0),\n        bytes_in_use(0),\n        peak_bytes_in_use(0),\n        largest_alloc_size(0),\n        bytes_reserved(0),\n        peak_bytes_reserved(0),\n        largest_free_block_bytes(0) {}\n\n  std::string DebugString() const;\n};\n\n// The type of the allocated memory.\nenum class AllocatorMemoryType {\n  kUnknown = 0,       // Memory type unknown.\n  kDevice = 1,        // Memory on device.\n  kHostPageable = 2,  // Memory on host and it is pagable.\n  kHostPinned = 3,    // Memory on host and it is pinned.\n};\n\n// Allocator is an abstract interface for allocating and deallocating\n// device memory.\nclass Allocator {\n public:\n  // Align to 64 byte boundary.\n  static constexpr size_t kAllocatorAlignment = 64;\n\n  virtual ~Allocator();\n\n  // Return a string identifying this allocator\n  virtual std::string Name() = 0;\n\n  // Return an uninitialized block of memory that is \"num_bytes\" bytes\n  // in size.  The returned pointer is guaranteed to be aligned to a\n  // multiple of \"alignment\" bytes.\n  // REQUIRES: \"alignment\" is a power of 2.\n  virtual void* AllocateRaw(size_t alignment, size_t num_bytes) = 0;\n\n  // Return an uninitialized block of memory that is \"num_bytes\" bytes\n  // in size with specified allocation attributes.  The returned pointer is\n  // guaranteed to be aligned to a multiple of \"alignment\" bytes.\n  // REQUIRES: \"alignment\" is a power of 2.\n  virtual void* AllocateRaw(size_t alignment, size_t num_bytes,\n                            const AllocationAttributes& allocation_attr) {\n    // The default behavior is to use the implementation without any allocation\n    // attributes.\n    return AllocateRaw(alignment, num_bytes);\n  }\n\n  // Deallocate a block of memory pointer to by \"ptr\"\n  // REQUIRES: \"ptr\" was previously returned by a call to AllocateRaw\n  virtual void DeallocateRaw(void* ptr) = 0;\n\n  // Returns true if this allocator tracks the sizes of allocations.\n  // RequestedSize and AllocatedSize must be overridden if\n  // TracksAllocationSizes is overridden to return true.\n  virtual bool TracksAllocationSizes() const { return false; }\n\n  // Returns true if this allocator allocates an opaque handle rather than the\n  // requested number of bytes.\n  //\n  // This method returns false for most allocators, but may be used by\n  // special-case allocators that track tensor usage. If this method returns\n  // true, AllocateRaw() should be invoked for all values of `num_bytes`,\n  // including 0.\n  //\n  // NOTE: It is the caller's responsibility to track whether an allocated\n  // object is a buffer or an opaque handle. In particular, when this method\n  // returns `true`, users of this allocator must not run any constructors or\n  // destructors for complex objects, since there is no backing store for the\n  // tensor in which to place their outputs.\n  virtual bool AllocatesOpaqueHandle() const { return false; }\n\n  // Returns the user-requested size of the data allocated at\n  // 'ptr'.  Note that the actual buffer allocated might be larger\n  // than requested, but this function returns the size requested by\n  // the user.\n  //\n  // REQUIRES: TracksAllocationSizes() is true.\n  //\n  // REQUIRES: 'ptr!=nullptr' and points to a buffer previously\n  // allocated by this allocator.\n  virtual size_t RequestedSize(const void* ptr) const {\n    CHECK(false) << \"allocator doesn't track sizes\";\n    return size_t(0);\n  }\n\n  // Returns the allocated size of the buffer at 'ptr' if known,\n  // otherwise returns RequestedSize(ptr). AllocatedSize(ptr) is\n  // guaranteed to be >= RequestedSize(ptr).\n  //\n  // REQUIRES: TracksAllocationSizes() is true.\n  //\n  // REQUIRES: 'ptr!=nullptr' and points to a buffer previously\n  // allocated by this allocator.\n  virtual size_t AllocatedSize(const void* ptr) const {\n    return RequestedSize(ptr);\n  }\n\n  // Returns either 0 or an identifier assigned to the buffer at 'ptr'\n  // when the buffer was returned by AllocateRaw. If non-zero, the\n  // identifier differs from every other ID assigned by this\n  // allocator.\n  //\n  // REQUIRES: TracksAllocationSizes() is true.\n  //\n  // REQUIRES: 'ptr!=nullptr' and points to a buffer previously\n  // allocated by this allocator.\n  virtual int64_t AllocationId(const void* ptr) const { return 0; }\n\n  // Returns the allocated size of the buffer at 'ptr' if known,\n  // otherwise returns 0. This method can be called when\n  // TracksAllocationSizes() is false, but can be extremely slow.\n  //\n  // REQUIRES: 'ptr!=nullptr' and points to a buffer previously\n  // allocated by this allocator.\n  virtual size_t AllocatedSizeSlow(const void* ptr) const {\n    if (TracksAllocationSizes()) {\n      return AllocatedSize(ptr);\n    }\n    return 0;\n  }\n\n  // Fills in 'stats' with statistics collected by this allocator.\n  virtual std::optional<AllocatorStats> GetStats() { return std::nullopt; }\n\n  // If implemented, clears the internal stats except for the `in_use` fields\n  // and sets the `peak_bytes_in_use` to be equal to the `bytes_in_use`. Returns\n  //  true if implemented.\n  //\n  // REQUIRES: GetStats is overridden.\n  [[nodiscard]] virtual bool ClearStats() { return false; }\n\n  virtual void SetSafeFrontier(uint64_t count) {}\n\n  // For allocator that are stream aware, allow to specify the compute\n  // stream this allocator is used for. This can also trigger memory\n  // preallocation.\n  virtual void SetStreamAndPreallocateMemory(void* stream) {}\n\n  // Returns the type of the memory allocated by this allocator.\n  virtual AllocatorMemoryType GetMemoryType() const {\n    return AllocatorMemoryType::kUnknown;\n  }\n};\n\n// An implementation of Allocator that delegates all calls to another Allocator.\n//\n// Useful to clients who want to override part of the functionality of another\n// allocator.\nclass TACHYON_EXPORT AllocatorWrapper : public Allocator {\n public:\n  explicit AllocatorWrapper(Allocator* wrapped) : wrapped_(wrapped) {}\n\n  ~AllocatorWrapper() override {}\n\n  // Returns the wrapped allocator to which all calls are delegated.\n  Allocator* wrapped() const { return wrapped_; }\n\n  std::string Name() override { return wrapped_->Name(); }\n\n  void* AllocateRaw(size_t alignment, size_t num_bytes) override {\n    return wrapped_->AllocateRaw(alignment, num_bytes);\n  }\n\n  void* AllocateRaw(size_t alignment, size_t num_bytes,\n                    const AllocationAttributes& allocation_attr) override {\n    return wrapped_->AllocateRaw(alignment, num_bytes, allocation_attr);\n  }\n\n  void DeallocateRaw(void* ptr) override { wrapped_->DeallocateRaw(ptr); }\n\n  bool TracksAllocationSizes() const override {\n    return wrapped_->TracksAllocationSizes();\n  }\n\n  bool AllocatesOpaqueHandle() const override {\n    return wrapped_->AllocatesOpaqueHandle();\n  }\n\n  size_t RequestedSize(const void* ptr) const override {\n    return wrapped_->RequestedSize(ptr);\n  }\n\n  size_t AllocatedSize(const void* ptr) const override {\n    return wrapped_->AllocatedSize(ptr);\n  }\n\n  int64_t AllocationId(const void* ptr) const override {\n    return wrapped_->AllocationId(ptr);\n  }\n\n  size_t AllocatedSizeSlow(const void* ptr) const override {\n    return wrapped_->AllocatedSizeSlow(ptr);\n  }\n\n  AllocatorMemoryType GetMemoryType() const override {\n    return wrapped_->GetMemoryType();\n  }\n\n private:\n  Allocator* const wrapped_;\n};\n\n// A tensorflow Op may need access to different kinds of memory that\n// are not simply a function of the device to which the Op has been\n// assigned.  For example, an Op executing on a GPU may still need\n// to allocate CPU RAM for some purpose.  Internal to the tensorflow\n// runtime we may choose to allocate CPU ram from special regions\n// that have been prepared for higher performance in some use\n// contexts, e.g. doing DMA with particular devices.  For these\n// reasons, the Device interface does not expose just one memory\n// Allocator, but instead provides an accessor that takes a\n// specification of the desired memory attributes in order to select\n// an Allocator.\n//\n// Example use:\n//  // Allocator for ordinary device memory:\n//  Allocator* a = allocator(AllocatorAttributes());\n// ...\n//  // Allocator for CPU RAM, regardless of where Op is executing:\n//  AllocatorAttributes attr;\n//  attr.set_on_host(true);\n//  Allocator* a = allocator(attr);\nstruct AllocatorAttributes {\n  void set_on_host(bool v) { value |= (static_cast<int>(v)); }\n  bool on_host() const { return value & 0x1; }\n  void set_nic_compatible(bool v) { value |= (static_cast<int>(v) << 1); }\n  bool nic_compatible() const { return value & (0x1 << 1); }\n  void set_gpu_compatible(bool v) { value |= (static_cast<int>(v) << 2); }\n  bool gpu_compatible() const { return value & (0x1 << 2); }\n  void Merge(AllocatorAttributes other) {\n    value |= other.value;\n    if (scope_id != other.scope_id) {\n      CHECK(scope_id == 0 || other.scope_id == 0)\n          << \"At least one scope_id should be zero to merge \"\n             \"AllocatorAttributes but found this.scope_id=\"\n          << scope_id << \" and other.scope_id=\" << other.scope_id;\n      scope_id = scope_id == 0 ? other.scope_id : scope_id;\n    }\n  }\n  // Returns true if the fields set in *this is a subset of or equal to\n  // those set in other.\n  bool IsEqualOrLessRestrictiveThan(const AllocatorAttributes& other) const {\n    return (value | other.value) == other.value;\n  }\n\n  // NOTE: The upper 8 bits of the value are reserved for\n  // device-specific uses.  Implementors of a device can interpret these\n  // upper 8 bits in device-specific ways, and ops implemented for those\n  // devices are responsible for setting those 8 bits appropriately.\n  uint32_t value = 0;\n  // EXPERIMENTAL: If this is greater than zero, then allocation is delegated to\n  // a named special-purpose allocator on the same device.\n  int32_t scope_id = 0;\n\n  // Returns a human readable representation of this.\n  std::string DebugString() const;\n};\n\n// Returns a trivial implementation of Allocator, which is a process singleton.\n// Access through this function is only intended for use by restricted parts\n// of the infrastructure.\nAllocator* cpu_allocator_base();\n\n// If available, calls ProcessState::GetCPUAllocator(numa_node).\n// If not, falls back to cpu_allocator_base().\n// Intended for use in contexts where ProcessState is not visible at\n// compile time. Where ProcessState is visible, it's preferable to\n// call it directly.\nAllocator* cpu_allocator(int numa_node = kNUMANoAffinity);\n\n// Enables AllocatorStats in the default CPU allocator implementation.  By\n// default, it's disabled.\nvoid EnableCPUAllocatorStats();\n// Disables AllocatorStats in the default CPU allocator implementation.  By\n// default, it's disabled.\nvoid DisableCPUAllocatorStats();\nbool CPUAllocatorStatsEnabled();\n\n// Enables full statistics collection in the default CPU allocator\n// implementation.  By default, it's disabled.\nvoid EnableCPUAllocatorFullStats();\nbool CPUAllocatorFullStatsEnabled();\n\n// An object that does the underlying suballoc/free of memory for a higher-level\n// allocator.  The expectation is that the higher-level allocator is doing some\n// kind of cache or pool management so that it will call SubAllocator::Alloc and\n// Free relatively infrequently, compared to the number of times its own\n// AllocateRaw and Free methods are called.\nclass SubAllocator {\n public:\n  // Visitor gets called with a pointer to a memory area and its\n  // size in bytes.  The index value will be numa_node for a CPU\n  // allocator and GPU id for a GPU allocator.\n  typedef std::function<void(void*, int index, size_t)> Visitor;\n\n  SubAllocator(const std::vector<Visitor>& alloc_visitors,\n               const std::vector<Visitor>& free_visitors);\n\n  virtual ~SubAllocator() {}\n  // Allocates at least num_bytes. Returns actual number of bytes allocated in\n  // bytes_received. The caller can safely use the full bytes_received sized\n  // buffer following the returend pointer.\n  virtual void* Alloc(size_t alignment, size_t num_bytes,\n                      size_t* bytes_received) = 0;\n  virtual void Free(void* ptr, size_t num_bytes) = 0;\n\n  // Returns true if the BFC allocator can safely coalesce adjacent regions\n  // returned by this allocator.\n  virtual bool SupportsCoalescing() const = 0;\n\n  // Returns the type of the memory allocated by this SubAllocator.\n  virtual AllocatorMemoryType GetMemoryType() const {\n    return AllocatorMemoryType::kUnknown;\n  }\n\n protected:\n  // Implementation of Alloc() method must call this on newly allocated\n  // value.\n  void VisitAlloc(void* ptr, int index, size_t num_bytes);\n\n  // Implementation of Free() method must call this on value to be\n  // freed immediately before deallocation.\n  void VisitFree(void* ptr, int index, size_t num_bytes);\n\n  const std::vector<Visitor> alloc_visitors_;\n  const std::vector<Visitor> free_visitors_;\n};\n\n}  // namespace tachyon::device\n\n#endif  // TACHYON_DEVICE_ALLOCATOR_H_\n"
  },
  {
    "path": "tachyon/device/allocator_registry.cc",
    "content": "/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n#include \"tachyon/device/allocator_registry.h\"\n\n#include <string>\n#include <utility>\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/no_destructor.h\"\n\nnamespace tachyon::device {\n\n// static\nAllocatorFactoryRegistry& AllocatorFactoryRegistry::Get() {\n  static base::NoDestructor<AllocatorFactoryRegistry> registry;\n  return *registry;\n}\n\nconst AllocatorFactoryRegistry::FactoryEntry*\nAllocatorFactoryRegistry::FindEntry(const std::string& name,\n                                    int priority) const {\n  for (auto& entry : factories_) {\n    if (!name.compare(entry.name) && priority == entry.priority) {\n      return &entry;\n    }\n  }\n  return nullptr;\n}\n\nvoid AllocatorFactoryRegistry::Register(const char* source_file,\n                                        int source_line,\n                                        const std::string& name, int priority,\n                                        AllocatorFactory* factory) {\n  absl::MutexLock l(&mu_);\n  CHECK(!first_alloc_made_) << \"Attempt to register an AllocatorFactory \"\n                            << \"after call to GetAllocator()\";\n  CHECK(!name.empty()) << \"Need a valid name for Allocator\";\n  CHECK_GE(priority, 0) << \"Priority needs to be non-negative\";\n\n  const FactoryEntry* existing = FindEntry(name, priority);\n  if (existing != nullptr) {\n    // Duplicate registration is a hard failure.\n    NOTREACHED() << \"New registration for AllocatorFactory with name=\" << name\n                 << \" priority=\" << priority << \" at location \" << source_file\n                 << \":\" << source_line\n                 << \" conflicts with previous registration at location \"\n                 << existing->source_file << \":\" << existing->source_line;\n  }\n\n  FactoryEntry entry;\n  entry.source_file = source_file;\n  entry.source_line = source_line;\n  entry.name = name;\n  entry.priority = priority;\n  entry.factory.reset(factory);\n  factories_.push_back(std::move(entry));\n}\n\nAllocator* AllocatorFactoryRegistry::GetAllocator() {\n  absl::MutexLock l(&mu_);\n  first_alloc_made_ = true;\n  FactoryEntry* best_entry = nullptr;\n  for (auto& entry : factories_) {\n    if (best_entry == nullptr) {\n      best_entry = &entry;\n    } else if (entry.priority > best_entry->priority) {\n      best_entry = &entry;\n    }\n  }\n  if (best_entry) {\n    if (!best_entry->allocator) {\n      best_entry->allocator.reset(best_entry->factory->CreateAllocator());\n    }\n    return best_entry->allocator.get();\n  } else {\n    NOTREACHED() << \"No registered CPU AllocatorFactory\";\n    return nullptr;\n  }\n}\n\nSubAllocator* AllocatorFactoryRegistry::GetSubAllocator(int numa_node) {\n  absl::MutexLock l(&mu_);\n  first_alloc_made_ = true;\n  FactoryEntry* best_entry = nullptr;\n  for (auto& entry : factories_) {\n    if (best_entry == nullptr) {\n      best_entry = &entry;\n    } else if (best_entry->factory->NumaEnabled()) {\n      if (entry.factory->NumaEnabled() &&\n          (entry.priority > best_entry->priority)) {\n        best_entry = &entry;\n      }\n    } else {\n      DCHECK(!best_entry->factory->NumaEnabled());\n      if (entry.factory->NumaEnabled() ||\n          (entry.priority > best_entry->priority)) {\n        best_entry = &entry;\n      }\n    }\n  }\n  if (best_entry) {\n    int index = 0;\n    if (numa_node != kNUMANoAffinity) {\n      CHECK_LE(numa_node, NUMANumNodes());\n      index = 1 + numa_node;\n    }\n    if (best_entry->sub_allocators.size() < static_cast<size_t>(index + 1)) {\n      best_entry->sub_allocators.resize(index + 1);\n    }\n    if (!best_entry->sub_allocators[index].get()) {\n      best_entry->sub_allocators[index].reset(\n          best_entry->factory->CreateSubAllocator(numa_node));\n    }\n    return best_entry->sub_allocators[index].get();\n  } else {\n    NOTREACHED() << \"No registered CPU AllocatorFactory\";\n    return nullptr;\n  }\n}\n\n}  // namespace tachyon::device\n"
  },
  {
    "path": "tachyon/device/allocator_registry.h",
    "content": "/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n// Classes to maintain a static registry of memory allocator factories.\n#ifndef TACHYON_DEVICE_ALLOCATOR_REGISTRY_H_\n#define TACHYON_DEVICE_ALLOCATOR_REGISTRY_H_\n\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"absl/base/thread_annotations.h\"\n#include \"absl/synchronization/mutex.h\"\n\n#include \"tachyon/device/allocator.h\"\n#include \"tachyon/export.h\"\n\nnamespace tachyon::device {\n\nclass TACHYON_EXPORT AllocatorFactory {\n public:\n  virtual ~AllocatorFactory() {}\n\n  // Returns true if the factory will create a functionally different\n  // SubAllocator for different (legal) values of numa_node.\n  virtual bool NumaEnabled() { return false; }\n\n  // Create an Allocator.\n  virtual Allocator* CreateAllocator() = 0;\n\n  // Create a SubAllocator. If NumaEnabled() is true, then returned SubAllocator\n  // will allocate memory local to numa_node.  If numa_node == kNUMANoAffinity\n  // then allocated memory is not specific to any NUMA node.\n  virtual SubAllocator* CreateSubAllocator(int numa_node) = 0;\n};\n\n// ProcessState is defined in a package that cannot be a dependency of\n// framework.  This definition allows us to access the one method we need.\nclass TACHYON_EXPORT ProcessStateInterface {\n public:\n  virtual ~ProcessStateInterface() {}\n  virtual Allocator* GetCPUAllocator(int numa_node) = 0;\n};\n\n// A singleton registry of AllocatorFactories.\n//\n// Allocators should be obtained through ProcessState or cpu_allocator()\n// (deprecated), not directly through this interface.  The purpose of this\n// registry is to allow link-time discovery of multiple AllocatorFactories among\n// which ProcessState will obtain the best fit at startup.\nclass TACHYON_EXPORT AllocatorFactoryRegistry {\n public:\n  AllocatorFactoryRegistry() {}\n  AllocatorFactoryRegistry(const AllocatorFactoryRegistry& other) = delete;\n  AllocatorFactoryRegistry& operator=(const AllocatorFactoryRegistry& other) =\n      delete;\n  ~AllocatorFactoryRegistry() {}\n\n  void Register(const char* source_file, int source_line,\n                const std::string& name, int priority,\n                AllocatorFactory* factory);\n\n  // Returns 'best fit' Allocator.  Find the factory with the highest priority\n  // and return an allocator constructed by it.  If multiple factories have\n  // been registered with the same priority, picks one by unspecified criteria.\n  Allocator* GetAllocator();\n\n  // Returns 'best fit' SubAllocator.  First look for the highest priority\n  // factory that is NUMA-enabled.  If none is registered, fall back to the\n  // highest priority non-NUMA-enabled factory.  If NUMA-enabled, return a\n  // SubAllocator specific to numa_node, otherwise return a NUMA-insensitive\n  // SubAllocator.\n  SubAllocator* GetSubAllocator(int numa_node);\n\n  // Returns the singleton value.\n  static AllocatorFactoryRegistry& Get();\n\n  ProcessStateInterface* process_state() const { return process_state_; }\n\n protected:\n  ProcessStateInterface* process_state_ = nullptr;\n\n private:\n  absl::Mutex mu_;\n  bool first_alloc_made_ = false;\n  struct FactoryEntry {\n    const char* source_file;\n    int source_line;\n    std::string name;\n    int priority;\n    std::unique_ptr<AllocatorFactory> factory;\n    std::unique_ptr<Allocator> allocator;\n    // Index 0 corresponds to kNUMANoAffinity, other indices are (numa_node +\n    // 1).\n    std::vector<std::unique_ptr<SubAllocator>> sub_allocators;\n  };\n  std::vector<FactoryEntry> factories_ ABSL_GUARDED_BY(mu_);\n\n  // Returns any FactoryEntry registered under 'name' and 'priority',\n  // or 'nullptr' if none found.\n  const FactoryEntry* FindEntry(const std::string& name, int priority) const\n      ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_);\n};\n\nclass TACHYON_EXPORT AllocatorFactoryRegistration {\n public:\n  AllocatorFactoryRegistration(const char* file, int line,\n                               const std::string& name, int priority,\n                               AllocatorFactory* factory) {\n    AllocatorFactoryRegistry::Get().Register(file, line, name, priority,\n                                             factory);\n  }\n};\n\n#define REGISTER_MEM_ALLOCATOR(name, priority, factory)                     \\\n  REGISTER_MEM_ALLOCATOR_UNIQ_HELPER(__COUNTER__, __FILE__, __LINE__, name, \\\n                                     priority, factory)\n\n#define REGISTER_MEM_ALLOCATOR_UNIQ_HELPER(ctr, file, line, name, priority, \\\n                                           factory)                         \\\n  REGISTER_MEM_ALLOCATOR_UNIQ(ctr, file, line, name, priority, factory)\n\n#define REGISTER_MEM_ALLOCATOR_UNIQ(ctr, file, line, name, priority, factory) \\\n  static AllocatorFactoryRegistration allocator_factory_reg_##ctr(            \\\n      file, line, name, priority, new factory)\n\n}  // namespace tachyon::device\n\n#endif  // TACHYON_DEVICE_ALLOCATOR_REGISTRY_H_\n"
  },
  {
    "path": "tachyon/device/gpu/BUILD.bazel",
    "content": "load(\"@local_config_cuda//cuda:build_defs.bzl\", \"if_cuda\")\nload(\"//bazel:tachyon.bzl\", \"if_gpu_is_configured\")\nload(\n    \"//bazel:tachyon_cc.bzl\",\n    \"tachyon_cc_library\",\n    \"tachyon_cuda_defines\",\n    \"tachyon_cuda_unittest\",\n)\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"device_perf_info\",\n    srcs = [\"device_perf_info.cc\"],\n    hdrs = [\"device_perf_info.h\"],\n    deps = [\n        \"//tachyon:export\",\n        \"//tachyon/base:no_destructor\",\n        \"//tachyon/build:build_config\",\n        \"@com_google_absl//absl/synchronization\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"gpu_cudamallocasync_allocator\",\n    srcs = if_cuda([\"gpu_cudamallocasync_allocator.cc\"]),\n    hdrs = [\"gpu_cudamallocasync_allocator.h\"],\n    defines = tachyon_cuda_defines(),\n    deps = [\n        \":gpu_logging\",\n        \":platform_device_id\",\n        \"//tachyon/device:allocator\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"gpu_device_functions\",\n    hdrs = [\"gpu_device_functions.h\"],\n    defines = tachyon_cuda_defines(),\n    deps = if_cuda([\n        \"@local_config_cuda//cuda:cuda_headers\",\n    ]),\n)\n\ntachyon_cc_library(\n    name = \"gpu_driver\",\n    srcs = if_gpu_is_configured([\"gpu_driver.cc\"]),\n    hdrs = [\"gpu_driver.h\"],\n    deps = [\n        \":gpu_types\",\n        \"//tachyon/base:logging\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"gpu_enums\",\n    hdrs = [\"gpu_enums.h\"],\n    defines = tachyon_cuda_defines(),\n    deps = if_cuda([\n        \"@local_config_cuda//cuda:cuda_headers\",\n    ]),\n)\n\ntachyon_cc_library(\n    name = \"gpu_info\",\n    srcs = [\n        \"gpu_info.cc\",\n        \"gpu_util.cc\",\n    ],\n    hdrs = [\n        \"gpu_info.h\",\n        \"gpu_util.h\",\n    ],\n    deps = [\n        \":device_perf_info\",\n        \"//tachyon/base/strings:string_number_conversions\",\n        \"//tachyon/base/time\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"gpu_init\",\n    srcs = [\"gpu_init.cc\"],\n    hdrs = [\"gpu_init.h\"],\n    defines = tachyon_cuda_defines(),\n    deps = [\"//tachyon/base:logging\"],\n)\n\ntachyon_cc_library(\n    name = \"gpu_logging\",\n    srcs = if_gpu_is_configured([\"gpu_logging.cc\"]),\n    hdrs = [\"gpu_logging.h\"],\n    deps = [\n        \":gpu_device_functions\",\n        \":gpu_enums\",\n        \"//tachyon/base:compiler_specific\",\n        \"//tachyon/base:logging\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"gpu_memory\",\n    srcs = if_gpu_is_configured([\"gpu_memory.cc\"]),\n    hdrs = [\"gpu_memory.h\"],\n    deps = [\n        \":gpu_logging\",\n        \"//tachyon:export\",\n        \"//tachyon/base/numerics:checked_math\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"gpu_types\",\n    hdrs = [\"gpu_types.h\"],\n    defines = tachyon_cuda_defines(),\n    deps = if_cuda([\n        \"@local_config_cuda//cuda:cuda_headers\",\n    ]),\n)\n\ntachyon_cc_library(\n    name = \"platform_device_id\",\n    hdrs = [\"platform_device_id.h\"],\n)\n\ntachyon_cc_library(\n    name = \"scoped_event\",\n    srcs = if_gpu_is_configured([\"scoped_event.cc\"]),\n    hdrs = [\"scoped_event.h\"],\n    deps = [\n        \":gpu_logging\",\n        \"//tachyon:export\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"scoped_mem_pool\",\n    srcs = if_gpu_is_configured([\"scoped_mem_pool.cc\"]),\n    hdrs = [\"scoped_mem_pool.h\"],\n    deps = [\n        \":gpu_logging\",\n        \"//tachyon:export\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"scoped_stream\",\n    srcs = if_gpu_is_configured([\"scoped_stream.cc\"]),\n    hdrs = [\"scoped_stream.h\"],\n    deps = [\n        \":gpu_logging\",\n        \"//tachyon:export\",\n    ],\n)\n\ntachyon_cuda_unittest(\n    name = \"gpu_unittests\",\n    srcs = if_gpu_is_configured([\"gpu_memory_unittest.cc\"]),\n    deps = [\n        \":gpu_memory\",\n        \":scoped_mem_pool\",\n        \"//tachyon/base:random\",\n        \"//tachyon/base/containers:container_util\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/device/gpu/cuda/BUILD.bazel",
    "content": "load(\"@local_config_cuda//cuda:build_defs.bzl\", \"if_cuda\")\nload(\"//bazel:tachyon_cc.bzl\", \"tachyon_cuda_library\", \"tachyon_cuda_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cuda_library(\n    name = \"cub_helper\",\n    hdrs = [\"cub_helper.h\"],\n    deps = [\"//tachyon/device/gpu:gpu_memory\"],\n)\n\ntachyon_cuda_library(\n    name = \"cuda_driver\",\n    srcs = if_cuda([\"cuda_driver.cc\"]),\n    hdrs = [\"cuda_driver.h\"],\n    deps = [\n        \"//tachyon/device/gpu:gpu_driver\",\n        \"@com_google_absl//absl/container:node_hash_map\",\n        \"@com_google_absl//absl/strings\",\n        \"@com_google_absl//absl/synchronization\",\n    ] + if_cuda([\n        \"@local_config_cuda//cuda:cuda_driver\",\n    ]),\n)\n\ntachyon_cuda_library(\n    name = \"cuda_memory\",\n    hdrs = [\"cuda_memory.h\"],\n    deps = [\"//tachyon/base:compiler_specific\"] + if_cuda([\n        \"@local_config_cuda//cuda:cuda_headers\",\n    ]),\n)\n\ntachyon_cuda_unittest(\n    name = \"cuda_unittests\",\n    srcs = [\n        \"cuda_driver_unittest.cc\",\n    ],\n    deps = [\n        \":cuda_driver\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/device/gpu/cuda/cub_helper.h",
    "content": "#ifndef TACHYON_DEVICE_GPU_CUDA_CUB_HELPER_H_\n#define TACHYON_DEVICE_GPU_CUDA_CUB_HELPER_H_\n\n#include <tuple>\n\n#include \"tachyon/device/gpu/gpu_memory.h\"\n\n#define CUB_TRY_ALLOCATE(fn, ...)                                    \\\n  ({                                                                 \\\n    size_t bytes = 0;                                                \\\n    cudaError_t error = fn(nullptr, bytes, __VA_ARGS__);             \\\n    if (error == cudaSuccess) {                                      \\\n      ::tachyon::device::gpu::GpuMemory<uint8_t> storage =           \\\n          ::tachyon::device::gpu::GpuMemory<uint8_t>::Malloc(bytes); \\\n      std::ignore = storage;                                         \\\n    } else {                                                         \\\n      GPU_LOG(ERROR, error) << \"Failed \" #fn;                        \\\n    }                                                                \\\n    error;                                                           \\\n  })\n\n#define CUB_INVOKE(fn, ...)                                          \\\n  ({                                                                 \\\n    size_t bytes = 0;                                                \\\n    cudaError_t error = fn(nullptr, bytes, __VA_ARGS__);             \\\n    if (error == cudaSuccess) {                                      \\\n      ::tachyon::device::gpu::GpuMemory<uint8_t> storage =           \\\n          ::tachyon::device::gpu::GpuMemory<uint8_t>::Malloc(bytes); \\\n      error = fn(storage.get(), bytes, __VA_ARGS__);                 \\\n    } else {                                                         \\\n      GPU_LOG(ERROR, error) << \"Failed \" #fn;                        \\\n    }                                                                \\\n    error;                                                           \\\n  })\n\n#define CUB_TRY_ALLOCATE_WITH_POOL(pool, stream, fn, ...)                  \\\n  ({                                                                       \\\n    size_t bytes = 0;                                                      \\\n    cudaError_t error = fn(nullptr, bytes, __VA_ARGS__);                   \\\n    if (error == cudaSuccess) {                                            \\\n      ::tachyon::device::gpu::GpuMemory<uint8_t> storage =                 \\\n          ::tachyon::device::gpu::GpuMemory<uint8_t>::MallocFromPoolAsync( \\\n              bytes, pool, stream);                                        \\\n      std::ignore = storage;                                               \\\n    } else {                                                               \\\n      GPU_LOG(ERROR, error) << \"Failed \" #fn;                              \\\n    }                                                                      \\\n    error;                                                                 \\\n  })\n\n#define CUB_INVOKE_WITH_POOL(pool, stream, fn, ...)                        \\\n  ({                                                                       \\\n    size_t bytes = 0;                                                      \\\n    cudaError_t error = fn(nullptr, bytes, __VA_ARGS__);                   \\\n    if (error == cudaSuccess) {                                            \\\n      ::tachyon::device::gpu::GpuMemory<uint8_t> storage =                 \\\n          ::tachyon::device::gpu::GpuMemory<uint8_t>::MallocFromPoolAsync( \\\n              bytes, pool, stream);                                        \\\n      error = fn(storage.get(), bytes, __VA_ARGS__);                       \\\n    } else {                                                               \\\n      GPU_LOG(ERROR, error) << \"Failed \" #fn;                              \\\n    }                                                                      \\\n    error;                                                                 \\\n  })\n\n#endif  // TACHYON_DEVICE_GPU_CUDA_CUB_HELPER_H_\n"
  },
  {
    "path": "tachyon/device/gpu/cuda/cuda_driver.cc",
    "content": "/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n#include \"tachyon/device/gpu/cuda/cuda_driver.h\"\n\n#define FAIL_IF_CUDA_RES_ERROR(expr, ...)                   \\\n  do {                                                      \\\n    CUresult _res = (expr);                                 \\\n    if (ABSL_PREDICT_FALSE(_res != CUDA_SUCCESS)) {         \\\n      LOG(FATAL) << absl::StrCat(__VA_ARGS__) << \": \"       \\\n                 << ::tachyon::device::gpu::ToString(_res); \\\n    }                                                       \\\n  } while (0)\n\nnamespace tachyon::device {\nnamespace gpu {\n\n// static\nabsl::Mutex CreatedContexts::mu_{absl::kConstInit};\n\n// static\nint64_t CreatedContexts::next_id_ = 1;  // 0 means \"no context\"\n\nScopedActivateContext::ScopedActivateContext(GpuContext* cuda_context) {\n  NOTIMPLEMENTED();\n}\n\nScopedActivateContext::~ScopedActivateContext() { NOTIMPLEMENTED(); }\n\n}  // namespace gpu\n\nnamespace cuda {\n\nCUcontext CurrentContextOrDie() {\n  CUcontext current = nullptr;\n  FAIL_IF_CUDA_RES_ERROR(cuCtxGetCurrent(&current),\n                         \"Failed to query current context\");\n  return current;\n}\n\n}  // namespace cuda\n}  // namespace tachyon::device\n"
  },
  {
    "path": "tachyon/device/gpu/cuda/cuda_driver.h",
    "content": "/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n// CUDA userspace driver library wrapper functionality.\n\n#ifndef TACHYON_DEVICE_GPU_CUDA_CUDA_DRIVER_H_\n#define TACHYON_DEVICE_GPU_CUDA_CUDA_DRIVER_H_\n\n#include <memory>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"absl/container/node_hash_map.h\"\n#include \"absl/strings/str_cat.h\"\n#include \"absl/synchronization/mutex.h\"\n#include \"third_party/gpus/cuda/include/cuda.h\"\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/device/gpu/gpu_driver.h\"\n#include \"tachyon/export.h\"\n\nnamespace tachyon::device {\nnamespace gpu {\n\n// Formats CUresult to output prettified values into a log stream.\nstatic std::string ToString(CUresult result) {\n  const char* error_name;\n  if (cuGetErrorName(result, &error_name)) {\n    return absl::StrCat(\"UNKNOWN ERROR (\", static_cast<int>(result), \")\");\n  }\n  const char* error_string;\n  if (cuGetErrorString(result, &error_string)) {\n    return error_name;\n  }\n  return absl::StrCat(error_name, \": \", error_string);\n}\n\n// CUDAContext wraps a cuda CUcontext handle, and includes a unique id. The\n// unique id is positive, and ids are not repeated within the process.\nclass GpuContext {\n public:\n  GpuContext(CUcontext context, int64_t id) : context_(context), id_(id) {}\n\n  CUcontext context() const { return context_; }\n  int64_t id() const { return id_; }\n\n  // Disallow copying and moving.\n  GpuContext(GpuContext&&) = delete;\n  GpuContext(const GpuContext&) = delete;\n  GpuContext& operator=(GpuContext&&) = delete;\n  GpuContext& operator=(const GpuContext&) = delete;\n\n private:\n  CUcontext const context_;\n  const int64_t id_;\n};\n\n// Manages the singleton map of contexts that we've created, mapping\n// from the CUcontext to the GpuContext* that we pass around internally.\n// This also manages assignment of unique ids to GpuContexts, to allow\n// for fast comparison of a context against the current context.\n//\n// CUDA-runtime-created contexts are avoided, if triple angle\n// brace launches are required, by using the scoped activations in\n// gpu/gpu_activation.h.\nclass CreatedContexts {\n public:\n  // Returns whether context is a member of the live set.\n  static bool Has(CUcontext context) {\n    absl::ReaderMutexLock lock(&mu_);\n    return Live()->find(context) != Live()->end();\n  }\n\n  // Adds context to the live set, or returns it if it's already present.\n  static GpuContext* Add(CUcontext context, int device_ordinal) {\n    CHECK(context != nullptr);\n    absl::MutexLock lock(&mu_);\n\n    auto insert_result = Live()->insert(std::make_pair(context, nullptr));\n    auto it = insert_result.first;\n    if (insert_result.second) {\n      // context was not present in the map.  Add it.\n      it->second = std::make_unique<GpuContext>(context, next_id_++);\n      (*LiveOrdinal())[device_ordinal].push_back(context);\n    }\n    return it->second.get();\n  }\n\n  // Removes context from the live set.\n  static void Remove(CUcontext context) {\n    CHECK(context != nullptr);\n    absl::MutexLock lock(&mu_);\n    auto it = Live()->find(context);\n    CHECK(it != Live()->end()) << context;\n    Live()->erase(it);\n    for (auto p : (*LiveOrdinal())) {\n      auto it2 = std::find(p.second.begin(), p.second.end(), context);\n      if (it2 != p.second.end()) {\n        p.second.erase(it2, it2++);\n        if (p.second.empty()) {\n          LiveOrdinal()->erase(p.first);\n        }\n        break;\n      }\n    }\n  }\n\n  // Return the context associated to that ptr.\n  static CUcontext GetAnyContext(void* ptr) {\n    absl::ReaderMutexLock lock(&mu_);\n    int device_ordinal;\n    CUresult result = cuPointerGetAttribute(static_cast<void*>(&device_ordinal),\n                                            CU_POINTER_ATTRIBUTE_DEVICE_ORDINAL,\n                                            reinterpret_cast<CUdeviceptr>(ptr));\n    if (result != CUDA_SUCCESS) {\n      LOG(FATAL) << \"Not able to get the device_ordinal for ptr: \" << ptr\n                 << \". Error: \" << ToString(result);\n    }\n    CHECK_EQ(LiveOrdinal()->count(device_ordinal), static_cast<size_t>(1));\n    CHECK(!LiveOrdinal()->at(device_ordinal).empty())\n        << \"Need at least one context.\";\n    return LiveOrdinal()->at(device_ordinal)[0];\n  }\n\n private:\n  // Returns the live map singleton.\n  static absl::node_hash_map<CUcontext, std::unique_ptr<GpuContext>>* Live() {\n    static auto singleton =\n        new absl::node_hash_map<CUcontext, std::unique_ptr<GpuContext>>;\n    return singleton;\n  }\n  static absl::node_hash_map<int, std::vector<CUcontext>>* LiveOrdinal() {\n    static auto singleton =\n        new absl::node_hash_map<int, std::vector<CUcontext>>;\n    return singleton;\n  }\n\n  // Lock that guards access-to/mutation-of the live set.\n  static absl::Mutex mu_;\n  static int64_t next_id_;\n};\n\n}  // namespace gpu\n\nnamespace cuda {\n\nusing MemorySpace = gpu::MemorySpace;\n\nusing CUDADriver = gpu::GpuDriver;\n\nusing ScopedActivateContext = gpu::ScopedActivateContext;\n\nusing CudaContext = gpu::GpuContext;\n\n// Returns the current context set in CUDA. This is done by calling the cuda\n// driver (e.g., this value is not our cached view of the current context).\nCUcontext CurrentContextOrDie();\n\n}  // namespace cuda\n}  // namespace tachyon::device\n\n#endif  // TACHYON_DEVICE_GPU_CUDA_CUDA_DRIVER_H_\n"
  },
  {
    "path": "tachyon/device/gpu/cuda/cuda_driver_unittest.cc",
    "content": "/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n#if TACHYON_CUDA\n\n#include \"tachyon/device/gpu/cuda/cuda_driver.h\"\n\n#include \"absl/memory/memory.h\"\n#include \"gtest/gtest.h\"\n#include \"third_party/gpus/cuda/include/cuda_runtime_api.h\"\n\nnamespace tachyon::device::gpu {\n\nvoid CheckCuda(CUresult result, const char* file, int line) {\n  if (result == CUDA_SUCCESS) {\n    return;\n  }\n  const char* name;\n  cuGetErrorName(result, &name);\n  const char* message;\n  cuGetErrorString(result, &message);\n  LOG(FATAL) << file << \"(\" << line << \"): \" << name << \", \" << message;\n}\n\nvoid CheckCuda(cudaError_t result, const char* file, int line) {\n  if (result == cudaSuccess) {\n    return;\n  }\n  const char* name = cudaGetErrorName(result);\n  const char* message = cudaGetErrorString(result);\n  LOG(FATAL) << file << \"(\" << line << \"): \" << name << \", \" << message;\n}\n\n#define CHECK_CUDA(result) CheckCuda(result, __FILE__, __LINE__)\n\nTEST(CudaDriverTest, ScopedActivateContextTest) {\n  CHECK_CUDA(cuInit(0));\n  CUdevice device;\n  CHECK_CUDA(cuDeviceGet(&device, 0));\n  CUcontext context0, context1;\n  CHECK_CUDA(cuCtxCreate(&context0, 0, device));\n  CHECK_CUDA(cuCtxCreate(&context1, 0, device));\n  GpuContext se_context1(context1, /*id=*/101);\n  {\n    ScopedActivateContext scope(&se_context1);\n    CUcontext c;\n    CHECK_CUDA(cuCtxGetCurrent(&c));\n    EXPECT_EQ(c, context1);\n  }\n  CHECK_CUDA(cuCtxSetCurrent(context0));\n  /*\n  TODO(chokobole): Enable this test.\n  // ScopedActivateContext must correctly set the CUDA context even if some\n  // other code changes the context between the two scopes.\n  {\n    ScopedActivateContext scope(&se_context1);\n    CUcontext c;\n    CHECK_CUDA(cuCtxGetCurrent(&c));\n    EXPECT_EQ(c, context1);\n  }\n  */\n}\n\n}  // namespace tachyon::device::gpu\n\n#endif  // TACHYON_CUDA\n"
  },
  {
    "path": "tachyon/device/gpu/cuda/cuda_memory.h",
    "content": "#ifndef TACHYON_DEVICE_GPU_CUDA_CUDA_MEMORY_H_\n#define TACHYON_DEVICE_GPU_CUDA_CUDA_MEMORY_H_\n\n#include <type_traits>\n\n#include \"third_party/gpus/cuda/include/cuda_runtime.h\"\n\n#include \"tachyon/base/compiler_specific.h\"\n\nnamespace tachyon::device::gpu {\n\nenum class StateSpace {\n  kNone,\n  kGlobal,\n};\n\nenum class CacheOperator {\n  kNone,\n  // Shared by store and load instruction\n  // Cache at global level(cache in L2 and below, not L1).\n  kGlobal,\n  // Cache streaming, likely to be accessed once.\n  kStreaming,\n  // Load instruction only\n  // Cache at all levels, likely to be accessed again.\n  kAll,\n  // Last use.\n  kLastUse,\n  // Cache as volatile (consider cached system memory lines stale, fetch again).\n  kVolatile,\n  // Store instruction only\n  // Cache write-back all coherent levels.\n  kWriteBack,\n  // Cache write-through.\n  kWriteThrough,\n};\n\ntemplate <typename T, StateSpace Space, CacheOperator Operator>\n__device__ ALWAYS_INLINE constexpr T LoadSingle(const T* ptr) {\n  if constexpr (Space == StateSpace::kGlobal) {\n    if constexpr (Operator == CacheOperator::kNone) {\n      return __ldg(ptr);\n    } else if constexpr (Operator == CacheOperator::kAll) {\n      return __ldca(ptr);\n    } else if constexpr (Operator == CacheOperator::kGlobal) {\n      return __ldcg(ptr);\n    } else if constexpr (Operator == CacheOperator::kStreaming) {\n      return __ldcs(ptr);\n    } else if constexpr (Operator == CacheOperator::kLastUse) {\n      return __ldlu(ptr);\n    } else if constexpr (Operator == CacheOperator::kVolatile) {\n      return __ldcv(ptr);\n    }\n  }\n  return *ptr;\n}\n\ntemplate <class T, typename U, CacheOperator Operator, size_t Stride>\n__device__ ALWAYS_INLINE constexpr T Load(const T* address, size_t offset) {\n  static_assert(alignof(T) % alignof(U) == 0);\n  static_assert(sizeof(T) % sizeof(U) == 0);\n  constexpr size_t count = sizeof(T) / sizeof(U);\n  T result = {};\n  auto pa = reinterpret_cast<const U*>(address) + offset;\n  auto pr = reinterpret_cast<U*>(&result);\n  for (size_t i = 0; i < count; ++i) {\n    pr[i] = LoadSingle<U, StateSpace::kGlobal, Operator>(&pa[i * Stride]);\n  }\n  return result;\n}\n\ntemplate <class T, CacheOperator Operator = CacheOperator::kNone,\n          size_t Stride = 1,\n          typename U = std::enable_if_t<sizeof(T) % sizeof(uint4) == 0, uint4>>\n__device__ ALWAYS_INLINE constexpr T Load(const T* address,\n                                          const size_t offset = 0,\n                                          [[maybe_unused]] uint4 _dummy = {}) {\n  return Load<T, U, Operator, Stride>(address, offset);\n}\n\ntemplate <typename T, CacheOperator Operator>\n__device__ ALWAYS_INLINE constexpr void StoreSingle(T* ptr, T value) {\n  if constexpr (Operator == CacheOperator::kWriteBack) {\n    __stwb(ptr, value);\n  } else if constexpr (Operator == CacheOperator::kGlobal) {\n    __stcg(ptr, value);\n  } else if constexpr (Operator == CacheOperator::kStreaming) {\n    __stcs(ptr, value);\n  } else if constexpr (Operator == CacheOperator::kWriteThrough) {\n    __stwt(ptr, value);\n  } else {\n    *ptr = value;\n  }\n}\n\n// T=uint4, U=uint4, Operator=tachyon::device::gpu::CacheOperator::kStreaming,\n// Stride=1UL]\"\ntemplate <class T, typename U, CacheOperator Operator, size_t Stride>\n__device__ ALWAYS_INLINE constexpr void Store(T* address, T value,\n                                              size_t offset) {\n  static_assert(alignof(T) % alignof(U) == 0);\n  static_assert(sizeof(T) % sizeof(U) == 0);\n  constexpr size_t count = sizeof(T) / sizeof(U);\n  auto pa = reinterpret_cast<U*>(address) + offset;\n  auto pv = reinterpret_cast<const U*>(&value);\n  for (size_t i = 0; i < count; ++i) {\n    StoreSingle<U, Operator>(&pa[i * Stride], pv[i]);\n  }\n}\n\ntemplate <class T, CacheOperator Operator = CacheOperator::kNone,\n          size_t Stride = 1,\n          typename U = std::enable_if_t<sizeof(T) % sizeof(uint4) == 0, uint4>>\n__device__ ALWAYS_INLINE constexpr void Store(\n    T* address, const T& value, const size_t offset = 0,\n    [[maybe_unused]] uint4 _dummy = {}) {\n  Store<T, U, Operator, Stride>(address, value, offset);\n}\n\n}  // namespace tachyon::device::gpu\n\n#endif  // TACHYON_DEVICE_GPU_CUDA_CUDA_MEMORY_H_\n"
  },
  {
    "path": "tachyon/device/gpu/device_perf_info.cc",
    "content": "// Copyright 2020 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/device/gpu/device_perf_info.h\"\n\n#include \"absl/synchronization/mutex.h\"\n\n#include \"tachyon/base/no_destructor.h\"\n\nnamespace tachyon::device::gpu {\n\nnamespace {\n\nstd::optional<DevicePerfInfo> g_device_perf_info;\n\nabsl::Mutex* GetLock() {\n  static base::NoDestructor<absl::Mutex> lock;\n  return lock.get();\n}\n\n}  // namespace\n\nstd::optional<DevicePerfInfo> GetDevicePerfInfo() {\n  absl::MutexLock lock(GetLock());\n  return g_device_perf_info;\n}\n\nvoid SetDevicePerfInfo(const DevicePerfInfo& device_perf_info) {\n  absl::MutexLock lock(GetLock());\n  g_device_perf_info = device_perf_info;\n}\n\n}  // namespace tachyon::device::gpu\n"
  },
  {
    "path": "tachyon/device/gpu/device_perf_info.h",
    "content": "// Copyright 2020 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_DEVICE_GPU_DEVICE_PERF_INFO_H_\n#define TACHYON_DEVICE_GPU_DEVICE_PERF_INFO_H_\n\n#include <cstdint>\n#include <optional>\n\n#include \"tachyon/build/build_config.h\"\n#include \"tachyon/export.h\"\n\nnamespace tachyon::device::gpu {\n\n// These values are persistent to logs. Entries should not be renumbered and\n// numeric values should never be reused.\nenum class IntelGpuGeneration {\n  kNonIntel = 0,\n  kUnknownIntel = 1,  // Intel GPU, but not one of the following generations.\n  kGen4 = 4,\n  kGen5 = 5,\n  kGen6 = 6,\n  kGen7 = 7,\n  kGen8 = 8,\n  kGen9 = 9,\n  kGen10 = 10,\n  kGen11 = 11,\n  kGen12 = 12,\n  kMaxValue = kGen12,\n};\n\nstruct TACHYON_EXPORT DevicePerfInfo {\n  uint32_t total_physical_memory_mb = 0u;\n  uint32_t total_disk_space_mb = 0u;\n  uint32_t hardware_concurrency = 0u;\n\n  IntelGpuGeneration intel_gpu_generation = IntelGpuGeneration::kNonIntel;\n  bool software_rendering = false;\n};\n\n// Thread-safe getter and setter of global instance of DevicePerfInfo.\nTACHYON_EXPORT std::optional<DevicePerfInfo> GetDevicePerfInfo();\nTACHYON_EXPORT void SetDevicePerfInfo(const DevicePerfInfo& device_perf_info);\n\n}  // namespace tachyon::device::gpu\n\n#endif  // TACHYON_DEVICE_GPU_DEVICE_PERF_INFO_H_\n"
  },
  {
    "path": "tachyon/device/gpu/gpu_cudamallocasync_allocator.cc",
    "content": "/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n#include \"tachyon/device/gpu/gpu_cudamallocasync_allocator.h\"\n\n#include <algorithm>\n#include <map>\n#include <memory>\n#include <mutex>  // NOLINT(build/c++11)\n#include <optional>\n#include <string>\n#include <vector>\n\n#ifdef TACHYON_CUDA\n// #include \"tensorflow/compiler/xla/stream_executor/cuda/cuda_activation.h\"\n#include \"third_party/gpus/cuda/include/cuda.h\"\n#endif  // TACHYON_CUDA\n\n#include \"absl/strings/str_cat.h\"\n#include \"absl/strings/str_join.h\"\n// #include \"tensorflow/compiler/xla/stream_executor/device_id_utils.h\"\n// #include \"tensorflow/compiler/xla/stream_executor/gpu/gpu_init.h\"\n// #include \"tensorflow/compiler/xla/stream_executor/stream_executor.h\"\n#include \"tachyon/device/gpu/gpu_logging.h\"\n\nnamespace tachyon::device::gpu {\n\n/*\nTODO(chokobole):\n#if TACHYON_CUDA\nstatic std::string GetCudaErrorMessage(CUresult result) {\n  const char* error;\n  cuGetErrorString(result, &error);\n  const char* name;\n  cuGetErrorName(result, &name);\n  return absl::StrCat(\"CUDA error: \", error ? error : \"<unknown>\", \" (\",\n                      name ? name : \"Unknown\", \")\");\n}\n#endif  // TACHYON_CUDA\n*/\n\nvoid GpuCudaMallocAsyncAllocator::PrintAllocatorStatisticsNoLock() {\n  std::map<size_t, int> size_map_histogram;\n  std::vector<std::string> ptr_size_string;\n  for (auto p : size_map_) {\n    if (VLOG_IS_ON(8)) {\n      ptr_size_string.push_back(\n          absl::StrCat(\"(\", absl::Hex(p.first), \",\", p.second) + \")\");\n    }\n    size_map_histogram[p.second]++;\n  }\n  LOG(ERROR) << \"Histogram of current allocation: (allocation_size_in_bytes, \"\n             << \"nb_allocation_of_that_sizes), ...;\";\n  for (auto p : size_map_histogram) {\n    LOG(ERROR) << p.first << \", \" << p.second;\n  }\n\n  VLOG(8) << \"\\nThe sorted list of (ptr,size):\";\n  VLOG(8) << absl::StrJoin(ptr_size_string, \",\");\n\n#if TACHYON_CUDA_MALLOC_ASYNC_SUPPORTED\n#if CUDA_VERSION >= 11030\n  cuuint64_t mem_reserved_current;\n  if (auto result = cuMemPoolGetAttribute(\n          pool_, CU_MEMPOOL_ATTR_RESERVED_MEM_CURRENT, &mem_reserved_current)) {\n    LOG(ERROR) << \"Error while fetching extra cudaMallocAsync pool attribute: \"\n               << GetCudaErrorMessage(result);\n  }\n  cuuint64_t mem_used_current;\n  if (auto result = cuMemPoolGetAttribute(\n          pool_, CU_MEMPOOL_ATTR_USED_MEM_CURRENT, &mem_used_current)) {\n    LOG(ERROR) << \"Error while fetching extra cudaMallocAsync pool attribute: \"\n               << GetCudaErrorMessage(result);\n  }\n  cuuint64_t mem_reserved_high;\n  if (auto result = cuMemPoolGetAttribute(\n          pool_, CU_MEMPOOL_ATTR_RESERVED_MEM_HIGH, &mem_reserved_high)) {\n    LOG(ERROR) << \"Error while fetching extra cudaMallocAsync pool attribute: \"\n               << GetCudaErrorMessage(result);\n  }\n  cuuint64_t mem_used_high;\n  if (auto result = cuMemPoolGetAttribute(pool_, CU_MEMPOOL_ATTR_USED_MEM_HIGH,\n                                          &mem_used_high)) {\n    LOG(ERROR) << \"Error while fetching extra cudaMallocAsync pool attribute: \"\n               << GetCudaErrorMessage(result);\n  }\n  LOG(ERROR) << \"CU_MEMPOOL_ATTR_RESERVED_MEM_CURRENT: \"\n             << mem_reserved_current;\n  LOG(ERROR) << \"CU_MEMPOOL_ATTR_USED_MEM_CURRENT: \" << mem_used_current;\n  LOG(ERROR) << \"CU_MEMPOOL_ATTR_RESERVED_MEM_HIGH: \" << mem_reserved_high;\n  LOG(ERROR) << \"CU_MEMPOOL_ATTR_USED_MEM_HIGH: \" << mem_used_high;\n#endif\n#endif  // TACHYON_CUDA_MALLOC_ASYNC_SUPPORTED\n}\n\nvoid GpuCudaMallocAsyncAllocator::PrintAllocatorStatistics() {\n  absl::MutexLock lock(&lock_);\n  PrintAllocatorStatisticsNoLock();\n}\n\nstd::atomic<int> GpuCudaMallocAsyncAllocator::number_instantiated_(0);\n\nGpuCudaMallocAsyncAllocator::GpuCudaMallocAsyncAllocator(\n    PlatformDeviceId platform_device_id, size_t pool_size, bool reserve_memory,\n    bool compute_stats)\n    : name_(absl::StrCat(\"gpu_async_\", platform_device_id)),\n      reserve_memory_(reserve_memory) {\n  ++number_instantiated_;\n\n  // Stop clang from complaining about unused private fields when\n  // TACHYON_CUDA_MALLOC_ASYNC_SUPPORTED is not defined.\n  (void)reserve_memory_;\n\n#if TACHYON_CUDA_MALLOC_ASYNC_SUPPORTED\n  stream_exec_ = DeviceIdUtil::ExecutorForPlatformDeviceId(GPUMachineManager(),\n                                                           platform_device_id)\n                     .value();\n  // Initialized here as it only exist if compiled with a recent\n  // enough CUDA.\n  pool_ = nullptr;\n  cuda_stream_ = nullptr;\n  int driverVersion;\n  cuDriverGetVersion(&driverVersion);\n  VLOG(2) << \"DRIVER VERSION: \" << driverVersion;\n  if (driverVersion < 11020) {\n    LOG(FATAL)  // Crash OK.\n        << \"Disable cuda_malloc_async or update your CUDA driver to a version\"\n        << \" compatible with CUDA 11.2 or higher.\"\n        << \" We detected a version compatible with: \" << driverVersion;\n  }\n\n  // WAR an CUDA 11.2 driver bug for multiple-GPU. It currently\n  // request that the context on GPU 0 is initialized. Which isn't the\n  // case for Tachyon+horovod.\n  if (platform_device_id.value() > 0 && driverVersion < 11030) {\n    CUcontext pctx;  // We loose track of it. But this is fine.\n    if (auto result = cuDevicePrimaryCtxRetain(&pctx, 0))\n      LOG(FATAL)  // Crash OK.\n          << \"Failed to retain context: \" << GetCudaErrorMessage(result);\n  }\n\n  cuda::ScopedActivateExecutorContext scoped_activation{stream_exec_};\n\n  // Check the CUDA runtime is recent enough.\n  if (auto status2 = cuDriverGetVersion(&driverVersion)) {\n    LOG(FATAL)  // Crash OK.\n        << \"Error while fetching driver version: \"\n        << GetCudaErrorMessage(status2);\n  }\n\n  // Check that cudaMallocAsync is supported.\n  int cuda_malloc_async_supported;\n  if (auto status =\n          cuDeviceGetAttribute(&cuda_malloc_async_supported,\n                               CU_DEVICE_ATTRIBUTE_MEMORY_POOLS_SUPPORTED,\n                               platform_device_id.value())) {\n    LOG(FATAL)  // Crash OK.\n        << \"On device: \" << platform_device_id.value()\n        << \" Current driver: \" << driverVersion\n        << \". Failed to get device attribute : \" << GetCudaErrorMessage(status);\n  }\n  if (!cuda_malloc_async_supported)\n    LOG(FATAL)  // Crash OK.\n        << \"TF_GPU_ALLOCATOR=cuda_malloc_async isn't currently supported on \"\n        << \"GPU id \" << platform_device_id.value() << \":\"\n        << \" Possible causes: device not supported (request SM60+), driver too \"\n           \"old, \"\n        << \" OS not supported, CUDA version too old(request CUDA11.2+).\";\n\n  if (auto status =\n          cuDeviceGetDefaultMemPool(&pool_, platform_device_id.value()))\n    LOG(FATAL) <<  // Crash OK.\n        \"Failed to get default CUDA pool: \" << GetCudaErrorMessage(status);\n\n  VLOG(1) << Name() << \" CudaMallocAsync initialized on platform: \"\n          << platform_device_id.value() << \" with pool size of: \" << pool_size\n          << \" this ptr: \" << this;\n  uint64_t pool_size_64 = pool_size;\n  if (auto status = cuMemPoolSetAttribute(\n          pool_, CU_MEMPOOL_ATTR_RELEASE_THRESHOLD, &pool_size_64))\n    LOG(FATAL) <<  // Crash OK.\n        \"Failed to set CUDA pool attribute: \" << GetCudaErrorMessage(status);\n\n  if (compute_stats) {\n    stats_ = std::make_unique<AllocatorStats>();\n    stats_->bytes_limit = static_cast<int64_t>(pool_size);\n  }  // If not set, it means we do not compute stats.\n\n  // If in TF_DETERMINISTIC_ALLOCATOR is set, then make the allocator behave\n  // determistically.\n  bool deterministic = false;\n  TF_CHECK_OK(tsl::ReadBoolFromEnvVar(\"TF_DETERMINISTIC_ALLOCATOR\",\n                                      /*default_val=*/false, &deterministic));\n  if (deterministic) {\n    int disable = 0;\n    if (auto status = cuMemPoolSetAttribute(\n            pool_, CU_MEMPOOL_ATTR_REUSE_ALLOW_OPPORTUNISTIC, &disable)) {\n      LOG(FATAL) <<  // Crash OK.\n          \"Failed to set CUDA pool attribute: \" << GetCudaErrorMessage(status);\n    }\n    if (auto status = cuMemPoolSetAttribute(\n            pool_, CU_MEMPOOL_ATTR_REUSE_ALLOW_INTERNAL_DEPENDENCIES,\n            &disable)) {\n      LOG(FATAL) <<  // Crash OK.\n          \"Failed to set CUDA pool attribute: \" << GetCudaErrorMessage(status);\n    }\n  }\n\n  // Set read/write access to all GPUs.\n  static auto* all_pools_ = new std::vector<CUmemoryPool*>();\n  static auto* all_ids_ = new std::vector<tsl::PlatformDeviceId>();\n  DCHECK(all_pools_->size() == all_ids_->size());\n  for (int i = 0; i < all_pools_->size(); ++i) {\n    // Set the current pool access to the previous GPUs.\n    CUmemAccessDesc map;\n    map.flags = CU_MEM_ACCESS_FLAGS_PROT_READWRITE;\n    map.location.id = (*all_ids_)[i].value();\n\n    map.location.type = CU_MEM_LOCATION_TYPE_DEVICE;\n    VLOG(2) << \"Setting access of the current pool to \"\n            << \" location id: \" << map.location.id;\n    int canAccessPeer;\n    if (auto status = cuDeviceCanAccessPeer(\n            &canAccessPeer, platform_device_id.value(), map.location.id)) {\n      pool_ = nullptr;\n      LOG(FATAL)  // Crash OK.\n          << \"cuDeviceCanAccessPeer failed to know if GPU id \"\n          << map.location.id << \" can access GPU id \"\n          << platform_device_id.value() << \": \" << GetCudaErrorMessage(status);\n    }\n    if (canAccessPeer == 1) {\n      if (auto status = cuMemPoolSetAccess(pool_, &map, 1)) {\n        pool_ = nullptr;\n        LOG(FATAL)  // Crash OK.\n            << \"Error when setting access to the pool id: \" << i\n            << \" location id: \" << map.location.id\n            << \" error: \" << GetCudaErrorMessage(status);\n      }\n    }\n\n    // Set the previous pools access to the current GPU.\n    map.location.id = platform_device_id.value();\n\n    VLOG(2) << \"Set access to the pool id: \" << i\n            << \" location id: \" << map.location.id;\n    if (auto status = cuDeviceCanAccessPeer(&canAccessPeer, i,\n                                            platform_device_id.value())) {\n      pool_ = nullptr;\n      LOG(FATAL)  // Crash OK.\n          << \"cuDeviceCanAccessPeer failed: \" << GetCudaErrorMessage(status);\n    }\n    if (canAccessPeer == 1) {\n      if (auto status = cuMemPoolSetAccess(*(*all_pools_)[i], &map, 1)) {\n        pool_ = nullptr;\n        LOG(FATAL)  // Crash OK.\n            << \"Error when setting access to the pool id: \" << i\n            << \" location id: \" << map.location.id\n            << \" error: \" << GetCudaErrorMessage(status);\n      }\n    }\n  }\n  all_pools_->push_back(&pool_);\n  all_ids_->push_back(platform_device_id);\n\n  VLOG(2) << Name() << \" GpuCudaMallocAsyncAllocator PoolSize \" << pool_size;\n#else   // TACHYON_CUDA_MALLOC_ASYNC_SUPPORTED\n  LOG(FATAL) << \"GpuCudaMallocAsyncAllocator requires CUDA 11.2+\";  // Crash OK.\n#endif  // TACHYON_CUDA_MALLOC_ASYNC_SUPPORTED\n}\n\nGpuCudaMallocAsyncAllocator::~GpuCudaMallocAsyncAllocator() {}\n\nvoid* GpuCudaMallocAsyncAllocator::AllocateRaw(size_t alignment,\n                                               size_t num_bytes) {\n#if TACHYON_CUDA_MALLOC_ASYNC_SUPPORTED\n  CHECK(cuda_stream_ != nullptr)\n      << \"A stream must be added to the GpuCudaMallocAsync allocator\";\n  if (pool_ == nullptr) {\n    LOG(FATAL)  // Crash OK.\n        << \"The instantiation of GpuCudaMallocAsyncAllocator failed.\"\n        << \" See previous errors.\";\n  }\n  // The lock is only needed when stats are enabled, but it must be around\n  // the cuMemAllocFromPoolAsync call as well to ensure consistency of the stats\n  // update.\n  std::unique_lock<absl::Mutex> lock(lock_, std::defer_lock);\n  if (stats_) {\n    lock.lock();\n  }\n  cuda::ScopedActivateExecutorContext scoped_activation{stream_exec_};\n  void* ptr = nullptr;\n  if (auto result =\n          cuMemAllocFromPoolAsync(reinterpret_cast<CUdeviceptr*>(&ptr),\n                                  num_bytes, pool_, cuda_stream_)) {\n    size_t free, total;\n    cuMemGetInfo(&free, &total);\n    LOG(ERROR) << Name() << \" cuMemAllocAsync failed to allocate \" << num_bytes\n               << \" bytes: \" << GetCudaErrorMessage(result)\n               << \"\\n Reported by CUDA: Free memory/Total memory: \" << free\n               << \"/\" << total;\n    if (stats_) {\n      LOG(ERROR) << \"Stats: \" << stats_->DebugString();\n      PrintAllocatorStatisticsNoLock();\n    }\n\n    return nullptr;\n  }\n\n  // Update stats.\n  if (stats_) {\n    ++(stats_->num_allocs);\n    stats_->bytes_in_use += num_bytes;\n    if (stats_->bytes_in_use > stats_->peak_bytes_in_use) {\n      VLOG(9) << \"New Peak memory usage of \" << stats_->bytes_in_use\n              << \" bytes.\";\n    }\n    stats_->peak_bytes_in_use =\n        std::max(stats_->peak_bytes_in_use, stats_->bytes_in_use);\n    stats_->largest_alloc_size =\n        std::max<std::size_t>(stats_->largest_alloc_size, num_bytes);\n    bool ptr_inserted = size_map_.emplace(ptr, num_bytes).second;\n    DCHECK(ptr_inserted);\n  }\n  VLOG(10) << Name() << \" Allocated \" << num_bytes << \" at \" << ptr;\n  return ptr;\n#else   // TACHYON_CUDA_MALLOC_ASYNC_SUPPORTED\n  return nullptr;\n#endif  // TACHYON_CUDA_MALLOC_ASYNC_SUPPORTED\n}\nvoid GpuCudaMallocAsyncAllocator::DeallocateRaw(void* ptr) {\n#if TACHYON_CUDA_MALLOC_ASYNC_SUPPORTED\n  if (ptr == nullptr) return;\n  // The lock is only needed when stats are enabled, but it must be around\n  // the cuMemFreeAsync call as well to ensure consistency of the stats update.\n  std::unique_lock<absl::Mutex> lock(lock_, std::defer_lock);\n  if (stats_) {\n    lock.lock();\n  }\n  if (auto result = cuMemFreeAsync(reinterpret_cast<const CUdeviceptr&>(ptr),\n                                   cuda_stream_)) {\n    if (result == CUDA_ERROR_DEINITIALIZED) {\n      // It happens with multi-GPU that Tachyon free the GPU allocation after\n      // the driver is unloaded. It is safe to ignore this error here.\n      // TODO(chokobole): Find how to fix the shutdown steps in Tachyon.\n      VLOG(1) << \"Ignoring CUDA error: \" << GetCudaErrorMessage(result);\n    } else {\n      size_t free, total;\n      cuda::ScopedActivateExecutorContext scoped_activation{stream_exec_};\n      cuMemGetInfo(&free, &total);\n      LOG(ERROR) << \"cudaFreeAsync failed to free \" << ptr << \": \"\n                 << GetCudaErrorMessage(result)\n                 << \"\\n Free memory/Total memory: \" << free << \"/\" << total;\n      if (stats_) {\n        LOG(ERROR) << \"Stats: \" << stats_->DebugString();\n      }\n    }\n  }\n\n  // Updates the stats.\n  if (stats_) {\n    DCHECK(size_map_.contains(ptr));\n    size_t size = size_map_[ptr];\n    stats_->bytes_in_use -= size;\n    size_map_.erase(ptr);\n  }\n\n  VLOG(10) << Name() << \" Freed ptr: \" << ptr;\n#endif  // TACHYON_CUDA_MALLOC_ASYNC_SUPPORTED\n}\n\nbool GpuCudaMallocAsyncAllocator::TracksAllocationSizes() const {\n  return static_cast<bool>(stats_);\n}\n\nsize_t GpuCudaMallocAsyncAllocator::RequestedSize(const void* ptr) const {\n  if (!stats_ || !ptr) return 0;\n  absl::MutexLock l(&lock_);\n  return size_map_.at(ptr);\n}\n\nsize_t GpuCudaMallocAsyncAllocator::AllocatedSize(const void* ptr) const {\n  if (!stats_ || !ptr) return 0;\n  absl::MutexLock l(&lock_);\n  return size_map_.at(ptr);\n}\n\nstd::optional<AllocatorStats> GpuCudaMallocAsyncAllocator::GetStats() {\n  if (!stats_) return std::nullopt;\n  absl::MutexLock l(&lock_);\n  return *stats_;\n}\n\nbool GpuCudaMallocAsyncAllocator::ClearStats() {\n  if (!stats_) return false;\n  absl::MutexLock l(&lock_);\n  stats_->num_allocs = 0;\n  stats_->peak_bytes_in_use = stats_->bytes_in_use;\n  stats_->largest_alloc_size = 0;\n  return true;\n}\n\nvoid GpuCudaMallocAsyncAllocator::SetStreamAndPreallocateMemory(void* stream) {\n#if TACHYON_CUDA_MALLOC_ASYNC_SUPPORTED\n  CUstream new_cuda_stream = *(static_cast<CUstream*>(stream));\n  // We don't need to re-set the CUDA stream if this is the same stream\n  if (cuda_stream_ != nullptr && new_cuda_stream != cuda_stream_) {\n    LOG(FATAL) <<  // Crash OK.\n        \"Trying to set the stream twice. This isn't supported. \";\n  }\n\n  uint64_t pool_size_64 = 0;\n  if (auto status = cuMemPoolGetAttribute(\n          pool_, CU_MEMPOOL_ATTR_RELEASE_THRESHOLD, &pool_size_64)) {\n    LOG(FATAL) <<  // Crash OK.\n        \"Failed to get CUDA pool attribute: \" << GetCudaErrorMessage(status);\n  }\n  cuda_stream_ = new_cuda_stream;\n  int64_t prealloc_size = 0;\n  // TACHYON_CUDA_MALLOC_ASYNC_SUPPORTED_PREALLOC=-1 is a special value that\n  // preallocates the total pool size.\n  TF_CHECK_OK(tsl::ReadInt64FromEnvVar(\n      \"TACHYON_CUDA_MALLOC_ASYNC_SUPPORTED_PREALLOC\", 0, &prealloc_size));\n  if (prealloc_size == -1) {\n    prealloc_size = pool_size_64;\n  } else if (reserve_memory_) {\n    prealloc_size = pool_size_64;\n  }\n\n  if (prealloc_size != 0) {\n    void* ptr = AllocateRaw(0, prealloc_size);\n    DeallocateRaw(ptr);\n    VLOG(2) << Name() << \" GpuCudaMallocAsyncAllocator reserved the pool for \"\n            << prealloc_size << \" bytes\"\n            << \". First ptr: \" << ptr;\n    ClearStats();\n  }\n#endif\n}\n\n}  // namespace tachyon::device::gpu\n"
  },
  {
    "path": "tachyon/device/gpu/gpu_cudamallocasync_allocator.h",
    "content": "/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n#ifndef TACHYON_DEVICE_GPU_GPU_CUDAMALLOCASYNC_ALLOCATOR_H_\n#define TACHYON_DEVICE_GPU_GPU_CUDAMALLOCASYNC_ALLOCATOR_H_\n\n#include <memory>\n#include <optional>\n#include <string>\n\n#include \"absl/base/thread_annotations.h\"\n#include \"absl/container/flat_hash_map.h\"\n// #include \"tensorflow/compiler/xla/stream_executor/stream_executor.h\"\n#include \"absl/synchronization/mutex.h\"\n\n#include \"tachyon/device/allocator.h\"\n#include \"tachyon/device/gpu/platform_device_id.h\"\n\n#if TACHYON_CUDA\n#include \"third_party/gpus/cuda/include/cuda.h\"\n\n#define TF_CUDA_MALLOC_ASYNC_SUPPORTED CUDA_VERSION >= 11020\n#endif  // TACHYON_CUDA\n\nnamespace tachyon::device::gpu {\n\n// An allocator that wraps cudaMallocAsync. It has fewer fragmentation\n// issues then the BFC memory allocator.  The compute-sanitizer tool\n// helps to detect OOB memory errors when using cudaMallocAsync. Use\n// the environment variable `TF_GPU_ALLOCATOR=cuda_malloc_async` to\n// enable it.\n//\n// It needs CUDA 11.2+. When using a container, this only needs the\n// container driver to be 11.2. It has a WAR again a driver bug in\n// multi-GPU setup with CUDA 11.2. The WAR creates an extra context on\n// GPU 0.\n//\n// We configure cudaMallocAsync to grow when more memory is needed\n// instead of preallocating everything up front and to keep a local\n// pool up to pool_size bytes that is never released to other processes.\n// So no other process will \"steal\" the GPU memory already used by the\n// current process. This is to speed up execution and prevent crashes\n// of long-running jobs. Use `reserve_memory=true` if you want to\n// preallocate the full pool_size. You can also use the environment\n// variable `TF_CUDA_MALLOC_ASYNC_SUPPORTED_PREALLOC=nb_bytes` to preallocate\n// that amount of memory. `TF_CUDA_MALLOC_ASYNC_SUPPORTED_PREALLOC=-1` is a\n// special value that preallocate all what the BFC memory allocator\n// would have allocated. This is useful when benchmarking as it doesn't\n// change when driver allocations are done.\n//\n// Here, the pool_size isn't the absolute max as for [Gpu]BFCAllocator.\n// The pool can grow above that up to the total GPU memory.  But the\n// driver can return the excess memory to other processes.\nclass GpuCudaMallocAsyncAllocator : public Allocator {\n public:\n  explicit GpuCudaMallocAsyncAllocator(PlatformDeviceId platform_device_id,\n                                       size_t pool_size,\n                                       bool reserve_memory = false,\n                                       bool compute_stats = true);\n  GpuCudaMallocAsyncAllocator(const GpuCudaMallocAsyncAllocator& other) =\n      delete;\n  GpuCudaMallocAsyncAllocator& operator=(\n      const GpuCudaMallocAsyncAllocator& other) = delete;\n  ~GpuCudaMallocAsyncAllocator() override;\n  std::string Name() override { return name_; }\n  void* AllocateRaw(size_t alignment,\n                    size_t num_bytes) override ABSL_NO_THREAD_SAFETY_ANALYSIS;\n  void DeallocateRaw(void* ptr) override ABSL_NO_THREAD_SAFETY_ANALYSIS;\n\n  bool TracksAllocationSizes() const override;\n\n  size_t RequestedSize(const void* ptr) const override;\n\n  size_t AllocatedSize(const void* ptr) const override;\n\n  std::optional<AllocatorStats> GetStats() override;\n\n  bool ClearStats() override;\n\n  void SetStreamAndPreallocateMemory(void* stream) override;\n\n  // With the right VLOG set, it prints:\n  // - the number of ptr currently allocated per size (histogram).\n  // - each ptr value and its size.\n  // - If CUDA_VERSION >= 11030, print cudaMallocAsync statistics.\n  void PrintAllocatorStatistics();\n\n  static int GetInstantiatedCountTestOnly() { return number_instantiated_; }\n\n  AllocatorMemoryType GetMemoryType() const override {\n    return AllocatorMemoryType::kDevice;\n  }\n\n private:\n  void PrintAllocatorStatisticsNoLock() ABSL_EXCLUSIVE_LOCKS_REQUIRED(lock_);\n\n#if TACHYON_CUDA_MALLOC_ASYNC_SUPPORTED\n  StreamExecutor* stream_exec_;  // Not owned.\n\n  // cudaMallocAsync is stream aware. But Tachyon StreamExecutor use only 1\n  // compute stream and already synchronize with the h2d, d2h and d2d\n  // stream. So we do not need to ask cudaMallocAsync to add extra\n  // synchronization.\n  // Not owned.\n  CUstream cuda_stream_;\n\n  // Not owned. The default pool of the associated GPU.\n  // If null, then the instantiation failed and the first allocation\n  // will return an error.\n  CUmemoryPool pool_;\n#endif  // TACHYON_CUDA_MALLOC_ASYNC_SUPPORTED\n\n  // Just a counter for the number of time this class is instantiated.\n  // Only useful for tests.\n  static std::atomic<int> number_instantiated_;\n\n  std::string name_;\n\n  bool reserve_memory_;\n\n  // Stats.\n  // Structures mutable after construction\n  mutable absl::Mutex lock_;\n  std::unique_ptr<AllocatorStats> stats_ ABSL_PT_GUARDED_BY(lock_);\n  absl::flat_hash_map<const void*, size_t> size_map_ ABSL_GUARDED_BY(lock_);\n};\n\n}  // namespace tachyon::device::gpu\n\n#endif  // TACHYON_DEVICE_GPU_GPU_CUDAMALLOCASYNC_ALLOCATOR_H_\n"
  },
  {
    "path": "tachyon/device/gpu/gpu_device_functions.h",
    "content": "#ifndef TACHYON_DEVICE_GPU_GPU_DEVICE_FUNCTIONS_H_\n#define TACHYON_DEVICE_GPU_GPU_DEVICE_FUNCTIONS_H_\n\n#include <string>\n\n#if TACHYON_CUDA\n#include \"third_party/gpus/cuda/include/cuda.h\"\n#include \"third_party/gpus/cuda/include/cuda_runtime_api.h\"\n#endif  // TACHYON_CUDA\n\n// clang-format off\n#if TACHYON_CUDA\nusing gpuError_t = cudaError_t;\n#define gpuGetLastError cudaGetLastError\n#define gpuGetErrorString cudaGetErrorString\n\n#define gpuDeviceReset cudaDeviceReset\n#define gpuDeviceSynchronize cudaDeviceSynchronize\n\nusing gpuEvent_t = cudaEvent_t;\n#define gpuEventCreate cudaEventCreate\n#define gpuEventCreateWithFlags cudaEventCreateWithFlags\n#define gpuEventDestroy cudaEventDestroy\n#define gpuEventDisableTiming cudaEventDisableTiming\n#define gpuEventElapsedTime cudaEventElapsedTime\n#define gpuEventRecord cudaEventRecord\n#define gpuEventSynchronize cudaEventSynchronize\n\n#if CUDA_VERSION >= 11020  // CUDA 11.2\n// See https://developer.nvidia.com/blog/enhancing-memory-allocation-with-new-cuda-11-2-features/\nusing gpuMemPool_t = cudaMemPool_t;\nusing gpuMemPoolProps = cudaMemPoolProps;\n#define gpuMemPoolCreate cudaMemPoolCreate\n#define gpuMemPoolDestroy cudaMemPoolDestroy\n#define gpuMemPoolSetAttribute cudaMemPoolSetAttribute\n#define gpuMemPoolGetAttribute cudaMemPoolGetAttribute\n#endif\n\nusing gpuStream_t = cudaStream_t;\n#define gpuStreamCreate cudaStreamCreate\n#define gpuStreamCreateWithFlags cudaStreamCreateWithFlags\n#define gpuStreamDestroy cudaStreamDestroy\n#define gpuStreamSynchronize cudaStreamSynchronize\n#define gpuStreamWaitEvent cudaStreamWaitEvent\n#elif TACHYON_USE_ROCM\nusing cudaError = int;\nusing cudaError_t = int;\n#define cudaSuccess 0\n#define cudaGetLastError hipGetLastError\n#define gpuGetLastError hipGetLastError\n#define gpuGetErrorString cudaGetErrorString\nstatic std::string cudaGetErrorString(int err) { return std::to_string(err); }\n\nusing gpuError_t = int;\n\n#define gpuDeviceSynchronize hipDeviceSynchronize\n\nusing gpuEvent_t = hipEvent_t;\n#define gpuEventCreate hipEventCreate\n#define gpuEventCreateWithFlags hipEventCreateWithFlags\n#define gpuEventDestroy hipEventDestroy\n#define gpuEventDisableTiming hipEventDisableTiming\n#define gpuEventElapsedTime hipEventElapsedTime\n#define gpuEventRecord hipEventRecord\n#define gpuEventSynchronize hipEventSynchronize\n\nusing gpuMemPool_t = hipMemPool_t;\nusing gpuMemPoolProps = hipMemPoolProps;\n#define gpuMemPoolCreate hipMemPoolCreate\n#define gpuMemPoolDestroy hipMemPoolDestroy\n#define gpuMemPoolSetAttribute hipMemPoolSetAttribute\n#define gpuMemPoolGetAttribute hipMemPoolGetAttribute\n\nusing gpuStream_t = hipStream_t;\n#define gpuStreamCreate hipStreamCreate\n#define gpuStreamCreateWithFlags hipStreamCreateWithFlags\n#define gpuStreamDestroy hipStreamDestroy\n#define gpuStreamSynchronize hipStreamSynchronize\n#define gpuStreamWaitEvent hipStreamWaitEvent\n#endif\n// clang-format on\n\n#endif  // TACHYON_DEVICE_GPU_GPU_DEVICE_FUNCTIONS_H_\n"
  },
  {
    "path": "tachyon/device/gpu/gpu_driver.cc",
    "content": "#include \"tachyon/device/gpu/gpu_driver.h\"\n\n#include \"tachyon/base/logging.h\"\n\nnamespace tachyon::device::gpu {\n\nstd::string MemorySpaceString(MemorySpace memory_space) {\n  switch (memory_space) {\n    case MemorySpace::kHost:\n      return \"host\";\n    case MemorySpace::kDevice:\n      return \"device\";\n    default:\n      LOG(FATAL) << \"impossible memory space\";\n  }\n}\n\n}  // namespace tachyon::device::gpu\n"
  },
  {
    "path": "tachyon/device/gpu/gpu_driver.h",
    "content": "#ifndef TACHYON_DEVICE_GPU_GPU_DRIVER_H_\n#define TACHYON_DEVICE_GPU_GPU_DRIVER_H_\n\n#include <string>\n\n#include \"tachyon/device/gpu/gpu_types.h\"\n#include \"tachyon/export.h\"\n\nnamespace tachyon::device::gpu {\n\n// Identifies the memory space where an allocation resides. See\n// GpuDriver::GetPointerMemorySpace().\nenum class MemorySpace { kHost, kDevice };\n\n// Returns a casual string, such as \"host\" for the provided memory space.\nTACHYON_EXPORT std::string MemorySpaceString(MemorySpace memory_space);\n\nclass GpuContext;\n\nclass TACHYON_EXPORT GpuDriver {};\n\n// Ensures a context is activated within a scope.\nclass TACHYON_EXPORT ScopedActivateContext {\n public:\n  // Activates the context via cuCtxSetCurrent, if it is not the currently\n  // active context (a la cuCtxGetCurrent). Note the alternative push/pop\n  // mechanism is said by NVIDIA to be relatively slow and deprecated.\n  // http://docs.nvidia.com/cuda/cuda-driver-api/group__CUDA__CTX.html#group__CUDA__CTX_1gbe562ee6258b4fcc272ca6478ca2a2f7\n  explicit ScopedActivateContext(GpuContext* context);\n\n  // Checks that the context has remained activated for the duration of the\n  // scope.\n  ~ScopedActivateContext();\n\n private:\n  GpuContext* to_restore_ = nullptr;\n};\n\n}  // namespace tachyon::device::gpu\n\n#endif  // TACHYON_DEVICE_GPU_GPU_DRIVER_H_\n"
  },
  {
    "path": "tachyon/device/gpu/gpu_enums.h",
    "content": "#ifndef TACHYON_DEVICE_GPU_GPU_ENUMS_H_\n#define TACHYON_DEVICE_GPU_GPU_ENUMS_H_\n\n#if TACHYON_CUDA\n#include \"third_party/gpus/cuda/include/cuda_runtime_api.h\"\n#endif  // TACHYON_CUDA\n\n// clang-format off\n#if TACHYON_CUDA\n#define gpuSuccess cudaSuccess\n\n// cudaMemAllocationHandleType\n#define gpuMemAllocationTypeInvalid cudaMemAllocationTypeInvalid\n#define gpuMemAllocationTypePinned cudaMemAllocationTypePinned\n#define gpuMemAllocationTypeMax cudaMemAllocationTypeMax\n\n// cudaMemHandleType\n#define gpuMemHandleTypeNone cudaMemHandleTypeNone\n#define gpuMemHandleTypePosixFileDescriptor cudaMemHandleTypePosixFileDescriptor\n#define gpuMemHandleTypeWin32 cudaMemHandleTypeWin32\n#define gpuMemHandleTypeWin32Kmt cudaMemHandleTypeWin32Kmt\n\n// cudaMemLocationType\n#define gpuMemLocationTypeInvalid cudaMemLocationTypeInvalid\n#define gpuMemLocationTypeDevice cudaMemLocationTypeDevice\n\n// cudaMemPoolAttr\n#define gpuMemPoolReuseFollowEventDependencies cudaMemPoolReuseFollowEventDependencies\n#define gpuMemPoolReuseAllowOpportunistic cudaMemPoolReuseAllowOpportunistic\n#define gpuMemPoolReuseAllowInternalDependencies cudaMemPoolReuseAllowInternalDependencies\n#define gpuMemPoolAttrReleaseThreshold cudaMemPoolAttrReleaseThreshold\n#define gpuMemPoolAttrReservedMemCurrent cudaMemPoolAttrReservedMemCurrent\n#define gpuMemPoolAttrReservedMemHigh cudaMemPoolAttrReservedMemHigh\n#define gpuMemPoolAttrUsedMemCurrent cudaMemPoolAttrUsedMemCurrent\n#define gpuMemPoolAttrUsedMemHigh cudaMemPoolAttrUsedMemHigh\n\n#elif TACHYON_USE_ROCM\n#define gpuSuccess hipSuccess\n\n// hipMemAllocationHandleType\n#define gpuMemAllocationTypeInvalid hipMemAllocationTypeInvalid\n#define gpuMemAllocationTypePinned hipMemAllocationTypePinned\n#define gpuMemAllocationTypeMax hipMemAllocationTypeMax\n\n// hipMemHandleType\n#define gpuMemHandleTypeNone hipMemHandleTypeNone\n#define gpuMemHandleTypePosixFileDescriptor hipMemHandleTypePosixFileDescriptor\n#define gpuMemHandleTypeWin32 hipMemHandleTypeWin32\n#define gpuMemHandleTypeWin32Kmt hipMemHandleTypeWin32Kmt\n\n// hipMemLocationType\n#define gpuMemLocationTypeInvalid hipMemLocationTypeInvalid\n#define gpuMemLocationTypeDevice hipMemLocationTypeDevice\n\n// hipMemPoolAttr\n#define gpuMemPoolReuseFollowEventDependencies hipMemPoolReuseFollowEventDependencies\n#define gpuMemPoolReuseAllowOpportunistic hipMemPoolReuseAllowOpportunistic\n#define gpuMemPoolReuseAllowInternalDependencies hipMemPoolReuseAllowInternalDependencies\n#define gpuMemPoolAttrReleaseThreshold hipMemPoolAttrReleaseThreshold\n#define gpuMemPoolAttrReservedMemCurrent hipMemPoolAttrReservedMemCurrent\n#define gpuMemPoolAttrReservedMemHigh hipMemPoolAttrReservedMemHigh\n#define gpuMemPoolAttrUsedMemCurrent hipMemPoolAttrUsedMemCurrent\n#define gpuMemPoolAttrUsedMemHigh hipMemPoolAttrUsedMemHigh\n#endif\n// clang-format on\n\n#endif  // TACHYON_DEVICE_GPU_GPU_ENUMS_H_\n"
  },
  {
    "path": "tachyon/device/gpu/gpu_info.cc",
    "content": "// Copyright 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/device/gpu/gpu_info.h\"\n\n#include <stdint.h>\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/build/build_config.h\"\n#include \"tachyon/device/gpu/gpu_util.h\"\n\nnamespace tachyon::device::gpu {\n\nnamespace {\n\nvoid EnumerateGPUDevice(const GPUInfo::GPUDevice& device,\n                        GPUInfo::Enumerator* enumerator) {\n  enumerator->BeginGPUDevice();\n  enumerator->AddInt(\"vendorId\", device.vendor_id);\n  enumerator->AddInt(\"deviceId\", device.device_id);\n#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS)\n  enumerator->AddInt(\"revision\", device.revision);\n#endif\n#if BUILDFLAG(IS_WIN)\n  enumerator->AddInt(\"subSysId\", device.sub_sys_id);\n#endif  // BUILDFLAG(IS_WIN)\n  enumerator->AddBool(\"active\", device.active);\n  enumerator->AddString(\"vendorString\", device.vendor_string);\n  enumerator->AddString(\"deviceString\", device.device_string);\n  enumerator->AddString(\"driverVendor\", device.driver_vendor);\n  enumerator->AddString(\"driverVersion\", device.driver_version);\n  enumerator->AddInt(\"cudaComputeCapabilityMajor\",\n                     device.cuda_compute_capability_major);\n  enumerator->EndGPUDevice();\n}\n\n}  // namespace\n\nGPUInfo::GPUDevice::GPUDevice() = default;\n\nGPUInfo::GPUDevice::GPUDevice(const GPUInfo::GPUDevice& other) = default;\n\nGPUInfo::GPUDevice::GPUDevice(GPUInfo::GPUDevice&& other) noexcept = default;\n\nGPUInfo::GPUDevice::~GPUDevice() noexcept = default;\n\nGPUInfo::GPUDevice& GPUInfo::GPUDevice::operator=(\n    const GPUInfo::GPUDevice& other) = default;\n\nGPUInfo::GPUDevice& GPUInfo::GPUDevice::operator=(\n    GPUInfo::GPUDevice&& other) noexcept = default;\n\nbool GPUInfo::GPUDevice::IsSoftwareRenderer() const {\n  switch (vendor_id) {\n    case 0x0000:  // Info collection failed to identify a GPU\n    case 0xffff:  // Chromium internal flag for software rendering\n    case 0x15ad:  // VMware\n    case 0x1414:  // Microsoft software renderer\n      return true;\n    default:\n      return false;\n  }\n}\n\nGPUInfo::GPUInfo() : optimus(false), amd_switchable(false) {}\n\nGPUInfo::GPUInfo(const GPUInfo& other) = default;\n\nGPUInfo::~GPUInfo() = default;\n\nGPUInfo::GPUDevice& GPUInfo::active_gpu() {\n  return const_cast<GPUInfo::GPUDevice&>(\n      const_cast<const GPUInfo&>(*this).active_gpu());\n}\n\nconst GPUInfo::GPUDevice& GPUInfo::active_gpu() const {\n  if (gpu.active || secondary_gpus.empty()) return gpu;\n  for (const auto& secondary_gpu : secondary_gpus) {\n    if (secondary_gpu.active) return secondary_gpu;\n  }\n  DVLOG(2) << \"No active GPU found, returning primary GPU.\";\n  return gpu;\n}\n\nbool GPUInfo::IsInitialized() const { return gpu.vendor_id != 0; }\n\nunsigned int GPUInfo::GpuCount() const {\n  unsigned int gpu_count = 0;\n  if (!gpu.IsSoftwareRenderer()) ++gpu_count;\n  for (const auto& secondary_gpu : secondary_gpus) {\n    if (!secondary_gpu.IsSoftwareRenderer()) ++gpu_count;\n  }\n  return gpu_count;\n}\n\n#if BUILDFLAG(IS_WIN)\nGPUInfo::GPUDevice* GPUInfo::FindGpuByLuid(DWORD low_part, LONG high_part) {\n  if (gpu.luid.LowPart == low_part && gpu.luid.HighPart == high_part)\n    return &gpu;\n  for (auto& device : secondary_gpus) {\n    if (device.luid.LowPart == low_part && device.luid.HighPart == high_part)\n      return &device;\n  }\n  return nullptr;\n}\n#endif  // BUILDFLAG(IS_WIN)\n\nvoid GPUInfo::EnumerateFields(Enumerator* enumerator) const {\n  struct GPUInfoKnownFields {\n    base::TimeDelta initialization_time;\n    bool optimus;\n    bool amd_switchable;\n    GPUDevice gpu;\n    std::vector<GPUDevice> secondary_gpus;\n    std::string machine_model_name;\n    std::string machine_model_version;\n    uint32_t target_cpu_bits;\n  };\n\n  // If this assert fails then most likely something below needs to be updated.\n  // Note that this assert is only approximate. If a new field is added to\n  // GPUInfo which fits within the current padding then it will not be caught.\n  static_assert(\n      sizeof(GPUInfo) == sizeof(GPUInfoKnownFields),\n      \"fields have changed in GPUInfo, GPUInfoKnownFields must be updated\");\n\n  // Required fields (according to DevTools protocol) first.\n  enumerator->AddString(\"machineModelName\", machine_model_name);\n  enumerator->AddString(\"machineModelVersion\", machine_model_version);\n  EnumerateGPUDevice(gpu, enumerator);\n  for (const auto& secondary_gpu : secondary_gpus)\n    EnumerateGPUDevice(secondary_gpu, enumerator);\n\n  enumerator->BeginAuxAttributes();\n  enumerator->AddTimeDeltaInSecondsF(\"initializationTime\", initialization_time);\n  enumerator->AddBool(\"optimus\", optimus);\n  enumerator->AddBool(\"amdSwitchable\", amd_switchable);\n  enumerator->AddInt(\"targetCpuBits\", static_cast<int>(target_cpu_bits));\n  enumerator->EndAuxAttributes();\n}\n\n}  // namespace tachyon::device::gpu\n"
  },
  {
    "path": "tachyon/device/gpu/gpu_info.h",
    "content": "// Copyright 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_DEVICE_GPU_GPU_INFO_H_\n#define TACHYON_DEVICE_GPU_GPU_INFO_H_\n\n// Provides access to the GPU information for the system\n// on which chrome is currently running.\n\n#include <stdint.h>\n\n#include <optional>\n#include <string>\n#include <vector>\n\n#include \"absl/types/span.h\"\n\n#include \"tachyon/base/time/time.h\"\n#include \"tachyon/build/build_config.h\"\n\nnamespace tachyon::device::gpu {\n\n// These values are persistent to logs. Entries should not be renumbered and\n// numeric values should never be reused.\n// This should match enum IntelGpuSeriesType in\n//  \\tools\\metrics\\histograms\\enums.xml\nenum class IntelGpuSeriesType {\n  kUnknown = 0,\n  // Intel 4th gen\n  kBroadwater = 16,\n  kEaglelake = 17,\n  // Intel 5th gen\n  kIronlake = 18,\n  // Intel 6th gen\n  kSandybridge = 1,\n  // Intel 7th gen\n  kBaytrail = 2,\n  kIvybridge = 3,\n  kHaswell = 4,\n  // Intel 8th gen\n  kCherrytrail = 5,\n  kBroadwell = 6,\n  // Intel 9th gen\n  kApollolake = 7,\n  kSkylake = 8,\n  kGeminilake = 9,\n  kKabylake = 10,\n  kAmberlake = 23,\n  kCoffeelake = 11,\n  kWhiskeylake = 12,\n  kCometlake = 13,\n  // Intel 10th gen\n  kCannonlake = 14,\n  // Intel 11th gen\n  kIcelake = 15,\n  kElkhartlake = 19,\n  kJasperlake = 20,\n  // Intel 12th gen\n  kTigerlake = 21,\n  kRocketlake = 24,\n  kDG1 = 25,\n  kAlderlake = 22,\n  kAlchemist = 26,\n  kRaptorlake = 27,\n  kMeteorlake = 28,\n  // Please also update |gpu_series_map| in process_json.py.\n  kMaxValue = kMeteorlake,\n};\n\nstruct TACHYON_EXPORT GPUInfo {\n  struct TACHYON_EXPORT GPUDevice {\n    GPUDevice();\n    GPUDevice(const GPUDevice& other);\n    GPUDevice(GPUDevice&& other) noexcept;\n    ~GPUDevice() noexcept;\n    GPUDevice& operator=(const GPUDevice& other);\n    GPUDevice& operator=(GPUDevice&& other) noexcept;\n\n    bool IsSoftwareRenderer() const;\n\n    // The DWORD (uint32_t) representing the graphics card vendor id.\n    uint32_t vendor_id = 0u;\n\n    // The DWORD (uint32_t) representing the graphics card device id.\n    // Device ids are unique to vendor, not to one another.\n    uint32_t device_id = 0u;\n\n#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS)\n    // The graphics card revision number.\n    uint32_t revision = 0u;\n#endif\n\n#if BUILDFLAG(IS_WIN)\n    // The graphics card subsystem id.\n    // The lower 16 bits represents the subsystem vendor id.\n    uint32_t sub_sys_id = 0u;\n\n    // The graphics card LUID. This is a unique identifier for the graphics card\n    // that is guaranteed to be unique until the computer is restarted. The LUID\n    // is used over the vendor id and device id because the device id is only\n    // unique relative its vendor, not to each other. If there are more than one\n    // of the same exact graphics card, they all have the same vendor id and\n    // device id but different LUIDs.\n    CHROME_LUID luid;\n#endif  // BUILDFLAG(IS_WIN)\n\n    // The 64-bit ID used for GPU selection by ANGLE_platform_angle_device_id.\n    // On Mac this matches the registry ID of an IOGraphicsAccelerator2 or\n    // AGXAccelerator.\n    // On Windows this matches the concatenated LUID.\n    uint64_t system_device_id = 0ULL;\n\n    // Whether this GPU is the currently used one.\n    // Currently this field is only supported and meaningful on OS X and on\n    // Windows using Angle with D3D11.\n    bool active = false;\n\n    // The strings that describe the GPU.\n    // In Linux these strings are obtained through libpci.\n    // In Win/MacOSX, these two strings are not filled at the moment.\n    // In Android, these are respectively GL_VENDOR and GL_RENDERER.\n    std::string vendor_string;\n    std::string device_string;\n\n    std::string driver_vendor;\n    std::string driver_version;\n\n    // NVIDIA CUDA compute capability, major version. 0 if undetermined. Can be\n    // used to determine the hardware generation that the GPU belongs to.\n    int cuda_compute_capability_major = 0;\n  };\n\n  GPUInfo();\n  GPUInfo(const GPUInfo& other);\n  ~GPUInfo();\n\n  // The currently active gpu.\n  GPUDevice& active_gpu();\n  const GPUDevice& active_gpu() const;\n\n  bool IsInitialized() const;\n\n  unsigned int GpuCount() const;\n\n#if BUILDFLAG(IS_WIN)\n  GPUDevice* FindGpuByLuid(DWORD low_part, LONG high_part);\n#endif  // BUILDFLAG(IS_WIN)\n\n  // The amount of time taken to get from the process starting to the message\n  // loop being pumped.\n  base::TimeDelta initialization_time;\n\n  // Computer has NVIDIA Optimus\n  bool optimus;\n\n  // Computer has AMD Dynamic Switchable Graphics\n  bool amd_switchable;\n\n  // Primary GPU, for example, the discrete GPU in a dual GPU machine.\n  GPUDevice gpu;\n\n  // Secondary GPUs, for example, the integrated GPU in a dual GPU machine.\n  std::vector<GPUDevice> secondary_gpus;\n\n  // The machine model identifier. They can contain any character, including\n  // whitespaces.  Currently it is supported on MacOSX and Android.\n  // Android examples: \"Naxus 5\", \"XT1032\".\n  // On MacOSX, the version is stripped out of the model identifier, for\n  // example, the original identifier is \"MacBookPro7,2\", and we put\n  // \"MacBookPro\" as machine_model_name, and \"7.2\" as machine_model_version.\n  std::string machine_model_name;\n\n  // The version of the machine model. Currently it is supported on MacOSX.\n  // See machine_model_name's comment.\n  std::string machine_model_version;\n\n  // Whether the gpu process is running in a sandbox.\n  bool sandboxed;\n\n#if defined(ARCH_CPU_64_BITS)\n  uint32_t target_cpu_bits = 64;\n#elif defined(ARCH_CPU_32_BITS)\n  uint32_t target_cpu_bits = 32;\n#elif defined(ARCH_CPU_31_BITS)\n  uint32_t target_cpu_bits = 31;\n#endif\n\n  // Note: when adding new members, please remember to update EnumerateFields\n  // in gpu_info.cc.\n\n  // In conjunction with EnumerateFields, this allows the embedder to\n  // enumerate the values in this structure without having to embed\n  // references to its specific member variables. This simplifies the\n  // addition of new fields to this type.\n  class Enumerator {\n   public:\n    // The following methods apply to the \"current\" object. Initially this\n    // is the root object, but calls to BeginGPUDevice/EndGPUDevice and\n    // BeginAuxAttributes/EndAuxAttributes change the object to which these\n    // calls should apply.\n    virtual void AddInt64(const char* name, int64_t value) = 0;\n    virtual void AddInt(const char* name, int value) = 0;\n    virtual void AddString(const char* name, const std::string& value) = 0;\n    virtual void AddBool(const char* name, bool value) = 0;\n    virtual void AddTimeDeltaInSecondsF(const char* name,\n                                        const base::TimeDelta& value) = 0;\n    virtual void AddBinary(const char* name,\n                           absl::Span<const uint8_t> blob) = 0;\n\n    // Markers indicating that a GPUDevice is being described.\n    virtual void BeginGPUDevice() = 0;\n    virtual void EndGPUDevice() = 0;\n\n    // Markers indicating that \"auxiliary\" attributes of the GPUInfo\n    // (according to the DevTools protocol) are being described.\n    virtual void BeginAuxAttributes() = 0;\n    virtual void EndAuxAttributes() = 0;\n\n   protected:\n    virtual ~Enumerator() = default;\n  };\n\n  // Outputs the fields in this structure to the provided enumerator.\n  void EnumerateFields(Enumerator* enumerator) const;\n};\n\n}  // namespace tachyon::device::gpu\n\n#endif  // TACHYON_DEVICE_GPU_GPU_INFO_H_\n"
  },
  {
    "path": "tachyon/device/gpu/gpu_init.cc",
    "content": "/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n#include \"tachyon/device/gpu/gpu_init.h\"\n\n#include <string>\n\n#include \"tachyon/base/logging.h\"\n\nnamespace tachyon::device::gpu {\n\nbool ValidateGPUMachineManager() {\n  NOTIMPLEMENTED();\n  // return MultiPlatformManager::PlatformWithName(GpuPlatformName()).status();\n  return false;\n}\n\nPlatform* GPUMachineManager() {\n  NOTIMPLEMENTED();\n  /*\n  TODO(chokobole):\n  auto result = MultiPlatformManager::PlatformWithName(GpuPlatformName());\n  if (!result.ok()) {\n    LOG(FATAL) << \"Could not find Platform with name \" << GpuPlatformName();\n    return nullptr;\n  }\n  return result.value();\n  */\n  return nullptr;\n}\n\nstd::string GpuPlatformName() {\n#if TACHYON_USE_ROCM\n  return \"ROCM\";\n#else\n  // This function will return \"CUDA\" even when building TF without GPU support\n  // This is done to preserve existing functionality\n  return \"CUDA\";\n#endif\n}\n\n}  // namespace tachyon::device::gpu\n"
  },
  {
    "path": "tachyon/device/gpu/gpu_init.h",
    "content": "/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n#ifndef TACHYON_DEVICE_GPU_GPU_INIT_H_\n#define TACHYON_DEVICE_GPU_GPU_INIT_H_\n\n#include <string>\n\nnamespace tachyon::device::gpu {\n\nclass Platform;\n\n// Initializes the GPU platform and returns true if the GPU\n// platform could be initialized.\nbool ValidateGPUMachineManager();\n\n// Returns the GPU machine manager singleton, creating it and\n// initializing the GPUs on the machine if needed the first time it is\n// called.  Must only be called when there is a valid GPU environment\n// in the process (e.g., ValidateGPUMachineManager() returns true).\nPlatform* GPUMachineManager();\n\n// Returns the string describing the name of the GPU platform in use.\n// This value is \"CUDA\" by default, and\n// \"ROCM\" when TACHYON is built with `--config==rocm`\nstd::string GpuPlatformName();\n\n}  // namespace tachyon::device::gpu\n\n#endif  // TACHYON_DEVICE_GPU_GPU_INIT_H_\n"
  },
  {
    "path": "tachyon/device/gpu/gpu_logging.cc",
    "content": "#include \"tachyon/device/gpu/gpu_logging.h\"\n\nnamespace google {\n\nGpuLogMessage::GpuLogMessage(const char* file_path, int line,\n                             LogSeverity severity, gpuError_t gpu_err)\n    : LogMessage(file_path, line, severity), gpu_err_(gpu_err) {}\n\nGpuLogMessage::~GpuLogMessage() {\n  stream() << \": \" << gpuGetErrorString(gpu_err_);\n}\n\n}  // namespace google\n"
  },
  {
    "path": "tachyon/device/gpu/gpu_logging.h",
    "content": "#ifndef TACHYON_DEVICE_GPU_GPU_LOGGING_H_\n#define TACHYON_DEVICE_GPU_GPU_LOGGING_H_\n\n#include \"tachyon/base/compiler_specific.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/device/gpu/gpu_device_functions.h\"\n#include \"tachyon/device/gpu/gpu_enums.h\"\n\nnamespace google {\n\nclass TACHYON_EXPORT GpuLogMessage : public LogMessage {\n public:\n  GpuLogMessage(const char* file_path, int line, LogSeverity severity,\n                gpuError_t gpu_err);\n\n  GpuLogMessage(const GpuLogMessage&) = delete;\n  GpuLogMessage& operator=(const GpuLogMessage&) = delete;\n\n  ~GpuLogMessage();\n\n private:\n  gpuError_t gpu_err_;\n};\n\n}  // namespace google\n\n#if DCHECK_IS_ON()\n#define GPU_DVLOG_IS_ON(verbose_level) VLOG_IS_ON(verbose_level)\n#else\n#define GPU_DVLOG_IS_ON(verbose_level) 0\n#endif\n\n#define GPU_LOG_STREAM(severity, gpu_err) \\\n  COMPACT_GOOGLE_LOG_EX_##severity(GpuLogMessage, gpu_err).stream()\n#define GPU_VLOG_STREAM(verbose_level, gpu_err) \\\n  ::google::GpuLogMessage(__FILE__, __LINE__, -verbose_level, gpu_err).stream()\n\n#define GPU_LOG(severity, gpu_err) \\\n  LAZY_STREAM(GPU_LOG_STREAM(severity, gpu_err), LOG_IS_ON(severity))\n#define GPU_LOG_IF(severity, condition, gpu_err) \\\n  LAZY_STREAM(GPU_LOG_STREAM(severity, gpu_err), \\\n              LOG_IS_ON(severity) && (condition))\n\n#define GPU_VLOG(verbose_level, gpu_err)               \\\n  LAZY_STREAM(GPU_VLOG_STREAM(verbose_level, gpu_err), \\\n              VLOG_IS_ON(verbose_level))\n#define GPU_VLOG_IF(verbose_level, condition, gpu_err) \\\n  LAZY_STREAM(GPU_VLOG_STREAM(verbose_level, gpu_err), \\\n              VLOG_IS_ON(verbose_level) && (condition))\n\n#define GPU_CHECK(condition, gpu_err)                       \\\n  LAZY_STREAM(GPU_LOG_STREAM(FATAL, gpu_err), !(condition)) \\\n      << \"Check failed: \" #condition << \". \"\n\n#define GPU_DLOG(severity, gpu_err) \\\n  LAZY_STREAM(GPU_LOG_STREAM(severity, gpu_err), DLOG_IS_ON(severity))\n#define GPU_DLOG_IF(severity, condition, gpu_err) \\\n  LAZY_STREAM(GPU_LOG_STREAM(severity, gpu_err),  \\\n              DLOG_IS_ON(severity) && (condition))\n\n#define GPU_DVLOG(verbose_level, gpu_err)              \\\n  LAZY_STREAM(GPU_VLOG_STREAM(verbose_level, gpu_err), \\\n              GPU_DVLOG_IS_ON(verbose_level))\n#define GPU_DVLOG_IF(verbose_level, condition, gpu_err) \\\n  LAZY_STREAM(GPU_VLOG_STREAM(verbose_level, gpu_err),  \\\n              GPU_DVLOG_IS_ON(verbose_level) && (condition))\n\n#define GPU_DCHECK(condition, gpu_err)                                        \\\n  LAZY_STREAM(GPU_LOG_STREAM(FATAL, gpu_err), DCHECK_IS_ON() && !(condition)) \\\n      << \"Check failed: \" #condition << \". \"\n\n#define LOG_IF_GPU_ERROR(x, msg)         \\\n  ({                                     \\\n    gpuError_t error = (x);              \\\n    if (UNLIKELY(error != gpuSuccess)) { \\\n      GPU_LOG(ERROR, error) << msg;      \\\n    }                                    \\\n    error;                               \\\n  })\n\n#define RETURN_AND_LOG_IF_GPU_ERROR(x, msg) \\\n  ({                                        \\\n    gpuError_t error = (x);                 \\\n    if (UNLIKELY(error != gpuSuccess)) {    \\\n      GPU_LOG(ERROR, error) << msg;         \\\n      return error;                         \\\n    }                                       \\\n  })\n\n#define LOG_IF_GPU_LAST_ERROR(msg)        \\\n  ({                                      \\\n    gpuError_t error = gpuGetLastError(); \\\n    if (UNLIKELY(error != gpuSuccess)) {  \\\n      GPU_LOG(ERROR, error) << msg;       \\\n    }                                     \\\n    error;                                \\\n  })\n\n#define GPU_MUST_SUCCEED(x, msg)                  \\\n  ({                                              \\\n    gpuError_t error = (x);                       \\\n    GPU_CHECK(error == gpuSuccess, error) << msg; \\\n  })\n\n#endif  // TACHYON_DEVICE_GPU_GPU_LOGGING_H_\n"
  },
  {
    "path": "tachyon/device/gpu/gpu_memory.cc",
    "content": "#include \"tachyon/device/gpu/gpu_memory.h\"\n\n#if TACHYON_CUDA\n#define gpuMalloc cudaMalloc\n#define gpuMallocHost cudaMallocHost\n#define gpuMallocFromPoolAsync cudaMallocFromPoolAsync\n\n#define gpuFree cudaFree\n#define gpuFreeHost cudaFreeHost\n#define gpuFreeAsync cudaFreeAsync\n\n#define gpuMemcpy cudaMemcpy\n#define gpuMemcpyAsync cudaMemcpyAsync\n#define gpuMemset cudaMemset\n#define gpuMemsetAsync cudaMemsetAsync\n\n#define gpuPointerGetAttributes cudaPointerGetAttributes\n#define gpuMemGetInfo cudaMemGetInfo\n#else\n#define gpuMalloc hipMalloc\n#define gpuMallocHost hipMallocHost\n#define gpuMallocFromPoolAsync hipMallocFromPoolAsync\n\n#define gpuFree hipFree\n#define gpuFreeHost hipFreeHost\n#define gpuFreeAsync hipFreeAsync\n\n#define gpuMemcpy hipMemcpy\n#define gpuMemcpyAsync hipMemcpyAsync\n#define gpuMemset hipMemset\n#define gpuMemsetAsync hipMemsetAsync\n\n#define gpuPointerGetAttributes hipPointerGetAttributes\n#define gpuMemGetInfo hipMemGetInfo\n#endif\n\nnamespace tachyon::device::gpu {\n\nstd::string GpuMemoryTypeToString(GpuMemoryType type) {\n  switch (type) {\n    case GpuMemoryType::kUnregistered:\n      return \"Unregistered\";\n    case GpuMemoryType::kDevice:\n      return \"Device\";\n    case GpuMemoryType::kHost:\n      return \"Host\";\n#if TACHYON_CUDA\n    case GpuMemoryType::kUnified:\n      return \"Unified\";\n#endif  // TACHYON_CUDA\n  }\n  NOTREACHED();\n  return \"\";\n}\n\nstd::ostream& operator<<(std::ostream& os, GpuMemoryType type) {\n  return os << GpuMemoryTypeToString(type);\n}\n\ngpuMemcpyKind ComputeGpuMemcpyKind(GpuMemoryType src_type,\n                                   GpuMemoryType dst_type) {\n  switch (src_type) {\n    case GpuMemoryType::kUnregistered:\n      NOTREACHED();\n    case GpuMemoryType::kDevice:\n      switch (dst_type) {\n        case GpuMemoryType::kUnregistered:\n          NOTREACHED();\n        case GpuMemoryType::kDevice:\n          return gpuMemcpyDeviceToDevice;\n        case GpuMemoryType::kHost:\n          return gpuMemcpyDeviceToHost;\n        case GpuMemoryType::kUnified:\n          return gpuMemcpyDefault;\n      }\n    case GpuMemoryType::kHost:\n      switch (dst_type) {\n        case GpuMemoryType::kUnregistered:\n          NOTREACHED();\n        case GpuMemoryType::kDevice:\n          return gpuMemcpyHostToDevice;\n        case GpuMemoryType::kHost:\n          return gpuMemcpyHostToHost;\n        case GpuMemoryType::kUnified:\n          return gpuMemcpyDefault;\n      }\n    case GpuMemoryType::kUnified: {\n      if (dst_type == GpuMemoryType::kUnregistered) {\n        NOTREACHED();\n      }\n      return gpuMemcpyDefault;\n    }\n  }\n  NOTREACHED();\n  return gpuMemcpyDefault;\n}\n\ngpuError_t GpuMalloc(void** ptr, size_t size) { return gpuMalloc(ptr, size); }\n\ngpuError_t GpuMallocHost(void** ptr, size_t size) {\n  return gpuMallocHost(ptr, size);\n}\n\n#if TACHYON_CUDA\ngpuError_t GpuMallocManaged(void** ptr, size_t size) {\n  return cudaMallocManaged(ptr, size);\n}\n#endif  // TACHYON_CUDA\n\ngpuError_t GpuMallocFromPoolAsync(void** ptr, size_t size, gpuMemPool_t pool,\n                                  gpuStream_t stream) {\n  return gpuMallocFromPoolAsync(ptr, size, pool, stream);\n}\n\nvoid GpuFreeMemory(gpuStream_t stream, void* ptr, GpuMemoryType type) {\n  if (stream) {\n    GPU_MUST_SUCCEED(gpuFreeAsync(ptr, stream), \"Failed gpuFreeAsync()\");\n  } else {\n    switch (type) {\n      case GpuMemoryType::kUnregistered:\n        NOTREACHED();\n      case GpuMemoryType::kDevice:\n        GPU_MUST_SUCCEED(gpuFree(ptr), \"Failed gpuFree()\");\n        break;\n      case GpuMemoryType::kHost:\n        GPU_MUST_SUCCEED(gpuFreeHost(ptr), \"Failed gpuFreeHost()\");\n        break;\n#if TACHYON_CUDA\n      case GpuMemoryType::kUnified:\n        GPU_MUST_SUCCEED(gpuFree(ptr), \"Failed gpuFree()\");\n        break;\n#endif\n    }\n  }\n}\n\ngpuError_t GpuMemset(void* ptr, int value, size_t size) {\n  return gpuMemset(ptr, value, size);\n}\n\ngpuError_t GpuMemsetAsync(void* ptr, int value, size_t size,\n                          cudaStream_t stream) {\n  return gpuMemsetAsync(ptr, value, size, stream);\n}\n\ngpuError_t GpuMemcpy(void* dst, const void* src, size_t size,\n                     gpuMemcpyKind kind) {\n  return gpuMemcpy(dst, src, size, kind);\n}\n\ngpuError_t GpuMemcpyAsync(void* dst, const void* src, size_t size,\n                          gpuMemcpyKind kind, cudaStream_t stream) {\n  return gpuMemcpyAsync(dst, src, size, kind, stream);\n}\n\ngpuError_t GpuPointerGetAttributes(gpuPointerAttributes* attributes,\n                                   const void* ptr) {\n  return gpuPointerGetAttributes(attributes, ptr);\n}\n\ngpuError_t GpuMemGetInfo(size_t* free, size_t* total) {\n  return gpuMemGetInfo(free, total);\n}\n\nsize_t GpuMemLimitInfo(MemoryUsage type) {\n  size_t free_memory, total_memory;\n  size_t available_memory = 0;\n\n  gpuError_t error = GpuMemGetInfo(&free_memory, &total_memory);\n  if (error != gpuSuccess) {\n    GPU_LOG(ERROR, error) << \"Failed GpuMemGetInfo()\";\n    return available_memory;\n  }\n\n  // NOTE(GideokKim): CUDA is not guaranteed to be able to allocate all of the\n  // memory that the OS reports as free returned by |cudaMemGetInfo| function.\n  // Therefore, only a portion of free memory is used.\n  // See\n  // https://docs.nvidia.com/cuda/cuda-runtime-api/group__CUDART__MEMORY.html#group__CUDART__MEMORY_1g376b97f5ab20321ca46f7cfa9511b978\n  available_memory = free_memory;\n  switch (type) {\n    case MemoryUsage::kHigh:\n      return static_cast<size_t>(available_memory * 0.9);\n    case MemoryUsage::kMedium:\n      return static_cast<size_t>(available_memory * 0.8);\n    case MemoryUsage::kLow:\n      return static_cast<size_t>(available_memory * 0.7);\n  }\n  NOTREACHED();\n  return 0;\n}\n\n}  // namespace tachyon::device::gpu\n"
  },
  {
    "path": "tachyon/device/gpu/gpu_memory.h",
    "content": "#ifndef TACHYON_DEVICE_GPU_GPU_MEMORY_H_\n#define TACHYON_DEVICE_GPU_GPU_MEMORY_H_\n\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"absl/types/span.h\"\n\n#include \"tachyon/base/numerics/checked_math.h\"\n#include \"tachyon/device/gpu/gpu_logging.h\"\n#include \"tachyon/export.h\"\n\nnamespace tachyon::device::gpu {\n\n// Memory usage is as follows:\n// kHigh    - 90% of available memory\n// kMedium  - 80% of available memory\n// kLow     - 70% of available memory\nenum class MemoryUsage { kHigh, kMedium, kLow };\n\nenum class GpuMemoryType {\n  kUnregistered,\n  kDevice,\n  kHost,\n#if TACHYON_CUDA\n  kUnified,\n#endif  // TACHYON_CUDA\n};\n\n#if TACHYON_CUDA\nusing gpuMemoryType = cudaMemoryType;\n#define gpuMemoryTypeUnregistered cudaMemoryTypeUnregistered\n#define gpuMemoryTypeHost cudaMemoryTypeHost\n#define gpuMemoryTypeDevice cudaMemoryTypeDevice\n#define gpuMemoryTypeManaged cudaMemoryTypeManaged\n\nusing gpuMemcpyKind = cudaMemcpyKind;\n#define gpuMemcpyHostToHost cudaMemcpyHostToHost\n#define gpuMemcpyHostToDevice cudaMemcpyHostToDevice\n#define gpuMemcpyDeviceToHost cudaMemcpyDeviceToHost\n#define gpuMemcpyDeviceToDevice cudaMemcpyDeviceToDevice\n#define gpuMemcpyDefault cudaMemcpyDefault\n\nusing gpuPointerAttributes = cudaPointerAttributes;\n\n#else\nusing gpuMemoryType = hipMemoryType;\n#define gpuMemoryTypeUnregistered hipMemoryTypeUnregistered\n#define gpuMemoryTypeHost hipMemoryTypeHost\n#define gpuMemoryTypeDevice hipMemoryTypeDevice\n#define gpuMemoryTypeManaged hipMemoryTypeManaged\n\nusing gpuMemcpyKind = hipMemcpyKind;\n#define gpuMemcpyHostToHost hipMemcpyHostToHost\n#define gpuMemcpyHostToDevice hipMemcpyHostToDevice\n#define gpuMemcpyDeviceToHost hipMemcpyDeviceToHost\n#define gpuMemcpyDeviceToDevice hipMemcpyDeviceToDevice\n#define gpuMemcpyDefault hipMemcpyDefault\n\nusing gpuPointerAttributes = hipPointerAttribute_t;\n#endif\n\nTACHYON_EXPORT std::string GpuMemoryTypeToString(GpuMemoryType type);\n\nTACHYON_EXPORT std::ostream& operator<<(std::ostream& os, GpuMemoryType type);\n\nTACHYON_EXPORT gpuMemcpyKind ComputeGpuMemcpyKind(GpuMemoryType src_type,\n                                                  GpuMemoryType dst_type);\n\nTACHYON_EXPORT gpuError_t GpuMalloc(void** ptr, size_t size);\nTACHYON_EXPORT gpuError_t GpuMallocHost(void** ptr, size_t size);\n#if TACHYON_CUDA\nTACHYON_EXPORT gpuError_t GpuMallocManaged(void** ptr, size_t size);\n#endif  // TACHYON_CUDA\nTACHYON_EXPORT gpuError_t GpuMallocFromPoolAsync(void** ptr, size_t size,\n                                                 gpuMemPool_t pool,\n                                                 gpuStream_t stream = nullptr);\n\nTACHYON_EXPORT void GpuFreeMemory(gpuStream_t stream, void* ptr,\n                                  GpuMemoryType type);\n\nTACHYON_EXPORT gpuError_t GpuMemset(void* ptr, int value, size_t size);\nTACHYON_EXPORT gpuError_t GpuMemsetAsync(void* ptr, int value, size_t size,\n                                         gpuStream_t stream = nullptr);\n\nTACHYON_EXPORT gpuError_t GpuMemcpy(void* dst, const void* src, size_t size,\n                                    gpuMemcpyKind kind);\nTACHYON_EXPORT gpuError_t GpuMemcpyAsync(void* dst, const void* src,\n                                         size_t size, gpuMemcpyKind kind,\n                                         gpuStream_t stream = nullptr);\n\nTACHYON_EXPORT gpuError_t\nGpuPointerGetAttributes(gpuPointerAttributes* attributes, const void* ptr);\n\nTACHYON_EXPORT gpuError_t GpuMemGetInfo(size_t* free, size_t* total);\n\nTACHYON_EXPORT size_t GpuMemLimitInfo(MemoryUsage type);\n\n#define COMPUTE_LEN(from, len)                                         \\\n  do {                                                                 \\\n    if (len == 0) {                                                    \\\n      len = (base::CheckedNumeric<size_t>(size_) - from).ValueOrDie(); \\\n    }                                                                  \\\n  } while (false)\n\n#define COMPUTE_FROM_AND_LEN(from, len) \\\n  COMPUTE_LEN(from, len);               \\\n  CHECK((base::CheckedNumeric<size_t>(from) + len).IsValid())\n\ntemplate <typename T>\nclass GpuMemory {\n public:\n  static GpuMemory Malloc(size_t size) {\n    T* ptr = nullptr;\n    GPU_MUST_SUCCEED(\n        GpuMalloc(reinterpret_cast<void**>(&ptr), sizeof(T) * size),\n        \"Failed GpuMalloc()\");\n    return GpuMemory(ptr, size, GpuMemoryType::kDevice);\n  }\n\n  static GpuMemory MallocHost(size_t size) {\n    T* ptr = nullptr;\n    GPU_MUST_SUCCEED(\n        GpuMallocHost(reinterpret_cast<void**>(&ptr), sizeof(T) * size),\n        \"Failed GpuMallocHost()\");\n    return GpuMemory(ptr, size, GpuMemoryType::kHost);\n  }\n\n#if TACHYON_CUDA\n  static GpuMemory MallocManaged(size_t size) {\n    T* ptr = nullptr;\n    GPU_MUST_SUCCEED(cudaMallocManaged(&ptr, sizeof(T) * size),\n                     \"Failed cudaMallocManaged()\");\n    return GpuMemory(ptr, size, GpuMemoryType::kUnified);\n  }\n#endif  // TACHYON_CUDA\n\n  static GpuMemory MallocFromPoolAsync(size_t size, gpuMemPool_t pool,\n                                       gpuStream_t stream) {\n    T* ptr = nullptr;\n    GPU_MUST_SUCCEED(GpuMallocFromPoolAsync(reinterpret_cast<void**>(&ptr),\n                                            sizeof(T) * size, pool, stream),\n                     \"Failed GpuMallocFromPoolAsync()\");\n    return GpuMemory(ptr, size, GpuMemoryType::kDevice, stream);\n  }\n\n  static GpuMemory FromRaw(T* ptr, size_t size, GpuMemoryType memory_type,\n                           gpuStream_t stream = nullptr) {\n    return GpuMemory(ptr, size, memory_type, stream);\n  }\n\n  GpuMemory() = default;\n  GpuMemory(const GpuMemory& other) = delete;\n  GpuMemory& operator=(const GpuMemory& other) = delete;\n  GpuMemory(GpuMemory&& other)\n      : ptr_(std::exchange(other.ptr_, nullptr)),\n        size_(std::exchange(other.size_, 0)),\n        memory_type_(\n            std::exchange(other.memory_type_, GpuMemoryType::kUnregistered)),\n        stream_(std::exchange(other.stream_, nullptr)) {}\n  GpuMemory& operator=(GpuMemory&& other) {\n    ptr_ = std::exchange(other.ptr_, nullptr);\n    size_ = std::exchange(other.size_, 0);\n    memory_type_ =\n        std::exchange(other.memory_type_, GpuMemoryType::kUnregistered);\n    stream_ = std::exchange(other.stream_, nullptr);\n    return *this;\n  }\n  ~GpuMemory() { reset(); }\n\n  size_t size() const { return size_; }\n  gpuStream_t stream() const { return stream_; }\n  GpuMemoryType memory_type() const { return memory_type_; }\n\n  T& operator*() { return *data(); }\n  const T& operator*() const { return *data(); }\n\n  T* operator->() { return data(); }\n  const T* operator->() const { return data(); }\n\n  T* data() { return ptr_; }\n  const T* data() const { return ptr_; }\n\n  T& operator[](size_t idx) { return ptr_[idx]; }\n  const T& operator[](size_t idx) const { return ptr_[idx]; }\n\n  void reset() {\n    if (ptr_) {\n      GpuFreeMemory(stream_, ptr_, memory_type_);\n    }\n    ptr_ = nullptr;\n    size_ = 0;\n    stream_ = nullptr;\n  }\n\n  bool GetAttributes(gpuPointerAttributes* attributes) const {\n    gpuError_t error = GpuPointerGetAttributes(attributes, ptr_);\n    if (error != gpuSuccess) {\n      GPU_LOG(ERROR, error) << \"Failed GpuPointerGetAttributes()\";\n      return false;\n    }\n    return true;\n  }\n\n  bool Memset(int value = 0, size_t from = 0, size_t len = 0) {\n    COMPUTE_FROM_AND_LEN(from, len);\n    gpuError_t error = GpuMemset(ptr_ + from, value, sizeof(T) * len);\n    if (error != gpuSuccess) {\n      GPU_LOG(ERROR, error) << \"Failed GpuMemset()\";\n      return false;\n    }\n    return true;\n  }\n\n  bool MemsetAsync(int value, gpuStream_t stream = nullptr, size_t from = 0,\n                   size_t len = 0) {\n    COMPUTE_FROM_AND_LEN(from, len);\n    gpuError_t error =\n        GpuMemsetAsync(ptr_ + from, value, sizeof(T) * len, stream);\n    if (error != gpuSuccess) {\n      GPU_LOG(ERROR, error) << \"Failed GpuMemsetAsync()\";\n      return false;\n    }\n    return true;\n  }\n\n  bool CopyTo(void* dst, GpuMemoryType dst_memory_type, size_t from = 0,\n              size_t len = 0) const {\n    COMPUTE_FROM_AND_LEN(from, len);\n    gpuError_t error =\n        GpuMemcpy(dst, ptr_ + from, sizeof(T) * len,\n                  ComputeGpuMemcpyKind(memory_type_, dst_memory_type));\n    if (error != gpuSuccess) {\n      GPU_LOG(ERROR, error) << \"Failed GpuMemcpy()\";\n      return false;\n    }\n    return true;\n  }\n\n  template <typename U>\n  bool CopyTo(const GpuMemory<U>& dst_memory, size_t from = 0, size_t len = 0) {\n    return CopyTo(dst_memory.data(), dst_memory.memory_type(), from, len);\n  }\n\n  bool CopyToAsync(void* dst, GpuMemoryType dst_memory_type,\n                   gpuStream_t stream = nullptr, size_t from = 0,\n                   size_t len = 0) const {\n    COMPUTE_FROM_AND_LEN(from, len);\n    gpuError_t error = GpuMemcpyAsync(\n        dst, ptr_ + from, sizeof(T) * len,\n        ComputeGpuMemcpyKind(memory_type_, dst_memory_type), stream);\n    if (error != gpuSuccess) {\n      GPU_LOG(ERROR, error) << \"Failed GpuMemcpyAsync()\";\n      return false;\n    }\n    return true;\n  }\n\n  template <typename U>\n  bool CopyToAsync(const GpuMemory<U>& dst_memory, gpuStream_t stream = nullptr,\n                   size_t from = 0, size_t len = 0) {\n    return CopyToAsync(dst_memory.data(), dst_memory.memory_type(), stream,\n                       from, len);\n  }\n\n  bool CopyFrom(const void* src, GpuMemoryType src_memory_type, size_t from = 0,\n                size_t len = 0) {\n    COMPUTE_FROM_AND_LEN(from, len);\n    gpuError_t error =\n        GpuMemcpy(ptr_ + from, src, sizeof(T) * len,\n                  ComputeGpuMemcpyKind(src_memory_type, memory_type_));\n    if (error != gpuSuccess) {\n      GPU_LOG(ERROR, error) << \"Failed GpuMemcpy()\";\n      return false;\n    }\n    return true;\n  }\n\n  template <typename U>\n  bool CopyFrom(const GpuMemory<U>& src_memory, size_t from = 0,\n                size_t len = 0) {\n    return CopyFrom(src_memory.data(), src_memory.memory_type(), from, len);\n  }\n\n  bool CopyFromAsync(const void* src, GpuMemoryType src_memory_type,\n                     gpuStream_t stream = nullptr, size_t from = 0,\n                     size_t len = 0) {\n    COMPUTE_FROM_AND_LEN(from, len);\n    gpuError_t error = GpuMemcpyAsync(\n        ptr_ + from, src, sizeof(T) * len,\n        ComputeGpuMemcpyKind(src_memory_type, memory_type_), stream);\n    if (error != gpuSuccess) {\n      GPU_LOG(ERROR, error) << \"Failed GpuMemcpyAsync()\";\n      return false;\n    }\n    return true;\n  }\n\n  template <typename U>\n  bool CopyFromAsync(const GpuMemory<U>& src_memory,\n                     gpuStream_t stream = nullptr, size_t from = 0,\n                     size_t len = 0) {\n    return CopyFromAsync(src_memory.data(), src_memory.memory_type(), stream,\n                         from, len);\n  }\n\n  template <typename R = T>\n  bool ToStdVector(std::vector<R>* ret, size_t from = 0, size_t len = 0) const {\n    COMPUTE_LEN(from, len);\n    ret->resize(len);\n    return CopyTo(ret->data(), GpuMemoryType::kHost, from, len);\n  }\n\n  template <typename R = T>\n  bool ToStdVectorAsync(std::vector<R>* ret, gpuStream_t stream = nullptr,\n                        size_t from = 0, size_t len = 0) const {\n    COMPUTE_LEN(from, len);\n    ret->resize(len);\n    return CopyToAsync(ret->data(), GpuMemoryType::kHost, stream, from, len);\n  }\n\n  template <typename R = T>\n  absl::Span<R> ToSpan(size_t from = 0, size_t len = 0) const {\n    COMPUTE_FROM_AND_LEN(from, len);\n    return absl::Span(reinterpret_cast<R*>(ptr_ + from), len);\n  }\n\n private:\n  GpuMemory(T* ptr, size_t size, GpuMemoryType memory_type,\n            gpuStream_t stream = nullptr)\n      : ptr_(ptr), size_(size), memory_type_(memory_type), stream_(stream) {}\n\n  T* ptr_ = nullptr;\n  size_t size_ = 0;\n  GpuMemoryType memory_type_ = GpuMemoryType::kUnregistered;\n  gpuStream_t stream_ = nullptr;\n};\n\n#undef COMPUTE_FROM_AND_LEN\n#undef COMPUTE_LEN\n\n}  // namespace tachyon::device::gpu\n\n#endif  // TACHYON_DEVICE_GPU_GPU_MEMORY_H_\n"
  },
  {
    "path": "tachyon/device/gpu/gpu_memory_unittest.cc",
    "content": "#include \"tachyon/device/gpu/gpu_memory.h\"\n\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/random.h\"\n#include \"tachyon/device/gpu/scoped_mem_pool.h\"\n\nnamespace tachyon::device::gpu {\n\nTEST(GpuMemoryTest, Malloc) {\n  auto memory = GpuMemory<int>::Malloc(512);\n  gpuPointerAttributes attr;\n  ASSERT_TRUE(memory.GetAttributes(&attr));\n  ASSERT_EQ(attr.type, gpuMemoryTypeDevice);\n}\n\nTEST(GpuMemoryTest, MallocHost) {\n  auto memory = GpuMemory<int>::MallocHost(512);\n  gpuPointerAttributes attr;\n  ASSERT_TRUE(memory.GetAttributes(&attr));\n  ASSERT_EQ(attr.type, gpuMemoryTypeHost);\n}\n\n#if TACHYON_CUDA\nTEST(GpuMemoryTest, MallocManaged) {\n  auto memory = GpuMemory<int>::MallocManaged(512);\n  gpuPointerAttributes attr;\n  ASSERT_TRUE(memory.GetAttributes(&attr));\n  ASSERT_EQ(attr.type, gpuMemoryTypeManaged);\n}\n#endif  // TACHYON_CUDA\n\nTEST(GpuMemoryTest, Memset) {\n  auto memory = GpuMemory<uint8_t>::Malloc(512);\n  int value = 3;\n  size_t from = 10;\n  size_t len = 400;\n\n  ASSERT_TRUE(memory.Memset(value, from, len));\n\n  std::vector<uint8_t> vec;\n  vec.resize(512);\n  memory.CopyTo(vec.data(), GpuMemoryType::kHost);\n\n  GPU_MUST_SUCCEED(gpuDeviceSynchronize(), \"\");\n\n  for (size_t i = 0; i < vec.size(); ++i) {\n    if (i >= from && i < (from + len)) {\n      ASSERT_EQ(vec[i], value);\n    } else {\n      ASSERT_EQ(vec[i], 0);\n    }\n  }\n}\n\nTEST(GpuMemoryTest, CopyFrom) {\n  std::vector<GpuMemory<int>> memories;\n  memories.push_back(GpuMemory<int>::Malloc(512));\n  memories.push_back(GpuMemory<int>::MallocHost(512));\n#if TACHYON_CUDA\n  memories.push_back(GpuMemory<int>::MallocManaged(512));\n#endif  // TACHYON_CUDA\n\n  {\n    std::vector<int> host_memory = base::CreateVector(\n        512, []() { return base::Uniform(base::Range<int>::From(0)); });\n    std::vector<std::vector<int>> results;\n    results.resize(memories.size());\n    for (size_t i = 0; i < memories.size(); ++i) {\n      ASSERT_TRUE(\n          memories[i].CopyFrom(host_memory.data(), GpuMemoryType::kHost));\n      ASSERT_TRUE(memories[i].ToStdVector(&results[i]));\n    }\n\n    GPU_MUST_SUCCEED(gpuDeviceSynchronize(), \"\");\n    for (size_t i = 0; i < memories.size(); ++i) {\n      EXPECT_EQ(results[i], host_memory);\n    }\n  }\n  // TODO(chokobole): Create random device random memory\n#if TACHYON_CUDA\n  {\n    auto unified_memory = GpuMemory<int>::MallocManaged(512);\n    absl::Span<int> unified_memory_view(unified_memory.data(), 512);\n    for (int& v : unified_memory_view) {\n      v = base::Uniform(base::Range<int>::From(0));\n    }\n    std::vector<std::vector<int>> results;\n    results.resize(memories.size());\n    for (size_t i = 0; i < memories.size(); ++i) {\n      ASSERT_TRUE(memories[i].CopyFrom(unified_memory_view.data(),\n                                       GpuMemoryType::kUnified));\n      ASSERT_TRUE(memories[i].ToStdVector(&results[i]));\n    }\n\n    GPU_MUST_SUCCEED(gpuDeviceSynchronize(), \"\");\n    for (size_t i = 0; i < memories.size(); ++i) {\n      EXPECT_EQ(results[i], unified_memory_view);\n    }\n  }\n#endif  // TACHYON_CUDA\n}\n\n}  // namespace tachyon::device::gpu\n"
  },
  {
    "path": "tachyon/device/gpu/gpu_types.h",
    "content": "/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n// GPU (ROCm / CUDA) specific type handle resolution\n\n#ifndef TACHYON_DEVICE_GPU_GPU_TYPES_H_\n#define TACHYON_DEVICE_GPU_GPU_TYPES_H_\n\n#if TACHYON_USE_ROCM\n\n#define __HIP_DISABLE_CPP_FUNCTIONS__\n\n#include \"rocm/include/hip/hip_complex.h\"\n#include \"rocm/include/hip/hip_runtime.h\"\n#include \"rocm/include/hiprand/hiprand.h\"\n\n#else  // CUDA\n\n#include \"third_party/gpus/cuda/include/cuComplex.h\"\n#include \"third_party/gpus/cuda/include/cuda.h\"\n\n// cannot include curand.h here\n//   because it triggers the #error in cuda/cuda_gpu_executor.cc\n//     (because curand.h includes cuda_runtime.h)\n// so explicitly adding the lone typedef we need from that file\ntypedef struct curandGenerator_st* curandGenerator_t;\n\n#endif\n\nnamespace tachyon::device::gpu {\n\n#if TACHYON_USE_ROCM\n\nusing GpuContextHandle = hipCtx_t;\nusing GpuStreamHandle = hipStream_t;\nusing GpuEventHandle = hipEvent_t;\nusing GpuFunctionHandle = hipFunction_t;\nusing GpuFunctionAttribute = hipDeviceAttribute_t;  // not a typo!\nusing GpuDeviceHandle = hipDevice_t;\nusing GpuDevicePtr = hipDeviceptr_t;\nusing GpuDeviceAttribute = hipDeviceAttribute_t;\nusing GpuDeviceProperty = hipDeviceProp_t;\nusing GpuModuleHandle = hipModule_t;\nusing GpuStatus = hipError_t;\nusing GpuFuncCachePreference = hipFuncCache_t;\nusing GpuSharedMemConfig = hipSharedMemConfig;\nusing GpuComplexType = hipComplex;\nusing GpuDoubleComplexType = hipDoubleComplex;\nusing GpuRngHandle = hiprandGenerator_t;\n\n#else  // CUDA\n\nusing GpuContextHandle = CUcontext;\nusing GpuStreamHandle = CUstream;\nusing GpuEventHandle = CUevent;\nusing GpuFunctionHandle = CUfunction;\nusing GpuFunctionAttribute = CUfunction_attribute;\nusing GpuDeviceHandle = CUdevice;\nusing GpuDevicePtr = CUdeviceptr;\nusing GpuDeviceAttribute = CUdevice_attribute;\nusing GpuDeviceProperty = CUdevprop;\nusing GpuModuleHandle = CUmodule;\nusing GpuStatus = CUresult;\nusing GpuFuncCachePreference = CUfunc_cache;\nusing GpuSharedMemConfig = CUsharedconfig;\nusing GpuComplexType = cuComplex;\nusing GpuDoubleComplexType = cuDoubleComplex;\nusing GpuRngHandle = curandGenerator_t;\n\n#endif\n\n}  // namespace tachyon::device::gpu\n\n#endif  // TACHYON_DEVICE_GPU_GPU_TYPES_H_\n"
  },
  {
    "path": "tachyon/device/gpu/gpu_util.cc",
    "content": "// Copyright 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"tachyon/device/gpu/gpu_util.h\"\n\n#include <vector>\n\n#include \"tachyon/base/strings/string_number_conversions.h\"\n#include \"tachyon/device/gpu/device_perf_info.h\"\n#include \"tachyon/device/gpu/gpu_info.h\"\n\nnamespace tachyon::device::gpu {\n\nIntelGpuSeriesType GetIntelGpuSeriesType(uint32_t vendor_id,\n                                         uint32_t device_id) {\n  // Note that this function's output should only depend on vendor_id and\n  // device_id of a GPU. This is because we record a histogram on the output\n  // and we don't want to expose an extra bit other than the already recorded\n  // vendor_id and device_id.\n  if (vendor_id == 0x8086) {  // Intel\n    // The device IDs of Intel GPU are based on following Mesa source files:\n    // include/pci_ids/i965_pci_ids.h and iris_pci_ids.h\n    uint32_t masked_device_id = device_id & 0xFF00;\n    switch (masked_device_id) {\n      case 0x2900:\n        return IntelGpuSeriesType::kBroadwater;\n      case 0x2A00:\n        if (device_id == 0x2A02 || device_id == 0x2A12)\n          return IntelGpuSeriesType::kBroadwater;\n        if (device_id == 0x2A42) return IntelGpuSeriesType::kEaglelake;\n        break;\n      case 0x2E00:\n        return IntelGpuSeriesType::kEaglelake;\n      case 0x0000:\n        return IntelGpuSeriesType::kIronlake;\n      case 0x0100:\n        if (device_id == 0x0152 || device_id == 0x0156 || device_id == 0x015A ||\n            device_id == 0x0162 || device_id == 0x0166 || device_id == 0x016A)\n          return IntelGpuSeriesType::kIvybridge;\n        if (device_id == 0x0155 || device_id == 0x0157)\n          return IntelGpuSeriesType::kBaytrail;\n        return IntelGpuSeriesType::kSandybridge;\n      case 0x0F00:\n        return IntelGpuSeriesType::kBaytrail;\n      case 0x0A00:\n        if (device_id == 0x0A84) return IntelGpuSeriesType::kApollolake;\n        return IntelGpuSeriesType::kHaswell;\n      case 0x0400:\n      case 0x0C00:\n      case 0x0D00:\n        return IntelGpuSeriesType::kHaswell;\n      case 0x2200:\n        return IntelGpuSeriesType::kCherrytrail;\n      case 0x1600:\n        return IntelGpuSeriesType::kBroadwell;\n      case 0x5A00:\n        if (device_id == 0x5A85 || device_id == 0x5A84)\n          return IntelGpuSeriesType::kApollolake;\n        return IntelGpuSeriesType::kCannonlake;\n      case 0x1900:\n        return IntelGpuSeriesType::kSkylake;\n      case 0x1A00:\n        return IntelGpuSeriesType::kApollolake;\n      case 0x3100:\n        return IntelGpuSeriesType::kGeminilake;\n      case 0x5900:\n        if (device_id == 0x591C) return IntelGpuSeriesType::kAmberlake;\n        return IntelGpuSeriesType::kKabylake;\n      case 0x8700:\n        if (device_id == 0x87C0) return IntelGpuSeriesType::kKabylake;\n        if (device_id == 0x87CA) return IntelGpuSeriesType::kCoffeelake;\n        break;\n      case 0x3E00:\n        if (device_id == 0x3EA0 || device_id == 0x3EA1 || device_id == 0x3EA2 ||\n            device_id == 0x3EA4 || device_id == 0x3EA3)\n          return IntelGpuSeriesType::kWhiskeylake;\n        return IntelGpuSeriesType::kCoffeelake;\n      case 0x9B00:\n        return IntelGpuSeriesType::kCometlake;\n      case 0x8A00:\n        return IntelGpuSeriesType::kIcelake;\n      case 0x4500:\n        return IntelGpuSeriesType::kElkhartlake;\n      case 0x4E00:\n        return IntelGpuSeriesType::kJasperlake;\n      case 0x9A00:\n        return IntelGpuSeriesType::kTigerlake;\n      case 0x4c00:\n        return IntelGpuSeriesType::kRocketlake;\n      case 0x4900:\n        return IntelGpuSeriesType::kDG1;\n      case 0x4600:\n        return IntelGpuSeriesType::kAlderlake;\n      case 0x4F00:\n      case 0x5600:\n        return IntelGpuSeriesType::kAlchemist;\n      case 0xa700:\n        return IntelGpuSeriesType::kRaptorlake;\n      case 0x7d00:\n        return IntelGpuSeriesType::kMeteorlake;\n      default:\n        break;\n    }\n  }\n  return IntelGpuSeriesType::kUnknown;\n}\n\nstd::string GetIntelGpuGeneration(uint32_t vendor_id, uint32_t device_id) {\n  if (vendor_id == 0x8086) {\n    IntelGpuSeriesType gpu_series = GetIntelGpuSeriesType(vendor_id, device_id);\n    switch (gpu_series) {\n      case IntelGpuSeriesType::kBroadwater:\n      case IntelGpuSeriesType::kEaglelake:\n        return \"4\";\n      case IntelGpuSeriesType::kIronlake:\n        return \"5\";\n      case IntelGpuSeriesType::kSandybridge:\n        return \"6\";\n      case IntelGpuSeriesType::kBaytrail:\n      case IntelGpuSeriesType::kIvybridge:\n      case IntelGpuSeriesType::kHaswell:\n        return \"7\";\n      case IntelGpuSeriesType::kCherrytrail:\n      case IntelGpuSeriesType::kBroadwell:\n        return \"8\";\n      case IntelGpuSeriesType::kApollolake:\n      case IntelGpuSeriesType::kSkylake:\n      case IntelGpuSeriesType::kGeminilake:\n      case IntelGpuSeriesType::kKabylake:\n      case IntelGpuSeriesType::kAmberlake:\n      case IntelGpuSeriesType::kCoffeelake:\n      case IntelGpuSeriesType::kWhiskeylake:\n      case IntelGpuSeriesType::kCometlake:\n        return \"9\";\n      case IntelGpuSeriesType::kCannonlake:\n        return \"10\";\n      case IntelGpuSeriesType::kIcelake:\n      case IntelGpuSeriesType::kElkhartlake:\n      case IntelGpuSeriesType::kJasperlake:\n        return \"11\";\n      case IntelGpuSeriesType::kTigerlake:\n      case IntelGpuSeriesType::kRocketlake:\n      case IntelGpuSeriesType::kDG1:\n      case IntelGpuSeriesType::kAlderlake:\n      case IntelGpuSeriesType::kAlchemist:\n      case IntelGpuSeriesType::kRaptorlake:\n      case IntelGpuSeriesType::kMeteorlake:\n        return \"12\";\n      default:\n        break;\n    }\n  }\n  return \"\";\n}\n\nIntelGpuGeneration GetIntelGpuGeneration(const GPUInfo& gpu_info) {\n  const uint32_t kIntelVendorId = 0x8086;\n  IntelGpuGeneration latest = IntelGpuGeneration::kNonIntel;\n  std::vector<uint32_t> intel_device_ids;\n  if (gpu_info.gpu.vendor_id == kIntelVendorId)\n    intel_device_ids.push_back(gpu_info.gpu.device_id);\n  for (const auto& gpu : gpu_info.secondary_gpus) {\n    if (gpu.vendor_id == kIntelVendorId)\n      intel_device_ids.push_back(gpu.device_id);\n  }\n  if (intel_device_ids.empty()) return latest;\n  latest = IntelGpuGeneration::kUnknownIntel;\n  for (uint32_t device_id : intel_device_ids) {\n    std::string gen_str = gpu::GetIntelGpuGeneration(kIntelVendorId, device_id);\n    int gen_int = 0;\n    if (gen_str.empty() || !base::StringToInt(gen_str, &gen_int)) continue;\n    DCHECK_GE(gen_int, static_cast<int>(IntelGpuGeneration::kUnknownIntel));\n    DCHECK_LE(gen_int, static_cast<int>(IntelGpuGeneration::kMaxValue));\n    if (gen_int > static_cast<int>(latest))\n      latest = static_cast<IntelGpuGeneration>(gen_int);\n  }\n  return latest;\n}\n\n}  // namespace tachyon::device::gpu\n"
  },
  {
    "path": "tachyon/device/gpu/gpu_util.h",
    "content": "// Copyright 2012 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef TACHYON_DEVICE_GPU_GPU_UTIL_H_\n#define TACHYON_DEVICE_GPU_GPU_UTIL_H_\n\n#include <stdint.h>\n\n#include <string>\n\n#include \"tachyon/build/build_config.h\"\n#include \"tachyon/export.h\"\n\nnamespace tachyon::device::gpu {\n\nstruct DevicePerfInfo;\nstruct GPUInfo;\nstruct GpuPreferences;\nenum class IntelGpuSeriesType;\nenum class IntelGpuGeneration;\n\nTACHYON_EXPORT IntelGpuSeriesType GetIntelGpuSeriesType(uint32_t vendor_id,\n                                                        uint32_t device_id);\n\nTACHYON_EXPORT std::string GetIntelGpuGeneration(uint32_t vendor_id,\n                                                 uint32_t device_id);\n\n// If multiple Intel GPUs are detected, this returns the latest generation.\nTACHYON_EXPORT IntelGpuGeneration\nGetIntelGpuGeneration(const GPUInfo& gpu_info);\n\n}  // namespace tachyon::device::gpu\n\n#endif  // TACHYON_DEVICE_GPU_GPU_UTIL_H_\n"
  },
  {
    "path": "tachyon/device/gpu/platform_device_id.h",
    "content": "#ifndef TACHYON_DEVICE_GPU_PLATFORM_DEVICE_ID_H_\n#define TACHYON_DEVICE_GPU_PLATFORM_DEVICE_ID_H_\n\n#include <stdint.h>\n\nnamespace tachyon::device::gpu {\n\nusing PlatformDeviceId = int32_t;\n\n}\n\n#endif  // TACHYON_DEVICE_GPU_PLATFORM_DEVICE_ID_H_\n"
  },
  {
    "path": "tachyon/device/gpu/scoped_event.cc",
    "content": "#include \"tachyon/device/gpu/scoped_event.h\"\n\nnamespace tachyon::device::gpu {\n\nScopedEvent CreateEvent() {\n  gpuEvent_t event = nullptr;\n  GPU_MUST_SUCCEED(gpuEventCreate(&event), \"Failed gpuEventCreate()\");\n  return ScopedEvent(event);\n}\n\nScopedEvent CreateEventWithFlags(unsigned int flags) {\n  gpuEvent_t event = nullptr;\n  GPU_MUST_SUCCEED(gpuEventCreateWithFlags(&event, flags),\n                   \"Failed gpuEventCreateWithFlags()\");\n  return ScopedEvent(event);\n}\n\n}  // namespace tachyon::device::gpu\n"
  },
  {
    "path": "tachyon/device/gpu/scoped_event.h",
    "content": "#ifndef TACHYON_DEVICE_GPU_SCOPED_EVENT_H_\n#define TACHYON_DEVICE_GPU_SCOPED_EVENT_H_\n\n#include <memory>\n#include <type_traits>\n\n#include \"tachyon/device/gpu/gpu_logging.h\"\n#include \"tachyon/export.h\"\n\nnamespace tachyon::device::gpu {\n\nstruct TACHYON_EXPORT EventDestroyer {\n  void operator()(gpuEvent_t event) const {\n    GPU_MUST_SUCCEED(gpuEventDestroy(event), \"Failed gpuEventDestroy()\");\n  }\n};\n\nusing ScopedEvent =\n    std::unique_ptr<std::remove_pointer_t<gpuEvent_t>, EventDestroyer>;\n\nTACHYON_EXPORT ScopedEvent CreateEvent();\nTACHYON_EXPORT ScopedEvent CreateEventWithFlags(unsigned int flags);\n\n}  // namespace tachyon::device::gpu\n\n#endif  // TACHYON_DEVICE_GPU_SCOPED_EVENT_H_\n"
  },
  {
    "path": "tachyon/device/gpu/scoped_mem_pool.cc",
    "content": "#include \"tachyon/device/gpu/scoped_mem_pool.h\"\n\nnamespace tachyon::device::gpu {\n\nScopedMemPool CreateMemPool(const gpuMemPoolProps* pool_props) {\n  gpuMemPool_t mem_pool = nullptr;\n  GPU_MUST_SUCCEED(gpuMemPoolCreate(&mem_pool, pool_props),\n                   \"Failed gpuMemPoolCreate()\");\n  return ScopedMemPool(mem_pool);\n}\n\n}  // namespace tachyon::device::gpu\n"
  },
  {
    "path": "tachyon/device/gpu/scoped_mem_pool.h",
    "content": "#ifndef TACHYON_DEVICE_GPU_SCOPED_MEM_POOL_H_\n#define TACHYON_DEVICE_GPU_SCOPED_MEM_POOL_H_\n\n#include <memory>\n#include <type_traits>\n\n#include \"tachyon/device/gpu/gpu_logging.h\"\n#include \"tachyon/export.h\"\n\nnamespace tachyon::device::gpu {\n\nstruct TACHYON_EXPORT MemPoolDestroyer {\n  void operator()(gpuMemPool_t mem_pool) const {\n    GPU_MUST_SUCCEED(gpuMemPoolDestroy(mem_pool), \"Failed gpuMemPoolDestroy()\");\n  }\n};\n\nusing ScopedMemPool =\n    std::unique_ptr<std::remove_pointer_t<gpuMemPool_t>, MemPoolDestroyer>;\n\nTACHYON_EXPORT ScopedMemPool CreateMemPool(const gpuMemPoolProps* pool_props);\n\n}  // namespace tachyon::device::gpu\n\n#endif  // TACHYON_DEVICE_GPU_SCOPED_MEM_POOL_H_\n"
  },
  {
    "path": "tachyon/device/gpu/scoped_stream.cc",
    "content": "#include \"tachyon/device/gpu/scoped_stream.h\"\n\nnamespace tachyon::device::gpu {\n\nScopedStream CreateStream() {\n  gpuStream_t event = nullptr;\n  GPU_MUST_SUCCEED(gpuStreamCreate(&event), \"Failed gpuStreamCreate()\");\n  return ScopedStream(event);\n}\n\nScopedStream CreateStreamWithFlags(unsigned int flags) {\n  gpuStream_t event = nullptr;\n  GPU_MUST_SUCCEED(gpuStreamCreateWithFlags(&event, flags),\n                   \"Failed gpuStreamCreateWithFlags()\");\n  return ScopedStream(event);\n}\n\n}  // namespace tachyon::device::gpu\n"
  },
  {
    "path": "tachyon/device/gpu/scoped_stream.h",
    "content": "#ifndef TACHYON_DEVICE_GPU_SCOPED_STREAM_H_\n#define TACHYON_DEVICE_GPU_SCOPED_STREAM_H_\n\n#include <memory>\n#include <type_traits>\n\n#include \"tachyon/device/gpu/gpu_device_functions.h\"\n#include \"tachyon/device/gpu/gpu_enums.h\"\n#include \"tachyon/device/gpu/gpu_logging.h\"\n#include \"tachyon/export.h\"\n\nnamespace tachyon::device::gpu {\n\nstruct TACHYON_EXPORT StreamDestroyer {\n  void operator()(gpuStream_t event) const {\n    GPU_MUST_SUCCEED(gpuStreamDestroy(event), \"Failed gpuStreamDestroy()\");\n  }\n};\n\nusing ScopedStream =\n    std::unique_ptr<std::remove_pointer_t<cudaStream_t>, StreamDestroyer>;\n\nTACHYON_EXPORT ScopedStream CreateStream();\nTACHYON_EXPORT ScopedStream CreateStreamWithFlags(unsigned int flags);\n\n}  // namespace tachyon::device::gpu\n\n#endif  // TACHYON_DEVICE_GPU_SCOPED_STREAM_H_\n"
  },
  {
    "path": "tachyon/device/numa.cc",
    "content": "#include \"tachyon/device/numa.h\"\n\n#include <algorithm>\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/memory/aligned_memory.h\"\n#include \"tachyon/build/build_config.h\"\n\n#if defined(TACHYON_USE_NUMA)\n// NOLINTNEXTLINE(build/include_subdir)\n#include \"hwloc.h\"  // from @hwloc\n#endif\n\nnamespace tachyon::device {\n\n#if BUILDFLAG(IS_WIN)\n\nbool NUMAEnabled() {\n  NOTIMPLEMENTED();\n  return false;\n}\n\nint NUMANumNodes() { return 1; }\n\nvoid NUMASetThreadNodeAffinity(int node) {}\n\nint NUMAGetThreadNodeAffinity() { return kNUMANoAffinity; }\n\nvoid* NUMAMalloc(int node, size_t size, int minimum_alignment) {\n  return tsl::port::AlignedMalloc(size, minimum_alignment);\n}\n\nvoid NUMAFree(void* ptr, size_t size) { tsl::port::Free(ptr); }\n\nint NUMAGetMemAffinity(const void* addr) { return kNUMANoAffinity; }\n\n#else\n\n#if defined(TACHYON_USE_NUMA)\nnamespace {\nstatic hwloc_topology_t hwloc_topology_handle;\n\nbool HaveHWLocTopology() {\n  // One time initialization\n  static bool init = []() {\n    if (hwloc_topology_init(&hwloc_topology_handle)) {\n      LOG(ERROR) << \"Call to hwloc_topology_init() failed\";\n      return false;\n    }\n    if (hwloc_topology_load(hwloc_topology_handle)) {\n      LOG(ERROR) << \"Call to hwloc_topology_load() failed\";\n      return false;\n    }\n    return true;\n  }();\n  return init;\n}\n\n// Return the first hwloc object of the given type whose os_index\n// matches 'index'.\nhwloc_obj_t GetHWLocTypeIndex(hwloc_obj_type_t tp, int index) {\n  hwloc_obj_t obj = nullptr;\n  if (index >= 0) {\n    while ((obj = hwloc_get_next_obj_by_type(hwloc_topology_handle, tp, obj)) !=\n           nullptr) {\n      if (obj->os_index == index) break;\n    }\n  }\n  return obj;\n}\n}  // namespace\n#endif  // defined(TACHYON_USE_NUMA)\n\nbool NUMAEnabled() { return (NUMANumNodes() > 1); }\n\nint NUMANumNodes() {\n#if defined(TENSORFLOW_USE_NUMA)\n  if (HaveHWLocTopology()) {\n    int num_numanodes =\n        hwloc_get_nbobjs_by_type(hwloc_topology_handle, HWLOC_OBJ_NUMANODE);\n    return std::max(1, num_numanodes);\n  } else {\n    return 1;\n  }\n#else\n  return 1;\n#endif  // defined(TENSORFLOW_USE_NUMA)\n}\n\nvoid NUMASetThreadNodeAffinity(int node) {\n#if defined(TACHYON_USE_NUMA)\n  if (HaveHWLocTopology()) {\n    // Find the corresponding NUMA node topology object.\n    hwloc_obj_t obj = GetHWLocTypeIndex(HWLOC_OBJ_NUMANODE, node);\n    if (obj) {\n      hwloc_set_cpubind(hwloc_topology_handle, obj->cpuset,\n                        HWLOC_CPUBIND_THREAD | HWLOC_CPUBIND_STRICT);\n    } else {\n      LOG(ERROR) << \"Could not find hwloc NUMA node \" << node;\n    }\n  }\n#endif  // defined(TACHYON_USE_NUMA)\n}\n\nint NUMAGetThreadNodeAffinity() {\n  int node_index = kNUMANoAffinity;\n#if defined(TACHYON_USE_NUMA)\n  if (HaveHWLocTopology()) {\n    hwloc_cpuset_t thread_cpuset = hwloc_bitmap_alloc();\n    hwloc_get_cpubind(hwloc_topology_handle, thread_cpuset,\n                      HWLOC_CPUBIND_THREAD);\n    hwloc_obj_t obj = nullptr;\n    // Return the first NUMA node whose cpuset is a (non-proper) superset of\n    // that of the current thread.\n    while ((obj = hwloc_get_next_obj_by_type(\n                hwloc_topology_handle, HWLOC_OBJ_NUMANODE, obj)) != nullptr) {\n      if (hwloc_bitmap_isincluded(thread_cpuset, obj->cpuset)) {\n        node_index = obj->os_index;\n        break;\n      }\n    }\n    hwloc_bitmap_free(thread_cpuset);\n  }\n#endif  // defined(TACHYON_USE_NUMA)\n  return node_index;\n}\n\nvoid* NUMAMalloc(int node, size_t size, int minimum_alignment) {\n#if defined(TACHYON_USE_NUMA)\n  if (HaveHWLocTopology()) {\n    hwloc_obj_t numa_node = GetHWLocTypeIndex(HWLOC_OBJ_NUMANODE, node);\n    if (numa_node) {\n      return hwloc_alloc_membind(hwloc_topology_handle, size,\n                                 numa_node->nodeset, HWLOC_MEMBIND_BIND,\n                                 HWLOC_MEMBIND_BYNODESET);\n    } else {\n      LOG(ERROR) << \"Failed to find hwloc NUMA node \" << node;\n    }\n  }\n#endif  // defined(TACHYON_USE_NUMA)\n  return base::AlignedAlloc(size, minimum_alignment);\n}\n\nvoid NUMAFree(void* ptr, size_t size) {\n#if defined(TACHYON_USE_NUMA)\n  if (HaveHWLocTopology()) {\n    hwloc_free(hwloc_topology_handle, ptr, size);\n    return;\n  }\n#endif  // defined(TACHYON_USE_NUMA)\n  base::AlignedFree(ptr);\n}\n\nint NUMAGetMemAffinity(const void* addr) {\n  int node = kNUMANoAffinity;\n#if defined(TACHYON_USE_NUMA)\n  if (HaveHWLocTopology() && addr) {\n    hwloc_nodeset_t nodeset = hwloc_bitmap_alloc();\n    if (!hwloc_get_area_memlocation(hwloc_topology_handle, addr, 4, nodeset,\n                                    HWLOC_MEMBIND_BYNODESET)) {\n      hwloc_obj_t obj = nullptr;\n      while ((obj = hwloc_get_next_obj_by_type(\n                  hwloc_topology_handle, HWLOC_OBJ_NUMANODE, obj)) != nullptr) {\n        if (hwloc_bitmap_isincluded(nodeset, obj->nodeset)) {\n          node = obj->os_index;\n          break;\n        }\n      }\n      hwloc_bitmap_free(nodeset);\n    } else {\n      LOG(ERROR) << \"Failed call to hwloc_get_area_memlocation.\";\n    }\n  }\n#endif  // defined(TACHYON_USE_NUMA)\n  return node;\n}\n\n#endif\n}  // namespace tachyon::device\n"
  },
  {
    "path": "tachyon/device/numa.h",
    "content": "#ifndef TACHYON_DEVICE_NUMA_H_\n#define TACHYON_DEVICE_NUMA_H_\n\n#include <stddef.h>\n\nnamespace tachyon::device {\n\n// Returns true iff NUMA functions are supported.\nbool NUMAEnabled();\n\n// Returns the number of NUMA nodes present with respect to CPU operations.\n// Typically this will be the number of sockets where some RAM has greater\n// affinity with one socket than another.\nint NUMANumNodes();\n\nstatic const int kNUMANoAffinity = -1;\n\n// If possible sets affinity of the current thread to the specified NUMA node.\n// If node == kNUMANoAffinity removes affinity to any particular node.\nvoid NUMASetThreadNodeAffinity(int node);\n\n// Returns NUMA node affinity of the current thread, kNUMANoAffinity if none.\nint NUMAGetThreadNodeAffinity();\n\n// Like AlignedMalloc, but allocates memory with affinity to the specified NUMA\n// node.\n//\n// Notes:\n//  1. node must be >= 0 and < NUMANumNodes.\n//  1. minimum_alignment must a factor of system page size, the memory\n//     returned will be page-aligned.\n//  2. This function is likely significantly slower than AlignedMalloc\n//     and should not be used for lots of small allocations.  It makes more\n//     sense as a backing allocator for BFCAllocator, PoolAllocator, or similar.\nvoid* NUMAMalloc(int node, size_t size, int minimum_alignment);\n\n// Memory allocated by NUMAMalloc must be freed via NUMAFree.\nvoid NUMAFree(void* ptr, size_t size);\n\n// Returns NUMA node affinity of memory address, kNUMANoAffinity if none.\nint NUMAGetMemAffinity(const void* ptr);\n\n}  // namespace tachyon::device\n\n#endif  // TACHYON_DEVICE_NUMA_H_\n"
  },
  {
    "path": "tachyon/device/numa_unittest.cc",
    "content": "/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n#include \"tachyon/device/numa.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/logging.h\"\n\nnamespace tachyon::device {\nnamespace internal {\n\nTEST(Numa, NumNodes) {\n  if (NUMAEnabled()) {\n    EXPECT_GE(NUMANumNodes(), 1);\n  }\n}\n\nTEST(Numa, Malloc) {\n  if (NUMAEnabled()) {\n    int num_nodes = NUMANumNodes();\n    for (int request_node = 0; request_node < num_nodes; ++request_node) {\n      void* ptr = NUMAMalloc(request_node, 8, 0);\n      EXPECT_NE(ptr, nullptr);\n      // Affinity cannot be tested until page is touched, so save a value.\n      *(reinterpret_cast<int*>(ptr)) = 0;\n      int affinity_node = NUMAGetMemAffinity(ptr);\n      EXPECT_EQ(affinity_node, request_node);\n      NUMAFree(ptr, 8);\n    }\n  }\n}\n\nTEST(Numa, SetNodeAffinity) {\n  // NOTE(tucker): This test is not reliable when executed under tap because\n  // the virtual machine may not have access to all of the available NUMA\n  // nodes.  Not sure what to do about that.\n  EXPECT_EQ(-1, NUMAGetThreadNodeAffinity());\n  if (NUMAEnabled()) {\n    int num_nodes = NUMANumNodes();\n    for (int request_node = 0; request_node < num_nodes; ++request_node) {\n      NUMASetThreadNodeAffinity(request_node);\n      int affinity_node = NUMAGetThreadNodeAffinity();\n      EXPECT_EQ(affinity_node, request_node);\n    }\n  }\n}\n\n}  // namespace internal\n}  // namespace tachyon::device\n"
  },
  {
    "path": "tachyon/device/tracking_allocator.cc",
    "content": "/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n#include \"tachyon/device/tracking_allocator.h\"\n\n#include <algorithm>\n#include <utility>\n\n#include \"tachyon/base/logging.h\"\n\nnamespace tachyon::device {\n\nTrackingAllocator::TrackingAllocator(Allocator* allocator, bool track_sizes)\n    : allocator_(allocator),\n      ref_(1),\n      allocated_(0),\n      high_watermark_(0),\n      total_bytes_(0),\n      track_sizes_locally_(track_sizes && !allocator_->TracksAllocationSizes()),\n      next_allocation_id_(0) {}\n\nvoid* TrackingAllocator::AllocateRaw(\n    size_t alignment, size_t num_bytes,\n    const AllocationAttributes& allocation_attr) {\n  void* ptr = allocator_->AllocateRaw(alignment, num_bytes, allocation_attr);\n  // If memory is exhausted AllocateRaw returns nullptr, and we should\n  // pass this through to the caller\n  if (nullptr == ptr) {\n    return ptr;\n  }\n  if (allocator_->TracksAllocationSizes()) {\n    size_t allocated_bytes = allocator_->AllocatedSize(ptr);\n    {\n      absl::MutexLock lock(&mu_);\n      allocated_ += allocated_bytes;\n      high_watermark_ = std::max(high_watermark_, allocated_);\n      total_bytes_ += allocated_bytes;\n      allocations_.emplace_back(allocated_bytes, base::TimeTicks::Now());\n      ++ref_;\n    }\n  } else if (track_sizes_locally_) {\n    // Call the underlying allocator to try to get the allocated size\n    // whenever possible, even when it might be slow. If this fails,\n    // use the requested size as an approximation.\n    size_t allocated_bytes = allocator_->AllocatedSizeSlow(ptr);\n    allocated_bytes = std::max(num_bytes, allocated_bytes);\n    absl::MutexLock lock(&mu_);\n    ++next_allocation_id_;\n    Chunk chunk = {num_bytes, allocated_bytes, next_allocation_id_};\n    in_use_.emplace(std::make_pair(ptr, chunk));\n    allocated_ += allocated_bytes;\n    high_watermark_ = std::max(high_watermark_, allocated_);\n    total_bytes_ += allocated_bytes;\n    allocations_.emplace_back(allocated_bytes, base::TimeTicks::Now());\n    ++ref_;\n  } else {\n    absl::MutexLock lock(&mu_);\n    total_bytes_ += num_bytes;\n    allocations_.emplace_back(num_bytes, base::TimeTicks::Now());\n    ++ref_;\n  }\n  return ptr;\n}\n\nvoid TrackingAllocator::DeallocateRaw(void* ptr) {\n  // freeing a null ptr is a no-op\n  if (nullptr == ptr) {\n    return;\n  }\n  bool should_delete;\n  // fetch the following outside the lock in case the call to\n  // AllocatedSize is slow\n  bool tracks_allocation_sizes = allocator_->TracksAllocationSizes();\n  size_t allocated_bytes = 0;\n  if (tracks_allocation_sizes) {\n    allocated_bytes = allocator_->AllocatedSize(ptr);\n  } else if (track_sizes_locally_) {\n    absl::MutexLock lock(&mu_);\n    auto itr = in_use_.find(ptr);\n    if (itr != in_use_.end()) {\n      tracks_allocation_sizes = true;\n      allocated_bytes = (*itr).second.allocated_size;\n      in_use_.erase(itr);\n    }\n  }\n  Allocator* allocator = allocator_;\n  {\n    absl::MutexLock lock(&mu_);\n    if (tracks_allocation_sizes) {\n      CHECK_GE(allocated_, allocated_bytes);\n      allocated_ -= allocated_bytes;\n      allocations_.emplace_back(-allocated_bytes, base::TimeTicks::Now());\n    }\n    should_delete = UnRef();\n  }\n  allocator->DeallocateRaw(ptr);\n  if (should_delete) {\n    delete this;\n  }\n}\n\nbool TrackingAllocator::TracksAllocationSizes() const {\n  return track_sizes_locally_ || allocator_->TracksAllocationSizes();\n}\n\nsize_t TrackingAllocator::RequestedSize(const void* ptr) const {\n  if (track_sizes_locally_) {\n    absl::MutexLock lock(&mu_);\n    auto it = in_use_.find(ptr);\n    if (it != in_use_.end()) {\n      return (*it).second.requested_size;\n    }\n    return 0;\n  } else {\n    return allocator_->RequestedSize(ptr);\n  }\n}\n\nsize_t TrackingAllocator::AllocatedSize(const void* ptr) const {\n  if (track_sizes_locally_) {\n    absl::MutexLock lock(&mu_);\n    auto it = in_use_.find(ptr);\n    if (it != in_use_.end()) {\n      return (*it).second.allocated_size;\n    }\n    return 0;\n  } else {\n    return allocator_->AllocatedSize(ptr);\n  }\n}\n\nint64_t TrackingAllocator::AllocationId(const void* ptr) const {\n  if (track_sizes_locally_) {\n    absl::MutexLock lock(&mu_);\n    auto it = in_use_.find(ptr);\n    if (it != in_use_.end()) {\n      return (*it).second.allocation_id;\n    }\n    return 0;\n  } else {\n    return allocator_->AllocationId(ptr);\n  }\n}\n\nstd::optional<AllocatorStats> TrackingAllocator::GetStats() {\n  return allocator_->GetStats();\n}\n\nbool TrackingAllocator::ClearStats() { return allocator_->ClearStats(); }\n\nstd::tuple<size_t, size_t, size_t> TrackingAllocator::GetSizes() {\n  size_t high_watermark;\n  size_t total_bytes;\n  size_t still_live_bytes;\n  {\n    absl::MutexLock lock(&mu_);\n    high_watermark = high_watermark_;\n    total_bytes = total_bytes_;\n    still_live_bytes = allocated_;\n  }\n  return std::make_tuple(total_bytes, high_watermark, still_live_bytes);\n}\n\nabsl::InlinedVector<AllocRecord, 4> TrackingAllocator::GetRecordsAndUnRef() {\n  bool should_delete;\n  absl::InlinedVector<AllocRecord, 4> allocations;\n  {\n    absl::MutexLock lock(&mu_);\n    allocations.swap(allocations_);\n    should_delete = UnRef();\n  }\n  if (should_delete) {\n    delete this;\n  }\n  return allocations;\n}\n\nabsl::InlinedVector<AllocRecord, 4> TrackingAllocator::GetCurrentRecords() {\n  absl::InlinedVector<AllocRecord, 4> allocations;\n  {\n    absl::MutexLock lock(&mu_);\n    for (const AllocRecord& alloc : allocations_) {\n      allocations.push_back(alloc);\n    }\n  }\n  return allocations;\n}\n\nbool TrackingAllocator::UnRef() {\n  CHECK_GE(ref_, 1);\n  --ref_;\n  return (ref_ == 0);\n}\n\n}  // namespace tachyon::device\n"
  },
  {
    "path": "tachyon/device/tracking_allocator.h",
    "content": "/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n#ifndef TACHYON_DEVICE_TRACKING_ALLOCATOR_H_\n#define TACHYON_DEVICE_TRACKING_ALLOCATOR_H_\n\n#include <optional>\n#include <string>\n#include <tuple>\n#include <unordered_map>\n\n#include \"absl/base/thread_annotations.h\"\n#include \"absl/container/inlined_vector.h\"\n#include \"absl/synchronization/mutex.h\"\n\n#include \"tachyon/base/time/time.h\"\n#include \"tachyon/device/allocator.h\"\n\nnamespace tachyon::device {\n\n// TrackingAllocator is a wrapper for an Allocator. It keeps a running\n// count of the number of bytes allocated through the wrapper. It is\n// used by the Executor to \"charge\" allocations to particular Op\n// executions. Each Op gets a separate TrackingAllocator wrapper\n// around the underlying allocator.\n//\n// The implementation assumes the invariant that all calls to\n// AllocateRaw by an Op (or work items spawned by the Op) will occur\n// before the Op's Compute method returns. Thus the high watermark is\n// established once Compute returns.\n//\n// DeallocateRaw can be called long after the Op has finished,\n// e.g. when an output tensor is deallocated, and the wrapper cannot\n// be deleted until the last of these calls has occurred.  The\n// TrackingAllocator keeps track of outstanding calls using a\n// reference count, and deletes itself once the last call has been\n// received and the high watermark has been retrieved.\nstruct TACHYON_EXPORT AllocRecord {\n  AllocRecord(int64_t alloc_bytes, base::TimeTicks alloc_time)\n      : alloc_bytes(alloc_bytes), alloc_time(alloc_time) {}\n  AllocRecord() : AllocRecord(0, base::TimeTicks()) {}\n\n  int64_t alloc_bytes;\n  base::TimeTicks alloc_time;\n};\n\nclass TACHYON_EXPORT TrackingAllocator : public Allocator {\n public:\n  explicit TrackingAllocator(Allocator* allocator, bool track_ids);\n  std::string Name() override { return allocator_->Name(); }\n  void* AllocateRaw(size_t alignment, size_t num_bytes) override {\n    return AllocateRaw(alignment, num_bytes, AllocationAttributes());\n  }\n  void* AllocateRaw(size_t alignment, size_t num_bytes,\n                    const AllocationAttributes& allocation_attr) override;\n  void DeallocateRaw(void* ptr) override;\n  bool TracksAllocationSizes() const override;\n  size_t RequestedSize(const void* ptr) const override;\n  size_t AllocatedSize(const void* ptr) const override;\n  int64_t AllocationId(const void* ptr) const override;\n  std::optional<AllocatorStats> GetStats() override;\n  bool ClearStats() override;\n\n  AllocatorMemoryType GetMemoryType() const override {\n    return allocator_->GetMemoryType();\n  }\n\n  // If the underlying allocator tracks allocation sizes, this returns\n  // a tuple where the first value is the total number of bytes\n  // allocated through this wrapper, the second value is the high\n  // watermark of bytes allocated through this wrapper and the third value is\n  // the allocated bytes through this wrapper that are still alive. If the\n  // underlying allocator does not track allocation sizes the first\n  // value is the total number of bytes requested through this wrapper\n  // and the second and the third are 0.\n  //\n  std::tuple<size_t, size_t, size_t> GetSizes();\n  // After GetRecordsAndUnRef is called, the only further calls allowed\n  // on this wrapper are calls to DeallocateRaw with pointers that\n  // were allocated by this wrapper and have not yet been\n  // deallocated. After this call completes and all allocated pointers\n  // have been deallocated the wrapper will delete itself.\n  absl::InlinedVector<AllocRecord, 4> GetRecordsAndUnRef();\n  // Returns a copy of allocation records collected so far.\n  absl::InlinedVector<AllocRecord, 4> GetCurrentRecords();\n\n protected:\n  ~TrackingAllocator() override {}\n\n private:\n  bool UnRef() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_);\n\n  Allocator* allocator_;  // not owned.\n  mutable absl::Mutex mu_;\n  // the number of calls to AllocateRaw that have not yet been matched\n  // by a corresponding call to DeAllocateRaw, plus 1 if the Executor\n  // has not yet read out the high watermark.\n  int ref_ ABSL_GUARDED_BY(mu_);\n  // the current number of outstanding bytes that have been allocated\n  // by this wrapper, or 0 if the underlying allocator does not track\n  // allocation sizes.\n  size_t allocated_ ABSL_GUARDED_BY(mu_);\n  // the maximum number of outstanding bytes that have been allocated\n  // by this wrapper, or 0 if the underlying allocator does not track\n  // allocation sizes.\n  size_t high_watermark_ ABSL_GUARDED_BY(mu_);\n  // the total number of bytes that have been allocated by this\n  // wrapper if the underlying allocator tracks allocation sizes,\n  // otherwise the total number of bytes that have been requested by\n  // this allocator.\n  size_t total_bytes_ ABSL_GUARDED_BY(mu_);\n\n  absl::InlinedVector<AllocRecord, 4> allocations_ ABSL_GUARDED_BY(mu_);\n\n  // Track allocations locally if requested in the constructor and the\n  // underlying allocator doesn't already do it for us.\n  const bool track_sizes_locally_;\n  struct Chunk {\n    size_t requested_size;\n    size_t allocated_size;\n    int64_t allocation_id;\n  };\n  std::unordered_map<const void*, Chunk> in_use_ ABSL_GUARDED_BY(mu_);\n  int64_t next_allocation_id_ ABSL_GUARDED_BY(mu_);\n};\n\n}  // namespace tachyon::device\n\n#endif  // TACHYON_DEVICE_TRACKING_ALLOCATOR_H_\n"
  },
  {
    "path": "tachyon/examples/BUILD.bazel",
    "content": "load(\"@local_config_cuda//cuda:build_defs.bzl\", \"if_cuda\")\nload(\n    \"//bazel:tachyon_cc.bzl\",\n    \"tachyon_cc_binary\",\n    \"tachyon_cuda_binary\",\n)\n\ntachyon_cuda_binary(\n    name = \"cuda_example\",\n    srcs = [\"cuda_example.cu.cc\"],\n    deps = if_cuda(\n        [\n            \"@local_config_cuda//cuda:cuda_headers\",\n        ],\n        [\n            \"//tachyon/base/console\",\n        ],\n    ),\n)\n\ntachyon_cc_binary(\n    name = \"version\",\n    srcs = [\"version.cc\"],\n    deps = [\n        \"//tachyon:version\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/examples/cuda_example.cu.cc",
    "content": "/* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *  * Neither the name of NVIDIA CORPORATION nor the names of its\n *    contributors may be used to endorse or promote products derived\n *    from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/**\n * Vector addition: C = A + B.\n *\n * This sample is a very basic sample that implements element by element\n * vector addition. It is the same as the sample illustrating Chapter 2\n * of the programming guide with some additions like error checking.\n */\n\n#include <math.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#if TACHYON_CUDA\n// For the CUDA runtime routines (prefixed with \"cuda_\")\n#include \"third_party/gpus/cuda/include/cuda_runtime.h\"\n\nnamespace tachyon {\n\n/**\n * CUDA Kernel Device code\n *\n * Computes the vector addition of A and B into C. The 3 vectors have the same\n * number of elements numElements.\n */\n__global__ void vectorAdd(const float *A, const float *B, float *C,\n                          int numElements) {\n  int i = blockDim.x * blockIdx.x + threadIdx.x;\n\n  if (i < numElements) {\n    C[i] = A[i] + B[i] + 0.0f;\n  }\n}\n\nint RealMain(int argc, char **argv) {\n  // Error code to check return values for CUDA calls\n  cudaError_t err = cudaSuccess;\n\n  // Print the vector length to be used, and compute its size\n  int numElements = 50000;\n  size_t size = numElements * sizeof(float);\n  printf(\"[Vector addition of %d elements]\\n\", numElements);\n\n  // Allocate the host input vector A\n  float *h_A = (float *)malloc(size);  // NOLINT(readability/casting)\n\n  // Allocate the host input vector B\n  float *h_B = (float *)malloc(size);  // NOLINT(readability/casting)\n\n  // Allocate the host output vector C\n  float *h_C = (float *)malloc(size);  // NOLINT(readability/casting)\n\n  // Verify that allocations succeeded\n  if (h_A == NULL || h_B == NULL || h_C == NULL) {\n    fprintf(stderr, \"Failed to allocate host vectors!\\n\");\n    exit(EXIT_FAILURE);\n  }\n\n  // Initialize the host input vectors\n  for (int i = 0; i < numElements; ++i) {\n    h_A[i] = rand() / (float)RAND_MAX;  // NOLINT\n    h_B[i] = rand() / (float)RAND_MAX;  // NOLINT\n  }\n\n  // Allocate the device input vector A\n  float *d_A = NULL;\n  err = cudaMalloc((void **)&d_A, size);  // NOLINT(readability/casting)\n\n  if (err != cudaSuccess) {\n    fprintf(stderr, \"Failed to allocate device vector A (error code %s)!\\n\",\n            cudaGetErrorString(err));\n    exit(EXIT_FAILURE);\n  }\n\n  // Allocate the device input vector B\n  float *d_B = NULL;\n  err = cudaMalloc((void **)&d_B, size);  // NOLINT(readability/casting)\n\n  if (err != cudaSuccess) {\n    fprintf(stderr, \"Failed to allocate device vector B (error code %s)!\\n\",\n            cudaGetErrorString(err));\n    exit(EXIT_FAILURE);\n  }\n\n  // Allocate the device output vector C\n  float *d_C = NULL;                      // NOLINT(readability/casting)\n  err = cudaMalloc((void **)&d_C, size);  // NOLINT(readability/casting)\n\n  if (err != cudaSuccess) {\n    fprintf(stderr, \"Failed to allocate device vector C (error code %s)!\\n\",\n            cudaGetErrorString(err));\n    exit(EXIT_FAILURE);\n  }\n\n  // Copy the host input vectors A and B in host memory to the device input\n  // vectors in\n  // device memory\n  printf(\"Copy input data from the host memory to the CUDA device\\n\");\n  err = cudaMemcpy(d_A, h_A, size, cudaMemcpyHostToDevice);\n\n  if (err != cudaSuccess) {\n    fprintf(stderr,\n            \"Failed to copy vector A from host to device (error code %s)!\\n\",\n            cudaGetErrorString(err));\n    exit(EXIT_FAILURE);\n  }\n\n  err = cudaMemcpy(d_B, h_B, size, cudaMemcpyHostToDevice);\n\n  if (err != cudaSuccess) {\n    fprintf(stderr,\n            \"Failed to copy vector B from host to device (error code %s)!\\n\",\n            cudaGetErrorString(err));\n    exit(EXIT_FAILURE);\n  }\n\n  // Launch the Vector Add CUDA Kernel\n  int threadsPerBlock = 256;\n  int blocksPerGrid = (numElements + threadsPerBlock - 1) / threadsPerBlock;\n  printf(\"CUDA kernel launch with %d blocks of %d threads\\n\", blocksPerGrid,\n         threadsPerBlock);\n  vectorAdd<<<blocksPerGrid, threadsPerBlock>>>(d_A, d_B, d_C, numElements);\n  err = cudaGetLastError();\n\n  if (err != cudaSuccess) {\n    fprintf(stderr, \"Failed to launch vectorAdd kernel (error code %s)!\\n\",\n            cudaGetErrorString(err));\n    exit(EXIT_FAILURE);\n  }\n\n  // Copy the device result vector in device memory to the host result vector\n  // in host memory.\n  printf(\"Copy output data from the CUDA device to the host memory\\n\");\n  err = cudaMemcpy(h_C, d_C, size, cudaMemcpyDeviceToHost);\n\n  if (err != cudaSuccess) {\n    fprintf(stderr,\n            \"Failed to copy vector C from device to host (error code %s)!\\n\",\n            cudaGetErrorString(err));\n    exit(EXIT_FAILURE);\n  }\n\n  // Verify that the result vector is correct\n  for (int i = 0; i < numElements; ++i) {\n    if (fabs(h_A[i] + h_B[i] - h_C[i]) > 1e-5) {\n      fprintf(stderr, \"Result verification failed at element %d!\\n\", i);\n      exit(EXIT_FAILURE);\n    }\n  }\n\n  printf(\"Test PASSED\\n\");\n\n  // Free device global memory\n  err = cudaFree(d_A);\n\n  if (err != cudaSuccess) {\n    fprintf(stderr, \"Failed to free device vector A (error code %s)!\\n\",\n            cudaGetErrorString(err));\n    exit(EXIT_FAILURE);\n  }\n\n  err = cudaFree(d_B);\n\n  if (err != cudaSuccess) {\n    fprintf(stderr, \"Failed to free device vector B (error code %s)!\\n\",\n            cudaGetErrorString(err));\n    exit(EXIT_FAILURE);\n  }\n\n  err = cudaFree(d_C);\n\n  if (err != cudaSuccess) {\n    fprintf(stderr, \"Failed to free device vector C (error code %s)!\\n\",\n            cudaGetErrorString(err));\n    exit(EXIT_FAILURE);\n  }\n\n  // Free host memory\n  free(h_A);\n  free(h_B);\n  free(h_C);\n\n  printf(\"Done\\n\");\n  return 0;\n}\n\n}  // namespace tachyon\n\nint main(int argc, char **argv) { return tachyon::RealMain(argc, argv); }\n#else\n#include \"tachyon/base/console/iostream.h\"\n\nint main(int argc, char **argv) {\n  tachyon_cerr << \"please build with --config cuda\" << std::endl;\n  return 1;\n}\n#endif  // TACHYON_CUDA\n"
  },
  {
    "path": "tachyon/examples/version.cc",
    "content": "#include <iostream>\n\n// clang-format off\n#include \"tachyon/version.h\"\n// clang-format on\n\nnamespace tachyon {\n\nint RealMain(int argc, char** argv) {\n  std::cout << GetRuntimeFullVersionStr() << std::endl;\n  return 0;\n}\n\n}  // namespace tachyon\n\nint main(int argc, char** argv) { return tachyon::RealMain(argc, argv); }\n"
  },
  {
    "path": "tachyon/export.h",
    "content": "#ifndef TACHYON_EXPORT_H_\n#define TACHYON_EXPORT_H_\n\n#if defined(TACHYON_COMPONENT_BUILD)\n\n#if defined(_WIN32)\n#ifdef TACHYON_COMPILE_LIBRARY\n#define TACHYON_EXPORT __declspec(dllexport)\n#else\n#define TACHYON_EXPORT __declspec(dllimport)\n#endif  // defined(TACHYON_COMPILE_LIBRARY)\n\n#else\n#ifdef TACHYON_COMPILE_LIBRARY\n#define TACHYON_EXPORT __attribute__((visibility(\"default\")))\n#else\n#define TACHYON_EXPORT\n#endif  // defined(TACHYON_COMPILE_LIBRARY)\n#endif  // defined(_WIN32)\n\n#else\n#define TACHYON_EXPORT\n#endif  // defined(TACHYON_COMPONENT_BUILD)\n\n#endif  // TACHYON_EXPORT_H_\n"
  },
  {
    "path": "tachyon/math/base/BUILD.bazel",
    "content": "load(\"//bazel:tachyon.bzl\", \"if_aarch64\", \"if_has_avx512\", \"if_x86_64\")\nload(\n    \"//bazel:tachyon_cc.bzl\",\n    \"tachyon_avx512_defines\",\n    \"tachyon_cc_benchmark\",\n    \"tachyon_cc_library\",\n    \"tachyon_cc_unittest\",\n)\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"arithmetics\",\n    hdrs = [\"arithmetics.h\"],\n    deps = [\n        \":arithmetics_results\",\n        \"//tachyon/base:bit_cast\",\n        \"//tachyon/base:compiler_specific\",\n        \"//tachyon/build:build_config\",\n        \"@com_google_absl//absl/numeric:int128\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"arithmetics_results\",\n    hdrs = [\"arithmetics_results.h\"],\n    deps = [\"@com_google_absl//absl/strings\"],\n)\n\ntachyon_cc_library(\n    name = \"big_int\",\n    srcs = [\"big_int.cc\"],\n    hdrs = [\"big_int.h\"],\n    deps = [\n        \":arithmetics\",\n        \":bit_traits_forward\",\n        \"//tachyon/base:bit_cast\",\n        \"//tachyon/base:endian_utils\",\n        \"//tachyon/base:random\",\n        \"//tachyon/base/buffer:copyable\",\n        \"//tachyon/base/json\",\n        \"//tachyon/base/strings:string_util\",\n        \"//tachyon/build:build_config\",\n        \"//tachyon/math/base/gmp:gmp_util\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"bit_iterator\",\n    hdrs = [\"bit_iterator.h\"],\n    deps = [\":bit_traits_forward\"],\n)\n\ntachyon_cc_library(\n    name = \"bit_traits_forward\",\n    hdrs = [\"bit_traits_forward.h\"],\n)\n\ntachyon_cc_library(\n    name = \"byinverter\",\n    hdrs = [\"byinverter.h\"],\n    deps = [\n        \"//tachyon/base:bits\",\n        \"//tachyon/math/base:big_int\",\n        \"@com_google_absl//absl/numeric:int128\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"const_init\",\n    hdrs = [\"const_init.h\"],\n)\n\ntachyon_cc_library(\n    name = \"egcd\",\n    hdrs = [\"egcd.h\"],\n)\n\ntachyon_cc_library(\n    name = \"field\",\n    hdrs = [\"field.h\"],\n    deps = [\":ring\"],\n)\n\ntachyon_cc_library(\n    name = \"groups\",\n    hdrs = [\"groups.h\"],\n    deps = [\n        \":semigroups\",\n        \"//tachyon/base:parallelize\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/base/types:always_false\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"parallelize_threshold\",\n    hdrs = [\"parallelize_threshold.h\"],\n)\n\ntachyon_cc_library(\n    name = \"rational_field\",\n    hdrs = [\"rational_field.h\"],\n    deps = [\n        \":field\",\n        \"//tachyon/base:optional\",\n        \"//tachyon/base:template_util\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"ring\",\n    hdrs = [\"ring.h\"],\n    deps = [\n        \":groups\",\n        \"//tachyon/base:parallelize\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"semigroups\",\n    hdrs = [\"semigroups.h\"],\n    deps = [\n        \":big_int\",\n        \":bit_iterator\",\n        \"//tachyon/base:bits\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base:parallelize\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/base/types:always_false\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"sign\",\n    srcs = [\"sign.cc\"],\n    hdrs = [\"sign.h\"],\n    deps = [\n        \"//tachyon/base:logging\",\n        \"//tachyon/base/strings:string_util\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"simd_int\",\n    srcs = if_x86_64([\n        \"simd_int128_x86.cc\",\n        \"simd_int256_x86.cc\",\n    ]) + if_has_avx512([\n        \"simd_int512_x86.cc\",\n    ]) + if_aarch64([\n        \"simd_int128_arm64.cc\",\n    ]),\n    hdrs = [\"simd_int.h\"],\n    copts = if_x86_64([\n        \"-mavx2\",\n    ]) + if_has_avx512([\n        \"-mavx512f\",\n    ]),\n    defines = tachyon_avx512_defines(),\n    deps = [\n        \"//tachyon:export\",\n        \"//tachyon/base:bit_cast\",\n        \"//tachyon/base:compiler_specific\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/build:build_config\",\n        \"//tachyon/math/base:big_int\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"base_unittests\",\n    srcs = [\n        \"arithmetics_unittest.cc\",\n        \"big_int_unittest.cc\",\n        \"bit_iterator_unittest.cc\",\n        \"byinverter_unittest.cc\",\n        \"egcd_unittest.cc\",\n        \"field_unittest.cc\",\n        \"groups_unittest.cc\",\n        \"rational_field_unittest.cc\",\n        \"semigroups_unittest.cc\",\n        \"sign_unittest.cc\",\n        \"simd_int_unittest.cc\",\n    ],\n    deps = [\n        \":big_int\",\n        \":bit_iterator\",\n        \":byinverter\",\n        \":egcd\",\n        \":groups\",\n        \":rational_field\",\n        \":sign\",\n        \":simd_int\",\n        \"//tachyon/base:optional\",\n        \"//tachyon/base/buffer:vector_buffer\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/base/strings:string_number_conversions\",\n        \"//tachyon/math/elliptic_curves/msm/test:variable_base_msm_test_set\",\n        \"//tachyon/math/elliptic_curves/short_weierstrass/test:sw_curve_config\",\n        \"//tachyon/math/finite_fields/test:finite_field_test\",\n        \"//tachyon/math/finite_fields/test:gf7\",\n        \"@com_google_absl//absl/container:inlined_vector\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ntachyon_cc_benchmark(\n    name = \"batch_inverse_benchmark\",\n    srcs = [\"batch_inverse_benchmark.cc\"],\n    deps = [\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:fq\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/math/base/arithmetics.h",
    "content": "#ifndef TACHYON_MATH_BASE_ARITHMETICS_H_\n#define TACHYON_MATH_BASE_ARITHMETICS_H_\n\n#include <stdint.h>\n\n#include <utility>\n\n#include \"absl/numeric/int128.h\"\n\n#include \"tachyon/base/bit_cast.h\"\n#include \"tachyon/base/compiler_specific.h\"\n#include \"tachyon/build/build_config.h\"\n#include \"tachyon/math/base/arithmetics_results.h\"\n\n#if defined(__clang__) && HAS_BUILTIN(__builtin_addc)\n// See\n// https://clang.llvm.org/docs/LanguageExtensions.html#multiprecision-arithmetic-builtins\n#elif ARCH_CPU_X86_64\n// See\n// https://www.intel.com/content/www/us/en/docs/cpp-compiler/developer-guide-reference/2021-8/intrinsics-for-multi-precision-arithmetic.html\n#if __GNUC__ > 10\n// See\n// https://github.com/gcc-mirror/gcc/blob/releases/gcc-11/gcc/config/i386/adxintrin.h#L25\n#include <x86gprintrin.h>\n#else\n// See\n// https://github.com/gcc-mirror/gcc/blob/releases/gcc-10/gcc/config/i386/adxintrin.h#L25\n#include <immintrin.h>\n#endif\n#endif\n\nnamespace tachyon::math::internal {\nnamespace u32 {\n\n// Calculates a + b + carry.\nALWAYS_INLINE AddResult<uint32_t> AddWithCarry(uint32_t a, uint32_t b,\n                                               uint32_t carry = 0) {\n  AddResult<uint32_t> result;\n#if defined(__clang__) && HAS_BUILTIN(__builtin_addc)\n  result.result = __builtin_addc(a, b, carry, &result.carry);\n#elif ARCH_CPU_X86_64\n  result.carry = _addcarry_u32(carry, a, b, &result.result);\n#else\n  uint64_t tmp = uint64_t{a} + uint64_t{b} + uint64_t{carry};\n  result.result = static_cast<uint32_t>(tmp);\n  result.carry = static_cast<uint32_t>(tmp >> 32);\n#endif\n  return result;\n}\n\n// Calculates a - b - borrow.\nALWAYS_INLINE SubResult<uint32_t> SubWithBorrow(uint32_t a, uint32_t b,\n                                                uint32_t borrow = 0) {\n  SubResult<uint32_t> result;\n#if defined(__clang__) && HAS_BUILTIN(__builtin_subc)\n  result.result = __builtin_subc(a, b, borrow, &result.borrow);\n#elif ARCH_CPU_X86_64\n  result.borrow = _subborrow_u32(borrow, a, b, &result.result);\n#else\n  uint64_t tmp =\n      (uint64_t{1} << 32) + uint64_t{a} - uint64_t{b} - uint64_t{borrow};\n  result.result = static_cast<uint32_t>(tmp);\n  result.borrow =\n      static_cast<uint32_t>(static_cast<uint32_t>((tmp >> 32) == 0 ? 1 : 0));\n#endif\n  return result;\n}\n\n// Calculates a + b * c.\nALWAYS_INLINE constexpr MulResult<uint32_t> MulAddWithCarry(\n    uint32_t a, uint32_t b, uint32_t c, uint32_t carry = 0) {\n  uint64_t tmp = uint64_t{a} + uint64_t{b} * uint64_t{c} + uint64_t{carry};\n  MulResult<uint32_t> result{};\n  result.lo = static_cast<uint32_t>(tmp);\n  result.hi = static_cast<uint32_t>(tmp >> 32);\n  return result;\n}\n\n}  // namespace u32\n\nnamespace u64 {\n\n// Calculates a + b + carry.\nALWAYS_INLINE AddResult<uint64_t> AddWithCarry(uint64_t a, uint64_t b,\n                                               uint64_t carry = 0) {\n  AddResult<uint64_t> result;\n#if defined(__clang__) && HAS_BUILTIN(__builtin_addcl)\n  unsigned long carry_result;  // NOLINT(runtime/int)\n  result.result = __builtin_addcl(a, b, carry, &carry_result);\n  result.carry = base::bit_cast<uint64_t>(carry_result);\n#elif ARCH_CPU_X86_64\n  unsigned long long add_result;  // NOLINT(runtime/int)\n  result.carry = _addcarry_u64(carry, a, b, &add_result);\n  result.result = base::bit_cast<uint64_t>(add_result);\n#else\n  absl::uint128 tmp =\n      absl::uint128(a) + absl::uint128(b) + absl::uint128(carry);\n  result.result = absl::Uint128Low64(tmp);\n  result.carry = absl::Uint128High64(tmp);\n#endif\n  return result;\n}\n\n// Calculates a - b - borrow.\nALWAYS_INLINE SubResult<uint64_t> SubWithBorrow(uint64_t a, uint64_t b,\n                                                uint64_t borrow = 0) {\n  SubResult<uint64_t> result;\n#if defined(__clang__) && HAS_BUILTIN(__builtin_subcl)\n  unsigned long borrow_result;  // NOLINT(runtime/int)\n  result.result = __builtin_subcl(a, b, borrow, &borrow_result);\n  result.borrow = base::bit_cast<uint64_t>(borrow_result);\n#elif ARCH_CPU_X86_64\n  unsigned long long sub_result;  // NOLINT(runtime/int)\n  result.borrow = _subborrow_u64(borrow, a, b, &sub_result);\n  result.result = base::bit_cast<uint64_t>(sub_result);\n#else\n  absl::uint128 tmp = (absl::uint128(1) << 64) + absl::uint128(a) -\n                      absl::uint128(b) - absl::uint128(borrow);\n  result.result = absl::Uint128Low64(tmp);\n  result.borrow = absl::Uint128High64(tmp);\n#endif\n  return result;\n}\n\n// Calculates a + b * c.\nALWAYS_INLINE MulResult<uint64_t> MulAddWithCarry(uint64_t a, uint64_t b,\n                                                  uint64_t c,\n                                                  uint64_t carry = 0) {\n  absl::uint128 tmp = absl::uint128(a) + absl::uint128(b) * absl::uint128(c) +\n                      absl::uint128(carry);\n  MulResult<uint64_t> result;\n  result.lo = absl::Uint128Low64(tmp);\n  result.hi = absl::Uint128High64(tmp);\n  return result;\n}\n\n}  // namespace u64\n}  // namespace tachyon::math::internal\n\n#endif  // TACHYON_MATH_BASE_ARITHMETICS_H_\n"
  },
  {
    "path": "tachyon/math/base/arithmetics_results.h",
    "content": "#ifndef TACHYON_MATH_BASE_ARITHMETICS_RESULTS_H_\n#define TACHYON_MATH_BASE_ARITHMETICS_RESULTS_H_\n\n#include <string>\n\n#include \"absl/strings/substitute.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename T>\nstruct AddResult {\n  T result;\n  T carry{0};\n\n  constexpr bool operator==(const AddResult& other) const {\n    return result == other.result && carry == other.carry;\n  }\n  constexpr bool operator!=(const AddResult& other) const {\n    return !operator==(other);\n  }\n\n  std::string ToString() const {\n    return absl::Substitute(\"($0, $1)\", result.ToString(), carry.ToString());\n  }\n};\n\ntemplate <typename T>\nstruct SubResult {\n  T result;\n  T borrow{0};\n\n  constexpr bool operator==(const SubResult& other) const {\n    return result == other.result && borrow == other.borrow;\n  }\n  constexpr bool operator!=(const SubResult& other) const {\n    return !operator==(other);\n  }\n\n  std::string ToString() const {\n    return absl::Substitute(\"($0, $1)\", result.ToString(), borrow.ToString());\n  }\n};\n\ntemplate <typename T>\nstruct MulResult {\n  T hi{0};\n  T lo{0};\n\n  constexpr bool operator==(const MulResult& other) const {\n    return hi == other.hi && lo == other.lo;\n  }\n  constexpr bool operator!=(const MulResult& other) const {\n    return !operator==(other);\n  }\n\n  std::string ToString() const {\n    return absl::Substitute(\"($0, $1)\", hi.ToString(), lo.ToString());\n  }\n};\n\ntemplate <typename T>\nstruct DivResult {\n  T quotient;\n  T remainder = T::Zero();\n\n  constexpr bool operator==(const DivResult& other) const {\n    return quotient == other.quotient && remainder == other.remainder;\n  }\n  constexpr bool operator!=(const DivResult& other) const {\n    return !operator==(other);\n  }\n\n  std::string ToString() const {\n    return absl::Substitute(\"($0, $1)\", quotient.ToString(),\n                            remainder.ToString());\n  }\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_BASE_ARITHMETICS_RESULTS_H_\n"
  },
  {
    "path": "tachyon/math/base/arithmetics_unittest.cc",
    "content": "#include \"tachyon/math/base/arithmetics.h\"\n\n#include <limits>\n\n#include \"absl/strings/substitute.h\"\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/random.h\"\n\nnamespace tachyon::math::internal {\nnamespace {\n\nuint32_t GetRandomLo32() {\n  return base::Uniform(\n      base::Range<uint32_t>::Until(std::numeric_limits<uint32_t>::max() / 2));\n}\n\nuint32_t GetRandomHi32() {\n  return base::Uniform(\n      base::Range<uint32_t>::From(std::numeric_limits<uint32_t>::max() / 2));\n}\n\nuint32_t GetRandomSqrt32() {\n  return base::Uniform(base::Range<uint32_t>::Until(\n      static_cast<uint32_t>(std::sqrt(std::numeric_limits<uint32_t>::max()))));\n}\n\nuint64_t GetRandomLo64() {\n  return base::Uniform(\n      base::Range<uint64_t>::Until(std::numeric_limits<uint64_t>::max() / 2));\n}\n\nuint64_t GetRandomHi64() {\n  return base::Uniform(\n      base::Range<uint64_t>::From(std::numeric_limits<uint64_t>::max() / 2));\n}\n\nuint64_t GetRandomSqrt64() {\n  return base::Uniform(base::Range<uint64_t>::Until(\n      static_cast<uint64_t>(std::sqrt(std::numeric_limits<uint64_t>::max()))));\n}\n\n}  // namespace\n\nTEST(Arithmetics, AddWithCarry32) {\n  uint32_t a = GetRandomLo32();\n  uint32_t b = GetRandomLo32();\n  SCOPED_TRACE(absl::Substitute(\"a: $0, b: $1\", a, b));\n\n  auto result = u32::AddWithCarry(a, b);\n  EXPECT_EQ(result.result, a + b);\n  EXPECT_EQ(result.carry, 0);\n\n  b = std::numeric_limits<uint32_t>::max() - a;\n  auto result2 = u32::AddWithCarry(a, b, 1);\n  EXPECT_EQ(result2.result, a + b + 1);\n  EXPECT_EQ(result2.carry, 1);\n}\n\nTEST(Arithmetics, AddWithCarry64) {\n  uint64_t a = GetRandomLo64();\n  uint64_t b = GetRandomLo64();\n  SCOPED_TRACE(absl::Substitute(\"a: $0, b: $1\", a, b));\n\n  auto result = u64::AddWithCarry(a, b);\n  EXPECT_EQ(result.result, a + b);\n  EXPECT_EQ(result.carry, 0);\n\n  b = std::numeric_limits<uint64_t>::max() - a;\n  auto result2 = u64::AddWithCarry(a, b, 1);\n  EXPECT_EQ(result2.result, a + b + 1);\n  EXPECT_EQ(result2.carry, 1);\n}\n\nTEST(Arithmetics, SubWithBorrow32) {\n  uint32_t a = GetRandomHi32();\n  uint32_t b = GetRandomLo32();\n  SCOPED_TRACE(absl::Substitute(\"a: $0, b: $1\", a, b));\n\n  auto result = u32::SubWithBorrow(a, b);\n  EXPECT_EQ(result.result, a - b);\n  EXPECT_EQ(result.borrow, 0);\n\n  auto result2 = u32::SubWithBorrow(b, a);\n  EXPECT_EQ(result2.result, b - a);\n  EXPECT_EQ(result2.borrow, 1);\n\n  b = a;\n  auto result3 = u32::SubWithBorrow(a, b, 1);\n  EXPECT_EQ(result3.result, a - b - 1);\n  EXPECT_EQ(result3.borrow, 1);\n}\n\nTEST(Arithmetics, SubWithBorrow64) {\n  uint64_t a = GetRandomHi64();\n  uint64_t b = GetRandomLo64();\n  SCOPED_TRACE(absl::Substitute(\"a: $0, b: $1\", a, b));\n\n  auto result = u64::SubWithBorrow(a, b);\n  EXPECT_EQ(result.result, a - b);\n  EXPECT_EQ(result.borrow, 0);\n\n  auto result2 = u64::SubWithBorrow(b, a);\n  EXPECT_EQ(result2.result, b - a);\n  EXPECT_EQ(result2.borrow, 1);\n\n  b = a;\n  auto result3 = u64::SubWithBorrow(a, b, 1);\n  EXPECT_EQ(result3.result, a - b - 1);\n  EXPECT_EQ(result3.borrow, 1);\n}\n\nTEST(Arithmetics, MulAddWithCarry32) {\n  uint32_t a = GetRandomLo32();\n  uint32_t b = GetRandomSqrt32() / 2;\n  uint32_t c = GetRandomSqrt32();\n  SCOPED_TRACE(absl::Substitute(\"a: $0, b: $1, c: $2\", a, b, c));\n\n  uint64_t bc = uint64_t{b} * uint64_t{c};\n  auto result = u32::MulAddWithCarry(a, b, c);\n  EXPECT_EQ(result.lo, static_cast<uint32_t>(uint64_t{a} + bc));\n  EXPECT_EQ(result.hi, 0);\n\n  a = static_cast<uint32_t>(std::numeric_limits<uint64_t>::max() - bc);\n  auto result2 = u32::MulAddWithCarry(a, b, c, 1);\n  EXPECT_EQ(result2.lo, static_cast<uint32_t>(uint64_t{a} + bc + uint64_t{1}));\n  EXPECT_EQ(result2.hi, 1);\n}\n\nTEST(Arithmetics, MulAddWithCarry64) {\n  uint64_t a = GetRandomLo64();\n  uint64_t b = GetRandomSqrt64() / 2;\n  uint64_t c = GetRandomSqrt64();\n  SCOPED_TRACE(absl::Substitute(\"a: $0, b: $1, c: $2\", a, b, c));\n\n  absl::uint128 bc = absl::uint128(b) * absl::uint128(c);\n  auto result = u64::MulAddWithCarry(a, b, c);\n  EXPECT_EQ(result.lo, absl::Uint128Low64(absl::uint128(a) + bc));\n  EXPECT_EQ(result.hi, 0);\n\n  a = absl::Uint128Low64(std::numeric_limits<absl::uint128>::max() - bc);\n  auto result2 = u64::MulAddWithCarry(a, b, c, 1);\n  EXPECT_EQ(result2.lo,\n            absl::Uint128Low64(absl::uint128(a) + bc + absl::uint128(1)));\n  EXPECT_EQ(result2.hi, 1);\n}\n\n}  // namespace tachyon::math::internal\n"
  },
  {
    "path": "tachyon/math/base/batch_inverse_benchmark.cc",
    "content": "#include \"benchmark/benchmark.h\"\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/fq.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename F>\nvoid BM_BatchInverseSerial(benchmark::State& state) {\n  using BigInt = typename F::BigIntTy;\n\n  std::vector<F> fields = base::CreateVectorParallel(\n      state.range(0), [](size_t i) { return F::FromBigInt(BigInt(i + 1)); });\n  for (auto _ : state) {\n    CHECK(F::BatchInverseInPlaceSerial(fields));\n  }\n  benchmark::DoNotOptimize(fields);\n}\n\ntemplate <typename F>\nvoid BM_BatchInverse(benchmark::State& state) {\n  using BigInt = typename F::BigIntTy;\n\n  std::vector<F> fields = base::CreateVectorParallel(\n      state.range(0), [](size_t i) { return F::FromBigInt(BigInt(i + 1)); });\n  for (auto _ : state) {\n    CHECK(F::BatchInverseInPlace(fields));\n  }\n  benchmark::DoNotOptimize(fields);\n}\n\ntemplate <typename F>\nvoid BM_InverseParallelFor(benchmark::State& state) {\n  using BigInt = typename F::BigIntTy;\n\n  std::vector<F> fields = base::CreateVectorParallel(\n      state.range(0), [](size_t i) { return F::FromBigInt(BigInt(i + 1)); });\n  for (auto _ : state) {\n    OMP_PARALLEL_FOR(size_t i = 0; i < fields.size(); ++i) {\n      CHECK(fields[i].InverseInPlace());\n    }\n  }\n  benchmark::DoNotOptimize(fields);\n}\n\nBENCHMARK_TEMPLATE(BM_BatchInverseSerial, bn254::Fq)\n    ->RangeMultiplier(2)\n    ->Range(1 << 5, 1 << 20);\n\nBENCHMARK_TEMPLATE(BM_BatchInverse, bn254::Fq)\n    ->RangeMultiplier(2)\n    ->Range(1 << 5, 1 << 20);\n\nBENCHMARK_TEMPLATE(BM_InverseParallelFor, bn254::Fq)\n    ->RangeMultiplier(2)\n    ->Range(1 << 5, 1 << 20);\n\n}  // namespace tachyon::math\n\n// clang-format off\n// Executing tests from //tachyon/math/base:batch_inverse_benchmark\n// -----------------------------------------------------------------------------\n// 2024-02-13T05:16:40+00:00\n// Running /home/ryan/.cache/bazel/_bazel_ryan/d6800124b8b6155cc6ab653ae18dfdd6/execroot/kroma_network_tachyon/bazel-out/k8-opt/bin/tachyon/math/base/batch_inverse_benchmark.runfiles/kroma_network_tachyon/tachyon/math/base/batch_inverse_benchmark\n// Run on (32 X 5500 MHz CPU s)\n// CPU Caches:\n//   L1 Data 48 KiB (x16)\n//   L1 Instruction 32 KiB (x16)\n//   L2 Unified 2048 KiB (x16)\n//   L3 Unified 36864 KiB (x1)\n// Load Average: 12.51, 18.64, 11.34\n// ------------------------------------------------------------------------------------\n// Benchmark                                          Time             CPU   Iterations\n// ------------------------------------------------------------------------------------\n// BM_BatchInverseSerial<bn254::Fq>/32            3107 ns         3107 ns       225050\n// BM_BatchInverseSerial<bn254::Fq>/64            5154 ns         5154 ns       135741\n// BM_BatchInverseSerial<bn254::Fq>/128           8236 ns         8235 ns        84985\n// BM_BatchInverseSerial<bn254::Fq>/256          14356 ns        14356 ns        48760\n// BM_BatchInverseSerial<bn254::Fq>/512          26553 ns        26553 ns        26368\n// BM_BatchInverseSerial<bn254::Fq>/1024         51145 ns        51144 ns        13658\n// BM_BatchInverseSerial<bn254::Fq>/2048        100272 ns       100267 ns         6978\n// BM_BatchInverseSerial<bn254::Fq>/4096        198646 ns       198616 ns         3521\n// BM_BatchInverseSerial<bn254::Fq>/8192        405130 ns       404911 ns         1768\n// BM_BatchInverseSerial<bn254::Fq>/16384       790625 ns       790610 ns          879\n// BM_BatchInverseSerial<bn254::Fq>/32768      1581307 ns      1581250 ns          442\n// BM_BatchInverseSerial<bn254::Fq>/65536      3160691 ns      3160623 ns          220\n// BM_BatchInverseSerial<bn254::Fq>/131072     6327060 ns      6326715 ns          109\n// BM_BatchInverseSerial<bn254::Fq>/262144    12804429 ns     12803991 ns           54\n// BM_BatchInverseSerial<bn254::Fq>/524288    25865104 ns     25863830 ns           27\n// BM_BatchInverseSerial<bn254::Fq>/1048576   60116311 ns     60114166 ns           12\n// BM_BatchInverse<bn254::Fq>/32                  3112 ns         3111 ns       225093\n// BM_BatchInverse<bn254::Fq>/64                  5211 ns         5210 ns       135043\n// BM_BatchInverse<bn254::Fq>/128                 8259 ns         8259 ns        84689\n// BM_BatchInverse<bn254::Fq>/256              1416243 ns      1257341 ns         1000\n// BM_BatchInverse<bn254::Fq>/512               157228 ns       144531 ns        75464\n// BM_BatchInverse<bn254::Fq>/1024             3287592 ns      2926591 ns         1000\n// BM_BatchInverse<bn254::Fq>/2048               29447 ns        27628 ns        51731\n// BM_BatchInverse<bn254::Fq>/4096              693075 ns       621306 ns        33576\n// BM_BatchInverse<bn254::Fq>/8192             8797123 ns      7401274 ns           94\n// BM_BatchInverse<bn254::Fq>/16384            8847607 ns      7432851 ns          107\n// BM_BatchInverse<bn254::Fq>/32768            8931447 ns      7878056 ns           88\n// BM_BatchInverse<bn254::Fq>/65536            9000511 ns      7951377 ns           84\n// BM_BatchInverse<bn254::Fq>/131072           4666971 ns      4170687 ns          199\n// BM_BatchInverse<bn254::Fq>/262144           6942271 ns      6849338 ns          335\n// BM_BatchInverse<bn254::Fq>/524288          10203157 ns      9504026 ns          148\n// BM_BatchInverse<bn254::Fq>/1048576          8687019 ns      7257869 ns          197\n// BM_InverseParallelFor<bn254::Fq>/32           60951 ns        59605 ns        10000\n// BM_InverseParallelFor<bn254::Fq>/64          110740 ns       101640 ns        66209\n// BM_InverseParallelFor<bn254::Fq>/128        5896806 ns      5134363 ns         1000\n// BM_InverseParallelFor<bn254::Fq>/256        8795295 ns      7209770 ns          114\n// BM_InverseParallelFor<bn254::Fq>/512        8776199 ns      7490824 ns           88\n// BM_InverseParallelFor<bn254::Fq>/1024       7800870 ns      6280526 ns          108\n// BM_InverseParallelFor<bn254::Fq>/2048       9148910 ns      7553591 ns           81\n// BM_InverseParallelFor<bn254::Fq>/4096       9734373 ns      8234960 ns          100\n// BM_InverseParallelFor<bn254::Fq>/8192       8042700 ns      6609198 ns           93\n// BM_InverseParallelFor<bn254::Fq>/16384      6758195 ns      5395180 ns          312\n// BM_InverseParallelFor<bn254::Fq>/32768      6857435 ns      5830095 ns          108\n// BM_InverseParallelFor<bn254::Fq>/65536     10401757 ns      9951429 ns           53\n// BM_InverseParallelFor<bn254::Fq>/131072    20822048 ns     18544346 ns           36\n// BM_InverseParallelFor<bn254::Fq>/262144    37196371 ns     34716896 ns           18\n// BM_InverseParallelFor<bn254::Fq>/524288    76307365 ns     73958721 ns            7\n// BM_InverseParallelFor<bn254::Fq>/1048576  154857337 ns    140068730 ns            4\n// clang-format on\n"
  },
  {
    "path": "tachyon/math/base/big_int.cc",
    "content": "#include \"tachyon/math/base/big_int.h\"\n\n#include <string>\n\n#include \"tachyon/base/strings/string_util.h\"\n#include \"tachyon/math/base/gmp/gmp_util.h\"\n\nnamespace tachyon::math::internal {\n\nnamespace {\n\n// TODO(chokobole): Remove gmp dependency.\ntemplate <size_t Base>\nbool DoStringToLimbs(std::string_view str, uint64_t* limbs, size_t limb_nums) {\n  mpz_class out;\n  if (!gmp::ParseIntoMpz(str, Base, &out)) {\n    LOG(ERROR) << \"failed to parse string(\" << str << \") with base(\" << Base\n               << \")\";\n    return false;\n  }\n  size_t actual_limb_nums = gmp::GetLimbSize(out);\n  if (actual_limb_nums > limb_nums) {\n    LOG(ERROR) << \"actual limb nums(\" << actual_limb_nums\n               << \") is greater than expected limb nums(\" << limb_nums << \")\";\n    return false;\n  }\n  gmp::CopyLimbs(out, limbs);\n  return true;\n}\n\ntemplate <size_t Base>\nstd::string DoLimbsToString(const uint64_t* limbs, size_t limb_nums,\n                            bool pad_zero) {\n  mpz_class out;\n  gmp::WriteLimbs(limbs, limb_nums, &out);\n  std::string str = out.get_str(Base);\n  if (!pad_zero) return str;\n  return base::ToHexStringWithLeadingZero(str, limb_nums * 8 * 2);\n}\n\n}  // namespace\n\nbool StringToLimbs(std::string_view str, uint64_t* limbs, size_t limb_nums) {\n  return DoStringToLimbs<10>(str, limbs, limb_nums);\n}\n\nbool HexStringToLimbs(std::string_view str, uint64_t* limbs, size_t limb_nums) {\n  return DoStringToLimbs<16>(str, limbs, limb_nums);\n}\n\nstd::string LimbsToString(const uint64_t* limbs, size_t limb_nums) {\n  return DoLimbsToString<10>(limbs, limb_nums, false);\n}\n\nstd::string LimbsToHexString(const uint64_t* limbs, size_t limb_nums,\n                             bool pad_zero) {\n  return base::MaybePrepend0x(DoLimbsToString<16>(limbs, limb_nums, pad_zero));\n}\n\n}  // namespace tachyon::math::internal\n"
  },
  {
    "path": "tachyon/math/base/big_int.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_BASE_BIG_INT_H_\n#define TACHYON_MATH_BASE_BIG_INT_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <array>\n#include <bitset>\n#include <limits>\n#include <optional>\n#include <string>\n#include <tuple>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/bit_cast.h\"\n#include \"tachyon/base/buffer/copyable.h\"\n#include \"tachyon/base/endian_utils.h\"\n#include \"tachyon/base/json/json.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/random.h\"\n#include \"tachyon/build/build_config.h\"\n#include \"tachyon/math/base/arithmetics.h\"\n#include \"tachyon/math/base/bit_traits_forward.h\"\n\nnamespace tachyon {\nnamespace math {\nnamespace internal {\n\nTACHYON_EXPORT bool StringToLimbs(std::string_view str, uint64_t* limbs,\n                                  size_t limb_nums);\nTACHYON_EXPORT bool HexStringToLimbs(std::string_view str, uint64_t* limbs,\n                                     size_t limb_nums);\n\nTACHYON_EXPORT std::string LimbsToString(const uint64_t* limbs,\n                                         size_t limb_nums);\nTACHYON_EXPORT std::string LimbsToHexString(const uint64_t* limbs,\n                                            size_t limb_nums, bool pad_zero);\n\n}  // namespace internal\n\n// BigInt is a fixed size array of uint64_t, capable of holding up to |N| limbs,\n// designed to support a wide range of big integer arithmetic operations.\ntemplate <size_t N>\nstruct BigInt {\n  uint64_t limbs[N];\n  constexpr static size_t kLimbNums = N;\n  constexpr static size_t kSmallestLimbIdx = SMALLEST_INDEX(N);\n  constexpr static size_t kBiggestLimbIdx = BIGGEST_INDEX(N);\n  constexpr static size_t kLimbByteNums = sizeof(uint64_t);\n  constexpr static size_t kByteNums = N * sizeof(uint64_t);\n  constexpr static size_t kLimbBitNums = kLimbByteNums * 8;\n  constexpr static size_t kBitNums = kByteNums * 8;\n\n  constexpr BigInt() = default;\n  template <typename T, std::enable_if_t<std::is_signed_v<T>>* = nullptr>\n  constexpr explicit BigInt(T value) : limbs{0} {\n    DCHECK_GE(value, 0);\n    limbs[kSmallestLimbIdx] = value;\n  }\n  template <typename T, std::enable_if_t<std::is_unsigned_v<T>>* = nullptr>\n  constexpr explicit BigInt(T value) : limbs{0} {\n    limbs[kSmallestLimbIdx] = value;\n  }\n  template <typename T, std::enable_if_t<std::is_signed_v<T>>* = nullptr>\n  constexpr explicit BigInt(std::initializer_list<T> values) : limbs{0} {\n    DCHECK_LE(values.size(), N);\n    auto it = values.begin();\n    for (size_t i = 0; i < values.size(); ++i, ++it) {\n      DCHECK_GE(*it, 0);\n      limbs[i] = *it;\n    }\n  }\n  template <typename T, std::enable_if_t<std::is_unsigned_v<T>>* = nullptr>\n  constexpr explicit BigInt(std::initializer_list<T> values) : limbs{0} {\n    DCHECK_LE(values.size(), N);\n    auto it = values.begin();\n    for (size_t i = 0; i < values.size(); ++i, ++it) {\n      limbs[i] = *it;\n    }\n  }\n  constexpr explicit BigInt(const uint64_t limbs[N]) {\n    memcpy(this->limbs, limbs, sizeof(uint64_t) * N);\n  }\n\n  constexpr static BigInt Zero() { return BigInt(0); }\n\n  constexpr static BigInt One() { return BigInt(1); }\n\n  // Returns the maximum representable value for BigInt.\n  constexpr static BigInt Max() {\n    BigInt ret{};\n    for (uint64_t& limb : ret.limbs) {\n      limb = std::numeric_limits<uint64_t>::max();\n    }\n    return ret;\n  }\n\n  // Generate a random BigInt between [0, |max|).\n  constexpr static BigInt Random(const BigInt& max = Max()) {\n    BigInt ret{};\n    for (size_t i = 0; i < N; ++i) {\n      ret[i] = base::Uniform(base::Range<uint64_t>::All());\n    }\n    while (ret >= max) {\n      ret.DivBy2InPlace();\n    }\n    return ret;\n  }\n\n  // Convert a decimal string to a BigInt.\n  static std::optional<BigInt> FromDecString(std::string_view str) {\n    BigInt ret(0);\n    if (!internal::StringToLimbs(str, ret.limbs, N)) return std::nullopt;\n    return ret;\n  }\n\n  // Convert a hexadecimal string to a BigInt.\n  static std::optional<BigInt> FromHexString(std::string_view str) {\n    BigInt ret(0);\n    if (!(internal::HexStringToLimbs(str, ret.limbs, N))) return std::nullopt;\n    return ret;\n  }\n\n  // Constructs a BigInt value from a given array of bits in little-endian\n  // order.\n  template <size_t BitNums = kBitNums>\n  constexpr static BigInt FromBitsLE(const std::bitset<BitNums>& bits) {\n    static_assert(BitNums <= kBitNums);\n    BigInt ret{};\n    size_t bit_idx = 0;\n    size_t limb_idx = 0;\n    std::bitset<kLimbBitNums> limb_bits;\n    FOR_FROM_SMALLEST(i, 0, BitNums) {\n      limb_bits.set(bit_idx++, bits[i]);\n      bool set = bit_idx == kLimbBitNums;\n#if ARCH_CPU_BIG_ENDIAN\n      set |= (i == 0);\n#else\n      set |= (i == BitNums - 1);\n#endif\n      if (set) {\n        uint64_t limb = base::bit_cast<uint64_t>(limb_bits.to_ullong());\n        ret.limbs[limb_idx++] = limb;\n        limb_bits.reset();\n        bit_idx = 0;\n      }\n    }\n    return ret;\n  }\n\n  // Constructs a BigInt value from a given array of bits in big-endian order.\n  template <size_t BitNums = kBitNums>\n  constexpr static BigInt FromBitsBE(const std::bitset<BitNums>& bits) {\n    static_assert(BitNums <= kBitNums);\n    BigInt ret{};\n    std::bitset<kLimbBitNums> limb_bits;\n    size_t bit_idx = 0;\n    size_t limb_idx = 0;\n    FOR_FROM_BIGGEST(i, 0, BitNums) {\n      limb_bits.set(bit_idx++, bits[i]);\n      bool set = bit_idx == kLimbBitNums;\n#if ARCH_CPU_BIG_ENDIAN\n      set |= (i == BitNums - 1);\n#else\n      set |= (i == 0);\n#endif\n      if (set) {\n        uint64_t limb = base::bit_cast<uint64_t>(limb_bits.to_ullong());\n        ret.limbs[limb_idx++] = limb;\n        limb_bits.reset();\n        bit_idx = 0;\n      }\n    }\n    return ret;\n  }\n\n  // Constructs a BigInt value from a given byte container interpreted in\n  // little-endian order. The method processes each byte of the input, packs\n  // them into 64-bit limbs, and then sets these limbs in the resulting BigInt.\n  // If the system is big-endian, adjustments are made to ensure correct byte\n  // ordering.\n  template <typename ByteContainer>\n  constexpr static BigInt FromBytesLE(const ByteContainer& bytes) {\n    BigInt ret{};\n    size_t byte_idx = 0;\n    size_t limb_idx = 0;\n    uint64_t limb = 0;\n    FOR_FROM_SMALLEST(i, 0, std::size(bytes)) {\n      reinterpret_cast<uint8_t*>(&limb)[byte_idx++] = bytes[i];\n      bool set = byte_idx == kLimbByteNums;\n#if ARCH_CPU_BIG_ENDIAN\n      set |= (i == 0);\n#else\n      set |= (i == std::size(bytes) - 1);\n#endif\n      if (set) {\n        ret.limbs[limb_idx++] = limb;\n        limb = 0;\n        byte_idx = 0;\n      }\n    }\n    return ret;\n  }\n\n  // Constructs a BigInt value from a given byte container interpreted in\n  // big-endian order. The method processes each byte of the input, packs them\n  // into 64-bit limbs, and then sets these limbs in the resulting BigInt. If\n  // the system is little-endian, adjustments are made to ensure correct byte\n  // ordering.\n  template <typename ByteContainer>\n  constexpr static BigInt FromBytesBE(const ByteContainer& bytes) {\n    BigInt ret{};\n    size_t byte_idx = 0;\n    size_t limb_idx = 0;\n    uint64_t limb = 0;\n    FOR_FROM_BIGGEST(i, 0, std::size(bytes)) {\n      reinterpret_cast<uint8_t*>(&limb)[byte_idx++] = bytes[i];\n      bool set = byte_idx == kLimbByteNums;\n#if ARCH_CPU_BIG_ENDIAN\n      set |= (i == std::size(bytes) - 1);\n#else\n      set |= (i == 0);\n#endif\n      if (set) {\n        ret.limbs[limb_idx++] = limb;\n        limb = 0;\n        byte_idx = 0;\n      }\n    }\n    return ret;\n  }\n\n  constexpr static BigInt FromMontgomery32(const BigInt<N>& value,\n                                           const BigInt<N>& modulus,\n                                           uint32_t inverse) {\n    return FromMontgomery(value, modulus, inverse);\n  }\n\n  constexpr static BigInt FromMontgomery64(const BigInt<N>& value,\n                                           const BigInt<N>& modulus,\n                                           uint64_t inverse) {\n    return FromMontgomery(value, modulus, inverse);\n  }\n\n  // Extend the current |N| size BigInt to a larger |N2| size.\n  template <size_t N2>\n  constexpr BigInt<N2> Extend() const {\n    static_assert(N2 > N);\n    BigInt<N2> ret;\n    for (size_t i = 0; i < N2; ++i) {\n      ret[i] = i < N ? limbs[i] : 0;\n    }\n    return ret;\n  }\n\n  // Shrink the current |N| size BigInt to a smaller |N2| size.\n  template <size_t N2>\n  constexpr BigInt<N2> Shrink() const {\n    static_assert(N2 < N);\n    BigInt<N2> ret;\n    for (size_t i = 0; i < N2; ++i) {\n      ret[i] = limbs[i];\n    }\n    return ret;\n  }\n\n  // Clamp the BigInt value with respect to a modulus.\n  // If the value is larger than or equal to the modulus, then the modulus is\n  // subtracted from the value. The function considers a spare bit in the\n  // modulus based on the template parameter.\n  template <bool ModulusHasSpareBit>\n  constexpr static void Clamp(const BigInt& modulus, BigInt* value,\n                              [[maybe_unused]] bool carry = false) {\n    bool needs_to_clamp = false;\n    if constexpr (ModulusHasSpareBit) {\n      needs_to_clamp = *value >= modulus;\n    } else {\n      needs_to_clamp = carry || *value >= modulus;\n    }\n    if (needs_to_clamp) {\n      value->SubInPlace(modulus);\n    }\n  }\n\n  template <bool ModulusHasSpareBit>\n  constexpr static void MontgomeryReduce32(BigInt<2 * N>& r,\n                                           const BigInt& modulus,\n                                           uint32_t inverse, BigInt* out) {\n    MontgomeryReduce<ModulusHasSpareBit>(r, modulus, inverse, out);\n  }\n\n  template <bool ModulusHasSpareBit>\n  constexpr static void MontgomeryReduce64(BigInt<2 * N>& r,\n                                           const BigInt& modulus,\n                                           uint64_t inverse, BigInt* out) {\n    MontgomeryReduce<ModulusHasSpareBit>(r, modulus, inverse, out);\n  }\n\n  constexpr bool IsZero() const {\n    for (size_t i = 0; i < N; ++i) {\n      if (limbs[i] != 0) return false;\n    }\n    return true;\n  }\n\n  constexpr bool IsOne() const {\n    FOR_BUT_SMALLEST(i, N) {\n      if (limbs[i] != 0) return false;\n    }\n    return limbs[kSmallestLimbIdx] == 1;\n  }\n\n  constexpr bool IsEven() const { return limbs[kSmallestLimbIdx] % 2 == 0; }\n  constexpr bool IsOdd() const { return limbs[kSmallestLimbIdx] % 2 == 1; }\n\n  // Return the largest (most significant) limb of the BigInt.\n  constexpr uint64_t& biggest_limb() { return limbs[kBiggestLimbIdx]; }\n  constexpr const uint64_t& biggest_limb() const {\n    return limbs[kBiggestLimbIdx];\n  }\n\n  // Return the smallest (least significant) limb of the BigInt.\n  constexpr uint64_t& smallest_limb() { return limbs[kSmallestLimbIdx]; }\n  constexpr const uint64_t& smallest_limb() const {\n    return limbs[kSmallestLimbIdx];\n  }\n\n  // Extracts a specified number of bits starting from a given bit offset and\n  // returns them as a uint64_t.\n  constexpr uint64_t ExtractBits64(size_t bit_offset, size_t bit_count) const {\n    return ExtractBits<uint64_t>(bit_offset, bit_count);\n  }\n\n  // Extracts a specified number of bits starting from a given bit offset and\n  // returns them as a uint32_t.\n  constexpr uint32_t ExtractBits32(size_t bit_offset, size_t bit_count) const {\n    return ExtractBits<uint32_t>(bit_offset, bit_count);\n  }\n\n  constexpr uint64_t& operator[](size_t i) {\n    DCHECK_LT(i, N);\n    return limbs[i];\n  }\n  constexpr const uint64_t& operator[](size_t i) const {\n    DCHECK_LT(i, N);\n    return limbs[i];\n  }\n\n  constexpr bool operator==(const BigInt& other) const {\n    for (size_t i = 0; i < N; ++i) {\n      if (limbs[i] != other.limbs[i]) return false;\n    }\n    return true;\n  }\n\n  constexpr bool operator!=(const BigInt& other) const {\n    return !operator==(other);\n  }\n\n  constexpr bool operator<(const BigInt& other) const {\n    FOR_FROM_BIGGEST(i, 0, N) {\n      if (limbs[i] == other.limbs[i]) continue;\n      return limbs[i] < other.limbs[i];\n    }\n    return false;\n  }\n\n  constexpr bool operator>(const BigInt& other) const {\n    FOR_FROM_BIGGEST(i, 0, N) {\n      if (limbs[i] == other.limbs[i]) continue;\n      return limbs[i] > other.limbs[i];\n    }\n    return false;\n  }\n\n  constexpr bool operator<=(const BigInt& other) const {\n    FOR_FROM_BIGGEST(i, 0, N) {\n      if (limbs[i] == other.limbs[i]) continue;\n      return limbs[i] < other.limbs[i];\n    }\n    return true;\n  }\n\n  constexpr bool operator>=(const BigInt& other) const {\n    FOR_FROM_BIGGEST(i, 0, N) {\n      if (limbs[i] == other.limbs[i]) continue;\n      return limbs[i] > other.limbs[i];\n    }\n    return true;\n  }\n\n  constexpr BigInt operator+(const BigInt& other) const { return Add(other); }\n\n  constexpr BigInt& operator+=(const BigInt& other) {\n    return AddInPlace(other);\n  }\n\n  constexpr BigInt operator-(const BigInt& other) const { return Sub(other); }\n\n  constexpr BigInt& operator-=(const BigInt& other) {\n    return SubInPlace(other);\n  }\n\n  constexpr BigInt operator*(const BigInt& other) const { return Mul(other); }\n\n  constexpr BigInt& operator*=(const BigInt& other) {\n    *this = Mul(other);\n    return *this;\n  }\n\n  constexpr std::optional<BigInt> operator/(const BigInt& other) const {\n    return Div(other);\n  }\n\n  [[nodiscard]] constexpr std::optional<BigInt*> operator/=(\n      const BigInt& other) {\n    const std::optional<BigInt> div = Div(other);\n    if (LIKELY(div)) {\n      *this = std::move(*div);\n      return this;\n    }\n    LOG_IF_NOT_GPU(ERROR) << \"Division by zero attempted\";\n    return std::nullopt;\n  }\n\n  constexpr BigInt operator%(const BigInt& other) const { return Mod(other); }\n\n  constexpr BigInt& operator%=(const BigInt& other) {\n    *this = Mod(other);\n    return *this;\n  }\n\n  constexpr BigInt operator&(const BigInt& other) const {\n    BigInt ret{};\n    if constexpr (N == 1) {\n      ret[0] = limbs[0] & other[0];\n    } else if constexpr (N == 2) {\n      ret[0] = limbs[0] & other[0];\n      ret[1] = limbs[1] & other[1];\n    } else {\n      DoAnd(*this, other, ret);\n    }\n    return ret;\n  }\n\n  constexpr BigInt& operator&=(const BigInt& other) {\n    if constexpr (N == 1) {\n      limbs[0] &= other[0];\n    } else if constexpr (N == 2) {\n      limbs[0] &= other[0];\n      limbs[1] &= other[1];\n    } else {\n      DoAnd(*this, other, *this);\n    }\n    return *this;\n  }\n\n  constexpr BigInt operator|(const BigInt& other) const {\n    BigInt ret{};\n    if constexpr (N == 1) {\n      ret[0] = limbs[0] | other[0];\n    } else if constexpr (N == 2) {\n      ret[0] = limbs[0] | other[0];\n      ret[1] = limbs[1] | other[1];\n    } else {\n      DoOr(*this, other, ret);\n    }\n    return ret;\n  }\n\n  constexpr BigInt& operator|=(const BigInt& other) {\n    if constexpr (N == 1) {\n      limbs[0] |= other[0];\n    } else if constexpr (N == 2) {\n      limbs[0] |= other[0];\n      limbs[1] |= other[1];\n    } else {\n      DoOr(*this, other, *this);\n    }\n    return *this;\n  }\n\n  constexpr BigInt operator^(const BigInt& other) const {\n    BigInt ret{};\n    if constexpr (N == 1) {\n      ret[0] = limbs[0] ^ other[0];\n    } else if constexpr (N == 2) {\n      ret[0] = limbs[0] ^ other[0];\n      ret[1] = limbs[1] ^ other[1];\n    } else {\n      DoXor(*this, other, ret);\n    }\n    return ret;\n  }\n\n  constexpr BigInt& operator^=(const BigInt& other) {\n    if constexpr (N == 1) {\n      limbs[0] ^= other[0];\n    } else if constexpr (N == 2) {\n      limbs[0] ^= other[0];\n      limbs[1] ^= other[1];\n    } else {\n      DoXor(*this, other, *this);\n    }\n    return *this;\n  }\n\n  constexpr BigInt operator<<(uint32_t count) const {\n    BigInt ret = *this;\n    ret.MulBy2ExpInPlace(count);\n    return ret;\n  }\n\n  constexpr BigInt& operator<<=(uint32_t count) {\n    MulBy2ExpInPlace(count);\n    return *this;\n  }\n\n  constexpr BigInt operator>>(uint32_t count) const {\n    BigInt ret = *this;\n    ret.DivBy2ExpInPlace(count);\n    return ret;\n  }\n\n  constexpr BigInt& operator>>=(uint32_t count) {\n    DivBy2ExpInPlace(count);\n    return *this;\n  }\n\n  constexpr BigInt Add(const BigInt& other) const {\n    uint64_t unused = 0;\n    return Add(other, unused);\n  }\n\n  constexpr BigInt Add(const BigInt& other, uint64_t& carry) const {\n    BigInt ret{};\n    DoAdd(*this, other, carry, ret);\n    return ret;\n  }\n\n  constexpr BigInt& AddInPlace(const BigInt& other) {\n    uint64_t unused = 0;\n    return AddInPlace(other, unused);\n  }\n\n  constexpr BigInt& AddInPlace(const BigInt& other, uint64_t& carry) {\n    DoAdd(*this, other, carry, *this);\n    return *this;\n  }\n\n  constexpr BigInt Sub(const BigInt& other) const {\n    uint64_t unused = 0;\n    return Sub(other, unused);\n  }\n\n  constexpr BigInt Sub(const BigInt& other, uint64_t& borrow) const {\n    BigInt ret{};\n    DoSub(*this, other, borrow, ret);\n    return ret;\n  }\n\n  constexpr BigInt& SubInPlace(const BigInt& other) {\n    uint64_t unused = 0;\n    return SubInPlace(other, unused);\n  }\n\n  constexpr BigInt& SubInPlace(const BigInt& other, uint64_t& borrow) {\n    DoSub(*this, other, borrow, *this);\n    return *this;\n  }\n\n  constexpr BigInt MulBy2() const {\n    uint64_t unused = 0;\n    return MulBy2(unused);\n  }\n\n  constexpr BigInt MulBy2(uint64_t& carry) const {\n    BigInt ret{};\n    DoMulBy2(*this, carry, ret);\n    return ret;\n  }\n\n  constexpr BigInt& MulBy2InPlace() {\n    uint64_t unused = 0;\n    return MulBy2InPlace(unused);\n  }\n\n  constexpr BigInt& MulBy2InPlace(uint64_t& carry) {\n    DoMulBy2(*this, carry, *this);\n    return *this;\n  }\n\n  constexpr BigInt& MulBy2ExpInPlace(uint32_t n) {\n    if (n >= static_cast<uint32_t>(64 * N)) {\n      memset(limbs, 0, sizeof(uint64_t) * N);\n      return *this;\n    }\n\n    while (n >= 64) {\n      uint64_t t = 0;\n      FOR_FROM_SMALLEST(i, 0, N) { std::swap(t, limbs[i]); }\n      n -= 64;\n    }\n\n    if (n > uint32_t{0}) {\n      uint64_t t = 0;\n      FOR_FROM_SMALLEST(i, 0, N) {\n        uint64_t t2 = limbs[i] >> (64 - n);\n        limbs[i] <<= n;\n        limbs[i] |= t;\n        t = t2;\n      }\n    }\n    return *this;\n  }\n\n  constexpr BigInt Mul(const BigInt& other) const { return Multiply(other).lo; }\n\n  constexpr BigInt<2 * N> MulExtend(const BigInt& other) const {\n    MulResult<BigInt> result = Multiply(other);\n    BigInt<2 * N> ret;\n    memcpy(&ret[0], &result.lo[0], sizeof(uint64_t) * N);\n    memcpy(&ret[N], &result.hi[0], sizeof(uint64_t) * N);\n    return ret;\n  }\n\n  constexpr MulResult<BigInt> Multiply(const BigInt& other) const {\n    MulResult<BigInt> ret;\n    MulResult<uint64_t> mul_result;\n    FOR_FROM_SMALLEST(i, 0, N) {\n      FOR_FROM_SMALLEST(j, 0, N) {\n        uint64_t& limb = (i + j) >= N ? ret.hi[(i + j) - N] : ret.lo[i + j];\n        mul_result = internal::u64::MulAddWithCarry(limb, limbs[i], other[j],\n                                                    mul_result.hi);\n        limb = mul_result.lo;\n      }\n      ret.hi[i] = mul_result.hi;\n      mul_result.hi = 0;\n    }\n    return ret;\n  }\n\n  constexpr BigInt& DivBy2InPlace() {\n    uint64_t last = 0;\n    FOR_FROM_BIGGEST(i, 0, N) {\n      uint64_t temp = limbs[i] << 63;\n      limbs[i] >>= 1;\n      limbs[i] |= last;\n      last = temp;\n    }\n    return *this;\n  }\n\n  constexpr BigInt& DivBy2ExpInPlace(uint32_t n) {\n    if (n >= static_cast<uint32_t>(64 * N)) {\n      memset(limbs, 0, sizeof(uint64_t) * N);\n      return *this;\n    }\n\n    if constexpr (N > 1) {\n      while (n >= 64) {\n        uint64_t t = 0;\n        FOR_FROM_BIGGEST(i, 0, N) { std::swap(t, limbs[i]); }\n        n -= 64;\n      }\n    }\n\n    if (n > uint32_t{0}) {\n      uint64_t t = 0;\n      FOR_FROM_BIGGEST(i, 0, N) {\n        uint64_t t2 = limbs[i] << (64 - n);\n        limbs[i] >>= n;\n        limbs[i] |= t;\n        t = t2;\n      }\n    }\n    return *this;\n  }\n\n  constexpr std::optional<BigInt> Div(const BigInt& other) const {\n    DivResult<BigInt> result;\n    if (LIKELY(Divide(other, result))) return result.quotient;\n    LOG_IF_NOT_GPU(ERROR) << \"Division by zero attempted\";\n    return std::nullopt;\n  }\n\n  // NOTE(ashjeong): we assume that mod 0 will never occur\n  constexpr BigInt Mod(const BigInt& other) const {\n    DivResult<BigInt> result;\n    std::ignore = Divide(other, result);\n    return result.remainder;\n  }\n\n  [[nodiscard]] constexpr bool Divide(const BigInt<N>& divisor,\n                                      DivResult<BigInt>& output) const {\n    // Stupid slow base-2 long division taken from\n    // https://en.wikipedia.org/wiki/Division_algorithm\n    if (UNLIKELY((divisor.IsZero()))) {\n      LOG_IF_NOT_GPU(ERROR) << \"Division by zero attempted\";\n      return false;\n    }\n    BigInt quotient(0);\n    BigInt remainder(0);\n    size_t bits = BitTraits<BigInt>::GetNumBits(*this);\n    uint64_t carry = 0;\n    uint64_t& smallest_bit = remainder.limbs[kSmallestLimbIdx];\n    FOR_FROM_BIGGEST(i, 0, bits) {\n      carry = 0;\n      remainder.MulBy2InPlace(carry);\n      smallest_bit |= BitTraits<BigInt>::TestBit(*this, i);\n      if (remainder >= divisor || carry) {\n        uint64_t borrow = 0;\n        remainder.SubInPlace(divisor, borrow);\n        if (UNLIKELY(borrow != carry)) return false;\n        BitTraits<BigInt>::SetBit(quotient, i, 1);\n      }\n    }\n    output = DivResult<BigInt>{std::move(quotient), std::move(remainder)};\n    return true;\n  }\n\n  std::string ToString() const { return internal::LimbsToString(limbs, N); }\n  std::string ToHexString(bool pad_zero = false) const {\n    return internal::LimbsToHexString(limbs, N, pad_zero);\n  }\n\n  // Converts the BigInt to a bit array in little-endian.\n  template <size_t BitNums = kBitNums>\n  std::bitset<BitNums> ToBitsLE() const {\n    std::bitset<BitNums> ret;\n    size_t bit_w_idx = 0;\n    FOR_FROM_SMALLEST(i, 0, BitNums) {\n      size_t limb_idx = i / kLimbBitNums;\n      size_t bit_r_idx = i % kLimbBitNums;\n      bool bit = (limbs[limb_idx] & (uint64_t{1} << bit_r_idx)) >> bit_r_idx;\n      ret.set(bit_w_idx++, bit);\n    }\n    return ret;\n  }\n\n  // Converts the BigInt to a bit array in big-endian.\n  template <size_t BitNums = kBitNums>\n  std::bitset<BitNums> ToBitsBE() const {\n    std::bitset<BitNums> ret;\n    size_t bit_w_idx = 0;\n    FOR_FROM_BIGGEST(i, 0, BitNums) {\n      size_t limb_idx = i / kLimbBitNums;\n      size_t bit_r_idx = i % kLimbBitNums;\n      bool bit = (limbs[limb_idx] & (uint64_t{1} << bit_r_idx)) >> bit_r_idx;\n      ret.set(bit_w_idx++, bit);\n    }\n    return ret;\n  }\n\n  // Converts the BigInt to a byte array in little-endian order. This method\n  // processes the limbs of the BigInt, extracts individual bytes, and sets them\n  // in the resulting array.\n  std::array<uint8_t, kByteNums> ToBytesLE() const {\n    std::array<uint8_t, kByteNums> ret;\n    auto it = ret.begin();\n    FOR_FROM_SMALLEST(i, 0, kByteNums) {\n      size_t limb_idx = i / kLimbByteNums;\n      uint64_t limb = limbs[limb_idx];\n      size_t byte_r_idx = i % kLimbByteNums;\n      *(it++) = reinterpret_cast<uint8_t*>(&limb)[byte_r_idx];\n    }\n    return ret;\n  }\n\n  // Converts the BigInt to a byte array in big-endian order. This method\n  // processes the limbs of the BigInt, extracts individual bytes, and sets them\n  // in the resulting array.\n  std::array<uint8_t, kByteNums> ToBytesBE() const {\n    std::array<uint8_t, kByteNums> ret;\n    auto it = ret.begin();\n    FOR_FROM_BIGGEST(i, 0, kByteNums) {\n      size_t limb_idx = i / kLimbByteNums;\n      uint64_t limb = limbs[limb_idx];\n      size_t byte_r_idx = i % kLimbByteNums;\n      *(it++) = reinterpret_cast<uint8_t*>(&limb)[byte_r_idx];\n    }\n    return ret;\n  }\n\n  template <bool ModulusHasSpareBit>\n  [[nodiscard]] constexpr bool MontgomeryInverse(const BigInt& modulus,\n                                                 const BigInt& r2,\n                                                 BigInt& output) const {\n    if (UNLIKELY(IsZero())) {\n      LOG_IF_NOT_GPU(ERROR) << \"Inverse of zero attempted\";\n      return false;\n    }\n\n    // Guajardo Kumar Paar Pelzl\n    // Efficient Software-Implementation of Finite Fields with Applications to\n    // Cryptography\n    // Algorithm 16 (BEA for Inversion in Fp)\n\n    BigInt u = *this;\n    BigInt v = modulus;\n    BigInt b = r2;\n    BigInt c = BigInt::Zero();\n\n    while (!u.IsOne() && !v.IsOne()) {\n      while (u.IsEven()) {\n        u.DivBy2InPlace();\n\n        if (b.IsEven()) {\n          b.DivBy2InPlace();\n        } else {\n          uint64_t carry = 0;\n          b.AddInPlace(modulus, carry);\n          b.DivBy2InPlace();\n          if constexpr (!ModulusHasSpareBit) {\n            if (carry) {\n              b[N - 1] |= uint64_t{1} << 63;\n            }\n          }\n        }\n      }\n\n      while (v.IsEven()) {\n        v.DivBy2InPlace();\n\n        if (c.IsEven()) {\n          c.DivBy2InPlace();\n        } else {\n          uint64_t carry = 0;\n          c.AddInPlace(modulus, carry);\n          c.DivBy2InPlace();\n          if constexpr (!ModulusHasSpareBit) {\n            if (carry) {\n              c[N - 1] |= uint64_t{1} << 63;\n            }\n          }\n        }\n      }\n\n      if (v < u) {\n        u.SubInPlace(v);\n        if (b >= c) {\n          b -= c;\n        } else {\n          b += (modulus - c);\n        }\n      } else {\n        v.SubInPlace(u);\n        if (c >= b) {\n          c -= b;\n        } else {\n          c += (modulus - b);\n        }\n      }\n    }\n\n    if (u.IsOne()) {\n      output = b;\n    } else {\n      output = c;\n    }\n    return true;\n  }\n\n  // TODO(chokobole): This can be optimized since the element of vector occupies\n  // fixed 2 bits, we can save much space. e.g, in a worst case for 4 limbs(254\n  // bits), 254 * 2 / 8 = 63.4 < 8 * sizeof(uint64_t).\n  //\n  // This converts bigint to NAF(Non-Adjacent-Form).\n  // e.g, 7 = (1 1 1)₂ = (1 0 0 -1)₂\n  // See https://en.wikipedia.org/wiki/Non-adjacent_form\n  // See cyclotomic_multiplicative_subgroup.h for use case.\n  std::vector<int8_t> ToNAF() const {\n    BigInt v(*this);\n    std::vector<int8_t> ret;\n    ret.reserve(8 * sizeof(uint64_t) * N);\n    while (!v.IsZero()) {\n      int8_t z;\n      // v = v₀ * 2⁰ + v₁ * 2¹ + v₂ * 2² + ... + vₙ₋₁ * 2ⁿ⁻¹\n      // if v₀ == 0:\n      //   z = 0\n      //   v = z * 2⁰ + v₁ * 2¹ + v₂ * 2² + ... + vₙ₋₁ * 2ⁿ⁻¹\n      // else if v₀ == 1 && v₁ == 0:\n      //   z = 2 - 1 = 1\n      //   v = z * 2⁰ + v₂ * 2² + ... + vₙ₋₁ * 2ⁿ⁻¹\n      // else if v₀ == 1 && v₁ == 1:\n      //   z = 2 - 3 = -1\n      //   v = z * 2⁰ + (v₂ + 1) * 2² + ... + vₙ₋₁ * 2ⁿ⁻¹\n      if (v.IsOdd()) {\n        z = 2 - (v[kSmallestLimbIdx] % 4);\n        if (z >= 0) {\n          v -= BigInt(z);\n        } else {\n          v += BigInt(-z);\n        }\n      } else {\n        z = 0;\n      }\n      ret.push_back(z);\n      v.DivBy2InPlace();\n    }\n    return ret;\n  }\n\n private:\n  constexpr static void DoAdd(const BigInt& a, const BigInt& b, uint64_t& carry,\n                              BigInt& c) {\n    AddResult<uint64_t> result;\n\n#define ADD_WITH_CARRY_INLINE(num)                                        \\\n  do {                                                                    \\\n    if constexpr (N >= (num + 1)) {                                       \\\n      result = internal::u64::AddWithCarry(a[num], b[num], result.carry); \\\n      c[num] = result.result;                                             \\\n    }                                                                     \\\n  } while (false)\n\n#if ARCH_CPU_BIG_ENDIAN\n    ADD_WITH_CARRY_INLINE(N - 1);\n    ADD_WITH_CARRY_INLINE(N - 2);\n    ADD_WITH_CARRY_INLINE(N - 3);\n    ADD_WITH_CARRY_INLINE(N - 4);\n    ADD_WITH_CARRY_INLINE(N - 5);\n    ADD_WITH_CARRY_INLINE(N - 6);\n#else  // ARCH_CPU_LITTLE_ENDIAN\n    ADD_WITH_CARRY_INLINE(0);\n    ADD_WITH_CARRY_INLINE(1);\n    ADD_WITH_CARRY_INLINE(2);\n    ADD_WITH_CARRY_INLINE(3);\n    ADD_WITH_CARRY_INLINE(4);\n    ADD_WITH_CARRY_INLINE(5);\n#endif\n\n#undef ADD_WITH_CARRY_INLINE\n\n    FOR_FROM_SMALLEST(i, 6, N) {\n      result = internal::u64::AddWithCarry(a[i], b[i], result.carry);\n      c[i] = result.result;\n    }\n    carry = result.carry;\n  }\n\n  constexpr static void DoSub(const BigInt& a, const BigInt& b,\n                              uint64_t& borrow, BigInt& c) {\n    SubResult<uint64_t> result;\n\n#define SUB_WITH_BORROW_INLINE(num)                                         \\\n  do {                                                                      \\\n    if constexpr (N >= (num + 1)) {                                         \\\n      result = internal::u64::SubWithBorrow(a[num], b[num], result.borrow); \\\n      c[num] = result.result;                                               \\\n    }                                                                       \\\n  } while (false)\n\n#if ARCH_CPU_BIG_ENDIAN\n    SUB_WITH_BORROW_INLINE(N - 1);\n    SUB_WITH_BORROW_INLINE(N - 2);\n    SUB_WITH_BORROW_INLINE(N - 3);\n    SUB_WITH_BORROW_INLINE(N - 4);\n    SUB_WITH_BORROW_INLINE(N - 5);\n    SUB_WITH_BORROW_INLINE(N - 6);\n#else  // ARCH_CPU_LITTLE_ENDIAN\n    SUB_WITH_BORROW_INLINE(0);\n    SUB_WITH_BORROW_INLINE(1);\n    SUB_WITH_BORROW_INLINE(2);\n    SUB_WITH_BORROW_INLINE(3);\n    SUB_WITH_BORROW_INLINE(4);\n    SUB_WITH_BORROW_INLINE(5);\n#endif\n\n#undef SUB_WITH_BORROW_INLINE\n\n    FOR_FROM_SMALLEST(i, 6, N) {\n      result = internal::u64::SubWithBorrow(a[i], b[i], result.borrow);\n      c[i] = result.result;\n    }\n    borrow = result.borrow;\n  }\n\n  constexpr static void DoMulBy2(const BigInt& a, uint64_t& carry, BigInt& b) {\n    FOR_FROM_SMALLEST(i, 0, N) {\n      uint64_t temp = a[i] >> 63;\n      b[i] = a[i] << 1;\n      b[i] |= carry;\n      carry = temp;\n    }\n  }\n\n  constexpr static void DoAnd(const BigInt& a, const BigInt& b, BigInt& c) {\n    for (size_t i = 0; i < N; ++i) {\n      c[i] = a[i] & b[i];\n    }\n  }\n\n  constexpr static void DoOr(const BigInt& a, const BigInt& b, BigInt& c) {\n    for (size_t i = 0; i < N; ++i) {\n      c[i] = a[i] | b[i];\n    }\n  }\n\n  constexpr static void DoXor(const BigInt& a, const BigInt& b, BigInt& c) {\n    for (size_t i = 0; i < N; ++i) {\n      c[i] = a[i] ^ b[i];\n    }\n  }\n\n  template <typename T>\n  constexpr T ExtractBits(size_t bit_offset, size_t bit_count) const {\n    size_t nums = 0;\n    size_t bits = 0;\n    if constexpr (std::is_same_v<T, uint32_t>) {\n      nums = 2 * N;\n      bits = 32;\n    } else {\n      nums = N;\n      bits = 64;\n    }\n\n    const T* limbs_ptr = reinterpret_cast<const T*>(limbs);\n    size_t limb_idx = bit_offset / bits;\n    size_t bit_idx = bit_offset % bits;\n\n    T ret;\n    if (bit_idx < bits - bit_count || limb_idx == nums - 1) {\n      ret = limbs_ptr[limb_idx] >> bit_idx;\n    } else {\n      ret = (limbs_ptr[limb_idx] >> bit_idx) |\n            (limbs_ptr[1 + limb_idx] << (bits - bit_idx));\n    }\n    T mask = (T{1} << bit_count) - T{1};\n    return ret & mask;\n  }\n\n  // Montgomery arithmetic is a technique that allows modular arithmetic to be\n  // done more efficiently, by avoiding the need for explicit divisions.\n  // See https://en.wikipedia.org/wiki/Montgomery_modular_multiplication\n\n  // Converts a BigInt value from the Montgomery domain back to the standard\n  // domain. |FromMontgomery()| performs the Montgomery reduction algorithm to\n  // transform a value from the Montgomery domain back to its standard\n  // representation.\n  template <typename T>\n  constexpr static BigInt FromMontgomery(const BigInt<N>& value,\n                                         const BigInt<N>& modulus, T inverse) {\n    BigInt<N> r = value;\n    T* r_ptr = reinterpret_cast<T*>(r.limbs);\n    const T* m_ptr = reinterpret_cast<const T*>(modulus.limbs);\n    size_t num = 0;\n    MulResult<T> (*mul_add_with_carry)(T, T, T, T);\n    if constexpr (std::is_same_v<T, uint32_t>) {\n      num = 2 * N;\n      mul_add_with_carry = internal::u32::MulAddWithCarry;\n    } else {\n      num = N;\n      mul_add_with_carry = internal::u64::MulAddWithCarry;\n    }\n    // Montgomery Reduction\n    FOR_FROM_SMALLEST(i, 0, num) {\n      T k = r_ptr[i] * inverse;\n      MulResult<T> result = mul_add_with_carry(r_ptr[i], k, m_ptr[0], 0);\n      FOR_FROM_SECOND_SMALLEST(j, 0, num) {\n        result =\n            mul_add_with_carry(r_ptr[(j + i) % num], k, m_ptr[j], result.hi);\n        r_ptr[(j + i) % num] = result.lo;\n      }\n      r_ptr[i] = result.hi;\n    }\n    return r;\n  }\n\n  // Performs Montgomery reduction on a doubled-sized BigInt, and populates\n  // |out| with the result.\n\n  // Inputs:\n  // - r: A BigInt representing a value (typically A x B) in Montgomery form.\n  // - modulus: The modulus M against which we're performing arithmetic.\n  // - inverse: The multiplicative inverse of the radix w.r.t. the modulus.\n\n  // Operation:\n  // 1. For each limb of r:\n  //    - Compute a tmp = r(current limb) * inverse.\n  //      This value aids in eliminating the lowest limb of r when multiplied by\n  //      the modulus.\n  //    - Incrementally add tmp * (modulus to r), effectively canceling out its\n  //      current lowest limb.\n  //\n  // 2. After iterating over all limbs, the higher half of r is the\n  //    Montgomery-reduced result of the original operation (like A x B). This\n  //    result remains in the Montgomery domain.\n  //\n  // 3. Apply a final correction (if necessary) to ensure the result is less\n  //    than |modulus|.\n  template <bool ModulusHasSpareBit, typename T>\n  constexpr static void MontgomeryReduce(BigInt<2 * N>& r,\n                                         const BigInt& modulus, T inverse,\n                                         BigInt* out) {\n    T* r_ptr = reinterpret_cast<T*>(r.limbs);\n    const T* m_ptr = reinterpret_cast<const T*>(modulus.limbs);\n    size_t num = 0;\n    MulResult<T> (*mul_add_with_carry)(T, T, T, T);\n    AddResult<T> (*add_with_carry)(T, T, T);\n    if constexpr (std::is_same_v<T, uint32_t>) {\n      num = 2 * N;\n      mul_add_with_carry = internal::u32::MulAddWithCarry;\n      add_with_carry = internal::u32::AddWithCarry;\n    } else {\n      num = N;\n      mul_add_with_carry = internal::u64::MulAddWithCarry;\n      add_with_carry = internal::u64::AddWithCarry;\n    }\n    AddResult<T> add_result;\n    FOR_FROM_SMALLEST(i, 0, num) {\n      T tmp = r_ptr[i] * inverse;\n      MulResult<T> mul_result;\n      mul_result = mul_add_with_carry(r_ptr[i], tmp, m_ptr[0], mul_result.hi);\n      FOR_FROM_SECOND_SMALLEST(j, 0, num) {\n        mul_result =\n            mul_add_with_carry(r_ptr[i + j], tmp, m_ptr[j], mul_result.hi);\n        r_ptr[i + j] = mul_result.lo;\n      }\n      add_result =\n          add_with_carry(r_ptr[num + i], mul_result.hi, add_result.carry);\n      r_ptr[num + i] = add_result.result;\n    }\n    memcpy(&(*out)[0], &r[N], sizeof(uint64_t) * N);\n    Clamp<ModulusHasSpareBit>(modulus, out, add_result.carry);\n  }\n};\n\ntemplate <size_t N>\nclass BitTraits<BigInt<N>> {\n public:\n  constexpr static bool kIsDynamic = false;\n\n  constexpr static size_t GetNumBits(const BigInt<N>& _) { return N * 64; }\n\n  constexpr static bool TestBit(const BigInt<N>& bigint, size_t index) {\n    size_t limb_index = index >> 6;\n    if (limb_index >= N) return false;\n    size_t bit_index = index & 63;\n    uint64_t bit_index_value = uint64_t{1} << bit_index;\n    return (bigint[limb_index] & bit_index_value) == bit_index_value;\n  }\n  constexpr static void SetBit(BigInt<N>& bigint, size_t index,\n                               bool bit_value) {\n    size_t limb_index = index >> 6;\n    if (limb_index >= N) return;\n    size_t bit_index = index & 63;\n    uint64_t bit_index_value = uint64_t{1} << bit_index;\n    if (bit_value) {\n      bigint[limb_index] |= bit_index_value;\n    } else {\n      bigint[limb_index] &= ~bit_index_value;\n    }\n  }\n};\n\n}  // namespace math\n\nnamespace base {\n\ntemplate <size_t N>\nclass Copyable<math::BigInt<N>> {\n public:\n  static bool WriteTo(const math::BigInt<N>& bigint, Buffer* buffer) {\n    return buffer->Write(bigint.limbs);\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer, math::BigInt<N>* bigint) {\n    return buffer.Read(bigint->limbs);\n  }\n\n  static size_t EstimateSize(const math::BigInt<N>& bigint) {\n    return base::EstimateSize(bigint.limbs);\n  }\n};\n\ntemplate <size_t N>\nclass RapidJsonValueConverter<math::BigInt<N>> {\n public:\n  // NOTE(dongchangYoo): Classes inheriting |BigInt<N>| have a member of\n  // type |uint64_t[N]|, but for the sake of readability in the JSON file,\n  // |BigInt<N>| is implemented to be converted into a hexadecimal string.\n  template <typename Allocator>\n  static rapidjson::Value From(const math::BigInt<N>& value,\n                               Allocator& allocator) {\n    std::string value_str = value.ToHexString();\n    return RapidJsonValueConverter<std::string>::From(value_str, allocator);\n  }\n\n  static bool To(const rapidjson::Value& json_value, std::string_view key,\n                 math::BigInt<N>* value, std::string* error) {\n    if (!json_value.IsString()) {\n      *error = RapidJsonMismatchedTypeError(key, \"string\", json_value);\n      return false;\n    }\n    std::string_view value_str;\n    if (!RapidJsonValueConverter<std::string_view>::To(json_value, \"\",\n                                                       &value_str, error))\n      return false;\n    std::optional<math::BigInt<N>> tmp =\n        math::BigInt<N>::FromHexString(value_str);\n    if (!tmp.has_value()) return false;\n    *value = std::move(tmp).value();\n    return true;\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_MATH_BASE_BIG_INT_H_\n"
  },
  {
    "path": "tachyon/math/base/big_int_unittest.cc",
    "content": "#include \"tachyon/math/base/big_int.h\"\n\n#include <algorithm>\n#include <vector>\n\n#include \"absl/container/inlined_vector.h\"\n#include \"absl/types/span.h\"\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/buffer/vector_buffer.h\"\n\nnamespace tachyon::math {\n\nTEST(BigIntTest, Zero) {\n  BigInt<2> big_int = BigInt<2>::Zero();\n  EXPECT_TRUE(big_int.IsZero());\n  EXPECT_FALSE(big_int.IsOne());\n  EXPECT_TRUE(big_int.IsEven());\n  EXPECT_FALSE(big_int.IsOdd());\n}\n\nTEST(BigIntTest, One) {\n  BigInt<2> big_int = BigInt<2>::One();\n  EXPECT_FALSE(big_int.IsZero());\n  EXPECT_TRUE(big_int.IsOne());\n  EXPECT_FALSE(big_int.IsEven());\n  EXPECT_TRUE(big_int.IsOdd());\n}\n\nTEST(BigIntTest, DecString) {\n  // 1 << 65\n  BigInt<2> big_int = *BigInt<2>::FromDecString(\"36893488147419103232\");\n  EXPECT_EQ(big_int.ToString(), \"36893488147419103232\");\n\n  EXPECT_FALSE(BigInt<2>::FromDecString(\"x\").has_value());\n}\n\nTEST(BigIntTest, HexString) {\n  {\n    // 1 << 65\n    BigInt<2> big_int = *BigInt<2>::FromHexString(\"20000000000000000\");\n    EXPECT_EQ(big_int.ToHexString(), \"0x20000000000000000\");\n  }\n  {\n    // 1 << 65\n    BigInt<2> big_int = *BigInt<2>::FromHexString(\"0x20000000000000000\");\n    EXPECT_EQ(big_int.ToHexString(), \"0x20000000000000000\");\n  }\n\n  EXPECT_FALSE(BigInt<2>::FromHexString(\"x\").has_value());\n}\n\nTEST(BigIntTest, BitsLEConversion) {\n  std::bitset<255> input(\n      \"011101111110011110110101010100110010011011110111011101000111010111110011\"\n      \"000100011000011100111011011100111101100101100111001101011010000011111110\"\n      \"000010011110011110001011111101111001100001100000111010000101111101010010\"\n      \"101011110101110101011101011001100110000\");\n  BigInt<4> big_int = BigInt<4>::FromBitsLE(input);\n  ASSERT_EQ(big_int, *BigInt<4>::FromDecString(\n                         \"27117311055620256798560880810000042840428\"\n                         \"971800021819916023577129547249660720\"));\n  EXPECT_EQ(big_int.ToBitsLE<255>(), input);\n}\n\nTEST(BigIntTest, BitsBEConversion) {\n  std::bitset<255> input(\n      \"000011001100110101110101011101011110101010010101111101000010111000001100\"\n      \"001100111101111110100011110011110010000011111110000010110101100111001101\"\n      \"001101111001110110111001110000110001000110011111010111000101110111011110\"\n      \"1100100110010101010110111100111111011100110000\");\n  BigInt<4> big_int = BigInt<4>::FromBitsBE(input);\n  ASSERT_EQ(big_int, *BigInt<4>::FromDecString(\n                         \"27117311055620256798560880810000042840428\"\n                         \"971800021819916023577129547249660720\"));\n  EXPECT_EQ(big_int.ToBitsBE<255>(), input);\n}\n\nnamespace {\n\ntemplate <typename Container>\nclass BigIntConversionTest : public testing::Test {\n public:\n  static void SetUpTestSuite() {\n    expected_ = *BigInt<4>::FromDecString(\n        \"271173110556202567985608808100000428404289718000\"\n        \"21819916023577129547249660720\");\n  }\n\n protected:\n  static BigInt<4> expected_;\n\n  static constexpr uint8_t kInputLE[32] = {\n      48,  179, 174, 174, 87,  169, 47,  116, 48,  204, 251,\n      197, 243, 4,   127, 208, 154, 179, 236, 185, 157, 195,\n      136, 249, 58,  186, 123, 147, 169, 218, 243, 59};\n\n  static constexpr uint8_t kInputBE[32] = {\n      59,  243, 218, 169, 147, 123, 186, 58,  249, 136, 195,\n      157, 185, 236, 179, 154, 208, 127, 4,   243, 197, 251,\n      204, 48,  116, 47,  169, 87,  174, 174, 179, 48};\n};\n\ntemplate <typename Container>\nmath::BigInt<4> BigIntConversionTest<Container>::expected_;\ntemplate <typename Container>\nconstexpr uint8_t BigIntConversionTest<Container>::kInputLE[32];\ntemplate <typename Container>\nconstexpr uint8_t BigIntConversionTest<Container>::kInputBE[32];\n\n}  // namespace\n\nusing ContainerTypes =\n    testing::Types<std::vector<uint8_t>, std::array<uint8_t, 32>,\n                   absl::InlinedVector<uint8_t, 32>, absl::Span<const uint8_t>>;\nTYPED_TEST_SUITE(BigIntConversionTest, ContainerTypes);\n\nTYPED_TEST(BigIntConversionTest, BytesLEConversion) {\n  using Container = TypeParam;\n\n  Container expected_input;\n\n  if constexpr (std::is_same_v<Container, std::vector<uint8_t>> ||\n                std::is_same_v<Container, absl::InlinedVector<uint8_t, 32>>) {\n    expected_input =\n        Container(std::begin(this->kInputLE), std::end(this->kInputLE));\n  } else if constexpr (std::is_same_v<Container, std::array<uint8_t, 32>>) {\n    std::copy(std::begin(this->kInputLE), std::end(this->kInputLE),\n              expected_input.begin());\n  } else if constexpr (std::is_same_v<Container, absl::Span<const uint8_t>>) {\n    expected_input = Container(this->kInputLE, sizeof(this->kInputLE));\n  }\n\n  BigInt<4> actual = BigInt<4>::FromBytesLE(expected_input);\n  ASSERT_EQ(actual, this->expected_);\n\n  std::array<uint8_t, 32> actual_input = actual.ToBytesLE();\n  EXPECT_TRUE(std::equal(actual_input.begin(), actual_input.end(),\n                         expected_input.begin()));\n}\n\nTYPED_TEST(BigIntConversionTest, BytesBEConversion) {\n  using Container = TypeParam;\n\n  Container expected_input;\n\n  if constexpr (std::is_same_v<Container, std::vector<uint8_t>> ||\n                std::is_same_v<Container, absl::InlinedVector<uint8_t, 32>>) {\n    expected_input =\n        Container(std::begin(this->kInputBE), std::end(this->kInputBE));\n  } else if constexpr (std::is_same_v<Container, std::array<uint8_t, 32>>) {\n    std::copy(std::begin(this->kInputBE), std::end(this->kInputBE),\n              expected_input.begin());\n  } else if constexpr (std::is_same_v<Container, absl::Span<const uint8_t>>) {\n    expected_input = Container(this->kInputBE, sizeof(this->kInputBE));\n  }\n\n  BigInt<4> actual = BigInt<4>::FromBytesBE(expected_input);\n  ASSERT_EQ(actual, this->expected_);\n\n  std::array<uint8_t, 32> actual_input = actual.ToBytesBE();\n  EXPECT_TRUE(std::equal(actual_input.begin(), actual_input.end(),\n                         expected_input.begin()));\n}\n\nTEST(BigIntTest, Comparison) {\n  // 1 << 65\n  BigInt<2> big_int = *BigInt<2>::FromHexString(\"20000000000000000\");\n  BigInt<2> big_int2 = *BigInt<2>::FromHexString(\"20000000000000001\");\n  EXPECT_TRUE(big_int == big_int);\n  EXPECT_TRUE(big_int != big_int2);\n  EXPECT_TRUE(big_int < big_int2);\n  EXPECT_TRUE(big_int <= big_int2);\n  EXPECT_TRUE(big_int2 > big_int);\n  EXPECT_TRUE(big_int2 >= big_int);\n}\n\nTEST(BigIntTest, ExtractBits) {\n  BigInt<2> big_int = BigInt<2>::Random();\n  size_t bit_count = 4;\n  for (size_t offset = 0; offset < 16; offset += bit_count) {\n    uint64_t value = 0;\n    for (size_t i = 0; i < bit_count; ++i) {\n      if (BitTraits<BigInt<2>>::TestBit(big_int, offset + i)) {\n        value |= UINT64_C(1) << i;\n      }\n    }\n    EXPECT_EQ(big_int.ExtractBits64(offset, bit_count), value);\n    EXPECT_EQ(big_int.ExtractBits32(offset, bit_count), value);\n  }\n}\n\nTEST(BigIntTest, Operations) {\n  BigInt<2> big_int =\n      *BigInt<2>::FromDecString(\"123456789012345678909876543211235312\");\n  BigInt<2> big_int2 =\n      *BigInt<2>::FromDecString(\"734581237591230158128731489729873983\");\n  {\n    uint64_t carry = 0;\n    BigInt<2> a = big_int;\n    BigInt<2> sum =\n        *BigInt<2>::FromDecString(\"858038026603575837038608032941109295\");\n    BigInt<2> b = big_int2;\n    EXPECT_EQ(a.AddInPlace(big_int2, carry), sum);\n    EXPECT_EQ(carry, 0);\n    EXPECT_EQ(b.AddInPlace(big_int, carry), sum);\n    EXPECT_EQ(carry, 0);\n  }\n  {\n    uint64_t borrow = 0;\n    BigInt<2> a = big_int;\n    BigInt<2> amb =\n        *BigInt<2>::FromDecString(\"339671242472359578984155752485249572785\");\n    EXPECT_EQ(a.SubInPlace(big_int2, borrow), amb);\n    EXPECT_EQ(borrow, 1);\n    BigInt<2> b = big_int2;\n    BigInt<2> bma =\n        *BigInt<2>::FromDecString(\"611124448578884479218854946518638671\");\n    EXPECT_EQ(b.SubInPlace(big_int, borrow), bma);\n    EXPECT_EQ(borrow, 0);\n  }\n  {\n    uint64_t carry = 0;\n    BigInt<2> a = big_int;\n    BigInt<2> mulby2 =\n        *BigInt<2>::FromDecString(\"246913578024691357819753086422470624\");\n    EXPECT_EQ(a.MulBy2InPlace(carry), mulby2);\n    EXPECT_EQ(carry, 0);\n  }\n  {\n    uint64_t carry = 0;\n    BigInt<2> a = big_int;\n    BigInt<2> mulbyn =\n        *BigInt<2>::FromDecString(\"3950617248395061725116049382759529984\");\n    EXPECT_EQ(a.MulBy2ExpInPlace(5), mulbyn);\n    EXPECT_EQ(carry, 0);\n  }\n  {\n    BigInt<2> a = big_int;\n    BigInt<2> b = big_int2;\n    MulResult<BigInt<2>> ret = a.Multiply(b);\n    EXPECT_EQ(ret.lo, *BigInt<2>::FromDecString(\n                          \"335394729415762779748307316131549975568\"));\n    EXPECT_EQ(ret.hi,\n              *BigInt<2>::FromDecString(\"266511138036132956757991041665338\"));\n  }\n  {\n    BigInt<2> a = big_int;\n    BigInt<2> divby2 =\n        *BigInt<2>::FromDecString(\"61728394506172839454938271605617656\");\n    EXPECT_EQ(a.DivBy2InPlace(), divby2);\n  }\n  {\n    BigInt<2> a = big_int;\n    BigInt<2> divbyn =\n        *BigInt<2>::FromDecString(\"3858024656635802465933641975351103\");\n    EXPECT_EQ(a.DivBy2ExpInPlace(5), divbyn);\n  }\n  {\n    BigInt<2> a = big_int;\n    BigInt<2> b = big_int2;\n    DivResult<BigInt<2>> adb = {\n        BigInt<2>(),\n        a,\n    };\n    DivResult<BigInt<2>> div1;\n    ASSERT_TRUE(a.Divide(b, div1));\n    EXPECT_EQ(div1, adb);\n    DivResult<BigInt<2>> bda = {\n        BigInt<2>(5),\n        *BigInt<2>::FromDecString(\"117297292529501763579348773673697423\"),\n    };\n    DivResult<BigInt<2>> div2;\n    ASSERT_TRUE(b.Divide(a, div2));\n    EXPECT_EQ(div2, bda);\n  }\n}\n\nTEST(BigIntTest, Copyable) {\n  BigInt<2> expected = BigInt<2>::Random();\n\n  base::Uint8VectorBuffer write_buf;\n  ASSERT_TRUE(write_buf.Grow(base::EstimateSize(expected)));\n  ASSERT_TRUE(write_buf.Write(expected));\n  ASSERT_TRUE(write_buf.Done());\n\n  write_buf.set_buffer_offset(0);\n\n  BigInt<2> value;\n  ASSERT_TRUE(write_buf.Read(&value));\n  EXPECT_EQ(value, expected);\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/base/bit_iterator.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_BASE_BIT_ITERATOR_H_\n#define TACHYON_MATH_BASE_BIT_ITERATOR_H_\n\n#include <iterator>\n#include <limits>\n\n#include \"tachyon/build/build_config.h\"\n#include \"tachyon/math/base/bit_traits_forward.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename T>\nclass BitIteratorBE {\n public:\n  using difference_type = std::ptrdiff_t;\n  using value_type = bool;\n  using pointer = bool;\n  using reference = bool;\n  using iterator_category = std::forward_iterator_tag;\n\n  constexpr explicit BitIteratorBE(const T* value) : BitIteratorBE(value, 0) {}\n  constexpr BitIteratorBE(const T* value, size_t index)\n      : value_(value), index_(index) {}\n  constexpr BitIteratorBE(const BitIteratorBE& other) = default;\n  constexpr BitIteratorBE& operator=(const BitIteratorBE& other) = default;\n\n  constexpr static BitIteratorBE begin(const T* value,\n                                       bool skip_leading_zeros = false) {\n#if ARCH_CPU_BIG_ENDIAN\n    BitIteratorBE ret(value, 0);\n#else  // ARCH_CPU_LITTLE_ENDIAN\n    size_t bits = BitTraits<T>::GetNumBits(*value);\n    if constexpr (BitTraits<T>::kIsDynamic) {\n      if (bits == 0) {\n        return end(value);\n      }\n    }\n    BitIteratorBE ret(value, bits - 1);\n#endif\n    if (skip_leading_zeros) {\n      while (!(*ret)) {\n        ++ret;\n#if ARCH_CPU_BIG_ENDIAN\n        if (ret.index_ == bits) break;\n#else  // ARCH_CPU_LITTLE_ENDIAN\n        if (ret.index_ == std::numeric_limits<size_t>::max()) break;\n#endif\n      }\n    }\n    return ret;\n  }\n\n  constexpr static BitIteratorBE end(const T* value) {\n#if ARCH_CPU_BIG_ENDIAN\n    size_t bits = BitTraits<T>::GetNumBits(*value);\n    if (bits == 0) {\n      return begin(value);\n    }\n    return BitIteratorBE(value, bits);\n#else  // ARCH_CPU_LITTLE_ENDIAN\n    return BitIteratorBE(value, std::numeric_limits<size_t>::max());\n#endif\n  }\n\n  constexpr bool operator==(const BitIteratorBE& other) const {\n    return value_ == other.value_ && index_ == other.index_;\n  }\n  constexpr bool operator!=(const BitIteratorBE& other) const {\n    return !(*this == other);\n  }\n\n  constexpr BitIteratorBE& operator++() {\n#if ARCH_CPU_BIG_ENDIAN\n    ++index_;\n#else  // ARCH_CPU_LITTLE_ENDIAN\n    --index_;\n#endif\n    return *this;\n  }\n\n  constexpr BitIteratorBE operator++(int) {\n    BitIteratorBE it(*this);\n    ++(*this);\n    return it;\n  }\n\n  constexpr bool operator->() const {\n    return BitTraits<T>::TestBit(*value_, index_);\n  }\n\n  constexpr bool operator*() const {\n    return BitTraits<T>::TestBit(*value_, index_);\n  }\n\n private:\n  const T* value_ = nullptr;\n  size_t index_ = 0;\n};\n\ntemplate <typename T>\nclass BitIteratorLE {\n public:\n  using difference_type = std::ptrdiff_t;\n  using value_type = bool;\n  using pointer = bool;\n  using reference = bool;\n  using iterator_category = std::forward_iterator_tag;\n\n  constexpr explicit BitIteratorLE(const T* value) : BitIteratorLE(value, 0) {}\n  constexpr BitIteratorLE(const T* value, size_t index)\n      : value_(value), index_(index) {}\n  constexpr BitIteratorLE(const BitIteratorLE& other) = default;\n  constexpr BitIteratorLE& operator=(const BitIteratorLE& other) = default;\n\n  constexpr static BitIteratorLE begin(const T* value) {\n#if ARCH_CPU_LITTLE_ENDIAN\n    return BitIteratorLE(value, 0);\n#else  // ARCH_CPU_BIG_ENDIAN\n    size_t bits = BitTraits<T>::GetNumBits(*value);\n    if constexpr (BitTraits<T>::kIsDynamic) {\n      if (bits == 0) {\n        return end(value);\n      }\n    }\n    return BitIteratorLE(value, bits - 1);\n#endif\n  }\n\n  constexpr static BitIteratorLE end(const T* value,\n                                     bool skip_trailing_zeros = false) {\n#if ARCH_CPU_LITTLE_ENDIAN\n    size_t bits = BitTraits<T>::GetNumBits(*value);\n    if (bits == 0) {\n      return begin(value);\n    }\n    BitIteratorLE ret(value, bits);\n#else  // ARCH_CPU_BIG_ENDIAN\n    BitIteratorLE ret(value, std::numeric_limits<size_t>::max());\n#endif\n    if (!skip_trailing_zeros) return ret;\n    while (!(*ret)) {\n      --ret;\n#if ARCH_CPU_LITTLE_ENDIAN\n      if (ret.index_ == std::numeric_limits<size_t>::max()) break;\n#else  // ARCH_CPU_BIG_ENDIAN\n      if (ret.index_ == bits) break;\n#endif\n    }\n    return ++ret;\n  }\n\n  bool operator==(const BitIteratorLE& other) const {\n    return value_ == other.value_ && index_ == other.index_;\n  }\n  bool operator!=(const BitIteratorLE& other) const {\n    return !(*this == other);\n  }\n\n  constexpr BitIteratorLE& operator++() {\n#if ARCH_CPU_LITTLE_ENDIAN\n    ++index_;\n#else  // ARCH_CPU_BIG_ENDIAN\n    --index_;\n#endif\n    return *this;\n  }\n\n  constexpr BitIteratorLE operator++(int) {\n    BitIteratorLE it(*this);\n    ++(*this);\n    return it;\n  }\n\n  constexpr bool operator->() const {\n    return BitTraits<T>::TestBit(*value_, index_);\n  }\n\n  constexpr bool operator*() const {\n    return BitTraits<T>::TestBit(*value_, index_);\n  }\n\n private:\n  constexpr BitIteratorLE& operator--() {\n#if ARCH_CPU_LITTLE_ENDIAN\n    --index_;\n#else  // ARCH_CPU_BIG_ENDIAN\n    ++index_;\n#endif\n    return *this;\n  }\n\n  constexpr BitIteratorLE operator--(int) {\n    BitIteratorLE it(*this);\n    --(*this);\n    return it;\n  }\n\n  const T* value_ = nullptr;\n  size_t index_ = 0;\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_BASE_BIT_ITERATOR_H_\n"
  },
  {
    "path": "tachyon/math/base/bit_iterator_unittest.cc",
    "content": "#include \"tachyon/math/base/bit_iterator.h\"\n\n#include <vector>\n\n#include \"absl/strings/substitute.h\"\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/base/big_int.h\"\n\nnamespace tachyon::math {\n\nTEST(GmpUtilTest, BitIteratorLE) {\n  struct {\n    BigInt<1> value;\n    std::vector<bool> answers;\n  } tests[] = {\n      {BigInt<1>(0), std::vector<bool>{}},\n      {BigInt<1>(1), std::vector<bool>{1}},\n      {BigInt<1>(2), std::vector<bool>{0, 1}},\n      {BigInt<1>(3), std::vector<bool>{1, 1}},\n      {BigInt<1>(4), std::vector<bool>{0, 0, 1}},\n      {BigInt<1>(5), std::vector<bool>{1, 0, 1}},\n  };\n\n  for (int i = 0; i < 2; ++i) {\n    bool skip_trailing_zeros = i == 0;\n    SCOPED_TRACE(\n        absl::Substitute(\"skip_trailing_zeros: $0\", skip_trailing_zeros));\n    for (const auto& test : tests) {\n      std::vector<bool> bits;\n      auto it = BitIteratorLE<BigInt<1>>::begin(&test.value);\n      auto end =\n          BitIteratorLE<BigInt<1>>::end(&test.value, skip_trailing_zeros);\n      while (it != end) {\n        bits.push_back(*it);\n        ++it;\n      }\n\n      if (skip_trailing_zeros) {\n        EXPECT_THAT(bits, testing::ContainerEq(test.answers));\n      } else {\n        std::vector<bool> answers = test.answers;\n        answers.reserve(64);\n        std::fill_n(std::back_inserter(answers), 64 - test.answers.size(),\n                    false);\n        EXPECT_THAT(bits, testing::ContainerEq(answers));\n      }\n    }\n  }\n}\n\nTEST(GmpUtilTest, BitIteratorBE) {\n  struct {\n    BigInt<1> value;\n    std::vector<bool> answers;\n  } tests[] = {\n      {BigInt<1>(0), std::vector<bool>{}},\n      {BigInt<1>(1), std::vector<bool>{1}},\n      {BigInt<1>(2), std::vector<bool>{1, 0}},\n      {BigInt<1>(3), std::vector<bool>{1, 1}},\n      {BigInt<1>(4), std::vector<bool>{1, 0, 0}},\n      {BigInt<1>(5), std::vector<bool>{1, 0, 1}},\n  };\n\n  for (int i = 0; i < 2; ++i) {\n    bool skip_leading_zeros = i == 0;\n    SCOPED_TRACE(\n        absl::Substitute(\"skip_leading_zeros: $0\", skip_leading_zeros));\n    for (const auto& test : tests) {\n      std::vector<bool> bits;\n      auto it =\n          BitIteratorBE<BigInt<1>>::begin(&test.value, skip_leading_zeros);\n      auto end = BitIteratorBE<BigInt<1>>::end(&test.value);\n      while (it != end) {\n        bits.push_back(*it);\n        ++it;\n      }\n\n      if (skip_leading_zeros) {\n        EXPECT_THAT(bits, testing::ContainerEq(test.answers));\n      } else {\n        std::vector<bool> answers;\n        answers.reserve(64);\n        std::fill_n(std::back_inserter(answers), 64 - test.answers.size(),\n                    false);\n        answers.insert(answers.end(), test.answers.begin(), test.answers.end());\n        EXPECT_THAT(bits, testing::ContainerEq(answers));\n      }\n    }\n  }\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/base/bit_traits_forward.h",
    "content": "#ifndef TACHYON_MATH_BASE_BIT_TRAITS_FORWARD_H_\n#define TACHYON_MATH_BASE_BIT_TRAITS_FORWARD_H_\n\nnamespace tachyon::math {\n\ntemplate <typename T, typename SFINAE = void>\nclass BitTraits;\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_BASE_BIT_TRAITS_FORWARD_H_\n"
  },
  {
    "path": "tachyon/math/base/byinverter.h",
    "content": "#ifndef TACHYON_MATH_BASE_BYINVERTER_H_\n#define TACHYON_MATH_BASE_BYINVERTER_H_\n\n#include <stdint.h>\n#include <stdlib.h>\n\n#include <algorithm>\n\n#include \"absl/numeric/int128.h\"\n\n#include \"tachyon/base/bits.h\"\n#include \"tachyon/math/base/big_int.h\"\n\nnamespace tachyon::math {\n\n// Unsigned 128-bit integer type\ntypedef absl::uint128 uint128_t;\n\n// Big signed (B * L)-bit integer type, whose variables\n// store numbers in the two's complement code as arrays\n// of B-bit chunks, where B < 64 and L > 0. The ordering\n// of the chunks in these arrays is little-endian. The\n// arithmetic operations for this type are wrapping ones\ntemplate <size_t B, size_t L>\nclass CInt {\n public:\n  // Mask, in which the B lowest bits are 1 and only they\n  constexpr static uint64_t MASK = ~uint64_t{0} >> (64 - B);\n\n  // Returns the number equal to the argument. The binary\n  // representation of the absolute value of the argument\n  // must have no more than B * L - 1 bits to ensure that\n  // the result and the argument represent the same number\n  constexpr static CInt Load(const int64_t input) {\n    CInt result;\n    uint64_t raw = input;\n    Convert<64, B>(&raw, 1, result.data, L);\n    if ((B * L > 64) && (input < 0)) {\n      // For the two's complement code and n > m the n-bit representation\n      // of a negative number can be expressed as its m-bit representation\n      // preceded by m - n bits equal to 1\n      result.data[64 / B] |= (~uint64_t{0} << (64 % B)) & MASK;\n      for (size_t i = 64 / B + 1; i < L; i++) {\n        result.data[i] = MASK;\n      }\n    }\n    return result;\n  }\n\n  // Returns the number, the absolute value of which is specified by the\n  // first argument. A non-zero return number is negative iff \"sign\" is\n  // \"true\". The binary representation of the absolute value of the input\n  // number must have no more than B * L - 1 bits to ensure that the result\n  // and arguments specify the same number\n  template <size_t N>\n  constexpr static CInt Load(const BigInt<N> &input, bool sign) {\n    CInt result;\n    Convert<64, B>(input.limbs, N, result.data, L);\n    return sign ? -result : result;\n  }\n\n  // Returns the current number. The binary representation\n  // of its absolute value must have no more than 63 bits\n  // to ensure that the result represents the current number\n  constexpr int64_t Save() const {\n    uint64_t result = 0;\n    Convert<B, 64>(data, L, &result, 1);\n    if ((B * L < 64) && IsNegative()) {\n      // For the two's complement code and n > m the n-bit representation\n      // of a negative number can be expressed as its m-bit representation\n      // preceded by m - n bits equal to 1\n      result |= ~uint64_t{0} << (B * L);\n    }\n    return result;\n  }\n\n  // Returns the sign of the current number and stores its absolute\n  // value into the variable, which is specified by the argument.\n  // The binary representation of the absolute value of the current\n  // number must contain no more than 64 * N bits to ensure that the\n  // result represents the current number\n  template <size_t N>\n  constexpr bool Save(BigInt<N> &output) const {\n    bool sign = IsNegative();\n    Convert<B, 64>((sign ? -*this : *this).data, L, output.limbs, N);\n    return sign;\n  }\n\n  // Returns the lowest B bits of the current number\n  constexpr uint64_t Lowest() const { return data[0]; }\n\n  // Returns \"true\" iff the current number is negative\n  constexpr bool IsNegative() const { return data[L - 1] > (MASK >> 1); }\n\n  // Returns the result of applying B-bit right\n  // arithmetical shift to the current number\n  constexpr CInt Shift() const {\n    CInt result;\n    if (IsNegative()) {\n      result.data[L - 1] = MASK;\n    }\n    for (size_t i = 1; i < L; i++) {\n      result.data[i - 1] = data[i];\n    }\n    return result;\n  }\n\n  constexpr bool operator==(const CInt &other) const {\n    for (size_t i = 0; i < L; i++) {\n      if (data[i] != other.data[i]) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  constexpr bool operator!=(const CInt &other) const {\n    return !(*this == other);\n  }\n\n  constexpr CInt operator+(const CInt &other) const {\n    CInt result;\n    uint64_t carry = 0;\n    for (size_t i = 0; i < L; i++) {\n      uint64_t sum = data[i] + other.data[i] + carry;\n      result.data[i] = sum & MASK;\n      carry = sum >> B;\n    }\n    return result;\n  }\n\n  constexpr CInt operator-(const CInt &other) const {\n    // For the two's complement code the additive negation is the result of\n    // adding 1 to the bitwise inverted argument's representation. Thus, for\n    // any encoded integers x and y we have x - y = x + !y + 1, where \"!\" is\n    // the bitwise inversion and addition is done according to the rules of\n    // the code. The algorithm below uses this formula and is the modified\n    // addition algorithm, where the carry flag is initialized with 1 and\n    // the chunks of the second argument are bitwise inverted\n    CInt result;\n    uint64_t carry = 1;\n    for (size_t i = 0; i < L; i++) {\n      uint64_t sum = data[i] + (other.data[i] ^ MASK) + carry;\n      result.data[i] = sum & MASK;\n      carry = sum >> B;\n    }\n    return result;\n  }\n\n  constexpr CInt operator-() const {\n    // For the two's complement code the additive negation is the result\n    // of adding 1 to the bitwise inverted argument's representation\n    CInt result;\n    uint64_t carry = 1;\n    for (size_t i = 0; i < L; i++) {\n      uint64_t sum = (data[i] ^ MASK) + carry;\n      result.data[i] = sum & MASK;\n      carry = sum >> B;\n    }\n    return result;\n  }\n\n  constexpr CInt operator*(const CInt &other) const {\n    CInt result;\n    for (size_t i = 0; i < L; i++) {\n      uint64_t carry = 0;\n      for (size_t k = i; k < L; k++) {\n        uint128_t sum = static_cast<uint128_t>(data[i]) * other.data[k - i] +\n                        result.data[k] + carry;\n        result.data[k] = static_cast<uint64_t>(sum & MASK);\n        carry = static_cast<uint64_t>(sum >> B);\n      }\n    }\n    return result;\n  }\n\n  constexpr CInt operator*(int64_t other) const {\n    CInt result;\n    uint64_t mask = 0;\n    uint64_t carry = 0;\n    // If the short multiplicand is non-negative, the standard multiplication\n    // algorithm is performed. Otherwise, the product of the additively negated\n    // multiplicands is found as follows. Since for the two's complement code\n    // the additive negation is the result of adding 1 to the bitwise inverted\n    // argument's representation, for any encoded integers x and y we have\n    // x * y = (-x) * (-y) = (!x + 1) * (-y) = !x * (-y) + (-y),  where \"!\" is\n    // the bitwise inversion and arithmetic operations are performed according\n    // to the rules of the code. If the short multiplicand is negative, the\n    // algorithm below uses this formula by substituting the short multiplicand\n    // for y and turns into the modified standard multiplication algorithm,\n    // where the carry flag is initialized with the additively negated short\n    // multiplicand and the chunks of the long multiplicand are bitwise inverted\n    if (other < 0) {\n      mask = MASK;\n      carry = other = -other;\n    }\n    for (size_t i = 0; i < L; i++) {\n      uint128_t sum = static_cast<uint128_t>(data[i] ^ mask) * other + carry;\n      result.data[i] = static_cast<uint64_t>(sum & MASK);\n      carry = static_cast<uint64_t>(sum >> B);\n    }\n    return result;\n  }\n\n private:\n  // B-bit chunks representing the current number\n  uint64_t data[L] = {0};\n\n  // Creates an instance representing zero\n  constexpr CInt() {}\n\n  // Stores at the address \"output\" the array of O-bit chunks, which\n  // represents the big unsigned integer equal modulo 2ᴼ * \"ᵒˢᶦᶻᵉ\" to\n  // the input big unsigned integer stored at the address \"input\" as\n  // an array of \"isize\" I-bit chunks. The ordering of the chunks in\n  // these arrays is little-endian\n  template <size_t I, size_t O>\n  constexpr static void Convert(const uint64_t *input, const size_t isize,\n                                uint64_t *output, const size_t osize) {\n    size_t bits = 0;\n    size_t total = std::min(isize * I, osize * O);\n    for (size_t i = 0; i < osize; i++) {\n      output[i] = 0;\n    }\n    while (bits < total) {\n      size_t i = bits % I;\n      size_t o = bits % O;\n      output[bits / O] |= (input[bits / I] >> i) << o;\n      bits += std::min(I - i, O - o);\n    }\n    uint64_t mask = ~uint64_t{0} >> (64 - O);\n    size_t filled = (total + O - 1) / O;\n    for (size_t i = 0; i < filled; i++) {\n      output[i] &= mask;\n    }\n  }\n};\n\n// Type of the modular multiplicative inverter based on the Bernstein-Yang\n// method. The inverter can be created for a specified odd modulus M and\n// adjusting parameter A to compute the adjusted multiplicative inverses\n// of positive integers, i.e. for computing (1 / x) * A (mod M) for a\n// positive integer x.\n//\n// The adjusting parameter allows computing the multiplicative inverses\n// in the case of using the Montgomery representation for the input or\n// the expected output. If R is the Montgomery factor, the multiplicative\n// inverses in the appropriate representation can be computed provided\n// that the value of A is chosen as follows:\n// - A = 1, if both the input and expected output are in the trivial form;\n// - A = R² mod M, if both the input and the expected output are in the\n// Montgomery form;\n// - A = R mod M, if either the input or the expected output is in the\n// Montgomery form, but not both of them.\n//\n// For a better understanding of the implementation, use following resources:\n// - D. Bernstein, B.-Y. Yang, \"Fast constant-time gcd computation and modular\n// inversion\", https://gcd.cr.yp.to/safegcd-20190413.pdf\n// - P. Wuille, \"The safegcd implementation in libsecp256k1 explained\",\n// https://github.com/bitcoin-core/secp256k1/blob/master/doc/safegcd_implementation.md\ntemplate <size_t L>\nclass BYInverter {\n public:\n  // Creates an inverter for specified modulus and adjusting parameter\n  constexpr BYInverter(const BigInt<L> &modulus, const BigInt<L> &adjuster)\n      : modulus(LCInt::Load(modulus, false)),\n        adjuster(LCInt::Load(adjuster, false)),\n        inverse62(Invert62(modulus.limbs[0])) {}\n\n  // Returns \"true\" and stores into the variable, which is specified by\n  // the second argument, the adjusted modular multiplicative inverse\n  // of the input number, if it is invertible for the modulus of the\n  // invertor (i.e. coprime with it). Otherwise, \"false\" is returned\n  bool Invert(const BigInt<L> &input, BigInt<L> &output) const {\n    int64_t delta = 1;\n    LCInt f = modulus;\n    LCInt g = LCInt::Load(input, false);\n    LCInt d = ZERO;\n    LCInt e = adjuster;\n    int64_t t[2][2];\n    while (g != ZERO) {\n      Jump(f.Lowest(), g.Lowest(), delta, t);\n      FG(f, g, t);\n      DE(d, e, t);\n    }\n    // At this point the absolute value of \"f\" equals the greatest\n    // common divisor of the integer to be inverted and the modulus\n    // the inverter was created for. Thus, if \"f\" is neither 1 nor\n    // -1, then the sought inverse does not exist\n    bool antiunit = f == MINUSONE;\n    bool invertible = (f == ONE) || antiunit;\n    if (invertible) {\n      Norm(d, antiunit).Save(output);\n    }\n    return invertible;\n  }\n\n private:\n  // The big signed integer type used by the inverter. The absolute\n  // values of big signed results of the intermediate computations\n  // have no more than 64 * L + 63 bits in their binary representation\n  typedef CInt<62, (64 * (L + 1) + 61) / 62> LCInt;\n\n  // The big signed representation of 0\n  constexpr static LCInt ZERO = LCInt::Load(0);\n\n  // The big signed representation of 1\n  constexpr static LCInt ONE = LCInt::Load(1);\n\n  // The big signed representation of -1\n  constexpr static LCInt MINUSONE = LCInt::Load(-1);\n\n  // Modulus\n  LCInt modulus;\n\n  // Adjusting parameter\n  LCInt adjuster;\n\n  // Multiplicative inverse of the modulus modulo 2⁶²\n  uint64_t inverse62;\n\n  // Stores the Bernstein-Yang transition matrix multiplied by 2⁶² and\n  // the new value of the delta variable for the 62 basic steps of the\n  // Bernstein-Yang method, which are to be performed sequentially for\n  // specified initial values of delta, f and g. The initial values of\n  // f and g are specified partially: only the least significant chunks\n  // of their LCInt representations are the arguments\n  static void Jump(uint64_t f, uint64_t g, int64_t &delta, int64_t t[2][2]) {\n    t[0][0] = t[1][1] = 1;\n    t[0][1] = t[1][0] = 0;\n    int64_t steps = 62;\n    int64_t y[2];\n    uint64_t x;\n\n    while (true) {\n      int64_t zeros = base::bits::CountTrailingZeroBits(g);\n      zeros = std::min(zeros, steps);\n      steps -= zeros;\n      delta += zeros;\n      g >>= zeros;\n      t[0][0] <<= zeros;\n      t[0][1] <<= zeros;\n\n      if (steps == 0) {\n        break;\n      }\n\n      if (delta > 0) {\n        delta = -delta;\n\n        y[0] = -t[0][0];\n        y[1] = -t[0][1];\n        t[0][0] = t[1][0];\n        t[0][1] = t[1][1];\n        t[1][0] = y[0];\n        t[1][1] = y[1];\n\n        x = -f;\n        f = g;\n        g = x;\n      }\n\n      // The formula (3 * x) xor 28 = -1 / x (mod 32) for an odd integer\n      // x in the two's complement code has been derived from the formula\n      // (3 * x) xor 2 = 1 / x (mod 32) attributed to Peter Montgomery\n      uint64_t mask =\n          (1 << std::min(std::min(steps, 1 - delta), int64_t{5})) - 1;\n      uint64_t w = g * ((3 * f) ^ 28) & mask;\n\n      y[0] = t[0][0] * w + t[1][0];\n      y[1] = t[0][1] * w + t[1][1];\n      t[1][0] = y[0];\n      t[1][1] = y[1];\n\n      g += w * f;\n    }\n  }\n\n  // Stores the updated values of the variables f and g for specified\n  // initial ones and Bernstein-Yang transition matrix multiplied by 2⁶².\n  // In the vector form this operation can be described using the formula:\n  // \"(f, g)' := matrix * (f, g)' / 2⁶²\", where \"'\" is the transpose\n  // operator and \":=\" denotes assignment\n  static void FG(LCInt &f, LCInt &g, const int64_t t[2][2]) {\n    LCInt x = (f * t[0][0] + g * t[0][1]).Shift();\n    LCInt y = (f * t[1][0] + g * t[1][1]).Shift();\n    f = x;\n    g = y;\n  }\n\n  // Stores the updated values of the variables d and e for specified\n  // initial ones and Bernstein-Yang transition matrix multiplied by\n  // 2⁶². The new value of the vector \"(d, e)'\" is congruent modulo M\n  // to \"matrix * (d, e)' / 2⁶² (mod M)\", where M is the modulus the\n  // inverter was created for and \"'\" stands for the transpose operator.\n  // Both the initial and new values of d and e lie in (-2 * M, M)\n  void DE(LCInt &d, LCInt &e, const int64_t (&t)[2][2]) const {\n    int64_t md = t[0][0] * d.IsNegative() + t[0][1] * e.IsNegative();\n    int64_t me = t[1][0] * d.IsNegative() + t[1][1] * e.IsNegative();\n    {\n      int64_t cd = (t[0][0] * d.Lowest() + t[0][1] * e.Lowest()) & LCInt::MASK;\n      int64_t ce = (t[1][0] * d.Lowest() + t[1][1] * e.Lowest()) & LCInt::MASK;\n\n      md -= (cd * inverse62 + md) & LCInt::MASK;\n      me -= (ce * inverse62 + me) & LCInt::MASK;\n    }\n    LCInt cd = d * t[0][0] + e * t[0][1] + modulus * md;\n    LCInt ce = d * t[1][0] + e * t[1][1] + modulus * me;\n    d = cd.Shift();\n    e = ce.Shift();\n  }\n\n  // Returns either \"value (mod M)\" or \"-value (mod M)\", where\n  // M is the modulus the inverter was created for, depending\n  // on \"negate\", which determines the presence of \"-\" in the\n  // used formula. The input integer lies in (-2 * M, M)\n  LCInt Norm(const LCInt &value, const bool negate) const {\n    LCInt result = value.IsNegative() ? value + modulus : value;\n    if (negate) {\n      result = -result;\n    }\n    if (result.IsNegative()) {\n      result = result + modulus;\n    }\n    return result;\n  }\n\n  // Returns the multiplicative inverse of the argument modulo 2⁶². The\n  // implementation is based on the Hurchalla's method for computing the\n  // multiplicative inverse modulo a power of two. For better understanding\n  // the implementation, the following paper is recommended:\n  // J. Hurchalla, \"An Improved Integer Multiplicative Inverse (modulo 2ʷ)\",\n  // https://arxiv.org/pdf/2204.04342.pdf\n  constexpr static uint64_t Invert62(const uint64_t value) {\n    uint64_t x = 3 * value ^ 2;\n    uint64_t y = 1 - x * value;\n    x *= y + 1;\n    y *= y;\n    x *= y + 1;\n    y *= y;\n    x *= y + 1;\n    y *= y;\n    return (x * (y + 1)) & LCInt::MASK;\n  }\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_BASE_BYINVERTER_H_\n"
  },
  {
    "path": "tachyon/math/base/byinverter_unittest.cc",
    "content": "#include \"tachyon/math/base/byinverter.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/base/big_int.h\"\n\nnamespace tachyon::math {\n\n// The cases of a prime modulus and trivial representation of\n// both input and output of the inversion method. The modulus\n// is the order of the scalar field of the bn254 curve\nTEST(BYInverterTest, PrimeTrivial) {\n  BigInt<4> modulus = *BigInt<4>::FromDecString(\n      \"218882428718392752222464057452572750885483644004160343436982041865758084\"\n      \"95617\");\n  BigInt<4> adjuster = BigInt<4>::One();\n  BYInverter<4> inverter = BYInverter<4>(modulus, adjuster);\n  BigInt<4> input, output, expected;\n\n  input = *BigInt<4>::FromDecString(\n      \"103016822455395885937009463448678224530860491458983009780242298691299944\"\n      \"91070\");\n  expected = *BigInt<4>::FromDecString(\n      \"198619552734958050563237927350841782377548356603726300802008990262189760\"\n      \"35016\");\n  EXPECT_TRUE(inverter.Invert(input, output));\n  EXPECT_EQ(output, expected);\n\n  input = *BigInt<4>::FromDecString(\n      \"126520449418548447151734230498264169965304739797018972163764030461079803\"\n      \"31138\");\n  expected = *BigInt<4>::FromDecString(\n      \"137411891073970879827055042948574283647817819871615941505158449651527160\"\n      \"38798\");\n  EXPECT_TRUE(inverter.Invert(input, output));\n  EXPECT_EQ(output, expected);\n\n  input = *BigInt<4>::FromDecString(\n      \"212303313650938401560033971399886135220900061766237263322117467937143842\"\n      \"61253\");\n  expected = *BigInt<4>::FromDecString(\n      \"199212938989172163056655895547418784289276648223865752630701101392037405\"\n      \"91967\");\n  EXPECT_TRUE(inverter.Invert(input, output));\n  EXPECT_EQ(output, expected);\n\n  EXPECT_FALSE(inverter.Invert(BigInt<4>::Zero(), output));\n}\n\n// The cases of a prime modulus and Montgomery representation of both\n// input and output of the inversion method. The modulus is the order\n// of the scalar field of the bn254 curve. The Montgomery factor equals\n// 2²⁵⁶.For the numbers specified in Montgomery representation their\n// trivial form is in the comments\nTEST(BYInverterTest, PrimeMontgomery) {\n  BigInt<4> modulus = *BigInt<4>::FromDecString(\n      \"218882428718392752222464057452572750885483644004160343436982041865758084\"\n      \"95617\");\n  BigInt<4> adjuster = *BigInt<4>::FromDecString(\n      \"944936681149208446651664254269745548490766851729442924617792859073125903\"\n      \"783\");\n  BYInverter<4> inverter = BYInverter<4>(modulus, adjuster);\n  BigInt<4> input, output, expected;\n\n  // 104956732223578925557223488830078362736943595779422517109872604305846127009997\n  input = *BigInt<4>::FromDecString(\n      \"358795011877924939067134028731607639090230656906793922100740187261350678\"\n      \"7085\");\n  // 13836902468045855406793973610070160223721877582113063977376419473093071358947\n  expected = *BigInt<4>::FromDecString(\n      \"211488592907427181113099752467384490255004996398674446817083716551085719\"\n      \"82666\");\n  EXPECT_TRUE(inverter.Invert(input, output));\n  EXPECT_EQ(output, expected);\n\n  // 58137146071470818113497440987533709949510360232295259246272891334885364755793\n  input = *BigInt<4>::FromDecString(\n      \"567056632061005688596945785975957941913343351371449825599748549267295198\"\n      \"3989\");\n  // 10012933272639613859587515636169426221587622189722693700891691228635545350648\n  expected = *BigInt<4>::FromDecString(\n      \"448106334201548424302265130892044159754236703369274706140399820044324154\"\n      \"3910\");\n  EXPECT_TRUE(inverter.Invert(input, output));\n  EXPECT_EQ(output, expected);\n\n  // 28936367257625975338385437547494218524455841456842373037716593520344969509397\n  input = *BigInt<4>::FromDecString(\n      \"394407580137911053895742321381425496701226931447443828555088855056098800\"\n      \"3234\");\n  // 5533769493925008693547098095539268782323697506580128579981487108293743418881\n  expected = *BigInt<4>::FromDecString(\n      \"137083209848048852584441963389817935909133205377178783422273787606625729\"\n      \"27772\");\n  EXPECT_TRUE(inverter.Invert(input, output));\n  EXPECT_EQ(output, expected);\n\n  EXPECT_FALSE(inverter.Invert(BigInt<4>::Zero(), output));\n}\n\n// The cases of a composite modulus and trivial representation\n// of both input and output of the inversion method. The modulus\n// is 2 plus the order of the scalar field of the bn254 curve\nTEST(BYInverterTest, CompositeTrivial) {\n  BigInt<4> modulus = *BigInt<4>::FromDecString(\n      \"218882428718392752222464057452572750885483644004160343436982041865758084\"\n      \"95619\");\n  BigInt<4> adjuster = BigInt<4>::One();\n  BYInverter<4> inverter = BYInverter<4>(modulus, adjuster);\n  BigInt<4> input, output, expected;\n\n  input = *BigInt<4>::FromDecString(\n      \"260400902909203205801927137882405069909206412991261887835516351877431832\"\n      \"0405\");\n  // Not invertible, since GCD(input, modulus) = 3\n  EXPECT_FALSE(inverter.Invert(input, output));\n\n  input = *BigInt<4>::FromDecString(\n      \"617886981998124808174262578967300334936713010129281877740500228549113711\"\n      \"3070\");\n  expected = *BigInt<4>::FromDecString(\n      \"118091461884980844928468691940872607811557868933035705144433998283587672\"\n      \"12457\");\n  EXPECT_TRUE(inverter.Invert(input, output));\n  EXPECT_EQ(output, expected);\n\n  input = *BigInt<4>::FromDecString(\n      \"118890046284150530256808306616576448503703129212685170076195830906438907\"\n      \"09598\");\n  expected = *BigInt<4>::FromDecString(\n      \"954727260627150970144166832159552548250347273530028832700546847220916690\"\n      \"8277\");\n  EXPECT_TRUE(inverter.Invert(input, output));\n  EXPECT_EQ(output, expected);\n\n  EXPECT_FALSE(inverter.Invert(BigInt<4>::Zero(), output));\n}\n\n// The cases of a composite modulus and Montgomery representation of both\n// input and output of the inversion method. The modulus is 2 plus the order\n// of the scalar field of the bn254 curve. The Montgomery factor equals 2²⁵⁶.\n// For the numbers specified in Montgomery representation their trivial form\n// is in the comments\nTEST(BYInverterTest, CompositeMontgomery) {\n  BigInt<4> modulus = *BigInt<4>::FromDecString(\n      \"218882428718392752222464057452572750885483644004160343436982041865758084\"\n      \"95619\");\n  BigInt<4> adjuster = *BigInt<4>::FromDecString(\n      \"157148295651647307182743343858683637233496413180705862961339672050283867\"\n      \"5869\");\n  BYInverter<4> inverter = BYInverter<4>(modulus, adjuster);\n  BigInt<4> input, output, expected;\n\n  // 11865630156646177845488966194957438762902332631956652259274124517358851833759\n  input = *BigInt<4>::FromDecString(\n      \"878201618454704726522650428350653751071479777045695423321764180703260530\"\n      \"5262\");\n  // Not invertible, since GCD(input, modulus) = 3\n  EXPECT_FALSE(inverter.Invert(input, output));\n\n  // 13626187621506977415144012068495580987161044579599980007720002490790855281118\n  input = *BigInt<4>::FromDecString(\n      \"112228015787800026601163349092648871925500177357276714834785886408856351\"\n      \"41103\");\n  // 15569609600917656079795063009991863730429494071872866284038456731797547021054\n  expected = *BigInt<4>::FromDecString(\n      \"183861344690836251394583500718777155185091441658607621272783642818974339\"\n      \"70018\");\n  EXPECT_TRUE(inverter.Invert(input, output));\n  EXPECT_EQ(output, expected);\n\n  // 4652503444426046901695366961113196520526566962552761000864601049682876484005\n  input = *BigInt<4>::FromDecString(\n      \"591068310329360988454145556194259332147196340254859988568619407143112124\"\n      \"7969\");\n  // 4735762809510279031273699449377321073262538802381459266345256675192986276306\n  expected = *BigInt<4>::FromDecString(\n      \"962405928941411729669007908489132377986537009453884327835765191029297149\"\n      \"4292\");\n  EXPECT_TRUE(inverter.Invert(input, output));\n  EXPECT_EQ(output, expected);\n\n  EXPECT_FALSE(inverter.Invert(BigInt<4>::Zero(), output));\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/base/const_init.h",
    "content": "#ifndef TACHYON_MATH_BASE_CONST_INIT_H_\n#define TACHYON_MATH_BASE_CONST_INIT_H_\n\nnamespace tachyon::math {\n\nenum ZeroInitType {\n  kZeroInit,\n};\n\nenum OneInitType {\n  kOneInit,\n};\n\nenum MinusOneInitType {\n  kMinusOneInit,\n};\n\nenum TwoInvInitType {\n  kTwoInvInit,\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_BASE_CONST_INIT_H_\n"
  },
  {
    "path": "tachyon/math/base/egcd.h",
    "content": "#ifndef TACHYON_MATH_BASE_EGCD_H_\n#define TACHYON_MATH_BASE_EGCD_H_\n\nnamespace tachyon::math {\n\ntemplate <typename T>\nstruct EGCD {\n  struct Result {\n    T s;\n    T t;\n    T r;\n\n    bool IsValid(T x, T y) const { return s * x + t * y == r; }\n  };\n\n  constexpr static Result Compute(T x, T y) {\n    T r_prev = x, r = y, s_prev = 1, s = 0, t_prev = 0, t = 1;\n\n    while (r != 0) {\n      T q = r_prev / r;\n\n      T temp = r;\n      r = r_prev - q * r;\n      r_prev = temp;\n\n      temp = s;\n      s = s_prev - q * s;\n      s_prev = temp;\n\n      temp = t;\n      t = t_prev - q * t;\n      t_prev = temp;\n    }\n\n    return {s_prev, t_prev, r_prev};\n  }\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_BASE_EGCD_H_\n"
  },
  {
    "path": "tachyon/math/base/egcd_unittest.cc",
    "content": "#include \"tachyon/math/base/egcd.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/random.h\"\n\nnamespace tachyon::math {\n\nTEST(EGCDTest, Compute) {\n  int x = base::Uniform(base::Range<int>::From(1));\n  int y = base::Uniform(base::Range<int>::From(1));\n  EGCD<int>::Result result = EGCD<int>::Compute(x, y);\n  EXPECT_TRUE(result.IsValid(x, y));\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/base/field.h",
    "content": "#ifndef TACHYON_MATH_BASE_FIELD_H_\n#define TACHYON_MATH_BASE_FIELD_H_\n\n#include <ostream>\n#include <utility>\n\n#include \"tachyon/math/base/ring.h\"\n\nnamespace tachyon::math {\n\n// Field is any set of elements that satisfies the field axioms for both\n// addition and multiplication and is commutative division algebra.\n// Simply put, a field is a ring in which multiplicative commutativity exists,\n// and every non-zero element has a multiplicative inverse.\n// See https://mathworld.wolfram.com/Field.html\n\n// The Field supports SumOfProducts and BatchInverse, inheriting the properties\n// of both AdditiveGroup and MultiplicativeGroup.\ntemplate <typename F>\nclass Field : public AdditiveGroup<F>, public MultiplicativeGroup<F> {\n public:\n  // Sum of products: a₁ * b₁ + a₂ * b₂ + ... + aₙ * bₙ\n  template <typename ContainerA, typename ContainerB>\n  constexpr static F SumOfProducts(const ContainerA& a, const ContainerB& b) {\n    return Ring<F>::SumOfProducts(a, b);\n  }\n\n  // Sum of products: a₁ * b₁ + a₂ * b₂ + ... + aₙ * bₙ\n  template <typename ContainerA, typename ContainerB>\n  constexpr static F SumOfProductsSerial(const ContainerA& a,\n                                         const ContainerB& b) {\n    return Ring<F>::SumOfProductsSerial(a, b);\n  }\n};\n\ntemplate <typename F>\nstd::ostream& operator<<(std::ostream& os, const Field<F>& f) {\n  const F& derived = static_cast<const F&>(f);\n  return os << derived.ToString();\n}\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_BASE_FIELD_H_\n"
  },
  {
    "path": "tachyon/math/base/field_unittest.cc",
    "content": "#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\nclass FieldTest : public FiniteFieldTest<GF7> {};\n\n}  // namespace\n\nTEST(FieldTest, SumOfProductsSerial) {\n  std::vector<GF7> a = {GF7(2), GF7(3), GF7(4)};\n  std::vector<GF7> b = {GF7(1), GF7(2), GF7(3)};\n\n  GF7 result = Field<GF7>::SumOfProductsSerial(a, b);\n  EXPECT_EQ(result, GF7(6));\n}\n\nTEST(FieldTest, SumOfProductsParallel) {\n#if defined(TACHYON_HAS_OPENMP)\n  size_t thread_nums = static_cast<size_t>(omp_get_max_threads());\n#else\n  size_t thread_nums = 1;\n#endif\n\n  std::vector<GF7> a =\n      base::CreateVector(thread_nums * 10, []() { return GF7::Random(); });\n  std::vector<GF7> b =\n      base::CreateVector(thread_nums * 10, []() { return GF7::Random(); });\n\n  EXPECT_EQ(Field<GF7>::SumOfProducts(a, b),\n            Field<GF7>::SumOfProductsSerial(a, b));\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/base/gmp/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"bit_traits\",\n    hdrs = [\"bit_traits.h\"],\n    deps = [\n        \":gmp_util\",\n        \"//tachyon/math/base:bit_traits_forward\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"gmp_util\",\n    srcs = [\"gmp_util.cc\"],\n    hdrs = [\"gmp_util.h\"],\n    deps = [\n        \"//tachyon/base:logging\",\n        \"//tachyon/base/strings:string_util\",\n        \"//tachyon/math/base:sign\",\n        \"@com_google_absl//absl/base\",\n        \"@local_config_gmp//:gmp\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"signed_value\",\n    hdrs = [\"signed_value.h\"],\n    deps = [\n        \":gmp_util\",\n        \"//tachyon/math/base:sign\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"gmp_unittests\",\n    srcs = [\n        \"gmp_util_unittest.cc\",\n    ],\n    deps = [\n        \":gmp_util\",\n        \"//tachyon/build:build_config\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/math/base/gmp/bit_traits.h",
    "content": "#ifndef TACHYON_MATH_BASE_GMP_BIT_TRAITS_H_\n#define TACHYON_MATH_BASE_GMP_BIT_TRAITS_H_\n\n#include \"tachyon/math/base/bit_traits_forward.h\"\n#include \"tachyon/math/base/gmp/gmp_util.h\"\n\nnamespace tachyon::math {\n\ntemplate <>\nclass BitTraits<mpz_class> {\n public:\n  constexpr static bool kIsDynamic = true;\n\n  static size_t GetNumBits(const mpz_class& value) {\n    return gmp::GetNumBits(value);\n  }\n\n  static bool TestBit(const mpz_class& value, size_t index) {\n    return gmp::TestBit(value, index);\n  }\n\n  static void SetBit(mpz_class& value, size_t index, bool bit_value) {\n    return gmp::SetBit(value, index, bit_value);\n  }\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_BASE_GMP_BIT_TRAITS_H_\n"
  },
  {
    "path": "tachyon/math/base/gmp/gmp.bzl",
    "content": ""
  },
  {
    "path": "tachyon/math/base/gmp/gmp_util.cc",
    "content": "#include \"tachyon/math/base/gmp/gmp_util.h\"\n\n#include \"absl/base/call_once.h\"\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/strings/string_util.h\"\n\nnamespace tachyon::math::gmp {\n\nnamespace {\n\ngmp_randstate_t& GetRandomState() {\n  static gmp_randstate_t random_state;\n  static absl::once_flag once;\n  absl::call_once(once, []() {\n    gmp_randinit_mt(random_state);\n    gmp_randseed_ui(random_state, time(NULL));\n  });\n  return random_state;\n}\n\n}  // namespace\n\nstatic_assert(sizeof(mp_limb_t) == sizeof(uint64_t), \"limb should be 64 bit\");\n\nmpz_class Random(mpz_class n) {\n  mpz_class value;\n  mpz_urandomm(value.get_mpz_t(), GetRandomState(), n.get_mpz_t());\n  return value;\n}\n\nbool ParseIntoMpz(std::string_view str, int base, mpz_class* out) {\n  if (base == 16) {\n    base::ConsumePrefix0x(&str);\n  }\n\n  return out->set_str(str.data(), base) == 0;\n}\n\nvoid MustParseIntoMpz(std::string_view str, int base, mpz_class* out) {\n  CHECK(ParseIntoMpz(str, base, out));\n}\n\nmpz_class FromDecString(std::string_view str) {\n  mpz_class ret;\n  MustParseIntoMpz(str, 10, &ret);\n  return ret;\n}\n\nmpz_class FromHexString(std::string_view str) {\n  mpz_class ret;\n  MustParseIntoMpz(str, 16, &ret);\n  return ret;\n}\n\nSign GetSign(const mpz_class& out) { return ToSign(mpz_sgn(out.get_mpz_t())); }\n\nbool IsZero(const mpz_class& value) { return GetSign(value) == Sign::kZero; }\n\nbool IsNegative(const mpz_class& value) {\n  return GetSign(value) == Sign::kNegative;\n}\n\nbool IsPositive(const mpz_class& value) {\n  return GetSign(value) == Sign::kPositive;\n}\n\nmpz_class GetAbs(const mpz_class& value) {\n  mpz_class ret;\n  mpz_abs(ret.get_mpz_t(), value.get_mpz_t());\n  return ret;\n}\n\nsize_t GetNumBits(const mpz_class& value) {\n  return GetLimbSize(value) * GMP_LIMB_BITS;\n}\n\nbool TestBit(const mpz_class& value, size_t index) {\n  return mpz_tstbit(value.get_mpz_t(), index) == 1;\n}\n\nvoid SetBit(mpz_class& value, size_t index, bool bit_value) {\n  bit_value ? SetBit(value, index) : ClearBit(value, index);\n}\n\nvoid SetBit(mpz_class& value, size_t index) {\n  mpz_setbit(value.get_mpz_t(), index);\n}\n\nvoid ClearBit(mpz_class& value, size_t index) {\n  mpz_clrbit(value.get_mpz_t(), index);\n}\n\nuint64_t* GetLimbs(const mpz_class& value) {\n  return reinterpret_cast<uint64_t*>(value.__get_mp()->_mp_d);\n}\n\nsize_t GetLimbSize(const mpz_class& value) {\n  return value.__get_mp()->_mp_size;\n}\n\nmp_limb_t GetLimb(const mpz_class& value, size_t idx) {\n  return value.__get_mp()->_mp_d[idx];\n}\n\nvoid CopyLimbs(const mpz_class& value, uint64_t* limbs) {\n  for (size_t i = 0; i < GetLimbSize(value); ++i) {\n    limbs[i] = GetLimb(value, i);\n  }\n}\n\nvoid WriteLimbs(const uint64_t* limbs_src, size_t limb_size, mpz_class* out) {\n  mp_ptr limbs_dst = mpz_limbs_write(out->get_mpz_t(), limb_size);\n  for (size_t i = 0; i < limb_size; ++i) {\n    limbs_dst[i] = limbs_src[i];\n  }\n  mpz_limbs_finish(out->get_mpz_t(), limb_size);\n}\n\n}  // namespace tachyon::math::gmp\n"
  },
  {
    "path": "tachyon/math/base/gmp/gmp_util.h",
    "content": "#ifndef TACHYON_MATH_BASE_GMP_GMP_UTIL_H_\n#define TACHYON_MATH_BASE_GMP_GMP_UTIL_H_\n\n#include <stddef.h>\n\n#include <string_view>\n#include <type_traits>\n\n#include \"third_party/gmp/include/gmpxx.h\"\n\n#include \"tachyon/math/base/sign.h\"\n\nnamespace tachyon::math::gmp {\n\n// Generate a uniform integer in the range 0 to n-1, inclusive.\nTACHYON_EXPORT mpz_class Random(mpz_class n);\n\nTACHYON_EXPORT bool ParseIntoMpz(std::string_view str, int base,\n                                 mpz_class* out);\n\nTACHYON_EXPORT void MustParseIntoMpz(std::string_view str, int base,\n                                     mpz_class* out);\n\nTACHYON_EXPORT mpz_class FromDecString(std::string_view str);\nTACHYON_EXPORT mpz_class FromHexString(std::string_view str);\n\ntemplate <typename T, std::enable_if_t<std::is_unsigned_v<T>>* = nullptr>\nmpz_class FromUnsignedInt(T value) {\n  mpz_class ret;\n  mpz_set_ui(ret.get_mpz_t(), value);\n  return ret;\n}\n\ntemplate <typename T, std::enable_if_t<std::is_signed_v<T>>* = nullptr>\nmpz_class FromSignedInt(T value) {\n  mpz_class ret;\n  mpz_set_si(ret.get_mpz_t(), value);\n  return ret;\n}\n\nTACHYON_EXPORT Sign GetSign(const mpz_class& value);\nTACHYON_EXPORT bool IsZero(const mpz_class& value);\nTACHYON_EXPORT bool IsNegative(const mpz_class& value);\nTACHYON_EXPORT bool IsPositive(const mpz_class& value);\n\nTACHYON_EXPORT mpz_class GetAbs(const mpz_class& value);\n\nTACHYON_EXPORT size_t GetNumBits(const mpz_class& value);\nTACHYON_EXPORT bool TestBit(const mpz_class& value, size_t index);\n// This calls either SetBit(value, index) or ClearBit(value, index)\n// based on the value of bit_value.\nTACHYON_EXPORT void SetBit(mpz_class& value, size_t index, bool bit_value);\n\nTACHYON_EXPORT void SetBit(mpz_class& value, size_t index);\nTACHYON_EXPORT void ClearBit(mpz_class& value, size_t index);\n\nTACHYON_EXPORT uint64_t* GetLimbs(const mpz_class& value);\nTACHYON_EXPORT size_t GetLimbSize(const mpz_class& value);\nTACHYON_EXPORT mp_limb_t GetLimb(const mpz_class& value, size_t idx);\nTACHYON_EXPORT void CopyLimbs(const mpz_class& value, uint64_t* limbs);\nTACHYON_EXPORT void WriteLimbs(const uint64_t* limbs, size_t limb_size,\n                               mpz_class* out);\n\n}  // namespace tachyon::math::gmp\n\n#endif  // TACHYON_MATH_BASE_GMP_GMP_UTIL_H_\n"
  },
  {
    "path": "tachyon/math/base/gmp/gmp_util_unittest.cc",
    "content": "#include \"tachyon/math/base/gmp/gmp_util.h\"\n\n#include <vector>\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/build/build_config.h\"\n\nnamespace tachyon::math::gmp {\n\nTEST(GmpUtilTest, FromDecString) {\n  EXPECT_EQ(FromDecString(\"1234\"), mpz_class(1234));\n  EXPECT_EQ(FromDecString(\"-1234\"), mpz_class(-1234));\n}\n\nTEST(GmpUtilTest, FromHexString) {\n  EXPECT_EQ(FromHexString(\"0x1234\"), mpz_class(0x1234));\n  EXPECT_EQ(FromHexString(\"1234\"), mpz_class(0x1234));\n}\n\nTEST(GmpUtilTest, FromUnsignedInt) {\n  EXPECT_EQ(FromUnsignedInt(uint64_t{1234}), mpz_class(1234));\n}\n\nTEST(GmpUtilTest, FromSignedInt) {\n  EXPECT_EQ(FromSignedInt(1234), mpz_class(1234));\n  EXPECT_EQ(FromSignedInt(-1234), mpz_class(-1234));\n}\n\nTEST(GmpUtilTest, Sign) {\n  mpz_class zero(0);\n  EXPECT_EQ(GetSign(zero), Sign::kZero);\n  EXPECT_TRUE(IsZero(zero));\n  EXPECT_FALSE(IsNegative(zero));\n  EXPECT_FALSE(IsPositive(zero));\n\n  mpz_class positive(1234);\n  EXPECT_EQ(GetSign(positive), Sign::kPositive);\n  EXPECT_FALSE(IsZero(positive));\n  EXPECT_FALSE(IsNegative(positive));\n  EXPECT_TRUE(IsPositive(positive));\n\n  mpz_class negative(-1234);\n  EXPECT_EQ(GetSign(negative), Sign::kNegative);\n  EXPECT_FALSE(IsZero(negative));\n  EXPECT_TRUE(IsNegative(negative));\n  EXPECT_FALSE(IsPositive(negative));\n}\n\nTEST(GmpUtilTest, Abs) {\n  mpz_class zero(0);\n  EXPECT_EQ(GetAbs(zero), zero);\n\n  mpz_class positive(1234);\n  EXPECT_EQ(GetAbs(positive), positive);\n\n  mpz_class negative(-1234);\n  EXPECT_EQ(GetAbs(negative), positive);\n}\n\nTEST(GmpUtilTest, Bits) {\n  mpz_class value(0b11001);\n  EXPECT_EQ(GetNumBits(value), 64);\n\n#if ARCH_CPU_LITTLE_ENDIAN\n  EXPECT_EQ(TestBit(value, 0), true);\n  EXPECT_EQ(TestBit(value, 1), false);\n  EXPECT_EQ(TestBit(value, 2), false);\n  EXPECT_EQ(TestBit(value, 3), true);\n  EXPECT_EQ(TestBit(value, 4), true);\n#else\n  EXPECT_EQ(TestBit(value, 0), true);\n  EXPECT_EQ(TestBit(value, 1), true);\n  EXPECT_EQ(TestBit(value, 2), false);\n  EXPECT_EQ(TestBit(value, 3), false);\n  EXPECT_EQ(TestBit(value, 4), true);\n#endif\n  ClearBit(value, 4);\n  EXPECT_EQ(TestBit(value, 4), false);\n  SetBit(value, 4);\n  EXPECT_EQ(TestBit(value, 4), true);\n}\n\nTEST(GmpUtilTest, Limbs) {\n  mpz_class value(1234);\n  EXPECT_EQ(GetLimbSize(value), size_t{1});\n  EXPECT_EQ(GetLimb(value, 0), 1234);\n}\n\n}  // namespace tachyon::math::gmp\n"
  },
  {
    "path": "tachyon/math/base/gmp/signed_value.h",
    "content": "#ifndef TACHYON_MATH_BASE_GMP_SIGNED_VALUE_H_\n#define TACHYON_MATH_BASE_GMP_SIGNED_VALUE_H_\n\n#include \"tachyon/math/base/gmp/gmp_util.h\"\n#include \"tachyon/math/base/sign.h\"\n\nnamespace tachyon::math {\n\ntemplate <>\nclass SignedValue<mpz_class> {\n public:\n  Sign sign;\n  mpz_class abs_value;\n\n  SignedValue() = default;\n  explicit SignedValue(const mpz_class& value)\n      : sign(gmp::GetSign(value)), abs_value(gmp::GetAbs(value)) {}\n\n  mpz_class ToValue() const {\n    switch (sign) {\n      case Sign::kZero:\n      case Sign::kPositive:\n        return abs_value;\n      case Sign::kNegative:\n        return -abs_value;\n      case Sign::kNaN:\n        NOTREACHED();\n        return abs_value;\n    }\n    NOTREACHED();\n    return abs_value;\n  }\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_BASE_GMP_SIGNED_VALUE_H_\n"
  },
  {
    "path": "tachyon/math/base/groups.h",
    "content": "#ifndef TACHYON_MATH_BASE_GROUPS_H_\n#define TACHYON_MATH_BASE_GROUPS_H_\n\n#include <atomic>\n#include <limits>\n#include <optional>\n#include <ostream>\n#include <tuple>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/parallelize.h\"\n#include \"tachyon/base/types/always_false.h\"\n#include \"tachyon/math/base/semigroups.h\"\n\nnamespace tachyon::math {\nnamespace internal {\n\nSUPPORTS_BINARY_OPERATOR(Sub);\n\n}  // namespace internal\n\n// Group 'G' is a set of elements together with a binary operation (called the\n// group operation) that together satisfy the four fundamental properties of\n// closure, associative, the identity property, and the inverse property.\n// See https://mathworld.wolfram.com/Group.html\n\n// MultiplicativeGroup is a group with the group operation '*'.\n// MultiplicativeGroup supports division and inversion, inheriting the\n// properties of MultiplicativeSemigroup.\ntemplate <typename G>\nclass MultiplicativeGroup : public MultiplicativeSemigroup<G> {\n public:\n  // NOTE(chokobole): This value was chosen empirically such that\n  // |batch_inverse_benchmark| performs better at fewer inputs compared to the\n  // number of cpu cores.\n  constexpr static size_t kParallelBatchInverseDivisorThreshold = 2;\n\n  // Division: a * b⁻¹\n  template <typename G2>\n  constexpr std::optional<G> operator/(const G2& other) const {\n    const std::optional<G> other_inv = other.Inverse();\n    const G* g = static_cast<const G*>(this);\n    if (LIKELY(other_inv)) return g->Mul(*other_inv);\n    LOG_IF_NOT_GPU(ERROR) << \"Division by zero attempted\";\n    return std::nullopt;\n  }\n\n  // Division in place: a *= b⁻¹\n  template <\n      typename G2,\n      std::enable_if_t<internal::SupportsMulInPlace<G, G2>::value>* = nullptr>\n  [[nodiscard]] constexpr std::optional<G*> operator/=(const G2& other) {\n    const std::optional<G> other_inv = other.Inverse();\n    G* g = static_cast<G*>(this);\n    if (LIKELY(other_inv)) return &g->MulInPlace(*other_inv);\n    LOG_IF_NOT_GPU(ERROR) << \"Division by zero attempted\";\n    return std::nullopt;\n  }\n\n  template <typename Container>\n  [[nodiscard]] constexpr static bool BatchInverseInPlace(\n      Container& groups, const G& coeff = G::One()) {\n    return BatchInverse(groups, &groups, coeff);\n  }\n\n  template <typename Container>\n  [[nodiscard]] constexpr static bool BatchInverseInPlaceSerial(\n      Container& groups, const G& coeff = G::One()) {\n    return BatchInverseSerial(groups, &groups, coeff);\n  }\n\n  // This is taken and modified from\n  // https://github.com/arkworks-rs/algebra/blob/5dfeedf/ff/src/fields/mod.rs#L355-L418.\n  // Batch inverse: [a₁, a₂, ..., aₙ] -> [a₁⁻¹, a₂⁻¹, ... , aₙ⁻¹]\n  template <typename InputContainer, typename OutputContainer>\n  [[nodiscard]] CONSTEXPR_IF_NOT_OPENMP static bool BatchInverse(\n      const InputContainer& groups, OutputContainer* inverses,\n      const G& coeff = G::One()) {\n    size_t size = std::size(groups);\n    if (size != std::size(*inverses)) {\n      LOG(ERROR) << \"Size of |groups| and |inverses| do not match\";\n      return false;\n    }\n\n#if defined(TACHYON_HAS_OPENMP)\n    size_t thread_nums = static_cast<size_t>(omp_get_max_threads());\n    if (size >=\n        size_t{1} << (thread_nums / kParallelBatchInverseDivisorThreshold)) {\n      std::atomic<bool> check_valid(true);\n      base::Parallelize(size, [&groups, inverses, &coeff, &check_valid](\n                                  size_t len, size_t chunk_offset,\n                                  size_t chunk_size) {\n        size_t start = chunk_offset * chunk_size;\n        absl::Span<const G> groups_chunk(&groups[start], len);\n        absl::Span<G> inverses_chunk(&(*inverses)[start], len);\n        if (UNLIKELY(!DoBatchInverse(groups_chunk, inverses_chunk, coeff))) {\n          check_valid.store(false, std::memory_order_relaxed);\n        }\n      });\n      if (UNLIKELY(!check_valid.load(std::memory_order_relaxed))) {\n        LOG(ERROR) << \"Inverse of zero attempted\";\n        return false;\n      }\n      return true;\n    }\n#endif\n    return DoBatchInverse(groups, absl::MakeSpan(*inverses), coeff);\n  }\n\n  template <typename InputContainer, typename OutputContainer>\n  [[nodiscard]] constexpr static bool BatchInverseSerial(\n      const InputContainer& groups, OutputContainer* inverses,\n      const G& coeff = G::One()) {\n    if (std::size(groups) != std::size(*inverses)) {\n      LOG(ERROR) << \"Size of |groups| and |inverses| do not match\";\n      return false;\n    }\n    return DoBatchInverse(groups, absl::MakeSpan(*inverses), coeff);\n  }\n\n private:\n  [[nodiscard]] constexpr static bool DoBatchInverse(absl::Span<const G> groups,\n                                                     absl::Span<G> inverses,\n                                                     const G& coeff) {\n    // Montgomery’s Trick and Fast Implementation of Masked AES\n    // Genelle, Prouff and Quisquater\n    // Section 3.2\n    // but with an optimization to multiply every element in the returned\n    // vector by |coeff|.\n\n    // First pass: compute [a₁, a₁ * a₂, ..., a₁ * a₂ * ... * aₙ]\n    std::vector<G> productions;\n    productions.reserve(groups.size() + 1);\n    productions.push_back(G::One());\n    G product = G::One();\n    for (const G& g : groups) {\n      if (!g.IsZero()) {\n        product *= g;\n        productions.push_back(product);\n      }\n    }\n\n    // Invert |product|.\n    // (a₁ * a₂ * ... *  aₙ)⁻¹\n    std::optional<G> product_inv_opt = product.Inverse();\n    if (UNLIKELY(!product_inv_opt)) {\n      LOG_IF_NOT_GPU(ERROR) << \"Inverse of zero attempted\";\n      return false;\n    }\n    G product_inv = std::move(*product_inv_opt);\n\n    // Multiply |product_inv| by |coeff|, so all inverses will be scaled by\n    // |coeff|.\n    // c * (a₁ * a₂ * ... *  aₙ)⁻¹\n    if (!coeff.IsOne()) product_inv *= coeff;\n\n    // Second pass: iterate backwards to compute inverses.\n    //              [c * a₁⁻¹, c * a₂,⁻¹ ..., c * aₙ⁻¹]\n    auto prod_it = productions.rbegin();\n    ++prod_it;\n    for (size_t i = groups.size() - 1; i != std::numeric_limits<size_t>::max();\n         --i) {\n      const G& g = groups[i];\n      if (!g.IsZero()) {\n        // c * (a₁ * a₂ * ... *  aᵢ)⁻¹ * aᵢ = c * (a₁ * a₂ * ... *  aᵢ₋₁)⁻¹\n        G new_product_inv = product_inv * g;\n        // v = c * (a₁ * a₂ * ... *  aᵢ)⁻¹ * (a₁ * a₂ * ... aᵢ₋₁) = c * aᵢ⁻¹\n        inverses[i] = product_inv * (*(prod_it++));\n        product_inv = std::move(new_product_inv);\n      } else {\n        inverses[i] = G::Zero();\n      }\n    }\n    return true;\n  }\n};\n\ntemplate <typename G>\nstd::ostream& operator<<(std::ostream& os, const MultiplicativeGroup<G>& g) {\n  const G& derived = static_cast<const G&>(g);\n  return os << derived.ToString();\n}\n\n// AdditiveGroup is a group with the group operation '+'.\n// AdditiveGroup supports subtraction and negation, inheriting the\n// properties of AdditiveSemigroup.\ntemplate <typename G>\nclass AdditiveGroup : public AdditiveSemigroup<G> {\n public:\n  // Subtraction:\n  //   1) a - b if subtraction is supported.\n  //   2) a + (-b) otherwise\n  template <typename G2,\n            std::enable_if_t<internal::SupportsAdd<G, G2>::value ||\n                             internal::SupportsSub<G, G2>::value>* = nullptr>\n  constexpr auto operator-(const G2& other) const {\n    if constexpr (internal::SupportsSub<G, G2>::value) {\n      const G* g = static_cast<const G*>(this);\n      return g->Sub(other);\n    } else {\n      return this->operator+(-other);\n    }\n  }\n\n  // Subtraction in place:\n  //   1) a -= b if subtraction is supported.\n  //   2) a += (-b) otherwise\n  template <\n      typename G2,\n      std::enable_if_t<internal::SupportsSubInPlace<G, G2>::value ||\n                       internal::SupportsAddInPlace<G, G2>::value>* = nullptr>\n  constexpr G& operator-=(const G2& other) {\n    if constexpr (internal::SupportsSubInPlace<G, G2>::value) {\n      G* g = static_cast<G*>(this);\n      return g->SubInPlace(other);\n    } else {\n      G* g = static_cast<G*>(this);\n      return g->AddInPlace(-other);\n    }\n  }\n\n  // Negation: -a\n  constexpr auto operator-() const {\n    const G* g = static_cast<const G*>(this);\n    return g->Negate();\n  }\n};\n\ntemplate <typename G>\nstd::ostream& operator<<(std::ostream& os, const AdditiveGroup<G>& g) {\n  const G& derived = static_cast<const G&>(g);\n  return os << derived.ToString();\n}\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_BASE_GROUPS_H_\n"
  },
  {
    "path": "tachyon/math/base/groups_unittest.cc",
    "content": "#include \"tachyon/math/base/groups.h\"\n\n#include <string>\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/optional.h\"\n#include \"tachyon/base/strings/string_number_conversions.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n\nnamespace tachyon::math {\n\nTEST(GroupsTest, Div) {\n  class Int : public MultiplicativeGroup<Int> {\n   public:\n    Int() = default;\n    explicit Int(int value) : value_(value) {}\n    Int(const Int& other) : value_(other.value_) {}\n\n    MOCK_METHOD(Int, Mul, (const Int& other), (const));\n    MOCK_METHOD(Int, Inverse, (), (const));\n\n    bool operator==(const Int& other) const { return value_ == other.value_; }\n\n    std::string ToString() const { return base::NumberToString(value_); }\n\n   private:\n    int value_ = 0;\n  };\n\n  Int a(3);\n  Int b(4);\n  EXPECT_CALL(a, Mul(testing::_)).Times(testing::Exactly(1));\n  EXPECT_CALL(b, Inverse()).Times(testing::Exactly(1));\n\n  Int c = unwrap(a / b);\n  static_cast<void>(c);\n}\n\nTEST(GroupsTest, InverseOverride) {\n  class IntInverse {\n   public:\n    IntInverse() = default;\n    explicit IntInverse(int denominator) : denominator_(denominator) {}\n    IntInverse(const IntInverse& other) : denominator_(other.denominator_) {}\n\n    bool operator==(const IntInverse& other) const {\n      return denominator_ == other.denominator_;\n    }\n\n    std::string ToString() const { return base::NumberToString(denominator_); }\n\n   private:\n    int denominator_ = 0;\n  };\n\n  class Int : public MultiplicativeGroup<Int> {\n   public:\n    Int() = default;\n    explicit Int(int value) : value_(value) {}\n    Int(const Int& other) : value_(other.value_) {}\n\n    IntInverse Inverse() const { return IntInverse(value_); }\n\n    bool operator==(const Int& other) const { return value_ == other.value_; }\n\n   private:\n    int value_ = 0;\n  };\n\n  Int a(3);\n  EXPECT_EQ(a.Inverse(), IntInverse(3));\n}\n\nTEST(GroupsTest, BatchInverse) {\n  GF7::Init();\n#if defined(TACHYON_HAS_OPENMP)\n  size_t size = size_t{1} << (static_cast<size_t>(omp_get_max_threads()) /\n                              GF7::kParallelBatchInverseDivisorThreshold);\n#else\n  size_t size = 5;\n#endif\n  // GF7 is MultiplicativeGroup because it satisfies the conditions of Field.\n  std::vector<GF7> groups =\n      base::CreateVector(size, []() { return GF7::Random(); });\n  std::vector<GF7> inverses;\n  inverses.resize(groups.size());\n  ASSERT_TRUE(GF7::BatchInverse(groups, &inverses));\n  for (size_t i = 0; i < groups.size(); ++i) {\n    if (groups[i].IsZero()) {\n      EXPECT_TRUE(inverses[i].IsZero());\n    } else {\n      EXPECT_TRUE((inverses[i] * groups[i]).IsOne());\n    }\n  }\n\n  ASSERT_TRUE(GF7::BatchInverseInPlace(groups));\n  EXPECT_EQ(groups, inverses);\n}\n\nTEST(GroupsTest, Sub) {\n  class Int : public AdditiveGroup<Int> {\n   public:\n    Int() = default;\n    explicit Int(int value) : value_(value) {}\n    Int(const Int& other) : value_(other.value_) {}\n\n    MOCK_METHOD(Int, Add, (const Int& other), (const));\n    MOCK_METHOD(Int, Negate, (), (const));\n\n    bool operator==(const Int& other) const { return value_ == other.value_; }\n\n    std::string ToString() const { return base::NumberToString(value_); }\n\n   private:\n    int value_ = 0;\n  };\n\n  Int a(3);\n  Int b(4);\n  EXPECT_CALL(a, Add(testing::_)).Times(testing::Exactly(1));\n  EXPECT_CALL(b, Negate()).Times(testing::Exactly(1));\n\n  Int c = a - b;\n  static_cast<void>(c);\n}\n\nTEST(GroupsTest, SubOverAdd) {\n  class Int : public AdditiveGroup<Int> {\n   public:\n    Int() = default;\n    explicit Int(int value) : value_(value) {}\n    Int(const Int& other) : value_(other.value_) {}\n\n    MOCK_METHOD(Int, Sub, (const Int& other), (const));\n    MOCK_METHOD(Int, Add, (const Int& other), (const));\n    MOCK_METHOD(Int, Inverse, (), (const));\n\n    bool operator==(const Int& other) const { return value_ == other.value_; }\n\n    std::string ToString() const { return base::NumberToString(value_); }\n\n   private:\n    int value_ = 0;\n  };\n\n  Int a(3);\n  Int b(4);\n  EXPECT_CALL(a, Sub(b)).Times(testing::Exactly(1));\n  EXPECT_CALL(a, Add(testing::_)).Times(testing::Exactly(0));\n  EXPECT_CALL(b, Inverse()).Times(testing::Exactly(0));\n\n  Int c = a - b;\n  static_cast<void>(c);\n}\n\nTEST(GroupsTest, NegateOverride) {\n  class IntNegate {\n   public:\n    IntNegate() = default;\n    explicit IntNegate(int value) : value_(value) {}\n    IntNegate(const IntNegate& other) : value_(other.value_) {}\n\n    bool operator==(const IntNegate& other) const {\n      return value_ == other.value_;\n    }\n\n   private:\n    int value_ = 0;\n  };\n\n  class Int : public AdditiveGroup<Int> {\n   public:\n    Int() = default;\n    explicit Int(int value) : value_(value) {}\n    Int(const Int& other) : value_(other.value_) {}\n\n    IntNegate Negate() const { return IntNegate(-value_); }\n\n    bool operator==(const Int& other) const { return value_ == other.value_; }\n\n   private:\n    int value_ = 0;\n  };\n\n  Int a(3);\n  EXPECT_EQ(-a, IntNegate(-3));\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/base/parallelize_threshold.h",
    "content": "#ifndef TACHYON_MATH_BASE_PARALLELIZE_THRESHOLD_H_\n#define TACHYON_MATH_BASE_PARALLELIZE_THRESHOLD_H_\n\nnamespace tachyon::math {\n\nstruct ParallelizeThreshold {\n  // The threshold for parallelizing a loop. If the size of the loop is less\n  // than this threshold, the loop will be executed sequentially.\n  static constexpr int kFieldInit = 1e6;\n  static constexpr int kFieldSimpleOp = 1e5;\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_BASE_PARALLELIZE_THRESHOLD_H_\n"
  },
  {
    "path": "tachyon/math/base/rational_field.h",
    "content": "#ifndef TACHYON_MATH_BASE_RATIONAL_FIELD_H_\n#define TACHYON_MATH_BASE_RATIONAL_FIELD_H_\n\n#include <optional>\n#include <string>\n#include <utility>\n\n#include \"tachyon/base/optional.h\"\n#include \"tachyon/base/template_util.h\"\n#include \"tachyon/math/base/field.h\"\n\nnamespace tachyon::math {\n\n// RationalField class can be used to optimize performance by delaying an\n// expensive inverse computation. The motivation is very similar to why you use\n// JacobianPoint or others instead of AffinePoint.\ntemplate <typename F>\nclass RationalField : public Field<RationalField<F>> {\n public:\n  using InnerField = F;\n\n  constexpr RationalField() = default;\n  constexpr explicit RationalField(const F& numerator)\n      : numerator_(numerator) {}\n  constexpr explicit RationalField(F&& numerator)\n      : numerator_(std::move(numerator)) {}\n  constexpr RationalField(const F& numerator, const F& denominator)\n      : numerator_(numerator), denominator_(denominator) {}\n  constexpr RationalField(F&& numerator, F&& denominator)\n      : numerator_(std::move(numerator)),\n        denominator_(std::move(denominator)) {}\n\n  constexpr static RationalField Zero() { return RationalField(F::Zero()); }\n\n  constexpr static RationalField One() { return RationalField(F::One()); }\n\n  constexpr static RationalField MinusOne() {\n    return RationalField(F::MinusOne());\n  }\n\n  constexpr static RationalField TwoInv() { return RationalField(F::TwoInv()); }\n\n  constexpr static RationalField Random() {\n    F denominator = F::Random();\n    while (denominator.IsZero()) {\n      denominator = F::Random();\n    }\n    return {F::Random(), std::move(denominator)};\n  }\n\n  template <typename InputContainer, typename OutputContainer>\n  constexpr static bool BatchEvaluate(const InputContainer& ration_fields,\n                                      OutputContainer* results,\n                                      const F& coeff = F::One()) {\n    static_assert(\n        std::is_same_v<base::container_value_t<InputContainer>, RationalField>);\n    static_assert(std::is_same_v<base::container_value_t<OutputContainer>, F>);\n\n    if (std::size(ration_fields) != std::size(*results)) {\n      LOG(ERROR) << \"Size of |ration_fields| and |results| do not match\";\n      return false;\n    }\n    base::Parallelize(*results,\n                      [&ration_fields](absl::Span<F> chunk, size_t chunk_offset,\n                                       size_t chunk_size) {\n                        size_t start = chunk_offset * chunk_size;\n                        for (size_t i = 0; i < chunk.size(); ++i) {\n                          chunk[i] = ration_fields[start + i].denominator_;\n                        }\n                        CHECK(F::BatchInverseInPlaceSerial(chunk));\n                        for (size_t i = 0; i < chunk.size(); ++i) {\n                          chunk[i] *= ration_fields[start + i].numerator_;\n                        }\n                      });\n    return true;\n  }\n\n  constexpr const F& numerator() const { return numerator_; }\n  constexpr const F& denominator() const { return denominator_; }\n\n  constexpr bool IsZero() const { return numerator_.IsZero(); }\n\n  constexpr bool IsOne() const {\n    return numerator_.IsOne() || numerator_ == denominator_;\n  }\n\n  constexpr bool IsMinusOne() const {\n    return numerator_.IsMinusOne() || (numerator_ + denominator_).IsZero();\n  }\n\n  std::string ToString() const {\n    return absl::Substitute(\"$0 / $1\", numerator_.ToString(),\n                            denominator_.ToString());\n  }\n\n  constexpr bool operator==(const RationalField& other) const {\n    return numerator_ * other.denominator_ == other.numerator_ * denominator_;\n  }\n  constexpr bool operator!=(const RationalField& other) const {\n    return numerator_ * other.denominator_ != other.numerator_ * denominator_;\n  }\n  constexpr bool operator<(const RationalField& other) const {\n    return numerator_ * other.denominator_ < other.numerator_ * denominator_;\n  }\n  constexpr bool operator>(const RationalField& other) const {\n    return numerator_ * other.denominator_ > other.numerator_ * denominator_;\n  }\n  constexpr bool operator<=(const RationalField& other) const {\n    return numerator_ * other.denominator_ <= other.numerator_ * denominator_;\n  }\n  constexpr bool operator>=(const RationalField& other) const {\n    return numerator_ * other.denominator_ >= other.numerator_ * denominator_;\n  }\n\n  F Evaluate() const { return unwrap(numerator_ / denominator_); }\n\n  // AdditiveSemigroup methods\n  constexpr RationalField Add(const RationalField& other) const {\n    return {numerator_ * other.denominator_ + other.numerator_ * denominator_,\n            denominator_ * other.denominator_};\n  }\n\n  constexpr RationalField& AddInPlace(const RationalField& other) {\n    numerator_ =\n        numerator_ * other.denominator_ + other.numerator_ * denominator_;\n    denominator_ *= other.denominator_;\n    return *this;\n  }\n\n  constexpr RationalField DoubleImpl() const {\n    return {numerator_.Double(), denominator_};\n  }\n\n  constexpr RationalField& DoubleImplInPlace() {\n    numerator_.DoubleInPlace();\n    return *this;\n  }\n\n  // AdditiveGroup methods\n  constexpr RationalField Sub(const RationalField& other) const {\n    return {numerator_ * other.denominator_ - other.numerator_ * denominator_,\n            denominator_ * other.denominator_};\n  }\n\n  constexpr RationalField& SubInPlace(const RationalField& other) {\n    numerator_ =\n        numerator_ * other.denominator_ - other.numerator_ * denominator_;\n    denominator_ *= other.denominator_;\n    return *this;\n  }\n\n  constexpr RationalField Negate() const { return {-numerator_, denominator_}; }\n\n  constexpr RationalField& NegateInPlace() {\n    numerator_.NegateInPlace();\n    return *this;\n  }\n\n  // MultiplicativeSemigroup methods\n  constexpr RationalField Mul(const RationalField& other) const {\n    return {numerator_ * other.numerator_, denominator_ * other.denominator_};\n  }\n\n  constexpr RationalField& MulInPlace(const RationalField& other) {\n    numerator_ *= other.numerator_;\n    denominator_ *= other.denominator_;\n    return *this;\n  }\n\n  constexpr RationalField SquareImpl() const {\n    return {numerator_.Square(), denominator_.Square()};\n  }\n\n  constexpr RationalField& SquareImplInPlace() {\n    numerator_.SquareInPlace();\n    denominator_.SquareInPlace();\n    return *this;\n  }\n\n  // MultiplicativeGroup methods\n  constexpr std::optional<RationalField> Inverse() const {\n    if (UNLIKELY(IsZero())) {\n      LOG_IF_NOT_GPU(ERROR) << \"Inverse of zero attempted\";\n      return std::nullopt;\n    }\n    return RationalField(denominator_, numerator_);\n  }\n\n  [[nodiscard]] constexpr std::optional<RationalField*> InverseInPlace() {\n    if (UNLIKELY(IsZero())) {\n      LOG_IF_NOT_GPU(ERROR) << \"Inverse of zero attempted\";\n      return std::nullopt;\n    }\n    std::swap(numerator_, denominator_);\n    return this;\n  }\n\n private:\n  F numerator_;\n  F denominator_ = F::One();\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_BASE_RATIONAL_FIELD_H_\n"
  },
  {
    "path": "tachyon/math/base/rational_field_unittest.cc",
    "content": "#include \"tachyon/math/base/rational_field.h\"\n\n#include <optional>\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n\nnamespace tachyon::math {\n\nusing R = RationalField<GF7>;\n\nnamespace {\n\nclass RationalFieldTest : public FiniteFieldTest<GF7> {};\n\n}  // namespace\n\nTEST_F(RationalFieldTest, Zero) {\n  EXPECT_TRUE(R::Zero().IsZero());\n  EXPECT_FALSE(R::One().IsZero());\n  EXPECT_FALSE(R::MinusOne().IsZero());\n}\n\nTEST_F(RationalFieldTest, One) {\n  EXPECT_TRUE(R::One().IsOne());\n  EXPECT_FALSE(R::Zero().IsOne());\n  EXPECT_FALSE(R::MinusOne().IsOne());\n  EXPECT_TRUE(R(GF7(3), GF7(3)).IsOne());\n}\n\nTEST_F(RationalFieldTest, MinusOne) {\n  EXPECT_TRUE(R::MinusOne().IsMinusOne());\n  EXPECT_FALSE(R::Zero().IsMinusOne());\n  EXPECT_FALSE(R::One().IsMinusOne());\n  EXPECT_TRUE(R(GF7(4), GF7(3)).IsMinusOne());\n}\n\nTEST_F(RationalFieldTest, TwoInv) {\n  EXPECT_TRUE((R::TwoInv() * R(GF7(2), GF7::One())).IsOne());\n  EXPECT_FALSE((R::TwoInv() * R::One()).IsOne());\n}\n\nTEST_F(RationalFieldTest, Random) {\n  bool success = false;\n  R r = R::Random();\n  for (size_t i = 0; i < 100; ++i) {\n    if (r != R::Random()) {\n      success = true;\n      break;\n    }\n  }\n  EXPECT_TRUE(success);\n}\n\nTEST_F(RationalFieldTest, NumeratorOnlyComparison) {\n  GF7 a_numerator = GF7::Random();\n  GF7 b_numerator = GF7::Random();\n  R a(a_numerator);\n  R b(b_numerator);\n\n#define COMPARISON_TEST(op)           \\\n  do {                                \\\n    if (a_numerator op b_numerator) { \\\n      EXPECT_TRUE(a op b);            \\\n    }                                 \\\n  } while (false)\n\n  COMPARISON_TEST(==);\n  COMPARISON_TEST(!=);\n  COMPARISON_TEST(<);  // NOLINT(whitespace/operators)\n  COMPARISON_TEST(<=);\n  COMPARISON_TEST(>);  // NOLINT(whitespace/operators)\n  COMPARISON_TEST(>=);\n\n#undef COMPARISON_TEST\n}\n\nTEST_F(RationalFieldTest, Comparison) {\n  R a = R::Random();\n  R b = R::Random();\n\n#define COMPARISON_TEST(op)                        \\\n  do {                                             \\\n    if ((a.numerator() * b.denominator())          \\\n            op(b.numerator() * a.denominator())) { \\\n      EXPECT_TRUE(a op b);                         \\\n    }                                              \\\n  } while (false)\n\n  COMPARISON_TEST(==);\n  COMPARISON_TEST(!=);\n  COMPARISON_TEST(<);  // NOLINT(whitespace/operators)\n  COMPARISON_TEST(<=);\n  COMPARISON_TEST(>);  // NOLINT(whitespace/operators)\n  COMPARISON_TEST(>=);\n\n#undef COMPARISON_TEST\n}\n\nTEST_F(RationalFieldTest, MpzClassConversion) {\n  R a = R::Random();\n  EXPECT_EQ(a.Evaluate(), a.numerator() / a.denominator());\n}\n\nTEST_F(RationalFieldTest, AdditiveOperators) {\n  struct {\n    R a;\n    R b;\n    R sum;\n    R amb;\n    R bma;\n  } tests[] = {\n      {R(GF7(3), GF7(2)), R(GF7(2), GF7(5)), R(GF7(5), GF7(3)),\n       R(GF7(4), GF7(3)), R(GF7(3), GF7(3))},\n      {R(GF7(5), GF7(3)), R(GF7(1), GF7(2)), R(GF7(6), GF7(6)),\n       R(GF7(0), GF7(6)), R(GF7(0), GF7(6))},\n  };\n\n  for (const auto& test : tests) {\n    EXPECT_EQ(test.a + test.b, test.sum);\n    EXPECT_EQ(test.b + test.a, test.sum);\n    EXPECT_EQ(test.a - test.b, test.amb);\n    EXPECT_EQ(test.b - test.a, test.bma);\n\n    R tmp = test.a;\n    tmp += test.b;\n    EXPECT_EQ(tmp, test.sum);\n    tmp -= test.b;\n    EXPECT_EQ(tmp, test.a);\n  }\n}\n\nTEST_F(RationalFieldTest, AdditiveGroupOperators) {\n  R r(GF7(3), GF7(2));\n  R neg_expected(GF7(4), GF7(2));\n  EXPECT_EQ(-r, neg_expected);\n  r.NegateInPlace();\n  EXPECT_EQ(r, neg_expected);\n\n  r = R(GF7(3), GF7(2));\n  R dbl_expected(GF7(6), GF7(2));\n  EXPECT_EQ(r.Double(), dbl_expected);\n  r.DoubleInPlace();\n  EXPECT_EQ(r, dbl_expected);\n}\n\nTEST_F(RationalFieldTest, MultiplicativeOperators) {\n  struct {\n    R a;\n    R b;\n    R mul;\n    R adb;\n    R bda;\n  } tests[] = {\n      {R(GF7(3), GF7(2)), R(GF7(2), GF7(5)), R(GF7(6), GF7(3)),\n       R(GF7(1), GF7(4)), R(GF7(4), GF7(1))},\n      {R(GF7(5), GF7(3)), R(GF7(1), GF7(2)), R(GF7(5), GF7(6)),\n       R(GF7(3), GF7(3)), R(GF7(3), GF7(3))},\n  };\n\n  for (const auto& test : tests) {\n    EXPECT_EQ(test.a * test.b, test.mul);\n    EXPECT_EQ(test.b * test.a, test.mul);\n    EXPECT_EQ(test.a / test.b, test.adb);\n    EXPECT_EQ(test.b / test.a, test.bda);\n\n    R tmp = test.a;\n    tmp *= test.b;\n    EXPECT_EQ(tmp, test.mul);\n    ASSERT_TRUE(tmp /= test.b);\n    EXPECT_EQ(tmp, test.a);\n  }\n}\n\nTEST_F(RationalFieldTest, MultiplicativeGroupOperators) {\n  R r = R::Random();\n  std::optional<R> r_inv = r.Inverse();\n  if (UNLIKELY(r.IsZero())) {\n    ASSERT_FALSE(r_inv);\n    ASSERT_FALSE(r.InverseInPlace());\n  } else {\n    EXPECT_TRUE((r * *r_inv).IsOne());\n    R r_tmp = r;\n    EXPECT_TRUE((**r.InverseInPlace() * r_tmp).IsOne());\n  }\n\n  r = R(GF7(3), GF7(2));\n  R expected = R(GF7(2), GF7(4));\n  EXPECT_EQ(r.Square(), expected);\n  r.SquareInPlace();\n  EXPECT_EQ(r, expected);\n\n  r = R(GF7(3), GF7(2));\n  EXPECT_EQ(r.Pow(5), R(GF7(5), GF7(4)));\n}\n\nTEST_F(RationalFieldTest, BatchEvaluate) {\n  size_t size = 100;\n  std::vector<R> test_set =\n      base::CreateVector(size, []() { return R::Random(); });\n  std::vector<GF7> results;\n  results.resize(100);\n  ASSERT_TRUE(R::BatchEvaluate(test_set, &results));\n  for (size_t i = 0; i < results.size(); ++i) {\n    EXPECT_EQ(test_set[i].Evaluate(), results[i]);\n  }\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/base/ring.h",
    "content": "#ifndef TACHYON_MATH_BASE_RING_H_\n#define TACHYON_MATH_BASE_RING_H_\n\n#include <type_traits>\n#include <vector>\n\n#include \"tachyon/base/parallelize.h\"\n#include \"tachyon/math/base/groups.h\"\n\nnamespace tachyon::math {\n\n// Ring is a set S with operations + and * that satisfies the followings:\n// 1. Additive associativity: (a + b) + c = a + (b + c)\n// 2. Additive commutativity: a + b = b + a\n// 3. Additive identity: a + 0 = 0 + a = a\n// 4. Additive inverse: a + (-a) = (-a) + a = 0\n// 5. Distributivity: a * (b + c) = (a * b) + (a * c)\n// 6. Multiplicative associativity: (a * b) * c = a * (b * c)\n// See https://mathworld.wolfram.com/Ring.html\n\n// The Ring supports SumOfProducts, inheriting the properties of both\n// AdditiveGroup and MultiplicativeSemigroup.\ntemplate <typename R>\nclass Ring : public AdditiveGroup<R>, public MultiplicativeSemigroup<R> {\n public:\n  // This is taken and modified from\n  // https://github.com/arkworks-rs/algebra/blob/5dfeedf/ff/src/fields/mod.rs#L298C1-L305\n  // Sum of products: a₁ * b₁ + a₂ * b₂ + ... + aₙ * bₙ\n  // TODO(chokobole): If I call |SumOfProducts()| instead of\n  // |SumOfProductsSerial| for all call sites, it gets stuck when doing\n  // unittests. I think we need a some general threshold to check whether it is\n  // good to doing parallelization.\n  template <typename ContainerA, typename ContainerB>\n  constexpr static R SumOfProducts(const ContainerA& a, const ContainerB& b) {\n    size_t size = std::size(a);\n    CHECK_EQ(size, std::size(b));\n    if (size == 0) return R::Zero();\n    std::vector<R> partial_sum_of_products = base::ParallelizeMap(\n        a,\n        [&b](absl::Span<const R> chunk, size_t chunk_idx, size_t chunk_size) {\n          R sum = R::Zero();\n          size_t i = chunk_idx * chunk_size;\n          for (size_t j = 0; j < chunk.size(); ++j) {\n            sum += (chunk[j] * b[i + j]);\n          }\n          return sum;\n        });\n    return std::accumulate(partial_sum_of_products.begin(),\n                           partial_sum_of_products.end(), R::Zero(),\n                           [](R& acc, const R& partial_sum_of_product) {\n                             return acc += partial_sum_of_product;\n                           });\n  }\n\n  // Sum of products: a₁ * b₁ + a₂ * b₂ + ... + aₙ * bₙ\n  template <typename ContainerA, typename ContainerB>\n  constexpr static R SumOfProductsSerial(const ContainerA& a,\n                                         const ContainerB& b) {\n    size_t size = std::size(a);\n    CHECK_EQ(size, std::size(b));\n    if (size == 0) return R::Zero();\n    return DoSumOfProductsSerial(a, b);\n  }\n\n private:\n  template <typename ContainerA, typename ContainerB>\n  constexpr static R DoSumOfProductsSerial(const ContainerA& a,\n                                           const ContainerB& b) {\n    size_t n = std::size(a);\n    R sum = R::Zero();\n    for (size_t i = 0; i < n; ++i) {\n      sum += (a[i] * b[i]);\n    }\n    return sum;\n  }\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_BASE_RING_H_\n"
  },
  {
    "path": "tachyon/math/base/semigroups.h",
    "content": "#ifndef TACHYON_MATH_BASE_SEMIGROUPS_H_\n#define TACHYON_MATH_BASE_SEMIGROUPS_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <algorithm>\n#include <utility>\n#include <vector>\n\n#include \"absl/types/span.h\"\n\n#include \"tachyon/base/bits.h\"\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/parallelize.h\"\n#include \"tachyon/base/types/always_false.h\"\n#include \"tachyon/math/base/big_int.h\"\n#include \"tachyon/math/base/bit_iterator.h\"\n\n#define SUPPORTS_BINARY_OPERATOR(Name)                                        \\\n  template <typename L, typename R, typename = void>                          \\\n  struct Supports##Name : std::false_type {};                                 \\\n                                                                              \\\n  template <typename L, typename R>                                           \\\n  struct Supports##Name<                                                      \\\n      L, R, decltype(void(std::declval<L>().Name(std::declval<const R&>())))> \\\n      : std::true_type {};                                                    \\\n                                                                              \\\n  template <typename L, typename R, typename = void>                          \\\n  struct Supports##Name##InPlace : std::false_type {};                        \\\n                                                                              \\\n  template <typename L, typename R>                                           \\\n  struct Supports##Name##InPlace<L, R,                                        \\\n                                 decltype(void(                               \\\n                                     std::declval<L>().Name##InPlace(         \\\n                                         std::declval<const R&>())))>         \\\n      : std::true_type {}\n\n#define SUPPORTS_UNARY_OPERATOR(Name)                                        \\\n  template <typename T, typename = void>                                     \\\n  struct Supports##Name : std::false_type {};                                \\\n                                                                             \\\n  template <typename T>                                                      \\\n  struct Supports##Name<T, decltype(void(std::declval<T>().Name()))>         \\\n      : std::true_type {};                                                   \\\n                                                                             \\\n  template <typename T, typename = void>                                     \\\n  struct Supports##Name##InPlace : std::false_type {};                       \\\n                                                                             \\\n  template <typename T>                                                      \\\n  struct Supports##Name##InPlace<T, decltype(void(                           \\\n                                        std::declval<T>().Name##InPlace()))> \\\n      : std::true_type {}\n\n#define SUPPORTS_DEDICATED_EXP_OPERATOR(Pow)                               \\\n  template <typename T, typename = void>                                   \\\n  struct SupportsExp##Pow : std::false_type {};                            \\\n                                                                           \\\n  template <typename T>                                                    \\\n  struct SupportsExp##Pow<T, decltype(void(std::declval<T>().Exp##Pow()))> \\\n      : std::true_type {};\n\nnamespace tachyon::math {\nnamespace internal {\n\nSUPPORTS_BINARY_OPERATOR(Mul);\nSUPPORTS_UNARY_OPERATOR(SquareImpl);\nSUPPORTS_BINARY_OPERATOR(Add);\nSUPPORTS_UNARY_OPERATOR(DoubleImpl);\nSUPPORTS_DEDICATED_EXP_OPERATOR(3);\nSUPPORTS_DEDICATED_EXP_OPERATOR(5);\nSUPPORTS_DEDICATED_EXP_OPERATOR(7);\n\ntemplate <typename T, typename = void>\nstruct SupportsSize : std::false_type {};\n\ntemplate <typename T>\nstruct SupportsSize<T, decltype(void(std::size(std::declval<T>())))>\n    : std::true_type {};\n\ntemplate <typename G>\nstruct MultiplicativeSemigroupTraits {\n  using ReturnTy = G;\n};\n\ntemplate <typename G, typename SFINAE = void>\nstruct AdditiveSemigroupTraits {\n  using ReturnTy = G;\n};\n\n}  // namespace internal\n\n// Semigroup <S, ○> is a mathematical object defined for a set 'S' and a binary\n// operator '○' in which the operation is associative.\n// Associativity: (a ○ b) ○ c = a ○ (b ○ c)\n// See https://mathworld.wolfram.com/Semigroup.html\n\n// MultiplicativeSemigroup is a semigroup with a multiplicative operator.\ntemplate <typename G>\nclass MultiplicativeSemigroup {\n public:\n  using MulResult =\n      typename internal::MultiplicativeSemigroupTraits<G>::ReturnTy;\n\n  // Multiplication: a * b\n  template <typename G2,\n            std::enable_if_t<internal::SupportsMul<G, G2>::value>* = nullptr>\n  constexpr auto operator*(const G2& other) const {\n    const G* g = static_cast<const G*>(this);\n    return g->Mul(other);\n  }\n\n  // Multiplication in place: a *= b\n  template <\n      typename G2,\n      std::enable_if_t<internal::SupportsMulInPlace<G, G2>::value>* = nullptr>\n  constexpr G& operator*=(const G2& other) {\n    G* g = static_cast<G*>(this);\n    return g->MulInPlace(other);\n  }\n\n  // a.Square(): a²\n  [[nodiscard]] constexpr auto Square() const {\n    if constexpr (internal::SupportsSquareImpl<G>::value) {\n      const G* g = static_cast<const G*>(this);\n      return g->SquareImpl();\n    } else {\n      return operator*(static_cast<const G&>(*this));\n    }\n  }\n\n  // a.SquareInPlace(): a = a²\n  constexpr G& SquareInPlace() {\n    if constexpr (internal::SupportsSquareImplInPlace<G>::value) {\n      G* g = static_cast<G*>(this);\n      return g->SquareImplInPlace();\n    } else if constexpr (internal::SupportsMulInPlace<G, G>::value) {\n      return operator*=(static_cast<const G&>(*this));\n    } else {\n      static_assert(base::AlwaysFalse<G>);\n    }\n  }\n\n  // a.Pow(e): aᵉ\n  // Square it as much as possible and multiply the remainder.\n  // ex) a¹³ = (((a)² * a)²)² * a\n  template <size_t N>\n  [[nodiscard]] constexpr auto Pow(const BigInt<N>& exponent) const {\n    return DoPow(exponent);\n  }\n\n  template <typename Scalar>\n  [[nodiscard]] constexpr auto Pow(const Scalar& scalar) const {\n    if constexpr (std::is_constructible_v<BigInt<1>, Scalar>) {\n      return DoPow(BigInt<1>(scalar));\n    } else {\n      return DoPow(scalar.ToBigInt());\n    }\n  }\n\n  template <uint32_t Power>\n  [[nodiscard]] constexpr auto ConstPow() const {\n    const G& g = static_cast<const G&>(*this);\n    if constexpr (Power == 0)\n      return MulResult::One();\n    else if constexpr (Power == 1)\n      return g;\n    else if constexpr (Power == 2)\n      return Square();\n    else if constexpr (Power == 3) {\n      if constexpr (internal::SupportsExp3<G>::value) {\n        return g.Exp3();\n      } else {\n        return Square() * g;\n      }\n    } else if constexpr (Power == 4) {\n      return Square().Square();\n    } else if constexpr (Power == 5) {\n      if constexpr (internal::SupportsExp5<G>::value) {\n        return g.Exp5();\n      } else {\n        MulResult g4 = Square();\n        g4.SquareInPlace();\n        return g4 * g;\n      }\n    } else if constexpr (Power == 6) {\n      MulResult g2 = Square();\n      MulResult g4 = g2;\n      g4.SquareInPlace();\n      return g4 * g2;\n    } else if constexpr (Power == 7) {\n      if constexpr (internal::SupportsExp7<G>::value) {\n        return g.Exp7();\n      } else {\n        MulResult g2 = Square();\n        MulResult g4 = g2;\n        g4.SquareInPlace();\n        return g4 * g2 * g;\n      }\n    } else {\n      return DoPow(BigInt<1>(Power));\n    }\n  }\n\n  // Computes the power of a base element using a pre-computed table of powers\n  // of two, instead of performing repeated multiplications.\n  template <size_t N>\n  static MulResult PowWithTable(absl::Span<const G> powers_of_2,\n                                const BigInt<N>& exponent) {\n    auto it = BitIteratorLE<BigInt<N>>::begin(&exponent);\n    auto end = BitIteratorLE<BigInt<N>>::end(&exponent, true);\n    MulResult g = MulResult::One();\n    size_t i = 0;\n    while (it != end) {\n      if (*it) {\n        g *= powers_of_2[i];\n      }\n      ++it;\n      ++i;\n    }\n    return g;\n  }\n\n  // generator: g\n  // return: [c, c * g, c * g², ..., c * g^{|size| - 1}]\n  constexpr static std::vector<MulResult> GetSuccessivePowers(\n      size_t size, const G& generator, const G& c = G::One()) {\n    std::vector<MulResult> ret(size);\n    base::Parallelize(\n        ret, [&generator, &c](absl::Span<G> chunk, size_t chunk_offset,\n                              size_t chunk_size) {\n          MulResult pow = generator.Pow(chunk_offset * chunk_size);\n          if (!c.IsOne()) pow *= c;\n\n          // NOTE: It is not possible to have empty chunk so this is safe\n          for (size_t i = 0; i < chunk.size() - 1; ++i) {\n            chunk[i] = pow;\n            pow *= generator;\n          }\n          chunk.back() = std::move(pow);\n        });\n    return ret;\n  }\n\n  // Refer to |GetSuccessivePowers()| for basic approach.\n  // Populates bit-reversed index instead of given index in a parallelized way.\n  constexpr static std::vector<MulResult> GetBitRevIndexSuccessivePowers(\n      size_t size, const G& generator, const G& c = G::One()) {\n    std::vector<MulResult> ret(size);\n    uint32_t log_size = base::bits::CheckedLog2(size);\n    base::Parallelize(\n        ret, [log_size, &generator, &c, &ret](\n                 absl::Span<G> chunk, size_t chunk_offset, size_t chunk_size) {\n          size_t chunk_start = chunk_offset * chunk_size;\n          MulResult pow = generator.Pow(chunk_start);\n          if (!c.IsOne()) pow *= c;\n          for (size_t idx = chunk_start; idx < chunk_start + chunk.size() - 1;\n               ++idx) {\n            size_t ridx = base::bits::ReverseBitsLen(idx, log_size);\n            ret[ridx] = pow;\n            pow *= generator;\n          }\n          ret[base::bits::ReverseBitsLen(chunk_start + chunk.size() - 1,\n                                         log_size)] = std::move(pow);\n        });\n    return ret;\n  }\n\n  // Refer to |GetSuccessivePowers()| for basic approach.\n  // Populates bit-reversed index instead of given index in a serial way.\n  constexpr static std::vector<MulResult> GetBitRevIndexSuccessivePowersSerial(\n      size_t size, const G& generator, const G& c = G::One()) {\n    std::vector<MulResult> ret(size);\n    uint32_t log_size = base::bits::CheckedLog2(size);\n    MulResult pow = c;\n    for (size_t idx = 0; idx < size - 1; ++idx) {\n      ret[base::bits::ReverseBitsLen(idx, log_size)] = pow;\n      pow *= generator;\n    }\n    ret[base::bits::ReverseBitsLen(size - 1, log_size)] = std::move(pow);\n    return ret;\n  }\n\n  constexpr auto ExpPowOfTwo(uint32_t log_n) const {\n    G val = *static_cast<const G*>(this);\n    for (size_t i = 0; i < log_n; ++i) {\n      val.SquareInPlace();\n    }\n    return val;\n  }\n\n private:\n  constexpr static size_t kDefaultParallelThreshold = 1024;\n\n  template <size_t N>\n  [[nodiscard]] constexpr MulResult DoPow(const BigInt<N>& exponent) const {\n    const G* g = static_cast<const G*>(this);\n    MulResult ret = MulResult::One();\n    auto it = BitIteratorBE<BigInt<N>>::begin(&exponent, true);\n    auto end = BitIteratorBE<BigInt<N>>::end(&exponent);\n    while (it != end) {\n      if constexpr (internal::SupportsSquareImplInPlace<G>::value ||\n                    internal::SupportsMulInPlace<G, G>::value) {\n        ret.SquareInPlace();\n      } else {\n        ret = ret.Square();\n      }\n      if (*it) {\n        if constexpr (internal::SupportsMulInPlace<MulResult, G>::value) {\n          ret.MulInPlace(*g);\n        } else {\n          ret = ret.Mul(*g);\n        }\n      }\n      ++it;\n    }\n    return ret;\n  }\n};\n\n// AdditiveSemigroup is a semigroup with an additive operator.\ntemplate <typename G>\nclass AdditiveSemigroup {\n public:\n  using AddResult = typename internal::AdditiveSemigroupTraits<G>::ReturnTy;\n\n  // Addition: a + b\n  template <typename G2,\n            std::enable_if_t<internal::SupportsAdd<G, G2>::value>* = nullptr>\n  constexpr auto operator+(const G2& other) const {\n    const G* g = static_cast<const G*>(this);\n    return g->Add(other);\n  }\n\n  // Addition in place: a += b\n  template <\n      typename G2,\n      std::enable_if_t<internal::SupportsAddInPlace<G, G2>::value>* = nullptr>\n  constexpr G& operator+=(const G2& other) {\n    G* g = static_cast<G*>(this);\n    return g->AddInPlace(other);\n  }\n\n  // a.Double(): 2a\n  [[nodiscard]] constexpr auto Double() const {\n    if constexpr (internal::SupportsDoubleImpl<G>::value) {\n      const G* g = static_cast<const G*>(this);\n      return g->DoubleImpl();\n    } else {\n      return operator+(static_cast<const G&>(*this));\n    }\n  }\n\n  // a.DoubleInPlace(): a = 2a\n  constexpr G& DoubleInPlace() {\n    if constexpr (internal::SupportsDoubleImplInPlace<G>::value) {\n      G* g = static_cast<G*>(this);\n      return g->DoubleImplInPlace();\n    } else if constexpr (internal::SupportsAddInPlace<G, G>::value) {\n      return operator+=(static_cast<const G&>(*this));\n    } else {\n      static_assert(base::AlwaysFalse<G>);\n    }\n  }\n\n  // FIXME(chokobole): It would be nice to support multiplication operator\n  // when multiplying scalar. But in g++\n  // (Ubuntu 11.3.0-1ubuntu1~22.04.1) 11.3.0, the commented line isn't compiled\n  // with this error.\n  // \"error: request for member 'operator*' is ambiguous\".\n  // constexpr auto operator*(const BigInt<N>& scalar) const {\n  template <size_t N>\n  [[nodiscard]] constexpr auto ScalarMul(const BigInt<N>& scalar) const {\n    return DoScalarMul(scalar);\n  }\n\n  template <typename Scalar>\n  [[nodiscard]] constexpr auto ScalarMul(const Scalar& scalar) const {\n    if constexpr (std::is_constructible_v<BigInt<1>, Scalar>) {\n      return DoScalarMul(BigInt<1>(scalar));\n    } else {\n      return DoScalarMul(scalar.ToBigInt());\n    }\n  }\n\n  template <uint64_t Scalar>\n  [[nodiscard]] constexpr auto ConstScalarMul() const {\n    const G& g = static_cast<const G&>(*this);\n    if constexpr (Scalar == 0)\n      return AddResult::Zero();\n    else if constexpr (Scalar == 1)\n      return g;\n    else if constexpr (Scalar == 2)\n      return Double();\n    else if constexpr (Scalar == 3)\n      return Double() + g;\n    else if constexpr (Scalar == 4)\n      return Double().Double();\n    else if constexpr (Scalar == 5) {\n      AddResult g4 = Double();\n      g4.DoubleInPlace();\n      return g4 + g;\n    } else if constexpr (Scalar == 6) {\n      AddResult g2 = Double();\n      AddResult g4 = g2;\n      g4.DoubleInPlace();\n      return g4 + g2;\n    } else if constexpr (Scalar == 7) {\n      AddResult g2 = Double();\n      AddResult g4 = g2;\n      g4.DoubleInPlace();\n      return g4 + g2 + g;\n    } else {\n      return DoScalarMul(BigInt<1>(Scalar));\n    }\n  }\n\n  // Return false if the size of function arguments are not matched.\n  // This supports 3 cases.\n  //\n  //   - Multi Scalar Multi Base\n  //     scalars: [s₀, s₁, ..., sₙ₋₁]\n  //     bases: [G₀, G₁, ..., Gₙ₋₁]\n  //     outputs: [s₀G₀, s₁G₁, ..., sₙ₋₁Gₙ₋₁]\n  //\n  //   - Multi Scalar Single Base\n  //     scalars: [s₀, s₁, ..., sₙ₋₁]\n  //     base: G\n  //     outputs: [s₀G, s₁G, ..., sₙ₋₁G]\n  //\n  //   - Single Scalar Multi Base\n  //     scalar: s\n  //     bases: [G₀, G₁, ..., Gₙ₋₁]\n  //     outputs: [sG₀, sG₁, ..., sGₙ₋₁]\n  template <typename ScalarOrScalars, typename BaseOrBases,\n            typename OutputContainer>\n  [[nodiscard]] constexpr static bool MultiScalarMul(\n      const ScalarOrScalars& scalar_or_scalars,\n      const BaseOrBases& base_or_bases, OutputContainer* outputs) {\n    if constexpr (internal::SupportsSize<ScalarOrScalars>::value &&\n                  internal::SupportsSize<BaseOrBases>::value) {\n      return MultiScalarMulMSMB(scalar_or_scalars, base_or_bases, outputs);\n    } else if constexpr (internal::SupportsSize<ScalarOrScalars>::value) {\n      return MultiScalarMulMSSB(scalar_or_scalars, base_or_bases, outputs);\n    } else {\n      return MultiScalarMulSSMB(scalar_or_scalars, base_or_bases, outputs);\n    }\n  }\n\n  // generator: G\n  // return: [0G, 1G, 2G, ..., (|size| - 1)G]\n  // NOTE(chokobole): Unlike |GetSuccessivePowers()|, this doesn't have an\n  // additional |c| parameter because there's no usecase that depends on\n  // it.\n  constexpr static std::vector<AddResult> GetSuccessiveScalarMuls(\n      size_t size, const G& generator) {\n    std::vector<AddResult> ret(size);\n    base::Parallelize(\n        ret, [&generator](absl::Span<G> chunk, size_t chunk_offset,\n                          size_t chunk_size) {\n          AddResult scalar_mul = generator.ScalarMul(chunk_offset * chunk_size);\n\n          // NOTE: It is not possible to have empty chunk so this is safe\n          for (size_t i = 0; i < chunk.size() - 1; ++i) {\n            chunk[i] = scalar_mul;\n            scalar_mul += generator;\n          }\n          chunk.back() = std::move(scalar_mul);\n        });\n    return ret;\n  }\n\n  // Linear combination\n  // - forward: a₀ * rⁿ⁻¹ + a₁ * rⁿ⁻² + ... + aₙ₋₁\n  // - backward: a₀ + a₁ * r + ... + aₙ₋₁ * rⁿ⁻¹\n  // NOTE(chokobole): For performance reasons, we recommend using\n  // |LinearCombinationInPlace()| if possible.\n  template <bool Forward, typename Container, typename T>\n  constexpr static AddResult LinearCombination(const Container& values,\n                                               const T& r) {\n    size_t size = std::size(values);\n    AddResult ret = AddResult::Zero();\n    if constexpr (Forward) {\n      for (size_t i = 0; i < size; ++i) {\n        ret *= r;\n        ret += values[i];\n      }\n    } else {\n      for (size_t i = size - 1; i != SIZE_MAX; --i) {\n        ret *= r;\n        ret += values[i];\n      }\n    }\n    return ret;\n  }\n\n  // Linear combination\n  // - forward: a₀ * rⁿ⁻¹ + a₁ * rⁿ⁻² + ... + aₙ₋₁\n  // - backward: a₀ + a₁ * r + ... + aₙ₋₁ * rⁿ⁻¹\n  // NOTE(chokobole): This gives more performant result than\n  // |LinearCombination()|.\n  //\n  // When using |LinearCombination()|, you can linearize groups as follows:\n  //\n  //   const std::vector<G> groups = {...};\n  //   G ret = G::LinearCombination(groups, G::Random());\n  //\n  // When using |LinearCombinationInPlace()|, you can save additional allocation\n  // cost.\n  //\n  //   // Note that |groups| are going to be changed.\n  //   std::vector<G> groups = {...};\n  //   G& ret = G::LinearCombinationInPlace(groups, G::Random());\n  template <bool Forward, typename Container, typename T,\n            typename AddResult =\n                typename internal::AdditiveSemigroupTraits<G>::ReturnTy,\n            std::enable_if_t<std::is_same_v<G, AddResult>>* = nullptr>\n  constexpr static G& LinearCombinationInPlace(Container& values, const T& r) {\n    size_t size = std::size(values);\n    CHECK_GT(size, size_t{0});\n    if constexpr (Forward) {\n      G& ret = values[0];\n      if (size > 1) {\n        for (size_t i = 1; i < size; ++i) {\n          ret *= r;\n          ret += values[i];\n        }\n      }\n      return ret;\n    } else {\n      G& ret = values[size - 1];\n      if (size > 1) {\n        for (size_t i = size - 2; i != SIZE_MAX; --i) {\n          ret *= r;\n          ret += values[i];\n        }\n      }\n      return ret;\n    }\n  }\n\n private:\n  constexpr static size_t kDefaultParallelThreshold = 1024;\n\n  template <size_t N>\n  [[nodiscard]] constexpr AddResult DoScalarMul(const BigInt<N>& scalar) const {\n    const G* g = static_cast<const G*>(this);\n    AddResult ret = AddResult::Zero();\n    auto it = BitIteratorBE<BigInt<N>>::begin(&scalar, true);\n    auto end = BitIteratorBE<BigInt<N>>::end(&scalar);\n    while (it != end) {\n      if constexpr (internal::SupportsDoubleImplInPlace<G>::value ||\n                    internal::SupportsAddInPlace<G, G>::value) {\n        ret.DoubleInPlace();\n      } else {\n        ret = ret.Double();\n      }\n      if (*it) {\n        if constexpr (internal::SupportsAddInPlace<AddResult, G>::value) {\n          ret.AddInPlace(*g);\n        } else {\n          ret = ret.Add(*g);\n        }\n      }\n      ++it;\n    }\n    return ret;\n  }\n\n  // Multi Scalar Multi Base\n  template <typename ScalarContainer, typename BaseContainer,\n            typename OutputContainer>\n  CONSTEXPR_IF_NOT_OPENMP static bool MultiScalarMulMSMB(\n      const ScalarContainer& scalars, const BaseContainer& bases,\n      OutputContainer* outputs) {\n    size_t size = scalars.size();\n    if (size != std::size(bases)) return false;\n    if (size != std::size(*outputs)) return false;\n    if (size == 0) {\n      LOG(ERROR) << \"scalars and bases are empty\";\n      return false;\n    }\n    OMP_PARALLEL_FOR(size_t i = 0; i < size; ++i) {\n      (*outputs)[i] = bases[i].ScalarMul(scalars[i]);\n    }\n    return true;\n  }\n\n  // Multi Scalar Single Base\n  template <typename ScalarContainer, typename OutputContainer>\n  CONSTEXPR_IF_NOT_OPENMP static bool MultiScalarMulMSSB(\n      const ScalarContainer& scalars, const G& base, OutputContainer* outputs) {\n    size_t size = std::size(scalars);\n    if (size != std::size(*outputs)) return false;\n    if (size == 0) {\n      LOG(ERROR) << \"scalars are empty\";\n      return false;\n    }\n    OMP_PARALLEL_FOR(size_t i = 0; i < size; ++i) {\n      (*outputs)[i] = base.ScalarMul(scalars[i]);\n    }\n    return true;\n  }\n\n  // Single Scalar Multi Base\n  template <typename Scalar, typename BaseContainer, typename OutputContainer>\n  CONSTEXPR_IF_NOT_OPENMP static bool MultiScalarMulSSMB(\n      const Scalar& scalar, const BaseContainer& bases,\n      OutputContainer* outputs) {\n    size_t size = std::size(bases);\n    if (size != std::size(*outputs)) return false;\n    if (size == 0) {\n      LOG(ERROR) << \"bases are empty\";\n      return false;\n    }\n    OMP_PARALLEL_FOR(size_t i = 0; i < size; ++i) {\n      (*outputs)[i] = bases[i].ScalarMul(scalar);\n    }\n    return true;\n  }\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_BASE_SEMIGROUPS_H_\n"
  },
  {
    "path": "tachyon/math/base/semigroups_unittest.cc",
    "content": "#include \"tachyon/math/base/semigroups.h\"\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/math/elliptic_curves/msm/test/variable_base_msm_test_set.h\"\n#include \"tachyon/math/elliptic_curves/short_weierstrass/test/sw_curve_config.h\"\n\nnamespace tachyon::math {\n\nTEST(SemigroupsTest, Mul) {\n  class Int : public MultiplicativeSemigroup<Int> {\n   public:\n    Int() = default;\n    explicit Int(int value) : value_(value) {}\n    Int(const Int& other) : value_(other.value_) {}\n\n    MOCK_METHOD(Int, Mul, (const Int& other), (const));\n\n    bool operator==(const Int& other) const { return value_ == other.value_; }\n\n   private:\n    int value_ = 0;\n  };\n\n  Int a(3);\n  Int b(4);\n  EXPECT_CALL(a, Mul(b)).Times(testing::Exactly(1));\n\n  Int c = a * b;\n  static_cast<void>(c);\n}\n\nTEST(SemigroupsTest, MulOverMulInPlace) {\n  class Int : public MultiplicativeSemigroup<Int> {\n   public:\n    Int() = default;\n    explicit Int(int value) : value_(value) {}\n    Int(const Int& other) : value_(other.value_) {}\n\n    MOCK_METHOD(Int, Mul, (const Int& other), (const));\n    MOCK_METHOD(Int&, MulInPlace, (const Int& other));\n\n    bool operator==(const Int& other) const { return value_ == other.value_; }\n\n   private:\n    int value_ = 0;\n  };\n\n  Int a(3);\n  Int b(4);\n  EXPECT_CALL(a, Mul(b)).Times(testing::Exactly(1));\n\n  Int c = a * b;\n  static_cast<void>(c);\n\n  EXPECT_CALL(c, Mul(c)).Times(testing::Exactly(1));\n\n  Int d = c.Square();\n  static_cast<void>(d);\n}\n\nTEST(SemigroupsTest, Add) {\n  class Int : public AdditiveSemigroup<Int> {\n   public:\n    Int() = default;\n    explicit Int(int value) : value_(value) {}\n    Int(const Int& other) : value_(other.value_) {}\n\n    MOCK_METHOD(Int, Add, (const Int& other), (const));\n\n    bool operator==(const Int& other) const { return value_ == other.value_; }\n\n   private:\n    int value_ = 0;\n  };\n\n  Int a(3);\n  Int b(4);\n  EXPECT_CALL(a, Add(b)).Times(testing::Exactly(1));\n\n  Int c = a + b;\n  static_cast<void>(c);\n}\n\nTEST(SemigroupsTest, AddOverAddInPlace) {\n  class Int : public AdditiveSemigroup<Int> {\n   public:\n    Int() = default;\n    explicit Int(int value) : value_(value) {}\n    Int(const Int& other) : value_(other.value_) {}\n\n    MOCK_METHOD(Int, Add, (const Int& other), (const));\n    MOCK_METHOD(Int&, AddInPlace, (const Int& other));\n\n    bool operator==(const Int& other) const { return value_ == other.value_; }\n\n   private:\n    int value_ = 0;\n  };\n\n  Int a(3);\n  Int b(4);\n  EXPECT_CALL(a, Add(b)).Times(testing::Exactly(1));\n\n  Int c = a + b;\n  static_cast<void>(c);\n\n  EXPECT_CALL(c, Add(c)).Times(testing::Exactly(1));\n\n  Int d = c.Double();\n  static_cast<void>(d);\n}\n\nnamespace {\n\nclass MultiScalarMulTest : public testing::Test {\n public:\n  static void SetUpTestSuite() {\n    test::G1Curve::Init();\n\n#if defined(TACHYON_HAS_OPENMP)\n    size_t thread_nums = static_cast<size_t>(omp_get_max_threads());\n#else\n    size_t thread_nums = 1;\n#endif\n    test_set_ = VariableBaseMSMTestSet<test::AffinePoint>::Random(\n        thread_nums * 5, VariableBaseMSMMethod::kNone);\n  }\n\n protected:\n  static VariableBaseMSMTestSet<test::AffinePoint> test_set_;\n};\n\nVariableBaseMSMTestSet<test::AffinePoint> MultiScalarMulTest::test_set_;\n\n}  // namespace\n\n// scalar: s\n// bases: [G₀, G₁, ..., Gₙ₋₁]\n// return: [sG₀, sG₁, ..., sGₙ₋₁]\nTEST_F(MultiScalarMulTest, SingleScalarMultiBases) {\n  const GF7& scalar = test_set_.scalars[0];\n  std::vector<test::JacobianPoint> expected = base::Map(\n      test_set_.bases,\n      [&scalar](const test::AffinePoint& base) { return base * scalar; });\n  {\n    std::vector<test::JacobianPoint> actual;\n    actual.resize(test_set_.bases.size());\n    ASSERT_TRUE(test::AffinePoint::MultiScalarMul(test_set_.scalars[0],\n                                                  test_set_.bases, &actual));\n    EXPECT_EQ(actual, expected);\n  }\n  {\n    std::vector<test::JacobianPoint> actual;\n    actual.resize(test_set_.bases.size());\n    ASSERT_TRUE(test::AffinePoint::MultiScalarMul(test_set_.scalars[0],\n                                                  test_set_.bases, &actual));\n    EXPECT_EQ(actual, expected);\n  }\n}\n\n// scalars: [s₀, s₁, ..., sₙ₋₁]\n// base: G\n// return: [s₀G, s₁G, ..., sₙ₋₁G]\nTEST_F(MultiScalarMulTest, MultiScalarsSingleBase) {\n  const test::AffinePoint& base = test_set_.bases[0];\n  std::vector<test::JacobianPoint> expected = base::Map(\n      test_set_.scalars, [&base](const GF7& scalar) { return base * scalar; });\n  {\n    std::vector<test::JacobianPoint> actual;\n    actual.resize(test_set_.scalars.size());\n    ASSERT_TRUE(test::AffinePoint::MultiScalarMul(test_set_.scalars,\n                                                  test_set_.bases[0], &actual));\n    EXPECT_EQ(actual, expected);\n  }\n  {\n    std::vector<test::JacobianPoint> actual;\n    actual.resize(test_set_.bases.size());\n    ASSERT_TRUE(test::AffinePoint::MultiScalarMul(test_set_.scalars,\n                                                  test_set_.bases[0], &actual));\n    EXPECT_EQ(actual, expected);\n  }\n}\n\n// scalars: [s₀, s₁, ..., sₙ₋₁]\n// bases: [G₀, G₁, ..., Gₙ₋₁]\n// return: [s₀G₀, s₁G₁, ..., sₙ₋₁Gₙ₋₁]\nTEST_F(MultiScalarMulTest, MultiScalarsMultiBases) {\n  std::vector<test::JacobianPoint> expected = base::Map(\n      test_set_.scalars,\n      [](size_t i, const GF7& scalar) { return test_set_.bases[i] * scalar; });\n  {\n    std::vector<test::JacobianPoint> actual;\n    actual.resize(test_set_.scalars.size());\n    ASSERT_TRUE(test::AffinePoint::MultiScalarMul(test_set_.scalars,\n                                                  test_set_.bases, &actual));\n    EXPECT_EQ(actual, expected);\n  }\n  {\n    std::vector<test::JacobianPoint> actual;\n    actual.resize(test_set_.bases.size());\n    ASSERT_TRUE(test::AffinePoint::MultiScalarMul(test_set_.scalars,\n                                                  test_set_.bases, &actual));\n    EXPECT_EQ(actual, expected);\n  }\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/base/sign.cc",
    "content": "#include \"tachyon/math/base/sign.h\"\n\n#include \"tachyon/base/strings/string_util.h\"\n\nnamespace tachyon::math {\n\nstd::string SignToString(Sign sign) {\n  switch (sign) {\n    case Sign::kZero:\n      return \"0\";\n    case Sign::kPositive:\n      return \"+\";\n    case Sign::kNegative:\n      return \"-\";\n    case Sign::kNaN:\n      return \"nan\";\n  }\n  NOTREACHED();\n  return base::EmptyString();\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/base/sign.h",
    "content": "#ifndef TACHYON_MATH_BASE_SIGN_H_\n#define TACHYON_MATH_BASE_SIGN_H_\n\n#include <cmath>\n#include <string>\n#include <type_traits>\n\n#include \"tachyon/base/logging.h\"\n\nnamespace tachyon::math {\n\nenum class Sign {\n  kZero,\n  kPositive,\n  kNegative,\n  // Not a number. GetSign(nan) gives this.\n  kNaN,\n};\n\nTACHYON_EXPORT std::string SignToString(Sign sign);\n\n//  0: kZero\n//  1: kPositive\n// -1: kNegative\nconstexpr Sign ToSign(int v) {\n  switch (v) {\n    case 0:\n      return Sign::kZero;\n    case 1:\n      return Sign::kPositive;\n    case -1:\n      return Sign::kNegative;\n  }\n  NOTREACHED();\n  return Sign::kNaN;\n}\n\n// Taken from\n// https://stackoverflow.com/questions/1903954/is-there-a-standard-sign-function-signum-sgn-in-c-c\ntemplate <typename T>\nconstexpr Sign GetSign(T x) {\n  if constexpr (std::is_signed_v<T>) {\n    if constexpr (std::is_floating_point_v<T>) {\n      if (std::isnan(x)) return Sign::kNaN;\n    }\n    return ToSign((T(0) < x) - (x < T(0)));\n  } else {\n    return ToSign(T(0) < x);\n  }\n}\n\ntemplate <typename T, typename SFINAE = void>\nclass SignedValue;\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_BASE_SIGN_H_\n"
  },
  {
    "path": "tachyon/math/base/sign_unittest.cc",
    "content": "#include \"tachyon/math/base/sign.h\"\n\n#include <limits>\n\n#include \"gtest/gtest.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename NumType>\nclass SignTest : public testing::Test {};\n\nusing NumTypes = testing::Types<int, unsigned int, double>;\nTYPED_TEST_SUITE(SignTest, NumTypes);\n\nTYPED_TEST(SignTest, GetSign) {\n  using NumType = TypeParam;\n\n  EXPECT_EQ(GetSign(NumType(0)), Sign::kZero);\n  EXPECT_EQ(GetSign(NumType(1)), Sign::kPositive);\n  if constexpr (std::is_signed_v<NumType>) {\n    EXPECT_EQ(GetSign(NumType(-1)), Sign::kNegative);\n  }\n  if constexpr (std::is_floating_point_v<NumType>) {\n    EXPECT_EQ(GetSign(NumType(std::numeric_limits<NumType>::quiet_NaN())),\n              Sign::kNaN);\n    EXPECT_EQ(GetSign(NumType(std::numeric_limits<NumType>::signaling_NaN())),\n              Sign::kNaN);\n    EXPECT_EQ(GetSign(NumType(std::numeric_limits<NumType>::infinity())),\n              Sign::kPositive);\n    EXPECT_EQ(GetSign(NumType(-std::numeric_limits<NumType>::infinity())),\n              Sign::kNegative);\n  }\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/base/simd_int.h",
    "content": "// Copyright 2024 Ulvetanna Inc.\n// Use of this source code is governed by a Apache-2.0 style license that\n// can be found in the LICENSE.ulvetanna file.\n\n#ifndef TACHYON_MATH_BASE_SIMD_INT_H_\n#define TACHYON_MATH_BASE_SIMD_INT_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <string>\n#include <type_traits>\n\n#include \"tachyon/build/build_config.h\"\n#include \"tachyon/export.h\"\n#include \"tachyon/math/base/big_int.h\"\n\nnamespace tachyon::math {\n\ntemplate <size_t Bits>\nclass SimdInt {\n public:\n  constexpr static size_t kBits = Bits;\n  constexpr static size_t kLimbNums = Bits / 64;\n\n  using value_type = BigInt<kLimbNums>;\n\n  SimdInt() = default;\n  template <typename T,\n            std::enable_if_t<std::is_constructible_v<value_type, T>>* = nullptr>\n  explicit SimdInt(T value) : SimdInt(value_type(value)) {}\n  explicit SimdInt(const value_type& value) : value_(value) {}\n\n  static SimdInt Zero() { return SimdInt(); }\n  static SimdInt One() { return SimdInt(1); }\n  static SimdInt Max() { return SimdInt(value_type::Max()); }\n  static SimdInt Broadcast(uint8_t value);\n  static SimdInt Broadcast(uint16_t value);\n  static SimdInt Broadcast(uint32_t value);\n  static SimdInt Broadcast(uint64_t value);\n  static SimdInt Random() { return SimdInt(value_type::Random()); }\n\n  const value_type& value() const { return value_; }\n\n  bool IsZero() const { return *this == Zero(); }\n  bool IsOne() const { return *this == One(); }\n  bool IsMax() const { return *this == Max(); }\n\n  bool operator==(const SimdInt& other) const;\n  bool operator!=(const SimdInt& other) const { return !operator==(other); }\n\n  SimdInt operator&(const SimdInt& other) const;\n  SimdInt& operator&=(const SimdInt& other) { return *this = *this & other; }\n\n  SimdInt operator|(const SimdInt& other) const;\n  SimdInt& operator|=(const SimdInt& other) { return *this = *this | other; }\n\n  SimdInt operator^(const SimdInt& other) const;\n  SimdInt& operator^=(const SimdInt& other) { return *this = *this ^ other; }\n\n  SimdInt operator!() const { return *this ^ Max(); }\n\n  SimdInt operator>>(uint32_t count) const;\n  SimdInt& operator>>=(uint32_t count) { return *this = *this >> count; }\n\n  SimdInt operator<<(uint32_t count) const;\n  SimdInt& operator<<=(uint32_t count) { return *this = *this << count; }\n\n  std::string ToString() const { return value_.ToString(); }\n  std::string ToHexString(bool pad_zero = false) const {\n    return value_.ToHexString(pad_zero);\n  }\n\n private:\n  value_type value_;\n};\n\n// clang-format off\n#define SPECIALIZE_SIMD_INT(bits)                                           \\\n  using SimdInt##bits = SimdInt<bits>;                                      \\\n                                                                            \\\n  template <>                                                               \\\n  SimdInt##bits SimdInt##bits::Broadcast(uint8_t value);                    \\\n                                                                            \\\n  template <>                                                               \\\n  SimdInt##bits SimdInt##bits::Broadcast(uint16_t value);                   \\\n                                                                            \\\n  template <>                                                               \\\n  SimdInt##bits SimdInt##bits::Broadcast(uint32_t value);                   \\\n                                                                            \\\n  template <>                                                               \\\n  SimdInt##bits SimdInt##bits::Broadcast(uint64_t value);                   \\\n                                                                            \\\n  template <>                                                               \\\n  bool SimdInt##bits::operator==(const SimdInt##bits& value) const;         \\\n                                                                            \\\n  template <>                                                               \\\n  SimdInt##bits SimdInt##bits::operator&(const SimdInt##bits& value) const; \\\n                                                                            \\\n  template <>                                                               \\\n  SimdInt##bits SimdInt##bits::operator|(const SimdInt##bits& value) const; \\\n                                                                            \\\n  template <>                                                               \\\n  SimdInt##bits SimdInt##bits::operator^(const SimdInt##bits& value) const; \\\n                                                                            \\\n  template <>                                                               \\\n  SimdInt##bits SimdInt##bits::operator>>(uint32_t count) const;            \\\n                                                                            \\\n  template <>                                                               \\\n  SimdInt##bits SimdInt##bits::operator<<(uint32_t count) const\n// clang-format on\n\nSPECIALIZE_SIMD_INT(128);\n#if ARCH_CPU_X86_64\nSPECIALIZE_SIMD_INT(256);\n#if defined(TACHYON_HAS_AVX512)\nSPECIALIZE_SIMD_INT(512);\n#endif\n#endif\n\n#undef SPECIALIZE_SIMD_INT\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_BASE_SIMD_INT_H_\n"
  },
  {
    "path": "tachyon/math/base/simd_int128_arm64.cc",
    "content": "// Copyright 2024 Ulvetanna Inc.\n// Use of this source code is governed by a Apache-2.0 style license that\n// can be found in the LICENSE.ulvetanna file.\n\n#include <arm_neon.h>\n\n#include <algorithm>\n\n#include \"tachyon/base/bit_cast.h\"\n#include \"tachyon/base/compiler_specific.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/math/base/simd_int.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\nuint8x16_t ToVector(const SimdInt128& value) {\n  return vld1q_u8(reinterpret_cast<const uint8_t*>(&value));\n}\n\nSimdInt128 FromVector(uint8x16_t vector) {\n  SimdInt128 ret;\n  vst1q_u8(reinterpret_cast<uint8_t*>(&ret), vector);\n  return ret;\n}\n\nSimdInt128 FromVector(uint16x8_t vector) {\n  SimdInt128 ret;\n  vst1q_u16(reinterpret_cast<uint16_t*>(&ret), vector);\n  return ret;\n}\n\nSimdInt128 FromVector(uint32x4_t vector) {\n  SimdInt128 ret;\n  vst1q_u32(reinterpret_cast<uint32_t*>(&ret), vector);\n  return ret;\n}\n\nSimdInt128 FromVector(uint64x2_t vector) {\n  SimdInt128 ret;\n  vst1q_u64(reinterpret_cast<uint64_t*>(&ret), vector);\n  return ret;\n}\n\n}  // namespace\n\n// static\ntemplate <>\nSimdInt128 SimdInt128::Broadcast(uint8_t value) {\n  return FromVector(vdupq_n_u8(value));\n}\n\n// static\ntemplate <>\nSimdInt128 SimdInt128::Broadcast(uint16_t value) {\n  return FromVector(vdupq_n_u16(value));\n}\n\n// static\ntemplate <>\nSimdInt128 SimdInt128::Broadcast(uint32_t value) {\n  return FromVector(vdupq_n_u32(value));\n}\n\n// static\ntemplate <>\nSimdInt128 SimdInt128::Broadcast(uint64_t value) {\n  return FromVector(vdupq_n_u64(value));\n}\n\ntemplate <>\nbool SimdInt128::operator==(const SimdInt128& other) const {\n  return value_ == other.value_;\n}\n\ntemplate <>\nSimdInt128 SimdInt128::operator&(const SimdInt128& other) const {\n  return FromVector(vandq_u8(ToVector(*this), ToVector(other)));\n}\n\ntemplate <>\nSimdInt128 SimdInt128::operator|(const SimdInt128& other) const {\n  return FromVector(vorrq_u8(ToVector(*this), ToVector(other)));\n}\n\ntemplate <>\nSimdInt128 SimdInt128::operator^(const SimdInt128& other) const {\n  return FromVector(veorq_u8(ToVector(*this), ToVector(other)));\n}\n\ntemplate <>\nSimdInt128 SimdInt128::operator>>(uint32_t count) const {\n  if (UNLIKELY(count == 0)) return *this;\n  if (UNLIKELY(count >= 128)) return SimdInt128();\n  return SimdInt128(value_ >> count);\n}\n\ntemplate <>\nSimdInt128 SimdInt128::operator<<(uint32_t count) const {\n  if (UNLIKELY(count == 0)) return *this;\n  if (UNLIKELY(count >= 128)) return SimdInt128();\n  return SimdInt128(value_ << count);\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/base/simd_int128_x86.cc",
    "content": "// Copyright 2024 Ulvetanna Inc.\n// Use of this source code is governed by a Apache-2.0 style license that\n// can be found in the LICENSE.ulvetanna file.\n\n#include <immintrin.h>\n\n#include <algorithm>\n\n#include \"tachyon/base/bit_cast.h\"\n#include \"tachyon/base/compiler_specific.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/math/base/simd_int.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\n__m128i ToVector(const SimdInt128& value) {\n  return _mm_loadu_si128(reinterpret_cast<const __m128i*>(&value));\n}\n\nSimdInt128 FromVector(__m128i vector) {\n  SimdInt128 ret;\n  _mm_storeu_si128(reinterpret_cast<__m128i*>(&ret), vector);\n  return ret;\n}\n\n}  // namespace\n\n// static\ntemplate <>\nSimdInt128 SimdInt128::Broadcast(uint8_t value) {\n  return FromVector(_mm_set1_epi8(value));\n}\n\n// static\ntemplate <>\nSimdInt128 SimdInt128::Broadcast(uint16_t value) {\n  return FromVector(_mm_set1_epi16(value));\n}\n\n// static\ntemplate <>\nSimdInt128 SimdInt128::Broadcast(uint32_t value) {\n  return FromVector(_mm_set1_epi32(value));\n}\n\n// static\ntemplate <>\nSimdInt128 SimdInt128::Broadcast(uint64_t value) {\n  return FromVector(_mm_set1_epi64(base::bit_cast<__m64>(value)));\n}\n\ntemplate <>\nbool SimdInt128::operator==(const SimdInt128& other) const {\n  __m128i neq = _mm_xor_si128(ToVector(*this), ToVector(other));\n  return _mm_test_all_zeros(neq, neq) == 1;\n}\n\ntemplate <>\nSimdInt128 SimdInt128::operator&(const SimdInt128& other) const {\n  return FromVector(_mm_and_si128(ToVector(*this), ToVector(other)));\n}\n\ntemplate <>\nSimdInt128 SimdInt128::operator|(const SimdInt128& other) const {\n  return FromVector(_mm_or_si128(ToVector(*this), ToVector(other)));\n}\n\ntemplate <>\nSimdInt128 SimdInt128::operator^(const SimdInt128& other) const {\n  return FromVector(_mm_xor_si128(ToVector(*this), ToVector(other)));\n}\n\ntemplate <>\nSimdInt128 SimdInt128::operator>>(uint32_t count) const {\n  if (UNLIKELY(count == 0)) return *this;\n  if (UNLIKELY(count >= 128)) return SimdInt128();\n  // See\n  // https://stackoverflow.com/questions/34478328/the-best-way-to-shift-a-m128i/34482688#34482688\n  for (uint32_t i = 1; i < 128; ++i) {\n    if (i == count) {\n      __m128i carry = _mm_bsrli_si128(ToVector(*this), 8);\n      if (count >= 64) {\n        return FromVector(\n            _mm_srli_epi64(carry, std::max(count - 64, uint32_t{0})));\n      } else {\n        carry = _mm_slli_epi64(carry, std::max(64 - count, uint32_t{0}));\n\n        __m128i val = _mm_srli_epi64(ToVector(*this), count);\n        return FromVector(_mm_or_si128(val, carry));\n      }\n    }\n  }\n  NOTREACHED();\n  return SimdInt128();\n}\n\ntemplate <>\nSimdInt128 SimdInt128::operator<<(uint32_t count) const {\n  if (UNLIKELY(count == 0)) return *this;\n  if (UNLIKELY(count >= 128)) return SimdInt128();\n  // See\n  // https://stackoverflow.com/questions/34478328/the-best-way-to-shift-a-m128i/34482688#34482688\n  for (uint32_t i = 1; i < 128; ++i) {\n    if (i == count) {\n      __m128i carry = _mm_bslli_si128(ToVector(*this), 8);\n      if (count >= 64) {\n        return FromVector(\n            _mm_slli_epi64(carry, std::max(count - 64, uint32_t{0})));\n      } else {\n        carry = _mm_srli_epi64(carry, std::max(64 - count, uint32_t{0}));\n\n        __m128i val = _mm_slli_epi64(ToVector(*this), count);\n        return FromVector(_mm_or_si128(val, carry));\n      }\n    }\n  }\n  NOTREACHED();\n  return SimdInt128();\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/base/simd_int256_x86.cc",
    "content": "// Copyright 2024 Ulvetanna Inc.\n// Use of this source code is governed by a Apache-2.0 style license that\n// can be found in the LICENSE.ulvetanna file.\n\n#include <immintrin.h>\n\n#include \"tachyon/base/bit_cast.h\"\n#include \"tachyon/base/compiler_specific.h\"\n#include \"tachyon/math/base/simd_int.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\n__m256i ToVector(const SimdInt256& value) {\n  return base::bit_cast<__m256i>(value);\n}\n\nSimdInt256 FromVector(__m256i vector) {\n  SimdInt256 ret;\n  _mm256_storeu_si256(reinterpret_cast<__m256i*>(&ret), vector);\n  return ret;\n}\n\n}  // namespace\n\n// static\ntemplate <>\nSimdInt256 SimdInt256::Broadcast(uint8_t value) {\n  return FromVector(_mm256_set1_epi8(value));\n}\n\n// static\ntemplate <>\nSimdInt256 SimdInt256::Broadcast(uint16_t value) {\n  return FromVector(_mm256_set1_epi16(value));\n}\n\n// static\ntemplate <>\nSimdInt256 SimdInt256::Broadcast(uint32_t value) {\n  return FromVector(_mm256_set1_epi32(value));\n}\n\n// static\ntemplate <>\nSimdInt256 SimdInt256::Broadcast(uint64_t value) {\n  return FromVector(_mm256_set1_epi64x(value));\n}\n\ntemplate <>\nbool SimdInt256::operator==(const SimdInt256& other) const {\n  __m256i pcmp = _mm256_cmpeq_epi32(ToVector(*this), ToVector(other));\n  unsigned int bitmask =\n      base::bit_cast<unsigned int>(_mm256_movemask_epi8(pcmp));\n  return bitmask == 0xffffffff;\n}\n\ntemplate <>\nSimdInt256 SimdInt256::operator&(const SimdInt256& other) const {\n  return FromVector(_mm256_and_si256(ToVector(*this), ToVector(other)));\n}\n\ntemplate <>\nSimdInt256 SimdInt256::operator|(const SimdInt256& other) const {\n  return FromVector(_mm256_or_si256(ToVector(*this), ToVector(other)));\n}\n\ntemplate <>\nSimdInt256 SimdInt256::operator^(const SimdInt256& other) const {\n  return FromVector(_mm256_xor_si256(ToVector(*this), ToVector(other)));\n}\n\ntemplate <>\nSimdInt256 SimdInt256::operator>>(uint32_t count) const {\n  if (UNLIKELY(count == 0)) return *this;\n  if (UNLIKELY(count >= 256)) return SimdInt256();\n  // TODO(chokobole): Optimize this.\n  return SimdInt256(value_ >> count);\n}\n\ntemplate <>\nSimdInt256 SimdInt256::operator<<(uint32_t count) const {\n  if (UNLIKELY(count == 0)) return *this;\n  if (UNLIKELY(count >= 256)) return SimdInt256();\n  // TODO(chokobole): Optimize this.\n  return SimdInt256(value_ << count);\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/base/simd_int512_x86.cc",
    "content": "// Copyright 2024 Ulvetanna Inc.\n// Use of this source code is governed by a Apache-2.0 style license that\n// can be found in the LICENSE.ulvetanna file.\n\n#include <immintrin.h>\n\n#include \"tachyon/base/bit_cast.h\"\n#include \"tachyon/base/compiler_specific.h\"\n#include \"tachyon/math/base/simd_int.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\n__m512i ToVector(const SimdInt512& value) { return _mm512_loadu_si512(&value); }\n\nSimdInt512 FromVector(__m512i vector) {\n  SimdInt512 ret;\n  _mm512_storeu_si512(&ret, vector);\n  return ret;\n}\n\n}  // namespace\n\n// static\ntemplate <>\nSimdInt512 SimdInt512::Broadcast(uint8_t value) {\n  return FromVector(_mm512_set1_epi8(value));\n}\n\n// static\ntemplate <>\nSimdInt512 SimdInt512::Broadcast(uint16_t value) {\n  return FromVector(_mm512_set1_epi16(value));\n}\n\n// static\ntemplate <>\nSimdInt512 SimdInt512::Broadcast(uint32_t value) {\n  return FromVector(_mm512_set1_epi32(value));\n}\n\n// static\ntemplate <>\nSimdInt512 SimdInt512::Broadcast(uint64_t value) {\n  return FromVector(_mm512_set1_epi64(value));\n}\n\ntemplate <>\nbool SimdInt512::operator==(const SimdInt512& other) const {\n  __mmask16 pcmp = _mm512_cmpeq_epi32_mask(ToVector(*this), ToVector(other));\n  return pcmp == 0xffff;\n}\n\ntemplate <>\nSimdInt512 SimdInt512::operator&(const SimdInt512& other) const {\n  return FromVector(_mm512_and_si512(ToVector(*this), ToVector(other)));\n}\n\ntemplate <>\nSimdInt512 SimdInt512::operator|(const SimdInt512& other) const {\n  return FromVector(_mm512_or_si512(ToVector(*this), ToVector(other)));\n}\n\ntemplate <>\nSimdInt512 SimdInt512::operator^(const SimdInt512& other) const {\n  return FromVector(_mm512_xor_si512(ToVector(*this), ToVector(other)));\n}\n\ntemplate <>\nSimdInt512 SimdInt512::operator>>(uint32_t count) const {\n  if (UNLIKELY(count == 0)) return *this;\n  if (UNLIKELY(count >= 512)) return SimdInt512();\n  // TODO(chokobole): Optimize this.\n  return SimdInt512(value_ >> count);\n}\n\ntemplate <>\nSimdInt512 SimdInt512::operator<<(uint32_t count) const {\n  if (UNLIKELY(count == 0)) return *this;\n  if (UNLIKELY(count >= 512)) return SimdInt512();\n  // TODO(chokobole): Optimize this.\n  return SimdInt512(value_ << count);\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/base/simd_int_unittest.cc",
    "content": "#include \"tachyon/math/base/simd_int.h\"\n\n#include \"gtest/gtest.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename SimdInt>\nclass SimdIntTest : public testing::Test {};\n\nusing SimdIntTypes = testing::Types<SimdInt128\n#if ARCH_CPU_X86_64\n                                    ,\n                                    SimdInt256\n#if defined(TACHYON_HAS_AVX512)\n                                    ,\n                                    SimdInt512\n#endif\n#endif\n                                    >;\nTYPED_TEST_SUITE(SimdIntTest, SimdIntTypes);\n\nTYPED_TEST(SimdIntTest, Zero) {\n  using SimdInt = TypeParam;\n\n  EXPECT_TRUE(SimdInt().IsZero());\n  EXPECT_TRUE(SimdInt::Zero().IsZero());\n  EXPECT_FALSE(SimdInt::One().IsZero());\n  EXPECT_FALSE(SimdInt::Max().IsZero());\n}\n\nTYPED_TEST(SimdIntTest, One) {\n  using SimdInt = TypeParam;\n\n  EXPECT_FALSE(SimdInt().IsOne());\n  EXPECT_FALSE(SimdInt::Zero().IsOne());\n  EXPECT_TRUE(SimdInt::One().IsOne());\n  EXPECT_FALSE(SimdInt::Max().IsOne());\n}\n\nTYPED_TEST(SimdIntTest, Max) {\n  using SimdInt = TypeParam;\n\n  EXPECT_FALSE(SimdInt().IsMax());\n  EXPECT_FALSE(SimdInt::Zero().IsMax());\n  EXPECT_FALSE(SimdInt::One().IsMax());\n  EXPECT_TRUE(SimdInt::Max().IsMax());\n}\n\ntemplate <typename SimdInt, typename T>\nvoid TestBroadcast() {\n  using BigInt = typename SimdInt::value_type;\n\n  T v = base::Uniform(base::Range<T>());\n  BigInt expected;\n  for (size_t i = 0; i < BigInt::kByteNums / sizeof(T); ++i) {\n    reinterpret_cast<T*>(&expected)[i] = v;\n  }\n  BigInt actual = SimdInt::Broadcast(v).value();\n  EXPECT_EQ(actual, expected);\n}\n\nTYPED_TEST(SimdIntTest, Broadcast) {\n  using SimdInt = TypeParam;\n\n  TestBroadcast<SimdInt, uint8_t>();\n  TestBroadcast<SimdInt, uint16_t>();\n  TestBroadcast<SimdInt, uint32_t>();\n  TestBroadcast<SimdInt, uint64_t>();\n}\n\nTYPED_TEST(SimdIntTest, EqualityOperations) {\n  using SimdInt = TypeParam;\n  using BigInt = typename SimdInt::value_type;\n\n  SimdInt a = SimdInt::Random();\n  SimdInt b = SimdInt(a.value() + BigInt(1));\n  EXPECT_EQ(a, a);\n  EXPECT_NE(a, b);\n}\n\nTYPED_TEST(SimdIntTest, BitOperations) {\n  using SimdInt = TypeParam;\n  using BigInt = typename SimdInt::value_type;\n\n  SimdInt a = SimdInt::Random();\n  SimdInt not_a = !a;\n  EXPECT_EQ(a & a, a);\n  EXPECT_TRUE((a & not_a).IsZero());\n  EXPECT_EQ(a | a, a);\n  EXPECT_TRUE((a | not_a).IsMax());\n  EXPECT_TRUE((a ^ a).IsZero());\n  EXPECT_TRUE((a ^ not_a).IsMax());\n\n  size_t count = base::Uniform(base::Range<size_t>(0, SimdInt::kBits));\n  BigInt expected = a.value() << count;\n  EXPECT_EQ((a << count).value(), expected);\n\n  expected = a.value() >> count;\n  EXPECT_EQ((a >> count).value(), expected);\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/circle/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"affine_point\",\n    hdrs = [\"affine_point.h\"],\n    deps = [\n        \"//tachyon/base:logging\",\n        \"//tachyon/math/base:groups\",\n        \"//tachyon/math/geometry:curve_type\",\n        \"//tachyon/math/geometry:point2\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"circle\",\n    hdrs = [\"circle.h\"],\n    deps = [\n        \":circle_traits_forward\",\n        \"//tachyon/math/geometry:affine_point\",\n        \"//tachyon/math/geometry:curve_type\",\n        \"@com_google_absl//absl/base\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"circle_traits_forward\",\n    hdrs = [\"circle_traits_forward.h\"],\n)\n\ntachyon_cc_unittest(\n    name = \"circle_unittests\",\n    srcs = [\"affine_point_unittest.cc\"],\n    deps = [\n        \"//tachyon/math/circle/m31:g1\",\n        \"//tachyon/math/circle/m31:g4\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/math/circle/affine_point.h",
    "content": "// Copyright 2024 StarkWare Industries Ltd\n// Use of this source code is governed by a Apache-2.0 style license that\n// can be found in the LICENSE-APACHE.stwo\n\n#ifndef TACHYON_MATH_CIRCLE_AFFINE_POINT_H_\n#define TACHYON_MATH_CIRCLE_AFFINE_POINT_H_\n\n#include <string>\n#include <utility>\n\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/math/base/groups.h\"\n#include \"tachyon/math/geometry/affine_point.h\"\n#include \"tachyon/math/geometry/curve_type.h\"\n#include \"tachyon/math/geometry/point2.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename _Curve>\nclass AffinePoint<_Curve, std::enable_if_t<_Curve::kType == CurveType::kCircle>>\n    final : public AdditiveGroup<AffinePoint<_Curve>> {\n public:\n  using Curve = _Curve;\n  using Circle = _Curve;\n  using BaseField = typename Circle::BaseField;\n  using ScalarField = typename Circle::ScalarField;\n\n  constexpr AffinePoint() : AffinePoint(BaseField::One(), BaseField::Zero()) {}\n  explicit constexpr AffinePoint(const Point2<BaseField>& point)\n      : AffinePoint(point.x, point.y) {}\n  explicit constexpr AffinePoint(Point2<BaseField>&& point)\n      : AffinePoint(std::move(point.x), std::move(point.y)) {}\n  constexpr AffinePoint(const BaseField& x, const BaseField& y)\n      : x_(x), y_(y) {}\n  constexpr AffinePoint(BaseField&& x, BaseField&& y)\n      : x_(std::move(x)), y_(std::move(y)) {}\n\n  constexpr static AffinePoint CreateChecked(const BaseField& x,\n                                             const BaseField& y) {\n    AffinePoint ret = {x, y};\n    CHECK(ret.IsOnCircle());\n    return ret;\n  }\n\n  constexpr static AffinePoint CreateChecked(BaseField&& x, BaseField&& y) {\n    AffinePoint ret = {std::move(x), std::move(y)};\n    CHECK(ret.IsOnCircle());\n    return ret;\n  }\n\n  constexpr static AffinePoint Zero() { return AffinePoint(); }\n\n  constexpr static AffinePoint One() { return Generator(); }\n\n  constexpr static AffinePoint Generator() {\n    return {Circle::Config::kGenerator.x, Circle::Config::kGenerator.y};\n  }\n\n  constexpr static AffinePoint Random() {\n    return ScalarField::Random() * Generator();\n  }\n\n  constexpr const BaseField& x() const { return x_; }\n  constexpr const BaseField& y() const { return y_; }\n\n  constexpr bool operator==(const AffinePoint& other) const {\n    return x_ == other.x_ && y_ == other.y_;\n  }\n  constexpr bool operator!=(const AffinePoint& other) const {\n    return !operator==(other);\n  }\n\n  constexpr bool IsZero() const { return x_.IsOne() && y_.IsZero(); }\n\n  constexpr bool IsOnCircle() { return Circle::IsOnCircle(*this); }\n\n  constexpr AffinePoint Conjugate() const { return {x_, -y_}; }\n\n  constexpr AffinePoint ComplexConjugate() const {\n    return {x_.ComplexConjugate(), y_.ComplexConjugate()};\n  }\n\n  constexpr AffinePoint Antipode() const { return {-x_, -y_}; }\n\n  std::string ToString() const {\n    return absl::Substitute(\"($0, $1)\", x_.ToString(), y_.ToString());\n  }\n\n  std::string ToHexString(bool pad_zero = false) const {\n    return absl::Substitute(\"($0, $1)\", x_.ToHexString(pad_zero),\n                            y_.ToHexString(pad_zero));\n  }\n\n  // AdditiveSemigroup methods\n  constexpr AffinePoint Add(const AffinePoint& other) const {\n    return {x_ * other.x_ - y_ * other.y_, x_ * other.y_ + y_ * other.x_};\n  }\n\n  constexpr AffinePoint& AddInPlace(const AffinePoint& other) {\n    return *this = {x_ * other.x_ - y_ * other.y_,\n                    x_ * other.y_ + y_ * other.x_};\n  }\n\n  constexpr AffinePoint DoubleImpl() const {\n    return {x_.Square().Double() - BaseField::One(), x_.Double() * y_};\n  }\n\n  constexpr AffinePoint& DoubleImplInPlace() {\n    y_ *= x_.Double();\n    x_.SquareInPlace().DoubleInPlace() -= BaseField::One();\n    return *this;\n  }\n\n  // AdditiveGroup methods\n  constexpr AffinePoint Sub(const AffinePoint& other) const {\n    return {x_ * other.x_ + y_ * other.y_, -x_ * other.y_ + y_ * other.x_};\n  }\n\n  constexpr AffinePoint& SubInPlace(const AffinePoint& other) {\n    return *this = {x_ * other.x_ + y_ * other.y_,\n                    -x_ * other.y_ + y_ * other.x_};\n  }\n\n  constexpr AffinePoint Negate() const { return {x_, -y_}; }\n\n  constexpr AffinePoint& NegateInPlace() {\n    y_.NegateInPlace();\n    return *this;\n  }\n\n  constexpr AffinePoint operator*(const ScalarField& idx) const {\n    return this->ScalarMul(idx.value());\n  }\n\n private:\n  BaseField x_;\n  BaseField y_;\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_CIRCLE_AFFINE_POINT_H_\n"
  },
  {
    "path": "tachyon/math/circle/affine_point_unittest.cc",
    "content": "#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/circle/m31/g1.h\"\n#include \"tachyon/math/circle/m31/g4.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename AffinePointType>\nclass AffinePointTest : public testing::Test {\n public:\n  static void SetUpTestSuite() { AffinePointType::Circle::Init(); }\n};\n\nusing AffinePointTypes = testing::Types<m31::G1AffinePoint, m31::G4AffinePoint>;\nTYPED_TEST_SUITE(AffinePointTest, AffinePointTypes);\n\nTYPED_TEST(AffinePointTest, IsZero) {\n  using AffinePoint = TypeParam;\n  using BaseField = typename AffinePoint::BaseField;\n\n  EXPECT_TRUE(AffinePoint::Zero().IsZero());\n  EXPECT_FALSE(AffinePoint(BaseField::Zero(), BaseField::One()).IsZero());\n}\n\nTYPED_TEST(AffinePointTest, Generator) {\n  using AffinePoint = TypeParam;\n  using Circle = typename AffinePoint::Circle;\n\n  EXPECT_EQ(\n      AffinePoint::Generator(),\n      AffinePoint(Circle::Config::kGenerator.x, Circle::Config::kGenerator.y));\n}\n\nTYPED_TEST(AffinePointTest, Order) {\n  using AffinePoint = TypeParam;\n\n  AffinePoint r = AffinePoint::Random();\n  EXPECT_TRUE(r.ScalarMul(AffinePoint::ScalarField::Config::kModulus).IsZero());\n}\n\nTYPED_TEST(AffinePointTest, Random) {\n  using AffinePoint = TypeParam;\n\n  bool success = false;\n  AffinePoint r = AffinePoint::Random();\n  for (size_t i = 0; i < 100; ++i) {\n    if (r != AffinePoint::Random()) {\n      success = true;\n      break;\n    }\n  }\n  EXPECT_TRUE(success);\n  EXPECT_TRUE(r.IsOnCircle());\n}\n\nTYPED_TEST(AffinePointTest, EqualityOperators) {\n  using AffinePoint = TypeParam;\n  using BaseField = typename AffinePoint::BaseField;\n\n  AffinePoint p(BaseField::Random(), BaseField::Random());\n  AffinePoint p2(BaseField::Random(), BaseField::Random());\n  EXPECT_EQ(p, p);\n  EXPECT_NE(p, p2);\n}\n\nTYPED_TEST(AffinePointTest, Conjugate) {\n  using AffinePoint = TypeParam;\n\n  AffinePoint p = AffinePoint::Random();\n  EXPECT_EQ(p.Conjugate(), AffinePoint(p.x(), -p.y()));\n}\n\nTYPED_TEST(AffinePointTest, Antipode) {\n  using AffinePoint = TypeParam;\n\n  AffinePoint p = AffinePoint::Random();\n  EXPECT_EQ(p.Antipode(), AffinePoint(-p.x(), -p.y()));\n}\n\nTYPED_TEST(AffinePointTest, AdditiveGroupOperators) {\n  using AffinePoint = TypeParam;\n\n  AffinePoint a = AffinePoint::Random();\n  AffinePoint b = AffinePoint::Random();\n  AffinePoint c = a + b;\n\n  EXPECT_EQ(c - a, b);\n  EXPECT_EQ(c - b, a);\n  AffinePoint doubled = a.Double();\n  EXPECT_EQ(doubled, a + a);\n  AffinePoint a_tmp = a;\n  EXPECT_EQ(a_tmp.DoubleInPlace(), doubled);\n\n  EXPECT_EQ(a + AffinePoint::Zero(), a);\n  a_tmp = a;\n  a_tmp.NegateInPlace();\n  EXPECT_EQ(a_tmp, -a);\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/circle/circle.h",
    "content": "#ifndef TACHYON_MATH_CIRCLE_CIRCLE_H_\n#define TACHYON_MATH_CIRCLE_CIRCLE_H_\n\n#include \"absl/base/call_once.h\"\n\n#include \"tachyon/math/circle/circle_traits_forward.h\"\n#include \"tachyon/math/geometry/affine_point.h\"\n#include \"tachyon/math/geometry/curve_type.h\"\n\nnamespace tachyon::math {\n\n// Config for Unit Circle.\n// This config represents x² + y² = 1.\ntemplate <typename CircleConfig>\nclass Circle {\n public:\n  using Config = CircleConfig;\n\n  using BaseField = typename Config::BaseField;\n  using ScalarField = typename Config::ScalarField;\n  using AffinePoint = typename CircleTraits<Config>::AffinePointTy;\n\n  constexpr static CurveType kType = CurveType::kCircle;\n\n  static void Init() {\n    static absl::once_flag once;\n    absl::call_once(once, []() {\n      BaseField::Init();\n      ScalarField::Init();\n\n      Config::Init();\n    });\n  }\n\n  constexpr static bool IsOnCircle(const AffinePoint& point) {\n    return (point.x().Square() + point.y().Square()).IsOne();\n  }\n};\n\ntemplate <typename Config>\nstruct CircleTraits {\n  using BaseField = typename Config::BaseField;\n  using ScalarField = typename Config::ScalarField;\n  using AffinePointTy = AffinePoint<Circle<Config>>;\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_CIRCLE_CIRCLE_H_\n"
  },
  {
    "path": "tachyon/math/circle/circle_traits_forward.h",
    "content": "#ifndef TACHYON_MATH_CIRCLE_CIRCLE_TRAITS_FORWARD_H_\n#define TACHYON_MATH_CIRCLE_CIRCLE_TRAITS_FORWARD_H_\n\nnamespace tachyon::math {\n\ntemplate <typename T>\nstruct CircleTraits;\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_CIRCLE_CIRCLE_TRAITS_FORWARD_H_\n"
  },
  {
    "path": "tachyon/math/circle/generator/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_binary\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_binary(\n    name = \"generator\",\n    srcs = [\"generator.cc\"],\n    data = [\"cpu.h.tpl\"],\n    deps = [\n        \"//tachyon/base/console\",\n        \"//tachyon/base/files:file_path_flag\",\n        \"//tachyon/base/flag:flag_parser\",\n        \"//tachyon/base/strings:string_number_conversions\",\n        \"//tachyon/build:cc_writer\",\n        \"//tachyon/math/finite_fields/generator:generator_util\",\n        \"@com_google_absl//absl/container:flat_hash_map\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/math/circle/generator/build_defs.bzl",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\ndef _generate_circle_point_impl(ctx):\n    cpu_hdr_tpl_path = ctx.expand_location(\"$(location @kroma_network_tachyon//tachyon/math/circle/generator:cpu.h.tpl)\", [ctx.attr.cpu_hdr_tpl_path])\n\n    arguments = [\n        \"--out=%s\" % (ctx.outputs.out.path),\n        \"--namespace=%s\" % (ctx.attr.namespace),\n        \"--base_field_degree=%s\" % (ctx.attr.base_field_degree),\n        \"--base_field=%s\" % (ctx.attr.base_field),\n        \"--base_field_hdr=%s\" % (ctx.attr.base_field_hdr),\n        \"--scalar_field=%s\" % (ctx.attr.scalar_field),\n        \"--scalar_field_hdr=%s\" % (ctx.attr.scalar_field_hdr),\n        \"--cpu_hdr_tpl_path=%s\" % (cpu_hdr_tpl_path),\n    ]\n    if len(ctx.attr.class_name) > 0:\n        arguments.append(\"--class=%s\" % (ctx.attr.class_name))\n\n    for x in ctx.attr.x:\n        arguments.append(\"-x=%s\" % (x))\n\n    for y in ctx.attr.y:\n        arguments.append(\"-y=%s\" % (y))\n\n    ctx.actions.run(\n        inputs = [ctx.files.cpu_hdr_tpl_path[0]],\n        tools = [ctx.executable._tool],\n        executable = ctx.executable._tool,\n        outputs = [ctx.outputs.out],\n        arguments = arguments,\n    )\n\n    return [DefaultInfo(files = depset([ctx.outputs.out]))]\n\ngenerate_circle_point = rule(\n    implementation = _generate_circle_point_impl,\n    attrs = {\n        \"out\": attr.output(mandatory = True),\n        \"namespace\": attr.string(mandatory = True),\n        \"class_name\": attr.string(),\n        \"base_field\": attr.string(mandatory = True),\n        \"base_field_degree\": attr.int(mandatory = True),\n        \"base_field_hdr\": attr.string(mandatory = True),\n        \"scalar_field\": attr.string(mandatory = True),\n        \"scalar_field_hdr\": attr.string(mandatory = True),\n        \"x\": attr.string_list(mandatory = True),\n        \"y\": attr.string_list(mandatory = True),\n        \"cpu_hdr_tpl_path\": attr.label(\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/math/circle/generator:cpu.h.tpl\"),\n        ),\n        \"_tool\": attr.label(\n            # TODO(chokobole): Change to \"exec\", so we can build on macos.\n            cfg = \"target\",\n            executable = True,\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/math/circle/generator\"),\n        ),\n    },\n)\n\ndef generate_circle_points(\n        name,\n        namespace,\n        base_field_degree,\n        base_field,\n        base_field_hdr,\n        base_field_dep,\n        scalar_field,\n        scalar_field_hdr,\n        scalar_field_dep,\n        x,\n        y,\n        class_name = \"\",\n        **kwargs):\n    for n in [\n        (\"{}_gen_hdr\".format(name), \"{}.h\".format(name)),\n    ]:\n        generate_circle_point(\n            namespace = namespace,\n            class_name = class_name,\n            base_field_degree = base_field_degree,\n            base_field = base_field,\n            base_field_hdr = base_field_hdr,\n            scalar_field = scalar_field,\n            scalar_field_hdr = scalar_field_hdr,\n            x = x,\n            y = y,\n            name = n[0],\n            out = n[1],\n        )\n\n    tachyon_cc_library(\n        name = name,\n        hdrs = [\":{}_gen_hdr\".format(name)],\n        deps = [\n            base_field_dep,\n            scalar_field_dep,\n            \"//tachyon/math/circle:affine_point\",\n            \"//tachyon/math/circle:circle\",\n            \"@com_google_absl//absl/base\",\n        ],\n        **kwargs\n    )\n"
  },
  {
    "path": "tachyon/math/circle/generator/cpu.h.tpl",
    "content": "// clang-format off\n#include \"absl/base/call_once.h\"\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/math/circle/affine_point.h\"\n#include \"tachyon/math/circle/circle.h\"\n#include \"tachyon/math/geometry/point2.h\"\n#include \"%{base_field_hdr}\"\n#include \"%{scalar_field_hdr}\"\n\nnamespace %{namespace} {\n\ntemplate <typename _BaseField, typename _ScalarField>\nclass %{class}CircleConfig {\n public:\n  using BaseField = _BaseField;\n  using BasePrimeField = %{base_prime_field};\n  using ScalarField = _ScalarField;\n\n  // TODO(chokobole): Make them constexpr.\n  static Point2<BaseField> kGenerator;\n\n  static void Init() {\n    static absl::once_flag once;\n    absl::call_once(once, &%{class}CircleConfig::DoInit);\n  }\n\n private:\n  static void DoInit() {\n%{x_init}\n%{y_init}\n    VLOG(1) << \"%{namespace}::%{class} initialized\";\n  }\n};\n\ntemplate <typename BaseField, typename ScalarField>\nPoint2<BaseField> %{class}CircleConfig<BaseField, ScalarField>::kGenerator;\n\nusing %{class}Circle = tachyon::math::Circle<%{class}CircleConfig<%{base_field}, %{scalar_field}>>;\nusing %{class}AffinePoint = tachyon::math::AffinePoint<%{class}Circle>;\n\n}  // namespace %{namespace}\n// clang-format on\n"
  },
  {
    "path": "tachyon/math/circle/generator/generator.cc",
    "content": "#include \"absl/container/flat_hash_map.h\"\n#include \"absl/strings/str_replace.h\"\n\n#include \"tachyon/base/console/iostream.h\"\n#include \"tachyon/base/files/file_path_flag.h\"\n#include \"tachyon/base/flag/flag_parser.h\"\n#include \"tachyon/base/strings/string_number_conversions.h\"\n#include \"tachyon/build/cc_writer.h\"\n#include \"tachyon/math/finite_fields/generator/generator_util.h\"\n\nnamespace tachyon {\n\nstruct GenerationConfig : public build::CcWriter {\n  base::FilePath cpu_hdr_tpl_path;\n\n  std::string ns_name;\n  std::string class_name;\n  int base_field_degree;\n  std::string base_field;\n  base::FilePath base_field_hdr;\n  std::string scalar_field;\n  base::FilePath scalar_field_hdr;\n  std::vector<std::string> x;\n  std::vector<std::string> y;\n\n  int GenerateConfigHdr() const;\n};\n\nint GenerationConfig::GenerateConfigHdr() const {\n  CHECK_EQ(x.size(), y.size());\n\n  absl::flat_hash_map<std::string, std::string> replacements = {\n      {\"%{namespace}\", ns_name},\n      {\"%{class}\", class_name},\n      {\"%{base_field}\", base_field},\n      {\"%{base_field_hdr}\", base_field_hdr.value()},\n      {\"%{scalar_field}\", scalar_field},\n      {\"%{scalar_field_hdr}\", scalar_field_hdr.value()},\n      {\"%{base_prime_field}\",\n       x.size() == 1 ? \"BaseField\" : \"typename BaseField::BasePrimeField\"}};\n\n  if (x.size() == 1) {\n    replacements[\"%{x_init}\"] =\n        math::GenerateInitField(\"kGenerator.x\", \"BaseField\", x[0]);\n    replacements[\"%{y_init}\"] =\n        math::GenerateInitField(\"kGenerator.y\", \"BaseField\", y[0]);\n  } else {\n    replacements[\"%{x_init}\"] = math::GenerateInitExtField(\n        \"kGenerator.x\", \"BaseField\", x, base_field_degree);\n    replacements[\"%{y_init}\"] = math::GenerateInitExtField(\n        \"kGenerator.y\", \"BaseField\", y, base_field_degree);\n  }\n\n  std::string tpl_content;\n  CHECK(base::ReadFileToString(cpu_hdr_tpl_path, &tpl_content));\n\n  std::string content = absl::StrReplaceAll(tpl_content, replacements);\n  return WriteHdr(content, false);\n}\n\nint RealMain(int argc, char** argv) {\n  GenerationConfig config;\n  config.generator = \"//tachyon/math/circle/generator\";\n\n  base::FlagParser parser;\n  parser.AddFlag<base::FilePathFlag>(&config.out)\n      .set_long_name(\"--out\")\n      .set_help(\"path to output\");\n  parser.AddFlag<base::StringFlag>(&config.ns_name)\n      .set_long_name(\"--namespace\")\n      .set_required();\n  parser.AddFlag<base::StringFlag>(&config.class_name).set_long_name(\"--class\");\n  parser.AddFlag<base::IntFlag>(&config.base_field_degree)\n      .set_long_name(\"--base_field_degree\")\n      .set_required();\n  parser.AddFlag<base::StringFlag>(&config.base_field)\n      .set_long_name(\"--base_field\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.base_field_hdr)\n      .set_long_name(\"--base_field_hdr\")\n      .set_required();\n  parser.AddFlag<base::StringFlag>(&config.scalar_field)\n      .set_long_name(\"--scalar_field\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.scalar_field_hdr)\n      .set_long_name(\"--scalar_field_hdr\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.cpu_hdr_tpl_path)\n      .set_long_name(\"--cpu_hdr_tpl_path\")\n      .set_required();\n  parser.AddFlag<base::Flag<std::vector<std::string>>>(&config.x)\n      .set_short_name(\"-x\")\n      .set_required();\n  parser.AddFlag<base::Flag<std::vector<std::string>>>(&config.y)\n      .set_short_name(\"-y\")\n      .set_required();\n\n  std::string error;\n  if (!parser.Parse(argc, argv, &error)) {\n    tachyon_cerr << error << std::endl;\n    return 1;\n  }\n\n  if (base::EndsWith(config.out.value(), \".h\")) {\n    return config.GenerateConfigHdr();\n  } else {\n    tachyon_cerr << \"suffix not supported:\" << config.out << std::endl;\n    return 1;\n  }\n}\n\n}  // namespace tachyon\n\nint main(int argc, char** argv) { return tachyon::RealMain(argc, argv); }\n"
  },
  {
    "path": "tachyon/math/circle/m31/BUILD.bazel",
    "content": "load(\"//tachyon/math/circle/generator:build_defs.bzl\", \"generate_circle_points\")\nload(\n    \"//tachyon/math/finite_fields/generator/ext_field_generator:build_defs.bzl\",\n    \"generate_fp2s\",\n    \"generate_fp4s\",\n)\nload(\"//tachyon/math/finite_fields/generator/prime_field_generator:build_defs.bzl\", \"generate_prime_fields\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ngenerate_circle_points(\n    name = \"g1\",\n    base_field = \"Mersenne31\",\n    base_field_degree = 1,\n    base_field_dep = \"//tachyon/math/finite_fields/mersenne31\",\n    base_field_hdr = \"tachyon/math/finite_fields/mersenne31/mersenne31.h\",\n    class_name = \"G1\",\n    namespace = \"tachyon::math::m31\",\n    scalar_field = \"Fr\",\n    scalar_field_dep = \":fr\",\n    scalar_field_hdr = \"tachyon/math/circle/m31/fr.h\",\n    x = [\"2\"],\n    y = [\"1268011823\"],\n)\n\ngenerate_prime_fields(\n    name = \"fr\",\n    class_name = \"Fr\",\n    # 2³¹\n    # Hex: 0x80000000\n    modulus = \"2147483648\",\n    namespace = \"tachyon::math::m31\",\n    reduce32 = \"return v & (kModulus - 1);\",\n    reduce64 = \"return v & (kModulus - 1);\",\n    use_montgomery = False,\n)\n\ngenerate_fp2s(\n    name = \"fq2\",\n    base_field = \"Mersenne31\",\n    base_field_hdr = \"tachyon/math/finite_fields/mersenne31/mersenne31.h\",\n    class_name = \"Fq2\",\n    is_packed = False,\n    namespace = \"tachyon::math::m31\",\n    non_residue = [\"-1\"],\n    deps = [\"//tachyon/math/finite_fields/mersenne31\"],\n)\n\ngenerate_fp4s(\n    name = \"fq4\",\n    base_field = \"Fq2\",\n    base_field_degree = 2,\n    base_field_hdr = \"tachyon/math/circle/m31/fq2.h\",\n    class_name = \"Fq4\",\n    is_packed = False,\n    namespace = \"tachyon::math::m31\",\n    non_residue = [\n        \"2\",\n        \"1\",\n    ],\n    deps = [\":fq2\"],\n)\n\ngenerate_prime_fields(\n    name = \"fr4\",\n    class_name = \"Fr4\",\n    # (2³¹ - 1)⁴ - 1\n    # Hex: 0xfffffff800000017ffffffe00000000\n    modulus = \"21267647892944572736998860269687930880\",\n    namespace = \"tachyon::math::m31\",\n    use_asm = False,\n)\n\ngenerate_circle_points(\n    name = \"g4\",\n    base_field = \"Fq4\",\n    base_field_degree = 4,\n    base_field_dep = \":fq4\",\n    base_field_hdr = \"tachyon/math/circle/m31/fq4.h\",\n    class_name = \"G4\",\n    namespace = \"tachyon::math::m31\",\n    scalar_field = \"Fr4\",\n    scalar_field_dep = \":fr4\",\n    scalar_field_hdr = \"tachyon/math/circle/m31/fr4.h\",\n    x = [\n        \"1\",\n        \"0\",\n        \"478637715\",\n        \"513582971\",\n    ],\n    y = [\n        \"992285211\",\n        \"649143431\",\n        \"740191619\",\n        \"1186584352\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"semigroups\",\n    hdrs = [\"semigroups.h\"],\n    deps = [\n        \"//tachyon/math/base:semigroups\",\n        \"//tachyon/math/geometry:affine_point\",\n        \"//tachyon/math/geometry:curve_type\",\n        \"//tachyon/math/geometry:jacobian_point\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/bls12/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"bls12_curve\",\n    hdrs = [\"bls12_curve.h\"],\n    deps = [\n        \":g2_prepared\",\n        \"//tachyon/base:optional\",\n        \"//tachyon/base:parallelize\",\n        \"//tachyon/math/elliptic_curves/pairing:pairing_friendly_curve\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"g2_prepared\",\n    hdrs = [\"g2_prepared.h\"],\n    deps = [\n        \"//tachyon/base:optional\",\n        \"//tachyon/math/base:big_int\",\n        \"//tachyon/math/base:bit_iterator\",\n        \"//tachyon/math/elliptic_curves/pairing:g2_prepared_base\",\n        \"//tachyon/math/elliptic_curves/pairing:g2_projective\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/bls12/bls12_381/BUILD.bazel",
    "content": "load(\"@bazel_skylib//rules:common_settings.bzl\", \"string_flag\")\nload(\"//tachyon/math/elliptic_curves/bls12/generator:build_defs.bzl\", \"generate_bls12_curves\")\nload(\"//tachyon/math/elliptic_curves/short_weierstrass/generator:build_defs.bzl\", \"generate_ec_points\")\nload(\n    \"//tachyon/math/finite_fields/generator/ext_field_generator:build_defs.bzl\",\n    \"generate_fp12s\",\n    \"generate_fp2s\",\n    \"generate_fp6s\",\n)\nload(\n    \"//tachyon/math/finite_fields/generator/prime_field_generator:build_defs.bzl\",\n    \"SMALL_SUBGROUP_ADICITY\",\n    \"SMALL_SUBGROUP_BASE\",\n    \"SUBGROUP_GENERATOR\",\n    \"generate_large_fft_prime_fields\",\n)\n\npackage(default_visibility = [\"//visibility:public\"])\n\nstring_flag(\n    name = \"fq_\" + SMALL_SUBGROUP_ADICITY,\n    build_setting_default = \"2\",\n)\n\nstring_flag(\n    name = \"fq_\" + SMALL_SUBGROUP_BASE,\n    build_setting_default = \"3\",\n)\n\nstring_flag(\n    name = \"fq_\" + SUBGROUP_GENERATOR,\n    build_setting_default = \"2\",\n)\n\n# Parameters are from https://zips.z.cash/protocol/protocol.pdf#page=98 and https://github.com/arkworks-rs/curves/tree/master/bls12_381/src/fields\ngenerate_large_fft_prime_fields(\n    name = \"fq\",\n    class_name = \"Fq\",\n    # Hex: 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab\n    modulus = \"4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787\",\n    namespace = \"tachyon::math::bls12_381\",\n    small_subgroup_adicity = \":fq_\" + SMALL_SUBGROUP_ADICITY,\n    small_subgroup_base = \":fq_\" + SMALL_SUBGROUP_BASE,\n    subgroup_generator = \":fq_\" + SUBGROUP_GENERATOR,\n)\n\nstring_flag(\n    name = \"fr_\" + SMALL_SUBGROUP_ADICITY,\n    build_setting_default = \"1\",\n)\n\nstring_flag(\n    name = \"fr_\" + SMALL_SUBGROUP_BASE,\n    build_setting_default = \"3\",\n)\n\nstring_flag(\n    name = \"fr_\" + SUBGROUP_GENERATOR,\n    build_setting_default = \"7\",\n)\n\ngenerate_large_fft_prime_fields(\n    name = \"fr\",\n    class_name = \"Fr\",\n    # Hex: 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001\n    modulus = \"52435875175126190479447740508185965837690552500527637822603658699938581184513\",\n    namespace = \"tachyon::math::bls12_381\",\n    small_subgroup_adicity = \":fr_\" + SMALL_SUBGROUP_ADICITY,\n    small_subgroup_base = \":fr_\" + SMALL_SUBGROUP_BASE,\n    subgroup_generator = \":fr_\" + SUBGROUP_GENERATOR,\n)\n\ngenerate_fp2s(\n    name = \"fq2\",\n    base_field = \"Fq\",\n    base_field_hdr = \"tachyon/math/elliptic_curves/bls12/bls12_381/fq.h\",\n    class_name = \"Fq2\",\n    is_packed = False,\n    namespace = \"tachyon::math::bls12_381\",\n    non_residue = [\"-1\"],\n    deps = [\":fq\"],\n)\n\ngenerate_fp6s(\n    name = \"fq6\",\n    base_field = \"Fq2\",\n    base_field_degree = 2,\n    base_field_hdr = \"tachyon/math/elliptic_curves/bls12/bls12_381/fq2.h\",\n    class_name = \"Fq6\",\n    is_packed = False,\n    mul_by_non_residue_override =\n        \"\"\"    // (c0 + c1 * u) * (1 + u) = (c0 - c1) + (c1 + c0) * u\n    return BaseField(v.c0() - v.c1(), v.c0() + v.c1());\"\"\",\n    namespace = \"tachyon::math::bls12_381\",\n    non_residue = [\n        \"1\",\n        \"1\",\n    ],\n    deps = [\":fq2\"],\n)\n\ngenerate_fp12s(\n    name = \"fq12\",\n    base_field = \"Fq6\",\n    base_field_hdr = \"tachyon/math/elliptic_curves/bls12/bls12_381/fq6.h\",\n    class_name = \"Fq12\",\n    is_packed = False,\n    namespace = \"tachyon::math::bls12_381\",\n    non_residue = [\n        \"0\",\n        \"1\",\n        \"0\",\n    ],\n    deps = [\":fq6\"],\n)\n\ngenerate_ec_points(\n    name = \"g1\",\n    a = [\"0\"],\n    b = [\"4\"],\n    base_field = \"Fq\",\n    base_field_degree = 1,\n    base_field_dep = \":fq\",\n    base_field_hdr = \"tachyon/math/elliptic_curves/bls12/bls12_381/fq.h\",\n    class_name = \"G1\",\n    # Hex: 0x5f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe\n    endomorphism_coefficient = [\"793479390729215512621379701633421447060886740281060493010456487427281649075476305620758731620350\"],\n    gen_gpu = True,\n    # Optimal decomposition as per Ch. 6.3.2: Decompositions for the k = 12 BLS\n    # Family, from Guide to Pairing Based Cryptography by El Mrabet\n    glv_coeffs = [\n        # X²\n        # Hex: 0xac45a4010001a4020000000100000000\n        \"228988810152649578064853576960394133504\",\n        \"1\",\n        \"-1\",\n        # X² - 1\n        # Hex: 0xac45a4010001a40200000000ffffffff\n        \"228988810152649578064853576960394133503\",\n    ],\n    # Hex: 0x73eda753299d7d483339d80809a1d804a7780001fffcb7fcfffffffe00000001\n    lambda_ = \"52435875175126190479447740508185965837461563690374988244538805122978187051009\",\n    namespace = \"tachyon::math::bls12_381\",\n    scalar_field = \"Fr\",\n    scalar_field_dep = \":fr\",\n    scalar_field_hdr = \"tachyon/math/elliptic_curves/bls12/bls12_381/fr.h\",\n    # Hex: 0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb\n    x = [\"3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507\"],\n    # Hex: 0x8b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1\n    y = [\"1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569\"],\n)\n\ngenerate_ec_points(\n    name = \"g2\",\n    a = [\n        \"0\",\n        \"0\",\n    ],\n    b = [\n        \"4\",\n        \"4\",\n    ],\n    base_field = \"Fq2\",\n    base_field_degree = 2,\n    base_field_dep = \":fq2\",\n    base_field_hdr = \"tachyon/math/elliptic_curves/bls12/bls12_381/fq2.h\",\n    class_name = \"G2\",\n    endomorphism_coefficient = [\n        # Hex: 0x5f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe\n        \"793479390729215512621379701633421447060886740281060493010456487427281649075476305620758731620350\",\n        \"0\",\n    ],\n    glv_coeffs = [\n        # Hex: 0xac45a4010001a40200000000ffffffff\n        \"-228988810152649578064853576960394133503\",\n        \"1\",\n        \"-1\",\n        # Hex: 0xac45a4010001a4020000000100000000\n        \"-228988810152649578064853576960394133504\",\n    ],\n    # Hex: 0xac45a4010001a40200000000ffffffff\n    lambda_ = \"228988810152649578064853576960394133503\",\n    namespace = \"tachyon::math::bls12_381\",\n    scalar_field = \"Fr\",\n    scalar_field_dep = \":fr\",\n    scalar_field_hdr = \"tachyon/math/elliptic_curves/bls12/bls12_381/fr.h\",\n    x = [\n        # Hex: 0x24aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8\n        \"352701069587466618187139116011060144890029952792775240219908644239793785735715026873347600343865175952761926303160\",\n        # Hex: 0x13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e\n        \"3059144344244213709971259814753781636986470325476647558659373206291635324768958432433509563104347017837885763365758\",\n    ],\n    y = [\n        # Hex: 0xce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801\n        \"1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905\",\n        # Hex: 0x606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be\n        \"927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582\",\n    ],\n)\n\ngenerate_bls12_curves(\n    name = \"bls12_381\",\n    class_name = \"BLS12_381\",\n    fq12_dep = \":fq12\",\n    fq12_hdr = \"tachyon/math/elliptic_curves/bls12/bls12_381/fq12.h\",\n    g1_dep = \":g1\",\n    g1_hdr = \"tachyon/math/elliptic_curves/bls12/bls12_381/g1.h\",\n    g2_dep = \":g2\",\n    g2_hdr = \"tachyon/math/elliptic_curves/bls12/bls12_381/g2.h\",\n    namespace = \"tachyon::math::bls12_381\",\n    twist_type = \"M\",\n    # Hex: 0xd201000000010000\n    x = \"-15132376222941642752\",\n)\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/bls12/bls12_curve.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_ELLIPTIC_CURVES_BLS12_BLS12_CURVE_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_BLS12_BLS12_CURVE_H_\n\n#include <functional>\n#include <vector>\n\n#include \"tachyon/base/optional.h\"\n#include \"tachyon/base/parallelize.h\"\n#include \"tachyon/math/elliptic_curves/bls12/g2_prepared.h\"\n#include \"tachyon/math/elliptic_curves/pairing/pairing_friendly_curve.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename Config>\nclass BLS12Curve : public PairingFriendlyCurve<Config> {\n public:\n  using Base = PairingFriendlyCurve<Config>;\n  using Fp12 = typename Config::Fp12;\n  using G2Prepared = bls12::G2Prepared<Config>;\n\n  // TODO(chokobole): Leave a comment to help understand readers.\n  template <typename G1AffinePointContainer, typename G2PreparedContainer>\n  static Fp12 MultiMillerLoop(const G1AffinePointContainer& a,\n                              const G2PreparedContainer& b) {\n    using Pair = typename Base::Pair;\n\n    std::vector<Pair> pairs = Base::CreatePairs(a, b);\n\n    auto callback = [](absl::Span<const Pair> pairs) {\n      Fp12 f = Fp12::One();\n      auto it = BitIteratorBE<BigInt<Config::kXLimbNums>>::begin(\n          &Config::kX,\n          /*skip_leading_zeros=*/true);\n      ++it;\n      auto end = BitIteratorBE<BigInt<Config::kXLimbNums>>::end(&Config::kX);\n\n      while (it != end) {\n        f.SquareInPlace();\n\n        for (const Pair& pair : pairs) {\n          Base::Ell(f, pair.NextEllCoeff(), pair.g1());\n        }\n\n        if ((*it)) {\n          for (const Pair& pair : pairs) {\n            Base::Ell(f, pair.NextEllCoeff(), pair.g1());\n          }\n        }\n        ++it;\n      }\n      return f;\n    };\n\n    std::vector<Fp12> results =\n        base::ParallelizeMapByChunkSize(pairs, 4, callback);\n    Fp12 f = std::accumulate(results.begin(), results.end(), Fp12::One(),\n                             std::multiplies<>());\n\n    if constexpr (Config::kXIsNegative) {\n      CHECK(f.CyclotomicInverseInPlace());\n    }\n    return f;\n  }\n\n  static Fp12 FinalExponentiation(const Fp12& f) {\n    // Computing the final exponentiation following\n    // https://eprint.iacr.org/2020/875\n    // Adapted from the implementation in\n    // https://github.com/ConsenSys/gurvy/pull/29\n\n    // f1 = f.CyclotomicInverseInPlace() = f^(q⁶)\n    Fp12 f1 = f;\n    CHECK(f1.CyclotomicInverseInPlace());\n\n    // f2 = f⁻¹\n    Fp12 f2 = unwrap(f.Inverse());\n\n    // r = f^(q⁶ - 1)\n    Fp12 r = f1 * f2;\n\n    // f2 = f^(q⁶ - 1)\n    f2 = r;\n    // r = f^((q⁶ - 1)(q²))\n    r.FrobeniusMapInPlace(2);\n\n    // r = f^((q⁶ - 1)(q²)) * f^(q⁶ - 1)\n    // r = f^((q⁶ - 1)(q² + 1))\n    r *= f2;\n\n    // Hard part of the final exponentiation:\n    // y0 = r²\n    Fp12 y0 = r.CyclotomicSquare();\n    // y1 = (r)ˣ\n    Fp12 y1 = Base::PowByX(r);\n    // y2 = (r)⁻¹\n    Fp12 y2 = unwrap(r.CyclotomicInverse());\n    // y1 = y1 * y2 = r^(x - 1)\n    y1 *= y2;\n    // y2 = (y1)ˣ = r^(x² - x)\n    y2 = Base::PowByX(y1);\n    // y1 = (y1)⁻¹ = r^(-x + 1)\n    CHECK(y1.CyclotomicInverseInPlace());\n    // y1 = y1 * y2 = r^(x² - 2x + 1)\n    y1 *= y2;\n    // y2 = (y1)ˣ = r^(x³ - 2x² + x)\n    y2 = Base::PowByX(y1);\n    // y1 = (y1)^q = r^(q * (x² - 2x + 1))\n    y1.FrobeniusMapInPlace(1);\n    // y1 = y1 * y2 = r^(q * (x² - 2x  + 1) +\n    //                   1 * (x³ - 2x² + x))\n    y1 *= y2;\n    // r = r * y0 = r³\n    r *= y0;\n    // y0 = (y1)ˣ = r^(q * (x³ - 2x² + x) +\n    //                 1 * (x⁴ - 2x³ + x²))\n    y0 = Base::PowByX(y1);\n    // y2 = (y0)ˣ = r^(q * (x⁴ - 2x³ + x²) +\n    //                 1 * (x⁵ - 2x⁴ + x³))\n    y2 = Base::PowByX(y0);\n    // y0 = (y1)^(q²) = r^(q³ * (x² - 2x  + 1)) +\n    //                     q² * (x³ - 2x² + x))\n    y0 = y1;\n    y0.FrobeniusMapInPlace(2);\n    // y1 = (y1)⁻¹ = r^(q * (-x² + 2x  - 1)) +\n    //                  1 * (-x³ + 2x² - x))\n    CHECK(y1.CyclotomicInverseInPlace());\n    // y1 = y1 * y2 = r^(q * (x⁴ - 2x³ + 2x  - 1)) +\n    //                   1 * (x⁵ - 2x⁴ + 2x² - x))\n    y1 *= y2;\n    // y1 = y1 * y0 = r^(q³ * (x² - 2x  +  1)) +\n    //                   q² * (x³ - 2x² +  x))\n    //                   q  * (x⁴ - 2x³ + 2x  - 1)) +\n    //                   1  * (x⁵ - 2x⁴ + 2x² - x))\n    y1 *= y0;\n    // r = r * y1 = r^(q³ * (x² - 2x  +  1)) +\n    //                 q² * (x³ - 2x² +  x))\n    //                 q  * (x⁴ - 2x³ + 2x  - 1)) +\n    //                 1  * (x⁵ - 2x⁴ + 2x² - x + 1))\n    r *= y1;\n    return r;\n  }\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_BLS12_BLS12_CURVE_H_\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/bls12/g2_prepared.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_ELLIPTIC_CURVES_BLS12_G2_PREPARED_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_BLS12_G2_PREPARED_H_\n\n#include <utility>\n\n#include \"tachyon/base/optional.h\"\n#include \"tachyon/math/base/big_int.h\"\n#include \"tachyon/math/base/bit_iterator.h\"\n#include \"tachyon/math/elliptic_curves/pairing/g2_prepared_base.h\"\n#include \"tachyon/math/elliptic_curves/pairing/g2_projective.h\"\n\nnamespace tachyon::math::bls12 {\n\ntemplate <typename BLS12CurveConfig>\nclass G2Prepared : public G2PreparedBase<BLS12CurveConfig> {\n public:\n  using Config = BLS12CurveConfig;\n  using G2Curve = typename Config::G2Curve;\n  using Fp2 = typename G2Curve::BaseField;\n  using Fp = typename Fp2::BaseField;\n  using G2AffinePoint = typename G2Curve::AffinePoint;\n\n  G2Prepared() = default;\n  explicit G2Prepared(const EllCoeffs<Fp2>& ell_coeffs)\n      : G2PreparedBase<BLS12CurveConfig>(ell_coeffs) {}\n  explicit G2Prepared(EllCoeffs<Fp2>&& ell_coeffs)\n      : G2PreparedBase<BLS12CurveConfig>(std::move(ell_coeffs)) {}\n\n  static G2Prepared From(const G2AffinePoint& q) {\n    if (q.IsZero()) {\n      return {};\n    } else {\n      EllCoeffs<Fp2> ell_coeffs;\n      size_t size = Config::kXLimbNums * 64;\n      // NOTE(chokobole): A bit array consists of elements from [0, 1].\n      // We reserve space in |ell_coeffs| assuming that these elements are\n      // uniformly distributed.\n      ell_coeffs.reserve(/*double=*/size + /*add=*/size / 2);\n\n      G2Projective<Config> r(q.x(), q.y(), Fp2::One());\n      auto it = BitIteratorBE<BigInt<Config::kXLimbNums>>::begin(\n          &Config::kX,\n          /*skip_leading_zeros=*/false);\n      ++it;\n      auto end = BitIteratorBE<BigInt<Config::kXLimbNums>>::end(&Config::kX);\n      while (it != end) {\n        ell_coeffs.push_back(r.DoubleInPlace(Fp::TwoInv()));\n        if (*it) {\n          ell_coeffs.push_back(r.AddInPlace(q));\n        }\n        ++it;\n      }\n\n      return G2Prepared(std::move(ell_coeffs));\n    }\n  }\n};\n\n}  // namespace tachyon::math::bls12\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_BLS12_G2_PREPARED_H_\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/bls12/generator/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_binary\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_binary(\n    name = \"generator\",\n    srcs = [\"generator.cc\"],\n    data = [\"curve.h.tpl\"],\n    deps = [\n        \"//tachyon/base/console\",\n        \"//tachyon/base/files:file_path_flag\",\n        \"//tachyon/base/flag:flag_parser\",\n        \"//tachyon/base/strings:string_number_conversions\",\n        \"//tachyon/build:cc_writer\",\n        \"//tachyon/math/base/gmp:bit_traits\",\n        \"//tachyon/math/base/gmp:gmp_util\",\n        \"//tachyon/math/elliptic_curves/pairing:twist_type\",\n        \"//tachyon/math/finite_fields/generator:generator_util\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/bls12/generator/build_defs.bzl",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\ndef _generate_bls12_curve_impl(ctx):\n    curve_hdr_tpl_path = ctx.expand_location(\"$(location @kroma_network_tachyon//tachyon/math/elliptic_curves/bls12/generator:curve.h.tpl)\", [ctx.attr.curve_hdr_tpl_path])\n\n    arguments = [\n        \"--out=%s\" % (ctx.outputs.out.path),\n        \"--namespace=%s\" % (ctx.attr.namespace),\n        \"--fq12_hdr=%s\" % (ctx.attr.fq12_hdr),\n        \"--g1_hdr=%s\" % (ctx.attr.g1_hdr),\n        \"--g2_hdr=%s\" % (ctx.attr.g2_hdr),\n        \"-x=%s\" % (ctx.attr.x),\n        \"--twist_type=%s\" % (ctx.attr.twist_type),\n        \"--curve_hdr_tpl_path=%s\" % (curve_hdr_tpl_path),\n    ]\n    if len(ctx.attr.class_name) > 0:\n        arguments.append(\"--class=%s\" % (ctx.attr.class_name))\n\n    ctx.actions.run(\n        inputs = [ctx.files.curve_hdr_tpl_path[0]],\n        tools = [ctx.executable._tool],\n        executable = ctx.executable._tool,\n        outputs = [ctx.outputs.out],\n        arguments = arguments,\n    )\n\n    return [DefaultInfo(files = depset([ctx.outputs.out]))]\n\ngenerate_bls12_curve = rule(\n    implementation = _generate_bls12_curve_impl,\n    attrs = {\n        \"out\": attr.output(mandatory = True),\n        \"namespace\": attr.string(mandatory = True),\n        \"class_name\": attr.string(),\n        \"fq12_hdr\": attr.string(mandatory = True),\n        \"g1_hdr\": attr.string(mandatory = True),\n        \"g2_hdr\": attr.string(mandatory = True),\n        \"x\": attr.string(mandatory = True),\n        \"twist_type\": attr.string(mandatory = True, values = [\"M\", \"D\"]),\n        \"curve_hdr_tpl_path\": attr.label(\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/math/elliptic_curves/bls12/generator:curve.h.tpl\"),\n        ),\n        \"_tool\": attr.label(\n            # TODO(chokobole): Change to \"exec\", so we can build on macos.\n            cfg = \"target\",\n            executable = True,\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/math/elliptic_curves/bls12/generator\"),\n        ),\n    },\n)\n\ndef generate_bls12_curves(\n        name,\n        namespace,\n        fq12_hdr,\n        fq12_dep,\n        g1_hdr,\n        g1_dep,\n        g2_hdr,\n        g2_dep,\n        x,\n        twist_type,\n        class_name = \"\",\n        **kwargs):\n    for n in [\n        (\"{}_gen_hdr\".format(name), \"{}.h\".format(name)),\n    ]:\n        generate_bls12_curve(\n            namespace = namespace,\n            class_name = class_name,\n            fq12_hdr = fq12_hdr,\n            g1_hdr = g1_hdr,\n            g2_hdr = g2_hdr,\n            x = x,\n            twist_type = twist_type,\n            name = n[0],\n            out = n[1],\n        )\n\n    tachyon_cc_library(\n        name = name,\n        hdrs = [\":{}_gen_hdr\".format(name)],\n        deps = [\n            fq12_dep,\n            g1_dep,\n            g2_dep,\n            \"//tachyon/base:logging\",\n            \"//tachyon/math/elliptic_curves/bls12:bls12_curve\",\n            \"//tachyon/math/elliptic_curves/pairing:twist_type\",\n            \"@com_google_absl//absl/base\",\n        ],\n        **kwargs\n    )\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/bls12/generator/curve.h.tpl",
    "content": "// clang-format off\n#include \"absl/base/call_once.h\"\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/math/elliptic_curves/bls12/bls12_curve.h\"\n#include \"tachyon/math/elliptic_curves/pairing/twist_type.h\"\n#include \"%{fq12_hdr}\"\n#include \"%{g1_hdr}\"\n#include \"%{g2_hdr}\"\n\nnamespace %{namespace} {\n\ntemplate <typename Fq, typename Fq2, typename Fq6, typename Fq12, typename _G1Curve, typename _G2Curve>\nclass %{class}Config {\n public:\n  constexpr static const char* kName = \"%{namespace}::%{class}\";\n\n  constexpr static BigInt<%{x_size}> kX = BigInt<%{x_size}>({\n    %{x}\n  });\n  constexpr static size_t kXLimbNums = %{x_size};\n  constexpr static bool kXIsNegative = %{x_is_negative};\n  constexpr static TwistType kTwistType = TwistType::k%{twist_type};\n\n  using Fp = Fq;\n  using Fp2 = Fq2;\n  using Fp6 = Fq6;\n  using Fp12 = Fq12;\n  using G1Curve = _G1Curve;\n  using G2Curve = _G2Curve;\n\n  static void Init() {\n    static absl::once_flag once;\n    absl::call_once(once, [] {\n      G1Curve::Init();\n      G2Curve::Init();\n      VLOG(1) << \"%{namespace}::%{class} initialized\";\n    });\n  }\n};\n\nusing %{class}Curve = BLS12Curve<%{class}Config<Fq, Fq2, Fq6, Fq12, G1Curve, G2Curve>>;\n\n}  // namespace %{namespace}\n// clang-format on\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/bls12/generator/generator.cc",
    "content": "#include \"absl/strings/str_replace.h\"\n\n#include \"tachyon/base/console/iostream.h\"\n#include \"tachyon/base/files/file_path_flag.h\"\n#include \"tachyon/base/flag/flag_parser.h\"\n#include \"tachyon/base/strings/string_number_conversions.h\"\n#include \"tachyon/build/cc_writer.h\"\n#include \"tachyon/math/base/gmp/gmp_util.h\"\n#include \"tachyon/math/elliptic_curves/pairing/twist_type.h\"\n#include \"tachyon/math/finite_fields/generator/generator_util.h\"\n\nnamespace tachyon {\n\nstruct GenerationConfig : public build::CcWriter {\n  base::FilePath curve_hdr_tpl_path;\n\n  std::string ns_name;\n  std::string class_name;\n  base::FilePath fq12_hdr;\n  base::FilePath g1_hdr;\n  base::FilePath g2_hdr;\n  std::string x;\n  math::TwistType twist_type;\n\n  int GenerateConfigHdr() const;\n};\n\nint GenerationConfig::GenerateConfigHdr() const {\n  std::map<std::string, std::string> replace_map = {\n      {\"%{namespace}\", ns_name},\n      {\"%{class}\", class_name},\n      {\"%{fq12_hdr}\", fq12_hdr.value()},\n      {\"%{g1_hdr}\", g1_hdr.value()},\n      {\"%{g2_hdr}\", g2_hdr.value()},\n      {\"%{twist_type}\", TwistTypeToString(twist_type)},\n  };\n\n  mpz_class x_mpz = math::gmp::FromDecString(x);\n  replace_map[\"%{x_is_negative}\"] =\n      base::BoolToString(math::gmp::IsNegative(x_mpz));\n  x_mpz = math::gmp::GetAbs(x_mpz);\n  replace_map[\"%{x}\"] = math::MpzClassToString(x_mpz);\n  replace_map[\"%{x_size}\"] =\n      base::NumberToString(math::gmp::GetLimbSize(x_mpz));\n\n  std::string tpl_content;\n  CHECK(base::ReadFileToString(curve_hdr_tpl_path, &tpl_content));\n  std::string content = absl::StrReplaceAll(tpl_content, replace_map);\n  return WriteHdr(content, false);\n}\n\nint RealMain(int argc, char** argv) {\n  GenerationConfig config;\n  config.generator = \"//tachyon/math/elliptic_curves/bls12/generator\";\n\n  base::FlagParser parser;\n  parser.AddFlag<base::FilePathFlag>(&config.out)\n      .set_long_name(\"--out\")\n      .set_help(\"path to output\");\n  parser.AddFlag<base::StringFlag>(&config.ns_name)\n      .set_long_name(\"--namespace\")\n      .set_required();\n  parser.AddFlag<base::StringFlag>(&config.class_name).set_long_name(\"--class\");\n  parser.AddFlag<base::FilePathFlag>(&config.fq12_hdr)\n      .set_long_name(\"--fq12_hdr\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.g1_hdr)\n      .set_long_name(\"--g1_hdr\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.g2_hdr)\n      .set_long_name(\"--g2_hdr\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.curve_hdr_tpl_path)\n      .set_long_name(\"--curve_hdr_tpl_path\")\n      .set_required();\n  parser.AddFlag<base::Flag<std::string>>(&config.x)\n      .set_short_name(\"-x\")\n      .set_required();\n  parser.AddFlag<base::Flag<math::TwistType>>(&config.twist_type)\n      .set_long_name(\"--twist_type\")\n      .set_required();\n  std::string error;\n  if (!parser.Parse(argc, argv, &error)) {\n    tachyon_cerr << error << std::endl;\n    return 1;\n  }\n\n  if (base::EndsWith(config.out.value(), \".h\")) {\n    return config.GenerateConfigHdr();\n  } else {\n    tachyon_cerr << \"suffix not supported:\" << config.out << std::endl;\n    return 1;\n  }\n}\n\n}  // namespace tachyon\n\nint main(int argc, char** argv) { return tachyon::RealMain(argc, argv); }\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/bn/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"bn_curve\",\n    hdrs = [\"bn_curve.h\"],\n    deps = [\n        \":g2_prepared\",\n        \"//tachyon/base:optional\",\n        \"//tachyon/base:parallelize\",\n        \"//tachyon/math/elliptic_curves/pairing:pairing_friendly_curve\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"g2_prepared\",\n    hdrs = [\"g2_prepared.h\"],\n    deps = [\n        \"//tachyon/base:optional\",\n        \"//tachyon/math/elliptic_curves/pairing:g2_prepared_base\",\n        \"//tachyon/math/elliptic_curves/pairing:g2_projective\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/bn/bn254/BUILD.bazel",
    "content": "load(\"@bazel_skylib//rules:common_settings.bzl\", \"string_flag\")\nload(\"//tachyon/math/elliptic_curves/bn/generator:build_defs.bzl\", \"generate_bn_curves\")\nload(\"//tachyon/math/elliptic_curves/short_weierstrass/generator:build_defs.bzl\", \"generate_ec_points\")\nload(\n    \"//tachyon/math/finite_fields/generator/ext_field_generator:build_defs.bzl\",\n    \"generate_fp12s\",\n    \"generate_fp2s\",\n    \"generate_fp6s\",\n)\nload(\n    \"//tachyon/math/finite_fields/generator/prime_field_generator:build_defs.bzl\",\n    \"SMALL_SUBGROUP_ADICITY\",\n    \"SMALL_SUBGROUP_BASE\",\n    \"SUBGROUP_GENERATOR\",\n    \"generate_fft_prime_fields\",\n    \"generate_large_fft_prime_fields\",\n)\n\npackage(default_visibility = [\"//visibility:public\"])\n\nstring_flag(\n    name = \"fq_\" + SUBGROUP_GENERATOR,\n    build_setting_default = \"3\",\n)\n\n# Parameters are from https://zips.z.cash/protocol/protocol.pdf#page=97 and https://github.com/arkworks-rs/curves/tree/master/bn254/src/fields\ngenerate_fft_prime_fields(\n    name = \"fq\",\n    class_name = \"Fq\",\n    # Hex: 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47\n    modulus = \"21888242871839275222246405745257275088696311157297823662689037894645226208583\",\n    namespace = \"tachyon::math::bn254\",\n    subgroup_generator = \":fq_\" + SUBGROUP_GENERATOR,\n)\n\nstring_flag(\n    name = \"fr_\" + SMALL_SUBGROUP_ADICITY,\n    build_setting_default = \"2\",\n)\n\nstring_flag(\n    name = \"fr_\" + SMALL_SUBGROUP_BASE,\n    build_setting_default = \"3\",\n)\n\nstring_flag(\n    name = \"fr_\" + SUBGROUP_GENERATOR,\n    build_setting_default = \"5\",\n)\n\ngenerate_large_fft_prime_fields(\n    name = \"fr\",\n    class_name = \"Fr\",\n    # Hex: 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001\n    modulus = \"21888242871839275222246405745257275088548364400416034343698204186575808495617\",\n    namespace = \"tachyon::math::bn254\",\n    small_subgroup_adicity = \":fr_\" + SMALL_SUBGROUP_ADICITY,\n    small_subgroup_base = \":fr_\" + SMALL_SUBGROUP_BASE,\n    subgroup_generator = \":fr_\" + SUBGROUP_GENERATOR,\n)\n\ngenerate_fp2s(\n    name = \"fq2\",\n    base_field = \"Fq\",\n    base_field_hdr = \"tachyon/math/elliptic_curves/bn/bn254/fq.h\",\n    class_name = \"Fq2\",\n    is_packed = False,\n    namespace = \"tachyon::math::bn254\",\n    non_residue = [\"-1\"],\n    deps = [\":fq\"],\n)\n\ngenerate_fp6s(\n    name = \"fq6\",\n    base_field = \"Fq2\",\n    base_field_degree = 2,\n    base_field_hdr = \"tachyon/math/elliptic_curves/bn/bn254/fq2.h\",\n    class_name = \"Fq6\",\n    is_packed = False,\n    mul_by_non_residue_override =\n        \"\"\"    // (c0 + c1 * u) * (9 + u) = (9 * c0 - c1) + (9 * c1 + c0) * u\n    BaseField f = v;\n    // f = 8 * c0 + 8 * c1 * u\n    f.DoubleInPlace().DoubleInPlace().DoubleInPlace();\n    // c0 = -c1\n    BasePrimeField c0 = BaseField::Config::MulByNonResidue(v.c1());\n    // c0 = 8 * c0 - c1\n    c0 += f.c0();\n    // c0 = 8 * c0 - c1 + c0\n    //    = 9 * c0 - c1\n    c0 += v.c0();\n    // c1 = 8 * c1 + c1 + c0\n    //    = 8 * c1 + c0\n    BasePrimeField c1 = f.c1() + v.c1() + v.c0();\n    return BaseField(std::move(c0), std::move(c1));\"\"\",\n    namespace = \"tachyon::math::bn254\",\n    non_residue = [\n        \"9\",\n        \"1\",\n    ],\n    deps = [\":fq2\"],\n)\n\ngenerate_fp12s(\n    name = \"fq12\",\n    base_field = \"Fq6\",\n    base_field_hdr = \"tachyon/math/elliptic_curves/bn/bn254/fq6.h\",\n    class_name = \"Fq12\",\n    is_packed = False,\n    namespace = \"tachyon::math::bn254\",\n    non_residue = [\n        \"0\",\n        \"1\",\n        \"0\",\n    ],\n    deps = [\":fq6\"],\n)\n\ngenerate_ec_points(\n    name = \"g1\",\n    a = [\"0\"],\n    b = [\"3\"],\n    base_field = \"Fq\",\n    base_field_degree = 1,\n    base_field_dep = \":fq\",\n    base_field_hdr = \"tachyon/math/elliptic_curves/bn/bn254/fq.h\",\n    class_name = \"G1\",\n    # Hex: 0x30644e72e131a0295e6dd9e7e0acccb0c28f069fbb966e3de4bd44e5607cfd48\n    endomorphism_coefficient = [\"21888242871839275220042445260109153167277707414472061641714758635765020556616\"],\n    gen_gpu = True,\n    glv_coeffs = [\n        # Hex: 0x6f4d8248eeb859fc8211bbeb7d4f1128\n        \"-147946756881789319000765030803803410728\",\n        # Hex: 0x89d3256894d213e3\n        \"9931322734385697763\",\n        \"-9931322734385697763\",\n        # Hex: 0x6f4d8248eeb859fd0be4e1541221250b\n        \"-147946756881789319010696353538189108491\",\n    ],\n    # Hex: 0x30644e72e131a029048b6e193fd84104cc37a73fec2bc5e9b8ca0b2d36636f23\n    lambda_ = \"21888242871839275217838484774961031246154997185409878258781734729429964517155\",\n    namespace = \"tachyon::math::bn254\",\n    scalar_field = \"Fr\",\n    scalar_field_dep = \":fr\",\n    scalar_field_hdr = \"tachyon/math/elliptic_curves/bn/bn254/fr.h\",\n    x = [\"1\"],\n    y = [\"2\"],\n)\n\ngenerate_ec_points(\n    name = \"g2\",\n    a = [\n        \"0\",\n        \"0\",\n    ],\n    # 3 / (u + 9)\n    b = [\n        # Hex: 0x2b149d40ceb8aaae81be18991be06ac3b5b4c5e559dbefa33267e6dc24a138e5\n        \"19485874751759354771024239261021720505790618469301721065564631296452457478373\",\n        # Hex: 0x9713b03af0fed4cd2cafadeed8fdf4a74fa084e52d1852e4a2bd0685c315d2\n        \"266929791119991161246907387137283842545076965332900288569378510910307636690\",\n    ],\n    base_field = \"Fq2\",\n    base_field_degree = 2,\n    base_field_dep = \":fq2\",\n    base_field_hdr = \"tachyon/math/elliptic_curves/bn/bn254/fq2.h\",\n    class_name = \"G2\",\n    endomorphism_coefficient = [\n        # Hex: 0x30644e72e131a0295e6dd9e7e0acccb0c28f069fbb966e3de4bd44e5607cfd48\n        \"21888242871839275220042445260109153167277707414472061641714758635765020556616\",\n        \"0\",\n    ],\n    glv_coeffs = [\n        # Hex: 0x6f4d8248eeb859fd0be4e1541221250b\n        \"-147946756881789319010696353538189108491\",\n        # Hex: 0x89d3256894d213e3\n        \"-9931322734385697763\",\n        \"9931322734385697763\",\n        # Hex: 0x6f4d8248eeb859fc8211bbeb7d4f1128\n        \"-147946756881789319000765030803803410728\",\n    ],\n    # Hex: 0xb3c4d79d41a917585bfc41088d8daaa78b17ea66b99c90dd\n    lambda_ = \"4407920970296243842393367215006156084916469457145843978461\",\n    namespace = \"tachyon::math::bn254\",\n    scalar_field = \"Fr\",\n    scalar_field_dep = \":fr\",\n    scalar_field_hdr = \"tachyon/math/elliptic_curves/bn/bn254/fr.h\",\n    x = [\n        # Hex: 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed\n        \"10857046999023057135944570762232829481370756359578518086990519993285655852781\",\n        # Hex: 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2\n        \"11559732032986387107991004021392285783925812861821192530917403151452391805634\",\n    ],\n    y = [\n        # Hex: 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa\n        \"8495653923123431417604973247489272438418190587263600148770280649306958101930\",\n        # Hex: 0x90689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b\n        \"4082367875863433681332203403145435568316851327593401208105741076214120093531\",\n    ],\n)\n\ngenerate_bn_curves(\n    name = \"bn254\",\n    class_name = \"BN254\",\n    fq12_dep = \":fq12\",\n    fq12_hdr = \"tachyon/math/elliptic_curves/bn/bn254/fq12.h\",\n    g1_dep = \":g1\",\n    g1_hdr = \"tachyon/math/elliptic_curves/bn/bn254/g1.h\",\n    g2_dep = \":g2\",\n    g2_hdr = \"tachyon/math/elliptic_curves/bn/bn254/g2.h\",\n    namespace = \"tachyon::math::bn254\",\n    twist_mul_by_q_x = [\n        # Hex: 0x2fb347984f7911f74c0bec3cf559b143b78cc310c2c3330c99e39557176f553d\n        \"21575463638280843010398324269430826099269044274347216827212613867836435027261\",\n        # Hex: 0x16c9e55061ebae204ba4cc8bd75a079432ae2a1d0b7c9dce1665d51c640fcba2\n        \"10307601595873709700152284273816112264069230130616436755625194854815875713954\",\n    ],\n    twist_mul_by_q_y = [\n        # Hex: 0x63cf305489af5dcdc5ec698b6e2f9b9dbaae0eda9c95998dc54014671a0135a\n        \"2821565182194536844548159561693502659359617185244120367078079554186484126554\",\n        # Hex: 0x7c03cbcac41049a0704b5a7ec796f2b21807dc98fa25bd282d37f632623b0e3\n        \"3505843767911556378687030309984248845540243509899259641013678093033130930403\",\n    ],\n    twist_type = \"D\",\n    x = \"4965661367192848881\",\n)\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/bn/bn254/halo2/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"bn254\",\n    srcs = [\"bn254.cc\"],\n    hdrs = [\"bn254.h\"],\n    deps = [\n        \"//tachyon:export\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:fr\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/bn/bn254/halo2/bn254.cc",
    "content": "#include \"tachyon/math/elliptic_curves/bn/bn254/halo2/bn254.h\"\n\n#include \"tachyon/math/elliptic_curves/bn/bn254/fr.h\"\n\nnamespace tachyon::math::halo2 {\n\nvoid OverrideSubgroupGenerator() {\n  // See\n  // https://github.com/kroma-network/halo2curves/blob/c0ac193/src/bn256/fr.rs#L68-L70.\n  bn254::Fr::Config::kSubgroupGenerator = BigInt<4>({\n      UINT64_C(3483395353741361115),\n      UINT64_C(3494632259903994625),\n      UINT64_C(6657987792994187913),\n      UINT64_C(108272644256946680),\n  });\n  // See\n  // https://github.com/kroma-network/halo2curves/blob/c0ac193/src/bn256/fr.rs#L74-L83.\n  bn254::Fr::Config::kTwoAdicRootOfUnity = BigInt<4>({\n      UINT64_C(10822932506504462008),\n      UINT64_C(10978899855858987673),\n      UINT64_C(12888607242213977304),\n      UINT64_C(2119232853909229097),\n  });\n  bn254::Fr::Config::kLargeSubgroupRootOfUnity = BigInt<4>({\n      UINT64_C(9055134861510678988),\n      UINT64_C(3166494206591041163),\n      UINT64_C(11983946130272577941),\n      UINT64_C(1690279781341100183),\n  });\n}\n\nScopedSubgroupGeneratorOverrider::ScopedSubgroupGeneratorOverrider() {\n  subgroup_generator = bn254::Fr::Config::kSubgroupGenerator;\n  two_adic_root_of_unity = bn254::Fr::Config::kTwoAdicRootOfUnity;\n  large_subgroup_root_of_unity = bn254::Fr::Config::kLargeSubgroupRootOfUnity;\n\n  OverrideSubgroupGenerator();\n}\n\nScopedSubgroupGeneratorOverrider::~ScopedSubgroupGeneratorOverrider() {\n  bn254::Fr::Config::kSubgroupGenerator = subgroup_generator;\n  bn254::Fr::Config::kTwoAdicRootOfUnity = two_adic_root_of_unity;\n  bn254::Fr::Config::kLargeSubgroupRootOfUnity = large_subgroup_root_of_unity;\n}\n\n}  // namespace tachyon::math::halo2\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/bn/bn254/halo2/bn254.h",
    "content": "#ifndef TACHYON_MATH_ELLIPTIC_CURVES_BN_BN254_HALO2_BN254_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_BN_BN254_HALO2_BN254_H_\n\n#include \"tachyon/export.h\"\n#include \"tachyon/math/base/big_int.h\"\n\nnamespace tachyon::math::halo2 {\n\nTACHYON_EXPORT void OverrideSubgroupGenerator();\n\nstruct TACHYON_EXPORT ScopedSubgroupGeneratorOverrider {\n  BigInt<4> subgroup_generator;\n  BigInt<4> two_adic_root_of_unity;\n  BigInt<4> large_subgroup_root_of_unity;\n\n  ScopedSubgroupGeneratorOverrider();\n  ~ScopedSubgroupGeneratorOverrider();\n};\n\n}  // namespace tachyon::math::halo2\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_BN_BN254_HALO2_BN254_H_\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/bn/bn384_small_two_adicity/BUILD.bazel",
    "content": "load(\"@bazel_skylib//rules:common_settings.bzl\", \"string_flag\")\nload(\n    \"//tachyon/math/finite_fields/generator/prime_field_generator:build_defs.bzl\",\n    \"SMALL_SUBGROUP_ADICITY\",\n    \"SMALL_SUBGROUP_BASE\",\n    \"SUBGROUP_GENERATOR\",\n    \"generate_large_fft_prime_fields\",\n)\n\npackage(default_visibility = [\"//visibility:public\"])\n\nstring_flag(\n    name = \"fq_\" + SMALL_SUBGROUP_ADICITY,\n    build_setting_default = \"2\",\n)\n\nstring_flag(\n    name = \"fq_\" + SMALL_SUBGROUP_BASE,\n    build_setting_default = \"3\",\n)\n\nstring_flag(\n    name = \"fq_\" + SUBGROUP_GENERATOR,\n    build_setting_default = \"7\",\n)\n\n# Parameters are from https://github.com/arkworks-rs/algebra/blob/master/test-curves/src/bn384_small_two_adicity/fq.rs\ngenerate_large_fft_prime_fields(\n    name = \"fq\",\n    class_name = \"Fq\",\n    # Hex: 0x26a192ce09033aefd13bdbf17786104ad304fe31dc79b326e86a281d61074bec649bdd411682c645207c4e269a431001\n    modulus = \"5945877603251831796258517492029536515488649313567122628447476625319762940580461319088175968449723373773214087057409\",\n    namespace = \"tachyon::math::bn384_small_two_adicity\",\n    small_subgroup_adicity = \":fq_\" + SMALL_SUBGROUP_ADICITY,\n    small_subgroup_base = \":fq_\" + SMALL_SUBGROUP_BASE,\n    subgroup_generator = \":fq_\" + SUBGROUP_GENERATOR,\n)\n\nstring_flag(\n    name = \"fr_\" + SMALL_SUBGROUP_ADICITY,\n    build_setting_default = \"2\",\n)\n\nstring_flag(\n    name = \"fr_\" + SMALL_SUBGROUP_BASE,\n    build_setting_default = \"3\",\n)\n\nstring_flag(\n    name = \"fr_\" + SUBGROUP_GENERATOR,\n    build_setting_default = \"5\",\n)\n\n# Parameters are from https://github.com/arkworks-rs/algebra/blob/master/test-curves/src/bn384_small_two_adicity/fr.rs\ngenerate_large_fft_prime_fields(\n    name = \"fr\",\n    class_name = \"Fr\",\n    # Hex: 0x26a192ce09033aefd13bdbf17786104ad304fe31dc79b32684f7e52225e599a5b91f07ba93282245f13a3723b4c31001\n    modulus = \"5945877603251831796258517492029536515488649313567122628445038208291596545947608789992834434053176523624102324539393\",\n    namespace = \"tachyon::math::bn384_small_two_adicity\",\n    small_subgroup_adicity = \":fr_\" + SMALL_SUBGROUP_ADICITY,\n    small_subgroup_base = \":fr_\" + SMALL_SUBGROUP_BASE,\n    subgroup_generator = \":fr_\" + SUBGROUP_GENERATOR,\n)\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/bn/bn_curve.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_ELLIPTIC_CURVES_BN_BN_CURVE_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_BN_BN_CURVE_H_\n\n#include <functional>\n#include <vector>\n\n#include \"tachyon/base/optional.h\"\n#include \"tachyon/base/parallelize.h\"\n#include \"tachyon/math/elliptic_curves/bn/g2_prepared.h\"\n#include \"tachyon/math/elliptic_curves/pairing/pairing_friendly_curve.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename Config>\nclass BNCurve : public PairingFriendlyCurve<Config> {\n public:\n  using Base = PairingFriendlyCurve<Config>;\n  using Fp12 = typename Config::Fp12;\n  using G2Prepared = bn::G2Prepared<Config>;\n\n  // TODO(chokobole): Leave a comment to help understand readers.\n  template <typename G1AffinePointContainer, typename G2PreparedContainer>\n  static Fp12 MultiMillerLoop(const G1AffinePointContainer& a,\n                              const G2PreparedContainer& b) {\n    using Pair = typename Base::Pair;\n\n    std::vector<Pair> pairs = Base::CreatePairs(a, b);\n\n    auto callback = [](absl::Span<const Pair> pairs) {\n      Fp12 f = Fp12::One();\n      for (size_t i = std::size(Config::kAteLoopCount) - 1; i >= 1; --i) {\n        if (i != std::size(Config::kAteLoopCount) - 1) {\n          f.SquareInPlace();\n        }\n\n        for (const Pair& pair : pairs) {\n          Base::Ell(f, pair.NextEllCoeff(), pair.g1());\n        }\n\n        int8_t bit = Config::kAteLoopCount[i - 1];\n        if (bit == 1 || bit == -1) {\n          for (const Pair& pair : pairs) {\n            Base::Ell(f, pair.NextEllCoeff(), pair.g1());\n          }\n        }\n      }\n      return f;\n    };\n\n    std::vector<Fp12> results =\n        base::ParallelizeMapByChunkSize(pairs, 4, callback);\n    Fp12 f = std::accumulate(results.begin(), results.end(), Fp12::One(),\n                             std::multiplies<>());\n\n    if constexpr (Config::kXIsNegative) {\n      CHECK(f.CyclotomicInverseInPlace());\n    }\n\n    for (const Pair& pair : pairs) {\n      Base::Ell(f, pair.NextEllCoeff(), pair.g1());\n    }\n\n    for (const Pair& pair : pairs) {\n      Base::Ell(f, pair.NextEllCoeff(), pair.g1());\n    }\n\n    return f;\n  }\n\n  static Fp12 FinalExponentiation(const Fp12& f) {\n    // clang-format off\n    // f^((q¹² - 1) / r) = f^((q⁶ - 1) * ((q⁶ + 1) / φ₁₂(q)) * (φ₁₂(q) / r))\n    //                   = f^((q⁶ - 1) * ((q⁶ + 1) / (q⁴ - q² + 1)) * ((q⁴ - q² + 1) / r))\n    //                   = f^((q⁶ - 1) * (q² + 1) * ((q⁴ - q² + 1) / r))\n    //                       <--- easy part --->    <--- hard part --->\n    // clang-format on\n    // Easy part: result = f^((q⁶ - 1) * (q² + 1)).\n    // Follows, e.g., Beuchat et al page 9, by computing result as follows:\n    //   f^((q⁶ - 1) * (q² + 1)) = (conj(f) * f⁻¹)^(q² + 1)\n\n    // f1 = f.CyclotomicInverse() = f^(q⁶)\n    Fp12 f1 = unwrap(f.CyclotomicInverse());\n\n    // f2 = f⁻¹\n    Fp12 f2 = unwrap(f.Inverse());\n\n    // r = f^(q⁶ - 1)\n    Fp12 r = f1 * f2;\n\n    // f2 = f^(q⁶ - 1)\n    f2 = r;\n    // r = f^((q⁶ - 1)(q²))\n    r.FrobeniusMapInPlace(2);\n\n    // r = f^((q⁶ - 1)(q²)) * f^(q⁶ - 1)\n    // r = f^((q⁶ - 1)(q² + 1))\n    r *= f2;\n\n    // Hard part follows Laura Fuentes-Castaneda et al. \"Faster hashing to G2\"\n    // by computing:\n    //\n    // result = f^(q³ * (12x³ +  6x² + 4x - 1) +\n    //             q² * (12x³ +  6x² + 6x    ) +\n    //             q  * (12x³ +  6x² + 4x    ) +\n    //             1  * (12x³ + 12x² + 6x + 1))\n    // which equals\n    //\n    // TODO(chokobole): Add easier comment after understanding what below means.\n    // I just guess that 2x * (6x² + 3x + 1) equals to (q⁶ - 1)(q² + 1).\n    // result = f^(2x * (6x² + 3x + 1) * ((q⁴ - q² + 1) / r))\n\n    // y0 = (r)⁻ˣ\n    Fp12 y0 = Base::PowByNegX(r);\n    // y1 = (y0)² = r^(-2x)\n    Fp12& y1 = y0.CyclotomicSquareInPlace();\n    // y2 = (y1)² = r^(-4x)\n    Fp12 y2 = y1.CyclotomicSquare();\n    // y3 = y2 * y1 = r^(-6x)\n    Fp12& y3 = y2 *= y1;\n    // y4 = (y3)⁻ˣ = r^(6x²)\n    Fp12 y4 = Base::PowByNegX(y3);\n    // y5 = (y4)² = r^(12x²)\n    Fp12 y5 = y4.CyclotomicSquare();\n    // y6 = (y5)⁻ˣ = r^(-12x³)\n    Fp12 y6 = Base::PowByNegX(y5);\n    // y3 = (y3)⁻¹ = r^(6x)\n    CHECK(y3.CyclotomicInverseInPlace());\n    // y6 = (y6)⁻¹ = r^(12x³)\n    CHECK(y6.CyclotomicInverseInPlace());\n    // y7 = y6 * y4 = r^(12x³ + 6x²)\n    Fp12& y7 = y6 *= y4;\n    // y8 = y7 * y3 = r^(12x³ + 6x² + 6x)\n    Fp12 y8 = y7 * y3;\n    // y9 = y8 * y1 = r^(12x³ + 6x² + 4x)\n    Fp12 y9 = y8 * y1;\n    // y10 = y8 * y4 = r^(12x³ + 12x² + 6x)\n    Fp12 y10 = y8 * y4;\n    // y11 = y10 * r = r^(12x³ + 12x² + 6x + 1)\n    Fp12& y11 = y10 *= r;\n    // y12 = (y9)^q = r^(q * (12x³ + 6x² + 4x))\n    Fp12 y12 = y9;\n    y12.FrobeniusMapInPlace(1);\n    // y13 = y12 * y11 = r^(q * (12x³ +  6x² + 4x) +\n    //                      1 * (12x³ + 12x² + 6x + 1))\n    Fp12& y13 = y12 *= y11;\n    // y8 = (y8)^q² = r^(q² * (12x³ + 6x² + 6x))\n    y8.FrobeniusMapInPlace(2);\n    // y14 = y8 * y13 = r^(q² * (12x³ +  6x² + 6x) +\n    //                     q  * (12x³ +  6x² + 4x) +\n    //                     1  * (12x³ + 12x² + 6x + 1))\n    Fp12& y14 = y8 *= y13;\n    CHECK(r.CyclotomicInverseInPlace());\n    // y15 = r⁻¹ * y9 = r^(12x³ + 6x² + 4x - 1)\n    Fp12& y15 = r *= y9;\n    // y15 = (y15)^q³ = r^(q³ * (12x³ + 6x² + 4x - 1))\n    y15.FrobeniusMapInPlace(3);\n    // y16 = y15 * y14 = r^(q³ * (12x³ +  6x² + 4x - 1) +\n    //                      q² * (12x³ +  6x² + 6x) +\n    //                      q  * (12x³ +  6x² + 4x) +\n    //                      1  * (12x³ + 12x² + 6x + 1))\n    Fp12& y16 = y15 *= y14;\n    return y16;\n  }\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_BN_BN_CURVE_H_\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/bn/g2_prepared.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_ELLIPTIC_CURVES_BN_G2_PREPARED_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_BN_G2_PREPARED_H_\n\n#include <utility>\n\n#include \"tachyon/base/optional.h\"\n#include \"tachyon/math/elliptic_curves/pairing/g2_prepared_base.h\"\n#include \"tachyon/math/elliptic_curves/pairing/g2_projective.h\"\n\nnamespace tachyon::math::bn {\n\ntemplate <typename BNCurveConfig>\nclass G2Prepared : public G2PreparedBase<BNCurveConfig> {\n public:\n  using Config = BNCurveConfig;\n  using G2Curve = typename Config::G2Curve;\n  using Fp2 = typename G2Curve::BaseField;\n  using Fp = typename Fp2::BaseField;\n  using G2AffinePoint = typename G2Curve::AffinePoint;\n\n  G2Prepared() = default;\n  explicit G2Prepared(const EllCoeffs<Fp2>& ell_coeffs)\n      : G2PreparedBase<BNCurveConfig>(ell_coeffs) {}\n  explicit G2Prepared(EllCoeffs<Fp2>&& ell_coeffs)\n      : G2PreparedBase<BNCurveConfig>(std::move(ell_coeffs)) {}\n\n  static G2Prepared From(const G2AffinePoint& q) {\n    if (q.IsZero()) {\n      return {};\n    } else {\n      G2Projective<Config> r(q.x(), q.y(), Fp2::One());\n\n      EllCoeffs<Fp2> ell_coeffs;\n      // NOTE(chokobole): |Config::kAteLoopCount| consists of elements\n      // from [-1, 0, 1]. We reserve space in |ell_coeffs| assuming that these\n      // elements are uniformly distributed.\n      size_t size = std::size(Config::kAteLoopCount);\n      ell_coeffs.reserve(/*double=*/size + /*add=*/size * 2 / 3);\n\n      G2AffinePoint neg_q = -q;\n\n      // NOTE(chokobole): skip the fist.\n      for (size_t i = size - 2; i != SIZE_MAX; --i) {\n        ell_coeffs.push_back(r.DoubleInPlace(Fp::TwoInv()));\n\n        switch (Config::kAteLoopCount[i]) {\n          case 1:\n            ell_coeffs.push_back(r.AddInPlace(q));\n            break;\n          case -1:\n            ell_coeffs.push_back(r.AddInPlace(neg_q));\n            break;\n          default:\n            continue;\n        }\n      }\n\n      G2AffinePoint q1 = MulByCharacteristic(q);\n      G2AffinePoint q2 = MulByCharacteristic(q1);\n      q2.NegateInPlace();\n\n      if constexpr (Config::kXIsNegative) {\n        r.NegateInPlace();\n      }\n\n      ell_coeffs.push_back(r.AddInPlace(q1));\n      ell_coeffs.push_back(r.AddInPlace(q2));\n\n      return G2Prepared(std::move(ell_coeffs));\n    }\n  }\n\n private:\n  static G2AffinePoint MulByCharacteristic(const G2AffinePoint& r) {\n    Fp2 x = r.x();\n    x.FrobeniusMapInPlace(1);\n    x *= Config::kTwistMulByQX;\n    Fp2 y = r.y();\n    y.FrobeniusMapInPlace(1);\n    y *= Config::kTwistMulByQY;\n    return G2AffinePoint(std::move(x), std::move(y));\n  }\n};\n\n}  // namespace tachyon::math::bn\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_BN_G2_PREPARED_H_\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/bn/generator/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_binary\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_binary(\n    name = \"generator\",\n    srcs = [\"generator.cc\"],\n    data = [\"curve.h.tpl\"],\n    deps = [\n        \"//tachyon/base/console\",\n        \"//tachyon/base/files:file_path_flag\",\n        \"//tachyon/base/flag:flag_parser\",\n        \"//tachyon/base/strings:string_number_conversions\",\n        \"//tachyon/build:cc_writer\",\n        \"//tachyon/math/base/gmp:bit_traits\",\n        \"//tachyon/math/base/gmp:gmp_util\",\n        \"//tachyon/math/elliptic_curves/pairing:twist_type\",\n        \"//tachyon/math/finite_fields/generator:generator_util\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/bn/generator/build_defs.bzl",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\ndef _generate_bn_curve_impl(ctx):\n    curve_hdr_path = ctx.expand_location(\"$(location @kroma_network_tachyon//tachyon/math/elliptic_curves/bn/generator:curve.h.tpl)\", [ctx.attr.curve_hdr_path])\n\n    arguments = [\n        \"--out=%s\" % (ctx.outputs.out.path),\n        \"--namespace=%s\" % (ctx.attr.namespace),\n        \"--fq12_hdr=%s\" % (ctx.attr.fq12_hdr),\n        \"--g1_hdr=%s\" % (ctx.attr.g1_hdr),\n        \"--g2_hdr=%s\" % (ctx.attr.g2_hdr),\n        \"-x=%s\" % (ctx.attr.x),\n        \"--twist_type=%s\" % (ctx.attr.twist_type),\n        \"--curve_hdr_path=%s\" % (curve_hdr_path),\n    ]\n    if len(ctx.attr.class_name) > 0:\n        arguments.append(\"--class=%s\" % (ctx.attr.class_name))\n\n    for twist_mul_by_q_x in ctx.attr.twist_mul_by_q_x:\n        arguments.append(\"--twist_mul_by_q_x=%s\" % (twist_mul_by_q_x))\n\n    for twist_mul_by_q_y in ctx.attr.twist_mul_by_q_y:\n        arguments.append(\"--twist_mul_by_q_y=%s\" % (twist_mul_by_q_y))\n\n    ctx.actions.run(\n        inputs = [ctx.files.curve_hdr_path[0]],\n        tools = [ctx.executable._tool],\n        executable = ctx.executable._tool,\n        outputs = [ctx.outputs.out],\n        arguments = arguments,\n    )\n\n    return [DefaultInfo(files = depset([ctx.outputs.out]))]\n\ngenerate_bn_curve = rule(\n    implementation = _generate_bn_curve_impl,\n    attrs = {\n        \"out\": attr.output(mandatory = True),\n        \"namespace\": attr.string(mandatory = True),\n        \"class_name\": attr.string(),\n        \"fq12_hdr\": attr.string(mandatory = True),\n        \"g1_hdr\": attr.string(mandatory = True),\n        \"g2_hdr\": attr.string(mandatory = True),\n        \"x\": attr.string(mandatory = True),\n        \"twist_mul_by_q_x\": attr.string_list(mandatory = True),\n        \"twist_mul_by_q_y\": attr.string_list(mandatory = True),\n        \"twist_type\": attr.string(mandatory = True, values = [\"M\", \"D\"]),\n        \"curve_hdr_path\": attr.label(\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/math/elliptic_curves/bn/generator:curve.h.tpl\"),\n        ),\n        \"_tool\": attr.label(\n            # TODO(chokobole): Change to \"exec\", so we can build on macos.\n            cfg = \"target\",\n            executable = True,\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/math/elliptic_curves/bn/generator\"),\n        ),\n    },\n)\n\ndef generate_bn_curves(\n        name,\n        namespace,\n        fq12_hdr,\n        fq12_dep,\n        g1_hdr,\n        g1_dep,\n        g2_hdr,\n        g2_dep,\n        x,\n        twist_mul_by_q_x,\n        twist_mul_by_q_y,\n        twist_type,\n        class_name = \"\",\n        **kwargs):\n    for n in [\n        (\"{}_gen_hdr\".format(name), \"{}.h\".format(name)),\n    ]:\n        generate_bn_curve(\n            namespace = namespace,\n            class_name = class_name,\n            fq12_hdr = fq12_hdr,\n            g1_hdr = g1_hdr,\n            g2_hdr = g2_hdr,\n            x = x,\n            twist_mul_by_q_x = twist_mul_by_q_x,\n            twist_mul_by_q_y = twist_mul_by_q_y,\n            twist_type = twist_type,\n            name = n[0],\n            out = n[1],\n        )\n\n    tachyon_cc_library(\n        name = name,\n        hdrs = [\":{}_gen_hdr\".format(name)],\n        deps = [\n            fq12_dep,\n            g1_dep,\n            g2_dep,\n            \"//tachyon/base:logging\",\n            \"//tachyon/math/elliptic_curves/bn:bn_curve\",\n            \"//tachyon/math/elliptic_curves/pairing:twist_type\",\n            \"@com_google_absl//absl/base\",\n        ],\n        **kwargs\n    )\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/bn/generator/curve.h.tpl",
    "content": "// clang-format off\n#include \"absl/base/call_once.h\"\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn_curve.h\"\n#include \"tachyon/math/elliptic_curves/pairing/twist_type.h\"\n#include \"%{fq12_hdr}\"\n#include \"%{g1_hdr}\"\n#include \"%{g2_hdr}\"\n\nnamespace %{namespace} {\n\ntemplate <typename Fq, typename Fq2, typename Fq6, typename Fq12, typename _G1Curve, typename _G2Curve>\nclass %{class}Config {\n public:\n  constexpr static const char* kName = \"%{namespace}::%{class}\";\n\n  constexpr static BigInt<%{x_size}> kX = BigInt<%{x_size}>({\n    %{x}\n  });\n\n  constexpr static bool kXIsNegative = %{x_is_negative};\n  constexpr static int8_t kAteLoopCount[] = {\n    %{ate_loop_count}\n  };\n  constexpr static TwistType kTwistType = TwistType::k%{twist_type};\n\n  using Fp = Fq;\n  using Fp2 = Fq2;\n  using Fp6 = Fq6;\n  using Fp12 = Fq12;\n  using G1Curve = _G1Curve;\n  using G2Curve = _G2Curve;\n\n  // NOTE(chokobole): Make them constexpr.\n  static Fq2 kTwistMulByQX;\n  static Fq2 kTwistMulByQY;\n\n  static void Init() {\n    static absl::once_flag once;\n    absl::call_once(once, &%{class}Config::DoInit);\n  }\n\n private:\n  static void DoInit() {\n    // TODO(chokobole): This line below is needed by |GenerateInitExtField()|.\n    // Later, I want GenerateInitExtField() to accept BasePrimeField as an argument.\n    using BasePrimeField = Fq;\n%{twist_mul_by_q_x_init_code}\n%{twist_mul_by_q_y_init_code}\n    G1Curve::Init();\n    G2Curve::Init();\n    VLOG(1) << \"%{namespace}::%{class} initialized\";\n  }\n};\n\ntemplate <typename Fq, typename Fq2, typename Fq6, typename Fq12, typename G1Curve, typename G2Curve>\nFq2 %{class}Config<Fq, Fq2, Fq6, Fq12, G1Curve, G2Curve>::kTwistMulByQX;\ntemplate <typename Fq, typename Fq2, typename Fq6, typename Fq12, typename G1Curve, typename G2Curve>\nFq2 %{class}Config<Fq, Fq2, Fq6, Fq12, G1Curve, G2Curve>::kTwistMulByQY;\n\nusing %{class}Curve = BNCurve<%{class}Config<Fq, Fq2, Fq6, Fq12, G1Curve, G2Curve>>;\n\n}  // namespace %{namespace}\n// clang-format on\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/bn/generator/generator.cc",
    "content": "#include \"absl/strings/str_replace.h\"\n\n#include \"tachyon/base/console/iostream.h\"\n#include \"tachyon/base/files/file_path_flag.h\"\n#include \"tachyon/base/flag/flag_parser.h\"\n#include \"tachyon/base/strings/string_number_conversions.h\"\n#include \"tachyon/build/cc_writer.h\"\n#include \"tachyon/math/base/gmp/gmp_util.h\"\n#include \"tachyon/math/elliptic_curves/pairing/twist_type.h\"\n#include \"tachyon/math/finite_fields/generator/generator_util.h\"\n\nnamespace tachyon {\n\ntemplate <size_t N>\nstd::vector<int8_t> ComputeAteLoopCount(const mpz_class& six_x_plus_2) {\n  math::BigInt<N> x(0);\n  math::gmp::CopyLimbs(six_x_plus_2, x.limbs);\n  return x.ToNAF();\n}\n\nstd::vector<int8_t> ComputeAteLoopCount(const mpz_class& x) {\n  mpz_class six_x_plus_2 = mpz_class(6) * x + mpz_class(2);\n  size_t limb_size = math::gmp::GetLimbSize(six_x_plus_2);\n  switch (limb_size) {\n    case 1:\n      return ComputeAteLoopCount<1>(six_x_plus_2);\n    case 2:\n      return ComputeAteLoopCount<2>(six_x_plus_2);\n    case 3:\n      return ComputeAteLoopCount<3>(six_x_plus_2);\n    case 4:\n      return ComputeAteLoopCount<4>(six_x_plus_2);\n    case 5:\n      return ComputeAteLoopCount<5>(six_x_plus_2);\n    case 6:\n      return ComputeAteLoopCount<6>(six_x_plus_2);\n    case 7:\n      return ComputeAteLoopCount<7>(six_x_plus_2);\n    case 8:\n      return ComputeAteLoopCount<8>(six_x_plus_2);\n    case 9:\n      return ComputeAteLoopCount<9>(six_x_plus_2);\n  }\n  NOTREACHED();\n  return {};\n}\n\nstruct GenerationConfig : public build::CcWriter {\n  base::FilePath curve_hdr_tpl_path;\n\n  std::string ns_name;\n  std::string class_name;\n  base::FilePath fq12_hdr;\n  base::FilePath g1_hdr;\n  base::FilePath g2_hdr;\n  std::string x;\n  std::vector<std::string> twist_mul_by_q_x;\n  std::vector<std::string> twist_mul_by_q_y;\n  math::TwistType twist_type;\n\n  int GenerateConfigHdr() const;\n};\n\nint GenerationConfig::GenerateConfigHdr() const {\n  std::map<std::string, std::string> replace_map = {\n      {\"%{namespace}\", ns_name},\n      {\"%{class}\", class_name},\n      {\"%{fq12_hdr}\", fq12_hdr.value()},\n      {\"%{g1_hdr}\", g1_hdr.value()},\n      {\"%{g2_hdr}\", g2_hdr.value()},\n      {\"%{twist_type}\", TwistTypeToString(twist_type)},\n  };\n\n  mpz_class x_mpz = math::gmp::FromDecString(x);\n  replace_map[\"%{x_is_negative}\"] =\n      base::BoolToString(math::gmp::IsNegative(x_mpz));\n  x_mpz = math::gmp::GetAbs(x_mpz);\n  replace_map[\"%{x}\"] = math::MpzClassToString(x_mpz);\n  replace_map[\"%{x_size}\"] =\n      base::NumberToString(math::gmp::GetLimbSize(x_mpz));\n\n  std::vector<int8_t> ate_loop_count = ComputeAteLoopCount(x_mpz);\n  replace_map[\"%{ate_loop_count}\"] = absl::StrJoin(ate_loop_count, \", \");\n\n  replace_map[\"%{twist_mul_by_q_x_init_code}\"] =\n      math::GenerateInitExtField(\"kTwistMulByQX\", \"Fq2\", twist_mul_by_q_x, 2);\n  replace_map[\"%{twist_mul_by_q_y_init_code}\"] =\n      math::GenerateInitExtField(\"kTwistMulByQY\", \"Fq2\", twist_mul_by_q_y, 2);\n\n  std::string tpl_content;\n  CHECK(base::ReadFileToString(curve_hdr_tpl_path, &tpl_content));\n  std::string content = absl::StrReplaceAll(tpl_content, replace_map);\n  return WriteHdr(content, false);\n}\n\nint RealMain(int argc, char** argv) {\n  GenerationConfig config;\n  config.generator = \"//tachyon/math/elliptic_curves/bn/generator\";\n\n  base::FlagParser parser;\n  parser.AddFlag<base::FilePathFlag>(&config.out)\n      .set_long_name(\"--out\")\n      .set_help(\"path to output\");\n  parser.AddFlag<base::StringFlag>(&config.ns_name)\n      .set_long_name(\"--namespace\")\n      .set_required();\n  parser.AddFlag<base::StringFlag>(&config.class_name).set_long_name(\"--class\");\n  parser.AddFlag<base::FilePathFlag>(&config.fq12_hdr)\n      .set_long_name(\"--fq12_hdr\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.g1_hdr)\n      .set_long_name(\"--g1_hdr\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.g2_hdr)\n      .set_long_name(\"--g2_hdr\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.curve_hdr_tpl_path)\n      .set_long_name(\"--curve_hdr_path\")\n      .set_required();\n  parser.AddFlag<base::Flag<std::string>>(&config.x)\n      .set_short_name(\"-x\")\n      .set_required();\n  parser.AddFlag<base::Flag<std::vector<std::string>>>(&config.twist_mul_by_q_x)\n      .set_long_name(\"--twist_mul_by_q_x\")\n      .set_required();\n  parser.AddFlag<base::Flag<std::vector<std::string>>>(&config.twist_mul_by_q_y)\n      .set_long_name(\"--twist_mul_by_q_y\")\n      .set_required();\n  parser.AddFlag<base::Flag<math::TwistType>>(&config.twist_type)\n      .set_long_name(\"--twist_type\")\n      .set_required();\n  std::string error;\n  if (!parser.Parse(argc, argv, &error)) {\n    tachyon_cerr << error << std::endl;\n    return 1;\n  }\n\n  if (base::EndsWith(config.out.value(), \".h\")) {\n    return config.GenerateConfigHdr();\n  } else {\n    tachyon_cerr << \"suffix not supported:\" << config.out << std::endl;\n    return 1;\n  }\n}\n\n}  // namespace tachyon\n\nint main(int argc, char** argv) { return tachyon::RealMain(argc, argv); }\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/msm/BUILD.bazel",
    "content": "load(\"//bazel:tachyon.bzl\", \"if_gpu_is_configured\")\nload(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"fixed_base_msm\",\n    hdrs = [\"fixed_base_msm.h\"],\n    deps = [\n        \":msm_ctx\",\n        \"//tachyon/base:parallelize\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/math/base:bit_iterator\",\n        \"//tachyon/math/base:semigroups\",\n        \"//tachyon/math/geometry:point_conversions\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"glv\",\n    hdrs = [\"glv.h\"],\n    deps = [\n        \"//tachyon/math/base:bit_iterator\",\n        \"//tachyon/math/base/gmp:bit_traits\",\n        \"//tachyon/math/base/gmp:signed_value\",\n        \"//tachyon/math/elliptic_curves:semigroups\",\n        \"//tachyon/math/matrix:gmp_num_traits\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"msm_ctx\",\n    hdrs = [\"msm_ctx.h\"],\n    deps = [\"//tachyon:export\"],\n)\n\ntachyon_cc_library(\n    name = \"msm_util\",\n    hdrs = [\"msm_util.h\"],\n    deps = [\"//tachyon/base:template_util\"],\n)\n\ntachyon_cc_library(\n    name = \"variable_base_msm\",\n    hdrs = [\"variable_base_msm.h\"],\n    deps = [\"//tachyon/math/elliptic_curves/msm/algorithms/pippenger:pippenger_adapter\"],\n)\n\ntachyon_cc_library(\n    name = \"variable_base_msm_gpu\",\n    hdrs = [\"variable_base_msm_gpu.h\"],\n    deps = [\"//tachyon/math/elliptic_curves/msm/algorithms/icicle\"],\n)\n\ntachyon_cc_unittest(\n    name = \"msm_unittests\",\n    srcs = [\n        \"fixed_base_msm_unittest.cc\",\n        \"glv_unittest.cc\",\n        \"variable_base_msm_unittest.cc\",\n    ],\n    deps = [\n        \":glv\",\n        \"//tachyon/math/elliptic_curves/bls12/bls12_381:g1\",\n        \"//tachyon/math/elliptic_curves/bls12/bls12_381:g2\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:g1\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:g2\",\n        \"//tachyon/math/elliptic_curves/msm/test:fixed_base_msm_test_set\",\n        \"//tachyon/math/elliptic_curves/msm/test:variable_base_msm_test_set\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"msm_gpu_unittests\",\n    srcs = if_gpu_is_configured([\"variable_base_msm_gpu_unittest.cc\"]),\n    deps = [\n        \":variable_base_msm_gpu\",\n        \"//tachyon/device/gpu:scoped_mem_pool\",\n        \"//tachyon/device/gpu:scoped_stream\",\n        \"//tachyon/math/elliptic_curves/msm/test:variable_base_msm_test_set\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/msm/algorithms/icicle/BUILD.bazel",
    "content": "load(\"@icicle//:build_defs.bzl\", \"BLS12_381\", \"BN254\", \"icicle_defines\")\nload(\"@local_config_cuda//cuda:build_defs.bzl\", \"if_cuda\")\nload(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cuda_library\")\n\ntachyon_cuda_library(\n    name = \"icicle_msm_bls12_381_g1\",\n    srcs = if_cuda([\"icicle_msm_bls12_381_g1.cc\"]),\n    hdrs = [\"icicle_msm_bls12_381_g1.h\"],\n    local_defines = icicle_defines(BLS12_381),\n    deps = [\n        \":icicle_msm\",\n        \":icicle_msm_utils\",\n        \"//tachyon/base:bit_cast\",\n        \"//tachyon/device/gpu:gpu_enums\",\n        \"//tachyon/device/gpu:gpu_logging\",\n        \"//tachyon/device/gpu:gpu_memory\",\n        \"@icicle//:msm_bls12_381\",\n    ] + if_cuda([\n        \"@local_config_cuda//cuda:cudart_static\",\n    ]),\n)\n\ntachyon_cuda_library(\n    name = \"icicle_msm_bls12_381_g2\",\n    srcs = if_cuda([\"icicle_msm_bls12_381_g2.cc\"]),\n    hdrs = [\"icicle_msm_bls12_381_g2.h\"],\n    local_defines = icicle_defines(BLS12_381),\n    deps = [\n        \":icicle_msm\",\n        \":icicle_msm_utils\",\n        \"//tachyon/base:bit_cast\",\n        \"//tachyon/device/gpu:gpu_enums\",\n        \"//tachyon/device/gpu:gpu_logging\",\n        \"//tachyon/device/gpu:gpu_memory\",\n        \"@icicle//:msm_bls12_381\",\n    ] + if_cuda([\n        \"@local_config_cuda//cuda:cudart_static\",\n    ]),\n)\n\ntachyon_cuda_library(\n    name = \"icicle_msm_bn254_g1\",\n    srcs = if_cuda([\"icicle_msm_bn254_g1.cc\"]),\n    hdrs = [\"icicle_msm_bn254_g1.h\"],\n    local_defines = icicle_defines(BN254),\n    deps = [\n        \":icicle_msm\",\n        \":icicle_msm_utils\",\n        \"//tachyon/base:bit_cast\",\n        \"//tachyon/device/gpu:gpu_enums\",\n        \"//tachyon/device/gpu:gpu_logging\",\n        \"//tachyon/device/gpu:gpu_memory\",\n        \"@icicle//:msm_bn254\",\n    ] + if_cuda([\n        \"@local_config_cuda//cuda:cudart_static\",\n    ]),\n)\n\ntachyon_cuda_library(\n    name = \"icicle_msm_bn254_g2\",\n    srcs = if_cuda([\"icicle_msm_bn254_g2.cc\"]),\n    hdrs = [\"icicle_msm_bn254_g2.h\"],\n    local_defines = icicle_defines(BN254),\n    deps = [\n        \":icicle_msm\",\n        \":icicle_msm_utils\",\n        \"//tachyon/base:bit_cast\",\n        \"//tachyon/device/gpu:gpu_enums\",\n        \"//tachyon/device/gpu:gpu_logging\",\n        \"//tachyon/device/gpu:gpu_memory\",\n        \"@icicle//:msm_bn254\",\n    ] + if_cuda([\n        \"@local_config_cuda//cuda:cudart_static\",\n    ]),\n)\n\ntachyon_cc_library(\n    name = \"icicle\",\n    visibility = [\"//visibility:public\"],\n    deps = [\n        \":icicle_msm_bls12_381_g1\",\n        \":icicle_msm_bls12_381_g2\",\n        \":icicle_msm_bn254_g1\",\n        \":icicle_msm_bn254_g2\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"icicle_msm\",\n    hdrs = [\"icicle_msm.h\"],\n    deps = [\n        \"//tachyon:export\",\n        \"//tachyon/device/gpu:gpu_device_functions\",\n        \"//tachyon/math/elliptic_curves/bls12/bls12_381:g1\",\n        \"//tachyon/math/elliptic_curves/bls12/bls12_381:g2\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:g1\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:g2\",\n        \"//tachyon/math/geometry:jacobian_point\",\n        \"@com_google_absl//absl/types:span\",\n        \"@icicle//:hdrs\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"icicle_msm_utils\",\n    srcs = if_cuda([\"icicle_msm_utils.cc\"]),\n    hdrs = [\"icicle_msm_utils.h\"],\n    deps = [\n        \"//tachyon:export\",\n        \"//tachyon/base:bits\",\n        \"//tachyon/device/gpu:gpu_memory\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/msm/algorithms/icicle/icicle_msm.h",
    "content": "#ifndef TACHYON_MATH_ELLIPTIC_CURVES_MSM_ALGORITHMS_ICICLE_ICICLE_MSM_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_MSM_ALGORITHMS_ICICLE_ICICLE_MSM_H_\n\n#include <memory>\n\n#include \"absl/types/span.h\"\n#include \"third_party/icicle/include/msm/msm_config.h\"\n\n#include \"tachyon/device/gpu/gpu_device_functions.h\"\n#include \"tachyon/export.h\"\n#include \"tachyon/math/elliptic_curves/bls12/bls12_381/g1.h\"\n#include \"tachyon/math/elliptic_curves/bls12/bls12_381/g2.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/g1.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/g2.h\"\n#include \"tachyon/math/geometry/projective_point.h\"\n\nnamespace tachyon::math {\n\nstruct TACHYON_EXPORT IcicleMSMOptions {\n  int points_size = 0;\n  int precompute_factor = 1;\n  int c = 0;\n  int bitsize = 0;\n  int large_bucket_factor = 10;\n  int batch_size = 1;\n  bool are_scalars_on_device = false;\n  bool are_scalars_montgomery_form = true;\n  bool are_points_on_device = false;\n  bool are_points_montgomery_form = true;\n  bool are_results_on_device = false;\n  bool is_big_triangle = false;\n  bool is_async = false;\n};\n\ntemplate <typename Point>\nclass IcicleMSM {\n public:\n  using Curve = typename Point::Curve;\n  using ScalarField = typename Point::ScalarField;\n\n  IcicleMSM(gpuMemPool_t mem_pool, gpuStream_t stream,\n            const IcicleMSMOptions& options = IcicleMSMOptions())\n      : mem_pool_(mem_pool), stream_(stream) {\n    ::device_context::DeviceContext ctx{stream_, /*device_id=*/0, mem_pool_};\n    config_.reset(new ::msm::MSMConfig{\n        ctx,\n        options.points_size,\n        options.precompute_factor,\n        options.c,\n        options.bitsize,\n        options.large_bucket_factor,\n        options.batch_size,\n        options.are_scalars_on_device,\n        options.are_scalars_montgomery_form,\n        // TODO(chokobole): Considering KZG commitment, bases can be loaded to\n        // the device just once initially.\n        options.are_points_on_device,\n        options.are_points_montgomery_form,\n        options.are_results_on_device,\n        options.is_big_triangle,\n        options.is_async,\n    });\n    VLOG(1) << \"IcicleMSM is created\";\n  }\n  IcicleMSM(const IcicleMSM& other) = delete;\n  IcicleMSM& operator=(const IcicleMSM& other) = delete;\n\n  [[nodiscard]] bool Run(absl::Span<const Point> bases,\n                         absl::Span<const ScalarField> cpu_scalars,\n                         ProjectivePoint<Curve>* cpu_result);\n\n private:\n  gpuMemPool_t mem_pool_ = nullptr;\n  gpuStream_t stream_ = nullptr;\n  std::unique_ptr<::msm::MSMConfig> config_;\n};\n\ntemplate <>\nTACHYON_EXPORT bool IcicleMSM<bls12_381::G1AffinePoint>::Run(\n    absl::Span<const bls12_381::G1AffinePoint> bases,\n    absl::Span<const ScalarField> cpu_scalars,\n    ProjectivePoint<Curve>* cpu_result);\n\ntemplate <>\nTACHYON_EXPORT bool IcicleMSM<bls12_381::G2AffinePoint>::Run(\n    absl::Span<const bls12_381::G2AffinePoint> bases,\n    absl::Span<const ScalarField> cpu_scalars,\n    ProjectivePoint<Curve>* cpu_result);\n\ntemplate <>\nTACHYON_EXPORT bool IcicleMSM<bn254::G1AffinePoint>::Run(\n    absl::Span<const bn254::G1AffinePoint> bases,\n    absl::Span<const ScalarField> cpu_scalars,\n    ProjectivePoint<Curve>* cpu_result);\n\ntemplate <>\nTACHYON_EXPORT bool IcicleMSM<bn254::G2AffinePoint>::Run(\n    absl::Span<const bn254::G2AffinePoint> bases,\n    absl::Span<const ScalarField> cpu_scalars,\n    ProjectivePoint<Curve>* cpu_result);\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_MSM_ALGORITHMS_ICICLE_ICICLE_MSM_H_\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/msm/algorithms/icicle/icicle_msm_bls12_381_g1.cc",
    "content": "#include \"tachyon/math/elliptic_curves/msm/algorithms/icicle/icicle_msm_bls12_381_g1.h\"\n\n#include \"third_party/icicle/src/msm/msm.cu.cc\"  // NOLINT(build/include)\n\n#include \"tachyon/base/bit_cast.h\"\n#include \"tachyon/device/gpu/gpu_enums.h\"\n#include \"tachyon/device/gpu/gpu_logging.h\"\n#include \"tachyon/device/gpu/gpu_memory.h\"\n#include \"tachyon/math/elliptic_curves/msm/algorithms/icicle/icicle_msm.h\"\n#include \"tachyon/math/elliptic_curves/msm/algorithms/icicle/icicle_msm_utils.h\"\n\ngpuError_t tachyon_bls12_381_g1_msm_cuda(const ::bls12_381::scalar_t* scalars,\n                                         const ::bls12_381::affine_t* points,\n                                         int msm_size, ::msm::MSMConfig& config,\n                                         ::bls12_381::projective_t* out) {\n  return ::msm::msm(scalars, points, msm_size, config, out);\n}\n\nnamespace tachyon::math {\n\ntemplate <>\nbool IcicleMSM<bls12_381::G1AffinePoint>::Run(\n    absl::Span<const bls12_381::G1AffinePoint> bases,\n    absl::Span<const bls12_381::Fr> cpu_scalars,\n    ProjectivePoint<Curve>* cpu_result) {\n#if FIELD_ID != BLS12_381\n#error Only BLS12_381 is supported\n#endif\n\n  size_t bases_size = bases.size();\n  size_t scalars_size = cpu_scalars.size();\n\n  if (bases_size != scalars_size) {\n    LOG(ERROR) << \"bases_size and scalars_size don't match\";\n    return false;\n  }\n\n  device::gpu::gpuPointerAttributes bases_attributes{};\n  RETURN_AND_LOG_IF_GPU_ERROR(\n      device::gpu::GpuPointerGetAttributes(&bases_attributes, bases.data()),\n      \"Failed to GpuPointerGetAttributes()\");\n\n  config_->are_points_on_device =\n      bases_attributes.type != gpuMemoryTypeUnregistered &&\n      bases_attributes.type != gpuMemoryTypeHost;\n\n  size_t bitsize =\n      static_cast<size_t>((config_->bitsize == 0) ? ::bls12_381::scalar_t::NBITS\n                                                  : config_->bitsize);\n\n  size_t divisions = DetermineMsmDivisionsForMemory(\n      sizeof(::bls12_381::scalar_t), sizeof(::bls12_381::affine_t),\n      sizeof(::bls12_381::projective_t), bases_size, config_->c, bitsize,\n      static_cast<size_t>(config_->precompute_factor),\n      static_cast<size_t>(config_->batch_size));\n\n  size_t offset = bases_size / divisions;\n  size_t remainder = bases_size % divisions;\n  ::bls12_381::projective_t final_value = ::bls12_381::projective_t::zero();\n  for (size_t idx = 0; idx < divisions; ++idx) {\n    size_t start_idx = idx * offset;\n    size_t data_size =\n        ((idx == divisions - 1) && (remainder != 0)) ? remainder : offset;\n    ::bls12_381::projective_t ret;\n    gpuError_t error = tachyon_bls12_381_g1_msm_cuda(\n        reinterpret_cast<const ::bls12_381::scalar_t*>(&cpu_scalars[start_idx]),\n        reinterpret_cast<const ::bls12_381::affine_t*>(&bases[start_idx]),\n        data_size, *config_, &ret);\n    if (error != gpuSuccess) {\n      GPU_LOG(ERROR, error) << \"Failed tachyon_bls12_381_g1_msm_cuda()\";\n      return false;\n    }\n    final_value = final_value + ret;\n  }\n  final_value = ::bls12_381::projective_t::to_montgomery(final_value);\n  // TODO(chokobole): Change it to |base::bit_cast| again if the\n  // |::bls12_381::projective_t| becomes trivially copyable.\n  *cpu_result = *reinterpret_cast<ProjectivePoint<Curve>*>(&final_value);\n  return true;\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/msm/algorithms/icicle/icicle_msm_bls12_381_g1.h",
    "content": "#ifndef TACHYON_MATH_ELLIPTIC_CURVES_MSM_ALGORITHMS_ICICLE_ICICLE_MSM_BLS12_381_G1_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_MSM_ALGORITHMS_ICICLE_ICICLE_MSM_BLS12_381_G1_H_\n\n#include \"third_party/icicle/include/curves/params/bls12_381.cu.h\"\n#include \"third_party/icicle/include/msm/msm.cu.h\"\n\n#include \"tachyon/device/gpu/gpu_device_functions.h\"\n\nextern \"C\" gpuError_t tachyon_bls12_381_g1_msm_cuda(\n    const ::bls12_381::scalar_t* scalars, const ::bls12_381::affine_t* points,\n    int msm_size, ::msm::MSMConfig& config, ::bls12_381::projective_t* out);\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_MSM_ALGORITHMS_ICICLE_ICICLE_MSM_BLS12_381_G1_H_\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/msm/algorithms/icicle/icicle_msm_bls12_381_g2.cc",
    "content": "#include \"tachyon/math/elliptic_curves/msm/algorithms/icicle/icicle_msm_bls12_381_g2.h\"\n\n#include \"third_party/icicle/src/msm/msm.cu.cc\"  // NOLINT(build/include)\n\n#include \"tachyon/base/bit_cast.h\"\n#include \"tachyon/device/gpu/gpu_enums.h\"\n#include \"tachyon/device/gpu/gpu_logging.h\"\n#include \"tachyon/device/gpu/gpu_memory.h\"\n#include \"tachyon/math/elliptic_curves/msm/algorithms/icicle/icicle_msm.h\"\n#include \"tachyon/math/elliptic_curves/msm/algorithms/icicle/icicle_msm_utils.h\"\n\ngpuError_t tachyon_bls12_381_g2_msm_cuda(const ::bls12_381::scalar_t* scalars,\n                                         const ::bls12_381::g2_affine_t* points,\n                                         int msm_size, ::msm::MSMConfig& config,\n                                         ::bls12_381::g2_projective_t* out) {\n  return ::msm::msm(scalars, points, msm_size, config, out);\n}\n\nnamespace tachyon::math {\n\ntemplate <>\nbool IcicleMSM<bls12_381::G2AffinePoint>::Run(\n    absl::Span<const bls12_381::G2AffinePoint> bases,\n    absl::Span<const bls12_381::Fr> cpu_scalars,\n    ProjectivePoint<Curve>* cpu_result) {\n#if FIELD_ID != BLS12_381\n#error Only BLS12_381 is supported\n#endif\n\n  size_t bases_size = bases.size();\n  size_t scalars_size = cpu_scalars.size();\n\n  if (bases_size != scalars_size) {\n    LOG(ERROR) << \"bases_size and scalars_size don't match\";\n    return false;\n  }\n\n  device::gpu::gpuPointerAttributes bases_attributes{};\n  RETURN_AND_LOG_IF_GPU_ERROR(\n      device::gpu::GpuPointerGetAttributes(&bases_attributes, bases.data()),\n      \"Failed to GpuPointerGetAttributes()\");\n\n  config_->are_points_on_device =\n      bases_attributes.type != gpuMemoryTypeUnregistered &&\n      bases_attributes.type != gpuMemoryTypeHost;\n\n  size_t bitsize =\n      static_cast<size_t>((config_->bitsize == 0) ? ::bls12_381::scalar_t::NBITS\n                                                  : config_->bitsize);\n\n  size_t divisions = DetermineMsmDivisionsForMemory(\n      sizeof(::bls12_381::scalar_t), sizeof(::bls12_381::g2_affine_t),\n      sizeof(::bls12_381::g2_projective_t), bases_size, config_->c, bitsize,\n      static_cast<size_t>(config_->precompute_factor),\n      static_cast<size_t>(config_->batch_size));\n\n  size_t offset = bases_size / divisions;\n  size_t remainder = bases_size % divisions;\n  ::bls12_381::g2_projective_t final_value =\n      ::bls12_381::g2_projective_t::zero();\n  for (size_t idx = 0; idx < divisions; ++idx) {\n    size_t start_idx = idx * offset;\n    size_t data_size =\n        ((idx == divisions - 1) && (remainder != 0)) ? remainder : offset;\n    ::bls12_381::g2_projective_t ret;\n    gpuError_t error = tachyon_bls12_381_g2_msm_cuda(\n        reinterpret_cast<const ::bls12_381::scalar_t*>(&cpu_scalars[start_idx]),\n        reinterpret_cast<const ::bls12_381::g2_affine_t*>(&bases[start_idx]),\n        data_size, *config_, &ret);\n    if (error != gpuSuccess) {\n      GPU_LOG(ERROR, error) << \"Failed tachyon_bls12_381_g2_msm_cuda()\";\n      return false;\n    }\n    final_value = final_value + ret;\n  }\n  final_value = ::bls12_381::g2_projective_t::to_montgomery(final_value);\n  // TODO(chokobole): Change it to |base::bit_cast| again if the\n  // |::bls12_381::g2_projective_t| becomes trivially copyable.\n  *cpu_result = *reinterpret_cast<ProjectivePoint<Curve>*>(&final_value);\n  return true;\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/msm/algorithms/icicle/icicle_msm_bls12_381_g2.h",
    "content": "#ifndef TACHYON_MATH_ELLIPTIC_CURVES_MSM_ALGORITHMS_ICICLE_ICICLE_MSM_BLS12_381_G2_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_MSM_ALGORITHMS_ICICLE_ICICLE_MSM_BLS12_381_G2_H_\n\n#include \"third_party/icicle/include/curves/params/bls12_381.cu.h\"\n#include \"third_party/icicle/include/msm/msm.cu.h\"\n\n#include \"tachyon/device/gpu/gpu_device_functions.h\"\n\nextern \"C\" gpuError_t tachyon_bls12_381_g2_msm_cuda(\n    const ::bls12_381::scalar_t* scalars,\n    const ::bls12_381::g2_affine_t* points, int msm_size,\n    ::msm::MSMConfig& config, ::bls12_381::g2_projective_t* out);\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_MSM_ALGORITHMS_ICICLE_ICICLE_MSM_BLS12_381_G2_H_\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/msm/algorithms/icicle/icicle_msm_bn254_g1.cc",
    "content": "#include \"tachyon/math/elliptic_curves/msm/algorithms/icicle/icicle_msm_bn254_g1.h\"\n\n#include \"third_party/icicle/src/msm/msm.cu.cc\"  // NOLINT(build/include)\n\n#include \"tachyon/base/bit_cast.h\"\n#include \"tachyon/device/gpu/gpu_enums.h\"\n#include \"tachyon/device/gpu/gpu_logging.h\"\n#include \"tachyon/device/gpu/gpu_memory.h\"\n#include \"tachyon/math/elliptic_curves/msm/algorithms/icicle/icicle_msm.h\"\n#include \"tachyon/math/elliptic_curves/msm/algorithms/icicle/icicle_msm_utils.h\"\n\ngpuError_t tachyon_bn254_g1_msm_cuda(const ::bn254::scalar_t* scalars,\n                                     const ::bn254::affine_t* points,\n                                     int msm_size, ::msm::MSMConfig& config,\n                                     ::bn254::projective_t* out) {\n  return ::msm::msm(scalars, points, msm_size, config, out);\n}\n\nnamespace tachyon::math {\n\ntemplate <>\nbool IcicleMSM<bn254::G1AffinePoint>::Run(\n    absl::Span<const bn254::G1AffinePoint> bases,\n    absl::Span<const bn254::Fr> cpu_scalars,\n    ProjectivePoint<Curve>* cpu_result) {\n#if FIELD_ID != BN254\n#error Only BN254 is supported\n#endif\n\n  size_t bases_size = bases.size();\n  size_t scalars_size = cpu_scalars.size();\n\n  if (bases_size != scalars_size) {\n    LOG(ERROR) << \"bases_size and scalars_size don't match\";\n    return false;\n  }\n\n  device::gpu::gpuPointerAttributes bases_attributes{};\n  RETURN_AND_LOG_IF_GPU_ERROR(\n      device::gpu::GpuPointerGetAttributes(&bases_attributes, bases.data()),\n      \"Failed to GpuPointerGetAttributes()\");\n\n  config_->are_points_on_device =\n      bases_attributes.type != gpuMemoryTypeUnregistered &&\n      bases_attributes.type != gpuMemoryTypeHost;\n\n  size_t bitsize = static_cast<size_t>(\n      (config_->bitsize == 0) ? ::bn254::scalar_t::NBITS : config_->bitsize);\n\n  size_t divisions = DetermineMsmDivisionsForMemory(\n      sizeof(::bn254::scalar_t), sizeof(::bn254::affine_t),\n      sizeof(::bn254::projective_t), bases_size, config_->c, bitsize,\n      static_cast<size_t>(config_->precompute_factor),\n      static_cast<size_t>(config_->batch_size));\n\n  size_t offset = bases_size / divisions;\n  size_t remainder = bases_size % divisions;\n  ::bn254::projective_t final_value = ::bn254::projective_t::zero();\n  for (size_t idx = 0; idx < divisions; ++idx) {\n    size_t start_idx = idx * offset;\n    size_t data_size =\n        ((idx == divisions - 1) && (remainder != 0)) ? remainder : offset;\n    ::bn254::projective_t ret;\n    gpuError_t error = tachyon_bn254_g1_msm_cuda(\n        reinterpret_cast<const ::bn254::scalar_t*>(&cpu_scalars[start_idx]),\n        reinterpret_cast<const ::bn254::affine_t*>(&bases[start_idx]),\n        data_size, *config_, &ret);\n    if (error != gpuSuccess) {\n      GPU_LOG(ERROR, error) << \"Failed tachyon_bn254_g1_msm_cuda()\";\n      return false;\n    }\n    final_value = final_value + ret;\n  }\n  final_value = ::bn254::projective_t::to_montgomery(final_value);\n  // TODO(chokobole): Change it to |base::bit_cast| again if the\n  // |::bn254::projective_t| becomes trivially copyable.\n  *cpu_result = *reinterpret_cast<ProjectivePoint<Curve>*>(&final_value);\n  return true;\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/msm/algorithms/icicle/icicle_msm_bn254_g1.h",
    "content": "#ifndef TACHYON_MATH_ELLIPTIC_CURVES_MSM_ALGORITHMS_ICICLE_ICICLE_MSM_BN254_G1_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_MSM_ALGORITHMS_ICICLE_ICICLE_MSM_BN254_G1_H_\n\n#include \"third_party/icicle/include/curves/params/bn254.cu.h\"\n#include \"third_party/icicle/include/msm/msm.cu.h\"\n\n#include \"tachyon/device/gpu/gpu_device_functions.h\"\n\nextern \"C\" gpuError_t tachyon_bn254_g1_msm_cuda(\n    const ::bn254::scalar_t* scalars, const ::bn254::affine_t* points,\n    int msm_size, ::msm::MSMConfig& config, ::bn254::projective_t* out);\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_MSM_ALGORITHMS_ICICLE_ICICLE_MSM_BN254_G1_H_\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/msm/algorithms/icicle/icicle_msm_bn254_g2.cc",
    "content": "#include \"tachyon/math/elliptic_curves/msm/algorithms/icicle/icicle_msm_bn254_g2.h\"\n\n#include \"third_party/icicle/src/msm/msm.cu.cc\"  // NOLINT(build/include)\n\n#include \"tachyon/base/bit_cast.h\"\n#include \"tachyon/device/gpu/gpu_enums.h\"\n#include \"tachyon/device/gpu/gpu_logging.h\"\n#include \"tachyon/device/gpu/gpu_memory.h\"\n#include \"tachyon/math/elliptic_curves/msm/algorithms/icicle/icicle_msm.h\"\n#include \"tachyon/math/elliptic_curves/msm/algorithms/icicle/icicle_msm_utils.h\"\n\ngpuError_t tachyon_bn254_g2_msm_cuda(const ::bn254::scalar_t* scalars,\n                                     const ::bn254::g2_affine_t* points,\n                                     int msm_size, ::msm::MSMConfig& config,\n                                     ::bn254::g2_projective_t* out) {\n  return ::msm::msm(scalars, points, msm_size, config, out);\n}\n\nnamespace tachyon::math {\n\ntemplate <>\nbool IcicleMSM<bn254::G2AffinePoint>::Run(\n    absl::Span<const bn254::G2AffinePoint> bases,\n    absl::Span<const bn254::Fr> cpu_scalars,\n    ProjectivePoint<Curve>* cpu_result) {\n#if FIELD_ID != BN254\n#error Only BN254 is supported\n#endif\n\n  size_t bases_size = bases.size();\n  size_t scalars_size = cpu_scalars.size();\n\n  if (bases_size != scalars_size) {\n    LOG(ERROR) << \"bases_size and scalars_size don't match\";\n    return false;\n  }\n\n  device::gpu::gpuPointerAttributes bases_attributes{};\n  RETURN_AND_LOG_IF_GPU_ERROR(\n      device::gpu::GpuPointerGetAttributes(&bases_attributes, bases.data()),\n      \"Failed to GpuPointerGetAttributes()\");\n\n  config_->are_points_on_device =\n      bases_attributes.type != gpuMemoryTypeUnregistered &&\n      bases_attributes.type != gpuMemoryTypeHost;\n\n  size_t bitsize = static_cast<size_t>(\n      (config_->bitsize == 0) ? ::bn254::scalar_t::NBITS : config_->bitsize);\n\n  size_t divisions = DetermineMsmDivisionsForMemory(\n      sizeof(::bn254::scalar_t), sizeof(::bn254::g2_affine_t),\n      sizeof(::bn254::g2_projective_t), bases_size, config_->c, bitsize,\n      static_cast<size_t>(config_->precompute_factor),\n      static_cast<size_t>(config_->batch_size));\n\n  size_t offset = bases_size / divisions;\n  size_t remainder = bases_size % divisions;\n  ::bn254::g2_projective_t final_value = ::bn254::g2_projective_t::zero();\n  for (size_t idx = 0; idx < divisions; ++idx) {\n    size_t start_idx = idx * offset;\n    size_t data_size =\n        ((idx == divisions - 1) && (remainder != 0)) ? remainder : offset;\n    ::bn254::g2_projective_t ret;\n    gpuError_t error = tachyon_bn254_g2_msm_cuda(\n        reinterpret_cast<const ::bn254::scalar_t*>(&cpu_scalars[start_idx]),\n        reinterpret_cast<const ::bn254::g2_affine_t*>(&bases[start_idx]),\n        data_size, *config_, &ret);\n    if (error != gpuSuccess) {\n      GPU_LOG(ERROR, error) << \"Failed tachyon_bn254_g2_msm_cuda()\";\n      return false;\n    }\n    final_value = final_value + ret;\n  }\n  final_value = ::bn254::g2_projective_t::to_montgomery(final_value);\n  // TODO(chokobole): Change it to |base::bit_cast| again if the\n  // |::bn254::g2_projective_t| becomes trivially copyable.\n  *cpu_result = *reinterpret_cast<ProjectivePoint<Curve>*>(&final_value);\n  return true;\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/msm/algorithms/icicle/icicle_msm_bn254_g2.h",
    "content": "#ifndef TACHYON_MATH_ELLIPTIC_CURVES_MSM_ALGORITHMS_ICICLE_ICICLE_MSM_BN254_G2_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_MSM_ALGORITHMS_ICICLE_ICICLE_MSM_BN254_G2_H_\n\n#include \"third_party/icicle/include/curves/params/bn254.cu.h\"\n#include \"third_party/icicle/include/msm/msm.cu.h\"\n\n#include \"tachyon/device/gpu/gpu_device_functions.h\"\n\nextern \"C\" gpuError_t tachyon_bn254_g2_msm_cuda(\n    const ::bn254::scalar_t* scalars, const ::bn254::g2_affine_t* points,\n    int msm_size, ::msm::MSMConfig& config, ::bn254::g2_projective_t* out);\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_MSM_ALGORITHMS_ICICLE_ICICLE_MSM_BN254_G2_H_\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/msm/algorithms/icicle/icicle_msm_utils.cc",
    "content": "#include \"tachyon/math/elliptic_curves/msm/algorithms/icicle/icicle_msm_utils.h\"\n\n#include <algorithm>\n\n#include \"tachyon/base/bits.h\"\n#include \"tachyon/device/gpu/gpu_memory.h\"\n\nnamespace tachyon::math {\n\nsize_t DetermineMsmDivisionsForMemory(size_t scalar_t_mem_size,\n                                      size_t affine_t_mem_size,\n                                      size_t projective_t_mem_size,\n                                      size_t msm_size, size_t user_c,\n                                      size_t bitsize, size_t precompute_factor,\n                                      size_t batch_size) {\n  size_t free_memory =\n      device::gpu::GpuMemLimitInfo(device::gpu::MemoryUsage::kHigh);\n  size_t shift = 0;\n  uint32_t log_msm_size = base::bits::Log2Ceiling(msm_size);\n\n  for (size_t number_of_divisions = 0; number_of_divisions < log_msm_size;\n       ++number_of_divisions) {\n    // See\n    // https://github.com/ingonyama-zk/icicle/blob/0cb0b49b/icicle/src/msm/msm.cu#L429-L431\n    // https://github.com/ingonyama-zk/icicle/blob/0cb0b49b/icicle/include/msm/msm.cuh#L50-L56\n    size_t c = (user_c == 0) ? static_cast<size_t>(std::max(\n                                   base::bits::Log2Ceiling(msm_size) - 4, 1))\n                             : user_c;\n    size_t total_bms_per_msm = (bitsize + c - 1) / c;\n\n    // Calculate memory requirements\n    // See\n    // https://github.com/ingonyama-zk/icicle/blob/0cb0b49b/icicle/src/msm/msm.cu#L408-L427\n    size_t scalars_memory_size = scalar_t_mem_size * msm_size;\n    // See\n    // https://github.com/ingonyama-zk/icicle/blob/0cb0b49b/icicle/src/msm/msm.cu#L439-L442\n    // https://github.com/ingonyama-zk/icicle/blob/0cb0b49b/icicle/src/msm/msm.cu#L461-L464\n    size_t scalar_indices_memory_size = 6 * 4 * total_bms_per_msm * msm_size;\n    scalar_indices_memory_size =\n        static_cast<size_t>(scalar_indices_memory_size * 1.02);\n    // See\n    // https://github.com/ingonyama-zk/icicle/blob/0cb0b49b/icicle/src/msm/msm.cu#L515-L535\n    size_t points_memory_size =\n        affine_t_mem_size * precompute_factor * msm_size;\n    // See\n    // https://github.com/ingonyama-zk/icicle/blob/0cb0b49b/icicle/src/msm/msm.cu#L545\n    // https://github.com/ingonyama-zk/icicle/blob/0cb0b49b/icicle/src/msm/msm.cu#L767-L834\n    size_t buckets_memory_size =\n        projective_t_mem_size * total_bms_per_msm * (size_t{3} << c);\n\n    // Estimate total memory usage\n    // See\n    // https://dev.ingonyama.com/icicle/primitives/msm#memory-usage-estimation\n    size_t estimated_memory =\n        std::max(scalar_indices_memory_size,\n                 points_memory_size + buckets_memory_size) +\n        scalars_memory_size;\n    estimated_memory = static_cast<size_t>(estimated_memory * batch_size * 1.1);\n\n    if (free_memory > estimated_memory) {\n      shift = number_of_divisions;\n      break;\n    }\n    msm_size >>= 1;\n  }\n\n  return size_t{1} << shift;\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/msm/algorithms/icicle/icicle_msm_utils.h",
    "content": "#ifndef TACHYON_MATH_ELLIPTIC_CURVES_MSM_ALGORITHMS_ICICLE_ICICLE_MSM_UTILS_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_MSM_ALGORITHMS_ICICLE_ICICLE_MSM_UTILS_H_\n\n#include <stddef.h>\n\n#include \"tachyon/export.h\"\n\nnamespace tachyon::math {\n\n// NOTE(GideokKim): The formula for memory usage estimation provided in the\n// document did not match the actual memory allocation, so some of the formula\n// was modified. |scalars_memory_size| and |points_memory_size| are exactly the\n// same, and |scalar_indices_memory_size| internally uses the sort function of\n// the cub library to set some free memory. |buckets_memory_size| uses more\n// memory than the actual formula, so it was modified to an empirically more\n// appropriate formula. See\n// https://dev.ingonyama.com/icicle/primitives/msm#memory-usage-estimation\nTACHYON_EXPORT size_t DetermineMsmDivisionsForMemory(\n    size_t scalar_t_mem_size, size_t affine_t_mem_size,\n    size_t projective_t_mem_size, size_t msm_size, size_t user_c,\n    size_t bitsize, size_t precompute_factor, size_t batch_size);\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_MSM_ALGORITHMS_ICICLE_ICICLE_MSM_UTILS_H_\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/msm/algorithms/pippenger/BUILD.bazel",
    "content": "load(\n    \"//bazel:tachyon_cc.bzl\",\n    \"tachyon_cc_benchmark\",\n    \"tachyon_cc_library\",\n    \"tachyon_cc_unittest\",\n)\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"pippenger\",\n    hdrs = [\"pippenger.h\"],\n    deps = [\n        \":pippenger_base\",\n        \"//tachyon/base:openmp_util\",\n        \"//tachyon/base:profiler\",\n        \"//tachyon/math/elliptic_curves:semigroups\",\n        \"//tachyon/math/elliptic_curves/msm:msm_ctx\",\n        \"//tachyon/math/elliptic_curves/msm:msm_util\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"pippenger_adapter\",\n    hdrs = [\"pippenger_adapter.h\"],\n    deps = [\n        \":pippenger\",\n        \"//tachyon/base:parallelize\",\n        \"//tachyon/base:profiler\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"pippenger_base\",\n    hdrs = [\"pippenger_base.h\"],\n    deps = [\n        \"//tachyon/base:profiler\",\n        \"//tachyon/base/containers:adapters\",\n        \"//tachyon/math/base:semigroups\",\n        \"//tachyon/math/geometry:affine_point\",\n        \"//tachyon/math/geometry:point_xyzz\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"algorithms_unittests\",\n    srcs = [\n        \"pippenger_adapter_unittest.cc\",\n        \"pippenger_unittest.cc\",\n    ],\n    deps = [\n        \":pippenger_adapter\",\n        \"//tachyon/math/elliptic_curves/bls12/bls12_381:g1\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:g1\",\n        \"//tachyon/math/elliptic_curves/msm/test:variable_base_msm_test_set\",\n    ],\n)\n\ntachyon_cc_benchmark(\n    name = \"pippenger_adapter_benchmark\",\n    srcs = [\"pippenger_adapter_benchmark.cc\"],\n    deps = [\n        \":pippenger_adapter\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:g1\",\n        \"//tachyon/math/elliptic_curves/msm/test:variable_base_msm_test_set\",\n    ],\n)\n\ntachyon_cc_benchmark(\n    name = \"pippenger_benchmark\",\n    srcs = [\"pippenger_benchmark.cc\"],\n    deps = [\n        \":pippenger\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:g1\",\n        \"//tachyon/math/elliptic_curves/msm/test:variable_base_msm_test_set\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/msm/algorithms/pippenger/pippenger.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_ELLIPTIC_CURVES_MSM_ALGORITHMS_PIPPENGER_PIPPENGER_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_MSM_ALGORITHMS_PIPPENGER_PIPPENGER_H_\n\n#include <algorithm>\n#include <numeric>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/openmp_util.h\"\n#include \"tachyon/base/profiler.h\"\n#include \"tachyon/math/base/big_int.h\"\n#include \"tachyon/math/elliptic_curves/msm/algorithms/pippenger/pippenger_base.h\"\n#include \"tachyon/math/elliptic_curves/msm/msm_ctx.h\"\n#include \"tachyon/math/elliptic_curves/msm/msm_util.h\"\n#include \"tachyon/math/elliptic_curves/semigroups.h\"\n\nnamespace tachyon::math {\n\n// From:\n// https://github.com/arkworks-rs/gemini/blob/main/src/kzg/msm/variable_base.rs#L20\ntemplate <size_t N>\nvoid FillDigits(const BigInt<N>& scalar, size_t window_bits,\n                std::vector<int64_t>* digits) {\n  uint64_t radix = uint64_t{1} << window_bits;\n\n  uint64_t carry = 0;\n  size_t bit_offset = 0;\n  for (size_t i = 0; i < digits->size(); ++i) {\n    // Construct a buffer of bits of the |scalar|, starting at\n    // `bit_offset`.\n    uint64_t bits = scalar.ExtractBits64(bit_offset, window_bits);\n\n    // Read the actual coefficient value from the window\n    uint64_t coeff = carry + bits;  // coeff = [0, 2^|window_bits|)\n\n    // Recenter coefficients from [0,2^|window_bits|) to\n    // [-2^|window_bits|/2, 2^|window_bits|/2)\n    carry = (coeff + radix / 2) >> window_bits;\n    (*digits)[i] = static_cast<int64_t>(coeff) -\n                   static_cast<int64_t>(carry << window_bits);\n    bit_offset += window_bits;\n  }\n\n  digits->back() += static_cast<int64_t>(carry << window_bits);\n}\n\ntemplate <typename Point>\nclass Pippenger : public PippengerBase<Point> {\n public:\n  using ScalarField = typename Point::ScalarField;\n  using Bucket = typename PippengerBase<Point>::Bucket;\n\n  constexpr static size_t N = ScalarField::N;\n\n  Pippenger() : use_msm_window_naf_(Point::kNegationIsCheap) {\n#if defined(TACHYON_HAS_OPENMP)\n    parallel_windows_ = true;\n#endif  // defined(TACHYON_HAS_OPENMP)\n  }\n\n  void SetParallelWindows(bool parallel_windows) {\n    parallel_windows_ = parallel_windows;\n  }\n\n  void SetUseMSMWindowNAForTesting(bool use_msm_window_naf) {\n    use_msm_window_naf_ = use_msm_window_naf;\n  }\n\n  template <typename BaseInputIterator, typename ScalarInputIterator,\n            std::enable_if_t<IsAbleToMSM<BaseInputIterator, ScalarInputIterator,\n                                         Point, ScalarField>>* = nullptr>\n  [[nodiscard]] bool Run(BaseInputIterator bases_first,\n                         BaseInputIterator bases_last,\n                         ScalarInputIterator scalars_first,\n                         ScalarInputIterator scalars_last, Bucket* ret) {\n    TRACE_EVENT(\"MSM\", \"Pippenger::Run\");\n    size_t bases_size = std::distance(bases_first, bases_last);\n    size_t scalars_size = std::distance(scalars_first, scalars_last);\n    if (bases_size != scalars_size) {\n      LOG(ERROR) << \"bases_size and scalars_size don't match\";\n      return false;\n    }\n    ctx_ = MSMCtx::CreateDefault<ScalarField>(scalars_size);\n\n    std::vector<BigInt<N>> scalars;\n    scalars.resize(scalars_size);\n    auto scalars_it = scalars_first;\n    for (size_t i = 0; i < scalars_size; ++i, ++scalars_it) {\n      scalars[i] = scalars_it->ToBigInt();\n    }\n\n    std::vector<Bucket> window_sums(ctx_.window_count);\n\n    if (use_msm_window_naf_) {\n      AccumulateWindowNAFSums(std::move(bases_first), scalars, &window_sums);\n    } else {\n      AccumulateWindowSums(std::move(bases_first), scalars, &window_sums);\n    }\n\n    *ret = PippengerBase<Point>::AccumulateWindowSums(window_sums,\n                                                      ctx_.window_bits);\n    return true;\n  }\n\n private:\n  template <typename BaseInputIterator>\n  void AccumulateSingleWindowNAFSum(\n      BaseInputIterator bases_it,\n      const std::vector<std::vector<int64_t>>& scalar_digits, size_t i,\n      Bucket* window_sum, bool is_last_window) {\n    TRACE_EVENT(\"Utils\", \"AccumulateSingleWindowNAFSum\");\n    size_t bucket_size;\n    if (is_last_window) {\n      bucket_size = size_t{1} << ctx_.window_bits;\n    } else {\n      bucket_size = size_t{1} << (ctx_.window_bits - 1);\n    }\n    std::vector<Bucket> buckets(bucket_size);\n    for (size_t j = 0; j < scalar_digits.size(); ++j, ++bases_it) {\n      const Point& base = *bases_it;\n      int64_t scalar = scalar_digits[j][i];\n      if (0 < scalar) {\n        buckets[static_cast<uint64_t>(scalar - 1)] += base;\n      } else if (0 > scalar) {\n        buckets[static_cast<uint64_t>(-scalar - 1)] -= base;\n      }\n    }\n    *window_sum = PippengerBase<Point>::AccumulateBuckets(buckets);\n  }\n\n  template <typename BaseInputIterator>\n  void AccumulateWindowNAFSums(BaseInputIterator bases_first,\n                               absl::Span<const BigInt<N>> scalars,\n                               std::vector<Bucket>* window_sums) {\n    TRACE_EVENT(\"Utils\", \"AccumulateWindowNAFSums\");\n\n    std::vector<std::vector<int64_t>> scalar_digits;\n    {\n      TRACE_EVENT(\"Subtask\", \"InitAndFillScalars\");\n      scalar_digits.resize(scalars.size());\n      for (std::vector<int64_t>& scalar_digit : scalar_digits) {\n        scalar_digit.resize(ctx_.window_count);\n      }\n      for (size_t i = 0; i < scalars.size(); ++i) {\n        FillDigits(scalars[i], ctx_.window_bits, &scalar_digits[i]);\n      }\n    }\n\n    if (parallel_windows_) {\n      TRACE_EVENT(\"Subtask\", \"ParallelWindows\");\n      OMP_PARALLEL_FOR(size_t i = 0; i < ctx_.window_count; ++i) {\n        AccumulateSingleWindowNAFSum(bases_first, scalar_digits, i,\n                                     &(*window_sums)[i],\n                                     i == ctx_.window_count - 1);\n      }\n    } else {\n      TRACE_EVENT(\"Subtask\", \"SerialWindows\");\n      for (size_t i = 0; i < ctx_.window_count; ++i) {\n        AccumulateSingleWindowNAFSum(bases_first, scalar_digits, i,\n                                     &(*window_sums)[i],\n                                     i == ctx_.window_count - 1);\n      }\n    }\n  }\n\n  template <typename BaseInputIterator>\n  void AccumulateSingleWindowSum(BaseInputIterator bases_first,\n                                 absl::Span<const BigInt<N>> scalars,\n                                 size_t window_offset, Bucket* out) {\n    TRACE_EVENT(\"Utils\", \"AccumulateSingleWindowSum\");\n    Bucket window_sum = Bucket::Zero();\n    // We don't need the \"zero\" bucket, so we only have 2^{window_bits} - 1\n    // buckets.\n    std::vector<Bucket> buckets((size_t{1} << ctx_.window_bits) - 1);\n    auto bases_it = bases_first;\n    for (size_t j = 0; j < scalars.size(); ++j, ++bases_it) {\n      const BigInt<N>& scalar = scalars[j];\n      if (scalar.IsZero()) continue;\n\n      const Point& base = *bases_it;\n      if (scalar.IsOne()) {\n        // We only process unit scalars once in the first window.\n        if (window_offset == 0) {\n          window_sum += base;\n        }\n      } else {\n        BigInt<N> scalar_tmp = scalar;\n        // We right-shift by |window_offset|, thus getting rid of the lower\n        // bits.\n        scalar_tmp.DivBy2ExpInPlace(window_offset);\n\n        // We mod the remaining bits by 2^{window_bits}, thus taking\n        // |window_bits|.\n        uint64_t idx = scalar_tmp[0] % (uint64_t{1} << ctx_.window_bits);\n\n        // If the scalar is non-zero, we update the corresponding\n        // bucket.\n        // (Recall that |buckets| doesn't have a zero bucket.)\n        if (idx != 0) {\n          buckets[idx - 1] += base;\n        }\n      }\n    }\n    *out = PippengerBase<Point>::AccumulateBuckets(buckets, window_sum);\n  }\n\n  template <typename BaseInputIterator>\n  void AccumulateWindowSums(BaseInputIterator bases_first,\n                            absl::Span<const BigInt<N>> scalars,\n                            std::vector<Bucket>* window_sums) {\n    TRACE_EVENT(\"Utils\", \"AccumulateWindowSums\");\n    if (parallel_windows_) {\n      TRACE_EVENT(\"Subtask\", \"ParallelWindows\");\n      OMP_PARALLEL_FOR(size_t i = 0; i < ctx_.window_count; ++i) {\n        AccumulateSingleWindowSum(bases_first, scalars, ctx_.window_bits * i,\n                                  &(*window_sums)[i]);\n      }\n    } else {\n      TRACE_EVENT(\"Subtask\", \"SerialWindows\");\n      for (size_t i = 0; i < ctx_.window_count; ++i) {\n        AccumulateSingleWindowSum(bases_first, scalars, ctx_.window_bits * i,\n                                  &(*window_sums)[i]);\n      }\n    }\n  }\n\n  bool use_msm_window_naf_ = false;\n  bool parallel_windows_ = false;\n  MSMCtx ctx_;\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_MSM_ALGORITHMS_PIPPENGER_PIPPENGER_H_\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/msm/algorithms/pippenger/pippenger_adapter.h",
    "content": "#ifndef TACHYON_MATH_ELLIPTIC_CURVES_MSM_ALGORITHMS_PIPPENGER_PIPPENGER_ADAPTER_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_MSM_ALGORITHMS_PIPPENGER_PIPPENGER_ADAPTER_H_\n\n#include <algorithm>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/parallelize.h\"\n#include \"tachyon/base/profiler.h\"\n#include \"tachyon/math/elliptic_curves/msm/algorithms/pippenger/pippenger.h\"\n\nnamespace tachyon::math {\n\nenum class PippengerParallelStrategy {\n  kNone,\n  kParallelWindow,\n  kParallelTerm,\n  kParallelWindowAndTerm,\n};\n\ntemplate <typename Point>\nclass PippengerAdapter {\n public:\n  using ScalarField = typename Point::ScalarField;\n  using Bucket = typename Pippenger<Point>::Bucket;\n\n  template <typename BaseInputIterator, typename ScalarInputIterator>\n  [[nodiscard]] bool Run(BaseInputIterator bases_first,\n                         BaseInputIterator bases_last,\n                         ScalarInputIterator scalars_first,\n                         ScalarInputIterator scalars_last, Bucket* ret) {\n    return RunWithStrategy(std::move(bases_first), std::move(bases_last),\n                           std::move(scalars_first), std::move(scalars_last),\n                           PippengerParallelStrategy::kParallelTerm, ret);\n  }\n\n  template <typename BaseInputIterator, typename ScalarInputIterator>\n  [[nodiscard]] bool RunWithStrategy(BaseInputIterator bases_first,\n                                     BaseInputIterator bases_last,\n                                     ScalarInputIterator scalars_first,\n                                     ScalarInputIterator scalars_last,\n                                     PippengerParallelStrategy strategy,\n                                     Bucket* ret) {\n    TRACE_EVENT(\"MSM\", \"PippengerAdapter::RunWithStrategy\", \"strategy\",\n                static_cast<int>(strategy));\n\n    if (strategy == PippengerParallelStrategy::kNone ||\n        strategy == PippengerParallelStrategy::kParallelWindow) {\n      Pippenger<Point> pippenger;\n      pippenger.SetParallelWindows(strategy ==\n                                   PippengerParallelStrategy::kParallelWindow);\n      return pippenger.Run(std::move(bases_first), std::move(bases_last),\n                           std::move(scalars_first), std::move(scalars_last),\n                           ret);\n    } else {\n      size_t bases_size = std::distance(bases_first, bases_last);\n      size_t scalars_size = std::distance(scalars_first, scalars_last);\n      if (bases_size != scalars_size) {\n        LOG(ERROR) << \"bases_size and scalars_size don't match\";\n        return false;\n      }\n      if (scalars_size == 0) {\n        *ret = Bucket::Zero();\n        return true;\n      }\n\n      struct Result {\n        Bucket value;\n        bool valid;\n      };\n\n#if defined(TACHYON_HAS_OPENMP)\n      int thread_nums = omp_get_max_threads();\n      if (strategy == PippengerParallelStrategy::kParallelWindowAndTerm) {\n        size_t window_bits = MSMCtx::ComputeWindowsBits(scalars_size);\n        size_t window_size =\n            MSMCtx::ComputeWindowsCount<ScalarField>(window_bits);\n        thread_nums = std::max(thread_nums / static_cast<int>(window_size), 2);\n      }\n      omp_set_num_threads(thread_nums);\n#endif\n      std::vector<Result> results = base::ParallelizeMap(\n          scalars_size,\n          [strategy, bases_first, scalars_first](\n              size_t len, size_t chunk_offset, size_t chunk_size) {\n            TRACE_EVENT(\"Subtask\", \"ParallelLoop\");\n            size_t start = chunk_offset * chunk_size;\n            Pippenger<Point> pippenger;\n            pippenger.SetParallelWindows(\n                strategy == PippengerParallelStrategy::kParallelWindowAndTerm);\n            auto bases_start = bases_first + start;\n            auto bases_end = bases_start + len;\n            auto scalars_start = scalars_first + start;\n            auto scalars_end = scalars_start + len;\n            Result result;\n            result.valid = pippenger.Run(bases_start, bases_end, scalars_start,\n                                         scalars_end, &result.value);\n            return result;\n          });\n#if defined(TACHYON_HAS_OPENMP)\n      omp_set_num_threads(omp_get_max_threads());\n#endif\n\n      TRACE_EVENT(\"Subtask\", \"CheckResultAndAccumulate\");\n      bool all_good =\n          std::all_of(results.begin(), results.end(),\n                      [](const Result& result) { return result.valid; });\n      if (!all_good) return false;\n\n      *ret = std::accumulate(results.begin(), results.end(), Bucket::Zero(),\n                             [](Bucket& total, const Result& result) {\n                               return total += result.value;\n                             });\n      return true;\n    }\n  }\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_MSM_ALGORITHMS_PIPPENGER_PIPPENGER_ADAPTER_H_\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/msm/algorithms/pippenger/pippenger_adapter_benchmark.cc",
    "content": "#include \"benchmark/benchmark.h\"\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/g1.h\"\n#include \"tachyon/math/elliptic_curves/msm/test/variable_base_msm_test_set.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename Point, bool IsRandom,\n          enum PippengerParallelStrategy Strategy>\nvoid BM_PippengerAdapter(benchmark::State& state) {\n  Point::Curve::Init();\n  VariableBaseMSMTestSet<Point> test_set;\n  if constexpr (IsRandom) {\n    test_set = VariableBaseMSMTestSet<Point>::Random(\n        state.range(0), VariableBaseMSMMethod::kNone);\n  } else {\n    test_set = VariableBaseMSMTestSet<Point>::NonUniform(\n        state.range(0), 10, VariableBaseMSMMethod::kNone);\n  }\n  PippengerAdapter<Point> pippenger;\n  using Bucket = typename PippengerAdapter<Point>::Bucket;\n  Bucket ret;\n  for (auto _ : state) {\n    CHECK(pippenger.RunWithStrategy(\n        test_set.bases.begin(), test_set.bases.end(), test_set.scalars.begin(),\n        test_set.scalars.end(), Strategy, &ret));\n  }\n  benchmark::DoNotOptimize(ret);\n}\n\ntemplate <typename Point>\nvoid BM_PippengerAdapterRandomWithParallelWindow(benchmark::State& state) {\n  BM_PippengerAdapter<Point, true, PippengerParallelStrategy::kParallelWindow>(\n      state);\n}\n\ntemplate <typename Point>\nvoid BM_PippengerAdapterNonUniformWithParallelWindow(benchmark::State& state) {\n  BM_PippengerAdapter<Point, false, PippengerParallelStrategy::kParallelWindow>(\n      state);\n}\n\ntemplate <typename Point>\nvoid BM_PippengerAdapterRandomWithParallelTerm(benchmark::State& state) {\n  BM_PippengerAdapter<Point, true, PippengerParallelStrategy::kParallelTerm>(\n      state);\n}\n\ntemplate <typename Point>\nvoid BM_PippengerAdapterNonUniformWithParallelTerm(benchmark::State& state) {\n  BM_PippengerAdapter<Point, false, PippengerParallelStrategy::kParallelTerm>(\n      state);\n}\n\ntemplate <typename Point>\nvoid BM_PippengerAdapterRandomWithParallelWindowAndTerm(\n    benchmark::State& state) {\n  BM_PippengerAdapter<Point, true,\n                      PippengerParallelStrategy::kParallelWindowAndTerm>(state);\n}\n\ntemplate <typename Point>\nvoid BM_PippengerAdapterNonUniformWithParallelWindowAndTerm(\n    benchmark::State& state) {\n  BM_PippengerAdapter<Point, false,\n                      PippengerParallelStrategy::kParallelWindowAndTerm>(state);\n}\n\nBENCHMARK_TEMPLATE(BM_PippengerAdapterRandomWithParallelWindow,\n                   bn254::G1AffinePoint)\n    ->RangeMultiplier(2)\n    ->Range(1 << 15, 1 << 20);\nBENCHMARK_TEMPLATE(BM_PippengerAdapterNonUniformWithParallelWindow,\n                   bn254::G1AffinePoint)\n    ->RangeMultiplier(2)\n    ->Range(1 << 15, 1 << 20);\nBENCHMARK_TEMPLATE(BM_PippengerAdapterRandomWithParallelTerm,\n                   bn254::G1AffinePoint)\n    ->RangeMultiplier(2)\n    ->Range(1 << 15, 1 << 20);\nBENCHMARK_TEMPLATE(BM_PippengerAdapterNonUniformWithParallelTerm,\n                   bn254::G1AffinePoint)\n    ->RangeMultiplier(2)\n    ->Range(1 << 15, 1 << 20);\nBENCHMARK_TEMPLATE(BM_PippengerAdapterRandomWithParallelWindowAndTerm,\n                   bn254::G1AffinePoint)\n    ->RangeMultiplier(2)\n    ->Range(1 << 15, 1 << 20);\nBENCHMARK_TEMPLATE(BM_PippengerAdapterNonUniformWithParallelWindowAndTerm,\n                   bn254::G1AffinePoint)\n    ->RangeMultiplier(2)\n    ->Range(1 << 15, 1 << 20);\n\n}  // namespace tachyon::math\n\n// clang-format off\n// Executing tests from //tachyon/math/elliptic_curves/msm/algorithms/pippenger:pippenger_adapter_benchmark\n// -----------------------------------------------------------------------------\n// 2023-09-13T01:54:29+00:00\n// Running /home/ryan/.cache/bazel/_bazel_ryan/2e01f4ccafa60589f9bbdbefc5d15e2a/execroot/kroma_network_tachyon/bazel-out/k8-opt/bin/tachyon/math/elliptic_curves/msm/algorithms/pippenger/pippenger_adapter_benchmark.runfiles/kroma_network_tachyon/tachyon/math/elliptic_curves/msm/algorithms/pippenger/pippenger_adapter_benchmark\n// Run on (32 X 5500 MHz CPU s)\n// CPU Caches:\n//   L1 Data 48 KiB (x16)\n//   L1 Instruction 32 KiB (x16)\n//   L2 Unified 2048 KiB (x16)\n//   L3 Unified 36864 KiB (x1)\n// Load Average: 0.58, 0.51, 0.53\n// -------------------------------------------------------------------------------------------------------------------------------\n// Benchmark                                                                                     Time             CPU   Iterations\n// -------------------------------------------------------------------------------------------------------------------------------\n// BM_PippengerAdapterRandomWithParallelWindow<bn254::G1AffinePoint>/32768                23141976 ns     23140942 ns           27\n// BM_PippengerAdapterRandomWithParallelWindow<bn254::G1AffinePoint>/65536                45281116 ns     44897489 ns           17\n// BM_PippengerAdapterRandomWithParallelWindow<bn254::G1AffinePoint>/131072               79303765 ns     67719233 ns           10\n// BM_PippengerAdapterRandomWithParallelWindow<bn254::G1AffinePoint>/262144              163616037 ns    125332664 ns            5\n// BM_PippengerAdapterRandomWithParallelWindow<bn254::G1AffinePoint>/524288              321303527 ns    222457503 ns            3\n// BM_PippengerAdapterRandomWithParallelWindow<bn254::G1AffinePoint>/1048576             628253341 ns    454716086 ns            2\n// BM_PippengerAdapterNonUniformWithParallelWindow<bn254::G1AffinePoint>/32768            22177564 ns     22176387 ns           27\n// BM_PippengerAdapterNonUniformWithParallelWindow<bn254::G1AffinePoint>/65536            43015189 ns     40323960 ns           18\n// BM_PippengerAdapterNonUniformWithParallelWindow<bn254::G1AffinePoint>/131072           76032487 ns     64443376 ns           11\n// BM_PippengerAdapterNonUniformWithParallelWindow<bn254::G1AffinePoint>/262144          161086917 ns    121884608 ns            6\n// BM_PippengerAdapterNonUniformWithParallelWindow<bn254::G1AffinePoint>/524288          304606597 ns    222545666 ns            3\n// BM_PippengerAdapterNonUniformWithParallelWindow<bn254::G1AffinePoint>/1048576         588236094 ns    438750185 ns            2\n// BM_PippengerAdapterRandomWithParallelTerm<bn254::G1AffinePoint>/32768                  23018500 ns     22043368 ns           34\n// BM_PippengerAdapterRandomWithParallelTerm<bn254::G1AffinePoint>/65536                  39519111 ns     38539498 ns           18\n// BM_PippengerAdapterRandomWithParallelTerm<bn254::G1AffinePoint>/131072                 74930620 ns     67770503 ns           10\n// BM_PippengerAdapterRandomWithParallelTerm<bn254::G1AffinePoint>/262144                137890458 ns    130253880 ns            6\n// BM_PippengerAdapterRandomWithParallelTerm<bn254::G1AffinePoint>/524288                246021986 ns    230435266 ns            3\n// BM_PippengerAdapterRandomWithParallelTerm<bn254::G1AffinePoint>/1048576               472836614 ns    416560205 ns            2\n// BM_PippengerAdapterNonUniformWithParallelTerm<bn254::G1AffinePoint>/32768              19471671 ns     19470170 ns           37\n// BM_PippengerAdapterNonUniformWithParallelTerm<bn254::G1AffinePoint>/65536              37021720 ns     36100273 ns           20\n// BM_PippengerAdapterNonUniformWithParallelTerm<bn254::G1AffinePoint>/131072             71205117 ns     68483757 ns           11\n// BM_PippengerAdapterNonUniformWithParallelTerm<bn254::G1AffinePoint>/262144            136134505 ns    115398568 ns            6\n// BM_PippengerAdapterNonUniformWithParallelTerm<bn254::G1AffinePoint>/524288            241173903 ns    225617420 ns            3\n// BM_PippengerAdapterNonUniformWithParallelTerm<bn254::G1AffinePoint>/1048576           439469576 ns    403132290 ns            2\n// BM_PippengerAdapterRandomWithParallelWindowAndTerm<bn254::G1AffinePoint>/32768        127219359 ns    127210795 ns            6\n// BM_PippengerAdapterRandomWithParallelWindowAndTerm<bn254::G1AffinePoint>/65536        231521209 ns    231510773 ns            3\n// BM_PippengerAdapterRandomWithParallelWindowAndTerm<bn254::G1AffinePoint>/131072       426907539 ns    426884276 ns            2\n// BM_PippengerAdapterRandomWithParallelWindowAndTerm<bn254::G1AffinePoint>/262144       830853224 ns    830388088 ns            1\n// BM_PippengerAdapterRandomWithParallelWindowAndTerm<bn254::G1AffinePoint>/524288      1585369587 ns   1580798267 ns            1\n// BM_PippengerAdapterRandomWithParallelWindowAndTerm<bn254::G1AffinePoint>/1048576     2858370543 ns   2855271982 ns            1\n// BM_PippengerAdapterNonUniformWithParallelWindowAndTerm<bn254::G1AffinePoint>/32768    123564482 ns    123560265 ns            6\n// BM_PippengerAdapterNonUniformWithParallelWindowAndTerm<bn254::G1AffinePoint>/65536    229732513 ns    229726383 ns            3\n// BM_PippengerAdapterNonUniformWithParallelWindowAndTerm<bn254::G1AffinePoint>/131072   417522907 ns    417503583 ns            2\n// BM_PippengerAdapterNonUniformWithParallelWindowAndTerm<bn254::G1AffinePoint>/262144   818992138 ns    818960598 ns            1\n// BM_PippengerAdapterNonUniformWithParallelWindowAndTerm<bn254::G1AffinePoint>/524288  1545445681 ns   1545361484 ns            1\n// BM_PippengerAdapterNonUniformWithParallelWindowAndTerm<bn254::G1AffinePoint>/1048576 2759630680 ns   2759482455 ns            1\n// clang-format on\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/msm/algorithms/pippenger/pippenger_adapter_unittest.cc",
    "content": "#include \"tachyon/math/elliptic_curves/msm/algorithms/pippenger/pippenger_adapter.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/elliptic_curves/bn/bn254/g1.h\"\n#include \"tachyon/math/elliptic_curves/msm/test/variable_base_msm_test_set.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\nconst size_t kSize = 1024;\n\nclass PippengerAdapterTest : public testing::Test {\n public:\n  static void SetUpTestSuite() { bn254::G1Curve::Init(); }\n\n  PippengerAdapterTest()\n      : test_set_(VariableBaseMSMTestSet<bn254::G1AffinePoint>::Random(\n            kSize, VariableBaseMSMMethod::kMSM)) {}\n  PippengerAdapterTest(const PippengerAdapterTest&) = delete;\n  PippengerAdapterTest& operator=(const PippengerAdapterTest&) = delete;\n  ~PippengerAdapterTest() override = default;\n\n protected:\n  VariableBaseMSMTestSet<bn254::G1AffinePoint> test_set_;\n};\n\n}  // namespace\n\nTEST_F(PippengerAdapterTest, RunWithStrategy) {\n  const VariableBaseMSMTestSet<bn254::G1AffinePoint>& test_set =\n      this->test_set_;\n\n  for (PippengerParallelStrategy strategy :\n       {PippengerParallelStrategy::kNone,\n        PippengerParallelStrategy::kParallelWindow,\n        PippengerParallelStrategy::kParallelTerm,\n        PippengerParallelStrategy::kParallelWindowAndTerm}) {\n    PippengerAdapter<bn254::G1AffinePoint> pippenger;\n    SCOPED_TRACE(absl::Substitute(\"strategy: $0\", static_cast<int>(strategy)));\n    bn254::G1PointXYZZ ret;\n    EXPECT_TRUE(pippenger.RunWithStrategy(\n        test_set.bases.begin(), test_set.bases.end(), test_set.scalars.begin(),\n        test_set.scalars.end(), strategy, &ret));\n    EXPECT_EQ(ret, test_set.answer);\n  }\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/msm/algorithms/pippenger/pippenger_base.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_ELLIPTIC_CURVES_MSM_ALGORITHMS_PIPPENGER_PIPPENGER_BASE_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_MSM_ALGORITHMS_PIPPENGER_PIPPENGER_BASE_H_\n\n#include \"absl/types/span.h\"\n\n#include \"tachyon/base/containers/adapters.h\"\n#include \"tachyon/math/base/semigroups.h\"\n#include \"tachyon/math/geometry/affine_point.h\"\n#include \"tachyon/math/geometry/point_xyzz.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename Point>\nclass PippengerTraits {\n public:\n  using Bucket = typename internal::AdditiveSemigroupTraits<Point>::ReturnTy;\n};\n\ntemplate <typename Curve>\nclass PippengerTraits<AffinePoint<Curve>> {\n public:\n  using Bucket = PointXYZZ<Curve>;\n};\n\ntemplate <typename Point,\n          typename Bucket_ = typename PippengerTraits<Point>::Bucket>\nclass PippengerBase {\n public:\n  using Bucket = Bucket_;\n\n  static Bucket AccumulateBuckets(\n      absl::Span<const Bucket> buckets,\n      const Bucket& initial_value = Bucket::Zero()) {\n    Bucket running_sum = Bucket::Zero();\n    Bucket window_sum = initial_value;\n\n    // This is computed below for b buckets, using 2b curve additions.\n    //\n    // We could first normalize |buckets| and then use mixed-addition\n    // here, but that's slower for the kinds of groups we care about\n    // (Short Weierstrass curves and Twisted Edwards curves).\n    // In the case of Short Weierstrass curves,\n    // mixed addition saves ~4 field multiplications per addition.\n    // However normalization (with the inversion batched) takes ~6\n    // field multiplications per element,\n    // hence batch normalization is a slowdown.\n    for (const auto& bucket : base::Reversed(buckets)) {\n      running_sum += bucket;\n      window_sum += running_sum;\n    }\n    return window_sum;\n  }\n\n  static Bucket AccumulateWindowSums(absl::Span<const Bucket> window_sums,\n                                     size_t window_bits) {\n    TRACE_EVENT(\"Utils\", \"PippengerBase::AccumulateWindowSums\");\n    // We store the sum for the lowest window.\n    Bucket lowest = window_sums.front();\n    window_sums.remove_prefix(1);\n\n    // We're traversing windows from high to low.\n    return lowest +\n           std::accumulate(window_sums.rbegin(), window_sums.rend(),\n                           Bucket::Zero(),\n                           [window_bits](Bucket& total, const Bucket& sum) {\n                             total += sum;\n                             for (size_t i = 0; i < window_bits; ++i) {\n                               total.DoubleInPlace();\n                             }\n                             return total;\n                           });\n  }\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_MSM_ALGORITHMS_PIPPENGER_PIPPENGER_BASE_H_\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/msm/algorithms/pippenger/pippenger_benchmark.cc",
    "content": "#include \"benchmark/benchmark.h\"\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/g1.h\"\n#include \"tachyon/math/elliptic_curves/msm/test/variable_base_msm_test_set.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename Point, bool IsRandom>\nvoid BM_Pippenger(benchmark::State& state) {\n  Point::Curve::Init();\n  VariableBaseMSMTestSet<Point> test_set;\n  if constexpr (IsRandom) {\n    test_set = VariableBaseMSMTestSet<Point>::Random(\n        state.range(0), VariableBaseMSMMethod::kNone);\n  } else {\n    test_set = VariableBaseMSMTestSet<Point>::NonUniform(\n        state.range(0), 10, VariableBaseMSMMethod::kNone);\n  }\n  Pippenger<Point> pippenger;\n  using Bucket = typename Pippenger<Point>::Bucket;\n  Bucket ret;\n  for (auto _ : state) {\n    CHECK(pippenger.Run(test_set.bases.begin(), test_set.bases.end(),\n                        test_set.scalars.begin(), test_set.scalars.end(),\n                        &ret));\n  }\n  benchmark::DoNotOptimize(ret);\n}\n\ntemplate <typename Point>\nvoid BM_PippengerRandom(benchmark::State& state) {\n  BM_Pippenger<Point, true>(state);\n}\n\ntemplate <typename Point>\nvoid BM_PippengerNonUniform(benchmark::State& state) {\n  BM_Pippenger<Point, false>(state);\n}\n\nBENCHMARK_TEMPLATE(BM_PippengerRandom, bn254::G1AffinePoint)\n    ->RangeMultiplier(2)\n    ->Range(1 << 15, 1 << 20);\nBENCHMARK_TEMPLATE(BM_PippengerNonUniform, bn254::G1AffinePoint)\n    ->RangeMultiplier(2)\n    ->Range(1 << 15, 1 << 20);\n\n}  // namespace tachyon::math\n\n// clang-format off\n// Executing tests from //tachyon/math/elliptic_curves/msm/algorithms/pippenger:pippenger_benchmark\n// -----------------------------------------------------------------------------\n// 2023-09-13T00:54:30+00:00\n// Running /home/ryan/.cache/bazel/_bazel_ryan/2e01f4ccafa60589f9bbdbefc5d15e2a/execroot/kroma_network_tachyon/bazel-out/k8-opt/bin/tachyon/math/elliptic_curves/msm/algorithms/pippenger/pippenger_benchmark.runfiles/kroma_network_tachyon/tachyon/math/elliptic_curves/msm/algorithms/pippenger/pippenger_benchmark\n// Run on (32 X 5500 MHz CPU s)\n// CPU Caches:\n//   L1 Data 48 KiB (x16)\n//   L1 Instruction 32 KiB (x16)\n//   L2 Unified 2048 KiB (x16)\n//   L3 Unified 36864 KiB (x1)\n// Load Average: 3.01, 1.86, 1.28\n// -----------------------------------------------------------------------------------------------\n// Benchmark                                                     Time             CPU   Iterations\n// -----------------------------------------------------------------------------------------------\n// BM_PippengerRandom<bn254::G1AffinePoint>/32768         22116457 ns     22115633 ns           28\n// BM_PippengerRandom<bn254::G1AffinePoint>/65536         43687728 ns     40820899 ns           18\n// BM_PippengerRandom<bn254::G1AffinePoint>/131072        78677741 ns     67452254 ns           11\n// BM_PippengerRandom<bn254::G1AffinePoint>/262144       166095297 ns    128153516 ns            6\n// BM_PippengerRandom<bn254::G1AffinePoint>/524288       321956635 ns    230632976 ns            3\n// BM_PippengerRandom<bn254::G1AffinePoint>/1048576      621665239 ns    435303495 ns            2\n// BM_PippengerNonUniform<bn254::G1AffinePoint>/32768     23061827 ns     23060417 ns           35\n// BM_PippengerNonUniform<bn254::G1AffinePoint>/65536     43744180 ns     42149318 ns           18\n// BM_PippengerNonUniform<bn254::G1AffinePoint>/131072    77002460 ns     67416707 ns           11\n// BM_PippengerNonUniform<bn254::G1AffinePoint>/262144   161179503 ns    125391620 ns            6\n// BM_PippengerNonUniform<bn254::G1AffinePoint>/524288   304928462 ns    212309880 ns            3\n// BM_PippengerNonUniform<bn254::G1AffinePoint>/1048576  589349508 ns    424155770 ns            2\n// clang-format on\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/msm/algorithms/pippenger/pippenger_unittest.cc",
    "content": "#include \"tachyon/math/elliptic_curves/msm/algorithms/pippenger/pippenger.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/elliptic_curves/bls12/bls12_381/g1.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/g1.h\"\n#include \"tachyon/math/elliptic_curves/msm/test/variable_base_msm_test_set.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\nconst size_t kSize = 40;\n\ntemplate <typename Point>\nclass PippengerTest : public testing::Test {\n public:\n  static void SetUpTestSuite() { Point::Curve::Init(); }\n\n  PippengerTest()\n      : test_set_(VariableBaseMSMTestSet<Point>::Random(\n            kSize, VariableBaseMSMMethod::kNaive)) {}\n  PippengerTest(const PippengerTest&) = delete;\n  PippengerTest& operator=(const PippengerTest&) = delete;\n  ~PippengerTest() override = default;\n\n protected:\n  VariableBaseMSMTestSet<Point> test_set_;\n};\n\n}  // namespace\n\nusing PointTypes =\n    testing::Types<bn254::G1AffinePoint, bn254::G1ProjectivePoint,\n                   bn254::G1JacobianPoint, bn254::G1PointXYZZ,\n                   // See https://github.com/kroma-network/tachyon/pull/31\n                   bls12_381::G1AffinePoint>;\nTYPED_TEST_SUITE(PippengerTest, PointTypes);\n\nTYPED_TEST(PippengerTest, Run) {\n  using Point = TypeParam;\n  using Bucket = typename Pippenger<Point>::Bucket;\n\n  const VariableBaseMSMTestSet<Point>& test_set = this->test_set_;\n\n  struct {\n    bool use_window_naf;\n    bool parallel_windows;\n  } tests[] = {\n    {false, false},\n    {true, false},\n#if defined(TACHYON_HAS_OPENMP)\n    {false, true},\n    {true, true},\n#endif  // defined(TACHYON_HAS_OPENMP)\n  };\n\n  for (const auto& test : tests) {\n    Pippenger<Point> pippenger;\n    SCOPED_TRACE(absl::Substitute(\"use_window_naf: $0 parallel_windows: $1\",\n                                  test.use_window_naf, test.parallel_windows));\n    pippenger.SetUseMSMWindowNAForTesting(test.use_window_naf);\n    pippenger.SetParallelWindows(test.parallel_windows);\n    Bucket ret;\n    EXPECT_TRUE(pippenger.Run(test_set.bases.begin(), test_set.bases.end(),\n                              test_set.scalars.begin(), test_set.scalars.end(),\n                              &ret));\n    EXPECT_EQ(ret, test_set.answer);\n  }\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/msm/fixed_base_msm.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_ELLIPTIC_CURVES_MSM_FIXED_BASE_MSM_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_MSM_FIXED_BASE_MSM_H_\n\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/parallelize.h\"\n#include \"tachyon/math/base/bit_iterator.h\"\n#include \"tachyon/math/base/semigroups.h\"\n#include \"tachyon/math/elliptic_curves/msm/msm_ctx.h\"\n#include \"tachyon/math/geometry/point_conversions.h\"\n\nnamespace tachyon::math {\n\n// MSM(Multi-Scalar Multiplication): s₀ * g⁰ + s₁ * g¹ + ... + sₙ₋₁ * gⁿ⁻¹\n// Fixed-base MSM is an operation that multiplies multiples of generators\n// with respective scalars, unlike the Variable-base MSM, which uses the\n// different base point for all multiplications.\ntemplate <typename Point>\nclass FixedBaseMSM {\n public:\n  using AddResult = typename internal::AdditiveSemigroupTraits<Point>::ReturnTy;\n  using ScalarField = typename Point::ScalarField;\n\n  constexpr void Reset(size_t size, const Point& base) {\n    ctx_ = MSMCtx::CreateDefault<ScalarField>(size);\n    UpdateWindowTable(base);\n  }\n\n  constexpr AddResult ScalarMul(const ScalarField& scalar) const {\n    // modulus_bits = 254\n    // window_bits = 5\n    // window_count = (254 + 4) / 5 = 51\n    //\n    // clang-format off\n    // SG = ((2⁰S₀ + 2¹S₁ + 2²S₂ + 2³S₃ + 2⁴S₄) + 2⁵(2⁰S₅ + 2¹S₆ + 2²S₇ + 2³S₈ + 2⁴S₉) + ... +\n    //       2²⁴⁵(2⁰S₂₄₅ + 2¹S₂₄₆ + 2²S₂₄₇ + 2³S₂₄₈ + 2⁴S₂₄₉) + 2²⁵⁰(2⁰S₂₅₀ + 2¹S₂₅₁ + 2²S₂₅₂ + 2³S₂₅₃))G\n    // clang-format on\n    using BigInt = typename ScalarField::BigIntTy;\n\n    BigInt scalar_bigint = scalar.ToBigInt();\n    auto it = BitIteratorLE<BigInt>::begin(&scalar_bigint);\n\n    unsigned int modulus_bits = ScalarField::Config::kModulusBits;\n\n    AddResult ret;\n    for (size_t i = 0; i < ctx_.window_count; ++i) {\n      size_t j = 0;\n      for (size_t bit_offset = 0; bit_offset < ctx_.window_bits; ++bit_offset) {\n        size_t bit_index = i * ctx_.window_bits + bit_offset;\n        if (bit_index < modulus_bits && *(it++)) {\n          j |= (size_t{1} << bit_offset);\n        }\n      }\n      ret += base_multiples_[i][j];\n    }\n    return ret;\n  }\n\n  template <typename ScalarIterator, typename OutputIterator>\n  [[nodiscard]] bool RunSerial(ScalarIterator scalars_first,\n                               ScalarIterator scalars_last,\n                               OutputIterator outputs_first,\n                               OutputIterator outputs_last) {\n    if (std::distance(scalars_first, scalars_last) !=\n        std::distance(outputs_first, outputs_last)) {\n      LOG(ERROR) << \"the size of scalar and output iterators don't match \";\n      return false;\n    }\n    auto scalars_it = scalars_first;\n    auto outputs_it = outputs_first;\n    while (scalars_it != scalars_last) {\n      *outputs_it = ScalarMul(*scalars_it);\n      ++scalars_it;\n      ++outputs_it;\n    }\n    return true;\n  }\n\n  template <typename ScalarContainer, typename OutputContainer>\n  [[nodiscard]] bool RunSerial(const ScalarContainer& scalars,\n                               OutputContainer& outputs) {\n    return RunSerial(std::begin(scalars), std::end(scalars),\n                     std::begin(outputs), std::end(outputs));\n  }\n\n  template <typename ScalarIterator, typename OutputIterator>\n  [[nodiscard]] bool Run(ScalarIterator scalars_first,\n                         ScalarIterator scalars_last,\n                         OutputIterator outputs_first,\n                         OutputIterator outputs_last) {\n    using scalar_iterator_category =\n        typename std::iterator_traits<ScalarIterator>::iterator_category;\n    using output_iterator_category =\n        typename std::iterator_traits<OutputIterator>::iterator_category;\n    return Run(std::move(scalars_first), std::move(scalars_last),\n               std::move(outputs_first), std::move(outputs_last),\n               scalar_iterator_category(), output_iterator_category());\n  }\n\n  template <typename ScalarContainer, typename OutputContainer>\n  [[nodiscard]] bool Run(const ScalarContainer& scalars,\n                         OutputContainer* outputs) {\n    return Run(std::begin(scalars), std::end(scalars), std::begin(*outputs),\n               std::end(*outputs));\n  }\n\n private:\n  template <typename ScalarIterator, typename OutputIterator>\n  [[nodiscard]] bool Run(ScalarIterator scalars_first,\n                         ScalarIterator scalars_last,\n                         OutputIterator outputs_first,\n                         OutputIterator outputs_last,\n                         const std::random_access_iterator_tag&,\n                         const std::random_access_iterator_tag&) {\n    using difference_type =\n        typename std::iterator_traits<ScalarIterator>::difference_type;\n    difference_type size = std::distance(scalars_first, scalars_last);\n    if (size != std::distance(outputs_first, outputs_last)) {\n      LOG(ERROR) << \"the size of scalar and output iterators don't match \";\n      return false;\n    }\n    OMP_PARALLEL_FOR(difference_type i = 0; i < size; ++i) {\n      *(outputs_first + i) = ScalarMul(*(scalars_first + i));\n    }\n    return true;\n  }\n\n  template <typename ScalarIterator, typename OutputIterator>\n  [[nodiscard]] bool Run(ScalarIterator scalars_first,\n                         ScalarIterator scalars_last,\n                         OutputIterator outputs_first,\n                         OutputIterator outputs_last,\n                         const std::input_iterator_tag&,\n                         const std::input_iterator_tag&) {\n    return RunSerial(std::move(scalars_first), std::move(scalars_last),\n                     std::move(outputs_first), std::move(outputs_last));\n  }\n\n  CONSTEXPR_IF_NOT_OPENMP void UpdateWindowTable(const Point& base) {\n    AddResult window_base;\n    if constexpr (std::is_same_v<AddResult, Point>) {\n      window_base = base;\n    } else {\n      window_base = ConvertPoint<AddResult>(base);\n    }\n    unsigned int window_bits = ctx_.window_bits;\n    unsigned int window_count = ctx_.window_count;\n    unsigned int window_size =\n        static_cast<unsigned int>(size_t{1} << window_bits);\n    unsigned int modulus_bits = ScalarField::Config::kModulusBits;\n    unsigned int last_window_size = static_cast<unsigned int>(\n        size_t{1} << (modulus_bits - (window_count - 1) * window_bits));\n    // modulus_bits = 254\n    // window_bits = 5\n    // window_count = (254 + 4) / 5 = 51\n    // window_size = 2⁵ = 32\n    // last_window_size = 2^(254 - 50 * 5) = 2⁴ = 16\n    //\n    // The contents of |window_bases| looks like following:\n    //\n    // |   0   |   1   |  ...  |   49  |   50  |\n    // +-------+-------+-------+-------+-------+\n    // |  2⁰G  |  2⁵G  |  ...  | 2²⁴⁵G | 2²⁵⁰G |\n    // +-------+-------+-------+-------+-------+\n    std::vector<AddResult> window_bases =\n        base::CreateVector(window_count, [&window_base, window_bits]() {\n          AddResult window_base_copy = window_base;\n          for (size_t i = 0; i < window_bits; ++i) {\n            window_base.DoubleInPlace();\n          }\n          return window_base_copy;\n        });\n\n    // clang-format off\n    // SG = ((2⁰S₀ + 2¹S₁ + 2²S₂ + 2³S₃ + 2⁴S₄) + 2⁵(2⁰S₅ + 2¹S₆ + 2²S₇ + 2³S₈ + 2⁴S₉) + ... +\n    //       2²⁴⁵(2⁰S₂₄₅ + 2¹S₂₄₆ + 2²S₂₄₇ + 2³S₂₄₈ + 2⁴S₂₄₉) + 2²⁵⁰(2⁰S₂₅₀ + 2¹S₂₅₁ + 2²S₂₅₂ + 2³S₂₅₃))G\n    // clang-format on\n\n    // The contents of |base_multiples_| looks like following:\n    //\n    //  j \\ i |     0     |     1     |  ...  |     49     |     50     |\n    // -------+-----------+-----------+-------+------------+------------+\n    //    0   |    2⁰G    |    2⁵G    |  ...  |    2²⁴⁵G   |    2²⁵⁰G   |\n    // -------+-----------+-----------+-------+------------+------------+\n    //    1   |  2 * 2⁰G  |  2 * 2⁵G  |  ...  |  2 * 2²⁴⁵G |  2 * 2²⁵⁰G |\n    // -------+-----------+-----------+-------+------------+------------+\n    //   ...  |    ...    |    ...    |  ...  |     ...    |     ...    |\n    // -------+-----------+-----------+-------+------------+------------+\n    //    30  |  30 * 2⁰G |  30 * 2⁵G |  ...  | 30 * 2²⁴⁵G |     0G     |\n    // -------+-----------+-----------+-------+------------+------------+\n    //    31  |  31 * 2⁰G |  31 * 2⁵G |  ...  | 31 * 2²⁴⁵G |     0G     |\n    // -------+-----------+-----------+-------+------------+------------+\n\n    base_multiples_ = std::vector<std::vector<AddResult>>(\n        window_count, std::vector<AddResult>(window_size));\n    OMP_PARALLEL_FOR(size_t i = 0; i < window_count; ++i) {\n      size_t cur_window_size =\n          i == window_count - 1 ? last_window_size : window_size;\n\n      std::vector<AddResult>& cur_window = base_multiples_[i];\n      AddResult base_multiple = AddResult::Zero();\n      const AddResult& base_multiple_base = window_bases[i];\n      for (size_t j = 0; j < cur_window_size; ++j) {\n        cur_window[j] = base_multiple;\n        base_multiple += base_multiple_base;\n      }\n    }\n  }\n\n  MSMCtx ctx_;\n  std::vector<std::vector<AddResult>> base_multiples_;\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_MSM_FIXED_BASE_MSM_H_\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/msm/fixed_base_msm_unittest.cc",
    "content": "#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/elliptic_curves/bn/bn254/g1.h\"\n#include \"tachyon/math/elliptic_curves/msm/test/fixed_base_msm_test_set.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\nconst size_t kSize = 40;\n\ntemplate <typename Point>\nclass FixedBaseMSMTest : public testing::Test {\n public:\n  static void SetUpTestSuite() { Point::Curve::Init(); }\n\n  FixedBaseMSMTest()\n      : test_set_(FixedBaseMSMTestSet<Point>::Random(\n            kSize, FixedBaseMSMMethod::kNaive)) {}\n  FixedBaseMSMTest(const FixedBaseMSMTest&) = delete;\n  FixedBaseMSMTest& operator=(const FixedBaseMSMTest&) = delete;\n  ~FixedBaseMSMTest() override = default;\n\n protected:\n  FixedBaseMSMTestSet<Point> test_set_;\n};\n\n}  // namespace\n\nusing PointTypes =\n    testing::Types<bn254::G1AffinePoint, bn254::G1ProjectivePoint,\n                   bn254::G1JacobianPoint, bn254::G1PointXYZZ>;\nTYPED_TEST_SUITE(FixedBaseMSMTest, PointTypes);\n\nTYPED_TEST(FixedBaseMSMTest, DoMSM) {\n  using Point = TypeParam;\n  using AddResult = typename internal::AdditiveSemigroupTraits<Point>::ReturnTy;\n\n  const FixedBaseMSMTestSet<Point>& test_set = this->test_set_;\n\n  FixedBaseMSM<Point> msm;\n  msm.Reset(test_set.scalars.size(), test_set.base);\n  std::vector<AddResult> ret(test_set.scalars.size());\n  EXPECT_TRUE(msm.Run(test_set.scalars, &ret));\n  EXPECT_EQ(ret, test_set.answer);\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/msm/glv.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_ELLIPTIC_CURVES_MSM_GLV_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_MSM_GLV_H_\n\n#include \"tachyon/math/base/bit_iterator.h\"\n#include \"tachyon/math/base/gmp/bit_traits.h\"\n#include \"tachyon/math/base/gmp/signed_value.h\"\n#include \"tachyon/math/elliptic_curves/semigroups.h\"\n#include \"tachyon/math/matrix/gmp_num_traits.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename Point>\nclass GLV {\n public:\n  using BaseField = typename Point::BaseField;\n  using ScalarField = typename Point::ScalarField;\n  using RetPoint = typename internal::AdditiveSemigroupTraits<Point>::ReturnTy;\n\n  struct CoefficientDecompositionResult {\n    SignedValue<mpz_class> k1;\n    SignedValue<mpz_class> k2;\n  };\n\n  static Point Endomorphism(const Point& point) {\n    return Point::Endomorphism(point);\n  }\n  // Decomposes a scalar |k| into k1, k2, s.t. k = k1 + lambda k2,\n  static CoefficientDecompositionResult Decompose(const ScalarField& k) {\n    using Config = typename Point::Curve::Config;\n\n    Eigen::Matrix<mpz_class, 2, 2> coefficients(\n        {{Config::kGLVCoeffs[0], Config::kGLVCoeffs[1]},\n         {Config::kGLVCoeffs[2], Config::kGLVCoeffs[3]}});\n\n    decltype(auto) scalar = k.ToMpzClass();\n    const mpz_class& n12 = coefficients(0, 1);\n    const mpz_class& n22 = coefficients(1, 1);\n    mpz_class r;\n    gmp::WriteLimbs(ScalarField::Config::kModulus.limbs, ScalarField::kLimbNums,\n                    &r);\n\n    // clang-format off\n    // NOTE(chokobole): We can't calculate using below directly.\n    //\n    // Eigen::Matrix<mpz_class, 1, 2>(scalar, mpz_class(0)) * coefficients.inverse()\n    //\n    // Eigen matrix emits an error like below:\n    //\n    // external/eigen_archive/Eigen/src/LU/InverseImpl.h:352:3: error: static assertion failed: THIS_FUNCTION_IS_NOT_FOR_INTEGER_NUMERIC_TYPES\n    // 352 |   EIGEN_STATIC_ASSERT(!NumTraits<Scalar>::IsInteger,THIS_FUNCTION_IS_NOT_FOR_INTEGER_NUMERIC_TYPES)\n    // clang-format on\n    mpz_class beta_1 = scalar * n22 / r;\n    mpz_class beta_2 = scalar * (-n12) / r;\n\n    Eigen::Matrix<mpz_class, 1, 2> b =\n        Eigen::Matrix<mpz_class, 1, 2>{beta_1, beta_2} * coefficients;\n\n    // k1\n    mpz_class k1 = scalar - b[0];\n\n    // k2\n    mpz_class k2 = -b[1];\n\n    return {SignedValue<mpz_class>(k1), SignedValue<mpz_class>(k2)};\n  }\n\n  static RetPoint Mul(const Point& p, const ScalarField& k) {\n    CoefficientDecompositionResult result = Decompose(k);\n\n    Point b1 = p;\n    Point b2 = Endomorphism(p);\n\n    if (result.k1.sign == Sign::kNegative) {\n      b1.NegateInPlace();\n    }\n    if (result.k2.sign == Sign::kNegative) {\n      b2.NegateInPlace();\n    }\n\n    RetPoint b1b2 = b1 + b2;\n\n    auto k1_begin = BitIteratorBE<mpz_class>::begin(&result.k1.abs_value);\n    auto k1_end = BitIteratorBE<mpz_class>::end(&result.k1.abs_value);\n    auto k2_begin = BitIteratorBE<mpz_class>::begin(&result.k2.abs_value);\n\n    RetPoint ret = RetPoint::Zero();\n    bool skip_zeros = true;\n    auto k1_it = k1_begin;\n    auto k2_it = k2_begin;\n    while (k1_it != k1_end) {\n      if (skip_zeros && !(*k1_it) && !(*k2_it)) {\n        skip_zeros = false;\n        ++k1_it;\n        ++k2_it;\n        continue;\n      }\n      skip_zeros = false;\n      ret.DoubleInPlace();\n      if ((*k1_it)) {\n        if (*(k2_it)) {\n          ret += b1b2;\n        } else {\n          ret += b1;\n        }\n      } else {\n        if (*(k2_it)) {\n          ret += b2;\n        }\n      }\n      ++k1_it;\n      ++k2_it;\n    }\n    return ret;\n  }\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_MSM_GLV_H_\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/msm/glv_unittest.cc",
    "content": "#include \"tachyon/math/elliptic_curves/msm/glv.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/elliptic_curves/bls12/bls12_381/g1.h\"\n#include \"tachyon/math/elliptic_curves/bls12/bls12_381/g2.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/g1.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/g2.h\"\n#include \"tachyon/math/geometry/point_conversions.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\ntemplate <typename Point>\nclass GLVTest : public testing::Test {\n public:\n  static void SetUpTestSuite() { Point::Curve::Init(); }\n};\n\n}  // namespace\n\n// This iterates all the points in bls12_381 G1 to test whether GLV works on\n// various points.\nusing PointTypes =\n    testing::Types<bls12_381::G1AffinePoint, bls12_381::G1ProjectivePoint,\n                   bls12_381::G1JacobianPoint, bls12_381::G1PointXYZZ,\n                   bls12_381::G2JacobianPoint, bn254::G1JacobianPoint,\n                   bn254::G2JacobianPoint>;\nTYPED_TEST_SUITE(GLVTest, PointTypes);\n\nTYPED_TEST(GLVTest, Endomorphism) {\n  using Point = TypeParam;\n  using RetPoint = typename internal::AdditiveSemigroupTraits<Point>::ReturnTy;\n\n  EXPECT_TRUE(Point::Curve::Config::kEndomorphismCoefficient.Pow(3).IsOne());\n  Point base = Point::Random();\n  EXPECT_EQ(base * Point::Curve::Config::kLambda,\n            ConvertPoint<RetPoint>(Point::Endomorphism(base)));\n}\n\nTYPED_TEST(GLVTest, Decompose) {\n  using Point = TypeParam;\n  using ScalarField = typename Point::ScalarField;\n\n  ScalarField scalar = ScalarField::Random();\n  auto result = GLV<Point>::Decompose(scalar);\n  ScalarField k1 = ScalarField::FromMpzClass(result.k1.abs_value);\n  ScalarField k2 = ScalarField::FromMpzClass(result.k2.abs_value);\n  if (result.k1.sign == Sign::kNegative) {\n    k1.NegateInPlace();\n  }\n  if (result.k2.sign == Sign::kNegative) {\n    k2.NegateInPlace();\n  }\n  EXPECT_EQ(scalar, k1 + Point::Curve::Config::kLambda * k2);\n}\n\nTYPED_TEST(GLVTest, Mul) {\n  using Point = TypeParam;\n  using ScalarField = typename Point::ScalarField;\n\n  Point base = Point::Random();\n  ScalarField scalar = ScalarField::Random();\n  EXPECT_EQ(GLV<Point>::Mul(base, scalar), base * scalar);\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/msm/msm_ctx.h",
    "content": "#ifndef TACHYON_MATH_ELLIPTIC_CURVES_MSM_MSM_CTX_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_MSM_MSM_CTX_H_\n\n#include <stddef.h>\n\n#include <cmath>\n\n#include \"tachyon/export.h\"\n\nnamespace tachyon::math {\n\nstruct TACHYON_EXPORT MSMCtx {\n  unsigned int window_count = 0;\n  unsigned int window_bits = 0;\n  unsigned int size = 0;\n\n  constexpr unsigned int GetWindowLength() const {\n    return static_cast<unsigned int>(1) << window_bits;\n  }\n\n  template <typename ScalarField>\n  constexpr static MSMCtx CreateDefault(size_t size) {\n    MSMCtx ctx{};\n    ctx.window_bits = ComputeWindowsBits(size);\n    ctx.window_count = ComputeWindowsCount<ScalarField>(ctx.window_bits);\n    ctx.size = size;\n    return ctx;\n  }\n\n  // The result of this function is only approximately `ln(a)`.\n  // See https://github.com/scipr-lab/zexe/issues/79#issue-556220473\n  constexpr static unsigned int LnWithoutFloats(size_t a) {\n    // log2(a) * ln(2)\n    return std::log2(a) * 69 / 100;\n  }\n\n  constexpr static unsigned int ComputeWindowsBits(size_t size) {\n    if (size < 32) {\n      return 3;\n    } else {\n      return LnWithoutFloats(size) + 2;\n    }\n  }\n\n  template <typename ScalarField>\n  constexpr static unsigned int ComputeWindowsCount(unsigned int window_bits) {\n    return (ScalarField::Config::kModulusBits + window_bits - 1) / window_bits;\n  }\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_MSM_MSM_CTX_H_\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/msm/msm_util.h",
    "content": "#ifndef TACHYON_MATH_ELLIPTIC_CURVES_MSM_MSM_UTIL_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_MSM_MSM_UTIL_H_\n\n#include <cmath>\n#include <type_traits>\n\n#include \"tachyon/base/template_util.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename BaseInputIterator, typename ScalarInputIterator,\n          typename Point, typename ScalarField>\ninline constexpr bool IsAbleToMSM =\n    std::is_same_v<Point, base::iter_value_t<BaseInputIterator>> &&\n    std::is_same_v<ScalarField, base::iter_value_t<ScalarInputIterator>>;\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_MSM_MSM_UTIL_H_\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/msm/test/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"fixed_base_msm_test_set\",\n    testonly = True,\n    hdrs = [\"fixed_base_msm_test_set.h\"],\n    deps = [\n        \"//tachyon/base:logging\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/base/files:file_util\",\n        \"//tachyon/math/elliptic_curves/msm:fixed_base_msm\",\n        \"//tachyon/math/elliptic_curves/test:random\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"variable_base_msm_test_set\",\n    testonly = True,\n    hdrs = [\"variable_base_msm_test_set.h\"],\n    deps = [\n        \"//tachyon/base:logging\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/base/files:file_util\",\n        \"//tachyon/math/elliptic_curves/msm:variable_base_msm\",\n        \"//tachyon/math/elliptic_curves/test:random\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/msm/test/fixed_base_msm_test_set.h",
    "content": "#ifndef TACHYON_MATH_ELLIPTIC_CURVES_MSM_TEST_FIXED_BASE_MSM_TEST_SET_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_MSM_TEST_FIXED_BASE_MSM_TEST_SET_H_\n\n#include <vector>\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/files/file_util.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/math/base/semigroups.h\"\n#include \"tachyon/math/elliptic_curves/msm/fixed_base_msm.h\"\n#include \"tachyon/math/elliptic_curves/test/random.h\"\n#include \"tachyon/math/geometry/point_conversions.h\"\n\nnamespace tachyon::math {\n\nenum class FixedBaseMSMMethod {\n  kNone,\n  kMSM,\n  kNaive,\n};\n\ntemplate <typename Point>\nstruct FixedBaseMSMTestSet {\n  using AddResult = typename internal::AdditiveSemigroupTraits<Point>::ReturnTy;\n  using ScalarField = typename Point::ScalarField;\n\n  Point base;\n  std::vector<ScalarField> scalars;\n  std::vector<AddResult> answer;\n\n  constexpr size_t size() const { return scalars.size(); }\n\n  static FixedBaseMSMTestSet Random(size_t size, FixedBaseMSMMethod method) {\n    FixedBaseMSMTestSet test_set;\n    test_set.base = Point::Random();\n    test_set.scalars = base::CreateVectorParallel(\n        size, []() { return ScalarField::Random(); });\n    test_set.ComputeAnswer(method);\n    return test_set;\n  }\n\n  static FixedBaseMSMTestSet Easy(size_t size, FixedBaseMSMMethod method) {\n    FixedBaseMSMTestSet test_set;\n    test_set.base = Point::Random();\n    ScalarField s = ScalarField::One();\n    test_set.scalars = base::CreateVector(size, [&s]() {\n      ScalarField ret = s;\n      s += ScalarField::One();\n      return ret;\n    });\n    test_set.ComputeAnswer(method);\n    return test_set;\n  }\n\n  bool WriteToFile(const base::FilePath& dir) const {\n    {\n      std::stringstream ss;\n      ss << base.ToString() << std::endl;\n      if (!base::WriteFile(dir.Append(\"base.txt\"), ss.str())) return false;\n    }\n    std::stringstream ss;\n    for (size_t i = 0; i < scalars.size(); ++i) {\n      ss << scalars[i].ToString() << std::endl;\n    }\n    return base::WriteFile(dir.Append(\"scalars.txt\"), ss.str());\n  }\n\n private:\n  void ComputeAnswer(FixedBaseMSMMethod method) {\n    switch (method) {\n      case FixedBaseMSMMethod::kNone:\n        break;\n      case FixedBaseMSMMethod::kMSM: {\n        FixedBaseMSM<Point> msm;\n        msm.Reset(scalars.size(), base);\n        answer.resize(scalars.size());\n        CHECK(msm.Run(scalars, &answer));\n        break;\n      }\n      case FixedBaseMSMMethod::kNaive: {\n        for (size_t i = 0; i < scalars.size(); ++i) {\n          answer.push_back(base * scalars[i]);\n        }\n        break;\n      }\n    }\n  }\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_MSM_TEST_FIXED_BASE_MSM_TEST_SET_H_\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/msm/test/variable_base_msm_test_set.h",
    "content": "#ifndef TACHYON_MATH_ELLIPTIC_CURVES_MSM_TEST_VARIABLE_BASE_MSM_TEST_SET_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_MSM_TEST_VARIABLE_BASE_MSM_TEST_SET_H_\n\n#include <vector>\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/files/file_util.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/math/base/semigroups.h\"\n#include \"tachyon/math/elliptic_curves/msm/variable_base_msm.h\"\n#include \"tachyon/math/elliptic_curves/test/random.h\"\n#include \"tachyon/math/geometry/point_conversions.h\"\n\nnamespace tachyon::math {\n\nenum class VariableBaseMSMMethod {\n  kNone,\n  kMSM,\n  kNaive,\n};\n\ntemplate <typename Point,\n          typename Bucket = typename VariableBaseMSM<Point>::Bucket>\nstruct VariableBaseMSMTestSet {\n  using ScalarField = typename Point::ScalarField;\n\n  std::vector<Point> bases;\n  std::vector<ScalarField> scalars;\n  Bucket answer;\n\n  constexpr size_t size() const { return bases.size(); }\n\n  static VariableBaseMSMTestSet Random(size_t size,\n                                       VariableBaseMSMMethod method) {\n    VariableBaseMSMTestSet test_set;\n    test_set.bases = CreatePseudoRandomPoints<Point>(size);\n    test_set.scalars = base::CreateVectorParallel(\n        size, []() { return ScalarField::Random(); });\n    test_set.ComputeAnswer(method);\n    return test_set;\n  }\n\n  static VariableBaseMSMTestSet NonUniform(size_t size, size_t scalar_size,\n                                           VariableBaseMSMMethod method) {\n    VariableBaseMSMTestSet test_set;\n    test_set.bases = CreatePseudoRandomPoints<Point>(size);\n    std::vector<ScalarField> scalar_sets =\n        base::CreateVector(scalar_size, []() { return ScalarField::Random(); });\n    test_set.scalars = base::CreateVectorParallel(\n        size, [&scalar_sets]() { return base::UniformElement(scalar_sets); });\n    test_set.ComputeAnswer(method);\n    return test_set;\n  }\n\n  static VariableBaseMSMTestSet Easy(size_t size,\n                                     VariableBaseMSMMethod method) {\n    VariableBaseMSMTestSet test_set;\n    test_set.bases =\n        base::CreateVectorParallel(size, []() { return Point::Generator(); });\n    ScalarField s = ScalarField::One();\n    test_set.scalars = base::CreateVector(size, [&s]() {\n      ScalarField ret = s;\n      s += ScalarField::One();\n      return ret;\n    });\n    test_set.ComputeAnswer(method);\n    return test_set;\n  }\n\n  bool WriteToFile(const base::FilePath& dir) const {\n    {\n      std::stringstream ss;\n      for (size_t i = 0; i < bases.size(); ++i) {\n        ss << bases[i].ToString() << std::endl;\n      }\n      if (!base::WriteFile(dir.Append(\"bases.txt\"), ss.str())) return false;\n    }\n    std::stringstream ss;\n    for (size_t i = 0; i < scalars.size(); ++i) {\n      ss << scalars[i].ToString() << std::endl;\n    }\n    return base::WriteFile(dir.Append(\"scalars.txt\"), ss.str());\n  }\n\n private:\n  void ComputeAnswer(VariableBaseMSMMethod method) {\n    answer = Bucket::Zero();\n    switch (method) {\n      case VariableBaseMSMMethod::kNone:\n        break;\n      case VariableBaseMSMMethod::kMSM: {\n        VariableBaseMSM<Point> msm;\n        CHECK(msm.Run(bases, scalars, &answer));\n        break;\n      }\n      case VariableBaseMSMMethod::kNaive: {\n        using AddResult =\n            typename internal::AdditiveSemigroupTraits<Point>::ReturnTy;\n        AddResult sum = AddResult::Zero();\n        for (size_t i = 0; i < bases.size(); ++i) {\n          sum += bases[i] * scalars[i];\n        }\n        answer = ConvertPoint<Bucket>(sum);\n        break;\n      }\n    }\n  }\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_MSM_TEST_VARIABLE_BASE_MSM_TEST_SET_H_\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/msm/variable_base_msm.h",
    "content": "#ifndef TACHYON_MATH_ELLIPTIC_CURVES_MSM_VARIABLE_BASE_MSM_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_MSM_VARIABLE_BASE_MSM_H_\n\n#include <utility>\n\n#include \"tachyon/math/elliptic_curves/msm/algorithms/pippenger/pippenger_adapter.h\"\n\nnamespace tachyon::math {\n\n// MSM(Multi-Scalar Multiplication): s₀ * g₀ + s₁ * g₁ + ... + sₙ₋₁ * gₙ₋₁\n// Variable-base MSM is an operation that multiplies different base points\n// with respective scalars, unlike the Fixed-base MSM, which uses the same\n// base point for all multiplications.\n// This implementation uses Pippenger's algorithm to compute the MSM.\ntemplate <typename Point>\nclass VariableBaseMSM {\n public:\n  using Bucket = typename Pippenger<Point>::Bucket;\n\n  template <typename BaseInputIterator, typename ScalarInputIterator>\n  [[nodiscard]] bool Run(BaseInputIterator bases_first,\n                         BaseInputIterator bases_last,\n                         ScalarInputIterator scalars_first,\n                         ScalarInputIterator scalars_last, Bucket* ret) {\n    PippengerAdapter<Point> pippenger;\n    return pippenger.Run(std::move(bases_first), std::move(bases_last),\n                         std::move(scalars_first), std::move(scalars_last),\n                         ret);\n  }\n\n  template <typename BaseContainer, typename ScalarContainer>\n  [[nodiscard]] bool Run(const BaseContainer& bases,\n                         const ScalarContainer& scalars, Bucket* ret) {\n    return Run(std::begin(bases), std::end(bases), std::begin(scalars),\n               std::end(scalars), ret);\n  }\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_MSM_VARIABLE_BASE_MSM_H_\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/msm/variable_base_msm_gpu.h",
    "content": "#ifndef TACHYON_MATH_ELLIPTIC_CURVES_MSM_VARIABLE_BASE_MSM_GPU_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_MSM_VARIABLE_BASE_MSM_GPU_H_\n\n#include <memory>\n\n#include \"tachyon/math/elliptic_curves/msm/algorithms/icicle/icicle_msm.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename Point>\nclass VariableBaseMSMGpu {\n public:\n  using Curve = typename Point::Curve;\n  using Bucket = ProjectivePoint<Curve>;\n\n  VariableBaseMSMGpu(gpuMemPool_t mem_pool, gpuStream_t stream)\n      : impl_(std::make_unique<IcicleMSM<Point>>(mem_pool, stream)) {}\n  VariableBaseMSMGpu(const VariableBaseMSMGpu& other) = delete;\n  VariableBaseMSMGpu& operator=(const VariableBaseMSMGpu& other) = delete;\n\n  template <typename BaseContainer, typename ScalarContainer>\n  [[nodiscard]] bool Run(const BaseContainer& bases,\n                         const ScalarContainer& cpu_scalars,\n                         ProjectivePoint<Curve>* cpu_result) {\n    return impl_->Run(bases, cpu_scalars, cpu_result);\n  }\n\n private:\n  std::unique_ptr<IcicleMSM<Point>> impl_;\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_MSM_VARIABLE_BASE_MSM_GPU_H_\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/msm/variable_base_msm_gpu_unittest.cc",
    "content": "#include \"tachyon/math/elliptic_curves/msm/variable_base_msm_gpu.h\"\n\n#include <limits>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/device/gpu/gpu_enums.h\"\n#include \"tachyon/device/gpu/scoped_mem_pool.h\"\n#include \"tachyon/device/gpu/scoped_stream.h\"\n#include \"tachyon/math/elliptic_curves/msm/test/variable_base_msm_test_set.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\nconstexpr size_t kThreadNum = 32;\n\nusing namespace device;\n\ntemplate <typename Point>\nclass VariableMSMCorrectnessGpuTest : public testing::Test {\n public:\n  using Curve = typename Point::Curve;\n\n  constexpr static size_t kLogCount = 10;\n  constexpr static size_t kCount = size_t{1} << kLogCount;\n\n  static void SetUpTestSuite() {\n    Point::Curve::Init();\n\n    test_set_ = VariableBaseMSMTestSet<Point>::Random(\n        kCount, VariableBaseMSMMethod::kMSM);\n\n    expected_ = test_set_.answer.ToProjective();\n  }\n\n  static void TearDownTestSuite() { GPU_MUST_SUCCEED(gpuDeviceReset(), \"\"); }\n\n protected:\n  static VariableBaseMSMTestSet<Point> test_set_;\n  static ProjectivePoint<Curve> expected_;\n};\n\ntemplate <typename Point>\nVariableBaseMSMTestSet<Point> VariableMSMCorrectnessGpuTest<Point>::test_set_;\ntemplate <typename Point>\nProjectivePoint<typename Point::Curve>\n    VariableMSMCorrectnessGpuTest<Point>::expected_;\n\n}  // namespace\n\nusing PointTypes =\n    testing::Types<bls12_381::G1AffinePoint, bls12_381::G2AffinePoint,\n                   bn254::G1AffinePoint, bn254::G2AffinePoint>;\nTYPED_TEST_SUITE(VariableMSMCorrectnessGpuTest, PointTypes);\n\nTYPED_TEST(VariableMSMCorrectnessGpuTest, MSM) {\n  using Point = TypeParam;\n  using Curve = typename Point::Curve;\n\n  gpuMemPoolProps props = {gpuMemAllocationTypePinned,\n                           gpuMemHandleTypeNone,\n                           {gpuMemLocationTypeDevice, 0}};\n  gpu::ScopedMemPool mem_pool = gpu::CreateMemPool(&props);\n\n  uint64_t mem_pool_threshold = std::numeric_limits<uint64_t>::max();\n  gpuError_t error = gpuMemPoolSetAttribute(\n      mem_pool.get(), gpuMemPoolAttrReleaseThreshold, &mem_pool_threshold);\n  ASSERT_EQ(error, gpuSuccess);\n\n  gpu::ScopedStream stream = gpu::CreateStream();\n\n  VariableBaseMSMGpu<Point> msm_gpu(mem_pool.get(), stream.get());\n  ProjectivePoint<Curve> actual;\n  ASSERT_TRUE(\n      msm_gpu.Run(this->test_set_.bases, this->test_set_.scalars, &actual));\n  EXPECT_EQ(actual, this->expected_);\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/msm/variable_base_msm_unittest.cc",
    "content": "#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/elliptic_curves/bn/bn254/g1.h\"\n#include \"tachyon/math/elliptic_curves/msm/test/variable_base_msm_test_set.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\nconst size_t kSize = 40;\n\ntemplate <typename Point>\nclass VariableBaseMSMTest : public testing::Test {\n public:\n  static void SetUpTestSuite() { Point::Curve::Init(); }\n\n  VariableBaseMSMTest()\n      : test_set_(VariableBaseMSMTestSet<Point>::Random(\n            kSize, VariableBaseMSMMethod::kNaive)) {}\n  VariableBaseMSMTest(const VariableBaseMSMTest&) = delete;\n  VariableBaseMSMTest& operator=(const VariableBaseMSMTest&) = delete;\n  ~VariableBaseMSMTest() override = default;\n\n protected:\n  VariableBaseMSMTestSet<Point> test_set_;\n};\n\n}  // namespace\n\nusing PointTypes =\n    testing::Types<bn254::G1AffinePoint, bn254::G1ProjectivePoint,\n                   bn254::G1JacobianPoint, bn254::G1PointXYZZ>;\nTYPED_TEST_SUITE(VariableBaseMSMTest, PointTypes);\n\nTYPED_TEST(VariableBaseMSMTest, DoMSM) {\n  using Point = TypeParam;\n  using Bucket = typename VariableBaseMSM<Point>::Bucket;\n\n  const VariableBaseMSMTestSet<Point>& test_set = this->test_set_;\n\n  VariableBaseMSM<Point> msm;\n  Bucket ret;\n  EXPECT_TRUE(msm.Run(test_set.bases, test_set.scalars, &ret));\n  EXPECT_EQ(ret, test_set.answer);\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/pairing/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"ell_coeff\",\n    hdrs = [\"ell_coeff.h\"],\n    deps = [\"@com_google_absl//absl/strings\"],\n)\n\ntachyon_cc_library(\n    name = \"g2_prepared_base\",\n    hdrs = [\"g2_prepared_base.h\"],\n    deps = [\n        \":ell_coeff\",\n        \"//tachyon/base/strings:string_util\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"g2_projective\",\n    hdrs = [\"g2_projective.h\"],\n    deps = [\n        \":ell_coeff\",\n        \":twist_type\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"pairing\",\n    hdrs = [\"pairing.h\"],\n    deps = [\n        \"//tachyon/base:template_util\",\n        \"//tachyon/base/containers:container_util\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"pairing_friendly_curve\",\n    hdrs = [\"pairing_friendly_curve.h\"],\n    deps = [\n        \":ell_coeff\",\n        \":twist_type\",\n        \"@com_google_absl//absl/base\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"twist_type\",\n    hdrs = [\"twist_type.h\"],\n    deps = [\n        \"//tachyon/base:logging\",\n        \"//tachyon/base/flag\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"pairing_unittests\",\n    srcs = [\"pairing_unittest.cc\"],\n    deps = [\n        \":pairing\",\n        \"//tachyon/math/elliptic_curves/bls12/bls12_381\",\n        \"//tachyon/math/elliptic_curves/bn/bn254\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/pairing/ell_coeff.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_ELLIPTIC_CURVES_PAIRING_ELL_COEFF_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_PAIRING_ELL_COEFF_H_\n\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"absl/strings/substitute.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename F>\nclass EllCoeff {\n public:\n  EllCoeff() = default;\n  EllCoeff(const F& c0, const F& c1, const F& c2) : c0_(c0), c1_(c1), c2_(c2) {}\n  EllCoeff(F&& c0, F&& c1, F&& c2)\n      : c0_(std::move(c0)), c1_(std::move(c1)), c2_(std::move(c2)) {}\n\n  const F& c0() const { return c0_; }\n  const F& c1() const { return c1_; }\n  const F& c2() const { return c2_; }\n\n  std::string ToString() const {\n    return absl::Substitute(\"{c0: $0, c1: $1, c2: $2}\", c0_.ToString(),\n                            c1_.ToString(), c2_.ToString());\n  }\n\n  std::string ToHexString(bool pad_zero = false) const {\n    return absl::Substitute(\n        \"{c0: $0, c1: $1, c2: $2}\", c0_.ToHexString(pad_zero),\n        c1_.ToHexString(pad_zero), c2_.ToHexString(pad_zero));\n  }\n\n private:\n  F c0_;\n  F c1_;\n  F c2_;\n};\n\ntemplate <typename F>\nusing EllCoeffs = std::vector<EllCoeff<F>>;\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_PAIRING_ELL_COEFF_H_\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/pairing/g2_prepared_base.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_ELLIPTIC_CURVES_PAIRING_G2_PREPARED_BASE_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_PAIRING_G2_PREPARED_BASE_H_\n\n#include <string>\n#include <utility>\n\n#include \"tachyon/base/strings/string_util.h\"\n#include \"tachyon/math/elliptic_curves/pairing/ell_coeff.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename PairingFriendlyCurveConfig>\nclass G2PreparedBase {\n public:\n  using Config = PairingFriendlyCurveConfig;\n  using G2Curve = typename Config::G2Curve;\n  using Fp2 = typename G2Curve::BaseField;\n\n  G2PreparedBase() = default;\n  explicit G2PreparedBase(const EllCoeffs<Fp2>& ell_coeffs)\n      : ell_coeffs_(ell_coeffs), infinity_(false) {}\n  explicit G2PreparedBase(EllCoeffs<Fp2>&& ell_coeffs)\n      : ell_coeffs_(std::move(ell_coeffs)), infinity_(false) {}\n\n  const EllCoeffs<Fp2>& ell_coeffs() const { return ell_coeffs_; }\n  bool infinity() const { return infinity_; }\n\n  std::string ToString() const {\n    return absl::Substitute(\"{ell_coeffs: $0, infinity: $1}\",\n                            base::ContainerToString(ell_coeffs_), infinity_);\n  }\n\n  std::string ToHexString(bool pad_zero = false) const {\n    return absl::Substitute(\"{ell_coeffs: $0, infinity: $1}\",\n                            base::ContainerToString(ell_coeffs_, pad_zero),\n                            infinity_);\n  }\n\n protected:\n  // Stores the coefficients of the line evaluations as calculated in\n  // https://eprint.iacr.org/2013/722.pdf\n  EllCoeffs<Fp2> ell_coeffs_;\n  bool infinity_ = true;\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_PAIRING_G2_PREPARED_BASE_H_\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/pairing/g2_projective.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_ELLIPTIC_CURVES_PAIRING_G2_PROJECTIVE_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_PAIRING_G2_PROJECTIVE_H_\n\n#include <utility>\n\n#include \"tachyon/math/elliptic_curves/pairing/ell_coeff.h\"\n#include \"tachyon/math/elliptic_curves/pairing/twist_type.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename PairingFriendlyCurveConfig>\nclass G2Projective {\n public:\n  using Config = PairingFriendlyCurveConfig;\n  using G2Curve = typename Config::G2Curve;\n  using Fp2 = typename G2Curve::BaseField;\n  using Fp = typename Fp2::BaseField;\n  using G2AffinePoint = typename G2Curve::AffinePoint;\n\n  G2Projective() = default;\n  G2Projective(const Fp2& x, const Fp2& y, const Fp2& z)\n      : x_(x), y_(y), z_(z) {}\n  G2Projective(Fp2&& x, Fp2&& y, Fp2&& z)\n      : x_(std::move(x)), y_(std::move(y)), z_(std::move(z)) {}\n\n  const Fp2& x() const { return x_; }\n  const Fp2& y() const { return y_; }\n  const Fp2& z() const { return z_; }\n\n  // TODO(chokobole): Leave a comment to help understand readers.\n  EllCoeff<Fp2> AddInPlace(const G2AffinePoint& q) {\n    // Formula for line function when working with\n    // homogeneous projective coordinates.\n    Fp2 theta = y_ - (q.y() * z_);\n    Fp2 lambda = x_ - (q.x() * z_);\n    Fp2 c = theta.Square();\n    Fp2 d = lambda.Square();\n    Fp2 e = lambda * d;\n    Fp2 f = z_ * c;\n    Fp2 g = x_ * d;\n    Fp2 h = e + f - g.Double();\n    x_ = lambda * h;\n    y_ = theta * (g - h) - (e * y_);\n    z_ *= e;\n    Fp2 j = theta * q.x() - (lambda * q.y());\n\n    if constexpr (Config::kTwistType == TwistType::kM) {\n      return {j, -theta, lambda};\n    } else {\n      return {lambda, -theta, j};\n    }\n  }\n\n  // TODO(chokobole): Leave a comment to help understand readers.\n  EllCoeff<Fp2> DoubleInPlace(const Fp& two_inv) {\n    // Formula for line function when working with\n    // homogeneous projective coordinates.\n    Fp2 a = x_ * y_;\n    a *= two_inv;\n    Fp2 b = y_.Square();\n    Fp2 c = z_.Square();\n    Fp2 e = G2Curve::Config::kB * (c.Double() + c);\n    Fp2 f = e.Double() + e;\n    Fp2 g = b + f;\n    g *= two_inv;\n    Fp2 h = (y_ + z_).Square() - (b + c);\n    Fp2 i = e - b;\n    Fp2 j = x_.Square();\n    Fp2 e_square = e.Square();\n\n    x_ = a * (b - f);\n    y_ = g.Square() - (e_square.Double() + e_square);\n    z_ = b * h;\n\n    if constexpr (Config::kTwistType == TwistType::kM) {\n      return {i, j.Double() + j, -h};\n    } else {\n      return {-h, j.Double() + j, i};\n    }\n  }\n\n  G2Projective& NegateInPlace() {\n    y_.NegateInPlace();\n    return *this;\n  }\n\n private:\n  Fp2 x_;\n  Fp2 y_;\n  Fp2 z_;\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_PAIRING_G2_PROJECTIVE_H_\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/pairing/pairing.h",
    "content": "#ifndef TACHYON_MATH_ELLIPTIC_CURVES_PAIRING_PAIRING_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_PAIRING_PAIRING_H_\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/template_util.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename Curve, typename G1AffinePointContainer,\n          typename G2AffineOrPreparedPointContainer>\nauto Pairing(const G1AffinePointContainer& a,\n             const G2AffineOrPreparedPointContainer& b) {\n  using G2Prepared = typename Curve::G2Prepared;\n  if constexpr (std::is_same_v<\n                    base::container_value_t<G2AffineOrPreparedPointContainer>,\n                    G2Prepared>) {\n    return Curve::FinalExponentiation(Curve::MultiMillerLoop(a, b));\n  } else {\n    using G2AffinePoint = typename Curve::G2Curve::AffinePoint;\n    return Curve::FinalExponentiation(\n        Curve::MultiMillerLoop(a, base::Map(b, [](const G2AffinePoint& point) {\n                                 return G2Prepared::From(point);\n                               })));\n  }\n}\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_PAIRING_PAIRING_H_\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/pairing/pairing_friendly_curve.h",
    "content": "#ifndef TACHYON_MATH_ELLIPTIC_CURVES_PAIRING_PAIRING_FRIENDLY_CURVE_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_PAIRING_PAIRING_FRIENDLY_CURVE_H_\n\n#include <vector>\n\n#include \"absl/base/call_once.h\"\n\n#include \"tachyon/math/elliptic_curves/pairing/ell_coeff.h\"\n#include \"tachyon/math/elliptic_curves/pairing/twist_type.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename _Config>\nclass PairingFriendlyCurve {\n public:\n  using Config = _Config;\n  using G1Curve = typename Config::G1Curve;\n  using G2Curve = typename Config::G2Curve;\n  using Fp2 = typename G2Curve::BaseField;\n  using Fp12 = typename Config::Fp12;\n  using G1AffinePoint = typename G1Curve::AffinePoint;\n\n  static void Init() {\n    static absl::once_flag once;\n    absl::call_once(once, []() {\n      Config::Init();\n      Fp12::Init();\n    });\n  }\n\n protected:\n  class Pair {\n   public:\n    Pair() = default;\n    Pair(const G1AffinePoint* g1, const std::vector<EllCoeff<Fp2>>* ell_coeffs)\n        : g1_(g1), ell_coeffs_(ell_coeffs) {}\n\n    const G1AffinePoint& g1() const { return *g1_; }\n    const EllCoeff<Fp2>& NextEllCoeff() const { return (*ell_coeffs_)[idx_++]; }\n\n   private:\n    const G1AffinePoint* g1_ = nullptr;\n    const std::vector<EllCoeff<Fp2>>* ell_coeffs_ = nullptr;\n    mutable size_t idx_ = 0;\n  };\n\n  static Fp12 PowByX(const Fp12& f_in) {\n    Fp12 f = f_in.CyclotomicPow(Config::kX);\n    if constexpr (Config::kXIsNegative) {\n      CHECK(f.CyclotomicInverseInPlace());\n    }\n    return f;\n  }\n\n  static Fp12 PowByNegX(const Fp12& f_in) {\n    Fp12 f = f_in.CyclotomicPow(Config::kX);\n    if constexpr (!Config::kXIsNegative) {\n      CHECK(f.CyclotomicInverseInPlace());\n    }\n    return f;\n  }\n\n  // TODO(chokobole): Leave a comment to help understand readers.\n  // Evaluates the line function at point |p|.\n  static void Ell(Fp12& f, const EllCoeff<Fp2>& coeffs,\n                  const G1AffinePoint& p) {\n    if constexpr (Config::kTwistType == TwistType::kM) {\n      f.MulInPlaceBy014(coeffs.c0(), coeffs.c1() * p.x(), coeffs.c2() * p.y());\n    } else {\n      f.MulInPlaceBy034(coeffs.c0() * p.y(), coeffs.c1() * p.x(), coeffs.c2());\n    }\n  }\n\n  template <typename G1AffinePointContainer, typename G2PreparedContainer>\n  static std::vector<Pair> CreatePairs(const G1AffinePointContainer& a,\n                                       const G2PreparedContainer& b) {\n    size_t size = std::size(a);\n    CHECK_EQ(size, std::size(b));\n    std::vector<Pair> pairs;\n    pairs.reserve(size);\n    for (size_t i = 0; i < size; ++i) {\n      if (!a[i].IsZero() && !b[i].infinity()) {\n        pairs.emplace_back(&a[i], &b[i].ell_coeffs());\n      }\n    }\n    return pairs;\n  }\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_PAIRING_PAIRING_FRIENDLY_CURVE_H_\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/pairing/pairing_unittest.cc",
    "content": "#include \"tachyon/math/elliptic_curves/pairing/pairing.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/elliptic_curves/bls12/bls12_381/bls12_381.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/bn254.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename Curve>\nclass PairingTest : public testing::Test {\n public:\n  static void SetUpTestSuite() { Curve::Init(); }\n};\n\nusing CurveTypes = testing::Types<bn254::BN254Curve, bls12_381::BLS12_381Curve>;\nTYPED_TEST_SUITE(PairingTest, CurveTypes);\n\nTYPED_TEST(PairingTest, Bilinearity) {\n  using Curve = TypeParam;\n  using G1Curve = typename Curve::G1Curve;\n  using G1AffinePoint = typename G1Curve::AffinePoint;\n  using G2Curve = typename Curve::G2Curve;\n  using G2AffinePoint = typename G2Curve::AffinePoint;\n  using ScalarField = typename G1Curve::ScalarField;\n  using Fp12 = typename Curve::Fp12;\n\n  G1AffinePoint g1 = G1AffinePoint::Random();\n  G2AffinePoint g2 = G2AffinePoint::Random();\n  ScalarField a = ScalarField::Random();\n  ScalarField b = ScalarField::Random();\n\n  Fp12 result;\n  {\n    G1AffinePoint g1s[] = {(a * b * g1).ToAffine()};\n    G2AffinePoint g2s[] = {g2};\n    result = Pairing<Curve>(g1s, g2s);\n  }\n\n  Fp12 result2;\n  {\n    G1AffinePoint g1s[] = {(a * g1).ToAffine()};\n    G2AffinePoint g2s[] = {(b * g2).ToAffine()};\n    result2 = Pairing<Curve>(g1s, g2s);\n  }\n\n  EXPECT_EQ(result, result2);\n\n  Fp12 result3;\n  {\n    G1AffinePoint g1s[] = {(b * g1).ToAffine()};\n    G2AffinePoint g2s[] = {(a * g2).ToAffine()};\n    result3 = Pairing<Curve>(g1s, g2s);\n  }\n\n  EXPECT_EQ(result, result3);\n\n  Fp12 result4;\n  {\n    G1AffinePoint g1s[] = {g1};\n    G2AffinePoint g2s[] = {(a * b * g2).ToAffine()};\n    result4 = Pairing<Curve>(g1s, g2s);\n  }\n\n  EXPECT_EQ(result, result4);\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/pairing/twist_type.h",
    "content": "#ifndef TACHYON_MATH_ELLIPTIC_CURVES_PAIRING_TWIST_TYPE_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_PAIRING_TWIST_TYPE_H_\n\n#include <string>\n\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/base/flag/flag_value_traits.h\"\n#include \"tachyon/base/logging.h\"\n\nnamespace tachyon {\nnamespace math {\n\nenum class TwistType {\n  kM,\n  kD,\n};\n\ninline const char* TwistTypeToString(TwistType type) {\n  switch (type) {\n    case TwistType::kM:\n      return \"M\";\n    case TwistType::kD:\n      return \"D\";\n  }\n  NOTREACHED();\n  return \"\";\n}\n\n}  // namespace math\n\nnamespace base {\n\ntemplate <>\nclass FlagValueTraits<math::TwistType> {\n public:\n  static bool ParseValue(std::string_view input, math::TwistType* value,\n                         std::string* reason) {\n    if (input == \"M\") {\n      *value = math::TwistType::kM;\n    } else if (input == \"D\") {\n      *value = math::TwistType::kD;\n    } else {\n      *reason = absl::Substitute(\"Unknown twist type: $0\", input);\n      return false;\n    }\n    return true;\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_PAIRING_TWIST_TYPE_H_\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/pasta/pallas/BUILD.bazel",
    "content": "load(\"@bazel_skylib//rules:common_settings.bzl\", \"string_flag\")\nload(\"//tachyon/math/elliptic_curves/short_weierstrass/generator:build_defs.bzl\", \"generate_ec_points\")\nload(\n    \"//tachyon/math/finite_fields/generator/prime_field_generator:build_defs.bzl\",\n    \"SUBGROUP_GENERATOR\",\n    \"generate_fft_prime_fields\",\n)\n\npackage(default_visibility = [\"//visibility:public\"])\n\nstring_flag(\n    name = \"fq_\" + SUBGROUP_GENERATOR,\n    build_setting_default = \"5\",\n)\n\n# Parameters are from https://o1-labs.github.io/proof-systems/specs/pasta.html#pallas\ngenerate_fft_prime_fields(\n    name = \"fq\",\n    class_name = \"Fq\",\n    # Hex: 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001\n    modulus = \"28948022309329048855892746252171976963363056481941560715954676764349967630337\",\n    namespace = \"tachyon::math::pallas\",\n    subgroup_generator = \":fq_\" + SUBGROUP_GENERATOR,\n)\n\nstring_flag(\n    name = \"fr_\" + SUBGROUP_GENERATOR,\n    build_setting_default = \"5\",\n)\n\ngenerate_fft_prime_fields(\n    name = \"fr\",\n    class_name = \"Fr\",\n    # Hex: 0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001\n    modulus = \"28948022309329048855892746252171976963363056481941647379679742748393362948097\",\n    namespace = \"tachyon::math::pallas\",\n    subgroup_generator = \":fr_\" + SUBGROUP_GENERATOR,\n)\n\ngenerate_ec_points(\n    name = \"curve\",\n    a = [\"0\"],\n    b = [\"5\"],\n    base_field = \"Fq\",\n    base_field_degree = 1,\n    base_field_dep = \":fq\",\n    base_field_hdr = \"tachyon/math/elliptic_curves/pasta/pallas/fq.h\",\n    gen_gpu = True,\n    namespace = \"tachyon::math::pallas\",\n    scalar_field = \"Fr\",\n    scalar_field_dep = \":fr\",\n    scalar_field_hdr = \"tachyon/math/elliptic_curves/pasta/pallas/fr.h\",\n    x = [\"-1\"],\n    y = [\"2\"],\n)\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/pasta/vesta/BUILD.bazel",
    "content": "load(\"@bazel_skylib//rules:common_settings.bzl\", \"string_flag\")\nload(\"//tachyon/math/elliptic_curves/short_weierstrass/generator:build_defs.bzl\", \"generate_ec_points\")\nload(\n    \"//tachyon/math/finite_fields/generator/prime_field_generator:build_defs.bzl\",\n    \"SUBGROUP_GENERATOR\",\n    \"generate_fft_prime_fields\",\n)\n\npackage(default_visibility = [\"//visibility:public\"])\n\nstring_flag(\n    name = \"fq_\" + SUBGROUP_GENERATOR,\n    build_setting_default = \"5\",\n)\n\n# Parameters are from https://o1-labs.github.io/proof-systems/specs/pasta.html#vesta\ngenerate_fft_prime_fields(\n    name = \"fq\",\n    class_name = \"Fq\",\n    # Hex: 0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001\n    modulus = \"28948022309329048855892746252171976963363056481941647379679742748393362948097\",\n    namespace = \"tachyon::math::vesta\",\n    subgroup_generator = \":fq_\" + SUBGROUP_GENERATOR,\n)\n\nstring_flag(\n    name = \"fr_\" + SUBGROUP_GENERATOR,\n    build_setting_default = \"5\",\n)\n\ngenerate_fft_prime_fields(\n    name = \"fr\",\n    class_name = \"Fr\",\n    # Hex: 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001\n    modulus = \"28948022309329048855892746252171976963363056481941560715954676764349967630337\",\n    namespace = \"tachyon::math::vesta\",\n    subgroup_generator = \":fr_\" + SUBGROUP_GENERATOR,\n)\n\ngenerate_ec_points(\n    name = \"curve\",\n    a = [\"0\"],\n    b = [\"5\"],\n    base_field = \"Fq\",\n    base_field_degree = 1,\n    base_field_dep = \":fq\",\n    base_field_hdr = \"tachyon/math/elliptic_curves/pasta/vesta/fq.h\",\n    gen_gpu = True,\n    namespace = \"tachyon::math::vesta\",\n    scalar_field = \"Fr\",\n    scalar_field_dep = \":fr\",\n    scalar_field_hdr = \"tachyon/math/elliptic_curves/pasta/vesta/fr.h\",\n    x = [\"-1\"],\n    y = [\"2\"],\n)\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/secp/secp256k1/BUILD.bazel",
    "content": "load(\"@bazel_skylib//rules:common_settings.bzl\", \"string_flag\")\nload(\"//tachyon/math/elliptic_curves/short_weierstrass/generator:build_defs.bzl\", \"generate_ec_points\")\nload(\n    \"//tachyon/math/finite_fields/generator/prime_field_generator:build_defs.bzl\",\n    \"SMALL_SUBGROUP_ADICITY\",\n    \"SMALL_SUBGROUP_BASE\",\n    \"SUBGROUP_GENERATOR\",\n    \"generate_large_fft_prime_fields\",\n)\n\npackage(default_visibility = [\"//visibility:public\"])\n\nstring_flag(\n    name = \"fq_\" + SMALL_SUBGROUP_ADICITY,\n    build_setting_default = \"1\",\n)\n\nstring_flag(\n    name = \"fq_\" + SMALL_SUBGROUP_BASE,\n    build_setting_default = \"3\",\n)\n\nstring_flag(\n    name = \"fq_\" + SUBGROUP_GENERATOR,\n    build_setting_default = \"3\",\n)\n\n# Parameters are from https://www.secg.org/sec2-v2.pdf#page=13 and https://github.com/arkworks-rs/curves/tree/master/secp256k1/src/fields\ngenerate_large_fft_prime_fields(\n    name = \"fq\",\n    class_name = \"Fq\",\n    # Hex: 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f\n    modulus = \"115792089237316195423570985008687907853269984665640564039457584007908834671663\",\n    namespace = \"tachyon::math::secp256k1\",\n    small_subgroup_adicity = \":fq_\" + SMALL_SUBGROUP_ADICITY,\n    small_subgroup_base = \":fq_\" + SMALL_SUBGROUP_BASE,\n    subgroup_generator = \":fq_\" + SUBGROUP_GENERATOR,\n)\n\nstring_flag(\n    name = \"fr_\" + SMALL_SUBGROUP_ADICITY,\n    build_setting_default = \"1\",\n)\n\nstring_flag(\n    name = \"fr_\" + SMALL_SUBGROUP_BASE,\n    build_setting_default = \"3\",\n)\n\nstring_flag(\n    name = \"fr_\" + SUBGROUP_GENERATOR,\n    build_setting_default = \"7\",\n)\n\ngenerate_large_fft_prime_fields(\n    name = \"fr\",\n    class_name = \"Fr\",\n    # Hex: 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141\n    modulus = \"115792089237316195423570985008687907852837564279074904382605163141518161494337\",\n    namespace = \"tachyon::math::secp256k1\",\n    small_subgroup_adicity = \":fr_\" + SMALL_SUBGROUP_ADICITY,\n    small_subgroup_base = \":fr_\" + SMALL_SUBGROUP_BASE,\n    subgroup_generator = \":fr_\" + SUBGROUP_GENERATOR,\n)\n\ngenerate_ec_points(\n    name = \"curve\",\n    a = [\"0\"],\n    b = [\"7\"],\n    base_field = \"Fq\",\n    base_field_degree = 1,\n    base_field_dep = \":fq\",\n    base_field_hdr = \"tachyon/math/elliptic_curves/secp/secp256k1/fq.h\",\n    gen_gpu = True,\n    namespace = \"tachyon::math::secp256k1\",\n    scalar_field = \"Fr\",\n    scalar_field_dep = \":fr\",\n    scalar_field_hdr = \"tachyon/math/elliptic_curves/secp/secp256k1/fr.h\",\n    # Hex: 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\n    x = [\"55066263022277343669578718895168534326250603453777594175500187360389116729240\"],\n    # Hex: 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8\n    y = [\"32670510020758816978083085130507043184471273380659243275938904335757337482424\"],\n)\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/semigroups.h",
    "content": "#ifndef TACHYON_MATH_ELLIPTIC_CURVES_SEMIGROUPS_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_SEMIGROUPS_H_\n\n#include \"tachyon/math/base/semigroups.h\"\n#include \"tachyon/math/geometry/affine_point.h\"\n#include \"tachyon/math/geometry/curve_type.h\"\n#include \"tachyon/math/geometry/jacobian_point.h\"\n\nnamespace tachyon::math::internal {\n\ntemplate <typename Curve>\nstruct AdditiveSemigroupTraits<\n    AffinePoint<Curve>,\n    std::enable_if_t<Curve::kType == CurveType::kShortWeierstrass>> {\n  using ReturnTy = JacobianPoint<Curve>;\n};\n\n}  // namespace tachyon::math::internal\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_SEMIGROUPS_H_\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/short_weierstrass/BUILD.bazel",
    "content": "load(\"//bazel:tachyon.bzl\", \"if_gpu_is_configured\")\nload(\n    \"//bazel:tachyon_cc.bzl\",\n    \"tachyon_cc_library\",\n    \"tachyon_cc_unittest\",\n    \"tachyon_cuda_test\",\n)\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"points\",\n    hdrs = [\n        \"affine_point.h\",\n        \"affine_point_impl.h\",\n        \"jacobian_point.h\",\n        \"jacobian_point_impl.h\",\n        \"point_xyzz.h\",\n        \"point_xyzz_impl.h\",\n        \"projective_point.h\",\n        \"projective_point_impl.h\",\n    ],\n    deps = [\n        \"//tachyon/base:parallelize\",\n        \"//tachyon/base/buffer:copyable\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/base/json\",\n        \"//tachyon/math/base:groups\",\n        \"//tachyon/math/elliptic_curves:semigroups\",\n        \"//tachyon/math/geometry:affine_point\",\n        \"//tachyon/math/geometry:curve_type\",\n        \"//tachyon/math/geometry:jacobian_point\",\n        \"//tachyon/math/geometry:point2\",\n        \"//tachyon/math/geometry:point3\",\n        \"//tachyon/math/geometry:point4\",\n        \"//tachyon/math/geometry:point_xyzz\",\n        \"//tachyon/math/geometry:projective_point\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"sw_curve\",\n    srcs = [\"sw_curve.h\"],\n    deps = [\n        \":sw_curve_traits_forward\",\n        \"//tachyon/math/geometry:affine_point\",\n        \"//tachyon/math/geometry:curve_type\",\n        \"//tachyon/math/geometry:jacobian_point\",\n        \"//tachyon/math/geometry:point_conversions\",\n        \"//tachyon/math/geometry:projective_point\",\n        \"@com_google_absl//absl/base\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"sw_curve_traits_forward\",\n    srcs = [\"sw_curve_traits_forward.h\"],\n)\n\ntachyon_cc_unittest(\n    name = \"short_weierstrass_unittests\",\n    srcs = [\n        \"affine_point_unittest.cc\",\n        \"jacobian_point_unittest.cc\",\n        \"point_xyzz_unittest.cc\",\n        \"projective_point_unittest.cc\",\n    ],\n    deps = [\n        \":points\",\n        \"//tachyon/base/buffer:vector_buffer\",\n        \"//tachyon/base/json\",\n        \"//tachyon/math/elliptic_curves/short_weierstrass/test:sw_curve_config\",\n        \"//tachyon/math/elliptic_curves/test:random\",\n    ],\n)\n\ntachyon_cuda_test(\n    name = \"short_weierstrass_correctness_gpu_tests\",\n    size = \"small\",\n    srcs = if_gpu_is_configured([\n        \"affine_point_correctness_gpu_test.cc\",\n        \"non_affine_point_correctness_gpu_test.cc\",\n    ]),\n    deps = [\n        \"//tachyon/device/gpu:gpu_memory\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:g1_gpu\",\n        \"//tachyon/math/elliptic_curves/short_weierstrass/kernels:elliptic_curve_ops\",\n        \"//tachyon/math/test:launch_op_macros\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/short_weierstrass/affine_point.h",
    "content": "#ifndef TACHYON_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_AFFINE_POINT_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_AFFINE_POINT_H_\n\n#include <optional>\n#include <string>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/json/json.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/parallelize.h\"\n#include \"tachyon/math/base/groups.h\"\n#include \"tachyon/math/elliptic_curves/semigroups.h\"\n#include \"tachyon/math/geometry/affine_point.h\"\n#include \"tachyon/math/geometry/curve_type.h\"\n#include \"tachyon/math/geometry/jacobian_point.h\"\n#include \"tachyon/math/geometry/point2.h\"\n#include \"tachyon/math/geometry/point_xyzz.h\"\n#include \"tachyon/math/geometry/projective_point.h\"\n\nnamespace tachyon {\nnamespace math {\n\ntemplate <typename _Curve>\nclass AffinePoint<\n    _Curve, std::enable_if_t<_Curve::kType == CurveType::kShortWeierstrass>>\n    final : public AdditiveGroup<AffinePoint<_Curve>> {\n public:\n  constexpr static bool kNegationIsCheap = true;\n\n  using Curve = _Curve;\n  using BaseField = typename Curve::BaseField;\n  using ScalarField = typename Curve::ScalarField;\n\n  constexpr AffinePoint() : AffinePoint(BaseField::Zero(), BaseField::Zero()) {}\n  explicit constexpr AffinePoint(const Point2<BaseField>& point)\n      : AffinePoint(point.x, point.y) {}\n  explicit constexpr AffinePoint(Point2<BaseField>&& point)\n      : AffinePoint(std::move(point.x), std::move(point.y)) {}\n  constexpr AffinePoint(const BaseField& x, const BaseField& y)\n      : x_(x), y_(y) {}\n  constexpr AffinePoint(BaseField&& x, BaseField&& y)\n      : x_(std::move(x)), y_(std::move(y)) {}\n\n  constexpr static AffinePoint CreateChecked(const BaseField& x,\n                                             const BaseField& y) {\n    AffinePoint ret = {x, y};\n    CHECK(ret.IsOnCurve());\n    return ret;\n  }\n\n  constexpr static AffinePoint CreateChecked(BaseField&& x, BaseField&& y) {\n    AffinePoint ret = {std::move(x), std::move(y)};\n    CHECK(ret.IsOnCurve());\n    return ret;\n  }\n\n  constexpr static std::optional<AffinePoint> CreateFromX(const BaseField& x,\n                                                          bool pick_odd) {\n    AffinePoint point{};\n    if (!Curve::GetPointFromX(x, pick_odd, &point)) return std::nullopt;\n    return point;\n  }\n\n  constexpr static AffinePoint Zero() { return AffinePoint(); }\n\n  constexpr static AffinePoint One() { return Generator(); }\n\n  constexpr static AffinePoint Generator() {\n    return {Curve::Config::kGenerator.x, Curve::Config::kGenerator.y};\n  }\n\n  constexpr static AffinePoint FromProjective(\n      const ProjectivePoint<Curve>& point) {\n    return point.ToAffine();\n  }\n\n  constexpr static AffinePoint FromJacobian(const JacobianPoint<Curve>& point) {\n    return point.ToAffine();\n  }\n\n  constexpr static AffinePoint FromXYZZ(const PointXYZZ<Curve>& point) {\n    return point.ToAffine();\n  }\n\n  constexpr static AffinePoint Random() {\n    return FromJacobian(JacobianPoint<Curve>::Random());\n  }\n\n  constexpr static AffinePoint Endomorphism(const AffinePoint& point) {\n    return AffinePoint(point.x_ * Curve::Config::kEndomorphismCoefficient,\n                       point.y_);\n  }\n\n  template <typename ScalarFieldContainer, typename AffineContainer>\n  [[nodiscard]] constexpr static bool BatchMapScalarFieldToPoint(\n      const AffinePoint& point, const ScalarFieldContainer& scalar_fields,\n      AffineContainer* affine_points) {\n    return DoBatchMapScalarFieldToPoint(point, scalar_fields, affine_points);\n  }\n\n  constexpr const BaseField& x() const { return x_; }\n  constexpr const BaseField& y() const { return y_; }\n\n  constexpr bool operator==(const AffinePoint& other) const {\n    if (IsZero()) {\n      return other.IsZero();\n    }\n\n    if (other.IsZero()) {\n      return false;\n    }\n\n    return x_ == other.x_ && y_ == other.y_;\n  }\n\n  constexpr bool operator!=(const AffinePoint& other) const {\n    return !operator==(other);\n  }\n\n  constexpr bool IsZero() const { return x_.IsZero() && y_.IsZero(); }\n\n  constexpr bool IsOnCurve() { return Curve::IsOnCurve(*this); }\n\n  constexpr ProjectivePoint<Curve> ToProjective() const {\n    if (IsZero()) return ProjectivePoint<Curve>::Zero();\n    return {x_, y_, BaseField::One()};\n  }\n\n  constexpr JacobianPoint<Curve> ToJacobian() const {\n    if (IsZero()) return JacobianPoint<Curve>::Zero();\n    return {x_, y_, BaseField::One()};\n  }\n\n  constexpr PointXYZZ<Curve> ToXYZZ() const {\n    if (IsZero()) return PointXYZZ<Curve>::Zero();\n    return {x_, y_, BaseField::One(), BaseField::One()};\n  }\n\n  std::string ToString() const {\n    return absl::Substitute(\"($0, $1)\", x_.ToString(), y_.ToString());\n  }\n\n  std::string ToHexString(bool pad_zero = false) const {\n    return absl::Substitute(\"($0, $1)\", x_.ToHexString(pad_zero),\n                            y_.ToHexString(pad_zero));\n  }\n\n  // AdditiveSemigroup methods\n  constexpr JacobianPoint<Curve> Add(const AffinePoint& other) const {\n    return ToJacobian() + other.ToJacobian();\n  }\n  constexpr ProjectivePoint<Curve> Add(\n      const ProjectivePoint<Curve>& other) const {\n    return ToProjective() + other;\n  }\n  constexpr JacobianPoint<Curve> Add(const JacobianPoint<Curve>& other) const {\n    return ToJacobian() + other;\n  }\n  constexpr PointXYZZ<Curve> Add(const PointXYZZ<Curve>& other) const {\n    return ToXYZZ() + other;\n  }\n\n  constexpr AffinePoint Negate() const { return {x_, -y_}; }\n\n  constexpr AffinePoint& NegateInPlace() {\n    y_.NegateInPlace();\n    return *this;\n  }\n\n  constexpr ProjectivePoint<Curve> DoubleProjective() const;\n  constexpr PointXYZZ<Curve> DoubleXYZZ() const;\n\n  constexpr JacobianPoint<Curve> operator*(const ScalarField& v) const {\n    return this->ScalarMul(v);\n  }\n\n private:\n  template <typename ScalarFieldContainer, typename AffineContainer>\n  [[nodiscard]] constexpr static bool DoBatchMapScalarFieldToPoint(\n      const AffinePoint& point, const ScalarFieldContainer& scalar_fields,\n      AffineContainer* affine_points) {\n    size_t size = std::size(scalar_fields);\n    if (size != std::size(*affine_points)) {\n      LOG(ERROR) << \"Size of |scalar_fields| and |affine_points| do not match\";\n      return false;\n    }\n    std::vector<JacobianPoint<Curve>> jacobian_points(size);\n    base::Parallelize(\n        jacobian_points, [&point, &scalar_fields, affine_points](\n                             absl::Span<JacobianPoint<Curve>> chunk,\n                             size_t chunk_idx, size_t chunk_size) {\n          size_t start = chunk_idx * chunk_size;\n          for (size_t i = 0; i < chunk.size(); ++i) {\n            chunk[i] = scalar_fields[start + i] * point;\n          }\n          absl::Span<AffinePoint> sub_affine =\n              absl::MakeSpan(*affine_points).subspan(start, chunk.size());\n          CHECK(JacobianPoint<Curve>::BatchNormalizeSerial(chunk, &sub_affine));\n        });\n    return true;\n  }\n\n  BaseField x_;\n  BaseField y_;\n};\n\n}  // namespace math\n\nnamespace base {\n\ntemplate <typename Curve>\nclass Copyable<math::AffinePoint<\n    Curve,\n    std::enable_if_t<Curve::kType == math::CurveType::kShortWeierstrass>>> {\n public:\n  static bool WriteTo(const math::AffinePoint<Curve>& point, Buffer* buffer) {\n    return buffer->WriteMany(point.x(), point.y());\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer,\n                       math::AffinePoint<Curve>* point) {\n    using BaseField = typename math::AffinePoint<Curve>::BaseField;\n    BaseField x, y;\n    if (!buffer.ReadMany(&x, &y)) return false;\n\n    *point = math::AffinePoint<Curve>(std::move(x), std::move(y));\n    return true;\n  }\n\n  static size_t EstimateSize(const math::AffinePoint<Curve>& point) {\n    return base::EstimateSize(point.x(), point.y());\n  }\n};\n\ntemplate <typename Curve>\nclass RapidJsonValueConverter<math::AffinePoint<\n    Curve,\n    std::enable_if_t<Curve::kType == math::CurveType::kShortWeierstrass>>> {\n public:\n  using Field = typename math::AffinePoint<Curve>::BaseField;\n\n  template <typename Allocator>\n  static rapidjson::Value From(const math::AffinePoint<Curve>& value,\n                               Allocator& allocator) {\n    rapidjson::Value object(rapidjson::kObjectType);\n    AddJsonElement(object, \"x\", value.x(), allocator);\n    AddJsonElement(object, \"y\", value.y(), allocator);\n    return object;\n  }\n\n  static bool To(const rapidjson::Value& json_value, std::string_view key,\n                 math::AffinePoint<Curve>* value, std::string* error) {\n    Field x;\n    Field y;\n    if (!ParseJsonElement(json_value, \"x\", &x, error)) return false;\n    if (!ParseJsonElement(json_value, \"y\", &y, error)) return false;\n    *value = math::AffinePoint<Curve>(std::move(x), std::move(y));\n    return true;\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#include \"tachyon/math/elliptic_curves/short_weierstrass/affine_point_impl.h\"\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_AFFINE_POINT_H_\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/short_weierstrass/affine_point_correctness_gpu_test.cc",
    "content": "#include \"absl/strings/substitute.h\"\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/device/gpu/gpu_memory.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/g1_gpu.h\"\n#include \"tachyon/math/elliptic_curves/short_weierstrass/kernels/elliptic_curve_ops.cu.h\"\n#include \"tachyon/math/geometry/point_conversions.h\"\n#include \"tachyon/math/test/launch_op_macros.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\nconstexpr size_t kThreadNum = 32;\n\n#define DEFINE_LAUNCH_FIELD_BINARY_OP(method)                          \\\n  DEFINE_LAUNCH_BINARY_OP(kThreadNum, method, bn254::G1AffinePointGpu, \\\n                          bn254::G1JacobianPointGpu)\n\nDEFINE_LAUNCH_FIELD_BINARY_OP(Add)\n\nDEFINE_LAUNCH_UNARY_OP(kThreadNum, Double, bn254::G1AffinePointGpu,\n                       bn254::G1JacobianPointGpu)\nDEFINE_LAUNCH_UNARY_OP(kThreadNum, Negate, bn254::G1AffinePointGpu,\n                       bn254::G1AffinePointGpu)\n\n#undef DEFINE_LAUNCH_FIELD_BINARY_OP\n\n#define DEFINE_LAUNCH_COMPARISON_OP(method) \\\n  DEFINE_LAUNCH_BINARY_OP(kThreadNum, method, bn254::G1AffinePointGpu, bool)\n\nDEFINE_LAUNCH_COMPARISON_OP(Eq)\nDEFINE_LAUNCH_COMPARISON_OP(Ne)\n\n#undef DEFINE_LAUNCH_COMPARISON_OP\n\nusing namespace device;\n\nclass AffinePointCorrectnessGpuTest : public testing::Test {\n public:\n  // Runs tests with |N| data.\n  constexpr static size_t N = kThreadNum * 2;\n\n  static void SetUpTestSuite() {\n    GPU_MUST_SUCCEED(gpuDeviceReset(), \"\");\n    xs_ = gpu::GpuMemory<bn254::G1AffinePointGpu>::MallocManaged(N);\n    ys_ = gpu::GpuMemory<bn254::G1AffinePointGpu>::MallocManaged(N);\n    affine_results_ = gpu::GpuMemory<bn254::G1AffinePointGpu>::MallocManaged(N);\n    jacobian_results_ =\n        gpu::GpuMemory<bn254::G1JacobianPointGpu>::MallocManaged(N);\n    bool_results_ = gpu::GpuMemory<bool>::MallocManaged(N);\n\n    bn254::G1Curve::Init();\n    bn254::G1CurveGpu::Init();\n\n    x_cpus_.reserve(N);\n    y_cpus_.reserve(N);\n\n    for (size_t i = 0; i < N; ++i) {\n      bn254::G1AffinePoint x_cpu = bn254::G1AffinePoint::Random();\n      bn254::G1AffinePoint y_cpu = bn254::G1AffinePoint::Random();\n\n      xs_[i] = ConvertPoint<bn254::G1AffinePointGpu>(x_cpu);\n      ys_[i] = ConvertPoint<bn254::G1AffinePointGpu>(y_cpu);\n\n      x_cpus_.push_back(std::move(x_cpu));\n      y_cpus_.push_back(std::move(y_cpu));\n    }\n  }\n\n  static void TearDownTestSuite() {\n    xs_.reset();\n    ys_.reset();\n    affine_results_.reset();\n    jacobian_results_.reset();\n    bool_results_.reset();\n\n    GPU_MUST_SUCCEED(gpuDeviceReset(), \"\");\n\n    x_cpus_.clear();\n    y_cpus_.clear();\n  }\n\n  void SetUp() override {\n    affine_results_.Memset();\n    jacobian_results_.Memset();\n    bool_results_.Memset();\n  }\n\n protected:\n  static gpu::GpuMemory<bn254::G1AffinePointGpu> xs_;\n  static gpu::GpuMemory<bn254::G1AffinePointGpu> ys_;\n  static gpu::GpuMemory<bn254::G1AffinePointGpu> affine_results_;\n  static gpu::GpuMemory<bn254::G1JacobianPointGpu> jacobian_results_;\n  static gpu::GpuMemory<bool> bool_results_;\n\n  static std::vector<bn254::G1AffinePoint> x_cpus_;\n  static std::vector<bn254::G1AffinePoint> y_cpus_;\n};\n\ngpu::GpuMemory<bn254::G1AffinePointGpu> AffinePointCorrectnessGpuTest::xs_;\ngpu::GpuMemory<bn254::G1AffinePointGpu> AffinePointCorrectnessGpuTest::ys_;\ngpu::GpuMemory<bn254::G1AffinePointGpu>\n    AffinePointCorrectnessGpuTest::affine_results_;\ngpu::GpuMemory<bn254::G1JacobianPointGpu>\n    AffinePointCorrectnessGpuTest::jacobian_results_;\ngpu::GpuMemory<bool> AffinePointCorrectnessGpuTest::bool_results_;\n\nstd::vector<bn254::G1AffinePoint> AffinePointCorrectnessGpuTest::x_cpus_;\nstd::vector<bn254::G1AffinePoint> AffinePointCorrectnessGpuTest::y_cpus_;\n\n}  // namespace\n\nTEST_F(AffinePointCorrectnessGpuTest, Add) {\n  GPU_MUST_SUCCEED(\n      LaunchAdd(xs_.data(), ys_.data(), jacobian_results_.data(), N), \"\");\n  for (size_t i = 0; i < N; ++i) {\n    SCOPED_TRACE(\n        absl::Substitute(\"a: $0, b: $1\", xs_[i].ToString(), ys_[i].ToString()));\n    auto result = ConvertPoint<bn254::G1JacobianPoint>(jacobian_results_[i]);\n    ASSERT_EQ(result, x_cpus_[i] + y_cpus_[i]);\n  }\n}\n\nTEST_F(AffinePointCorrectnessGpuTest, Double) {\n  GPU_MUST_SUCCEED(LaunchDouble(xs_.data(), jacobian_results_.data(), N), \"\");\n  for (size_t i = 0; i < N; ++i) {\n    SCOPED_TRACE(absl::Substitute(\"a: $0\", xs_[i].ToString()));\n    auto result = ConvertPoint<bn254::G1JacobianPoint>(jacobian_results_[i]);\n    ASSERT_EQ(result, x_cpus_[i].Double());\n  }\n}\n\nTEST_F(AffinePointCorrectnessGpuTest, Negate) {\n  GPU_MUST_SUCCEED(LaunchNegate(xs_.data(), affine_results_.data(), N), \"\");\n  for (size_t i = 0; i < N; ++i) {\n    SCOPED_TRACE(absl::Substitute(\"a: $0\", xs_[i].ToString()));\n    auto result = ConvertPoint<bn254::G1AffinePoint>(affine_results_[i]);\n    ASSERT_EQ(result, -x_cpus_[i]);\n  }\n}\n\nTEST_F(AffinePointCorrectnessGpuTest, Eq) {\n  GPU_MUST_SUCCEED(LaunchEq(xs_.data(), xs_.data(), bool_results_.data(), N),\n                   \"\");\n  for (size_t i = 0; i < N; ++i) {\n    SCOPED_TRACE(\n        absl::Substitute(\"a: $0, b: $1\", xs_[i].ToString(), xs_[i].ToString()));\n    ASSERT_TRUE(bool_results_[i]);\n  }\n}\n\nTEST_F(AffinePointCorrectnessGpuTest, Ne) {\n  GPU_MUST_SUCCEED(LaunchNe(xs_.data(), ys_.data(), bool_results_.data(), N),\n                   \"\");\n  for (size_t i = 0; i < N; ++i) {\n    SCOPED_TRACE(\n        absl::Substitute(\"a: $0, b: $1\", xs_[i].ToString(), ys_[i].ToString()));\n    ASSERT_TRUE(bool_results_[i]);\n  }\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/short_weierstrass/affine_point_impl.h",
    "content": "#ifndef TACHYON_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_AFFINE_POINT_IMPL_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_AFFINE_POINT_IMPL_H_\n\n#include <utility>\n\n#include \"tachyon/math/elliptic_curves/short_weierstrass/affine_point.h\"\n\nnamespace tachyon::math {\n\n#define CLASS        \\\n  AffinePoint<Curve, \\\n              std::enable_if_t<Curve::kType == CurveType::kShortWeierstrass>>\n\ntemplate <typename Curve>\nconstexpr ProjectivePoint<Curve> CLASS::DoubleProjective() const {\n  if (IsZero()) {\n    return ProjectivePoint<Curve>::Zero();\n  }\n\n  // https://hyperelliptic.org/EFD/g1p/auto-shortw-projective.html#doubling-mdbl-2007-bl\n  // XX = X1²\n  BaseField xx = x_.Square();\n\n  // w = a + 3 * XX\n  BaseField w = xx;\n  w += w.Double();\n  if constexpr (!Curve::Config::kAIsZero) {\n    // TODO(chokobole): Implement constexpr version of Curve::Config::AddByA()\n    // for GPU.\n    w += Curve::Config::kA;\n  }\n\n  // R = 2 * Y1²\n  BaseField r = y_.Square();\n  r.DoubleInPlace();\n\n  // sss = 4 * Y1 * R\n  BaseField sss = y_ * r;\n  sss.DoubleInPlace().DoubleInPlace();\n\n  // RR = R²\n  BaseField rr = r.Square();\n\n  // B = (X1 + R)² - XX - RR\n  BaseField b = x_ + r;\n  b.SquareInPlace();\n  b -= xx;\n  b -= rr;\n\n  // h = w² - 2 * B\n  BaseField h = w.Square();\n  h -= b.Double();\n\n  // X3 = 2 * h * Y1\n  BaseField x = h * y_;\n  x.DoubleInPlace();\n\n  // Y3 = w * (B - h) - 2 * RR\n  BaseField y = b - h;\n  y *= w;\n  y -= rr.Double();\n\n  // Z3 = sss\n  BaseField z = std::move(sss);\n\n  return {std::move(x), std::move(y), std::move(z)};\n}\n\ntemplate <typename Curve>\nconstexpr PointXYZZ<Curve> CLASS::DoubleXYZZ() const {\n  if (IsZero()) {\n    return PointXYZZ<Curve>::Zero();\n  }\n\n  // https://hyperelliptic.org/EFD/g1p/auto-shortw-xyzz.html#doubling-mdbl-2008-s-1\n  // U = 2 * Y1\n  BaseField u = y_.Double();\n\n  // V = U²\n  BaseField v = u.Square();\n\n  // W = U * V\n  BaseField w = u * v;\n\n  // S = X1 * V\n  BaseField s = x_ * v;\n\n  // M = 3 * X1² + a\n  BaseField m = x_.Square();\n  m += m.Double();\n  if constexpr (!Curve::Config::kAIsZero) {\n    // TODO(chokobole): Implement constexpr version of Curve::Config::AddByA()\n    // for GPU.\n    m += Curve::Config::kA;\n  }\n\n  // X3 = M² - 2 * S\n  BaseField x = m.Square();\n  x -= s.Double();\n\n  // Y3 = M * (S - X3) - W * Y1\n  BaseField lefts[] = {std::move(m), -w};\n  BaseField rights[] = {s - x, y_};\n  BaseField y = BaseField::SumOfProductsSerial(lefts, rights);\n\n  // ZZ3 = V\n  BaseField zz = std::move(v);\n\n  // ZZZ3 = W\n  BaseField zzz = std::move(w);\n\n  return {std::move(x), std::move(y), std::move(zz), std::move(zzz)};\n}\n\n#undef CLASS\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_AFFINE_POINT_IMPL_H_\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/short_weierstrass/affine_point_unittest.cc",
    "content": "#include \"tachyon/math/elliptic_curves/short_weierstrass/affine_point.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/buffer/vector_buffer.h\"\n#include \"tachyon/math/elliptic_curves/short_weierstrass/jacobian_point.h\"\n#include \"tachyon/math/elliptic_curves/short_weierstrass/point_xyzz.h\"\n#include \"tachyon/math/elliptic_curves/short_weierstrass/projective_point.h\"\n#include \"tachyon/math/elliptic_curves/short_weierstrass/test/sw_curve_config.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\nclass AffinePointTest : public testing::Test {\n public:\n  static void SetUpTestSuite() { test::G1Curve::Init(); }\n};\n\n}  // namespace\n\nTEST_F(AffinePointTest, Zero) {\n  EXPECT_TRUE(test::AffinePoint::Zero().IsZero());\n  EXPECT_FALSE(test::AffinePoint(GF7(1), GF7(2)).IsZero());\n}\n\nTEST_F(AffinePointTest, Generator) {\n  EXPECT_EQ(test::AffinePoint::Generator(),\n            test::AffinePoint(test::G1Curve::Config::kGenerator.x,\n                              test::G1Curve::Config::kGenerator.y));\n}\n\nTEST_F(AffinePointTest, Random) {\n  bool success = false;\n  test::AffinePoint r = test::AffinePoint::Random();\n  for (size_t i = 0; i < 100; ++i) {\n    if (r != test::AffinePoint::Random()) {\n      success = true;\n      break;\n    }\n  }\n  EXPECT_TRUE(success);\n}\n\nTEST_F(AffinePointTest, EqualityOperators) {\n  test::AffinePoint p(GF7(1), GF7(2));\n  test::AffinePoint p2(GF7(3), GF7(4));\n  EXPECT_EQ(p, p);\n  EXPECT_NE(p, p2);\n}\n\nTEST_F(AffinePointTest, AdditiveGroupOperators) {\n  test::AffinePoint ap = test::AffinePoint::CreateChecked(GF7(5), GF7(5));\n  test::AffinePoint ap2 = test::AffinePoint::CreateChecked(GF7(3), GF7(2));\n  test::AffinePoint ap3 = test::AffinePoint::CreateChecked(GF7(3), GF7(5));\n  test::AffinePoint ap4 = test::AffinePoint::CreateChecked(GF7(6), GF7(5));\n  test::JacobianPoint jp = ap.ToJacobian();\n  test::JacobianPoint jp2 = ap2.ToJacobian();\n  test::JacobianPoint jp3 = ap3.ToJacobian();\n  test::JacobianPoint jp4 = ap4.ToJacobian();\n\n  EXPECT_EQ(ap + ap2, jp3);\n  EXPECT_EQ(ap + ap, jp4);\n  EXPECT_EQ(ap3 - ap2, jp);\n  EXPECT_EQ(ap4 - ap, jp);\n\n  EXPECT_EQ(ap + jp2, jp3);\n  EXPECT_EQ(ap + jp, jp4);\n  EXPECT_EQ(ap - jp3, -jp2);\n  EXPECT_EQ(ap - jp4, -jp);\n\n  EXPECT_EQ(ap.Double(), jp4);\n  EXPECT_EQ(ap.DoubleProjective(), ap4.ToProjective());\n  EXPECT_EQ(ap.DoubleXYZZ(), ap4.ToXYZZ());\n\n  EXPECT_EQ(-ap, test::AffinePoint(GF7(5), GF7(2)));\n  {\n    test::AffinePoint ap_tmp = ap;\n    ap_tmp.NegateInPlace();\n    EXPECT_EQ(ap_tmp, test::AffinePoint(GF7(5), GF7(2)));\n  }\n\n  EXPECT_EQ(ap * GF7(2), jp4);\n  EXPECT_EQ(GF7(2) * ap, jp4);\n}\n\nTEST_F(AffinePointTest, ToProjective) {\n  EXPECT_EQ(test::AffinePoint::Zero().ToProjective(),\n            test::ProjectivePoint::Zero());\n  test::AffinePoint p(GF7(3), GF7(2));\n  EXPECT_EQ(p.ToProjective(), test::ProjectivePoint(GF7(3), GF7(2), GF7(1)));\n}\n\nTEST_F(AffinePointTest, ToJacobian) {\n  EXPECT_EQ(test::AffinePoint::Zero().ToJacobian(),\n            test::JacobianPoint::Zero());\n  test::AffinePoint p(GF7(3), GF7(2));\n  EXPECT_EQ(p.ToJacobian(), test::JacobianPoint(GF7(3), GF7(2), GF7(1)));\n}\n\nTEST_F(AffinePointTest, ToPointXYZZ) {\n  EXPECT_EQ(test::AffinePoint::Zero().ToXYZZ(), test::PointXYZZ::Zero());\n  test::AffinePoint p(GF7(3), GF7(2));\n  EXPECT_EQ(p.ToXYZZ(), test::PointXYZZ(GF7(3), GF7(2), GF7(1), GF7(1)));\n}\n\nTEST_F(AffinePointTest, BatchMapScalarFieldToPoint) {\n#if defined(TACHYON_HAS_OPENMP)\n  size_t size = size_t{1} << (static_cast<size_t>(omp_get_max_threads()) /\n                              GF7::kParallelBatchInverseDivisorThreshold);\n#else\n  size_t size = 7;\n#endif  // defined(TACHYON_HAS_OPENMP)\n  std::vector<GF7> scalar_fields =\n      base::CreateVector(size, [](int i) { return GF7(i % 7); });\n  test::AffinePoint point = test::AffinePoint::Generator();\n\n  std::vector<test::AffinePoint> affine_points;\n  affine_points.resize(scalar_fields.size() - 1);\n  ASSERT_FALSE(test::AffinePoint::BatchMapScalarFieldToPoint(\n      point, scalar_fields, &affine_points));\n\n  affine_points.resize(scalar_fields.size());\n  ASSERT_TRUE(test::AffinePoint::BatchMapScalarFieldToPoint(\n      point, scalar_fields, &affine_points));\n\n  std::vector<test::AffinePoint> expected_affine_points =\n      base::Map(scalar_fields, [&point](const GF7& scalar_field) {\n        return (scalar_field * point).ToAffine();\n      });\n  EXPECT_EQ(affine_points, expected_affine_points);\n}\n\nTEST_F(AffinePointTest, IsOnCurve) {\n  test::AffinePoint invalid_point(GF7(1), GF7(2));\n  EXPECT_FALSE(invalid_point.IsOnCurve());\n  test::AffinePoint valid_point(GF7(3), GF7(2));\n  EXPECT_TRUE(valid_point.IsOnCurve());\n  valid_point = test::AffinePoint(GF7(3), GF7(5));\n  EXPECT_TRUE(valid_point.IsOnCurve());\n}\n\nTEST_F(AffinePointTest, CreateFromX) {\n  {\n    std::optional<test::AffinePoint> p =\n        test::AffinePoint::CreateFromX(GF7(3), /*pick_odd=*/true);\n    ASSERT_TRUE(p.has_value());\n    EXPECT_EQ(p->y(), GF7(5));\n  }\n  {\n    std::optional<test::AffinePoint> p =\n        test::AffinePoint::CreateFromX(GF7(3), /*pick_odd=*/false);\n    ASSERT_TRUE(p.has_value());\n    EXPECT_EQ(p->y(), GF7(2));\n  }\n  {\n    std::optional<test::AffinePoint> p =\n        test::AffinePoint::CreateFromX(GF7(1), /*pick_odd=*/false);\n    ASSERT_FALSE(p.has_value());\n  }\n}\n\nTEST_F(AffinePointTest, Copyable) {\n  test::AffinePoint expected = test::AffinePoint::Random();\n\n  base::Uint8VectorBuffer write_buf;\n  ASSERT_TRUE(write_buf.Grow(base::EstimateSize(expected)));\n  ASSERT_TRUE(write_buf.Write(expected));\n  ASSERT_TRUE(write_buf.Done());\n\n  write_buf.set_buffer_offset(0);\n\n  test::AffinePoint value;\n  ASSERT_TRUE(write_buf.Read(&value));\n  EXPECT_EQ(expected, value);\n}\n\nTEST_F(AffinePointTest, JsonValueConverter) {\n  test::AffinePoint expected_point(GF7(1), GF7(2));\n  std::string expected_json = R\"({\"x\":1,\"y\":2})\";\n\n  test::AffinePoint p;\n  std::string error;\n  ASSERT_TRUE(base::ParseJson(expected_json, &p, &error));\n  ASSERT_TRUE(error.empty());\n  EXPECT_EQ(p, expected_point);\n\n  std::string json = base::WriteToJson(p);\n  EXPECT_EQ(json, expected_json);\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/short_weierstrass/generator/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_binary\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_binary(\n    name = \"generator\",\n    srcs = [\"generator.cc\"],\n    data = [\n        \"cpu.h.tpl\",\n        \"gpu.h.tpl\",\n    ],\n    deps = [\n        \"//tachyon/base/console\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/base/files:file_path_flag\",\n        \"//tachyon/base/flag:flag_parser\",\n        \"//tachyon/base/strings:string_number_conversions\",\n        \"//tachyon/build:cc_writer\",\n        \"//tachyon/math/base/gmp:bit_traits\",\n        \"//tachyon/math/finite_fields/generator:generator_util\",\n        \"@com_google_absl//absl/container:flat_hash_map\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/short_weierstrass/generator/build_defs.bzl",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\ndef _generate_ec_point_impl(ctx):\n    cpu_hdr_tpl_path = ctx.expand_location(\"$(location @kroma_network_tachyon//tachyon/math/elliptic_curves/short_weierstrass/generator:cpu.h.tpl)\", [ctx.attr.cpu_hdr_tpl_path])\n    gpu_hdr_tpl_path = ctx.expand_location(\"$(location @kroma_network_tachyon//tachyon/math/elliptic_curves/short_weierstrass/generator:gpu.h.tpl)\", [ctx.attr.gpu_hdr_tpl_path])\n\n    arguments = [\n        \"--out=%s\" % (ctx.outputs.out.path),\n        \"--namespace=%s\" % (ctx.attr.namespace),\n        \"--base_field=%s\" % (ctx.attr.base_field),\n        \"--base_field_degree=%s\" % (ctx.attr.base_field_degree),\n        \"--base_field_hdr=%s\" % (ctx.attr.base_field_hdr),\n        \"--scalar_field=%s\" % (ctx.attr.scalar_field),\n        \"--scalar_field_hdr=%s\" % (ctx.attr.scalar_field_hdr),\n        \"--cpu_hdr_tpl_path=%s\" % (cpu_hdr_tpl_path),\n        \"--gpu_hdr_tpl_path=%s\" % (gpu_hdr_tpl_path),\n    ]\n    if len(ctx.attr.class_name) > 0:\n        arguments.append(\"--class=%s\" % (ctx.attr.class_name))\n\n    for a in ctx.attr.a:\n        arguments.append(\"-a=%s\" % (a))\n\n    for b in ctx.attr.b:\n        arguments.append(\"-b=%s\" % (b))\n\n    for x in ctx.attr.x:\n        arguments.append(\"-x=%s\" % (x))\n\n    for y in ctx.attr.y:\n        arguments.append(\"-y=%s\" % (y))\n\n    if len(ctx.attr.mul_by_a_override) > 0:\n        arguments.append(\"--mul_by_a_override=%s\" % (ctx.attr.mul_by_a_override))\n\n    if len(ctx.attr.glv_coeffs) > 0:\n        for endomorphism_coefficient in ctx.attr.endomorphism_coefficient:\n            arguments.append(\"--endomorphism_coefficient=%s\" % (endomorphism_coefficient))\n\n        arguments.append(\"--lambda=%s\" % (ctx.attr.lambda_))\n\n        for glv_coeff in ctx.attr.glv_coeffs:\n            arguments.append(\"--glv_coefficients=%s\" % glv_coeff)\n\n    ctx.actions.run(\n        inputs = [ctx.files.cpu_hdr_tpl_path[0], ctx.files.gpu_hdr_tpl_path[0]],\n        tools = [ctx.executable._tool],\n        executable = ctx.executable._tool,\n        outputs = [ctx.outputs.out],\n        arguments = arguments,\n    )\n\n    return [DefaultInfo(files = depset([ctx.outputs.out]))]\n\ngenerate_ec_point = rule(\n    implementation = _generate_ec_point_impl,\n    attrs = {\n        \"out\": attr.output(mandatory = True),\n        \"namespace\": attr.string(mandatory = True),\n        \"class_name\": attr.string(),\n        \"base_field_degree\": attr.int(mandatory = True),\n        \"base_field\": attr.string(mandatory = True),\n        \"base_field_hdr\": attr.string(mandatory = True),\n        \"scalar_field\": attr.string(mandatory = True),\n        \"scalar_field_hdr\": attr.string(mandatory = True),\n        \"a\": attr.string_list(mandatory = True),\n        \"b\": attr.string_list(mandatory = True),\n        \"x\": attr.string_list(mandatory = True),\n        \"y\": attr.string_list(mandatory = True),\n        \"mul_by_a_override\": attr.string(),\n        \"endomorphism_coefficient\": attr.string_list(),\n        \"lambda_\": attr.string(),\n        \"glv_coeffs\": attr.string_list(),\n        \"cpu_hdr_tpl_path\": attr.label(\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/math/elliptic_curves/short_weierstrass/generator:cpu.h.tpl\"),\n        ),\n        \"gpu_hdr_tpl_path\": attr.label(\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/math/elliptic_curves/short_weierstrass/generator:gpu.h.tpl\"),\n        ),\n        \"_tool\": attr.label(\n            # TODO(chokobole): Change to \"exec\", so we can build on macos.\n            cfg = \"target\",\n            executable = True,\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/math/elliptic_curves/short_weierstrass/generator\"),\n        ),\n    },\n)\n\ndef generate_ec_points(\n        name,\n        namespace,\n        base_field_degree,\n        base_field,\n        base_field_hdr,\n        base_field_dep,\n        scalar_field,\n        scalar_field_hdr,\n        scalar_field_dep,\n        a,\n        b,\n        x,\n        y,\n        class_name = \"\",\n        mul_by_a_override = \"\",\n        endomorphism_coefficient = [],\n        lambda_ = \"\",\n        glv_coeffs = [],\n        gen_gpu = False,\n        **kwargs):\n    for n in [\n        (\"{}_gen_hdr\".format(name), \"{}.h\".format(name)),\n        (\"{}_gen_gpu_hdr\".format(name), \"{}_gpu.h\".format(name)),\n    ]:\n        generate_ec_point(\n            namespace = namespace,\n            class_name = class_name,\n            base_field_degree = base_field_degree,\n            base_field = base_field,\n            base_field_hdr = base_field_hdr,\n            scalar_field = scalar_field,\n            scalar_field_hdr = scalar_field_hdr,\n            a = a,\n            b = b,\n            x = x,\n            y = y,\n            mul_by_a_override = mul_by_a_override,\n            endomorphism_coefficient = endomorphism_coefficient,\n            lambda_ = lambda_,\n            glv_coeffs = glv_coeffs,\n            name = n[0],\n            out = n[1],\n        )\n\n    tachyon_cc_library(\n        name = name,\n        hdrs = [\":{}_gen_hdr\".format(name)],\n        deps = [\n            base_field_dep,\n            scalar_field_dep,\n            \"//tachyon/math/elliptic_curves/short_weierstrass:points\",\n            \"//tachyon/math/elliptic_curves/short_weierstrass:sw_curve\",\n            \"@com_google_absl//absl/base\",\n        ],\n        **kwargs\n    )\n\n    # TODO(chokobole): Remove this if condition once GPU G2 is implemented.\n    if gen_gpu:\n        tachyon_cc_library(\n            name = \"{}_gpu\".format(name),\n            hdrs = [\":{}_gen_gpu_hdr\".format(name)],\n            deps = [\n                \":{}\".format(name),\n                base_field_dep + \"_gpu\",\n                scalar_field_dep + \"_gpu\",\n            ],\n            **kwargs\n        )\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/short_weierstrass/generator/cpu.h.tpl",
    "content": "// clang-format off\n#include \"absl/base/call_once.h\"\n\n#include \"tachyon/base/logging.h\"\n#include \"%{base_field_hdr}\"\n#include \"%{scalar_field_hdr}\"\n%{if HasGLVCoefficients}\n#include \"tachyon/math/base/gmp/gmp_util.h\"\n%{endif HasGLVCoefficients}\n#include \"tachyon/math/elliptic_curves/short_weierstrass/affine_point.h\"\n#include \"tachyon/math/elliptic_curves/short_weierstrass/jacobian_point.h\"\n#include \"tachyon/math/elliptic_curves/short_weierstrass/point_xyzz.h\"\n#include \"tachyon/math/elliptic_curves/short_weierstrass/projective_point.h\"\n#include \"tachyon/math/elliptic_curves/short_weierstrass/sw_curve.h\"\n\nnamespace %{namespace} {\n\ntemplate <typename _BaseField, typename _ScalarField>\nclass %{class}CurveConfig {\n public:\n  using BaseField = _BaseField;\n  using BasePrimeField = %{base_prime_field};\n  using ScalarField = _ScalarField;\n\n  using CpuBaseField = typename BaseField::CpuField;\n  using CpuScalarField = typename ScalarField::CpuField;\n  using GpuBaseField = typename BaseField::GpuField;\n  using GpuScalarField = typename ScalarField::GpuField;\n  using CpuCurveConfig = %{class}CurveConfig<CpuBaseField, CpuScalarField>;\n  using GpuCurveConfig = %{class}CurveConfig<GpuBaseField, GpuScalarField>;\n\n  constexpr static bool kAIsZero = %{a_is_zero};\n\n  // TODO(chokobole): Make them constexpr.\n  static BaseField kA;\n  static BaseField kB;\n  static Point2<BaseField> kGenerator;\n%{if HasGLVCoefficients}\n  static BaseField kEndomorphismCoefficient;\n%{endif HasGLVCoefficients}\n  static ScalarField kLambda;\n  static mpz_class kGLVCoeffs[4];\n\n  static void Init() {\n    static absl::once_flag once;\n    absl::call_once(once, &%{class}CurveConfig::DoInit);\n  }\n\n  constexpr static BaseField MulByA(const BaseField& v) {\n%{mul_by_a_code}\n  }\n\n private:\n  static void DoInit() {\n%{a_init}\n%{b_init}\n%{x_init}\n%{y_init}\n%{if HasGLVCoefficients}\n%{endomorphism_coefficient_init_code}\n%{endif HasGLVCoefficients}\n%{lambda_init_code}\n%{glv_coeffs_init_code}\n    VLOG(1) << \"%{namespace}::%{class} initialized\";\n  }\n};\n\ntemplate <typename BaseField, typename ScalarField>\nBaseField %{class}CurveConfig<BaseField, ScalarField>::kA;\ntemplate <typename BaseField, typename ScalarField>\nBaseField %{class}CurveConfig<BaseField, ScalarField>::kB;\ntemplate <typename BaseField, typename ScalarField>\nPoint2<BaseField> %{class}CurveConfig<BaseField, ScalarField>::kGenerator;\n%{if HasGLVCoefficients}\ntemplate <typename BaseField, typename ScalarField>\nBaseField %{class}CurveConfig<BaseField, ScalarField>::kEndomorphismCoefficient;\n%{endif HasGLVCoefficients}\ntemplate <typename BaseField, typename ScalarField>\nScalarField %{class}CurveConfig<BaseField, ScalarField>::kLambda;\ntemplate <typename BaseField, typename ScalarField>\nmpz_class %{class}CurveConfig<BaseField, ScalarField>::kGLVCoeffs[4];\n\nusing %{class}Curve = SWCurve<%{class}CurveConfig<%{base_field}, %{scalar_field}>>;\nusing %{class}AffinePoint = AffinePoint<%{class}Curve>;\nusing %{class}ProjectivePoint = ProjectivePoint<%{class}Curve>;\nusing %{class}JacobianPoint = JacobianPoint<%{class}Curve>;\nusing %{class}PointXYZZ = PointXYZZ<%{class}Curve>;\n\n}  // namespace %{namespace}\n// clang-format on\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/short_weierstrass/generator/generator.cc",
    "content": "#include \"absl/container/flat_hash_map.h\"\n#include \"absl/strings/str_replace.h\"\n\n#include \"tachyon/base/console/iostream.h\"\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/files/file_path_flag.h\"\n#include \"tachyon/base/flag/flag_parser.h\"\n#include \"tachyon/base/strings/string_number_conversions.h\"\n#include \"tachyon/build/cc_writer.h\"\n#include \"tachyon/math/base/gmp/bit_traits.h\"\n#include \"tachyon/math/finite_fields/generator/generator_util.h\"\n\nnamespace tachyon {\n\nstd::string GenerateMulByAFunc(bool mul_by_a_fast, int64_t a_value) {\n  if (a_value == 0) return \"    return BaseField::Zero();\";\n  if (mul_by_a_fast) {\n    std::stringstream ss;\n    ss << \"    return \";\n    ss << math::GenerateFastMultiplication(a_value) << \";\";\n    return ss.str();\n  }\n  return \"    return kA * v;\";\n}\n\nstruct GenerationConfig : public build::CcWriter {\n  base::FilePath cpu_hdr_tpl_path;\n  base::FilePath gpu_hdr_tpl_path;\n\n  std::string ns_name;\n  std::string class_name;\n  int base_field_degree;\n  std::string base_field;\n  base::FilePath base_field_hdr;\n  std::string scalar_field;\n  base::FilePath scalar_field_hdr;\n  std::vector<std::string> a;\n  std::vector<std::string> b;\n  std::vector<std::string> x;\n  std::vector<std::string> y;\n  std::string mul_by_a_override;\n\n  // For GLV\n  std::vector<std::string> endomorphism_coefficient;\n  std::string lambda;\n  std::vector<std::string> glv_coefficients;\n\n  int GenerateConfigHdr() const;\n  int GenerateConfigGpuHdr() const;\n};\n\nint GenerationConfig::GenerateConfigHdr() const {\n  CHECK_EQ(b.size(), a.size());\n  CHECK_EQ(x.size(), a.size());\n  CHECK_EQ(y.size(), a.size());\n\n  absl::flat_hash_map<std::string, std::string> replacements = {\n      {\"%{namespace}\", ns_name},\n      {\"%{class}\", class_name},\n      {\"%{base_field}\", base_field},\n      {\"%{base_field_hdr}\", base_field_hdr.value()},\n      {\"%{scalar_field}\", scalar_field},\n      {\"%{scalar_field_hdr}\", scalar_field_hdr.value()},\n      {\"%{base_prime_field}\",\n       a.size() == 1 ? \"BaseField\" : \"typename BaseField::BasePrimeField\"}};\n\n  if (a.size() == 1) {\n    replacements[\"%{a_init}\"] =\n        math::GenerateInitField(\"kA\", \"BaseField\", a[0]);\n    replacements[\"%{b_init}\"] =\n        math::GenerateInitField(\"kB\", \"BaseField\", b[0]);\n    replacements[\"%{x_init}\"] =\n        math::GenerateInitField(\"kGenerator.x\", \"BaseField\", x[0]);\n    replacements[\"%{y_init}\"] =\n        math::GenerateInitField(\"kGenerator.y\", \"BaseField\", y[0]);\n  } else {\n    replacements[\"%{a_init}\"] =\n        math::GenerateInitExtField(\"kA\", \"BaseField\", a, base_field_degree);\n    replacements[\"%{b_init}\"] =\n        math::GenerateInitExtField(\"kB\", \"BaseField\", b, base_field_degree);\n    replacements[\"%{x_init}\"] = math::GenerateInitExtField(\n        \"kGenerator.x\", \"BaseField\", x, base_field_degree);\n    replacements[\"%{y_init}\"] = math::GenerateInitExtField(\n        \"kGenerator.y\", \"BaseField\", y, base_field_degree);\n  }\n\n  int64_t a_value;\n  CHECK(base::StringToInt64(a[0], &a_value));\n  bool a_is_zero = a_value == 0;\n  replacements[\"%{a_is_zero}\"] = base::BoolToString(a_is_zero);\n\n  bool mul_by_a_fast =\n      a.size() == 1\n          ? true\n          : std::all_of(a.begin() + 1, a.end(), [](const std::string& a) {\n              return math::gmp::FromDecString(a) == mpz_class(0);\n            });\n\n  replacements[\"%{mul_by_a_code}\"] =\n      !mul_by_a_override.empty() ? mul_by_a_override\n                                 : GenerateMulByAFunc(mul_by_a_fast, a_value);\n\n  std::string tpl_content;\n  CHECK(base::ReadFileToString(cpu_hdr_tpl_path, &tpl_content));\n  std::vector<std::string> tpl_lines = absl::StrSplit(tpl_content, '\\n');\n\n  bool has_glv_coefficients = !glv_coefficients.empty();\n  if (has_glv_coefficients) {\n    if (endomorphism_coefficient.size() == 1) {\n      replacements[\"%{endomorphism_coefficient_init_code}\"] =\n          math::GenerateInitField(\"kEndomorphismCoefficient\", \"BaseField\",\n                                  endomorphism_coefficient[0]);\n    } else {\n      replacements[\"%{endomorphism_coefficient_init_code}\"] =\n          math::GenerateInitExtField(\"kEndomorphismCoefficient\", \"BaseField\",\n                                     endomorphism_coefficient,\n                                     base_field_degree);\n    }\n    replacements[\"%{lambda_init_code}\"] =\n        math::GenerateInitField(\"kLambda\", \"ScalarField\", lambda);\n    replacements[\"%{glv_coeffs_init_code}\"] = absl::StrJoin(\n        base::CreateVector(4,\n                           [this](size_t i) {\n                             return math::GenerateInitMpzClass(\n                                 absl::Substitute(\"kGLVCoeffs[$0]\", i),\n                                 glv_coefficients[i]);\n                           }),\n        \"\\n\");\n\n    RemoveOptionalLines(tpl_lines, \"HasGLVCoefficients\", has_glv_coefficients);\n  }\n  tpl_content = absl::StrJoin(tpl_lines, \"\\n\");\n  std::string content = absl::StrReplaceAll(tpl_content, replacements);\n  return WriteHdr(content, false);\n}\n\nint GenerationConfig::GenerateConfigGpuHdr() const {\n  absl::flat_hash_map<std::string, std::string> replacements = {\n      {\"%{namespace}\", ns_name},\n      {\"%{class}\", class_name},\n      {\"%{base_field}\", base_field},\n      {\"%{base_field_header}\", math::ConvertToGpuHdr(base_field_hdr).value()},\n      {\"%{scalar_field}\", scalar_field},\n      {\"%{scalar_field_header}\",\n       math::ConvertToGpuHdr(scalar_field_hdr).value()},\n      {\"%{cpu_header_path}\", math::ConvertToCpuHdr(GetHdrPath()).value()},\n  };\n\n  std::string tpl_content;\n  CHECK(base::ReadFileToString(gpu_hdr_tpl_path, &tpl_content));\n  std::string content = absl::StrReplaceAll(tpl_content, replacements);\n  return WriteHdr(content, false);\n}\n\nint RealMain(int argc, char** argv) {\n  GenerationConfig config;\n  config.generator =\n      \"//tachyon/math/elliptic_curves/short_weierstrass/generator\";\n\n  base::FlagParser parser;\n  parser.AddFlag<base::FilePathFlag>(&config.out)\n      .set_long_name(\"--out\")\n      .set_help(\"path to output\");\n  parser.AddFlag<base::StringFlag>(&config.ns_name)\n      .set_long_name(\"--namespace\")\n      .set_required();\n  parser.AddFlag<base::StringFlag>(&config.class_name).set_long_name(\"--class\");\n  parser.AddFlag<base::IntFlag>(&config.base_field_degree)\n      .set_long_name(\"--base_field_degree\")\n      .set_required();\n  parser.AddFlag<base::StringFlag>(&config.base_field)\n      .set_long_name(\"--base_field\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.base_field_hdr)\n      .set_long_name(\"--base_field_hdr\")\n      .set_required();\n  parser.AddFlag<base::StringFlag>(&config.scalar_field)\n      .set_long_name(\"--scalar_field\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.scalar_field_hdr)\n      .set_long_name(\"--scalar_field_hdr\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.cpu_hdr_tpl_path)\n      .set_long_name(\"--cpu_hdr_tpl_path\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.gpu_hdr_tpl_path)\n      .set_long_name(\"--gpu_hdr_tpl_path\")\n      .set_required();\n  parser.AddFlag<base::Flag<std::vector<std::string>>>(&config.a)\n      .set_short_name(\"-a\")\n      .set_required();\n  parser.AddFlag<base::Flag<std::vector<std::string>>>(&config.b)\n      .set_short_name(\"-b\")\n      .set_required();\n  parser.AddFlag<base::Flag<std::vector<std::string>>>(&config.x)\n      .set_short_name(\"-x\")\n      .set_required();\n  parser.AddFlag<base::Flag<std::vector<std::string>>>(&config.y)\n      .set_short_name(\"-y\")\n      .set_required();\n  parser\n      .AddFlag<base::Flag<std::vector<std::string>>>(\n          &config.endomorphism_coefficient)\n      .set_long_name(\"--endomorphism_coefficient\");\n  parser.AddFlag<base::StringFlag>(&config.lambda).set_long_name(\"--lambda\");\n  parser.AddFlag<base::Flag<std::vector<std::string>>>(&config.glv_coefficients)\n      .set_long_name(\"--glv_coefficients\");\n  parser.AddFlag<base::StringFlag>(&config.mul_by_a_override)\n      .set_long_name(\"--mul_by_a_override\");\n\n  std::string error;\n  if (!parser.Parse(argc, argv, &error)) {\n    tachyon_cerr << error << std::endl;\n    return 1;\n  }\n\n  if (base::EndsWith(config.out.value(), \"_gpu.h\")) {\n    return config.GenerateConfigGpuHdr();\n  } else if (base::EndsWith(config.out.value(), \".h\")) {\n    return config.GenerateConfigHdr();\n  } else {\n    tachyon_cerr << \"suffix not supported:\" << config.out << std::endl;\n    return 1;\n  }\n}\n\n}  // namespace tachyon\n\nint main(int argc, char** argv) { return tachyon::RealMain(argc, argv); }\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/short_weierstrass/generator/gpu.h.tpl",
    "content": "// clang-format off\n#include \"%{base_field_header}\"\n#include \"%{scalar_field_header}\"\n#include \"%{cpu_header_path}\"\n\nnamespace %{namespace} {\n\nusing %{class}CurveGpu = SWCurve<%{class}CurveConfig<%{base_field}Gpu, %{scalar_field}Gpu>>;\nusing %{class}AffinePointGpu = AffinePoint<%{class}CurveGpu>;\nusing %{class}ProjectivePointGpu = ProjectivePoint<%{class}CurveGpu>;\nusing %{class}JacobianPointGpu = JacobianPoint<%{class}CurveGpu>;\nusing %{class}PointXYZZGpu = PointXYZZ<%{class}CurveGpu>;\n\n}  // namespace %{namespace}\n// clang-format on\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/short_weierstrass/jacobian_point.h",
    "content": "#ifndef TACHYON_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_JACOBIAN_POINT_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_JACOBIAN_POINT_H_\n\n#include <optional>\n#include <string>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/base/buffer/copyable.h\"\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/json/json.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/parallelize.h\"\n#include \"tachyon/math/base/groups.h\"\n#include \"tachyon/math/geometry/affine_point.h\"\n#include \"tachyon/math/geometry/curve_type.h\"\n#include \"tachyon/math/geometry/jacobian_point.h\"\n#include \"tachyon/math/geometry/point3.h\"\n#include \"tachyon/math/geometry/point_xyzz.h\"\n#include \"tachyon/math/geometry/projective_point.h\"\n\nnamespace tachyon {\nnamespace math {\n\ntemplate <typename _Curve>\nclass JacobianPoint<\n    _Curve, std::enable_if_t<_Curve::kType == CurveType::kShortWeierstrass>>\n    final : public AdditiveGroup<JacobianPoint<_Curve>> {\n public:\n  constexpr static bool kNegationIsCheap = true;\n\n  using Curve = _Curve;\n  using BaseField = typename Curve::BaseField;\n  using ScalarField = typename Curve::ScalarField;\n\n  constexpr JacobianPoint()\n      : JacobianPoint(BaseField::One(), BaseField::One(), BaseField::Zero()) {}\n  explicit constexpr JacobianPoint(const Point3<BaseField>& point)\n      : JacobianPoint(point.x, point.y, point.z) {}\n  explicit constexpr JacobianPoint(Point3<BaseField>&& point)\n      : JacobianPoint(std::move(point.x), std::move(point.y),\n                      std::move(point.z)) {}\n  constexpr JacobianPoint(const BaseField& x, const BaseField& y,\n                          const BaseField& z)\n      : x_(x), y_(y), z_(z) {}\n  constexpr JacobianPoint(BaseField&& x, BaseField&& y, BaseField&& z)\n      : x_(std::move(x)), y_(std::move(y)), z_(std::move(z)) {}\n\n  constexpr static JacobianPoint CreateChecked(const BaseField& x,\n                                               const BaseField& y,\n                                               const BaseField& z) {\n    JacobianPoint ret = {x, y, z};\n    CHECK(ret.IsOnCurve());\n    return ret;\n  }\n\n  constexpr static JacobianPoint CreateChecked(BaseField&& x, BaseField&& y,\n                                               BaseField&& z) {\n    JacobianPoint ret = {std::move(x), std::move(y), std::move(z)};\n    CHECK(ret.IsOnCurve());\n    return ret;\n  }\n\n  constexpr static std::optional<JacobianPoint> CreateFromX(const BaseField& x,\n                                                            bool pick_odd) {\n    JacobianPoint point{};\n    if (!Curve::GetPointFromX(x, pick_odd, &point)) return std::nullopt;\n    return point;\n  }\n\n  constexpr static JacobianPoint Zero() { return JacobianPoint(); }\n\n  constexpr static JacobianPoint One() { return Generator(); }\n\n  constexpr static JacobianPoint Generator() {\n    return {Curve::Config::kGenerator.x, Curve::Config::kGenerator.y,\n            BaseField::One()};\n  }\n\n  constexpr static JacobianPoint FromAffine(const AffinePoint<Curve>& point) {\n    return point.ToJacobian();\n  }\n\n  constexpr static JacobianPoint FromProjective(\n      const ProjectivePoint<Curve>& point) {\n    return point.ToJacobian();\n  }\n\n  constexpr static JacobianPoint FromXYZZ(const PointXYZZ<Curve>& point) {\n    return point.ToJacobian();\n  }\n\n  constexpr static JacobianPoint Random() {\n    return ScalarField::Random() * Generator();\n  }\n\n  constexpr static JacobianPoint Endomorphism(const JacobianPoint& point) {\n    return JacobianPoint(point.x_ * Curve::Config::kEndomorphismCoefficient,\n                         point.y_, point.z_);\n  }\n\n  template <typename JacobianContainer, typename AffineContainer>\n  [[nodiscard]] CONSTEXPR_IF_NOT_OPENMP static bool BatchNormalize(\n      const JacobianContainer& jacobian_points,\n      AffineContainer* affine_points) {\n    size_t size = std::size(jacobian_points);\n    if (size != std::size(*affine_points)) {\n      LOG(ERROR)\n          << \"Size of |jacobian_points| and |affine_points| do not match\";\n      return false;\n    }\n#if defined(TACHYON_HAS_OPENMP)\n    size_t thread_nums = static_cast<size_t>(omp_get_max_threads());\n    if (size >=\n        size_t{1} << (thread_nums /\n                      ScalarField::kParallelBatchInverseDivisorThreshold)) {\n      base::Parallelize(\n          size, [&jacobian_points, affine_points](\n                    size_t len, size_t chunk_offset, size_t chunk_size) {\n            size_t start = chunk_offset * chunk_size;\n            absl::Span<AffinePoint<Curve>> affine_points_chunk(\n                &(*affine_points)[start], len);\n            absl::Span<const JacobianPoint> jacobian_points_chunk(\n                &jacobian_points[start], len);\n            CHECK(BatchNormalizeSerial(jacobian_points_chunk,\n                                       &affine_points_chunk));\n          });\n      return true;\n    }\n#endif\n    return BatchNormalizeSerial(jacobian_points, affine_points);\n  }\n\n  template <typename JacobianContainer, typename AffineContainer>\n  [[nodiscard]] constexpr static bool BatchNormalizeSerial(\n      const JacobianContainer& jacobian_points,\n      AffineContainer* affine_points) {\n    size_t size = std::size(jacobian_points);\n    if (size != std::size(*affine_points)) {\n      LOG(ERROR)\n          << \"Size of |jacobian_points| and |affine_points| do not match\";\n      return false;\n    }\n    std::vector<BaseField> z_inverses = base::Map(\n        jacobian_points, [](const JacobianPoint& point) { return point.z_; });\n    if (!BaseField::BatchInverseInPlaceSerial(z_inverses)) return false;\n    for (size_t i = 0; i < size; ++i) {\n      const BaseField& z_inv = z_inverses[i];\n      if (z_inv.IsZero()) {\n        (*affine_points)[i] = AffinePoint<Curve>::Zero();\n      } else if (z_inv.IsOne()) {\n        (*affine_points)[i] = {jacobian_points[i].x_, jacobian_points[i].y_};\n      } else {\n        BaseField z_inv_square = z_inv.Square();\n        (*affine_points)[i] = {jacobian_points[i].x_ * z_inv_square,\n                               jacobian_points[i].y_ * z_inv_square * z_inv};\n      }\n    }\n    return true;\n  }\n\n  constexpr const BaseField& x() const { return x_; }\n  constexpr const BaseField& y() const { return y_; }\n  constexpr const BaseField& z() const { return z_; }\n\n  constexpr bool operator==(const JacobianPoint& other) const {\n    if (IsZero()) {\n      return other.IsZero();\n    }\n\n    if (other.IsZero()) {\n      return false;\n    }\n\n    // The points (X, Y, Z) and (X', Y', Z')\n    // are equal when (X * Z'²) = (X' * Z²)\n    // and (Y * Z'³) = (Y' * Z³).\n    const BaseField z1z1 = z_ * z_;\n    const BaseField z2z2 = other.z_ * other.z_;\n\n    if (x_ * z2z2 != other.x_ * z1z1) {\n      return false;\n    } else {\n      return y_ * (z2z2 * other.z_) == other.y_ * (z1z1 * z_);\n    }\n  }\n\n  constexpr bool operator!=(const JacobianPoint& other) const {\n    return !operator==(other);\n  }\n\n  constexpr bool IsZero() const { return z_.IsZero(); }\n\n  constexpr bool IsOnCurve() { return Curve::IsOnCurve(*this); }\n\n  // The jacobian point X, Y, Z is represented in the affine\n  // coordinates as X/Z², Y/Z³.\n  constexpr AffinePoint<Curve> ToAffine() const {\n    if (IsZero()) {\n      return AffinePoint<Curve>::Zero();\n    } else if (z_.IsOne()) {\n      return {x_, y_};\n    } else {\n      // NOTE(ashjeong): if |z_| is 0, |IsZero()| will also evaluate to true,\n      // and this block will not be executed\n      BaseField z_inv = *z_.Inverse();\n      BaseField z_inv_square = z_inv.Square();\n      return {x_ * z_inv_square, y_ * z_inv_square * z_inv};\n    }\n  }\n\n  // The jacobian point X, Y, Z is represented in the projective\n  // coordinates as X*Z, Y, Z³.\n  constexpr ProjectivePoint<Curve> ToProjective() const {\n    BaseField zz = z_.Square();\n    return {x_ * z_, y_, zz * z_};\n  }\n\n  // The jacobian point X, Y, Z is represented in the xyzz\n  // coordinates as X, Y, Z², Z³.\n  constexpr PointXYZZ<Curve> ToXYZZ() const {\n    BaseField zz = z_.Square();\n    return {x_, y_, zz, zz * z_};\n  }\n\n  std::string ToString() const {\n    return absl::Substitute(\"($0, $1, $2)\", x_.ToString(), y_.ToString(),\n                            z_.ToString());\n  }\n\n  std::string ToHexString(bool pad_zero = false) const {\n    return absl::Substitute(\"($0, $1, $2)\", x_.ToHexString(pad_zero),\n                            y_.ToHexString(pad_zero), z_.ToHexString(pad_zero));\n  }\n\n  // AdditiveSemigroup methods\n  constexpr JacobianPoint Add(const JacobianPoint& other) const;\n  constexpr JacobianPoint& AddInPlace(const JacobianPoint& other);\n  constexpr JacobianPoint Add(const AffinePoint<Curve>& other) const;\n  constexpr JacobianPoint& AddInPlace(const AffinePoint<Curve>& other);\n  constexpr JacobianPoint DoubleImpl() const;\n  constexpr JacobianPoint& DoubleImplInPlace();\n\n  // AdditiveGroup methods\n  constexpr JacobianPoint Negate() const { return {x_, -y_, z_}; }\n\n  constexpr JacobianPoint& NegateInPlace() {\n    y_.NegateInPlace();\n    return *this;\n  }\n\n  constexpr JacobianPoint operator*(const ScalarField& v) const {\n    return this->ScalarMul(v);\n  }\n  constexpr JacobianPoint& operator*=(const ScalarField& v) {\n    return *this = operator*(v);\n  }\n\n private:\n  constexpr static void DoAdd(const JacobianPoint& a, const JacobianPoint& b,\n                              JacobianPoint& c);\n  constexpr static void DoAdd(const JacobianPoint& a,\n                              const AffinePoint<Curve>& b, JacobianPoint& c);\n  constexpr static void DoDoubleImpl(const JacobianPoint& a, JacobianPoint& b);\n\n  BaseField x_;\n  BaseField y_;\n  BaseField z_;\n};\n}  // namespace math\n\nnamespace base {\n\ntemplate <typename Curve>\nclass Copyable<math::JacobianPoint<\n    Curve,\n    std::enable_if_t<Curve::kType == math::CurveType::kShortWeierstrass>>> {\n public:\n  static bool WriteTo(const math::JacobianPoint<Curve>& point, Buffer* buffer) {\n    return buffer->WriteMany(point.x(), point.y(), point.z());\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer,\n                       math::JacobianPoint<Curve>* point) {\n    using BaseField = typename math::JacobianPoint<Curve>::BaseField;\n    BaseField x, y, z;\n    if (!buffer.ReadMany(&x, &y, &z)) return false;\n\n    *point =\n        math::JacobianPoint<Curve>(std::move(x), std::move(y), std::move(z));\n    return true;\n  }\n\n  static size_t EstimateSize(const math::JacobianPoint<Curve>& point) {\n    return base::EstimateSize(point.x(), point.y(), point.z());\n  }\n};\n\ntemplate <typename Curve>\nclass RapidJsonValueConverter<math::JacobianPoint<\n    Curve,\n    std::enable_if_t<Curve::kType == math::CurveType::kShortWeierstrass>>> {\n public:\n  using Field = typename math::JacobianPoint<Curve>::BaseField;\n\n  template <typename Allocator>\n  static rapidjson::Value From(const math::JacobianPoint<Curve>& value,\n                               Allocator& allocator) {\n    rapidjson::Value object(rapidjson::kObjectType);\n    AddJsonElement(object, \"x\", value.x(), allocator);\n    AddJsonElement(object, \"y\", value.y(), allocator);\n    AddJsonElement(object, \"z\", value.z(), allocator);\n    return object;\n  }\n\n  static bool To(const rapidjson::Value& json_value, std::string_view key,\n                 math::JacobianPoint<Curve>* value, std::string* error) {\n    Field x;\n    Field y;\n    Field z;\n    if (!ParseJsonElement(json_value, \"x\", &x, error)) return false;\n    if (!ParseJsonElement(json_value, \"y\", &y, error)) return false;\n    if (!ParseJsonElement(json_value, \"z\", &z, error)) return false;\n    *value =\n        math::JacobianPoint<Curve>(std::move(x), std::move(y), std::move(z));\n    return true;\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#include \"tachyon/math/elliptic_curves/short_weierstrass/jacobian_point_impl.h\"\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_JACOBIAN_POINT_H_\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/short_weierstrass/jacobian_point_impl.h",
    "content": "#ifndef TACHYON_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_JACOBIAN_POINT_IMPL_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_JACOBIAN_POINT_IMPL_H_\n\n#include <utility>\n\n#include \"tachyon/math/elliptic_curves/short_weierstrass/jacobian_point.h\"\n\nnamespace tachyon::math {\n\n#define CLASS    \\\n  JacobianPoint< \\\n      Curve, std::enable_if_t<Curve::kType == CurveType::kShortWeierstrass>>\n\ntemplate <typename Curve>\nconstexpr CLASS CLASS::Add(const JacobianPoint& other) const {\n  if (IsZero()) {\n    return other;\n  }\n\n  if (other.IsZero()) {\n    return *this;\n  }\n\n  JacobianPoint ret;\n  DoAdd(*this, other, ret);\n  return ret;\n}\n\ntemplate <typename Curve>\nconstexpr CLASS& CLASS::AddInPlace(const JacobianPoint& other) {\n  if (IsZero()) {\n    return *this = other;\n  }\n\n  if (other.IsZero()) {\n    return *this;\n  }\n\n  DoAdd(*this, other, *this);\n  return *this;\n}\n\n// static\ntemplate <typename Curve>\nconstexpr void CLASS::DoAdd(const JacobianPoint& a, const JacobianPoint& b,\n                            JacobianPoint& c) {\n  // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl\n  // Z1Z1 = Z1²\n  BaseField z1z1 = a.z_.Square();\n\n  // Z2Z2 = Z2²\n  BaseField z2z2 = b.z_.Square();\n\n  // U1 = X1 * Z2Z2\n  BaseField u1 = a.x_ * z2z2;\n\n  // U2 = X2 * Z1Z1\n  BaseField u2 = b.x_ * z1z1;\n\n  // S1 = Y1 * Z2 * Z2Z2\n  BaseField s1 = a.y_ * b.z_;\n  s1 *= z2z2;\n\n  // S2 = Y2 * Z1 * Z1Z1\n  BaseField s2 = b.y_ * a.z_;\n  s2 *= z1z1;\n\n  if (u1 == u2 && s1 == s2) {\n    // The two points are equal, so we Double.\n    if (&a == &c) {\n      c.DoubleInPlace();\n    } else {\n      c = a.Double();\n    }\n  } else {\n    // If we're adding -a and a together, c.z_ becomes zero as H becomes zero.\n\n    // H = U2 - U1\n    BaseField h = u2 - u1;\n\n    // I = (2 * H)²\n    BaseField i = h.Double();\n    i.SquareInPlace();\n\n    // J = -H * I\n    BaseField j = h * i;\n    j.NegateInPlace();\n\n    // r = 2 * (S2 - S1)\n    BaseField r = s2 - s1;\n    r.DoubleInPlace();\n\n    // V = U1 * I\n    BaseField v = u1 * i;\n\n    // X3 = r² + J - 2 * V\n    c.x_ = r.Square();\n    c.x_ += j;\n    c.x_ -= v.Double();\n\n    // Y3 = r * (V - X3) + 2 * S1 * J\n    BaseField lefts[] = {std::move(r), s1.Double()};\n    BaseField rights[] = {v - c.x_, std::move(j)};\n    c.y_ = BaseField::SumOfProductsSerial(lefts, rights);\n\n    // Z3 = ((Z1 + Z2)² - Z1Z1 - Z2Z2) * H\n    // This is equal to Z3 = 2 * Z1 * Z2 * H, and computing it this way is\n    // faster.\n    c.z_ = a.z_ * b.z_;\n    c.z_.DoubleInPlace();\n    c.z_ *= h;\n  }\n}\n\ntemplate <typename Curve>\nconstexpr CLASS CLASS::Add(const AffinePoint<Curve>& other) const {\n  if (other.IsZero()) return *this;\n  if (IsZero()) {\n    return JacobianPoint::FromAffine(other);\n  }\n\n  JacobianPoint ret;\n  DoAdd(*this, other, ret);\n  return ret;\n}\n\ntemplate <typename Curve>\nconstexpr CLASS& CLASS::AddInPlace(const AffinePoint<Curve>& other) {\n  if (other.IsZero()) return *this;\n  if (IsZero()) {\n    return *this = JacobianPoint::FromAffine(other);\n  }\n\n  DoAdd(*this, other, *this);\n  return *this;\n}\n\n// static\ntemplate <typename Curve>\nconstexpr void CLASS::DoAdd(const JacobianPoint& a, const AffinePoint<Curve>& b,\n                            JacobianPoint& c) {\n  // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-madd-2007-bl\n  // Z1Z1 = Z1²\n  BaseField z1z1 = a.z_.Square();\n\n  // U2 = X2 * Z1Z1\n  BaseField u2 = b.x() * z1z1;\n\n  // S2 = Y2 * Z1 * Z1Z1\n  BaseField s2 = b.y() * a.z_;\n  s2 *= z1z1;\n\n  if (a.x_ == u2 && a.y_ == s2) {\n    // The two points are equal, so we Double.\n    if (&a == &c) {\n      c.DoubleInPlace();\n    } else {\n      c = a.Double();\n    }\n  } else {\n    // If we're adding -a and a together, c.z_ becomes zero as H becomes zero.\n\n    // H = U2 - X1\n    BaseField h = u2 - a.x_;\n\n    // I = 4 * H²\n    BaseField i = h.Square();\n    i.DoubleInPlace().DoubleInPlace();\n\n    // J = -H * I\n    BaseField j = h * i;\n    j.NegateInPlace();\n\n    // r = 2 * (S2 - Y1)\n    BaseField r = s2 - a.y_;\n    r.DoubleInPlace();\n\n    // V = X1 * I\n    BaseField v = a.x_ * i;\n\n    // X3 = r² + J - 2 * V\n    c.x_ = r.Square();\n    c.x_ += j;\n    c.x_ -= v.Double();\n\n    // Y3 = r * (V - X3) + 2 * Y1 * J\n    BaseField lefts[] = {std::move(r), a.y_.Double()};\n    BaseField rights[] = {v - c.x_, std::move(j)};\n    c.y_ = BaseField::SumOfProductsSerial(lefts, rights);\n\n    // Z3 = 2 * Z1 * H;\n    // Can alternatively be computed as (Z1 + H)² - Z1Z1 - HH, but the latter is\n    // slower.\n    c.z_ = a.z_ * h;\n    c.z_.DoubleInPlace();\n  }\n}\n\ntemplate <typename Curve>\nconstexpr CLASS CLASS::DoubleImpl() const {\n  if (IsZero()) {\n    return JacobianPoint::Zero();\n  }\n\n  JacobianPoint ret;\n  DoDoubleImpl(*this, ret);\n  return ret;\n}\n\ntemplate <typename Curve>\nconstexpr CLASS& CLASS::DoubleImplInPlace() {\n  if (IsZero()) {\n    return *this;\n  }\n\n  DoDoubleImpl(*this, *this);\n  return *this;\n}\n\n// static\ntemplate <typename Curve>\nconstexpr void CLASS::DoDoubleImpl(const JacobianPoint& a, JacobianPoint& b) {\n  if constexpr (Curve::Config::kAIsZero) {\n    // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l\n    // XX = X1²\n    BaseField xx = a.x_.Square();\n\n    // YY = Y1²\n    BaseField yy = a.y_.Square();\n\n    // YYYY = YY²\n    BaseField yyyy = yy.Square();\n\n    // D = 2 * ((X1 + B)² - XX - YYYY)\n    //   = 2 * ((X1 + Y1²)² - XX - YYYY)\n    //   = 2 * 2 * X1 * Y1²\n    BaseField d;\n    if constexpr (BaseField::ExtensionDegree() == 1 ||\n                  BaseField::ExtensionDegree() == 2) {\n      d = a.x_ * yy;\n      d.DoubleInPlace().DoubleInPlace();\n    } else {\n      d = a.x_ + yy;\n      d.SquareInPlace();\n      d -= xx;\n      d -= yyyy;\n      d.DoubleInPlace();\n    }\n\n    // E = 3 * XX\n    BaseField e = xx.Double();\n    e += xx;\n\n    // Z3 = 2 * Y1 * Z1\n    b.z_ = a.y_ * a.z_;\n    b.z_.DoubleInPlace();\n\n    // X3 = E² - 2 * D\n    b.x_ = e.Square();\n    b.x_ -= d.Double();\n\n    // Y3 = E * (D - X3) - 8 * YYYY\n    b.y_ = d - b.x_;\n    b.y_ *= e;\n    b.y_ -= yyyy.DoubleInPlace().DoubleInPlace().DoubleInPlace();\n  } else {\n    // https://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#doubling-dbl-2007-bl\n    // XX = X1²\n    BaseField xx = a.x_.Square();\n\n    // YY = Y1²\n    BaseField yy = a.y_.Square();\n\n    // YYYY = YY²\n    BaseField yyyy = yy.Square();\n\n    // ZZ = Z1²\n    BaseField zz = a.z_.Square();\n\n    // S = 2 * ((X1 + YY)² - XX - YYYY)\n    BaseField s = a.x_ + yy;\n    s.SquareInPlace();\n    s -= xx;\n    s -= yyyy;\n    s.DoubleInPlace();\n\n    // M = 3 * XX + a * ZZ²\n    BaseField m = xx.Double();\n    m += xx;\n    if constexpr (!Curve::Config::kAIsZero) {\n      m += Curve::Config::MulByA(zz.Square());\n    }\n\n    // T = M² - 2 * S\n    // X3 = T\n    b.x_ = m.Square();\n    b.x_ -= s.Double();\n\n    // Z3 = (Y1 + Z1)² - YY - ZZ\n    // Can be calculated as Z3 = 2 * Y1 * Z1, and this is faster.\n    b.z_ = a.y_ + a.z_;\n    b.z_.DoubleInPlace();\n\n    // Y3 = M * (S - X3) - 8 * YYYY\n    b.y_ = s - b.x_;\n    b.y_ *= m;\n    b.y_ -= yyyy.DoubleInPlace().DoubleInPlace().DoubleInPlace();\n  }\n}\n\n#undef CLASS\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_JACOBIAN_POINT_IMPL_H_\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/short_weierstrass/jacobian_point_unittest.cc",
    "content": "#include \"tachyon/math/elliptic_curves/short_weierstrass/jacobian_point.h\"\n\n#include <vector>\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/buffer/vector_buffer.h\"\n#include \"tachyon/math/elliptic_curves/short_weierstrass/affine_point.h\"\n#include \"tachyon/math/elliptic_curves/short_weierstrass/point_xyzz.h\"\n#include \"tachyon/math/elliptic_curves/short_weierstrass/projective_point.h\"\n#include \"tachyon/math/elliptic_curves/short_weierstrass/test/sw_curve_config.h\"\n#include \"tachyon/math/elliptic_curves/test/random.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\nclass JacobianPointTest : public testing::Test {\n public:\n  static void SetUpTestSuite() { test::G1Curve::Init(); }\n};\n\n}  // namespace\n\nTEST_F(JacobianPointTest, IsZero) {\n  EXPECT_TRUE(test::JacobianPoint::Zero().IsZero());\n  EXPECT_FALSE(test::JacobianPoint(GF7(1), GF7(2), GF7(1)).IsZero());\n}\n\nTEST_F(JacobianPointTest, Generator) {\n  EXPECT_EQ(test::JacobianPoint::Generator(),\n            test::JacobianPoint(\n                test::JacobianPoint::Curve::Config::kGenerator.x,\n                test::JacobianPoint::Curve::Config::kGenerator.y, GF7::One()));\n}\n\nTEST_F(JacobianPointTest, Random) {\n  bool success = false;\n  test::JacobianPoint r = test::JacobianPoint::Random();\n  for (size_t i = 0; i < 100; ++i) {\n    if (r != test::JacobianPoint::Random()) {\n      success = true;\n      break;\n    }\n  }\n  EXPECT_TRUE(success);\n}\n\nTEST_F(JacobianPointTest, EqualityOperators) {\n  {\n    SCOPED_TRACE(\"p.IsZero() && p2.IsZero()\");\n    test::JacobianPoint p(GF7(1), GF7(2), GF7(0));\n    test::JacobianPoint p2(GF7(3), GF7(4), GF7(0));\n    EXPECT_EQ(p, p2);\n    EXPECT_EQ(p2, p);\n  }\n\n  {\n    SCOPED_TRACE(\"!p.IsZero() && p2.IsZero()\");\n    test::JacobianPoint p(GF7(1), GF7(2), GF7(1));\n    test::JacobianPoint p2(GF7(3), GF7(4), GF7(0));\n    EXPECT_NE(p, p2);\n    EXPECT_NE(p2, p);\n  }\n\n  {\n    SCOPED_TRACE(\"other\");\n    test::JacobianPoint p(GF7(1), GF7(2), GF7(3));\n    test::JacobianPoint p2(GF7(1), GF7(2), GF7(3));\n    EXPECT_EQ(p, p2);\n    EXPECT_EQ(p2, p);\n  }\n}\n\nTEST_F(JacobianPointTest, AdditiveGroupOperators) {\n  test::JacobianPoint jp =\n      test::JacobianPoint::CreateChecked(GF7(5), GF7(5), GF7(1));\n  test::JacobianPoint jp2 =\n      test::JacobianPoint::CreateChecked(GF7(3), GF7(2), GF7(1));\n  test::JacobianPoint jp3 =\n      test::JacobianPoint::CreateChecked(GF7(3), GF7(5), GF7(1));\n  test::JacobianPoint jp4 =\n      test::JacobianPoint::CreateChecked(GF7(6), GF7(5), GF7(1));\n  test::AffinePoint ap = jp.ToAffine();\n  test::AffinePoint ap2 = jp2.ToAffine();\n\n  EXPECT_EQ(jp + jp2, jp3);\n  EXPECT_EQ(jp - jp3, -jp2);\n  EXPECT_EQ(jp + jp, jp4);\n  EXPECT_EQ(jp - jp4, -jp);\n\n  {\n    test::JacobianPoint jp_tmp = jp;\n    jp_tmp += jp2;\n    EXPECT_EQ(jp_tmp, jp3);\n    jp_tmp -= jp2;\n    EXPECT_EQ(jp_tmp, jp);\n  }\n\n  EXPECT_EQ(jp + ap2, jp3);\n  EXPECT_EQ(jp + ap, jp4);\n  EXPECT_EQ(jp - jp3, -jp2);\n  EXPECT_EQ(jp - jp4, -jp);\n\n  EXPECT_EQ(jp.Double(), jp4);\n  {\n    test::JacobianPoint jp_tmp = jp;\n    jp_tmp.DoubleInPlace();\n    EXPECT_EQ(jp_tmp, jp4);\n  }\n\n  EXPECT_EQ(-jp, test::JacobianPoint(GF7(5), GF7(2), GF7(1)));\n  {\n    test::JacobianPoint jp_tmp = jp;\n    jp_tmp.NegateInPlace();\n    EXPECT_EQ(jp_tmp, test::JacobianPoint(GF7(5), GF7(2), GF7(1)));\n  }\n\n  EXPECT_EQ(jp * GF7(2), jp4);\n  EXPECT_EQ(GF7(2) * jp, jp4);\n  EXPECT_EQ(jp *= GF7(2), jp4);\n}\n\nTEST_F(JacobianPointTest, ScalarMulOperator) {\n  std::vector<test::AffinePoint> points;\n  for (size_t i = 0; i < 7; ++i) {\n    points.push_back((GF7(i) * test::JacobianPoint::Generator()).ToAffine());\n  }\n\n  EXPECT_THAT(\n      points,\n      testing::UnorderedElementsAreArray(std::vector<test::AffinePoint>{\n          test::AffinePoint(GF7(0), GF7(0)), test::AffinePoint(GF7(3), GF7(2)),\n          test::AffinePoint(GF7(5), GF7(2)), test::AffinePoint(GF7(6), GF7(2)),\n          test::AffinePoint(GF7(3), GF7(5)), test::AffinePoint(GF7(5), GF7(5)),\n          test::AffinePoint(GF7(6), GF7(5))}));\n}\n\nTEST_F(JacobianPointTest, ToAffine) {\n  EXPECT_EQ(test::JacobianPoint(GF7(1), GF7(2), GF7(0)).ToAffine(),\n            test::AffinePoint::Zero());\n  EXPECT_EQ(test::JacobianPoint(GF7(1), GF7(2), GF7(1)).ToAffine(),\n            test::AffinePoint(GF7(1), GF7(2)));\n  EXPECT_EQ(test::JacobianPoint(GF7(1), GF7(2), GF7(3)).ToAffine(),\n            test::AffinePoint(GF7(4), GF7(5)));\n}\n\nTEST_F(JacobianPointTest, ToProjective) {\n  EXPECT_EQ(test::JacobianPoint(GF7(1), GF7(2), GF7(0)).ToProjective(),\n            test::ProjectivePoint::Zero());\n  EXPECT_EQ(test::JacobianPoint(GF7(1), GF7(2), GF7(3)).ToProjective(),\n            test::ProjectivePoint(GF7(3), GF7(2), GF7(6)));\n}\n\nTEST_F(JacobianPointTest, ToXYZZ) {\n  EXPECT_EQ(test::JacobianPoint(GF7(1), GF7(2), GF7(0)).ToXYZZ(),\n            test::PointXYZZ::Zero());\n  EXPECT_EQ(test::JacobianPoint(GF7(1), GF7(2), GF7(3)).ToXYZZ(),\n            test::PointXYZZ(GF7(1), GF7(2), GF7(2), GF7(6)));\n}\n\n#if defined(TACHYON_HAS_OPENMP)\nTEST_F(JacobianPointTest, BatchNormalize) {\n  size_t size = size_t{1} << (static_cast<size_t>(omp_get_max_threads()) /\n                              GF7::kParallelBatchInverseDivisorThreshold);\n  for (size_t i = 0; i < 1; ++i) {\n    // NOTE(chokobole): if i == 0 runs in parallel, otherwise runs in serial.\n    std::vector<test::JacobianPoint> jacobian_points =\n        CreatePseudoRandomPoints<test::JacobianPoint>(size - i);\n\n    std::vector<test::AffinePoint> affine_points;\n    affine_points.resize(jacobian_points.size() - 1);\n    ASSERT_FALSE(\n        test::JacobianPoint::BatchNormalize(jacobian_points, &affine_points));\n\n    affine_points.resize(jacobian_points.size());\n    ASSERT_TRUE(\n        test::JacobianPoint::BatchNormalize(jacobian_points, &affine_points));\n\n    std::vector<test::AffinePoint> expected_affine_points = base::Map(\n        jacobian_points,\n        [](const test::JacobianPoint& point) { return point.ToAffine(); });\n    EXPECT_EQ(affine_points, expected_affine_points);\n  }\n}\n#endif  // defined(TACHYON_HAS_OPENMP)\n\nTEST_F(JacobianPointTest, BatchNormalizeSerial) {\n  std::vector<test::JacobianPoint> jacobian_points = {\n      test::JacobianPoint(GF7(1), GF7(2), GF7(0)),\n      test::JacobianPoint(GF7(1), GF7(2), GF7(1)),\n      test::JacobianPoint(GF7(1), GF7(2), GF7(3))};\n\n  std::vector<test::AffinePoint> affine_points;\n  affine_points.resize(2);\n  ASSERT_FALSE(test::JacobianPoint::BatchNormalizeSerial(jacobian_points,\n                                                         &affine_points));\n\n  affine_points.resize(3);\n  ASSERT_TRUE(test::JacobianPoint::BatchNormalizeSerial(jacobian_points,\n                                                        &affine_points));\n\n  std::vector<test::AffinePoint> expected_affine_points = {\n      test::AffinePoint::Zero(), test::AffinePoint(GF7(1), GF7(2)),\n      test::AffinePoint(GF7(4), GF7(5))};\n  EXPECT_EQ(affine_points, expected_affine_points);\n}\n\nTEST_F(JacobianPointTest, IsOnCurve) {\n  test::JacobianPoint invalid_point(GF7(1), GF7(2), GF7(1));\n  EXPECT_FALSE(invalid_point.IsOnCurve());\n  test::JacobianPoint valid_point(GF7(3), GF7(2), GF7(1));\n  EXPECT_TRUE(valid_point.IsOnCurve());\n  valid_point = test::JacobianPoint(GF7(3), GF7(5), GF7(1));\n  EXPECT_TRUE(valid_point.IsOnCurve());\n}\n\nTEST_F(JacobianPointTest, CreateFromX) {\n  {\n    std::optional<test::JacobianPoint> p =\n        test::JacobianPoint::CreateFromX(GF7(3), /*pick_odd=*/true);\n    ASSERT_TRUE(p.has_value());\n    EXPECT_EQ(p->y(), GF7(5));\n  }\n  {\n    std::optional<test::JacobianPoint> p =\n        test::JacobianPoint::CreateFromX(GF7(3), /*pick_odd=*/false);\n    ASSERT_TRUE(p.has_value());\n    EXPECT_EQ(p->y(), GF7(2));\n  }\n  {\n    std::optional<test::JacobianPoint> p =\n        test::JacobianPoint::CreateFromX(GF7(1), /*pick_odd=*/false);\n    ASSERT_FALSE(p.has_value());\n  }\n}\n\nTEST_F(JacobianPointTest, Copyable) {\n  test::JacobianPoint expected = test::JacobianPoint::Random();\n\n  base::Uint8VectorBuffer write_buf;\n  ASSERT_TRUE(write_buf.Grow(base::EstimateSize(expected)));\n  ASSERT_TRUE(write_buf.Write(expected));\n  ASSERT_TRUE(write_buf.Done());\n\n  write_buf.set_buffer_offset(0);\n\n  test::JacobianPoint value;\n  ASSERT_TRUE(write_buf.Read(&value));\n  EXPECT_EQ(expected, value);\n}\n\nTEST_F(JacobianPointTest, JsonValueConverter) {\n  test::JacobianPoint expected_point(GF7(1), GF7(2), GF7(3));\n  std::string expected_json = R\"({\"x\":1,\"y\":2,\"z\":3})\";\n\n  test::JacobianPoint p;\n  std::string error;\n  ASSERT_TRUE(base::ParseJson(expected_json, &p, &error));\n  ASSERT_TRUE(error.empty());\n  EXPECT_EQ(p, expected_point);\n\n  std::string json = base::WriteToJson(p);\n  EXPECT_EQ(json, expected_json);\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/short_weierstrass/kernels/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"elliptic_curve_ops\",\n    hdrs = [\"elliptic_curve_ops.cu.h\"],\n    deps = [\"//tachyon/math/elliptic_curves/short_weierstrass:points\"],\n)\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/short_weierstrass/kernels/elliptic_curve_ops.cu.h",
    "content": "#ifndef TACHYON_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_KERNELS_ELLIPTIC_CURVE_OPS_CU_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_KERNELS_ELLIPTIC_CURVE_OPS_CU_H_\n\n#include <stddef.h>\n\n#include \"third_party/gpus/cuda/include/cuda_runtime.h\"\n\n#include \"tachyon/math/elliptic_curves/short_weierstrass/affine_point.h\"\n#include \"tachyon/math/elliptic_curves/short_weierstrass/jacobian_point.h\"\n#include \"tachyon/math/elliptic_curves/short_weierstrass/point_xyzz.h\"\n#include \"tachyon/math/elliptic_curves/short_weierstrass/projective_point.h\"\n\nnamespace tachyon::math::kernels {\n\n#define DEFINE_FIELD_OP(method, operator, src_type, dst_type)                  \\\n  template <typename Config>                                                   \\\n  __global__ void method(const src_type<Config>* x, const src_type<Config>* y, \\\n                         dst_type<Config>* result, unsigned int count) {       \\\n    unsigned int gid = blockIdx.x * blockDim.x + threadIdx.x;                  \\\n    if (gid >= count) return;                                                  \\\n    result[gid] = x[gid] operator y[gid];                                      \\\n  }\n\nDEFINE_FIELD_OP(Add, +, AffinePoint, JacobianPoint)\nDEFINE_FIELD_OP(Add, +, ProjectivePoint, ProjectivePoint)\nDEFINE_FIELD_OP(Add, +, JacobianPoint, JacobianPoint)\nDEFINE_FIELD_OP(Add, +, PointXYZZ, PointXYZZ)\n\n#undef DEFINE_FIELD_OP\n\n#define DEFINE_COMPARISON_OP(method, operator, type)                   \\\n  template <typename Config>                                           \\\n  __global__ void method(const type<Config>* x, const type<Config>* y, \\\n                         bool* result, unsigned int count) {           \\\n    unsigned int gid = blockIdx.x * blockDim.x + threadIdx.x;          \\\n    if (gid >= count) return;                                          \\\n    result[gid] = x[gid] operator y[gid];                              \\\n  }\n\nDEFINE_COMPARISON_OP(Eq, ==, AffinePoint)\nDEFINE_COMPARISON_OP(Ne, !=, AffinePoint)\nDEFINE_COMPARISON_OP(Eq, ==, ProjectivePoint)\nDEFINE_COMPARISON_OP(Ne, !=, ProjectivePoint)\nDEFINE_COMPARISON_OP(Eq, ==, JacobianPoint)\nDEFINE_COMPARISON_OP(Ne, !=, JacobianPoint)\nDEFINE_COMPARISON_OP(Eq, ==, PointXYZZ)\nDEFINE_COMPARISON_OP(Ne, !=, PointXYZZ)\n\n#undef DEFINE_COMPARISON_OP\n\n#define DEFINE_DOUBLE_OP(src_type, dst_type)                                  \\\n  template <typename Config>                                                  \\\n  __global__ void Double(const src_type<Config>* x, dst_type<Config>* result, \\\n                         unsigned int count) {                                \\\n    unsigned int gid = blockIdx.x * blockDim.x + threadIdx.x;                 \\\n    if (gid >= count) return;                                                 \\\n    result[gid] = x[gid].Double();                                            \\\n  }\n\nDEFINE_DOUBLE_OP(AffinePoint, JacobianPoint)\nDEFINE_DOUBLE_OP(JacobianPoint, JacobianPoint)\nDEFINE_DOUBLE_OP(ProjectivePoint, ProjectivePoint)\nDEFINE_DOUBLE_OP(PointXYZZ, PointXYZZ)\n\n#undef DEFINE_DOUBLE_OP\n\n#define DEFINE_NEGATE_OP(src_type, dst_type)                                  \\\n  template <typename Config>                                                  \\\n  __global__ void Negate(const src_type<Config>* x, dst_type<Config>* result, \\\n                         unsigned int count) {                                \\\n    unsigned int gid = blockIdx.x * blockDim.x + threadIdx.x;                 \\\n    if (gid >= count) return;                                                 \\\n    result[gid] = -x[gid];                                                    \\\n  }\n\nDEFINE_NEGATE_OP(AffinePoint, AffinePoint)\nDEFINE_NEGATE_OP(JacobianPoint, JacobianPoint)\nDEFINE_NEGATE_OP(ProjectivePoint, ProjectivePoint)\nDEFINE_NEGATE_OP(PointXYZZ, PointXYZZ)\n\n#undef DEFINE_NEGATE_OP\n\n}  // namespace tachyon::math::kernels\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_KERNELS_ELLIPTIC_CURVE_OPS_CU_H_\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/short_weierstrass/non_affine_point_correctness_gpu_test.cc",
    "content": "#include \"absl/strings/substitute.h\"\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/device/gpu/gpu_memory.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/g1_gpu.h\"\n#include \"tachyon/math/elliptic_curves/short_weierstrass/kernels/elliptic_curve_ops.cu.h\"\n#include \"tachyon/math/geometry/point_conversions.h\"\n#include \"tachyon/math/test/launch_op_macros.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\nconstexpr size_t kThreadNum = 32;\n\n#define DEFINE_LAUNCH_FIELD_BINARY_OP(method)                              \\\n  DEFINE_LAUNCH_BINARY_OP(kThreadNum, method, bn254::G1ProjectivePointGpu, \\\n                          bn254::G1ProjectivePointGpu)                     \\\n  DEFINE_LAUNCH_BINARY_OP(kThreadNum, method, bn254::G1JacobianPointGpu,   \\\n                          bn254::G1JacobianPointGpu)                       \\\n  DEFINE_LAUNCH_BINARY_OP(kThreadNum, method, bn254::G1PointXYZZGpu,       \\\n                          bn254::G1PointXYZZGpu)\n\nDEFINE_LAUNCH_FIELD_BINARY_OP(Add)\n\n#undef DEFINE_LAUNCH_FIELD_BINARY_OP\n\n#define DEFINE_LAUNCH_FIELD_UNARY_OP(method)                              \\\n  DEFINE_LAUNCH_UNARY_OP(kThreadNum, method, bn254::G1ProjectivePointGpu, \\\n                         bn254::G1ProjectivePointGpu)                     \\\n  DEFINE_LAUNCH_UNARY_OP(kThreadNum, method, bn254::G1JacobianPointGpu,   \\\n                         bn254::G1JacobianPointGpu)                       \\\n  DEFINE_LAUNCH_UNARY_OP(kThreadNum, method, bn254::G1PointXYZZGpu,       \\\n                         bn254::G1PointXYZZGpu)\n\nDEFINE_LAUNCH_FIELD_UNARY_OP(Double)\nDEFINE_LAUNCH_FIELD_UNARY_OP(Negate)\n\n#undef DEFINE_LAUNCH_FIELD_UNARY_OP\n\n#define DEFINE_LAUNCH_COMPARISON_OP(method)                                    \\\n  DEFINE_LAUNCH_BINARY_OP(kThreadNum, method, bn254::G1ProjectivePointGpu,     \\\n                          bool)                                                \\\n  DEFINE_LAUNCH_BINARY_OP(kThreadNum, method, bn254::G1JacobianPointGpu, bool) \\\n  DEFINE_LAUNCH_BINARY_OP(kThreadNum, method, bn254::G1PointXYZZGpu, bool)\n\nDEFINE_LAUNCH_COMPARISON_OP(Eq)\nDEFINE_LAUNCH_COMPARISON_OP(Ne)\n\n#undef DEFINE_LAUNCH_COMPARISON_OP\n\nusing namespace device;\n\ntemplate <typename Point>\nclass PointCorrectnessGpuTest : public testing::Test {\n public:\n  using Actual = typename Point::Actual;\n  using Expected = typename Point::Expected;\n\n  // Runs tests with |N| data.\n  constexpr static size_t N = kThreadNum * 2;\n\n  static void SetUpTestSuite() {\n    GPU_MUST_SUCCEED(gpuDeviceReset(), \"\");\n    xs_ = gpu::GpuMemory<Actual>::MallocManaged(N);\n    ys_ = gpu::GpuMemory<Actual>::MallocManaged(N);\n    results_ = gpu::GpuMemory<Actual>::MallocManaged(N);\n    bool_results_ = gpu::GpuMemory<bool>::MallocManaged(N);\n\n    Expected::Curve::Init();\n    Actual::Curve::Init();\n\n    x_cpus_.reserve(N);\n    y_cpus_.reserve(N);\n\n    for (size_t i = 0; i < N; ++i) {\n      Expected x_cpu = Expected::Random();\n      Expected y_cpu = Expected::Random();\n\n      xs_[i] = ConvertPoint<Actual>(x_cpu);\n      ys_[i] = ConvertPoint<Actual>(y_cpu);\n\n      x_cpus_.push_back(std::move(x_cpu));\n      y_cpus_.push_back(std::move(y_cpu));\n    }\n  }\n\n  static void TearDownTestSuite() {\n    xs_.reset();\n    ys_.reset();\n    results_.reset();\n    bool_results_.reset();\n\n    GPU_MUST_SUCCEED(gpuDeviceReset(), \"\");\n\n    x_cpus_.clear();\n    y_cpus_.clear();\n  }\n\n  void SetUp() override {\n    CHECK(results_.Memset());\n    CHECK(bool_results_.Memset());\n  }\n\n protected:\n  static gpu::GpuMemory<Actual> xs_;\n  static gpu::GpuMemory<Actual> ys_;\n  static gpu::GpuMemory<Actual> results_;\n  static gpu::GpuMemory<bool> bool_results_;\n\n  static std::vector<Expected> x_cpus_;\n  static std::vector<Expected> y_cpus_;\n};\n\ntemplate <typename Point>\ngpu::GpuMemory<typename PointCorrectnessGpuTest<Point>::Actual>\n    PointCorrectnessGpuTest<Point>::xs_;\n\ntemplate <typename Point>\ngpu::GpuMemory<typename PointCorrectnessGpuTest<Point>::Actual>\n    PointCorrectnessGpuTest<Point>::ys_;\n\ntemplate <typename Point>\ngpu::GpuMemory<typename PointCorrectnessGpuTest<Point>::Actual>\n    PointCorrectnessGpuTest<Point>::results_;\n\ntemplate <typename Point>\ngpu::GpuMemory<bool> PointCorrectnessGpuTest<Point>::bool_results_;\n\ntemplate <typename Point>\nstd::vector<typename PointCorrectnessGpuTest<Point>::Expected>\n    PointCorrectnessGpuTest<Point>::x_cpus_;\n\ntemplate <typename Point>\nstd::vector<typename PointCorrectnessGpuTest<Point>::Expected>\n    PointCorrectnessGpuTest<Point>::y_cpus_;\n\n}  // namespace\n\nstruct ProjectivePointTypes {\n  using Actual = bn254::G1ProjectivePointGpu;\n  using Expected = bn254::G1ProjectivePoint;\n};\n\nstruct JacobianPointTypes {\n  using Actual = bn254::G1JacobianPointGpu;\n  using Expected = bn254::G1JacobianPoint;\n};\n\nstruct PointXYZZTypes {\n  using Actual = bn254::G1PointXYZZGpu;\n  using Expected = bn254::G1PointXYZZ;\n};\n\nusing PointTypes =\n    testing::Types<ProjectivePointTypes, JacobianPointTypes, PointXYZZTypes>;\nTYPED_TEST_SUITE(PointCorrectnessGpuTest, PointTypes);\n\nTYPED_TEST(PointCorrectnessGpuTest, Add) {\n  using Expected = typename TypeParam::Expected;\n  size_t N = PointCorrectnessGpuTest<TypeParam>::N;\n\n  GPU_MUST_SUCCEED(\n      LaunchAdd(this->xs_.data(), this->ys_.data(), this->results_.data(), N),\n      \"\");\n  for (size_t i = 0; i < N; ++i) {\n    SCOPED_TRACE(absl::Substitute(\"a: $0, b: $1\", this->xs_[i].ToString(),\n                                  this->ys_[i].ToString()));\n    auto result = ConvertPoint<Expected>(this->results_[i]);\n    ASSERT_EQ(result, this->x_cpus_[i] + this->y_cpus_[i]);\n  }\n}\n\nTYPED_TEST(PointCorrectnessGpuTest, Double) {\n  using Expected = typename TypeParam::Expected;\n  size_t N = PointCorrectnessGpuTest<TypeParam>::N;\n\n  GPU_MUST_SUCCEED(LaunchDouble(this->xs_.data(), this->results_.data(), N),\n                   \"\");\n  for (size_t i = 0; i < N; ++i) {\n    SCOPED_TRACE(absl::Substitute(\"a: $0\", this->xs_[i].ToString()));\n    auto result = ConvertPoint<Expected>(this->results_[i]);\n    ASSERT_EQ(result, this->x_cpus_[i].Double());\n  }\n}\n\nTYPED_TEST(PointCorrectnessGpuTest, Negate) {\n  using Expected = typename TypeParam::Expected;\n  size_t N = PointCorrectnessGpuTest<TypeParam>::N;\n\n  GPU_MUST_SUCCEED(LaunchNegate(this->xs_.data(), this->results_.data(), N),\n                   \"\");\n  for (size_t i = 0; i < N; ++i) {\n    SCOPED_TRACE(absl::Substitute(\"a: $0\", this->xs_[i].ToString()));\n    auto result = ConvertPoint<Expected>(this->results_[i]);\n    ASSERT_EQ(result, -this->x_cpus_[i]);\n  }\n}\n\nTYPED_TEST(PointCorrectnessGpuTest, Eq) {\n  size_t N = PointCorrectnessGpuTest<TypeParam>::N;\n\n  GPU_MUST_SUCCEED(LaunchEq(this->xs_.data(), this->xs_.data(),\n                            this->bool_results_.data(), N),\n                   \"\");\n  for (size_t i = 0; i < N; ++i) {\n    SCOPED_TRACE(absl::Substitute(\"a: $0, b: $1\", this->xs_[i].ToString(),\n                                  this->xs_[i].ToString()));\n    ASSERT_TRUE(this->bool_results_[i]);\n  }\n}\n\nTYPED_TEST(PointCorrectnessGpuTest, Ne) {\n  size_t N = PointCorrectnessGpuTest<TypeParam>::N;\n\n  GPU_MUST_SUCCEED(LaunchNe(this->xs_.data(), this->ys_.data(),\n                            this->bool_results_.data(), N),\n                   \"\");\n  for (size_t i = 0; i < N; ++i) {\n    SCOPED_TRACE(absl::Substitute(\"a: $0, b: $1\", this->xs_[i].ToString(),\n                                  this->ys_[i].ToString()));\n    ASSERT_TRUE(this->bool_results_[i]);\n  }\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/short_weierstrass/point_xyzz.h",
    "content": "#ifndef TACHYON_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_POINT_XYZZ_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_POINT_XYZZ_H_\n\n#include <optional>\n#include <string>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/json/json.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/math/base/groups.h\"\n#include \"tachyon/math/geometry/affine_point.h\"\n#include \"tachyon/math/geometry/curve_type.h\"\n#include \"tachyon/math/geometry/jacobian_point.h\"\n#include \"tachyon/math/geometry/point4.h\"\n#include \"tachyon/math/geometry/point_xyzz.h\"\n#include \"tachyon/math/geometry/projective_point.h\"\n\nnamespace tachyon {\nnamespace math {\n\ntemplate <typename _Curve>\nclass PointXYZZ<_Curve,\n                std::enable_if_t<_Curve::kType == CurveType::kShortWeierstrass>>\n    final : public AdditiveGroup<PointXYZZ<_Curve>> {\n public:\n  constexpr static bool kNegationIsCheap = true;\n\n  using Curve = _Curve;\n  using BaseField = typename Curve::BaseField;\n  using ScalarField = typename Curve::ScalarField;\n\n  constexpr PointXYZZ()\n      : PointXYZZ(BaseField::One(), BaseField::One(), BaseField::Zero(),\n                  BaseField::Zero()) {}\n  explicit constexpr PointXYZZ(const Point4<BaseField>& point)\n      : PointXYZZ(point.x, point.y, point.z, point.w) {}\n  explicit constexpr PointXYZZ(Point4<BaseField>&& point)\n      : PointXYZZ(std::move(point.x), std::move(point.y), std::move(point.z),\n                  std::move(point.w)) {}\n  constexpr PointXYZZ(const BaseField& x, const BaseField& y,\n                      const BaseField& zz, const BaseField& zzz)\n      : x_(x), y_(y), zz_(zz), zzz_(zzz) {}\n  constexpr PointXYZZ(BaseField&& x, BaseField&& y, BaseField&& zz,\n                      BaseField&& zzz)\n      : x_(std::move(x)),\n        y_(std::move(y)),\n        zz_(std::move(zz)),\n        zzz_(std::move(zzz)) {}\n\n  constexpr static PointXYZZ CreateChecked(const BaseField& x,\n                                           const BaseField& y,\n                                           const BaseField& zz,\n                                           const BaseField& zzz) {\n    PointXYZZ ret = {x, y, zz, zzz};\n    CHECK(ret.IsOnCurve());\n    return ret;\n  }\n\n  constexpr static PointXYZZ CreateChecked(BaseField&& x, BaseField&& y,\n                                           BaseField&& zz, BaseField&& zzz) {\n    PointXYZZ ret = {std::move(x), std::move(y), std::move(zz), std::move(zzz)};\n    CHECK(ret.IsOnCurve());\n    return ret;\n  }\n\n  constexpr static std::optional<PointXYZZ> CreateFromX(const BaseField& x,\n                                                        bool pick_odd) {\n    PointXYZZ point{};\n    if (!Curve::GetPointFromX(x, pick_odd, &point)) return std::nullopt;\n    return point;\n  }\n\n  constexpr static PointXYZZ Zero() { return PointXYZZ(); }\n\n  constexpr static PointXYZZ One() { return Generator(); }\n\n  constexpr static PointXYZZ Generator() {\n    return {Curve::Config::kGenerator.x, Curve::Config::kGenerator.y,\n            BaseField::One(), BaseField::One()};\n  }\n\n  constexpr static PointXYZZ FromAffine(const AffinePoint<Curve>& point) {\n    return point.ToXYZZ();\n  }\n\n  constexpr static PointXYZZ FromProjective(\n      const ProjectivePoint<Curve>& point) {\n    return point.ToXYZZ();\n  }\n\n  constexpr static PointXYZZ FromJacobian(const JacobianPoint<Curve>& point) {\n    return point.ToXYZZ();\n  }\n\n  constexpr static PointXYZZ Random() {\n    return FromJacobian(JacobianPoint<Curve>::Random());\n  }\n\n  constexpr static PointXYZZ Endomorphism(const PointXYZZ& point) {\n    return PointXYZZ(point.x_ * Curve::Config::kEndomorphismCoefficient,\n                     point.y_, point.zz_, point.zzz_);\n  }\n\n  template <typename PointXYZZContainer, typename AffineContainer>\n  [[nodiscard]] CONSTEXPR_IF_NOT_OPENMP static bool BatchNormalize(\n      const PointXYZZContainer& point_xyzzs, AffineContainer* affine_points) {\n    size_t size = std::size(point_xyzzs);\n    if (size != std::size(*affine_points)) {\n      LOG(ERROR) << \"Size of |point_xyzzs| and |affine_points| do not match\";\n      return false;\n    }\n#if defined(TACHYON_HAS_OPENMP)\n    size_t thread_nums = static_cast<size_t>(omp_get_max_threads());\n    if (size >=\n        size_t{1} << (thread_nums /\n                      ScalarField::kParallelBatchInverseDivisorThreshold)) {\n      base::Parallelize(size, [&point_xyzzs, affine_points](size_t len,\n                                                            size_t chunk_offset,\n                                                            size_t chunk_size) {\n        size_t start = chunk_offset * chunk_size;\n        absl::Span<AffinePoint<Curve>> affine_points_chunk(\n            &(*affine_points)[start], len);\n        absl::Span<const PointXYZZ> point_xyzzs_chunk(&point_xyzzs[start], len);\n        CHECK(BatchNormalizeSerial(point_xyzzs_chunk, &affine_points_chunk));\n      });\n      return true;\n    }\n#endif\n    return BatchNormalizeSerial(point_xyzzs, affine_points);\n  }\n\n  template <typename PointXYZZContainer, typename AffineContainer>\n  [[nodiscard]] constexpr static bool BatchNormalizeSerial(\n      const PointXYZZContainer& point_xyzzs, AffineContainer* affine_points) {\n    size_t size = std::size(point_xyzzs);\n    if (size != std::size(*affine_points)) {\n      LOG(ERROR) << \"Size of |point_xyzzs| and |affine_points| do not match\";\n      return false;\n    }\n    std::vector<BaseField> zzz_inverses = base::Map(\n        point_xyzzs, [](const PointXYZZ& point) { return point.zzz_; });\n    if (!BaseField::BatchInverseInPlaceSerial(zzz_inverses)) return false;\n    for (size_t i = 0; i < size; ++i) {\n      const PointXYZZ& point_xyzz = point_xyzzs[i];\n      if (point_xyzz.zz_.IsZero()) {\n        (*affine_points)[i] = AffinePoint<Curve>::Zero();\n      } else if (point_xyzz.zz_.IsOne()) {\n        (*affine_points)[i] = {point_xyzz.x_, point_xyzz.y_};\n      } else {\n        const BaseField& z_inv_cubic = zzz_inverses[i];\n        BaseField z_inv_square = z_inv_cubic * point_xyzz.zz_;\n        z_inv_square.SquareInPlace();\n        (*affine_points)[i] = {point_xyzz.x_ * z_inv_square,\n                               point_xyzz.y_ * z_inv_cubic};\n      }\n    }\n    return true;\n  }\n\n  constexpr const BaseField& x() const { return x_; }\n  constexpr const BaseField& y() const { return y_; }\n  constexpr const BaseField& zz() const { return zz_; }\n  constexpr const BaseField& zzz() const { return zzz_; }\n\n  constexpr bool operator==(const PointXYZZ& other) const {\n    if (IsZero()) {\n      return other.IsZero();\n    }\n\n    if (other.IsZero()) {\n      return false;\n    }\n\n    // The points (X, Y, ZZ, ZZZ) and (X', Y', ZZ', ZZZ')\n    // are equal when (X * ZZ') = (X' * ZZ)\n    // and (Y * Z'³) = (Y' * Z³).\n    if (x_ * other.zz_ != other.x_ * zz_) {\n      return false;\n    } else {\n      return y_ * other.zzz_ == other.y_ * zzz_;\n    }\n  }\n\n  constexpr bool operator!=(const PointXYZZ& other) const {\n    return !operator==(other);\n  }\n\n  constexpr bool IsZero() const { return zz_.IsZero(); }\n\n  constexpr bool IsOnCurve() { return Curve::IsOnCurve(*this); }\n\n  // The xyzz point X, Y, ZZ, ZZZ is represented in the affine\n  // coordinates as X/ZZ, Y/ZZZ.\n  constexpr AffinePoint<Curve> ToAffine() const {\n    if (IsZero()) {\n      return AffinePoint<Curve>::Zero();\n    } else if (zz_.IsOne()) {\n      return {x_, y_};\n    } else {\n      // NOTE(ashjeong): if |zzz_| is 0, |IsZero()| will also evaluate to true,\n      // and this block will not be executed\n      BaseField z_inv_cubic = *zzz_.Inverse();\n      BaseField z_inv_square = z_inv_cubic * zz_;\n      z_inv_square.SquareInPlace();\n      return {x_ * z_inv_square, y_ * z_inv_cubic};\n    }\n  }\n\n  // The xyzz point X, Y, ZZ, ZZZ is represented in the projective\n  // coordinates as X*ZZZ, Y*ZZ, ZZ*ZZZ.\n  constexpr ProjectivePoint<Curve> ToProjective() const {\n    if (IsZero()) {\n      return ProjectivePoint<Curve>::Zero();\n    } else if (zz_.IsOne()) {\n      return {x_, y_, BaseField::One()};\n    } else {\n      return {x_ * zzz_, y_ * zz_, zz_ * zzz_};\n    }\n  }\n\n  // The xyzz point X, Y, ZZ, ZZZ is represented in the jacobian\n  // coordinates as X*ZZZ²*ZZ, Y*ZZ³*ZZZ², ZZZ*ZZ.\n  constexpr JacobianPoint<Curve> ToJacobian() const {\n    if (IsZero()) {\n      return JacobianPoint<Curve>::Zero();\n    } else if (zz_.IsOne()) {\n      return {x_, y_, BaseField::One()};\n    } else {\n      BaseField z = zz_ * zzz_;\n      return {x_ * zzz_ * z, y_ * zz_ * z.Square(), z};\n    }\n  }\n\n  std::string ToString() const {\n    return absl::Substitute(\"($0, $1, $2, $3)\", x_.ToString(), y_.ToString(),\n                            zz_.ToString(), zzz_.ToString());\n  }\n\n  std::string ToHexString(bool pad_zero = false) const {\n    return absl::Substitute(\"($0, $1, $2, $3)\", x_.ToHexString(pad_zero),\n                            y_.ToHexString(pad_zero), zz_.ToHexString(pad_zero),\n                            zzz_.ToHexString(pad_zero));\n  }\n\n  // AdditiveSemigroup methods\n  constexpr PointXYZZ Add(const PointXYZZ& other) const;\n  constexpr PointXYZZ& AddInPlace(const PointXYZZ& other);\n  constexpr PointXYZZ Add(const AffinePoint<Curve>& other) const;\n  constexpr PointXYZZ& AddInPlace(const AffinePoint<Curve>& other);\n  constexpr PointXYZZ DoubleImpl() const;\n  constexpr PointXYZZ& DoubleImplInPlace();\n\n  // AdditiveGroup methods\n  constexpr PointXYZZ Negate() const { return {x_, -y_, zz_, zzz_}; }\n\n  constexpr PointXYZZ& NegateInPlace() {\n    y_.NegateInPlace();\n    return *this;\n  }\n\n  constexpr PointXYZZ operator*(const ScalarField& v) const {\n    return this->ScalarMul(v);\n  }\n  constexpr PointXYZZ& operator*=(const ScalarField& v) {\n    return *this = operator*(v);\n  }\n\n private:\n  constexpr static void DoAdd(const PointXYZZ& a, const PointXYZZ& b,\n                              PointXYZZ& c);\n  CONSTEXPR_IF_NOT_OPENMP static void DoAdd(const PointXYZZ& a,\n                                            const AffinePoint<Curve>& b,\n                                            PointXYZZ& c);\n  constexpr static void DoDoubleImpl(const PointXYZZ& a, PointXYZZ& b);\n\n  BaseField x_;\n  BaseField y_;\n  BaseField zz_;\n  BaseField zzz_;\n};\n\n}  // namespace math\n\nnamespace base {\n\ntemplate <typename Curve>\nclass Copyable<math::PointXYZZ<\n    Curve,\n    std::enable_if_t<Curve::kType == math::CurveType::kShortWeierstrass>>> {\n public:\n  static bool WriteTo(const math::PointXYZZ<Curve>& point, Buffer* buffer) {\n    return buffer->WriteMany(point.x(), point.y(), point.zz(), point.zzz());\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer,\n                       math::PointXYZZ<Curve>* point) {\n    using BaseField = typename math::PointXYZZ<Curve>::BaseField;\n    BaseField x, y, zz, zzz;\n    if (!buffer.ReadMany(&x, &y, &zz, &zzz)) return false;\n\n    *point = math::PointXYZZ<Curve>(std::move(x), std::move(y), std::move(zz),\n                                    std::move(zzz));\n    return true;\n  }\n\n  static size_t EstimateSize(const math::PointXYZZ<Curve>& point) {\n    return base::EstimateSize(point.x(), point.y(), point.zz(), point.zzz());\n  }\n};\n\ntemplate <typename Curve>\nclass RapidJsonValueConverter<math::PointXYZZ<\n    Curve,\n    std::enable_if_t<Curve::kType == math::CurveType::kShortWeierstrass>>> {\n public:\n  using Field = typename math::PointXYZZ<Curve>::BaseField;\n\n  template <typename Allocator>\n  static rapidjson::Value From(const math::PointXYZZ<Curve>& value,\n                               Allocator& allocator) {\n    rapidjson::Value object(rapidjson::kObjectType);\n    AddJsonElement(object, \"x\", value.x(), allocator);\n    AddJsonElement(object, \"y\", value.y(), allocator);\n    AddJsonElement(object, \"zz\", value.zz(), allocator);\n    AddJsonElement(object, \"zzz\", value.zzz(), allocator);\n    return object;\n  }\n\n  static bool To(const rapidjson::Value& json_value, std::string_view key,\n                 math::PointXYZZ<Curve>* value, std::string* error) {\n    Field x;\n    Field y;\n    Field zz;\n    Field zzz;\n    if (!ParseJsonElement(json_value, \"x\", &x, error)) return false;\n    if (!ParseJsonElement(json_value, \"y\", &y, error)) return false;\n    if (!ParseJsonElement(json_value, \"zz\", &zz, error)) return false;\n    if (!ParseJsonElement(json_value, \"zzz\", &zzz, error)) return false;\n    *value = math::PointXYZZ<Curve>(std::move(x), std::move(y), std::move(zz),\n                                    std::move(zzz));\n    return true;\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#include \"tachyon/math/elliptic_curves/short_weierstrass/point_xyzz_impl.h\"\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_POINT_XYZZ_H_\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/short_weierstrass/point_xyzz_impl.h",
    "content": "#ifndef TACHYON_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_POINT_XYZZ_IMPL_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_POINT_XYZZ_IMPL_H_\n\n#include <utility>\n\n#include \"tachyon/math/elliptic_curves/short_weierstrass/point_xyzz.h\"\n\nnamespace tachyon::math {\n\n#define CLASS      \\\n  PointXYZZ<Curve, \\\n            std::enable_if_t<Curve::kType == CurveType::kShortWeierstrass>>\n\ntemplate <typename Curve>\nconstexpr CLASS CLASS::Add(const PointXYZZ& other) const {\n  if (IsZero()) {\n    return other;\n  }\n\n  if (other.IsZero()) {\n    return *this;\n  }\n\n  PointXYZZ ret;\n  DoAdd(*this, other, ret);\n  return ret;\n}\n\ntemplate <typename Curve>\nconstexpr CLASS& CLASS::AddInPlace(const PointXYZZ& other) {\n  if (IsZero()) {\n    return *this = other;\n  }\n\n  if (other.IsZero()) {\n    return *this;\n  }\n\n  DoAdd(*this, other, *this);\n  return *this;\n}\n\n// static\ntemplate <typename Curve>\nconstexpr void CLASS::DoAdd(const PointXYZZ& a, const PointXYZZ& b,\n                            PointXYZZ& c) {\n  // https://hyperelliptic.org/EFD/g1p/auto-shortw-xyzz.html#addition-add-2008-s\n  // U1 = X1 * ZZ2\n  BaseField u1 = a.x_ * b.zz_;\n\n  // S1 = Y1 * ZZZ2\n  BaseField s1 = a.y_ * b.zzz_;\n\n  // P = X2 * ZZ1 - U1\n  BaseField p = b.x_ * a.zz_;\n  p -= u1;\n\n  // R = Y2 * ZZZ1 - S1\n  BaseField r = b.y_ * a.zzz_;\n  r -= s1;\n\n  if (p.IsZero() && r.IsZero()) {\n    if (&a == &c) {\n      c.DoubleInPlace();\n    } else {\n      c = a.Double();\n    }\n    return;\n  }\n\n  // PP = P²\n  BaseField pp = p.Square();\n\n  // PPP = P * PP\n  BaseField ppp = p * pp;\n\n  // Q = U1 * PP\n  BaseField q = u1 * pp;\n\n  // X3 = R² - PPP - 2 * Q\n  c.x_ = r.Square();\n  c.x_ -= ppp;\n  c.x_ -= q.Double();\n\n  // Y3 = R * (Q - X3) - S1 * PPP\n  BaseField lefts[] = {std::move(r), -s1};\n  BaseField rights[] = {q - c.x_, ppp};\n  c.y_ = BaseField::SumOfProductsSerial(lefts, rights);\n\n  // ZZ3 = ZZ1 * ZZ2 * PP\n  c.zz_ = a.zz_ * b.zz_;\n  c.zz_ *= pp;\n\n  // ZZZ3 = ZZZ1 * ZZZ2 * PPP\n  c.zzz_ = a.zzz_ * b.zzz_;\n  c.zzz_ *= ppp;\n}\n\ntemplate <typename Curve>\nconstexpr CLASS CLASS::Add(const AffinePoint<Curve>& other) const {\n  if (IsZero()) {\n    return other.ToXYZZ();\n  }\n\n  if (other.IsZero()) {\n    return *this;\n  }\n\n  PointXYZZ ret;\n  DoAdd(*this, other, ret);\n  return ret;\n}\n\ntemplate <typename Curve>\nconstexpr CLASS& CLASS::AddInPlace(const AffinePoint<Curve>& other) {\n  if (IsZero()) {\n    return *this = other.ToXYZZ();\n  }\n\n  if (other.IsZero()) {\n    return *this;\n  }\n\n  DoAdd(*this, other, *this);\n  return *this;\n}\n\n// static\ntemplate <typename Curve>\nCONSTEXPR_IF_NOT_OPENMP void CLASS::DoAdd(const PointXYZZ& a,\n                                          const AffinePoint<Curve>& b,\n                                          PointXYZZ& c) {\n  // https://hyperelliptic.org/EFD/g1p/auto-shortw-xyzz.html#addition-madd-2008-s\n\n  // P = X2 * ZZ1 - X1\n  BaseField p = b.x() * a.zz_;\n  p -= a.x_;\n\n  // R = Y2 * ZZZ1 - Y1\n  BaseField r = b.y() * a.zzz_;\n  r -= a.y_;\n\n  if (p.IsZero() && r.IsZero()) {\n    if (&a == &c) {\n      c.DoubleInPlace();\n    } else {\n      c = a.Double();\n    }\n    return;\n  }\n\n  // PP = P²\n  BaseField pp = p.Square();\n\n  // PPP = P * PP\n  BaseField ppp = p * pp;\n\n  // Q = X1 * PP\n  BaseField q = a.x_ * pp;\n\n  // X3 = R² - PPP - 2 * Q\n  c.x_ = r.Square();\n  c.x_ -= ppp;\n  c.x_ -= q.Double();\n\n  // Y3 = R * (Q - X3) - Y1 * PPP\n  BaseField lefts[] = {std::move(r), -a.y_};\n  BaseField rights[] = {q - c.x_, ppp};\n  c.y_ = BaseField::SumOfProductsSerial(lefts, rights);\n\n  // ZZ3 = ZZ1 * PP\n  c.zz_ = a.zz_ * pp;\n\n  // ZZZ3 = ZZZ1 * PPP\n  c.zzz_ = a.zzz_ * ppp;\n}\n\ntemplate <typename Curve>\nconstexpr CLASS CLASS::DoubleImpl() const {\n  if (IsZero()) {\n    return PointXYZZ::Zero();\n  }\n\n  PointXYZZ ret;\n  DoDoubleImpl(*this, ret);\n  return ret;\n}\n\ntemplate <typename Curve>\nconstexpr CLASS& CLASS::DoubleImplInPlace() {\n  if (IsZero()) {\n    return *this;\n  }\n\n  DoDoubleImpl(*this, *this);\n  return *this;\n}\n\n// static\ntemplate <typename Curve>\nconstexpr void CLASS::DoDoubleImpl(const PointXYZZ& a, PointXYZZ& b) {\n  // https://hyperelliptic.org/EFD/g1p/auto-shortw-xyzz.html#doubling-dbl-2008-s-1\n  // U = 2 * Y1\n  BaseField u = a.y_.Double();\n\n  // V = U²\n  BaseField v = u.Square();\n\n  // W = U * V\n  BaseField w = u * v;\n\n  // S = X1 * V\n  BaseField s = a.x_ * v;\n\n  // M = 3 * X1² + a * ZZ1²\n  BaseField m = a.x_.Square();\n  m += m.Double();\n  if constexpr (!Curve::Config::kAIsZero) {\n    m += Curve::Config::MulByA(a.zz_.Square());\n  }\n\n  // X3 = M² - 2 * S\n  b.x_ = m.Square();\n  b.x_ -= s.Double();\n\n  // Y3 = M * (S - X3) - W * Y1\n  BaseField lefts[] = {std::move(m), -w};\n  BaseField rights[] = {s - b.x_, a.y_};\n  b.y_ = BaseField::SumOfProductsSerial(lefts, rights);\n\n  // ZZ3 = V * ZZ1\n  b.zz_ = v * a.zz_;\n\n  // ZZZ3 = W * ZZZ1\n  b.zzz_ = w * a.zzz_;\n}\n\n#undef CLASS\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_POINT_XYZZ_IMPL_H_\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/short_weierstrass/point_xyzz_unittest.cc",
    "content": "#include \"tachyon/math/elliptic_curves/short_weierstrass/point_xyzz.h\"\n\n#include <vector>\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/buffer/vector_buffer.h\"\n#include \"tachyon/math/elliptic_curves/short_weierstrass/affine_point.h\"\n#include \"tachyon/math/elliptic_curves/short_weierstrass/jacobian_point.h\"\n#include \"tachyon/math/elliptic_curves/short_weierstrass/projective_point.h\"\n#include \"tachyon/math/elliptic_curves/short_weierstrass/test/sw_curve_config.h\"\n#include \"tachyon/math/elliptic_curves/test/random.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\nclass PointXYZZTest : public testing::Test {\n public:\n  static void SetUpTestSuite() { test::G1Curve::Init(); }\n};\n\n}  // namespace\n\nTEST_F(PointXYZZTest, IsZero) {\n  EXPECT_TRUE(test::PointXYZZ(GF7(1), GF7(2), GF7(0), GF7(0)).IsZero());\n  EXPECT_FALSE(test::PointXYZZ(GF7(1), GF7(2), GF7(1), GF7(0)).IsZero());\n  EXPECT_TRUE(test::PointXYZZ(GF7(1), GF7(2), GF7(0), GF7(1)).IsZero());\n}\n\nTEST_F(PointXYZZTest, Generator) {\n  EXPECT_EQ(test::PointXYZZ::Generator(),\n            test::PointXYZZ(test::PointXYZZ::Curve::Config::kGenerator.x,\n                            test::PointXYZZ::Curve::Config::kGenerator.y,\n                            GF7::One(), GF7::One()));\n}\n\nTEST_F(PointXYZZTest, Random) {\n  bool success = false;\n  test::PointXYZZ r = test::PointXYZZ::Random();\n  for (size_t i = 0; i < 100; ++i) {\n    if (r != test::PointXYZZ::Random()) {\n      success = true;\n      break;\n    }\n  }\n  EXPECT_TRUE(success);\n}\n\nTEST_F(PointXYZZTest, EqualityOperators) {\n  {\n    SCOPED_TRACE(\"p.IsZero() && p2.IsZero()\");\n    test::PointXYZZ p(GF7(1), GF7(2), GF7(0), GF7(0));\n    test::PointXYZZ p2(GF7(3), GF7(4), GF7(0), GF7(0));\n    EXPECT_EQ(p, p2);\n    EXPECT_EQ(p2, p);\n  }\n\n  {\n    SCOPED_TRACE(\"!p.IsZero() && p2.IsZero()\");\n    test::PointXYZZ p(GF7(1), GF7(2), GF7(1), GF7(0));\n    test::PointXYZZ p2(GF7(3), GF7(4), GF7(0), GF7(0));\n    EXPECT_NE(p, p2);\n    EXPECT_NE(p2, p);\n  }\n\n  {\n    SCOPED_TRACE(\"other\");\n    test::PointXYZZ p(GF7(1), GF7(2), GF7(2), GF7(6));\n    test::PointXYZZ p2(GF7(1), GF7(2), GF7(2), GF7(6));\n    EXPECT_EQ(p, p2);\n    EXPECT_EQ(p2, p);\n  }\n}\n\nTEST_F(PointXYZZTest, AdditiveGroupOperators) {\n  test::PointXYZZ p =\n      test::PointXYZZ::CreateChecked(GF7(5), GF7(5), GF7(1), GF7(1));\n  test::PointXYZZ p2 =\n      test::PointXYZZ::CreateChecked(GF7(3), GF7(2), GF7(1), GF7(1));\n  test::PointXYZZ p3 =\n      test::PointXYZZ::CreateChecked(GF7(3), GF7(5), GF7(1), GF7(1));\n  test::PointXYZZ p4 =\n      test::PointXYZZ::CreateChecked(GF7(6), GF7(5), GF7(1), GF7(1));\n  test::AffinePoint ap = p.ToAffine();\n  test::AffinePoint ap2 = p2.ToAffine();\n\n  EXPECT_EQ(p + p2, p3);\n  EXPECT_EQ(p - p3, -p2);\n  EXPECT_EQ(p + p, p4);\n  EXPECT_EQ(p - p4, -p);\n\n  {\n    test::PointXYZZ p_tmp = p;\n    p_tmp += p2;\n    EXPECT_EQ(p_tmp, p3);\n    p_tmp -= p2;\n    EXPECT_EQ(p_tmp, p);\n  }\n\n  EXPECT_EQ(p + ap2, p3);\n  EXPECT_EQ(p + ap, p4);\n  EXPECT_EQ(p - p3, -p2);\n  EXPECT_EQ(p - p4, -p);\n\n  EXPECT_EQ(p.Double(), p4);\n  {\n    test::PointXYZZ p_tmp = p;\n    p_tmp.DoubleInPlace();\n    EXPECT_EQ(p_tmp, p4);\n  }\n\n  EXPECT_EQ(-p, test::PointXYZZ(GF7(5), GF7(2), GF7(1), GF7(1)));\n  {\n    test::PointXYZZ p_tmp = p;\n    p_tmp.NegateInPlace();\n    EXPECT_EQ(p_tmp, test::PointXYZZ(GF7(5), GF7(2), GF7(1), GF7(1)));\n  }\n\n  EXPECT_EQ(p * GF7(2), p4);\n  EXPECT_EQ(GF7(2) * p, p4);\n  EXPECT_EQ(p *= GF7(2), p4);\n}\n\nTEST_F(PointXYZZTest, ScalarMulOperator) {\n  std::vector<test::AffinePoint> points;\n  for (size_t i = 0; i < 7; ++i) {\n    points.push_back((GF7(i) * test::PointXYZZ::Generator()).ToAffine());\n  }\n\n  EXPECT_THAT(\n      points,\n      testing::UnorderedElementsAreArray(std::vector<test::AffinePoint>{\n          test::AffinePoint(GF7(0), GF7(0)), test::AffinePoint(GF7(3), GF7(2)),\n          test::AffinePoint(GF7(5), GF7(2)), test::AffinePoint(GF7(6), GF7(2)),\n          test::AffinePoint(GF7(3), GF7(5)), test::AffinePoint(GF7(5), GF7(5)),\n          test::AffinePoint(GF7(6), GF7(5))}));\n}\n\nTEST_F(PointXYZZTest, ToAffine) {\n  EXPECT_EQ(test::PointXYZZ(GF7(1), GF7(2), GF7(0), GF7(0)).ToAffine(),\n            test::AffinePoint::Zero());\n  EXPECT_EQ(test::PointXYZZ(GF7(1), GF7(2), GF7(1), GF7(1)).ToAffine(),\n            test::AffinePoint(GF7(1), GF7(2)));\n  EXPECT_EQ(test::PointXYZZ(GF7(1), GF7(2), GF7(2), GF7(6)).ToAffine(),\n            test::AffinePoint(GF7(4), GF7(5)));\n}\n\nTEST_F(PointXYZZTest, ToProjective) {\n  EXPECT_EQ(test::PointXYZZ(GF7(1), GF7(2), GF7(0), GF7(0)).ToProjective(),\n            test::ProjectivePoint::Zero());\n  EXPECT_EQ(test::PointXYZZ(GF7(1), GF7(2), GF7(1), GF7(1)).ToProjective(),\n            test::ProjectivePoint(GF7(1), GF7(2), GF7(1)));\n  EXPECT_EQ(test::PointXYZZ(GF7(1), GF7(2), GF7(2), GF7(6)).ToProjective(),\n            test::ProjectivePoint(GF7(6), GF7(4), GF7(5)));\n}\n\nTEST_F(PointXYZZTest, ToJacobian) {\n  EXPECT_EQ(test::PointXYZZ(GF7(1), GF7(2), GF7(0), GF7(0)).ToJacobian(),\n            test::JacobianPoint::Zero());\n  EXPECT_EQ(test::PointXYZZ(GF7(1), GF7(2), GF7(1), GF7(1)).ToJacobian(),\n            test::JacobianPoint(GF7(1), GF7(2), GF7(1)));\n  EXPECT_EQ(test::PointXYZZ(GF7(1), GF7(2), GF7(2), GF7(6)).ToJacobian(),\n            test::JacobianPoint(GF7(2), GF7(2), GF7(5)));\n}\n\n#if defined(TACHYON_HAS_OPENMP)\nTEST_F(PointXYZZTest, BatchNormalize) {\n  size_t size = size_t{1} << (static_cast<size_t>(omp_get_max_threads()) /\n                              GF7::kParallelBatchInverseDivisorThreshold);\n  for (size_t i = 0; i < 1; ++i) {\n    // NOTE(chokobole): if i == 0 runs in parallel, otherwise runs in serial.\n    std::vector<test::PointXYZZ> point_xyzzs =\n        CreatePseudoRandomPoints<test::PointXYZZ>(size - i);\n\n    std::vector<test::AffinePoint> affine_points;\n    affine_points.resize(point_xyzzs.size() - 1);\n    ASSERT_FALSE(test::PointXYZZ::BatchNormalize(point_xyzzs, &affine_points));\n\n    affine_points.resize(point_xyzzs.size());\n    ASSERT_TRUE(test::PointXYZZ::BatchNormalize(point_xyzzs, &affine_points));\n\n    std::vector<test::AffinePoint> expected_affine_points = base::Map(\n        point_xyzzs,\n        [](const test::PointXYZZ& point) { return point.ToAffine(); });\n    EXPECT_EQ(affine_points, expected_affine_points);\n  }\n}\n#endif  // defined(TACHYON_HAS_OPENMP)\n\nTEST_F(PointXYZZTest, BatchNormalizeSerial) {\n  std::vector<test::PointXYZZ> point_xyzzs = {\n      test::PointXYZZ(GF7(1), GF7(2), GF7(0), GF7(0)),\n      test::PointXYZZ(GF7(1), GF7(2), GF7(1), GF7(1)),\n      test::PointXYZZ(GF7(1), GF7(2), GF7(2), GF7(6))};\n\n  std::vector<test::AffinePoint> affine_points;\n  affine_points.resize(2);\n  ASSERT_FALSE(\n      test::PointXYZZ::BatchNormalizeSerial(point_xyzzs, &affine_points));\n\n  affine_points.resize(3);\n  ASSERT_TRUE(\n      test::PointXYZZ::BatchNormalizeSerial(point_xyzzs, &affine_points));\n\n  std::vector<test::AffinePoint> expected_affine_points = {\n      test::AffinePoint::Zero(), test::AffinePoint(GF7(1), GF7(2)),\n      test::AffinePoint(GF7(4), GF7(5))};\n  EXPECT_EQ(affine_points, expected_affine_points);\n}\n\nTEST_F(PointXYZZTest, IsOnCurve) {\n  test::PointXYZZ invalid_point(GF7(1), GF7(2), GF7(1), GF7(1));\n  EXPECT_FALSE(invalid_point.IsOnCurve());\n  test::PointXYZZ valid_point(GF7(3), GF7(2), GF7(1), GF7(1));\n  EXPECT_TRUE(valid_point.IsOnCurve());\n  valid_point = test::PointXYZZ(GF7(3), GF7(5), GF7(1), GF7(1));\n  EXPECT_TRUE(valid_point.IsOnCurve());\n}\n\nTEST_F(PointXYZZTest, CreateFromX) {\n  {\n    std::optional<test::PointXYZZ> p =\n        test::PointXYZZ::CreateFromX(GF7(3), /*pick_odd=*/true);\n    ASSERT_TRUE(p.has_value());\n    EXPECT_EQ(p->y(), GF7(5));\n  }\n  {\n    std::optional<test::PointXYZZ> p =\n        test::PointXYZZ::CreateFromX(GF7(3), /*pick_odd=*/false);\n    ASSERT_TRUE(p.has_value());\n    EXPECT_EQ(p->y(), GF7(2));\n  }\n  {\n    std::optional<test::PointXYZZ> p =\n        test::PointXYZZ::CreateFromX(GF7(1), /*pick_odd=*/false);\n    ASSERT_FALSE(p.has_value());\n  }\n}\n\nTEST_F(PointXYZZTest, Copyable) {\n  test::PointXYZZ expected = test::PointXYZZ::Random();\n\n  base::Uint8VectorBuffer write_buf;\n  ASSERT_TRUE(write_buf.Grow(base::EstimateSize(expected)));\n  ASSERT_TRUE(write_buf.Write(expected));\n  ASSERT_TRUE(write_buf.Done());\n\n  write_buf.set_buffer_offset(0);\n\n  test::PointXYZZ value;\n  ASSERT_TRUE(write_buf.Read(&value));\n  EXPECT_EQ(expected, value);\n}\n\nTEST_F(PointXYZZTest, JsonValueConverter) {\n  test::PointXYZZ expected_point(GF7(1), GF7(2), GF7(3), GF7(4));\n  std::string expected_json = R\"({\"x\":1,\"y\":2,\"zz\":3,\"zzz\":4})\";\n\n  test::PointXYZZ p;\n  std::string error;\n  ASSERT_TRUE(base::ParseJson(expected_json, &p, &error));\n  ASSERT_TRUE(error.empty());\n  EXPECT_EQ(p, expected_point);\n\n  std::string json = base::WriteToJson(p);\n  EXPECT_EQ(json, expected_json);\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/short_weierstrass/projective_point.h",
    "content": "#ifndef TACHYON_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_PROJECTIVE_POINT_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_PROJECTIVE_POINT_H_\n\n#include <string>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/math/base/groups.h\"\n#include \"tachyon/math/geometry/affine_point.h\"\n#include \"tachyon/math/geometry/curve_type.h\"\n#include \"tachyon/math/geometry/jacobian_point.h\"\n#include \"tachyon/math/geometry/point3.h\"\n#include \"tachyon/math/geometry/point_xyzz.h\"\n#include \"tachyon/math/geometry/projective_point.h\"\n\nnamespace tachyon {\nnamespace math {\n\ntemplate <typename _Curve>\nclass ProjectivePoint<\n    _Curve, std::enable_if_t<_Curve::kType == CurveType::kShortWeierstrass>>\n    final : public AdditiveGroup<ProjectivePoint<_Curve>> {\n public:\n  constexpr static bool kNegationIsCheap = true;\n\n  using Curve = _Curve;\n  using BaseField = typename Curve::BaseField;\n  using ScalarField = typename Curve::ScalarField;\n\n  constexpr ProjectivePoint()\n      : ProjectivePoint(BaseField::One(), BaseField::One(), BaseField::Zero()) {\n  }\n  explicit constexpr ProjectivePoint(const Point3<BaseField>& point)\n      : ProjectivePoint(point.x, point.y, point.z) {}\n  explicit constexpr ProjectivePoint(Point3<BaseField>&& point)\n      : ProjectivePoint(std::move(point.x), std::move(point.y),\n                        std::move(point.z)) {}\n  constexpr ProjectivePoint(const BaseField& x, const BaseField& y,\n                            const BaseField& z)\n      : x_(x), y_(y), z_(z) {}\n  constexpr ProjectivePoint(BaseField&& x, BaseField&& y, BaseField&& z)\n      : x_(std::move(x)), y_(std::move(y)), z_(std::move(z)) {}\n\n  constexpr static ProjectivePoint CreateChecked(const BaseField& x,\n                                                 const BaseField& y,\n                                                 const BaseField& z) {\n    ProjectivePoint ret = {x, y, z};\n    CHECK(ret.IsOnCurve());\n    return ret;\n  }\n\n  constexpr static ProjectivePoint CreateChecked(BaseField&& x, BaseField&& y,\n                                                 BaseField&& z) {\n    ProjectivePoint ret = {std::move(x), std::move(y), std::move(z)};\n    CHECK(ret.IsOnCurve());\n    return ret;\n  }\n\n  constexpr static std::optional<ProjectivePoint> CreateFromX(\n      const BaseField& x, bool pick_odd) {\n    ProjectivePoint point{};\n    if (!Curve::GetPointFromX(x, pick_odd, &point)) return std::nullopt;\n    return point;\n  }\n\n  constexpr static ProjectivePoint Zero() { return ProjectivePoint(); }\n\n  constexpr static ProjectivePoint One() { return Generator(); }\n\n  constexpr static ProjectivePoint Generator() {\n    return {Curve::Config::kGenerator.x, Curve::Config::kGenerator.y,\n            BaseField::One()};\n  }\n\n  constexpr static ProjectivePoint FromAffine(const AffinePoint<Curve>& point) {\n    return point.ToProjective();\n  }\n\n  constexpr static ProjectivePoint FromJacobian(\n      const JacobianPoint<Curve>& point) {\n    return point.ToProjective();\n  }\n\n  constexpr static ProjectivePoint FromXYZZ(const PointXYZZ<Curve>& point) {\n    return point.ToProjective();\n  }\n\n  constexpr static ProjectivePoint Random() {\n    return ScalarField::Random() * Generator();\n  }\n\n  constexpr static ProjectivePoint Endomorphism(const ProjectivePoint& point) {\n    return ProjectivePoint(point.x_ * Curve::Config::kEndomorphismCoefficient,\n                           point.y_, point.z_);\n  }\n\n  template <typename ProjectiveContainer, typename AffineContainer>\n  [[nodiscard]] CONSTEXPR_IF_NOT_OPENMP static bool BatchNormalize(\n      const ProjectiveContainer& projective_points,\n      AffineContainer* affine_points) {\n    size_t size = std::size(projective_points);\n    if (size != std::size(*affine_points)) {\n      LOG(ERROR)\n          << \"Size of |projective_points| and |affine_points| do not match\";\n      return false;\n    }\n#if defined(TACHYON_HAS_OPENMP)\n    size_t thread_nums = static_cast<size_t>(omp_get_max_threads());\n    if (size >=\n        size_t{1} << (thread_nums /\n                      ScalarField::kParallelBatchInverseDivisorThreshold)) {\n      base::Parallelize(\n          size, [&projective_points, affine_points](\n                    size_t len, size_t chunk_offset, size_t chunk_size) {\n            size_t start = chunk_offset * chunk_size;\n            absl::Span<AffinePoint<Curve>> affine_points_chunk(\n                &(*affine_points)[start], len);\n            absl::Span<const ProjectivePoint> projective_points_chunk(\n                &projective_points[start], len);\n            CHECK(BatchNormalizeSerial(projective_points_chunk,\n                                       &affine_points_chunk));\n          });\n      return true;\n    }\n#endif\n    return BatchNormalizeSerial(projective_points, affine_points);\n  }\n\n  template <typename ProjectiveContainer, typename AffineContainer>\n  [[nodiscard]] constexpr static bool BatchNormalizeSerial(\n      const ProjectiveContainer& projective_points,\n      AffineContainer* affine_points) {\n    size_t size = std::size(projective_points);\n    if (size != std::size(*affine_points)) {\n      LOG(ERROR)\n          << \"Size of |projective_points| and |affine_points| do not match\";\n      return false;\n    }\n    std::vector<BaseField> z_inverses =\n        base::Map(projective_points,\n                  [](const ProjectivePoint& point) { return point.z_; });\n    if (!BaseField::BatchInverseInPlaceSerial(z_inverses)) return false;\n    for (size_t i = 0; i < size; ++i) {\n      const BaseField& z_inv = z_inverses[i];\n      if (z_inv.IsZero()) {\n        (*affine_points)[i] = AffinePoint<Curve>::Zero();\n      } else if (z_inv.IsOne()) {\n        (*affine_points)[i] = {projective_points[i].x_,\n                               projective_points[i].y_};\n      } else {\n        (*affine_points)[i] = {projective_points[i].x_ * z_inv,\n                               projective_points[i].y_ * z_inv};\n      }\n    }\n    return true;\n  }\n\n  constexpr const BaseField& x() const { return x_; }\n  constexpr const BaseField& y() const { return y_; }\n  constexpr const BaseField& z() const { return z_; }\n\n  constexpr bool operator==(const ProjectivePoint& other) const {\n    if (IsZero()) {\n      return other.IsZero();\n    }\n\n    if (other.IsZero()) {\n      return false;\n    }\n\n    // The points (X, Y, Z) and (X', Y', Z')\n    // are equal when (X * Z') = (X' * Z)\n    // and (Y * Z') = (Y' * Z).\n    if (x_ * other.z_ != other.x_ * z_) {\n      return false;\n    } else {\n      return y_ * other.z_ == other.y_ * z_;\n    }\n  }\n\n  constexpr bool operator!=(const ProjectivePoint& other) const {\n    return !operator==(other);\n  }\n\n  constexpr bool IsZero() const { return z_.IsZero(); }\n\n  constexpr bool IsOnCurve() { return Curve::IsOnCurve(*this); }\n\n  // The jacobian point X, Y, Z is represented in the affine\n  // coordinates as X/Z, Y/Z.\n  constexpr AffinePoint<Curve> ToAffine() const {\n    if (IsZero()) {\n      return AffinePoint<Curve>::Zero();\n    } else if (z_.IsOne()) {\n      return {x_, y_};\n    } else {\n      // NOTE(ashjeong): if |z_| is 0, |IsZero()| will also evaluate to true,\n      // and this block will not be executed\n      BaseField z_inv = *z_.Inverse();\n      return {x_ * z_inv, y_ * z_inv};\n    }\n  }\n\n  // The jacobian point X, Y, Z is represented in the jacobian\n  // coordinates as X*Z, Y*Z², Z.\n  constexpr JacobianPoint<Curve> ToJacobian() const {\n    BaseField zz = z_.Square();\n    return {x_ * z_, y_ * zz, z_};\n  }\n\n  // The jacobian point X, Y, Z is represented in the xyzz\n  // coordinates as X*Z, Y*Z², Z², Z³.\n  constexpr PointXYZZ<Curve> ToXYZZ() const {\n    BaseField zz = z_.Square();\n    return {x_ * z_, y_ * zz, zz, z_ * zz};\n  }\n\n  std::string ToString() const {\n    return absl::Substitute(\"($0, $1, $2)\", x_.ToString(), y_.ToString(),\n                            z_.ToString());\n  }\n\n  std::string ToHexString(bool pad_zero = false) const {\n    return absl::Substitute(\"($0, $1, $2)\", x_.ToHexString(pad_zero),\n                            y_.ToHexString(pad_zero), z_.ToHexString(pad_zero));\n  }\n\n  // AdditiveSemigroup methods\n  constexpr ProjectivePoint Add(const ProjectivePoint& other) const;\n  constexpr ProjectivePoint& AddInPlace(const ProjectivePoint& other);\n  constexpr ProjectivePoint Add(const AffinePoint<Curve>& other) const;\n  constexpr ProjectivePoint& AddInPlace(const AffinePoint<Curve>& other);\n  constexpr ProjectivePoint DoubleImpl() const;\n  constexpr ProjectivePoint& DoubleImplInPlace();\n\n  // AdditiveGroup methods\n  constexpr ProjectivePoint Negate() const { return {x_, -y_, z_}; }\n\n  constexpr ProjectivePoint& NegateInPlace() {\n    y_.NegateInPlace();\n    return *this;\n  }\n\n  constexpr ProjectivePoint operator*(const ScalarField& v) const {\n    return this->ScalarMul(v);\n  }\n  constexpr ProjectivePoint& operator*=(const ScalarField& v) {\n    return *this = operator*(v);\n  }\n\n private:\n  constexpr static void DoAdd(const ProjectivePoint& a,\n                              const ProjectivePoint& b, ProjectivePoint& c);\n  constexpr static void DoAdd(const ProjectivePoint& a,\n                              const AffinePoint<Curve>& b, ProjectivePoint& c);\n  constexpr static void DoDoubleImpl(const ProjectivePoint& a,\n                                     ProjectivePoint& b);\n\n  BaseField x_;\n  BaseField y_;\n  BaseField z_;\n};\n\n}  // namespace math\n\nnamespace base {\n\ntemplate <typename Curve>\nclass Copyable<math::ProjectivePoint<\n    Curve,\n    std::enable_if_t<Curve::kType == math::CurveType::kShortWeierstrass>>> {\n public:\n  static bool WriteTo(const math::ProjectivePoint<Curve>& point,\n                      Buffer* buffer) {\n    return buffer->WriteMany(point.x(), point.y(), point.z());\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer,\n                       math::ProjectivePoint<Curve>* point) {\n    using BaseField = typename math::ProjectivePoint<Curve>::BaseField;\n    BaseField x, y, z;\n    if (!buffer.ReadMany(&x, &y, &z)) return false;\n\n    *point =\n        math::ProjectivePoint<Curve>(std::move(x), std::move(y), std::move(z));\n    return true;\n  }\n\n  static size_t EstimateSize(const math::ProjectivePoint<Curve>& point) {\n    return base::EstimateSize(point.x(), point.y(), point.z());\n  }\n};\n\ntemplate <typename Curve>\nclass RapidJsonValueConverter<math::ProjectivePoint<\n    Curve,\n    std::enable_if_t<Curve::kType == math::CurveType::kShortWeierstrass>>> {\n public:\n  using Field = typename math::ProjectivePoint<Curve>::BaseField;\n\n  template <typename Allocator>\n  static rapidjson::Value From(const math::ProjectivePoint<Curve>& value,\n                               Allocator& allocator) {\n    rapidjson::Value object(rapidjson::kObjectType);\n    AddJsonElement(object, \"x\", value.x(), allocator);\n    AddJsonElement(object, \"y\", value.y(), allocator);\n    AddJsonElement(object, \"z\", value.z(), allocator);\n    return object;\n  }\n\n  static bool To(const rapidjson::Value& json_value, std::string_view key,\n                 math::ProjectivePoint<Curve>* value, std::string* error) {\n    Field x;\n    Field y;\n    Field z;\n    if (!ParseJsonElement(json_value, \"x\", &x, error)) return false;\n    if (!ParseJsonElement(json_value, \"y\", &y, error)) return false;\n    if (!ParseJsonElement(json_value, \"z\", &z, error)) return false;\n    *value =\n        math::ProjectivePoint<Curve>(std::move(x), std::move(y), std::move(z));\n    return true;\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#include \"tachyon/math/elliptic_curves/short_weierstrass/projective_point_impl.h\"\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_PROJECTIVE_POINT_H_\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/short_weierstrass/projective_point_impl.h",
    "content": "#ifndef TACHYON_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_PROJECTIVE_POINT_IMPL_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_PROJECTIVE_POINT_IMPL_H_\n\n#include <utility>\n\n#include \"tachyon/math/elliptic_curves/short_weierstrass/projective_point.h\"\nnamespace tachyon::math {\n\n#define CLASS      \\\n  ProjectivePoint< \\\n      Curve, std::enable_if_t<Curve::kType == CurveType::kShortWeierstrass>>\n\ntemplate <typename Curve>\nconstexpr CLASS CLASS::Add(const ProjectivePoint& other) const {\n  if (IsZero()) {\n    return other;\n  }\n\n  if (other.IsZero()) {\n    return *this;\n  }\n\n  ProjectivePoint ret;\n  DoAdd(*this, other, ret);\n  return ret;\n}\n\ntemplate <typename Curve>\nconstexpr CLASS& CLASS::AddInPlace(const ProjectivePoint& other) {\n  if (IsZero()) {\n    return *this = other;\n  }\n\n  if (other.IsZero()) {\n    return *this;\n  }\n\n  DoAdd(*this, other, *this);\n  return *this;\n}\n\n// static\ntemplate <typename Curve>\nconstexpr void CLASS::DoAdd(const ProjectivePoint& a, const ProjectivePoint& b,\n                            ProjectivePoint& c) {\n  // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-projective.html#addition-add-1998-cmo-2\n  // Y1Z2 = Y1 * Z2\n  BaseField y1z2 = a.y_ * b.z_;\n\n  // X1Z2 = X1 * Z2\n  BaseField x1z2 = a.x_ * b.z_;\n\n  // Z1Z2 = Z1 * Z2\n  BaseField z1z2 = a.z_ * b.z_;\n\n  // u = Y2 * Z1 - Y1Z2\n  BaseField u = b.y_ * a.z_;\n  u -= y1z2;\n\n  // v = X2 * Z1 - X1Z2\n  BaseField v = b.x_ * a.z_;\n  v -= x1z2;\n\n  if (u.IsZero() && v.IsZero()) {\n    if (&a == &c) {\n      c.DoubleInPlace();\n    } else {\n      c = a.Double();\n    }\n    return;\n  }\n\n  // vv = v²\n  BaseField vv = v.Square();\n\n  // vvv = v * vv\n  BaseField vvv = v * vv;\n\n  // R = vv * X1Z2\n  BaseField r = vv * x1z2;\n\n  // D = u² * Z1Z2 - vvv - 2 * R\n  BaseField d = u.Square();\n  d *= z1z2;\n  d -= vvv;\n  d -= r.Double();\n\n  // X3 = v * D\n  c.x_ = v * d;\n\n  // Y3 = u * (R - D) - vvv * Y1Z2\n  BaseField lefts[] = {std::move(u), -vvv};\n  BaseField rights[] = {r - d, std::move(y1z2)};\n  c.y_ = BaseField::SumOfProductsSerial(lefts, rights);\n\n  // Z3 = vvv * Z1Z2\n  c.z_ = vvv * z1z2;\n}\n\ntemplate <typename Curve>\nconstexpr CLASS CLASS::Add(const AffinePoint<Curve>& other) const {\n  if (IsZero()) {\n    return other.ToProjective();\n  }\n\n  if (other.IsZero()) {\n    return *this;\n  }\n\n  ProjectivePoint ret;\n  DoAdd(*this, other, ret);\n  return ret;\n}\n\ntemplate <typename Curve>\nconstexpr CLASS& CLASS::AddInPlace(const AffinePoint<Curve>& other) {\n  if (IsZero()) {\n    return *this = other.ToProjective();\n  }\n\n  if (other.IsZero()) {\n    return *this;\n  }\n\n  DoAdd(*this, other, *this);\n  return *this;\n}\n\n// static\ntemplate <typename Curve>\nconstexpr void CLASS::DoAdd(const ProjectivePoint& a,\n                            const AffinePoint<Curve>& b, ProjectivePoint& c) {\n  // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-projective.html#addition-madd-1998-cmo\n  // u = Y2 * Z1 - Y1\n  BaseField u = b.y() * a.z_;\n  u -= a.y_;\n\n  // v = X2 * Z1 - X1\n  BaseField v = b.x() * a.z_;\n  v -= a.x_;\n\n  if (u.IsZero() && v.IsZero()) {\n    if (&a == &c) {\n      c.DoubleInPlace();\n    } else {\n      c = a.Double();\n    }\n    return;\n  }\n\n  // vv = v²\n  BaseField vv = v.Square();\n\n  // vvv = v * vv\n  BaseField vvv = v * vv;\n\n  // R = vv * X1\n  BaseField r = vv * a.x_;\n\n  // D = u² * Z1 - vvv - 2 * R\n  BaseField d = u.Square();\n  d *= a.z_;\n  d -= vvv;\n  d -= r.Double();\n\n  // X3 = v * D\n  c.x_ = v * d;\n\n  // Y3 = u * (R - D) - vvv * Y1\n  BaseField lefts[] = {std::move(u), -vvv};\n  BaseField rights[] = {r - d, a.y_};\n  c.y_ = BaseField::SumOfProductsSerial(lefts, rights);\n\n  // Z3 = vvv * Z1\n  c.z_ = vvv * a.z_;\n}\n\ntemplate <typename Curve>\nconstexpr CLASS CLASS::DoubleImpl() const {\n  if (IsZero()) {\n    return ProjectivePoint::Zero();\n  }\n\n  ProjectivePoint ret;\n  DoDoubleImpl(*this, ret);\n  return ret;\n}\n\ntemplate <typename Curve>\nconstexpr CLASS& CLASS::DoubleImplInPlace() {\n  if (IsZero()) {\n    return *this;\n  }\n\n  DoDoubleImpl(*this, *this);\n  return *this;\n}\n\n// static\ntemplate <typename Curve>\nconstexpr void CLASS::DoDoubleImpl(const ProjectivePoint& a,\n                                   ProjectivePoint& b) {\n  // https://hyperelliptic.org/EFD/g1p/auto-shortw-projective.html#doubling-dbl-2007-bl\n  // XX = X1²\n  BaseField xx = a.x_.Square();\n\n  // ZZ = Z1²\n  BaseField zz = a.z_.Square();\n\n  // w = a * ZZ + 3 * XX\n  BaseField w = xx.Double();\n  w += xx;\n  if constexpr (!Curve::Config::kAIsZero) {\n    w += Curve::Config::MulByA(zz);\n  }\n\n  // s = 2 * Y1 * Z1\n  BaseField s = a.y_ * a.z_;\n  s.DoubleInPlace();\n\n  // sss = s³\n  BaseField sss = s.Square();\n  sss *= s;\n\n  // R = Y1 * s\n  BaseField r = a.y_ * s;\n\n  // RR = R²\n  BaseField rr = r.Square();\n\n  // D = (X1 + R)² - XX - RR\n  BaseField d = a.x_ + r;\n  d.SquareInPlace();\n  d -= xx;\n  d -= rr;\n\n  // h = w² - 2 * D\n  BaseField h = w.Square();\n  h -= d.Double();\n\n  // X3 = h * s\n  b.x_ = h * s;\n\n  // Y3 = w * (D - h) - 2 * RR\n  b.y_ = d - h;\n  b.y_ *= w;\n  b.y_ -= rr.Double();\n\n  // Z3 = sss\n  b.z_ = std::move(sss);\n}\n\n#undef CLASS\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_PROJECTIVE_POINT_IMPL_H_\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/short_weierstrass/projective_point_unittest.cc",
    "content": "#include \"tachyon/math/elliptic_curves/short_weierstrass/projective_point.h\"\n\n#include <vector>\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/buffer/vector_buffer.h\"\n#include \"tachyon/math/elliptic_curves/short_weierstrass/affine_point.h\"\n#include \"tachyon/math/elliptic_curves/short_weierstrass/jacobian_point.h\"\n#include \"tachyon/math/elliptic_curves/short_weierstrass/point_xyzz.h\"\n#include \"tachyon/math/elliptic_curves/short_weierstrass/test/sw_curve_config.h\"\n#include \"tachyon/math/elliptic_curves/test/random.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\nclass ProjectivePointTest : public testing::Test {\n public:\n  static void SetUpTestSuite() { test::G1Curve::Init(); }\n};\n\n}  // namespace\n\nTEST_F(ProjectivePointTest, IsZero) {\n  EXPECT_TRUE(test::ProjectivePoint::Zero().IsZero());\n  EXPECT_FALSE(test::ProjectivePoint(GF7(1), GF7(2), GF7(1)).IsZero());\n}\n\nTEST_F(ProjectivePointTest, Generator) {\n  EXPECT_EQ(\n      test::ProjectivePoint::Generator(),\n      test::ProjectivePoint(test::ProjectivePoint::Curve::Config::kGenerator.x,\n                            test::ProjectivePoint::Curve::Config::kGenerator.y,\n                            GF7::One()));\n}\n\nTEST_F(ProjectivePointTest, Random) {\n  bool success = false;\n  test::ProjectivePoint r = test::ProjectivePoint::Random();\n  for (size_t i = 0; i < 100; ++i) {\n    if (r != test::ProjectivePoint::Random()) {\n      success = true;\n      break;\n    }\n  }\n  EXPECT_TRUE(success);\n}\n\nTEST_F(ProjectivePointTest, EqualityOperators) {\n  {\n    SCOPED_TRACE(\"p.IsZero() && p2.IsZero()\");\n    test::ProjectivePoint p(GF7(1), GF7(2), GF7(0));\n    test::ProjectivePoint p2(GF7(3), GF7(4), GF7(0));\n    EXPECT_EQ(p, p2);\n    EXPECT_EQ(p2, p);\n  }\n\n  {\n    SCOPED_TRACE(\"!p.IsZero() && p2.IsZero()\");\n    test::ProjectivePoint p(GF7(1), GF7(2), GF7(1));\n    test::ProjectivePoint p2(GF7(3), GF7(4), GF7(0));\n    EXPECT_NE(p, p2);\n    EXPECT_NE(p2, p);\n  }\n\n  {\n    SCOPED_TRACE(\"other\");\n    test::ProjectivePoint p(GF7(1), GF7(2), GF7(3));\n    test::ProjectivePoint p2(GF7(1), GF7(2), GF7(3));\n    EXPECT_EQ(p, p2);\n    EXPECT_EQ(p2, p);\n  }\n}\n\nTEST_F(ProjectivePointTest, AdditiveGroupOperators) {\n  test::ProjectivePoint pp =\n      test::ProjectivePoint::CreateChecked(GF7(5), GF7(5), GF7(1));\n  test::ProjectivePoint pp2 =\n      test::ProjectivePoint::CreateChecked(GF7(3), GF7(2), GF7(1));\n  test::ProjectivePoint pp3 =\n      test::ProjectivePoint::CreateChecked(GF7(3), GF7(5), GF7(1));\n  test::ProjectivePoint pp4 =\n      test::ProjectivePoint::CreateChecked(GF7(6), GF7(5), GF7(1));\n  test::AffinePoint ap = pp.ToAffine();\n  test::AffinePoint ap2 = pp2.ToAffine();\n\n  EXPECT_EQ(pp + pp2, pp3);\n  EXPECT_EQ(pp - pp3, -pp2);\n  EXPECT_EQ(pp + pp, pp4);\n  EXPECT_EQ(pp - pp4, -pp);\n\n  {\n    test::ProjectivePoint pp_tmp = pp;\n    pp_tmp += pp2;\n    EXPECT_EQ(pp_tmp, pp3);\n    pp_tmp -= pp2;\n    EXPECT_EQ(pp_tmp, pp);\n  }\n\n  EXPECT_EQ(pp + ap2, pp3);\n  EXPECT_EQ(pp + ap, pp4);\n  EXPECT_EQ(pp - pp3, -pp2);\n  EXPECT_EQ(pp - pp4, -pp);\n\n  EXPECT_EQ(pp.Double(), pp4);\n  {\n    test::ProjectivePoint pp_tmp = pp;\n    pp_tmp.DoubleInPlace();\n    EXPECT_EQ(pp_tmp, pp4);\n  }\n\n  EXPECT_EQ(-pp, test::ProjectivePoint(GF7(5), GF7(2), GF7(1)));\n  {\n    test::ProjectivePoint pp_tmp = pp;\n    pp_tmp.NegateInPlace();\n    EXPECT_EQ(pp_tmp, test::ProjectivePoint(GF7(5), GF7(2), GF7(1)));\n  }\n\n  EXPECT_EQ(pp * GF7(2), pp4);\n  EXPECT_EQ(GF7(2) * pp, pp4);\n  EXPECT_EQ(pp *= GF7(2), pp4);\n}\n\nTEST_F(ProjectivePointTest, ScalarMulOperator) {\n  std::vector<test::AffinePoint> points;\n  for (size_t i = 0; i < 7; ++i) {\n    points.push_back((GF7(i) * test::ProjectivePoint::Generator()).ToAffine());\n  }\n\n  EXPECT_THAT(\n      points,\n      testing::UnorderedElementsAreArray(std::vector<test::AffinePoint>{\n          test::AffinePoint(GF7(0), GF7(0)), test::AffinePoint(GF7(3), GF7(2)),\n          test::AffinePoint(GF7(5), GF7(2)), test::AffinePoint(GF7(6), GF7(2)),\n          test::AffinePoint(GF7(3), GF7(5)), test::AffinePoint(GF7(5), GF7(5)),\n          test::AffinePoint(GF7(6), GF7(5))}));\n}\n\nTEST_F(ProjectivePointTest, ToAffine) {\n  EXPECT_EQ(test::ProjectivePoint(GF7(1), GF7(2), GF7(0)).ToAffine(),\n            test::AffinePoint::Zero());\n  EXPECT_EQ(test::ProjectivePoint(GF7(1), GF7(2), GF7(1)).ToAffine(),\n            test::AffinePoint(GF7(1), GF7(2)));\n  EXPECT_EQ(test::ProjectivePoint(GF7(1), GF7(2), GF7(3)).ToAffine(),\n            test::AffinePoint(GF7(5), GF7(3)));\n}\n\nTEST_F(ProjectivePointTest, ToJacobian) {\n  EXPECT_EQ(test::ProjectivePoint(GF7(1), GF7(2), GF7(0)).ToJacobian(),\n            test::JacobianPoint::Zero());\n  EXPECT_EQ(test::ProjectivePoint(GF7(1), GF7(2), GF7(1)).ToJacobian(),\n            test::JacobianPoint(GF7(1), GF7(2), GF7(1)));\n  EXPECT_EQ(test::ProjectivePoint(GF7(1), GF7(2), GF7(3)).ToJacobian(),\n            test::JacobianPoint(GF7(3), GF7(4), GF7(3)));\n}\n\nTEST_F(ProjectivePointTest, ToXYZZ) {\n  EXPECT_EQ(test::ProjectivePoint(GF7(1), GF7(2), GF7(0)).ToXYZZ(),\n            test::PointXYZZ::Zero());\n  EXPECT_EQ(test::ProjectivePoint(GF7(1), GF7(2), GF7(3)).ToXYZZ(),\n            test::PointXYZZ(GF7(3), GF7(4), GF7(2), GF7(6)));\n}\n\n#if defined(TACHYON_HAS_OPENMP)\nTEST_F(ProjectivePointTest, BatchNormalize) {\n  size_t size = size_t{1} << (static_cast<size_t>(omp_get_max_threads()) /\n                              GF7::kParallelBatchInverseDivisorThreshold);\n  for (size_t i = 0; i < 1; ++i) {\n    // NOTE(chokobole): if i == 0 runs in parallel, otherwise runs in serial.\n    std::vector<test::ProjectivePoint> projective_points =\n        CreatePseudoRandomPoints<test::ProjectivePoint>(size - i);\n\n    std::vector<test::AffinePoint> affine_points;\n    affine_points.resize(projective_points.size() - 1);\n    ASSERT_FALSE(test::ProjectivePoint::BatchNormalize(projective_points,\n                                                       &affine_points));\n\n    affine_points.resize(projective_points.size());\n    ASSERT_TRUE(test::ProjectivePoint::BatchNormalize(projective_points,\n                                                      &affine_points));\n\n    std::vector<test::AffinePoint> expected_affine_points = base::Map(\n        projective_points,\n        [](const test::ProjectivePoint& point) { return point.ToAffine(); });\n    EXPECT_EQ(affine_points, expected_affine_points);\n  }\n}\n#endif  // defined(TACHYON_HAS_OPENMP)\n\nTEST_F(ProjectivePointTest, BatchNormalizeSerial) {\n  std::vector<test::ProjectivePoint> projective_points = {\n      test::ProjectivePoint(GF7(1), GF7(2), GF7(0)),\n      test::ProjectivePoint(GF7(1), GF7(2), GF7(1)),\n      test::ProjectivePoint(GF7(1), GF7(2), GF7(3))};\n\n  std::vector<test::AffinePoint> affine_points;\n  affine_points.resize(2);\n  ASSERT_FALSE(test::ProjectivePoint::BatchNormalizeSerial(projective_points,\n                                                           &affine_points));\n\n  affine_points.resize(3);\n  ASSERT_TRUE(test::ProjectivePoint::BatchNormalizeSerial(projective_points,\n                                                          &affine_points));\n\n  std::vector<test::AffinePoint> expected_affine_points = {\n      test::AffinePoint::Zero(), test::AffinePoint(GF7(1), GF7(2)),\n      test::AffinePoint(GF7(5), GF7(3))};\n  EXPECT_EQ(affine_points, expected_affine_points);\n}\n\nTEST_F(ProjectivePointTest, IsOnCurve) {\n  test::ProjectivePoint invalid_point(GF7(1), GF7(2), GF7(1));\n  EXPECT_FALSE(invalid_point.IsOnCurve());\n  test::ProjectivePoint valid_point(GF7(3), GF7(2), GF7(1));\n  EXPECT_TRUE(valid_point.IsOnCurve());\n  valid_point = test::ProjectivePoint(GF7(3), GF7(5), GF7(1));\n  EXPECT_TRUE(valid_point.IsOnCurve());\n}\n\nTEST_F(ProjectivePointTest, CreateFromX) {\n  {\n    std::optional<test::ProjectivePoint> p =\n        test::ProjectivePoint::CreateFromX(GF7(3), /*pick_odd=*/true);\n    ASSERT_TRUE(p.has_value());\n    EXPECT_EQ(p->y(), GF7(5));\n  }\n  {\n    std::optional<test::ProjectivePoint> p =\n        test::ProjectivePoint::CreateFromX(GF7(3), /*pick_odd=*/false);\n    ASSERT_TRUE(p.has_value());\n    EXPECT_EQ(p->y(), GF7(2));\n  }\n  {\n    std::optional<test::ProjectivePoint> p =\n        test::ProjectivePoint::CreateFromX(GF7(1), /*pick_odd=*/false);\n    ASSERT_FALSE(p.has_value());\n  }\n}\n\nTEST_F(ProjectivePointTest, Copyable) {\n  test::ProjectivePoint expected = test::ProjectivePoint::Random();\n\n  base::Uint8VectorBuffer write_buf;\n  ASSERT_TRUE(write_buf.Grow(base::EstimateSize(expected)));\n  ASSERT_TRUE(write_buf.Write(expected));\n  ASSERT_TRUE(write_buf.Done());\n\n  write_buf.set_buffer_offset(0);\n\n  test::ProjectivePoint value;\n  ASSERT_TRUE(write_buf.Read(&value));\n  EXPECT_EQ(expected, value);\n}\n\nTEST_F(ProjectivePointTest, JsonValueConverter) {\n  test::ProjectivePoint expected_point(GF7(1), GF7(2), GF7(3));\n  std::string expected_json = R\"({\"x\":1,\"y\":2,\"z\":3})\";\n\n  test::ProjectivePoint p;\n  std::string error;\n  ASSERT_TRUE(base::ParseJson(expected_json, &p, &error));\n  ASSERT_TRUE(error.empty());\n  EXPECT_EQ(p, expected_point);\n\n  std::string json = base::WriteToJson(p);\n  EXPECT_EQ(json, expected_json);\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/short_weierstrass/sw_curve.h",
    "content": "#ifndef TACHYON_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_SW_CURVE_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_SW_CURVE_H_\n\n#include <utility>\n\n#include \"absl/base/call_once.h\"\n\n#include \"tachyon/math/elliptic_curves/short_weierstrass/sw_curve_traits_forward.h\"\n#include \"tachyon/math/geometry/affine_point.h\"\n#include \"tachyon/math/geometry/curve_type.h\"\n#include \"tachyon/math/geometry/jacobian_point.h\"\n#include \"tachyon/math/geometry/point_conversions.h\"\n#include \"tachyon/math/geometry/point_xyzz.h\"\n#include \"tachyon/math/geometry/projective_point.h\"\n\nnamespace tachyon::math {\n\n// Config for Short Weierstrass model.\n// See https://www.hyperelliptic.org/EFD/g1p/auto-shortw.html for more details.\n// This config represents y² = x³ + a * x + b, where a and b are\n// constants.\ntemplate <typename SWCurveConfig>\nclass SWCurve {\n public:\n  using Config = SWCurveConfig;\n\n  using BaseField = typename Config::BaseField;\n  using ScalarField = typename Config::ScalarField;\n  using AffinePoint = typename SWCurveTraits<Config>::AffinePointTy;\n  using ProjectivePoint = typename SWCurveTraits<Config>::ProjectivePointTy;\n  using JacobianPoint = typename SWCurveTraits<Config>::JacobianPointTy;\n  using PointXYZZ = typename SWCurveTraits<Config>::PointXYZZTy;\n\n  using CpuCurve = SWCurve<typename Config::CpuCurveConfig>;\n  using GpuCurve = SWCurve<typename Config::GpuCurveConfig>;\n\n  constexpr static CurveType kType = CurveType::kShortWeierstrass;\n\n  static void Init() {\n    static absl::once_flag once;\n    absl::call_once(once, []() {\n      BaseField::Init();\n      ScalarField::Init();\n\n      Config::Init();\n    });\n  }\n\n  // Attempts to construct an affine point given an |x| coordinate. The\n  // point is not guaranteed to be in the prime order subgroup.\n  // If |pick_odd| is set to true, it chooses an odd(positive) y coordinate.\n  template <typename Point>\n  constexpr static bool GetPointFromX(const BaseField& x, bool pick_odd,\n                                      Point* point) {\n    BaseField even_y{};\n    BaseField odd_y{};\n    if (!GetYsFromX(x, &even_y, &odd_y)) return false;\n    if constexpr (std::is_same_v<Point, AffinePoint>) {\n      *point = AffinePoint(x, pick_odd ? odd_y : even_y);\n    } else {\n      AffinePoint affine_point(x, pick_odd ? odd_y : even_y);\n      *point = ConvertPoint<Point>(affine_point);\n    }\n    return true;\n  }\n\n  // Returns true and populates |even_y| and |odd_y| with the two possible\n  // y-coordinates corresponding to the given x-coordinate if the |x| coordinate\n  // corresponds to a curve point. Otherwise, returns false.\n  constexpr static bool GetYsFromX(const BaseField& x, BaseField* even_y,\n                                   BaseField* odd_y) {\n    BaseField right = x.Square() * x + Config::kB;\n    if constexpr (!Config::kAIsZero) {\n      right += Config::kA * x;\n    }\n    BaseField y;\n    if (!right.SquareRoot(&y)) return false;\n\n    if (y.ToBigInt().IsEven()) {\n      *odd_y = -y;\n      *even_y = std::move(y);\n    } else {\n      *even_y = -y;\n      *odd_y = std::move(y);\n    }\n    return true;\n  }\n\n  constexpr static bool IsOnCurve(const AffinePoint& point) {\n    if (point.IsZero()) return false;\n    BaseField right = point.x().Square() * point.x() + Config::kB;\n    if constexpr (!Config::kAIsZero) {\n      right += Config::kA * point.x();\n    }\n    return point.y().Square() == right;\n  }\n\n  constexpr static bool IsOnCurve(const ProjectivePoint& point) {\n    if (point.z().IsZero()) return false;\n    BaseField z2 = point.z().Square();\n    BaseField z3 = z2 * point.z();\n    BaseField right = point.x().Square() * point.x() + Config::kB * z3;\n    if constexpr (!Config::kAIsZero) {\n      right += Config::kA * point.x() * z2;\n    }\n    return point.y().Square() * point.z() == right;\n  }\n\n  constexpr static bool IsOnCurve(const JacobianPoint& point) {\n    if (point.z().IsZero()) return false;\n    BaseField z3 = point.z().Square() * point.z();\n    BaseField right = point.x().Square() * point.x() + Config::kB * z3.Square();\n    if constexpr (!Config::kAIsZero) {\n      right += Config::kA * point.x() * z3 * point.z();\n    }\n    return point.y().Square() == right;\n  }\n\n  constexpr static bool IsOnCurve(const PointXYZZ& point) {\n    if (point.zzz().IsZero()) return false;\n    BaseField right =\n        point.x().Square() * point.x() + Config::kB * point.zzz().Square();\n    if constexpr (!Config::kAIsZero) {\n      right += Config::kA * point.x() * point.zz().Square();\n    }\n    return point.y().Square() == right;\n  }\n};\n\ntemplate <typename Config>\nstruct SWCurveTraits {\n  using BaseField = typename Config::BaseField;\n  using ScalarField = typename Config::ScalarField;\n  using AffinePointTy = AffinePoint<SWCurve<Config>>;\n  using ProjectivePointTy = ProjectivePoint<SWCurve<Config>>;\n  using JacobianPointTy = JacobianPoint<SWCurve<Config>>;\n  using PointXYZZTy = PointXYZZ<SWCurve<Config>>;\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_SW_CURVE_H_\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/short_weierstrass/sw_curve_traits_forward.h",
    "content": "#ifndef TACHYON_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_SW_CURVE_TRAITS_FORWARD_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_SW_CURVE_TRAITS_FORWARD_H_\n\nnamespace tachyon::math {\n\ntemplate <typename T>\nstruct SWCurveTraits;\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_SW_CURVE_TRAITS_FORWARD_H_\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/short_weierstrass/test/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"sw_curve_config\",\n    testonly = True,\n    hdrs = [\"sw_curve_config.h\"],\n    deps = [\n        \"//tachyon/math/elliptic_curves/short_weierstrass:points\",\n        \"//tachyon/math/elliptic_curves/short_weierstrass:sw_curve\",\n        \"//tachyon/math/finite_fields/test:gf7\",\n        \"@com_google_absl//absl/base\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/short_weierstrass/test/sw_curve_config.h",
    "content": "#ifndef TACHYON_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_TEST_SW_CURVE_CONFIG_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_TEST_SW_CURVE_CONFIG_H_\n\n#include \"absl/base/call_once.h\"\n\n#include \"tachyon/math/elliptic_curves/short_weierstrass/affine_point.h\"\n#include \"tachyon/math/elliptic_curves/short_weierstrass/jacobian_point.h\"\n#include \"tachyon/math/elliptic_curves/short_weierstrass/point_xyzz.h\"\n#include \"tachyon/math/elliptic_curves/short_weierstrass/projective_point.h\"\n#include \"tachyon/math/elliptic_curves/short_weierstrass/sw_curve.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n#include \"tachyon/math/geometry/point2.h\"\n\nnamespace tachyon::math {\nnamespace test {\n\ntemplate <typename _BaseField, typename _ScalarField>\nclass SWCurveConfig {\n public:\n  using BaseField = _BaseField;\n  using BasePrimeField = BaseField;\n  using ScalarField = _ScalarField;\n\n  using CpuBaseField = typename BaseField::CpuField;\n  using CpuScalarField = typename ScalarField::CpuField;\n  using GpuBaseField = typename BaseField::GpuField;\n  using GpuScalarField = typename ScalarField::GpuField;\n  using CpuCurveConfig = SWCurveConfig<CpuBaseField, CpuScalarField>;\n  using GpuCurveConfig = SWCurveConfig<GpuBaseField, GpuScalarField>;\n\n  constexpr static bool kAIsZero = true;\n\n  static BaseField kA;\n  static BaseField kB;\n  static Point2<BaseField> kGenerator;\n\n  static void Init() {\n    static absl::once_flag once;\n    absl::call_once(once, []() {\n      kA = BaseField::Zero();\n      kB = BaseField(5);\n      kGenerator.x = BaseField(5);\n      kGenerator.y = BaseField(5);\n    });\n  }\n};\n\ntemplate <typename BaseField, typename ScalarField>\nBaseField SWCurveConfig<BaseField, ScalarField>::kA;\ntemplate <typename BaseField, typename ScalarField>\nBaseField SWCurveConfig<BaseField, ScalarField>::kB;\ntemplate <typename BaseField, typename ScalarField>\nPoint2<BaseField> SWCurveConfig<BaseField, ScalarField>::kGenerator;\n\nusing G1Curve = SWCurve<SWCurveConfig<GF7, GF7>>;\nusing AffinePoint = AffinePoint<G1Curve>;\nusing ProjectivePoint = ProjectivePoint<G1Curve>;\nusing JacobianPoint = JacobianPoint<G1Curve>;\nusing PointXYZZ = PointXYZZ<G1Curve>;\n\n}  // namespace test\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_TEST_SW_CURVE_CONFIG_H_\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/test/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"random\",\n    testonly = True,\n    hdrs = [\"random.h\"],\n    deps = [\n        \"//tachyon/base:parallelize\",\n        \"//tachyon/math/geometry:point_conversions\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/math/elliptic_curves/test/random.h",
    "content": "#ifndef TACHYON_MATH_ELLIPTIC_CURVES_TEST_RANDOM_H_\n#define TACHYON_MATH_ELLIPTIC_CURVES_TEST_RANDOM_H_\n\n#include <vector>\n\n#include \"tachyon/base/parallelize.h\"\n#include \"tachyon/math/geometry/point_conversions.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename Point>\nstd::vector<Point> CreatePseudoRandomPoints(size_t size) {\n  // NOTE(chokobole): Point::Random() is an expensive operation, which\n  // internally, randomly picks a scalar field and multiplies by a\n  // generator. In most case, including MSM, bases doesn't affect to a\n  // performance or test. So here it just produces pseudo random points by\n  // doubling from a first selected random point.\n  // TODO(chokobole): make |ConvertPoints()| use |BatchInverse()| internally,\n  // and use |ConvertPoints()|.\n  std::vector<Point> ret(size);\n  base::Parallelize(ret, [](absl::Span<Point> chunk) {\n    Point r = Point::Random();\n    for (Point& p : chunk) {\n      p = ConvertPoint<Point>(r);\n      r = ConvertPoint<Point>(r.Double());\n    }\n  });\n  return ret;\n}\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_ELLIPTIC_CURVES_TEST_RANDOM_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/BUILD.bazel",
    "content": "load(\"//bazel:tachyon.bzl\", \"if_gpu_is_configured\")\nload(\n    \"//bazel:tachyon_cc.bzl\",\n    \"tachyon_cc_library\",\n    \"tachyon_cc_unittest\",\n    \"tachyon_cuda_test\",\n    \"tachyon_cuda_unittest\",\n)\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"cubic_extension_field\",\n    hdrs = [\"cubic_extension_field.h\"],\n    deps = [\n        \":cyclotomic_multiplicative_subgroup\",\n        \":extension_field_base\",\n        \"//tachyon/base/buffer:copyable\",\n        \"//tachyon/base/json\",\n        \"@com_google_absl//absl/strings\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"cyclotomic_multiplicative_subgroup\",\n    hdrs = [\"cyclotomic_multiplicative_subgroup.h\"],\n    deps = [\n        \":finite_field\",\n        \"//tachyon/base:optional\",\n        \"//tachyon/base/containers:adapters\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"extended_packed_field_traits_forward\",\n    hdrs = [\"extended_packed_field_traits_forward.h\"],\n)\n\ntachyon_cc_library(\n    name = \"extension_field_base\",\n    hdrs = [\"extension_field_base.h\"],\n    deps = [\n        \":extended_packed_field_traits_forward\",\n        \":extension_field_traits_forward\",\n        \":packed_field_traits_forward\",\n        \"//tachyon/base/containers:container_util\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"extension_field_traits_forward\",\n    hdrs = [\"extension_field_traits_forward.h\"],\n)\n\ntachyon_cc_library(\n    name = \"finite_field\",\n    hdrs = [\"finite_field.h\"],\n    deps = [\n        \":finite_field_traits\",\n        \"//tachyon/math/base:field\",\n        \"//tachyon/math/finite_fields/square_root_algorithms\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"finite_field_forwards\",\n    hdrs = [\"finite_field_forwards.h\"],\n)\n\ntachyon_cc_library(\n    name = \"finite_field_traits\",\n    hdrs = [\"finite_field_traits.h\"],\n    deps = [\n        \":finite_field_forwards\",\n        \"//tachyon/math/matrix:cost_calculator_forward\",\n        \"@eigen_archive//:eigen3\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"fp2\",\n    hdrs = [\"fp2.h\"],\n    deps = [\n        \":extension_field_traits_forward\",\n        \":quadratic_extension_field\",\n        \"@com_google_absl//absl/base\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"fp3\",\n    hdrs = [\"fp3.h\"],\n    deps = [\n        \":cubic_extension_field\",\n        \":extension_field_traits_forward\",\n        \"//tachyon/math/base/gmp:gmp_util\",\n        \"@com_google_absl//absl/base\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"fp4\",\n    hdrs = [\"fp4.h\"],\n    deps = [\n        \":extension_field_traits_forward\",\n        \":quadratic_extension_field\",\n        \":quartic_extension_field\",\n        \"//tachyon/math/base/gmp:gmp_util\",\n        \"@com_google_absl//absl/base\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"fp6\",\n    hdrs = [\"fp6.h\"],\n    deps = [\n        \":cubic_extension_field\",\n        \":extension_field_traits_forward\",\n        \":quadratic_extension_field\",\n        \"//tachyon/math/base/gmp:gmp_util\",\n        \"@com_google_absl//absl/base\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"fp12\",\n    hdrs = [\"fp12.h\"],\n    deps = [\n        \":extension_field_traits_forward\",\n        \":quadratic_extension_field\",\n        \"//tachyon/math/base/gmp:gmp_util\",\n        \"@com_google_absl//absl/base\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"legendre_symbol\",\n    hdrs = [\"legendre_symbol.h\"],\n)\n\ntachyon_cc_library(\n    name = \"modulus\",\n    hdrs = [\"modulus.h\"],\n    deps = [\"//tachyon/math/base:big_int\"],\n)\n\ntachyon_cc_library(\n    name = \"packed_prime_field_base\",\n    hdrs = [\"packed_prime_field_base.h\"],\n    deps = [\n        \":packed_field_traits_forward\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/base/functional:callback\",\n        \"//tachyon/base/strings:string_util\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"packed_field_traits_forward\",\n    hdrs = [\"packed_field_traits_forward.h\"],\n)\n\ntachyon_cc_library(\n    name = \"packed_prime_field32_avx2\",\n    hdrs = [\"packed_prime_field32_avx2.h\"],\n    deps = [\n        \"//tachyon/base:compiler_specific\",\n        \"//tachyon/base/functional:callback\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"packed_prime_field32_avx512\",\n    hdrs = [\"packed_prime_field32_avx512.h\"],\n    deps = [\"//tachyon/base:compiler_specific\"],\n)\n\ntachyon_cc_library(\n    name = \"packed_prime_field32_neon\",\n    hdrs = [\"packed_prime_field32_neon.h\"],\n    deps = [\"//tachyon/base:compiler_specific\"],\n)\n\ntachyon_cc_library(\n    name = \"prime_field_base\",\n    hdrs = [\"prime_field_base.h\"],\n    deps = [\n        \":extension_field_traits_forward\",\n        \":finite_field\",\n        \":legendre_symbol\",\n        \":packed_field_traits_forward\",\n        \":prime_field_util\",\n        \"//tachyon/base:bits\",\n        \"//tachyon/base/json\",\n        \"//tachyon/base/strings:string_number_conversions\",\n        \"//tachyon/math/base/gmp:gmp_util\",\n        \"@com_google_absl//absl/hash\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"prime_field_conversions\",\n    hdrs = [\"prime_field_conversions.h\"],\n    deps = [\":finite_field_forwards\"],\n)\n\ntachyon_cc_library(\n    name = \"prime_field_fallback\",\n    hdrs = [\"prime_field_fallback.h\"],\n    deps = [\n        \":prime_field_base\",\n        \"//tachyon/base:compiler_specific\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base/containers:adapters\",\n        \"//tachyon/base/strings:string_util\",\n        \"//tachyon/math/base:arithmetics\",\n        \"//tachyon/math/base:byinverter\",\n        \"//tachyon/math/base/gmp:gmp_util\",\n        \"@com_google_absl//absl/base\",\n        \"@com_google_googletest//:gtest_prod\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"prime_field_gpu\",\n    hdrs = [\"prime_field_gpu.h\"],\n    deps = [\n        \":finite_field_forwards\",\n        \":modulus\",\n        \":prime_field_base\",\n        \"//tachyon/math/finite_fields/kernels:carry_chain\",\n        \"@com_google_absl//absl/base\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"prime_field_gpu_debug\",\n    hdrs = [\n        \"carry_chain.h\",\n        \"prime_field_gpu_debug.h\",\n        \"prime_field_ops_internal.h\",\n    ],\n    deps = [\":finite_field_forwards\"],\n)\n\ntachyon_cc_library(\n    name = \"prime_field_util\",\n    srcs = [\"prime_field_util.cc\"],\n    hdrs = [\"prime_field_util.h\"],\n    deps = [\n        \"//tachyon:export\",\n        \"@local_config_gmp//:gmp\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"prime_field_x86\",\n    hdrs = [\"prime_field_x86.h\"],\n    deps = [\n        \":modulus\",\n        \":prime_field_base\",\n        \"//tachyon/base:compiler_specific\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base/containers:adapters\",\n        \"//tachyon/base/strings:string_util\",\n        \"//tachyon/math/base:arithmetics\",\n        \"//tachyon/math/base/gmp:gmp_util\",\n        \"@com_google_googletest//:gtest_prod\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"quadratic_extension_field\",\n    hdrs = [\"quadratic_extension_field.h\"],\n    deps = [\n        \":cyclotomic_multiplicative_subgroup\",\n        \":extension_field_base\",\n        \"//tachyon/base/buffer:copyable\",\n        \"//tachyon/base/json\",\n        \"@com_google_absl//absl/strings\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"quartic_extension_field\",\n    hdrs = [\"quartic_extension_field.h\"],\n    deps = [\n        \":cyclotomic_multiplicative_subgroup\",\n        \":extension_field_base\",\n        \"//tachyon/base/buffer:copyable\",\n        \"//tachyon/base/json\",\n        \"@com_google_absl//absl/base\",\n        \"@com_google_absl//absl/strings\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"small_prime_field\",\n    hdrs = [\"small_prime_field.h\"],\n    deps = [\n        \":prime_field_base\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base:random\",\n        \"//tachyon/base/strings:string_number_conversions\",\n        \"//tachyon/base/strings:string_util\",\n        \"//tachyon/build:build_config\",\n        \"//tachyon/math/base:egcd\",\n        \"@com_google_absl//absl/base\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"small_prime_field_mont\",\n    hdrs = [\"small_prime_field_mont.h\"],\n    deps = [\n        \":prime_field_base\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base:random\",\n        \"//tachyon/base/strings:string_number_conversions\",\n        \"//tachyon/base/strings:string_util\",\n        \"//tachyon/build:build_config\",\n        \"@com_google_absl//absl/base\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"finite_fields_unittests\",\n    srcs = [\n        \"cubic_extension_field_unittest.cc\",\n        \"finite_field_unittest.cc\",\n        \"fp12_unittest.cc\",\n        \"fp2_unittest.cc\",\n        \"fp6_unittest.cc\",\n        \"modulus_unittest.cc\",\n        \"prime_field_base_unittest.cc\",\n        \"prime_field_generator_unittest.cc\",\n        \"prime_field_unittest.cc\",\n        \"quadratic_extension_field_unittest.cc\",\n        \"quartic_extension_field_unittest.cc\",\n    ] + select({\n        \"@platforms//cpu:x86_64\": [\"packed_prime_field_unittest.cc\"],\n        \"@platforms//cpu:aarch64\": [\"packed_prime_field_unittest.cc\"],\n        \"//conditions:default\": [],\n    }),\n    deps = [\n        \":modulus\",\n        \":packed_field_traits_forward\",\n        \":prime_field_base\",\n        \":prime_field_gpu_debug\",\n        \"//tachyon/base:auto_reset\",\n        \"//tachyon/base:bits\",\n        \"//tachyon/base:optional\",\n        \"//tachyon/base/buffer:vector_buffer\",\n        \"//tachyon/build:build_config\",\n        \"//tachyon/math/elliptic_curves/bls12/bls12_381:fq\",\n        \"//tachyon/math/elliptic_curves/bls12/bls12_381:fr\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:fq\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:fq12\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:fr\",\n        \"//tachyon/math/elliptic_curves/pasta/pallas:fq\",\n        \"//tachyon/math/elliptic_curves/pasta/pallas:fr\",\n        \"//tachyon/math/elliptic_curves/pasta/vesta:fq\",\n        \"//tachyon/math/elliptic_curves/pasta/vesta:fr\",\n        \"//tachyon/math/elliptic_curves/secp/secp256k1:fq\",\n        \"//tachyon/math/elliptic_curves/secp/secp256k1:fr\",\n        \"//tachyon/math/finite_fields/baby_bear\",\n        \"//tachyon/math/finite_fields/baby_bear:baby_bear4\",\n        \"//tachyon/math/finite_fields/binary_fields\",\n        \"//tachyon/math/finite_fields/goldilocks\",\n        \"//tachyon/math/finite_fields/koala_bear\",\n        \"//tachyon/math/finite_fields/mersenne31\",\n        \"//tachyon/math/finite_fields/test:finite_field_test\",\n        \"//tachyon/math/finite_fields/test:gf7\",\n        \"//tachyon/math/finite_fields/test:gf7_2\",\n        \"//tachyon/math/finite_fields/test:gf7_3\",\n        \"@com_google_absl//absl/hash:hash_testing\",\n    ] + select({\n        \"@platforms//cpu:x86_64\": [\n            \"//tachyon/math/finite_fields/baby_bear/internal:packed_baby_bear_avx2\",\n            \"//tachyon/math/finite_fields/baby_bear/internal:packed_baby_bear_avx512\",\n            \"//tachyon/math/finite_fields/koala_bear/internal:packed_koala_bear_avx2\",\n            \"//tachyon/math/finite_fields/koala_bear/internal:packed_koala_bear_avx512\",\n            \"//tachyon/math/finite_fields/mersenne31/internal:packed_mersenne31_avx2\",\n            \"//tachyon/math/finite_fields/mersenne31/internal:packed_mersenne31_avx512\",\n        ],\n        \"@platforms//cpu:aarch64\": [\n            \"//tachyon/math/finite_fields/baby_bear/internal:packed_baby_bear_neon\",\n            \"//tachyon/math/finite_fields/koala_bear/internal:packed_koala_bear_neon\",\n            \"//tachyon/math/finite_fields/mersenne31/internal:packed_mersenne31_neon\",\n        ],\n        \"//conditions:default\": [],\n    }),\n)\n\ntachyon_cuda_unittest(\n    name = \"finite_fields_gpu_unittests\",\n    srcs = if_gpu_is_configured([\"prime_field_gpu_unittest.cc\"]),\n    deps = [\n        \":prime_field_conversions\",\n        \"//tachyon/device/gpu:gpu_memory\",\n        \"//tachyon/math/finite_fields/kernels:prime_field_ops\",\n        \"//tachyon/math/finite_fields/test:finite_field_test\",\n        \"//tachyon/math/finite_fields/test:gf7\",\n        \"//tachyon/math/finite_fields/test:gf7_gpu\",\n        \"//tachyon/math/test:launch_op_macros\",\n    ],\n)\n\ntachyon_cuda_test(\n    name = \"finite_field_correctness_gpu_tests\",\n    size = \"small\",\n    srcs = if_gpu_is_configured([\"prime_field_correctness_gpu_test.cc\"]),\n    deps = [\n        \":prime_field_conversions\",\n        \"//tachyon/device/gpu:gpu_memory\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:fq\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:fq_gpu\",\n        \"//tachyon/math/finite_fields/kernels:prime_field_ops\",\n        \"//tachyon/math/finite_fields/test:finite_field_test\",\n        \"//tachyon/math/test:launch_op_macros\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/math/finite_fields/baby_bear/BUILD.bazel",
    "content": "load(\"@bazel_skylib//rules:common_settings.bzl\", \"string_flag\")\nload(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\nload(\"//tachyon/math/finite_fields/generator/prime_field_generator:build_defs.bzl\", \"SUBGROUP_GENERATOR\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nstring_flag(\n    name = SUBGROUP_GENERATOR,\n    build_setting_default = \"31\",\n)\n\ntachyon_cc_library(\n    name = \"baby_bear\",\n    hdrs = [\"baby_bear.h\"],\n    deps = [\"//tachyon/math/finite_fields/baby_bear/internal:packed_baby_bear\"],\n)\n\ntachyon_cc_library(\n    name = \"baby_bear4\",\n    hdrs = [\"baby_bear4.h\"],\n    deps = [\n        \"//tachyon/math/finite_fields/baby_bear/internal:baby_bear4\",\n        \"//tachyon/math/finite_fields/baby_bear/internal:packed_baby_bear4\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"packed_baby_bear4\",\n    hdrs = [\"packed_baby_bear4.h\"],\n    deps = [\n        \"//tachyon/math/finite_fields/baby_bear/internal:baby_bear4\",\n        \"//tachyon/math/finite_fields/baby_bear/internal:packed_baby_bear4\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/math/finite_fields/baby_bear/baby_bear.h",
    "content": "#ifndef TACHYON_MATH_FINITE_FIELDS_BABY_BEAR_BABY_BEAR_H_\n#define TACHYON_MATH_FINITE_FIELDS_BABY_BEAR_BABY_BEAR_H_\n\n#include \"tachyon/math/finite_fields/baby_bear/internal/packed_baby_bear.h\"\n\nnamespace tachyon::math {\n\ntemplate <>\nstruct PackedFieldTraits<BabyBear> {\n  using PackedField = PackedBabyBear;\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_BABY_BEAR_BABY_BEAR_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/baby_bear/baby_bear4.h",
    "content": "#ifndef TACHYON_MATH_FINITE_FIELDS_BABY_BEAR_BABY_BEAR4_H_\n#define TACHYON_MATH_FINITE_FIELDS_BABY_BEAR_BABY_BEAR4_H_\n\n#include \"tachyon/math/finite_fields/baby_bear/internal/baby_bear4.h\"\n#include \"tachyon/math/finite_fields/baby_bear/internal/packed_baby_bear4.h\"\n\nnamespace tachyon::math {\n\ntemplate <>\nstruct ExtendedPackedFieldTraits<BabyBear4> {\n  constexpr static bool kIsExtendedPackedField = false;\n  using ExtendedPackedField = PackedBabyBear4;\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_BABY_BEAR_BABY_BEAR4_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/baby_bear/internal/BUILD.bazel",
    "content": "load(\"//bazel:tachyon.bzl\", \"if_aarch64\", \"if_has_avx512\", \"if_x86_64\")\nload(\"//bazel:tachyon_cc.bzl\", \"tachyon_avx512_defines\", \"tachyon_cc_library\")\nload(\"//tachyon/math/finite_fields/generator/ext_field_generator:build_defs.bzl\", \"generate_fp4s\")\nload(\"//tachyon/math/finite_fields/generator/prime_field_generator:build_defs.bzl\", \"SUBGROUP_GENERATOR\", \"generate_fft_prime_fields\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ngenerate_fp4s(\n    name = \"baby_bear4\",\n    base_field = \"BabyBear\",\n    base_field_degree = 1,\n    base_field_hdr = \"tachyon/math/finite_fields/baby_bear/internal/baby_bear.h\",\n    class_name = \"BabyBear4\",\n    is_packed = False,\n    namespace = \"tachyon::math\",\n    # See https://github.com/Plonky3/Plonky3/blob/d9ef390/baby-bear/src/baby_bear.rs#L80.\n    non_residue = [\"11\"],\n    deps = [\":baby_bear\"],\n)\n\ngenerate_fp4s(\n    name = \"packed_baby_bear4\",\n    base_field = \"PackedBabyBear\",\n    base_field_degree = 1,\n    base_field_hdr = \"tachyon/math/finite_fields/baby_bear/internal/packed_baby_bear.h\",\n    class_name = \"PackedBabyBear4\",\n    is_packed = True,\n    namespace = \"tachyon::math\",\n    # See https://github.com/Plonky3/Plonky3/blob/d9ef390/baby-bear/src/baby_bear.rs#L80.\n    non_residue = [\"11\"],\n    deps = [\":packed_baby_bear\"],\n)\n\ngenerate_fft_prime_fields(\n    name = \"baby_bear\",\n    class_name = \"BabyBear\",\n    # 2³¹ - 2²⁷ + 1\n    # Hex: 0x78000001\n    modulus = \"2013265921\",\n    namespace = \"tachyon::math\",\n    subgroup_generator = \"//tachyon/math/finite_fields/baby_bear:\" + SUBGROUP_GENERATOR,\n    use_montgomery = True,\n)\n\ntachyon_cc_library(\n    name = \"packed_baby_bear\",\n    hdrs = [\"packed_baby_bear.h\"],\n    defines = tachyon_avx512_defines(),\n    deps = [\n        \":packed_baby_bear_neon\",\n        \"//tachyon/build:build_config\",\n        \"//tachyon/math/finite_fields:extended_packed_field_traits_forward\",\n        \"//tachyon/math/finite_fields:finite_field_traits\",\n        \"//tachyon/math/matrix:prime_field_num_traits\",\n    ] + if_has_avx512(\n        [\":packed_baby_bear_avx512\"],\n        [\":packed_baby_bear_avx2\"],\n    ),\n)\n\ntachyon_cc_library(\n    name = \"packed_baby_bear_avx2\",\n    srcs = if_x86_64([\"packed_baby_bear_avx2.cc\"]),\n    hdrs = if_x86_64([\"packed_baby_bear_avx2.h\"]),\n    copts = if_x86_64([\"-mavx2\"]),\n    deps = [\n        \":baby_bear\",\n        \"//tachyon:export\",\n        \"//tachyon/math/finite_fields:packed_prime_field32_avx2\",\n        \"//tachyon/math/finite_fields:packed_prime_field_base\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"packed_baby_bear_avx512\",\n    srcs = if_x86_64([\"packed_baby_bear_avx512.cc\"]),\n    hdrs = if_x86_64([\"packed_baby_bear_avx512.h\"]),\n    copts = if_x86_64([\n        \"-mavx512f\",\n        # NOTE(chokobole): See https://gitlab.com/libeigen/eigen/-/blob/0b51f76/Eigen/src/Core/util/ConfigureVectorization.h#L248.\n        \"-mfma\",\n    ]),\n    deps = [\n        \":baby_bear\",\n        \"//tachyon:export\",\n        \"//tachyon/math/finite_fields:packed_prime_field32_avx512\",\n        \"//tachyon/math/finite_fields:packed_prime_field_base\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"packed_baby_bear_neon\",\n    srcs = if_aarch64([\"packed_baby_bear_neon.cc\"]),\n    hdrs = if_aarch64([\"packed_baby_bear_neon.h\"]),\n    deps = [\n        \":baby_bear\",\n        \"//tachyon:export\",\n        \"//tachyon/math/finite_fields:packed_prime_field32_neon\",\n        \"//tachyon/math/finite_fields:packed_prime_field_base\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/math/finite_fields/baby_bear/internal/packed_baby_bear.h",
    "content": "#ifndef TACHYON_MATH_FINITE_FIELDS_BABY_BEAR_INTERNAL_PACKED_BABY_BEAR_H_\n#define TACHYON_MATH_FINITE_FIELDS_BABY_BEAR_INTERNAL_PACKED_BABY_BEAR_H_\n\n#include \"tachyon/build/build_config.h\"\n\n#if ARCH_CPU_X86_64\n#if defined(TACHYON_HAS_AVX512)\n#include \"tachyon/math/finite_fields/baby_bear/internal/packed_baby_bear_avx512.h\"\n#else\n#include \"tachyon/math/finite_fields/baby_bear/internal/packed_baby_bear_avx2.h\"\n#endif\n#elif ARCH_CPU_ARM64\n#include \"tachyon/math/finite_fields/baby_bear/internal/packed_baby_bear_neon.h\"\n#endif\n#include \"tachyon/math/finite_fields/finite_field_traits.h\"\n#include \"tachyon/math/matrix/prime_field_num_traits.h\"\n\nnamespace tachyon::math {\n\n#if ARCH_CPU_X86_64\n#if defined(TACHYON_HAS_AVX512)\nusing PackedBabyBear = PackedBabyBearAVX512;\n#else\nusing PackedBabyBear = PackedBabyBearAVX2;\n#endif\n#elif ARCH_CPU_ARM64\nusing PackedBabyBear = PackedBabyBearNeon;\n#endif\n\ntemplate <>\nstruct FiniteFieldTraits<PackedBabyBear> {\n  static constexpr bool kIsPrimeField = true;\n  static constexpr bool kIsPackedPrimeField = true;\n  static constexpr bool kIsExtensionField = false;\n\n  using PrimeField = BabyBear;\n  using Config = BabyBear::Config;\n};\n\n}  // namespace tachyon::math\n\nnamespace Eigen {\n\ntemplate <>\nstruct NumTraits<tachyon::math::PackedBabyBear>\n    : GenericNumTraits<tachyon::math::PackedBabyBear> {\n  using PrimeField = tachyon::math::BabyBear;\n  constexpr static size_t N = tachyon::math::PackedBabyBear::N;\n\n  enum {\n    IsInteger = 1,\n    IsField = 1,\n    IsSigned = 0,\n    IsComplex = 0,\n    RequireInitialization = 0,\n    ReadCost = tachyon::math::CostCalculator<PrimeField>::ComputeReadCost() * N,\n    AddCost = tachyon::math::CostCalculator<PrimeField>::ComputeAddCost() * N,\n    MulCost = tachyon::math::CostCalculator<PrimeField>::ComputeMulCost() * N,\n  };\n};\n\n}  // namespace Eigen\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_BABY_BEAR_INTERNAL_PACKED_BABY_BEAR_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/baby_bear/internal/packed_baby_bear_avx2.cc",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#include \"tachyon/math/finite_fields/baby_bear/internal/packed_baby_bear_avx2.h\"\n\n#include <immintrin.h>\n\n#include \"tachyon/math/finite_fields/packed_prime_field32_avx2.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\n__m256i kP;\n__m256i kInv;\n__m256i kZero;\n__m256i kOne;\n__m256i kMinusOne;\n__m256i kTwoInv;\n__m256i kRawOne;\n\n__m256i ToVector(const PackedBabyBearAVX2& packed) {\n  return _mm256_loadu_si256(\n      reinterpret_cast<const __m256i_u*>(packed.values().data()));\n}\n\nPackedBabyBearAVX2 FromVector(__m256i vector) {\n  PackedBabyBearAVX2 ret;\n  _mm256_storeu_si256(reinterpret_cast<__m256i_u*>(ret.values().data()),\n                      vector);\n  return ret;\n}\n\n__m256i Add(__m256i lhs, __m256i rhs) { return AddMod32(lhs, rhs, kP); }\n\n__m256i Sub(__m256i lhs, __m256i rhs) { return SubMod32(lhs, rhs, kP); }\n\n__m256i Negate(__m256i val) { return NegateMod32(val, kP); }\n\n__m256i Mul(__m256i lhs, __m256i rhs) {\n  return MontMulMod32(lhs, rhs, kP, kInv);\n}\n\n}  // namespace\n\nPackedBabyBearAVX2::PackedBabyBearAVX2(uint32_t value) {\n  __m256i vector = _mm256_set1_epi32(BabyBear::Config::ToMontgomery(value));\n  _mm256_storeu_si256(reinterpret_cast<__m256i_u*>(values_.data()), vector);\n}\n\n// static\nvoid PackedBabyBearAVX2::Init() {\n  BabyBear::Init();\n  kP = _mm256_set1_epi32(BabyBear::Config::kModulus);\n  kInv = _mm256_set1_epi32(BabyBear::Config::kInverse32);\n  kZero = _mm256_set1_epi32(0);\n  kOne = _mm256_set1_epi32(BabyBear::Config::kOne);\n  kMinusOne = _mm256_set1_epi32(BabyBear::Config::kMinusOne);\n  kTwoInv = _mm256_set1_epi32(BabyBear::Config::kTwoInv);\n  kRawOne = _mm256_set1_epi32(1);\n}\n\n// static\nPackedBabyBearAVX2 PackedBabyBearAVX2::Zero() { return FromVector(kZero); }\n\n// static\nPackedBabyBearAVX2 PackedBabyBearAVX2::One() { return FromVector(kOne); }\n\n// static\nPackedBabyBearAVX2 PackedBabyBearAVX2::MinusOne() {\n  return FromVector(kMinusOne);\n}\n\n// static\nPackedBabyBearAVX2 PackedBabyBearAVX2::TwoInv() { return FromVector(kTwoInv); }\n\n// static\nPackedBabyBearAVX2 PackedBabyBearAVX2::RawOne() { return FromVector(kRawOne); }\n\n// static\nPackedBabyBearAVX2 PackedBabyBearAVX2::Broadcast(const PrimeField& value) {\n  return FromVector(_mm256_set1_epi32(value.value()));\n}\n\nPackedBabyBearAVX2 PackedBabyBearAVX2::Add(\n    const PackedBabyBearAVX2& other) const {\n  return FromVector(math::Add(ToVector(*this), ToVector(other)));\n}\n\nPackedBabyBearAVX2 PackedBabyBearAVX2::Sub(\n    const PackedBabyBearAVX2& other) const {\n  return FromVector(math::Sub(ToVector(*this), ToVector(other)));\n}\n\nPackedBabyBearAVX2 PackedBabyBearAVX2::Negate() const {\n  return FromVector(math::Negate(ToVector(*this)));\n}\n\nPackedBabyBearAVX2 PackedBabyBearAVX2::Mul(\n    const PackedBabyBearAVX2& other) const {\n  return FromVector(math::Mul(ToVector(*this), ToVector(other)));\n}\n\nPackedBabyBearAVX2 PackedBabyBearAVX2::Exp3() const {\n  return FromVector(math::Exp3(ToVector(*this), kP, kInv));\n}\n\nPackedBabyBearAVX2 PackedBabyBearAVX2::Exp5() const {\n  return FromVector(math::Exp5(ToVector(*this), kP, kInv));\n}\n\nPackedBabyBearAVX2 PackedBabyBearAVX2::Exp7() const {\n  return FromVector(math::Exp7(ToVector(*this), kP, kInv));\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/finite_fields/baby_bear/internal/packed_baby_bear_avx2.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_MATH_FINITE_FIELDS_BABY_BEAR_INTERNAL_PACKED_BABY_BEAR_AVX2_H_\n#define TACHYON_MATH_FINITE_FIELDS_BABY_BEAR_INTERNAL_PACKED_BABY_BEAR_AVX2_H_\n\n#include <stddef.h>\n\n#include \"tachyon/export.h\"\n#include \"tachyon/math/finite_fields/baby_bear/internal/baby_bear.h\"\n#include \"tachyon/math/finite_fields/packed_prime_field_base.h\"\n\nnamespace tachyon::math {\n\nclass PackedBabyBearAVX2;\n\ntemplate <>\nstruct PackedFieldTraits<PackedBabyBearAVX2> {\n  using Field = BabyBear;\n\n  constexpr static size_t N = 8;\n};\n\nclass TACHYON_EXPORT PackedBabyBearAVX2 final\n    : public PackedPrimeFieldBase<PackedBabyBearAVX2> {\n public:\n  using PrimeField = BabyBear;\n\n  constexpr static size_t N = 8;\n\n  PackedBabyBearAVX2() = default;\n  // NOTE(chokobole): This is needed by Eigen matrix.\n  explicit PackedBabyBearAVX2(uint32_t value);\n  PackedBabyBearAVX2(const PackedBabyBearAVX2& other) = default;\n  PackedBabyBearAVX2& operator=(const PackedBabyBearAVX2& other) = default;\n  PackedBabyBearAVX2(PackedBabyBearAVX2&& other) = default;\n  PackedBabyBearAVX2& operator=(PackedBabyBearAVX2&& other) = default;\n\n  static void Init();\n\n  static PackedBabyBearAVX2 Zero();\n\n  static PackedBabyBearAVX2 One();\n\n  static PackedBabyBearAVX2 MinusOne();\n\n  static PackedBabyBearAVX2 TwoInv();\n\n  static PackedBabyBearAVX2 RawOne();\n\n  static PackedBabyBearAVX2 Broadcast(const PrimeField& value);\n\n  // AdditiveSemigroup methods\n  PackedBabyBearAVX2 Add(const PackedBabyBearAVX2& other) const;\n\n  // AdditiveGroup methods\n  PackedBabyBearAVX2 Sub(const PackedBabyBearAVX2& other) const;\n\n  PackedBabyBearAVX2 Negate() const;\n\n  // MultiplicativeSemigroup methods\n  PackedBabyBearAVX2 Mul(const PackedBabyBearAVX2& other) const;\n\n  PackedBabyBearAVX2 Exp3() const;\n  PackedBabyBearAVX2 Exp5() const;\n  PackedBabyBearAVX2 Exp7() const;\n};\n\n}  // namespace tachyon::math\n\n#endif  //  TACHYON_MATH_FINITE_FIELDS_BABY_BEAR_INTERNAL_PACKED_BABY_BEAR_AVX2_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/baby_bear/internal/packed_baby_bear_avx512.cc",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#if defined(TACHYON_HAS_AVX512)\n\n#include \"tachyon/math/finite_fields/baby_bear/internal/packed_baby_bear_avx512.h\"\n\n#include <immintrin.h>\n\n#include \"tachyon/math/finite_fields/packed_prime_field32_avx512.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\n__m512i kP;\n__m512i kInv;\n__m512i kZero;\n__m512i kOne;\n__m512i kMinusOne;\n__m512i kTwoInv;\n__m512i kRawOne;\n\n__m512i ToVector(const PackedBabyBearAVX512& packed) {\n  return _mm512_loadu_si512(packed.values().data());\n}\n\nPackedBabyBearAVX512 FromVector(__m512i vector) {\n  PackedBabyBearAVX512 ret;\n  _mm512_storeu_si512(ret.values().data(), vector);\n  return ret;\n}\n\n__m512i Add(__m512i lhs, __m512i rhs) { return AddMod32(lhs, rhs, kP); }\n\n__m512i Sub(__m512i lhs, __m512i rhs) { return SubMod32(lhs, rhs, kP); }\n\n__m512i Negate(__m512i val) { return NegateMod32(val, kP); }\n\n__m512i Mul(__m512i lhs, __m512i rhs) {\n  return MontMulMod32(lhs, rhs, kP, kInv);\n}\n\n}  // namespace\n\nPackedBabyBearAVX512::PackedBabyBearAVX512(uint32_t value) {\n  __m512i vector = _mm512_set1_epi32(BabyBear::Config::ToMontgomery(value));\n  _mm512_storeu_si512(values_.data(), vector);\n}\n\n// static\nvoid PackedBabyBearAVX512::Init() {\n  BabyBear::Init();\n  kP = _mm512_set1_epi32(BabyBear::Config::kModulus);\n  kInv = _mm512_set1_epi32(BabyBear::Config::kInverse32);\n  kZero = _mm512_set1_epi32(0);\n  kOne = _mm512_set1_epi32(BabyBear::Config::kOne);\n  kMinusOne = _mm512_set1_epi32(BabyBear::Config::kMinusOne);\n  kTwoInv = _mm512_set1_epi32(BabyBear::Config::kTwoInv);\n  kRawOne = _mm512_set1_epi32(1);\n}\n\n// static\nPackedBabyBearAVX512 PackedBabyBearAVX512::Zero() { return FromVector(kZero); }\n\n// static\nPackedBabyBearAVX512 PackedBabyBearAVX512::One() { return FromVector(kOne); }\n\n// static\nPackedBabyBearAVX512 PackedBabyBearAVX512::MinusOne() {\n  return FromVector(kMinusOne);\n}\n\n// static\nPackedBabyBearAVX512 PackedBabyBearAVX512::TwoInv() {\n  return FromVector(kTwoInv);\n}\n\n// static\nPackedBabyBearAVX512 PackedBabyBearAVX512::RawOne() {\n  return FromVector(kRawOne);\n}\n\n// static\nPackedBabyBearAVX512 PackedBabyBearAVX512::Broadcast(const PrimeField& value) {\n  return FromVector(_mm512_set1_epi32(value.value()));\n}\n\nPackedBabyBearAVX512 PackedBabyBearAVX512::Add(\n    const PackedBabyBearAVX512& other) const {\n  return FromVector(math::Add(ToVector(*this), ToVector(other)));\n}\n\nPackedBabyBearAVX512 PackedBabyBearAVX512::Sub(\n    const PackedBabyBearAVX512& other) const {\n  return FromVector(math::Sub(ToVector(*this), ToVector(other)));\n}\n\nPackedBabyBearAVX512 PackedBabyBearAVX512::Negate() const {\n  return FromVector(math::Negate(ToVector(*this)));\n}\n\nPackedBabyBearAVX512 PackedBabyBearAVX512::Mul(\n    const PackedBabyBearAVX512& other) const {\n  return FromVector(math::Mul(ToVector(*this), ToVector(other)));\n}\n\n}  // namespace tachyon::math\n\n#endif  // defined(TACHYON_HAS_AVX512)\n"
  },
  {
    "path": "tachyon/math/finite_fields/baby_bear/internal/packed_baby_bear_avx512.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_MATH_FINITE_FIELDS_BABY_BEAR_INTERNAL_PACKED_BABY_BEAR_AVX512_H_\n#define TACHYON_MATH_FINITE_FIELDS_BABY_BEAR_INTERNAL_PACKED_BABY_BEAR_AVX512_H_\n\n#include <stddef.h>\n\n#include \"tachyon/export.h\"\n#include \"tachyon/math/finite_fields/baby_bear/internal/baby_bear.h\"\n#include \"tachyon/math/finite_fields/packed_prime_field_base.h\"\n\nnamespace tachyon::math {\n\nclass PackedBabyBearAVX512;\n\ntemplate <>\nstruct PackedFieldTraits<PackedBabyBearAVX512> {\n  using Field = BabyBear;\n\n  constexpr static size_t N = 16;\n};\n\nclass TACHYON_EXPORT PackedBabyBearAVX512 final\n    : public PackedPrimeFieldBase<PackedBabyBearAVX512> {\n public:\n  using PrimeField = BabyBear;\n\n  constexpr static size_t N = 16;\n\n  PackedBabyBearAVX512() = default;\n  // NOTE(chokobole): This is needed by Eigen matrix.\n  explicit PackedBabyBearAVX512(uint32_t value);\n  PackedBabyBearAVX512(const PackedBabyBearAVX512& other) = default;\n  PackedBabyBearAVX512& operator=(const PackedBabyBearAVX512& other) = default;\n  PackedBabyBearAVX512(PackedBabyBearAVX512&& other) = default;\n  PackedBabyBearAVX512& operator=(PackedBabyBearAVX512&& other) = default;\n\n  static void Init();\n\n  static PackedBabyBearAVX512 Zero();\n\n  static PackedBabyBearAVX512 One();\n\n  static PackedBabyBearAVX512 MinusOne();\n\n  static PackedBabyBearAVX512 TwoInv();\n\n  static PackedBabyBearAVX512 RawOne();\n\n  static PackedBabyBearAVX512 Broadcast(const PrimeField& value);\n\n  // AdditiveSemigroup methods\n  PackedBabyBearAVX512 Add(const PackedBabyBearAVX512& other) const;\n\n  // AdditiveGroup methods\n  PackedBabyBearAVX512 Sub(const PackedBabyBearAVX512& other) const;\n\n  PackedBabyBearAVX512 Negate() const;\n\n  // MultiplicativeSemigroup methods\n  PackedBabyBearAVX512 Mul(const PackedBabyBearAVX512& other) const;\n};\n\n}  // namespace tachyon::math\n\n#endif  //  TACHYON_MATH_FINITE_FIELDS_BABY_BEAR_INTERNAL_PACKED_BABY_BEAR_AVX512_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/baby_bear/internal/packed_baby_bear_neon.cc",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#include \"tachyon/math/finite_fields/baby_bear/internal/packed_baby_bear_neon.h\"\n\n#include <arm_neon.h>\n\n#include \"tachyon/math/finite_fields/packed_prime_field32_neon.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\nuint32x4_t kP;\nuint32x4_t kInv;\nuint32x4_t kZero;\nuint32x4_t kOne;\nuint32x4_t kMinusOne;\nuint32x4_t kTwoInv;\nuint32x4_t kRawOne;\n\nuint32x4_t ToVector(const PackedBabyBearNeon& packed) {\n  return vld1q_u32(reinterpret_cast<const uint32_t*>(packed.values().data()));\n}\n\nPackedBabyBearNeon FromVector(uint32x4_t vector) {\n  PackedBabyBearNeon ret;\n  vst1q_u32(reinterpret_cast<uint32_t*>(ret.values().data()), vector);\n  return ret;\n}\n\nuint32x4_t Add(uint32x4_t lhs, uint32x4_t rhs) {\n  return AddMod32(lhs, rhs, kP);\n}\n\nuint32x4_t Sub(uint32x4_t lhs, uint32x4_t rhs) {\n  return SubMod32(lhs, rhs, kP);\n}\n\nuint32x4_t Negate(uint32x4_t val) { return NegateMod32(val, kP); }\n\nuint32x4_t Mul(uint32x4_t lhs, uint32x4_t rhs) {\n  return MontMulMod32(lhs, rhs, kP, kInv);\n}\n\n}  // namespace\n\nPackedBabyBearNeon::PackedBabyBearNeon(uint32_t value) {\n  uint32x4_t vector = vdupq_n_u32(BabyBear::Config::ToMontgomery(value));\n  vst1q_u32(reinterpret_cast<uint32_t*>(values_.data()), vector);\n}\n\n// static\nvoid PackedBabyBearNeon::Init() {\n  BabyBear::Init();\n  kP = vdupq_n_u32(BabyBear::Config::kModulus);\n  kInv = vdupq_n_u32(BabyBear::Config::kInverse32);\n  kZero = vdupq_n_u32(0);\n  kOne = vdupq_n_u32(BabyBear::Config::kOne);\n  kMinusOne = vdupq_n_u32(BabyBear::Config::kMinusOne);\n  kTwoInv = vdupq_n_u32(BabyBear::Config::kTwoInv);\n  kRawOne = vdupq_n_u32(1);\n}\n\n// static\nPackedBabyBearNeon PackedBabyBearNeon::Zero() { return FromVector(kZero); }\n\n// static\nPackedBabyBearNeon PackedBabyBearNeon::One() { return FromVector(kOne); }\n\n// static\nPackedBabyBearNeon PackedBabyBearNeon::MinusOne() {\n  return FromVector(kMinusOne);\n}\n\n// static\nPackedBabyBearNeon PackedBabyBearNeon::TwoInv() { return FromVector(kTwoInv); }\n\n// static\nPackedBabyBearNeon PackedBabyBearNeon::RawOne() { return FromVector(kRawOne); }\n\n// static\nPackedBabyBearNeon PackedBabyBearNeon::Broadcast(const PrimeField& value) {\n  return FromVector(vdupq_n_u32(value.value()));\n}\n\nPackedBabyBearNeon PackedBabyBearNeon::Add(\n    const PackedBabyBearNeon& other) const {\n  return FromVector(math::Add(ToVector(*this), ToVector(other)));\n}\n\nPackedBabyBearNeon PackedBabyBearNeon::Sub(\n    const PackedBabyBearNeon& other) const {\n  return FromVector(math::Sub(ToVector(*this), ToVector(other)));\n}\n\nPackedBabyBearNeon PackedBabyBearNeon::Negate() const {\n  return FromVector(math::Negate(ToVector(*this)));\n}\n\nPackedBabyBearNeon PackedBabyBearNeon::Mul(\n    const PackedBabyBearNeon& other) const {\n  return FromVector(math::Mul(ToVector(*this), ToVector(other)));\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/finite_fields/baby_bear/internal/packed_baby_bear_neon.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_MATH_FINITE_FIELDS_BABY_BEAR_INTERNAL_PACKED_BABY_BEAR_NEON_H_\n#define TACHYON_MATH_FINITE_FIELDS_BABY_BEAR_INTERNAL_PACKED_BABY_BEAR_NEON_H_\n\n#include <stddef.h>\n\n#include \"tachyon/export.h\"\n#include \"tachyon/math/finite_fields/baby_bear/internal/baby_bear.h\"\n#include \"tachyon/math/finite_fields/packed_prime_field_base.h\"\n\nnamespace tachyon::math {\n\nclass PackedBabyBearNeon;\n\ntemplate <>\nstruct PackedFieldTraits<PackedBabyBearNeon> {\n  using Field = BabyBear;\n\n  constexpr static size_t N = 4;\n};\n\nclass TACHYON_EXPORT PackedBabyBearNeon final\n    : public PackedPrimeFieldBase<PackedBabyBearNeon> {\n public:\n  using PrimeField = BabyBear;\n\n  constexpr static size_t N = 4;\n\n  PackedBabyBearNeon() = default;\n  // NOTE(chokobole): This is needed by Eigen matrix.\n  explicit PackedBabyBearNeon(uint32_t value);\n  PackedBabyBearNeon(const PackedBabyBearNeon& other) = default;\n  PackedBabyBearNeon& operator=(const PackedBabyBearNeon& other) = default;\n  PackedBabyBearNeon(PackedBabyBearNeon&& other) = default;\n  PackedBabyBearNeon& operator=(PackedBabyBearNeon&& other) = default;\n\n  static void Init();\n\n  static PackedBabyBearNeon Zero();\n\n  static PackedBabyBearNeon One();\n\n  static PackedBabyBearNeon MinusOne();\n\n  static PackedBabyBearNeon TwoInv();\n\n  static PackedBabyBearNeon RawOne();\n\n  static PackedBabyBearNeon Broadcast(const PrimeField& value);\n\n  // AdditiveSemigroup methods\n  PackedBabyBearNeon Add(const PackedBabyBearNeon& other) const;\n\n  // AdditiveGroup methods\n  PackedBabyBearNeon Sub(const PackedBabyBearNeon& other) const;\n\n  PackedBabyBearNeon Negate() const;\n\n  // MultiplicativeSemigroup methods\n  PackedBabyBearNeon Mul(const PackedBabyBearNeon& other) const;\n};\n\n}  // namespace tachyon::math\n\n#endif  //  TACHYON_MATH_FINITE_FIELDS_BABY_BEAR_INTERNAL_PACKED_BABY_BEAR_NEON_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/baby_bear/packed_baby_bear4.h",
    "content": "#ifndef TACHYON_MATH_FINITE_FIELDS_BABY_BEAR_PACKED_BABY_BEAR4_H_\n#define TACHYON_MATH_FINITE_FIELDS_BABY_BEAR_PACKED_BABY_BEAR4_H_\n\n#include \"tachyon/math/finite_fields/baby_bear/internal/packed_baby_bear4.h\"\n\nnamespace tachyon::math {\n\ntemplate <>\nstruct ExtendedPackedFieldTraits<PackedBabyBear4> {\n  constexpr static bool kIsExtendedPackedField = true;\n  // Note(ashjeong): |ExtendedPackedField| is defaulted as itself. See\n  // extension_field_base.h to see how it is used.\n  using ExtendedPackedField = PackedBabyBear4;\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_BABY_BEAR_PACKED_BABY_BEAR4_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/binary_fields/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\nload(\n    \"//tachyon/math/finite_fields/generator/binary_field_generator:build_defs.bzl\",\n    \"generate_binary_fields\",\n)\n\npackage(default_visibility = [\"//visibility:public\"])\n\nBITS = [\n    1,\n    2,\n    4,\n    8,\n    16,\n    32,\n    64,\n    128,\n]\n\n[\n    generate_binary_fields(\n        name = \"binary_field{}\".format(bit),\n        class_name = \"BinaryField{}\".format(bit),\n        modulus = str(1 << bit),\n        namespace = \"tachyon::math\",\n    )\n    for bit in BITS\n]\n\ntachyon_cc_library(\n    name = \"binary_field\",\n    hdrs = [\n        \"binary_field.h\",\n        \"binary_field_traits_forward.h\",\n        \"binary_tower_operations.h\",\n    ],\n    deps = [\n        \"//tachyon/base:logging\",\n        \"//tachyon/base:random\",\n        \"//tachyon/base/numerics:safe_conversions\",\n        \"//tachyon/base/strings:string_number_conversions\",\n        \"//tachyon/base/strings:string_util\",\n        \"//tachyon/build:build_config\",\n        \"//tachyon/math/finite_fields:finite_field\",\n        \"@com_google_absl//absl/base\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"binary_fields\",\n    hdrs = [\"binary_fields.h\"],\n    # buildifier: leave-alone\n    deps = [\n        \":binary_field1\",\n        \":binary_field2\",\n        \":binary_field4\",\n        \":binary_field8\",\n        \":binary_field16\",\n        \":binary_field32\",\n        \":binary_field64\",\n        \":binary_field128\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"binary_fields_unittests\",\n    srcs = [\"binary_fields_unittest.cc\"],\n    deps = [\":binary_fields\"],\n)\n"
  },
  {
    "path": "tachyon/math/finite_fields/binary_fields/binary_field.h",
    "content": "// Copyright 2023 Ulvetanna Inc.\n// Use of this source code is governed by a Apache-2.0 style license that\n// can be found in the LICENSE.ulvetanna file.\n\n#ifndef TACHYON_MATH_FINITE_FIELDS_BINARY_FIELDS_BINARY_FIELD_H_\n#define TACHYON_MATH_FINITE_FIELDS_BINARY_FIELDS_BINARY_FIELD_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <limits>\n#include <optional>\n#include <string>\n#include <tuple>\n#include <type_traits>\n\n#include \"absl/base/call_once.h\"\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/numerics/safe_conversions.h\"\n#include \"tachyon/base/random.h\"\n#include \"tachyon/base/strings/string_number_conversions.h\"\n#include \"tachyon/base/strings/string_util.h\"\n#include \"tachyon/build/build_config.h\"\n#include \"tachyon/math/finite_fields/binary_fields/binary_field_traits_forward.h\"\n#include \"tachyon/math/finite_fields/binary_fields/binary_tower_operations.h\"\n#include \"tachyon/math/finite_fields/finite_field.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename _Config>\nclass BinaryField final : public FiniteField<BinaryField<_Config>> {\n public:\n  constexpr static size_t kBits = _Config::kModulusBits - 1;\n  constexpr static size_t kLimbNums = (kBits + 63) / 64;\n  constexpr static size_t N = kLimbNums;\n\n  using Config = _Config;\n  using Type = std::conditional_t<\n      kBits <= 8, uint8_t,\n      std::conditional_t<\n          kBits == 16, uint16_t,\n          std::conditional_t<\n              kBits == 32, uint32_t,\n              std::conditional_t<kBits == 64, uint64_t, BigInt<2>>>>>;\n  using SubField = BinaryField<typename BinaryFieldTraits<Config>::SubConfig>;\n  using BigIntTy = BigInt<N>;\n  using value_type = Type;\n\n  using CpuField = BinaryField<Config>;\n  using GpuField = BinaryField<Config>;\n\n  constexpr BinaryField() = default;\n  template <typename T, std::enable_if_t<std::is_integral_v<T>>* = nullptr>\n  constexpr explicit BinaryField(T value) : value_(value) {\n    if constexpr (kBits <= 64) {\n      DCHECK(base::IsValueInRangeForNumericType<Type>(value));\n      DCHECK_LE(static_cast<Type>(value), GetMax());\n    }\n  }\n\n  constexpr explicit BinaryField(BigInt<1> value) : BinaryField(value[0]) {\n    static_assert(kBits <= 64);\n  }\n  constexpr explicit BinaryField(BigInt<2> value) : value_(value) {\n    static_assert(kBits == 128);\n  }\n  constexpr BinaryField(const BinaryField& other) = default;\n  constexpr BinaryField& operator=(const BinaryField& other) = default;\n  constexpr BinaryField(BinaryField&& other) = default;\n  constexpr BinaryField& operator=(BinaryField&& other) = default;\n\n  constexpr static BinaryField Zero() { return BinaryField(); }\n  constexpr static BinaryField One() { return BinaryField(1); }\n  constexpr static BinaryField MinusOne() { return One(); }\n  constexpr static BinaryField TwoInv() { return *BinaryField(2).Inverse(); }\n\n  static BinaryField Random() {\n    if constexpr (kBits <= 64) {\n      return BinaryField(\n          base::Uniform(base::Range<Type, true, true>::Until(GetMax())));\n    } else {\n      static_assert(kBits == 128);\n      return BinaryField(BigInt<2>::Random());\n    }\n  }\n\n  static std::optional<BinaryField> FromDecString(std::string_view str) {\n    if constexpr (kBits <= 64) {\n      using MaybePromotedType =\n          std::conditional_t<(kBits <= 32), uint32_t, Type>;\n      MaybePromotedType value;\n      if (!absl::SimpleAtoi(str, &value)) return std::nullopt;\n      if constexpr (kBits <= 16) {\n        if (value > GetMax()) {\n          LOG(ERROR) << \"value(\" << str\n                     << \") is greater than or equal to modulus\";\n          return std::nullopt;\n        }\n      }\n      return BinaryField(value);\n    } else {\n      static_assert(kBits == 128);\n      std::optional<BigInt<2>> value = BigInt<2>::FromDecString(str);\n      if (!value) return std::nullopt;\n      return BinaryField(*value);\n    }\n  }\n  static std::optional<BinaryField> FromHexString(std::string_view str) {\n    if constexpr (kBits <= 64) {\n      using MaybePromotedType =\n          std::conditional_t<(kBits <= 32), uint32_t, Type>;\n      MaybePromotedType value;\n      if (!absl::SimpleHexAtoi(str, &value)) return std::nullopt;\n      if constexpr (kBits <= 16) {\n        if (value > GetMax()) {\n          LOG(ERROR) << \"value(\" << str\n                     << \") is greater than or equal to modulus\";\n          return std::nullopt;\n        }\n      }\n      return BinaryField(value);\n    } else {\n      static_assert(kBits == 128);\n      std::optional<BigInt<2>> value = BigInt<2>::FromHexString(str);\n      if (!value) return std::nullopt;\n      return BinaryField(*value);\n    }\n  }\n\n  constexpr static BinaryField FromBigInt(BigInt<N> big_int) {\n    return BinaryField(big_int);\n  }\n\n  template <typename T = Type,\n            std::enable_if_t<!std::is_same_v<T, uint8_t>>* = nullptr>\n  constexpr static BinaryField Compose(SubField lo, SubField hi) {\n    if constexpr (kBits <= 64) {\n      using sub_value_type = typename SubField::value_type;\n      return BinaryField(\n          (value_type{hi.value()} << (sizeof(sub_value_type) * 8)) +\n          value_type{lo.value()});\n    } else {\n      static_assert(kBits == 128);\n      return BinaryField(BigInt<2>{lo.value(), hi.value()});\n    }\n  }\n\n  static void Init() {\n    static absl::once_flag once;\n    absl::call_once(once, []() {\n      Config::Init();\n      VLOG(1) << Config::kName << \" initialized\";\n    });\n  }\n\n  constexpr value_type value() const { return value_; }\n\n  constexpr bool IsZero() const {\n    if constexpr (kBits <= 64) {\n      return value_ == 0;\n    } else {\n      static_assert(kBits == 128);\n      return value_.IsZero();\n    }\n  }\n\n  constexpr bool IsOne() const {\n    if constexpr (kBits <= 64) {\n      return value_ == 1;\n    } else {\n      static_assert(kBits == 128);\n      return value_.IsOne();\n    }\n  }\n\n  constexpr bool IsMinusOne() const { return IsOne(); }\n\n  template <typename T = Type,\n            std::enable_if_t<!std::is_same_v<T, uint8_t>>* = nullptr>\n  constexpr std::tuple<SubField, SubField> Decompose() const {\n    std::tuple<SubField, SubField> ret;\n    if constexpr (kBits <= 64) {\n      using sub_value_type = typename SubField::value_type;\n      std::get<0>(ret) = SubField(static_cast<sub_value_type>(value_));\n      std::get<1>(ret) = SubField(\n          static_cast<sub_value_type>(value_ >> (sizeof(sub_value_type) * 8)));\n    } else {\n      static_assert(kBits == 128);\n      std::get<0>(ret) = SubField(value_[0]);\n      std::get<1>(ret) = SubField(value_[1]);\n    }\n    return ret;\n  }\n\n  std::string ToString() const {\n    if constexpr (kBits <= 64) {\n      return base::NumberToString(value_);\n    } else {\n      static_assert(kBits == 128);\n      return ToBigInt().ToString();\n    }\n  }\n\n  std::string ToHexString(bool pad_zero = false) const {\n    if constexpr (kBits <= 64) {\n      std::string str = base::HexToString(value_);\n      if (pad_zero) {\n        str = base::ToHexStringWithLeadingZero(str, 8);\n      }\n      return base::MaybePrepend0x(str);\n    } else {\n      static_assert(kBits == 128);\n      return ToBigInt().ToHexString(pad_zero);\n    }\n  }\n\n  constexpr BigInt<N> ToBigInt() const {\n    if constexpr (kBits <= 64) {\n      return BigInt<1>(value_);\n    } else {\n      static_assert(kBits == 128);\n      return value_;\n    }\n  }\n\n  bool operator==(BinaryField other) const { return value_ == other.value_; }\n  bool operator!=(BinaryField other) const { return value_ != other.value_; }\n  bool operator<(BinaryField other) const { return value_ < other.value_; }\n  bool operator>(BinaryField other) const { return value_ > other.value_; }\n  bool operator<=(BinaryField other) const { return value_ <= other.value_; }\n  bool operator>=(BinaryField other) const { return value_ >= other.value_; }\n\n  // AdditiveSemigroup methods\n  constexpr BinaryField Add(BinaryField other) const {\n    return BinaryField(value_ ^ other.value_);\n  }\n\n  constexpr BinaryField& AddInPlace(BinaryField other) {\n    value_ ^= other.value_;\n    return *this;\n  }\n\n  constexpr BinaryField DoubleImpl() const { return BinaryField::Zero(); }\n\n  constexpr BinaryField& DoubleImplInPlace() {\n    value_ = Type{0};\n    return *this;\n  }\n\n  // AdditiveGroup methods\n  constexpr BinaryField Sub(BinaryField other) const {\n    return BinaryField(value_ ^ other.value_);\n  }\n\n  constexpr BinaryField& SubInPlace(BinaryField other) {\n    value_ ^= other.value_;\n    return *this;\n  }\n\n  constexpr BinaryField Negate() const { return BinaryField(value_); }\n\n  constexpr BinaryField& NegateInPlace() { return *this; }\n\n  // MultiplicativeSemigroup methods\n  constexpr BinaryField Mul(BinaryField other) const {\n    return BinaryTowerOperations<BinaryField>::Mul(*this, other);\n  }\n\n  constexpr BinaryField& MulInPlace(BinaryField other) {\n    return *this = BinaryTowerOperations<BinaryField>::Mul(*this, other);\n  }\n\n  constexpr BinaryField SquareImpl() const {\n    return BinaryTowerOperations<BinaryField>::Square(*this);\n  }\n\n  constexpr BinaryField& SquareImplInPlace() {\n    return *this = BinaryTowerOperations<BinaryField>::Square(*this);\n  }\n\n  // MultiplicativeGroup methods\n  constexpr std::optional<BinaryField> Inverse() const {\n    return BinaryTowerOperations<BinaryField>::Inverse(*this);\n  }\n\n  [[nodiscard]] constexpr std::optional<BinaryField*> InverseInPlace() {\n    std::optional<BinaryField> ret =\n        BinaryTowerOperations<BinaryField>::Inverse(*this);\n    if (LIKELY(ret)) {\n      *this = *ret;\n      return this;\n    }\n    return std::nullopt;\n  }\n\n private:\n  constexpr static Type GetMax() {\n    if constexpr (kBits <= 8) {\n      return (Type{1} << kBits) - 1;\n    } else if constexpr (kBits <= 64) {\n      return static_cast<Type>(std::numeric_limits<Type>::max());\n    } else {\n      static_assert(kBits == 128);\n      return BigInt<2>::Max();\n    }\n  }\n\n  Type value_;\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_BINARY_FIELDS_BINARY_FIELD_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/binary_fields/binary_field_traits_forward.h",
    "content": "#ifndef TACHYON_MATH_FINITE_FIELDS_BINARY_FIELDS_BINARY_FIELD_TRAITS_FORWARD_H_\n#define TACHYON_MATH_FINITE_FIELDS_BINARY_FIELDS_BINARY_FIELD_TRAITS_FORWARD_H_\n\nnamespace tachyon::math {\n\ntemplate <typename T, typename SFINAE = void>\nstruct BinaryFieldTraits;\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_BINARY_FIELDS_BINARY_FIELD_TRAITS_FORWARD_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/binary_fields/binary_fields.h",
    "content": "// Copyright 2023 Ulvetanna Inc.\n// Use of this source code is governed by a Apache-2.0 style license that\n// can be found in the LICENSE.ulvetanna file.\n\n#ifndef TACHYON_MATH_FINITE_FIELDS_BINARY_FIELDS_BINARY_FIELDS_H_\n#define TACHYON_MATH_FINITE_FIELDS_BINARY_FIELDS_BINARY_FIELDS_H_\n\n// clang-format off\n#include \"tachyon/math/finite_fields/binary_fields/binary_field1_config.h\"\n#include \"tachyon/math/finite_fields/binary_fields/binary_field2_config.h\"\n#include \"tachyon/math/finite_fields/binary_fields/binary_field4_config.h\"\n#include \"tachyon/math/finite_fields/binary_fields/binary_field8_config.h\"\n#include \"tachyon/math/finite_fields/binary_fields/binary_field16_config.h\"\n#include \"tachyon/math/finite_fields/binary_fields/binary_field32_config.h\"\n#include \"tachyon/math/finite_fields/binary_fields/binary_field64_config.h\"\n#include \"tachyon/math/finite_fields/binary_fields/binary_field128_config.h\"\n// clang-format on\n#include \"tachyon/math/finite_fields/binary_fields/binary_field_traits_forward.h\"\n\nnamespace tachyon::math {\n\ntemplate <>\nstruct BinaryFieldTraits<BinaryField1Config> {\n  using SubConfig = BinaryField1Config;\n};\n\ntemplate <>\nstruct BinaryFieldTraits<BinaryField2Config> {\n  using SubConfig = BinaryField2Config;\n};\n\ntemplate <>\nstruct BinaryFieldTraits<BinaryField4Config> {\n  using SubConfig = BinaryField4Config;\n};\n\ntemplate <>\nstruct BinaryFieldTraits<BinaryField8Config> {\n  using SubConfig = BinaryField8Config;\n};\n\ntemplate <>\nstruct BinaryFieldTraits<BinaryField16Config> {\n  using SubConfig = BinaryField8Config;\n};\n\ntemplate <>\nstruct BinaryFieldTraits<BinaryField32Config> {\n  using SubConfig = BinaryField16Config;\n};\n\ntemplate <>\nstruct BinaryFieldTraits<BinaryField64Config> {\n  using SubConfig = BinaryField32Config;\n};\n\ntemplate <>\nstruct BinaryFieldTraits<BinaryField128Config> {\n  using SubConfig = BinaryField64Config;\n};\n\n}  // namespace tachyon::math\n\n// clang-format off\n#include \"tachyon/math/finite_fields/binary_fields/binary_field1.h\"\n#include \"tachyon/math/finite_fields/binary_fields/binary_field2.h\"\n#include \"tachyon/math/finite_fields/binary_fields/binary_field4.h\"\n#include \"tachyon/math/finite_fields/binary_fields/binary_field8.h\"\n#include \"tachyon/math/finite_fields/binary_fields/binary_field16.h\"\n#include \"tachyon/math/finite_fields/binary_fields/binary_field32.h\"\n#include \"tachyon/math/finite_fields/binary_fields/binary_field64.h\"\n#include \"tachyon/math/finite_fields/binary_fields/binary_field128.h\"\n// clang-format on\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_BINARY_FIELDS_BINARY_FIELDS_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/binary_fields/binary_fields_unittest.cc",
    "content": "#include \"tachyon/math/finite_fields/binary_fields/binary_fields.h\"\n\n#include \"gtest/gtest.h\"\n\nnamespace tachyon::math {\n\nnamespace {\ntemplate <typename BinaryField>\nclass BinaryFieldsTest : public testing::Test {\n public:\n  static void SetUpTestSuite() { BinaryField::Init(); }\n};\n\n}  // namespace\n\nusing BinaryFieldTypes =\n    testing::Types<BinaryField1, BinaryField2, BinaryField4, BinaryField8,\n                   BinaryField16, BinaryField32, BinaryField64>;\n\nTYPED_TEST_SUITE(BinaryFieldsTest, BinaryFieldTypes);\n\nTYPED_TEST(BinaryFieldsTest, FromString) {\n  using BinaryField = TypeParam;\n  if constexpr (BinaryField::Config::kModulusBits > 2) {\n    EXPECT_EQ(*BinaryField::FromDecString(\"3\"), BinaryField(3));\n    EXPECT_FALSE(BinaryField::FromDecString(\"x\").has_value());\n    EXPECT_EQ(*BinaryField::FromHexString(\"0x3\"), BinaryField(3));\n    EXPECT_FALSE(BinaryField::FromHexString(\"x\").has_value());\n  } else {\n    GTEST_SKIP() << \"Modulus is too small\";\n  }\n}\n\nTYPED_TEST(BinaryFieldsTest, ToString) {\n  using BinaryField = TypeParam;\n  if constexpr (BinaryField::Config::kModulusBits > 2) {\n    BinaryField f(3);\n\n    EXPECT_EQ(f.ToString(), \"3\");\n    EXPECT_EQ(f.ToHexString(), \"0x3\");\n  } else {\n    GTEST_SKIP() << \"Modulus is too small\";\n  }\n}\n\nTYPED_TEST(BinaryFieldsTest, Zero) {\n  using BinaryField = TypeParam;\n  EXPECT_TRUE(BinaryField::Zero().IsZero());\n  EXPECT_FALSE(BinaryField::One().IsZero());\n  EXPECT_FALSE(BinaryField::MinusOne().IsZero());\n}\n\nTYPED_TEST(BinaryFieldsTest, One) {\n  using BinaryField = TypeParam;\n  EXPECT_TRUE(BinaryField::One().IsOne());\n  EXPECT_FALSE(BinaryField::Zero().IsOne());\n  EXPECT_TRUE(BinaryField::MinusOne().IsOne());\n  EXPECT_EQ(BinaryField::Config::kOne, BinaryField(1).value());\n}\n\nTYPED_TEST(BinaryFieldsTest, MinusOne) {\n  using BinaryField = TypeParam;\n  EXPECT_TRUE(BinaryField::MinusOne().IsMinusOne());\n  EXPECT_FALSE(BinaryField::Zero().IsMinusOne());\n  EXPECT_TRUE(BinaryField::One().IsMinusOne());\n}\n\nTYPED_TEST(BinaryFieldsTest, TwoInv) {\n  using BinaryField = TypeParam;\n\n  if constexpr (BinaryField::Config::kModulusBits > 2) {\n    // NOTE(ashjeong): This |constexpr| variable is created since\n    // |BinaryField::TwoInv()| is a true |constexpr| function unlike other\n    // |TwoInv()| functions in unittests of other field types.\n    constexpr BinaryField kTwoInv = BinaryField::TwoInv();\n    EXPECT_TRUE((kTwoInv * BinaryField(2)).IsOne());\n    EXPECT_FALSE((kTwoInv * BinaryField::One()).IsOne());\n  } else {\n    GTEST_SKIP() << \"Modulus is too small\";\n  }\n}\n\nTYPED_TEST(BinaryFieldsTest, BigIntConversion) {\n  using BinaryField = TypeParam;\n  BinaryField r = BinaryField::Random();\n  EXPECT_EQ(BinaryField::FromBigInt(r.ToBigInt()), r);\n}\n\nTYPED_TEST(BinaryFieldsTest, EqualityOperators) {\n  using BinaryField = TypeParam;\n  if constexpr (BinaryField::Config::kModulusBits > 3) {\n    BinaryField f(3);\n    BinaryField f2(4);\n    EXPECT_EQ(f, f);\n    EXPECT_NE(f, f2);\n  } else {\n    GTEST_SKIP() << \"Modulus is too small\";\n  }\n}\n\nTYPED_TEST(BinaryFieldsTest, ComparisonOperator) {\n  using BinaryField = TypeParam;\n  if constexpr (BinaryField::Config::kModulusBits > 3) {\n    BinaryField f(3);\n    BinaryField f2(4);\n    EXPECT_LT(f, f2);\n    EXPECT_LE(f, f2);\n    EXPECT_GT(f2, f);\n    EXPECT_GE(f2, f);\n  } else {\n    GTEST_SKIP() << \"Modulus is too small\";\n  }\n}\n\nTYPED_TEST(BinaryFieldsTest, AdditiveGroupOperators) {\n  using BinaryField = TypeParam;\n  BinaryField f = BinaryField::Random();\n  SCOPED_TRACE(absl::Substitute(\"f: $0\", f.ToString()));\n  BinaryField f_neg = -f;\n  EXPECT_TRUE((f_neg + f).IsZero());\n  f.NegateInPlace();\n  EXPECT_EQ(f, f_neg);\n\n  BinaryField f_double = f.Double();\n  EXPECT_EQ(f + f, f_double);\n  f.DoubleInPlace();\n  EXPECT_EQ(f, f_double);\n}\n\nTYPED_TEST(BinaryFieldsTest, MultiplicativeGroupOperators) {\n  using BinaryField = TypeParam;\n  BinaryField f = BinaryField::Random();\n  SCOPED_TRACE(absl::Substitute(\"f: $0\", f.ToString()));\n  std::optional<BinaryField> f_inv = f.Inverse();\n  if (UNLIKELY(f.IsZero())) {\n    ASSERT_FALSE(f_inv);\n    ASSERT_FALSE(f.InverseInPlace());\n  } else {\n    EXPECT_EQ(f * *f_inv, BinaryField::One());\n    EXPECT_EQ(**f.InverseInPlace(), f_inv);\n  }\n\n  BinaryField f_sqr = f.Square();\n  EXPECT_EQ(f * f, f_sqr);\n  f.SquareInPlace();\n  EXPECT_EQ(f, f_sqr);\n\n  BinaryField f_pow = f.Pow(5);\n  EXPECT_EQ(f * f * f * f * f, f_pow);\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/finite_fields/binary_fields/binary_tower_operations.h",
    "content": "// Copyright 2023 Ulvetanna Inc.\n// Use of this source code is governed by a Apache-2.0 style license that\n// can be found in the LICENSE.ulvetanna file.\n\n#ifndef TACHYON_MATH_FINITE_FIELDS_BINARY_FIELDS_BINARY_TOWER_OPERATIONS_H_\n#define TACHYON_MATH_FINITE_FIELDS_BINARY_FIELDS_BINARY_TOWER_OPERATIONS_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include \"tachyon/base/logging.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename T, typename SFINAE = void>\nstruct BinaryTowerOperations;\n\nnamespace internal {\n\nconstexpr uint8_t DoBinaryMul(uint8_t lhs, uint8_t rhs) {\n  // clang-format off\n  constexpr uint8_t kTable[] = {\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe,\n    0x20, 0x13, 0xa8, 0x9b, 0xec, 0xdf, 0x64, 0x57,\n    0x30, 0x21, 0xfc, 0xed, 0x74, 0x65, 0xb8, 0xa9,\n    0x40, 0xc8, 0xd9, 0x51, 0xae, 0x26, 0x37, 0xbf,\n    0x50, 0xfa, 0x8d, 0x27, 0x36, 0x9c, 0xeb, 0x41,\n    0x60, 0xdb, 0x71, 0xca, 0x42, 0xf9, 0x53, 0xe8,\n    0x70, 0xe9, 0x25, 0xbc, 0xda, 0x43, 0x8f, 0x16,\n    0x80, 0x4c, 0x6e, 0xa2, 0xf7, 0x3b, 0x19, 0xd5,\n    0x90, 0x7e, 0x3a, 0xd4, 0x6f, 0x81, 0xc5, 0x2b,\n    0xa0, 0x5f, 0xc6, 0x39, 0x1b, 0xe4, 0x7d, 0x82,\n    0xb0, 0x6d, 0x92, 0x4f, 0x83, 0x5e, 0xa1, 0x7c,\n    0xc0, 0x84, 0xb7, 0xf3, 0x59, 0x1d, 0x2e, 0x6a,\n    0xd0, 0xb6, 0xe3, 0x85, 0xc1, 0xa7, 0xf2, 0x94,\n    0xe0, 0x97, 0x1f, 0x68, 0xb5, 0xc2, 0x4a, 0x3d,\n    0xf0, 0xa5, 0x4b, 0x1e, 0x2d, 0x78, 0x96, 0xc3,\n  };\n  // clang-format on\n  size_t idx = size_t{lhs} << 4 | rhs;\n  return (kTable[idx >> 1] >> ((idx & 1) * 4)) & 0x0f;\n}\n\nconstexpr uint8_t DoBinaryInverse(uint8_t x) {\n  // clang-format off\n  constexpr uint8_t kTable[] = {\n    0x00, 0x01, 0x03, 0x02, 0x06, 0x0e, 0x04, 0x0f,\n    0x0d, 0x0a, 0x09, 0x0c, 0x0b, 0x08, 0x05, 0x07,\n    0x14, 0x67, 0x94, 0x7b, 0x10, 0x66, 0x9e, 0x7e,\n    0xd2, 0x81, 0x27, 0x4b, 0xd1, 0x8f, 0x2f, 0x42,\n    0x3c, 0xe6, 0xde, 0x7c, 0xb3, 0xc1, 0x4a, 0x1a,\n    0x30, 0xe9, 0xdd, 0x79, 0xb1, 0xc6, 0x43, 0x1e,\n    0x28, 0xe8, 0x9d, 0xb9, 0x63, 0x39, 0x8d, 0xc2,\n    0x62, 0x35, 0x83, 0xc5, 0x20, 0xe7, 0x97, 0xbb,\n    0x61, 0x48, 0x1f, 0x2e, 0xac, 0xc8, 0xbc, 0x56,\n    0x41, 0x60, 0x26, 0x1b, 0xcf, 0xaa, 0x5b, 0xbe,\n    0xef, 0x73, 0x6d, 0x5e, 0xf7, 0x86, 0x47, 0xbd,\n    0x88, 0xfc, 0xbf, 0x4e, 0x76, 0xe0, 0x53, 0x6c,\n    0x49, 0x40, 0x38, 0x34, 0xe4, 0xeb, 0x15, 0x11,\n    0x8b, 0x85, 0xaf, 0xa9, 0x5f, 0x52, 0x98, 0x92,\n    0xfb, 0xb5, 0xee, 0x51, 0xb7, 0xf0, 0x5c, 0xe1,\n    0xdc, 0x2b, 0x95, 0x13, 0x23, 0xdf, 0x17, 0x9f,\n    0xd3, 0x19, 0xc4, 0x3a, 0x8a, 0x69, 0x55, 0xf6,\n    0x58, 0xfd, 0x84, 0x68, 0xc3, 0x36, 0xd0, 0x1d,\n    0xa6, 0xf3, 0x6f, 0x99, 0x12, 0x7a, 0xba, 0x3e,\n    0x6e, 0x93, 0xa0, 0xf8, 0xb8, 0x32, 0x16, 0x7f,\n    0x9a, 0xf9, 0xe2, 0xdb, 0xed, 0xd8, 0x90, 0xf2,\n    0xae, 0x6b, 0x4d, 0xce, 0x44, 0xc9, 0xa8, 0x6a,\n    0xc7, 0x2c, 0xc0, 0x24, 0xfa, 0x71, 0xf1, 0x74,\n    0x9c, 0x33, 0x96, 0x3f, 0x46, 0x57, 0x4f, 0x5a,\n    0xb2, 0x25, 0x37, 0x8c, 0x82, 0x3b, 0x2d, 0xb0,\n    0x45, 0xad, 0xd7, 0xff, 0xf4, 0xd4, 0xab, 0x4c,\n    0x8e, 0x1c, 0x18, 0x80, 0xcd, 0xf5, 0xfe, 0xca,\n    0xa5, 0xec, 0xe3, 0xa3, 0x78, 0x2a, 0x22, 0x7d,\n    0x5d, 0x77, 0xa2, 0xda, 0x64, 0xea, 0x21, 0x3d,\n    0x31, 0x29, 0xe5, 0x65, 0xd9, 0xa4, 0x72, 0x50,\n    0x75, 0xb6, 0xa7, 0x91, 0xcc, 0xd5, 0x87, 0x54,\n    0x9b, 0xa1, 0xb4, 0x70, 0x59, 0x89, 0xd6, 0xcb,\n  };\n  // clang-format on\n  return kTable[x];\n}\n\n}  // namespace internal\n\ntemplate <typename F>\nstruct BinaryTowerOperations<F, std::enable_if_t<F::kBits == 1>> {\n  constexpr static F Mul(F lhs, F rhs) { return F(lhs.value() & rhs.value()); }\n\n  constexpr static F MulByAlpha(F x) { return x; }\n\n  constexpr static F Square(F x) { return x; }\n\n  constexpr static std::optional<F> Inverse(F x) {\n    if (UNLIKELY(x.IsZero())) {\n      LOG_IF_NOT_GPU(ERROR) << \"Inverse of zero attempted\";\n      return std::nullopt;\n    }\n    return x;\n  }\n};\n\ntemplate <typename F>\nstruct BinaryTowerOperations<F, std::enable_if_t<F::kBits == 2>> {\n  constexpr static F Mul(F lhs, F rhs) {\n    return F(internal::DoBinaryMul(lhs.value(), rhs.value()));\n  }\n\n  constexpr static F MulByAlpha(F x) { return Mul(x, F(2)); }\n\n  constexpr static F Square(F x) { return Mul(x, x); }\n\n  constexpr static std::optional<F> Inverse(F x) {\n    if (UNLIKELY(x.IsZero())) {\n      LOG_IF_NOT_GPU(ERROR) << \"Inverse of zero attempted\";\n      return std::nullopt;\n    }\n    return F(internal::DoBinaryInverse(x.value()));\n  }\n};\n\ntemplate <typename F>\nstruct BinaryTowerOperations<F, std::enable_if_t<F::kBits == 4>> {\n  constexpr static F Mul(F lhs, F rhs) {\n    return F(internal::DoBinaryMul(lhs.value(), rhs.value()));\n  }\n\n  constexpr static F MulByAlpha(F x) { return Mul(x, F(4)); }\n\n  constexpr static F Square(F x) { return Mul(x, x); }\n\n  constexpr static std::optional<F> Inverse(F x) {\n    if (UNLIKELY(x.IsZero())) {\n      LOG_IF_NOT_GPU(ERROR) << \"Inverse of zero attempted\";\n      return std::nullopt;\n    }\n    return F(internal::DoBinaryInverse(x.value()));\n  }\n};\n\ntemplate <typename F>\nstruct BinaryTowerOperations<F, std::enable_if_t<F::kBits == 8>> {\n  constexpr static F Mul(F lhs, F rhs) {\n    // clang-format off\n    constexpr uint8_t kExpTable[] = {\n      0x1,  0x13, 0x43, 0x66, 0xab, 0x8c, 0x60, 0xc6,\n      0x91, 0xca, 0x59, 0xb2, 0x6a, 0x63, 0xf4, 0x53,\n      0x17, 0x0f, 0xfa, 0xba, 0xee, 0x87, 0xd6, 0xe0,\n      0x6e, 0x2f, 0x68, 0x42, 0x75, 0xe8, 0xea, 0xcb,\n      0x4a, 0xf1, 0x0c, 0xc8, 0x78, 0x33, 0xd1, 0x9e,\n      0x30, 0xe3, 0x5c, 0xed, 0xb5, 0x14, 0x3d, 0x38,\n      0x67, 0xb8, 0xcf, 0x06, 0x6d, 0x1d, 0xaa, 0x9f,\n      0x23, 0xa0, 0x3a, 0x46, 0x39, 0x74, 0xfb, 0xa9,\n      0xad, 0xe1, 0x7d, 0x6c, 0x0e, 0xe9, 0xf9, 0x88,\n      0x2c, 0x5a, 0x80, 0xa8, 0xbe, 0xa2, 0x1b, 0xc7,\n      0x82, 0x89, 0x3f, 0x19, 0xe6, 0x03, 0x32, 0xc2,\n      0xdd, 0x56, 0x48, 0xd0, 0x8d, 0x73, 0x85, 0xf7,\n      0x61, 0xd5, 0xd2, 0xac, 0xf2, 0x3e, 0x0a, 0xa5,\n      0x65, 0x99, 0x4e, 0xbd, 0x90, 0xd9, 0x1a, 0xd4,\n      0xc1, 0xef, 0x94, 0x95, 0x86, 0xc5, 0xa3, 0x08,\n      0x84, 0xe4, 0x22, 0xb3, 0x79, 0x20, 0x92, 0xf8,\n      0x9b, 0x6f, 0x3c, 0x2b, 0x24, 0xde, 0x64, 0x8a,\n      0xd,  0xdb, 0x3b, 0x55, 0x7a, 0x12, 0x50, 0x25,\n      0xcd, 0x27, 0xec, 0xa6, 0x57, 0x5b, 0x93, 0xeb,\n      0xd8, 0x09, 0x97, 0xa7, 0x44, 0x18, 0xf5, 0x40,\n      0x54, 0x69, 0x51, 0x36, 0x8e, 0x41, 0x47, 0x2a,\n      0x37, 0x9d, 0x02, 0x21, 0x81, 0xbb, 0xfd, 0xc4,\n      0xb0, 0x4b, 0xe2, 0x4f, 0xae, 0xd3, 0xbf, 0xb1,\n      0x58, 0xa1, 0x29, 0x05, 0x5f, 0xdf, 0x77, 0xc9,\n      0x6b, 0x70, 0xb7, 0x35, 0xbc, 0x83, 0x9a, 0x7c,\n      0x7f, 0x4d, 0x8f, 0x52, 0x04, 0x4c, 0x9c, 0x11,\n      0x62, 0xe7, 0x10, 0x71, 0xa4, 0x76, 0xda, 0x28,\n      0x16, 0x1c, 0xb9, 0xdc, 0x45, 0x0b, 0xb6, 0x26,\n      0xff, 0xe5, 0x31, 0xf0, 0x1f, 0x8b, 0x1e, 0x98,\n      0x5d, 0xfe, 0xf6, 0x72, 0x96, 0xb4, 0x07, 0x7e,\n      0x5e, 0xcc, 0x34, 0xaf, 0xc0, 0xfc, 0xd7, 0xf3,\n      0x2d, 0x49, 0xc3, 0xce, 0x15, 0x2e, 0x7b, 0x00,\n    };\n    constexpr uint8_t kLogTable[] = {\n      0x00, 0x00, 0xaa, 0x55, 0xcc, 0xbb, 0x33, 0xee,\n      0x77, 0x99, 0x66, 0xdd, 0x22, 0x88, 0x44, 0x11,\n      0xd2, 0xcf, 0x8d, 0x01, 0x2d, 0xfc, 0xd8, 0x10,\n      0x9d, 0x53, 0x6e, 0x4e, 0xd9, 0x35, 0xe6, 0xe4,\n      0x7d, 0xab, 0x7a, 0x38, 0x84, 0x8f, 0xdf, 0x91,\n      0xd7, 0xba, 0xa7, 0x83, 0x48, 0xf8, 0xfd, 0x19,\n      0x28, 0xe2, 0x56, 0x25, 0xf2, 0xc3, 0xa3, 0xa8,\n      0x2f, 0x3c, 0x3a, 0x8a, 0x82, 0x2e, 0x65, 0x52,\n      0x9f, 0xa5, 0x1b, 0x02, 0x9c, 0xdc, 0x3b, 0xa6,\n      0x5a, 0xf9, 0x20, 0xb1, 0xcd, 0xc9, 0x6a, 0xb3,\n      0x8e, 0xa2, 0xcb, 0x0f, 0xa0, 0x8b, 0x59, 0x94,\n      0xb8, 0x0a, 0x49, 0x95, 0x2a, 0xe8, 0xf0, 0xbc,\n      0x06, 0x60, 0xd0, 0x0d, 0x86, 0x68, 0x03, 0x30,\n      0x1a, 0xa1, 0x0c, 0xc0, 0x43, 0x34, 0x18, 0x81,\n      0xc1, 0xd3, 0xeb, 0x5d, 0x3d, 0x1c, 0xd5, 0xbe,\n      0x24, 0x7c, 0x8c, 0xfe, 0xc7, 0x42, 0xef, 0xc8,\n      0x4a, 0xac, 0x50, 0xc5, 0x78, 0x5e, 0x74, 0x15,\n      0x47, 0x51, 0x87, 0xe5, 0x05, 0x5c, 0xa4, 0xca,\n      0x6c, 0x08, 0x7e, 0x96, 0x72, 0x73, 0xec, 0x9a,\n      0xe7, 0x69, 0xc6, 0x80, 0xce, 0xa9, 0x27, 0x37,\n      0x39, 0xb9, 0x4d, 0x76, 0xd4, 0x67, 0x93, 0x9b,\n      0x4b, 0x3f, 0x36, 0x04, 0x63, 0x40, 0xb4, 0xf3,\n      0xb0, 0xb7, 0x0b, 0x7b, 0xed, 0x2c, 0xde, 0xc2,\n      0x31, 0xda, 0x13, 0xad, 0xc4, 0x6b, 0x4c, 0xb6,\n      0xf4, 0x70, 0x57, 0xfa, 0xaf, 0x75, 0x07, 0x4f,\n      0x23, 0xbf, 0x09, 0x1f, 0xf1, 0x90, 0xfb, 0x32,\n      0x5b, 0x26, 0x62, 0xb5, 0x6f, 0x61, 0x16, 0xf6,\n      0x98, 0x6d, 0xd6, 0x89, 0xdb, 0x58, 0x85, 0xbd,\n      0x17, 0x41, 0xb2, 0x29, 0x79, 0xe1, 0x54, 0xd1,\n      0x1d, 0x45, 0x1e, 0x97, 0x92, 0x2b, 0x14, 0x71,\n      0xe3, 0x21, 0x64, 0xf7, 0x0e, 0x9e, 0xea, 0x5f,\n      0x7f, 0x46, 0x12, 0x3e, 0xf5, 0xae, 0xe9, 0xe0,\n    };\n    // clang-format on\n    if (lhs.IsZero() || rhs.IsZero()) return F::Zero();\n\n    // Safety: |log_table_index| is smaller than 255 because:\n    // - all values in |kLogTable| do not exceed 254\n    // - sum of two values do not exceed 254*2\n    // - the previous line reduces |log_table_index| by 255 if it is\n    //   bigger than 254\n    size_t log_table_index =\n        size_t{kLogTable[lhs.value()]} + kLogTable[rhs.value()];\n    size_t exp_table_index =\n        log_table_index > 254 ? log_table_index - 255 : log_table_index;\n    return F(kExpTable[exp_table_index]);\n  }\n\n  constexpr static F MulByAlpha(F x) {\n    // clang-format off\n    constexpr uint8_t kTable[] = {\n      0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70,\n      0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0,\n      0x41, 0x51, 0x61, 0x71, 0x01, 0x11, 0x21, 0x31,\n      0xc1, 0xd1, 0xe1, 0xf1, 0x81, 0x91, 0xa1, 0xb1,\n      0x82, 0x92, 0xa2, 0xb2, 0xc2, 0xd2, 0xe2, 0xf2,\n      0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62, 0x72,\n      0xc3, 0xd3, 0xe3, 0xf3, 0x83, 0x93, 0xa3, 0xb3,\n      0x43, 0x53, 0x63, 0x73, 0x03, 0x13, 0x23, 0x33,\n      0x94, 0x84, 0xb4, 0xa4, 0xd4, 0xc4, 0xf4, 0xe4,\n      0x14, 0x04, 0x34, 0x24, 0x54, 0x44, 0x74, 0x64,\n      0xd5, 0xc5, 0xf5, 0xe5, 0x95, 0x85, 0xb5, 0xa5,\n      0x55, 0x45, 0x75, 0x65, 0x15, 0x05, 0x35, 0x25,\n      0x16, 0x06, 0x36, 0x26, 0x56, 0x46, 0x76, 0x66,\n      0x96, 0x86, 0xb6, 0xa6, 0xd6, 0xc6, 0xf6, 0xe6,\n      0x57, 0x47, 0x77, 0x67, 0x17, 0x07, 0x37, 0x27,\n      0xd7, 0xc7, 0xf7, 0xe7, 0x97, 0x87, 0xb7, 0xa7,\n      0xe8, 0xf8, 0xc8, 0xd8, 0xa8, 0xb8, 0x88, 0x98,\n      0x68, 0x78, 0x48, 0x58, 0x28, 0x38, 0x08, 0x18,\n      0xa9, 0xb9, 0x89, 0x99, 0xe9, 0xf9, 0xc9, 0xd9,\n      0x29, 0x39, 0x09, 0x19, 0x69, 0x79, 0x49, 0x59,\n      0x6a, 0x7a, 0x4a, 0x5a, 0x2a, 0x3a, 0x0a, 0x1a,\n      0xea, 0xfa, 0xca, 0xda, 0xaa, 0xba, 0x8a, 0x9a,\n      0x2b, 0x3b, 0x0b, 0x1b, 0x6b, 0x7b, 0x4b, 0x5b,\n      0xab, 0xbb, 0x8b, 0x9b, 0xeb, 0xfb, 0xcb, 0xdb,\n      0x7c, 0x6c, 0x5c, 0x4c, 0x3c, 0x2c, 0x1c, 0x0c,\n      0xfc, 0xec, 0xdc, 0xcc, 0xbc, 0xac, 0x9c, 0x8c,\n      0x3d, 0x2d, 0x1d, 0x0d, 0x7d, 0x6d, 0x5d, 0x4d,\n      0xbd, 0xad, 0x9d, 0x8d, 0xfd, 0xed, 0xdd, 0xcd,\n      0xfe, 0xee, 0xde, 0xce, 0xbe, 0xae, 0x9e, 0x8e,\n      0x7e, 0x6e, 0x5e, 0x4e, 0x3e, 0x2e, 0x1e, 0x0e,\n      0xbf, 0xaf, 0x9f, 0x8f, 0xff, 0xef, 0xdf, 0xcf,\n      0x3f, 0x2f, 0x1f, 0x0f, 0x7f, 0x6f, 0x5f, 0x4f,\n    };\n    // clang-format on\n    return F(kTable[x.value()]);\n  }\n\n  constexpr static F Square(F x) {\n    // clang-format off\n    constexpr uint8_t kTable[] = {\n      0x00, 0x01, 0x03, 0x02, 0x09, 0x08, 0x0a, 0x0b,\n      0x07, 0x06, 0x04, 0x05, 0x0e, 0x0f, 0x0d, 0x0c,\n      0x41, 0x40, 0x42, 0x43, 0x48, 0x49, 0x4b, 0x4a,\n      0x46, 0x47, 0x45, 0x44, 0x4f, 0x4e, 0x4c, 0x4d,\n      0xc3, 0xc2, 0xc0, 0xc1, 0xca, 0xcb, 0xc9, 0xc8,\n      0xc4, 0xc5, 0xc7, 0xc6, 0xcd, 0xcc, 0xce, 0xcf,\n      0x82, 0x83, 0x81, 0x80, 0x8b, 0x8a, 0x88, 0x89,\n      0x85, 0x84, 0x86, 0x87, 0x8c, 0x8d, 0x8f, 0x8e,\n      0xa9, 0xa8, 0xaa, 0xab, 0xa0, 0xa1, 0xa3, 0xa2,\n      0xae, 0xaf, 0xad, 0xac, 0xa7, 0xa6, 0xa4, 0xa5,\n      0xe8, 0xe9, 0xeb, 0xea, 0xe1, 0xe0, 0xe2, 0xe3,\n      0xef, 0xee, 0xec, 0xed, 0xe6, 0xe7, 0xe5, 0xe4,\n      0x6a, 0x6b, 0x69, 0x68, 0x63, 0x62, 0x60, 0x61,\n      0x6d, 0x6c, 0x6e, 0x6f, 0x64, 0x65, 0x67, 0x66,\n      0x2b, 0x2a, 0x28, 0x29, 0x22, 0x23, 0x21, 0x20,\n      0x2c, 0x2d, 0x2f, 0x2e, 0x25, 0x24, 0x26, 0x27,\n      0x57, 0x56, 0x54, 0x55, 0x5e, 0x5f, 0x5d, 0x5c,\n      0x50, 0x51, 0x53, 0x52, 0x59, 0x58, 0x5a, 0x5b,\n      0x16, 0x17, 0x15, 0x14, 0x1f, 0x1e, 0x1c, 0x1d,\n      0x11, 0x10, 0x12, 0x13, 0x18, 0x19, 0x1b, 0x1a,\n      0x94, 0x95, 0x97, 0x96, 0x9d, 0x9c, 0x9e, 0x9f,\n      0x93, 0x92, 0x90, 0x91, 0x9a, 0x9b, 0x99, 0x98,\n      0xd5, 0xd4, 0xd6, 0xd7, 0xdc, 0xdd, 0xdf, 0xde,\n      0xd2, 0xd3, 0xd1, 0xd0, 0xdb, 0xda, 0xd8, 0xd9,\n      0xfe, 0xff, 0xfd, 0xfc, 0xf7, 0xf6, 0xf4, 0xf5,\n      0xf9, 0xf8, 0xfa, 0xfb, 0xf0, 0xf1, 0xf3, 0xf2,\n      0xbf, 0xbe, 0xbc, 0xbd, 0xb6, 0xb7, 0xb5, 0xb4,\n      0xb8, 0xb9, 0xbb, 0xba, 0xb1, 0xb0, 0xb2, 0xb3,\n      0x3d, 0x3c, 0x3e, 0x3f, 0x34, 0x35, 0x37, 0x36,\n      0x3a, 0x3b, 0x39, 0x38, 0x33, 0x32, 0x30, 0x31,\n      0x7c, 0x7d, 0x7f, 0x7e, 0x75, 0x74, 0x76, 0x77,\n      0x7b, 0x7a, 0x78, 0x79, 0x72, 0x73, 0x71, 0x70,\n    };\n    // clang-format on\n    return F(kTable[x.value()]);\n  }\n\n  constexpr static std::optional<F> Inverse(F x) {\n    if (UNLIKELY(x.IsZero())) {\n      LOG_IF_NOT_GPU(ERROR) << \"Inverse of zero attempted\";\n      return std::nullopt;\n    }\n    return F(internal::DoBinaryInverse(x.value()));\n  }\n};\n\ntemplate <typename F>\nstruct BinaryTowerOperations<F, std::enable_if_t<(F::kBits >= 16)>> {\n  using SubField = typename F::SubField;\n\n  constexpr static F Mul(F lhs, F rhs) {\n    // clang-format off\n    //   (lhs₀ + lhs₁X) * (rhs₀ + rhs₁X)\n    // = lhs₀ * rhs₀ + (lhs₀ * rhs₁ + lhs₁ * rhs₀)X + (lhs₁ * rhs₁)X²\n    // = lhs₀ * rhs₀ + (lhs₀ * rhs₁ + lhs₁ * rhs₀)X + (lhs₁ * rhs₁)(αX + 1)\n    // = lhs₀ * rhs₀ + lhs₁ * rhs₁ + (lhs₀ * rhs₁ + lhs₁ * rhs₀ + lhs₁ * rhs₁ * α)X\n    // clang-format on\n    auto [lhs0, lhs1] = lhs.Decompose();\n    auto [rhs0, rhs1] = rhs.Decompose();\n    SubField z0 = lhs0 * rhs0;\n    SubField z1 = lhs1 * rhs1;\n    SubField z0z1 = z0 + z1;\n    SubField z2 = (lhs0 + lhs1) * (rhs0 + rhs1) - z0z1;\n    SubField z1a = BinaryTowerOperations<SubField>::MulByAlpha(z1);\n    return F::Compose(z0z1, z2 + z1a);\n  }\n\n  constexpr static F MulByAlpha(F x) {\n    //   (x₀ + x₁X) * X\n    // = x₀X + x₁X²\n    // = x₀X + x₁(αX + 1)\n    // = x₁ + (x₀ + x₁ * α)X\n    auto [x0, x1] = x.Decompose();\n    SubField x1a = BinaryTowerOperations<SubField>::MulByAlpha(x1);\n    return F::Compose(x1, x0 + x1a);\n  }\n\n  constexpr static F Square(F x) {\n    //   (x₀ + x₁X)²\n    // = x₀² + (2 * x₀ * x₁)X + x₁²X²\n    // = x₀² + x₁²X²\n    // = x₀² + x₁²(αX + 1)\n    // = x₀² + x₁² + (x₁² * α)X\n    auto [x0, x1] = x.Decompose();\n    SubField z0 = x0.Square();\n    SubField z1 = x1.Square();\n    SubField z1a = BinaryTowerOperations<SubField>::MulByAlpha(z1);\n    return F::Compose(z0 + z1, z1a);\n  }\n\n  constexpr static std::optional<F> Inverse(F x) {\n    //   (x₀ + x₁X)(x₀ + x₁ * α + x₁X)\n    // = x₀² + x₀ * x₁ * α + (x₀ * x₁)X + (x₀ * x₁)X + (x₁² * α)X + x₁²X²\n    // = x₀² + x₀ * x₁ * α + (x₁² * α)X + x₁²X²\n    // = x₀² + x₀ * x₁ * α + x₁²(αX + X²)\n    // = x₀² + x₀ * x₁ * α + x₁²\n    // = x₀(x₀ + x₁ * α) + x₁²\n    // = δ\n    // Therefore, (x₀ + x₁X)⁻¹ = δ⁻¹(x₀ + x₁ * α + x₁X)\n    auto [x0, x1] = x.Decompose();\n    SubField x0x1a = x0 + BinaryTowerOperations<SubField>::MulByAlpha(x1);\n    SubField delta = x0 * x0x1a + x1.Square();\n    std::optional<SubField> delta_inv = delta.Inverse();\n    if (LIKELY(delta_inv)) {\n      SubField inv0 = *delta_inv * x0x1a;\n      SubField inv1 = *delta_inv * x1;\n      return F::Compose(inv0, inv1);\n    }\n    return std::nullopt;\n  }\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_BINARY_FIELDS_BINARY_TOWER_OPERATIONS_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/carry_chain.h",
    "content": "// Copyright (c) 2022 Matter Labs\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.era-bellman-cuda and the\n// LICENCE-APACHE.era-bellman-cuda file.\n\n#ifndef TACHYON_MATH_FINITE_FIELDS_CARRY_CHAIN_H_\n#define TACHYON_MATH_FINITE_FIELDS_CARRY_CHAIN_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <limits>\n\n#include \"tachyon/math/finite_fields/prime_field_ops_internal.h\"\n\nnamespace tachyon::math {\nnamespace u32 {\n\ntemplate <size_t OpsCount = std::numeric_limits<size_t>::max(),\n          bool CarryIn = false, bool CarryOut = false>\nstruct CarryChain {\n  size_t index = 0;\n\n  AddResult<uint32_t> Add(uint32_t x, uint32_t y, uint32_t carry = 0) {\n    ++index;\n    if (index == 1 && OpsCount == 1 && !CarryIn && !CarryOut) {\n      return internal::u32::Add(x, y);\n    } else if (index == 1 && !CarryIn) {\n      return internal::u32::AddCc(x, y);\n    } else if (index < OpsCount || CarryOut) {\n      return internal::u32::AddcCc(x, y, carry);\n    } else {\n      return internal::u32::Addc(x, y, carry);\n    }\n  }\n\n  SubResult<uint32_t> Sub(uint32_t x, uint32_t y, uint32_t carry = 0) {\n    ++index;\n    if (index == 1 && OpsCount == 1 && !CarryIn && !CarryOut) {\n      return internal::u32::Sub(x, y);\n    } else if (index == 1 && !CarryIn) {\n      return internal::u32::SubCc(x, y);\n    } else if (index < OpsCount || CarryOut) {\n      return internal::u32::SubcCc(x, y, carry);\n    } else {\n      return internal::u32::Subc(x, y, carry);\n    }\n  }\n\n  AddResult<uint32_t> MadLo(uint32_t x, uint32_t y, uint32_t z,\n                            uint32_t carry = 0) {\n    ++index;\n    if (index == 1 && OpsCount == 1 && !CarryIn && !CarryOut) {\n      return internal::u32::MadLo(x, y, z);\n    } else if (index == 1 && !CarryIn) {\n      return internal::u32::MadLoCc(x, y, z);\n    } else if (index < OpsCount || CarryOut) {\n      return internal::u32::MadcLoCc(x, y, z, carry);\n    } else {\n      return internal::u32::MadcLo(x, y, z, carry);\n    }\n  }\n\n  AddResult<uint32_t> MadHi(uint32_t x, uint32_t y, uint32_t z,\n                            uint32_t carry = 0) {\n    ++index;\n    if (index == 1 && OpsCount == 1 && !CarryIn && !CarryOut) {\n      return internal::u32::MadHi(x, y, z);\n    } else if (index == 1 && !CarryIn) {\n      return internal::u32::MadHiCc(x, y, z);\n    } else if (index < OpsCount || CarryOut) {\n      return internal::u32::MadcHiCc(x, y, z, carry);\n    } else {\n      return internal::u32::MadcHi(x, y, z, carry);\n    }\n  }\n};\n\n}  // namespace u32\n\nnamespace u64 {\n\ntemplate <size_t OpsCount = std::numeric_limits<size_t>::max(),\n          bool CarryIn = false, bool CarryOut = false>\nstruct CarryChain {\n  size_t index = 0;\n\n  AddResult<uint64_t> Add(uint64_t x, uint64_t y, uint64_t carry = 0) {\n    ++index;\n    if (index == 1 && OpsCount == 1 && !CarryIn && !CarryOut) {\n      return internal::u64::Add(x, y);\n    } else if (index == 1 && !CarryIn) {\n      return internal::u64::AddCc(x, y);\n    } else if (index < OpsCount || CarryOut) {\n      return internal::u64::AddcCc(x, y, carry);\n    } else {\n      return internal::u64::Addc(x, y, carry);\n    }\n  }\n\n  SubResult<uint64_t> Sub(uint64_t x, uint64_t y, uint64_t carry = 0) {\n    ++index;\n    if (index == 1 && OpsCount == 1 && !CarryIn && !CarryOut) {\n      return internal::u64::Sub(x, y);\n    } else if (index == 1 && !CarryIn) {\n      return internal::u64::SubCc(x, y);\n    } else if (index < OpsCount || CarryOut) {\n      return internal::u64::SubcCc(x, y, carry);\n    } else {\n      return internal::u64::Subc(x, y, carry);\n    }\n  }\n\n  AddResult<uint64_t> MadLo(uint64_t x, uint64_t y, uint64_t z,\n                            uint64_t carry = 0) {\n    ++index;\n    if (index == 1 && OpsCount == 1 && !CarryIn && !CarryOut) {\n      return internal::u64::MadLo(x, y, z);\n    } else if (index == 1 && !CarryIn) {\n      return internal::u64::MadLoCc(x, y, z);\n    } else if (index < OpsCount || CarryOut) {\n      return internal::u64::MadcLoCc(x, y, z, carry);\n    } else {\n      return internal::u64::MadcLo(x, y, z, carry);\n    }\n  }\n\n  AddResult<uint64_t> MadHi(uint64_t x, uint64_t y, uint64_t z,\n                            uint64_t carry = 0) {\n    ++index;\n    if (index == 1 && OpsCount == 1 && !CarryIn && !CarryOut) {\n      return internal::u64::MadHi(x, y, z);\n    } else if (index == 1 && !CarryIn) {\n      return internal::u64::MadHiCc(x, y, z);\n    } else if (index < OpsCount || CarryOut) {\n      return internal::u64::MadcHiCc(x, y, z, carry);\n    } else {\n      return internal::u64::MadcHi(x, y, z, carry);\n    }\n  }\n};\n\n}  // namespace u64\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_CARRY_CHAIN_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/cubic_extension_field.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_FINITE_FIELDS_CUBIC_EXTENSION_FIELD_H_\n#define TACHYON_MATH_FINITE_FIELDS_CUBIC_EXTENSION_FIELD_H_\n\n#include <array>\n#include <optional>\n#include <string>\n#include <utility>\n\n#include \"absl/strings/substitute.h\"\n#include \"absl/types/span.h\"\n\n#include \"tachyon/base/buffer/copyable.h\"\n#include \"tachyon/base/json/json.h\"\n#include \"tachyon/math/finite_fields/cyclotomic_multiplicative_subgroup.h\"\n#include \"tachyon/math/finite_fields/extension_field_base.h\"\n\nnamespace tachyon {\nnamespace math {\n\ntemplate <typename Derived>\nclass CubicExtensionField : public CyclotomicMultiplicativeSubgroup<Derived>,\n                            public ExtensionFieldBase<Derived> {\n public:\n  using Config = typename FiniteField<Derived>::Config;\n  using BaseField = typename Config::BaseField;\n  using BasePrimeField = typename Config::BasePrimeField;\n  using ConstIterator = typename ExtensionFieldBase<Derived>::ConstIterator;\n\n  constexpr CubicExtensionField() = default;\n  // NOTE(chokobole): This is needed by Eigen matrix.\n  template <typename T, std::enable_if_t<std::is_integral_v<T>>* = nullptr>\n  constexpr explicit CubicExtensionField(T value) : c0_(value) {}\n  constexpr explicit CubicExtensionField(const BaseField& c0)\n      : c0_(c0), c1_(BaseField::Zero()), c2_(BaseField::Zero()) {}\n  constexpr explicit CubicExtensionField(BaseField&& c0)\n      : c0_(std::move(c0)), c1_(BaseField::Zero()), c2_(BaseField::Zero()) {}\n  constexpr CubicExtensionField(const BaseField& c0, const BaseField& c1,\n                                const BaseField& c2)\n      : c0_(c0), c1_(c1), c2_(c2) {}\n  constexpr CubicExtensionField(BaseField&& c0, BaseField&& c1, BaseField&& c2)\n      : c0_(std::move(c0)), c1_(std::move(c1)), c2_(std::move(c2)) {}\n\n  constexpr static Derived Zero() {\n    return {BaseField::Zero(), BaseField::Zero(), BaseField::Zero()};\n  }\n\n  constexpr static Derived One() {\n    return {BaseField::One(), BaseField::Zero(), BaseField::Zero()};\n  }\n\n  constexpr static Derived MinusOne() {\n    return {BaseField::MinusOne(), BaseField::Zero(), BaseField::Zero()};\n  }\n\n  constexpr static Derived TwoInv() {\n    return {BaseField::TwoInv(), BaseField::Zero(), BaseField::Zero()};\n  }\n\n  static Derived Random() {\n    return {BaseField::Random(), BaseField::Random(), BaseField::Random()};\n  }\n\n  // TODO(chokobole): Should be generalized for packed extension field.\n  static Derived FromBasePrimeFields(\n      absl::Span<const BasePrimeField> prime_fields) {\n    CHECK_EQ(prime_fields.size(), ExtensionDegree());\n    constexpr size_t kBaseFieldDegree = BaseField::ExtensionDegree();\n    if constexpr (kBaseFieldDegree == 1) {\n      return Derived(prime_fields[0], prime_fields[1], prime_fields[2]);\n    } else {\n      BaseField c0 = BaseField::FromBasePrimeFields(\n          prime_fields.subspan(0, kBaseFieldDegree));\n      BaseField c1 = BaseField::FromBasePrimeFields(\n          prime_fields.subspan(kBaseFieldDegree, kBaseFieldDegree));\n      BaseField c2 = BaseField::FromBasePrimeFields(\n          prime_fields.subspan(2 * kBaseFieldDegree, kBaseFieldDegree));\n      return Derived(std::move(c0), std::move(c1), std::move(c2));\n    }\n  }\n\n  constexpr std::array<BaseField, 3> ToBaseFields() const {\n    return {c0_, c1_, c2_};\n  }\n\n  ConstIterator end() const { return {static_cast<const Derived&>(*this), 3}; }\n\n  constexpr bool IsZero() const {\n    return c0_.IsZero() && c1_.IsZero() && c2_.IsZero();\n  }\n\n  constexpr bool IsOne() const {\n    return c0_.IsOne() && c1_.IsZero() && c2_.IsZero();\n  }\n\n  constexpr bool IsMinusOne() const {\n    return c0_.IsMinusOne() && c1_.IsZero() && c2_.IsZero();\n  }\n\n  constexpr static uint32_t ExtensionDegree() {\n    return 3 * BaseField::ExtensionDegree();\n  }\n\n  // Calculate the norm of an element with respect to |BaseField|.\n  // The norm maps an element |a| in the extension field Fqᵐ to an element\n  // in the |BaseField| Fq. |a.Norm() = a * a^q * a^q²|\n  constexpr BaseField Norm() const {\n    // w.r.t to |BaseField|, we need the 0th, 1st & 2nd powers of q.\n    // Since Frobenius coefficients on the towered extensions are\n    // indexed w.r.t. to |BasePrimeField|, we need to calculate the correct\n    // index.\n    // NOTE(chokobole): This assumes that |BaseField::ExtensionDegree()|\n    // never overflows even on a 32-bit machine.\n    size_t index_multiplier = size_t{BaseField::ExtensionDegree()};\n    Derived self_to_p = static_cast<const Derived&>(*this);\n    self_to_p.FrobeniusMapInPlace(index_multiplier);\n    Derived self_to_p2 = static_cast<const Derived&>(*this);\n    self_to_p2.FrobeniusMapInPlace(2 * index_multiplier);\n    self_to_p *= (self_to_p2 * static_cast<const Derived&>(*this));\n    // NOTE(chokobole): The |CHECK()| below is not device code.\n    // See https://github.com/kroma-network/tachyon/issues/76\n    CHECK(self_to_p.c1().IsZero() && self_to_p.c2().IsZero());\n    return self_to_p.c0();\n  }\n\n  constexpr Derived& FrobeniusMapInPlace(uint32_t exponent) {\n    c0_.FrobeniusMapInPlace(exponent);\n    c1_.FrobeniusMapInPlace(exponent);\n    c2_.FrobeniusMapInPlace(exponent);\n    c1_ *=\n        Config::kFrobeniusCoeffs[exponent % Config::kDegreeOverBasePrimeField];\n    c2_ *=\n        Config::kFrobeniusCoeffs2[exponent % Config::kDegreeOverBasePrimeField];\n    return *static_cast<Derived*>(this);\n  }\n\n  std::string ToString() const {\n    return absl::Substitute(\"($0, $1, $2)\", c0_.ToString(), c1_.ToString(),\n                            c2_.ToString());\n  }\n\n  std::string ToHexString(bool pad_zero = false) const {\n    return absl::Substitute(\"($0, $1, $2)\", c0_.ToHexString(pad_zero),\n                            c1_.ToHexString(pad_zero),\n                            c2_.ToHexString(pad_zero));\n  }\n\n  constexpr const BaseField& c0() const { return c0_; }\n  constexpr const BaseField& c1() const { return c1_; }\n  constexpr const BaseField& c2() const { return c2_; }\n\n  constexpr const BaseField& operator[](size_t index) const {\n    switch (index) {\n      case 0:\n        return c0_;\n      case 1:\n        return c1_;\n      case 2:\n        return c2_;\n    }\n    NOTREACHED();\n    return c0_;\n  }\n\n  constexpr BaseField& operator[](size_t index) {\n    return const_cast<BaseField&>(std::as_const(*this).operator[](index));\n  }\n\n  constexpr bool operator==(const Derived& other) const {\n    return c0_ == other.c0_ && c1_ == other.c1_ && c2_ == other.c2_;\n  }\n\n  constexpr bool operator!=(const Derived& other) const {\n    return !operator==(other);\n  }\n\n  constexpr bool operator<(const Derived& other) const {\n    if (c2_ == other.c2_) {\n      if (c1_ == other.c1_) return c0_ < other.c0_;\n      return c1_ < other.c1_;\n    }\n    return c2_ < other.c2_;\n  }\n\n  constexpr bool operator>(const Derived& other) const {\n    if (c2_ == other.c2_) {\n      if (c1_ == other.c1_) return c0_ > other.c0_;\n      return c1_ > other.c1_;\n    }\n    return c2_ > other.c2_;\n  }\n\n  constexpr bool operator<=(const Derived& other) const {\n    return !operator>(other);\n  }\n\n  constexpr bool operator>=(const Derived& other) const {\n    return !operator<(other);\n  }\n\n  // AdditiveSemigroup methods\n  constexpr Derived Add(const Derived& other) const {\n    return {\n        c0_ + other.c0_,\n        c1_ + other.c1_,\n        c2_ + other.c2_,\n    };\n  }\n\n  constexpr Derived& AddInPlace(const Derived& other) {\n    c0_ += other.c0_;\n    c1_ += other.c1_;\n    c2_ += other.c2_;\n    return *static_cast<Derived*>(this);\n  }\n\n  constexpr Derived DoubleImpl() const {\n    return {\n        c0_.Double(),\n        c1_.Double(),\n        c2_.Double(),\n    };\n  }\n\n  constexpr Derived& DoubleImplInPlace() {\n    c0_.DoubleInPlace();\n    c1_.DoubleInPlace();\n    c2_.DoubleInPlace();\n    return *static_cast<Derived*>(this);\n  }\n\n  // AdditiveGroup methods\n  constexpr Derived Sub(const Derived& other) const {\n    return {\n        c0_ - other.c0_,\n        c1_ - other.c1_,\n        c2_ - other.c2_,\n    };\n  }\n\n  constexpr Derived& SubInPlace(const Derived& other) {\n    c0_ -= other.c0_;\n    c1_ -= other.c1_;\n    c2_ -= other.c2_;\n    return *static_cast<Derived*>(this);\n  }\n\n  constexpr Derived Negate() const {\n    return {\n        -c0_,\n        -c1_,\n        -c2_,\n    };\n  }\n\n  constexpr Derived& NegateInPlace() {\n    c0_.NegateInPlace();\n    c1_.NegateInPlace();\n    c2_.NegateInPlace();\n    return *static_cast<Derived*>(this);\n  }\n\n  // MultiplicativeSemigroup methods\n  constexpr Derived Mul(const Derived& other) const {\n    Derived ret{};\n    DoMul(*static_cast<const Derived*>(this), other, ret);\n    return ret;\n  }\n\n  constexpr Derived& MulInPlace(const Derived& other) {\n    DoMul(*static_cast<const Derived*>(this), other,\n          *static_cast<Derived*>(this));\n    return *static_cast<Derived*>(this);\n  }\n\n  constexpr Derived Mul(const BaseField& element) const {\n    return {\n        c0_ * element,\n        c1_ * element,\n        c2_ * element,\n    };\n  }\n\n  constexpr Derived& MulInPlace(const BaseField& element) {\n    c0_ *= element;\n    c1_ *= element;\n    c2_ *= element;\n    return *static_cast<Derived*>(this);\n  }\n\n  constexpr Derived SquareImpl() const {\n    Derived ret{};\n    DoSquareImpl(*static_cast<const Derived*>(this), ret);\n    return ret;\n  }\n\n  constexpr Derived& SquareImplInPlace() {\n    DoSquareImpl(*static_cast<const Derived*>(this),\n                 *static_cast<Derived*>(this));\n    return *static_cast<Derived*>(this);\n  }\n\n  // MultiplicativeGroup methods\n  constexpr std::optional<Derived> Inverse() const {\n    Derived ret{};\n    if (LIKELY(DoInverse(*static_cast<const Derived*>(this), ret))) return ret;\n    LOG_IF_NOT_GPU(ERROR) << \"Inverse of zero attempted\";\n    return std::nullopt;\n  }\n\n  [[nodiscard]] constexpr std::optional<Derived*> InverseInPlace() {\n    if (LIKELY(DoInverse(*static_cast<const Derived*>(this),\n                         *static_cast<Derived*>(this)))) {\n      return static_cast<Derived*>(this);\n    }\n    LOG_IF_NOT_GPU(ERROR) << \"Inverse of zero attempted\";\n    return std::nullopt;\n  }\n\n protected:\n  template <typename Config, typename SFINAE>\n  friend class Fp6;\n  template <typename Config>\n  friend class Fp12;\n\n  constexpr static void DoMul(const Derived& a, const Derived& b, Derived& c) {\n    // clang-format off\n    // (a.c0, a.c1, a.c2) * (b.c0, b.c1, b.c2)\n    //   = (a.c0 + a.c1 * x + a.c2 * x²) * (b.c0 + b.c1 * x + b.c2 * x²)\n    //   = a.c0 * b.c0 + (a.c0 * b.c1 + a.c1 * b.c0) * x + (a.c0 * b.c2 + a.c1 * b.c1 + a.c2 * b.c0) * x² +\n    //     (a.c1 * b.c2 + a.c2 * b.c1) * x³ + a.c2 * b.c2 * x⁴\n    //   = a.c0 * b.c0 + (a.c1 * b.c2 + a.c2 * b.c1) * x³ +\n    //     (a.c0 * b.c1 + a.c1 * b.c0) * x + a.c2 * b.c2 * x⁴ +\n    //     (a.c0 * b.c2 + a.c1 * b.c1 + a.c2 * b.c0) * x²\n    //   = a.c0 * b.c0 + (a.c1 * b.c2 + a.c2 * b.c1) * q +\n    //     (a.c0 * b.c1 + a.c1 * b.c0) * x + a.c2 * b.c2 * q * x +\n    //     (a.c0 * b.c2 + a.c1 * b.c1 + a.c2 * b.c0) * x²\n    //   = (a.c0 * b.c0 + (a.c1 * b.c2 + a.c2 * b.c1) * q,\n    //     a.c0 * b.c1 + a.c1 * b.c0 + a.c2 * b.c2 * q,\n    //     a.c0 * b.c2 + a.c1 * b.c1 + a.c2 * b.c0)\n    // where q is |Config::kNonResidue|.\n\n    // See https://eprint.iacr.org/2006/471.pdf\n    // Devegili OhEig Scott Dahab --- Multiplication and Squaring on AbstractPairing-Friendly Fields.pdf; Section 4 (Karatsuba)\n    // clang-format on\n\n    BaseField v0 = a.c0_ * b.c0_;\n    BaseField v1 = a.c1_ * b.c1_;\n    BaseField v2 = a.c2_ * b.c2_;\n\n    // x = a.c0 * b.c1 + a.c1 * b.c0\n    BaseField x = (a.c0_ + a.c1_) * (b.c0_ + b.c1_) - v0 - v1;\n    // y = a.c0 * b.c2 + a.c2 * b.c0\n    BaseField y = (a.c0_ + a.c2_) * (b.c0_ + b.c2_) - v0 - v2;\n    // z = a.c1 * b.c2 + a.c2 * b.c1\n    BaseField z = (a.c1_ + a.c2_) * (b.c1_ + b.c2_) - v1 - v2;\n\n    // c.c0 = a.c0 * b.c0 + (a.c1 * b.c2 + a.c2 * b.c1) * q\n    c.c0_ = v0 + Config::MulByNonResidue(z);\n    // c.c1 = a.c0 * b.c1 + a.c1 * b.c0 + a.c2 * b.c2 * q\n    c.c1_ = x + Config::MulByNonResidue(v2);\n    // c.c2 = a.c0 * b.c2 + a.c2 * b.c0 + a.c1 * b.c1\n    c.c2_ = y + v1;\n  }\n\n  constexpr static void DoSquareImpl(const Derived& a, Derived& b) {\n    // clang-format off\n    // (c0, c1, c2)²\n    //   = (c0 + c1 * x + c2 * x²)²\n    //   = c0² + 2 * c0 * c1 * x + (c1² + 2 * c0 * c2) * x² + 2 * c1 * c2 * x³ + c2² * x⁴\n    //   = c0² + 2 * c0 * c1 * x + 2 * c1 * c2 * x³ + (c1² + 2 * c0 * c2) * x² + c2² * x⁴\n    //   = c0² + 2 * c1 * c2 * x³ + 2 * c0 * c1 * x + c2² * x⁴ + (c1² + 2 * c0 * c2) * x²\n    //   = c0² + 2 * c1 * c2 * q + (2 * c0 * c1  + c2² * q) * x + (c1² + 2 * c0 * c2) * x²\n    //   = (c0² + 2 * c1 * c2 * q, 2 * c0 * c1  + c2² * q, c1² + 2 * c0 * c2)\n    // where q is |Config::kNonResidue|.\n\n    // See https://eprint.iacr.org/2006/471.pdf\n    // Devegili OhEig Scott Dahab --- Multiplication and Squaring on AbstractPairing-Friendly Fields.pdf; Section 4 (CH-SQR2)\n    // clang-format on\n\n    // s0 = c0²\n    BaseField s0 = a.c0_.Square();\n    // s1 = 2 * c0 * c1\n    BaseField s1 = (a.c0_ * a.c1_).Double();\n    // s2 = (c0 - c1 + c2)²\n    //    = c0² + c1² + c2² - 2 * c0 * c1 - 2 c1 * c2 + 2 * c0 * c2\n    BaseField s2 = (a.c0_ - a.c1_ + a.c2_).Square();\n    // s3 = 2 * c1 * c2\n    BaseField s3 = (a.c1_ * a.c2_).Double();\n    // s4 = c2²\n    BaseField s4 = a.c2_.Square();\n\n    // c0 = c0² + 2 * c1 * c2 * q\n    b.c0_ = s0 + Config::MulByNonResidue(s3);\n    // c1 = 2 * c0 * c1 + c2² * q\n    b.c1_ = s1 + Config::MulByNonResidue(s4);\n    // c2 = 2 * c0 * c1 +\n    //      c0² + c1² + c2² - 2 * c0 * c1 - 2 c1 * c2 + 2 * c0 * c2 +\n    //      2 * c1 * c2 -\n    //      c0² -\n    //      c2²\n    //    = c1² + 2 * c0 * c2\n    b.c2_ = s1 + s2 + s3 - s0 - s4;\n  }\n\n  [[nodiscard]] constexpr static bool DoInverse(const Derived& a, Derived& b) {\n    if (UNLIKELY(a.IsZero())) {\n      LOG_IF_NOT_GPU(ERROR) << \"Inverse of zero attempted\";\n      return false;\n    }\n    // clang-format off\n    // See https://eprint.iacr.org/2010/354.pdf\n    // From \"High-Speed Software Implementation of the Optimal Ate AbstractPairing over Barreto-Naehrig Curves\"; Algorithm 17\n    // clang-format on\n\n    // t0 = c0²\n    BaseField t0 = a.c0_.Square();\n    // t1 = c1²\n    BaseField t1 = a.c1_.Square();\n    // t2 = c2²\n    BaseField t2 = a.c2_.Square();\n    // t3 = c0 * c1\n    BaseField t3 = a.c0_ * a.c1_;\n    // t4 = c0 * c2\n    BaseField t4 = a.c0_ * a.c2_;\n    // t5 = c1 * c2\n    BaseField t5 = a.c1_ * a.c2_;\n\n    // s0 = t0 - t5 * q\n    //    = t0 - c1 * c2 * q\n    BaseField s0 = t0 - Config::MulByNonResidue(t5);\n    // s1 = t2 * q - t3\n    //    = c2² * q - c0 * c1\n    BaseField s1 = Config::MulByNonResidue(t2) - t3;\n    // See\n    // https://github.com/arkworks-rs/algebra/blob/c92be0e/ff/src/fields/models/cubic_extension.rs#L315\n    // s2 = t1 - t4\n    //    = c1² - c0 * c2\n    BaseField s2 = t1 - t4;\n\n    // a1 = c2 * s1\n    //    = c2 * (c2² * q - c0 * c1)\n    //    = c2³ * q - c0 * c1 * c2\n    BaseField a1 = a.c2_ * s1;\n    // a2 = c1 * s2\n    //    = c1 * (c1² - c0 * c2)\n    //    = c1³ - c0 * c1 * c2\n    BaseField a2 = a.c1_ * s2;\n    // a3 = c1³ + c2³ * q - 2 * c0 * c1 * c2\n    BaseField a3 = Config::MulByNonResidue(a1 + a2);\n    // t6 = 1 / (c0 * s0 + a3)\n    //    = 1 / (c0 * (t0 - t5 * q) + c1³ + c2³ * q - 2 * c0 * c1 * c2)\n    //    = 1 / (c0 * (c0² - c1 * c2 * q) + c1³ + c2³ * q - 2 * c0 * c1 * c2)\n    //    = 1 / (c0³ + c1³ + c2³ - (2 + q) * c0 * c1 * c2)\n    const std::optional<BaseField> t6_opt = (a.c0_ * s0 + a3).Inverse();\n    if (UNLIKELY(!t6_opt)) return false;\n    BaseField t6 = std::move(*t6_opt);\n\n    // c0 = s0 * t6\n    // c0 = (t0 - c1 * c2 * q) / (c0³ + c1³ + c2³ - (2 + q) * c0 * c1 * c2)\n    //    = (c0² - c1 * c2 * q) / (c0³ + c1³ + c2³ - (2 + q) * c0 * c1 * c2)\n    b.c0_ = s0 * t6;\n    // c1 = s1 * t6\n    //    = (c2² * q - c0 * c1) / (c0³ + c1³ + c2³ - (2 + q) * c0 * c1 * c2)\n    b.c1_ = s1 * t6;\n    // c2 = s1 * t6\n    //    = (c1² - c0 * c2) / (c0³ + c1³ + c2³ - (2 + q) * c0 * c1 * c2)\n    b.c2_ = s2 * t6;\n    return true;\n  }\n\n  // c = c0_ + c1_ * X + c2_ * X²\n  BaseField c0_;\n  BaseField c1_;\n  BaseField c2_;\n};\n\ntemplate <\n    typename BaseField, typename Derived,\n    std::enable_if_t<std::is_same_v<BaseField, typename Derived::BaseField>>* =\n        nullptr>\nDerived operator*(const BaseField& element,\n                  const CubicExtensionField<Derived>& f) {\n  return static_cast<const Derived&>(f) * element;\n}\n\ntemplate <typename H, typename Derived>\nH AbslHashValue(H h, const CubicExtensionField<Derived>& f) {\n  return H::combine(std::move(h), f.c0(), f.c1(), f.c2());\n}\n\n}  // namespace math\n\nnamespace base {\n\ntemplate <typename Derived>\nclass Copyable<Derived, std::enable_if_t<std::is_base_of_v<\n                            math::CubicExtensionField<Derived>, Derived>>> {\n public:\n  static bool WriteTo(\n      const math::CubicExtensionField<Derived>& cubic_extension_field,\n      Buffer* buffer) {\n    return buffer->WriteMany(cubic_extension_field.c0(),\n                             cubic_extension_field.c1(),\n                             cubic_extension_field.c2());\n  }\n\n  static bool ReadFrom(\n      const ReadOnlyBuffer& buffer,\n      math::CubicExtensionField<Derived>* cubic_extension_field) {\n    typename Derived::BaseField c0;\n    typename Derived::BaseField c1;\n    typename Derived::BaseField c2;\n    if (!buffer.ReadMany(&c0, &c1, &c2)) return false;\n\n    *cubic_extension_field = math::CubicExtensionField<Derived>(\n        std::move(c0), std::move(c1), std::move(c2));\n    return true;\n  }\n\n  static size_t EstimateSize(\n      const math::CubicExtensionField<Derived>& cubic_extension_field) {\n    return base::EstimateSize(cubic_extension_field.c0(),\n                              cubic_extension_field.c1(),\n                              cubic_extension_field.c2());\n  }\n};\n\ntemplate <typename Derived>\nclass RapidJsonValueConverter<\n    Derived, std::enable_if_t<std::is_base_of_v<\n                 math::CubicExtensionField<Derived>, Derived>>> {\n public:\n  using BaseField = typename math::CubicExtensionField<Derived>::BaseField;\n\n  template <typename Allocator>\n  static rapidjson::Value From(const math::CubicExtensionField<Derived>& value,\n                               Allocator& allocator) {\n    rapidjson::Value object(rapidjson::kObjectType);\n    AddJsonElement(object, \"c0\", value.c0(), allocator);\n    AddJsonElement(object, \"c1\", value.c1(), allocator);\n    AddJsonElement(object, \"c2\", value.c2(), allocator);\n    return object;\n  }\n\n  static bool To(const rapidjson::Value& json_value, std::string_view key,\n                 math::CubicExtensionField<Derived>* value,\n                 std::string* error) {\n    BaseField c0;\n    BaseField c1;\n    BaseField c2;\n    if (!ParseJsonElement(json_value, \"c0\", &c0, error)) return false;\n    if (!ParseJsonElement(json_value, \"c1\", &c1, error)) return false;\n    if (!ParseJsonElement(json_value, \"c2\", &c2, error)) return false;\n    *value = math::CubicExtensionField<Derived>(std::move(c0), std::move(c1),\n                                                std::move(c2));\n    return true;\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_CUBIC_EXTENSION_FIELD_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/cubic_extension_field_unittest.cc",
    "content": "#include <optional>\n\n#include \"absl/hash/hash_testing.h\"\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n#include \"tachyon/math/finite_fields/test/gf7_3.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\nclass CubicExtensionFieldTest : public FiniteFieldTest<GF7_3> {};\n\n}  // namespace\n\nTEST_F(CubicExtensionFieldTest, Zero) {\n  EXPECT_TRUE(GF7_3::Zero().IsZero());\n  EXPECT_FALSE(GF7_3::One().IsZero());\n  EXPECT_FALSE(GF7_3::MinusOne().IsZero());\n}\n\nTEST_F(CubicExtensionFieldTest, One) {\n  EXPECT_TRUE(GF7_3::One().IsOne());\n  EXPECT_FALSE(GF7_3::Zero().IsOne());\n  EXPECT_FALSE(GF7_3::MinusOne().IsOne());\n}\n\nTEST_F(CubicExtensionFieldTest, MinusOne) {\n  EXPECT_FALSE(GF7_3::One().IsMinusOne());\n  EXPECT_FALSE(GF7_3::Zero().IsMinusOne());\n  EXPECT_TRUE(GF7_3::MinusOne().IsMinusOne());\n}\n\nTEST_F(CubicExtensionFieldTest, TwoInv) {\n  EXPECT_TRUE((GF7_3::TwoInv() * GF7_3(GF7(2))).IsOne());\n  EXPECT_FALSE((GF7_3::TwoInv() * GF7_3::One()).IsOne());\n}\n\nTEST_F(CubicExtensionFieldTest, Random) {\n  bool success = false;\n  GF7_3 r = GF7_3::Random();\n  for (size_t i = 0; i < 100; ++i) {\n    if (r != GF7_3::Random()) {\n      success = true;\n      break;\n    }\n  }\n  EXPECT_TRUE(success);\n}\n\nTEST_F(CubicExtensionFieldTest, Norm) {\n  constexpr static uint32_t kModulus = GF7::Config::kModulus;\n  GF7_3 r = GF7_3::Random();\n  GF7_3 r_to_p = r.Pow(kModulus);\n  GF7_3 r_to_p2 = r_to_p.Pow(kModulus);\n  EXPECT_EQ(r.Norm(), (r * r_to_p * r_to_p2).c0());\n}\n\nTEST_F(CubicExtensionFieldTest, EqualityOperators) {\n  GF7_3 f(GF7(3), GF7(4), GF7(5));\n  GF7_3 f2(GF7(4), GF7(4), GF7(5));\n  EXPECT_NE(f, f2);\n\n  GF7_3 f3(GF7(4), GF7(3), GF7(5));\n  EXPECT_NE(f2, f3);\n\n  GF7_3 f4(GF7(3), GF7(4), GF7(6));\n  EXPECT_NE(f, f4);\n\n  GF7_3 f5(GF7(3), GF7(4), GF7(5));\n  EXPECT_EQ(f, f5);\n}\n\nTEST_F(CubicExtensionFieldTest, ComparisonOperator) {\n  GF7_3 f(GF7(3), GF7(4), GF7(5));\n  GF7_3 f2(GF7(4), GF7(4), GF7(5));\n  EXPECT_LT(f, f2);\n  EXPECT_LE(f, f2);\n  EXPECT_GT(f2, f);\n  EXPECT_GE(f2, f);\n\n  GF7_3 f3(GF7(4), GF7(3), GF7(5));\n  GF7_3 f4(GF7(3), GF7(4), GF7(5));\n  EXPECT_LT(f3, f4);\n  EXPECT_LE(f3, f4);\n  EXPECT_GT(f4, f3);\n  EXPECT_GE(f4, f3);\n\n  GF7_3 f5(GF7(4), GF7(5), GF7(3));\n  GF7_3 f6(GF7(3), GF7(2), GF7(5));\n  EXPECT_LT(f5, f6);\n  EXPECT_LE(f5, f6);\n  EXPECT_GT(f6, f5);\n  EXPECT_GE(f6, f5);\n}\n\nTEST_F(CubicExtensionFieldTest, AdditiveOperators) {\n  struct {\n    GF7_3 a;\n    GF7_3 b;\n    GF7_3 sum;\n    GF7_3 amb;\n    GF7_3 bma;\n  } tests[] = {\n      {\n          {GF7(1), GF7(2), GF7(3)},\n          {GF7(3), GF7(5), GF7(6)},\n          {GF7(4), GF7(0), GF7(2)},\n          {GF7(5), GF7(4), GF7(4)},\n          {GF7(2), GF7(3), GF7(3)},\n      },\n  };\n\n  for (const auto& test : tests) {\n    EXPECT_EQ(test.a + test.b, test.sum);\n    EXPECT_EQ(test.b + test.a, test.sum);\n    EXPECT_EQ(test.a - test.b, test.amb);\n    EXPECT_EQ(test.b - test.a, test.bma);\n\n    GF7_3 tmp = test.a;\n    tmp += test.b;\n    EXPECT_EQ(tmp, test.sum);\n    tmp -= test.b;\n    EXPECT_EQ(tmp, test.a);\n  }\n}\n\nTEST_F(CubicExtensionFieldTest, AdditiveGroupOperators) {\n  GF7_3 f(GF7(3), GF7(4), GF7(5));\n  GF7_3 f_neg(GF7(4), GF7(3), GF7(2));\n  EXPECT_EQ(-f, f_neg);\n  f.NegateInPlace();\n  EXPECT_EQ(f, f_neg);\n\n  f = GF7_3(GF7(3), GF7(4), GF7(5));\n  GF7_3 f_dbl(GF7(6), GF7(1), GF7(3));\n  EXPECT_EQ(f.Double(), f_dbl);\n  f.DoubleInPlace();\n  EXPECT_EQ(f, f_dbl);\n}\n\nTEST_F(CubicExtensionFieldTest, MultiplicativeOperators) {\n  struct {\n    GF7_3 a;\n    GF7_3 b;\n    GF7_3 mul;\n    GF7_3 adb;\n    GF7_3 bda;\n  } tests[] = {\n      {\n          {GF7(1), GF7(2), GF7(3)},\n          {GF7(3), GF7(5), GF7(6)},\n          {GF7(1), GF7(5), GF7(4)},\n          {GF7(3), GF7(3), GF7(4)},\n          {GF7(3), GF7(1), GF7(2)},\n      },\n  };\n\n  for (const auto& test : tests) {\n    EXPECT_EQ(test.a * test.b, test.mul);\n    EXPECT_EQ(test.b * test.a, test.mul);\n    EXPECT_EQ(test.a / test.b, test.adb);\n    EXPECT_EQ(test.b / test.a, test.bda);\n\n    GF7_3 tmp = test.a;\n    tmp *= test.b;\n    EXPECT_EQ(tmp, test.mul);\n    ASSERT_TRUE(tmp /= test.b);\n    EXPECT_EQ(tmp, test.a);\n  }\n}\n\nTEST_F(CubicExtensionFieldTest, MultiplicativeOperators2) {\n  GF7_3 f(GF7(3), GF7(4), GF7(5));\n  GF7_3 f_mul(GF7(6), GF7(1), GF7(3));\n  EXPECT_EQ(f * GF7(2), f_mul);\n  f *= GF7(2);\n  EXPECT_EQ(f, f_mul);\n}\n\nTEST_F(CubicExtensionFieldTest, MultiplicativeGroupOperators) {\n  GF7_3 f = GF7_3::Random();\n  std::optional<GF7_3> f_inv = f.Inverse();\n  if (UNLIKELY(f.IsZero())) {\n    ASSERT_FALSE(f_inv);\n    ASSERT_FALSE(f.InverseInPlace());\n  } else {\n    EXPECT_EQ(f * *f_inv, GF7_3::One());\n    GF7_3 f_tmp = f;\n    EXPECT_EQ(**f.InverseInPlace() * f_tmp, GF7_3::One());\n  }\n\n  f = GF7_3(GF7(3), GF7(4), GF7(5));\n  GF7_3 f_sqr = GF7_3(GF7(5), GF7(4), GF7(4));\n  EXPECT_EQ(f.Square(), f_sqr);\n  f.SquareInPlace();\n  EXPECT_EQ(f, f_sqr);\n}\n\nTEST_F(CubicExtensionFieldTest, JsonValueConverter) {\n  GF7_3 expected_point(GF7(1), GF7(2), GF7(3));\n  std::string expected_json = R\"({\"c0\":1,\"c1\":2,\"c2\":3})\";\n\n  GF7_3 p;\n  std::string error;\n  ASSERT_TRUE(base::ParseJson(expected_json, &p, &error));\n  ASSERT_TRUE(error.empty());\n  EXPECT_EQ(p, expected_point);\n\n  std::string json = base::WriteToJson(p);\n  EXPECT_EQ(json, expected_json);\n}\n\nTEST_F(CubicExtensionFieldTest, Hash) {\n  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(\n      std::make_tuple(GF7_3::Zero(), GF7_3::One(), GF7_3::Random())));\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/finite_fields/cyclotomic_multiplicative_subgroup.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_FINITE_FIELDS_CYCLOTOMIC_MULTIPLICATIVE_SUBGROUP_H_\n#define TACHYON_MATH_FINITE_FIELDS_CYCLOTOMIC_MULTIPLICATIVE_SUBGROUP_H_\n\n#include <optional>\n#include <tuple>\n#include <vector>\n\n#include \"tachyon/base/containers/adapters.h\"\n#include \"tachyon/base/optional.h\"\n#include \"tachyon/math/finite_fields/finite_field.h\"\n\nnamespace tachyon::math {\nnamespace internal {\n\nSUPPORTS_UNARY_OPERATOR(FastCyclotomicSquare);\nSUPPORTS_UNARY_OPERATOR(FastCyclotomicInverse);\n\n}  // namespace internal\n\n// clang-format off\n// Fields that have a cyclotomic multiplicative subgroup, and which can\n// leverage efficient inversion and squaring algorithms for elements in this\n// subgroup. If a field has multiplicative order pᵈ - 1, the cyclotomic\n// subgroups refer to subgroups of order φₙ(p), for any n < d, where φₙ is the\n// [n-th cyclotomic polynomial](https://en.wikipedia.org/wiki/Cyclotomic_polynomial).\n// clang-format on\ntemplate <typename F>\nclass CyclotomicMultiplicativeSubgroup : public FiniteField<F> {\n public:\n  [[nodiscard]] constexpr F CyclotomicSquare() const {\n    const F* f = static_cast<const F*>(this);\n    if constexpr (internal::SupportsFastCyclotomicSquare<F>::value) {\n      return f->FastCyclotomicSquare();\n    } else {\n      return f->Square();\n    }\n  }\n\n  constexpr F& CyclotomicSquareInPlace() {\n    F* f = static_cast<F*>(this);\n    if constexpr (internal::SupportsFastCyclotomicSquareInPlace<F>::value) {\n      return f->FastCyclotomicSquareInPlace();\n    } else {\n      return f->SquareInPlace();\n    }\n  }\n\n  constexpr std::optional<F> CyclotomicInverse() const {\n    const F* f = static_cast<const F*>(this);\n    if constexpr (internal::SupportsFastCyclotomicInverse<F>::value) {\n      return f->FastCyclotomicInverse();\n    } else {\n      return f->Inverse();\n    }\n  }\n\n  [[nodiscard]] constexpr std::optional<F*> CyclotomicInverseInPlace() {\n    F* f = static_cast<F*>(this);\n    if constexpr (internal::SupportsFastCyclotomicInverseInPlace<F>::value) {\n      return f->FastCyclotomicInverseInPlace();\n    } else {\n      return f->InverseInPlace();\n    }\n  }\n\n  template <size_t N>\n  [[nodiscard]] F CyclotomicPow(const BigInt<N>& exponent) const {\n    const F* f = static_cast<const F*>(this);\n    if (f->IsZero()) return F::Zero();\n\n    if constexpr (internal::SupportsFastCyclotomicInverse<F>::value) {\n      // We only use NAF-based exponentiation if inverses are fast to compute.\n      std::vector<int8_t> naf = exponent.ToNAF();\n      auto naf_rev_iterator = base::Reversed(naf);\n      return DoCyclotomicPow(naf_rev_iterator.begin(), naf_rev_iterator.end());\n    } else {\n      return DoCyclotomicPow(BitIteratorBE<BigInt<N>>::begin(&exponent, true),\n                             BitIteratorBE<BigInt<N>>::end(&exponent));\n    }\n  }\n\n  template <size_t N>\n  F& CyclotomicPowInPlace(const BigInt<N>& exponent) {\n    F* f = static_cast<F*>(this);\n    return *f = CyclotomicPow(exponent);\n  }\n\n private:\n  // Helper function to calculate the double-and-add loop for exponentiation.\n  template <typename Iterator>\n  constexpr F DoCyclotomicPow(Iterator begin, Iterator end) const {\n    const F* f = static_cast<const F*>(this);\n    // If the inverse is fast and we're using naf, we compute the inverse of the\n    // base. Otherwise we do nothing with the variable, so we default it to one.\n    F inverse = F::One();\n    if constexpr (internal::SupportsFastCyclotomicInverse<F>::value) {\n      inverse = unwrap(CyclotomicInverse());\n    } else {\n      std::ignore = inverse;\n    }\n\n    F ret = F::One();\n    bool found_nonzero = false;\n    for (Iterator it = begin; it != end; ++it) {\n      if (found_nonzero) {\n        ret.CyclotomicSquareInPlace();\n      }\n\n      int8_t v = static_cast<int8_t>(*it);\n      if (v == 0) continue;\n      found_nonzero = true;\n\n      if (v > 0) {\n        ret *= *f;\n      } else {\n        if constexpr (internal::SupportsFastCyclotomicInverse<F>::value) {\n          // only use naf if inversion is fast.\n          ret *= inverse;\n        }\n      }\n    }\n    return ret;\n  }\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_CYCLOTOMIC_MULTIPLICATIVE_SUBGROUP_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/extended_packed_field_traits_forward.h",
    "content": "#ifndef TACHYON_MATH_FINITE_FIELDS_EXTENDED_PACKED_FIELD_TRAITS_FORWARD_H_\n#define TACHYON_MATH_FINITE_FIELDS_EXTENDED_PACKED_FIELD_TRAITS_FORWARD_H_\n\nnamespace tachyon::math {\n\ntemplate <typename T, typename SFINAE = void>\nstruct ExtendedPackedFieldTraits;\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_EXTENDED_PACKED_FIELD_TRAITS_FORWARD_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/extension_field_base.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_MATH_FINITE_FIELDS_EXTENSION_FIELD_BASE_H_\n#define TACHYON_MATH_FINITE_FIELDS_EXTENSION_FIELD_BASE_H_\n\n#include <iterator>\n#include <vector>\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/math/finite_fields/extended_packed_field_traits_forward.h\"\n#include \"tachyon/math/finite_fields/extension_field_traits_forward.h\"\n#include \"tachyon/math/finite_fields/packed_field_traits_forward.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename Derived>\nclass ExtensionFieldBase {\n public:\n  class ConstIterator {\n   public:\n    using difference_type = std::ptrdiff_t;\n    using value_type = typename ExtensionFieldTraits<Derived>::BaseField;\n    using pointer = value_type*;\n    using reference = value_type&;\n    using iterator_category = std::forward_iterator_tag;\n\n    ConstIterator(const Derived& derived, size_t idx)\n        : derived_(derived), idx_(idx) {}\n\n    bool operator==(const ConstIterator& other) const {\n      return &derived_ == &other.derived_ && idx_ == other.idx_;\n    }\n    bool operator!=(const ConstIterator& other) const {\n      return !(*this == other);\n    }\n\n    ConstIterator& operator++() {\n      ++idx_;\n      return *this;\n    }\n\n    ConstIterator operator++(int) {\n      ConstIterator iterator(*this);\n      ++(*this);\n      return iterator;\n    }\n\n    const pointer operator->() const { return &derived_[idx_]; }\n\n    const value_type& operator*() const { return derived_[idx_]; }\n\n   private:\n    const Derived& derived_;\n    size_t idx_;\n  };\n\n  template <\n      typename T,\n      typename ExtendedPackedField =\n          typename ExtendedPackedFieldTraits<T>::ExtendedPackedField,\n      std::enable_if_t<!ExtendedPackedFieldTraits<T>::kIsExtendedPackedField>* =\n          nullptr>\n  static std::vector<ExtendedPackedField> GetExtendedPackedPowers(const T& base,\n                                                                  size_t size) {\n    using BaseField = typename T::BaseField;\n    using PackedField = typename PackedFieldTraits<BaseField>::PackedField;\n    constexpr uint32_t kDegree = T::ExtensionDegree();\n\n    // if |PackedField::N| = 8,\n    // |first_n_powers| = [ 1, b, b², b³,..., b⁶, b⁷ ], where b is an extension\n    // field of |base|.\n    ExtendedPackedField first_n_powers;\n    T pow = T::One();\n    for (size_t i = 0; i < PackedField::N; ++i) {\n      for (uint32_t j = 0; j < kDegree; ++j) {\n        first_n_powers[j][i] = pow[j];\n      }\n      pow *= base;\n    }\n\n    // |multiplier| = [ b⁸, b⁸, ..., b⁸, b⁸ ], where b is an extension field of\n    // |base|. #|multiplier| = 8\n    ExtendedPackedField multiplier;\n    for (size_t i = 0; i < PackedField::N; ++i) {\n      for (uint32_t j = 0; j < kDegree; ++j) {\n        multiplier[j][i] = pow[j];\n      }\n    }\n\n    // |ret[i]| = [ b⁸ⁱ, b⁸ⁱ⁺¹, ..., b⁸ⁱ⁺⁶, b⁸ⁱ⁺⁷ ], where b is an extension\n    // field of |base|.\n    std::vector<ExtendedPackedField> ret;\n    ret.reserve(size);\n    ret.emplace_back(first_n_powers);\n    for (size_t i = 0; i < size - 1; ++i) {\n      ret.emplace_back(ret[i] * multiplier);\n    }\n    return ret;\n  }\n\n  ConstIterator begin() const {\n    return {static_cast<const Derived&>(*this), 0};\n  }\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_EXTENSION_FIELD_BASE_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/extension_field_traits_forward.h",
    "content": "#ifndef TACHYON_MATH_FINITE_FIELDS_EXTENSION_FIELD_TRAITS_FORWARD_H_\n#define TACHYON_MATH_FINITE_FIELDS_EXTENSION_FIELD_TRAITS_FORWARD_H_\n\nnamespace tachyon::math {\n\ntemplate <typename T, typename SFINAE = void>\nstruct ExtensionFieldTraits;\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_EXTENSION_FIELD_TRAITS_FORWARD_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/finite_field.h",
    "content": "#ifndef TACHYON_MATH_FINITE_FIELDS_FINITE_FIELD_H_\n#define TACHYON_MATH_FINITE_FIELDS_FINITE_FIELD_H_\n\n#include \"tachyon/math/base/field.h\"\n#include \"tachyon/math/finite_fields/finite_field_traits.h\"\n#include \"tachyon/math/finite_fields/square_root_algorithms/shanks.h\"\n#include \"tachyon/math/finite_fields/square_root_algorithms/tonelli_shanks.h\"\n\nnamespace tachyon::math {\n\n// FiniteField is a field with a finite field order (i.e., number of\n// elements), also called a Galois field. The order of a finite field is\n// always a prime or a power of a prime (Birkhoff and Mac Lane 1996). For each\n// prime power, there exists exactly one (with the usual caveat that \"exactly\n// one\" means \"exactly one up to an isomorphism\") finite field GF(pⁿ), often\n// written as F(pⁿ) in current usage.\n// See https://mathworld.wolfram.com/FiniteField.html\ntemplate <typename F>\nclass FiniteField : public Field<F> {\n public:\n  using Config = typename FiniteFieldTraits<F>::Config;\n\n  constexpr bool SquareRoot(F* ret) const {\n    if constexpr (Config::kModulusModFourIsThree) {\n      return ComputeShanksSquareRoot(*static_cast<const F*>(this), ret);\n    } else {\n      static_assert(Config::kHasTwoAdicRootOfUnity);\n      return ComputeTonelliShanksSquareRoot(\n          *static_cast<const F*>(this),\n          F::FromMontgomery(Config::kTwoAdicRootOfUnity), ret);\n    }\n    return false;\n  }\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_FINITE_FIELD_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/finite_field_forwards.h",
    "content": "#ifndef TACHYON_MATH_FINITE_FIELDS_FINITE_FIELD_FORWARDS_H_\n#define TACHYON_MATH_FINITE_FIELDS_FINITE_FIELD_FORWARDS_H_\n\nnamespace tachyon::math {\n\ntemplate <typename Config>\nclass BinaryField;\n\ntemplate <typename Config, typename SFINAE = void>\nclass PrimeField;\n\ntemplate <typename Config>\nclass PrimeFieldGpu;\n\ntemplate <typename Config>\nclass PrimeFieldGpuDebug;\n\ntemplate <typename Config>\nclass Fp2;\n\ntemplate <typename Config>\nclass Fp3;\n\ntemplate <typename Config, typename SFINAE = void>\nclass Fp4;\n\ntemplate <typename Config, typename SFINAE = void>\nclass Fp6;\n\ntemplate <typename Config>\nclass Fp12;\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_FINITE_FIELD_FORWARDS_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/finite_field_traits.h",
    "content": "#ifndef TACHYON_MATH_FINITE_FIELDS_FINITE_FIELD_TRAITS_H_\n#define TACHYON_MATH_FINITE_FIELDS_FINITE_FIELD_TRAITS_H_\n\n#include <stdint.h>\n\n#include \"third_party/eigen3/Eigen/Core\"\n\n#include \"tachyon/math/finite_fields/finite_field_forwards.h\"\n#include \"tachyon/math/matrix/cost_calculator_forward.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename T>\nstruct FiniteFieldTraits {\n  static constexpr bool kIsFiniteField = false;\n  static constexpr bool kIsPrimeField = false;\n  static constexpr bool kIsPackedPrimeField = false;\n  static constexpr bool kIsExtensionField = false;\n};\n\ntemplate <typename _Config>\nstruct FiniteFieldTraits<BinaryField<_Config>> {\n  static constexpr bool kIsFiniteField = true;\n  static constexpr bool kIsPrimeField = false;\n  static constexpr bool kIsPackedPrimeField = false;\n  static constexpr bool kIsExtensionField = true;\n\n  using Config = _Config;\n};\n\ntemplate <typename _Config>\nstruct FiniteFieldTraits<PrimeField<_Config>> {\n  static constexpr bool kIsFiniteField = true;\n  static constexpr bool kIsPrimeField = true;\n  static constexpr bool kIsPackedPrimeField = false;\n  static constexpr bool kIsExtensionField = false;\n\n  using PrimeField = tachyon::math::PrimeField<_Config>;\n  using Config = _Config;\n};\n\ntemplate <typename _Config>\nstruct FiniteFieldTraits<PrimeFieldGpu<_Config>> {\n  static constexpr bool kIsFiniteField = true;\n  static constexpr bool kIsPrimeField = true;\n  static constexpr bool kIsPackedPrimeField = false;\n  static constexpr bool kIsExtensionField = false;\n\n  using Config = _Config;\n};\n\ntemplate <typename _Config>\nstruct FiniteFieldTraits<PrimeFieldGpuDebug<_Config>> {\n  static constexpr bool kIsFiniteField = true;\n  static constexpr bool kIsPrimeField = true;\n  static constexpr bool kIsPackedPrimeField = false;\n  static constexpr bool kIsExtensionField = false;\n\n  using Config = _Config;\n};\n\ntemplate <typename _Config>\nstruct FiniteFieldTraits<Fp2<_Config>> {\n  static constexpr bool kIsFiniteField = true;\n  static constexpr bool kIsPrimeField = false;\n  static constexpr bool kIsPackedPrimeField = false;\n  static constexpr bool kIsExtensionField = true;\n\n  using Config = _Config;\n};\n\ntemplate <typename _Config>\nstruct FiniteFieldTraits<Fp3<_Config>> {\n  static constexpr bool kIsFiniteField = true;\n  static constexpr bool kIsPrimeField = false;\n  static constexpr bool kIsPackedPrimeField = false;\n  static constexpr bool kIsExtensionField = true;\n\n  using Config = _Config;\n};\n\ntemplate <typename _Config>\nstruct FiniteFieldTraits<Fp4<_Config>> {\n  static constexpr bool kIsFiniteField = true;\n  static constexpr bool kIsPrimeField = false;\n  static constexpr bool kIsPackedPrimeField = false;\n  static constexpr bool kIsExtensionField = true;\n\n  using Config = _Config;\n};\n\ntemplate <typename _Config>\nstruct FiniteFieldTraits<Fp6<_Config>> {\n  static constexpr bool kIsFiniteField = true;\n  static constexpr bool kIsPrimeField = false;\n  static constexpr bool kIsPackedPrimeField = false;\n  static constexpr bool kIsExtensionField = true;\n\n  using Config = _Config;\n};\n\ntemplate <typename _Config>\nstruct FiniteFieldTraits<Fp12<_Config>> {\n  static constexpr bool kIsFiniteField = true;\n  static constexpr bool kIsPrimeField = false;\n  static constexpr bool kIsPackedPrimeField = false;\n  static constexpr bool kIsExtensionField = true;\n\n  using Config = _Config;\n};\n\ntemplate <typename T>\nusing MaybeUnpack =\n    std::conditional_t<FiniteFieldTraits<T>::kIsPackedPrimeField,\n                       typename FiniteFieldTraits<T>::PrimeField, T>;\n\n}  // namespace tachyon::math\n\nnamespace Eigen {\n\ntemplate <typename F>\nstruct NumTraits<\n    F, std::enable_if_t<tachyon::math::FiniteFieldTraits<F>::kIsExtensionField>>\n    : GenericNumTraits<F> {\n  using BasePrimeField = typename F::BasePrimeField;\n  constexpr static uint32_t kDegreeOverBasePrimeField =\n      F::kDegreeOverBasePrimeField;\n\n  enum {\n    IsInteger = 1,\n    IsField = 1,\n    IsSigned = 0,\n    IsComplex = 0,\n    RequireInitialization = 0,\n    ReadCost =\n        tachyon::math::CostCalculator<BasePrimeField>::ComputeReadCost() *\n        kDegreeOverBasePrimeField,\n    AddCost = tachyon::math::CostCalculator<BasePrimeField>::ComputeAddCost() *\n              kDegreeOverBasePrimeField,\n    MulCost = tachyon::math::CostCalculator<BasePrimeField>::ComputeMulCost() *\n              kDegreeOverBasePrimeField,\n  };\n};\n\n}  // namespace Eigen\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_FINITE_FIELD_TRAITS_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/finite_field_unittest.cc",
    "content": "#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/elliptic_curves/bn/bn254/fq.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/fr.h\"\n\nnamespace tachyon::math {\n\nnamespace {\ntemplate <typename PrimeField>\nclass FiniteFieldTest : public testing::Test {\n public:\n  static void SetUpTestSuite() { PrimeField::Init(); }\n};\n\n}  // namespace\n\nusing PrimeFieldTypes = testing::Types<bn254::Fq, bn254::Fr>;\n\nTYPED_TEST_SUITE(FiniteFieldTest, PrimeFieldTypes);\n\nTYPED_TEST(FiniteFieldTest, SquareRoot) {\n  using F = TypeParam;\n\n  static_assert(bn254::Fq::Config::kModulusModFourIsThree);\n  static_assert(!bn254::Fr::Config::kModulusModFourIsThree);\n\n  bool success = false;\n  F f = F::Random();\n  for (size_t i = 0; i < 100; ++i) {\n    F sqrt;\n    if (f.SquareRoot(&sqrt)) {\n      EXPECT_EQ(sqrt.SquareInPlace(), f);\n      success = true;\n      break;\n    } else {\n      EXPECT_EQ(f.Legendre(), LegendreSymbol::kMinusOne);\n    }\n    f = F::Random();\n  }\n  EXPECT_TRUE(success);\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/finite_fields/fp12.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_FINITE_FIELDS_FP12_H_\n#define TACHYON_MATH_FINITE_FIELDS_FP12_H_\n\n#include <type_traits>\n#include <utility>\n\n#include \"absl/base/call_once.h\"\n\n#include \"tachyon/math/base/gmp/gmp_util.h\"\n#include \"tachyon/math/finite_fields/extension_field_traits_forward.h\"\n#include \"tachyon/math/finite_fields/quadratic_extension_field.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename Config>\nclass Fp12 final : public QuadraticExtensionField<Fp12<Config>> {\n public:\n  using BaseField = typename Config::BaseField;\n  using BasePrimeField = typename Config::BasePrimeField;\n  using FrobeniusCoefficient = typename Config::FrobeniusCoefficient;\n\n  using Fp6 = BaseField;\n  using Fp2 = typename Fp6::BaseField;\n\n  using CpuField = Fp12<Config>;\n  // TODO(chokobole): Implement Fp12Gpu\n  using GpuField = Fp12<Config>;\n\n  using QuadraticExtensionField<Fp12<Config>>::QuadraticExtensionField;\n\n  static_assert(Config::kDegreeOverBaseField == 2);\n  static_assert(BaseField::ExtensionDegree() == 6);\n\n  constexpr static uint32_t kDegreeOverBasePrimeField = 12;\n\n  static void Init() {\n    static absl::once_flag once;\n    absl::call_once(once, &Fp12::DoInit);\n  }\n\n  // CyclotomicMultiplicativeSubgroup methods\n  Fp12 FastCyclotomicSquare() const {\n    Fp12 ret;\n    DoFastCyclotomicSquare(*this, ret);\n    return ret;\n  }\n\n  Fp12& FastCyclotomicSquareInPlace() {\n    DoFastCyclotomicSquare(*this, *this);\n    return *this;\n  }\n\n  // Return α = (α₀', α₁', α₂', α₃', α₄', α₅'), such that\n  // α = (α₀ + α₁x + α₂x² + (α₃ + α₄x + α₅x²)y) * (β₀ + β₃y + β₄xy)\n  Fp12& MulInPlaceBy034(const Fp2& beta0, const Fp2& beta3, const Fp2& beta4) {\n    // clang-format off\n    // α = (α₀ + α₁x + α₂x² + (α₃ + α₄x + α₅x²)y) * (β₀ + β₃y + β₄xy)\n    //   = (α₀β₀ + α₃β₃p + α₅β₄pq) + (α₁β₀ + α₃β₄p + α₄β₃p)x + (α₂β₀ + α₄β₄p + α₅β₃p)x² +\n    //     (α₀β₃ + α₂β₄q + α₃β₀ + (α₀β₄ + α₁β₃ + α₄β₀)x + (α₁β₄ + α₂β₃ + α₅β₀)x²)y\n    //   = (α₃β₃ + α₅β₄q + (α₃β₄ + α₄β₃)x + (α₄β₄ + α₅β₃)x²)p + α₀β₀ + α₁β₀x + α₂β₀x²\n    //     (α₀β₃ + α₂β₄q + α₃β₀ + (α₀β₄ + α₁β₃ + α₄β₀)x + (α₁β₄ + α₂β₃ + α₅β₀)x²)y,\n    //      where p = y² and q = x³\n    // clang-format on\n\n    // a = α₀β₀ + α₁β₀x + α₂β₀x²\n    Fp6 a = this->c0_ * beta0;\n    // b = (α₃ + α₄x + α₅x²) * (β₃ + β₄x)\n    //   = (α₃β₃ + α₅β₄q) + (α₃β₄ + α₄β₃)x + (α₄β₄ + α₅β₃)x², where q = x³\n    Fp6 b = this->c1_;\n    b.MulInPlaceBy01(beta3, beta4);\n\n    // c1 = (α₀ + α₃) + (α₁ + α₄)x + (α₂ + α₅)x²\n    this->c1_ += this->c0_;\n    // c1 = ((α₀ + α₃) + (α₁ + α₄)x + (α₂ + α₅)x²) * (β₀ + β₃ + β₄x)\n    //    = ((α₀ + α₃) * (β₀ + β₃) + (α₂ + α₅)β₄q) +\n    //      ((α₁ + α₄) * (β₀ + β₃) + (α₀ + α₃)β₄)x +\n    //      ((α₂ + α₅) * (β₀ + β₃) + (α₁ + α₄)β₄)x², where q = x³\n    //    = (α₀β₀ + α₀β₃ + α₃β₀ + α₃β₃ + α₂β₄q + α₅β₄q) +\n    //      (α₁β₀ + α₁β₃ + α₄β₀ + α₄β₃ + α₀β₄ + α₃β₄)x +\n    //      (α₂β₀ + α₂β₃ + α₅β₀ + α₅β₃ + α₁β₄ + α₄β₄)x², where q = x³\n    //    = (α₀β₀ + α₀β₃ + α₂β₄q + α₃β₀ + α₃β₃ + α₅β₄q) +\n    //      (α₀β₄ + α₁β₀ + α₁β₃ + α₃β₄ + α₄β₀ + α₄β₃)x +\n    //      (α₁β₄ + α₂β₀ + α₂β₃ + α₄β₄ + α₅β₀ + α₅β₃)x², where q = x³\n    this->c1_.MulInPlaceBy01(beta0 + beta3, beta4);\n    // c1 = (α₀β₃ + α₂β₄q + α₃β₀ + α₃β₃ + α₅β₄q) +\n    //      (α₀β₄ + α₁β₃ + α₃β₄ + α₄β₀ + α₄β₃)x +\n    //      (α₁β₄ + α₂β₃ + α₄β₄ + α₅β₀ + α₅β₃)x², where q = x³\n    this->c1_ -= a;\n    // c1 = (α₀β₃ + α₂β₄q + α₃β₀) +\n    //      (α₀β₄ + α₁β₃ + α₄β₀)x +\n    //      (α₁β₄ + α₂β₃ + α₅β₀)x², where q = x³\n    this->c1_ -= b;\n    // c0 = ((α₃β₃ + α₅β₄q) + (α₄β₃ + α₃β₄)x + (α₅β₃ + α₄β₄)x²)p +\n    //      α₀β₀ + α₁β₀x + α₂β₀x², where p = y² and q = x³\n    this->c0_ = Config::MulByNonResidue(b);\n    this->c0_ += a;\n    return *this;\n  }\n\n  // Return α = (α₀', α₁', α₂', α₃', α₄', α₅'), such that\n  // α = (α₀ + α₁x + α₂x² + (α₃ + α₄x + α₅x²)y) * (β₀ + β₁x + β₄xy)\n  Fp12& MulInPlaceBy014(const Fp2& beta0, const Fp2& beta1, const Fp2& beta4) {\n    // clang-format off\n    // α = (α₀ + α₁x + α₂x² + (α₃ + α₄x + α₅x²)y) * (β₀ + β₁x + β₄xy)\n    //   = (α₀β₀ + α₂β₁q + α₅β₄pq) + (α₀β₁ + α₁β₀ + α₃β₄p)x + (α₁β₁ + α₂β₀ + α₄β₄p)x² +\n    //     (α₂β₄q + α₃β₀ + α₅β₁q + (α₀β₄ + α₃β₁ + α₄β₀)x + (α₁β₄ + α₄β₁ + α₅β₀)x²)y\n    //   = (α₅β₄q + α₃β₄x + α₄β₄x²)p + (α₀β₀ + α₂β₁q) + (α₀β₁ + α₁β₀)x + (α₁β₁ + α₂β₀)x² +\n    //     (α₂β₄q + α₃β₀ + α₅β₁q + (α₀β₄ + α₃β₁ + α₄β₀)x + (α₁β₄ + α₄β₁ + α₅β₀)x²)y,\n    //      where p = y² and q = x³\n    // clang-format on\n\n    // c0 = (α₅β₄q + α₃β₄x + α₄β₄x²)p +\n    //      (α₀β₀ + α₂β₁q) + (α₀β₁ + α₁β₀)x + (α₁β₁ + α₂β₀)x²,\n    //      where p = y² and q = x³\n    // c1 = (α₃β₀ + (α₂β₄ + α₅β₁)q) +\n    //      (α₀β₄ + α₃β₁ + α₄β₀)x +\n    //      (α₁β₄ + α₄β₁ + α₅β₀)x², where q = x³\n\n    // a = (α₀ + α₁x + α₂x²) * (β₀ + β₁x)\n    //   = (α₀β₀ + α₂β₁q) + (α₀β₁ + α₁β₀)x + (α₁β₁ + α₂β₀)x², where q = x³\n    Fp6 a = this->c0_;\n    a.MulInPlaceBy01(beta0, beta1);\n    // b = (α₃ + α₄x + α₅x²) * β₄x\n    //   = α₅β₄q + α₃β₄x + α₄β₄x²\n    Fp6 b = this->c1_;\n    b.MulInPlaceBy1(beta4);\n\n    // c1 = (α₀ + α₃) + (α₁ + α₄)x + (α₂ + α₅)x²\n    this->c1_ += this->c0_;\n    // c1 = ((α₀ + α₃) + (α₁ + α₄)x + (α₂ + α₅)x²) * (β₀ + (β₁ + β₄)x)\n    //    = ((α₀ + α₃) * β₀ + (α₂ + α₅)(β₁ + β₄)q) +\n    //      ((α₁ + α₄) * β₀ + (α₀ + α₃)(β₁ + β₄))x +\n    //      ((α₂ + α₅) * β₀ + (α₁ + α₄)(β₁ + β₄))x², where q = x³\n    //    = (α₀β₀ + α₃β₀ + (α₂β₁ + α₂β₄ + α₅β₁ + α₅β₄)q) +\n    //      (α₁β₀ + α₄β₀ + α₀β₁ + α₀β₄ + α₃β₁ + α₃β₄)x +\n    //      (α₂β₀ + α₅β₀ + α₁β₁ + α₁β₄ + α₄β₁ + α₄β₄)x², where q = x³\n    //    = (α₀β₀ + α₂β₁q + α₂β₄q + α₃β₀ + α₅β₁q + α₅β₄q) +\n    //      (α₀β₁ + α₀β₄ + α₁β₀ + α₃β₁ + α₃β₄ + α₄β₀)x +\n    //      (α₁β₁ + α₁β₄ + α₂β₀ + α₄β₁ + α₄β₄ + α₅β₀)x², where q = x³\n    this->c1_.MulInPlaceBy01(beta0, beta1 + beta4);\n    // c1 = (α₂β₄q + α₃β₀ + α₅β₁q + α₅β₄q) +\n    //      (α₀β₄ + α₃β₁ + α₃β₄ + α₄β₀)x +\n    //      (α₁β₄ + α₄β₁ + α₄β₄ + α₅β₀)x², where q = x³\n    this->c1_ -= a;\n    // c1 = (α₂β₄q + α₃β₀ + α₅β₁q) +\n    //      (α₀β₄ + α₃β₁ + α₄β₀)x +\n    //      (α₁β₄ + α₄β₁ + α₅β₀)x², where q = x³\n    this->c1_ -= b;\n    // c0 = (α₅β₄q + α₃β₄x + α₄β₄x²)p +\n    //      (α₀β₀ + α₂β₁q) + (α₁β₀ + α₀β₁)x + (α₂β₀ + α₁β₁)x²,\n    //      where p = y² and q = x³\n    this->c0_ = Config::MulByNonResidue(b);\n    this->c0_ += a;\n    return *this;\n  }\n\n  static void DoFastCyclotomicSquare(const Fp12& a, Fp12& b) {\n    // Faster Squaring in the Cyclotomic Subgroup of Sixth Degree Extensions\n    // - Robert Granger and Michael Scott\n\n    if constexpr (BasePrimeField::Config::kModulusModSixIsOne) {\n      const Fp2& a0 = a.c0_.c0_;\n      const Fp2& a1 = a.c0_.c1_;\n      const Fp2& a2 = a.c0_.c2_;\n      const Fp2& a3 = a.c1_.c0_;\n      const Fp2& a4 = a.c1_.c1_;\n      const Fp2& a5 = a.c1_.c2_;\n\n      // a² = (α₀ + α₄x)² = α₀² + 2α₀α₄x + α₄²x²\n      //                  = α₀² + α₄²q + 2α₀α₄x (where q = x²)\n      //                  = t₀ + t₁x\n      Fp2 tmp = a0 * a4;\n      // t₀ = (α₀ + α₄) * (α₀ + α₄q) - α₀α₄ - α₀α₄x\n      //    = α₀² + α₄²q\n      Fp2 t0 = (a0 + a4) * (a0 + Fp6::Config::MulByNonResidue(a4)) - tmp -\n               Fp6::Config::MulByNonResidue(tmp);\n      // t₁ = 2α₀α₄\n      Fp2 t1 = tmp.Double();\n\n      // b² = (α₃ + α₂x)² = α₃² + 2α₂α₃x + α₂²x²\n      //                  = α₃² + α₂²q + 2α₂α₃x (where q = x²)\n      //                  = t₂ + t₃x\n      tmp = a3 * a2;\n      // t₂ = (α₃ + α₂) * (α₃ + α₂q) - α₂α₃ - α₂α₃x\n      //    = α₃² + α₂²q\n      Fp2 t2 = (a3 + a2) * (a3 + Fp6::Config::MulByNonResidue(a2)) - tmp -\n               Fp6::Config::MulByNonResidue(tmp);\n      // t₃ = 2α₂α₃\n      Fp2 t3 = tmp.Double();\n\n      // c² = (α₁ + α₅x)² = α₁² + 2α₁α₅x + α₅²x²\n      //                  = α₁² + α₅²q + 2α₁α₅x (where q = x²)\n      //                  = t₄ + t₅x\n      tmp = a1 * a5;\n      // t₄ = (α₁ + α₅) * (α₁ + α₅q) - α₁α₅ - α₁α₅x\n      //    = α₁² + α₅²q\n      Fp2 t4 = (a1 + a5) * (a1 + Fp6::Config::MulByNonResidue(a5)) - tmp -\n               Fp6::Config::MulByNonResidue(tmp);\n      // t₅ = 2α₁α₅\n      Fp2 t5 = tmp.Double();\n\n      Fp2& z0 = (&a == &b) ? b.c0_.c0_ : b.c0_.c0_ = a.c0_.c0_;\n      Fp2& z4 = (&a == &b) ? b.c0_.c1_ : b.c0_.c1_ = a.c0_.c1_;\n      Fp2& z3 = (&a == &b) ? b.c0_.c2_ : b.c0_.c2_ = a.c0_.c2_;\n      Fp2& z2 = (&a == &b) ? b.c1_.c0_ : b.c1_.c0_ = a.c1_.c0_;\n      Fp2& z1 = (&a == &b) ? b.c1_.c1_ : b.c1_.c1_ = a.c1_.c1_;\n      Fp2& z5 = (&a == &b) ? b.c1_.c2_ : b.c1_.c2_ = a.c1_.c2_;\n\n      // for A\n\n      // z₀ = 3 * t₀ - 2 * z₀\n      //    = 2 * (t₀ - z₀) + t₀\n      z0 = t0 - z0;\n      z0.DoubleInPlace();\n      z0 += t0;\n\n      // z₁ = 3 * t₁ + 2 * z₁\n      //    = 2 * (t₁ + z₁) + t₁\n      z1 = t1 + z1;\n      z1.DoubleInPlace();\n      z1 += t1;\n\n      // for B\n\n      // z₂ = 3 * (q * t₅) + 2 * z₂\n      //    = 2 * (z₂ + q * t₅) + q * t₅\n      tmp = Fp6::Config::MulByNonResidue(t5);\n      z2 += tmp;\n      z2.DoubleInPlace();\n      z2 += tmp;\n\n      // z₃ = 3 * t₄ - 2 * z₃\n      //    = 2 * (t₄ - z₃) + t₄\n      z3 = t4 - z3;\n      z3.DoubleInPlace();\n      z3 += t4;\n\n      // for C\n\n      // z₄ = 3 * t₂ - 2 * z₄\n      //    = 2 * (t₂ - z₄) + t₂\n      z4 = t2 - z4;\n      z4.DoubleInPlace();\n      z4 += t2;\n\n      // z₅ = 3 * t₃ + 2 * z₅\n      //    = 2 * (t₃ + z₅) + t₃\n      z5 += t3;\n      z5.DoubleInPlace();\n      z5 += t3;\n    } else {\n      if (&a == &b) {\n        b.SquareInPlace();\n      } else {\n        b = a.Square();\n      }\n    }\n  }\n\n private:\n  static void DoInit() {\n    using BaseFieldConfig = typename BaseField::Config;\n    // x⁶ = q = |BaseFieldConfig::kNonResidue|\n\n    Config::Init();\n\n    // αᴾ = (α₀ + α₁x)ᴾ\n    //    = α₀ᴾ + α₁ᴾxᴾ\n    //    = α₀ᴾ + α₁ᴾxᴾ⁻¹x\n    //    = α₀ᴾ + α₁ᴾ(x⁶)^((P - 1) / 6) * x\n    //    = α₀ᴾ + α₁ᴾωx, where ω is a sextic root of unity.\n\n    using UnpackedBasePrimeField = MaybeUnpack<BasePrimeField>;\n\n    constexpr size_t N = UnpackedBasePrimeField::kLimbNums;\n    // m₁ = P\n    mpz_class m1;\n    if constexpr (UnpackedBasePrimeField::Config::kModulusBits <= 32) {\n      m1 = mpz_class(UnpackedBasePrimeField::Config::kModulus);\n    } else {\n      gmp::WriteLimbs(UnpackedBasePrimeField::Config::kModulus.limbs, N, &m1);\n    }\n\n#define SET_M(d, d_prev) mpz_class m##d = m##d_prev * m1\n\n    // m₂ = m₁ * P = P²\n    SET_M(2, 1);\n    // m₃ = m₂ * P = P³\n    SET_M(3, 2);\n    // m₄ = m₃ * P = P⁴\n    SET_M(4, 3);\n    // m₅ = m₄ * P = P⁵\n    SET_M(5, 4);\n    // m₆ = m₅ * P = P⁶\n    SET_M(6, 5);\n    // m₇ = m₆ * P = P⁷\n    SET_M(7, 6);\n    // m₈ = m₇ * P = P⁸\n    SET_M(8, 7);\n    // m₉ = m₈ * P = P⁹\n    SET_M(9, 8);\n    // m₁₀ = m₉ * P = P¹⁰\n    SET_M(10, 9);\n    // m₁₁ = m₁₀ * P = P¹¹\n    SET_M(11, 10);\n\n#undef SET_M\n\n#define SET_EXP_GMP(d) mpz_class exp##d##_gmp = (m##d - 1) / mpz_class(6)\n\n    // exp₁ = (m₁ - 1) / 6 = (P¹ - 1) / 6\n    SET_EXP_GMP(1);\n    // exp₂ = (m₂ - 1) / 6 = (P² - 1) / 6\n    SET_EXP_GMP(2);\n    // exp₃ = (m₃ - 1) / 6 = (P³ - 1) / 6\n    SET_EXP_GMP(3);\n    // exp₄ = (m₄ - 1) / 6 = (P⁴ - 1) / 6\n    SET_EXP_GMP(4);\n    // exp₅ = (m₅ - 1) / 6 = (P⁵ - 1) / 6\n    SET_EXP_GMP(5);\n    // exp₆ = (m₅ - 1) / 6 = (P⁶ - 1) / 6\n    SET_EXP_GMP(6);\n    // exp₇ = (m₆ - 1) / 6 = (P⁷ - 1) / 6\n    SET_EXP_GMP(7);\n    // exp₈ = (m₇ - 1) / 6 = (P⁸ - 1) / 6\n    SET_EXP_GMP(8);\n    // exp₉ = (m₈ - 1) / 6 = (P⁹ - 1) / 6\n    SET_EXP_GMP(9);\n    // exp₁₀ = (m₉ - 1) / 6 = (P¹⁰ - 1) / 6\n    SET_EXP_GMP(10);\n    // exp₁₁ = (m₁₀ - 1) / 6 = (P¹¹ - 1) / 6\n    SET_EXP_GMP(11);\n\n#undef SET_EXP_GMP\n\n    // |kFrobeniusCoeffs[0]| = q^((P⁰ - 1) / 6)\n    Config::kFrobeniusCoeffs[0] = FrobeniusCoefficient::One();\n#define SET_FROBENIUS_COEFF(d)                \\\n  BigInt<d * N> exp##d(0);                    \\\n  gmp::CopyLimbs(exp##d##_gmp, exp##d.limbs); \\\n  Config::kFrobeniusCoeffs[d] = BaseFieldConfig::kNonResidue.Pow(exp##d)\n    // |kFrobeniusCoeffs[1]| = q^(exp₁) = q^((P¹ - 1) / 6)\n    SET_FROBENIUS_COEFF(1);\n    // |kFrobeniusCoeffs[2]| = q^(exp₂) = q^((P² - 1) / 6)\n    SET_FROBENIUS_COEFF(2);\n    // |kFrobeniusCoeffs[3]| = q^(exp₃) = q^((P³ - 1) / 6)\n    SET_FROBENIUS_COEFF(3);\n    // |kFrobeniusCoeffs[4]| = q^(exp₄) = q^((P⁴ - 1) / 6)\n    SET_FROBENIUS_COEFF(4);\n    // |kFrobeniusCoeffs[5]| = q^(exp₅) = q^((P⁵ - 1) / 6)\n    SET_FROBENIUS_COEFF(5);\n    // |kFrobeniusCoeffs[6]| = q^(exp₆) = q^((P⁶ - 1) / 6)\n    SET_FROBENIUS_COEFF(6);\n    // |kFrobeniusCoeffs[7]| = q^(exp₇) = q^((P⁷ - 1) / 6)\n    SET_FROBENIUS_COEFF(7);\n    // |kFrobeniusCoeffs[8]| = q^(exp₈) = q^((P⁸ - 1) / 6)\n    SET_FROBENIUS_COEFF(8);\n    // |kFrobeniusCoeffs[9]| = q^(exp₉) = q^((P⁹ - 1) / 6)\n    SET_FROBENIUS_COEFF(9);\n    // |kFrobeniusCoeffs[10]| = q^(exp₁₀) = q^((P¹⁰ - 1) / 6)\n    SET_FROBENIUS_COEFF(10);\n    // |kFrobeniusCoeffs[11]| = q^(exp₁₁) = q^((P¹¹ - 1) / 6)\n    SET_FROBENIUS_COEFF(11);\n\n#undef SET_FROBENIUS_COEFF\n  }\n};\n\ntemplate <typename Config>\nstruct ExtensionFieldTraits<Fp12<Config>> {\n  constexpr static uint32_t kDegreeOverBaseField = Config::kDegreeOverBaseField;\n  constexpr static uint32_t kDegreeOverBasePrimeField = 12;\n\n  using BaseField = typename Fp12<Config>::BaseField;\n  using BasePrimeField = typename Fp12<Config>::BasePrimeField;\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_FP12_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/fp12_unittest.cc",
    "content": "#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/buffer/vector_buffer.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/fq12.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\nclass Fp12Test : public FiniteFieldTest<bn254::Fq12> {};\n\n}  // namespace\n\nTEST_F(Fp12Test, TypeTest) {\n  EXPECT_TRUE((std::is_same_v<bn254::Fq12::BaseField, bn254::Fq6>));\n  EXPECT_TRUE((std::is_same_v<bn254::Fq12::BasePrimeField, bn254::Fq>));\n}\n\nTEST_F(Fp12Test, Copyable) {\n  using F = bn254::Fq12;\n\n  const F expected = F::Random();\n\n  base::Uint8VectorBuffer write_buf;\n  ASSERT_TRUE(write_buf.Grow(base::EstimateSize(expected)));\n  ASSERT_TRUE(write_buf.Write(expected));\n  ASSERT_TRUE(write_buf.Done());\n\n  write_buf.set_buffer_offset(0);\n\n  F value;\n  ASSERT_TRUE(write_buf.Read(&value));\n  EXPECT_EQ(expected, value);\n}\n\nTEST_F(Fp12Test, JsonValueConverter) {\n  using F = bn254::Fq12;\n\n  F expected = F::Random();\n  std::string json = base::WriteToJson(expected);\n\n  F value;\n  std::string error;\n  ASSERT_TRUE(base::ParseJson(json, &value, &error));\n  ASSERT_TRUE(error.empty());\n  EXPECT_EQ(value, expected);\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/finite_fields/fp2.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_FINITE_FIELDS_FP2_H_\n#define TACHYON_MATH_FINITE_FIELDS_FP2_H_\n\n#include \"absl/base/call_once.h\"\n\n#include \"tachyon/math/finite_fields/extension_field_traits_forward.h\"\n#include \"tachyon/math/finite_fields/quadratic_extension_field.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename Config>\nclass Fp2 final : public QuadraticExtensionField<Fp2<Config>> {\n public:\n  using BaseField = typename Config::BaseField;\n  using BasePrimeField = typename Config::BasePrimeField;\n  using FrobeniusCoefficient = typename Config::FrobeniusCoefficient;\n\n  using CpuField = Fp2<Config>;\n  // TODO(chokobole): Implement Fp2Gpu\n  using GpuField = Fp2<Config>;\n\n  using QuadraticExtensionField<Fp2<Config>>::QuadraticExtensionField;\n\n  static_assert(Config::kDegreeOverBaseField == 2);\n  static_assert(BaseField::ExtensionDegree() == 1);\n\n  constexpr static uint32_t kDegreeOverBasePrimeField = 2;\n\n  static void Init() {\n    static absl::once_flag once;\n    absl::call_once(once, &Fp2::DoInit);\n  }\n\n private:\n  static void DoInit() {\n    Config::Init();\n\n    // αᴾ = (α₀ + α₁x)ᴾ\n    //    = α₀ᴾ + α₁ᴾxᴾ\n    //    = α₀ + α₁xᴾ <- Fermat's little theorem\n    //    = α₀ + α₁xᴾ⁻¹x\n    //    = α₀ + α₁(x²)^((P - 1) / 2) * x\n    //    = α₀ - α₁x <- Euler's Criterion\n\n    Config::kFrobeniusCoeffs[0] = FrobeniusCoefficient::One();\n    Config::kFrobeniusCoeffs[1] = -FrobeniusCoefficient::One();\n  }\n};\n\ntemplate <typename Config>\nstruct ExtensionFieldTraits<Fp2<Config>> {\n  constexpr static uint32_t kDegreeOverBaseField = 2;\n  constexpr static uint32_t kDegreeOverBasePrimeField = 2;\n\n  using BaseField = typename Fp2<Config>::BaseField;\n  using BasePrimeField = typename Fp2<Config>::BasePrimeField;\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_FP2_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/fp2_unittest.cc",
    "content": "#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/buffer/vector_buffer.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/fq2.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\nclass Fp2Test : public FiniteFieldTest<bn254::Fq2> {};\n\n}  // namespace\n\nTEST_F(Fp2Test, TypeTest) {\n  EXPECT_TRUE((std::is_same_v<bn254::Fq2::BaseField, bn254::Fq>));\n  EXPECT_TRUE((std::is_same_v<bn254::Fq2::BasePrimeField, bn254::Fq>));\n}\n\nTEST_F(Fp2Test, Copyable) {\n  using F = bn254::Fq2;\n\n  const F expected = F::Random();\n\n  base::Uint8VectorBuffer write_buf;\n  ASSERT_TRUE(write_buf.Grow(base::EstimateSize(expected)));\n  ASSERT_TRUE(write_buf.Write(expected));\n  ASSERT_TRUE(write_buf.Done());\n\n  write_buf.set_buffer_offset(0);\n\n  F value;\n  ASSERT_TRUE(write_buf.Read(&value));\n  EXPECT_EQ(expected, value);\n}\n\nTEST_F(Fp2Test, JsonValueConverter) {\n  using F = bn254::Fq2;\n\n  F expected = F::Random();\n  std::string json = base::WriteToJson(expected);\n\n  F value;\n  std::string error;\n  ASSERT_TRUE(base::ParseJson(json, &value, &error));\n  ASSERT_TRUE(error.empty());\n  EXPECT_EQ(value, expected);\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/finite_fields/fp3.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_FINITE_FIELDS_FP3_H_\n#define TACHYON_MATH_FINITE_FIELDS_FP3_H_\n\n#include <type_traits>\n\n#include \"absl/base/call_once.h\"\n\n#include \"tachyon/math/base/gmp/gmp_util.h\"\n#include \"tachyon/math/finite_fields/cubic_extension_field.h\"\n#include \"tachyon/math/finite_fields/extension_field_traits_forward.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename Config>\nclass Fp3 final : public CubicExtensionField<Fp3<Config>> {\n public:\n  using BaseField = typename Config::BaseField;\n  using BasePrimeField = typename Config::BasePrimeField;\n  using FrobeniusCoefficient = typename Config::FrobeniusCoefficient;\n\n  using CpuField = Fp3<Config>;\n  // TODO(chokobole): Implement Fp3Gpu\n  using GpuField = Fp3<Config>;\n\n  using CubicExtensionField<Fp3<Config>>::CubicExtensionField;\n\n  static_assert(Config::kDegreeOverBaseField == 3);\n  static_assert(BaseField::ExtensionDegree() == 1);\n\n  constexpr static uint32_t kDegreeOverBasePrimeField = 3;\n\n  static void Init() {\n    static absl::once_flag once;\n    absl::call_once(once, &Fp3::DoInit);\n  }\n\n private:\n  static void DoInit() {\n    Config::Init();\n    // x³ = q = |Config::kNonResidue|\n\n    // αᴾ = (α₀ + α₁x + α₂x²)ᴾ\n    //    = α₀ᴾ + α₁ᴾxᴾ + α₂ᴾx²ᴾ\n    //    = α₀ + α₁xᴾ + α₂x²ᴾ <- Fermat's little theorem\n    //    = α₀ + α₁xᴾ⁻¹x + α₂x²ᴾ⁻²x²\n    //    = α₀ + α₁(x³)^((P - 1) / 3) * x + α₂(x³)^(2 * (P - 1) / 3) * x²\n    //    = α₀ + α₁ωx + α₂ω²x², where ω is a cubic root of unity.\n\n    using UnpackedBasePrimeField = MaybeUnpack<BasePrimeField>;\n\n    constexpr size_t N = UnpackedBasePrimeField::kLimbNums;\n    // m₁ = P\n    mpz_class m1;\n    if constexpr (UnpackedBasePrimeField::Config::kModulusBits <= 32) {\n      m1 = mpz_class(UnpackedBasePrimeField::Config::kModulus);\n    } else {\n      gmp::WriteLimbs(UnpackedBasePrimeField::Config::kModulus.limbs, N, &m1);\n    }\n\n#define SET_M(d, d_prev) mpz_class m##d = m##d_prev * m1\n\n    // m₂ = m₁ * P = P²\n    SET_M(2, 1);\n\n#undef SET_M\n\n#define SET_EXP_GMP(d) mpz_class exp##d##_gmp = (m##d - 1) / mpz_class(3)\n\n    // exp₁ = (m₁ - 1) / 3 = (P¹ - 1) / 3\n    SET_EXP_GMP(1);\n    // exp₂ = (m₂ - 1) / 3 = (P² - 1) / 3\n    SET_EXP_GMP(2);\n\n#undef SET_EXP_GMP\n\n    // |kFrobeniusCoeffs[0]| = q^((P⁰ - 1) / 3) = 1\n    Config::kFrobeniusCoeffs[0] = FrobeniusCoefficient::One();\n#define SET_FROBENIUS_COEFF(d)                \\\n  BigInt<d * N> exp##d(0);                    \\\n  gmp::CopyLimbs(exp##d##_gmp, exp##d.limbs); \\\n  Config::kFrobeniusCoeffs[d] = Config::kNonResidue.Pow(exp##d)\n\n    // |kFrobeniusCoeffs[1]| = q^(exp₁) = q^((P¹ - 1) / 3) = ω\n    SET_FROBENIUS_COEFF(1);\n    // |kFrobeniusCoeffs[2]| = q^(exp₂) = q^((P² - 1) / 3)\n    SET_FROBENIUS_COEFF(2);\n\n#undef SET_FROBENIUS_COEFF\n\n    // kFrobeniusCoeffs2[0] = q^(2 * (P⁰ - 1) / 3) = 1\n    Config::kFrobeniusCoeffs2[0] = FrobeniusCoefficient::One();\n#define SET_FROBENIUS_COEFF2(d)                              \\\n  gmp::CopyLimbs(mpz_class(2) * exp##d##_gmp, exp##d.limbs); \\\n  Config::kFrobeniusCoeffs2[d] = Config::kNonResidue.Pow(exp##d)\n\n    // kFrobeniusCoeffs2[1] = q^(2 * exp₁) = q^(2 * (P¹ - 1) / 3) = ω²\n    SET_FROBENIUS_COEFF2(1);\n    // kFrobeniusCoeffs2[2] = q^(2 * exp₂) = q^(2 * (P² - 1) / 3)\n    SET_FROBENIUS_COEFF2(2);\n\n#undef SET_FROBENIUS_COEFF2\n  }\n};\n\ntemplate <typename Config>\nstruct ExtensionFieldTraits<Fp3<Config>> {\n  constexpr static uint32_t kDegreeOverBaseField = 3;\n  constexpr static uint32_t kDegreeOverBasePrimeField = 3;\n\n  using BaseField = typename Fp3<Config>::BaseField;\n  using BasePrimeField = typename Fp3<Config>::BasePrimeField;\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_FP3_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/fp4.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_FINITE_FIELDS_FP4_H_\n#define TACHYON_MATH_FINITE_FIELDS_FP4_H_\n\n#include <type_traits>\n\n#include \"absl/base/call_once.h\"\n\n#include \"tachyon/math/base/gmp/gmp_util.h\"\n#include \"tachyon/math/finite_fields/extension_field_traits_forward.h\"\n#include \"tachyon/math/finite_fields/quadratic_extension_field.h\"\n#include \"tachyon/math/finite_fields/quartic_extension_field.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename Config>\nclass Fp4<Config, std::enable_if_t<Config::kDegreeOverBaseField == 2>> final\n    : public QuadraticExtensionField<Fp4<Config>> {\n public:\n  using BaseField = typename Config::BaseField;\n  using BasePrimeField = typename Config::BasePrimeField;\n  using FrobeniusCoefficient = typename Config::FrobeniusCoefficient;\n\n  using CpuField = Fp4<Config>;\n  // TODO(chokobole): Implement Fp4Gpu\n  using GpuField = Fp4<Config>;\n\n  using QuadraticExtensionField<Fp4<Config>>::QuadraticExtensionField;\n\n  static_assert(Config::kDegreeOverBaseField == 2);\n  static_assert(BaseField::ExtensionDegree() == 2);\n\n  constexpr static uint32_t kDegreeOverBasePrimeField = 4;\n\n  static void Init() {\n    static absl::once_flag once;\n    absl::call_once(once, &Fp4::DoInit);\n  }\n\n private:\n  static void DoInit() {\n    using BaseFieldConfig = typename BaseField::Config;\n    // x⁴ = q = |BaseFieldConfig::kNonResidue|\n\n    Config::Init();\n\n    // αᴾ = (α₀ + α₁x)ᴾ\n    //    = α₀ᴾ + α₁ᴾxᴾ\n    //    = ᾱ₀ + ᾱ₁xᴾ <- conjugate\n    //    = ᾱ₀ + ᾱ₁xᴾ⁻¹x\n    //    = ᾱ₀ + ᾱ₁(x⁴)^((p - 1) / 4) * x\n    //    = ᾱ₀ - ᾱ₁ωx, where ω is a quartic root of unity.\n\n    using UnpackedBasePrimeField = MaybeUnpack<BasePrimeField>;\n\n    constexpr size_t N = UnpackedBasePrimeField::kLimbNums;\n    // m₁ = P\n    mpz_class m1;\n    if constexpr (UnpackedBasePrimeField::Config::kModulusBits <= 32) {\n      m1 = mpz_class(UnpackedBasePrimeField::Config::kModulus);\n    } else {\n      gmp::WriteLimbs(UnpackedBasePrimeField::Config::kModulus.limbs, N, &m1);\n    }\n\n#define SET_M(d, d_prev) mpz_class m##d = m##d_prev * m1\n\n    // m₂ = m₁ * P = P²\n    SET_M(2, 1);\n    // m₃ = m₂ * P = P³\n    SET_M(3, 2);\n\n#undef SET_M\n\n#define SET_EXP_GMP(d) mpz_class exp##d##_gmp = (m##d - 1) / mpz_class(4)\n\n    // exp₁ = (m₁ - 1) / 4 = (P¹ - 1) / 4\n    SET_EXP_GMP(1);\n    // exp₂ = (m₂ - 1) / 4 = (P² - 1) / 4\n    SET_EXP_GMP(2);\n    // exp₃ = (m₃ - 1) / 4 = (P³ - 1) / 4\n    SET_EXP_GMP(3);\n\n#undef SET_EXP_GMP\n\n    // |kFrobeniusCoeffs[0]| = q^((P⁰ - 1) / 4) = 1\n    Config::kFrobeniusCoeffs[0] = FrobeniusCoefficient::One();\n#define SET_FROBENIUS_COEFF(d)                \\\n  BigInt<d * N> exp##d(0);                    \\\n  gmp::CopyLimbs(exp##d##_gmp, exp##d.limbs); \\\n  Config::kFrobeniusCoeffs[d] = BaseFieldConfig::kNonResidue.Pow(exp##d)\n\n    // |kFrobeniusCoeffs[1]| = q^(exp₁) = q^((P¹ - 1) / 4) = ω\n    SET_FROBENIUS_COEFF(1);\n    // |kFrobeniusCoeffs[2]| = q^(exp₂) = q^((P² - 1) / 4)\n    SET_FROBENIUS_COEFF(2);\n    // |kFrobeniusCoeffs[3]| = q^(exp₃) = q^((P³ - 1) / 4)\n    SET_FROBENIUS_COEFF(3);\n\n#undef SET_FROBENIUS_COEFF\n  }\n};\n\ntemplate <typename Config>\nclass Fp4<Config, std::enable_if_t<Config::kDegreeOverBaseField == 4>> final\n    : public QuarticExtensionField<Fp4<Config>> {\n public:\n  using BaseField = typename Config::BaseField;\n  using BasePrimeField = typename Config::BasePrimeField;\n  using FrobeniusCoefficient = typename Config::FrobeniusCoefficient;\n\n  using CpuField = Fp4<Config>;\n  // TODO(chokobole): Implement Fp4Gpu\n  using GpuField = Fp4<Config>;\n\n  using QuarticExtensionField<Fp4<Config>>::QuarticExtensionField;\n\n  static_assert(Config::kDegreeOverBaseField == 4);\n  static_assert(BaseField::ExtensionDegree() == 1);\n\n  constexpr static uint32_t kDegreeOverBasePrimeField = 4;\n\n  static void Init() {\n    static absl::once_flag once;\n    absl::call_once(once, &Fp4::DoInit);\n  }\n\n private:\n  static void DoInit() {\n    Config::Init();\n    QuarticExtensionField<Fp4<Config>>::Init();\n    // x⁴ = q = |Config::kNonResidue|\n\n    // αᴾ = (α₀ + α₁x + α₂x² + α₃x³)ᴾ\n    //    = α₀ᴾ + α₁ᴾxᴾ + α₂ᴾx²ᴾ + α₃ᴾx³ᴾ\n    //    = α₀ + α₁xᴾ + α₂x²ᴾ + α₃x³ᴾ <- Fermat's little theorem\n    //    = α₀ + α₁xᴾ⁻¹x + α₂x²ᴾ⁻²x² + α₃x³ᴾ⁻³x³\n    //    = α₀ + α₁(x⁴)^((P - 1) / 4) * x + α₂(x⁴)^(2 * (P - 1) / 4) * x² +\n    //      α₃(x⁴)^(3 * (P - 1) / 4) * x³\n    //    = α₀ + α₁ωx + α₂ω²x² + α₃ω³x³, where ω is a quartic root of unity.\n\n    using UnpackedBasePrimeField = MaybeUnpack<BasePrimeField>;\n\n    constexpr size_t N = UnpackedBasePrimeField::kLimbNums;\n    // m₁ = P\n    mpz_class m1;\n    if constexpr (UnpackedBasePrimeField::Config::kModulusBits <= 32) {\n      m1 = mpz_class(UnpackedBasePrimeField::Config::kModulus);\n    } else {\n      gmp::WriteLimbs(UnpackedBasePrimeField::Config::kModulus.limbs, N, &m1);\n    }\n\n#define SET_M(d, d_prev) mpz_class m##d = m##d_prev * m1\n\n    // m₂ = m₁ * P = P²\n    SET_M(2, 1);\n    // m₃ = m₂ * P = P³\n    SET_M(3, 2);\n\n#undef SET_M\n\n#define SET_EXP_GMP(d) mpz_class exp##d##_gmp = (m##d - 1) / mpz_class(4)\n\n    // exp₁ = (m₁ - 1) / 4 = (P¹ - 1) / 4\n    SET_EXP_GMP(1);\n    // exp₂ = (m₂ - 1) / 4 = (P² - 1) / 4\n    SET_EXP_GMP(2);\n    // exp₃ = (m₃ - 1) / 4 = (P³ - 1) / 4\n    SET_EXP_GMP(3);\n\n#undef SET_EXP_GMP\n\n    // |kFrobeniusCoeffs[0]| = q^((P⁰ - 1) / 4) = 1\n    Config::kFrobeniusCoeffs[0] = FrobeniusCoefficient::One();\n#define SET_FROBENIUS_COEFF(d)                \\\n  BigInt<d * N> exp##d(0);                    \\\n  gmp::CopyLimbs(exp##d##_gmp, exp##d.limbs); \\\n  Config::kFrobeniusCoeffs[d] = Config::kNonResidue.Pow(exp##d)\n\n    // |kFrobeniusCoeffs[1]| = q^(exp₁) = q^((P¹ - 1) / 4) = ω\n    SET_FROBENIUS_COEFF(1);\n    // |kFrobeniusCoeffs[2]| = q^(exp₂) = q^((P² - 1) / 4)\n    SET_FROBENIUS_COEFF(2);\n    // |kFrobeniusCoeffs[3]| = q^(exp₃) = q^((P³ - 1) / 4)\n    SET_FROBENIUS_COEFF(3);\n\n#undef SET_FROBENIUS_COEFF\n\n    // |kFrobeniusCoeffs2[0]| = q^(2 * (P⁰ - 1) / 4) = 1\n    Config::kFrobeniusCoeffs2[0] = FrobeniusCoefficient::One();\n#define SET_FROBENIUS_COEFF2(d)                              \\\n  gmp::CopyLimbs(mpz_class(2) * exp##d##_gmp, exp##d.limbs); \\\n  Config::kFrobeniusCoeffs2[d] = Config::kNonResidue.Pow(exp##d)\n\n    // |kFrobeniusCoeffs2[1]| = q^(2 * exp₁) = q^(2 * (P¹ - 1) / 4) = ω²\n    SET_FROBENIUS_COEFF2(1);\n    // |kFrobeniusCoeffs2[2]| = q^(2 * exp₂) = q^(2 * (P² - 1) / 4)\n    SET_FROBENIUS_COEFF2(2);\n    // |kFrobeniusCoeffs2[3]| = q^(2 * exp₃) = q^(2 * (P³ - 1) / 4)\n    SET_FROBENIUS_COEFF2(3);\n\n#undef SET_FROBENIUS_COEFF2\n\n    // |kFrobeniusCoeffs3[0]| = q^(3 * (P⁰ - 1) / 4) = 1\n    Config::kFrobeniusCoeffs3[0] = FrobeniusCoefficient::One();\n#define SET_FROBENIUS_COEFF3(d)                              \\\n  gmp::CopyLimbs(mpz_class(3) * exp##d##_gmp, exp##d.limbs); \\\n  Config::kFrobeniusCoeffs3[d] = Config::kNonResidue.Pow(exp##d)\n\n    // |kFrobeniusCoeffs3[1]| = q^(3 * exp₁) = q^(3 * (P¹ - 1) / 4) = ω³\n    SET_FROBENIUS_COEFF3(1);\n    // |kFrobeniusCoeffs3[2]| = q^(3 * exp₂) = q^(3 * (P² - 1) / 4)\n    SET_FROBENIUS_COEFF3(2);\n    // |kFrobeniusCoeffs3[3]| = q^(3 * exp₃) = q^(3 * (P³ - 1) / 4)\n    SET_FROBENIUS_COEFF3(3);\n\n#undef SET_FROBENIUS_COEFF3\n  }\n};\n\ntemplate <typename Config>\nstruct ExtensionFieldTraits<Fp4<Config>> {\n  constexpr static uint32_t kDegreeOverBaseField = Config::kDegreeOverBaseField;\n  constexpr static uint32_t kDegreeOverBasePrimeField = 4;\n\n  using BaseField = typename Fp4<Config>::BaseField;\n  using BasePrimeField = typename Fp4<Config>::BasePrimeField;\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_FP4_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/fp6.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_FINITE_FIELDS_FP6_H_\n#define TACHYON_MATH_FINITE_FIELDS_FP6_H_\n\n#include <type_traits>\n#include <utility>\n\n#include \"absl/base/call_once.h\"\n\n#include \"tachyon/math/base/gmp/gmp_util.h\"\n#include \"tachyon/math/finite_fields/cubic_extension_field.h\"\n#include \"tachyon/math/finite_fields/extension_field_traits_forward.h\"\n#include \"tachyon/math/finite_fields/quadratic_extension_field.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename Config>\nclass Fp6<Config, std::enable_if_t<Config::kDegreeOverBaseField == 2>> final\n    : public QuadraticExtensionField<Fp6<Config>> {\n public:\n  using BaseField = typename Config::BaseField;\n  using BasePrimeField = typename Config::BasePrimeField;\n  using FrobeniusCoefficient = typename Config::FrobeniusCoefficient;\n\n  using Fp = BasePrimeField;\n  using Fp3 = BaseField;\n\n  using CpuField = Fp6<Config>;\n  // TODO(chokobole): Implement Fp6Gpu\n  using GpuField = Fp6<Config>;\n\n  using QuadraticExtensionField<Fp6<Config>>::QuadraticExtensionField;\n\n  static_assert(BaseField::ExtensionDegree() == 3);\n\n  constexpr static uint32_t kDegreeOverBasePrimeField = 6;\n\n  static void Init() {\n    static absl::once_flag once;\n    absl::call_once(once, &Fp6::DoInit);\n  }\n\n  // Return α = {α₀', α₁', α₂', α₃', α₄', α₅'}, such that\n  // α = (α₀ + α₁x + α₂x² + (α₃ + α₄x + α₅x²)y) * (β₀ + β₃y + β₄xy)\n  Fp6& MulInPlaceBy034(const Fp& beta0, const Fp& beta3, const Fp& beta4) {\n    // α = (α₀ + α₁x + α₂x² + (α₃ + α₄x + α₅x²)y) * (β₀ + β₃y + β₄xy)\n    //   = (α₀β₀ + α₄β₄q + α₅β₃q) + <- I am not clear here\n    //     (α₁β₀ + α₃β₃ + α₅β₄q)x + <- I am not clear here\n    //     (α₂β₀ + α₃β₄ + α₄β₃)x² + <- I am not clear here\n    //     (α₀β₃ + α₂β₄q + α₃β₀)y +\n    //     (α₀β₄ + α₁β₃ + α₄β₀)xy +\n    //     (α₁β₄ + α₂β₃ + α₅β₀)x²y, where q is a cubic non residue.\n    // NOTE(chokobole): This equation above works when assuming y² = x holds.\n\n    // z0 = α₀\n    Fp z0 = this->c0_.c0_;\n    // z1 = α₁\n    Fp z1 = this->c0_.c1_;\n    // z2 = α₂\n    Fp z2 = this->c0_.c2_;\n    // z3 = α₃\n    Fp z3 = this->c1_.c0_;\n    // z4 = α₄\n    Fp z4 = this->c1_.c1_;\n    // z5 = α₅\n    Fp z5 = this->c1_.c2_;\n\n    // x0 = β₀\n    Fp x0 = beta0;\n    // x3 = β₃\n    Fp x3 = beta3;\n    // x4 = β₄\n    Fp x4 = beta4;\n\n    // tmp1 = β₃q\n    Fp tmp1 = Fp3::Config::MulByNonResidue(x3);\n    // tmp2 = β₄q\n    Fp tmp2 = Fp3::Config::MulByNonResidue(x4);\n\n    // α₀' = α₀β₀ + α₄β₄q + α₅β₃q\n    this->c0_.c0_ = (z0 * x0) + (z4 * tmp2) + (z5 * tmp1);\n    // α₁' = α₁β₀ + α₃β₃ + α₅β₄q\n    this->c0_.c1_ = (z1 * x0) + (z3 * x3) + (z5 * tmp2);\n    // α₂' = α₂β₀ + α₃β₄ + α₄β₃\n    this->c0_.c2_ = (z2 * x0) + (z3 * x4) + (z4 * x3);\n    // α₃' = α₀β₃ + α₂β₄q + α₃β₀\n    this->c1_.c0_ = (z0 * x3) + (z2 * tmp2) + (z3 * x0);\n    // α₄' = α₀β₄ + α₁β₃ + α₄β₀\n    this->c1_.c1_ = (z0 * x4) + (z1 * x3) + (z4 * x0);\n    // α₅' = α₁β₄ + α₂β₃ + α₅β₀\n    this->c1_.c2_ = (z1 * x4) + (z2 * x3) + (z5 * x0);\n    return *this;\n  }\n\n  // Return α = {α₀', α₁', α₂', α₃', α₄', α₅'}, such that\n  // α = (α₀ + α₁x + α₂x² + (α₃ + α₄x + α₅x²)y) * (β₀ + β₁x + β₄xy)\n  Fp6& MulInPlaceBy014(const Fp& beta0, const Fp& beta1, const Fp& beta4) {\n    // α = (α₀ + α₁x + α₂x² + (α₃ + α₄x + α₅x²)y) * (β₀ + β₁x + β₄xy)\n    //   = (α₀β₀ + α₂β₁q + α₄β₄q) + <- I am not clear here\n    //     (α₀β₁ + α₁β₀ + α₅β₄q)x + <- I am not clear here\n    //     (α₁β₁ + α₂β₀ + α₃β₄)x² + <- I am not clear here\n    //     (α₂β₄q + α₃β₀ + α₅β₁q)y +\n    //     (α₀β₄ + α₃β₁ + α₄β₀)xy +\n    //     (α₁β₄ + α₄β₁ + α₅β₀)x²y, where q is a cubic non residue.\n    // NOTE(chokobole): This equation above works when assuming y² = x holds.\n\n    // z0 = α₀\n    Fp z0 = this->c0_.c0_;\n    // z1 = α₁\n    Fp z1 = this->c0_.c1_;\n    // z2 = α₂\n    Fp z2 = this->c0_.c2_;\n    // z3 = α₃\n    Fp z3 = this->c1_.c0_;\n    // z4 = α₄\n    Fp z4 = this->c1_.c1_;\n    // z5 = α₅\n    Fp z5 = this->c1_.c2_;\n\n    // x0 = β₀\n    Fp x0 = beta0;\n    // x1 = β₁\n    Fp x1 = beta1;\n    // x4 = β₄\n    Fp x4 = beta4;\n\n    // tmp1 = β₁q\n    Fp tmp1 = Fp3::Config::MulByNonResidue(x1);\n    // tmp2 = β₄q\n    Fp tmp2 = Fp3::Config::MulByNonResidue(x4);\n\n    // α₀' = α₀β₀ + α₂β₁q + α₄β₄q\n    this->c0_.c0_ = (z0 * x0) + (z2 * tmp1) + (z4 * tmp2);\n    // α₁' = α₀β₁ + α₁β₀ + α₅β₄q\n    this->c0_.c1_ = (z0 * x1) + (z1 * x0) + (z5 * tmp2);\n    // α₂' = α₁β₁ + α₂β₀ + α₃β₄\n    this->c0_.c2_ = (z1 * x1) + (z2 * x0) + (z3 * x4);\n    // α₃' = α₂β₄q + α₃β₀ + α₅β₁q\n    this->c1_.c0_ = (z2 * tmp2) + (z3 * x0) + (z5 * tmp1);\n    // α₄' = α₀β₄ + α₃β₁ + α₄β₀\n    this->c1_.c1_ = (z0 * x4) + (z3 * x1) + (z4 * x0);\n    // α₅' = α₁β₄ + α₄β₁ + α₅β₀\n    this->c1_.c2_ = (z1 * x4) + (z4 * x1) + (z5 * x0);\n    return *this;\n  }\n\n private:\n  static void DoInit() {\n    using BaseFieldConfig = typename BaseField::Config;\n    // x⁶ = q = |BaseFieldConfig::kNonResidue|\n\n    Config::Init();\n\n    // αᴾ = (α₀ + α₁x)ᴾ\n    //    = α₀ᴾ + α₁ᴾxᴾ\n    //    = α₀ᴾ + α₁ᴾxᴾ⁻¹x\n    //    = α₀ᴾ + α₁ᴾ(x⁶)^((P - 1) / 6) * x\n    //    = α₀ᴾ + α₁ᴾωx, where ω is a sextic root of unity.\n\n    using UnpackedBasePrimeField = MaybeUnpack<BasePrimeField>;\n\n    constexpr size_t N = UnpackedBasePrimeField::kLimbNums;\n    // m₁ = P\n    mpz_class m1;\n    if constexpr (UnpackedBasePrimeField::Config::kModulusBits <= 32) {\n      m1 = mpz_class(UnpackedBasePrimeField::Config::kModulus);\n    } else {\n      gmp::WriteLimbs(UnpackedBasePrimeField::Config::kModulus.limbs, N, &m1);\n    }\n\n#define SET_M(d, d_prev) mpz_class m##d = m##d_prev * m1\n\n    // m₂ = m₁ * P = P²\n    SET_M(2, 1);\n    // m₃ = m₂ * P = P³\n    SET_M(3, 2);\n    // m₄ = m₃ * P = P⁴\n    SET_M(4, 3);\n    // m₅ = m₄ * P = P⁵\n    SET_M(5, 4);\n\n#undef SET_M\n\n#define SET_EXP_GMP(d) mpz_class exp##d##_gmp = (m##d - 1) / mpz_class(6)\n\n    // exp₁ = (m₁ - 1) / 6 = (P¹ - 1) / 6\n    SET_EXP_GMP(1);\n    // exp₂ = (m₂ - 1) / 6 = (P² - 1) / 6\n    SET_EXP_GMP(2);\n    // exp₃ = (m₃ - 1) / 6 = (P³ - 1) / 6\n    SET_EXP_GMP(3);\n    // exp₄ = (m₄ - 1) / 6 = (P⁴ - 1) / 6\n    SET_EXP_GMP(4);\n    // exp₅ = (m₅ - 1) / 6 = (P⁵ - 1) / 6\n    SET_EXP_GMP(5);\n\n#undef SET_EXP_GMP\n\n    // |kFrobeniusCoeffs[0]| = q^((P⁰ - 1) / 6) = 1\n    Config::kFrobeniusCoeffs[0] = FrobeniusCoefficient::One();\n#define SET_FROBENIUS_COEFF(d)                \\\n  BigInt<d * N> exp##d(0);                    \\\n  gmp::CopyLimbs(exp##d##_gmp, exp##d.limbs); \\\n  Config::kFrobeniusCoeffs[d] = BaseFieldConfig::kNonResidue.Pow(exp##d)\n\n    // |kFrobeniusCoeffs[1]| = q^(exp₁) = q^((P¹ - 1) / 6) = ω\n    SET_FROBENIUS_COEFF(1);\n    // |kFrobeniusCoeffs[2]| = q^(exp₂) = q^((P² - 1) / 6)\n    SET_FROBENIUS_COEFF(2);\n    // |kFrobeniusCoeffs[3]| = q^(exp₃) = q^((P³ - 1) / 6)\n    SET_FROBENIUS_COEFF(3);\n    // |kFrobeniusCoeffs[4]| = q^(exp₄) = q^((P⁴ - 1) / 6)\n    SET_FROBENIUS_COEFF(4);\n    // |kFrobeniusCoeffs[5]| = q^(exp₅) = q^((P⁵ - 1) / 6)\n    SET_FROBENIUS_COEFF(5);\n\n#undef SET_FROBENIUS_COEFF\n  }\n};\n\ntemplate <typename Config>\nclass Fp6<Config, std::enable_if_t<Config::kDegreeOverBaseField == 3>> final\n    : public CubicExtensionField<Fp6<Config>> {\n public:\n  using BaseField = typename Config::BaseField;\n  using BasePrimeField = typename Config::BasePrimeField;\n  using FrobeniusCoefficient = typename Config::FrobeniusCoefficient;\n\n  using Fp2 = BaseField;\n\n  using CpuField = Fp6<Config>;\n  // TODO(chokobole): Implement Fp6Gpu\n  using GpuField = Fp6<Config>;\n\n  using CubicExtensionField<Fp6<Config>>::CubicExtensionField;\n\n  static_assert(BaseField::ExtensionDegree() == 2);\n\n  constexpr static uint32_t kDegreeOverBasePrimeField = 6;\n\n  static void Init() {\n    static absl::once_flag once;\n    absl::call_once(once, &Fp6::DoInit);\n  }\n\n  // Return α = {α₀', α₁', α₂'}, such that α = (α₀ + α₁x + α₂x²) * β₁x\n  Fp6& MulInPlaceBy1(const Fp2& beta1) {\n    // α = (α₀ + α₁x + α₂x²) * β₁x\n    //   = α₂β₁q + α₀β₁x + α₁β₁x², where q is a cubic non residue.\n\n    // t0 = α₂β₁\n    Fp2 t0 = this->c2_ * beta1;\n\n    // c2 = α₁β₁\n    this->c2_ = this->c1_ * beta1;\n    // c1 = α₀β₁\n    this->c1_ = this->c0_ * beta1;\n    // c0 = α₂β₁q\n    this->c0_ = Config::MulByNonResidue(t0);\n    return *this;\n  }\n\n  // Return α = {α₀', α₁', α₂'}, such that α = (α₀ + α₁x + α₂x²) * (β₀ + β₁x)\n  Fp6& MulInPlaceBy01(const Fp2& beta0, const Fp2& beta1) {\n    // α = (α₀ + α₁x + α₂x²) * (β₀ + β₁x)\n    //   = α₀β₀ + α₂β₁q + (α₀β₁ + α₁β₀)x + (α₂β₀ + α₁β₁)x²,\n    //     where q is a cubic non residue.\n\n    // The naive approach you need to multiply 6 times, but this code is\n    // optimized to multiply 5 times.\n\n    // t0 = α₂β₁\n    Fp2 t0 = this->c2_ * beta1;\n    // t0 = α₂β₁q\n    t0 = Config::MulByNonResidue(t0);\n    {\n      // tmp = α₀β₀\n      Fp2 tmp = this->c0_ * beta0;\n      // t0 = α₀β₀ + α₂β₁q\n      t0 += tmp;\n    }\n\n    // t1 = α₀β₁\n    Fp2 t1 = this->c0_ * beta1;\n    {\n      // tmp = α₁β₀\n      Fp2 tmp = this->c1_ * beta0;\n      // t1 = α₀β₁ + α₁β₀\n      t1 += tmp;\n    }\n\n    // t2 = α₂β₀\n    Fp2 t2 = this->c2_ * beta0;\n    {\n      // tmp = α₁β₁\n      Fp2 tmp = this->c1_ * beta1;\n      // t2 = α₂β₀ + α₁β₁\n      t2 += tmp;\n    }\n\n    // c0 = α₀β₀ + α₂β₁q\n    this->c0_ = std::move(t0);\n    // c1 = α₀β₁ + α₁β₀\n    this->c1_ = std::move(t1);\n    // c2 = α₂β₀ + α₁β₁\n    this->c2_ = std::move(t2);\n    return *this;\n  }\n\n private:\n  static void DoInit() {\n    Config::Init();\n    // x³ = q = |Config::kNonResidue|\n\n    // αᴾ = (α₀ + α₁x + α₂x²)ᴾ\n    //    = α₀ᴾ + α₁ᴾxᴾ + α₂ᴾx²ᴾ\n    //    = ᾱ₀ + ᾱ₁xᴾ + ᾱx²ᴾ <- conjugate\n    //    = ᾱ₀ + ᾱ₁xᴾ⁻¹x + ᾱx²ᴾ⁻²x²\n    //    = ᾱ₀ + ᾱ₁(x³)^((P - 1) / 3) * x + ᾱ(x³)^(2 * (P - 1) / 3) * x²\n    //    = ᾱ₀ + ᾱ₁ωx + ᾱω²x², where ω is a cubic root of unity.\n\n    using UnpackedBasePrimeField = MaybeUnpack<BasePrimeField>;\n\n    constexpr size_t N = UnpackedBasePrimeField::kLimbNums;\n    // m₁ = P\n    mpz_class m1;\n    if constexpr (UnpackedBasePrimeField::Config::kModulusBits <= 32) {\n      m1 = mpz_class(UnpackedBasePrimeField::Config::kModulus);\n    } else {\n      gmp::WriteLimbs(UnpackedBasePrimeField::Config::kModulus.limbs, N, &m1);\n    }\n\n#define SET_M(d, d_prev) mpz_class m##d = m##d_prev * m1\n\n    // m₂ = m₁ * P = P²\n    SET_M(2, 1);\n    // m₃ = m₂ * P = P³\n    SET_M(3, 2);\n    // m₄ = m₃ * P = P⁴\n    SET_M(4, 3);\n    // m₅ = m₄ * P = P⁵\n    SET_M(5, 4);\n\n#undef SET_M\n\n#define SET_EXP_GMP(d) mpz_class exp##d##_gmp = (m##d - 1) / mpz_class(3)\n\n    // exp₁ = (m₁ - 1) / 3 = (P¹ - 1) / 3\n    SET_EXP_GMP(1);\n    // exp₂ = (m₂ - 1) / 3 = (P² - 1) / 3\n    SET_EXP_GMP(2);\n    // exp₃ = (m₃ - 1) / 3 = (P³ - 1) / 3\n    SET_EXP_GMP(3);\n    // exp₄ = (m₄ - 1) / 3 = (P⁴ - 1) / 3\n    SET_EXP_GMP(4);\n    // exp₅ = (m₅ - 1) / 3 = (P⁵ - 1) / 3\n    SET_EXP_GMP(5);\n\n#undef SET_EXP_GMP\n\n    // |kFrobeniusCoeffs[0]| = q^((P⁰ - 1) / 3)\n    Config::kFrobeniusCoeffs[0] = FrobeniusCoefficient::One();\n#define SET_FROBENIUS_COEFF(d)                \\\n  BigInt<d * N> exp##d(0);                    \\\n  gmp::CopyLimbs(exp##d##_gmp, exp##d.limbs); \\\n  Config::kFrobeniusCoeffs[d] = Config::kNonResidue.Pow(exp##d)\n\n    // |kFrobeniusCoeffs[1]| = q^(exp₁) = q^((P¹ - 1) / 3)\n    SET_FROBENIUS_COEFF(1);\n    // |kFrobeniusCoeffs[2]| = q^(exp₂) = q^((P² - 1) / 3)\n    SET_FROBENIUS_COEFF(2);\n    // |kFrobeniusCoeffs[3]| = q^(exp₃) = q^((P³ - 1) / 3)\n    SET_FROBENIUS_COEFF(3);\n    // |kFrobeniusCoeffs[4]| = q^(exp₄) = q^((P⁴ - 1) / 3)\n    SET_FROBENIUS_COEFF(4);\n    // |kFrobeniusCoeffs[5]| = q^(exp₅) = q^((P⁵ - 1) / 3)\n    SET_FROBENIUS_COEFF(5);\n\n#undef SET_FROBENIUS_COEFF\n\n    // kFrobeniusCoeffs2[0] = q^((P⁰ - 1) / 3)\n    Config::kFrobeniusCoeffs2[0] = FrobeniusCoefficient::One();\n#define SET_FROBENIUS_COEFF2(d)                              \\\n  gmp::CopyLimbs(mpz_class(2) * exp##d##_gmp, exp##d.limbs); \\\n  Config::kFrobeniusCoeffs2[d] = Config::kNonResidue.Pow(exp##d)\n\n    // kFrobeniusCoeffs2[1] = q^(2 * exp₁) = q^(2 * (P¹ - 1) / 3)\n    SET_FROBENIUS_COEFF2(1);\n    // kFrobeniusCoeffs2[2] = q^(2 * exp₂) = q^(2 * (P² - 1) / 3)\n    SET_FROBENIUS_COEFF2(2);\n    // kFrobeniusCoeffs2[3] = q^(2 * exp₃) = q^(2 * (P³ - 1) / 3)\n    SET_FROBENIUS_COEFF2(3);\n    // kFrobeniusCoeffs2[4] = q^(2 * exp₄) = q^(2 * (P⁴ - 1) / 3)\n    SET_FROBENIUS_COEFF2(4);\n    // kFrobeniusCoeffs2[5] = q^(2 * exp₅) = q^(2 * (P⁵ - 1) / 3)\n    SET_FROBENIUS_COEFF2(5);\n\n#undef SET_FROBENIUS_COEFF2\n  }\n};\n\ntemplate <typename Config>\nstruct ExtensionFieldTraits<Fp6<Config>> {\n  constexpr static uint32_t kDegreeOverBaseField = Config::kDegreeOverBaseField;\n  constexpr static uint32_t kDegreeOverBasePrimeField = 6;\n\n  using BaseField = typename Fp6<Config>::BaseField;\n  using BasePrimeField = typename Fp6<Config>::BasePrimeField;\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_FP6_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/fp6_unittest.cc",
    "content": "#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/buffer/vector_buffer.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/fq6.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\nclass Fp6Test : public FiniteFieldTest<bn254::Fq6> {};\n\n}  // namespace\n\nTEST_F(Fp6Test, TypeTest) {\n  EXPECT_TRUE((std::is_same_v<bn254::Fq6::BaseField, bn254::Fq2>));\n  EXPECT_TRUE((std::is_same_v<bn254::Fq6::BasePrimeField, bn254::Fq>));\n}\n\nTEST_F(Fp6Test, Copyable) {\n  using F = bn254::Fq6;\n\n  const F expected = F::Random();\n\n  base::Uint8VectorBuffer write_buf;\n  ASSERT_TRUE(write_buf.Grow(base::EstimateSize(expected)));\n  ASSERT_TRUE(write_buf.Write(expected));\n  ASSERT_TRUE(write_buf.Done());\n\n  write_buf.set_buffer_offset(0);\n\n  F value;\n  ASSERT_TRUE(write_buf.Read(&value));\n  EXPECT_EQ(expected, value);\n}\n\nTEST_F(Fp6Test, JsonValueConverter) {\n  using F = bn254::Fq6;\n\n  F expected = F::Random();\n  std::string json = base::WriteToJson(expected);\n\n  F value;\n  std::string error;\n  ASSERT_TRUE(base::ParseJson(json, &value, &error));\n  ASSERT_TRUE(error.empty());\n  EXPECT_EQ(value, expected);\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/finite_fields/generator/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"generator_util\",\n    srcs = [\"generator_util.cc\"],\n    hdrs = [\"generator_util.h\"],\n    deps = [\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/base/files:file_path\",\n        \"//tachyon/base/strings:string_number_conversions\",\n        \"//tachyon/base/strings:string_util\",\n        \"//tachyon/math/base:big_int\",\n        \"//tachyon/math/base:bit_iterator\",\n        \"//tachyon/math/base/gmp:gmp_util\",\n        \"//tachyon/math/finite_fields:modulus\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/math/finite_fields/generator/binary_field_generator/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_binary\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_binary(\n    name = \"binary_field_generator\",\n    srcs = [\"binary_field_generator.cc\"],\n    data = [\n        \"binary_field_config.h.tpl\",\n        \"binary_field_cpu.h.tpl\",\n        \"binary_field_gpu.h.tpl\",\n    ],\n    deps = [\n        \"//tachyon/base/console\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/base/files:file_path_flag\",\n        \"//tachyon/base/files:file_util\",\n        \"//tachyon/base/flag:flag_parser\",\n        \"//tachyon/build:cc_writer\",\n        \"//tachyon/math/base:bit_iterator\",\n        \"//tachyon/math/base/gmp:bit_traits\",\n        \"//tachyon/math/finite_fields/generator:generator_util\",\n        \"@com_google_absl//absl/container:flat_hash_map\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/math/finite_fields/generator/binary_field_generator/binary_field_config.h.tpl",
    "content": "// clang-format off\n#include <stddef.h>\n#include <stdint.h>\n\n#include \"tachyon/export.h\"\n%{if NeedsBigInt}\n#include \"tachyon/math/base/big_int.h\"\n%{endif NeedsBigInt}\n\nnamespace %{namespace} {\n\nclass TACHYON_EXPORT %{class}Config {\n public:\n  constexpr static const char* kName = \"%{namespace}::%{class}\";\n\n  constexpr static size_t kModulusBits = %{modulus_bits};\n  constexpr static %{modulus_type} kModulus = %{modulus};\n\n  constexpr static %{value_type} kOne = %{value};\n\n  static void Init() {}\n};\n\n}  // namespace %{namespace}\n// clang-format on\n"
  },
  {
    "path": "tachyon/math/finite_fields/generator/binary_field_generator/binary_field_cpu.h.tpl",
    "content": "// clang-format off\n#include \"%{config_header_path}\"\n\n#include \"tachyon/math/finite_fields/binary_fields/binary_field.h\"\n\nnamespace %{namespace} {\n\nusing %{class} = BinaryField<%{class}Config>;\n\n}  // namespace %{namespace}\n// clang-format on\n"
  },
  {
    "path": "tachyon/math/finite_fields/generator/binary_field_generator/binary_field_generator.cc",
    "content": "#include <string>\n\n#include \"absl/container/flat_hash_map.h\"\n#include \"absl/strings/str_replace.h\"\n\n#include \"tachyon/base/console/iostream.h\"\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/files/file_path_flag.h\"\n#include \"tachyon/base/files/file_util.h\"\n#include \"tachyon/base/flag/flag_parser.h\"\n#include \"tachyon/base/strings/string_util.h\"\n#include \"tachyon/build/cc_writer.h\"\n#include \"tachyon/math/base/big_int.h\"\n#include \"tachyon/math/base/bit_iterator.h\"\n#include \"tachyon/math/base/gmp/bit_traits.h\"\n#include \"tachyon/math/finite_fields/generator/generator_util.h\"\n\nnamespace tachyon {\n\nsize_t GetNumBits(const mpz_class& m) {\n  auto it = math::BitIteratorBE<mpz_class>::begin(&m, true);\n  auto end = math::BitIteratorBE<mpz_class>::end(&m);\n  size_t num_bits = 0;\n  while (it != end) {\n    ++it;\n    ++num_bits;\n  }\n  return num_bits;\n}\nstruct GenerationConfig : public build::CcWriter {\n  base::FilePath binary_config_hdr_tpl_path;\n  base::FilePath binary_cpu_hdr_tpl_path;\n  base::FilePath binary_gpu_hdr_tpl_path;\n\n  std::string ns_name;\n  std::string class_name;\n  std::string modulus;\n\n  int GenerateConfigHdr() const;\n  int GenerateCpuHdr() const;\n  int GenerateGpuHdr() const;\n};\n\nint GenerationConfig::GenerateConfigHdr() const {\n  absl::flat_hash_map<std::string, std::string> replacements = {\n      {\"%{namespace}\", ns_name},\n      {\"%{class}\", class_name},\n  };\n\n  mpz_class m = math::gmp::FromDecString(modulus);\n  size_t num_bits = GetNumBits(m);\n  if (num_bits == 128 + 1) {\n    replacements[\"%{modulus}\"] =\n        absl::Substitute(\"BigInt<3>{$0}\", math::MpzClassToString(m));\n    replacements[\"%{modulus_type}\"] = \"BigInt<3>\";\n    replacements[\"%{value_type}\"] = \"BigInt<2>\";\n    replacements[\"%{value}\"] = \"BigInt<2>{1}\";\n  } else if (num_bits == 64 + 1) {\n    replacements[\"%{modulus}\"] =\n        absl::Substitute(\"BigInt<2>{$0}\", math::MpzClassToString(m));\n    replacements[\"%{modulus_type}\"] = \"BigInt<2>\";\n    replacements[\"%{value_type}\"] = \"uint64_t\";\n    replacements[\"%{value}\"] = \"1\";\n  } else {\n    replacements[\"%{modulus}\"] = math::MpzClassToString(m);\n    std::string modulus_type;\n    std::string value_type;\n    switch (num_bits) {\n      case 1 + 1:\n      case 2 + 1:\n      case 4 + 1:\n        modulus_type = \"uint8_t\";\n        value_type = \"uint8_t\";\n        break;\n      case 8 + 1:\n        modulus_type = \"uint16_t\";\n        value_type = \"uint8_t\";\n        break;\n      case 16 + 1:\n        modulus_type = \"uint32_t\";\n        value_type = \"uint16_t\";\n        break;\n      case 32 + 1:\n        modulus_type = \"uint64_t\";\n        value_type = \"uint32_t\";\n        break;\n      default:\n        NOTREACHED();\n    }\n    replacements[\"%{modulus_type}\"] = modulus_type;\n    replacements[\"%{value_type}\"] = value_type;\n    replacements[\"%{value}\"] = \"1\";\n  }\n  replacements[\"%{modulus_bits}\"] = base::NumberToString(num_bits);\n\n  std::string tpl_content;\n  CHECK(base::ReadFileToString(binary_config_hdr_tpl_path, &tpl_content));\n\n  std::vector<std::string> tpl_lines = absl::StrSplit(tpl_content, '\\n');\n  RemoveOptionalLines(tpl_lines, \"NeedsBigInt\", num_bits >= 64 + 1);\n\n  tpl_content = absl::StrJoin(tpl_lines, \"\\n\");\n  std::string content =\n      absl::StrReplaceAll(tpl_content, std::move(replacements));\n\n  return WriteHdr(content, false);\n}\n\nint GenerationConfig::GenerateCpuHdr() const {\n  std::string tpl_content;\n  CHECK(base::ReadFileToString(binary_cpu_hdr_tpl_path, &tpl_content));\n\n  base::FilePath hdr_path = GetHdrPath();\n  base::FilePath basename = hdr_path.BaseName().RemoveExtension();\n  base::FilePath config_header_path =\n      hdr_path.DirName().Append(basename.value() + \"_config.h\");\n\n  std::string content = absl::StrReplaceAll(\n      tpl_content, {\n                       {\"%{config_header_path}\", config_header_path.value()},\n                       {\"%{namespace}\", ns_name},\n                       {\"%{class}\", class_name},\n                   });\n\n  return WriteHdr(content, false);\n}\n\nint GenerationConfig::GenerateGpuHdr() const {\n  std::string tpl_content;\n  CHECK(base::ReadFileToString(binary_gpu_hdr_tpl_path, &tpl_content));\n\n  std::string content = absl::StrReplaceAll(\n      tpl_content, {\n                       {\"%{config_header_path}\",\n                        math::ConvertToConfigHdr(GetHdrPath()).value()},\n                       {\"%{namespace}\", ns_name},\n                       {\"%{class}\", class_name},\n                   });\n\n  return WriteHdr(content, false);\n}\n\nint RealMain(int argc, char** argv) {\n  GenerationConfig config;\n  config.generator = \"//tachyon/math/finite_fields/prime_field_field_generator\";\n\n  base::FlagParser parser;\n  parser.AddFlag<base::FilePathFlag>(&config.out)\n      .set_long_name(\"--out\")\n      .set_help(\"path to output\");\n  parser.AddFlag<base::StringFlag>(&config.ns_name)\n      .set_long_name(\"--namespace\")\n      .set_required();\n  parser.AddFlag<base::StringFlag>(&config.class_name).set_long_name(\"--class\");\n  parser.AddFlag<base::StringFlag>(&config.modulus)\n      .set_long_name(\"--modulus\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.binary_config_hdr_tpl_path)\n      .set_long_name(\"--binary_config_hdr_tpl_path\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.binary_cpu_hdr_tpl_path)\n      .set_long_name(\"--binary_cpu_hdr_tpl_path\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.binary_gpu_hdr_tpl_path)\n      .set_long_name(\"--binary_gpu_hdr_tpl_path\")\n      .set_required();\n\n  std::string error;\n  if (!parser.Parse(argc, argv, &error)) {\n    tachyon_cerr << error << std::endl;\n    return 1;\n  }\n\n  if (base::EndsWith(config.out.value(), \"_config.h\")) {\n    return config.GenerateConfigHdr();\n  } else if (base::EndsWith(config.out.value(), \"_gpu.h\")) {\n    return config.GenerateGpuHdr();\n  } else if (base::EndsWith(config.out.value(), \".h\")) {\n    return config.GenerateCpuHdr();\n  } else {\n    tachyon_cerr << \"suffix not supported:\" << config.out << std::endl;\n    return 1;\n  }\n}\n\n}  // namespace tachyon\n\nint main(int argc, char** argv) { return tachyon::RealMain(argc, argv); }\n"
  },
  {
    "path": "tachyon/math/finite_fields/generator/binary_field_generator/binary_field_gpu.h.tpl",
    "content": "// clang-format off\n#include \"%{config_header_path}\"\n\n#include \"tachyon/math/finite_fields/binary_field.h\"\n\nnamespace %{namespace} {\n\nusing %{class}Gpu = BinaryField<%{class}Config>;\n\n}  // namespace %{namespace}\n// clang-format on\n"
  },
  {
    "path": "tachyon/math/finite_fields/generator/binary_field_generator/build_defs.bzl",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\ndef _generate_binary_field_impl(ctx):\n    binary_config_hdr_tpl_path = ctx.expand_location(\"$(location @kroma_network_tachyon//tachyon/math/finite_fields/generator/binary_field_generator:binary_field_config.h.tpl)\", [ctx.attr.binary_config_hdr_tpl])\n    binary_cpu_hdr_tpl_path = ctx.expand_location(\"$(location @kroma_network_tachyon//tachyon/math/finite_fields/generator/binary_field_generator:binary_field_cpu.h.tpl)\", [ctx.attr.binary_cpu_hdr_tpl])\n    binary_gpu_hdr_tpl_path = ctx.expand_location(\"$(location @kroma_network_tachyon//tachyon/math/finite_fields/generator/binary_field_generator:binary_field_gpu.h.tpl)\", [ctx.attr.binary_gpu_hdr_tpl])\n\n    arguments = [\n        \"--out=%s\" % (ctx.outputs.out.path),\n        \"--namespace=%s\" % (ctx.attr.namespace),\n        \"--class=%s\" % (ctx.attr.class_name),\n        \"--modulus=%s\" % (ctx.attr.modulus),\n        \"--binary_config_hdr_tpl_path=%s\" % (binary_config_hdr_tpl_path),\n        \"--binary_cpu_hdr_tpl_path=%s\" % (binary_cpu_hdr_tpl_path),\n        \"--binary_gpu_hdr_tpl_path=%s\" % (binary_gpu_hdr_tpl_path),\n    ]\n\n    ctx.actions.run(\n        inputs = [\n            ctx.files.binary_config_hdr_tpl[0],\n            ctx.files.binary_cpu_hdr_tpl[0],\n            ctx.files.binary_gpu_hdr_tpl[0],\n        ],\n        tools = [ctx.executable._tool],\n        executable = ctx.executable._tool,\n        outputs = [ctx.outputs.out],\n        arguments = arguments,\n    )\n\n    return [DefaultInfo(files = depset([ctx.outputs.out]))]\n\ngenerate_binary_field = rule(\n    implementation = _generate_binary_field_impl,\n    attrs = {\n        \"out\": attr.output(mandatory = True),\n        \"namespace\": attr.string(mandatory = True),\n        \"class_name\": attr.string(mandatory = True),\n        \"modulus\": attr.string(mandatory = True),\n        \"binary_config_hdr_tpl\": attr.label(\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/math/finite_fields/generator/binary_field_generator:binary_field_config.h.tpl\"),\n        ),\n        \"binary_cpu_hdr_tpl\": attr.label(\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/math/finite_fields/generator/binary_field_generator:binary_field_cpu.h.tpl\"),\n        ),\n        \"binary_gpu_hdr_tpl\": attr.label(\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/math/finite_fields/generator/binary_field_generator:binary_field_gpu.h.tpl\"),\n        ),\n        \"_tool\": attr.label(\n            # TODO(chokobole): Change to \"exec\", so we can build on macos.\n            cfg = \"target\",\n            executable = True,\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/math/finite_fields/generator/binary_field_generator\"),\n        ),\n    },\n)\n\ndef generate_binary_fields(\n        name,\n        namespace,\n        class_name,\n        modulus,\n        **kwargs):\n    for n in [\n        (\"{}_gen_config_hdr\".format(name), \"{}_config.h\".format(name)),\n        (\"{}_gen_hdr\".format(name), \"{}.h\".format(name)),\n        (\"{}_gen_gpu_hdr\".format(name), \"{}_gpu.h\".format(name)),\n    ]:\n        generate_binary_field(\n            namespace = namespace,\n            class_name = class_name,\n            modulus = modulus,\n            name = n[0],\n            out = n[1],\n        )\n\n    modulus_int = int(modulus)\n    needs_bigint = modulus_int == 1 << 64\n\n    tachyon_cc_library(\n        name = \"{}_config\".format(name),\n        hdrs = [\":{}_gen_config_hdr\".format(name)],\n        deps = [\"//tachyon:export\"] + [\"//tachyon/math/base:big_int\"] if needs_bigint else [],\n    )\n\n    tachyon_cc_library(\n        name = name,\n        hdrs = [\":{}_gen_hdr\".format(name)],\n        deps = [\n            \":{}_config\".format(name),\n            \"//tachyon/math/finite_fields/binary_fields:binary_field\",\n        ],\n        **kwargs\n    )\n\n    tachyon_cc_library(\n        name = \"{}_gpu\".format(name),\n        hdrs = [\":{}_gen_gpu_hdr\".format(name)],\n        deps = [\n            \":{}_config\".format(name),\n            \"//tachyon/math/finite_fields/binary_fields:binary_field\",\n        ],\n        **kwargs\n    )\n"
  },
  {
    "path": "tachyon/math/finite_fields/generator/ext_field_generator/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_binary\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_binary(\n    name = \"ext_field_generator\",\n    srcs = [\"ext_field_generator.cc\"],\n    data = [\"fp.h.tpl\"],\n    deps = [\n        \"//tachyon/base/console\",\n        \"//tachyon/base/files:file_path_flag\",\n        \"//tachyon/base/flag:flag_parser\",\n        \"//tachyon/base/strings:string_number_conversions\",\n        \"//tachyon/build:cc_writer\",\n        \"//tachyon/math/finite_fields/generator:generator_util\",\n        \"@com_google_absl//absl/base\",\n        \"@com_google_absl//absl/container:flat_hash_map\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/math/finite_fields/generator/ext_field_generator/build_defs.bzl",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\ndef _generate_ext_field_impl(ctx):\n    fp_hdr_tpl_path = ctx.expand_location(\"$(location @kroma_network_tachyon//tachyon/math/finite_fields/generator/ext_field_generator:fp.h.tpl)\", [ctx.attr.fp_hdr_tpl_path])\n\n    arguments = [\n        \"--out=%s\" % (ctx.outputs.out.path),\n        \"--namespace=%s\" % (ctx.attr.namespace),\n        \"--class=%s\" % (ctx.attr.class_name),\n        \"--degree=%s\" % (ctx.attr.degree),\n        \"--base_field_degree=%s\" % (ctx.attr.base_field_degree),\n        \"--base_field_hdr=%s\" % (ctx.attr.base_field_hdr),\n        \"--base_field=%s\" % (ctx.attr.base_field),\n        \"--fp_hdr_tpl_path=%s\" % (fp_hdr_tpl_path),\n    ]\n    if ctx.attr.is_packed:\n        arguments.append(\"--is_packed\")\n\n    for non_residue in ctx.attr.non_residue:\n        arguments.append(\"--non_residue=%s\" % (non_residue))\n\n    if len(ctx.attr.mul_by_non_residue_override) > 0:\n        arguments.append(\"--mul_by_non_residue_override=%s\" % (ctx.attr.mul_by_non_residue_override))\n\n    ctx.actions.run(\n        inputs = [ctx.files.fp_hdr_tpl_path[0]],\n        tools = [ctx.executable._tool],\n        executable = ctx.executable._tool,\n        outputs = [ctx.outputs.out],\n        arguments = arguments,\n    )\n\n    return [DefaultInfo(files = depset([ctx.outputs.out]))]\n\ngenerate_ext_field = rule(\n    implementation = _generate_ext_field_impl,\n    attrs = {\n        \"out\": attr.output(mandatory = True),\n        \"namespace\": attr.string(mandatory = True),\n        \"class_name\": attr.string(mandatory = True),\n        \"degree\": attr.int(mandatory = True),\n        \"non_residue\": attr.string_list(mandatory = True),\n        \"base_field_degree\": attr.int(mandatory = True),\n        \"base_field_hdr\": attr.string(mandatory = True),\n        \"base_field\": attr.string(mandatory = True),\n        \"is_packed\": attr.bool(mandatory = True),\n        \"mul_by_non_residue_override\": attr.string(),\n        \"fp_hdr_tpl_path\": attr.label(\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/math/finite_fields/generator/ext_field_generator:fp.h.tpl\"),\n        ),\n        \"_tool\": attr.label(\n            # TODO(chokobole): Change to \"exec\", so we can build on macos.\n            cfg = \"target\",\n            executable = True,\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/math/finite_fields/generator/ext_field_generator\"),\n        ),\n    },\n)\n\ndef _generate_ext_fields(\n        name,\n        namespace,\n        class_name,\n        degree,\n        non_residue,\n        base_field_degree,\n        base_field_hdr,\n        base_field,\n        is_packed,\n        mul_by_non_residue_override = \"\",\n        ext_field_deps = [],\n        deps = [],\n        **kwargs):\n    for n in [\n        (\"{}_gen_hdr\".format(name), \"{}.h\".format(name)),\n    ]:\n        generate_ext_field(\n            namespace = namespace,\n            class_name = class_name,\n            degree = degree,\n            non_residue = non_residue,\n            base_field_degree = base_field_degree,\n            base_field_hdr = base_field_hdr,\n            base_field = base_field,\n            is_packed = is_packed,\n            mul_by_non_residue_override = mul_by_non_residue_override,\n            name = n[0],\n            out = n[1],\n        )\n\n    tachyon_cc_library(\n        name = name,\n        hdrs = [\":{}_gen_hdr\".format(name)],\n        deps = deps + ext_field_deps + [\n            \"//tachyon/base:logging\",\n        ],\n        **kwargs\n    )\n\ndef generate_fp2s(\n        name,\n        **kwargs):\n    _generate_ext_fields(\n        name = name,\n        degree = 2,\n        base_field_degree = 1,\n        ext_field_deps = [\"//tachyon/math/finite_fields:fp2\"],\n        **kwargs\n    )\n\ndef generate_fp3s(\n        name,\n        **kwargs):\n    _generate_ext_fields(\n        name = name,\n        degree = 3,\n        base_field_degree = 1,\n        ext_field_deps = [\"//tachyon/math/finite_fields:fp3\"],\n        **kwargs\n    )\n\ndef generate_fp4s(\n        name,\n        **kwargs):\n    _generate_ext_fields(\n        name = name,\n        degree = 4,\n        ext_field_deps = [\"//tachyon/math/finite_fields:fp4\"],\n        **kwargs\n    )\n\ndef generate_fp6s(\n        name,\n        **kwargs):\n    _generate_ext_fields(\n        name = name,\n        degree = 6,\n        ext_field_deps = [\"//tachyon/math/finite_fields:fp6\"],\n        **kwargs\n    )\n\ndef generate_fp12s(\n        name,\n        **kwargs):\n    _generate_ext_fields(\n        name = name,\n        degree = 12,\n        base_field_degree = 6,\n        ext_field_deps = [\"//tachyon/math/finite_fields:fp12\"],\n        **kwargs\n    )\n"
  },
  {
    "path": "tachyon/math/finite_fields/generator/ext_field_generator/ext_field_generator.cc",
    "content": "#include \"absl/container/flat_hash_map.h\"\n#include \"absl/strings/str_replace.h\"\n\n#include \"tachyon/base/console/iostream.h\"\n#include \"tachyon/base/files/file_path_flag.h\"\n#include \"tachyon/base/flag/flag_parser.h\"\n#include \"tachyon/base/strings/string_number_conversions.h\"\n#include \"tachyon/base/strings/string_util.h\"\n#include \"tachyon/build/cc_writer.h\"\n#include \"tachyon/math/finite_fields/generator/generator_util.h\"\n\nnamespace tachyon {\n\nstruct GenerationConfig : public build::CcWriter {\n  base::FilePath fp_hdr_tpl_path;\n\n  std::string ns_name;\n  std::string class_name;\n  int degree;\n  int base_field_degree;\n  std::vector<std::string> non_residue;\n  std::string base_field_hdr;\n  std::string base_field;\n  bool is_packed;\n  std::string mul_by_non_residue_override;\n\n  std::string GenerateFastMulByNonResidueCode(int64_t a_value) const;\n  std::string GenerateMulByNonResidueCodeByDegree() const;\n  std::string GenerateInitCode(bool mul_by_non_residue_fast) const;\n  int GenerateConfigHdr() const;\n};\n\nstd::string GenerationConfig::GenerateInitCode(\n    bool mul_by_non_residue_fast) const {\n  std::string init;\n  if (mul_by_non_residue_fast) {\n    if (is_packed) {\n      init = math::GenerateInitPackedField(\"kNonResidue\", \"BaseField\",\n                                           non_residue[0]);\n    } else {\n      init =\n          math::GenerateInitField(\"kNonResidue\", \"BaseField\", non_residue[0]);\n    }\n  } else {\n    init = math::GenerateInitExtField(\"kNonResidue\", \"BaseField\", non_residue,\n                                      base_field_degree);\n  }\n  return init;\n}\n\nstd::string GenerationConfig::GenerateFastMulByNonResidueCode(\n    int64_t a_value) const {\n  std::stringstream ss;\n  // clang-format off\n  ss << \"    BaseField ret = v;\" << std::endl;\n  ss << \"    return ret\" << math::GenerateFastMultiplication(a_value) << \";\";\n  // clang-format on\n  return ss.str();\n}\n\nstd::string GenerationConfig::GenerateMulByNonResidueCodeByDegree() const {\n  std::stringstream ss;\n  // clang-format off\n\n  if (degree == 4 && non_residue.size() == 2 && non_residue[0] == \"0\" && non_residue[1] == \"1\") {\n    ss << \"    // See [[DESD06, Section 5.1]](https://eprint.iacr.org/2006/471.pdf).\" << std::endl;\n    ss << \"    return BaseField(BaseField::Config::MulByNonResidue(v.c1()), v.c0());\";\n  } else if (degree == 12 && non_residue.size() == 3 && non_residue[0] == \"0\" && non_residue[1] == \"1\" && non_residue[2] == \"0\") {\n    ss << \"    // See [[DESD06, Section 6.1]](https://eprint.iacr.org/2006/471.pdf).\" << std::endl;\n    ss << \"    return BaseField(BaseField::Config::MulByNonResidue(v.c2()), v.c0(), v.c1());\";\n  } else {\n    return \"    return v * kNonResidue;\";\n  }\n  // clang-format on\n  return ss.str();\n}\n\nint GenerationConfig::GenerateConfigHdr() const {\n  absl::flat_hash_map<std::string, std::string> replacements = {\n      {\"%{namespace}\", ns_name},\n      {\"%{class}\", class_name},\n      {\"%{degree}\", base::NumberToString(degree)},\n      {\"%{degree_over_base_prime_field}\", base::NumberToString(degree)},\n      {\"%{base_field}\", base_field},\n      {\"%{base_field_hdr}\", base_field_hdr},\n      {\"%{frobenius_coeffs_size}\", base::NumberToString(degree)}};\n\n  int degree_over_base_field = degree / base_field_degree;\n  replacements[\"%{degree_over_base_field}\"] =\n      base::NumberToString(degree_over_base_field);\n  replacements[\"%{base_prime_field}\"] =\n      degree == degree_over_base_field ? \"BaseField\"\n                                       : \"typename BaseField::BasePrimeField\";\n  if (degree_over_base_field == 3) {\n    replacements[\"%{frobenius_coeffs2_size}\"] = base::NumberToString(degree);\n  }\n\n  bool mul_by_non_residue_fast =\n      non_residue.size() == 1\n          ? true\n          : std::all_of(non_residue.begin() + 1, non_residue.end(),\n                        [](const std::string& e) { return e == \"0\"; });\n\n  replacements[\"%{init_code}\"] = GenerateInitCode(mul_by_non_residue_fast);\n\n  int64_t a_value;\n  CHECK(base::StringToInt64(non_residue[0], &a_value));\n  replacements[\"%{non_residue_is_minus_one}\"] =\n      base::BoolToString(a_value == -1);\n\n  if (!mul_by_non_residue_override.empty()) {\n    replacements[\"%{mul_by_non_residue_code}\"] = mul_by_non_residue_override;\n  } else if (mul_by_non_residue_fast) {\n    replacements[\"%{mul_by_non_residue_code}\"] =\n        GenerateFastMulByNonResidueCode(a_value);\n  } else {\n    replacements[\"%{mul_by_non_residue_code}\"] =\n        GenerateMulByNonResidueCodeByDegree();\n  }\n\n  replacements[\"%{frobenius_coefficient}\"] =\n      ((degree == 4 && base_field_degree == 2) ||\n       (degree == 6 && base_field_degree == 3) || degree == 12)\n          ? \"typename BaseField::BaseField\"\n          : \"BaseField\";\n\n  std::string tpl_content;\n  CHECK(base::ReadFileToString(fp_hdr_tpl_path, &tpl_content));\n\n  std::vector<std::string> tpl_lines = absl::StrSplit(tpl_content, '\\n');\n\n  RemoveOptionalLines(tpl_lines, \"FrobeniusCoefficient2\",\n                      degree_over_base_field >= 3);\n  RemoveOptionalLines(tpl_lines, \"FrobeniusCoefficient3\",\n                      degree_over_base_field >= 4);\n\n  tpl_content = absl::StrJoin(tpl_lines, \"\\n\");\n  std::string content = absl::StrReplaceAll(tpl_content, replacements);\n  return WriteHdr(content, false);\n}\n\nint RealMain(int argc, char** argv) {\n  GenerationConfig config;\n  config.generator = \"//tachyon/math/finite_fields/ext_field_generator\";\n\n  base::FlagParser parser;\n  parser.AddFlag<base::FilePathFlag>(&config.out)\n      .set_long_name(\"--out\")\n      .set_help(\"path to output\");\n  parser.AddFlag<base::StringFlag>(&config.ns_name)\n      .set_long_name(\"--namespace\")\n      .set_required();\n  parser.AddFlag<base::StringFlag>(&config.class_name)\n      .set_long_name(\"--class\")\n      .set_required();\n  parser.AddFlag<base::IntFlag>(&config.degree)\n      .set_long_name(\"--degree\")\n      .set_required();\n  parser.AddFlag<base::IntFlag>(&config.base_field_degree)\n      .set_long_name(\"--base_field_degree\")\n      .set_required();\n  parser.AddFlag<base::Flag<std::vector<std::string>>>(&config.non_residue)\n      .set_long_name(\"--non_residue\")\n      .set_required();\n  parser.AddFlag<base::StringFlag>(&config.base_field_hdr)\n      .set_long_name(\"--base_field_hdr\")\n      .set_required();\n  parser.AddFlag<base::StringFlag>(&config.base_field)\n      .set_long_name(\"--base_field\")\n      .set_required();\n  parser.AddFlag<base::BoolFlag>(&config.is_packed)\n      .set_long_name(\"--is_packed\")\n      .set_default_value(false);\n  parser.AddFlag<base::FilePathFlag>(&config.fp_hdr_tpl_path)\n      .set_long_name(\"--fp_hdr_tpl_path\")\n      .set_required();\n  parser.AddFlag<base::StringFlag>(&config.mul_by_non_residue_override)\n      .set_long_name(\"--mul_by_non_residue_override\");\n\n  std::string error;\n  if (!parser.Parse(argc, argv, &error)) {\n    tachyon_cerr << error << std::endl;\n    return 1;\n  }\n\n  if (base::EndsWith(config.out.value(), \".h\")) {\n    return config.GenerateConfigHdr();\n  } else {\n    tachyon_cerr << \"suffix not supported:\" << config.out << std::endl;\n    return 1;\n  }\n}\n\n}  // namespace tachyon\n\nint main(int argc, char** argv) { return tachyon::RealMain(argc, argv); }\n"
  },
  {
    "path": "tachyon/math/finite_fields/generator/ext_field_generator/fp.h.tpl",
    "content": "// clang-format off\n#include \"absl/base/call_once.h\"\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/math/finite_fields/fp%{degree}.h\"\n#include \"%{base_field_hdr}\"\n\nnamespace %{namespace} {\n\ntemplate <typename _BaseField>\nclass %{class}Config {\n public:\n  using BaseField = _BaseField;\n  using BasePrimeField = %{base_prime_field};\n  using FrobeniusCoefficient = %{frobenius_coefficient};\n\n  // TODO(chokobole): Make them constexpr.\n  static BaseField kNonResidue;\n  static FrobeniusCoefficient kFrobeniusCoeffs[%{frobenius_coeffs_size}];\n%{if FrobeniusCoefficient2}\n  static FrobeniusCoefficient kFrobeniusCoeffs2[%{frobenius_coeffs_size}];\n%{endif FrobeniusCoefficient2}\n%{if FrobeniusCoefficient3}\n  static FrobeniusCoefficient kFrobeniusCoeffs3[%{frobenius_coeffs_size}];\n%{endif FrobeniusCoefficient3}\n\n  constexpr static bool kNonResidueIsMinusOne = %{non_residue_is_minus_one};\n  constexpr static uint32_t kDegreeOverBaseField = %{degree_over_base_field};\n  constexpr static uint32_t kDegreeOverBasePrimeField = %{degree_over_base_prime_field};\n\n  static BaseField MulByNonResidue(const BaseField& v) {\n%{mul_by_non_residue_code}\n  }\n\n  static void Init() {\n    static absl::once_flag once;\n    absl::call_once(once, %{class}Config::DoInit);\n  }\n\n private:\n  static void DoInit() {\n    BaseField::Init();\n%{init_code}\n    VLOG(1) << \"%{namespace}::%{class} initialized\";\n  }\n};\n\ntemplate <typename BaseField>\nBaseField %{class}Config<BaseField>::kNonResidue;\ntemplate <typename BaseField>\ntypename %{class}Config<BaseField>::FrobeniusCoefficient %{class}Config<BaseField>::kFrobeniusCoeffs[%{frobenius_coeffs_size}];\n%{if FrobeniusCoefficient2}\ntemplate <typename BaseField>\ntypename %{class}Config<BaseField>::FrobeniusCoefficient %{class}Config<BaseField>::kFrobeniusCoeffs2[%{frobenius_coeffs_size}];\n%{endif FrobeniusCoefficient2}\n%{if FrobeniusCoefficient3}\ntemplate <typename BaseField>\ntypename %{class}Config<BaseField>::FrobeniusCoefficient %{class}Config<BaseField>::kFrobeniusCoeffs3[%{frobenius_coeffs_size}];\n%{endif FrobeniusCoefficient3}\nusing %{class} = Fp%{degree}<%{class}Config<%{base_field}>>;\n\n}  // namespace %{namespace}\n// clang-format on\n"
  },
  {
    "path": "tachyon/math/finite_fields/generator/generator_util.cc",
    "content": "#include \"tachyon/math/finite_fields/generator/generator_util.h\"\n\n#include <limits>\n#include <string>\n#include <vector>\n\n#include \"absl/strings/str_join.h\"\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/strings/string_number_conversions.h\"\n#include \"tachyon/base/strings/string_util.h\"\n#include \"tachyon/math/base/big_int.h\"\n#include \"tachyon/math/base/bit_iterator.h\"\n\nnamespace tachyon::math {\n\n// TODO(chokobole): Consider bigendian.\nstd::string MpzClassToString(const mpz_class& m) {\n  size_t limb_size = gmp::GetLimbSize(m);\n  if (limb_size == 0) {\n    return \"0\";\n  }\n\n  enum class MaximumType {\n    kInt,\n    kUint32,\n    kUint64,\n  };\n\n  // This code checks the maximum type of the vector. It determines the template\n  // type |T| of the |std::initializer_list<T>|.\n  MaximumType type = MaximumType::kInt;\n  for (size_t i = 0; i < limb_size; ++i) {\n    mp_limb_t limb = gmp::GetLimb(m, i);\n    if (limb <= std::numeric_limits<int>::max()) {\n      continue;\n    } else if (limb <= std::numeric_limits<uint32_t>::max()) {\n      type = MaximumType::kUint32;\n    } else {\n      type = MaximumType::kUint64;\n      break;\n    }\n  }\n\n  std::vector<std::string> ret =\n      base::CreateVector(limb_size, [type, &m](size_t i) {\n        mp_limb_t limb = gmp::GetLimb(m, i);\n        switch (type) {\n          case MaximumType::kInt:\n            return absl::StrCat(limb);\n          case MaximumType::kUint32:\n            return absl::Substitute(\"UINT32_C($0)\", limb);\n          case MaximumType::kUint64:\n            return absl::Substitute(\"UINT64_C($0)\", limb);\n        }\n        NOTREACHED();\n        return base::EmptyString();\n      });\n  return absl::StrJoin(ret, \", \");\n}\n\nstd::string MpzClassToMontString(const mpz_class& v, const mpz_class& m) {\n  size_t limb_size = gmp::GetLimbSize(m);\n  switch (limb_size) {\n    case 1:\n      return MpzClassToMontString<1>(v, m);\n    case 2:\n      return MpzClassToMontString<2>(v, m);\n    case 3:\n      return MpzClassToMontString<3>(v, m);\n    case 4:\n      return MpzClassToMontString<4>(v, m);\n    case 5:\n      return MpzClassToMontString<5>(v, m);\n    case 6:\n      return MpzClassToMontString<6>(v, m);\n    case 7:\n      return MpzClassToMontString<7>(v, m);\n    case 8:\n      return MpzClassToMontString<8>(v, m);\n    case 9:\n      return MpzClassToMontString<9>(v, m);\n  }\n  NOTREACHED();\n  return \"\";\n}\n\nstd::string GenerateFastMultiplication(int64_t value) {\n  CHECK_NE(value, 0);\n  bool is_negative = value < 0;\n  BigInt<1> scalar(is_negative ? -value : value);\n  auto it = BitIteratorBE<BigInt<1>>::begin(&scalar, true);\n  ++it;\n  auto end = BitIteratorBE<BigInt<1>>::end(&scalar);\n  std::stringstream ss;\n  while (it != end) {\n    ss << \".DoubleInPlace()\";\n    if (*it) {\n      ss << \".AddInPlace(v)\";\n    }\n    ++it;\n  }\n  if (is_negative) ss << \".NegateInPlace()\";\n  return ss.str();\n}\n\nbase::FilePath ConvertToConfigHdr(const base::FilePath& path) {\n  std::string basename = path.BaseName().value();\n  basename = basename.substr(0, basename.find(\"_gpu\"));\n  return path.DirName().Append(basename + \"_config.h\");\n}\n\nbase::FilePath ConvertToCpuHdr(const base::FilePath& path) {\n  std::string basename = path.BaseName().value();\n  basename = basename.substr(0, basename.find(\"_gpu\"));\n  return path.DirName().Append(basename + \".h\");\n}\n\nbase::FilePath ConvertToGpuHdr(const base::FilePath& path) {\n  std::string basename = path.BaseName().RemoveExtension().value();\n  return path.DirName().Append(basename + \"_gpu.h\");\n}\n\nstd::string GenerateInitMpzClass(std::string_view name,\n                                 std::string_view value) {\n  std::stringstream ss;\n  ss << \"    \" << name << \" = \";\n  if (base::ConsumePrefix(&value, \"-\")) {\n    ss << \"-\";\n  }\n  ss << \"gmp::FromDecString(\\\"\" << value << \"\\\");\";\n  return ss.str();\n}\n\nnamespace {\n\nstd::string DoGenerateInitField(std::string_view type, std::string_view value) {\n  std::stringstream ss;\n  if (base::ConsumePrefix(&value, \"-\")) {\n    ss << \"-\";\n  }\n  ss << \"*\" << type << \"::FromDecString(\\\"\" << value << \"\\\")\";\n  return ss.str();\n}\n\n}  // namespace\n\nstd::string GenerateInitField(std::string_view name, std::string_view type,\n                              std::string_view value) {\n  std::stringstream ss;\n  ss << \"    \" << name << \" = \" << DoGenerateInitField(type, value) << \";\";\n  return ss.str();\n}\n\nstd::string GenerateInitPackedField(std::string_view name,\n                                    std::string_view type,\n                                    std::string_view value) {\n  std::stringstream ss;\n  // clang-format off\n  ss << \"    using UnpackedPrimeField = typename math::FiniteFieldTraits<\" << type << \">::PrimeField;\" << std::endl;\n  ss << \"    \" << name << \" = \" << type << \"::Broadcast(\" << DoGenerateInitField(\"UnpackedPrimeField\", value) << \");\";\n  // clang-format on\n  return ss.str();\n}\n\n// TODO(chokobole): Should be generalized for packed extension field.\nstd::string GenerateInitExtField(std::string_view name, std::string_view type,\n                                 absl::Span<const std::string> values,\n                                 size_t degree) {\n  std::stringstream ss;\n  ss << \"    \";\n  ss << name;\n  ss << \" = \";\n  ss << type;\n\n  bool is_prime_field = values.size() == degree;\n  if (is_prime_field) {\n    ss << \"::FromBasePrimeFields(std::vector<BasePrimeField>{\";\n  } else {\n    ss << \"(\";\n  }\n  for (size_t i = 0; i < values.size(); ++i) {\n    std::string_view value = values[i];\n    if (base::ConsumePrefix(&value, \"-\")) {\n      ss << \"-\";\n    }\n    if (is_prime_field) {\n      ss << \"*BasePrimeField::FromDecString(\\\"\" << value << \"\\\")\";\n    } else {\n      uint64_t abs_value;\n      CHECK(base::StringToUint64(value, &abs_value));\n      if (abs_value == 0) {\n        ss << \"BaseField::BaseField::Zero()\";\n      } else if (abs_value == 1) {\n        ss << \"BaseField::BaseField::One()\";\n      } else {\n        ss << \"BaseField::BaseField(\" << abs_value << \")\";\n      }\n    }\n    if (i != values.size() - 1) {\n      ss << \", \";\n    }\n  }\n  if (is_prime_field) {\n    ss << \"}\";\n  }\n  ss << \");\";\n  return ss.str();\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/finite_fields/generator/generator_util.h",
    "content": "#ifndef TACHYON_MATH_FINITE_FIELDS_GENERATOR_GENERATOR_UTIL_H_\n#define TACHYON_MATH_FINITE_FIELDS_GENERATOR_GENERATOR_UTIL_H_\n\n#include <string>\n\n#include \"absl/types/span.h\"\n#include \"third_party/gmp/include/gmpxx.h\"\n\n#include \"tachyon/base/compiler_specific.h\"\n#include \"tachyon/base/files/file_path.h\"\n#include \"tachyon/math/base/gmp/gmp_util.h\"\n#include \"tachyon/math/finite_fields/modulus.h\"\n\nnamespace tachyon::math {\n\n// TODO(chokobole): Consider bigendian.\nstd::string MpzClassToString(const mpz_class& m);\n\ntemplate <size_t N>\nstd::string MpzClassToMontString(const mpz_class& v_in, const mpz_class& m_in) {\n  BigInt<N> m(0);\n\n  if constexpr (N == 1) {\n    // NOLINTNEXTLINE(runtime/int)\n    if (m_in < mpz_class(static_cast<unsigned long>(uint64_t{1} << 32))) {\n      return MpzClassToString(mpz_class((v_in.get_ui() << 32) % m_in.get_ui()));\n    }\n  }\n\n  gmp::CopyLimbs(m_in, m.limbs);\n  BigInt<N> r2 = Modulus<N>::MontgomeryR2(m);\n  uint64_t inv = Modulus<N>::template Inverse<uint64_t>(m);\n\n  BigInt<N> v(0);\n  if (gmp::IsNegative(v_in)) {\n    gmp::CopyLimbs(m_in + v_in, v.limbs);\n  } else {\n    gmp::CopyLimbs(v_in, v.limbs);\n  }\n\n  BigInt<2 * N> mul_result = v.MulExtend(r2);\n  BigInt<N>::template MontgomeryReduce64<false>(mul_result, m, inv, &v);\n\n  mpz_class v_mont;\n  gmp::WriteLimbs(v.limbs, N, &v_mont);\n  return MpzClassToString(v_mont);\n}\n\nstd::string MpzClassToMontString(const mpz_class& v, const mpz_class& m);\n\nstd::string GenerateFastMultiplication(int64_t value);\n\nbase::FilePath ConvertToConfigHdr(const base::FilePath& path);\n\nbase::FilePath ConvertToCpuHdr(const base::FilePath& path);\n\nbase::FilePath ConvertToGpuHdr(const base::FilePath& path);\n\nstd::string GenerateInitMpzClass(std::string_view name, std::string_view value);\n\nstd::string GenerateInitField(std::string_view name, std::string_view type,\n                              std::string_view value);\n\nstd::string GenerateInitPackedField(std::string_view name,\n                                    std::string_view type,\n                                    std::string_view value);\n\nstd::string GenerateInitExtField(std::string_view name, std::string_view type,\n                                 absl::Span<const std::string> values,\n                                 size_t degree);\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_GENERATOR_GENERATOR_UTIL_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/generator/prime_field_generator/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_binary\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_binary(\n    name = \"prime_field_generator\",\n    srcs = [\"prime_field_generator.cc\"],\n    data = [\n        \"fail.cc.tpl\",\n        \"fail.h.tpl\",\n        \"prime_field_config.cc.tpl\",\n        \"prime_field_config.h.tpl\",\n        \"prime_field_cpu.h.tpl\",\n        \"prime_field_gpu.h.tpl\",\n        \"prime_field_x86.h.tpl\",\n        \"small_prime_field_config.cc.tpl\",\n        \"small_prime_field_config.h.tpl\",\n        \"small_prime_field_cpu.h.tpl\",\n        \"small_prime_field_gpu.h.tpl\",\n    ],\n    deps = [\n        \"//tachyon/base/console\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/base/files:file_path_flag\",\n        \"//tachyon/base/files:file_util\",\n        \"//tachyon/base/flag:flag_parser\",\n        \"//tachyon/build:cc_writer\",\n        \"//tachyon/math/base:bit_iterator\",\n        \"//tachyon/math/base:const_init\",\n        \"//tachyon/math/base/gmp:bit_traits\",\n        \"//tachyon/math/finite_fields:prime_field_util\",\n        \"//tachyon/math/finite_fields/generator:generator_util\",\n        \"@com_google_absl//absl/container:flat_hash_map\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/math/finite_fields/generator/prime_field_generator/build_defs.bzl",
    "content": "load(\"@bazel_skylib//rules:common_settings.bzl\", \"BuildSettingInfo\")\nload(\"@iden3_ffiasm//:build_defs.bzl\", generate_ffiasm_prime_field = \"generate_prime_field\")\nload(\"//bazel:tachyon.bzl\", \"if_has_asm_prime_field\")\nload(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\nSMALL_SUBGROUP_ADICITY = \"small_subgroup_adicity\"\nSMALL_SUBGROUP_BASE = \"small_subgroup_base\"\nSUBGROUP_GENERATOR = \"subgroup_generator\"\n\n_PRIME_FIELD = 0\n_FFT_PRIME_FIELD = 1\n_LARGE_FFT_PRIME_FIELD = 2\n\ndef _do_generate_prime_field_impl(ctx, type):\n    x86_hdr_tpl_path = ctx.expand_location(\"$(location @kroma_network_tachyon//tachyon/math/finite_fields/generator/prime_field_generator:prime_field_x86.h.tpl)\", [ctx.attr.x86_hdr_tpl])\n    fail_hdr_tpl_path = ctx.expand_location(\"$(location @kroma_network_tachyon//tachyon/math/finite_fields/generator/prime_field_generator:fail.h.tpl)\", [ctx.attr.fail_hdr_tpl])\n    fail_src_tpl_path = ctx.expand_location(\"$(location @kroma_network_tachyon//tachyon/math/finite_fields/generator/prime_field_generator:fail.cc.tpl)\", [ctx.attr.fail_src_tpl])\n    config_hdr_tpl_path = ctx.expand_location(\"$(location @kroma_network_tachyon//tachyon/math/finite_fields/generator/prime_field_generator:prime_field_config.h.tpl)\", [ctx.attr.config_hdr_tpl])\n    config_src_tpl_path = ctx.expand_location(\"$(location @kroma_network_tachyon//tachyon/math/finite_fields/generator/prime_field_generator:prime_field_config.cc.tpl)\", [ctx.attr.config_src_tpl])\n    small_config_hdr_tpl_path = ctx.expand_location(\"$(location @kroma_network_tachyon//tachyon/math/finite_fields/generator/prime_field_generator:small_prime_field_config.h.tpl)\", [ctx.attr.small_config_hdr_tpl])\n    small_config_src_tpl_path = ctx.expand_location(\"$(location @kroma_network_tachyon//tachyon/math/finite_fields/generator/prime_field_generator:small_prime_field_config.cc.tpl)\", [ctx.attr.small_config_src_tpl])\n    cpu_hdr_tpl_path = ctx.expand_location(\"$(location @kroma_network_tachyon//tachyon/math/finite_fields/generator/prime_field_generator:prime_field_cpu.h.tpl)\", [ctx.attr.cpu_hdr_tpl])\n    small_cpu_hdr_tpl_path = ctx.expand_location(\"$(location @kroma_network_tachyon//tachyon/math/finite_fields/generator/prime_field_generator:small_prime_field_cpu.h.tpl)\", [ctx.attr.small_cpu_hdr_tpl])\n    gpu_hdr_tpl_path = ctx.expand_location(\"$(location @kroma_network_tachyon//tachyon/math/finite_fields/generator/prime_field_generator:prime_field_gpu.h.tpl)\", [ctx.attr.gpu_hdr_tpl])\n    small_gpu_hdr_tpl_path = ctx.expand_location(\"$(location @kroma_network_tachyon//tachyon/math/finite_fields/generator/prime_field_generator:small_prime_field_gpu.h.tpl)\", [ctx.attr.small_gpu_hdr_tpl])\n\n    arguments = [\n        \"--out=%s\" % (ctx.outputs.out.path),\n        \"--namespace=%s\" % (ctx.attr.namespace),\n        \"--class=%s\" % (ctx.attr.class_name),\n        \"--modulus=%s\" % (ctx.attr.modulus),\n        \"--x86_hdr_tpl_path=%s\" % (x86_hdr_tpl_path),\n        \"--fail_hdr_tpl_path=%s\" % (fail_hdr_tpl_path),\n        \"--fail_src_tpl_path=%s\" % (fail_src_tpl_path),\n        \"--config_hdr_tpl_path=%s\" % (config_hdr_tpl_path),\n        \"--config_src_tpl_path=%s\" % (config_src_tpl_path),\n        \"--small_config_hdr_tpl_path=%s\" % (small_config_hdr_tpl_path),\n        \"--small_config_src_tpl_path=%s\" % (small_config_src_tpl_path),\n        \"--cpu_hdr_tpl_path=%s\" % (cpu_hdr_tpl_path),\n        \"--small_cpu_hdr_tpl_path=%s\" % (small_cpu_hdr_tpl_path),\n        \"--gpu_hdr_tpl_path=%s\" % (gpu_hdr_tpl_path),\n        \"--small_gpu_hdr_tpl_path=%s\" % (small_gpu_hdr_tpl_path),\n    ]\n\n    if ctx.attr.use_asm:\n        if ctx.attr._has_asm_prime_field[BuildSettingInfo].value:\n            arguments.append(\"--use_asm\")\n\n    if ctx.attr.use_montgomery:\n        arguments.append(\"--use_montgomery\")\n\n    if len(ctx.attr.reduce32) > 0:\n        arguments.append(\"--reduce32=%s\" % (ctx.attr.reduce32))\n\n    if len(ctx.attr.reduce64) > 0:\n        arguments.append(\"--reduce64=%s\" % (ctx.attr.reduce64))\n\n    if type >= _FFT_PRIME_FIELD:\n        arguments.append(\"--subgroup_generator=%s\" % (ctx.attr.subgroup_generator[BuildSettingInfo].value))\n\n    if type >= _LARGE_FFT_PRIME_FIELD:\n        arguments.append(\"--small_subgroup_adicity=%s\" % (ctx.attr.small_subgroup_adicity[BuildSettingInfo].value))\n        arguments.append(\"--small_subgroup_base=%s\" % (ctx.attr.small_subgroup_base[BuildSettingInfo].value))\n\n    ctx.actions.run(\n        inputs = [\n            ctx.files.x86_hdr_tpl[0],\n            ctx.files.fail_hdr_tpl[0],\n            ctx.files.fail_src_tpl[0],\n            ctx.files.config_hdr_tpl[0],\n            ctx.files.config_src_tpl[0],\n            ctx.files.small_config_hdr_tpl[0],\n            ctx.files.small_config_src_tpl[0],\n            ctx.files.cpu_hdr_tpl[0],\n            ctx.files.small_cpu_hdr_tpl[0],\n            ctx.files.gpu_hdr_tpl[0],\n            ctx.files.small_gpu_hdr_tpl[0],\n        ],\n        tools = [ctx.executable._tool],\n        executable = ctx.executable._tool,\n        outputs = [ctx.outputs.out],\n        arguments = arguments,\n    )\n\n    return [DefaultInfo(files = depset([ctx.outputs.out]))]\n\ndef _generate_prime_field_impl(ctx):\n    _do_generate_prime_field_impl(ctx, _PRIME_FIELD)\n\ndef _generate_fft_prime_field_impl(ctx):\n    _do_generate_prime_field_impl(ctx, _FFT_PRIME_FIELD)\n\ndef _generate_large_fft_prime_field_impl(ctx):\n    _do_generate_prime_field_impl(ctx, _LARGE_FFT_PRIME_FIELD)\n\ndef _attrs(type):\n    d = {\n        \"out\": attr.output(mandatory = True),\n        \"namespace\": attr.string(mandatory = True),\n        \"class_name\": attr.string(mandatory = True),\n        \"modulus\": attr.string(mandatory = True),\n        \"reduce32\": attr.string(mandatory = True),\n        \"reduce64\": attr.string(mandatory = True),\n        \"use_asm\": attr.bool(mandatory = True),\n        \"use_montgomery\": attr.bool(mandatory = True),\n        \"x86_hdr_tpl\": attr.label(\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/math/finite_fields/generator/prime_field_generator:prime_field_x86.h.tpl\"),\n        ),\n        \"fail_hdr_tpl\": attr.label(\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/math/finite_fields/generator/prime_field_generator:fail.h.tpl\"),\n        ),\n        \"fail_src_tpl\": attr.label(\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/math/finite_fields/generator/prime_field_generator:fail.cc.tpl\"),\n        ),\n        \"config_hdr_tpl\": attr.label(\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/math/finite_fields/generator/prime_field_generator:prime_field_config.h.tpl\"),\n        ),\n        \"config_src_tpl\": attr.label(\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/math/finite_fields/generator/prime_field_generator:prime_field_config.cc.tpl\"),\n        ),\n        \"small_config_hdr_tpl\": attr.label(\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/math/finite_fields/generator/prime_field_generator:small_prime_field_config.h.tpl\"),\n        ),\n        \"small_config_src_tpl\": attr.label(\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/math/finite_fields/generator/prime_field_generator:small_prime_field_config.cc.tpl\"),\n        ),\n        \"cpu_hdr_tpl\": attr.label(\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/math/finite_fields/generator/prime_field_generator:prime_field_cpu.h.tpl\"),\n        ),\n        \"small_cpu_hdr_tpl\": attr.label(\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/math/finite_fields/generator/prime_field_generator:small_prime_field_cpu.h.tpl\"),\n        ),\n        \"gpu_hdr_tpl\": attr.label(\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/math/finite_fields/generator/prime_field_generator:prime_field_gpu.h.tpl\"),\n        ),\n        \"small_gpu_hdr_tpl\": attr.label(\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/math/finite_fields/generator/prime_field_generator:small_prime_field_gpu.h.tpl\"),\n        ),\n        \"_has_asm_prime_field\": attr.label(\n            default = Label(\"@kroma_network_tachyon//:has_asm_prime_field\"),\n        ),\n        \"_tool\": attr.label(\n            # TODO(chokobole): Change to \"exec\", so we can build on macos.\n            cfg = \"target\",\n            executable = True,\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/math/finite_fields/generator/prime_field_generator\"),\n        ),\n    }\n\n    if type >= _FFT_PRIME_FIELD:\n        d |= {\n            \"subgroup_generator\": attr.label(),\n        }\n\n    if type >= _LARGE_FFT_PRIME_FIELD:\n        d |= {\n            \"small_subgroup_adicity\": attr.label(),\n            \"small_subgroup_base\": attr.label(),\n        }\n\n    return d\n\ngenerate_prime_field = rule(\n    implementation = _generate_prime_field_impl,\n    attrs = _attrs(_PRIME_FIELD),\n)\n\ngenerate_fft_prime_field = rule(\n    implementation = _generate_fft_prime_field_impl,\n    attrs = _attrs(_FFT_PRIME_FIELD),\n)\n\ngenerate_large_fft_prime_field = rule(\n    implementation = _generate_large_fft_prime_field_impl,\n    attrs = _attrs(_LARGE_FFT_PRIME_FIELD),\n)\n\ndef _do_generate_prime_fields(\n        name,\n        namespace,\n        modulus,\n        use_asm,\n        use_montgomery,\n        **kwargs):\n    is_small_prime_field = int(modulus) < 1 << 32\n\n    config_deps = [\n        \"//tachyon:export\",\n        \"//tachyon/build:build_config\",\n        \"//tachyon/math/base:big_int\",\n        \"//tachyon/math/base:const_init\",\n        \"@com_google_absl//absl/base\",\n    ]\n\n    tachyon_cc_library(\n        name = \"{}_config\".format(name),\n        srcs = [\":{}_gen_config_src\".format(name)],\n        hdrs = [\":{}_gen_config_hdr\".format(name)],\n        deps = config_deps,\n    )\n\n    if is_small_prime_field:\n        if use_montgomery:\n            small_prime_field_dep = \"//tachyon/math/finite_fields:small_prime_field_mont\"\n        else:\n            small_prime_field_dep = \"//tachyon/math/finite_fields:small_prime_field\"\n\n        tachyon_cc_library(\n            name = name,\n            hdrs = [\":{}_gen_hdr\".format(name)],\n            deps = [\n                \":{}_config\".format(name),\n                small_prime_field_dep,\n            ],\n            **kwargs\n        )\n    elif use_asm:\n        prefix = namespace.replace(\"::\", \"_\") + \"_\" + name\n        generate_ffiasm_prime_field(\n            name = prefix,\n            asm_out = \"{}.asm\".format(name),\n            modulus = modulus,\n        )\n\n        cmd_linux_x86 = \"\\n\".join([\n            \"for out in $(OUTS); do\",\n            \"$(location @nasm//:nasm) -f elf64 -o $$out $$(dirname $(location \" + name + \".asm))/$$(basename $${out%.o}.asm)\",\n            \"done\",\n        ])\n        cmd_macos_x86 = \"\\n\".join([\n            \"for out in $(OUTS); do\",\n            \"$(location @nasm//:nasm) -f macho64 --prefix _ -o $$out $$(dirname $(location \" + name + \".asm))/$$(basename $${out%.o}.asm)\",\n            \"done\",\n        ])\n\n        native.genrule(\n            name = \"{}_asm\".format(name),\n            srcs = [\"{}.asm\".format(name)],\n            outs = [\"{}.o\".format(name)],\n            cmd = select({\n                \"@kroma_network_tachyon//:linux_x86_64\": cmd_linux_x86,\n                \"@kroma_network_tachyon//:macos_x86_64\": cmd_macos_x86,\n                \"//conditions:default\": \"touch $@\",\n            }),\n            tools = [\"@nasm\"],\n        )\n\n        tachyon_cc_library(\n            name = \"{}_object\".format(name),\n            srcs = select({\n                \"@kroma_network_tachyon//:linux_x86_64\": [\"{}.o\".format(name)],\n                \"@kroma_network_tachyon//:macos_x86_64\": [\"{}.o\".format(name)],\n                \"//conditions:default\": [],\n            }),\n            linkstatic = True,\n        )\n\n        tachyon_cc_library(\n            name = \"{}_fail\".format(name),\n            srcs = select({\n                \"@kroma_network_tachyon//:linux_x86_64\": [\":{}_gen_fail_src\".format(name)],\n                \"@kroma_network_tachyon//:macos_x86_64\": [\":{}_gen_fail_src\".format(name)],\n                \"//conditions:default\": [],\n            }),\n            hdrs = select({\n                \"@kroma_network_tachyon//:linux_x86_64\": [\":{}_gen_fail_hdr\".format(name)],\n                \"@kroma_network_tachyon//:macos_x86_64\": [\":{}_gen_fail_hdr\".format(name)],\n                \"//conditions:default\": [],\n            }),\n            deps = [\"//tachyon/base:logging\"],\n        )\n\n        tachyon_cc_library(\n            name = \"{}_ffiasm_impl\".format(name),\n            hdrs = [\n                \":{}_gen_hdr\".format(name),\n            ] + select({\n                \"@kroma_network_tachyon//:linux_x86_64\": [\":{}_gen_prime_field_x86_hdr\".format(name)],\n                \"@kroma_network_tachyon//:macos_x86_64\": [\":{}_gen_prime_field_x86_hdr\".format(name)],\n                \"//conditions:default\": [],\n            }),\n            deps = [\n                \":{}_config\".format(name),\n            ] + select({\n                \"@kroma_network_tachyon//:linux_x86_64\": [\n                    \":{}_object\".format(name),\n                    \":{}_fail\".format(name),\n                    \"//tachyon/math/base:byinverter\",\n                    \"//tachyon/math/finite_fields:prime_field_base\",\n                    \"@com_google_absl//absl/base\",\n                ],\n                \"@kroma_network_tachyon//:macos_x86_64\": [\n                    \":{}_object\".format(name),\n                    \":{}_fail\".format(name),\n                    \"//tachyon/math/base:byinverter\",\n                    \"//tachyon/math/finite_fields:prime_field_base\",\n                    \"@com_google_absl//absl/base\",\n                ],\n                \"//conditions:default\": [\"//tachyon/math/finite_fields:prime_field_fallback\"],\n            }),\n            **kwargs\n        )\n\n        tachyon_cc_library(\n            name = \"{}_fallback_impl\".format(name),\n            hdrs = [\":{}_gen_hdr\".format(name)],\n            deps = [\n                \":{}_config\".format(name),\n                \"//tachyon/math/finite_fields:prime_field_fallback\",\n            ],\n            **kwargs\n        )\n\n        tachyon_cc_library(\n            name = name,\n            deps = if_has_asm_prime_field(\n                [\":{}_ffiasm_impl\".format(name)],\n                [\":{}_fallback_impl\".format(name)],\n            ),\n        )\n    else:\n        tachyon_cc_library(\n            name = name,\n            hdrs = [\":{}_gen_hdr\".format(name)],\n            deps = [\n                \":{}_config\".format(name),\n                \"//tachyon/math/finite_fields:prime_field_fallback\",\n            ],\n            **kwargs\n        )\n\n    if is_small_prime_field:\n        prime_field_gpu_dep = small_prime_field_dep\n    else:\n        prime_field_gpu_dep = \"//tachyon/math/finite_fields:prime_field_gpu\"\n\n    tachyon_cc_library(\n        name = \"{}_gpu\".format(name),\n        hdrs = [\":{}_gen_gpu_hdr\".format(name)],\n        deps = [\n            \":{}_config\".format(name),\n            prime_field_gpu_dep,\n        ],\n        **kwargs\n    )\n\ndef _gen_name_out_pairs(name):\n    return [\n        (\"{}_gen_config_hdr\".format(name), \"{}_config.h\".format(name)),\n        (\"{}_gen_config_src\".format(name), \"{}_config.cc\".format(name)),\n        (\"{}_gen_hdr\".format(name), \"{}.h\".format(name)),\n        (\"{}_gen_gpu_hdr\".format(name), \"{}_gpu.h\".format(name)),\n        (\"{}_gen_prime_field_x86_hdr\".format(name), \"{}_prime_field_x86.h\".format(name)),\n        (\"{}_gen_fail_hdr\".format(name), \"{}_fail.h\".format(name)),\n        (\"{}_gen_fail_src\".format(name), \"{}_fail.cc\".format(name)),\n    ]\n\ndef generate_prime_fields(\n        name,\n        namespace,\n        class_name,\n        modulus,\n        reduce32 = \"\",\n        reduce64 = \"\",\n        use_asm = True,\n        use_montgomery = True,\n        **kwargs):\n    for n in _gen_name_out_pairs(name):\n        generate_prime_field(\n            namespace = namespace,\n            class_name = class_name,\n            modulus = modulus,\n            reduce32 = reduce32,\n            reduce64 = reduce64,\n            use_asm = use_asm,\n            use_montgomery = use_montgomery,\n            name = n[0],\n            out = n[1],\n        )\n\n    _do_generate_prime_fields(name, namespace, modulus, use_asm, use_montgomery, **kwargs)\n\ndef generate_fft_prime_fields(\n        name,\n        namespace,\n        class_name,\n        modulus,\n        subgroup_generator,\n        reduce32 = \"\",\n        reduce64 = \"\",\n        use_asm = True,\n        use_montgomery = True,\n        **kwargs):\n    for n in _gen_name_out_pairs(name):\n        generate_fft_prime_field(\n            namespace = namespace,\n            class_name = class_name,\n            modulus = modulus,\n            reduce32 = reduce32,\n            reduce64 = reduce64,\n            use_asm = use_asm,\n            use_montgomery = use_montgomery,\n            subgroup_generator = subgroup_generator,\n            name = n[0],\n            out = n[1],\n        )\n\n    _do_generate_prime_fields(name, namespace, modulus, use_asm, use_montgomery, **kwargs)\n\ndef generate_large_fft_prime_fields(\n        name,\n        namespace,\n        class_name,\n        modulus,\n        small_subgroup_adicity,\n        small_subgroup_base,\n        subgroup_generator,\n        reduce32 = \"\",\n        reduce64 = \"\",\n        use_asm = True,\n        use_montgomery = True,\n        **kwargs):\n    for n in _gen_name_out_pairs(name):\n        generate_large_fft_prime_field(\n            namespace = namespace,\n            class_name = class_name,\n            modulus = modulus,\n            reduce32 = reduce32,\n            reduce64 = reduce64,\n            use_asm = use_asm,\n            use_montgomery = use_montgomery,\n            small_subgroup_adicity = small_subgroup_adicity,\n            small_subgroup_base = small_subgroup_base,\n            subgroup_generator = subgroup_generator,\n            name = n[0],\n            out = n[1],\n        )\n\n    _do_generate_prime_fields(name, namespace, modulus, use_asm, use_montgomery, **kwargs)\n"
  },
  {
    "path": "tachyon/math/finite_fields/generator/prime_field_generator/fail.cc.tpl",
    "content": "// clang-format off\n#include \"tachyon/base/logging.h\"\n\nvoid %{prefix}_fail() { NOTREACHED(); }\n// clang-format on\n"
  },
  {
    "path": "tachyon/math/finite_fields/generator/prime_field_generator/fail.h.tpl",
    "content": "// clang-format off\n\nextern \"C\" void %{prefix}_fail();\n// clang-format on\n"
  },
  {
    "path": "tachyon/math/finite_fields/generator/prime_field_generator/prime_field_config.cc.tpl",
    "content": "// clang-format off\nnamespace %{namespace} {\n\n%{if kHasTwoAdicRootOfUnity}\n// static\nBigInt<%{n}> %{class}Config::kSubgroupGenerator;\n// static\nBigInt<%{n}> %{class}Config::kTwoAdicRootOfUnity;\n%{if kHasLargeSubgroupRootOfUnity}\n// static\nBigInt<%{n}> %{class}Config::kLargeSubgroupRootOfUnity;\n%{endif kHasLargeSubgroupRootOfUnity}\n%{endif kHasTwoAdicRootOfUnity}\n\n}  // namespace %{namespace}\n// clang-format on\n"
  },
  {
    "path": "tachyon/math/finite_fields/generator/prime_field_generator/prime_field_config.h.tpl",
    "content": "// clang-format off\n#include \"absl/base/call_once.h\"\n\n#include \"tachyon/export.h\"\n#include \"tachyon/build/build_config.h\"\n#include \"tachyon/math/base/big_int.h\"\n\nnamespace %{namespace} {\n\nclass TACHYON_EXPORT %{class}Config {\n public:\n  constexpr static const char* kName = \"%{namespace}::%{class}\";\n\n  constexpr static bool kUseMontgomery = %{use_montgomery};\n%{if kUseAsm}\n#if ARCH_CPU_X86_64\n  constexpr static bool kUseAsm = %{use_asm};\n  constexpr static bool %{asm_flag} = true;\n#else\n  constexpr static bool kUseAsm = false;\n#endif\n%{endif kUseAsm}\n%{if !kUseAsm}\n  constexpr static bool kUseAsm = false;\n%{endif !kUseAsm}\n\n  constexpr static size_t kModulusBits = %{modulus_bits};\n  constexpr static BigInt<%{n}> kModulus = BigInt<%{n}>({\n    %{modulus}\n  });\n  constexpr static BigInt<%{n}> kModulusMinusOneDivTwo = BigInt<%{n}>({\n    %{modulus_minus_one_div_two}\n  });\n  constexpr static BigInt<%{n}> kModulusPlusOneDivFour = BigInt<%{n}>({\n    %{modulus_plus_one_div_four}\n  });\n  constexpr static BigInt<%{n}> kTrace = BigInt<%{n}>({\n    %{trace}\n  });\n  constexpr static BigInt<%{n}> kTraceMinusOneDivTwo = BigInt<%{n}>({\n    %{trace_minus_one_div_two}\n  });\n  constexpr static bool kModulusModFourIsThree = %{modulus_mod_four_is_three};\n  constexpr static bool kModulusModSixIsOne = %{modulus_mod_six_is_one};\n  constexpr static bool kModulusHasSpareBit = %{modulus_has_spare_bit};\n  constexpr static bool kCanUseNoCarryMulOptimization = %{can_use_no_carry_mul_optimization};\n  constexpr static BigInt<%{n}> kMontgomeryR = BigInt<%{n}>({\n    %{r}\n  });\n  constexpr static BigInt<%{n}> kMontgomeryR2 = BigInt<%{n}>({\n    %{r2}\n  });\n  constexpr static BigInt<%{n}> kMontgomeryR3 = BigInt<%{n}>({\n    %{r3}\n  });\n  constexpr static uint64_t kInverse64 = UINT64_C(%{inverse64});\n  constexpr static uint32_t kInverse32 = %{inverse32};\n\n  constexpr static BigInt<%{n}> kOne = BigInt<%{n}>({\n    %{one}\n  });\n\n  constexpr static BigInt<%{n}> kMinusOne = BigInt<%{n}>({\n    %{minus_one}\n  });\n\n  constexpr static BigInt<%{n}> kTwoInv = BigInt<%{n}>({\n    %{two_inv}\n  });\n  \n  constexpr static bool kHasTwoAdicRootOfUnity = %{has_two_adic_root_of_unity};\n\n  constexpr static bool kHasLargeSubgroupRootOfUnity = %{has_large_subgroup_root_of_unity};\n\n%{if kHasTwoAdicRootOfUnity}\n  static BigInt<%{n}> kSubgroupGenerator;\n  constexpr static uint32_t kTwoAdicity = %{two_adicity};\n  static BigInt<%{n}> kTwoAdicRootOfUnity;\n\n%{if kHasLargeSubgroupRootOfUnity}\n  constexpr static uint32_t kSmallSubgroupBase = %{small_subgroup_base};\n  constexpr static uint32_t kSmallSubgroupAdicity = %{small_subgroup_adicity};\n  static BigInt<%{n}> kLargeSubgroupRootOfUnity;\n%{endif kHasLargeSubgroupRootOfUnity}\n%{endif kHasTwoAdicRootOfUnity}\n\n  static void Init() {\n    static absl::once_flag once;\n    absl::call_once(once, &%{class}Config::DoInit);\n  }\n\n private:\n  static void DoInit() {\n%{if kHasTwoAdicRootOfUnity}\n    kSubgroupGenerator = BigInt<%{n}>({\n      %{subgroup_generator}\n    });\n    kTwoAdicRootOfUnity = BigInt<%{n}>({\n      %{two_adic_root_of_unity}\n    });\n%{if kHasLargeSubgroupRootOfUnity}\n    kLargeSubgroupRootOfUnity = BigInt<%{n}>({\n      %{large_subgroup_root_of_unity}\n    });\n%{endif kHasLargeSubgroupRootOfUnity}\n%{endif kHasTwoAdicRootOfUnity}\n  }\n};\n\n}  // namespace %{namespace}\n// clang-format on\n"
  },
  {
    "path": "tachyon/math/finite_fields/generator/prime_field_generator/prime_field_cpu.h.tpl",
    "content": "// clang-format off\n#include \"%{config_header_path}\"\n\n%{if kUseAsm}\n#if ARCH_CPU_X86_64\n#include \"%{prime_field_x86_hdr}\"\n#else\n%{endif kUseAsm}\n#include \"tachyon/math/finite_fields/prime_field_fallback.h\"\n%{if kUseAsm}\n#endif\n%{endif kUseAsm}\n\nnamespace %{namespace} {\n\nusing %{class} = PrimeField<%{class}Config>;\n\n}  // namespace %{namespace}\n// clang-format on\n"
  },
  {
    "path": "tachyon/math/finite_fields/generator/prime_field_generator/prime_field_generator.cc",
    "content": "#include <string>\n\n#include \"absl/container/flat_hash_map.h\"\n#include \"absl/strings/str_replace.h\"\n\n#include \"tachyon/base/console/iostream.h\"\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/files/file_path_flag.h\"\n#include \"tachyon/base/files/file_util.h\"\n#include \"tachyon/base/flag/flag_parser.h\"\n#include \"tachyon/base/strings/string_util.h\"\n#include \"tachyon/build/cc_writer.h\"\n#include \"tachyon/math/base/big_int.h\"\n#include \"tachyon/math/base/bit_iterator.h\"\n#include \"tachyon/math/base/gmp/bit_traits.h\"\n#include \"tachyon/math/finite_fields/generator/generator_util.h\"\n#include \"tachyon/math/finite_fields/prime_field_util.h\"\n\nnamespace tachyon {\n\nstd::string CreateAsmFlagName(std::string_view ns_name,\n                              std::string_view class_name) {\n  std::vector<std::string> ns_components = absl::StrSplit(ns_name, \"::\");\n  for (std::string& ns_component : ns_components) {\n    ns_component[0] = absl::ascii_toupper(ns_component[0]);\n  }\n  std::string camel_ns_name = absl::StrJoin(ns_components, \"\");\n  return absl::Substitute(\"kIs$0$1\", camel_ns_name, class_name);\n}\n\nsize_t GetNumBits(const mpz_class& m) {\n  auto it = math::BitIteratorBE<mpz_class>::begin(&m, true);\n  auto end = math::BitIteratorBE<mpz_class>::end(&m);\n  size_t num_bits = 0;\n  while (it != end) {\n    ++it;\n    ++num_bits;\n  }\n  return num_bits;\n}\n\nstruct ModulusInfo {\n  bool modulus_has_spare_bit;\n  bool can_use_no_carry_mul_optimization;\n  mpz_class r;\n  mpz_class r2;\n  mpz_class r3;\n  uint64_t inverse64;\n  uint32_t inverse32;\n\n  template <size_t N>\n  static ModulusInfo From(const mpz_class& m_in) {\n    math::BigInt<N> m(0);\n    math::gmp::CopyLimbs(m_in, m.limbs);\n\n    ModulusInfo ret;\n    if constexpr (N == 1) {\n      // NOLINTNEXTLINE(runtime/int)\n      if (m_in < mpz_class(static_cast<unsigned long>(uint64_t{1} << 32))) {\n        ret.inverse32 = -math::Modulus<N>::template Inverse<uint32_t>(m);\n        return ret;\n      }\n    }\n    ret.modulus_has_spare_bit = math::Modulus<N>::HasSpareBit(m);\n    ret.can_use_no_carry_mul_optimization =\n        math::Modulus<N>::CanUseNoCarryMulOptimization(m);\n    math::BigInt<N> r = math::Modulus<N>::MontgomeryR(m);\n    math::gmp::WriteLimbs(r.limbs, N, &ret.r);\n    math::BigInt<N> r2 = math::Modulus<N>::MontgomeryR2(m);\n    math::gmp::WriteLimbs(r2.limbs, N, &ret.r2);\n    math::BigInt<N> r3 = math::Modulus<N>::MontgomeryR3(m);\n    math::gmp::WriteLimbs(r3.limbs, N, &ret.r3);\n    ret.inverse64 = math::Modulus<N>::template Inverse<uint64_t>(m);\n    ret.inverse32 = math::Modulus<N>::template Inverse<uint32_t>(m);\n    return ret;\n  }\n\n  static ModulusInfo From(const mpz_class& m) {\n    size_t limb_size = math::gmp::GetLimbSize(m);\n    switch (limb_size) {\n      case 1:\n        return From<1>(m);\n      case 2:\n        return From<2>(m);\n      case 3:\n        return From<3>(m);\n      case 4:\n        return From<4>(m);\n      case 5:\n        return From<5>(m);\n      case 6:\n        return From<6>(m);\n      case 7:\n        return From<7>(m);\n      case 8:\n        return From<8>(m);\n      case 9:\n        return From<9>(m);\n    }\n    NOTREACHED();\n    return {};\n  }\n};\n\nstruct GenerationConfig : public build::CcWriter {\n  base::FilePath config_hdr_tpl_path;\n  base::FilePath config_src_tpl_path;\n  base::FilePath small_config_hdr_tpl_path;\n  base::FilePath small_config_src_tpl_path;\n  base::FilePath cpu_hdr_tpl_path;\n  base::FilePath small_cpu_hdr_tpl_path;\n  base::FilePath fail_src_tpl_path;\n  base::FilePath fail_hdr_tpl_path;\n  base::FilePath gpu_hdr_tpl_path;\n  base::FilePath small_gpu_hdr_tpl_path;\n  base::FilePath x86_hdr_tpl_path;\n\n  std::string ns_name;\n  std::string class_name;\n  std::string modulus;\n  std::string reduce32;\n  std::string reduce64;\n  bool use_asm = false;\n  bool use_montgomery = false;\n  std::string subgroup_generator;\n  std::string small_subgroup_base;\n  std::string small_subgroup_adicity;\n\n  int GenerateFailHdr() const;\n  int GenerateFailSrc() const;\n  int GeneratePrimeFieldX86Hdr() const;\n  int GenerateConfigHdr() const;\n  int GenerateConfigSrc() const;\n  int GenerateCpuHdr() const;\n  int GenerateGpuHdr() const;\n\n  std::string GetPrefix() const {\n    return absl::Substitute(\"$0_$1\",\n                            absl::StrReplaceAll(ns_name, {{\"::\", \"_\"}}),\n                            base::ToLowerASCII(class_name));\n  }\n};\n\nint GenerationConfig::GenerateFailHdr() const {\n  std::string tpl_content;\n  CHECK(base::ReadFileToString(fail_hdr_tpl_path, &tpl_content));\n\n  std::string content =\n      absl::StrReplaceAll(tpl_content, {{\"%{prefix}\", GetPrefix()}});\n  return WriteHdr(content, false);\n}\n\nint GenerationConfig::GenerateFailSrc() const {\n  std::string tpl_content;\n  CHECK(base::ReadFileToString(fail_src_tpl_path, &tpl_content));\n\n  std::string content =\n      absl::StrReplaceAll(tpl_content, {{\"%{prefix}\", GetPrefix()}});\n  return WriteSrc(content);\n}\n\nint GenerationConfig::GeneratePrimeFieldX86Hdr() const {\n  std::string tpl_content;\n  CHECK(base::ReadFileToString(x86_hdr_tpl_path, &tpl_content));\n\n  mpz_class m = math::gmp::FromDecString(modulus);\n  size_t n = math::gmp::GetLimbSize(m);\n\n  std::string content = absl::StrReplaceAll(\n      tpl_content, {\n                       {\"%{prefix}\", GetPrefix()},\n                       {\"%{n}\", base::NumberToString(n)},\n                       {\"%{asm_flag}\", CreateAsmFlagName(ns_name, class_name)},\n                   });\n  return WriteHdr(content, false);\n}\n\nint GenerationConfig::GenerateConfigHdr() const {\n  absl::flat_hash_map<std::string, std::string> replacements = {\n      {\"%{namespace}\", ns_name},\n      {\"%{class}\", class_name},\n      {\"%{asm_flag}\", CreateAsmFlagName(ns_name, class_name)},\n      {\"%{use_asm}\", base::BoolToString(use_asm)},\n  };\n\n  mpz_class m = math::gmp::FromDecString(modulus);\n  replacements[\"%{modulus}\"] = math::MpzClassToString(m);\n  replacements[\"%{modulus_minus_one_div_two}\"] =\n      math::MpzClassToString((m - mpz_class(1)) / mpz_class(2));\n  replacements[\"%{modulus_plus_one_div_four}\"] =\n      math::MpzClassToString((m + mpz_class(1)) / mpz_class(4));\n  replacements[\"%{modulus_mod_four_is_three}\"] =\n      base::BoolToString(m % mpz_class(4) == mpz_class(3));\n  replacements[\"%{modulus_mod_six_is_one}\"] =\n      base::BoolToString(m % mpz_class(6) == mpz_class(1));\n\n  mpz_class trace = math::ComputeTrace(2, m - mpz_class(1));\n  replacements[\"%{trace}\"] = math::MpzClassToString(trace);\n  replacements[\"%{trace_minus_one_div_two}\"] =\n      math::MpzClassToString((trace - mpz_class(1)) / mpz_class(2));\n\n  size_t num_bits = GetNumBits(m);\n  replacements[\"%{n}\"] = base::NumberToString(math::gmp::GetLimbSize(m));\n  replacements[\"%{modulus_bits}\"] = base::NumberToString(num_bits);\n\n  ModulusInfo modulus_info = ModulusInfo::From(m);\n  replacements[\"%{modulus_has_spare_bit}\"] =\n      base::BoolToString(modulus_info.modulus_has_spare_bit);\n  replacements[\"%{can_use_no_carry_mul_optimization}\"] =\n      base::BoolToString(modulus_info.can_use_no_carry_mul_optimization);\n  replacements[\"%{r}\"] = math::MpzClassToString(modulus_info.r);\n  replacements[\"%{r2}\"] = math::MpzClassToString(modulus_info.r2);\n  replacements[\"%{r3}\"] = math::MpzClassToString(modulus_info.r3);\n  replacements[\"%{inverse64}\"] = base::NumberToString(modulus_info.inverse64);\n  replacements[\"%{inverse32}\"] = base::NumberToString(modulus_info.inverse32);\n\n  replacements[\"%{use_montgomery}\"] = base::BoolToString(use_montgomery);\n  mpz_class two(2);\n  mpz_class two_inv;\n  mpz_invert(two_inv.get_mpz_t(), two.get_mpz_t(), m.get_mpz_t());\n  if (use_montgomery) {\n    replacements[\"%{one}\"] = math::MpzClassToMontString(mpz_class(1), m);\n    replacements[\"%{minus_one}\"] =\n        math::MpzClassToMontString(m - mpz_class(1), m);\n    replacements[\"%{two_inv}\"] = math::MpzClassToMontString(two_inv, m);\n  } else {\n    replacements[\"%{one}\"] = \"1\";\n    replacements[\"%{minus_one}\"] = math::MpzClassToString(m - mpz_class(1));\n    replacements[\"%{two_inv}\"] = math::MpzClassToString(two_inv);\n  }\n\n  std::string tpl_content;\n  bool is_small_field = num_bits <= 32;\n  if (is_small_field) {\n    CHECK(base::ReadFileToString(small_config_hdr_tpl_path, &tpl_content));\n  } else {\n    CHECK(base::ReadFileToString(config_hdr_tpl_path, &tpl_content));\n  }\n  std::vector<std::string> tpl_lines = absl::StrSplit(tpl_content, \"\\n\");\n\n  RemoveOptionalLines(tpl_lines, \"kUseMontgomery\", use_montgomery);\n  RemoveOptionalLines(tpl_lines, \"!kUseMontgomery\", !use_montgomery);\n\n  if (is_small_field) {\n    if (reduce32.empty()) {\n      // clang-format off\n      replacements[\"%{reduce32}\"] = \"return v >= kModulus? v - kModulus : v;\";\n      // clang-format on\n    } else {\n      replacements[\"%{reduce32}\"] = reduce32;\n    }\n    if (reduce64.empty()) {\n      // clang-format off\n      replacements[\"%{reduce64}\"] = \"return v >= kModulus? v - kModulus : v;\";\n      // clang-format on\n    } else {\n      replacements[\"%{reduce64}\"] = reduce64;\n    }\n  } else {\n    RemoveOptionalLines(tpl_lines, \"kUseAsm\", use_asm);\n    RemoveOptionalLines(tpl_lines, \"!kUseAsm\", !use_asm);\n  }\n\n  bool has_two_adic_root_of_unity = !subgroup_generator.empty();\n  replacements[\"%{has_two_adic_root_of_unity}\"] =\n      base::BoolToString(has_two_adic_root_of_unity);\n  RemoveOptionalLines(tpl_lines, \"kHasTwoAdicRootOfUnity\",\n                      has_two_adic_root_of_unity);\n  mpz_class subgroup_generator_mpz;\n  if (has_two_adic_root_of_unity) {\n    subgroup_generator_mpz = math::gmp::FromDecString(subgroup_generator);\n    replacements[\"%{subgroup_generator}\"] =\n        math::MpzClassToMontString(subgroup_generator_mpz, m);\n\n    // 1) 2ˢ * t = m - 1,\n    // According to Fermat's Little Theorem, the following equations hold:\n    // 2) g^(m - 1) = 1 (mod m)\n    // 3) g^(2ˢ * t) = 1 (mod m)\n    // 4) gᵗ^(2ˢ) = 1 (mod m)\n    // Where subgroup_generator = g, two_adicity = s, trace = t and\n    // two_adic_root_of_unity = gᵗ.\n    mpz_class two_adic_root_of_unity;\n    mpz_powm(two_adic_root_of_unity.get_mpz_t(),\n             subgroup_generator_mpz.get_mpz_t(), trace.get_mpz_t(),\n             m.get_mpz_t());\n    replacements[\"%{two_adicity}\"] =\n        base::NumberToString(math::ComputeAdicity(2, m - mpz_class(1)));\n    replacements[\"%{two_adic_root_of_unity}\"] =\n        math::MpzClassToMontString(two_adic_root_of_unity, m);\n  }\n\n  bool has_large_subgroup_root_of_unity = !small_subgroup_base.empty();\n  replacements[\"%{has_large_subgroup_root_of_unity}\"] =\n      base::BoolToString(has_large_subgroup_root_of_unity);\n  RemoveOptionalLines(tpl_lines, \"kHasLargeSubgroupRootOfUnity\",\n                      has_large_subgroup_root_of_unity);\n  if (has_two_adic_root_of_unity && has_large_subgroup_root_of_unity) {\n    // 5) gᵗ^(2ˢ) = 1 (mod m)\n    // 6) g^(t / bᵃ)^(2ˢ * bᵃ) = 1 (mod m)\n    // Where small_subgroup_base = b, small_subgroup_adicity = a,\n    // remaining_subgroup_size = t / bᵃ\n    // and large_subgroup_root_of_unity = g^(t / bᵃ).\n    mpz_class small_subgroup_base_pow_adicity;\n    mpz_powm(small_subgroup_base_pow_adicity.get_mpz_t(),\n             math::gmp::FromDecString(small_subgroup_base).get_mpz_t(),\n             math::gmp::FromDecString(small_subgroup_adicity).get_mpz_t(),\n             m.get_mpz_t());\n    mpz_class large_subgroup_root_of_unity;\n    {\n      mpz_class remaining_subgroup_size;\n      mpz_div(remaining_subgroup_size.get_mpz_t(), trace.get_mpz_t(),\n              small_subgroup_base_pow_adicity.get_mpz_t());\n      mpz_powm(large_subgroup_root_of_unity.get_mpz_t(),\n               subgroup_generator_mpz.get_mpz_t(),\n               remaining_subgroup_size.get_mpz_t(), m.get_mpz_t());\n    }\n\n    replacements[\"%{small_subgroup_base}\"] = small_subgroup_base;\n    replacements[\"%{small_subgroup_adicity}\"] = small_subgroup_adicity;\n    replacements[\"%{large_subgroup_root_of_unity}\"] =\n        math::MpzClassToMontString(large_subgroup_root_of_unity, m);\n  }\n\n  tpl_content = absl::StrJoin(tpl_lines, \"\\n\");\n  std::string content =\n      absl::StrReplaceAll(tpl_content, std::move(replacements));\n\n  return WriteHdr(content, false);\n}\n\nint GenerationConfig::GenerateConfigSrc() const {\n  mpz_class m = math::gmp::FromDecString(modulus);\n  size_t n = math::gmp::GetLimbSize(m);\n  size_t num_bits = GetNumBits(m);\n\n  std::string tpl_content;\n  bool is_small_field = num_bits <= 32;\n  if (is_small_field) {\n    CHECK(base::ReadFileToString(small_config_src_tpl_path, &tpl_content));\n  } else {\n    CHECK(base::ReadFileToString(config_src_tpl_path, &tpl_content));\n  }\n  std::vector<std::string> tpl_lines = absl::StrSplit(tpl_content, \"\\n\");\n\n  bool has_two_adic_root_of_unity = !subgroup_generator.empty();\n  RemoveOptionalLines(tpl_lines, \"kHasTwoAdicRootOfUnity\",\n                      has_two_adic_root_of_unity);\n  bool has_large_subgroup_root_of_unity = !small_subgroup_base.empty();\n  RemoveOptionalLines(tpl_lines, \"kHasLargeSubgroupRootOfUnity\",\n                      has_large_subgroup_root_of_unity);\n\n  tpl_content = absl::StrJoin(tpl_lines, \"\\n\");\n  std::string content =\n      absl::StrReplaceAll(tpl_content, {\n                                           {\"%{namespace}\", ns_name},\n                                           {\"%{class}\", class_name},\n                                           {\"%{n}\", base::NumberToString(n)},\n                                       });\n  return WriteSrc(content);\n}\n\nint GenerationConfig::GenerateCpuHdr() const {\n  mpz_class m = math::gmp::FromDecString(modulus);\n  size_t num_bits = GetNumBits(m);\n  bool is_small_field = num_bits <= 32;\n\n  std::string tpl_content;\n  if (is_small_field) {\n    CHECK(base::ReadFileToString(small_cpu_hdr_tpl_path, &tpl_content));\n  } else {\n    CHECK(base::ReadFileToString(cpu_hdr_tpl_path, &tpl_content));\n  }\n\n  base::FilePath hdr_path = GetHdrPath();\n  base::FilePath basename = hdr_path.BaseName().RemoveExtension();\n  base::FilePath config_header_path =\n      hdr_path.DirName().Append(basename.value() + \"_config.h\");\n  base::FilePath prime_field_x86_hdr_path =\n      hdr_path.DirName().Append(basename.value() + \"_prime_field_x86.h\");\n\n  absl::flat_hash_map<std::string, std::string> replacements = {\n      {\"%{config_header_path}\", config_header_path.value()},\n      {\"%{prime_field_x86_hdr}\", prime_field_x86_hdr_path.value()},\n      {\"%{namespace}\", ns_name},\n      {\"%{class}\", class_name},\n  };\n\n  std::vector<std::string> tpl_lines = absl::StrSplit(tpl_content, \"\\n\");\n\n  RemoveOptionalLines(tpl_lines, \"kUseMontgomery\", use_montgomery);\n  RemoveOptionalLines(tpl_lines, \"!kUseMontgomery\", !use_montgomery);\n  if (!is_small_field) RemoveOptionalLines(tpl_lines, \"kUseAsm\", use_asm);\n\n  tpl_content = absl::StrJoin(tpl_lines, \"\\n\");\n\n  std::string content =\n      absl::StrReplaceAll(tpl_content, std::move(replacements));\n\n  return WriteHdr(content, false);\n}\n\nint GenerationConfig::GenerateGpuHdr() const {\n  mpz_class m = math::gmp::FromDecString(modulus);\n  size_t num_bits = GetNumBits(m);\n  bool is_small_field = num_bits <= 32;\n\n  std::string tpl_content;\n  if (is_small_field) {\n    CHECK(base::ReadFileToString(small_gpu_hdr_tpl_path, &tpl_content));\n  } else {\n    CHECK(base::ReadFileToString(gpu_hdr_tpl_path, &tpl_content));\n  }\n\n  absl::flat_hash_map<std::string, std::string> replacements = {\n      {\"%{config_header_path}\", math::ConvertToConfigHdr(GetHdrPath()).value()},\n      {\"%{namespace}\", ns_name},\n      {\"%{class}\", class_name},\n  };\n\n  std::vector<std::string> tpl_lines = absl::StrSplit(tpl_content, \"\\n\");\n\n  RemoveOptionalLines(tpl_lines, \"kUseMontgomery\", use_montgomery);\n  RemoveOptionalLines(tpl_lines, \"!kUseMontgomery\", !use_montgomery);\n\n  tpl_content = absl::StrJoin(tpl_lines, \"\\n\");\n\n  std::string content =\n      absl::StrReplaceAll(tpl_content, std::move(replacements));\n\n  return WriteHdr(content, false);\n}\n\nint RealMain(int argc, char** argv) {\n  GenerationConfig config;\n  config.generator = \"//tachyon/math/finite_fields/prime_field_field_generator\";\n\n  base::FlagParser parser;\n  parser.AddFlag<base::FilePathFlag>(&config.out)\n      .set_long_name(\"--out\")\n      .set_help(\"path to output\");\n  parser.AddFlag<base::StringFlag>(&config.ns_name)\n      .set_long_name(\"--namespace\")\n      .set_required();\n  parser.AddFlag<base::StringFlag>(&config.class_name).set_long_name(\"--class\");\n  parser.AddFlag<base::StringFlag>(&config.modulus)\n      .set_long_name(\"--modulus\")\n      .set_required();\n  parser.AddFlag<base::StringFlag>(&config.reduce32)\n      .set_long_name(\"--reduce32\");\n  parser.AddFlag<base::StringFlag>(&config.reduce64)\n      .set_long_name(\"--reduce64\");\n  parser.AddFlag<base::BoolFlag>(&config.use_asm).set_long_name(\"--use_asm\");\n  parser.AddFlag<base::BoolFlag>(&config.use_montgomery)\n      .set_long_name(\"--use_montgomery\");\n  parser.AddFlag<base::FilePathFlag>(&config.x86_hdr_tpl_path)\n      .set_long_name(\"--x86_hdr_tpl_path\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.fail_hdr_tpl_path)\n      .set_long_name(\"--fail_hdr_tpl_path\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.fail_src_tpl_path)\n      .set_long_name(\"--fail_src_tpl_path\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.config_hdr_tpl_path)\n      .set_long_name(\"--config_hdr_tpl_path\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.config_src_tpl_path)\n      .set_long_name(\"--config_src_tpl_path\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.small_config_hdr_tpl_path)\n      .set_long_name(\"--small_config_hdr_tpl_path\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.small_config_src_tpl_path)\n      .set_long_name(\"--small_config_src_tpl_path\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.cpu_hdr_tpl_path)\n      .set_long_name(\"--cpu_hdr_tpl_path\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.small_cpu_hdr_tpl_path)\n      .set_long_name(\"--small_cpu_hdr_tpl_path\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.gpu_hdr_tpl_path)\n      .set_long_name(\"--gpu_hdr_tpl_path\")\n      .set_required();\n  parser.AddFlag<base::FilePathFlag>(&config.small_gpu_hdr_tpl_path)\n      .set_long_name(\"--small_gpu_hdr_tpl_path\")\n      .set_required();\n  parser.AddFlag<base::StringFlag>(&config.subgroup_generator)\n      .set_long_name(\"--subgroup_generator\");\n  parser.AddFlag<base::StringFlag>(&config.small_subgroup_base)\n      .set_long_name(\"--small_subgroup_base\");\n  parser.AddFlag<base::StringFlag>(&config.small_subgroup_adicity)\n      .set_long_name(\"--small_subgroup_adicity\");\n\n  std::string error;\n  if (!parser.Parse(argc, argv, &error)) {\n    tachyon_cerr << error << std::endl;\n    return 1;\n  }\n\n  if (config.use_montgomery) {\n    if (!config.reduce32.empty()) {\n      tachyon_cerr << \"reduce32 and use_montgomery shouldn't be used together\"\n                   << std::endl;\n      return 1;\n    }\n    if (!config.reduce64.empty()) {\n      tachyon_cerr << \"reduce64 and use_montgomery shouldn't be used together\"\n                   << std::endl;\n      return 1;\n    }\n  }\n\n  if (base::EndsWith(config.out.value(), \"_fail.h\")) {\n    return config.GenerateFailHdr();\n  } else if (base::EndsWith(config.out.value(), \"_fail.cc\")) {\n    return config.GenerateFailSrc();\n  } else if (base::EndsWith(config.out.value(), \"_prime_field_x86.h\")) {\n    return config.GeneratePrimeFieldX86Hdr();\n  } else if (base::EndsWith(config.out.value(), \"_config.h\")) {\n    return config.GenerateConfigHdr();\n  } else if (base::EndsWith(config.out.value(), \"_config.cc\")) {\n    return config.GenerateConfigSrc();\n  } else if (base::EndsWith(config.out.value(), \"_gpu.h\")) {\n    return config.GenerateGpuHdr();\n  } else if (base::EndsWith(config.out.value(), \".h\")) {\n    return config.GenerateCpuHdr();\n  } else {\n    tachyon_cerr << \"suffix not supported:\" << config.out << std::endl;\n    return 1;\n  }\n}\n\n}  // namespace tachyon\n\nint main(int argc, char** argv) { return tachyon::RealMain(argc, argv); }\n"
  },
  {
    "path": "tachyon/math/finite_fields/generator/prime_field_generator/prime_field_gpu.h.tpl",
    "content": "// clang-format off\n#include \"%{config_header_path}\"\n\n#include \"tachyon/math/finite_fields/prime_field_gpu.h\"\n\nnamespace %{namespace} {\n\nusing %{class}Gpu = PrimeFieldGpu<%{class}Config>;\n\n}  // namespace %{namespace}\n// clang-format on\n"
  },
  {
    "path": "tachyon/math/finite_fields/generator/prime_field_generator/prime_field_x86.h.tpl",
    "content": "// clang-format off\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <string>\n#include <optional>\n\n#include \"absl/base/call_once.h\"\n\n#include \"tachyon/math/base/big_int.h\"\n#include \"tachyon/math/base/const_init.h\"\n#include \"tachyon/math/base/gmp/gmp_util.h\"\n#include \"tachyon/math/finite_fields/prime_field_base.h\"\n#include \"tachyon/math/base/byinverter.h\"\n\nextern \"C\" void %{prefix}_rawAdd(uint64_t result[%{n}], const uint64_t a[%{n}], const uint64_t b[%{n}]);\nextern \"C\" void %{prefix}_rawSub(uint64_t result[%{n}], const uint64_t a[%{n}], const uint64_t b[%{n}]);\nextern \"C\" void %{prefix}_rawNeg(uint64_t result[%{n}], const uint64_t a[%{n}]);\nextern \"C\" void %{prefix}_rawMMul(uint64_t result[%{n}], const uint64_t a[%{n}], const uint64_t b[%{n}]);\nextern \"C\" void %{prefix}_rawMSquare(uint64_t result[%{n}], const uint64_t a[%{n}]);\nextern \"C\" void %{prefix}_rawToMontgomery(uint64_t result[%{n}], const uint64_t a[%{n}]);\nextern \"C\" void %{prefix}_rawFromMontgomery(uint64_t result[%{n}], const uint64_t a[%{n}]);\nextern \"C\" int %{prefix}_rawIsEq(const uint64_t a[%{n}], const uint64_t b[%{n}]);\nextern \"C\" int %{prefix}_rawIsZero(const uint64_t v[%{n}]);\n\nnamespace tachyon::math {\n\ntemplate <typename Config>\nclass PrimeFieldGpu;\n\ntemplate <typename _Config>\nclass PrimeField<_Config, std::enable_if_t<_Config::%{asm_flag}>> final\n    : public PrimeFieldBase<PrimeField<_Config>> {\n public:\n  constexpr static size_t kModulusBits = _Config::kModulusBits;\n  constexpr static size_t kLimbNums = (kModulusBits + 63) / 64;\n  constexpr static size_t N = kLimbNums;\n\n  using Config = _Config;\n  using BigIntTy = BigInt<N>;\n  using value_type = BigInt<N>;\n\n  using CpuField = PrimeField<Config>;\n  using GpuField = PrimeFieldGpu<Config>;\n\n  constexpr static BYInverter<N> inverter =\n      BYInverter<N>(Config::kModulus, Config::kMontgomeryR2);\n\n  PrimeField() {}\n  template <typename T,\n            std::enable_if_t<std::is_constructible_v<BigInt<N>, T>>* = nullptr>\n  constexpr explicit PrimeField(T value) : PrimeField(BigInt<N>(value)) {}\n  constexpr explicit PrimeField(const BigInt<N>& value) {\n    DCHECK_LT(value, Config::kModulus);\n    %{prefix}_rawToMontgomery(value_.limbs, value.limbs);\n  }\n  constexpr explicit PrimeField(ZeroInitType zero_init) : value_(0) {}\n  constexpr explicit PrimeField(OneInitType one_init) : value_(Config::kOne) {}\n  constexpr explicit PrimeField(MinusOneInitType minus_one_init) : value_(Config::kMinusOne) {}\n  constexpr explicit PrimeField(TwoInvInitType two_inv_init) : value_(Config::kTwoInv) {}\n\n  constexpr PrimeField(const PrimeField& other) = default;\n  constexpr PrimeField& operator=(const PrimeField& other) = default;\n  constexpr PrimeField(PrimeField&& other) = default;\n  constexpr PrimeField& operator=(PrimeField&& other) = default;\n\n  constexpr static PrimeField Zero() { return PrimeField(kZeroInit); }\n\n  constexpr static PrimeField One() { return PrimeField(kOneInit); }\n\n  constexpr static PrimeField MinusOne() { return PrimeField(kMinusOneInit); }\n\n  constexpr static PrimeField TwoInv() { return PrimeField(kTwoInvInit); }\n\n  static PrimeField Random() {\n    return PrimeField(BigInt<N>::Random(Config::kModulus));\n  }\n\n  constexpr static std::optional<PrimeField> FromDecString(std::string_view str) {\n    std::optional<BigInt<N>> value = BigInt<N>::FromDecString(str);\n    if (!value.has_value()) return std::nullopt;\n    if (value >= Config::kModulus) {\n      LOG(ERROR) << \"value(\" << str << \") is greater than or equal to modulus\";\n      return std::nullopt;\n    }\n    return PrimeField(std::move(value).value());\n  }\n  constexpr static std::optional<PrimeField> FromHexString(std::string_view str) {\n    std::optional<BigInt<N>> value = BigInt<N>::FromHexString(str);\n    if (!value.has_value()) return std::nullopt;\n    if (value >= Config::kModulus) {\n      LOG(ERROR) << \"value(\" << str << \") is greater than or equal to modulus\";\n      return std::nullopt;\n    }\n    return PrimeField(std::move(value).value());\n  }\n\n  constexpr static PrimeField FromBigInt(const BigInt<N>& big_int) {\n    return PrimeField(big_int);\n  }\n\n  constexpr static PrimeField FromMontgomery(const BigInt<N>& big_int) {\n    PrimeField ret{};\n    ret.value_ = big_int;\n    return ret;\n  }\n\n  static PrimeField FromMpzClass(const mpz_class& value) {\n    BigInt<N> big_int(0);\n    gmp::CopyLimbs(value, big_int.limbs);\n    return FromBigInt(big_int);\n  }\n\n  static void Init() {\n    static absl::once_flag once;\n    absl::call_once(once, []() {\n      Config::Init();\n      VLOG(1) << Config::kName << \" initialized\";\n    });\n  }\n\n  const value_type& value() const { return value_; }\n\n  constexpr bool IsZero() const { return %{prefix}_rawIsZero(value_.limbs); }\n\n  constexpr bool IsOne() const {\n    return %{prefix}_rawIsEq(value_.limbs, Config::kOne.limbs);\n  }\n\n  constexpr bool IsMinusOne() const {\n    return %{prefix}_rawIsEq(value_.limbs, Config::kMinusOne.limbs);\n  }\n\n  std::string ToString() const { return ToBigInt().ToString(); }\n  std::string ToHexString(bool pad_zero = false) const {\n    return ToBigInt().ToHexString(pad_zero);\n  }\n\n  mpz_class ToMpzClass() const {\n    mpz_class ret;\n    gmp::WriteLimbs(ToBigInt().limbs, N, &ret);\n    return ret;\n  }\n\n  // TODO(chokobole): Support bigendian.\n  constexpr BigInt<N> ToBigInt() const {\n    BigInt<N> ret;\n    %{prefix}_rawFromMontgomery(ret.limbs, value_.limbs);\n    return ret;\n  }\n\n  constexpr uint64_t& operator[](size_t i) { return value_[i]; }\n  constexpr const uint64_t& operator[](size_t i) const { return value_[i]; }\n\n  constexpr bool operator==(const PrimeField& other) const {\n    return %{prefix}_rawIsEq(value_.limbs, other.value_.limbs);\n  }\n\n  constexpr bool operator!=(const PrimeField& other) const {\n    return !operator==(other);\n  }\n\n  constexpr bool operator<(const PrimeField& other) const {\n    return ToBigInt() < other.ToBigInt();\n  }\n\n  constexpr bool operator>(const PrimeField& other) const {\n    return ToBigInt() > other.ToBigInt();\n  }\n\n  constexpr bool operator<=(const PrimeField& other) const {\n    return ToBigInt() <= other.ToBigInt();\n  }\n\n  constexpr bool operator>=(const PrimeField& other) const {\n    return ToBigInt() >= other.ToBigInt();\n  }\n\n  // AdditiveSemigroup methods\n  constexpr PrimeField Add(const PrimeField& other) const {\n    PrimeField ret{};\n    %{prefix}_rawAdd(ret.value_.limbs, value_.limbs, other.value_.limbs);\n    return ret;\n  }\n\n  constexpr PrimeField& AddInPlace(const PrimeField& other) {\n    %{prefix}_rawAdd(value_.limbs, value_.limbs, other.value_.limbs);\n    return *this;\n  }\n\n  // AdditiveGroup methods\n  constexpr PrimeField Sub(const PrimeField& other) const {\n    PrimeField ret{};\n    %{prefix}_rawSub(ret.value_.limbs, value_.limbs, other.value_.limbs);\n    return ret;\n  }\n\n  constexpr PrimeField& SubInPlace(const PrimeField& other) {\n    %{prefix}_rawSub(value_.limbs, value_.limbs, other.value_.limbs);\n    return *this;\n  }\n\n  constexpr PrimeField Negate() const {\n    PrimeField ret{};\n    %{prefix}_rawNeg(ret.value_.limbs, value_.limbs);\n    return ret;\n  }\n\n  constexpr PrimeField& NegateInPlace() {\n    %{prefix}_rawNeg(value_.limbs, value_.limbs);\n    return *this;\n  }\n\n  // TODO(chokobole): Support bigendian.\n  // MultiplicativeSemigroup methods\n  constexpr PrimeField Mul(const PrimeField& other) const {\n    PrimeField ret{};\n    %{prefix}_rawMMul(ret.value_.limbs, value_.limbs, other.value_.limbs);\n    return ret;\n  }\n\n  constexpr PrimeField& MulInPlace(const PrimeField& other) {\n    %{prefix}_rawMMul(value_.limbs, value_.limbs, other.value_.limbs);\n    return *this;\n  }\n\n  constexpr PrimeField Square() const {\n    PrimeField ret{};\n    %{prefix}_rawMSquare(ret.value_.limbs, value_.limbs);\n    return ret;\n  }\n\n  constexpr PrimeField& SquareInPlace() {\n    %{prefix}_rawMSquare(value_.limbs, value_.limbs);\n    return *this;\n  }\n\n  // MultiplicativeGroup methods\n  constexpr std::optional<PrimeField> Inverse() const {\n    PrimeField ret{};\n    if (inverter.Invert(value_, ret.value_)) {\n      return ret;\n    }\n    LOG_IF_NOT_GPU(ERROR) << \"Inverse of zero attempted\";\n    return std::nullopt;\n  }\n\n  [[nodiscard]] constexpr std::optional<PrimeField*> InverseInPlace() {\n    if (inverter.Invert(value_, value_)) {\n      return this;\n    }\n    LOG_IF_NOT_GPU(ERROR) << \"Inverse of zero attempted\";\n    return std::nullopt;\n  }\n\n private:\n  BigInt<N> value_;\n};\n\n}  // namespace tachyon::math\n\n// clang-format on\n"
  },
  {
    "path": "tachyon/math/finite_fields/generator/prime_field_generator/small_prime_field_config.cc.tpl",
    "content": "// clang-format off\nnamespace %{namespace} {\n\n%{if kHasTwoAdicRootOfUnity}\n// static\nuint32_t %{class}Config::kSubgroupGenerator;\n// static\nuint32_t %{class}Config::kTwoAdicRootOfUnity;\n%{endif kHasTwoAdicRootOfUnity}\n\n}  // namespace %{namespace}\n// clang-format on\n"
  },
  {
    "path": "tachyon/math/finite_fields/generator/prime_field_generator/small_prime_field_config.h.tpl",
    "content": "// clang-format off\n#include <stdint.h>\n\n#include \"absl/base/call_once.h\"\n\n#include \"tachyon/export.h\"\n#include \"tachyon/build/build_config.h\"\n#include \"tachyon/math/base/big_int.h\"\n\nnamespace %{namespace} {\n\nclass TACHYON_EXPORT %{class}Config {\n public:\n  constexpr static const char* kName = \"%{namespace}::%{class}\";\n\n  constexpr static bool kUseMontgomery = %{use_montgomery};\n\n  constexpr static size_t kModulusBits = %{modulus_bits};\n  constexpr static uint32_t kModulus = %{modulus};\n  constexpr static uint32_t kModulusMinusOneDivTwo = %{modulus_minus_one_div_two};\n  constexpr static uint32_t kModulusPlusOneDivFour = %{modulus_plus_one_div_four};\n  constexpr static uint32_t kTrace = %{trace};\n  constexpr static uint32_t kTraceMinusOneDivTwo = %{trace_minus_one_div_two};\n  constexpr static bool kModulusModFourIsThree = %{modulus_mod_four_is_three};\n  constexpr static bool kModulusModSixIsOne = %{modulus_mod_six_is_one};\n  constexpr static bool kModulusHasSpareBit = %{modulus_has_spare_bit};\n  constexpr static uint32_t kInverse32 = %{inverse32};\n\n  constexpr static uint32_t kOne = %{one};\n  constexpr static uint32_t kMinusOne = %{minus_one};\n  constexpr static uint32_t kTwoInv = %{two_inv};\n\n  constexpr static bool kHasTwoAdicRootOfUnity = %{has_two_adic_root_of_unity};\n\n  constexpr static bool kHasLargeSubgroupRootOfUnity = %{has_large_subgroup_root_of_unity};\n\n%{if kHasTwoAdicRootOfUnity}\n  static uint32_t kSubgroupGenerator;\n  constexpr static uint32_t kTwoAdicity = %{two_adicity};\n  static uint32_t kTwoAdicRootOfUnity;\n%{endif kHasTwoAdicRootOfUnity}\n\n  static void Init() {\n    static absl::once_flag once;\n    absl::call_once(once, []() {\n%{if kHasTwoAdicRootOfUnity}\n      kSubgroupGenerator = %{subgroup_generator};\n      kTwoAdicRootOfUnity = %{two_adic_root_of_unity};\n%{endif kHasTwoAdicRootOfUnity}\n    });\n  }\n\n  constexpr static uint32_t AddMod(uint32_t a, uint32_t b) {\n    // NOTE(chokobole): This assumes that the 2m - 2 < 2³², where m is modulus.\n    return Reduce(a + b);\n  }\n\n  constexpr static uint32_t SubMod(uint32_t a, uint32_t b) {\n    // NOTE(chokobole): This assumes that the 2m - 2 < 2³², where m is modulus.\n    return Reduce(a + kModulus - b);\n  }\n\n  constexpr static uint32_t Reduce(uint32_t v) {\n    %{reduce32}\n  }\n\n  constexpr static uint32_t Reduce(uint64_t v) {\n    %{reduce64}\n  }\n\n  constexpr static uint32_t ToMontgomery(uint32_t v) {\n    return (uint64_t{v} << 32) % kModulus;\n  }\n\n  constexpr static uint32_t ToMontgomery(uint64_t v) {\n    return static_cast<uint32_t>(((tachyon::math::BigInt<2>(v) << 32) % tachyon::math::BigInt<2>(kModulus))[0]);\n  }\n\n  constexpr static uint32_t FromMontgomery(uint64_t v) {\n    constexpr uint64_t kMask = (uint64_t{1} << 32) - 1;\n    uint64_t t = (v * kInverse32) & kMask;\n    uint64_t u = t * kModulus;\n    uint32_t ret = (v - u) >> 32;\n    if (v < u) {\n      return ret + kModulus;\n    } else {\n      return ret;\n    }\n  }\n};\n\n}  // namespace %{namespace}\n// clang-format on\n"
  },
  {
    "path": "tachyon/math/finite_fields/generator/prime_field_generator/small_prime_field_cpu.h.tpl",
    "content": "// clang-format off\n#include \"%{config_header_path}\"\n\n%{if kUseMontgomery}\n#include \"tachyon/math/finite_fields/small_prime_field_mont.h\"\n%{endif kUseMontgomery}\n%{if !kUseMontgomery}\n#include \"tachyon/math/finite_fields/small_prime_field.h\"\n%{endif !kUseMontgomery}\n\nnamespace %{namespace} {\n\nusing %{class} = PrimeField<%{class}Config>;\n\n}  // namespace %{namespace}\n// clang-format on\n"
  },
  {
    "path": "tachyon/math/finite_fields/generator/prime_field_generator/small_prime_field_gpu.h.tpl",
    "content": "// clang-format off\n#include \"%{config_header_path}\"\n\n%{if kUseMontgomery}\n#include \"tachyon/math/finite_fields/small_prime_field_mont.h\"\n%{endif kUseMontgomery}\n%{if !kUseMontgomery}\n#include \"tachyon/math/finite_fields/small_prime_field.h\"\n%{endif !kUseMontgomery}\n\nnamespace %{namespace} {\n\nusing %{class}Gpu = PrimeField<%{class}Config>;\n\n}  // namespace %{namespace}\n// clang-format on\n"
  },
  {
    "path": "tachyon/math/finite_fields/goldilocks/BUILD.bazel",
    "content": "load(\"@bazel_skylib//rules:common_settings.bzl\", \"string_flag\")\nload(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\nload(\n    \"//tachyon/math/finite_fields/generator/prime_field_generator:build_defs.bzl\",\n    \"SMALL_SUBGROUP_ADICITY\",\n    \"SMALL_SUBGROUP_BASE\",\n    \"SUBGROUP_GENERATOR\",\n)\n\npackage(default_visibility = [\"//visibility:public\"])\n\nstring_flag(\n    name = SMALL_SUBGROUP_ADICITY,\n    build_setting_default = \"1\",\n)\n\nstring_flag(\n    name = SMALL_SUBGROUP_BASE,\n    build_setting_default = \"3\",\n)\n\nstring_flag(\n    name = SUBGROUP_GENERATOR,\n    build_setting_default = \"7\",\n)\n\ntachyon_cc_library(\n    name = \"goldilocks\",\n    hdrs = [\"goldilocks.h\"],\n    deps = [\n        \"//tachyon/math/finite_fields/goldilocks/internal:goldilocks\",\n        \"//tachyon/math/finite_fields/goldilocks/internal:goldilocks_prime_field_x86_special\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"goldilocks_unittests\",\n    srcs = [\"goldilocks_unittest.cc\"],\n    deps = [\":goldilocks\"],\n)\n"
  },
  {
    "path": "tachyon/math/finite_fields/goldilocks/goldilocks.h",
    "content": "#ifndef TACHYON_MATH_FINITE_FIELDS_GOLDILOCKS_GOLDILOCKS_H_\n#define TACHYON_MATH_FINITE_FIELDS_GOLDILOCKS_GOLDILOCKS_H_\n\n#include \"tachyon/build/build_config.h\"\n\n#if defined(TACHYON_HAS_ASM_PRIME_FIELD) && ARCH_CPU_X86_64\n#include \"tachyon/math/finite_fields/goldilocks/internal/goldilocks_prime_field_x86_special.h\"\n#else\n#include \"tachyon/math/finite_fields/goldilocks/internal/goldilocks.h\"\n#endif\n\nnamespace tachyon::math {\n\nusing Goldilocks = PrimeField<GoldilocksConfig>;\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_GOLDILOCKS_GOLDILOCKS_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/goldilocks/goldilocks_unittest.cc",
    "content": "#include \"tachyon/math/finite_fields/goldilocks/goldilocks.h\"\n\n#include \"absl/numeric/int128.h\"\n#include \"absl/strings/substitute.h\"\n#include \"gtest/gtest.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\nuint64_t RandomForTesting() {\n  return base::Uniform(\n      base::Range<uint64_t>::Until(Goldilocks::Config::kModulus[0]));\n}\n\n}  // namespace\n\nTEST(GoldilocksPrimeFieldTest, AdditiveOperators) {\n  absl::uint128 M(Goldilocks::Config::kModulus[0]);\n\n  absl::uint128 a(RandomForTesting());\n  absl::uint128 b(RandomForTesting());\n  SCOPED_TRACE(absl::Substitute(\"a: $0, b: $1\", static_cast<uint64_t>(a),\n                                static_cast<uint64_t>(b)));\n\n  uint64_t sum = static_cast<uint64_t>((a + b) % M);\n  uint64_t amb = static_cast<uint64_t>((a > b ? a - b : a + M - b) % M);\n  uint64_t bma = static_cast<uint64_t>((b > a ? b - a : b + M - a) % M);\n\n  Goldilocks fa = Goldilocks(static_cast<uint64_t>(a));\n  Goldilocks fb = Goldilocks(static_cast<uint64_t>(b));\n\n  EXPECT_EQ((fa + fb).ToBigInt()[0], sum);\n  EXPECT_EQ((fb + fa).ToBigInt()[0], sum);\n  EXPECT_EQ((fa - fb).ToBigInt()[0], amb);\n  EXPECT_EQ((fb - fa).ToBigInt()[0], bma);\n\n  Goldilocks tmp = fa;\n  tmp += fb;\n  EXPECT_EQ(tmp.ToBigInt()[0], sum);\n  tmp -= fb;\n  EXPECT_EQ(tmp.ToBigInt()[0], a);\n}\n\nTEST(GoldilocksPrimeFieldTest, MultiplicativeOperators) {\n  absl::uint128 M = Goldilocks::Config::kModulus[0];\n\n  absl::uint128 a(RandomForTesting());\n  absl::uint128 b(RandomForTesting());\n  SCOPED_TRACE(absl::Substitute(\"a: $0, b: $1\", static_cast<uint64_t>(a),\n                                static_cast<uint64_t>(b)));\n\n  uint64_t mul = static_cast<uint64_t>((a * b) % M);\n\n  Goldilocks fa = Goldilocks(static_cast<uint64_t>(a));\n  Goldilocks fb = Goldilocks(static_cast<uint64_t>(b));\n\n  EXPECT_EQ((fa * fb).ToBigInt()[0], mul);\n  EXPECT_EQ((fb * fa).ToBigInt()[0], mul);\n\n  Goldilocks tmp = fa;\n  tmp *= fb;\n  EXPECT_EQ((tmp).ToBigInt()[0], mul);\n  if (UNLIKELY(fb.IsZero())) {\n    ASSERT_FALSE(tmp /= fb);\n  } else {\n    ASSERT_TRUE(tmp /= fb);\n  }\n  EXPECT_EQ((tmp).ToBigInt()[0], a);\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/finite_fields/goldilocks/internal/BUILD.bazel",
    "content": "load(\"//bazel:tachyon.bzl\", \"if_has_avx512\", \"if_x86_64\")\nload(\"//bazel:tachyon_cc.bzl\", \"tachyon_asm_prime_field_defines\", \"tachyon_cc_library\")\nload(\"//tachyon/math/finite_fields/generator/ext_field_generator:build_defs.bzl\", \"generate_fp2s\")\nload(\n    \"//tachyon/math/finite_fields/generator/prime_field_generator:build_defs.bzl\",\n    \"SMALL_SUBGROUP_ADICITY\",\n    \"SMALL_SUBGROUP_BASE\",\n    \"SUBGROUP_GENERATOR\",\n    \"generate_large_fft_prime_fields\",\n)\n\npackage(default_visibility = [\"//visibility:public\"])\n\ngenerate_large_fft_prime_fields(\n    name = \"goldilocks\",\n    class_name = \"Goldilocks\",\n    # 2⁶⁴ - 2³² + 1\n    # Hex: 0xffffffff00000001\n    modulus = \"18446744069414584321\",\n    namespace = \"tachyon::math\",\n    small_subgroup_adicity = \"//tachyon/math/finite_fields/goldilocks:\" + SMALL_SUBGROUP_ADICITY,\n    small_subgroup_base = \"//tachyon/math/finite_fields/goldilocks:\" + SMALL_SUBGROUP_BASE,\n    subgroup_generator = \"//tachyon/math/finite_fields/goldilocks:\" + SUBGROUP_GENERATOR,\n    # NOTE(chokobole): Polygon's Goldilocks doesn't use montgomery reduction by default.\n    use_montgomery = if_x86_64(False, True),\n)\n\ngenerate_fp2s(\n    name = \"goldilocks2\",\n    base_field = \"Goldilocks\",\n    base_field_hdr = \"tachyon/math/finite_fields/goldilocks/goldilocks.h\",\n    class_name = \"Goldilocks2\",\n    is_packed = False,\n    namespace = \"tachyon::math\",\n    # See https://github.com/Plonky3/Plonky3/blob/d9ef390/goldilocks/src/extension.rs#L7-L11.\n    non_residue = [\"7\"],\n    deps = [\":goldilocks\"],\n)\n\ntachyon_cc_library(\n    name = \"goldilocks_prime_field_x86_special\",\n    srcs = if_x86_64([\"goldilocks_prime_field_x86_special.cc\"]),\n    hdrs = if_x86_64([\"goldilocks_prime_field_x86_special.h\"]),\n    copts = if_x86_64([\n        \"-mavx2\",\n    ]) + if_has_avx512([\n        \"-mavx512f\",\n    ]),\n    defines = tachyon_asm_prime_field_defines(),\n    deps = if_x86_64([\n        \":goldilocks_config\",\n        \"//tachyon/base:random\",\n        \"//tachyon/base:optional\",\n        \"//tachyon/base/strings:string_number_conversions\",\n        \"//tachyon/base/strings:string_util\",\n        \"//tachyon/math/finite_fields:prime_field_base\",\n        \"@com_google_absl//absl/base\",\n        \"@goldilocks//:base_field\",\n    ]),\n)\n"
  },
  {
    "path": "tachyon/math/finite_fields/goldilocks/internal/goldilocks_prime_field_x86_special.cc",
    "content": "#if defined(TACHYON_HAS_ASM_PRIME_FIELD)\n\n#include \"tachyon/math/finite_fields/goldilocks/internal/goldilocks_prime_field_x86_special.h\"\n\n#include <optional>\n\n#include \"third_party/goldilocks/include/goldilocks_base_field.hpp\"\n\n#include \"tachyon/base/optional.h\"\n#include \"tachyon/base/random.h\"\n#include \"tachyon/base/strings/string_number_conversions.h\"\n#include \"tachyon/base/strings/string_util.h\"\n#include \"tachyon/math/base/gmp/gmp_util.h\"\n\nnamespace tachyon::math {\n\n#define CLASS \\\n  PrimeField<Config, std::enable_if_t<Config::kIsTachyonMathGoldilocks>>\n\n// static\ntemplate <typename Config>\nCLASS CLASS::One() {\n  return PrimeField(::Goldilocks::one().fe);\n}\n\n// static\ntemplate <typename Config>\nCLASS CLASS::MinusOne() {\n#if USE_MONTGOMERY == 1\n  return PrimeField::FromMontgomery(Config::kMinusOne[0]);\n#else\n  return PrimeField(Config::kModulus[0] - 1);\n#endif\n}\n\n// static\ntemplate <typename Config>\nCLASS CLASS::TwoInv() {\n#if USE_MONTGOMERY == 1\n  return PrimeField::FromMontgomery(Config::kTwoInv[0]);\n#else\n  return unwrap(PrimeField(2).Inverse());\n#endif\n}\n\n// static\ntemplate <typename Config>\nCLASS CLASS::Random() {\n  return PrimeField(\n      ::Goldilocks::fromU64(\n          base::Uniform(base::Range<uint64_t>::Until(Config::kModulus[0])))\n          .fe);\n}\n\n// static\ntemplate <typename Config>\nstd::optional<CLASS> CLASS::FromDecString(std::string_view str) {\n  uint64_t value = 0;\n  if (!base::StringToUint64(str, &value)) return std::nullopt;\n  if (value >= Config::kModulus[0]) {\n    LOG(ERROR) << \"value(\" << str << \") is greater than or equal to modulus\";\n    return std::nullopt;\n  }\n  return PrimeField(value);\n}\n\n// static\ntemplate <typename Config>\nstd::optional<CLASS> CLASS::FromHexString(std::string_view str) {\n  uint64_t value = 0;\n  if (!base::HexStringToUint64(str, &value)) return std::nullopt;\n  if (value >= Config::kModulus[0]) {\n    LOG(ERROR) << \"value(\" << str << \") is greater than or equal to modulus\";\n    return std::nullopt;\n  }\n  return PrimeField(value);\n}\n\n// static\ntemplate <typename Config>\nCLASS CLASS::FromBigInt(BigInt<N> big_int) {\n  return PrimeField(big_int[0]);\n}\n\n#if USE_MONTGOMERY == 1\n// static\ntemplate <typename Config>\nCLASS CLASS::FromMontgomery(BigInt<N> big_int) {\n  PrimeField ret;\n  ret.value_ = big_int[0];\n  return ret;\n}\n#endif\n\ntemplate <typename Config>\nbool CLASS::IsZero() const {\n  return ::Goldilocks::isZero(\n      reinterpret_cast<const ::Goldilocks::Element&>(value_));\n}\n\ntemplate <typename Config>\nbool CLASS::IsOne() const {\n  return ::Goldilocks::isOne(\n      reinterpret_cast<const ::Goldilocks::Element&>(value_));\n}\n\ntemplate <typename Config>\nbool CLASS::IsMinusOne() const {\n#if USE_MONTGOMERY == 1\n  return value_ == Config::kMinusOne[0];\n#else\n  return value_ == Config::kModulus[0] - 1;\n#endif\n}\n\ntemplate <typename Config>\nstd::string CLASS::ToString() const {\n  return ::Goldilocks::toString(\n      reinterpret_cast<const ::Goldilocks::Element&>(value_), 10);\n}\n\ntemplate <typename Config>\nstd::string CLASS::ToHexString(bool pad_zero) const {\n  std::string str = ::Goldilocks::toString(\n      reinterpret_cast<const ::Goldilocks::Element&>(value_), 16);\n  if (pad_zero) {\n    str = base::ToHexStringWithLeadingZero(str, 16);\n  }\n  return base::MaybePrepend0x(str);\n}\n\ntemplate <typename Config>\nCLASS CLASS::Add(const PrimeField& other) const {\n  PrimeField ret;\n  ::Goldilocks::add(\n      reinterpret_cast<::Goldilocks::Element&>(ret.value_),\n      reinterpret_cast<const ::Goldilocks::Element&>(value_),\n      reinterpret_cast<const ::Goldilocks::Element&>(other.value_));\n  return ret;\n}\n\ntemplate <typename Config>\nCLASS& CLASS::AddInPlace(const PrimeField& other) {\n  ::Goldilocks::add(\n      reinterpret_cast<::Goldilocks::Element&>(value_),\n      reinterpret_cast<const ::Goldilocks::Element&>(value_),\n      reinterpret_cast<const ::Goldilocks::Element&>(other.value_));\n  return *this;\n}\n\ntemplate <typename Config>\nCLASS CLASS::Sub(const PrimeField& other) const {\n  PrimeField ret;\n  ::Goldilocks::sub(\n      reinterpret_cast<::Goldilocks::Element&>(ret.value_),\n      reinterpret_cast<const ::Goldilocks::Element&>(value_),\n      reinterpret_cast<const ::Goldilocks::Element&>(other.value_));\n  return ret;\n}\n\ntemplate <typename Config>\nCLASS& CLASS::SubInPlace(const PrimeField& other) {\n  ::Goldilocks::sub(\n      reinterpret_cast<::Goldilocks::Element&>(value_),\n      reinterpret_cast<const ::Goldilocks::Element&>(value_),\n      reinterpret_cast<const ::Goldilocks::Element&>(other.value_));\n  return *this;\n}\n\ntemplate <typename Config>\nCLASS CLASS::Negate() const {\n  PrimeField ret;\n  ::Goldilocks::neg(reinterpret_cast<::Goldilocks::Element&>(ret.value_),\n                    reinterpret_cast<const ::Goldilocks::Element&>(value_));\n  return ret;\n}\n\ntemplate <typename Config>\nCLASS& CLASS::NegateInPlace() {\n  ::Goldilocks::neg(reinterpret_cast<::Goldilocks::Element&>(value_),\n                    reinterpret_cast<const ::Goldilocks::Element&>(value_));\n  return *this;\n}\n\ntemplate <typename Config>\nCLASS CLASS::Mul(const PrimeField& other) const {\n  PrimeField ret;\n  ::Goldilocks::mul(\n      reinterpret_cast<::Goldilocks::Element&>(ret.value_),\n      reinterpret_cast<const ::Goldilocks::Element&>(value_),\n      reinterpret_cast<const ::Goldilocks::Element&>(other.value_));\n  return ret;\n}\n\ntemplate <typename Config>\nCLASS& CLASS::MulInPlace(const PrimeField& other) {\n  ::Goldilocks::mul(\n      reinterpret_cast<::Goldilocks::Element&>(value_),\n      reinterpret_cast<const ::Goldilocks::Element&>(value_),\n      reinterpret_cast<const ::Goldilocks::Element&>(other.value_));\n  return *this;\n}\n\ntemplate <typename Config>\nCLASS CLASS::SquareImpl() const {\n  PrimeField ret;\n  ::Goldilocks::square(reinterpret_cast<::Goldilocks::Element&>(ret.value_),\n                       reinterpret_cast<const ::Goldilocks::Element&>(value_));\n  return ret;\n}\n\ntemplate <typename Config>\nCLASS& CLASS::SquareImplInPlace() {\n  ::Goldilocks::square(reinterpret_cast<::Goldilocks::Element&>(value_),\n                       reinterpret_cast<const ::Goldilocks::Element&>(value_));\n  return *this;\n}\n\ntemplate <typename Config>\nstd::optional<CLASS> CLASS::Inverse() const {\n  if (UNLIKELY(IsZero())) {\n    LOG_IF_NOT_GPU(ERROR) << \"Inverse of zero attempted\";\n    return std::nullopt;\n  }\n  PrimeField ret;\n  ::Goldilocks::inv(reinterpret_cast<::Goldilocks::Element&>(ret.value_),\n                    reinterpret_cast<const ::Goldilocks::Element&>(value_));\n  return ret;\n}\n\ntemplate <typename Config>\nstd::optional<CLASS*> CLASS::InverseInPlace() {\n  if (UNLIKELY(IsZero())) {\n    LOG_IF_NOT_GPU(ERROR) << \"Inverse of zero attempted\";\n    return std::nullopt;\n  }\n  ::Goldilocks::inv(reinterpret_cast<::Goldilocks::Element&>(value_),\n                    reinterpret_cast<const ::Goldilocks::Element&>(value_));\n  return this;\n}\n\n#undef CLASS\n\ntemplate class PrimeField<GoldilocksConfig>;\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_HAS_ASM_PRIME_FIELD\n"
  },
  {
    "path": "tachyon/math/finite_fields/goldilocks/internal/goldilocks_prime_field_x86_special.h",
    "content": "#ifndef TACHYON_MATH_FINITE_FIELDS_GOLDILOCKS_INTERNAL_GOLDILOCKS_PRIME_FIELD_X86_SPECIAL_H_\n#define TACHYON_MATH_FINITE_FIELDS_GOLDILOCKS_INTERNAL_GOLDILOCKS_PRIME_FIELD_X86_SPECIAL_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <optional>\n#include <string>\n\n#include \"absl/base/call_once.h\"\n\n#include \"tachyon/math/finite_fields/goldilocks/internal/goldilocks_config.h\"\n#include \"tachyon/math/finite_fields/prime_field_base.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename _Config>\nclass PrimeField<_Config, std::enable_if_t<_Config::kIsTachyonMathGoldilocks>>\n    final : public PrimeFieldBase<PrimeField<_Config>> {\n public:\n#if defined(USE_MONTGOMERY)\n  static_assert(USE_MONTGOMERY == 0);\n#endif  // defined(USE_MONTGOMERY)\n\n  constexpr static size_t kModulusBits = _Config::kModulusBits;\n  constexpr static size_t kLimbNums = (kModulusBits + 63) / 64;\n  constexpr static size_t N = kLimbNums;\n\n  using Config = _Config;\n  using BigIntTy = BigInt<N>;\n  using value_type = BigInt<N>;\n\n  constexpr PrimeField() = default;\n  // NOTE(ashjeong): This constructor was changed to |constexpr| due to the\n  // |constexpr| function |GetPoseidon2InternalDiagonalArray()|. Note that\n  // running with |USE_MONTGOMERY| will now cause errors. Refer to this pr:\n  // https://github.com/kroma-network/tachyon/pull/536.\n  constexpr explicit PrimeField(uint64_t value) : value_(value) {}\n  constexpr explicit PrimeField(BigInt<N> value) : PrimeField(value[0]) {}\n  constexpr PrimeField(const PrimeField& other) = default;\n  constexpr PrimeField& operator=(const PrimeField& other) = default;\n  constexpr PrimeField(PrimeField&& other) = default;\n  constexpr PrimeField& operator=(PrimeField&& other) = default;\n\n  constexpr static PrimeField Zero() { return PrimeField(); }\n  static PrimeField One();\n  static PrimeField MinusOne();\n  static PrimeField TwoInv();\n  static PrimeField Random();\n\n  static std::optional<PrimeField> FromDecString(std::string_view str);\n  static std::optional<PrimeField> FromHexString(std::string_view str);\n  static PrimeField FromBigInt(BigInt<N> big_int);\n#if USE_MONTGOMERY == 1\n  static PrimeField FromMontgomery(BigInt<N> big_int);\n#endif\n\n  static void Init() {\n    static absl::once_flag once;\n    absl::call_once(once, []() {\n      Config::Init();\n      VLOG(1) << Config::kName << \" initialized\";\n    });\n  }\n\n  // NOTE(chokobole): To be consistent with `PrimeField<F>` defined in\n  // prime_field_fallback.h, it returns the value as a `BigInt<N>`.\n  value_type value() const { return value_type(value_); }\n\n  bool IsZero() const;\n  bool IsOne() const;\n  bool IsMinusOne() const;\n\n  std::string ToString() const;\n  std::string ToHexString(bool pad_zero = false) const;\n\n  // TODO(chokobole): Support bigendian.\n  BigInt<N> ToBigInt() const { return BigInt<N>(value_); }\n\n  operator uint64_t() const { return value_; }\n\n  bool operator==(const PrimeField& other) const {\n    return value_ == other.value_;\n  }\n  bool operator!=(const PrimeField& other) const {\n    return value_ != other.value_;\n  }\n  bool operator<(const PrimeField& other) const {\n    return value_ < other.value_;\n  }\n  bool operator>(const PrimeField& other) const {\n    return value_ > other.value_;\n  }\n  bool operator<=(const PrimeField& other) const {\n    return value_ <= other.value_;\n  }\n  bool operator>=(const PrimeField& other) const {\n    return value_ >= other.value_;\n  }\n\n  // AdditiveSemigroup methods\n  PrimeField Add(const PrimeField& other) const;\n  PrimeField& AddInPlace(const PrimeField& other);\n\n  // AdditiveGroup methods\n  PrimeField Sub(const PrimeField& other) const;\n  PrimeField& SubInPlace(const PrimeField& other);\n  PrimeField Negate() const;\n  PrimeField& NegateInPlace();\n\n  // TODO(chokobole): Support bigendian.\n  // MultiplicativeSemigroup methods\n  PrimeField Mul(const PrimeField& other) const;\n  PrimeField& MulInPlace(const PrimeField& other);\n  PrimeField SquareImpl() const;\n  PrimeField& SquareImplInPlace();\n\n  // MultiplicativeGroup methods\n  std::optional<PrimeField> Inverse() const;\n  [[nodiscard]] std::optional<PrimeField*> InverseInPlace();\n\n private:\n  uint64_t value_;\n};\n\nextern template class PrimeField<GoldilocksConfig>;\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_GOLDILOCKS_INTERNAL_GOLDILOCKS_PRIME_FIELD_X86_SPECIAL_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/kernels/BUILD.bazel",
    "content": "load(\"@local_config_cuda//cuda:build_defs.bzl\", \"if_cuda\")\nload(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"carry_chain\",\n    hdrs = [\"carry_chain.h\"],\n    deps = [\":prime_field_ops_internal\"],\n)\n\ntachyon_cc_library(\n    name = \"prime_field_ops\",\n    hdrs = [\"prime_field_ops.cu.h\"],\n    deps = if_cuda([\n        \"@local_config_cuda//cuda:cuda_headers\",\n    ]),\n)\n\ntachyon_cc_library(\n    name = \"prime_field_ops_internal\",\n    hdrs = [\"prime_field_ops_internal.cu.h\"],\n    deps = [\n        \"//tachyon/base:compiler_specific\",\n    ] + if_cuda([\n        \"@local_config_cuda//cuda:cuda_headers\",\n    ]),\n)\n"
  },
  {
    "path": "tachyon/math/finite_fields/kernels/carry_chain.h",
    "content": "#ifndef TACHYON_MATH_FINITE_FIELDS_KERNELS_CARRY_CHAIN_H_\n#define TACHYON_MATH_FINITE_FIELDS_KERNELS_CARRY_CHAIN_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <limits>\n\n#include \"tachyon/math/finite_fields/kernels/prime_field_ops_internal.cu.h\"\n\nnamespace tachyon::math::kernels {\nnamespace u32 {\n\ntemplate <size_t OpsCount = std::numeric_limits<size_t>::max(),\n          bool CarryIn = false, bool CarryOut = false>\nstruct CarryChain {\n  size_t index = 0;\n\n  __device__ uint32_t Add(uint32_t x, uint32_t y) {\n    ++index;\n    if (index == 1 && OpsCount == 1 && !CarryIn && !CarryOut) {\n      return ptx::u32::Add(x, y);\n    } else if (index == 1 && !CarryIn) {\n      return ptx::u32::AddCc(x, y);\n    } else if (index < OpsCount || CarryOut) {\n      return ptx::u32::AddcCc(x, y);\n    } else {\n      return ptx::u32::Addc(x, y);\n    }\n  }\n\n  __device__ uint32_t Sub(uint32_t x, uint32_t y) {\n    ++index;\n    if (index == 1 && OpsCount == 1 && !CarryIn && !CarryOut) {\n      return ptx::u32::Sub(x, y);\n    } else if (index == 1 && !CarryIn) {\n      return ptx::u32::SubCc(x, y);\n    } else if (index < OpsCount || CarryOut) {\n      return ptx::u32::SubcCc(x, y);\n    } else {\n      return ptx::u32::Subc(x, y);\n    }\n  }\n\n  __device__ uint32_t MadLo(uint32_t x, uint32_t y, uint32_t z) {\n    ++index;\n    if (index == 1 && OpsCount == 1 && !CarryIn && !CarryOut) {\n      return ptx::u32::MadLo(x, y, z);\n    } else if (index == 1 && !CarryIn) {\n      return ptx::u32::MadLoCc(x, y, z);\n    } else if (index < OpsCount || CarryOut) {\n      return ptx::u32::MadcLoCc(x, y, z);\n    } else {\n      return ptx::u32::MadcLo(x, y, z);\n    }\n  }\n\n  __device__ uint32_t MadHi(uint32_t x, uint32_t y, uint32_t z) {\n    ++index;\n    if (index == 1 && OpsCount == 1 && !CarryIn && !CarryOut) {\n      return ptx::u32::MadHi(x, y, z);\n    } else if (index == 1 && !CarryIn) {\n      return ptx::u32::MadHiCc(x, y, z);\n    } else if (index < OpsCount || CarryOut) {\n      return ptx::u32::MadcHiCc(x, y, z);\n    } else {\n      return ptx::u32::MadcHi(x, y, z);\n    }\n  }\n};\n\n}  // namespace u32\n\nnamespace u64 {\n\ntemplate <size_t OpsCount = std::numeric_limits<size_t>::max(),\n          bool CarryIn = false, bool CarryOut = false>\nstruct CarryChain {\n  size_t index = 0;\n\n  __device__ uint64_t Add(uint64_t x, uint64_t y) {\n    ++index;\n    if (index == 1 && OpsCount == 1 && !CarryIn && !CarryOut) {\n      return ptx::u64::Add(x, y);\n    } else if (index == 1 && !CarryIn) {\n      return ptx::u64::AddCc(x, y);\n    } else if (index < OpsCount || CarryOut) {\n      return ptx::u64::AddcCc(x, y);\n    } else {\n      return ptx::u64::Addc(x, y);\n    }\n  }\n\n  __device__ uint64_t Sub(uint64_t x, uint64_t y) {\n    ++index;\n    if (index == 1 && OpsCount == 1 && !CarryIn && !CarryOut) {\n      return ptx::u64::Sub(x, y);\n    } else if (index == 1 && !CarryIn) {\n      return ptx::u64::SubCc(x, y);\n    } else if (index < OpsCount || CarryOut) {\n      return ptx::u64::SubcCc(x, y);\n    } else {\n      return ptx::u64::Subc(x, y);\n    }\n  }\n\n  __device__ uint64_t MadLo(uint64_t x, uint64_t y, uint64_t z) {\n    ++index;\n    if (index == 1 && OpsCount == 1 && !CarryIn && !CarryOut) {\n      return ptx::u64::MadLo(x, y, z);\n    } else if (index == 1 && !CarryIn) {\n      return ptx::u64::MadLoCc(x, y, z);\n    } else if (index < OpsCount || CarryOut) {\n      return ptx::u64::MadcLoCc(x, y, z);\n    } else {\n      return ptx::u64::MadcLo(x, y, z);\n    }\n  }\n\n  __device__ uint64_t MadHi(uint64_t x, uint64_t y, uint64_t z) {\n    ++index;\n    if (index == 1 && OpsCount == 1 && !CarryIn && !CarryOut) {\n      return ptx::u64::MadHi(x, y, z);\n    } else if (index == 1 && !CarryIn) {\n      return ptx::u64::MadHiCc(x, y, z);\n    } else if (index < OpsCount || CarryOut) {\n      return ptx::u64::MadcHiCc(x, y, z);\n    } else {\n      return ptx::u64::MadcHi(x, y, z);\n    }\n  }\n};\n\n}  // namespace u64\n}  // namespace tachyon::math::kernels\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_KERNELS_CARRY_CHAIN_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/kernels/prime_field_ops.cu.h",
    "content": "#ifndef TACHYON_MATH_FINITE_FIELDS_KERNELS_PRIME_FIELD_OPS_CU_H_\n#define TACHYON_MATH_FINITE_FIELDS_KERNELS_PRIME_FIELD_OPS_CU_H_\n\n#include <stddef.h>\n\n#include <optional>\n#include <utility>\n\n#include \"third_party/gpus/cuda/include/cuda_runtime.h\"\n\nnamespace tachyon::math::kernels {\n\n#define DEFINE_FIELD_OP(method, operator)                     \\\n  template <typename T>                                       \\\n  __global__ void method(const T* x, const T* y, T* result,   \\\n                         unsigned int count) {                \\\n    unsigned int gid = blockIdx.x * blockDim.x + threadIdx.x; \\\n    if (gid >= count) return;                                 \\\n    result[gid] = x[gid] operator y[gid];                     \\\n  }\n\nDEFINE_FIELD_OP(Add, +)\nDEFINE_FIELD_OP(Sub, -)\nDEFINE_FIELD_OP(Mul, *)\n\ntemplate <typename T>\n__global__ void Div(const T* x, const T* y, T* result, unsigned int count) {\n  unsigned int gid = blockIdx.x * blockDim.x + threadIdx.x;\n  if (gid >= count) return;\n  const std::optional<T> div = x[gid] / y[gid];\n  assert(div);\n  result[gid] = std::move(*div);\n}\n\n#undef DEFINE_FIELD_OP\n\n#define DEFINE_COMPARISON_OP(method, operator)                 \\\n  template <typename T>                                        \\\n  __global__ void method(const T* x, const T* y, bool* result, \\\n                         unsigned int count) {                 \\\n    unsigned int gid = blockIdx.x * blockDim.x + threadIdx.x;  \\\n    if (gid >= count) return;                                  \\\n    result[gid] = x[gid] operator y[gid];                      \\\n  }\n\nDEFINE_COMPARISON_OP(Eq, ==)\nDEFINE_COMPARISON_OP(Ne, !=)\nDEFINE_COMPARISON_OP(Lt, <)\nDEFINE_COMPARISON_OP(Le, <=)\nDEFINE_COMPARISON_OP(Gt, >)\nDEFINE_COMPARISON_OP(Ge, >=)\n\n#undef DEFINE_COMPARISON_OP\n\n}  // namespace tachyon::math::kernels\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_KERNELS_PRIME_FIELD_OPS_CU_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/kernels/prime_field_ops_internal.cu.h",
    "content": "#ifndef TACHYON_MATH_FINITE_FIELDS_KERNELS_PRIME_FIELD_OPS_INTERNAL_CU_H_\n#define TACHYON_MATH_FINITE_FIELDS_KERNELS_PRIME_FIELD_OPS_INTERNAL_CU_H_\n\n#include <stdint.h>\n\n#include \"third_party/gpus/cuda/include/cuda_runtime_api.h\"\n\n#include \"tachyon/base/compiler_specific.h\"\n\nnamespace tachyon::math::ptx {\nnamespace u32 {\n\n__device__ ALWAYS_INLINE uint32_t Add(uint32_t x, uint32_t y) {\n  uint32_t result;\n  asm(\"add.u32 %0, %1, %2;\" : \"=r\"(result) : \"r\"(x), \"r\"(y));\n  return result;\n}\n\n__device__ ALWAYS_INLINE uint32_t AddCc(uint32_t x, uint32_t y) {\n  uint32_t result;\n  asm volatile(\"add.cc.u32 %0, %1, %2;\" : \"=r\"(result) : \"r\"(x), \"r\"(y));\n  return result;\n}\n\n__device__ ALWAYS_INLINE uint32_t Addc(uint32_t x, uint32_t y) {\n  uint32_t result;\n  asm volatile(\"addc.u32 %0, %1, %2;\" : \"=r\"(result) : \"r\"(x), \"r\"(y));\n  return result;\n}\n\n__device__ ALWAYS_INLINE uint32_t AddcCc(uint32_t x, uint32_t y) {\n  uint32_t result;\n  asm volatile(\"addc.cc.u32 %0, %1, %2;\" : \"=r\"(result) : \"r\"(x), \"r\"(y));\n  return result;\n}\n\n__device__ ALWAYS_INLINE uint32_t Sub(uint32_t x, uint32_t y) {\n  uint32_t result;\n  asm(\"sub.u32 %0, %1, %2;\" : \"=r\"(result) : \"r\"(x), \"r\"(y));\n  return result;\n}\n\n__device__ ALWAYS_INLINE uint32_t SubCc(uint32_t x, uint32_t y) {\n  uint32_t result;\n  asm volatile(\"sub.cc.u32 %0, %1, %2;\" : \"=r\"(result) : \"r\"(x), \"r\"(y));\n  return result;\n}\n\n__device__ ALWAYS_INLINE uint32_t Subc(uint32_t x, uint32_t y) {\n  uint32_t result;\n  asm volatile(\"subc.u32 %0, %1, %2;\" : \"=r\"(result) : \"r\"(x), \"r\"(y));\n  return result;\n}\n\n__device__ ALWAYS_INLINE uint32_t SubcCc(uint32_t x, uint32_t y) {\n  uint32_t result;\n  asm volatile(\"subc.cc.u32 %0, %1, %2;\" : \"=r\"(result) : \"r\"(x), \"r\"(y));\n  return result;\n}\n\n__device__ ALWAYS_INLINE uint32_t MulLo(uint32_t x, uint32_t y) {\n  uint32_t result;\n  asm(\"mul.lo.u32 %0, %1, %2;\" : \"=r\"(result) : \"r\"(x), \"r\"(y));\n  return result;\n}\n\n__device__ ALWAYS_INLINE uint32_t MulHi(uint32_t x, uint32_t y) {\n  uint32_t result;\n  asm(\"mul.hi.u32 %0, %1, %2;\" : \"=r\"(result) : \"r\"(x), \"r\"(y));\n  return result;\n}\n\n__device__ ALWAYS_INLINE uint32_t MadLo(uint32_t x, uint32_t y, uint32_t z) {\n  uint32_t result;\n  asm(\"mad.lo.u32 %0, %1, %2, %3;\" : \"=r\"(result) : \"r\"(x), \"r\"(y), \"r\"(z));\n  return result;\n}\n\n__device__ ALWAYS_INLINE uint32_t MadHi(uint32_t x, uint32_t y, uint32_t z) {\n  uint32_t result;\n  asm(\"mad.hi.u32 %0, %1, %2, %3;\" : \"=r\"(result) : \"r\"(x), \"r\"(y), \"r\"(z));\n  return result;\n}\n\n__device__ ALWAYS_INLINE uint32_t MadLoCc(uint32_t x, uint32_t y, uint32_t z) {\n  uint32_t result;\n  asm volatile(\"mad.lo.cc.u32 %0, %1, %2, %3;\"\n               : \"=r\"(result)\n               : \"r\"(x), \"r\"(y), \"r\"(z));\n  return result;\n}\n\n__device__ ALWAYS_INLINE uint32_t MadHiCc(uint32_t x, uint32_t y, uint32_t z) {\n  uint32_t result;\n  asm volatile(\"mad.hi.cc.u32 %0, %1, %2, %3;\"\n               : \"=r\"(result)\n               : \"r\"(x), \"r\"(y), \"r\"(z));\n  return result;\n}\n\n__device__ ALWAYS_INLINE uint32_t MadcLo(uint32_t x, uint32_t y, uint32_t z) {\n  uint32_t result;\n  asm volatile(\"madc.lo.u32 %0, %1, %2, %3;\"\n               : \"=r\"(result)\n               : \"r\"(x), \"r\"(y), \"r\"(z));\n  return result;\n}\n\n__device__ ALWAYS_INLINE uint32_t MadcHi(uint32_t x, uint32_t y, uint32_t z) {\n  uint32_t result;\n  asm volatile(\"madc.hi.u32 %0, %1, %2, %3;\"\n               : \"=r\"(result)\n               : \"r\"(x), \"r\"(y), \"r\"(z));\n  return result;\n}\n\n__device__ ALWAYS_INLINE uint32_t MadcLoCc(uint32_t x, uint32_t y, uint32_t z) {\n  uint32_t result;\n  asm volatile(\"madc.lo.cc.u32 %0, %1, %2, %3;\"\n               : \"=r\"(result)\n               : \"r\"(x), \"r\"(y), \"r\"(z));\n  return result;\n}\n\n__device__ ALWAYS_INLINE uint32_t MadcHiCc(uint32_t x, uint32_t y, uint32_t z) {\n  uint32_t result;\n  asm volatile(\"madc.hi.cc.u32 %0, %1, %2, %3;\"\n               : \"=r\"(result)\n               : \"r\"(x), \"r\"(y), \"r\"(z));\n  return result;\n}\n\n__device__ ALWAYS_INLINE uint64_t MovB64(uint32_t lo, uint32_t hi) {\n  uint64_t result;\n  asm(\"mov.b64 %0, {%1,%2};\" : \"=l\"(result) : \"r\"(lo), \"r\"(hi));\n  return result;\n}\n\n}  // namespace u32\n\nnamespace u64 {\n\n__device__ ALWAYS_INLINE uint64_t Add(uint64_t x, uint64_t y) {\n  uint64_t result;\n  asm(\"add.u64 %0, %1, %2;\" : \"=l\"(result) : \"l\"(x), \"l\"(y));\n  return result;\n}\n\n__device__ ALWAYS_INLINE uint64_t AddCc(uint64_t x, uint64_t y) {\n  uint64_t result;\n  asm volatile(\"add.cc.u64 %0, %1, %2;\" : \"=l\"(result) : \"l\"(x), \"l\"(y));\n  return result;\n}\n\n__device__ ALWAYS_INLINE uint64_t Addc(uint64_t x, uint64_t y) {\n  uint64_t result;\n  asm volatile(\"addc.u64 %0, %1, %2;\" : \"=l\"(result) : \"l\"(x), \"l\"(y));\n  return result;\n}\n\n__device__ ALWAYS_INLINE uint64_t AddcCc(uint64_t x, uint64_t y) {\n  uint64_t result;\n  asm volatile(\"addc.cc.u64 %0, %1, %2;\" : \"=l\"(result) : \"l\"(x), \"l\"(y));\n  return result;\n}\n\n__device__ ALWAYS_INLINE uint64_t Sub(uint64_t x, uint64_t y) {\n  uint64_t result;\n  asm(\"sub.u64 %0, %1, %2;\" : \"=l\"(result) : \"l\"(x), \"l\"(y));\n  return result;\n}\n\n__device__ ALWAYS_INLINE uint64_t SubCc(uint64_t x, uint64_t y) {\n  uint64_t result;\n  asm volatile(\"sub.cc.u64 %0, %1, %2;\" : \"=l\"(result) : \"l\"(x), \"l\"(y));\n  return result;\n}\n\n__device__ ALWAYS_INLINE uint64_t Subc(uint64_t x, uint64_t y) {\n  uint64_t result;\n  asm volatile(\"subc.u64 %0, %1, %2;\" : \"=l\"(result) : \"l\"(x), \"l\"(y));\n  return result;\n}\n\n__device__ ALWAYS_INLINE uint64_t SubcCc(uint64_t x, uint64_t y) {\n  uint64_t result;\n  asm volatile(\"subc.cc.u64 %0, %1, %2;\" : \"=l\"(result) : \"l\"(x), \"l\"(y));\n  return result;\n}\n\n__device__ ALWAYS_INLINE uint64_t MulLo(uint64_t x, uint64_t y) {\n  uint64_t result;\n  asm(\"mul.lo.u64 %0, %1, %2;\" : \"=l\"(result) : \"l\"(x), \"l\"(y));\n  return result;\n}\n\n__device__ ALWAYS_INLINE uint64_t MulHi(uint64_t x, uint64_t y) {\n  uint64_t result;\n  asm(\"mul.hi.u64 %0, %1, %2;\" : \"=l\"(result) : \"l\"(x), \"l\"(y));\n  return result;\n}\n\n__device__ ALWAYS_INLINE uint64_t MadLo(uint64_t x, uint64_t y, uint64_t z) {\n  uint64_t result;\n  asm(\"mad.lo.u64 %0, %1, %2, %3;\" : \"=l\"(result) : \"l\"(x), \"l\"(y), \"l\"(z));\n  return result;\n}\n\n__device__ ALWAYS_INLINE uint64_t MadHi(uint64_t x, uint64_t y, uint64_t z) {\n  uint64_t result;\n  asm(\"mad.hi.u64 %0, %1, %2, %3;\" : \"=l\"(result) : \"l\"(x), \"l\"(y), \"l\"(z));\n  return result;\n}\n\n__device__ ALWAYS_INLINE uint64_t MadLoCc(uint64_t x, uint64_t y, uint64_t z) {\n  uint64_t result;\n  asm volatile(\"mad.lo.cc.u64 %0, %1, %2, %3;\"\n               : \"=l\"(result)\n               : \"l\"(x), \"l\"(y), \"l\"(z));\n  return result;\n}\n\n__device__ ALWAYS_INLINE uint64_t MadHiCc(uint64_t x, uint64_t y, uint64_t z) {\n  uint64_t result;\n  asm volatile(\"mad.hi.cc.u64 %0, %1, %2, %3;\"\n               : \"=l\"(result)\n               : \"l\"(x), \"l\"(y), \"l\"(z));\n  return result;\n}\n\n__device__ ALWAYS_INLINE uint64_t MadcLo(uint64_t x, uint64_t y, uint64_t z) {\n  uint64_t result;\n  asm volatile(\"madc.lo.u64 %0, %1, %2, %3;\"\n               : \"=l\"(result)\n               : \"l\"(x), \"l\"(y), \"l\"(z));\n  return result;\n}\n\n__device__ ALWAYS_INLINE uint64_t MadcHi(uint64_t x, uint64_t y, uint64_t z) {\n  uint64_t result;\n  asm volatile(\"madc.hi.u64 %0, %1, %2, %3;\"\n               : \"=l\"(result)\n               : \"l\"(x), \"l\"(y), \"l\"(z));\n  return result;\n}\n\n__device__ ALWAYS_INLINE uint64_t MadcLoCc(uint64_t x, uint64_t y, uint64_t z) {\n  uint64_t result;\n  asm volatile(\"madc.lo.cc.u64 %0, %1, %2, %3;\"\n               : \"=l\"(result)\n               : \"l\"(x), \"l\"(y), \"l\"(z));\n  return result;\n}\n\n__device__ ALWAYS_INLINE uint64_t MadcHiCc(uint64_t x, uint64_t y, uint64_t z) {\n  uint64_t result;\n  asm volatile(\"madc.hi.cc.u64 %0, %1, %2, %3;\"\n               : \"=l\"(result)\n               : \"l\"(x), \"l\"(y), \"l\"(z));\n  return result;\n}\n\n}  // namespace u64\n\n__device__ ALWAYS_INLINE void BarArrive(const unsigned name,\n                                        const unsigned count) {\n  asm volatile(\"bar.arrive %0, %1;\" : : \"r\"(name), \"r\"(count) : \"memory\");\n}\n\n__device__ ALWAYS_INLINE void BarSync(const unsigned name,\n                                      const unsigned count) {\n  asm volatile(\"bar.sync %0, %1;\" : : \"r\"(name), \"r\"(count) : \"memory\");\n}\n\n}  // namespace tachyon::math::ptx\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_KERNELS_PRIME_FIELD_OPS_INTERNAL_CU_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/koala_bear/BUILD.bazel",
    "content": "load(\"@bazel_skylib//rules:common_settings.bzl\", \"string_flag\")\nload(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\nload(\"//tachyon/math/finite_fields/generator/prime_field_generator:build_defs.bzl\", \"SUBGROUP_GENERATOR\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nstring_flag(\n    name = SUBGROUP_GENERATOR,\n    build_setting_default = \"3\",\n)\n\ntachyon_cc_library(\n    name = \"koala_bear\",\n    hdrs = [\"koala_bear.h\"],\n    deps = [\"//tachyon/math/finite_fields/koala_bear/internal:packed_koala_bear\"],\n)\n\ntachyon_cc_library(\n    name = \"koala_bear4\",\n    hdrs = [\"koala_bear4.h\"],\n    deps = [\n        \"//tachyon/math/finite_fields/koala_bear/internal:koala_bear4\",\n        \"//tachyon/math/finite_fields/koala_bear/internal:packed_koala_bear4\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"packed_koala_bear4\",\n    hdrs = [\"packed_koala_bear4.h\"],\n    deps = [\n        \"//tachyon/math/finite_fields/koala_bear/internal:koala_bear4\",\n        \"//tachyon/math/finite_fields/koala_bear/internal:packed_koala_bear4\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/math/finite_fields/koala_bear/internal/BUILD.bazel",
    "content": "load(\"//bazel:tachyon.bzl\", \"if_aarch64\", \"if_has_avx512\", \"if_x86_64\")\nload(\"//bazel:tachyon_cc.bzl\", \"tachyon_avx512_defines\", \"tachyon_cc_library\")\nload(\"//tachyon/math/finite_fields/generator/ext_field_generator:build_defs.bzl\", \"generate_fp4s\")\nload(\"//tachyon/math/finite_fields/generator/prime_field_generator:build_defs.bzl\", \"SUBGROUP_GENERATOR\", \"generate_fft_prime_fields\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ngenerate_fp4s(\n    name = \"koala_bear4\",\n    base_field = \"KoalaBear\",\n    base_field_degree = 1,\n    base_field_hdr = \"tachyon/math/finite_fields/koala_bear/internal//koala_bear.h\",\n    class_name = \"KoalaBear4\",\n    is_packed = False,\n    namespace = \"tachyon::math\",\n    # See https://github.com/Plonky3/Plonky3/blob/d9ef390/koala-bear/src/koala_bear.rs#L80.\n    non_residue = [\"3\"],\n    deps = [\":koala_bear\"],\n)\n\ngenerate_fp4s(\n    name = \"packed_koala_bear4\",\n    base_field = \"PackedKoalaBear\",\n    base_field_degree = 1,\n    base_field_hdr = \"tachyon/math/finite_fields/koala_bear/internal//packed_koala_bear.h\",\n    class_name = \"PackedKoalaBear4\",\n    is_packed = True,\n    namespace = \"tachyon::math\",\n    # See https://github.com/Plonky3/Plonky3/blob/d9ef390/koala-bear/src/koala_bear.rs#L80.\n    non_residue = [\"3\"],\n    deps = [\":packed_koala_bear\"],\n)\n\ngenerate_fft_prime_fields(\n    name = \"koala_bear\",\n    class_name = \"KoalaBear\",\n    # 2³¹ - 2²⁴ + 1\n    # Hex: 0x7f000001\n    modulus = \"2130706433\",\n    namespace = \"tachyon::math\",\n    subgroup_generator = \"//tachyon/math/finite_fields/koala_bear:\" + SUBGROUP_GENERATOR,\n    use_montgomery = True,\n)\n\ntachyon_cc_library(\n    name = \"packed_koala_bear\",\n    hdrs = [\"packed_koala_bear.h\"],\n    defines = tachyon_avx512_defines(),\n    deps = [\n        \":packed_koala_bear_neon\",\n        \"//tachyon/build:build_config\",\n        \"//tachyon/math/finite_fields:extended_packed_field_traits_forward\",\n        \"//tachyon/math/finite_fields:finite_field_traits\",\n        \"//tachyon/math/matrix:prime_field_num_traits\",\n    ] + if_has_avx512(\n        [\":packed_koala_bear_avx512\"],\n        [\":packed_koala_bear_avx2\"],\n    ),\n)\n\ntachyon_cc_library(\n    name = \"packed_koala_bear_avx2\",\n    srcs = if_x86_64([\"packed_koala_bear_avx2.cc\"]),\n    hdrs = if_x86_64([\"packed_koala_bear_avx2.h\"]),\n    copts = if_x86_64([\"-mavx2\"]),\n    deps = [\n        \":koala_bear\",\n        \"//tachyon:export\",\n        \"//tachyon/math/finite_fields:packed_prime_field32_avx2\",\n        \"//tachyon/math/finite_fields:packed_prime_field_base\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"packed_koala_bear_avx512\",\n    srcs = if_x86_64([\"packed_koala_bear_avx512.cc\"]),\n    hdrs = if_x86_64([\"packed_koala_bear_avx512.h\"]),\n    copts = if_x86_64([\n        \"-mavx512f\",\n        # NOTE(chokobole): See https://gitlab.com/libeigen/eigen/-/blob/0b51f76/Eigen/src/Core/util/ConfigureVectorization.h#L248.\n        \"-mfma\",\n    ]),\n    deps = [\n        \":koala_bear\",\n        \"//tachyon:export\",\n        \"//tachyon/math/finite_fields:packed_prime_field32_avx512\",\n        \"//tachyon/math/finite_fields:packed_prime_field_base\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"packed_koala_bear_neon\",\n    srcs = if_aarch64([\"packed_koala_bear_neon.cc\"]),\n    hdrs = if_aarch64([\"packed_koala_bear_neon.h\"]),\n    deps = [\n        \":koala_bear\",\n        \"//tachyon:export\",\n        \"//tachyon/math/finite_fields:packed_prime_field32_neon\",\n        \"//tachyon/math/finite_fields:packed_prime_field_base\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/math/finite_fields/koala_bear/internal/packed_koala_bear.h",
    "content": "#ifndef TACHYON_MATH_FINITE_FIELDS_KOALA_BEAR_INTERNAL_PACKED_KOALA_BEAR_H_\n#define TACHYON_MATH_FINITE_FIELDS_KOALA_BEAR_INTERNAL_PACKED_KOALA_BEAR_H_\n\n#include \"tachyon/build/build_config.h\"\n\n#if ARCH_CPU_X86_64\n#if defined(TACHYON_HAS_AVX512)\n#include \"tachyon/math/finite_fields/koala_bear/internal/packed_koala_bear_avx512.h\"\n#else\n#include \"tachyon/math/finite_fields/koala_bear/internal/packed_koala_bear_avx2.h\"\n#endif\n#elif ARCH_CPU_ARM64\n#include \"tachyon/math/finite_fields/koala_bear/internal/packed_koala_bear_neon.h\"\n#endif\n#include \"tachyon/math/finite_fields/finite_field_traits.h\"\n#include \"tachyon/math/matrix/prime_field_num_traits.h\"\n\nnamespace tachyon::math {\n\n#if ARCH_CPU_X86_64\n#if defined(TACHYON_HAS_AVX512)\nusing PackedKoalaBear = PackedKoalaBearAVX512;\n#else\nusing PackedKoalaBear = PackedKoalaBearAVX2;\n#endif\n#elif ARCH_CPU_ARM64\nusing PackedKoalaBear = PackedKoalaBearNeon;\n#endif\n\ntemplate <>\nstruct FiniteFieldTraits<PackedKoalaBear> {\n  static constexpr bool kIsPrimeField = true;\n  static constexpr bool kIsPackedPrimeField = true;\n  static constexpr bool kIsExtensionField = false;\n\n  using PrimeField = KoalaBear;\n  using Config = KoalaBear::Config;\n};\n\n}  // namespace tachyon::math\n\nnamespace Eigen {\n\ntemplate <>\nstruct NumTraits<tachyon::math::PackedKoalaBear>\n    : GenericNumTraits<tachyon::math::PackedKoalaBear> {\n  using PrimeField = tachyon::math::KoalaBear;\n  constexpr static size_t N = tachyon::math::PackedKoalaBear::N;\n\n  enum {\n    IsInteger = 1,\n    IsField = 1,\n    IsSigned = 0,\n    IsComplex = 0,\n    RequireInitialization = 0,\n    ReadCost = tachyon::math::CostCalculator<PrimeField>::ComputeReadCost() * N,\n    AddCost = tachyon::math::CostCalculator<PrimeField>::ComputeAddCost() * N,\n    MulCost = tachyon::math::CostCalculator<PrimeField>::ComputeMulCost() * N,\n  };\n};\n\n}  // namespace Eigen\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_KOALA_BEAR_INTERNAL_PACKED_KOALA_BEAR_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/koala_bear/internal/packed_koala_bear_avx2.cc",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#include \"tachyon/math/finite_fields/koala_bear/internal/packed_koala_bear_avx2.h\"\n\n#include <immintrin.h>\n\n#include \"tachyon/math/finite_fields/packed_prime_field32_avx2.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\n__m256i kP;\n__m256i kInv;\n__m256i kZero;\n__m256i kOne;\n__m256i kMinusOne;\n__m256i kTwoInv;\n__m256i kRawOne;\n\n__m256i ToVector(const PackedKoalaBearAVX2& packed) {\n  return _mm256_loadu_si256(\n      reinterpret_cast<const __m256i_u*>(packed.values().data()));\n}\n\nPackedKoalaBearAVX2 FromVector(__m256i vector) {\n  PackedKoalaBearAVX2 ret;\n  _mm256_storeu_si256(reinterpret_cast<__m256i_u*>(ret.values().data()),\n                      vector);\n  return ret;\n}\n\n__m256i Add(__m256i lhs, __m256i rhs) { return AddMod32(lhs, rhs, kP); }\n\n__m256i Sub(__m256i lhs, __m256i rhs) { return SubMod32(lhs, rhs, kP); }\n\n__m256i Negate(__m256i val) { return NegateMod32(val, kP); }\n\n__m256i Mul(__m256i lhs, __m256i rhs) {\n  return MontMulMod32(lhs, rhs, kP, kInv);\n}\n\n}  // namespace\n\nPackedKoalaBearAVX2::PackedKoalaBearAVX2(uint32_t value) {\n  __m256i vector = _mm256_set1_epi32(KoalaBear::Config::ToMontgomery(value));\n  _mm256_storeu_si256(reinterpret_cast<__m256i_u*>(values_.data()), vector);\n}\n\n// static\nvoid PackedKoalaBearAVX2::Init() {\n  KoalaBear::Init();\n  kP = _mm256_set1_epi32(KoalaBear::Config::kModulus);\n  kInv = _mm256_set1_epi32(KoalaBear::Config::kInverse32);\n  kZero = _mm256_set1_epi32(0);\n  kOne = _mm256_set1_epi32(KoalaBear::Config::kOne);\n  kMinusOne = _mm256_set1_epi32(KoalaBear::Config::kMinusOne);\n  kTwoInv = _mm256_set1_epi32(KoalaBear::Config::kTwoInv);\n  kRawOne = _mm256_set1_epi32(1);\n}\n\n// static\nPackedKoalaBearAVX2 PackedKoalaBearAVX2::Zero() { return FromVector(kZero); }\n\n// static\nPackedKoalaBearAVX2 PackedKoalaBearAVX2::One() { return FromVector(kOne); }\n\n// static\nPackedKoalaBearAVX2 PackedKoalaBearAVX2::MinusOne() {\n  return FromVector(kMinusOne);\n}\n\n// static\nPackedKoalaBearAVX2 PackedKoalaBearAVX2::TwoInv() {\n  return FromVector(kTwoInv);\n}\n\n// static\nPackedKoalaBearAVX2 PackedKoalaBearAVX2::RawOne() {\n  return FromVector(kRawOne);\n}\n\n// static\nPackedKoalaBearAVX2 PackedKoalaBearAVX2::Broadcast(const PrimeField& value) {\n  return FromVector(_mm256_set1_epi32(value.value()));\n}\n\nPackedKoalaBearAVX2 PackedKoalaBearAVX2::Add(\n    const PackedKoalaBearAVX2& other) const {\n  return FromVector(math::Add(ToVector(*this), ToVector(other)));\n}\n\nPackedKoalaBearAVX2 PackedKoalaBearAVX2::Sub(\n    const PackedKoalaBearAVX2& other) const {\n  return FromVector(math::Sub(ToVector(*this), ToVector(other)));\n}\n\nPackedKoalaBearAVX2 PackedKoalaBearAVX2::Negate() const {\n  return FromVector(math::Negate(ToVector(*this)));\n}\n\nPackedKoalaBearAVX2 PackedKoalaBearAVX2::Mul(\n    const PackedKoalaBearAVX2& other) const {\n  return FromVector(math::Mul(ToVector(*this), ToVector(other)));\n}\n\nPackedKoalaBearAVX2 PackedKoalaBearAVX2::Exp3() const {\n  return FromVector(math::Exp3(ToVector(*this), kP, kInv));\n}\n\nPackedKoalaBearAVX2 PackedKoalaBearAVX2::Exp5() const {\n  return FromVector(math::Exp5(ToVector(*this), kP, kInv));\n}\n\nPackedKoalaBearAVX2 PackedKoalaBearAVX2::Exp7() const {\n  return FromVector(math::Exp7(ToVector(*this), kP, kInv));\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/finite_fields/koala_bear/internal/packed_koala_bear_avx2.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_MATH_FINITE_FIELDS_KOALA_BEAR_INTERNAL_PACKED_KOALA_BEAR_AVX2_H_\n#define TACHYON_MATH_FINITE_FIELDS_KOALA_BEAR_INTERNAL_PACKED_KOALA_BEAR_AVX2_H_\n\n#include <stddef.h>\n\n#include \"tachyon/export.h\"\n#include \"tachyon/math/finite_fields/koala_bear/internal/koala_bear.h\"\n#include \"tachyon/math/finite_fields/packed_prime_field_base.h\"\n\nnamespace tachyon::math {\n\nclass PackedKoalaBearAVX2;\n\ntemplate <>\nstruct PackedFieldTraits<PackedKoalaBearAVX2> {\n  using Field = KoalaBear;\n\n  constexpr static size_t N = 8;\n};\n\nclass TACHYON_EXPORT PackedKoalaBearAVX2 final\n    : public PackedPrimeFieldBase<PackedKoalaBearAVX2> {\n public:\n  using PrimeField = KoalaBear;\n\n  constexpr static size_t N = 8;\n\n  PackedKoalaBearAVX2() = default;\n  // NOTE(chokobole): This is needed by Eigen matrix.\n  explicit PackedKoalaBearAVX2(uint32_t value);\n  PackedKoalaBearAVX2(const PackedKoalaBearAVX2& other) = default;\n  PackedKoalaBearAVX2& operator=(const PackedKoalaBearAVX2& other) = default;\n  PackedKoalaBearAVX2(PackedKoalaBearAVX2&& other) = default;\n  PackedKoalaBearAVX2& operator=(PackedKoalaBearAVX2&& other) = default;\n\n  static void Init();\n\n  static PackedKoalaBearAVX2 Zero();\n\n  static PackedKoalaBearAVX2 One();\n\n  static PackedKoalaBearAVX2 MinusOne();\n\n  static PackedKoalaBearAVX2 TwoInv();\n\n  static PackedKoalaBearAVX2 RawOne();\n\n  static PackedKoalaBearAVX2 Broadcast(const PrimeField& value);\n\n  // AdditiveSemigroup methods\n  PackedKoalaBearAVX2 Add(const PackedKoalaBearAVX2& other) const;\n\n  // AdditiveGroup methods\n  PackedKoalaBearAVX2 Sub(const PackedKoalaBearAVX2& other) const;\n\n  PackedKoalaBearAVX2 Negate() const;\n\n  // MultiplicativeSemigroup methods\n  PackedKoalaBearAVX2 Mul(const PackedKoalaBearAVX2& other) const;\n\n  PackedKoalaBearAVX2 Exp3() const;\n  PackedKoalaBearAVX2 Exp5() const;\n  PackedKoalaBearAVX2 Exp7() const;\n};\n\n}  // namespace tachyon::math\n\n#endif  //  TACHYON_MATH_FINITE_FIELDS_KOALA_BEAR_INTERNAL_PACKED_KOALA_BEAR_AVX2_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/koala_bear/internal/packed_koala_bear_avx512.cc",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#if defined(TACHYON_HAS_AVX512)\n\n#include \"tachyon/math/finite_fields/koala_bear/internal/packed_koala_bear_avx512.h\"\n\n#include <immintrin.h>\n\n#include \"tachyon/math/finite_fields/packed_prime_field32_avx512.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\n__m512i kP;\n__m512i kInv;\n__m512i kZero;\n__m512i kOne;\n__m512i kMinusOne;\n__m512i kTwoInv;\n__m512i kRawOne;\n\n__m512i ToVector(const PackedKoalaBearAVX512& packed) {\n  return _mm512_loadu_si512(packed.values().data());\n}\n\nPackedKoalaBearAVX512 FromVector(__m512i vector) {\n  PackedKoalaBearAVX512 ret;\n  _mm512_storeu_si512(ret.values().data(), vector);\n  return ret;\n}\n\n__m512i Add(__m512i lhs, __m512i rhs) { return AddMod32(lhs, rhs, kP); }\n\n__m512i Sub(__m512i lhs, __m512i rhs) { return SubMod32(lhs, rhs, kP); }\n\n__m512i Negate(__m512i val) { return NegateMod32(val, kP); }\n\n__m512i Mul(__m512i lhs, __m512i rhs) {\n  return MontMulMod32(lhs, rhs, kP, kInv);\n}\n\n}  // namespace\n\nPackedKoalaBearAVX512::PackedKoalaBearAVX512(uint32_t value) {\n  __m512i vector = _mm512_set1_epi32(KoalaBear::Config::ToMontgomery(value));\n  _mm512_storeu_si512(values_.data(), vector);\n}\n\n// static\nvoid PackedKoalaBearAVX512::Init() {\n  KoalaBear::Init();\n  kP = _mm512_set1_epi32(KoalaBear::Config::kModulus);\n  kInv = _mm512_set1_epi32(KoalaBear::Config::kInverse32);\n  kZero = _mm512_set1_epi32(0);\n  kOne = _mm512_set1_epi32(KoalaBear::Config::kOne);\n  kMinusOne = _mm512_set1_epi32(KoalaBear::Config::kMinusOne);\n  kTwoInv = _mm512_set1_epi32(KoalaBear::Config::kTwoInv);\n  kRawOne = _mm512_set1_epi32(1);\n}\n\n// static\nPackedKoalaBearAVX512 PackedKoalaBearAVX512::Zero() {\n  return FromVector(kZero);\n}\n\n// static\nPackedKoalaBearAVX512 PackedKoalaBearAVX512::One() { return FromVector(kOne); }\n\n// static\nPackedKoalaBearAVX512 PackedKoalaBearAVX512::MinusOne() {\n  return FromVector(kMinusOne);\n}\n\n// static\nPackedKoalaBearAVX512 PackedKoalaBearAVX512::TwoInv() {\n  return FromVector(kTwoInv);\n}\n\n// static\nPackedKoalaBearAVX512 PackedKoalaBearAVX512::RawOne() {\n  return FromVector(kRawOne);\n}\n\n// static\nPackedKoalaBearAVX512 PackedKoalaBearAVX512::Broadcast(\n    const PrimeField& value) {\n  return FromVector(_mm512_set1_epi32(value.value()));\n}\n\nPackedKoalaBearAVX512 PackedKoalaBearAVX512::Add(\n    const PackedKoalaBearAVX512& other) const {\n  return FromVector(math::Add(ToVector(*this), ToVector(other)));\n}\n\nPackedKoalaBearAVX512 PackedKoalaBearAVX512::Sub(\n    const PackedKoalaBearAVX512& other) const {\n  return FromVector(math::Sub(ToVector(*this), ToVector(other)));\n}\n\nPackedKoalaBearAVX512 PackedKoalaBearAVX512::Negate() const {\n  return FromVector(math::Negate(ToVector(*this)));\n}\n\nPackedKoalaBearAVX512 PackedKoalaBearAVX512::Mul(\n    const PackedKoalaBearAVX512& other) const {\n  return FromVector(math::Mul(ToVector(*this), ToVector(other)));\n}\n\n}  // namespace tachyon::math\n\n#endif  // defined(TACHYON_HAS_AVX512)\n"
  },
  {
    "path": "tachyon/math/finite_fields/koala_bear/internal/packed_koala_bear_avx512.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_MATH_FINITE_FIELDS_KOALA_BEAR_INTERNAL_PACKED_KOALA_BEAR_AVX512_H_\n#define TACHYON_MATH_FINITE_FIELDS_KOALA_BEAR_INTERNAL_PACKED_KOALA_BEAR_AVX512_H_\n\n#include <stddef.h>\n\n#include \"tachyon/export.h\"\n#include \"tachyon/math/finite_fields/koala_bear/internal/koala_bear.h\"\n#include \"tachyon/math/finite_fields/packed_prime_field_base.h\"\n\nnamespace tachyon::math {\n\nclass PackedKoalaBearAVX512;\n\ntemplate <>\nstruct PackedFieldTraits<PackedKoalaBearAVX512> {\n  using Field = KoalaBear;\n\n  constexpr static size_t N = 16;\n};\n\nclass TACHYON_EXPORT PackedKoalaBearAVX512 final\n    : public PackedPrimeFieldBase<PackedKoalaBearAVX512> {\n public:\n  using PrimeField = KoalaBear;\n\n  constexpr static size_t N = 16;\n\n  PackedKoalaBearAVX512() = default;\n  // NOTE(chokobole): This is needed by Eigen matrix.\n  explicit PackedKoalaBearAVX512(uint32_t value);\n  PackedKoalaBearAVX512(const PackedKoalaBearAVX512& other) = default;\n  PackedKoalaBearAVX512& operator=(const PackedKoalaBearAVX512& other) =\n      default;\n  PackedKoalaBearAVX512(PackedKoalaBearAVX512&& other) = default;\n  PackedKoalaBearAVX512& operator=(PackedKoalaBearAVX512&& other) = default;\n\n  static void Init();\n\n  static PackedKoalaBearAVX512 Zero();\n\n  static PackedKoalaBearAVX512 One();\n\n  static PackedKoalaBearAVX512 MinusOne();\n\n  static PackedKoalaBearAVX512 TwoInv();\n\n  static PackedKoalaBearAVX512 RawOne();\n\n  static PackedKoalaBearAVX512 Broadcast(const PrimeField& value);\n\n  // AdditiveSemigroup methods\n  PackedKoalaBearAVX512 Add(const PackedKoalaBearAVX512& other) const;\n\n  // AdditiveGroup methods\n  PackedKoalaBearAVX512 Sub(const PackedKoalaBearAVX512& other) const;\n\n  PackedKoalaBearAVX512 Negate() const;\n\n  // MultiplicativeSemigroup methods\n  PackedKoalaBearAVX512 Mul(const PackedKoalaBearAVX512& other) const;\n};\n\n}  // namespace tachyon::math\n\n#endif  //  TACHYON_MATH_FINITE_FIELDS_KOALA_BEAR_INTERNAL_PACKED_KOALA_BEAR_AVX512_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/koala_bear/internal/packed_koala_bear_neon.cc",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#include \"tachyon/math/finite_fields/koala_bear/internal/packed_koala_bear_neon.h\"\n\n#include <arm_neon.h>\n\n#include \"tachyon/math/finite_fields/packed_prime_field32_neon.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\nuint32x4_t kP;\nuint32x4_t kInv;\nuint32x4_t kZero;\nuint32x4_t kOne;\nuint32x4_t kMinusOne;\nuint32x4_t kTwoInv;\nuint32x4_t kRawOne;\n\nuint32x4_t ToVector(const PackedKoalaBearNeon& packed) {\n  return vld1q_u32(reinterpret_cast<const uint32_t*>(packed.values().data()));\n}\n\nPackedKoalaBearNeon FromVector(uint32x4_t vector) {\n  PackedKoalaBearNeon ret;\n  vst1q_u32(reinterpret_cast<uint32_t*>(ret.values().data()), vector);\n  return ret;\n}\n\nuint32x4_t Add(uint32x4_t lhs, uint32x4_t rhs) {\n  return AddMod32(lhs, rhs, kP);\n}\n\nuint32x4_t Sub(uint32x4_t lhs, uint32x4_t rhs) {\n  return SubMod32(lhs, rhs, kP);\n}\n\nuint32x4_t Negate(uint32x4_t val) { return NegateMod32(val, kP); }\n\nuint32x4_t Mul(uint32x4_t lhs, uint32x4_t rhs) {\n  return MontMulMod32(lhs, rhs, kP, kInv);\n}\n\n}  // namespace\n\nPackedKoalaBearNeon::PackedKoalaBearNeon(uint32_t value) {\n  uint32x4_t vector = vdupq_n_u32(KoalaBear::Config::ToMontgomery(value));\n  vst1q_u32(reinterpret_cast<uint32_t*>(values_.data()), vector);\n}\n\n// static\nvoid PackedKoalaBearNeon::Init() {\n  KoalaBear::Init();\n  kP = vdupq_n_u32(KoalaBear::Config::kModulus);\n  kInv = vdupq_n_u32(KoalaBear::Config::kInverse32);\n  kZero = vdupq_n_u32(0);\n  kOne = vdupq_n_u32(KoalaBear::Config::kOne);\n  kMinusOne = vdupq_n_u32(KoalaBear::Config::kMinusOne);\n  kTwoInv = vdupq_n_u32(KoalaBear::Config::kTwoInv);\n  kRawOne = vdupq_n_u32(1);\n}\n\n// static\nPackedKoalaBearNeon PackedKoalaBearNeon::Zero() { return FromVector(kZero); }\n\n// static\nPackedKoalaBearNeon PackedKoalaBearNeon::One() { return FromVector(kOne); }\n\n// static\nPackedKoalaBearNeon PackedKoalaBearNeon::MinusOne() {\n  return FromVector(kMinusOne);\n}\n\n// static\nPackedKoalaBearNeon PackedKoalaBearNeon::TwoInv() {\n  return FromVector(kTwoInv);\n}\n\n// static\nPackedKoalaBearNeon PackedKoalaBearNeon::RawOne() {\n  return FromVector(kRawOne);\n}\n\n// static\nPackedKoalaBearNeon PackedKoalaBearNeon::Broadcast(const PrimeField& value) {\n  return FromVector(vdupq_n_u32(value.value()));\n}\n\nPackedKoalaBearNeon PackedKoalaBearNeon::Add(\n    const PackedKoalaBearNeon& other) const {\n  return FromVector(math::Add(ToVector(*this), ToVector(other)));\n}\n\nPackedKoalaBearNeon PackedKoalaBearNeon::Sub(\n    const PackedKoalaBearNeon& other) const {\n  return FromVector(math::Sub(ToVector(*this), ToVector(other)));\n}\n\nPackedKoalaBearNeon PackedKoalaBearNeon::Negate() const {\n  return FromVector(math::Negate(ToVector(*this)));\n}\n\nPackedKoalaBearNeon PackedKoalaBearNeon::Mul(\n    const PackedKoalaBearNeon& other) const {\n  return FromVector(math::Mul(ToVector(*this), ToVector(other)));\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/finite_fields/koala_bear/internal/packed_koala_bear_neon.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_MATH_FINITE_FIELDS_KOALA_BEAR_INTERNAL_PACKED_KOALA_BEAR_NEON_H_\n#define TACHYON_MATH_FINITE_FIELDS_KOALA_BEAR_INTERNAL_PACKED_KOALA_BEAR_NEON_H_\n\n#include <stddef.h>\n\n#include \"tachyon/export.h\"\n#include \"tachyon/math/finite_fields/koala_bear/internal/koala_bear.h\"\n#include \"tachyon/math/finite_fields/packed_prime_field_base.h\"\n\nnamespace tachyon::math {\n\nclass PackedKoalaBearNeon;\n\ntemplate <>\nstruct PackedFieldTraits<PackedKoalaBearNeon> {\n  using Field = KoalaBear;\n\n  constexpr static size_t N = 4;\n};\n\nclass TACHYON_EXPORT PackedKoalaBearNeon final\n    : public PackedPrimeFieldBase<PackedKoalaBearNeon> {\n public:\n  using PrimeField = KoalaBear;\n\n  constexpr static size_t N = 4;\n\n  PackedKoalaBearNeon() = default;\n  // NOTE(chokobole): This is needed by Eigen matrix.\n  explicit PackedKoalaBearNeon(uint32_t value);\n  PackedKoalaBearNeon(const PackedKoalaBearNeon& other) = default;\n  PackedKoalaBearNeon& operator=(const PackedKoalaBearNeon& other) = default;\n  PackedKoalaBearNeon(PackedKoalaBearNeon&& other) = default;\n  PackedKoalaBearNeon& operator=(PackedKoalaBearNeon&& other) = default;\n\n  static void Init();\n\n  static PackedKoalaBearNeon Zero();\n\n  static PackedKoalaBearNeon One();\n\n  static PackedKoalaBearNeon MinusOne();\n\n  static PackedKoalaBearNeon TwoInv();\n\n  static PackedKoalaBearNeon RawOne();\n\n  static PackedKoalaBearNeon Broadcast(const PrimeField& value);\n\n  // AdditiveSemigroup methods\n  PackedKoalaBearNeon Add(const PackedKoalaBearNeon& other) const;\n\n  // AdditiveGroup methods\n  PackedKoalaBearNeon Sub(const PackedKoalaBearNeon& other) const;\n\n  PackedKoalaBearNeon Negate() const;\n\n  // MultiplicativeSemigroup methods\n  PackedKoalaBearNeon Mul(const PackedKoalaBearNeon& other) const;\n};\n\n}  // namespace tachyon::math\n\n#endif  //  TACHYON_MATH_FINITE_FIELDS_KOALA_BEAR_INTERNAL_PACKED_KOALA_BEAR_NEON_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/koala_bear/koala_bear.h",
    "content": "#ifndef TACHYON_MATH_FINITE_FIELDS_KOALA_BEAR_KOALA_BEAR_H_\n#define TACHYON_MATH_FINITE_FIELDS_KOALA_BEAR_KOALA_BEAR_H_\n\n#include \"tachyon/math/finite_fields/koala_bear/internal/packed_koala_bear.h\"\n\nnamespace tachyon::math {\n\ntemplate <>\nstruct PackedFieldTraits<KoalaBear> {\n  using PackedField = PackedKoalaBear;\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_KOALA_BEAR_KOALA_BEAR_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/koala_bear/koala_bear4.h",
    "content": "#ifndef TACHYON_MATH_FINITE_FIELDS_KOALA_BEAR_KOALA_BEAR4_H_\n#define TACHYON_MATH_FINITE_FIELDS_KOALA_BEAR_KOALA_BEAR4_H_\n\n#include \"tachyon/math/finite_fields/baby_bear/internal/koala_bear4.h\"\n#include \"tachyon/math/finite_fields/baby_bear/internal/packed_koala_bear4.h\"\n\nnamespace tachyon::math {\n\ntemplate <>\nstruct ExtendedPackedFieldTraits<KoalaBear4> {\n  constexpr static bool kIsExtendedPackedField = false;\n  using ExtendedPackedField = PackedKoalaBear4;\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_KOALA_BEAR_KOALA_BEAR4_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/koala_bear/packed_koala_bear4.h",
    "content": "#ifndef TACHYON_MATH_FINITE_FIELDS_KOALA_BEAR_PACKED_KOALA_BEAR4_H_\n#define TACHYON_MATH_FINITE_FIELDS_KOALA_BEAR_PACKED_KOALA_BEAR4_H_\n\n#include \"tachyon/math/finite_fields/baby_bear/internal/packed_koala_bear4.h\"\n\nnamespace tachyon::math {\n\ntemplate <>\nstruct ExtendedPackedFieldTraits<PackedKoalaBear4> {\n  constexpr static bool kIsExtendedPackedField = true;\n  // Note(ashjeong): |ExtendedPackedField| is defaulted as itself. See\n  // extension_field_base.h to see how it is used.\n  using ExtendedPackedField = PackedKoalaBear4;\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_KOALA_BEAR_PACKED_KOALA_BEAR4_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/legendre_symbol.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_FINITE_FIELDS_LEGENDRE_SYMBOL_H_\n#define TACHYON_MATH_FINITE_FIELDS_LEGENDRE_SYMBOL_H_\n\nnamespace tachyon::math {\n\n// See https://en.wikipedia.org/wiki/Legendre_symbol\nenum class LegendreSymbol {\n  kOne = 1,\n  kMinusOne = -1,\n  kZero = 0,\n  kQuadraticResidue = kOne,\n  kNoneQuadraticResidue = kMinusOne,\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_LEGENDRE_SYMBOL_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/mersenne31/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"mersenne31\",\n    hdrs = [\"mersenne31.h\"],\n    deps = [\"//tachyon/math/finite_fields/mersenne31/internal:packed_mersenne31\"],\n)\n"
  },
  {
    "path": "tachyon/math/finite_fields/mersenne31/internal/BUILD.bazel",
    "content": "load(\"//bazel:tachyon.bzl\", \"if_aarch64\", \"if_has_avx512\", \"if_x86_64\")\nload(\"//bazel:tachyon_cc.bzl\", \"tachyon_avx512_defines\", \"tachyon_cc_library\")\nload(\"//tachyon/math/finite_fields/generator/prime_field_generator:build_defs.bzl\", \"generate_prime_fields\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ngenerate_prime_fields(\n    name = \"mersenne31\",\n    class_name = \"Mersenne31\",\n    # 2³¹ - 1\n    # Hex: 0x7fffffff\n    modulus = \"2147483647\",\n    namespace = \"tachyon::math\",\n    reduce64 = \"return ((((v >> kModulusBits) + v + 1) >> kModulusBits) + v) & kModulus;\",\n    use_montgomery = False,\n)\n\ntachyon_cc_library(\n    name = \"packed_mersenne31\",\n    hdrs = [\"packed_mersenne31.h\"],\n    defines = tachyon_avx512_defines(),\n    deps = [\n        \":packed_mersenne31_neon\",\n        \"//tachyon/build:build_config\",\n        \"//tachyon/math/finite_fields:finite_field_traits\",\n        \"//tachyon/math/matrix:prime_field_num_traits\",\n    ] + if_has_avx512(\n        [\":packed_mersenne31_avx512\"],\n        [\":packed_mersenne31_avx2\"],\n    ),\n)\n\ntachyon_cc_library(\n    name = \"packed_mersenne31_avx2\",\n    srcs = if_x86_64([\"packed_mersenne31_avx2.cc\"]),\n    hdrs = if_x86_64([\"packed_mersenne31_avx2.h\"]),\n    copts = if_x86_64([\"-mavx2\"]),\n    deps = [\n        \":mersenne31\",\n        \"//tachyon:export\",\n        \"//tachyon/math/finite_fields:packed_prime_field32_avx2\",\n        \"//tachyon/math/finite_fields:packed_prime_field_base\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"packed_mersenne31_avx512\",\n    srcs = if_x86_64([\"packed_mersenne31_avx512.cc\"]),\n    hdrs = if_x86_64([\"packed_mersenne31_avx512.h\"]),\n    copts = if_x86_64([\n        \"-mavx512f\",\n        # NOTE(chokobole): See https://gitlab.com/libeigen/eigen/-/blob/0b51f76/Eigen/src/Core/util/ConfigureVectorization.h#L248.\n        \"-mfma\",\n    ]),\n    deps = [\n        \":mersenne31\",\n        \"//tachyon:export\",\n        \"//tachyon/math/finite_fields:packed_prime_field32_avx512\",\n        \"//tachyon/math/finite_fields:packed_prime_field_base\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"packed_mersenne31_neon\",\n    srcs = if_aarch64([\"packed_mersenne31_neon.cc\"]),\n    hdrs = if_aarch64([\"packed_mersenne31_neon.h\"]),\n    deps = [\n        \":mersenne31\",\n        \"//tachyon:export\",\n        \"//tachyon/math/finite_fields:packed_prime_field32_neon\",\n        \"//tachyon/math/finite_fields:packed_prime_field_base\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/math/finite_fields/mersenne31/internal/packed_mersenne31.h",
    "content": "#ifndef TACHYON_MATH_FINITE_FIELDS_MERSENNE31_INTERNAL_PACKED_MERSENNE31_H_\n#define TACHYON_MATH_FINITE_FIELDS_MERSENNE31_INTERNAL_PACKED_MERSENNE31_H_\n\n#include \"tachyon/build/build_config.h\"\n\n#if ARCH_CPU_X86_64\n#if defined(TACHYON_HAS_AVX512)\n#include \"tachyon/math/finite_fields/mersenne31/internal/packed_mersenne31_avx512.h\"\n#else\n#include \"tachyon/math/finite_fields/mersenne31/internal/packed_mersenne31_avx2.h\"\n#endif\n#elif ARCH_CPU_ARM64\n#include \"tachyon/math/finite_fields/mersenne31/internal/packed_mersenne31_neon.h\"\n#endif\n#include \"tachyon/math/finite_fields/finite_field_traits.h\"\n#include \"tachyon/math/matrix/prime_field_num_traits.h\"\n\nnamespace tachyon::math {\n\n#if ARCH_CPU_X86_64\n#if defined(TACHYON_HAS_AVX512)\nusing PackedMersenne31 = PackedMersenne31AVX512;\n#else\nusing PackedMersenne31 = PackedMersenne31AVX2;\n#endif\n#elif ARCH_CPU_ARM64\nusing PackedMersenne31 = PackedMersenne31Neon;\n#endif\n\ntemplate <>\nstruct FiniteFieldTraits<PackedMersenne31> {\n  static constexpr bool kIsPrimeField = true;\n  static constexpr bool kIsPackedPrimeField = true;\n  static constexpr bool kIsExtensionField = false;\n\n  using PrimeField = Mersenne31;\n  using Config = Mersenne31::Config;\n};\n\n}  // namespace tachyon::math\n\nnamespace Eigen {\n\ntemplate <>\nstruct NumTraits<tachyon::math::PackedMersenne31>\n    : GenericNumTraits<tachyon::math::PackedMersenne31> {\n  using PrimeField = tachyon::math::Mersenne31;\n  constexpr static size_t N = tachyon::math::PackedMersenne31::N;\n\n  enum {\n    IsInteger = 1,\n    IsField = 1,\n    IsSigned = 0,\n    IsComplex = 0,\n    RequireInitialization = 0,\n    ReadCost = tachyon::math::CostCalculator<PrimeField>::ComputeReadCost() * N,\n    AddCost = tachyon::math::CostCalculator<PrimeField>::ComputeAddCost() * N,\n    MulCost = tachyon::math::CostCalculator<PrimeField>::ComputeMulCost() * N,\n  };\n};\n\n}  // namespace Eigen\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_MERSENNE31_INTERNAL_PACKED_MERSENNE31_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/mersenne31/internal/packed_mersenne31_avx2.cc",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#include \"tachyon/math/finite_fields/mersenne31/internal/packed_mersenne31_avx2.h\"\n\n#include <immintrin.h>\n\n#include \"tachyon/math/finite_fields/packed_prime_field32_avx2.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\n__m256i kP;\n__m256i kZero;\n__m256i kOne;\n__m256i kMinusOne;\n__m256i kTwoInv;\n\n__m256i ToVector(const PackedMersenne31AVX2& packed) {\n  return _mm256_loadu_si256(\n      reinterpret_cast<const __m256i_u*>(packed.values().data()));\n}\n\nPackedMersenne31AVX2 FromVector(__m256i vector) {\n  PackedMersenne31AVX2 ret;\n  _mm256_storeu_si256(reinterpret_cast<__m256i_u*>(ret.values().data()),\n                      vector);\n  return ret;\n}\n\n__m256i Add(__m256i lhs, __m256i rhs) { return AddMod32(lhs, rhs, kP); }\n\n__m256i Sub(__m256i lhs, __m256i rhs) { return SubMod32(lhs, rhs, kP); }\n\n__m256i Negate(__m256i val) { return NegateMod32(val, kP); }\n\n__m256i movehdup_epi32(__m256i x) {\n  // The instruction is only available in the floating-point flavor; this\n  // distinction is only for historical reasons and no longer matters. We cast\n  // to floats, duplicate, and cast back.\n  return _mm256_castps_si256(_mm256_movehdup_ps(_mm256_castsi256_ps(x)));\n}\n\n__m256i Mul(__m256i lhs, __m256i rhs) {\n  // We want this to compile to:\n  // vpsrlq     lhs_odd_dbl, lhs, 31\n  // vmovshdup  rhs_odd, rhs\n  // vpmuludq   prod_odd_dbl, lhs_odd_dbl, rhs_odd\n  // vpmuludq   prod_evn, lhs, rhs\n  // vpsllq     prod_odd_lo_dirty, prod_odd_dbl, 31\n  // vpsrlq     prod_evn_hi, prod_evn, 31\n  // vpblendd   prod_lo_dirty, prod_evn, prod_odd_lo_dirty, aah\n  // vpblendd   prod_hi, prod_evn_hi, prod_odd_dbl, aah\n  // vpand      prod_lo, prod_lo_dirty, p\n  // vpaddd     t, prod_lo, prod_hi\n  // vpsubd     u, t, p\n  // vpminud    r, t, u\n  // throughput: 4 cyc/vec (2 els/cyc)\n  // latency: 13 cyc\n\n  // vpmuludq only reads the bottom 32 bits of every 64-bit quadword.\n  // The even indices are already in the bottom 32 bits of a quadword, so we\n  // can leave them.\n  //\n  //            ┌──────┬──────┬─────┬──────┬──────┐\n  // |lhs_evn|: │ lhs₀ │ lhs₁ │ ... │ lhs₆ │ lhs₇ │\n  //            └──────┴──────┴─────┴──────┴──────┘\n  __m256i lhs_evn = lhs;\n  //            ┌──────┬──────┬─────┬──────┬──────┐\n  // |rhs_evn|: │ rhs₀ │ rhs₁ │ ... │ rhs₆ │ rhs₇ │\n  //            └──────┴──────┴─────┴──────┴──────┘\n  __m256i rhs_evn = rhs;\n  // Right shift by 31 is equivalent to moving the high 32 bits down to the\n  // low 32, and then doubling it. So these are the odd indices in lhs, but\n  // doubled.\n  //                ┌──────────┬───┬─────┬──────────┬───┐\n  // |lhs_odd_dbl|: │ 2 * lhs₁ │ 0 │ ... │ 2 * lhs₇ │ 0 │\n  //                └──────────┴───┴─────┴──────────┴───┘\n  __m256i lhs_odd_dbl = _mm256_srli_epi64(lhs, 31);\n  // Copy the high 32 bits in each quadword of rhs down to the low 32.\n  //            ┌──────┬──────┬─────┬──────┬──────┐\n  // |rhs_odd|: │ rhs₁ │ rhs₁ │ ... │ rhs₇ │ rhs₇ │\n  //            └──────┴──────┴─────┴──────┴──────┘\n  __m256i rhs_odd = movehdup_epi32(rhs);\n\n  // Multiply odd indices; since |lhs_odd_dbl| is doubled, these products are\n  // also doubled.\n  // clang-format off\n  //                 ┌─────────────────────┬─────────────────────┬─────┬─────────────────────┬─────────────────────┐\n  // |prod_odd_dbl|: │ Lo(2 * lhs₁ * rhs₁) │ Hi(2 * lhs₁ * rhs₁) | ... │ Lo(2 * lhs₇ * rhs₇) │ Hi(2 * lhs₇ * rhs₇) |\n  //                 └─────────────────────┴─────────────────────┴─────┴─────────────────────┴─────────────────────┘\n  // clang-format on\n  __m256i prod_odd_dbl = _mm256_mul_epu32(rhs_odd, lhs_odd_dbl);\n  // Multiply even indices.\n  // clang-format off\n  //             ┌─────────────────┬─────────────────┬─────┬─────────────────┬─────────────────┐\n  // |prod_evn|: │ Lo(lhs₀ * rhs₀) │ Hi(lhs₀ * rhs₀) | ... │ Lo(lhs₆ * rhs₆) │ Hi(lhs₆ * rhs₆) |\n  //             └─────────────────┴─────────────────┴─────┴─────────────────┴─────────────────┘\n  // clang-format on\n  __m256i prod_evn = _mm256_mul_epu32(rhs_evn, lhs_evn);\n\n  // We now need to extract the low 31 bits and the high 31 bits of each 62\n  // bit product and prepare to add them. Put the low 31 bits of the product\n  // (recall that it is shifted left by 1) in an odd doubleword. (Notice that\n  // the high 31 bits are already in an odd doubleword in |prod_odd_dbl|.) We\n  // will still need to clear the sign bit, hence we mark it dirty.\n  // clang-format off\n  //                      ┌───┬─────────────────────────────┬─────┬───┬─────────────────────────────┐\n  // |prod_odd_lo_dirty|: │ 0 │ (lhs₁ * rhs₁)[0..31)(dirty) │ ... │ 0 │ (lhs₇ * rhs₇)[0..31)(dirty) │\n  //                      └───┴─────────────────────────────┴─────┴───┴─────────────────────────────┘\n  // clang-format on\n  __m256i prod_odd_lo_dirty = _mm256_slli_epi64(prod_odd_dbl, 31);\n  // Put the high 31 bits in an even doubleword, again noting that in |prod_evn|\n  // the even doublewords contain the low 31 bits (with a dirty sign bit).\n  // clang-format off\n  //                ┌───────────────────────┬───┬─────┬───────────────────────┬──-┐\n  // |prod_evn_hi|: │ (lhs₀ * rhs₀)[31..63) │ 0 │ ... │ (lhs₆ * rhs₆)[31..63) │ 0 |\n  //                └───────────────────────┴───┴─────┴───────────────────────┴──-┘\n  // clang-format on\n  __m256i prod_evn_hi = _mm256_srli_epi64(prod_evn, 31);\n  // Put all the low halves of all the products into one vector. Take the even\n  // values from prod_evn and odd values from prod_odd_lo_dirty. Note that the\n  // sign bits still need clearing.\n  // clang-format off\n  //                  ┌─────────────────┬─────────────────────────────┬─────┬─────────────────┬─────────────────────────────┐\n  // |prod_lo_dirty|: │ Lo(lhs₀ * rhs₀) │ (lhs₁ * rhs₁)[0..31)(dirty) │ ... │ Lo(lhs₆ * rhs₆) │ (lhs₇ * rhs₇)[0..31)(dirty) │\n  //                  └─────────────────┴─────────────────────────────┴─────┴─────────────────┴─────────────────────────────┘\n  // clang-format on\n  __m256i prod_lo_dirty =\n      _mm256_blend_epi32(prod_evn, prod_odd_lo_dirty, 0b10101010);\n  // Now put all the high halves into one vector. The even values come from\n  // |prod_evn_hi| and the odd values come from |prod_odd_dbl|.\n  // clang-format off\n  //            ┌───────────────────────┬─────────────────────┬─────┬───────────────────────┬─────────────────────┐\n  // |prod_hi|: │ (lhs₀ * rhs₀)[31..63) │ Hi(2 * lhs₁ * rhs₁) │ ... │ (lhs₆ * rhs₆)[31..63) │ Hi(2 * lhs₇ * rhs₇) │\n  //            └───────────────────────┴─────────────────────┴─────┴───────────────────────┴─────────────────────┘\n  // clang-format on\n  __m256i prod_hi = _mm256_blend_epi32(prod_evn_hi, prod_odd_dbl, 0b10101010);\n  // Clear the most significant bit.\n  // clang-format off\n  //            ┌──────────────────────┬──────────────────────┬─────┬──────────────────────┬──────────────────────┐\n  // |prod_lo|: │ (lhs₀ * rhs₀)[0..31) │ (lhs₁ * rhs₁)[0..31) │ ... │ (lhs₆ * rhs₆)[0..31) │ (lhs₇ * rhs₇)[0..31) │\n  //            └──────────────────────┴──────────────────────┴─────┴──────────────────────┴──────────────────────┘\n  // clang-format on\n  __m256i prod_lo = _mm256_and_si256(prod_lo_dirty, kP);\n\n  // clang-format off\n  // lhsᵢ * rhsᵢ = a₀ + 2 * a₁ + ... + 2⁶⁰ * a₆₀ + 2⁶¹ * a₆₁ (mod p)\n  //             = a₀ + 2 * a₁ + ... + 2²⁹ * a₂₉ + 2³⁰ * a₃₀ + 2³¹ * (a₃₁ + 2 * a₃₂ + ... + 2²⁹ * a₆₀ + 2³⁰ * a₆₁) (mod p)\n  //             = a₀ + 2 * a₁ + ... + 2²⁹ * a₂₉ + 2³⁰ * a₃₀ + (p + 1) * (a₃₁ + 2 * a₃₂ + ... + 2²⁹ * a₆₀ + 2³⁰ * a₆₁) (mod p)\n  //             = a₀ + 2 * a₁ + ... + 2²⁹ * a₂₉ + 2³⁰ * a₃₀ + (a₃₁ + 2 * a₃₂ + ... + 2²⁹ * a₆₀ + 2³⁰ * a₆₁) (mod p)\n  //             = a₀ + 2 * a₁ + ... + 2²⁹ * a₂₉ + 2³⁰ * a₃₀ + Hi(2 * lhsᵢ * rhsᵢ) (mod p)\n  // clang-format on\n  // Standard addition of two 31-bit values.\n  return Add(prod_lo, prod_hi);\n}\n\n}  // namespace\n\nPackedMersenne31AVX2::PackedMersenne31AVX2(uint32_t value) {\n  __m256i vector = _mm256_set1_epi32(value);\n  _mm256_storeu_si256(reinterpret_cast<__m256i_u*>(values_.data()), vector);\n}\n\n// static\nvoid PackedMersenne31AVX2::Init() {\n  Mersenne31::Init();\n  kP = _mm256_set1_epi32(Mersenne31::Config::kModulus);\n  kZero = _mm256_set1_epi32(0);\n  kOne = _mm256_set1_epi32(1);\n  kMinusOne = _mm256_set1_epi32(Mersenne31::Config::kModulus - 1);\n  kTwoInv = _mm256_set1_epi32(Mersenne31::Config::kTwoInv);\n}\n\n// static\nPackedMersenne31AVX2 PackedMersenne31AVX2::Zero() { return FromVector(kZero); }\n\n// static\nPackedMersenne31AVX2 PackedMersenne31AVX2::One() { return FromVector(kOne); }\n\n// static\nPackedMersenne31AVX2 PackedMersenne31AVX2::MinusOne() {\n  return FromVector(kMinusOne);\n}\n\n// static\nPackedMersenne31AVX2 PackedMersenne31AVX2::TwoInv() {\n  return FromVector(kTwoInv);\n}\n\n// static\nPackedMersenne31AVX2 PackedMersenne31AVX2::Broadcast(const PrimeField& value) {\n  return FromVector(_mm256_set1_epi32(value.value()));\n}\n\nPackedMersenne31AVX2 PackedMersenne31AVX2::Add(\n    const PackedMersenne31AVX2& other) const {\n  return FromVector(math::Add(ToVector(*this), ToVector(other)));\n}\n\nPackedMersenne31AVX2 PackedMersenne31AVX2::Sub(\n    const PackedMersenne31AVX2& other) const {\n  return FromVector(math::Sub(ToVector(*this), ToVector(other)));\n}\n\nPackedMersenne31AVX2 PackedMersenne31AVX2::Negate() const {\n  return FromVector(math::Negate(ToVector(*this)));\n}\n\nPackedMersenne31AVX2 PackedMersenne31AVX2::Mul(\n    const PackedMersenne31AVX2& other) const {\n  return FromVector(math::Mul(ToVector(*this), ToVector(other)));\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/finite_fields/mersenne31/internal/packed_mersenne31_avx2.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_MATH_FINITE_FIELDS_MERSENNE31_INTERNAL_PACKED_MERSENNE31_AVX2_H_\n#define TACHYON_MATH_FINITE_FIELDS_MERSENNE31_INTERNAL_PACKED_MERSENNE31_AVX2_H_\n\n#include <stddef.h>\n\n#include \"tachyon/export.h\"\n#include \"tachyon/math/finite_fields/mersenne31/internal/mersenne31.h\"\n#include \"tachyon/math/finite_fields/packed_prime_field_base.h\"\n\nnamespace tachyon::math {\n\nclass PackedMersenne31AVX2;\n\ntemplate <>\nstruct PackedFieldTraits<PackedMersenne31AVX2> {\n  using Field = Mersenne31;\n\n  constexpr static size_t N = 8;\n};\n\nclass TACHYON_EXPORT PackedMersenne31AVX2 final\n    : public PackedPrimeFieldBase<PackedMersenne31AVX2> {\n public:\n  using PrimeField = Mersenne31;\n\n  constexpr static size_t N = PackedFieldTraits<PackedMersenne31AVX2>::N;\n\n  PackedMersenne31AVX2() = default;\n  // NOTE(chokobole): This is needed by Eigen matrix.\n  explicit PackedMersenne31AVX2(uint32_t value);\n  PackedMersenne31AVX2(const PackedMersenne31AVX2& other) = default;\n  PackedMersenne31AVX2& operator=(const PackedMersenne31AVX2& other) = default;\n  PackedMersenne31AVX2(PackedMersenne31AVX2&& other) = default;\n  PackedMersenne31AVX2& operator=(PackedMersenne31AVX2&& other) = default;\n\n  static void Init();\n\n  static PackedMersenne31AVX2 Zero();\n\n  static PackedMersenne31AVX2 One();\n\n  static PackedMersenne31AVX2 MinusOne();\n\n  static PackedMersenne31AVX2 TwoInv();\n\n  static PackedMersenne31AVX2 Broadcast(const PrimeField& value);\n\n  // AdditiveSemigroup methods\n  PackedMersenne31AVX2 Add(const PackedMersenne31AVX2& other) const;\n\n  // AdditiveGroup methods\n  PackedMersenne31AVX2 Sub(const PackedMersenne31AVX2& other) const;\n\n  PackedMersenne31AVX2 Negate() const;\n\n  // MultiplicativeSemigroup methods\n  PackedMersenne31AVX2 Mul(const PackedMersenne31AVX2& other) const;\n};\n\n}  // namespace tachyon::math\n\n#endif  //  TACHYON_MATH_FINITE_FIELDS_MERSENNE31_INTERNAL_PACKED_MERSENNE31_AVX2_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/mersenne31/internal/packed_mersenne31_avx512.cc",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#if defined(TACHYON_HAS_AVX512)\n\n#include \"tachyon/math/finite_fields/mersenne31/internal/packed_mersenne31_avx512.h\"\n\n#include <immintrin.h>\n\n#include \"tachyon/math/finite_fields/packed_prime_field32_avx512.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\n__m512i kP;\n__m512i kZero;\n__m512i kOne;\n__m512i kMinusOne;\n__m512i kTwoInv;\n\n__mmask16 kEvens = 0b0101010101010101;\n__mmask16 kOdds = 0b1010101010101010;\n\n__m512i ToVector(const PackedMersenne31AVX512& packed) {\n  return _mm512_loadu_si512(packed.values().data());\n}\n\nPackedMersenne31AVX512 FromVector(__m512i vector) {\n  PackedMersenne31AVX512 ret;\n  _mm512_storeu_si512(ret.values().data(), vector);\n  return ret;\n}\n\n__m512i Add(__m512i lhs, __m512i rhs) { return AddMod32(lhs, rhs, kP); }\n\n__m512i Sub(__m512i lhs, __m512i rhs) { return SubMod32(lhs, rhs, kP); }\n\n__m512i Negate(__m512i val) { return NegateMod32(val, kP); }\n\n__m512i Mul(__m512i lhs, __m512i rhs) {\n  // We want this to compile to:\n  // vpaddd     lhs_evn_dbl, lhs, lhs\n  // vmovshdup  rhs_odd, rhs\n  // vpsrlq     lhs_odd_dbl, lhs, 31\n  // vpmuludq   prod_lo_dbl, lhs_evn_dbl, rhs\n  // vpmuludq   prod_odd_dbl, lhs_odd_dbl, rhs_odd\n  // vmovdqa32  prod_hi, prod_odd_dbl\n  // vmovshdup  prod_hi{kEvens}, prod_lo_dbl\n  // vmovsldup  prod_lo_dbl{kOdds prod_odd_dbl\n  // vpsrld     prod_lo, prod_lo_dbl, 1\n  // vpaddd     t, prod_lo, prod_hi\n  // vpsubd     u, t, p\n  // vpminud    r, t, u\n  // throughput: 5.5 cyc/vec (2.91 els/cyc)\n  // latency: (lhs->r) 15 cyc, (rhs->r) 14 cyc\n\n  // vpmuludq only reads the bottom 32 bits of every 64-bit quadword.\n  // The even indices are already in the bottom 32 bits of a quadword, so we\n  // can leave them.\n  //            ┌──────┬──────┬─────┬───────┬───────┐\n  // |rhs_evn|: │ rhs₀ │ rhs₁ │ ... │ rhs₁₄ │ rhs₁₅ │\n  //            └──────┴──────┴─────┴───────┴───────┘\n  __m512i rhs_evn = rhs;\n  // Again, vpmuludq only reads the bottom 32 bits so we don't need to clear\n  // the top. But we do want to double the lhs.\n  //                ┌──────────┬──────────┬─────┬───────────┬───────────┐\n  // |lhs_evn_dbl|: │ 2 * lhs₀ │ 2 * lhs₁ │ ... │ 2 * lhs₁₄ │ 2 * lhs₁₅ │\n  //                └──────────┴──────────┴─────┴───────────┴───────────┘\n  __m512i lhs_evn_dbl = _mm512_add_epi32(lhs, lhs);\n  // Copy the high 32 bits in each quadword of rhs down to the low 32.\n  //            ┌──────┬──────┬─────┬───────┬───────┐\n  // |rhs_odd|: │ rhs₁ │ rhs₁ │ ... │ rhs₁₅ │ rhs₁₅ │\n  //            └──────┴──────┴─────┴───────┴───────┘\n  __m512i rhs_odd = movehdup_epi32(rhs);\n  // Right shift by 31 is equivalent to moving the high 32 bits down to the\n  // low 32, and then doubling it. So these are the odd indices in lhs, but\n  // doubled.\n  //                ┌──────────┬───┬─────┬───────────┬───┐\n  // |lhs_odd_dbl|: │ 2 * lhs₁ │ 0 │ ... │ 2 * lhs₁₅ │ 0 │\n  //                └──────────┴───┴─────┴───────────┴───┘\n  __m512i lhs_odd_dbl = _mm512_srli_epi64(lhs, 31);\n\n  // Multiply odd indices; since |lhs_odd_dbl| is doubled, these products are\n  // also doubled.\n  // clang-format off\n  //                 ┌─────────────────────┬─────────────────────┬─────┬───────────────────────┬───────────────────────┐\n  // |prod_odd_dbl|: │ Lo(2 * lhs₁ * rhs₁) │ Hi(2 * lhs₁ * rhs₁) | ... │ Lo(2 * lhs₁₅ * rhs₁₅) │ Hi(2 * lhs₁₅ * rhs₁₅) |\n  //                 └─────────────────────┴─────────────────────┴─────┴───────────────────────┴───────────────────────┘\n  // clang-format on\n  __m512i prod_odd_dbl = _mm512_mul_epu32(lhs_odd_dbl, rhs_odd);\n  // Multiply even indices; these are also doubled.\n  // clang-format off\n  //                 ┌─────────────────────┬─────────────────────┬─────┬───────────────────────┬───────────────────────┐\n  // |prod_odd_dbl|: │ Lo(2 * lhs₀ * rhs₀) │ Hi(2 * lhs₀ * rhs₀) | ... │ Lo(2 * lhs₁₄ * rhs₁₄) │ Hi(2 * lhs₁₄ * rhs₁₄) |\n  //                 └─────────────────────┴─────────────────────┴─────┴───────────────────────┴───────────────────────┘\n  // clang-format on\n  __m512i prod_evn_dbl = _mm512_mul_epu32(lhs_evn_dbl, rhs_evn);\n\n  // TODO(chokobole): Add diagram for better explanation.\n  // Move the low halves of odd products into odd positions; keep the low\n  // halves of even products in even positions (where they already are). Note\n  // that the products are doubled, so the result is a vector of all the low\n  // halves, but doubled.\n  __m512i prod_lo_dbl = mask_moveldup_epi32(prod_evn_dbl, kOdds, prod_odd_dbl);\n  // Move the high halves of even products into even positions, keeping the\n  // high halves of odd products where they are. The products are doubled, but\n  // we are looking at (prod >> 32), which cancels out the doubling, so this\n  // result is _not_ doubled.\n  __m512i prod_hi = mask_movehdup_epi32(prod_odd_dbl, kEvens, prod_evn_dbl);\n  // Right shift to undo the doubling.\n  __m512i prod_lo = _mm512_srli_epi32(prod_lo_dbl, 1);\n\n  // Standard addition of two 31-bit values.\n  return Add(prod_lo, prod_hi);\n}\n\n}  // namespace\n\nPackedMersenne31AVX512::PackedMersenne31AVX512(uint32_t value) {\n  __m512i vector = _mm512_set1_epi32(value);\n  _mm512_storeu_si512(values_.data(), vector);\n}\n\n// static\nvoid PackedMersenne31AVX512::Init() {\n  Mersenne31::Init();\n  kP = _mm512_set1_epi32(Mersenne31::Config::kModulus);\n  kZero = _mm512_set1_epi32(0);\n  kOne = _mm512_set1_epi32(1);\n  kMinusOne = _mm512_set1_epi32(Mersenne31::Config::kModulus - 1);\n  kTwoInv = _mm512_set1_epi32(Mersenne31::Config::kTwoInv);\n}\n\n// static\nPackedMersenne31AVX512 PackedMersenne31AVX512::Zero() {\n  return FromVector(kZero);\n}\n\n// static\nPackedMersenne31AVX512 PackedMersenne31AVX512::One() {\n  return FromVector(kOne);\n}\n\n// static\nPackedMersenne31AVX512 PackedMersenne31AVX512::MinusOne() {\n  return FromVector(kMinusOne);\n}\n\n// static\nPackedMersenne31AVX512 PackedMersenne31AVX512::TwoInv() {\n  return FromVector(kTwoInv);\n}\n\n// static\nPackedMersenne31AVX512 PackedMersenne31AVX512::Broadcast(\n    const PrimeField& value) {\n  return FromVector(_mm512_set1_epi32(value.value()));\n}\n\nPackedMersenne31AVX512 PackedMersenne31AVX512::Add(\n    const PackedMersenne31AVX512& other) const {\n  return FromVector(math::Add(ToVector(*this), ToVector(other)));\n}\n\nPackedMersenne31AVX512 PackedMersenne31AVX512::Sub(\n    const PackedMersenne31AVX512& other) const {\n  return FromVector(math::Sub(ToVector(*this), ToVector(other)));\n}\n\nPackedMersenne31AVX512 PackedMersenne31AVX512::Negate() const {\n  return FromVector(math::Negate(ToVector(*this)));\n}\n\nPackedMersenne31AVX512 PackedMersenne31AVX512::Mul(\n    const PackedMersenne31AVX512& other) const {\n  return FromVector(math::Mul(ToVector(*this), ToVector(other)));\n}\n\n}  // namespace tachyon::math\n\n#endif  // defined(TACHYON_HAS_AVX512)\n"
  },
  {
    "path": "tachyon/math/finite_fields/mersenne31/internal/packed_mersenne31_avx512.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_MATH_FINITE_FIELDS_MERSENNE31_INTERNAL_PACKED_MERSENNE31_AVX512_H_\n#define TACHYON_MATH_FINITE_FIELDS_MERSENNE31_INTERNAL_PACKED_MERSENNE31_AVX512_H_\n\n#include <stddef.h>\n\n#include \"tachyon/export.h\"\n#include \"tachyon/math/finite_fields/mersenne31/internal/mersenne31.h\"\n#include \"tachyon/math/finite_fields/packed_prime_field_base.h\"\n\nnamespace tachyon::math {\n\nclass PackedMersenne31AVX512;\n\ntemplate <>\nstruct PackedFieldTraits<PackedMersenne31AVX512> {\n  using Field = Mersenne31;\n\n  constexpr static size_t N = 16;\n};\n\nclass TACHYON_EXPORT PackedMersenne31AVX512 final\n    : public PackedPrimeFieldBase<PackedMersenne31AVX512> {\n public:\n  using PrimeField = Mersenne31;\n\n  constexpr static size_t N = PackedFieldTraits<PackedMersenne31AVX512>::N;\n\n  PackedMersenne31AVX512() = default;\n  // NOTE(chokobole): This is needed by Eigen matrix.\n  explicit PackedMersenne31AVX512(uint32_t value);\n  PackedMersenne31AVX512(const PackedMersenne31AVX512& other) = default;\n  PackedMersenne31AVX512& operator=(const PackedMersenne31AVX512& other) =\n      default;\n  PackedMersenne31AVX512(PackedMersenne31AVX512&& other) = default;\n  PackedMersenne31AVX512& operator=(PackedMersenne31AVX512&& other) = default;\n\n  static void Init();\n\n  static PackedMersenne31AVX512 Zero();\n\n  static PackedMersenne31AVX512 One();\n\n  static PackedMersenne31AVX512 MinusOne();\n\n  static PackedMersenne31AVX512 TwoInv();\n\n  static PackedMersenne31AVX512 Broadcast(const PrimeField& value);\n\n  // AdditiveSemigroup methods\n  PackedMersenne31AVX512 Add(const PackedMersenne31AVX512& other) const;\n\n  // AdditiveGroup methods\n  PackedMersenne31AVX512 Sub(const PackedMersenne31AVX512& other) const;\n\n  PackedMersenne31AVX512 Negate() const;\n\n  // MultiplicativeSemigroup methods\n  PackedMersenne31AVX512 Mul(const PackedMersenne31AVX512& other) const;\n};\n\n}  // namespace tachyon::math\n\n#endif  //  TACHYON_MATH_FINITE_FIELDS_MERSENNE31_INTERNAL_PACKED_MERSENNE31_AVX512_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/mersenne31/internal/packed_mersenne31_neon.cc",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#include \"tachyon/math/finite_fields/mersenne31/internal/packed_mersenne31_neon.h\"\n\n#include <arm_neon.h>\n\n#include \"tachyon/math/finite_fields/packed_prime_field32_neon.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\nuint32x4_t kP;\nuint32x4_t kZero;\nuint32x4_t kOne;\nuint32x4_t kMinusOne;\nuint32x4_t kTwoInv;\n\nuint32x4_t ToVector(const PackedMersenne31Neon& packed) {\n  return vld1q_u32(reinterpret_cast<const uint32_t*>(packed.values().data()));\n}\n\nPackedMersenne31Neon FromVector(uint32x4_t vector) {\n  PackedMersenne31Neon ret;\n  vst1q_u32(reinterpret_cast<uint32_t*>(ret.values().data()), vector);\n  return ret;\n}\n\nuint32x4_t Add(uint32x4_t lhs, uint32x4_t rhs) {\n  return AddMod32(lhs, rhs, kP);\n}\n\nuint32x4_t Sub(uint32x4_t lhs, uint32x4_t rhs) {\n  return SubMod32(lhs, rhs, kP);\n}\n\nuint32x4_t Negate(uint32x4_t val) { return NegateMod32(val, kP); }\n\n// Multiply two 31-bit numbers to obtain a 62-bit immediate result, and return\n// the high 31 bits of that result. Results are arbitrary if the inputs do not\n// fit in 31 bits.\nuint32x4_t mul_31x31_to_hi_31(uint32x4_t lhs, uint32x4_t rhs) {\n  return vreinterpretq_u32_s32(\n      vqdmulhq_s32(vreinterpretq_s32_u32(lhs), vreinterpretq_s32_u32(rhs)));\n}\n\nuint32x4_t Mul(uint32x4_t lhs, uint32x4_t rhs) {\n  // We want this to compile to:\n  //      sqdmulh  prod_hi31.4s, lhs.4s, rhs.4s\n  //      mul      t.4s, lhs.4s, rhs.4s\n  //      mla      t.4s, prod_hi31.4s, P.4s\n  //      sub      u.4s, t.4s, P.4s\n  //      umin     r.4s, t.4s, u.4s\n  // throughput: 1.25 cyc/vec (3.2 els/cyc)\n  // latency: 10 cyc\n\n  // Let prod := lhs * rhs\n  //     prod_hi31 := (prod >> 2³¹) (mod 2³¹)\n  //               = a₃₁ + 2 * a₃₂ + 2² * a₃₃ + ... + 2³⁰ * a₆₂\n  //     prod_lo31 := prod (mod 2³¹) = a₀ + 2 * a₁ + 2² * a₂ + ... + 2³⁰ * a₃₀\n  //     prod_lo32 := prod (mod 2³²) = a₀ + 2 * a₁ + 2² * a₂ + ... + 2³¹ * a₃₁\n  //     t := prod_hi31 + prod_lo31\n  //\n  //     prod = 2³¹ * prod_hi31 + prod_lo31 (mod P)\n  //          = (2³¹ - 1) * prod_hi31 + prod_hi31 + prod_lo31 (mod P)\n  //          = P * prod_hi31 + prod_hi31 + prod_lo31 (mod P)\n  //          = prod_hi31 + prod_lo31 (mod P)\n  //          = t (mod P)\n  //\n  //     prod_lo32 = prod_lo31 + 2³¹ (prod_hi31 mod 2)\n  //               = prod_lo31 + 2³¹ prod_hi31 (mod 2³²)\n  //\n  //     2³¹ * prod_hi31 = 2³¹ (a₃₁ + 2 * a₃₂ + ... + 2³⁰ * a₆₂) (mod 2³²)\n  //                     = 2³¹ * a₃₁ + 2³²(a₃₂ + ... + 2²⁹ * a₆₂) (mod 2³²)\n  //                     = 2³¹ * a₃₁ (mod 2³²)\n  //\n  //     t = prod_lo31 + prod_hi31 (mod 2³²)\n  //       = prod_lo32 - 2³¹ * a₃₁ + prod_hi31 (mod 2³²)\n  //       = prod_lo32 - 2³¹ * prod_hi31 + prod_hi31 (mod 2³²)\n  //       = prod_lo32 - (2³¹ - 1) prod_hi31 (mod 2³²)\n  //       = prod_lo32 - P * prod_hi31 (mod 2³²)\n  //\n  // 0 ≤ t ≤ 2³² - 1 = 2(P + 1) - 1 = 2P - 1\n  // So, ReduceSum32(t) is in range {0, ..., P - 1}.\n  uint32x4_t prod_hi31 = mul_31x31_to_hi_31(lhs, rhs);\n  uint32x4_t prod_lo32 = vmulq_u32(lhs, rhs);\n  uint32x4_t t = vmlsq_u32(prod_lo32, prod_hi31, kP);\n  return ReduceSum32(t, kP);\n}\n\n}  // namespace\n\nPackedMersenne31Neon::PackedMersenne31Neon(uint32_t value) {\n  uint32x4_t vector = vdupq_n_u32(value);\n  vst1q_u32(reinterpret_cast<uint32_t*>(values_.data()), vector);\n}\n\n// static\nvoid PackedMersenne31Neon::Init() {\n  Mersenne31::Init();\n  kP = vdupq_n_u32(Mersenne31::Config::kModulus);\n  kZero = vdupq_n_u32(0);\n  kOne = vdupq_n_u32(1);\n  kMinusOne = vdupq_n_u32(Mersenne31::Config::kModulus - 1);\n  kTwoInv = vdupq_n_u32(Mersenne31::Config::kTwoInv);\n}\n\n// static\nPackedMersenne31Neon PackedMersenne31Neon::Zero() { return FromVector(kZero); }\n\n// static\nPackedMersenne31Neon PackedMersenne31Neon::One() { return FromVector(kOne); }\n\n// static\nPackedMersenne31Neon PackedMersenne31Neon::MinusOne() {\n  return FromVector(kMinusOne);\n}\n\n// static\nPackedMersenne31Neon PackedMersenne31Neon::TwoInv() {\n  return FromVector(kTwoInv);\n}\n\n// static\nPackedMersenne31Neon PackedMersenne31Neon::Broadcast(const PrimeField& value) {\n  return FromVector(vdupq_n_u32(value.value()));\n}\n\nPackedMersenne31Neon PackedMersenne31Neon::Add(\n    const PackedMersenne31Neon& other) const {\n  return FromVector(math::Add(ToVector(*this), ToVector(other)));\n}\n\nPackedMersenne31Neon PackedMersenne31Neon::Sub(\n    const PackedMersenne31Neon& other) const {\n  return FromVector(math::Sub(ToVector(*this), ToVector(other)));\n}\n\nPackedMersenne31Neon PackedMersenne31Neon::Negate() const {\n  return FromVector(math::Negate(ToVector(*this)));\n}\n\nPackedMersenne31Neon PackedMersenne31Neon::Mul(\n    const PackedMersenne31Neon& other) const {\n  return FromVector(math::Mul(ToVector(*this), ToVector(other)));\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/finite_fields/mersenne31/internal/packed_mersenne31_neon.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_MATH_FINITE_FIELDS_MERSENNE31_INTERNAL_PACKED_MERSENNE31_NEON_H_\n#define TACHYON_MATH_FINITE_FIELDS_MERSENNE31_INTERNAL_PACKED_MERSENNE31_NEON_H_\n\n#include <stddef.h>\n\n#include \"tachyon/export.h\"\n#include \"tachyon/math/finite_fields/mersenne31/internal/mersenne31.h\"\n#include \"tachyon/math/finite_fields/packed_prime_field_base.h\"\n\nnamespace tachyon::math {\n\nclass PackedMersenne31Neon;\n\ntemplate <>\nstruct PackedFieldTraits<PackedMersenne31Neon> {\n  using Field = Mersenne31;\n\n  constexpr static size_t N = 4;\n};\n\nclass TACHYON_EXPORT PackedMersenne31Neon final\n    : public PackedPrimeFieldBase<PackedMersenne31Neon> {\n public:\n  using PrimeField = Mersenne31;\n\n  constexpr static size_t N = PackedFieldTraits<PackedMersenne31Neon>::N;\n\n  PackedMersenne31Neon() = default;\n  // NOTE(chokobole): This is needed by Eigen matrix.\n  explicit PackedMersenne31Neon(uint32_t value);\n  PackedMersenne31Neon(const PackedMersenne31Neon& other) = default;\n  PackedMersenne31Neon& operator=(const PackedMersenne31Neon& other) = default;\n  PackedMersenne31Neon(PackedMersenne31Neon&& other) = default;\n  PackedMersenne31Neon& operator=(PackedMersenne31Neon&& other) = default;\n\n  static void Init();\n\n  static PackedMersenne31Neon Zero();\n\n  static PackedMersenne31Neon One();\n\n  static PackedMersenne31Neon MinusOne();\n\n  static PackedMersenne31Neon TwoInv();\n\n  static PackedMersenne31Neon Broadcast(const PrimeField& value);\n\n  // AdditiveSemigroup methods\n  PackedMersenne31Neon Add(const PackedMersenne31Neon& other) const;\n\n  // AdditiveGroup methods\n  PackedMersenne31Neon Sub(const PackedMersenne31Neon& other) const;\n\n  PackedMersenne31Neon Negate() const;\n\n  // MultiplicativeSemigroup methods\n  PackedMersenne31Neon Mul(const PackedMersenne31Neon& other) const;\n};\n\n}  // namespace tachyon::math\n\n#endif  //  TACHYON_MATH_FINITE_FIELDS_MERSENNE31_INTERNAL_PACKED_MERSENNE31_NEON_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/mersenne31/mersenne31.h",
    "content": "#ifndef TACHYON_MATH_FINITE_FIELDS_MERSENNE31_MERSENNE31_H_\n#define TACHYON_MATH_FINITE_FIELDS_MERSENNE31_MERSENNE31_H_\n\n#include \"tachyon/math/finite_fields/mersenne31/internal/packed_mersenne31.h\"\n\nnamespace tachyon::math {\n\ntemplate <>\nstruct PackedFieldTraits<Mersenne31> {\n  using PackedField = PackedMersenne31;\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_MERSENNE31_MERSENNE31_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/modulus.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_FINITE_FIELDS_MODULUS_H_\n#define TACHYON_MATH_FINITE_FIELDS_MODULUS_H_\n\n#include <limits>\n\n#include \"tachyon/math/base/big_int.h\"\n\nnamespace tachyon::math {\n\ntemplate <size_t N>\nclass Modulus {\n public:\n  // Can we use the no-carry optimization for multiplication\n  // outlined [here](https://hackmd.io/@gnark/modular_multiplication)?\n  //\n  // This optimization applies if\n  // (a) `modulus[biggest_limb_idx] < max(uint64_t) >> 1`, and\n  // (b) the bits of the modulus are not all 1.\n  constexpr static bool CanUseNoCarryMulOptimization(const BigInt<N>& modulus) {\n    uint64_t biggest_limb = modulus[BigInt<N>::kBiggestLimbIdx];\n    bool top_bit_is_zero = biggest_limb >> 63 == 0;\n    bool all_remain_bits_are_one =\n        biggest_limb == std::numeric_limits<uint64_t>::max() >> 1;\n    FOR_BUT_SMALLEST(i, N) {\n      all_remain_bits_are_one &=\n          modulus[N - i - 1] == std::numeric_limits<uint64_t>::max();\n    }\n    return top_bit_is_zero && !all_remain_bits_are_one;\n  }\n\n  // Does the modulus have a spare unused bit?\n  //\n  // This condition applies if\n  // (a) `modulus[biggest_limb_idx] >> 63 == 0`\n  constexpr static bool HasSpareBit(const BigInt<N>& modulus) {\n    uint64_t biggest_limb = modulus[BigInt<N>::kBiggestLimbIdx];\n    return biggest_limb >> 63 == 0;\n  }\n\n  constexpr static BigInt<N> MontgomeryR(const BigInt<N>& modulus) {\n    BigInt<N + 1> two_pow_n_times_64(0);\n    two_pow_n_times_64.biggest_limb() = uint64_t{1};\n    return (two_pow_n_times_64 % modulus.template Extend<N + 1>())\n        .template Shrink<N>();\n  }\n\n  constexpr static BigInt<N> MontgomeryR2(const BigInt<N>& modulus) {\n    BigInt<2 * N + 1> two_pow_n_times_64_square(0);\n    two_pow_n_times_64_square.biggest_limb() = uint64_t{1};\n    return (two_pow_n_times_64_square % modulus.template Extend<2 * N + 1>())\n        .template Shrink<N>();\n  }\n\n  constexpr static BigInt<N> MontgomeryR3(const BigInt<N>& modulus) {\n    BigInt<3 * N + 1> two_pow_n_times_64_cubic(0);\n    two_pow_n_times_64_cubic.biggest_limb() = uint64_t{1};\n    return (two_pow_n_times_64_cubic % modulus.template Extend<3 * N + 1>())\n        .template Shrink<N>();\n  }\n\n  // Compute -M⁻¹ mod 2ᴮ.\n  template <typename T, size_t B = 8 * sizeof(T),\n            std::enable_if_t<std::is_unsigned_v<T>>* = nullptr>\n  constexpr static T Inverse(const BigInt<N>& modulus) {\n    // We compute this as follows.\n    // First, modulus mod 2ᴮ is just the lower B bits of modulus.\n    // Hence modulus mod 2ᴮ = modulus[0] mod 2ᴮ.\n    //\n    // Next, computing the inverse mod 2ᴮ involves exponentiating by\n    // the multiplicative group order, which is euler_totient(2ᴮ) - 1.\n    // Now, euler_totient(2ᴮ) = 1 << (B - 1), and so\n    // euler_totient(2ᴮ) - 1 = (1 << (B - 1)) - 1 = 1111111... ((B - 1)\n    // digits). We compute this powering via standard square and multiply.\n    T inv = 1;\n    for (size_t i = 0; i < (B - 1); ++i) {\n      // Square\n      inv *= inv;\n      // Multiply\n      inv *= static_cast<T>(modulus[0]);\n    }\n    return -inv;\n  }\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_MODULUS_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/modulus_unittest.cc",
    "content": "#include \"tachyon/math/finite_fields/modulus.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/elliptic_curves/bn/bn254/fq.h\"\n\nnamespace tachyon::math {\n\nTEST(ModulusTest, CanUseNoCarryMulOptimization) {\n  BigInt<1> modulus = *BigInt<1>::FromHexString(\"0x8000000000000000\");\n  EXPECT_FALSE(Modulus<1>::CanUseNoCarryMulOptimization(modulus));\n  modulus = *BigInt<1>::FromHexString(\"0x7fffffffffffffff\");\n  EXPECT_FALSE(Modulus<1>::CanUseNoCarryMulOptimization(modulus));\n  modulus = *BigInt<1>::FromHexString(\"0x7000000000000000\");\n  EXPECT_TRUE(Modulus<1>::CanUseNoCarryMulOptimization(modulus));\n}\n\nTEST(ModulusTest, HasSpareBit) {\n  BigInt<1> modulus = *BigInt<1>::FromHexString(\"0x8000000000000000\");\n  EXPECT_FALSE(Modulus<1>::HasSpareBit(modulus));\n  modulus = *BigInt<1>::FromHexString(\"0x7000000000000000\");\n  EXPECT_TRUE(Modulus<1>::HasSpareBit(modulus));\n}\n\nTEST(ModulusTest, Montgomery) {\n  EXPECT_EQ(Modulus<4>::MontgomeryR(bn254::FqConfig::kModulus),\n            BigInt<4>({\n                UINT64_C(15230403791020821917),\n                UINT64_C(754611498739239741),\n                UINT64_C(7381016538464732716),\n                UINT64_C(1011752739694698287),\n            }));\n  EXPECT_EQ(Modulus<4>::MontgomeryR2(bn254::FqConfig::kModulus),\n            BigInt<4>({\n                UINT64_C(17522657719365597833),\n                UINT64_C(13107472804851548667),\n                UINT64_C(5164255478447964150),\n                UINT64_C(493319470278259999),\n            }));\n}\n\nTEST(ModulusTest, Inverse) {\n  uint32_t inv32 = Modulus<4>::Inverse<uint32_t>(bn254::FqConfig::kModulus);\n  EXPECT_EQ(inv32, UINT32_C(3834012553));\n  EXPECT_EQ(static_cast<uint32_t>(inv32 * bn254::FqConfig::kModulus[0]), -1);\n  uint64_t inv64 = Modulus<4>::Inverse<uint64_t>(bn254::FqConfig::kModulus);\n  EXPECT_EQ(inv64, UINT64_C(9786893198990664585));\n  EXPECT_EQ(inv64 * bn254::FqConfig::kModulus[0], -1);\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/finite_fields/packed_field_traits_forward.h",
    "content": "#ifndef TACHYON_MATH_FINITE_FIELDS_PACKED_FIELD_TRAITS_FORWARD_H_\n#define TACHYON_MATH_FINITE_FIELDS_PACKED_FIELD_TRAITS_FORWARD_H_\n\nnamespace tachyon::math {\n\ntemplate <typename T, typename SFINAE = void>\nstruct PackedFieldTraits;\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_PACKED_FIELD_TRAITS_FORWARD_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/packed_prime_field32_avx2.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_MATH_FINITE_FIELDS_PACKED_PRIME_FIELD32_AVX2_H_\n#define TACHYON_MATH_FINITE_FIELDS_PACKED_PRIME_FIELD32_AVX2_H_\n\n#include <immintrin.h>\n\n#include \"tachyon/base/compiler_specific.h\"\n#include \"tachyon/base/functional/callback.h\"\n\nnamespace tachyon::math {\n\nALWAYS_INLINE __m256i AddMod32(__m256i lhs, __m256i rhs, __m256i p) {\n  // NOTE(chokobole): This assumes 2p < 2³², where p is the modulus.\n  // We want this to compile to:\n  //      vpaddd   t, lhs, rhs\n  //      vpsubd   u, t, p\n  //      vpminud  r, t, u\n  // throughput: 1 cyc/vec (8 els/cyc)\n  // latency: 3 cyc\n\n  // Let t := lhs + rhs\n  //     u := (t - p) mod 2³²\n  //     r := min(t, u)\n  //     m := { p     if this is montgomery form, which is 0\n  //          { p - 1 otherwise\n  //\n  // 0 ≤ lhs, rhs ≤ m\n  //\n  // 1) (lhs = p && rhs = 0) || (lhs = 0 && rhs = p)\n  //    t = p\n  //    u = 0\n  //    r = 0\n  //\n  // 2) lhs = p && rhs = p\n  //    t = 2p\n  //    u = p\n  //    r = p, which is 0.\n  //\n  // 3) (lhs = p && 1 ≤ rhs ≤ p - 1) || (1 ≤ lhs ≤ p - 1 && rhs = p)\n  //    p + 1 ≤ t ≤ 2p - 1, go to 5)\n  //\n  // 4) 0 ≤ t ≤ p - 1\n  //    p < 2³² - p ≤ u ≤ 2³² - 1\n  //    r = t\n  //\n  // 5) p + 1 ≤ t ≤ 2p - 1\n  //    1 ≤ u ≤ p - 1 ≤ p\n  //    r = u\n  //\n  // In all cases, r is in {0, ..., m}.\n  __m256i t = _mm256_add_epi32(lhs, rhs);\n  __m256i u = _mm256_sub_epi32(t, p);\n  return _mm256_min_epu32(t, u);\n}\n\nALWAYS_INLINE __m256i SubMod32(__m256i lhs, __m256i rhs, __m256i p) {\n  // NOTE(chokobole): This assumes 2p < 2³², where p is the modulus.\n  // We want this to compile to:\n  //      vpsubd   t, lhs, rhs\n  //      vpaddd   u, t, p\n  //      vpminud  r, t, u\n  // throughput: 1 cyc/vec (8 els/cyc)\n  // latency: 3 cyc\n\n  // Let d := lhs - rhs\n  //     t := d mod 2³²\n  //     u := (t + p) mod 2³²\n  //     r := min(t, u)\n  //     m := { p     if this is montgomery form, which is 0\n  //          { p - 1 otherwise\n  //\n  // 0 ≤ lhs, rhs ≤ m\n  //\n  // 1) lhs = p && rhs = 0\n  //    d = p\n  //    t = p\n  //    r = p, which is 0.\n  //\n  // 2) lhs = 0 && rhs = p\n  //    d = -p\n  //    t = 2³² - p\n  //    u = 0\n  //    r = 0\n  //\n  // 3) lhs = p && rhs = p\n  //    d = 0\n  //    t = 0\n  //    u = p\n  //    r = 0\n  //\n  // 4) lhs = p && 1 ≤ rhs ≤ p - 1\n  //    1 ≤ d ≤ p - 1, go to 6)\n  //\n  // 5) 1 ≤ lhs ≤ p - 1 && rhs = p\n  //    -p + 1 ≤ d ≤ -1, go to 7)\n  //\n  // 6) 1 ≤ d ≤ p - 1\n  //    1 ≤ t ≤ p - 1\n  //    p + 1 ≤ u ≤ 2p - 1\n  //    r = t\n  //\n  // 7) -p + 1 ≤ d ≤ -1\n  //    2³² - p + 1 ≤ t ≤ 2³² - 1\n  //    1 ≤ u ≤ p - 1\n  //    r = u\n  //\n  // In all cases, r is in {0, ..., m}.\n  __m256i t = _mm256_sub_epi32(lhs, rhs);\n  __m256i u = _mm256_add_epi32(t, p);\n  return _mm256_min_epu32(t, u);\n}\n\nALWAYS_INLINE __m256i NegateMod32(__m256i val, __m256i p) {\n  // We want this to compile to:\n  //      vpsubd   t, p, val\n  //      vpsignd  r, t, val\n  // throughput: .67 cyc/vec (12 els/cyc)\n  // latency: 2 cyc\n\n  // Let t := (p - val) mod 2³²\n  //     r := vpsignd(t, val)\n  //                      { x            if y > 0\n  //     vpsignd(x, y) := { 0            if y = 0\n  //                      { -x mod 2³²   if y < 0\n  //     m := { p     if this is montgomery form, which is 0\n  //          { p - 1 otherwise\n  //\n  // 0 ≤ val ≤ m\n  //\n  // 1) val = 0\n  //    r = 0\n  //\n  // 2) val = p\n  //    r = t\n  //    t = 0\n  //\n  // 3) 1 ≤ val ≤ p - 1\n  //    r = t\n  //    2³² - p + 1 ≤ -val ≤ 2³² - 1\n  //    1 ≤ t ≤ p - 1\n  //\n  // In all cases, r is in {0, ..., m}.\n  __m256i t = _mm256_sub_epi32(p, val);\n  return _mm256_sign_epi32(t, val);\n}\n\n// MONTGOMERY MULTIPLICATION\n//   This implementation is based on [1] but with minor changes. The reduction\n//   is as follows:\n//\n// Constants: P = modulus of the prime field\n//            B = 2³²\n//            μ = P⁻¹ mod B\n// Input: 0 ≤ C < P * B\n// Output: 0 ≤ R < P such that R = C * B⁻¹ (mod P)\n//   1. Q := μ * C mod B\n//   2. D := (C - Q * P) / B\n//   3. R := if D < 0 then D + P else D\n//\n// We first show that the division in step 2. is exact. It suffices to show that\n// C = Q * P (mod B). By definition of Q and μ, we have\n// Q * P = μ * C * P = P⁻¹ * C * P = C (mod B).\n// We also have C - Q * P = C (mod P), so thus D = C * B⁻¹ (mod P).\n//\n// It remains to show that R is in the correct range. It suffices to show that\n// -P ≤ D < P. We know that 0 ≤ C < P * B and 0 ≤ Q * P < P * B.\n// Then -P * B < C - Q * P < P * B and -P < D < P, as desired.\n//\n// [1] Modern Computer Arithmetic, Richard Brent and Paul Zimmermann,\n//     Cambridge University Press, 2010, algorithm 2.7.\n\n// We provide 2 variants of Montgomery reduction depending on if the inputs are\n// unsigned or signed. The unsigned variant follows steps 1 and 2 in the above\n// protocol to produce D in (-P, ..., P). For the signed variant we assume -PB/2\n// < C < PB/2 and let Q := μ C mod B be the unique representative in [-B/2, ...,\n// B/2 - 1]. The division in step 2 is clearly still exact and |C - Q P| <= |C|\n// + |Q||P| < PB so D still lies in (-P, ..., P).\n\n// Perform a partial Montgomery reduction on each 64 bit element.\n// Input must lie in {0, ..., 2³²P}.\n// The output will lie in {-P, ..., P} and be stored in the upper 32 bits.\nALWAYS_INLINE __m256i PartialMontyRedUnsignedToSigned(__m256i input, __m256i p,\n                                                      __m256i inv) {\n  __m256i q = _mm256_mul_epu32(input, inv);\n  __m256i q_p = _mm256_mul_epu32(q, p);\n  // By construction, the bottom 32 bits of input and q_p are equal.\n  // Thus |_mm256_sub_epi32| and |_mm256_sub_epi64| should act identically.\n  // However for some reason, the compiler gets confused if we use\n  // |_mm256_sub_epi64| and outputs a load of nonsense, see:\n  // https://godbolt.org/z/3W8M7Tv84.\n  return _mm256_sub_epi32(input, q_p);\n}\n// Perform a partial Montgomery reduction on each 64 bit element.\n// Input must lie in {-2³¹P, ..., 2³¹P}.\n// The output will lie in {-P, ..., P} and be stored in the upper 32 bits.\nALWAYS_INLINE __m256i PartialMontyRedSignedToSigned(__m256i input, __m256i p,\n                                                    __m256i inv) {\n  __m256i q = _mm256_mul_epi32(input, inv);\n  __m256i q_p = _mm256_mul_epi32(q, p);\n  // Unlike the previous case the compiler output is essentially identical\n  // between |_mm256_sub_epi32| and |_mm256_sub_epi64|. We use\n  // |_mm256_sub_epi32| again just for consistency.\n  return _mm256_sub_epi32(input, q_p);\n}\n\n// Multiply the field elements in the even index entries.\n// |lhs[2i]|, |rhs[2i]| must be unsigned 32-bit integers such that\n// |lhs[2i]| * |rhs[2i]| lies in {0, ..., 2³²P}.\n// The output will lie in {-P, ..., P} and be stored in |output[2i + 1]|.\nALWAYS_INLINE __m256i MontyMul(__m256i lhs, __m256i rhs, __m256i p,\n                               __m256i inv) {\n  __m256i prod = _mm256_mul_epu32(lhs, rhs);\n  return PartialMontyRedSignedToSigned(prod, p, inv);\n}\n\n// Multiply the field elements in the even index entries.\n// |lhs[2i]|, |rhs[2i]| must be signed 32-bit integers such that\n// |lhs[2i]| * |rhs[2i]| lies in {-2³¹P, ..., 2³¹P}.\n// The output will lie in {-P, ..., P} stored in |output[2i + 1]|.\nALWAYS_INLINE __m256i MontyMulSigned(__m256i lhs, __m256i rhs, __m256i p,\n                                     __m256i inv) {\n  __m256i prod = _mm256_mul_epi32(lhs, rhs);\n  return PartialMontyRedSignedToSigned(prod, p, inv);\n}\n\nALWAYS_INLINE __m256i movehdup_epi32(__m256i x) {\n  // This instruction is only available in the floating-point flavor; this\n  // distinction is only for historical reasons and no longer matters. We cast\n  // to floats, duplicate, and cast back.\n  return _mm256_castps_si256(_mm256_movehdup_ps(_mm256_castsi256_ps(x)));\n}\n\nALWAYS_INLINE __m256i MontMulMod32(__m256i lhs, __m256i rhs, __m256i p,\n                                   __m256i inv) {\n  // We want this to compile to:\n  //      vmovshdup  lhs_odd, lhs\n  //      vmovshdup  rhs_odd, rhs\n  //      vpmuludq   prod_evn, lhs, rhs\n  //      vpmuludq   prod_odd, lhs_odd, rhs_odd\n  //      vpmuludq   q_evn, prod_evn, inv\n  //      vpmuludq   q_odd, prod_odd, inv\n  //      vpmuludq   q_p_evn, q_evn, p\n  //      vpmuludq   q_p_odd, q_odd, p\n  //      vpsubq     d_evn, prod_evn, q_p_evn\n  //      vpsubq     d_odd, prod_odd, q_p_odd\n  //      vmovshdup  d_evn_hi, d_evn\n  //      vpblendd   t, d_evn_hi, d_odd, aah\n  //      vpaddd     u, t, p\n  //      vpminud    r, t, u\n  // throughput: 4.67 cyc/vec (1.71 els/cyc)\n  // latency: 21 cyc\n\n  __m256i lhs_evn = lhs;\n  __m256i rhs_evn = rhs;\n  __m256i lhs_odd = movehdup_epi32(lhs);\n  __m256i rhs_odd = movehdup_epi32(rhs);\n\n  __m256i d_evn = MontyMul(lhs_evn, rhs_evn, p, inv);\n  __m256i d_odd = MontyMul(lhs_odd, rhs_odd, p, inv);\n\n  __m256i d_evn_hi = movehdup_epi32(d_evn);\n  __m256i t = _mm256_blend_epi32(d_evn_hi, d_odd, 0b10101010);\n\n  __m256i u = _mm256_add_epi32(t, p);\n  return _mm256_min_epu32(t, u);\n}\n\n// Square the field elements in the even index entries.\n// Inputs must be signed 32-bit integers.\n// Outputs will be a signed integer in (-P, ..., P) copied into both the even\n// and odd indices.\nALWAYS_INLINE __m256i ShiftedSquare(__m256i input, __m256i p, __m256i inv) {\n  // Note that we do not need a restriction on the size of |input[i]²| as\n  // 2³⁰ < P and |i32| <= 2³¹ and so => |input[i]²| <= 2⁶² < 2³²P.\n  __m256i square = _mm256_mul_epi32(input, input);\n  __m256i square_red = PartialMontyRedSignedToSigned(square, p, inv);\n  return movehdup_epi32(square_red);\n}\n\n// Apply callback to the even and odd indices of the input vector.\n// callback should only depend in the 32 bit entries in the even indices.\n// The output of callback must lie in (-P, ..., P) and be stored in the odd\n// indices. The even indices of the output of callback will not be read. The\n// input should conform to the requirements of |callback|.\n// NOTE(chokobole): This is to suppress the error below.\n// clang-format off\n// error: ignoring attributes on template argument '__m256i(__m256i, __m256i, __m256i)' [-Werror=ignored-attributes]\n// clang-format on\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wignored-attributes\"\nALWAYS_INLINE __m256i ApplyFuncToEvenOdd(\n    __m256i input, __m256i p, __m256i inv,\n    base::RepeatingCallback<__m256i(__m256i, __m256i, __m256i)> callback) {\n  __m256i input_evn = input;\n  __m256i input_odd = movehdup_epi32(input);\n  __m256i d_evn = callback.Run(input_evn, p, inv);\n  __m256i d_odd = callback.Run(input_odd, p, inv);\n  __m256i d_evn_hi = movehdup_epi32(d_evn);\n  __m256i t = _mm256_blend_epi32(d_evn_hi, d_odd, 0b10101010);\n  __m256i u = _mm256_add_epi32(t, p);\n  return _mm256_min_epu32(t, u);\n}\n#pragma GCC diagnostic pop\n\n// Cube the field elements in the even index entries.\n// Inputs must be signed 32-bit integers in [-P, ..., P].\n// Outputs will be a signed integer in (-P, ..., P) stored in the odd indices.\nALWAYS_INLINE __m256i DoExp3(__m256i input, __m256i p, __m256i inv) {\n  __m256i square = ShiftedSquare(input, p, inv);\n  return MontyMulSigned(square, input, p, inv);\n}\n\nALWAYS_INLINE __m256i Exp3(__m256i input, __m256i p, __m256i inv) {\n  return ApplyFuncToEvenOdd(input, p, inv, &DoExp3);\n}\n\n// Take the fifth power of the field elements in the even index\n// entries. Inputs must be signed 32-bit integers in [-P, ..., P]. Outputs will\n// be a signed integer in (-P, ..., P) stored in the odd indices.\nALWAYS_INLINE __m256i DoExp5(__m256i input, __m256i p, __m256i inv) {\n  __m256i square = ShiftedSquare(input, p, inv);\n  __m256i quad = ShiftedSquare(square, p, inv);\n  return MontyMulSigned(quad, input, p, inv);\n}\n\nALWAYS_INLINE __m256i Exp5(__m256i input, __m256i p, __m256i inv) {\n  return ApplyFuncToEvenOdd(input, p, inv, &DoExp5);\n}\n\n/// Take the seventh power of the field elements in the even index\n/// entries. Inputs must lie in [-P, ..., P]. Outputs will also lie in (-P, ...,\n/// P) stored in the odd indices.\nALWAYS_INLINE __m256i DoExp7(__m256i input, __m256i p, __m256i inv) {\n  __m256i square = ShiftedSquare(input, p, inv);\n  __m256i cube = MontyMulSigned(square, input, p, inv);\n  __m256i cube_shifted = movehdup_epi32(cube);\n  __m256i quad = ShiftedSquare(square, p, inv);\n  return MontyMulSigned(quad, cube_shifted, p, inv);\n}\n\nALWAYS_INLINE __m256i Exp7(__m256i input, __m256i p, __m256i inv) {\n  return ApplyFuncToEvenOdd(input, p, inv, &DoExp7);\n}\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_PACKED_PRIME_FIELD32_AVX2_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/packed_prime_field32_avx512.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_MATH_FINITE_FIELDS_PACKED_PRIME_FIELD32_AVX512_H_\n#define TACHYON_MATH_FINITE_FIELDS_PACKED_PRIME_FIELD32_AVX512_H_\n\n#include <immintrin.h>\n\n#include \"tachyon/base/compiler_specific.h\"\n\nnamespace tachyon::math {\n\nALWAYS_INLINE __m512i AddMod32(__m512i lhs, __m512i rhs, __m512i p) {\n  // NOTE(chokobole): This assumes 2p < 2³², where p is the modulus.\n  // We want this to compile to:\n  //      vpaddd   t, lhs, rhs\n  //      vpsubd   u, t, p\n  //      vpminud  r, t, u\n  // throughput: 1.5 cyc/vec (10.67 els/cyc)\n  // latency: 3 cyc\n\n  // Let t := lhs + rhs\n  //     u := (t - p) mod 2³²\n  //     r := min(t, u)\n  //     m := { p     if this is montgomery form, which is 0\n  //          { p - 1 otherwise\n  //\n  // 0 ≤ lhs, rhs ≤ m\n  //\n  // 1) (lhs = p && rhs = 0) || (lhs = 0 && rhs = p)\n  //    t = p\n  //    u = 0\n  //    r = 0\n  //\n  // 2) lhs = p && rhs = p\n  //    t = 2p\n  //    u = p\n  //    r = p, which is 0.\n  //\n  // 3) (lhs = p && 1 ≤ rhs ≤ p - 1) || (1 ≤ lhs ≤ p - 1 && rhs = p)\n  //    p + 1 ≤ t ≤ 2p - 1, go to 5)\n  //\n  // 4) 0 ≤ t ≤ p - 1\n  //    p < 2³² - p ≤ u ≤ 2³² - 1\n  //    r = t\n  //\n  // 5) p + 1 ≤ t ≤ 2p - 1\n  //    1 ≤ u ≤ p - 1 ≤ p\n  //    r = u\n  //\n  // In all cases, r is in {0, ..., m}.\n  __m512i t = _mm512_add_epi32(lhs, rhs);\n  __m512i u = _mm512_sub_epi32(t, p);\n  return _mm512_min_epu32(t, u);\n}\n\nALWAYS_INLINE __m512i SubMod32(__m512i lhs, __m512i rhs, __m512i p) {\n  // NOTE(chokobole): This assumes 2p < 2³², where p is the modulus.\n  // We want this to compile to:\n  //      vpsubd   t, lhs, rhs\n  //      vpaddd   u, t, p\n  //      vpminud  r, t, u\n  // throughput: 1.5 cyc/vec (10.67 els/cyc)\n  // latency: 3 cyc\n\n  // Let d := lhs - rhs\n  //     t := d mod 2³²\n  //     u := (t + p) mod 2³²\n  //     r := min(t, u)\n  //     m := { p     if this is montgomery form, which is 0\n  //          { p - 1 otherwise\n  //\n  // 0 ≤ lhs, rhs ≤ m\n  //\n  // 1) lhs = p && rhs = 0\n  //    d = p\n  //    t = p\n  //    r = p, which is 0.\n  //\n  // 2) lhs = 0 && rhs = p\n  //    d = -p\n  //    t = 2³² - p\n  //    u = 0\n  //    r = 0\n  //\n  // 3) lhs = p && rhs = p\n  //    d = 0\n  //    t = 0\n  //    u = p\n  //    r = 0\n  //\n  // 4) lhs = p && 1 ≤ rhs ≤ p - 1\n  //    1 ≤ d ≤ p - 1, go to 6)\n  //\n  // 5) 1 ≤ lhs ≤ p - 1 && rhs = p\n  //    -p + 1 ≤ d ≤ -1, go to 7)\n  //\n  // 6) 1 ≤ d ≤ p - 1\n  //    1 ≤ t ≤ p - 1\n  //    p + 1 ≤ u ≤ 2p - 1\n  //    r = t\n  //\n  // 7) -p + 1 ≤ d ≤ -1\n  //    2³² - p + 1 ≤ t ≤ 2³² - 1\n  //    1 ≤ u ≤ p - 1\n  //    r = u\n  //\n  // In all cases, r is in {0, ..., m}.\n  __m512i t = _mm512_sub_epi32(lhs, rhs);\n  __m512i u = _mm512_add_epi32(t, p);\n  return _mm512_min_epu32(t, u);\n}\n\nALWAYS_INLINE __m512i NegateMod32(__m512i val, __m512i p) {\n  // We want this to compile to:\n  //      vptestmd  nonzero, val, val\n  //      vpsubd    res{nonzero}{z}, p, val\n  // throughput: 1 cyc/vec (16 els/cyc)\n  // latency: 4 cyc\n\n  // NOTE: This routine prioritizes throughput over latency. An alternative\n  // method would be to do sub(0, val), which would result in shorter\n  // latency, but also lower throughput.\n\n  // If |val| is zero, then the result is zeroed by masking.\n  // Else if |val| is p, then the result is 0.\n  // Otherwise |val| is in {1, ..., p - 1} and |p - val| is in the same range.\n  __mmask16 nonzero = _mm512_test_epi32_mask(val, val);\n  return _mm512_maskz_sub_epi32(nonzero, p, val);\n}\n\nALWAYS_INLINE __m512i movehdup_epi32(__m512i a) {\n  // The instruction is only available in the floating-point flavor; this\n  // distinction is only for historical reasons and no longer matters. We cast\n  // to floats, do the thing, and cast back.\n  return _mm512_castps_si512(_mm512_movehdup_ps(_mm512_castsi512_ps(a)));\n}\n\n// Viewing |a| as a vector of 16 |uint32_t|s, copy the odd elements into the\n// even elements below them, then merge with |src| according to the mask\n// provided. In other words, for all 0 ≤ i < 8, set the even elements\n// according to\n// |res[2 * i] := if k[2 * i] { a[2 * i + 1] } else { src[2 * i] }|, and\n// the odd elements according to\n// |res[2 * i + 1] := if k[2 * i + 1] { a[2 * i + 1] } else { src[2 * i + 1] }|.\nALWAYS_INLINE __m512i mask_movehdup_epi32(__m512i src, __mmask16 k, __m512i a) {\n  // The instruction is only available in the floating-point flavor; this\n  // distinction is only for historical reasons and no longer matters. We cast\n  // to floats, do the thing, and cast back.\n  return _mm512_castps_si512(_mm512_mask_movehdup_ps(\n      _mm512_castsi512_ps(src), k, _mm512_castsi512_ps(a)));\n}\n\nALWAYS_INLINE __m512i mask_moveldup_epi32(__m512i src, __mmask16 k, __m512i a) {\n  // The instruction is only available in the floating-point flavor; this\n  // distinction is only for historical reasons and no longer matters. We cast\n  // to floats, do the thing, and cast back.\n  return _mm512_castps_si512(_mm512_mask_moveldup_ps(\n      _mm512_castsi512_ps(src), k, _mm512_castsi512_ps(a)));\n}\n\nALWAYS_INLINE __m512i MontMulMod32(__m512i lhs, __m512i rhs, __m512i p,\n                                   __m512i inv) {\n  constexpr __mmask16 kEvens = 0b0101010101010101;\n\n  // We want this to compile to:\n  //      vmovshdup  lhs_odd, lhs\n  //      vmovshdup  rhs_odd, rhs\n  //      vpmuludq   prod_evn, lhs, rhs\n  //      vpmuludq   prod_hi, lhs_odd, rhs_odd\n  //      vpmuludq   q_evn, prod_evn, inv\n  //      vpmuludq   q_odd, prod_hi, inv\n  //      vmovshdup  prod_hi{kEvens}, prod_evn\n  //      vpmuludq   q_p_evn, q_evn, p\n  //      vpmuludq   q_p_hi, q_odd, p\n  //      vmovshdup  q_p_hi{kEvens}, q_p_evn\n  //      vpcmpltud  underflow, prod_hi, q_p_hi\n  //      vpsubd     r, prod_hi, q_p_hi\n  //      vpaddd     r{underflow}, r, p\n  // throughput: 6.5 cyc/vec (2.46 els/cyc)\n  // latency: 21 cyc\n\n  // |vpmuludq| only reads the even doublewords, so when we pass |lhs| and\n  // |rhs| directly we get the eight products at even positions.\n  //            ┌──────┬──────┬─────┬───────┬───────┐\n  // |lhs_evn|: │ lhs₀ │ lhs₁ │ ... │ lhs₁₄ │ lhs₁₅ │\n  //            └──────┴──────┴─────┴───────┴───────┘\n  __m512i lhs_evn = lhs;\n  //            ┌──────┬──────┬─────┬───────┬───────┐\n  // |rhs_evn|: │ rhs₀ │ rhs₁ │ ... │ rhs₁₄ │ rhs₁₅ │\n  //            └──────┴──────┴─────┴───────┴───────┘\n  __m512i rhs_evn = rhs;\n\n  // Copy the odd doublewords into even positions to compute the eight\n  // products at odd positions. NB: The odd doublewords are ignored by\n  // |vpmuludq|, so we have a lot of choices for how to do this; |vmovshdup|\n  // is nice because it runs on a memory port if the operand is in memory,\n  // thus improving our throughput.\n  //            ┌──────┬──────┬─────┬───────┬───────┐\n  // |lhs_odd|: │ lhs₁ │ lhs₁ │ ... │ lhs₁₅ │ lhs₁₅ │\n  //            └──────┴──────┴─────┴───────┴───────┘\n  __m512i lhs_odd = movehdup_epi32(lhs);\n  //            ┌──────┬──────┬─────┬───────┬───────┐\n  // |rhs_odd|: │ rhs₁ │ rhs₁ │ ... │ rhs₁₅ │ rhs₁₅ │\n  //            └──────┴──────┴─────┴───────┴───────┘\n  __m512i rhs_odd = movehdup_epi32(rhs);\n\n  // clang-format off\n  //             ┌─────────────────┬─────────────────┬─────┬───────────────────┬───────────────────┐\n  // |prod_evn|: │ Lo(lhs₀ * rhs₀) │ Hi(lhs₀ * rhs₀) | ... │ Lo(lhs₁₄ * rhs₁₄) │ Hi(lhs₁₄ * rhs₁₄) |\n  //             └─────────────────┴─────────────────┴─────┴───────────────────┴───────────────────┘\n  // clang-format on\n  __m512i prod_evn = _mm512_mul_epu32(lhs_evn, rhs_evn);\n  // clang-format off\n  //             ┌─────────────────┬─────────────────┬─────┬───────────────────┬───────────────────┐\n  // |prod_odd|: │ Lo(lhs₁ * rhs₁) │ Hi(lhs₁ * rhs₁) | ... │ Lo(lhs₁₅ * rhs₁₅) │ Hi(lhs₁₅ * rhs₁₅) |\n  //             └─────────────────┴─────────────────┴─────┴───────────────────┴───────────────────┘\n  // clang-format on\n  __m512i prod_odd = _mm512_mul_epu32(lhs_odd, rhs_odd);\n\n  // clang-format off\n  //          ┌───────────────────────┬───────────────────────┬─────┬─────────────────────────┬─────────────────────────┐\n  // |q_evn|: │ Lo(lhs₀ * rhs₀ * inv) │ Hi(lhs₀ * rhs₀ * inv) | ... │ Lo(lhs₁₄ * rhs₁₄ * inv) │ Hi(lhs₁₄ * rhs₁₄ * inv) |\n  //          └───────────────────────┴───────────────────────┴─────┴─────────────────────────┴─────────────────────────┘\n  // clang-format on\n  __m512i q_evn = _mm512_mul_epu32(prod_evn, inv);\n  // clang-format off\n  //          ┌───────────────────────┬───────────────────────┬─────┬─────────────────────────┬─────────────────────────┐\n  // |q_odd|: │ Lo(lhs₁ * rhs₁ * inv) │ Hi(lhs₁ * rhs₁ * inv) | ... │ Lo(lhs₁₅ * rhs₁₅ * inv) │ Hi(lhs₁₅ * rhs₁₅ * inv) |\n  //          └───────────────────────┴───────────────────────┴─────┴─────────────────────────┴─────────────────────────┘\n  // clang-format on\n  __m512i q_odd = _mm512_mul_epu32(prod_odd, inv);\n\n  // TODO(chokobole): Add diagram for better explanation.\n  // Get all the high halves as one vector: this is |(lhs * rhs) >> 32|.\n  // NB: |vpermt2d| may feel like a more intuitive choice here, but it has\n  // much higher latency.\n  __m512i prod_hi = mask_movehdup_epi32(prod_odd, kEvens, prod_evn);\n\n  // Normally we'd want to mask to perform % 2**32, but the instruction below\n  // only reads the low 32 bits anyway.\n  // clang-format off\n  //            ┌───────────────────────────┬───────────────────────────┬─────┬─────────────────────────────┬─────────────────────────────┐\n  // |q_p_evn|: │ Lo(lhs₀ * rhs₀ * inv * p) │ Hi(lhs₀ * rhs₀ * inv * p) | ... │ Lo(lhs₁₄ * rhs₁₄ * inv * p) │ Hi(lhs₁₄ * rhs₁₄ * inv * p) |\n  //            └───────────────────────────┴───────────────────────────┴─────┴─────────────────────────────┴─────────────────────────────┘\n  // clang-format on\n  __m512i q_p_evn = _mm512_mul_epu32(q_evn, p);\n  // clang-format off\n  //            ┌───────────────────────────┬───────────────────────────┬─────┬─────────────────────────────┬─────────────────────────────┐\n  // |q_p_odd|: │ Lo(lhs₁ * rhs₁ * inv * p) │ Hi(lhs₁ * rhs₁ * inv * p) | ... │ Lo(lhs₁₅ * rhs₁₅ * inv * p) │ Hi(lhs₁₅ * rhs₁₅ * inv * p) |\n  //            └───────────────────────────┴───────────────────────────┴─────┴─────────────────────────────┴─────────────────────────────┘\n  // clang-format on\n  __m512i q_p_odd = _mm512_mul_epu32(q_odd, p);\n\n  // We can ignore all the low halves of |q_p| as they cancel out. Get all the\n  // high halves as one vector.\n  __m512i q_p_hi = mask_movehdup_epi32(q_p_odd, kEvens, q_p_evn);\n\n  // Subtraction |prod_hi - q_p_hi| modulo |p|.\n  // NB: Normally we'd |vpaddd P| and take the |vpminud|, but |vpminud| runs\n  // on port 0, which is already under a lot of pressure performing\n  // multiplications. To relieve this pressure, we check for underflow to\n  // generate a mask, and then conditionally add |p|. The underflow check runs\n  // on port 5, increasing our throughput, although it does cost us an\n  // additional cycle of latency.\n  __mmask16 underflow = _mm512_cmplt_epu32_mask(prod_hi, q_p_hi);\n  __m512i t = _mm512_sub_epi32(prod_hi, q_p_hi);\n  return _mm512_mask_add_epi32(t, underflow, t, p);\n}\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_PACKED_PRIME_FIELD32_AVX512_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/packed_prime_field32_neon.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_MATH_FINITE_FIELDS_PACKED_PRIME_FIELD32_NEON_H_\n#define TACHYON_MATH_FINITE_FIELDS_PACKED_PRIME_FIELD32_NEON_H_\n\n#include <arm_neon.h>\n\n#include \"tachyon/base/compiler_specific.h\"\n\nnamespace tachyon::math {\n\n// Given a |val| in {0, ..., 2p}, return a |res| in {0, ..., p} such\n// that |res = val (mod p)|.\nALWAYS_INLINE uint32x4_t ReduceSum32(uint32x4_t val, uint32x4_t p) {\n  // NOTE(chokobole): This assumes 2p < 2³², where p is the modulus.\n  // Let u := (val - p) mod 2³² and r := min(val, u).\n  // 0 ≤ val ≤ 2p\n  //\n  // 1) 0 ≤ val ≤ p - 1\n  //    p < 2³² - p ≤ u ≤ 2³² - 1\n  //    r = val\n  //\n  // 2) p ≤ val ≤ 2p\n  //    0 ≤ u ≤ p\n  //    r = u\n  //\n  // In both cases, r is in {0, ..., p}.\n  uint32x4_t u = vsubq_u32(val, p);\n  return vminq_u32(val, u);\n}\n\nALWAYS_INLINE uint32x4_t AddMod32(uint32x4_t lhs, uint32x4_t rhs,\n                                  uint32x4_t p) {\n  // NOTE(chokobole): This assumes 2p < 2³², where p is the modulus.\n  // We want this to compile to:\n  //      add   t.4s, lhs.4s, rhs.4s\n  //      sub   u.4s, t.4s, p.4s\n  //      umin  r.4s, t.4s, u.4s\n  // throughput: .75 cyc/vec (5.33 els/cyc)\n  // latency: 6 cyc\n\n  // Let t := lhs + rhs\n  //     u := (t - p) mod 2³²\n  //     r := min(t, u)\n  //     m := { p     if this is montgomery form, which is 0\n  //          { p - 1 otherwise\n  //\n  // 0 ≤ lhs, rhs ≤ m\n  //\n  // 1) (lhs = p && rhs = 0) || (lhs = 0 && rhs = p)\n  //    t = p\n  //    u = 0\n  //    r = 0\n  //\n  // 2) lhs = p && rhs = p\n  //    t = 2p\n  //    u = p\n  //    r = p, which is 0.\n  //\n  // 3) (lhs = p && 1 ≤ rhs ≤ p - 1) || (1 ≤ lhs ≤ p - 1 && rhs = p)\n  //    p + 1 ≤ t ≤ 2p - 1, go to 5)\n  //\n  // 4) 0 ≤ t ≤ p - 1\n  //    p < 2³² - p ≤ u ≤ 2³² - 1\n  //    r = t\n  //\n  // 5) p + 1 ≤ t ≤ 2p - 1\n  //    1 ≤ u ≤ p - 1 ≤ p\n  //    r = u\n  //\n  // In all cases, r is in {0, ..., m}.\n  uint32x4_t t = vaddq_u32(lhs, rhs);\n  return ReduceSum32(t, p);\n}\n\nALWAYS_INLINE uint32x4_t SubMod32(uint32x4_t lhs, uint32x4_t rhs,\n                                  uint32x4_t p) {\n  // NOTE(chokobole): This assumes 2p < 2³², where p is the modulus.\n  // We want this to compile to:\n  //      sub   r.4s, lhs.4s, rhs.4s\n  //      cmhi  underflow.4s, rhs.4s, lhs.4s\n  //      mls   r.4s, underflow.4s, p.4s\n  // throughput: .75 cyc/vec (5.33 els/cyc)\n  // latency: 5 cyc\n\n  // Let t := lhs - rhs\n  //     diff := t mod 2³²\n  //     underflow := 0 if lhs ≥ rhs else 2³² - 1\n  //     r := (diff - underflow * p) mod 2³²\n  //     m := { p     if this is montgomery form, which is 0\n  //          { p - 1 otherwise\n  //\n  // 0 ≤ lhs, rhs ≤ m\n  //\n  // 1) lhs = p && rhs = 0\n  //    t = p\n  //    diff = p\n  //    underflow = 0\n  //    r = diff = p, which is 0.\n  //\n  // 2) lhs = 0 && rhs = p\n  //    t = -p\n  //    diff = 2³² - p\n  //    underflow = 2³² - 1\n  //    r = 2³² - p - (2³² - 1)p = 0\n  //\n  // 3) lhs = p && rhs = p\n  //    t = 0\n  //    diff = 0\n  //    underflow = 0\n  //    r = diff = 0\n  //\n  // 4) lhs = p && 1 ≤ rhs ≤ p - 1\n  //    1 ≤ t ≤ p - 1, go to 6)\n  //\n  // 5) 1 ≤ lhs ≤ p - 1 && rhs = p\n  //    -p + 1 ≤ t ≤ -1, go to 7)\n  //\n  // 6) 1 ≤ t ≤ p - 1\n  //    1 ≤ diff ≤ p - 1\n  //    underflow = 0\n  //    r = t\n  //\n  // 7) -p + 1 ≤ t ≤ -1\n  //    2³² - p + 1 ≤ diff ≤ 2³² - 1\n  //    underflow = 2³² - 1\n  //    2³² - p + 1 - (2³² - 1)p ≤ r ≤ 2³² - 1 - (2³² - 1)p\n  //    1 ≤ r ≤ p - 1\n  //\n  // In all cases, r is in {0, ..., m}.\n  uint32x4_t diff = vsubq_u32(lhs, rhs);\n  uint32x4_t underflow = vcltq_u32(lhs, rhs);\n  return vmlsq_u32(diff, underflow, p);\n}\n\nALWAYS_INLINE uint32x4_t NegateMod32(uint32x4_t val, uint32x4_t p) {\n  // We want this to compile to:\n  //      sub   t.4s, p.4s, val.4s\n  //      cmeq  is_zero.4s, val.4s, #0\n  //      bic   r.4s, t.4s, is_zero.4s\n  // throughput: .75 cyc/vec (5.33 els/cyc)\n  // latency: 4 cyc\n\n  // This has the same throughput as |Sub(kZero, val)| but slightly lower\n  // latency.\n  //\n  // Let t := p - val\n  //     is_zero := 2³² - 1 if val = 0 else 0\n  //     r := t & ~is_zero\n  //     m := { p     if this is montgomery form, which is 0\n  //          { p - 1 otherwise\n  //\n  // 0 ≤ val ≤ m\n  //\n  // 1) val = 0\n  //    is_zero = 2³² - 1\n  //    r = 0\n  //\n  // 2) val = p\n  //    is_zero = 0\n  //    t = 0\n  //    r = 0\n  //\n  // 3) 1 ≤ val ≤ p - 1\n  //    is_zero = 0\n  //    2³² - p + 1 ≤ -val ≤ 2³² - 1\n  //    1 ≤ t ≤ p - 1\n  //    r = t\n  //\n  // In all cases, r is in {0, ..., m}.\n  uint32x4_t t = vsubq_u32(p, val);\n  uint32x4_t is_zero = vceqzq_u32(val);\n  return vbicq_u32(t, is_zero);\n}\n\nALWAYS_INLINE int32x4_t MulByInv(int32x4_t val, int32x4_t inv) {\n  // We want this to compile to:\n  //      mul      r.4s, val.4s, inv.4s\n  // throughput: .25 cyc/vec (16 els/cyc)\n  // latency: 3 cyc\n\n  return vmulq_s32(val, inv);\n}\n\nALWAYS_INLINE int32x4_t GetCHi(int32x4_t lhs, int32x4_t rhs) {\n  // We want this to compile to:\n  //      sqdmulh  c_hi.4s, lhs.4s, rhs.4s\n  // throughput: .25 cyc/vec (16 els/cyc)\n  // latency: 3 cyc\n\n  // Get bits 31, ..., 62 of C. Note that |sqdmulh| saturates when the product\n  // doesn't fit in a 63 bit integer, but this cannot happen here due to our\n  // bounds on |lhs| and |rhs|.\n  return vqdmulhq_s32(lhs, rhs);\n}\n\nALWAYS_INLINE int32x4_t GetQPHi(int32x4_t lhs, int32x4_t inv_rhs, int32x4_t p) {\n  // We want this to compile to:\n  //      mul      q.4s, lhs.4s, inv_rhs.4s\n  //      sqdmulh  qp_hi.4s, q.4s, p.4s\n  // throughput: .5 cyc/vec (8 els/cyc)\n  // latency: 6 cyc\n\n  int32x4_t q = vmulq_s32(lhs, inv_rhs);\n\n  // Gets bits 31, ..., 62 of q * p. Again, saturation is not an issue because\n  // |p| is not -2³¹.\n  return vqdmulhq_s32(q, vreinterpretq_s32_u32(p));\n}\n\nALWAYS_INLINE int32x4_t GetD(int32x4_t c_hi, int32x4_t qp_hi) {\n  // We want this to compile to:\n  //      shsub    r.4s, c_hi.4s, qp_hi.4s\n  // throughput: .25 cyc/vec (16 els/cyc)\n  // latency: 2 cyc\n\n  // Form D. Note that |c_hi| is C >> 31 and |qp_hi| is (Q * P) >> 31, whereas\n  // we want (C - Q * P) >> 32, so we need to subtract and divide by 2. Luckily\n  // NEON has an instruction for that! The lowest bit of |c_hi| and |qp_hi| is\n  // the same, so the division is exact.\n  return vhsubq_s32(c_hi, qp_hi);\n}\n\nALWAYS_INLINE uint32x4_t GetReducedD(int32x4_t c_hi, int32x4_t qp_hi,\n                                     int32x4_t p) {\n  // We want this to compile to:\n  //      shsub    r.4s, c_hi.4s, qp_hi.4s\n  //      cmgt     underflow.4s, qp_hi.4s, c_hi.4s\n  //      mls      r.4s, underflow.4s, p.4s\n  // throughput: .75 cyc/vec (5.33 els/cyc)\n  // latency: 5 cyc\n\n  uint32x4_t d = vreinterpretq_u32_s32(GetD(c_hi, qp_hi));\n\n  // Finally, we reduce D to canonical form. D is negative iff |c_hi| > |qp_hi|,\n  // so if that's the case then we add P. Note that if |c_hi| > |qp_hi| then\n  // |underflow| is -1, so we must subtract |underflow * p|.\n  uint32x4_t underflow = vcltq_s32(c_hi, qp_hi);\n  // TODO(chokobole): add |ConfuseCompiler()|.\n  // See\n  // https://github.com/Plonky3/Plonky3/blob/6034010/baby-bear/src/aarch64_neon/packing.rs#L279.\n  return vmlsq_u32(d, underflow, p);\n}\n\nALWAYS_INLINE uint32x4_t MontMulMod32(uint32x4_t lhs, uint32x4_t rhs,\n                                      uint32x4_t p, uint32x4_t inv) {\n  // We want this to compile to:\n  //      sqdmulh  c_hi.4s, lhs.4s, rhs.4s\n  //      mul      inv_rhs.4s, rhs.4s, Inv.4s\n  //      mul      q.4s, lhs.4s, inv_rhs.4s\n  //      sqdmulh  qp_hi.4s, q.4s, p.4s\n  //      shsub    r.4s, c_hi.4s, qp_hi.4s\n  //      cmgt     underflow.4s, qp_hi.4s, c_hi.4s\n  //      mls      r.4s, underflow.4s, p.4s\n  // throughput: 1.75 cyc/vec (2.29 els/cyc)\n  // latency: (lhs->) 11 cyc, (rhs->) 14 cyc\n\n  // See comments \"MONTGOMERY MULTIPLICATION\" in\n  // \"tachyon/math/finite_fields/packed_prime_field_avx2.h\".\n  // No-op. The inputs are non-negative so we're free to interpret them as\n  // signed numbers.\n  int32x4_t s_lhs = vreinterpretq_s32_u32(lhs);\n  int32x4_t s_rhs = vreinterpretq_s32_u32(rhs);\n\n  uint32x4_t inv_rhs = MulByInv(s_rhs, inv);\n  uint32x4_t c_hi = GetCHi(s_lhs, s_rhs);\n  uint32x4_t qp_hi = GetQPHi(s_lhs, inv_rhs, p);\n  return GetReducedD(c_hi, qp_hi, p);\n}\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_PACKED_PRIME_FIELD32_NEON_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/packed_prime_field_base.h",
    "content": "#ifndef TACHYON_MATH_FINITE_FIELDS_PACKED_PRIME_FIELD_BASE_H_\n#define TACHYON_MATH_FINITE_FIELDS_PACKED_PRIME_FIELD_BASE_H_\n\n#include <stddef.h>\n\n#include <array>\n#include <optional>\n#include <string>\n#include <utility>\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/functional/callback.h\"\n#include \"tachyon/base/strings/string_util.h\"\n#include \"tachyon/math/finite_fields/packed_field_traits_forward.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename Derived>\nclass PackedPrimeFieldBase : public Field<Derived> {\n public:\n  using PrimeField = typename PackedFieldTraits<Derived>::Field;\n  using Generator = base::RepeatingCallback<PrimeField(size_t)>;\n\n  constexpr static size_t N = PackedFieldTraits<Derived>::N;\n\n  constexpr static uint32_t ExtensionDegree() {\n    return PrimeField::ExtensionDegree();\n  }\n\n  static Derived Random() {\n    Derived ret;\n    for (size_t i = 0; i < N; ++i) {\n      ret.values_[i] = PrimeField::Random();\n    }\n    return ret;\n  }\n\n  static Derived From(Generator generator) {\n    Derived ret;\n    for (size_t i = 0; i < N; ++i) {\n      ret.values_[i] = generator.Run(i);\n    }\n    return ret;\n  }\n\n  const std::array<PrimeField, N>& values() const { return values_; }\n  std::array<PrimeField, N>& values() { return values_; }\n\n  constexpr bool IsZero() const {\n    for (size_t i = 0; i < N; ++i) {\n      if (!values_[i].IsZero()) return false;\n    }\n    return true;\n  }\n\n  constexpr bool IsOne() const {\n    for (size_t i = 0; i < N; ++i) {\n      if (!values_[i].IsOne()) return false;\n    }\n    return true;\n  }\n\n  constexpr bool IsMinusOne() const {\n    for (size_t i = 0; i < N; ++i) {\n      if (!values_[i].IsMinusOne()) return false;\n    }\n    return true;\n  }\n\n  std::string ToString() const { return base::ContainerToString(values_); }\n  std::string ToHexString(bool pad_zero = false) const {\n    return base::ContainerToString(\n        base::Map(values_, [pad_zero](const PrimeField& value) {\n          return value.ToHexString(pad_zero);\n        }));\n  }\n\n  constexpr PrimeField& operator[](size_t i) { return values_[i]; }\n  constexpr const PrimeField& operator[](size_t i) const { return values_[i]; }\n\n  constexpr bool operator==(const Derived& other) const {\n    return values_ == other.values_;\n  }\n  constexpr bool operator!=(const Derived& other) const {\n    return values_ != other.values_;\n  }\n\n  // NOTE(chokobole): These are needed by Eigen Matrix inverse operation.\n  constexpr bool operator<(const Derived& other) const {\n    for (size_t i = 0; i < values_.size(); ++i) {\n      if (values_[i] == other.values_[i]) continue;\n      return values_[i] < other.values_[i];\n    }\n    return false;\n  }\n  constexpr bool operator>(const Derived& other) const {\n    for (size_t i = 0; i < values_.size(); ++i) {\n      if (values_[i] == other.values_[i]) continue;\n      return values_[i] > other.values_[i];\n    }\n    return false;\n  }\n  constexpr bool operator<=(const Derived& other) const {\n    return !operator>(other);\n  }\n\n  constexpr bool operator>=(const Derived& other) const {\n    return !operator<(other);\n  }\n\n  // AdditiveSemigroup methods\n  Derived& AddInPlace(const Derived& other) {\n    Derived& self = static_cast<Derived&>(*this);\n    return self = self + other;\n  }\n\n  // AdditiveGroup methods\n  Derived& SubInPlace(const Derived& other) {\n    Derived& self = static_cast<Derived&>(*this);\n    return self = self - other;\n  }\n\n  Derived& NegateInPlace() {\n    Derived& self = static_cast<Derived&>(*this);\n    return self = self.Negate();\n  }\n\n  // MultiplicativeSemigroup methods\n  Derived& MulInPlace(const Derived& other) {\n    Derived& self = static_cast<Derived&>(*this);\n    return self = self * other;\n  }\n\n  // MultiplicativeGroup methods\n  std::optional<Derived> Inverse() const {\n    Derived ret;\n    CHECK(PrimeField::BatchInverseSerial(values_, &ret.values_));\n    return ret;\n  }\n\n  [[nodiscard]] std::optional<Derived*> InverseInPlace() {\n    CHECK(PrimeField::BatchInverseInPlaceSerial(values_));\n    return static_cast<Derived*>(this);\n  }\n\n  constexpr Derived& FrobeniusMapInPlace(uint32_t exponent) {\n    // Do nothing.\n    return static_cast<Derived&>(*this);\n  }\n\n protected:\n  std::array<PrimeField, N> values_;\n};\n\ntemplate <typename H, typename Derived>\nH AbslHashValue(H h, const PackedPrimeFieldBase<Derived>& f) {\n  return H::combine(std::move(h), f.values());\n}\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_PACKED_PRIME_FIELD_BASE_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/packed_prime_field_unittest.cc",
    "content": "#include \"absl/hash/hash_testing.h\"\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/build/build_config.h\"\n#include \"tachyon/math/finite_fields/baby_bear/internal/packed_baby_bear.h\"\n#include \"tachyon/math/finite_fields/packed_field_traits_forward.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n\n#if ARCH_CPU_X86_64\n#include \"tachyon/math/finite_fields/baby_bear/internal/packed_baby_bear_avx2.h\"\n#include \"tachyon/math/finite_fields/koala_bear/internal/packed_koala_bear_avx2.h\"\n#include \"tachyon/math/finite_fields/mersenne31/internal/packed_mersenne31_avx2.h\"\n#if defined(TACHYON_HAS_AVX512)\n#include \"tachyon/math/finite_fields/baby_bear/internal/packed_baby_bear_avx512.h\"\n#include \"tachyon/math/finite_fields/koala_bear/internal/packed_koala_bear_avx512.h\"\n#include \"tachyon/math/finite_fields/mersenne31/internal/packed_mersenne31_avx512.h\"\n#endif\n#elif ARCH_CPU_ARM64\n#include \"tachyon/math/finite_fields/baby_bear/internal/packed_baby_bear_neon.h\"\n#include \"tachyon/math/finite_fields/koala_bear/internal/packed_koala_bear_neon.h\"\n#include \"tachyon/math/finite_fields/mersenne31/internal/packed_mersenne31_neon.h\"\n#endif\n\nnamespace tachyon::math {\n\nnamespace {\n\ntemplate <typename PackedPrimeField>\nclass PackedPrimeFieldTest : public FiniteFieldTest<PackedPrimeField> {};\n\n}  // namespace\n\nusing PackedPrimeFieldTypes = testing::Types<\n#if ARCH_CPU_X86_64\n    PackedBabyBearAVX2, PackedMersenne31AVX2, PackedKoalaBearAVX2\n#if defined(TACHYON_HAS_AVX512)\n    ,\n    PackedBabyBearAVX512, PackedMersenne31AVX512, PackedKoalaBearAVX512\n#endif\n#elif ARCH_CPU_ARM64\n    PackedBabyBearNeon, PackedMersenne31Neon, PackedKoalaBearNeon\n#endif\n    >;\n\nTYPED_TEST_SUITE(PackedPrimeFieldTest, PackedPrimeFieldTypes);\n\nTYPED_TEST(PackedPrimeFieldTest, Zero) {\n  using PackedPrimeField = TypeParam;\n\n  PackedPrimeField zero = PackedPrimeField::Zero();\n  EXPECT_TRUE(zero.IsZero());\n  for (size_t i = 0; i < PackedPrimeField::N; ++i) {\n    EXPECT_TRUE(zero[i].IsZero());\n  }\n  EXPECT_FALSE(PackedPrimeField::Random().IsZero());\n}\n\nTYPED_TEST(PackedPrimeFieldTest, One) {\n  using PackedPrimeField = TypeParam;\n\n  PackedPrimeField one = PackedPrimeField::One();\n  EXPECT_TRUE(one.IsOne());\n  for (size_t i = 0; i < PackedPrimeField::N; ++i) {\n    EXPECT_TRUE(one[i].IsOne());\n  }\n  EXPECT_FALSE(PackedPrimeField::Random().IsOne());\n}\n\nTYPED_TEST(PackedPrimeFieldTest, MinusOne) {\n  using PackedPrimeField = TypeParam;\n\n  PackedPrimeField one = PackedPrimeField::MinusOne();\n  EXPECT_TRUE(one.IsMinusOne());\n  for (size_t i = 0; i < PackedPrimeField::N; ++i) {\n    EXPECT_TRUE(one[i].IsMinusOne());\n  }\n  EXPECT_FALSE(PackedPrimeField::Random().IsMinusOne());\n}\n\nTYPED_TEST(PackedPrimeFieldTest, TwoInv) {\n  using PackedPrimeField = TypeParam;\n  using PrimeField = typename PackedFieldTraits<PackedPrimeField>::Field;\n  PackedPrimeField two_inv = PackedPrimeField::TwoInv();\n  EXPECT_TRUE((two_inv * PackedPrimeField(2)).IsOne());\n  EXPECT_FALSE((two_inv * PackedPrimeField::One()).IsOne());\n  for (size_t i = 0; i < PackedPrimeField::N; ++i) {\n    EXPECT_TRUE((two_inv[i] * PrimeField(2)).IsOne());\n  }\n}\n\nTYPED_TEST(PackedPrimeFieldTest, RawOne) {\n  using PackedPrimeField = TypeParam;\n  using PrimeField = typename PackedFieldTraits<PackedPrimeField>::Field;\n\n  if constexpr (PrimeField::Config::kUseMontgomery) {\n    EXPECT_EQ(PackedPrimeField::RawOne(),\n              PackedPrimeField::Broadcast(PrimeField::FromMontgomery(1)));\n  } else {\n    GTEST_SKIP() << \"RawOne() doesn't exist\";\n  }\n}\n\nTYPED_TEST(PackedPrimeFieldTest, Broadcast) {\n  using PackedPrimeField = TypeParam;\n  using PrimeField = typename PackedPrimeField::PrimeField;\n\n  PrimeField r = PrimeField::Random();\n  PackedPrimeField f = PackedPrimeField::Broadcast(r);\n  for (size_t i = 0; i < PackedPrimeField::N; ++i) {\n    EXPECT_EQ(f[i], r);\n  }\n\n  EXPECT_EQ(f, PackedPrimeField(r.ToBigInt()[0]));\n}\n\nTYPED_TEST(PackedPrimeFieldTest, Random) {\n  using PackedPrimeField = TypeParam;\n\n  bool success = false;\n  PackedPrimeField r = PackedPrimeField::Random();\n  for (size_t i = 0; i < 100; ++i) {\n    if (r != PackedPrimeField::Random()) {\n      success = true;\n      break;\n    }\n  }\n  EXPECT_TRUE(success);\n}\n\nTYPED_TEST(PackedPrimeFieldTest, Add) {\n  using PackedPrimeField = TypeParam;\n\n  PackedPrimeField a = PackedPrimeField::Random();\n  PackedPrimeField b = PackedPrimeField::Random();\n  PackedPrimeField zero = PackedPrimeField::Zero();\n\n  struct {\n    PackedPrimeField a;\n    PackedPrimeField b;\n  } tests[] = {\n      {a, b},\n      {a, zero},\n  };\n\n  for (auto& test : tests) {\n    PackedPrimeField c = test.a + test.b;\n    for (size_t i = 0; i < PackedPrimeField::N; ++i) {\n      EXPECT_EQ(c[i], test.a[i] + test.b[i]);\n    }\n    test.a += test.b;\n    for (size_t i = 0; i < PackedPrimeField::N; ++i) {\n      EXPECT_EQ(c[i], test.a[i]);\n    }\n  }\n}\n\nTYPED_TEST(PackedPrimeFieldTest, Sub) {\n  using PackedPrimeField = TypeParam;\n\n  PackedPrimeField a = PackedPrimeField::Random();\n  PackedPrimeField b = PackedPrimeField::Random();\n  PackedPrimeField zero = PackedPrimeField::Zero();\n\n  struct {\n    PackedPrimeField a;\n    PackedPrimeField b;\n  } tests[] = {\n      {a, b},\n      {a, zero},\n  };\n\n  for (auto& test : tests) {\n    PackedPrimeField c = test.a - test.b;\n    for (size_t i = 0; i < PackedPrimeField::N; ++i) {\n      EXPECT_EQ(c[i], test.a[i] - test.b[i]);\n    }\n    test.a -= test.b;\n    for (size_t i = 0; i < PackedPrimeField::N; ++i) {\n      EXPECT_EQ(c[i], test.a[i]);\n    }\n  }\n}\n\nTYPED_TEST(PackedPrimeFieldTest, Negate) {\n  using PackedPrimeField = TypeParam;\n\n  PackedPrimeField a = PackedPrimeField::Random();\n  PackedPrimeField zero = PackedPrimeField::Zero();\n\n  struct {\n    PackedPrimeField a;\n  } tests[] = {\n      {a},\n      {zero},\n  };\n\n  for (auto& test : tests) {\n    PackedPrimeField c = -test.a;\n    for (size_t i = 0; i < PackedPrimeField::N; ++i) {\n      EXPECT_EQ(c[i], -test.a[i]);\n    }\n    test.a.NegateInPlace();\n    for (size_t i = 0; i < PackedPrimeField::N; ++i) {\n      EXPECT_EQ(c[i], test.a[i]);\n    }\n  }\n}\n\nTYPED_TEST(PackedPrimeFieldTest, Mul) {\n  using PackedPrimeField = TypeParam;\n\n  PackedPrimeField a = PackedPrimeField::Random();\n  PackedPrimeField b = PackedPrimeField::Random();\n  PackedPrimeField zero = PackedPrimeField::Zero();\n  PackedPrimeField one = PackedPrimeField::One();\n\n  struct {\n    PackedPrimeField a;\n    PackedPrimeField b;\n  } tests[] = {\n      {a, b},\n      {a, zero},\n      {a, one},\n  };\n\n  for (auto& test : tests) {\n    PackedPrimeField c = test.a * test.b;\n    for (size_t i = 0; i < PackedPrimeField::N; ++i) {\n      EXPECT_EQ(c[i], test.a[i] * test.b[i]);\n    }\n    test.a *= test.b;\n    for (size_t i = 0; i < PackedPrimeField::N; ++i) {\n      EXPECT_EQ(c[i], test.a[i]);\n    }\n  }\n}\n\nTYPED_TEST(PackedPrimeFieldTest, Inverse) {\n  using PackedPrimeField = TypeParam;\n\n  PackedPrimeField a = PackedPrimeField::Random();\n  PackedPrimeField zero = PackedPrimeField::Zero();\n\n  struct {\n    PackedPrimeField a;\n  } tests[] = {\n      {a},\n      {zero},\n  };\n\n  for (auto& test : tests) {\n    std::optional<PackedPrimeField> c = test.a.Inverse();\n    if (test.a.IsZero()) {\n      for (size_t i = 0; i < PackedPrimeField::N; ++i) {\n        EXPECT_TRUE((*c)[i].IsZero());\n      }\n    } else {\n      for (size_t i = 0; i < PackedPrimeField::N; ++i) {\n        EXPECT_EQ((*c)[i], test.a[i].Inverse());\n      }\n    }\n    ASSERT_TRUE(test.a.InverseInPlace());\n    for (size_t i = 0; i < PackedPrimeField::N; ++i) {\n      EXPECT_EQ((*c)[i], test.a[i]);\n    }\n  }\n}\n\nTYPED_TEST(PackedPrimeFieldTest, Hash) {\n  using PackedPrimeField = TypeParam;\n  using F = typename PackedFieldTraits<PackedPrimeField>::Field;\n\n  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(\n      std::make_tuple(F::Zero(), F::One(), F::Random())));\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/finite_fields/prime_field_base.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_FINITE_FIELDS_PRIME_FIELD_BASE_H_\n#define TACHYON_MATH_FINITE_FIELDS_PRIME_FIELD_BASE_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <cmath>\n#include <string>\n#include <utility>\n\n#include \"absl/hash/hash.h\"\n\n#include \"tachyon/base/bits.h\"\n#include \"tachyon/base/buffer/copyable.h\"\n#include \"tachyon/base/json/json.h\"\n#include \"tachyon/base/strings/string_number_conversions.h\"\n#include \"tachyon/math/base/gmp/gmp_util.h\"\n#include \"tachyon/math/finite_fields/extension_field_traits_forward.h\"\n#include \"tachyon/math/finite_fields/finite_field.h\"\n#include \"tachyon/math/finite_fields/legendre_symbol.h\"\n#include \"tachyon/math/finite_fields/packed_field_traits_forward.h\"\n#include \"tachyon/math/finite_fields/prime_field_util.h\"\n\nnamespace tachyon {\nnamespace math {\n\nstruct TACHYON_EXPORT PrimeFieldFactors {\n  uint32_t q_adicity;\n  uint64_t q_part;\n  uint32_t two_adicity;\n  uint64_t two_part;\n};\n\n// PrimeField is a finite field GF(p) for p is prime.\n// See https://mathworld.wolfram.com/PrimeField.html\ntemplate <typename F>\nclass PrimeFieldBase : public FiniteField<F> {\n public:\n  using Config = typename FiniteFieldTraits<F>::Config;\n\n  constexpr static bool HasRootOfUnity() {\n    return Config::kHasTwoAdicRootOfUnity ||\n           Config::kHasLargeSubgroupRootOfUnity;\n  }\n\n  // An invariant of a field which is prime number N\n  // such that N * e(unit element) = 0.\n  // It is uniquely determined for a given field.\n  // See https://encyclopediaofmath.org/wiki/Characteristic_of_a_field\n  constexpr static F Characteristic() {\n    return F::FromBigint(Config::kModulus);\n  }\n\n  constexpr static uint32_t ExtensionDegree() { return 1; }\n\n  constexpr static bool Decompose(uint64_t n, PrimeFieldFactors* factors) {\n    static_assert(Config::kHasLargeSubgroupRootOfUnity);\n\n    // Compute the size of our evaluation domain\n    uint64_t q = uint64_t{F::Config::kSmallSubgroupBase};\n    uint32_t q_adicity = ComputeAdicity(q, gmp::FromUnsignedInt(n));\n    uint64_t q_part = static_cast<uint64_t>(std::pow(q, q_adicity));\n\n    uint32_t two_adicity = ComputeAdicity(2, gmp::FromUnsignedInt(n));\n    uint64_t two_part = static_cast<uint64_t>(std::pow(2, two_adicity));\n    if (n != q_part * two_part) return false;\n\n    factors->q_adicity = q_adicity;\n    factors->q_part = q_part;\n    factors->two_adicity = two_adicity;\n    factors->two_part = two_part;\n    return true;\n  }\n\n  // Returns false for either of the following cases:\n  //\n  // When there exists |Config::kLargeSubgroupRootOfUnity|:\n  //   1. n is not a power of 2 times a power of |Config::kSmallSubgroupBase|.\n  //   2. two-adicity of n is greater than |Config::kTwoAdicity|.\n  //\n  // When |Config::kLargeSubgroupRootOfUnity| does not exist:\n  //   1. n is not a power of 2.\n  //   2. two-adicity of next power of 2 of n is greater than\n  //   |Config::kTwoAdicity|.\n  [[nodiscard]] static bool GetRootOfUnity(uint64_t n, F* ret) {\n    static_assert(HasRootOfUnity());\n    F omega;\n    if constexpr (Config::kHasLargeSubgroupRootOfUnity) {\n      PrimeFieldFactors factors;\n      if (!Decompose(n, &factors)) return false;\n      if (factors.two_adicity > Config::kTwoAdicity ||\n          factors.q_adicity > Config::kSmallSubgroupAdicity) {\n        return false;\n      }\n\n      if constexpr (F::Config::kUseMontgomery) {\n        omega = F::FromMontgomery(Config::kLargeSubgroupRootOfUnity);\n      } else {\n        omega = F(Config::kLargeSubgroupRootOfUnity);\n      }\n      for (size_t i = factors.q_adicity; i < Config::kSmallSubgroupAdicity;\n           ++i) {\n        omega = omega.Pow(Config::kSmallSubgroupBase);\n      }\n\n      for (size_t i = factors.two_adicity; i < Config::kTwoAdicity; ++i) {\n        omega.SquareInPlace();\n      }\n    } else {\n      uint32_t log_size_of_group = base::bits::Log2Ceiling(n);\n      uint64_t size = uint64_t{1} << log_size_of_group;\n\n      if (n != size || log_size_of_group > Config::kTwoAdicity) {\n        return false;\n      }\n\n      if constexpr (F::Config::kUseMontgomery) {\n        omega = F::FromMontgomery(Config::kTwoAdicRootOfUnity);\n      } else {\n        omega = F(Config::kTwoAdicRootOfUnity);\n      }\n      for (uint32_t i = log_size_of_group; i < Config::kTwoAdicity; ++i) {\n        omega.SquareInPlace();\n      }\n    }\n    *ret = omega;\n    return true;\n  }\n\n  constexpr LegendreSymbol Legendre() const {\n    const F* f = static_cast<const F*>(this);\n    // s = a^((p - 1) / 2)\n    F s = f->Pow(Config::kModulusMinusOneDivTwo);\n    if (s.IsZero())\n      return LegendreSymbol::kZero;\n    else if (s.IsOne())\n      return LegendreSymbol::kOne;\n    return LegendreSymbol::kMinusOne;\n  }\n\n  constexpr F& FrobeniusMapInPlace(uint32_t exponent) {\n    // Do nothing.\n    return static_cast<F&>(*this);\n  }\n};\n\ntemplate <typename H, typename F,\n          std::enable_if_t<std::is_base_of_v<PrimeFieldBase<F>, F>>* = nullptr>\nH AbslHashValue(H h, const F& prime_field) {\n  if constexpr (F::Config::kModulusBits > 32) {\n    for (uint64_t limb : prime_field.value().limbs) {\n      h = H::combine(std::move(h), limb);\n    }\n  } else {\n    h = H::combine(std::move(h), prime_field.value());\n  }\n  return h;\n}\n\n// NOTE(ashjeong): Base prime fields with no packed field version have their\n// packed prime field type defaulted as themselves to ensure prime field\n// standardization. See how it is used in\n// tachyon/math/polynomials/univariate/radix2_evaluation_domain.h.\ntemplate <typename T>\nstruct PackedFieldTraits<\n    T, std::enable_if_t<std::is_base_of_v<math::PrimeFieldBase<T>, T>>> {\n  using PackedField = T;\n};\n\ntemplate <typename T>\nstruct ExtensionFieldTraits<\n    T, std::enable_if_t<std::is_base_of_v<PrimeFieldBase<T>, T>>> {\n  constexpr static uint32_t kDegreeOverBaseField = 1;\n  constexpr static uint32_t kDegreeOverBasePrimeField = 1;\n\n  using BaseField = T;\n  using BasePrimeField = T;\n};\n\n}  // namespace math\n\nnamespace base {\n\ntemplate <typename T>\nclass Copyable<\n    T, std::enable_if_t<std::is_base_of_v<math::PrimeFieldBase<T>, T>>> {\n public:\n  using value_type = typename T::value_type;\n  using BigInt = typename T::BigIntTy;\n\n  static bool s_is_in_montgomery;\n\n  static bool WriteTo(const T& prime_field, Buffer* buffer) {\n    if constexpr (T::Config::kUseMontgomery) {\n      if (s_is_in_montgomery) {\n        return buffer->Write(prime_field.value());\n      }\n    }\n    if constexpr (T::Config::kModulusBits <= 32) {\n      if constexpr (T::Config::kUseMontgomery) {\n        return buffer->Write(T::Config::FromMontgomery(prime_field.value()));\n      } else {\n        return buffer->Write(prime_field.value());\n      }\n    } else {\n      return buffer->Write(prime_field.ToBigInt());\n    }\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer, T* prime_field) {\n    value_type v;\n    if (!buffer.Read(&v)) return false;\n    if constexpr (T::Config::kUseMontgomery) {\n      if (s_is_in_montgomery) {\n        *prime_field = T::FromMontgomery(v);\n        return true;\n      }\n    }\n    *prime_field = T(v);\n    return true;\n  }\n\n  static size_t EstimateSize(const T& prime_field) {\n    if constexpr (T::Config::kModulusBits <= 32) {\n      return sizeof(uint32_t);\n    } else {\n      return BigInt::kLimbNums * sizeof(uint64_t);\n    }\n  }\n};\n\n// static\ntemplate <typename T>\nbool Copyable<T, std::enable_if_t<std::is_base_of_v<math::PrimeFieldBase<T>,\n                                                    T>>>::s_is_in_montgomery =\n    false;\n\ntemplate <typename T>\nclass RapidJsonValueConverter<\n    T, std::enable_if_t<std::is_base_of_v<math::PrimeFieldBase<T>, T>>> {\n public:\n  using value_type = typename T::value_type;\n  using BigInt = typename T::BigIntTy;\n\n  static bool s_is_in_montgomery;\n\n  template <typename Allocator>\n  static rapidjson::Value From(const T& value, Allocator& allocator) {\n    if constexpr (T::Config::kUseMontgomery) {\n      if (s_is_in_montgomery) {\n        return RapidJsonValueConverter<value_type>::From(value.value(),\n                                                         allocator);\n      }\n    }\n    if constexpr (T::Config::kModulusBits <= 32) {\n      if constexpr (T::Config::kUseMontgomery) {\n        return RapidJsonValueConverter<uint32_t>::From(\n            T::Config::FromMontgomery(value.value()), allocator);\n      } else {\n        return RapidJsonValueConverter<uint32_t>::From(value.value(),\n                                                       allocator);\n      }\n    } else {\n      return RapidJsonValueConverter<BigInt>::From(value.ToBigInt(), allocator);\n    }\n  }\n\n  static bool To(const rapidjson::Value& json_value, std::string_view key,\n                 T* value, std::string* error) {\n    value_type v;\n    if (!RapidJsonValueConverter<value_type>::To(json_value, key, &v, error))\n      return false;\n\n    if constexpr (T::Config::kUseMontgomery) {\n      if (s_is_in_montgomery) {\n        *value = T::FromMontgomery(v);\n        return true;\n      }\n    }\n    *value = T(v);\n    return true;\n  }\n};\n\n// static\ntemplate <typename T>\nbool RapidJsonValueConverter<\n    T, std::enable_if_t<std::is_base_of_v<math::PrimeFieldBase<T>, T>>>::\n    s_is_in_montgomery = false;\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_PRIME_FIELD_BASE_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/prime_field_base_unittest.cc",
    "content": "#include \"tachyon/math/finite_fields/prime_field_base.h\"\n\n#include \"absl/hash/hash_testing.h\"\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/elliptic_curves/bn/bn254/fq.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/fr.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n\nnamespace tachyon::math {\n\nnamespace {\ntemplate <typename PrimeField>\nclass PrimeFieldBaseTest : public FiniteFieldTest<PrimeField> {};\n\n}  // namespace\n\nusing PrimeFieldTypes = testing::Types<bn254::Fq, bn254::Fr>;\n\nTYPED_TEST_SUITE(PrimeFieldBaseTest, PrimeFieldTypes);\n\nTYPED_TEST(PrimeFieldBaseTest, Decompose) {\n  using F = TypeParam;\n\n  if constexpr (F::Config::kHasLargeSubgroupRootOfUnity) {\n    for (size_t i = 0; i <= F::Config::kTwoAdicity; ++i) {\n      for (size_t j = 0; j <= F::Config::kSmallSubgroupAdicity; ++j) {\n        size_t n = (size_t{1} << i) *\n                   std::pow(size_t{F::Config::kSmallSubgroupBase}, j);\n\n        PrimeFieldFactors unused;\n        EXPECT_TRUE(F::Decompose(n, &unused));\n      }\n    }\n  } else {\n    GTEST_SKIP() << \"No LargeSubgroupRootOfUnity\";\n  }\n}\n\nTYPED_TEST(PrimeFieldBaseTest, TwoAdicRootOfUnity) {\n  using F = TypeParam;\n\n  F n = F(2).Pow(F::Config::kTwoAdicity);\n  EXPECT_EQ(F::FromMontgomery(F::Config::kTwoAdicRootOfUnity).Pow(n.ToBigInt()),\n            F::One());\n}\n\nTYPED_TEST(PrimeFieldBaseTest, LargeSubgroupOfUnity) {\n  using F = TypeParam;\n\n  if constexpr (F::Config::kHasLargeSubgroupRootOfUnity) {\n    F n =\n        F(2).Pow(F::Config::kTwoAdicity) *\n        F(F::Config::kSmallSubgroupBase).Pow(F::Config::kSmallSubgroupAdicity);\n    EXPECT_EQ(F::FromMontgomery(F::Config::kLargeSubgroupRootOfUnity)\n                  .Pow(n.ToBigInt()),\n              F::One());\n  } else {\n    GTEST_SKIP() << \"No LargeSubgroupRootOfUnity\";\n  }\n}\n\nTYPED_TEST(PrimeFieldBaseTest, GetRootOfUnity) {\n  using F = TypeParam;\n\n  if constexpr (F::Config::kHasLargeSubgroupRootOfUnity) {\n    for (size_t i = 0; i <= F::Config::kTwoAdicity; ++i) {\n      for (size_t j = 0; j <= F::Config::kSmallSubgroupAdicity; ++j) {\n        size_t n = (size_t{1} << i) *\n                   std::pow(size_t{F::Config::kSmallSubgroupBase}, j);\n        F root;\n        ASSERT_TRUE(F::GetRootOfUnity(n, &root));\n        ASSERT_EQ(root.Pow(n), F::One());\n      }\n    }\n  } else {\n    for (size_t i = 0; i <= F::Config::kTwoAdicity; ++i) {\n      size_t n = size_t{1} << i;\n      F root;\n      ASSERT_TRUE(F::GetRootOfUnity(n, &root));\n      ASSERT_EQ(root.Pow(n), F::One());\n    }\n  }\n}\n\nTYPED_TEST(PrimeFieldBaseTest, LegendreSymbol) {\n  using F = TypeParam;\n\n  F f = F::Random();\n  LegendreSymbol symbol = f.Legendre();\n  F expected;\n  switch (symbol) {\n    case LegendreSymbol::kOne:\n      expected = F::One();\n      break;\n    case LegendreSymbol::kMinusOne:\n      expected = F(F::Config::kModulus - typename F::BigIntTy(1));\n      break;\n    case LegendreSymbol::kZero:\n      expected = F::Zero();\n      break;\n  }\n  EXPECT_EQ(f.Pow(F::Config::kModulusMinusOneDivTwo), expected);\n}\n\nTYPED_TEST(PrimeFieldBaseTest, Hash) {\n  using F = TypeParam;\n\n  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(\n      std::make_tuple(F::Random(), F::Random())));\n}\n\nTYPED_TEST(PrimeFieldBaseTest, JsonValueConverter) {\n  using F = TypeParam;\n\n  F expected_point(3);\n  std::string expected_json = R\"(\"0x3\")\";\n\n  F p;\n  std::string error;\n\n  rapidjson::Document document;\n  document.Parse(expected_json.data(), expected_json.length());\n  base::RapidJsonValueConverter<F>::To(document, \"\", &p, &error);\n\n  ASSERT_TRUE(error.empty());\n  EXPECT_EQ(p, expected_point);\n\n  std::string json = base::WriteToJson(p);\n  EXPECT_EQ(json, expected_json);\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/finite_fields/prime_field_conversions.h",
    "content": "#ifndef TACHYON_MATH_FINITE_FIELDS_PRIME_FIELD_CONVERSIONS_H_\n#define TACHYON_MATH_FINITE_FIELDS_PRIME_FIELD_CONVERSIONS_H_\n\n#include \"tachyon/math/finite_fields/finite_field_forwards.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename SrcPrimeField, typename DstPrimeField>\nstruct PrimeFieldConversions;\n\ntemplate <typename DstPrimeField, typename SrcPrimeField>\nconstexpr DstPrimeField ConvertPrimeField(\n    const SrcPrimeField& src_prime_field) {\n  return PrimeFieldConversions<SrcPrimeField, DstPrimeField>::Convert(\n      src_prime_field);\n}\n\ntemplate <typename Config>\nstruct PrimeFieldConversions<PrimeField<Config>, PrimeField<Config>> {\n  constexpr static const PrimeField<Config>& Convert(\n      const PrimeField<Config>& src_prime_field) {\n    return reinterpret_cast<const PrimeField<Config>&>(src_prime_field);\n  }\n};\n\n#if TACHYON_CUDA\ntemplate <typename Config>\nstruct PrimeFieldConversions<PrimeField<Config>, PrimeFieldGpu<Config>> {\n  constexpr static const PrimeFieldGpu<Config>& Convert(\n      const PrimeField<Config>& src_prime_field) {\n    return reinterpret_cast<const PrimeFieldGpu<Config>&>(src_prime_field);\n  }\n};\n#endif  // TACHYON_CUDA\n\ntemplate <typename Config>\nstruct PrimeFieldConversions<PrimeField<Config>, PrimeFieldGpuDebug<Config>> {\n  constexpr static const PrimeFieldGpuDebug<Config>& Convert(\n      const PrimeField<Config>& src_prime_field) {\n    return reinterpret_cast<const PrimeFieldGpuDebug<Config>&>(src_prime_field);\n  }\n};\n\n#if TACHYON_CUDA\ntemplate <typename Config>\nstruct PrimeFieldConversions<PrimeFieldGpu<Config>, PrimeField<Config>> {\n  constexpr static const PrimeField<Config>& Convert(\n      const PrimeFieldGpu<Config>& src_prime_field) {\n    return reinterpret_cast<const PrimeField<Config>&>(src_prime_field);\n  }\n};\n\ntemplate <typename Config>\nstruct PrimeFieldConversions<PrimeFieldGpu<Config>,\n                             PrimeFieldGpuDebug<Config>> {\n  constexpr static const PrimeFieldGpuDebug<Config>& Convert(\n      const PrimeFieldGpu<Config>& src_prime_field) {\n    return reinterpret_cast<const PrimeFieldGpuDebug<Config>&>(src_prime_field);\n  }\n};\n#endif  // TACHYON_CUDA\n\ntemplate <typename Config>\nstruct PrimeFieldConversions<PrimeFieldGpuDebug<Config>, PrimeField<Config>> {\n  constexpr static const PrimeField<Config>& Convert(\n      const PrimeFieldGpuDebug<Config>& src_prime_field) {\n    return reinterpret_cast<const PrimeField<Config>&>(src_prime_field);\n  }\n};\n\n#if TACHYON_CUDA\ntemplate <typename Config>\nstruct PrimeFieldConversions<PrimeFieldGpuDebug<Config>,\n                             PrimeFieldGpu<Config>> {\n  constexpr static const PrimeFieldGpu<Config>& Convert(\n      const PrimeFieldGpuDebug<Config>& src_prime_field) {\n    return reinterpret_cast<const PrimeFieldGpu<Config>&>(src_prime_field);\n  }\n};\n#endif  // TACHYON_CUDA\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_PRIME_FIELD_CONVERSIONS_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/prime_field_correctness_gpu_test.cc",
    "content": "#include \"absl/strings/substitute.h\"\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/device/gpu/gpu_memory.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/fq.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/fq_gpu.h\"\n#include \"tachyon/math/finite_fields/kernels/prime_field_ops.cu.h\"\n#include \"tachyon/math/finite_fields/prime_field_conversions.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/test/launch_op_macros.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\nconstexpr size_t kThreadNum = 32;\n\n#define DEFINE_LAUNCH_FIELD_OP(method) \\\n  DEFINE_LAUNCH_BINARY_OP(kThreadNum, method, bn254::FqGpu, bn254::FqGpu)\n\nDEFINE_LAUNCH_FIELD_OP(Add)\nDEFINE_LAUNCH_FIELD_OP(Sub)\nDEFINE_LAUNCH_FIELD_OP(Mul)\nDEFINE_LAUNCH_FIELD_OP(Div)\n\n#undef DEFINE_LAUNCH_FIELD_OP\n\nusing namespace device;\n\nclass PrimeFieldCorrectnessGpuTest : public FiniteFieldTest<bn254::Fq> {\n public:\n  // Runs tests with |N| data.\n  constexpr static size_t N = kThreadNum * 2;\n\n  static void SetUpTestSuite() {\n    FiniteFieldTest<bn254::Fq>::SetUpTestSuite();\n\n    GPU_MUST_SUCCEED(gpuDeviceReset(), \"\");\n    xs_ = gpu::GpuMemory<bn254::FqGpu>::MallocManaged(N);\n    ys_ = gpu::GpuMemory<bn254::FqGpu>::MallocManaged(N);\n    results_ = gpu::GpuMemory<bn254::FqGpu>::MallocManaged(N);\n\n    x_cpus_.reserve(N);\n    y_cpus_.reserve(N);\n\n    for (size_t i = 0; i < N; ++i) {\n      bn254::Fq x_cpu = bn254::Fq::Random();\n      bn254::Fq y_cpu = bn254::Fq::Random();\n\n      xs_[i] = ConvertPrimeField<bn254::FqGpu>(x_cpu);\n      ys_[i] = ConvertPrimeField<bn254::FqGpu>(y_cpu);\n\n      x_cpus_.push_back(std::move(x_cpu));\n      y_cpus_.push_back(std::move(y_cpu));\n    }\n  }\n\n  static void TearDownTestSuite() {\n    xs_.reset();\n    ys_.reset();\n    results_.reset();\n\n    GPU_MUST_SUCCEED(gpuDeviceReset(), \"\");\n\n    x_cpus_.clear();\n    y_cpus_.clear();\n  }\n\n  void SetUp() override { CHECK(results_.Memset()); }\n\n protected:\n  static gpu::GpuMemory<bn254::FqGpu> xs_;\n  static gpu::GpuMemory<bn254::FqGpu> ys_;\n  static gpu::GpuMemory<bn254::FqGpu> results_;\n\n  static std::vector<bn254::Fq> x_cpus_;\n  static std::vector<bn254::Fq> y_cpus_;\n};\n\ngpu::GpuMemory<bn254::FqGpu> PrimeFieldCorrectnessGpuTest::xs_;\ngpu::GpuMemory<bn254::FqGpu> PrimeFieldCorrectnessGpuTest::ys_;\ngpu::GpuMemory<bn254::FqGpu> PrimeFieldCorrectnessGpuTest::results_;\n\nstd::vector<bn254::Fq> PrimeFieldCorrectnessGpuTest::x_cpus_;\nstd::vector<bn254::Fq> PrimeFieldCorrectnessGpuTest::y_cpus_;\n\n}  // namespace\n\n#define RUN_OPERATION_TESTS(method)                                            \\\n  GPU_MUST_SUCCEED(Launch##method(xs_.data(), ys_.data(), results_.data(), N), \\\n                   \"\");                                                        \\\n  for (size_t i = 0; i < N; ++i)\n\nTEST_F(PrimeFieldCorrectnessGpuTest, Add) {\n  RUN_OPERATION_TESTS(Add) {\n    SCOPED_TRACE(\n        absl::Substitute(\"a: $0, b: $1\", xs_[i].ToString(), ys_[i].ToString()));\n    ASSERT_EQ(ConvertPrimeField<bn254::Fq>(results_[i]),\n              x_cpus_[i] + y_cpus_[i]);\n  }\n}\n\nTEST_F(PrimeFieldCorrectnessGpuTest, Sub) {\n  RUN_OPERATION_TESTS(Sub) {\n    SCOPED_TRACE(\n        absl::Substitute(\"a: $0, b: $1\", xs_[i].ToString(), ys_[i].ToString()));\n    ASSERT_EQ(ConvertPrimeField<bn254::Fq>(results_[i]),\n              x_cpus_[i] - y_cpus_[i]);\n  }\n}\n\nTEST_F(PrimeFieldCorrectnessGpuTest, Mul) {\n  RUN_OPERATION_TESTS(Mul) {\n    SCOPED_TRACE(\n        absl::Substitute(\"a: $0, b: $1\", xs_[i].ToString(), ys_[i].ToString()));\n    ASSERT_EQ(ConvertPrimeField<bn254::Fq>(results_[i]),\n              x_cpus_[i] * y_cpus_[i]);\n  }\n}\n\nTEST_F(PrimeFieldCorrectnessGpuTest, Div) {\n  RUN_OPERATION_TESTS(Div) {\n    SCOPED_TRACE(\n        absl::Substitute(\"a: $0, b: $1\", xs_[i].ToString(), ys_[i].ToString()));\n    ASSERT_EQ(ConvertPrimeField<bn254::Fq>(results_[i]),\n              x_cpus_[i] / y_cpus_[i]);\n  }\n}\n\n#undef RUN_OPERATION_TESTS\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/finite_fields/prime_field_fallback.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_FINITE_FIELDS_PRIME_FIELD_FALLBACK_H_\n#define TACHYON_MATH_FINITE_FIELDS_PRIME_FIELD_FALLBACK_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <optional>\n#include <string>\n#include <utility>\n\n#include \"absl/base/call_once.h\"\n#include \"gtest/gtest_prod.h\"\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/math/base/arithmetics.h\"\n#include \"tachyon/math/base/big_int.h\"\n#include \"tachyon/math/base/byinverter.h\"\n#include \"tachyon/math/base/gmp/gmp_util.h\"\n#include \"tachyon/math/finite_fields/prime_field_base.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename Config>\nclass PrimeFieldGpu;\n\n// A prime field is finite field GF(p) where p is a prime number.\ntemplate <typename _Config>\nclass PrimeField<_Config, std::enable_if_t<!_Config::kUseAsm &&\n                                           (_Config::kModulusBits > 32)>>\n    final : public PrimeFieldBase<PrimeField<_Config>> {\n public:\n  constexpr static size_t kModulusBits = _Config::kModulusBits;\n  constexpr static size_t kLimbNums = (kModulusBits + 63) / 64;\n  constexpr static size_t N = kLimbNums;\n\n  using Config = _Config;\n  using BigIntTy = BigInt<N>;\n  using value_type = BigInt<N>;\n\n  using CpuField = PrimeField<Config>;\n  using GpuField = PrimeFieldGpu<Config>;\n\n  constexpr static BYInverter<N> inverter =\n      BYInverter<N>(Config::kModulus, Config::kMontgomeryR2);\n\n  constexpr PrimeField() = default;\n  template <typename T,\n            std::enable_if_t<std::is_constructible_v<BigInt<N>, T>>* = nullptr>\n  constexpr explicit PrimeField(T value) : PrimeField(BigInt<N>(value)) {}\n  constexpr explicit PrimeField(const BigInt<N>& value) : value_(value) {\n    DCHECK_LT(value_, Config::kModulus);\n    PrimeField p;\n    p.value_ = Config::kMontgomeryR2;\n    MulInPlace(p);\n  }\n  constexpr PrimeField(const PrimeField& other) = default;\n  constexpr PrimeField& operator=(const PrimeField& other) = default;\n  constexpr PrimeField(PrimeField&& other) = default;\n  constexpr PrimeField& operator=(PrimeField&& other) = default;\n\n  constexpr static PrimeField Zero() { return PrimeField(); }\n\n  constexpr static PrimeField One() {\n    PrimeField ret{};\n    ret.value_ = Config::kOne;\n    return ret;\n  }\n\n  constexpr static PrimeField MinusOne() {\n    PrimeField ret{};\n    ret.value_ = Config::kMinusOne;\n    return ret;\n  }\n\n  constexpr static PrimeField TwoInv() {\n    PrimeField ret{};\n    ret.value_ = Config::kTwoInv;\n    return ret;\n  }\n\n  static PrimeField Random() {\n    return PrimeField(BigInt<N>::Random(Config::kModulus));\n  }\n\n  constexpr static std::optional<PrimeField> FromDecString(\n      std::string_view str) {\n    std::optional<BigInt<N>> value = BigInt<N>::FromDecString(str);\n    if (!value.has_value()) return std::nullopt;\n    if (value >= Config::kModulus) {\n      LOG(ERROR) << \"value(\" << str << \") is greater than or equal to modulus\";\n      return std::nullopt;\n    }\n    return PrimeField(std::move(value).value());\n  }\n  constexpr static std::optional<PrimeField> FromHexString(\n      std::string_view str) {\n    std::optional<BigInt<N>> value = BigInt<N>::FromHexString(str);\n    if (!value.has_value()) return std::nullopt;\n    if (value >= Config::kModulus) {\n      LOG(ERROR) << \"value(\" << str << \") is greater than or equal to modulus\";\n      return std::nullopt;\n    }\n    return PrimeField(std::move(value).value());\n  }\n\n  constexpr static PrimeField FromBigInt(const BigInt<N>& big_int) {\n    return PrimeField(big_int);\n  }\n\n  constexpr static PrimeField FromMontgomery(const BigInt<N>& mont) {\n    PrimeField ret{};\n    ret.value_ = mont;\n    return ret;\n  }\n\n  static PrimeField FromMpzClass(const mpz_class& value) {\n    BigInt<N> big_int(0);\n    gmp::CopyLimbs(value, big_int.limbs);\n    return FromBigInt(big_int);\n  }\n\n  static void Init() {\n    static absl::once_flag once;\n    absl::call_once(once, []() {\n      Config::Init();\n      VLOG(1) << Config::kName << \" initialized\";\n    });\n  }\n\n  const value_type& value() const { return value_; }\n  size_t GetLimbSize() const { return N; }\n\n  constexpr bool IsZero() const { return value_.IsZero(); }\n\n  constexpr bool IsOne() const {\n    for (size_t i = 0; i < N; ++i) {\n      if (value_[i] != Config::kOne[i]) return false;\n    }\n    return true;\n  }\n\n  constexpr bool IsMinusOne() const {\n    for (size_t i = 0; i < N; ++i) {\n      if (value_[i] != Config::kMinusOne[i]) return false;\n    }\n    return true;\n  }\n\n  std::string ToString() const { return ToBigInt().ToString(); }\n  std::string ToHexString(bool pad_zero = false) const {\n    return ToBigInt().ToHexString(pad_zero);\n  }\n\n  mpz_class ToMpzClass() const {\n    mpz_class ret;\n    gmp::WriteLimbs(ToBigInt().limbs, N, &ret);\n    return ret;\n  }\n\n  // TODO(chokobole): Support bigendian.\n  constexpr BigInt<N> ToBigInt() const {\n    return BigInt<N>::FromMontgomery64(value_, Config::kModulus,\n                                       Config::kInverse64);\n  }\n\n  constexpr uint64_t& operator[](size_t i) { return value_[i]; }\n  constexpr const uint64_t& operator[](size_t i) const { return value_[i]; }\n\n  constexpr bool operator==(const PrimeField& other) const {\n    return value_ == other.value_;\n  }\n\n  constexpr bool operator!=(const PrimeField& other) const {\n    return value_ != other.value_;\n  }\n\n  constexpr bool operator<(const PrimeField& other) const {\n    return ToBigInt() < other.ToBigInt();\n  }\n\n  constexpr bool operator>(const PrimeField& other) const {\n    return ToBigInt() > other.ToBigInt();\n  }\n\n  constexpr bool operator<=(const PrimeField& other) const {\n    return ToBigInt() <= other.ToBigInt();\n  }\n\n  constexpr bool operator>=(const PrimeField& other) const {\n    return ToBigInt() >= other.ToBigInt();\n  }\n\n  // AdditiveSemigroup methods\n  constexpr PrimeField Add(const PrimeField& other) const {\n    PrimeField ret{};\n    uint64_t carry = 0;\n    ret.value_ = value_.Add(other.value_, carry);\n    BigInt<N>::template Clamp<Config::kModulusHasSpareBit>(Config::kModulus,\n                                                           &ret.value_, carry);\n    return ret;\n  }\n\n  constexpr PrimeField& AddInPlace(const PrimeField& other) {\n    uint64_t carry = 0;\n    value_.AddInPlace(other.value_, carry);\n    BigInt<N>::template Clamp<Config::kModulusHasSpareBit>(Config::kModulus,\n                                                           &value_, carry);\n    return *this;\n  }\n\n  constexpr PrimeField DoubleImpl() const {\n    PrimeField ret{};\n    uint64_t carry = 0;\n    ret.value_ = value_.MulBy2(carry);\n    BigInt<N>::template Clamp<Config::kModulusHasSpareBit>(Config::kModulus,\n                                                           &ret.value_, carry);\n    return ret;\n  }\n\n  constexpr PrimeField& DoubleImplInPlace() {\n    uint64_t carry = 0;\n    value_.MulBy2InPlace(carry);\n    BigInt<N>::template Clamp<Config::kModulusHasSpareBit>(Config::kModulus,\n                                                           &value_, carry);\n    return *this;\n  }\n\n  // AdditiveGroup methods\n  constexpr PrimeField Sub(const PrimeField& other) const {\n    PrimeField ret{};\n    if (other.value_ > value_) {\n      ret.value_ = value_.Add(Config::kModulus);\n      ret.value_.SubInPlace(other.value_);\n    } else {\n      ret.value_ = value_.Sub(other.value_);\n    }\n    return ret;\n  }\n\n  constexpr PrimeField& SubInPlace(const PrimeField& other) {\n    if (other.value_ > value_) {\n      value_.AddInPlace(Config::kModulus);\n    }\n    value_.SubInPlace(other.value_);\n    return *this;\n  }\n\n  constexpr PrimeField Negate() const {\n    PrimeField ret{};\n    if (!IsZero()) {\n      ret.value_ = Config::kModulus;\n      ret.value_.SubInPlace(value_);\n    }\n    return ret;\n  }\n\n  constexpr PrimeField& NegateInPlace() {\n    if (!IsZero()) {\n      BigInt<N> tmp(Config::kModulus);\n      tmp.SubInPlace(value_);\n      value_ = tmp;\n    }\n    return *this;\n  }\n\n  // TODO(chokobole): Support bigendian.\n  // MultiplicativeSemigroup methods\n  constexpr PrimeField Mul(const PrimeField& other) const {\n    PrimeField ret{};\n    if constexpr (Config::kCanUseNoCarryMulOptimization) {\n      DoFastMul(*this, other, ret);\n    } else {\n      DoSlowMul(*this, other, ret);\n    }\n    return ret;\n  }\n\n  constexpr PrimeField& MulInPlace(const PrimeField& other) {\n    if constexpr (Config::kCanUseNoCarryMulOptimization) {\n      DoFastMul(*this, other, *this);\n    } else {\n      DoSlowMul(*this, other, *this);\n    }\n    return *this;\n  }\n\n  constexpr PrimeField SquareImpl() const {\n    if (N == 1) {\n      return Mul(*this);\n    }\n    PrimeField ret{};\n    DoSquareImpl(*this, ret);\n    return ret;\n  }\n\n  constexpr PrimeField& SquareImplInPlace() {\n    if (N == 1) {\n      return MulInPlace(*this);\n    }\n    DoSquareImpl(*this, *this);\n    return *this;\n  }\n\n  // MultiplicativeGroup methods\n  constexpr std::optional<PrimeField> Inverse() const {\n    PrimeField ret{};\n    if (inverter.Invert(value_, ret.value_)) {\n      return ret;\n    }\n    LOG_IF_NOT_GPU(ERROR) << \"Inverse of zero attempted\";\n    return std::nullopt;\n  }\n\n  [[nodiscard]] constexpr std::optional<PrimeField*> InverseInPlace() {\n    if (inverter.Invert(value_, value_)) {\n      return this;\n    }\n    LOG_IF_NOT_GPU(ERROR) << \"Inverse of zero attempted\";\n    return std::nullopt;\n  }\n\n private:\n  template <typename PrimeField>\n  FRIEND_TEST(PrimeFieldCorrectnessTest, MultiplicativeOperators);\n\n  constexpr static void DoFastMul(const PrimeField& a, const PrimeField& b,\n                                  PrimeField& c) {\n    BigInt<N> r(0);\n    for (size_t i = 0; i < N; ++i) {\n      MulResult<uint64_t> result;\n      result = internal::u64::MulAddWithCarry(r[0], a[0], b[i]);\n      r[0] = result.lo;\n\n      uint64_t k = r[0] * Config::kInverse64;\n      MulResult<uint64_t> result2;\n      result2 = internal::u64::MulAddWithCarry(r[0], k, Config::kModulus[0]);\n\n      for (size_t j = 1; j < N; ++j) {\n        result = internal::u64::MulAddWithCarry(r[j], a[j], b[i], result.hi);\n        r[j] = result.lo;\n        result2 = internal::u64::MulAddWithCarry(r[j], k, Config::kModulus[j],\n                                                 result2.hi);\n        r[j - 1] = result2.lo;\n      }\n      r[N - 1] = result.hi + result2.hi;\n    }\n    c.value_ = r;\n    BigInt<N>::template Clamp<Config::kModulusHasSpareBit>(Config::kModulus,\n                                                           &c.value_, 0);\n  }\n\n  constexpr static void DoSlowMul(const PrimeField& a, const PrimeField& b,\n                                  PrimeField& c) {\n    BigInt<2 * N> r = a.value_.MulExtend(b.value_);\n    BigInt<N>::template MontgomeryReduce64<Config::kModulusHasSpareBit>(\n        r, Config::kModulus, Config::kInverse64, &c.value_);\n  }\n\n  constexpr static void DoSquareImpl(const PrimeField& a, PrimeField& b) {\n    BigInt<2 * N> r(0);\n    MulResult<uint64_t> mul_result;\n    for (size_t i = 0; i < N - 1; ++i) {\n      for (size_t j = i + 1; j < N; ++j) {\n        mul_result =\n            internal::u64::MulAddWithCarry(r[i + j], a[i], a[j], mul_result.hi);\n        r[i + j] = mul_result.lo;\n      }\n      r[i + N] = mul_result.hi;\n      mul_result.hi = 0;\n    }\n\n    r[2 * N - 1] = r[2 * N - 2] >> 63;\n    for (size_t i = 2; i < 2 * N - 1; ++i) {\n      r[2 * N - i] = (r[2 * N - i] << 1) | (r[2 * N - (i + 1)] >> 63);\n    }\n    r[1] <<= 1;\n\n    AddResult<uint64_t> add_result;\n    for (size_t i = 0; i < N; ++i) {\n      mul_result =\n          internal::u64::MulAddWithCarry(r[2 * i], a[i], a[i], mul_result.hi);\n      r[2 * i] = mul_result.lo;\n      add_result = internal::u64::AddWithCarry(r[2 * i + 1], mul_result.hi);\n      r[2 * i + 1] = add_result.result;\n      mul_result.hi = add_result.carry;\n    }\n    BigInt<N>::template MontgomeryReduce64<Config::kModulusHasSpareBit>(\n        r, Config::kModulus, Config::kInverse64, &b.value_);\n  }\n\n  BigInt<N> value_;\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_PRIME_FIELD_FALLBACK_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/prime_field_generator_unittest.cc",
    "content": "#include <optional>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/elliptic_curves/bls12/bls12_381/fq.h\"\n#include \"tachyon/math/elliptic_curves/bls12/bls12_381/fr.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/fq.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/fr.h\"\n#include \"tachyon/math/elliptic_curves/pasta/pallas/fq.h\"\n#include \"tachyon/math/elliptic_curves/pasta/pallas/fr.h\"\n#include \"tachyon/math/elliptic_curves/pasta/vesta/fq.h\"\n#include \"tachyon/math/elliptic_curves/pasta/vesta/fr.h\"\n#include \"tachyon/math/elliptic_curves/secp/secp256k1/fq.h\"\n#include \"tachyon/math/elliptic_curves/secp/secp256k1/fr.h\"\n#include \"tachyon/math/finite_fields/baby_bear/baby_bear.h\"\n#include \"tachyon/math/finite_fields/goldilocks/goldilocks.h\"\n#include \"tachyon/math/finite_fields/koala_bear/koala_bear.h\"\n#include \"tachyon/math/finite_fields/mersenne31/mersenne31.h\"\n\nnamespace tachyon::math {\n\nnamespace {\ntemplate <typename PrimeField>\nclass PrimeFieldGeneratorTest : public testing::Test {\n public:\n  static void SetUpTestSuite() { PrimeField::Init(); }\n};\n\n}  // namespace\n\nusing PrimeFieldTypes =\n    testing::Types<bls12_381::Fq, bls12_381::Fr, bn254::Fq, bn254::Fr,\n                   pallas::Fq, pallas::Fr, vesta::Fq, vesta::Fr, secp256k1::Fr,\n                   secp256k1::Fq, BabyBear, Goldilocks, KoalaBear, Mersenne31>;\n\nTYPED_TEST_SUITE(PrimeFieldGeneratorTest, PrimeFieldTypes);\n\nTYPED_TEST(PrimeFieldGeneratorTest, FromString) {\n  using PrimeField = TypeParam;\n  EXPECT_EQ(*PrimeField::FromDecString(\"3\"), PrimeField(3));\n  EXPECT_FALSE(PrimeField::FromDecString(\"x\").has_value());\n  EXPECT_EQ(*PrimeField::FromHexString(\"0x3\"), PrimeField(3));\n  EXPECT_FALSE(PrimeField::FromHexString(\"x\").has_value());\n}\n\nTYPED_TEST(PrimeFieldGeneratorTest, ToString) {\n  using PrimeField = TypeParam;\n  PrimeField f(3);\n\n  EXPECT_EQ(f.ToString(), \"3\");\n  EXPECT_EQ(f.ToHexString(), \"0x3\");\n}\n\nTYPED_TEST(PrimeFieldGeneratorTest, Zero) {\n  using PrimeField = TypeParam;\n  EXPECT_TRUE(PrimeField::Zero().IsZero());\n  EXPECT_FALSE(PrimeField::One().IsZero());\n  EXPECT_FALSE(PrimeField::MinusOne().IsZero());\n}\n\nTYPED_TEST(PrimeFieldGeneratorTest, One) {\n  using PrimeField = TypeParam;\n  EXPECT_TRUE(PrimeField::One().IsOne());\n  EXPECT_FALSE(PrimeField::Zero().IsOne());\n  EXPECT_FALSE(PrimeField::MinusOne().IsOne());\n  EXPECT_EQ(PrimeField::Config::kOne, PrimeField(1).value());\n}\n\nTYPED_TEST(PrimeFieldGeneratorTest, MinusOne) {\n  using PrimeField = TypeParam;\n  EXPECT_TRUE(PrimeField::MinusOne().IsMinusOne());\n  EXPECT_FALSE(PrimeField::Zero().IsMinusOne());\n  EXPECT_FALSE(PrimeField::One().IsMinusOne());\n}\n\nTYPED_TEST(PrimeFieldGeneratorTest, TwoInv) {\n  using PrimeField = TypeParam;\n  EXPECT_TRUE((PrimeField::TwoInv() * PrimeField(2)).IsOne());\n  EXPECT_FALSE((PrimeField::TwoInv() * PrimeField::One()).IsOne());\n}\n\nTYPED_TEST(PrimeFieldGeneratorTest, BigIntConversion) {\n  using PrimeField = TypeParam;\n  PrimeField r = PrimeField::Random();\n  EXPECT_EQ(PrimeField::FromBigInt(r.ToBigInt()), r);\n}\n\nTYPED_TEST(PrimeFieldGeneratorTest, EqualityOperators) {\n  using PrimeField = TypeParam;\n  PrimeField f(3);\n  PrimeField f2(4);\n  EXPECT_EQ(f, f);\n  EXPECT_NE(f, f2);\n}\n\nTYPED_TEST(PrimeFieldGeneratorTest, ComparisonOperator) {\n  using PrimeField = TypeParam;\n  PrimeField f(3);\n  PrimeField f2(4);\n  EXPECT_LT(f, f2);\n  EXPECT_LE(f, f2);\n  EXPECT_GT(f2, f);\n  EXPECT_GE(f2, f);\n}\n\nTYPED_TEST(PrimeFieldGeneratorTest, AdditiveGroupOperators) {\n  using PrimeField = TypeParam;\n  PrimeField f = PrimeField::Random();\n  SCOPED_TRACE(absl::Substitute(\"f: $0\", f.ToString()));\n  PrimeField f_neg = -f;\n  EXPECT_TRUE((f_neg + f).IsZero());\n  f.NegateInPlace();\n  EXPECT_EQ(f, f_neg);\n\n  PrimeField f_double = f.Double();\n  EXPECT_EQ(f + f, f_double);\n  f.DoubleInPlace();\n  EXPECT_EQ(f, f_double);\n}\n\nTYPED_TEST(PrimeFieldGeneratorTest, MultiplicativeGroupOperators) {\n  using PrimeField = TypeParam;\n  PrimeField f = PrimeField::Random();\n  SCOPED_TRACE(absl::Substitute(\"f: $0\", f.ToString()));\n  std::optional<PrimeField> f_inv = f.Inverse();\n  if (UNLIKELY(f.IsZero())) {\n    ASSERT_FALSE(f_inv);\n    ASSERT_FALSE(f.InverseInPlace());\n  } else {\n    EXPECT_EQ(f * *f_inv, PrimeField::One());\n    EXPECT_EQ(**f.InverseInPlace(), f_inv);\n  }\n\n  PrimeField f_sqr = f.Square();\n  EXPECT_EQ(f * f, f_sqr);\n  f.SquareInPlace();\n  EXPECT_EQ(f, f_sqr);\n\n  PrimeField f_pow = f.Pow(5);\n  EXPECT_EQ(f * f * f * f * f, f_pow);\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/finite_fields/prime_field_gpu.h",
    "content": "// Copyright (c) 2022 Matter Labs\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.era-bellman-cuda and the\n// LICENCE-APACHE.era-bellman-cuda file.\n\n#ifndef TACHYON_MATH_FINITE_FIELDS_PRIME_FIELD_GPU_H_\n#define TACHYON_MATH_FINITE_FIELDS_PRIME_FIELD_GPU_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <optional>\n#include <string>\n#include <utility>\n\n#include \"absl/base/call_once.h\"\n\n#if TACHYON_CUDA\n#include \"third_party/gpus/cuda/include/cuda_runtime.h\"\n#endif\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/math/base/arithmetics.h\"\n#include \"tachyon/math/base/big_int.h\"\n#include \"tachyon/math/base/gmp/gmp_util.h\"\n#include \"tachyon/math/finite_fields/finite_field_forwards.h\"\n#include \"tachyon/math/finite_fields/kernels/carry_chain.h\"\n#include \"tachyon/math/finite_fields/modulus.h\"\n#include \"tachyon/math/finite_fields/prime_field_base.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename _Config>\nclass PrimeFieldGpu final : public PrimeFieldBase<PrimeFieldGpu<_Config>> {\n public:\n  constexpr static size_t kModulusBits = _Config::kModulusBits;\n  constexpr static size_t kLimbNums = (kModulusBits + 63) / 64;\n  constexpr static size_t N = kLimbNums;\n  constexpr static size_t N32 = kLimbNums * 2;\n\n  using Config = _Config;\n  using BigIntTy = BigInt<N>;\n  using value_type = BigInt<N>;\n\n  using CpuField = PrimeField<Config>;\n  using GpuField = PrimeFieldGpu<Config>;\n\n  constexpr PrimeFieldGpu() = default;\n  template <typename T,\n            std::enable_if_t<std::is_constructible_v<BigInt<N>, T>>* = nullptr>\n  constexpr explicit PrimeFieldGpu(T value) : PrimeFieldGpu(BigInt<N>(value)) {}\n  constexpr explicit PrimeFieldGpu(const BigInt<N>& value) {\n    DCHECK_LT(value, GetModulus());\n    PrimeField<Config> p(value);\n    value_ = p.value();\n  }\n  constexpr PrimeFieldGpu(const PrimeFieldGpu& other) = default;\n  constexpr PrimeFieldGpu& operator=(const PrimeFieldGpu& other) = default;\n  constexpr PrimeFieldGpu(PrimeFieldGpu&& other) = default;\n  constexpr PrimeFieldGpu& operator=(PrimeFieldGpu&& other) = default;\n\n  constexpr static PrimeFieldGpu Zero() { return PrimeFieldGpu(); }\n\n  constexpr static PrimeFieldGpu One() {\n    PrimeFieldGpu ret{};\n    ret.value_ = GetOne();\n    return ret;\n  }\n\n  static PrimeFieldGpu Random() {\n    return PrimeFieldGpu(BigInt<N>::Random(Config::kModulus));\n  }\n\n  constexpr static std::optional<PrimeFieldGpu> FromDecString(\n      std::string_view str) {\n    std::optional<BigInt<N>> value = BigInt<N>::FromDecString(str);\n    if (!value.has_value()) return std::nullopt;\n    if (value >= Config::kModulus) {\n      LOG(ERROR) << \"value(\" << str << \") is greater than or equal to modulus\";\n      return std::nullopt;\n    }\n    return PrimeFieldGpu(std::move(value).value());\n  }\n  constexpr static std::optional<PrimeFieldGpu> FromHexString(\n      std::string_view str) {\n    std::optional<BigInt<N>> value = BigInt<N>::FromHexString(str);\n    if (!value.has_value()) return std::nullopt;\n    if (value >= Config::kModulus) {\n      LOG(ERROR) << \"value(\" << str << \") is greater than or equal to modulus\";\n      return std::nullopt;\n    }\n    return PrimeFieldGpu(std::move(value).value());\n  }\n\n  constexpr static PrimeFieldGpu FromBigInt(const BigInt<N>& big_int) {\n    return PrimeFieldGpu(big_int);\n  }\n\n  constexpr static PrimeFieldGpu FromMontgomery(const BigInt<N>& mont) {\n    PrimeFieldGpu ret{};\n    ret.value_ = mont;\n    return ret;\n  }\n\n  static PrimeFieldGpu FromMpzClass(const mpz_class& value) {\n    BigInt<N> big_int(0);\n    gmp::CopyLimbs(value, big_int.limbs);\n    return FromBigInt(big_int);\n  }\n\n  static void Init() {\n    static absl::once_flag once;\n    absl::call_once(once, []() {\n      Config::Init();\n      VLOG(1) << Config::kName << \" initialized\";\n    });\n  }\n\n  constexpr const value_type& value() const { return value_; }\n  constexpr size_t GetLimbSize() const { return N; }\n\n  constexpr bool IsZero() const {\n    const uint64_t* x = value_.limbs;\n    uint64_t limbs_or = x[0];\n    for (size_t i = 1; i < N; ++i) limbs_or |= x[i];\n    return limbs_or == 0;\n  }\n\n  constexpr bool IsOne() const {\n    BigInt<N> one = GetOne();\n    for (size_t i = 0; i < N; ++i) {\n      if (value_[i] != one[i]) return false;\n    }\n    return true;\n  }\n\n  constexpr bool IsEven() const { return value_.IsEven(); }\n\n  constexpr bool IsOdd() const { return value_.IsOdd(); }\n\n  std::string ToString() const { return ToBigIntHost().ToString(); }\n  std::string ToHexString(bool pad_zero = false) const {\n    return ToBigIntHost().ToHexString(pad_zero);\n  }\n\n  mpz_class ToMpzClass() const {\n    mpz_class ret;\n    gmp::WriteLimbs(ToBigIntHost().limbs, N, &ret);\n    return ret;\n  }\n\n  __device__ constexpr BigInt<N> ToBigInt() const {\n    return this->operator*(FromMontgomery(BigInt<N>(1))).value_;\n  }\n\n  constexpr BigInt<N> ToBigIntHost() const {\n    return BigInt<N>::FromMontgomery64(value_, Config::kModulus,\n                                       Config::kInverse64);\n  }\n\n  // This is needed by MSM.\n  // See\n  // tachyon/math/elliptic_curves/msm/kernels/variable_base_msm_kernels.cu.h\n  __device__ constexpr uint32_t ExtractBits(unsigned int offset,\n                                            unsigned int count) const {\n    unsigned int limb_index = offset / warpSize;\n    const uint32_t* x = reinterpret_cast<const uint32_t*>(value_.limbs);\n    const uint32_t low_limb = x[limb_index];\n    const uint32_t high_limb = limb_index < (N32 - 1) ? x[limb_index + 1] : 0;\n    uint32_t result = __funnelshift_r(low_limb, high_limb, offset);\n    result &= (uint32_t{1} << count) - 1;\n    return result;\n  }\n\n  constexpr uint64_t& operator[](size_t i) { return value_[i]; }\n  constexpr const uint64_t& operator[](size_t i) const { return value_[i]; }\n\n  constexpr bool operator==(const PrimeFieldGpu& other) const {\n    const uint64_t* x = value_.limbs;\n    const uint64_t* y = other.value_.limbs;\n    uint64_t limbs_or = x[0] ^ y[0];\n    for (size_t i = 1; i < N; ++i) limbs_or |= x[i] ^ y[i];\n    return limbs_or == 0;\n  }\n\n  constexpr bool operator!=(const PrimeFieldGpu& other) const {\n    return !operator==(other);\n  }\n\n  __device__ constexpr bool operator<(const PrimeFieldGpu& other) const {\n    PrimeFieldGpu results;\n    uint64_t carry = SubLimbs<true>(value_, other.value_, results.value_);\n    return carry;\n  }\n\n  __device__ constexpr bool operator>(const PrimeFieldGpu& other) const {\n    PrimeFieldGpu results;\n    uint64_t carry = SubLimbs<true>(other.value_, value_, results.value_);\n    return carry;\n  }\n\n  __device__ constexpr bool operator<=(const PrimeFieldGpu& other) const {\n    return !operator>(other);\n  }\n\n  __device__ constexpr bool operator>=(const PrimeFieldGpu& other) const {\n    return !operator<(other);\n  }\n\n  // AdditiveSemigroup methods\n  __device__ constexpr PrimeFieldGpu Add(const PrimeFieldGpu& other) const {\n    PrimeFieldGpu ret{};\n    AddLimbs<false>(value_, other.value_, ret.value_);\n    return Clamp(ret);\n  }\n\n  __device__ constexpr PrimeFieldGpu& AddInPlace(const PrimeFieldGpu& other) {\n    AddLimbs<false>(value_, other.value_, value_);\n    *this = Clamp(*this);\n    return *this;\n  }\n\n  // AdditiveGroup methods\n  __device__ constexpr PrimeFieldGpu Sub(const PrimeFieldGpu& other) const {\n    PrimeFieldGpu ret{};\n    uint64_t carry = SubLimbs<true>(value_, other.value_, ret.value_);\n    if (carry == 0) return ret;\n    AddLimbs<false>(ret.value_, GetModulus(), ret.value_);\n    return ret;\n  }\n\n  __device__ constexpr PrimeFieldGpu& SubInPlace(const PrimeFieldGpu& other) {\n    uint64_t carry = SubLimbs<true>(value_, other.value_, value_);\n    if (carry == 0) return *this;\n    AddLimbs<false>(value_, GetModulus(), value_);\n    return *this;\n  }\n\n  __device__ constexpr PrimeFieldGpu Negate() const {\n    PrimeFieldGpu ret{};\n    SubLimbs<false>(GetModulus(), value_, ret.value_);\n    return ret;\n  }\n\n  __device__ constexpr PrimeFieldGpu& NegateInPlace() {\n    BigInt<N> result;\n    SubLimbs<false>(GetModulus(), value_, result);\n    value_ = result;\n    return *this;\n  }\n\n  // MultiplicativeSemigroup methods\n  __device__ constexpr PrimeFieldGpu Mul(const PrimeFieldGpu& other) const {\n    // Forces us to think more carefully about the last carry bit if we use a\n    // modulus with fewer than 2 leading zeroes of slack.\n    static_assert(!(Config::kModulus[N - 1] >> 62));\n    PrimeFieldGpu ret{};\n    MulLimbs(value_, other.value_, ret.value_);\n    return Clamp(ret);\n  }\n\n  __device__ constexpr PrimeFieldGpu& MulInPlace(const PrimeFieldGpu& other) {\n    // Forces us to think more carefully about the last carry bit if we use a\n    // modulus with fewer than 2 leading zeroes of slack.\n    static_assert(!(Config::kModulus[N - 1] >> 62));\n    BigInt<N> result;\n    MulLimbs(value_, other.value_, result);\n    value_ = result;\n    *this = Clamp(*this);\n    return *this;\n  }\n\n  // MultiplicativeGroup methods\n  __device__ constexpr std::optional<PrimeFieldGpu> Inverse() const {\n    PrimeFieldGpu ret{};\n    if (UNLIKELY(!DoInverse(*this, ret))) {\n      // TODO(ashjeong): add CUDA error logging\n      return std::nullopt;\n    }\n    return ret;\n  }\n\n  [[nodiscard]] __device__ constexpr std::optional<PrimeFieldGpu*>\n  InverseInPlace() {\n    if (UNLIKELY(!DoInverse(*this, *this))) {\n      // TODO(ashjeong): add CUDA error logging\n      return std::nullopt;\n    }\n    return this;\n  }\n\n private:\n  constexpr static BigInt<N> GetModulus() { return Config::kModulus; }\n\n  constexpr static BigInt<N> GetOne() { return Config::kOne; }\n\n  template <bool CarryOut>\n  __device__ constexpr static uint64_t AddLimbs(const BigInt<N>& xs,\n                                                const BigInt<N>& ys,\n                                                BigInt<N>& results) {\n    const uint64_t* x = xs.limbs;\n    const uint64_t* y = ys.limbs;\n    uint64_t* r = results.limbs;\n    kernels::u64::CarryChain<CarryOut ? N + 1 : N> chain;\n    for (size_t i = 0; i < N; ++i) {\n      r[i] = chain.Add(x[i], y[i]);\n    }\n    if constexpr (!CarryOut) return 0;\n    return chain.Add(0, 0);\n  }\n\n  template <bool CarryOut>\n  __device__ constexpr static uint64_t SubLimbs(const BigInt<N>& xs,\n                                                const BigInt<N>& ys,\n                                                BigInt<N>& results) {\n    const uint64_t* x = xs.limbs;\n    const uint64_t* y = ys.limbs;\n    uint64_t* r = results.limbs;\n    kernels::u64::CarryChain<CarryOut ? N + 1 : N> chain;\n    for (size_t i = 0; i < N; ++i) {\n      r[i] = chain.Sub(x[i], y[i]);\n    }\n    if constexpr (!CarryOut) return 0;\n    return chain.Sub(0, 0);\n  }\n\n  __device__ constexpr static void MulLimbs(const BigInt<N>& xs,\n                                            const BigInt<N>& ys,\n                                            BigInt<N>& results) {\n    constexpr uint32_t n = N32;\n    const uint32_t* x = reinterpret_cast<const uint32_t*>(xs.limbs);\n    const uint32_t* y = reinterpret_cast<const uint32_t*>(ys.limbs);\n    uint32_t* even = reinterpret_cast<uint32_t*>(results.limbs);\n\n    ALIGNAS(8)\n    uint32_t odd[n + 1] = {\n        0,\n    };\n    size_t i;\n    for (i = 0; i < n; i += 2) {\n      MadNRedc(&even[0], &odd[0], x, y[i], i == 0);\n      MadNRedc(&odd[0], &even[0], x, y[i + 1]);\n    }\n\n    // merge |even| and |odd|\n    even[0] = ptx::u32::AddCc(even[0], odd[1]);\n    for (i = 1; i < n - 1; ++i) {\n      even[i] = ptx::u32::AddcCc(even[i], odd[i + 1]);\n    }\n    even[i] = ptx::u32::Addc(even[i], 0);\n\n    // final reduction from [0, 2 * mod) to [0, mod) not done here, instead\n    // performed optionally in MulInPlace.\n  }\n\n  __device__ constexpr static BigInt<N> DivBy2Limbs(const BigInt<N>& xs) {\n    BigInt<N> results;\n    const uint32_t* x = reinterpret_cast<const uint32_t*>(xs.limbs);\n    uint32_t* r = reinterpret_cast<uint32_t*>(results.limbs);\n    for (size_t i = 0; i < N32 - 1; ++i) {\n      r[i] = uint32_t{__funnelshift_rc(x[i], x[i + 1], 1)};\n    }\n    r[N32 - 1] = x[N32 - 1] >> 1;\n    return results;\n  }\n\n  // The following algorithms are adaptations of\n  // http://www.acsel-lab.com/arithmetic/arith23/data/1616a047.pdf,\n  // taken from https://github.com/z-prize/test-msm-gpu (under Apache 2.0\n  // license) and modified to use our datatypes. We had our own implementation\n  // of http://www.acsel-lab.com/arithmetic/arith23/data/1616a047.pdf, but the\n  // sppark versions achieved lower instruction count thanks to clever carry\n  // handling, so we decided to just use theirs.\n\n  __device__ constexpr static void MulN(uint32_t* acc, const uint32_t* a,\n                                        uint32_t bi, size_t n = N32) {\n    for (size_t i = 0; i < n; i += 2) {\n      acc[i] = ptx::u32::MulLo(a[i], bi);\n      acc[i + 1] = ptx::u32::MulHi(a[i], bi);\n    }\n  }\n\n  __device__ constexpr static void CMadN(uint32_t* acc, const uint32_t* a,\n                                         uint32_t bi, size_t n = N32) {\n    acc[0] = ptx::u32::MadLoCc(a[0], bi, acc[0]);\n    acc[1] = ptx::u32::MadcHiCc(a[0], bi, acc[1]);\n    for (size_t i = 2; i < n; i += 2) {\n      acc[i] = ptx::u32::MadcLoCc(a[i], bi, acc[i]);\n      acc[i + 1] = ptx::u32::MadcHiCc(a[i], bi, acc[i + 1]);\n    }\n  }\n\n  __device__ constexpr static void MadcNRshift(uint32_t* odd, const uint32_t* a,\n                                               uint32_t bi) {\n    constexpr uint32_t n = N32;\n    for (size_t i = 0; i < n - 2; i += 2) {\n      odd[i] = ptx::u32::MadcLoCc(a[i], bi, odd[i + 2]);\n      odd[i + 1] = ptx::u32::MadcHiCc(a[i], bi, odd[i + 3]);\n    }\n    odd[n - 2] = ptx::u32::MadcLoCc(a[n - 2], bi, 0);\n    odd[n - 1] = ptx::u32::MadcHi(a[n - 2], bi, 0);\n  }\n\n  __device__ constexpr static void MadNRedc(uint32_t* even, uint32_t* odd,\n                                            const uint32_t* a, uint32_t bi,\n                                            bool first = false) {\n    constexpr uint32_t n = N32;\n    const uint32_t* const modulus =\n        reinterpret_cast<const uint32_t* const>(GetModulus().limbs);\n    if (first) {\n      MulN(odd, a + 1, bi);\n      MulN(even, a, bi);\n    } else {\n      even[0] = ptx::u32::AddCc(even[0], odd[1]);\n      MadcNRshift(odd, a + 1, bi);\n      CMadN(even, a, bi);\n      odd[n - 1] = ptx::u32::Addc(odd[n - 1], 0);\n    }\n    uint32_t mi = even[0] * Config::kInverse32;\n    CMadN(odd, modulus + 1, mi);\n    CMadN(even, modulus, mi);\n    odd[n - 1] = ptx::u32::Addc(odd[n - 1], 0);\n  }\n\n  __device__ constexpr static PrimeFieldGpu Clamp(PrimeFieldGpu& xs) {\n    PrimeFieldGpu results;\n    return SubLimbs<true>(xs.value_, GetModulus(), results.value_) ? xs\n                                                                   : results;\n  }\n\n  [[nodiscard]] __device__ constexpr static bool DoInverse(\n      const PrimeFieldGpu& a, PrimeFieldGpu& b) {\n    if (UNLIKELY(a.IsZero())) {\n      // TODO(ashjeong): add CUDA error logging\n      return false;\n    }\n\n    BigInt<N> u = a.value_;\n    BigInt<N> v = GetModulus();\n    PrimeFieldGpu c;\n    c.value_ = Config::kMontgomeryR2;\n    // NOTE(chokobole): Do not use this.\n    // PrimeFieldGpu d = PrimeFieldGpu::Zero();\n    PrimeFieldGpu d;\n\n    while (!u.IsOne() && !v.IsOne()) {\n      while (u.IsEven()) {\n        u = DivBy2Limbs(u);\n        if (c.IsOdd()) AddLimbs<false>(c.value_, GetModulus(), c.value_);\n        c.value_ = DivBy2Limbs(c.value_);\n      }\n\n      while (v.IsEven()) {\n        v = DivBy2Limbs(v);\n        if (d.IsOdd()) AddLimbs<false>(d.value_, GetModulus(), d.value_);\n        d.value_ = DivBy2Limbs(d.value_);\n      }\n\n      if (v < u) {\n        SubLimbs<false>(u, v, u);\n        c -= d;\n      } else {\n        SubLimbs<false>(v, u, v);\n        d -= c;\n      }\n    }\n    if (u.IsOne()) {\n      b = c;\n    } else {\n      b = d;\n    }\n    return true;\n  }\n\n  BigInt<N> value_;\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_PRIME_FIELD_GPU_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/prime_field_gpu_debug.h",
    "content": "#ifndef TACHYON_MATH_FINITE_FIELDS_PRIME_FIELD_GPU_DEBUG_H_\n#define TACHYON_MATH_FINITE_FIELDS_PRIME_FIELD_GPU_DEBUG_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <optional>\n#include <string>\n#include <utility>\n\n#include \"tachyon/base/random.h\"\n#include \"tachyon/math/base/arithmetics.h\"\n#include \"tachyon/math/base/big_int.h\"\n#include \"tachyon/math/base/gmp/gmp_util.h\"\n#include \"tachyon/math/finite_fields/carry_chain.h\"\n#include \"tachyon/math/finite_fields/finite_field_forwards.h\"\n#include \"tachyon/math/finite_fields/modulus.h\"\n#include \"tachyon/math/finite_fields/prime_field_base.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename Config>\nclass PrimeFieldGpu;\n\ntemplate <typename _Config>\nclass PrimeFieldGpuDebug final\n    : public PrimeFieldBase<PrimeFieldGpuDebug<_Config>> {\n public:\n  constexpr static size_t kModulusBits = _Config::kModulusBits;\n  constexpr static size_t kLimbNums = (kModulusBits + 63) / 64;\n  constexpr static size_t N = kLimbNums;\n  constexpr static size_t N32 = kLimbNums * 2;\n\n  using Config = _Config;\n  using BigIntTy = BigInt<N>;\n  using value_type = BigInt<N>;\n\n  using CpuField = PrimeFieldGpuDebug<Config>;\n  using GpuField = PrimeFieldGpu<Config>;\n\n  constexpr PrimeFieldGpuDebug() = default;\n  template <typename T,\n            std::enable_if_t<std::is_constructible_v<BigInt<N>, T>>* = nullptr>\n  constexpr explicit PrimeFieldGpuDebug(T value)\n      : PrimeFieldGpuDebug(BigInt<N>(value)) {}\n  constexpr explicit PrimeFieldGpuDebug(const BigInt<N>& value) {\n    DCHECK_LT(value, Config::kModulus);\n    PrimeField<Config> p(value);\n    value_ = p.value();\n  }\n  constexpr PrimeFieldGpuDebug(const PrimeFieldGpuDebug& other) = default;\n  constexpr PrimeFieldGpuDebug& operator=(const PrimeFieldGpuDebug& other) =\n      default;\n  constexpr PrimeFieldGpuDebug(PrimeFieldGpuDebug&& other) = default;\n  constexpr PrimeFieldGpuDebug& operator=(PrimeFieldGpuDebug&& other) = default;\n\n  constexpr static PrimeFieldGpuDebug Zero() { return PrimeFieldGpuDebug(); }\n\n  constexpr static PrimeFieldGpuDebug One() {\n    PrimeFieldGpuDebug ret{};\n    ret.value_ = Config::kOne;\n    return ret;\n  }\n\n  constexpr static PrimeFieldGpuDebug MinusOne() {\n    PrimeFieldGpuDebug ret{};\n    ret.value_ = Config::kMinusOne;\n    return ret;\n  }\n\n  constexpr static PrimeFieldGpuDebug TwoInv() {\n    PrimeFieldGpuDebug ret{};\n    ret.value_ = Config::kTwoInv;\n    return ret;\n  }\n\n  static PrimeFieldGpuDebug Random() {\n    PrimeFieldGpuDebug ret{};\n    ret.value_ = PrimeField<Config>::Random().value();\n    return ret;\n  }\n\n  constexpr static std::optional<PrimeFieldGpuDebug> FromDecString(\n      std::string_view str) {\n    std::optional<BigInt<N>> value = BigInt<N>::FromDecString(str);\n    if (!value.has_value()) return std::nullopt;\n    if (value >= Config::kModulus) {\n      LOG(ERROR) << \"value(\" << str << \") is greater than or equal to modulus\";\n      return std::nullopt;\n    }\n    return PrimeFieldGpuDebug(std::move(value).value());\n  }\n  constexpr static std::optional<PrimeFieldGpuDebug> FromHexString(\n      std::string_view str) {\n    std::optional<BigInt<N>> value = BigInt<N>::FromHexString(str);\n    if (!value.has_value()) return std::nullopt;\n    if (value >= Config::kModulus) {\n      LOG(ERROR) << \"value(\" << str << \") is greater than or equal to modulus\";\n      return std::nullopt;\n    }\n    return PrimeFieldGpuDebug(std::move(value).value());\n  }\n\n  constexpr static PrimeFieldGpuDebug FromBigInt(const BigInt<N>& big_int) {\n    return PrimeFieldGpuDebug(big_int);\n  }\n\n  constexpr static PrimeFieldGpuDebug FromMontgomery(const BigInt<N>& mont) {\n    PrimeFieldGpuDebug ret{};\n    ret.value_ = mont;\n    return ret;\n  }\n\n  static PrimeFieldGpuDebug FromMpzClass(const mpz_class& value) {\n    BigInt<N> big_int(0);\n    gmp::CopyLimbs(value, big_int.limbs);\n    return FromBigInt(big_int);\n  }\n\n  static void Init() {\n    // Do nothing.\n  }\n\n  const value_type& value() const { return value_; }\n  size_t GetLimbSize() const { return N; }\n\n  constexpr bool IsZero() const { return value_.IsZero(); }\n\n  constexpr bool IsOne() const {\n    for (size_t i = 0; i < N; ++i) {\n      if (value_[i] != Config::kOne[i]) return false;\n    }\n    return true;\n  }\n\n  constexpr bool IsEven() const { return value_.IsEven(); }\n\n  constexpr bool IsOdd() const { return value_.IsOdd(); }\n\n  std::string ToString() const { return ToBigInt().ToString(); }\n  std::string ToHexString(bool pad_zero = false) const {\n    return ToBigInt().ToHexString(pad_zero);\n  }\n\n  mpz_class ToMpzClass() const {\n    mpz_class ret;\n    gmp::WriteLimbs(ToBigInt().limbs, N, &ret);\n    return ret;\n  }\n\n  constexpr BigInt<N> ToBigInt() const {\n    return BigInt<N>::FromMontgomery64(value_, Config::kModulus,\n                                       Config::kInverse64);\n  }\n\n  constexpr uint64_t& operator[](size_t i) { return value_[i]; }\n  constexpr const uint64_t& operator[](size_t i) const { return value_[i]; }\n\n  constexpr bool operator==(const PrimeFieldGpuDebug& other) const {\n    return value_ == other.value_;\n  }\n\n  constexpr bool operator!=(const PrimeFieldGpuDebug& other) const {\n    return value_ != other.value_;\n  }\n\n  constexpr bool operator<(const PrimeFieldGpuDebug& other) const {\n    return ToBigInt() < other.ToBigInt();\n  }\n\n  constexpr bool operator>(const PrimeFieldGpuDebug& other) const {\n    return ToBigInt() > other.ToBigInt();\n  }\n\n  constexpr bool operator<=(const PrimeFieldGpuDebug& other) const {\n    return ToBigInt() <= other.ToBigInt();\n  }\n\n  constexpr bool operator>=(const PrimeFieldGpuDebug& other) const {\n    return ToBigInt() >= other.ToBigInt();\n  }\n\n  // AdditiveSemigroup methods\n  constexpr PrimeFieldGpuDebug Add(const PrimeFieldGpuDebug& other) const {\n    PrimeFieldGpuDebug ret{};\n    AddLimbs<false>(value_, other.value_, ret.value_);\n    return Clamp(ret);\n  }\n\n  constexpr PrimeFieldGpuDebug& AddInPlace(const PrimeFieldGpuDebug& other) {\n    AddLimbs<false>(value_, other.value_, value_);\n    *this = Clamp(*this);\n    return *this;\n  }\n\n  // AdditiveGroup methods\n  constexpr PrimeFieldGpuDebug Sub(const PrimeFieldGpuDebug& other) const {\n    PrimeFieldGpuDebug ret{};\n    uint64_t carry = SubLimbs<true>(value_, other.value_, ret.value_);\n    if (carry == 0) return ret;\n    AddLimbs<false>(ret.value_, Config::kModulus, ret.value_);\n    return ret;\n  }\n\n  constexpr PrimeFieldGpuDebug& SubInPlace(const PrimeFieldGpuDebug& other) {\n    uint64_t carry = SubLimbs<true>(value_, other.value_, value_);\n    if (carry == 0) return *this;\n    AddLimbs<false>(value_, Config::kModulus, value_);\n    return *this;\n  }\n\n  constexpr PrimeFieldGpuDebug Negate() const {\n    PrimeFieldGpuDebug ret{};\n    SubLimbs<false>(Config::kModulus, value_, ret.value_);\n    return ret;\n  }\n\n  constexpr PrimeFieldGpuDebug& NegateInPlace() {\n    BigInt<N> result;\n    SubLimbs<false>(Config::kModulus, value_, result);\n    value_ = result;\n    return *this;\n  }\n\n  // MultiplicativeSemigroup methods\n  constexpr PrimeFieldGpuDebug Mul(const PrimeFieldGpuDebug& other) const {\n    // Forces us to think more carefully about the last carry bit if we use a\n    // modulus with fewer than 2 leading zeroes of slack.\n    static_assert(!(Config::kModulus[N - 1] >> 62));\n    PrimeFieldGpuDebug ret{};\n    MulLimbs(value_, other.value_, ret.value_);\n    return Clamp(ret);\n  }\n\n  constexpr PrimeFieldGpuDebug& MulInPlace(const PrimeFieldGpuDebug& other) {\n    // Forces us to think more carefully about the last carry bit if we use a\n    // modulus with fewer than 2 leading zeroes of slack.\n    static_assert(!(Config::kModulus[N - 1] >> 62));\n    BigInt<N> result;\n    MulLimbs(value_, other.value_, result);\n    value_ = result;\n    *this = Clamp(*this);\n    return *this;\n  }\n\n  // MultiplicativeGroup methods\n  // TODO(chokobole): Share codes with PrimeField and PrimeFieldGpu.\n  constexpr std::optional<PrimeFieldGpuDebug> Inverse() const {\n    PrimeFieldGpuDebug ret{};\n    if (LIKELY(DoInverse(*this, ret))) return ret;\n    LOG_IF_NOT_GPU(ERROR) << \"Inverse of zero attempted\";\n    return std::nullopt;\n  }\n\n  [[nodiscard]] constexpr std::optional<PrimeFieldGpuDebug*> InverseInPlace() {\n    if (LIKELY(DoInverse(*this, *this))) return this;\n    LOG_IF_NOT_GPU(ERROR) << \"Inverse of zero attempted\";\n    return std::nullopt;\n  }\n\n private:\n  template <bool CarryOut>\n  constexpr static uint64_t AddLimbs(const BigInt<N>& xs, const BigInt<N>& ys,\n                                     BigInt<N>& results) {\n    const uint64_t* x = xs.limbs;\n    const uint64_t* y = ys.limbs;\n    uint64_t* r = results.limbs;\n    u64::CarryChain<CarryOut ? N + 1 : N> chain;\n    AddResult<uint64_t> result;\n    for (size_t i = 0; i < N; ++i) {\n      result = chain.Add(x[i], y[i], result.carry);\n      r[i] = result.result;\n    }\n    if constexpr (!CarryOut) return 0;\n    return chain.Add(0, 0, result.carry).result;\n  }\n\n  template <bool CarryOut>\n  constexpr static uint64_t SubLimbs(const BigInt<N>& xs, const BigInt<N>& ys,\n                                     BigInt<N>& results) {\n    const uint64_t* x = xs.limbs;\n    const uint64_t* y = ys.limbs;\n    uint64_t* r = results.limbs;\n    u64::CarryChain<CarryOut ? N + 1 : N> chain;\n    SubResult<uint64_t> result;\n    for (size_t i = 0; i < N; ++i) {\n      result = chain.Sub(x[i], y[i], result.borrow);\n      r[i] = result.result;\n    }\n    if constexpr (!CarryOut) return 0;\n    return chain.Sub(0, 0, result.borrow).result;\n  }\n\n  constexpr static void MulLimbs(const BigInt<N>& xs, const BigInt<N>& ys,\n                                 BigInt<N>& results) {\n    constexpr uint32_t n = N32;\n    const uint32_t* x = reinterpret_cast<const uint32_t*>(xs.limbs);\n    const uint32_t* y = reinterpret_cast<const uint32_t*>(ys.limbs);\n    uint32_t* even = reinterpret_cast<uint32_t*>(results.limbs);\n\n    ALIGNAS(8)\n    uint32_t odd[n + 1] = {\n        0,\n    };\n    size_t i = 0;\n    for (i = 0; i < n; i += 2) {\n      MadNRedc(&even[0], &odd[0], x, y[i], i == 0);\n      MadNRedc(&odd[0], &even[0], x, y[i + 1]);\n    }\n\n    // merge |even| and |odd|\n    AddResult<uint32_t> result;\n    result = internal::u32::AddCc(even[0], odd[1]);\n    even[0] = result.result;\n    for (i = 1; i < n - 1; ++i) {\n      result = internal::u32::AddcCc(even[i], odd[i + 1], result.carry);\n      even[i] = result.result;\n    }\n    result = internal::u32::Addc(even[i], 0, result.carry);\n    even[i] = result.result;\n    // final reduction from [0, 2 * mod) to [0, mod) not done here, instead\n    // performed optionally in MulInPlace.\n  }\n\n  constexpr static void MulN(uint32_t* acc, const uint32_t* a, uint32_t bi,\n                             size_t n = N32) {\n    for (size_t i = 0; i < n; i += 2) {\n      acc[i] = internal::u32::MulLo(a[i], bi);\n      acc[i + 1] = internal::u32::MulHi(a[i], bi);\n    }\n  }\n\n  constexpr static uint32_t CMadN(uint32_t* acc, const uint32_t* a, uint32_t bi,\n                                  size_t n = N32) {\n    AddResult<uint32_t> result;\n    result = internal::u32::MadLoCc(a[0], bi, acc[0]);\n    acc[0] = result.result;\n    result = internal::u32::MadcHiCc(a[0], bi, acc[1], result.carry);\n    acc[1] = result.result;\n    for (size_t i = 2; i < n; i += 2) {\n      result = internal::u32::MadcLoCc(a[i], bi, acc[i], result.carry);\n      acc[i] = result.result;\n      result = internal::u32::MadcHiCc(a[i], bi, acc[i + 1], result.carry);\n      acc[i + 1] = result.result;\n    }\n    return result.carry;\n  }\n\n  constexpr static void MadcNRshift(uint32_t* odd, const uint32_t* a,\n                                    uint32_t bi, uint32_t carry) {\n    constexpr uint32_t n = N32;\n    AddResult<uint32_t> result;\n    result.carry = carry;\n    for (size_t i = 0; i < n - 2; i += 2) {\n      result = internal::u32::MadcLoCc(a[i], bi, odd[i + 2], result.carry);\n      odd[i] = result.result;\n      result = internal::u32::MadcHiCc(a[i], bi, odd[i + 3], result.carry);\n      odd[i + 1] = result.result;\n    }\n    result = internal::u32::MadcLoCc(a[n - 2], bi, 0, result.carry);\n    odd[n - 2] = result.result;\n    result = internal::u32::MadcHi(a[n - 2], bi, 0, result.carry);\n    odd[n - 1] = result.result;\n    CHECK_EQ(result.carry, uint32_t{0});\n  }\n\n  constexpr static void MadNRedc(uint32_t* even, uint32_t* odd,\n                                 const uint32_t* a, uint32_t bi,\n                                 bool first = false) {\n    constexpr uint32_t n = N32;\n    const uint32_t* const modulus =\n        reinterpret_cast<const uint32_t* const>(Config::kModulus.limbs);\n    if (first) {\n      MulN(odd, a + 1, bi);\n      MulN(even, a, bi);\n    } else {\n      AddResult<uint32_t> result;\n      result = internal::u32::AddCc(even[0], odd[1]);\n      even[0] = result.result;\n      MadcNRshift(odd, a + 1, bi, result.carry);\n      uint32_t carry = CMadN(even, a, bi);\n      result = internal::u32::Addc(odd[n - 1], 0, carry);\n      odd[n - 1] = result.result;\n      CHECK_EQ(result.carry, uint32_t{0});\n    }\n    uint32_t mi = even[0] * Config::kInverse32;\n    uint32_t carry = CMadN(odd, modulus + 1, mi);\n    CHECK_EQ(carry, uint32_t{0});\n    carry = CMadN(even, modulus, mi);\n    AddResult<uint32_t> result = internal::u32::Addc(odd[n - 1], 0, carry);\n    odd[n - 1] = result.result;\n    CHECK_EQ(result.carry, uint32_t{0});\n  }\n\n  constexpr static PrimeFieldGpuDebug Clamp(PrimeFieldGpuDebug& xs) {\n    PrimeFieldGpuDebug results;\n    return SubLimbs<true>(xs.value_, Config::kModulus, results.value_)\n               ? xs\n               : results;\n  }\n\n  [[nodiscard]] constexpr static bool DoInverse(const PrimeFieldGpuDebug& a,\n                                                PrimeFieldGpuDebug& b) {\n    if (UNLIKELY(a.IsZero())) {\n      LOG_IF_NOT_GPU(ERROR) << \"Inverse of zero attempted\";\n      return false;\n    }\n\n    BigInt<N> u = a.value_;\n    BigInt<N> v = Config::kModulus;\n    PrimeFieldGpuDebug c;\n    c.value_ = Config::kMontgomeryR2;\n    PrimeFieldGpuDebug d = PrimeFieldGpuDebug::Zero();\n\n    while (!u.IsOne() && !v.IsOne()) {\n      while (u.IsEven()) {\n        u.DivBy2InPlace();\n        if (c.IsOdd()) AddLimbs<false>(c.value_, Config::kModulus, c.value_);\n        c.value_.DivBy2InPlace();\n      }\n\n      while (v.IsEven()) {\n        v.DivBy2InPlace();\n        if (d.IsOdd()) AddLimbs<false>(d.value_, Config::kModulus, d.value_);\n        d.value_.DivBy2InPlace();\n      }\n      if (v < u) {\n        SubLimbs<false>(u, v, u);\n        c -= d;\n      } else {\n        SubLimbs<false>(v, u, v);\n        d -= c;\n      }\n    }\n    if (u.IsOne()) {\n      b = c;\n    } else {\n      b = d;\n    }\n    return true;\n  }\n\n  BigInt<N> value_;\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_PRIME_FIELD_GPU_DEBUG_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/prime_field_gpu_unittest.cc",
    "content": "#include \"gtest/gtest.h\"\n\n#include \"tachyon/device/gpu/gpu_memory.h\"\n#include \"tachyon/math/finite_fields/kernels/prime_field_ops.cu.h\"\n#include \"tachyon/math/finite_fields/prime_field_conversions.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n#include \"tachyon/math/finite_fields/test/gf7_gpu.h\"\n#include \"tachyon/math/test/launch_op_macros.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\nconstexpr size_t kThreadNum = 32;\n\n#define DEFINE_LAUNCH_FIELD_OP(method) \\\n  DEFINE_LAUNCH_BINARY_OP(kThreadNum, method, GF7Gpu, GF7Gpu)\n#define DEFINE_LAUNCH_COMPARISON_OP(method) \\\n  DEFINE_LAUNCH_BINARY_OP(kThreadNum, method, GF7Gpu, bool)\n\nDEFINE_LAUNCH_FIELD_OP(Add)\nDEFINE_LAUNCH_FIELD_OP(Sub)\nDEFINE_LAUNCH_FIELD_OP(Mul)\n\nDEFINE_LAUNCH_COMPARISON_OP(Eq)\nDEFINE_LAUNCH_COMPARISON_OP(Ne)\nDEFINE_LAUNCH_COMPARISON_OP(Lt)\nDEFINE_LAUNCH_COMPARISON_OP(Le)\nDEFINE_LAUNCH_COMPARISON_OP(Gt)\nDEFINE_LAUNCH_COMPARISON_OP(Ge)\n\n#undef DEFINE_LAUNCH_COMPARISON_OP\n#undef DEFINE_LAUNCH_FIELD_OP\n\nusing namespace device;\n\nclass PrimeFieldGpuTest : public FiniteFieldTest<GF7> {\n public:\n  // Runs tests with |N| data.\n  constexpr static size_t N = kThreadNum * 2;\n\n  static void SetUpTestSuite() {\n    FiniteFieldTest<GF7>::SetUpTestSuite();\n\n    GPU_MUST_SUCCEED(gpuDeviceReset(), \"\");\n    xs_ = gpu::GpuMemory<GF7Gpu>::MallocManaged(N);\n    ys_ = gpu::GpuMemory<GF7Gpu>::MallocManaged(N);\n    results_ = gpu::GpuMemory<GF7Gpu>::MallocManaged(N);\n    bool_results_ = gpu::GpuMemory<bool>::MallocManaged(N);\n  }\n\n  static void TearDownTestSuite() {\n    xs_.reset();\n    ys_.reset();\n    results_.reset();\n    bool_results_.reset();\n\n    GPU_MUST_SUCCEED(gpuDeviceReset(), \"\");\n  }\n\n  void SetUp() override {\n    CHECK(xs_.Memset());\n    CHECK(ys_.Memset());\n    CHECK(results_.Memset());\n    CHECK(bool_results_.Memset());\n  }\n\n protected:\n  static gpu::GpuMemory<GF7Gpu> xs_;\n  static gpu::GpuMemory<GF7Gpu> ys_;\n  static gpu::GpuMemory<GF7Gpu> results_;\n  static gpu::GpuMemory<bool> bool_results_;\n};\n\ngpu::GpuMemory<GF7Gpu> PrimeFieldGpuTest::xs_;\ngpu::GpuMemory<GF7Gpu> PrimeFieldGpuTest::ys_;\ngpu::GpuMemory<GF7Gpu> PrimeFieldGpuTest::results_;\ngpu::GpuMemory<bool> PrimeFieldGpuTest::bool_results_;\n\n}  // namespace\n\n#define RUN_OPERATION_TESTS(method, results)                              \\\n  for (size_t i = 0; i < std::size(tests); ++i) {                         \\\n    const auto& test = tests[i];                                          \\\n    xs_[i] = GF7Gpu::FromBigInt(test.x.ToBigInt());                       \\\n    ys_[i] = GF7Gpu::FromBigInt(test.y.ToBigInt());                       \\\n  }                                                                       \\\n  GPU_MUST_SUCCEED(Launch##method(xs_.data(), ys_.data(), results.data(), \\\n                                  std::size(tests)),                      \\\n                   \"Failed \" #method \"()\");                               \\\n  for (size_t i = 0; i < std::size(tests); ++i)\n\n#define RUN_FIELD_OPERATION_TESTS(method) \\\n  RUN_OPERATION_TESTS(method, results_)   \\\n  ASSERT_EQ(ConvertPrimeField<GF7>(results_[i]), tests[i].result)\n\n#define RUN_COMPARISON_OPERATION_TESTS(method) \\\n  RUN_OPERATION_TESTS(method, bool_results_)   \\\n  ASSERT_EQ(bool_results_[i], tests[i].result)\n\nTEST_F(PrimeFieldGpuTest, FromString) {\n  EXPECT_EQ(*GF7Gpu::FromDecString(\"3\"), GF7Gpu(3));\n  EXPECT_FALSE(GF7Gpu::FromDecString(\"x\").has_value());\n  EXPECT_EQ(*GF7Gpu::FromHexString(\"0x3\"), GF7Gpu(3));\n  EXPECT_FALSE(GF7Gpu::FromHexString(\"x\").has_value());\n}\n\nTEST_F(PrimeFieldGpuTest, ToString) {\n  GF7Gpu f(3);\n\n  EXPECT_EQ(f.ToString(), \"3\");\n  EXPECT_EQ(f.ToHexString(), \"0x3\");\n}\n\nTEST_F(PrimeFieldGpuTest, Zero) {\n  EXPECT_TRUE(GF7Gpu::Zero().IsZero());\n  EXPECT_FALSE(GF7Gpu::One().IsZero());\n  EXPECT_FALSE(GF7Gpu::MinusOne().IsZero());\n}\n\nTEST_F(PrimeFieldGpuTest, One) {\n  EXPECT_TRUE(GF7Gpu::One().IsOne());\n  EXPECT_FALSE(GF7Gpu::Zero().IsOne());\n  EXPECT_FALSE(GF7Gpu::MinusOne().IsOne());\n}\n\nTEST_F(PrimeFieldGpuTest, MinusOne) {\n  EXPECT_TRUE(GF7Gpu::MinusOne().IsMinusOne());\n  EXPECT_FALSE(GF7Gpu::Zero().IsMinusOne());\n  EXPECT_FALSE(GF7Gpu::One().IsMinusOne());\n}\n\nTEST_F(PrimeFieldGpuTest, Add) {\n  struct {\n    GF7 x;\n    GF7 y;\n    GF7 result;\n  } tests[] = {\n      {GF7(3), GF7(2), GF7(5)},\n      {GF7(2), GF7(3), GF7(5)},\n      {GF7(5), GF7(3), GF7(1)},\n      {GF7(3), GF7(5), GF7(1)},\n  };\n\n  RUN_FIELD_OPERATION_TESTS(Add);\n}\n\nTEST_F(PrimeFieldGpuTest, Sub) {\n  struct {\n    GF7 x;\n    GF7 y;\n    GF7 result;\n  } tests[] = {\n      {GF7(3), GF7(2), GF7(1)},\n      {GF7(2), GF7(3), GF7(6)},\n      {GF7(5), GF7(3), GF7(2)},\n      {GF7(3), GF7(5), GF7(5)},\n  };\n\n  RUN_FIELD_OPERATION_TESTS(Sub);\n}\n\nTEST_F(PrimeFieldGpuTest, Mul) {\n  struct {\n    GF7 x;\n    GF7 y;\n    GF7 result;\n  } tests[] = {\n      {GF7(3), GF7(2), GF7(6)},\n      {GF7(2), GF7(3), GF7(6)},\n      {GF7(5), GF7(3), GF7(1)},\n      {GF7(3), GF7(5), GF7(1)},\n  };\n\n  RUN_FIELD_OPERATION_TESTS(Mul);\n}\n\nTEST_F(PrimeFieldGpuTest, Eq) {\n  struct {\n    GF7 x;\n    GF7 y;\n    bool result;\n  } tests[] = {\n      {GF7(3), GF7(2), false},\n      {GF7(2), GF7(3), false},\n      {GF7(3), GF7(3), true},\n  };\n\n  for (size_t i = 0; i < std::size(tests); ++i) {\n    const auto& test = tests[i];\n    xs_[i] = GF7Gpu::FromBigInt(test.x.ToBigInt());\n    ys_[i] = GF7Gpu::FromBigInt(test.y.ToBigInt());\n    ASSERT_EQ(xs_[i] == ys_[i], test.result);\n  }\n\n  GPU_MUST_SUCCEED(\n      LaunchEq(xs_.data(), ys_.data(), bool_results_.data(), std::size(tests)),\n      \"Failed Eq()\");\n  for (size_t i = 0; i < std::size(tests); ++i) {\n    ASSERT_EQ(bool_results_[i], tests[i].result);\n  }\n}\n\nTEST_F(PrimeFieldGpuTest, Ne) {\n  struct {\n    GF7 x;\n    GF7 y;\n    bool result;\n  } tests[] = {\n      {GF7(3), GF7(2), true},\n      {GF7(2), GF7(3), true},\n      {GF7(3), GF7(3), false},\n  };\n\n  for (size_t i = 0; i < std::size(tests); ++i) {\n    const auto& test = tests[i];\n    xs_[i] = GF7Gpu::FromBigInt(test.x.ToBigInt());\n    ys_[i] = GF7Gpu::FromBigInt(test.y.ToBigInt());\n    ASSERT_EQ(xs_[i] != ys_[i], test.result);\n  }\n\n  GPU_MUST_SUCCEED(\n      LaunchNe(xs_.data(), ys_.data(), bool_results_.data(), std::size(tests)),\n      \"Failed Ne()\");\n  for (size_t i = 0; i < std::size(tests); ++i) {\n    ASSERT_EQ(bool_results_[i], tests[i].result);\n  }\n}\n\nTEST_F(PrimeFieldGpuTest, Lt) {\n  struct {\n    GF7 x;\n    GF7 y;\n    bool result;\n  } tests[] = {\n      {GF7(3), GF7(2), false},\n      {GF7(2), GF7(3), true},\n      {GF7(3), GF7(3), false},\n  };\n\n  RUN_COMPARISON_OPERATION_TESTS(Lt);\n}\n\nTEST_F(PrimeFieldGpuTest, Le) {\n  struct {\n    GF7 x;\n    GF7 y;\n    bool result;\n  } tests[] = {\n      {GF7(3), GF7(2), false},\n      {GF7(2), GF7(3), true},\n      {GF7(3), GF7(3), true},\n  };\n\n  RUN_COMPARISON_OPERATION_TESTS(Le);\n}\n\nTEST_F(PrimeFieldGpuTest, Gt) {\n  struct {\n    GF7 x;\n    GF7 y;\n    bool result;\n  } tests[] = {\n      {GF7(3), GF7(2), true},\n      {GF7(2), GF7(3), false},\n      {GF7(3), GF7(3), false},\n  };\n\n  RUN_COMPARISON_OPERATION_TESTS(Gt);\n}\n\nTEST_F(PrimeFieldGpuTest, Ge) {\n  struct {\n    GF7 x;\n    GF7 y;\n    bool result;\n  } tests[] = {\n      {GF7(3), GF7(2), true},\n      {GF7(2), GF7(3), false},\n      {GF7(3), GF7(3), true},\n  };\n\n  RUN_COMPARISON_OPERATION_TESTS(Ge);\n}\n\n#undef RUN_OPERATION_TESTS\n#undef RUN_COMPARISON_OPERATION_TESTS\n#undef RUN_FIELD_OPERATION_TESTS\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/finite_fields/prime_field_ops_internal.h",
    "content": "// Copyright (c) 2022 Matter Labs\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.era-bellman-cuda and the\n// LICENCE-APACHE.era-bellman-cuda file.\n\n#ifndef TACHYON_MATH_FINITE_FIELDS_PRIME_FIELD_OPS_INTERNAL_H_\n#define TACHYON_MATH_FINITE_FIELDS_PRIME_FIELD_OPS_INTERNAL_H_\n\n#include <stdint.h>\n\n#include \"tachyon/base/compiler_specific.h\"\n#include \"tachyon/math/base/arithmetics.h\"\n\nnamespace tachyon::math::internal {\nnamespace u32 {\n\nALWAYS_INLINE AddResult<uint32_t> Add(uint32_t x, uint32_t y) {\n  AddResult<uint32_t> result = AddWithCarry(x, y);\n  result.carry = 0;\n  return result;\n}\n\nALWAYS_INLINE AddResult<uint32_t> AddCc(uint32_t x, uint32_t y) {\n  return AddWithCarry(x, y);\n}\n\nALWAYS_INLINE AddResult<uint32_t> Addc(uint32_t x, uint32_t y, uint32_t carry) {\n  AddResult<uint32_t> result = AddWithCarry(x, y, carry);\n  result.carry = 0;\n  return result;\n}\n\nALWAYS_INLINE AddResult<uint32_t> AddcCc(uint32_t x, uint32_t y,\n                                         uint32_t carry) {\n  return AddWithCarry(x, y, carry);\n}\n\nALWAYS_INLINE SubResult<uint32_t> Sub(uint32_t x, uint32_t y) {\n  SubResult<uint32_t> result = SubWithBorrow(x, y);\n  result.borrow = 0;\n  return result;\n}\n\nALWAYS_INLINE SubResult<uint32_t> SubCc(uint32_t x, uint32_t y) {\n  return SubWithBorrow(x, y);\n}\n\nALWAYS_INLINE SubResult<uint32_t> Subc(uint32_t x, uint32_t y,\n                                       uint32_t borrow) {\n  SubResult<uint32_t> result = SubWithBorrow(x, y, borrow);\n  result.borrow = 0;\n  return result;\n}\n\nALWAYS_INLINE SubResult<uint32_t> SubcCc(uint32_t x, uint32_t y,\n                                         uint32_t borrow) {\n  return SubWithBorrow(x, y, borrow);\n}\n\nALWAYS_INLINE constexpr uint32_t MulLo(uint32_t x, uint32_t y) {\n  return MulAddWithCarry(0, x, y).lo;\n}\n\nALWAYS_INLINE constexpr uint32_t MulHi(uint32_t x, uint32_t y) {\n  return MulAddWithCarry(0, x, y).hi;\n}\n\nALWAYS_INLINE AddResult<uint32_t> MadLo(uint32_t x, uint32_t y, uint32_t z) {\n  return Add(MulLo(x, y), z);\n}\n\nALWAYS_INLINE AddResult<uint32_t> MadHi(uint32_t x, uint32_t y, uint32_t z) {\n  return Add(MulHi(x, y), z);\n}\n\nALWAYS_INLINE AddResult<uint32_t> MadLoCc(uint32_t x, uint32_t y, uint32_t z) {\n  return AddCc(MulLo(x, y), z);\n}\n\nALWAYS_INLINE AddResult<uint32_t> MadHiCc(uint32_t x, uint32_t y, uint32_t z) {\n  return AddCc(MulHi(x, y), z);\n}\n\nALWAYS_INLINE AddResult<uint32_t> MadcLo(uint32_t x, uint32_t y, uint32_t z,\n                                         uint32_t carry) {\n  return Addc(MulLo(x, y), z, carry);\n}\n\nALWAYS_INLINE AddResult<uint32_t> MadcHi(uint32_t x, uint32_t y, uint32_t z,\n                                         uint32_t carry) {\n  return Addc(MulHi(x, y), z, carry);\n}\n\nALWAYS_INLINE AddResult<uint32_t> MadcLoCc(uint32_t x, uint32_t y, uint32_t z,\n                                           uint32_t carry) {\n  return AddcCc(MulLo(x, y), z, carry);\n}\n\nALWAYS_INLINE AddResult<uint32_t> MadcHiCc(uint32_t x, uint32_t y, uint32_t z,\n                                           uint32_t carry) {\n  return AddcCc(MulHi(x, y), z, carry);\n}\n\n}  // namespace u32\n\nnamespace u64 {\n\nALWAYS_INLINE AddResult<uint64_t> Add(uint64_t x, uint64_t y) {\n  AddResult<uint64_t> result = AddWithCarry(x, y);\n  result.carry = 0;\n  return result;\n}\n\nALWAYS_INLINE AddResult<uint64_t> AddCc(uint64_t x, uint64_t y) {\n  return AddWithCarry(x, y);\n}\n\nALWAYS_INLINE AddResult<uint64_t> Addc(uint64_t x, uint64_t y, uint64_t carry) {\n  AddResult<uint64_t> result = AddWithCarry(x, y, carry);\n  result.carry = 0;\n  return result;\n}\n\nALWAYS_INLINE AddResult<uint64_t> AddcCc(uint64_t x, uint64_t y,\n                                         uint64_t carry) {\n  return AddWithCarry(x, y, carry);\n}\n\nALWAYS_INLINE SubResult<uint64_t> Sub(uint64_t x, uint64_t y) {\n  SubResult<uint64_t> result = SubWithBorrow(x, y);\n  result.borrow = 0;\n  return result;\n}\n\nALWAYS_INLINE SubResult<uint64_t> SubCc(uint64_t x, uint64_t y) {\n  return SubWithBorrow(x, y);\n}\n\nALWAYS_INLINE SubResult<uint64_t> Subc(uint64_t x, uint64_t y,\n                                       uint64_t borrow) {\n  SubResult<uint64_t> result = SubWithBorrow(x, y, borrow);\n  result.borrow = 0;\n  return result;\n}\n\nALWAYS_INLINE SubResult<uint64_t> SubcCc(uint64_t x, uint64_t y,\n                                         uint64_t borrow) {\n  return SubWithBorrow(x, y, borrow);\n}\n\nALWAYS_INLINE uint64_t MulLo(uint64_t x, uint64_t y) {\n  return MulAddWithCarry(0, x, y).lo;\n}\n\nALWAYS_INLINE uint64_t MulHi(uint64_t x, uint64_t y) {\n  return MulAddWithCarry(0, x, y).hi;\n}\n\nALWAYS_INLINE AddResult<uint64_t> MadLo(uint64_t x, uint64_t y, uint64_t z) {\n  return Add(MulLo(x, y), z);\n}\n\nALWAYS_INLINE AddResult<uint64_t> MadHi(uint64_t x, uint64_t y, uint64_t z) {\n  return Add(MulHi(x, y), z);\n}\n\nALWAYS_INLINE AddResult<uint64_t> MadLoCc(uint64_t x, uint64_t y, uint64_t z) {\n  return AddCc(MulLo(x, y), z);\n}\n\nALWAYS_INLINE AddResult<uint64_t> MadHiCc(uint64_t x, uint64_t y, uint64_t z) {\n  return AddCc(MulHi(x, y), z);\n}\n\nALWAYS_INLINE AddResult<uint64_t> MadcLo(uint64_t x, uint64_t y, uint64_t z,\n                                         uint64_t carry) {\n  return Addc(MulLo(x, y), z, carry);\n}\n\nALWAYS_INLINE AddResult<uint64_t> MadcHi(uint64_t x, uint64_t y, uint64_t z,\n                                         uint64_t carry) {\n  return Addc(MulHi(x, y), z, carry);\n}\n\nALWAYS_INLINE AddResult<uint64_t> MadcLoCc(uint64_t x, uint64_t y, uint64_t z,\n                                           uint64_t carry) {\n  return AddcCc(MulLo(x, y), z, carry);\n}\n\nALWAYS_INLINE AddResult<uint64_t> MadcHiCc(uint64_t x, uint64_t y, uint64_t z,\n                                           uint64_t carry) {\n  return AddcCc(MulHi(x, y), z, carry);\n}\n\n}  // namespace u64\n}  // namespace tachyon::math::internal\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_PRIME_FIELD_OPS_INTERNAL_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/prime_field_unittest.cc",
    "content": "#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/buffer/vector_buffer.h\"\n#include \"tachyon/base/optional.h\"\n// #include \"tachyon/math/finite_fields/prime_field_gpu_debug.h\"\n#include \"tachyon/base/auto_reset.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\ntemplate <typename PrimeField>\nclass PrimeFieldTest : public FiniteFieldTest<PrimeField> {};\n\n}  // namespace\n\n// TODO(chokobole): Enable test for |PrimeFieldGpuDebug<GF7Config>|.\n// Since |PrimeFieldGpuDebug| only supports the montgomery version and |GF7|\n// disables montgomery, this makes the test fail.\nusing PrimeFieldTypes = testing::Types<GF7 /*, PrimeFieldGpuDebug<GF7Config>*/>;\nTYPED_TEST_SUITE(PrimeFieldTest, PrimeFieldTypes);\n\nTYPED_TEST(PrimeFieldTest, FromString) {\n  using F = TypeParam;\n\n  EXPECT_EQ(*F::FromDecString(\"3\"), F(3));\n  EXPECT_FALSE(F::FromDecString(\"a\").has_value());\n  EXPECT_EQ(*F::FromHexString(\"0x3\"), F(3));\n  EXPECT_FALSE(F::FromHexString(\"a\").has_value());\n}\n\nTYPED_TEST(PrimeFieldTest, ToString) {\n  using F = TypeParam;\n\n  F f(3);\n\n  EXPECT_EQ(f.ToString(), \"3\");\n  EXPECT_EQ(f.ToHexString(), \"0x3\");\n}\n\nTYPED_TEST(PrimeFieldTest, Zero) {\n  using F = TypeParam;\n\n  EXPECT_TRUE(F::Zero().IsZero());\n  EXPECT_FALSE(F::One().IsZero());\n  EXPECT_FALSE(F::MinusOne().IsZero());\n}\n\nTYPED_TEST(PrimeFieldTest, One) {\n  using F = TypeParam;\n\n  EXPECT_TRUE(F::One().IsOne());\n  EXPECT_FALSE(F::Zero().IsOne());\n  EXPECT_FALSE(F::MinusOne().IsOne());\n  EXPECT_EQ(F::Config::kOne, F(1).value());\n}\n\nTYPED_TEST(PrimeFieldTest, MinusOne) {\n  using F = TypeParam;\n\n  EXPECT_TRUE(F::MinusOne().IsMinusOne());\n  EXPECT_FALSE(F::Zero().IsMinusOne());\n  EXPECT_FALSE(F::One().IsMinusOne());\n}\n\nTYPED_TEST(PrimeFieldTest, TwoInv) {\n  using F = TypeParam;\n\n  EXPECT_TRUE((F::TwoInv() * F(2)).IsOne());\n  EXPECT_FALSE((F::TwoInv() * F(1)).IsOne());\n}\n\nTYPED_TEST(PrimeFieldTest, BigIntConversion) {\n  using F = TypeParam;\n\n  F r = F::Random();\n  EXPECT_EQ(F::FromBigInt(r.ToBigInt()), r);\n}\n\nTYPED_TEST(PrimeFieldTest, EqualityOperators) {\n  using F = TypeParam;\n\n  F f(3);\n  F f2(4);\n  EXPECT_EQ(f, f);\n  EXPECT_NE(f, f2);\n}\n\nTYPED_TEST(PrimeFieldTest, ComparisonOperator) {\n  using F = TypeParam;\n\n  F f(3);\n  F f2(4);\n  EXPECT_LT(f, f2);\n  EXPECT_LE(f, f2);\n  EXPECT_GT(f2, f);\n  EXPECT_GE(f2, f);\n}\n\nTYPED_TEST(PrimeFieldTest, AdditiveOperators) {\n  using F = TypeParam;\n\n  struct {\n    F a;\n    F b;\n    F sum;\n    F amb;\n    F bma;\n  } tests[] = {\n      {F(3), F(2), F(5), F(1), F(6)},\n      {F(5), F(3), F(1), F(2), F(5)},\n  };\n\n  for (const auto& test : tests) {\n    EXPECT_EQ(test.a + test.b, test.sum);\n    EXPECT_EQ(test.b + test.a, test.sum);\n    EXPECT_EQ(test.a - test.b, test.amb);\n    EXPECT_EQ(test.b - test.a, test.bma);\n\n    F tmp = test.a;\n    tmp += test.b;\n    EXPECT_EQ(tmp, test.sum);\n    tmp -= test.b;\n    EXPECT_EQ(tmp, test.a);\n  }\n}\n\nTYPED_TEST(PrimeFieldTest, AdditiveGroupOperators) {\n  using F = TypeParam;\n\n  F f(3);\n  EXPECT_EQ(-f, F(4));\n  f.NegateInPlace();\n  EXPECT_EQ(f, F(4));\n\n  f = F(3);\n  EXPECT_EQ(f.Double(), F(6));\n  f.DoubleInPlace();\n  EXPECT_EQ(f, F(6));\n}\n\nTYPED_TEST(PrimeFieldTest, MultiplicativeOperators) {\n  using F = TypeParam;\n\n  struct {\n    F a;\n    F b;\n    F mul;\n    F adb;\n    F bda;\n  } tests[] = {\n      {F(3), F(2), F(6), F(5), F(3)},\n      {F(5), F(3), F(1), F(4), F(2)},\n  };\n\n  for (const auto& test : tests) {\n    EXPECT_EQ(test.a * test.b, test.mul);\n    EXPECT_EQ(test.b * test.a, test.mul);\n    EXPECT_EQ(test.a / test.b, test.adb);\n    EXPECT_EQ(test.b / test.a, test.bda);\n\n    F tmp = test.a;\n    tmp *= test.b;\n    EXPECT_EQ(tmp, test.mul);\n    ASSERT_TRUE(tmp /= test.b);\n    EXPECT_EQ(tmp, test.a);\n  }\n}\n\nTYPED_TEST(PrimeFieldTest, MultiplicativeGroupOperators) {\n  using F = TypeParam;\n\n  for (int i = 0; i < 7; ++i) {\n    F f(i);\n    std::optional<F> f_inv = f.Inverse();\n    if (f.IsZero()) {\n      ASSERT_FALSE(f_inv);\n      ASSERT_FALSE(f.InverseInPlace());\n    } else {\n      EXPECT_EQ(f * *f_inv, F::One());\n      F f_tmp = f;\n      EXPECT_EQ(**f.InverseInPlace() * f_tmp, F::One());\n    }\n  }\n\n  F f(3);\n  EXPECT_EQ(f.Square(), F(2));\n  f.SquareInPlace();\n  EXPECT_EQ(f, F(2));\n\n  f = F(3);\n  EXPECT_EQ(f.Pow(5), F(5));\n}\n\nTYPED_TEST(PrimeFieldTest, SumOfProductsSerial) {\n  using F = TypeParam;\n\n  const F a[] = {F(3), F(2)};\n  const F b[] = {F(2), F(5)};\n  EXPECT_EQ(F::SumOfProductsSerial(a, b), F(2));\n}\n\nTYPED_TEST(PrimeFieldTest, Random) {\n  using F = TypeParam;\n\n  bool success = false;\n  F r = F::Random();\n  for (size_t i = 0; i < 100; ++i) {\n    if (r != F::Random()) {\n      success = true;\n      break;\n    }\n  }\n  EXPECT_TRUE(success);\n}\n\nTYPED_TEST(PrimeFieldTest, Copyable) {\n  using F = TypeParam;\n\n  const F expected = F::Random();\n\n  for (size_t i = 0; i < 2; ++i) {\n    bool s_is_in_montgomery = i == 0;\n    SCOPED_TRACE(\n        absl::Substitute(\"s_is_in_montgomery: $0\", s_is_in_montgomery));\n    base::AutoReset<bool> auto_reset(&base::Copyable<F>::s_is_in_montgomery,\n                                     s_is_in_montgomery);\n    base::Uint8VectorBuffer write_buf;\n    ASSERT_TRUE(write_buf.Grow(base::EstimateSize(expected)));\n    ASSERT_TRUE(write_buf.Write(expected));\n    ASSERT_TRUE(write_buf.Done());\n\n    write_buf.set_buffer_offset(0);\n\n    F value;\n    ASSERT_TRUE(write_buf.Read(&value));\n    EXPECT_EQ(expected, value);\n  }\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/finite_fields/prime_field_util.cc",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#include \"tachyon/math/finite_fields/prime_field_util.h\"\n\nnamespace tachyon::math {\n\n// The integer s such that |n| = |k|ˢ * t for some odd integer t.\nuint32_t ComputeAdicity(uint32_t k, mpz_class n) {\n  uint32_t adicity = 0;\n  while (n > 1) {\n    if (n % k == 0) {\n      ++adicity;\n      n /= k;\n    } else {\n      break;\n    }\n  }\n  return adicity;\n}\n\n// The integer t such that |n| = |k|ˢ * t for some odd integer t.\nmpz_class ComputeTrace(size_t k, mpz_class n) {\n  mpz_class trace = 0;\n  while (n > 1) {\n    if (n % k == 0) {\n      n /= k;\n    } else {\n      trace = n;\n      break;\n    }\n  }\n  return trace;\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/finite_fields/prime_field_util.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_FINITE_FIELDS_PRIME_FIELD_UTIL_H_\n#define TACHYON_MATH_FINITE_FIELDS_PRIME_FIELD_UTIL_H_\n\n#include <stdint.h>\n\n#include \"third_party/gmp/include/gmpxx.h\"\n\n#include \"tachyon/export.h\"\n\nnamespace tachyon::math {\n\nTACHYON_EXPORT uint32_t ComputeAdicity(uint32_t k, mpz_class n);\n\nTACHYON_EXPORT mpz_class ComputeTrace(size_t k, mpz_class n);\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_PRIME_FIELD_UTIL_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/quadratic_extension_field.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_FINITE_FIELDS_QUADRATIC_EXTENSION_FIELD_H_\n#define TACHYON_MATH_FINITE_FIELDS_QUADRATIC_EXTENSION_FIELD_H_\n\n#include <array>\n#include <optional>\n#include <string>\n#include <utility>\n\n#include \"absl/strings/substitute.h\"\n#include \"absl/types/span.h\"\n\n#include \"tachyon/base/buffer/copyable.h\"\n#include \"tachyon/base/json/json.h\"\n#include \"tachyon/math/finite_fields/cyclotomic_multiplicative_subgroup.h\"\n#include \"tachyon/math/finite_fields/extension_field_base.h\"\n\nnamespace tachyon {\nnamespace math {\n\ntemplate <typename Derived>\nclass QuadraticExtensionField\n    : public CyclotomicMultiplicativeSubgroup<Derived>,\n      public ExtensionFieldBase<Derived> {\n public:\n  using Config = typename FiniteField<Derived>::Config;\n  using BaseField = typename Config::BaseField;\n  using BasePrimeField = typename Config::BasePrimeField;\n  using ConstIterator = typename ExtensionFieldBase<Derived>::ConstIterator;\n\n  constexpr QuadraticExtensionField() = default;\n  // NOTE(chokobole): This is needed by Eigen matrix.\n  template <typename T, std::enable_if_t<std::is_integral_v<T>>* = nullptr>\n  constexpr explicit QuadraticExtensionField(T value) : c0_(value) {}\n  constexpr explicit QuadraticExtensionField(const BaseField& c0)\n      : c0_(c0), c1_(BaseField::Zero()) {}\n  constexpr explicit QuadraticExtensionField(BaseField&& c0)\n      : c0_(std::move(c0)), c1_(BaseField::Zero()) {}\n  constexpr QuadraticExtensionField(const BaseField& c0, const BaseField& c1)\n      : c0_(c0), c1_(c1) {}\n  constexpr QuadraticExtensionField(BaseField&& c0, BaseField&& c1)\n      : c0_(std::move(c0)), c1_(std::move(c1)) {}\n\n  constexpr static Derived Zero() {\n    return {BaseField::Zero(), BaseField::Zero()};\n  }\n\n  constexpr static Derived One() {\n    return {BaseField::One(), BaseField::Zero()};\n  }\n\n  constexpr static Derived MinusOne() {\n    return {BaseField::MinusOne(), BaseField::Zero()};\n  }\n\n  constexpr static Derived TwoInv() {\n    return {BaseField::TwoInv(), BaseField::Zero()};\n  }\n\n  static Derived Random() { return {BaseField::Random(), BaseField::Random()}; }\n\n  // TODO(chokobole): Should be generalized for packed extension field.\n  static Derived FromBasePrimeFields(\n      absl::Span<const BasePrimeField> prime_fields) {\n    CHECK_EQ(prime_fields.size(), ExtensionDegree());\n    constexpr size_t kBaseFieldDegree = BaseField::ExtensionDegree();\n    if constexpr (kBaseFieldDegree == 1) {\n      return Derived(prime_fields[0], prime_fields[1]);\n    } else {\n      BaseField c0 = BaseField::FromBasePrimeFields(\n          prime_fields.subspan(0, kBaseFieldDegree));\n      BaseField c1 = BaseField::FromBasePrimeFields(\n          prime_fields.subspan(kBaseFieldDegree));\n      return Derived(std::move(c0), std::move(c1));\n    }\n  }\n\n  constexpr std::array<BaseField, 2> ToBaseFields() const { return {c0_, c1_}; }\n\n  ConstIterator end() const { return {static_cast<const Derived&>(*this), 2}; }\n\n  constexpr bool IsZero() const { return c0_.IsZero() && c1_.IsZero(); }\n\n  constexpr bool IsOne() const { return c0_.IsOne() && c1_.IsZero(); }\n\n  constexpr bool IsMinusOne() const { return c0_.IsMinusOne() && c1_.IsZero(); }\n\n  constexpr static uint32_t ExtensionDegree() {\n    return 2 * BaseField::ExtensionDegree();\n  }\n\n  constexpr Derived Conjugate() const {\n    return {\n        c0_,\n        -c1_,\n    };\n  }\n\n  constexpr Derived& ConjugateInPlace() {\n    c1_.NegateInPlace();\n    return *static_cast<Derived*>(this);\n  }\n\n  // Norm of QuadraticExtensionField over |BaseField|:\n  // |a.Norm() = a * a.Conjugate()|.\n  // This simplifies to: |a.Norm() = a.c0² - Config::kNonResidue * a.c1²|.\n  // This is alternatively expressed as |a.Norm() = aᵖ⁺¹|.\n  constexpr BaseField Norm() const {\n    return c0_.Square() - Config::MulByNonResidue(c1_.Square());\n  }\n\n  constexpr Derived& FrobeniusMapInPlace(uint32_t exponent) {\n    c0_.FrobeniusMapInPlace(exponent);\n    c1_.FrobeniusMapInPlace(exponent);\n    c1_ *=\n        Config::kFrobeniusCoeffs[exponent % Config::kDegreeOverBasePrimeField];\n    return *static_cast<Derived*>(this);\n  }\n\n  std::string ToString() const {\n    return absl::Substitute(\"($0, $1)\", c0_.ToString(), c1_.ToString());\n  }\n\n  std::string ToHexString(bool pad_zero = false) const {\n    return absl::Substitute(\"($0, $1)\", c0_.ToHexString(pad_zero),\n                            c1_.ToHexString(pad_zero));\n  }\n\n  constexpr const BaseField& c0() const { return c0_; }\n  constexpr const BaseField& c1() const { return c1_; }\n\n  constexpr const BaseField& operator[](size_t index) const {\n    switch (index) {\n      case 0:\n        return c0_;\n      case 1:\n        return c1_;\n    }\n    NOTREACHED();\n    return c0_;\n  }\n\n  constexpr BaseField& operator[](size_t index) {\n    return const_cast<BaseField&>(std::as_const(*this).operator[](index));\n  }\n\n  constexpr bool operator==(const Derived& other) const {\n    return c0_ == other.c0_ && c1_ == other.c1_;\n  }\n\n  constexpr bool operator!=(const Derived& other) const {\n    return !operator==(other);\n  }\n\n  constexpr bool operator<(const Derived& other) const {\n    if (c1_ == other.c1_) return c0_ < other.c0_;\n    return c1_ < other.c1_;\n  }\n\n  constexpr bool operator>(const Derived& other) const {\n    if (c1_ == other.c1_) return c0_ > other.c0_;\n    return c1_ > other.c1_;\n  }\n\n  constexpr bool operator<=(const Derived& other) const {\n    return !operator>(other);\n  }\n\n  constexpr bool operator>=(const Derived& other) const {\n    return !operator<(other);\n  }\n\n  // AdditiveSemigroup methods\n  constexpr Derived Add(const Derived& other) const {\n    return {\n        c0_ + other.c0_,\n        c1_ + other.c1_,\n    };\n  }\n\n  constexpr Derived& AddInPlace(const Derived& other) {\n    c0_ += other.c0_;\n    c1_ += other.c1_;\n    return *static_cast<Derived*>(this);\n  }\n\n  constexpr Derived DoubleImpl() const {\n    return {\n        c0_.Double(),\n        c1_.Double(),\n    };\n  }\n\n  constexpr Derived& DoubleImplInPlace() {\n    c0_.DoubleInPlace();\n    c1_.DoubleInPlace();\n    return *static_cast<Derived*>(this);\n  }\n\n  // AdditiveGroup methods\n  constexpr Derived Sub(const Derived& other) const {\n    return {\n        c0_ - other.c0_,\n        c1_ - other.c1_,\n    };\n  }\n\n  constexpr Derived& SubInPlace(const Derived& other) {\n    c0_ -= other.c0_;\n    c1_ -= other.c1_;\n    return *static_cast<Derived*>(this);\n  }\n\n  constexpr Derived Negate() const {\n    return {\n        -c0_,\n        -c1_,\n    };\n  }\n\n  constexpr Derived& NegateInPlace() {\n    c0_.NegateInPlace();\n    c1_.NegateInPlace();\n    return *static_cast<Derived*>(this);\n  }\n\n  // MultiplicativeSemigroup methods\n  constexpr Derived Mul(const Derived& other) const {\n    Derived ret{};\n    DoMul(*static_cast<const Derived*>(this), other, ret);\n    return ret;\n  }\n\n  constexpr Derived& MulInPlace(const Derived& other) {\n    DoMul(*static_cast<const Derived*>(this), other,\n          *static_cast<Derived*>(this));\n    return *static_cast<Derived*>(this);\n  }\n\n  constexpr Derived Mul(const BaseField& element) const {\n    return {\n        c0_ * element,\n        c1_ * element,\n    };\n  }\n\n  constexpr Derived& MulInPlace(const BaseField& element) {\n    c0_ *= element;\n    c1_ *= element;\n    return *static_cast<Derived*>(this);\n  }\n\n  constexpr Derived SquareImpl() const {\n    Derived ret{};\n    DoSquareImpl(*static_cast<const Derived*>(this), ret);\n    return ret;\n  }\n\n  constexpr Derived& SquareImplInPlace() {\n    DoSquareImpl(*static_cast<const Derived*>(this),\n                 *static_cast<Derived*>(this));\n    return *static_cast<Derived*>(this);\n  }\n\n  // MultiplicativeGroup methods\n  constexpr std::optional<Derived> Inverse() const {\n    Derived ret{};\n    if (LIKELY(DoInverse(*static_cast<const Derived*>(this), ret))) {\n      return ret;\n    }\n    LOG_IF_NOT_GPU(ERROR) << \"Inverse of zero attempted\";\n    return std::nullopt;\n  }\n\n  [[nodiscard]] constexpr std::optional<Derived*> InverseInPlace() {\n    if (LIKELY(DoInverse(*static_cast<const Derived*>(this),\n                         *static_cast<Derived*>(this)))) {\n      return static_cast<Derived*>(this);\n    }\n    LOG_IF_NOT_GPU(ERROR) << \"Inverse of zero attempted\";\n    return std::nullopt;\n  }\n\n  // CyclotomicMultiplicativeSubgroup methods\n  constexpr std::optional<Derived> FastCyclotomicInverse() const {\n    // As the multiplicative subgroup is of order p² - 1, the\n    // only non-trivial cyclotomic subgroup is of order p + 1\n    // Therefore, for any element in the cyclotomic subgroup, we have that\n    // |xᵖ⁺¹ = 1|. Recall that |xᵖ⁺¹| in a quadratic extension\n    // field is equal to the norm in the base field, so we have that\n    // |x * x.Conjugate() = 1|. By uniqueness of inverses, for this subgroup,\n    // |x.Inverse() = x.Conjugate()|.\n    if (UNLIKELY(IsZero())) {\n      LOG_IF_NOT_GPU(ERROR) << \"Inverse of zero attempted\";\n      return std::nullopt;\n    }\n    return Conjugate();\n  }\n\n  [[nodiscard]] constexpr std::optional<Derived*>\n  FastCyclotomicInverseInPlace() {\n    if (UNLIKELY(IsZero())) {\n      LOG_IF_NOT_GPU(ERROR) << \"Inverse of zero attempted\";\n      return std::nullopt;\n    }\n    ConjugateInPlace();\n    return static_cast<Derived*>(this);\n  }\n\n protected:\n  constexpr static void DoMul(const Derived& a, const Derived& b, Derived& c) {\n    // clang-format off\n    // (a.c0, a.c1) * (b.c0, b.c1)\n    //   = (a.c0 + a.c1 * x) * (b.c0 + b.c1 * x)\n    //   = a.c0 * b.c0 + (a.c0 * b.c1 + a.c1 * b.c0) * x + a.c1 * b.c1 * x²\n    //   = a.c0 * b.c0 + a.c1 * b.c1 * x² + (a.c0 * b.c1 + a.c1 * b.c0) * x\n    //   = a.c0 * b.c0 + a.c1 * b.c1 * q + (a.c0 * b.c1 + a.c1 * b.c0) * x\n    //   = (a.c0 * b.c0 + a.c1 * b.c1 * q, a.c0 * b.c1 + a.c1 * b.c0)\n    // where q is |Config::kNonResidue|.\n    // clang-format on\n    if constexpr (ExtensionDegree() == 2) {\n      BaseField c0;\n      {\n        BaseField lefts[] = {a.c0_, Config::MulByNonResidue(a.c1_)};\n        BaseField rights[] = {b.c0_, b.c1_};\n        c0 = BaseField::SumOfProductsSerial(lefts, rights);\n      }\n      {\n        BaseField lefts[] = {a.c0_, a.c1_};\n        BaseField rights[] = {b.c1_, b.c0_};\n        c.c1_ = BaseField::SumOfProductsSerial(lefts, rights);\n      }\n      c.c0_ = std::move(c0);\n    } else {\n      // See https://www.math.u-bordeaux.fr/~damienrobert/csi/book/book.pdf\n      // Karatsuba multiplication;\n      // Guide to Pairing-based cryptography, Algorithm 5.16.\n      // v0 = a.c0 * b.c0\n      BaseField v0 = a.c0_ * b.c0_;\n      // v1 = a.c1 * b.c1\n      BaseField v1 = a.c1_ * b.c1_;\n\n      // c.c1 = a.c0 + a.c1\n      c.c1_ = a.c0_ + a.c1_;\n      // c.c1 = (a.c0 + a.c1) * (b.c0 + b.c1)\n      // c.c1 = a.c0 * b.c0 + a.c0 * b.c1 + a.c1 * b.c0 + a.c1 * b.c1\n      c.c1_ *= (b.c0_ + b.c1_);\n      // c.c1 = a.c0 * b.c1 + a.c1 * b.c0 + a.c1 * b.c1\n      c.c1_ -= v0;\n      // c.c1 = a.c0 * b.c1 + a.c1 * b.c0\n      c.c1_ -= v1;\n      // c.c0 = a.c0 * b.c0 + a.c1 * b.c1 * q\n      c.c0_ = v0 + Config::MulByNonResidue(v1);\n    }\n  }\n\n  constexpr static void DoSquareImpl(const Derived& a, Derived& b) {\n    // (c0, c1)² = (c0 + c1 * x)²\n    //            = c0² + 2 * c0 * c1 * x + c1² * x²\n    //            = c0² + c1² * x² + 2 * c0 * c1 * x\n    //            = c0² + c1² * q + 2 * c0 * c1 * x\n    //            = (c0² + c1² * q, 2 * c0 * c1)\n    // where q is |Config::kNonResidue|.\n    // When q = -1, we can re-use intermediate additions to improve performance.\n\n    // v0 = c0 - c1\n    BaseField v0 = a.c0_ - a.c1_;\n    // v1 = c0 * c1\n    BaseField v1 = a.c0_ * a.c1_;\n    if constexpr (Config::kNonResidueIsMinusOne) {\n      // When the non-residue is -1, we save 2 intermediate additions,\n      // and use one fewer intermediate variable\n\n      // v0 = (c0 - c1) * (c0 + c1)\n      //    = c0² - c1²\n      v0 *= (a.c0_ + a.c1_);\n\n      // c0 = c0² - c1²\n      b.c0_ = std::move(v0);\n      // c1 = 2 * c0 * c1\n      b.c1_ = v1.Double();\n    } else {\n      // v2 = c0 - q * c1\n      BaseField v2 = a.c0_ - Config::MulByNonResidue(a.c1_);\n\n      // v0 = (v0 * v2)\n      // v0 = (c0 - c1) * (c0 - c1 * q)\n      // v0 = c0² - c0 * c1 * q - c0 * c1 + c1² * q\n      // v0 = c0² - (q + 1) * c0 * c1 + c1² * q\n      // v0 = c0² + c1² * q - (q + 1) * c0 * c1\n      v0 *= v2;\n\n      // c0 = v0 + (q + 1) * c0 * c1\n      // c0 = c0² + c1² * q - (q + 1) * c0 * c1 + (q + 1) * c0 * c1\n      // c0 = c0² + c1² * q\n      b.c0_ = v0 + v1;\n      b.c0_ += Config::MulByNonResidue(v1);\n      // c1 = 2 * c0 * c1\n      b.c1_ = v1.Double();\n    }\n  }\n\n  [[nodiscard]] constexpr static bool DoInverse(const Derived& a, Derived& b) {\n    if (UNLIKELY(a.IsZero())) {\n      LOG_IF_NOT_GPU(ERROR) << \"Inverse of zero attempted\";\n      return false;\n    }\n    // See https://www.math.u-bordeaux.fr/~damienrobert/csi/book/book.pdf\n    // Guide to Pairing-based Cryptography, Algorithm 5.19.\n    // v1 = c1²\n    BaseField v1 = a.c1_.Square();\n    // v0 = c0² - q * v1\n    BaseField v0 = a.c0_.Square();\n    v0 -= Config::MulByNonResidue(v1);\n\n    const std::optional<BaseField> v0_inv = v0.Inverse();\n    if (UNLIKELY(!v0_inv)) return false;\n    v1 = std::move(*v0_inv);\n    b.c0_ = a.c0_ * v1;\n    b.c1_ = a.c1_ * v1;\n    b.c1_.NegateInPlace();\n\n    return true;\n  }\n\n  // c = c0_ + c1_ * X\n  BaseField c0_;\n  BaseField c1_;\n};\n\ntemplate <\n    typename BaseField, typename Derived,\n    std::enable_if_t<std::is_same_v<BaseField, typename Derived::BaseField>>* =\n        nullptr>\nDerived operator*(const BaseField& element,\n                  const QuadraticExtensionField<Derived>& f) {\n  return static_cast<const Derived&>(f) * element;\n}\n\ntemplate <typename H, typename Derived>\nH AbslHashValue(H h, const QuadraticExtensionField<Derived>& f) {\n  return H::combine(std::move(h), f.c0(), f.c1());\n}\n\n}  // namespace math\n\nnamespace base {\n\ntemplate <typename Derived>\nclass Copyable<Derived, std::enable_if_t<std::is_base_of_v<\n                            math::QuadraticExtensionField<Derived>, Derived>>> {\n public:\n  static bool WriteTo(\n      const math::QuadraticExtensionField<Derived>& quadratic_extension_field,\n      Buffer* buffer) {\n    return buffer->WriteMany(quadratic_extension_field.c0(),\n                             quadratic_extension_field.c1());\n  }\n\n  static bool ReadFrom(\n      const ReadOnlyBuffer& buffer,\n      math::QuadraticExtensionField<Derived>* quadratic_extension_field) {\n    typename Derived::BaseField c0;\n    typename Derived::BaseField c1;\n    if (!buffer.ReadMany(&c0, &c1)) return false;\n\n    *quadratic_extension_field =\n        math::QuadraticExtensionField<Derived>(std::move(c0), std::move(c1));\n    return true;\n  }\n\n  static size_t EstimateSize(\n      const math::QuadraticExtensionField<Derived>& quadratic_extension_field) {\n    return base::EstimateSize(quadratic_extension_field.c0(),\n                              quadratic_extension_field.c1());\n  }\n};\n\ntemplate <typename Derived>\nclass RapidJsonValueConverter<\n    Derived, std::enable_if_t<std::is_base_of_v<\n                 math::QuadraticExtensionField<Derived>, Derived>>> {\n public:\n  using BaseField = typename math::QuadraticExtensionField<Derived>::BaseField;\n\n  template <typename Allocator>\n  static rapidjson::Value From(\n      const math::QuadraticExtensionField<Derived>& value,\n      Allocator& allocator) {\n    rapidjson::Value object(rapidjson::kObjectType);\n    AddJsonElement(object, \"c0\", value.c0(), allocator);\n    AddJsonElement(object, \"c1\", value.c1(), allocator);\n    return object;\n  }\n\n  static bool To(const rapidjson::Value& json_value, std::string_view key,\n                 math::QuadraticExtensionField<Derived>* value,\n                 std::string* error) {\n    BaseField c0;\n    BaseField c1;\n    if (!ParseJsonElement(json_value, \"c0\", &c0, error)) return false;\n    if (!ParseJsonElement(json_value, \"c1\", &c1, error)) return false;\n    *value =\n        math::QuadraticExtensionField<Derived>(std::move(c0), std::move(c1));\n    return true;\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_QUADRATIC_EXTENSION_FIELD_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/quadratic_extension_field_unittest.cc",
    "content": "#include <optional>\n\n#include \"absl/hash/hash_testing.h\"\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/elliptic_curves/bn/bn254/fq6.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n#include \"tachyon/math/finite_fields/test/gf7_2.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\nclass QuadraticExtensionFieldTest : public FiniteFieldTest<GF7_2> {};\n\n}  // namespace\n\nTEST_F(QuadraticExtensionFieldTest, Zero) {\n  EXPECT_TRUE(GF7_2::Zero().IsZero());\n  EXPECT_FALSE(GF7_2::One().IsZero());\n  EXPECT_FALSE(GF7_2::MinusOne().IsZero());\n}\n\nTEST_F(QuadraticExtensionFieldTest, One) {\n  EXPECT_TRUE(GF7_2::One().IsOne());\n  EXPECT_FALSE(GF7_2::Zero().IsOne());\n  EXPECT_FALSE(GF7_2::MinusOne().IsOne());\n}\n\nTEST_F(QuadraticExtensionFieldTest, MinusOne) {\n  EXPECT_TRUE(GF7_2::MinusOne().IsMinusOne());\n  EXPECT_FALSE(GF7_2::Zero().IsMinusOne());\n  EXPECT_FALSE(GF7_2::One().IsMinusOne());\n}\n\nTEST_F(QuadraticExtensionFieldTest, TwoInv) {\n  EXPECT_TRUE((GF7_2::TwoInv() * GF7_2(GF7(2))).IsOne());\n  EXPECT_FALSE((GF7_2::TwoInv() * GF7_2::One()).IsOne());\n}\n\nTEST_F(QuadraticExtensionFieldTest, Random) {\n  bool success = false;\n  GF7_2 r = GF7_2::Random();\n  for (size_t i = 0; i < 100; ++i) {\n    if (r != GF7_2::Random()) {\n      success = true;\n      break;\n    }\n  }\n  EXPECT_TRUE(success);\n}\n\nTEST_F(QuadraticExtensionFieldTest, Norm) {\n  constexpr static uint32_t kModulus = GF7::Config::kModulus;\n  GF7_2 r = GF7_2::Random();\n  GF7_2 r_to_p = r.Pow(kModulus);\n  EXPECT_EQ(r.Norm(), (r * r_to_p).c0());\n}\n\nTEST_F(QuadraticExtensionFieldTest, ConjugateInPlace) {\n  GF7_2 f = GF7_2::Random();\n  GF7_2 f2 = f;\n  f2.ConjugateInPlace();\n  EXPECT_EQ(f.c0(), f2.c0());\n  EXPECT_EQ(f.c1(), -f2.c1());\n}\n\nTEST_F(QuadraticExtensionFieldTest, EqualityOperators) {\n  GF7_2 f(GF7(3), GF7(4));\n  GF7_2 f2(GF7(4), GF7(4));\n  EXPECT_NE(f, f2);\n\n  GF7_2 f3(GF7(4), GF7(3));\n  EXPECT_NE(f2, f3);\n\n  GF7_2 f4(GF7(4), GF7(4));\n  EXPECT_EQ(f2, f4);\n}\n\nTEST_F(QuadraticExtensionFieldTest, ComparisonOperator) {\n  GF7_2 f(GF7(3), GF7(4));\n  GF7_2 f2(GF7(4), GF7(4));\n  EXPECT_LT(f, f2);\n  EXPECT_LE(f, f2);\n  EXPECT_GT(f2, f);\n  EXPECT_GE(f2, f);\n\n  GF7_2 f3(GF7(4), GF7(3));\n  GF7_2 f4(GF7(4), GF7(4));\n  EXPECT_LT(f3, f4);\n  EXPECT_LE(f3, f4);\n  EXPECT_GT(f4, f3);\n  EXPECT_GE(f4, f3);\n}\n\nTEST_F(QuadraticExtensionFieldTest, AdditiveOperators) {\n  struct {\n    GF7_2 a;\n    GF7_2 b;\n    GF7_2 sum;\n    GF7_2 amb;\n    GF7_2 bma;\n  } tests[] = {\n      {\n          {GF7(1), GF7(2)},\n          {GF7(3), GF7(5)},\n          {GF7(4), GF7(0)},\n          {GF7(5), GF7(4)},\n          {GF7(2), GF7(3)},\n      },\n  };\n\n  for (const auto& test : tests) {\n    EXPECT_EQ(test.a + test.b, test.sum);\n    EXPECT_EQ(test.b + test.a, test.sum);\n    EXPECT_EQ(test.a - test.b, test.amb);\n    EXPECT_EQ(test.b - test.a, test.bma);\n\n    GF7_2 tmp = test.a;\n    tmp += test.b;\n    EXPECT_EQ(tmp, test.sum);\n    tmp -= test.b;\n    EXPECT_EQ(tmp, test.a);\n  }\n}\n\nTEST_F(QuadraticExtensionFieldTest, AdditiveGroupOperators) {\n  GF7_2 f(GF7(3), GF7(4));\n  GF7_2 f_neg(GF7(4), GF7(3));\n  EXPECT_EQ(-f, f_neg);\n  f.NegateInPlace();\n  EXPECT_EQ(f, f_neg);\n\n  f = GF7_2(GF7(3), GF7(4));\n  GF7_2 f_dbl(GF7(6), GF7(1));\n  EXPECT_EQ(f.Double(), f_dbl);\n  f.DoubleInPlace();\n  EXPECT_EQ(f, f_dbl);\n}\n\nTEST_F(QuadraticExtensionFieldTest, MultiplicativeOperators) {\n  struct {\n    GF7_2 a;\n    GF7_2 b;\n    GF7_2 mul;\n    GF7_2 adb;\n    GF7_2 bda;\n  } tests[] = {\n      {\n          {GF7(1), GF7(2)},\n          {GF7(3), GF7(5)},\n          {GF7(0), GF7(4)},\n          {GF7(1), GF7(6)},\n          {GF7(4), GF7(4)},\n      },\n  };\n\n  for (const auto& test : tests) {\n    EXPECT_EQ(test.a * test.b, test.mul);\n    EXPECT_EQ(test.b * test.a, test.mul);\n    EXPECT_EQ(test.a / test.b, test.adb);\n    EXPECT_EQ(test.b / test.a, test.bda);\n\n    GF7_2 tmp = test.a;\n    tmp *= test.b;\n    EXPECT_EQ(tmp, test.mul);\n    ASSERT_TRUE(tmp /= test.b);\n    EXPECT_EQ(tmp, test.a);\n  }\n}\n\nTEST_F(QuadraticExtensionFieldTest, MultiplicativeOperators2) {\n  GF7_2 f(GF7(3), GF7(4));\n  GF7_2 f_mul(GF7(6), GF7(1));\n  EXPECT_EQ(f * GF7(2), f_mul);\n  f *= GF7(2);\n  EXPECT_EQ(f, f_mul);\n}\n\nTEST_F(QuadraticExtensionFieldTest, MultiplicativeGroupOperators) {\n  GF7_2 f = GF7_2::Random();\n  std::optional<GF7_2> f_inv = f.Inverse();\n  if (UNLIKELY(f.IsZero())) {\n    ASSERT_FALSE(f_inv);\n    ASSERT_FALSE(f.InverseInPlace());\n  } else {\n    EXPECT_EQ(f * *f_inv, GF7_2::One());\n    GF7_2 f_tmp = f;\n    EXPECT_EQ(**f.InverseInPlace() * f_tmp, GF7_2::One());\n  }\n\n  f = GF7_2(GF7(3), GF7(4));\n  GF7_2 f_sqr = GF7_2(GF7(0), GF7(3));\n  EXPECT_EQ(f.Square(), f_sqr);\n  f.SquareInPlace();\n  EXPECT_EQ(f, f_sqr);\n}\n\nTEST(CyclotomicInverseTest, FastCyclotomicInverse) {\n  bn254::Fq6::Init();\n  bn254::Fq6 f = bn254::Fq6::Random();\n  bn254::Fq6 f_tmp = f;\n  if (UNLIKELY(f.IsZero())) {\n    ASSERT_FALSE(f.InverseInPlace());\n    ASSERT_FALSE(f_tmp.CyclotomicInverseInPlace());\n  } else {\n    ASSERT_TRUE(f.InverseInPlace());\n    ASSERT_TRUE(f_tmp.CyclotomicInverseInPlace());\n  }\n  EXPECT_EQ(f, f_tmp);\n}\n\nTEST_F(QuadraticExtensionFieldTest, JsonValueConverter) {\n  GF7_2 expected_point(GF7(1), GF7(2));\n  std::string expected_json = R\"({\"c0\":1,\"c1\":2})\";\n\n  GF7_2 p;\n  std::string error;\n  ASSERT_TRUE(base::ParseJson(expected_json, &p, &error));\n  ASSERT_TRUE(error.empty());\n  EXPECT_EQ(p, expected_point);\n\n  std::string json = base::WriteToJson(p);\n  EXPECT_EQ(json, expected_json);\n}\n\nTEST_F(QuadraticExtensionFieldTest, Hash) {\n  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(\n      std::make_tuple(GF7_2::Zero(), GF7_2::One(), GF7_2::Random())));\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/finite_fields/quartic_extension_field.h",
    "content": "#ifndef TACHYON_MATH_FINITE_FIELDS_QUARTIC_EXTENSION_FIELD_H_\n#define TACHYON_MATH_FINITE_FIELDS_QUARTIC_EXTENSION_FIELD_H_\n\n#include <array>\n#include <optional>\n#include <string>\n#include <utility>\n\n#include \"absl/base/call_once.h\"\n#include \"absl/strings/substitute.h\"\n#include \"absl/types/span.h\"\n\n#include \"tachyon/base/buffer/copyable.h\"\n#include \"tachyon/base/json/json.h\"\n#include \"tachyon/math/finite_fields/cyclotomic_multiplicative_subgroup.h\"\n#include \"tachyon/math/finite_fields/extension_field_base.h\"\n\nnamespace tachyon {\nnamespace math {\n\ntemplate <typename Derived>\nclass QuarticExtensionField : public CyclotomicMultiplicativeSubgroup<Derived>,\n                              public ExtensionFieldBase<Derived> {\n public:\n  using Config = typename FiniteField<Derived>::Config;\n  using BaseField = typename Config::BaseField;\n  using BasePrimeField = typename Config::BasePrimeField;\n  using ConstIterator = typename ExtensionFieldBase<Derived>::ConstIterator;\n\n  constexpr QuarticExtensionField() = default;\n  // NOTE(chokobole): This is needed by Eigen matrix.\n  template <typename T, std::enable_if_t<std::is_integral_v<T>>* = nullptr>\n  constexpr explicit QuarticExtensionField(T value) : c0_(value) {}\n  constexpr explicit QuarticExtensionField(const BaseField& c0)\n      : c0_(c0),\n        c1_(BaseField::Zero()),\n        c2_(BaseField::Zero()),\n        c3_(BaseField::Zero()) {}\n  constexpr explicit QuarticExtensionField(BaseField&& c0)\n      : c0_(std::move(c0)),\n        c1_(BaseField::Zero()),\n        c2_(BaseField::Zero()),\n        c3_(BaseField::Zero()) {}\n  constexpr QuarticExtensionField(const BaseField& c0, const BaseField& c1,\n                                  const BaseField& c2, const BaseField& c3)\n      : c0_(c0), c1_(c1), c2_(c2), c3_(c3) {}\n  constexpr QuarticExtensionField(BaseField&& c0, BaseField&& c1,\n                                  BaseField&& c2, BaseField&& c3)\n      : c0_(std::move(c0)),\n        c1_(std::move(c1)),\n        c2_(std::move(c2)),\n        c3_(std::move(c3)) {}\n\n  constexpr static Derived Zero() {\n    return {BaseField::Zero(), BaseField::Zero(), BaseField::Zero(),\n            BaseField::Zero()};\n  }\n\n  constexpr static Derived One() {\n    return {BaseField::One(), BaseField::Zero(), BaseField::Zero(),\n            BaseField::Zero()};\n  }\n\n  constexpr static Derived MinusOne() {\n    return {BaseField::MinusOne(), BaseField::Zero(), BaseField::Zero(),\n            BaseField::Zero()};\n  }\n\n  constexpr static Derived TwoInv() {\n    return {BaseField::TwoInv(), BaseField::Zero(), BaseField::Zero(),\n            BaseField::Zero()};\n  }\n\n  static Derived Random() {\n    return {BaseField::Random(), BaseField::Random(), BaseField::Random(),\n            BaseField::Random()};\n  }\n\n  // TODO(chokobole): Should be generalized for packed extension field.\n  static Derived FromBasePrimeFields(\n      absl::Span<const BasePrimeField> prime_fields) {\n    CHECK_EQ(prime_fields.size(), ExtensionDegree());\n    constexpr size_t kBaseFieldDegree = BaseField::ExtensionDegree();\n    if constexpr (kBaseFieldDegree == 1) {\n      return Derived(prime_fields[0], prime_fields[1], prime_fields[2],\n                     prime_fields[3]);\n    } else {\n      BaseField c0 = BaseField::FromBasePrimeFields(\n          prime_fields.subspan(0, kBaseFieldDegree));\n      prime_fields.remove_prefix(kBaseFieldDegree);\n      BaseField c1 = BaseField::FromBasePrimeFields(\n          prime_fields.subspan(0, kBaseFieldDegree));\n      prime_fields.remove_prefix(kBaseFieldDegree);\n      BaseField c2 = BaseField::FromBasePrimeFields(\n          prime_fields.subspan(0, kBaseFieldDegree));\n      prime_fields.remove_prefix(kBaseFieldDegree);\n      BaseField c3 = BaseField::FromBasePrimeFields(\n          prime_fields.subspan(kBaseFieldDegree));\n      return Derived(std::move(c0), std::move(c1), std::move(c2),\n                     std::move(c3));\n    }\n  }\n\n  constexpr std::array<BaseField, 4> ToBaseFields() const {\n    return {c0_, c1_, c2_, c3_};\n  }\n\n  ConstIterator end() const { return {static_cast<const Derived&>(*this), 4}; }\n\n  static void Init() {\n    static absl::once_flag once;\n    absl::call_once(once, []() {\n      kInv3 = *BaseField(3).Inverse();\n      kInv4 = *BaseField(4).Inverse();\n      kInv6 = *BaseField(6).Inverse();\n      kInv12 = *BaseField(12).Inverse();\n      kInv20 = *BaseField(20).Inverse();\n      kInv24 = *BaseField(24).Inverse();\n      kInv30 = *BaseField(30).Inverse();\n      kInv120 = *BaseField(120).Inverse();\n      kNeg5 = -BaseField(5);\n      kNegInv2 = -BaseField::TwoInv();\n      kNegInv3 = -kInv3;\n      kNegInv4 = -kInv4;\n      kNegInv6 = -kInv6;\n      kNegInv12 = -kInv12;\n      kNegInv24 = -kInv24;\n      kNegInv120 = -kInv120;\n    });\n  }\n\n  constexpr bool IsZero() const {\n    return c0_.IsZero() && c1_.IsZero() && c2_.IsZero() && c3_.IsZero();\n  }\n\n  constexpr bool IsOne() const {\n    return c0_.IsOne() && c1_.IsZero() && c2_.IsZero() && c3_.IsZero();\n  }\n\n  constexpr bool IsMinusOne() const {\n    return c0_.IsMinusOne() && c1_.IsZero() && c2_.IsZero() && c3_.IsZero();\n  }\n\n  constexpr static uint32_t ExtensionDegree() {\n    return 4 * BaseField::ExtensionDegree();\n  }\n\n  // Calculate the norm of an element with respect to |BaseField|.\n  // The norm maps an element |a| in the extension field Fqᵐ to an element\n  // in the |BaseField| Fq. |a.Norm() = a * a^q * a^q² * a^q³|\n  constexpr BaseField Norm() const {\n    // w.r.t to |BaseField|, we need the 0th, 1st, 2nd & 3rd powers of q.\n    // Since Frobenius coefficients on the towered extensions are\n    // indexed w.r.t. to |BasePrimeField|, we need to calculate the correct\n    // index.\n    // NOTE(chokobole): This assumes that |BaseField::ExtensionDegree()|\n    // never overflows even on a 32-bit machine.\n    size_t index_multiplier = size_t{BaseField::ExtensionDegree()};\n    Derived self_to_p = static_cast<const Derived&>(*this);\n    self_to_p.FrobeniusMapInPlace(index_multiplier);\n    Derived self_to_p2 = static_cast<const Derived&>(*this);\n    self_to_p2.FrobeniusMapInPlace(2 * index_multiplier);\n    Derived self_to_p3 = static_cast<const Derived&>(*this);\n    self_to_p3.FrobeniusMapInPlace(3 * index_multiplier);\n    self_to_p *= (self_to_p2 * self_to_p3 * static_cast<const Derived&>(*this));\n    // NOTE(chokobole): The |CHECK()| below is not device code.\n    // See https://github.com/kroma-network/tachyon/issues/76\n    CHECK(self_to_p.c1().IsZero() && self_to_p.c2().IsZero() &&\n          self_to_p.c3().IsZero());\n    return self_to_p.c0();\n  }\n\n  constexpr Derived& FrobeniusMapInPlace(uint32_t exponent) {\n    c0_.FrobeniusMapInPlace(exponent);\n    c1_.FrobeniusMapInPlace(exponent);\n    c2_.FrobeniusMapInPlace(exponent);\n    c3_.FrobeniusMapInPlace(exponent);\n    c1_ *=\n        Config::kFrobeniusCoeffs[exponent % Config::kDegreeOverBasePrimeField];\n    c2_ *=\n        Config::kFrobeniusCoeffs2[exponent % Config::kDegreeOverBasePrimeField];\n    c3_ *=\n        Config::kFrobeniusCoeffs3[exponent % Config::kDegreeOverBasePrimeField];\n    return *static_cast<Derived*>(this);\n  }\n\n  std::string ToString() const {\n    return absl::Substitute(\"($0, $1, $2, $3)\", c0_.ToString(), c1_.ToString(),\n                            c2_.ToString(), c3_.ToString());\n  }\n\n  std::string ToHexString(bool pad_zero = false) const {\n    return absl::Substitute(\"($0, $1, $2, $3)\", c0_.ToHexString(pad_zero),\n                            c1_.ToHexString(pad_zero),\n                            c2_.ToHexString(pad_zero),\n                            c3_.ToHexString(pad_zero));\n  }\n\n  constexpr const BaseField& c0() const { return c0_; }\n  constexpr const BaseField& c1() const { return c1_; }\n  constexpr const BaseField& c2() const { return c2_; }\n  constexpr const BaseField& c3() const { return c3_; }\n\n  constexpr const BaseField& operator[](size_t index) const {\n    switch (index) {\n      case 0:\n        return c0_;\n      case 1:\n        return c1_;\n      case 2:\n        return c2_;\n      case 3:\n        return c3_;\n    }\n    NOTREACHED();\n    return c0_;\n  }\n\n  constexpr BaseField& operator[](size_t index) {\n    return const_cast<BaseField&>(std::as_const(*this).operator[](index));\n  }\n\n  constexpr bool operator==(const Derived& other) const {\n    return c0_ == other.c0_ && c1_ == other.c1_ && c2_ == other.c2_ &&\n           c3_ == other.c3_;\n  }\n\n  constexpr bool operator!=(const Derived& other) const {\n    return !operator==(other);\n  }\n\n  constexpr bool operator<(const Derived& other) const {\n    if (c3_ == other.c3_) {\n      if (c2_ == other.c2_) {\n        if (c1_ == other.c1_) return c0_ < other.c0_;\n        return c1_ < other.c1_;\n      }\n      return c2_ < other.c2_;\n    }\n    return c3_ < other.c3_;\n  }\n\n  constexpr bool operator>(const Derived& other) const {\n    if (c3_ == other.c3_) {\n      if (c2_ == other.c2_) {\n        if (c1_ == other.c1_) return c0_ > other.c0_;\n        return c1_ > other.c1_;\n      }\n      return c2_ > other.c2_;\n    }\n    return c3_ > other.c3_;\n  }\n\n  constexpr bool operator<=(const Derived& other) const {\n    return !operator>(other);\n  }\n\n  constexpr bool operator>=(const Derived& other) const {\n    return !operator<(other);\n  }\n\n  // AdditiveSemigroup methods\n  constexpr Derived Add(const Derived& other) const {\n    return {\n        c0_ + other.c0_,\n        c1_ + other.c1_,\n        c2_ + other.c2_,\n        c3_ + other.c3_,\n    };\n  }\n\n  constexpr Derived& AddInPlace(const Derived& other) {\n    c0_ += other.c0_;\n    c1_ += other.c1_;\n    c2_ += other.c2_;\n    c3_ += other.c3_;\n    return *static_cast<Derived*>(this);\n  }\n\n  constexpr Derived DoubleImpl() const {\n    return {\n        c0_.Double(),\n        c1_.Double(),\n        c2_.Double(),\n        c3_.Double(),\n    };\n  }\n\n  constexpr Derived& DoubleImplInPlace() {\n    c0_.DoubleInPlace();\n    c1_.DoubleInPlace();\n    c2_.DoubleInPlace();\n    c3_.DoubleInPlace();\n    return *static_cast<Derived*>(this);\n  }\n\n  // AdditiveGroup methods\n  constexpr Derived Sub(const Derived& other) const {\n    return {\n        c0_ - other.c0_,\n        c1_ - other.c1_,\n        c2_ - other.c2_,\n        c3_ - other.c3_,\n    };\n  }\n\n  constexpr Derived& SubInPlace(const Derived& other) {\n    c0_ -= other.c0_;\n    c1_ -= other.c1_;\n    c2_ -= other.c2_;\n    c3_ -= other.c3_;\n    return *static_cast<Derived*>(this);\n  }\n\n  constexpr Derived Negate() const {\n    return {\n        -c0_,\n        -c1_,\n        -c2_,\n        -c3_,\n    };\n  }\n\n  constexpr Derived& NegateInPlace() {\n    c0_.NegateInPlace();\n    c1_.NegateInPlace();\n    c2_.NegateInPlace();\n    c3_.NegateInPlace();\n    return *static_cast<Derived*>(this);\n  }\n\n  // MultiplicativeSemigroup methods\n  constexpr Derived Mul(const Derived& other) const {\n    Derived ret{};\n    DoMul(*static_cast<const Derived*>(this), other, ret);\n    return ret;\n  }\n\n  constexpr Derived& MulInPlace(const Derived& other) {\n    DoMul(*static_cast<const Derived*>(this), other,\n          *static_cast<Derived*>(this));\n    return *static_cast<Derived*>(this);\n  }\n\n  constexpr Derived Mul(const BaseField& element) const {\n    return {\n        c0_ * element,\n        c1_ * element,\n        c2_ * element,\n        c3_ * element,\n    };\n  }\n\n  constexpr Derived& MulInPlace(const BaseField& element) {\n    c0_ *= element;\n    c1_ *= element;\n    c2_ *= element;\n    c3_ *= element;\n    return *static_cast<Derived*>(this);\n  }\n\n  constexpr Derived SquareImpl() const {\n    Derived ret{};\n    DoSquareImpl(*static_cast<const Derived*>(this), ret);\n    return ret;\n  }\n\n  constexpr Derived& SquareImplInPlace() {\n    DoSquareImpl(*static_cast<const Derived*>(this),\n                 *static_cast<Derived*>(this));\n    return *static_cast<Derived*>(this);\n  }\n\n  // MultiplicativeGroup methods\n  constexpr std::optional<Derived> Inverse() const {\n    Derived ret{};\n    if (LIKELY(DoInverse(*static_cast<const Derived*>(this), ret))) {\n      return ret;\n    }\n    LOG_IF_NOT_GPU(ERROR) << \"Inverse of zero attempted\";\n    return std::nullopt;\n  }\n\n  [[nodiscard]] constexpr std::optional<Derived*> InverseInPlace() {\n    if (LIKELY(DoInverse(*static_cast<const Derived*>(this),\n                         *static_cast<Derived*>(this)))) {\n      return static_cast<Derived*>(this);\n    }\n    LOG_IF_NOT_GPU(ERROR) << \"Inverse of zero attempted\";\n    return std::nullopt;\n  }\n\n protected:\n  constexpr static void DoMul(const Derived& a, const Derived& b, Derived& c) {\n    // clang-format off\n    // (a.c0, a.c1, a.c2, a.c3) * (b.c0, b.c1, b.c2, b.c3)\n    //   = (a.c0 + a.c1 * x + a.c2 * x² + a.c3 * x³) * (b.c0 + b.c1 * x + b.c2 * x² + b.c3 * x³)\n    //   = a.c0 * b.c0 + (a.c0 * b.c1 + a.c1 * b.c0) * x + (a.c0 * b.c2 + a.c1 * b.c1 + a.c2 * b.c0) * x² +\n    //     (a.c0 * b.c3 + a.c1 * b.c2 + a.c2 * b.c1 + a.c3 * b.c0) * x³ + (a.c1 * b.c3 + a.c2 * b.c2 + a.c3 * b.c1) * x⁴ +\n    //     (a.c2 * b.c3 + a.c3 * b.c2) * x⁵ + a.c3 * b.c3 * x⁶\n    //   = a.c0 * b.c0 + (a.c1 * b.c3 + a.c2 * b.c2 + a.c3 * b.c1) * x⁴ +\n    //     (a.c0 * b.c1 + a.c1 * b.c0) * x + (a.c2 * b.c3 + a.c3 * b.c2) * x⁵ +\n    //     (a.c0 * b.c2 + a.c1 * b.c1 + a.c2 * b.c0) * x² + a.c3 * b.c3 * x⁶ +\n    //     (a.c0 * b.c3 + a.c1 * b.c2 + a.c2 * b.c1 + a.c3 * b.c0) * x³\n    //   = a.c0 * b.c0 + (a.c1 * b.c3 + a.c2 * b.c2 + a.c3 * b.c1) * q +\n    //     (a.c0 * b.c1 + a.c1 * b.c0) * x + (a.c2 * b.c3 + a.c3 * b.c2) * q * x +\n    //     (a.c0 * b.c2 + a.c1 * b.c1 + a.c2 * b.c0) * x² + a.c3 * b.c3 * q * x² +\n    //     (a.c0 * b.c3 + a.c1 * b.c2 + a.c2 * b.c1 + a.c3 * b.c0) * x³\n    //   = (a.c0 * b.c0 + (a.c1 * b.c3 + a.c2 * b.c2 + a.c3 * b.c1) * q,\n    //      a.c0 * b.c1 + a.c1 * b.c0 + (a.c2 * b.c3 + a.c3 * b.c2) * q,\n    //      a.c0 * b.c2 + a.c1 * b.c1 + a.c2 * b.c0 + a.c3 * b.c3 * q,\n    //      a.c0 * b.c3 + a.c1 * b.c2 + a.c2 * b.c1 + a.c3 * b.c0)\n    // where q is |Config::kNonResidue|.\n\n    // See https://eprint.iacr.org/2006/471.pdf\n    // Devegili OhEig Scott Dahab --- Multiplication and Squaring on AbstractPairing-Friendly Fields.pdf; Section 5.2\n    // clang-format on\n\n    // h1 = 2 * a.c1\n    BaseField h1 = a.c1_.Double();\n    // h2 = 4 * a.c2\n    BaseField h2 = a.c2_.Double();\n    h2.DoubleInPlace();\n    // h3 = 8 * a.c3\n    BaseField h3 = a.c3_.Double();\n    h3.DoubleInPlace().DoubleInPlace();\n    // h4 = 2 * b.c1\n    BaseField h4 = b.c1_.Double();\n    // h5 = 4 * b.c2\n    BaseField h5 = b.c2_.Double();\n    h5.DoubleInPlace();\n    // h6 = 8 * b.c3\n    BaseField h6 = b.c3_.Double();\n    h6.DoubleInPlace().DoubleInPlace();\n\n    // v0 = a.c0 * b.c0\n    BaseField v0 = a.c0_ * b.c0_;\n    // v1 = (a.c0 + a.c1 + a.c2 + a.c3) * (b.c0 + b.c1 + b.c2 + b.c3)\n    BaseField v1 =\n        (a.c0_ + a.c1_ + a.c2_ + a.c3_) * (b.c0_ + b.c1_ + b.c2_ + b.c3_);\n    // v2 = (a.c0 - a.c1 + a.c2 - a.c3) * (b.c0 - b.c1 + b.c2 - b.c3)\n    BaseField v2 =\n        (a.c0_ - a.c1_ + a.c2_ - a.c3_) * (b.c0_ - b.c1_ + b.c2_ - b.c3_);\n    // v3 = (a.c0 + 2 * a.c1 + 4 * a.c2 + 8 * a.c3) *\n    //      (b.c0 + 2 * b.c1 + 4 * b.c2 + 8 * b.c3)\n    BaseField v3 = (a.c0_ + h1 + h2 + h3) * (b.c0_ + h4 + h5 + h6);\n    // v4 = (a.c0 - 2 * a.c1 + 4 * a.c2 - 8 * a.c3) *\n    //      (b.c0 - 2 * b.c1 + 4 * b.c2 - 8 * b.c3)\n    BaseField v4 = (a.c0_ - h1 + h2 - h3) * (b.c0_ - h4 + h5 - h6);\n    // h1 = 3 * a.c1\n    h1 += a.c1_;\n    // h2 = 9 * a.c2\n    h2.DoubleInPlace().AddInPlace(a.c2_);\n    // h3 = 27 * a.c3\n    h3 += a.c3_;\n    h3 += h3.Double();\n    // h4 = 3 * b.c1\n    h4 += b.c1_;\n    // h5 = 9 * b.c2\n    h5.DoubleInPlace().AddInPlace(b.c2_);\n    // h6 = 27 * b.c3\n    h6 += b.c3_;\n    h6 += h6.Double();\n    // v5 = (a.c0 + 3 * a.c1 + 9 * a.c2 + 27 * a.c3) *\n    //      (b.c0 + 3 * b.c1 + 9 * b.c2 + 27 * b.c3)\n    BaseField v5 = (a.c0_ + h1 + h2 + h3) * (b.c0_ + h4 + h5 + h6);\n    // v6 = a.c3 * b.c3\n    BaseField v6 = a.c3_ * b.c3_;\n\n    // v0_5 = 5 * v0\n    BaseField v0_5 = v0.Double();\n    v0_5.DoubleInPlace().AddInPlace(v0);\n    // v6_3 = 3 * v6\n    BaseField v6_3 = v6.Double();\n    v6_3 += v6;\n\n    // clang-format off\n    // c.c0 = v0 +\n    //        q * ((1 / 4) * v0 - (1 / 6) * (v1 + v2) + (1 / 24) * (v3 + v4) - 5 * v6)\n    c.c0_ = v0 +\n            Config::MulByNonResidue(kInv4 * v0 + kNegInv6 * (v1 + v2) + kInv24 * (v3 + v4) + kNeg5 * v6);\n    // c.c1 = -(1 / 3) * v0 + v1 - (1 / 2) * v2 - (1 / 4) * v3 + (1 / 20) * v4 + (1 / 30) * v5 - 12 * v6 +\n    //        q * (-(1 / 12) * (v0 - v1) + (1 / 24) * (v2 - v3) - (1 / 120) * (v4 - v5) - 3 * v6)\n    c.c1_ = kNegInv3 * v0 + v1 + kNegInv2 * v2 + kNegInv4 * v3 + kInv20 * v4 + kInv30 * v5 - v6_3.Double().Double() +\n            Config::MulByNonResidue(kNegInv12 * (v0 - v1) + kInv24 * (v2 - v3) + kNegInv120 * (v4 - v5) - v6_3);\n    // c.c2 = -(5 / 4) * v0 + (2 / 3) * (v1 + v2) - (1 / 24) * (v3 + v4) + 4 * v6 +\n    //        q * v6\n    c.c2_ = kNegInv4 * v0_5 + kInv3 * (v1 + v2).Double() + kNegInv24 * (v3 + v4) + v6.Double().Double() +\n            Config::MulByNonResidue(v6);\n    // c.c3 = (1 / 12) * (5 * v0 - 7 * v1) - (1 / 24) * (v2 - 7 * v3 + v4 + v5) + 15 * v6\n    c.c3_ = kInv12 * (v0_5 - v1.Double().Double().Double() + v1) + kNegInv24 * (v2 - v3.Double().Double().Double() + v3 + v4 + v5) + v6_3.Double().Double() + v6_3;\n    // clang-format on\n  }\n\n  constexpr static void DoSquareImpl(const Derived& a, Derived& b) {\n    // clang-format off\n    // (c0, c1, c2, c3)²\n    //   = (c0 + c1 * x + c2 * x² + c3 * x³)²\n    //   = c0² + 2 * c0 * c1 * x + (c1² + 2 * c0 * c2) * x² + 2 * (c0 * c3 + c1 * c2) * x³ + (c2² + 2 * c1 * c3) * x⁴ + 2 * c2 * c3 * x⁵ + c3 * x⁶\n    //   = c0² + (c2² + 2 * c1 * c3) * x⁴ + 2 * c0 * c1 * x + 2 * c2 * c3 * x⁵ + (c1² + 2 * c0 * c2) * x² + c3 * x⁶ + 2 * (c0 * c3 + c1 * c2) * x³\n    //   = c0² + (c2² + 2 * c1 * c3) * q + 2 * (c0 * c1 + c2 * c3 * q) * x + (c1² + 2 * c0 * c2 + c3 * q) * x² + 2 * (c0 * c3 + c1 * c2) * x³\n    //   = (c0² + (c2² + 2 * c1 * c3) * q, 2 * (c0 * c1 + c2 * c3 * q), c1² + 2 * c0 * c2 + c3 * q, 2 * (c0 * c3 + c1 * c2))\n    // where q is |Config::kNonResidue|.\n\n    // See https://eprint.iacr.org/2006/471.pdf\n    // Devegili OhEig Scott Dahab --- Multiplication and Squaring on AbstractPairing-Friendly Fields.pdf; Section 5\n    // clang-format on\n\n    // h1 = 2 * c1\n    BaseField h1 = a.c1_.Double();\n    // h2 = 4 * c2\n    BaseField h2 = a.c2_.Double();\n    h2.DoubleInPlace();\n    // h3 = 8 * c3\n    BaseField h3 = a.c3_.Double();\n    h3.DoubleInPlace().DoubleInPlace();\n\n    // v0 = c0²\n    BaseField v0 = a.c0_.Square();\n    // v1 = (c0 + c1 + c2 + c3)²\n    BaseField v1 = (a.c0_ + a.c1_ + a.c2_ + a.c3_).Square();\n    // v2 = (c0 - c1 + c2 - c3)²\n    BaseField v2 = (a.c0_ - a.c1_ + a.c2_ - a.c3_).Square();\n    // v3 = (c0 + 2 * c1 + 4 * c2 + 8 * c3)²\n    BaseField v3 = (a.c0_ + h1 + h2 + h3).Square();\n    // v4 = (c0 - 2 * c1 + 4 * c2 - 8 * c3)²\n    BaseField v4 = (a.c0_ - h1 + h2 - h3).Square();\n    // h1 = 3 * c1\n    h1 += a.c1_;\n    // h2 = 9 * c2\n    h2.DoubleInPlace().AddInPlace(a.c2_);\n    // h3 = 27 * c3\n    h3 += a.c3_;\n    h3 += h3.Double();\n    // v5 = (c0 + 3 * c1 + 9 * c2 + 27 * c3)²\n    BaseField v5 = (a.c0_ + h1 + h2 + h3).Square();\n    // v6 = c3²\n    BaseField v6 = a.c3_.Square();\n\n    // v0_5 = 5 * v0\n    BaseField v0_5 = v0.Double();\n    v0_5.DoubleInPlace().AddInPlace(v0);\n    // v6_3 = 3 * v6\n    BaseField v6_3 = v6.Double();\n    v6_3 += v6;\n\n    // clang-format off\n    // b.c0 = v0 +\n    //        q * ((1 / 4) * v0 - (1 / 6) * (v1 + v2) + (1 / 24) * (v3 + v4) - 5 * v6)\n    b.c0_ = v0 +\n            Config::MulByNonResidue(kInv4 * v0 + kNegInv6 * (v1 + v2) + kInv24 * (v3 + v4) + kNeg5 * v6);\n    // b.c1 = -(1 / 3) * v0 + v1 - (1 / 2) * v2 - (1 / 4) * v3 + (1 / 20) * v4 + (1 / 30) * v5 - 12 * v6 +\n    //        q * (-(1 / 12) * (v0 - v1) + (1 / 24) * (v2 - v3) - (1 / 120) * (v4 - v5) - 3 * v6)\n    b.c1_ = kNegInv3 * v0 + v1 + kNegInv2 * v2 + kNegInv4 * v3 + kInv20 * v4 + kInv30 * v5 - v6_3.Double().Double() +\n            Config::MulByNonResidue(kNegInv12 * (v0 - v1) + kInv24 * (v2 - v3) + kNegInv120 * (v4 - v5) - v6_3);\n    // b.c2 = -(5 / 4) * v0 + (2 / 3) * (v1 + v2) - (1 / 24) * (v3 + v4) + 4 * v6 +\n    //        q * v6\n    b.c2_ = kNegInv4 * v0_5 + kInv3 * (v1 + v2).Double() + kNegInv24 * (v3 + v4) + v6.Double().Double() +\n            Config::MulByNonResidue(v6);\n    // b.c3 = (1 / 12) * (5 * v0 - 7 * v1) - (1 / 24) * (v2 - 7 * v3 + v4 + v5) + 15 * v6\n    b.c3_ = kInv12 * (v0_5 - v1.Double().Double().Double() + v1) + kNegInv24 * (v2 - v3.Double().Double().Double() + v3 + v4 + v5) + v6_3.Double().Double() + v6_3;\n    // clang-format on\n  }\n\n  [[nodiscard]] constexpr static bool DoInverse(const Derived& a, Derived& b) {\n    if (UNLIKELY(a.IsZero())) {\n      LOG_IF_NOT_GPU(ERROR) << \"Inverse of zero attempted\";\n      return false;\n    }\n\n    // See Algorithm 11.3.4 in Handbook of Elliptic and Hyperelliptic Curve\n    // Cryptography.\n    // Compute aʳ⁻¹, where r = (p⁴ - 1) / (p - 1) = p³ + p² + p + 1\n    size_t index_multiplier = size_t{BaseField::ExtensionDegree()};\n    // f = a^{p³ + p² + p}\n    Derived a_to_r_minus_1 = a;\n    a_to_r_minus_1.FrobeniusMapInPlace(index_multiplier);\n    Derived a_to_p2 = a;\n    a_to_p2.FrobeniusMapInPlace(2 * index_multiplier);\n    Derived a_to_p3 = a;\n    a_to_p3.FrobeniusMapInPlace(3 * index_multiplier);\n    a_to_r_minus_1 *= (a_to_p2 * a_to_p3);\n\n    // Since aʳ, which is |Norm()|, is in the base field, computing the constant\n    // is enough.\n    BaseField a_to_r = BaseField::Zero();\n    a_to_r += a.c1_ * a_to_r_minus_1.c3_;\n    a_to_r += a.c2_ * a_to_r_minus_1.c2_;\n    a_to_r += a.c3_ * a_to_r_minus_1.c1_;\n    a_to_r = Config::MulByNonResidue(a_to_r);\n    a_to_r += a.c0_ * a_to_r_minus_1.c0_;\n\n    // a⁻¹ = aʳ⁻¹ * a⁻ʳ\n    b = a_to_r_minus_1 * *a_to_r.Inverse();\n    return true;\n  }\n\n  // c = c0_ + c1_ * X + c2_ * X² + c3_ * X³\n  BaseField c0_;\n  BaseField c1_;\n  BaseField c2_;\n  BaseField c3_;\n\n  static BaseField kInv3;\n  static BaseField kInv4;\n  static BaseField kInv6;\n  static BaseField kInv12;\n  static BaseField kInv20;\n  static BaseField kInv24;\n  static BaseField kInv30;\n  static BaseField kInv120;\n  static BaseField kNeg5;\n  static BaseField kNegInv2;\n  static BaseField kNegInv3;\n  static BaseField kNegInv4;\n  static BaseField kNegInv6;\n  static BaseField kNegInv12;\n  static BaseField kNegInv24;\n  static BaseField kNegInv120;\n};\n\n#define ADD_STATIC_MEMBER(name)                      \\\n  template <typename Derived>                        \\\n  typename QuarticExtensionField<Derived>::BaseField \\\n      QuarticExtensionField<Derived>::name\n\nADD_STATIC_MEMBER(kInv3);\nADD_STATIC_MEMBER(kInv4);\nADD_STATIC_MEMBER(kInv6);\nADD_STATIC_MEMBER(kInv12);\nADD_STATIC_MEMBER(kInv20);\nADD_STATIC_MEMBER(kInv24);\nADD_STATIC_MEMBER(kInv30);\nADD_STATIC_MEMBER(kInv120);\nADD_STATIC_MEMBER(kNeg5);\nADD_STATIC_MEMBER(kNegInv2);\nADD_STATIC_MEMBER(kNegInv3);\nADD_STATIC_MEMBER(kNegInv4);\nADD_STATIC_MEMBER(kNegInv6);\nADD_STATIC_MEMBER(kNegInv12);\nADD_STATIC_MEMBER(kNegInv24);\nADD_STATIC_MEMBER(kNegInv120);\n\n#undef ADD_STATIC_MEMBER\n\ntemplate <\n    typename BaseField, typename Derived,\n    std::enable_if_t<std::is_same_v<BaseField, typename Derived::BaseField>>* =\n        nullptr>\nDerived operator*(const BaseField& element,\n                  const QuarticExtensionField<Derived>& f) {\n  return static_cast<const Derived&>(f) * element;\n}\n\ntemplate <typename H, typename Derived>\nH AbslHashValue(H h, const QuarticExtensionField<Derived>& f) {\n  return H::combine(std::move(h), f.c0(), f.c1(), f.c2(), f.c3());\n}\n\n}  // namespace math\n\nnamespace base {\n\ntemplate <typename Derived>\nclass Copyable<Derived, std::enable_if_t<std::is_base_of_v<\n                            math::QuarticExtensionField<Derived>, Derived>>> {\n public:\n  static bool WriteTo(\n      const math::QuarticExtensionField<Derived>& quadratic_extension_field,\n      Buffer* buffer) {\n    return buffer->WriteMany(\n        quadratic_extension_field.c0(), quadratic_extension_field.c1(),\n        quadratic_extension_field.c2(), quadratic_extension_field.c3());\n  }\n\n  static bool ReadFrom(\n      const ReadOnlyBuffer& buffer,\n      math::QuarticExtensionField<Derived>* quadratic_extension_field) {\n    typename Derived::BaseField c0;\n    typename Derived::BaseField c1;\n    typename Derived::BaseField c2;\n    typename Derived::BaseField c3;\n    if (!buffer.ReadMany(&c0, &c1, &c2, &c3)) return false;\n\n    *quadratic_extension_field = math::QuarticExtensionField<Derived>(\n        std::move(c0), std::move(c1), std::move(c2), std::move(c3));\n    return true;\n  }\n\n  static size_t EstimateSize(\n      const math::QuarticExtensionField<Derived>& quadratic_extension_field) {\n    return base::EstimateSize(\n        quadratic_extension_field.c0(), quadratic_extension_field.c1(),\n        quadratic_extension_field.c2(), quadratic_extension_field.c3());\n  }\n};\n\ntemplate <typename Derived>\nclass RapidJsonValueConverter<\n    Derived, std::enable_if_t<std::is_base_of_v<\n                 math::QuarticExtensionField<Derived>, Derived>>> {\n public:\n  using BaseField = typename math::QuarticExtensionField<Derived>::BaseField;\n\n  template <typename Allocator>\n  static rapidjson::Value From(\n      const math::QuarticExtensionField<Derived>& value, Allocator& allocator) {\n    rapidjson::Value object(rapidjson::kObjectType);\n    AddJsonElement(object, \"c0\", value.c0(), allocator);\n    AddJsonElement(object, \"c1\", value.c1(), allocator);\n    AddJsonElement(object, \"c2\", value.c2(), allocator);\n    AddJsonElement(object, \"c3\", value.c3(), allocator);\n    return object;\n  }\n\n  static bool To(const rapidjson::Value& json_value, std::string_view key,\n                 math::QuarticExtensionField<Derived>* value,\n                 std::string* error) {\n    BaseField c0;\n    BaseField c1;\n    BaseField c2;\n    BaseField c3;\n    if (!ParseJsonElement(json_value, \"c0\", &c0, error)) return false;\n    if (!ParseJsonElement(json_value, \"c1\", &c1, error)) return false;\n    if (!ParseJsonElement(json_value, \"c2\", &c2, error)) return false;\n    if (!ParseJsonElement(json_value, \"c3\", &c3, error)) return false;\n    *value = math::QuarticExtensionField<Derived>(std::move(c0), std::move(c1),\n                                                  std::move(c2), std::move(c3));\n    return true;\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_QUARTIC_EXTENSION_FIELD_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/quartic_extension_field_unittest.cc",
    "content": "#include <optional>\n\n#include \"absl/hash/hash_testing.h\"\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/finite_fields/baby_bear/baby_bear4.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\ntemplate <typename FieldType>\nclass QuarticExtensionFieldTest : public FiniteFieldTest<FieldType> {};\n\n}  // namespace\n\nusing FieldTypes = testing::Types<BabyBear4, PackedBabyBear4>;\nTYPED_TEST_SUITE(QuarticExtensionFieldTest, FieldTypes);\n\nTYPED_TEST(QuarticExtensionFieldTest, Zero) {\n  using F4 = TypeParam;\n\n  EXPECT_TRUE(F4::Zero().IsZero());\n  EXPECT_FALSE(F4::One().IsZero());\n  EXPECT_FALSE(F4::MinusOne().IsZero());\n}\n\nTYPED_TEST(QuarticExtensionFieldTest, One) {\n  using F4 = TypeParam;\n\n  EXPECT_TRUE(F4::One().IsOne());\n  EXPECT_FALSE(F4::Zero().IsOne());\n  EXPECT_FALSE(F4::MinusOne().IsOne());\n}\n\nTYPED_TEST(QuarticExtensionFieldTest, MinusOne) {\n  using F4 = TypeParam;\n\n  EXPECT_TRUE(F4::MinusOne().IsMinusOne());\n  EXPECT_FALSE(F4::Zero().IsMinusOne());\n  EXPECT_FALSE(F4::One().IsMinusOne());\n}\n\nTYPED_TEST(QuarticExtensionFieldTest, TwoInv) {\n  using F4 = TypeParam;\n  using F = typename ExtensionFieldTraits<F4>::BaseField;\n\n  EXPECT_TRUE((F4::TwoInv() * F4(F(2))).IsOne());\n  EXPECT_FALSE((F4::TwoInv() * F4::One()).IsOne());\n}\n\nTYPED_TEST(QuarticExtensionFieldTest, Random) {\n  using F4 = TypeParam;\n\n  bool success = false;\n  F4 r = F4::Random();\n  for (size_t i = 0; i < 100; ++i) {\n    if (r != F4::Random()) {\n      success = true;\n      break;\n    }\n  }\n  EXPECT_TRUE(success);\n}\n\nTYPED_TEST(QuarticExtensionFieldTest, Norm) {\n  using F4 = TypeParam;\n\n  constexpr static uint32_t kModulus = BabyBear::Config::kModulus;\n  F4 r = F4::Random();\n  F4 r_to_p = r.Pow(kModulus);\n  F4 r_to_p2 = r_to_p.Pow(kModulus);\n  F4 r_to_p3 = r_to_p2.Pow(kModulus);\n  EXPECT_EQ(r.Norm(), (r * r_to_p * r_to_p2 * r_to_p3).c0());\n}\n\nTYPED_TEST(QuarticExtensionFieldTest, EqualityOperators) {\n  using F4 = TypeParam;\n  using F = typename F4::BaseField;\n\n  F4 f(F(3), F(4), F(5), F(6));\n  F4 f2(F(4), F(4), F(5), F(6));\n  EXPECT_NE(f, f2);\n  F4 f3(F(4), F(3), F(5), F(6));\n  EXPECT_NE(f2, f3);\n\n  F4 f4(F(3), F(4), F(5), F(7));\n  EXPECT_NE(f, f4);\n\n  F4 f5(F(3), F(4), F(5), F(6));\n  EXPECT_EQ(f, f5);\n}\n\nTYPED_TEST(QuarticExtensionFieldTest, ComparisonOperator) {\n  using F4 = TypeParam;\n  using F = typename F4::BaseField;\n\n  if constexpr (std::is_same_v<F4, PackedBabyBear4>) {\n    GTEST_SKIP();\n  } else {\n    F4 f(F(3), F(4), F(5), F(6));\n    F4 f2(F(4), F(4), F(5), F(6));\n    EXPECT_LT(f, f2);\n    EXPECT_LE(f, f2);\n    EXPECT_GT(f2, f);\n    EXPECT_GE(f2, f);\n\n    F4 f3(F(4), F(3), F(5), F(6));\n    F4 f4(F(3), F(4), F(5), F(6));\n    EXPECT_LT(f3, f4);\n    EXPECT_LE(f3, f4);\n    EXPECT_GT(f4, f3);\n    EXPECT_GE(f4, f3);\n\n    F4 f5(F(4), F(5), F(6), F(3));\n    F4 f6(F(3), F(2), F(6), F(5));\n    EXPECT_LT(f5, f6);\n    EXPECT_LE(f5, f6);\n    EXPECT_GT(f6, f5);\n    EXPECT_GE(f6, f5);\n  }\n}\n\nTYPED_TEST(QuarticExtensionFieldTest, AdditiveOperators) {\n  using F4 = TypeParam;\n  using F = typename F4::BaseField;\n\n  struct {\n    F4 a;\n    F4 b;\n    F4 sum;\n    F4 amb;\n    F4 bma;\n  } tests[] = {\n      {\n          {F(1), F(2), F(3), F(4)},\n          {F(3), F(5), F(6), F(8)},\n          {F(4), F(7), F(9), F(12)},\n          {-F(2), -F(3), -F(3), -F(4)},\n          {F(2), F(3), F(3), F(4)},\n      },\n  };\n\n  for (const auto& test : tests) {\n    EXPECT_EQ(test.a + test.b, test.sum);\n    EXPECT_EQ(test.b + test.a, test.sum);\n    EXPECT_EQ(test.a - test.b, test.amb);\n    EXPECT_EQ(test.b - test.a, test.bma);\n\n    F4 tmp = test.a;\n    tmp += test.b;\n    EXPECT_EQ(tmp, test.sum);\n    tmp -= test.b;\n    EXPECT_EQ(tmp, test.a);\n  }\n}\n\nTYPED_TEST(QuarticExtensionFieldTest, AdditiveGroupOperators) {\n  using F4 = TypeParam;\n  using F = typename F4::BaseField;\n\n  F4 f(F(3), F(4), F(5), F(6));\n  F4 f_neg(-F(3), -F(4), -F(5), -F(6));\n  EXPECT_EQ(-f, f_neg);\n  f.NegateInPlace();\n  EXPECT_EQ(f, f_neg);\n\n  f = F4(F(3), F(4), F(5), F(6));\n  F4 f_dbl(F(6), F(8), F(10), F(12));\n  EXPECT_EQ(f.Double(), f_dbl);\n  f.DoubleInPlace();\n  EXPECT_EQ(f, f_dbl);\n}\n\nTYPED_TEST(QuarticExtensionFieldTest, MultiplicativeOperators) {\n  using F4 = TypeParam;\n  using F = typename F4::BaseField;\n\n  struct {\n    F4 a;\n    F4 b;\n    F4 mul;\n    F4 adb;\n    F4 bda;\n  } tests[] = {\n      {\n          {F(1), F(2), F(3), F(4)},\n          {F(3), F(5), F(6), F(8)},\n          {F(597), F(539), F(377), F(47)},\n          {F(1144494179), F(1502926259), F(1509084158), F(151175067)},\n          {F(653096429), F(494869942), F(67683040), F(1807436149)},\n      },\n  };\n\n  for (const auto& test : tests) {\n    EXPECT_EQ(test.a * test.b, test.mul);\n    EXPECT_EQ(test.b * test.a, test.mul);\n    EXPECT_EQ(test.a / test.b, test.adb);\n    EXPECT_EQ(test.b / test.a, test.bda);\n\n    F4 tmp = test.a;\n    tmp *= test.b;\n    EXPECT_EQ(tmp, test.mul);\n    ASSERT_TRUE(tmp /= test.b);\n    EXPECT_EQ(tmp, test.a);\n  }\n}\n\nTYPED_TEST(QuarticExtensionFieldTest, MultiplicativeOperators2) {\n  using F4 = TypeParam;\n  using F = typename F4::BaseField;\n\n  F4 f(F(3), F(4), F(5), F(6));\n  F4 f_mul(F(6), F(8), F(10), F(12));\n  EXPECT_EQ(f * F(2), f_mul);\n  f *= F(2);\n  EXPECT_EQ(f, f_mul);\n}\n\nTYPED_TEST(QuarticExtensionFieldTest, MultiplicativeGroupOperators) {\n  using F4 = TypeParam;\n  using F = typename F4::BaseField;\n\n  F4 f = F4::Random();\n  std::optional<F4> f_inv = f.Inverse();\n  if (UNLIKELY(f.IsZero())) {\n    ASSERT_FALSE(f_inv);\n    ASSERT_FALSE(f.InverseInPlace());\n  } else {\n    EXPECT_EQ(f * *f_inv, F4::One());\n    F4 f_tmp = f;\n    EXPECT_EQ(**f.InverseInPlace() * f_tmp, F4::One());\n  }\n\n  f = F4(F(3), F(4), F(5), F(6));\n  F4 f_sqr = F4(F(812), F(684), F(442), F(76));\n  EXPECT_EQ(f.Square(), f_sqr);\n  f.SquareInPlace();\n  EXPECT_EQ(f, f_sqr);\n}\n\nTYPED_TEST(QuarticExtensionFieldTest, JsonValueConverter) {\n  using F4 = TypeParam;\n  using F = typename F4::BaseField;\n\n  // TODO(chokobole): Enable this test.\n  if constexpr (std::is_same_v<F4, PackedBabyBear4>) {\n    GTEST_SKIP();\n  } else {\n    F4 expected_point(F(1), F(2), F(3), F(4));\n    std::string expected_json = R\"({\"c0\":1,\"c1\":2,\"c2\":3,\"c3\":4})\";\n\n    F4 p;\n    std::string error;\n    ASSERT_TRUE(base::ParseJson(expected_json, &p, &error));\n    ASSERT_TRUE(error.empty());\n    EXPECT_EQ(p, expected_point);\n\n    std::string json = base::WriteToJson(p);\n    EXPECT_EQ(json, expected_json);\n  }\n}\n\nTYPED_TEST(QuarticExtensionFieldTest, Hash) {\n  using F4 = TypeParam;\n\n  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(\n      std::make_tuple(F4::Zero(), F4::One(), F4::Random())));\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/finite_fields/small_prime_field.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_FINITE_FIELDS_SMALL_PRIME_FIELD_H_\n#define TACHYON_MATH_FINITE_FIELDS_SMALL_PRIME_FIELD_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <optional>\n#include <string>\n\n#include \"absl/base/call_once.h\"\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/random.h\"\n#include \"tachyon/base/strings/string_number_conversions.h\"\n#include \"tachyon/base/strings/string_util.h\"\n#include \"tachyon/build/build_config.h\"\n#include \"tachyon/math/base/egcd.h\"\n#include \"tachyon/math/finite_fields/prime_field_base.h\"\n\nnamespace tachyon::math {\n\n// A prime field is finite field GF(p) where p is a prime number.\ntemplate <typename _Config>\nclass PrimeField<_Config, std::enable_if_t<(_Config::kModulusBits <= 32) &&\n                                           !_Config::kUseMontgomery>>\n    final : public PrimeFieldBase<PrimeField<_Config>> {\n public:\n  constexpr static size_t kModulusBits = _Config::kModulusBits;\n  constexpr static size_t kLimbNums = (kModulusBits + 63) / 64;\n  constexpr static size_t N = kLimbNums;\n\n  using Config = _Config;\n  using BigIntTy = BigInt<N>;\n  using value_type = uint32_t;\n\n  using CpuField = PrimeField<Config>;\n  using GpuField = PrimeField<Config>;\n\n  constexpr PrimeField() = default;\n  constexpr explicit PrimeField(uint32_t value) : value_(value) {\n    DCHECK_LT(value_, GetModulus());\n  }\n  constexpr explicit PrimeField(BigInt<N> value) : PrimeField(value[0]) {\n    DCHECK_LT(value[0], GetModulus());\n  }\n  constexpr PrimeField(const PrimeField& other) = default;\n  constexpr PrimeField& operator=(const PrimeField& other) = default;\n  constexpr PrimeField(PrimeField&& other) = default;\n  constexpr PrimeField& operator=(PrimeField&& other) = default;\n\n  constexpr static PrimeField Zero() { return PrimeField(); }\n  constexpr static PrimeField One() { return PrimeField(1); }\n  constexpr static PrimeField MinusOne() {\n    return PrimeField(GetModulus() - 1);\n  }\n  constexpr static PrimeField TwoInv() { return PrimeField(Config::kTwoInv); }\n\n  static PrimeField Random() {\n    return PrimeField(\n        base::Uniform(base::Range<uint32_t>::Until(GetModulus())));\n  }\n\n  static std::optional<PrimeField> FromDecString(std::string_view str) {\n    uint64_t value;\n    if (!base::StringToUint64(str, &value)) return std::nullopt;\n    if (value >= uint64_t{GetModulus()}) {\n      LOG(ERROR) << \"value(\" << str << \") is greater than or equal to modulus\";\n      return std::nullopt;\n    }\n    return PrimeField(value);\n  }\n  static std::optional<PrimeField> FromHexString(std::string_view str) {\n    uint64_t value;\n    if (!base::HexStringToUint64(str, &value)) return std::nullopt;\n    if (value >= uint64_t{GetModulus()}) {\n      LOG(ERROR) << \"value(\" << str << \") is greater than or equal to modulus\";\n      return std::nullopt;\n    }\n    return PrimeField(value);\n  }\n\n  constexpr static PrimeField FromBigInt(BigInt<N> big_int) {\n    return PrimeField(big_int);\n  }\n\n  static void Init() {\n    static absl::once_flag once;\n    absl::call_once(once, []() {\n      Config::Init();\n      VLOG(1) << Config::kName << \" initialized\";\n    });\n  }\n\n  constexpr value_type value() const { return value_; }\n\n  constexpr bool IsZero() const { return value_ == 0; }\n\n  constexpr bool IsOne() const { return value_ == 1; }\n\n  constexpr bool IsMinusOne() const { return value_ == GetModulus() - 1; }\n\n  std::string ToString() const { return base::NumberToString(value_); }\n\n  std::string ToHexString(bool pad_zero = false) const {\n    std::string str = base::HexToString(value_);\n    if (pad_zero) {\n      str = base::ToHexStringWithLeadingZero(str, 8);\n    }\n    return base::MaybePrepend0x(str);\n  }\n\n  // TODO(chokobole): Support bigendian.\n  constexpr BigInt<N> ToBigInt() const { return BigInt<N>(value_); }\n\n  constexpr bool operator==(PrimeField other) const {\n    return value_ == other.value_;\n  }\n  constexpr bool operator!=(PrimeField other) const {\n    return value_ != other.value_;\n  }\n  constexpr bool operator<(PrimeField other) const {\n    return value_ < other.value_;\n  }\n  constexpr bool operator>(PrimeField other) const {\n    return value_ > other.value_;\n  }\n  constexpr bool operator<=(PrimeField other) const {\n    return value_ <= other.value_;\n  }\n  constexpr bool operator>=(PrimeField other) const {\n    return value_ >= other.value_;\n  }\n\n  // AdditiveSemigroup methods\n  constexpr PrimeField Add(PrimeField other) const {\n    return PrimeField(Config::AddMod(value_, other.value_));\n  }\n\n  constexpr PrimeField& AddInPlace(PrimeField other) {\n    value_ = Config::AddMod(value_, other.value_);\n    return *this;\n  }\n\n  // AdditiveGroup methods\n  constexpr PrimeField Sub(PrimeField other) const {\n    return PrimeField(Config::SubMod(value_, other.value_));\n  }\n\n  constexpr PrimeField& SubInPlace(PrimeField other) {\n    value_ = Config::SubMod(value_, other.value_);\n    return *this;\n  }\n\n  constexpr PrimeField Negate() const {\n    return PrimeField(Config::SubMod(0, value_));\n  }\n\n  constexpr PrimeField& NegateInPlace() {\n    value_ = Config::SubMod(0, value_);\n    return *this;\n  }\n\n  // MultiplicativeSemigroup methods\n  constexpr PrimeField Mul(PrimeField other) const {\n// NOTE(chokobole): g++ 11.4.0 gives a warning. It seems to be a g++ bug.\n#if defined(COMPILER_GCC) && !defined(__clang__)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wmaybe-uninitialized\"\n#endif\n    return PrimeField(Config::Reduce(uint64_t{value_} * other.value_));\n#if defined(COMPILER_GCC) && !defined(__clang__)\n#pragma GCC diagnostic pop\n#endif\n  }\n\n  constexpr PrimeField& MulInPlace(PrimeField other) {\n    value_ = Config::Reduce(uint64_t{value_} * other.value_);\n    return *this;\n  }\n\n  // MultiplicativeGroup methods\n  constexpr std::optional<PrimeField> Inverse() const {\n    // TODO(chokobole): Benchmark between this and |this->Pow(GetModulus() - 2)|\n    // and use the faster one.\n    // |result.s| * |value_| + |result.t| * |GetModulus()| = |result.r|\n    // |result.s| * |value_| = |result.r| (mod |GetModulus()|)\n    if (UNLIKELY(IsZero())) {\n      LOG_IF_NOT_GPU(ERROR) << \"Inverse of zero attempted\";\n      return std::nullopt;\n    }\n    EGCD<int64_t>::Result result = EGCD<int64_t>::Compute(value_, GetModulus());\n    if (UNLIKELY(result.r != 1)) {\n      LOG_IF_NOT_GPU(ERROR) << \"result.r != 1\";\n      return std::nullopt;\n    }\n    if (result.s > 0) {\n      return PrimeField(result.s);\n    } else {\n      return PrimeField(int64_t{GetModulus()} + result.s);\n    }\n  }\n\n  [[nodiscard]] constexpr std::optional<PrimeField*> InverseInPlace() {\n    // See comment in |Inverse()|.\n    if (UNLIKELY(IsZero())) {\n      LOG_IF_NOT_GPU(ERROR) << \"Inverse of zero attempted\";\n      return std::nullopt;\n    }\n    EGCD<int64_t>::Result result = EGCD<int64_t>::Compute(value_, GetModulus());\n    if (UNLIKELY(result.r != 1)) {\n      LOG_IF_NOT_GPU(ERROR) << \"result.r != 1\";\n      return std::nullopt;\n    }\n    if (result.s > 0) {\n      value_ = result.s;\n    } else {\n      value_ = int64_t{GetModulus()} + result.s;\n    }\n    return this;\n  }\n\n private:\n  constexpr static uint32_t GetModulus() { return Config::kModulus; }\n\n  uint32_t value_;\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_SMALL_PRIME_FIELD_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/small_prime_field_mont.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_FINITE_FIELDS_SMALL_PRIME_FIELD_MONT_H_\n#define TACHYON_MATH_FINITE_FIELDS_SMALL_PRIME_FIELD_MONT_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <optional>\n#include <string>\n\n#include \"absl/base/call_once.h\"\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/random.h\"\n#include \"tachyon/base/strings/string_number_conversions.h\"\n#include \"tachyon/base/strings/string_util.h\"\n#include \"tachyon/build/build_config.h\"\n#include \"tachyon/math/finite_fields/prime_field_base.h\"\n\nnamespace tachyon::math {\n\n// A prime field is finite field GF(p) where p is a prime number.\ntemplate <typename _Config>\nclass PrimeField<_Config, std::enable_if_t<(_Config::kModulusBits <= 32) &&\n                                           _Config::kUseMontgomery>>\n    final : public PrimeFieldBase<PrimeField<_Config>> {\n public:\n  constexpr static size_t kModulusBits = _Config::kModulusBits;\n  constexpr static size_t kLimbNums = (kModulusBits + 63) / 64;\n  constexpr static size_t N = kLimbNums;\n\n  using Config = _Config;\n  using BigIntTy = BigInt<N>;\n  using value_type = uint32_t;\n\n  using CpuField = PrimeField<Config>;\n  using GpuField = PrimeField<Config>;\n\n  constexpr PrimeField() = default;\n  constexpr explicit PrimeField(uint32_t value)\n      : value_(Config::ToMontgomery(value)) {\n    DCHECK_LT(value, GetModulus());\n  }\n  constexpr explicit PrimeField(BigInt<N> value) : PrimeField(value[0]) {\n    DCHECK_LT(value[0], GetModulus());\n  }\n  constexpr PrimeField(const PrimeField& other) = default;\n  constexpr PrimeField& operator=(const PrimeField& other) = default;\n  constexpr PrimeField(PrimeField&& other) = default;\n  constexpr PrimeField& operator=(PrimeField&& other) = default;\n\n  constexpr static PrimeField Zero() { return PrimeField(); }\n  constexpr static PrimeField One() {\n    PrimeField ret{};\n    ret.value_ = Config::kOne;\n    return ret;\n  }\n  constexpr static PrimeField MinusOne() {\n    PrimeField ret{};\n    ret.value_ = Config::kMinusOne;\n    return ret;\n  }\n\n  constexpr static PrimeField TwoInv() {\n    PrimeField ret{};\n    ret.value_ = Config::kTwoInv;\n    return ret;\n  }\n\n  static PrimeField Random() {\n    return PrimeField(\n        base::Uniform(base::Range<uint32_t>::Until(GetModulus())));\n  }\n\n  static std::optional<PrimeField> FromDecString(std::string_view str) {\n    uint64_t value;\n    if (!base::StringToUint64(str, &value)) return std::nullopt;\n    if (value >= uint64_t{GetModulus()}) {\n      LOG(ERROR) << \"value(\" << str << \") is greater than or equal to modulus\";\n      return std::nullopt;\n    }\n    return PrimeField(value);\n  }\n  static std::optional<PrimeField> FromHexString(std::string_view str) {\n    uint64_t value;\n    if (!base::HexStringToUint64(str, &value)) return std::nullopt;\n    if (value >= uint64_t{GetModulus()}) {\n      LOG(ERROR) << \"value(\" << str << \") is greater than or equal to modulus\";\n      return std::nullopt;\n    }\n    return PrimeField(value);\n  }\n\n  constexpr static PrimeField FromBigInt(BigInt<N> big_int) {\n    return PrimeField(big_int);\n  }\n\n  constexpr static PrimeField FromMontgomery(const uint32_t value) {\n    PrimeField ret{};\n    ret.value_ = value;\n    return ret;\n  }\n\n  static void Init() {\n    static absl::once_flag once;\n    absl::call_once(once, []() {\n      Config::Init();\n      VLOG(1) << Config::kName << \" initialized\";\n    });\n  }\n\n  constexpr value_type value() const { return value_; }\n\n  constexpr bool IsZero() const { return value_ == 0; }\n\n  constexpr bool IsOne() const { return value_ == Config::kOne; }\n\n  constexpr bool IsMinusOne() const { return value_ == Config::kMinusOne; }\n\n  std::string ToString() const {\n    return base::NumberToString(Config::FromMontgomery(value_));\n  }\n\n  std::string ToHexString(bool pad_zero = false) const {\n    std::string str = base::HexToString(Config::FromMontgomery(value_));\n    if (pad_zero) {\n      str = base::ToHexStringWithLeadingZero(str, 8);\n    }\n    return base::MaybePrepend0x(str);\n  }\n\n  // TODO(chokobole): Support bigendian.\n  constexpr BigInt<N> ToBigInt() const {\n    return BigInt<N>(Config::FromMontgomery(value_));\n  }\n\n  constexpr bool operator==(PrimeField other) const {\n    return value_ == other.value_;\n  }\n  constexpr bool operator!=(PrimeField other) const {\n    return value_ != other.value_;\n  }\n  constexpr bool operator<(PrimeField other) const {\n    return Config::FromMontgomery(value_) <\n           Config::FromMontgomery(other.value_);\n  }\n  constexpr bool operator>(PrimeField other) const {\n    return Config::FromMontgomery(value_) >\n           Config::FromMontgomery(other.value_);\n  }\n  constexpr bool operator<=(PrimeField other) const {\n    return Config::FromMontgomery(value_) <=\n           Config::FromMontgomery(other.value_);\n  }\n  constexpr bool operator>=(PrimeField other) const {\n    return Config::FromMontgomery(value_) >=\n           Config::FromMontgomery(other.value_);\n  }\n\n  // AdditiveSemigroup methods\n  constexpr PrimeField Add(PrimeField other) const {\n    PrimeField ret{};\n    ret.value_ = Config::AddMod(value_, other.value_);\n    return ret;\n  }\n\n  constexpr PrimeField& AddInPlace(PrimeField other) {\n    value_ = Config::AddMod(value_, other.value_);\n    return *this;\n  }\n\n  // AdditiveGroup methods\n  constexpr PrimeField Sub(PrimeField other) const {\n    PrimeField ret{};\n    ret.value_ = Config::SubMod(value_, other.value_);\n    return ret;\n  }\n\n  constexpr PrimeField& SubInPlace(PrimeField other) {\n    value_ = Config::SubMod(value_, other.value_);\n    return *this;\n  }\n\n  constexpr PrimeField Negate() const {\n    PrimeField ret{};\n    ret.value_ = Config::SubMod(0, value_);\n    return ret;\n  }\n\n  constexpr PrimeField& NegateInPlace() {\n    value_ = Config::SubMod(0, value_);\n    return *this;\n  }\n\n  // MultiplicativeSemigroup methods\n  constexpr PrimeField Mul(PrimeField other) const {\n    PrimeField ret{};\n    ret.value_ = Config::FromMontgomery(uint64_t{value_} * other.value_);\n    return ret;\n  }\n\n  constexpr PrimeField& MulInPlace(PrimeField other) {\n    value_ = Config::FromMontgomery(uint64_t{value_} * other.value_);\n    return *this;\n  }\n\n  // MultiplicativeGroup methods\n  constexpr std::optional<PrimeField> Inverse() const {\n    if (UNLIKELY(IsZero())) {\n      LOG_IF_NOT_GPU(ERROR) << \"Inverse of zero attempted\";\n      return std::nullopt;\n    }\n    return this->Pow(GetModulus() - 2);\n  }\n\n  [[nodiscard]] constexpr std::optional<PrimeField*> InverseInPlace() {\n    if (UNLIKELY(IsZero())) {\n      LOG_IF_NOT_GPU(ERROR) << \"Inverse of zero attempted\";\n      return std::nullopt;\n    }\n    *this = this->Pow(GetModulus() - 2);\n    return this;\n  }\n\n private:\n  constexpr static uint32_t GetModulus() { return Config::kModulus; }\n\n  uint32_t value_;\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_SMALL_PRIME_FIELD_MONT_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/square_root_algorithms/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//tachyon/math/finite_fields:__pkg__\"])\n\ntachyon_cc_library(\n    name = \"square_root_algorithms\",\n    hdrs = [\n        \"shanks.h\",\n        \"tonelli_shanks.h\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/math/finite_fields/square_root_algorithms/shanks.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_FINITE_FIELDS_SQUARE_ROOT_ALGORITHMS_SHANKS_H_\n#define TACHYON_MATH_FINITE_FIELDS_SQUARE_ROOT_ALGORITHMS_SHANKS_H_\n\n#include <utility>\n\nnamespace tachyon::math {\n\ntemplate <typename F>\nconstexpr bool ComputeShanksSquareRoot(const F& a, F* ret) {\n  // https://eprint.iacr.org/2012/685.pdf (page 9, algorithm 2)\n  // clang-format off\n  // a² = b\n  // a⁴ = b²\n  //    = b^(p+1) (since b^(p-1) = 1, See https://en.wikipedia.org/wiki/Fermat%27s_little_theorem)\n  // a  = b^((p + 1) / 4)\n  // clang-format on\n  F sqrt = a.Pow(F::Config::kModulusPlusOneDivFour);\n  if (sqrt.Square() == a) {\n    *ret = std::move(sqrt);\n    return true;\n  }\n  return false;\n}\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_SQUARE_ROOT_ALGORITHMS_SHANKS_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/square_root_algorithms/tonelli_shanks.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_FINITE_FIELDS_SQUARE_ROOT_ALGORITHMS_TONELLI_SHANKS_H_\n#define TACHYON_MATH_FINITE_FIELDS_SQUARE_ROOT_ALGORITHMS_TONELLI_SHANKS_H_\n\n#include <utility>\n\nnamespace tachyon::math {\n\ntemplate <typename F>\nconstexpr bool ComputeTonelliShanksSquareRoot(\n    const F& a, const F& quadratic_non_residue_to_trace, F* ret) {\n  // Fins x such that x² = a.\n  // Here. modulus M is 2ˢ * T + 1. (where s is two adicity and T is trace).\n  // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5)\n  if (a.IsZero()) {\n    *ret = F::Zero();\n    return true;\n  }\n\n  // Note that if a has a square root(in other words, a is a quadratic residue),\n  // a^((M - 1) / 2) = 1 by Euler's criterion,\n  // See https://en.wikipedia.org/wiki/Euler%27s_criterion\n  // a^((M - 1) / 2) = a^(2ˢ⁻¹ * T) = (aᵀ)^(2ˢ⁻¹) = 1\n  // aᵀ is 1 or 2ˢ⁻¹-th root of unity.\n\n  // If we try\n  // aᵀ * a = (a^((T + 1) / 2))^2\n  // and if aᵀ is 1, then we can say the square root of a is a^((T + 1) / 2).\n  F w = a.Pow(F::Config::kTraceMinusOneDivTwo);\n  // x = aw = a^((T + 1) / 2)\n  F x = w * a;\n  // b = xw = aᵀ\n  F b = x * w;\n\n  if (!b.IsOne()) {\n    // Otherwise, let's find a pair of x and b such that it satisfies\n    // 1) x² = a * b\n    // 2) b is 2ᵏ⁻¹-th root of unity. (1st iteration: b = aᵀ and k = s)\n    // until b is 1.\n\n    // z = cᵀ (where c is a non quadratic residue).\n    // z^(2ᵛ⁻¹) = c^(2ᵛ⁻¹ * T) = c^((2ᵛ * T) / 2) = c^((M - 1) / 2) = -1\n    // (since v = s)\n    F z = quadratic_non_residue_to_trace;\n    // v = s\n    size_t v = size_t{F::Config::kTwoAdicity};\n    do {\n      size_t k = 0;\n\n      // Find least integer k >= 0 such that b^(2ᵏ) = 1.\n      F b2k = b;\n      while (!b2k.IsOne()) {\n        // invariant: b2k = b^(2ᵏ) after entering this loop\n        b2k.SquareInPlace();\n        ++k;\n      }\n\n      if (k == size_t{F::Config::kTwoAdicity}) {\n        // We are in the case where a^(2ˢ * T) = xᴹ⁻¹ = 1,\n        // which means that no square root exists.\n        return false;\n      }\n\n      size_t j = v - k;\n      // w = z^(2ᵛ⁻ᵏ⁻¹)\n      w = z;\n      for (size_t i = 1; i < j; ++i) {\n        w.SquareInPlace();\n      }\n\n      // clang-format off\n      // We have to find w and we replace x and b with xw and b * w²\n      // This holds 1) because:\n      // (x')² = (xw)² = x² * w² = a * b * w² = a * b'\n      // This also holds 2) because:\n      // (b')^(2ᵏ⁻¹) = b^(2ᵏ⁻¹) * (w²)^(2ᵏ⁻¹)\n      //\n      //   a) b^(2ᵏ⁻¹) = -1 because:\n      //      b^(2ᵏ) = 1\n      //      (b^(2ᵏ⁻¹) - 1) * (b^(2ᵏ⁻¹) + 1) = 0\n      //      b^(2ᵏ⁻¹) = -1 (b^(2ᵏ⁻¹) can't be 1(since b is 2ᵏth root of unity)) <- Halving lemma\n      //\n      //   b) w²^(2ᵏ⁻¹) = -1 because:\n      //      ((z^(2ᵛ⁻ᵏ⁻¹))^2)^(2ᵏ⁻¹) = (z^(2ᵛ⁻ᵏ))^(2ᵏ⁻¹) = z^(2ᵛ⁻¹) = -1 (See above why)\n      //\n      // Therefore, b' is 2ᵏ⁻¹ th root of unity.\n      // clang-format on\n\n      // z = w²\n      z = w.Square();\n      // b = bz\n      b *= z;\n      // x = xw\n      x *= w;\n      // v = k\n      v = k;\n    } while (!b.IsOne());\n  }\n\n  if (x.Square() == a) {\n    *ret = std::move(x);\n    return true;\n  }\n  return false;\n}\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_SQUARE_ROOT_ALGORITHMS_TONELLI_SHANKS_H_\n"
  },
  {
    "path": "tachyon/math/finite_fields/test/BUILD.bazel",
    "content": "load(\"@bazel_skylib//rules:common_settings.bzl\", \"string_flag\")\nload(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\nload(\n    \"//tachyon/math/finite_fields/generator/ext_field_generator:build_defs.bzl\",\n    \"generate_fp2s\",\n    \"generate_fp3s\",\n)\nload(\n    \"//tachyon/math/finite_fields/generator/prime_field_generator:build_defs.bzl\",\n    \"SMALL_SUBGROUP_ADICITY\",\n    \"generate_fft_prime_fields\",\n)\n\npackage(default_visibility = [\"//visibility:public\"])\n\nstring_flag(\n    name = SMALL_SUBGROUP_ADICITY,\n    build_setting_default = \"3\",\n)\n\ngenerate_fft_prime_fields(\n    name = \"gf7\",\n    testonly = True,\n    class_name = \"GF7\",\n    modulus = \"7\",\n    namespace = \"tachyon::math\",\n    reduce32 = \"return static_cast<uint32_t>(v % ((1 << kModulusBits) - 1));\",\n    reduce64 = \"return static_cast<uint32_t>(static_cast<uint32_t>(v) % ((1 << kModulusBits) - 1));\",\n    subgroup_generator = \":\" + SMALL_SUBGROUP_ADICITY,\n    use_montgomery = False,\n)\n\ngenerate_fp2s(\n    name = \"gf7_2\",\n    testonly = True,\n    base_field = \"GF7\",\n    base_field_hdr = \"tachyon/math/finite_fields/test/gf7.h\",\n    class_name = \"GF7_2\",\n    is_packed = False,\n    namespace = \"tachyon::math\",\n    non_residue = [\"-1\"],\n    deps = [\":gf7\"],\n)\n\ngenerate_fp3s(\n    name = \"gf7_3\",\n    testonly = True,\n    base_field = \"GF7\",\n    base_field_hdr = \"tachyon/math/finite_fields/test/gf7.h\",\n    class_name = \"GF7_3\",\n    is_packed = False,\n    namespace = \"tachyon::math\",\n    non_residue = [\"2\"],\n    deps = [\":gf7\"],\n)\n\ntachyon_cc_library(\n    name = \"finite_field_test\",\n    testonly = True,\n    hdrs = [\"finite_field_test.h\"],\n    deps = [\"@com_google_googletest//:gtest\"],\n)\n"
  },
  {
    "path": "tachyon/math/finite_fields/test/finite_field_test.h",
    "content": "#ifndef TACHYON_MATH_FINITE_FIELDS_TEST_FINITE_FIELD_TEST_H_\n#define TACHYON_MATH_FINITE_FIELDS_TEST_FINITE_FIELD_TEST_H_\n\n#include \"gtest/gtest.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename F>\nclass FiniteFieldTest : public testing::Test {\n public:\n  static void SetUpTestSuite() { F::Init(); }\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_FINITE_FIELDS_TEST_FINITE_FIELD_TEST_H_\n"
  },
  {
    "path": "tachyon/math/geometry/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"affine_point\",\n    hdrs = [\"affine_point.h\"],\n    deps = [\":point_conversions_forward\"],\n)\n\ntachyon_cc_library(\n    name = \"curve_type\",\n    hdrs = [\"curve_type.h\"],\n)\n\ntachyon_cc_library(\n    name = \"dimensions\",\n    hdrs = [\"dimensions.h\"],\n    deps = [\n        \"//tachyon:export\",\n        \"//tachyon/base/buffer:copyable\",\n        \"//tachyon/base/json\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"jacobian_point\",\n    hdrs = [\"jacobian_point.h\"],\n    deps = [\n        \":point3\",\n        \":point_conversions_forward\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"point2\",\n    hdrs = [\"point2.h\"],\n    deps = [\n        \"//tachyon/base/buffer:copyable\",\n        \"//tachyon/base/json\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"point3\",\n    hdrs = [\"point3.h\"],\n    deps = [\n        \"//tachyon/base/buffer:copyable\",\n        \"//tachyon/base/json\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"point4\",\n    hdrs = [\"point4.h\"],\n    deps = [\n        \"//tachyon/base/buffer:copyable\",\n        \"//tachyon/base/json\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"point_conversions_forward\",\n    hdrs = [\"point_conversions_forward.h\"],\n)\n\ntachyon_cc_library(\n    name = \"point_conversions\",\n    hdrs = [\"point_conversions.h\"],\n    deps = [\n        \":affine_point\",\n        \":jacobian_point\",\n        \":point_conversions_forward\",\n        \":point_xyzz\",\n        \":projective_point\",\n        \"//tachyon/base:template_util\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"point_xyzz\",\n    hdrs = [\"point_xyzz.h\"],\n    deps = [\n        \":point4\",\n        \":point_conversions_forward\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"projective_point\",\n    hdrs = [\"projective_point.h\"],\n    deps = [\n        \":point3\",\n        \":point_conversions_forward\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"geometry_unittests\",\n    srcs = [\n        \"dimensions_unittest.cc\",\n        \"point2_unittest.cc\",\n        \"point3_unittest.cc\",\n        \"point4_unittest.cc\",\n    ],\n    deps = [\n        \":dimensions\",\n        \":point2\",\n        \":point3\",\n        \":point4\",\n        \"//tachyon/base/buffer:vector_buffer\",\n        \"//tachyon/math/finite_fields/test:gf7\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/math/geometry/affine_point.h",
    "content": "#ifndef TACHYON_MATH_GEOMETRY_AFFINE_POINT_H_\n#define TACHYON_MATH_GEOMETRY_AFFINE_POINT_H_\n\n#include <utility>\n\n#include \"tachyon/math/geometry/point_conversions_forward.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename Curve, typename SFINAE = void>\nclass AffinePoint;\n\ntemplate <typename ScalarField, typename Curve,\n          std::enable_if_t<std::is_same_v<\n              ScalarField, typename Curve::ScalarField>>* = nullptr>\nauto operator*(const ScalarField& v, const AffinePoint<Curve>& point) {\n  return point * v;\n}\n\ntemplate <typename Curve>\nstruct PointConversions<AffinePoint<Curve>, AffinePoint<Curve>> {\n  constexpr static const AffinePoint<Curve>& Convert(\n      const AffinePoint<Curve>& src_point) {\n    return src_point;\n  }\n};\n\ntemplate <typename SrcCurve, typename DstCurve>\nstruct PointConversions<AffinePoint<SrcCurve>, AffinePoint<DstCurve>,\n                        std::enable_if_t<!std::is_same_v<SrcCurve, DstCurve>>> {\n  static const AffinePoint<DstCurve>& Convert(\n      const AffinePoint<SrcCurve>& src_point) {\n    static_assert(SrcCurve::kType == DstCurve::kType);\n    return reinterpret_cast<const AffinePoint<DstCurve>&>(src_point);\n  }\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_GEOMETRY_AFFINE_POINT_H_\n"
  },
  {
    "path": "tachyon/math/geometry/curve_type.h",
    "content": "#ifndef TACHYON_MATH_GEOMETRY_CURVE_TYPE_H_\n#define TACHYON_MATH_GEOMETRY_CURVE_TYPE_H_\n\nnamespace tachyon::math {\n\nenum class CurveType {\n  kCircle,\n  kShortWeierstrass,\n  kTwistedEdwards,\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_GEOMETRY_CURVE_TYPE_H_\n"
  },
  {
    "path": "tachyon/math/geometry/dimensions.h",
    "content": "#ifndef TACHYON_MATH_GEOMETRY_DIMENSIONS_H_\n#define TACHYON_MATH_GEOMETRY_DIMENSIONS_H_\n\n#include <stddef.h>\n\n#include <string>\n\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/base/buffer/copyable.h\"\n#include \"tachyon/base/json/json.h\"\n#include \"tachyon/export.h\"\n\nnamespace tachyon {\nnamespace math {\n\n// NOTE(chokobole): |Dimensions| class is copyable, assignable, and occupies 128\n// bits per instance. Prefer to pass them by value.\nstruct TACHYON_EXPORT Dimensions {\n  size_t width = 0;\n  size_t height = 0;\n\n  constexpr Dimensions() = default;\n  constexpr Dimensions(size_t width, size_t height)\n      : width(width), height(height) {}\n\n  constexpr bool operator==(Dimensions other) const {\n    return width == other.width && height == other.height;\n  }\n\n  constexpr bool operator!=(Dimensions other) const {\n    return !operator==(other);\n  }\n\n  std::string ToString() const {\n    return absl::Substitute(\"($0, $1)\", width, height);\n  }\n};\n\n}  // namespace math\n\nnamespace base {\n\ntemplate <>\nclass Copyable<math::Dimensions> {\n public:\n  static bool WriteTo(math::Dimensions dimensions, Buffer* buffer) {\n    return buffer->WriteMany(dimensions.width, dimensions.height);\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer,\n                       math::Dimensions* dimensions) {\n    return buffer.ReadMany(&dimensions->width, &dimensions->height);\n  }\n\n  static size_t EstimateSize(math::Dimensions dimensions) {\n    return base::EstimateSize(dimensions.width, dimensions.height);\n  }\n};\n\ntemplate <>\nclass RapidJsonValueConverter<math::Dimensions> {\n public:\n  template <typename Allocator>\n  static rapidjson::Value From(math::Dimensions value, Allocator& allocator) {\n    rapidjson::Value object(rapidjson::kObjectType);\n    AddJsonElement(object, \"width\", value.width, allocator);\n    AddJsonElement(object, \"height\", value.height, allocator);\n    return object;\n  }\n\n  static bool To(const rapidjson::Value& json_value, std::string_view key,\n                 math::Dimensions* value, std::string* error) {\n    size_t width;\n    size_t height;\n    if (!ParseJsonElement(json_value, \"width\", &width, error)) return false;\n    if (!ParseJsonElement(json_value, \"height\", &height, error)) return false;\n    value->width = width;\n    value->height = height;\n    return true;\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_MATH_GEOMETRY_DIMENSIONS_H_\n"
  },
  {
    "path": "tachyon/math/geometry/dimensions_unittest.cc",
    "content": "#include \"tachyon/math/geometry/dimensions.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/buffer/vector_buffer.h\"\n\nnamespace tachyon::math {\n\nTEST(DimensionsTest, Construct) {\n  Dimensions dimensions;\n  EXPECT_EQ(dimensions.width, 0);\n  EXPECT_EQ(dimensions.height, 0);\n\n  dimensions = Dimensions(1, 2);\n  EXPECT_EQ(dimensions.width, 1);\n  EXPECT_EQ(dimensions.height, 2);\n}\n\nTEST(DimensionsTest, EqualityOperators) {\n  Dimensions dimensions(1, 2);\n  Dimensions dimensions2(4, 5);\n  EXPECT_EQ(dimensions, dimensions);\n  EXPECT_NE(dimensions, dimensions2);\n}\n\nTEST(DimensionsTest, ToString) {\n  EXPECT_EQ(Dimensions(1, 2).ToString(), \"(1, 2)\");\n}\n\nTEST(DimensionsTest, Copyable) {\n  Dimensions expected(1, 2);\n\n  base::Uint8VectorBuffer write_buf;\n  ASSERT_TRUE(write_buf.Grow(base::EstimateSize(expected)));\n  ASSERT_TRUE(write_buf.Write(expected));\n  ASSERT_TRUE(write_buf.Done());\n\n  write_buf.set_buffer_offset(0);\n\n  Dimensions value;\n  ASSERT_TRUE(write_buf.Read(&value));\n  EXPECT_EQ(expected, value);\n}\n\nTEST(DimensionsTest, JsonValueConverter) {\n  Dimensions expected_dimensions(1, 2);\n  std::string expected_json = R\"({\"width\":1,\"height\":2})\";\n\n  Dimensions dimensions;\n  std::string error;\n  ASSERT_TRUE(base::ParseJson(expected_json, &dimensions, &error));\n  ASSERT_TRUE(error.empty());\n  EXPECT_EQ(dimensions, expected_dimensions);\n\n  std::string json = base::WriteToJson(dimensions);\n  EXPECT_EQ(json, expected_json);\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/geometry/jacobian_point.h",
    "content": "#ifndef TACHYON_MATH_GEOMETRY_JACOBIAN_POINT_H_\n#define TACHYON_MATH_GEOMETRY_JACOBIAN_POINT_H_\n\n#include <utility>\n\n#include \"tachyon/math/geometry/point3.h\"\n#include \"tachyon/math/geometry/point_conversions_forward.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename Curve, typename SFINAE = void>\nclass JacobianPoint;\n\ntemplate <typename ScalarField, typename Curve,\n          std::enable_if_t<std::is_same_v<\n              ScalarField, typename Curve::ScalarField>>* = nullptr>\nJacobianPoint<Curve> operator*(const ScalarField& v,\n                               const JacobianPoint<Curve>& point) {\n  return point * v;\n}\n\ntemplate <typename Curve>\nstruct PointConversions<JacobianPoint<Curve>, JacobianPoint<Curve>> {\n  constexpr static const JacobianPoint<Curve>& Convert(\n      const JacobianPoint<Curve>& src_point) {\n    return src_point;\n  }\n};\n\ntemplate <typename SrcCurve, typename DstCurve>\nstruct PointConversions<JacobianPoint<SrcCurve>, JacobianPoint<DstCurve>,\n                        std::enable_if_t<!std::is_same_v<SrcCurve, DstCurve>>> {\n  static const JacobianPoint<DstCurve>& Convert(\n      const JacobianPoint<SrcCurve>& src_point) {\n    static_assert(SrcCurve::kType == DstCurve::kType);\n    return reinterpret_cast<const JacobianPoint<DstCurve>&>(src_point);\n  }\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_GEOMETRY_JACOBIAN_POINT_H_\n"
  },
  {
    "path": "tachyon/math/geometry/point2.h",
    "content": "#ifndef TACHYON_MATH_GEOMETRY_POINT2_H_\n#define TACHYON_MATH_GEOMETRY_POINT2_H_\n\n#include <string>\n#include <utility>\n\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/base/buffer/copyable.h\"\n#include \"tachyon/base/json/json.h\"\n\nnamespace tachyon {\nnamespace math {\n\n// |Point2| represents points expanded into 2-element vectors.\n// It can represent elliptic curve points in Affine form.\ntemplate <typename T>\nstruct Point2 {\n  using value_type = T;\n\n  T x;\n  T y;\n\n  constexpr Point2() : Point2(T::Zero(), T::Zero()) {}\n  constexpr Point2(const T& x, const T& y) : x(x), y(y) {}\n  constexpr Point2(T&& x, T&& y) : x(std::move(x)), y(std::move(y)) {}\n\n  constexpr bool operator==(const Point2& other) const {\n    return x == other.x && y == other.y;\n  }\n\n  constexpr bool operator!=(const Point2& other) const {\n    return !operator==(other);\n  }\n\n  std::string ToString() const {\n    return absl::Substitute(\"($0, $1)\", x.ToString(), y.ToString());\n  }\n\n  std::string ToHexString(bool pad_zero = false) const {\n    return absl::Substitute(\"($0, $1)\", x.ToHexString(pad_zero),\n                            y.ToHexString(pad_zero));\n  }\n};\n\n}  // namespace math\n\nnamespace base {\n\ntemplate <typename T>\nclass Copyable<math::Point2<T>> {\n public:\n  static bool WriteTo(const math::Point2<T>& point, Buffer* buffer) {\n    return buffer->WriteMany(point.x, point.y);\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer, math::Point2<T>* point) {\n    return buffer.ReadMany(&point->x, &point->y);\n  }\n\n  static size_t EstimateSize(const math::Point2<T>& point) {\n    return base::EstimateSize(point.x, point.y);\n  }\n};\n\ntemplate <typename T>\nclass RapidJsonValueConverter<math::Point2<T>> {\n public:\n  template <typename Allocator>\n  static rapidjson::Value From(const math::Point2<T>& value,\n                               Allocator& allocator) {\n    rapidjson::Value object(rapidjson::kObjectType);\n    AddJsonElement(object, \"x\", value.x, allocator);\n    AddJsonElement(object, \"y\", value.y, allocator);\n    return object;\n  }\n\n  static bool To(const rapidjson::Value& json_value, std::string_view key,\n                 math::Point2<T>* value, std::string* error) {\n    T x;\n    T y;\n    if (!ParseJsonElement(json_value, \"x\", &x, error)) return false;\n    if (!ParseJsonElement(json_value, \"y\", &y, error)) return false;\n    value->x = std::move(x);\n    value->y = std::move(y);\n    return true;\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_MATH_GEOMETRY_POINT2_H_\n"
  },
  {
    "path": "tachyon/math/geometry/point2_unittest.cc",
    "content": "#include \"tachyon/math/geometry/point2.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/buffer/vector_buffer.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\nusing Point2GF7 = Point2<GF7>;\n\n}  // namespace\n\nTEST(Point2Test, Construct) {\n  Point2GF7 point;\n  EXPECT_EQ(point.x, GF7(0));\n  EXPECT_EQ(point.y, GF7(0));\n\n  point = Point2GF7(GF7(1), GF7(2));\n  EXPECT_EQ(point.x, GF7(1));\n  EXPECT_EQ(point.y, GF7(2));\n}\n\nTEST(Point2Test, EqualityOperators) {\n  Point2GF7 point(GF7(1), GF7(2));\n  Point2GF7 point2(GF7(4), GF7(5));\n  EXPECT_EQ(point, point);\n  EXPECT_NE(point, point2);\n}\n\nTEST(Point2Test, ToString) {\n  EXPECT_EQ(Point2GF7(GF7(1), GF7(2)).ToString(), \"(1, 2)\");\n}\n\nTEST(Point2Test, ToHexString) {\n  EXPECT_EQ(Point2GF7(GF7(1), GF7(2)).ToHexString(), \"(0x1, 0x2)\");\n}\n\nTEST(Point2Test, Copyable) {\n  Point2GF7 expected(GF7(1), GF7(2));\n\n  base::Uint8VectorBuffer write_buf;\n  ASSERT_TRUE(write_buf.Grow(base::EstimateSize(expected)));\n  ASSERT_TRUE(write_buf.Write(expected));\n  ASSERT_TRUE(write_buf.Done());\n\n  write_buf.set_buffer_offset(0);\n\n  Point2GF7 value;\n  ASSERT_TRUE(write_buf.Read(&value));\n  EXPECT_EQ(expected, value);\n}\n\nTEST(Point2Test, JsonValueConverter) {\n  Point2GF7 expected_point(GF7(1), GF7(2));\n  std::string expected_json = R\"({\"x\":1,\"y\":2})\";\n\n  Point2GF7 p;\n  std::string error;\n  ASSERT_TRUE(base::ParseJson(expected_json, &p, &error));\n  ASSERT_TRUE(error.empty());\n  EXPECT_EQ(p, expected_point);\n\n  std::string json = base::WriteToJson(p);\n  EXPECT_EQ(json, expected_json);\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/geometry/point3.h",
    "content": "#ifndef TACHYON_MATH_GEOMETRY_POINT3_H_\n#define TACHYON_MATH_GEOMETRY_POINT3_H_\n\n#include <string>\n#include <utility>\n\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/base/buffer/copyable.h\"\n#include \"tachyon/base/json/json.h\"\n\nnamespace tachyon {\nnamespace math {\n\n// |Point3| represents points expanded into 3-element vectors.\n// It can represent elliptic curve points in either Projective or Jacobian form.\ntemplate <typename T>\nstruct Point3 {\n  using value_type = T;\n\n  T x;\n  T y;\n  T z;\n\n  constexpr Point3() : Point3(T::Zero(), T::Zero(), T::Zero()) {}\n  constexpr Point3(const T& x, const T& y, const T& z) : x(x), y(y), z(z) {}\n  constexpr Point3(T&& x, T&& y, T&& z)\n      : x(std::move(x)), y(std::move(y)), z(std::move(z)) {}\n\n  constexpr bool operator==(const Point3& other) const {\n    return x == other.x && y == other.y && z == other.z;\n  }\n\n  constexpr bool operator!=(const Point3& other) const {\n    return !operator==(other);\n  }\n\n  std::string ToString() const {\n    return absl::Substitute(\"($0, $1, $2)\", x.ToString(), y.ToString(),\n                            z.ToString());\n  }\n\n  std::string ToHexString(bool pad_zero = false) const {\n    return absl::Substitute(\"($0, $1, $2)\", x.ToHexString(pad_zero),\n                            y.ToHexString(pad_zero), z.ToHexString(pad_zero));\n  }\n};\n\n}  // namespace math\n\nnamespace base {\n\ntemplate <typename T>\nclass Copyable<math::Point3<T>> {\n public:\n  static bool WriteTo(const math::Point3<T>& point, Buffer* buffer) {\n    return buffer->WriteMany(point.x, point.y, point.z);\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer, math::Point3<T>* point) {\n    return buffer.ReadMany(&point->x, &point->y, &point->z);\n  }\n\n  static size_t EstimateSize(const math::Point3<T>& point) {\n    return base::EstimateSize(point.x, point.y, point.z);\n  }\n};\n\ntemplate <typename T>\nclass RapidJsonValueConverter<math::Point3<T>> {\n public:\n  template <typename Allocator>\n  static rapidjson::Value From(const math::Point3<T>& value,\n                               Allocator& allocator) {\n    rapidjson::Value object(rapidjson::kObjectType);\n    AddJsonElement(object, \"x\", value.x, allocator);\n    AddJsonElement(object, \"y\", value.y, allocator);\n    AddJsonElement(object, \"z\", value.z, allocator);\n    return object;\n  }\n\n  static bool To(const rapidjson::Value& json_value, std::string_view key,\n                 math::Point3<T>* value, std::string* error) {\n    T x;\n    T y;\n    T z;\n    if (!ParseJsonElement(json_value, \"x\", &x, error)) return false;\n    if (!ParseJsonElement(json_value, \"y\", &y, error)) return false;\n    if (!ParseJsonElement(json_value, \"z\", &z, error)) return false;\n    value->x = std::move(x);\n    value->y = std::move(y);\n    value->z = std::move(z);\n    return true;\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_MATH_GEOMETRY_POINT3_H_\n"
  },
  {
    "path": "tachyon/math/geometry/point3_unittest.cc",
    "content": "#include \"tachyon/math/geometry/point3.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/buffer/vector_buffer.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\nusing Point3GF7 = Point3<GF7>;\n\n}  // namespace\n\nTEST(Point3Test, Construct) {\n  Point3GF7 point;\n  EXPECT_EQ(point.x, GF7(0));\n  EXPECT_EQ(point.y, GF7(0));\n  EXPECT_EQ(point.z, GF7(0));\n\n  point = Point3GF7(GF7(1), GF7(2), GF7(3));\n  EXPECT_EQ(point.x, GF7(1));\n  EXPECT_EQ(point.y, GF7(2));\n  EXPECT_EQ(point.z, GF7(3));\n}\n\nTEST(Point3Test, EqualityOperators) {\n  Point3GF7 point(GF7(1), GF7(2), GF7(3));\n  Point3GF7 point2(GF7(4), GF7(5), GF7(6));\n  EXPECT_EQ(point, point);\n  EXPECT_NE(point, point2);\n}\n\nTEST(Point3Test, ToString) {\n  EXPECT_EQ(Point3GF7(GF7(1), GF7(2), GF7(3)).ToString(), \"(1, 2, 3)\");\n}\n\nTEST(Point3Test, ToHexString) {\n  EXPECT_EQ(Point3GF7(GF7(1), GF7(2), GF7(3)).ToHexString(), \"(0x1, 0x2, 0x3)\");\n}\n\nTEST(Point3Test, Copyable) {\n  Point3GF7 expected(GF7(1), GF7(2), GF7(3));\n\n  base::Uint8VectorBuffer write_buf;\n  ASSERT_TRUE(write_buf.Grow(base::EstimateSize(expected)));\n  ASSERT_TRUE(write_buf.Write(expected));\n  ASSERT_TRUE(write_buf.Done());\n\n  write_buf.set_buffer_offset(0);\n\n  Point3GF7 value;\n  ASSERT_TRUE(write_buf.Read(&value));\n  EXPECT_EQ(expected, value);\n}\n\nTEST(Point3Test, JsonValueConverter) {\n  Point3GF7 expected_point(GF7(1), GF7(2), GF7(3));\n  std::string expected_json = R\"({\"x\":1,\"y\":2,\"z\":3})\";\n\n  Point3GF7 p;\n  std::string error;\n  ASSERT_TRUE(base::ParseJson(expected_json, &p, &error));\n  ASSERT_TRUE(error.empty());\n  EXPECT_EQ(p, expected_point);\n\n  std::string json = base::WriteToJson(p);\n  EXPECT_EQ(json, expected_json);\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/geometry/point4.h",
    "content": "#ifndef TACHYON_MATH_GEOMETRY_POINT4_H_\n#define TACHYON_MATH_GEOMETRY_POINT4_H_\n\n#include <string>\n#include <utility>\n\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/base/buffer/copyable.h\"\n#include \"tachyon/base/json/json.h\"\n\nnamespace tachyon {\nnamespace math {\n\n// |Point4| represents points expanded into 4-element vectors.\n// |Point4| is used to represent elliptic curve points in the XYZZ form.\ntemplate <typename T>\nstruct Point4 {\n  using value_type = T;\n\n  T x;\n  T y;\n  T z;\n  T w;\n\n  constexpr Point4() : Point4(T::Zero(), T::Zero(), T::Zero(), T::Zero()) {}\n  constexpr Point4(const T& x, const T& y, const T& z, const T& w)\n      : x(x), y(y), z(z), w(w) {}\n  constexpr Point4(T&& x, T&& y, T&& z, T&& w)\n      : x(std::move(x)), y(std::move(y)), z(std::move(z)), w(std::move(w)) {}\n\n  constexpr bool operator==(const Point4& other) const {\n    return x == other.x && y == other.y && z == other.z && w == other.w;\n  }\n\n  constexpr bool operator!=(const Point4& other) const {\n    return !operator==(other);\n  }\n\n  std::string ToString() const {\n    return absl::Substitute(\"($0, $1, $2, $3)\", x.ToString(), y.ToString(),\n                            z.ToString(), w.ToString());\n  }\n\n  std::string ToHexString(bool pad_zero = false) const {\n    return absl::Substitute(\"($0, $1, $2, $3)\", x.ToHexString(pad_zero),\n                            y.ToHexString(pad_zero), z.ToHexString(pad_zero),\n                            w.ToHexString(pad_zero));\n  }\n};\n\n}  // namespace math\n\nnamespace base {\n\ntemplate <typename T>\nclass Copyable<math::Point4<T>> {\n public:\n  static bool WriteTo(const math::Point4<T>& point, Buffer* buffer) {\n    return buffer->WriteMany(point.x, point.y, point.z, point.w);\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer, math::Point4<T>* point) {\n    return buffer.ReadMany(&point->x, &point->y, &point->z, &point->w);\n  }\n\n  static size_t EstimateSize(const math::Point4<T>& point) {\n    return base::EstimateSize(point.x, point.y, point.z, point.w);\n  }\n};\n\ntemplate <typename T>\nclass RapidJsonValueConverter<math::Point4<T>> {\n public:\n  template <typename Allocator>\n  static rapidjson::Value From(const math::Point4<T>& value,\n                               Allocator& allocator) {\n    rapidjson::Value object(rapidjson::kObjectType);\n    AddJsonElement(object, \"x\", value.x, allocator);\n    AddJsonElement(object, \"y\", value.y, allocator);\n    AddJsonElement(object, \"z\", value.z, allocator);\n    AddJsonElement(object, \"w\", value.w, allocator);\n    return object;\n  }\n\n  static bool To(const rapidjson::Value& json_value, std::string_view key,\n                 math::Point4<T>* value, std::string* error) {\n    T x;\n    T y;\n    T z;\n    T w;\n    if (!ParseJsonElement(json_value, \"x\", &x, error)) return false;\n    if (!ParseJsonElement(json_value, \"y\", &y, error)) return false;\n    if (!ParseJsonElement(json_value, \"z\", &z, error)) return false;\n    if (!ParseJsonElement(json_value, \"w\", &w, error)) return false;\n    value->x = std::move(x);\n    value->y = std::move(y);\n    value->z = std::move(z);\n    value->w = std::move(w);\n    return true;\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_MATH_GEOMETRY_POINT4_H_\n"
  },
  {
    "path": "tachyon/math/geometry/point4_unittest.cc",
    "content": "#include \"tachyon/math/geometry/point4.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/buffer/vector_buffer.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\nusing Point4GF7 = Point4<GF7>;\n\n}  // namespace\n\nTEST(Point4Test, Construct) {\n  Point4GF7 point;\n  EXPECT_EQ(point.x, GF7(0));\n  EXPECT_EQ(point.y, GF7(0));\n  EXPECT_EQ(point.z, GF7(0));\n  EXPECT_EQ(point.w, GF7(0));\n\n  point = Point4GF7(GF7(1), GF7(2), GF7(3), GF7(4));\n  EXPECT_EQ(point.x, GF7(1));\n  EXPECT_EQ(point.y, GF7(2));\n  EXPECT_EQ(point.z, GF7(3));\n  EXPECT_EQ(point.w, GF7(4));\n}\n\nTEST(Point4Test, EqualityOperators) {\n  Point4GF7 point(GF7(1), GF7(2), GF7(3), GF7(4));\n  Point4GF7 point2(GF7(4), GF7(5), GF7(6), GF7(0));\n  EXPECT_EQ(point, point);\n  EXPECT_NE(point, point2);\n}\n\nTEST(Point4Test, ToString) {\n  EXPECT_EQ(Point4GF7(GF7(1), GF7(2), GF7(3), GF7(4)).ToString(),\n            \"(1, 2, 3, 4)\");\n}\n\nTEST(Point4Test, ToHexString) {\n  EXPECT_EQ(Point4GF7(GF7(1), GF7(2), GF7(3), GF7(4)).ToHexString(),\n            \"(0x1, 0x2, 0x3, 0x4)\");\n}\n\nTEST(Point4Test, Copyable) {\n  Point4GF7 expected(GF7(1), GF7(2), GF7(3), GF7(4));\n\n  base::Uint8VectorBuffer write_buf;\n  ASSERT_TRUE(write_buf.Grow(base::EstimateSize(expected)));\n  ASSERT_TRUE(write_buf.Write(expected));\n  ASSERT_TRUE(write_buf.Done());\n\n  write_buf.set_buffer_offset(0);\n\n  Point4GF7 value;\n  ASSERT_TRUE(write_buf.Read(&value));\n  EXPECT_EQ(expected, value);\n}\n\nTEST(Point4Test, JsonValueConverter) {\n  Point4GF7 expected_point(GF7(1), GF7(2), GF7(3), GF7(4));\n  std::string expected_json = R\"({\"x\":1,\"y\":2,\"z\":3,\"w\":4})\";\n\n  Point4GF7 p;\n  std::string error;\n  ASSERT_TRUE(base::ParseJson(expected_json, &p, &error));\n  ASSERT_TRUE(error.empty());\n  EXPECT_EQ(p, expected_point);\n\n  std::string json = base::WriteToJson(p);\n  EXPECT_EQ(json, expected_json);\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/geometry/point_conversions.h",
    "content": "#ifndef TACHYON_MATH_GEOMETRY_POINT_CONVERSIONS_H_\n#define TACHYON_MATH_GEOMETRY_POINT_CONVERSIONS_H_\n\n#include \"tachyon/base/template_util.h\"\n#include \"tachyon/math/geometry/affine_point.h\"\n#include \"tachyon/math/geometry/jacobian_point.h\"\n#include \"tachyon/math/geometry/point_conversions_forward.h\"\n#include \"tachyon/math/geometry/point_xyzz.h\"\n#include \"tachyon/math/geometry/projective_point.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename DstPoint, typename SrcPoint>\nconstexpr DstPoint ConvertPoint(const SrcPoint& src_point) {\n  return PointConversions<SrcPoint, DstPoint>::Convert(src_point);\n}\n\n// TODO(insun35): Parallelize ConvertPoints using OpenMP.\ntemplate <typename DstContainer, typename SrcContainer>\n[[nodiscard]] constexpr bool ConvertPoints(const SrcContainer& src_points,\n                                           DstContainer* dst_points) {\n  using DstPoint = base::container_value_t<DstContainer>;\n\n  if (std::size(src_points) != std::size(*dst_points)) return false;\n  for (size_t i = 0; i < std::size(src_points); ++i) {\n    (*dst_points)[i] = ConvertPoint<DstPoint>(src_points[i]);\n  }\n  return true;\n}\n\ntemplate <typename Curve>\nstruct PointConversions<AffinePoint<Curve>, ProjectivePoint<Curve>> {\n  constexpr static ProjectivePoint<Curve> Convert(\n      const AffinePoint<Curve>& src_point) {\n    return src_point.ToProjective();\n  }\n};\n\ntemplate <typename Curve>\nstruct PointConversions<AffinePoint<Curve>, JacobianPoint<Curve>> {\n  constexpr static JacobianPoint<Curve> Convert(\n      const AffinePoint<Curve>& src_point) {\n    return src_point.ToJacobian();\n  }\n};\n\ntemplate <typename Curve>\nstruct PointConversions<AffinePoint<Curve>, PointXYZZ<Curve>> {\n  constexpr static PointXYZZ<Curve> Convert(\n      const AffinePoint<Curve>& src_point) {\n    return src_point.ToXYZZ();\n  }\n};\n\ntemplate <typename Curve>\nstruct PointConversions<ProjectivePoint<Curve>, AffinePoint<Curve>> {\n  constexpr static AffinePoint<Curve> Convert(\n      const ProjectivePoint<Curve>& src_point) {\n    return src_point.ToAffine();\n  }\n};\n\ntemplate <typename Curve>\nstruct PointConversions<ProjectivePoint<Curve>, JacobianPoint<Curve>> {\n  constexpr static JacobianPoint<Curve> Convert(\n      const ProjectivePoint<Curve>& src_point) {\n    return src_point.ToJacobian();\n  }\n};\n\ntemplate <typename Curve>\nstruct PointConversions<ProjectivePoint<Curve>, PointXYZZ<Curve>> {\n  constexpr static PointXYZZ<Curve> Convert(\n      const ProjectivePoint<Curve>& src_point) {\n    return src_point.ToXYZZ();\n  }\n};\n\ntemplate <typename Curve>\nstruct PointConversions<JacobianPoint<Curve>, AffinePoint<Curve>> {\n  constexpr static AffinePoint<Curve> Convert(\n      const JacobianPoint<Curve>& src_point) {\n    return src_point.ToAffine();\n  }\n};\n\ntemplate <typename Curve>\nstruct PointConversions<JacobianPoint<Curve>, ProjectivePoint<Curve>> {\n  constexpr static ProjectivePoint<Curve> Convert(\n      const JacobianPoint<Curve>& src_point) {\n    return src_point.ToProjective();\n  }\n};\n\ntemplate <typename Curve>\nstruct PointConversions<JacobianPoint<Curve>, PointXYZZ<Curve>> {\n  constexpr static PointXYZZ<Curve> Convert(\n      const JacobianPoint<Curve>& src_point) {\n    return src_point.ToXYZZ();\n  }\n};\n\ntemplate <typename Curve>\nstruct PointConversions<PointXYZZ<Curve>, AffinePoint<Curve>> {\n  constexpr static AffinePoint<Curve> Convert(\n      const PointXYZZ<Curve>& src_point) {\n    return src_point.ToAffine();\n  }\n};\n\ntemplate <typename Curve>\nstruct PointConversions<PointXYZZ<Curve>, ProjectivePoint<Curve>> {\n  constexpr static ProjectivePoint<Curve> Convert(\n      const PointXYZZ<Curve>& src_point) {\n    return src_point.ToProjective();\n  }\n};\n\ntemplate <typename Curve>\nstruct PointConversions<PointXYZZ<Curve>, JacobianPoint<Curve>> {\n  constexpr static JacobianPoint<Curve> Convert(\n      const PointXYZZ<Curve>& src_point) {\n    return src_point.ToJacobian();\n  }\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_GEOMETRY_POINT_CONVERSIONS_H_\n"
  },
  {
    "path": "tachyon/math/geometry/point_conversions_forward.h",
    "content": "#ifndef TACHYON_MATH_GEOMETRY_POINT_CONVERSIONS_FORWARD_H_\n#define TACHYON_MATH_GEOMETRY_POINT_CONVERSIONS_FORWARD_H_\n\nnamespace tachyon::math {\n\ntemplate <typename SrcPoint, typename DstPoint, typename SFINAE = void>\nstruct PointConversions;\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_GEOMETRY_POINT_CONVERSIONS_FORWARD_H_\n"
  },
  {
    "path": "tachyon/math/geometry/point_xyzz.h",
    "content": "#ifndef TACHYON_MATH_GEOMETRY_POINT_XYZZ_H_\n#define TACHYON_MATH_GEOMETRY_POINT_XYZZ_H_\n\n#include <utility>\n\n#include \"tachyon/math/geometry/point4.h\"\n#include \"tachyon/math/geometry/point_conversions_forward.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename Curve, typename SFINAE = void>\nclass PointXYZZ;\n\ntemplate <typename ScalarField, typename Curve,\n          std::enable_if_t<std::is_same_v<\n              ScalarField, typename Curve::ScalarField>>* = nullptr>\nPointXYZZ<Curve> operator*(const ScalarField& v,\n                           const PointXYZZ<Curve>& point) {\n  return point * v;\n}\n\ntemplate <typename Curve>\nstruct PointConversions<PointXYZZ<Curve>, PointXYZZ<Curve>> {\n  constexpr static const PointXYZZ<Curve>& Convert(\n      const PointXYZZ<Curve>& src_point) {\n    return src_point;\n  }\n};\n\ntemplate <typename SrcCurve, typename DstCurve>\nstruct PointConversions<PointXYZZ<SrcCurve>, PointXYZZ<DstCurve>,\n                        std::enable_if_t<!std::is_same_v<SrcCurve, DstCurve>>> {\n  static const PointXYZZ<DstCurve>& Convert(\n      const PointXYZZ<SrcCurve>& src_point) {\n    static_assert(SrcCurve::kType == DstCurve::kType);\n    return reinterpret_cast<const PointXYZZ<DstCurve>&>(src_point);\n  }\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_GEOMETRY_POINT_XYZZ_H_\n"
  },
  {
    "path": "tachyon/math/geometry/projective_point.h",
    "content": "#ifndef TACHYON_MATH_GEOMETRY_PROJECTIVE_POINT_H_\n#define TACHYON_MATH_GEOMETRY_PROJECTIVE_POINT_H_\n\n#include <utility>\n\n#include \"tachyon/math/geometry/point3.h\"\n#include \"tachyon/math/geometry/point_conversions_forward.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename Curve, typename SFINAE = void>\nclass ProjectivePoint;\n\ntemplate <typename ScalarField, typename Curve,\n          std::enable_if_t<std::is_same_v<\n              ScalarField, typename Curve::ScalarField>>* = nullptr>\nProjectivePoint<Curve> operator*(const ScalarField& v,\n                                 const ProjectivePoint<Curve>& point) {\n  return point * v;\n}\n\ntemplate <typename Curve>\nstruct PointConversions<ProjectivePoint<Curve>, ProjectivePoint<Curve>> {\n  constexpr static const ProjectivePoint<Curve>& Convert(\n      const ProjectivePoint<Curve>& src_point) {\n    return src_point;\n  }\n};\n\ntemplate <typename SrcCurve, typename DstCurve>\nstruct PointConversions<ProjectivePoint<SrcCurve>, ProjectivePoint<DstCurve>,\n                        std::enable_if_t<!std::is_same_v<SrcCurve, DstCurve>>> {\n  static const ProjectivePoint<DstCurve>& Convert(\n      const ProjectivePoint<SrcCurve>& src_point) {\n    static_assert(SrcCurve::kType == DstCurve::kType);\n    return reinterpret_cast<const ProjectivePoint<DstCurve>&>(src_point);\n  }\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_GEOMETRY_PROJECTIVE_POINT_H_\n"
  },
  {
    "path": "tachyon/math/matrix/BUILD.bazel",
    "content": "load(\n    \"//bazel:tachyon_cc.bzl\",\n    \"tachyon_cc_benchmark\",\n    \"tachyon_cc_library\",\n    \"tachyon_cc_unittest\",\n)\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"cost_calculator_forward\",\n    hdrs = [\"cost_calculator_forward.h\"],\n)\n\ntachyon_cc_library(\n    name = \"gmp_num_traits\",\n    hdrs = [\"gmp_num_traits.h\"],\n    deps = [\n        \"@eigen_archive//:eigen3\",\n        \"@local_config_gmp//:gmp\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"matrix_operations\",\n    hdrs = [\"matrix_operations.h\"],\n    deps = [\n        \":matrix_types\",\n        \"//tachyon/base:openmp_util\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"matrix_types\",\n    hdrs = [\"matrix_types.h\"],\n    deps = [\n        \":prime_field_num_traits\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base/buffer:copyable\",\n        \"//tachyon/math/finite_fields:finite_field_traits\",\n        \"@eigen_archive//:eigen3\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"matrix_utils\",\n    hdrs = [\"matrix_utils.h\"],\n    deps = [\n        \":matrix_types\",\n        \"//tachyon/base:bits\",\n        \"//tachyon/base:compiler_specific\",\n        \"//tachyon/base:openmp_util\",\n        \"//tachyon/base:profiler\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/math/finite_fields:extended_packed_field_traits_forward\",\n        \"//tachyon/math/finite_fields:extension_field_traits_forward\",\n        \"//tachyon/math/finite_fields:finite_field_traits\",\n        \"//tachyon/math/finite_fields:packed_field_traits_forward\",\n        \"@eigen_archive//:eigen3\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"prime_field_num_traits\",\n    hdrs = [\"prime_field_num_traits.h\"],\n    deps = [\n        \":cost_calculator_forward\",\n        \"//tachyon/math/finite_fields:prime_field_base\",\n        \"@eigen_archive//:eigen3\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"matrix_unittests\",\n    srcs = [\n        \"matrix_inverse_unittest.cc\",\n        \"matrix_operations_unittest.cc\",\n        \"matrix_types_unittest.cc\",\n        \"matrix_utils_unittest.cc\",\n    ],\n    deps = [\n        \":matrix_operations\",\n        \":matrix_types\",\n        \":matrix_utils\",\n        \"//tachyon/base/buffer:vector_buffer\",\n        \"//tachyon/math/finite_fields/baby_bear:baby_bear4\",\n        \"//tachyon/math/finite_fields/test:finite_field_test\",\n        \"//tachyon/math/finite_fields/test:gf7\",\n    ],\n)\n\ntachyon_cc_benchmark(\n    name = \"matrix_operations_benchmark\",\n    srcs = [\"matrix_operations_benchmark.cc\"],\n    deps = [\n        \":matrix_operations\",\n        \":matrix_types\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:fr\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/math/matrix/cost_calculator_forward.h",
    "content": "#ifndef TACHYON_MATH_MATRIX_COST_CALCULATOR_FORWARD_H_\n#define TACHYON_MATH_MATRIX_COST_CALCULATOR_FORWARD_H_\n\nnamespace tachyon::math {\n\ntemplate <typename T, typename SFINAE = void>\nstruct CostCalculator;\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_MATRIX_COST_CALCULATOR_FORWARD_H_\n"
  },
  {
    "path": "tachyon/math/matrix/gmp_num_traits.h",
    "content": "#ifndef TACHYON_MATH_MATRIX_GMP_NUM_TRAITS_H_\n#define TACHYON_MATH_MATRIX_GMP_NUM_TRAITS_H_\n\n#include \"third_party/eigen3/Eigen/Core\"\n#include \"third_party/gmp/include/gmpxx.h\"\n\nnamespace Eigen {\n\ntemplate <>\nstruct NumTraits<mpz_class> : GenericNumTraits<mpz_class> {\n  enum {\n    IsInteger = 1,\n    IsSigned = 1,\n    IsComplex = 0,\n    RequireInitialization = 0,\n    // NOTE(chokobole): I just used the same values defined at\n    // https://eigen.tuxfamily.org/dox/TopicCustomizing_CustomScalar.html.\n    ReadCost = 6,\n    AddCost = 150,\n    MulCost = 100,\n  };\n};\n\n}  // namespace Eigen\n\n#endif  // TACHYON_MATH_MATRIX_GMP_NUM_TRAITS_H_\n"
  },
  {
    "path": "tachyon/math/matrix/matrix_inverse_unittest.cc",
    "content": "#include \"third_party/eigen3/Eigen/LU\"\n\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n#include \"tachyon/math/matrix/matrix_types.h\"\n\nnamespace tachyon::math {\n\nclass MatrixInverseTest : public FiniteFieldTest<GF7> {};\n\nTEST_F(MatrixInverseTest, Inverse) {\n  for (size_t i = 1; i < 10; ++i) {\n    Matrix<GF7> matrix = Matrix<GF7>::Random(i, i);\n    if (!matrix.determinant().IsZero()) {\n      Matrix<GF7> inverse = matrix.inverse();\n      EXPECT_TRUE(matrix * inverse == Matrix<GF7>::Identity(i, i));\n    }\n  }\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/matrix/matrix_operations.h",
    "content": "#ifndef TACHYON_MATH_MATRIX_MATRIX_OPERATIONS_H_\n#define TACHYON_MATH_MATRIX_MATRIX_OPERATIONS_H_\n\n#include \"tachyon/base/openmp_util.h\"\n#include \"tachyon/math/matrix/matrix_types.h\"\n\nnamespace tachyon::math {\n\n// The motivation of the variants of |MulMatVec()| is as follows:\n\n// NOTE(chokobole): Eigen matrix multiplication has a computational\n// overhead unlike naive matrix multiplication.\n//\n// Example: Multiplying a 2x2 matrix by a 2x1 vector:\n//\n// +----+----+   +----+\n// | m₀ | m₁ | * | v₀ |\n// +----+----+   +----+\n// | m₂ | m₃ |   | v₁ |\n// +----+----+   +----+\n//\n// The operations involved in this multiplication are as follows:\n//\n// 1 * 1\n// 1 * 1\n// m₀ * v₀\n// m₀v₀ + 0\n// m₂ * v₀\n// m₂v₀ + 0\n// m₁ * v₁\n// m₁v₁ + m₀v₀\n// m₃ * v₁\n// m₃v₁ + m₂v₀\n// m₁v₁ + m₀v₀ * 1\n// m₁v₁ + m₀v₀ + 0\n// m₃v₁ + m₂v₀ * 1\n// m₃v₁ + m₂v₀ + 0\n\ntemplate <typename Derived, typename Derived2,\n          typename F = typename Derived::Scalar>\nmath::Vector<F> MulMatVec(const Eigen::MatrixBase<Derived>& matrix,\n                          const Eigen::MatrixBase<Derived2>& vector) {\n  static_assert(std::is_same_v<F, typename Derived2::Scalar>);\n\n  math::Vector<F> ret = math::Vector<F>::Constant(vector.size(), F::Zero());\n  OMP_PARALLEL_FOR(Eigen::Index i = 0; i < matrix.rows(); ++i) {\n    for (Eigen::Index j = 0; j < matrix.cols(); ++j) {\n      ret[i] += matrix(i, j) * vector[j];\n    }\n  }\n  return ret;\n}\n\ntemplate <typename Derived, typename Derived2,\n          typename F = typename Derived::Scalar>\nmath::Vector<F> MulMatVecSerial(const Eigen::MatrixBase<Derived>& matrix,\n                                const Eigen::MatrixBase<Derived2>& vector) {\n  static_assert(std::is_same_v<F, typename Derived2::Scalar>);\n\n  math::Vector<F> ret = math::Vector<F>::Constant(vector.size(), F::Zero());\n  for (Eigen::Index i = 0; i < matrix.rows(); ++i) {\n    for (Eigen::Index j = 0; j < matrix.cols(); ++j) {\n      ret[i] += matrix(i, j) * vector[j];\n    }\n  }\n  return ret;\n}\n\ntemplate <typename Derived, typename Derived2, enum Eigen::AccessorLevels Level,\n          typename F = typename Derived::Scalar>\nmath::Vector<F> MulMatVec(\n    const Eigen::MatrixBase<Derived>& matrix,\n    const Eigen::DenseCoeffsBase<Derived2, Level>& vector) {\n  static_assert(std::is_same_v<F, typename Derived2::Scalar>);\n\n  math::Vector<F> ret = math::Vector<F>::Constant(vector.size(), F::Zero());\n  if (vector.rows() == 1) {\n    OMP_PARALLEL_FOR(Eigen::Index i = 0; i < matrix.rows(); ++i) {\n      for (Eigen::Index j = 0; j < matrix.cols(); ++j) {\n        ret[i] += matrix(i, j) * vector(0, j);\n      }\n    }\n  } else if (vector.cols() == 1) {\n    OMP_PARALLEL_FOR(Eigen::Index i = 0; i < matrix.rows(); ++i) {\n      for (Eigen::Index j = 0; j < matrix.cols(); ++j) {\n        ret[i] += matrix(i, j) * vector(j, 0);\n      }\n    }\n  } else {\n    NOTREACHED();\n  }\n  return ret;\n}\n\ntemplate <typename Derived, typename Derived2, enum Eigen::AccessorLevels Level,\n          typename F = typename Derived::Scalar>\nmath::Vector<F> MulMatVecSerial(\n    const Eigen::MatrixBase<Derived>& matrix,\n    const Eigen::DenseCoeffsBase<Derived2, Level>& vector) {\n  static_assert(std::is_same_v<F, typename Derived2::Scalar>);\n\n  math::Vector<F> ret = math::Vector<F>::Constant(vector.size(), F::Zero());\n  if (vector.rows() == 1) {\n    for (Eigen::Index i = 0; i < matrix.rows(); ++i) {\n      for (Eigen::Index j = 0; j < matrix.cols(); ++j) {\n        ret[i] += matrix(i, j) * vector(0, j);\n      }\n    }\n  } else if (vector.cols() == 1) {\n    for (Eigen::Index i = 0; i < matrix.rows(); ++i) {\n      for (Eigen::Index j = 0; j < matrix.cols(); ++j) {\n        ret[i] += matrix(i, j) * vector(j, 0);\n      }\n    }\n  } else {\n    NOTREACHED();\n  }\n  return ret;\n}\n\ntemplate <typename Derived, typename Derived2,\n          typename F = typename Derived::Scalar>\nmath::Matrix<F> MulMatMat(const Eigen::MatrixBase<Derived>& matrix,\n                          const Eigen::MatrixBase<Derived2>& matrix2) {\n  static_assert(std::is_same_v<F, typename Derived2::Scalar>);\n  CHECK_EQ(matrix.cols(), matrix2.rows());\n\n  math::Matrix<F> ret =\n      math::Matrix<F>::Constant(matrix.rows(), matrix2.cols(), F::Zero());\n  OMP_PARALLEL_FOR(Eigen::Index i = 0; i < matrix.rows(); ++i) {\n    for (Eigen::Index j = 0; j < matrix.cols(); ++j) {\n      for (Eigen::Index k = 0; k < matrix2.cols(); ++k) {\n        ret(i, k) += matrix(i, j) * matrix2(j, k);\n      }\n    }\n  }\n  return ret;\n}\n\ntemplate <typename Derived, typename Derived2,\n          typename F = typename Derived::Scalar>\nmath::Matrix<F> MulMatMatSerial(const Eigen::MatrixBase<Derived>& matrix,\n                                const Eigen::MatrixBase<Derived2>& matrix2) {\n  static_assert(std::is_same_v<F, typename Derived2::Scalar>);\n  CHECK_EQ(matrix.cols(), matrix2.rows());\n\n  math::Matrix<F> ret =\n      math::Matrix<F>::Constant(matrix.rows(), matrix2.cols(), F::Zero());\n  for (Eigen::Index i = 0; i < matrix.rows(); ++i) {\n    for (Eigen::Index j = 0; j < matrix.cols(); ++j) {\n      for (Eigen::Index k = 0; k < matrix2.cols(); ++k) {\n        ret(i, k) += matrix(i, j) * matrix2(j, k);\n      }\n    }\n  }\n  return ret;\n}\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_MATRIX_MATRIX_OPERATIONS_H_\n"
  },
  {
    "path": "tachyon/math/matrix/matrix_operations_benchmark.cc",
    "content": "#include \"benchmark/benchmark.h\"\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/fr.h\"\n#include \"tachyon/math/matrix/matrix_operations.h\"\n#include \"tachyon/math/matrix/matrix_types.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename F>\nvoid BM_MulMatVecNaive(benchmark::State& state) {\n  F::Init();\n  Matrix<F> matrix = Matrix<F>::Constant(state.range(0), state.range(0), F(5));\n  Vector<F> vector = Vector<F>::Constant(state.range(0), F(5));\n  for (auto _ : state) {\n    vector = matrix * vector;\n  }\n  benchmark::DoNotOptimize(vector);\n}\n\ntemplate <typename F>\nvoid BM_MulMatVecSerial(benchmark::State& state) {\n  F::Init();\n  Matrix<F> matrix = Matrix<F>::Constant(state.range(0), state.range(0), F(5));\n  Vector<F> vector = Vector<F>::Constant(state.range(0), F(5));\n  for (auto _ : state) {\n    vector = MulMatVecSerial(matrix, vector);\n  }\n  benchmark::DoNotOptimize(vector);\n}\n\ntemplate <typename F>\nvoid BM_MulMatVec(benchmark::State& state) {\n  F::Init();\n  Matrix<F> matrix = Matrix<F>::Constant(state.range(0), state.range(0), F(5));\n  Vector<F> vector = Vector<F>::Constant(state.range(0), F(5));\n  for (auto _ : state) {\n    vector = MulMatVec(matrix, vector);\n  }\n  benchmark::DoNotOptimize(vector);\n}\n\ntemplate <typename F>\nvoid BM_MulMatMatNaive(benchmark::State& state) {\n  F::Init();\n  Matrix<F> matrix = Matrix<F>::Constant(state.range(0), state.range(0), F(5));\n  Matrix<F> matrix2 = Matrix<F>::Constant(state.range(0), state.range(0), F(5));\n  for (auto _ : state) {\n    matrix2 = matrix * matrix2;\n  }\n  benchmark::DoNotOptimize(matrix2);\n}\n\ntemplate <typename F>\nvoid BM_MulMatMatSerial(benchmark::State& state) {\n  F::Init();\n  Matrix<F> matrix = Matrix<F>::Constant(state.range(0), state.range(0), F(5));\n  Matrix<F> matrix2 = Matrix<F>::Constant(state.range(0), state.range(0), F(5));\n  for (auto _ : state) {\n    matrix2 = MulMatMatSerial(matrix, matrix2);\n  }\n  benchmark::DoNotOptimize(matrix2);\n}\n\ntemplate <typename F>\nvoid BM_MulMatMat(benchmark::State& state) {\n  F::Init();\n  Matrix<F> matrix = Matrix<F>::Constant(state.range(0), state.range(0), F(5));\n  Matrix<F> matrix2 = Matrix<F>::Constant(state.range(0), state.range(0), F(5));\n  for (auto _ : state) {\n    matrix2 = MulMatMat(matrix, matrix2);\n  }\n  benchmark::DoNotOptimize(matrix2);\n}\n\nBENCHMARK_TEMPLATE(BM_MulMatVecNaive, bn254::Fr)\n    ->RangeMultiplier(2)\n    ->Range(1 << 1, 1 << 10);\nBENCHMARK_TEMPLATE(BM_MulMatVecSerial, bn254::Fr)\n    ->RangeMultiplier(2)\n    ->Range(1 << 1, 1 << 10);\nBENCHMARK_TEMPLATE(BM_MulMatVec, bn254::Fr)\n    ->RangeMultiplier(2)\n    ->Range(1 << 1, 1 << 10);\n\nBENCHMARK_TEMPLATE(BM_MulMatMatNaive, bn254::Fr)\n    ->RangeMultiplier(2)\n    ->Range(1 << 1, 1 << 9);\nBENCHMARK_TEMPLATE(BM_MulMatMatSerial, bn254::Fr)\n    ->RangeMultiplier(2)\n    ->Range(1 << 1, 1 << 9);\nBENCHMARK_TEMPLATE(BM_MulMatMat, bn254::Fr)\n    ->RangeMultiplier(2)\n    ->Range(1 << 1, 1 << 9);\n\n}  // namespace tachyon::math\n\n// clang-format off\n// Executing tests from //tachyon/math/matrix:matrix_operations_benchmark\n// -----------------------------------------------------------------------------\n// 2024-07-19T04:56:59+00:00\n// Running /home/chokobole/.cache/bazel/_bazel_chokobole/234690e3562329d13f7f07caac03dae4/execroot/kroma_network_tachyon/bazel-out/k8-opt/bin/tachyon/math/matrix/matrix_operations_benchmark.runfiles/kroma_network_tachyon/tachyon/math/matrix/matrix_operations_benchmark\n// Run on (32 X 5500.06 MHz CPU s)\n// CPU Caches:\n//   L1 Data 48 KiB (x16)\n//   L1 Instruction 32 KiB (x16)\n//   L2 Unified 2048 KiB (x16)\n//   L3 Unified 36864 KiB (x1)\n// Load Average: 1.19, 1.31, 1.18\n// -----------------------------------------------------------------------------\n// Benchmark                                   Time             CPU   Iterations\n// -----------------------------------------------------------------------------\n// BM_MulMatVecNaive<bn254::Fr>/2            123 ns          122 ns      5617150\n// BM_MulMatVecNaive<bn254::Fr>/4            280 ns          280 ns      2497914\n// BM_MulMatVecNaive<bn254::Fr>/8            859 ns          859 ns       816942\n// BM_MulMatVecNaive<bn254::Fr>/16          3113 ns         3112 ns       225830\n// BM_MulMatVecNaive<bn254::Fr>/32         11736 ns        11736 ns        59687\n// BM_MulMatVecNaive<bn254::Fr>/64         46452 ns        46442 ns        15218\n// BM_MulMatVecNaive<bn254::Fr>/128       193267 ns       193259 ns         3652\n// BM_MulMatVecNaive<bn254::Fr>/256       769266 ns       769131 ns          913\n// BM_MulMatVecNaive<bn254::Fr>/512      3055500 ns      3055356 ns          229\n// BM_MulMatVecNaive<bn254::Fr>/1024    14999901 ns     14999210 ns           48\n// BM_MulMatVecSerial<bn254::Fr>/2          55.1 ns         55.1 ns     12941369\n// BM_MulMatVecSerial<bn254::Fr>/4           196 ns          196 ns      3668682\n// BM_MulMatVecSerial<bn254::Fr>/8           740 ns          740 ns       954441\n// BM_MulMatVecSerial<bn254::Fr>/16         2863 ns         2863 ns       242618\n// BM_MulMatVecSerial<bn254::Fr>/32        11403 ns        11403 ns        61397\n// BM_MulMatVecSerial<bn254::Fr>/64        46126 ns        46126 ns        15170\n// BM_MulMatVecSerial<bn254::Fr>/128      187898 ns       187899 ns         3730\n// BM_MulMatVecSerial<bn254::Fr>/256      763410 ns       763404 ns          919\n// BM_MulMatVecSerial<bn254::Fr>/512     3586593 ns      3586489 ns          198\n// BM_MulMatVecSerial<bn254::Fr>/1024   32458958 ns     32457299 ns           23\n// BM_MulMatVec<bn254::Fr>/2                9752 ns         9231 ns       192896\n// BM_MulMatVec<bn254::Fr>/4                4711 ns         4480 ns       256574\n// BM_MulMatVec<bn254::Fr>/8                5064 ns         4990 ns       160772\n// BM_MulMatVec<bn254::Fr>/16               8102 ns         6706 ns       100000\n// BM_MulMatVec<bn254::Fr>/32               5156 ns         5014 ns       149700\n// BM_MulMatVec<bn254::Fr>/64              21412 ns        19453 ns        66057\n// BM_MulMatVec<bn254::Fr>/128            105175 ns        95884 ns        16822\n// BM_MulMatVec<bn254::Fr>/256           1653757 ns      1462683 ns         8835\n// BM_MulMatVec<bn254::Fr>/512           1968314 ns      1750242 ns         1000\n// BM_MulMatVec<bn254::Fr>/1024          3888062 ns      3581259 ns          153\n// BM_MulMatMatNaive<bn254::Fr>/2            145 ns          145 ns      4820759\n// BM_MulMatMatNaive<bn254::Fr>/4            835 ns          835 ns       835765\n// BM_MulMatMatNaive<bn254::Fr>/8           8772 ns         8771 ns        79835\n// BM_MulMatMatNaive<bn254::Fr>/16         63928 ns        63833 ns        11102\n// BM_MulMatMatNaive<bn254::Fr>/32        481063 ns       481060 ns         1455\n// BM_MulMatMatNaive<bn254::Fr>/64        963437 ns       963423 ns          729\n// BM_MulMatMatNaive<bn254::Fr>/128      6737850 ns      6255369 ns          234\n// BM_MulMatMatNaive<bn254::Fr>/256     17992549 ns     17970574 ns           41\n// BM_MulMatMatNaive<bn254::Fr>/512    146447301 ns    146146565 ns            4\n// BM_MulMatMatSerial<bn254::Fr>/2           102 ns          102 ns      6888084\n// BM_MulMatMatSerial<bn254::Fr>/4           750 ns          750 ns       934540\n// BM_MulMatMatSerial<bn254::Fr>/8          5855 ns         5855 ns       119456\n// BM_MulMatMatSerial<bn254::Fr>/16        46651 ns        46647 ns        14998\n// BM_MulMatMatSerial<bn254::Fr>/32       371483 ns       371476 ns         1884\n// BM_MulMatMatSerial<bn254::Fr>/64      3000850 ns      3000784 ns          234\n// BM_MulMatMatSerial<bn254::Fr>/128    24536289 ns     24536348 ns           29\n// BM_MulMatMatSerial<bn254::Fr>/256   201230526 ns    201217998 ns            4\n// BM_MulMatMatSerial<bn254::Fr>/512  1850822449 ns   1850779270 ns            1\n// BM_MulMatMat<bn254::Fr>/2                6833 ns         6833 ns       100000\n// BM_MulMatMat<bn254::Fr>/4                6848 ns         6847 ns       146381\n// BM_MulMatMat<bn254::Fr>/8                7575 ns         7575 ns        91743\n// BM_MulMatMat<bn254::Fr>/16              33371 ns        32436 ns        48004\n// BM_MulMatMat<bn254::Fr>/32              50228 ns        50148 ns        10431\n// BM_MulMatMat<bn254::Fr>/64            2107477 ns      1953816 ns         1000\n// BM_MulMatMat<bn254::Fr>/128          13232534 ns     10078648 ns           67\n// BM_MulMatMat<bn254::Fr>/256          21831894 ns     20891728 ns           30\n// BM_MulMatMat<bn254::Fr>/512         163877392 ns    156880803 ns            5\n// clang-format on\n"
  },
  {
    "path": "tachyon/math/matrix/matrix_operations_unittest.cc",
    "content": "#include \"tachyon/math/matrix/matrix_operations.h\"\n\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n\nnamespace tachyon::math {\n\nclass MatrixOperationsTest : public FiniteFieldTest<GF7> {};\n\nTEST_F(MatrixOperationsTest, MulMatVecSerialWithVector) {\n  Matrix<GF7> matrix = Matrix<GF7>::Random(2, 2);\n  Vector<GF7> vector = Vector<GF7>::Random(2);\n\n  EXPECT_EQ(matrix * vector, MulMatVecSerial(matrix, vector));\n}\n\nTEST_F(MatrixOperationsTest, MulMatVecSerialWithRowVector) {\n  Matrix<GF7> matrix = Matrix<GF7>::Random(2, 2);\n  RowVector<GF7> vector = RowVector<GF7>::Random(2);\n\n  EXPECT_EQ(matrix * vector.transpose(), MulMatVecSerial(matrix, vector));\n}\n\nTEST_F(MatrixOperationsTest, MulMatVecSerialWithCoefficients) {\n  Matrix<GF7> matrix = Matrix<GF7>::Random(2, 2);\n  Matrix<GF7> matrix2 = Matrix<GF7>::Random(2, 2);\n\n  EXPECT_EQ(matrix * matrix2.col(0), MulMatVecSerial(matrix, matrix2.col(0)));\n  EXPECT_EQ(matrix * matrix2.row(0).transpose(),\n            MulMatVecSerial(matrix, matrix2.row(0)));\n}\n\nTEST_F(MatrixOperationsTest, MulMatMatSerial) {\n  Matrix<GF7> matrix = Matrix<GF7>::Random(3, 2);\n  Matrix<GF7> matrix2 = Matrix<GF7>::Random(2, 5);\n\n  EXPECT_EQ(matrix * matrix2, MulMatMatSerial(matrix, matrix2));\n}\n\n#if defined(TACHYON_HAS_OPENMP)\nTEST_F(MatrixOperationsTest, MulMatVecWithVector) {\n  Matrix<GF7> matrix = Matrix<GF7>::Random(100, 100);\n  Vector<GF7> vector = Vector<GF7>::Random(100);\n\n  EXPECT_EQ(matrix * vector, MulMatVec(matrix, vector));\n}\n\nTEST_F(MatrixOperationsTest, MulMatVecWithRowVector) {\n  Matrix<GF7> matrix = Matrix<GF7>::Random(100, 100);\n  RowVector<GF7> vector = RowVector<GF7>::Random(100);\n\n  EXPECT_EQ(matrix * vector.transpose(), MulMatVec(matrix, vector));\n}\n\nTEST_F(MatrixOperationsTest, MulMatVecWithCoefficients) {\n  Matrix<GF7> matrix = Matrix<GF7>::Random(100, 100);\n  Matrix<GF7> matrix2 = Matrix<GF7>::Random(100, 100);\n\n  EXPECT_EQ(matrix * matrix2.col(0), MulMatVec(matrix, matrix2.col(0)));\n  EXPECT_EQ(matrix * matrix2.row(0).transpose(),\n            MulMatVec(matrix, matrix2.row(0)));\n}\n\nTEST_F(MatrixOperationsTest, MulMatMat) {\n  Matrix<GF7> matrix = Matrix<GF7>::Random(3, 2);\n  Matrix<GF7> matrix2 = Matrix<GF7>::Random(2, 5);\n\n  EXPECT_EQ(matrix * matrix2, MulMatMat(matrix, matrix2));\n}\n#endif\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/matrix/matrix_types.h",
    "content": "#ifndef TACHYON_MATH_MATRIX_MATRIX_TYPES_H_\n#define TACHYON_MATH_MATRIX_MATRIX_TYPES_H_\n\n#include <type_traits>\n#include <utility>\n\n#include \"third_party/eigen3/Eigen/Core\"\n\n#include \"tachyon/base/buffer/copyable.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/math/finite_fields/finite_field_traits.h\"\n\nnamespace tachyon {\nnamespace math {\n\n// See\n// https://gitlab.com/libeigen/eigen/-/blob/c29c800/Eigen/src/Core/util/ForwardDeclarations.h#L66-68.\ntemplate <int Rows, int Cols>\nconstexpr int GetDefaultEigenOptions() {\n  return Eigen::AutoAlign | ((Rows == 1 && Cols != 1) ? Eigen::RowMajor\n                             : (Cols == 1 && Rows != 1)\n                                 ? Eigen::ColMajor\n                                 : EIGEN_DEFAULT_MATRIX_STORAGE_ORDER_OPTION);\n}\n\ntemplate <typename Field, int Rows = Eigen::Dynamic, int Cols = Eigen::Dynamic,\n          int Options = GetDefaultEigenOptions<Rows, Cols>(),\n          int MaxRows = Rows, int MaxCols = Cols>\nusing Matrix = Eigen::Matrix<Field, Rows, Cols, Options, MaxRows, MaxCols>;\n\ntemplate <typename Field, int Rows = Eigen::Dynamic, int Cols = Eigen::Dynamic,\n          int MaxRows = Rows, int MaxCols = Cols>\nusing ColMajorMatrix =\n    Eigen::Matrix<Field, Rows, Cols, Eigen::ColMajor, MaxRows, MaxCols>;\n\ntemplate <typename Field, int Rows = Eigen::Dynamic, int Cols = Eigen::Dynamic,\n          int MaxRows = Rows, int MaxCols = Cols>\nusing RowMajorMatrix =\n    Eigen::Matrix<Field, Rows, Cols, Eigen::RowMajor, MaxRows, MaxCols>;\n\ntemplate <typename Field, int Size = Eigen::Dynamic, int MaxSize = Size>\nusing DiagonalMatrix = Eigen::DiagonalMatrix<Field, Size, MaxSize>;\n\ntemplate <typename Field, int Rows = Eigen::Dynamic,\n          int Options = GetDefaultEigenOptions<Rows, 1>(), int MaxRows = Rows>\nusing Vector = Eigen::Matrix<Field, Rows, 1, Options, MaxRows, 1>;\n\ntemplate <typename Field, int Cols = Eigen::Dynamic,\n          int Options = GetDefaultEigenOptions<1, Cols>(), int MaxCols = Cols>\nusing RowVector = Eigen::Matrix<Field, 1, Cols, Options, 1, MaxCols>;\n\n}  // namespace math\n\nnamespace base {\n\ntemplate <typename Field, int Rows, int Cols, int Options, int MaxRows,\n          int MaxCols>\nclass Copyable<Eigen::Matrix<Field, Rows, Cols, Options, MaxRows, MaxCols>> {\n public:\n  using Matrix = Eigen::Matrix<Field, Rows, Cols, Options, MaxRows, MaxCols>;\n\n  static bool WriteTo(const Matrix& matrix, Buffer* buffer) {\n    if (!buffer->WriteMany(matrix.rows(), matrix.cols())) return false;\n    for (Eigen::Index i = 0; i < matrix.size(); ++i) {\n      if (!buffer->Write(matrix.data()[i])) return false;\n    }\n    return true;\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer, Matrix* matrix) {\n    Eigen::Index rows, cols;\n    Matrix matrix_tmp;\n    if (!buffer.ReadMany(&rows, &cols)) return false;\n    if (Rows != Eigen::Dynamic && Cols != Eigen::Dynamic) {\n      if (rows != Rows || cols != Cols) return false;\n    } else if (Rows != Eigen::Dynamic) {\n      if (rows != Rows) return false;\n      matrix_tmp.resize(Eigen::NoChange, cols);\n    } else if (Cols != Eigen::Dynamic) {\n      if (cols != Cols) return false;\n      matrix_tmp.resize(rows, Eigen::NoChange);\n    } else {\n      matrix_tmp.resize(rows, cols);\n    }\n    for (Eigen::Index i = 0; i < matrix_tmp.size(); ++i) {\n      if (!buffer.Read(&matrix_tmp.data()[i])) return false;\n    }\n    *matrix = std::move(matrix_tmp);\n    return true;\n  }\n\n  static size_t EstimateSize(const Matrix& matrix) {\n    return matrix.size() * sizeof(Field) + sizeof(Eigen::Index) * 2;\n  }\n};\n\ntemplate <typename Derived>\nclass Copyable<Eigen::Map<Derived>> {\n public:\n  using Matrix = Eigen::Map<Derived>;\n\n  static bool WriteTo(const Matrix& matrix, Buffer* buffer) {\n    return Copyable<std::decay_t<Derived>>::WriteTo(matrix, buffer);\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer, Matrix* matrix) {\n    NOTREACHED();\n    return false;\n  }\n\n  static size_t EstimateSize(const Matrix& matrix) {\n    return Copyable<std::decay_t<Derived>>::EstimateSize(matrix);\n  }\n};\n\ntemplate <typename Field, int Size, int MaxSize>\nclass Copyable<Eigen::DiagonalMatrix<Field, Size, MaxSize>> {\n public:\n  using DiagonalMatrix = Eigen::DiagonalMatrix<Field, Size, MaxSize>;\n  using DiagonalVector = typename DiagonalMatrix::DiagonalVectorType;\n\n  static bool WriteTo(const DiagonalMatrix& matrix, Buffer* buffer) {\n    if (!buffer->WriteMany(matrix.rows())) return false;\n    const DiagonalVector& diagonal = matrix.diagonal();\n    for (Eigen::Index i = 0; i < diagonal.size(); ++i) {\n      if (!buffer->Write(diagonal.data()[i])) return false;\n    }\n    return true;\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer, DiagonalMatrix* matrix) {\n    Eigen::Index size;\n    DiagonalVector vector_tmp;\n    if (!buffer.ReadMany(&size)) return false;\n    if (Size != Eigen::Dynamic) {\n      if (size != Size) return false;\n    } else {\n      vector_tmp.resize(size);\n    }\n    for (Eigen::Index i = 0; i < vector_tmp.size(); ++i) {\n      if (!buffer.Read(&vector_tmp.data()[i])) return false;\n    }\n    *matrix = DiagonalMatrix(std::move(vector_tmp));\n    return true;\n  }\n\n  static size_t EstimateSize(const DiagonalMatrix& matrix) {\n    return matrix.rows() * sizeof(Field) + sizeof(Eigen::Index);\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\nnamespace Eigen::internal {\n\ntemplate <typename T>\nstruct scalar_random_op<\n    T, std::enable_if_t<tachyon::math::FiniteFieldTraits<T>::kIsFiniteField>> {\n  inline const T operator()() const { return T::Random(); }\n};\n\n}  // namespace Eigen::internal\n\n#include \"tachyon/math/matrix/prime_field_num_traits.h\"\n\n#endif  // TACHYON_MATH_MATRIX_MATRIX_TYPES_H_\n"
  },
  {
    "path": "tachyon/math/matrix/matrix_types_unittest.cc",
    "content": "#include \"tachyon/math/matrix/matrix_types.h\"\n\n#include <vector>\n\n#include \"tachyon/base/buffer/vector_buffer.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n\nnamespace tachyon::math {\n\nclass MatrixTypesTest : public FiniteFieldTest<GF7> {};\n\nTEST_F(MatrixTypesTest, CopyableDynamicMatrix) {\n  Matrix<GF7> expected{\n      {GF7(0), GF7(1), GF7(2)},\n      {GF7(3), GF7(4), GF7(5)},\n      {GF7(6), GF7(0), GF7(1)},\n  };\n\n  base::Uint8VectorBuffer write_buf;\n  ASSERT_TRUE(write_buf.Grow(base::EstimateSize(expected)));\n  ASSERT_TRUE(write_buf.Write(expected));\n  ASSERT_TRUE(write_buf.Done());\n\n  {\n    write_buf.set_buffer_offset(0);\n    Matrix<GF7, 2, 3> value;\n    ASSERT_FALSE(write_buf.Read(&value));\n  }\n  {\n    write_buf.set_buffer_offset(0);\n    Matrix<GF7, 3, 2> value;\n    ASSERT_FALSE(write_buf.Read(&value));\n  }\n  {\n    write_buf.set_buffer_offset(0);\n    Matrix<GF7, 3, 3> value;\n    ASSERT_TRUE(write_buf.Read(&value));\n    EXPECT_EQ(value, expected);\n  }\n  {\n    write_buf.set_buffer_offset(0);\n    Matrix<GF7> value;\n    ASSERT_TRUE(write_buf.Read(&value));\n    EXPECT_EQ(value, expected);\n  }\n}\n\nTEST_F(MatrixTypesTest, Copyable3x3Matrix) {\n  Matrix<GF7, 3, 3> expected{\n      {GF7(0), GF7(1), GF7(2)},\n      {GF7(3), GF7(4), GF7(5)},\n      {GF7(6), GF7(0), GF7(1)},\n  };\n\n  base::Uint8VectorBuffer write_buf;\n  ASSERT_TRUE(write_buf.Grow(base::EstimateSize(expected)));\n  ASSERT_TRUE(write_buf.Write(expected));\n  ASSERT_TRUE(write_buf.Done());\n\n  {\n    write_buf.set_buffer_offset(0);\n    Matrix<GF7, 2, 3> value;\n    ASSERT_FALSE(write_buf.Read(&value));\n  }\n  {\n    write_buf.set_buffer_offset(0);\n    Matrix<GF7, 3, 2> value;\n    ASSERT_FALSE(write_buf.Read(&value));\n  }\n  {\n    write_buf.set_buffer_offset(0);\n    Matrix<GF7, 3, 3> value;\n    ASSERT_TRUE(write_buf.Read(&value));\n    EXPECT_EQ(value, expected);\n  }\n  {\n    write_buf.set_buffer_offset(0);\n    Matrix<GF7> value;\n    ASSERT_TRUE(write_buf.Read(&value));\n    EXPECT_EQ(value, expected);\n  }\n}\n\nTEST_F(MatrixTypesTest, CopyableDynamicMap) {\n  std::vector<GF7> data = {\n      GF7(0), GF7(1), GF7(2), GF7(3), GF7(4), GF7(5), GF7(6), GF7(0), GF7(1),\n  };\n\n  Eigen::Map<Matrix<GF7>> expected = Eigen::Map<Matrix<GF7>>(data.data(), 3, 3);\n\n  base::Uint8VectorBuffer write_buf;\n  ASSERT_TRUE(write_buf.Grow(base::EstimateSize(expected)));\n  ASSERT_TRUE(write_buf.Write(expected));\n  ASSERT_TRUE(write_buf.Done());\n\n  {\n    write_buf.set_buffer_offset(0);\n    Matrix<GF7, 2, 3> value;\n    ASSERT_FALSE(write_buf.Read(&value));\n  }\n  {\n    write_buf.set_buffer_offset(0);\n    Matrix<GF7, 3, 2> value;\n    ASSERT_FALSE(write_buf.Read(&value));\n  }\n  {\n    write_buf.set_buffer_offset(0);\n    Matrix<GF7, 3, 3> value;\n    ASSERT_TRUE(write_buf.Read(&value));\n    EXPECT_EQ(value, expected);\n  }\n  {\n    write_buf.set_buffer_offset(0);\n    Matrix<GF7> value;\n    ASSERT_TRUE(write_buf.Read(&value));\n    EXPECT_EQ(value, expected);\n  }\n}\n\nTEST_F(MatrixTypesTest, Copyable3x3Map) {\n  std::vector<GF7> data = {\n      GF7(0), GF7(1), GF7(2), GF7(3), GF7(4), GF7(5), GF7(6), GF7(0), GF7(1),\n  };\n\n  Eigen::Map<Matrix<GF7, 3, 3>> expected =\n      Eigen::Map<Matrix<GF7, 3, 3>>(data.data());\n\n  base::Uint8VectorBuffer write_buf;\n  ASSERT_TRUE(write_buf.Grow(base::EstimateSize(expected)));\n  ASSERT_TRUE(write_buf.Write(expected));\n  ASSERT_TRUE(write_buf.Done());\n\n  {\n    write_buf.set_buffer_offset(0);\n    Matrix<GF7, 2, 3> value;\n    ASSERT_FALSE(write_buf.Read(&value));\n  }\n  {\n    write_buf.set_buffer_offset(0);\n    Matrix<GF7, 3, 2> value;\n    ASSERT_FALSE(write_buf.Read(&value));\n  }\n  {\n    write_buf.set_buffer_offset(0);\n    Matrix<GF7, 3, 3> value;\n    ASSERT_TRUE(write_buf.Read(&value));\n    EXPECT_EQ(value, expected);\n  }\n  {\n    write_buf.set_buffer_offset(0);\n    Matrix<GF7> value;\n    ASSERT_TRUE(write_buf.Read(&value));\n    EXPECT_EQ(value, expected);\n  }\n}\n\nTEST_F(MatrixTypesTest, CopyableDynamicDiagonalMatrix) {\n  DiagonalMatrix<GF7> expected{{GF7(1), GF7(2), GF7(3)}};\n\n  base::Uint8VectorBuffer write_buf;\n  ASSERT_TRUE(write_buf.Grow(base::EstimateSize(expected)));\n  ASSERT_TRUE(write_buf.Write(expected));\n  ASSERT_TRUE(write_buf.Done());\n\n  {\n    write_buf.set_buffer_offset(0);\n    DiagonalMatrix<GF7, 2> value;\n    ASSERT_FALSE(write_buf.Read(&value));\n  }\n  {\n    write_buf.set_buffer_offset(0);\n    DiagonalMatrix<GF7, 3> value;\n    ASSERT_TRUE(write_buf.Read(&value));\n    EXPECT_EQ(value.diagonal(), expected.diagonal());\n  }\n  {\n    write_buf.set_buffer_offset(0);\n    DiagonalMatrix<GF7> value;\n    ASSERT_TRUE(write_buf.Read(&value));\n    EXPECT_EQ(value.diagonal(), expected.diagonal());\n  }\n}\n\nTEST_F(MatrixTypesTest, Copyable3x3DiagonalMatrix) {\n  DiagonalMatrix<GF7, 3> expected{GF7(1), GF7(2), GF7(3)};\n\n  base::Uint8VectorBuffer write_buf;\n  ASSERT_TRUE(write_buf.Grow(base::EstimateSize(expected)));\n  ASSERT_TRUE(write_buf.Write(expected));\n  ASSERT_TRUE(write_buf.Done());\n\n  {\n    write_buf.set_buffer_offset(0);\n    DiagonalMatrix<GF7, 2> value;\n    ASSERT_FALSE(write_buf.Read(&value));\n  }\n  {\n    write_buf.set_buffer_offset(0);\n    DiagonalMatrix<GF7, 3> value;\n    ASSERT_TRUE(write_buf.Read(&value));\n    EXPECT_EQ(value.diagonal(), expected.diagonal());\n  }\n  {\n    write_buf.set_buffer_offset(0);\n    DiagonalMatrix<GF7> value;\n    ASSERT_TRUE(write_buf.Read(&value));\n    EXPECT_EQ(value.diagonal(), expected.diagonal());\n  }\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/matrix/matrix_utils.h",
    "content": "#ifndef TACHYON_MATH_MATRIX_MATRIX_UTILS_H_\n#define TACHYON_MATH_MATRIX_MATRIX_UTILS_H_\n\n#include <numeric>\n#include <utility>\n#include <vector>\n\n#include \"third_party/eigen3/Eigen/Core\"\n\n#include \"tachyon/base/bits.h\"\n#include \"tachyon/base/compiler_specific.h\"\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/openmp_util.h\"\n#include \"tachyon/base/profiler.h\"\n#include \"tachyon/math/finite_fields/extended_packed_field_traits_forward.h\"\n#include \"tachyon/math/finite_fields/extension_field_traits_forward.h\"\n#include \"tachyon/math/finite_fields/finite_field_traits.h\"\n#include \"tachyon/math/finite_fields/packed_field_traits_forward.h\"\n#include \"tachyon/math/matrix/matrix_types.h\"\n\nnamespace tachyon::math {\n\n// See https://eigen.tuxfamily.org/dox/TopicCustomizing_NullaryExpr.html\ntemplate <class ArgType>\nstruct CirculantHelper {\n  typedef Eigen::Matrix<typename ArgType::Scalar, ArgType::SizeAtCompileTime,\n                        ArgType::SizeAtCompileTime, Eigen::ColMajor,\n                        ArgType::MaxSizeAtCompileTime,\n                        ArgType::MaxSizeAtCompileTime>\n      MatrixType;\n};\n\ntemplate <class ArgType>\nclass CirculantFunctor {\n public:\n  explicit CirculantFunctor(const ArgType& arg) : vec_(arg) {}\n\n  const typename ArgType::Scalar& operator()(Eigen::Index row,\n                                             Eigen::Index col) const {\n    Eigen::Index index = row - col;\n    if (index < 0) index += vec_.size();\n    return vec_(index);\n  }\n\n private:\n  const ArgType& vec_;\n};\n\ntemplate <class ArgType>\nEigen::CwiseNullaryOp<CirculantFunctor<ArgType>,\n                      typename CirculantHelper<ArgType>::MatrixType>\nMakeCirculant(const Eigen::MatrixBase<ArgType>& arg) {\n  typedef typename CirculantHelper<ArgType>::MatrixType MatrixType;\n  return MatrixType::NullaryExpr(arg.size(), arg.size(),\n                                 CirculantFunctor<ArgType>(arg.derived()));\n}\n\n// Creates a vector of packed fields for a given matrix row. If the length\n// of the row is not a multiple of |PackedField::N|, the last |PackedField|\n// element populates leftover values with |F::Zero()|.\ntemplate <typename Expr, int BlockRows, int BlockCols, bool InnerPanel,\n          typename PackedField =\n              typename PackedFieldTraits<typename Expr::Scalar>::PackedField>\nstd::vector<PackedField> PackRowHorizontallyPadded(\n    const Eigen::Block<Expr, BlockRows, BlockCols, InnerPanel>& matrix_row) {\n  using F = typename FiniteFieldTraits<PackedField>::PrimeField;\n\n  size_t cols = static_cast<size_t>(matrix_row.cols());\n  size_t size = (cols + PackedField::N - 1) / PackedField::N;\n  return base::CreateVector(size, [cols, &matrix_row](size_t i) {\n    size_t col = i * PackedField::N;\n    return PackedField::From([col, cols, &matrix_row](size_t c) {\n      if (LIKELY(col + c < cols)) {\n        return matrix_row[col + c];\n      } else {\n        return F::Zero();\n      }\n    });\n  });\n}\n\n// Packs |PackedField::N| rows, starting at the given row index. Each\n// |PackedField::N| selected elements in a column are converted into a\n// PackedField. Rows included wrap around to the starting index 0 to fully\n// populate the packed fields.\ntemplate <typename PackedField, typename Derived>\nstd::vector<PackedField> PackRowVertically(\n    const Eigen::MatrixBase<Derived>& matrix, size_t row) {\n  using Scalar = typename Eigen::internal::traits<Derived>::Scalar;\n  if constexpr (FiniteFieldTraits<Scalar>::kIsExtensionField) {\n    constexpr size_t kDegree =\n        ExtensionFieldTraits<Scalar>::kDegreeOverBasePrimeField;\n    size_t num = matrix.cols() * kDegree;\n    return base::CreateVector(num, [row, &matrix](size_t n) {\n      size_t col = n / kDegree;\n      size_t idx = n - col * kDegree;\n      return PackedField::From([row, col, idx, &matrix](size_t i) {\n        return matrix((row + i) % matrix.rows(), col)[idx];\n      });\n    });\n  } else {\n    return base::CreateVector(matrix.cols(), [row, &matrix](size_t col) {\n      return PackedField::From([row, col, &matrix](size_t i) {\n        return matrix((row + i) % matrix.rows(), col);\n      });\n    });\n  }\n}\n\n// Swaps rows of a |Eigen::MatrixBase| such that each row is changed to the row\n// accessed with the reversed bits of the current index. Crashes if the number\n// of rows is not a power of two.\ntemplate <typename Derived>\n#if HAS_ATTRIBUTE(optimize)\nvoid __attribute__((optimize(3)))\nReverseMatrixIndexBits(Eigen::MatrixBase<Derived>& mat) {\n#else\nvoid ReverseMatrixIndexBits(Eigen::MatrixBase<Derived>& mat) {\n#endif\n  TRACE_EVENT(\"Utils\", \"MatrixUtils::ReverseMatrixIndexBits\");\n\n  static_assert(Derived::IsRowMajor);\n\n  size_t rows = static_cast<size_t>(mat.rows());\n  if (rows == 0) {\n    return;\n  }\n  uint32_t log_n = base::bits::CheckedLog2(rows);\n\n  OMP_PARALLEL_FOR(size_t row = 1; row < rows; ++row) {\n    size_t ridx = base::bits::ReverseBitsLen(row, log_n);\n    if (row < ridx) {\n      absl::Span<uint8_t> row1(\n          reinterpret_cast<uint8_t*>(mat.derived().data() + row * mat.cols()),\n          mat.cols() * sizeof(typename Derived::Scalar));\n      absl::Span<uint8_t> row2(\n          reinterpret_cast<uint8_t*>(mat.derived().data() + ridx * mat.cols()),\n          mat.cols() * sizeof(typename Derived::Scalar));\n\n      std::swap_ranges(row1.begin(), row1.end(), row2.begin());\n    }\n  }\n}\n\n// Returns a vector of size |num_chunks| that holds submatrices with\n// |num_chunks| amount of rows of a given matrix |mat| using vertical\n// striding.\ntemplate <typename Derived>\nstd::vector<Eigen::Block<Derived>> SplitMat(Eigen::Index num_chunks,\n                                            Eigen::MatrixBase<Derived>& mat) {\n  Eigen::Index total_span = mat.rows() - num_chunks + 1;\n  CHECK_GE(mat.rows(), num_chunks);\n  Eigen::Index num_cols = mat.cols();\n  return base::CreateVector(num_chunks, [&mat, total_span, num_cols](size_t i) {\n    return mat.block(i, 0, total_span, num_cols);\n  });\n}\n\ntemplate <typename ExtField, typename Derived>\nstd::vector<ExtField> DotExtPowers(const Eigen::MatrixBase<Derived>& mat,\n                                   const ExtField& base) {\n  using F = typename ExtensionFieldTraits<ExtField>::BaseField;\n  using PackedField = typename PackedFieldTraits<F>::PackedField;\n  using ExtendedPackedField =\n      typename ExtendedPackedFieldTraits<ExtField>::ExtendedPackedField;\n  Eigen::Index rows = mat.rows();\n  size_t packed_n = PackedField::N;\n  std::vector<ExtendedPackedField> packed_ext_powers =\n      ExtField::GetExtendedPackedPowers(\n          base, ((static_cast<size_t>(mat.cols()) + packed_n - 1) / packed_n) *\n                    packed_n);\n  return base::CreateVectorParallel(rows, [&mat,\n                                           &packed_ext_powers](Eigen::Index r) {\n    std::vector<PackedField> row_packed = PackRowHorizontallyPadded(mat.row(r));\n    ExtendedPackedField packed_sum_of_packed(ExtendedPackedField::Zero());\n    for (size_t i = 0; i < row_packed.size(); ++i) {\n      packed_sum_of_packed += packed_ext_powers[i] * row_packed[i];\n    }\n    ExtField ret(ExtField::Zero());\n    for (size_t i = 0; i < ExtField::ExtensionDegree(); ++i) {\n      const PackedField& packed = packed_sum_of_packed[i];\n      for (size_t j = 0; j < PackedField::N; ++j) {\n        ret[i] += packed[j];\n      }\n    }\n    return ret;\n  });\n}\n\ntemplate <typename Derived>\nEigen::Map<Derived> Map(Eigen::PlainObjectBase<Derived>& mat) {\n  return Eigen::Map<Derived>(mat.data(), mat.rows(), mat.cols());\n}\n\ntemplate <typename Derived>\nEigen::Map<const Derived> Map(const Eigen::PlainObjectBase<Derived>& mat) {\n  return Eigen::Map<const Derived>(mat.data(), mat.rows(), mat.cols());\n}\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_MATRIX_MATRIX_UTILS_H_\n"
  },
  {
    "path": "tachyon/math/matrix/matrix_utils_unittest.cc",
    "content": "#include \"tachyon/math/matrix/matrix_utils.h\"\n\n#include \"tachyon/math/finite_fields/baby_bear/baby_bear4.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n#include \"tachyon/math/matrix/matrix_types.h\"\n\nnamespace tachyon::math {\n\nclass MatrixUtilsTest : public FiniteFieldTest<GF7> {};\n\nTEST_F(MatrixUtilsTest, Circulant) {\n  Matrix<GF7> circulant = MakeCirculant(Vector<GF7>{{GF7(2), GF7(3), GF7(4)}});\n  Matrix<GF7> expected{{\n      {GF7(2), GF7(4), GF7(3)},\n      {GF7(3), GF7(2), GF7(4)},\n      {GF7(4), GF7(3), GF7(2)},\n  }};\n  EXPECT_EQ(circulant, expected);\n}\n\nclass MatrixPackingTest : public FiniteFieldTest<PackedBabyBear> {};\n\nTEST_F(MatrixPackingTest, PackRowVerticallyWithPrimeField) {\n  constexpr size_t N = PackedBabyBear::N;\n  constexpr size_t R = 3;\n\n  Matrix<BabyBear> matrix = Matrix<BabyBear>::Random(N, N);\n  std::vector<PackedBabyBear> packed_values =\n      PackRowVertically<PackedBabyBear>(matrix, R);\n  ASSERT_EQ(packed_values.size(), N);\n  for (size_t i = 0; i < packed_values.size(); ++i) {\n    for (size_t j = 0; j < N; ++j) {\n      EXPECT_EQ(packed_values[i][j], matrix((R + j) % matrix.rows(), i));\n    }\n  }\n}\n\nTEST_F(MatrixPackingTest, PackRowVerticallyWithExtensionField) {\n  constexpr size_t N = PackedBabyBear::N;\n  constexpr size_t R = 3;\n\n  Matrix<BabyBear4> matrix = Matrix<BabyBear4>::Random(N, N);\n  std::vector<PackedBabyBear> packed_values =\n      PackRowVertically<PackedBabyBear>(matrix, R);\n  ASSERT_EQ(packed_values.size(), 4 * N);\n  for (size_t i = 0; i < packed_values.size(); ++i) {\n    for (size_t j = 0; j < N; ++j) {\n      size_t col = i / 4;\n      size_t idx = i % 4;\n      EXPECT_EQ(packed_values[i][j], matrix((R + j) % matrix.rows(), col)[idx]);\n    }\n  }\n}\n\nTEST_F(MatrixPackingTest, SplitMat) {\n  Matrix<BabyBear> matrix = Matrix<BabyBear>::Random(10, 10);\n  std::vector<Eigen::Block<Matrix<BabyBear>>> result = SplitMat(4, matrix);\n  for (size_t i = 0; i < result.size(); ++i) {\n    EXPECT_EQ(result[i], matrix.block(i, 0, 7, matrix.cols()));\n  }\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/matrix/prime_field_num_traits.h",
    "content": "#ifndef TACHYON_MATH_MATRIX_PRIME_FIELD_NUM_TRAITS_H_\n#define TACHYON_MATH_MATRIX_PRIME_FIELD_NUM_TRAITS_H_\n\n#include <type_traits>\n\n#include \"third_party/eigen3/Eigen/Core\"\n\n#include \"tachyon/math/finite_fields/prime_field_base.h\"\n#include \"tachyon/math/matrix/cost_calculator_forward.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename F>\nstruct CostCalculator<\n    F, std::enable_if_t<std::is_base_of_v<PrimeFieldBase<F>, F>>> {\n  constexpr static size_t kLimbNums = F::kLimbNums;\n  // NOLINTNEXTLINE(whitespace/operators)\n  using NumTraitsType = std::conditional_t<F::Config::kModulusBits <= 32,\n                                           Eigen::NumTraits<uint32_t>,\n                                           Eigen::NumTraits<uint64_t>>;\n\n  constexpr static int ComputeReadCost() {\n    return static_cast<int>(kLimbNums * NumTraitsType::ReadCost);\n  }\n  constexpr static int ComputeAddCost() {\n    // In general, c = (a + b) % M = (a + b) > M ? (a + b) - M : (a + b)\n    return static_cast<int>(kLimbNums * NumTraitsType::AddCost * 3 / 2);\n  }\n  constexpr static int ComputeMulCost() {\n    // In general, c = (a * b) % M = (a * b) - [(a * b) / M] * M\n    return static_cast<int>(\n        kLimbNums * (4 * NumTraitsType::MulCost + NumTraitsType::AddCost));\n  }\n};\n\n}  // namespace tachyon::math\n\nnamespace Eigen {\n\ntemplate <typename F>\nstruct NumTraits<\n    F, std::enable_if_t<std::is_base_of_v<tachyon::math::PrimeFieldBase<F>, F>>>\n    : GenericNumTraits<F> {\n  enum {\n    IsInteger = 1,\n    IsField = 1,\n    IsSigned = 0,\n    IsComplex = 0,\n    RequireInitialization = 0,\n    ReadCost = tachyon::math::CostCalculator<F>::ComputeReadCost(),\n    AddCost = tachyon::math::CostCalculator<F>::ComputeAddCost(),\n    MulCost = tachyon::math::CostCalculator<F>::ComputeMulCost(),\n  };\n};\n\n}  // namespace Eigen\n\n#endif  // TACHYON_MATH_MATRIX_PRIME_FIELD_NUM_TRAITS_H_\n"
  },
  {
    "path": "tachyon/math/matrix/sparse/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"sparse_matrix\",\n    hdrs = [\"sparse_matrix.h\"],\n    deps = [\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/base/ranges:algorithm\",\n        \"//tachyon/base/strings:string_util\",\n        \"@com_google_absl//absl/types:span\",\n        \"@eigen_archive//:eigen3\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"sparse_unittests\",\n    srcs = [\"sparse_matrix_unittest.cc\"],\n    deps = [\n        \":sparse_matrix\",\n        \"//tachyon/math/finite_fields/test:gf7\",\n        \"//tachyon/math/matrix:prime_field_num_traits\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/math/matrix/sparse/sparse_matrix.h",
    "content": "#ifndef TACHYON_MATH_MATRIX_SPARSE_SPARSE_MATRIX_H_\n#define TACHYON_MATH_MATRIX_SPARSE_SPARSE_MATRIX_H_\n\n#include <algorithm>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"absl/types/span.h\"\n#include \"third_party/eigen3/Eigen/SparseCore\"\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/ranges/algorithm.h\"\n#include \"tachyon/base/strings/string_util.h\"\n\nnamespace tachyon::math {\n\n// A sparse matrix primarily consists of zero values.\n// Specific algorithms are optimized for sparse matrices as compared to general\n// matrices.\ntemplate <typename T>\nclass CSRSparseMatrix;\n\n// The ELL format utilizes two arrays for storing sparse matrix data.\n// One array contains the matrix elements while the other has the\n// column indices from the original matrix.\n// For example, the following matrix:\n//   1 0 4 0\n//   0 0 0 0\n//   0 5 2 2\n//   3 0 0 1\n// is stored as:\n//   values: [[1, 4], [], [5, 2, 2], [3, 1]]\n//   indices: [[0, 2], [], [1, 2, 3], [0, 3]]\ntemplate <typename T>\nclass ELLSparseMatrix {\n public:\n  struct Element {\n    size_t index;\n    T value;\n\n    bool operator<(const Element& other) const { return index < other.index; }\n    bool operator==(const Element& other) const {\n      return index == other.index && value == other.value;\n    }\n    bool operator!=(const Element& other) const { return !operator==(other); }\n  };\n\n  using Elements = std::vector<Element>;\n\n  ELLSparseMatrix() = default;\n  explicit ELLSparseMatrix(const std::vector<Elements>& elements_list)\n      : elements_list_(elements_list) {}\n  explicit ELLSparseMatrix(std::vector<Elements>&& elements_list)\n      : elements_list_(std::move(elements_list)) {}\n  ELLSparseMatrix(const ELLSparseMatrix& other) = default;\n  ELLSparseMatrix& operator=(const ELLSparseMatrix& other) = default;\n  ELLSparseMatrix(ELLSparseMatrix&& other) = default;\n  ELLSparseMatrix& operator=(ELLSparseMatrix&& other) = default;\n\n  template <int Options, typename StorageIndex,\n            typename InnerIterator = typename Eigen::SparseMatrix<\n                T, Options, StorageIndex>::InnerIterator>\n  static ELLSparseMatrix FromEigenSparseMatrix(\n      const Eigen::SparseMatrix<T, Options, StorageIndex>& matrix) {\n    ELLSparseMatrix ret;\n    ret.elements_list_.resize(matrix.rows());\n    for (int k = 0; k < matrix.outerSize(); ++k) {\n      for (InnerIterator it(matrix, k); it; ++it) {\n        ret.elements_list_[it.row()].push_back(\n            {static_cast<size_t>(it.col()), it.value()});\n      }\n    }\n    ret.Sort();\n    return ret;\n  }\n\n  static ELLSparseMatrix FromCSR(const CSRSparseMatrix<T>& matrix) {\n    return matrix.ToELL();\n  }\n\n  std::vector<std::vector<T>> GetData() const {\n    return base::Map(elements_list_, [](const Elements& elements) {\n      return base::Map(elements,\n                       [](const Element& element) { return element.value; });\n    });\n  }\n\n  std::vector<std::vector<size_t>> GetColumnIndices() const {\n    return base::Map(elements_list_, [](const Elements& elements) {\n      return base::Map(elements,\n                       [](const Element& element) { return element.index; });\n    });\n  }\n\n  size_t MaxRows() const { return elements_list_.size(); }\n\n  // NOTE: Returns a valid value when |this| is sorted.\n  size_t MaxCols() const {\n    auto it = std::max_element(elements_list_.begin(), elements_list_.end(),\n                               [](const Elements& a, const Elements& b) {\n                                 return a.back().index < b.back().index;\n                               });\n    return it->back().index + 1;\n  }\n\n  size_t NonZeros() const {\n    return std::accumulate(elements_list_.begin(), elements_list_.end(), 0,\n                           [](size_t total, const Elements& elements) {\n                             return total + elements.size();\n                           });\n  }\n\n  // NOTE: Returns a valid value when |this| and |other| are sorted.\n  bool operator==(const ELLSparseMatrix& other) const {\n    return elements_list_ == other.elements_list_;\n  }\n  bool operator!=(const ELLSparseMatrix& other) const {\n    return !operator==(other);\n  }\n\n  void Sort() {\n    for (Elements& elements : elements_list_) {\n      base::ranges::sort(elements.begin(), elements.end());\n    }\n  }\n\n  bool IsSorted() const {\n    for (const Elements& elements : elements_list_) {\n      if (!base::ranges::is_sorted(elements.begin(), elements.end()))\n        return false;\n    }\n    return true;\n  }\n\n  // NOTE: Returns a valid value when |this| is sorted.\n  CSRSparseMatrix<T> ToCSR() const {\n    CSRSparseMatrix<T> ret;\n    ret.row_ptrs_.reserve(elements_list_.size() + 1);\n    ret.row_ptrs_.push_back(0);\n    for (const Elements& elements : elements_list_) {\n      ret.row_ptrs_.push_back(ret.row_ptrs_.back() + elements.size());\n      for (const Element& element : elements) {\n        ret.elements_.push_back({element.index, element.value});\n      }\n    }\n    return ret;\n  }\n\n  Eigen::SparseMatrix<T> ToEigenSparseMatrix() const {\n    using StorageIndex = typename Eigen::SparseMatrix<T>::StorageIndex;\n    std::vector<Eigen::Triplet<T>> coefficients;\n    for (size_t i = 0; i < elements_list_.size(); ++i) {\n      for (const Element& element : elements_list_[i]) {\n        coefficients.push_back({static_cast<StorageIndex>(i),\n                                static_cast<StorageIndex>(element.index),\n                                element.value});\n      }\n    }\n    Eigen::SparseMatrix<T> ret(MaxRows(), MaxCols());\n    ret.setFromTriplets(coefficients.begin(), coefficients.end());\n    return ret;\n  }\n\n  std::string ToString() const {\n    std::stringstream ss;\n    ss << \"{\\n\";\n    ss << \"  data: \" << base::Container2DToString(GetData()) << \"\\n\";\n    ss << \"  col_indices: \" << base::Container2DToString(GetColumnIndices())\n       << \"\\n\";\n    ss << \"}\";\n    return ss.str();\n  }\n\n private:\n  friend class CSRSparseMatrix<T>;\n\n  std::vector<Elements> elements_list_;\n};\n\n// CSR(Compressed Sparse Row) format is a sparse matrix format that stores only\n// non-zero values in a vector. The |row_ptrs| vector stores the index of the\n// first element of each row in the |elements| vector.\n// For example, the following matrix:\n//   1 0 4 0\n//   0 0 0 0\n//   0 5 2 2\n//   3 0 0 1\n// is stored as:\n//   |elements|: [1, 4, 5, 2, 2, 3, 1]\n//   |col_indices|: [0, 2, 1, 2, 3, 0, 3]\n//   |row_ptrs|: [0, 2, 2, 5, 7]\ntemplate <typename T>\nclass CSRSparseMatrix {\n public:\n  struct Element {\n    size_t index;\n    T value;\n\n    bool operator<(const Element& other) const { return index < other.index; }\n    bool operator==(const Element& other) const {\n      return index == other.index && value == other.value;\n    }\n    bool operator!=(const Element& other) const { return !operator==(other); }\n  };\n\n  using Elements = std::vector<Element>;\n\n  CSRSparseMatrix() = default;\n  CSRSparseMatrix(const Elements& elements, const std::vector<size_t>& row_ptrs)\n      : elements_(elements), row_ptrs_(row_ptrs) {}\n  CSRSparseMatrix(Elements&& elements, std::vector<size_t>&& row_ptrs)\n      : elements_(std::move(elements)), row_ptrs_(std::move(row_ptrs)) {}\n  CSRSparseMatrix(const CSRSparseMatrix& other) = default;\n  CSRSparseMatrix& operator=(const CSRSparseMatrix& other) = default;\n  CSRSparseMatrix(CSRSparseMatrix&& other) = default;\n  CSRSparseMatrix& operator=(CSRSparseMatrix&& other) = default;\n\n  template <int Options, typename StorageIndex,\n            typename InnerIterator = typename Eigen::SparseMatrix<\n                T, Options, StorageIndex>::InnerIterator>\n  static CSRSparseMatrix FromEigenSparseMatrix(\n      const Eigen::SparseMatrix<T, Options, StorageIndex>& matrix) {\n    return FromELL(ELLSparseMatrix<T>::FromEigenSparseMatrix(matrix));\n  }\n\n  static CSRSparseMatrix FromELL(const ELLSparseMatrix<T>& matrix) {\n    return matrix.ToCSR();\n  }\n\n  const std::vector<size_t>& row_ptrs() const { return row_ptrs_; }\n\n  std::vector<T> GetData() const {\n    return base::Map(elements_,\n                     [](const Element& element) { return element.value; });\n  }\n\n  std::vector<size_t> GetColumnIndices() const {\n    return base::Map(elements_,\n                     [](const Element& element) { return element.index; });\n  }\n\n  size_t MaxRows() const {\n    if (row_ptrs_.empty()) return 0;\n    return row_ptrs_.size() - 1;\n  }\n\n  // NOTE: Returns a valid value when |this| is sorted.\n  size_t MaxCols() const {\n    auto it = std::max_element(\n        elements_.begin(), elements_.end(),\n        [](const Element& a, const Element& b) { return a.index < b.index; });\n    return it->index + 1;\n  }\n\n  size_t NonZeros() const { return elements_.size(); }\n\n  // Returns a valid value when |this| and |other| are sorted.\n  bool operator==(const CSRSparseMatrix& other) const {\n    return elements_ == other.elements_ && row_ptrs_ == other.row_ptrs_;\n  }\n  bool operator!=(const CSRSparseMatrix& other) const {\n    return !operator==(other);\n  }\n\n  void Sort() {\n    for (size_t i = 0; i < row_ptrs_.size(); ++i) {\n      if (i != row_ptrs_.size() - 1) {\n        base::ranges::sort(elements_.begin() + row_ptrs_[i],\n                           elements_.begin() + row_ptrs_[i + 1]);\n      }\n    }\n    base::ranges::sort(row_ptrs_.begin(), row_ptrs_.end());\n  }\n\n  bool IsSorted() const {\n    for (size_t i = 0; i < row_ptrs_.size(); ++i) {\n      if (i != row_ptrs_.size() - 1) {\n        if (!base::ranges::is_sorted(elements_.begin() + row_ptrs_[i],\n                                     elements_.begin() + row_ptrs_[i + 1]))\n          return false;\n      }\n    }\n    return base::ranges::is_sorted(row_ptrs_);\n  }\n\n  // NOTE: Returns a valid value when |this| is sorted.\n  ELLSparseMatrix<T> ToELL() const {\n    using ELLElements = typename ELLSparseMatrix<T>::Elements;\n    ELLSparseMatrix<T> ret;\n    auto it = elements_.begin();\n    for (size_t i = 0; i < row_ptrs_.size(); ++i) {\n      if (i != row_ptrs_.size() - 1) {\n        size_t cols = row_ptrs_[i + 1] - row_ptrs_[i];\n        ELLElements elements;\n        elements.reserve(cols);\n        for (size_t j = 0; j < cols; ++j, ++it) {\n          elements.push_back({it->index, it->value});\n        }\n        ret.elements_list_.push_back(std::move(elements));\n      }\n    }\n    return ret;\n  }\n\n  Eigen::SparseMatrix<T> ToEigenSparseMatrix() const {\n    using StorageIndex = typename Eigen::SparseMatrix<T>::StorageIndex;\n    std::vector<Eigen::Triplet<T>> coefficients;\n    auto it = elements_.begin();\n    for (size_t i = 0; i < row_ptrs_.size(); ++i) {\n      if (i != row_ptrs_.size() - 1) {\n        size_t cols = row_ptrs_[i + 1] - row_ptrs_[i];\n        for (size_t j = 0; j < cols; ++j, ++it) {\n          coefficients.push_back({static_cast<StorageIndex>(i),\n                                  static_cast<StorageIndex>(it->index),\n                                  it->value});\n        }\n      }\n    }\n    Eigen::SparseMatrix<T> ret(MaxRows(), MaxCols());\n    ret.setFromTriplets(coefficients.begin(), coefficients.end());\n    return ret;\n  }\n\n  std::string ToString() const {\n    std::stringstream ss;\n    ss << \"{\\n\";\n    ss << \"  data: \" << base::ContainerToString(GetData()) << \"\\n\";\n    ss << \"  col_indices: \" << base::ContainerToString(GetColumnIndices())\n       << \"\\n\";\n    ss << \"  row_ptrs: \" << base::ContainerToString(row_ptrs_) << \"\\n\";\n    ss << \"}\";\n    return ss.str();\n  }\n\n private:\n  friend class ELLSparseMatrix<T>;\n\n  Elements elements_;\n  std::vector<size_t> row_ptrs_;\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_MATRIX_SPARSE_SPARSE_MATRIX_H_\n"
  },
  {
    "path": "tachyon/math/matrix/sparse/sparse_matrix_unittest.cc",
    "content": "#include \"tachyon/math/matrix/sparse/sparse_matrix.h\"\n\n#include <vector>\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n#include \"tachyon/math/matrix/prime_field_num_traits.h\"\n\nnamespace tachyon::math {\n\nclass SparseMatrixTest : public testing::Test {\n public:\n  void SetUp() override {\n    std::vector<std::vector<Eigen::Triplet<GF7>>> coefficients_list;\n    eigen_matrices_.push_back(Eigen::SparseMatrix<GF7>(4, 6));\n    coefficients_list.push_back({\n        {0, 3, GF7(1)},\n        {1, 4, GF7(2)},\n        {1, 5, GF7(3)},\n        {2, 0, GF7(4)},\n        {2, 1, GF7(5)},\n        {3, 2, GF7(6)},\n    });\n    for (size_t i = 0; i < eigen_matrices_.size(); ++i) {\n      eigen_matrices_[i].setFromTriplets(coefficients_list[i].begin(),\n                                         coefficients_list[i].end());\n      ell_matrices_.push_back(\n          ELLSparseMatrix<GF7>::FromEigenSparseMatrix(eigen_matrices_[i]));\n      csr_matrices_.push_back(\n          CSRSparseMatrix<GF7>::FromEigenSparseMatrix(eigen_matrices_[i]));\n    }\n  }\n\n protected:\n  std::vector<Eigen::SparseMatrix<GF7>> eigen_matrices_;\n  std::vector<ELLSparseMatrix<GF7>> ell_matrices_;\n  std::vector<CSRSparseMatrix<GF7>> csr_matrices_;\n};\n\nTEST_F(SparseMatrixTest, ELLSparseMatrixFromEigenMatrix) {\n  for (size_t i = 0; i < eigen_matrices_.size(); ++i) {\n    EXPECT_EQ(ELLSparseMatrix<GF7>::FromEigenSparseMatrix(eigen_matrices_[i]),\n              ell_matrices_[i]);\n    EXPECT_EQ(\n        ELLSparseMatrix<GF7>::FromEigenSparseMatrix(\n            Eigen::SparseMatrix<GF7, Eigen::RowMajor>(eigen_matrices_[i])),\n        ell_matrices_[i]);\n  }\n}\n\nTEST_F(SparseMatrixTest, ELLSparseMatrixSort) {\n  // clang-format off\n  ELLSparseMatrix<GF7> test_data({\n    {{3, GF7(1)}             },\n    {{5, GF7(3)}, {4, GF7(2)}},\n    {{0, GF7(4)}, {1, GF7(5)}},\n    {{2, GF7(6)}             },\n  });\n  // clang-format on\n  EXPECT_FALSE(test_data.IsSorted());\n\n  test_data.Sort();\n  EXPECT_TRUE(test_data.IsSorted());\n}\n\nTEST_F(SparseMatrixTest, ELLSparseMatrixGetXXX) {\n  // clang-format off\n  struct {\n    size_t max_cols;\n    size_t non_zeros;\n    std::vector<std::vector<GF7>> data;\n    std::vector<std::vector<size_t>> column_indices;\n  } answers[] = {\n    {\n      6,\n      6,\n      {\n        {{GF7(1)}},\n        {{GF7(2), GF7(3)}},\n        {{GF7(4), GF7(5)}},\n        {{GF7(6)}}\n      },\n      {\n        {std::vector<size_t>{3}},\n        {std::vector<size_t>{4, 5}},\n        {std::vector<size_t>{0, 1}},\n        {std::vector<size_t>{2}}\n      },\n    },\n  };\n  // clang-format on\n  for (size_t i = 0; i < ell_matrices_.size(); ++i) {\n    EXPECT_EQ(ell_matrices_[i].MaxCols(), answers[i].max_cols);\n    EXPECT_EQ(ell_matrices_[i].NonZeros(), answers[i].non_zeros);\n    EXPECT_THAT(ell_matrices_[i].GetData(),\n                testing::ContainerEq(answers[i].data));\n    EXPECT_THAT(ell_matrices_[i].GetColumnIndices(),\n                testing::ContainerEq(answers[i].column_indices));\n  }\n}\n\nTEST_F(SparseMatrixTest, ELLSparseMatrixToCSR) {\n  for (size_t i = 0; i < ell_matrices_.size(); ++i) {\n    EXPECT_EQ(ell_matrices_[i].ToCSR(), csr_matrices_[i]);\n  }\n}\n\nTEST_F(SparseMatrixTest, ELLSparseMatrixToEigenSparseMatrix) {\n  for (size_t i = 0; i < ell_matrices_.size(); ++i) {\n    EXPECT_EQ(ELLSparseMatrix<GF7>::FromEigenSparseMatrix(\n                  ell_matrices_[i].ToEigenSparseMatrix()),\n              ell_matrices_[i]);\n  }\n}\n\nTEST_F(SparseMatrixTest, CSRSparseMatrixFromEigenMatrix) {\n  for (size_t i = 0; i < eigen_matrices_.size(); ++i) {\n    EXPECT_EQ(CSRSparseMatrix<GF7>::FromEigenSparseMatrix(eigen_matrices_[i]),\n              csr_matrices_[i]);\n    EXPECT_EQ(\n        CSRSparseMatrix<GF7>::FromEigenSparseMatrix(\n            Eigen::SparseMatrix<GF7, Eigen::RowMajor>(eigen_matrices_[i])),\n        csr_matrices_[i]);\n  }\n}\n\nTEST_F(SparseMatrixTest, CSRSparseMatrixSort) {\n  // clang-format off\n  CSRSparseMatrix<GF7> test_data(\n    {{3, GF7(1)}, {5, GF7(3)}, {4, GF7(2)}, {0, GF7(4)}, {1, GF7(5)}, {2, GF7(6)}},\n    {0, 1, 3, 5, 6});\n  // clang-format on\n  EXPECT_FALSE(test_data.IsSorted());\n\n  test_data.Sort();\n  EXPECT_TRUE(test_data.IsSorted());\n}\n\nTEST_F(SparseMatrixTest, CSRSparseMatrixGetXXX) {\n  // clang-format off\n  struct {\n    size_t max_cols;\n    size_t non_zeros;\n    std::vector<GF7> data;\n    std::vector<size_t> column_indices;\n  } answers[] = {\n    {\n      6,\n      6,\n      {GF7(1), GF7(2), GF7(3), GF7(4), GF7(5), GF7(6)},\n      {3, 4, 5, 0, 1, 2},\n    },\n  };\n  // clang-format on\n  for (size_t i = 0; i < csr_matrices_.size(); ++i) {\n    EXPECT_EQ(csr_matrices_[i].MaxCols(), answers[i].max_cols);\n    EXPECT_EQ(csr_matrices_[i].NonZeros(), answers[i].non_zeros);\n    EXPECT_THAT(csr_matrices_[i].GetData(),\n                testing::ContainerEq(answers[i].data));\n    EXPECT_THAT(csr_matrices_[i].GetColumnIndices(),\n                testing::ContainerEq(answers[i].column_indices));\n  }\n}\n\nTEST_F(SparseMatrixTest, CSRSparseMatrixToELL) {\n  for (size_t i = 0; i < csr_matrices_.size(); ++i) {\n    EXPECT_EQ(csr_matrices_[i].ToELL(), ell_matrices_[i]);\n  }\n}\n\nTEST_F(SparseMatrixTest, CSRSparseMatrixToEigenSparseMatrix) {\n  for (size_t i = 0; i < csr_matrices_.size(); ++i) {\n    EXPECT_EQ(CSRSparseMatrix<GF7>::FromEigenSparseMatrix(\n                  csr_matrices_[i].ToEigenSparseMatrix()),\n              csr_matrices_[i]);\n  }\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/polynomials/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"evaluation_domain\",\n    hdrs = [\"evaluation_domain.h\"],\n)\n\ntachyon_cc_library(\n    name = \"polynomial\",\n    hdrs = [\n        \"polynomial.h\",\n        \"support_poly_operators.h\",\n    ],\n    deps = [\n        \":polynomial_traits_forward\",\n        \"//tachyon/math/base:ring\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"polynomial_traits_forward\",\n    hdrs = [\"polynomial_traits_forward.h\"],\n)\n"
  },
  {
    "path": "tachyon/math/polynomials/evaluation_domain.h",
    "content": "#ifndef TACHYON_MATH_POLYNOMIALS_EVALUATION_DOMAIN_H_\n#define TACHYON_MATH_POLYNOMIALS_EVALUATION_DOMAIN_H_\n\n#include <stddef.h>\n\nnamespace tachyon::math {\n\ntemplate <typename F, size_t MaxDegree>\nclass EvaluationDomain {};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_POLYNOMIALS_EVALUATION_DOMAIN_H_\n"
  },
  {
    "path": "tachyon/math/polynomials/multivariate/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"linear_combination\",\n    hdrs = [\"linear_combination.h\"],\n    deps = [\n        \":linear_combination_term\",\n        \"//tachyon/base:parallelize\",\n        \"//tachyon/base:random\",\n        \"//tachyon/base/containers:container_util\",\n        \"@com_google_absl//absl/container:flat_hash_map\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"linear_combination_term\",\n    hdrs = [\"linear_combination_term.h\"],\n    deps = [\n        \"//tachyon/base:parallelize\",\n        \"//tachyon/base/buffer:copyable\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"multilinear_extension\",\n    hdrs = [\n        \"multilinear_dense_evaluations.h\",\n        \"multilinear_extension.h\",\n        \"multilinear_extension_ops.h\",\n        \"support_poly_operators.h\",\n    ],\n    deps = [\n        \"//tachyon/base:bits\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base:openmp_util\",\n        \"//tachyon/base:parallelize\",\n        \"//tachyon/base/buffer:copyable\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/base/strings:string_util\",\n        \"//tachyon/math/base:parallelize_threshold\",\n        \"//tachyon/math/polynomials:polynomial\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"multivariate_polynomial\",\n    hdrs = [\n        \"multivariate_polynomial.h\",\n        \"multivariate_polynomial_ops.h\",\n        \"multivariate_sparse_coefficients.h\",\n        \"support_poly_operators.h\",\n    ],\n    deps = [\n        \"//tachyon/base:logging\",\n        \"//tachyon/base:parallelize\",\n        \"//tachyon/base/containers:adapters\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/base/containers:cxx20_erase\",\n        \"//tachyon/base/ranges:algorithm\",\n        \"//tachyon/base/strings:string_util\",\n        \"//tachyon/math/polynomials:polynomial\",\n        \"@com_google_absl//absl/hash\",\n        \"@com_google_absl//absl/numeric:bits\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"multivariate_unittests\",\n    srcs = [\n        \"linear_combination_unittest.cc\",\n        \"multilinear_dense_evaluations_unittest.cc\",\n        \"multivariate_polynomial_unittest.cc\",\n    ],\n    deps = [\n        \":linear_combination\",\n        \":multilinear_extension\",\n        \":multivariate_polynomial\",\n        \"//tachyon/math/finite_fields/test:finite_field_test\",\n        \"//tachyon/math/finite_fields/test:gf7\",\n        \"@com_google_absl//absl/hash:hash_testing\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/math/polynomials/multivariate/linear_combination.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_POLYNOMIALS_MULTIVARIATE_LINEAR_COMBINATION_H_\n#define TACHYON_MATH_POLYNOMIALS_MULTIVARIATE_LINEAR_COMBINATION_H_\n\n#include <algorithm>\n#include <functional>\n#include <memory>\n#include <numeric>\n#include <utility>\n#include <vector>\n\n#include \"absl/container/flat_hash_map.h\"\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/parallelize.h\"\n#include \"tachyon/base/random.h\"\n#include \"tachyon/math/polynomials/multivariate/linear_combination_term.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename MLE>\nclass LinearCombination {\n public:\n  using F = typename MLE::Field;\n  using Point = typename MLE::Point;\n\n  LinearCombination() = default;\n  explicit LinearCombination(size_t num_variables)\n      : num_variables_(num_variables) {}\n  LinearCombination(LinearCombination& other) = default;\n  LinearCombination& operator=(LinearCombination& other) = default;\n\n  size_t max_evaluations() const { return max_evaluations_; }\n  size_t num_variables() const { return num_variables_; }\n  const std::vector<LinearCombinationTerm<F>>& terms() const { return terms_; }\n  const std::vector<std::shared_ptr<MLE>>& flattened_ml_evaluations() const {\n    return flattened_ml_evaluations_;\n  }\n\n  static LinearCombination Random(size_t num_variables,\n                                  base::Range<size_t> max_evaluations_range,\n                                  size_t num_terms) {\n    LinearCombination linear_combination(num_variables);\n\n    for (size_t i = 0; i < num_terms; ++i) {\n      F coefficient = F::Random();\n      size_t max_evaluations = base::Uniform(max_evaluations_range);\n      std::vector<std::shared_ptr<MLE>> evaluations =\n          base::CreateVector(max_evaluations, [num_variables]() {\n            return std::make_shared<MLE>(MLE::Random(num_variables));\n          });\n      linear_combination.AddTerm(coefficient, evaluations);\n    }\n    return linear_combination;\n  }\n\n  // Adds a |LinearCombinationTerm| to member |terms_|\n  void AddTerm(const F& coefficient,\n               const std::vector<std::shared_ptr<MLE>>& evaluations) {\n    if (evaluations.empty()) return;\n    size_t num_evaluations = evaluations.size();\n    max_evaluations_ = std::max(max_evaluations_, num_evaluations);\n    std::vector<size_t> indexes =\n        base::Map(evaluations, [this](const std::shared_ptr<MLE>& evaluation) {\n          CHECK_EQ(evaluation->Degree(), num_variables_);\n          const MLE* evaluation_ptr = evaluation.get();\n          const auto [it, inserted] = lookup_table_.try_emplace(\n              evaluation_ptr, flattened_ml_evaluations_.size());\n          if (inserted) {\n            flattened_ml_evaluations_.push_back(evaluation);\n          }\n          return it->second;\n        });\n    terms_.push_back({coefficient, std::move(indexes)});\n  }\n\n  // Combines the given |LinearCombination| into “H”, or the sum of all possible\n  // values of all the variables. Refer to\n  // https://people.cs.georgetown.edu/jthaler/sumcheck.pdf for more info.\n  F Combine() const {\n    std::vector<F> results = base::ParallelizeMap(\n        terms_, [this](absl::Span<const LinearCombinationTerm<F>> chunk) {\n          return CombineSerial(num_variables_, flattened_ml_evaluations_,\n                               chunk);\n        });\n    return std::accumulate(results.begin(), results.end(), F::Zero(),\n                           std::plus<>());\n  }\n\n  // Evaluates all terms together on an evaluation point for each variable:\n  // ∑ᵢ₌₀..ₙ(coefficientᵢ⋅∏ⱼ₌₀..ₘevaluationᵢⱼ)\n  // where n = total terms, m = number of evaluations per term\n  F Evaluate(const Point& point) const {\n    CHECK_EQ(point.size(), num_variables_);\n    std::vector<F> results = base::ParallelizeMap(\n        terms_,\n        [this, &point](absl::Span<const LinearCombinationTerm<F>> chunk) {\n          return EvaluateSerial(point, flattened_ml_evaluations_, chunk);\n        });\n    return std::accumulate(results.begin(), results.end(), F::Zero(),\n                           std::plus<>());\n  }\n\n private:\n  static F CombineSerial(\n      size_t num_variables,\n      const std::vector<std::shared_ptr<MLE>>& flattened_ml_evaluations,\n      absl::Span<const LinearCombinationTerm<F>> terms) {\n    return std::accumulate(\n        terms.begin(), terms.end(), F::Zero(),\n        [num_variables, &flattened_ml_evaluations](\n            F& acc, const LinearCombinationTerm<F>& term) {\n          return acc += term.Combine(num_variables, flattened_ml_evaluations);\n        });\n  }\n\n  static F EvaluateSerial(\n      const Point& point,\n      const std::vector<std::shared_ptr<MLE>>& flattened_ml_evaluations,\n      absl::Span<const LinearCombinationTerm<F>> terms) {\n    return std::accumulate(terms.begin(), terms.end(), F::Zero(),\n                           [&point, &flattened_ml_evaluations](\n                               F& acc, const LinearCombinationTerm<F>& term) {\n                             return acc += term.Evaluate(\n                                        point, flattened_ml_evaluations);\n                           });\n  }\n\n  // max number of evaluations for each term\n  size_t max_evaluations_ = 0;\n  // number of variables of the polynomial\n  size_t num_variables_ = 0;\n  // list of coefficients and their corresponding list of indexes in reference\n  // to evaluations in |flattened_ml_evaluations_|\n  std::vector<LinearCombinationTerm<F>> terms_;\n  // holds shared pointers of unique multilinear evaluations\n  std::vector<std::shared_ptr<MLE>> flattened_ml_evaluations_;\n  // holds all unique dense multilinear evaluations currently used & their\n  // corresponding index in |flattened_ml_evaluations_|\n  // not owned\n  absl::flat_hash_map<const MLE*, size_t> lookup_table_;\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_POLYNOMIALS_MULTIVARIATE_LINEAR_COMBINATION_H_\n"
  },
  {
    "path": "tachyon/math/polynomials/multivariate/linear_combination_term.h",
    "content": "#ifndef TACHYON_MATH_POLYNOMIALS_MULTIVARIATE_LINEAR_COMBINATION_TERM_H_\n#define TACHYON_MATH_POLYNOMIALS_MULTIVARIATE_LINEAR_COMBINATION_TERM_H_\n\n#include <functional>\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/buffer/copyable.h\"\n#include \"tachyon/base/parallelize.h\"\n\nnamespace tachyon {\nnamespace math {\n\ntemplate <typename F>\nstruct LinearCombinationTerm {\n  // Note(ashjeong): Member |indexes| relies on |LinearCombination|'s\n  // |flattened_ml_evaluations|\n  F coefficient;\n  std::vector<size_t> indexes;\n\n  template <typename Container, typename MLE>\n  F Evaluate(\n      const Container& point,\n      const std::vector<std::shared_ptr<MLE>>& flattened_ml_evaluations) const {\n    std::vector<F> results = base::ParallelizeMap(\n        indexes,\n        [&flattened_ml_evaluations, &point](absl::Span<const size_t> chunk) {\n          return EvaluateSerial(point, flattened_ml_evaluations, chunk);\n        });\n    return coefficient * std::accumulate(results.begin(), results.end(),\n                                         F::One(), std::multiplies<>());\n  }\n\n  template <typename MLE>\n  F Combine(\n      size_t num_variables_,\n      const std::vector<std::shared_ptr<MLE>>& flattened_ml_evaluations) const {\n    constexpr size_t kParallelFactor = 16;\n    CHECK(!indexes.empty());\n\n#if defined(TACHYON_HAS_OPENMP)\n    size_t thread_nums = static_cast<size_t>(omp_get_max_threads());\n#else\n    size_t thread_nums = 1;\n#endif\n\n    size_t size = size_t{1} << num_variables_;\n    std::vector<F> sums = base::ParallelizeMap(\n        size,\n        [this, &flattened_ml_evaluations](size_t len, size_t chunk_offset,\n                                          size_t chunk_size) {\n          size_t start = chunk_offset * chunk_size;\n          F sum = F::Zero();\n          for (size_t i = start; i < start + len; ++i) {\n            sum += std::accumulate(\n                indexes.begin(), indexes.end(), F::One(),\n                [&flattened_ml_evaluations, i](F& acc, size_t index) {\n                  const std::vector<F>& evals =\n                      flattened_ml_evaluations[index]->evaluations();\n                  return (i < evals.size()) ? (acc *= evals[i]) : acc;\n                });\n          }\n          return sum;\n        },\n        kParallelFactor * thread_nums);\n    F sum = std::accumulate(sums.begin(), sums.end(), F::Zero());\n    return sum *= coefficient;\n  }\n\n  bool operator==(const LinearCombinationTerm& other) const {\n    return coefficient == other.coefficient && indexes == other.indexes;\n  }\n  bool operator!=(const LinearCombinationTerm& other) const {\n    return !operator==(other);\n  }\n\n private:\n  template <typename Container, typename MLE>\n  static F EvaluateSerial(\n      const Container& point,\n      const std::vector<std::shared_ptr<MLE>>& flattened_ml_evaluations,\n      absl::Span<const size_t> indexes) {\n    return std::accumulate(\n        indexes.begin(), indexes.end(), F::One(),\n        [&flattened_ml_evaluations, &point](F& acc, const size_t index) {\n          return acc *= flattened_ml_evaluations[index]->Evaluate(point);\n        });\n  }\n};\n\n}  // namespace math\n\nnamespace base {\n\ntemplate <typename F>\nclass Copyable<math::LinearCombinationTerm<F>> {\n public:\n  static bool WriteTo(const math::LinearCombinationTerm<F>& term,\n                      Buffer* buffer) {\n    return buffer->WriteMany(term.coefficient, term.indexes);\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer,\n                       math::LinearCombinationTerm<F>* term) {\n    F coefficient;\n    std::vector<size_t> indexes;\n    if (!buffer.ReadMany(&coefficient, &indexes)) return false;\n    *term = {std::move(coefficient), std::move(indexes)};\n    return true;\n  }\n\n  static size_t EstimateSize(const math::LinearCombinationTerm<F>& term) {\n    return base::EstimateSize(term.coefficient, term.indexes);\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_MATH_POLYNOMIALS_MULTIVARIATE_LINEAR_COMBINATION_TERM_H_\n"
  },
  {
    "path": "tachyon/math/polynomials/multivariate/linear_combination_unittest.cc",
    "content": "#include \"tachyon/math/polynomials/multivariate/linear_combination.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n#include \"tachyon/math/polynomials/multivariate/multilinear_dense_evaluations.h\"\n\nnamespace tachyon::math {\nnamespace {\n\nconst size_t kMaxDegree = 4;\n\nusing F = GF7;\nusing Poly = MultilinearDenseEvaluations<F, kMaxDegree>;\nusing Point = Poly::Point;\nusing PolyPtr = std::shared_ptr<Poly>;\n\nclass LinearCombinationTest : public FiniteFieldTest<F> {};\n\n}  // namespace\n\n// Tests |AddTerm()| and |Evaluate()|\nTEST_F(LinearCombinationTest, AddTermAndEvaluate) {\n  LinearCombination<Poly> linear_combination(kMaxDegree);\n  F coefficient1 = F::Random();\n\n  // |evals| takes in 5 random polys and an additional 5 of the same random\n  // poly |test_multiple| to test logic of inputting the same random poly\n  PolyPtr test_multiple = std::make_shared<Poly>(Poly::Random(kMaxDegree));\n  std::vector<PolyPtr> evals1 =\n      base::CreateVector(10, [test_multiple](size_t i) {\n        if (i % 2 != 0) return std::make_shared<Poly>(Poly::Random(kMaxDegree));\n        return test_multiple;\n      });\n  linear_combination.AddTerm(coefficient1, evals1);\n\n  Point evaluation_point =\n      base::CreateVector(kMaxDegree, []() { return F::Random(); });\n\n  // Set up the expected evaluation result for this 1 term\n  F expected_evaluation =\n      coefficient1 *\n      std::accumulate(evals1.begin(), evals1.end(), F::One(),\n                      [&evaluation_point](F& acc, const PolyPtr eval) {\n                        return acc *= eval->Evaluate(evaluation_point);\n                      });\n\n  // Test validity of |AddTerm()| and |Evaluate()| on 1 term\n  EXPECT_EQ(linear_combination.Evaluate(evaluation_point), expected_evaluation);\n\n  // Test |AddTerm()| and |Evaluate()| on 2 terms\n  F coefficient2 = F::Random();\n  std::vector<PolyPtr> evals2 = base::CreateVector(\n      5, []() { return std::make_shared<Poly>(Poly::Random(kMaxDegree)); });\n  linear_combination.AddTerm(coefficient2, evals2);\n  expected_evaluation +=\n      coefficient2 *\n      std::accumulate(evals2.begin(), evals2.end(), F::One(),\n                      [&evaluation_point](F& acc, const PolyPtr eval) {\n                        return acc *= eval->Evaluate(evaluation_point);\n                      });\n  EXPECT_EQ(linear_combination.Evaluate(evaluation_point), expected_evaluation);\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/polynomials/multivariate/multilinear_dense_evaluations.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_POLYNOMIALS_MULTIVARIATE_MULTILINEAR_DENSE_EVALUATIONS_H_\n#define TACHYON_MATH_POLYNOMIALS_MULTIVARIATE_MULTILINEAR_DENSE_EVALUATIONS_H_\n\n#include <stddef.h>\n\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"absl/hash/hash.h\"\n\n#include \"tachyon/base/bits.h\"\n#include \"tachyon/base/buffer/copyable.h\"\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/parallelize.h\"\n#include \"tachyon/base/strings/string_util.h\"\n#include \"tachyon/math/base/parallelize_threshold.h\"\n#include \"tachyon/math/polynomials/multivariate/support_poly_operators.h\"\n\nnamespace tachyon {\nnamespace math {\n\ntemplate <typename F, size_t MaxDegree>\nclass MultilinearDenseEvaluations {\n public:\n  constexpr static size_t kMaxDegree = MaxDegree;\n  constexpr static F kZero = F::Zero();\n\n  using Field = F;\n  using Point = std::vector<F>;\n\n  constexpr MultilinearDenseEvaluations() = default;\n  constexpr explicit MultilinearDenseEvaluations(\n      const std::vector<F>& evaluations)\n      : evaluations_(evaluations) {\n    CHECK_LE(Degree(), MaxDegree);\n  }\n  constexpr explicit MultilinearDenseEvaluations(std::vector<F>&& evaluations)\n      : evaluations_(std::move(evaluations)) {\n    CHECK_LE(Degree(), MaxDegree);\n  }\n\n  // NOTE(chokobole): The zero polynomial can be represented in two forms:\n  // 1. An empty vector.\n  // 2. A vector filled with |F::Zero()|.\n  constexpr static MultilinearDenseEvaluations Zero() {\n    return MultilinearDenseEvaluations();\n  }\n\n  constexpr static MultilinearDenseEvaluations One(size_t degree) {\n    return MultilinearDenseEvaluations(\n        std::vector<F>(size_t{1} << degree, F::One()));\n  }\n\n  constexpr static MultilinearDenseEvaluations Random(size_t degree) {\n    return MultilinearDenseEvaluations(\n        base::CreateVector(size_t{1} << degree, []() { return F::Random(); }));\n  }\n\n  constexpr const std::vector<F>& evaluations() const { return evaluations_; }\n  constexpr std::vector<F>& evaluations() { return evaluations_; }\n\n  constexpr bool operator==(const MultilinearDenseEvaluations& other) const {\n    if (evaluations_.empty()) {\n      return other.IsZero();\n    }\n    if (other.evaluations_.empty()) {\n      return IsZero();\n    }\n    return evaluations_ == other.evaluations_;\n  }\n\n  constexpr bool operator!=(const MultilinearDenseEvaluations& other) const {\n    return !operator==(other);\n  }\n\n  constexpr F& at(size_t i) {\n    CHECK_LT(i, evaluations_.size());\n    return evaluations_[i];\n  }\n\n  constexpr const F& at(size_t i) const { return (*this)[i]; }\n\n  constexpr const F& operator[](size_t i) const {\n    if (i < evaluations_.size()) {\n      return evaluations_[i];\n    }\n    return kZero;\n  }\n\n  constexpr bool IsZero() const {\n    if (evaluations_.empty()) return true;\n    return std::all_of(evaluations_.begin(), evaluations_.end(),\n                       [](const F& value) { return value.IsZero(); });\n  }\n\n  constexpr bool IsOne() const {\n    if (evaluations_.empty()) return false;\n    return std::all_of(evaluations_.begin(), evaluations_.end(),\n                       [](const F& value) { return value.IsOne(); });\n  }\n\n  constexpr size_t Degree() const {\n    return base::bits::SafeLog2Ceiling(evaluations_.size());\n  }\n\n  MultilinearDenseEvaluations FixVariables(const Point& partial_point) const {\n    std::vector<F> poly = evaluations_;\n    RunFixVariables(partial_point, poly);\n    return MultilinearDenseEvaluations(poly);\n  }\n\n  MultilinearDenseEvaluations FixVariablesInPlace(const Point& partial_point) {\n    RunFixVariables(partial_point, evaluations_);\n    return *this;\n  }\n\n  // Evaluate polynomial at |point|. It uses |FixVariables()| internally. The\n  // |point| is a vector in {0, 1}ᵏ in little-endian form. If the size of\n  // |point| is less than the degree of the polynomial, the remaining components\n  // of |point| are assumed to be zeros.\n  //\n  //   MultilinearDenseEvaluations<GF7, 3> evals\n  //       MultilinearDenseEvaluations<GF7, 3>::Random();\n  //   GF7 a = evals.Evaluate({GF7(2), GF7(3)});\n  //   GF7 b = evals.Evaluate({GF7(2), GF7(3), GF7(0)});\n  //   CHECK_EQ(a, b);\n  F Evaluate(const Point& point) const {\n    CHECK_EQ(\n        point.size(),\n        static_cast<size_t>(base::bits::SafeLog2Ceiling(evaluations_.size())));\n\n    MultilinearDenseEvaluations fixed = FixVariables(point);\n\n    if (fixed.IsZero()) return F::Zero();\n    return fixed.evaluations_[0];\n  }\n\n  std::string ToString() const { return base::ContainerToString(evaluations_); }\n\n private:\n  friend class internal::MultilinearExtensionOp<\n      MultilinearDenseEvaluations<F, MaxDegree>>;\n\n  // NOTE(chokobole): This creates a polynomial that contains |F::Zero()| up to\n  // |degree| + 1.\n  constexpr static MultilinearDenseEvaluations Zero(size_t degree) {\n    MultilinearDenseEvaluations ret;\n    ret.evaluations_.resize(size_t{1} << degree);\n    base::ParallelizeFill(ret.evaluations_, F::Zero(),\n                          /*threshold=*/ParallelizeThreshold::kFieldInit);\n    return ret;\n  }\n\n  // Fix k variables out of n variables, where k is\n  // |partial_point.size()| and n is |Degree()|.\n  void RunFixVariables(const Point& partial_point,\n                       std::vector<F>& evaluations) const {\n    size_t k = partial_point.size();\n    size_t n = Degree();\n    CHECK_LE(k, n);\n\n    // clang-format off\n    // P(x₀, x₁) = 1(1 - x₀)(1 - x₁) + 2x₀(1 - x₁) + 3(1 - x₀)x₁ + 4x₀x₁\n    //\n    // Fixing s₀:\n    // P(s₀, x₁) = 1(1 - s₀)(1 - x₁) + 2s₀(1 - x₁) + 3(1 - s₀)x₁ + 4s₀x₁\n    //           = (1(1 - s₀) + 2s₀)(1 - x₁) + (3(1 - s₀) + 4s₀)x₁\n    //           = (left₀(1 - s₀) + right₀s₀)(1 - x₁) + (left₁(1 - s₀) + right₁s₀)x₁\n    //             (where left₀ = 1, right₀ = 2, left₁ = 3 and right₁ = 4)\n    //           = (left₀ + s₀(right₀ - left₀))(1 - x₁) + (left₁ + s₀(right₁ - left₁))x₁\n    //\n    // Fixing s₁:\n    // P(s₀, s₁) = (1 + (2 - 1)s₀)(1 - s₁) + (3 + (4 - 1)4s₀)s₁\n    //           = (left₀(1 - s₀) + right₀s₀)(1 - x₁) + (left₁(1 - s₀) + right₁s₀)x₁\n    //             (where left₀ = 1 + (2 - 1)s₀ and right₀ = (3 + (4 - 1)4s₀)\n    //           = left₀ + s₁(right₀ - left₀)\n    // clang-format on\n    for (size_t i = 1; i <= k; ++i) {\n      const F& r = partial_point[i - 1];\n      for (size_t b = 0; b < (size_t{1} << (n - i)); ++b) {\n        const F& left = evaluations[b << 1];\n        const F& right = evaluations[(b << 1) + 1];\n        evaluations[b] = left + r * (right - left);\n      }\n    }\n    evaluations.resize(size_t{1} << (n - k));\n  }\n\n  std::vector<F> evaluations_;\n};\n\ntemplate <typename H, typename F, size_t MaxDegree>\nH AbslHashValue(H h, const MultilinearDenseEvaluations<F, MaxDegree>& evals) {\n  // NOTE(chokobole): We shouldn't hash only with a non-zero term.\n  // See https://abseil.io/docs/cpp/guides/hash#the-abslhashvalue-overload\n  size_t degree = 0;\n  for (const F& eval : evals.evaluations()) {\n    h = H::combine(std::move(h), eval);\n    ++degree;\n  }\n  F zero = F::Zero();\n  for (size_t i = degree; i < size_t{1} << MaxDegree; ++i) {\n    h = H::combine(std::move(h), zero);\n  }\n  return h;\n}\n\n}  // namespace math\n\nnamespace base {\n\ntemplate <typename F, size_t MaxDegree>\nclass Copyable<math::MultilinearDenseEvaluations<F, MaxDegree>> {\n public:\n  static bool WriteTo(\n      const math::MultilinearDenseEvaluations<F, MaxDegree>& evals,\n      Buffer* buffer) {\n    return buffer->Write(evals.evaluations());\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer,\n                       math::MultilinearDenseEvaluations<F, MaxDegree>* evals) {\n    std::vector<F> evaluations;\n    if (!buffer.Read(&evaluations)) return false;\n    *evals =\n        math::MultilinearDenseEvaluations<F, MaxDegree>(std::move(evaluations));\n    return true;\n  }\n\n  static size_t EstimateSize(\n      const math::MultilinearDenseEvaluations<F, MaxDegree>& evals) {\n    return base::EstimateSize(evals.evaluations());\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_MATH_POLYNOMIALS_MULTIVARIATE_MULTILINEAR_DENSE_EVALUATIONS_H_\n"
  },
  {
    "path": "tachyon/math/polynomials/multivariate/multilinear_dense_evaluations_unittest.cc",
    "content": "#include \"tachyon/math/polynomials/multivariate/multilinear_dense_evaluations.h\"\n\n#include <tuple>\n\n#include \"absl/hash/hash_testing.h\"\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n#include \"tachyon/math/polynomials/multivariate/multilinear_extension.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\nconst size_t kMaxDegree = 4;\n\nusing Point = std::vector<GF7>;\nusing Poly = MultilinearExtension<MultilinearDenseEvaluations<GF7, kMaxDegree>>;\nusing Evals = MultilinearDenseEvaluations<GF7, kMaxDegree>;\n\nclass MultilinearDenseEvaluationsTest : public FiniteFieldTest<GF7> {\n public:\n  void SetUp() override {\n    polys_.push_back(Poly(Evals({GF7(2), GF7(3)})));\n    polys_.push_back(Poly(Evals({GF7(4), GF7(2)})));\n    polys_.push_back(Poly(Evals(\n        {GF7(2), GF7(3), GF7(2), GF7(6), GF7(5), GF7(1), GF7(4), GF7(2)})));\n    polys_.push_back(Poly(Evals(\n        {GF7(3), GF7(1), GF7(1), GF7(4), GF7(2), GF7(3), GF7(2), GF7(5)})));\n  }\n\n protected:\n  std::vector<Poly> polys_;\n};\n\n}  // namespace\n\nTEST_F(MultilinearDenseEvaluationsTest, IsZero) {\n  EXPECT_TRUE(Poly().IsZero());\n  EXPECT_TRUE(Poly::Zero().IsZero());\n  EXPECT_TRUE(Poly(Evals({GF7(0)})).IsZero());\n  for (size_t i = 0; i < polys_.size(); ++i) {\n    EXPECT_FALSE(Poly(polys_[i]).IsZero());\n  }\n}\n\nTEST_F(MultilinearDenseEvaluationsTest, IsOne) {\n  EXPECT_TRUE(Poly::One(0).IsOne());\n  EXPECT_TRUE(Poly(Evals({GF7(1)})).IsOne());\n  for (size_t i = 0; i < polys_.size(); ++i) {\n    EXPECT_FALSE(polys_[i].IsOne());\n  }\n}\n\nTEST_F(MultilinearDenseEvaluationsTest, Random) {\n  bool success = false;\n  Poly r = Poly::Random(kMaxDegree);\n  for (size_t i = 0; i < 100; ++i) {\n    if (r != Poly::Random(kMaxDegree)) {\n      success = true;\n      break;\n    }\n  }\n  EXPECT_TRUE(success);\n}\n\nTEST_F(MultilinearDenseEvaluationsTest, IndexingOperator) {\n  struct {\n    const Poly& poly;\n    std::vector<int> evaluations;\n  } tests[] = {\n      {polys_[0], {2, 3}},\n      {polys_[1], {4, 2}},\n      {polys_[2], {2, 3, 2, 6, 5, 1, 4, 2}},\n      {polys_[3], {3, 1, 1, 4, 2, 3, 2, 5}},\n  };\n\n  for (const auto& test : tests) {\n    for (size_t i = 0; i < kMaxDegree; ++i) {\n      if (i < test.evaluations.size()) {\n        EXPECT_EQ(test.poly[i], GF7(test.evaluations[i]));\n      } else {\n        EXPECT_EQ(test.poly[i], GF7::Zero());\n      }\n    }\n  }\n}\n\nTEST_F(MultilinearDenseEvaluationsTest, Degree) {\n  struct {\n    const Poly& poly;\n    size_t degree;\n  } tests[] = {{polys_[0], 1}, {polys_[1], 1}, {polys_[2], 3}, {polys_[3], 3}};\n\n  for (const auto& test : tests) {\n    EXPECT_EQ(test.poly.Degree(), test.degree);\n  }\n  EXPECT_LE(Poly::Random(kMaxDegree).Degree(), kMaxDegree);\n}\n\nTEST_F(MultilinearDenseEvaluationsTest, Evaluate) {\n  std::function<Point(size_t, size_t)> convert_to_le = [](size_t number,\n                                                          size_t degree) {\n    Point ret;\n    for (size_t i = 0; i < degree; ++i) {\n      ret.push_back(GF7(uint64_t{(number & (size_t{1} << i)) != 0}));\n    }\n    return ret;\n  };\n\n  for (const Poly& poly : polys_) {\n    size_t degree = poly.Degree();\n    for (size_t i = 0; i < (size_t{1} << degree); ++i) {\n      const GF7& elem = poly[i];\n      if (elem.IsZero()) break;\n      Point point = convert_to_le(i, degree);\n      EXPECT_EQ(poly.Evaluate(point), elem);\n    }\n  }\n}\n\nTEST_F(MultilinearDenseEvaluationsTest, ToString) {\n  struct {\n    const Poly& poly;\n    std::string_view expected;\n  } tests[] = {\n      {polys_[0], \"[2, 3]\"},\n      {polys_[1], \"[4, 2]\"},\n      {polys_[2], \"[2, 3, 2, 6, 5, 1, 4, 2]\"},\n      {polys_[3], \"[3, 1, 1, 4, 2, 3, 2, 5]\"},\n  };\n\n  for (const auto& test : tests) {\n    EXPECT_EQ(test.poly.ToString(), test.expected);\n  }\n}\n\nTEST_F(MultilinearDenseEvaluationsTest, AdditiveOperators) {\n  struct {\n    const Poly& a;\n    const Poly& b;\n    Poly sum;\n    Poly amb;\n    Poly bma;\n  } tests[] = {\n      {\n          polys_[0],\n          polys_[1],\n          Poly(Evals({GF7(6), GF7(5)})),\n          Poly(Evals({GF7(5), GF7(1)})),\n          Poly(Evals({GF7(2), GF7(6)})),\n      },\n      {\n          polys_[2],\n          polys_[3],\n          Poly(Evals({GF7(5), GF7(4), GF7(3), GF7(3), GF7(0), GF7(4), GF7(6),\n                      GF7(0)})),\n          Poly(Evals({GF7(6), GF7(2), GF7(1), GF7(2), GF7(3), GF7(5), GF7(2),\n                      GF7(4)})),\n          Poly(Evals({GF7(1), GF7(5), GF7(6), GF7(5), GF7(4), GF7(2), GF7(5),\n                      GF7(3)})),\n      },\n  };\n\n  for (const auto& test : tests) {\n    EXPECT_EQ(test.a + test.b, test.sum);\n    EXPECT_EQ(test.b + test.a, test.sum);\n    EXPECT_EQ(test.a - test.b, test.amb);\n    EXPECT_EQ(test.b - test.a, test.bma);\n\n    Poly tmp = test.a;\n    tmp += test.b;\n    EXPECT_EQ(tmp, test.sum);\n    tmp -= test.b;\n    EXPECT_EQ(tmp, test.a);\n  }\n}\n\nTEST_F(MultilinearDenseEvaluationsTest, MultiplicativeOperators) {\n  struct {\n    const Poly& a;\n    const Poly& b;\n    Poly mul;\n    Poly adb;\n    Poly bda;\n  } tests[] = {\n      {\n          polys_[0],\n          polys_[1],\n          Poly(Evals({GF7(1), GF7(6)})),\n          Poly(Evals({GF7(4), GF7(5)})),\n          Poly(Evals({GF7(2), GF7(3)})),\n      },\n      {\n          polys_[2],\n          polys_[3],\n          Poly(Evals({GF7(6), GF7(3), GF7(2), GF7(3), GF7(3), GF7(3), GF7(1),\n                      GF7(3)})),\n          Poly(Evals({GF7(3), GF7(3), GF7(2), GF7(5), GF7(6), GF7(5), GF7(2),\n                      GF7(6)})),\n          Poly(Evals({GF7(5), GF7(5), GF7(4), GF7(3), GF7(6), GF7(3), GF7(4),\n                      GF7(6)})),\n      },\n  };\n\n  for (const auto& test : tests) {\n    EXPECT_EQ(test.a * test.b, test.mul);\n    EXPECT_EQ(test.b * test.a, test.mul);\n    EXPECT_EQ(test.a / test.b, test.adb);\n    EXPECT_EQ(test.b / test.a, test.bda);\n\n    Poly tmp = test.a;\n    tmp *= test.b;\n    EXPECT_EQ(tmp, test.mul);\n    ASSERT_TRUE(tmp /= test.b);\n    EXPECT_EQ(tmp, test.a);\n  }\n}\n\nTEST_F(MultilinearDenseEvaluationsTest, Hash) {\n  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(\n      std::make_tuple(Poly(), Poly::Zero(),\n                      Poly(Evals(std::vector<GF7>(size_t{1} << kMaxDegree))),\n                      Poly::One(kMaxDegree), Poly::Random(kMaxDegree),\n                      Poly::Random(kMaxDegree))));\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/polynomials/multivariate/multilinear_extension.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_POLYNOMIALS_MULTIVARIATE_MULTILINEAR_EXTENSION_H_\n#define TACHYON_MATH_POLYNOMIALS_MULTIVARIATE_MULTILINEAR_EXTENSION_H_\n\n#include <optional>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"absl/hash/hash.h\"\n\n#include \"tachyon/base/buffer/copyable.h\"\n#include \"tachyon/math/polynomials/multivariate/multilinear_dense_evaluations.h\"\n#include \"tachyon/math/polynomials/polynomial.h\"\n\nnamespace tachyon {\nnamespace math {\n\n// MultilinearExtension represents a multilinear polynomial in evaluation form.\n// Unlike UnivariateEvaluations, its evaluation domain is fixed to {0, 1}ᵏ (i.e.\n// Boolean hypercube).\ntemplate <typename Evaluations>\nclass MultilinearExtension final\n    : public Polynomial<MultilinearExtension<Evaluations>> {\n public:\n  using Field = typename Evaluations::Field;\n  using Point = std::vector<Field>;\n\n  constexpr MultilinearExtension() = default;\n  constexpr explicit MultilinearExtension(const Evaluations& evaluations)\n      : evaluations_(evaluations) {}\n  constexpr explicit MultilinearExtension(Evaluations&& evaluations)\n      : evaluations_(std::move(evaluations)) {}\n\n  constexpr static bool IsCoefficientForm() { return false; }\n\n  constexpr static bool IsEvaluationForm() { return true; }\n\n  constexpr static MultilinearExtension Zero() {\n    return MultilinearExtension(Evaluations::Zero());\n  }\n\n  constexpr static MultilinearExtension One(size_t degree) {\n    return MultilinearExtension(Evaluations::One(degree));\n  }\n\n  constexpr static MultilinearExtension Random(size_t degree) {\n    return MultilinearExtension(Evaluations::Random(degree));\n  }\n\n  constexpr bool IsZero() const { return evaluations_.IsZero(); }\n\n  constexpr bool IsOne() const { return evaluations_.IsOne(); }\n\n  const Evaluations& evaluations() const { return evaluations_; }\n\n  constexpr bool operator==(const MultilinearExtension& other) const {\n    return evaluations_ == other.evaluations_;\n  }\n\n  constexpr bool operator!=(const MultilinearExtension& other) const {\n    return !operator==(other);\n  }\n\n  // Returns a reference to the coefficient for the given |i| if it exists.\n  // Otherwise, it terminates the program.\n  constexpr Field& at(size_t i) { return evaluations_.at(i); }\n\n  // Returns a reference to the coefficient for the given |i| if it exists.\n  // Otherwise, returns a reference to the |Field::Zero()|.\n  constexpr const Field& at(size_t i) const { return evaluations_.at(i); }\n\n  // Returns a reference to the coefficient for the given |i| if it exists.\n  // Otherwise, returns a reference to the |Field::Zero()|.\n  constexpr const Field& operator[](size_t i) const { return evaluations_[i]; }\n\n  constexpr size_t Degree() const { return evaluations_.Degree(); }\n\n  // Evaluate a polynomial at the specified |point|. The |point| is a vector in\n  // {0, 1}ᵏ in little-endian form. If the size of |point| is less than the\n  // degree of the polynomial, the remaining components of |point| are assumed\n  // to be zeros. For example:\n\n  //   P(x₀, x₁) = 1(1 - x₀)(1 - x₁) + 3x₀(1 - x₁) + 5(1 - x₀)x₁ + 2x₀x₁\n  //   P(0, 0) = 1\n  //   P(0, 1) = 5\n  //   P(1, 0) = 3\n  //   P(1, 1) = 2\n\n  // In this context, {x₀, x₁} corresponds to the components of the |point|.\n  constexpr Field Evaluate(const Point& point) const {\n    return evaluations_.Evaluate(point);\n  }\n\n  decltype(auto) ToDense() const {\n    return internal::MultilinearExtensionOp<Evaluations>::ToDense(*this);\n  }\n\n  std::string ToString() const { return evaluations_.ToString(); }\n\n// TODO(ashjeong): change all internal functions to constexpr\n#define OPERATION_METHOD(Name)                                                 \\\n  template <typename Evaluations2,                                             \\\n            std::enable_if_t<internal::SupportsPoly##Name<                     \\\n                Evaluations, MultilinearExtension<Evaluations>,                \\\n                MultilinearExtension<Evaluations2>>::value>* = nullptr>        \\\n  constexpr auto Name(const MultilinearExtension<Evaluations2>& other) const { \\\n    return internal::MultilinearExtensionOp<Evaluations>::Name(*this, other);  \\\n  }                                                                            \\\n                                                                               \\\n  template <typename Evaluations2,                                             \\\n            std::enable_if_t<internal::SupportsPoly##Name##InPlace<            \\\n                Evaluations, MultilinearExtension<Evaluations>,                \\\n                MultilinearExtension<Evaluations2>>::value>* = nullptr>        \\\n  constexpr auto& Name##InPlace(                                               \\\n      const MultilinearExtension<Evaluations2>& other) {                       \\\n    return internal::MultilinearExtensionOp<Evaluations>::Name##InPlace(       \\\n        *this, other);                                                         \\\n  }\n\n  // AdditiveSemigroup methods\n  OPERATION_METHOD(Add)\n\n  // AdditiveGroup methods\n  OPERATION_METHOD(Sub)\n\n  MultilinearExtension Negate() const {\n    return internal::MultilinearExtensionOp<Evaluations>::Negate(*this);\n  }\n\n  MultilinearExtension& NegateInPlace() {\n    return internal::MultilinearExtensionOp<Evaluations>::NegateInPlace(*this);\n  }\n\n  // MultiplicativeSemigroup methods\n  OPERATION_METHOD(Mul)\n\n  template <typename Evaluations2,\n            std::enable_if_t<internal::SupportsPolyDiv<\n                Evaluations, MultilinearExtension<Evaluations>,\n                MultilinearExtension<Evaluations2>>::value>* = nullptr>\n  constexpr auto Div(const MultilinearExtension<Evaluations2>& other) const {\n    return internal::MultilinearExtensionOp<Evaluations>::Div(*this, other);\n  }\n\n  template <typename Evaluations2,\n            std::enable_if_t<internal::SupportsPolyDivInPlace<\n                Evaluations, MultilinearExtension<Evaluations>,\n                MultilinearExtension<Evaluations2>>::value>* = nullptr>\n  [[nodiscard]] constexpr auto DivInPlace(\n      const MultilinearExtension<Evaluations2>& other) {\n    return internal::MultilinearExtensionOp<Evaluations>::DivInPlace(*this,\n                                                                     other);\n  }\n\n  constexpr std::optional<MultilinearExtension> operator/(\n      const MultilinearExtension& other) const {\n    return Div(other);\n  }\n\n  [[nodiscard]] constexpr std::optional<MultilinearExtension*> operator/=(\n      const MultilinearExtension& other) {\n    return DivInPlace(other);\n  }\n\n private:\n  friend class internal::MultilinearExtensionOp<\n      MultilinearDenseEvaluations<Field, Evaluations::kMaxDegree>>;\n\n  Evaluations evaluations_;\n};\n\ntemplate <typename F, size_t MaxDegree>\nusing MultilinearDenseExtension =\n    MultilinearExtension<MultilinearDenseEvaluations<F, MaxDegree>>;\n\ntemplate <typename Evaluations>\nclass PolynomialTraits<MultilinearExtension<Evaluations>> {\n public:\n  constexpr static bool kIsEvaluationForm = true;\n};\n\ntemplate <typename H, typename Evaluations>\nH AbslHashValue(H h, const MultilinearExtension<Evaluations>& mle) {\n  return H::combine(std::move(h), mle.evaluations());\n}\n\n}  // namespace math\nnamespace base {\n\ntemplate <typename Evaluations>\nclass Copyable<math::MultilinearExtension<Evaluations>> {\n public:\n  static bool WriteTo(const math::MultilinearExtension<Evaluations>& mle,\n                      Buffer* buffer) {\n    return buffer->Write(mle.evaluations());\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer,\n                       math::MultilinearExtension<Evaluations>* mle) {\n    Evaluations evaluations;\n    if (!buffer.Read(&evaluations)) return false;\n    *mle = math::MultilinearExtension<Evaluations>(std::move(evaluations));\n    return true;\n  }\n\n  static size_t EstimateSize(\n      const math::MultilinearExtension<Evaluations>& mle) {\n    return base::EstimateSize(mle.evaluations());\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#include \"tachyon/math/polynomials/multivariate/multilinear_extension_ops.h\"\n\n#endif  // TACHYON_MATH_POLYNOMIALS_MULTIVARIATE_MULTILINEAR_EXTENSION_H_\n"
  },
  {
    "path": "tachyon/math/polynomials/multivariate/multilinear_extension_ops.h",
    "content": "#ifndef TACHYON_MATH_POLYNOMIALS_MULTIVARIATE_MULTILINEAR_EXTENSION_OPS_H_\n#define TACHYON_MATH_POLYNOMIALS_MULTIVARIATE_MULTILINEAR_EXTENSION_OPS_H_\n\n#include <algorithm>\n#include <atomic>\n#include <optional>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/openmp_util.h\"\n#include \"tachyon/math/polynomials/multivariate/multilinear_extension.h\"\n\nnamespace tachyon::math {\nnamespace internal {\n\ntemplate <typename F, size_t MaxDegree>\nclass MultilinearExtensionOp<MultilinearDenseEvaluations<F, MaxDegree>> {\n public:\n  using D = MultilinearDenseEvaluations<F, MaxDegree>;\n\n  static MultilinearExtension<D> Add(const MultilinearExtension<D>& self,\n                                     const MultilinearExtension<D>& other) {\n    const std::vector<F>& l_evaluations = self.evaluations_.evaluations_;\n    const std::vector<F>& r_evaluations = other.evaluations_.evaluations_;\n    if (l_evaluations.empty()) {\n      // 0 + g(x)\n      return other;\n    }\n    if (r_evaluations.empty()) {\n      // f(x) + 0\n      return self;\n    }\n    CHECK_EQ(l_evaluations.size(), r_evaluations.size());\n    std::vector<F> o_evaluations(r_evaluations.size());\n    OMP_PARALLEL_FOR(size_t i = 0; i < r_evaluations.size(); ++i) {\n      o_evaluations[i] = l_evaluations[i] + r_evaluations[i];\n    }\n    return MultilinearExtension<D>(D(std::move(o_evaluations)));\n  }\n\n  static MultilinearExtension<D>& AddInPlace(\n      MultilinearExtension<D>& self, const MultilinearExtension<D>& other) {\n    std::vector<F>& l_evaluations = self.evaluations_.evaluations_;\n    const std::vector<F>& r_evaluations = other.evaluations_.evaluations_;\n    if (l_evaluations.empty()) {\n      // 0 + g(x)\n      return self = other;\n    }\n    if (r_evaluations.empty()) {\n      // f(x) + 0\n      return self;\n    }\n    CHECK_EQ(l_evaluations.size(), r_evaluations.size());\n    OMP_PARALLEL_FOR(size_t i = 0; i < r_evaluations.size(); ++i) {\n      l_evaluations[i] += r_evaluations[i];\n    }\n    return self;\n  }\n\n  static MultilinearExtension<D> Sub(const MultilinearExtension<D>& self,\n                                     const MultilinearExtension<D>& other) {\n    const std::vector<F>& l_evaluations = self.evaluations_.evaluations_;\n    const std::vector<F>& r_evaluations = other.evaluations_.evaluations_;\n    if (l_evaluations.empty()) {\n      // 0 - g(x)\n      return -other;\n    }\n    if (r_evaluations.empty()) {\n      // f(x) - 0\n      return self;\n    }\n    CHECK_EQ(l_evaluations.size(), r_evaluations.size());\n    std::vector<F> o_evaluations(r_evaluations.size());\n    OMP_PARALLEL_FOR(size_t i = 0; i < r_evaluations.size(); ++i) {\n      o_evaluations[i] = l_evaluations[i] - r_evaluations[i];\n    }\n    return MultilinearExtension<D>(D(std::move(o_evaluations)));\n  }\n\n  static MultilinearExtension<D>& SubInPlace(\n      MultilinearExtension<D>& self, const MultilinearExtension<D>& other) {\n    std::vector<F>& l_evaluations = self.evaluations_.evaluations_;\n    const std::vector<F>& r_evaluations = other.evaluations_.evaluations_;\n    if (l_evaluations.empty()) {\n      // 0 - g(x)\n      return self = -other;\n    }\n    if (r_evaluations.empty()) {\n      // f(x) - 0\n      return self;\n    }\n    CHECK_EQ(l_evaluations.size(), r_evaluations.size());\n    OMP_PARALLEL_FOR(size_t i = 0; i < r_evaluations.size(); ++i) {\n      l_evaluations[i] -= r_evaluations[i];\n    }\n    return self;\n  }\n\n  static MultilinearExtension<D> Negate(const MultilinearExtension<D>& self) {\n    const std::vector<F>& i_evaluations = self.evaluations_.evaluations_;\n    if (i_evaluations.empty()) {\n      return self;\n    }\n    std::vector<F> o_evaluations(i_evaluations.size());\n    OMP_PARALLEL_FOR(size_t i = 0; i < o_evaluations.size(); ++i) {\n      o_evaluations[i] = -i_evaluations[i];\n    }\n    return MultilinearExtension<D>(D(std::move(o_evaluations)));\n  }\n\n  static MultilinearExtension<D>& NegateInPlace(MultilinearExtension<D>& self) {\n    std::vector<F>& evaluations = self.evaluations_.evaluations_;\n    if (evaluations.empty()) {\n      return self;\n    }\n    // clang-format off\n    OMP_PARALLEL_FOR(F& evaluation : evaluations) {\n      // clang-format on\n      evaluation.NegateInPlace();\n    }\n    return self;\n  }\n\n  static MultilinearExtension<D> Mul(const MultilinearExtension<D>& self,\n                                     const MultilinearExtension<D>& other) {\n    const std::vector<F>& l_evaluations = self.evaluations_.evaluations_;\n    const std::vector<F>& r_evaluations = other.evaluations_.evaluations_;\n    if (l_evaluations.empty() || r_evaluations.empty()) {\n      // 0 * g(x) or f(x) * 0\n      return MultilinearExtension<D>::Zero();\n    }\n    CHECK_EQ(l_evaluations.size(), r_evaluations.size());\n    std::vector<F> o_evaluations(r_evaluations.size());\n    OMP_PARALLEL_FOR(size_t i = 0; i < l_evaluations.size(); ++i) {\n      o_evaluations[i] = l_evaluations[i] * r_evaluations[i];\n    }\n    return MultilinearExtension<D>(D(std::move(o_evaluations)));\n  }\n\n  static MultilinearExtension<D>& MulInPlace(\n      MultilinearExtension<D>& self, const MultilinearExtension<D>& other) {\n    std::vector<F>& l_evaluations = self.evaluations_.evaluations_;\n    const std::vector<F>& r_evaluations = other.evaluations_.evaluations_;\n    if (l_evaluations.empty()) {\n      // 0 * g(x)\n      return self;\n    }\n    if (r_evaluations.empty()) {\n      // f(x) * 0\n      l_evaluations.clear();\n      return self;\n    }\n    CHECK_EQ(l_evaluations.size(), r_evaluations.size());\n    OMP_PARALLEL_FOR(size_t i = 0; i < l_evaluations.size(); ++i) {\n      l_evaluations[i] *= r_evaluations[i];\n    }\n    return self;\n  }\n\n  CONSTEXPR_IF_NOT_OPENMP static std::optional<MultilinearExtension<D>> Div(\n      const MultilinearExtension<D>& self,\n      const MultilinearExtension<D>& other) {\n    const std::vector<F>& l_evaluations = self.evaluations_.evaluations_;\n    const std::vector<F>& r_evaluations = other.evaluations_.evaluations_;\n    // f(x) / 0\n    if (UNLIKELY(r_evaluations.empty())) {\n      LOG_IF_NOT_GPU(ERROR) << \"Division by zero attempted\";\n      return std::nullopt;\n    }\n    // 0 / g(x)\n    if (l_evaluations.empty()) {\n      return self;\n    }\n    // f(x) & g(x) unequal evaluation sizes\n    if (UNLIKELY(l_evaluations.size() != r_evaluations.size())) {\n      LOG_IF_NOT_GPU(ERROR) << \"Evaluation sizes unequal for division\";\n      return std::nullopt;\n    }\n    std::vector<F> o_evaluations(r_evaluations.size());\n    std::atomic<bool> check_valid(true);\n    OMP_PARALLEL_FOR(size_t i = 0; i < l_evaluations.size(); ++i) {\n      std::optional<F> div = l_evaluations[i] / r_evaluations[i];\n      if (UNLIKELY(!div)) {\n        check_valid.store(false, std::memory_order_relaxed);\n        continue;\n      }\n      o_evaluations[i] = std::move(div).value();\n    }\n    if (LIKELY(check_valid.load(std::memory_order_relaxed))) {\n      return MultilinearExtension<D>(D(std::move(o_evaluations)));\n    }\n    LOG_IF_NOT_GPU(ERROR) << \"Division by zero attempted\";\n    return std::nullopt;\n  }\n\n  [[nodiscard]] CONSTEXPR_IF_NOT_OPENMP static std::optional<\n      MultilinearExtension<D>*>\n  DivInPlace(MultilinearExtension<D>& self,\n             const MultilinearExtension<D>& other) {\n    std::vector<F>& l_evaluations = self.evaluations_.evaluations_;\n    const std::vector<F>& r_evaluations = other.evaluations_.evaluations_;\n    // f(x) / 0\n    if (UNLIKELY(r_evaluations.empty())) {\n      LOG_IF_NOT_GPU(ERROR) << \"Division by zero attempted\";\n      return std::nullopt;\n    }\n    // 0 / g(x)\n    if (l_evaluations.empty()) {\n      return &self;\n    }\n    // f(x) & g(x) unequal evaluation sizes\n    if (UNLIKELY(l_evaluations.size() != r_evaluations.size())) {\n      LOG_IF_NOT_GPU(ERROR) << \"Evaluation sizes unequal for division\";\n      return std::nullopt;\n    }\n    std::atomic<bool> check_valid(true);\n    OMP_PARALLEL_FOR(size_t i = 0; i < r_evaluations.size(); ++i) {\n      if (UNLIKELY(!(l_evaluations[i] /= r_evaluations[i])))\n        check_valid.store(false, std::memory_order_relaxed);\n    }\n    if (LIKELY(check_valid.load(std::memory_order_relaxed))) {\n      return &self;\n    }\n    LOG_IF_NOT_GPU(ERROR) << \"Division by zero attempted\";\n    return std::nullopt;\n  }\n\n  static const MultilinearExtension<D>& ToDense(\n      const MultilinearExtension<D>& self) {\n    return self;\n  }\n};\n\n}  // namespace internal\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_POLYNOMIALS_MULTIVARIATE_MULTILINEAR_EXTENSION_OPS_H_\n"
  },
  {
    "path": "tachyon/math/polynomials/multivariate/multivariate_polynomial.h",
    "content": "#ifndef TACHYON_MATH_POLYNOMIALS_MULTIVARIATE_MULTIVARIATE_POLYNOMIAL_H_\n#define TACHYON_MATH_POLYNOMIALS_MULTIVARIATE_MULTIVARIATE_POLYNOMIAL_H_\n\n#include <stddef.h>\n\n#include <string>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/math/polynomials/multivariate/multivariate_sparse_coefficients.h\"\n#include \"tachyon/math/polynomials/polynomial.h\"\n\nnamespace tachyon::math {\n\n// MultivariatePolynomial represents a polynomial with multiple variables.\n// For example, 3x²y + 2yz + 1 is a multivariate polynomial with 3 variables.\ntemplate <typename Coefficients>\nclass MultivariatePolynomial final\n    : public Polynomial<MultivariatePolynomial<Coefficients>> {\n public:\n  constexpr static size_t kMaxDegree = Coefficients::kMaxDegree;\n\n  using Field = typename Coefficients::Field;\n  using Literal = typename Coefficients::Literal;\n  using Point = std::vector<Field>;\n\n  constexpr MultivariatePolynomial() = default;\n  explicit constexpr MultivariatePolynomial(const Coefficients& coefficients)\n      : coefficients_(coefficients) {}\n  explicit constexpr MultivariatePolynomial(Coefficients&& coefficients)\n      : coefficients_(std::move(coefficients)) {}\n\n  constexpr static MultivariatePolynomial Zero() {\n    return MultivariatePolynomial(Coefficients::Zero());\n  }\n\n  constexpr static MultivariatePolynomial One() {\n    return MultivariatePolynomial(Coefficients::One());\n  }\n\n  constexpr static MultivariatePolynomial Random(size_t arity, size_t degree) {\n    return MultivariatePolynomial(Coefficients::Random(arity, degree));\n  }\n\n  constexpr static bool IsCoefficientForm() { return true; }\n\n  constexpr static bool IsEvaluationForm() { return false; }\n\n  constexpr bool IsZero() const { return coefficients_.IsZero(); }\n\n  constexpr bool IsOne() const { return coefficients_.IsOne(); }\n\n  constexpr const Coefficients& coefficients() const { return coefficients_; }\n\n  constexpr bool operator==(const MultivariatePolynomial& other) const {\n    return coefficients_ == other.coefficients_;\n  }\n\n  constexpr bool operator!=(const MultivariatePolynomial& other) const {\n    return !operator==(other);\n  }\n\n  // Returns a reference to the coefficient for the given |i| if it exists.\n  // Otherwise, it terminates the program.\n  constexpr Field& at(const Literal& literal) {\n    return coefficients_.at(literal);\n  }\n\n  // Returns a reference to the coefficient for the given |i| if it exists.\n  // Otherwise, returns a reference to the |Field::Zero()|.\n  constexpr const Field& at(const Literal& literal) const {\n    return coefficients_.at(literal);\n  }\n\n  // Returns a reference to the coefficient for the given |i| if it exists.\n  // Otherwise, returns a reference to the |Field::Zero()|.\n  constexpr const Field& operator[](const Literal& literal) const {\n    return coefficients_[literal];\n  }\n\n  // Returns a reference to the leading coefficient if it exists.\n  // Otherwise, returns a reference to the |Field::Zero()|.\n  constexpr const Field& GetLeadingCoefficient() const {\n    return coefficients_.GetLeadingCoefficient();\n  }\n\n  constexpr size_t Degree() const { return coefficients_.Degree(); }\n\n  constexpr Field Evaluate(const Point& point) const {\n    return coefficients_.Evaluate(point);\n  }\n\n  std::string ToString() const { return coefficients_.ToString(); }\n\n#define OPERATION_METHOD(Name)                                              \\\n  template <typename Coefficients2,                                         \\\n            std::enable_if_t<internal::SupportsPoly##Name<                  \\\n                Coefficients, MultivariatePolynomial<Coefficients>,         \\\n                MultivariatePolynomial<Coefficients2>>::value>* = nullptr>  \\\n  constexpr auto Name(const MultivariatePolynomial<Coefficients2>& other)   \\\n      const {                                                               \\\n    return internal::MultivariatePolynomialOp<Coefficients>::Name(*this,    \\\n                                                                  other);   \\\n  }                                                                         \\\n                                                                            \\\n  template <typename Coefficients2,                                         \\\n            std::enable_if_t<internal::SupportsPoly##Name##InPlace<         \\\n                Coefficients, MultivariatePolynomial<Coefficients>,         \\\n                MultivariatePolynomial<Coefficients2>>::value>* = nullptr>  \\\n  constexpr auto& Name##InPlace(                                            \\\n      const MultivariatePolynomial<Coefficients2>& other) {                 \\\n    return internal::MultivariatePolynomialOp<Coefficients>::Name##InPlace( \\\n        *this, other);                                                      \\\n  }\n\n  // AdditiveSemigroup methods\n  OPERATION_METHOD(Add)\n\n  // AdditiveGroup methods\n  OPERATION_METHOD(Sub)\n\n  MultivariatePolynomial Negate() const {\n    return internal::MultivariatePolynomialOp<Coefficients>::Negate(*this);\n  }\n\n  MultivariatePolynomial& NegateInPlace() {\n    return internal::MultivariatePolynomialOp<Coefficients>::NegateInPlace(\n        *this);\n  }\n#undef OPERATION_METHOD\n\n private:\n  friend class internal::MultivariatePolynomialOp<Coefficients>;\n\n  Coefficients coefficients_;\n};\n\ntemplate <typename F, size_t MaxDegree>\nusing MultivariateSparsePolynomial =\n    MultivariatePolynomial<MultivariateSparseCoefficients<F, MaxDegree>>;\n\ntemplate <typename Coefficients>\nclass PolynomialTraits<MultivariatePolynomial<Coefficients>> {\n public:\n  constexpr static bool kIsCoefficientForm = true;\n};\n\n}  // namespace tachyon::math\n\n#include \"tachyon/math/polynomials/multivariate/multivariate_polynomial_ops.h\"\n\n#endif  // TACHYON_MATH_POLYNOMIALS_MULTIVARIATE_MULTIVARIATE_POLYNOMIAL_H_\n"
  },
  {
    "path": "tachyon/math/polynomials/multivariate/multivariate_polynomial_ops.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_POLYNOMIALS_MULTIVARIATE_MULTIVARIATE_POLYNOMIAL_OPS_H_\n#define TACHYON_MATH_POLYNOMIALS_MULTIVARIATE_MULTIVARIATE_POLYNOMIAL_OPS_H_\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/openmp_util.h\"\n#include \"tachyon/math/polynomials/multivariate/multivariate_polynomial.h\"\n\nnamespace tachyon::math {\nnamespace internal {\n\ntemplate <typename F, size_t MaxDegree>\nclass MultivariatePolynomialOp<MultivariateSparseCoefficients<F, MaxDegree>> {\n public:\n  using S = MultivariateSparseCoefficients<F, MaxDegree>;\n  using Term = typename S::Term;\n  using Terms = std::vector<Term>;\n\n  static MultivariatePolynomial<S> Add(const MultivariatePolynomial<S>& self,\n                                       const MultivariatePolynomial<S>& other) {\n    if (self.IsZero()) {\n      return other;\n    } else if (other.IsZero()) {\n      return self;\n    }\n\n    MultivariatePolynomial<S> ret;\n    DoAdd<false>(self, other, ret);\n    return ret;\n  }\n\n  static MultivariatePolynomial<S>& AddInPlace(\n      MultivariatePolynomial<S>& self, const MultivariatePolynomial<S>& other) {\n    if (self.IsZero()) {\n      return self = other;\n    } else if (other.IsZero()) {\n      return self;\n    }\n\n    DoAdd<false>(self, other, self);\n    return self;\n  }\n\n  static MultivariatePolynomial<S> Sub(const MultivariatePolynomial<S>& self,\n                                       const MultivariatePolynomial<S>& other) {\n    if (self.IsZero()) {\n      return -other;\n    } else if (other.IsZero()) {\n      return self;\n    }\n\n    MultivariatePolynomial<S> ret;\n    DoAdd<true>(self, other, ret);\n    return ret;\n  }\n\n  static MultivariatePolynomial<S>& SubInPlace(\n      MultivariatePolynomial<S>& self, const MultivariatePolynomial<S>& other) {\n    if (self.IsZero()) {\n      return self = -other;\n    } else if (other.IsZero()) {\n      return self;\n    }\n\n    DoAdd<true>(self, other, self);\n    return self;\n  }\n\n  static MultivariatePolynomial<S> Negate(\n      const MultivariatePolynomial<S>& self) {\n    if (self.IsZero()) {\n      return self;\n    }\n    const Terms& i_terms = self.coefficients_.terms_;\n    Terms o_terms(i_terms.size());\n    OMP_PARALLEL_FOR(size_t i = 0; i < o_terms.size(); ++i) {\n      o_terms[i] = -i_terms[i];\n    }\n    return MultivariatePolynomial<S>(\n        S(self.coefficients_.num_vars_, std::move(o_terms)));\n  }\n\n  static MultivariatePolynomial<S>& NegateInPlace(\n      MultivariatePolynomial<S>& self) {\n    if (self.IsZero()) {\n      return self;\n    }\n    Terms& terms = self.coefficients_.terms_;\n    // clang-format off\n    OMP_PARALLEL_FOR(Term& term : terms) { term.coefficient.NegateInPlace(); }\n    // clang-format on\n    return self;\n  }\n\n private:\n  template <bool NEGATION>\n  static void DoAdd(const MultivariatePolynomial<S>& a,\n                    const MultivariatePolynomial<S>& b,\n                    MultivariatePolynomial<S>& c) {\n    const Terms& a_terms = a.coefficients_.terms_;\n    const Terms& b_terms = b.coefficients_.terms_;\n    Terms c_terms;\n\n    auto a_it = a_terms.begin();\n    auto b_it = b_terms.begin();\n    while (a_it != a_terms.end() || b_it != b_terms.end()) {\n      if (a_it == a_terms.end()) {\n        if constexpr (NEGATION) {\n          c_terms.push_back(-(*b_it));\n        } else {\n          c_terms.push_back(*b_it);\n        }\n        ++b_it;\n        continue;\n      }\n      if (b_it == b_terms.end()) {\n        c_terms.push_back(*a_it);\n        ++a_it;\n        continue;\n      }\n      if (a_it->literal < b_it->literal) {\n        c_terms.push_back(*a_it);\n        ++a_it;\n      } else if (b_it->literal < a_it->literal) {\n        if constexpr (NEGATION) {\n          c_terms.push_back(-(*b_it));\n        } else {\n          c_terms.push_back(*b_it);\n        }\n        ++b_it;\n      } else {\n        F coeff;\n        if constexpr (NEGATION) {\n          coeff = a_it->coefficient - b_it->coefficient;\n        } else {\n          coeff = a_it->coefficient + b_it->coefficient;\n        }\n        if (!coeff.IsZero()) {\n          c_terms.push_back({a_it->literal, std::move(coeff)});\n        }\n        ++a_it;\n        ++b_it;\n      }\n    }\n\n    c.coefficients_.terms_ = std::move(c_terms);\n    c.coefficients_.Compact();\n  }\n};\n\n}  // namespace internal\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_POLYNOMIALS_MULTIVARIATE_MULTIVARIATE_POLYNOMIAL_OPS_H_\n"
  },
  {
    "path": "tachyon/math/polynomials/multivariate/multivariate_polynomial_unittest.cc",
    "content": "#include \"tachyon/math/polynomials/multivariate/multivariate_polynomial.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\nconst size_t kNumVars = 2;\nconst size_t kMaxDegree = 5;\n\nusing Point = std::vector<GF7>;\nusing Poly = MultivariateSparsePolynomial<GF7, kMaxDegree>;\nusing Coeffs = MultivariateSparseCoefficients<GF7, kMaxDegree>;\n\nclass MultivariatePolynomialTest : public FiniteFieldTest<GF7> {\n public:\n  void SetUp() override {\n    // poly0: 2\n    polys_.push_back(Poly(Coeffs(1, {\n                                        {\n                                            {\n                                                {\n                                                    {0, 0}  // Element\n                                                }           // vector<Element>\n                                            },              // Literal\n                                            GF7(2)          // coefficient\n                                        }                   // Term\n                                    })));                   // vector<Term>\n    // poly1: 1 * x₀\n    polys_.push_back(Poly(Coeffs(1, {\n                                        {\n                                            {\n                                                {\n                                                    {0, 1}  // Element\n                                                }           // vector<Element>\n                                            },              // Literal\n                                            GF7(1)          // coefficient\n                                        }                   // Term\n                                    })));                   // vector<Term>\n    // poly2: 1 * x₀ (num_vars = 2)\n    polys_.push_back(Poly(Coeffs(2, {\n                                        {\n                                            {\n                                                {\n                                                    {0, 1}  // Element\n                                                }           // vector<Element>\n                                            },              // Literal\n                                            GF7(1)          // coefficient\n                                        }                   // Term\n                                    })));                   // vector<Term>\n    // poly3: 1 * x₀ + 1 * x₁\n    polys_.push_back(Poly(Coeffs(2, {\n                                        {\n                                            {\n                                                {\n                                                    {0, 1}  // Element\n                                                }           // vector<Element>\n                                            },              // Literal\n                                            GF7(1)          // coefficient\n                                        },                  // Term\n                                        {\n                                            {\n                                                {\n                                                    {1, 1}  // Element\n                                                }           // vector<Element>\n                                            },              // Literal\n                                            GF7(1)          // coefficient\n                                        }                   // Term\n                                    })));                   // vector<Term>\n\n    // poly4: 1 * x₁ + 1 * x₀\n    polys_.push_back(Poly(Coeffs(2, {\n                                        {\n                                            {\n                                                {\n                                                    {0, 1}  // Element\n                                                }           // vector<Element>\n                                            },              // Literal\n                                            GF7(1)          // coefficient\n                                        },                  // Term\n                                        {\n                                            {\n                                                {\n                                                    {1, 1}  // Element\n                                                }           // vector<Element>\n                                            },              // Literal\n                                            GF7(1)          // coefficient\n                                        }                   // Term\n                                    })));                   // vector<Term>\n\n    // poly5: 1 * x₀² + 2 * x₀x₁ + 3 * x₁² + 4\n    polys_.push_back(Poly(Coeffs(2, {\n                                        {\n                                            {\n                                                {\n                                                    {}  // Element\n                                                }       // vector<Element>\n                                            },          // Literal\n                                            GF7(4)      // coefficient\n                                        },              // Term\n                                        {\n                                            {\n                                                {\n                                                    {0, 2}  // Element\n                                                }           // vector<Element>\n                                            },              // Literal\n                                            GF7(1)          // coefficient\n                                        },                  // Term\n                                        {\n                                            {\n                                                {\n                                                    {0, 1},  // Element\n                                                    {1, 1}   // Element\n                                                }            // vector<Element>\n                                            },               // Literal\n                                            GF7(2)           // coefficient\n                                        },                   // Term\n                                        {\n                                            {\n                                                {\n                                                    {1, 2}  // Element\n                                                }           // vector<Element>\n                                            },              // Literal\n                                            GF7(3)          // coefficient\n                                        }                   // Term\n                                    })));                   // vector<Term>\n    // poly6: 1 * x₀³ + 2 * x₀x₁² + 3 * x₁³ + 4\n    polys_.push_back(Poly(Coeffs(2, {\n                                        {\n                                            {\n                                                {\n                                                    {1, 0}  // Element\n                                                }           // vector<Element>\n                                            },              // Literal\n                                            GF7(4)          // coefficient\n                                        },                  // Term\n                                        {\n                                            {\n                                                {\n                                                    {0, 3}  // Element\n                                                }           // vector<Element>\n                                            },              // Literal\n                                            GF7(1)          // coefficient\n                                        },                  // Term\n                                        {\n                                            {\n                                                {\n                                                    {0, 1},  // Element\n                                                    {1, 2}   // Element\n                                                }            // vector<Element>\n                                            },               // Literal\n                                            GF7(2)           // coefficient\n                                        },                   // Term\n                                        {\n                                            {\n                                                {\n                                                    {1, 3}  // Element\n                                                }           // vector<Element>\n                                            },              // Literal\n                                            GF7(3)          // coefficient\n                                        },                  // Term\n                                    })));                   // vector<Term>\n  }\n\n protected:\n  std::vector<Poly> polys_;\n};\n\n}  // namespace\n\nTEST_F(MultivariatePolynomialTest, IsZero) {\n  EXPECT_TRUE(Poly().IsZero());\n  EXPECT_TRUE(Poly::Zero().IsZero());\n  Coeffs a = Coeffs(1, {\n                           {\n                               {\n                                   {\n                                       {0, 0}  // Element\n                                   }           // vector<Element>\n                               },              // Literal\n                               GF7(0)          // coefficient\n                           }                   // Term\n                       });                     // vector<Term>\n  a.Compact();\n  EXPECT_TRUE(Poly(a).IsZero());\n  for (size_t i = 0; i < polys_.size(); ++i) {\n    EXPECT_FALSE(polys_[i].IsZero());\n  }\n}\n\nTEST_F(MultivariatePolynomialTest, IsOne) {\n  EXPECT_TRUE(Poly::One().IsOne());\n  EXPECT_TRUE(Poly(Coeffs(1,\n                          {\n                              {\n                                  {\n                                      {\n                                          {0, 0}  // Element\n                                      }           // vector<Element>\n                                  },              // Literal\n                                  GF7(1)          // coefficient\n                              }                   // Term\n                          }))                     // vector<Term>\n                  .IsOne());\n  for (size_t i = 0; i < polys_.size() - 1; ++i) {\n    EXPECT_FALSE(polys_[i].IsOne());\n  }\n}\n\nTEST_F(MultivariatePolynomialTest, CreateChecked) {\n  EXPECT_DEATH(Coeffs::CreateChecked(2,\n                                     {\n                                         {\n                                             {\n                                                 {\n                                                     {1, 1}  // Element\n                                                 }           // vector<Element>\n                                             },              // Literal\n                                             GF7(1)          // coefficient\n                                         },                  // Term\n                                         {\n                                             {\n                                                 {\n                                                     {0, 1}  // Element\n                                                 }           // vector<Element>\n                                             },              // Literal\n                                             GF7(1)          // coefficient\n                                         }                   // Term\n                                     }),                     // vector<Term>\n               \"\");\n  EXPECT_EQ(Poly(Coeffs::CreateChecked(2,\n                                       {\n                                           {\n                                               {\n                                                   {\n                                                       {0, 1}  // Element\n                                                   }   // vector<Element>\n                                               },      // Literal\n                                               GF7(1)  // coefficient\n                                           },          // Term\n                                           {\n                                               {\n                                                   {\n                                                       {1, 1}  // Element\n                                                   }   // vector<Element>\n                                               },      // Literal\n                                               GF7(1)  // coefficient\n                                           }           // Term\n                                       }))             // vector<Term>\n            ,\n            polys_[3]);\n}\n\nTEST_F(MultivariatePolynomialTest, Random) {\n  bool success = false;\n  Poly r = Poly::Random(kNumVars, kMaxDegree);\n  for (size_t i = 0; i < 100; ++i) {\n    if (r != Poly::Random(kNumVars, kMaxDegree)) {\n      success = true;\n      break;\n    }\n  }\n  EXPECT_TRUE(success);\n}\n\nTEST_F(MultivariatePolynomialTest, IndexingOperator) {\n  struct {\n    const Poly& poly;\n    Coeffs::Literal literal;\n    GF7 expected;\n  } tests[] = {\n      {polys_[0], {{{}}}, GF7(2)},\n      {polys_[1], {{{0, 1}}}, GF7(1)},\n      {polys_[2], {{{0, 1}}}, GF7(1)},\n      {polys_[3], {{{0, 1}}}, GF7(1)},\n      {polys_[3], {{{1, 1}}}, GF7(1)},\n      {polys_[4], {{{0, 1}}}, GF7(1)},\n      {polys_[4], {{{1, 1}}}, GF7(1)},\n      {polys_[5], {{{0, 2}}}, GF7(1)},\n      {polys_[5], {{{0, 1}, {1, 1}}}, GF7(2)},\n      {polys_[5], {{{1, 2}}}, GF7(3)},\n      {polys_[5], {{{}}}, GF7(4)},\n      {polys_[6], {{{0, 3}}}, GF7(1)},\n      {polys_[6], {{{0, 1}, {1, 2}}}, GF7(2)},\n      {polys_[6], {{{1, 3}}}, GF7(3)},\n      {polys_[6], {{{}}}, GF7(4)},\n  };\n\n  for (const auto& test : tests) {\n    EXPECT_EQ(test.poly[test.literal], test.expected);\n  }\n}\n\nTEST_F(MultivariatePolynomialTest, Degree) {\n  struct {\n    const Poly& poly;\n    size_t degree;\n  } tests[] = {{polys_[0], 0}, {polys_[1], 1}, {polys_[2], 1}, {polys_[3], 1},\n               {polys_[4], 1}, {polys_[5], 2}, {polys_[6], 3}};\n\n  for (const auto& test : tests) {\n    EXPECT_EQ(test.poly.Degree(), test.degree);\n  }\n  EXPECT_LE(Poly::Random(kNumVars, kMaxDegree).Degree(), kMaxDegree);\n}\n\nTEST_F(MultivariatePolynomialTest, Evaluate) {\n  struct {\n    const Poly& poly;\n    Point evaluation_point;\n    GF7 expected;\n  } tests[] = {\n      {polys_[0], {}, GF7(2)},\n      {polys_[1], {GF7(3)}, GF7(3)},\n      {polys_[2], {GF7(3)}, GF7(3)},\n      {polys_[3], {GF7(3), GF7(3)}, GF7(6)},\n      {polys_[4], {GF7(3), GF7(3)}, GF7(6)},\n      {polys_[5], {GF7(3), GF7(3)}, GF7(2)},\n      {polys_[6], {GF7(3), GF7(3)}, GF7(5)},\n  };\n\n  for (const auto& test : tests) {\n    EXPECT_EQ(test.poly.Evaluate(test.evaluation_point), test.expected);\n  }\n}\n\nTEST_F(MultivariatePolynomialTest, ToString) {\n  struct {\n    const Poly& poly;\n    std::string_view expected;\n  } tests[] = {\n      {polys_[0], \"2\"},\n      {polys_[1], \"1 * x_0\"},\n      {polys_[2], \"1 * x_0\"},\n      {polys_[3], \"1 * x_1 + 1 * x_0\"},\n      {polys_[4], \"1 * x_1 + 1 * x_0\"},\n      {polys_[5], \"3 * x_1^2 + 2 * x_1x_0 + 1 * x_0^2 + 4\"},\n      {polys_[6], \"3 * x_1^3 + 2 * x_1^2x_0 + 1 * x_0^3 + 4\"},\n  };\n\n  EXPECT_EQ(Poly().ToString(), \"\");\n\n  for (const auto& test : tests) {\n    EXPECT_EQ(test.poly.ToString(), test.expected);\n  }\n}\n\nTEST_F(MultivariatePolynomialTest, AdditiveOperators) {\n  struct {\n    const Poly& a;\n    const Poly& b;\n    Poly sum;\n    Poly amb;\n    Poly bma;\n  } tests[] = {\n      {\n          polys_[0], polys_[1],\n          Poly(Coeffs(1,\n                      {\n                          {\n                              {\n                                  {\n                                      {0, 0}  // Element\n                                  }           // vector<Element>\n                              },              // Literal\n                              GF7(2)          // coefficient\n                          },                  // Term\n                          {\n                              {\n                                  {\n                                      {0, 1}  // Element\n                                  }           // vector<Element>\n                              },              // Literal\n                              GF7(1)          // coefficient\n                          }                   // Term\n                      })),                    // vector<Term>\n          Poly(Coeffs(1,\n                      {\n                          {\n                              {\n                                  {\n                                      {0, 0}  // Element\n                                  }           // vector<Element>\n                              },              // Literal\n                              GF7(2)          // coefficient\n                          },                  // Term\n                          {\n                              {\n                                  {\n                                      {0, 1}  // Element\n                                  }           // vector<Element>\n                              },              // Literal\n                              GF7(6)          // coefficient\n                          }                   // Term\n                      })),                    // vector<Term>\n          Poly(Coeffs(1,\n                      {\n                          {\n                              {\n                                  {\n                                      {0, 0}  // Element\n                                  }           // vector<Element>\n                              },              // Literal\n                              GF7(5)          // coefficient\n                          },                  // Term\n                          {\n                              {\n                                  {\n                                      {0, 1}  // Element\n                                  }           // vector<Element>\n                              },              // Literal\n                              GF7(1)          // coefficient\n                          }                   // Term\n                      })),                    // vector<Term>\n      },\n      {\n          polys_[0], polys_[3],\n          Poly(Coeffs(2,\n                      {\n                          {\n                              {\n                                  {\n                                      {0, 0}  // Element\n                                  }           // vector<Element>\n                              },              // Literal\n                              GF7(2)          // coefficient\n                          },                  // Term\n                          {\n                              {\n                                  {\n                                      {0, 1}  // Element\n                                  }           // vector<Element>\n                              },              // Literal\n                              GF7(1)          // coefficient\n                          },                  // Term\n                          {\n                              {\n                                  {\n                                      {1, 1}  // Element\n                                  }           // vector<Element>\n                              },              // Literal\n                              GF7(1)          // coefficient\n                          }                   // Term\n                      })),                    // vector<Term>\n          Poly(Coeffs(2,\n                      {\n                          {\n                              {\n                                  {\n                                      {0, 0}  // Element\n                                  }           // vector<Element>\n                              },              // Literal\n                              GF7(2)          // coefficient\n                          },                  // Term\n                          {\n                              {\n                                  {\n                                      {0, 1}  // Element\n                                  }           // vector<Element>\n                              },              // Literal\n                              GF7(6)          // coefficient\n                          },                  // Term\n                          {\n                              {\n                                  {\n                                      {1, 1}  // Element\n                                  }           // vector<Element>\n                              },              // Literal\n                              GF7(6)          // coefficient\n                          }                   // Term\n                      })),                    // vector<Term>\n\n          Poly(Coeffs(2,\n                      {\n                          {\n                              {\n                                  {\n                                      {0, 0}  // Element\n                                  }           // vector<Element>\n                              },              // Literal\n                              GF7(5)          // coefficient\n                          },                  // Term\n                          {\n                              {\n                                  {\n                                      {0, 1}  // Element\n                                  }           // vector<Element>\n                              },              // Literal\n                              GF7(1)          // coefficient\n                          },                  // Term\n                          {\n                              {\n                                  {\n                                      {1, 1}  // Element\n                                  }           // vector<Element>\n                              },              // Literal\n                              GF7(1)          // coefficient\n                          }                   // Term\n                      })),                    // vector<Term>\n      },\n      {polys_[5], polys_[6],\n       Poly(Coeffs(2,\n                   {\n                       {\n                           {\n                               {\n                                   {1, 0}  // Element\n                               }           // vector<Element>\n                           },              // Literal\n                           GF7(1)          // coefficient\n                       },                  // Term\n                       {\n                           {\n                               {\n                                   {0, 2}  // Element\n                               }           // vector<Element>\n                           },              // Literal\n                           GF7(1)          // coefficient\n                       },                  // Term\n                       {\n                           {\n                               {\n                                   {0, 3}  // Element\n                               }           // vector<Element>\n                           },              // Literal\n                           GF7(1)          // coefficient\n                       },                  // Term\n                       {\n                           {\n                               {\n                                   {0, 1},  // Element\n                                   {1, 1}   // Element\n                               }            // vector<Element>\n                           },               // Literal\n                           GF7(2)           // coefficient\n                       },                   // Term\n                       {\n                           {\n                               {\n                                   {1, 2}  // Element\n                               }           // vector<Element>\n                           },              // Literal\n                           GF7(3)          // coefficient\n                       },                  // Term\n                       {\n                           {\n                               {\n                                   {0, 1},  // Element\n                                   {1, 2}   // Element\n                               }            // vector<Element>\n                           },               // Literal\n                           GF7(2)           // coefficient\n                       },                   // Term\n                       {\n                           {\n                               {\n                                   {1, 3}  // Element\n                               }           // vector<Element>\n                           },              // Literal\n                           GF7(3)          // coefficient\n                       },                  // Term\n                   })),                    // vector<Term>\n       Poly(Coeffs(2,\n                   {\n                       {\n                           {\n                               {\n                                   {0, 2}  // Element\n                               }           // vector<Element>\n                           },              // Literal\n                           GF7(1)          // coefficient\n                       },                  // Term\n                       {\n                           {\n                               {\n                                   {0, 3}  // Element\n                               }           // vector<Element>\n                           },              // Literal\n                           GF7(6)          // coefficient\n                       },                  // Term\n                       {\n                           {\n                               {\n                                   {0, 1},  // Element\n                                   {1, 1}   // Element\n                               }            // vector<Element>\n                           },               // Literal\n                           GF7(2)           // coefficient\n                       },                   // Term\n                       {\n                           {\n                               {\n                                   {1, 2}  // Element\n                               }           // vector<Element>\n                           },              // Literal\n                           GF7(3)          // coefficient\n                       },                  // Term\n                       {\n                           {\n                               {\n                                   {0, 1},  // Element\n                                   {1, 2}   // Element\n                               }            // vector<Element>\n                           },               // Literal\n                           GF7(5)           // coefficient\n                       },                   // Term\n                       {\n                           {\n                               {\n                                   {1, 3}  // Element\n                               }           // vector<Element>\n                           },              // Literal\n                           GF7(4)          // coefficient\n                       },                  // Term\n                   })),                    // vector<Term>\n\n       Poly(Coeffs(2,\n                   {\n                       {\n                           {\n                               {\n                                   {0, 2}  // Element\n                               }           // vector<Element>\n                           },              // Literal\n                           GF7(6)          // coefficient\n                       },                  // Term\n                       {\n                           {\n                               {\n                                   {0, 3}  // Element\n                               }           // vector<Element>\n                           },              // Literal\n                           GF7(1)          // coefficient\n                       },                  // Term\n                       {\n                           {\n                               {\n                                   {0, 1},  // Element\n                                   {1, 1}   // Element\n                               }            // vector<Element>\n                           },               // Literal\n                           GF7(5)           // coefficient\n                       },                   // Term\n                       {\n                           {\n                               {\n                                   {1, 2}  // Element\n                               }           // vector<Element>\n                           },              // Literal\n                           GF7(4)          // coefficient\n                       },                  // Term\n                       {\n                           {\n                               {\n                                   {0, 1},  // Element\n                                   {1, 2}   // Element\n                               }            // vector<Element>\n                           },               // Literal\n                           GF7(2)           // coefficient\n                       },                   // Term\n                       {\n                           {\n                               {\n                                   {1, 3}  // Element\n                               }           // vector<Element>\n                           },              // Literal\n                           GF7(3)          // coefficient\n                       },                  // Term\n                   }))},                   // vector<Term>\n  };\n\n  for (const auto& test : tests) {\n    EXPECT_EQ(test.a + test.b, test.sum);\n    EXPECT_EQ(test.b + test.a, test.sum);\n    EXPECT_EQ(test.a - test.b, test.amb);\n    EXPECT_EQ(test.b - test.a, test.bma);\n\n    Poly tmp = test.a;\n    tmp += test.b;\n    EXPECT_EQ(tmp, test.sum);\n    tmp -= test.b;\n    EXPECT_EQ(tmp, test.a);\n  }\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/polynomials/multivariate/multivariate_sparse_coefficients.h",
    "content": "#ifndef TACHYON_MATH_POLYNOMIALS_MULTIVARIATE_MULTIVARIATE_SPARSE_COEFFICIENTS_H_\n#define TACHYON_MATH_POLYNOMIALS_MULTIVARIATE_MULTIVARIATE_SPARSE_COEFFICIENTS_H_\n\n#include <stddef.h>\n\n#include <algorithm>\n#include <functional>\n#include <sstream>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"absl/numeric/internal/bits.h\"\n\n#include \"tachyon/base/containers/adapters.h\"\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/containers/cxx20_erase_vector.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/parallelize.h\"\n#include \"tachyon/base/random.h\"\n#include \"tachyon/base/ranges/algorithm.h\"\n#include \"tachyon/base/strings/string_util.h\"\n#include \"tachyon/math/base/big_int.h\"\n#include \"tachyon/math/polynomials/multivariate/support_poly_operators.h\"\n\nnamespace tachyon::math {\ntemplate <typename F, size_t MaxDegree>\nclass MultivariateSparseCoefficients {\n public:\n  constexpr static size_t kMaxDegree = MaxDegree;\n  constexpr static F kZero = F::Zero();\n\n  using Field = F;\n  using Point = std::vector<F>;\n\n  struct Element {\n    size_t variable = 0;\n    size_t exponent = 0;\n\n    bool operator<(const Element& other) const {\n      if (exponent != 0 && other.exponent != 0) {\n        if (variable != other.variable) {\n          return variable < other.variable;\n        }\n        // At this point, variables are equal, so compare the exponents.\n        return exponent < other.exponent;\n      }\n      // If either exponent is zero, compare the exponents directly.\n      // When both exponents are zero, the order based on the variable does not\n      // matter since constant terms are compacted. See the Compact() method.\n      return exponent < other.exponent;\n    }\n    bool operator==(const Element& other) const {\n      return variable == other.variable && exponent == other.exponent;\n    }\n    bool operator!=(const Element& other) const {\n      return variable != other.variable || exponent != other.exponent;\n    }\n  };\n\n  struct Literal {\n    std::vector<Element> elements;\n\n    Literal() = default;\n\n    // NOLINTNEXTLINE(runtime/explicit)\n    Literal(const std::vector<Element>& elems) : elements(elems) {}\n\n    // NOLINTNEXTLINE(runtime/explicit)\n    Literal(std::vector<Element>&& elems) : elements(std::move(elems)) {}\n\n    constexpr static Literal CreateChecked(\n        const std::vector<Element>& elements) {\n      CHECK(base::ranges::is_sorted(elements.begin(), elements.end()));\n      return Literal(elements);\n    }\n\n    constexpr static Literal CreateChecked(std::vector<Element>&& elements) {\n      CHECK(base::ranges::is_sorted(elements.begin(), elements.end()));\n      return Literal(std::move(elements));\n    }\n\n    bool operator<(const Literal& other) const {\n      if (elements.back() == other.elements.back()) {\n        return elements.size() < other.elements.size();\n      } else {\n        return elements.back() < other.elements.back();\n      }\n    }\n    bool operator==(const Literal& other) const {\n      return (Degree() == 0 && other.Degree() == 0) ||\n             elements == other.elements;\n    }\n    bool operator!=(const Literal& other) const {\n      return (Degree() != 0 || other.Degree() != 0) &&\n             elements != other.elements;\n    }\n\n    size_t Degree() const {\n      if (elements.empty()) return 0;\n      return std::accumulate(\n          elements.begin(), elements.end(), 0,\n          [](size_t acc, const Element& elem) { return acc + elem.exponent; });\n    }\n\n    F Evaluate(const Point& points) const {\n      std::vector<F> results = base::ParallelizeMap(\n          elements, [&points](absl::Span<const Element> chunk) {\n            return EvaluateSerial(chunk, points);\n          });\n      return std::accumulate(results.begin(), results.end(), F::One(),\n                             std::multiplies<>());\n    }\n\n    std::string ToString() const {\n      std::stringstream ss;\n      for (const Element& elem : base::Reversed(elements)) {\n        ss << \"x_\" << elem.variable;\n        if (elem.exponent > 1) {\n          ss << \"^\" << elem.exponent;\n        }\n      }\n      return ss.str();\n    }\n\n   private:\n    static F EvaluateSerial(absl::Span<const Element> elements,\n                            const Point& points) {\n      return std::accumulate(elements.begin(), elements.end(), F::One(),\n                             [&points](F& acc, const Element& elem) {\n                               return acc *=\n                                      points[elem.variable].Pow(elem.exponent);\n                             });\n    }\n  };\n\n  struct Term {\n    Literal literal;\n    F coefficient;\n\n    Term operator-() const { return {literal, -coefficient}; }\n\n    bool operator<(const Term& other) const {\n      if (literal != other.literal) {\n        return literal < other.literal;\n      } else {\n        return coefficient < other.coefficient;\n      }\n    }\n    bool operator==(const Term& other) const {\n      return literal == other.literal && coefficient == other.coefficient;\n    }\n    bool operator!=(const Term& other) const {\n      return literal != other.literal || coefficient != other.coefficient;\n    }\n\n    size_t Degree() const { return literal.Degree(); }\n\n    static Term Constant(F field) { return {{{{}}}, field}; }\n  };\n\n  using Terms = std::vector<Term>;\n\n  constexpr MultivariateSparseCoefficients() = default;\n  constexpr MultivariateSparseCoefficients(size_t num_vars, const Terms& terms)\n      : num_vars_(num_vars), terms_(terms) {\n    CHECK_LE(Degree(), kMaxDegree);\n  }\n  constexpr MultivariateSparseCoefficients(size_t num_vars, Terms&& terms)\n      : num_vars_(num_vars), terms_(std::move(terms)) {\n    CHECK_LE(Degree(), kMaxDegree);\n  }\n\n  constexpr static MultivariateSparseCoefficients CreateChecked(\n      size_t num_vars, const Terms& terms) {\n    CHECK(base::ranges::is_sorted(terms.begin(), terms.end()));\n    return {num_vars, terms};\n  }\n\n  constexpr static MultivariateSparseCoefficients CreateChecked(size_t num_vars,\n                                                                Terms&& terms) {\n    CHECK(base::ranges::is_sorted(terms.begin(), terms.end()));\n    return {num_vars, std::move(terms)};\n  }\n\n  constexpr static MultivariateSparseCoefficients Zero() {\n    return MultivariateSparseCoefficients();\n  }\n\n  constexpr static MultivariateSparseCoefficients One() {\n    return MultivariateSparseCoefficients(1, {{{{{0, 0}}}, F::One()}});\n  }\n\n  static MultivariateSparseCoefficients Random(size_t arity, size_t exponent,\n                                               size_t min_term = 1,\n                                               size_t max_term = 999) {\n    Terms terms;\n    size_t num_terms = base::Uniform(base::Range<size_t>(min_term, max_term));\n    terms.push_back(Term::Constant(F::Random()));\n    for (size_t i = 1; i < num_terms; ++i) {\n      for (size_t j = 0; j < arity; ++j) {\n        if (base::Bernoulli(0.5) > 0.5) {\n          terms.insert(\n              terms.begin(),\n              {{{{j, base::Uniform(base::Range<size_t>(0, exponent))}}},\n               F::Random()});\n        }\n      }\n    }\n    base::ranges::sort(terms.begin(), terms.end());\n    return MultivariateSparseCoefficients(arity, std::move(terms));\n  }\n\n  constexpr size_t num_vars() const { return num_vars_; }\n\n  constexpr const std::vector<Term>& terms() const { return terms_; }\n  constexpr std::vector<Term>& terms() { return terms_; }\n\n  constexpr bool IsConstant() const {\n    return terms_.size() == 1 && terms_[0].Degree() == 0;\n  }\n\n  constexpr bool operator==(const MultivariateSparseCoefficients& other) const {\n    if (IsZero()) {\n      return other.IsZero();\n    }\n    if (other.IsZero()) {\n      return false;\n    }\n    return terms_ == other.terms_;\n  }\n\n  constexpr bool operator!=(const MultivariateSparseCoefficients& other) const {\n    return !operator==(other);\n  }\n\n  constexpr F& at(const Literal& literal) {\n    const F* ptr = const_cast<F*>(std::as_const(*this).GetCoefficient(literal));\n    if (ptr) return *ptr;\n    return kZero;\n  }\n\n  constexpr const F& at(const Literal& literal) const {\n    return (*this)[literal];\n  }\n\n  constexpr const F& operator[](const Literal& literal) const {\n    const F* ptr = GetCoefficient(literal);\n    if (ptr) return *ptr;\n    return kZero;\n  }\n\n  constexpr const F& GetLeadingCoefficient() const {\n    if (IsZero()) return kZero;\n    return terms_.begin().coefficient;\n  }\n\n  constexpr bool IsZero() const { return terms_.empty(); }\n\n  constexpr bool IsOne() const {\n    return terms_.size() == 1 && terms_[0].Degree() == 0 &&\n           terms_[0].coefficient.IsOne();\n  }\n\n  constexpr size_t Degree() const {\n    if (IsConstant()) return 0;\n    return terms_.back().Degree();\n  }\n\n  constexpr F Evaluate(const Point& points) const {\n    CHECK_LE(points.size(), num_vars_) << \"Invalid evaluation domain\";\n    if (IsZero()) {\n      return F::Zero();\n    }\n    std::vector<F> results =\n        base::ParallelizeMap(terms_, [&points](absl::Span<const Term> chunk) {\n          return EvaluateSerial(chunk, points);\n        });\n    return std::accumulate(results.begin(), results.end(), F::Zero(),\n                           std::plus<>());\n  }\n\n  std::string ToString() const {\n    if (IsZero()) return base::EmptyString();\n    std::stringstream ss;\n    bool has_term = false;\n    for (const Term& term : base::Reversed(terms_)) {\n      if (has_term) ss << \" + \";\n      has_term = true;\n      ss << term.coefficient\n                .ToString();  // No need to consider the case where\n                              // the coefficient is 0 since we\n                              // compact the polynomial on initialization.\n      if (term.literal.Degree() == 0) {\n        // do nothing\n      } else {\n        ss << \" * \" << term.literal.ToString();\n      }\n    }\n    return ss.str();\n  }\n\n  void Compact() {\n    auto write_itr = terms_.begin();\n    for (auto read_itr = terms_.begin() + 1; read_itr != terms_.end();\n         ++read_itr) {\n      if (write_itr->literal == read_itr->literal) {\n        write_itr->coefficient += read_itr->coefficient;\n      } else {\n        ++write_itr;\n        *write_itr = *read_itr;\n      }\n    }\n    terms_.erase(write_itr + 1, terms_.end());\n    // Compact the constant term.\n    F constant = F::Zero();\n    while (terms_.size() > 0 && terms_.front().Degree() == 0) {\n      constant += terms_.front().coefficient;\n      terms_.erase(terms_.begin());\n    }\n    terms_.insert(terms_.begin(), Term::Constant(constant));\n    base::EraseIf(terms_,\n                  [](const Term& term) { return term.coefficient.IsZero(); });\n  }\n\n private:\n  friend class internal::MultivariatePolynomialOp<\n      MultivariateSparseCoefficients<F, MaxDegree>>;\n\n  constexpr const F* GetCoefficient(const Literal& literal) const {\n    auto it = std::lower_bound(terms_.begin(), terms_.end(), literal,\n                               [](const Term& term, const Literal& literal) {\n                                 return term.literal < literal;\n                               });\n    if (it != terms_.end() && it->literal == literal) {\n      return &it->coefficient;\n    }\n    return nullptr;\n  }\n\n  static F EvaluateSerial(absl::Span<const Term> terms, const Point& points) {\n    return std::accumulate(\n        terms.begin(), terms.end(), F::Zero(),\n        [&points](F& acc, const Term& term) {\n          return acc += (term.coefficient * term.literal.Evaluate(points));\n        });\n  }\n\n  size_t num_vars_;\n  std::vector<Term> terms_;\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_POLYNOMIALS_MULTIVARIATE_MULTIVARIATE_SPARSE_COEFFICIENTS_H_\n"
  },
  {
    "path": "tachyon/math/polynomials/multivariate/support_poly_operators.h",
    "content": "#ifndef TACHYON_MATH_POLYNOMIALS_MULTIVARIATE_SUPPORT_POLY_OPERATORS_H_\n#define TACHYON_MATH_POLYNOMIALS_MULTIVARIATE_SUPPORT_POLY_OPERATORS_H_\n\n#include \"tachyon/math/polynomials/support_poly_operators.h\"\n\n#define SUPPORTS_POLY_OPERATOR(Name)                                       \\\n  template <typename Coefficients, typename L, typename R>                 \\\n  struct SupportsPoly##Name<                                               \\\n      Coefficients, L, R,                                                  \\\n      decltype(void(MultivariatePolynomialOp<Coefficients>::Name(          \\\n          std::declval<const L&>(), std::declval<const R&>())))>           \\\n      : std::true_type {};                                                 \\\n                                                                           \\\n  template <typename Coefficients, typename L, typename R>                 \\\n  struct SupportsPoly##Name##InPlace<                                      \\\n      Coefficients, L, R,                                                  \\\n      decltype(void(MultivariatePolynomialOp<Coefficients>::Name##InPlace( \\\n          std::declval<L&>(), std::declval<const R&>())))> : std::true_type {}\n\n#define SUPPORTS_MLE_OPERATOR(Name)                                     \\\n  template <typename Evaluations, typename L, typename R>               \\\n  struct SupportsPoly##Name<                                            \\\n      Evaluations, L, R,                                                \\\n      decltype(void(MultilinearExtensionOp<Evaluations>::Name(          \\\n          std::declval<const L&>(), std::declval<const R&>())))>        \\\n      : std::true_type {};                                              \\\n                                                                        \\\n  template <typename Evaluations, typename L, typename R>               \\\n  struct SupportsPoly##Name##InPlace<                                   \\\n      Evaluations, L, R,                                                \\\n      decltype(void(MultilinearExtensionOp<Evaluations>::Name##InPlace( \\\n          std::declval<L&>(), std::declval<const R&>())))> : std::true_type {}\n\nnamespace tachyon::math::internal {\n\ntemplate <typename Coefficients, typename SFINAE = void>\nclass MultivariatePolynomialOp;\n\nSUPPORTS_POLY_OPERATOR(Add);\nSUPPORTS_POLY_OPERATOR(Sub);\n\ntemplate <typename Evaluations, typename SFINAE = void>\nclass MultilinearExtensionOp;\n\nSUPPORTS_MLE_OPERATOR(Add);\nSUPPORTS_MLE_OPERATOR(Sub);\nSUPPORTS_MLE_OPERATOR(Mul);\nSUPPORTS_MLE_OPERATOR(Div);\nSUPPORTS_MLE_OPERATOR(Mod);\n\n}  // namespace tachyon::math::internal\n\n#undef SUPPORTS_POLY_OPERATOR\n#undef SUPPORTS_MLE_OPERATOR\n\n#endif  // TACHYON_MATH_POLYNOMIALS_MULTIVARIATE_SUPPORT_POLY_OPERATORS_H_\n"
  },
  {
    "path": "tachyon/math/polynomials/polynomial.h",
    "content": "#ifndef TACHYON_MATH_POLYNOMIALS_POLYNOMIAL_H_\n#define TACHYON_MATH_POLYNOMIALS_POLYNOMIAL_H_\n\n#include <type_traits>\n\n#include \"tachyon/math/base/ring.h\"\n#include \"tachyon/math/polynomials/polynomial_traits_forward.h\"\n\nnamespace tachyon::math {\ntemplate <typename T, typename SFINAE = void>\nclass Polynomial;\n\ntemplate <typename Derived>\nclass Polynomial<\n    Derived, std::enable_if_t<PolynomialTraits<Derived>::kIsCoefficientForm>>\n    : public Ring<Derived> {};\n\ntemplate <typename Derived>\nclass Polynomial<Derived,\n                 std::enable_if_t<PolynomialTraits<Derived>::kIsEvaluationForm>>\n    : public Ring<Derived> {};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_POLYNOMIALS_POLYNOMIAL_H_\n"
  },
  {
    "path": "tachyon/math/polynomials/polynomial_traits_forward.h",
    "content": "#ifndef TACHYON_MATH_POLYNOMIALS_POLYNOMIAL_TRAITS_FORWARD_H_\n#define TACHYON_MATH_POLYNOMIALS_POLYNOMIAL_TRAITS_FORWARD_H_\n\n#include <type_traits>\n\n#include \"tachyon/math/base/ring.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename T>\nclass PolynomialTraits;\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_POLYNOMIALS_POLYNOMIAL_TRAITS_FORWARD_H_\n"
  },
  {
    "path": "tachyon/math/polynomials/support_poly_operators.h",
    "content": "#ifndef TACHYON_MATH_POLYNOMIALS_SUPPORT_POLY_OPERATORS_H_\n#define TACHYON_MATH_POLYNOMIALS_SUPPORT_POLY_OPERATORS_H_\n\n#define SUPPORTS_POLY_OPERATOR(Name)                             \\\n  template <typename T, typename L, typename R, typename = void> \\\n  struct SupportsPoly##Name : std::false_type {};                \\\n                                                                 \\\n  template <typename T, typename L, typename R, typename = void> \\\n  struct SupportsPoly##Name##InPlace : std::false_type {}\n\nnamespace tachyon::math::internal {\n\nSUPPORTS_POLY_OPERATOR(Add);\nSUPPORTS_POLY_OPERATOR(Sub);\nSUPPORTS_POLY_OPERATOR(Mul);\nSUPPORTS_POLY_OPERATOR(Div);\nSUPPORTS_POLY_OPERATOR(Mod);\n\n}  // namespace tachyon::math::internal\n\n#undef SUPPORTS_POLY_OPERATOR\n\n#endif  // TACHYON_MATH_POLYNOMIALS_SUPPORT_POLY_OPERATORS_H_\n"
  },
  {
    "path": "tachyon/math/polynomials/univariate/BUILD.bazel",
    "content": "load(\"//bazel:tachyon.bzl\", \"if_gpu_is_configured\")\nload(\n    \"//bazel:tachyon_cc.bzl\",\n    \"tachyon_cc_library\",\n    \"tachyon_cc_unittest\",\n    \"tachyon_openmp_num_threads_env\",\n)\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"evaluations_utils\",\n    hdrs = [\"evaluations_utils.h\"],\n    deps = [\n        \"//tachyon/base:bits\",\n        \"//tachyon/base:openmp_util\",\n        \"//tachyon/base:profiler\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"fft_algorithm\",\n    srcs = [\"fft_algorithm.cc\"],\n    hdrs = [\"fft_algorithm.h\"],\n    deps = [\n        \"//tachyon:export\",\n        \"//tachyon/base:logging\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"lagrange_interpolation\",\n    hdrs = [\"lagrange_interpolation.h\"],\n    deps = [\n        \":univariate_polynomial\",\n        \"//tachyon/base:parallelize\",\n        \"//tachyon/base:template_util\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"mixed_radix_evaluation_domain\",\n    hdrs = [\"mixed_radix_evaluation_domain.h\"],\n    deps = [\n        \":evaluations_utils\",\n        \":univariate_evaluation_domain\",\n        \"//tachyon/base:profiler\",\n        \"//tachyon/base/numerics:checked_math\",\n        \"//tachyon/math/base/gmp:gmp_util\",\n        \"//tachyon/math/finite_fields:prime_field_base\",\n        \"@com_google_absl//absl/memory\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"naive_batch_fft\",\n    hdrs = [\"naive_batch_fft.h\"],\n    deps = [\n        \":two_adic_subgroup\",\n        \"//tachyon/base:bits\",\n        \"//tachyon/base:parallelize\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"radix2_evaluation_domain\",\n    hdrs = [\"radix2_evaluation_domain.h\"],\n    deps = [\n        \":evaluations_utils\",\n        \":radix2_twiddle_cache\",\n        \":two_adic_subgroup\",\n        \":univariate_evaluation_domain\",\n        \"//tachyon/base:bits\",\n        \"//tachyon/base:openmp_util\",\n        \"//tachyon/base:parallelize\",\n        \"//tachyon/base:profiler\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/math/finite_fields:packed_prime_field_base\",\n        \"//tachyon/math/matrix:matrix_types\",\n        \"//tachyon/math/matrix:matrix_utils\",\n        \"@com_google_absl//absl/memory\",\n        \"@com_google_absl//absl/types:span\",\n        \"@com_google_googletest//:gtest_prod\",\n        \"@eigen_archive//:eigen3\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"radix2_twiddle_cache\",\n    hdrs = [\"radix2_twiddle_cache.h\"],\n    deps = [\n        \"//tachyon/base:no_destructor\",\n        \"//tachyon/base:openmp_util\",\n        \"//tachyon/base:profiler\",\n        \"@com_google_absl//absl/base:core_headers\",\n        \"@com_google_absl//absl/container:flat_hash_map\",\n        \"@com_google_absl//absl/synchronization\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"two_adic_subgroup\",\n    hdrs = [\"two_adic_subgroup.h\"],\n    deps = [\n        \":two_adic_subgroup_traits_forward\",\n        \"//tachyon/base:optional\",\n        \"//tachyon/math/matrix:matrix_types\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"two_adic_subgroup_traits_forward\",\n    hdrs = [\"two_adic_subgroup_traits_forward.h\"],\n)\n\ntachyon_cc_library(\n    name = \"univariate_evaluation_domain\",\n    hdrs = [\"univariate_evaluation_domain.h\"],\n    deps = [\n        \":evaluations_utils\",\n        \":fft_algorithm\",\n        \":univariate_evaluation_domain_forwards\",\n        \":univariate_evaluations\",\n        \":univariate_polynomial\",\n        \"//tachyon/base:bits\",\n        \"//tachyon/base:optional\",\n        \"//tachyon/base:parallelize\",\n        \"//tachyon/base:profiler\",\n        \"//tachyon/base:range\",\n        \"//tachyon/math/polynomials:evaluation_domain\",\n        \"//tachyon/math/polynomials/univariate/icicle:fft_algorithm_conversion\",\n        \"//tachyon/math/polynomials/univariate/icicle:icicle_ntt_holder\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"univariate_evaluation_domain_forwards\",\n    hdrs = [\"univariate_evaluation_domain_forwards.h\"],\n)\n\ntachyon_cc_library(\n    name = \"univariate_evaluation_domain_factory\",\n    hdrs = [\"univariate_evaluation_domain_factory.h\"],\n    deps = [\n        \":mixed_radix_evaluation_domain\",\n        \":radix2_evaluation_domain\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"univariate_evaluations\",\n    hdrs = [\n        \"univariate_evaluations.h\",\n        \"univariate_evaluations_ops.h\",\n    ],\n    deps = [\n        \":univariate_evaluation_domain_forwards\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base:openmp_util\",\n        \"//tachyon/base:optional\",\n        \"//tachyon/base:parallelize\",\n        \"//tachyon/base/buffer:copyable\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/base/json\",\n        \"//tachyon/math/base:parallelize_threshold\",\n        \"//tachyon/math/polynomials:polynomial\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"univariate_polynomial\",\n    hdrs = [\n        \"support_poly_operators.h\",\n        \"univariate_dense_coefficients.h\",\n        \"univariate_polynomial.h\",\n        \"univariate_polynomial_ops.h\",\n        \"univariate_sparse_coefficients.h\",\n    ],\n    deps = [\n        \":univariate_evaluation_domain_forwards\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base:optional\",\n        \"//tachyon/base:parallelize\",\n        \"//tachyon/base:sort\",\n        \"//tachyon/base/buffer:copyable\",\n        \"//tachyon/base/containers:adapters\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/base/containers:cxx20_erase\",\n        \"//tachyon/base/json\",\n        \"//tachyon/base/ranges:algorithm\",\n        \"//tachyon/base/strings:string_util\",\n        \"//tachyon/math/base:arithmetics_results\",\n        \"//tachyon/math/base:parallelize_threshold\",\n        \"//tachyon/math/polynomials:polynomial\",\n        \"@com_google_absl//absl/hash\",\n        \"@com_google_absl//absl/numeric:bits\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"univariate_unittests\",\n    srcs = [\n        \"lagrange_interpolation_unittest.cc\",\n        \"radix2_evaluation_domain_unittest.cc\",\n        \"univariate_dense_polynomial_unittest.cc\",\n        \"univariate_evaluation_domain_unittest.cc\",\n        \"univariate_evaluations_unittest.cc\",\n        \"univariate_sparse_polynomial_unittest.cc\",\n    ],\n    env = tachyon_openmp_num_threads_env(4),\n    deps = [\n        \":lagrange_interpolation\",\n        \":mixed_radix_evaluation_domain\",\n        \":naive_batch_fft\",\n        \":radix2_evaluation_domain\",\n        \":univariate_polynomial\",\n        \"//tachyon/base:optional\",\n        \"//tachyon/base/buffer:vector_buffer\",\n        \"//tachyon/base/containers:contains\",\n        \"//tachyon/base/containers:cxx20_erase\",\n        \"//tachyon/base/functional:function_ref\",\n        \"//tachyon/math/elliptic_curves/bls12/bls12_381:fr\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:fr\",\n        \"//tachyon/math/elliptic_curves/bn/bn384_small_two_adicity:fq\",\n        \"//tachyon/math/finite_fields/baby_bear\",\n        \"//tachyon/math/finite_fields/koala_bear\",\n        \"//tachyon/math/finite_fields/test:finite_field_test\",\n        \"//tachyon/math/finite_fields/test:gf7\",\n        \"@com_google_absl//absl/hash:hash_testing\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"univariate_gpu_unittests\",\n    srcs = if_gpu_is_configured([\"univariate_evaluation_domain_gpu_unittest.cc\"]),\n    deps = [\n        \":mixed_radix_evaluation_domain\",\n        \":radix2_evaluation_domain\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:fr\",\n        \"//tachyon/math/finite_fields/test:finite_field_test\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/math/polynomials/univariate/evaluations_utils.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_MATH_POLYNOMIALS_UNIVARIATE_EVALUATIONS_UTILS_H_\n#define TACHYON_MATH_POLYNOMIALS_UNIVARIATE_EVALUATIONS_UTILS_H_\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/bits.h\"\n#include \"tachyon/base/openmp_util.h\"\n#include \"tachyon/base/profiler.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename F>\nstd::vector<F> SwapBitRevElements(const std::vector<F>& vals) {\n  size_t n = vals.size();\n  std::vector<F> ret = vals;\n  SwapBitRevElementsInPlace(ret, n, base::bits::CheckedLog2(n));\n  return ret;\n}\n\n// Swaps the elements at each index with the element at its bit-reversed index\n// in place.\n// ex. For a vector of size 8, the element at index 1(001) and the\n//     element at index 4(100) are swapped.\ntemplate <typename Container>\nvoid SwapBitRevElementsInPlace(Container& container, size_t size,\n                               uint32_t log_len) {\n  TRACE_EVENT(\"Utils\", \"SwapBitRevElementsInPlace\");\n  if (size <= 1) return;\n  OMP_PARALLEL_FOR(size_t idx = 1; idx < size; ++idx) {\n    size_t ridx = base::bits::ReverseBitsLen(idx, log_len);\n    if (idx < ridx) {\n      std::swap(container.at(idx), container.at(ridx));\n    }\n  }\n}\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_POLYNOMIALS_UNIVARIATE_EVALUATIONS_UTILS_H_\n"
  },
  {
    "path": "tachyon/math/polynomials/univariate/fft_algorithm.cc",
    "content": "#include \"tachyon/math/polynomials/univariate/fft_algorithm.h\"\n\n#include \"tachyon/base/logging.h\"\n\nnamespace tachyon::math {\n\nstd::string FFTAlgorithmToString(FFTAlgorithm algorithm) {\n  switch (algorithm) {\n    case FFTAlgorithm::kRadix2:\n      return \"Radix2\";\n    case FFTAlgorithm::kMixedRadix:\n      return \"MixedRadix\";\n  }\n  NOTREACHED();\n  return \"\";\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/polynomials/univariate/fft_algorithm.h",
    "content": "#ifndef TACHYON_MATH_POLYNOMIALS_UNIVARIATE_FFT_ALGORITHM_H_\n#define TACHYON_MATH_POLYNOMIALS_UNIVARIATE_FFT_ALGORITHM_H_\n\n#include <string>\n\n#include \"tachyon/export.h\"\n\nnamespace tachyon::math {\n\nenum class FFTAlgorithm {\n  kRadix2,\n  kMixedRadix,\n};\n\nTACHYON_EXPORT std::string FFTAlgorithmToString(FFTAlgorithm algorithm);\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_POLYNOMIALS_UNIVARIATE_FFT_ALGORITHM_H_\n"
  },
  {
    "path": "tachyon/math/polynomials/univariate/icicle/BUILD.bazel",
    "content": "load(\"@icicle//:build_defs.bzl\", \"BABY_BEAR\", \"BLS12_381\", \"BN254\", \"icicle_defines\")\nload(\"@local_config_cuda//cuda:build_defs.bzl\", \"if_cuda\")\nload(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cuda_library\")\n\ntachyon_cc_library(\n    name = \"fft_algorithm_conversion\",\n    srcs = if_cuda([\"fft_algorithm_conversion.cc\"]),\n    hdrs = [\"fft_algorithm_conversion.h\"],\n    visibility = [\"//visibility:public\"],\n    deps = [\n        \"//tachyon:export\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/math/polynomials/univariate:fft_algorithm\",\n        \"@icicle//:hdrs\",\n    ],\n)\n\ntachyon_cuda_library(\n    name = \"icicle_ntt_baby_bear\",\n    srcs = if_cuda([\"icicle_ntt_baby_bear.cc\"]),\n    hdrs = [\"icicle_ntt_baby_bear.h\"],\n    local_defines = icicle_defines(BABY_BEAR),\n    deps = [\n        \":icicle_ntt\",\n        \"//tachyon/base:bit_cast\",\n        \"//tachyon/device/gpu:gpu_enums\",\n        \"//tachyon/device/gpu:gpu_logging\",\n        \"@icicle//:ntt_baby_bear\",\n    ] + if_cuda([\n        \"@local_config_cuda//cuda:cudart_static\",\n    ]),\n)\n\ntachyon_cuda_library(\n    name = \"icicle_ntt_bls12_381\",\n    srcs = if_cuda([\"icicle_ntt_bls12_381.cc\"]),\n    hdrs = [\"icicle_ntt_bls12_381.h\"],\n    local_defines = icicle_defines(BLS12_381),\n    deps = [\n        \":icicle_ntt\",\n        \"//tachyon/base:bit_cast\",\n        \"//tachyon/device/gpu:gpu_enums\",\n        \"//tachyon/device/gpu:gpu_logging\",\n        \"@icicle//:ntt_bls12_381\",\n    ] + if_cuda([\n        \"@local_config_cuda//cuda:cudart_static\",\n    ]),\n)\n\ntachyon_cuda_library(\n    name = \"icicle_ntt_bn254\",\n    srcs = if_cuda([\"icicle_ntt_bn254.cc\"]),\n    hdrs = [\"icicle_ntt_bn254.h\"],\n    local_defines = icicle_defines(BN254),\n    deps = [\n        \":icicle_ntt\",\n        \"//tachyon/base:bit_cast\",\n        \"//tachyon/device/gpu:gpu_enums\",\n        \"//tachyon/device/gpu:gpu_logging\",\n        \"@icicle//:ntt_bn254\",\n    ] + if_cuda([\n        \"@local_config_cuda//cuda:cudart_static\",\n    ]),\n)\n\ntachyon_cc_library(\n    name = \"icicle_ntt\",\n    hdrs = [\"icicle_ntt.h\"],\n    deps = [\n        \"//tachyon/base:logging\",\n        \"//tachyon/device/gpu:gpu_device_functions\",\n        \"//tachyon/math/elliptic_curves/bls12/bls12_381:fr\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:fr\",\n        \"//tachyon/math/finite_fields/baby_bear\",\n        \"//tachyon/math/polynomials/univariate:univariate_evaluations\",\n        \"//tachyon/math/polynomials/univariate:univariate_polynomial\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"icicle_ntt_holder\",\n    hdrs = [\"icicle_ntt_holder.h\"],\n    visibility = [\"//visibility:public\"],\n    deps = [\n        \":icicle_ntt_baby_bear\",\n        \":icicle_ntt_bls12_381\",\n        \":icicle_ntt_bn254\",\n        \"//tachyon/device/gpu:scoped_mem_pool\",\n        \"//tachyon/device/gpu:scoped_stream\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/math/polynomials/univariate/icicle/fft_algorithm_conversion.cc",
    "content": "#include \"tachyon/math/polynomials/univariate/icicle/fft_algorithm_conversion.h\"\n\n#include \"tachyon/base/logging.h\"\n\nnamespace tachyon::math {\n\n::ntt::NttAlgorithm FFTAlgorithmToIcicleNTTAlgorithm(FFTAlgorithm algorithm) {\n  switch (algorithm) {\n    case FFTAlgorithm::kRadix2:\n      return ::ntt::NttAlgorithm::Radix2;\n    case FFTAlgorithm::kMixedRadix:\n      return ::ntt::NttAlgorithm::MixedRadix;\n  }\n  NOTREACHED();\n  return ::ntt::NttAlgorithm::Auto;\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/polynomials/univariate/icicle/fft_algorithm_conversion.h",
    "content": "#ifndef TACHYON_MATH_POLYNOMIALS_UNIVARIATE_ICICLE_FFT_ALGORITHM_CONVERSION_H_\n#define TACHYON_MATH_POLYNOMIALS_UNIVARIATE_ICICLE_FFT_ALGORITHM_CONVERSION_H_\n\n#include \"third_party/icicle/include/ntt/ntt_algorithm.h\"\n\n#include \"tachyon/export.h\"\n#include \"tachyon/math/polynomials/univariate/fft_algorithm.h\"\n\nnamespace tachyon::math {\n\nTACHYON_EXPORT ::ntt::NttAlgorithm FFTAlgorithmToIcicleNTTAlgorithm(\n    FFTAlgorithm algorithm);\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_POLYNOMIALS_UNIVARIATE_ICICLE_FFT_ALGORITHM_CONVERSION_H_\n"
  },
  {
    "path": "tachyon/math/polynomials/univariate/icicle/icicle_ntt.h",
    "content": "#ifndef TACHYON_MATH_POLYNOMIALS_UNIVARIATE_ICICLE_ICICLE_NTT_H_\n#define TACHYON_MATH_POLYNOMIALS_UNIVARIATE_ICICLE_ICICLE_NTT_H_\n\n#include <memory>\n#include <vector>\n\n#include \"third_party/icicle/include/ntt/ntt.cu.h\"\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/device/gpu/gpu_device_functions.h\"\n#include \"tachyon/export.h\"\n#include \"tachyon/math/elliptic_curves/bls12/bls12_381/fr.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/fr.h\"\n#include \"tachyon/math/finite_fields/baby_bear/baby_bear.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluations.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_polynomial.h\"\n\nnamespace tachyon::math {\n\ntemplate <class F>\nstruct IsIcicleNTTSupportedImpl {\n  constexpr static bool value = false;\n};\n\ntemplate <>\nstruct IsIcicleNTTSupportedImpl<BabyBear> {\n  constexpr static bool value = true;\n};\n\ntemplate <>\nstruct IsIcicleNTTSupportedImpl<bls12_381::Fr> {\n  constexpr static bool value = true;\n};\n\ntemplate <>\nstruct IsIcicleNTTSupportedImpl<bn254::Fr> {\n  constexpr static bool value = true;\n};\n\ntemplate <typename F>\nconstexpr bool IsIcicleNTTSupported = IsIcicleNTTSupportedImpl<F>::value;\n\nstruct TACHYON_EXPORT IcicleNTTOptions {\n  bool fast_twiddles_mode = true;\n  int batch_size = 1;\n  bool columns_batch = false;\n  ::ntt::Ordering ordering = ::ntt::Ordering::kNN;\n  bool are_inputs_on_device = false;\n  bool are_outputs_on_device = false;\n  bool is_async = false;\n};\n\ntemplate <typename F>\nclass IcicleNTT {\n public:\n  using BigInt = typename F::BigIntTy;\n\n  IcicleNTT(gpuMemPool_t mem_pool, gpuStream_t stream)\n      : mem_pool_(mem_pool), stream_(stream) {\n    VLOG(1) << \"IcicleNTT is created\";\n  }\n  IcicleNTT(const IcicleNTT& other) = delete;\n  IcicleNTT& operator=(const IcicleNTT& other) = delete;\n  ~IcicleNTT() { CHECK(Release()); }\n\n  [[nodiscard]] bool Init(const F& group_gen, const IcicleNTTOptions& options =\n                                                  IcicleNTTOptions()) {\n    NOTIMPLEMENTED();\n    return true;\n  }\n\n  template <size_t MaxDegree>\n  [[nodiscard]] bool FFT(::ntt::NttAlgorithm algorithm, const BigInt& coset,\n                         UnivariateEvaluations<F, MaxDegree>& evals) const {\n    const std::vector<F>& evaluations = evals.evaluations();\n    return Run(algorithm, coset, const_cast<F*>(evaluations.data()),\n               evaluations.size(), ::ntt::NTTDir::kForward);\n  }\n\n  template <size_t MaxDegree>\n  [[nodiscard]] bool IFFT(::ntt::NttAlgorithm algorithm, const BigInt& coset,\n                          UnivariateDensePolynomial<F, MaxDegree>& poly) const {\n    const std::vector<F>& coefficients = poly.coefficients().coefficients();\n    return Run(algorithm, coset, const_cast<F*>(coefficients.data()),\n               coefficients.size(), ::ntt::NTTDir::kInverse);\n  }\n\n private:\n  [[nodiscard]] bool Run(::ntt::NttAlgorithm algorithm, const BigInt& coset,\n                         F* inout, int size, ::ntt::NTTDir dir) const {\n    NOTIMPLEMENTED();\n    return false;\n  }\n  [[nodiscard]] bool Release() {\n    NOTIMPLEMENTED();\n    return true;\n  }\n\n  gpuMemPool_t mem_pool_ = nullptr;\n  gpuStream_t stream_ = nullptr;\n  mutable std::unique_ptr<::ntt::NTTConfig<F>> config_;\n};\n\ntemplate <>\nTACHYON_EXPORT bool IcicleNTT<BabyBear>::Init(const BabyBear& group_gen,\n                                              const IcicleNTTOptions& options);\n\ntemplate <>\nTACHYON_EXPORT bool IcicleNTT<BabyBear>::Run(::ntt::NttAlgorithm algorithm,\n                                             const BigInt& coset,\n                                             BabyBear* inout, int size,\n                                             ::ntt::NTTDir dir) const;\n\ntemplate <>\nTACHYON_EXPORT bool IcicleNTT<BabyBear>::Release();\n\ntemplate <>\nTACHYON_EXPORT bool IcicleNTT<bls12_381::Fr>::Init(\n    const bls12_381::Fr& group_gen, const IcicleNTTOptions& options);\n\ntemplate <>\nTACHYON_EXPORT bool IcicleNTT<bls12_381::Fr>::Run(::ntt::NttAlgorithm algorithm,\n                                                  const BigInt& coset,\n                                                  bls12_381::Fr* inout,\n                                                  int size,\n                                                  ::ntt::NTTDir dir) const;\n\ntemplate <>\nTACHYON_EXPORT bool IcicleNTT<bls12_381::Fr>::Release();\n\ntemplate <>\nTACHYON_EXPORT bool IcicleNTT<bn254::Fr>::Init(const bn254::Fr& group_gen,\n                                               const IcicleNTTOptions& options);\n\ntemplate <>\nTACHYON_EXPORT bool IcicleNTT<bn254::Fr>::Run(::ntt::NttAlgorithm algorithm,\n                                              const BigInt& coset,\n                                              bn254::Fr* inout, int size,\n                                              ::ntt::NTTDir dir) const;\n\ntemplate <>\nTACHYON_EXPORT bool IcicleNTT<bn254::Fr>::Release();\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_POLYNOMIALS_UNIVARIATE_ICICLE_ICICLE_NTT_H_\n"
  },
  {
    "path": "tachyon/math/polynomials/univariate/icicle/icicle_ntt_baby_bear.cc",
    "content": "#include \"tachyon/math/polynomials/univariate/icicle/icicle_ntt_baby_bear.h\"\n\n#include \"third_party/icicle/include/fields/id.h\"\n#include \"third_party/icicle/src/ntt/ntt.cu.cc\"  // NOLINT(build/include)\n\n#include \"tachyon/base/bit_cast.h\"\n#include \"tachyon/device/gpu/gpu_enums.h\"\n#include \"tachyon/device/gpu/gpu_logging.h\"\n#include \"tachyon/math/polynomials/univariate/icicle/icicle_ntt.h\"\n\ngpuError_t tachyon_babybear_initialize_domain_cuda(\n    const ::babybear::scalar_t& primitive_root,\n    ::device_context::DeviceContext& ctx, bool fast_twiddles_mode) {\n  return ::ntt::init_domain(primitive_root, ctx, fast_twiddles_mode);\n}\n\ngpuError_t tachyon_babybear_ntt_cuda(\n    const ::babybear::scalar_t* input, int size, ::ntt::NTTDir dir,\n    ::ntt::NTTConfig<::babybear::scalar_t>& config,\n    ::babybear::scalar_t* output) {\n  return ::ntt::ntt(input, size, dir, config, output);\n}\n\ngpuError_t tachyon_babybear_release_domain_cuda(\n    ::device_context::DeviceContext& ctx) {\n  return ::ntt::release_domain<::babybear::scalar_t>(ctx);\n}\n\nnamespace tachyon::math {\n\ntemplate <>\nbool IcicleNTT<BabyBear>::Init(const BabyBear& group_gen,\n                               const IcicleNTTOptions& options) {\n#if FIELD_ID != BABY_BEAR\n#error Only BABY_BEAR is supported\n#endif\n  ::device_context::DeviceContext ctx{stream_, /*device_id=*/0, mem_pool_};\n  math::BigInt<1> group_gen_big_int = group_gen.ToBigInt();\n  // TODO(chokobole): We must handle these issues with domain initialization:\n  // 1. It gets too slow when the domain size is 1, 2, 4, or small in general.\n  //    See \"vendors/circom/prover_main.cc\".\n  // 2. |fast_twiddles_mode| consumes a lot of memory, so we need to disable it\n  //    if the ram of the GPU is not enough. See\n  //    https://github.com/ingonyama-zk/icicle/blob/4fef542/icicle/include/ntt/ntt.cuh#L26-L40.\n  gpuError_t error = tachyon_babybear_initialize_domain_cuda(\n      reinterpret_cast<const ::babybear::scalar_t&>(group_gen_big_int), ctx,\n      options.fast_twiddles_mode);\n  if (error != gpuSuccess) {\n    GPU_LOG(ERROR, error) << \"Failed tachyon_babybear_initialize_domain_cuda()\";\n    return false;\n  }\n  VLOG(1) << \"IcicleNTT is initialized\";\n\n  auto one = ::babybear::scalar_t::one();\n  config_.reset(new ::ntt::NTTConfig<BabyBear>{\n      ctx,\n      // TODO(chokobole): Change it to |base::bit_cast| again if the\n      // |::babybear::scalar_t| becomes trivially copyable.\n      *reinterpret_cast<BabyBear*>(&one),\n      options.batch_size,\n      options.columns_batch,\n      options.ordering,\n      options.are_inputs_on_device,\n      options.are_outputs_on_device,\n      options.is_async,\n      /*ntt_algorithm=*/::ntt::NttAlgorithm::Auto,\n  });\n  return true;\n}\n\ntemplate <>\nbool IcicleNTT<BabyBear>::Run(::ntt::NttAlgorithm algorithm,\n                              const BigInt& coset, BabyBear* inout, int size,\n                              ::ntt::NTTDir dir) const {\n#if FIELD_ID != BABY_BEAR\n#error Only BABY_BEAR is supported\n#endif\n\n  // NOTE(chokobole): Manual copy is needed even though\n  // |sizeof(::babybear::scalar_t)| and |sizeof(BabyBear)| are same. This\n  // is because their alignments are different. See\n  // https://github.com/ingonyama-zk/icicle/blob/4fef542/icicle/include/fields/storage.cuh.\n  uint32_t coset_gen = static_cast<uint32_t>(coset[0]);\n  ::ntt::NTTConfig<::babybear::scalar_t> config{\n      config_->ctx,\n      // TODO(chokobole): Change it to |base::bit_cast| again if the\n      // |::babybear::scalar_t| becomes trivially copyable.\n      *reinterpret_cast<::babybear::scalar_t*>(&coset_gen),\n      config_->batch_size,\n      config_->columns_batch,\n      config_->ordering,\n      config_->are_inputs_on_device,\n      config_->are_outputs_on_device,\n      config_->is_async,\n      algorithm,\n  };\n\n  gpuError_t error = tachyon_babybear_ntt_cuda(\n      reinterpret_cast<const ::babybear::scalar_t*>(inout), size, dir, config,\n      reinterpret_cast<::babybear::scalar_t*>(inout));\n  if (error != gpuSuccess) {\n    GPU_LOG(ERROR, error) << \"Failed tachyon_babybear_ntt_cuda()\";\n    return false;\n  }\n  return true;\n}\n\ntemplate <>\nbool IcicleNTT<BabyBear>::Release() {\n#if FIELD_ID != BABY_BEAR\n#error Only BABY_BEAR is supported\n#endif\n\n  ::device_context::DeviceContext ctx{stream_, /*device_id=*/0, mem_pool_};\n  gpuError_t error = tachyon_babybear_release_domain_cuda(ctx);\n  if (error != gpuSuccess) {\n    GPU_LOG(ERROR, error) << \"Failed tachyon_babybear_release_domain_cuda()\";\n    return false;\n  }\n  return true;\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/polynomials/univariate/icicle/icicle_ntt_baby_bear.h",
    "content": "#ifndef TACHYON_MATH_POLYNOMIALS_UNIVARIATE_ICICLE_ICICLE_NTT_BABY_BEAR_H_\n#define TACHYON_MATH_POLYNOMIALS_UNIVARIATE_ICICLE_ICICLE_NTT_BABY_BEAR_H_\n\n#include <stdint.h>\n\n#include \"third_party/icicle/include/fields/stark_fields/babybear.cu.h\"\n#include \"third_party/icicle/include/ntt/ntt.cu.h\"\n\n#include \"tachyon/device/gpu/gpu_device_functions.h\"\n\nextern \"C\" gpuError_t tachyon_babybear_initialize_domain_cuda(\n    const ::babybear::scalar_t& primitive_root,\n    ::device_context::DeviceContext& ctx, bool fast_twiddles_mode);\n\nextern \"C\" gpuError_t tachyon_babybear_ntt_cuda(\n    const ::babybear::scalar_t* input, int size, ::ntt::NTTDir dir,\n    ::ntt::NTTConfig<::babybear::scalar_t>& config,\n    ::babybear::scalar_t* output);\n\nextern \"C\" gpuError_t tachyon_babybear_release_domain_cuda(\n    ::device_context::DeviceContext& ctx);\n\n#endif  // TACHYON_MATH_POLYNOMIALS_UNIVARIATE_ICICLE_ICICLE_NTT_BABY_BEAR_H_\n"
  },
  {
    "path": "tachyon/math/polynomials/univariate/icicle/icicle_ntt_bls12_381.cc",
    "content": "#include \"tachyon/math/polynomials/univariate/icicle/icicle_ntt_bls12_381.h\"\n\n#include \"third_party/icicle/include/fields/id.h\"\n#include \"third_party/icicle/src/ntt/ntt.cu.cc\"  // NOLINT(build/include)\n\n#include \"tachyon/base/bit_cast.h\"\n#include \"tachyon/device/gpu/gpu_enums.h\"\n#include \"tachyon/device/gpu/gpu_logging.h\"\n#include \"tachyon/math/polynomials/univariate/icicle/icicle_ntt.h\"\n\ngpuError_t tachyon_bls12_381_initialize_domain_cuda(\n    const ::bls12_381::scalar_t& primitive_root,\n    ::device_context::DeviceContext& ctx, bool fast_twiddles_mode) {\n  return ::ntt::init_domain(primitive_root, ctx, fast_twiddles_mode);\n}\n\ngpuError_t tachyon_bls12_381_ntt_cuda(\n    const ::bls12_381::scalar_t* input, int size, ::ntt::NTTDir dir,\n    ::ntt::NTTConfig<::bls12_381::scalar_t>& config,\n    ::bls12_381::scalar_t* output) {\n  return ::ntt::ntt(input, size, dir, config, output);\n}\n\ngpuError_t tachyon_bls12_381_release_domain_cuda(\n    ::device_context::DeviceContext& ctx) {\n  return ::ntt::release_domain<::bls12_381::scalar_t>(ctx);\n}\n\nnamespace tachyon::math {\n\ntemplate <>\nbool IcicleNTT<bls12_381::Fr>::Init(const bls12_381::Fr& group_gen,\n                                    const IcicleNTTOptions& options) {\n#if FIELD_ID != BLS12_381\n#error Only BLS12_381 is supported\n#endif\n  ::device_context::DeviceContext ctx{stream_, /*device_id=*/0, mem_pool_};\n  math::BigInt<4> group_gen_big_int = group_gen.ToBigInt();\n  // TODO(chokobole): We must handle these issues with domain initialization:\n  // 1. It gets too slow when the domain size is 1, 2, 4, or small in general.\n  //    See \"vendors/circom/prover_main.cc\".\n  // 2. |fast_twiddles_mode| consumes a lot of memory, so we need to disable it\n  //    if the ram of the GPU is not enough. See\n  //    https://github.com/ingonyama-zk/icicle/blob/4fef542/icicle/include/ntt/ntt.cuh#L26-L40.\n  gpuError_t error = tachyon_bls12_381_initialize_domain_cuda(\n      reinterpret_cast<const ::bls12_381::scalar_t&>(group_gen_big_int), ctx,\n      options.fast_twiddles_mode);\n  if (error != gpuSuccess) {\n    GPU_LOG(ERROR, error)\n        << \"Failed tachyon_bls12_381_initialize_domain_cuda()\";\n    return false;\n  }\n  VLOG(1) << \"IcicleNTT is initialized\";\n\n  auto one = ::bls12_381::scalar_t::one();\n  config_.reset(new ::ntt::NTTConfig<bls12_381::Fr>{\n      ctx,\n      // TODO(chokobole): Change it to |base::bit_cast| again if the\n      // |::bls12_381::scalar_t| becomes trivially copyable.\n      *reinterpret_cast<bls12_381::Fr*>(&one),\n      options.batch_size,\n      options.columns_batch,\n      options.ordering,\n      options.are_inputs_on_device,\n      options.are_outputs_on_device,\n      options.is_async,\n      /*ntt_algorithm=*/::ntt::NttAlgorithm::Auto,\n  });\n  return true;\n}\n\ntemplate <>\nbool IcicleNTT<bls12_381::Fr>::Run(::ntt::NttAlgorithm algorithm,\n                                   const BigInt& coset, bls12_381::Fr* inout,\n                                   int size, ::ntt::NTTDir dir) const {\n#if FIELD_ID != BLS12_381\n#error Only BLS12_381 is supported\n#endif\n\n  // NOTE(chokobole): Manual copy is needed even though\n  // |sizeof(::bls12_381::scalar_t)| and |sizeof(bls12_381::Fr)| are same. This\n  // is because their alignments are different. See\n  // https://github.com/ingonyama-zk/icicle/blob/4fef542/icicle/include/fields/storage.cuh.\n  ::ntt::NTTConfig<::bls12_381::scalar_t> config{\n      config_->ctx,\n      // TODO(chokobole): Change it to |base::bit_cast| again if the\n      // |::bls12_381::scalar_t| becomes trivially copyable.\n      *reinterpret_cast<::bls12_381::scalar_t*>(const_cast<BigInt*>(&coset)),\n      config_->batch_size,\n      config_->columns_batch,\n      config_->ordering,\n      config_->are_inputs_on_device,\n      config_->are_outputs_on_device,\n      config_->is_async,\n      algorithm,\n  };\n\n  gpuError_t error = tachyon_bls12_381_ntt_cuda(\n      reinterpret_cast<const ::bls12_381::scalar_t*>(inout), size, dir, config,\n      reinterpret_cast<::bls12_381::scalar_t*>(inout));\n  if (error != gpuSuccess) {\n    GPU_LOG(ERROR, error) << \"Failed tachyon_bls12_381_ntt_cuda()\";\n    return false;\n  }\n  return true;\n}\n\ntemplate <>\nbool IcicleNTT<bls12_381::Fr>::Release() {\n#if FIELD_ID != BLS12_381\n#error Only BLS12_381 is supported\n#endif\n\n  ::device_context::DeviceContext ctx{stream_, /*device_id=*/0, mem_pool_};\n  gpuError_t error = tachyon_bls12_381_release_domain_cuda(ctx);\n  if (error != gpuSuccess) {\n    GPU_LOG(ERROR, error) << \"Failed tachyon_bls12_381_release_domain_cuda()\";\n    return false;\n  }\n  return true;\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/polynomials/univariate/icicle/icicle_ntt_bls12_381.h",
    "content": "#ifndef TACHYON_MATH_POLYNOMIALS_UNIVARIATE_ICICLE_ICICLE_NTT_BLS12_381_H_\n#define TACHYON_MATH_POLYNOMIALS_UNIVARIATE_ICICLE_ICICLE_NTT_BLS12_381_H_\n\n#include <stdint.h>\n\n#include \"third_party/icicle/include/curves/params/bls12_381.cu.h\"\n#include \"third_party/icicle/include/ntt/ntt.cu.h\"\n\n#include \"tachyon/device/gpu/gpu_device_functions.h\"\n\nextern \"C\" gpuError_t tachyon_bls12_381_initialize_domain_cuda(\n    const ::bls12_381::scalar_t& primitive_root,\n    ::device_context::DeviceContext& ctx, bool fast_twiddles_mode);\n\nextern \"C\" gpuError_t tachyon_bls12_381_ntt_cuda(\n    const ::bls12_381::scalar_t* input, int size, ::ntt::NTTDir dir,\n    ::ntt::NTTConfig<::bls12_381::scalar_t>& config,\n    ::bls12_381::scalar_t* output);\n\nextern \"C\" gpuError_t tachyon_bls12_381_release_domain_cuda(\n    ::device_context::DeviceContext& ctx);\n\n#endif  // TACHYON_MATH_POLYNOMIALS_UNIVARIATE_ICICLE_ICICLE_NTT_BLS12_381_H_\n"
  },
  {
    "path": "tachyon/math/polynomials/univariate/icicle/icicle_ntt_bn254.cc",
    "content": "#include \"tachyon/math/polynomials/univariate/icicle/icicle_ntt_bn254.h\"\n\n#include \"third_party/icicle/include/fields/id.h\"\n#include \"third_party/icicle/src/ntt/ntt.cu.cc\"  // NOLINT(build/include)\n\n#include \"tachyon/base/bit_cast.h\"\n#include \"tachyon/device/gpu/gpu_enums.h\"\n#include \"tachyon/device/gpu/gpu_logging.h\"\n#include \"tachyon/math/polynomials/univariate/icicle/icicle_ntt.h\"\n\ngpuError_t tachyon_bn254_initialize_domain_cuda(\n    const ::bn254::scalar_t& primitive_root,\n    ::device_context::DeviceContext& ctx, bool fast_twiddles_mode) {\n  return ::ntt::init_domain(primitive_root, ctx, fast_twiddles_mode);\n}\n\ngpuError_t tachyon_bn254_ntt_cuda(const ::bn254::scalar_t* input, int size,\n                                  ::ntt::NTTDir dir,\n                                  ::ntt::NTTConfig<::bn254::scalar_t>& config,\n                                  ::bn254::scalar_t* output) {\n  return ::ntt::ntt(input, size, dir, config, output);\n}\n\ngpuError_t tachyon_bn254_release_domain_cuda(\n    ::device_context::DeviceContext& ctx) {\n  return ::ntt::release_domain<::bn254::scalar_t>(ctx);\n}\n\nnamespace tachyon::math {\n\ntemplate <>\nbool IcicleNTT<bn254::Fr>::Init(const bn254::Fr& group_gen,\n                                const IcicleNTTOptions& options) {\n#if FIELD_ID != BN254\n#error Only BN254 is supported\n#endif\n  ::device_context::DeviceContext ctx{stream_, /*device_id=*/0, mem_pool_};\n  math::BigInt<4> group_gen_big_int = group_gen.ToBigInt();\n  // TODO(chokobole): We must handle these issues with domain initialization:\n  // 1. It gets too slow when the domain size is 1, 2, 4, or small in general.\n  //    See \"vendors/circom/prover_main.cc\".\n  // 2. |fast_twiddles_mode| consumes a lot of memory, so we need to disable it\n  //    if the ram of the GPU is not enough. See\n  //    https://github.com/ingonyama-zk/icicle/blob/4fef542/icicle/include/ntt/ntt.cuh#L26-L40.\n  gpuError_t error = tachyon_bn254_initialize_domain_cuda(\n      reinterpret_cast<const ::bn254::scalar_t&>(group_gen_big_int), ctx,\n      options.fast_twiddles_mode);\n  if (error != gpuSuccess) {\n    GPU_LOG(ERROR, error) << \"Failed tachyon_bn254_initialize_domain_cuda()\";\n    return false;\n  }\n  VLOG(1) << \"IcicleNTT is initialized\";\n\n  auto one = ::bn254::scalar_t::one();\n  config_.reset(new ::ntt::NTTConfig<bn254::Fr>{\n      ctx,\n      // TODO(chokobole): Change it to |base::bit_cast| again if the\n      // |::bn254::scalar_t| becomes trivially copyable.\n      *reinterpret_cast<bn254::Fr*>(&one),\n      options.batch_size,\n      options.columns_batch,\n      options.ordering,\n      options.are_inputs_on_device,\n      options.are_outputs_on_device,\n      options.is_async,\n      /*ntt_algorithm=*/::ntt::NttAlgorithm::Auto,\n  });\n  return true;\n}\n\ntemplate <>\nbool IcicleNTT<bn254::Fr>::Run(::ntt::NttAlgorithm algorithm,\n                               const BigInt& coset, bn254::Fr* inout, int size,\n                               ::ntt::NTTDir dir) const {\n#if FIELD_ID != BN254\n#error Only BN254 is supported\n#endif\n\n  // NOTE(chokobole): Manual copy is needed even though\n  // |sizeof(::bn254::scalar_t)| and |sizeof(bn254::Fr)| are same. This is\n  // because their alignments are different.\n  // See\n  // https://github.com/ingonyama-zk/icicle/blob/4fef542/icicle/include/fields/storage.cuh.\n  ::ntt::NTTConfig<::bn254::scalar_t> config{\n      config_->ctx,\n      // TODO(chokobole): Change it to |base::bit_cast| again if the\n      // |::bn254::scalar_t| becomes trivially copyable.\n      *reinterpret_cast<::bn254::scalar_t*>(const_cast<BigInt*>(&coset)),\n      config_->batch_size,\n      config_->columns_batch,\n      config_->ordering,\n      config_->are_inputs_on_device,\n      config_->are_outputs_on_device,\n      config_->is_async,\n      algorithm,\n  };\n\n  gpuError_t error = tachyon_bn254_ntt_cuda(\n      reinterpret_cast<const ::bn254::scalar_t*>(inout), size, dir, config,\n      reinterpret_cast<::bn254::scalar_t*>(inout));\n  if (error != gpuSuccess) {\n    GPU_LOG(ERROR, error) << \"Failed tachyon_bn254_ntt_cuda()\";\n    return false;\n  }\n  return true;\n}\n\ntemplate <>\nbool IcicleNTT<bn254::Fr>::Release() {\n#if FIELD_ID != BN254\n#error Only BN254 is supported\n#endif\n\n  ::device_context::DeviceContext ctx{stream_, /*device_id=*/0, mem_pool_};\n  gpuError_t error = tachyon_bn254_release_domain_cuda(ctx);\n  if (error != gpuSuccess) {\n    GPU_LOG(ERROR, error) << \"Failed tachyon_bn254_release_domain_cuda()\";\n    return false;\n  }\n  return true;\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/polynomials/univariate/icicle/icicle_ntt_bn254.h",
    "content": "#ifndef TACHYON_MATH_POLYNOMIALS_UNIVARIATE_ICICLE_ICICLE_NTT_BN254_H_\n#define TACHYON_MATH_POLYNOMIALS_UNIVARIATE_ICICLE_ICICLE_NTT_BN254_H_\n\n#include <stdint.h>\n\n#include \"third_party/icicle/include/curves/params/bn254.cu.h\"\n#include \"third_party/icicle/include/ntt/ntt.cu.h\"\n\n#include \"tachyon/device/gpu/gpu_device_functions.h\"\n\nextern \"C\" gpuError_t tachyon_bn254_initialize_domain_cuda(\n    const ::bn254::scalar_t& primitive_root,\n    ::device_context::DeviceContext& ctx, bool fast_twiddles_mode);\n\nextern \"C\" gpuError_t tachyon_bn254_ntt_cuda(\n    const ::bn254::scalar_t* input, int size, ::ntt::NTTDir dir,\n    ::ntt::NTTConfig<::bn254::scalar_t>& config, ::bn254::scalar_t* output);\n\nextern \"C\" gpuError_t tachyon_bn254_release_domain_cuda(\n    ::device_context::DeviceContext& ctx);\n\n#endif  // TACHYON_MATH_POLYNOMIALS_UNIVARIATE_ICICLE_ICICLE_NTT_BN254_H_\n"
  },
  {
    "path": "tachyon/math/polynomials/univariate/icicle/icicle_ntt_holder.h",
    "content": "#ifndef TACHYON_MATH_POLYNOMIALS_UNIVARIATE_ICICLE_ICICLE_NTT_HOLDER_H_\n#define TACHYON_MATH_POLYNOMIALS_UNIVARIATE_ICICLE_ICICLE_NTT_HOLDER_H_\n\n#include <limits>\n#include <memory>\n#include <utility>\n\n#include \"tachyon/device/gpu/scoped_mem_pool.h\"\n#include \"tachyon/device/gpu/scoped_stream.h\"\n#include \"tachyon/math/polynomials/univariate/icicle/icicle_ntt.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename F>\nclass IcicleNTTHolder {\n public:\n  IcicleNTTHolder() = default;\n\n  const IcicleNTT<F>& operator*() const { return *get(); }\n  IcicleNTT<F>& operator*() { return *get(); }\n\n  const IcicleNTT<F>* operator->() const { return get(); }\n  IcicleNTT<F>* operator->() { return get(); }\n\n  operator bool() const { return !!icicle_; }\n\n  const IcicleNTT<F>* get() const { return icicle_.get(); }\n  IcicleNTT<F>* get() { return icicle_.get(); }\n\n  static IcicleNTTHolder Create() {\n    gpuMemPoolProps props = {gpuMemAllocationTypePinned,\n                             gpuMemHandleTypeNone,\n                             {gpuMemLocationTypeDevice, 0}};\n    device::gpu::ScopedMemPool mem_pool = device::gpu::CreateMemPool(&props);\n\n    uint64_t mem_pool_threshold = std::numeric_limits<uint64_t>::max();\n    gpuError_t error = gpuMemPoolSetAttribute(\n        mem_pool.get(), gpuMemPoolAttrReleaseThreshold, &mem_pool_threshold);\n    CHECK_EQ(error, gpuSuccess);\n    device::gpu::ScopedStream stream = device::gpu::CreateStream();\n\n    return {std::move(mem_pool), std::move(stream),\n            std::make_unique<IcicleNTT<F>>(mem_pool.get(), stream.get())};\n  }\n\n  void Release() {\n    icicle_.reset();\n    stream_.reset();\n    mem_pool_.reset();\n  }\n\n private:\n  IcicleNTTHolder(device::gpu::ScopedMemPool mem_pool,\n                  device::gpu::ScopedStream stream,\n                  std::unique_ptr<IcicleNTT<F>> icicle)\n      : mem_pool_(std::move(mem_pool)),\n        stream_(std::move(stream)),\n        icicle_(std::move(icicle)) {}\n\n  device::gpu::ScopedMemPool mem_pool_;\n  device::gpu::ScopedStream stream_;\n  std::unique_ptr<IcicleNTT<F>> icicle_;\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_POLYNOMIALS_UNIVARIATE_ICICLE_ICICLE_NTT_HOLDER_H_\n"
  },
  {
    "path": "tachyon/math/polynomials/univariate/lagrange_interpolation.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_MATH_POLYNOMIALS_UNIVARIATE_LAGRANGE_INTERPOLATION_H_\n#define TACHYON_MATH_POLYNOMIALS_UNIVARIATE_LAGRANGE_INTERPOLATION_H_\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/parallelize.h\"\n#include \"tachyon/base/template_util.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_polynomial.h\"\n\nnamespace tachyon::math {\n\ntemplate <size_t MaxDegree, typename Container>\nbool LagrangeInterpolate(\n    const Container& points, const Container& evals,\n    UnivariateDensePolynomial<base::container_value_t<Container>, MaxDegree>*\n        ret) {\n  using F = base::container_value_t<Container>;\n  using Poly = UnivariateDensePolynomial<F, MaxDegree>;\n  using Coeffs = UnivariateDenseCoefficients<F, MaxDegree>;\n\n  if (points.size() != evals.size()) {\n    LOG(ERROR) << \"points and evals sizes don't match\";\n    return false;\n  }\n\n  if (points.size() == 1) {\n    *ret = Poly(Coeffs({evals[0]}, true));\n    return true;\n  }\n\n  // points = [x₀, x₁, ..., xₙ₋₁]\n  // |denoms[i]| = Dᵢ = 1 / (xᵢ - x₀)(xᵢ - x₁)...(xᵢ - xₙ₋₁)\n  std::vector<F> denoms(points.size(), F::One());\n  base::Parallelize(denoms, [&points](absl::Span<F> chunk, size_t chunk_offset,\n                                      size_t chunk_size) {\n    size_t i = chunk_offset * chunk_size;\n    for (F& denom : chunk) {\n      for (size_t j = 0; j < points.size(); ++j) {\n        if (i == j) continue;\n        denom *= (points[i] - points[j]);\n      }\n      ++i;\n    }\n    CHECK(F::BatchInverseInPlaceSerial(chunk));\n  });\n\n  std::vector<std::vector<F>> coeffs_sums = base::ParallelizeMap(\n      evals, [&points, &denoms](absl::Span<const F> chunk, size_t chunk_offset,\n                                size_t chunk_size) {\n        // See comments in |UnivariateDenseCoefficients::FromRoots()|.\n        // clang-format off\n        // NOTE(chokobole): This computes the polynomial whose roots are {x₀, ..., xᵢ₋₁, xᵢ₊₁, ..., xₙ₋₁}.\n        // Nᵢ(X) = (X - x₀)...(X - xᵢ₋₁)(X - xᵢ₊₁)...(X - xₙ₋₁)\n        //       = cₙ₋₁Xⁿ⁻¹ + cₙ₋₂Xⁿ⁻² + ... + c₁X¹ + c₀X⁰\n        // |coeffs[i]| = cᵢ\n        // clang-format on\n\n        std::vector<F> coeffs(points.size());\n\n        // NOTE(batzor): We initialize |coeffs_sum| serially since it is already\n        // inside a parallel loop.\n        std::vector<F> coeffs_sum(points.size(), F::Zero());\n\n        size_t start = chunk_offset * chunk_size;\n        for (size_t chunk_idx = 0; chunk_idx < chunk.size(); ++chunk_idx) {\n          size_t i = start + chunk_idx;\n          const F& eval = chunk[chunk_idx];\n          const F& denom = denoms[i];\n\n          coeffs[0] = F::One();\n          for (size_t j = 1; j < points.size(); ++j) {\n            coeffs[j] = F::Zero();\n          }\n\n          size_t k_start = 1;\n          for (size_t j = 0; j < points.size(); ++j) {\n            if (i == j) continue;\n            for (size_t k = k_start; k > 0; --k) {\n              coeffs[k] = coeffs[k - 1] - points[j] * coeffs[k];\n            }\n            ++k_start;\n            coeffs[0] *= -points[j];\n          }\n\n          // P(X) = ∑ᵢ(P(Xᵢ) * Dᵢ * Nᵢ(X))\n          //      = ∑ᵢ(P(Xᵢ) * Dᵢ * (cₙ₋₁Xⁿ⁻¹ + cₙ₋₂Xⁿ⁻² + ... + c₁X¹ + c₀X⁰))\n          for (size_t j = 0; j < coeffs.size(); ++j) {\n            coeffs[j] *= eval;\n            coeffs[j] *= denom;\n            coeffs_sum[j] += coeffs[j];\n          }\n        }\n        return coeffs_sum;\n      });\n  for (size_t i = 1; i < coeffs_sums.size(); ++i) {\n    for (size_t j = 0; j < points.size(); ++j) {\n      coeffs_sums[0][j] += coeffs_sums[i][j];\n    }\n  }\n  *ret = Poly(Coeffs(std::move(coeffs_sums[0]), true));\n  return true;\n}\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_POLYNOMIALS_UNIVARIATE_LAGRANGE_INTERPOLATION_H_\n"
  },
  {
    "path": "tachyon/math/polynomials/univariate/lagrange_interpolation_unittest.cc",
    "content": "#include \"tachyon/math/polynomials/univariate/lagrange_interpolation.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/elliptic_curves/bn/bn254/fr.h\"\n\nnamespace tachyon::math {\n\nTEST(LagrangeInterpolationTest, LagrangeInterpolate) {\n  using F = bn254::Fr;\n  F::Init();\n\n#if defined(TACHYON_HAS_OPENMP)\n  constexpr size_t kDegree = 128;\n#else\n  constexpr size_t kDegree = 4;\n#endif\n\n  std::vector<F> points =\n      base::CreateVector(kDegree + 1, [](size_t i) { return F(i); });\n  std::vector<F> evals =\n      base::CreateVector(kDegree + 1, []() { return F::Random(); });\n\n  UnivariateDensePolynomial<F, kDegree> poly;\n  EXPECT_TRUE(LagrangeInterpolate(points, evals, &poly));\n\n  OMP_PARALLEL_FOR(size_t i = 0; i < points.size(); ++i) {\n    EXPECT_EQ(poly.Evaluate(points[i]), evals[i]);\n  }\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/polynomials/univariate/mixed_radix_evaluation_domain.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n// This header contains a |MixedRadixEvaluationDomain| for performing various\n// kinds of polynomial arithmetic on top of fields that are FFT-friendly but do\n// not have high-enough two-adicity to perform the FFT efficiently, i.e. the\n// multiplicative subgroup G generated by |F::Config::kTwoAdicRootOfUnity| is\n// not large enough. |MixedRadixEvaluationDomain| resolves this issue by using a\n// larger subgroup obtained by combining G with another subgroup of size\n// |F::Config::kSmallSubgroupBase|^|F::Config::kSmallSubgroupAdicity|, to obtain\n// a subgroup generated by |F::Config::kLargeSubgroupRootOfUnity|.\n\n#ifndef TACHYON_MATH_POLYNOMIALS_UNIVARIATE_MIXED_RADIX_EVALUATION_DOMAIN_H_\n#define TACHYON_MATH_POLYNOMIALS_UNIVARIATE_MIXED_RADIX_EVALUATION_DOMAIN_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <algorithm>\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"absl/memory/memory.h\"\n\n#include \"tachyon/base/bits.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/numerics/checked_math.h\"\n#include \"tachyon/base/profiler.h\"\n#include \"tachyon/math/base/gmp/gmp_util.h\"\n#include \"tachyon/math/finite_fields/prime_field_base.h\"\n#include \"tachyon/math/polynomials/univariate/evaluations_utils.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluation_domain.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename F>\nconstexpr size_t MaxDegreeForMixedRadixEvaluationDomain() {\n  size_t i = 1;\n  for (size_t i = 0; i <= F::Config::kSmallSubgroupAdicity; ++i) {\n    i *= F::Config::kSmallSubgroupBase;\n  }\n  return i * (size_t{1} << F::Config::kTwoAdicity) - 1;\n}\n\n// Defines a domain over which finite field (I)FFTs can be performed. Works only\n// for fields that have a multiplicative subgroup of size that is a power-of-2\n// and another small subgroup over a different base defined.\ntemplate <typename F,\n          size_t MaxDegree = MaxDegreeForMixedRadixEvaluationDomain<F>()>\nclass MixedRadixEvaluationDomain\n    : public UnivariateEvaluationDomain<F, MaxDegree> {\n public:\n  using Base = UnivariateEvaluationDomain<F, MaxDegree>;\n  using Field = F;\n  using Evals = UnivariateEvaluations<F, MaxDegree>;\n  using DensePoly = UnivariateDensePolynomial<F, MaxDegree>;\n  using SparsePoly = UnivariateSparsePolynomial<F, MaxDegree>;\n\n  constexpr static size_t kMaxDegree = MaxDegree;\n\n  constexpr static std::unique_ptr<MixedRadixEvaluationDomain> Create(\n      size_t num_coeffs) {\n    size_t size = 0;\n    PrimeFieldFactors factors;\n    CHECK(ComputeSizeAndFactors(num_coeffs, &size, &factors));\n    return absl::WrapUnique(\n        new MixedRadixEvaluationDomain(size, factors.two_adicity));\n  }\n\n  constexpr static bool IsValidNumCoeffs(size_t num_coeffs) {\n    if constexpr (!F::Config::kHasLargeSubgroupRootOfUnity) return false;\n    size_t size_unused = 0;\n    PrimeFieldFactors factors_unused;\n    return ComputeSizeAndFactors(num_coeffs, &size_unused, &factors_unused);\n  }\n\n private:\n  using UnivariateEvaluationDomain<F, MaxDegree>::UnivariateEvaluationDomain;\n\n  // UnivariateEvaluationDomain methods\n  FFTAlgorithm GetAlgorithm() const override {\n    return FFTAlgorithm::kMixedRadix;\n  }\n\n  constexpr std::unique_ptr<UnivariateEvaluationDomain<F, MaxDegree>> Clone()\n      const override {\n    return absl::WrapUnique(new MixedRadixEvaluationDomain(*this));\n  }\n\n  CONSTEXPR_IF_NOT_OPENMP void DoFFT(Evals& evals) const override {\n    TRACE_EVENT(\"EvaluationDomain\", \"MixedRadixEvaluationDomain::DoFFT\");\n    if (!this->offset_.IsOne()) {\n      Base::DistributePowers(evals, this->offset_);\n    }\n    evals.evaluations_.resize(this->size_, F::Zero());\n    BestFFT(evals, this->group_gen_);\n  }\n\n  CONSTEXPR_IF_NOT_OPENMP void DoIFFT(DensePoly& poly) const override {\n    TRACE_EVENT(\"EvaluationDomain\", \"MixedRadixEvaluationDomain::DoIFFT\");\n    poly.coefficients_.coefficients_.resize(this->size_, F::Zero());\n    BestFFT(poly, this->group_gen_inv_);\n    if (this->offset_.IsOne()) {\n      // clang-format off\n      OMP_PARALLEL_FOR(F& coeff : poly.coefficients_.coefficients_) {\n        // clang-format on\n        coeff *= this->size_inv_;\n      }\n    } else {\n      Base::DistributePowersAndMulByConst(poly, this->offset_inv_,\n                                          this->size_inv_);\n    }\n    poly.coefficients_.RemoveHighDegreeZeros();\n  }\n\n  constexpr static bool ComputeSizeAndFactors(size_t num_coeffs,\n                                              size_t* size_out,\n                                              PrimeFieldFactors* factors) {\n    TRACE_EVENT(\"EvaluationDomain\", \"ComputeSizeAndFactors\");\n    base::CheckedNumeric<size_t> checked_size = BestMixedDomainSize(num_coeffs);\n    size_t size = checked_size.ValueOrDie();\n    if (size > MaxDegree + 1) return false;\n    *size_out = size;\n    return F::Decompose(size, factors);\n  }\n\n  constexpr static size_t MixedRadixFFTPermute(uint32_t two_adicity,\n                                               uint32_t q_adicity, uint64_t q,\n                                               size_t n, size_t i) {\n    TRACE_EVENT(\"EvaluationDomain\", \"MixedRadixFFTPermute\");\n    // This is the permutation obtained by splitting into 2 groups |two_adicity|\n    // times and then |q| groups |q_adicity| many times. It can be efficiently\n    // described as follows:\n    // clang-format off\n    // i = 2⁰ * b₀ + 2¹ * b₁ + ... + 2ˢ⁻¹ * bₛ₋₁ + 2ˢ * (q⁰ * x₀ + q¹ * x₁ + ... + qᵗ⁻¹ * xₜ₋₁)\n    // where s = |two_adicity| and t = |q_adicity|\n    // clang-format on\n    // We want to return\n    // clang-format off\n    // j = b₀ * (n / 2¹) + b₁ * (n / 2²) + ... + bₛ₋₁ * (n / 2ˢ) + x₀ * (n / (2ˢ * q¹)) + x₁ * (n / (2ˢ * q²)) ... + xₜ₋₁ * (n / (2ˢ * qᵗ))\n    // where s = |two_adicity| and t = |q_adicity|\n    // clang-format on\n    size_t res = 0;\n    size_t shift = n;\n    for (size_t j = 0; j < two_adicity; ++j) {\n      shift /= 2;\n      res += (i % 2) * shift;\n      i /= 2;\n    }\n    for (size_t j = 0; j < q_adicity; ++j) {\n      shift /= q;\n      res += (i % q) * shift;\n      i /= q;\n    }\n    return res;\n  }\n\n  constexpr static uint64_t BestMixedDomainSize(uint64_t min_size) {\n    TRACE_EVENT(\"EvaluationDomain\", \"BestMixedDomainSize\");\n    uint64_t best = UINT64_MAX;\n    for (size_t i = 0; i <= F::Config::kSmallSubgroupAdicity; ++i) {\n      uint64_t r = static_cast<uint64_t>(\n          std::pow(uint64_t{F::Config::kSmallSubgroupBase}, i));\n      uint32_t two_adicity = 0;\n      while (r < min_size) {\n        r *= 2;\n        ++two_adicity;\n      }\n      if (two_adicity <= F::Config::kTwoAdicity) {\n        best = std::min(best, r);\n      }\n    }\n    return best;\n  }\n\n  template <typename PolyOrEvals>\n  void BestFFT(PolyOrEvals& poly_or_evals, const F& omega) const {\n    TRACE_EVENT(\"EvaluationDomain\", \"BestFFT\");\n#if defined(TACHYON_HAS_OPENMP)\n    uint32_t thread_nums = static_cast<uint32_t>(omp_get_max_threads());\n    uint32_t log_thread_nums = base::bits::Log2Floor(thread_nums);\n    if (this->log_size_of_group_ <= log_thread_nums) {\n#endif\n      return SerialFFT(poly_or_evals, omega, this->log_size_of_group_);\n#if defined(TACHYON_HAS_OPENMP)\n    } else {\n      return ParallelFFT(poly_or_evals, omega, this->log_size_of_group_,\n                         log_thread_nums);\n    }\n#endif\n  }\n\n  template <typename PolyOrEvals>\n  static void SerialFFT(PolyOrEvals& a, const F& omega, uint32_t two_adicity) {\n    TRACE_EVENT(\"EvaluationDomain\", \"SerialFFT\");\n    // Conceptually, this FFT first splits into 2 sub-arrays |two_adicity| many\n    // times, and then splits into q sub-arrays |q_adicity| many times.\n\n    size_t n = a.NumElements();\n    uint64_t q = uint64_t{F::Config::kSmallSubgroupBase};\n    uint64_t n_u64 = n;\n\n    uint32_t q_adicity = ComputeAdicity(q, gmp::FromUnsignedInt(n_u64));\n    uint64_t q_part = static_cast<uint64_t>(std::pow(q, q_adicity));\n    uint64_t two_part =\n        static_cast<uint64_t>(std::pow(uint64_t{2}, two_adicity));\n\n    CHECK_EQ(n_u64, q_part * two_part);\n\n    size_t m = 1;\n    if (q_adicity > 0) {\n      // If we're using the other radix, we have to do two things differently\n      // than in the radix 2 case. 1. Applying the index permutation is a bit\n      // more complicated. It isn't an involution (like it is in the radix 2\n      // case) so we need to remember which elements we've moved as we go along\n      // and can't use the trick of just swapping when processing the first\n      // element of a 2-cycle.\n      //\n      // 2. We need to do |q_adicity| many merge passes, each of which is a bit\n      // more complicated than the specialized q=2 case.\n\n      // Applying the permutation\n      std::vector<bool> seen(n, false);\n      for (size_t k = 0; k < n; ++k) {\n        size_t i = k;\n        F& a_i = a.at(i);\n        while (!seen[i]) {\n          size_t dest = MixedRadixFFTPermute(two_adicity, q_adicity, q, n, i);\n          std::swap(a.at(dest), a_i);\n          seen[i] = true;\n          i = dest;\n        }\n      }\n\n      F omega_q = omega.Pow(n / q);\n      std::vector<F> qth_roots(q, F::One());\n      for (size_t i = 1; i < q; ++i) {\n        qth_roots[i] = qth_roots[i - 1] * omega_q;\n      }\n\n      std::vector<F> terms(q - 1, F::Zero());\n\n      // Doing the q_adicity passes.\n      for (size_t i = 0; i < q_adicity; ++i) {\n        F w_m = omega.Pow(n / (q * m));\n        for (size_t k = 0; k < n; k += q * m) {\n          F w_j = F::One();  // ωⱼ is ωₘʲ\n          for (size_t j = 0; j < m; ++j) {\n            const F& base_term = a[k + j];\n            F w_j_i = w_j;\n            for (size_t i = 1; i < q; ++i) {\n              terms[i - 1] = a[k + j + i * m];\n              terms[i - 1] *= w_j_i;\n              w_j_i *= w_j;\n            }\n\n            for (size_t i = 0; i < q; ++i) {\n              a.at(k + j + i * m) = base_term;\n              for (size_t l = 1; l < q; ++l) {\n                F tmp = terms[l - 1] * qth_roots[(i * l) % q];\n                a.at(k + j + i * m) += tmp;\n              }\n            }\n\n            w_j *= w_m;\n          }\n        }\n        m *= q;\n      }\n    } else {\n      // Swapping in place (from Storer's book)\n      SwapBitRevElementsInPlace(a, n, two_adicity);\n    }\n\n    for (size_t i = 0; i < two_adicity; ++i) {\n      // ωₘ is 2ˢ-th root of unity now\n      F w_m = omega.Pow(n / (2 * m));\n      for (size_t k = 0; k < n; k += 2 * m) {\n        F w = F::One();\n        for (size_t j = 0; j < m; ++j) {\n          UnivariateEvaluationDomain<F, MaxDegree>::template ButterflyFnOutIn(\n              a.at(k + j), a.at((k + m) + j), w);\n          w *= w_m;\n        }\n      }\n      m *= 2;\n    }\n  }\n\n#if defined(TACHYON_HAS_OPENMP)\n  template <typename PolyOrEvals>\n  static void ParallelFFT(PolyOrEvals& a, const F& omega, uint32_t two_adicity,\n                          uint32_t log_num_threads) {\n    TRACE_EVENT(\"EvaluationDomain\", \"ParallelFFT\");\n    CHECK_GE(two_adicity, log_num_threads);\n    // For documentation purposes, comments explain things\n    // as though |a| is a polynomial that we are trying to evaluate.\n\n    // Partition |a| equally into the number of threads.\n    // each partition is then of size m / num_threads.\n    size_t m = a.NumElements();\n    size_t num_threads = size_t{1} << (log_num_threads);\n    size_t num_cosets = num_threads;\n    CHECK_EQ(m % num_threads, size_t{0});\n    size_t coset_size = m / num_threads;\n\n    // We compute the FFT non-mutatively first in |tmp| first,\n    // and then shuffle it back into |a|.\n    // The evaluations are going to be arranged in cosets, each of size |a| /\n    // |num_threads|. so the first coset is (1, g^{|num_cosets|}, g^{2 *\n    // |num_cosets|}, etc.) the second coset is (g, g^{1 + |num_cosets|}, g^{1 +\n    // 2 * |num_cosets|}, etc.) These are cosets with generator\n    // g^{|num_cosets|}, and varying shifts.\n    std::vector<PolyOrEvals> tmp(num_cosets, PolyOrEvals::Zero(coset_size - 1));\n    F new_omega = omega.Pow(num_cosets);\n    uint32_t new_two_adicity =\n        ComputeAdicity(2, gmp::FromUnsignedInt(coset_size));\n\n    // For each coset, we first build a polynomial of degree |coset size|,\n    // whose evaluations over coset k will agree with the evaluations of a over\n    // the coset. Denote the kth such polynomial as poly_k\n#pragma omp parallel for\n    for (size_t k = 0; k < tmp.size(); ++k) {\n      PolyOrEvals& kth_poly_coeffs = tmp[k];\n      // Shuffle into a sub-FFT\n      F omega_k = omega.Pow(k);\n      F omega_step = omega.Pow(k * coset_size);\n\n      F elt = F::One();\n      // Construct |kth_poly_coeffs|, which is a polynomial whose evaluations on\n      // this coset should equal the evaluations of |a| on this coset.\n      // clang-format off\n        // |kth_poly_coeffs[i] = Σ{c in num_cosets} g^{k * (i + c * |coset|)} * a[i + c * |coset|]|\n      // clang-format on\n      // Where c represents the index of the coset being considered. multiplying\n      // by g^{k * i} corresponds to the shift for just being in a different\n      // coset.\n      //\n      // TODO(chokobole): Come back and improve the speed, and make this a more\n      // 'normal'\n      // See\n      // https://github.com/arkworks-rs/algebra/blob/993a4e7/poly/src/domain/utils.rs#L151\n      // Cooley-Tukey. This appears to be an FFT of the polynomial\n      // |P(x) = Σ{c in num_cosets} a[i + c |coset|] * x^c|\n      // onto this coset.\n      // However this is being evaluated in time O(N) instead of time\n      // O(|coset|log(|coset|)). If this understanding is the case, its not\n      // doing standard Cooley-Tukey. At the moment, this has time complexity\n      // of at least 2 * N field mul's per thread, so we will be getting\n      // pretty bad parallelism. Exact complexity per thread atm is\n      // |2 * N + (N / num threads)log(N / num threads)| field muls.\n      // Compare to the time complexity of serial is Nlog(N) field muls), with\n      // log(N) in [15, 25]\n      for (size_t i = 0; i < coset_size; ++i) {\n        for (size_t c = 0; c < num_threads; ++c) {\n          size_t idx = i + (c * coset_size);\n          // t = the value of a corresponding to the ith element of\n          // the sth coset.\n          F t = a[idx] * elt;\n          kth_poly_coeffs.at(i) += t;\n          // elt = g^{k * idx}\n          elt *= omega_step;\n        }\n        elt *= omega_k;\n      }\n\n      // Perform sub-FFT\n      // Since the sub-FFT is mutative, after this point\n      // |kth_poly_coeffs| should be renamed |kth_coset_evals|.\n      SerialFFT(kth_poly_coeffs, new_omega, new_two_adicity);\n    }\n\n    // shuffle the values computed above into a\n    // The evaluations of a should be ordered as (1, g, g², ...)\n    for (size_t i = 0; i < a.NumElements(); ++i) {\n      a.at(i) = tmp[i % num_cosets][i / num_cosets];\n    }\n  }\n#endif\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_POLYNOMIALS_UNIVARIATE_MIXED_RADIX_EVALUATION_DOMAIN_H_\n"
  },
  {
    "path": "tachyon/math/polynomials/univariate/naive_batch_fft.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_MATH_POLYNOMIALS_UNIVARIATE_NAIVE_BATCH_FFT_H_\n#define TACHYON_MATH_POLYNOMIALS_UNIVARIATE_NAIVE_BATCH_FFT_H_\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/bits.h\"\n#include \"tachyon/base/parallelize.h\"\n#include \"tachyon/math/polynomials/univariate/two_adic_subgroup.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename F>\nclass NaiveBatchFFT : public TwoAdicSubgroup<NaiveBatchFFT<F>> {\n public:\n  template <typename Derived>\n  void FFTBatch(Eigen::MatrixBase<Derived>& mat) const {\n    size_t rows = mat.rows();\n    size_t cols = mat.cols();\n    CHECK(base::bits::IsPowerOfTwo(rows));\n    F g;\n    CHECK(F::GetRootOfUnity(rows, &g));\n\n    RowMajorMatrix<F> res = RowMajorMatrix<F>::Zero(rows, cols);\n\n    base::Parallelize(\n        rows, [rows, cols, &res, &mat, &g](size_t len, size_t chunk_offset,\n                                           size_t chunk_size) {\n          size_t src_row = chunk_offset * chunk_size;\n          F base_pow = g.Pow(src_row);\n          // NOTE (chokobole): |base_pow| is multiplied one extra time for the\n          // sake of code readability, but we choose to overlook this for\n          // simplicity.\n          for (size_t res_r = src_row; res_r < src_row + len; ++res_r) {\n            // NOTE(chokobole): |rows| is guaranteed to be positive number\n            // because of the above |CHECK(base::bits::IsPowerOfTwo(rows))|.\n            F pow = F::One();\n            for (size_t src_r = 0; src_r < rows - 1; ++src_r) {\n              for (size_t col = 0; col < cols; ++col) {\n                res(res_r, col) += pow * mat(src_r, col);\n              }\n              pow *= base_pow;\n            }\n            for (size_t col = 0; col < cols; ++col) {\n              res(res_r, col) += pow * mat(rows - 1, col);\n            }\n            base_pow *= g;\n          }\n        });\n\n    mat = std::move(res);\n  }\n\n  // Compute the low-degree extension of each column in |mat| onto a coset of\n  // a larger subgroup and populate |out| with the result.\n  template <typename Derived>\n  void CosetLDEBatch(const Eigen::MatrixBase<Derived>& mat, size_t added_bits,\n                     F shift, Eigen::MatrixBase<Derived>& out) const {\n    Derived mat_tmp = mat;\n    CosetLDEBatch(std::move(mat_tmp), added_bits, shift, out);\n  }\n\n  template <typename Derived>\n  void CosetLDEBatch(Eigen::MatrixBase<Derived>&& mat, size_t added_bits,\n                     F shift, Eigen::MatrixBase<Derived>& out) const {\n    if constexpr (F::Config::kModulusBits > 32) {\n      NOTREACHED();\n    }\n    Eigen::Index rows = mat.rows();\n    Eigen::Index new_rows = rows << added_bits;\n    CHECK_EQ(out.rows(), new_rows);\n    CHECK_EQ(out.cols(), mat.cols());\n    this->IFFTBatch(mat);\n\n    OMP_PARALLEL_FOR(Eigen::Index row = 0; row < new_rows; ++row) {\n      if (row < rows) {\n        out.row(row) = mat.row(row);\n      } else {\n        out.row(row).setZero();\n      }\n    }\n    CosetFFTBatch(out, shift);\n  }\n\n private:\n  // Compute the \"coset DFT\" of each column in |mat|. This can be viewed as\n  // interpolation onto a coset of a multiplicative subgroup, rather than the\n  // subgroup itself.\n  void CosetFFTBatch(Eigen::MatrixBase<RowMajorMatrix<F>>& mat, F shift) const {\n    if constexpr (F::Config::kModulusBits > 32) {\n      NOTREACHED();\n    }\n    // Observe that\n    // yᵢ = ∑ⱼ cⱼ (s gⁱ)ʲ\n    //    = ∑ⱼ (cⱼ sʲ) (gⁱ)ʲ\n    // which has the structure of an ordinary DFT, except each coefficient cⱼ\n    // is first replaced by cⱼ s.\n    size_t rows = mat.rows();\n    base::Parallelize(\n        rows, [shift, &mat](Eigen::Index len, Eigen::Index chunk_offset,\n                            Eigen::Index chunk_size) {\n          Eigen::Index start = chunk_offset * chunk_size;\n          F weight = shift.Pow(start);\n          // NOTE: It is not possible to have empty chunk so this is safe\n          for (Eigen::Index row = start; row < start + len - 1; ++row) {\n            for (Eigen::Index col = 0; col < mat.cols(); ++col) {\n              mat(row, col) *= weight;\n            }\n            weight *= shift;\n          }\n          for (Eigen::Index col = 0; col < mat.cols(); ++col) {\n            mat(start + len - 1, col) *= weight;\n          }\n        });\n    FFTBatch(mat);\n  }\n};\n\ntemplate <typename F>\nstruct TwoAdicSubgroupTraits<NaiveBatchFFT<F>> {\n  using Field = F;\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_POLYNOMIALS_UNIVARIATE_NAIVE_BATCH_FFT_H_\n"
  },
  {
    "path": "tachyon/math/polynomials/univariate/radix2_evaluation_domain.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n// This header defines |Radix2EvaluationDomain|, an |UnivariateEvaluationDomain|\n// for performing various kinds of polynomial arithmetic on top of fields that\n// are FFT-friendly. |Radix2EvaluationDomain| supports FFTs of size at most\n// 2^|F::Config::kTwoAdicity|.\n\n#ifndef TACHYON_MATH_POLYNOMIALS_UNIVARIATE_RADIX2_EVALUATION_DOMAIN_H_\n#define TACHYON_MATH_POLYNOMIALS_UNIVARIATE_RADIX2_EVALUATION_DOMAIN_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <algorithm>\n#include <cmath>\n#include <memory>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"absl/memory/memory.h\"\n#include \"absl/types/span.h\"\n#include \"gtest/gtest_prod.h\"\n#include \"third_party/eigen3/Eigen/Core\"\n\n#include \"tachyon/base/bits.h\"\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/openmp_util.h\"\n#include \"tachyon/base/parallelize.h\"\n#include \"tachyon/base/profiler.h\"\n#include \"tachyon/math/finite_fields/packed_field_traits_forward.h\"\n#include \"tachyon/math/matrix/matrix_types.h\"\n#include \"tachyon/math/matrix/matrix_utils.h\"\n#include \"tachyon/math/polynomials/univariate/evaluations_utils.h\"\n#include \"tachyon/math/polynomials/univariate/radix2_twiddle_cache.h\"\n#include \"tachyon/math/polynomials/univariate/two_adic_subgroup.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluation_domain.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_polynomial.h\"\n\nnamespace tachyon::math {\n\n// Defines a domain over which finite field (I)FFTs can be performed. Works\n// only for fields that have a large multiplicative subgroup of size that is a\n// power-of-2.\ntemplate <typename F,\n          size_t MaxDegree = (size_t{1} << F::Config::kTwoAdicity) - 1>\nclass Radix2EvaluationDomain\n    : public UnivariateEvaluationDomain<F, MaxDegree>,\n      public TwoAdicSubgroup<Radix2EvaluationDomain<F, MaxDegree>> {\n public:\n  using Base = UnivariateEvaluationDomain<F, MaxDegree>;\n  using Field = F;\n  using Evals = UnivariateEvaluations<F, MaxDegree>;\n  using DensePoly = UnivariateDensePolynomial<F, MaxDegree>;\n  using SparsePoly = UnivariateSparsePolynomial<F, MaxDegree>;\n  using PackedPrimeField =\n      // NOLINTNEXTLINE(whitespace/operators)\n      std::conditional_t<F::Config::kModulusBits <= 32,\n                         typename PackedFieldTraits<F>::PackedField, F>;\n\n  constexpr static size_t kMaxDegree = MaxDegree;\n  // Factor that determines if a the degree aware FFT should be called.\n  constexpr static size_t kDegreeAwareFFTThresholdFactor = size_t{1} << 2;\n\n  enum class FFTOrder {\n    // The input of the FFT must be in-order, but the output does not have to\n    // be.\n    kInOut,\n    // The input of the FFT can be out of order, but the output must be\n    // in-order.\n    kOutIn\n  };\n\n  static std::unique_ptr<Radix2EvaluationDomain> Create(size_t num_coeffs) {\n    auto ret = absl::WrapUnique(new Radix2EvaluationDomain(\n        absl::bit_ceil(num_coeffs), base::bits::SafeLog2Ceiling(num_coeffs)));\n    ret->cache_ =\n        Radix2TwiddleCache<F>::GetItem(ret.get(), /*packed_vec_only=*/false);\n    return ret;\n  }\n\n  // libfqfft uses >\n  // https://github.com/scipr-lab/libfqfft/blob/e0183b2/libfqfft/evaluation_domain/domains/basic_radix2_domain.tcc#L33\n  // (See\n  // https://github.com/arkworks-rs/algebra/blob/master/poly/src/domain/radix2/mod.rs#L62)\n  constexpr static bool IsValidNumCoeffs(size_t num_coeffs) {\n    return base::bits::SafeLog2Ceiling(num_coeffs) <= F::Config::kTwoAdicity;\n  }\n\n  template <typename Derived>\n  void FFTBatch(Eigen::MatrixBase<Derived>& mat) const {\n    TRACE_EVENT(\"EvaluationDomain\", \"Radix2EvaluationDomain::FFTBatch\");\n    if constexpr (F::Config::kModulusBits > 32) {\n      NOTREACHED();\n    }\n    CHECK_GT(cache_->roots_vec.size(), size_t{0});\n    CHECK_GT(cache_->packed_roots_vec.size(), size_t{0});\n    CHECK_EQ(this->size_, static_cast<size_t>(mat.rows()));\n\n    // The first half looks like a normal DIT.\n    ReverseMatrixIndexBits(mat);\n    RunParallelRowChunks(mat, cache_->roots_vec.back(),\n                         cache_->packed_roots_vec[0]);\n\n    // For the second half, we flip the DIT, working in bit-reversed order.\n    ReverseMatrixIndexBits(mat);\n    RunParallelRowChunksReversed(mat, cache_->bitrev_roots_vec,\n                                 cache_->packed_roots_vec[1]);\n    ReverseMatrixIndexBits(mat);\n  }\n\n  template <typename Derived>\n  CONSTEXPR_IF_NOT_OPENMP void CosetLDEBatch(\n      const Eigen::MatrixBase<Derived>& mat, size_t added_bits, F shift,\n      Eigen::MatrixBase<Derived>& out, bool reverse_at_last = true) const {\n    Derived mat_tmp = mat;\n    CosetLDEBatch(std::move(mat_tmp), added_bits, shift, out);\n  }\n\n  template <typename Derived>\n  CONSTEXPR_IF_NOT_OPENMP void CosetLDEBatch(\n      Eigen::MatrixBase<Derived>&& mat, size_t added_bits, F shift,\n      Eigen::MatrixBase<Derived>& out, bool reverse_at_last = true) const {\n    TRACE_EVENT(\"EvaluationDomain\", \"Radix2EvaluationDomain::CosetLDEBatch\");\n    if constexpr (F::Config::kModulusBits > 32) {\n      NOTREACHED();\n    }\n    CHECK_GT(cache_->roots_vec.size(), size_t{0});\n    CHECK_GT(cache_->packed_roots_vec.size(), size_t{0});\n    CHECK_EQ(this->size_, static_cast<size_t>(mat.rows()));\n\n    // The first half looks like a normal DIT.\n    ReverseMatrixIndexBits(mat);\n    RunParallelRowChunks(mat, cache_->inv_roots_vec[0],\n                         cache_->packed_inv_roots_vec[0]);\n\n    // For the second half, we flip the DIT, working in bit-reversed order.\n    ReverseMatrixIndexBits(mat);\n    RunParallelRowChunksReversed(mat, cache_->bitrev_inv_roots_vec,\n                                 cache_->packed_inv_roots_vec[1]);\n    // We skip the final bit-reversal, since the next FFT expects bit-reversed\n    // input.\n\n    // Rescale coefficients in two ways:\n    // - divide by number of rows (since we're doing an inverse DFT)\n    // - multiply by powers of the coset shift (see default coset LDE impl for\n    // an explanation)\n    base::Parallelize(this->size_, [this, shift, &mat](size_t len,\n                                                       size_t chunk_offset,\n                                                       size_t chunk_size) {\n      // Reverse bits because |mat| is encoded in bit-reversed order\n      size_t start = chunk_offset * chunk_size;\n      F weight = this->size_inv_ * shift.Pow(start);\n      // NOTE: It is not possible to have an empty chunk so this is safe\n      for (size_t row = start; row < start + len - 1; ++row) {\n        mat.row(base::bits::ReverseBitsLen(row, this->log_size_of_group_)) *=\n            weight;\n        weight *= shift;\n      }\n      mat.row(base::bits::ReverseBitsLen(start + len - 1,\n                                         this->log_size_of_group_)) *= weight;\n    });\n\n    if (added_bits == 0) {\n      out = std::move(mat);\n    } else {\n      ExpandWithZeroPad(mat, added_bits, out);\n    }\n\n    size_t rows = static_cast<size_t>(out.rows());\n    uint32_t log_size_of_group = base::bits::CheckedLog2(rows);\n    auto domain =\n        absl::WrapUnique(new Radix2EvaluationDomain(rows, log_size_of_group));\n    domain->cache_ =\n        Radix2TwiddleCache<F>::GetItem(domain.get(), /*packed_vec_only=*/true);\n\n    // The first half looks like a normal DIT.\n    domain->RunParallelRowChunks(out, domain->cache_->roots_vec.back(),\n                                 domain->cache_->packed_roots_vec[0]);\n\n    // For the second half, we flip the DIT, working in bit-reversed order.\n    ReverseMatrixIndexBits(out);\n    domain->RunParallelRowChunksReversed(out, domain->cache_->bitrev_roots_vec,\n                                         domain->cache_->packed_roots_vec[1]);\n    if (reverse_at_last) {\n      ReverseMatrixIndexBits(out);\n    }\n  }\n\n private:\n  template <typename T>\n  FRIEND_TEST(UnivariateEvaluationDomainTest, RootsOfUnity);\n\n  using UnivariateEvaluationDomain<F, MaxDegree>::UnivariateEvaluationDomain;\n\n  // UnivariateEvaluationDomain methods\n  FFTAlgorithm GetAlgorithm() const override { return FFTAlgorithm::kRadix2; }\n\n  constexpr std::unique_ptr<UnivariateEvaluationDomain<F, MaxDegree>> Clone()\n      const override {\n    return absl::WrapUnique(new Radix2EvaluationDomain(*this));\n  }\n\n  CONSTEXPR_IF_NOT_OPENMP void DoFFT(Evals& evals) const override {\n    TRACE_EVENT(\"EvaluationDomain\", \"Radix2EvaluationDomain::DoFFT\");\n    DegreeAwareFFTInPlace(evals);\n  }\n\n  CONSTEXPR_IF_NOT_OPENMP void DoIFFT(DensePoly& poly) const override {\n    TRACE_EVENT(\"EvaluationDomain\", \"Radix2EvaluationDomain::DoIFFT\");\n    poly.coefficients_.coefficients_.resize(this->size_, F::Zero());\n    InOrderIFFTInPlace(poly);\n    poly.coefficients_.RemoveHighDegreeZeros();\n  }\n\n  // Degree aware FFT that runs in O(n log d) instead of O(n log n).\n  // Implementation copied from libiop. (See\n  // https://github.com/arkworks-rs/algebra/blob/master/poly/src/domain/radix2/fft.rs#L28)\n  CONSTEXPR_IF_NOT_OPENMP void DegreeAwareFFTInPlace(Evals& evals) const {\n    TRACE_EVENT(\"EvaluationDomain\", \"DegreeAwareFFTInPlace\");\n    if (!this->offset_.IsOne()) {\n      Base::DistributePowers(evals, this->offset_);\n    }\n    size_t n = this->size_;\n    uint32_t log_n = this->log_size_of_group_;\n    size_t num_coeffs = evals.evaluations_.size();\n    uint32_t log_d = base::bits::SafeLog2Ceiling(num_coeffs);\n    // When the polynomial is of size k * |coset|, for k < 2ⁱ, the first i\n    // iterations of Cooley-Tukey are easily predictable. This is because they\n    // will be combining g(ω²) + ω * h(ω²), but g or h will always refer to a\n    // coefficient that is 0. Therefore those first i rounds have the effect\n    // of copying the evaluations into more locations, so we handle this in\n    // initialization, and reduce the number of loops that are performing\n    // arithmetic. The number of times we copy each initial non-zero element\n    // is as below:\n    CHECK_GE(log_n, log_d);\n    size_t duplicity_of_initials = size_t{1} << (log_n - log_d);\n    evals.evaluations_.resize(n, F::Zero());\n    SwapBitRevElementsInPlace(evals, num_coeffs, log_n);\n    size_t start_gap = 1;\n    if (duplicity_of_initials >= kDegreeAwareFFTThresholdFactor) {\n      base::ParallelizeByChunkSize(evals.evaluations_, duplicity_of_initials,\n                                   [](absl::Span<F> chunk) {\n                                     const F& v = chunk[0];\n                                     for (size_t j = 1; j < chunk.size(); ++j) {\n                                       chunk[j] = v;\n                                     }\n                                   });\n      start_gap = duplicity_of_initials;\n    }\n    OutInHelper(evals, start_gap);\n  }\n\n  CONSTEXPR_IF_NOT_OPENMP void InOrderIFFTInPlace(DensePoly& poly) const {\n    TRACE_EVENT(\"EvaluationDomain\", \"InOrderIFFTInPlace\");\n    IFFTHelperInPlace(poly);\n    if (this->offset_.IsOne()) {\n      TRACE_EVENT(\"Subtask\", \"OMPMulBySizeInv\");\n      // clang-format off\n      OMP_PARALLEL_FOR(F& val : poly.coefficients_.coefficients_) {\n        // clang-format on\n        val *= this->size_inv_;\n      }\n    } else {\n      Base::DistributePowersAndMulByConst(poly, this->offset_inv_,\n                                          this->size_inv_);\n    }\n  }\n\n  // Handles doing an IFFT with handling of being in order and out of order.\n  // The results here must all be divided by |poly|, which is left up to the\n  // caller to do.\n  CONSTEXPR_IF_NOT_OPENMP void IFFTHelperInPlace(DensePoly& poly) const {\n    TRACE_EVENT(\"EvaluationDomain\", \"IFFTHelperInPlace\");\n    InOutHelper(poly);\n    SwapBitRevElementsInPlace(poly, poly.coefficients_.coefficients_.size(),\n                              this->log_size_of_group_);\n  }\n\n  template <FFTOrder Order, typename PolyOrEvals>\n  CONSTEXPR_IF_NOT_OPENMP static void ApplyButterfly(PolyOrEvals& poly_or_evals,\n                                                     absl::Span<const F> roots,\n                                                     size_t gap) {\n    TRACE_EVENT(\"EvaluationDomain\", \"ApplyButterfly\");\n    void (*fn)(F&, F&, const F&);\n\n    if constexpr (Order == FFTOrder::kInOut) {\n      fn = UnivariateEvaluationDomain<F, MaxDegree>::ButterflyFnInOut;\n    } else {\n      static_assert(Order == FFTOrder::kOutIn);\n      fn = UnivariateEvaluationDomain<F,\n                                      MaxDegree>::template ButterflyFnOutIn<F>;\n    }\n\n    // Each butterfly cluster uses 2 * |gap| positions.\n    size_t chunk_size = 2 * gap;\n    OMP_PARALLEL_NESTED_FOR(size_t i = 0; i < poly_or_evals.NumElements();\n                            i += chunk_size) {\n      for (size_t j = 0; j < gap; ++j) {\n        fn(poly_or_evals.at(i + j), poly_or_evals.at(i + j + gap), roots[j]);\n      }\n    }\n  }\n\n  CONSTEXPR_IF_NOT_OPENMP void InOutHelper(DensePoly& poly) const {\n    TRACE_EVENT(\"EvaluationDomain\", \"InOutHelper\");\n    size_t gap = poly.coefficients_.coefficients_.size() / 2;\n    size_t idx = 0;\n    while (gap > 0) {\n      ApplyButterfly<FFTOrder::kInOut>(poly, cache_->inv_roots_vec[idx++], gap);\n      gap /= 2;\n    }\n  }\n\n  CONSTEXPR_IF_NOT_OPENMP void OutInHelper(Evals& evals,\n                                           size_t start_gap) const {\n    TRACE_EVENT(\"EvaluationDomain\", \"OutInHelper\");\n    size_t gap = start_gap;\n    size_t idx = base::bits::SafeLog2Ceiling(start_gap);\n    while (gap < evals.evaluations_.size()) {\n      ApplyButterfly<FFTOrder::kOutIn>(evals, cache_->roots_vec[idx++], gap);\n      gap *= 2;\n    }\n  }\n\n  // This can be used as the first half of a parallelized butterfly network.\n  template <typename Derived>\n  CONSTEXPR_IF_NOT_OPENMP void RunParallelRowChunks(\n      Eigen::MatrixBase<Derived>& mat, absl::Span<const F> twiddles,\n      absl::Span<const PackedPrimeField> packed_twiddles_rev) const {\n    TRACE_EVENT(\"EvaluationDomain\", \"RunParallelRowChunks\");\n    if constexpr (F::Config::kModulusBits > 32) {\n      NOTREACHED();\n    }\n    CHECK(base::bits::IsPowerOfTwo(mat.rows()));\n    size_t cols = static_cast<size_t>(mat.cols());\n    uint32_t log_n = this->log_size_of_group_;\n    uint32_t mid = log_n / 2;\n    size_t chunk_rows = size_t{1} << mid;\n\n    // max block size: 2^|mid|\n    OMP_PARALLEL_FOR(size_t block_start = 0; block_start < this->size_;\n                     block_start += chunk_rows) {\n      size_t cur_chunk_rows = std::min(chunk_rows, this->size_ - block_start);\n      Eigen::Block<Derived> submat =\n          mat.block(block_start, 0, cur_chunk_rows, cols);\n      for (uint32_t layer = 0; layer < mid; ++layer) {\n        RunDitLayers(submat, layer, twiddles, packed_twiddles_rev, false);\n      }\n    }\n  }\n\n  // This can be used as the second half of a parallelized butterfly network.\n  template <typename Derived>\n  CONSTEXPR_IF_NOT_OPENMP void RunParallelRowChunksReversed(\n      Eigen::MatrixBase<Derived>& mat, absl::Span<const F> twiddles_rev,\n      absl::Span<const PackedPrimeField> packed_twiddles_rev) const {\n    TRACE_EVENT(\"EvaluationDomain\", \"RunParallelRowChunksReversed\");\n\n    if constexpr (F::Config::kModulusBits > 32) {\n      NOTREACHED();\n    }\n    CHECK(base::bits::IsPowerOfTwo(mat.rows()));\n    size_t cols = static_cast<size_t>(mat.cols());\n    uint32_t log_n = this->log_size_of_group_;\n    uint32_t mid = log_n / 2;\n    size_t chunk_rows = size_t{1} << (log_n - mid);\n\n    TRACE_EVENT(\"Subtask\", \"RunDitLayersLoop\");\n    // max block size: 2^(|log_n| - |mid|)\n    OMP_PARALLEL_FOR(size_t block_start = 0; block_start < this->size_;\n                     block_start += chunk_rows) {\n      size_t thread = block_start / chunk_rows;\n      size_t cur_chunk_rows = std::min(chunk_rows, this->size_ - block_start);\n      Eigen::Block<Derived> submat =\n          mat.block(block_start, 0, cur_chunk_rows, cols);\n      for (uint32_t layer = mid; layer < log_n; ++layer) {\n        size_t first_block = thread << (layer - mid);\n        RunDitLayers(submat, layer,\n                     twiddles_rev.subspan(first_block,\n                                          twiddles_rev.size() - first_block),\n                     packed_twiddles_rev.subspan(\n                         first_block, packed_twiddles_rev.size() - first_block),\n                     true);\n      }\n    }\n  }\n\n  template <typename Derived>\n  CONSTEXPR_IF_NOT_OPENMP void RunDitLayers(\n      Eigen::Block<Derived>& submat, uint32_t layer,\n      absl::Span<const F> twiddles,\n      absl::Span<const PackedPrimeField> packed_twiddles, bool rev) const {\n    if constexpr (F::Config::kModulusBits > 32) {\n      NOTREACHED();\n    }\n    uint32_t layer_rev = this->log_size_of_group_ - 1 - layer;\n    size_t half_block_size = size_t{1} << (rev ? layer_rev : layer);\n    size_t block_size = half_block_size * 2;\n    size_t sub_rows = static_cast<size_t>(submat.rows());\n    DCHECK_GE(sub_rows, block_size);\n\n    for (size_t block_start = 0; block_start < sub_rows;\n         block_start += block_size) {\n      for (size_t i = 0; i < half_block_size; ++i) {\n        size_t lo = block_start + i;\n        size_t hi = lo + half_block_size;\n        F twiddle =\n            rev ? twiddles[block_start / block_size] : twiddles[i << layer_rev];\n        const PackedPrimeField& packed_twiddle =\n            rev ? packed_twiddles[block_start / block_size]\n                : packed_twiddles[i << layer_rev];\n        ApplyButterflyToRows(submat, lo, hi, twiddle, packed_twiddle);\n      }\n    }\n  }\n\n  template <typename Derived>\n  CONSTEXPR_IF_NOT_OPENMP static void ApplyButterflyToRows(\n      Eigen::Block<Derived>& mat, size_t row_1, size_t row_2, F twiddle,\n      const PackedPrimeField& packed_twiddle) {\n    if constexpr (F::Config::kModulusBits > 32) {\n      NOTREACHED();\n    }\n    auto row_1_block = mat.row(row_1);\n    auto row_2_block = mat.row(row_2);\n\n    for (size_t i = 0; i < row_1_block.cols() / PackedPrimeField::N; ++i) {\n      UnivariateEvaluationDomain<F, MaxDegree>::template ButterflyFnOutIn(\n          *reinterpret_cast<PackedPrimeField*>(\n              &row_1_block.data()[PackedPrimeField::N * i]),\n          *reinterpret_cast<PackedPrimeField*>(\n              &row_2_block.data()[PackedPrimeField::N * i]),\n          packed_twiddle);\n    }\n    size_t remaining_start_idx =\n        row_1_block.cols() / PackedPrimeField::N * PackedPrimeField::N;\n    for (size_t i = remaining_start_idx;\n         i < static_cast<size_t>(row_1_block.cols()); ++i) {\n      UnivariateEvaluationDomain<F, MaxDegree>::template ButterflyFnOutIn(\n          *reinterpret_cast<F*>(&row_1_block.data()[i]),\n          *reinterpret_cast<F*>(&row_2_block.data()[i]), twiddle);\n    }\n  }\n\n  // Expands a |Eigen::MatrixBase|'s rows from |rows| to |rows|^(|added_bits|),\n  // moving values from row |i| to row |i|^(|added_bits|). All new entries are\n  // set to |F::Zero()|.\n  // Note that it crashes if the |added_bits| is zero.\n  template <typename Derived>\n  CONSTEXPR_IF_NOT_OPENMP static void ExpandWithZeroPad(\n      Eigen::MatrixBase<Derived>& mat, size_t added_bits,\n      Eigen::MatrixBase<Derived>& out) {\n    CHECK_GT(added_bits, size_t{0});\n\n    Eigen::Index new_rows = mat.rows() << added_bits;\n    CHECK_EQ(out.rows(), new_rows);\n    CHECK_EQ(out.cols(), mat.cols());\n    Eigen::Index mask = (Eigen::Index{1} << added_bits) - 1;\n\n    OMP_PARALLEL_FOR(Eigen::Index row = 0; row < new_rows; ++row) {\n      if ((row & mask) == 0) {\n        out.row(row) = mat.row(row >> added_bits);\n      } else {\n        out.row(row).setZero();\n      }\n    }\n  }\n\n  typename Radix2TwiddleCache<F>::Item* cache_ = nullptr;\n};\n\ntemplate <typename F, size_t MaxDegree>\nstruct TwoAdicSubgroupTraits<Radix2EvaluationDomain<F, MaxDegree>> {\n  using Field = F;\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_POLYNOMIALS_UNIVARIATE_RADIX2_EVALUATION_DOMAIN_H_\n"
  },
  {
    "path": "tachyon/math/polynomials/univariate/radix2_evaluation_domain_unittest.cc",
    "content": "#include \"tachyon/math/polynomials/univariate/radix2_evaluation_domain.h\"\n\n#include <memory>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/finite_fields/baby_bear/baby_bear.h\"\n#include \"tachyon/math/finite_fields/koala_bear/koala_bear.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/matrix/matrix_types.h\"\n#include \"tachyon/math/polynomials/univariate/naive_batch_fft.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\ntemplate <typename F>\nclass Radix2EvaluationDomainTest\n    : public FiniteFieldTest<typename PackedFieldTraits<F>::PackedField> {};\n\n}  // namespace\n\nusing PrimeFieldTypes = testing::Types<BabyBear, KoalaBear>;\nTYPED_TEST_SUITE(Radix2EvaluationDomainTest, PrimeFieldTypes);\n\nTYPED_TEST(Radix2EvaluationDomainTest, FFTBatchDeath) {\n  using F = TypeParam;\n  std::unique_ptr<Radix2EvaluationDomain<F>> domain =\n      Radix2EvaluationDomain<F>::Create(1);\n  RowMajorMatrix<F> matrix = RowMajorMatrix<F>::Random(1, 3);\n  EXPECT_DEATH(domain->FFTBatch(matrix), \"\");\n}\n\nTYPED_TEST(Radix2EvaluationDomainTest, FFTBatch) {\n  using F = TypeParam;\n  for (uint32_t log_r = 1; log_r < 5; ++log_r) {\n    RowMajorMatrix<F> expected =\n        RowMajorMatrix<F>::Random(size_t{1} << log_r, 3);\n    RowMajorMatrix<F> result = expected;\n    NaiveBatchFFT<F> naive;\n    naive.FFTBatch(expected);\n    std::unique_ptr<Radix2EvaluationDomain<F>> domain =\n        Radix2EvaluationDomain<F>::Create(size_t{1} << log_r);\n    domain->FFTBatch(result);\n    EXPECT_EQ(expected, result);\n  }\n}\n\nTYPED_TEST(Radix2EvaluationDomainTest, CosetLDEBatchDeath) {\n  using F = TypeParam;\n  std::unique_ptr<Radix2EvaluationDomain<F>> domain =\n      Radix2EvaluationDomain<F>::Create(1);\n  RowMajorMatrix<F> matrix = RowMajorMatrix<F>::Random(1, 3);\n  F shift = F::FromMontgomery(F::Config::kSubgroupGenerator);\n  RowMajorMatrix<F> result(2, 3);\n  EXPECT_DEATH(domain->CosetLDEBatch(std::move(matrix), 1, shift, result), \"\");\n}\n\nTYPED_TEST(Radix2EvaluationDomainTest, CosetLDEBatch) {\n  using F = TypeParam;\n  for (uint32_t log_r = 1; log_r < 5; ++log_r) {\n    RowMajorMatrix<F> input = RowMajorMatrix<F>::Random(size_t{1} << log_r, 3);\n    NaiveBatchFFT<F> naive;\n    F shift = F::FromMontgomery(F::Config::kSubgroupGenerator);\n    RowMajorMatrix<F> expected(size_t{1} << (log_r + 1), 3);\n    naive.CosetLDEBatch(input, 1, shift, expected);\n    std::unique_ptr<Radix2EvaluationDomain<F>> domain =\n        Radix2EvaluationDomain<F>::Create(size_t{1} << log_r);\n    RowMajorMatrix<F> result(size_t{1} << (log_r + 1), 3);\n    domain->CosetLDEBatch(std::move(input), 1, shift, result);\n    EXPECT_EQ(expected, result);\n  }\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/polynomials/univariate/radix2_twiddle_cache.h",
    "content": "#ifndef TACHYON_MATH_POLYNOMIALS_UNIVARIATE_RADIX2_TWIDDLE_CACHE_H_\n#define TACHYON_MATH_POLYNOMIALS_UNIVARIATE_RADIX2_TWIDDLE_CACHE_H_\n\n#include <stddef.h>\n\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"absl/base/thread_annotations.h\"\n#include \"absl/container/flat_hash_map.h\"\n#include \"absl/synchronization/mutex.h\"\n\n#include \"tachyon/base/no_destructor.h\"\n#include \"tachyon/base/openmp_util.h\"\n#include \"tachyon/base/profiler.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename F>\nclass Radix2TwiddleCache {\n public:\n  using PackedPrimeField =\n      // NOLINTNEXTLINE(whitespace/operators)\n      std::conditional_t<F::Config::kModulusBits <= 32,\n                         typename PackedFieldTraits<F>::PackedField, F>;\n\n  struct Item {\n    // For small prime fields\n    std::vector<F> bitrev_roots_vec;\n    std::vector<F> bitrev_inv_roots_vec;\n    std::vector<std::vector<PackedPrimeField>> packed_roots_vec;\n    std::vector<std::vector<PackedPrimeField>> packed_inv_roots_vec;\n    // For all finite fields\n    std::vector<std::vector<F>> roots_vec;\n    std::vector<std::vector<F>> inv_roots_vec;\n    bool packed_vec_only;\n\n    // clang-format off\n    // Precompute |roots_vec| and |inv_roots_vec| for |OutInHelper()| and |InOutHelper()|.\n    // Here is an example where |domain->size()| equals 32.\n    // |roots_vec| = [\n    //   [1],\n    //   [1, ω⁸],\n    //   [1, ω⁴, ω⁸, ω¹²],\n    //   [1, ω², ω⁴, ω⁶, ω⁸, ω¹⁰, ω¹², ω¹⁴],\n    //   [1, ω, ω², ω³, ω⁴, ω⁵, ω⁶, ω⁷, ω⁸, ω⁹, ω¹⁰, ω¹¹, ω¹², ω¹³, ω¹⁴, ω¹⁵],\n    // ]\n    // |inv_roots_vec| = [\n    //   [1, ω⁻¹, ω⁻², ω⁻³, ω⁻⁴, ω⁻⁵, ω⁻⁶, ω⁻⁷, ω⁻⁸, ω⁻⁹, ω⁻¹⁰, ω⁻¹¹, ω⁻¹², ω⁻¹³, ω⁻¹⁴, ω⁻¹⁵],\n    //   [1, ω⁻², ω⁻⁴, ω⁻⁶, ω⁻⁸, ω⁻¹⁰, ω⁻¹², ω⁻¹⁴],\n    //   [1, ω⁻⁴, ω⁻⁸, ω⁻¹²],\n    //   [1, ω⁻⁸],\n    //   [1],\n    // ]\n    // clang-format on\n    template <typename Domain>\n    Item(const Domain* domain, bool packed_vec_only) {\n      TRACE_EVENT(\"Utils\", \"Radix2TwiddleCache<F>::Item::Init\");\n      if (domain->log_size_of_group() == 0) return;\n\n      this->packed_vec_only = packed_vec_only;\n\n      roots_vec.resize(domain->log_size_of_group());\n      inv_roots_vec.resize(domain->log_size_of_group());\n\n      size_t vec_largest_size = domain->size() / 2;\n\n      // Compute biggest vector of |roots_vec_| and |inv_roots_vec_| first.\n      std::vector<F> largest =\n          Domain::GetRootsOfUnity(vec_largest_size, domain->group_gen());\n      std::vector<F> largest_inv =\n          Domain::GetRootsOfUnity(vec_largest_size, domain->group_gen_inv());\n\n      if constexpr (F::Config::kModulusBits <= 32) {\n        TRACE_EVENT(\"Subtask\", \"PreparePackedVec\");\n        packed_roots_vec.resize(2);\n        packed_inv_roots_vec.resize(2);\n        packed_roots_vec[0].resize(vec_largest_size);\n        packed_inv_roots_vec[0].resize(vec_largest_size);\n        packed_roots_vec[1].resize(vec_largest_size);\n        packed_inv_roots_vec[1].resize(vec_largest_size);\n        bitrev_roots_vec = SwapBitRevElements(largest);\n        bitrev_inv_roots_vec = SwapBitRevElements(largest_inv);\n        OMP_PARALLEL_FOR(size_t i = 0; i < vec_largest_size; ++i) {\n          packed_roots_vec[0][i] = PackedPrimeField::Broadcast(largest[i]);\n          packed_inv_roots_vec[0][i] =\n              PackedPrimeField::Broadcast(largest_inv[i]);\n          packed_roots_vec[1][i] =\n              PackedPrimeField::Broadcast(bitrev_roots_vec[i]);\n          packed_inv_roots_vec[1][i] =\n              PackedPrimeField::Broadcast(bitrev_inv_roots_vec[i]);\n        }\n      }\n\n      TRACE_EVENT(\"Subtask\", \"PrepareRootsVec\");\n\n      roots_vec[domain->log_size_of_group() - 1] = std::move(largest);\n      inv_roots_vec[0] = std::move(largest_inv);\n\n      if (packed_vec_only) return;\n\n      // Prepare space in each vector for the others.\n      size_t size = domain->size() / 2;\n      for (size_t i = 1; i < domain->log_size_of_group(); ++i) {\n        size /= 2;\n        roots_vec[domain->log_size_of_group() - i - 1].resize(size);\n        inv_roots_vec[i].resize(size);\n      }\n\n      // Assign every element based on the biggest vector.\n      OMP_PARALLEL_FOR(size_t i = 1; i < domain->log_size_of_group(); ++i) {\n        size_t pow2_i = size_t{1} << i;\n        for (size_t j = 0; j < domain->size() / (pow2_i << 1); ++j) {\n          size_t k = pow2_i * j;\n          roots_vec[domain->log_size_of_group() - i - 1][j] =\n              roots_vec.back()[k];\n          inv_roots_vec[i][j] = inv_roots_vec.front()[k];\n        }\n      }\n    }\n  };\n\n  template <typename Domain>\n  static Item* GetItem(const Domain* domain, bool packed_vec_only) {\n    static base::NoDestructor<Radix2TwiddleCache> twiddle_cache;\n\n    absl::MutexLock lock(&twiddle_cache->mutex_);\n    auto it = twiddle_cache->items_.find(\n        std::make_pair(domain->size(), domain->group_gen()));\n    if (it == twiddle_cache->items_.end() ||\n        (it->second->packed_vec_only && !packed_vec_only)) {\n      it = twiddle_cache->items_.insert(\n          it,\n          std::make_pair(std::make_pair(domain->size(), domain->group_gen()),\n                         std::make_unique<Item>(domain, packed_vec_only)));\n    }\n    return it->second.get();\n  }\n\n private:\n  absl::Mutex mutex_;\n  absl::flat_hash_map<std::pair<size_t, F>, std::unique_ptr<Item>> items_\n      ABSL_GUARDED_BY(mutex_);\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_POLYNOMIALS_UNIVARIATE_RADIX2_TWIDDLE_CACHE_H_\n"
  },
  {
    "path": "tachyon/math/polynomials/univariate/support_poly_operators.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_POLYNOMIALS_UNIVARIATE_SUPPORT_POLY_OPERATORS_H_\n#define TACHYON_MATH_POLYNOMIALS_UNIVARIATE_SUPPORT_POLY_OPERATORS_H_\n\n#include \"tachyon/math/polynomials/support_poly_operators.h\"\n\n#define SUPPORTS_POLY_OPERATOR(Name)                                     \\\n  template <typename Coefficients, typename L, typename R>               \\\n  struct SupportsPoly##Name<                                             \\\n      Coefficients, L, R,                                                \\\n      decltype(void(UnivariatePolynomialOp<Coefficients>::Name(          \\\n          std::declval<const L&>(), std::declval<const R&>())))>         \\\n      : std::true_type {};                                               \\\n                                                                         \\\n  template <typename Coefficients, typename L, typename R>               \\\n  struct SupportsPoly##Name##InPlace<                                    \\\n      Coefficients, L, R,                                                \\\n      decltype(void(UnivariatePolynomialOp<Coefficients>::Name##InPlace( \\\n          std::declval<L&>(), std::declval<const R&>())))> : std::true_type {}\n\nnamespace tachyon::math::internal {\n\ntemplate <typename Coefficients>\nclass UnivariatePolynomialOp;\n\nSUPPORTS_POLY_OPERATOR(Add);\nSUPPORTS_POLY_OPERATOR(Sub);\nSUPPORTS_POLY_OPERATOR(Mul);\nSUPPORTS_POLY_OPERATOR(Div);\nSUPPORTS_POLY_OPERATOR(Mod);\n\n}  // namespace tachyon::math::internal\n\n#undef SUPPORTS_POLY_OPERATOR\n\n#endif  // TACHYON_MATH_POLYNOMIALS_UNIVARIATE_SUPPORT_POLY_OPERATORS_H_\n"
  },
  {
    "path": "tachyon/math/polynomials/univariate/two_adic_subgroup.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_MATH_POLYNOMIALS_UNIVARIATE_TWO_ADIC_SUBGROUP_H_\n#define TACHYON_MATH_POLYNOMIALS_UNIVARIATE_TWO_ADIC_SUBGROUP_H_\n\n#include <optional>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/optional.h\"\n#include \"tachyon/math/matrix/matrix_types.h\"\n#include \"tachyon/math/polynomials/univariate/two_adic_subgroup_traits_forward.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename Derived>\nclass TwoAdicSubgroup {\n public:\n  using F = typename TwoAdicSubgroupTraits<Derived>::Field;\n\n  // Compute the inverse DFT of each column in |mat|.\n  template <typename T>\n  void IFFTBatch(Eigen::MatrixBase<T>& mat) const {\n    if constexpr (F::Config::kModulusBits > 32) {\n      NOTREACHED();\n    }\n    const Derived* derived = static_cast<const Derived*>(this);\n    derived->FFTBatch(mat);\n    Eigen::Index rows = mat.rows();\n    // TODO(chokobole): Use |size_inv_| instead of directly computing the value.\n    F inv = unwrap(F(rows).Inverse());\n\n    mat.row(0) *= inv;\n    mat.row(rows / 2) *= inv;\n    OMP_PARALLEL_FOR(Eigen::Index row = 1; row < rows / 2; ++row) {\n      auto row1 = mat.row(row);\n      auto row2 = mat.row(rows - row);\n      row1 *= inv;\n      row2 *= inv;\n      row1.swap(row2);\n    }\n  }\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_POLYNOMIALS_UNIVARIATE_TWO_ADIC_SUBGROUP_H_\n"
  },
  {
    "path": "tachyon/math/polynomials/univariate/two_adic_subgroup_traits_forward.h",
    "content": "// Copyright (c) 2022 The Plonky3 Authors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.plonky3 and the LICENCE-APACHE.plonky3\n// file.\n\n#ifndef TACHYON_MATH_POLYNOMIALS_UNIVARIATE_TWO_ADIC_SUBGROUP_TRAITS_FORWARD_H_\n#define TACHYON_MATH_POLYNOMIALS_UNIVARIATE_TWO_ADIC_SUBGROUP_TRAITS_FORWARD_H_\n\nnamespace tachyon::math {\n\ntemplate <typename Derived>\nstruct TwoAdicSubgroupTraits;\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_POLYNOMIALS_UNIVARIATE_TWO_ADIC_SUBGROUP_TRAITS_FORWARD_H_\n"
  },
  {
    "path": "tachyon/math/polynomials/univariate/univariate_dense_coefficients.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_POLYNOMIALS_UNIVARIATE_UNIVARIATE_DENSE_COEFFICIENTS_H_\n#define TACHYON_MATH_POLYNOMIALS_UNIVARIATE_UNIVARIATE_DENSE_COEFFICIENTS_H_\n\n#include <stddef.h>\n\n#include <functional>\n#include <numeric>\n#include <sstream>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"absl/hash/hash.h\"\n\n#include \"tachyon/base/buffer/copyable.h\"\n#include \"tachyon/base/containers/adapters.h\"\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/parallelize.h\"\n#include \"tachyon/base/strings/string_util.h\"\n#include \"tachyon/math/base/parallelize_threshold.h\"\n#include \"tachyon/math/polynomials/univariate/support_poly_operators.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluation_domain_forwards.h\"\n\nnamespace tachyon {\nnamespace math {\n\ntemplate <typename Coefficients>\nclass UnivariatePolynomial;\n\ntemplate <typename F, size_t MaxDegree>\nclass UnivariateSparseCoefficients;\n\n// DenseCoefficients class provides a representation for polynomials where\n// coefficients are stored contiguously for each degree. This is efficient for\n// polynomials where most of the degrees have non-zero coefficients.\ntemplate <typename F, size_t MaxDegree>\nclass UnivariateDenseCoefficients {\n public:\n  constexpr static size_t kMaxDegree = MaxDegree;\n\n  constexpr static F kZero = F::Zero();\n\n  using Field = F;\n  using Point = F;\n\n  constexpr UnivariateDenseCoefficients() = default;\n  constexpr explicit UnivariateDenseCoefficients(\n      const std::vector<F>& coefficients, bool cleanup = false)\n      : coefficients_(coefficients) {\n    if (cleanup) RemoveHighDegreeZeros();\n    CHECK_LE(Degree(), kMaxDegree);\n  }\n\n  constexpr explicit UnivariateDenseCoefficients(std::vector<F>&& coefficients,\n                                                 bool cleanup = false)\n      : coefficients_(std::move(coefficients)) {\n    if (cleanup) RemoveHighDegreeZeros();\n    CHECK_LE(Degree(), kMaxDegree);\n  }\n\n  constexpr static UnivariateDenseCoefficients Zero() {\n    return UnivariateDenseCoefficients();\n  }\n\n  constexpr static UnivariateDenseCoefficients One() {\n    return UnivariateDenseCoefficients({F::One()});\n  }\n\n  constexpr static UnivariateDenseCoefficients Random(size_t degree) {\n    std::vector v =\n        base::CreateVector(degree + 1, []() { return F::Random(); });\n    if (v[degree].IsZero()) {\n      v[degree] = F::One();\n    }\n    return UnivariateDenseCoefficients(std::move(v));\n  }\n\n  // Return dense coefficients according to the given |roots|.\n  // This is taken and modified from\n  // https://github.com/Plonky3/Plonky3/blob/b21d54f/field/src/helpers.rs#L81-L92.\n  template <typename Container>\n  constexpr static UnivariateDenseCoefficients FromRoots(\n      const Container& roots) {\n    // clang-format off\n    // For (X - x₀)(X - x₁)(X - x₂)(X - x₃), what this function does looks as follows:\n    //\n    //       |     c[0] |             c[1] |             c[2] |             c[3] |             c[4] |\n    // ------|----------|------------------|------------------|------------------| -----------------|\n    // init  |        1 |               0  |               0  |               0  |               0  |\n    // i = 0 |      -x₀ | c[0] - x₀ * c[1] | c[1] - x₀ * c[2] | c[2] - x₀ * c[3] | c[3] - x₀ * c[4] |\n    // i = 1 |     x₀x₁ | c[0] - x₁ * c[1] | c[1] - x₁ * c[2] | c[2] - x₁ * c[3] | c[3] - x₁ * c[4] |\n    // i = 2 |  -x₀x₁x₂ | c[0] - x₂ * c[1] | c[1] - x₂ * c[2] | c[2] - x₂ * c[3] | c[3] - x₂ * c[4] |\n    // i = 3 | x₀x₁x₂x₃ | c[0] - x₃ * c[1] | c[1] - x₃ * c[2] | c[2] - x₃ * c[3] | c[3] - x₃ * c[4] |\n\n    // Then the values are changed as follows:\n    //\n    //       |     c[0] |                                 c[1] |                                    c[2] |                 c[3] | c[4] |\n    // ------|----------|--------------------------------------|-----------------------------------------|----------------------|------|\n    // init  |        1 |                                    0 |                                       0 |                    0 |    0 |\n    // i = 0 |      -x₀ |                                    1 |                                       0 |                    0 |    0 |\n    // i = 1 |     x₀x₁ |                           -(x₀ + x₁) |                                       1 |                    0 |    0 |\n    // i = 2 |  -x₀x₁x₂ |                   x₀x₁ + x₀x₂ + x₁x₂ |                          -(x₀ + x₁ +x₂) |                    1 |    0 |\n    // i = 3 | x₀x₁x₂x₃ | -(x₀x₁x₂ + x₀x₁x₃ + x₀x₂x₃ + x₁x₂x₃) | x₀x₁ + x₀x₂ + x₀x₃ + x₁x₂ + x₁x₃ + x₂x₃ | -(x₀ + x₁ + x₂ + x₃) |    1 |\n    // clang-format on\n\n    std::vector<F> coefficients(std::size(roots) + 1);\n    base::ParallelizeFill(coefficients, F::Zero(),\n                          /*threshold=*/ParallelizeThreshold::kFieldInit);\n    coefficients[0] = F::One();\n    for (size_t i = 0; i < std::size(roots); ++i) {\n      for (size_t j = i + 1; j > 0; --j) {\n        coefficients[j] = coefficients[j - 1] - roots[i] * coefficients[j];\n      }\n      coefficients[0] *= -roots[i];\n    }\n\n    UnivariateDenseCoefficients ret;\n    ret.coefficients_ = std::move(coefficients);\n    return ret;\n  }\n\n  constexpr const std::vector<F>& coefficients() const { return coefficients_; }\n  constexpr std::vector<F>& coefficients() { return coefficients_; }\n\n  std::vector<F>&& TakeCoefficients() && { return std::move(coefficients_); }\n\n  constexpr bool operator==(const UnivariateDenseCoefficients& other) const {\n    return coefficients_ == other.coefficients_;\n  }\n\n  constexpr bool operator!=(const UnivariateDenseCoefficients& other) const {\n    return !operator==(other);\n  }\n\n  constexpr F& at(size_t i) {\n    CHECK_LT(i, coefficients_.size());\n    return coefficients_[i];\n  }\n  constexpr const F& at(size_t i) const { return (*this)[i]; }\n\n  constexpr const F& operator[](size_t i) const {\n    if (i < coefficients_.size()) {\n      return coefficients_[i];\n    }\n    return kZero;\n  }\n\n  constexpr const F& GetLeadingCoefficient() const {\n    if (IsZero()) return kZero;\n    return coefficients_.back();\n  }\n\n  constexpr bool IsZero() const { return coefficients_.empty(); }\n\n  constexpr bool IsOne() const {\n    return coefficients_.size() == 1 && coefficients_[0].IsOne();\n  }\n\n  constexpr size_t Degree() const {\n    if (IsZero()) return 0;\n    return coefficients_.size() - 1;\n  }\n\n  constexpr size_t NumElements() const { return coefficients_.size(); }\n\n  constexpr F Evaluate(const Point& point) const {\n    if (IsZero()) return F::Zero();\n    if (point.IsZero()) return coefficients_[0];\n    return DoEvaluate(point);\n  }\n\n  // Return coefficients where the original coefficients reduce their degree\n  // by categorizing coefficients into even and odd degrees,\n  // multiplying coefficients with odd degrees by a specified random field |r|,\n  // and summing them together.\n  CONSTEXPR_IF_NOT_OPENMP UnivariateDenseCoefficients\n  Fold(const Field& r) const {\n    size_t size = coefficients_.size();\n    std::vector<F> coefficients((size + 1) >> 1);\n    base::ParallelizeFill(coefficients, F::Zero(),\n                          /*threshold=*/ParallelizeThreshold::kFieldInit);\n    OMP_PARALLEL_FOR(size_t i = 0; i < size; i += 2) {\n      coefficients[i >> 1] = coefficients_[i + 1] * r;\n      coefficients[i >> 1] += coefficients_[i];\n    }\n    if (size % 2 != 0) {\n      coefficients[size >> 1] = coefficients_[size - 1];\n    }\n    return UnivariateDenseCoefficients(std::move(coefficients), true);\n  }\n\n  std::string ToString() const {\n    if (IsZero()) return base::EmptyString();\n    size_t len = coefficients_.size() - 1;\n    std::stringstream ss;\n    bool has_coeff = false;\n    for (const F& coeff : base::Reversed(coefficients_)) {\n      size_t i = len--;\n      if (!coeff.IsZero()) {\n        if (has_coeff) ss << \" + \";\n        has_coeff = true;\n        ss << coeff.ToString();\n        if (i == 0) {\n          // do nothing\n        } else if (i == 1) {\n          ss << \" * x\";\n        } else {\n          ss << \" * x^\" << i;\n        }\n      }\n    }\n    return ss.str();\n  }\n\n  void RemoveHighDegreeZeros() {\n    while (!IsZero()) {\n      if (coefficients_.back().IsZero()) {\n        coefficients_.pop_back();\n      } else {\n        break;\n      }\n    }\n  }\n\n  constexpr bool IsClean() const {\n    return coefficients_.size() == 0 || !coefficients_.back().IsZero();\n  }\n\n private:\n  friend class internal::UnivariatePolynomialOp<\n      UnivariateDenseCoefficients<F, MaxDegree>>;\n  friend class internal::UnivariatePolynomialOp<\n      UnivariateSparseCoefficients<F, MaxDegree>>;\n  friend class UnivariatePolynomial<UnivariateDenseCoefficients<F, MaxDegree>>;\n  friend class UnivariateEvaluationDomain<F, MaxDegree>;\n  friend class Radix2EvaluationDomain<F, MaxDegree>;\n  friend class MixedRadixEvaluationDomain<F, MaxDegree>;\n  friend class base::Copyable<UnivariateDenseCoefficients<F, MaxDegree>>;\n\n  // NOTE(chokobole): This doesn't call |RemoveHighDegreeZeros()| internally.\n  // So when the returned instance of |UnivariateDenseCoefficients| is called\n  // with |IsZero()|, it returns false. So please use it carefully!\n  constexpr static UnivariateDenseCoefficients Zero(size_t degree) {\n    UnivariateDenseCoefficients ret;\n    ret.coefficients_.resize(degree + 1);\n    base::ParallelizeFill(ret.coefficients_, F::Zero(),\n                          /*threshold=*/ParallelizeThreshold::kFieldInit);\n    return ret;\n  }\n\n  constexpr F DoEvaluate(const Point& point) const {\n    // Horner's method - parallel method\n    // run Horner's method on each thread as follows:\n    // 1) Split up the coefficients across each thread evenly.\n    // 2) Do polynomial evaluation via Horner's method for the thread's\n    // coefficients\n    // 3) Scale the result point^{thread coefficient start index}\n    // Then obtain the final polynomial evaluation by summing each threads\n    // result.\n    std::vector<F> results = base::ParallelizeMap(\n        coefficients_, [&point](absl::Span<const F> chunk, size_t chunk_offset,\n                                size_t chunk_size) {\n          return HornerEvaluate(chunk, point) *\n                 point.Pow(chunk_offset * chunk_size);\n        });\n    return std::accumulate(results.begin(), results.end(), F::Zero(),\n                           std::plus<>());\n  }\n\n  constexpr static F HornerEvaluate(absl::Span<const F> coefficients,\n                                    const Point& point) {\n    return std::accumulate(coefficients.rbegin(), coefficients.rend(),\n                           F::Zero(), [&point](F& result, const F& coeff) {\n                             result *= point;\n                             return result += coeff;\n                           });\n  }\n\n  std::vector<F> coefficients_;\n};\n\ntemplate <typename H, typename F, size_t MaxDegree>\nH AbslHashValue(H h,\n                const UnivariateDenseCoefficients<F, MaxDegree>& coefficients) {\n  // NOTE(chokobole): We shouldn't hash only with a non-zero term.\n  // See https://abseil.io/docs/cpp/guides/hash#the-abslhashvalue-overload\n  size_t degree = 0;\n  for (const F& coefficient : coefficients.coefficients()) {\n    h = H::combine(std::move(h), coefficient);\n    ++degree;\n  }\n  F zero = F::Zero();\n  for (size_t i = degree; i < MaxDegree + 1; ++i) {\n    h = H::combine(std::move(h), zero);\n  }\n  return h;\n}\n\n}  // namespace math\n\nnamespace base {\n\ntemplate <typename F, size_t MaxDegree>\nclass Copyable<math::UnivariateDenseCoefficients<F, MaxDegree>> {\n public:\n  static bool WriteTo(\n      const math::UnivariateDenseCoefficients<F, MaxDegree>& coeffs,\n      Buffer* buffer) {\n    return buffer->Write(coeffs.coefficients_);\n  }\n\n  static bool ReadFrom(\n      const ReadOnlyBuffer& buffer,\n      math::UnivariateDenseCoefficients<F, MaxDegree>* coeffs) {\n    std::vector<F> raw_coeff;\n    if (!buffer.Read(&raw_coeff)) return false;\n    *coeffs =\n        math::UnivariateDenseCoefficients<F, MaxDegree>(std::move(raw_coeff));\n    return true;\n  }\n\n  static size_t EstimateSize(\n      const math::UnivariateDenseCoefficients<F, MaxDegree>& coeffs) {\n    return base::EstimateSize(coeffs.coefficients_);\n  }\n};\n\ntemplate <typename F, size_t MaxDegree>\nclass RapidJsonValueConverter<math::UnivariateDenseCoefficients<F, MaxDegree>> {\n public:\n  template <typename Allocator>\n  static rapidjson::Value From(\n      const math::UnivariateDenseCoefficients<F, MaxDegree>& value,\n      Allocator& allocator) {\n    rapidjson::Value object(rapidjson::kObjectType);\n    AddJsonElement(object, \"coefficients\", value.coefficients(), allocator);\n    return object;\n  }\n\n  static bool To(const rapidjson::Value& json_value, std::string_view key,\n                 math::UnivariateDenseCoefficients<F, MaxDegree>* value,\n                 std::string* error) {\n    std::vector<F> coeffs;\n    if (!ParseJsonElement(json_value, \"coefficients\", &coeffs, error))\n      return false;\n    *value = math::UnivariateDenseCoefficients<F, MaxDegree>(std::move(coeffs));\n    return true;\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_MATH_POLYNOMIALS_UNIVARIATE_UNIVARIATE_DENSE_COEFFICIENTS_H_\n"
  },
  {
    "path": "tachyon/math/polynomials/univariate/univariate_dense_polynomial_unittest.cc",
    "content": "#include <tuple>\n\n#include \"absl/hash/hash_testing.h\"\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/buffer/vector_buffer.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_polynomial.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\nconst size_t kMaxDegree = 5;\n\nusing Poly = UnivariateDensePolynomial<GF7, kMaxDegree>;\nusing Coeffs = UnivariateDenseCoefficients<GF7, kMaxDegree>;\n\nclass UnivariateDensePolynomialTest : public FiniteFieldTest<GF7> {\n public:\n  void SetUp() override {\n    polys_.push_back(Poly(Coeffs({GF7(3), GF7(0), GF7(1), GF7(0), GF7(2)})));\n    polys_.push_back(Poly(Coeffs({GF7(3)})));\n    polys_.push_back(Poly(Coeffs({GF7(0), GF7(0), GF7(0), GF7(5)})));\n    polys_.push_back(Poly(Coeffs({GF7(0), GF7(0), GF7(0), GF7(0), GF7(5)})));\n    polys_.push_back(Poly::Zero());\n  }\n\n protected:\n  std::vector<Poly> polys_;\n};\n\n}  // namespace\n\nTEST_F(UnivariateDensePolynomialTest, IsZero) {\n  EXPECT_TRUE(Poly().IsZero());\n  EXPECT_TRUE(Poly::Zero().IsZero());\n  EXPECT_TRUE(Poly(Coeffs({GF7(0), GF7(0)}, true)).IsZero());\n  for (size_t i = 0; i < polys_.size() - 1; ++i) {\n    EXPECT_FALSE(polys_[i].IsZero());\n  }\n  EXPECT_TRUE(polys_[polys_.size() - 1].IsZero());\n}\n\nTEST_F(UnivariateDensePolynomialTest, IsOne) {\n  EXPECT_TRUE(Poly::One().IsOne());\n  EXPECT_TRUE(Poly(Coeffs({GF7(1)})).IsOne());\n  for (size_t i = 0; i < polys_.size(); ++i) {\n    EXPECT_FALSE(polys_[i].IsOne());\n  }\n}\n\nTEST_F(UnivariateDensePolynomialTest, Random) {\n  bool success = false;\n  Poly r = Poly::Random(kMaxDegree);\n  for (size_t i = 0; i < 100; ++i) {\n    if (r != Poly::Random(kMaxDegree)) {\n      success = true;\n      break;\n    }\n  }\n  EXPECT_TRUE(success);\n}\n\nTEST_F(UnivariateDensePolynomialTest, IndexingOperator) {\n  struct {\n    const Poly& poly;\n    std::vector<int> coefficients;\n  } tests[] = {\n      {polys_[0], {3, 0, 1, 0, 2}}, {polys_[1], {3}}, {polys_[2], {0, 0, 0, 5}},\n      {polys_[3], {0, 0, 0, 0, 5}}, {polys_[4], {}},\n  };\n\n  for (const auto& test : tests) {\n    for (size_t i = 0; i < kMaxDegree; ++i) {\n      if (i < test.coefficients.size()) {\n        EXPECT_EQ(test.poly[i], GF7(test.coefficients[i]));\n      } else {\n        EXPECT_EQ(test.poly[i], GF7::Zero());\n      }\n    }\n  }\n}\n\nTEST_F(UnivariateDensePolynomialTest, Degree) {\n  struct {\n    const Poly& poly;\n    size_t degree;\n  } tests[] = {\n      {polys_[0], 4}, {polys_[1], 0}, {polys_[2], 3},\n      {polys_[3], 4}, {polys_[4], 0},\n  };\n\n  for (const auto& test : tests) {\n    EXPECT_EQ(test.poly.Degree(), test.degree);\n  }\n  EXPECT_LE(Poly::Random(kMaxDegree).Degree(), kMaxDegree);\n}\n\nTEST_F(UnivariateDensePolynomialTest, Evaluate) {\n  struct {\n    const Poly& poly;\n    GF7 expected;\n  } tests[] = {\n      {polys_[0], GF7(6)}, {polys_[1], GF7(3)}, {polys_[2], GF7(2)},\n      {polys_[3], GF7(6)}, {polys_[4], GF7(0)},\n  };\n\n  for (const auto& test : tests) {\n    EXPECT_EQ(test.poly.Evaluate(GF7(3)), test.expected);\n  }\n}\n\nTEST_F(UnivariateDensePolynomialTest, ToString) {\n  struct {\n    const Poly& poly;\n    std::string_view expected;\n  } tests[] = {\n      {polys_[0], \"2 * x^4 + 1 * x^2 + 3\"},\n      {polys_[1], \"3\"},\n      {polys_[2], \"5 * x^3\"},\n      {polys_[3], \"5 * x^4\"},\n      {polys_[4], \"\"},\n  };\n\n  for (const auto& test : tests) {\n    EXPECT_EQ(test.poly.ToString(), test.expected);\n  }\n}\n\nTEST_F(UnivariateDensePolynomialTest, AdditiveOperators) {\n  struct {\n    const Poly& a;\n    const Poly& b;\n    Poly sum;\n    Poly amb;\n    Poly bma;\n  } tests[] = {\n      {\n          polys_[0],\n          polys_[1],\n          Poly(Coeffs({GF7(6), GF7(0), GF7(1), GF7(0), GF7(2)})),\n          Poly(Coeffs({GF7(0), GF7(0), GF7(1), GF7(0), GF7(2)})),\n          Poly(Coeffs({GF7(0), GF7(0), GF7(6), GF7(0), GF7(5)})),\n      },\n      {\n          polys_[0],\n          polys_[2],\n          Poly(Coeffs({GF7(3), GF7(0), GF7(1), GF7(5), GF7(2)})),\n          Poly(Coeffs({GF7(3), GF7(0), GF7(1), GF7(2), GF7(2)})),\n          Poly(Coeffs({GF7(4), GF7(0), GF7(6), GF7(5), GF7(5)})),\n      },\n      {\n          polys_[0],\n          polys_[3],\n          Poly(Coeffs({GF7(3), GF7(0), GF7(1)})),\n          Poly(Coeffs({GF7(3), GF7(0), GF7(1), GF7(0), GF7(4)})),\n          Poly(Coeffs({GF7(4), GF7(0), GF7(6), GF7(0), GF7(3)})),\n      },\n      {\n          polys_[0],\n          polys_[4],\n          Poly(Coeffs({GF7(3), GF7(0), GF7(1), GF7(0), GF7(2)})),\n          Poly(Coeffs({GF7(3), GF7(0), GF7(1), GF7(0), GF7(2)})),\n          Poly(Coeffs({GF7(4), GF7(0), GF7(6), GF7(0), GF7(5)})),\n      },\n  };\n\n  for (const auto& test : tests) {\n    const auto a_sparse = test.a.ToSparse();\n    const auto b_sparse = test.b.ToSparse();\n    EXPECT_EQ(test.a + test.b, test.sum);\n    EXPECT_EQ(test.b + test.a, test.sum);\n    EXPECT_EQ(test.a + b_sparse, test.sum);\n    EXPECT_EQ(test.b + a_sparse, test.sum);\n    EXPECT_EQ(test.a - test.b, test.amb);\n    EXPECT_EQ(test.b - test.a, test.bma);\n    EXPECT_EQ(test.a - b_sparse, test.amb);\n    EXPECT_EQ(test.b - a_sparse, test.bma);\n\n    {\n      Poly tmp = test.a;\n      tmp += test.b;\n      EXPECT_EQ(tmp, test.sum);\n      tmp -= test.b;\n      EXPECT_EQ(tmp, test.a);\n    }\n    {\n      Poly tmp = test.a;\n      tmp += b_sparse;\n      EXPECT_EQ(tmp, test.sum);\n      tmp -= b_sparse;\n      EXPECT_EQ(tmp, test.a);\n    }\n  }\n}\n\nTEST_F(UnivariateDensePolynomialTest, MultiplicativeOperators) {\n  Poly a(Coeffs({GF7(3), GF7(1)}));\n  Poly b(Coeffs({GF7(5), GF7(2), GF7(5)}));\n  Poly one = Poly::One();\n  Poly zero = Poly::Zero();\n\n  struct {\n    const Poly& a;\n    const Poly& b;\n    Poly mul;\n    Poly adb;\n    Poly amb;\n    Poly bda;\n    Poly bma;\n  } tests[] = {\n      {\n          a,\n          b,\n          Poly(Coeffs({GF7(1), GF7(4), GF7(3), GF7(5)})),\n          zero,\n          a,\n          Poly(Coeffs({GF7(1), GF7(5)})),\n          Poly(Coeffs({GF7(2)})),\n      },\n      {\n          a,\n          one,\n          a,\n          a,\n          zero,\n          zero,\n          one,\n      },\n      {\n          a,\n          zero,\n          zero,\n          zero,\n          zero,\n          zero,\n          a,\n      },\n  };\n\n  for (const auto& test : tests) {\n    const auto a_sparse = test.a.ToSparse();\n    const auto b_sparse = test.b.ToSparse();\n    EXPECT_EQ(test.a * test.b, test.mul);\n    EXPECT_EQ(test.b * test.a, test.mul);\n    if (!test.b.IsZero()) {\n      EXPECT_EQ(test.a / test.b, test.adb);\n      EXPECT_EQ(test.a % test.b, test.amb);\n    } else {\n      ASSERT_FALSE(test.a / test.b);\n    }\n    if (!test.a.IsZero()) {\n      EXPECT_EQ(test.b / test.a, test.bda);\n      EXPECT_EQ(test.b % test.a, test.bma);\n    } else {\n      ASSERT_FALSE(test.b / test.a);\n    }\n    EXPECT_EQ(test.a * b_sparse, test.mul);\n    EXPECT_EQ(test.b * a_sparse, test.mul);\n    if (!b_sparse.IsZero()) {\n      EXPECT_EQ(test.a / b_sparse, test.adb);\n      EXPECT_EQ(test.a % b_sparse, test.amb);\n    } else {\n      ASSERT_FALSE(test.a / b_sparse);\n    }\n    if (!a_sparse.IsZero()) {\n      EXPECT_EQ(test.b / a_sparse, test.bda);\n      EXPECT_EQ(test.b % a_sparse, test.bma);\n    } else {\n      ASSERT_FALSE(test.b / a_sparse);\n    }\n\n    Poly tmp = test.a;\n    tmp *= test.b;\n    EXPECT_EQ(tmp, test.mul);\n    if (!test.b.IsZero()) {\n      ASSERT_TRUE(tmp /= test.b);\n      EXPECT_EQ(tmp, test.a);\n    } else {\n      ASSERT_FALSE(tmp /= test.b);\n    }\n  }\n}\n\nTEST_F(UnivariateDensePolynomialTest, MulScalar) {\n  Poly poly = Poly::Random(kMaxDegree);\n  GF7 scalar = GF7::Random();\n\n  std::vector<GF7> expected_coeffs;\n  const std::vector<GF7>& coeffs = poly.coefficients().coefficients();\n  expected_coeffs.reserve(coeffs.size());\n  for (size_t i = 0; i < coeffs.size(); ++i) {\n    expected_coeffs.push_back(coeffs[i] * scalar);\n  }\n\n  Poly actual = poly * scalar;\n  Poly expected(Coeffs(std::move(expected_coeffs), true));\n  EXPECT_EQ(actual, expected);\n  poly *= scalar;\n  EXPECT_EQ(poly, expected);\n}\n\nTEST_F(UnivariateDensePolynomialTest, DivScalar) {\n  Poly poly = Poly::Random(kMaxDegree);\n  GF7 scalar = GF7::Random();\n  while (scalar.IsZero()) {\n    scalar = GF7::Random();\n  }\n\n  std::vector<GF7> expected_coeffs;\n  const std::vector<GF7>& coeffs = poly.coefficients().coefficients();\n  expected_coeffs.reserve(coeffs.size());\n  for (size_t i = 0; i < coeffs.size(); ++i) {\n    expected_coeffs.push_back(unwrap(coeffs[i] / scalar));\n  }\n\n  Poly actual = unwrap(poly / scalar);\n  Poly expected(Coeffs(std::move(expected_coeffs)));\n  EXPECT_EQ(actual, expected);\n  ASSERT_TRUE(poly /= scalar);\n  EXPECT_EQ(poly, expected);\n}\n\nTEST_F(UnivariateDensePolynomialTest, FromRoots) {\n  // poly = x⁴ + 2x² + 4 = (x - 1)(x - 2)(x + 1)(x + 2)\n  Poly poly = Poly(Coeffs({GF7(4), GF7::Zero(), GF7(2), GF7::Zero(), GF7(1)}));\n  std::vector<GF7> roots = {GF7(1), GF7(2), GF7(6), GF7(5)};\n  EXPECT_EQ(Poly::FromRoots(roots), poly);\n}\n\nTEST_F(UnivariateDensePolynomialTest, EvaluateVanishingPolyByRoots) {\n  // poly = x⁴ + 2x² + 4 = (x - 1)(x - 2)(x + 1)(x + 2)\n  Poly poly = Poly(Coeffs({GF7(4), GF7::Zero(), GF7(2), GF7::Zero(), GF7(1)}));\n  std::vector<GF7> roots = {GF7(1), GF7(2), GF7(6), GF7(5)};\n  GF7 point = GF7::Random();\n  EXPECT_EQ(Poly::EvaluateVanishingPolyByRoots(roots, point),\n            poly.Evaluate(point));\n}\n\nTEST_F(UnivariateDensePolynomialTest, Fold) {\n  Poly poly = Poly::Random(kMaxDegree);\n  GF7 r = GF7::Random();\n  Poly folded = poly.Fold(r);\n  EXPECT_EQ(folded, Poly(Coeffs({poly[0] + r * poly[1], poly[2] + r * poly[3],\n                                 poly[4] + r * poly[5]},\n                                true)));\n\n  GF7 r2 = GF7::Random();\n  Poly folded2 = folded.Fold(r2);\n  EXPECT_EQ(folded2,\n            Poly(Coeffs({folded[0] + r2 * folded[1], folded[2]}, true)));\n\n  GF7 r3 = GF7::Random();\n  Poly folded3 = folded2.Fold(r3);\n  EXPECT_EQ(folded3, Poly(Coeffs({folded2[0] + r3 * folded2[1]}, true)));\n}\n\nTEST_F(UnivariateDensePolynomialTest, Copyable) {\n  Poly expected = Poly::Random(kMaxDegree);\n\n  base::Uint8VectorBuffer write_buf;\n  ASSERT_TRUE(write_buf.Grow(base::EstimateSize(expected)));\n  ASSERT_TRUE(write_buf.Write(expected));\n  ASSERT_TRUE(write_buf.Done());\n\n  write_buf.set_buffer_offset(0);\n\n  Poly value;\n  ASSERT_TRUE(write_buf.Read(&value));\n  EXPECT_EQ(expected, value);\n}\n\nTEST_F(UnivariateDensePolynomialTest, Hash) {\n  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(\n      std::make_tuple(Poly(), Poly::Zero(), Poly::One(),\n                      Poly::Random(kMaxDegree), Poly::Random(kMaxDegree))));\n}\n\nTEST_F(UnivariateDensePolynomialTest, JsonValueConverter) {\n  Poly expected_poly(Coeffs({GF7(1), GF7(2), GF7(3), GF7(4), GF7(5)}));\n  std::string expected_json =\n      R\"({\"coefficients\":{\"coefficients\":[1,2,3,4,5]}})\";\n\n  Poly poly;\n  std::string error;\n  ASSERT_TRUE(base::ParseJson(expected_json, &poly, &error));\n  ASSERT_TRUE(error.empty());\n  EXPECT_EQ(poly, expected_poly);\n\n  std::string json = base::WriteToJson(poly);\n  EXPECT_EQ(json, expected_json);\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/polynomials/univariate/univariate_evaluation_domain.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_POLYNOMIALS_UNIVARIATE_UNIVARIATE_EVALUATION_DOMAIN_H_\n#define TACHYON_MATH_POLYNOMIALS_UNIVARIATE_UNIVARIATE_EVALUATION_DOMAIN_H_\n\n#include <algorithm>\n#include <memory>\n#include <optional>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/optional.h\"\n#include \"tachyon/base/parallelize.h\"\n#include \"tachyon/base/profiler.h\"\n#include \"tachyon/base/range.h\"\n#include \"tachyon/math/polynomials/evaluation_domain.h\"\n#include \"tachyon/math/polynomials/univariate/evaluations_utils.h\"\n#include \"tachyon/math/polynomials/univariate/fft_algorithm.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluation_domain_forwards.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluations.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_polynomial.h\"\n\n#if TACHYON_CUDA\n#include \"tachyon/math/polynomials/univariate/icicle/fft_algorithm_conversion.h\"\n#include \"tachyon/math/polynomials/univariate/icicle/icicle_ntt_holder.h\"\n#endif\n\nnamespace tachyon::math {\n\ntemplate <typename F, size_t MaxDegree>\nclass UnivariateEvaluationDomainFactory;\n\ntemplate <typename F, size_t MaxDegree>\nclass UnivariateEvaluationDomain : public EvaluationDomain<F, MaxDegree> {\n public:\n  static_assert(F::HasRootOfUnity(),\n                \"UnivariateEvaluationDomain should have root of unity\");\n\n  using Field = F;\n  using Evals = UnivariateEvaluations<F, MaxDegree>;\n  using DensePoly = UnivariateDensePolynomial<F, MaxDegree>;\n  using DenseCoeffs = UnivariateDenseCoefficients<F, MaxDegree>;\n  using SparseCoeffs = UnivariateSparseCoefficients<F, MaxDegree>;\n  using SparsePoly = UnivariateSparsePolynomial<F, MaxDegree>;\n  using BigInt = typename F::BigIntTy;\n\n  constexpr static size_t kMaxDegree = MaxDegree;\n\n  constexpr UnivariateEvaluationDomain() = default;\n\n  constexpr UnivariateEvaluationDomain(size_t size, uint32_t log_size_of_group)\n      : size_(size), log_size_of_group_(log_size_of_group) {\n    size_as_field_element_ = F::FromBigInt(BigInt(size_));\n    size_inv_ = unwrap(size_as_field_element_.Inverse());\n\n    // Compute the generator for the multiplicative subgroup.\n    // It should be the |size_|-th root of unity.\n    CHECK(F::GetRootOfUnity(size_, &group_gen_));\n    // Check that it is indeed the |size_|-th root of unity.\n    DCHECK_EQ(group_gen_.Pow(size_), F::One());\n    group_gen_inv_ = unwrap(group_gen_.Inverse());\n#if TACHYON_CUDA\n    offset_big_int_ = F::One().ToBigInt();\n#endif\n  }\n\n  virtual ~UnivariateEvaluationDomain() = default;\n\n  constexpr static std::unique_ptr<UnivariateEvaluationDomain> Create(\n      size_t num_coeffs) {\n    return UnivariateEvaluationDomainFactory<F, MaxDegree>::Create(num_coeffs);\n  }\n\n  constexpr size_t size() const { return size_; }\n\n  constexpr uint32_t log_size_of_group() const { return log_size_of_group_; }\n\n  constexpr const F& size_as_field_element() const {\n    return size_as_field_element_;\n  }\n\n  constexpr const F& size_inv() const { return size_inv_; }\n\n  constexpr const F& group_gen() const { return group_gen_; }\n\n  constexpr const F& group_gen_inv() const { return group_gen_inv_; }\n\n  constexpr const F& offset() const { return offset_; }\n\n  constexpr const F& offset_inv() const { return offset_inv_; }\n\n  constexpr const F& offset_pow_size() const { return offset_pow_size_; }\n\n#if TACHYON_CUDA\n  void set_icicle(IcicleNTTHolder<F>* icicle) { icicle_ = icicle; }\n#endif\n\n  constexpr std::unique_ptr<UnivariateEvaluationDomain> GetCoset(\n      const F& offset) const {\n    std::unique_ptr<UnivariateEvaluationDomain> coset = Clone();\n    coset->offset_ = offset;\n    coset->offset_inv_ = unwrap(offset.Inverse());\n    coset->offset_pow_size_ = offset.Pow(size_);\n#if TACHYON_CUDA\n    // TODO(chokobole): Remove this condition.\n    if constexpr (IsIcicleNTTSupported<F>) {\n      if (icicle_) {\n        coset->offset_big_int_ = offset.ToBigInt();\n      }\n    }\n#endif\n    return coset;\n  }\n\n  // Sample an element that is *not* in the domain.\n  constexpr F SampleElementOutsideDomain() const {\n    F t = F::Random();\n    while (EvaluateVanishingPolynomial(t).IsZero()) {\n      t = F::Random();\n    }\n    return t;\n  }\n\n  template <typename T>\n  constexpr T Zero() const {\n    return T::Zero(size_ - 1);\n  }\n\n  template <typename T>\n  constexpr T Random() const {\n    return T::Random(size_ - 1);\n  }\n\n  virtual FFTAlgorithm GetAlgorithm() const = 0;\n\n  // Compute a FFT.\n  [[nodiscard]] constexpr Evals FFT(const DensePoly& poly) const {\n    TRACE_EVENT(\"EvaluationDomain\", \"UnivariateEvaluationDomain::FFT\");\n    if (poly.IsZero()) return {};\n\n    Evals evals;\n    evals.evaluations_ = poly.coefficients_.coefficients_;\n#if TACHYON_CUDA\n    // TODO(chokobole): Remove this condition.\n    if constexpr (IsIcicleNTTSupported<F>) {\n      if (icicle_) {\n        evals.evaluations_.resize(size_);\n        CHECK((*icicle_)->FFT(FFTAlgorithmToIcicleNTTAlgorithm(GetAlgorithm()),\n                              offset_big_int_, evals));\n        return evals;\n      }\n    }\n#endif\n    DoFFT(evals);\n    return evals;\n  }\n\n  // Compute a FFT.\n  [[nodiscard]] constexpr Evals FFT(DensePoly&& poly) const {\n    TRACE_EVENT(\"EvaluationDomain\", \"UnivariateEvaluationDomain::FFT\");\n    if (poly.IsZero()) return {};\n\n    Evals evals;\n    evals.evaluations_ = std::move(poly.coefficients_.coefficients_);\n#if TACHYON_CUDA\n    // TODO(chokobole): Remove this condition.\n    if constexpr (IsIcicleNTTSupported<F>) {\n      if (icicle_) {\n        evals.evaluations_.resize(size_);\n        CHECK((*icicle_)->FFT(FFTAlgorithmToIcicleNTTAlgorithm(GetAlgorithm()),\n                              offset_big_int_, evals));\n        return evals;\n      }\n    }\n#endif\n    DoFFT(evals);\n    return evals;\n  }\n\n  constexpr virtual void DoFFT(Evals& evals) const = 0;\n\n  // Compute an IFFT.\n  [[nodiscard]] constexpr DensePoly IFFT(const Evals& evals) const {\n    TRACE_EVENT(\"EvaluationDomain\", \"UnivariateEvaluationDomain::IFFT\");\n    // NOTE(chokobole): |evals.IsZero()| can be super slow!\n    // See https://github.com/kroma-network/tachyon/pull/104.\n    if (evals.evaluations_.empty()) return {};\n\n    DensePoly poly;\n    poly.coefficients_.coefficients_ = evals.evaluations_;\n#if TACHYON_CUDA\n    // TODO(chokobole): Remove this condition.\n    if constexpr (IsIcicleNTTSupported<F>) {\n      if (icicle_) {\n        poly.coefficients_.coefficients_.resize(size_);\n        CHECK((*icicle_)->IFFT(FFTAlgorithmToIcicleNTTAlgorithm(GetAlgorithm()),\n                               offset_big_int_, poly));\n        return poly;\n      }\n    }\n#endif\n    DoIFFT(poly);\n    return poly;\n  }\n\n  // Compute an IFFT.\n  [[nodiscard]] constexpr DensePoly IFFT(Evals&& evals) const {\n    TRACE_EVENT(\"EvaluationDomain\", \"UnivariateEvaluationDomain::IFFT\");\n    // NOTE(chokobole): |evals.IsZero()| can be super slow!\n    // See https://github.com/kroma-network/tachyon/pull/104.\n    if (evals.evaluations_.empty()) return {};\n\n    DensePoly poly;\n    poly.coefficients_.coefficients_ = std::move(evals.evaluations_);\n#if TACHYON_CUDA\n    // TODO(chokobole): Remove this condition.\n    if constexpr (IsIcicleNTTSupported<F>) {\n      if (icicle_) {\n        poly.coefficients_.coefficients_.resize(size_);\n        CHECK((*icicle_)->IFFT(FFTAlgorithmToIcicleNTTAlgorithm(GetAlgorithm()),\n                               offset_big_int_, poly));\n        return poly;\n      }\n    }\n#endif\n    DoIFFT(poly);\n    return poly;\n  }\n\n  constexpr virtual void DoIFFT(DensePoly& poly) const = 0;\n\n  // Computes the first |size| roots of unity for the entire domain.\n  // e.g. for the domain [1, g, g², ..., gⁿ⁻¹}] and |size| = n / 2, it\n  // computes [1, g, g², ..., g^{(n / 2) - 1}]\n  constexpr static std::vector<F> GetRootsOfUnity(size_t size, const F& root) {\n    return F::GetSuccessivePowers(size, root);\n  }\n\n  // Define the following as\n  // - H: The coset we are in, with generator g and offset h\n  // - m: The size of the coset H\n  // - Z_H: The vanishing polynomial for H.\n  //        Z_H(x) = Π{i in m} (x - h * gⁱ) = xᵐ - hᵐ\n  // - vᵢ: A sequence of values, where v₀ = 1 / (m * hᵐ⁻¹), and\n  //       vᵢ₊₁ = g * vᵢ\n  //\n  // clang-format off\n  //       Proof)\n  //\n  //       vᵢ = 1 / (h * gⁱ - h * g⁰) * ... * (h * gⁱ - h * gⁱ⁻¹) * (h * gⁱ - h * gⁱ⁺¹) * ... * (h * gⁱ - h * gᵐ⁻²) * (h * gⁱ - h * gᵐ⁻¹)\n  //          = gᵐ⁻¹ / (h * gⁱ⁺¹ - h * g¹) * ... * (h * gⁱ⁺¹ - h * gⁱ) * (h * gⁱ⁺¹ - h * gⁱ⁺²) * ... * (h * gⁱ⁺¹ - h * gᵐ⁻¹) * (h * gⁱ⁺¹ - h * gᵐ)\n  //          = gᵐ⁻¹ / (h * gⁱ⁺¹ - h * g⁰) * (h * gⁱ⁺¹ - h * g¹) * ... * (h * gⁱ⁺¹ - h * gⁱ) * (h * gⁱ⁺¹ - h * gⁱ⁺²) * ... * (h * gⁱ⁺¹ - h * gᵐ⁻¹)\n  //          = gᵐ⁻¹ * vᵢ₊₁\n  //          = 1 / g * vᵢ₊₁\n  //\n  //       v₀ = 1 / ((h - h * g¹) * (h - h * g²) * .... * (h - h * g^((m / 2) - 2)) * (h - h * g^((m / 2) - 1)) * (h - h * g^(m / 2)) * (h - h * g^((m / 2) + 1)) * (h - h * g^((m / 2) + 2))... * (h - h * gᵐ⁻²) * (h - h * gᵐ⁻¹))\n  //          = 1 / (hᵐ⁻¹ * (1 - g¹) * (1 - g²) * .... * (1 - g^((m / 2) - 2)) * (1 - g^((m / 2) - 1)) * (1 - g^(m / 2)) * (1 - g^((m / 2) + 1)) * (1 - g^((m / 2) + 2))... * (1 - gᵐ⁻²) * (1 - gᵐ⁻¹))\n  //          = 1 / (hᵐ⁻¹ * (1 - g¹) * (1 - g²) * .... * (1 - g^((m / 2) - 2)) * (1 - g^((m / 2) - 1)) * (1 + 1) * ... * (1 + g¹) * (1 + g²) * ... * (1 + g^((m / 2) - 2)) * (1 + g^((m / 2) - 1))) <- g^(m / 2) = -1\n  //          = 1 / (2 * hᵐ⁻¹ * (1 - g¹) * (1 + g¹) * (1 - g²) * (1 + g²) * .... * (1 - g^((m / 2) - 1)) * (1 + g^((m / 2) - 1)))\n  //          = 1 / (2 * hᵐ⁻¹ * (1 - g²) * (1 - g⁴) * .... * (1 - gᵐ⁻²))\n  //          = 1 / (4 * hᵐ⁻¹ * (1 - g⁴) * (1 - g⁸) * .... * (1 - gᵐ⁻³))\n  //          = 1 / (8 * hᵐ⁻¹ * (1 - g⁸) * (1 - g¹⁶) * .... * (1 - gᵐ⁻⁴))\n  //          ...\n  //          = 1 / (m * hᵐ⁻¹)\n  //\n  // clang-format on\n  //       See Barycentric Weight for more details.\n  //       https://people.maths.ox.ac.uk/trefethen/barycentric.pdf\n  // - Lᵢ_H: The value of i-th lagrange coefficient for H\n  //\n  // Evaluate all the lagrange polynomials defined by H at the point τ. This\n  // is computed in time O(m). Then given the evaluations of a degree d\n  // polynomial P over H, where d < m, P(τ) can be computed as P(τ) =\n  // Σ{i in m} Lᵢ_H(τ) * P(gⁱ).\n  constexpr std::vector<F> EvaluateAllLagrangeCoefficients(const F& tau) const {\n    return EvaluatePartialLagrangeCoefficients(\n        tau, base::Range<size_t>::Until(size_));\n  }\n\n  // Almost same with above, but it only computes parts of the lagrange\n  // coefficients defined by |range|.\n  // TODO(chokobole): If we want to accept IsStartInclusive as a template\n  // parameter, we need a way to get a starting index from |range|.\n  template <typename T, bool IsEndInclusive>\n  constexpr std::vector<F> EvaluatePartialLagrangeCoefficients(\n      const F& tau, base::Range<T, true, IsEndInclusive> range) const {\n    TRACE_EVENT(\n        \"EvaluationDomain\",\n        \"UnivariateEvaluationDomain::EvaluatePartialLagranceCoefficients\");\n\n    size_t size = range.GetSize();\n    CHECK_LE(size, size_);\n    if (size == 0) return {};\n\n    // Evaluate all Lagrange polynomials at τ to get the lagrange\n    // coefficients.\n    //\n    // We then compute Lᵢ_H(τ) as Lᵢ_H(τ) = Z_H(τ) * vᵢ / (τ - h * gⁱ)\n    //\n    // However, if τ is in H, both the numerator and denominator equal 0\n    // when i corresponds to the value τ equals, and the coefficient is 0\n    // everywhere else. We handle this case separately, and we can easily\n    // detect by checking if the vanishing poly evaluates to 0 at τ.\n    F z_h_at_tau = EvaluateVanishingPolynomial(tau);\n    if (z_h_at_tau.IsZero()) {\n      // In this case, we know that τ = h * gⁱ, for some value i.\n      // Then i-th lagrange coefficient in this case is then simply 1,\n      // and all other lagrange coefficients are 0.\n      // Thus we find i by brute force.\n      std::vector<F> u(size, F::Zero());\n      F omega_i = GetElement(range.from);\n      for (F& u_i : u) {\n        if (omega_i == tau) {\n          u_i = F::One();\n          break;\n        }\n        omega_i *= group_gen_;\n      }\n      return u;\n    } else {\n      // In this case we have to compute Z_H(τ) * vᵢ / (τ - h * gⁱ)\n      // for i in 0..|size_|. We actually compute this by computing\n      // (Z_H(τ) * vᵢ)⁻¹ * (τ - h * gⁱ) and then batch inverting to\n      // get the correct lagrange coefficients. We let\n      // lᵢ = (Z_H(τ) * vᵢ)⁻¹ and rᵢ = τ - h * gⁱ. Notice that\n      // since Z_H(τ) is i-independent, and vᵢ = g * vᵢ₋₁, it follows\n      // that lᵢ = g⁻¹ * lᵢ₋₁\n\n      // t = m * hᵐ = v₀⁻¹ * h\n      F t = size_as_field_element_ * offset_pow_size_;\n      F omega_i = GetElement(range.from);\n      // lᵢ = (Z_H(τ) * h * gᵢ)⁻¹ * t\n      //    = (Z_H(τ) * h * gᵢ * t⁻¹)⁻¹\n      //    = (Z_H(τ) * h * gᵢ * v₀⁻¹ * h⁻¹)⁻¹\n      //    = (Z_H(τ) * gᵢ * v₀)⁻¹\n      F l_i = unwrap((z_h_at_tau * omega_i).Inverse()) * t;\n      F negative_omega_i = -omega_i;\n      std::vector<F> lagrange_coefficients_inverse(size);\n      base::Parallelize(\n          lagrange_coefficients_inverse,\n          [this, &l_i, &tau, &negative_omega_i](\n              absl::Span<F> chunk, size_t chunk_idx, size_t chunk_size) {\n            size_t n = chunk_idx * chunk_size;\n            F l_i_pow = l_i * group_gen_inv_.Pow(n);\n            F negative_omega_i_pow = negative_omega_i * group_gen_.Pow(n);\n\n            // NOTE: It is not possible to have empty chunk so this is safe\n            for (size_t i = 0; i < chunk.size() - 1; ++i) {\n              // (Z_H(τ) * vᵢ)⁻¹ * (τ - h * gⁱ)\n              chunk[i] = l_i_pow * (tau + negative_omega_i_pow);\n              // lᵢ₊₁ = g⁻¹ * lᵢ\n              l_i_pow *= group_gen_inv_;\n              // (- h * gⁱ) * g\n              negative_omega_i_pow *= group_gen_;\n            }\n            chunk.back() = std::move(l_i_pow);\n            chunk.back() *= tau + negative_omega_i_pow;\n          });\n\n      // Invert |lagrange_coefficients_inverse| to get the actual\n      // coefficients, and return these Z_H(τ) * vᵢ / (τ - h * gⁱ)\n      CHECK(F::BatchInverseInPlace(lagrange_coefficients_inverse));\n      return lagrange_coefficients_inverse;\n    }\n  }\n\n  // Return the sparse vanishing polynomial.\n  constexpr SparsePoly GetVanishingPolynomial() const {\n    TRACE_EVENT(\"EvaluationDomain\",\n                \"UnivariateEvaluationDomain::GetVanishingPolynomial\");\n    // Z_H(x) = Π{i in m} (x - h * gⁱ) = xᵐ - hᵐ,\n    // where m = |size_| and hᵐ = |offset_pow_size_|.\n    return SparsePoly(\n        SparseCoeffs({{0, -offset_pow_size_}, {size_, F::One()}}));\n  }\n\n  // This evaluates the vanishing polynomial for this domain at tau.\n  // TODO(TomTaehoonKim): Consider precomputed exponentiation tables if we\n  // need this to be faster. (See\n  // https://github.com/arkworks-rs/algebra/blob/4152c41/poly/src/domain/mod.rs#L232-L233)\n  constexpr F EvaluateVanishingPolynomial(const F& tau) const {\n    TRACE_EVENT(\"EvaluationDomain\",\n                \"UnivariateEvaluationDomain::EvaluateVanishingPolynomial\");\n    // Z_H(τ) = Π{i in m} (τ - h * gⁱ) = τᵐ - hᵐ,\n    // where m = |size_| and hᵐ = |offset_pow_size_|.\n    return tau.Pow(size_) - offset_pow_size_;\n  }\n\n  // Return the filter polynomial of |*this| with respect to |subdomain|.\n  // Assumes that |subdomain| is contained within |*this|.\n  //\n  // Crashes if |subdomain| is not contained within |*this|.\n  constexpr DensePoly GetFilterPolynomial(\n      const UnivariateEvaluationDomain& subdomain) const {\n    TRACE_EVENT(\"EvaluationDomain\",\n                \"UnivariateEvaluationDomain::GetFilterPolynomial\");\n    SparsePoly domain_vanishing_poly =\n        GetVanishingPolynomial() *\n        SparsePoly(SparseCoeffs({{0, subdomain.size_as_field_element_ *\n                                         subdomain.offset_pow_size_}}));\n    SparsePoly subdomain_vanishing_poly =\n        subdomain.GetVanishingPolynomial() *\n        SparsePoly(SparseCoeffs({{0, size_as_field_element_}}));\n    std::optional<DivResult<DensePoly>> result =\n        domain_vanishing_poly.DivMod(subdomain_vanishing_poly);\n    CHECK(result);\n    CHECK(result->remainder.IsZero());\n    return result->quotient;\n  }\n\n  // This evaluates at |tau| the filter polynomial for |*this| with respect to\n  // |subdomain|.\n  constexpr std::optional<F> EvaluateFilterPolynomial(\n      const UnivariateEvaluationDomain& subdomain, const F& tau) const {\n    TRACE_EVENT(\"EvaluationDomain\",\n                \"UnivariateEvaluationDomain::EvaluateFilterPolynomial\");\n    F v_subdomain_of_tau = subdomain.EvaluateVanishingPolynomial(tau);\n    if (v_subdomain_of_tau.IsZero()) {\n      return F::One();\n    } else {\n      const std::optional<F> div =\n          EvaluateVanishingPolynomial(tau) /\n          (size_as_field_element_ * v_subdomain_of_tau);\n      if (LIKELY(div)) {\n        return subdomain.size_as_field_element_ * *div;\n      }\n      return std::nullopt;\n    }\n  }\n\n  // Returns the |i|-th element of the domain.\n  constexpr F GetElement(int64_t i) const {\n    F result;\n    if (i > 0) {\n      result = group_gen_.Pow(i);\n    } else {\n      result = group_gen_inv_.Pow(-i);\n    }\n    if (!offset_.IsOne()) {\n      result *= offset_;\n    }\n    return result;\n  }\n\n  // Returns all the elements of the domain.\n  CONSTEXPR_IF_NOT_OPENMP std::vector<F> GetElements() const {\n    return F::GetSuccessivePowers(size_, group_gen_, offset_);\n  }\n\n  // Multiply the i-th element of |poly_or_evals| with |g|ⁱ.\n  template <typename PolyOrEvals>\n  CONSTEXPR_IF_NOT_OPENMP static void DistributePowers(\n      PolyOrEvals& poly_or_evals, const F& g) {\n    DistributePowersAndMulByConst(poly_or_evals, g, F::One());\n  }\n\n protected:\n  // Multiply the i-th element of |poly_or_evals| with |c|*|g|ⁱ.\n  template <typename PolyOrEvals>\n  CONSTEXPR_IF_NOT_OPENMP static void DistributePowersAndMulByConst(\n      PolyOrEvals& poly_or_evals, const F& g, const F& c) {\n    TRACE_EVENT(\"EvaluationDomain\",\n                \"UnivariateEvaluationDomain::DistributePowersAndMulByConst\");\n    // Invariant: |pow| = |c|*|g|ⁱ at the i-th iteration of the loop\n    constexpr size_t kParallelFactor = 1024;\n#if defined(TACHYON_HAS_OPENMP)\n    size_t thread_nums = static_cast<size_t>(omp_get_max_threads());\n#else\n    size_t thread_nums = 1;\n#endif\n    size_t size = poly_or_evals.NumElements();\n    base::Parallelize(\n        size,\n        [&poly_or_evals, &g, &c](size_t len, size_t chunk_offset,\n                                 size_t chunk_size) {\n          TRACE_EVENT(\"Subtask\", \"MultiplyLoop\");\n          size_t begin = chunk_offset * chunk_size;\n          F pow = c * g.Pow(begin);\n          for (size_t i = begin; i < begin + len; ++i) {\n            poly_or_evals.at(i) *= pow;\n            pow *= g;\n          }\n        },\n        kParallelFactor * thread_nums);\n  }\n\n  // See https://en.wikipedia.org/wiki/Butterfly_diagram\n  // |lo| = |lo| + |hi|\n  // |hi| = (|lo| - |hi|) * |root|\n  // The simplest example would be\n  // clang-format off\n  // | f(ω⁰) | = | 1  0  1  0  | * | 1  0  1  0  | * | c₀ |\n  // | f(ω²) |   | ω⁰ 0 -ω⁰ 0  |   | ω⁰ 0 -ω⁰ 0  |   | c₁ |\n  // | f(ω¹) |   | 0  1  0  1  |   | 0  1  0  1  |   | c₂ |\n  // | f(ω³) |   | 0  ω⁰ 0 -ω⁰ |   | 0  ω¹ 0 -ω¹ |   | c₃ |\n  //           = | 1  0  1  0  | * | c₀      + c₂      |\n  //             | ω⁰ 0 -ω⁰ 0  |   | c₀ * ω⁰ - c₂ * ω⁰ |\n  //             | 0  1  0  1  |   | c₁      + c₃      |\n  //             | 0  ω⁰ 0 -ω⁰ |   | c₁ * ω¹ - c₃ * ω¹ |\n  //           = | c₀ + c₂                 + c₁ + c₃                  |\n  //             | ω⁰ * (c₀ + c₂)          - ω⁰ * (c₁ + c₃)           |\n  //             | c₀ * ω⁰ - c₂ * ω⁰       + c₁ * ω¹ - c₃ * ω¹        |\n  //             | ω⁰ * (c₀ * ω⁰ - c₂ * ω⁰) -ω⁰ * (c₁ * ω¹ - c₃ * ω¹) |\n  //           = | c₀ * ω⁰ + c₁ * ω⁰ + c₂ * ω⁰ + c₃ * ω⁰ |\n  //             | c₀ * ω⁰ - c₁ * ω⁰ + c₂ * ω⁰ - c₃ * ω⁰ |\n  //             | c₀ * ω⁰ + c₁ * ω¹ - c₂ * ω⁰ - c₃ * ω¹ |\n  //             | c₀ * ω⁰ - c₁ * ω¹ - c₂ * ω⁰ + c₃ * ω¹ |\n  //           = | c₀ * ω⁰ + c₁ * ω⁰ + c₂ * ω⁰ + c₃ * ω⁰ |\n  //             | c₀ * ω⁰ + c₁ * ω² + c₂ * ω⁴ + c₃ * ω⁶ |\n  //             | c₀ * ω⁰ + c₁ * ω¹ + c₂ * ω² + c₃ * ω³ |\n  //             | c₀ * ω⁰ + c₁ * ω³ + c₂ * ω⁶ + c₃ * ω⁹ |\n  // Note that the coefficients are in order and evaluations are out of order(should be swapped after).\n  // clang-format on\n  constexpr static void ButterflyFnInOut(F& lo, F& hi, const F& root) {\n    F neg = lo - hi;\n\n    lo += hi;\n\n    hi = neg * root;\n  }\n\n  // See https://en.wikipedia.org/wiki/Butterfly_diagram\n  // |lo| = |lo| + |hi| * |root|\n  // |hi| = |lo| - |hi| * |root|\n  // The simplest example would be\n  // clang-format off\n  // | f(ω⁰) | = | 1 0  ω⁰ 0 | * | 1  ω⁰ 0  0  | * | c₀ |\n  // | f(ω¹) |   | 0 1  0  ω¹|   | 1 -ω⁰ 0  0  |   | c₂ |\n  // | f(ω²) |   | 1 0 -ω⁰ 0 |   | 0  0  1  ω⁰ |   | c₁ |\n  // | f(ω³) |   | 0 1  0 -ω¹|   | 0  0  1 -ω⁰ |   | c₃ |\n  //           = | 1 0  ω⁰ 0 | * | c₀ + c₂ * ω⁰ |\n  //             | 0 1  0  ω¹|   | c₀ - c₂ * ω⁰ |\n  //             | 1 0 -ω⁰ 0 |   | c₁ + c₃ * ω⁰ |\n  //             | 0 1  0 -ω¹|   | c₁ - c₃ * ω⁰ |\n  //           = | 1 0  ω⁰ 0 | * | c₀ + c₂      |\n  //             | 0 1  0  ω¹|   | c₀ + c₂ * ω² | ω² = -1, because ω⁴ = 1 => (ω² - 1)(ω² + 1)  = 0 => ω² = -1\n  //             | 1 0 -ω⁰ 0 |   | c₁ + c₃      | Since ω is 4-th root of unity, ω² can't be 1)\n  //             | 0 1  0 -ω¹|   | c₁ + c₃ * ω² |\n  //           = | c₀ + c₂      + ω⁰ * (c₁ + c₃)      |\n  //             | c₀ + c₂ * ω² + ω¹ * (c₁ + c₃ * ω²) |\n  //             | c₀ + c₂      - ω⁰ * (c₁ + c₃)      |\n  //             | c₀ + c₂ * ω² - ω¹ * (c₁ + c₃ * ω²) |\n  //           = | c₀ * ω⁰ + c₁ * ω⁰ + c₂ * ω⁰ + c₃ * ω⁰ |\n  //             | c₀ * ω⁰ + c₁ * ω¹ + c₂ * ω² + c₃ * ω³ |\n  //             | c₀ * ω⁰ - c₁ * ω⁰ + c₂ * ω⁰ - c₃ * ω⁰ |\n  //             | c₀ * ω⁰ - c₁ * ω¹ + c₂ * ω² - c₃ * ω³ |\n  //           = | c₀ * ω⁰ + c₁ * ω⁰ + c₂ * ω⁰ + c₃ * ω⁰ |\n  //             | c₀ * ω⁰ + c₁ * ω¹ + c₂ * ω² + c₃ * ω³ |\n  //             | c₀ * ω⁰ + c₁ * ω² + c₂ * ω⁴ + c₃ * ω⁶ |\n  //             | c₀ * ω⁰ + c₁ * ω³ + c₂ * ω⁶ + c₃ * ω⁹ |\n  // Note that the coefficients are out of order the evaluations are in order(should be swapped before).\n  // clang-format on\n  template <typename FTy>\n  constexpr static void ButterflyFnOutIn(FTy& lo, FTy& hi, const FTy& root) {\n    hi *= root;\n\n    FTy neg = lo - hi;\n\n    lo += hi;\n\n    hi = std::move(neg);\n  }\n\n  constexpr virtual std::unique_ptr<UnivariateEvaluationDomain> Clone()\n      const = 0;\n\n  // The size of the domain.\n  size_t size_ = 0;\n  // log2(|size_|).\n  uint32_t log_size_of_group_ = 0;\n  // Size of the domain as a field element.\n  F size_as_field_element_;\n  // Inverse of the size in the field.\n  F size_inv_;\n  // A generator of the subgroup.\n  F group_gen_;\n  // Inverse of the generator of the subgroup.\n  F group_gen_inv_;\n  // Offset that specifies the coset.\n  F offset_ = F::One();\n  // Inverse of the offset that specifies the coset.\n  F offset_inv_ = F::One();\n  // Constant coefficient for the vanishing polynomial.\n  // Equals |offset_|^|size_|.\n  F offset_pow_size_ = F::One();\n#if TACHYON_CUDA\n  // not owned\n  const IcicleNTTHolder<F>* icicle_ = nullptr;\n  BigInt offset_big_int_ = BigInt::One();\n#endif\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_POLYNOMIALS_UNIVARIATE_UNIVARIATE_EVALUATION_DOMAIN_H_\n"
  },
  {
    "path": "tachyon/math/polynomials/univariate/univariate_evaluation_domain_factory.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_POLYNOMIALS_UNIVARIATE_UNIVARIATE_EVALUATION_DOMAIN_FACTORY_H_\n#define TACHYON_MATH_POLYNOMIALS_UNIVARIATE_UNIVARIATE_EVALUATION_DOMAIN_FACTORY_H_\n\n#include <memory>\n\n#include \"tachyon/math/polynomials/univariate/mixed_radix_evaluation_domain.h\"\n#include \"tachyon/math/polynomials/univariate/radix2_evaluation_domain.h\"\n\nnamespace tachyon::math {\n\ntemplate <typename F>\nconstexpr size_t MaxDegreeForEvaluationDomainFactory() {\n  size_t i = 1;\n  if constexpr (F::Config::kHasLargeSubgroupRootOfUnity) {\n    for (size_t i = 0; i <= F::Config::kSmallSubgroupAdicity; ++i) {\n      i *= F::Config::kSmallSubgroupBase;\n    }\n  }\n  return i * (size_t{1} << F::Config::kTwoAdicity) - 1;\n}\n\ntemplate <typename F,\n          size_t MaxDegree = MaxDegreeForEvaluationDomainFactory<F>()>\nclass UnivariateEvaluationDomainFactory {\n public:\n  // Construct a domain that is large enough for evaluations of a polynomial\n  // having |num_coeffs| coefficients.\n  constexpr static std::unique_ptr<UnivariateEvaluationDomain<F, MaxDegree>>\n  Create(size_t num_coeffs) {\n    if (Radix2EvaluationDomain<F, MaxDegree>::IsValidNumCoeffs(num_coeffs)) {\n      return Radix2EvaluationDomain<F, MaxDegree>::Create(num_coeffs);\n    } else if (MixedRadixEvaluationDomain<F, MaxDegree>::IsValidNumCoeffs(\n                   num_coeffs)) {\n      return MixedRadixEvaluationDomain<F, MaxDegree>::Create(num_coeffs);\n    }\n    NOTREACHED();\n    return nullptr;\n  }\n};\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_POLYNOMIALS_UNIVARIATE_UNIVARIATE_EVALUATION_DOMAIN_FACTORY_H_\n"
  },
  {
    "path": "tachyon/math/polynomials/univariate/univariate_evaluation_domain_forwards.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_POLYNOMIALS_UNIVARIATE_UNIVARIATE_EVALUATION_DOMAIN_FORWARDS_H_\n#define TACHYON_MATH_POLYNOMIALS_UNIVARIATE_UNIVARIATE_EVALUATION_DOMAIN_FORWARDS_H_\n\n#include <stddef.h>\n\nnamespace tachyon::math {\n\ntemplate <typename F, size_t MaxDegree>\nclass UnivariateEvaluationDomain;\n\ntemplate <typename F, size_t MaxDegree>\nclass Radix2EvaluationDomain;\n\ntemplate <typename F, size_t MaxDegree>\nclass MixedRadixEvaluationDomain;\n\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_POLYNOMIALS_UNIVARIATE_UNIVARIATE_EVALUATION_DOMAIN_FORWARDS_H_\n"
  },
  {
    "path": "tachyon/math/polynomials/univariate/univariate_evaluation_domain_gpu_unittest.cc",
    "content": "#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/elliptic_curves/bls12/bls12_381/fr.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/fr.h\"\n#include \"tachyon/math/finite_fields/baby_bear/baby_bear.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/polynomials/univariate/mixed_radix_evaluation_domain.h\"\n#include \"tachyon/math/polynomials/univariate/radix2_evaluation_domain.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\ntemplate <typename Domain>\nclass UnivariateEvaluationDomainGpuTest\n    : public FiniteFieldTest<typename Domain::Field> {};\n\n}  // namespace\n\nusing UnivariateEvaluationDomainTypes = testing::Types<\n    Radix2EvaluationDomain<BabyBear>, Radix2EvaluationDomain<bls12_381::Fr>,\n    MixedRadixEvaluationDomain<bls12_381::Fr>,\n    Radix2EvaluationDomain<bn254::Fr>, MixedRadixEvaluationDomain<bn254::Fr>>;\nTYPED_TEST_SUITE(UnivariateEvaluationDomainGpuTest,\n                 UnivariateEvaluationDomainTypes);\n\ntemplate <typename Domain>\nvoid Test(Domain* domain, IcicleNTTHolder<typename Domain::Field>* holder) {\n  using DensePoly = typename Domain::DensePoly;\n  using Evals = typename Domain::Evals;\n\n  {\n    auto poly = DensePoly::Random(domain->size() - 1);\n    domain->set_icicle(nullptr);\n    Evals expected_evals = domain->FFT(poly);\n    domain->set_icicle(holder);\n    Evals evals = domain->FFT(std::move(poly));\n    EXPECT_EQ(evals, expected_evals);\n  }\n\n  {\n    auto evals = Evals::Random(domain->size() - 1);\n    domain->set_icicle(nullptr);\n    DensePoly expected_poly = domain->IFFT(evals);\n    domain->set_icicle(holder);\n    DensePoly poly = domain->IFFT(std::move(evals));\n    EXPECT_EQ(poly, expected_poly);\n  }\n}\n\nTYPED_TEST(UnivariateEvaluationDomainGpuTest, FFTCorrectness) {\n  using Domain = TypeParam;\n  using F = typename Domain::Field;\n\n  for (size_t i = 5; i < 15; ++i) {\n    IcicleNTTHolder<F> holder = IcicleNTTHolder<F>::Create();\n    size_t size = size_t{1} << i;\n    auto domain = Domain::Create(size);\n    CHECK(holder->Init(domain->group_gen()));\n    Test(domain.get(), &holder);\n\n    auto coset_domain =\n        domain->GetCoset(F::FromMontgomery(F::Config::kSubgroupGenerator));\n    Test(coset_domain.get(), &holder);\n  }\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/polynomials/univariate/univariate_evaluation_domain_unittest.cc",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#include \"absl/types/span.h\"\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/containers/contains.h\"\n#include \"tachyon/base/functional/function_ref.h\"\n#include \"tachyon/base/optional.h\"\n#include \"tachyon/math/elliptic_curves/bls12/bls12_381/fr.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn384_small_two_adicity/fq.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/polynomials/univariate/mixed_radix_evaluation_domain.h\"\n#include \"tachyon/math/polynomials/univariate/radix2_evaluation_domain.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\nconstexpr size_t kNumCoeffs = 12;\n\ntemplate <typename Domain>\nclass UnivariateEvaluationDomainTest\n    : public FiniteFieldTest<typename Domain::Field> {\n public:\n  using F = typename Domain::Field;\n  using BaseDomain = UnivariateEvaluationDomain<F, Domain::kMaxDegree>;\n\n protected:\n  void TestDomains(size_t num_coeffs,\n                   base::FunctionRef<void(const BaseDomain&)> callback) {\n    std::unique_ptr<BaseDomain> domain = Domain::Create(num_coeffs);\n    std::unique_ptr<BaseDomain> coset_domain =\n        domain->GetCoset(F::FromMontgomery(F::Config::kSubgroupGenerator));\n    for (bool use_coset : {true, false}) {\n      const std::unique_ptr<BaseDomain>& d = use_coset ? coset_domain : domain;\n      callback(*d);\n    }\n  }\n};\n\n}  // namespace\n\nusing UnivariateEvaluationDomainTypes =\n    testing::Types<Radix2EvaluationDomain<bls12_381::Fr>,\n                   MixedRadixEvaluationDomain<bn384_small_two_adicity::Fq>>;\nTYPED_TEST_SUITE(UnivariateEvaluationDomainTest,\n                 UnivariateEvaluationDomainTypes);\n\nTYPED_TEST(UnivariateEvaluationDomainTest, VanishingPolynomialEvaluation) {\n  using Domain = TypeParam;\n  using F = typename Domain::Field;\n  using BaseDomain = UnivariateEvaluationDomain<F, Domain::kMaxDegree>;\n  using SparsePoly = typename Domain::SparsePoly;\n\n  for (size_t coeffs = 0; coeffs < kNumCoeffs; ++coeffs) {\n    this->TestDomains(coeffs, [](const BaseDomain& d) {\n      SparsePoly z = d.GetVanishingPolynomial();\n      for (size_t i = 0; i < 100; ++i) {\n        F point = F::Random();\n        EXPECT_EQ(z.Evaluate(point), d.EvaluateVanishingPolynomial(point));\n      }\n    });\n  }\n}\n\nTYPED_TEST(UnivariateEvaluationDomainTest,\n           VanishingPolynomialVanishesOnDomain) {\n  using Domain = TypeParam;\n  using F = typename Domain::Field;\n  using BaseDomain = UnivariateEvaluationDomain<F, Domain::kMaxDegree>;\n  using SparsePoly = typename Domain::SparsePoly;\n\n  for (size_t coeffs = 0; coeffs < kNumCoeffs; ++coeffs) {\n    this->TestDomains(coeffs, [](const BaseDomain& d) {\n      SparsePoly z = d.GetVanishingPolynomial();\n      for (const F& element : d.GetElements()) {\n        EXPECT_TRUE(z.Evaluate(element).IsZero());\n      }\n    });\n  }\n}\n\nTYPED_TEST(UnivariateEvaluationDomainTest, FilterPolynomial) {\n  using Domain = TypeParam;\n  using F = typename Domain::Field;\n  using BaseDomain = UnivariateEvaluationDomain<F, Domain::kMaxDegree>;\n  using DensePoly = typename Domain::DensePoly;\n\n  if constexpr (std::is_same_v<F, bls12_381::Fr>) {\n    for (uint32_t log_domain_size = 1; log_domain_size < 4; ++log_domain_size) {\n      size_t domain_size = size_t{1} << log_domain_size;\n      std::unique_ptr<Domain> domain = Domain::Create(domain_size);\n      for (uint32_t log_subdomain_size = 1;\n           log_subdomain_size <= log_domain_size; ++log_subdomain_size) {\n        size_t subdomain_size = size_t{1} << log_subdomain_size;\n        std::unique_ptr<Domain> subdomain = Domain::Create(subdomain_size);\n\n        // Obtain all possible offsets of |subdomain| within |domain|.\n        std::vector<bls12_381::Fr> possible_offsets = {bls12_381::Fr::One()};\n        const bls12_381::Fr& domain_generator = domain->group_gen();\n\n        bls12_381::Fr offset = domain_generator;\n        const bls12_381::Fr& subdomain_generator = subdomain->group_gen();\n        while (offset != subdomain_generator) {\n          possible_offsets.push_back(offset);\n          offset *= domain_generator;\n        }\n        EXPECT_EQ(possible_offsets.size(), domain_size / subdomain_size);\n\n        // Get all possible cosets of |subdomain| within |domain|.\n        for (const bls12_381::Fr& offset : possible_offsets) {\n          std::unique_ptr<BaseDomain> coset = subdomain->GetCoset(offset);\n          std::vector<bls12_381::Fr> coset_elements = coset->GetElements();\n          DensePoly filter_poly = domain->GetFilterPolynomial(*coset);\n          EXPECT_EQ(filter_poly.Degree(), domain_size - subdomain_size);\n          for (const bls12_381::Fr& element : domain->GetElements()) {\n            bls12_381::Fr evaluation =\n                unwrap(domain->EvaluateFilterPolynomial(*coset, element));\n            EXPECT_EQ(evaluation, filter_poly.Evaluate(element));\n            if (base::Contains(coset_elements, element)) {\n              EXPECT_TRUE(evaluation.IsOne());\n            } else {\n              EXPECT_TRUE(evaluation.IsZero());\n            }\n          }\n        }\n      }\n    }\n  } else {\n    GTEST_SKIP()\n        << \"Skip testing FilterPolynomial on MixedRadixEvaluationDomain\";\n  }\n}\n\nTYPED_TEST(UnivariateEvaluationDomainTest, SizeOfElements) {\n  using Domain = TypeParam;\n\n  for (size_t coeffs = 0; coeffs < kNumCoeffs; ++coeffs) {\n    size_t size = size_t{1} << coeffs;\n    std::unique_ptr<Domain> domain = Domain::Create(size);\n    EXPECT_EQ(domain->size(), domain->GetElements().size());\n  }\n}\n\nTYPED_TEST(UnivariateEvaluationDomainTest, ContentsOfElements) {\n  using Domain = TypeParam;\n  using F = typename Domain::Field;\n  using BaseDomain = UnivariateEvaluationDomain<F, Domain::kMaxDegree>;\n\n  for (size_t coeffs = 0; coeffs < kNumCoeffs; ++coeffs) {\n    size_t size = size_t{1} << coeffs;\n    this->TestDomains(size, [size](const BaseDomain& d) {\n      std::vector<F> elements = d.GetElements();\n      for (size_t i = 0; i < size; ++i) {\n        EXPECT_EQ(elements[i], d.offset() * d.group_gen().Pow(i));\n        EXPECT_EQ(elements[i], d.GetElement(i));\n      }\n    });\n  }\n}\n\n// Test that lagrange interpolation for a random polynomial at a random\n// point works.\nTYPED_TEST(UnivariateEvaluationDomainTest, NonSystematicLagrangeCoefficients) {\n  using Domain = TypeParam;\n  using F = typename Domain::Field;\n  using BaseDomain = UnivariateEvaluationDomain<F, Domain::kMaxDegree>;\n  using DensePoly = typename Domain::DensePoly;\n  using Evals = typename Domain::Evals;\n\n  for (size_t domain_dim = 1; domain_dim < 5; ++domain_dim) {\n    size_t domain_size = size_t{1} << domain_dim;\n    F rand_pt = F::Random();\n    DensePoly rand_poly = DensePoly::Random(domain_size - 1);\n    F actual_eval = rand_poly.Evaluate(rand_pt);\n    this->TestDomains(domain_size, [domain_size, &rand_pt, &rand_poly,\n                                    &actual_eval](const BaseDomain& d) {\n      std::vector<F> lagrange_coeffs =\n          d.EvaluateAllLagrangeCoefficients(rand_pt);\n\n      std::vector<F> sub_lagrange_coeffs =\n          d.EvaluatePartialLagrangeCoefficients(\n              rand_pt, base::Range<size_t>(1, domain_size - 1));\n\n      if (domain_size > 2) {\n        sub_lagrange_coeffs.push_back(lagrange_coeffs.back());\n        sub_lagrange_coeffs.insert(sub_lagrange_coeffs.begin(),\n                                   lagrange_coeffs.front());\n        EXPECT_EQ(lagrange_coeffs, sub_lagrange_coeffs);\n      }\n\n      Evals poly_evals = d.FFT(rand_poly);\n\n      // Do lagrange interpolation, and compare against the actual\n      // evaluation\n      F interpolated_eval = F::Zero();\n      for (size_t i = 0; i < domain_size; ++i) {\n        interpolated_eval += lagrange_coeffs[i] * poly_evals[i];\n      }\n      EXPECT_EQ(actual_eval, interpolated_eval);\n    });\n  }\n}\n\n/// Test that lagrange coefficients for a point in the domain is correct\nTYPED_TEST(UnivariateEvaluationDomainTest, SystematicLagrangeCoefficients) {\n  // This runs in time O(N²) in the domain size, so keep the domain dimension\n  // low. We generate lagrange coefficients for each element in the domain.\n  using Domain = TypeParam;\n  using F = typename Domain::Field;\n  using BaseDomain = UnivariateEvaluationDomain<F, Domain::kMaxDegree>;\n\n  for (size_t domain_dim = 1; domain_dim < 5; ++domain_dim) {\n    size_t domain_size = size_t{1} << domain_dim;\n    this->TestDomains(domain_size, [domain_size](const BaseDomain& d) {\n      for (size_t i = 0; i < domain_size; ++i) {\n        F x = d.GetElement(i);\n        std::vector<F> lagrange_coeffs = d.EvaluateAllLagrangeCoefficients(x);\n\n        std::vector<F> sub_lagrange_coeffs =\n            d.EvaluatePartialLagrangeCoefficients(\n                x, base::Range<size_t>(1, domain_size - 1));\n\n        if (domain_size > 2) {\n          sub_lagrange_coeffs.push_back(lagrange_coeffs.back());\n          sub_lagrange_coeffs.insert(sub_lagrange_coeffs.begin(),\n                                     lagrange_coeffs.front());\n          EXPECT_EQ(lagrange_coeffs, sub_lagrange_coeffs);\n        }\n\n        for (size_t j = 0; j < domain_size; ++j) {\n          // Lagrange coefficient for the evaluation point,\n          // which should be 1 if i == j\n          if (i == j) {\n            EXPECT_TRUE(lagrange_coeffs[j].IsOne());\n          } else {\n            EXPECT_TRUE(lagrange_coeffs[j].IsZero());\n          }\n        }\n      }\n    });\n  }\n}\n\n// Tests that the ffts output the correct result.\n// This assumes a correct polynomial evaluation at point procedure.\n// It tests consistency of FFT/IFFT, and coset_fft/coset_ifft,\n// along with testing that each individual evaluation is correct.\nTYPED_TEST(UnivariateEvaluationDomainTest, FFTCorrectness) {\n  // Runs in time O(degree²)\n  using Domain = TypeParam;\n  using F = typename Domain::Field;\n  using BaseDomain = UnivariateEvaluationDomain<F, Domain::kMaxDegree>;\n  using DensePoly = typename Domain::DensePoly;\n  using Evals = typename Domain::Evals;\n\n  // NOTE(TomTaehoonKim): |kDegree| is set to 2⁵ -1 for default, but for the\n  // |Radix2EvaluationDomain| test with openmp, it is updated to\n  // |Radix2EvaluationDomain::kDefaultMinNumChunksForCompaction| - 1.\n  const size_t kLogDegree = 5;\n  const size_t kDegree = (size_t{1} << kLogDegree) - 1;\n  DensePoly rand_poly = DensePoly::Random(kDegree);\n  for (uint32_t log_domain_size = kLogDegree; log_domain_size < kLogDegree + 2;\n       ++log_domain_size) {\n    size_t domain_size = size_t{1} << log_domain_size;\n    this->TestDomains(\n        domain_size, [domain_size, &rand_poly](const BaseDomain& d) {\n          Evals poly_evals = d.FFT(rand_poly);\n          for (size_t i = 0; i < domain_size; ++i) {\n            EXPECT_EQ(poly_evals[i], rand_poly.Evaluate(d.GetElement(i)));\n          }\n          EXPECT_EQ(rand_poly, d.IFFT(std::move(poly_evals)));\n        });\n  }\n}\n\n// Test that the degree aware FFT (O(n log d)) matches the regular FFT\n// (O(n log n)).\nTYPED_TEST(UnivariateEvaluationDomainTest, DegreeAwareFFTCorrectness) {\n  using Domain = TypeParam;\n  using F = typename Domain::Field;\n  using BaseDomain = UnivariateEvaluationDomain<F, Domain::kMaxDegree>;\n  using DensePoly = typename Domain::DensePoly;\n  using Evals = typename Domain::Evals;\n\n  if constexpr (std::is_same_v<F, bls12_381::Fr>) {\n    const size_t kLogDegree = 5;\n    const size_t kDegree = (size_t{1} << kLogDegree) - 1;\n    DensePoly rand_poly = DensePoly::Random(kDegree);\n    size_t domain_size = (kDegree + 1) * Domain::kDegreeAwareFFTThresholdFactor;\n    this->TestDomains(domain_size, [domain_size,\n                                    &rand_poly](const BaseDomain& d) {\n      Evals deg_aware_fft_evals = d.FFT(rand_poly);\n      for (size_t i = 0; i < domain_size; ++i) {\n        EXPECT_EQ(deg_aware_fft_evals[i], rand_poly.Evaluate(d.GetElement(i)));\n      }\n    });\n  } else {\n    GTEST_SKIP() << \"Skip testing DegreeAwareFFTCorrectness on \"\n                    \"MixedRadixEvaluationDomain\";\n  }\n}\n\nTYPED_TEST(UnivariateEvaluationDomainTest, RootsOfUnity) {\n  using Domain = TypeParam;\n  using F = typename Domain::Field;\n\n  for (size_t coeffs = 0; coeffs < kNumCoeffs; ++coeffs) {\n    std::unique_ptr<Domain> domain = Domain::Create(coeffs);\n    std::vector<F> actual_roots =\n        Domain::GetRootsOfUnity(domain->size(), domain->group_gen());\n    for (const F& value : actual_roots) {\n      EXPECT_TRUE(domain->EvaluateVanishingPolynomial(value).IsZero());\n    }\n    EXPECT_EQ(actual_roots.size(), domain->size());\n    std::vector<F> expected_roots = domain->GetElements();\n    EXPECT_EQ(absl::MakeConstSpan(actual_roots),\n              absl::Span(expected_roots.data(), actual_roots.size()));\n  }\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/polynomials/univariate/univariate_evaluations.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_POLYNOMIALS_UNIVARIATE_UNIVARIATE_EVALUATIONS_H_\n#define TACHYON_MATH_POLYNOMIALS_UNIVARIATE_UNIVARIATE_EVALUATIONS_H_\n\n#include <stddef.h>\n\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"absl/hash/hash.h\"\n\n#include \"tachyon/base/buffer/copyable.h\"\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/json/json.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/parallelize.h\"\n#include \"tachyon/base/strings/string_util.h\"\n#include \"tachyon/math/base/parallelize_threshold.h\"\n#include \"tachyon/math/polynomials/polynomial.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluation_domain_forwards.h\"\n\nnamespace tachyon {\nnamespace math {\nnamespace internal {\n\ntemplate <typename F, size_t MaxDegree>\nclass UnivariateEvaluationsOp;\n\n}  // namespace internal\n\n// UnivariateEvaluations represents a univariate polynomial in evaluation form.\n// For a univariate polynomial like 3x² + 2x + 1, it can be represented as [(0,\n// 1), (1, 6), (2, 17)]. Using Lagrange interpolation, we can easily convert it\n// into coefficient form, which is [1, 2, 3]. UnivariateEvaluations only stores\n// the y-coordinates, such as [1, 6, 17] in this example. Depending on its\n// evaluation domain, the univariate polynomial can vary.\n// For more information, refer to\n// https://en.wikipedia.org/wiki/Lagrange_polynomial\ntemplate <typename F, size_t MaxDegree>\nclass UnivariateEvaluations final\n    : public Polynomial<UnivariateEvaluations<F, MaxDegree>> {\n public:\n  constexpr static size_t kMaxDegree = MaxDegree;\n  constexpr static F kZero = F::Zero();\n\n  using Field = F;\n\n  constexpr UnivariateEvaluations() = default;\n  constexpr explicit UnivariateEvaluations(const std::vector<F>& evaluations)\n      : evaluations_(evaluations) {\n    CHECK_LE(Degree(), MaxDegree);\n  }\n  constexpr explicit UnivariateEvaluations(std::vector<F>&& evaluations)\n      : evaluations_(std::move(evaluations)) {\n    CHECK_LE(Degree(), MaxDegree);\n  }\n\n  constexpr static bool IsCoefficientForm() { return false; }\n\n  constexpr static bool IsEvaluationForm() { return true; }\n\n  // NOTE(chokobole): The zero polynomial can be represented in two forms:\n  // 1. An empty vector.\n  // 2. A vector filled with |F::Zero()|.\n  constexpr static UnivariateEvaluations Zero() {\n    return UnivariateEvaluations();\n  }\n\n  constexpr static UnivariateEvaluations One(size_t degree) {\n    UnivariateEvaluations ret;\n    ret.evaluations_ = std::vector<F>(degree + 1, F::One());\n    return ret;\n  }\n\n  constexpr static UnivariateEvaluations Random(size_t degree) {\n    return UnivariateEvaluations(\n        base::CreateVector(degree + 1, []() { return F::Random(); }));\n  }\n\n  constexpr const std::vector<F>& evaluations() const { return evaluations_; }\n  constexpr std::vector<F>& evaluations() { return evaluations_; }\n\n  constexpr std::vector<F>&& TakeEvaluations() && {\n    return std::move(evaluations_);\n  }\n\n  // NOTE(chokobole): Sometimes, this degree doesn't match with the exact\n  // degree of the coefficients that is produced by IFFT. We leave it for\n  // consistency with another polynomial.\n  // For example, [0, 0, 0, 0] gives you 3, but it's 0 in reality.\n  constexpr size_t Degree() const {\n    if (evaluations_.empty()) return 0;\n    return evaluations_.size() - 1;\n  }\n\n  constexpr size_t NumElements() const { return evaluations_.size(); }\n\n  constexpr bool IsZero() const {\n    if (evaluations_.empty()) return true;\n    return std::all_of(evaluations_.begin(), evaluations_.end(),\n                       [](const F& value) { return value.IsZero(); });\n  }\n\n  constexpr bool IsOne() const {\n    if (evaluations_.empty()) return false;\n    return std::all_of(evaluations_.begin(), evaluations_.end(),\n                       [](const F& value) { return value.IsOne(); });\n  }\n\n  constexpr bool operator==(const UnivariateEvaluations& other) const {\n    if (evaluations_.empty()) {\n      return other.IsZero();\n    }\n    if (other.evaluations_.empty()) {\n      return IsZero();\n    }\n    return evaluations_ == other.evaluations_;\n  }\n\n  constexpr bool operator!=(const UnivariateEvaluations& other) const {\n    return !operator==(other);\n  }\n\n  // Returns a reference to the coefficient for the given |i| if it exists.\n  // Otherwise, it terminates the program.\n  constexpr F& at(size_t i) {\n    CHECK_LT(i, evaluations_.size());\n    return evaluations_[i];\n  }\n\n  // Returns a reference to the coefficient for the given |i| if it exists.\n  // Otherwise, returns a reference to the |F::Zero()|.\n  constexpr const F& at(size_t i) const { return (*this)[i]; }\n\n  // Returns a reference to the coefficient for the given |i| if it exists.\n  // Otherwise, returns a reference to the |F::Zero()|.\n  constexpr const F& operator[](size_t i) const {\n    if (i < evaluations_.size()) {\n      return evaluations_[i];\n    }\n    return kZero;\n  }\n\n  std::string ToString() const { return base::ContainerToString(evaluations_); }\n\n  // AdditiveSemigroup methods\n  UnivariateEvaluations Add(const UnivariateEvaluations& other) const {\n    return internal::UnivariateEvaluationsOp<F, MaxDegree>::Add(*this, other);\n  }\n\n  UnivariateEvaluations& AddInPlace(const UnivariateEvaluations& other) {\n    return internal::UnivariateEvaluationsOp<F, MaxDegree>::AddInPlace(*this,\n                                                                       other);\n  }\n\n  // AdditiveGroup methods\n  UnivariateEvaluations Sub(const UnivariateEvaluations& other) const {\n    return internal::UnivariateEvaluationsOp<F, MaxDegree>::Sub(*this, other);\n  }\n\n  UnivariateEvaluations& SubInPlace(const UnivariateEvaluations& other) {\n    return internal::UnivariateEvaluationsOp<F, MaxDegree>::SubInPlace(*this,\n                                                                       other);\n  }\n\n  UnivariateEvaluations Negate() const {\n    return internal::UnivariateEvaluationsOp<F, MaxDegree>::Negate(*this);\n  }\n\n  UnivariateEvaluations& NegateInPlace() {\n    return internal::UnivariateEvaluationsOp<F, MaxDegree>::NegateInPlace(\n        *this);\n  }\n\n  // MultiplicativeSemigroup methods\n  UnivariateEvaluations Mul(const UnivariateEvaluations& other) const {\n    return internal::UnivariateEvaluationsOp<F, MaxDegree>::Mul(*this, other);\n  }\n\n  UnivariateEvaluations& MulInPlace(const UnivariateEvaluations& other) {\n    return internal::UnivariateEvaluationsOp<F, MaxDegree>::MulInPlace(*this,\n                                                                       other);\n  }\n\n  UnivariateEvaluations Mul(const F& scalar) const {\n    return internal::UnivariateEvaluationsOp<F, MaxDegree>::Mul(*this, scalar);\n  }\n\n  UnivariateEvaluations& MulInPlace(const F& scalar) {\n    return internal::UnivariateEvaluationsOp<F, MaxDegree>::MulInPlace(*this,\n                                                                       scalar);\n  }\n\n  constexpr std::optional<UnivariateEvaluations> Div(\n      const UnivariateEvaluations& other) const {\n    return internal::UnivariateEvaluationsOp<F, MaxDegree>::Div(*this, other);\n  }\n\n  [[nodiscard]] constexpr std::optional<UnivariateEvaluations*> DivInPlace(\n      const UnivariateEvaluations& other) {\n    return internal::UnivariateEvaluationsOp<F, MaxDegree>::DivInPlace(*this,\n                                                                       other);\n  }\n\n  constexpr std::optional<UnivariateEvaluations> Div(const F& scalar) const {\n    return internal::UnivariateEvaluationsOp<F, MaxDegree>::Div(*this, scalar);\n  }\n\n  [[nodiscard]] constexpr std::optional<UnivariateEvaluations*> DivInPlace(\n      const F& scalar) {\n    return internal::UnivariateEvaluationsOp<F, MaxDegree>::DivInPlace(*this,\n                                                                       scalar);\n  }\n\n  constexpr std::optional<UnivariateEvaluations> operator/(\n      const UnivariateEvaluations& other) const {\n    return Div(other);\n  }\n\n  [[nodiscard]] constexpr std::optional<UnivariateEvaluations*> operator/=(\n      const UnivariateEvaluations& other) {\n    return DivInPlace(other);\n  }\n\n  constexpr std::optional<UnivariateEvaluations> operator/(\n      const F& scalar) const {\n    return Div(scalar);\n  }\n\n  [[nodiscard]] constexpr std::optional<UnivariateEvaluations*> operator/=(\n      const F& scalar) {\n    return DivInPlace(scalar);\n  }\n\n private:\n  friend class internal::UnivariateEvaluationsOp<F, MaxDegree>;\n  // NOTE(chokobole): Commented code below doesn't work since we have code\n  // that tries to create evaluations from a domain whose field is\n  // |RationalField<F>|.\n  // friend class UnivariateEvaluationDomain<F, MaxDegree>;\n  template <typename, size_t>\n  friend class UnivariateEvaluationDomain;\n  friend class Radix2EvaluationDomain<F, MaxDegree>;\n  friend class MixedRadixEvaluationDomain<F, kMaxDegree>;\n\n  // NOTE(chokobole): This creates a polynomial that contains |F::Zero()| up to\n  // |degree| + 1.\n  constexpr static UnivariateEvaluations Zero(size_t degree) {\n    UnivariateEvaluations ret;\n    ret.evaluations_.resize(degree + 1);\n    base::ParallelizeFill(ret.evaluations_, F::Zero(),\n                          /*threshold=*/ParallelizeThreshold::kFieldInit);\n    return ret;\n  }\n\n  std::vector<F> evaluations_;\n};\n\ntemplate <typename F, size_t MaxDegree>\nclass PolynomialTraits<UnivariateEvaluations<F, MaxDegree>> {\n public:\n  constexpr static bool kIsEvaluationForm = true;\n};\n\ntemplate <typename H, typename F, size_t MaxDegree>\nH AbslHashValue(H h, const UnivariateEvaluations<F, MaxDegree>& evals) {\n  // NOTE(chokobole): We shouldn't hash only with a non-zero term.\n  // See https://abseil.io/docs/cpp/guides/hash#the-abslhashvalue-overload\n  size_t degree = 0;\n  for (const F& eval : evals.evaluations()) {\n    h = H::combine(std::move(h), eval);\n    ++degree;\n  }\n  F zero = F::Zero();\n  for (size_t i = degree; i < MaxDegree + 1; ++i) {\n    h = H::combine(std::move(h), zero);\n  }\n  return h;\n}\n\n}  // namespace math\n\nnamespace base {\n\ntemplate <typename F, size_t MaxDegree>\nclass Copyable<math::UnivariateEvaluations<F, MaxDegree>> {\n public:\n  static bool WriteTo(const math::UnivariateEvaluations<F, MaxDegree>& evals,\n                      Buffer* buffer) {\n    return buffer->Write(evals.evaluations());\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer,\n                       math::UnivariateEvaluations<F, MaxDegree>* evals) {\n    std::vector<F> evals_vec;\n    if (!buffer.Read(&evals_vec)) return false;\n    *evals = math::UnivariateEvaluations<F, MaxDegree>(evals_vec);\n    return true;\n  }\n\n  static size_t EstimateSize(\n      const math::UnivariateEvaluations<F, MaxDegree>& evals) {\n    return base::EstimateSize(evals.evaluations());\n  }\n};\n\ntemplate <typename F, size_t MaxDegree>\nclass RapidJsonValueConverter<math::UnivariateEvaluations<F, MaxDegree>> {\n public:\n  template <typename Allocator>\n  static rapidjson::Value From(\n      const math::UnivariateEvaluations<F, MaxDegree>& value,\n      Allocator& allocator) {\n    rapidjson::Value object(rapidjson::kObjectType);\n    AddJsonElement(object, \"evaluations\", value.evaluations(), allocator);\n    return object;\n  }\n\n  static bool To(const rapidjson::Value& json_value, std::string_view key,\n                 math::UnivariateEvaluations<F, MaxDegree>* value,\n                 std::string* error) {\n    std::vector<F> evals;\n    if (!ParseJsonElement(json_value, \"evaluations\", &evals, error))\n      return false;\n    *value = math::UnivariateEvaluations<F, MaxDegree>(std::move(evals));\n    return true;\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#include \"tachyon/math/polynomials/univariate/univariate_evaluations_ops.h\"\n\n#endif  // TACHYON_MATH_POLYNOMIALS_UNIVARIATE_UNIVARIATE_EVALUATIONS_H_\n"
  },
  {
    "path": "tachyon/math/polynomials/univariate/univariate_evaluations_ops.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_POLYNOMIALS_UNIVARIATE_UNIVARIATE_EVALUATIONS_OPS_H_\n#define TACHYON_MATH_POLYNOMIALS_UNIVARIATE_UNIVARIATE_EVALUATIONS_OPS_H_\n\n#include <algorithm>\n#include <atomic>\n#include <optional>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/openmp_util.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluations.h\"\n\nnamespace tachyon::math {\nnamespace internal {\n\ntemplate <typename F, size_t MaxDegree>\nclass UnivariateEvaluationsOp {\n public:\n  using Poly = UnivariateEvaluations<F, MaxDegree>;\n\n  static Poly Add(const Poly& self, const Poly& other) {\n    const std::vector<F>& l_evaluations = self.evaluations_;\n    const std::vector<F>& r_evaluations = other.evaluations_;\n    if (l_evaluations.empty()) {\n      // 0 + g(x)\n      return other;\n    }\n    if (r_evaluations.empty()) {\n      // f(x) + 0\n      return self;\n    }\n    CHECK_EQ(l_evaluations.size(), r_evaluations.size());\n    std::vector<F> o_evaluations(r_evaluations.size());\n    OMP_PARALLEL_FOR(size_t i = 0; i < r_evaluations.size(); ++i) {\n      o_evaluations[i] = l_evaluations[i] + r_evaluations[i];\n    }\n    return Poly(std::move(o_evaluations));\n  }\n\n  static Poly& AddInPlace(Poly& self, const Poly& other) {\n    std::vector<F>& l_evaluations = self.evaluations_;\n    const std::vector<F>& r_evaluations = other.evaluations_;\n    if (l_evaluations.empty()) {\n      // 0 + g(x)\n      return self = other;\n    }\n    if (r_evaluations.empty()) {\n      // f(x) + 0\n      return self;\n    }\n    CHECK_EQ(l_evaluations.size(), r_evaluations.size());\n    OMP_PARALLEL_FOR(size_t i = 0; i < r_evaluations.size(); ++i) {\n      l_evaluations[i] += r_evaluations[i];\n    }\n    return self;\n  }\n\n  static Poly Sub(const Poly& self, const Poly& other) {\n    const std::vector<F>& l_evaluations = self.evaluations_;\n    const std::vector<F>& r_evaluations = other.evaluations_;\n    if (l_evaluations.empty()) {\n      // 0 - g(x)\n      return -other;\n    }\n    if (r_evaluations.empty()) {\n      // f(x) - 0\n      return self;\n    }\n    CHECK_EQ(l_evaluations.size(), r_evaluations.size());\n    std::vector<F> o_evaluations(r_evaluations.size());\n    OMP_PARALLEL_FOR(size_t i = 0; i < r_evaluations.size(); ++i) {\n      o_evaluations[i] = l_evaluations[i] - r_evaluations[i];\n    }\n    return Poly(std::move(o_evaluations));\n  }\n\n  static Poly& SubInPlace(Poly& self, const Poly& other) {\n    std::vector<F>& l_evaluations = self.evaluations_;\n    const std::vector<F>& r_evaluations = other.evaluations_;\n    if (l_evaluations.empty()) {\n      // 0 - g(x)\n      return self = -other;\n    }\n    if (r_evaluations.empty()) {\n      // f(x) - 0\n      return self;\n    }\n    CHECK_EQ(l_evaluations.size(), r_evaluations.size());\n    OMP_PARALLEL_FOR(size_t i = 0; i < r_evaluations.size(); ++i) {\n      l_evaluations[i] -= r_evaluations[i];\n    }\n    return self;\n  }\n\n  static Poly Negate(const Poly& self) {\n    const std::vector<F>& i_evaluations = self.evaluations_;\n    if (i_evaluations.empty()) {\n      return self;\n    }\n    std::vector<F> o_evaluations(i_evaluations.size());\n    OMP_PARALLEL_FOR(size_t i = 0; i < i_evaluations.size(); ++i) {\n      o_evaluations[i] = -i_evaluations[i];\n    }\n    return Poly(std::move(o_evaluations));\n  }\n\n  static Poly& NegateInPlace(Poly& self) {\n    std::vector<F>& evaluations = self.evaluations_;\n    if (evaluations.empty()) {\n      return self;\n    }\n    // clang-format off\n    OMP_PARALLEL_FOR(F& evaluation : evaluations) {\n      // clang-format on\n      evaluation.NegateInPlace();\n    }\n    return self;\n  }\n\n  static Poly Mul(const Poly& self, const Poly& other) {\n    const std::vector<F>& l_evaluations = self.evaluations_;\n    const std::vector<F>& r_evaluations = other.evaluations_;\n    if (l_evaluations.empty() || r_evaluations.empty()) {\n      // 0 * g(x) or f(x) * 0\n      return Poly::Zero();\n    }\n    CHECK_EQ(l_evaluations.size(), r_evaluations.size());\n    std::vector<F> o_evaluations(r_evaluations.size());\n    OMP_PARALLEL_FOR(size_t i = 0; i < r_evaluations.size(); ++i) {\n      o_evaluations[i] = l_evaluations[i] * r_evaluations[i];\n    }\n    return Poly(std::move(o_evaluations));\n  }\n\n  static Poly& MulInPlace(Poly& self, const Poly& other) {\n    std::vector<F>& l_evaluations = self.evaluations_;\n    const std::vector<F>& r_evaluations = other.evaluations_;\n    if (l_evaluations.empty()) {\n      // 0 * g(x)\n      return self;\n    }\n    if (r_evaluations.empty()) {\n      // f(x) * 0\n      l_evaluations.clear();\n      return self;\n    }\n    CHECK_EQ(l_evaluations.size(), r_evaluations.size());\n    OMP_PARALLEL_FOR(size_t i = 0; i < r_evaluations.size(); ++i) {\n      l_evaluations[i] *= r_evaluations[i];\n    }\n    return self;\n  }\n\n  static Poly Mul(const Poly& self, const F& scalar) {\n    const std::vector<F>& l_evaluations = self.evaluations_;\n    if (l_evaluations.empty() || scalar.IsZero()) {\n      // 0 * s or f(x) * 0\n      return Poly::Zero();\n    }\n    if (scalar.IsOne()) {\n      // f(x) * 1\n      return self;\n    }\n    std::vector<F> o_evaluations(l_evaluations.size());\n    OMP_PARALLEL_FOR(size_t i = 0; i < l_evaluations.size(); ++i) {\n      o_evaluations[i] = l_evaluations[i] * scalar;\n    }\n    return Poly(std::move(o_evaluations));\n  }\n\n  static Poly& MulInPlace(Poly& self, const F& scalar) {\n    std::vector<F>& l_evaluations = self.evaluations_;\n    if (l_evaluations.empty() || scalar.IsOne()) {\n      // 0 * s or f(x) * 1\n      return self;\n    }\n    OMP_PARALLEL_FOR(size_t i = 0; i < l_evaluations.size(); ++i) {\n      l_evaluations[i] *= scalar;\n    }\n    return self;\n  }\n\n  CONSTEXPR_IF_NOT_OPENMP static std::optional<Poly> Div(const Poly& self,\n                                                         const Poly& other) {\n    const std::vector<F>& l_evaluations = self.evaluations_;\n    const std::vector<F>& r_evaluations = other.evaluations_;\n    // f(x) / 0\n    if (UNLIKELY(r_evaluations.empty())) {\n      LOG_IF_NOT_GPU(ERROR) << \"Division by zero attempted\";\n      return std::nullopt;\n    }\n    // 0 / g(x)\n    if (l_evaluations.empty()) {\n      return self;\n    }\n    // f(x) & g(x) unequal evaluation sizes\n    if (UNLIKELY(l_evaluations.size() != r_evaluations.size())) {\n      LOG_IF_NOT_GPU(ERROR) << \"Evaluation sizes unequal for division\";\n      return std::nullopt;\n    }\n    std::vector<F> o_evaluations(r_evaluations.size());\n    std::atomic<bool> check_valid(true);\n    OMP_PARALLEL_FOR(size_t i = 0; i < r_evaluations.size(); ++i) {\n      const std::optional<F> div = l_evaluations[i] / r_evaluations[i];\n      if (UNLIKELY(!div)) {\n        check_valid.store(false, std::memory_order_relaxed);\n        continue;\n      }\n      o_evaluations[i] = std::move(*div);\n    }\n    if (LIKELY(check_valid.load(std::memory_order_relaxed))) {\n      return Poly(std::move(o_evaluations));\n    }\n    LOG_IF_NOT_GPU(ERROR) << \"Division by zero attempted\";\n    return std::nullopt;\n  }\n\n  [[nodiscard]] CONSTEXPR_IF_NOT_OPENMP static std::optional<Poly*> DivInPlace(\n      Poly& self, const Poly& other) {\n    std::vector<F>& l_evaluations = self.evaluations_;\n    const std::vector<F>& r_evaluations = other.evaluations_;\n    // f(x) / 0\n    if (UNLIKELY(r_evaluations.empty())) {\n      LOG_IF_NOT_GPU(ERROR) << \"Division by zero attempted\";\n      return std::nullopt;\n    }\n    // 0 / g(x)\n    if (l_evaluations.empty()) {\n      return &self;\n    }\n    // f(x) & g(x) unequal evaluation sizes\n    if (UNLIKELY(l_evaluations.size() != r_evaluations.size())) {\n      LOG_IF_NOT_GPU(ERROR) << \"Evaluation sizes unequal for division\";\n      return std::nullopt;\n    }\n    std::atomic<bool> check_valid(true);\n    OMP_PARALLEL_FOR(size_t i = 0; i < r_evaluations.size(); ++i) {\n      if (UNLIKELY(!(l_evaluations[i] /= r_evaluations[i])))\n        check_valid.store(false, std::memory_order_relaxed);\n    }\n    if (LIKELY(check_valid.load(std::memory_order_relaxed))) {\n      return &self;\n    }\n    LOG_IF_NOT_GPU(ERROR) << \"Division by zero attempted\";\n    return std::nullopt;\n  }\n\n  constexpr static std::optional<Poly> Div(const Poly& self, const F& scalar) {\n    const std::optional<F> scalar_inv = scalar.Inverse();\n    if (LIKELY(scalar_inv)) return Mul(self, *scalar_inv);\n    LOG_IF_NOT_GPU(ERROR) << \"Division by zero attempted\";\n    return std::nullopt;\n  }\n\n  [[nodiscard]] constexpr static std::optional<Poly*> DivInPlace(\n      Poly& self, const F& scalar) {\n    const std::optional<F> scalar_inv = scalar.Inverse();\n    if (LIKELY(scalar_inv)) return &MulInPlace(self, *scalar_inv);\n    LOG_IF_NOT_GPU(ERROR) << \"Division by zero attempted\";\n    return std::nullopt;\n  }\n};\n\n}  // namespace internal\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_POLYNOMIALS_UNIVARIATE_UNIVARIATE_EVALUATIONS_OPS_H_\n"
  },
  {
    "path": "tachyon/math/polynomials/univariate/univariate_evaluations_unittest.cc",
    "content": "#include \"tachyon/math/polynomials/univariate/univariate_evaluations.h\"\n\n#include \"absl/hash/hash_testing.h\"\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/buffer/vector_buffer.h\"\n#include \"tachyon/base/optional.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\nconst size_t kMaxDegree = 4;\n\nusing Poly = UnivariateEvaluations<GF7, kMaxDegree>;\n\nclass UnivariateEvaluationsTest : public FiniteFieldTest<GF7> {\n public:\n  void SetUp() override {\n    polys_.push_back(Poly({GF7(3), GF7(6), GF7(4), GF7(6), GF7(6)}));\n    polys_.push_back(Poly({GF7(3), GF7(4), GF7(5), GF7(0), GF7(2)}));\n    polys_.push_back(Poly({GF7(2), GF7(5), GF7(5), GF7(2), GF7(1)}));\n    polys_.push_back(Poly({GF7(1), GF7(5), GF7(3), GF7(6), GF7(6)}));\n    polys_.push_back(Poly({GF7(0), GF7(0), GF7(0), GF7(0), GF7(0)}));\n    polys_.push_back(Poly::Zero());\n    polys_.push_back(Poly({GF7(1), GF7(1), GF7(1), GF7(1), GF7(1)}));\n  }\n\n protected:\n  std::vector<Poly> polys_;\n};\n\n}  // namespace\n\nTEST_F(UnivariateEvaluationsTest, IsZero) {\n  EXPECT_TRUE(Poly().IsZero());\n  EXPECT_TRUE(Poly::Zero().IsZero());\n  for (size_t i = 0; i < polys_.size() - 1; ++i) {\n    if (i == polys_.size() - 2 || i == polys_.size() - 3) {\n      EXPECT_TRUE(polys_[i].IsZero());\n    } else {\n      EXPECT_FALSE(polys_[i].IsZero());\n    }\n  }\n}\n\nTEST_F(UnivariateEvaluationsTest, IsOne) {\n  EXPECT_FALSE(Poly().IsOne());\n  EXPECT_TRUE(Poly::One(kMaxDegree).IsOne());\n  for (size_t i = 0; i < polys_.size() - 1; ++i) {\n    if (i == polys_.size() - 1) {\n      EXPECT_TRUE(polys_[i].IsOne());\n    } else {\n      EXPECT_FALSE(polys_[i].IsOne());\n    }\n  }\n}\n\nTEST_F(UnivariateEvaluationsTest, Random) {\n  bool success = false;\n  Poly r = Poly::Random(kMaxDegree);\n  for (size_t i = 0; i < 100; ++i) {\n    if (r != Poly::Random(kMaxDegree)) {\n      success = true;\n      break;\n    }\n  }\n  EXPECT_TRUE(success);\n}\n\nTEST_F(UnivariateEvaluationsTest, IndexingOperator) {\n  struct {\n    const Poly& poly;\n    std::vector<int> evaluations;\n  } tests[] = {{polys_[0], {3, 6, 4, 6, 6}}, {polys_[1], {3, 4, 5, 0, 2}},\n               {polys_[2], {2, 5, 5, 2, 1}}, {polys_[3], {1, 5, 3, 6, 6}},\n               {polys_[4], {0, 0, 0, 0, 0}}, {polys_[5], {}},\n               {polys_[6], {1, 1, 1, 1, 1}}};\n\n  for (const auto& test : tests) {\n    for (size_t i = 0; i < kMaxDegree; ++i) {\n      if (i < test.evaluations.size()) {\n        EXPECT_EQ(test.poly[i], GF7(test.evaluations[i]));\n      } else {\n        EXPECT_EQ(test.poly[i], GF7::Zero());\n      }\n    }\n  }\n}\n\nTEST_F(UnivariateEvaluationsTest, EqualityOperators) {\n  struct {\n    const Poly& a;\n    const Poly& b;\n    bool equal;\n  } tests[] = {\n      {polys_[0], polys_[1], false},\n      {polys_[0], polys_[4], false},\n      {polys_[4], polys_[5], true},\n  };\n\n  for (const auto& test : tests) {\n    if (test.equal) {\n      EXPECT_EQ(test.a, test.b);\n      EXPECT_EQ(test.b, test.a);\n    } else {\n      EXPECT_NE(test.a, test.b);\n      EXPECT_NE(test.b, test.a);\n    }\n  }\n}\n\nTEST_F(UnivariateEvaluationsTest, AdditiveOperators) {\n  struct {\n    const Poly& a;\n    const Poly& b;\n    Poly sum;\n    Poly amb;\n    Poly bma;\n  } tests[] = {\n      {\n          polys_[0],\n          polys_[3],\n          Poly({GF7(4), GF7(4), GF7(0), GF7(5), GF7(5)}),\n          Poly({GF7(2), GF7(1), GF7(1), GF7(0), GF7(0)}),\n          Poly({GF7(5), GF7(6), GF7(6), GF7(0), GF7(0)}),\n      },\n      {\n          polys_[0],\n          polys_[4],\n          polys_[0],\n          polys_[0],\n          -polys_[0],\n      },\n      {\n          polys_[0],\n          polys_[5],\n          polys_[0],\n          polys_[0],\n          -polys_[0],\n      },\n  };\n\n  for (const auto& test : tests) {\n    EXPECT_EQ(test.a + test.b, test.sum);\n    EXPECT_EQ(test.b + test.a, test.sum);\n    EXPECT_EQ(test.a - test.b, test.amb);\n    EXPECT_EQ(test.b - test.a, test.bma);\n\n    Poly tmp = test.a;\n    tmp += test.b;\n    EXPECT_EQ(tmp, test.sum);\n    tmp -= test.b;\n    EXPECT_EQ(tmp, test.a);\n  }\n}\n\nTEST_F(UnivariateEvaluationsTest, MultiplicativeOperators) {\n  struct {\n    const Poly& a;\n    const Poly& b;\n    Poly mul;\n    Poly adb;\n    Poly bda;\n  } tests[] = {\n      {\n          polys_[0],\n          polys_[3],\n          Poly({GF7(3), GF7(2), GF7(5), GF7(1), GF7(1)}),\n          Poly({GF7(3), GF7(4), GF7(6), GF7(1), GF7(1)}),\n          Poly({GF7(5), GF7(2), GF7(6), GF7(1), GF7(1)}),\n      },\n      {\n          polys_[0],\n          polys_[4],\n          Poly::Zero(),\n          // NOTE(chokobole): division by zero.\n          Poly::Zero(),\n          Poly::Zero(),\n      },\n      {\n          polys_[0],\n          polys_[5],\n          Poly::Zero(),\n          // NOTE(chokobole): division by zero.\n          Poly::Zero(),\n          Poly::Zero(),\n      },\n      {\n          polys_[0],\n          polys_[6],\n          polys_[0],\n          polys_[0],\n          Poly({GF7(5), GF7(6), GF7(2), GF7(6), GF7(6)}),\n      },\n  };\n\n  for (const auto& test : tests) {\n    EXPECT_EQ(test.a * test.b, test.mul);\n    EXPECT_EQ(test.b * test.a, test.mul);\n    if (!test.b.IsZero()) {\n      EXPECT_EQ(*(test.a / test.b), test.adb);\n    } else {\n      ASSERT_FALSE(test.a / test.b);\n    }\n    if (!test.a.IsZero()) {\n      EXPECT_EQ(*(test.b / test.a), test.bda);\n    } else {\n      ASSERT_FALSE(test.b / test.a);\n    }\n    Poly tmp = test.a;\n    tmp *= test.b;\n    EXPECT_EQ(tmp, test.mul);\n    tmp = test.a;\n    if (!test.b.IsZero()) {\n      ASSERT_TRUE(tmp /= test.b);\n      EXPECT_EQ(tmp, test.adb);\n    } else {\n      ASSERT_FALSE(tmp /= test.b);\n    }\n  }\n}\n\nTEST_F(UnivariateEvaluationsTest, MulScalar) {\n  Poly poly = Poly::Random(kMaxDegree);\n  GF7 scalar = GF7::Random();\n\n  std::vector<GF7> expected_evals;\n  const std::vector<GF7>& evals = poly.evaluations();\n  expected_evals.reserve(evals.size());\n  for (size_t i = 0; i < evals.size(); ++i) {\n    expected_evals.push_back(evals[i] * scalar);\n  }\n\n  Poly actual = poly * scalar;\n  Poly expected(std::move(expected_evals));\n  EXPECT_EQ(actual, expected);\n  poly *= scalar;\n  EXPECT_EQ(poly, expected);\n}\n\nTEST_F(UnivariateEvaluationsTest, DivScalar) {\n  Poly poly = Poly::Random(kMaxDegree);\n  GF7 scalar = GF7::Random();\n  while (scalar.IsZero()) {\n    scalar = GF7::Random();\n  }\n\n  std::vector<GF7> expected_evals;\n  const std::vector<GF7>& evals = poly.evaluations();\n  expected_evals.reserve(evals.size());\n  for (size_t i = 0; i < evals.size(); ++i) {\n    expected_evals.push_back(unwrap(evals[i] / scalar));\n  }\n\n  Poly actual = unwrap(poly / scalar);\n  Poly expected(std::move(expected_evals));\n  EXPECT_EQ(actual, expected);\n  EXPECT_EQ(**(poly /= scalar), expected);\n}\n\nTEST_F(UnivariateEvaluationsTest, Copyable) {\n  Poly expected = Poly::Random(kMaxDegree);\n\n  base::Uint8VectorBuffer write_buf;\n  ASSERT_TRUE(write_buf.Grow(base::EstimateSize(expected)));\n  ASSERT_TRUE(write_buf.Write(expected));\n  ASSERT_TRUE(write_buf.Done());\n\n  write_buf.set_buffer_offset(0);\n\n  Poly value;\n  ASSERT_TRUE(write_buf.Read(&value));\n  EXPECT_EQ(expected, value);\n}\n\nTEST_F(UnivariateEvaluationsTest, Hash) {\n  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple(\n      Poly(), Poly::Zero(), Poly({std::vector<GF7>(kMaxDegree + 1)}),\n      Poly::One(kMaxDegree), Poly::Random(kMaxDegree),\n      Poly::Random(kMaxDegree))));\n}\n\nTEST_F(UnivariateEvaluationsTest, JsonValueConverter) {\n  Poly expected_poly({GF7(1), GF7(2), GF7(3), GF7(4), GF7(5)});\n  std::string expected_json = R\"({\"evaluations\":[1,2,3,4,5]})\";\n\n  Poly poly;\n  std::string error;\n  ASSERT_TRUE(base::ParseJson(expected_json, &poly, &error));\n  ASSERT_TRUE(error.empty());\n  EXPECT_EQ(poly, expected_poly);\n\n  std::string json = base::WriteToJson(poly);\n  EXPECT_EQ(json, expected_json);\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/polynomials/univariate/univariate_polynomial.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_POLYNOMIALS_UNIVARIATE_UNIVARIATE_POLYNOMIAL_H_\n#define TACHYON_MATH_POLYNOMIALS_UNIVARIATE_UNIVARIATE_POLYNOMIAL_H_\n\n#include <stddef.h>\n\n#include <algorithm>\n#include <functional>\n#include <string>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"absl/hash/hash.h\"\n\n#include \"tachyon/base/buffer/copyable.h\"\n#include \"tachyon/base/json/json.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/parallelize.h\"\n#include \"tachyon/math/polynomials/polynomial.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_dense_coefficients.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluation_domain_forwards.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_sparse_coefficients.h\"\n\nnamespace tachyon {\nnamespace math {\n\n// UnivariatePolynomial represents a polynomial with a single variable.\n// For example, 3x² + 2x + 1 is a univariate polynomial, while 3x²y + 2yz + 1 is\n// not a univariate polynomial. The polynomial is represented as a vector of its\n// coefficients. These coefficients are stored in an object which can be\n// DenseCoefficients or SparseCoefficients.\ntemplate <typename _Coefficients>\nclass UnivariatePolynomial final\n    : public Polynomial<UnivariatePolynomial<_Coefficients>> {\n public:\n  using Coefficients = _Coefficients;\n\n  constexpr static size_t kMaxDegree = Coefficients::kMaxDegree;\n\n  using Field = typename Coefficients::Field;\n  using Point = Field;\n\n  constexpr UnivariatePolynomial() = default;\n  constexpr explicit UnivariatePolynomial(const Coefficients& coefficients)\n      : coefficients_(coefficients) {\n    DCHECK(coefficients_.IsClean()) << \"Coefficients are not cleaned up.\";\n  }\n  constexpr explicit UnivariatePolynomial(Coefficients&& coefficients)\n      : coefficients_(std::move(coefficients)) {\n    DCHECK(coefficients_.IsClean()) << \"Coefficients are not cleaned up.\";\n  }\n\n  constexpr static bool IsCoefficientForm() { return true; }\n\n  constexpr static bool IsEvaluationForm() { return false; }\n\n  constexpr static UnivariatePolynomial Zero() {\n    return UnivariatePolynomial(Coefficients::Zero());\n  }\n\n  constexpr static UnivariatePolynomial One() {\n    return UnivariatePolynomial(Coefficients::One());\n  }\n\n  constexpr static UnivariatePolynomial Random(size_t degree) {\n    return UnivariatePolynomial(Coefficients::Random(degree));\n  }\n\n  // Return a vanishing polynomial according to the given |roots|.\n  template <typename Container>\n  constexpr static UnivariatePolynomial FromRoots(const Container& roots) {\n    using DenseCoeffs = UnivariateDenseCoefficients<Field, kMaxDegree>;\n    if constexpr (std::is_same_v<Coefficients, DenseCoeffs>) {\n      return UnivariatePolynomial(Coefficients::FromRoots(roots));\n    } else {\n      using DensePoly = UnivariatePolynomial<DenseCoeffs>;\n      return DensePoly(DenseCoeffs::FromRoots(roots)).ToSparse();\n    }\n  }\n\n  constexpr bool IsZero() const { return coefficients_.IsZero(); }\n\n  constexpr bool IsOne() const { return coefficients_.IsOne(); }\n\n  const Coefficients& coefficients() const { return coefficients_; }\n  Coefficients& coefficients() { return coefficients_; }\n\n  Coefficients&& TakeCoefficients() && { return std::move(coefficients_); }\n\n  constexpr bool operator==(const UnivariatePolynomial& other) const {\n    return coefficients_ == other.coefficients_;\n  }\n\n  constexpr bool operator!=(const UnivariatePolynomial& other) const {\n    return !operator==(other);\n  }\n\n  // Returns a reference to the coefficient for the given |i| if it exists.\n  // Otherwise, it terminates the program.\n  constexpr Field& at(size_t i) { return coefficients_.at(i); }\n\n  // Returns a reference to the coefficient for the given |i| if it exists.\n  // Otherwise, returns a reference to the |Field::Zero()|.\n  constexpr const Field& at(size_t i) const { return coefficients_.at(i); }\n\n  // Returns a reference to the coefficient for the given |i| if it exists.\n  // Otherwise, returns a reference to the |Field::Zero()|.\n  constexpr const Field& operator[](size_t i) const { return coefficients_[i]; }\n\n  // Returns a reference to the leading coefficient if it exists.\n  // Otherwise, returns a reference to the |Field::Zero()|.\n  constexpr const Field& GetLeadingCoefficient() const {\n    return coefficients_.GetLeadingCoefficient();\n  }\n\n  constexpr size_t Degree() const { return coefficients_.Degree(); }\n\n  constexpr const size_t NumElements() const {\n    return coefficients_.NumElements();\n  }\n\n  constexpr Field Evaluate(const Point& point) const {\n    return coefficients_.Evaluate(point);\n  }\n\n  template <typename Container>\n  constexpr static Field EvaluateVanishingPolyByRootsSerial(\n      const Container& roots, const Field& point) {\n    return std::accumulate(roots.begin(), roots.end(), Field::One(),\n                           [point](Field& acc, const Field& root) {\n                             return acc *= (point - root);\n                           });\n  }\n\n  template <typename Container>\n  constexpr static Field EvaluateVanishingPolyByRoots(const Container& roots,\n                                                      const Field& point) {\n    std::vector<Field> products =\n        base::ParallelizeMap(roots, [&point](absl::Span<const Field> chunk) {\n          return EvaluateVanishingPolyByRootsSerial(chunk, point);\n        });\n    return std::accumulate(products.begin(), products.end(), Field::One(),\n                           std::multiplies<>());\n  }\n\n  // Return a polynomial where the original polynomial reduces its degree\n  // by categorizing coefficients into even and odd degrees,\n  // multiplying coefficients with odd degrees by a specified random field |r|,\n  // and summing them together.\n  constexpr UnivariatePolynomial Fold(const Field& r) const {\n    return UnivariatePolynomial(coefficients_.Fold(r));\n  }\n\n  decltype(auto) ToSparse() const {\n    return internal::UnivariatePolynomialOp<Coefficients>::ToSparse(*this);\n  }\n\n  decltype(auto) ToDense() const {\n    return internal::UnivariatePolynomialOp<Coefficients>::ToDense(*this);\n  }\n\n  std::string ToString() const { return coefficients_.ToString(); }\n\n// TODO(ashjeong): change all internal functions to constexpr\n#define OPERATION_METHOD(Name)                                                 \\\n  template <typename Coefficients2,                                            \\\n            std::enable_if_t<internal::SupportsPoly##Name<                     \\\n                Coefficients, UnivariatePolynomial<Coefficients>,              \\\n                UnivariatePolynomial<Coefficients2>>::value>* = nullptr>       \\\n  constexpr auto Name(const UnivariatePolynomial<Coefficients2>& other)        \\\n      const {                                                                  \\\n    return internal::UnivariatePolynomialOp<Coefficients>::Name(*this, other); \\\n  }                                                                            \\\n                                                                               \\\n  template <typename Coefficients2,                                            \\\n            std::enable_if_t<internal::SupportsPoly##Name##InPlace<            \\\n                Coefficients, UnivariatePolynomial<Coefficients>,              \\\n                UnivariatePolynomial<Coefficients2>>::value>* = nullptr>       \\\n  constexpr auto& Name##InPlace(                                               \\\n      const UnivariatePolynomial<Coefficients2>& other) {                      \\\n    return internal::UnivariatePolynomialOp<Coefficients>::Name##InPlace(      \\\n        *this, other);                                                         \\\n  }\n\n  // AdditiveSemigroup methods\n  OPERATION_METHOD(Add)\n\n  // AdditiveGroup methods\n  OPERATION_METHOD(Sub)\n\n  UnivariatePolynomial Negate() const {\n    return internal::UnivariatePolynomialOp<Coefficients>::Negate(*this);\n  }\n\n  UnivariatePolynomial& NegateInPlace() {\n    return internal::UnivariatePolynomialOp<Coefficients>::NegateInPlace(*this);\n  }\n\n  // MultiplicativeSemigroup methods\n  OPERATION_METHOD(Mul)\n\n  UnivariatePolynomial Mul(const Field& scalar) const {\n    return internal::UnivariatePolynomialOp<Coefficients>::Mul(*this, scalar);\n  }\n\n  UnivariatePolynomial& MulInPlace(const Field& scalar) {\n    return internal::UnivariatePolynomialOp<Coefficients>::MulInPlace(*this,\n                                                                      scalar);\n  }\n\n  template <typename Coefficients2,\n            std::enable_if_t<internal::SupportsPolyDiv<\n                Coefficients, UnivariatePolynomial<Coefficients>,\n                UnivariatePolynomial<Coefficients2>>::value>* = nullptr>\n  constexpr auto Div(const UnivariatePolynomial<Coefficients2>& other) const {\n    return internal::UnivariatePolynomialOp<Coefficients>::Div(*this, other);\n  }\n\n  template <typename Coefficients2,\n            std::enable_if_t<internal::SupportsPolyDivInPlace<\n                Coefficients, UnivariatePolynomial<Coefficients>,\n                UnivariatePolynomial<Coefficients2>>::value>* = nullptr>\n  [[nodiscard]] constexpr auto DivInPlace(\n      const UnivariatePolynomial<Coefficients2>& other) {\n    return internal::UnivariatePolynomialOp<Coefficients>::DivInPlace(*this,\n                                                                      other);\n  }\n\n  template <typename Coefficients2,\n            std::enable_if_t<internal::SupportsPolyMod<\n                Coefficients, UnivariatePolynomial<Coefficients>,\n                UnivariatePolynomial<Coefficients2>>::value>* = nullptr>\n  constexpr auto Mod(const UnivariatePolynomial<Coefficients2>& other) const {\n    return internal::UnivariatePolynomialOp<Coefficients>::Mod(*this, other);\n  }\n\n  template <typename Coefficients2,\n            std::enable_if_t<internal::SupportsPolyModInPlace<\n                Coefficients, UnivariatePolynomial<Coefficients>,\n                UnivariatePolynomial<Coefficients2>>::value>* = nullptr>\n  [[nodiscard]] constexpr auto ModInPlace(\n      const UnivariatePolynomial<Coefficients2>& other) {\n    return internal::UnivariatePolynomialOp<Coefficients>::ModInPlace(*this,\n                                                                      other);\n  }\n\n#undef OPERATION_METHOD\n\n  constexpr std::optional<UnivariatePolynomial> operator/(\n      const Field& scalar) const {\n    return internal::UnivariatePolynomialOp<Coefficients>::Div(*this, scalar);\n  }\n\n  [[nodiscard]] constexpr std::optional<UnivariatePolynomial*> operator/=(\n      const Field& scalar) {\n    return internal::UnivariatePolynomialOp<Coefficients>::DivInPlace(*this,\n                                                                      scalar);\n  }\n\n  template <typename Coefficients2>\n  constexpr auto operator/(\n      const UnivariatePolynomial<Coefficients2>& other) const {\n    return Div(other);\n  }\n\n  template <typename Coefficients2>\n  [[nodiscard]] constexpr auto operator/=(\n      const UnivariatePolynomial<Coefficients2>& other) {\n    return DivInPlace(other);\n  }\n\n  template <typename Coefficients2>\n  constexpr auto operator%(\n      const UnivariatePolynomial<Coefficients2>& other) const {\n    return Mod(other);\n  }\n\n  template <typename Coefficients2>\n  constexpr auto& operator%=(const UnivariatePolynomial<Coefficients2>& other) {\n    return ModInPlace(other);\n  }\n\n  template <typename Coefficients2>\n  constexpr auto DivMod(\n      const UnivariatePolynomial<Coefficients2>& other) const {\n    return internal::UnivariatePolynomialOp<Coefficients>::DivMod(*this, other);\n  }\n\n private:\n  friend class internal::UnivariatePolynomialOp<Coefficients>;\n  friend class UnivariateEvaluationDomain<Field, kMaxDegree>;\n  friend class Radix2EvaluationDomain<Field, kMaxDegree>;\n  friend class MixedRadixEvaluationDomain<Field, kMaxDegree>;\n\n  // NOTE(chokobole): This doesn't call |RemoveHighDegreeZeros()| internally.\n  // So when the returned instance of |UnivariatePolynomial| is called with\n  // |IsZero()|, it returns false. So please use it carefully!\n  constexpr static UnivariatePolynomial Zero(size_t degree) {\n    UnivariatePolynomial ret;\n    ret.coefficients_ = Coefficients::Zero(degree);\n    return ret;\n  }\n\n  Coefficients coefficients_;\n};\n\ntemplate <typename F, size_t MaxDegree>\nusing UnivariateDensePolynomial =\n    UnivariatePolynomial<UnivariateDenseCoefficients<F, MaxDegree>>;\n\ntemplate <typename F, size_t MaxDegree>\nusing UnivariateSparsePolynomial =\n    UnivariatePolynomial<UnivariateSparseCoefficients<F, MaxDegree>>;\n\ntemplate <typename Coefficients>\nclass PolynomialTraits<UnivariatePolynomial<Coefficients>> {\n public:\n  constexpr static bool kIsCoefficientForm = true;\n};\n\ntemplate <typename H, typename Coefficients>\nH AbslHashValue(H h, const UnivariatePolynomial<Coefficients>& poly) {\n  return H::combine(std::move(h), poly.coefficients());\n}\n\n}  // namespace math\n\nnamespace base {\n\ntemplate <typename Coefficients>\nclass Copyable<math::UnivariatePolynomial<Coefficients>> {\n public:\n  static bool WriteTo(const math::UnivariatePolynomial<Coefficients>& poly,\n                      Buffer* buffer) {\n    return buffer->Write(poly.coefficients());\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer,\n                       math::UnivariatePolynomial<Coefficients>* poly) {\n    Coefficients coeff;\n    if (!buffer.Read(&coeff)) return false;\n    *poly = math::UnivariatePolynomial<Coefficients>(coeff);\n    return true;\n  }\n\n  static size_t EstimateSize(\n      const math::UnivariatePolynomial<Coefficients>& poly) {\n    return base::EstimateSize(poly.coefficients());\n  }\n};\n\ntemplate <typename F, size_t MaxDegree>\nclass RapidJsonValueConverter<math::UnivariateDensePolynomial<F, MaxDegree>> {\n public:\n  template <typename Allocator>\n  static rapidjson::Value From(\n      const math::UnivariateDensePolynomial<F, MaxDegree>& value,\n      Allocator& allocator) {\n    rapidjson::Value object(rapidjson::kObjectType);\n    AddJsonElement(object, \"coefficients\", value.coefficients(), allocator);\n    return object;\n  }\n\n  static bool To(const rapidjson::Value& json_value, std::string_view key,\n                 math::UnivariateDensePolynomial<F, MaxDegree>* value,\n                 std::string* error) {\n    math::UnivariateDenseCoefficients<F, MaxDegree> dense_coeffs;\n    if (!ParseJsonElement(json_value, \"coefficients\", &dense_coeffs, error))\n      return false;\n    dense_coeffs.RemoveHighDegreeZeros();\n    *value = math::UnivariatePolynomial(std::move(dense_coeffs));\n    return true;\n  }\n};\n\ntemplate <typename F, size_t MaxDegree>\nclass RapidJsonValueConverter<math::UnivariateSparsePolynomial<F, MaxDegree>> {\n public:\n  template <typename Allocator>\n  static rapidjson::Value From(\n      const math::UnivariateSparsePolynomial<F, MaxDegree>& value,\n      Allocator& allocator) {\n    rapidjson::Value object(rapidjson::kObjectType);\n    AddJsonElement(object, \"coefficients\", value.coefficients(), allocator);\n    return object;\n  }\n\n  static bool To(const rapidjson::Value& json_value, std::string_view key,\n                 math::UnivariateSparsePolynomial<F, MaxDegree>* value,\n                 std::string* error) {\n    math::UnivariateSparseCoefficients<F, MaxDegree> sparse_coeffs;\n    if (!ParseJsonElement(json_value, \"coefficients\", &sparse_coeffs, error))\n      return false;\n    *value = math::UnivariatePolynomial(std::move(sparse_coeffs));\n    return true;\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#include \"tachyon/math/polynomials/univariate/univariate_polynomial_ops.h\"\n\n#endif  // TACHYON_MATH_POLYNOMIALS_UNIVARIATE_UNIVARIATE_POLYNOMIAL_H_\n"
  },
  {
    "path": "tachyon/math/polynomials/univariate/univariate_polynomial_ops.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_POLYNOMIALS_UNIVARIATE_UNIVARIATE_POLYNOMIAL_OPS_H_\n#define TACHYON_MATH_POLYNOMIALS_UNIVARIATE_UNIVARIATE_POLYNOMIAL_OPS_H_\n\n#include <algorithm>\n#include <iterator>\n#include <optional>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/openmp_util.h\"\n#include \"tachyon/base/optional.h\"\n#include \"tachyon/base/parallelize.h\"\n#include \"tachyon/base/sort.h\"\n#include \"tachyon/math/base/arithmetics_results.h\"\n#include \"tachyon/math/base/parallelize_threshold.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_polynomial.h\"\n\nnamespace tachyon::math {\nnamespace internal {\n\ntemplate <typename F, size_t MaxDegree>\nclass UnivariatePolynomialOp<UnivariateDenseCoefficients<F, MaxDegree>> {\n public:\n  using D = UnivariateDenseCoefficients<F, MaxDegree>;\n  using S = UnivariateSparseCoefficients<F, MaxDegree>;\n  using Term = typename S::Term;\n\n  static UnivariatePolynomial<D> Add(const UnivariatePolynomial<D>& self,\n                                     const UnivariatePolynomial<D>& other) {\n    if (self.IsZero()) {\n      return other;\n    } else if (other.IsZero()) {\n      return self;\n    }\n\n    const std::vector<F>& l_coefficients = self.coefficients_.coefficients_;\n    const std::vector<F>& r_coefficients = other.coefficients_.coefficients_;\n    UnivariatePolynomial<D> ret;\n    std::vector<F>& o_coefficients = ret.coefficients_.coefficients_;\n    o_coefficients.resize(\n        std::max(l_coefficients.size(), r_coefficients.size()));\n    OMP_PARALLEL_FOR(size_t i = 0; i < o_coefficients.size(); ++i) {\n      o_coefficients[i] = self.coefficients_[i] + other.coefficients_[i];\n    }\n\n    ret.coefficients_.RemoveHighDegreeZeros();\n    return ret;\n  }\n\n  static UnivariatePolynomial<D>& AddInPlace(\n      UnivariatePolynomial<D>& self, const UnivariatePolynomial<D>& other) {\n    if (self.IsZero()) {\n      return self = other;\n    } else if (other.IsZero()) {\n      return self;\n    }\n\n    std::vector<F>& l_coefficients = self.coefficients_.coefficients_;\n    const std::vector<F>& r_coefficients = other.coefficients_.coefficients_;\n    base::ParallelizeResize(\n        l_coefficients, std::max(l_coefficients.size(), r_coefficients.size()),\n        F::Zero(),\n        /*threshold=*/ParallelizeThreshold::kFieldInit);\n\n    OMP_PARALLEL_FOR(size_t i = 0; i < r_coefficients.size(); ++i) {\n      l_coefficients[i] += r_coefficients[i];\n    }\n\n    self.coefficients_.RemoveHighDegreeZeros();\n    return self;\n  }\n\n  static UnivariatePolynomial<D> Add(const UnivariatePolynomial<D>& self,\n                                     const UnivariatePolynomial<S>& other) {\n    if (self.IsZero()) {\n      return other.ToDense();\n    } else if (other.IsZero()) {\n      return self;\n    }\n\n    size_t degree = self.Degree();\n    size_t other_degree = other.Degree();\n    UnivariatePolynomial<D> ret;\n    std::vector<F>& o_coefficients = ret.coefficients_.coefficients_;\n    o_coefficients.resize(std::max(degree, other_degree) + 1);\n    OMP_PARALLEL_FOR(size_t i = 0; i < o_coefficients.size(); ++i) {\n      o_coefficients[i] = self.coefficients_[i] + other.coefficients()[i];\n    }\n\n    ret.coefficients_.RemoveHighDegreeZeros();\n    return ret;\n  }\n\n  static UnivariatePolynomial<D>& AddInPlace(\n      UnivariatePolynomial<D>& self, const UnivariatePolynomial<S>& other) {\n    if (self.IsZero()) {\n      return Copy<false>(self, other);\n    } else if (other.IsZero()) {\n      return self;\n    }\n\n    size_t degree = self.Degree();\n    size_t other_degree = other.Degree();\n    std::vector<F> upper_coeffs;\n    if (degree < other_degree) {\n      base::ParallelizeResize(upper_coeffs, other_degree - degree, F::Zero(),\n                              /*threshold=*/ParallelizeThreshold::kFieldInit);\n    }\n\n    std::vector<F>& l_coefficients = self.coefficients_.coefficients_;\n    const std::vector<Term>& r_terms = other.coefficients().terms_;\n    OMP_PARALLEL_FOR(const Term& r_term : r_terms) {\n      if (r_term.degree <= degree) {\n        l_coefficients[r_term.degree] += r_term.coefficient;\n      } else {\n        upper_coeffs[r_term.degree - degree - 1] = r_term.coefficient;\n      }\n    }\n    base::Extend(l_coefficients, std::move(upper_coeffs));\n\n    self.coefficients_.RemoveHighDegreeZeros();\n    return self;\n  }\n\n  static UnivariatePolynomial<D> Sub(const UnivariatePolynomial<D>& self,\n                                     const UnivariatePolynomial<D>& other) {\n    if (self.IsZero()) {\n      return -other;\n    } else if (other.IsZero()) {\n      return self;\n    }\n\n    const std::vector<F>& l_coefficients = self.coefficients_.coefficients_;\n    const std::vector<F>& r_coefficients = other.coefficients_.coefficients_;\n    UnivariatePolynomial<D> ret;\n    std::vector<F>& o_coefficients = ret.coefficients_.coefficients_;\n    o_coefficients.resize(\n        std::max(l_coefficients.size(), r_coefficients.size()));\n    OMP_PARALLEL_FOR(size_t i = 0; i < o_coefficients.size(); ++i) {\n      o_coefficients[i] = self.coefficients_[i] - other.coefficients_[i];\n    }\n\n    ret.coefficients_.RemoveHighDegreeZeros();\n    return ret;\n  }\n\n  static UnivariatePolynomial<D>& SubInPlace(\n      UnivariatePolynomial<D>& self, const UnivariatePolynomial<D>& other) {\n    if (self.IsZero()) {\n      return self = -other;\n    } else if (other.IsZero()) {\n      return self;\n    }\n\n    std::vector<F>& l_coefficients = self.coefficients_.coefficients_;\n    const std::vector<F>& r_coefficients = other.coefficients_.coefficients_;\n    base::ParallelizeResize(\n        l_coefficients, std::max(l_coefficients.size(), r_coefficients.size()),\n        F::Zero(),\n        /*threshold=*/ParallelizeThreshold::kFieldInit);\n    OMP_PARALLEL_FOR(size_t i = 0; i < r_coefficients.size(); ++i) {\n      l_coefficients[i] -= r_coefficients[i];\n    }\n\n    self.coefficients_.RemoveHighDegreeZeros();\n    return self;\n  }\n\n  static UnivariatePolynomial<D> Sub(const UnivariatePolynomial<D>& self,\n                                     const UnivariatePolynomial<S>& other) {\n    if (self.IsZero()) {\n      return (-other).ToDense();\n    } else if (other.IsZero()) {\n      return self;\n    }\n\n    size_t degree = self.Degree();\n    size_t other_degree = other.Degree();\n    UnivariatePolynomial<D> ret;\n    std::vector<F>& o_coefficients = ret.coefficients_.coefficients_;\n    o_coefficients.resize(std::max(degree, other_degree) + 1);\n    OMP_PARALLEL_FOR(size_t i = 0; i < o_coefficients.size(); ++i) {\n      o_coefficients[i] = self.coefficients_[i] - other.coefficients()[i];\n    }\n\n    ret.coefficients_.RemoveHighDegreeZeros();\n    return ret;\n  }\n\n  static UnivariatePolynomial<D>& SubInPlace(\n      UnivariatePolynomial<D>& self, const UnivariatePolynomial<S>& other) {\n    if (self.IsZero()) {\n      return Copy<true>(self, other);\n    } else if (other.IsZero()) {\n      return self;\n    }\n\n    size_t degree = self.Degree();\n    size_t other_degree = other.Degree();\n    std::vector<F> upper_coeffs;\n    if (degree < other_degree) {\n      upper_coeffs = std::vector<F>(other_degree - degree);\n    }\n\n    std::vector<F>& l_coefficients = self.coefficients_.coefficients_;\n    const std::vector<Term>& r_terms = other.coefficients().terms_;\n    OMP_PARALLEL_FOR(const Term& r_term : r_terms) {\n      if (r_term.degree <= degree) {\n        l_coefficients[r_term.degree] -= r_term.coefficient;\n      } else {\n        upper_coeffs[r_term.degree - degree - 1] = -r_term.coefficient;\n      }\n    }\n    base::Extend(l_coefficients, std::move(upper_coeffs));\n\n    self.coefficients_.RemoveHighDegreeZeros();\n    return self;\n  }\n\n  static UnivariatePolynomial<D> Negate(const UnivariatePolynomial<D>& self) {\n    if (self.IsZero()) {\n      return self;\n    }\n    const std::vector<F>& i_coefficients = self.coefficients_.coefficients_;\n    std::vector<F> o_coefficients(i_coefficients.size());\n    OMP_PARALLEL_FOR(size_t i = 0; i < o_coefficients.size(); ++i) {\n      o_coefficients[i] = -i_coefficients[i];\n    }\n    return UnivariatePolynomial<D>(D(std::move(o_coefficients)));\n  }\n\n  static UnivariatePolynomial<D>& NegateInPlace(UnivariatePolynomial<D>& self) {\n    if (self.IsZero()) {\n      return self;\n    }\n    std::vector<F>& coefficients = self.coefficients_.coefficients_;\n    // clang-format off\n    OMP_PARALLEL_FOR(F& coefficient : coefficients) {\n      // clang-format on\n      coefficient.NegateInPlace();\n    }\n    self.coefficients_.RemoveHighDegreeZeros();\n    return self;\n  }\n\n  static UnivariatePolynomial<D> Mul(const UnivariatePolynomial<D>& self,\n                                     const F& scalar) {\n    if (self.IsZero() || scalar.IsOne()) {\n      return self;\n    } else if (scalar.IsZero()) {\n      return UnivariatePolynomial<D>::Zero();\n    }\n    const std::vector<F>& l_coefficients = self.coefficients_.coefficients_;\n    std::vector<F> o_coefficients(l_coefficients.size());\n    OMP_PARALLEL_FOR(size_t i = 0; i < l_coefficients.size(); ++i) {\n      o_coefficients[i] = l_coefficients[i] * scalar;\n    }\n    return UnivariatePolynomial<D>(D(std::move(o_coefficients)));\n  }\n\n  static UnivariatePolynomial<D>& MulInPlace(UnivariatePolynomial<D>& self,\n                                             const F& scalar) {\n    if (self.IsZero() || scalar.IsOne()) {\n      return self;\n    }\n    std::vector<F>& coefficients = self.coefficients_.coefficients_;\n    // clang-format off\n    OMP_PARALLEL_FOR(F& coefficient : coefficients) {\n      // clang-format on\n      coefficient *= scalar;\n    }\n    self.coefficients_.RemoveHighDegreeZeros();\n    return self;\n  }\n\n  static UnivariatePolynomial<D> Mul(const UnivariatePolynomial<D>& self,\n                                     const UnivariatePolynomial<D>& other) {\n    if (self.IsZero() || other.IsZero()) {\n      return UnivariatePolynomial<D>::Zero();\n    } else if (self.IsOne()) {\n      return other;\n    } else if (other.IsOne()) {\n      return self;\n    }\n\n    UnivariatePolynomial<D> ret;\n    DoMul(self, other, ret);\n    return ret;\n  }\n\n  static UnivariatePolynomial<D>& MulInPlace(\n      UnivariatePolynomial<D>& self, const UnivariatePolynomial<D>& other) {\n    std::vector<F>& l_coefficients = self.coefficients_.coefficients_;\n    const std::vector<F>& r_coefficients = other.coefficients_.coefficients_;\n    if (self.IsZero() || other.IsOne()) {\n      return self;\n    } else if (self.IsOne()) {\n      l_coefficients = r_coefficients;\n      return self;\n    } else if (other.IsZero()) {\n      l_coefficients = {};\n      return self;\n    }\n\n    DoMul(self, other, self);\n    return self;\n  }\n\n  static UnivariatePolynomial<D> Mul(const UnivariatePolynomial<D>& self,\n                                     const UnivariatePolynomial<S>& other) {\n    if (self.IsZero() || other.IsZero()) {\n      return UnivariatePolynomial<D>::Zero();\n    } else if (self.IsOne()) {\n      return other.ToDense();\n    } else if (other.IsOne()) {\n      return self;\n    }\n\n    UnivariatePolynomial<D> ret;\n    DoMul(self, other, ret);\n    return ret;\n  }\n\n  static UnivariatePolynomial<D>& MulInPlace(\n      UnivariatePolynomial<D>& self, const UnivariatePolynomial<S>& other) {\n    std::vector<F>& l_coefficients = self.coefficients_.coefficients_;\n    if (self.IsZero() || other.IsOne()) {\n      return self;\n    } else if (self.IsOne()) {\n      return Copy<false>(self, other);\n    } else if (other.IsZero()) {\n      l_coefficients = {};\n      return self;\n    }\n\n    DoMul(self, other, self);\n    return self;\n  }\n\n  static std::optional<UnivariatePolynomial<D>> Div(\n      const UnivariatePolynomial<D>& self, const F& scalar) {\n    const std::optional<F> scalar_inv = scalar.Inverse();\n    if (LIKELY(scalar_inv)) return Mul(self, *scalar_inv);\n    LOG_IF_NOT_GPU(ERROR) << \"Division by zero attempted\";\n    return std::nullopt;\n  }\n\n  [[nodiscard]] static std::optional<UnivariatePolynomial<D>*> DivInPlace(\n      UnivariatePolynomial<D>& self, const F& scalar) {\n    const std::optional<F> scalar_inv = scalar.Inverse();\n    if (LIKELY(scalar_inv)) return &MulInPlace(self, *scalar_inv);\n    LOG_IF_NOT_GPU(ERROR) << \"Division by zero attempted\";\n    return std::nullopt;\n  }\n\n  template <typename DOrS>\n  constexpr static std::optional<UnivariatePolynomial<D>> Div(\n      const UnivariatePolynomial<D>& self,\n      const UnivariatePolynomial<DOrS>& other) {\n    DivResult<UnivariatePolynomial<D>> result;\n    if (LIKELY(Divide(self, other, result))) {\n      result.quotient.coefficients_.RemoveHighDegreeZeros();\n      return result.quotient;\n    }\n    LOG_IF_NOT_GPU(ERROR) << \"Division by zero attempted\";\n    return std::nullopt;\n  }\n\n  template <typename DOrS>\n  [[nodiscard]] constexpr static std::optional<UnivariatePolynomial<D>*>\n  DivInPlace(UnivariatePolynomial<D>& self,\n             const UnivariatePolynomial<DOrS>& other) {\n    DivResult<UnivariatePolynomial<D>> result;\n    if (LIKELY(Divide(self, other, result))) {\n      self = std::move(result.quotient);\n      self.coefficients_.RemoveHighDegreeZeros();\n      return &self;\n    }\n    LOG_IF_NOT_GPU(ERROR) << \"Division by zero attempted\";\n    return std::nullopt;\n  }\n\n  template <typename DOrS>\n  constexpr static std::optional<UnivariatePolynomial<D>> Mod(\n      const UnivariatePolynomial<D>& self,\n      const UnivariatePolynomial<DOrS>& other) {\n    DivResult<UnivariatePolynomial<D>> result;\n    if (LIKELY(Divide(self, other, result))) {\n      result.remainder.coefficients_.RemoveHighDegreeZeros();\n      return result.remainder;\n    }\n    LOG_IF_NOT_GPU(ERROR) << \"Division by zero attempted by mod\";\n    return std::nullopt;\n  }\n\n  template <typename DOrS>\n  [[nodiscard]] constexpr static std::optional<UnivariatePolynomial<D>*>\n  ModInPlace(UnivariatePolynomial<D>& self,\n             const UnivariatePolynomial<DOrS>& other) {\n    DivResult<UnivariatePolynomial<D>> result;\n    if (LIKELY(Divide(self, other, result))) {\n      self = std::move(result.remainder);\n      self.coefficients_.RemoveHighDegreeZeros();\n      return &self;\n    }\n    LOG_IF_NOT_GPU(ERROR) << \"Division by zero attempted by mod\";\n    return std::nullopt;\n  }\n\n  template <typename DOrS>\n  constexpr static std::optional<DivResult<UnivariatePolynomial<D>>> DivMod(\n      const UnivariatePolynomial<D>& self,\n      const UnivariatePolynomial<DOrS>& other) {\n    DivResult<UnivariatePolynomial<D>> result;\n    if (LIKELY(Divide(self, other, result))) return result;\n    LOG_IF_NOT_GPU(ERROR) << \"Division by zero attempted by divmod\";\n    return std::nullopt;\n  }\n\n  static const UnivariatePolynomial<D>& ToDense(\n      const UnivariatePolynomial<D>& self) {\n    return self;\n  }\n\n  static UnivariatePolynomial<S> ToSparse(const UnivariatePolynomial<D>& self) {\n    std::vector<Term> terms;\n    size_t size = self.Degree() + 1;\n    // TODO(chokobole): What if this dense polynomial is really sparse..?\n    terms.reserve(size);\n    for (size_t i = 0; i < size; ++i) {\n      if (self[i].IsZero()) {\n        continue;\n      }\n      terms.push_back({i, self[i]});\n    }\n    return UnivariatePolynomial<S>(S(std::move(terms)));\n  }\n\n private:\n  template <bool NEGATION>\n  static UnivariatePolynomial<D>& Copy(UnivariatePolynomial<D>& self,\n                                       const UnivariatePolynomial<S>& other) {\n    std::vector<F>& l_coefficients = self.coefficients_.coefficients_;\n    l_coefficients.resize(other.Degree() + 1);\n    base::ParallelizeFill(l_coefficients, F::Zero(),\n                          /*threshold=*/ParallelizeThreshold::kFieldInit);\n\n    const std::vector<Term>& r_terms = other.coefficients().terms_;\n    OMP_PARALLEL_FOR(const Term& r_term : r_terms) {\n      if constexpr (NEGATION) {\n        l_coefficients[r_term.degree] = -r_term.coefficient;\n      } else {\n        l_coefficients[r_term.degree] = r_term.coefficient;\n      }\n    }\n    self.coefficients_.RemoveHighDegreeZeros();\n    return self;\n  }\n\n  static void DoMul(const UnivariatePolynomial<D>& a,\n                    const UnivariatePolynomial<D>& b,\n                    UnivariatePolynomial<D>& c) {\n    size_t a_degree = a.Degree();\n    size_t b_degree = b.Degree();\n\n    const std::vector<F>& a_coefficients = a.coefficients_.coefficients_;\n    const std::vector<F>& b_coefficients = b.coefficients_.coefficients_;\n    std::vector<F> c_coefficients(a_degree + b_degree + 1);\n    base::ParallelizeFill(c_coefficients, F::Zero(),\n                          /*threshold=*/ParallelizeThreshold::kFieldInit);\n    for (size_t i = 0; i < b_coefficients.size(); ++i) {\n      const F& b = b_coefficients[i];\n      if (b.IsZero()) {\n        continue;\n      } else {\n        for (size_t j = 0; j < a_coefficients.size(); ++j) {\n          c_coefficients[i + j] += a_coefficients[j] * b;\n        }\n      }\n    }\n\n    c.coefficients_.coefficients_ = std::move(c_coefficients);\n    c.coefficients_.RemoveHighDegreeZeros();\n  }\n\n  static void DoMul(const UnivariatePolynomial<D>& a,\n                    const UnivariatePolynomial<S>& b,\n                    UnivariatePolynomial<D>& c) {\n    size_t a_degree = a.Degree();\n    size_t b_degree = b.Degree();\n\n    const std::vector<F>& a_coefficients = a.coefficients_.coefficients_;\n    const std::vector<Term>& b_terms = b.coefficients().terms_;\n    std::vector<F> c_coefficients(a_degree + b_degree + 1);\n    base::ParallelizeFill(c_coefficients, F::Zero(),\n                          /*threshold=*/ParallelizeThreshold::kFieldInit);\n    for (size_t i = 0; i < b_terms.size(); ++i) {\n      const F& b = b_terms[i].coefficient;\n      if (b.IsZero()) {\n        continue;\n      } else {\n        size_t b_degree = b_terms[i].degree;\n        for (size_t j = 0; j < a_coefficients.size(); ++j) {\n          c_coefficients[b_degree + j] += a_coefficients[j] * b;\n        }\n      }\n    }\n\n    c.coefficients_.coefficients_ = std::move(c_coefficients);\n    c.coefficients_.RemoveHighDegreeZeros();\n  }\n\n  template <typename DOrS>\n  constexpr static bool Divide(const UnivariatePolynomial<D>& self,\n                               const UnivariatePolynomial<DOrS>& other,\n                               DivResult<UnivariatePolynomial<D>>& output) {\n    if (UNLIKELY(other.IsZero())) {\n      LOG_IF_NOT_GPU(ERROR) << \"Division by zero attempted\";\n      return false;\n    }\n    if (self.IsZero()) {\n      output = DivResult<UnivariatePolynomial<D>>{\n          UnivariatePolynomial<D>::Zero(), other.ToDense()};\n      return true;\n    } else if (self.Degree() < other.Degree()) {\n      output = DivResult<UnivariatePolynomial<D>>{\n          UnivariatePolynomial<D>::Zero(), self.ToDense()};\n      return true;\n    }\n    std::vector<F> quotient(self.Degree() - other.Degree() + 1);\n    base::ParallelizeFill(quotient, F::Zero(),\n                          /*threshold=*/ParallelizeThreshold::kFieldInit);\n    UnivariatePolynomial<D> remainder = self.ToDense();\n    std::vector<F>& r_coefficients = remainder.coefficients_.coefficients_;\n    F divisor_leading_inv = *other.GetLeadingCoefficient().Inverse();\n\n    while (!remainder.IsZero() && remainder.Degree() >= other.Degree()) {\n      F q_coeff =\n          remainder.coefficients_.coefficients_.back() * divisor_leading_inv;\n      size_t degree = remainder.Degree() - other.Degree();\n      quotient[degree] = q_coeff;\n\n      if constexpr (std::is_same_v<DOrS, D>) {\n        const std::vector<F>& d_terms = other.coefficients_.coefficients_;\n        for (size_t i = 0; i < d_terms.size(); ++i) {\n          r_coefficients[degree + i] -= q_coeff * d_terms[i];\n        }\n      } else {\n        const std::vector<Term>& d_terms = other.coefficients().terms_;\n        for (const Term& d_term : d_terms) {\n          r_coefficients[degree + d_term.degree] -=\n              q_coeff * d_term.coefficient;\n        }\n      }\n      remainder.coefficients_.RemoveHighDegreeZeros();\n    }\n    D d(std::move(quotient));\n    d.RemoveHighDegreeZeros();\n    output = DivResult<UnivariatePolynomial<D>>{\n        UnivariatePolynomial<D>(std::move(d)), std::move(remainder)};\n    return true;\n  }\n};\n\ntemplate <typename F, size_t MaxDegree>\nclass UnivariatePolynomialOp<UnivariateSparseCoefficients<F, MaxDegree>> {\n public:\n  using D = UnivariateDenseCoefficients<F, MaxDegree>;\n  using S = UnivariateSparseCoefficients<F, MaxDegree>;\n  using Term = typename S::Term;\n\n  static UnivariatePolynomial<D> Add(const UnivariatePolynomial<S>& self,\n                                     const UnivariatePolynomial<D>& other) {\n    return other + self;\n  }\n\n  static UnivariatePolynomial<S> Add(const UnivariatePolynomial<S>& self,\n                                     const UnivariatePolynomial<S>& other) {\n    if (self.IsZero()) {\n      return other;\n    } else if (other.IsZero()) {\n      return self;\n    }\n\n    UnivariatePolynomial<S> ret;\n    DoAdd<false>(self, other, ret);\n    return ret;\n  }\n\n  static UnivariatePolynomial<S>& AddInPlace(\n      UnivariatePolynomial<S>& self, const UnivariatePolynomial<S>& other) {\n    if (self.IsZero()) {\n      return self = other;\n    } else if (other.IsZero()) {\n      return self;\n    }\n\n    DoAdd<false>(self, other, self);\n    return self;\n  }\n\n  static UnivariatePolynomial<D> Sub(const UnivariatePolynomial<S>& self,\n                                     const UnivariatePolynomial<D>& other) {\n    return -other + self;\n  }\n\n  static UnivariatePolynomial<S> Sub(const UnivariatePolynomial<S>& self,\n                                     const UnivariatePolynomial<S>& other) {\n    if (self.IsZero()) {\n      return -other;\n    } else if (other.IsZero()) {\n      return self;\n    }\n\n    UnivariatePolynomial<S> ret;\n    DoAdd<true>(self, other, ret);\n    return ret;\n  }\n\n  static UnivariatePolynomial<S>& SubInPlace(\n      UnivariatePolynomial<S>& self, const UnivariatePolynomial<S>& other) {\n    if (self.IsZero()) {\n      return self = -other;\n    } else if (other.IsZero()) {\n      return self;\n    }\n\n    DoAdd<true>(self, other, self);\n    return self;\n  }\n\n  static UnivariatePolynomial<S> Negate(const UnivariatePolynomial<S>& self) {\n    if (self.IsZero()) {\n      return self;\n    }\n    const std::vector<Term>& l_terms = self.coefficients_.terms_;\n    std::vector<Term> o_terms(l_terms.size());\n    for (size_t i = 0; i < o_terms.size(); ++i) {\n      o_terms[i] = -l_terms[i];\n    }\n    return UnivariatePolynomial<S>(S(std::move(o_terms)));\n  }\n\n  static UnivariatePolynomial<S>& NegateInPlace(UnivariatePolynomial<S>& self) {\n    if (self.IsZero()) {\n      return self;\n    }\n    std::vector<Term>& terms = self.coefficients_.terms_;\n    for (Term& term : terms) {\n      term.coefficient.NegateInPlace();\n    }\n    return self;\n  }\n\n  static UnivariatePolynomial<D> Mul(const UnivariatePolynomial<S>& self,\n                                     const UnivariatePolynomial<D>& other) {\n    return other * self;\n  }\n\n  static UnivariatePolynomial<S> Mul(const UnivariatePolynomial<S>& self,\n                                     const F& scalar) {\n    if (self.IsZero() || scalar.IsZero()) {\n      return UnivariatePolynomial<S>::Zero();\n    }\n    const std::vector<Term>& l_terms = self.coefficients_.terms_;\n    std::vector<Term> o_terms(l_terms.size());\n    for (size_t i = 0; i < l_terms.size(); ++i) {\n      o_terms[i] = l_terms[i] * scalar;\n    }\n    return UnivariatePolynomial<S>(S(std::move(o_terms)));\n  }\n\n  static UnivariatePolynomial<S>& MulInPlace(UnivariatePolynomial<S>& self,\n                                             const F& scalar) {\n    std::vector<Term>& terms = self.coefficients_.terms_;\n    if (self.IsZero() || scalar.IsOne()) {\n      return self;\n    } else if (scalar.IsZero()) {\n      terms.clear();\n      return self;\n    }\n    for (Term& term : terms) {\n      term.coefficient *= scalar;\n    }\n    return self;\n  }\n\n  static UnivariatePolynomial<S> Mul(const UnivariatePolynomial<S>& self,\n                                     const UnivariatePolynomial<S>& other) {\n    if (self.IsZero() || other.IsZero()) {\n      return UnivariatePolynomial<S>::Zero();\n    } else if (self.IsOne()) {\n      return other;\n    } else if (other.IsOne()) {\n      return self;\n    }\n\n    UnivariatePolynomial<S> ret;\n    DoMul(self, other, ret);\n    return ret;\n  }\n\n  static UnivariatePolynomial<S>& MulInPlace(\n      UnivariatePolynomial<S>& self, const UnivariatePolynomial<S>& other) {\n    if (self.IsZero() || other.IsOne()) {\n      return self;\n    } else if (self.IsOne()) {\n      return self = other;\n    } else if (other.IsZero()) {\n      self.coefficients_.terms_.clear();\n      return self;\n    }\n\n    DoMul(self, other, self);\n    return self;\n  }\n\n  static std::optional<UnivariatePolynomial<S>> Div(\n      const UnivariatePolynomial<S>& self, const F& scalar) {\n    const std::optional<F> scalar_inv = scalar.Inverse();\n    if (LIKELY(scalar_inv)) return Mul(self, *scalar_inv);\n    LOG_IF_NOT_GPU(ERROR) << \"Division by zero attempted\";\n    return std::nullopt;\n  }\n\n  [[nodiscard]] static std::optional<UnivariatePolynomial<S>*> DivInPlace(\n      UnivariatePolynomial<S>& self, const F& scalar) {\n    const std::optional<F> scalar_inv = scalar.Inverse();\n    if (LIKELY(scalar_inv)) return &MulInPlace(self, *scalar_inv);\n    LOG_IF_NOT_GPU(ERROR) << \"Division by zero attempted\";\n    return std::nullopt;\n  }\n\n  template <typename DOrS>\n  constexpr static std::optional<UnivariatePolynomial<D>> Div(\n      const UnivariatePolynomial<S>& self,\n      const UnivariatePolynomial<DOrS>& other) {\n    if (self.IsZero()) {\n      return UnivariatePolynomial<D>::Zero();\n    }\n    return self.ToDense() / other;\n  }\n\n  template <typename DOrS>\n  constexpr static std::optional<UnivariatePolynomial<D>> Mod(\n      const UnivariatePolynomial<S>& self,\n      const UnivariatePolynomial<DOrS>& other) {\n    if (self.IsZero()) {\n      return other.ToDense();\n    }\n    return self.ToDense() % other;\n  }\n\n  template <typename DOrS>\n  constexpr static std::optional<DivResult<UnivariatePolynomial<D>>> DivMod(\n      const UnivariatePolynomial<S>& self,\n      const UnivariatePolynomial<DOrS>& other) {\n    if (self.IsZero()) {\n      return DivResult<UnivariatePolynomial<D>>{UnivariatePolynomial<D>::Zero(),\n                                                other.ToDense()};\n    }\n    return self.ToDense().DivMod(other);\n  }\n\n  static UnivariatePolynomial<D> ToDense(const UnivariatePolynomial<S>& self) {\n    if (self.IsZero()) {\n      return UnivariatePolynomial<D>::Zero();\n    }\n    size_t size = self.Degree() + 1;\n    std::vector<F> coefficients(size);\n    OMP_PARALLEL_FOR(size_t i = 0; i < size; ++i) { coefficients[i] = self[i]; }\n    return UnivariatePolynomial<D>(D(std::move(coefficients)));\n  }\n\n  static const UnivariatePolynomial<S>& ToSparse(\n      const UnivariatePolynomial<S>& self) {\n    return self;\n  }\n\n private:\n  template <bool NEGATION>\n  static void DoAdd(const UnivariatePolynomial<S>& a,\n                    const UnivariatePolynomial<S>& b,\n                    UnivariatePolynomial<S>& c) {\n    const std::vector<Term>& a_terms = a.coefficients_.terms_;\n    const std::vector<Term>& b_terms = b.coefficients_.terms_;\n    std::vector<Term> c_terms;\n    c_terms.reserve(std::max(a_terms.size(), b_terms.size()));\n\n    auto a_it = a_terms.begin();\n    auto b_it = b_terms.begin();\n    while (a_it != a_terms.end() || b_it != b_terms.end()) {\n      if (a_it == a_terms.end()) {\n        if constexpr (NEGATION) {\n          c_terms.push_back(-(*b_it));\n        } else {\n          c_terms.push_back(*b_it);\n        }\n        ++b_it;\n        continue;\n      }\n      if (b_it == b_terms.end()) {\n        c_terms.push_back(*a_it);\n        ++a_it;\n        continue;\n      }\n      if (a_it->degree < b_it->degree) {\n        c_terms.push_back(*a_it);\n        ++a_it;\n      } else if (a_it->degree > b_it->degree) {\n        if constexpr (NEGATION) {\n          c_terms.push_back(-(*b_it));\n        } else {\n          c_terms.push_back(*b_it);\n        }\n        ++b_it;\n      } else {\n        F coeff;\n        if constexpr (NEGATION) {\n          coeff = a_it->coefficient - b_it->coefficient;\n        } else {\n          coeff = a_it->coefficient + b_it->coefficient;\n        }\n        if (!coeff.IsZero()) {\n          c_terms.push_back({a_it->degree, std::move(coeff)});\n        }\n        ++a_it;\n        ++b_it;\n      }\n    }\n\n    c.coefficients_ = S(std::move(c_terms));\n  }\n\n  static void DoMul(const UnivariatePolynomial<S>& a,\n                    const UnivariatePolynomial<S>& b,\n                    UnivariatePolynomial<S>& c) {\n    const std::vector<Term>& a_terms = a.coefficients_.terms_;\n    const std::vector<Term>& b_terms = b.coefficients_.terms_;\n    std::vector<Term> c_terms;\n    for (const Term& a_term : a_terms) {\n      for (const Term& b_term : b_terms) {\n        F f = a_term.coefficient * b_term.coefficient;\n        if (f.IsZero()) continue;\n        size_t degree = a_term.degree + b_term.degree;\n        auto it = base::ranges::find_if(c_terms, [degree](const Term& term) {\n          return term.degree == degree;\n        });\n        if (it != c_terms.end()) {\n          it->coefficient += f;\n          if (it->coefficient.IsZero()) {\n            c_terms.erase(it);\n          }\n        } else {\n          c_terms.push_back({degree, std::move(f)});\n        }\n      }\n    }\n    base::UnstableSort(c_terms.begin(), c_terms.end());\n    c.coefficients_ = S(std::move(c_terms));\n  }\n};\n\n}  // namespace internal\n}  // namespace tachyon::math\n\n#endif  // TACHYON_MATH_POLYNOMIALS_UNIVARIATE_UNIVARIATE_POLYNOMIAL_OPS_H_\n"
  },
  {
    "path": "tachyon/math/polynomials/univariate/univariate_sparse_coefficients.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_MATH_POLYNOMIALS_UNIVARIATE_UNIVARIATE_SPARSE_COEFFICIENTS_H_\n#define TACHYON_MATH_POLYNOMIALS_UNIVARIATE_UNIVARIATE_SPARSE_COEFFICIENTS_H_\n\n#include <stddef.h>\n\n#include <algorithm>\n#include <sstream>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"absl/hash/hash.h\"\n#include \"absl/numeric/internal/bits.h\"\n#include \"absl/types/span.h\"\n\n#include \"tachyon/base/buffer/copyable.h\"\n#include \"tachyon/base/containers/adapters.h\"\n#include \"tachyon/base/containers/cxx20_erase_vector.h\"\n#include \"tachyon/base/json/json.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/optional.h\"\n#include \"tachyon/base/ranges/algorithm.h\"\n#include \"tachyon/base/strings/string_util.h\"\n#include \"tachyon/math/polynomials/univariate/support_poly_operators.h\"\n\nnamespace tachyon {\nnamespace math {\n\ntemplate <typename F, size_t MaxDegree>\nclass UnivariateDenseCoefficients;\n\ntemplate <typename F>\nstruct UnivariateTerm {\n  size_t degree;\n  F coefficient;\n\n  UnivariateTerm operator+(const F& scalar) const {\n    return {degree, coefficient + scalar};\n  }\n\n  UnivariateTerm& operator+=(const F& scalar) {\n    coefficient += scalar;\n    return *this;\n  }\n\n  UnivariateTerm operator-(const F& scalar) const {\n    return {degree, coefficient - scalar};\n  }\n\n  UnivariateTerm& operator-=(const F& scalar) {\n    coefficient -= scalar;\n    return *this;\n  }\n\n  UnivariateTerm operator-() const { return {degree, -coefficient}; }\n\n  UnivariateTerm operator*(const F& scalar) const {\n    return {degree, coefficient * scalar};\n  }\n\n  UnivariateTerm& operator*=(const F& scalar) {\n    coefficient *= scalar;\n    return *this;\n  }\n\n  UnivariateTerm operator/(const F& scalar) const {\n    return UnivariateTerm{degree, unwrap(coefficient / scalar)};\n  }\n\n  UnivariateTerm& operator/=(const F& scalar) {\n    CHECK(coefficient /= scalar);\n    return *this;\n  }\n\n  bool operator<(const UnivariateTerm& other) const {\n    return degree < other.degree;\n  }\n  bool operator==(const UnivariateTerm& other) const {\n    return degree == other.degree && coefficient == other.coefficient;\n  }\n  bool operator!=(const UnivariateTerm& other) const {\n    return degree != other.degree || coefficient != other.coefficient;\n  }\n};\n\n// UnivariateSparseCoefficients class provides a representation for polynomials\n// where only non-zero coefficients are stored. This is efficient for\n// polynomials where most of the degrees have zero coefficients.\ntemplate <typename F, size_t MaxDegree>\nclass UnivariateSparseCoefficients {\n public:\n  constexpr static size_t kMaxDegree = MaxDegree;\n  constexpr static F kZero = F::Zero();\n\n  using Field = F;\n  using Point = F;\n  using Term = UnivariateTerm<F>;\n\n  constexpr UnivariateSparseCoefficients() = default;\n  constexpr explicit UnivariateSparseCoefficients(\n      const std::vector<Term>& terms, bool cleanup = false)\n      : terms_(terms) {\n    if (cleanup) RemoveZeros();\n    CHECK_LE(Degree(), kMaxDegree);\n  }\n  constexpr explicit UnivariateSparseCoefficients(std::vector<Term>&& terms,\n                                                  bool cleanup = false)\n      : terms_(std::move(terms)) {\n    if (cleanup) RemoveZeros();\n    CHECK_LE(Degree(), kMaxDegree);\n  }\n\n  constexpr static UnivariateSparseCoefficients CreateChecked(\n      const std::vector<Term>& terms) {\n    CHECK(base::ranges::is_sorted(terms.begin(), terms.end()));\n    return UnivariateSparseCoefficients(terms);\n  }\n\n  constexpr static UnivariateSparseCoefficients CreateChecked(\n      std::vector<Term>&& terms) {\n    CHECK(base::ranges::is_sorted(terms.begin(), terms.end()));\n    return UnivariateSparseCoefficients(std::move(terms));\n  }\n\n  constexpr static UnivariateSparseCoefficients Zero() {\n    return UnivariateSparseCoefficients();\n  }\n\n  constexpr static UnivariateSparseCoefficients One() {\n    return UnivariateSparseCoefficients({{0, F::One()}});\n  }\n\n  constexpr static UnivariateSparseCoefficients Random(size_t degree) {\n    // TODO(chokobole): Better idea?\n    std::vector<Term> terms;\n    for (size_t i = 0; i < degree + 1; ++i) {\n      F f = F::Random();\n      if (f.IsZero()) continue;\n      terms.push_back({i, std::move(f)});\n    }\n    return UnivariateSparseCoefficients(std::move(terms));\n  }\n\n  constexpr const std::vector<Term>& terms() const { return terms_; }\n  constexpr std::vector<Term>& terms() { return terms_; }\n\n  std::vector<Term>&& TakeTerms() && { return std::move(terms_); }\n\n  constexpr bool operator==(const UnivariateSparseCoefficients& other) const {\n    return terms_ == other.terms_;\n  }\n\n  constexpr bool operator!=(const UnivariateSparseCoefficients& other) const {\n    return !operator==(other);\n  }\n\n  constexpr bool IsClean() const {\n    for (const Term& term : terms_) {\n      if (term.coefficient.IsZero()) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  constexpr F& at(size_t i) {\n    F* ptr = const_cast<F*>(std::as_const(*this).GetCoefficient(i));\n    CHECK(ptr);\n    return *ptr;\n  }\n\n  constexpr const F& at(size_t i) const { return (*this)[i]; }\n\n  constexpr const F& operator[](size_t i) const {\n    const F* ptr = GetCoefficient(i);\n    if (ptr) return *ptr;\n    return kZero;\n  }\n\n  constexpr const F& GetLeadingCoefficient() const {\n    if (IsZero()) return kZero;\n    return terms_.back().coefficient;\n  }\n\n  constexpr bool IsZero() const { return terms_.empty(); }\n\n  constexpr bool IsOne() const {\n    return terms_.size() == 1 && terms_[0].degree == 0 &&\n           terms_[0].coefficient.IsOne();\n  }\n\n  constexpr size_t Degree() const {\n    if (IsZero()) return 0;\n    return terms_.back().degree;\n  }\n\n  constexpr size_t NumElements() const { return terms_.size(); }\n\n  constexpr F Evaluate(const Point& point) const {\n    if (IsZero()) return F::Zero();\n\n    static_assert(sizeof(size_t) == sizeof(uint64_t));\n    size_t num_powers = absl::numeric_internal::CountLeadingZeroes64(0) -\n                        absl::numeric_internal::CountLeadingZeroes64(Degree());\n    std::vector<Point> powers_of_2;\n    powers_of_2.reserve(num_powers);\n\n    Point p = point;\n    powers_of_2.push_back(p);\n    for (size_t i = 1; i < num_powers; ++i) {\n      p.SquareInPlace();\n      powers_of_2.push_back(p);\n    }\n\n    F sum = F::Zero();\n    for (const Term& term : terms_) {\n      sum += F::PowWithTable(powers_of_2, F(term.degree).ToBigInt()) *\n             term.coefficient;\n    }\n    return sum;\n  }\n\n  // Return coefficients where the original coefficients reduce their degree\n  // by categorizing coefficients into even and odd degrees,\n  // multiplying coefficients with odd degrees by a specified random field |r|,\n  // and summing them together.\n  constexpr UnivariateSparseCoefficients Fold(const Field& r) const {\n    std::vector<Term> terms;\n    terms.reserve(terms_.size() >> 1);\n    bool r_is_zero = r.IsZero();\n    for (const Term& term : terms_) {\n      if (term.degree % 2 == 0) {\n        terms.push_back({term.degree >> 1, term.coefficient});\n      } else {\n        if (!terms.empty() && terms.back().degree == (term.degree >> 1)) {\n          if (!r_is_zero) {\n            terms.back() += (term.coefficient * r);\n          }\n          if (terms.back().coefficient.IsZero()) {\n            terms.pop_back();\n          }\n        } else {\n          if (!r_is_zero) {\n            terms.push_back({term.degree >> 1, term.coefficient * r});\n          }\n        }\n      }\n    }\n    return UnivariateSparseCoefficients(std::move(terms));\n  }\n\n  std::string ToString() const {\n    if (IsZero()) return base::EmptyString();\n    std::stringstream ss;\n    bool has_term = false;\n    for (const Term& term : base::Reversed(terms_)) {\n      if (has_term) ss << \" + \";\n      has_term = true;\n      ss << term.coefficient.ToString();\n      if (term.degree == 0) {\n        // do nothing\n      } else if (term.degree == 1) {\n        ss << \" * x\";\n      } else {\n        ss << \" * x^\" << term.degree;\n      }\n    }\n    return ss.str();\n  }\n\n  void RemoveZeros() {\n    base::EraseIf(terms_, [](const Term& x) { return x.coefficient.IsZero(); });\n  }\n\n private:\n  friend class internal::UnivariatePolynomialOp<\n      UnivariateDenseCoefficients<F, MaxDegree>>;\n  friend class internal::UnivariatePolynomialOp<\n      UnivariateSparseCoefficients<F, MaxDegree>>;\n  friend class base::Copyable<UnivariateSparseCoefficients<F, MaxDegree>>;\n\n  constexpr const F* GetCoefficient(size_t i) const {\n    auto it = std::lower_bound(\n        terms_.begin(), terms_.end(), i,\n        [](const Term& term, size_t degree) { return term.degree < degree; });\n    if (it == terms_.end()) return nullptr;\n    if (it->degree != i) return nullptr;\n    return &it->coefficient;\n  }\n\n  std::vector<Term> terms_;\n};\n\ntemplate <typename H, typename F, size_t MaxDegree>\nH AbslHashValue(\n    H h, const UnivariateSparseCoefficients<F, MaxDegree>& coefficients) {\n  // NOTE(chokobole): We shouldn't hash only with a non-zero term.\n  // See https://abseil.io/docs/cpp/guides/hash#the-abslhashvalue-overload\n  F zero = F::Zero();\n  size_t degree = 0;\n  for (const UnivariateTerm<F>& term : coefficients.terms()) {\n    for (size_t i = degree; i < term.degree; ++i) {\n      h = H::combine(std::move(h), zero);\n    }\n    h = H::combine(std::move(h), term.coefficient);\n    degree = term.degree + 1;\n  }\n  for (size_t i = degree; i < MaxDegree + 1; ++i) {\n    h = H::combine(std::move(h), zero);\n  }\n  return h;\n}\n\n}  // namespace math\n\nnamespace base {\n\ntemplate <typename F>\nclass Copyable<typename math::UnivariateTerm<F>> {\n public:\n  using Term = math::UnivariateTerm<F>;\n  static bool WriteTo(const Term& term, Buffer* buffer) {\n    return buffer->WriteMany(term.degree, term.coefficient);\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer, Term* term) {\n    size_t degree;\n    F coefficient;\n    if (!buffer.ReadMany(&degree, &coefficient)) return false;\n    *term = {degree, std::move(coefficient)};\n    return true;\n  }\n\n  static size_t EstimateSize(const Term& term) {\n    return base::EstimateSize(term.degree, term.coefficient);\n  }\n};\n\ntemplate <typename F, size_t MaxDegree>\nclass Copyable<math::UnivariateSparseCoefficients<F, MaxDegree>> {\n public:\n  static bool WriteTo(\n      const math::UnivariateSparseCoefficients<F, MaxDegree>& coeffs,\n      Buffer* buffer) {\n    return buffer->Write(coeffs.terms_);\n  }\n\n  static bool ReadFrom(\n      const ReadOnlyBuffer& buffer,\n      math::UnivariateSparseCoefficients<F, MaxDegree>* coeffs) {\n    std::vector<math::UnivariateTerm<F>> terms;\n    if (!buffer.Read(&terms)) return false;\n    *coeffs = math::UnivariateSparseCoefficients<F, MaxDegree>(terms);\n    return true;\n  }\n\n  static size_t EstimateSize(\n      const math::UnivariateSparseCoefficients<F, MaxDegree>& coeffs) {\n    return base::EstimateSize(coeffs.terms_);\n  }\n};\n\ntemplate <typename F>\nclass RapidJsonValueConverter<math::UnivariateTerm<F>> {\n public:\n  template <typename Allocator>\n  static rapidjson::Value From(const math::UnivariateTerm<F>& value,\n                               Allocator& allocator) {\n    rapidjson::Value object(rapidjson::kObjectType);\n    AddJsonElement(object, \"degree\", value.degree, allocator);\n    AddJsonElement(object, \"coefficient\", value.coefficient, allocator);\n    return object;\n  }\n\n  static bool To(const rapidjson::Value& json_value, std::string_view key,\n                 math::UnivariateTerm<F>* value, std::string* error) {\n    size_t degree;\n    F coefficient;\n    if (!ParseJsonElement(json_value, \"degree\", &degree, error)) return false;\n    if (!ParseJsonElement(json_value, \"coefficient\", &coefficient, error))\n      return false;\n    value->degree = degree;\n    value->coefficient = std::move(coefficient);\n    return true;\n  }\n};\n\ntemplate <typename F, size_t MaxDegree>\nclass RapidJsonValueConverter<\n    math::UnivariateSparseCoefficients<F, MaxDegree>> {\n public:\n  template <typename Allocator>\n  static rapidjson::Value From(\n      const math::UnivariateSparseCoefficients<F, MaxDegree>& value,\n      Allocator& allocator) {\n    rapidjson::Value object(rapidjson::kObjectType);\n    AddJsonElement(object, \"terms\", value.terms(), allocator);\n    return object;\n  }\n\n  static bool To(const rapidjson::Value& json_value, std::string_view key,\n                 math::UnivariateSparseCoefficients<F, MaxDegree>* value,\n                 std::string* error) {\n    std::vector<math::UnivariateTerm<F>> terms;\n    if (!ParseJsonElement(json_value, \"terms\", &terms, error)) return false;\n    *value = math::UnivariateSparseCoefficients<F, MaxDegree>(std::move(terms));\n    return true;\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_MATH_POLYNOMIALS_UNIVARIATE_UNIVARIATE_SPARSE_COEFFICIENTS_H_\n"
  },
  {
    "path": "tachyon/math/polynomials/univariate/univariate_sparse_polynomial_unittest.cc",
    "content": "#include <optional>\n\n#include \"absl/hash/hash_testing.h\"\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/buffer/vector_buffer.h\"\n#include \"tachyon/base/containers/cxx20_erase_vector.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_polynomial.h\"\n\nnamespace tachyon::math {\n\nnamespace {\n\nconst size_t kMaxDegree = 5;\n\nusing Poly = UnivariateSparsePolynomial<GF7, kMaxDegree>;\nusing Coeffs = UnivariateSparseCoefficients<GF7, kMaxDegree>;\n\nclass UnivariateSparsePolynomialTest : public FiniteFieldTest<GF7> {\n public:\n  void SetUp() override {\n    polys_.push_back(Poly(Coeffs({{0, GF7(3)}, {2, GF7(1)}, {4, GF7(2)}})));\n    polys_.push_back(Poly(Coeffs({{0, GF7(3)}})));\n    polys_.push_back(Poly(Coeffs({{3, GF7(5)}})));\n    polys_.push_back(Poly(Coeffs({{4, GF7(5)}})));\n    polys_.push_back(Poly(Coeffs({{0, GF7(3)}, {1, GF7(4)}})));\n    polys_.push_back(Poly(Coeffs({{0, GF7(3)}, {1, GF7(4)}, {2, GF7(1)}})));\n    polys_.push_back(Poly::Zero());\n  }\n\n protected:\n  std::vector<Poly> polys_;\n};\n\n}  // namespace\n\nTEST_F(UnivariateSparsePolynomialTest, IsZero) {\n  EXPECT_TRUE(Poly().IsZero());\n  EXPECT_TRUE(Poly::Zero().IsZero());\n  EXPECT_TRUE(Poly(Coeffs({{0, GF7(0)}, {1, GF7(0)}}, true)).IsZero());\n\n  for (size_t i = 0; i < polys_.size() - 1; ++i) {\n    EXPECT_FALSE(polys_[i].IsZero());\n  }\n  EXPECT_TRUE(polys_[polys_.size() - 1].IsZero());\n}\n\nTEST_F(UnivariateSparsePolynomialTest, IsOne) {\n  EXPECT_TRUE(Poly::One().IsOne());\n  EXPECT_TRUE(Poly(Coeffs({{0, GF7(1)}})).IsOne());\n  EXPECT_FALSE(Poly(Coeffs({{1, GF7(1)}})).IsOne());\n  for (size_t i = 0; i < polys_.size(); ++i) {\n    EXPECT_FALSE(polys_[i].IsOne());\n  }\n}\n\nTEST_F(UnivariateSparsePolynomialTest, Random) {\n  bool success = false;\n  Poly r = Poly::Random(kMaxDegree);\n  for (size_t i = 0; i < 100; ++i) {\n    if (r != Poly::Random(kMaxDegree)) {\n      success = true;\n      break;\n    }\n  }\n  EXPECT_TRUE(success);\n}\n\nTEST_F(UnivariateSparsePolynomialTest, CreateChecked) {\n  EXPECT_DEATH(Coeffs::CreateChecked({{2, GF7(3)}, {0, GF7(1)}, {4, GF7(2)}}),\n               \"\");\n  EXPECT_EQ(\n      Poly(Coeffs::CreateChecked({{0, GF7(3)}, {2, GF7(1)}, {4, GF7(2)}})),\n      polys_[0]);\n}\n\nTEST_F(UnivariateSparsePolynomialTest, IndexingOperator) {\n  struct {\n    const Poly& poly;\n    std::vector<std::optional<int>> coefficients;\n  } tests[] = {\n      {polys_[0], {3, std::nullopt, 1, std::nullopt, 2}},\n      {polys_[1], {3}},\n      {polys_[2], {std::nullopt, std::nullopt, std::nullopt, 5}},\n      {polys_[3], {std::nullopt, std::nullopt, std::nullopt, std::nullopt, 5}},\n      {polys_[4], {3, 4}},\n      {polys_[5], {3, 4, 1}},\n      {polys_[6], {}},\n  };\n\n  for (const auto& test : tests) {\n    for (size_t i = 0; i < kMaxDegree; ++i) {\n      if (i < test.coefficients.size()) {\n        if (test.coefficients[i].has_value()) {\n          EXPECT_EQ(test.poly[i], GF7(test.coefficients[i].value()));\n        } else {\n          EXPECT_EQ(test.poly[i], GF7::Zero());\n        }\n      } else {\n        EXPECT_EQ(test.poly[i], GF7::Zero());\n      }\n    }\n  }\n}\n\nTEST_F(UnivariateSparsePolynomialTest, Degree) {\n  struct {\n    const Poly& poly;\n    size_t degree;\n  } tests[] = {\n      {polys_[0], 4}, {polys_[1], 0}, {polys_[2], 3}, {polys_[3], 4},\n      {polys_[4], 1}, {polys_[5], 2}, {polys_[6], 0},\n  };\n\n  for (const auto& test : tests) {\n    EXPECT_EQ(test.poly.Degree(), test.degree);\n  }\n  EXPECT_LE(Poly::Random(kMaxDegree).Degree(), kMaxDegree);\n}\n\nTEST_F(UnivariateSparsePolynomialTest, Evaluate) {\n  struct {\n    const Poly& poly;\n    GF7 expected;\n  } tests[] = {\n      {polys_[0], GF7(6)}, {polys_[1], GF7(3)}, {polys_[2], GF7(2)},\n      {polys_[3], GF7(6)}, {polys_[4], GF7(1)}, {polys_[5], GF7(3)},\n      {polys_[6], GF7(0)},\n  };\n\n  for (const auto& test : tests) {\n    EXPECT_EQ(test.poly.Evaluate(GF7(3)), test.expected);\n  }\n}\n\nTEST_F(UnivariateSparsePolynomialTest, ToString) {\n  struct {\n    const Poly& poly;\n    std::string_view expected;\n  } tests[] = {\n      {polys_[0], \"2 * x^4 + 1 * x^2 + 3\"},\n      {polys_[1], \"3\"},\n      {polys_[2], \"5 * x^3\"},\n      {polys_[3], \"5 * x^4\"},\n      {polys_[4], \"4 * x + 3\"},\n      {polys_[5], \"1 * x^2 + 4 * x + 3\"},\n      {polys_[6], \"\"},\n  };\n\n  for (const auto& test : tests) {\n    EXPECT_EQ(test.poly.ToString(), test.expected);\n  }\n}\n\nTEST_F(UnivariateSparsePolynomialTest, AdditiveOperators) {\n  struct {\n    const Poly& a;\n    const Poly& b;\n    Poly sum;\n    Poly amb;\n    Poly bma;\n  } tests[] = {\n      {\n          polys_[0],\n          polys_[1],\n          Poly(Coeffs({{0, GF7(6)}, {2, GF7(1)}, {4, GF7(2)}})),\n          Poly(Coeffs({{2, GF7(1)}, {4, GF7(2)}})),\n          Poly(Coeffs({{2, GF7(6)}, {4, GF7(5)}})),\n      },\n      {\n          polys_[0],\n          polys_[2],\n          Poly(Coeffs({{0, GF7(3)}, {2, GF7(1)}, {3, GF7(5)}, {4, GF7(2)}})),\n          Poly(Coeffs({{0, GF7(3)}, {2, GF7(1)}, {3, GF7(2)}, {4, GF7(2)}})),\n          Poly(Coeffs({{0, GF7(4)}, {2, GF7(6)}, {3, GF7(5)}, {4, GF7(5)}})),\n      },\n      {\n          polys_[0],\n          polys_[3],\n          Poly(Coeffs({{0, GF7(3)}, {2, GF7(1)}})),\n          Poly(Coeffs({{0, GF7(3)}, {2, GF7(1)}, {4, GF7(4)}})),\n          Poly(Coeffs({{0, GF7(4)}, {2, GF7(6)}, {4, GF7(3)}})),\n      },\n      {\n          polys_[0],\n          polys_[4],\n          Poly(Coeffs({{0, GF7(6)}, {1, GF7(4)}, {2, GF7(1)}, {4, GF7(2)}})),\n          Poly(Coeffs({{1, GF7(3)}, {2, GF7(1)}, {4, GF7(2)}})),\n          Poly(Coeffs({{1, GF7(4)}, {2, GF7(6)}, {4, GF7(5)}})),\n      },\n      {\n          polys_[0],\n          polys_[5],\n          Poly(Coeffs({{0, GF7(6)}, {1, GF7(4)}, {2, GF7(2)}, {4, GF7(2)}})),\n          Poly(Coeffs({{1, GF7(3)}, {4, GF7(2)}})),\n          Poly(Coeffs({{1, GF7(4)}, {4, GF7(5)}})),\n      },\n      {\n          polys_[0],\n          polys_[6],\n          Poly(Coeffs({{0, GF7(3)}, {2, GF7(1)}, {4, GF7(2)}})),\n          Poly(Coeffs({{0, GF7(3)}, {2, GF7(1)}, {4, GF7(2)}})),\n          Poly(Coeffs({{0, GF7(4)}, {2, GF7(6)}, {4, GF7(5)}})),\n      },\n  };\n\n  for (const auto& test : tests) {\n    const auto a_dense = test.a.ToDense();\n    const auto b_dense = test.b.ToDense();\n    const auto sum_dense = test.sum.ToDense();\n    EXPECT_EQ(test.a + test.b, test.sum);\n    EXPECT_EQ(test.b + test.a, test.sum);\n    EXPECT_EQ(test.a + b_dense, sum_dense);\n    EXPECT_EQ(test.b + a_dense, sum_dense);\n    EXPECT_EQ(test.a - test.b, test.amb);\n    EXPECT_EQ(test.b - test.a, test.bma);\n    EXPECT_EQ(test.a - b_dense, test.amb.ToDense());\n    EXPECT_EQ(test.b - a_dense, test.bma.ToDense());\n\n    Poly tmp = test.a;\n    tmp += test.b;\n    EXPECT_EQ(tmp, test.sum);\n    tmp -= test.b;\n    EXPECT_EQ(tmp, test.a);\n  }\n}\n\nTEST_F(UnivariateSparsePolynomialTest, MultiplicativeOperators) {\n  Poly a(Coeffs({{0, GF7(3)}, {1, GF7(1)}}));\n  Poly b(Coeffs({{0, GF7(5)}, {1, GF7(2)}, {2, GF7(5)}}));\n  Poly one = Poly::One();\n  Poly zero = Poly::Zero();\n\n  using DensePoly = UnivariateDensePolynomial<GF7, kMaxDegree>;\n  using DenseCoeffs = UnivariateDenseCoefficients<GF7, kMaxDegree>;\n\n  struct {\n    const Poly& a;\n    const Poly& b;\n    Poly mul;\n    DensePoly adb;\n    DensePoly amb;\n    DensePoly bda;\n    DensePoly bma;\n  } tests[] = {\n      {\n          a,\n          b,\n          Poly(Coeffs({{0, GF7(1)}, {1, GF7(4)}, {2, GF7(3)}, {3, GF7(5)}})),\n          zero.ToDense(),\n          a.ToDense(),\n          DensePoly(DenseCoeffs({GF7(1), GF7(5)})),\n          DensePoly(DenseCoeffs({GF7(2)})),\n      },\n      {\n          a,\n          one,\n          a,\n          a.ToDense(),\n          zero.ToDense(),\n          zero.ToDense(),\n          one.ToDense(),\n      },\n      {\n          a,\n          zero,\n          zero,\n          zero.ToDense(),\n          zero.ToDense(),\n          zero.ToDense(),\n          a.ToDense(),\n      },\n  };\n\n  for (const auto& test : tests) {\n    const auto a_dense = test.a.ToDense();\n    const auto b_dense = test.b.ToDense();\n    const auto mul_dense = test.mul.ToDense();\n    EXPECT_EQ(test.a * test.b, test.mul);\n    EXPECT_EQ(test.b * test.a, test.mul);\n    if (!test.b.IsZero()) {\n      EXPECT_EQ(test.a / test.b, test.adb);\n      EXPECT_EQ(test.a % test.b, test.amb);\n    } else {\n      ASSERT_FALSE(test.a / test.b);\n    }\n    if (!test.a.IsZero()) {\n      EXPECT_EQ(test.b / test.a, test.bda);\n      EXPECT_EQ(test.b % test.a, test.bma);\n    } else {\n      ASSERT_FALSE(test.b / test.a);\n    }\n    EXPECT_EQ(test.a * b_dense, mul_dense);\n    EXPECT_EQ(test.b * a_dense, mul_dense);\n    if (!b_dense.IsZero()) {\n      EXPECT_EQ(test.a / b_dense, test.adb);\n      EXPECT_EQ(test.a % b_dense, test.amb);\n    } else {\n      ASSERT_FALSE(test.a / b_dense);\n    }\n    if (!a_dense.IsZero()) {\n      EXPECT_EQ(test.b / a_dense, test.bda);\n      EXPECT_EQ(test.b % a_dense, test.bma);\n    } else {\n      ASSERT_FALSE(test.a / a_dense);\n    }\n\n    Poly tmp = test.a;\n    tmp *= test.b;\n    EXPECT_EQ(tmp, test.mul);\n  }\n}\n\nTEST_F(UnivariateSparsePolynomialTest, MulScalar) {\n  Poly poly = Poly::Random(kMaxDegree);\n  GF7 scalar = GF7::Random();\n\n  std::vector<UnivariateTerm<GF7>> expected_terms;\n  const std::vector<UnivariateTerm<GF7>>& terms = poly.coefficients().terms();\n  expected_terms.reserve(terms.size());\n  for (size_t i = 0; i < terms.size(); ++i) {\n    expected_terms.push_back(terms[i] * scalar);\n  }\n\n  Poly actual = poly * scalar;\n  Poly expected(Coeffs(std::move(expected_terms), true));\n  EXPECT_EQ(actual, expected);\n  poly *= scalar;\n  EXPECT_EQ(poly, expected);\n}\n\nTEST_F(UnivariateSparsePolynomialTest, DivScalar) {\n  Poly poly = Poly::Random(kMaxDegree);\n  GF7 scalar = GF7::Random();\n  // NOTE(ashjeong): |scalar| is guaranteed to not be zero\n  while (scalar.IsZero()) {\n    scalar = GF7::Random();\n  }\n\n  std::vector<UnivariateTerm<GF7>> expected_terms;\n  const std::vector<UnivariateTerm<GF7>>& terms = poly.coefficients().terms();\n  expected_terms.reserve(terms.size());\n  for (size_t i = 0; i < terms.size(); ++i) {\n    expected_terms.push_back(terms[i] / scalar);\n  }\n\n  Poly actual = unwrap(poly / scalar);\n  Poly expected(Coeffs(std::move(expected_terms)));\n  EXPECT_EQ(actual, expected);\n  ASSERT_TRUE(poly /= scalar);\n  EXPECT_EQ(poly, expected);\n}\n\nTEST_F(UnivariateSparsePolynomialTest, FromRoots) {\n  // poly = x⁴ + 2x² + 4 = (x - 1)(x - 2)(x + 1)(x + 2)\n  Poly poly = Poly(Coeffs({{0, GF7(4)}, {2, GF7(2)}, {4, GF7(1)}}));\n  std::vector<GF7> roots = {GF7(1), GF7(2), GF7(6), GF7(5)};\n  EXPECT_EQ(Poly::FromRoots(roots), poly);\n}\n\nTEST_F(UnivariateSparsePolynomialTest, EvaluateVanishingPolyByRoots) {\n  // poly = x⁴ + 2x² + 4 = (x - 1)(x - 2)(x + 1)(x + 2)\n  Poly poly = Poly(Coeffs({{0, GF7(4)}, {2, GF7(2)}, {4, GF7(1)}}));\n  std::vector<GF7> roots = {GF7(1), GF7(2), GF7(6), GF7(5)};\n  GF7 point = GF7::Random();\n  EXPECT_EQ(Poly::EvaluateVanishingPolyByRoots(roots, point),\n            poly.Evaluate(point));\n}\n\nTEST_F(UnivariateSparsePolynomialTest, Fold) {\n  Poly poly = Poly::Random(kMaxDegree);\n  GF7 r = GF7::Random();\n  Poly folded = poly.Fold(r);\n  std::vector<UnivariateTerm<GF7>> terms{{0, poly[0] + r * poly[1]},\n                                         {1, poly[2] + r * poly[3]},\n                                         {2, poly[4] + r * poly[5]}};\n  base::EraseIf(terms, [](const UnivariateTerm<GF7>& term) {\n    return term.coefficient.IsZero();\n  });\n  EXPECT_EQ(folded, Poly(Coeffs(std::move(terms))));\n\n  GF7 r2 = GF7::Random();\n  Poly folded2 = folded.Fold(r2);\n  terms = {{0, folded[0] + r2 * folded[1]}, {1, folded[2]}};\n  base::EraseIf(terms, [](const UnivariateTerm<GF7>& term) {\n    return term.coefficient.IsZero();\n  });\n  EXPECT_EQ(folded2, Poly(Coeffs(std::move(terms))));\n\n  GF7 r3 = GF7::Random();\n  Poly folded3 = folded2.Fold(r3);\n  terms = {{0, folded2[0] + r3 * folded2[1]}};\n  base::EraseIf(terms, [](const UnivariateTerm<GF7>& term) {\n    return term.coefficient.IsZero();\n  });\n  EXPECT_EQ(folded3, Poly(Coeffs(std::move(terms))));\n}\n\nTEST_F(UnivariateSparsePolynomialTest, Copyable) {\n  Poly expected = Poly::Random(kMaxDegree);\n\n  base::Uint8VectorBuffer write_buf;\n  ASSERT_TRUE(write_buf.Grow(base::EstimateSize(expected)));\n  ASSERT_TRUE(write_buf.Write(expected));\n  ASSERT_TRUE(write_buf.Done());\n\n  write_buf.set_buffer_offset(0);\n\n  Poly value;\n  ASSERT_TRUE(write_buf.Read(&value));\n  EXPECT_EQ(expected, value);\n}\n\nTEST_F(UnivariateSparsePolynomialTest, Hash) {\n  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(\n      std::make_tuple(Poly(), Poly::Zero(), Poly::One(),\n                      Poly::Random(kMaxDegree), Poly::Random(kMaxDegree))));\n}\n\nTEST_F(UnivariateSparsePolynomialTest, JsonValueConverter) {\n  Poly expected_poly(Coeffs({{0, GF7(1)}, {1, GF7(2)}, {4, GF7(3)}}));\n  std::string expected_json =\n      R\"({\"coefficients\":{\"terms\":[{\"degree\":0,\"coefficient\":1},{\"degree\":1,\"coefficient\":2},{\"degree\":4,\"coefficient\":3}]}})\";\n\n  Poly poly;\n  std::string error;\n  ASSERT_TRUE(base::ParseJson(expected_json, &poly, &error));\n  ASSERT_TRUE(error.empty());\n  EXPECT_EQ(poly, expected_poly);\n\n  std::string json = base::WriteToJson(poly);\n  EXPECT_EQ(json, expected_json);\n}\n\n}  // namespace tachyon::math\n"
  },
  {
    "path": "tachyon/math/test/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"launch_op_macros\",\n    testonly = True,\n    hdrs = [\"launch_op_macros.h\"],\n    deps = [\n        \"//tachyon/device/gpu:gpu_device_functions\",\n        \"//tachyon/device/gpu:gpu_logging\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/math/test/launch_op_macros.h",
    "content": "#ifndef TACHYON_MATH_TEST_LAUNCH_OP_MACROS_H_\n#define TACHYON_MATH_TEST_LAUNCH_OP_MACROS_H_\n\n#include \"tachyon/device/gpu/gpu_device_functions.h\"\n#include \"tachyon/device/gpu/gpu_logging.h\"\n\n#define DEFINE_LAUNCH_UNARY_OP(thread_num, method, type, result_type)   \\\n  gpuError_t Launch##method(const type* x, result_type* result,         \\\n                            size_t count) {                             \\\n    ::tachyon::math::kernels::                                          \\\n        method<<<(count - 1) / thread_num + 1, thread_num>>>(x, result, \\\n                                                             count);    \\\n    gpuError_t error = LOG_IF_GPU_LAST_ERROR(\"Failed \" #method \"()\");   \\\n    return error == gpuSuccess                                          \\\n               ? LOG_IF_GPU_ERROR(gpuDeviceSynchronize(),               \\\n                                  \"Failed gpuDeviceSynchronize()\")      \\\n               : error;                                                 \\\n  }\n\n#define DEFINE_LAUNCH_BINARY_OP(thread_num, method, type, result_type)         \\\n  gpuError_t Launch##method(const type* x, const type* y, result_type* result, \\\n                            size_t count) {                                    \\\n    ::tachyon::math::kernels::                                                 \\\n        method<<<(count - 1) / thread_num + 1, thread_num>>>(x, y, result,     \\\n                                                             count);           \\\n    gpuError_t error = LOG_IF_GPU_LAST_ERROR(\"Failed \" #method \"()\");          \\\n    return error == gpuSuccess                                                 \\\n               ? LOG_IF_GPU_ERROR(gpuDeviceSynchronize(),                      \\\n                                  \"Failed gpuDeviceSynchronize()\")             \\\n               : error;                                                        \\\n  }\n\n#endif  // TACHYON_MATH_TEST_LAUNCH_OP_MACROS_H_\n"
  },
  {
    "path": "tachyon/node/BUILD.bazel",
    "content": "load(\"//bazel:tachyon.bzl\", \"if_node_binding\")\nload(\"//bazel:tachyon_node.bzl\", \"tachyon_node_library\")\n\ntachyon_node_library(\n    name = \"tachyon\",\n    srcs = if_node_binding([\"tachyon.cc\"]),\n    deps = if_node_binding([\n        \"//tachyon/node/math\",\n    ]),\n)\n"
  },
  {
    "path": "tachyon/node/base/BUILD.bazel",
    "content": "load(\"//bazel:tachyon.bzl\", \"if_node_binding\")\nload(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\ntachyon_cc_library(\n    name = \"node_base\",\n    srcs = if_node_binding([\n        \"node_constructors.cc\",\n        \"node_constructors.h\",\n        \"node_cpp_bindable.cc\",\n        \"node_cpp_bindable.h\",\n        \"node_cpp_callable.h\",\n        \"node_cpp_callable_data.h\",\n        \"node_cpp_constructor.cc\",\n        \"node_cpp_constructor.h\",\n        \"node_cpp_constructor_matcher.h\",\n        \"node_cpp_object.h\",\n        \"node_errors.cc\",\n        \"node_internals.h\",\n        \"node_internals_forwards.h\",\n        \"node_module.cc\",\n    ]),\n    hdrs = [\n        \"node_cpp_class.h\",\n        \"node_cpp_enum.h\",\n        \"node_errors.h\",\n        \"node_module.h\",\n    ],\n    visibility = [\"//visibility:public\"],\n    deps = [\n        \"//tachyon/base:logging\",\n        \"//tachyon/base:template_util\",\n        \"//tachyon/base/binding:binding_util\",\n        \"//tachyon/base/functional:functor_traits\",\n        \"//tachyon/base/numerics:safe_conversions\",\n        \"@com_google_absl//absl/container:flat_hash_map\",\n        \"@com_google_absl//absl/container:inlined_vector\",\n        \"@com_google_absl//absl/strings\",\n    ] + if_node_binding([\n        \"@node_addon_api\",\n    ]),\n)\n"
  },
  {
    "path": "tachyon/node/base/node_constructors.cc",
    "content": "#include \"tachyon/node/base/node_constructors.h\"\n\n#include <utility>\n\nnamespace tachyon::node {\n\n// static\nNodeConstructors& NodeConstructors::GetInstance() {\n  static base::NoDestructor<NodeConstructors> node_constructors;\n  return *node_constructors;\n}\n\nNodeConstructors::NodeConstructors() = default;\n\nNodeConstructors::~NodeConstructors() = default;\n\nvoid NodeConstructors::AddConstructor(std::string_view type,\n                                      Napi::FunctionReference constructor) {\n  constructors_[type] = std::move(constructor);\n}\n\nbool NodeConstructors::GetConstructor(std::string_view type,\n                                      Napi::Function* constructor) const {\n  auto it = constructors_.find(type);\n  if (it == constructors_.end()) return false;\n  if (it->second.IsEmpty()) return false;\n  *constructor = it->second.Value();\n  return true;\n}\n\nbool NodeConstructors::InstanceOf(Napi::Object object, std::string_view name) {\n  Napi::Function constructor;\n  if (!GetConstructor(name, &constructor)) return false;\n  return object.InstanceOf(constructor);\n}\n\n}  // namespace tachyon::node\n"
  },
  {
    "path": "tachyon/node/base/node_constructors.h",
    "content": "#ifndef TACHYON_NODE_BASE_NODE_CONSTRUCTORS_H_\n#define TACHYON_NODE_BASE_NODE_CONSTRUCTORS_H_\n\n#include <string>\n#include <string_view>\n\n#include \"absl/container/flat_hash_map.h\"\n#include \"third_party/node_addon_api/napi.h\"\n\n#include \"tachyon/base/no_destructor.h\"\n#include \"tachyon/export.h\"\n\nnamespace tachyon::node {\n\nclass TACHYON_EXPORT NodeConstructors {\n public:\n  static NodeConstructors& GetInstance();\n\n  NodeConstructors(const NodeConstructors& other) = delete;\n  NodeConstructors& operator=(const NodeConstructors& other) = delete;\n  ~NodeConstructors();\n\n  void AddConstructor(std::string_view type,\n                      Napi::FunctionReference constructor);\n  bool GetConstructor(std::string_view type, Napi::Function* constructor) const;\n\n  bool InstanceOf(Napi::Object object, std::string_view name);\n\n private:\n  friend class base::NoDestructor<NodeConstructors>;\n\n  NodeConstructors();\n\n  absl::flat_hash_map<std::string, Napi::FunctionReference> constructors_;\n};\n\n}  // namespace tachyon::node\n\n#endif  // TACHYON_NODE_BASE_NODE_CONSTRUCTORS_H_\n"
  },
  {
    "path": "tachyon/node/base/node_cpp_bindable.cc",
    "content": "#include \"tachyon/node/base/node_cpp_bindable.h\"\n\n#include \"absl/strings/substitute.h\"\n\nnamespace tachyon::node {\n\nNodeCppBindable::NodeCppBindable(Napi::Env env, Napi::Object exports)\n    : env_(env), exports_(exports) {}\n\nNodeCppBindable::NodeCppBindable(Napi::Env env, Napi::Object exports,\n                                 std::string_view name,\n                                 std::string_view parent_full_name)\n    : env_(env),\n      exports_(exports),\n      name_(name),\n      full_name_(parent_full_name.empty()\n                     ? std::string(name)\n                     : absl::Substitute(\"$0.$1\", parent_full_name, name_)) {}\n\nNodeCppBindable::NodeCppBindable(NodeCppBindable&& other) noexcept = default;\n\nNodeCppBindable& NodeCppBindable::operator=(NodeCppBindable&& other) noexcept =\n    default;\n\nNodeCppBindable::~NodeCppBindable() = default;\n\n}  // namespace tachyon::node\n"
  },
  {
    "path": "tachyon/node/base/node_cpp_bindable.h",
    "content": "#ifndef TACHYON_NODE_BASE_NODE_CPP_BINDABLE_H_\n#define TACHYON_NODE_BASE_NODE_CPP_BINDABLE_H_\n\n#include <string>\n#include <string_view>\n\n#include \"third_party/node_addon_api/napi.h\"\n\n#include \"tachyon/export.h\"\n\nnamespace tachyon::node {\n\nclass TACHYON_EXPORT NodeCppBindable {\n public:\n  NodeCppBindable(Napi::Env env, Napi::Object exports);\n  NodeCppBindable(Napi::Env env, Napi::Object exports, std::string_view name,\n                  std::string_view parent_full_name);\n  NodeCppBindable(const NodeCppBindable& other) = delete;\n  NodeCppBindable& operator=(const NodeCppBindable& other) = delete;\n  NodeCppBindable(NodeCppBindable&& other) noexcept;\n  NodeCppBindable& operator=(NodeCppBindable&& other) noexcept;\n  ~NodeCppBindable();\n\n protected:\n  Napi::Env env_;\n  Napi::Object exports_;\n  std::string_view name_;\n  std::string full_name_;\n};\n\n}  // namespace tachyon::node\n\n#endif  // TACHYON_NODE_BASE_NODE_CPP_BINDABLE_H_\n"
  },
  {
    "path": "tachyon/node/base/node_cpp_callable.h",
    "content": "#ifndef TACHYON_NODE_BASE_NODE_CPP_CALLABLE_H_\n#define TACHYON_NODE_BASE_NODE_CPP_CALLABLE_H_\n\n#include <memory>\n#include <tuple>\n#include <utility>\n#include <vector>\n\n#include \"absl/strings/substitute.h\"\n#include \"third_party/node_addon_api/napi.h\"\n\n#include \"tachyon/base/binding/callable_util.h\"\n#include \"tachyon/base/binding/cpp_value.h\"\n#include \"tachyon/base/binding/property_util.h\"\n#include \"tachyon/base/functional/functor_traits.h\"\n#include \"tachyon/node/base/node_cpp_callable_data.h\"\n#include \"tachyon/node/base/node_errors.h\"\n#include \"tachyon/node/base/node_internals_forwards.h\"\n\nnamespace tachyon::node {\n\nnamespace internal {\n\ninline bool CallbackInfoToTuple(const Napi::CallbackInfo& info, size_t nargs,\n                                std::tuple<>* v, std::make_index_sequence<0>) {\n  return true;\n}\n\ntemplate <typename Tuple, size_t N>\nbool CallbackInfoToTuple(const Napi::CallbackInfo& info, size_t nargs, Tuple* v,\n                         std::index_sequence<N>) {\n  if (N >= nargs) return true;\n  if (!ToNativeValue(info[N], &std::get<N>(*v))) {\n    NAPI_THROW(InvalidArgument(info.Env(), N), false);\n  }\n  return true;\n}\n\ntemplate <typename Tuple, size_t N, size_t... Is>\nbool CallbackInfoToTuple(const Napi::CallbackInfo& info, size_t nargs, Tuple* v,\n                         std::index_sequence<N, N + 1, Is...>) {\n  if (!CallbackInfoToTuple(info, nargs, v, std::index_sequence<N>{}))\n    return false;\n  return CallbackInfoToTuple(info, nargs, v,\n                             std::index_sequence<N + 1, Is...>{});\n}\n\ntemplate <typename Tuple>\nbool CallbackInfoToTuple(const Napi::CallbackInfo& info, size_t nargs,\n                         Tuple* v) {\n  return CallbackInfoToTuple(\n      info, nargs, v,\n      std::make_index_sequence<std::tuple_size<Tuple>::value>{});\n}\n\ninline void SetDefaultArguments(std::tuple<>, std::make_index_sequence<0>,\n                                const std::vector<void*>&, size_t&) {}\n\ntemplate <typename Tuple, size_t Idx>\nvoid SetDefaultArguments(Tuple& args, std::index_sequence<Idx>,\n                         const std::vector<void*>& default_args,\n                         size_t& required_default_args) {\n  using T = std::tuple_element_t<Idx, Tuple>;\n  constexpr size_t N = std::tuple_size<Tuple>::value;\n  if (Idx < N - required_default_args) return;\n  std::get<Idx>(args) = std::move(*reinterpret_cast<T*>(\n      default_args[default_args.size() - required_default_args]));\n  --required_default_args;\n}\n\ntemplate <typename Tuple, size_t Idx, size_t... Is>\nvoid SetDefaultArguments(Tuple& args, std::index_sequence<Idx, Idx + 1, Is...>,\n                         const std::vector<void*>& default_args,\n                         size_t& required_default_args) {\n  SetDefaultArguments(args, std::index_sequence<Idx>{}, default_args,\n                      required_default_args);\n  SetDefaultArguments(args, std::index_sequence<Idx + 1, Is...>{}, default_args,\n                      required_default_args);\n}\n\ntemplate <typename Tuple>\nvoid SetDefaultArguments(Tuple& args, const std::vector<void*>& default_args,\n                         size_t& required_default_args) {\n  SetDefaultArguments(args,\n                      std::make_index_sequence<std::tuple_size<Tuple>::value>{},\n                      default_args, required_default_args);\n}\n\n}  // namespace internal\n\nclass NodeCppCallable {\n  template <typename ArgList>\n  using ConvertTypeListToDeclarableTuple =\n      base::internal::ConvertTypeListToDeclarableTuple<\n          internal::HasToNativeValue, ArgList>;\n\n#define PRE_CALL(info, data, ...)                                  \\\n  using Tuple = ConvertTypeListToDeclarableTuple<ArgList>;         \\\n  Tuple args;                                                      \\\n  size_t nargs = info.Length();                                    \\\n  constexpr size_t N = std::tuple_size<Tuple>::value;              \\\n  if (nargs > N) {                                                 \\\n    NAPI_THROW(WrongNumberOfArguments(info.Env()), __VA_ARGS__);   \\\n  } else if (nargs < N) {                                          \\\n    size_t required_default_args = N - nargs;                      \\\n    if (data->default_args.size() < required_default_args) {       \\\n      NAPI_THROW(WrongNumberOfArguments(info.Env()), __VA_ARGS__); \\\n    }                                                              \\\n    internal::SetDefaultArguments(args, data->default_args,        \\\n                                  required_default_args);          \\\n  }                                                                \\\n  internal::CallbackInfoToTuple(info, nargs, &args);               \\\n  if (info.Env().IsExceptionPending()) {                           \\\n    return __VA_ARGS__;                                            \\\n  }\n\n#define arg_cast base::internal::arg_cast\n#define ret_cast base::internal::RetTypeCaster<internal::HasToJSValue>::cast\n#define property_cast \\\n  base::internal::PropertyTypeCaster<internal::HasToJSValue>::cast\n\n public:\n  template <typename Functor,\n            typename FunctorTraits = base::internal::MakeFunctorTraits<Functor>,\n            typename ReturnType = typename FunctorTraits::ReturnType,\n            typename RunType = typename FunctorTraits::RunType,\n            typename ArgList = base::internal::ExtractArgs<RunType>,\n            std::enable_if_t<!std::is_same<ReturnType, void>::value>* = nullptr>\n  static Napi::Value Call(const Napi::CallbackInfo& info) {\n    NodeCppFunctionData* data =\n        reinterpret_cast<NodeCppFunctionData*>(info.Data());\n    PRE_CALL(info, data, info.Env().Null())\n    Functor f = reinterpret_cast<Functor>(data->function);\n    return internal::ToJSValue(info, ret_cast<ReturnType>(CallHelper(f, args)));\n  }\n\n  template <typename Functor,\n            typename FunctorTraits = base::internal::MakeFunctorTraits<Functor>,\n            typename ReturnType = typename FunctorTraits::ReturnType,\n            typename RunType = typename FunctorTraits::RunType,\n            typename ArgList = base::internal::ExtractArgs<RunType>,\n            std::enable_if_t<std::is_same<ReturnType, void>::value>* = nullptr>\n  static void Call(const Napi::CallbackInfo& info) {\n    NodeCppFunctionData* data =\n        reinterpret_cast<NodeCppFunctionData*>(info.Data());\n    PRE_CALL(info, data)\n    Functor f = reinterpret_cast<Functor>(data->function);\n    CallHelper(f, args);\n  }\n\n  template <typename Functor,\n            typename FunctorTraits = base::internal::MakeFunctorTraits<Functor>,\n            typename ReturnType = typename FunctorTraits::ReturnType,\n            typename RunType = typename FunctorTraits::RunType,\n            typename ArgList = base::internal::ExtractArgs<RunType>>\n  static std::unique_ptr<base::CppValue> CallConstructor(\n      const Napi::CallbackInfo& info, NodeCppConstructorData* data) {\n    PRE_CALL(info, data, nullptr);\n    Functor f = reinterpret_cast<Functor>(data->cpp_function);\n    return CallHelper(f, args);\n  }\n\n  template <typename Functor, typename Class,\n            typename FunctorTraits = base::internal::MakeFunctorTraits<Functor>,\n            typename ReturnType = typename FunctorTraits::ReturnType,\n            typename RunType = typename FunctorTraits::RunType,\n            typename ArgList = base::internal::DropTypeListItem<\n                1, base::internal::ExtractArgs<RunType>>,\n            std::enable_if_t<!std::is_same<ReturnType, void>::value>* = nullptr>\n  static Napi::Value CallMethod(const Napi::CallbackInfo& info, Class* cls) {\n    NodeCppMethodData<Functor>* data =\n        reinterpret_cast<NodeCppMethodData<Functor>*>(info.Data());\n    if (!data->is_const && std::is_const<Class>::value) {\n      NAPI_THROW(CallNonConstMethod(info.Env()), info.Env().Null());\n    }\n    PRE_CALL(info, data, info.Env().Null())\n    Functor* f = data->method;\n    return internal::ToJSValue(info,\n                               ret_cast<ReturnType>(CallHelper(*f, cls, args)));\n  }\n\n  template <typename Functor, typename Class,\n            typename FunctorTraits = base::internal::MakeFunctorTraits<Functor>,\n            typename ReturnType = typename FunctorTraits::ReturnType,\n            typename RunType = typename FunctorTraits::RunType,\n            typename ArgList = base::internal::DropTypeListItem<\n                1, base::internal::ExtractArgs<RunType>>,\n            std::enable_if_t<std::is_same<ReturnType, void>::value>* = nullptr>\n  static void CallMethod(const Napi::CallbackInfo& info, Class* cls) {\n    NodeCppMethodData<Functor>* data =\n        reinterpret_cast<NodeCppMethodData<Functor>*>(info.Data());\n    PRE_CALL(info, data)\n    Functor* f = data->method;\n    CallHelper(*f, cls, args);\n  }\n\n  template <typename Getter,\n            typename FunctorTraits = base::internal::MakeFunctorTraits<Getter>,\n            typename ReturnType = typename FunctorTraits::ReturnType>\n  static Napi::Value CallStaticGetter(const Napi::CallbackInfo& info) {\n    NodeCppStaticPropertyAccessorData* accessor =\n        reinterpret_cast<NodeCppStaticPropertyAccessorData*>(info.Data());\n    Getter f = reinterpret_cast<Getter>(accessor->getter);\n    return internal::ToJSValue(\n        info, ret_cast<ReturnType>(CallHelper(*f, std::tuple<>{})));\n  }\n\n  template <typename Setter,\n            typename FunctorTraits = base::internal::MakeFunctorTraits<Setter>,\n            typename RunType = typename FunctorTraits::RunType,\n            typename ArgList = base::internal::ExtractArgs<RunType>>\n  static void CallStaticSetter(const Napi::CallbackInfo& info,\n                               const Napi::Value& value) {\n    using Tuple = ConvertTypeListToDeclarableTuple<ArgList>;\n    Tuple args;\n    if (!internal::ToNativeValue(value, &std::get<0>(args))) {\n      NAPI_THROW(InvalidArgument0(info.Env()));\n    }\n    NodeCppStaticPropertyAccessorData* accessor =\n        reinterpret_cast<NodeCppStaticPropertyAccessorData*>(info.Data());\n    Setter f = reinterpret_cast<Setter>(accessor->setter);\n    CallHelper(*f, args);\n  }\n\n  template <typename Getter, typename Setter, typename Class,\n            typename FunctorTraits = base::internal::MakeFunctorTraits<Getter>,\n            typename ReturnType = typename FunctorTraits::ReturnType,\n            typename PropertyAccessorData =\n                NodeCppPropertyAccessorData<Getter, Setter>>\n  static Napi::Value CallGetter(const Napi::CallbackInfo& info, Class* cls) {\n    PropertyAccessorData* accessor =\n        reinterpret_cast<PropertyAccessorData*>(info.Data());\n    Getter* f = reinterpret_cast<Getter*>(accessor->getter);\n    return internal::ToJSValue(\n        info, ret_cast<ReturnType>(CallHelper(*f, cls, std::tuple<>{})));\n  }\n\n  template <typename Getter, typename Setter, typename Class,\n            typename PropertyAccessorData =\n                NodeCppPropertyAccessorData<Getter, Setter>,\n            typename FunctorTraits = base::internal::MakeFunctorTraits<Setter>,\n            typename RunType = typename FunctorTraits::RunType,\n            typename ArgList = base::internal::DropTypeListItem<\n                1, base::internal::ExtractArgs<RunType>>>\n  static void CallSetter(const Napi::CallbackInfo& info, Class* cls,\n                         const Napi::Value& value) {\n    using Tuple = ConvertTypeListToDeclarableTuple<ArgList>;\n    Tuple args;\n    if (!internal::ToNativeValue(value, &std::get<0>(args))) {\n      NAPI_THROW(InvalidArgument0(info.Env()));\n    }\n    PropertyAccessorData* accessor =\n        reinterpret_cast<PropertyAccessorData*>(info.Data());\n    Setter* f = reinterpret_cast<Setter*>(accessor->setter);\n    CallHelper(*f, cls, args);\n  }\n\n  template <typename T>\n  static Napi::Value GetStaticProperty(const Napi::CallbackInfo& info) {\n    T* ptr = reinterpret_cast<T*>(info.Data());\n    return internal::ToJSValue(info, property_cast(*ptr));\n  }\n\n  template <typename T>\n  static void SetStaticProperty(const Napi::CallbackInfo& info,\n                                const Napi::Value& value) {\n    using Tuple = ConvertTypeListToDeclarableTuple<base::internal::TypeList<T>>;\n    Tuple v;\n    if (!internal::ToNativeValue(value, &std::get<0>(v))) {\n      NAPI_THROW(InvalidArgument0(info.Env()));\n    }\n    *reinterpret_cast<T*>(info.Data()) = std::move(std::get<0>(v));\n  }\n\n  template <typename Class, typename T>\n  static Napi::Value GetProperty(const Napi::CallbackInfo& info, Class* cls) {\n    T Class::*member = *reinterpret_cast<T Class::**>(info.Data());\n    return internal::ToJSValue(info, property_cast(cls->*member));\n  }\n\n  template <typename Class, typename T>\n  static void SetProperty(const Napi::CallbackInfo& info, Class* cls,\n                          const Napi::Value& value) {\n    using Tuple = ConvertTypeListToDeclarableTuple<base::internal::TypeList<T>>;\n    Tuple v;\n    if (!internal::ToNativeValue(value, &std::get<0>(v))) {\n      NAPI_THROW(InvalidArgument0(info.Env()));\n    }\n    T Class::*member = *reinterpret_cast<T Class::**>(info.Data());\n    cls->*member = std::move(std::get<0>(v));\n  }\n\n private:\n  template <typename Functor, typename Tuple,\n            typename FunctorTraits = base::internal::MakeFunctorTraits<Functor>,\n            typename ReturnType = typename FunctorTraits::ReturnType,\n            typename RunType = typename FunctorTraits::RunType,\n            typename ArgList = base::internal::ExtractArgs<RunType>>\n  static ReturnType CallHelper(Functor&& f, Tuple&& args) {\n    return DoCall(std::forward<Functor>(f), std::forward<Tuple>(args),\n                  std::make_index_sequence<\n                      std::tuple_size<std::decay_t<Tuple>>::value>{});\n  }\n\n  template <typename Functor, typename Tuple, size_t... Is,\n            typename FunctorTraits = base::internal::MakeFunctorTraits<Functor>,\n            typename ReturnType = typename FunctorTraits::ReturnType,\n            typename RunType = typename FunctorTraits::RunType,\n            typename ArgList = base::internal::ExtractArgs<RunType>>\n  static ReturnType DoCall(Functor&& f, Tuple&& args,\n                           std::index_sequence<Is...>) {\n    using OriginalTuple = base::internal::ConvertTypeListToTuple<ArgList>;\n    return f(arg_cast<std::tuple_element_t<Is, OriginalTuple>>(\n        std::move(std::get<Is>(args)))...);\n  }\n\n  template <typename Functor, typename Class, typename Tuple,\n            typename FunctorTraits = base::internal::MakeFunctorTraits<Functor>,\n            typename ReturnType = typename FunctorTraits::ReturnType,\n            typename RunType = typename FunctorTraits::RunType,\n            typename ArgList = base::internal::DropTypeListItem<\n                1, base::internal::ExtractArgs<RunType>>>\n  static ReturnType CallHelper(Functor&& f, Class* cls, Tuple&& args) {\n    return DoCall(std::forward<Functor>(f), cls, std::forward<Tuple>(args),\n                  std::make_index_sequence<\n                      std::tuple_size<std::decay_t<Tuple>>::value>{});\n  }\n\n  template <typename Functor, typename Class, typename Tuple, size_t... Is,\n            typename FunctorTraits = base::internal::MakeFunctorTraits<Functor>,\n            typename ReturnType = typename FunctorTraits::ReturnType,\n            typename RunType = typename FunctorTraits::RunType,\n            typename ArgList = base::internal::DropTypeListItem<\n                1, base::internal::ExtractArgs<RunType>>>\n  static ReturnType DoCall(Functor&& f, Class* cls, Tuple&& args,\n                           std::index_sequence<Is...>) {\n    using OriginalTuple = base::internal::ConvertTypeListToTuple<ArgList>;\n    return (cls->*f)(arg_cast<std::tuple_element_t<Is, OriginalTuple>>(\n        std::move(std::get<Is>(args)))...);\n  }\n\n#undef property_cast\n#undef ret_cast\n#undef arg_cast\n#undef PRE_CALL\n};\n\n}  // namespace tachyon::node\n\n#endif  // TACHYON_NODE_BASE_NODE_CPP_CALLABLE_H_\n"
  },
  {
    "path": "tachyon/node/base/node_cpp_callable_data.h",
    "content": "#ifndef TACHYON_NODE_BASE_NODE_CPP_CALLABLE_DATA_H_\n#define TACHYON_NODE_BASE_NODE_CPP_CALLABLE_DATA_H_\n\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/export.h\"\n\nnamespace tachyon::node {\n\nstruct TACHYON_EXPORT NodeCppCallableDataBase {\n  std::vector<void*> default_args;\n\n  void Set() {}\n\n  template <typename Arg, typename AllocatableArg = std::decay_t<Arg>>\n  void Set(Arg&& arg) {\n    default_args.push_back(new AllocatableArg(std::move(arg)));\n  }\n\n  template <typename Arg, typename... DefaultArgs>\n  void Set(Arg&& arg, DefaultArgs&&... default_args) {\n    Set(std::forward<Arg>(arg));\n    Set(std::forward<DefaultArgs>(default_args)...);\n  }\n};\n\nstruct TACHYON_EXPORT NodeCppFunctionData : NodeCppCallableDataBase {\n  void* function = nullptr;\n};\n\nstruct TACHYON_EXPORT NodeCppConstructorData : NodeCppCallableDataBase {\n  void* cpp_function = nullptr;\n  void* js_function = nullptr;\n};\n\ntemplate <typename Method>\nstruct NodeCppMethodData : NodeCppCallableDataBase {\n  ~NodeCppMethodData() { delete method; }\n\n  Method* method = nullptr;\n  bool is_const = false;\n};\n\ntemplate <typename Getter, typename Setter>\nstruct NodeCppPropertyAccessorData : public NodeCppCallableDataBase {\n  ~NodeCppPropertyAccessorData() {\n    delete getter;\n    delete setter;\n  }\n\n  Getter* getter = nullptr;\n  Setter* setter = nullptr;\n};\n\nstruct TACHYON_EXPORT NodeCppStaticPropertyAccessorData\n    : public NodeCppCallableDataBase {\n  void* getter = nullptr;\n  void* setter = nullptr;\n};\n\n}  // namespace tachyon::node\n\n#endif  // TACHYON_NODE_BASE_NODE_CPP_CALLABLE_DATA_H_\n"
  },
  {
    "path": "tachyon/node/base/node_cpp_class.h",
    "content": "#ifndef TACHYON_NODE_BASE_NODE_CPP_CLASS_H_\n#define TACHYON_NODE_BASE_NODE_CPP_CLASS_H_\n\n#include <memory>\n#include <string>\n#include <string_view>\n#include <utility>\n#include <vector>\n\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/base/binding/cpp_type.h\"\n#include \"tachyon/base/binding/holder_util.h\"\n#include \"tachyon/node/base/node_cpp_bindable.h\"\n#include \"tachyon/node/base/node_cpp_callable_data.h\"\n#include \"tachyon/node/base/node_cpp_constructor.h\"\n#include \"tachyon/node/base/node_cpp_constructor_matcher.h\"\n#include \"tachyon/node/base/node_cpp_enum.h\"\n#include \"tachyon/node/base/node_cpp_object.h\"\n\nnamespace tachyon::node {\n\ntemplate <typename Class, typename... Options>\nclass NodeCppClass : public NodeCppBindable {\n  template <typename T>\n  using is_holder = base::internal::is_holder_type<Class, T>;\n  template <typename T>\n  using is_base = base::is_strict_base_of<T, Class>;\n\n  template <typename T>\n  struct is_valid_class_option : base::any_of<is_holder<T>, is_base<T>> {};\n\n public:\n  static_assert(base::all_of<is_valid_class_option<Options>...>::value,\n                \"Unknown/invalid NodeCppClass template parameters provided\");\n\n  using Holder = base::exactly_one_t<is_holder, Class, Options...>;\n\n  using Object = NodeCppObject<Class>;\n\n  NodeCppClass(const NodeCppClass& other) = delete;\n  NodeCppClass& operator=(const NodeCppClass& other) = delete;\n  NodeCppClass(NodeCppClass&& other) noexcept = default;\n  NodeCppClass& operator=(NodeCppClass&& other) noexcept = default;\n  ~NodeCppClass() {\n    Object::Init(env_, exports_, name_, full_name_, properties_,\n                 constructor_data_vec_);\n    TACHYON_EXPAND_SIDE_EFFECTS(RegisterParent<Options>());\n  }\n\n  template <typename... Args, typename... DefaultArgs>\n  NodeCppClass& AddConstructor(DefaultArgs&&... default_args) {\n    static_assert(sizeof...(Args) >= sizeof...(DefaultArgs),\n                  \"Too many default args\");\n    if (!constructor_data_vec_) {\n      constructor_data_vec_ = new std::vector<NodeCppConstructorData>();\n    }\n\n    NodeCppConstructorMatcher<Class>& constructor_matcher =\n        NodeCppConstructorMatcher<Class>::GetInstance();\n    std::unique_ptr<NodeCppConstructor> constructor(new NodeCppConstructor());\n    TACHYON_EXPAND_SIDE_EFFECTS(constructor->AddArgumentTypename<Args>());\n    constructor->ValidateAndMaybeDie();\n    constructor->SetDefaultArgsNum(sizeof...(DefaultArgs));\n    constructor_matcher.Add(std::move(constructor));\n\n    NodeCppConstructorData data;\n    data.cpp_function = reinterpret_cast<void*>(\n        &NodeCppConstructor::Create<Holder, Class, Args...>);\n    data.js_function = reinterpret_cast<void*>(\n        &NodeCppCallable::CallConstructor<std::unique_ptr<base::CppValue> (*)(\n            Args...)>);\n    data.Set(std::forward<DefaultArgs>(default_args)...);\n    constructor_data_vec_->push_back(std::move(data));\n    return *this;\n  }\n\n  template <typename T>\n  NodeCppClass& AddStaticReadOnly(std::string_view name, T* value) {\n    return AddStaticReadOnlyWithAttributes(name, value, napi_default);\n  }\n\n  template <typename T>\n  NodeCppClass& AddStaticReadOnlyWithAttributes(\n      std::string_view name, T* value, napi_property_attributes attributes) {\n    properties_.push_back(Napi::ObjectWrap<Object>::StaticAccessor(\n        name.data(), &NodeCppCallable::GetStaticProperty<T>, nullptr,\n        attributes, value));\n    return *this;\n  }\n\n  template <typename T>\n  NodeCppClass& AddStaticReadWrite(std::string_view name, T* value) {\n    return AddStaticReadWriteWithAttributes(name, value, napi_default);\n  }\n\n  template <typename T>\n  NodeCppClass& AddStaticReadWriteWithAttributes(\n      std::string_view name, T* value, napi_property_attributes attributes) {\n    properties_.push_back(Napi::ObjectWrap<Object>::StaticAccessor(\n        name.data(), &NodeCppCallable::GetStaticProperty<T>,\n        &NodeCppCallable::SetStaticProperty<T>, attributes, value));\n    return *this;\n  }\n\n  template <typename T>\n  NodeCppClass& AddStaticReadOnlyProperty(std::string_view name,\n                                          T (*getter)()) {\n    return AddStaticReadOnlyPropertyWithAttributes(name, getter, napi_default);\n  }\n\n  template <typename T>\n  NodeCppClass& AddStaticReadOnlyPropertyWithAttributes(\n      std::string_view name, T (*getter)(),\n      napi_property_attributes attributes) {\n    return AddStaticPropertyWithAttributes(name, getter, nullptr, attributes);\n  }\n\n  template <typename T, typename U>\n  NodeCppClass& AddStaticProperty(std::string_view name, T (*getter)(),\n                                  void (*setter)(U)) {\n    return AddStaticPropertyWithAttributes(name, getter, setter, napi_default);\n  }\n\n  template <typename T, typename U>\n  NodeCppClass& AddStaticPropertyWithAttributes(\n      std::string_view name, T (*getter)(), void (*setter)(U),\n      napi_property_attributes attributes) {\n    using Getter = T (*)();\n    using Setter = void (*)(U);\n\n    NodeCppStaticPropertyAccessorData* accessor =\n        new NodeCppStaticPropertyAccessorData;\n    accessor->getter = reinterpret_cast<void*>(getter);\n    accessor->setter = reinterpret_cast<void*>(setter);\n    properties_.push_back(Napi::ObjectWrap<Object>::StaticAccessor(\n        name.data(), &NodeCppCallable::CallStaticGetter<Getter>,\n        setter ? &NodeCppCallable::CallStaticSetter<Setter> : nullptr,\n        attributes, accessor));\n    return *this;\n  }\n\n  template <typename R, typename... Args, typename... DefaultArgs>\n  NodeCppClass& AddStaticMethod(std::string_view name, R (*f)(Args...),\n                                DefaultArgs&&... default_args) {\n    return AddStaticMethodWithAttributes(\n        name, f, napi_default, std::forward<DefaultArgs>(default_args)...);\n  }\n\n  template <typename R, typename... Args, typename... DefaultArgs>\n  NodeCppClass& AddStaticMethodWithAttributes(\n      std::string_view name, R (*f)(Args...),\n      napi_property_attributes attributes, DefaultArgs&&... default_args) {\n    static_assert(sizeof...(Args) >= sizeof...(DefaultArgs),\n                  \"Too many default args\");\n    using StaticMethod = R (*)(Args...);\n\n    NodeCppFunctionData* data = new NodeCppFunctionData;\n    data->function = reinterpret_cast<void*>(f);\n    data->Set(std::forward<DefaultArgs>(default_args)...);\n    properties_.push_back(Napi::ObjectWrap<Object>::StaticMethod(\n        name.data(), &NodeCppCallable::template Call<StaticMethod>, attributes,\n        data));\n    return *this;\n  }\n\n  template <\n      typename T, typename BaseClass,\n      std::enable_if_t<std::is_base_of<BaseClass, Class>::value>* = nullptr>\n  NodeCppClass& AddReadOnly(std::string_view name, T BaseClass::*value) {\n    return AddReadOnlyWithAttributes(name, value, napi_default);\n  }\n\n  template <\n      typename T, typename BaseClass,\n      std::enable_if_t<std::is_base_of<BaseClass, Class>::value>* = nullptr>\n  NodeCppClass& AddReadOnlyWithAttributes(std::string_view name,\n                                          T BaseClass::*value,\n                                          napi_property_attributes attributes) {\n    return DoAddProperty(name, value, attributes, /*needs_setter=*/false);\n  }\n\n  template <\n      typename T, typename BaseClass,\n      std::enable_if_t<std::is_base_of<BaseClass, Class>::value>* = nullptr>\n  NodeCppClass& AddReadWrite(std::string_view name, T BaseClass::*value) {\n    return AddReadWriteWithAttributes(name, value, napi_default);\n  }\n\n  template <\n      typename T, typename BaseClass,\n      std::enable_if_t<std::is_base_of<BaseClass, Class>::value>* = nullptr>\n  NodeCppClass& AddReadWriteWithAttributes(\n      std::string_view name, T BaseClass::*value,\n      napi_property_attributes attributes) {\n    return DoAddProperty(name, value, attributes, /*needs_setter=*/true);\n  }\n\n  template <\n      typename T, typename BaseClass,\n      std::enable_if_t<std::is_base_of<BaseClass, Class>::value>* = nullptr>\n  NodeCppClass& AddReadOnlyProperty(std::string_view name,\n                                    T (BaseClass::*getter)() const) {\n    return AddReadOnlyPropertyWithAttributes(name, getter, napi_default);\n  }\n\n  template <\n      typename T, typename BaseClass,\n      std::enable_if_t<std::is_base_of<BaseClass, Class>::value>* = nullptr>\n  NodeCppClass& AddReadOnlyPropertyWithAttributes(\n      std::string_view name, T (BaseClass::*getter)() const,\n      napi_property_attributes attributes) {\n    using Getter = T (BaseClass::*)() const;\n    using PropertyAccessorData = NodeCppPropertyAccessorData<Getter, void>;\n\n    PropertyAccessorData* accessor = new PropertyAccessorData;\n    accessor->getter = new Getter(getter);\n    accessor->setter = nullptr;\n    properties_.push_back(Napi::InstanceWrap<Object>::InstanceAccessor(\n        name.data(), &Object::template CallGetter<Getter, void>, nullptr,\n        attributes, accessor));\n    return *this;\n  }\n\n  template <\n      typename T, typename U, typename BaseClass,\n      std::enable_if_t<std::is_base_of<BaseClass, Class>::value>* = nullptr>\n  NodeCppClass& AddProperty(std::string_view name,\n                            T (BaseClass::*getter)() const,\n                            void (BaseClass::*setter)(U)) {\n    return AddPropertyWithAttributes(name, getter, setter, napi_default);\n  }\n\n  template <\n      typename T, typename U, typename BaseClass,\n      std::enable_if_t<std::is_base_of<BaseClass, Class>::value>* = nullptr>\n  NodeCppClass& AddPropertyWithAttributes(std::string_view name,\n                                          T (BaseClass::*getter)() const,\n                                          void (BaseClass::*setter)(U),\n                                          napi_property_attributes attributes) {\n    using Getter = T (BaseClass::*)() const;\n    using Setter = void (BaseClass::*)(U);\n    using PropertyAccessorData = NodeCppPropertyAccessorData<Getter, Setter>;\n\n    PropertyAccessorData* accessor = new PropertyAccessorData;\n    accessor->getter = new Getter(getter);\n    accessor->setter = new Setter(setter);\n    properties_.push_back(Napi::InstanceWrap<Object>::InstanceAccessor(\n        name.data(), &Object::template CallGetter<Getter, Setter>,\n        &Object::template CallSetter<Getter, Setter>, attributes, accessor));\n    return *this;\n  }\n\n  template <\n      typename R, typename BaseClass, typename... Args, typename... DefaultArgs,\n      std::enable_if_t<std::is_base_of<BaseClass, Class>::value>* = nullptr>\n  NodeCppClass& AddMethod(std::string_view name,\n                          R (BaseClass::*method)(Args...),\n                          DefaultArgs&&... default_args) {\n    return AddMethodWithAttributes(name, method, napi_default,\n                                   std::forward<DefaultArgs>(default_args)...);\n  }\n\n  template <\n      typename R, typename BaseClass, typename... Args, typename... DefaultArgs,\n      std::enable_if_t<std::is_base_of<BaseClass, Class>::value>* = nullptr>\n  NodeCppClass& AddMethodWithAttributes(std::string_view name,\n                                        R (BaseClass::*method)(Args...),\n                                        napi_property_attributes attributes,\n                                        DefaultArgs&&... default_args) {\n    static_assert(sizeof...(Args) >= sizeof...(DefaultArgs),\n                  \"Too many default args\");\n    return DoAddMethod(name, method, attributes, false,\n                       std::forward<DefaultArgs>(default_args)...);\n  }\n\n  template <\n      typename R, typename BaseClass, typename... Args, typename... DefaultArgs,\n      std::enable_if_t<std::is_base_of<BaseClass, Class>::value>* = nullptr>\n  NodeCppClass& AddMethod(std::string_view name,\n                          R (BaseClass::*method)(Args...) const,\n                          DefaultArgs&&... default_args) {\n    return AddMethodWithAttributes(name, method, napi_default,\n                                   std::forward<DefaultArgs>(default_args)...);\n  }\n\n  template <\n      typename R, typename BaseClass, typename... Args, typename... DefaultArgs,\n      std::enable_if_t<std::is_base_of<BaseClass, Class>::value>* = nullptr>\n  NodeCppClass& AddMethodWithAttributes(std::string_view name,\n                                        R (BaseClass::*method)(Args...) const,\n                                        napi_property_attributes attributes,\n                                        DefaultArgs&&... default_args) {\n    static_assert(sizeof...(Args) >= sizeof...(DefaultArgs),\n                  \"Too many default args\");\n    return DoAddMethod(name, method, attributes, true,\n                       std::forward<DefaultArgs>(default_args)...);\n  }\n\n  template <typename SubClass, typename... SubClassOptions>\n  NodeCppClass<SubClass, SubClassOptions...> NewClass(std::string_view name) {\n    return NodeCppClass<SubClass, SubClassOptions...>(env_, exports_, name,\n                                                      full_name_);\n  }\n\n  template <typename Enum>\n  NodeCppEnum<Enum> NewEnum(std::string_view name) {\n    return NodeCppEnum<Enum>(env_, exports_, name, full_name_);\n  }\n\n private:\n  friend class NodeModule;\n\n  NodeCppClass(Napi::Env env, Napi::Object exports, std::string_view name,\n               std::string_view parent_full_name)\n      : NodeCppBindable(env, exports, name, parent_full_name) {\n    base::CppType<Class>::Get().set_name(full_name_);\n    base::CppType<const Class>::Get().set_name(\n        absl::Substitute(\"const $0\", full_name_));\n  }\n\n  template <typename BaseClass,\n            std::enable_if_t<!is_base<BaseClass>::value>* = nullptr>\n  void RegisterParent() {}\n\n  template <typename BaseClass,\n            std::enable_if_t<is_base<BaseClass>::value>* = nullptr>\n  void RegisterParent() {\n    NodeConstructors& constructors = NodeConstructors::GetInstance();\n    const std::string& parent_full_name =\n        base::CppType<BaseClass>::Get().name();\n    Napi::Function parent_constructor;\n    CHECK(constructors.GetConstructor(parent_full_name, &parent_constructor))\n        << \"Did you forget to register parent: \" << parent_full_name << \"?\";\n    Object::RegisterParent(parent_constructor);\n  }\n\n  template <\n      typename T, typename BaseClass,\n      std::enable_if_t<std::is_base_of<BaseClass, Class>::value>* = nullptr>\n  NodeCppClass& DoAddProperty(std::string_view name, T BaseClass::*value,\n                              napi_property_attributes attributes,\n                              bool needs_setter) {\n    using Member = T BaseClass::*;\n\n    Member* member = new Member(value);\n    properties_.push_back(Napi::InstanceWrap<Object>::InstanceAccessor(\n        name.data(), &Object::template GetProperty<T>,\n        needs_setter ? &Object::template SetProperty<T> : nullptr, attributes,\n        member));\n    return *this;\n  }\n\n  template <typename Method, typename... DefaultArgs>\n  NodeCppClass& DoAddMethod(std::string_view name, Method method,\n                            napi_property_attributes attributes, bool is_const,\n                            DefaultArgs&&... default_args) {\n    NodeCppMethodData<Method>* data = new NodeCppMethodData<Method>;\n    data->method = new Method(method);\n    data->is_const = is_const;\n    data->Set(std::forward<DefaultArgs>(default_args)...);\n    properties_.push_back(Napi::InstanceWrap<Object>::InstanceMethod(\n        name.data(), &Object::template CallMethod<Method>, attributes, data));\n    return *this;\n  }\n\n  std::vector<NodeCppConstructorData>* constructor_data_vec_ = nullptr;\n  std::vector<Napi::ClassPropertyDescriptor<Object>> properties_;\n};\n\n}  // namespace tachyon::node\n\n#endif  // TACHYON_NODE_BASE_NODE_CPP_CLASS_H_\n"
  },
  {
    "path": "tachyon/node/base/node_cpp_constructor.cc",
    "content": "#include \"tachyon/node/base/node_cpp_constructor.h\"\n\n#include \"tachyon/base/strings/string_util.h\"\n#include \"tachyon/node/base/node_constructors.h\"\n#include \"tachyon/node/base/node_errors.h\"\n\nnamespace tachyon::node {\n\nNodeCppConstructor::NodeCppConstructor() = default;\n\nNodeCppConstructor::~NodeCppConstructor() = default;\n\nbool NodeCppConstructor::Match(const Napi::CallbackInfo& info) const {\n  size_t nargs = info.Length();\n  if (arg_type_names_.size() - default_args_num_ > nargs ||\n      nargs > arg_type_names_.size())\n    return false;\n\n  for (size_t i = 0; i < nargs; ++i) {\n    Napi::Value value = info[i];\n    std::string_view arg_type_name = arg_type_names_[i];\n    napi_valuetype type = value.Type();\n    switch (type) {\n      case napi_undefined:\n        return false;\n      case napi_null:\n        return false;\n      case napi_boolean:\n        if (arg_type_name != base::kCppBoolTypeName) return false;\n        break;\n      case napi_number:\n        if (arg_type_name != base::kCppNumberTypeName) return false;\n        break;\n      case napi_string:\n        if (arg_type_name != base::kCppStringTypeName) return false;\n        break;\n      case napi_bigint:\n        if (arg_type_name != base::kCppInt64TypeName &&\n            arg_type_name != base::kCppUint64TypeName)\n          return false;\n        break;\n      case napi_object: {\n        if (value.IsArray()) {\n          if (!base::StartsWith(arg_type_name, base::kCppVectorTypePrefix))\n            return false;\n        } else if (value.IsArrayBuffer()) {\n        } else if (value.IsTypedArray()) {\n        } else if (value.IsPromise()) {\n        } else if (value.IsBuffer()) {\n        } else if (value.IsFunction()) {\n        } else if (value.IsDataView()) {\n        } else {\n          std::string_view name = UnwrapPointer(arg_type_name);\n          Napi::Object object = value.ToObject();\n          Napi::Value is_const = object.Get(\"__is_const__\");\n          CHECK(is_const.IsBoolean());\n          if (!base::StartsWith(name, \"const \") &&\n              is_const.As<Napi::Boolean>().Value()) {\n            return false;\n          }\n\n          if (!NodeConstructors::GetInstance().InstanceOf(object, name))\n            return false;\n        }\n        break;\n      }\n      default:\n        NOTIMPLEMENTED() << \"Unknown js type: \" << type;\n        return false;\n    }\n  }\n  return true;\n}\n\n}  // namespace tachyon::node\n"
  },
  {
    "path": "tachyon/node/base/node_cpp_constructor.h",
    "content": "#ifndef TACHYON_NODE_BASE_NODE_CPP_CONSTRUCTOR_H_\n#define TACHYON_NODE_BASE_NODE_CPP_CONSTRUCTOR_H_\n\n#include <memory>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"third_party/node_addon_api/napi.h\"\n\n#include \"tachyon/base/binding/cpp_constructor.h\"\n#include \"tachyon/base/binding/cpp_type_names.h\"\n#include \"tachyon/base/binding/cpp_value_factory.h\"\n#include \"tachyon/base/binding/holder_util.h\"\n#include \"tachyon/node/base/node_internals_forwards.h\"\n\nnamespace tachyon::node {\n\nnamespace internal {\n\ntemplate <typename T>\nconstexpr bool should_override_to_number_v =\n    std::is_enum<T>::value || (std::is_integral<T>::value && sizeof(T) <= 4 &&\n                               !std::is_same<T, bool>::value);\n\n}  // namespace internal\n\nclass TACHYON_EXPORT NodeCppConstructor : public base::CppConstructor {\n public:\n  // TODO(chokobole): Need to use universal reference and perfect forwarding\n  template <typename Holder, typename Class, typename... Args>\n  static std::unique_ptr<base::CppValue> Create(Args... args) {\n    return base::CppValueFactory<Holder>::Create(\n        base::internal::HolderCreator<Holder, Class(Args...)>::DoCreate(\n            std::forward<Args>(args)...));\n  }\n\n  NodeCppConstructor();\n  NodeCppConstructor(const NodeCppConstructor& other) = delete;\n  NodeCppConstructor& operator=(const NodeCppConstructor& other) = delete;\n  NodeCppConstructor(NodeCppConstructor&& other) noexcept;\n  NodeCppConstructor& operator=(NodeCppConstructor&& other) noexcept;\n  ~NodeCppConstructor();\n\n  template <\n      typename Arg,\n      std::enable_if_t<internal::should_override_to_number_v<Arg>>* = nullptr>\n  void AddArgumentTypename() {\n    arg_type_names_.push_back(base::kCppNumberTypeName);\n  }\n\n  template <\n      typename Arg,\n      std::enable_if_t<!internal::should_override_to_number_v<Arg>>* = nullptr>\n  void AddArgumentTypename() {\n    arg_type_names_.push_back(\n        internal::CppValueTraits<internal::NativeCppType<Arg>>::GetTypeName());\n  }\n\n  bool Match(const Napi::CallbackInfo& info) const;\n};\n\n}  // namespace tachyon::node\n\n#endif  // TACHYON_NODE_BASE_NODE_CPP_CONSTRUCTOR_H_\n"
  },
  {
    "path": "tachyon/node/base/node_cpp_constructor_matcher.h",
    "content": "#ifndef TACHYON_NODE_BASE_NODE_CPP_CONSTRUCTOR_MATCHER_H_\n#define TACHYON_NODE_BASE_NODE_CPP_CONSTRUCTOR_MATCHER_H_\n\n#include <limits>\n\n#include \"third_party/node_addon_api/napi.h\"\n\n#include \"tachyon/base/binding/cpp_constructor_matcher.h\"\n#include \"tachyon/base/no_destructor.h\"\n\nnamespace tachyon::node {\n\ntemplate <typename Class>\nclass NodeCppConstructorMatcher : public base::CppConstructorMatcher {\n public:\n  constexpr static size_t kInvalidIndex = std::numeric_limits<size_t>::max();\n\n  static NodeCppConstructorMatcher& GetInstance() {\n    static base::NoDestructor<NodeCppConstructorMatcher> matcher;\n    return *matcher;\n  }\n\n  NodeCppConstructorMatcher(const NodeCppConstructorMatcher& other) = delete;\n  NodeCppConstructorMatcher& operator=(const NodeCppConstructorMatcher& other) =\n      delete;\n  ~NodeCppConstructorMatcher() = default;\n\n  size_t FindConstructorIndex(const Napi::CallbackInfo& info) const {\n    for (size_t i = 0; i < constructors_.size(); ++i) {\n      NodeCppConstructor* node_cpp_constructor =\n          reinterpret_cast<NodeCppConstructor*>(constructors_[i].get());\n      if (node_cpp_constructor->Match(info)) {\n        return i;\n      }\n    }\n    return kInvalidIndex;\n  }\n\n private:\n  friend base::NoDestructor<NodeCppConstructorMatcher>;\n\n  NodeCppConstructorMatcher() = default;\n};\n\n}  // namespace tachyon::node\n\n#endif  // TACHYON_NODE_BASE_NODE_CPP_CONSTRUCTOR_MATCHER_H_\n"
  },
  {
    "path": "tachyon/node/base/node_cpp_enum.h",
    "content": "#ifndef TACHYON_NODE_BASE_NODE_CPP_ENUM_H_\n#define TACHYON_NODE_BASE_NODE_CPP_ENUM_H_\n\n#include <string_view>\n#include <type_traits>\n\n#include \"tachyon/base/binding/cpp_type.h\"\n#include \"tachyon/node/base/node_cpp_bindable.h\"\n\nnamespace tachyon::node {\n\ntemplate <typename Enum>\nclass NodeCppEnum : public NodeCppBindable {\n public:\n  NodeCppEnum(const NodeCppEnum& other) = delete;\n  NodeCppEnum& operator=(const NodeCppEnum& other) = delete;\n  NodeCppEnum(NodeCppEnum&& other) noexcept = default;\n  NodeCppEnum& operator=(NodeCppEnum&& other) noexcept = default;\n  ~NodeCppEnum() {\n    // TODO(chokobole): Need to freeze |env_|.\n  }\n\n  NodeCppEnum& AddValue(std::string_view name, Enum value) {\n    enum_.DefineProperty(Napi::PropertyDescriptor::Value(\n        name.data(),\n        Napi::Number::New(env_,\n                          static_cast<std::underlying_type_t<Enum>>(value)),\n        napi_enumerable));\n    return *this;\n  }\n\n private:\n  friend class NodeModule;\n\n  NodeCppEnum(Napi::Env env, Napi::Object exports, std::string_view name,\n              std::string_view parent_full_name)\n      : NodeCppBindable(env, exports, name, parent_full_name) {\n    base::CppType<Enum>::Get().set_name(full_name_);\n    enum_ = Napi::Object::New(env_);\n    exports_.Set(name.data(), enum_);\n  }\n\n  Napi::Object enum_;\n};\n\n}  // namespace tachyon::node\n\n#endif  // TACHYON_NODE_BASE_NODE_CPP_ENUM_H_\n"
  },
  {
    "path": "tachyon/node/base/node_cpp_object.h",
    "content": "#ifndef TACHYON_NODE_BASE_NODE_CPP_OBJECT_H_\n#define TACHYON_NODE_BASE_NODE_CPP_OBJECT_H_\n\n#include <memory>\n#include <string_view>\n#include <vector>\n\n#include \"tachyon/base/binding/cpp_stack_value.h\"\n#include \"tachyon/base/binding/cpp_value.h\"\n#include \"tachyon/node/base/node_constructors.h\"\n#include \"tachyon/node/base/node_cpp_callable.h\"\n#include \"tachyon/node/base/node_errors.h\"\n\nnamespace tachyon::node {\n\ntemplate <typename Class>\nclass NodeCppObject : public Napi::ObjectWrap<NodeCppObject<Class>> {\n public:\n  explicit NodeCppObject(const Napi::CallbackInfo& info)\n      : Napi::ObjectWrap<NodeCppObject>(info) {\n    if (info.Length() == 1 && info[0].IsExternal()) {\n      value_.reset(info[0].As<Napi::External<base::CppValue>>().Data());\n    } else {\n      NodeCppConstructorMatcher<Class>& constructor_matcher =\n          NodeCppConstructorMatcher<Class>::GetInstance();\n      size_t idx = constructor_matcher.FindConstructorIndex(info);\n      if (idx == NodeCppConstructorMatcher<Class>::kInvalidIndex) {\n        NAPI_THROW(NoSuchConstructor(info.Env()));\n      }\n      std::vector<NodeCppConstructorData>* data_vec =\n          reinterpret_cast<std::vector<NodeCppConstructorData>*>(info.Data());\n      NodeCppConstructorData& data = (*data_vec)[idx];\n      using JSFunction = std::unique_ptr<base::CppValue> (*)(\n          const Napi::CallbackInfo&, NodeCppConstructorData*);\n      JSFunction js_function = reinterpret_cast<JSFunction>(data.js_function);\n      value_ = js_function(info, &data);\n    }\n  }\n  NodeCppObject(const NodeCppObject& other) = delete;\n  NodeCppObject& operator=(const NodeCppObject& other) = delete;\n  ~NodeCppObject() = default;\n\n  base::CppValue* value() { return value_.get(); }\n\n  static void Init(\n      Napi::Env env, Napi::Object exports, std::string_view name,\n      std::string_view full_name,\n      std::vector<Napi::ClassPropertyDescriptor<NodeCppObject>>& properties,\n      void* data = nullptr) {\n    properties.push_back(NodeCppObject::InstanceAccessor(\n        \"__is_const__\", &NodeCppObject::GetIsConst, nullptr));\n    Napi::Function func =\n        NodeCppObject::DefineClass(env, name.data(), properties, data);\n    s_constructor_ = Napi::Persistent(func);\n    s_constructor_.SuppressDestruct();\n    exports.Set(name.data(), func);\n    NodeConstructors& constructors = NodeConstructors::GetInstance();\n    constructors.AddConstructor(full_name, Napi::Weak(func));\n    constructors.AddConstructor(absl::Substitute(\"const $0\", full_name),\n                                Napi::Weak(func));\n  }\n\n  static void RegisterParent(Napi::Function parent_constructor) {\n    s_constructor_.Value().Set(\"prototype\",\n                               parent_constructor.Get(\"prototype\"));\n  }\n\n  static Napi::Object NewInstance(Napi::Env env,\n                                  Napi::External<base::CppValue> arg) {\n    Napi::EscapableHandleScope scope(env);\n    Napi::Object obj = s_constructor_.New({arg});\n    return scope.Escape(napi_value(obj)).ToObject();\n  }\n\n  template <typename Functor,\n            typename FunctorTraits = base::internal::MakeFunctorTraits<Functor>,\n            typename ReturnType = typename FunctorTraits::ReturnType,\n            std::enable_if_t<std::is_same<ReturnType, void>::value>* = nullptr>\n  void CallMethod(const Napi::CallbackInfo& info) {\n    NodeCppMethodData<Functor>* data =\n        reinterpret_cast<NodeCppMethodData<Functor>*>(info.Data());\n    if (value_->is_const() && !data->is_const) {\n      NAPI_THROW(CallNonConstMethod(info.Env()));\n    }\n    NodeCppCallable::CallMethod<Functor>(\n        info, reinterpret_cast<Class*>(value_->raw_ptr()));\n  }\n\n  template <typename Functor,\n            typename FunctorTraits = base::internal::MakeFunctorTraits<Functor>,\n            typename ReturnType = typename FunctorTraits::ReturnType,\n            std::enable_if_t<!std::is_same<ReturnType, void>::value>* = nullptr>\n  Napi::Value CallMethod(const Napi::CallbackInfo& info) {\n    NodeCppMethodData<Functor>* data =\n        reinterpret_cast<NodeCppMethodData<Functor>*>(info.Data());\n    if (value_->is_const() && !data->is_const) {\n      NAPI_THROW(CallNonConstMethod(info.Env()), info.Env().Null());\n    }\n    return NodeCppCallable::CallMethod<Functor>(\n        info, reinterpret_cast<Class*>(value_->raw_ptr()));\n  }\n\n  template <typename T>\n  Napi::Value GetProperty(const Napi::CallbackInfo& info) {\n    if (value_->is_const()) {\n      return NodeCppCallable::GetProperty<const Class, T>(\n          info, reinterpret_cast<const Class*>(value_->raw_ptr()));\n    } else {\n      return NodeCppCallable::GetProperty<Class, T>(\n          info, reinterpret_cast<Class*>(value_->raw_ptr()));\n    }\n  }\n\n  template <typename T>\n  void SetProperty(const Napi::CallbackInfo& info, const Napi::Value& value) {\n    if (value_->is_const()) {\n      NAPI_THROW(CallNonConstMethod(info.Env()));\n    }\n    NodeCppCallable::SetProperty<Class, T>(\n        info, reinterpret_cast<Class*>(value_->raw_ptr()), value);\n  }\n\n  template <typename Getter, typename Setter>\n  Napi::Value CallGetter(const Napi::CallbackInfo& info) {\n    if (value_->is_const()) {\n      return NodeCppCallable::CallGetter<Getter, Setter>(\n          info, reinterpret_cast<const Class*>(value_->raw_ptr()));\n    } else {\n      return NodeCppCallable::CallGetter<Getter, Setter>(\n          info, reinterpret_cast<Class*>(value_->raw_ptr()));\n    }\n  }\n\n  template <typename Getter, typename Setter>\n  void CallSetter(const Napi::CallbackInfo& info, const Napi::Value& value) {\n    if (value_->is_const()) {\n      NAPI_THROW(CallNonConstMethod(info.Env()));\n    }\n    NodeCppCallable::CallSetter<Getter, Setter>(\n        info, reinterpret_cast<Class*>(value_->raw_ptr()), value);\n  }\n\n private:\n  Napi::Value GetIsConst(const Napi::CallbackInfo& info) {\n    return Napi::Boolean::New(info.Env(), value_->is_const());\n  }\n\n  static Napi::FunctionReference s_constructor_;\n  std::unique_ptr<base::CppValue> value_;\n};\n\ntemplate <typename T>\nNapi::FunctionReference NodeCppObject<T>::s_constructor_;\n\n}  // namespace tachyon::node\n\n#endif  // TACHYON_NODE_BASE_NODE_CPP_OBJECT_H_\n"
  },
  {
    "path": "tachyon/node/base/node_errors.cc",
    "content": "#include \"tachyon/node/base/node_errors.h\"\n\n#include \"absl/strings/substitute.h\"\n\nnamespace tachyon::node {\n\nNapi::Error WrongNumberOfArguments(Napi::Env env) {\n  return Napi::TypeError::New(env, \"Wrong number of arguments\");\n}\n\nNapi::Error InvalidArgument(Napi::Env env, size_t n) {\n  return Napi::TypeError::New(env, absl::Substitute(\"Invalid argument #$0\", n));\n}\n\nNapi::Error InvalidArgument0(Napi::Env env) {\n  return Napi::TypeError::New(env, \"Invalid argument #0\");\n}\n\nNapi::Error CallNonConstMethod(Napi::Env env) {\n  return Napi::TypeError::New(env, \"Call non-const method\");\n}\n\nNapi::Error NoSuchConstructor(Napi::Env env) {\n  return Napi::TypeError::New(env, \"No such constructor\");\n}\n\n}  // namespace tachyon::node\n"
  },
  {
    "path": "tachyon/node/base/node_errors.h",
    "content": "#ifndef TACHYON_NODE_BASE_NODE_ERRORS_H_\n#define TACHYON_NODE_BASE_NODE_ERRORS_H_\n\n#include <stddef.h>\n\n#include \"third_party/node_addon_api/napi.h\"\n\n#include \"tachyon/export.h\"\n\nnamespace tachyon::node {\n\nTACHYON_EXPORT Napi::Error WrongNumberOfArguments(Napi::Env env);\nTACHYON_EXPORT Napi::Error InvalidArgument(Napi::Env env, size_t n);\nTACHYON_EXPORT Napi::Error InvalidArgument0(Napi::Env env);\nTACHYON_EXPORT Napi::Error CallNonConstMethod(Napi::Env env);\nTACHYON_EXPORT Napi::Error NoSuchConstructor(Napi::Env env);\n\n}  // namespace tachyon::node\n\n#endif  // TACHYON_NODE_BASE_NODE_ERRORS_H_\n"
  },
  {
    "path": "tachyon/node/base/node_internals.h",
    "content": "#ifndef TACHYON_NODE_BASE_NODE_INTERNALS_H_\n#define TACHYON_NODE_BASE_NODE_INTERNALS_H_\n\n#include <memory>\n#include <string>\n#include <string_view>\n#include <utility>\n#include <vector>\n\n#include \"absl/container/inlined_vector.h\"\n\n#include \"tachyon/base/binding/cpp_raw_ptr.h\"\n#include \"tachyon/base/binding/cpp_shared_ptr.h\"\n#include \"tachyon/base/binding/cpp_stack_value.h\"\n#include \"tachyon/base/binding/cpp_type_names.h\"\n#include \"tachyon/base/binding/cpp_unique_ptr.h\"\n#include \"tachyon/base/numerics/safe_conversions.h\"\n#include \"tachyon/base/template_util.h\"\n#include \"tachyon/node/base/node_cpp_object.h\"\n#include \"tachyon/node/base/node_internals_forwards.h\"\n\nnamespace tachyon::node {\nnamespace internal {\n\ntemplate <typename T>\nstruct CppTypeWrapperInternal {\n  using Type = base::CppStackValue<std::remove_reference_t<T>>;\n};\n\ntemplate <typename T>\nstruct CppTypeWrapperInternal<T*> {\n  using Type = T*;\n};\n\ntemplate <typename T>\nstruct NativeCppTypeWrapper<\n    T, std::enable_if_t<HasToNativeValue<std::decay_t<T>>::value>> {\n  using Type = std::decay_t<T>;\n};\n\ntemplate <typename T>\nstruct NativeCppTypeWrapper<\n    T, std::enable_if_t<!HasToNativeValue<std::decay_t<T>>::value>> {\n  using Type =\n      typename CppTypeWrapperInternal<base::reference_to_pointer_t<T>>::Type;\n};\n\ntemplate <typename T>\nstruct JSCppTypeWrapper<\n    T, std::enable_if_t<HasToJSValue<std::decay_t<T>>::value>> {\n  using Type = std::decay_t<T>;\n};\n\ntemplate <typename T>\nstruct JSCppTypeWrapper<\n    T, std::enable_if_t<!HasToJSValue<std::decay_t<T>>::value>> {\n  using Type = typename CppTypeWrapperInternal<T>::Type;\n};\n\ntemplate <>\nclass CppValueTraits<bool> {\n public:\n  static bool ToNativeValue(const Napi::Value& value, bool* v) {\n    if (!value.IsBoolean()) return false;\n    *v = value.As<Napi::Boolean>().Value();\n    return true;\n  }\n\n  static Napi::Value ToJSValue(const Napi::CallbackInfo& info, bool value) {\n    return Napi::Boolean::New(info.Env(), value);\n  }\n\n  static const char* GetTypeName() { return base::kCppBoolTypeName; }\n};\n\ntemplate <typename T>\nclass CppValueTraits<T, std::enable_if_t<std::is_integral<T>::value &&\n                                         std::is_signed<T>::value &&\n                                         sizeof(T) <= sizeof(int32_t)>> {\n public:\n  static bool ToNativeValue(const Napi::Value& value, T* v) {\n#if NAPI_VERSION > 5\n    if (value.IsBigInt()) {\n      int64_t d = value.As<Napi::BigInt>().Int64Value(nullptr);\n      if (!base::IsValueInRangeForNumericType<T>(d)) return false;\n      *v = d;\n      return true;\n    }\n#endif\n    if (!value.IsNumber()) return false;\n    int32_t d = value.As<Napi::Number>().Int32Value();\n    if (!base::IsValueInRangeForNumericType<T>(d)) return false;\n    *v = d;\n    return true;\n  }\n\n  static Napi::Value ToJSValue(const Napi::CallbackInfo& info, T value) {\n    return Napi::Number::New(info.Env(), value);\n  }\n\n  static const char* GetTypeName() { return base::kCppIntTypeName; }\n};\n\ntemplate <typename T>\nclass CppValueTraits<\n    T, std::enable_if_t<\n           std::is_integral<T>::value && !std::is_same<bool, T>::value &&\n           !std::is_signed<T>::value && sizeof(T) <= sizeof(uint32_t)>> {\n public:\n  static bool ToNativeValue(const Napi::Value& value, T* v) {\n#if NAPI_VERSION > 5\n    if (value.IsBigInt()) {\n      uint64_t d = value.As<Napi::BigInt>().Uint64Value(nullptr);\n      if (!base::IsValueInRangeForNumericType<T>(d)) return false;\n      *v = d;\n      return true;\n    }\n#endif\n    if (!value.IsNumber()) return false;\n    uint32_t d = value.As<Napi::Number>().Uint32Value();\n    if (!base::IsValueInRangeForNumericType<T>(d)) return false;\n    *v = d;\n    return true;\n  }\n\n  static Napi::Value ToJSValue(const Napi::CallbackInfo& info, T value) {\n    return Napi::Number::New(info.Env(), value);\n  }\n\n  static const char* GetTypeName() { return base::kCppUintTypeName; }\n};\n\ntemplate <>\nclass CppValueTraits<int64_t> {\n public:\n  static bool ToNativeValue(const Napi::Value& value, int64_t* v) {\n#if NAPI_VERSION > 5\n    if (value.IsBigInt()) {\n      bool lossless;\n      *v = value.As<Napi::BigInt>().Int64Value(&lossless);\n      return lossless;\n    }\n#endif\n    if (!value.IsNumber()) return false;\n    *v = value.As<Napi::Number>().Int64Value();\n    return true;\n  }\n\n  static Napi::Value ToJSValue(const Napi::CallbackInfo& info, int64_t value) {\n#if NAPI_VERSION > 5\n    return Napi::BigInt::New(info.Env(), value);\n#else\n    return Napi::Number::New(info.Env(), value);\n#endif\n  }\n\n  static const char* GetTypeName() { return base::kCppInt64TypeName; }\n};\n\ntemplate <>\nclass CppValueTraits<uint64_t> {\n public:\n  static bool ToNativeValue(const Napi::Value& value, uint64_t* v) {\n#if NAPI_VERSION > 5\n    if (value.IsBigInt()) {\n      bool lossless;\n      *v = value.As<Napi::BigInt>().Uint64Value(&lossless);\n      return lossless;\n    }\n#endif\n    if (!value.IsNumber()) return false;\n    int64_t d = value.As<Napi::Number>().Int64Value();\n    if (d < 0) return false;\n    *v = d;\n    return true;\n  }\n\n  static Napi::Value ToJSValue(const Napi::CallbackInfo& info, uint64_t value) {\n#if NAPI_VERSION > 5\n    return Napi::BigInt::New(info.Env(), value);\n#else\n    return Napi::Number::New(info.Env(), value);\n#endif\n  }\n\n  static const char* GetTypeName() { return base::kCppUint64TypeName; }\n};\n\ntemplate <>\nclass CppValueTraits<float> {\n public:\n  static bool ToNativeValue(const Napi::Value& value, float* v) {\n    if (!value.IsNumber()) return false;\n    *v = value.As<Napi::Number>().FloatValue();\n    return true;\n  }\n\n  static Napi::Value ToJSValue(const Napi::CallbackInfo& info, float value) {\n    return Napi::Number::New(info.Env(), value);\n  }\n\n  static const char* GetTypeName() { return base::kCppNumberTypeName; }\n};\n\ntemplate <>\nclass CppValueTraits<double> {\n public:\n  static bool ToNativeValue(const Napi::Value& value, double* v) {\n    if (!value.IsNumber()) return false;\n    *v = value.As<Napi::Number>().DoubleValue();\n    return true;\n  }\n\n  static Napi::Value ToJSValue(const Napi::CallbackInfo& info, double value) {\n    return Napi::Number::New(info.Env(), value);\n  }\n\n  static const char* GetTypeName() { return base::kCppNumberTypeName; }\n};\n\ntemplate <>\nclass CppValueTraits<std::string> {\n public:\n  static bool ToNativeValue(const Napi::Value& value, std::string* v) {\n    if (!value.IsString()) return false;\n    *v = value.As<Napi::String>().Utf8Value();\n    return true;\n  }\n\n  static Napi::Value ToJSValue(const Napi::CallbackInfo& info,\n                               const std::string& value) {\n    return Napi::String::New(info.Env(), value.c_str(), value.length());\n  }\n\n  static const char* GetTypeName() { return base::kCppStringTypeName; }\n};\n\ntemplate <>\nclass CppValueTraits<std::string_view> {\n public:\n  static Napi::Value ToJSValue(const Napi::CallbackInfo& info,\n                               std::string_view value) {\n    return Napi::String::New(info.Env(), value.data(), value.length());\n  }\n};\n\ntemplate <typename T>\nclass CppValueTraits<T, std::enable_if_t<std::is_enum<T>::value>> {\n public:\n  using U = std::underlying_type_t<T>;\n\n  static bool ToNativeValue(const Napi::Value& value, T* v) {\n    return CppValueTraits<U>::ToNativeValue(value, reinterpret_cast<U*>(v));\n  }\n\n  static Napi::Value ToJSValue(const Napi::CallbackInfo& info, T value) {\n    return Napi::Number::New(info.Env(), static_cast<U>(value));\n  }\n\n  static const std::string& GetTypeName() {\n    return base::CppType<T>::Get().name();\n  }\n};\n\ntemplate <typename Vector, typename value_type = typename Vector::value_type>\nbool VectorToNativeValueHelper(const Napi::Value& value, Vector* v) {\n  Vector values;\n  Napi::Array arr = value.As<Napi::Array>();\n  values.reserve(arr.Length());\n  for (size_t i = 0; i < arr.Length(); ++i) {\n    value_type value;\n    if (!ToNativeValue(arr[i], &value)) return false;\n    values.push_back(std::move(value));\n  }\n  *v = std::move(values);\n  return true;\n}\n\ntemplate <typename Vector, typename value_type = typename Vector::value_type>\nNapi::Value VectorToJSValue(const Napi::CallbackInfo& info,\n                            const Vector& value) {\n  Napi::Array ret = Napi::Array::New(info.Env(), value.size());\n  for (size_t i = 0; i < value.size(); ++i) {\n    ret[i] = CppValueTraits<value_type>::ToJSValue(info, value[i]);\n  }\n  return ret;\n}\n\ntemplate <typename T>\nclass CppValueTraits<std::vector<T>> {\n public:\n  static bool ToNativeValue(const Napi::Value& value, std::vector<T>* v) {\n    return VectorToNativeValueHelper(value, v);\n  }\n\n  static Napi::Value ToJSValue(const Napi::CallbackInfo& info,\n                               const std::vector<T>& value) {\n    return VectorToJSValue(info, value);\n  }\n\n  static std::string GetTypeName() {\n    return base::MakeCppVectorTypeName(\n        CppValueTraits<NativeCppType<T>>::GetTypeName());\n  }\n};\n\ntemplate <typename T, size_t N>\nclass CppValueTraits<absl::InlinedVector<T, N>> {\n public:\n  static bool ToNativeValue(const Napi::Value& value,\n                            absl::InlinedVector<T, N>* v) {\n    return VectorToNativeValueHelper(value, v);\n  }\n\n  static Napi::Value ToJSValue(const Napi::CallbackInfo& info,\n                               const absl::InlinedVector<T, N>& value) {\n    return VectorToJSValue(info, value);\n  }\n\n  static std::string GetTypeName() {\n    return base::MakeCppVectorTypeName(\n        CppValueTraits<NativeCppType<T>>::GetTypeName());\n  }\n};\n\ntemplate <typename T>\nclass CppValueTraits<T*> {\n  using Class = std::decay_t<T>;\n  using Object = NodeCppObject<Class>;\n\n public:\n  static bool ToNativeValue(const Napi::Value& value, T** v) {\n    if (!value.IsObject()) return false;\n    if (!NodeConstructors::GetInstance().InstanceOf(\n            value.ToObject(), base::CppType<T>::Get().name()))\n      return false;\n    Object* object = Napi::ObjectWrap<Object>::Unwrap(value.As<Napi::Object>());\n    base::CppValue* cpp_value = object->value();\n    if (!std::is_const<T>::value && cpp_value->is_const()) return false;\n    T* raw_ptr = reinterpret_cast<T*>(cpp_value->raw_ptr());\n    if (!raw_ptr) return false;\n    *v = raw_ptr;\n    return true;\n  }\n\n  static Napi::Value ToJSValue(const Napi::CallbackInfo& info, T* v) {\n    return Object::NewInstance(info.Env(),\n                               Napi::External<base::CppValue>::New(\n                                   info.Env(), new base::CppRawPtr<T>(v)));\n  }\n\n  static std::string GetTypeName() {\n    return base::MakeCppRawPtrTypeName(\n        CppValueTraits<NativeCppType<T>>::GetTypeName());\n  }\n};\n\ntemplate <typename T>\nclass CppValueTraits<std::reference_wrapper<T>> {\n  using Class = std::decay_t<T>;\n  using Object = NodeCppObject<Class>;\n\n public:\n  static Napi::Value ToJSValue(const Napi::CallbackInfo& info,\n                               std::reference_wrapper<T> v) {\n    return Object::NewInstance(\n        info.Env(),\n        Napi::External<base::CppValue>::New(\n            info.Env(), new base::CppRawPtr<T>(std::addressof(v.get()))));\n  }\n};\n\ntemplate <typename T>\nclass CppValueTraits<std::shared_ptr<T>> {\n  using Class = std::decay_t<T>;\n  using Object = NodeCppObject<Class>;\n\n public:\n  static bool ToNativeValue(const Napi::Value& value, std::shared_ptr<T>* v) {\n    if (!value.IsObject()) return false;\n    if (!NodeConstructors::GetInstance().InstanceOf(\n            value.ToObject(), base::CppType<T>::Get().name()))\n      return false;\n    Object* object = Napi::ObjectWrap<Object>::Unwrap(value.As<Napi::Object>());\n    base::CppValue* cpp_value = object->value();\n    if (!std::is_const<T>::value && cpp_value->is_const()) return false;\n    if (!cpp_value->IsCppSharedPtr()) return false;\n    *v = cpp_value->ToCppSharedPtr<T>()->shared_ptr();\n    return true;\n  }\n\n  static Napi::Value ToJSValue(const Napi::CallbackInfo& info,\n                               const std::shared_ptr<T>& v) {\n    return Object::NewInstance(info.Env(),\n                               Napi::External<base::CppValue>::New(\n                                   info.Env(), new base::CppSharedPtr<T>(v)));\n  }\n\n  static std::string GetTypeName() {\n    return base::MakeCppSharedPtrTypeName(\n        CppValueTraits<NativeCppType<T>>::GetTypeName());\n  }\n};\n\ntemplate <typename T, typename Deleter>\nclass CppValueTraits<std::unique_ptr<T, Deleter>> {\n  using Class = std::decay_t<T>;\n  using Object = NodeCppObject<Class>;\n\n public:\n  static bool ToNativeValue(const Napi::Value& value,\n                            std::unique_ptr<T, Deleter>* v) {\n    if (!value.IsObject()) return false;\n    if (!NodeConstructors::GetInstance().InstanceOf(\n            value.ToObject(), base::CppType<T>::Get().name()))\n      return false;\n    Object* object = Napi::ObjectWrap<Object>::Unwrap(value.As<Napi::Object>());\n    base::CppValue* cpp_value = object->value();\n    if (!std::is_const<T>::value && cpp_value->is_const()) return false;\n    if (!cpp_value->IsCppUniquePtr()) return false;\n    *v = std::move(cpp_value->ToCppUniquePtr<T, Deleter>()->unique_ptr());\n    return true;\n  }\n\n  static Napi::Value ToJSValue(const Napi::CallbackInfo& info,\n                               std::unique_ptr<T, Deleter> v) {\n    return Object::NewInstance(\n        info.Env(),\n        Napi::External<base::CppValue>::New(\n            info.Env(), new base::CppUniquePtr<T, Deleter>(std::move(v))));\n  }\n\n  static std::string GetTypeName() {\n    return base::MakeCppUniquePtrTypeName(\n        CppValueTraits<NativeCppType<T>>::GetTypeName());\n  }\n};\n\ntemplate <typename T>\nclass CppValueTraits<base::CppStackValue<T>> {\n  using Class = std::decay_t<T>;\n  using Object = NodeCppObject<Class>;\n\n public:\n  static bool ToNativeValue(const Napi::Value& value, T* v) {\n    if (!value.IsObject()) return false;\n    if (!NodeConstructors::GetInstance().InstanceOf(\n            value.ToObject(), base::CppType<T>::Get().name()))\n      return false;\n    Object* object = Napi::ObjectWrap<Object>::Unwrap(value.As<Napi::Object>());\n    base::CppValue* cpp_value = object->value();\n    if (!std::is_const<T>::value && cpp_value->is_const()) return false;\n    AssignHelper(cpp_value, v);\n    return true;\n  }\n\n  static Napi::Value ToJSValue(const Napi::CallbackInfo& info, const T& v) {\n    return Object::NewInstance(info.Env(),\n                               Napi::External<base::CppValue>::New(\n                                   info.Env(), new base::CppStackValue<T>(v)));\n  }\n\n  static Napi::Value ToJSValue(const Napi::CallbackInfo& info, T&& v) {\n    return Object::NewInstance(\n        info.Env(), Napi::External<base::CppValue>::New(\n                        info.Env(), new base::CppStackValue<T>(std::move(v))));\n  }\n\n  static const std::string& GetTypeName() {\n    return base::CppType<T>::Get().name();\n  }\n\n private:\n  template <typename U = T,\n            std::enable_if_t<std::is_copy_assignable<U>::value>* = nullptr>\n  static void AssignHelper(base::CppValue* value, T* v) {\n    *v = *reinterpret_cast<T*>(value->raw_ptr());\n  }\n\n  template <typename U = T,\n            std::enable_if_t<!std::is_copy_assignable<U>::value>* = nullptr>\n  static void AssignHelper(base::CppValue* value, T* v) {\n    *v = std::move(*reinterpret_cast<T*>(value->raw_ptr()));\n  }\n};\n\ntemplate <typename T>\nclass CppValueTraits<std::optional<T>> {\n public:\n  static bool ToNativeValue(const Napi::Value& value, std::optional<T>* v) {\n    if (value.IsUndefined() || value.IsNull())\n      *v = std::nullopt;\n    else\n      *v = reinterpret_cast<T>(value);\n    return true;\n  }\n\n  static Napi::Value ToJSValue(const Napi::CallbackInfo& info,\n                               const std::optional<T>& value) {\n    if (!value)\n      return Napi::Value(info.Env(), nullptr);\n    else\n      return CppValueTraits<NativeCppType<T>>::ToJSValue(info, *value);\n  }\n\n  static std::string GetTypeName() {\n    return base::MakeCppOptionalTypeName(\n        CppValueTraits<NativeCppType<T>>::GetTypeName());\n  }\n};\n\ntemplate <typename T>\nbool ToNativeValue(const Napi::Value& value, T* v) {\n  return CppValueTraits<NativeCppType<T>>::ToNativeValue(value, v);\n}\n\ntemplate <typename T>\nNapi::Value ToJSValue(const Napi::CallbackInfo& info, T&& value) {\n  return CppValueTraits<JSCppType<T>>::ToJSValue(info, std::forward<T>(value));\n}\n\n}  // namespace internal\n}  // namespace tachyon::node\n\n#endif  // TACHYON_NODE_BASE_NODE_INTERNALS_H_\n"
  },
  {
    "path": "tachyon/node/base/node_internals_forwards.h",
    "content": "#ifndef TACHYON_NODE_BASE_NODE_INTERNALS_FORWARDS_H_\n#define TACHYON_NODE_BASE_NODE_INTERNALS_FORWARDS_H_\n\n#include \"absl/meta/type_traits.h\"\n#include \"third_party/node_addon_api/napi.h\"\n\nnamespace tachyon::node {\nnamespace internal {\n\ntemplate <typename T, typename SFINAE = void>\nclass CppValueTraits;\n\ntemplate <typename, typename SFINAE = void>\nstruct JSCppTypeWrapper;\n\ntemplate <typename T>\nusing JSCppType = typename JSCppTypeWrapper<T>::Type;\n\ntemplate <typename, typename SFINAE = void>\nstruct NativeCppTypeWrapper;\n\ntemplate <typename T>\nusing NativeCppType = typename NativeCppTypeWrapper<T>::Type;\n\ntemplate <typename T>\nbool ToNativeValue(const Napi::Value& value, T* v);\n\ntemplate <typename T>\nNapi::Value ToJSValue(const Napi::CallbackInfo& info, T&& value);\n\ntemplate <typename, typename = void>\nstruct HasToNativeValue : std::false_type {};\n\ntemplate <typename T>\nstruct HasToNativeValue<T,\n                        absl::void_t<decltype(CppValueTraits<T>::ToNativeValue(\n                            std::declval<Napi::Value>(), std::declval<T*>()))>>\n    : std::true_type {};\n\ntemplate <typename, typename = void>\nstruct HasToJSValue : std::false_type {};\n\ntemplate <typename T>\nstruct HasToJSValue<\n    T, absl::void_t<decltype(CppValueTraits<T>::ToJSValue(\n           std::declval<Napi::CallbackInfo>(), std::declval<T>()))>>\n    : std::true_type {};\n\n}  // namespace internal\n}  // namespace tachyon::node\n\n#endif  // TACHYON_NODE_BASE_NODE_INTERNALS_FORWARDS_H_\n"
  },
  {
    "path": "tachyon/node/base/node_module.cc",
    "content": "#include \"tachyon/node/base/node_module.h\"\n\nnamespace tachyon::node {\n\nNodeModule::NodeModule(Napi::Env env, Napi::Object exports)\n    : NodeCppBindable(env, exports) {}\n\nNodeModule::NodeModule(Napi::Env env, Napi::Object exports,\n                       std::string_view name, std::string_view parent_full_name)\n    : NodeCppBindable(env, exports, name, parent_full_name) {}\n\nNodeModule::NodeModule(NodeModule&& other) noexcept = default;\n\nNodeModule& NodeModule::operator=(NodeModule&& other) noexcept = default;\n\nNodeModule::~NodeModule() = default;\n\nNodeModule NodeModule::AddSubModule(std::string_view name) {\n  Napi::Object exports = Napi::Object::New(env_);\n  exports_.Set(name.data(), exports);\n  return NodeModule(env_, exports, name, full_name_);\n}\n\n}  // namespace tachyon::node\n"
  },
  {
    "path": "tachyon/node/base/node_module.h",
    "content": "#ifndef TACHYON_NODE_BASE_NODE_MODULE_H_\n#define TACHYON_NODE_BASE_NODE_MODULE_H_\n\n#include <utility>\n\n#include \"tachyon/export.h\"\n#include \"tachyon/node/base/node_cpp_bindable.h\"\n#include \"tachyon/node/base/node_cpp_callable.h\"\n#include \"tachyon/node/base/node_cpp_callable_data.h\"\n#include \"tachyon/node/base/node_cpp_class.h\"\n#include \"tachyon/node/base/node_cpp_enum.h\"\n#include \"tachyon/node/base/node_internals.h\"\n\nnamespace tachyon::node {\n\nclass TACHYON_EXPORT NodeModule : public NodeCppBindable {\n public:\n  NodeModule(Napi::Env env, Napi::Object exports);\n  NodeModule(const NodeModule& other) = delete;\n  NodeModule& operator=(const NodeModule& other) = delete;\n  NodeModule(NodeModule&& other) noexcept;\n  NodeModule& operator=(NodeModule&& other) noexcept;\n  ~NodeModule();\n\n  NodeModule AddSubModule(std::string_view name);\n\n  template <typename R, typename... Args, typename... DefaultArgs>\n  NodeModule& AddFunction(std::string_view name, R (*f)(Args...),\n                          DefaultArgs&&... default_args) {\n    static_assert(sizeof...(Args) >= sizeof...(DefaultArgs),\n                  \"Too many default args\");\n    using Function = R (*)(Args...);\n    NodeCppFunctionData* data = new NodeCppFunctionData;\n    data->function = reinterpret_cast<void*>(f);\n    data->Set(std::forward<DefaultArgs>(default_args)...);\n    exports_.Set(name.data(),\n                 Napi::Function::New<NodeCppCallable::Call<Function>>(\n                     env_, name.data(), data));\n    return *this;\n  }\n\n  template <typename Class, typename... Options>\n  NodeCppClass<Class, Options...> NewClass(std::string_view name) {\n    return NodeCppClass<Class, Options...>(env_, exports_, name, full_name_);\n  }\n\n  template <typename Enum>\n  NodeCppEnum<Enum> NewEnum(std::string_view name) {\n    return NodeCppEnum<Enum>(env_, exports_, name, full_name_);\n  }\n\n private:\n  NodeModule(Napi::Env env, Napi::Object exports, std::string_view name,\n             std::string_view parent_full_name);\n};\n\n}  // namespace tachyon::node\n\n#endif  // TACHYON_NODE_BASE_NODE_MODULE_H_\n"
  },
  {
    "path": "tachyon/node/base/test/BUILD.bazel",
    "content": "load(\"//bazel:tachyon.bzl\", \"if_node_binding\")\nload(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\nload(\"//bazel:tachyon_node.bzl\", \"tachyon_node_library\")\n\ntachyon_cc_library(\n    name = \"color\",\n    testonly = True,\n    srcs = if_node_binding([\"color.cc\"]),\n    hdrs = [\"color.h\"],\n    deps = [\n        \"//tachyon/base/binding/test:enum\",\n        \"//tachyon/node/base:node_base\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"point\",\n    testonly = True,\n    srcs = if_node_binding([\"point.cc\"]),\n    hdrs = [\"point.h\"],\n    deps = [\n        \"//tachyon/base/binding/test:class\",\n        \"//tachyon/node/base:node_base\",\n    ],\n)\n\ntachyon_node_library(\n    name = \"test\",\n    testonly = True,\n    srcs = if_node_binding([\"test.cc\"]),\n    deps = [\n        \":color\",\n        \":point\",\n        \"//tachyon/base/binding/test:functions\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/node/base/test/color.cc",
    "content": "#include \"tachyon/node/base/test/color.h\"\n\n#include \"tachyon/base/binding/test/color.h\"\n\nvoid AddColor(tachyon::node::NodeModule& m) {\n  using namespace tachyon::base::test;\n}\n"
  },
  {
    "path": "tachyon/node/base/test/color.h",
    "content": "#ifndef TACHYON_NODE_BASE_TEST_COLOR_H_\n#define TACHYON_NODE_BASE_TEST_COLOR_H_\n\n#include \"tachyon/node/base/node_module.h\"\n\nvoid AddColor(tachyon::node::NodeModule& m);\n\n#endif  // TACHYON_NODE_BASE_TEST_COLOR_H_\n"
  },
  {
    "path": "tachyon/node/base/test/enum.cc",
    "content": "#include \"tachyon/node/base/test/color.h\"\n\nNapi::Object Init(Napi::Env env, Napi::Object exports) {\n  tachyon::node::NodeModule module(env, exports);\n  AddColor(module);\n  return exports;\n}\n\nNODE_API_MODULE(enum, Init)\n"
  },
  {
    "path": "tachyon/node/base/test/function.cc",
    "content": "#include \"tachyon/base/binding/test/functions.h\"\n#include \"tachyon/node/base/node_module.h\"\n\nNapi::Object Init(Napi::Env env, Napi::Object exports) {\n  using namespace tachyon::base::test;\n  tachyon::node::NodeModule module(env, exports);\n  module.AddFunction(\"hello\", &Hello)\n      .AddFunction(\"sum\", &Sum, 1, 2)\n      .AddFunction(\"do_nothing\", &DoNothing);\n  return exports;\n}\n\nNODE_API_MODULE(function, Init)\n"
  },
  {
    "path": "tachyon/node/base/test/point.cc",
    "content": "#include \"tachyon/node/base/test/point.h\"\n\n#include <memory>\n\nvoid AddPoint(tachyon::node::NodeModule& m) {\n  AddPointImpl<tachyon::base::test::Point>(m, \"Point\");\n}\n\nvoid AddSharedPoint(tachyon::node::NodeModule& m) {\n  AddPointImpl<std::shared_ptr<tachyon::base::test::Point>>(m, \"Point\");\n}\n\nvoid AddUniquePoint(tachyon::node::NodeModule& m) {\n  AddPointImpl<std::unique_ptr<tachyon::base::test::Point>>(m, \"Point\");\n}\n\ntachyon::base::test::Point DoubleWithValue(tachyon::base::test::Point p) {\n  return {p.x * 2, p.y * 2};\n}\n\nvoid DoubleWithReference(tachyon::base::test::Point& p) {\n  p.x *= 2;\n  p.y *= 2;\n}\n\nvoid DoubleWithSharedPtr(std::shared_ptr<tachyon::base::test::Point> p) {\n  p->x *= 2;\n  p->y *= 2;\n}\n\nvoid DoubleWithUniquePtr(std::unique_ptr<tachyon::base::test::Point> p) {}\n"
  },
  {
    "path": "tachyon/node/base/test/point.h",
    "content": "#ifndef TACHYON_NODE_BASE_TEST_POINT_H_\n#define TACHYON_NODE_BASE_TEST_POINT_H_\n\n#include <memory>\n#include <string_view>\n\n#include \"tachyon/base/binding/test/point.h\"\n#include \"tachyon/node/base/node_module.h\"\n\ntemplate <typename Holder>\nvoid AddPointImpl(tachyon::node::NodeModule& m, std::string_view name) {\n  using namespace tachyon::base::test;\n  m.NewClass<Point, Holder>(name)\n      .template AddConstructor<>()\n      .template AddConstructor<int, int>()\n      .AddStaticMethod(\"getDimension\", &Point::GetDimension)\n      .AddStaticMethod(\"setDimension\", &Point::SetDimension)\n      .AddStaticMethod(\"distance\", &Point::Distance)\n      .AddStaticProperty(\"propertyDimension\", &Point::GetDimension,\n                         &Point::SetDimension)\n      .AddStaticReadWrite(\"dimension\", &Point::s_dimension)\n      .AddMethod(\"getX\", &Point::GetX)\n      .AddMethod(\"setX\", &Point::SetX)\n      .AddMethod(\"getY\", &Point::GetY)\n      .AddMethod(\"setY\", &Point::SetY)\n      .AddProperty(\"propertyX\", &Point::GetX, &Point::SetX)\n      .AddProperty(\"propertyY\", &Point::GetY, &Point::SetY)\n      .AddReadWrite(\"x\", &Point::x)\n      .AddReadWrite(\"y\", &Point::y);\n}\n\nvoid AddPoint(tachyon::node::NodeModule& m);\n\nvoid AddSharedPoint(tachyon::node::NodeModule& m);\n\nvoid AddUniquePoint(tachyon::node::NodeModule& m);\n\ntachyon::base::test::Point DoubleWithValue(tachyon::base::test::Point p);\n\nvoid DoubleWithReference(tachyon::base::test::Point& p);\n\nvoid DoubleWithSharedPtr(std::shared_ptr<tachyon::base::test::Point> p);\n\nvoid DoubleWithUniquePtr(std::unique_ptr<tachyon::base::test::Point> p);\n\n#endif  // TACHYON_NODE_BASE_TEST_POINT_H_\n"
  },
  {
    "path": "tachyon/node/base/test/test.cc",
    "content": "#include \"tachyon/base/binding/test/adder.h\"\n#include \"tachyon/base/binding/test/colored_point.h\"\n#include \"tachyon/base/binding/test/functions.h\"\n#include \"tachyon/base/binding/test/rect.h\"\n#include \"tachyon/base/binding/test/variant.h\"\n#include \"tachyon/node/base/node_module.h\"\n#include \"tachyon/node/base/test/color.h\"\n#include \"tachyon/node/base/test/point.h\"\n\nvoid AcceptColoredPoint(tachyon::base::test::ColoredPoint& cp) {}\n\nNapi::Object Init(Napi::Env env, Napi::Object exports) {\n  using namespace tachyon::base::test;\n\n  tachyon::node::NodeModule m(env, exports);\n\n  m.AddFunction(\"hello\", &Hello)\n      .AddFunction(\"sum\", &Sum, 1, 2)\n      .AddFunction(\"do_nothing\", &DoNothing);\n\n  m.NewEnum<Color>(\"color\")\n      .AddValue(\"red\", Color::kRed)\n      .AddValue(\"green\", Color::kGreen)\n      .AddValue(\"blue\", Color::kBlue);\n\n  // TODO(chokobole): I tried testing all these variation either in same addon\n  // or different addon. But it somehow corrupts this addon. So I need to figure\n  // out how to gracefully test! Uncomment either one of AddXXXPoint.\n  AddPoint(m);\n  // This enable 'shared_ptr' test in 'class.spec.ts'.\n  // AddSharedPoint(m);\n  // This enable 'unique_ptr' test in 'class.spec.ts'.\n  // AddUniquePoint(m);\n\n  m.NewClass<ColoredPoint, Point>(\"ColoredPoint\")\n      .AddConstructor<>()\n      .AddConstructor<int, int, Color>();\n\n  m.NewClass<Rect>(\"Rect\")\n      .AddConstructor<>()\n      .AddConstructor<const Point&, const Point&>()\n      .AddReadWrite(\"topLeft\", &Rect::top_left)\n      .AddReadWrite(\"bottomRight\", &Rect::bottom_right)\n      .AddMethod(\"getTopLeft\", &Rect::GetTopLeft)\n      .AddMethod(\"getConstTopLeft\", &Rect::GetConstTopLeft)\n      .AddMethod(\"getBottomRight\", &Rect::GetBottomRight)\n      .AddMethod(\"getConstBottomRight\", &Rect::GetConstBottomRight);\n\n  m.NewClass<Adder>(\"Adder\")\n      .AddConstructor<>()\n      .AddMethod(\"add\", &Adder::Add, 1, 2, 3, 4)\n      .AddStaticMethod(\"sAdd\", &Adder::SAdd, 1, 2, 3, 4);\n\n  m.NewClass<Variant>(\"Variant\")\n      .AddConstructor<bool>()\n      .AddConstructor<int>()\n      .AddConstructor<int64_t>()\n      .AddConstructor<const std::string&>()\n      .AddConstructor<const std::vector<int>&>()\n      .AddConstructor<int, const std::string&>()\n      .AddConstructor<const Point&>()\n      .AddReadWrite(\"b\", &Variant::b)\n      .AddReadWrite(\"i\", &Variant::i)\n      .AddReadWrite(\"i64\", &Variant::i64)\n      .AddReadWrite(\"s\", &Variant::s)\n      .AddReadWrite(\"ivec\", &Variant::ivec)\n      .AddReadWrite(\"p\", &Variant::p);\n\n  m.AddFunction(\"doubleWithValue\", &DoubleWithValue)\n      .AddFunction(\"doubleWithReference\", &DoubleWithReference)\n      .AddFunction(\"doubleWithSharedPtr\", &DoubleWithSharedPtr)\n      .AddFunction(\"doubleWithUniquePtr\", &DoubleWithUniquePtr)\n      .AddFunction(\"acceptColoredPoint\", &AcceptColoredPoint);\n  return exports;\n}\n\nNODE_API_MODULE(test, Init)\n"
  },
  {
    "path": "tachyon/node/math/BUILD.bazel",
    "content": "load(\"//bazel:tachyon.bzl\", \"if_node_binding\")\nload(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//tachyon/node:__pkg__\"])\n\nTPLS = [\n    \"//tachyon/node/math/elliptic_curves/{}:fq\",\n    \"//tachyon/node/math/elliptic_curves/{}:fr\",\n    \"//tachyon/node/math/elliptic_curves/{}:g1\",\n]\n\nCURVES = [\n    \"bls12/bls12_381\",\n    \"bn/bn254\",\n]\n\nCURVE_DEPS = [tpl.format(curve) for tpl in TPLS for curve in CURVES]\n\ntachyon_cc_library(\n    name = \"math\",\n    srcs = if_node_binding([\"math.cc\"]),\n    hdrs = [\"math.h\"],\n    deps = CURVE_DEPS,\n)\n"
  },
  {
    "path": "tachyon/node/math/base/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//tachyon/node/math:__subpackages__\"])\n\ntachyon_cc_library(\n    name = \"big_int\",\n    hdrs = [\"big_int.h\"],\n    deps = [\n        \"//tachyon/math/base:big_int\",\n        \"//tachyon/node/base:node_base\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/node/math/base/big_int.h",
    "content": "#ifndef TACHYON_NODE_MATH_BASE_BIG_INT_H_\n#define TACHYON_NODE_MATH_BASE_BIG_INT_H_\n\n#include <string>\n\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/math/base/big_int.h\"\n#include \"tachyon/node/base/node_internals_forwards.h\"\n\nnamespace tachyon::node {\nnamespace internal {\n\ntemplate <size_t N>\nclass CppValueTraits<tachyon::math::BigInt<N>> {\n public:\n  static bool ToNativeValue(const Napi::Value& value,\n                            tachyon::math::BigInt<N>* v) {\n#if NAPI_VERSION > 5\n    if (value.IsBigInt()) {\n      int sign_bit;\n      size_t word_count = N;\n      value.As<Napi::BigInt>().ToWords(&sign_bit, &word_count, v->limbs);\n      if (sign_bit == 1) return false;\n      return word_count <= N;\n    }\n#endif\n    if (!value.IsNumber()) return false;\n    int64_t d = value.As<Napi::Number>().Int64Value();\n    if (d < 0) return false;\n    *v = tachyon::math::BigInt<N>(d);\n    return true;\n  }\n\n  static Napi::Value ToJSValue(const Napi::CallbackInfo& info,\n                               const tachyon::math::BigInt<N>& value) {\n#if NAPI_VERSION > 5\n    return Napi::BigInt::New(info.Env(), false, N, value.limbs);\n#else\n    NAPI_THROW(Napi::TypeError::New(env, \"BigInt is not supported\"));\n#endif\n  }\n\n  static std::string GetTypeName() {\n    return absl::Substitute(\"tachyon::tachyon::math::Bigint<$0>\", N);\n  }\n};\n\ntemplate <>\nclass CppValueTraits<tachyon::math::BigInt<1>> {\n public:\n  static bool ToNativeValue(const Napi::Value& value,\n                            tachyon::math::BigInt<1>* v) {\n    uint64_t v_tmp;\n    if (!CppValueTraits<uint64_t>::ToNativeValue(value, &v_tmp)) return false;\n    *v = tachyon::math::BigInt<1>(v_tmp);\n    return true;\n  }\n\n  static Napi::Value ToJSValue(const Napi::CallbackInfo& info,\n                               tachyon::math::BigInt<1>& value) {\n    return CppValueTraits<uint64_t>::ToJSValue(info, value[0]);\n  }\n\n  static const char* GetTypeName() {\n    return \"tachyon::tachyon::math::Bigint<1>\";\n  }\n};\n\n}  // namespace internal\n}  // namespace tachyon::node\n\n#endif  // TACHYON_NODE_MATH_BASE_BIG_INT_H_\n"
  },
  {
    "path": "tachyon/node/math/elliptic_curves/bls12/bls12_381/BUILD.bazel",
    "content": "load(\"//tachyon/node/math/elliptic_curves/generator:build_defs.bzl\", \"generate_ec_points\")\n\npackage(default_visibility = [\"//tachyon/node/math:__pkg__\"])\n\ngenerate_ec_points(\n    name = \"bls12_381\",\n    g1_deps = [\"//tachyon/math/elliptic_curves/bls12/bls12_381:g1\"],\n)\n"
  },
  {
    "path": "tachyon/node/math/elliptic_curves/bn/bn254/BUILD.bazel",
    "content": "load(\"//tachyon/node/math/elliptic_curves/generator:build_defs.bzl\", \"generate_ec_points\")\n\npackage(default_visibility = [\"//tachyon/node/math:__pkg__\"])\n\ngenerate_ec_points(\n    name = \"bn254\",\n    g1_deps = [\"//tachyon/math/elliptic_curves/bn/bn254:g1\"],\n)\n"
  },
  {
    "path": "tachyon/node/math/elliptic_curves/generator/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_binary\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_binary(\n    name = \"generator\",\n    srcs = [\"generator.cc\"],\n    deps = [\n        \"//tachyon/base/console\",\n        \"//tachyon/base/files:file_path_flag\",\n        \"//tachyon/base/flag:flag_parser\",\n        \"//tachyon/build:cc_writer\",\n        \"//tachyon/c/math/elliptic_curves/generator:generator_util\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/node/math/elliptic_curves/generator/build_defs.bzl",
    "content": "load(\"//bazel:tachyon.bzl\", \"if_node_binding\")\nload(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\ndef _generate_ec_point_impl(ctx):\n    arguments = [\n        \"--out=%s\" % (ctx.outputs.out.path),\n        \"--type=%s\" % (ctx.attr.type),\n    ]\n\n    ctx.actions.run(\n        tools = [ctx.executable._tool],\n        executable = ctx.executable._tool,\n        outputs = [ctx.outputs.out],\n        arguments = arguments,\n    )\n\n    return [DefaultInfo(files = depset([ctx.outputs.out]))]\n\ngenerate_ec_point = rule(\n    implementation = _generate_ec_point_impl,\n    attrs = {\n        \"out\": attr.output(mandatory = True),\n        \"type\": attr.string(mandatory = True),\n        \"_tool\": attr.label(\n            # TODO(chokobole): Change to \"exec\", so we can build on macos.\n            cfg = \"target\",\n            executable = True,\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/node/math/elliptic_curves/generator\"),\n        ),\n    },\n)\n\ndef generate_ec_points(\n        name,\n        g1_deps):\n    for n in [\n        (\"gen_fq_hdr\", \"fq.h\"),\n        (\"gen_fq_src\", \"fq.cc\"),\n        (\"gen_fr_hdr\", \"fr.h\"),\n        (\"gen_fr_src\", \"fr.cc\"),\n        (\"gen_g1_hdr\", \"g1.h\"),\n        (\"gen_g1_src\", \"g1.cc\"),\n    ]:\n        generate_ec_point(\n            type = name,\n            name = n[0],\n            out = n[1],\n        )\n\n    tachyon_cc_library(\n        name = \"fq\",\n        hdrs = [\"fq.h\"],\n        srcs = if_node_binding([\"fq.cc\"]),\n        deps = g1_deps + [\n            \"//tachyon/node/math/finite_fields:prime_field\",\n        ],\n    )\n\n    tachyon_cc_library(\n        name = \"fr\",\n        hdrs = [\"fr.h\"],\n        srcs = if_node_binding([\"fr.cc\"]),\n        deps = g1_deps + [\n            \"//tachyon/node/math/finite_fields:prime_field\",\n        ],\n    )\n\n    tachyon_cc_library(\n        name = \"g1\",\n        hdrs = [\"g1.h\"],\n        srcs = if_node_binding([\"g1.cc\"]),\n        deps = g1_deps + [\n            \"//tachyon/node/math/elliptic_curves/short_weierstrass:points\",\n        ],\n    )\n"
  },
  {
    "path": "tachyon/node/math/elliptic_curves/generator/generator.cc",
    "content": "#include \"absl/strings/str_replace.h\"\n\n#include \"tachyon/base/console/iostream.h\"\n#include \"tachyon/base/files/file_path_flag.h\"\n#include \"tachyon/base/flag/flag_parser.h\"\n#include \"tachyon/base/strings/string_util.h\"\n#include \"tachyon/build/cc_writer.h\"\n#include \"tachyon/c/math/elliptic_curves/generator/generator_util.h\"\n\nnamespace tachyon {\n\nstruct GenerationConfig : public build::CcWriter {\n  std::string type;\n\n  int GeneratePrimeFieldHdr(std::string_view suffix) const;\n  int GenerateFqHdr() const;\n  int GenerateFrHdr() const;\n  int GeneratePrimeFieldSrc(std::string_view suffix) const;\n  int GenerateFqSrc() const;\n  int GenerateFrSrc() const;\n  int GenerateG1Hdr() const;\n  int GenerateG1Src() const;\n};\n\nint GenerationConfig::GeneratePrimeFieldHdr(std::string_view suffix) const {\n  // clang-format off\n  std::vector<std::string_view> tpl = {\n      \"#include \\\"tachyon/node/base/node_module.h\\\"\",\n      \"\",\n      \"namespace tachyon::node::math::%{type} {\",\n      \"\",\n      \"void Add%{cc_field}(NodeModule& m);\",\n      \"\",\n      \"} // namespace tachyon::node::math::%{type}\",\n  };\n  // clang-format on\n\n  std::string tpl_content = absl::StrJoin(tpl, \"\\n\");\n\n  std::string content = absl::StrReplaceAll(\n      tpl_content, {\n                       {\"%{type}\", type},\n                       {\"%{cc_field}\", suffix == \"fq\" ? \"Fq\" : \"Fr\"},\n                   });\n  return WriteHdr(content, false);\n}\n\nint GenerationConfig::GenerateFqHdr() const {\n  return GeneratePrimeFieldHdr(\"fq\");\n}\n\nint GenerationConfig::GenerateFrHdr() const {\n  return GeneratePrimeFieldHdr(\"fr\");\n}\n\nint GenerationConfig::GeneratePrimeFieldSrc(std::string_view suffix) const {\n  // clang-format off\n  std::vector<std::string_view> tpl = {\n      \"#include \\\"tachyon/math/elliptic_curves/%{header_dir_name}/%{suffix}.h\\\"\",\n      \"#include \\\"tachyon/node/math/finite_fields/prime_field.h\\\"\",\n      \"\",\n      \"namespace tachyon::node::math::%{type} {\",\n      \"\",\n      \"void Add%{cc_field}(NodeModule& m) {\",\n      \"  AddPrimeField<tachyon::math::%{type}::%{cc_field}>(m, \\\"%{cc_field}\\\");\",\n      \"}\",\n      \"\",\n      \"} // namespace tachyon::node::math::%{type}\",\n  };\n  // clang-format on\n\n  std::string tpl_content = absl::StrJoin(tpl, \"\\n\");\n\n  std::string content = absl::StrReplaceAll(\n      tpl_content, {\n                       {\"%{header_dir_name}\", c::math::GetLocation(type)},\n                       {\"%{suffix}\", suffix},\n                       {\"%{type}\", type},\n                       {\"%{cc_field}\", suffix == \"fq\" ? \"Fq\" : \"Fr\"},\n                   });\n  return WriteSrc(content);\n}\n\nint GenerationConfig::GenerateFqSrc() const {\n  return GeneratePrimeFieldSrc(\"fq\");\n}\n\nint GenerationConfig::GenerateFrSrc() const {\n  return GeneratePrimeFieldSrc(\"fr\");\n}\n\nint GenerationConfig::GenerateG1Hdr() const {\n  // clang-format off\n  std::vector<std::string_view> tpl = {\n      \"#include \\\"tachyon/node/base/node_module.h\\\"\",\n      \"\",\n      \"namespace tachyon::node::math::%{type} {\",\n      \"\",\n      \"void AddG1(NodeModule& m);\",\n      \"\",\n      \"} // namespace tachyon::node::math::%{type}\",\n  };\n  // clang-format on\n\n  std::string tpl_content = absl::StrJoin(tpl, \"\\n\");\n\n  std::string content = absl::StrReplaceAll(tpl_content, {\n                                                             {\"%{type}\", type},\n                                                         });\n  return WriteHdr(content, false);\n}\n\nint GenerationConfig::GenerateG1Src() const {\n  // clang-format off\n  std::vector<std::string_view> tpl = {\n      \"#include \\\"tachyon/math/elliptic_curves/%{header_dir_name}/g1.h\\\"\",\n      \"#include \\\"tachyon/node/math/elliptic_curves/short_weierstrass/affine_point.h\\\"\",\n      \"#include \\\"tachyon/node/math/elliptic_curves/short_weierstrass/projective_point.h\\\"\",\n      \"#include \\\"tachyon/node/math/elliptic_curves/short_weierstrass/jacobian_point.h\\\"\",\n      \"#include \\\"tachyon/node/math/elliptic_curves/short_weierstrass/point_xyzz.h\\\"\",\n      \"\",\n      \"namespace tachyon::node::math::%{type} {\",\n      \"\",\n      \"void AddG1(NodeModule& m) {\",\n      \"  AddJacobianPoint<tachyon::math::%{type}::G1JacobianPoint>(m, \\\"G1JacobianPoint\\\");\",\n      \"  AddAffinePoint<tachyon::math::%{type}::G1AffinePoint>(m, \\\"G1AffinePoint\\\");\",\n      \"  AddProjectivePoint<tachyon::math::%{type}::G1ProjectivePoint>(m, \\\"G1ProjectivePoint\\\");\",\n      \"  AddPointXYZZ<tachyon::math::%{type}::G1PointXYZZ>(m, \\\"G1PointXYZZ\\\");\",\n      \"}\",\n      \"\",\n      \"} // namespace tachyon::node::math::%{type}\",\n  };\n  // clang-format on\n\n  std::string tpl_content = absl::StrJoin(tpl, \"\\n\");\n\n  std::string content = absl::StrReplaceAll(\n      tpl_content, {\n                       {\"%{header_dir_name}\", c::math::GetLocation(type)},\n                       {\"%{type}\", type},\n                   });\n  return WriteSrc(content);\n}\n\nint RealMain(int argc, char** argv) {\n  GenerationConfig config;\n  config.generator = \"//tachyon/node/math/elliptic_curves/generator\";\n\n  base::FlagParser parser;\n  parser.AddFlag<base::FilePathFlag>(&config.out)\n      .set_long_name(\"--out\")\n      .set_help(\"path to output\");\n  parser.AddFlag<base::StringFlag>(&config.type)\n      .set_long_name(\"--type\")\n      .set_required();\n\n  std::string error;\n  if (!parser.Parse(argc, argv, &error)) {\n    tachyon_cerr << error << std::endl;\n    return 1;\n  }\n\n  if (base::EndsWith(config.out.value(), \"fq.h\")) {\n    return config.GenerateFqHdr();\n  } else if (base::EndsWith(config.out.value(), \"fq.cc\")) {\n    return config.GenerateFqSrc();\n  } else if (base::EndsWith(config.out.value(), \"fr.h\")) {\n    return config.GenerateFrHdr();\n  } else if (base::EndsWith(config.out.value(), \"fr.cc\")) {\n    return config.GenerateFrSrc();\n  } else if (base::EndsWith(config.out.value(), \"g1.h\")) {\n    return config.GenerateG1Hdr();\n  } else if (base::EndsWith(config.out.value(), \"g1.cc\")) {\n    return config.GenerateG1Src();\n  } else {\n    tachyon_cerr << \"suffix not supported:\" << config.out << std::endl;\n    return 1;\n  }\n}\n\n}  // namespace tachyon\n\nint main(int argc, char** argv) { return tachyon::RealMain(argc, argv); }\n"
  },
  {
    "path": "tachyon/node/math/elliptic_curves/short_weierstrass/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//tachyon/node/math/elliptic_curves:__subpackages__\"])\n\ntachyon_cc_library(\n    name = \"points\",\n    hdrs = [\n        \"affine_point.h\",\n        \"jacobian_point.h\",\n        \"point_xyzz.h\",\n        \"projective_point.h\",\n    ],\n    deps = [\n        \"//tachyon/math/geometry:affine_point\",\n        \"//tachyon/math/geometry:jacobian_point\",\n        \"//tachyon/math/geometry:point_xyzz\",\n        \"//tachyon/math/geometry:projective_point\",\n        \"//tachyon/node/base:node_base\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/node/math/elliptic_curves/short_weierstrass/affine_point.h",
    "content": "#ifndef TACHYON_NODE_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_AFFINE_POINT_H_\n#define TACHYON_NODE_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_AFFINE_POINT_H_\n\n#include \"tachyon/node/base/node_module.h\"\n\nnamespace tachyon::node::math {\n\ntemplate <typename AffinePoint,\n          typename BaseField = typename AffinePoint::BaseField>\nvoid AddAffinePoint(NodeModule& m, std::string_view name) {\n  m.NewClass<AffinePoint>(name)\n      .template AddConstructor<>()\n      .template AddConstructor<const BaseField&, const BaseField&>()\n      .AddStaticMethod(\"zero\", &AffinePoint::Zero)\n      .AddStaticMethod(\"generator\", &AffinePoint::Generator)\n      .AddStaticMethod(\"random\", &AffinePoint::Random)\n      .AddReadOnlyProperty(\"x\", &AffinePoint::x)\n      .AddReadOnlyProperty(\"y\", &AffinePoint::y)\n      .AddMethod(\"isZero\", &AffinePoint::IsZero)\n      .AddMethod(\"isOnCurve\", &AffinePoint::IsOnCurve)\n      .AddMethod(\"toString\", &AffinePoint::ToString)\n      .AddMethod(\"toHexString\", &AffinePoint::ToHexString, false)\n      .AddMethod(\"eq\", &AffinePoint::operator==)\n      .AddMethod(\"ne\", &AffinePoint::operator!=)\n      .AddMethod(\"add\", &AffinePoint::template operator+ <const AffinePoint&>)\n      .AddMethod(\"sub\", &AffinePoint::template operator- <const AffinePoint&>)\n      .AddMethod(\"negate\", static_cast<AffinePoint (AffinePoint::*)() const>(\n                               &AffinePoint::operator-))\n      .AddMethod(\"double\", &AffinePoint::Double);\n}\n\n}  // namespace tachyon::node::math\n\n#endif  // TACHYON_NODE_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_AFFINE_POINT_H_\n"
  },
  {
    "path": "tachyon/node/math/elliptic_curves/short_weierstrass/jacobian_point.h",
    "content": "#ifndef TACHYON_NODE_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_JACOBIAN_POINT_H_\n#define TACHYON_NODE_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_JACOBIAN_POINT_H_\n\n#include \"tachyon/math/geometry/affine_point.h\"\n#include \"tachyon/node/base/node_module.h\"\n\nnamespace tachyon::node::math {\n\ntemplate <typename JacobianPoint,\n          typename BaseField = typename JacobianPoint::BaseField,\n          typename Curve = typename JacobianPoint::Curve,\n          typename AffinePointTy = tachyon::math::AffinePoint<Curve>>\nvoid AddJacobianPoint(NodeModule& m, std::string_view name) {\n  m.NewClass<JacobianPoint>(name)\n      .template AddConstructor<>()\n      .template AddConstructor<const BaseField&, const BaseField&,\n                               const BaseField&>()\n      .AddStaticMethod(\"zero\", &JacobianPoint::Zero)\n      .AddStaticMethod(\"generator\", &JacobianPoint::Generator)\n      .AddStaticMethod(\"random\", &JacobianPoint::Random)\n      .AddReadOnlyProperty(\"x\", &JacobianPoint::x)\n      .AddReadOnlyProperty(\"y\", &JacobianPoint::y)\n      .AddReadOnlyProperty(\"z\", &JacobianPoint::z)\n      .AddMethod(\"isZero\", &JacobianPoint::IsZero)\n      .AddMethod(\"isOnCurve\", &JacobianPoint::IsOnCurve)\n      .AddMethod(\"toString\", &JacobianPoint::ToString)\n      .AddMethod(\"toHexString\", &JacobianPoint::ToHexString, false)\n      .AddMethod(\"eq\", &JacobianPoint::operator==)\n      .AddMethod(\"ne\", &JacobianPoint::operator!=)\n      .AddMethod(\"add\",\n                 &JacobianPoint::template operator+ <const JacobianPoint&>)\n      .AddMethod(\"addMixed\",\n                 &JacobianPoint::template operator+ <const AffinePointTy&>)\n      .AddMethod(\"sub\",\n                 &JacobianPoint::template operator- <const JacobianPoint&>)\n      .AddMethod(\"subMixed\",\n                 &JacobianPoint::template operator- <const AffinePointTy&>)\n      .AddMethod(\"negate\",\n                 static_cast<JacobianPoint (JacobianPoint::*)() const>(\n                     &JacobianPoint::operator-))\n      .AddMethod(\"double\", &JacobianPoint::Double);\n}\n\n}  // namespace tachyon::node::math\n\n#endif  // TACHYON_NODE_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_JACOBIAN_POINT_H_\n"
  },
  {
    "path": "tachyon/node/math/elliptic_curves/short_weierstrass/point_xyzz.h",
    "content": "#ifndef TACHYON_NODE_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_POINT_XYZZ_H_\n#define TACHYON_NODE_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_POINT_XYZZ_H_\n\n#include \"tachyon/math/geometry/affine_point.h\"\n#include \"tachyon/node/base/node_module.h\"\n\nnamespace tachyon::node::math {\n\ntemplate <typename PointXYZZ,\n          typename BaseField = typename PointXYZZ::BaseField,\n          typename Curve = typename PointXYZZ::Curve,\n          typename AffinePointTy = tachyon::math::AffinePoint<Curve>>\nvoid AddPointXYZZ(NodeModule& m, std::string_view name) {\n  m.NewClass<PointXYZZ>(name)\n      .template AddConstructor<>()\n      .template AddConstructor<const BaseField&, const BaseField&,\n                               const BaseField&, const BaseField&>()\n      .AddStaticMethod(\"zero\", &PointXYZZ::Zero)\n      .AddStaticMethod(\"generator\", &PointXYZZ::Generator)\n      .AddStaticMethod(\"random\", &PointXYZZ::Random)\n      .AddReadOnlyProperty(\"x\", &PointXYZZ::x)\n      .AddReadOnlyProperty(\"y\", &PointXYZZ::y)\n      .AddReadOnlyProperty(\"zz\", &PointXYZZ::zz)\n      .AddReadOnlyProperty(\"zzz\", &PointXYZZ::zzz)\n      .AddMethod(\"isZero\", &PointXYZZ::IsZero)\n      .AddMethod(\"isOnCurve\", &PointXYZZ::IsOnCurve)\n      .AddMethod(\"toString\", &PointXYZZ::ToString)\n      .AddMethod(\"toHexString\", &PointXYZZ::ToHexString, false)\n      .AddMethod(\"eq\", &PointXYZZ::operator==)\n      .AddMethod(\"ne\", &PointXYZZ::operator!=)\n      .AddMethod(\"add\", &PointXYZZ::template operator+ <const PointXYZZ&>)\n      .AddMethod(\"addMixed\",\n                 &PointXYZZ::template operator+ <const AffinePointTy&>)\n      .AddMethod(\"sub\", &PointXYZZ::template operator- <const PointXYZZ&>)\n      .AddMethod(\"subMixed\",\n                 &PointXYZZ::template operator- <const AffinePointTy&>)\n      .AddMethod(\"negate\", static_cast<PointXYZZ (PointXYZZ::*)() const>(\n                               &PointXYZZ::operator-))\n      .AddMethod(\"double\", &PointXYZZ::Double);\n}\n\n}  // namespace tachyon::node::math\n\n#endif  // TACHYON_NODE_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_POINT_XYZZ_H_\n"
  },
  {
    "path": "tachyon/node/math/elliptic_curves/short_weierstrass/projective_point.h",
    "content": "#ifndef TACHYON_NODE_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_PROJECTIVE_POINT_H_\n#define TACHYON_NODE_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_PROJECTIVE_POINT_H_\n\n#include \"tachyon/math/geometry/affine_point.h\"\n#include \"tachyon/node/base/node_module.h\"\n\nnamespace tachyon::node::math {\n\ntemplate <typename ProjectivePoint,\n          typename BaseField = typename ProjectivePoint::BaseField,\n          typename Curve = typename ProjectivePoint::Curve,\n          typename AffinePointTy = tachyon::math::AffinePoint<Curve>>\nvoid AddProjectivePoint(NodeModule& m, std::string_view name) {\n  m.NewClass<ProjectivePoint>(name)\n      .template AddConstructor<>()\n      .template AddConstructor<const BaseField&, const BaseField&,\n                               const BaseField&>()\n      .AddStaticMethod(\"zero\", &ProjectivePoint::Zero)\n      .AddStaticMethod(\"generator\", &ProjectivePoint::Generator)\n      .AddStaticMethod(\"random\", &ProjectivePoint::Random)\n      .AddReadOnlyProperty(\"x\", &ProjectivePoint::x)\n      .AddReadOnlyProperty(\"y\", &ProjectivePoint::y)\n      .AddReadOnlyProperty(\"z\", &ProjectivePoint::z)\n      .AddMethod(\"isZero\", &ProjectivePoint::IsZero)\n      .AddMethod(\"isOnCurve\", &ProjectivePoint::IsOnCurve)\n      .AddMethod(\"toString\", &ProjectivePoint::ToString)\n      .AddMethod(\"toHexString\", &ProjectivePoint::ToHexString, false)\n      .AddMethod(\"eq\", &ProjectivePoint::operator==)\n      .AddMethod(\"ne\", &ProjectivePoint::operator!=)\n      .AddMethod(\"add\",\n                 &ProjectivePoint::template operator+ <const ProjectivePoint&>)\n      .AddMethod(\"addMixed\",\n                 &ProjectivePoint::template operator+ <const AffinePointTy&>)\n      .AddMethod(\"sub\",\n                 &ProjectivePoint::template operator- <const ProjectivePoint&>)\n      .AddMethod(\"subMixed\",\n                 &ProjectivePoint::template operator- <const AffinePointTy&>)\n      .AddMethod(\"negate\",\n                 static_cast<ProjectivePoint (ProjectivePoint::*)() const>(\n                     &ProjectivePoint::operator-))\n      .AddMethod(\"double\", &ProjectivePoint::Double);\n}\n\n}  // namespace tachyon::node::math\n\n#endif  // TACHYON_NODE_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_PROJECTIVE_POINT_H_\n"
  },
  {
    "path": "tachyon/node/math/finite_fields/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//tachyon/node/math/elliptic_curves:__subpackages__\"])\n\ntachyon_cc_library(\n    name = \"prime_field\",\n    hdrs = [\"prime_field.h\"],\n    deps = [\n        \"//tachyon/node/base:node_base\",\n        \"//tachyon/node/math/base:big_int\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/node/math/finite_fields/prime_field.h",
    "content": "#ifndef TACHYON_NODE_MATH_FINITE_FIELDS_PRIME_FIELD_H_\n#define TACHYON_NODE_MATH_FINITE_FIELDS_PRIME_FIELD_H_\n\n#include <string>\n\n#include \"tachyon/node/base/node_module.h\"\n#include \"tachyon/node/math/base/big_int.h\"\n\nnamespace tachyon::node::math {\n\ntemplate <typename PrimeField, size_t N>\nPrimeField PrimeFieldFromNumber(const tachyon::math::BigInt<N>& value) {\n  return PrimeField(value);\n}\n\ntemplate <typename PrimeField>\nPrimeField PrimeFieldFromDecString(const std::string& str) {\n  // TODO(chokobole): Throw errors\n  return *PrimeField::FromDecString(str);\n}\n\ntemplate <typename PrimeField>\nPrimeField PrimeFieldFromHexString(const std::string& str) {\n  // TODO(chokobole): Throw errors\n  return *PrimeField::FromHexString(str);\n}\n\ntemplate <typename PrimeField, size_t N = PrimeField::N>\nvoid AddPrimeField(NodeModule& m, std::string_view name) {\n  m.NewClass<PrimeField>(name)\n      .AddStaticMethod(\"zero\", &PrimeField::Zero)\n      .AddStaticMethod(\"one\", &PrimeField::One)\n      .AddStaticMethod(\"random\", &PrimeField::Random)\n      .AddStaticMethod(\"fromNumber\", &PrimeFieldFromNumber<PrimeField, N>)\n      .AddStaticMethod(\"fromDecString\", &PrimeFieldFromDecString<PrimeField>)\n      .AddStaticMethod(\"fromHexString\", &PrimeFieldFromHexString<PrimeField>)\n      .AddMethod(\"isZero\", &PrimeField::IsZero)\n      .AddMethod(\"isOne\", &PrimeField::IsOne)\n      .AddMethod(\"toString\", &PrimeField::ToString)\n      .AddMethod(\"toHexString\", &PrimeField::ToHexString, false)\n      .AddMethod(\"eq\", &PrimeField::operator==)\n      .AddMethod(\"ne\", &PrimeField::operator!=)\n      // NOLINTNEXTLINE(whitespace/operators)\n      .AddMethod(\"lt\", &PrimeField::operator<)\n      .AddMethod(\"le\", &PrimeField::operator<=)\n      // NOLINTNEXTLINE(whitespace/operators)\n      .AddMethod(\"gt\", &PrimeField::operator>)\n      .AddMethod(\"ge\", &PrimeField::operator>=)\n      .AddMethod(\"add\", &PrimeField::template operator+ <const PrimeField&>)\n      .AddMethod(\"sub\", &PrimeField::template operator- <const PrimeField&>)\n      .AddMethod(\"mul\", &PrimeField::template operator* <const PrimeField&>)\n      .AddMethod(\"div\", &PrimeField::template operator/ <const PrimeField&>)\n      .AddMethod(\"negate\", static_cast<PrimeField (PrimeField::*)() const>(\n                               &PrimeField::operator-))\n      .AddMethod(\"double\", &PrimeField::Double)\n      .AddMethod(\"square\", &PrimeField::Square);\n}\n\n}  // namespace tachyon::node::math\n\n#endif  // TACHYON_NODE_MATH_FINITE_FIELDS_PRIME_FIELD_H_\n"
  },
  {
    "path": "tachyon/node/math/math.cc",
    "content": "#include \"tachyon/node/math/math.h\"\n\n#include \"tachyon/math/elliptic_curves/bls12/bls12_381/g1.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/g1.h\"\n#include \"tachyon/node/math/elliptic_curves/bls12/bls12_381/fq.h\"\n#include \"tachyon/node/math/elliptic_curves/bls12/bls12_381/fr.h\"\n#include \"tachyon/node/math/elliptic_curves/bls12/bls12_381/g1.h\"\n#include \"tachyon/node/math/elliptic_curves/bn/bn254/fq.h\"\n#include \"tachyon/node/math/elliptic_curves/bn/bn254/fr.h\"\n#include \"tachyon/node/math/elliptic_curves/bn/bn254/g1.h\"\n\nnamespace tachyon::node::math {\n\nvoid AddMath(NodeModule& m) {\n  NodeModule bls12_381 = m.AddSubModule(\"bls12_381\");\n  bls12_381.AddFunction(\"init\", &tachyon::math::bls12_381::G1Curve::Init);\n  bls12_381::AddFq(bls12_381);\n  bls12_381::AddFr(bls12_381);\n  bls12_381::AddG1(bls12_381);\n\n  NodeModule bn254 = m.AddSubModule(\"bn254\");\n  bn254.AddFunction(\"init\", &tachyon::math::bn254::G1Curve::Init);\n  bn254::AddFq(bn254);\n  bn254::AddFr(bn254);\n  bn254::AddG1(bn254);\n}\n\n}  // namespace tachyon::node::math\n"
  },
  {
    "path": "tachyon/node/math/math.h",
    "content": "#ifndef TACHYON_NODE_MATH_MATH_H_\n#define TACHYON_NODE_MATH_MATH_H_\n\n#include \"tachyon/node/base/node_module.h\"\n\nnamespace tachyon::node::math {\n\nvoid AddMath(NodeModule& m);\n\n}  // namespace tachyon::node::math\n\n#endif  // TACHYON_NODE_MATH_MATH_H_\n"
  },
  {
    "path": "tachyon/node/tachyon.cc",
    "content": "#include \"tachyon/node/math/math.h\"\n\nNapi::Object Init(Napi::Env env, Napi::Object exports) {\n  tachyon::node::NodeModule m(env, exports);\n  tachyon::node::NodeModule math = m.AddSubModule(\"math\");\n  tachyon::node::math::AddMath(math);\n  return exports;\n}\n\nNODE_API_MODULE(tachyon, Init)\n"
  },
  {
    "path": "tachyon/node/test/.aspect/rules/external_repository_action_cache/npm_translate_lock_LTIxMDEzMjgwMDY=",
    "content": "# @generated\n# Input hashes for repository rule npm_translate_lock(name = \"npm_deps\", pnpm_lock = \"//:pnpm-lock.yaml\").\n# This file should be checked into version control along with the pnpm-lock.yaml file.\npnpm-lock.yaml=-1086213679\nyarn.lock=-1603899843\npackage.json=-1653773938\n"
  },
  {
    "path": "tachyon/node/test/.bazelignore",
    "content": "node_modules\n"
  },
  {
    "path": "tachyon/node/test/.bazelrc",
    "content": "# TODO(chokobole): Remove when `cc_shared_library` is enabled by default\nbuild --experimental_cc_shared_library\n\n# Enable platform specific configurations by default depending on host machine.\ncommon --enable_platform_specific_config\n\nbuild:linux --cxxopt=-std=c++17\nbuild:linux --host_cxxopt=-std=c++17\nbuild:macos --cxxopt=-std=c++17\nbuild:macos --host_cxxopt=-std=c++17\nbuild:macos --objccopt=-std=c++17\nbuild:windows --cxxopt=/std:c++17\nbuild:windows --host_cxxopt=/std:c++17\n\nbuild:macos_x86_64 --config=macos\nbuild:macos_x86_64 --cpu=darwin_x86_64\nbuild:macos_x86_64 --host_cpu=darwin_x86_64\nbuild:macos_arm64 --config=macos\nbuild:macos_arm64 --cpu=darwin_arm64\nbuild:macos_arm64 --host_cpu=darwin_arm64\n\nbuild --@kroma_network_tachyon//:node_binding\n# See https://www.typescriptlang.org/tsconfig#skipLibCheck\nbuild --@aspect_rules_ts//ts:skipLibCheck=honor_tsconfig\nfetch --@aspect_rules_ts//ts:skipLibCheck=honor_tsconfig\nquery --@aspect_rules_ts//ts:skipLibCheck=honor_tsconfig\n\n# gmp needs exception.\nbuild --@kroma_network_tachyon//:has_exception\n\nbuild --@kroma_network_tachyon//:shared_object\n"
  },
  {
    "path": "tachyon/node/test/.bazelversion",
    "content": "6.3.0\n"
  },
  {
    "path": "tachyon/node/test/.prettierrc.js",
    "content": "module.exports = {\n  ...require('@ssen/prettier-config'),\n};\n"
  },
  {
    "path": "tachyon/node/test/.swcrc",
    "content": "{\n  \"jsc\": {\n    \"loose\": true,\n    \"target\": \"es2020\",\n    \"parser\": {\n      \"syntax\": \"typescript\",\n      \"decorators\": true\n    },\n    \"transform\": {\n      \"legacyDecorator\": true,\n      \"decoratorMetadata\": true\n    }\n  },\n  \"module\": {\n    \"type\": \"commonjs\",\n    \"strict\": true,\n    \"strictMode\": true,\n    \"lazy\": true,\n    \"noInterop\": true\n  }\n}\n"
  },
  {
    "path": "tachyon/node/test/BUILD.bazel",
    "content": "load(\"@aspect_bazel_lib//lib:copy_to_bin.bzl\", \"copy_to_bin\")\nload(\"@aspect_rules_ts//ts:defs.bzl\", \"ts_config\")\nload(\"@npm_deps//:defs.bzl\", \"npm_link_all_packages\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\".swcrc\"])\n\nnpm_link_all_packages()\n\nts_config(\n    name = \"tsconfig\",\n    src = \"tsconfig.json\",\n)\n\ncopy_to_bin(\n    name = \"jest.config\",\n    srcs = [\"jest.config.js\"],\n    visibility = [\"//src:__subpackages__\"],\n)\n"
  },
  {
    "path": "tachyon/node/test/README.md",
    "content": "### tachyon node test\n\n```shell\nbazel test //...\n```\n"
  },
  {
    "path": "tachyon/node/test/WORKSPACE",
    "content": "load(\"@bazel_tools//tools/build_defs/repo:http.bzl\", \"http_archive\")\n\nhttp_archive(\n    name = \"aspect_rules_ts\",\n    sha256 = \"8aabb2055629a7becae2e77ae828950d3581d7fc3602fe0276e6e039b65092cb\",\n    strip_prefix = \"rules_ts-2.0.0\",\n    url = \"https://github.com/aspect-build/rules_ts/releases/download/v2.0.0/rules_ts-v2.0.0.tar.gz\",\n)\n\nhttp_archive(\n    name = \"aspect_rules_swc\",\n    sha256 = \"8eb9e42ed166f20cacedfdb22d8d5b31156352eac190fc3347db55603745a2d8\",\n    strip_prefix = \"rules_swc-1.1.0\",\n    url = \"https://github.com/aspect-build/rules_swc/releases/download/v1.1.0/rules_swc-v1.1.0.tar.gz\",\n)\n\nhttp_archive(\n    name = \"aspect_rules_jest\",\n    sha256 = \"098186ffc450f2a604843d8ba14217088a0e259ea6a03294af5360a7f1bcd3e8\",\n    strip_prefix = \"rules_jest-0.19.5\",\n    url = \"https://github.com/aspect-build/rules_jest/releases/download/v0.19.5/rules_jest-v0.19.5.tar.gz\",\n)\n\n##################\n# rules_ts setup #\n##################\n# Fetches the rules_ts dependencies.\n# If you want to have a different version of some dependency,\n# you should fetch it *before* calling this.\n# Alternatively, you can skip calling this function, so long as you've\n# already fetched all the dependencies.\nload(\"@aspect_rules_ts//ts:repositories.bzl\", \"rules_ts_dependencies\")\n\nrules_ts_dependencies(\n    # This keeps the TypeScript version in-sync with the editor, which is typically best.\n    ts_version_from = \"//:package.json\",\n\n    # Alternatively, you could pick a specific version, or use\n    # load(\"@aspect_rules_ts//ts:repositories.bzl\", \"LATEST_TYPESCRIPT_VERSION\")\n    # ts_version = LATEST_TYPESCRIPT_VERSION\n)\n\nload(\"@aspect_rules_js//js:repositories.bzl\", \"rules_js_dependencies\")\n\nrules_js_dependencies()\n\nload(\"@bazel_features//:deps.bzl\", \"bazel_features_deps\")\n\nbazel_features_deps()\n\n# Fetch and register node, if you haven't already\nload(\"@rules_nodejs//nodejs:repositories.bzl\", \"DEFAULT_NODE_VERSION\", \"nodejs_register_toolchains\")\n\nnodejs_register_toolchains(\n    name = \"nodejs\",\n    node_version = DEFAULT_NODE_VERSION,\n)\n\nload(\"@aspect_rules_js//npm:npm_import.bzl\", \"npm_translate_lock\")\n\n###################\n# rules_swc setup #\n###################\n\n# Fetches the rules_swc dependencies.\n# If you want to have a different version of some dependency,\n# you should fetch it *before* calling this.\n# Alternatively, you can skip calling this function, so long as you've\n# already fetched all the dependencies.\nload(\"@aspect_rules_swc//swc:dependencies.bzl\", \"rules_swc_dependencies\")\n\nrules_swc_dependencies()\n\n# Fetches a SWC cli from\n# https://github.com/swc-project/swc/releases\n# If you'd rather compile it from source, you can use rules_rust, fetch the project,\n# then register the toolchain yourself. (Note, this is not yet documented)\nload(\"@aspect_rules_swc//swc:repositories.bzl\", \"LATEST_SWC_VERSION\", \"swc_register_toolchains\")\n\nswc_register_toolchains(\n    name = \"swc\",\n    swc_version = LATEST_SWC_VERSION,\n)\n\nnpm_translate_lock(\n    name = \"npm_deps\",\n    data = [\"//:package.json\"],\n    pnpm_lock = \"//:pnpm-lock.yaml\",\n    verify_node_modules_ignored = \"//:.bazelignore\",\n    yarn_lock = \"//:yarn.lock\",\n)\n\nload(\"@npm_deps//:repositories.bzl\", npm_deps_respositores = \"npm_repositories\")\n\nnpm_deps_respositores()\n\n###########################################\n\nlocal_repository(\n    name = \"kroma_network_tachyon\",\n    path = \"../../../\",\n)\n\nload(\"@kroma_network_tachyon//bazel:tachyon_deps.bzl\", \"tachyon_deps\")\n\ntachyon_deps()\n\nload(\"@kroma_network_tachyon//bazel:buildifier_deps.bzl\", \"buildifier_deps\")\n\nbuildifier_deps()\n\nload(\"@kroma_network_tachyon//bazel:js_deps.bzl\", \"js_deps\")\n\njs_deps()\n\nload(\"@aspect_bazel_lib//lib:repositories.bzl\", \"aspect_bazel_lib_dependencies\")\n\naspect_bazel_lib_dependencies()\n\nnpm_translate_lock(\n    name = \"npm\",\n    data = [\"@iden3_ffiasm//:package.json\"],\n    npm_package_lock = \"@iden3_ffiasm//:package-lock.json\",\n    pnpm_lock = \"@iden3_ffiasm//:pnpm-lock.yaml\",\n    update_pnpm_lock = True,\n    verify_node_modules_ignored = \"@iden3_ffiasm//:.bazelignore\",\n)\n\nload(\"@npm//:repositories.bzl\", \"npm_repositories\")\n\nnpm_repositories()\n"
  },
  {
    "path": "tachyon/node/test/bazel/BUILD.bazel",
    "content": ""
  },
  {
    "path": "tachyon/node/test/bazel/tachyon_jest.bzl",
    "content": "load(\"@aspect_rules_jest//jest:defs.bzl\", \"jest_test\")\n\ndef tachyon_jest_test(\n        name,\n        config = \"//:jest.config\",\n        node_modules = \"//:node_modules\",\n        **kwargs):\n    jest_test(\n        name = name,\n        config = config,\n        node_modules = node_modules,\n        **kwargs\n    )\n\ndef tachyon_jest_unittest(\n        name,\n        size = \"small\",\n        **kwargs):\n    tachyon_jest_test(\n        name = name,\n        size = size,\n        **kwargs\n    )\n"
  },
  {
    "path": "tachyon/node/test/bazel/tachyon_ts.bzl",
    "content": "load(\"@aspect_rules_swc//swc:defs.bzl\", \"swc\")\nload(\"@aspect_rules_ts//ts:defs.bzl\", \"ts_project\")\nload(\"@bazel_skylib//lib:partial.bzl\", \"partial\")\n\ndef tachyon_ts_project(\n        name,\n        declaration = True,\n        source_map = True,\n        transpiler = partial.make(\n            swc,\n            swcrc = \"//:.swcrc\",\n        ),\n        tsconfig = \"//:tsconfig\",\n        deps = [],\n        **kwargs):\n    ts_project(\n        name = name,\n        declaration = declaration,\n        source_map = source_map,\n        transpiler = transpiler,\n        tsconfig = tsconfig,\n        deps = [\n            \"//:node_modules/@jest/globals\",\n            \"//:node_modules/@types/jest\",\n            \"//:node_modules/@types/node\",\n        ] + deps,\n        **kwargs\n    )\n"
  },
  {
    "path": "tachyon/node/test/bazelignore",
    "content": "node_modules\n"
  },
  {
    "path": "tachyon/node/test/jest.config.js",
    "content": "module.exports = {\n  testEnvironment: 'node'\n};\n"
  },
  {
    "path": "tachyon/node/test/package.json",
    "content": "{\n  \"private\": true,\n  \"devDependencies\": {\n    \"@jest/globals\": \"^29.7.0\",\n    \"@ssen/prettier-config\": \"^2.0.1\",\n    \"@types/jest\": \"^29.5.4\",\n    \"@types/node\": \"^20.6.0\",\n    \"jest-cli\": \"^29.7.0\",\n    \"jest-junit\": \"^16.0.0\",\n    \"ts-jest\": \"^29.1.1\",\n    \"typescript\": \"5.1.6\"\n  }\n}\n"
  },
  {
    "path": "tachyon/node/test/src/base/test/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_jest.bzl\", \"tachyon_jest_test\")\nload(\"//bazel:tachyon_ts.bzl\", \"tachyon_ts_project\")\n\ntachyon_ts_project(\n    name = \"test_ts\",\n    testonly = True,\n    srcs = [\n        \"class.spec.ts\",\n        \"enum.spec.ts\",\n        \"function.spec.ts\",\n    ],\n    data = [\"@kroma_network_tachyon//tachyon/node/base/test\"],\n)\n\ntachyon_jest_test(\n    name = \"test\",\n    data = [\":test_ts\"],\n)\n"
  },
  {
    "path": "tachyon/node/test/src/base/test/class.spec.ts",
    "content": "import { describe, expect, test } from '@jest/globals';\n\nconst class_test = require(\n    '../../../external/kroma_network_tachyon/tachyon/node/base/test/test.node');\n\nconst callNonConstMethod = TypeError('Call non-const method');\nconst noSuchConstructor = TypeError('No such constructor');\n\ndescribe('class', () => {\n  test('method setter/getter', () => {\n    let p: any = null;\n    expect(() => (p = new class_test.Point())).not.toThrow();\n    expect(() => p.setX(1)).not.toThrow();\n    expect(p.getX()).toBe(1);\n    expect(() => p.setY(2)).not.toThrow();\n    expect(p.getY()).toBe(2);\n  });\n\n  test('property setter/getter', () => {\n    let p: any = null;\n    expect(() => (p = new class_test.Point())).not.toThrow();\n    expect(() => (p.propertyX = 1)).not.toThrow();\n    expect(p.propertyX).toBe(1);\n    expect(() => (p.propertyY = 2)).not.toThrow();\n    expect(p.propertyY).toBe(2);\n    expect(p.getX()).toBe(1);\n    expect(p.getY()).toBe(2);\n  });\n\n  test('member setter/getter', () => {\n    let p: any = null;\n    expect(() => (p = new class_test.Point())).not.toThrow();\n    expect(() => (p.x = 1)).not.toThrow();\n    expect(p.x).toBe(1);\n    expect(() => (p.y = 2)).not.toThrow();\n    expect(p.y).toBe(2);\n    expect(p.getX()).toBe(1);\n    expect(p.getY()).toBe(2);\n  });\n\n  test('static method setter/getter', () => {\n    expect(() => class_test.Point.setDimension(3)).not.toThrow();\n    expect(class_test.Point.getDimension()).toBe(3);\n  });\n\n  test('static property setter/getter', () => {\n    expect(() => (class_test.Point.propertyDimension = 3)).not.toThrow();\n    expect(class_test.Point.propertyDimension).toBe(3);\n    expect(class_test.Point.getDimension()).toBe(3);\n  });\n\n  test('static member setter/getter', () => {\n    expect(() => (class_test.Point.dimension = 3)).not.toThrow();\n    expect(class_test.Point.dimension).toBe(3);\n    expect(class_test.Point.getDimension()).toBe(3);\n  });\n\n  test('default arguments', () => {\n    let adder: any = null;\n    expect(() => (adder = new class_test.Adder())).not.toThrow();\n    expect(adder.add()).toBe(10);\n    expect(adder.add(2)).toBe(21);\n    expect(adder.add(2, 3)).toBe(33);\n    expect(adder.add(2, 3, 4)).toBe(46);\n    expect(adder.add(2, 3, 4, 5)).toBe(60);\n\n    expect(class_test.Adder.sAdd()).toBe(10);\n    expect(class_test.Adder.sAdd(2)).toBe(11);\n    expect(class_test.Adder.sAdd(2, 3)).toBe(12);\n    expect(class_test.Adder.sAdd(2, 3, 4)).toBe(13);\n    expect(class_test.Adder.sAdd(2, 3, 4, 5)).toBe(14);\n  });\n\n  test('reference method setter/getter', () => {\n    let rect: any = null;\n    expect(() => (rect = new class_test.Rect())).not.toThrow();\n    expect(() => (rect.getTopLeft().x = 1)).not.toThrow();\n    expect(rect.getTopLeft().x).toBe(1);\n    expect(() => (rect.getTopLeft().y = 2)).not.toThrow();\n    expect(rect.getTopLeft().y).toBe(2);\n  });\n\n  test('reference member setter/getter', () => {\n    let rect: any = null;\n    expect(() => (rect = new class_test.Rect())).not.toThrow();\n    expect(() => (rect.topLeft.x = 1)).not.toThrow();\n    expect(rect.topLeft.x).toBe(1);\n    expect(() => (rect.topLeft.y = 2)).not.toThrow();\n    expect(rect.topLeft.y).toBe(2);\n    expect(rect.getTopLeft().x).toBe(1);\n    expect(rect.getTopLeft().y).toBe(2);\n  });\n\n  test('const reference method setter/getter', () => {\n    let rect: any = null;\n    expect(() => (rect = new class_test.Rect())).not.toThrow();\n    expect(() => rect.getConstTopLeft().setX(1)).toThrow(callNonConstMethod);\n    expect(() => rect.getConstTopLeft().setY(2)).toThrow(callNonConstMethod);\n    expect(() => (rect.getConstTopLeft().x = 1)).toThrow(callNonConstMethod);\n    expect(() => (rect.getConstTopLeft().y = 2)).toThrow(callNonConstMethod);\n  });\n\n  test('pass const reference as an argument', () => {\n    expect(\n      class_test.Point.distance(\n        new class_test.Point(),\n        new class_test.Point(3, 4),\n      ),\n    ).toBe(5);\n  });\n\n  test('pass reference as an argument', () => {\n    let p: any;\n    expect(() => {\n      p = new class_test.Point(3, 4);\n      class_test.doubleWithReference(p);\n    }).not.toThrow();\n    expect(p.x).toBe(6);\n    expect(p.y).toBe(8);\n    expect(() =>\n      class_test.doubleWithReference(new class_test.Rect()),\n    ).toThrow();\n    // TODO(chokobole): Enable these tests\n    // expect(() => class_test.doubleWithSharedPtr(p)).toThrow();\n    // expect(() => class_test.doubleWithUniquePtr(p)).toThrow();\n  });\n\n  test('pass value as an argument', () => {\n    let p: any,\n      q: any = null;\n    expect(() => {\n      p = new class_test.Point(3, 4);\n      q = class_test.doubleWithValue(p);\n    }).not.toThrow();\n    expect(p.x).toBe(3);\n    expect(p.y).toBe(4);\n    expect(q.x).toBe(6);\n    expect(q.y).toBe(8);\n    expect(() => class_test.doubleWithValue(new class_test.Rect())).toThrow();\n    // TODO(chokobole): Enable these tests\n    // expect(() => class_test.doubleWithSharedPtr(p)).toThrow();\n    // expect(() => class_test.doubleWithUniquePtr(p)).toThrow();\n  });\n\n  test('construct with object', () => {\n    let r: any;\n    expect(() => {\n      r = new class_test.Rect(\n        new class_test.Point(1, 2),\n        new class_test.Point(3, 4),\n      );\n    }).not.toThrow();\n    expect(r.topLeft.x).toBe(1);\n    expect(r.topLeft.y).toBe(2);\n    expect(r.bottomRight.x).toBe(3);\n    expect(r.bottomRight.y).toBe(4);\n  });\n\n  test('constructor overloadding', () => {\n    let v: any;\n    expect(() => (v = new class_test.Variant(true))).not.toThrow();\n    expect(v.b).toBe(true);\n    expect(() => (v = new class_test.Variant(5))).not.toThrow();\n    expect(v.i).toBe(5);\n    expect(() => (v = new class_test.Variant(BigInt(10)))).not.toThrow();\n    expect(v.i64).toBe(BigInt(10));\n    expect(() => (v = new class_test.Variant('hello'))).not.toThrow();\n    expect(v.s).toBe('hello');\n    expect(\n      () => (v = new class_test.Variant(new Array(1, 2, 3))),\n    ).not.toThrow();\n    expect(v.ivec).toStrictEqual(new Array(1, 2, 3));\n    expect(() => (v = new class_test.Variant(1, 'hello'))).not.toThrow();\n    expect(v.i).toBe(1);\n    expect(v.s).toBe('hello');\n    expect(\n      () => (v = new class_test.Variant(new class_test.Point(1, 2))),\n    ).not.toThrow();\n    expect(v.p.x).toBe(1);\n    expect(v.p.y).toBe(2);\n    expect(() => (v = new class_test.Variant(new class_test.Rect()))).toThrow(\n      noSuchConstructor,\n    );\n  });\n\n  test('inheritance', () => {\n    let cp: any;\n    expect(\n      () => (cp = new class_test.ColoredPoint(1, 2, class_test.color.red)),\n    ).not.toThrow();\n    expect(() => class_test.doubleWithValue(cp)).not.toThrow();\n    expect(cp.x).toBe(1);\n    expect(cp.y).toBe(2);\n    expect(() => class_test.doubleWithReference(cp)).not.toThrow();\n    expect(cp.x).toBe(2);\n    expect(cp.y).toBe(4);\n  });\n\n  // TODO(chokobole): Enable these tests\n  // test('shared_ptr', () => {\n  //   let p: any;\n  //   expect(() => (p = new class_test.Point(1, 2))).not.toThrow();\n  //   expect(() => class_test.doubleWithReference(p)).not.toThrow();\n  //   expect(p.x).toBe(2);\n  //   expect(p.y).toBe(4);\n  //   expect(() => class_test.doubleWithSharedPtr(p)).not.toThrow();\n  //   expect(p.x).toBe(4);\n  //   expect(p.y).toBe(8);\n  //   expect(() => class_test.doubleWithUniquePtr(p)).toThrow();\n  // });\n\n  // test('unique_ptr', () => {\n  //   let p: any;\n  //   expect(() => (p = new class_test.Point(1, 2))).not.toThrow();\n  //   expect(() => class_test.doubleWithReference(p)).not.toThrow();\n  //   expect(p.x).toBe(2);\n  //   expect(p.y).toBe(4);\n  //   expect(() => class_test.doubleWithSharedPtr(p)).toThrow();\n  //   expect(() => class_test.doubleWithUniquePtr(p)).not.toThrow();\n  // });\n});\n"
  },
  {
    "path": "tachyon/node/test/src/base/test/enum.spec.ts",
    "content": "import { describe, expect, test } from '@jest/globals';\n\nconst enum_test = require(\n    '../../../external/kroma_network_tachyon/tachyon/node/base/test/test.node');\n\ndescribe('enum', () => {\n  test('enum values', () => {\n    expect(enum_test.color.red).toBe(0);\n    expect(enum_test.color.green).toBe(1);\n    expect(enum_test.color.blue).toBe(2);\n  });\n\n  test('enum not writable', () => {\n    expect(() => (enum_test.color.red = 1)).toThrow();\n  });\n\n  test('enum not configurable', () => {\n    expect(() => delete enum_test.color.red).toThrow();\n  });\n\n  test('enum enumerable', () => {\n    expect(enum_test.color.propertyIsEnumerable('red')).toBe(true);\n  });\n});\n"
  },
  {
    "path": "tachyon/node/test/src/base/test/function.spec.ts",
    "content": "import { describe, expect, test } from '@jest/globals';\n\nconst functionTest = require(\n    '../../../external/kroma_network_tachyon/tachyon/node/base/test/test.node');\n\nconst wrongNumberOfArguments = TypeError('Wrong number of arguments');\n\nfunction invalidArgument(i: number) {\n  return TypeError(`Invalid argument #${i}`);\n}\n\ndescribe('function', () => {\n  test('function std::string()', () => {\n    expect(functionTest.hello()).toBe('world');\n    expect(() => functionTest.hello(1)).toThrow(wrongNumberOfArguments);\n  });\n\n  test('function int(int, int) with default arguments', () => {\n    expect(functionTest.sum()).toBe(3);\n    expect(functionTest.sum(2)).toBe(4);\n    expect(functionTest.sum(2, 3)).toBe(5);\n    expect(() => functionTest.sum(1, 'abc')).toThrow(invalidArgument(1));\n    expect(() => functionTest.sum(2, 3, 4)).toThrow(wrongNumberOfArguments);\n  });\n\n  test('function void()', () => {\n    expect(() => functionTest.do_nothing()).not.toThrow();\n    expect(() => functionTest.do_nothing(1)).toThrow(wrongNumberOfArguments);\n  });\n});\n"
  },
  {
    "path": "tachyon/node/test/src/math/elliptic_curves/short_weierstrass/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_jest.bzl\", \"tachyon_jest_unittest\")\nload(\"//bazel:tachyon_ts.bzl\", \"tachyon_ts_project\")\n\ntachyon_ts_project(\n    name = \"short_weierstrass\",\n    testonly = True,\n    srcs = [\n        \"affine_point.spec.ts\",\n        \"jacobian_point.spec.ts\",\n        \"point_xyzz.spec.ts\",\n        \"projective_point.spec.ts\",\n    ],\n    data = [\"@kroma_network_tachyon//tachyon/node:tachyon\"],\n)\n\ntachyon_jest_unittest(\n    name = \"short_weierstrass_unittests\",\n    data = [\":short_weierstrass\"],\n)\n"
  },
  {
    "path": "tachyon/node/test/src/math/elliptic_curves/short_weierstrass/affine_point.spec.ts",
    "content": "import { beforeAll, describe, expect, test } from '@jest/globals';\n\nconst tachyon = require('../../../../external/kroma_network_tachyon/tachyon/node/tachyon.node');\n\nbeforeAll(() => {\n  tachyon.math.bn254.init();\n});\n\ndescribe('AffinePoint', () => {\n  test('new AffinePoint()', () => {\n    const p = new tachyon.math.bn254.G1AffinePoint();\n    expect(p.x.isZero()).toBe(true);\n    expect(p.y.isZero()).toBe(true);\n\n    const x = tachyon.math.bn254.Fq.random();\n    const y = tachyon.math.bn254.Fq.random();\n    const p2 = new tachyon.math.bn254.G1AffinePoint(x, y);\n    expect(p2.x.eq(x)).toBe(true);\n    expect(p2.y.eq(y)).toBe(true);\n  });\n\n  test('AffinePoint.zero()', () => {\n    const p = tachyon.math.bn254.G1AffinePoint.zero();\n    expect(p.isZero()).toBe(true);\n  });\n\n  test('AffinePoint.generator()', () => {\n    const p = tachyon.math.bn254.G1AffinePoint.generator();\n    expect(p.isOnCurve()).toBe(true);\n  });\n\n  test('AffinePoint.random()', () => {\n    const p = tachyon.math.bn254.G1AffinePoint.random();\n    const p2 = tachyon.math.bn254.G1AffinePoint.random();\n    expect(p.eq(p2)).toBe(false);\n  });\n\n  test('AffinePoint.eq()', () => {\n    const p = tachyon.math.bn254.G1AffinePoint.zero();\n    const p2 = tachyon.math.bn254.G1AffinePoint.generator();\n    expect(p.eq(p)).toBe(true);\n    expect(p2.eq(p2)).toBe(true);\n    expect(p.eq(p2)).toBe(false);\n    expect(p2.eq(p)).toBe(false);\n  });\n\n  test('AffinePoint.ne()', () => {\n    const p = tachyon.math.bn254.G1AffinePoint.zero();\n    const p2 = tachyon.math.bn254.G1AffinePoint.generator();\n    expect(p.ne(p)).toBe(false);\n    expect(p2.ne(p2)).toBe(false);\n    expect(p.ne(p2)).toBe(true);\n    expect(p2.ne(p)).toBe(true);\n  });\n\n  test('AffinePoint.add()', () => {\n    const p = tachyon.math.bn254.G1AffinePoint.generator();\n    const p2 = tachyon.math.bn254.G1AffinePoint.generator();\n    expect(p.add(p2).eq(new tachyon.math.bn254.G1JacobianPoint(\n      tachyon.math.bn254.Fq.fromDecString('21888242871839275222246405745257275088696311157297823662689037894645226208560'),\n      tachyon.math.bn254.Fq.fromDecString('21888242871839275222246405745257275088696311157297823662689037894645226208572'),\n      tachyon.math.bn254.Fq.fromNumber(4),\n    ))).toBe(true);\n  });\n\n  test('AffinePoint.sub()', () => {\n    const p = tachyon.math.bn254.G1AffinePoint.generator();\n    const p2 = tachyon.math.bn254.G1AffinePoint.generator();\n    expect(p.sub(p2).isZero()).toBe(true);\n  });\n\n  test('AffinePoint.negate()', () => {\n    const p = tachyon.math.bn254.G1AffinePoint.generator();\n    expect(p.negate().eq(new tachyon.math.bn254.G1AffinePoint(\n      p.x,\n      p.y.negate(),\n    ))).toBe(true);\n  });\n\n  test('AffinePoint.double()', () => {\n    const p = tachyon.math.bn254.G1AffinePoint.generator();\n    expect(p.double().eq(new tachyon.math.bn254.G1JacobianPoint(\n      tachyon.math.bn254.Fq.fromDecString('21888242871839275222246405745257275088696311157297823662689037894645226208560'),\n      tachyon.math.bn254.Fq.fromDecString('21888242871839275222246405745257275088696311157297823662689037894645226208572'),\n      tachyon.math.bn254.Fq.fromNumber(4),\n    ))).toBe(true);\n  });\n});\n"
  },
  {
    "path": "tachyon/node/test/src/math/elliptic_curves/short_weierstrass/jacobian_point.spec.ts",
    "content": "import { beforeAll, describe, expect, test } from '@jest/globals';\n\nconst tachyon = require('../../../../external/kroma_network_tachyon/tachyon/node/tachyon.node');\n\nbeforeAll(() => {\n  tachyon.math.bn254.init();\n});\n\ndescribe('JacobianPoint', () => {\n  test('new JacobianPoint()', () => {\n    const p = new tachyon.math.bn254.G1JacobianPoint();\n    expect(p.x.isOne()).toBe(true);\n    expect(p.y.isOne()).toBe(true);\n    expect(p.z.isZero()).toBe(true);\n\n    const x = tachyon.math.bn254.Fq.random();\n    const y = tachyon.math.bn254.Fq.random();\n    const z = tachyon.math.bn254.Fq.random();\n    const p2 = new tachyon.math.bn254.G1JacobianPoint(x, y, z);\n    expect(p2.x.eq(x)).toBe(true);\n    expect(p2.y.eq(y)).toBe(true);\n    expect(p2.z.eq(z)).toBe(true);\n  });\n\n  test('JacobianPoint.zero()', () => {\n    const p = tachyon.math.bn254.G1JacobianPoint.zero();\n    expect(p.isZero()).toBe(true);\n    expect(p.z.isZero()).toBe(true);\n  });\n\n  test('JacobianPoint.generator()', () => {\n    const p = tachyon.math.bn254.G1JacobianPoint.generator();\n    expect(p.isOnCurve()).toBe(true);\n    expect(p.z.isZero()).toBe(false);\n  });\n\n  test('JacobianPoint.random()', () => {\n    const p = tachyon.math.bn254.G1JacobianPoint.random();\n    const p2 = tachyon.math.bn254.G1JacobianPoint.random();\n    expect(p.eq(p2)).toBe(false);\n  });\n\n  test('JacobianPoint.eq()', () => {\n    const p = tachyon.math.bn254.G1JacobianPoint.zero();\n    const p2 = tachyon.math.bn254.G1JacobianPoint.generator();\n    expect(p.eq(p)).toBe(true);\n    expect(p2.eq(p2)).toBe(true);\n    expect(p.eq(p2)).toBe(false);\n    expect(p2.eq(p)).toBe(false);\n  });\n\n  test('JacobianPoint.ne()', () => {\n    const p = tachyon.math.bn254.G1JacobianPoint.zero();\n    const p2 = tachyon.math.bn254.G1JacobianPoint.generator();\n    expect(p.ne(p)).toBe(false);\n    expect(p2.ne(p2)).toBe(false);\n    expect(p.ne(p2)).toBe(true);\n    expect(p2.ne(p)).toBe(true);\n  });\n\n  test('JacobianPoint.add()', () => {\n    const p = tachyon.math.bn254.G1JacobianPoint.generator();\n    const p2 = tachyon.math.bn254.G1JacobianPoint.generator();\n    expect(p.add(p2).eq(new tachyon.math.bn254.G1JacobianPoint(\n      tachyon.math.bn254.Fq.fromDecString('21888242871839275222246405745257275088696311157297823662689037894645226208560'),\n      tachyon.math.bn254.Fq.fromDecString('21888242871839275222246405745257275088696311157297823662689037894645226208572'),\n      tachyon.math.bn254.Fq.fromNumber(4),\n    ))).toBe(true);\n  });\n\n  test('JacobianPoint.addMixed()', () => {\n    const p = tachyon.math.bn254.G1JacobianPoint.generator();\n    const p2 = tachyon.math.bn254.G1AffinePoint.generator();\n    expect(p.addMixed(p2).eq(new tachyon.math.bn254.G1JacobianPoint(\n      tachyon.math.bn254.Fq.fromDecString('21888242871839275222246405745257275088696311157297823662689037894645226208560'),\n      tachyon.math.bn254.Fq.fromDecString('21888242871839275222246405745257275088696311157297823662689037894645226208572'),\n      tachyon.math.bn254.Fq.fromNumber(4),\n    ))).toBe(true);\n  });\n\n  test('JacobianPoint.sub()', () => {\n    const p = tachyon.math.bn254.G1JacobianPoint.generator();\n    const p2 = tachyon.math.bn254.G1JacobianPoint.generator();\n    expect(p.sub(p2).isZero()).toBe(true);\n  });\n\n  test('JacobianPoint.subMixed()', () => {\n    const p = tachyon.math.bn254.G1JacobianPoint.generator();\n    const p2 = tachyon.math.bn254.G1AffinePoint.generator();\n    expect(p.subMixed(p2).isZero()).toBe(true);\n  });\n\n  test('JacobianPoint.negate()', () => {\n    const p = tachyon.math.bn254.G1JacobianPoint.generator();\n    expect(p.negate().eq(new tachyon.math.bn254.G1JacobianPoint(\n      p.x,\n      p.y.negate(),\n      p.z,\n    ))).toBe(true);\n  });\n\n  test('JacobianPoint.double()', () => {\n    const p = tachyon.math.bn254.G1JacobianPoint.generator();\n    expect(p.double().eq(new tachyon.math.bn254.G1JacobianPoint(\n      tachyon.math.bn254.Fq.fromDecString('21888242871839275222246405745257275088696311157297823662689037894645226208560'),\n      tachyon.math.bn254.Fq.fromDecString('21888242871839275222246405745257275088696311157297823662689037894645226208572'),\n      tachyon.math.bn254.Fq.fromNumber(4),\n    ))).toBe(true);\n  });\n});\n"
  },
  {
    "path": "tachyon/node/test/src/math/elliptic_curves/short_weierstrass/point_xyzz.spec.ts",
    "content": "import { beforeAll, describe, expect, test } from '@jest/globals';\n\nconst tachyon = require('../../../../external/kroma_network_tachyon/tachyon/node/tachyon.node');\n\nbeforeAll(() => {\n  tachyon.math.bn254.init();\n});\n\ndescribe('PointXYZZ', () => {\n  test('new PointXYZZ()', () => {\n    const p = new tachyon.math.bn254.G1PointXYZZ();\n    expect(p.x.isOne()).toBe(true);\n    expect(p.y.isOne()).toBe(true);\n    expect(p.zz.isZero()).toBe(true);\n    expect(p.zzz.isZero()).toBe(true);\n\n    const x = tachyon.math.bn254.Fq.random();\n    const y = tachyon.math.bn254.Fq.random();\n    const zz = tachyon.math.bn254.Fq.random();\n    const zzz = tachyon.math.bn254.Fq.random();\n    const p2 = new tachyon.math.bn254.G1PointXYZZ(x, y, zz, zzz);\n    expect(p2.x.eq(x)).toBe(true);\n    expect(p2.y.eq(y)).toBe(true);\n    expect(p2.zz.eq(zz)).toBe(true);\n    expect(p2.zzz.eq(zzz)).toBe(true);\n  });\n\n  test('PointXYZZ.zero()', () => {\n    const p = tachyon.math.bn254.G1PointXYZZ.zero();\n    expect(p.isZero()).toBe(true);\n    expect(p.zz.isZero()).toBe(true);\n  });\n\n  test('PointXYZZ.generator()', () => {\n    const p = tachyon.math.bn254.G1PointXYZZ.generator();\n    expect(p.isOnCurve()).toBe(true);\n    expect(p.zz.isZero()).toBe(false);\n  });\n\n  test('PointXYZZ.random()', () => {\n    const p = tachyon.math.bn254.G1PointXYZZ.random();\n    const p2 = tachyon.math.bn254.G1PointXYZZ.random();\n    expect(p.eq(p2)).toBe(false);\n  });\n\n  test('PointXYZZ.eq()', () => {\n    const p = tachyon.math.bn254.G1PointXYZZ.zero();\n    const p2 = tachyon.math.bn254.G1PointXYZZ.generator();\n    expect(p.eq(p)).toBe(true);\n    expect(p2.eq(p2)).toBe(true);\n    expect(p.eq(p2)).toBe(false);\n    expect(p2.eq(p)).toBe(false);\n  });\n\n  test('PointXYZZ.ne()', () => {\n    const p = tachyon.math.bn254.G1PointXYZZ.zero();\n    const p2 = tachyon.math.bn254.G1PointXYZZ.generator();\n    expect(p.ne(p)).toBe(false);\n    expect(p2.ne(p2)).toBe(false);\n    expect(p.ne(p2)).toBe(true);\n    expect(p2.ne(p)).toBe(true);\n  });\n\n  test('PointXYZZ.add()', () => {\n    const p = tachyon.math.bn254.G1PointXYZZ.generator();\n    const p2 = tachyon.math.bn254.G1PointXYZZ.generator();\n    expect(p.add(p2).eq(new tachyon.math.bn254.G1PointXYZZ(\n      tachyon.math.bn254.Fq.fromDecString('21888242871839275222246405745257275088696311157297823662689037894645226208560'),\n      tachyon.math.bn254.Fq.fromDecString('21888242871839275222246405745257275088696311157297823662689037894645226208572'),\n      tachyon.math.bn254.Fq.fromNumber(16),\n      tachyon.math.bn254.Fq.fromNumber(64),\n    ))).toBe(true);\n  });\n\n  test('PointXYZZ.addMixed()', () => {\n    const p = tachyon.math.bn254.G1PointXYZZ.generator();\n    const p2 = tachyon.math.bn254.G1AffinePoint.generator();\n    expect(p.addMixed(p2).eq(new tachyon.math.bn254.G1PointXYZZ(\n      tachyon.math.bn254.Fq.fromDecString('21888242871839275222246405745257275088696311157297823662689037894645226208560'),\n      tachyon.math.bn254.Fq.fromDecString('21888242871839275222246405745257275088696311157297823662689037894645226208572'),\n      tachyon.math.bn254.Fq.fromNumber(16),\n      tachyon.math.bn254.Fq.fromNumber(64),\n    ))).toBe(true);\n  });\n\n  test('PointXYZZ.sub()', () => {\n    const p = tachyon.math.bn254.G1PointXYZZ.generator();\n    const p2 = tachyon.math.bn254.G1PointXYZZ.generator();\n    expect(p.sub(p2).isZero()).toBe(true);\n  });\n\n  test('PointXYZZ.subMixed()', () => {\n    const p = tachyon.math.bn254.G1PointXYZZ.generator();\n    const p2 = tachyon.math.bn254.G1AffinePoint.generator();\n    expect(p.subMixed(p2).isZero()).toBe(true);\n  });\n\n  test('PointXYZZ.negate()', () => {\n    const p = tachyon.math.bn254.G1PointXYZZ.generator();\n    expect(p.negate().eq(new tachyon.math.bn254.G1PointXYZZ(\n      p.x,\n      p.y.negate(),\n      p.zz,\n      p.zzz,\n    ))).toBe(true);\n  });\n\n  test('PointXYZZ.double()', () => {\n    const p = tachyon.math.bn254.G1PointXYZZ.generator();\n    expect(p.double().eq(new tachyon.math.bn254.G1PointXYZZ(\n      tachyon.math.bn254.Fq.fromDecString('21888242871839275222246405745257275088696311157297823662689037894645226208560'),\n      tachyon.math.bn254.Fq.fromDecString('21888242871839275222246405745257275088696311157297823662689037894645226208572'),\n      tachyon.math.bn254.Fq.fromNumber(16),\n      tachyon.math.bn254.Fq.fromNumber(64),\n    ))).toBe(true);\n  });\n});\n"
  },
  {
    "path": "tachyon/node/test/src/math/elliptic_curves/short_weierstrass/projective_point.spec.ts",
    "content": "import { beforeAll, describe, expect, test } from '@jest/globals';\n\nconst tachyon = require('../../../../external/kroma_network_tachyon/tachyon/node/tachyon.node');\n\nbeforeAll(() => {\n  tachyon.math.bn254.init();\n});\n\ndescribe('ProjectivePoint', () => {\n  test('new ProjectivePoint()', () => {\n    const p = new tachyon.math.bn254.G1ProjectivePoint();\n    expect(p.x.isOne()).toBe(true);\n    expect(p.y.isOne()).toBe(true);\n    expect(p.z.isZero()).toBe(true);\n\n    const x = tachyon.math.bn254.Fq.random();\n    const y = tachyon.math.bn254.Fq.random();\n    const z = tachyon.math.bn254.Fq.random();\n    const p2 = new tachyon.math.bn254.G1ProjectivePoint(x, y, z);\n    expect(p2.x.eq(x)).toBe(true);\n    expect(p2.y.eq(y)).toBe(true);\n    expect(p2.z.eq(z)).toBe(true);\n  });\n\n  test('ProjectivePoint.zero()', () => {\n    const p = tachyon.math.bn254.G1ProjectivePoint.zero();\n    expect(p.isZero()).toBe(true);\n    expect(p.z.isZero()).toBe(true);\n  });\n\n  test('ProjectivePoint.generator()', () => {\n    const p = tachyon.math.bn254.G1ProjectivePoint.generator();\n    expect(p.isOnCurve()).toBe(true);\n    expect(p.z.isZero()).toBe(false);\n  });\n\n  test('ProjectivePoint.random()', () => {\n    const p = tachyon.math.bn254.G1ProjectivePoint.random();\n    const p2 = tachyon.math.bn254.G1ProjectivePoint.random();\n    expect(p.eq(p2)).toBe(false);\n  });\n\n  test('ProjectivePoint.eq()', () => {\n    const p = tachyon.math.bn254.G1ProjectivePoint.zero();\n    const p2 = tachyon.math.bn254.G1ProjectivePoint.generator();\n    expect(p.eq(p)).toBe(true);\n    expect(p2.eq(p2)).toBe(true);\n    expect(p.eq(p2)).toBe(false);\n    expect(p2.eq(p)).toBe(false);\n  });\n\n  test('ProjectivePoint.ne()', () => {\n    const p = tachyon.math.bn254.G1ProjectivePoint.zero();\n    const p2 = tachyon.math.bn254.G1ProjectivePoint.generator();\n    expect(p.ne(p)).toBe(false);\n    expect(p2.ne(p2)).toBe(false);\n    expect(p.ne(p2)).toBe(true);\n    expect(p2.ne(p)).toBe(true);\n  });\n\n  test('ProjectivePoint.add()', () => {\n    const p = tachyon.math.bn254.G1ProjectivePoint.generator();\n    const p2 = tachyon.math.bn254.G1ProjectivePoint.generator();\n    expect(p.add(p2).eq(new tachyon.math.bn254.G1ProjectivePoint(\n      tachyon.math.bn254.Fq.fromDecString('21888242871839275222246405745257275088696311157297823662689037894645226208491'),\n      tachyon.math.bn254.Fq.fromDecString('21888242871839275222246405745257275088696311157297823662689037894645226208572'),\n      tachyon.math.bn254.Fq.fromNumber(64),\n    ))).toBe(true);\n  });\n\n  test('ProjectivePoint.addMixed()', () => {\n    const p = tachyon.math.bn254.G1ProjectivePoint.generator();\n    const p2 = tachyon.math.bn254.G1AffinePoint.generator();\n    expect(p.addMixed(p2).eq(new tachyon.math.bn254.G1ProjectivePoint(\n      tachyon.math.bn254.Fq.fromDecString('21888242871839275222246405745257275088696311157297823662689037894645226208491'),\n      tachyon.math.bn254.Fq.fromDecString('21888242871839275222246405745257275088696311157297823662689037894645226208572'),\n      tachyon.math.bn254.Fq.fromNumber(64),\n    ))).toBe(true);\n  });\n\n  test('ProjectivePoint.sub()', () => {\n    const p = tachyon.math.bn254.G1ProjectivePoint.generator();\n    const p2 = tachyon.math.bn254.G1ProjectivePoint.generator();\n    expect(p.sub(p2).isZero()).toBe(true);\n  });\n\n  test('ProjectivePoint.subMixed()', () => {\n    const p = tachyon.math.bn254.G1ProjectivePoint.generator();\n    const p2 = tachyon.math.bn254.G1AffinePoint.generator();\n    expect(p.subMixed(p2).isZero()).toBe(true);\n  });\n\n  test('ProjectivePoint.negate()', () => {\n    const p = tachyon.math.bn254.G1ProjectivePoint.generator();\n    expect(p.negate().eq(new tachyon.math.bn254.G1ProjectivePoint(\n      p.x,\n      p.y.negate(),\n      p.z,\n    ))).toBe(true);\n  });\n\n  test('ProjectivePoint.double()', () => {\n    const p = tachyon.math.bn254.G1ProjectivePoint.generator();\n    expect(p.double().eq(new tachyon.math.bn254.G1ProjectivePoint(\n      tachyon.math.bn254.Fq.fromDecString('21888242871839275222246405745257275088696311157297823662689037894645226208491'),\n      tachyon.math.bn254.Fq.fromDecString('21888242871839275222246405745257275088696311157297823662689037894645226208572'),\n      tachyon.math.bn254.Fq.fromNumber(64),\n    ))).toBe(true);\n  });\n});\n"
  },
  {
    "path": "tachyon/node/test/src/math/finite_fields/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_jest.bzl\", \"tachyon_jest_unittest\")\nload(\"//bazel:tachyon_ts.bzl\", \"tachyon_ts_project\")\n\ntachyon_ts_project(\n    name = \"finite_fields\",\n    testonly = True,\n    srcs = [\"prime_field.spec.ts\"],\n    data = [\"@kroma_network_tachyon//tachyon/node:tachyon\"],\n)\n\ntachyon_jest_unittest(\n    name = \"finite_fields_unittests\",\n    data = [\":finite_fields\"],\n)\n"
  },
  {
    "path": "tachyon/node/test/src/math/finite_fields/prime_field.spec.ts",
    "content": "import { beforeAll, describe, expect, test } from '@jest/globals';\n\nconst tachyon = require('../../../external/kroma_network_tachyon/tachyon/node/tachyon.node');\n\nbeforeAll(() => {\n  tachyon.math.bn254.init();\n});\n\ndescribe('PrimeField', () => {\n  test('PrimeField.zero()', () => {\n    const f = tachyon.math.bn254.Fq.zero();\n    expect(f.isZero()).toBe(true);\n    expect(f.isOne()).toBe(false);\n  });\n\n  test('PrimeField.one()', () => {\n    const f = tachyon.math.bn254.Fq.one();\n    expect(f.isZero()).toBe(false);\n    expect(f.isOne()).toBe(true);\n  });\n\n  test('PrimeField.random()', () => {\n    const f = tachyon.math.bn254.Fq.random();\n    const f2 = tachyon.math.bn254.Fq.random();\n    expect(f.eq(f2)).toBe(false);\n  });\n\n  test('PrimeField.fromDecString()', () => {\n    const f = tachyon.math.bn254.Fq.fromDecString('12345678910');\n    expect(f.toString()).toBe('12345678910');\n    expect(f.toHexString()).toBe('0x2dfdc1c3e');\n  });\n\n  test('PrimeField.fromHexString()', () => {\n    const f = tachyon.math.bn254.Fq.fromHexString('0x2dfdc1c3e');\n    expect(f.toString()).toBe('12345678910');\n    expect(f.toHexString()).toBe('0x2dfdc1c3e');\n  });\n\n  test('PrimeField.eq()', () => {\n    const f = tachyon.math.bn254.Fq.zero();\n    const f2 = tachyon.math.bn254.Fq.one();\n    expect(f.eq(f)).toBe(true);\n    expect(f2.eq(f2)).toBe(true);\n    expect(f.eq(f2)).toBe(false);\n    expect(f2.eq(f)).toBe(false);\n  });\n\n  test('PrimeField.ne()', () => {\n    const f = tachyon.math.bn254.Fq.zero();\n    const f2 = tachyon.math.bn254.Fq.one();\n    expect(f.ne(f)).toBe(false);\n    expect(f2.ne(f2)).toBe(false);\n    expect(f.ne(f2)).toBe(true);\n    expect(f2.ne(f)).toBe(true);\n  });\n\n  test('PrimeField.lt()', () => {\n    const f = tachyon.math.bn254.Fq.zero();\n    const f2 = tachyon.math.bn254.Fq.one();\n    expect(f.lt(f)).toBe(false);\n    expect(f2.lt(f2)).toBe(false);\n    expect(f.lt(f2)).toBe(true);\n    expect(f2.lt(f)).toBe(false);\n  });\n\n  test('PrimeField.le()', () => {\n    const f = tachyon.math.bn254.Fq.zero();\n    const f2 = tachyon.math.bn254.Fq.one();\n    expect(f.le(f)).toBe(true);\n    expect(f2.le(f2)).toBe(true);\n    expect(f.le(f2)).toBe(true);\n    expect(f2.le(f)).toBe(false);\n  });\n\n  test('PrimeField.gt()', () => {\n    const f = tachyon.math.bn254.Fq.zero();\n    const f2 = tachyon.math.bn254.Fq.one();\n    expect(f.gt(f)).toBe(false);\n    expect(f2.gt(f2)).toBe(false);\n    expect(f.gt(f2)).toBe(false);\n    expect(f2.gt(f)).toBe(true);\n  });\n\n  test('PrimeField.ge()', () => {\n    const f = tachyon.math.bn254.Fq.zero();\n    const f2 = tachyon.math.bn254.Fq.one();\n    expect(f.ge(f)).toBe(true);\n    expect(f2.ge(f2)).toBe(true);\n    expect(f.ge(f2)).toBe(false);\n    expect(f2.ge(f)).toBe(true);\n  });\n\n  test('PrimeField.add()', () => {\n    const f = tachyon.math.bn254.Fq.fromNumber(4);\n    const f2 = tachyon.math.bn254.Fq.fromNumber(3);\n    expect(f.add(f2).eq(tachyon.math.bn254.Fq.fromNumber(7))).toBe(true);\n  });\n\n  test('PrimeField.sub()', () => {\n    const f = tachyon.math.bn254.Fq.fromNumber(4);\n    const f2 = tachyon.math.bn254.Fq.fromNumber(3);\n    expect(f.sub(f2).eq(tachyon.math.bn254.Fq.fromNumber(1))).toBe(true);\n  });\n\n  test('PrimeField.mul()', () => {\n    const f = tachyon.math.bn254.Fq.fromNumber(4);\n    const f2 = tachyon.math.bn254.Fq.fromNumber(3);\n    expect(f.mul(f2).eq(tachyon.math.bn254.Fq.fromNumber(12))).toBe(true);\n  });\n\n  test('PrimeField.div()', () => {\n    const f = tachyon.math.bn254.Fq.fromNumber(4);\n    const f2 = tachyon.math.bn254.Fq.fromNumber(3);\n    expect(f.div(f2).eq(tachyon.math.bn254.Fq.fromDecString('14592161914559516814830937163504850059130874104865215775126025263096817472390'))).toBe(true);\n  });\n\n  test('PrimeField.negate()', () => {\n    const f = tachyon.math.bn254.Fq.fromNumber(4);\n    expect(f.negate().eq(tachyon.math.bn254.Fq.fromDecString('21888242871839275222246405745257275088696311157297823662689037894645226208579'))).toBe(true);\n  });\n\n  test('PrimeField.double()', () => {\n    const f = tachyon.math.bn254.Fq.fromNumber(4);\n    expect(f.double().eq(tachyon.math.bn254.Fq.fromNumber(8))).toBe(true);\n  });\n\n  test('PrimeField.square()', () => {\n    const f = tachyon.math.bn254.Fq.fromNumber(4);\n    expect(f.square().eq(tachyon.math.bn254.Fq.fromNumber(16))).toBe(true);\n  });\n});\n"
  },
  {
    "path": "tachyon/node/test/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es2015\",\n    \"module\": \"commonjs\",\n    \"declaration\": true,\n    \"sourceMap\": true,\n    \"inlineSources\": true,\n    \"esModuleInterop\": true\n  }\n}\n"
  },
  {
    "path": "tachyon/py/BUILD.bazel",
    "content": "load(\"//bazel:tachyon.bzl\", \"if_py_binding\")\nload(\"//bazel:tachyon_py.bzl\", \"tachyon_pybind_extension\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_pybind_extension(\n    name = \"tachyon\",\n    srcs = if_py_binding([\"tachyon.cc\"]),\n    deps = [\n        \"//tachyon/py/math\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/py/base/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_py.bzl\", \"tachyon_pybind_library\")\n\npackage(default_visibility = [\"//tachyon/py:__subpackages__\"])\n\ntachyon_pybind_library(\n    name = \"pybind11\",\n    hdrs = [\"pybind11.h\"],\n)\n"
  },
  {
    "path": "tachyon/py/base/pybind11.h",
    "content": "#ifndef TACHYON_PY_BASE_PYBIND11_H_\n#define TACHYON_PY_BASE_PYBIND11_H_\n\n#include \"pybind11/pybind11.h\"\n\nnamespace py11 = pybind11;\n\n#endif  // TACHYON_PY_BASE_PYBIND11_H_\n"
  },
  {
    "path": "tachyon/py/math/BUILD.bazel",
    "content": "load(\"//bazel:tachyon.bzl\", \"if_py_binding\")\nload(\"//bazel:tachyon_py.bzl\", \"tachyon_pybind_library\")\n\npackage(default_visibility = [\"//tachyon/py:__pkg__\"])\n\nTPLS = [\n    \"//tachyon/py/math/elliptic_curves/{}:fq\",\n    \"//tachyon/py/math/elliptic_curves/{}:fr\",\n    \"//tachyon/py/math/elliptic_curves/{}:g1\",\n]\n\nCURVES = [\n    \"bls12/bls12_381\",\n    \"bn/bn254\",\n]\n\nCURVE_DEPS = [tpl.format(curve) for tpl in TPLS for curve in CURVES]\n\ntachyon_pybind_library(\n    name = \"math\",\n    srcs = if_py_binding([\"math.cc\"]),\n    hdrs = [\"math.h\"],\n    deps = CURVE_DEPS,\n)\n"
  },
  {
    "path": "tachyon/py/math/base/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_py.bzl\", \"tachyon_pybind_library\")\n\npackage(default_visibility = [\"//tachyon/py/math:__subpackages__\"])\n\ntachyon_pybind_library(\n    name = \"big_int\",\n    hdrs = [\"big_int.h\"],\n    deps = [\"//tachyon/math/base:big_int\"],\n)\n"
  },
  {
    "path": "tachyon/py/math/base/big_int.h",
    "content": "#ifndef TACHYON_PY_MATH_BASE_BIG_INT_H_\n#define TACHYON_PY_MATH_BASE_BIG_INT_H_\n\n#include \"pybind11/pybind11.h\"\n\n#include \"tachyon/build/build_config.h\"\n#include \"tachyon/math/base/big_int.h\"\n\nnamespace pybind11 {\nnamespace detail {\n\ninline constexpr const char* GetByteOrder() {\n#if ARCH_CPU_BIG_ENDIAN == 1\n  return \"big\";\n#else\n  return \"little\";\n#endif\n}\n\n#define DEFINE_BIGINT_TYPE_CASTER(N)                                          \\\n  template <>                                                                 \\\n  struct type_caster<tachyon::math::BigInt<N>> {                              \\\n   public:                                                                    \\\n    PYBIND11_TYPE_CASTER(tachyon::math::BigInt<N>,                            \\\n                         const_name(\"tachyon.math.BigInt\" #N));               \\\n                                                                              \\\n    bool load(handle src, bool) {                                             \\\n      PyObject* source = src.ptr();                                           \\\n      PyObject* bit_length_obj =                                              \\\n          PyObject_CallMethod(source, \"bit_length\", nullptr);                 \\\n      size_t bit_length = PyLong_AsSize_t(bit_length_obj);                    \\\n      Py_DECREF(bit_length_obj);                                              \\\n      size_t byte_length = (bit_length + 7) / 8;                              \\\n      if (byte_length > N) return false;                                      \\\n      PyObject* bytes =                                                       \\\n          PyObject_CallMethod(source, \"to_bytes\", \"(is)\",                     \\\n                              static_cast<int>(byte_length), GetByteOrder()); \\\n      if (!bytes) return false;                                               \\\n      char* buf;                                                              \\\n      Py_ssize_t len;                                                         \\\n      int ret = PyBytes_AsStringAndSize(bytes, &buf, &len);                   \\\n      Py_DECREF(bytes);                                                       \\\n      if (ret == -1) return false;                                            \\\n      memcpy(&value.limbs[0], buf, len);                                      \\\n      return !PyErr_Occurred();                                               \\\n    }                                                                         \\\n                                                                              \\\n    static handle cast(const tachyon::math::BigInt<N>& src,                   \\\n                       return_value_policy, handle) {                         \\\n      return PyBytes_FromStringAndSize(                                       \\\n          reinterpret_cast<const char*>(src.limbs), N);                       \\\n    }                                                                         \\\n  }\n\nDEFINE_BIGINT_TYPE_CASTER(4);\nDEFINE_BIGINT_TYPE_CASTER(6);\n\n}  // namespace detail\n}  // namespace pybind11\n\n#endif  // TACHYON_PY_MATH_BASE_BIG_INT_H_\n"
  },
  {
    "path": "tachyon/py/math/elliptic_curves/bls12/bls12_381/BUILD.bazel",
    "content": "load(\"//tachyon/py/math/elliptic_curves/generator:build_defs.bzl\", \"generate_ec_points\")\n\npackage(default_visibility = [\"//tachyon/py/math:__pkg__\"])\n\ngenerate_ec_points(\n    name = \"bls12_381\",\n    g1_deps = [\"//tachyon/math/elliptic_curves/bls12/bls12_381:g1\"],\n)\n"
  },
  {
    "path": "tachyon/py/math/elliptic_curves/bn/bn254/BUILD.bazel",
    "content": "load(\"//tachyon/py/math/elliptic_curves/generator:build_defs.bzl\", \"generate_ec_points\")\n\npackage(default_visibility = [\"//tachyon/py/math:__pkg__\"])\n\ngenerate_ec_points(\n    name = \"bn254\",\n    g1_deps = [\"//tachyon/math/elliptic_curves/bn/bn254:g1\"],\n)\n"
  },
  {
    "path": "tachyon/py/math/elliptic_curves/generator/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_binary\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_binary(\n    name = \"generator\",\n    srcs = [\"generator.cc\"],\n    deps = [\n        \"//tachyon/base/console\",\n        \"//tachyon/base/files:file_path_flag\",\n        \"//tachyon/base/flag:flag_parser\",\n        \"//tachyon/build:cc_writer\",\n        \"//tachyon/c/math/elliptic_curves/generator:generator_util\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/py/math/elliptic_curves/generator/build_defs.bzl",
    "content": "load(\"//bazel:tachyon.bzl\", \"if_py_binding\")\nload(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\ndef _generate_ec_point_impl(ctx):\n    arguments = [\n        \"--out=%s\" % (ctx.outputs.out.path),\n        \"--type=%s\" % (ctx.attr.type),\n    ]\n\n    ctx.actions.run(\n        tools = [ctx.executable._tool],\n        executable = ctx.executable._tool,\n        outputs = [ctx.outputs.out],\n        arguments = arguments,\n    )\n\n    return [DefaultInfo(files = depset([ctx.outputs.out]))]\n\ngenerate_ec_point = rule(\n    implementation = _generate_ec_point_impl,\n    attrs = {\n        \"out\": attr.output(mandatory = True),\n        \"type\": attr.string(mandatory = True),\n        \"_tool\": attr.label(\n            # TODO(chokobole): Change to \"exec\", so we can build on macos.\n            cfg = \"target\",\n            executable = True,\n            allow_single_file = True,\n            default = Label(\"@kroma_network_tachyon//tachyon/py/math/elliptic_curves/generator\"),\n        ),\n    },\n)\n\ndef generate_ec_points(\n        name,\n        g1_deps):\n    for n in [\n        (\"gen_fq_hdr\", \"fq.h\"),\n        (\"gen_fq_src\", \"fq.cc\"),\n        (\"gen_fr_hdr\", \"fr.h\"),\n        (\"gen_fr_src\", \"fr.cc\"),\n        (\"gen_g1_hdr\", \"g1.h\"),\n        (\"gen_g1_src\", \"g1.cc\"),\n    ]:\n        generate_ec_point(\n            type = name,\n            name = n[0],\n            out = n[1],\n        )\n\n    tachyon_cc_library(\n        name = \"fq\",\n        hdrs = [\"fq.h\"],\n        srcs = if_py_binding([\"fq.cc\"]),\n        deps = g1_deps + [\n            \"//tachyon/py/math/finite_fields:prime_field\",\n        ],\n    )\n\n    tachyon_cc_library(\n        name = \"fr\",\n        hdrs = [\"fr.h\"],\n        srcs = if_py_binding([\"fr.cc\"]),\n        deps = g1_deps + [\n            \"//tachyon/py/math/finite_fields:prime_field\",\n        ],\n    )\n\n    tachyon_cc_library(\n        name = \"g1\",\n        hdrs = [\"g1.h\"],\n        srcs = if_py_binding([\"g1.cc\"]),\n        deps = g1_deps + [\n            \"//tachyon/py/math/elliptic_curves/short_weierstrass:points\",\n        ],\n    )\n"
  },
  {
    "path": "tachyon/py/math/elliptic_curves/generator/generator.cc",
    "content": "#include \"absl/strings/str_replace.h\"\n\n#include \"tachyon/base/console/iostream.h\"\n#include \"tachyon/base/files/file_path_flag.h\"\n#include \"tachyon/base/flag/flag_parser.h\"\n#include \"tachyon/base/strings/string_util.h\"\n#include \"tachyon/build/cc_writer.h\"\n#include \"tachyon/c/math/elliptic_curves/generator/generator_util.h\"\n\nnamespace tachyon {\n\nstruct GenerationConfig : public build::CcWriter {\n  std::string type;\n\n  int GeneratePrimeFieldHdr(std::string_view suffix) const;\n  int GenerateFqHdr() const;\n  int GenerateFrHdr() const;\n  int GeneratePrimeFieldSrc(std::string_view suffix) const;\n  int GenerateFqSrc() const;\n  int GenerateFrSrc() const;\n  int GenerateG1Hdr() const;\n  int GenerateG1Src() const;\n};\n\nint GenerationConfig::GeneratePrimeFieldHdr(std::string_view suffix) const {\n  // clang-format off\n  std::vector<std::string_view> tpl = {\n      \"#include \\\"tachyon/py/base/pybind11.h\\\"\",\n      \"\",\n      \"namespace tachyon::py::math::%{type} {\",\n      \"\",\n      \"void Add%{cc_field}(py11::module& m);\",\n      \"\",\n      \"} // namespace tachyon::py::math::%{type}\",\n  };\n  // clang-format on\n\n  std::string tpl_content = absl::StrJoin(tpl, \"\\n\");\n\n  std::string content = absl::StrReplaceAll(\n      tpl_content, {\n                       {\"%{type}\", type},\n                       {\"%{cc_field}\", suffix == \"fq\" ? \"Fq\" : \"Fr\"},\n                   });\n  return WriteHdr(content, false);\n}\n\nint GenerationConfig::GenerateFqHdr() const {\n  return GeneratePrimeFieldHdr(\"fq\");\n}\n\nint GenerationConfig::GenerateFrHdr() const {\n  return GeneratePrimeFieldHdr(\"fr\");\n}\n\nint GenerationConfig::GeneratePrimeFieldSrc(std::string_view suffix) const {\n  // clang-format off\n  std::vector<std::string_view> tpl = {\n      \"#include \\\"tachyon/math/elliptic_curves/%{header_dir_name}/%{suffix}.h\\\"\",\n      \"#include \\\"tachyon/py/math/finite_fields/prime_field.h\\\"\",\n      \"\",\n      \"namespace tachyon::py::math::%{type} {\",\n      \"\",\n      \"void Add%{cc_field}(py11::module& m) {\",\n      \"  AddPrimeField<tachyon::math::%{type}::%{cc_field}>(m, \\\"%{cc_field}\\\");\",\n      \"}\",\n      \"\",\n      \"} // namespace tachyon::py::math::%{type}\",\n  };\n  // clang-format on\n\n  std::string tpl_content = absl::StrJoin(tpl, \"\\n\");\n\n  std::string content = absl::StrReplaceAll(\n      tpl_content, {\n                       {\"%{header_dir_name}\", c::math::GetLocation(type)},\n                       {\"%{suffix}\", suffix},\n                       {\"%{type}\", type},\n                       {\"%{cc_field}\", suffix == \"fq\" ? \"Fq\" : \"Fr\"},\n                   });\n  return WriteSrc(content);\n}\n\nint GenerationConfig::GenerateFqSrc() const {\n  return GeneratePrimeFieldSrc(\"fq\");\n}\n\nint GenerationConfig::GenerateFrSrc() const {\n  return GeneratePrimeFieldSrc(\"fr\");\n}\n\nint GenerationConfig::GenerateG1Hdr() const {\n  // clang-format off\n  std::vector<std::string_view> tpl = {\n      \"#include \\\"tachyon/py/base/pybind11.h\\\"\",\n      \"\",\n      \"namespace tachyon::py::math::%{type} {\",\n      \"\",\n      \"void AddG1(py11::module& m);\",\n      \"\",\n      \"} // namespace tachyon::py::math::%{type}\",\n  };\n  // clang-format on\n\n  std::string tpl_content = absl::StrJoin(tpl, \"\\n\");\n\n  std::string content = absl::StrReplaceAll(tpl_content, {\n                                                             {\"%{type}\", type},\n                                                         });\n  return WriteHdr(content, false);\n}\n\nint GenerationConfig::GenerateG1Src() const {\n  // clang-format off\n  std::vector<std::string_view> tpl = {\n      \"#include \\\"tachyon/math/elliptic_curves/%{header_dir_name}/g1.h\\\"\",\n      \"#include \\\"tachyon/py/math/elliptic_curves/short_weierstrass/affine_point.h\\\"\",\n      \"#include \\\"tachyon/py/math/elliptic_curves/short_weierstrass/projective_point.h\\\"\",\n      \"#include \\\"tachyon/py/math/elliptic_curves/short_weierstrass/jacobian_point.h\\\"\",\n      \"#include \\\"tachyon/py/math/elliptic_curves/short_weierstrass/point_xyzz.h\\\"\",\n      \"\",\n      \"namespace tachyon::py::math::%{type} {\",\n      \"\",\n      \"void AddG1(py11::module& m) {\",\n      \"  AddJacobianPoint<tachyon::math::%{type}::G1JacobianPoint>(m, \\\"G1JacobianPoint\\\");\",\n      \"  AddAffinePoint<tachyon::math::%{type}::G1AffinePoint>(m, \\\"G1AffinePoint\\\");\",\n      \"  AddProjectivePoint<tachyon::math::%{type}::G1ProjectivePoint>(m, \\\"G1ProjectivePoint\\\");\",\n      \"  AddPointXYZZ<tachyon::math::%{type}::G1PointXYZZ>(m, \\\"G1PointXYZZ\\\");\",\n      \"}\",\n      \"\",\n      \"} // namespace tachyon::py::math::%{type}\",\n  };\n  // clang-format on\n\n  std::string tpl_content = absl::StrJoin(tpl, \"\\n\");\n\n  std::string content = absl::StrReplaceAll(\n      tpl_content, {\n                       {\"%{header_dir_name}\", c::math::GetLocation(type)},\n                       {\"%{type}\", type},\n                   });\n  return WriteSrc(content);\n}\n\nint RealMain(int argc, char** argv) {\n  GenerationConfig config;\n  config.generator = \"//tachyon/py/math/elliptic_curves/generator\";\n\n  base::FlagParser parser;\n  parser.AddFlag<base::FilePathFlag>(&config.out)\n      .set_long_name(\"--out\")\n      .set_help(\"path to output\");\n  parser.AddFlag<base::StringFlag>(&config.type)\n      .set_long_name(\"--type\")\n      .set_required();\n\n  std::string error;\n  if (!parser.Parse(argc, argv, &error)) {\n    tachyon_cerr << error << std::endl;\n    return 1;\n  }\n\n  if (base::EndsWith(config.out.value(), \"fq.h\")) {\n    return config.GenerateFqHdr();\n  } else if (base::EndsWith(config.out.value(), \"fq.cc\")) {\n    return config.GenerateFqSrc();\n  } else if (base::EndsWith(config.out.value(), \"fr.h\")) {\n    return config.GenerateFrHdr();\n  } else if (base::EndsWith(config.out.value(), \"fr.cc\")) {\n    return config.GenerateFrSrc();\n  } else if (base::EndsWith(config.out.value(), \"g1.h\")) {\n    return config.GenerateG1Hdr();\n  } else if (base::EndsWith(config.out.value(), \"g1.cc\")) {\n    return config.GenerateG1Src();\n  } else {\n    tachyon_cerr << \"suffix not supported:\" << config.out << std::endl;\n    return 1;\n  }\n}\n\n}  // namespace tachyon\n\nint main(int argc, char** argv) { return tachyon::RealMain(argc, argv); }\n"
  },
  {
    "path": "tachyon/py/math/elliptic_curves/short_weierstrass/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_py.bzl\", \"tachyon_pybind_library\")\n\npackage(default_visibility = [\"//tachyon/py/math/elliptic_curves:__subpackages__\"])\n\ntachyon_pybind_library(\n    name = \"points\",\n    hdrs = [\n        \"affine_point.h\",\n        \"jacobian_point.h\",\n        \"point_xyzz.h\",\n        \"projective_point.h\",\n    ],\n    deps = [\n        \"//tachyon/math/geometry:affine_point\",\n        \"//tachyon/math/geometry:jacobian_point\",\n        \"//tachyon/math/geometry:point_xyzz\",\n        \"//tachyon/math/geometry:projective_point\",\n        \"//tachyon/py/base:pybind11\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/py/math/elliptic_curves/short_weierstrass/affine_point.h",
    "content": "#ifndef TACHYON_PY_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_AFFINE_POINT_H_\n#define TACHYON_PY_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_AFFINE_POINT_H_\n\n#include <string>\n\n#include \"pybind11/operators.h\"\n\n#include \"tachyon/math/base/big_int.h\"\n#include \"tachyon/py/base/pybind11.h\"\n\nnamespace tachyon::py::math {\n\ntemplate <typename AffinePoint,\n          typename BaseField = typename AffinePoint::BaseField,\n          typename ScalarField = typename AffinePoint::ScalarField>\nvoid AddAffinePoint(py11::module& m, const std::string& name) {\n  py11::class_<AffinePoint>(m, name.data())\n      .def(py11::init<>())\n      .def(py11::init<const BaseField&, const BaseField&>(), py11::arg(\"x\"),\n           py11::arg(\"y\"))\n      .def_static(\"zero\", &AffinePoint::Zero)\n      .def_static(\"generator\", &AffinePoint::Generator)\n      .def_static(\"random\", &AffinePoint::Random)\n      .def_property_readonly(\"x\", &AffinePoint::x)\n      .def_property_readonly(\"y\", &AffinePoint::y)\n      .def(\"is_zero\", &AffinePoint::IsZero)\n      .def(\"is_on_curve\", &AffinePoint::IsOnCurve)\n      .def(\"to_string\", &AffinePoint::ToString)\n      .def(\"to_hex_string\", &AffinePoint::ToHexString,\n           py11::arg(\"pad_zero\") = false)\n      .def(py11::self == py11::self)\n      .def(py11::self != py11::self)\n      .def(py11::self + py11::self)\n      .def(py11::self - py11::self)\n      .def(py11::self * ScalarField())\n      .def(ScalarField() * py11::self)\n      .def(-py11::self)\n      .def(\"double\", &AffinePoint::Double)\n      .def(\"__repr__\", [name](const AffinePoint& point) {\n        return absl::Substitute(\"$0$1\", name, point.ToString());\n      });\n}\n\n}  // namespace tachyon::py::math\n\n#endif  // TACHYON_PY_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_AFFINE_POINT_H_\n"
  },
  {
    "path": "tachyon/py/math/elliptic_curves/short_weierstrass/jacobian_point.h",
    "content": "#ifndef TACHYON_PY_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_JACOBIAN_POINT_H_\n#define TACHYON_PY_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_JACOBIAN_POINT_H_\n\n#include <string>\n\n#include \"pybind11/operators.h\"\n\n#include \"tachyon/math/geometry/affine_point.h\"\n#include \"tachyon/py/base/pybind11.h\"\n\nnamespace tachyon::py::math {\n\ntemplate <typename JacobianPoint,\n          typename BaseField = typename JacobianPoint::BaseField,\n          typename ScalarField = typename JacobianPoint::ScalarField,\n          typename Curve = typename JacobianPoint::Curve,\n          typename AffinePointTy = tachyon::math::AffinePoint<Curve>>\nvoid AddJacobianPoint(py11::module& m, const std::string& name) {\n  py11::class_<JacobianPoint>(m, name.data())\n      .def(py11::init<>())\n      .def(py11::init<const BaseField&, const BaseField&, const BaseField&>(),\n           py11::arg(\"x\"), py11::arg(\"y\"), py11::arg(\"z\"))\n      .def_static(\"zero\", &JacobianPoint::Zero)\n      .def_static(\"generator\", &JacobianPoint::Generator)\n      .def_static(\"random\", &JacobianPoint::Random)\n      .def_property_readonly(\"x\", &JacobianPoint::x)\n      .def_property_readonly(\"y\", &JacobianPoint::y)\n      .def_property_readonly(\"z\", &JacobianPoint::z)\n      .def(\"is_zero\", &JacobianPoint::IsZero)\n      .def(\"is_on_curve\", &JacobianPoint::IsOnCurve)\n      .def(\"to_string\", &JacobianPoint::ToString)\n      .def(\"to_hex_string\", &JacobianPoint::ToHexString,\n           py11::arg(\"pad_zero\") = false)\n      .def(py11::self == py11::self)\n      .def(py11::self != py11::self)\n      .def(py11::self + py11::self)\n      .def(py11::self += py11::self)\n      .def(py11::self + AffinePointTy())\n      .def(py11::self += AffinePointTy())\n      .def(py11::self - py11::self)\n      // NOTE(chokobole): See https://github.com/pybind/pybind11/issues/1893\n      // .def(py11::self -= py11::self)\n      .def(\n          \"__isub__\",\n          [](JacobianPoint& lhs, const JacobianPoint& rhs) {\n            return lhs -= rhs;\n          },\n          py11::is_operator())\n      .def(py11::self - AffinePointTy())\n      .def(py11::self -= AffinePointTy())\n      .def(py11::self * ScalarField())\n      .def(py11::self *= ScalarField())\n      .def(ScalarField() * py11::self)\n      .def(-py11::self)\n      .def(\"double\", &JacobianPoint::Double)\n      .def(\"double_in_place\", &JacobianPoint::DoubleInPlace)\n      .def(\"__repr__\", [name](const JacobianPoint& point) {\n        return absl::Substitute(\"$0$1\", name, point.ToString());\n      });\n}\n\n}  // namespace tachyon::py::math\n\n#endif  // TACHYON_PY_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_JACOBIAN_POINT_H_\n"
  },
  {
    "path": "tachyon/py/math/elliptic_curves/short_weierstrass/point_xyzz.h",
    "content": "#ifndef TACHYON_PY_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_POINT_XYZZ_H_\n#define TACHYON_PY_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_POINT_XYZZ_H_\n\n#include <string>\n\n#include \"pybind11/operators.h\"\n\n#include \"tachyon/math/geometry/affine_point.h\"\n#include \"tachyon/py/base/pybind11.h\"\n\nnamespace tachyon::py::math {\n\ntemplate <typename PointXYZZ,\n          typename BaseField = typename PointXYZZ::BaseField,\n          typename ScalarField = typename PointXYZZ::ScalarField,\n          typename Curve = typename PointXYZZ::Curve,\n          typename AffinePointTy = tachyon::math::AffinePoint<Curve>>\nvoid AddPointXYZZ(py11::module& m, const std::string& name) {\n  py11::class_<PointXYZZ>(m, name.data())\n      .def(py11::init<>())\n      .def(py11::init<const BaseField&, const BaseField&, const BaseField&,\n                      const BaseField&>(),\n           py11::arg(\"x\"), py11::arg(\"y\"), py11::arg(\"zz\"), py11::arg(\"zzz\"))\n      .def_static(\"zero\", &PointXYZZ::Zero)\n      .def_static(\"generator\", &PointXYZZ::Generator)\n      .def_static(\"random\", &PointXYZZ::Random)\n      .def_property_readonly(\"x\", &PointXYZZ::x)\n      .def_property_readonly(\"y\", &PointXYZZ::y)\n      .def_property_readonly(\"zz\", &PointXYZZ::zz)\n      .def_property_readonly(\"zzz\", &PointXYZZ::zzz)\n      .def(\"is_zero\", &PointXYZZ::IsZero)\n      .def(\"is_on_curve\", &PointXYZZ::IsOnCurve)\n      .def(\"to_string\", &PointXYZZ::ToString)\n      .def(\"to_hex_string\", &PointXYZZ::ToHexString)\n      .def(py11::self == py11::self)\n      .def(py11::self != py11::self)\n      .def(py11::self + py11::self)\n      .def(py11::self += py11::self)\n      .def(py11::self + AffinePointTy())\n      .def(py11::self += AffinePointTy())\n      .def(py11::self - py11::self)\n      // NOTE(chokobole): See https://github.com/pybind/pybind11/issues/1893\n      // .def(py11::self -= py11::self)\n      .def(\n          \"__isub__\",\n          [](PointXYZZ& lhs, const PointXYZZ& rhs) { return lhs -= rhs; },\n          py11::is_operator())\n      .def(py11::self - AffinePointTy())\n      .def(py11::self -= AffinePointTy())\n      .def(py11::self * ScalarField())\n      .def(py11::self *= ScalarField())\n      .def(ScalarField() * py11::self)\n      .def(-py11::self)\n      .def(\"double\", &PointXYZZ::Double)\n      .def(\"double_in_place\", &PointXYZZ::DoubleInPlace)\n      .def(\"__repr__\", [name](const PointXYZZ& point) {\n        return absl::Substitute(\"$0$1\", name, point.ToString());\n      });\n}\n\n}  // namespace tachyon::py::math\n\n#endif  // TACHYON_PY_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_POINT_XYZZ_H_\n"
  },
  {
    "path": "tachyon/py/math/elliptic_curves/short_weierstrass/projective_point.h",
    "content": "#ifndef TACHYON_PY_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_PROJECTIVE_POINT_H_\n#define TACHYON_PY_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_PROJECTIVE_POINT_H_\n\n#include <string>\n\n#include \"pybind11/operators.h\"\n\n#include \"tachyon/math/geometry/affine_point.h\"\n#include \"tachyon/py/base/pybind11.h\"\n\nnamespace tachyon::py::math {\n\ntemplate <typename ProjectivePoint,\n          typename BaseField = typename ProjectivePoint::BaseField,\n          typename ScalarField = typename ProjectivePoint::ScalarField,\n          typename Curve = typename ProjectivePoint::Curve,\n          typename AffinePointTy = tachyon::math::AffinePoint<Curve>>\nvoid AddProjectivePoint(py11::module& m, const std::string& name) {\n  py11::class_<ProjectivePoint>(m, name.data())\n      .def(py11::init<>())\n      .def(py11::init<const BaseField&, const BaseField&, const BaseField&>(),\n           py11::arg(\"x\"), py11::arg(\"y\"), py11::arg(\"z\"))\n      .def_static(\"zero\", &ProjectivePoint::Zero)\n      .def_static(\"generator\", &ProjectivePoint::Generator)\n      .def_static(\"random\", &ProjectivePoint::Random)\n      .def_property_readonly(\"x\", &ProjectivePoint::x)\n      .def_property_readonly(\"y\", &ProjectivePoint::y)\n      .def_property_readonly(\"z\", &ProjectivePoint::z)\n      .def(\"is_zero\", &ProjectivePoint::IsZero)\n      .def(\"is_on_curve\", &ProjectivePoint::IsOnCurve)\n      .def(\"to_string\", &ProjectivePoint::ToString)\n      .def(\"to_hex_string\", &ProjectivePoint::ToHexString,\n           py11::arg(\"pad_zero\") = false)\n      .def(py11::self == py11::self)\n      .def(py11::self != py11::self)\n      .def(py11::self + py11::self)\n      .def(py11::self += py11::self)\n      .def(py11::self + AffinePointTy())\n      .def(py11::self += AffinePointTy())\n      .def(py11::self - py11::self)\n      // NOTE(chokobole): See https://github.com/pybind/pybind11/issues/1893\n      // .def(py11::self -= py11::self)\n      .def(\n          \"__isub__\",\n          [](ProjectivePoint& lhs, const ProjectivePoint& rhs) {\n            return lhs -= rhs;\n          },\n          py11::is_operator())\n      .def(py11::self - AffinePointTy())\n      .def(py11::self -= AffinePointTy())\n      .def(py11::self * ScalarField())\n      .def(py11::self *= ScalarField())\n      .def(ScalarField() * py11::self)\n      .def(-py11::self)\n      .def(\"double\", &ProjectivePoint::Double)\n      .def(\"double_in_place\", &ProjectivePoint::DoubleInPlace)\n      .def(\"__repr__\", [name](const ProjectivePoint& point) {\n        return absl::Substitute(\"$0$1\", name, point.ToString());\n      });\n}\n\n}  // namespace tachyon::py::math\n\n#endif  // TACHYON_PY_MATH_ELLIPTIC_CURVES_SHORT_WEIERSTRASS_PROJECTIVE_POINT_H_\n"
  },
  {
    "path": "tachyon/py/math/finite_fields/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//tachyon/py/math/elliptic_curves:__subpackages__\"])\n\ntachyon_cc_library(\n    name = \"prime_field\",\n    hdrs = [\"prime_field.h\"],\n    deps = [\n        \"//tachyon/py/base:pybind11\",\n        \"//tachyon/py/math/base:big_int\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/py/math/finite_fields/prime_field.h",
    "content": "#ifndef TACHYON_PY_MATH_FINITE_FIELDS_PRIME_FIELD_H_\n#define TACHYON_PY_MATH_FINITE_FIELDS_PRIME_FIELD_H_\n\n#include <string>\n\n#include \"pybind11/operators.h\"\n#include \"pybind11/stl.h\"\n\n#include \"tachyon/py/base/pybind11.h\"\n#include \"tachyon/py/math/base/big_int.h\"\n\nnamespace tachyon::py::math {\n\ntemplate <typename PrimeField, size_t N = PrimeField::N>\nvoid AddPrimeField(py11::module& m, const std::string& name) {\n  py11::class_<PrimeField>(m, name.data())\n      .def(py11::init<const tachyon::math::BigInt<N>>())\n      .def_static(\"zero\", &PrimeField::Zero)\n      .def_static(\"one\", &PrimeField::One)\n      .def_static(\"random\", &PrimeField::Random)\n      .def_static(\"from_dec_string\", &PrimeField::FromDecString)\n      .def_static(\"from_hex_string\", &PrimeField::FromHexString)\n      .def(\"is_zero\", &PrimeField::IsZero)\n      .def(\"is_one\", &PrimeField::IsOne)\n      .def(\"to_string\", &PrimeField::ToString)\n      .def(\"to_hex_string\", &PrimeField::ToHexString,\n           py11::arg(\"pad_zero\") = false)\n      .def(py11::self == py11::self)\n      .def(py11::self != py11::self)\n      .def(py11::self < py11::self)\n      .def(py11::self <= py11::self)\n      .def(py11::self > py11::self)\n      .def(py11::self >= py11::self)\n      .def(py11::self + py11::self)\n      .def(py11::self += py11::self)\n      .def(py11::self - py11::self)\n      // NOTE(chokobole): See https://github.com/pybind/pybind11/issues/1893\n      // .def(py11::self -= py11::self)\n      .def(\n          \"__isub__\",\n          [](PrimeField& lhs, const PrimeField& rhs) { return lhs -= rhs; },\n          py11::is_operator())\n      .def(py11::self * py11::self)\n      .def(py11::self *= py11::self)\n      .def(py11::self / py11::self)\n      // NOTE(chokobole): See https://github.com/pybind/pybind11/issues/1893\n      // .def(py11::self /= py11::self)\n      .def(\n          \"__idiv__\",\n          [](PrimeField& lhs, const PrimeField& rhs) { return lhs /= rhs; },\n          py11::is_operator())\n      .def(-py11::self)\n      .def(\"double\", &PrimeField::Double)\n      .def(\"double_in_place\", &PrimeField::DoubleInPlace)\n      .def(\"square\", &PrimeField::Square)\n      .def(\"square_in_place\", &PrimeField::SquareInPlace)\n      .def(\"__repr__\", [name](const PrimeField& field) {\n        return absl::Substitute(\"$0($1)\", name, field.ToString());\n      });\n}\n\n}  // namespace tachyon::py::math\n\n#endif  // TACHYON_PY_MATH_FINITE_FIELDS_PRIME_FIELD_H_\n"
  },
  {
    "path": "tachyon/py/math/math.cc",
    "content": "#include \"tachyon/py/math/math.h\"\n\n#include \"tachyon/math/elliptic_curves/bls12/bls12_381/g1.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/g1.h\"\n#include \"tachyon/py/math/elliptic_curves/bls12/bls12_381/fq.h\"\n#include \"tachyon/py/math/elliptic_curves/bls12/bls12_381/fr.h\"\n#include \"tachyon/py/math/elliptic_curves/bls12/bls12_381/g1.h\"\n#include \"tachyon/py/math/elliptic_curves/bn/bn254/fq.h\"\n#include \"tachyon/py/math/elliptic_curves/bn/bn254/fr.h\"\n#include \"tachyon/py/math/elliptic_curves/bn/bn254/g1.h\"\n\nnamespace tachyon::py::math {\n\nvoid AddMath(py11::module& m) {\n  py11::module math = m.def_submodule(\"math\");\n  py11::module bls12_381 = math.def_submodule(\"bls12_381\");\n  bls12_381.def(\"init\", &tachyon::math::bls12_381::G1Curve::Init);\n  bls12_381::AddFq(bls12_381);\n  bls12_381::AddFr(bls12_381);\n  bls12_381::AddG1(bls12_381);\n\n  py11::module bn254 = math.def_submodule(\"bn254\");\n  bn254.def(\"init\", &tachyon::math::bn254::G1Curve::Init);\n  bn254::AddFq(bn254);\n  bn254::AddFr(bn254);\n  bn254::AddG1(bn254);\n}\n\n}  // namespace tachyon::py::math\n"
  },
  {
    "path": "tachyon/py/math/math.h",
    "content": "#ifndef TACHYON_PY_MATH_MATH_H_\n#define TACHYON_PY_MATH_MATH_H_\n\n#include \"tachyon/py/base/pybind11.h\"\n\nnamespace tachyon::py::math {\n\nvoid AddMath(py11::module& m);\n\n}  // namespace tachyon::py::math\n\n#endif  // TACHYON_PY_MATH_MATH_H_\n"
  },
  {
    "path": "tachyon/py/tachyon.cc",
    "content": "#include \"tachyon/py/math/math.h\"\n\nnamespace tachyon::py {\n\nPYBIND11_MODULE(tachyon, m) {\n  m.doc() = \"Bindings for tachyon.\";\n\n  math::AddMath(m);\n}\n\n}  // namespace tachyon::py\n"
  },
  {
    "path": "tachyon/py/test/.bazelrc",
    "content": "# Enable platform specific configurations by default depending on host machine.\ncommon --enable_platform_specific_config\n\nbuild:linux --cxxopt=-std=c++17\nbuild:linux --host_cxxopt=-std=c++17\nbuild:macos --cxxopt=-std=c++17\nbuild:macos --host_cxxopt=-std=c++17\nbuild:macos --objccopt=-std=c++17\nbuild:windows --cxxopt=/std:c++17\nbuild:windows --host_cxxopt=/std:c++17\n\nbuild:macos_x86_64 --config=macos\nbuild:macos_x86_64 --cpu=darwin_x86_64\nbuild:macos_x86_64 --host_cpu=darwin_x86_64\nbuild:macos_arm64 --config=macos\nbuild:macos_arm64 --cpu=darwin_arm64\nbuild:macos_arm64 --host_cpu=darwin_arm64\n\nbuild --@kroma_network_tachyon//:py_binding\n\n# gmp needs exception.\nbuild --@kroma_network_tachyon//:has_exception\n# pybind needs rtti\nbuild --@kroma_network_tachyon//:has_rtti\n\nbuild --@kroma_network_tachyon//:shared_object\n"
  },
  {
    "path": "tachyon/py/test/.bazelversion",
    "content": "6.3.0\n"
  },
  {
    "path": "tachyon/py/test/BUILD.bazel",
    "content": ""
  },
  {
    "path": "tachyon/py/test/WORKSPACE",
    "content": "load(\"@bazel_tools//tools/build_defs/repo:http.bzl\", \"http_archive\")\n\nhttp_archive(\n    name = \"rules_python\",\n    sha256 = \"84aec9e21cc56fbc7f1335035a71c850d1b9b5cc6ff497306f84cced9a769841\",\n    strip_prefix = \"rules_python-0.23.1\",\n    urls = [\"https://github.com/bazelbuild/rules_python/releases/download/0.23.1/rules_python-0.23.1.tar.gz\"],\n)\n\nload(\"@rules_python//python:repositories.bzl\", \"py_repositories\")\n\npy_repositories()\n\nload(\"@rules_python//python:pip.bzl\", \"pip_parse\")\n\n# Create a central repo that knows about the dependencies needed from\n# requirements_lock.txt.\npip_parse(\n    name = \"pip_deps\",\n    requirements_lock = \"//:requirements_lock.txt\",\n)\n\n# Load the starlark macro, which will define your dependencies.\nload(\"@pip_deps//:requirements.bzl\", \"install_deps\")\n\n# Call it to define repos for your requirements.\ninstall_deps()\n\n###########################################\n\nlocal_repository(\n    name = \"kroma_network_tachyon\",\n    path = \"../../../\",\n)\n\nload(\"@kroma_network_tachyon//bazel:tachyon_deps.bzl\", \"tachyon_deps\")\n\ntachyon_deps()\n\nload(\"@kroma_network_tachyon//bazel:pybind11_deps.bzl\", \"pybind11_deps\")\n\npybind11_deps()\n\nload(\"@kroma_network_tachyon//bazel:buildifier_deps.bzl\", \"buildifier_deps\")\n\nbuildifier_deps()\n\n# Start of rules_js\nload(\"@kroma_network_tachyon//bazel:js_deps.bzl\", \"js_deps\")\n\njs_deps()\n\nload(\"@aspect_rules_js//js:repositories.bzl\", \"rules_js_dependencies\")\n\nrules_js_dependencies()\n\nload(\"@aspect_bazel_lib//lib:repositories.bzl\", \"aspect_bazel_lib_dependencies\")\n\naspect_bazel_lib_dependencies()\n\n# Fetch and register node, if you haven't already\nload(\"@rules_nodejs//nodejs:repositories.bzl\", \"DEFAULT_NODE_VERSION\", \"nodejs_register_toolchains\")\n\nnodejs_register_toolchains(\n    name = \"nodejs\",\n    node_version = DEFAULT_NODE_VERSION,\n)\n\nload(\"@aspect_rules_js//npm:repositories.bzl\", \"npm_translate_lock\")\n\nnpm_translate_lock(\n    name = \"npm\",\n    data = [\"@iden3_ffiasm//:package.json\"],\n    npm_package_lock = \"@iden3_ffiasm//:package-lock.json\",\n    pnpm_lock = \"@iden3_ffiasm//:pnpm-lock.yaml\",\n    update_pnpm_lock = True,\n    verify_node_modules_ignored = \"@iden3_ffiasm//:.bazelignore\",\n)\n\nload(\"@npm//:repositories.bzl\", \"npm_repositories\")\n\nnpm_repositories()\n\n# End of rules_js\n"
  },
  {
    "path": "tachyon/py/test/bazel/BUILD.bazel",
    "content": ""
  },
  {
    "path": "tachyon/py/test/bazel/tachyon_py.bzl",
    "content": "load(\"@rules_python//python:defs.bzl\", \"py_binary\", \"py_library\", \"py_test\")\n\ndef tachyon_py_library(\n        name,\n        python_version = \"PY3\",\n        srcs_version = \"PY3\",\n        **kwargs):\n    py_library(\n        name = name,\n        python_version = python_version,\n        srcs_version = srcs_version,\n        **kwargs\n    )\n\ndef tachyon_py_binary(\n        name,\n        python_version = \"PY3\",\n        srcs_version = \"PY3\",\n        **kwargs):\n    py_binary(\n        name = name,\n        python_version = python_version,\n        srcs_version = srcs_version,\n        **kwargs\n    )\n\ndef tachyon_py_test(\n        name,\n        python_version = \"PY3\",\n        srcs_version = \"PY3\",\n        **kwargs):\n    py_test(\n        name = name,\n        python_version = python_version,\n        srcs_version = srcs_version,\n        **kwargs\n    )\n\ndef tachyon_py_unittest(\n        name,\n        **kwargs):\n    py_test(\n        name = name + \"_unittests\",\n        **kwargs\n    )\n"
  },
  {
    "path": "tachyon/py/test/requirements_lock.txt",
    "content": "absl-py==2.0.0\nnumpy==1.26.0\n"
  },
  {
    "path": "tachyon/py/test/tachyon/math/elliptic_curves/short_weierstrass/BUILD.bazel",
    "content": "load(\"@pip_deps//:requirements.bzl\", \"requirement\")\nload(\"//bazel:tachyon_py.bzl\", \"tachyon_py_unittest\")\n\ntachyon_py_unittest(\n    name = \"short_weierstrass_unittests\",\n    srcs = [\n        \"affine_point_unittest.py\",\n        \"jacobian_point_unittest.py\",\n        \"point_xyzz_unittest.py\",\n        \"projective_point_unittest.py\",\n        \"short_weierstrass_unittests.py\",\n    ],\n    data = [\"@kroma_network_tachyon//tachyon/py:tachyon.so\"],\n    main = \"short_weierstrass_unittests.py\",\n    deps = [\n        requirement(\"absl-py\"),\n    ],\n)\n"
  },
  {
    "path": "tachyon/py/test/tachyon/math/elliptic_curves/short_weierstrass/affine_point_unittest.py",
    "content": "from absl.testing import absltest\n\nfrom external.kroma_network_tachyon.tachyon.py import tachyon\n\n\nclass AffinePointTest(absltest.TestCase):\n\n    @classmethod\n    def setUpClass(cls):\n        tachyon.math.bn254.init()\n\n    def test_constructor(self):\n        p = tachyon.math.bn254.G1AffinePoint()\n        self.assertTrue(p.x.is_zero())\n        self.assertTrue(p.y.is_zero())\n\n        x = tachyon.math.bn254.Fq.random()\n        y = tachyon.math.bn254.Fq.random()\n        p2 = tachyon.math.bn254.G1AffinePoint(x, y)\n        self.assertEqual(p2.x, x)\n        self.assertEqual(p2.y, y)\n\n    def test_zero(self):\n        p = tachyon.math.bn254.G1AffinePoint.zero()\n        self.assertTrue(p.is_zero())\n\n    def test_generator(self):\n        p = tachyon.math.bn254.G1AffinePoint.generator()\n        self.assertTrue(p.is_on_curve())\n\n    def test_random(self):\n        p = tachyon.math.bn254.G1AffinePoint.random()\n        p2 = tachyon.math.bn254.G1AffinePoint.random()\n        self.assertNotEqual(p, p2)\n\n    def test_eq(self):\n        p = tachyon.math.bn254.G1AffinePoint.zero()\n        p2 = tachyon.math.bn254.G1AffinePoint.generator()\n        self.assertTrue(p == p)\n        self.assertTrue(p2 == p2)\n        self.assertFalse(p == p2)\n        self.assertFalse(p2 == p)\n\n    def test_ne(self):\n        p = tachyon.math.bn254.G1AffinePoint.zero()\n        p2 = tachyon.math.bn254.G1AffinePoint.generator()\n        self.assertFalse(p != p)\n        self.assertFalse(p2 != p2)\n        self.assertTrue(p != p2)\n        self.assertTrue(p2 != p)\n\n    def test_add(self):\n        p = tachyon.math.bn254.G1AffinePoint.generator()\n        p2 = tachyon.math.bn254.G1AffinePoint.generator()\n        self.assertEqual(p + p2, tachyon.math.bn254.G1JacobianPoint(\n            tachyon.math.bn254.Fq.from_dec_string(\n                '21888242871839275222246405745257275088696311157297823662689037894645226208560'),\n            tachyon.math.bn254.Fq.from_dec_string(\n                '21888242871839275222246405745257275088696311157297823662689037894645226208572'),\n            tachyon.math.bn254.Fq(4)\n        ))\n\n    def test_sub(self):\n        p = tachyon.math.bn254.G1AffinePoint.generator()\n        p2 = tachyon.math.bn254.G1AffinePoint.generator()\n        self.assertTrue((p - p2).is_zero())\n\n    def test_scalar_mul(self):\n        p = tachyon.math.bn254.G1AffinePoint.generator()\n        n = tachyon.math.bn254.Fr(2)\n        expected = tachyon.math.bn254.G1JacobianPoint(\n            tachyon.math.bn254.Fq.from_dec_string(\n                '21888242871839275222246405745257275088696311157297823662689037894645226208560'),\n            tachyon.math.bn254.Fq.from_dec_string(\n                '21888242871839275222246405745257275088696311157297823662689037894645226208572'),\n            tachyon.math.bn254.Fq(4))\n        self.assertEqual(p * n, expected)\n        self.assertEqual(n * p, expected)\n\n    def test_negative(self):\n        p = tachyon.math.bn254.G1AffinePoint.generator()\n        self.assertEqual(-p, tachyon.math.bn254.G1AffinePoint(\n            p.x,\n            -p.y\n        ))\n\n    def test_double(self):\n        p = tachyon.math.bn254.G1AffinePoint.generator()\n        self.assertEqual(p.double(), tachyon.math.bn254.G1JacobianPoint(\n            tachyon.math.bn254.Fq.from_dec_string(\n                '21888242871839275222246405745257275088696311157297823662689037894645226208560'),\n            tachyon.math.bn254.Fq.from_dec_string(\n                '21888242871839275222246405745257275088696311157297823662689037894645226208572'),\n            tachyon.math.bn254.Fq(4)\n        ))\n"
  },
  {
    "path": "tachyon/py/test/tachyon/math/elliptic_curves/short_weierstrass/jacobian_point_unittest.py",
    "content": "from absl.testing import absltest\n\nfrom external.kroma_network_tachyon.tachyon.py import tachyon\n\n\nclass JacobianPointTest(absltest.TestCase):\n\n    @classmethod\n    def setUpClass(cls):\n        tachyon.math.bn254.init()\n\n    def test_constructor(self):\n        p = tachyon.math.bn254.G1JacobianPoint()\n        self.assertTrue(p.x.is_one())\n        self.assertTrue(p.y.is_one())\n        self.assertTrue(p.z.is_zero())\n\n        x = tachyon.math.bn254.Fq.random()\n        y = tachyon.math.bn254.Fq.random()\n        z = tachyon.math.bn254.Fq.random()\n        p2 = tachyon.math.bn254.G1JacobianPoint(x, y, z)\n        self.assertEqual(p2.x, x)\n        self.assertEqual(p2.y, y)\n        self.assertEqual(p2.z, z)\n\n    def test_zero(self):\n        p = tachyon.math.bn254.G1JacobianPoint.zero()\n        self.assertTrue(p.is_zero())\n        self.assertTrue(p.z.is_zero())\n\n    def test_generator(self):\n        p = tachyon.math.bn254.G1JacobianPoint.generator()\n        self.assertTrue(p.is_on_curve())\n        self.assertFalse(p.z.is_zero())\n\n    def test_random(self):\n        p = tachyon.math.bn254.G1JacobianPoint.random()\n        p2 = tachyon.math.bn254.G1JacobianPoint.random()\n        self.assertNotEqual(p, p2)\n\n    def test_eq(self):\n        p = tachyon.math.bn254.G1JacobianPoint.zero()\n        p2 = tachyon.math.bn254.G1JacobianPoint.generator()\n        self.assertTrue(p == p)\n        self.assertTrue(p2 == p2)\n        self.assertFalse(p == p2)\n        self.assertFalse(p2 == p)\n\n    def test_ne(self):\n        p = tachyon.math.bn254.G1JacobianPoint.zero()\n        p2 = tachyon.math.bn254.G1JacobianPoint.generator()\n        self.assertFalse(p != p)\n        self.assertFalse(p2 != p2)\n        self.assertTrue(p != p2)\n        self.assertTrue(p2 != p)\n\n    def test_add(self):\n        p = tachyon.math.bn254.G1JacobianPoint.generator()\n        p2 = tachyon.math.bn254.G1JacobianPoint.generator()\n        expected = tachyon.math.bn254.G1JacobianPoint(\n            tachyon.math.bn254.Fq.from_dec_string(\n                '21888242871839275222246405745257275088696311157297823662689037894645226208560'),\n            tachyon.math.bn254.Fq.from_dec_string(\n                '21888242871839275222246405745257275088696311157297823662689037894645226208572'),\n            tachyon.math.bn254.Fq(4)\n        )\n        self.assertEqual(p + p2, expected)\n        p += p2\n        self.assertEqual(p, expected)\n\n        p = tachyon.math.bn254.G1JacobianPoint.generator()\n        ap2 = tachyon.math.bn254.G1AffinePoint.generator()\n        self.assertEqual(p + ap2, expected)\n        p += ap2\n        self.assertEqual(p, expected)\n\n    def test_sub(self):\n        p = tachyon.math.bn254.G1JacobianPoint.generator()\n        p2 = tachyon.math.bn254.G1JacobianPoint.generator()\n        self.assertTrue((p - p2).is_zero())\n        p -= p2\n        self.assertTrue(p.is_zero())\n\n        p = tachyon.math.bn254.G1JacobianPoint.generator()\n        ap2 = tachyon.math.bn254.G1AffinePoint.generator()\n        self.assertTrue((p - ap2).is_zero())\n        p -= ap2\n        self.assertTrue((p).is_zero())\n\n    def test_scalar_mul(self):\n        p = tachyon.math.bn254.G1JacobianPoint.generator()\n        n = tachyon.math.bn254.Fr(2)\n        expected = tachyon.math.bn254.G1JacobianPoint(\n            tachyon.math.bn254.Fq.from_dec_string(\n                '21888242871839275222246405745257275088696311157297823662689037894645226208560'),\n            tachyon.math.bn254.Fq.from_dec_string(\n                '21888242871839275222246405745257275088696311157297823662689037894645226208572'),\n            tachyon.math.bn254.Fq(4)\n        )\n        self.assertEqual(p * n, expected)\n        self.assertEqual(n * p, expected)\n        p *= n\n        self.assertEqual(p, expected)\n\n    def test_negative(self):\n        p = tachyon.math.bn254.G1JacobianPoint.generator()\n        self.assertEqual(-p, tachyon.math.bn254.G1JacobianPoint(\n            p.x,\n            -p.y,\n            p.z\n        ))\n\n    def test_double(self):\n        p = tachyon.math.bn254.G1JacobianPoint.generator()\n        expected = tachyon.math.bn254.G1JacobianPoint(\n            tachyon.math.bn254.Fq.from_dec_string(\n                '21888242871839275222246405745257275088696311157297823662689037894645226208560'),\n            tachyon.math.bn254.Fq.from_dec_string(\n                '21888242871839275222246405745257275088696311157297823662689037894645226208572'),\n            tachyon.math.bn254.Fq(4)\n        )\n        self.assertEqual(p.double(), expected)\n        p.double_in_place()\n        self.assertEqual(p, expected)\n"
  },
  {
    "path": "tachyon/py/test/tachyon/math/elliptic_curves/short_weierstrass/point_xyzz_unittest.py",
    "content": "from absl.testing import absltest\n\nfrom external.kroma_network_tachyon.tachyon.py import tachyon\n\n\nclass PointXYZZTest(absltest.TestCase):\n\n    @classmethod\n    def setUpClass(cls):\n        tachyon.math.bn254.init()\n\n    def test_constructor(self):\n        p = tachyon.math.bn254.G1PointXYZZ()\n        self.assertTrue(p.x.is_one())\n        self.assertTrue(p.y.is_one())\n        self.assertTrue(p.zz.is_zero())\n        self.assertTrue(p.zzz.is_zero())\n\n        x = tachyon.math.bn254.Fq.random()\n        y = tachyon.math.bn254.Fq.random()\n        zz = tachyon.math.bn254.Fq.random()\n        zzz = tachyon.math.bn254.Fq.random()\n        p2 = tachyon.math.bn254.G1PointXYZZ(x, y, zz, zzz)\n        self.assertEqual(p2.x, x)\n        self.assertEqual(p2.y, y)\n        self.assertEqual(p2.zz, zz)\n        self.assertEqual(p2.zzz, zzz)\n\n    def test_zero(self):\n        p = tachyon.math.bn254.G1PointXYZZ.zero()\n        self.assertTrue(p.is_zero())\n        self.assertTrue(p.zz.is_zero())\n\n    def test_generator(self):\n        p = tachyon.math.bn254.G1PointXYZZ.generator()\n        self.assertTrue(p.is_on_curve())\n        self.assertFalse(p.zz.is_zero())\n\n    def test_random(self):\n        p = tachyon.math.bn254.G1PointXYZZ.random()\n        p2 = tachyon.math.bn254.G1PointXYZZ.random()\n        self.assertNotEqual(p, p2)\n\n    def test_eq(self):\n        p = tachyon.math.bn254.G1PointXYZZ.zero()\n        p2 = tachyon.math.bn254.G1PointXYZZ.generator()\n        self.assertTrue(p == p)\n        self.assertTrue(p2 == p2)\n        self.assertFalse(p == p2)\n        self.assertFalse(p2 == p)\n\n    def test_ne(self):\n        p = tachyon.math.bn254.G1PointXYZZ.zero()\n        p2 = tachyon.math.bn254.G1PointXYZZ.generator()\n        self.assertFalse(p != p)\n        self.assertFalse(p2 != p2)\n        self.assertTrue(p != p2)\n        self.assertTrue(p2 != p)\n\n    def test_add(self):\n        p = tachyon.math.bn254.G1PointXYZZ.generator()\n        p2 = tachyon.math.bn254.G1PointXYZZ.generator()\n        expected = tachyon.math.bn254.G1PointXYZZ(\n            tachyon.math.bn254.Fq.from_dec_string(\n                '21888242871839275222246405745257275088696311157297823662689037894645226208560'),\n            tachyon.math.bn254.Fq.from_dec_string(\n                '21888242871839275222246405745257275088696311157297823662689037894645226208572'),\n            tachyon.math.bn254.Fq(16),\n            tachyon.math.bn254.Fq(64)\n        )\n        self.assertEqual(p + p2, expected)\n        p += p2\n        self.assertEqual(p, expected)\n\n        p = tachyon.math.bn254.G1PointXYZZ.generator()\n        ap2 = tachyon.math.bn254.G1AffinePoint.generator()\n        self.assertEqual(p + ap2, expected)\n        p += ap2\n        self.assertEqual(p, expected)\n\n    def test_sub(self):\n        p = tachyon.math.bn254.G1PointXYZZ.generator()\n        p2 = tachyon.math.bn254.G1PointXYZZ.generator()\n        self.assertTrue((p - p2).is_zero())\n        p -= p2\n        self.assertTrue(p.is_zero())\n\n        p = tachyon.math.bn254.G1PointXYZZ.generator()\n        ap2 = tachyon.math.bn254.G1AffinePoint.generator()\n        self.assertTrue((p - ap2).is_zero())\n        p -= ap2\n        self.assertTrue((p).is_zero())\n\n    def test_scalar_mul(self):\n        p = tachyon.math.bn254.G1PointXYZZ.generator()\n        n = tachyon.math.bn254.Fr(2)\n        expected = tachyon.math.bn254.G1PointXYZZ(\n            tachyon.math.bn254.Fq.from_dec_string(\n                '21888242871839275222246405745257275088696311157297823662689037894645226208560'),\n            tachyon.math.bn254.Fq.from_dec_string(\n                '21888242871839275222246405745257275088696311157297823662689037894645226208572'),\n            tachyon.math.bn254.Fq(16),\n            tachyon.math.bn254.Fq(64)\n        )\n        self.assertEqual(p * n, expected)\n        self.assertEqual(n * p, expected)\n        p *= n\n        self.assertEqual(p, expected)\n\n    def test_negative(self):\n        p = tachyon.math.bn254.G1PointXYZZ.generator()\n        self.assertEqual(-p, tachyon.math.bn254.G1PointXYZZ(\n            p.x,\n            -p.y,\n            p.zz,\n            p.zzz\n        ))\n\n    def test_double(self):\n        p = tachyon.math.bn254.G1PointXYZZ.generator()\n        expected = tachyon.math.bn254.G1PointXYZZ(\n            tachyon.math.bn254.Fq.from_dec_string(\n                '21888242871839275222246405745257275088696311157297823662689037894645226208560'),\n            tachyon.math.bn254.Fq.from_dec_string(\n                '21888242871839275222246405745257275088696311157297823662689037894645226208572'),\n            tachyon.math.bn254.Fq(16),\n            tachyon.math.bn254.Fq(64)\n        )\n        self.assertEqual(p.double(), expected)\n        p.double_in_place()\n        self.assertEqual(p, expected)\n"
  },
  {
    "path": "tachyon/py/test/tachyon/math/elliptic_curves/short_weierstrass/projective_point_unittest.py",
    "content": "from absl.testing import absltest\n\nfrom external.kroma_network_tachyon.tachyon.py import tachyon\n\n\nclass ProjectivePointTest(absltest.TestCase):\n\n    @classmethod\n    def setUpClass(cls):\n        tachyon.math.bn254.init()\n\n    def test_constructor(self):\n        p = tachyon.math.bn254.G1ProjectivePoint()\n        self.assertTrue(p.x.is_one())\n        self.assertTrue(p.y.is_one())\n        self.assertTrue(p.z.is_zero())\n\n        x = tachyon.math.bn254.Fq.random()\n        y = tachyon.math.bn254.Fq.random()\n        z = tachyon.math.bn254.Fq.random()\n        p2 = tachyon.math.bn254.G1ProjectivePoint(x, y, z)\n        self.assertEqual(p2.x, x)\n        self.assertEqual(p2.y, y)\n        self.assertEqual(p2.z, z)\n\n    def test_zero(self):\n        p = tachyon.math.bn254.G1ProjectivePoint.zero()\n        self.assertTrue(p.is_zero())\n        self.assertTrue(p.z.is_zero())\n\n    def test_generator(self):\n        p = tachyon.math.bn254.G1ProjectivePoint.generator()\n        self.assertTrue(p.is_on_curve())\n        self.assertFalse(p.z.is_zero())\n\n    def test_random(self):\n        p = tachyon.math.bn254.G1ProjectivePoint.random()\n        p2 = tachyon.math.bn254.G1ProjectivePoint.random()\n        self.assertNotEqual(p, p2)\n\n    def test_eq(self):\n        p = tachyon.math.bn254.G1ProjectivePoint.zero()\n        p2 = tachyon.math.bn254.G1ProjectivePoint.generator()\n        self.assertTrue(p == p)\n        self.assertTrue(p2 == p2)\n        self.assertFalse(p == p2)\n        self.assertFalse(p2 == p)\n\n    def test_ne(self):\n        p = tachyon.math.bn254.G1ProjectivePoint.zero()\n        p2 = tachyon.math.bn254.G1ProjectivePoint.generator()\n        self.assertFalse(p != p)\n        self.assertFalse(p2 != p2)\n        self.assertTrue(p != p2)\n        self.assertTrue(p2 != p)\n\n    def test_add(self):\n        p = tachyon.math.bn254.G1ProjectivePoint.generator()\n        p2 = tachyon.math.bn254.G1ProjectivePoint.generator()\n        expected = tachyon.math.bn254.G1ProjectivePoint(\n            tachyon.math.bn254.Fq.from_dec_string(\n                '21888242871839275222246405745257275088696311157297823662689037894645226208491'),\n            tachyon.math.bn254.Fq.from_dec_string(\n                '21888242871839275222246405745257275088696311157297823662689037894645226208572'),\n            tachyon.math.bn254.Fq(64)\n        )\n        self.assertEqual(p + p2, expected)\n        p += p2\n        self.assertEqual(p, expected)\n\n        p = tachyon.math.bn254.G1ProjectivePoint.generator()\n        ap2 = tachyon.math.bn254.G1AffinePoint.generator()\n        self.assertEqual(p + ap2, expected)\n        p += ap2\n        self.assertEqual(p, expected)\n\n    def test_sub(self):\n        p = tachyon.math.bn254.G1ProjectivePoint.generator()\n        p2 = tachyon.math.bn254.G1ProjectivePoint.generator()\n        self.assertTrue((p - p2).is_zero())\n        p -= p2\n        self.assertTrue(p.is_zero())\n\n        p = tachyon.math.bn254.G1ProjectivePoint.generator()\n        ap2 = tachyon.math.bn254.G1AffinePoint.generator()\n        self.assertTrue((p - ap2).is_zero())\n        p -= ap2\n        self.assertTrue((p).is_zero())\n\n    def test_scalar_mul(self):\n        p = tachyon.math.bn254.G1ProjectivePoint.generator()\n        n = tachyon.math.bn254.Fr(2)\n        expected = tachyon.math.bn254.G1ProjectivePoint(\n            tachyon.math.bn254.Fq.from_dec_string(\n                '21888242871839275222246405745257275088696311157297823662689037894645226208491'),\n            tachyon.math.bn254.Fq.from_dec_string(\n                '21888242871839275222246405745257275088696311157297823662689037894645226208572'),\n            tachyon.math.bn254.Fq(64)\n        )\n        self.assertEqual(p * n, expected)\n        self.assertEqual(n * p, expected)\n        p *= n\n        self.assertEqual(p, expected)\n\n    def test_negative(self):\n        p = tachyon.math.bn254.G1ProjectivePoint.generator()\n        self.assertEqual(-p, tachyon.math.bn254.G1ProjectivePoint(\n            p.x,\n            -p.y,\n            p.z\n        ))\n\n    def test_double(self):\n        p = tachyon.math.bn254.G1ProjectivePoint.generator()\n        expected = tachyon.math.bn254.G1ProjectivePoint(\n            tachyon.math.bn254.Fq.from_dec_string(\n                '21888242871839275222246405745257275088696311157297823662689037894645226208491'),\n            tachyon.math.bn254.Fq.from_dec_string(\n                '21888242871839275222246405745257275088696311157297823662689037894645226208572'),\n            tachyon.math.bn254.Fq(64)\n        )\n        self.assertEqual(p.double(), expected)\n        p.double_in_place()\n        self.assertEqual(p, expected)\n"
  },
  {
    "path": "tachyon/py/test/tachyon/math/elliptic_curves/short_weierstrass/short_weierstrass_unittests.py",
    "content": "from absl.testing import absltest\n\nfrom affine_point_unittest import *\nfrom point_xyzz_unittest import *\nfrom projective_point_unittest import *\nfrom jacobian_point_unittest import *\n\nif __name__ == '__main__':\n    absltest.main()\n"
  },
  {
    "path": "tachyon/py/test/tachyon/math/finite_fields/BUILD.bazel",
    "content": "load(\"@pip_deps//:requirements.bzl\", \"requirement\")\nload(\"//bazel:tachyon_py.bzl\", \"tachyon_py_unittest\")\n\ntachyon_py_unittest(\n    name = \"finite_fields_unittests\",\n    srcs = [\n        \"finite_fields_unittests.py\",\n        \"prime_field_unittest.py\",\n    ],\n    data = [\"@kroma_network_tachyon//tachyon/py:tachyon.so\"],\n    main = \"finite_fields_unittests.py\",\n    deps = [\n        requirement(\"absl-py\"),\n    ],\n)\n"
  },
  {
    "path": "tachyon/py/test/tachyon/math/finite_fields/finite_fields_unittests.py",
    "content": "from absl.testing import absltest\n\nfrom prime_field_unittest import *\n\nif __name__ == '__main__':\n    absltest.main()\n"
  },
  {
    "path": "tachyon/py/test/tachyon/math/finite_fields/prime_field_unittest.py",
    "content": "from absl.testing import absltest\n\nfrom external.kroma_network_tachyon.tachyon.py import tachyon\n\n\nclass PrimeFieldTest(absltest.TestCase):\n\n    @classmethod\n    def setUpClass(cls):\n        tachyon.math.bn254.init()\n\n    def test_zero(self):\n        f = tachyon.math.bn254.Fq.zero()\n        self.assertTrue(f.is_zero())\n        self.assertFalse(f.is_one())\n\n    def test_one(self):\n        f = tachyon.math.bn254.Fq.one()\n        self.assertFalse(f.is_zero())\n        self.assertTrue(f.is_one())\n\n    def test_random(self):\n        f = tachyon.math.bn254.Fq.random()\n        f2 = tachyon.math.bn254.Fq.random()\n        self.assertNotEqual(f, f2)\n\n    def test_from_dec_string(self):\n        f = tachyon.math.bn254.Fq.from_dec_string('12345678910')\n        self.assertEqual(f.to_string(), '12345678910')\n        self.assertEqual(f.to_hex_string(), '0x2dfdc1c3e')\n\n    def test_from_hex_string(self):\n        f = tachyon.math.bn254.Fq.from_hex_string('0x2dfdc1c3e')\n        self.assertEqual(f.to_string(), '12345678910')\n        self.assertEqual(f.to_hex_string(), '0x2dfdc1c3e')\n\n    def test_eq(self):\n        f = tachyon.math.bn254.Fq.zero()\n        f2 = tachyon.math.bn254.Fq.one()\n        self.assertTrue(f == f)\n        self.assertTrue(f2 == f2)\n        self.assertFalse(f == f2)\n        self.assertFalse(f2 == f)\n\n    def test_ne(self):\n        f = tachyon.math.bn254.Fq.zero()\n        f2 = tachyon.math.bn254.Fq.one()\n        self.assertFalse(f != f)\n        self.assertFalse(f2 != f2)\n        self.assertTrue(f != f2)\n        self.assertTrue(f2 != f)\n\n    def test_lt(self):\n        f = tachyon.math.bn254.Fq.zero()\n        f2 = tachyon.math.bn254.Fq.one()\n        self.assertFalse(f < f)\n        self.assertFalse(f2 < f2)\n        self.assertTrue(f < f2)\n        self.assertFalse(f2 < f)\n\n    def test_le(self):\n        f = tachyon.math.bn254.Fq.zero()\n        f2 = tachyon.math.bn254.Fq.one()\n        self.assertTrue(f <= f)\n        self.assertTrue(f2 <= f2)\n        self.assertTrue(f <= f2)\n        self.assertFalse(f2 <= f)\n\n    def test_gt(self):\n        f = tachyon.math.bn254.Fq.zero()\n        f2 = tachyon.math.bn254.Fq.one()\n        self.assertFalse(f > f)\n        self.assertFalse(f2 > f2)\n        self.assertFalse(f > f2)\n        self.assertTrue(f2 > f)\n\n    def test_ge(self):\n        f = tachyon.math.bn254.Fq.zero()\n        f2 = tachyon.math.bn254.Fq.one()\n        self.assertTrue(f >= f)\n        self.assertTrue(f2 >= f2)\n        self.assertFalse(f >= f2)\n        self.assertTrue(f2 >= f)\n\n    def test_ge(self):\n        f = tachyon.math.bn254.Fq.zero()\n        f2 = tachyon.math.bn254.Fq.one()\n        self.assertTrue(f >= f)\n        self.assertTrue(f2 >= f2)\n        self.assertFalse(f >= f2)\n        self.assertTrue(f2 >= f)\n\n    def test_add(self):\n        p = tachyon.math.bn254.Fq(4)\n        p2 = tachyon.math.bn254.Fq(3)\n        expected = tachyon.math.bn254.Fq(7)\n        self.assertEqual(p + p2, expected)\n        p += p2\n        self.assertEqual(p, expected)\n\n    def test_sub(self):\n        p = tachyon.math.bn254.Fq(4)\n        p2 = tachyon.math.bn254.Fq(3)\n        expected = tachyon.math.bn254.Fq(1)\n        self.assertEqual(p - p2, expected)\n        p -= p2\n        self.assertEqual(p, expected)\n\n    def test_mul(self):\n        p = tachyon.math.bn254.Fq(4)\n        p2 = tachyon.math.bn254.Fq(3)\n        expected = tachyon.math.bn254.Fq(12)\n        self.assertEqual(p * p2, expected)\n        p *= p2\n        self.assertEqual(p, expected)\n\n    def test_div(self):\n        p = tachyon.math.bn254.Fq(4)\n        p2 = tachyon.math.bn254.Fq(3)\n        expected = tachyon.math.bn254.Fq.from_dec_string(\n            '14592161914559516814830937163504850059130874104865215775126025263096817472390')\n        self.assertEqual(p / p2, expected)\n        p /= p2\n        self.assertEqual(p, expected)\n\n    def test_negative(self):\n        f = tachyon.math.bn254.Fq(4)\n        self.assertEqual(-f, tachyon.math.bn254.Fq.from_dec_string(\n            '21888242871839275222246405745257275088696311157297823662689037894645226208579'))\n\n    def test_double(self):\n        f = tachyon.math.bn254.Fq(4)\n        expected = tachyon.math.bn254.Fq(8)\n        self.assertEqual(f.double(), expected)\n        f.double_in_place()\n        self.assertEqual(f, expected)\n\n    def test_square(self):\n        f = tachyon.math.bn254.Fq(4)\n        expected = tachyon.math.bn254.Fq(16)\n        self.assertEqual(f.square(), expected)\n        f.square_in_place()\n        self.assertEqual(f, expected)\n"
  },
  {
    "path": "tachyon/rs/BUILD.bazel",
    "content": "load(\"@crate_index//:defs.bzl\", \"aliases\", \"all_crate_deps\")\nload(\"//bazel:tachyon_rust.bzl\", \"tachyon_rust_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_rust_library(\n    name = \"tachyon_rs\",\n    srcs = glob([\"src/**/*.rs\"]),\n    aliases = aliases(),\n    proc_macro_deps = all_crate_deps(proc_macro = True),\n    deps = all_crate_deps(normal = True),\n)\n"
  },
  {
    "path": "tachyon/rs/Cargo.toml",
    "content": "[package]\nname = \"tachyon_rs\"\nversion = \"0.0.1\"\nauthors = [\"The Tachyon Authors <tachyon-discuss@kroma.network>\"]\nedition = \"2021\"\nrust-version = \"1.56.1\"\ndescription = \"\"\"\nTachyon is a blazing fast Zero-Knowledge Proof (ZKP) library developed in C++.\n\"\"\"\nlicense = \"MIT OR Apache-2.0\"\nrepository = \"https://github.com/kroma-network/tachyon\"\nreadme = \"README.md\"\ncategories = [\"cryptography\"]\nkeywords = [\"tachyon\", \"proofs\", \"zkp\", \"zkSNARKs\"]\npublish = false\n\n[dependencies]\nzeroize = { version = \"1\", default-features = false, features = [\n  \"zeroize_derive\",\n] }\n"
  },
  {
    "path": "tachyon/rs/base/BUILD.bazel",
    "content": "load(\"@bazel_skylib//rules:common_settings.bzl\", \"bool_flag\")\nload(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\n# We need to change the default value if we change the default rustc version.\nbool_flag(\n    name = \"rustc_version_ge_1.67.0\",\n    build_setting_default = True,\n)\n\nconfig_setting(\n    name = \"rustc_version_ge_1.67.0_is_true\",\n    flag_values = {\"rustc_version_ge_1.67.0\": \"true\"},\n)\n\ntachyon_cc_library(\n    name = \"container_util\",\n    hdrs = [\"container_util.h\"],\n    deps = [\n        \"//tachyon/base:template_util\",\n        \"//tachyon/base/functional:functor_traits\",\n        \"@com_google_absl//absl/types:span\",\n        \"@cxx.rs//:core\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"rust_vec\",\n    hdrs = [\"rust_vec.h\"],\n    defines = select({\n        \":rustc_version_ge_1.67.0_is_true\": [\"RUSTC_VERSION_GE_1_67_0\"],\n        \"//conditions:default\": [],\n    }),\n    deps = [\n        \"//tachyon:export\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base/buffer:copyable\",\n        \"//tachyon/base/strings:string_number_conversions\",\n        \"@com_google_absl//absl/strings\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/rs/base/container_util.h",
    "content": "#ifndef TACHYON_RS_BASE_CONTAINER_UTIL_H_\n#define TACHYON_RS_BASE_CONTAINER_UTIL_H_\n\n#include \"absl/types/span.h\"\n#include \"rust/cxx.h\"\n\n#include \"tachyon/base/functional/functor_traits.h\"\n#include \"tachyon/base/template_util.h\"\n\nnamespace tachyon::rs {\n\ntemplate <typename Container, typename T = base::container_value_t<Container>>\nrust::Vec<T> ConvertCppContainerToRustVec(const Container& container) {\n  rust::Vec<T> ret;\n  ret.reserve(std::size(container));\n  for (const T& elem : container) {\n    ret.push_back(elem);\n  }\n  return ret;\n}\n\ntemplate <typename Container, typename UnaryOp,\n          typename FunctorTraits = base::internal::MakeFunctorTraits<UnaryOp>,\n          typename ReturnType = typename FunctorTraits::ReturnType,\n          typename T = base::container_value_t<Container>>\nrust::Vec<ReturnType> ConvertCppContainerToRustVec(const Container& container,\n                                                   UnaryOp&& op) {\n  rust::Vec<ReturnType> ret;\n  ret.reserve(std::size(container));\n  for (const T& elem : container) {\n    ret.push_back(op(elem));\n  }\n  return ret;\n}\n\ntemplate <typename R, typename Container>\nrust::Slice<const R> ConvertCppContainerToRustSlice(\n    const Container& container) {\n  return {reinterpret_cast<const R*>(std::data(container)),\n          std::size(container)};\n}\n\ntemplate <typename R, typename T>\nabsl::Span<R> ConvertRustSliceToCppSpan(rust::Slice<T> slice) {\n  return {reinterpret_cast<R*>(slice.data()), slice.size()};\n}\n\ntemplate <typename T>\nrust::Vec<T> CreateDefaultVector(size_t size) {\n  rust::Vec<T> ret;\n  // NOTE(chokobole): |rust::Vec<T>| doesn't have |resize()|.\n  ret.reserve(size);\n  for (size_t i = 0; i < size; ++i) {\n    ret.push_back(T{});\n  }\n  return ret;\n}\n\n}  // namespace tachyon::rs\n\n#endif  // TACHYON_RS_BASE_CONTAINER_UTIL_H_\n"
  },
  {
    "path": "tachyon/rs/base/rust_vec.h",
    "content": "#ifndef TACHYON_RS_BASE_RUST_VEC_H_\n#define TACHYON_RS_BASE_RUST_VEC_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <string>\n#include <vector>\n\n#include \"absl/strings/substitute.h\"\n#include \"absl/types/span.h\"\n\n#include \"tachyon/base/buffer/copyable.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/strings/string_number_conversions.h\"\n#include \"tachyon/export.h\"\n\nnamespace tachyon {\nnamespace rs {\n\nstruct TACHYON_EXPORT RustVec {\n  uintptr_t ptr;\n  size_t capacity;\n  size_t length;\n\n  template <typename T>\n  absl::Span<T> ToSpan() {\n    return absl::Span(reinterpret_cast<T*>(ptr), length);\n  }\n\n  template <typename T>\n  std::vector<T> ToVec() {\n    return std::vector<T>(reinterpret_cast<T*>(ptr),\n                          reinterpret_cast<T*>(ptr) + length);\n  }\n\n  std::string ToString() const {\n    return absl::Substitute(\"{ptr: $0, capacity: $1, length: $2}\",\n                            base::HexToString(ptr), capacity, length);\n  }\n};\n\n}  // namespace rs\n\nnamespace base {\n\ntemplate <>\nclass Copyable<rs::RustVec> {\n public:\n  static bool WriteTo(const rs::RustVec& rust_vec, Buffer* buffer) {\n    NOTREACHED();\n    return false;\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer, rs::RustVec* rust_vec) {\n    uintptr_t ptr;\n    size_t capacity;\n    size_t length;\n#if defined(RUSTC_VERSION_GE_1_67_0)\n    if (!buffer.ReadMany(&capacity, &ptr, &length)) return false;\n#else\n    if (!buffer.ReadMany(&ptr, &capacity, &length)) return false;\n#endif\n    *rust_vec = {ptr, capacity, length};\n    return true;\n  }\n\n  static size_t EstimateSize(const rs::RustVec& rust_vec) {\n    return sizeof(rust_vec.ptr) + sizeof(rust_vec.capacity) +\n           sizeof(rust_vec.length);\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_RS_BASE_RUST_VEC_H_\n"
  },
  {
    "path": "tachyon/rs/src/lib.rs",
    "content": "pub mod math;\n"
  },
  {
    "path": "tachyon/rs/src/math/base/mod.rs",
    "content": "use zeroize::Zeroize;\n\n#[repr(transparent)]\n#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Zeroize)]\npub struct BigInt<const N: usize>(pub [u64; N]);\n"
  },
  {
    "path": "tachyon/rs/src/math/elliptic_curves/bn/bn254/mod.rs",
    "content": "use crate::math::{\n    elliptic_curves::short_weierstrass::{AffinePoint, JacobianPoint, PointXYZZ, ProjectivePoint},\n    finite_fields::{Fp2, PrimeField},\n    geometry::{Point2, Point3, Point4},\n};\n\npub type Fq = PrimeField<4>;\npub type Fr = PrimeField<4>;\n\npub type G1AffinePoint = AffinePoint<Fq>;\npub type G1JacobianPoint = JacobianPoint<Fq>;\npub type G1ProjectivePoint = ProjectivePoint<Fq>;\npub type G1PointXYZZ = PointXYZZ<Fq>;\n\npub type G1Point2 = Point2<Fq>;\npub type G1Point3 = Point3<Fq>;\npub type G1Point4 = Point4<Fq>;\n\npub type G2AffinePoint = AffinePoint<Fp2<Fq>>;\n"
  },
  {
    "path": "tachyon/rs/src/math/elliptic_curves/bn/mod.rs",
    "content": "pub mod bn254;\n"
  },
  {
    "path": "tachyon/rs/src/math/elliptic_curves/mod.rs",
    "content": "pub mod bn;\npub mod short_weierstrass;\n"
  },
  {
    "path": "tachyon/rs/src/math/elliptic_curves/short_weierstrass/mod.rs",
    "content": "pub use crate::math::finite_fields::PrimeField;\nuse zeroize::Zeroize;\n\n#[repr(C)]\n#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Zeroize)]\npub struct AffinePoint<PrimeField> {\n    pub x: PrimeField,\n    pub y: PrimeField,\n}\n\n#[repr(C)]\n#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Zeroize)]\npub struct JacobianPoint<PrimeField> {\n    pub x: PrimeField,\n    pub y: PrimeField,\n    pub z: PrimeField,\n}\n\n#[repr(C)]\n#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Zeroize)]\npub struct ProjectivePoint<PrimeField> {\n    pub x: PrimeField,\n    pub y: PrimeField,\n    pub z: PrimeField,\n}\n\n#[repr(C)]\n#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Zeroize)]\npub struct PointXYZZ<PrimeField> {\n    pub x: PrimeField,\n    pub y: PrimeField,\n    pub zz: PrimeField,\n    pub zzz: PrimeField,\n}\n"
  },
  {
    "path": "tachyon/rs/src/math/finite_fields/baby_bear/mod.rs",
    "content": "pub struct BabyBear(pub u32);\n"
  },
  {
    "path": "tachyon/rs/src/math/finite_fields/mod.rs",
    "content": "pub mod baby_bear;\n\nuse crate::math::base::BigInt;\nuse zeroize::Zeroize;\n\n#[repr(transparent)]\n#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Zeroize)]\npub struct PrimeField<const N: usize>(pub BigInt<N>);\n\n#[repr(C)]\n#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Zeroize)]\npub struct Fp2<PrimeField> {\n    pub c0: PrimeField,\n    pub c1: PrimeField,\n}\n\n#[repr(C)]\n#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Zeroize)]\npub struct Fp4<PrimeField> {\n    pub c0: PrimeField,\n    pub c1: PrimeField,\n    pub c2: PrimeField,\n    pub c3: PrimeField,\n}\n\n#[repr(C)]\n#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Zeroize)]\npub struct Fp6<Fp2> {\n    pub c0: Fp2,\n    pub c1: Fp2,\n    pub c2: Fp2,\n}\n\n#[repr(C)]\n#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Zeroize)]\npub struct Fp12<Fp6> {\n    pub c0: Fp6,\n    pub c1: Fp6,\n}\n"
  },
  {
    "path": "tachyon/rs/src/math/geometry/mod.rs",
    "content": "use zeroize::Zeroize;\n\n#[repr(C)]\n#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Zeroize)]\npub struct Point2<T> {\n    pub x: T,\n    pub y: T,\n}\n\n#[repr(C)]\n#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Zeroize)]\npub struct Point3<T> {\n    pub x: T,\n    pub y: T,\n    pub z: T,\n}\n\n#[repr(C)]\n#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Zeroize)]\npub struct Point4<T> {\n    pub x: T,\n    pub y: T,\n    pub z: T,\n    pub w: T,\n}\n"
  },
  {
    "path": "tachyon/rs/src/math/mod.rs",
    "content": "pub mod base;\npub mod elliptic_curves;\npub mod finite_fields;\npub mod geometry;\n"
  },
  {
    "path": "tachyon/version.cc",
    "content": "#include \"tachyon/version.h\"\n\nnamespace tachyon {\n\nuint32_t GetRuntimeVersion() { return TACHYON_VERSION; }\n\nstd::string_view GetRuntimeVersionStr() { return TACHYON_VERSION_STR; }\n\nstd::string_view GetRuntimeFullVersionStr() { return TACHYON_VERSION_FULL_STR; }\n\n}  // namespace tachyon\n"
  },
  {
    "path": "tachyon/version.h",
    "content": "#ifndef TACHYON_VERSION_H_\n#define TACHYON_VERSION_H_\n\n#include <stdint.h>\n\n#include <string_view>\n\n#include \"tachyon/export.h\"\n#include \"tachyon/version_generated.h\"\n\nnamespace tachyon {\n\nTACHYON_EXPORT uint32_t GetRuntimeVersion();\n\nTACHYON_EXPORT std::string_view GetRuntimeVersionStr();\n\nTACHYON_EXPORT std::string_view GetRuntimeFullVersionStr();\n\n}  // namespace tachyon\n\n#endif  // TACHYON_VERSION_H_\n"
  },
  {
    "path": "tachyon/version_unittest.cc",
    "content": "#include \"tachyon/version.h\"\n\n#include \"absl/strings/substitute.h\"\n#include \"gtest/gtest.h\"\n\nnamespace tachyon {\n\nTEST(VersionTest, CompileTimeVersionTest) {\n  EXPECT_EQ(absl::Substitute(\"$0.$1.$2\", TACHYON_VERSION_MAJOR,\n                             TACHYON_VERSION_MINOR, TACHYON_VERSION_PATCH),\n            TACHYON_VERSION_STR);\n  EXPECT_EQ(TACHYON_VERSION_MAJOR * 10000 + TACHYON_VERSION_MINOR * 100 +\n                TACHYON_VERSION_PATCH,\n            TACHYON_VERSION);\n}\n\nTEST(VersionTest, RunTimeVersionTest) {\n  EXPECT_EQ(TACHYON_VERSION, GetRuntimeVersion());\n  EXPECT_EQ(TACHYON_VERSION_STR, GetRuntimeVersionStr());\n  EXPECT_EQ(TACHYON_VERSION_FULL_STR, GetRuntimeFullVersionStr());\n}\n\n}  // namespace tachyon\n"
  },
  {
    "path": "tachyon/zk/air/constraint_system/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"constraint_system\",\n    hdrs = [\"constraint_system.h\"],\n    deps = [\n        \":variable\",\n        \"//tachyon/math/matrix:matrix_types\",\n        \"//tachyon/zk/air/expressions:air_evaluator\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"variable\",\n    hdrs = [\"variable.h\"],\n    deps = [\n        \"//tachyon:export\",\n        \"//tachyon/base:logging\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"air_unittests\",\n    srcs = [\"constraint_system_unittest.cc\"],\n    deps = [\n        \":constraint_system\",\n        \"//tachyon/math/finite_fields/test:finite_field_test\",\n        \"//tachyon/math/finite_fields/test:gf7\",\n        \"//tachyon/zk/air/expressions:expression_factory\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/zk/air/constraint_system/constraint_system.h",
    "content": "#ifndef TACHYON_ZK_AIR_CONSTRAINT_SYSTEM_CONSTRAINT_SYSTEM_H_\n#define TACHYON_ZK_AIR_CONSTRAINT_SYSTEM_CONSTRAINT_SYSTEM_H_\n\n#include <stddef.h>\n\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/math/matrix/matrix_types.h\"\n#include \"tachyon/zk/air/constraint_system/variable.h\"\n#include \"tachyon/zk/air/expressions/air_evaluator.h\"\n\nnamespace tachyon::zk::air {\n\ntemplate <typename F>\nclass ConstraintSystem {\n public:\n  using RowMajorMatrix = math::RowMajorMatrix<F>;\n  using ConstMatrixBlock = Eigen::Block<const RowMajorMatrix>;\n\n  ConstraintSystem() = default;\n  ConstraintSystem(size_t num_public_values, size_t main_width,\n                   size_t preprocessed_width)\n      : num_public_values_(num_public_values),\n        main_width_(main_width),\n        preprocessed_width_(preprocessed_width),\n        preprocessed_buf_(std::make_unique<char[]>(sizeof(ConstMatrixBlock))) {}\n\n  void EnforceFirstRowConstraint(std::unique_ptr<Expression<F>> constraint) {\n    EnforceConstraint(ExpressionFactory<F>::FirstRow(std::move(constraint)));\n  }\n\n  void EnforceLastRowConstraint(std::unique_ptr<Expression<F>> constraint) {\n    EnforceConstraint(ExpressionFactory<F>::LastRow(std::move(constraint)));\n  }\n\n  void EnforceTransitionConstraint(std::unique_ptr<Expression<F>> constraint) {\n    EnforceConstraint(ExpressionFactory<F>::Transition(std::move(constraint)));\n  }\n\n  void EnforceConstraint(std::unique_ptr<Expression<F>> constraint) {\n    constraints_.push_back(std::move(constraint));\n  }\n\n  bool IsSatisfied(AirEvaluator<F>& evaluator,\n                   const std::vector<F>& public_values,\n                   const RowMajorMatrix& main,\n                   const RowMajorMatrix* preprocessed = nullptr) const {\n    CHECK_EQ(public_values.size(), num_public_values_);\n    CHECK_EQ(main.cols(), main_width_);\n    CHECK_GE(main.rows(), 1);\n    if (preprocessed) {\n      CHECK_EQ(preprocessed->cols(), preprocessed_width_);\n      CHECK_EQ(preprocessed->rows(), main.rows());\n    } else {\n      CHECK_EQ(preprocessed_width_, 0);\n    }\n\n    evaluator.set_public_values(public_values);\n    evaluator.set_num_rows(main.rows());\n\n    for (Eigen::Index i = 0; i < main.rows() - 1; ++i) {\n      const ConstMatrixBlock main_window(main, i, 0, 2, main_width_);\n\n      if (preprocessed) {\n        new (preprocessed_buf_.get())\n            ConstMatrixBlock(*preprocessed, i, 0, 2, preprocessed_width_);\n        evaluator.SetCurrentWindowData(\n            i, &main_window,\n            reinterpret_cast<const ConstMatrixBlock*>(preprocessed_buf_.get()));\n      } else {\n        evaluator.SetCurrentWindowData(i, &main_window, nullptr);\n      }\n\n      for (const std::unique_ptr<Expression<F>>& constraint : constraints_) {\n        if (!evaluator.Evaluate(constraint.get()).IsZero()) {\n          return false;\n        }\n      }\n    }\n    return true;\n  }\n\n private:\n  const size_t num_public_values_ = 0;\n  const Eigen::Index main_width_ = 0;\n  const Eigen::Index preprocessed_width_ = 0;\n  std::vector<std::unique_ptr<Expression<F>>> constraints_;\n  std::unique_ptr<char[]> preprocessed_buf_;\n};\n\n}  // namespace tachyon::zk::air\n\n#endif  // TACHYON_ZK_AIR_CONSTRAINT_SYSTEM_CONSTRAINT_SYSTEM_H_\n"
  },
  {
    "path": "tachyon/zk/air/constraint_system/constraint_system_unittest.cc",
    "content": "#include \"tachyon/zk/air/constraint_system/constraint_system.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n#include \"tachyon/math/matrix/matrix_types.h\"\n#include \"tachyon/zk/air/expressions/air_evaluator.h\"\n#include \"tachyon/zk/air/expressions/expression_factory.h\"\n\nnamespace tachyon::zk::air {\n\nnamespace {\n\nusing F = math::GF7;\n\nclass ConstraintSystemTest : public math::FiniteFieldTest<F> {};\n\n}  // namespace\n\nTEST_F(ConstraintSystemTest, FibonacciAirTest) {\n  ConstraintSystem<F> constraint_system(3, 2, 0);\n  // Check initial inputs in the first row: public₀ = main₀,₀\n  std::unique_ptr<Expression<F>> expr1 =\n      ExpressionFactory<F>::Variable(Variable::Public(0)) -\n      ExpressionFactory<F>::Variable(Variable::Main(0, 0));\n  constraint_system.EnforceFirstRowConstraint(std::move(expr1));\n\n  // Check initial inputs in the first row: public₁ = main₀,₁\n  std::unique_ptr<Expression<F>> expr2 =\n      ExpressionFactory<F>::Variable(Variable::Public(1)) -\n      ExpressionFactory<F>::Variable(Variable::Main(0, 1));\n  constraint_system.EnforceFirstRowConstraint(std::move(expr2));\n\n  // Check transition step: main₁,₀  = main₀,₁\n  std::unique_ptr<Expression<F>> expr3 =\n      ExpressionFactory<F>::Variable(Variable::Main(1, 0)) -\n      ExpressionFactory<F>::Variable(Variable::Main(0, 1));\n  constraint_system.EnforceTransitionConstraint(std::move(expr3));\n\n  // Check transition step: main₁,₁ = main₀,₀ + main₀,₁\n  std::unique_ptr<Expression<F>> expr4 =\n      ExpressionFactory<F>::Variable(Variable::Main(1, 1)) -\n      ExpressionFactory<F>::Variable(Variable::Main(0, 0)) -\n      ExpressionFactory<F>::Variable(Variable::Main(0, 1));\n  constraint_system.EnforceTransitionConstraint(std::move(expr4));\n\n  // In the last row, check if output is same: main₁,₁ = public₂\n  std::unique_ptr<Expression<F>> expr5 =\n      ExpressionFactory<F>::Variable(Variable::Main(1, 1)) -\n      ExpressionFactory<F>::Variable(Variable::Public(2));\n  constraint_system.EnforceLastRowConstraint(std::move(expr5));\n\n  std::vector<F> public_trace = {F(1), F(1), F(1)};\n  math::RowMajorMatrix<F> main_trace(5, 2);\n\n  // clang-format off\n  main_trace << F(1), F(1),\n                F(1), F(2),\n                F(2), F(3),\n                F(3), F(5),\n                F(5), F(1);\n  // clang-format on\n\n  AirEvaluator<F> evaluator;\n  bool is_satisfied = constraint_system.IsSatisfied(evaluator, public_trace,\n                                                    main_trace, nullptr);\n  EXPECT_TRUE(is_satisfied);\n}\n\nTEST_F(ConstraintSystemTest, PreprocessedAirTest) {\n  ConstraintSystem<F> constraint_system(0, 2, 2);\n\n  std::unique_ptr<Expression<F>> expr1 =\n      ExpressionFactory<F>::Variable(Variable::Preprocessed(0, 0)) *\n      (ExpressionFactory<F>::Variable(Variable::Main(0, 0)) -\n       ExpressionFactory<F>::Variable(Variable::Main(1, 0)));\n  constraint_system.EnforceTransitionConstraint(std::move(expr1));\n\n  std::unique_ptr<Expression<F>> expr2 =\n      ExpressionFactory<F>::Variable(Variable::Preprocessed(0, 1)) *\n      (ExpressionFactory<F>::Variable(Variable::Main(0, 1)) -\n       ExpressionFactory<F>::Variable(Variable::Main(1, 1)));\n  constraint_system.EnforceTransitionConstraint(std::move(expr2));\n\n  std::vector<F> public_trace = {};\n  math::RowMajorMatrix<F> main_trace(4, 2);\n  math::RowMajorMatrix<F> preprocessed_trace(4, 2);\n\n  // clang-format off\n  main_trace << F(5), F(0),\n                F(5), F(4),\n                F(5), F(4),\n                F(0), F(4);\n\n  preprocessed_trace << F(1), F(0),\n                        F(1), F(1),\n                        F(0), F(1),\n                        F(0), F(1);\n  // clang-format on\n\n  AirEvaluator<F> evaluator;\n  bool is_satisfied = constraint_system.IsSatisfied(\n      evaluator, public_trace, main_trace, &preprocessed_trace);\n  EXPECT_TRUE(is_satisfied);\n}\n\n}  // namespace tachyon::zk::air\n"
  },
  {
    "path": "tachyon/zk/air/constraint_system/variable.h",
    "content": "#ifndef TACHYON_ZK_AIR_CONSTRAINT_SYSTEM_VARIABLE_H_\n#define TACHYON_ZK_AIR_CONSTRAINT_SYSTEM_VARIABLE_H_\n\n#include <string>\n\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/export.h\"\n\nnamespace tachyon::zk::air {\n\nclass TACHYON_EXPORT Variable {\n public:\n  enum class Type {\n    kPreprocessed,\n    kMain,\n    kPublic,\n    kPermutation,\n    kChallenge,\n  };\n\n  constexpr static Variable Preprocessed(size_t row_index, size_t col_index) {\n    return Variable(Type::kPreprocessed, row_index, col_index);\n  }\n  constexpr static Variable Main(size_t row_index, size_t col_index) {\n    return Variable(Type::kMain, row_index, col_index);\n  }\n  constexpr static Variable Public(size_t row_index) {\n    return Variable(Type::kPublic, row_index, 0);\n  }\n  constexpr static Variable Permutation(size_t row_index,\n                                        size_t col_index = 0) {\n    return Variable(Type::kPermutation, row_index, col_index);\n  }\n  constexpr static Variable Challenge(size_t row_index, size_t col_index = 0) {\n    return Variable(Type::kChallenge, row_index, col_index);\n  }\n\n  constexpr static std::string_view TypeToString(Type type) {\n    switch (type) {\n      case Type::kPreprocessed:\n        return \"Preprocessed\";\n      case Type::kMain:\n        return \"Main\";\n      case Type::kPublic:\n        return \"Public\";\n      case Type::kPermutation:\n        return \"Permutation\";\n      case Type::kChallenge:\n        return \"Challenge\";\n    }\n    NOTREACHED();\n    return \"\";\n  }\n\n  constexpr Type type() const { return type_; }\n  constexpr size_t row_index() const { return row_index_; }\n  constexpr size_t col_index() const { return col_index_; }\n\n  size_t Degree() const {\n    switch (type_) {\n      case Type::kPreprocessed:\n      case Type::kMain:\n      case Type::kPublic:\n        return 1;\n      case Type::kPermutation:\n      case Type::kChallenge:\n        return 0;\n    }\n    NOTREACHED();\n    return 0;\n  }\n\n  bool operator==(const Variable& other) const {\n    return type_ == other.type_ && row_index_ == other.row_index_ &&\n           col_index_ == other.col_index_;\n  }\n\n  bool operator!=(const Variable& other) const { return !operator==(other); }\n\n  std::string ToString() const {\n    return absl::Substitute(\"type: $0, row_index: $1, col_index: $2\",\n                            TypeToString(type_), row_index_, col_index_);\n  }\n\n private:\n  constexpr Variable(Type type, size_t row_index, size_t col_index)\n      : type_(type), row_index_(row_index), col_index_(col_index) {}\n\n  Type type_;\n\n  // NOTE(batzor): |row_index_| is index within current transition window. It is\n  // not the row index of the whole trace.\n  size_t row_index_;\n  size_t col_index_;\n};\n\n}  // namespace tachyon::zk::air\n\n#endif  // TACHYON_ZK_AIR_CONSTRAINT_SYSTEM_VARIABLE_H_\n"
  },
  {
    "path": "tachyon/zk/air/expressions/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"air_evaluator\",\n    hdrs = [\"air_evaluator.h\"],\n    deps = [\n        \":first_row_expression\",\n        \":last_row_expression\",\n        \":transition_expression\",\n        \":variable_expression\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/math/matrix:matrix_types\",\n        \"//tachyon/zk/expressions:evaluator\",\n        \"//tachyon/zk/expressions:expression\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"expression_factory\",\n    hdrs = [\"expression_factory.h\"],\n    deps = [\n        \":first_row_expression\",\n        \":last_row_expression\",\n        \":transition_expression\",\n        \":variable_expression\",\n        \"//tachyon/zk/expressions:expression_factory\",\n        \"@com_google_absl//absl/memory\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"first_row_expression\",\n    hdrs = [\"first_row_expression.h\"],\n    deps = [\n        \"//tachyon/zk/expressions:expression\",\n        \"@com_google_absl//absl/memory\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"last_row_expression\",\n    hdrs = [\"last_row_expression.h\"],\n    deps = [\n        \"//tachyon/zk/expressions:expression\",\n        \"@com_google_absl//absl/memory\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"transition_expression\",\n    hdrs = [\"transition_expression.h\"],\n    deps = [\n        \"//tachyon/zk/expressions:expression\",\n        \"@com_google_absl//absl/memory\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"variable_expression\",\n    hdrs = [\"variable_expression.h\"],\n    deps = [\n        \"//tachyon/zk/expressions:expression\",\n        \"@com_google_absl//absl/memory\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/zk/air/expressions/air_evaluator.h",
    "content": "#ifndef TACHYON_ZK_AIR_EXPRESSIONS_AIR_EVALUATOR_H_\n#define TACHYON_ZK_AIR_EXPRESSIONS_AIR_EVALUATOR_H_\n\n#include \"absl/types/span.h\"\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/math/matrix/matrix_types.h\"\n#include \"tachyon/zk/air/expressions/first_row_expression.h\"\n#include \"tachyon/zk/air/expressions/last_row_expression.h\"\n#include \"tachyon/zk/air/expressions/transition_expression.h\"\n#include \"tachyon/zk/air/expressions/variable_expression.h\"\n#include \"tachyon/zk/expressions/evaluator.h\"\n#include \"tachyon/zk/expressions/expression.h\"\n\nnamespace tachyon::zk::air {\n\ntemplate <typename F>\nclass AirEvaluator : public Evaluator<F, F> {\n public:\n  using RowMajorMatrix = math::RowMajorMatrix<F>;\n  using ConstMatrixBlock = Eigen::Block<const RowMajorMatrix>;\n\n  F Evaluate(const Expression<F>* input) override {\n    switch (input->type()) {\n      case ExpressionType::kConstant:\n        return input->ToConstant()->value();\n      case ExpressionType::kSum: {\n        const SumExpression<F>* sum = input->ToSum();\n        return Evaluate(sum->left()) + Evaluate(sum->right());\n      }\n      case ExpressionType::kProduct: {\n        const ProductExpression<F>* product = input->ToProduct();\n        return Evaluate(product->left()) * Evaluate(product->right());\n      }\n      case ExpressionType::kNegated: {\n        const NegatedExpression<F>* negated = input->ToNegated();\n        return -Evaluate(negated->expr());\n      }\n      case ExpressionType::kScaled: {\n        const ScaledExpression<F>* scaled = input->ToScaled();\n        return Evaluate(scaled->expr()) * scaled->scale();\n      }\n      case ExpressionType::kFirstRow: {\n        if (current_row_ == 0) {\n          const FirstRowExpression<F>* first_row = input->ToFirstRow();\n          return Evaluate(first_row->expr());\n        }\n        return F::Zero();\n      }\n      case ExpressionType::kLastRow: {\n        if (current_row_ == num_rows_ - 2) {\n          const LastRowExpression<F>* last_row = input->ToLastRow();\n          return Evaluate(last_row->expr());\n        }\n        return F::Zero();\n      }\n      case ExpressionType::kTransition: {\n        const TransitionExpression<F>* transition = input->ToTransition();\n        return Evaluate(transition->expr());\n      }\n      case ExpressionType::kVariable: {\n        Variable variable = input->ToVariable()->variable();\n        switch (variable.type()) {\n          case Variable::Type::kPublic:\n            return public_values_[variable.row_index()];\n          case Variable::Type::kMain:\n            CHECK_NE(main_window_, nullptr);\n            return main_window_->coeff(variable.row_index(),\n                                       variable.col_index());\n          case Variable::Type::kPreprocessed: {\n            CHECK_NE(preprocessed_window_, nullptr);\n            return preprocessed_window_->coeff(variable.row_index(),\n                                               variable.col_index());\n          }\n          case Variable::Type::kChallenge:\n          case Variable::Type::kPermutation:\n            NOTIMPLEMENTED();\n            return F::Zero();\n        }\n      }\n      case ExpressionType::kAdvice:\n      case ExpressionType::kChallenge:\n      case ExpressionType::kFixed:\n      case ExpressionType::kInstance:\n      case ExpressionType::kSelector:\n        NOTREACHED() << ExpressionTypeToString(input->type())\n                     << \" expression is not allowed in AIR!\";\n        return F::Zero();\n    }\n    NOTREACHED();\n    return F::Zero();\n  }\n\n  void set_public_values(absl::Span<const F> public_values) {\n    public_values_ = public_values;\n  }\n\n  void set_num_rows(Eigen::Index num_rows) { num_rows_ = num_rows; }\n\n  void SetCurrentWindowData(Eigen::Index current_row,\n                            const ConstMatrixBlock* main_window,\n                            const ConstMatrixBlock* preprocessed_window) {\n    current_row_ = current_row;\n    main_window_ = main_window;\n    preprocessed_window_ = preprocessed_window;\n  }\n\n private:\n  Eigen::Index num_rows_ = 0;\n  absl::Span<const F> public_values_;\n\n  Eigen::Index current_row_ = 0;\n\n  // not owned\n  const ConstMatrixBlock* main_window_ = nullptr;\n  const ConstMatrixBlock* preprocessed_window_ = nullptr;\n};\n\n}  // namespace tachyon::zk::air\n\n#endif  // TACHYON_ZK_AIR_EXPRESSIONS_AIR_EVALUATOR_H_\n"
  },
  {
    "path": "tachyon/zk/air/expressions/expression_factory.h",
    "content": "#ifndef TACHYON_ZK_AIR_EXPRESSIONS_EXPRESSION_FACTORY_H_\n#define TACHYON_ZK_AIR_EXPRESSIONS_EXPRESSION_FACTORY_H_\n\n#include <memory>\n#include <utility>\n\n#include \"absl/memory/memory.h\"\n\n#include \"tachyon/zk/air/expressions/first_row_expression.h\"\n#include \"tachyon/zk/air/expressions/last_row_expression.h\"\n#include \"tachyon/zk/air/expressions/transition_expression.h\"\n#include \"tachyon/zk/air/expressions/variable_expression.h\"\n#include \"tachyon/zk/expressions/expression_factory.h\"\n\nnamespace tachyon::zk::air {\n\ntemplate <typename F>\nclass ExpressionFactory : public tachyon::zk::ExpressionFactory<F> {\n public:\n  using Expr = Expression<F>;\n\n  static std::unique_ptr<Expr> FirstRow(std::unique_ptr<Expr> expr) {\n    return absl::WrapUnique(new FirstRowExpression<F>(std::move(expr)));\n  }\n\n  static std::unique_ptr<Expr> LastRow(std::unique_ptr<Expr> expr) {\n    return absl::WrapUnique(new LastRowExpression<F>(std::move(expr)));\n  }\n\n  static std::unique_ptr<Expr> Transition(std::unique_ptr<Expr> expr) {\n    return absl::WrapUnique(new TransitionExpression<F>(std::move(expr)));\n  }\n\n  static std::unique_ptr<Expr> Variable(const Variable& variable) {\n    return absl::WrapUnique(new VariableExpression<F>(variable));\n  }\n};\n\n}  // namespace tachyon::zk::air\n\n#endif  // TACHYON_ZK_AIR_EXPRESSIONS_EXPRESSION_FACTORY_H_\n"
  },
  {
    "path": "tachyon/zk/air/expressions/first_row_expression.h",
    "content": "#ifndef TACHYON_ZK_AIR_EXPRESSIONS_FIRST_ROW_EXPRESSION_H_\n#define TACHYON_ZK_AIR_EXPRESSIONS_FIRST_ROW_EXPRESSION_H_\n\n#include <memory>\n#include <string>\n#include <utility>\n\n#include \"absl/memory/memory.h\"\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/zk/expressions/expression.h\"\n\nnamespace tachyon::zk::air {\n\ntemplate <typename F>\nclass ExpressionFactory;\n\ntemplate <typename F>\nclass FirstRowExpression : public Expression<F> {\n public:\n  static std::unique_ptr<FirstRowExpression> CreateForTesting(\n      std::unique_ptr<Expression<F>> expr) {\n    return absl::WrapUnique(new FirstRowExpression(std::move(expr)));\n  }\n\n  Expression<F>* expr() const { return expr_.get(); }\n\n  // Expression methods\n  size_t Degree() const override { return expr_->Degree(); }\n\n  uint64_t Complexity() const override { return expr_->Complexity(); }\n\n  std::unique_ptr<Expression<F>> Clone() const override {\n    return absl::WrapUnique(new FirstRowExpression(expr_->Clone()));\n  }\n\n  std::string ToString() const override {\n    return absl::Substitute(\"{type: $0, expr: $1}\",\n                            ExpressionTypeToString(this->type_),\n                            expr_->ToString());\n  }\n\n  bool operator==(const Expression<F>& other) const override {\n    if (!Expression<F>::operator==(other)) return false;\n    const FirstRowExpression* first_row = other.ToFirstRow();\n    return *expr_ == *first_row->expr_;\n  }\n\n private:\n  friend class ExpressionFactory<F>;\n\n  explicit FirstRowExpression(std::unique_ptr<Expression<F>> expr)\n      : Expression<F>(ExpressionType::kFirstRow), expr_(std::move(expr)) {}\n\n  std::unique_ptr<Expression<F>> expr_;\n};\n\n}  // namespace tachyon::zk::air\n\n#endif  // TACHYON_ZK_AIR_EXPRESSIONS_FIRST_ROW_EXPRESSION_H_\n"
  },
  {
    "path": "tachyon/zk/air/expressions/last_row_expression.h",
    "content": "#ifndef TACHYON_ZK_AIR_EXPRESSIONS_LAST_ROW_EXPRESSION_H_\n#define TACHYON_ZK_AIR_EXPRESSIONS_LAST_ROW_EXPRESSION_H_\n\n#include <memory>\n#include <string>\n#include <utility>\n\n#include \"absl/memory/memory.h\"\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/zk/expressions/expression.h\"\n\nnamespace tachyon::zk::air {\n\ntemplate <typename F>\nclass ExpressionFactory;\n\ntemplate <typename F>\nclass LastRowExpression : public Expression<F> {\n public:\n  static std::unique_ptr<LastRowExpression> CreateForTesting(\n      std::unique_ptr<Expression<F>> expr) {\n    return absl::WrapUnique(new LastRowExpression(std::move(expr)));\n  }\n\n  Expression<F>* expr() const { return expr_.get(); }\n\n  // Expression methods\n  size_t Degree() const override { return expr_->Degree(); }\n\n  uint64_t Complexity() const override { return expr_->Complexity(); }\n\n  std::unique_ptr<Expression<F>> Clone() const override {\n    return absl::WrapUnique(new LastRowExpression(expr_->Clone()));\n  }\n\n  std::string ToString() const override {\n    return absl::Substitute(\"{type: $0, expr: $1}\",\n                            ExpressionTypeToString(this->type_),\n                            expr_->ToString());\n  }\n\n  bool operator==(const Expression<F>& other) const override {\n    if (!Expression<F>::operator==(other)) return false;\n    const LastRowExpression* last_row = other.ToLastRow();\n    return *expr_ == *last_row->expr_;\n  }\n\n private:\n  friend class ExpressionFactory<F>;\n\n  explicit LastRowExpression(std::unique_ptr<Expression<F>> expr)\n      : Expression<F>(ExpressionType::kLastRow), expr_(std::move(expr)) {}\n\n  std::unique_ptr<Expression<F>> expr_;\n};\n\n}  // namespace tachyon::zk::air\n\n#endif  // TACHYON_ZK_AIR_EXPRESSIONS_LAST_ROW_EXPRESSION_H_\n"
  },
  {
    "path": "tachyon/zk/air/expressions/transition_expression.h",
    "content": "#ifndef TACHYON_ZK_AIR_EXPRESSIONS_TRANSITION_EXPRESSION_H_\n#define TACHYON_ZK_AIR_EXPRESSIONS_TRANSITION_EXPRESSION_H_\n\n#include <memory>\n#include <string>\n#include <utility>\n\n#include \"absl/memory/memory.h\"\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/zk/expressions/expression.h\"\n\nnamespace tachyon::zk::air {\n\ntemplate <typename F>\nclass ExpressionFactory;\n\ntemplate <typename F>\nclass TransitionExpression : public Expression<F> {\n public:\n  static std::unique_ptr<TransitionExpression> CreateForTesting(\n      std::unique_ptr<Expression<F>> expr) {\n    return absl::WrapUnique(new TransitionExpression(std::move(expr)));\n  }\n\n  Expression<F>* expr() const { return expr_.get(); }\n\n  // Expression methods\n  size_t Degree() const override { return expr_->Degree(); }\n\n  uint64_t Complexity() const override { return expr_->Complexity(); }\n\n  std::unique_ptr<Expression<F>> Clone() const override {\n    return absl::WrapUnique(new TransitionExpression(expr_->Clone()));\n  }\n\n  std::string ToString() const override {\n    return absl::Substitute(\"{type: $0, expr: $1}\",\n                            ExpressionTypeToString(this->type_),\n                            expr_->ToString());\n  }\n\n  bool operator==(const Expression<F>& other) const override {\n    if (!Expression<F>::operator==(other)) return false;\n    const TransitionExpression* transition = other.ToTransition();\n    return *expr_ == *transition->expr_;\n  }\n\n private:\n  friend class ExpressionFactory<F>;\n\n  explicit TransitionExpression(std::unique_ptr<Expression<F>> expr)\n      : Expression<F>(ExpressionType::kTransition), expr_(std::move(expr)) {}\n\n  std::unique_ptr<Expression<F>> expr_;\n};\n\n}  // namespace tachyon::zk::air\n\n#endif  // TACHYON_ZK_AIR_EXPRESSIONS_TRANSITION_EXPRESSION_H_\n"
  },
  {
    "path": "tachyon/zk/air/expressions/variable_expression.h",
    "content": "#ifndef TACHYON_ZK_AIR_EXPRESSIONS_VARIABLE_EXPRESSION_H_\n#define TACHYON_ZK_AIR_EXPRESSIONS_VARIABLE_EXPRESSION_H_\n\n#include <memory>\n#include <string>\n\n#include \"absl/memory/memory.h\"\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/zk/air/constraint_system/variable.h\"\n#include \"tachyon/zk/expressions/expression.h\"\n\nnamespace tachyon::zk::air {\n\ntemplate <typename F>\nclass ExpressionFactory;\n\ntemplate <typename F>\nclass VariableExpression : public Expression<F> {\n public:\n  static std::unique_ptr<VariableExpression> CreateForTesting(\n      const Variable& variable) {\n    return absl::WrapUnique(new VariableExpression(variable));\n  }\n\n  const Variable& variable() const { return variable_; }\n\n  // Expression methods\n  size_t Degree() const override { return variable_.Degree(); }\n\n  uint64_t Complexity() const override { return 1; }\n\n  std::unique_ptr<Expression<F>> Clone() const override {\n    return absl::WrapUnique(new VariableExpression(variable_));\n  }\n\n  std::string ToString() const override {\n    return absl::Substitute(\"{type: $0, variable: $1}\",\n                            ExpressionTypeToString(this->type_),\n                            variable_.ToString());\n  }\n\n  bool operator==(const Expression<F>& other) const override {\n    if (!Expression<F>::operator==(other)) return false;\n    const VariableExpression* variable = other.ToVariable();\n    return variable_ == variable->variable();\n  }\n\n private:\n  friend class ExpressionFactory<F>;\n\n  explicit VariableExpression(const Variable& variable)\n      : Expression<F>(ExpressionType::kVariable), variable_(variable) {}\n\n  Variable variable_;\n};\n\n}  // namespace tachyon::zk::air\n\n#endif  // TACHYON_ZK_AIR_EXPRESSIONS_VARIABLE_EXPRESSION_H_\n"
  },
  {
    "path": "tachyon/zk/base/BUILD.bazel",
    "content": "load(\n    \"//bazel:tachyon_cc.bzl\",\n    \"tachyon_cc_benchmark\",\n    \"tachyon_cc_library\",\n    \"tachyon_cc_unittest\",\n)\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"blinder\",\n    hdrs = [\"blinder.h\"],\n    deps = [\n        \":random_field_generator_base\",\n        \":row_types\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"blinded_polynomial\",\n    hdrs = [\"blinded_polynomial.h\"],\n    deps = [\"@com_google_absl//absl/strings\"],\n)\n\ntachyon_cc_library(\n    name = \"random_field_generator_base\",\n    hdrs = [\"random_field_generator_base.h\"],\n)\n\ntachyon_cc_library(\n    name = \"rotation\",\n    hdrs = [\"rotation.h\"],\n    deps = [\n        \":row_types\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base/numerics:checked_math\",\n        \"//tachyon/base/strings:string_number_conversions\",\n        \"//tachyon/math/polynomials/univariate:univariate_evaluation_domain\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"row_types\",\n    hdrs = [\"row_types.h\"],\n)\n\ntachyon_cc_library(\n    name = \"univarate_polynomial_commitment_scheme_extension\",\n    hdrs = [\"univarate_polynomial_commitment_scheme_extension.h\"],\n    deps = [\n        \"//tachyon/crypto/commitments:univariate_polynomial_commitment_scheme\",\n        \"//tachyon/math/base:rational_field\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"value\",\n    hdrs = [\"value.h\"],\n    deps = [\"//tachyon/math/base:rational_field\"],\n)\n\ntachyon_cc_unittest(\n    name = \"base_unittests\",\n    srcs = [\n        \"blinder_unittest.cc\",\n        \"rotation_unittest.cc\",\n        \"value_unittest.cc\",\n    ],\n    deps = [\n        \":blinder\",\n        \":rotation\",\n        \":value\",\n        \"//tachyon/base:random\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/math/finite_fields/test:gf7\",\n        \"//tachyon/math/polynomials/univariate:univariate_evaluations\",\n    ],\n)\n\ntachyon_cc_benchmark(\n    name = \"nested_for_loop_openmp_benchmark\",\n    srcs = [\"nested_for_loop_openmp_benchmark.cc\"],\n    deps = [\n        \"//tachyon/base:parallelize\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:fr\",\n    ],\n)\n\ntachyon_cc_benchmark(\n    name = \"parallelize_benchmark\",\n    srcs = [\"parallelize_benchmark.cc\"],\n    deps = [\n        \"//tachyon/base:parallelize\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:fr\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/zk/base/blinded_polynomial.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_BASE_BLINDED_POLYNOMIAL_H_\n#define TACHYON_ZK_BASE_BLINDED_POLYNOMIAL_H_\n\n#include <string>\n#include <utility>\n\n#include \"absl/strings/substitute.h\"\n\nnamespace tachyon::zk {\n\ntemplate <typename Poly, typename Evals>\nclass BlindedPolynomial {\n public:\n  using F = typename Poly::Field;\n\n  BlindedPolynomial() = default;\n  BlindedPolynomial(Evals&& evals, const F& blind)\n      : evals_(std::move(evals)), blind_(blind) {}\n  BlindedPolynomial(Evals&& evals, F&& blind)\n      : evals_(std::move(evals)), blind_(std::move(blind)) {}\n  BlindedPolynomial(Poly&& poly, const F& blind)\n      : poly_(std::move(poly)), blind_(blind) {}\n  BlindedPolynomial(Poly&& poly, F&& blind)\n      : poly_(std::move(poly)), blind_(std::move(blind)) {}\n\n  const Poly& poly() const { return poly_; }\n  const Evals& evals() const { return evals_; }\n  const F& blind() const { return blind_; }\n\n  template <typename Domain>\n  void TransformEvalsToPoly(const Domain* domain) {\n    poly_ = domain->IFFT(std::move(evals_));\n  }\n\n  std::string ToString() const {\n    if (evals_.NumElements() == 0) {\n      return absl::Substitute(\"{poly: $0, blind: $1}\", poly_.ToString(),\n                              blind_.ToString());\n    } else {\n      return absl::Substitute(\"{evals: $0, blind: $1}\", evals_.ToString(),\n                              blind_.ToString());\n    }\n  }\n\n private:\n  Poly poly_;\n  Evals evals_;\n  F blind_;\n};\n\n}  // namespace tachyon::zk\n\n#endif  // TACHYON_ZK_BASE_BLINDED_POLYNOMIAL_H_\n"
  },
  {
    "path": "tachyon/zk/base/blinder.h",
    "content": "#ifndef TACHYON_ZK_BASE_BLINDER_H_\n#define TACHYON_ZK_BASE_BLINDER_H_\n\n#include <stddef.h>\n\n#include \"tachyon/zk/base/random_field_generator_base.h\"\n#include \"tachyon/zk/base/row_types.h\"\n\nnamespace tachyon::zk {\n\ntemplate <typename F>\nclass Blinder {\n public:\n  Blinder(RandomFieldGeneratorBase<F>* random_field_generator,\n          RowIndex blinding_factors)\n      : random_field_generator_(random_field_generator),\n        blinding_factors_(blinding_factors) {}\n\n  const RandomFieldGeneratorBase<F>* random_field_generator() const {\n    return random_field_generator_;\n  }\n  void set_blinding_factors(RowIndex blinding_factors) {\n    blinding_factors_ = blinding_factors;\n  }\n  RowIndex blinding_factors() const { return blinding_factors_; }\n\n  // The number of |blinding_rows| is determined to be either\n  // |blinding_factors_| or |blinding_factors_| + 1, depending on the\n  // |include_last_row| option.\n  // Blinds |evals| at behind by |blinding_rows|.\n  // Returns false if |evals.NumElements()| is less than |blinding_rows|.\n  template <typename Evals>\n  bool Blind(Evals& evals, bool include_last_row = false) {\n    // NOTE(chokobole): It's safe to downcast because domain is already checked.\n    RowIndex size = static_cast<RowIndex>(evals.NumElements());\n    RowIndex blinding_rows = blinding_factors_;\n    if (include_last_row) ++blinding_rows;\n    if (size < blinding_rows) return false;\n    RowIndex start = size - blinding_rows;\n    for (RowIndex i = start; i < size; ++i) {\n      // NOTE(chokobole): Boundary check is the responsibility of API callers.\n      evals.at(i) = random_field_generator_->Generate();\n    }\n    return true;\n  }\n\n  F Generate() { return random_field_generator_->Generate(); }\n\n private:\n  // not owned\n  RandomFieldGeneratorBase<F>* random_field_generator_ = nullptr;\n  RowIndex blinding_factors_ = 0;\n};\n\n}  // namespace tachyon::zk\n\n#endif  // TACHYON_ZK_BASE_BLINDER_H_\n"
  },
  {
    "path": "tachyon/zk/base/blinder_unittest.cc",
    "content": "#include \"tachyon/zk/base/blinder.h\"\n\n#include <utility>\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/random.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluations.h\"\n\nnamespace tachyon::zk {\n\nnamespace {\n\nclass FakeRandomFieldGenerator : public RandomFieldGeneratorBase<math::GF7> {\n public:\n  explicit FakeRandomFieldGenerator(const std::vector<math::GF7>& values)\n      : values_(values) {}\n\n  // RandomFieldGeneratorBase<math::GF7> methods\n  math::GF7 Generate() override {\n    CHECK_LT(idx_, values_.size());\n    return values_[idx_++];\n  }\n\n private:\n  const std::vector<math::GF7>& values_;\n  size_t idx_ = 0;\n};\n\n}  // namespace\n\nTEST(BlinderUnittest, Blind) {\n  constexpr size_t kMaxDegree = 16;\n  constexpr RowIndex kBlindingFactors = 10;\n\n  using Evals = math::UnivariateEvaluations<math::GF7, kMaxDegree>;\n\n  std::vector<math::GF7> blinding_values = base::CreateVector(\n      kBlindingFactors + 1, []() { return math::GF7::Random(); });\n\n  for (size_t i = 0; i < 2; ++i) {\n    bool include_last_row = i == 0;\n\n    FakeRandomFieldGenerator generator(blinding_values);\n    Blinder<math::GF7> blinder(&generator, kBlindingFactors);\n\n    RowIndex blinded_rows = kBlindingFactors;\n    if (include_last_row) ++blinded_rows;\n    std::vector<math::GF7> values = base::CreateVector(\n        blinded_rows - 1, []() { return math::GF7::Random(); });\n    Evals evals(std::move(values));\n    ASSERT_FALSE(blinder.Blind(evals, include_last_row));\n\n    RowIndex rows = kBlindingFactors + 5;\n    values = base::CreateVector(rows, []() { return math::GF7::Random(); });\n    evals = Evals(values);\n    ASSERT_TRUE(blinder.Blind(evals, include_last_row));\n\n    RowIndex not_blinded_rows = rows - blinded_rows;\n    for (RowIndex i = 0; i < rows; ++i) {\n      if (i < not_blinded_rows) {\n        EXPECT_EQ(evals[i], values[i]);\n      } else {\n        EXPECT_EQ(evals[i], blinding_values[i - not_blinded_rows]);\n      }\n    }\n  }\n}\n\n}  // namespace tachyon::zk\n"
  },
  {
    "path": "tachyon/zk/base/commitments/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"gwc_extension\",\n    hdrs = [\"gwc_extension.h\"],\n    deps = [\n        \":univariate_polynomial_commitment_scheme_extension\",\n        \"//tachyon/crypto/commitments/kzg:gwc\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"shplonk_extension\",\n    hdrs = [\"shplonk_extension.h\"],\n    deps = [\n        \":univariate_polynomial_commitment_scheme_extension\",\n        \"//tachyon/crypto/commitments/kzg:shplonk\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"univariate_polynomial_commitment_scheme_extension\",\n    hdrs = [\"univariate_polynomial_commitment_scheme_extension.h\"],\n    deps = [\n        \":univariate_polynomial_commitment_scheme_extension_traits_forward\",\n        \"//tachyon/crypto/commitments:univariate_polynomial_commitment_scheme\",\n        \"//tachyon/math/base:rational_field\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"univariate_polynomial_commitment_scheme_extension_traits_forward\",\n    hdrs = [\"univariate_polynomial_commitment_scheme_extension_traits_forward.h\"],\n)\n"
  },
  {
    "path": "tachyon/zk/base/commitments/gwc_extension.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_BASE_COMMITMENTS_GWC_EXTENSION_H_\n#define TACHYON_ZK_BASE_COMMITMENTS_GWC_EXTENSION_H_\n\n#include <stddef.h>\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/crypto/commitments/batch_commitment_state.h\"\n#include \"tachyon/crypto/commitments/kzg/gwc.h\"\n#include \"tachyon/zk/base/commitments/univariate_polynomial_commitment_scheme_extension.h\"\n\nnamespace tachyon {\nnamespace c::zk::plonk::halo2 {\n\ntemplate <typename PS>\nclass KZGFamilyProverImpl;\n\n}  // namespace c::zk::plonk::halo2\n\nnamespace halo2_api::bn254 {\n\nclass Prover;\n\n}  // namespace halo2_api::bn254\n\nnamespace zk {\n\ntemplate <typename Curve, size_t MaxDegree, size_t MaxExtendedDegree,\n          typename Commitment>\nclass GWCExtension final\n    : public UnivariatePolynomialCommitmentSchemeExtension<\n          GWCExtension<Curve, MaxDegree, MaxExtendedDegree, Commitment>> {\n public:\n  // NOTE(dongchangYoo): The following values are pre-determined according to\n  // the Commitment Opening Scheme.\n  // https://github.com/kroma-network/halo2/blob/7d0a369/halo2_proofs/src/poly/kzg/multiopen/gwc/prover.rs#L35\n  constexpr static bool kQueryInstance = true;\n\n  using Base = UnivariatePolynomialCommitmentSchemeExtension<\n      GWCExtension<Curve, MaxDegree, MaxExtendedDegree, Commitment>>;\n  using G1Point = typename Curve::G1Curve::AffinePoint;\n  using G2Point = typename Curve::G2Curve::AffinePoint;\n  using Field = typename Base::Field;\n  using Poly = typename Base::Poly;\n  using Evals = typename Base::Evals;\n\n  GWCExtension() = default;\n  explicit GWCExtension(crypto::GWC<Curve, MaxDegree, Commitment>&& gwc)\n      : gwc_(std::move(gwc)) {}\n\n  GWCExtension(std::vector<G1Point>&& g1_powers_of_tau,\n               std::vector<G1Point>&& g1_powers_of_tau_lagrange,\n               G2Point&& s_g2) {\n    crypto::KZG<G1Point, MaxDegree, Commitment> kzg(\n        std::move(g1_powers_of_tau), std::move(g1_powers_of_tau_lagrange));\n    gwc_ = crypto::GWC<Curve, MaxDegree, Commitment>(std::move(kzg),\n                                                     std::move(s_g2));\n  }\n\n  const char* Name() { return gwc_.Name(); }\n\n  size_t N() const { return gwc_.N(); }\n\n  size_t D() const { return N() - 1; }\n\n  const G2Point& SG2() const { return gwc_.s_g2(); }\n\n  crypto::BatchCommitmentState& batch_commitment_state() {\n    return gwc_.batch_commitment_state();\n  }\n  bool GetBatchMode() const { return gwc_.GetBatchMode(); }\n\n  void SetBatchMode(size_t batch_count) { gwc_.SetBatchMode(batch_count); }\n\n  std::vector<Commitment> GetBatchCommitments() {\n    return gwc_.GetBatchCommitments();\n  }\n\n  [[nodiscard]] bool DoUnsafeSetup(size_t size) {\n    return gwc_.DoUnsafeSetup(size);\n  }\n\n  [[nodiscard]] bool DoUnsafeSetup(size_t size, const Field& tau) {\n    return gwc_.DoUnsafeSetup(size, tau);\n  }\n\n  template <typename ScalarContainer>\n  [[nodiscard]] bool DoCommit(const ScalarContainer& v, Commitment* out) const {\n    return gwc_.DoCommit(v, out);\n  }\n\n  template <typename ScalarContainer>\n  [[nodiscard]] bool DoCommit(const ScalarContainer& v,\n                              crypto::BatchCommitmentState& state,\n                              size_t index) {\n    return gwc_.DoCommit(v, state, index);\n  }\n\n  [[nodiscard]] bool DoCommit(const Poly& poly, Commitment* out) const {\n    return gwc_.DoCommit(poly, out);\n  }\n\n  [[nodiscard]] bool DoCommit(const Poly& poly,\n                              crypto::BatchCommitmentState& state,\n                              size_t index) {\n    return gwc_.DoCommit(poly, state, index);\n  }\n\n  [[nodiscard]] bool DoCommitLagrange(const Evals& evals,\n                                      Commitment* out) const {\n    return gwc_.DoCommitLagrange(evals, out);\n  }\n\n  [[nodiscard]] bool DoCommitLagrange(const Evals& evals,\n                                      crypto::BatchCommitmentState& state,\n                                      size_t index) {\n    return gwc_.DoCommitLagrange(evals, state, index);\n  }\n\n  template <typename ScalarContainer>\n  [[nodiscard]] bool DoCommitLagrange(const ScalarContainer& v,\n                                      Commitment* out) const {\n    return gwc_.DoCommitLagrange(v, out);\n  }\n\n  template <typename ScalarContainer>\n  [[nodiscard]] bool DoCommitLagrange(const ScalarContainer& v,\n                                      crypto::BatchCommitmentState& state,\n                                      size_t index) {\n    return gwc_.DoCommitLagrange(v, state, index);\n  }\n\n  template <typename Container, typename Proof>\n  [[nodiscard]] bool DoCreateOpeningProof(const Container& poly_openings,\n                                          Proof* proof) {\n    return gwc_.DoCreateOpeningProof(poly_openings, proof);\n  }\n\n  template <typename Container, typename Proof>\n  [[nodiscard]] bool DoVerifyOpeningProof(const Container& poly_openings,\n                                          Proof* proof) const {\n    return gwc_.DoVerifyOpeningProof(poly_openings, proof);\n  }\n\n private:\n  template <typename PS>\n  friend class c::zk::plonk::halo2::KZGFamilyProverImpl;\n  friend class halo2_api::bn254::Prover;\n  friend class base::Copyable<\n      GWCExtension<Curve, MaxDegree, MaxExtendedDegree, Commitment>>;\n\n  const std::vector<G1Point>& GetG1PowersOfTau() const {\n    return this->gwc_.kzg().g1_powers_of_tau();\n  }\n\n  const std::vector<G1Point>& GetG1PowersOfTauLagrange() const {\n    return this->gwc_.kzg().g1_powers_of_tau_lagrange();\n  }\n\n#if TACHYON_CUDA\n  const device::gpu::GpuMemory<G1Point>& GetDeviceG1PowersOfTau() const {\n    return this->gwc_.kzg().d_g1_powers_of_tau();\n  }\n\n  const device::gpu::GpuMemory<G1Point>& GetDeviceG1PowersOfTauLagrange()\n      const {\n    return this->gwc_.kzg().d_g1_powers_of_tau_lagrange();\n  }\n\n  bool UsesGPU() const { return this->gwc_.kzg().UsesGPU(); }\n#endif\n\n  template <typename BaseContainer, typename ScalarContainer,\n            typename OutCommitment>\n  [[nodiscard]] bool DoMSM(const BaseContainer& bases,\n                           const ScalarContainer& scalars,\n                           OutCommitment* out) const {\n    return this->gwc_.kzg().DoMSM(bases, scalars, out);\n  }\n\n  crypto::GWC<Curve, MaxDegree, Commitment> gwc_;\n};\n\ntemplate <typename Curve, size_t MaxDegree, size_t MaxExtendedDegree,\n          typename Commitment>\nstruct UnivariatePolynomialCommitmentSchemeExtensionTraits<\n    GWCExtension<Curve, MaxDegree, MaxExtendedDegree, Commitment>> {\n public:\n  constexpr static size_t kMaxExtendedDegree = MaxExtendedDegree;\n  constexpr static size_t kMaxExtendedSize = kMaxExtendedDegree + 1;\n};\n\n}  // namespace zk\n\nnamespace crypto {\n\ntemplate <typename Curve, size_t MaxDegree, size_t MaxExtendedDegree,\n          typename _Commitment>\nstruct VectorCommitmentSchemeTraits<\n    zk::GWCExtension<Curve, MaxDegree, MaxExtendedDegree, _Commitment>> {\n public:\n  using G1Point = typename Curve::G1Curve::AffinePoint;\n  using Field = typename G1Point::ScalarField;\n  using Commitment = _Commitment;\n\n  constexpr static size_t kMaxSize = MaxDegree + 1;\n  constexpr static bool kIsTransparent = false;\n  constexpr static bool kSupportsBatchMode = true;\n};\n\n}  // namespace crypto\n\nnamespace base {\n\ntemplate <typename Curve, size_t MaxDegree, size_t MaxExtendedDegree,\n          typename Commitment>\nclass Copyable<\n    zk::GWCExtension<Curve, MaxDegree, MaxExtendedDegree, Commitment>> {\n public:\n  using PCS = zk::GWCExtension<Curve, MaxDegree, MaxExtendedDegree, Commitment>;\n\n  static bool WriteTo(const PCS& pcs, Buffer* buffer) {\n    return buffer->WriteMany(pcs.gwc_);\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer, PCS* pcs) {\n    crypto::GWC<Curve, MaxDegree, Commitment> gwc;\n    if (!buffer.ReadMany(&gwc)) {\n      return false;\n    }\n\n    pcs->gwc_ = std::move(gwc);\n    return true;\n  }\n\n  static size_t EstimateSize(const PCS& pcs) {\n    return base::EstimateSize(pcs.gwc_);\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_ZK_BASE_COMMITMENTS_GWC_EXTENSION_H_\n"
  },
  {
    "path": "tachyon/zk/base/commitments/shplonk_extension.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_BASE_COMMITMENTS_SHPLONK_EXTENSION_H_\n#define TACHYON_ZK_BASE_COMMITMENTS_SHPLONK_EXTENSION_H_\n\n#include <stddef.h>\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/crypto/commitments/batch_commitment_state.h\"\n#include \"tachyon/crypto/commitments/kzg/shplonk.h\"\n#include \"tachyon/zk/base/commitments/univariate_polynomial_commitment_scheme_extension.h\"\n\nnamespace tachyon {\nnamespace c::zk::plonk::halo2 {\n\ntemplate <typename PS>\nclass KZGFamilyProverImpl;\n\n}  // namespace c::zk::plonk::halo2\n\nnamespace halo2_api::bn254 {\n\nclass Prover;\n\n}  // namespace halo2_api::bn254\n\nnamespace zk {\n\ntemplate <typename Curve, size_t MaxDegree, size_t MaxExtendedDegree,\n          typename Commitment>\nclass SHPlonkExtension final\n    : public UnivariatePolynomialCommitmentSchemeExtension<\n          SHPlonkExtension<Curve, MaxDegree, MaxExtendedDegree, Commitment>> {\n public:\n  // NOTE(dongchangYoo): The following values are pre-determined according to\n  // the Commitment Opening Scheme.\n  // https://github.com/kroma-network/halo2/blob/7d0a369/halo2_proofs/src/poly/kzg/multiopen/shplonk/prover.rs#L111\n  constexpr static bool kQueryInstance = false;\n\n  using Base = UnivariatePolynomialCommitmentSchemeExtension<\n      SHPlonkExtension<Curve, MaxDegree, MaxExtendedDegree, Commitment>>;\n  using G1Point = typename Curve::G1Curve::AffinePoint;\n  using G2Point = typename Curve::G2Curve::AffinePoint;\n  using Field = typename Base::Field;\n  using Poly = typename Base::Poly;\n  using Evals = typename Base::Evals;\n\n  SHPlonkExtension() = default;\n  explicit SHPlonkExtension(\n      crypto::SHPlonk<Curve, MaxDegree, Commitment>&& shplonk)\n      : shplonk_(std::move(shplonk)) {}\n\n  SHPlonkExtension(std::vector<G1Point>&& g1_powers_of_tau,\n                   std::vector<G1Point>&& g1_powers_of_tau_lagrange,\n                   G2Point&& s_g2) {\n    crypto::KZG<G1Point, MaxDegree, Commitment> kzg(\n        std::move(g1_powers_of_tau), std::move(g1_powers_of_tau_lagrange));\n    shplonk_ = crypto::SHPlonk<Curve, MaxDegree, Commitment>(std::move(kzg),\n                                                             std::move(s_g2));\n  }\n\n  const char* Name() { return shplonk_.Name(); }\n\n  size_t N() const { return shplonk_.N(); }\n\n  size_t D() const { return N() - 1; }\n\n  const G2Point& SG2() const { return shplonk_.s_g2(); }\n\n  crypto::BatchCommitmentState& batch_commitment_state() {\n    return shplonk_.batch_commitment_state();\n  }\n  bool GetBatchMode() const { return shplonk_.GetBatchMode(); }\n\n  void SetBatchMode(size_t batch_count) { shplonk_.SetBatchMode(batch_count); }\n\n  std::vector<Commitment> GetBatchCommitments() {\n    return shplonk_.GetBatchCommitments();\n  }\n\n  [[nodiscard]] bool DoUnsafeSetup(size_t size) {\n    return shplonk_.DoUnsafeSetup(size);\n  }\n\n  [[nodiscard]] bool DoUnsafeSetup(size_t size, const Field& tau) {\n    return shplonk_.DoUnsafeSetup(size, tau);\n  }\n\n  template <typename ScalarContainer>\n  [[nodiscard]] bool DoCommit(const ScalarContainer& v, Commitment* out) const {\n    return shplonk_.DoCommit(v, out);\n  }\n\n  template <typename ScalarContainer>\n  [[nodiscard]] bool DoCommit(const ScalarContainer& v,\n                              crypto::BatchCommitmentState& state,\n                              size_t index) {\n    return shplonk_.DoCommit(v, state, index);\n  }\n\n  [[nodiscard]] bool DoCommit(const Poly& poly, Commitment* out) const {\n    return shplonk_.DoCommit(poly, out);\n  }\n\n  [[nodiscard]] bool DoCommit(const Poly& poly,\n                              crypto::BatchCommitmentState& state,\n                              size_t index) {\n    return shplonk_.DoCommit(poly, state, index);\n  }\n\n  [[nodiscard]] bool DoCommitLagrange(const Evals& evals,\n                                      Commitment* out) const {\n    return shplonk_.DoCommitLagrange(evals, out);\n  }\n\n  [[nodiscard]] bool DoCommitLagrange(const Evals& evals,\n                                      crypto::BatchCommitmentState& state,\n                                      size_t index) {\n    return shplonk_.DoCommitLagrange(evals, state, index);\n  }\n\n  template <typename ScalarContainer>\n  [[nodiscard]] bool DoCommitLagrange(const ScalarContainer& v,\n                                      Commitment* out) const {\n    return shplonk_.DoCommitLagrange(v, out);\n  }\n\n  template <typename ScalarContainer>\n  [[nodiscard]] bool DoCommitLagrange(const ScalarContainer& v,\n                                      crypto::BatchCommitmentState& state,\n                                      size_t index) {\n    return shplonk_.DoCommitLagrange(v, state, index);\n  }\n\n  template <typename Container, typename Proof>\n  [[nodiscard]] bool DoCreateOpeningProof(const Container& poly_openings,\n                                          Proof* proof) {\n    return shplonk_.DoCreateOpeningProof(poly_openings, proof);\n  }\n\n  template <typename Container, typename Proof>\n  [[nodiscard]] bool DoVerifyOpeningProof(const Container& poly_openings,\n                                          Proof* proof) const {\n    return shplonk_.DoVerifyOpeningProof(poly_openings, proof);\n  }\n\n private:\n  template <typename PS>\n  friend class c::zk::plonk::halo2::KZGFamilyProverImpl;\n  friend class halo2_api::bn254::Prover;\n  friend class base::Copyable<\n      SHPlonkExtension<Curve, MaxDegree, MaxExtendedDegree, Commitment>>;\n\n  const std::vector<G1Point>& GetG1PowersOfTau() const {\n    return this->shplonk_.kzg().g1_powers_of_tau();\n  }\n\n  const std::vector<G1Point>& GetG1PowersOfTauLagrange() const {\n    return this->shplonk_.kzg().g1_powers_of_tau_lagrange();\n  }\n\n#if TACHYON_CUDA\n  const device::gpu::GpuMemory<G1Point>& GetDeviceG1PowersOfTau() const {\n    return this->shplonk_.kzg().d_g1_powers_of_tau();\n  }\n\n  const device::gpu::GpuMemory<G1Point>& GetDeviceG1PowersOfTauLagrange()\n      const {\n    return this->shplonk_.kzg().d_g1_powers_of_tau_lagrange();\n  }\n\n  bool UsesGPU() const { return this->shplonk_.kzg().UsesGPU(); }\n#endif\n\n  template <typename BaseContainer, typename ScalarContainer,\n            typename OutCommitment>\n  [[nodiscard]] bool DoMSM(const BaseContainer& bases,\n                           const ScalarContainer& scalars,\n                           OutCommitment* out) const {\n    return this->shplonk_.kzg().DoMSM(bases, scalars, out);\n  }\n\n  crypto::SHPlonk<Curve, MaxDegree, Commitment> shplonk_;\n};\n\ntemplate <typename Curve, size_t MaxDegree, size_t MaxExtendedDegree,\n          typename Commitment>\nstruct UnivariatePolynomialCommitmentSchemeExtensionTraits<\n    SHPlonkExtension<Curve, MaxDegree, MaxExtendedDegree, Commitment>> {\n public:\n  constexpr static size_t kMaxExtendedDegree = MaxExtendedDegree;\n  constexpr static size_t kMaxExtendedSize = kMaxExtendedDegree + 1;\n};\n\n}  // namespace zk\n\nnamespace crypto {\n\ntemplate <typename Curve, size_t MaxDegree, size_t MaxExtendedDegree,\n          typename _Commitment>\nstruct VectorCommitmentSchemeTraits<\n    zk::SHPlonkExtension<Curve, MaxDegree, MaxExtendedDegree, _Commitment>> {\n public:\n  using G1Point = typename Curve::G1Curve::AffinePoint;\n  using Field = typename G1Point::ScalarField;\n  using Commitment = _Commitment;\n\n  constexpr static size_t kMaxSize = MaxDegree + 1;\n  constexpr static bool kIsTransparent = false;\n  constexpr static bool kSupportsBatchMode = true;\n};\n\n}  // namespace crypto\n\nnamespace base {\n\ntemplate <typename Curve, size_t MaxDegree, size_t MaxExtendedDegree,\n          typename Commitment>\nclass Copyable<\n    zk::SHPlonkExtension<Curve, MaxDegree, MaxExtendedDegree, Commitment>> {\n public:\n  using PCS =\n      zk::SHPlonkExtension<Curve, MaxDegree, MaxExtendedDegree, Commitment>;\n\n  static bool WriteTo(const PCS& pcs, Buffer* buffer) {\n    return buffer->WriteMany(pcs.shplonk_);\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer, PCS* pcs) {\n    crypto::SHPlonk<Curve, MaxDegree, Commitment> shplonk;\n    if (!buffer.ReadMany(&shplonk)) {\n      return false;\n    }\n\n    pcs->shplonk_ = std::move(shplonk);\n    return true;\n  }\n\n  static size_t EstimateSize(const PCS& pcs) {\n    return base::EstimateSize(pcs.shplonk_);\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_ZK_BASE_COMMITMENTS_SHPLONK_EXTENSION_H_\n"
  },
  {
    "path": "tachyon/zk/base/commitments/univariate_polynomial_commitment_scheme_extension.h",
    "content": "#ifndef TACHYON_ZK_BASE_COMMITMENTS_UNIVARIATE_POLYNOMIAL_COMMITMENT_SCHEME_EXTENSION_H_\n#define TACHYON_ZK_BASE_COMMITMENTS_UNIVARIATE_POLYNOMIAL_COMMITMENT_SCHEME_EXTENSION_H_\n\n#include <stddef.h>\n\n#include \"tachyon/crypto/commitments/univariate_polynomial_commitment_scheme.h\"\n#include \"tachyon/math/base/rational_field.h\"\n#include \"tachyon/zk/base/commitments/univariate_polynomial_commitment_scheme_extension_traits_forward.h\"\n\nnamespace tachyon::zk {\n\ntemplate <typename Derived>\nclass UnivariatePolynomialCommitmentSchemeExtension\n    : public crypto::UnivariatePolynomialCommitmentScheme<Derived> {\n public:\n  constexpr static size_t kMaxDegree =\n      crypto::UnivariatePolynomialCommitmentScheme<Derived>::kMaxDegree;\n  constexpr static size_t kMaxExtendedDegree =\n      UnivariatePolynomialCommitmentSchemeExtensionTraits<\n          Derived>::kMaxExtendedDegree;\n\n  using Field =\n      typename crypto::UnivariatePolynomialCommitmentScheme<Derived>::Field;\n  using RationalField = math::RationalField<Field>;\n  using RationalPoly =\n      math::UnivariateDensePolynomial<RationalField, kMaxDegree>;\n  using RationalEvals = math::UnivariateEvaluations<RationalField, kMaxDegree>;\n\n  using ExtendedDomain =\n      math::UnivariateEvaluationDomain<Field, kMaxExtendedDegree>;\n  using ExtendedPoly =\n      math::UnivariateDensePolynomial<Field, kMaxExtendedDegree>;\n  using ExtendedEvals = math::UnivariateEvaluations<Field, kMaxExtendedDegree>;\n};\n\n}  // namespace tachyon::zk\n\n#endif  // TACHYON_ZK_BASE_COMMITMENTS_UNIVARIATE_POLYNOMIAL_COMMITMENT_SCHEME_EXTENSION_H_\n"
  },
  {
    "path": "tachyon/zk/base/commitments/univariate_polynomial_commitment_scheme_extension_traits_forward.h",
    "content": "#ifndef TACHYON_ZK_BASE_COMMITMENTS_UNIVARIATE_POLYNOMIAL_COMMITMENT_SCHEME_EXTENSION_TRAITS_FORWARD_H_\n#define TACHYON_ZK_BASE_COMMITMENTS_UNIVARIATE_POLYNOMIAL_COMMITMENT_SCHEME_EXTENSION_TRAITS_FORWARD_H_\n\nnamespace tachyon::zk {\n\ntemplate <typename C>\nstruct UnivariatePolynomialCommitmentSchemeExtensionTraits;\n\n}  // namespace tachyon::zk\n\n#endif  // TACHYON_ZK_BASE_COMMITMENTS_UNIVARIATE_POLYNOMIAL_COMMITMENT_SCHEME_EXTENSION_TRAITS_FORWARD_H_\n"
  },
  {
    "path": "tachyon/zk/base/entities/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"entity\",\n    hdrs = [\"entity.h\"],\n    deps = [\n        \"//tachyon/base:logging\",\n        \"//tachyon/crypto/commitments:vector_commitment_scheme_traits_forward\",\n        \"//tachyon/crypto/transcripts:transcript\",\n        \"//tachyon/math/polynomials/univariate/icicle:icicle_ntt_holder\",\n        \"//tachyon/zk/base:row_types\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"prover_base\",\n    hdrs = [\"prover_base.h\"],\n    deps = [\n        \":entity\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/zk/base:blinded_polynomial\",\n        \"//tachyon/zk/base:blinder\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"verifier_base\",\n    hdrs = [\"verifier_base.h\"],\n    deps = [\":entity\"],\n)\n"
  },
  {
    "path": "tachyon/zk/base/entities/entity.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_BASE_ENTITIES_ENTITY_H_\n#define TACHYON_ZK_BASE_ENTITIES_ENTITY_H_\n\n#include <limits>\n#include <memory>\n#include <utility>\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/crypto/commitments/vector_commitment_scheme_traits_forward.h\"\n#include \"tachyon/crypto/transcripts/transcript.h\"\n#include \"tachyon/zk/base/row_types.h\"\n\n#if TACHYON_CUDA\n#include \"tachyon/math/polynomials/univariate/icicle/icicle_ntt_holder.h\"\n#endif\n\nnamespace tachyon::zk {\n\n// |Entity| class is a parent class of |Prover| and |Verifier|.\n//\n// - If you write codes only for prover, you should use |Prover| class.\n// - If you write codes only for verifier, you should use |Verifier| class.\n// - If you write codes for both prover and verifier, you should use\n//  |Entity| class.\ntemplate <typename _PCS>\nclass Entity {\n public:\n  using PCS = _PCS;\n  using F = typename PCS::Field;\n  using Domain = typename PCS::Domain;\n  using ExtendedDomain = typename PCS::ExtendedDomain;\n  using Evals = typename PCS::Evals;\n  using Poly = typename PCS::Poly;\n  using Commitment = typename PCS::Commitment;\n\n  Entity(PCS&& pcs, std::unique_ptr<crypto::Transcript<Commitment>> transcript)\n      : pcs_(std::move(pcs)), transcript_(std::move(transcript)) {}\n\n  const PCS& pcs() const { return pcs_; }\n  PCS& pcs() { return pcs_; }\n  void set_domain(std::unique_ptr<Domain> domain) {\n    CHECK_LE(domain->size(), size_t{std::numeric_limits<RowIndex>::max()});\n    domain_ = std::move(domain);\n#if TACHYON_CUDA\n    if (icicle_ntt_holder_) {\n      domain_->set_icicle(&icicle_ntt_holder_);\n    }\n#endif\n  }\n  const Domain* domain() const { return domain_.get(); }\n  void set_extended_domain(std::unique_ptr<ExtendedDomain> extended_domain) {\n    extended_domain_ = std::move(extended_domain);\n#if TACHYON_CUDA\n    if (icicle_ntt_holder_) {\n      extended_domain_->set_icicle(&icicle_ntt_holder_);\n    }\n#endif\n  }\n  const ExtendedDomain* extended_domain() const {\n    return extended_domain_.get();\n  }\n  crypto::Transcript<Commitment>* transcript() { return transcript_.get(); }\n  const crypto::Transcript<Commitment>* transcript() const {\n    return transcript_.get();\n  }\n\n#if TACHYON_CUDA\n  math::IcicleNTTHolder<F>&& TakeIcicleNTTHolder() && {\n    return std::move(icicle_ntt_holder_);\n  }\n\n  void set_icicle_ntt_holder(math::IcicleNTTHolder<F>&& icicle_ntt_holder) {\n    CHECK(!icicle_ntt_holder_);\n    icicle_ntt_holder_ = std::move(icicle_ntt_holder);\n  }\n\n  void EnableIcicleNTT() {\n    if (icicle_ntt_holder_) {\n      LOG(WARNING)\n          << \"EnableIcicleNTT() is called more than once. If you see \"\n             \"this log while running unittests, this is intended. The first \"\n             \"call is made in 'tachyon/zk/plonk/halo2/prover_test.h'.\";\n    } else {\n      icicle_ntt_holder_ = math::IcicleNTTHolder<F>::Create();\n      CHECK(icicle_ntt_holder_->Init(extended_domain_->group_gen()));\n      domain_->set_icicle(&icicle_ntt_holder_);\n      extended_domain_->set_icicle(&icicle_ntt_holder_);\n    }\n  }\n#endif\n\n  RowIndex GetUsableRows(RowIndex blinding_factors) const {\n    return domain_->size() - (blinding_factors + 1);\n  }\n\n  constexpr static RowOffset GetLastRow(RowIndex blinding_factors) {\n    return -(blinding_factors + 1);\n  }\n\n  Commitment Commit(const Poly& poly) {\n    Commitment commitment;\n    CHECK(pcs_.Commit(poly, &commitment));\n    return commitment;\n  }\n\n  Commitment Commit(const Evals& evals) {\n    Commitment commitment;\n    CHECK(pcs_.CommitLagrange(evals, &commitment));\n    return commitment;\n  }\n\n  template <typename Container>\n  Commitment Commit(const Container& container) {\n    Commitment commitment;\n    CHECK(pcs_.DoCommit(container, &commitment));\n    return commitment;\n  }\n\n  template <typename T = PCS,\n            std::enable_if_t<crypto::VectorCommitmentSchemeTraits<\n                T>::kSupportsBatchMode>* = nullptr>\n  void BatchCommitAt(const Poly& poly, size_t index) {\n    CHECK(pcs_.Commit(poly, index));\n  }\n\n  template <typename T = PCS,\n            std::enable_if_t<crypto::VectorCommitmentSchemeTraits<\n                T>::kSupportsBatchMode>* = nullptr>\n  void BatchCommitAt(const Evals& evals, size_t index) {\n    CHECK(pcs_.CommitLagrange(evals, index));\n  }\n\n  template <typename T = PCS, typename Container,\n            std::enable_if_t<crypto::VectorCommitmentSchemeTraits<\n                T>::kSupportsBatchMode>* = nullptr>\n  void BatchCommitAt(const Container& container, size_t index) {\n    CHECK(pcs_.DoCommit(container, pcs_.batch_commitment_state(), index));\n  }\n\n protected:\n  PCS pcs_;\n  std::unique_ptr<Domain> domain_;\n  std::unique_ptr<ExtendedDomain> extended_domain_;\n  std::unique_ptr<crypto::Transcript<Commitment>> transcript_;\n#if TACHYON_CUDA\n  math::IcicleNTTHolder<F> icicle_ntt_holder_;\n#endif\n};\n\n}  // namespace tachyon::zk\n\n#endif  // TACHYON_ZK_BASE_ENTITIES_ENTITY_H_\n"
  },
  {
    "path": "tachyon/zk/base/entities/prover_base.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_BASE_ENTITIES_PROVER_BASE_H_\n#define TACHYON_ZK_BASE_ENTITIES_PROVER_BASE_H_\n\n#include <stddef.h>\n\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/zk/base/blinded_polynomial.h\"\n#include \"tachyon/zk/base/blinder.h\"\n#include \"tachyon/zk/base/entities/entity.h\"\n\nnamespace tachyon::zk {\n\ntemplate <typename PCS>\nclass ProverBase : public Entity<PCS> {\n public:\n  using F = typename PCS::Field;\n  using Evals = typename PCS::Evals;\n  using Poly = typename PCS::Poly;\n  using Commitment = typename PCS::Commitment;\n\n  ProverBase(PCS&& pcs,\n             std::unique_ptr<crypto::TranscriptWriter<Commitment>> writer,\n             Blinder<F>&& blinder)\n      : Entity<PCS>(std::move(pcs), std::move(writer)),\n        blinder_(std::move(blinder)) {}\n\n  Blinder<F>& blinder() { return blinder_; }\n\n  crypto::TranscriptWriter<Commitment>* GetWriter() {\n    return this->transcript()->ToWriter();\n  }\n  const crypto::TranscriptWriter<Commitment>* GetWriter() const {\n    return this->transcript()->ToWriter();\n  }\n\n  RowIndex GetUsableRows() const {\n    return Entity<PCS>::GetUsableRows(blinder_.blinding_factors());\n  }\n\n  RowOffset GetLastRow() const {\n    return Entity<PCS>::GetLastRow(blinder_.blinding_factors());\n  }\n\n  void CommitAndWriteToTranscript(const Poly& poly) {\n    Commitment commitment = this->Commit(poly);\n    CHECK(GetWriter()->WriteToTranscript(commitment));\n  }\n\n  void CommitAndWriteToProof(const Poly& poly) {\n    Commitment commitment = this->Commit(poly);\n    CHECK(GetWriter()->WriteToProof(commitment));\n  }\n\n  void CommitAndWriteToTranscript(const Evals& evals) {\n    Commitment commitment = this->Commit(evals);\n    CHECK(GetWriter()->WriteToTranscript(commitment));\n  }\n\n  void CommitAndWriteToProof(const Evals& evals) {\n    Commitment commitment = this->Commit(evals);\n    CHECK(GetWriter()->WriteToProof(commitment));\n  }\n\n  template <typename Container>\n  void CommitAndWriteToTranscript(const Container& container) {\n    Commitment commitment = this->Commit(container);\n    CHECK(GetWriter()->WriteToTranscript(commitment));\n  }\n\n  template <typename Container>\n  void CommitAndWriteToProof(const Container& container) {\n    Commitment commitment = this->Commit(container);\n    CHECK(GetWriter()->WriteToProof(commitment));\n  }\n\n  void EvaluateAndWriteToProof(const Poly& poly, const F& x) {\n    F result = poly.Evaluate(x);\n    CHECK(GetWriter()->WriteToProof(result));\n  }\n\n  template <typename T = PCS,\n            std::enable_if_t<crypto::VectorCommitmentSchemeTraits<\n                T>::kSupportsBatchMode>* = nullptr>\n  void RetrieveAndWriteBatchCommitmentsToProof() {\n    std::vector<Commitment> commitments = this->pcs_.GetBatchCommitments();\n    for (const Commitment& commitment : commitments) {\n      CHECK(GetWriter()->WriteToProof(commitment));\n    }\n  }\n\n  template <typename T = PCS,\n            std::enable_if_t<crypto::VectorCommitmentSchemeTraits<\n                T>::kSupportsBatchMode>* = nullptr>\n  void RetrieveAndWriteBatchCommitmentsToTranscript() {\n    std::vector<Commitment> commitments = this->pcs_.GetBatchCommitments();\n    for (const Commitment& commitment : commitments) {\n      CHECK(GetWriter()->WriteToTranscript(commitment));\n    }\n  }\n\n protected:\n  Blinder<F> blinder_;\n};\n\n}  // namespace tachyon::zk\n\n#endif  // TACHYON_ZK_BASE_ENTITIES_PROVER_BASE_H_\n"
  },
  {
    "path": "tachyon/zk/base/entities/verifier_base.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_BASE_ENTITIES_VERIFIER_BASE_H_\n#define TACHYON_ZK_BASE_ENTITIES_VERIFIER_BASE_H_\n\n#include <memory>\n#include <utility>\n\n#include \"tachyon/zk/base/entities/entity.h\"\n\nnamespace tachyon::zk {\n\ntemplate <typename PCS>\nclass VerifierBase : public Entity<PCS> {\n public:\n  using Commitment = typename PCS::Commitment;\n\n  VerifierBase(PCS&& pcs,\n               std::unique_ptr<crypto::TranscriptReader<Commitment>> transcript)\n      : Entity<PCS>(std::move(pcs), std::move(transcript)) {}\n\n  crypto::TranscriptReader<Commitment>* GetReader() {\n    return this->transcript()->ToReader();\n  }\n};\n\n}  // namespace tachyon::zk\n\n#endif  // TACHYON_ZK_BASE_ENTITIES_VERIFIER_BASE_H_\n"
  },
  {
    "path": "tachyon/zk/base/nested_for_loop_openmp_benchmark.cc",
    "content": "#include \"benchmark/benchmark.h\"\n\n#include \"tachyon/base/parallelize.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/fr.h\"\n\nnamespace tachyon::zk {\n\ntemplate <typename F>\nvoid BM_NestedForLoopParallelCols(benchmark::State& state) {\n  size_t cols = state.range(0);\n  size_t rows = state.range(1);\n  std::vector<std::vector<F>> table =\n      base::CreateVectorParallel(cols, [rows]() {\n        return base::CreateVector(rows, []() { return F::Random(); });\n      });\n  for (auto _ : state) {\n    OMP_PARALLEL_FOR(size_t i = 0; i < cols; ++i) {\n      for (size_t j = 0; j < rows; ++j) {\n        table[i][j].DoubleInPlace();\n      }\n    }\n  }\n  benchmark::DoNotOptimize(table);\n}\n\ntemplate <typename F>\nvoid BM_NestedForLoopParallelRows(benchmark::State& state) {\n  size_t cols = state.range(0);\n  size_t rows = state.range(1);\n  std::vector<std::vector<F>> table =\n      base::CreateVectorParallel(cols, [rows]() {\n        return base::CreateVector(rows, []() { return F::Random(); });\n      });\n  for (auto _ : state) {\n    for (size_t i = 0; i < cols; ++i) {\n      OMP_PARALLEL_FOR(size_t j = 0; j < rows; ++j) {\n        table[i][j].DoubleInPlace();\n      }\n    }\n  }\n  benchmark::DoNotOptimize(table);\n}\n\ntemplate <typename F>\nvoid BM_NestedForLoopParallelCollapse(benchmark::State& state) {\n  size_t cols = state.range(0);\n  size_t rows = state.range(1);\n  std::vector<std::vector<F>> table =\n      base::CreateVectorParallel(cols, [rows]() {\n        return base::CreateVector(rows, []() { return F::Random(); });\n      });\n  for (auto _ : state) {\n    OMP_PARALLEL_NESTED_FOR(size_t i = 0; i < cols; ++i) {\n      for (size_t j = 0; j < rows; ++j) {\n        table[i][j].DoubleInPlace();\n      }\n    }\n  }\n  benchmark::DoNotOptimize(table);\n}\n\nBENCHMARK_TEMPLATE(BM_NestedForLoopParallelCols, math::bn254::Fr)\n    ->ArgsProduct({benchmark::CreateDenseRange(500, 1000, /*step=*/100),\n                   benchmark::CreateRange(1 << 10, 1 << 15, /*multi=*/2)});\n\nBENCHMARK_TEMPLATE(BM_NestedForLoopParallelRows, math::bn254::Fr)\n    ->ArgsProduct({benchmark::CreateDenseRange(500, 1000, /*step=*/100),\n                   benchmark::CreateRange(1 << 10, 1 << 15, /*multi=*/2)});\n\nBENCHMARK_TEMPLATE(BM_NestedForLoopParallelCollapse, math::bn254::Fr)\n    ->ArgsProduct({benchmark::CreateDenseRange(500, 1000, /*step=*/100),\n                   benchmark::CreateRange(1 << 10, 1 << 15, /*multi=*/2)});\n\n}  // namespace tachyon::zk\n\n// clang-format off\n// Executing tests from //tachyon/zk/base:nested_for_loop_openmp_benchmark\n// -----------------------------------------------------------------------------\n// 2024-02-14T01:39:58+00:00\n// Running /home/ryan/.cache/bazel/_bazel_ryan/d6800124b8b6155cc6ab653ae18dfdd6/execroot/kroma_network_tachyon/bazel-out/k8-opt/bin/tachyon/zk/base/nested_for_loop_openmp_benchmark.runfiles/kroma_network_tachyon/tachyon/zk/base/nested_for_loop_openmp_benchmark\n// Run on (32 X 5500 MHz CPU s)\n// CPU Caches:\n//   L1 Data 48 KiB (x16)\n//   L1 Instruction 32 KiB (x16)\n//   L2 Unified 2048 KiB (x16)\n//   L3 Unified 36864 KiB (x1)\n// Load Average: 0.26, 0.17, 0.19\n// -------------------------------------------------------------------------------------------------------\n// Benchmark                                                             Time             CPU   Iterations\n// -------------------------------------------------------------------------------------------------------\n// BM_NestedForLoopParallelCols<math::bn254::Fr>/500/1024          8801567 ns      8040507 ns           87\n// BM_NestedForLoopParallelCols<math::bn254::Fr>/600/1024          8925674 ns      7830494 ns           85\n// BM_NestedForLoopParallelCols<math::bn254::Fr>/700/1024          9003939 ns      7779225 ns          112\n// BM_NestedForLoopParallelCols<math::bn254::Fr>/800/1024          9030484 ns      7531339 ns           91\n// BM_NestedForLoopParallelCols<math::bn254::Fr>/900/1024          8995042 ns      7910739 ns          121\n// BM_NestedForLoopParallelCols<math::bn254::Fr>/1000/1024         9050964 ns      7898973 ns           89\n// BM_NestedForLoopParallelCols<math::bn254::Fr>/500/2048          9021839 ns      8085116 ns           95\n// BM_NestedForLoopParallelCols<math::bn254::Fr>/600/2048          9036271 ns      8287476 ns           84\n// BM_NestedForLoopParallelCols<math::bn254::Fr>/700/2048          9254811 ns      8349779 ns          108\n// BM_NestedForLoopParallelCols<math::bn254::Fr>/800/2048          9513442 ns      8219528 ns           89\n// BM_NestedForLoopParallelCols<math::bn254::Fr>/900/2048          9563439 ns      7934262 ns          100\n// BM_NestedForLoopParallelCols<math::bn254::Fr>/1000/2048         9932232 ns      8465107 ns           80\n// BM_NestedForLoopParallelCols<math::bn254::Fr>/500/4096          9961692 ns      8186292 ns           90\n// BM_NestedForLoopParallelCols<math::bn254::Fr>/600/4096         10435311 ns      8437591 ns           89\n// BM_NestedForLoopParallelCols<math::bn254::Fr>/700/4096         10956990 ns     10013659 ns           75\n// BM_NestedForLoopParallelCols<math::bn254::Fr>/800/4096         11579415 ns     10750777 ns           68\n// BM_NestedForLoopParallelCols<math::bn254::Fr>/900/4096         12267116 ns      9536249 ns           77\n// BM_NestedForLoopParallelCols<math::bn254::Fr>/1000/4096        12359321 ns     11485318 ns           60\n// BM_NestedForLoopParallelCols<math::bn254::Fr>/500/8192         12697713 ns     10886044 ns           60\n// BM_NestedForLoopParallelCols<math::bn254::Fr>/600/8192         13833057 ns     11446369 ns           62\n// BM_NestedForLoopParallelCols<math::bn254::Fr>/700/8192         14833471 ns     14832698 ns           47\n// BM_NestedForLoopParallelCols<math::bn254::Fr>/800/8192         16094893 ns     12794323 ns           47\n// BM_NestedForLoopParallelCols<math::bn254::Fr>/900/8192         17802661 ns     15956079 ns           48\n// BM_NestedForLoopParallelCols<math::bn254::Fr>/1000/8192        18653991 ns     16865193 ns           59\n// BM_NestedForLoopParallelCols<math::bn254::Fr>/500/16384        18098826 ns     16406897 ns           47\n// BM_NestedForLoopParallelCols<math::bn254::Fr>/600/16384        20269987 ns     16955481 ns           39\n// BM_NestedForLoopParallelCols<math::bn254::Fr>/700/16384        21743041 ns     17145256 ns           40\n// BM_NestedForLoopParallelCols<math::bn254::Fr>/800/16384        23719447 ns     21584281 ns           35\n// BM_NestedForLoopParallelCols<math::bn254::Fr>/900/16384        25161989 ns     21385216 ns           33\n// BM_NestedForLoopParallelCols<math::bn254::Fr>/1000/16384       27030126 ns     23671931 ns           30\n// BM_NestedForLoopParallelCols<math::bn254::Fr>/500/32768        27190628 ns     23602275 ns           29\n// BM_NestedForLoopParallelCols<math::bn254::Fr>/600/32768        30077304 ns     24809468 ns           28\n// BM_NestedForLoopParallelCols<math::bn254::Fr>/700/32768        33541336 ns     29339242 ns           25\n// BM_NestedForLoopParallelCols<math::bn254::Fr>/800/32768        36750392 ns     32140843 ns           22\n// BM_NestedForLoopParallelCols<math::bn254::Fr>/900/32768        41558349 ns     36509732 ns           20\n// BM_NestedForLoopParallelCols<math::bn254::Fr>/1000/32768       45558915 ns     37758209 ns           17\n// BM_NestedForLoopParallelRows<math::bn254::Fr>/500/1024       4336841822 ns   4285953805 ns            1\n// BM_NestedForLoopParallelRows<math::bn254::Fr>/600/1024       5208263159 ns   5058216868 ns            1\n// BM_NestedForLoopParallelRows<math::bn254::Fr>/700/1024       6044492960 ns   5915146990 ns            1\n// BM_NestedForLoopParallelRows<math::bn254::Fr>/800/1024       6922183037 ns   5931739080 ns            1\n// BM_NestedForLoopParallelRows<math::bn254::Fr>/900/1024       7746754408 ns   6681311598 ns            1\n// BM_NestedForLoopParallelRows<math::bn254::Fr>/1000/1024      8651807547 ns   8186728777 ns            1\n// BM_NestedForLoopParallelRows<math::bn254::Fr>/500/2048       4287798643 ns   3932394790 ns            1\n// BM_NestedForLoopParallelRows<math::bn254::Fr>/600/2048       5186981201 ns   4775116743 ns            1\n// BM_NestedForLoopParallelRows<math::bn254::Fr>/700/2048       6069154024 ns   5310053507 ns            1\n// BM_NestedForLoopParallelRows<math::bn254::Fr>/800/2048       6981294632 ns   6459602603 ns            1\n// BM_NestedForLoopParallelRows<math::bn254::Fr>/900/2048       2702628613 ns   2357686183 ns            1\n// BM_NestedForLoopParallelRows<math::bn254::Fr>/1000/2048      1880954981 ns   1789799983 ns            1\n// BM_NestedForLoopParallelRows<math::bn254::Fr>/500/4096          4988417 ns      4717985 ns          264\n// BM_NestedForLoopParallelRows<math::bn254::Fr>/600/4096         28701242 ns     26118750 ns          159\n// BM_NestedForLoopParallelRows<math::bn254::Fr>/700/4096          8012837 ns      7494953 ns          102\n// BM_NestedForLoopParallelRows<math::bn254::Fr>/800/4096          8716901 ns      8209507 ns          133\n// BM_NestedForLoopParallelRows<math::bn254::Fr>/900/4096         11234994 ns     10559360 ns          107\n// BM_NestedForLoopParallelRows<math::bn254::Fr>/1000/4096         7201372 ns      7201110 ns          101\n// BM_NestedForLoopParallelRows<math::bn254::Fr>/500/8192          9088564 ns      8945928 ns          124\n// BM_NestedForLoopParallelRows<math::bn254::Fr>/600/8192         12592125 ns     12379448 ns           95\n// BM_NestedForLoopParallelRows<math::bn254::Fr>/700/8192         21225240 ns     20438229 ns           54\n// BM_NestedForLoopParallelRows<math::bn254::Fr>/800/8192         16714320 ns     16178267 ns           50\n// BM_NestedForLoopParallelRows<math::bn254::Fr>/900/8192         42304723 ns     40716275 ns           61\n// BM_NestedForLoopParallelRows<math::bn254::Fr>/1000/8192        22066152 ns     21179481 ns           54\n// BM_NestedForLoopParallelRows<math::bn254::Fr>/500/16384        34603290 ns     33077561 ns           57\n// BM_NestedForLoopParallelRows<math::bn254::Fr>/600/16384      1448661089 ns   1259494978 ns            1\n// BM_NestedForLoopParallelRows<math::bn254::Fr>/700/16384        50462682 ns     47181769 ns           29\n// BM_NestedForLoopParallelRows<math::bn254::Fr>/800/16384        23063511 ns     22644394 ns           37\n// BM_NestedForLoopParallelRows<math::bn254::Fr>/900/16384        51485399 ns     48692191 ns           36\n// BM_NestedForLoopParallelRows<math::bn254::Fr>/1000/16384      176147251 ns    161074240 ns           17\n// BM_NestedForLoopParallelRows<math::bn254::Fr>/500/32768        78185296 ns     73173143 ns           10\n// ^BBM_NestedForLoopParallelRows<math::bn254::Fr>/600/32768       149321532 ns    139500894 ns           10\n// BM_NestedForLoopParallelRows<math::bn254::Fr>/700/32768       291554535 ns    250161912 ns           17\n// BM_NestedForLoopParallelRows<math::bn254::Fr>/800/32768       130893564 ns    120231909 ns           10\n// BM_NestedForLoopParallelRows<math::bn254::Fr>/900/32768       115641832 ns    109627786 ns           11\n// BM_NestedForLoopParallelRows<math::bn254::Fr>/1000/32768       91647482 ns     86392369 ns           15\n// BM_NestedForLoopParallelCollapse<math::bn254::Fr>/500/1024      3230082 ns      2961001 ns         4752\n// BM_NestedForLoopParallelCollapse<math::bn254::Fr>/600/1024      4188581 ns      3587240 ns         1000\n// BM_NestedForLoopParallelCollapse<math::bn254::Fr>/700/1024      2469267 ns      2197792 ns         3084\n// BM_NestedForLoopParallelCollapse<math::bn254::Fr>/800/1024       665211 ns       611305 ns          820\n// BM_NestedForLoopParallelCollapse<math::bn254::Fr>/900/1024       258700 ns       257955 ns         2382\n// BM_NestedForLoopParallelCollapse<math::bn254::Fr>/1000/1024      583019 ns       557772 ns         1843\n// BM_NestedForLoopParallelCollapse<math::bn254::Fr>/500/2048      1686582 ns      1535978 ns         2364\n// BM_NestedForLoopParallelCollapse<math::bn254::Fr>/600/2048      4188626 ns      3440178 ns         1000\n// BM_NestedForLoopParallelCollapse<math::bn254::Fr>/700/2048      1336820 ns      1198356 ns         1587\n// BM_NestedForLoopParallelCollapse<math::bn254::Fr>/800/2048      2190726 ns      2052386 ns         1000\n// BM_NestedForLoopParallelCollapse<math::bn254::Fr>/900/2048      1762969 ns      1530832 ns         1138\n// BM_NestedForLoopParallelCollapse<math::bn254::Fr>/1000/2048     7961306 ns      6703722 ns          520\n// BM_NestedForLoopParallelCollapse<math::bn254::Fr>/500/4096     11053157 ns      9109342 ns           80\n// BM_NestedForLoopParallelCollapse<math::bn254::Fr>/600/4096      9912193 ns      8139388 ns           72\n// BM_NestedForLoopParallelCollapse<math::bn254::Fr>/700/4096     11803536 ns      9460880 ns           68\n// BM_NestedForLoopParallelCollapse<math::bn254::Fr>/800/4096     11462906 ns      9122242 ns           67\n// BM_NestedForLoopParallelCollapse<math::bn254::Fr>/900/4096      7112131 ns      6236221 ns          100\n// BM_NestedForLoopParallelCollapse<math::bn254::Fr>/1000/4096     8327859 ns      7121217 ns          149\n// BM_NestedForLoopParallelCollapse<math::bn254::Fr>/500/8192      8801355 ns      7689890 ns          149\n// BM_NestedForLoopParallelCollapse<math::bn254::Fr>/600/8192     10712903 ns     10024969 ns          121\n// BM_NestedForLoopParallelCollapse<math::bn254::Fr>/700/8192      9197602 ns      8394859 ns          102\n// BM_NestedForLoopParallelCollapse<math::bn254::Fr>/800/8192     12644033 ns     11226135 ns           86\n// BM_NestedForLoopParallelCollapse<math::bn254::Fr>/900/8192     12511659 ns     11929592 ns           77\n// BM_NestedForLoopParallelCollapse<math::bn254::Fr>/1000/8192    11700689 ns     10882559 ns           73\n// BM_NestedForLoopParallelCollapse<math::bn254::Fr>/500/16384    11866962 ns     11114133 ns           68\n// BM_NestedForLoopParallelCollapse<math::bn254::Fr>/600/16384    13764891 ns     13641851 ns           58\n// BM_NestedForLoopParallelCollapse<math::bn254::Fr>/700/16384    16557857 ns     16087721 ns           51\n// BM_NestedForLoopParallelCollapse<math::bn254::Fr>/800/16384    20248668 ns     19096909 ns           43\n// BM_NestedForLoopParallelCollapse<math::bn254::Fr>/900/16384    22054337 ns     21400207 ns           32\n// BM_NestedForLoopParallelCollapse<math::bn254::Fr>/1000/16384   25220033 ns     20827619 ns           35\n// BM_NestedForLoopParallelCollapse<math::bn254::Fr>/500/32768    21271888 ns     21097723 ns           34\n// BM_NestedForLoopParallelCollapse<math::bn254::Fr>/600/32768    28138599 ns     25294890 ns           25\n// BM_NestedForLoopParallelCollapse<math::bn254::Fr>/700/32768    34027233 ns     30336595 ns           25\n// BM_NestedForLoopParallelCollapse<math::bn254::Fr>/800/32768    40258440 ns     33080362 ns           22\n// BM_NestedForLoopParallelCollapse<math::bn254::Fr>/900/32768    38469553 ns     33483133 ns           19\n// BM_NestedForLoopParallelCollapse<math::bn254::Fr>/1000/32768   47346022 ns     36566534 ns           18\n// clang-format on\n"
  },
  {
    "path": "tachyon/zk/base/parallelize_benchmark.cc",
    "content": "#include \"benchmark/benchmark.h\"\n\n#include \"tachyon/base/parallelize.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/fr.h\"\n\nnamespace tachyon::zk {\n\ntemplate <typename F>\nvoid BM_Parallelize(benchmark::State& state) {\n  size_t n = state.range(0);\n  std::vector<F> vec =\n      base::CreateVectorParallel(n, []() { return F::Random(); });\n  for (auto _ : state) {\n    base::Parallelize(vec, [](absl::Span<F> chunk) {\n      for (F& v : chunk) {\n        v.DoubleInPlace();\n      }\n    });\n  }\n  benchmark::DoNotOptimize(vec);\n}\n\ntemplate <typename F>\nvoid BM_ForLoop(benchmark::State& state) {\n  size_t n = state.range(0);\n  std::vector<F> vec =\n      base::CreateVectorParallel(n, []() { return F::Random(); });\n  for (auto _ : state) {\n    OMP_PARALLEL_FOR(size_t i = 0; i < n; ++i) { vec[i].DoubleInPlace(); }\n  }\n  benchmark::DoNotOptimize(vec);\n}\n\nBENCHMARK_TEMPLATE(BM_Parallelize, math::bn254::Fr)\n    ->RangeMultiplier(2)\n    ->Range(1 << 15, 1 << 20);\n\nBENCHMARK_TEMPLATE(BM_ForLoop, math::bn254::Fr)\n    ->RangeMultiplier(2)\n    ->Range(1 << 15, 1 << 20);\n\n}  // namespace tachyon::zk\n\n// clang-format off\n// Executing tests from //tachyon/zk/base:parallelize_benchmark\n// -----------------------------------------------------------------------------\n// 2024-02-08T13:37:00+00:00\n// Running /home/chokobole/.cache/bazel/_bazel_chokobole/623c4ddaca6f9399eb551ba277ab230c/execroot/kroma_network_tachyon/bazel-out/k8-opt/bin/tachyon/zk/base/parallelize_benchmark.runfiles/kroma_network_tachyon/tachyon/zk/base/parallelize_benchmark\n// Run on (8 X 4600.18 MHz CPU s)\n// CPU Caches:\n//   L1 Data 32 KiB (x8)\n//   L1 Instruction 32 KiB (x8)\n//   L2 Unified 256 KiB (x8)\n//   L3 Unified 12288 KiB (x1)\n// Load Average: 2.21, 1.48, 1.54\n// ----------------------------------------------------------------------------------\n// Benchmark                                        Time             CPU   Iterations\n// ----------------------------------------------------------------------------------\n// BM_Parallelize<math::bn254::Fr>/32768        32137 ns        31658 ns        22342\n// BM_Parallelize<math::bn254::Fr>/65536        64475 ns        64003 ns        11505\n// BM_Parallelize<math::bn254::Fr>/131072      135352 ns       128462 ns         5805\n// BM_Parallelize<math::bn254::Fr>/262144      348907 ns       336052 ns         2630\n// BM_Parallelize<math::bn254::Fr>/524288      730779 ns       675620 ns         1322\n// BM_Parallelize<math::bn254::Fr>/1048576    1993262 ns      1908117 ns          431\n// BM_ForLoop<math::bn254::Fr>/32768            33624 ns        33551 ns        21413\n// BM_ForLoop<math::bn254::Fr>/65536            66033 ns        65931 ns        10689\n// BM_ForLoop<math::bn254::Fr>/131072          142503 ns       136927 ns         5561\n// BM_ForLoop<math::bn254::Fr>/262144          284805 ns       269413 ns         2689\n// BM_ForLoop<math::bn254::Fr>/524288          584841 ns       583951 ns         1369\n// BM_ForLoop<math::bn254::Fr>/1048576        1621698 ns      1621422 ns          389\n// clang-format on\n"
  },
  {
    "path": "tachyon/zk/base/random_field_generator_base.h",
    "content": "#ifndef TACHYON_ZK_BASE_RANDOM_FIELD_GENERATOR_BASE_H_\n#define TACHYON_ZK_BASE_RANDOM_FIELD_GENERATOR_BASE_H_\n\nnamespace tachyon::zk {\n\ntemplate <typename F>\nclass RandomFieldGeneratorBase {\n public:\n  virtual ~RandomFieldGeneratorBase() = default;\n\n  virtual F Generate() = 0;\n};\n\n}  // namespace tachyon::zk\n\n#endif  // TACHYON_ZK_BASE_RANDOM_FIELD_GENERATOR_BASE_H_\n"
  },
  {
    "path": "tachyon/zk/base/rotation.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_BASE_ROTATION_H_\n#define TACHYON_ZK_BASE_ROTATION_H_\n\n#include <string>\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/numerics/checked_math.h\"\n#include \"tachyon/base/strings/string_number_conversions.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluation_domain.h\"\n#include \"tachyon/zk/base/row_types.h\"\n\nnamespace tachyon::zk {\n\n// NOTE(lightscale-luke): Rotation class is copyable, assignable, and occupies\n// 32 bits per instance. Prefer to pass them by value.\nclass TACHYON_EXPORT Rotation {\n public:\n  Rotation() = default;\n  explicit Rotation(int32_t value) : value_(value) {}\n\n  static Rotation Cur() { return Rotation(0); }\n\n  static Rotation Prev() { return Rotation(-1); }\n\n  static Rotation Next() { return Rotation(1); }\n\n  int32_t value() const { return value_; }\n\n  bool operator==(const Rotation& other) const {\n    return value_ == other.value_;\n  }\n  bool operator!=(const Rotation& other) const {\n    return value_ != other.value_;\n  }\n\n  std::string ToString() const { return base::NumberToString(value_); }\n\n  // Returns (|idx| + |value_| * |scale|) modulo |size|.\n  RowIndex GetIndex(int32_t idx, int32_t scale, int32_t size) const {\n    CHECK_GT(size, 0);\n    base::CheckedNumeric<int32_t> value = value_;\n    int32_t result = ((idx + value * scale) % size).ValueOrDie();\n    if (result < 0) result += size;\n    return result;\n  }\n\n  template <typename Domain, typename F>\n  F RotateOmega(const Domain* domain, const F& point) const {\n    return point * domain->GetElement(value_);\n  }\n\n private:\n  int32_t value_ = 0;\n};\n\n}  // namespace tachyon::zk\n\n#endif  // TACHYON_ZK_BASE_ROTATION_H_\n"
  },
  {
    "path": "tachyon/zk/base/rotation_unittest.cc",
    "content": "#include \"tachyon/zk/base/rotation.h\"\n\n#include \"gtest/gtest.h\"\n\nnamespace tachyon::zk {\n\nTEST(RotationTest, GetRotationIdx) {\n  struct {\n    int32_t idx;\n    Rotation rotation;\n    int32_t scale;\n    int32_t size;\n    RowIndex expected;\n  } tests[] = {\n      // (2 + 1 * 3) % 2 = 1\n      {2, Rotation(1), 3, 2, 1},\n      // (4 + 2 * 5) % 3 = 2\n      {4, Rotation(2), 5, 3, 2},\n      // (6 + 3 * 4) % 6 = 0\n      {6, Rotation(3), 4, 6, 0},\n  };\n  for (const auto& test : tests) {\n    EXPECT_EQ(test.expected,\n              test.rotation.GetIndex(test.idx, test.scale, test.size));\n  }\n}\n\n}  // namespace tachyon::zk\n"
  },
  {
    "path": "tachyon/zk/base/row_types.h",
    "content": "#ifndef TACHYON_ZK_BASE_ROW_TYPES_H_\n#define TACHYON_ZK_BASE_ROW_TYPES_H_\n\n#include <stdint.h>\n\nnamespace tachyon::zk {\n\nusing RowIndex = uint32_t;\nusing RowOffset = int32_t;\n\n}  // namespace tachyon::zk\n\n#endif  // TACHYON_ZK_BASE_ROW_TYPES_H_\n"
  },
  {
    "path": "tachyon/zk/base/value.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_BASE_VALUE_H_\n#define TACHYON_ZK_BASE_VALUE_H_\n\n#include <optional>\n#include <string>\n#include <utility>\n\n#include \"tachyon/math/base/rational_field.h\"\n\nnamespace tachyon::zk {\n\ntemplate <typename T>\nclass Value : public math::Field<Value<T>> {\n public:\n  constexpr Value() = default;\n\n  constexpr static Value Unknown() { return Value(); }\n\n  constexpr static Value Known(const T& value) { return Value(value); }\n\n  constexpr static Value Known(T&& value) { return Value(std::move(value)); }\n\n  constexpr static Value Zero() { return Value(T::Zero()); }\n\n  constexpr static Value One() { return Value(T::One()); }\n\n  constexpr static Value MinusOne() { return Value(T::MinusOne()); }\n\n  static Value Random() { return Value(T::Random()); }\n\n  constexpr bool IsZero() const {\n    if (IsNone()) return false;\n    return value_->IsZero();\n  }\n\n  constexpr bool IsOne() const {\n    if (IsNone()) return false;\n    return value_->IsOne();\n  }\n\n  constexpr bool IsMinusOne() const {\n    if (IsNone()) return false;\n    return value_->IsMinusOne();\n  }\n\n  constexpr bool IsNone() const { return !value_.has_value(); }\n\n  std::string ToString() const {\n    if (IsNone()) return \"None\";\n    return value_->ToString();\n  }\n\n  constexpr bool operator==(const Value& other) const {\n    if (IsNone()) return other.IsNone();\n    if (other.IsNone()) return false;\n    return *value_ == *other.value_;\n  }\n  constexpr bool operator!=(const Value& other) const {\n    return operator!=(other);\n  }\n\n  const T& value() const { return *value_; }\n  T& value() { return *value_; }\n\n  const T* operator->() const { return value_.operator->(); }\n  T* operator->() { return value_.operator->(); }\n\n  // AdditiveSemigroup methods\n  constexpr Value Add(const Value& other) const {\n    if (IsNone() || other.IsNone()) return Unknown();\n    return Value::Known(*value_ + *other.value_);\n  }\n\n  constexpr Value Add(const T& other) const {\n    if (IsNone()) return Unknown();\n    return Value::Known(*value_ + other);\n  }\n\n  constexpr Value DoubleImpl() const {\n    if (IsNone()) return Unknown();\n    return Value::Known(value_->Double());\n  }\n\n  constexpr Value& DoubleImplInPlace() {\n    if (IsNone()) return *this;\n    value_->DoubleInPlace();\n    return *this;\n  }\n\n  // AdditiveGroup methods\n  constexpr Value Sub(const Value& other) const {\n    if (IsNone() || other.IsNone()) return Unknown();\n    return Value::Known(*value_ - *other.value_);\n  }\n\n  constexpr Value Sub(const T& other) const {\n    if (IsNone()) return Unknown();\n    return Value::Known(*value_ - other);\n  }\n\n  constexpr Value Negate() const {\n    if (IsNone()) return Unknown();\n    return Value::Known(-*value_);\n  }\n\n  constexpr Value& NegateInPlace() {\n    if (IsNone()) return *this;\n    value_->NegateInPlace();\n    return *this;\n  }\n\n  // MultiplicativeSemigroup methods\n  constexpr Value Mul(const Value& other) const {\n    if (IsNone() || other.IsNone()) return Unknown();\n    return Value::Known(*value_ * *other.value_);\n  }\n\n  constexpr Value Mul(const T& other) const {\n    if (IsNone()) return Unknown();\n    return Value::Known(*value_ * other);\n  }\n\n  constexpr Value SquareImpl() const {\n    if (IsNone()) return Unknown();\n    return Value::Known(value_->Square());\n  }\n\n  constexpr Value& SquareImplInPlace() {\n    if (IsNone()) return *this;\n    value_->SquareInPlace();\n    return *this;\n  }\n\n  // MultiplicativeGroup methods\n  constexpr std::optional<Value> Inverse() const {\n    if (IsNone()) return Unknown();\n    const std::optional<T> val_inv = value_->Inverse();\n    if (LIKELY(val_inv)) return Value::Known(std::move(*val_inv));\n    LOG_IF_NOT_GPU(ERROR) << \"Inverse of zero attempted\";\n    return std::nullopt;\n  }\n\n  [[nodiscard]] constexpr std::optional<Value*> InverseInPlace() {\n    if (IsNone()) return this;\n    if (LIKELY((*value_).InverseInPlace())) return this;\n    LOG_IF_NOT_GPU(ERROR) << \"Inverse of zero attempted\";\n    return std::nullopt;\n  }\n\n  Value<math::RationalField<T>> ToRationalFieldValue() const {\n    if (IsNone()) return Value<math::RationalField<T>>::Unknown();\n    return Value<math::RationalField<T>>::Known(\n        math::RationalField<T>(value_.value()));\n  }\n\n private:\n  constexpr explicit Value(const T& value) : value_(value) {}\n  constexpr explicit Value(T&& value) : value_(std::move(value)) {}\n\n  std::optional<T> value_;\n};\n\n}  // namespace tachyon::zk\n\n#endif  // TACHYON_ZK_BASE_VALUE_H_\n"
  },
  {
    "path": "tachyon/zk/base/value_unittest.cc",
    "content": "#include \"tachyon/zk/base/value.h\"\n\n#include <tuple>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n\nnamespace tachyon::zk {\n\nTEST(ValueTest, Zero) {\n  EXPECT_TRUE(Value<math::GF7>::Zero().IsZero());\n  EXPECT_FALSE(Value<math::GF7>::One().IsZero());\n  EXPECT_FALSE(Value<math::GF7>::MinusOne().IsZero());\n}\n\nTEST(ValueTest, One) {\n  EXPECT_TRUE(Value<math::GF7>::One().IsOne());\n  EXPECT_FALSE(Value<math::GF7>::Zero().IsOne());\n  EXPECT_FALSE(Value<math::GF7>::MinusOne().IsOne());\n}\n\nTEST(ValueTest, MinusOne) {\n  EXPECT_TRUE(Value<math::GF7>::MinusOne().IsMinusOne());\n  EXPECT_FALSE(Value<math::GF7>::Zero().IsMinusOne());\n  EXPECT_FALSE(Value<math::GF7>::One().IsMinusOne());\n}\n\nTEST(ValueTest, IsNone) {\n  EXPECT_TRUE(Value<math::GF7>::Unknown().IsNone());\n  EXPECT_FALSE(Value<math::GF7>::Known(math::GF7(3)).IsNone());\n}\n\nTEST(ValueTest, AdditiveOperators) {\n  math::GF7 a = math::GF7::Random();\n  math::GF7 b = math::GF7::Random();\n\n  struct {\n    Value<math::GF7> a;\n    Value<math::GF7> b;\n    Value<math::GF7> sum;\n    Value<math::GF7> amb;\n    Value<math::GF7> bma;\n  } tests[] = {\n      {Value<math::GF7>::Known(a), Value<math::GF7>::Known(b),\n       Value<math::GF7>::Known(a + b), Value<math::GF7>::Known(a - b),\n       Value<math::GF7>::Known(b - a)},\n      {Value<math::GF7>::Known(a), Value<math::GF7>::Unknown(),\n       Value<math::GF7>::Unknown(), Value<math::GF7>::Unknown(),\n       Value<math::GF7>::Unknown()},\n  };\n\n  for (const auto& test : tests) {\n    if (test.a.IsNone() || test.b.IsNone()) {\n      EXPECT_TRUE((test.a + test.b).IsNone());\n      EXPECT_TRUE((test.b + test.a).IsNone());\n      EXPECT_TRUE((test.a - test.b).IsNone());\n      EXPECT_TRUE((test.b - test.a).IsNone());\n    } else {\n      EXPECT_EQ(test.a + test.b, test.sum);\n      EXPECT_EQ(test.b + test.a, test.sum);\n      EXPECT_EQ(test.a - test.b, test.amb);\n      EXPECT_EQ(test.b - test.a, test.bma);\n    }\n  }\n}\n\nTEST(ValueTest, AdditiveGroupOperators) {\n  math::GF7 a = math::GF7::Random();\n\n  struct {\n    Value<math::GF7> a;\n    Value<math::GF7> neg;\n    Value<math::GF7> dbl;\n  } tests[] = {\n      {\n          Value<math::GF7>::Known(a),\n          Value<math::GF7>::Known(-a),\n          Value<math::GF7>::Known(a.Double()),\n      },\n      {\n          Value<math::GF7>::Unknown(),\n          Value<math::GF7>::Unknown(),\n          Value<math::GF7>::Unknown(),\n      },\n  };\n\n  for (auto& test : tests) {\n    if (test.a.IsNone()) {\n      EXPECT_TRUE(-test.a.IsNone());\n      EXPECT_TRUE(test.a.NegateInPlace().IsNone());\n      EXPECT_TRUE(test.a.Double().IsNone());\n      EXPECT_TRUE(test.a.DoubleInPlace().IsNone());\n    } else {\n      EXPECT_EQ(-test.a, test.neg);\n      Value<math::GF7> a_tmp = test.a;\n      a_tmp.NegateInPlace();\n      EXPECT_EQ(a_tmp, test.neg);\n\n      EXPECT_EQ(test.a.Double(), test.dbl);\n      a_tmp = test.a;\n      a_tmp.DoubleInPlace();\n      EXPECT_EQ(a_tmp, test.dbl);\n    }\n  }\n}\n\nTEST(ValueTest, MultiplicativeOperators) {\n  math::GF7 a = math::GF7::Random();\n  math::GF7 b = math::GF7::Random();\n  while (a.IsZero()) {\n    a = math::GF7::Random();\n  }\n  while (b.IsZero()) {\n    b = math::GF7::Random();\n  }\n\n  struct {\n    Value<math::GF7> a;\n    Value<math::GF7> b;\n    Value<math::GF7> mul;\n    Value<math::GF7> adb;\n    Value<math::GF7> bda;\n  } tests[] = {\n      {Value<math::GF7>::Known(a), Value<math::GF7>::Known(b),\n       Value<math::GF7>::Known(a * b), Value<math::GF7>::Known(*(a / b)),\n       Value<math::GF7>::Known(*(b / a))},\n      {Value<math::GF7>::Known(a), Value<math::GF7>::Unknown(),\n       Value<math::GF7>::Unknown(), Value<math::GF7>::Unknown(),\n       Value<math::GF7>::Unknown()},\n  };\n\n  for (const auto& test : tests) {\n    if (test.a.IsNone() || test.b.IsNone()) {\n      EXPECT_TRUE((test.a * test.b).IsNone());\n      EXPECT_TRUE((test.b * test.a).IsNone());\n      EXPECT_TRUE((*(test.a / test.b)).IsNone());\n      EXPECT_TRUE((*(test.b / test.a)).IsNone());\n    } else {\n      EXPECT_EQ(test.a * test.b, test.mul);\n      EXPECT_EQ(test.b * test.a, test.mul);\n      EXPECT_EQ(*(test.a / test.b), test.adb);\n      EXPECT_EQ(*(test.b / test.a), test.bda);\n    }\n  }\n}\n\nTEST(ValueTest, MultiplicativeGroupOperators) {\n  math::GF7 a = math::GF7::Random();\n\n  struct {\n    Value<math::GF7> a;\n    Value<math::GF7> inverse;\n    Value<math::GF7> sqr;\n    Value<math::GF7> pow;\n  } tests[] = {\n      {\n          Value<math::GF7>::Known(a),\n          a.IsZero() ? Value<math::GF7>::Unknown()\n                     : Value<math::GF7>::Known(*a.Inverse()),\n          Value<math::GF7>::Known(a.Square()),\n          Value<math::GF7>::Known(a.Pow(5)),\n      },\n      {\n          Value<math::GF7>::Unknown(),\n          Value<math::GF7>::Unknown(),\n          Value<math::GF7>::Unknown(),\n          Value<math::GF7>::Unknown(),\n      },\n  };\n\n  for (auto& test : tests) {\n    if (test.a.IsNone()) {\n      EXPECT_TRUE(test.a.Inverse()->IsNone());\n      EXPECT_TRUE((*test.a.InverseInPlace())->IsNone());\n      EXPECT_TRUE(test.a.Square().IsNone());\n      EXPECT_TRUE(test.a.SquareInPlace().IsNone());\n      EXPECT_TRUE(test.a.Pow(5).IsNone());\n    } else {\n      Value<math::GF7> a_tmp;\n      if (UNLIKELY(test.a.IsZero())) {\n        ASSERT_FALSE(test.a.Inverse());\n        ASSERT_FALSE(test.a.InverseInPlace());\n      } else {\n        EXPECT_EQ(*test.a.Inverse(), test.inverse);\n        a_tmp = test.a;\n        EXPECT_EQ(**a_tmp.InverseInPlace(), test.inverse);\n      }\n      EXPECT_EQ(test.a.Square(), test.sqr);\n      a_tmp = test.a;\n      a_tmp.SquareInPlace();\n      EXPECT_EQ(a_tmp, test.sqr);\n\n      EXPECT_EQ(test.a.Pow(5), test.pow);\n    }\n  }\n}\n\nTEST(ValueTest, ToRationalFieldValue) {\n  math::GF7 a = math::GF7::Random();\n  Value<math::GF7> v = Value<math::GF7>::Known(a);\n  EXPECT_EQ(v.ToRationalFieldValue(),\n            Value<math::RationalField<math::GF7>>::Known(\n                math::RationalField<math::GF7>(a)));\n\n  EXPECT_EQ(Value<math::GF7>::Unknown().ToRationalFieldValue(),\n            Value<math::RationalField<math::GF7>>::Unknown());\n}\n\n}  // namespace tachyon::zk\n"
  },
  {
    "path": "tachyon/zk/expressions/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"constant_expression\",\n    hdrs = [\"constant_expression.h\"],\n    deps = [\n        \":expression\",\n        \"@com_google_absl//absl/memory\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"evaluator\",\n    hdrs = [\"evaluator.h\"],\n    deps = [\":expression\"],\n)\n\ntachyon_cc_library(\n    name = \"expression\",\n    hdrs = [\"expression.h\"],\n    deps = [\n        \":expression_type\",\n        \"//tachyon/base/containers:container_util\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"expression_factory\",\n    hdrs = [\"expression_factory.h\"],\n    deps = [\n        \":constant_expression\",\n        \":negated_expression\",\n        \":product_expression\",\n        \":scaled_expression\",\n        \":sum_expression\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"expression_type\",\n    srcs = [\"expression_type.cc\"],\n    hdrs = [\"expression_type.h\"],\n    deps = [\"//tachyon/base:logging\"],\n)\n\ntachyon_cc_library(\n    name = \"negated_expression\",\n    hdrs = [\"negated_expression.h\"],\n    deps = [\n        \":expression\",\n        \"@com_google_absl//absl/memory\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"product_expression\",\n    hdrs = [\"product_expression.h\"],\n    deps = [\n        \":expression\",\n        \"@com_google_absl//absl/memory\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"scaled_expression\",\n    hdrs = [\"scaled_expression.h\"],\n    deps = [\n        \":expression\",\n        \"@com_google_absl//absl/memory\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"sum_expression\",\n    hdrs = [\"sum_expression.h\"],\n    deps = [\n        \":expression\",\n        \"@com_google_absl//absl/memory\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"expression_unittests\",\n    srcs = [\n        \"constant_expression_unittest.cc\",\n        \"expression_unittest.cc\",\n        \"negated_expression_unittest.cc\",\n        \"product_expression_unittest.cc\",\n        \"scaled_expression_unittest.cc\",\n        \"sum_expression_unittest.cc\",\n    ],\n    deps = [\n        \":expression_factory\",\n        \"//tachyon/math/finite_fields/test:finite_field_test\",\n        \"//tachyon/math/finite_fields/test:gf7\",\n        \"//tachyon/math/polynomials/univariate:univariate_polynomial\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/zk/expressions/constant_expression.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_EXPRESSIONS_CONSTANT_EXPRESSION_H_\n#define TACHYON_ZK_EXPRESSIONS_CONSTANT_EXPRESSION_H_\n\n#include <memory>\n#include <string>\n#include <utility>\n\n#include \"absl/memory/memory.h\"\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/zk/expressions/expression.h\"\n\nnamespace tachyon::zk {\n\ntemplate <typename F>\nclass ConstantExpression : public Expression<F> {\n public:\n  static std::unique_ptr<ConstantExpression> CreateForTesting(const F& value) {\n    return absl::WrapUnique(new ConstantExpression(value));\n  }\n\n  const F& value() const { return value_; }\n\n  // Expression methods\n  size_t Degree() const override { return 0; }\n\n  uint64_t Complexity() const override { return 0; }\n\n  std::unique_ptr<Expression<F>> Clone() const override {\n    return absl::WrapUnique(new ConstantExpression(value_));\n  }\n\n  std::string ToString() const override {\n    return absl::Substitute(\"{type: $0, value: $1}\",\n                            ExpressionTypeToString(this->type_),\n                            value_.ToString());\n  }\n\n  bool operator==(const Expression<F>& other) const override {\n    if (!Expression<F>::operator==(other)) return false;\n    const ConstantExpression* constant = other.ToConstant();\n    return value_ == constant->value_;\n  }\n\n private:\n  friend class ExpressionFactory<F>;\n\n  explicit ConstantExpression(const F& value)\n      : Expression<F>(ExpressionType::kConstant), value_(value) {}\n  explicit ConstantExpression(F&& value)\n      : Expression<F>(ExpressionType::kConstant), value_(std::move(value)) {}\n\n  F value_;\n};\n\n}  // namespace tachyon::zk\n\n#endif  // TACHYON_ZK_EXPRESSIONS_CONSTANT_EXPRESSION_H_\n"
  },
  {
    "path": "tachyon/zk/expressions/constant_expression_unittest.cc",
    "content": "#include \"tachyon/zk/expressions/constant_expression.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n\nnamespace tachyon::zk {\n\nusing F = math::GF7;\n\nclass ConstantExpressionTest : public math::FiniteFieldTest<F> {};\n\nTEST_F(ConstantExpressionTest, DegreeComplexity) {\n  std::unique_ptr<ConstantExpression<F>> expr =\n      ConstantExpression<F>::CreateForTesting(F::One());\n  EXPECT_EQ(expr->Degree(), size_t{0});\n  EXPECT_EQ(expr->Complexity(), uint64_t{0});\n}\n\n}  // namespace tachyon::zk\n"
  },
  {
    "path": "tachyon/zk/expressions/evaluator.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_EXPRESSIONS_EVALUATOR_H_\n#define TACHYON_ZK_EXPRESSIONS_EVALUATOR_H_\n\n#include \"tachyon/zk/expressions/expression.h\"\n\nnamespace tachyon::zk {\n\ntemplate <typename F, typename T>\nclass Evaluator {\n public:\n  virtual ~Evaluator() = default;\n\n  virtual T Evaluate(const Expression<F>* input) = 0;\n};\n\n}  // namespace tachyon::zk\n\n#endif  // TACHYON_ZK_EXPRESSIONS_EVALUATOR_H_\n"
  },
  {
    "path": "tachyon/zk/expressions/expression.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_EXPRESSIONS_EXPRESSION_H_\n#define TACHYON_ZK_EXPRESSIONS_EXPRESSION_H_\n\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/zk/expressions/expression_type.h\"\n\nnamespace tachyon::zk {\n\ntemplate <typename F>\nclass ExpressionFactory;\n\ntemplate <typename F, typename T>\nclass Evaluator;\n\ntemplate <typename F>\nclass ConstantExpression;\n\ntemplate <typename F>\nclass NegatedExpression;\n\ntemplate <typename F>\nclass SumExpression;\n\ntemplate <typename F>\nclass ProductExpression;\n\ntemplate <typename F>\nclass ScaledExpression;\n\nnamespace plonk {\ntemplate <typename F>\nclass SelectorExpression;\n\ntemplate <typename F>\nclass FixedExpression;\n\ntemplate <typename F>\nclass AdviceExpression;\n\ntemplate <typename F>\nclass InstanceExpression;\n\ntemplate <typename F>\nclass ChallengeExpression;\n}  // namespace plonk\n\nnamespace air {\ntemplate <typename F>\nclass FirstRowExpression;\n\ntemplate <typename F>\nclass LastRowExpression;\n\ntemplate <typename F>\nclass TransitionExpression;\n\ntemplate <typename F>\nclass VariableExpression;\n}  // namespace air\n\n// A Expression represents a polynomial.\ntemplate <typename F>\nclass Expression {\n public:\n  virtual ~Expression() = default;\n\n  ExpressionType type() const { return type_; }\n\n  // Returns the degree of the polynomial\n  virtual size_t Degree() const = 0;\n\n  // Returns the approximated computational complexity of this expression.\n  virtual uint64_t Complexity() const = 0;\n  virtual std::string ToString() const = 0;\n\n  virtual std::unique_ptr<Expression> Clone() const = 0;\n\n  virtual bool operator==(const Expression& other) const {\n    return type_ == other.type_;\n  }\n  bool operator!=(const Expression& other) const { return !operator==(other); }\n\n  std::unique_ptr<Expression> operator-() const {\n    return ExpressionFactory<F>::Negated(Clone());\n  }\n\n  static std::vector<std::unique_ptr<Expression>> CloneExpressions(\n      const std::vector<std::unique_ptr<Expression>>& expressions) {\n    return base::CreateVector(expressions.size(), [&expressions](size_t i) {\n      return expressions[i]->Clone();\n    });\n  }\n\n  template <typename Evaluated>\n  Evaluated Evaluate(Evaluator<F, Evaluated>* evaluator) const {\n    return evaluator->Evaluate(this);\n  }\n\n#define DEFINE_CONVERSION_METHOD(type)                    \\\n  type##Expression<F>* To##type() {                       \\\n    CHECK_EQ(type_, ExpressionType::k##type);             \\\n    return static_cast<type##Expression<F>*>(this);       \\\n  }                                                       \\\n                                                          \\\n  const type##Expression<F>* To##type() const {           \\\n    CHECK_EQ(type_, ExpressionType::k##type);             \\\n    return static_cast<const type##Expression<F>*>(this); \\\n  }\n\n  DEFINE_CONVERSION_METHOD(Constant)\n  DEFINE_CONVERSION_METHOD(Negated)\n  DEFINE_CONVERSION_METHOD(Sum)\n  DEFINE_CONVERSION_METHOD(Product)\n  DEFINE_CONVERSION_METHOD(Scaled)\n\n#undef DEFINE_CONVERSION_METHOD\n\n#define DEFINE_NAMESPACED_CONVERSION_METHOD(ns, type)         \\\n  ns::type##Expression<F>* To##type() {                       \\\n    CHECK_EQ(type_, ExpressionType::k##type);                 \\\n    return static_cast<ns::type##Expression<F>*>(this);       \\\n  }                                                           \\\n                                                              \\\n  const ns::type##Expression<F>* To##type() const {           \\\n    CHECK_EQ(type_, ExpressionType::k##type);                 \\\n    return static_cast<const ns::type##Expression<F>*>(this); \\\n  }\n  DEFINE_NAMESPACED_CONVERSION_METHOD(plonk, Selector)\n  DEFINE_NAMESPACED_CONVERSION_METHOD(plonk, Fixed)\n  DEFINE_NAMESPACED_CONVERSION_METHOD(plonk, Advice)\n  DEFINE_NAMESPACED_CONVERSION_METHOD(plonk, Instance)\n  DEFINE_NAMESPACED_CONVERSION_METHOD(plonk, Challenge)\n  DEFINE_NAMESPACED_CONVERSION_METHOD(air, FirstRow)\n  DEFINE_NAMESPACED_CONVERSION_METHOD(air, LastRow)\n  DEFINE_NAMESPACED_CONVERSION_METHOD(air, Transition)\n  DEFINE_NAMESPACED_CONVERSION_METHOD(air, Variable)\n#undef DEFINE_NAMESPACED_CONVERSION_METHOD\n\n protected:\n  explicit Expression(ExpressionType type) : type_(type) {}\n\n  ExpressionType type_;\n};\n\ntemplate <typename F>\nstd::ostream& operator<<(std::ostream& os, const Expression<F>& expression) {\n  return os << expression.ToString();\n}\n\n}  // namespace tachyon::zk\n\n#endif  // TACHYON_ZK_EXPRESSIONS_EXPRESSION_H_\n"
  },
  {
    "path": "tachyon/zk/expressions/expression_factory.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_EXPRESSIONS_EXPRESSION_FACTORY_H_\n#define TACHYON_ZK_EXPRESSIONS_EXPRESSION_FACTORY_H_\n\n#include <memory>\n#include <utility>\n\n#include \"absl/memory/memory.h\"\n\n#include \"tachyon/zk/expressions/constant_expression.h\"\n#include \"tachyon/zk/expressions/expression.h\"\n#include \"tachyon/zk/expressions/negated_expression.h\"\n#include \"tachyon/zk/expressions/product_expression.h\"\n#include \"tachyon/zk/expressions/scaled_expression.h\"\n#include \"tachyon/zk/expressions/sum_expression.h\"\n\nnamespace tachyon::zk {\n\ntemplate <typename F>\nclass ExpressionFactory {\n public:\n  using Expr = Expression<F>;\n\n  static std::unique_ptr<Expr> Constant(const F& value) {\n    return absl::WrapUnique(new ConstantExpression<F>(value));\n  }\n\n  static std::unique_ptr<Expr> Negated(std::unique_ptr<Expr> value) {\n    return absl::WrapUnique(new NegatedExpression<F>(std::move(value)));\n  }\n\n  static std::unique_ptr<Expr> Sum(std::unique_ptr<Expr> left,\n                                   std::unique_ptr<Expr> right) {\n    return absl::WrapUnique(\n        new SumExpression<F>(std::move(left), std::move(right)));\n  }\n\n  static std::unique_ptr<Expr> Product(std::unique_ptr<Expr> left,\n                                       std::unique_ptr<Expr> right) {\n    return absl::WrapUnique(\n        new ProductExpression<F>(std::move(left), std::move(right)));\n  }\n\n  static std::unique_ptr<Expr> Scaled(std::unique_ptr<Expr> left,\n                                      const F& scale) {\n    return absl::WrapUnique(new ScaledExpression<F>(std::move(left), scale));\n  }\n};\n\ntemplate <typename F>\nstd::unique_ptr<Expression<F>> operator+(\n    const std::unique_ptr<Expression<F>>& lhs,\n    const std::unique_ptr<Expression<F>>& rhs) {\n  return operator+(lhs->Clone(), rhs->Clone());\n}\n\ntemplate <typename F>\nstd::unique_ptr<Expression<F>> operator+(std::unique_ptr<Expression<F>>&& lhs,\n                                         std::unique_ptr<Expression<F>>&& rhs) {\n  return ExpressionFactory<F>::Sum(std::move(lhs), std::move(rhs));\n}\n\ntemplate <typename F>\nstd::unique_ptr<Expression<F>> operator-(\n    const std::unique_ptr<Expression<F>>& lhs,\n    const std::unique_ptr<Expression<F>>& rhs) {\n  return operator-(lhs->Clone(), rhs->Clone());\n}\n\ntemplate <typename F>\nstd::unique_ptr<Expression<F>> operator-(std::unique_ptr<Expression<F>>&& lhs,\n                                         std::unique_ptr<Expression<F>>&& rhs) {\n  return ExpressionFactory<F>::Sum(std::move(lhs), operator-(std::move(rhs)));\n}\n\ntemplate <typename F>\nstd::unique_ptr<Expression<F>> operator*(\n    const std::unique_ptr<Expression<F>>& lhs,\n    const std::unique_ptr<Expression<F>>& rhs) {\n  return operator*(lhs->Clone(), rhs->Clone());\n}\n\ntemplate <typename F>\nstd::unique_ptr<Expression<F>> operator*(std::unique_ptr<Expression<F>>&& lhs,\n                                         std::unique_ptr<Expression<F>>&& rhs) {\n  return ExpressionFactory<F>::Product(std::move(lhs), std::move(rhs));\n}\n\ntemplate <typename F>\nstd::unique_ptr<Expression<F>> operator*(\n    const std::unique_ptr<Expression<F>>& expr, const F& scale) {\n  return operator*(expr->Clone(), scale);\n}\n\ntemplate <typename F>\nstd::unique_ptr<Expression<F>> operator*(std::unique_ptr<Expression<F>>&& expr,\n                                         const F& scale) {\n  return ExpressionFactory<F>::Scaled(std::move(expr), scale);\n}\n\ntemplate <typename F>\nstd::unique_ptr<Expression<F>> operator-(\n    const std::unique_ptr<Expression<F>>& expr) {\n  return operator-(expr->Clone());\n}\n\ntemplate <typename F>\nstd::unique_ptr<Expression<F>> operator-(\n    std::unique_ptr<Expression<F>>&& expr) {\n  return ExpressionFactory<F>::Negated(std::move(expr));\n}\n\n}  // namespace tachyon::zk\n\n#endif  // TACHYON_ZK_EXPRESSIONS_EXPRESSION_FACTORY_H_\n"
  },
  {
    "path": "tachyon/zk/expressions/expression_type.cc",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#include \"tachyon/zk/expressions/expression_type.h\"\n\n#include \"tachyon/base/logging.h\"\n\nnamespace tachyon::zk {\n\nstd::string_view ExpressionTypeToString(ExpressionType type) {\n  switch (type) {\n    case ExpressionType::kConstant:\n      return \"Constant\";\n    case ExpressionType::kNegated:\n      return \"Negated\";\n    case ExpressionType::kSum:\n      return \"Sum\";\n    case ExpressionType::kProduct:\n      return \"Product\";\n    case ExpressionType::kScaled:\n      return \"Scaled\";\n    case ExpressionType::kSelector:\n      return \"Selector\";\n    case ExpressionType::kFixed:\n      return \"Fixed\";\n    case ExpressionType::kAdvice:\n      return \"Advice\";\n    case ExpressionType::kInstance:\n      return \"Instance\";\n    case ExpressionType::kChallenge:\n      return \"Challenge\";\n    case ExpressionType::kFirstRow:\n      return \"FirstRow\";\n    case ExpressionType::kLastRow:\n      return \"LastRow\";\n    case ExpressionType::kTransition:\n      return \"Transition\";\n    case ExpressionType::kVariable:\n      return \"Variable\";\n  }\n  NOTREACHED();\n  return \"\";\n}\n\nstd::ostream& operator<<(std::ostream& os, ExpressionType type) {\n  return os << ExpressionTypeToString(type);\n}\n\n}  // namespace tachyon::zk\n"
  },
  {
    "path": "tachyon/zk/expressions/expression_type.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_EXPRESSIONS_EXPRESSION_TYPE_H_\n#define TACHYON_ZK_EXPRESSIONS_EXPRESSION_TYPE_H_\n\n#include <ostream>\n#include <string_view>\n\n#include \"tachyon/export.h\"\n\nnamespace tachyon::zk {\n\nenum class ExpressionType {\n  kConstant = 1,\n  kNegated = 2,\n  kSum = 3,\n  kProduct = 4,\n  kScaled = 5,\n\n  // Used in PLONK\n  kSelector = 21,\n  kFixed = 22,\n  kAdvice = 23,\n  kInstance = 24,\n  kChallenge = 25,\n\n  // Used in AIR\n  kFirstRow = 31,\n  kLastRow = 32,\n  kTransition = 33,\n  kVariable = 34,\n};\n\nTACHYON_EXPORT std::string_view ExpressionTypeToString(ExpressionType type);\n\nTACHYON_EXPORT std::ostream& operator<<(std::ostream& os, ExpressionType type);\n\n}  // namespace tachyon::zk\n\n#endif  // TACHYON_ZK_EXPRESSIONS_EXPRESSION_TYPE_H_\n"
  },
  {
    "path": "tachyon/zk/expressions/expression_unittest.cc",
    "content": "#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/random.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n#include \"tachyon/zk/expressions/expression_factory.h\"\n\nnamespace tachyon::zk {\n\nusing F = math::GF7;\n\nnamespace {\n\nclass ExpressionTest : public math::FiniteFieldTest<F> {\n public:\n  void SetUp() override {\n    expressions_.push_back(ExpressionFactory<F>::Constant(F(1)));\n    expressions_.push_back(\n        ExpressionFactory<F>::Negated(ExpressionFactory<F>::Constant(F(1))));\n    expressions_.push_back(\n        ExpressionFactory<F>::Sum(ExpressionFactory<F>::Constant(F(1)),\n                                  ExpressionFactory<F>::Constant(F(2))));\n    expressions_.push_back(\n        ExpressionFactory<F>::Product(ExpressionFactory<F>::Constant(F(3)),\n                                      ExpressionFactory<F>::Constant(F(4))));\n    expressions_.push_back(ExpressionFactory<F>::Scaled(\n        ExpressionFactory<F>::Constant(F(5)), F(6)));\n  }\n\n protected:\n  std::vector<std::unique_ptr<Expression<F>>> expressions_;\n};\n\n}  // namespace\n\nTEST_F(ExpressionTest, ArithmeticOperatorWithClone) {\n  std::unique_ptr<Expression<F>> left =\n      base::UniformElement(expressions_)->Clone();\n  std::unique_ptr<Expression<F>> right =\n      base::UniformElement(expressions_)->Clone();\n\n  std::unique_ptr<Expression<F>> add = left + right;\n  EXPECT_EQ(*add->ToSum()->left(), *left);\n  EXPECT_EQ(*add->ToSum()->right(), *right);\n  EXPECT_TRUE(left);\n  EXPECT_TRUE(right);\n\n  std::unique_ptr<Expression<F>> sub = left - right;\n  EXPECT_EQ(*sub->ToSum()->left(), *left);\n  EXPECT_EQ(*sub->ToSum()->right()->ToNegated()->expr(), *right);\n  EXPECT_TRUE(left);\n  EXPECT_TRUE(right);\n\n  std::unique_ptr<Expression<F>> mul = left * right;\n  EXPECT_EQ(*mul->ToProduct()->left(), *left);\n  EXPECT_EQ(*mul->ToProduct()->right(), *right);\n  EXPECT_TRUE(left);\n  EXPECT_TRUE(right);\n\n  F scale = F::Random();\n  std::unique_ptr<Expression<F>> scaled = left * scale;\n  EXPECT_EQ(*scaled->ToScaled()->expr(), *left);\n  EXPECT_EQ(scaled->ToScaled()->scale(), scale);\n  EXPECT_TRUE(left);\n\n  std::unique_ptr<Expression<F>> neg = -left;\n  EXPECT_EQ(*neg->ToNegated()->expr(), *left);\n  EXPECT_TRUE(left);\n}\n\nTEST_F(ExpressionTest, ArithmeticOperatorWithMove) {\n  std::unique_ptr<Expression<F>> left =\n      base::UniformElement(expressions_)->Clone();\n  std::unique_ptr<Expression<F>> right =\n      base::UniformElement(expressions_)->Clone();\n\n  {\n    std::unique_ptr<Expression<F>> left_tmp = left->Clone();\n    std::unique_ptr<Expression<F>> right_tmp = right->Clone();\n    std::unique_ptr<Expression<F>> add =\n        std::move(left_tmp) + std::move(right_tmp);\n    EXPECT_EQ(*add->ToSum()->left(), *left);\n    EXPECT_EQ(*add->ToSum()->right(), *right);\n    EXPECT_FALSE(left_tmp);\n    EXPECT_FALSE(right_tmp);\n  }\n\n  {\n    std::unique_ptr<Expression<F>> left_tmp = left->Clone();\n    std::unique_ptr<Expression<F>> right_tmp = right->Clone();\n    std::unique_ptr<Expression<F>> sub =\n        std::move(left_tmp) - std::move(right_tmp);\n    EXPECT_EQ(*sub->ToSum()->left(), *left);\n    EXPECT_EQ(*sub->ToSum()->right()->ToNegated()->expr(), *right);\n    EXPECT_FALSE(left_tmp);\n    EXPECT_FALSE(right_tmp);\n  }\n\n  {\n    std::unique_ptr<Expression<F>> left_tmp = left->Clone();\n    std::unique_ptr<Expression<F>> right_tmp = right->Clone();\n    std::unique_ptr<Expression<F>> mul =\n        std::move(left_tmp) * std::move(right_tmp);\n    EXPECT_EQ(*mul->ToProduct()->left(), *left);\n    EXPECT_EQ(*mul->ToProduct()->right(), *right);\n    EXPECT_FALSE(left_tmp);\n    EXPECT_FALSE(right_tmp);\n  }\n\n  {\n    std::unique_ptr<Expression<F>> left_tmp = left->Clone();\n    F scale = F::Random();\n    std::unique_ptr<Expression<F>> scaled = std::move(left_tmp) * scale;\n    EXPECT_EQ(*scaled->ToScaled()->expr(), *left);\n    EXPECT_EQ(scaled->ToScaled()->scale(), scale);\n    EXPECT_FALSE(left_tmp);\n  }\n\n  {\n    std::unique_ptr<Expression<F>> left_tmp = left->Clone();\n    std::unique_ptr<Expression<F>> neg = -std::move(left_tmp);\n    EXPECT_EQ(*neg->ToNegated()->expr(), *left);\n    EXPECT_FALSE(left_tmp);\n  }\n}\n\n}  // namespace tachyon::zk\n"
  },
  {
    "path": "tachyon/zk/expressions/negated_expression.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_EXPRESSIONS_NEGATED_EXPRESSION_H_\n#define TACHYON_ZK_EXPRESSIONS_NEGATED_EXPRESSION_H_\n\n#include <memory>\n#include <string>\n#include <utility>\n\n#include \"absl/memory/memory.h\"\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/zk/expressions/expression.h\"\n\nnamespace tachyon::zk {\n\ntemplate <typename F>\nclass NegatedExpression : public Expression<F> {\n public:\n  constexpr static uint64_t kOverhead = 30;\n\n  static std::unique_ptr<NegatedExpression> CreateForTesting(\n      std::unique_ptr<Expression<F>> expr) {\n    return absl::WrapUnique(new NegatedExpression(std::move(expr)));\n  }\n\n  Expression<F>* expr() const { return expr_.get(); }\n\n  // Expression methods\n  size_t Degree() const override { return expr_->Degree(); }\n\n  uint64_t Complexity() const override {\n    return expr_->Complexity() + kOverhead;\n  }\n\n  std::unique_ptr<Expression<F>> Clone() const override {\n    return absl::WrapUnique(new NegatedExpression(expr_->Clone()));\n  }\n\n  std::string ToString() const override {\n    return absl::Substitute(\"{type: $0, expr: $1}\",\n                            ExpressionTypeToString(this->type_),\n                            expr_->ToString());\n  }\n\n  bool operator==(const Expression<F>& other) const override {\n    if (!Expression<F>::operator==(other)) return false;\n    const NegatedExpression* negated = other.ToNegated();\n    return *expr_ == *negated->expr_;\n  }\n\n private:\n  friend class ExpressionFactory<F>;\n\n  explicit NegatedExpression(std::unique_ptr<Expression<F>> expr)\n      : Expression<F>(ExpressionType::kNegated), expr_(std::move(expr)) {}\n\n  std::unique_ptr<Expression<F>> expr_;\n};\n\n}  // namespace tachyon::zk\n\n#endif  // TACHYON_ZK_EXPRESSIONS_NEGATED_EXPRESSION_H_\n"
  },
  {
    "path": "tachyon/zk/expressions/negated_expression_unittest.cc",
    "content": "#include \"tachyon/zk/expressions/negated_expression.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n#include \"tachyon/zk/expressions/constant_expression.h\"\n\nnamespace tachyon::zk {\n\nusing F = math::GF7;\n\nclass NegatedExpressionTest : public math::FiniteFieldTest<F> {};\n\nTEST_F(NegatedExpressionTest, DegreeComplexity) {\n  std::unique_ptr<ConstantExpression<F>> expr =\n      ConstantExpression<F>::CreateForTesting(F::One());\n\n  size_t expr_degree = expr->Degree();\n  uint64_t expr_complexity = expr->Complexity();\n\n  std::unique_ptr<NegatedExpression<F>> negated_expression =\n      NegatedExpression<F>::CreateForTesting(std::move(expr));\n\n  EXPECT_EQ(negated_expression->Degree(), expr_degree);\n  EXPECT_EQ(negated_expression->Complexity(),\n            expr_complexity + NegatedExpression<F>::kOverhead);\n}\n\n}  // namespace tachyon::zk\n"
  },
  {
    "path": "tachyon/zk/expressions/product_expression.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_EXPRESSIONS_PRODUCT_EXPRESSION_H_\n#define TACHYON_ZK_EXPRESSIONS_PRODUCT_EXPRESSION_H_\n\n#include <memory>\n#include <string>\n#include <utility>\n\n#include \"absl/memory/memory.h\"\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/zk/expressions/expression.h\"\n\nnamespace tachyon::zk {\n\ntemplate <typename F>\nclass ProductExpression : public Expression<F> {\n public:\n  constexpr static uint64_t kOverhead = 30;\n\n  static std::unique_ptr<ProductExpression> CreateForTesting(\n      std::unique_ptr<Expression<F>> left,\n      std::unique_ptr<Expression<F>> right) {\n    return absl::WrapUnique(\n        new ProductExpression(std::move(left), std::move(right)));\n  }\n\n  const Expression<F>* left() const { return left_.get(); }\n  const Expression<F>* right() const { return right_.get(); }\n\n  // Expression methods\n  size_t Degree() const override { return left_->Degree() + right_->Degree(); }\n\n  uint64_t Complexity() const override {\n    return left_->Complexity() + right_->Complexity() + kOverhead;\n  }\n\n  std::unique_ptr<Expression<F>> Clone() const override {\n    return absl::WrapUnique(\n        new ProductExpression(left_->Clone(), right_->Clone()));\n  }\n\n  std::string ToString() const override {\n    return absl::Substitute(\"{type: $0, left: $1, right: $2}\",\n                            ExpressionTypeToString(this->type_),\n                            left_->ToString(), right_->ToString());\n  }\n\n  bool operator==(const Expression<F>& other) const override {\n    if (!Expression<F>::operator==(other)) return false;\n    const ProductExpression* product = other.ToProduct();\n    return *left_ == *product->left_ && *right_ == *product->right_;\n  }\n\n private:\n  friend class ExpressionFactory<F>;\n\n  ProductExpression(std::unique_ptr<Expression<F>> left,\n                    std::unique_ptr<Expression<F>> right)\n      : Expression<F>(ExpressionType::kProduct),\n        left_(std::move(left)),\n        right_(std::move(right)) {}\n\n  std::unique_ptr<Expression<F>> left_;\n  std::unique_ptr<Expression<F>> right_;\n};\n\n}  // namespace tachyon::zk\n\n#endif  // TACHYON_ZK_EXPRESSIONS_PRODUCT_EXPRESSION_H_\n"
  },
  {
    "path": "tachyon/zk/expressions/product_expression_unittest.cc",
    "content": "#include \"tachyon/zk/expressions/product_expression.h\"\n\n#include <algorithm>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n#include \"tachyon/zk/expressions/constant_expression.h\"\n\nnamespace tachyon::zk {\n\nusing F = math::GF7;\n\nclass ProductExpressionTest : public math::FiniteFieldTest<F> {};\n\nTEST_F(ProductExpressionTest, DegreeComplexity) {\n  std::unique_ptr<ConstantExpression<F>> left =\n      ConstantExpression<F>::CreateForTesting(F::One());\n  std::unique_ptr<ConstantExpression<F>> right =\n      ConstantExpression<F>::CreateForTesting(F::Zero());\n\n  size_t left_degree = left->Degree();\n  uint64_t left_complexity = left->Complexity();\n  size_t right_degree = right->Degree();\n  uint64_t right_complexity = right->Complexity();\n\n  std::unique_ptr<ProductExpression<F>> prod_expression =\n      ProductExpression<F>::CreateForTesting(std::move(left), std::move(right));\n\n  EXPECT_EQ(prod_expression->Degree(), std::max(left_degree, right_degree));\n  EXPECT_EQ(prod_expression->Complexity(), left_complexity + right_complexity +\n                                               ProductExpression<F>::kOverhead);\n}\n\n}  // namespace tachyon::zk\n"
  },
  {
    "path": "tachyon/zk/expressions/scaled_expression.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_EXPRESSIONS_SCALED_EXPRESSION_H_\n#define TACHYON_ZK_EXPRESSIONS_SCALED_EXPRESSION_H_\n\n#include <memory>\n#include <string>\n#include <utility>\n\n#include \"absl/memory/memory.h\"\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/zk/expressions/expression.h\"\n\nnamespace tachyon::zk {\n\ntemplate <typename F>\nclass ScaledExpression : public Expression<F> {\n public:\n  constexpr static uint64_t kOverhead = 30;\n\n  static std::unique_ptr<ScaledExpression> CreateForTesting(\n      std::unique_ptr<Expression<F>> expr, const F& scale) {\n    return absl::WrapUnique(new ScaledExpression(std::move(expr), scale));\n  }\n\n  const Expression<F>* expr() const { return expr_.get(); }\n  const F& scale() const { return scale_; }\n\n  // Expression methods\n  size_t Degree() const override { return expr_->Degree(); }\n\n  uint64_t Complexity() const override {\n    return expr_->Complexity() + kOverhead;\n  }\n\n  std::unique_ptr<Expression<F>> Clone() const override {\n    return absl::WrapUnique(new ScaledExpression(expr_->Clone(), scale_));\n  }\n\n  std::string ToString() const override {\n    return absl::Substitute(\"{type: $0, expr: $1, scale: $2}\",\n                            ExpressionTypeToString(this->type_),\n                            expr_->ToString(), scale_.ToString());\n  }\n\n  bool operator==(const Expression<F>& other) const override {\n    if (!Expression<F>::operator==(other)) return false;\n    const ScaledExpression* scaled = other.ToScaled();\n    return *expr_ == *scaled->expr_ && scale_ == scaled->scale_;\n  }\n\n private:\n  friend class ExpressionFactory<F>;\n\n  ScaledExpression(std::unique_ptr<Expression<F>> expr, const F& scale)\n      : Expression<F>(ExpressionType::kScaled),\n        expr_(std::move(expr)),\n        scale_(scale) {}\n  ScaledExpression(std::unique_ptr<Expression<F>> expr, F&& scale)\n      : Expression<F>(ExpressionType::kScaled),\n        expr_(std::move(expr)),\n        scale_(std::move(scale)) {}\n\n  std::unique_ptr<Expression<F>> expr_;\n  F scale_;\n};\n\n}  // namespace tachyon::zk\n\n#endif  // TACHYON_ZK_EXPRESSIONS_SCALED_EXPRESSION_H_\n"
  },
  {
    "path": "tachyon/zk/expressions/scaled_expression_unittest.cc",
    "content": "#include \"tachyon/zk/expressions/scaled_expression.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n#include \"tachyon/zk/expressions/constant_expression.h\"\n\nnamespace tachyon::zk {\n\nusing F = math::GF7;\n\nclass ScaledExpressionTest : public math::FiniteFieldTest<F> {};\n\nTEST_F(ScaledExpressionTest, DegreeComplexity) {\n  F scale = F::One();\n  std::unique_ptr<ConstantExpression<F>> expr =\n      ConstantExpression<F>::CreateForTesting(F::One());\n\n  size_t expr_degree = expr->Degree();\n  uint64_t expr_complexity = expr->Complexity();\n\n  std::unique_ptr<ScaledExpression<F>> scaled_expr =\n      ScaledExpression<F>::CreateForTesting(std::move(expr), std::move(scale));\n\n  EXPECT_EQ(scaled_expr->Degree(), expr_degree);\n  EXPECT_EQ(scaled_expr->Complexity(),\n            expr_complexity + ScaledExpression<F>::kOverhead);\n}\n\n}  // namespace tachyon::zk\n"
  },
  {
    "path": "tachyon/zk/expressions/sum_expression.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_EXPRESSIONS_SUM_EXPRESSION_H_\n#define TACHYON_ZK_EXPRESSIONS_SUM_EXPRESSION_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <algorithm>\n#include <memory>\n#include <string>\n#include <utility>\n\n#include \"absl/memory/memory.h\"\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/zk/expressions/expression.h\"\n\nnamespace tachyon::zk {\n\ntemplate <typename F>\nclass SumExpression : public Expression<F> {\n public:\n  constexpr static uint64_t kOverhead = 15;\n  static std::unique_ptr<SumExpression> CreateForTesting(\n      std::unique_ptr<Expression<F>> left,\n      std::unique_ptr<Expression<F>> right) {\n    return absl::WrapUnique(\n        new SumExpression(std::move(left), std::move(right)));\n  }\n\n  const Expression<F>* left() const { return left_.get(); }\n  const Expression<F>* right() const { return right_.get(); }\n\n  // Expression methods\n  size_t Degree() const override {\n    return std::max(left_->Degree(), right_->Degree());\n  }\n\n  uint64_t Complexity() const override {\n    return left_->Complexity() + right_->Complexity() + kOverhead;\n  }\n\n  std::unique_ptr<Expression<F>> Clone() const override {\n    return absl::WrapUnique(new SumExpression(left_->Clone(), right_->Clone()));\n  }\n\n  std::string ToString() const override {\n    return absl::Substitute(\"{type: $0, left: $1 right: $2}\",\n                            ExpressionTypeToString(this->type_),\n                            left_->ToString(), right_->ToString());\n  }\n\n  bool operator==(const Expression<F>& other) const override {\n    if (!Expression<F>::operator==(other)) return false;\n    const SumExpression* sum = other.ToSum();\n    return *left_ == *sum->left_ && *right_ == *sum->right_;\n  }\n\n private:\n  friend class ExpressionFactory<F>;\n\n  SumExpression(std::unique_ptr<Expression<F>> left,\n                std::unique_ptr<Expression<F>> right)\n      : Expression<F>(ExpressionType::kSum),\n        left_(std::move(left)),\n        right_(std::move(right)) {}\n\n  std::unique_ptr<Expression<F>> left_;\n  std::unique_ptr<Expression<F>> right_;\n};\n\n}  // namespace tachyon::zk\n\n#endif  // TACHYON_ZK_EXPRESSIONS_SUM_EXPRESSION_H_\n"
  },
  {
    "path": "tachyon/zk/expressions/sum_expression_unittest.cc",
    "content": "#include \"tachyon/zk/expressions/sum_expression.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n#include \"tachyon/zk/expressions/constant_expression.h\"\n\nnamespace tachyon::zk {\n\nusing F = math::GF7;\n\nclass SumExpressionTest : public math::FiniteFieldTest<F> {};\n\nTEST_F(SumExpressionTest, DegreeComplexity) {\n  std::unique_ptr<ConstantExpression<F>> left =\n      ConstantExpression<F>::CreateForTesting(F::One());\n  std::unique_ptr<ConstantExpression<F>> right =\n      ConstantExpression<F>::CreateForTesting(F::One());\n\n  size_t left_degree = left->Degree();\n  uint64_t left_complexity = left->Complexity();\n  size_t right_degree = right->Degree();\n  uint64_t right_complexity = right->Complexity();\n\n  std::unique_ptr<SumExpression<F>> sum_expression =\n      SumExpression<F>::CreateForTesting(std::move(left), std::move(right));\n\n  EXPECT_EQ(sum_expression->Degree(), std::max(left_degree, right_degree));\n  EXPECT_EQ(sum_expression->Complexity(),\n            left_complexity + right_complexity + SumExpression<F>::kOverhead);\n}\n\n}  // namespace tachyon::zk\n"
  },
  {
    "path": "tachyon/zk/lookup/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"argument\",\n    hdrs = [\"argument.h\"],\n    deps = [\n        \":pair\",\n        \"//tachyon/zk/expressions:expression\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"evaluator\",\n    hdrs = [\"evaluator.h\"],\n    deps = [\n        \":type\",\n        \"//tachyon/zk/lookup/halo2:evaluator\",\n        \"//tachyon/zk/lookup/log_derivative_halo2:evaluator\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"pair\",\n    hdrs = [\"pair.h\"],\n    deps = [\"//tachyon/base/json\"],\n)\n\ntachyon_cc_library(\n    name = \"prover\",\n    hdrs = [\"prover.h\"],\n    deps = [\n        \":type\",\n        \"//tachyon/zk/lookup/halo2:prover\",\n        \"//tachyon/zk/lookup/log_derivative_halo2:prover\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"type\",\n    hdrs = [\"type.h\"],\n)\n\ntachyon_cc_library(\n    name = \"verifier\",\n    hdrs = [\"verifier.h\"],\n    deps = [\n        \":type\",\n        \"//tachyon/zk/lookup/halo2:verifier\",\n        \"//tachyon/zk/lookup/log_derivative_halo2:verifier\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"verifier_traits_forward\",\n    hdrs = [\"verifier_traits_forward.h\"],\n)\n"
  },
  {
    "path": "tachyon/zk/lookup/argument.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_LOOKUP_ARGUMENT_H_\n#define TACHYON_ZK_LOOKUP_ARGUMENT_H_\n\n#include <algorithm>\n#include <memory>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/zk/expressions/expression.h\"\n#include \"tachyon/zk/lookup/pair.h\"\n\nnamespace tachyon::zk::lookup {\n\ntemplate <typename F>\nclass Argument {\n public:\n  Argument() = default;\n  Argument(std::string_view name,\n           std::vector<std::unique_ptr<Expression<F>>> input_expressions,\n           std::vector<std::unique_ptr<Expression<F>>> table_expressions)\n      : name_(std::string(name)),\n        table_expressions_(std::move(table_expressions)) {\n    inputs_expressions_.push_back(std::move(input_expressions));\n  }\n  Argument(std::string_view name,\n           std::vector<std::vector<std::unique_ptr<Expression<F>>>>\n               inputs_expressions,\n           std::vector<std::unique_ptr<Expression<F>>> table_expressions)\n      : name_(std::string(name)),\n        inputs_expressions_(std::move(inputs_expressions)),\n        table_expressions_(std::move(table_expressions)) {}\n  Argument(std::string_view name, Pairs<std::unique_ptr<Expression<F>>> pairs)\n      : name_(std::string(name)) {\n    std::vector<std::unique_ptr<Expression<F>>> input_expressions;\n    input_expressions.reserve(pairs.size());\n    table_expressions_.reserve(pairs.size());\n\n    for (Pair<std::unique_ptr<Expression<F>>>& pair : pairs) {\n      input_expressions.push_back(std::move(pair).TakeInput());\n      table_expressions_.push_back(std::move(pair).TakeTable());\n    }\n    inputs_expressions_.push_back(std::move(input_expressions));\n\n    pairs.clear();\n  }\n\n  const std::vector<std::vector<std::unique_ptr<Expression<F>>>>&\n  inputs_expressions() const {\n    return inputs_expressions_;\n  }\n\n  std::vector<std::vector<std::unique_ptr<Expression<F>>>>&\n  inputs_expressions() {\n    return inputs_expressions_;\n  }\n\n  const std::vector<std::unique_ptr<Expression<F>>>& input_expressions() const {\n    return inputs_expressions_[0];\n  }\n\n  std::vector<std::unique_ptr<Expression<F>>>& input_expressions() {\n    return inputs_expressions_[0];\n  }\n\n  const std::vector<std::unique_ptr<Expression<F>>>& table_expressions() const {\n    return table_expressions_;\n  }\n\n  std::vector<std::unique_ptr<Expression<F>>>& table_expressions() {\n    return table_expressions_;\n  }\n\n  bool operator==(const Argument& other) const {\n    if (name_ != other.name_) return false;\n    if (inputs_expressions_.size() != other.inputs_expressions_.size())\n      return false;\n    if (table_expressions_.size() != other.table_expressions_.size())\n      return false;\n    for (size_t i = 0; i < inputs_expressions_.size(); ++i) {\n      if (inputs_expressions_[i].size() != other.inputs_expressions_[i].size())\n        return false;\n      for (size_t j = 0; j < inputs_expressions_[i].size(); ++j) {\n        if (*inputs_expressions_[i][j] != *other.inputs_expressions_[i][j])\n          return false;\n      }\n    }\n    for (size_t i = 0; i < table_expressions_.size(); ++i) {\n      if (*table_expressions_[i] != *other.table_expressions_[i]) return false;\n    }\n    return true;\n  }\n  bool operator!=(const Argument& other) const { return !operator==(other); }\n\n  size_t RequiredDegree() const {\n    for (const std::vector<std::unique_ptr<Expression<F>>>& input_expressions :\n         inputs_expressions_) {\n      CHECK_EQ(input_expressions.size(), table_expressions_.size());\n    }\n    // [Halo2 Lookup]\n    // See https://zcash.github.io/halo2/design/proving-system/lookup.html\n    // for more details.\n    //\n    // The first value in the permutation poly should be one.\n    // degree 2:\n    // l_first(X) * (1 - Z(X)) = 0\n    //\n    // The \"last\" value in the permutation poly should be a boolean, for\n    // completeness and soundness.\n    //\n    // degree 3:\n    // l_last(X) * (Z(X)² - Z(X)) = 0\n    //\n    // Enable the permutation argument for only the rows involved.\n    // degree (2 + |combined_input_degree| + |max_table_degree|) or 4, whichever\n    // is larger:\n    // clang-format off\n    // (1 - (l_last(X) + l_blind(X))) * (Z(ω * X) * (A'(X) + β) * (S'(X) + γ) - Z(X) * (A_compressed(X) + β) * (S_compressed(X) + γ)) = 0\n    // clang-format on\n    //\n    // The first two values of A' and S' should be the same.\n    //\n    // degree 2:\n    // l_first(X) * (A'(X) - S'(X)) = 0\n    //\n    // Either the two values are the same, or the previous value of A' is the\n    // same as the current value.\n    //\n    // degree 3:\n    // clang-format off\n    // (1 - (l_last(X) + l_blind(X))) * (A′(X) − S′(X)) * (A′(X) − A′(ω⁻¹ * X)) = 0\n    // clang-format on\n    //\n    // [LogDerivativeHalo2]\n    // The first value in the sum poly should be zero.\n    // degree 2:\n    // l_first(X) * ϕ(X) = 0\n    //\n    // The last value in the sum poly should be zero.\n    // degree 2:\n    // l_last(X) * ϕ(X) = 0\n    //\n    // Enable the sum argument for only the rows involved.\n    // degree (2 + |combined_input_degree| + |max_table_degree|) or\n    // (3 + |inputs_expressions_.size()|), whichever is larger:\n    // clang-format off\n    // φᵢ(X) = fᵢ(X) + β\n    // τ(X) = t(X) + β\n    // LHS = τ(X) * Π(φᵢ(X)) * (ϕ(ω * X) - ϕ(X))\n    //     ↪ DEG(LHS) = |max_table_degree| + |combined_input_degree| + 1\n    // RHS = τ(X) * Π(φᵢ(X)) * ((Σ 1/φᵢ(X)) - m(X) / τ(X))\n    //     ↪ DEG(RHS) = |combined_input_degree| + 1\n    // (1 - (l_last(X) + l_blind(X))) * (ϕ(ω * X) * τ(X) * Π(φᵢ(X)) - (LHS - RHS))\n    // clang-format on\n    size_t combined_input_degree = std::accumulate(\n        inputs_expressions_.begin(), inputs_expressions_.end(), 0,\n        [](size_t combined_degree,\n           const std::vector<std::unique_ptr<Expression<F>>>&\n               input_expressions) {\n          return combined_degree + GetMaxExprDegree(input_expressions);\n        });\n\n    size_t max_table_degree = GetMaxExprDegree(table_expressions_);\n\n    // In practice because input_degree and table_degree are initialized to\n    // one, the latter half of this max() invocation is at least 4 always,\n    // rendering this call pointless except to be explicit in case we change\n    // the initialization of input_degree/table_degree in the future.\n    return std::max(\n        // clang-format off\n        // [Halo2 Lookup]\n        // NOTE(Insun35): The size of |inputs_expressions_| for Halo2 lookup is 1.\n        // Thus, (3 + |inputs_expressions_.size()|) is always 4.\n        // (1 - (l_last(X) + l_blind(X))) * Z(ω * X) * (A'(X) + β) * (S'(X) + γ)\n        //   ↪ degree = 4\n        //\n        // [LogDerivativeHalo2]\n        // (1 - (l_last(X) + l_blind(X))) * ϕ(ω * X) * τ(X) * Π(φᵢ(X))\n        //   ↪ degree = |3 + inputs_expressions_.size()|\n        // clang-format on\n        3 + inputs_expressions_.size(),\n        // clang-format off\n        // [Halo2 Lookup]\n        // max_degree =\n        //    DEG((l_last(X) + l_blind(X))) * Z(X) * (A_compressed(X) + β) * (S_compressed(X) + γ))\n        //\n        // [LogDerivativeHalo2]\n        // LHS = τ(X) * Π(φᵢ(X)) * (ϕ(ω * X) - ϕ(X))\n        //     ↪ DEG(LHS) = |max_table_degree| + |combined_input_degree| + 1\n        // RHS = τ(X) * Π(φᵢ(X)) * ((Σ 1/φᵢ(X)) - m(X) / τ(X))\n        //     ↪ DEG(RHS) = |combined_input_degree| + 1\n        // max_degree = DEG((1 - (l_last(X) + l_blind(X))) * (LHS - RHS))\n        //            = 1 + DEG(LHS) = 2 + |combined_input_degree| + |max_table_degree|\n        // clang-format on\n        size_t{2} + combined_input_degree + max_table_degree);\n  }\n\n private:\n  static size_t GetMaxExprDegree(\n      const std::vector<std::unique_ptr<Expression<F>>>& expressions) {\n    return std::accumulate(\n        expressions.begin(), expressions.end(), 1,\n        [](size_t degree, const std::unique_ptr<Expression<F>>& expr_ptr) {\n          return std::max(degree, expr_ptr->Degree());\n        });\n  }\n\n  std::string name_;\n  std::vector<std::vector<std::unique_ptr<Expression<F>>>> inputs_expressions_;\n  std::vector<std::unique_ptr<Expression<F>>> table_expressions_;\n};\n\n}  // namespace tachyon::zk::lookup\n\n#endif  // TACHYON_ZK_LOOKUP_ARGUMENT_H_\n"
  },
  {
    "path": "tachyon/zk/lookup/evaluator.h",
    "content": "#ifndef TACHYON_ZK_LOOKUP_EVALUATOR_H_\n#define TACHYON_ZK_LOOKUP_EVALUATOR_H_\n\n#include <type_traits>\n\n#include \"tachyon/zk/lookup/halo2/evaluator.h\"\n#include \"tachyon/zk/lookup/log_derivative_halo2/evaluator.h\"\n#include \"tachyon/zk/lookup/type.h\"\n\nnamespace tachyon::zk::lookup {\n\ntemplate <Type kType, typename EvalsOrExtendedEvals>\nusing Evaluator = std::conditional_t<\n    kType == lookup::Type::kHalo2,\n    lookup::halo2::Evaluator<EvalsOrExtendedEvals>,\n    lookup::log_derivative_halo2::Evaluator<EvalsOrExtendedEvals>>;\n\n}  // namespace tachyon::zk::lookup\n\n#endif  // TACHYON_ZK_LOOKUP_EVALUATOR_H_\n"
  },
  {
    "path": "tachyon/zk/lookup/halo2/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"evaluator\",\n    hdrs = [\"evaluator.h\"],\n    deps = [\n        \":prover\",\n        \"//tachyon/zk/plonk/vanishing:circuit_polynomial_builder_forward\",\n        \"//tachyon/zk/plonk/vanishing:graph_evaluator\",\n        \"//tachyon/zk/plonk/vanishing:vanishing_utils\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"opening_point_set\",\n    hdrs = [\"opening_point_set.h\"],\n)\n\ntachyon_cc_library(\n    name = \"permute_expression_pair\",\n    hdrs = [\"permute_expression_pair.h\"],\n    deps = [\n        \"//tachyon/base:parallelize\",\n        \"//tachyon/base:sort\",\n        \"//tachyon/math/base:parallelize_threshold\",\n        \"//tachyon/zk/base/entities:prover_base\",\n        \"//tachyon/zk/lookup:pair\",\n        \"@com_google_absl//absl/container:btree\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"prover\",\n    hdrs = [\n        \"prover.h\",\n        \"prover_impl.h\",\n    ],\n    deps = [\n        \":opening_point_set\",\n        \":permute_expression_pair\",\n        \"//tachyon/base:profiler\",\n        \"//tachyon/base:ref\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/crypto/commitments:polynomial_openings\",\n        \"//tachyon/zk/base/entities:prover_base\",\n        \"//tachyon/zk/lookup:argument\",\n        \"//tachyon/zk/plonk/base:multi_phase_ref_table\",\n        \"//tachyon/zk/plonk/expressions:compress_expression\",\n        \"//tachyon/zk/plonk/expressions:proving_evaluator\",\n        \"//tachyon/zk/plonk/permutation:grand_product_argument\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"scheme\",\n    hdrs = [\"scheme.h\"],\n    deps = [\"//tachyon/zk/lookup:type\"],\n)\n\ntachyon_cc_library(\n    name = \"utils\",\n    hdrs = [\"utils.h\"],\n    deps = [\n        \"//tachyon/base:logging\",\n        \"//tachyon/zk/lookup:type\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"verifier\",\n    hdrs = [\"verifier.h\"],\n    deps = [\n        \":opening_point_set\",\n        \":verifier_data\",\n        \"//tachyon/crypto/commitments:polynomial_openings\",\n        \"//tachyon/zk/lookup:argument\",\n        \"//tachyon/zk/plonk/base:l_values\",\n        \"//tachyon/zk/plonk/expressions:verifying_evaluator\",\n        \"//tachyon/zk/plonk/halo2:proof\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"verifier_data\",\n    hdrs = [\"verifier_data.h\"],\n    deps = [\n        \"//tachyon/zk/lookup:pair\",\n        \"//tachyon/zk/plonk/base:multi_phase_evaluations\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"halo2_unittests\",\n    srcs = [\"permute_expression_pair_unittest.cc\"],\n    deps = [\n        \":permute_expression_pair\",\n        \"//tachyon/base:random\",\n        \"//tachyon/zk/plonk/halo2:bn254_shplonk_prover_test\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/zk/lookup/halo2/evaluator.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_LOOKUP_HALO2_EVALUATOR_H_\n#define TACHYON_ZK_LOOKUP_HALO2_EVALUATOR_H_\n\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/zk/lookup/halo2/prover.h\"\n#include \"tachyon/zk/plonk/vanishing/circuit_polynomial_builder_forward.h\"\n#include \"tachyon/zk/plonk/vanishing/graph_evaluator.h\"\n#include \"tachyon/zk/plonk/vanishing/vanishing_utils.h\"\n\nnamespace tachyon::zk::lookup::halo2 {\n\ntemplate <typename EvalsOrExtendedEvals>\nclass Evaluator {\n public:\n  using F = typename EvalsOrExtendedEvals::Field;\n\n  void Construct(const std::vector<Argument<F>>& arguments) {\n    TRACE_EVENT(\"ProofGeneration\", \"Lookup::Halo2::Evaluator::Construct\");\n    evaluators_.reserve(arguments.size());\n    for (const Argument<F>& argument : arguments) {\n      plonk::GraphEvaluator<F> graph;\n\n      auto compress =\n          [&graph](\n              const std::vector<std::unique_ptr<Expression<F>>>& expressions) {\n            std::vector<plonk::ValueSource> parts = base::Map(\n                expressions,\n                [&graph](const std::unique_ptr<Expression<F>>& expression) {\n                  return graph.AddExpression(expression.get());\n                });\n            return graph.AddCalculation(plonk::Calculation::Horner(\n                plonk::ValueSource::ZeroConstant(), std::move(parts),\n                plonk::ValueSource::Theta()));\n          };\n\n      // A_compressed(X) = θᵐ⁻¹A₀(X) + θᵐ⁻²A₁(X) + ... + θAₘ₋₂(X) + Aₘ₋₁(X)\n      plonk::ValueSource compressed_input_coset =\n          compress(argument.input_expressions());\n      // S_compressed(X) = θᵐ⁻¹S₀(X) + θᵐ⁻²S₁(X) + ... + θSₘ₋₂(X) + Sₘ₋₁(X)\n      plonk::ValueSource compressed_table_coset =\n          compress(argument.table_expressions());\n\n      // A_compressed(X) + β\n      plonk::ValueSource left = graph.AddCalculation(plonk::Calculation::Add(\n          compressed_input_coset, plonk::ValueSource::Beta()));\n      // S_compressed(X) + γ\n      plonk::ValueSource right = graph.AddCalculation(plonk::Calculation::Add(\n          compressed_table_coset, plonk::ValueSource::Gamma()));\n      // (A_compressed(X) + β) * (S_compressed(X) + γ)\n      graph.AddCalculation(plonk::Calculation::Mul(left, right));\n\n      evaluators_.push_back(std::move(graph));\n    }\n  }\n\n  template <typename PS>\n  void Evaluate(plonk::CircuitPolynomialBuilder<PS>& builder,\n                absl::Span<F> chunk, size_t chunk_offset, size_t chunk_size) {\n    TRACE_EVENT(\"ProofGeneration\", \"Lookup::Halo2::Evaluator::Evaluate\");\n    for (size_t i = 0; i < evaluators_.size(); ++i) {\n      const plonk::GraphEvaluator<F>& ev = evaluators_[i];\n      const EvalsOrExtendedEvals& input_coset = input_cosets_[i];\n      const EvalsOrExtendedEvals& table_coset = table_cosets_[i];\n      const EvalsOrExtendedEvals& product_coset = product_cosets_[i];\n\n      plonk::EvaluationInput<EvalsOrExtendedEvals> evaluation_input =\n          builder.ExtractEvaluationInput(ev.CreateInitialIntermediates(),\n                                         ev.CreateEmptyRotations());\n\n      size_t start = chunk_offset * chunk_size;\n      for (size_t j = 0; j < chunk.size(); ++j) {\n        size_t idx = start + j;\n\n        F zero = F::Zero();\n        F table_value = ev.Evaluate(evaluation_input, idx, /*scale=*/1, zero);\n\n        RowIndex r_next = Rotation(1).GetIndex(idx, /*scale=*/1, builder.n_);\n        RowIndex r_prev = Rotation(-1).GetIndex(idx, /*scale=*/1, builder.n_);\n\n        F a_minus_s = input_coset[idx] - table_coset[idx];\n\n        // l_first(X) * (1 - z(X)) = 0\n        chunk[j] *= builder.y_;\n        chunk[j] += builder.l_first_[idx] * (F::One() - product_coset[idx]);\n\n        // l_last(X) * (z(X)² - z(X)) = 0\n        chunk[j] *= builder.y_;\n        chunk[j] += builder.l_last_[idx] *\n                    (product_coset[idx].Square() - product_coset[idx]);\n\n        // clang-format off\n        // A * (B - C) = 0 where\n        //  - A = 1 - (l_last(X) + l_blind(X))\n        //  - B = z(ωX) * (a'(X) + β) * (s'(X) + γ)\n        //  - C = z(X) * (θᵐ⁻¹ a₀(X) + ... + aₘ₋₁(X) + β) * (θᵐ⁻¹ s₀(X) + ... + sₘ₋₁(X) + γ)\n        // clang-format on\n        chunk[j] *= builder.y_;\n        chunk[j] +=\n            builder.l_active_row_[idx] *\n            (product_coset[r_next] * (input_coset[idx] + builder.beta_) *\n                 (table_coset[idx] + builder.gamma_) -\n             product_coset[idx] * table_value);\n\n        // Check that the first values in the permuted input expression and\n        // permuted fixed expression are the same.\n        // l_first(X) * (a'(X) - s'(X)) = 0\n        chunk[j] *= builder.y_;\n        chunk[j] += builder.l_first_[idx] * a_minus_s;\n\n        // Check that each value in the permuted lookup input expression is\n        // either equal to the value above it, or the value at the same\n        // index in the permuted table expression. (1 - (l_last + l_blind)) *\n        // (a′(X) − s′(X)) * (a′(X) − a′(ω⁻¹X)) = 0\n        chunk[j] *= builder.y_;\n        chunk[j] += builder.l_active_row_[idx] * a_minus_s *\n                    (input_coset[idx] - input_coset[r_prev]);\n      }\n    }\n  }\n\n  template <typename PS>\n  void UpdateCosets(plonk::CircuitPolynomialBuilder<PS>& builder,\n                    size_t circuit_idx) {\n    TRACE_EVENT(\"ProofGeneration\", \"Lookup::Halo2::Evaluator::UpdateCosets\");\n    using PCS = typename PS::PCS;\n    using Poly = typename PCS::Poly;\n    using Evals = typename PCS::Evals;\n    using LookupProver = Prover<Poly, Evals>;\n\n    size_t num_lookups =\n        builder.lookup_provers_[circuit_idx].grand_product_polys().size();\n    if (num_lookups == 0) return;\n\n    const LookupProver& prover = builder.lookup_provers_[circuit_idx];\n    product_cosets_.resize(num_lookups);\n    input_cosets_.resize(num_lookups);\n    table_cosets_.resize(num_lookups);\n    for (size_t i = 0; i < num_lookups; ++i) {\n      if constexpr (PS::kVendor == plonk::halo2::Vendor::kPSE) {\n        product_cosets_[i] = plonk::CoeffToExtended(\n            prover.grand_product_polys()[i].poly(), builder.extended_domain_);\n        input_cosets_[i] =\n            plonk::CoeffToExtended(prover.permuted_pairs()[i].input().poly(),\n                                   builder.extended_domain_);\n        table_cosets_[i] =\n            plonk::CoeffToExtended(prover.permuted_pairs()[i].table().poly(),\n                                   builder.extended_domain_);\n      } else {\n        product_cosets_[i] =\n            builder.coset_domain_->FFT(prover.grand_product_polys()[i].poly());\n        input_cosets_[i] = builder.coset_domain_->FFT(\n            prover.permuted_pairs()[i].input().poly());\n        table_cosets_[i] = builder.coset_domain_->FFT(\n            prover.permuted_pairs()[i].table().poly());\n      }\n    }\n  }\n\n private:\n  std::vector<plonk::GraphEvaluator<F>> evaluators_;\n  std::vector<EvalsOrExtendedEvals> product_cosets_;\n  std::vector<EvalsOrExtendedEvals> input_cosets_;\n  std::vector<EvalsOrExtendedEvals> table_cosets_;\n};\n\n}  // namespace tachyon::zk::lookup::halo2\n\n#endif  // TACHYON_ZK_LOOKUP_HALO2_EVALUATOR_H_\n"
  },
  {
    "path": "tachyon/zk/lookup/halo2/opening_point_set.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_LOOKUP_HALO2_OPENING_POINT_SET_H_\n#define TACHYON_ZK_LOOKUP_HALO2_OPENING_POINT_SET_H_\n\nnamespace tachyon::zk::lookup::halo2 {\n\ntemplate <typename F>\nstruct OpeningPointSet {\n  OpeningPointSet(const F& x, const F& x_prev, const F& x_next)\n      : x(x), x_prev(x_prev), x_next(x_next) {}\n\n  const F& x;\n  const F& x_prev;\n  const F& x_next;\n};\n\n}  // namespace tachyon::zk::lookup::halo2\n\n#endif  // TACHYON_ZK_LOOKUP_HALO2_OPENING_POINT_SET_H_\n"
  },
  {
    "path": "tachyon/zk/lookup/halo2/permute_expression_pair.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_LOOKUP_HALO2_PERMUTE_EXPRESSION_PAIR_H_\n#define TACHYON_ZK_LOOKUP_HALO2_PERMUTE_EXPRESSION_PAIR_H_\n\n#include <algorithm>\n#include <utility>\n#include <vector>\n\n#include \"absl/container/btree_map.h\"\n\n#include \"tachyon/base/parallelize.h\"\n#include \"tachyon/base/sort.h\"\n#include \"tachyon/math/base/parallelize_threshold.h\"\n#include \"tachyon/zk/base/entities/prover_base.h\"\n#include \"tachyon/zk/lookup/pair.h\"\n\nnamespace tachyon::zk::lookup::halo2 {\n\n// Given a vector of input values A and a vector of table values S,\n// this method permutes A and S to produce A' and S', such that:\n// - like values in A' are vertically adjacent to each other; and\n// - the first row in a sequence of like values in A' is the row\n//   that has the corresponding value in S'.\n// This method returns (A', S') if no errors are encountered.\ntemplate <typename PCS, typename Evals, typename F = typename Evals::Field>\n[[nodiscard]] bool PermuteExpressionPair(ProverBase<PCS>* prover,\n                                         const Pair<Evals>& in,\n                                         Pair<Evals>* out) {\n  size_t domain_size = prover->domain()->size();\n  RowIndex usable_rows = prover->GetUsableRows();\n\n  std::vector<F> permuted_input_expressions = in.input().evaluations();\n\n  // sort input lookup expression values\n  base::UnstableSort(permuted_input_expressions.begin(),\n                     permuted_input_expressions.begin() + usable_rows);\n\n  // a map of each unique element in the table expression and its count\n  absl::btree_map<F, RowIndex> leftover_table_map;\n\n  for (RowIndex i = 0; i < usable_rows; ++i) {\n    const F& coeff = in.table()[i];\n    // if key doesn't exist, insert the key and value 1 for the key.\n    auto [it, inserted] = leftover_table_map.try_emplace(coeff, RowIndex{1});\n    // no inserted value, meaning the key exists.\n    if (!inserted) {\n      // Increase value by 1 if not inserted.\n      ++(it->second);\n    }\n  }\n\n  std::vector<F> permuted_table_expressions(domain_size);\n  // NOTE(batzor): First |usable_rows| elements will be initialized afterwards.\n  auto init_span = absl::MakeSpan(permuted_table_expressions)\n                       .last(domain_size - usable_rows);\n  base::ParallelizeFill(init_span, F::Zero(),\n                        /*threshold=*/math::ParallelizeThreshold::kFieldInit);\n\n  std::vector<RowIndex> repeated_input_rows;\n  repeated_input_rows.reserve(usable_rows - 1);\n  for (RowIndex row = 0; row < usable_rows; ++row) {\n    const F& input_value = permuted_input_expressions[row];\n\n    // ref: https://zcash.github.io/halo2/design/proving-system/lookup.html\n    //\n    // Lookup Argument must satisfy these 2 constraints.\n    //\n    // - constraint 1: l_first(X) * (A'(X) - S'(x)) = 0\n    // - constraint 2: (A'(X) - S'(x)) * (A'(X) - A'(ω⁻¹X)) = 0\n    //\n    // - What 'row == 0' condition means: l_first(x) == 1.\n    // To satisfy constraint 1, A'(x) - S'(x) must be 0.\n    // => checking if A'(x) == S'(x)\n    // - What 'input_value != permuted_input_expressions[row-1]' condition\n    //   means: (A'(x) - A'(ω⁻¹x)) != 0.\n    // To satisfy constraint 2, A'(x) - S'(x) must be 0.\n    // => checking if A'(x) == S'(x)\n    //\n    // Example\n    //\n    // Assume that\n    //  * in.input.evaluations() = [1,2,1,5]\n    //  * in.table.evaluations() = [1,2,4,5]\n    //\n    // Result after for loop\n    //\n    //                   A'                      S'\n    //               --------                --------\n    //              |    1   |              |    1   |\n    //               --------                --------\n    //              |    1   |              |    4   |\n    //               --------                --------\n    //              |    2   |              |    2   |\n    //               --------                --------\n    //              |    5   |              |    5   |\n    //               --------                --------\n    // we can see that elements of A' {1,2,5} is in S' {1,4,2,5}\n    //\n    if (row == 0 || input_value != permuted_input_expressions[row - 1]) {\n      // Assign S'(x) with A'(x).\n      permuted_table_expressions[row] = input_value;\n\n      // remove one instance of input_value from |leftover_table_map|.\n      auto it = leftover_table_map.find(input_value);\n      // if input value is not found, return error\n      if (it == leftover_table_map.end()) {\n        LOG(ERROR) << \"input(\" << input_value.ToString()\n                   << \") is not found in table\";\n        return false;\n      }\n\n      // input value found, check if the value > 0.\n      // then decrement the value by 1\n      CHECK_GT(it->second--, RowIndex{0});\n    } else {\n      repeated_input_rows.push_back(row);\n    }\n  }\n\n  // populate permuted table at unfilled rows with leftover table elements\n  for (auto it = leftover_table_map.begin(); it != leftover_table_map.end();\n       ++it) {\n    const F& coeff = it->first;\n    const RowIndex count = it->second;\n\n    for (RowIndex i = 0; i < count; ++i) {\n      CHECK(!repeated_input_rows.empty());\n      RowIndex row = repeated_input_rows.back();\n      permuted_table_expressions[row] = coeff;\n      repeated_input_rows.pop_back();\n    }\n  }\n\n  CHECK(repeated_input_rows.empty());\n\n  Evals input(std::move(permuted_input_expressions));\n  Evals table(std::move(permuted_table_expressions));\n\n  prover->blinder().Blind(input, /*include_last_row=*/true);\n  prover->blinder().Blind(table, /*include_last_row=*/true);\n\n  *out = {std::move(input), std::move(table)};\n  return true;\n}\n\n}  // namespace tachyon::zk::lookup::halo2\n\n#endif  // TACHYON_ZK_LOOKUP_HALO2_PERMUTE_EXPRESSION_PAIR_H_\n"
  },
  {
    "path": "tachyon/zk/lookup/halo2/permute_expression_pair_unittest.cc",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#include \"tachyon/zk/lookup/halo2/permute_expression_pair.h\"\n\n#include <algorithm>\n#include <optional>\n#include <utility>\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/random.h\"\n#include \"tachyon/zk/plonk/halo2/bn254_shplonk_prover_test.h\"\n\nnamespace tachyon::zk::lookup::halo2 {\n\nclass PermuteExpressionPairTest : public plonk::halo2::BN254SHPlonkProverTest {\n};\n\nTEST_F(PermuteExpressionPairTest, PermuteExpressionPairTest) {\n  prover_->blinder().set_blinding_factors(5);\n  size_t n = prover_->pcs().N();\n  RowIndex usable_rows = prover_->GetUsableRows();\n\n  std::vector<F> table_evals =\n      base::CreateVector(n, []() { return F::Random(); });\n\n  std::vector<F> input_evals =\n      base::CreateVector(n, [usable_rows, &table_evals]() {\n        return table_evals[base::Uniform(\n            base::Range<RowIndex>::Until(usable_rows))];\n      });\n\n  Pair<Evals> input(Evals(std::move(input_evals)),\n                    Evals(std::move(table_evals)));\n  Pair<Evals> output;\n  ASSERT_TRUE(PermuteExpressionPair(prover_.get(), input, &output));\n\n  // sanity check brought from halo2\n  std::optional<F> last;\n  for (size_t i = 0; i < usable_rows; ++i) {\n    const F& perm_input_expr = output.input()[i];\n    const F& perm_table_coeff = output.table()[i];\n\n    if (perm_input_expr != perm_table_coeff) {\n      EXPECT_EQ(perm_input_expr, last.value());\n    }\n    last = perm_input_expr;\n  }\n}\n\nTEST_F(PermuteExpressionPairTest, PermuteExpressionPairTestWrong) {\n  // set input_evals not included within table_evals;\n  size_t n = prover_->pcs().N();\n  std::vector<F> input_evals =\n      base::CreateVector(n, [](size_t i) { return F(i * 2); });\n\n  std::vector<F> table_evals =\n      base::CreateVector(n, [](size_t i) { return F(i * 3); });\n\n  Pair<Evals> input = {Evals(std::move(input_evals)),\n                       Evals(std::move(table_evals))};\n  Pair<Evals> output;\n  ASSERT_FALSE(PermuteExpressionPair(prover_.get(), input, &output));\n}\n\n}  // namespace tachyon::zk::lookup::halo2\n"
  },
  {
    "path": "tachyon/zk/lookup/halo2/prover.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_LOOKUP_HALO2_PROVER_H_\n#define TACHYON_ZK_LOOKUP_HALO2_PROVER_H_\n\n#include <stddef.h>\n\n#include <vector>\n\n#include \"absl/types/span.h\"\n\n#include \"tachyon/base/profiler.h\"\n#include \"tachyon/crypto/commitments/polynomial_openings.h\"\n#include \"tachyon/zk/base/blinded_polynomial.h\"\n#include \"tachyon/zk/base/entities/prover_base.h\"\n#include \"tachyon/zk/lookup/argument.h\"\n#include \"tachyon/zk/lookup/halo2/opening_point_set.h\"\n#include \"tachyon/zk/lookup/pair.h\"\n#include \"tachyon/zk/plonk/base/multi_phase_ref_table.h\"\n#include \"tachyon/zk/plonk/expressions/proving_evaluator.h\"\n\nnamespace tachyon::zk::lookup::halo2 {\n\ntemplate <typename Poly, typename Evals>\nclass Prover {\n public:\n  using F = typename Poly::Field;\n\n  const std::vector<Pair<Evals>>& compressed_pairs() const {\n    return compressed_pairs_;\n  }\n  const std::vector<Pair<BlindedPolynomial<Poly, Evals>>>& permuted_pairs()\n      const {\n    return permuted_pairs_;\n  }\n  const std::vector<BlindedPolynomial<Poly, Evals>>& grand_product_polys()\n      const {\n    return grand_product_polys_;\n  }\n\n  template <typename Domain>\n  static void BatchCompressPairs(\n      std::vector<Prover>& lookup_provers, const Domain* domain,\n      const std::vector<Argument<F>>& arguments, const F& theta,\n      const std::vector<plonk::MultiPhaseRefTable<Evals>>& tables);\n\n  template <typename PCS>\n  static void BatchPermutePairs(std::vector<Prover>& lookup_provers,\n                                ProverBase<PCS>* prover) {\n    TRACE_EVENT(\"ProofGeneration\", \"Lookup::Halo2::Prover::BatchPermutePairs\");\n    for (Prover& lookup_prover : lookup_provers) {\n      lookup_prover.PermutePairs(prover);\n    }\n  }\n\n  constexpr static size_t GetNumPermutedPairsCommitments(\n      const std::vector<Prover>& lookup_provers) {\n    if (lookup_provers.empty()) return 0;\n    return lookup_provers.size() * lookup_provers[0].permuted_pairs_.size() * 2;\n  }\n\n  template <typename PCS>\n  static void BatchCommitPermutedPairs(\n      const std::vector<Prover>& lookup_provers, ProverBase<PCS>* prover,\n      size_t& commit_idx);\n\n  template <typename PCS>\n  static void BatchCreateGrandProductPolys(std::vector<Prover>& lookup_provers,\n                                           ProverBase<PCS>* prover,\n                                           const F& beta, const F& gamma) {\n    TRACE_EVENT(\"ProofGeneration\",\n                \"Lookup::Halo2::Prover::BatchCreateGrandProductPolys\");\n    for (Prover& lookup_prover : lookup_provers) {\n      lookup_prover.CreateGrandProductPolys(prover, beta, gamma);\n    }\n  }\n\n  constexpr static size_t GetNumGrandProductPolysCommitments(\n      const std::vector<Prover>& lookup_provers) {\n    if (lookup_provers.empty()) return 0;\n    return lookup_provers.size() *\n           lookup_provers[0].grand_product_polys_.size();\n  }\n\n  template <typename PCS>\n  static void BatchCommitGrandProductPolys(\n      const std::vector<Prover>& lookup_provers, ProverBase<PCS>* prover,\n      size_t& commit_idx);\n\n  template <typename Domain>\n  static void TransformEvalsToPoly(std::vector<Prover>& lookup_provers,\n                                   const Domain* domain) {\n    TRACE_EVENT(\"ProofGeneration\",\n                \"Lookup::Halo2::Prover::TransformEvalsToPoly\");\n    VLOG(2) << \"Transform lookup virtual columns to polys\";\n    for (Prover& lookup_prover : lookup_provers) {\n      lookup_prover.TransformEvalsToPoly(domain);\n    }\n  }\n\n  template <typename PCS>\n  static void BatchEvaluate(const std::vector<Prover>& lookup_provers,\n                            ProverBase<PCS>* prover,\n                            const OpeningPointSet<F>& point_set) {\n    TRACE_EVENT(\"ProofGeneration\", \"Lookup::Halo2::Prover::BatchEvaluate\");\n    for (const Prover& lookup_prover : lookup_provers) {\n      lookup_prover.Evaluate(prover, point_set);\n    }\n  }\n\n  void Open(const OpeningPointSet<F>& point_set,\n            std::vector<crypto::PolynomialOpening<Poly>>& openings) const;\n\n private:\n  template <typename Domain>\n  static Pair<Evals> CompressPair(\n      const Domain* domain, const Argument<F>& argument, const F& theta,\n      const plonk::ProvingEvaluator<Evals>& evaluator_tpl);\n\n  template <typename PCS>\n  static Pair<BlindedPolynomial<Poly, Evals>> PermutePair(\n      ProverBase<PCS>* prover, const Pair<Evals>& compressed_pair);\n\n  template <typename PCS>\n  static BlindedPolynomial<Poly, Evals> CreateGrandProductPoly(\n      ProverBase<PCS>* prover, const Pair<Evals>& compressed_pair,\n      const Pair<BlindedPolynomial<Poly, Evals>>& permuted_pair, const F& beta,\n      const F& gamma);\n\n  template <typename Domain>\n  void CompressPairs(const Domain* domain,\n                     const std::vector<Argument<F>>& arguments, const F& theta,\n                     const plonk::ProvingEvaluator<Evals>& evaluator_tpl);\n\n  template <typename PCS>\n  void PermutePairs(ProverBase<PCS>* prover);\n\n  template <typename PCS>\n  void CreateGrandProductPolys(ProverBase<PCS>* prover, const F& beta,\n                               const F& gamma);\n\n  template <typename Domain>\n  void TransformEvalsToPoly(const Domain* domain);\n\n  template <typename PCS>\n  void Evaluate(ProverBase<PCS>* prover,\n                const OpeningPointSet<F>& point_set) const;\n\n  static std::function<F(RowIndex)> CreateNumeratorCallback(\n      const Pair<Evals>& compressed_pair, const F& beta, const F& gamma);\n\n  static std::function<F(RowIndex)> CreateDenominatorCallback(\n      const Pair<BlindedPolynomial<Poly, Evals>>& permuted_pair, const F& beta,\n      const F& gamma);\n\n  // A_compressedᵢ(X), S_compressedᵢ(X)\n  std::vector<Pair<Evals>> compressed_pairs_;\n  // A'ᵢ(X), S'ᵢ(X)\n  std::vector<Pair<BlindedPolynomial<Poly, Evals>>> permuted_pairs_;\n  // Zₗ,ᵢ(X)\n  std::vector<BlindedPolynomial<Poly, Evals>> grand_product_polys_;\n};\n\n}  // namespace tachyon::zk::lookup::halo2\n\n#include \"tachyon/zk/lookup/halo2/prover_impl.h\"\n\n#endif  // TACHYON_ZK_LOOKUP_HALO2_PROVER_H_\n"
  },
  {
    "path": "tachyon/zk/lookup/halo2/prover_impl.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_LOOKUP_HALO2_PROVER_IMPL_H_\n#define TACHYON_ZK_LOOKUP_HALO2_PROVER_IMPL_H_\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/profiler.h\"\n#include \"tachyon/base/ref.h\"\n#include \"tachyon/zk/lookup/halo2/permute_expression_pair.h\"\n#include \"tachyon/zk/lookup/halo2/prover.h\"\n#include \"tachyon/zk/plonk/expressions/compress_expression.h\"\n#include \"tachyon/zk/plonk/permutation/grand_product_argument.h\"\n\nnamespace tachyon::zk::lookup::halo2 {\n\n// static\ntemplate <typename Poly, typename Evals>\ntemplate <typename Domain>\nPair<Evals> Prover<Poly, Evals>::CompressPair(\n    const Domain* domain, const Argument<F>& argument, const F& theta,\n    const plonk::ProvingEvaluator<Evals>& evaluator_tpl) {\n  TRACE_EVENT(\"Utils\", \"CompressPair\");\n  // A_compressedᵢ(X) = θᵐ⁻¹A₀(X) + θᵐ⁻²A₁(X) + ... + θAₘ₋₂(X) + Aₘ₋₁(X)\n  Evals compressed_input = plonk::CompressExpressions(\n      domain, argument.input_expressions(), theta, evaluator_tpl);\n\n  // S_compressedᵢ(X) = θᵐ⁻¹S₀(X) + θᵐ⁻²S₁(X) + ... + θSₘ₋₂(X) + Sₘ₋₁(X)\n  Evals compressed_table = plonk::CompressExpressions(\n      domain, argument.table_expressions(), theta, evaluator_tpl);\n\n  return {std::move(compressed_input), std::move(compressed_table)};\n}\n\ntemplate <typename Poly, typename Evals>\ntemplate <typename Domain>\nvoid Prover<Poly, Evals>::CompressPairs(\n    const Domain* domain, const std::vector<Argument<F>>& arguments,\n    const F& theta, const plonk::ProvingEvaluator<Evals>& evaluator_tpl) {\n  TRACE_EVENT(\"Utils\", \"CompressPairs\");\n  compressed_pairs_ = base::Map(\n      arguments, [domain, &theta, &evaluator_tpl](const Argument<F>& argument) {\n        return CompressPair(domain, argument, theta, evaluator_tpl);\n      });\n}\n\n// static\ntemplate <typename Poly, typename Evals>\ntemplate <typename Domain>\nvoid Prover<Poly, Evals>::BatchCompressPairs(\n    std::vector<Prover>& lookup_provers, const Domain* domain,\n    const std::vector<Argument<F>>& arguments, const F& theta,\n    const std::vector<plonk::MultiPhaseRefTable<Evals>>& tables) {\n  TRACE_EVENT(\"ProofGeneration\", \"Lookup::Halo2::Prover::BatchCompressPairs\");\n  CHECK_EQ(lookup_provers.size(), tables.size());\n  // NOTE(chokobole): It's safe to downcast because domain is already checked.\n  int32_t n = static_cast<int32_t>(domain->size());\n  for (size_t i = 0; i < lookup_provers.size(); ++i) {\n    plonk::ProvingEvaluator<Evals> proving_evaluator(0, n, 1, tables[i]);\n    lookup_provers[i].CompressPairs(domain, arguments, theta,\n                                    proving_evaluator);\n  }\n}\n\n// static\ntemplate <typename Poly, typename Evals>\ntemplate <typename PCS>\nPair<BlindedPolynomial<Poly, Evals>> Prover<Poly, Evals>::PermutePair(\n    ProverBase<PCS>* prover, const Pair<Evals>& compressed_pair) {\n  TRACE_EVENT(\"Utils\", \"PermutePair\");\n  // A'ᵢ(X), S'ᵢ(X)\n  Pair<Evals> permuted_pair;\n  CHECK(PermuteExpressionPair(prover, compressed_pair, &permuted_pair));\n\n  F input_blind = prover->blinder().Generate();\n  F table_blind = prover->blinder().Generate();\n  return {{std::move(permuted_pair).TakeInput(), std::move(input_blind)},\n          {std::move(permuted_pair).TakeTable(), std::move(table_blind)}};\n}\n\ntemplate <typename Poly, typename Evals>\ntemplate <typename PCS>\nvoid Prover<Poly, Evals>::PermutePairs(ProverBase<PCS>* prover) {\n  TRACE_EVENT(\"Utils\", \"PermutePairs\");\n  permuted_pairs_ = base::Map(compressed_pairs_,\n                              [prover](const Pair<Evals>& compressed_pair) {\n                                return PermutePair(prover, compressed_pair);\n                              });\n}\n\n// static\ntemplate <typename Poly, typename Evals>\ntemplate <typename PCS>\nvoid Prover<Poly, Evals>::BatchCommitPermutedPairs(\n    const std::vector<Prover>& lookup_provers, ProverBase<PCS>* prover,\n    size_t& commit_idx) {\n  TRACE_EVENT(\"ProofGeneration\",\n              \"Lookup::Halo2::Prover::BatchCommitPermutedPairs\");\n  if (lookup_provers.empty()) return;\n\n  if constexpr (PCS::kSupportsBatchMode) {\n    for (const Prover& lookup_prover : lookup_provers) {\n      for (const Pair<BlindedPolynomial<Poly, Evals>>& permuted_pair :\n           lookup_prover.permuted_pairs_) {\n        prover->BatchCommitAt(permuted_pair.input().evals(), commit_idx++);\n        prover->BatchCommitAt(permuted_pair.table().evals(), commit_idx++);\n      }\n    }\n  } else {\n    for (const Prover& lookup_prover : lookup_provers) {\n      for (const Pair<BlindedPolynomial<Poly, Evals>>& permuted_pair :\n           lookup_prover.permuted_pairs_) {\n        prover->CommitAndWriteToProof(permuted_pair.input().evals());\n        prover->CommitAndWriteToProof(permuted_pair.table().evals());\n      }\n    }\n  }\n}\n\n// static\ntemplate <typename Poly, typename Evals>\ntemplate <typename PCS>\nBlindedPolynomial<Poly, Evals> Prover<Poly, Evals>::CreateGrandProductPoly(\n    ProverBase<PCS>* prover, const Pair<Evals>& compressed_pair,\n    const Pair<BlindedPolynomial<Poly, Evals>>& permuted_pair, const F& beta,\n    const F& gamma) {\n  TRACE_EVENT(\"Utils\", \"CreateGrandProductPoly\");\n  return {plonk::GrandProductArgument::CreatePolySerial(\n              prover, CreateNumeratorCallback(compressed_pair, beta, gamma),\n              CreateDenominatorCallback(permuted_pair, beta, gamma)),\n          prover->blinder().Generate()};\n}\n\ntemplate <typename Poly, typename Evals>\ntemplate <typename PCS>\nvoid Prover<Poly, Evals>::CreateGrandProductPolys(ProverBase<PCS>* prover,\n                                                  const F& beta,\n                                                  const F& gamma) {\n  TRACE_EVENT(\"Utils\", \"CreateGrandProductPolys\");\n  // Zₗ,ᵢ(X)\n  CHECK_EQ(compressed_pairs_.size(), permuted_pairs_.size());\n  grand_product_polys_.resize(compressed_pairs_.size());\n\n  // NOTE(dongchangYoo): do not change this code to parallelized logic.\n  grand_product_polys_ = base::Map(\n      compressed_pairs_, [this, prover, &beta, &gamma](\n                             size_t i, const Pair<Evals>& compressed_pair) {\n        return CreateGrandProductPoly(prover, compressed_pair,\n                                      permuted_pairs_[i], beta, gamma);\n      });\n  compressed_pairs_.clear();\n}\n\n// static\ntemplate <typename Poly, typename Evals>\ntemplate <typename PCS>\nvoid Prover<Poly, Evals>::BatchCommitGrandProductPolys(\n    const std::vector<Prover>& lookup_provers, ProverBase<PCS>* prover,\n    size_t& commit_idx) {\n  TRACE_EVENT(\"ProofGeneration\",\n              \"Lookup::Halo2::Prover::BatchCommitGrandProductPolys\");\n  if (lookup_provers.empty()) return;\n\n  if constexpr (PCS::kSupportsBatchMode) {\n    for (const Prover& lookup_prover : lookup_provers) {\n      for (const BlindedPolynomial<Poly, Evals>& grand_product_poly :\n           lookup_prover.grand_product_polys_) {\n        prover->BatchCommitAt(grand_product_poly.evals(), commit_idx++);\n      }\n    }\n  } else {\n    for (const Prover& lookup_prover : lookup_provers) {\n      for (const BlindedPolynomial<Poly, Evals>& grand_product_poly :\n           lookup_prover.grand_product_polys_) {\n        prover->CommitAndWriteToProof(grand_product_poly.evals());\n      }\n    }\n  }\n}\n\ntemplate <typename Poly, typename Evals>\ntemplate <typename Domain>\nvoid Prover<Poly, Evals>::TransformEvalsToPoly(const Domain* domain) {\n  TRACE_EVENT(\"Utils\", \"TransformEvalsToPoly\");\n  for (Pair<BlindedPolynomial<Poly, Evals>>& permuted_pair : permuted_pairs_) {\n    permuted_pair.input().TransformEvalsToPoly(domain);\n    permuted_pair.table().TransformEvalsToPoly(domain);\n  }\n  for (BlindedPolynomial<Poly, Evals>& grand_product_poly :\n       grand_product_polys_) {\n    grand_product_poly.TransformEvalsToPoly(domain);\n  }\n}\n\ntemplate <typename Poly, typename Evals>\ntemplate <typename PCS>\nvoid Prover<Poly, Evals>::Evaluate(ProverBase<PCS>* prover,\n                                   const OpeningPointSet<F>& point_set) const {\n  TRACE_EVENT(\"Utils\", \"Evaluate\");\n  size_t size = grand_product_polys_.size();\n  CHECK_EQ(size, permuted_pairs_.size());\n\n#define EVALUATE(polynomial, point) \\\n  prover->EvaluateAndWriteToProof(polynomial.poly(), point_set.point)\n\n  // THE ORDER IS IMPORTANT!! DO NOT CHANGE!\n  // See\n  // https://github.com/kroma-network/halo2/blob/7d0a369/halo2_proofs/src/plonk/lookup/prover.rs#L309-L337.\n  for (size_t i = 0; i < size; ++i) {\n    const BlindedPolynomial<Poly, Evals>& grand_product_poly =\n        grand_product_polys_[i];\n    const Pair<BlindedPolynomial<Poly, Evals>>& permuted_pair =\n        permuted_pairs_[i];\n\n    // Zₗ,ᵢ(x)\n    EVALUATE(grand_product_poly, x);\n    // Zₗ,ᵢ(ω * x)\n    EVALUATE(grand_product_poly, x_next);\n    // A'ᵢ(x)\n    EVALUATE(permuted_pair.input(), x);\n    // A'ᵢ(ω⁻¹ * x)\n    EVALUATE(permuted_pair.input(), x_prev);\n    // S'ᵢ(x)\n    EVALUATE(permuted_pair.table(), x);\n  }\n#undef EVALUATE\n}\n\ntemplate <typename Poly, typename Evals>\nvoid Prover<Poly, Evals>::Open(\n    const OpeningPointSet<F>& point_set,\n    std::vector<crypto::PolynomialOpening<Poly>>& openings) const {\n  TRACE_EVENT(\"ProofGeneration\", \"Lookup::Halo2::Prover::Open\");\n  size_t size = grand_product_polys_.size();\n  CHECK_EQ(size, permuted_pairs_.size());\n\n#define OPENING(polynomial, point)                            \\\n  base::Ref<const Poly>(&polynomial.poly()), point_set.point, \\\n      polynomial.poly().Evaluate(point_set.point)\n\n  // THE ORDER IS IMPORTANT!! DO NOT CHANGE!\n  // See\n  // https://github.com/kroma-network/halo2/blob/7d0a369/halo2_proofs/src/plonk/lookup/prover.rs#L340-L381.\n  for (size_t i = 0; i < size; ++i) {\n    const BlindedPolynomial<Poly, Evals>& grand_product_poly =\n        grand_product_polys_[i];\n    const Pair<BlindedPolynomial<Poly, Evals>>& permuted_pair =\n        permuted_pairs_[i];\n\n    // Zₗ,ᵢ(x)\n    openings.emplace_back(OPENING(grand_product_poly, x));\n    // A'ᵢ(x)\n    openings.emplace_back(OPENING(permuted_pair.input(), x));\n    // S'ᵢ(x)\n    openings.emplace_back(OPENING(permuted_pair.table(), x));\n    // A'ᵢ(ω⁻¹ * x)\n    openings.emplace_back(OPENING(permuted_pair.input(), x_prev));\n    // Zₗ,ᵢ(ω * x)\n    openings.emplace_back(OPENING(grand_product_poly, x_next));\n  }\n#undef OPENING\n}\n\n// static\ntemplate <typename Poly, typename Evals>\nstd::function<typename Poly::Field(RowIndex)>\nProver<Poly, Evals>::CreateNumeratorCallback(const Pair<Evals>& compressed_pair,\n                                             const F& beta, const F& gamma) {\n  TRACE_EVENT(\"ProofGeneration\",\n              \"Lookup::Halo2::Prover::CreateNumeratorCallback\");\n  // (A_compressedᵢ(x) + β) * (S_compressedᵢ(x) + γ)\n  return [&compressed_pair, &beta, &gamma](RowIndex row_index) {\n    return (compressed_pair.input()[row_index] + beta) *\n           (compressed_pair.table()[row_index] + gamma);\n  };\n}\n\n// static\ntemplate <typename Poly, typename Evals>\nstd::function<typename Poly::Field(RowIndex)>\nProver<Poly, Evals>::CreateDenominatorCallback(\n    const Pair<BlindedPolynomial<Poly, Evals>>& permuted_pair, const F& beta,\n    const F& gamma) {\n  TRACE_EVENT(\"ProofGeneration\",\n              \"Lookup::Halo2::Prover::CreateDenominatorCallback\");\n  return [&permuted_pair, &beta, &gamma](RowIndex row_index) {\n    // (A'ᵢ(x) + β) * (S'ᵢ(x) + γ)\n    return (permuted_pair.input().evals()[row_index] + beta) *\n           (permuted_pair.table().evals()[row_index] + gamma);\n  };\n}\n\n}  // namespace tachyon::zk::lookup::halo2\n\n#endif  // TACHYON_ZK_LOOKUP_HALO2_PROVER_IMPL_H_\n"
  },
  {
    "path": "tachyon/zk/lookup/halo2/scheme.h",
    "content": "#ifndef TACHYON_ZK_LOOKUP_HALO2_SCHEME_H_\n#define TACHYON_ZK_LOOKUP_HALO2_SCHEME_H_\n\n#include \"tachyon/zk/lookup/type.h\"\n\nnamespace tachyon::zk::lookup::halo2 {\n\ntemplate <typename _Poly, typename _Evals, typename _Commitment,\n          typename _ExtendedPoly, typename _ExtendedEvals>\nstruct Scheme {\n  using Poly = _Poly;\n  using Evals = _Evals;\n  using Commitment = _Commitment;\n  using ExtendedPoly = _ExtendedPoly;\n  using ExtendedEvals = _ExtendedEvals;\n  using Field = typename Poly::Field;\n\n  constexpr static Type kType = Type::kHalo2;\n};\n\n}  // namespace tachyon::zk::lookup::halo2\n\n#endif  // TACHYON_ZK_LOOKUP_HALO2_SCHEME_H_\n"
  },
  {
    "path": "tachyon/zk/lookup/halo2/utils.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_LOOKUP_HALO2_UTILS_H_\n#define TACHYON_ZK_LOOKUP_HALO2_UTILS_H_\n\n#include <stddef.h>\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/zk/lookup/type.h\"\n\nnamespace tachyon::zk::lookup::halo2 {\n\nconstexpr size_t GetNumEvals(Type lookup_type, size_t num_circuits,\n                             size_t num_lookups) {\n  switch (lookup_type) {\n    case Type::kHalo2:\n      return num_circuits * num_lookups * 5;\n    case Type::kLogDerivativeHalo2:\n      return num_circuits * num_lookups * 3;\n  }\n  NOTREACHED();\n  return 0;\n}\n\nconstexpr size_t GetNumOpenings(Type lookup_type, size_t num_circuits,\n                                size_t num_lookups) {\n  switch (lookup_type) {\n    case Type::kHalo2:\n      return num_circuits * num_lookups * 5;\n    case Type::kLogDerivativeHalo2:\n      return num_circuits * num_lookups * 3;\n  }\n  NOTREACHED();\n  return 0;\n}\n\n}  // namespace tachyon::zk::lookup::halo2\n\n#endif  // TACHYON_ZK_LOOKUP_HALO2_UTILS_H_\n"
  },
  {
    "path": "tachyon/zk/lookup/halo2/verifier.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_LOOKUP_HALO2_VERIFIER_H_\n#define TACHYON_ZK_LOOKUP_HALO2_VERIFIER_H_\n\n#include <memory>\n#include <vector>\n\n#include \"tachyon/crypto/commitments/polynomial_openings.h\"\n#include \"tachyon/zk/lookup/argument.h\"\n#include \"tachyon/zk/lookup/halo2/opening_point_set.h\"\n#include \"tachyon/zk/lookup/halo2/verifier_data.h\"\n#include \"tachyon/zk/plonk/base/l_values.h\"\n#include \"tachyon/zk/plonk/expressions/verifying_evaluator.h\"\n#include \"tachyon/zk/plonk/halo2/proof.h\"\n\nnamespace tachyon::zk::lookup::halo2 {\n\ntemplate <typename F, typename C>\nclass Verifier {\n public:\n  using Proof = plonk::halo2::Halo2Proof<F, C>;\n\n  Verifier(const Proof& proof, size_t circuit_idx)\n      : data_(proof.ToLookupVerifierData(circuit_idx)) {}\n\n  Verifier(const Proof& proof, size_t circuit_idx,\n           const plonk::LValues<F>& l_values)\n      : data_(proof.ToLookupVerifierData(circuit_idx)), l_values_(&l_values) {}\n\n  void Evaluate(const std::vector<Argument<F>>& arguments,\n                std::vector<F>& evals) {\n    plonk::VerifyingEvaluator<F> evaluator(data_);\n\n    F active_rows = F::One() - (l_values_->last + l_values_->blind);\n    for (size_t i = 0; i < data_.grand_product_commitments.size(); ++i) {\n      // l_first(x) * (1 - Zₗ,ᵢ(x)) = 0\n      evals.push_back(l_values_->first *\n                      (F::One() - data_.grand_product_evals[i]));\n      // l_last(x) * (Zₗ,ᵢ(x)² - Zₗ,ᵢ(x)) = 0\n      evals.push_back(l_values_->last * (data_.grand_product_evals[i].Square() -\n                                         data_.grand_product_evals[i]));\n      // (1 - (l_last(x) + l_blind(x))) * (\n      //  Zₗ,ᵢ(ω * x) * (A'ᵢ(x) + β) * (S'ᵢ(x) + γ) -\n      //  Zₗ,ᵢ(x) * (A_compressedᵢ(x) + β) * (S_compressedᵢ(x) + γ)\n      // ) = 0\n      evals.push_back(active_rows *\n                      CreateGrandProductEvaluation(i, arguments[i], evaluator));\n      // l_first(x) * (A'ᵢ(x) - S'ᵢ(x)) = 0\n      evals.push_back(l_values_->first * (data_.permuted_input_evals[i] -\n                                          data_.permuted_table_evals[i]));\n      // (1 - (l_last(x) + l_blind(x))) *\n      // (A'ᵢ(x) − S'ᵢ(x)) * (A'ᵢ(x) − A'ᵢ(ω⁻¹ * x)) = 0\n      evals.push_back(\n          active_rows *\n          (data_.permuted_input_evals[i] - data_.permuted_table_evals[i]) *\n          (data_.permuted_input_evals[i] - data_.permuted_input_prev_evals[i]));\n    }\n  }\n\n  template <typename Poly>\n  void Open(const OpeningPointSet<F>& point_set,\n            std::vector<crypto::PolynomialOpening<Poly, C>>& openings) const {\n    if (data_.grand_product_commitments.empty()) return;\n\n#define OPENING(commitment, point, eval) \\\n  base::Ref<const C>(&data_.commitment), point_set.point, data_.eval\n\n    for (size_t i = 0; i < data_.grand_product_commitments.size(); ++i) {\n      openings.emplace_back(\n          OPENING(grand_product_commitments[i], x, grand_product_evals[i]));\n      openings.emplace_back(\n          OPENING(permuted_commitments[i].input(), x, permuted_input_evals[i]));\n      openings.emplace_back(\n          OPENING(permuted_commitments[i].table(), x, permuted_table_evals[i]));\n      openings.emplace_back(OPENING(permuted_commitments[i].input(), x_prev,\n                                    permuted_input_prev_evals[i]));\n      openings.emplace_back(OPENING(grand_product_commitments[i], x_next,\n                                    grand_product_next_evals[i]));\n    }\n\n#undef OPENING\n  }\n\n private:\n  F CompressExpressions(\n      const std::vector<std::unique_ptr<Expression<F>>>& expressions,\n      plonk::VerifyingEvaluator<F>& evaluator) const {\n    F compressed_value = F::Zero();\n    for (const std::unique_ptr<Expression<F>>& expression : expressions) {\n      compressed_value *= data_.theta;\n      compressed_value += evaluator.Evaluate(expression.get());\n    }\n    return compressed_value;\n  }\n\n  F CreateGrandProductEvaluation(size_t i, const Argument<F>& argument,\n                                 plonk::VerifyingEvaluator<F>& evaluator) {\n    // Zₗ,ᵢ(ω * x) * (A'ᵢ(x) + β) * (S'ᵢ(x) + γ)\n    // - Zₗ,ᵢ(x) * (A_compressedᵢ(x) + β) * (S_compressedᵢ(x) + γ)\n    F left = data_.grand_product_next_evals[i] *\n             (data_.permuted_input_evals[i] + data_.beta) *\n             (data_.permuted_table_evals[i] + data_.gamma);\n    F compressed_input_expression =\n        CompressExpressions(argument.input_expressions(), evaluator);\n    F compressed_table_expression =\n        CompressExpressions(argument.table_expressions(), evaluator);\n    F right = data_.grand_product_evals[i] *\n              (compressed_input_expression + data_.beta) *\n              (compressed_table_expression + data_.gamma);\n    return left - right;\n  }\n\n  VerifierData<F, C> data_;\n  const plonk::LValues<F>* l_values_ = nullptr;\n};\n\n}  // namespace tachyon::zk::lookup::halo2\n\n#endif  // TACHYON_ZK_LOOKUP_HALO2_VERIFIER_H_\n"
  },
  {
    "path": "tachyon/zk/lookup/halo2/verifier_data.h",
    "content": "#ifndef TACHYON_ZK_LOOKUP_HALO2_VERIFIER_DATA_H_\n#define TACHYON_ZK_LOOKUP_HALO2_VERIFIER_DATA_H_\n\n#include \"tachyon/zk/lookup/pair.h\"\n#include \"tachyon/zk/plonk/base/multi_phase_evaluations.h\"\n\nnamespace tachyon::zk::lookup::halo2 {\n\ntemplate <typename F, typename C>\nstruct VerifierData : public plonk::MultiPhaseEvaluations<F> {\n  VerifierData(absl::Span<const F> fixed_evals,\n               absl::Span<const F> advice_evals,\n               absl::Span<const F> instance_evals,\n               absl::Span<const F> challenges,\n               absl::Span<const Pair<C>> permuted_commitments,\n               absl::Span<const C> grand_product_commitments,\n               absl::Span<const F> grand_product_evals,\n               absl::Span<const F> grand_product_next_evals,\n               absl::Span<const F> permuted_input_evals,\n               absl::Span<const F> permuted_input_prev_evals,\n               absl::Span<const F> permuted_table_evals, const F& theta,\n               const F& beta, const F& gamma)\n      : plonk::MultiPhaseEvaluations<F>(fixed_evals, advice_evals,\n                                        instance_evals, challenges),\n        permuted_commitments(permuted_commitments),\n        grand_product_commitments(grand_product_commitments),\n        grand_product_evals(grand_product_evals),\n        grand_product_next_evals(grand_product_next_evals),\n        permuted_input_evals(permuted_input_evals),\n        permuted_input_prev_evals(permuted_input_prev_evals),\n        permuted_table_evals(permuted_table_evals),\n        theta(theta),\n        beta(beta),\n        gamma(gamma) {}\n\n  // [{A'ᵢ(τ), S'ᵢ(τ)}]₁\n  absl::Span<const Pair<C>> permuted_commitments;\n  // [Zₗ,ᵢ(τ)]₁\n  absl::Span<const C> grand_product_commitments;\n  // Zₗ,ᵢ(x)\n  absl::Span<const F> grand_product_evals;\n  // Zₗ,ᵢ(ω * x)\n  absl::Span<const F> grand_product_next_evals;\n  // A'ᵢ(x)\n  absl::Span<const F> permuted_input_evals;\n  // A'ᵢ(ω⁻¹ * x)\n  absl::Span<const F> permuted_input_prev_evals;\n  // S'ᵢ(x)\n  absl::Span<const F> permuted_table_evals;\n  const F& theta;\n  const F& beta;\n  const F& gamma;\n};\n\n}  // namespace tachyon::zk::lookup::halo2\n\n#endif  // TACHYON_ZK_LOOKUP_HALO2_VERIFIER_DATA_H_\n"
  },
  {
    "path": "tachyon/zk/lookup/log_derivative_halo2/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"evaluator\",\n    hdrs = [\"evaluator.h\"],\n    deps = [\n        \":prover\",\n        \"//tachyon/zk/plonk/vanishing:circuit_polynomial_builder_forward\",\n        \"//tachyon/zk/plonk/vanishing:graph_evaluator\",\n        \"//tachyon/zk/plonk/vanishing:vanishing_utils\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"prover\",\n    hdrs = [\n        \"prover.h\",\n        \"prover_impl.h\",\n    ],\n    deps = [\n        \"//tachyon/base:parallelize\",\n        \"//tachyon/base:profiler\",\n        \"//tachyon/base:ref\",\n        \"//tachyon/base:sort\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/crypto/commitments:polynomial_openings\",\n        \"//tachyon/zk/base/entities:prover_base\",\n        \"//tachyon/zk/lookup:argument\",\n        \"//tachyon/zk/lookup/halo2:opening_point_set\",\n        \"//tachyon/zk/plonk/base:multi_phase_ref_table\",\n        \"//tachyon/zk/plonk/expressions:compress_expression\",\n        \"//tachyon/zk/plonk/expressions:proving_evaluator\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"scheme\",\n    hdrs = [\"scheme.h\"],\n    deps = [\"//tachyon/zk/lookup:type\"],\n)\n\ntachyon_cc_library(\n    name = \"verifier\",\n    hdrs = [\"verifier.h\"],\n    deps = [\n        \":verifier_data\",\n        \"//tachyon/crypto/commitments:polynomial_openings\",\n        \"//tachyon/zk/lookup:argument\",\n        \"//tachyon/zk/lookup/halo2:opening_point_set\",\n        \"//tachyon/zk/plonk/base:l_values\",\n        \"//tachyon/zk/plonk/expressions:verifying_evaluator\",\n        \"//tachyon/zk/plonk/halo2:proof\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"verifier_data\",\n    hdrs = [\"verifier_data.h\"],\n    deps = [\"//tachyon/zk/plonk/base:multi_phase_evaluations\"],\n)\n"
  },
  {
    "path": "tachyon/zk/lookup/log_derivative_halo2/evaluator.h",
    "content": "// Copyright (c) 2022-2024 Scroll\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.scroll and the LICENCE-APACHE.scroll\n// file.\n\n#ifndef TACHYON_ZK_LOOKUP_LOG_DERIVATIVE_HALO2_EVALUATOR_H_\n#define TACHYON_ZK_LOOKUP_LOG_DERIVATIVE_HALO2_EVALUATOR_H_\n\n#include <memory>\n#include <numeric>\n#include <tuple>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/zk/lookup/log_derivative_halo2/prover.h\"\n#include \"tachyon/zk/plonk/vanishing/circuit_polynomial_builder_forward.h\"\n#include \"tachyon/zk/plonk/vanishing/graph_evaluator.h\"\n#include \"tachyon/zk/plonk/vanishing/vanishing_utils.h\"\n\nnamespace tachyon::zk::lookup::log_derivative_halo2 {\n\ntemplate <typename F>\nstruct LookupEvaluatorsPair {\n  std::vector<plonk::GraphEvaluator<F>> inputs_evaluator;\n  plonk::GraphEvaluator<F> table_evaluator;\n\n  LookupEvaluatorsPair(std::vector<plonk::GraphEvaluator<F>>&& inputs_evaluator,\n                       plonk::GraphEvaluator<F>&& table_evaluator)\n      : inputs_evaluator(std::move(inputs_evaluator)),\n        table_evaluator(std::move(table_evaluator)) {}\n};\n\ntemplate <typename EvalsOrExtendedEvals>\nclass Evaluator {\n public:\n  using F = typename EvalsOrExtendedEvals::Field;\n\n  void Construct(const std::vector<Argument<F>>& arguments) {\n    evaluators_pairs_.reserve(arguments.size());\n    for (const Argument<F>& argument : arguments) {\n      plonk::GraphEvaluator<F> graph_table;\n      std::vector<plonk::GraphEvaluator<F>> graph_inputs(\n          argument.inputs_expressions().size());\n\n      auto compress =\n          [](plonk::GraphEvaluator<F>& graph,\n             const std::vector<std::unique_ptr<Expression<F>>>& expressions) {\n            std::vector<plonk::ValueSource> parts = base::Map(\n                expressions,\n                [&graph](const std::unique_ptr<Expression<F>>& expression) {\n                  return graph.AddExpression(expression.get());\n                });\n            return graph.AddCalculation(plonk::Calculation::Horner(\n                plonk::ValueSource::ZeroConstant(), std::move(parts),\n                plonk::ValueSource::Theta()));\n          };\n\n      for (size_t i = 0; i < argument.inputs_expressions().size(); ++i) {\n        // f_compressed(X) = θᵐ⁻¹f₀(X) + θᵐ⁻²f₁(X) + ... + θfₘ₋₂(X) + fₘ₋₁(X)\n        plonk::ValueSource compressed_input_coset =\n            compress(graph_inputs[i], argument.inputs_expressions()[i]);\n        // f_compressed(X) + β\n        graph_inputs[i].AddCalculation(plonk::Calculation::Add(\n            compressed_input_coset, plonk::ValueSource::Beta()));\n      }\n      // t_compressed(X) = θᵐ⁻¹t₀(X) + θᵐ⁻²t₁(X) + ... + θtₘ₋₂(X) + tₘ₋₁(X)\n      plonk::ValueSource compressed_table_coset =\n          compress(graph_table, argument.table_expressions());\n      // t_compressed(X) + β\n      graph_table.AddCalculation(plonk::Calculation::Add(\n          compressed_table_coset, plonk::ValueSource::Beta()));\n\n      evaluators_pairs_.push_back(LookupEvaluatorsPair<F>(\n          std::move(graph_inputs), std::move(graph_table)));\n    }\n  }\n\n  template <typename PS>\n  void Evaluate(plonk::CircuitPolynomialBuilder<PS>& builder,\n                absl::Span<F> chunk, size_t chunk_offset, size_t chunk_size) {\n    for (size_t lookup_idx = 0; lookup_idx < evaluators_pairs_.size();\n         ++lookup_idx) {\n      const std::vector<plonk::GraphEvaluator<F>>& inputs_evaluator =\n          evaluators_pairs_[lookup_idx].inputs_evaluator;\n      const plonk::GraphEvaluator<F>& table_evaluator =\n          evaluators_pairs_[lookup_idx].table_evaluator;\n      const EvalsOrExtendedEvals& sum_coset = sum_cosets_[lookup_idx];\n      const EvalsOrExtendedEvals& m_coset = m_cosets_[lookup_idx];\n\n      std::vector<plonk::EvaluationInput<EvalsOrExtendedEvals>>\n          inputs_eval_data = base::Map(\n              inputs_evaluator,\n              [&builder](const plonk::GraphEvaluator<F>& input_evaluator) {\n                return builder.ExtractEvaluationInput(\n                    input_evaluator.CreateInitialIntermediates(),\n                    input_evaluator.CreateEmptyRotations());\n              });\n\n      plonk::EvaluationInput<EvalsOrExtendedEvals> table_eval_data =\n          builder.ExtractEvaluationInput(\n              table_evaluator.CreateInitialIntermediates(),\n              table_evaluator.CreateEmptyRotations());\n\n      // φᵢ(X) = fᵢ(X) + β\n      // τ(X) = t(X) + β\n      // LHS = τ(X) * Π(φᵢ(X)) * (ϕ(ω * X) - ϕ(X))\n      // RHS = τ(X) * Π(φᵢ(X)) * (Σ 1/(φᵢ(X)) - m(X) / τ(X))))\n      //     = (τ(X) * Π(φᵢ(X)) * Σ 1/(φᵢ(X))) - Π(φᵢ(X)) * m(X)\n      //     = Π(φᵢ(X)) * (τ(X) * Σ 1/(φᵢ(X)) - m(X))\n      //     = Σᵢ(τ(X) * Π_{j != i} φⱼ(X)) - m(X) * Π(φᵢ(X))\n      //\n      // (1 - (l_last(X) + l_blind(X))) * (LHS - RHS) = 0\n      std::vector<F> inputs_value;\n      size_t start = chunk_offset * chunk_size;\n      for (size_t idx = 0; idx < chunk.size(); ++idx) {\n        size_t cur_idx = start + idx;\n\n        // φᵢ(X) = fᵢ(X) + β\n        if (idx == 0) {\n          inputs_value = base::Map(\n              inputs_eval_data,\n              [&inputs_evaluator, &cur_idx](\n                  size_t i, plonk::EvaluationInput<EvalsOrExtendedEvals>&\n                                input_eval_data) {\n                return inputs_evaluator[i].Evaluate(input_eval_data, cur_idx,\n                                                    /*scale=*/1, F::Zero());\n              });\n        } else {\n          for (size_t i = 0; i < inputs_value.size(); ++i) {\n            inputs_value[i] =\n                inputs_evaluator[i].Evaluate(inputs_eval_data[i], cur_idx,\n                                             /*scale=*/1, F::Zero());\n          }\n        }\n\n        // Π(φᵢ(X))\n        F inputs_prod = std::accumulate(\n            inputs_value.begin(), inputs_value.end(), F::One(),\n            [](F& acc, const F& input) { return acc *= input; });\n\n        // τ(X) = t(X) + β\n        F table_value = table_evaluator.Evaluate(table_eval_data, cur_idx,\n                                                 /*scale=*/1, F::Zero());\n        RowIndex r_next =\n            Rotation(1).GetIndex(cur_idx, /*scale=*/1, builder.n_);\n\n        // LHS = τ(X) * Π(φᵢ(X)) * (ϕ(ω * X) - ϕ(X))\n        F lhs = table_value * inputs_prod *\n                (sum_coset[r_next] - sum_coset[cur_idx]);\n\n        F inputs_exclusive = F::Zero();\n        for (size_t i = 0; i < inputs_value.size(); ++i) {\n          // Π_{j != i} φⱼ(X)\n          F inputs_exclusive_prod = F::One();\n          for (size_t j = 0; j < inputs_value.size(); ++j) {\n            if (j != i) {\n              inputs_exclusive_prod *= inputs_value[j];\n            }\n          }\n          inputs_exclusive += inputs_exclusive_prod;\n        }\n\n        // RHS = Σᵢ τ(X) * Π_{j != i} φⱼ(X) - m(X) * Π(φᵢ(X))\n        F rhs = table_value * inputs_exclusive - inputs_prod * m_coset[cur_idx];\n\n        // l_first(X) * ϕ(X) = 0\n        chunk[idx] *= builder.y_;\n        chunk[idx] += builder.l_first_[cur_idx] * sum_coset[cur_idx];\n\n        // l_last(X) * ϕ(X) = 0\n        chunk[idx] *= builder.y_;\n        chunk[idx] += builder.l_last_[cur_idx] * sum_coset[cur_idx];\n\n        // (1 - (l_last(X) + l_blind(X))) * (lhs - rhs) = 0\n        chunk[idx] *= builder.y_;\n        chunk[idx] += (lhs - rhs) * builder.l_active_row_[cur_idx];\n      }\n    }\n  }\n\n  template <typename PS>\n  void UpdateCosets(plonk::CircuitPolynomialBuilder<PS>& builder,\n                    size_t circuit_idx) {\n    using PCS = typename PS::PCS;\n    using Poly = typename PCS::Poly;\n    using Evals = typename PCS::Evals;\n    using LookupProver = Prover<Poly, Evals>;\n\n    size_t num_lookups =\n        builder.lookup_provers_[circuit_idx].grand_sum_polys().size();\n    if (num_lookups == 0) return;\n\n    const LookupProver& prover = builder.lookup_provers_[circuit_idx];\n    sum_cosets_.resize(num_lookups);\n    m_cosets_.resize(num_lookups);\n    for (size_t i = 0; i < num_lookups; ++i) {\n      if constexpr (PS::kVendor == plonk::halo2::Vendor::kPSE) {\n        sum_cosets_[i] = plonk::CoeffToExtended(\n            prover.grand_sum_polys()[i].poly(), builder.extended_domain_);\n        m_cosets_[i] = plonk::CoeffToExtended(prover.m_polys()[i].poly(),\n                                              builder.extended_domain_);\n      } else {\n        sum_cosets_[i] =\n            builder.coset_domain_->FFT(prover.grand_sum_polys()[i].poly());\n        m_cosets_[i] = builder.coset_domain_->FFT(prover.m_polys()[i].poly());\n      }\n    }\n  }\n\n private:\n  std::vector<LookupEvaluatorsPair<F>> evaluators_pairs_;\n  std::vector<EvalsOrExtendedEvals> sum_cosets_;\n  std::vector<EvalsOrExtendedEvals> m_cosets_;\n};\n\n}  // namespace tachyon::zk::lookup::log_derivative_halo2\n\n#endif  // TACHYON_ZK_LOOKUP_LOG_DERIVATIVE_HALO2_EVALUATOR_H_\n"
  },
  {
    "path": "tachyon/zk/lookup/log_derivative_halo2/prover.h",
    "content": "// Copyright (c) 2022-2024 Scroll\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.scroll and the LICENCE-APACHE.scroll\n// file.\n\n#ifndef TACHYON_ZK_LOOKUP_LOG_DERIVATIVE_HALO2_PROVER_H_\n#define TACHYON_ZK_LOOKUP_LOG_DERIVATIVE_HALO2_PROVER_H_\n\n#include <atomic>\n#include <vector>\n\n#include \"tachyon/base/openmp_util.h\"\n#include \"tachyon/crypto/commitments/polynomial_openings.h\"\n#include \"tachyon/zk/base/blinded_polynomial.h\"\n#include \"tachyon/zk/base/entities/prover_base.h\"\n#include \"tachyon/zk/lookup/argument.h\"\n#include \"tachyon/zk/lookup/halo2/opening_point_set.h\"\n#include \"tachyon/zk/plonk/base/multi_phase_ref_table.h\"\n#include \"tachyon/zk/plonk/expressions/proving_evaluator.h\"\n\nnamespace tachyon::zk::lookup::log_derivative_halo2 {\n\ntemplate <typename BigInt>\nstruct TableEvalWithIndex {\n  RowIndex index;\n  BigInt eval;\n\n  TableEvalWithIndex() = default;\n  TableEvalWithIndex(RowIndex index, const BigInt& eval)\n      : index(index), eval(eval) {}\n\n  bool operator<(const TableEvalWithIndex& other) const {\n    return eval < other.eval;\n  }\n  bool operator<=(const TableEvalWithIndex& other) const {\n    return eval <= other.eval;\n  }\n  bool operator>(const TableEvalWithIndex& other) const {\n    return eval > other.eval;\n  }\n};\n\ntemplate <typename BigInt>\nstruct ComputeMPolysTempStorage {\n  std::vector<TableEvalWithIndex<BigInt>> sorted_table_with_indices;\n  std::vector<std::atomic<size_t>> m_values_atomic;\n\n  explicit ComputeMPolysTempStorage(size_t usable_rows)\n      : sorted_table_with_indices(usable_rows), m_values_atomic(usable_rows) {\n    OMP_PARALLEL_FOR(RowIndex i = 0; i < usable_rows; ++i) {\n      m_values_atomic[i] = 0;\n    }\n  }\n};\n\ntemplate <typename F>\nstruct GrandSumPolysTempStorage {\n  std::vector<F> inputs_log_derivatives;\n  std::vector<F> table_log_derivatives;\n\n  // NOTE(batzor): Constructing these vectors without initialization is safe\n  // since |CreateGrandSumPoly| will overwrite them.\n  explicit GrandSumPolysTempStorage(size_t usable_rows)\n      : inputs_log_derivatives(usable_rows),\n        table_log_derivatives(usable_rows) {}\n};\n\ntemplate <typename Poly, typename Evals>\nclass Prover {\n public:\n  using F = typename Poly::Field;\n  using BigInt = typename F::BigIntTy;\n\n  const std::vector<std::vector<Evals>>& compressed_inputs_vec() const {\n    return compressed_inputs_vec_;\n  }\n  const std::vector<Evals>& compressed_tables() const {\n    return compressed_tables_;\n  }\n  const std::vector<BlindedPolynomial<Poly, Evals>>& m_polys() const {\n    return m_polys_;\n  }\n  const std::vector<BlindedPolynomial<Poly, Evals>>& grand_sum_polys() const {\n    return grand_sum_polys_;\n  }\n\n  template <typename Domain>\n  static void BatchCompressPairs(\n      std::vector<Prover>& lookup_provers, const Domain* domain,\n      const std::vector<Argument<F>>& arguments, const F& theta,\n      const std::vector<plonk::MultiPhaseRefTable<Evals>>& tables);\n\n  template <typename PCS>\n  static void BatchComputeMPolys(std::vector<Prover>& lookup_provers,\n                                 ProverBase<PCS>* prover) {\n    TRACE_EVENT(\"ProofGeneration\",\n                \"Lookup::LogDerivativeHalo2::Prover::BatchComputeMPolys\");\n    ComputeMPolysTempStorage<BigInt> storage(prover->GetUsableRows());\n    for (Prover& lookup_prover : lookup_provers) {\n      lookup_prover.ComputeMPolys(prover, storage);\n    }\n  }\n\n  constexpr static size_t GetNumMPolysCommitments(\n      const std::vector<Prover>& lookup_provers) {\n    if (lookup_provers.empty()) return 0;\n    return lookup_provers.size() * lookup_provers[0].m_polys_.size();\n  }\n\n  template <typename PCS>\n  static void BatchCommitMPolys(const std::vector<Prover>& lookup_provers,\n                                ProverBase<PCS>* prover, size_t& commit_idx);\n\n  template <typename PCS>\n  static void BatchCreateGrandSumPolys(std::vector<Prover>& lookup_provers,\n                                       ProverBase<PCS>* prover, const F& beta) {\n    TRACE_EVENT(\"ProofGeneration\",\n                \"Lookup::LogDerivativeHalo2::Prover::BatchCreateGrandSumPolys\");\n    GrandSumPolysTempStorage<F> storage(prover->GetUsableRows());\n    for (Prover& lookup_prover : lookup_provers) {\n      lookup_prover.CreateGrandSumPolys(prover, beta, storage);\n    }\n  }\n\n  constexpr static size_t GetNumGrandSumPolysCommitments(\n      const std::vector<Prover>& lookup_provers) {\n    if (lookup_provers.empty()) return 0;\n    return lookup_provers.size() * lookup_provers[0].grand_sum_polys_.size();\n  }\n\n  template <typename PCS>\n  static void BatchCommitGrandSumPolys(\n      const std::vector<Prover>& lookup_provers, ProverBase<PCS>* prover,\n      size_t& commit_idx);\n\n  template <typename Domain>\n  static void TransformEvalsToPoly(std::vector<Prover>& lookup_provers,\n                                   const Domain* domain) {\n    TRACE_EVENT(\"ProofGeneration\",\n                \"Lookup::LogDerivativeHalo2::Prover::TransformEvalsToPoly\");\n    VLOG(2) << \"Transform lookup virtual columns to polys\";\n    for (Prover& lookup_prover : lookup_provers) {\n      lookup_prover.TransformEvalsToPoly(domain);\n    }\n  }\n\n  template <typename PCS>\n  static void BatchEvaluate(const std::vector<Prover>& lookup_provers,\n                            ProverBase<PCS>* prover,\n                            const halo2::OpeningPointSet<F>& point_set) {\n    TRACE_EVENT(\"ProofGeneration\",\n                \"Lookup::LogDerivativeHalo2::Prover::BatchEvaluate\");\n    for (const Prover& lookup_prover : lookup_provers) {\n      lookup_prover.Evaluate(prover, point_set);\n    }\n  }\n\n  void Open(const halo2::OpeningPointSet<F>& point_set,\n            std::vector<crypto::PolynomialOpening<Poly>>& openings) const;\n\n private:\n  template <typename Domain>\n  static std::vector<Evals> CompressInputs(\n      const Domain* domain, const Argument<F>& argument, const F& theta,\n      const plonk::ProvingEvaluator<Evals>& evaluator_tpl);\n\n  template <typename Domain>\n  static Evals CompressTable(\n      const Domain* domain, const Argument<F>& argument, const F& theta,\n      const plonk::ProvingEvaluator<Evals>& evaluator_tpl);\n\n  template <typename Domain>\n  void CompressPairs(const Domain* domain,\n                     const std::vector<Argument<F>>& arguments, const F& theta,\n                     const plonk::ProvingEvaluator<Evals>& evaluator_tpl);\n\n  template <typename PCS>\n  static BlindedPolynomial<Poly, Evals> ComputeMPoly(\n      ProverBase<PCS>* prover, const std::vector<Evals>& compressed_inputs,\n      const Evals& compressed_table, ComputeMPolysTempStorage<BigInt>& storage);\n\n  template <typename PCS>\n  void ComputeMPolys(ProverBase<PCS>* prover,\n                     ComputeMPolysTempStorage<BigInt>& storage);\n\n  static void ComputeLogDerivatives(const Evals& evals, const F& beta,\n                                    std::vector<F>& ret);\n\n  template <typename PCS>\n  static BlindedPolynomial<Poly, Evals> CreateGrandSumPoly(\n      ProverBase<PCS>* prover, const Evals& m_values,\n      const std::vector<Evals>& compressed_inputs,\n      const Evals& compressed_table, const F& beta,\n      GrandSumPolysTempStorage<F>& storage);\n\n  template <typename PCS>\n  void CreateGrandSumPolys(ProverBase<PCS>* prover, const F& beta,\n                           GrandSumPolysTempStorage<F>& storage);\n\n  template <typename Domain>\n  void TransformEvalsToPoly(const Domain* domain);\n\n  template <typename PCS>\n  void Evaluate(ProverBase<PCS>* prover,\n                const halo2::OpeningPointSet<F>& point_set) const;\n\n  // fᵢ(X)\n  std::vector<std::vector<Evals>> compressed_inputs_vec_;\n  // t(X)\n  std::vector<Evals> compressed_tables_;\n  // m(X)\n  std::vector<BlindedPolynomial<Poly, Evals>> m_polys_;\n  // ϕ(X)\n  std::vector<BlindedPolynomial<Poly, Evals>> grand_sum_polys_;\n};\n\n}  // namespace tachyon::zk::lookup::log_derivative_halo2\n\n#include \"tachyon/zk/lookup/log_derivative_halo2/prover_impl.h\"\n\n#endif  // TACHYON_ZK_LOOKUP_LOG_DERIVATIVE_HALO2_PROVER_H_\n"
  },
  {
    "path": "tachyon/zk/lookup/log_derivative_halo2/prover_impl.h",
    "content": "// Copyright (c) 2022-2024 Scroll\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.scroll and the LICENCE-APACHE.scroll\n// file.\n\n#ifndef TACHYON_ZK_LOOKUP_LOG_DERIVATIVE_HALO2_PROVER_IMPL_H_\n#define TACHYON_ZK_LOOKUP_LOG_DERIVATIVE_HALO2_PROVER_IMPL_H_\n\n#include <algorithm>\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/parallelize.h\"\n#include \"tachyon/base/profiler.h\"\n#include \"tachyon/base/ref.h\"\n#include \"tachyon/base/sort.h\"\n#include \"tachyon/zk/lookup/log_derivative_halo2/prover.h\"\n#include \"tachyon/zk/plonk/expressions/compress_expression.h\"\n\nnamespace tachyon::zk::lookup::log_derivative_halo2 {\n\n// static\ntemplate <typename Poly, typename Evals>\ntemplate <typename Domain>\nstd::vector<Evals> Prover<Poly, Evals>::CompressInputs(\n    const Domain* domain, const Argument<F>& argument, const F& theta,\n    const plonk::ProvingEvaluator<Evals>& evaluator_tpl) {\n  TRACE_EVENT(\"Utils\", \"CompressInputs\");\n  // f_compressedᵢ(X) = θᵐ⁻¹f₀(X) + θᵐ⁻²f₁(X) + ... + θfₘ₋₂(X) + fₘ₋₁(X)\n  return base::Map(argument.inputs_expressions(),\n                   [&domain, &theta, &evaluator_tpl](\n                       const std::vector<std::unique_ptr<Expression<F>>>&\n                           input_expressions) {\n                     return plonk::CompressExpressions(\n                         domain, input_expressions, theta, evaluator_tpl);\n                   });\n}\n\n// static\ntemplate <typename Poly, typename Evals>\ntemplate <typename Domain>\nEvals Prover<Poly, Evals>::CompressTable(\n    const Domain* domain, const Argument<F>& argument, const F& theta,\n    const plonk::ProvingEvaluator<Evals>& evaluator_tpl) {\n  TRACE_EVENT(\"Utils\", \"CompressTable\");\n  // t_compressedᵢ(X) = θᵐ⁻¹t₀(X) + θᵐ⁻²t₁(X) + ... + θtₘ₋₂(X) + tₘ₋₁(X)\n  return plonk::CompressExpressions(domain, argument.table_expressions(), theta,\n                                    evaluator_tpl);\n}\n\ntemplate <typename Poly, typename Evals>\ntemplate <typename Domain>\nvoid Prover<Poly, Evals>::CompressPairs(\n    const Domain* domain, const std::vector<Argument<F>>& arguments,\n    const F& theta, const plonk::ProvingEvaluator<Evals>& evaluator_tpl) {\n  TRACE_EVENT(\"Utils\", \"CompressPairs\");\n  compressed_inputs_vec_.reserve(arguments.size());\n  compressed_tables_.reserve(arguments.size());\n  for (const Argument<F>& argument : arguments) {\n    compressed_inputs_vec_.push_back(\n        CompressInputs(domain, argument, theta, evaluator_tpl));\n    compressed_tables_.push_back(\n        CompressTable(domain, argument, theta, evaluator_tpl));\n  }\n}\n\n// static\ntemplate <typename Poly, typename Evals>\ntemplate <typename Domain>\nvoid Prover<Poly, Evals>::BatchCompressPairs(\n    std::vector<Prover>& lookup_provers, const Domain* domain,\n    const std::vector<Argument<F>>& arguments, const F& theta,\n    const std::vector<plonk::MultiPhaseRefTable<Evals>>& tables) {\n  TRACE_EVENT(\"ProofGeneration\",\n              \"Lookup::LogDerivativeHalo2::Prover::BatchCompressPairs\");\n  CHECK_EQ(lookup_provers.size(), tables.size());\n  // NOTE(chokobole): It's safe to downcast because domain is already checked.\n  int32_t n = static_cast<int32_t>(domain->size());\n  for (size_t i = 0; i < lookup_provers.size(); ++i) {\n    plonk::ProvingEvaluator<Evals> proving_evaluator(0, n, 1, tables[i]);\n    lookup_provers[i].CompressPairs(domain, arguments, theta,\n                                    proving_evaluator);\n  }\n}\n\ntemplate <typename BigInt>\nstruct LessThan {\n  bool operator()(const TableEvalWithIndex<BigInt>& a, const BigInt& b) const {\n    return a.eval < b;\n  }\n\n  bool operator()(const BigInt& a, const TableEvalWithIndex<BigInt>& b) const {\n    return a < b.eval;\n  }\n};\n\n// static\ntemplate <typename Poly, typename Evals>\ntemplate <typename PCS>\nBlindedPolynomial<Poly, Evals> Prover<Poly, Evals>::ComputeMPoly(\n    ProverBase<PCS>* prover, const std::vector<Evals>& compressed_inputs,\n    const Evals& compressed_table, ComputeMPolysTempStorage<BigInt>& storage) {\n  TRACE_EVENT(\"Utils\", \"ComputeMPoly\");\n  RowIndex usable_rows = prover->GetUsableRows();\n\n  OMP_PARALLEL_FOR(RowIndex i = 0; i < usable_rows; ++i) {\n    storage.sorted_table_with_indices[i] = {i, compressed_table[i].ToBigInt()};\n  }\n\n  base::StableSort(storage.sorted_table_with_indices.begin(),\n                   storage.sorted_table_with_indices.end());\n\n  // NOTE(batzor): This vector is initialized below in the parallel loop so\n  // it is safe to keep it uninitialized here.\n  std::vector<F> m_values(prover->pcs().N());\n  OMP_PARALLEL {\n    OMP_NESTED_FOR(size_t i = 0; i < compressed_inputs.size(); ++i) {\n      for (RowIndex j = 0; j < usable_rows; ++j) {\n        BigInt input = compressed_inputs[i][j].ToBigInt();\n        auto it = base::BinarySearchByKey(\n            storage.sorted_table_with_indices.begin(),\n            storage.sorted_table_with_indices.end(), input, LessThan<BigInt>{});\n        if (it != storage.sorted_table_with_indices.end()) {\n          storage.m_values_atomic[it->index].fetch_add(\n              1, std::memory_order_relaxed);\n        }\n      }\n    }\n\n    // Convert atomic |m_values| to |Evals|.\n    OMP_FOR(RowIndex i = 0; i < m_values.size(); ++i) {\n      m_values[i] = i < usable_rows ? F(storage.m_values_atomic[i].exchange(\n                                          0, std::memory_order_relaxed))\n                                    : F::Zero();\n    }\n  }\n\n  BlindedPolynomial<Poly, Evals> m_poly(Evals(std::move(m_values)),\n                                        prover->blinder().Generate());\n  return m_poly;\n}\n\ntemplate <typename Poly, typename Evals>\ntemplate <typename PCS>\nvoid Prover<Poly, Evals>::ComputeMPolys(\n    ProverBase<PCS>* prover, ComputeMPolysTempStorage<BigInt>& storage) {\n  TRACE_EVENT(\"Utils\", \"ComputeMPolys\");\n  CHECK_EQ(compressed_inputs_vec_.size(), compressed_tables_.size());\n  m_polys_ =\n      base::Map(compressed_inputs_vec_,\n                [this, prover, &storage](\n                    size_t i, const std::vector<Evals>& compressed_inputs) {\n                  return ComputeMPoly(prover, compressed_inputs,\n                                      compressed_tables_[i], storage);\n                });\n}\n\n// static\ntemplate <typename Poly, typename Evals>\ntemplate <typename PCS>\nvoid Prover<Poly, Evals>::BatchCommitMPolys(\n    const std::vector<Prover>& lookup_provers, ProverBase<PCS>* prover,\n    size_t& commit_idx) {\n  TRACE_EVENT(\"ProofGeneration\",\n              \"Lookup::LogDerivativeHalo2::Prover::BatchCommitMPolys\");\n  if (lookup_provers.empty()) return;\n\n  if constexpr (PCS::kSupportsBatchMode) {\n    for (const Prover& lookup_prover : lookup_provers) {\n      for (const BlindedPolynomial<Poly, Evals>& m_poly :\n           lookup_prover.m_polys()) {\n        prover->BatchCommitAt(m_poly.evals(), commit_idx++);\n      }\n    }\n  } else {\n    for (const Prover& lookup_prover : lookup_provers) {\n      for (const BlindedPolynomial<Poly, Evals>& m_poly :\n           lookup_prover.m_polys()) {\n        prover->CommitAndWriteToProof(m_poly.evals());\n      }\n    }\n  }\n}\n\n// static\ntemplate <typename Poly, typename Evals>\nvoid Prover<Poly, Evals>::ComputeLogDerivatives(const Evals& evals,\n                                                const F& beta,\n                                                std::vector<F>& ret) {\n  TRACE_EVENT(\"Utils\", \"ComputeLogDerivatives\");\n  base::Parallelize(ret,\n                    [&evals, &beta](absl::Span<F> chunk, size_t chunk_offset,\n                                    size_t chunk_size) {\n                      size_t start = chunk_offset * chunk_size;\n                      for (size_t i = 0; i < chunk.size(); ++i) {\n                        chunk[i] = beta + evals.evaluations()[start + i];\n                      }\n                      CHECK(F::BatchInverseInPlaceSerial(chunk));\n                    });\n}\n\n// static\ntemplate <typename Poly, typename Evals>\ntemplate <typename PCS>\nBlindedPolynomial<Poly, Evals> Prover<Poly, Evals>::CreateGrandSumPoly(\n    ProverBase<PCS>* prover, const Evals& m_values,\n    const std::vector<Evals>& compressed_inputs, const Evals& compressed_table,\n    const F& beta, GrandSumPolysTempStorage<F>& storage) {\n  TRACE_EVENT(\"Utils\", \"CreateGrandSumPoly\");\n  size_t n = prover->pcs().N();\n  RowIndex usable_rows = prover->GetUsableRows();\n\n  // Σ 1/(φᵢ(X))\n  // NOTE(chokobole): To save memory, |input_log_derivatives| uses the storage\n  // space of |storage.table_log_derivatives| since\n  // |storage.table_log_derivatives| is currently empty and will be assigned\n  // with relevant values after |input_log_derivatives| is finished being used.\n  std::vector<F>& input_log_derivatives = storage.table_log_derivatives;\n  for (size_t i = 0; i < compressed_inputs.size(); ++i) {\n    ComputeLogDerivatives(compressed_inputs[i], beta, input_log_derivatives);\n\n    if (i == 0) {\n      OMP_PARALLEL_FOR(size_t j = 0; j < usable_rows; ++j) {\n        storage.inputs_log_derivatives[j] = input_log_derivatives[j];\n      }\n    } else {\n      OMP_PARALLEL_FOR(size_t j = 0; j < usable_rows; ++j) {\n        storage.inputs_log_derivatives[j] += input_log_derivatives[j];\n      }\n    }\n  }\n\n  // 1 / τ(X)\n  ComputeLogDerivatives(compressed_table, beta, storage.table_log_derivatives);\n\n  // NOTE(batzor): This vector is initialized below in the parallel loop so\n  // it is safe to keep it uninitialized here.\n  std::vector<F> grand_sum(n);\n  grand_sum[0] = F::Zero();\n\n  // (Σ 1/φᵢ(X)) - m(X) / τ(X)\n  // NOTE(chokobole): To save memory, |log_derivatives_diff| overwrites\n  // |storage.inputs_log_derivatives| since the current values of\n  // |storage.inputs_log_derivatives| are not needed anymore.\n  std::vector<F>& log_derivatives_diff = storage.inputs_log_derivatives;\n  OMP_PARALLEL_FOR(size_t i = 0; i < n; ++i) {\n    if (i < usable_rows) {\n      log_derivatives_diff[i] -= m_values[i] * storage.table_log_derivatives[i];\n      if (i != usable_rows - 1) {\n        grand_sum[i + 1] = log_derivatives_diff[i];\n      }\n    } else {\n      grand_sum[i] = F::Zero();\n    }\n  }\n\n// let L(X) = (Σ 1/φᵢ(X)) - m(X) / τ(X)\n// ϕ(ω⁰) = 0\n// ϕ(ω¹) = L(ω⁰)\n// ...\n// ϕ(ω^last) = L(ω⁰) + L(ω¹) + ... + L(ω^{usable_rows - 1})\n#if defined(TACHYON_HAS_OPENMP)\n  // NOTE(chokobole): To save memory, |segment_sum| overwrites\n  // |storage.table_log_derivatives| since the current values of\n  // |storage.table_log_derivatives| are not needed anymore.\n  std::vector<F>& segment_sum = storage.table_log_derivatives;\n\n  absl::Span<F> grand_sum_sub_span =\n      absl::MakeSpan(grand_sum).subspan(0, usable_rows);\n  base::Parallelize(\n      grand_sum_sub_span,\n      [&log_derivatives_diff, &segment_sum](\n          absl::Span<F> chunk, size_t chunk_idx, size_t chunk_size) {\n        size_t start = chunk_idx * chunk_size;\n        for (size_t i = 1; i < chunk.size(); ++i) {\n          chunk[i] = chunk[i - 1] + log_derivatives_diff[start + i - 1];\n        }\n        segment_sum[chunk_idx] = chunk.back();\n      });\n\n  for (size_t i = 1; i < segment_sum.size(); ++i) {\n    segment_sum[i] += segment_sum[i - 1];\n  }\n\n  base::Parallelize(\n      grand_sum_sub_span,\n      [&segment_sum](absl::Span<F> chunk, size_t chunk_idx, size_t chunk_size) {\n        if (chunk_idx == 0) return;\n        const F& prefix_sum = segment_sum[chunk_idx - 1];\n        for (F& v : chunk) {\n          v += prefix_sum;\n        }\n      });\n#else\n  std::partial_sum(log_derivatives_diff.begin(), log_derivatives_diff.end() - 1,\n                   grand_sum.begin() + 1);\n#endif\n\n  Evals grand_sum_poly(std::move(grand_sum));\n\n  CHECK(prover->blinder().Blind(grand_sum_poly));\n\n  return BlindedPolynomial<Poly, Evals>(std::move(grand_sum_poly),\n                                        prover->blinder().Generate());\n}\n\ntemplate <typename Poly, typename Evals>\ntemplate <typename PCS>\nvoid Prover<Poly, Evals>::CreateGrandSumPolys(\n    ProverBase<PCS>* prover, const F& beta,\n    GrandSumPolysTempStorage<F>& storage) {\n  TRACE_EVENT(\"Utils\", \"CreateGrandSumPolys\");\n  CHECK_EQ(compressed_inputs_vec_.size(), compressed_tables_.size());\n\n  grand_sum_polys_ =\n      base::Map(compressed_inputs_vec_,\n                [this, &prover, &beta, &storage](\n                    size_t i, const std::vector<Evals>& compressed_inputs) {\n                  return CreateGrandSumPoly(\n                      prover, m_polys_[i].evals(), compressed_inputs,\n                      compressed_tables_[i], beta, storage);\n                });\n}\n\n// static\ntemplate <typename Poly, typename Evals>\ntemplate <typename PCS>\nvoid Prover<Poly, Evals>::BatchCommitGrandSumPolys(\n    const std::vector<Prover>& lookup_provers, ProverBase<PCS>* prover,\n    size_t& commit_idx) {\n  TRACE_EVENT(\"ProofGeneration\",\n              \"Lookup::LogDerivativeHalo2::Prover::BatchCommitGrandSumPolys\");\n  if (lookup_provers.empty()) return;\n\n  if constexpr (PCS::kSupportsBatchMode) {\n    for (const Prover& lookup_prover : lookup_provers) {\n      for (const BlindedPolynomial<Poly, Evals>& grand_sum_poly :\n           lookup_prover.grand_sum_polys_) {\n        prover->BatchCommitAt(grand_sum_poly.evals(), commit_idx++);\n      }\n    }\n  } else {\n    for (const Prover& lookup_prover : lookup_provers) {\n      for (const BlindedPolynomial<Poly, Evals>& grand_sum_poly :\n           lookup_prover.grand_sum_polys_) {\n        prover->CommitAndWriteToProof(grand_sum_poly.evals());\n      }\n    }\n  }\n}\n\ntemplate <typename Poly, typename Evals>\ntemplate <typename Domain>\nvoid Prover<Poly, Evals>::TransformEvalsToPoly(const Domain* domain) {\n  TRACE_EVENT(\"Utils\", \"TransformEvalsToPoly\");\n  for (BlindedPolynomial<Poly, Evals>& m_poly : m_polys_) {\n    m_poly.TransformEvalsToPoly(domain);\n  }\n  for (BlindedPolynomial<Poly, Evals>& grand_sum_poly : grand_sum_polys_) {\n    grand_sum_poly.TransformEvalsToPoly(domain);\n  }\n}\n\ntemplate <typename Poly, typename Evals>\ntemplate <typename PCS>\nvoid Prover<Poly, Evals>::Evaluate(\n    ProverBase<PCS>* prover, const halo2::OpeningPointSet<F>& point_set) const {\n  TRACE_EVENT(\"Utils\", \"Evaluate\");\n  size_t size = grand_sum_polys_.size();\n  CHECK_EQ(size, m_polys_.size());\n\n#define EVALUATE(polynomial, point) \\\n  prover->EvaluateAndWriteToProof(polynomial.poly(), point_set.point)\n\n  // THE ORDER IS IMPORTANT!! DO NOT CHANGE!\n  // See\n  // https://github.com/scroll-tech/halo2/blob/1070391/halo2_proofs/src/plonk/mv_lookup/prover.rs#L428-L453\n  for (size_t i = 0; i < size; ++i) {\n    const BlindedPolynomial<Poly, Evals>& grand_sum_poly = grand_sum_polys_[i];\n    const BlindedPolynomial<Poly, Evals>& m_poly = m_polys_[i];\n\n    // ϕᵢ(x)\n    EVALUATE(grand_sum_poly, x);\n    // ϕᵢ(ω * x)\n    EVALUATE(grand_sum_poly, x_next);\n    // mᵢ(x)\n    EVALUATE(m_poly, x);\n  }\n#undef EVALUATE\n}\n\ntemplate <typename Poly, typename Evals>\nvoid Prover<Poly, Evals>::Open(\n    const halo2::OpeningPointSet<F>& point_set,\n    std::vector<crypto::PolynomialOpening<Poly>>& openings) const {\n  TRACE_EVENT(\"ProofGeneration\", \"Lookup::LogDerivativeHalo2::Prover::Open\");\n  size_t size = grand_sum_polys_.size();\n  CHECK_EQ(size, m_polys_.size());\n\n#define OPENING(polynomial, point)                            \\\n  base::Ref<const Poly>(&polynomial.poly()), point_set.point, \\\n      polynomial.poly().Evaluate(point_set.point)\n\n  // THE ORDER IS IMPORTANT!! DO NOT CHANGE!\n  // See\n  // https://github.com/scroll-tech/halo2/blob/1070391/halo2_proofs/src/plonk/mv_lookup/prover.rs#L455-L480\n  for (size_t i = 0; i < size; ++i) {\n    const BlindedPolynomial<Poly, Evals>& grand_sum_poly = grand_sum_polys_[i];\n    const BlindedPolynomial<Poly, Evals>& m_poly = m_polys_[i];\n    // ϕᵢ(x)\n    openings.emplace_back(OPENING(grand_sum_poly, x));\n    // ϕᵢ(ω * x)\n    openings.emplace_back(OPENING(grand_sum_poly, x_next));\n    // mᵢ(x)\n    openings.emplace_back(OPENING(m_poly, x));\n  }\n#undef OPENING\n}\n\n}  // namespace tachyon::zk::lookup::log_derivative_halo2\n\n#endif  // TACHYON_ZK_LOOKUP_LOG_DERIVATIVE_HALO2_PROVER_IMPL_H_\n"
  },
  {
    "path": "tachyon/zk/lookup/log_derivative_halo2/scheme.h",
    "content": "#ifndef TACHYON_ZK_LOOKUP_LOG_DERIVATIVE_HALO2_SCHEME_H_\n#define TACHYON_ZK_LOOKUP_LOG_DERIVATIVE_HALO2_SCHEME_H_\n\n#include \"tachyon/zk/lookup/type.h\"\n\nnamespace tachyon::zk::lookup::log_derivative_halo2 {\n\ntemplate <typename _Poly, typename _Evals, typename _Commitment,\n          typename _ExtendedPoly, typename _ExtendedEvals>\nstruct Scheme {\n  using Poly = _Poly;\n  using Evals = _Evals;\n  using Commitment = _Commitment;\n  using ExtendedPoly = _ExtendedPoly;\n  using ExtendedEvals = _ExtendedEvals;\n  using Field = typename Poly::Field;\n\n  constexpr static Type kType = Type::kLogDerivativeHalo2;\n};\n\n}  // namespace tachyon::zk::lookup::log_derivative_halo2\n\n#endif  // TACHYON_ZK_LOOKUP_LOG_DERIVATIVE_HALO2_SCHEME_H_\n"
  },
  {
    "path": "tachyon/zk/lookup/log_derivative_halo2/verifier.h",
    "content": "// Copyright (c) 2022-2024 Scroll\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.scroll and the LICENCE-APACHE.scroll\n// file.\n\n#ifndef TACHYON_ZK_LOOKUP_LOG_DERIVATIVE_HALO2_VERIFIER_H_\n#define TACHYON_ZK_LOOKUP_LOG_DERIVATIVE_HALO2_VERIFIER_H_\n\n#include <memory>\n#include <vector>\n\n#include \"tachyon/crypto/commitments/polynomial_openings.h\"\n#include \"tachyon/zk/lookup/argument.h\"\n#include \"tachyon/zk/lookup/halo2/opening_point_set.h\"\n#include \"tachyon/zk/lookup/log_derivative_halo2/verifier_data.h\"\n#include \"tachyon/zk/plonk/base/l_values.h\"\n#include \"tachyon/zk/plonk/expressions/verifying_evaluator.h\"\n#include \"tachyon/zk/plonk/halo2/proof.h\"\n\nnamespace tachyon::zk::lookup::log_derivative_halo2 {\n\ntemplate <typename F, typename C>\nclass Verifier {\n public:\n  using Proof = plonk::halo2::LogDerivativeHalo2Proof<F, C>;\n\n  Verifier(const Proof& proof, size_t circuit_idx)\n      : data_(proof.ToLookupVerifierData(circuit_idx)) {}\n\n  Verifier(const Proof& proof, size_t circuit_idx,\n           const plonk::LValues<F>& l_values)\n      : data_(proof.ToLookupVerifierData(circuit_idx)), l_values_(&l_values) {}\n\n  void Evaluate(const std::vector<Argument<F>>& arguments,\n                std::vector<F>& evals) {\n    plonk::VerifyingEvaluator<F> evaluator(data_);\n\n    F active_rows = F::One() - (l_values_->last + l_values_->blind);\n\n    for (size_t i = 0; i < data_.grand_sum_commitments.size(); ++i) {\n      // l_first(X) * ϕ(X) = 0\n      evals.push_back(l_values_->first * data_.grand_sum_evals[i]);\n      // l_last(X) * ϕ(X) = 0\n      evals.push_back(l_values_->last * data_.grand_sum_evals[i]);\n      // (1 - (l_last(X) + l_blind(X))) * (lhs - rhs) = 0\n      evals.push_back(active_rows *\n                      CreateGrandSumEvaluation(i, arguments[i], evaluator));\n    }\n  }\n\n  template <typename Poly>\n  void Open(const halo2::OpeningPointSet<F>& point_set,\n            std::vector<crypto::PolynomialOpening<Poly, C>>& openings) const {\n    if (data_.grand_sum_commitments.empty()) return;\n\n#define OPENING(commitment, point, eval) \\\n  base::Ref<const C>(&data_.commitment), point_set.point, data_.eval\n\n    for (size_t i = 0; i < data_.grand_sum_commitments.size(); ++i) {\n      openings.emplace_back(\n          OPENING(grand_sum_commitments[i], x, grand_sum_evals[i]));\n      openings.emplace_back(\n          OPENING(grand_sum_commitments[i], x_next, grand_sum_next_evals[i]));\n      openings.emplace_back(OPENING(m_poly_commitments[i], x, m_evals[i]));\n    }\n\n#undef OPENING\n  }\n\n private:\n  F CompressExpressions(\n      const std::vector<std::unique_ptr<Expression<F>>>& expressions,\n      plonk::VerifyingEvaluator<F>& evaluator) const {\n    F compressed_value = F::Zero();\n    for (const std::unique_ptr<Expression<F>>& expression : expressions) {\n      compressed_value *= data_.theta;\n      compressed_value += evaluator.Evaluate(expression.get());\n    }\n    return compressed_value;\n  }\n\n  F CreateGrandSumEvaluation(size_t i, const Argument<F>& argument,\n                             plonk::VerifyingEvaluator<F>& evaluator) {\n    // φᵢ(X) = fᵢ(X) + β\n    std::vector<F> f_evals = base::Map(\n        argument.inputs_expressions(),\n        [this, &evaluator](const std::vector<std::unique_ptr<Expression<F>>>&\n                               input_expressions) {\n          return CompressExpressions(input_expressions, evaluator) + data_.beta;\n        });\n\n    F t_eval = CompressExpressions(argument.table_expressions(), evaluator);\n\n    F tau = t_eval + data_.beta;\n\n    // Π(φᵢ(X))\n    F prod_fi =\n        std::accumulate(f_evals.begin(), f_evals.end(), F::One(),\n                        [](F& acc, const F& f_eval) { return acc *= f_eval; });\n\n    CHECK(F::BatchInverseInPlace(f_evals));\n\n    // Σ 1/(φᵢ(X))\n    F sum_inv_fi =\n        std::accumulate(f_evals.begin(), f_evals.end(), F::Zero(),\n                        [](F& acc, const F& f_eval) { return acc += f_eval; });\n\n    // LHS = τ(X) * Π(φᵢ(X)) * (ϕ(ω * X) - ϕ(X))\n    F lhs = tau * prod_fi *\n            (data_.grand_sum_next_evals[i] - data_.grand_sum_evals[i]);\n\n    // RHS = τ(X) * Π(φᵢ(X)) * (Σ 1/(φᵢ(X)) - m(X) / τ(X))\n    F rhs = tau * prod_fi * (sum_inv_fi - data_.m_evals[i] * *tau.Inverse());\n\n    return lhs - rhs;\n  }\n\n  VerifierData<F, C> data_;\n  const plonk::LValues<F>* l_values_ = nullptr;\n};\n\n}  // namespace tachyon::zk::lookup::log_derivative_halo2\n\n#endif  // TACHYON_ZK_LOOKUP_LOG_DERIVATIVE_HALO2_VERIFIER_H_\n"
  },
  {
    "path": "tachyon/zk/lookup/log_derivative_halo2/verifier_data.h",
    "content": "// Copyright (c) 2022-2024 Scroll\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.scroll and the LICENCE-APACHE.scroll\n// file.\n\n#ifndef TACHYON_ZK_LOOKUP_LOG_DERIVATIVE_HALO2_VERIFIER_DATA_H_\n#define TACHYON_ZK_LOOKUP_LOG_DERIVATIVE_HALO2_VERIFIER_DATA_H_\n\n#include \"tachyon/zk/plonk/base/multi_phase_evaluations.h\"\n\nnamespace tachyon::zk::lookup::log_derivative_halo2 {\n\ntemplate <typename F, typename C>\nstruct VerifierData : public plonk::MultiPhaseEvaluations<F> {\n  VerifierData(absl::Span<const F> fixed_evals,\n               absl::Span<const F> advice_evals,\n               absl::Span<const F> instance_evals,\n               absl::Span<const F> challenges,\n               absl::Span<const C> m_poly_commitments,\n               absl::Span<const C> grand_sum_commitments,\n               absl::Span<const F> grand_sum_evals,\n               absl::Span<const F> grand_sum_next_evals,\n               absl::Span<const F> m_evals, const F& theta, const F& beta)\n      : plonk::MultiPhaseEvaluations<F>(fixed_evals, advice_evals,\n                                        instance_evals, challenges),\n        m_poly_commitments(m_poly_commitments),\n        grand_sum_commitments(grand_sum_commitments),\n        grand_sum_evals(grand_sum_evals),\n        grand_sum_next_evals(grand_sum_next_evals),\n        m_evals(m_evals),\n        theta(theta),\n        beta(beta) {}\n\n  // [m(τ)]₁\n  absl::Span<const C> m_poly_commitments;\n  // [ϕ(τ)]₁\n  absl::Span<const C> grand_sum_commitments;\n  // ϕ(X)\n  absl::Span<const F> grand_sum_evals;\n  // ϕ(ω * X)\n  absl::Span<const F> grand_sum_next_evals;\n  // m(X)\n  absl::Span<const F> m_evals;\n  const F& theta;\n  const F& beta;\n};\n\n}  // namespace tachyon::zk::lookup::log_derivative_halo2\n\n#endif  // TACHYON_ZK_LOOKUP_LOG_DERIVATIVE_HALO2_VERIFIER_DATA_H_\n"
  },
  {
    "path": "tachyon/zk/lookup/pair.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_LOOKUP_PAIR_H_\n#define TACHYON_ZK_LOOKUP_PAIR_H_\n\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/json/json.h\"\n\nnamespace tachyon {\nnamespace zk::lookup {\n\ntemplate <typename T, typename U = T>\nclass Pair {\n public:\n  Pair() = default;\n  Pair(T input, U table) : input_(std::move(input)), table_(std::move(table)) {}\n\n  const T& input() const { return input_; }\n  const U& table() const { return table_; }\n  T& input() { return input_; }\n  U& table() { return table_; }\n\n  T&& TakeInput() && { return std::move(input_); }\n  U&& TakeTable() && { return std::move(table_); }\n\n  bool operator==(const Pair& other) const {\n    return input_ == other.input_ && table_ == other.table_;\n  }\n  bool operator!=(const Pair& other) const { return !operator==(other); }\n\n private:\n  T input_;\n  U table_;\n};\n\ntemplate <typename T, typename U = T>\nusing Pairs = std::vector<Pair<T, U>>;\n\n}  // namespace zk::lookup\n\nnamespace base {\n\ntemplate <typename T, typename U>\nclass RapidJsonValueConverter<zk::lookup::Pair<T, U>> {\n public:\n  template <typename Allocator>\n  static rapidjson::Value From(const zk::lookup::Pair<T, U>& value,\n                               Allocator& allocator) {\n    rapidjson::Value object(rapidjson::kObjectType);\n    AddJsonElement(object, \"input\", value.input(), allocator);\n    AddJsonElement(object, \"table\", value.table(), allocator);\n    return object;\n  }\n\n  static bool To(const rapidjson::Value& json_value, std::string_view key,\n                 zk::lookup::Pair<T, U>* value, std::string* error) {\n    T input;\n    U table;\n    if (!ParseJsonElement(json_value, \"input\", &input, error)) return false;\n    if (!ParseJsonElement(json_value, \"table\", &table, error)) return false;\n    *value = zk::lookup::Pair<T, U>(std::move(input), std::move(table));\n    return true;\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_ZK_LOOKUP_PAIR_H_\n"
  },
  {
    "path": "tachyon/zk/lookup/prover.h",
    "content": "#ifndef TACHYON_ZK_LOOKUP_PROVER_H_\n#define TACHYON_ZK_LOOKUP_PROVER_H_\n\n#include <type_traits>\n\n#include \"tachyon/zk/lookup/halo2/prover.h\"\n#include \"tachyon/zk/lookup/log_derivative_halo2/prover.h\"\n#include \"tachyon/zk/lookup/type.h\"\n\nnamespace tachyon::zk::lookup {\n\ntemplate <Type kType, typename Poly, typename Evals>\nusing Prover =\n    std::conditional_t<kType == Type::kHalo2, halo2::Prover<Poly, Evals>,\n                       log_derivative_halo2::Prover<Poly, Evals>>;\n\n}  // namespace tachyon::zk::lookup\n\n#endif  // TACHYON_ZK_LOOKUP_PROVER_H_\n"
  },
  {
    "path": "tachyon/zk/lookup/type.h",
    "content": "#ifndef TACHYON_ZK_LOOKUP_TYPE_H_\n#define TACHYON_ZK_LOOKUP_TYPE_H_\n\nnamespace tachyon::zk::lookup {\n\nenum class Type { kHalo2, kLogDerivativeHalo2 };\n\n}  // namespace tachyon::zk::lookup\n\n#endif  // TACHYON_ZK_LOOKUP_TYPE_H_\n"
  },
  {
    "path": "tachyon/zk/lookup/verifier.h",
    "content": "#ifndef TACHYON_ZK_LOOKUP_VERIFIER_H_\n#define TACHYON_ZK_LOOKUP_VERIFIER_H_\n\n#include <type_traits>\n\n#include \"tachyon/zk/lookup/halo2/verifier.h\"\n#include \"tachyon/zk/lookup/log_derivative_halo2/verifier.h\"\n#include \"tachyon/zk/lookup/type.h\"\n\nnamespace tachyon::zk::lookup {\n\ntemplate <Type kType, typename F, typename C>\nusing Verifier =\n    std::conditional_t<kType == Type::kHalo2, halo2::Verifier<F, C>,\n                       log_derivative_halo2::Verifier<F, C>>;\n\n}  // namespace tachyon::zk::lookup\n\n#endif  // TACHYON_ZK_LOOKUP_VERIFIER_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/base/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"column_key\",\n    hdrs = [\"column_key.h\"],\n    deps = [\n        \":column_type\",\n        \":phase\",\n        \"//tachyon/base:logging\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"column_type\",\n    srcs = [\"column_type.cc\"],\n    hdrs = [\"column_type.h\"],\n    deps = [\"//tachyon/base:logging\"],\n)\n\ntachyon_cc_library(\n    name = \"l_values\",\n    hdrs = [\"l_values.h\"],\n)\n\ntachyon_cc_library(\n    name = \"multi_phase_evaluations\",\n    hdrs = [\"multi_phase_evaluations.h\"],\n    deps = [\"@com_google_absl//absl/types:span\"],\n)\n\ntachyon_cc_library(\n    name = \"multi_phase_ref_table\",\n    hdrs = [\"multi_phase_ref_table.h\"],\n    deps = [\":ref_table\"],\n)\n\ntachyon_cc_library(\n    name = \"owned_table\",\n    hdrs = [\"owned_table.h\"],\n    deps = [\":table_base\"],\n)\n\ntachyon_cc_library(\n    name = \"phase\",\n    hdrs = [\"phase.h\"],\n    deps = [\n        \"//tachyon/base/strings:string_number_conversions\",\n        \"@com_google_absl//absl/hash\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"ref_table\",\n    hdrs = [\"ref_table.h\"],\n    deps = [\":table_base\"],\n)\n\ntachyon_cc_library(\n    name = \"table_base\",\n    hdrs = [\"table_base.h\"],\n    deps = [\n        \":column_key\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base:ref\",\n        \"//tachyon/base:template_util\",\n        \"//tachyon/base/containers:container_util\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"base_unittests\",\n    srcs = [\n        \"column_key_unittest.cc\",\n        \"phase_unittest.cc\",\n        \"ref_table_unittest.cc\",\n    ],\n    deps = [\n        \":column_key\",\n        \":phase\",\n        \":ref_table\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:g1\",\n        \"//tachyon/math/polynomials/univariate:univariate_evaluations\",\n        \"@com_google_absl//absl/hash:hash_testing\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/zk/plonk/base/column_key.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_BASE_COLUMN_KEY_H_\n#define TACHYON_ZK_PLONK_BASE_COLUMN_KEY_H_\n\n#include <stddef.h>\n\n#include <string>\n#include <utility>\n\n#include \"absl/hash/hash.h\"\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/zk/plonk/base/column_type.h\"\n#include \"tachyon/zk/plonk/base/phase.h\"\n\nnamespace tachyon::zk::plonk {\n\nclass TACHYON_EXPORT ColumnKeyBase {\n public:\n  constexpr ColumnKeyBase() = default;\n  constexpr ColumnKeyBase(ColumnType type, size_t index)\n      : type_(type), index_(index) {}\n\n  ColumnType type() const { return type_; }\n  size_t index() const { return index_; }\n\n  bool operator==(const ColumnKeyBase& other) const {\n    return type_ == other.type_ && index_ == other.index_;\n  }\n  bool operator!=(const ColumnKeyBase& other) const {\n    return !operator==(other);\n  }\n\n  std::string ToString() const {\n    return absl::Substitute(\"{type: $0, index: $1}\", ColumnTypeToString(type_),\n                            index_);\n  }\n\n protected:\n  ColumnType type_ = ColumnType::kAny;\n  size_t index_ = 0;\n};\n\ntemplate <typename H>\nH AbslHashValue(H h, const ColumnKeyBase& column) {\n  return H::combine(std::move(h), column.type(), column.index());\n}\n\n// AnyColumnKey c = FixedColumnKey(1); -> ok\n// AnyColumnKey c = AnyColumnKey(1); -> ok\n// FixedColumnKey c = FixedColumnKey(1); -> ok\n// FixedColumnKey c = InstanceColumnKey(1); -> no\n// FixedColumnKey c = AnyColumnKey(1); -> yes\n// See column_unittest.cc\ntemplate <ColumnType C>\nclass ColumnKey : public ColumnKeyBase {\n public:\n  constexpr static ColumnType kDefaultType = C;\n\n  constexpr ColumnKey() : ColumnKeyBase(C, 0) {}\n  constexpr explicit ColumnKey(size_t index) : ColumnKeyBase(C, index) {}\n\n  // NOTE(chokobole): AdviceColumnKey can be constructed with an additional\n  // argument |phase|.\n  //\n  //   AdviceColumnKey column(1, kSecondPhase);\n  template <ColumnType C2 = C,\n            std::enable_if_t<C2 == ColumnType::kAdvice>* = nullptr>\n  constexpr ColumnKey(size_t index, Phase phase)\n      : ColumnKeyBase(C, index), phase_(phase) {}\n\n  // NOTE(chokobole): in this case, |type_| is changed!\n  //\n  //   AnyColumnKey c(FixedColumnKey(1));\n  //   CHECK_EQ(c.type(), ColumnType::kFixed);\n  //\n  //   AnyColumnKey c(AdviceColumnKey(1, kSecondPhase));\n  //   CHECK_EQ(c.type(), ColumnType::kAdvice);\n  //   CHECK_EQ(c.phase(), kSecondPhase);\n  template <ColumnType C2,\n            std::enable_if_t<C == ColumnType::kAny && C != C2>* = nullptr>\n  constexpr ColumnKey(const ColumnKey<C2>& other)\n      : ColumnKeyBase(other.type_, other.index_), phase_(other.phase_) {}\n\n  // NOTE(chokobole): in this case, |type_| is not changed!\n  //\n  //   FixedColumnKey c(AnyColumnKey(1));\n  //   CHECK_EQ(c.type(), ColumnType::kFixed);\n  //\n  //   AnyColumnKey c(AdviceColumnKey(1, kSecondPhase));\n  //   AdviceColumnKey c2(c);\n  //   CHECK_EQ(c2.type(), ColumnType::kAdvice);\n  //   CHECK_EQ(c2.phase(), kSecondPhase);\n  template <ColumnType C2, std::enable_if_t<C != ColumnType::kAny &&\n                                            C2 == ColumnType::kAny>* = nullptr>\n  constexpr ColumnKey(const ColumnKey<C2>& other)\n      : ColumnKeyBase(C, other.index_), phase_(other.phase_) {}\n\n  // FixedColumnKey c(FixedColumnKey(1));\n  constexpr ColumnKey(const ColumnKey& other) = default;\n\n  // NOTE(chokobole): in this case, |type_| is changed!\n  //\n  //   AnyColumnKey c;\n  //   c = FixedColumnKey(1);\n  //   CHECK_EQ(c.type(), ColumnType::kFixed);\n  //\n  //   AnyColumnKey c;\n  //   c = AdviceColumnKey(1, kSecondPhase);\n  //   CHECK_EQ(c.type(), ColumnType::kAdvice);\n  //   CHECK_EQ(c.phase(), kSecondPhase);\n  template <ColumnType C2,\n            std::enable_if_t<C == ColumnType::kAny && C != C2>* = nullptr>\n  ColumnKey& operator=(const ColumnKey<C2>& other) {\n    ColumnKeyBase::operator=(other);\n    phase_ = other.phase_;\n    return *this;\n  }\n\n  // NOTE(chokobole): in this case, |type_| is not changed!\n  //\n  //   FixedColumnKey c;\n  //   c = AnyColumnKey(1);\n  //   CHECK_EQ(c.type(), ColumnType::kFixed);\n  //\n  //   AdviceColumnKey c;\n  //   c = AnyColumnKey(AdviceColumnKey(1, kSecondPhase));\n  //   CHECK_EQ(c.type(), ColumnType::kAdvice);\n  //   CHECK_EQ(c.phase(), kSecondPhase);\n  template <ColumnType C2, std::enable_if_t<C != ColumnType::kAny &&\n                                            C2 == ColumnType::kAny>* = nullptr>\n  ColumnKey& operator=(const ColumnKey<C2>& other) {\n    index_ = other.index_;\n    phase_ = other.phase_;\n    return *this;\n  }\n\n  // FixedColumnKey c;\n  // c = FixedColumnKey(1);\n  ColumnKey& operator=(const ColumnKey& other) = default;\n\n  ColumnType type() const { return type_; }\n  size_t index() const { return index_; }\n  Phase phase() const { return phase_; }\n\n  template <ColumnType C2>\n  bool operator==(const ColumnKey<C2>& other) const {\n    if (!ColumnKeyBase::operator==(other)) return false;\n    if (type_ == ColumnType::kAdvice) {\n      return phase_ == other.phase_;\n    }\n    return true;\n  }\n  template <ColumnType C2>\n  bool operator!=(const ColumnKey<C2>& other) const {\n    return !operator==(other);\n  }\n\n  // This ordering is consensus-critical! The layouters rely on deterministic\n  // column orderings.\n  template <ColumnType C2>\n  bool operator<(const ColumnKey<C2>& other) const {\n    CHECK_NE(type_, ColumnType::kAny);\n    CHECK_NE(other.type_, ColumnType::kAny);\n\n    if (type_ == other.type_) {\n      if (type_ == ColumnType::kInstance || type_ == ColumnType::kFixed) {\n        return false;\n      } else {\n        return phase_ < other.phase_;\n      }\n    } else {\n      // Across column types, sort Instance < Advice < Fixed.\n      return static_cast<int>(type_) < static_cast<int>(other.type_);\n    }\n  }\n\n  std::string ToString() const {\n    if (type_ == ColumnType::kAdvice) {\n      return absl::Substitute(\"{type: $0, index: $1, phase: $2}\",\n                              ColumnTypeToString(type_), index_,\n                              phase_.ToString());\n    } else {\n      return absl::Substitute(\"{type: $0, index: $1}\",\n                              ColumnTypeToString(type_), index_);\n    }\n  }\n\n private:\n  template <ColumnType C2>\n  friend class ColumnKey;\n\n  Phase phase_;\n};\n\nusing AnyColumnKey = ColumnKey<ColumnType::kAny>;\nusing FixedColumnKey = ColumnKey<ColumnType::kFixed>;\nusing AdviceColumnKey = ColumnKey<ColumnType::kAdvice>;\nusing InstanceColumnKey = ColumnKey<ColumnType::kInstance>;\n\ntemplate <typename H, ColumnType C>\nH AbslHashValue(H h, const ColumnKey<C>& column) {\n  return H::combine(std::move(h), column.type(), column.index(),\n                    column.phase());\n}\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_BASE_COLUMN_KEY_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/base/column_key_unittest.cc",
    "content": "#include \"tachyon/zk/plonk/base/column_key.h\"\n\n#include \"absl/hash/hash_testing.h\"\n#include \"gtest/gtest.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename ColumnKey>\nclass ColumnKeyTest : public testing::Test {};\n\nusing ColumnKeyTypes =\n    testing::Types<FixedColumnKey, AdviceColumnKey, InstanceColumnKey>;\nTYPED_TEST_SUITE(ColumnKeyTest, ColumnKeyTypes);\n\nTYPED_TEST(ColumnKeyTest, AnyColumnKeyConstruction) {\n  using ColumnKey = TypeParam;\n\n  AnyColumnKey any(ColumnKey(1));\n  EXPECT_EQ(any.type(), ColumnKey::kDefaultType);\n  AnyColumnKey any2;\n  EXPECT_EQ(any2.type(), ColumnType::kAny);\n  any2 = ColumnKey(1);\n  EXPECT_EQ(any2.type(), ColumnKey::kDefaultType);\n\n  if constexpr (std::is_same_v<ColumnKey, AdviceColumnKey>) {\n    AnyColumnKey any(ColumnKey(1, kSecondPhase));\n    EXPECT_EQ(any.phase(), kSecondPhase);\n    AnyColumnKey any2;\n    EXPECT_EQ(any2.phase(), kFirstPhase);\n    any2 = ColumnKey(1, kSecondPhase);\n    EXPECT_EQ(any2.phase(), kSecondPhase);\n  }\n}\n\nTYPED_TEST(ColumnKeyTest, NonAnyColumnKeyConstruction) {\n  using ColumnKey = TypeParam;\n\n  ColumnKey c(ColumnKey(1));\n  EXPECT_EQ(c.type(), ColumnKey::kDefaultType);\n  c = ColumnKey(1);\n  EXPECT_EQ(c.type(), ColumnKey::kDefaultType);\n  ColumnKey c2(AnyColumnKey(1));\n  EXPECT_EQ(c2.type(), ColumnKey::kDefaultType);\n  ColumnKey c3;\n  EXPECT_EQ(c3.type(), ColumnKey::kDefaultType);\n  c3 = AnyColumnKey(1);\n  EXPECT_EQ(c3.type(), ColumnKey::kDefaultType);\n}\n\nTYPED_TEST(ColumnKeyTest, Hash) {\n  using ColumnKey = TypeParam;\n  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(\n      std::make_tuple(ColumnKey(), ColumnKey(1))));\n}\n\nTEST(ColumnKeyTest, Equality) {\n  // When types and indices are equal.\n  EXPECT_EQ(AnyColumnKey(), AnyColumnKey());\n  EXPECT_EQ(InstanceColumnKey(), InstanceColumnKey());\n  EXPECT_EQ(AdviceColumnKey(), AdviceColumnKey());\n  EXPECT_EQ(FixedColumnKey(), FixedColumnKey());\n\n  // When types are different.\n  EXPECT_NE(AnyColumnKey(), InstanceColumnKey());\n  EXPECT_NE(AnyColumnKey(), AdviceColumnKey());\n  EXPECT_NE(AnyColumnKey(), FixedColumnKey());\n  EXPECT_NE(InstanceColumnKey(), AdviceColumnKey());\n  EXPECT_NE(InstanceColumnKey(), FixedColumnKey());\n  EXPECT_NE(AdviceColumnKey(), FixedColumnKey());\n\n  // When types are equal but indices (and/or phase) are different.\n  EXPECT_NE(AnyColumnKey(), AnyColumnKey(1));\n  EXPECT_NE(InstanceColumnKey(), InstanceColumnKey(1));\n  EXPECT_NE(AdviceColumnKey(), AdviceColumnKey(0, kSecondPhase));\n  EXPECT_NE(AdviceColumnKey(), AdviceColumnKey(1, kSecondPhase));\n  EXPECT_NE(FixedColumnKey(), FixedColumnKey(1));\n}\n\nTEST(ColumnKeyTest, Order) {\n  EXPECT_DEATH(CHECK_LT(AnyColumnKey(), FixedColumnKey()), \"\");\n  EXPECT_DEATH(CHECK_LT(FixedColumnKey(), AnyColumnKey()), \"\");\n\n  EXPECT_FALSE(InstanceColumnKey() < InstanceColumnKey());\n  EXPECT_FALSE(InstanceColumnKey() < InstanceColumnKey(1));\n\n  EXPECT_FALSE(AdviceColumnKey() < AdviceColumnKey());\n  EXPECT_FALSE(AdviceColumnKey() < AdviceColumnKey(0, kFirstPhase));\n  EXPECT_FALSE(AdviceColumnKey() < AdviceColumnKey(1, kFirstPhase));\n  EXPECT_TRUE(AdviceColumnKey() < AdviceColumnKey(0, kSecondPhase));\n\n  EXPECT_FALSE(FixedColumnKey() < FixedColumnKey());\n  EXPECT_FALSE(FixedColumnKey() < FixedColumnKey(1));\n\n  EXPECT_TRUE(InstanceColumnKey() < AdviceColumnKey());\n  EXPECT_TRUE(InstanceColumnKey(1) < AdviceColumnKey());\n  EXPECT_TRUE(InstanceColumnKey() < FixedColumnKey());\n  EXPECT_TRUE(InstanceColumnKey(1) < FixedColumnKey());\n  EXPECT_TRUE(AdviceColumnKey() < FixedColumnKey());\n  EXPECT_TRUE(AdviceColumnKey(1, kSecondPhase) < FixedColumnKey());\n}\n\nTEST(ColumnKeyBaseTest, Hash) {\n  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(\n      std::make_tuple(ColumnKeyBase(), ColumnKeyBase(ColumnType::kFixed, 0),\n                      ColumnKeyBase(ColumnType::kFixed, 1),\n                      ColumnKeyBase(ColumnType::kAdvice, 0),\n                      ColumnKeyBase(ColumnType::kAdvice, 1),\n                      ColumnKeyBase(ColumnType::kInstance, 0),\n                      ColumnKeyBase(ColumnType::kInstance, 1))));\n}\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/base/column_type.cc",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#include \"tachyon/zk/plonk/base/column_type.h\"\n\n#include \"tachyon/base/logging.h\"\n\nnamespace tachyon::zk::plonk {\n\nstd::string_view ColumnTypeToString(ColumnType type) {\n  switch (type) {\n    case ColumnType::kAny:\n      return \"Any\";\n    case ColumnType::kFixed:\n      return \"Fixed\";\n    case ColumnType::kAdvice:\n      return \"Advice\";\n    case ColumnType::kInstance:\n      return \"Instance\";\n  }\n  NOTREACHED();\n  return \"\";\n}\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/base/column_type.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_BASE_COLUMN_TYPE_H_\n#define TACHYON_ZK_PLONK_BASE_COLUMN_TYPE_H_\n\n#include <ostream>\n#include <string_view>\n\n#include \"tachyon/export.h\"\n\nnamespace tachyon::zk::plonk {\n\n// As the names denotes, |kFixed|, |kAdvice| and |kInstance| types\n// represent fixed, advice and instance columns.\n// |kAny| type of column can be either fixed, advice or instance column\n// THE ORDER OF ELEMENTS ARE IMPORTANT!! DO NOT CHANGE!\n// The comparison operator depends on the order of elements.\n// Please refer to tachyon/zk/plonk/base/column_key.h for details.\nenum class ColumnType {\n  kAny,\n  kInstance,\n  kAdvice,\n  kFixed,\n};\n\nTACHYON_EXPORT std::string_view ColumnTypeToString(ColumnType type);\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_BASE_COLUMN_TYPE_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/base/l_values.h",
    "content": "#ifndef TACHYON_ZK_PLONK_BASE_L_VALUES_H_\n#define TACHYON_ZK_PLONK_BASE_L_VALUES_H_\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F>\nstruct LValues {\n  LValues(const F& first, const F& blind, const F& last)\n      : first(first), blind(blind), last(last) {}\n\n  const F& first;\n  const F& blind;\n  const F& last;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_BASE_L_VALUES_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/base/multi_phase_evaluations.h",
    "content": "#ifndef TACHYON_ZK_PLONK_BASE_MULTI_PHASE_EVALUATIONS_H_\n#define TACHYON_ZK_PLONK_BASE_MULTI_PHASE_EVALUATIONS_H_\n\n#include \"absl/types/span.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F>\nstruct MultiPhaseEvaluations {\n  MultiPhaseEvaluations() = default;\n  MultiPhaseEvaluations(absl::Span<const F> fixed_evals,\n                        absl::Span<const F> advice_evals,\n                        absl::Span<const F> instance_evals,\n                        absl::Span<const F> challenges)\n      : fixed_evals(fixed_evals),\n        advice_evals(advice_evals),\n        instance_evals(instance_evals),\n        challenges(challenges) {}\n\n  absl::Span<const F> fixed_evals;\n  absl::Span<const F> advice_evals;\n  absl::Span<const F> instance_evals;\n  absl::Span<const F> challenges;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_BASE_MULTI_PHASE_EVALUATIONS_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/base/multi_phase_owned_table.h",
    "content": "#ifndef TACHYON_ZK_PLONK_BASE_MULTI_PHASE_OWNED_TABLE_H_\n#define TACHYON_ZK_PLONK_BASE_MULTI_PHASE_OWNED_TABLE_H_\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/zk/plonk/base/owned_table.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename PolyOrEvals>\nclass MultiPhaseOwnedTable : public OwnedTable<PolyOrEvals> {\n public:\n  using F = typename PolyOrEvals::Field;\n\n  MultiPhaseOwnedTable() = default;\n  MultiPhaseOwnedTable(std::vector<PolyOrEvals>&& fixed_columns,\n                       std::vector<PolyOrEvals>&& advice_columns,\n                       std::vector<PolyOrEvals>&& instance_columns,\n                       absl::Span<const F> challenges)\n      : OwnedTable<PolyOrEvals>(std::move(fixed_columns),\n                                std::move(advice_columns),\n                                std::move(instance_columns)),\n        challenges_(challenges) {}\n\n  void set_challenges(absl::Span<const F> challenges) {\n    challenges_ = challenges;\n  }\n  absl::Span<const F> challenges() const { return challenges_; }\n\n private:\n  absl::Span<const F> challenges_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_BASE_MULTI_PHASE_OWNED_TABLE_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/base/multi_phase_ref_table.h",
    "content": "#ifndef TACHYON_ZK_PLONK_BASE_MULTI_PHASE_REF_TABLE_H_\n#define TACHYON_ZK_PLONK_BASE_MULTI_PHASE_REF_TABLE_H_\n\n#include \"tachyon/zk/plonk/base/ref_table.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename PolyOrEvals>\nclass MultiPhaseRefTable : public RefTable<PolyOrEvals> {\n public:\n  using F = typename PolyOrEvals::Field;\n\n  MultiPhaseRefTable() = default;\n  MultiPhaseRefTable(absl::Span<const PolyOrEvals> fixed_columns,\n                     absl::Span<const PolyOrEvals> advice_columns,\n                     absl::Span<const PolyOrEvals> instance_columns,\n                     absl::Span<const F> challenges)\n      : RefTable<PolyOrEvals>(fixed_columns, advice_columns, instance_columns),\n        challenges_(challenges) {}\n\n  absl::Span<const F> challenges() const { return challenges_; }\n\n private:\n  absl::Span<const F> challenges_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_BASE_MULTI_PHASE_REF_TABLE_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/base/owned_table.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_BASE_OWNED_TABLE_H_\n#define TACHYON_ZK_PLONK_BASE_OWNED_TABLE_H_\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/zk/plonk/base/table_base.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename PolyOrEvals>\nclass OwnedTable : public TableBase<PolyOrEvals> {\n public:\n  OwnedTable() = default;\n  OwnedTable(std::vector<PolyOrEvals>&& fixed_columns,\n             std::vector<PolyOrEvals>&& advice_columns,\n             std::vector<PolyOrEvals>&& instance_columns)\n      : fixed_columns_(std::move(fixed_columns)),\n        advice_columns_(std::move(advice_columns)),\n        instance_columns_(std::move(instance_columns)) {}\n\n  std::vector<PolyOrEvals>& fixed_columns() { return fixed_columns_; }\n  std::vector<PolyOrEvals>& advice_columns() { return advice_columns_; }\n  std::vector<PolyOrEvals>& instance_columns() { return instance_columns_; }\n\n  // TableBase<PolyOrEvals> methods\n  absl::Span<const PolyOrEvals> GetFixedColumns() const override {\n    return fixed_columns_;\n  }\n  absl::Span<const PolyOrEvals> GetAdviceColumns() const override {\n    return advice_columns_;\n  }\n  absl::Span<const PolyOrEvals> GetInstanceColumns() const override {\n    return instance_columns_;\n  }\n\n protected:\n  std::vector<PolyOrEvals> fixed_columns_;\n  std::vector<PolyOrEvals> advice_columns_;\n  std::vector<PolyOrEvals> instance_columns_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_BASE_OWNED_TABLE_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/base/phase.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_BASE_PHASE_H_\n#define TACHYON_ZK_PLONK_BASE_PHASE_H_\n\n#include <stdint.h>\n\n#include <string>\n#include <utility>\n\n#include \"absl/hash/hash.h\"\n\n#include \"tachyon/base/strings/string_number_conversions.h\"\n#include \"tachyon/export.h\"\n\nnamespace tachyon::zk::plonk {\n\n// NOTE(lightscale-luke): Phase class is copyable, assignable, and occupies 8\n// bits per instance. Prefer to pass them by value.\nclass TACHYON_EXPORT Phase {\n public:\n  constexpr Phase() = default;\n  constexpr explicit Phase(uint8_t value) : value_(value) {}\n\n  bool Prev(Phase* prev) const {\n    if (value_ == 0) return false;\n    *prev = Phase(value_ - 1);\n    return true;\n  }\n\n  uint8_t value() const { return value_; }\n\n  bool operator==(Phase other) const { return value_ == other.value_; }\n  bool operator!=(Phase other) const { return value_ != other.value_; }\n  bool operator>(Phase other) const { return value_ > other.value_; }\n  bool operator>=(Phase other) const { return value_ >= other.value_; }\n  bool operator<(Phase other) const { return value_ < other.value_; }\n  bool operator<=(Phase other) const { return value_ <= other.value_; }\n\n  std::string ToString() const { return base::NumberToString(value_); }\n\n private:\n  uint8_t value_ = 0;\n};\n\nconstexpr static Phase kFirstPhase = Phase(0);\nconstexpr static Phase kSecondPhase = Phase(1);\n\ntemplate <typename H>\nH AbslHashValue(H h, Phase phase) {\n  return H::combine(std::move(h), phase.value());\n}\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_BASE_PHASE_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/base/phase_unittest.cc",
    "content": "#include \"tachyon/zk/plonk/base/phase.h\"\n\n#include \"absl/hash/hash_testing.h\"\n#include \"gtest/gtest.h\"\n\nnamespace tachyon::zk::plonk {\n\nTEST(PhaseTest, Hash) {\n  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(\n      std::make_tuple(kFirstPhase, kSecondPhase)));\n}\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/base/ref_table.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_BASE_REF_TABLE_H_\n#define TACHYON_ZK_PLONK_BASE_REF_TABLE_H_\n\n#include <vector>\n\n#include \"tachyon/zk/plonk/base/table_base.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename PolyOrEvals>\nclass RefTable : public TableBase<PolyOrEvals> {\n public:\n  RefTable() = default;\n  RefTable(absl::Span<const PolyOrEvals> fixed_columns,\n           absl::Span<const PolyOrEvals> advice_columns,\n           absl::Span<const PolyOrEvals> instance_columns)\n      : fixed_columns_(fixed_columns),\n        advice_columns_(advice_columns),\n        instance_columns_(instance_columns) {}\n\n  // TableBase<PolyOrEvals> methods\n  absl::Span<const PolyOrEvals> GetFixedColumns() const override {\n    return fixed_columns_;\n  }\n  absl::Span<const PolyOrEvals> GetAdviceColumns() const override {\n    return advice_columns_;\n  }\n  absl::Span<const PolyOrEvals> GetInstanceColumns() const override {\n    return instance_columns_;\n  }\n\n protected:\n  absl::Span<const PolyOrEvals> fixed_columns_;\n  absl::Span<const PolyOrEvals> advice_columns_;\n  absl::Span<const PolyOrEvals> instance_columns_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_BASE_REF_TABLE_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/base/ref_table_unittest.cc",
    "content": "#include \"tachyon/zk/plonk/base/ref_table.h\"\n\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/elliptic_curves/bn/bn254/g1.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluations.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename ColumnKey>\nclass RefTableTest : public testing::Test {\n public:\n  constexpr static size_t kMaxDegree = (size_t{1} << 3) - 1;\n\n  using F = math::bn254::G1AffinePoint::ScalarField;\n  using Evals = math::UnivariateEvaluations<F, kMaxDegree>;\n\n  static void SetUpTestSuite() { math::bn254::G1Curve::Init(); }\n\n  void SetUp() override {\n    for (size_t i = 0; i < 5; ++i) {\n      fixed_columns_.push_back(\n          math::UnivariateEvaluations<F, kMaxDegree>::Random(kMaxDegree));\n      advice_columns_.push_back(\n          math::UnivariateEvaluations<F, kMaxDegree>::Random(kMaxDegree));\n      instance_columns_.push_back(\n          math::UnivariateEvaluations<F, kMaxDegree>::Random(kMaxDegree));\n    }\n  }\n\n protected:\n  std::vector<Evals> fixed_columns_;\n  std::vector<Evals> advice_columns_;\n  std::vector<Evals> instance_columns_;\n};\n\nusing ColumnKeyTypes = testing::Types<ColumnKeyBase, AnyColumnKey>;\nTYPED_TEST_SUITE(RefTableTest, ColumnKeyTypes);\n\nTYPED_TEST(RefTableTest, GetColumns) {\n  using ColumnKey = TypeParam;\n  using Evals = typename RefTableTest<ColumnKey>::Evals;\n\n  std::vector<ColumnKey> targets = {\n      FixedColumnKey(1), AdviceColumnKey(1, kFirstPhase), InstanceColumnKey(1),\n      FixedColumnKey(2), AdviceColumnKey(2, kFirstPhase), InstanceColumnKey(2),\n  };\n\n  RefTable<Evals> table(this->fixed_columns_, this->advice_columns_,\n                        this->instance_columns_);\n\n  std::vector<base::Ref<const Evals>> evals_refs = table.GetColumns(targets);\n\n  EXPECT_EQ(*evals_refs[0], this->fixed_columns_[1]);\n  EXPECT_EQ(*evals_refs[1], this->advice_columns_[1]);\n  EXPECT_EQ(*evals_refs[2], this->instance_columns_[1]);\n  EXPECT_EQ(*evals_refs[3], this->fixed_columns_[2]);\n  EXPECT_EQ(*evals_refs[4], this->advice_columns_[2]);\n  EXPECT_EQ(*evals_refs[5], this->instance_columns_[2]);\n}\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/base/table_base.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_BASE_TABLE_BASE_H_\n#define TACHYON_ZK_PLONK_BASE_TABLE_BASE_H_\n\n#include <vector>\n\n#include \"absl/types/span.h\"\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/ref.h\"\n#include \"tachyon/base/template_util.h\"\n#include \"tachyon/zk/plonk/base/column_key.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename PolyOrEvals>\nclass TableBase {\n public:\n  virtual ~TableBase() = default;\n\n  virtual absl::Span<const PolyOrEvals> GetFixedColumns() const = 0;\n  virtual absl::Span<const PolyOrEvals> GetAdviceColumns() const = 0;\n  virtual absl::Span<const PolyOrEvals> GetInstanceColumns() const = 0;\n\n  base::Ref<const PolyOrEvals> GetColumn(\n      const ColumnKeyBase& column_key) const {\n    switch (column_key.type()) {\n      case ColumnType::kFixed:\n        return base::Ref<const PolyOrEvals>(\n            &GetFixedColumns()[column_key.index()]);\n      case ColumnType::kAdvice:\n        return base::Ref<const PolyOrEvals>(\n            &GetAdviceColumns()[column_key.index()]);\n      case ColumnType::kInstance:\n        return base::Ref<const PolyOrEvals>(\n            &GetInstanceColumns()[column_key.index()]);\n      case ColumnType::kAny:\n        break;\n    }\n    NOTREACHED();\n    return base::Ref<const PolyOrEvals>();\n  }\n\n  template <typename Container>\n  std::vector<base::Ref<const PolyOrEvals>> GetColumns(\n      const Container& column_keys) const {\n    return base::Map(\n        column_keys,\n        [this](const base::container_value_t<Container>& column_key) {\n          return GetColumn(column_key);\n        });\n  }\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_BASE_TABLE_BASE_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/constraint_system/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"challenge\",\n    hdrs = [\"challenge.h\"],\n    deps = [\n        \"//tachyon/zk/plonk/base:phase\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"circuit\",\n    hdrs = [\"circuit.h\"],\n    deps = [\n        \"//tachyon/zk/plonk/layout:layouter\",\n        \"//tachyon/zk/plonk/layout/floor_planner\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"constraint\",\n    hdrs = [\"constraint.h\"],\n    deps = [\"//tachyon/zk/expressions:expression\"],\n)\n\ntachyon_cc_library(\n    name = \"constraint_system\",\n    hdrs = [\"constraint_system.h\"],\n    deps = [\n        \":constraint\",\n        \":gate\",\n        \":lookup_tracker\",\n        \":query\",\n        \":selector_compressor\",\n        \":virtual_cells\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/base/containers:contains\",\n        \"//tachyon/base/functional:callback\",\n        \"//tachyon/base/strings:string_util\",\n        \"//tachyon/zk/base:row_types\",\n        \"//tachyon/zk/lookup:argument\",\n        \"//tachyon/zk/lookup:type\",\n        \"//tachyon/zk/plonk/expressions/evaluator:cells_querier\",\n        \"//tachyon/zk/plonk/expressions/evaluator:identifier\",\n        \"//tachyon/zk/plonk/expressions/evaluator:selector_replacer\",\n        \"//tachyon/zk/plonk/expressions/evaluator:simple_selector_extractor\",\n        \"//tachyon/zk/plonk/expressions/evaluator:simple_selector_finder\",\n        \"//tachyon/zk/plonk/keys:c_proving_key_impl_forward\",\n        \"//tachyon/zk/plonk/layout:lookup_table_column\",\n        \"//tachyon/zk/plonk/permutation:permutation_argument\",\n        \"//tachyon/zk/plonk/permutation:permutation_utils\",\n        \"//tachyon/zk/shuffle:argument\",\n        \"//tachyon/zk/shuffle:pair\",\n        \"@com_google_absl//absl/container:btree\",\n        \"@com_google_absl//absl/container:flat_hash_map\",\n        \"@com_google_absl//absl/numeric:bits\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"exclusion_matrix\",\n    hdrs = [\"exclusion_matrix.h\"],\n    deps = [\n        \":selector_description\",\n        \"//tachyon:export\",\n        \"//tachyon/base/containers:container_util\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"gate\",\n    hdrs = [\"gate.h\"],\n    deps = [\n        \":selector\",\n        \":virtual_cell\",\n        \"//tachyon/zk/expressions:expression\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"lookup_tracker\",\n    hdrs = [\"lookup_tracker.h\"],\n    deps = [\"//tachyon/zk/expressions:expression\"],\n)\n\ntachyon_cc_library(\n    name = \"query\",\n    hdrs = [\"query.h\"],\n    deps = [\n        \"//tachyon/base/strings:string_util\",\n        \"//tachyon/zk/base:rotation\",\n        \"//tachyon/zk/plonk/base:column_key\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"selector\",\n    hdrs = [\"selector.h\"],\n    deps = [\n        \"//tachyon:export\",\n        \"//tachyon/zk/base:row_types\",\n        \"@com_google_absl//absl/hash\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"selector_assignment\",\n    hdrs = [\"selector_assignment.h\"],\n    deps = [\"//tachyon/zk/expressions:expression\"],\n)\n\ntachyon_cc_library(\n    name = \"selector_compressor\",\n    hdrs = [\"selector_compressor.h\"],\n    deps = [\n        \":exclusion_matrix\",\n        \":selector_assignment\",\n        \":selector_description\",\n        \"//tachyon/base:parallelize\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/base/containers:cxx20_erase\",\n        \"//tachyon/base/functional:callback\",\n        \"//tachyon/math/base:parallelize_threshold\",\n        \"//tachyon/zk/expressions:expression_factory\",\n        \"@com_google_googletest//:gtest_prod\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"selector_description\",\n    hdrs = [\"selector_description.h\"],\n    deps = [\n        \"//tachyon:export\",\n        \"//tachyon/base/containers:adapters\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"virtual_cell\",\n    hdrs = [\"virtual_cell.h\"],\n    deps = [\n        \"//tachyon:export\",\n        \"//tachyon/zk/base:rotation\",\n        \"//tachyon/zk/plonk/base:column_key\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"virtual_cells\",\n    hdrs = [\"virtual_cells.h\"],\n    deps = [\n        \":selector\",\n        \":virtual_cell\",\n        \"//tachyon/zk/plonk/expressions:expression_factory\",\n        \"//tachyon/zk/plonk/expressions/evaluator:simple_selector_finder\",\n        \"//tachyon/zk/plonk/layout:lookup_table_column\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"plonk_unittests\",\n    srcs = [\n        \"constraint_system_unittest.cc\",\n        \"exclusion_matrix_unittest.cc\",\n        \"selector_compressor_unittest.cc\",\n        \"selector_description_unittest.cc\",\n        \"selector_unittest.cc\",\n    ],\n    deps = [\n        \":constraint_system\",\n        \"//tachyon/math/finite_fields/test:finite_field_test\",\n        \"//tachyon/math/finite_fields/test:gf7\",\n        \"@com_google_absl//absl/hash:hash_testing\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/zk/plonk/constraint_system/challenge.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_CHALLENGE_H_\n#define TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_CHALLENGE_H_\n\n#include <string>\n\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/zk/plonk/base/phase.h\"\n\nnamespace tachyon::zk::plonk {\n\n// NOTE(dongchangYoo): Challenge class is copyable, assignable, and occupies 72\n// bits per instance. Prefer to pass them by value.\nclass TACHYON_EXPORT Challenge {\n public:\n  Challenge(size_t index, Phase phase) : index_(index), phase_(phase) {}\n\n  size_t index() const { return index_; }\n  Phase phase() const { return phase_; }\n\n  bool operator==(Challenge other) const {\n    return index_ == other.index_ && phase_ == other.phase_;\n  }\n  bool operator!=(Challenge other) const { return !operator==(other); }\n\n  std::string ToString() const {\n    return absl::Substitute(\"{index: $0, phase: $1}\", index_,\n                            phase_.ToString());\n  }\n\n private:\n  size_t index_ = 0;\n  Phase phase_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_CHALLENGE_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/constraint_system/circuit.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_CIRCUIT_H_\n#define TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_CIRCUIT_H_\n\n#include <memory>\n\n#include \"tachyon/zk/plonk/layout/layouter.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename _Config>\nclass Circuit {\n public:\n  using Config = _Config;\n  using Field = typename Config::Field;\n\n  virtual ~Circuit() = default;\n\n  virtual std::unique_ptr<Circuit> WithoutWitness() const = 0;\n\n  virtual void Synthesize(Config&& config, Layouter<Field>* layouter) const = 0;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_CIRCUIT_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/constraint_system/constraint.h",
    "content": "#ifndef TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_CONSTRAINT_H_\n#define TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_CONSTRAINT_H_\n\n#include <memory>\n#include <string>\n#include <utility>\n\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/zk/expressions/expression.h\"\n\nnamespace tachyon::zk::plonk {\n\n// An individual polynomial constraint.\n//\n// These are returned by the closures passed to\n// |ConstraintSystem::CreateGate()|.\ntemplate <typename F>\nclass Constraint {\n public:\n  explicit Constraint(std::unique_ptr<Expression<F>> expression)\n      : expression_(std::move(expression)) {}\n  Constraint(std::string_view name, std::unique_ptr<Expression<F>> expression)\n      : name_(std::string(name)), expression_(std::move(expression)) {}\n\n  const std::string& name() const& { return name_; }\n  const std::unique_ptr<Expression<F>>& expression() const& {\n    return expression_;\n  }\n\n  std::string&& TakeName() && { return std::move(name_); }\n  std::unique_ptr<Expression<F>>&& TakeExpression() && {\n    return std::move(expression_);\n  }\n\n  std::string ToString() const {\n    return absl::Substitute(\"{name: $0, expression: $1}\", name_,\n                            expression_->ToString());\n  }\n\n private:\n  std::string name_;\n  std::unique_ptr<Expression<F>> expression_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_CONSTRAINT_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/constraint_system/constraint_system.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_CONSTRAINT_SYSTEM_H_\n#define TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_CONSTRAINT_SYSTEM_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <algorithm>\n#include <memory>\n#include <optional>\n#include <sstream>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"absl/container/btree_map.h\"\n#include \"absl/container/flat_hash_map.h\"\n#include \"absl/numeric/bits.h\"\n#include \"gtest/gtest_prod.h\"\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/containers/contains.h\"\n#include \"tachyon/base/functional/callback.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/strings/string_util.h\"\n#include \"tachyon/zk/base/row_types.h\"\n#include \"tachyon/zk/lookup/argument.h\"\n#include \"tachyon/zk/lookup/type.h\"\n#include \"tachyon/zk/plonk/constraint_system/constraint.h\"\n#include \"tachyon/zk/plonk/constraint_system/gate.h\"\n#include \"tachyon/zk/plonk/constraint_system/lookup_tracker.h\"\n#include \"tachyon/zk/plonk/constraint_system/query.h\"\n#include \"tachyon/zk/plonk/constraint_system/selector_compressor.h\"\n#include \"tachyon/zk/plonk/constraint_system/virtual_cells.h\"\n#include \"tachyon/zk/plonk/expressions/evaluator/cells_querier.h\"\n#include \"tachyon/zk/plonk/expressions/evaluator/identifier.h\"\n#include \"tachyon/zk/plonk/expressions/evaluator/selector_replacer.h\"\n#include \"tachyon/zk/plonk/expressions/evaluator/simple_selector_extractor.h\"\n#include \"tachyon/zk/plonk/expressions/evaluator/simple_selector_finder.h\"\n#include \"tachyon/zk/plonk/keys/c_proving_key_impl_forward.h\"\n#include \"tachyon/zk/plonk/layout/lookup_table_column.h\"\n#include \"tachyon/zk/plonk/permutation/permutation_argument.h\"\n#include \"tachyon/zk/plonk/permutation/permutation_utils.h\"\n#include \"tachyon/zk/shuffle/argument.h\"\n#include \"tachyon/zk/shuffle/pair.h\"\n\nnamespace tachyon::zk::plonk {\n\n// This is a description of the circuit environment, such as the gate, column\n// and permutation arrangements.\ntemplate <typename F>\nclass ConstraintSystem {\n public:\n  using LookupCallback =\n      base::OnceCallback<lookup::Pairs<std::unique_ptr<Expression<F>>,\n                                       LookupTableColumn>(VirtualCells<F>&)>;\n  using LookupAnyCallback =\n      base::OnceCallback<lookup::Pairs<std::unique_ptr<Expression<F>>>(\n          VirtualCells<F>&)>;\n  using ShuffleCallback =\n      base::OnceCallback<shuffle::Pairs<std::unique_ptr<Expression<F>>>(\n          VirtualCells<F>&)>;\n  using ConstrainCallback =\n      base::OnceCallback<std::vector<Constraint<F>>(VirtualCells<F>&)>;\n\n  ConstraintSystem() = default;\n\n  lookup::Type lookup_type() const { return lookup_type_; }\n\n  void set_lookup_type(lookup::Type lookup_type) { lookup_type_ = lookup_type; }\n\n  size_t num_fixed_columns() const { return num_fixed_columns_; }\n\n  size_t num_advice_columns() const { return num_advice_columns_; }\n\n  size_t num_instance_columns() const { return num_instance_columns_; }\n\n  size_t num_simple_selectors() const { return num_simple_selectors_; }\n\n  size_t num_complex_selectors() const { return num_complex_selectors_; }\n\n  size_t num_challenges() const { return num_challenges_; }\n\n  const std::vector<Phase>& advice_column_phases() const {\n    return advice_column_phases_;\n  }\n\n  const std::vector<Phase>& challenge_phases() const {\n    return challenge_phases_;\n  }\n\n  const std::vector<FixedColumnKey>& selector_map() const {\n    return selector_map_;\n  }\n\n  const std::vector<Gate<F>>& gates() const { return gates_; }\n\n  const std::vector<AdviceQueryData>& advice_queries() const {\n    return advice_queries_;\n  }\n\n  const std::vector<RowIndex>& num_advice_queries() const {\n    return num_advice_queries_;\n  }\n\n  const std::vector<InstanceQueryData>& instance_queries() const {\n    return instance_queries_;\n  }\n\n  const std::vector<FixedQueryData>& fixed_queries() const {\n    return fixed_queries_;\n  }\n\n  const PermutationArgument& permutation() const { return permutation_; }\n\n  const std::vector<lookup::Argument<F>>& lookups() const { return lookups_; }\n\n  const absl::btree_map<std::string, LookupTracker<F>>& lookups_map() const {\n    return lookups_map_;\n  }\n\n  const std::vector<shuffle::Argument<F>>& shuffles() const {\n    return shuffles_;\n  }\n\n  const absl::flat_hash_map<ColumnKeyBase, std::string>&\n  general_column_annotations() const {\n    return general_column_annotations_;\n  }\n\n  const std::vector<FixedColumnKey>& constants() const { return constants_; }\n\n  const std::optional<size_t>& minimum_degree() const {\n    return minimum_degree_;\n  }\n\n  // Sets the minimum degree required by the circuit, which can be set to a\n  // larger amount than actually needed. This can be used, for example, to\n  // force the permutation argument to involve more columns in the same set.\n  void set_minimum_degree(size_t degree) {\n    if (minimum_degree_) {\n      minimum_degree_ = std::max(minimum_degree_.value(), degree);\n    } else {\n      minimum_degree_ = degree;\n    }\n  }\n\n  size_t GetNumSelectors() const {\n    return num_simple_selectors_ + num_complex_selectors_;\n  }\n\n  // Enables this fixed |column| to be used for global constant assignments.\n  // The |column| will be equality-enabled, too.\n  void EnableConstant(const FixedColumnKey& column) {\n    // TODO(chokobole): should it be std::set<FixedColumnKey>?\n    if (!base::Contains(constants_, column)) {\n      constants_.push_back(column);\n      EnableEquality(AnyColumnKey(column));\n    }\n  }\n\n  // Enable the ability to enforce equality over cells in this column\n  void EnableEquality(const AnyColumnKey& column) {\n    QueryAnyIndex(column, Rotation::Cur());\n    permutation_.AddColumn(column);\n  }\n\n  // Add a lookup argument for some input expressions and table columns.\n  //\n  // |callback| returns a map between the input expressions and the table\n  // columns they need to match.\n  void Lookup(std::string_view name, LookupCallback callback) {\n    VirtualCells cells(this);\n    lookup::Pairs<std::unique_ptr<Expression<F>>, LookupTableColumn> pairs =\n        std::move(callback).Run(cells);\n\n    switch (lookup_type_) {\n      case lookup::Type::kHalo2: {\n        lookup::Pairs<std::unique_ptr<Expression<F>>> lookup_pairs = base::Map(\n            pairs, [&cells](lookup::Pair<std::unique_ptr<Expression<F>>,\n                                         LookupTableColumn>& pair) {\n              std::unique_ptr<Expression<F>> table =\n                  cells.QueryLookupTable(pair, Rotation::Cur());\n              QueryCells(pair.input().get(), cells);\n              QueryCells(table.get(), cells);\n              return lookup::Pair<std::unique_ptr<Expression<F>>>(\n                  std::move(pair).TakeInput(), std::move(table));\n            });\n        lookups_.emplace_back(name, std::move(lookup_pairs));\n        break;\n      }\n      case lookup::Type::kLogDerivativeHalo2: {\n        std::vector<std::unique_ptr<Expression<F>>> input_expressions;\n        std::vector<std::unique_ptr<Expression<F>>> table_expressions;\n\n        input_expressions.reserve(pairs.size());\n        table_expressions.reserve(pairs.size());\n\n        for (lookup::Pair<std::unique_ptr<Expression<F>>, LookupTableColumn>&\n                 pair : pairs) {\n          std::unique_ptr<Expression<F>> table =\n              cells.QueryLookupTable(pair, Rotation::Cur());\n          QueryCells(pair.input().get(), cells);\n          QueryCells(table.get(), cells);\n          input_expressions.push_back(std::move(pair).TakeInput());\n          table_expressions.push_back(std::move(table));\n        }\n        UpdateLookupsMap(name, std::move(input_expressions),\n                         std::move(table_expressions));\n        break;\n      }\n    }\n  }\n\n  // Add a lookup argument for some input expressions and table expressions.\n  //\n  // |callback| returns a map between the input expressions and the table\n  // expressions they need to match.\n  void LookupAny(std::string_view name, LookupAnyCallback callback) {\n    VirtualCells cells(this);\n    lookup::Pairs<std::unique_ptr<Expression<F>>> pairs =\n        std::move(callback).Run(cells);\n\n    switch (lookup_type_) {\n      case lookup::Type::kHalo2: {\n        for (const lookup::Pair<std::unique_ptr<Expression<F>>>& pair : pairs) {\n          CHECK(!ContainsSimpleSelector(pair.input().get()))\n              << \"expression containing simple selector \"\n                 \"supplied to lookup argument\";\n        }\n        lookups_.emplace_back(name, std::move(pairs));\n        break;\n      }\n      case lookup::Type::kLogDerivativeHalo2: {\n        std::vector<std::unique_ptr<Expression<F>>> input_expressions;\n        std::vector<std::unique_ptr<Expression<F>>> table_expressions;\n\n        input_expressions.reserve(pairs.size());\n        table_expressions.reserve(pairs.size());\n\n        for (lookup::Pair<std::unique_ptr<Expression<F>>>& pair : pairs) {\n          CHECK(!ContainsSimpleSelector(pair.input().get()))\n              << \"expression containing simple selector \"\n                 \"supplied to lookup argument\";\n\n          input_expressions.push_back(std::move(pair).TakeInput());\n          table_expressions.push_back(std::move(pair).TakeTable());\n        }\n\n        UpdateLookupsMap(name, std::move(input_expressions),\n                         std::move(table_expressions));\n        break;\n      }\n    }\n  }\n\n  // Chunk lookup arguments into pieces below a given degree bound. Compute the\n  // |minimum_degree| from gates and lookups, and then construct the\n  // |lookup_arguments| of chunks smaller than the |minimum_degree| from the\n  // |lookups_map|.\n  void ChunkLookups() {\n    CHECK_EQ(lookup_type_, lookup::Type::kLogDerivativeHalo2);\n\n    if (lookups_map_.empty()) {\n      return;\n    }\n\n    size_t max_gate_degree = ComputeGateRequiredDegree();\n\n    size_t max_single_lookup_degree = 0;\n    for (const auto& [_, lookup_tracker] : lookups_map_) {\n      size_t table_degree = ComputeColumnDegree(lookup_tracker.table);\n\n      // Compute base degree only from table without inputs.\n      size_t base_lookup_degree = ComputeBaseDegree(table_degree);\n\n      size_t max_inputs_degree = 0;\n      for (const std::vector<std::unique_ptr<Expression<F>>>& input :\n           lookup_tracker.inputs) {\n        max_inputs_degree =\n            std::max(max_inputs_degree, ComputeColumnDegree(input));\n      }\n\n      size_t current_degree =\n          ComputeDegreeWithInput(base_lookup_degree, max_inputs_degree);\n      max_single_lookup_degree =\n          std::max(max_single_lookup_degree, current_degree);\n    }\n\n    size_t required_degree =\n        std::max(max_gate_degree, max_single_lookup_degree);\n\n    // The smallest power of 2 greater than or equal to |required_degree|.\n    size_t next_power_of_two = absl::bit_ceil(required_degree);\n\n    set_minimum_degree(next_power_of_two + 1);\n\n    size_t minimum_degree = minimum_degree_.value();\n\n    for (const auto& [_, lookup_tracker] : lookups_map_) {\n      std::vector<std::unique_ptr<Expression<F>>> cloned_input =\n          Expression<F>::CloneExpressions(lookup_tracker.inputs[0]);\n      std::vector<std::unique_ptr<Expression<F>>> cloned_table =\n          Expression<F>::CloneExpressions(lookup_tracker.table);\n      lookups_.emplace_back(lookup_tracker.name, std::move(cloned_input),\n                            std::move(cloned_table));\n\n      for (auto input = lookup_tracker.inputs.begin() + 1;\n           input != lookup_tracker.inputs.end(); ++input) {\n        // Compute the degree of the current set of input expressions.\n        size_t cur_input_degree = ComputeColumnDegree(*input);\n\n        bool added = false;\n        for (lookup::Argument<F>& lookup_argument : lookups_) {\n          // Try to fit input into one of the |lookup_arguments|.\n          size_t cur_argument_degree = lookup_argument.RequiredDegree();\n          size_t new_potential_degree = cur_argument_degree + cur_input_degree;\n          if (new_potential_degree <= minimum_degree) {\n            std::vector<std::unique_ptr<Expression<F>>> cloned_input =\n                Expression<F>::CloneExpressions(*input);\n            lookup_argument.inputs_expressions().push_back(\n                std::move(cloned_input));\n            added = true;\n            break;\n          }\n        }\n\n        if (!added) {\n          std::vector<std::unique_ptr<Expression<F>>> cloned_input =\n              Expression<F>::CloneExpressions(*input);\n          std::vector<std::unique_ptr<Expression<F>>> cloned_table =\n              Expression<F>::CloneExpressions(lookup_tracker.table);\n          lookups_.emplace_back(lookup_tracker.name, std::move(cloned_input),\n                                std::move(cloned_table));\n        }\n      }\n    }\n  }\n\n  // Add a shuffle argument for some input expressions and shuffle expressions.\n  size_t Shuffle(std::string_view name, ShuffleCallback callback) {\n    VirtualCells cells(this);\n    shuffle::Pairs<std::unique_ptr<Expression<F>>> pairs =\n        std::move(callback).Run(cells);\n\n    std::vector<std::unique_ptr<Expression<F>>> input_expressions;\n    std::vector<std::unique_ptr<Expression<F>>> shuffle_expressions;\n\n    input_expressions.reserve(pairs.size());\n    shuffle_expressions.reserve(pairs.size());\n\n    for (shuffle::Pair<std::unique_ptr<Expression<F>>>& pair : pairs) {\n      input_expressions.push_back(std::move(pair).TakeInput());\n      shuffle_expressions.push_back(std::move(pair).TakeShuffle());\n    }\n\n    shuffles_.emplace_back(name, std::move(input_expressions),\n                           std::move(shuffle_expressions));\n    return shuffles_.size() - 1;\n  }\n\n  size_t QueryFixedIndex(const FixedColumnKey& column, Rotation at) {\n    // Return existing query, if it exists\n    size_t index;\n    if (QueryIndex(fixed_queries_, column, at, &index)) return index;\n\n    // Make a new query\n    fixed_queries_.emplace_back(at, column);\n    return fixed_queries_.size() - 1;\n  }\n\n  size_t QueryAdviceIndex(const AdviceColumnKey& column, Rotation at) {\n    // Return existing query, if it exists\n    size_t index;\n    if (QueryIndex(advice_queries_, column, at, &index)) return index;\n\n    // Make a new query\n    advice_queries_.emplace_back(at, column);\n    ++num_advice_queries_[column.index()];\n    return advice_queries_.size() - 1;\n  }\n\n  size_t QueryInstanceIndex(const InstanceColumnKey& column, Rotation at) {\n    // Return existing query, if it exists\n    size_t index;\n    if (QueryIndex(instance_queries_, column, at, &index)) return index;\n\n    // Make a new query\n    instance_queries_.emplace_back(at, column);\n    return instance_queries_.size() - 1;\n  }\n\n  size_t QueryAnyIndex(const AnyColumnKey& column, Rotation at) {\n    switch (column.type()) {\n      case ColumnType::kFixed:\n        return QueryFixedIndex(FixedColumnKey(column), at);\n      case ColumnType::kAdvice:\n        return QueryAdviceIndex(AdviceColumnKey(column), at);\n      case ColumnType::kInstance:\n        return QueryInstanceIndex(InstanceColumnKey(column), at);\n      case ColumnType::kAny:\n        break;\n    }\n    NOTREACHED();\n    return 0;\n  }\n\n  size_t GetFixedQueryIndex(const FixedColumnKey& column, Rotation at) const {\n    size_t index;\n    CHECK(QueryIndex(fixed_queries_, column, at, &index));\n    return index;\n  }\n\n  size_t GetAdviceQueryIndex(const AdviceColumnKey& column, Rotation at) const {\n    size_t index;\n    CHECK(QueryIndex(advice_queries_, column, at, &index));\n    return index;\n  }\n\n  size_t GetInstanceQueryIndex(const InstanceColumnKey& column,\n                               Rotation at) const {\n    size_t index;\n    CHECK(QueryIndex(instance_queries_, column, at, &index));\n    return index;\n  }\n\n  size_t GetAnyQueryIndex(const AnyColumnKey& column, Rotation at) const {\n    switch (column.type()) {\n      case ColumnType::kFixed:\n        return GetFixedQueryIndex(FixedColumnKey(column), at);\n      case ColumnType::kAdvice:\n        return GetAdviceQueryIndex(AdviceColumnKey(column), at);\n      case ColumnType::kInstance:\n        return GetInstanceQueryIndex(InstanceColumnKey(column), at);\n      case ColumnType::kAny:\n        break;\n    }\n    NOTREACHED();\n    return 0;\n  }\n\n  // Creates a new gate.\n  //\n  // A gate is required to contain polynomial constraints. This method will\n  // crash if |constrain| returns an empty iterator.\n  void CreateGate(std::string_view name, ConstrainCallback constrain) {\n    VirtualCells cells(this);\n    std::vector<Constraint<F>> constraints = std::move(constrain).Run(cells);\n    std::vector<Selector> queried_selectors =\n        std::move(cells).TakeQueriedSelectors();\n    std::vector<VirtualCell> queried_cells =\n        std::move(cells).TakeQueriedCells();\n\n    std::vector<std::string> constraint_names;\n    constraint_names.reserve(constraints.size());\n    std::vector<std::unique_ptr<Expression<F>>> polys;\n    polys.reserve(constraints.size());\n    for (Constraint<F>& constraint : constraints) {\n      QueryCells(constraint.expression().get(), cells);\n      constraint_names.push_back(std::move(constraint).TakeName());\n      polys.push_back(std::move(constraint).TakeExpression());\n    }\n    CHECK(!polys.empty()) << \"Gates must contain at least one constraint.\";\n\n    gates_.emplace_back(name, std::move(constraint_names), std::move(polys),\n                        std::move(queried_selectors), std::move(queried_cells));\n  }\n\n  // This will compress selectors together depending on their provided\n  // assignments. This |ConstraintSystem| will then be modified to add new\n  // fixed columns (representing the actual selectors) and will return the\n  // polynomials for those columns. Finally, an internal map is updated to\n  // find which fixed column corresponds with a given |Selector|.\n  //\n  // Do not call this twice. Yes, this should be a builder pattern instead.\n  std::vector<std::vector<F>> CompressSelectors(\n      const std::vector<std::vector<bool>>& selectors) {\n    // The number of provided selector assignments must be the number we\n    // counted for this constraint system.\n    CHECK_EQ(selectors.size(), GetNumSelectors());\n\n    // Compute the maximal degree of every selector. We only consider the\n    // expressions in gates, as lookup arguments cannot support simple\n    // selectors. Selectors that are complex or do not appear in any gates\n    // will have degree zero.\n    std::vector<size_t> degrees(selectors.size(), size_t{0});\n    for (const Gate<F>& gate : gates_) {\n      for (const std::unique_ptr<Expression<F>>& expression : gate.polys()) {\n        std::optional<Selector> selector =\n            ExtractSimpleSelector(expression.get());\n        if (selector.has_value()) {\n          degrees[selector->index()] =\n              std::max(degrees[selector->index()], expression->Degree());\n        }\n      }\n    }\n\n    // We will not increase the degree of the constraint system, so we limit\n    // ourselves to the largest existing degree constraint.\n    std::vector<FixedColumnKey> new_columns;\n    SelectorCompressor<F> selector_compressor;\n    selector_compressor.Process(\n        selectors, degrees, ComputeDegree(), [this, &new_columns]() {\n          FixedColumnKey column = CreateFixedColumn();\n          new_columns.push_back(column);\n          return ExpressionFactory<F>::Fixed(\n              FixedQuery(QueryFixedIndex(column, Rotation::Cur()),\n                         Rotation::Cur(), FixedColumnKey(column.index())));\n        });\n\n    std::vector<FixedColumnKey> selector_map;\n    selector_map.resize(selector_compressor.selector_assignments().size());\n    std::vector<base::Ref<const Expression<F>>> selector_replacements(\n        selector_compressor.selector_assignments().size(),\n        base::Ref<const Expression<F>>());\n    for (const SelectorAssignment<F>& assignment :\n         selector_compressor.selector_assignments()) {\n      selector_replacements[assignment.selector_index()] =\n          base::Ref<const Expression<F>>(assignment.expression());\n      selector_map[assignment.selector_index()] =\n          new_columns[assignment.combination_index()];\n    }\n\n    selector_map_ = std::move(selector_map);\n\n    for (Gate<F>& gate : gates_) {\n      for (std::unique_ptr<Expression<F>>& expression : gate.polys()) {\n        expression =\n            ReplaceSelectors(expression.get(), selector_replacements, false);\n      }\n    }\n    for (lookup::Argument<F>& lookup : lookups_) {\n      for (std::vector<std::unique_ptr<Expression<F>>>& input_expressions :\n           lookup.inputs_expressions()) {\n        for (std::unique_ptr<Expression<F>>& expression : input_expressions) {\n          expression =\n              ReplaceSelectors(expression.get(), selector_replacements, true);\n        }\n      }\n      for (std::unique_ptr<Expression<F>>& expression :\n           lookup.table_expressions()) {\n        expression =\n            ReplaceSelectors(expression.get(), selector_replacements, true);\n      }\n    }\n    for (shuffle::Argument<F>& shuffle : shuffles_) {\n      for (std::unique_ptr<Expression<F>>& expression :\n           shuffle.input_expressions()) {\n        expression =\n            ReplaceSelectors(expression.get(), selector_replacements, true);\n      }\n      for (std::unique_ptr<Expression<F>>& expression :\n           shuffle.shuffle_expressions()) {\n        expression =\n            ReplaceSelectors(expression.get(), selector_replacements, true);\n      }\n    }\n\n    return selector_compressor.combination_assignments();\n  }\n\n  // Allocate a new simple selector. Simple selectors cannot be added to\n  // expressions nor multiplied by other expressions containing simple\n  // selectors. Also, simple selectors may not appear in lookup argument\n  // inputs.\n  Selector CreateSimpleSelector() {\n    return Selector::Simple(num_simple_selectors_++ + num_complex_selectors_);\n  }\n\n  // Allocate a new complex selector that can appear anywhere\n  // within expressions.\n  Selector CreateComplexSelector() {\n    return Selector::Complex(num_simple_selectors_ + num_complex_selectors_++);\n  }\n\n  // Allocate a new fixed column that can be used in a lookup table.\n  LookupTableColumn CreateLookupTableColumn() {\n    return LookupTableColumn(CreateFixedColumn());\n  }\n\n  // Annotate a Lookup column.\n  void AnnotateLookupColumn(const LookupTableColumn& column,\n                            std::string_view name) {\n    // We don't care if the table has already an annotation. If it's the case we\n    // keep the new one.\n    general_column_annotations_[ColumnKeyBase(\n        ColumnType::kFixed, column.column().index())] = std::string(name);\n  }\n\n  // Annotate an Any column.\n  void AnnotateLookupAnyColumn(const AnyColumnKey& column,\n                               std::string_view name) {\n    // We don't care if the table has already an annotation. If it's the case we\n    // keep the new one.\n    general_column_annotations_[ColumnKeyBase(column.type(), column.index())] =\n        std::string(name);\n  }\n\n  // Allocate a new fixed column\n  FixedColumnKey CreateFixedColumn() {\n    return FixedColumnKey(num_fixed_columns_++);\n  }\n\n  // Allocate a new advice column at |kFirstPhase|.\n  AdviceColumnKey CreateAdviceColumn() {\n    return CreateAdviceColumn(kFirstPhase);\n  }\n\n  // Allocate a new advice column in given phase\n  AdviceColumnKey CreateAdviceColumn(Phase phase) {\n    Phase previous_phase;\n    if (phase.Prev(&previous_phase)) {\n      CHECK(base::Contains(advice_column_phases_, previous_phase))\n          << \"Phase \" << previous_phase.ToString() << \" is not used\";\n    }\n\n    AdviceColumnKey column(num_advice_columns_++, phase);\n    num_advice_queries_.push_back(0);\n    advice_column_phases_.push_back(phase);\n    return column;\n  }\n\n  // Allocate a new instance column\n  InstanceColumnKey CreateInstanceColumn() {\n    return InstanceColumnKey(num_instance_columns_++);\n  }\n\n  Challenge CreateChallengeUsableAfter(Phase phase) {\n    CHECK(base::Contains(advice_column_phases_, phase))\n        << \"Phase \" << phase.ToString() << \" is not used\";\n    Challenge challenge(num_challenges_++, phase);\n    challenge_phases_.push_back(phase);\n    return challenge;\n  }\n\n  Phase ComputeMaxPhase() const {\n    auto max_phase_it = std::max_element(advice_column_phases_.begin(),\n                                         advice_column_phases_.end());\n    if (max_phase_it == advice_column_phases_.end()) return kFirstPhase;\n    return *max_phase_it;\n  }\n\n  std::vector<Phase> GetPhases() const {\n    Phase max_phase = ComputeMaxPhase();\n    return base::CreateVector(\n        static_cast<size_t>(max_phase.value() + 1),\n        [](size_t i) { return Phase(static_cast<uint8_t>(i)); });\n  }\n\n  // Compute the degree of the constraint system (the maximum degree of all\n  // constraints).\n  size_t ComputeDegree() const {\n    if (!cached_degree_.has_value()) {\n      // The permutation argument will serve alongside the gates, so must be\n      // accounted for.\n      size_t degree = permutation_.RequiredDegree();\n\n      // The lookup argument also serves alongside the gates and must be\n      // accounted for.\n      degree = std::max(degree, ComputeLookupRequiredDegree());\n\n      // The shuffle argument also serves alongside the gates and must be\n      // accounted for.\n      degree = std::max(degree, ComputeShuffleRequiredDegree());\n\n      // Account for each gate to ensure our quotient polynomial is the\n      // correct degree and that our extended domain is the right size.\n      degree = std::max(degree, ComputeGateRequiredDegree());\n\n      cached_degree_ = std::max(degree, minimum_degree_.value_or(1));\n    }\n    return *cached_degree_;\n  }\n\n  // Compute the extended K. Since the degree of h(X) exceeds 2ᵏ - 1, h(X) will\n  // be split to h₀(X), h₁(X), ..., h_{d - 1}(X). The extended K denotes the K\n  // of the extended domain for these.\n  //\n  // h(X) = (gate₀(X) + y * gate₁(X) + ... + yⁱ * gateᵢ(X) + ...) / t(X)\n  //\n  // h₀(X) + Xⁿ * h₁(X) + ... + Xⁿ⁽ᵈ⁻¹⁾ * h_{d - 1}(X).\n  // H = [Commit(h₀(X)), Commit(h₁(X)), ..., Commit(h_{d - 1}(X))]\n  //\n  // See\n  // https://zcash.github.io/halo2/design/proving-system/vanishing.html#committing-to-hx.\n  uint32_t ComputeExtendedK(uint32_t k) const {\n    size_t quotient_poly_degree = ComputeDegree() - 1;\n    return std::max(\n        base::bits::SafeLog2Ceiling((size_t{1} << k) * quotient_poly_degree),\n        k);\n  }\n\n  // Compute the number of blinding factors necessary to perfectly blind\n  // each of the prover's witness polynomials.\n  RowIndex ComputeBlindingFactors() const {\n    if (!cached_blinding_factors_.has_value()) {\n      // All of the prover's advice columns are evaluated at no more than\n      auto max_num_advice_query_it = std::max_element(\n          num_advice_queries_.begin(), num_advice_queries_.end());\n\n      RowIndex factors = max_num_advice_query_it == num_advice_queries_.end()\n                             ? 1\n                             : *max_num_advice_query_it;\n      // distinct points during gate checks.\n\n      // - The permutation argument witness polynomials are evaluated at most 3\n      //   times.\n      // - Each lookup argument has independent witness polynomials, and they\n      //   are evaluated at most 2 times.\n      factors = std::max(RowIndex{3}, factors);\n      CHECK_LE(factors, UINT32_MAX - 2);\n\n      // Each polynomial is evaluated at most an additional time during\n      // multiopen (at x₃ to produce q_evals):\n      ++factors;\n\n      // h(x) is derived by the other evaluations so it does not reveal\n      // anything; in fact it does not even appear in the proof.\n\n      // h(x₃) is also not revealed; the verifier only learns a single\n      // evaluation of a polynomial in x₁ which has h(x₃) and another random\n      // polynomial evaluated at x₃ as coefficients -- this random polynomial\n      // is \"random_poly\" in the vanishing argument.\n\n      // Add an additional blinding factor as a slight defense against\n      // off-by-one errors.\n      cached_blinding_factors_ = ++factors;\n    }\n    return *cached_blinding_factors_;\n  }\n\n  RowOffset ComputeLastRow() const { return -(ComputeBlindingFactors() + 1); }\n\n  // Returns the minimum necessary rows that need to exist in order to\n  // account for e.g. blinding factors.\n  RowIndex ComputeMinimumRows() const {\n    return ComputeBlindingFactors()  // m blinding factors\n           + 1                       // for l_{-(m + 1)} (l_last)\n           +\n           1  // for l_first (just for extra breathing room for the permutation\n              // argument, to essentially force a separation in the\n           // permutation polynomial between the roles of l_last, l_first\n           // and the interstitial values.)\n           + 1;  // for at least one row\n  }\n\n  // Return the length of permutation chunk.\n  size_t ComputePermutationChunkLen() const {\n    return ComputePermutationChunkLength(ComputeDegree());\n  }\n\n  // Return the number of permutation products.\n  size_t ComputePermutationProductNums() const {\n    size_t chunk_len = ComputePermutationChunkLen();\n    return (permutation_.columns().size() + chunk_len - 1) / chunk_len;\n  }\n\n  std::string ToString() const {\n    std::stringstream ss;\n    ss << \"num_fixed_columns: \" << num_fixed_columns_\n       << \", num_advice_columns: \" << num_advice_columns_\n       << \", num_instance_columns: \" << num_instance_columns_\n       << \", num_simple_selectors: \" << num_simple_selectors_\n       << \", num_complex_selectors: \" << num_complex_selectors_\n       << \", num_challenges: \" << num_challenges_\n       << \", degree: \" << ComputeDegree()\n       << \", minimum_degree: \" << base::OptionalToString(minimum_degree_)\n       << \", blinding_factors: \" << ComputeBlindingFactors()\n       << \", max_phase: \" << uint32_t{ComputeMaxPhase().value()}\n       << \", permutations: \" << permutation_.columns().size()\n       << \", lookups: \" << lookups_.size()\n       << \", shuffles: \" << shuffles_.size();\n    return ss.str();\n  }\n\n private:\n  template <typename PS>\n  friend class c::zk::plonk::ProvingKeyImpl;\n\n  FRIEND_TEST(ConstraintSystemTest, Lookup);\n  FRIEND_TEST(ConstraintSystemTest, LookupAny);\n\n  void UpdateLookupsMap(\n      std::string_view name,\n      std::vector<std::unique_ptr<Expression<F>>>&& input_expressions,\n      std::vector<std::unique_ptr<Expression<F>>>&& table_expressions) {\n    std::stringstream table_expressions_ss;\n    for (const std::unique_ptr<Expression<F>>& expr : table_expressions) {\n      table_expressions_ss << GetIdentifier(expr.get());\n    }\n\n    std::string table_expressions_identifier = table_expressions_ss.str();\n\n    auto it = lookups_map_.find(table_expressions_identifier);\n    if (it != lookups_map_.end()) {\n      it->second.inputs.push_back(std::move(input_expressions));\n    } else {\n      LookupTracker<F> lookup_tracker(name, std::move(table_expressions),\n                                      std::move(input_expressions));\n      lookups_map_[table_expressions_identifier] = std::move(lookup_tracker);\n    }\n  }\n\n  template <typename QueryData, typename Column>\n  static bool QueryIndex(const std::vector<QueryData>& queries,\n                         const Column& column, Rotation at, size_t* index_out) {\n    std::optional<size_t> index =\n        base::FindIndexIf(queries, [&column, at](const QueryData& query) {\n          return query.column() == column && query.rotation() == at;\n        });\n    if (!index.has_value()) return false;\n    *index_out = index.value();\n    return true;\n  }\n\n  static size_t ComputeColumnDegree(\n      const std::vector<std::unique_ptr<Expression<F>>>& column) {\n    return (*std::max_element(column.begin(), column.end(),\n                              [](const std::unique_ptr<Expression<F>>& a,\n                                 const std::unique_ptr<Expression<F>>& b) {\n                                return a->Degree() < b->Degree();\n                              }))\n        ->Degree();\n  }\n\n  size_t ComputeLookupRequiredDegree() const {\n    std::vector<size_t> required_degrees =\n        base::Map(lookups_, [](const lookup::Argument<F>& argument) {\n          return argument.RequiredDegree();\n        });\n    auto max_required_degree =\n        std::max_element(required_degrees.begin(), required_degrees.end());\n    if (max_required_degree == required_degrees.end()) return 1;\n    return *max_required_degree;\n  }\n\n  size_t ComputeShuffleRequiredDegree() const {\n    std::vector<size_t> required_degrees =\n        base::Map(shuffles_, [](const shuffle::Argument<F>& argument) {\n          return argument.RequiredDegree();\n        });\n    auto max_required_degree =\n        std::max_element(required_degrees.begin(), required_degrees.end());\n    if (max_required_degree == required_degrees.end()) return 1;\n    return *max_required_degree;\n  }\n\n  size_t ComputeGateRequiredDegree() const {\n    std::vector<size_t> required_degrees =\n        base::FlatMap(gates_, [](const Gate<F>& gate) {\n          return base::Map(gate.polys(),\n                           [](const std::unique_ptr<Expression<F>>& poly) {\n                             return poly->Degree();\n                           });\n        });\n    auto max_required_degree =\n        std::max_element(required_degrees.begin(), required_degrees.end());\n    if (max_required_degree == required_degrees.end()) return 0;\n    return *max_required_degree;\n  }\n\n  // Degree of lookup without inputs\n  constexpr static size_t ComputeBaseDegree(size_t table_degree) {\n    // φᵢ(X) = fᵢ(X) + α\n    // τ(X) = t(X) + α\n    //\n    // LHS = τ(X) * Π(φᵢ(X)) * (ϕ(gX) - ϕ(X))\n    // ↪ DEG(LHS) = |table_degree| + inputs_degree(= 0) + 1\n    //            = |table_degree| + 1\n    //\n    // RHS = τ(X) * Π(φᵢ(X)) * (∑ 1/(φᵢ(X)) - m(X) / τ(X))\n    // ↪ DEG(RHS) = |table_degree|\n    //\n    // (1 - (l_last(X) + l_blind(X))) * (LHS - RHS)\n    // ↪ degree = DEG(LHS) + 1 = |table_degree| + 2\n    return std::max(size_t{3}, table_degree + 2);\n  }\n\n  constexpr static size_t ComputeDegreeWithInput(\n      size_t base_degree, size_t input_expression_degree) {\n    return base_degree + input_expression_degree;\n  }\n\n  // TODO(Insun35): Change default |lookup_type_| to |kLogDerivativeHalo2|\n  // after implementing C API for LogDerivativeHalo2 scheme.\n  lookup::Type lookup_type_ = lookup::Type::kHalo2;\n\n  size_t num_fixed_columns_ = 0;\n  size_t num_advice_columns_ = 0;\n  size_t num_instance_columns_ = 0;\n  size_t num_simple_selectors_ = 0;\n  size_t num_complex_selectors_ = 0;\n  size_t num_challenges_ = 0;\n\n  // Contains the phase for each advice column. Should have same length as\n  // num_advice_columns.\n  std::vector<Phase> advice_column_phases_;\n  // Contains the phase for each challenge. Should have same length as\n  // num_challenges.\n  std::vector<Phase> challenge_phases_;\n\n  // This is a cached vector that maps virtual selectors to the concrete\n  // fixed column that they were compressed into. This is just used by dev\n  // tooling right now.\n  std::vector<FixedColumnKey> selector_map_;\n  std::vector<Gate<F>> gates_;\n  std::vector<AdviceQueryData> advice_queries_;\n  // Contains an integer for each advice column\n  // identifying how many distinct queries it has\n  // so far; should be same length as num_advice_columns.\n  std::vector<RowIndex> num_advice_queries_;\n  std::vector<InstanceQueryData> instance_queries_;\n  std::vector<FixedQueryData> fixed_queries_;\n\n  // Permutation argument for performing equality constraints.\n  PermutationArgument permutation_;\n\n  // Vector of lookup arguments, where each corresponds\n  // to a sequence of input expressions and a sequence\n  // of table expressions involved in the lookup.\n  std::vector<lookup::Argument<F>> lookups_;\n\n  // NOTE(Insun35): |lookups_map_| is only used for LogDerivativeHalo2.\n  // btree_map with |table_expressions_identifier| as a key and |LookupTracker|\n  // as values.\n  absl::btree_map<std::string, LookupTracker<F>> lookups_map_;\n\n  // Vector of shuffle arguments, where each corresponds\n  // to a sequence of input expressions and a sequence\n  // of shuffle expressions involved in the shuffle.\n  std::vector<shuffle::Argument<F>> shuffles_;\n\n  // List of indexes of Fixed columns which are associated to a\n  // circuit-general Column tied to their annotation.\n  absl::flat_hash_map<ColumnKeyBase, std::string> general_column_annotations_;\n\n  // Vector of fixed columns, which can be used to store constant values\n  // that are copied into advice columns.\n  std::vector<FixedColumnKey> constants_;\n\n  std::optional<size_t> minimum_degree_;\n\n  mutable std::optional<size_t> cached_degree_;\n  mutable std::optional<RowIndex> cached_blinding_factors_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_CONSTRAINT_SYSTEM_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/constraint_system/constraint_system_unittest.cc",
    "content": "#include \"tachyon/zk/plonk/constraint_system/constraint_system.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n\nnamespace tachyon::zk::plonk {\n\nnamespace {\n\nusing F = math::GF7;\n\nclass ConstraintSystemTest : public math::FiniteFieldTest<F> {};\n\n}  // namespace\n\nTEST_F(ConstraintSystemTest, EnableConstant) {\n  ConstraintSystem<F> constraint_system;\n  std::vector<FixedColumnKey> expected_constants;\n  std::vector<AnyColumnKey> expected_permutation_columns;\n  EXPECT_EQ(constraint_system.constants(), expected_constants);\n  EXPECT_EQ(constraint_system.permutation().columns(),\n            expected_permutation_columns);\n\n  FixedColumnKey column = constraint_system.CreateFixedColumn();\n  constraint_system.EnableConstant(column);\n  expected_constants.push_back(column);\n  EXPECT_EQ(constraint_system.constants(), expected_constants);\n  expected_permutation_columns.push_back(AnyColumnKey(column));\n  EXPECT_EQ(constraint_system.permutation().columns(),\n            expected_permutation_columns);\n\n  constraint_system.EnableConstant(column);\n  EXPECT_EQ(constraint_system.constants(), expected_constants);\n  EXPECT_EQ(constraint_system.permutation().columns(),\n            expected_permutation_columns);\n}\n\nTEST_F(ConstraintSystemTest, Lookup) {\n  ConstraintSystem<F> constraint_system;\n  constraint_system.set_lookup_type(lookup::Type::kHalo2);\n\n  std::array<AdviceColumnKey, 2> advice = {\n      constraint_system.CreateAdviceColumn(),\n      constraint_system.CreateAdviceColumn(),\n  };\n  std::array<LookupTableColumn, 2> table = {\n      constraint_system.CreateLookupTableColumn(),\n      constraint_system.CreateLookupTableColumn(),\n  };\n\n  Selector simple_selector = constraint_system.CreateSimpleSelector();\n  EXPECT_DEATH(\n      constraint_system.Lookup(\n          \"lookup\",\n          [simple_selector, &advice, &table](VirtualCells<F>& cells) {\n            std::unique_ptr<Expression<F>> simple_selector_expr =\n                cells.QuerySelector(simple_selector);\n            std::unique_ptr<Expression<F>> advice0_expr =\n                cells.QueryAdvice(advice[0], Rotation::Cur());\n\n            lookup::Pairs<std::unique_ptr<Expression<F>>, LookupTableColumn>\n                lookup_pairs;\n            lookup_pairs.emplace_back(\n                std::move(simple_selector_expr) * std::move(advice0_expr),\n                table[0]);\n            return lookup_pairs;\n          }),\n      \"expression containing simple selector supplied to lookup argument\");\n\n  Selector complex_selector = constraint_system.CreateComplexSelector();\n  constraint_system.Lookup(\"lookup\", [complex_selector, &advice,\n                                      &table](VirtualCells<F>& cells) {\n    std::unique_ptr<Expression<F>> complex_selector_expr =\n        cells.QuerySelector(complex_selector);\n    std::unique_ptr<Expression<F>> advice0_expr =\n        cells.QueryAdvice(advice[0], Rotation::Cur());\n\n    lookup::Pairs<std::unique_ptr<Expression<F>>, LookupTableColumn>\n        lookup_pairs;\n    lookup_pairs.emplace_back(\n        std::move(complex_selector_expr) * std::move(advice0_expr), table[0]);\n    return lookup_pairs;\n  });\n\n  EXPECT_EQ(constraint_system.lookups().size(), 1);\n\n  constraint_system.Lookup(\n      \"lookup\", [complex_selector, &advice, &table](VirtualCells<F>& cells) {\n        std::unique_ptr<Expression<F>> complex_selector_expr =\n            cells.QuerySelector(complex_selector);\n        std::unique_ptr<Expression<F>> advice1_expr =\n            cells.QueryAdvice(advice[1], Rotation::Cur());\n        std::unique_ptr<Expression<F>> not_complex_selector_expr =\n            ExpressionFactory<F>::Constant(F::One()) -\n            complex_selector_expr->Clone();\n        std::unique_ptr<Expression<F>> default_expr =\n            ExpressionFactory<F>::Constant(F(2));\n\n        lookup::Pairs<std::unique_ptr<Expression<F>>, LookupTableColumn>\n            lookup_pairs;\n        lookup_pairs.emplace_back(\n            std::move(complex_selector_expr) * std::move(advice1_expr) +\n                std::move(not_complex_selector_expr) * std::move(default_expr),\n            table[1]);\n        return lookup_pairs;\n      });\n\n  EXPECT_EQ(constraint_system.lookups().size(), 2);\n\n  EXPECT_EQ(constraint_system.ComputeLookupRequiredDegree(), 5);\n\n  std::vector<lookup::Argument<F>> expected_lookups;\n  {\n    lookup::Pairs<std::unique_ptr<Expression<F>>> pairs;\n    pairs.emplace_back(ExpressionFactory<F>::Product(\n                           ExpressionFactory<F>::Selector(complex_selector),\n                           ExpressionFactory<F>::Advice(\n                               AdviceQuery(0, Rotation::Cur(), advice[0]))),\n                       ExpressionFactory<F>::Fixed(\n                           FixedQuery(0, Rotation::Cur(), table[0].column())));\n    expected_lookups.emplace_back(\"lookup\", std::move(pairs));\n\n    lookup::Pairs<std::unique_ptr<Expression<F>>> pairs2;\n    pairs2.emplace_back(\n        ExpressionFactory<F>::Sum(\n            ExpressionFactory<F>::Product(\n                ExpressionFactory<F>::Selector(complex_selector),\n                ExpressionFactory<F>::Advice(\n                    AdviceQuery(1, Rotation::Cur(), advice[1]))),\n            ExpressionFactory<F>::Product(\n                ExpressionFactory<F>::Sum(\n                    ExpressionFactory<F>::Constant(F::One()),\n                    ExpressionFactory<F>::Negated(\n                        ExpressionFactory<F>::Selector(complex_selector))),\n                ExpressionFactory<F>::Constant(F(2)))),\n        ExpressionFactory<F>::Fixed(\n            FixedQuery(1, Rotation::Cur(), table[1].column())));\n    expected_lookups.emplace_back(\"lookup\", std::move(pairs2));\n  }\n  EXPECT_EQ(constraint_system.lookups(), expected_lookups);\n}\n\nTEST_F(ConstraintSystemTest, LookupAny) {\n  ConstraintSystem<F> constraint_system;\n  constraint_system.set_lookup_type(lookup::Type::kHalo2);\n\n  AdviceColumnKey advice = constraint_system.CreateAdviceColumn();\n  InstanceColumnKey table = constraint_system.CreateInstanceColumn();\n  AdviceColumnKey advice_table = constraint_system.CreateAdviceColumn();\n\n  Selector simple_selector = constraint_system.CreateSimpleSelector();\n  EXPECT_DEATH(\n      constraint_system.LookupAny(\n          \"lookup\",\n          [&advice, simple_selector, &advice_table,\n           &table](VirtualCells<F>& cells) {\n            std::unique_ptr<Expression<F>> advice_expr =\n                cells.QueryAdvice(advice, Rotation::Cur());\n            std::unique_ptr<Expression<F>> simple_selector_expr =\n                cells.QuerySelector(simple_selector);\n            std::unique_ptr<Expression<F>> advice_table_expr =\n                cells.QueryAdvice(advice_table, Rotation::Cur());\n            std::unique_ptr<Expression<F>> table_expr =\n                cells.QueryInstance(table, Rotation::Cur());\n\n            lookup::Pairs<std::unique_ptr<Expression<F>>> lookup_pairs;\n            lookup_pairs.emplace_back(\n                simple_selector_expr->Clone() * advice_expr->Clone(),\n                std::move(table_expr));\n            lookup_pairs.emplace_back(\n                std::move(simple_selector_expr) * std::move(advice_expr),\n                std::move(advice_table_expr));\n            return lookup_pairs;\n          }),\n      \"expression containing simple selector supplied to lookup argument\");\n\n  Selector complex_selector = constraint_system.CreateComplexSelector();\n  constraint_system.LookupAny(\n      \"lookup\", [&advice, complex_selector, &advice_table,\n                 &table](VirtualCells<F>& cells) {\n        std::unique_ptr<Expression<F>> advice_expr =\n            cells.QueryAdvice(advice, Rotation::Cur());\n        std::unique_ptr<Expression<F>> complex_selector_expr =\n            cells.QuerySelector(complex_selector);\n        std::unique_ptr<Expression<F>> not_complex_selector_expr =\n            ExpressionFactory<F>::Constant(F::One()) -\n            complex_selector_expr->Clone();\n        std::unique_ptr<Expression<F>> default_expr =\n            ExpressionFactory<F>::Constant(F(2));\n        std::unique_ptr<Expression<F>> advice_table_expr =\n            cells.QueryAdvice(advice_table, Rotation::Cur());\n        std::unique_ptr<Expression<F>> table_expr =\n            cells.QueryInstance(table, Rotation::Cur());\n\n        lookup::Pairs<std::unique_ptr<Expression<F>>> lookup_pairs;\n        lookup_pairs.emplace_back(\n            complex_selector_expr->Clone() * advice_expr->Clone() +\n                not_complex_selector_expr->Clone() * default_expr->Clone(),\n            std::move(table_expr));\n        lookup_pairs.emplace_back(\n            std::move(complex_selector_expr) * std::move(advice_expr) +\n                std::move(not_complex_selector_expr) * std::move(default_expr),\n            std::move(advice_table_expr));\n        return lookup_pairs;\n      });\n\n  EXPECT_EQ(constraint_system.lookups().size(), 1);\n\n  std::vector<lookup::Argument<F>> expected_lookups;\n  {\n    lookup::Pairs<std::unique_ptr<Expression<F>>> pairs;\n    pairs.emplace_back(\n        ExpressionFactory<F>::Sum(\n            ExpressionFactory<F>::Product(\n                ExpressionFactory<F>::Selector(complex_selector),\n                ExpressionFactory<F>::Advice(\n                    AdviceQuery(0, Rotation::Cur(), advice))),\n            ExpressionFactory<F>::Product(\n                ExpressionFactory<F>::Sum(\n                    ExpressionFactory<F>::Constant(F::One()),\n                    ExpressionFactory<F>::Negated(\n                        ExpressionFactory<F>::Selector(complex_selector))),\n                ExpressionFactory<F>::Constant(F(2)))),\n        ExpressionFactory<F>::Instance(\n            InstanceQuery(0, Rotation::Cur(), table)));\n    pairs.emplace_back(\n        ExpressionFactory<F>::Sum(\n            ExpressionFactory<F>::Product(\n                ExpressionFactory<F>::Selector(complex_selector),\n                ExpressionFactory<F>::Advice(\n                    AdviceQuery(0, Rotation::Cur(), advice))),\n            ExpressionFactory<F>::Product(\n                ExpressionFactory<F>::Sum(\n                    ExpressionFactory<F>::Constant(F::One()),\n                    ExpressionFactory<F>::Negated(\n                        ExpressionFactory<F>::Selector(complex_selector))),\n                ExpressionFactory<F>::Constant(F(2)))),\n        ExpressionFactory<F>::Advice(\n            AdviceQuery(1, Rotation::Cur(), advice_table)));\n    expected_lookups.emplace_back(\"lookup\", std::move(pairs));\n  }\n  EXPECT_EQ(constraint_system.lookups(), expected_lookups);\n}\n\nTEST_F(ConstraintSystemTest, QueryFixedIndex) {\n  ConstraintSystem<F> constraint_system;\n  FixedColumnKey column = constraint_system.CreateFixedColumn();\n  Rotation rotation = Rotation::Cur();\n  EXPECT_EQ(constraint_system.QueryFixedIndex(column, rotation), 0);\n  EXPECT_EQ(constraint_system.QueryFixedIndex(column, rotation), 0);\n\n  column = constraint_system.CreateFixedColumn();\n  rotation = Rotation::Cur();\n  EXPECT_EQ(constraint_system.QueryFixedIndex(column, rotation), 1);\n\n  rotation = Rotation::Next();\n  EXPECT_EQ(constraint_system.QueryFixedIndex(column, rotation), 2);\n}\n\nTEST_F(ConstraintSystemTest, QueryAdviceIndex) {\n  ConstraintSystem<F> constraint_system;\n  AdviceColumnKey column = constraint_system.CreateAdviceColumn();\n  Rotation rotation = Rotation::Cur();\n\n  EXPECT_EQ(constraint_system.QueryAdviceIndex(column, rotation), 0);\n  EXPECT_EQ(constraint_system.QueryAdviceIndex(column, rotation), 0);\n\n  column = constraint_system.CreateAdviceColumn();\n  rotation = Rotation::Cur();\n  EXPECT_EQ(constraint_system.QueryAdviceIndex(column, rotation), 1);\n\n  rotation = Rotation::Next();\n  EXPECT_EQ(constraint_system.QueryAdviceIndex(column, rotation), 2);\n}\n\nTEST_F(ConstraintSystemTest, QueryInstanceIndex) {\n  ConstraintSystem<F> constraint_system;\n  InstanceColumnKey column = constraint_system.CreateInstanceColumn();\n  Rotation rotation = Rotation::Cur();\n  EXPECT_EQ(constraint_system.QueryInstanceIndex(column, rotation), 0);\n  EXPECT_EQ(constraint_system.QueryInstanceIndex(column, rotation), 0);\n\n  column = constraint_system.CreateInstanceColumn();\n  rotation = Rotation::Cur();\n  EXPECT_EQ(constraint_system.QueryInstanceIndex(column, rotation), 1);\n\n  rotation = Rotation::Next();\n  EXPECT_EQ(constraint_system.QueryInstanceIndex(column, rotation), 2);\n}\n\nTEST_F(ConstraintSystemTest, Phases) {\n  ConstraintSystem<F> constraint_system;\n  EXPECT_DEATH(constraint_system.CreateAdviceColumn(kSecondPhase), \"\");\n\n  std::vector<Phase> phases = {kFirstPhase};\n  EXPECT_EQ(constraint_system.ComputeMaxPhase(), kFirstPhase);\n  EXPECT_EQ(constraint_system.GetPhases(), phases);\n\n  constraint_system.CreateAdviceColumn(kFirstPhase);\n  EXPECT_EQ(constraint_system.ComputeMaxPhase(), kFirstPhase);\n  EXPECT_EQ(constraint_system.GetPhases(), phases);\n\n  constraint_system.CreateAdviceColumn(kSecondPhase);\n  phases.push_back(kSecondPhase);\n  EXPECT_EQ(constraint_system.ComputeMaxPhase(), kSecondPhase);\n  EXPECT_EQ(constraint_system.GetPhases(), phases);\n}\n\nnamespace {\n\ntemplate <typename ColumnKey>\nclass ConstraintSystemTypedTest : public math::FiniteFieldTest<F> {};\n\n}  // namespace\n\nusing ColumnKeyTypes =\n    testing::Types<FixedColumnKey, AdviceColumnKey, InstanceColumnKey>;\nTYPED_TEST_SUITE(ConstraintSystemTypedTest, ColumnKeyTypes);\n\nTYPED_TEST(ConstraintSystemTypedTest, EnableEquality) {\n  using ColumnKey = TypeParam;\n\n  ConstraintSystem<F> constraint_system;\n  std::vector<AnyColumnKey> expected_permutation_columns;\n  std::vector<FixedQueryData> fixed_queries;\n  std::vector<AdviceQueryData> advice_queries;\n  std::vector<RowIndex> num_advice_queries;\n  std::vector<InstanceQueryData> instance_queries;\n  EXPECT_EQ(constraint_system.permutation().columns(),\n            expected_permutation_columns);\n\n  ColumnKey column;\n  if constexpr (std::is_same_v<ColumnKey, FixedColumnKey>) {\n    column = constraint_system.CreateFixedColumn();\n    fixed_queries.push_back(FixedQueryData(Rotation::Cur(), column));\n  } else if constexpr (std::is_same_v<ColumnKey, AdviceColumnKey>) {\n    column = constraint_system.CreateAdviceColumn();\n    num_advice_queries.push_back(0);\n    EXPECT_EQ(constraint_system.num_advice_queries(), num_advice_queries);\n    ++num_advice_queries[column.index()];\n    advice_queries.push_back(AdviceQueryData(Rotation::Cur(), column));\n  } else {\n    column = constraint_system.CreateInstanceColumn();\n    instance_queries.push_back(InstanceQueryData(Rotation::Cur(), column));\n  }\n  constraint_system.EnableEquality(column);\n  expected_permutation_columns.push_back(AnyColumnKey(column));\n  EXPECT_EQ(constraint_system.permutation().columns(),\n            expected_permutation_columns);\n  EXPECT_EQ(constraint_system.fixed_queries(), fixed_queries);\n  EXPECT_EQ(constraint_system.advice_queries(), advice_queries);\n  EXPECT_EQ(constraint_system.num_advice_queries(), num_advice_queries);\n  EXPECT_EQ(constraint_system.instance_queries(), instance_queries);\n\n  constraint_system.EnableEquality(column);\n  EXPECT_EQ(constraint_system.permutation().columns(),\n            expected_permutation_columns);\n  EXPECT_EQ(constraint_system.fixed_queries(), fixed_queries);\n  EXPECT_EQ(constraint_system.advice_queries(), advice_queries);\n  EXPECT_EQ(constraint_system.num_advice_queries(), num_advice_queries);\n  EXPECT_EQ(constraint_system.instance_queries(), instance_queries);\n}\n\nTYPED_TEST(ConstraintSystemTypedTest, QueryAnyIndex) {\n  using ColumnKey = TypeParam;\n\n  ConstraintSystem<F> constraint_system;\n  std::function<ColumnKey()> create_column = [&constraint_system]() {\n    if constexpr (std::is_same_v<ColumnKey, FixedColumnKey>) {\n      return constraint_system.CreateFixedColumn();\n    } else if constexpr (std::is_same_v<ColumnKey, AdviceColumnKey>) {\n      return constraint_system.CreateAdviceColumn();\n    } else {\n      return constraint_system.CreateInstanceColumn();\n    }\n  };\n\n  ColumnKey column = create_column();\n  Rotation rotation = Rotation::Cur();\n  EXPECT_EQ(constraint_system.QueryAnyIndex(column, rotation), 0);\n  EXPECT_EQ(constraint_system.QueryAnyIndex(column, rotation), 0);\n\n  column = create_column();\n  rotation = Rotation::Cur();\n  EXPECT_EQ(constraint_system.QueryAnyIndex(column, rotation), 1);\n\n  rotation = Rotation::Next();\n  EXPECT_EQ(constraint_system.QueryAnyIndex(column, rotation), 2);\n}\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/constraint_system/exclusion_matrix.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_EXCLUSION_MATRIX_H_\n#define TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_EXCLUSION_MATRIX_H_\n\n#include <algorithm>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/export.h\"\n#include \"tachyon/zk/plonk/constraint_system/selector_description.h\"\n\nnamespace tachyon::zk::plonk {\n\n// Exclusion matrix that has (i, j) = true if activations vector of selector i\n// and selector j conflict -- that is, they are both enabled on the same row.\n// This matrix is symmetric and the diagonal entries are false, so we only need\n// to store the lower triangular entries.\n//\n// For example, given the following selectors:\n//   [1, 0, 0, 0, 0, 0, 0, 0, 1]\n//   [1, 0, 0, 0, 0, 0, 0, 1, 0]\n//   [1, 0, 0, 0, 0, 0, 1, 0, 0]\n//   [0, 1, 0, 0, 0, 1, 1, 1, 0]\n//   [0, 1, 0, 0, 1, 0, 1, 0, 1]\n//   [0, 1, 0, 1, 0, 0, 0, 1, 1]\n//   [0, 0, 1, 1, 1, 0, 0, 0, 0]\n//   [0, 0, 1, 1, 0, 1, 0, 0, 0]\n//   [0, 0, 1, 0, 1, 1, 0, 0, 0]\n//\n// Exclusion matrix is the lower triangular entries\n// of the following matrix:\n// i \\ j |  0  |  1  |  2  |  3  |  4  |  5  |  6  |  7  |  8  |\n// ------+-----+-----+-----+-----+-----+-----+-----+-----+-----+\n//   0   |  0  |  1  |  1  |  0  |  1  |  1  |  0  |  0  |  0  |\n// ------+-----+-----+-----+-----+-----+-----+-----+-----+-----+\n//   1   |  1  |  0  |  1  |  1  |  0  |  1  |  0  |  0  |  0  |\n// ------+-----+-----+-----+-----+-----+-----+-----+-----+-----+\n//   2   |  1  |  1  |  0  |  1  |  1  |  0  |  0  |  0  |  0  |\n// ------+-----+-----+-----+-----+-----+-----+-----+-----+-----+\n//   3   |  0  |  1  |  1  |  0  |  1  |  1  |  0  |  1  |  1  |\n// ------+-----+-----+-----+-----+-----+-----+-----+-----+-----+\n//   4   |  1  |  0  |  1  |  1  |  0  |  1  |  1  |  0  |  1  |\n// ------+-----+-----+-----+-----+-----+-----+-----+-----+-----+\n//   5   |  1  |  1  |  0  |  1  |  1  |  0  |  1  |  1  |  0  |\n// ------+-----+-----+-----+-----+-----+-----+-----+-----+-----+\n//   6   |  0  |  0  |  0  |  0  |  1  |  1  |  0  |  1  |  1  |\n// ------+-----+-----+-----+-----+-----+-----+-----+-----+-----+\n//   7   |  0  |  0  |  0  |  1  |  0  |  1  |  1  |  0  |  1  |\n// ------+-----+-----+-----+-----+-----+-----+-----+-----+-----+\n//   8   |  0  |  0  |  0  |  1  |  1  |  0  |  1  |  1  |  0  |\n// ------+-----+-----+-----+-----+-----+-----+-----+-----+-----+\n\nclass TACHYON_EXPORT ExclusionMatrix {\n public:\n  explicit ExclusionMatrix(const std::vector<SelectorDescription>& selectors) {\n    lower_triangular_matrix_ =\n        base::CreateVector(selectors.size(), [&selectors](size_t i) {\n          const SelectorDescription& selector = selectors[i];\n          return base::CreateVector(i, [&selector, &selectors](size_t j) {\n            return !selector.IsOrthogonal(selectors[j]);\n          });\n        });\n  }\n\n  const std::vector<std::vector<bool>>& lower_triangular_matrix() const {\n    return lower_triangular_matrix_;\n  }\n\n  bool IsExclusive(size_t src, size_t dst) const {\n    size_t i = std::max(src, dst);\n    size_t j = std::min(src, dst);\n    return lower_triangular_matrix_[i][j];\n  }\n\n private:\n  std::vector<std::vector<bool>> lower_triangular_matrix_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_EXCLUSION_MATRIX_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/constraint_system/exclusion_matrix_unittest.cc",
    "content": "#include \"tachyon/zk/plonk/constraint_system/exclusion_matrix.h\"\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n\nnamespace tachyon::zk::plonk {\n\nnamespace {\n\nclass ExclusionMatrixTest : public ::testing::Test {\n public:\n  void SetUp() override {\n    selector_activations_ = {\n        // [1, 0, 0, 0, 0, 0, 0, 0, 1]\n        {true, false, false, false, false, false, false, false, true},\n        // [1, 0, 0, 0, 0, 0, 0, 1, 0]\n        {true, false, false, false, false, false, false, true, false},\n        // [1, 0, 0, 0, 0, 0, 1, 0, 0]\n        {true, false, false, false, false, false, true, false, false},\n        // [0, 1, 0, 0, 0, 1, 1, 1, 0]\n        {false, true, false, false, false, true, true, true, false},\n        // [0, 1, 0, 0, 1, 0, 1, 0, 1]\n        {false, true, false, false, true, false, true, false, true},\n        // [0, 1, 0, 1, 0, 0, 0, 1, 1]\n        {false, true, false, true, false, false, false, true, true},\n        // [0, 0, 1, 1, 1, 0, 0, 0, 0]\n        {false, false, true, true, true, false, false, false, false},\n        // [0, 0, 1, 1, 0, 1, 0, 0, 0]\n        {false, false, true, true, false, true, false, false, false},\n        // [0, 0, 1, 0, 1, 1, 0, 0, 0]\n        {false, false, true, false, true, true, false, false, false}};\n\n    // |exclusion_matrix| should be the lower triangular of the whole matrix.\n    // +-----+\n    // |  1  |\n    // +-----+-----+\n    // |  1  |  1  |\n    // +-----+-----+-----+\n    // |  0  |  1  |  1  |\n    // +-----+-----+-----+-----+\n    // |  1  |  0  |  1  |  1  |\n    // +-----+-----+-----+-----+-----+\n    // |  1  |  1  |  0  |  1  |  1  |\n    // +-----+-----+-----+-----+-----+-----+\n    // |  0  |  0  |  0  |  0  |  1  |  1  |\n    // +-----+-----+-----+-----+-----+-----+-----+\n    // |  0  |  0  |  0  |  1  |  0  |  1  |  1  |\n    // +-----+-----+-----+-----+-----+-----+-----+-----+\n    // |  0  |  0  |  0  |  1  |  1  |  0  |  1  |  1  |\n    // +-----+-----+-----+-----+-----+-----+-----+-----+\n    expected_matrix_ = {{},\n                        {true},\n                        {true, true},\n                        {false, true, true},\n                        {true, false, true, true},\n                        {true, true, false, true, true},\n                        {false, false, false, false, true, true},\n                        {false, false, false, true, false, true, true},\n                        {false, false, false, true, true, false, true, true}};\n\n    selectors_ =\n        base::CreateVector(selector_activations_.size(), [this](size_t i) {\n          return SelectorDescription(i, &selector_activations_[i], 1);\n        });\n  }\n\n protected:\n  std::vector<std::vector<bool>> selector_activations_;\n  std::vector<std::vector<bool>> expected_matrix_;\n  std::vector<SelectorDescription> selectors_;\n};\n\n}  // namespace\n\nTEST_F(ExclusionMatrixTest, ConstructExclusionMatrix) {\n  ExclusionMatrix exclusion_matrix(selectors_);\n\n  std::vector<std::vector<bool>> actual_matrix =\n      exclusion_matrix.lower_triangular_matrix();\n  EXPECT_EQ(actual_matrix, expected_matrix_);\n}\n\nTEST_F(ExclusionMatrixTest, IsExclusive) {\n  ExclusionMatrix exclusion_matrix(selectors_);\n\n  // IsExclusive(i, j) returns the value of the exclusion matrix at (i, j).\n  for (size_t i = 0; i < selectors_.size(); ++i) {\n    for (size_t j = 0; j < i; ++j) {\n      EXPECT_EQ(exclusion_matrix.IsExclusive(i, j), expected_matrix_[i][j]);\n    }\n  }\n\n  // Since the matrix is symmetric, the lower triangular and upper triangular\n  // matrix must be the same.\n  for (size_t i = 0; i < selectors_.size(); ++i) {\n    for (size_t j = 0; j < i; ++j) {\n      EXPECT_EQ(exclusion_matrix.IsExclusive(i, j),\n                exclusion_matrix.IsExclusive(j, i));\n    }\n  }\n}\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/constraint_system/gate.h",
    "content": "#ifndef TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_GATE_H_\n#define TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_GATE_H_\n\n#include <memory>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"absl/strings/str_join.h\"\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/zk/expressions/expression.h\"\n#include \"tachyon/zk/plonk/constraint_system/selector.h\"\n#include \"tachyon/zk/plonk/constraint_system/virtual_cell.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F>\nclass Gate {\n public:\n  Gate() = default;\n  Gate(std::string_view name, std::vector<std::string>&& constraint_names,\n       std::vector<std::unique_ptr<Expression<F>>>&& polys,\n       std::vector<Selector>&& queried_selectors,\n       std::vector<VirtualCell>&& queried_cells)\n      : name_(std::string(name)),\n        constraint_names_(std::move(constraint_names)),\n        polys_(std::move(polys)),\n        queried_selectors_(std::move(queried_selectors)),\n        queried_cells_(std::move(queried_cells)) {}\n\n  const std::string& name() const { return name_; }\n  const std::vector<std::string>& constraint_names() const {\n    return constraint_names_;\n  }\n  const std::vector<std::unique_ptr<Expression<F>>>& polys() const {\n    return polys_;\n  }\n  std::vector<std::unique_ptr<Expression<F>>>& polys() { return polys_; }\n  const std::vector<Selector>& queried_selectors() const {\n    return queried_selectors_;\n  }\n  const std::vector<VirtualCell>& queried_cells() const {\n    return queried_cells_;\n  }\n\n  bool operator==(const Gate& other) const {\n    if (!(name_ == other.name_ &&\n          constraint_names_ == other.constraint_names_ &&\n          queried_selectors_ == other.queried_selectors_ &&\n          queried_cells_ == other.queried_cells_))\n      return false;\n    if (polys_.size() != other.polys_.size()) return false;\n    for (size_t i = 0; i < polys_.size(); ++i) {\n      if (*polys_[i] != *other.polys_[i]) return false;\n    }\n    return true;\n  }\n  bool operator!=(const Gate& other) const { return !operator==(other); }\n\n  std::string ToString() const {\n    std::vector<std::string> polys_str =\n        base::Map(polys_, [](const std::unique_ptr<Expression<F>>& expr) {\n          return expr->ToString();\n        });\n    std::vector<std::string> queried_selectors_str =\n        base::Map(queried_selectors_,\n                  [](Selector selector) { return selector.ToString(); });\n    std::vector<std::string> queried_cells_str =\n        base::Map(queried_cells_, [](const VirtualCell& queried_cell) {\n          return queried_cell.ToString();\n        });\n    return absl::Substitute(\n        \"name: $0, constraint_names: [$1], polys: [$2], queried_selectors: \"\n        \"[$3], queried_cells: [$4]\",\n        name_, absl::StrJoin(constraint_names_, \", \"),\n        absl::StrJoin(polys_str, \", \"),\n        absl::StrJoin(queried_selectors_str, \", \"),\n        absl::StrJoin(queried_cells_str, \", \"));\n  }\n\n private:\n  std::string name_;\n  std::vector<std::string> constraint_names_;\n  std::vector<std::unique_ptr<Expression<F>>> polys_;\n  // We track queried selectors separately from other cells, so that we can use\n  // them to trigger debug checks on gates.\n  std::vector<Selector> queried_selectors_;\n  std::vector<VirtualCell> queried_cells_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_GATE_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/constraint_system/lookup_tracker.h",
    "content": "#ifndef TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_LOOKUP_TRACKER_H_\n#define TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_LOOKUP_TRACKER_H_\n\n#include <memory>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/zk/expressions/expression.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F>\nstruct LookupTracker {\n  std::string name;\n  std::vector<std::unique_ptr<Expression<F>>> table;\n  std::vector<std::vector<std::unique_ptr<Expression<F>>>> inputs;\n  LookupTracker() = default;\n  LookupTracker(std::string_view name,\n                std::vector<std::unique_ptr<Expression<F>>> table,\n                std::vector<std::unique_ptr<Expression<F>>> input)\n      : name(std::string(name)), table(std::move(table)) {\n    inputs.push_back(std::move(input));\n  }\n  LookupTracker(std::string_view name,\n                std::vector<std::unique_ptr<Expression<F>>> table,\n                std::vector<std::vector<std::unique_ptr<Expression<F>>>> input)\n      : name(std::string(name)),\n        table(std::move(table)),\n        inputs(std::move(input)) {}\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_LOOKUP_TRACKER_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/constraint_system/query.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_QUERY_H_\n#define TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_QUERY_H_\n\n#include <optional>\n#include <string>\n\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/base/strings/string_util.h\"\n#include \"tachyon/zk/base/rotation.h\"\n#include \"tachyon/zk/plonk/base/column_key.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <ColumnType C>\nclass QueryData {\n public:\n  QueryData() = default;\n  QueryData(Rotation rotation, const ColumnKey<C>& column)\n      : rotation_(rotation), column_(column) {}\n\n  Rotation rotation() const { return rotation_; }\n  const ColumnKey<C>& column() const { return column_; }\n\n  bool operator==(const QueryData& other) const {\n    return rotation_ == other.rotation_ && column_ == other.column_;\n  }\n  bool operator!=(const QueryData& other) const { return !operator==(other); }\n\n  std::string ToString() const {\n    return absl::Substitute(\"{rotation: $0, column: $1}\", rotation_.ToString(),\n                            column_.ToString());\n  }\n\n protected:\n  Rotation rotation_;\n  ColumnKey<C> column_;\n};\n\nusing FixedQueryData = QueryData<ColumnType::kFixed>;\nusing InstanceQueryData = QueryData<ColumnType::kInstance>;\nusing AdviceQueryData = QueryData<ColumnType::kAdvice>;\n\ntemplate <ColumnType C>\nclass Query : public QueryData<C> {\n public:\n  Query() = default;\n  Query(std::optional<size_t> index, Rotation rotation,\n        const ColumnKey<C>& column)\n      : QueryData<C>(rotation, column), index_(index) {}\n\n  size_t GetIndex() const { return index_.value(); }\n  void SetIndex(size_t index) { index_ = index; }\n  bool HasIndex() const { return index_.has_value(); }\n\n  bool operator==(const Query& other) const {\n    return QueryData<C>::operator==(other) && index_ == other.index_;\n  }\n  bool operator!=(const Query& other) const { return !operator==(other); }\n\n  std::string ToString() const {\n    return absl::Substitute(\n        \"{index: $0, rotation: $1, column: $2}\", base::OptionalToString(index_),\n        this->rotation_.ToString(), this->column_.ToString());\n  }\n\n private:\n  std::optional<size_t> index_;\n};\n\nusing FixedQuery = Query<ColumnType::kFixed>;\nusing InstanceQuery = Query<ColumnType::kInstance>;\nusing AdviceQuery = Query<ColumnType::kAdvice>;\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_QUERY_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/constraint_system/selector.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_SELECTOR_H_\n#define TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_SELECTOR_H_\n\n#include <stddef.h>\n\n#include <string>\n#include <utility>\n\n#include \"absl/hash/hash.h\"\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/export.h\"\n#include \"tachyon/zk/base/row_types.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F>\nclass Region;\n\n// NOTE(dongchangYoo): Selector class is copyable, assignable, and occupies 65\n// bits per instance. Prefer to pass them by value.\nclass TACHYON_EXPORT Selector {\n public:\n  static Selector Simple(size_t index) { return {index, true}; }\n\n  static Selector Complex(size_t index) { return {index, false}; }\n\n  size_t index() const { return index_; }\n  bool is_simple() const { return is_simple_; }\n\n  bool operator==(Selector other) const {\n    return index_ == other.index_ && is_simple_ == other.is_simple_;\n  }\n  bool operator!=(Selector other) const { return !operator==(other); }\n\n  // Defined in region.h\n  template <typename F>\n  void Enable(Region<F>& region, RowIndex offset) const;\n\n  std::string ToString() const {\n    return absl::Substitute(\"{index: $0, is_simple: $1}\", index_, is_simple_);\n  }\n\n private:\n  Selector(size_t index, bool is_simple)\n      : index_(index), is_simple_(is_simple) {}\n\n  size_t index_ = 0;\n  bool is_simple_ = false;\n};\n\ntemplate <typename H>\nH AbslHashValue(H h, Selector selector) {\n  return H::combine(std::move(h), selector.index(), selector.is_simple());\n}\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_SELECTOR_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/constraint_system/selector_assignment.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_SELECTOR_ASSIGNMENT_H_\n#define TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_SELECTOR_ASSIGNMENT_H_\n\n#include <memory>\n#include <utility>\n\n#include \"tachyon/zk/expressions/expression.h\"\n\nnamespace tachyon::zk::plonk {\n\n// This describes the assigned combination of a particular selector as well as\n// the expression it should be substituted with.\ntemplate <typename F>\nclass SelectorAssignment {\n public:\n  SelectorAssignment() = default;\n  SelectorAssignment(size_t selector_index, size_t combination_index,\n                     std::unique_ptr<Expression<F>> expression)\n      : selector_index_(selector_index),\n        combination_index_(combination_index),\n        expression_(std::move(expression)) {}\n\n  size_t selector_index() const { return selector_index_; }\n  size_t combination_index() const { return combination_index_; }\n  const Expression<F>* expression() const { return expression_.get(); }\n\n private:\n  // The selector that this structure references, by index.\n  size_t selector_index_ = 0;\n  // The combination this selector was assigned to.\n  size_t combination_index_ = 0;\n  // The expression we wish to substitute with.\n  std::unique_ptr<Expression<F>> expression_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_SELECTOR_ASSIGNMENT_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/constraint_system/selector_compressor.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_SELECTOR_COMPRESSOR_H_\n#define TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_SELECTOR_COMPRESSOR_H_\n\n#include <stddef.h>\n\n#include <algorithm>\n#include <memory>\n#include <tuple>\n#include <utility>\n#include <vector>\n\n#include \"gtest/gtest_prod.h\"\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/containers/cxx20_erase_vector.h\"\n#include \"tachyon/base/functional/callback.h\"\n#include \"tachyon/base/parallelize.h\"\n#include \"tachyon/math/base/parallelize_threshold.h\"\n#include \"tachyon/zk/expressions/expression_factory.h\"\n#include \"tachyon/zk/plonk/constraint_system/exclusion_matrix.h\"\n#include \"tachyon/zk/plonk/constraint_system/selector_assignment.h\"\n#include \"tachyon/zk/plonk/constraint_system/selector_description.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F>\nclass SelectorCompressor {\n public:\n  using AllocateFixedColumnCallback =\n      base::RepeatingCallback<std::unique_ptr<Expression<F>>()>;\n\n  SelectorCompressor() = default;\n\n  const std::vector<std::vector<F>>& combination_assignments() const {\n    return combination_assignments_;\n  }\n  const std::vector<SelectorAssignment<F>>& selector_assignments() const {\n    return selector_assignments_;\n  }\n\n  // This function takes a vector that defines each selector as well as a\n  // closure used to allocate new fixed columns, and returns the assignment of\n  // each combination as well as details about each selector assignment.\n  //\n  // This function takes\n  // * |selectors_in|, a vector of vector of bool.\n  // * |max_degree|, the maximum allowed degree of any gate.\n  // * |callback|, a closure that constructs a new fixed column and\n  //   queries it at |Rotation::Cur()|, returning the expression.\n  //\n  // and returns |Result| containing the assignment of each new fixed\n  // column (which each correspond to a combination) as well as a vector of\n  // |SelectorAssignment<F>| that the caller can use to perform the necessary\n  // substitutions to the constraint system.\n  //\n  // This function is completely deterministic.\n  void Process(const std::vector<std::vector<bool>>& selectors_in,\n               const std::vector<size_t>& degrees, size_t max_degree,\n               AllocateFixedColumnCallback callback) {\n    if (selectors_in.empty()) return;\n\n    callback_ = std::move(callback);\n\n    // For example, suppose we have the following inputs:\n    //\n    // |selectors_in| = [\n    //   [1, 0, 0, 0, 0, 0, 0, 0, 1],\n    //   [1, 0, 0, 0, 0, 0, 0, 1, 0],\n    //   [1, 0, 0, 0, 0, 0, 1, 0, 0],\n    //   [0, 1, 0, 0, 0, 1, 1, 1, 0],\n    //   [0, 1, 0, 0, 1, 0, 1, 0, 1],\n    //   [0, 1, 0, 1, 0, 0, 0, 1, 1],\n    //   [0, 0, 1, 1, 1, 0, 0, 0, 0],\n    //   [0, 0, 1, 1, 0, 1, 0, 0, 0],\n    //   [0, 0, 1, 0, 1, 1, 0, 0, 0],\n    //   [1, 1, 1, 1, 1, 1, 1, 1, 1],\n    // ]\n    //\n    // |degrees| = [3, 3, 3, 3, 3, 3, 7, 7, 7, 0]\n    //\n    // |max_degree| = 10\n    //\n    // The length of all provided selectors must be the same.\n    size_t n = selectors_in[0].size();\n    DCHECK(std::all_of(selectors_in.begin(), selectors_in.end(),\n                       [n](const std::vector<bool>& activations) {\n                         return activations.size() == n;\n                       }));\n\n    // |selectors| = [\n    //   s₀: SelectorDescription(0, [1, 0, 0, 0, 0, 0, 0, 0, 1], 3),\n    //   s₁: SelectorDescription(1, [1, 0, 0, 0, 0, 0, 0, 1, 0], 3),\n    //   s₂: SelectorDescription(2, [1, 0, 0, 0, 0, 0, 1, 0, 0], 3),\n    //   s₃: SelectorDescription(3, [0, 1, 0, 0, 0, 1, 1, 1, 0], 3),\n    //   s₄: SelectorDescription(4, [0, 1, 0, 0, 1, 0, 1, 0, 1], 3),\n    //   s₅: SelectorDescription(5, [0, 1, 0, 1, 0, 0, 0, 1, 1], 3),\n    //   s₆: SelectorDescription(6, [0, 0, 1, 1, 1, 0, 0, 0, 0], 7),\n    //   s₇: SelectorDescription(7, [0, 0, 1, 1, 0, 1, 0, 0, 0], 7),\n    //   s₈: SelectorDescription(8, [0, 0, 1, 0, 1, 1, 0, 0, 0], 7),\n    //   s₉: SelectorDescription(9, [1, 1, 1, 1, 1, 1, 1, 1, 1], 0),\n    // ]\n    selectors_ =\n        base::Map(selectors_in,\n                  [&degrees](size_t i, const std::vector<bool>& activations) {\n                    size_t max_degree = degrees[i];\n                    return SelectorDescription(i, &activations, max_degree);\n                  });\n\n    // Selectors with zero degree should be handled first before combining.\n    HandleZeroDegreeSelectors();\n\n    // All of the remaining |selectors| are simple. Let's try to combine them.\n    // First, we compute the exclusion matrix.\n    // See tachyon/zk/plonk/constraint_system/exclusion_matrix.h for details.\n    ExclusionMatrix exclusion_matrix(selectors_);\n\n    // Then, we combine the remaining |selectors|.\n    // combination for s₀, s₃, s₆:\n    //   s₀: [1, 0, 0, 0, 0, 0, 0, 0, 1] -> [1, 0, 0, 0, 0, 0, 0, 0, 1]\n    //   s₃: [0, 1, 0, 0, 0, 1, 1, 1, 0] -> [1, 2, 0, 0, 0, 2, 2, 2, 1]\n    //   s₆: [0, 0, 1, 1, 1, 0, 0, 0, 0] -> [1, 2, 3, 3, 3, 2, 2, 2, 1]\n    //   => [1, 2, 3, 3, 3, 2, 2, 2, 1]\n    //\n    // combination for s₁, s₄, s₇:\n    //   s₁: [1, 0, 0, 0, 0, 0, 0, 1, 0] -> [1, 0, 0, 0, 0, 0, 0, 1, 0]\n    //   s₄: [0, 1, 0, 0, 1, 0, 1, 0, 1] -> [1, 2, 0, 0, 2, 0, 2, 1, 2]\n    //   s₇: [0, 0, 1, 1, 0, 1, 0, 0, 0] -> [1, 2, 3, 3, 2, 3, 2, 1, 2]\n    //   => [1, 2, 3, 3, 2, 3, 2, 1, 2]\n    //\n    // combination for s₂, s₅, s₈:\n    //   s₂: [1, 0, 0, 0, 0, 0, 1, 0, 0] -> [1, 0, 0, 0, 0, 0, 1, 0, 0]\n    //   s₅: [0, 1, 0, 1, 0, 0, 0, 1, 1] -> [1, 2, 0, 2, 0, 0, 1, 2, 2]\n    //   s₈: [0, 0, 1, 0, 1, 1, 0, 0, 0] -> [1, 2, 3, 2, 3, 3, 1, 2, 2]\n    //   => [1, 2, 3, 2, 3, 3, 1, 2, 2]\n    //\n    // |combination_assignments| = [\n    //   [1, 1, 1, 1, 1, 1, 1, 1, 1],\n    //   [1, 2, 3, 3, 3, 2, 2, 2, 1],\n    //   [1, 2, 3, 3, 2, 3, 2, 1, 2],\n    //   [1, 2, 3, 2, 3, 3, 1, 2, 2],\n    // ]\n    CombineSimpleSelectors(n, exclusion_matrix, max_degree);\n  }\n\n private:\n  FRIEND_TEST(SelectorCompressorTest, HandleZeroDegreeSelectors);\n  FRIEND_TEST(SelectorCompressorTest, ConstructCombinedSelector);\n\n  void HandleZeroDegreeSelectors() {\n    // All provided selectors of degree 0 are assumed to be either concrete\n    // selectors or do not appear in a gate. Let's address these first.\n    // s₉ should be erased.\n    base::EraseIf(selectors_, [this](const SelectorDescription& selector) {\n      if (selector.max_degree() != 0) return false;\n      // This is a complex selector, or a selector that does not\n      // appear in any gate constraint.\n      std::unique_ptr<Expression<F>> expression = callback_.Run();\n\n      std::vector<F> combination_assignment =\n          base::Map(selector.activations(),\n                    [](bool b) { return b ? F::One() : F::Zero(); });\n      size_t combination_index = combination_assignments_.size();\n      combination_assignments_.push_back(std::move(combination_assignment));\n      selector_assignments_.push_back(SelectorAssignment<F>(\n          selector.selector_index(), combination_index, std::move(expression)));\n      return true;\n    });\n  }\n\n  void CombineSimpleSelectors(size_t n, const ExclusionMatrix& exclusion_matrix,\n                              size_t max_degree) {\n    // Simple selectors that we've added to combinations already.\n    std::vector<bool> added(selectors_.size(), false);\n    // For example in the first iteration, it adds:\n    //   s₀: SelectorDescription(0, [1, 0, 0, 0, 0, 0, 0, 0, 1], 3)\n    //   s₃: SelectorDescription(3, [0, 1, 0, 0, 0, 1, 1, 1, 0], 3)\n    //   s₆: SelectorDescription(6, [0, 0, 1, 1, 1, 0, 0, 0, 0], 7)\n    for (size_t i = 0; i < selectors_.size(); ++i) {\n      if (added[i]) continue;\n      added[i] = true;\n      const SelectorDescription& selector = selectors_[i];\n      CHECK_LE(selector.max_degree(), max_degree);\n      // This is used to keep track of the largest degree gate involved in the\n      // combination so far. We subtract by one to omit the virtual selector\n      // which will be substituted by the caller with the expression we give\n      // them.\n      size_t d = selector.max_degree() - 1;\n      std::vector<SelectorDescription> combination = {selector};\n      std::vector<size_t> combination_added = {i};\n\n      // Try to find other selectors that can join this one.\n      for (size_t j = i + 1; j < selectors_.size(); ++j) {\n        // The iteration breaks after s₆ is added to the combination.\n        // |s₆.max_degree()| - 1 + |combination.size()| = 7 + 3 = 10\n        if (d + combination.size() == max_degree) {\n          // Short circuit; nothing can be added to this\n          // combination.\n          break;\n        }\n\n        // Skip selectors that have been added to previous combinations\n        if (added[j]) continue;\n\n        // Is this selector excluded from co-existing in the same\n        // combination with any of the other selectors so far?\n        // s₁, s₂, s₄, s₅, s₇, s₈ are excluded in the first iteration.\n        bool excluded =\n            std::any_of(combination_added.begin(), combination_added.end(),\n                        [&exclusion_matrix, j](size_t i) {\n                          return exclusion_matrix.IsExclusive(j, i);\n                        });\n        if (excluded) continue;\n\n        // Can the new selector join the combination? Reminder: we use\n        // |selector.max_degree() - 1| to omit the influence of the virtual\n        // selector on the degree, as it will be substituted.\n        size_t new_d = std::max(d, selector.max_degree() - 1);\n        if (new_d + combination.size() + 1 > max_degree) {\n          // Guess not.\n          continue;\n        }\n\n        d = new_d;\n        combination.push_back(selectors_[j]);\n        combination_added.push_back(j);\n        added[j] = true;\n      }\n\n      ConstructCombinedSelector(n, combination);\n    }\n  }\n\n  void ConstructCombinedSelector(\n      size_t n, const std::vector<SelectorDescription>& combination) {\n    // Now, compute the selector and combination assignments.\n    std::vector<F> combination_assignment(n);\n    base::ParallelizeFill(combination_assignment, F::Zero(),\n                          /*threshold=*/math::ParallelizeThreshold::kFieldInit);\n    size_t combination_len = combination.size();\n    size_t combination_index = combination_assignments_.size();\n    std::unique_ptr<Expression<F>> query = callback_.Run();\n\n    F assigned_root = F::One();\n    selector_assignments_.reserve(selector_assignments_.size() +\n                                  combination.size());\n    for (const SelectorDescription& selector : combination) {\n      // Compute the expression for substitution. This produces an expression\n      // of the form\n      //     q * Prod[i = 1..combination_len, i != assigned_root](i - q)\n      //\n      // which is non-zero only on rows where |combination_assignment| is set\n      // to |assigned_root|. In particular, rows set to 0 correspond to all\n      // selectors being disabled.\n      // For example, in the first iteration, it produces:\n      //     q * (2 - q) * (3 - q)\n      std::unique_ptr<Expression<F>> expression = query->Clone();\n      F root = F::One();\n      for (size_t i = 0; i < combination_len; ++i) {\n        if (root != assigned_root) {\n          expression = std::move(expression) *\n                       (ExpressionFactory<F>::Constant(root) - query);\n        }\n        root += F::One();\n      }\n\n      // Update the combination assignment\n      const std::vector<bool>& activations = selector.activations();\n      for (size_t i = 0; i < n; ++i) {\n        // This will not overwrite another selector's activations\n        // because we have ensured that selectors are disjoint.\n        if (activations[i]) {\n          combination_assignment[i] = assigned_root;\n        }\n      }\n\n      assigned_root += F::One();\n      selector_assignments_.emplace_back(\n          selector.selector_index(), combination_index, std::move(expression));\n    }\n    combination_assignments_.push_back(std::move(combination_assignment));\n  }\n\n  std::vector<std::vector<F>> combination_assignments_;\n  std::vector<SelectorAssignment<F>> selector_assignments_;\n  AllocateFixedColumnCallback callback_;\n  std::vector<SelectorDescription> selectors_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_SELECTOR_COMPRESSOR_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/constraint_system/selector_compressor_unittest.cc",
    "content": "#include \"tachyon/zk/plonk/constraint_system/selector_compressor.h\"\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/functional/callback.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n#include \"tachyon/zk/plonk/expressions/expression_factory.h\"\n\nnamespace tachyon::zk::plonk {\n\nnamespace {\n\nusing F = math::GF7;\nusing AllocateFixedColumnCallback =\n    base::RepeatingCallback<std::unique_ptr<Expression<F>>()>;\n\nclass SelectorCompressorTest : public math::FiniteFieldTest<F> {\n public:\n  void SetUp() override {\n    // [1, 0, 0, 0, 0, 0, 0, 0, 1],\n    selectors_in_.push_back(\n        {true, false, false, false, false, false, false, false, true});\n    // [1, 0, 0, 0, 0, 0, 0, 1, 0],\n    selectors_in_.push_back(\n        {true, false, false, false, false, false, false, true, false});\n    // [1, 0, 0, 0, 0, 0, 1, 0, 0],\n    selectors_in_.push_back(\n        {true, false, false, false, false, false, true, false, false});\n    // [0, 1, 0, 0, 0, 1, 1, 1, 0],\n    selectors_in_.push_back(\n        {false, true, false, false, false, true, true, true, false});\n    // [0, 1, 0, 0, 1, 0, 1, 0, 1],\n    selectors_in_.push_back(\n        {false, true, false, false, true, false, true, false, true});\n    // [0, 1, 0, 1, 0, 0, 0, 1, 1],\n    selectors_in_.push_back(\n        {false, true, false, true, false, false, false, true, true});\n    // [0, 0, 1, 1, 1, 0, 0, 0, 0],\n    selectors_in_.push_back(\n        {false, false, true, true, true, false, false, false, false});\n    // [0, 0, 1, 1, 0, 1, 0, 0, 0],\n    selectors_in_.push_back(\n        {false, false, true, true, false, true, false, false, false});\n    // [0, 0, 1, 0, 1, 1, 0, 0, 0],\n    selectors_in_.push_back(\n        {false, false, true, false, true, true, false, false, false});\n    // [1, 1, 1, 1, 1, 1, 1, 1, 1],\n    selectors_in_.push_back(\n        {true, true, true, true, true, true, true, true, true});\n\n    degrees_ = {3, 3, 3, 3, 3, 3, 7, 7, 7, 0};\n\n    callback_ = [this]() {\n      auto ret = ExpressionFactory<F>::Fixed(\n          FixedQuery(new_column_index_, Rotation::Cur(),\n                     FixedColumnKey(new_column_index_)));\n      new_column_index_++;\n      return ret;\n    };\n  }\n\n protected:\n  size_t new_column_index_ = 0;\n  std::vector<std::vector<bool>> selectors_in_;\n  std::vector<size_t> degrees_;\n  AllocateFixedColumnCallback callback_;\n};\n\n}  // namespace\n\nTEST_F(SelectorCompressorTest, HandleZeroDegreeSelectors) {\n  SelectorCompressor<F> selector_compressor;\n  selector_compressor.callback_ = callback_;\n\n  selectors_in_.push_back(\n      {false, true, false, true, false, true, false, true, false});\n  selectors_in_.push_back(\n      {false, false, true, true, true, false, false, false, false});\n  degrees_.push_back(0);\n  degrees_.push_back(0);\n\n  selector_compressor.selectors_ = base::Map(\n      selectors_in_, [this](size_t i, const std::vector<bool>& activations) {\n        size_t max_degree = degrees_[i];\n        return SelectorDescription(i, &activations, max_degree);\n      });\n\n  // All selectors of degree 0 should be filtered out.\n  selector_compressor.HandleZeroDegreeSelectors();\n  EXPECT_EQ(selector_compressor.selectors_.size(), 9);\n  for (const SelectorDescription& selector : selector_compressor.selectors_) {\n    EXPECT_NE(selector.max_degree(), 0);\n  }\n}\n\nTEST_F(SelectorCompressorTest, ConstructCombinedSelector) {\n  SelectorCompressor<F> selector_compressor;\n  selector_compressor.callback_ = callback_;\n\n  // s₀: SelectorDescription(0, [1, 0, 0, 0, 0, 0, 0, 0, 1], 3)\n  // s₃: SelectorDescription(3, [0, 1, 0, 0, 0, 1, 1, 1, 0], 3)\n  // s₆: SelectorDescription(6, [0, 0, 1, 1, 1, 0, 0, 0, 0], 7)\n  // => [1, 2, 3, 3, 3, 2, 2, 2, 1]\n  std::vector<SelectorDescription> before_combination = {\n      {0, &selectors_in_[0], 3},\n      {3, &selectors_in_[3], 3},\n      {6, &selectors_in_[6], 7},\n  };\n  selector_compressor.ConstructCombinedSelector(9, before_combination);\n  std::vector<F> expected_combination_assignment = {\n      F(1), F(2), F(3), F(3), F(3), F(2), F(2), F(2), F(1)};\n  EXPECT_EQ(selector_compressor.combination_assignments()[0],\n            expected_combination_assignment);\n}\n\nTEST_F(SelectorCompressorTest, Process) {\n  std::vector<std::vector<F>> expected_polys = {\n      {F(1), F(1), F(1), F(1), F(1), F(1), F(1), F(1), F(1)},\n      {F(1), F(2), F(3), F(3), F(3), F(2), F(2), F(2), F(1)},\n      {F(1), F(2), F(3), F(3), F(2), F(3), F(2), F(1), F(2)},\n      {F(1), F(2), F(3), F(2), F(3), F(3), F(1), F(2), F(2)},\n  };\n\n  std::vector<size_t> expected_selector_indices = {9, 0, 3, 6, 1,\n                                                   4, 7, 2, 5, 8};\n\n  SelectorCompressor<F> selector_compressor;\n\n  selector_compressor.Process(selectors_in_, degrees_, 10, callback_);\n\n  std::vector<size_t> actual_selector_indices =\n      base::Map(selector_compressor.selector_assignments(),\n                [](const SelectorAssignment<F>& assignment) {\n                  return assignment.selector_index();\n                });\n\n  EXPECT_EQ(selector_compressor.combination_assignments(), expected_polys);\n  EXPECT_EQ(actual_selector_indices, expected_selector_indices);\n}\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/constraint_system/selector_description.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_SELECTOR_DESCRIPTION_H_\n#define TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_SELECTOR_DESCRIPTION_H_\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/containers/adapters.h\"\n#include \"tachyon/export.h\"\n\nnamespace tachyon::zk::plonk {\n\n// This describes a selector and where it is activated.\nclass TACHYON_EXPORT SelectorDescription {\n public:\n  SelectorDescription() = default;\n  SelectorDescription(size_t selector_index,\n                      const std::vector<bool>* activations, size_t max_degree)\n      : selector_index_(selector_index),\n        activations_(activations),\n        max_degree_(max_degree) {}\n\n  size_t selector_index() const { return selector_index_; }\n  const std::vector<bool>& activations() const { return *activations_; }\n  size_t max_degree() const { return max_degree_; }\n\n  // Checks if two |activations| vectors are orthogonal.\n  // In a mathematical sense, two vectors are orthogonal if their dot product is\n  // zero. For boolean vectors, this means there is no position where both have\n  // true values.\n  bool IsOrthogonal(const SelectorDescription& other) const {\n    for (auto [l, r] : base::Zipped(*activations_, other.activations())) {\n      if (l & r) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n private:\n  // The selector that this description references, by index.\n  size_t selector_index_ = 0;\n  // not owned\n  // The vector of booleans defining which rows are active for this selector.\n  const std::vector<bool>* activations_ = nullptr;\n  // The maximum degree of a gate involving this selector, including the\n  // virtual selector itself. This means this will be at least 1 for any\n  // expression containing a simple selector, even if that selector is not\n  // multiplied by anything.\n  size_t max_degree_ = 0;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_SELECTOR_DESCRIPTION_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/constraint_system/selector_description_unittest.cc",
    "content": "#include \"tachyon/zk/plonk/constraint_system/selector_description.h\"\n\n#include \"gtest/gtest.h\"\n\nnamespace tachyon::zk::plonk {\n\nTEST(SelectorDescriptionTest, IsOrthogonal) {\n  std::vector<std::vector<bool>> selector_activations = {\n      // [1, 0, 1, 0, 1]\n      {true, false, true, false, true},\n      // [0, 1, 0, 1, 0]\n      {false, true, false, true, false},\n      // [1, 0, 0, 0, 0]\n      {true, false, false, false, false},\n  };\n  SelectorDescription a(size_t{0}, &selector_activations[0], size_t{1});\n  SelectorDescription b(size_t{1}, &selector_activations[1], size_t{1});\n  SelectorDescription c(size_t{2}, &selector_activations[2], size_t{1});\n\n  // [1, 0, 1, 0, 1]\n  // [0, 1, 0, 1, 0]\n  EXPECT_TRUE(a.IsOrthogonal(b));\n\n  // [0, 1, 0, 1, 0]\n  // [1, 0, 0, 0, 0]\n  EXPECT_TRUE(b.IsOrthogonal(c));\n\n  // [1, 0, 1, 0, 1]\n  // [1, 0, 0, 0, 0]\n  // They are not Orthogonal due to the first index.\n  EXPECT_FALSE(a.IsOrthogonal(c));\n}\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/constraint_system/selector_unittest.cc",
    "content": "#include \"tachyon/zk/plonk/constraint_system/selector.h\"\n\n#include \"absl/hash/hash_testing.h\"\n#include \"gtest/gtest.h\"\n\nnamespace tachyon::zk::plonk {\n\nTEST(SelectorTest, Hash) {\n  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(\n      std::make_tuple(Selector::Simple(0), Selector::Simple(1),\n                      Selector::Complex(0), Selector::Complex(1))));\n}\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/constraint_system/virtual_cell.h",
    "content": "#ifndef TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_VIRTUAL_CELL_H_\n#define TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_VIRTUAL_CELL_H_\n\n#include <string>\n\n#include \"tachyon/export.h\"\n#include \"tachyon/zk/base/rotation.h\"\n#include \"tachyon/zk/plonk/base/column_key.h\"\n\nnamespace tachyon::zk::plonk {\n\nclass TACHYON_EXPORT VirtualCell {\n public:\n  VirtualCell() = default;\n  VirtualCell(const AnyColumnKey& column, Rotation rotation)\n      : column_(column), rotation_(rotation) {}\n\n  const AnyColumnKey& column() const { return column_; }\n  Rotation rotation() const { return rotation_; }\n\n  bool operator==(const VirtualCell& other) const {\n    return column_ == other.column_ && rotation_ == other.rotation_;\n  }\n  bool operator!=(const VirtualCell& other) const { return !operator==(other); }\n\n  std::string ToString() const {\n    return absl::Substitute(\"column: $0, rotation: $1\", column_.ToString(),\n                            rotation_.ToString());\n  }\n\n private:\n  AnyColumnKey column_;\n  Rotation rotation_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_VIRTUAL_CELL_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/constraint_system/virtual_cells.h",
    "content": "#ifndef TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_VIRTUAL_CELLS_H_\n#define TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_VIRTUAL_CELLS_H_\n\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/zk/plonk/constraint_system/selector.h\"\n#include \"tachyon/zk/plonk/constraint_system/virtual_cell.h\"\n#include \"tachyon/zk/plonk/expressions/evaluator/simple_selector_finder.h\"\n#include \"tachyon/zk/plonk/expressions/expression_factory.h\"\n#include \"tachyon/zk/plonk/layout/lookup_table_column.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F>\nclass CellsQuerier;\n\ntemplate <typename F>\nclass ConstraintSystem;\n\n// Exposes the \"virtual cells\" that can be queried while creating a custom gate\n// or lookup table.\ntemplate <typename F>\nclass VirtualCells {\n public:\n  explicit VirtualCells(ConstraintSystem<F>* meta) : meta_(meta) {}\n\n  const std::vector<Selector>& queried_selectors() const& {\n    return queried_selectors_;\n  }\n  const std::vector<VirtualCell>& queried_cells() const& {\n    return queried_cells_;\n  }\n\n  std::vector<Selector>&& TakeQueriedSelectors() && {\n    return std::move(queried_selectors_);\n  }\n  std::vector<VirtualCell>&& TakeQueriedCells() && {\n    return std::move(queried_cells_);\n  }\n\n  // Query a selector at the current position.\n  std::unique_ptr<Expression<F>> QuerySelector(Selector selector) {\n    queried_selectors_.push_back(selector);\n    return ExpressionFactory<F>::Selector(selector);\n  }\n\n  // Query a fixed column at a relative position\n  std::unique_ptr<Expression<F>> QueryFixed(const FixedColumnKey& column,\n                                            Rotation at) {\n    queried_cells_.emplace_back(column, at);\n    return ExpressionFactory<F>::Fixed(\n        {meta_->QueryFixedIndex(column, at), at, column});\n  }\n\n  // Query an advice column at a relative position\n  std::unique_ptr<Expression<F>> QueryAdvice(const AdviceColumnKey& column,\n                                             Rotation at) {\n    queried_cells_.emplace_back(column, at);\n    return ExpressionFactory<F>::Advice(\n        {meta_->QueryAdviceIndex(column, at), at, column});\n  }\n\n  // Query an instance column at a relative position\n  std::unique_ptr<Expression<F>> QueryInstance(const InstanceColumnKey& column,\n                                               Rotation at) {\n    queried_cells_.emplace_back(column, at);\n    return ExpressionFactory<F>::Instance(\n        {meta_->QueryInstanceIndex(column, at), at, column});\n  }\n\n  // Query an Any column at a relative position\n  std::unique_ptr<Expression<F>> QueryAny(const AnyColumnKey& column,\n                                          Rotation at) {\n    switch (column.type()) {\n      case ColumnType::kAdvice:\n        return QueryAdvice(column, at);\n      case ColumnType::kFixed:\n        return QueryFixed(column, at);\n      case ColumnType::kInstance:\n        return QueryInstance(column, at);\n      case ColumnType::kAny:\n        break;\n    }\n    NOTREACHED();\n    return nullptr;\n  }\n\n  // Query a challenge\n  std::unique_ptr<Expression<F>> QueryChallenge(Challenge challenge) {\n    return ExpressionFactory<F>::Challenge(challenge);\n  }\n\n  // Query a lookup table\n  std::unique_ptr<Expression<F>> QueryLookupTable(\n      const lookup::Pair<std::unique_ptr<Expression<F>>, LookupTableColumn>&\n          pair,\n      Rotation at) {\n    CHECK(!ContainsSimpleSelector(pair.input().get()))\n        << \"expression containing simple selector supplied to lookup argument\";\n\n    return QueryFixed(pair.table().column(), at);\n  }\n\n private:\n  friend class CellsQuerier<F>;\n\n  // not owned\n  ConstraintSystem<F>* const meta_;\n  std::vector<Selector> queried_selectors_;\n  std::vector<VirtualCell> queried_cells_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_CONSTRAINT_SYSTEM_VIRTUAL_CELLS_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/examples/BUILD.bazel",
    "content": "load(\n    \"//bazel:tachyon_cc.bzl\",\n    \"tachyon_cc_library\",\n    \"tachyon_cc_unittest\",\n    \"tachyon_openmp_num_threads_env\",\n)\n\npackage(default_visibility = [\"//visibility:public\"])\n\nCOMMON_TEST_DATA_DEPS = [\n    \":circuit_test_data\",\n    \":circuit_test_type_traits\",\n    \":point\",\n]\n\nCOMMON_TEST_DEPS = [\n    \":circuit_test\",\n    \"//tachyon/zk/plonk/layout/floor_planner:simple_floor_planner\",\n    \"//tachyon/zk/plonk/layout/floor_planner/v1:v1_floor_planner\",\n]\n\ntachyon_cc_library(\n    name = \"circuit_test_data\",\n    testonly = True,\n    hdrs = [\"circuit_test_data.h\"],\n    deps = [\n        \":point\",\n        \"//tachyon/base:range\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"circuit_test_type_traits\",\n    testonly = True,\n    hdrs = [\"circuit_test_type_traits.h\"],\n    deps = [\n        \"//tachyon/math/elliptic_curves/bn/bn254\",\n        \"//tachyon/zk/base/commitments:gwc_extension\",\n        \"//tachyon/zk/base/commitments:shplonk_extension\",\n        \"//tachyon/zk/plonk/examples/fibonacci:fibonacci1_circuit\",\n        \"//tachyon/zk/plonk/examples/fibonacci:fibonacci2_circuit\",\n        \"//tachyon/zk/plonk/examples/fibonacci:fibonacci3_circuit\",\n        \"//tachyon/zk/plonk/halo2:prover_test\",\n        \"//tachyon/zk/plonk/halo2:proving_scheme\",\n        \"//tachyon/zk/plonk/layout/floor_planner:simple_floor_planner\",\n        \"//tachyon/zk/plonk/layout/floor_planner/v1:v1_floor_planner\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"circuit_test\",\n    testonly = True,\n    hdrs = [\"circuit_test.h\"],\n    deps = [\n        \":point\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/math/elliptic_curves/bn/bn254\",\n        \"//tachyon/math/elliptic_curves/bn/bn254/halo2:bn254\",\n        \"//tachyon/zk/lookup:pair\",\n        \"//tachyon/zk/plonk/halo2:pinned_constraint_system\",\n        \"//tachyon/zk/plonk/halo2:pinned_verifying_key\",\n        \"//tachyon/zk/plonk/halo2:prover_test\",\n        \"//tachyon/zk/plonk/keys:proving_key\",\n        \"//tachyon/zk/plonk/layout/floor_planner:simple_floor_planner\",\n        \"//tachyon/zk/plonk/layout/floor_planner/v1:v1_floor_planner\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"point\",\n    hdrs = [\"point.h\"],\n)\n\ntachyon_cc_library(\n    name = \"multi_lookup_circuit\",\n    hdrs = [\"multi_lookup_circuit.h\"],\n    deps = [\"//tachyon/zk/plonk/constraint_system:circuit\"],\n)\n\ntachyon_cc_library(\n    name = \"multi_lookup_circuit_test_data\",\n    testonly = True,\n    hdrs = [\"multi_lookup_circuit_test_data.h\"],\n    deps = COMMON_TEST_DATA_DEPS,\n)\n\ntachyon_cc_library(\n    name = \"shuffle_circuit\",\n    hdrs = [\"shuffle_circuit.h\"],\n    deps = [\n        \"//tachyon/zk/plonk/constraint_system:circuit\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"shuffle_circuit_test_data\",\n    testonly = True,\n    hdrs = [\"shuffle_circuit_test_data.h\"],\n    deps = COMMON_TEST_DATA_DEPS,\n)\n\ntachyon_cc_library(\n    name = \"shuffle_api_circuit\",\n    hdrs = [\"shuffle_api_circuit.h\"],\n    deps = [\n        \"//tachyon/zk/plonk/constraint_system:circuit\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"shuffle_api_circuit_test_data\",\n    testonly = True,\n    hdrs = [\"shuffle_api_circuit_test_data.h\"],\n    deps = COMMON_TEST_DATA_DEPS,\n)\n\ntachyon_cc_library(\n    name = \"simple_circuit\",\n    hdrs = [\"simple_circuit.h\"],\n    deps = [\"//tachyon/zk/plonk/constraint_system:circuit\"],\n)\n\ntachyon_cc_library(\n    name = \"simple_circuit_test_data\",\n    testonly = True,\n    hdrs = [\"simple_circuit_test_data.h\"],\n    deps = COMMON_TEST_DATA_DEPS,\n)\n\ntachyon_cc_library(\n    name = \"simple_lookup_circuit\",\n    hdrs = [\"simple_lookup_circuit.h\"],\n    deps = [\"//tachyon/zk/plonk/constraint_system:circuit\"],\n)\n\ntachyon_cc_library(\n    name = \"simple_lookup_circuit_test_data\",\n    testonly = True,\n    hdrs = [\"simple_lookup_circuit_test_data.h\"],\n    deps = COMMON_TEST_DATA_DEPS + [\"//tachyon/base:range\"],\n)\n\ntachyon_cc_unittest(\n    name = \"multi_lookup_circuit_test\",\n    srcs = [\"multi_lookup_circuit_test.cc\"],\n    env = tachyon_openmp_num_threads_env(4),\n    tags = [\"gpu_heavy_test\"],\n    deps = COMMON_TEST_DEPS + [\n        \":multi_lookup_circuit\",\n        \":multi_lookup_circuit_test_data\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"shuffle_circuit_test\",\n    srcs = [\"shuffle_circuit_test.cc\"],\n    env = tachyon_openmp_num_threads_env(4),\n    tags = [\"gpu_heavy_test\"],\n    deps = COMMON_TEST_DEPS + [\n        \":shuffle_circuit\",\n        \":shuffle_circuit_test_data\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"shuffle_api_circuit_test\",\n    srcs = [\"shuffle_api_circuit_test.cc\"],\n    env = tachyon_openmp_num_threads_env(4),\n    tags = [\"gpu_heavy_test\"],\n    deps = COMMON_TEST_DEPS + [\n        \":shuffle_api_circuit\",\n        \":shuffle_api_circuit_test_data\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"simple_circuit_test\",\n    srcs = [\"simple_circuit_test.cc\"],\n    env = tachyon_openmp_num_threads_env(4),\n    tags = [\"gpu_heavy_test\"],\n    deps = COMMON_TEST_DEPS + [\n        \":simple_circuit\",\n        \":simple_circuit_test_data\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"simple_lookup_circuit_test\",\n    srcs = [\"simple_lookup_circuit_test.cc\"],\n    env = tachyon_openmp_num_threads_env(4),\n    tags = [\"gpu_heavy_test\"],\n    deps = COMMON_TEST_DEPS + [\n        \":simple_lookup_circuit\",\n        \":simple_lookup_circuit_test_data\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/zk/plonk/examples/circuit_test.h",
    "content": "#ifndef TACHYON_ZK_PLONK_EXAMPLES_CIRCUIT_TEST_H_\n#define TACHYON_ZK_PLONK_EXAMPLES_CIRCUIT_TEST_H_\n\n#include <optional>\n#include <utility>\n#include <vector>\n\n#include \"absl/types/span.h\"\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/bn254.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/halo2/bn254.h\"\n#include \"tachyon/zk/lookup/pair.h\"\n#include \"tachyon/zk/plonk/examples/circuit_test_type_traits.h\"\n#include \"tachyon/zk/plonk/examples/point.h\"\n#include \"tachyon/zk/plonk/halo2/pinned_constraint_system.h\"\n#include \"tachyon/zk/plonk/halo2/pinned_verifying_key.h\"\n#include \"tachyon/zk/plonk/halo2/prover_test.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename _Circuit, typename _PS>\nstruct TestArguments {\n  using Circuit = _Circuit;\n  using PS = _PS;\n};\n\ntemplate <typename TestArguments, typename TestData>\nclass CircuitTest : public halo2::ProverTest<typename TestArguments::PS> {\n public:\n  using Circuit = typename TestArguments::Circuit;\n  using PS = typename TestArguments::PS;\n  using PCS = typename PS::PCS;\n  using F = typename PCS::Field;\n  using Poly = typename PCS::Poly;\n  using Evals = typename PCS::Evals;\n  using Domain = typename PCS::Domain;\n  using Commitment = typename PCS::Commitment;\n  using RationalEvals = typename PCS::RationalEvals;\n\n  constexpr static lookup::Type kLookupType = PS::kLookupType;\n\n  static void SetUpTestSuite() {\n    math::bn254::BN254Curve::Init();\n    math::halo2::OverrideSubgroupGenerator();\n  }\n\n  void ConfigureTest() {\n    ConstraintSystem<F> constraint_system;\n    constraint_system.set_lookup_type(kLookupType);\n\n    auto config = Circuit::Configure(constraint_system);\n\n    TestData::TestConfig(config);\n\n    halo2::PinnedConstraintSystem<F> pinned_constraint_system(\n        constraint_system);\n    EXPECT_EQ(TestData::kPinnedConstraintSystem,\n              base::ToRustDebugString(pinned_constraint_system));\n\n    EXPECT_TRUE(constraint_system.selector_map().empty());\n    EXPECT_TRUE(constraint_system.general_column_annotations().empty());\n  }\n\n  void SynthesizeTest() {\n    CHECK(this->prover_->pcs().UnsafeSetup(TestData::kN, F(2)));\n    this->prover_->set_domain(Domain::Create(TestData::kN));\n    const Domain* domain = this->prover_->domain();\n\n    ConstraintSystem<F> constraint_system;\n    constraint_system.set_lookup_type(kLookupType);\n\n    auto config = Circuit::Configure(constraint_system);\n    Assembly<RationalEvals> assembly =\n        VerifyingKey<F, Commitment>::template CreateAssembly<RationalEvals>(\n            domain, constraint_system);\n\n    Circuit circuit = TestData::GetCircuit();\n    typename Circuit::FloorPlanner floor_planner;\n    floor_planner.Synthesize(&assembly, circuit, std::move(config),\n                             constraint_system.constants());\n\n    if constexpr (TestData::kAssemblyFixedColumnsFlag) {\n      std::vector<RationalEvals> expected_fixed_columns = CreateRationalColumns(\n          base::Array2DToVector2D(TestData::kAssemblyFixedColumns));\n      EXPECT_EQ(assembly.fixed_columns(), expected_fixed_columns);\n    } else {\n      EXPECT_TRUE(assembly.fixed_columns().empty());\n    }\n\n    if constexpr (TestData::kAssemblyPermutationColumnsFlag) {\n      std::vector<AnyColumnKey> expected_columns =\n          base::ArrayToVector(TestData::kAssemblyPermutationColumns);\n      EXPECT_EQ(assembly.permutation().columns(), expected_columns);\n    } else {\n      EXPECT_TRUE(assembly.permutation().columns().empty());\n    }\n\n    const CycleStore& cycle_store = assembly.permutation().cycle_store();\n\n    if constexpr (TestData::kCycleStoreMappingFlag) {\n      CycleStore::Table<Label> expected_mapping(\n          base::Array2DToVector2D(TestData::kCycleStoreMapping));\n      EXPECT_EQ(cycle_store.mapping(), expected_mapping);\n    } else {\n      EXPECT_TRUE(cycle_store.mapping().IsEmpty());\n    }\n\n    if constexpr (TestData::kCycleStoreAuxFlag) {\n      CycleStore::Table<Label> expected_aux(\n          base::Array2DToVector2D(TestData::kCycleStoreAux));\n      EXPECT_EQ(cycle_store.aux(), expected_aux);\n    } else {\n      EXPECT_TRUE(cycle_store.aux().IsEmpty());\n    }\n\n    if constexpr (TestData::kCycleStoreSizesFlag) {\n      CycleStore::Table<size_t> expected_sizes(\n          base::Array2DToVector2D(TestData::kCycleStoreSizes));\n      EXPECT_EQ(cycle_store.sizes(), expected_sizes);\n    } else {\n      EXPECT_TRUE(cycle_store.sizes().IsEmpty());\n    }\n\n    std::vector<std::vector<bool>> expected_selectors =\n        base::Array2DToVector2D(TestData::kSelectors);\n    EXPECT_EQ(assembly.selectors(), expected_selectors);\n\n    EXPECT_EQ(assembly.usable_rows(), TestData::kUsableRows);\n  }\n\n  void LoadVerifyingKeyTest() {\n    CHECK(this->prover_->pcs().UnsafeSetup(TestData::kN, F(2)));\n    this->prover_->set_domain(Domain::Create(TestData::kN));\n\n    Circuit circuit = TestData::GetCircuit();\n\n    VerifyingKey<F, Commitment> vkey;\n    ASSERT_TRUE(vkey.Load(this->prover_.get(), circuit, kLookupType));\n\n    halo2::PinnedVerifyingKey pinned_vkey(this->prover_.get(), vkey);\n    EXPECT_EQ(base::ToRustDebugString(pinned_vkey),\n              TestData::kPinnedVerifyingKey);\n\n    F expected_transcript_repr = *F::FromHexString(TestData::kTranscriptRepr);\n    EXPECT_EQ(vkey.transcript_repr(), expected_transcript_repr);\n  }\n\n  void LoadProvingKeyTest() {\n    CHECK(this->prover_->pcs().UnsafeSetup(TestData::kN, F(2)));\n    this->prover_->set_domain(Domain::Create(TestData::kN));\n\n    Circuit circuit = TestData::GetCircuit();\n\n    for (size_t i = 0; i < 2; ++i) {\n      ProvingKey<PS> pkey;\n      bool load_verifying_key = i == 0;\n      SCOPED_TRACE(\n          absl::Substitute(\"load_verifying_key: $0\", load_verifying_key));\n      if (load_verifying_key) {\n        VerifyingKey<F, Commitment> vkey;\n        ASSERT_TRUE(vkey.Load(this->prover_.get(), circuit, kLookupType));\n        ASSERT_TRUE(pkey.LoadWithVerifyingKey(this->prover_.get(), circuit,\n                                              std::move(vkey)));\n      } else {\n        ASSERT_TRUE(pkey.Load(this->prover_.get(), circuit));\n      }\n\n      if constexpr (TestData::kLFirstFlag) {\n        Poly expected_l_first =\n            CreatePoly(base::ArrayToVector(TestData::kLFirst));\n        EXPECT_EQ(pkey.l_first(), expected_l_first);\n      } else {\n        EXPECT_EQ(pkey.l_first().NumElements(), 0);\n      }\n\n      if constexpr (TestData::kLLastFlag) {\n        Poly expected_l_last =\n            CreatePoly(base::ArrayToVector(TestData::kLLast));\n        EXPECT_EQ(pkey.l_last(), expected_l_last);\n      } else {\n        EXPECT_EQ(pkey.l_last().NumElements(), 0);\n      }\n\n      if constexpr (TestData::kLActiveRowFlag) {\n        Poly expected_l_active_row =\n            CreatePoly(base::ArrayToVector(TestData::kLActiveRow));\n        EXPECT_EQ(pkey.l_active_row(), expected_l_active_row);\n      } else {\n        EXPECT_EQ(pkey.l_active_row().NumElements(), 0);\n      }\n\n      if constexpr (TestData::kFixedColumnsFlag) {\n        std::vector<Evals> expected_fixed_columns =\n            CreateColumns(base::Array2DToVector2D(TestData::kFixedColumns));\n        EXPECT_EQ(pkey.fixed_columns(), expected_fixed_columns);\n      } else {\n        EXPECT_TRUE(pkey.fixed_columns().empty());\n      }\n\n      if constexpr (TestData::kFixedPolysFlag) {\n        std::vector<Poly> expected_fixed_polys =\n            CreatePolys(base::Array2DToVector2D(TestData::kFixedPolys));\n        EXPECT_EQ(pkey.fixed_polys(), expected_fixed_polys);\n      } else {\n        EXPECT_TRUE(pkey.fixed_polys().empty());\n      }\n\n      if constexpr (TestData::kPermutationsColumnsFlag) {\n        std::vector<Evals> expected_permutations_columns = CreateColumns(\n            base::Array2DToVector2D(TestData::kPermutationsColumns));\n        EXPECT_EQ(pkey.permutation_proving_key().permutations(),\n                  expected_permutations_columns);\n      } else {\n        EXPECT_TRUE(pkey.permutation_proving_key().permutations().empty());\n      }\n\n      if constexpr (TestData::kPermutationsPolysFlag) {\n        std::vector<Poly> expected_fixed_polys =\n            CreatePolys(base::Array2DToVector2D(TestData::kPermutationsPolys));\n        EXPECT_EQ(pkey.permutation_proving_key().polys(), expected_fixed_polys);\n      } else {\n        EXPECT_TRUE(pkey.permutation_proving_key().polys().empty());\n      }\n    }\n  }\n\n  void CreateProofTest() {\n    CHECK(this->prover_->pcs().UnsafeSetup(TestData::kN, F(2)));\n    this->prover_->set_domain(Domain::Create(TestData::kN));\n\n    std::vector<Circuit> circuits = TestData::Get2Circuits();\n\n    std::vector<Evals> instance_columns = TestData::GetInstanceColumns();\n    std::vector<std::vector<Evals>> instance_columns_vec = {\n        instance_columns, std::move(instance_columns)};\n\n    ProvingKey<PS> pkey;\n    ASSERT_TRUE(pkey.Load(this->prover_.get(), circuits[0]));\n    this->prover_->CreateProof(pkey, std::move(instance_columns_vec), circuits);\n\n    std::vector<uint8_t> proof =\n        this->prover_->GetWriter()->buffer().owned_buffer();\n    EXPECT_EQ(proof, base::ArrayToVector(TestData::kProof));\n  }\n\n  void VerifyProofTest() {\n    using Proof = halo2::Proof<kLookupType, F, Commitment>;\n    CHECK(this->prover_->pcs().UnsafeSetup(TestData::kN, F(2)));\n    this->prover_->set_domain(Domain::Create(TestData::kN));\n\n    Circuit circuit = TestData::GetCircuit();\n\n    VerifyingKey<F, Commitment> vkey;\n    ASSERT_TRUE(vkey.Load(this->prover_.get(), circuit, kLookupType));\n\n    std::vector<uint8_t> owned_proof = base::ArrayToVector(TestData::kProof);\n\n    halo2::Verifier<PS> verifier = this->CreateVerifier(\n        CreateBufferWithProof(absl::MakeSpan(owned_proof)));\n\n    std::vector<Evals> instance_columns = TestData::GetInstanceColumns();\n    std::vector<std::vector<Evals>> instance_columns_vec = {\n        instance_columns, std::move(instance_columns)};\n\n    Proof proof;\n    F h_eval;\n    ASSERT_TRUE(verifier.VerifyProofForTesting(vkey, instance_columns_vec,\n                                               &proof, &h_eval));\n\n    if constexpr (TestData::kAdviceCommitmentsFlag) {\n      std::vector<std::vector<Commitment>> expected_advice_commitments_vec{\n          CreateCommitments(\n              base::ArrayToVector(TestData::kAdviceCommitments[0])),\n          CreateCommitments(\n              base::ArrayToVector(TestData::kAdviceCommitments[1])),\n      };\n      EXPECT_EQ(proof.advices_commitments_vec, expected_advice_commitments_vec);\n    } else {\n      EXPECT_TRUE(proof.advices_commitments_vec[0].empty());\n    }\n\n    if constexpr (TestData::kChallengesFlag) {\n      std::vector<F> expected_challenges =\n          CreateEvals(base::ArrayToVector(TestData::kChallenges));\n      EXPECT_EQ(proof.challenges, expected_challenges);\n    } else {\n      EXPECT_TRUE(proof.challenges.empty());\n    }\n\n    F expected_theta = *F::FromHexString(TestData::kTheta);\n    EXPECT_EQ(proof.theta, expected_theta);\n\n    if constexpr (TestData::kLookupPermutedCommitmentsFlag) {\n      std::vector<std::vector<lookup::Pair<Commitment>>>\n          expected_lookup_permuted_commitments_vec{\n              CreateLookupPermutedCommitments(\n                  base::ArrayToVector(\n                      TestData::kLookupPermutedCommitmentsInput[0]),\n                  base::ArrayToVector(\n                      TestData::kLookupPermutedCommitmentsTable[0])),\n              CreateLookupPermutedCommitments(\n                  base::ArrayToVector(\n                      TestData::kLookupPermutedCommitmentsInput[1]),\n                  base::ArrayToVector(\n                      TestData::kLookupPermutedCommitmentsTable[1])),\n          };\n      EXPECT_EQ(proof.lookup_permuted_commitments_vec,\n                expected_lookup_permuted_commitments_vec);\n    } else if constexpr (TestData::kLookupMPolyCommitmentsFlag) {\n      std::vector<std::vector<Commitment>>\n          expected_lookup_m_poly_commitments_vec{\n              CreateCommitments(\n                  base::ArrayToVector(TestData::kLookupMPolyCommitments[0])),\n              CreateCommitments(\n                  base::ArrayToVector(TestData::kLookupMPolyCommitments[1])),\n          };\n      EXPECT_EQ(proof.lookup_m_poly_commitments_vec,\n                expected_lookup_m_poly_commitments_vec);\n    }\n\n    F expected_beta = *F::FromHexString(TestData::kBeta);\n    EXPECT_EQ(proof.beta, expected_beta);\n\n    F expected_gamma = *F::FromHexString(TestData::kGamma);\n    EXPECT_EQ(proof.gamma, expected_gamma);\n\n    if constexpr (TestData::kPermutationProductCommitmentsFlag) {\n      std::vector<std::vector<Commitment>>\n          expected_permutation_product_commitments_vec{\n              CreateCommitments(base::ArrayToVector(\n                  TestData::kPermutationProductCommitments[0])),\n              CreateCommitments(base::ArrayToVector(\n                  TestData::kPermutationProductCommitments[1])),\n          };\n      EXPECT_EQ(proof.permutation_product_commitments_vec,\n                expected_permutation_product_commitments_vec);\n    } else {\n      EXPECT_TRUE(proof.permutation_product_commitments_vec[0].empty());\n    }\n\n    if constexpr (TestData::kLookupProductCommitmentsFlag) {\n      std::vector<std::vector<Commitment>>\n          expected_lookup_product_commitments_vec{\n              CreateCommitments(\n                  base::ArrayToVector(TestData::kLookupProductCommitments[0])),\n              CreateCommitments(\n                  base::ArrayToVector(TestData::kLookupProductCommitments[1])),\n          };\n      EXPECT_EQ(proof.lookup_product_commitments_vec,\n                expected_lookup_product_commitments_vec);\n    } else if constexpr (TestData::kLookupSumCommitmentsFlag) {\n      std::vector<std::vector<Commitment>>\n          expected_lookup_sum_commitments_commitments_vec{\n              CreateCommitments(\n                  base::ArrayToVector(TestData::kLookupSumCommitments[0])),\n              CreateCommitments(\n                  base::ArrayToVector(TestData::kLookupSumCommitments[1])),\n          };\n      EXPECT_EQ(proof.lookup_sum_commitments_vec,\n                expected_lookup_sum_commitments_commitments_vec);\n    }\n\n    if constexpr (TestData::kShuffleProductCommitmentsFlag) {\n      std::vector<std::vector<Commitment>>\n          expected_shuffle_product_commitments_vec{\n              CreateCommitments(\n                  base::ArrayToVector(TestData::kShuffleProductCommitments[0])),\n              CreateCommitments(\n                  base::ArrayToVector(TestData::kShuffleProductCommitments[1])),\n          };\n      EXPECT_EQ(proof.shuffle_product_commitments_vec,\n                expected_shuffle_product_commitments_vec);\n    } else {\n      EXPECT_TRUE(proof.shuffle_product_commitments_vec[0].empty());\n    }\n\n    Commitment expected_vanishing_random_poly_commitment =\n        CreateCommitment(TestData::kVanishingRandomPolyCommitment);\n    EXPECT_EQ(proof.vanishing_random_poly_commitment,\n              expected_vanishing_random_poly_commitment);\n\n    F expected_y = *F::FromHexString(TestData::kY);\n    EXPECT_EQ(proof.y, expected_y);\n\n    std::vector<Commitment> expected_vanishing_h_poly_commitments =\n        CreateCommitments(\n            base::ArrayToVector(TestData::kVanishingHPolyCommitments));\n    EXPECT_EQ(proof.vanishing_h_poly_commitments,\n              expected_vanishing_h_poly_commitments);\n\n    F expected_x = *F::FromHexString(TestData::kX);\n    EXPECT_EQ(proof.x, expected_x);\n\n    if constexpr (TestData::kAdviceEvalsFlag) {\n      std::vector<std::vector<F>> expected_advice_evals_vec{\n          CreateEvals(base::ArrayToVector(TestData::kAdviceEvals[0])),\n          CreateEvals(base::ArrayToVector(TestData::kAdviceEvals[1])),\n      };\n      EXPECT_EQ(proof.advice_evals_vec, expected_advice_evals_vec);\n    } else {\n      EXPECT_TRUE(proof.advice_evals_vec[0].empty());\n    }\n\n    if constexpr (TestData::kFixedEvalsFlag) {\n      std::vector<F> expected_fixed_evals =\n          CreateEvals(base::ArrayToVector(TestData::kFixedEvals));\n      EXPECT_EQ(proof.fixed_evals, expected_fixed_evals);\n    } else {\n      EXPECT_TRUE(proof.fixed_evals.empty());\n    }\n\n    F expected_vanishing_random_eval =\n        *F::FromHexString(TestData::kVanishingRandomEval);\n    EXPECT_EQ(proof.vanishing_random_eval, expected_vanishing_random_eval);\n\n    if constexpr (TestData::kCommonPermutationEvalsFlag) {\n      std::vector<F> expected_common_permutation_evals =\n          CreateEvals(base::ArrayToVector(TestData::kCommonPermutationEvals));\n      EXPECT_EQ(proof.common_permutation_evals,\n                expected_common_permutation_evals);\n    } else {\n      EXPECT_TRUE(proof.common_permutation_evals.empty());\n    }\n\n    if constexpr (TestData::kPermutationProductEvalsFlag) {\n      std::vector<std::vector<F>> expected_permutation_product_evals_vec{\n          CreateEvals(\n              base::ArrayToVector(TestData::kPermutationProductEvals[0])),\n          CreateEvals(\n              base::ArrayToVector(TestData::kPermutationProductEvals[1])),\n      };\n      EXPECT_EQ(proof.permutation_product_evals_vec,\n                expected_permutation_product_evals_vec);\n    } else {\n      EXPECT_TRUE(proof.permutation_product_evals_vec[0].empty());\n    }\n\n    if constexpr (TestData::kPermutationProductNextEvalsFlag) {\n      std::vector<std::vector<F>> expected_permutation_product_next_evals_vec{\n          CreateEvals(\n              base::ArrayToVector(TestData::kPermutationProductNextEvals[0])),\n          CreateEvals(\n              base::ArrayToVector(TestData::kPermutationProductNextEvals[1])),\n      };\n      EXPECT_EQ(proof.permutation_product_next_evals_vec,\n                expected_permutation_product_next_evals_vec);\n    } else {\n      EXPECT_TRUE(proof.permutation_product_next_evals_vec[0].empty());\n    }\n\n    if constexpr (TestData::kPermutationProductLastEvalsFlag) {\n      std::vector<std::vector<std::optional<F>>>\n          expected_permutation_product_last_evals_vec{\n              CreateOptionalEvals(base::ArrayToVector(\n                  TestData::kPermutationProductLastEvals[0])),\n              CreateOptionalEvals(base::ArrayToVector(\n                  TestData::kPermutationProductLastEvals[1])),\n          };\n      EXPECT_EQ(proof.permutation_product_last_evals_vec,\n                expected_permutation_product_last_evals_vec);\n    } else {\n      EXPECT_TRUE(proof.permutation_product_last_evals_vec[0].empty());\n    }\n\n    if constexpr (TestData::kLookupProductEvalsFlag) {\n      std::vector<std::vector<F>> expected_lookup_product_evals_vec{\n          CreateEvals(base::ArrayToVector(TestData::kLookupProductEvals[0])),\n          CreateEvals(base::ArrayToVector(TestData::kLookupProductEvals[1])),\n      };\n      EXPECT_EQ(proof.lookup_product_evals_vec,\n                expected_lookup_product_evals_vec);\n    } else if constexpr (TestData::kLookupSumEvalsFlag) {\n      std::vector<std::vector<F>> expected_lookup_sum_evals_vec{\n          CreateEvals(base::ArrayToVector(TestData::kLookupSumEvals[0])),\n          CreateEvals(base::ArrayToVector(TestData::kLookupSumEvals[1])),\n      };\n      EXPECT_EQ(proof.lookup_sum_evals_vec, expected_lookup_sum_evals_vec);\n    }\n\n    if constexpr (TestData::kLookupProductNextEvalsFlag) {\n      std::vector<std::vector<F>> expected_lookup_product_next_evals_vec{\n          CreateEvals(\n              base::ArrayToVector(TestData::kLookupProductNextEvals[0])),\n          CreateEvals(\n              base::ArrayToVector(TestData::kLookupProductNextEvals[1])),\n      };\n      EXPECT_EQ(proof.lookup_product_next_evals_vec,\n                expected_lookup_product_next_evals_vec);\n    } else if constexpr (TestData::kLookupSumNextEvalsFlag) {\n      std::vector<std::vector<F>> expected_lookup_sum_next_evals_vec{\n          CreateEvals(base::ArrayToVector(TestData::kLookupSumNextEvals[0])),\n          CreateEvals(base::ArrayToVector(TestData::kLookupSumNextEvals[1])),\n      };\n      EXPECT_EQ(proof.lookup_sum_next_evals_vec,\n                expected_lookup_sum_next_evals_vec);\n    }\n\n    if constexpr (TestData::kLookupPermutedInputEvalsFlag) {\n      std::vector<std::vector<F>> expected_lookup_permuted_input_evals_vec{\n          CreateEvals(\n              base::ArrayToVector(TestData::kLookupPermutedInputEvals[0])),\n          CreateEvals(\n              base::ArrayToVector(TestData::kLookupPermutedInputEvals[1])),\n      };\n      EXPECT_EQ(proof.lookup_permuted_input_evals_vec,\n                expected_lookup_permuted_input_evals_vec);\n    }\n\n    if constexpr (TestData::kLookupPermutedInputPrevEvalsFlag) {\n      std::vector<std::vector<F>> expected_lookup_permuted_input_prev_evals_vec{\n          CreateEvals(\n              base::ArrayToVector(TestData::kLookupPermutedInputPrevEvals[0])),\n          CreateEvals(\n              base::ArrayToVector(TestData::kLookupPermutedInputPrevEvals[1])),\n      };\n      EXPECT_EQ(proof.lookup_permuted_input_prev_evals_vec,\n                expected_lookup_permuted_input_prev_evals_vec);\n    }\n\n    if constexpr (TestData::kLookupPermutedTableEvalsFlag) {\n      std::vector<std::vector<F>> expected_lookup_permuted_table_evals_vec{\n          CreateEvals(\n              base::ArrayToVector(TestData::kLookupPermutedTableEvals[0])),\n          CreateEvals(\n              base::ArrayToVector(TestData::kLookupPermutedTableEvals[1])),\n      };\n      EXPECT_EQ(proof.lookup_permuted_table_evals_vec,\n                expected_lookup_permuted_table_evals_vec);\n    }\n\n    if constexpr (TestData::kLookupMEvalsFlag) {\n      std::vector<std::vector<F>> expected_lookup_m_evals_vec{\n          CreateEvals(base::ArrayToVector(TestData::kLookupMEvals[0])),\n          CreateEvals(base::ArrayToVector(TestData::kLookupMEvals[1])),\n      };\n      EXPECT_EQ(proof.lookup_m_evals_vec, expected_lookup_m_evals_vec);\n    }\n\n    if constexpr (TestData::kShuffleProductEvalsFlag) {\n      std::vector<std::vector<F>> expected_shuffle_product_evals_vec{\n          CreateEvals(base::ArrayToVector(TestData::kShuffleProductEvals[0])),\n          CreateEvals(base::ArrayToVector(TestData::kShuffleProductEvals[1])),\n      };\n      EXPECT_EQ(proof.shuffle_product_evals_vec,\n                expected_shuffle_product_evals_vec);\n    } else {\n      EXPECT_TRUE(proof.shuffle_product_evals_vec[0].empty());\n    }\n\n    if constexpr (TestData::kShuffleProductNextEvalsFlag) {\n      std::vector<std::vector<F>> expected_shuffle_product_next_evals_vec{\n          CreateEvals(\n              base::ArrayToVector(TestData::kShuffleProductNextEvals[0])),\n          CreateEvals(\n              base::ArrayToVector(TestData::kShuffleProductNextEvals[1])),\n      };\n      EXPECT_EQ(proof.shuffle_product_next_evals_vec,\n                expected_shuffle_product_next_evals_vec);\n    } else {\n      EXPECT_TRUE(proof.shuffle_product_next_evals_vec[0].empty());\n    }\n\n    // TODO(ashjeong): get |h_eval| for fibonacci tests\n    if constexpr (!IsFibonacci<Circuit>) {\n      F expected_h_eval = *F::FromHexString(TestData::kHEval);\n      EXPECT_EQ(h_eval, expected_h_eval);\n    }\n  }\n\n private:\n  static Commitment CreateCommitment(const Point& point) {\n    using BaseField = typename Commitment::BaseField;\n    return Commitment(*BaseField::FromHexString(point.x),\n                      *BaseField::FromHexString(point.y));\n  }\n\n  static std::vector<Commitment> CreateCommitments(\n      const std::vector<Point>& points) {\n    return base::Map(points, &CreateCommitment);\n  }\n\n  static std::vector<lookup::Pair<Commitment>> CreateLookupPermutedCommitments(\n      const std::vector<Point>& input_points,\n      const std::vector<Point>& table_points) {\n    std::vector<lookup::Pair<Commitment>> lookup_pairs;\n    return base::Map(\n        input_points, [&table_points](size_t i, const Point& input_point) {\n          return lookup::Pair<Commitment>(CreateCommitment(input_point),\n                                          CreateCommitment(table_points[i]));\n        });\n  }\n\n  static Evals CreateColumn(const std::vector<std::string_view>& column) {\n    std::vector<F> evaluations = base::Map(column, [](std::string_view coeff) {\n      return *F::FromHexString(coeff);\n    });\n    return Evals(std::move(evaluations));\n  }\n\n  static std::vector<Evals> CreateColumns(\n      const std::vector<std::vector<std::string_view>>& columns) {\n    return base::Map(columns, &CreateColumn);\n  }\n\n  static RationalEvals CreateRationalColumn(\n      const std::vector<std::string_view>& column) {\n    std::vector<math::RationalField<F>> evaluations =\n        base::Map(column, [](std::string_view coeff) {\n          return math::RationalField<F>(*F::FromHexString(coeff));\n        });\n    return RationalEvals(std::move(evaluations));\n  }\n\n  static std::vector<RationalEvals> CreateRationalColumns(\n      const std::vector<std::vector<std::string_view>>& columns) {\n    return base::Map(columns, &CreateRationalColumn);\n  }\n\n  static Poly CreatePoly(const std::vector<std::string_view>& poly) {\n    std::vector<F> coefficients = base::Map(\n        poly, [](std::string_view coeff) { return *F::FromHexString(coeff); });\n    return Poly(math::UnivariateDenseCoefficients<F, halo2::kMaxDegree>(\n        std::move(coefficients), true));\n  }\n\n  static std::vector<Poly> CreatePolys(\n      const std::vector<std::vector<std::string_view>>& polys) {\n    return base::Map(polys, &CreatePoly);\n  }\n\n  static std::vector<F> CreateEvals(\n      const std::vector<std::string_view>& evals) {\n    return base::Map(\n        evals, [](std::string_view eval) { return *F::FromHexString(eval); });\n  }\n\n  static std::vector<std::optional<F>> CreateOptionalEvals(\n      const std::vector<std::string_view>& evals) {\n    return base::Map(evals, [](std::string_view eval) {\n      if (eval.empty()) return std::optional<F>();\n      return F::FromHexString(eval);\n    });\n  }\n\n  static base::Buffer CreateBufferWithProof(absl::Span<uint8_t> proof) {\n    return {proof.data(), proof.size()};\n  }\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_EXAMPLES_CIRCUIT_TEST_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/examples/circuit_test_data.h",
    "content": "#ifndef TACHYON_ZK_PLONK_EXAMPLES_CIRCUIT_TEST_DATA_H_\n#define TACHYON_ZK_PLONK_EXAMPLES_CIRCUIT_TEST_DATA_H_\n\n#include <string_view>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/range.h\"\n#include \"tachyon/zk/plonk/examples/point.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename Circuit, typename PS>\nclass CircuitTestData {\n public:\n  using PCS = typename PS::PCS;\n  using Evals = typename PCS::Evals;\n  using RationalEvals = typename PCS::RationalEvals;\n\n  constexpr static bool kAssemblyFixedColumnsFlag = false;\n  constexpr static bool kAssemblyPermutationColumnsFlag = false;\n  constexpr static bool kCycleStoreMappingFlag = false;\n  constexpr static bool kCycleStoreAuxFlag = false;\n  constexpr static bool kCycleStoreSizesFlag = false;\n  constexpr static bool kLFirstFlag = false;\n  constexpr static bool kLLastFlag = false;\n  constexpr static bool kLActiveRowFlag = false;\n  constexpr static bool kFixedColumnsFlag = false;\n  constexpr static bool kFixedPolysFlag = false;\n  constexpr static bool kPermutationsColumnsFlag = false;\n  constexpr static bool kPermutationsPolysFlag = false;\n  constexpr static bool kAdviceCommitmentsFlag = false;\n  constexpr static bool kChallengesFlag = false;\n  constexpr static bool kLookupPermutedCommitmentsFlag = false;\n  constexpr static bool kLookupMPolyCommitmentsFlag = false;\n  constexpr static bool kPermutationProductCommitmentsFlag = false;\n  constexpr static bool kLookupProductCommitmentsFlag = false;\n  constexpr static bool kLookupSumCommitmentsFlag = false;\n  constexpr static bool kShuffleProductCommitmentsFlag = false;\n  constexpr static bool kAdviceEvalsFlag = false;\n  constexpr static bool kFixedEvalsFlag = false;\n  constexpr static bool kCommonPermutationEvalsFlag = false;\n  constexpr static bool kPermutationProductEvalsFlag = false;\n  constexpr static bool kPermutationProductNextEvalsFlag = false;\n  constexpr static bool kPermutationProductLastEvalsFlag = false;\n  constexpr static bool kLookupProductEvalsFlag = false;\n  constexpr static bool kLookupSumEvalsFlag = false;\n  constexpr static bool kLookupProductNextEvalsFlag = false;\n  constexpr static bool kLookupSumNextEvalsFlag = false;\n  constexpr static bool kLookupPermutedInputEvalsFlag = false;\n  constexpr static bool kLookupPermutedInputPrevEvalsFlag = false;\n  constexpr static bool kLookupPermutedTableEvalsFlag = false;\n  constexpr static bool kLookupMEvalsFlag = false;\n  constexpr static bool kShuffleProductEvalsFlag = false;\n  constexpr static bool kShuffleProductNextEvalsFlag = false;\n\n  constexpr static base::Range<RowIndex> kUsableRows =\n      base::Range<RowIndex>::Until(10);\n  constexpr static Point kVanishingRandomPolyCommitment{\n      \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n      \"0x0000000000000000000000000000000000000000000000000000000000000002\"};\n  constexpr static std::string_view kVanishingRandomEval =\n      \"0x0000000000000000000000000000000000000000000000000000000000000001\";\n\n  static Circuit GetCircuit() { return Circuit(); }\n\n  static std::vector<Circuit> Get2Circuits() {\n    Circuit circuit = GetCircuit();\n    return {circuit, std::move(circuit)};\n  }\n\n  static std::vector<RationalEvals> GetFixedColumns() { return {}; }\n\n  static std::vector<Evals> GetInstanceColumns() { return {}; }\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_EXAMPLES_CIRCUIT_TEST_DATA_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/examples/circuit_test_type_traits.h",
    "content": "#ifndef TACHYON_ZK_PLONK_EXAMPLES_CIRCUIT_TEST_TYPE_TRAITS_H_\n#define TACHYON_ZK_PLONK_EXAMPLES_CIRCUIT_TEST_TYPE_TRAITS_H_\n\n#include <type_traits>\n\n#include \"tachyon/math/elliptic_curves/bn/bn254/bn254.h\"\n#include \"tachyon/zk/base/commitments/gwc_extension.h\"\n#include \"tachyon/zk/base/commitments/shplonk_extension.h\"\n#include \"tachyon/zk/plonk/examples/fibonacci/fibonacci1_circuit.h\"\n#include \"tachyon/zk/plonk/examples/fibonacci/fibonacci2_circuit.h\"\n#include \"tachyon/zk/plonk/examples/fibonacci/fibonacci3_circuit.h\"\n#include \"tachyon/zk/plonk/halo2/prover_test.h\"\n#include \"tachyon/zk/plonk/halo2/proving_scheme.h\"\n#include \"tachyon/zk/plonk/layout/floor_planner/simple_floor_planner.h\"\n#include \"tachyon/zk/plonk/layout/floor_planner/v1/v1_floor_planner.h\"\n\nnamespace tachyon::zk::plonk {\n\nusing BN254SHPlonk =\n    SHPlonkExtension<math::bn254::BN254Curve, halo2::kMaxDegree,\n                     halo2::kMaxExtendedDegree, math::bn254::G1AffinePoint>;\nusing BN254GWC =\n    GWCExtension<math::bn254::BN254Curve, halo2::kMaxDegree,\n                 halo2::kMaxExtendedDegree, math::bn254::G1AffinePoint>;\n\nusing BN254SHPlonkHalo2 =\n    halo2::ProvingScheme<halo2::Vendor::kScroll, lookup::Type::kHalo2,\n                         BN254SHPlonk>;\nusing BN254SHPlonkLogDerivativeHalo2 =\n    halo2::ProvingScheme<halo2::Vendor::kScroll,\n                         lookup::Type::kLogDerivativeHalo2, BN254SHPlonk>;\nusing BN254GWCHalo2 = halo2::ProvingScheme<halo2::Vendor::kScroll,\n                                           lookup::Type::kHalo2, BN254GWC>;\nusing BN254GWCLogDerivativeHalo2 =\n    halo2::ProvingScheme<halo2::Vendor::kScroll,\n                         lookup::Type::kLogDerivativeHalo2, BN254GWC>;\n\ntemplate <typename Circuit>\nconstexpr bool IsSimpleFloorPlanner =\n    std::is_same_v<typename Circuit::FloorPlanner, SimpleFloorPlanner<Circuit>>;\n\ntemplate <typename Circuit>\nconstexpr bool IsV1FloorPlanner =\n    std::is_same_v<typename Circuit::FloorPlanner, V1FloorPlanner<Circuit>>;\n\ntemplate <typename T>\nstruct IsSHPlonkImpl {\n  static constexpr bool value = false;\n};\n\ntemplate <typename Curve, size_t MaxDegree, size_t ExtendedMaxDegree,\n          typename Commitment>\nstruct IsSHPlonkImpl<\n    SHPlonkExtension<Curve, MaxDegree, ExtendedMaxDegree, Commitment>> {\n  static constexpr bool value = true;\n};\n\ntemplate <typename PCS>\nconstexpr bool IsSHPlonk = IsSHPlonkImpl<PCS>::value;\n\ntemplate <typename T>\nstruct IsGWCImpl {\n  static constexpr bool value = false;\n};\n\ntemplate <typename Curve, size_t MaxDegree, size_t ExtendedMaxDegree,\n          typename Commitment>\nstruct IsGWCImpl<\n    GWCExtension<Curve, MaxDegree, ExtendedMaxDegree, Commitment>> {\n  static constexpr bool value = true;\n};\n\ntemplate <typename PCS>\nconstexpr bool IsGWC = IsGWCImpl<PCS>::value;\n\ntemplate <typename T>\nstruct IsFibonacciImpl {\n  static constexpr bool value = false;\n};\n\ntemplate <typename F, template <typename> class FloorPlanner>\nstruct IsFibonacciImpl<Fibonacci1Circuit<F, FloorPlanner>> {\n  static constexpr bool value = true;\n};\n\ntemplate <typename F, template <typename> class FloorPlanner>\nstruct IsFibonacciImpl<Fibonacci2Circuit<F, FloorPlanner>> {\n  static constexpr bool value = true;\n};\n\ntemplate <typename F, template <typename> class FloorPlanner>\nstruct IsFibonacciImpl<Fibonacci3Circuit<F, FloorPlanner>> {\n  static constexpr bool value = true;\n};\n\ntemplate <typename T>\nconstexpr bool IsFibonacci = IsFibonacciImpl<T>::value;\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_EXAMPLES_CIRCUIT_TEST_TYPE_TRAITS_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/examples/fibonacci/BUILD.bazel",
    "content": "load(\n    \"//bazel:tachyon_cc.bzl\",\n    \"tachyon_cc_library\",\n    \"tachyon_cc_unittest\",\n    \"tachyon_openmp_num_threads_env\",\n)\n\npackage(default_visibility = [\"//visibility:public\"])\n\nCOMMON_TEST_DATA_DEPS = [\n    \"//tachyon/zk/plonk/examples:circuit_test_data\",\n    \"//tachyon/zk/plonk/examples:circuit_test_type_traits\",\n    \"//tachyon/zk/plonk/examples:point\",\n]\n\nCOMMON_TEST_DEPS = [\n    \"//tachyon/zk/plonk/examples:circuit_test\",\n    \"//tachyon/zk/plonk/layout/floor_planner:simple_floor_planner\",\n    \"//tachyon/zk/plonk/layout/floor_planner/v1:v1_floor_planner\",\n]\n\ntachyon_cc_library(\n    name = \"fibonacci1_circuit\",\n    hdrs = [\"fibonacci1_circuit.h\"],\n    deps = [\"//tachyon/zk/plonk/constraint_system:circuit\"],\n)\n\ntachyon_cc_library(\n    name = \"fibonacci1_circuit_test_data\",\n    testonly = True,\n    hdrs = [\"fibonacci1_circuit_test_data.h\"],\n    deps = COMMON_TEST_DATA_DEPS,\n)\n\ntachyon_cc_library(\n    name = \"fibonacci2_circuit\",\n    hdrs = [\"fibonacci2_circuit.h\"],\n    deps = [\"//tachyon/zk/plonk/constraint_system:circuit\"],\n)\n\ntachyon_cc_library(\n    name = \"fibonacci2_circuit_test_data\",\n    testonly = True,\n    hdrs = [\"fibonacci2_circuit_test_data.h\"],\n    deps = COMMON_TEST_DATA_DEPS,\n)\n\ntachyon_cc_library(\n    name = \"fibonacci3_circuit\",\n    hdrs = [\"fibonacci3_circuit.h\"],\n    deps = [\n        \":is_zero_chip\",\n        \"//tachyon/zk/plonk/constraint_system:circuit\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"fibonacci3_circuit_test_data\",\n    testonly = True,\n    hdrs = [\"fibonacci3_circuit_test_data.h\"],\n    deps = COMMON_TEST_DATA_DEPS,\n)\n\ntachyon_cc_library(\n    name = \"is_zero_chip\",\n    hdrs = [\"is_zero_chip.h\"],\n    deps = [\n        \"//tachyon/zk/expressions:expression_factory\",\n        \"//tachyon/zk/plonk/constraint_system\",\n        \"//tachyon/zk/plonk/layout:region\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"fibonacci1_circuit_test\",\n    srcs = [\"fibonacci1_circuit_test.cc\"],\n    env = tachyon_openmp_num_threads_env(4),\n    tags = [\"gpu_heavy_test\"],\n    deps = COMMON_TEST_DEPS + [\n        \"fibonacci1_circuit_test_data\",\n        \":fibonacci1_circuit\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"fibonacci2_circuit_test\",\n    srcs = [\"fibonacci2_circuit_test.cc\"],\n    env = tachyon_openmp_num_threads_env(4),\n    tags = [\"gpu_heavy_test\"],\n    deps = COMMON_TEST_DEPS + [\n        \"fibonacci2_circuit_test_data\",\n        \":fibonacci2_circuit\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"fibonacci3_circuit_test\",\n    srcs = [\"fibonacci3_circuit_test.cc\"],\n    env = tachyon_openmp_num_threads_env(4),\n    tags = [\"gpu_heavy_test\"],\n    deps = COMMON_TEST_DEPS + [\n        \"fibonacci3_circuit_test_data\",\n        \":fibonacci3_circuit\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/zk/plonk/examples/fibonacci/README.md",
    "content": "# Fibonacci Circuits Test\n\n## Acknowledgements\n\nThe three circuits and single chip originally come from the [halo2-examples](https://github.com/icemelon/halo2-examples/tree/6b8676487635169367e085a44e3b73c56290883f/src/fibonacci) repository by [icemelon](https://github.com/icemelon), demonstrating four different circuit designs for the Fibonacci sequence. `example4.rs` had not been implemented as it requires a larger evaluation domain for testing.\n\n## License\n\nAs of [#279](https://github.com/kroma-network/tachyon/pull/279), the [halo2-examples](https://github.com/icemelon/halo2-examples/tree/6b8676487635169367e085a44e3b73c56290883f/src/fibonacci) repository does not specify a license. Consequently, the legal permissions for using, modifying, and distributing this code are unclear. So please consider the licensing status of this code as pending.\n"
  },
  {
    "path": "tachyon/zk/plonk/examples/fibonacci/fibonacci1_circuit.h",
    "content": "#ifndef TACHYON_ZK_PLONK_EXAMPLES_FIBONACCI_FIBONACCI1_CIRCUIT_H_\n#define TACHYON_ZK_PLONK_EXAMPLES_FIBONACCI_FIBONACCI1_CIRCUIT_H_\n\n#include <array>\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/zk/plonk/constraint_system/circuit.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F>\nstruct AssignedRow {\n  AssignedCell<F> a;\n  AssignedCell<F> b;\n  AssignedCell<F> c;\n\n  AssignedRow() = default;\n  AssignedRow(AssignedCell<F>&& a, AssignedCell<F>&& b, AssignedCell<F>&& c)\n      : a(std::move(a)), b(std::move(b)), c(std::move(c)) {}\n};\n\ntemplate <typename F>\nstruct Fibonacci1Config {\n  using Field = F;\n\n  Fibonacci1Config(std::array<AdviceColumnKey, 3> advice, Selector selector,\n                   const InstanceColumnKey& instance)\n      : advice(advice), selector(selector), instance(instance) {}\n\n  Fibonacci1Config Clone() const { return {advice, selector, instance}; }\n\n  std::array<AdviceColumnKey, 3> advice;\n  Selector selector;\n  InstanceColumnKey instance;\n};\n\ntemplate <typename F>\nclass Fibonacci1Chip {\n public:\n  explicit Fibonacci1Chip(const Fibonacci1Config<F>& config)\n      : config_(config) {}\n\n  static Fibonacci1Config<F> Configure(ConstraintSystem<F>& meta) {\n    std::array<AdviceColumnKey, 3> advice = {\n        meta.CreateAdviceColumn(),\n        meta.CreateAdviceColumn(),\n        meta.CreateAdviceColumn(),\n    };\n    Selector selector = meta.CreateSimpleSelector();\n    InstanceColumnKey instance = meta.CreateInstanceColumn();\n\n    for (const AdviceColumnKey& column : advice) {\n      meta.EnableEquality(column);\n    }\n    meta.EnableEquality(instance);\n\n    meta.CreateGate(\"add\", [selector, &advice](VirtualCells<F>& meta) {\n      //\n      // advice[0] | advice[1] | advice[2] | selector\n      //    a           b           c           s\n      //\n      std::unique_ptr<Expression<F>> s = meta.QuerySelector(selector);\n      std::unique_ptr<Expression<F>> a =\n          meta.QueryAdvice(advice[0], Rotation::Cur());\n      std::unique_ptr<Expression<F>> b =\n          meta.QueryAdvice(advice[1], Rotation::Cur());\n      std::unique_ptr<Expression<F>> c =\n          meta.QueryAdvice(advice[2], Rotation::Cur());\n\n      std::vector<Constraint<F>> constraints;\n      constraints.emplace_back(std::move(s) *\n                               (std::move(a) + std::move(b) - std::move(c)));\n      return constraints;\n    });\n\n    return Fibonacci1Config<F>(std::move(advice), selector,\n                               std::move(instance));\n  }\n\n  AssignedRow<F> AssignFirstRow(Layouter<F>* layouter) const {\n    AssignedRow<F> ret;\n    layouter->AssignRegion(\"first row\", [this, &ret](Region<F>& region) {\n      config_.selector.Enable(region, 0);\n\n      AssignedCell<F> a_cell = region.AssignAdviceFromInstance(\n          \"f(0)\", config_.instance, 0, config_.advice[0], 0);\n      AssignedCell<F> b_cell = region.AssignAdviceFromInstance(\n          \"f(1)\", config_.instance, 1, config_.advice[1], 0);\n      AssignedCell<F> c_cell = region.AssignAdvice(\n          \"a + b\", config_.advice[2], 0,\n          [&a_cell, &b_cell]() { return a_cell.value() + b_cell.value(); });\n\n      ret = AssignedRow<F>(std::move(a_cell), std::move(b_cell),\n                           std::move(c_cell));\n    });\n    return ret;\n  }\n\n  AssignedCell<F> AssignRow(Layouter<F>* layouter,\n                            const AssignedCell<F>& prev_b,\n                            const AssignedCell<F>& prev_c) const {\n    AssignedCell<F> ret;\n    layouter->AssignRegion(\n        \"next row\", [this, &ret, &prev_b, &prev_c](Region<F>& region) {\n          config_.selector.Enable(region, 0);\n\n          // Copy the value from b & c in previous row to a & b in current row\n          const AssignedCell<F> a_cell =\n              prev_b.CopyAdvice(\"a\", region, config_.advice[0], 0);\n          const AssignedCell<F> b_cell =\n              prev_c.CopyAdvice(\"b\", region, config_.advice[1], 0);\n\n          const AssignedCell<F> c_cell = region.AssignAdvice(\n              \"a + b\", config_.advice[2], 0,\n              [&a_cell, &b_cell]() { return a_cell.value() + b_cell.value(); });\n\n          ret = std::move(c_cell);\n        });\n    return ret;\n  }\n\n  void ExposePublic(Layouter<F>* layouter, const AssignedCell<F>& cell,\n                    RowIndex row) const {\n    layouter->ConstrainInstance(cell.cell(), config_.instance, row);\n  }\n\n private:\n  Fibonacci1Config<F> config_;\n};\n\ntemplate <typename F, template <typename> class _FloorPlanner>\nclass Fibonacci1Circuit : public Circuit<Fibonacci1Config<F>> {\n public:\n  using FloorPlanner = _FloorPlanner<Fibonacci1Circuit<F, _FloorPlanner>>;\n\n  std::unique_ptr<Circuit<Fibonacci1Config<F>>> WithoutWitness()\n      const override {\n    return std::make_unique<Fibonacci1Circuit>();\n  }\n\n  static Fibonacci1Config<F> Configure(ConstraintSystem<F>& meta) {\n    return Fibonacci1Chip<F>::Configure(meta);\n  }\n\n  void Synthesize(Fibonacci1Config<F>&& config,\n                  Layouter<F>* layouter) const override {\n    Fibonacci1Chip<F> fibonacci1_chip(std::move(config));\n\n    AssignedRow<F> first_row =\n        fibonacci1_chip.AssignFirstRow(layouter->Namespace(\"first row\").get());\n\n    AssignedCell<F> prev_b = first_row.b;\n    AssignedCell<F> prev_c = first_row.c;\n\n    for (RowIndex i = 3; i < 10; ++i) {\n      AssignedCell<F> c_cell = fibonacci1_chip.AssignRow(\n          layouter->Namespace(\"next row\").get(), prev_b, prev_c);\n      prev_b = prev_c;\n      prev_c = c_cell;\n    }\n\n    fibonacci1_chip.ExposePublic(layouter->Namespace(\"out\").get(), prev_c, 2);\n  }\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_EXAMPLES_FIBONACCI_FIBONACCI1_CIRCUIT_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/examples/fibonacci/fibonacci1_circuit_test.cc",
    "content": "#include \"tachyon/zk/plonk/examples/fibonacci/fibonacci1_circuit.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/zk/plonk/examples/circuit_test.h\"\n#include \"tachyon/zk/plonk/examples/fibonacci/fibonacci1_circuit_test_data.h\"\n#include \"tachyon/zk/plonk/layout/floor_planner/simple_floor_planner.h\"\n#include \"tachyon/zk/plonk/layout/floor_planner/v1/v1_floor_planner.h\"\n\nnamespace tachyon::zk::plonk {\n\nnamespace {\n\ntemplate <typename TestArguments>\nclass Fibonacci1CircuitTest\n    : public CircuitTest<TestArguments,\n                         Fibonacci1TestData<typename TestArguments::Circuit,\n                                            typename TestArguments::PS>> {};\n\n}  // namespace\n\nusing Fibonacci1TestArgumentsList = testing::Types<\n    TestArguments<Fibonacci1Circuit<BN254SHPlonk::Field, SimpleFloorPlanner>,\n                  BN254SHPlonkHalo2>,\n    TestArguments<Fibonacci1Circuit<BN254SHPlonk::Field, V1FloorPlanner>,\n                  BN254SHPlonkHalo2>>;\n\nTYPED_TEST_SUITE(Fibonacci1CircuitTest, Fibonacci1TestArgumentsList);\n\nTYPED_TEST(Fibonacci1CircuitTest, Configure) { this->ConfigureTest(); }\nTYPED_TEST(Fibonacci1CircuitTest, Synthesize) { this->SynthesizeTest(); }\nTYPED_TEST(Fibonacci1CircuitTest, LoadVerifyingKey) {\n  this->LoadVerifyingKeyTest();\n}\nTYPED_TEST(Fibonacci1CircuitTest, LoadProvingKey) {\n  this->LoadProvingKeyTest();\n}\nTYPED_TEST(Fibonacci1CircuitTest, CreateProof) { this->CreateProofTest(); }\nTYPED_TEST(Fibonacci1CircuitTest, VerifyProof) { this->VerifyProofTest(); }\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/examples/fibonacci/fibonacci1_circuit_test_data.h",
    "content": "#ifndef TACHYON_ZK_PLONK_EXAMPLES_FIBONACCI_FIBONACCI1_CIRCUIT_TEST_DATA_H_\n#define TACHYON_ZK_PLONK_EXAMPLES_FIBONACCI_FIBONACCI1_CIRCUIT_TEST_DATA_H_\n\n#include <stdint.h>\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/zk/plonk/examples/circuit_test_data.h\"\n#include \"tachyon/zk/plonk/examples/circuit_test_type_traits.h\"\n#include \"tachyon/zk/plonk/examples/point.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename Circuit, typename PS, typename SFINAE = void>\nclass Fibonacci1TestData : public CircuitTestData<Circuit, PS> {};\n\n// FloorPlanner = SimpleFloorPlanner\ntemplate <typename Circuit, typename PS>\nclass Fibonacci1TestData<Circuit, PS,\n                         std::enable_if_t<IsSimpleFloorPlanner<Circuit>>>\n    : public CircuitTestData<Circuit, PS> {\n public:\n  using PCS = typename PS::PCS;\n  using F = typename PCS::Field;\n  using Evals = typename PCS::Evals;\n\n  constexpr static bool kAssemblyPermutationColumnsFlag = true;\n  constexpr static bool kCycleStoreMappingFlag = true;\n  constexpr static bool kCycleStoreAuxFlag = true;\n  constexpr static bool kCycleStoreSizesFlag = true;\n  constexpr static bool kLFirstFlag = true;\n  constexpr static bool kLLastFlag = true;\n  constexpr static bool kLActiveRowFlag = true;\n  constexpr static bool kFixedColumnsFlag = true;\n  constexpr static bool kFixedPolysFlag = true;\n  constexpr static bool kPermutationsColumnsFlag = true;\n  constexpr static bool kPermutationsPolysFlag = true;\n  constexpr static bool kAdviceCommitmentsFlag = true;\n  constexpr static bool kPermutationProductCommitmentsFlag = true;\n  constexpr static bool kAdviceEvalsFlag = true;\n  constexpr static bool kFixedEvalsFlag = true;\n  constexpr static bool kCommonPermutationEvalsFlag = true;\n  constexpr static bool kPermutationProductEvalsFlag = true;\n  constexpr static bool kPermutationProductNextEvalsFlag = true;\n  constexpr static bool kPermutationProductLastEvalsFlag = true;\n\n  constexpr static size_t kN = 16;\n\n  // clang-format off\n  constexpr static std::string_view kPinnedConstraintSystem =\n      \"PinnedConstraintSystem { \"\n        \"num_fixed_columns: 0, \"\n        \"num_advice_columns: 3, \"\n        \"num_instance_columns: 1, \"\n        \"num_selectors: 1, \"\n        \"gates: [Product(\"\n          \"Selector(Selector(0, true)), \"\n          \"Sum(\"\n            \"Sum(\"\n              \"Advice { \"\n                \"query_index: 0, \"\n                \"column_index: 0, \"\n                \"rotation: Rotation(0) \"\n              \"}, \"\n              \"Advice { \"\n                \"query_index: 1, \"\n                \"column_index: 1, \"\n                \"rotation: Rotation(0) \"\n              \"}\"\n            \"), \"\n            \"Negated(Advice { \"\n              \"query_index: 2, \"\n              \"column_index: 2, \"\n              \"rotation: Rotation(0) \"\n            \"})\"\n          \")\"\n        \")], \"\n        \"advice_queries: [\"\n          \"(\"\n            \"Column { \"\n              \"index: 0, \"\n              \"column_type: Advice \"\n            \"}, \"\n            \"Rotation(0)\"\n          \"), \"\n          \"(\"\n            \"Column { \"\n              \"index: 1, \"\n              \"column_type: Advice \"\n            \"}, \"\n            \"Rotation(0)\"\n          \"), \"\n          \"(\"\n            \"Column { \"\n              \"index: 2, \"\n              \"column_type: Advice \"\n            \"}, \"\n            \"Rotation(0)\"\n          \")\"\n        \"], \"\n        \"instance_queries: [(\"\n          \"Column { \"\n            \"index: 0, \"\n            \"column_type: Instance \"\n          \"}, \"\n          \"Rotation(0)\"\n        \")], \"\n        \"fixed_queries: [], \"\n        \"permutation: Argument { columns: [\"\n          \"Column { \"\n            \"index: 0, \"\n            \"column_type: Advice \"\n          \"}, \"\n          \"Column { \"\n            \"index: 1, \"\n            \"column_type: Advice \"\n          \"}, \"\n          \"Column { \"\n            \"index: 2, \"\n            \"column_type: Advice \"\n          \"}, \"\n          \"Column { \"\n            \"index: 0, \"\n            \"column_type: Instance \"\n          \"}\"\n        \"] }, \"\n        \"lookups: [], \"\n        \"constants: [], \"\n        \"minimum_degree: None \"\n      \"}\";\n  // clang-format on\n\n  constexpr static AnyColumnKey kAssemblyPermutationColumns[] = {\n      AdviceColumnKey(0),\n      AdviceColumnKey(1),\n      AdviceColumnKey(2),\n      InstanceColumnKey(0),\n  };\n\n  // clang-format off\n  constexpr static Label kCycleStoreMapping[][kN] = {\n      {{3, 0}, {3, 1}, {1, 1},  {1, 2},  {1, 3},  {1, 4},  {1, 5},  {1, 6},\n      {0, 8}, {0, 9}, {0, 10}, {0, 11}, {0, 12}, {0, 13}, {0, 14}, {0, 15}},\n      {{0, 1}, {2, 0}, {2, 1},  {2, 2},  {2, 3},  {2, 4},  {2, 5},  {2, 6},\n      {1, 8}, {1, 9}, {1, 10}, {1, 11}, {1, 12}, {1, 13}, {1, 14}, {1, 15}},\n      {{0, 2}, {0, 3}, {0, 4},  {0, 5},  {0, 6},  {0, 7},  {1, 7},  {3, 2},\n      {2, 8}, {2, 9}, {2, 10}, {2, 11}, {2, 12}, {2, 13}, {2, 14}, {2, 15}},\n      {{0, 0}, {1, 0}, {2, 7},  {3, 3},  {3, 4},  {3, 5},  {3, 6},  {3, 7},\n      {3, 8}, {3, 9}, {3, 10}, {3, 11}, {3, 12}, {3, 13}, {3, 14}, {3, 15}},\n  };\n\n  constexpr static Label kCycleStoreAux[][kN] = {\n      {{0, 0}, {1, 0},  {1, 1},  {1, 2},  {1, 3},  {1, 4},  {1, 5},  {1, 6},\n        {0, 8}, {0, 9}, {0, 10}, {0, 11}, {0, 12}, {0, 13}, {0, 14}, {0, 15}},\n      {{1, 0}, {1, 1},  {1, 2},  {1, 3},  {1, 4},  {1, 5},  {1, 6},  {1, 7},\n        {1, 8}, {1, 9}, {1, 10}, {1, 11}, {1, 12}, {1, 13}, {1, 14}, {1, 15}},\n      {{1, 1}, {1, 2},  {1, 3},  {1, 4},  {1, 5},  {1, 6},  {1, 7},  {2, 7},\n        {2, 8}, {2, 9}, {2, 10}, {2, 11}, {2, 12}, {2, 13}, {2, 14}, {2, 15}},\n      {{0, 0}, {1, 0},  {2, 7},  {3, 3},  {3, 4},  {3, 5},  {3, 6},  {3, 7},\n        {3, 8}, {3, 9}, {3, 10}, {3, 11}, {3, 12}, {3, 13}, {3, 14}, {3, 15}},\n  };\n  // clang-format on\n\n  constexpr static size_t kCycleStoreSizes[][kN] = {\n      {2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},\n      {3, 3, 3, 3, 3, 3, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1},\n      {1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1},\n      {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},\n  };\n\n  // clang-format off\n  constexpr static bool kSelectors[][kN] = {\n      {true,  true,  true,  true,  true,  true,  true,  true,\n       false, false, false, false, false, false, false, false}\n  };\n  // clang-format on\n\n  // clang-format off\n  constexpr static std::string_view kPinnedVerifyingKey =\n      \"PinnedVerificationKey { \"\n        \"base_modulus: \\\"0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47\\\", \"\n        \"scalar_modulus: \\\"0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001\\\", \"\n        \"domain: PinnedEvaluationDomain { \"\n          \"k: 4, \"\n          \"extended_k: 5, \"\n          \"omega: 0x21082ca216cbbf4e1c6e4f4594dd508c996dfbe1174efb98b11509c6e306460b \"\n        \"}, \"\n        \"cs: PinnedConstraintSystem { \"\n          \"num_fixed_columns: 1, \"\n          \"num_advice_columns: 3, \"\n          \"num_instance_columns: 1, \"\n          \"num_selectors: 1, \"\n          \"gates: [Product(\"\n            \"Fixed { \"\n              \"query_index: 0, \"\n              \"column_index: 0, \"\n              \"rotation: Rotation(0) \"\n            \"}, \"\n            \"Sum(\"\n              \"Sum(\"\n                \"Advice { \"\n                  \"query_index: 0, \"\n                  \"column_index: 0, \"\n                  \"rotation: Rotation(0) \"\n                \"}, \"\n                \"Advice { \"\n                  \"query_index: 1, \"\n                  \"column_index: 1, \"\n                  \"rotation: Rotation(0) \"\n                \"}\"\n              \"), \"\n              \"Negated(Advice { \"\n                \"query_index: 2, \"\n                \"column_index: 2, \"\n                \"rotation: Rotation(0) \"\n              \"})\"\n            \")\"\n          \")], \"\n          \"advice_queries: [\"\n            \"(\"\n              \"Column { \"\n                \"index: 0, \"\n                \"column_type: Advice \"\n              \"}, \"\n              \"Rotation(0)\"\n            \"), \"\n            \"(\"\n              \"Column { \"\n                \"index: 1, \"\n                \"column_type: Advice \"\n              \"}, \"\n              \"Rotation(0)\"\n            \"), \"\n            \"(\"\n              \"Column { \"\n                \"index: 2, \"\n                \"column_type: Advice \"\n              \"}, \"\n              \"Rotation(0)\"\n            \")\"\n          \"], \"\n          \"instance_queries: [(\"\n            \"Column { \"\n              \"index: 0, \"\n              \"column_type: Instance \"\n            \"}, \"\n            \"Rotation(0)\"\n          \")], \"\n          \"fixed_queries: [(\"\n            \"Column { \"\n              \"index: 0, \"\n              \"column_type: Fixed \"\n            \"}, \"\n            \"Rotation(0)\"\n          \")], \"\n          \"permutation: Argument { columns: [\"\n            \"Column { \"\n              \"index: 0, \"\n              \"column_type: Advice \"\n            \"}, \"\n            \"Column { \"\n              \"index: 1, \"\n              \"column_type: Advice \"\n            \"}, \"\n            \"Column { \"\n              \"index: 2, \"\n              \"column_type: Advice \"\n            \"}, \"\n            \"Column { \"\n              \"index: 0, \"\n              \"column_type: Instance \"\n            \"}\"\n          \"] }, \"\n          \"lookups: [], \"\n          \"constants: [], \"\n          \"minimum_degree: None \"\n        \"}, \"\n        \"fixed_commitments: [\"\n          \"(0x297ff8a661d1fa1196c065b6fb7df901fb16b5b83168ddca6c7749d963cc967a, \"\n            \"0x1a7b1f2b5f3e35fc4c706ece6b8f646e95904f9aa417f8a31fd37a41da167ec1)\"\n        \"], \"\n        \"permutation: VerifyingKey { commitments: [\"\n          \"(0x28472c5c287afbed2c1cbad418f7736db3be191e735e933e7531f790cc0b454b, \"\n            \"0x2553db7d81ee798baa1b89b83e3cfd5516872410ac1ef7ff8e97801a32f54b22), \"\n          \"(0x1881f5e53bde69bb20eec1af0e8b17f23f3e49cf6929f13417a5dc389f70acec, \"\n            \"0x19e1cd19519ba4353b95ba5a16a10479eec522952c97acd31199eb96b558b158), \"\n          \"(0x1a899815b7cb019ce89ac7da39c4c0e071bd042ebffe13965de4e9e1cb968e17, \"\n            \"0x1c2bc1e987ea49a29fa8a6e2168541a29fc27b0150b2b85f8bb2d76e1abd029b), \"\n          \"(0x02f5f4023c8be80b0fe6ff231b1c292527881597645688da1cbfe83be61bfdc9, \"\n            \"0x0eb912c0ac4f39a2e76c0fb7ac2d3977a4cc909db67680eb9e3c2436f4dc85ae)\"\n        \"] } \"\n      \"}\";\n  // clang-format on\n\n  constexpr static std::string_view kTranscriptRepr =\n      \"0x0e149c09b16d13bdc8a09508e1dab4af7399ebe708e0fc37a7fd59d43974596f\";\n\n  constexpr static std::string_view kLFirst[] = {\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n  };\n\n  constexpr static std::string_view kLLast[] = {\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2014447de15a99b6df03833e95f96ae1299c9ec6ff990b6e75fa3b3b04846a57\",\n      \"0x0f1f5883e65f820d14d56342dc92fd12a944d4cbbdce5377b7439bd07108fc9d\",\n      \"0x02b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e8\",\n      \"0x030644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000\",\n      \"0x105009f4ffd70672d94cc277eb87ed7bfe9749817a206522cde7ba58eb7b95aa\",\n      \"0x2144f5eefad21e1ca37ae273a4ee5b4a7eef137cbbeb1d198c9e59c37ef70364\",\n      \"0x2db11694c4a58b3789868bd388165969c30dc1120a37fff09a991abf43e42a19\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2014447de15a99b6df03833e95f96ae1299c9ec6ff990b6e75fa3b3b04846a57\",\n      \"0x0f1f5883e65f820d14d56342dc92fd12a944d4cbbdce5377b7439bd07108fc9d\",\n      \"0x02b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e8\",\n      \"0x030644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000\",\n      \"0x105009f4ffd70672d94cc277eb87ed7bfe9749817a206522cde7ba58eb7b95aa\",\n      \"0x2144f5eefad21e1ca37ae273a4ee5b4a7eef137cbbeb1d198c9e59c37ef70364\",\n      \"0x2db11694c4a58b3789868bd388165969c30dc1120a37fff09a991abf43e42a19\",\n  };\n\n  constexpr static std::string_view kLActiveRow[] = {\n      \"0x12259d6b14729c0fa51e1a2470908122ef13771b2da58a367974bc177a000001\",\n      \"0x1a8133201ba2fe22aff30c7fd04fadcb17ceab0715a13371b06d35acd9695b3d\",\n      \"0x0d49c50dd1c3ec703dc7be1c836fd7f62c140afcf284ce19b9a99affac7b95aa\",\n      \"0x117dae38a05bcb8c7c290ee16cec493d73ef8dafa5c61fa4a2efd9a39e63abf4\",\n      \"0x0c19139cb84c680a79505ee7747ae78cd6c196473632bc6ea3057c773208fc9d\",\n      \"0x136156e428a2662bc2fddfd3b39f6475dafecb8699a611f3da6edd22c3479af1\",\n      \"0x2aaad1ad96927134ee0187781ffe43e3f08a828d829c68e7865afb6604e42a19\",\n      \"0x28d7d254c17b7ea40ebc4659996adacebd0d8f52d021284040d407c2f33b896f\",\n      \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n      \"0x1ef7221577b1e8f8cebdcb2fcd10296b6d9d43267b395c820757c0fc87681d81\",\n      \"0x1d0dff96b3477fb4437e7ee32de1555b5719604277fd746561bc1be1c5846a57\",\n      \"0x1be193603aacb384678281793fa8f20b949cf5976d9493017ba8a357ee49da57\",\n      \"0x1e3eb107ccbf041a07f5de183cd645c4ac6bd4f8344f861078603a6a3ff70364\",\n      \"0x20080468beb85b16c9f71b3ea2ce10fb6cdc81c346721c888ebc9109900adec6\",\n      \"0x30114169cfaa9b194b94fb3e12d441cabad6d0fa619f4a28d8ecb10f5d1bd5e9\",\n      \"0x2edcc3ce4ec47abd9b83b31a4db9571236223b590c30997fd30ce24f7bf2fdd6\",\n  };\n\n  constexpr static std::string_view kFixedColumns[][kN] = {\n      {\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n      },\n  };\n\n  constexpr static std::string_view kFixedPolys[][kN] = {\n      {\n          \"0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000001\",\n          \"0x1db5b8e23dbf6d914e37d939eb6b037c619b3e5ea827cdb2b030fc247bdc9dcb\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x15612923e039d29887b96603ef6ab434a2c021b8674489fcfa35d3972944e837\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x186635fde662f3b15c5e1b5fc456d148c26786b9cda347e81f7f4c2f8c9761ed\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x14a1b21f8b9c269f87d74740cf8c84a3046338799106ae503c4dfb0f0b0b250e\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x15b61284f96f4584f96ef5bee1c4a8ae7eca32c5d97b942edf17bbd266f4daf3\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x11f18ea69ea8787324e8219fecfa5c08c0c5e4859cdefa96fbe66ab1e5689e14\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x14f69b80a4d1998bf98cd6fbc1e6791ce06d4987033db882212fe34a48bb17ca\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0ca20bc2474bfe93330e63c5c5e629d521922ce0c25a74cc6b34babcf6236236\",\n      },\n  };\n\n  constexpr static std::string_view kPermutationsColumns[][kN] = {\n      {\n          \"0x18afdf23e9bd9302673fc1e076a492d4d65bd18ebc4d854ed189139bab313e52\",\n          \"0x2f0e061e83e8b12c1bdf8df7cca02483295e89f78a462ce38859661e06501815\",\n          \"0x133f51f46c2a51201fbb89dc1d6868acfef0f7ce0e572ccb98e04e99eac3ea36\",\n          \"0x1240a374ee6f71e12df10a129946ca9ba824f58a50e3a58b4c68dd5590b74ad8\",\n          \"0x009553089042fe83ab570b54e4e64d307d8e8b20d568e782ece3926981d0a96c\",\n          \"0x14a6c152ace4b16a42e1377e400798e15ac60320c21d90277890dbdd551d1912\",\n          \"0x035992598be4d2ae5334f24f30355a1fca7f0e28762a178d79e993dca70eaabf\",\n          \"0x03b645319eb70d7a692ea8c87fbcab1f292cd502071d9ffb7291ebe28356aa07\",\n          \"0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000\",\n          \"0x0f5c21d0ca65e0db9be1f670eca407d08ec5ec67626a74f892ccebcd0cf9b9f6\",\n          \"0x0530d09118705106cbb4a786ead16926d5d174e181a26686af5448492e42a181\",\n          \"0x1fe9a328fad7382fddb3730a89f574d14e57caeac619eeb30d24fb38a4fc6fbe\",\n          \"0x0000000000000000b3c4d79d41a91758cb49c3517c4604a520cff123608fc9cb\",\n          \"0x0dd360411caed09700b52c71a6655715c4d558439e2d34f4307da9a4be13c42e\",\n          \"0x130b17119778465cfb3acaee30f81dee20710ead41671f568b11d9ab07b95a9b\",\n          \"0x02e40daf409556c02bfc85eb303402b774954d30aeb0337eb85a71e6373428de\",\n      },\n      {\n          \"0x21082ca216cbbf4e1c6e4f4594dd508c996dfbe1174efb98b11509c6e306460b\",\n          \"0x13b360d4e82fe915fed16081038f98c211427b87a281bd733c277dbadf10372b\",\n          \"0x08e7cbfea108224b0777f0558503af41585b75ab8d4d807505158f4bc8c771de\",\n          \"0x0b6f861977ce57ddb2e647048ed9b9433cac640ba0599e264e24446765d915d3\",\n          \"0x26f93d99832c6285d9abf8c5b896ea753ed137516972c5ddcc464b56c488d600\",\n          \"0x086398ace043cf0db4e99f2712f392ddbd9c7b9202b2fcadf26e9b145316df9f\",\n          \"0x04765b5102d6627cfcc389436e96bbc9aa478dcb720b546c77063077a40910ce\",\n          \"0x2e39c17d3e15071a9341cfcea233ad5f25a14dea28716de88ae369ac2c9944ac\",\n          \"0x2741e304be6aaf5f53641f0bacb8e9ebccd45eba1b23316bbcd39ed80acc165f\",\n          \"0x1d24fc7e75074f099894bbda6418efb02942f07a6b6243c5ab01a6fa053c15cb\",\n          \"0x1e23aafdf2c22e488a5f3ba3e83a8dc1800ef2be28d5cb05f779183e5f48b529\",\n          \"0x2fcefb6a50eea1a60cf93a619c9b0b2caaa55d27a450890e56fe632a6e2f5695\",\n          \"0x1bbd8d20344ceebf756f0e384179bf7bcd6de527b79be069cb5119b69ae2e6ef\",\n          \"0x2d0abc19554ccd7b651b5367514bfe3d5db4da20038f5903c9f861b748f15542\",\n          \"0x2cae0941427a92af4f219cee01c4ad3dff071346729bd095d15009b16ca955fa\",\n          \"0x0d4615fec1d5611cd5ffa9e358e64eb494829d350acf01c136f55acac8e3624c\",\n      },\n      {\n          \"0x2b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e80\",\n          \"0x107aab49e65a67f9da9cd2abf78be38bd9dc1d5db39f81de36bcfa5b4b039043\",\n          \"0x30644e72e131a029048b6e193fd841045cea24f6fd736bec231204708f703636\",\n          \"0x2290ee31c482cf92b79b1944db1c0147635e9004db8c3b9d13644bef31ec3bd3\",\n          \"0x1d59376149b959ccbd157ac850893a6f07c2d99b3852513ab8d01be8e846a566\",\n          \"0x2d8040c3a09c49698c53bfcb514d55a5b39e9b17cb093d128b8783adb8cbd723\",\n          \"0x231e38741f5c3f0ce2509bd3289b09a893b14b136eea6ed00cec9ac9271c9db5\",\n          \"0x034183d253b6b250dae0a457797029523434d2b6bc41c09b6ef409bd970e4208\",\n          \"0x1cb0ed9df901b713b97ee5357df1bf9b16f16cc0d737b31e07ba77d910efc8d6\",\n          \"0x277c827440297ddeb0d85560fc7da91bcfd8729cec6bf01c3ecc664827388e23\",\n          \"0x24f4c8596963484c0569feb1f2a79f19eb87843cd95fd26af5bdb12c8a26ea2e\",\n          \"0x096b10d95e053da3dea44cf0c8ea6de7e962b0f71046aab3779baa3d2b772a01\",\n          \"0x2800b5c600edd11c0366a68f6e8dc57f6a976cb6770673e351735a7f9ce92062\",\n          \"0x2bedf321de5b3dacbb8cbc7312ea9c937dec5a7d07ae1c24ccdbc51c4bf6ef33\",\n          \"0x022a8cf5a31c990f250e75e7df4daafe02929a5e514802a8b8fe8be7c366bb55\",\n          \"0x06272e83847dd219527e22d89b87a6efdf7fc2b0f32d564762853d937378875d\",\n      },\n      {\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x09226b6e22c6f0ca64ec26aad4c86e715b5f898e5e963f25870e56bbe533e9a2\",\n          \"0x2a3d1fef5cb3ce1065d222dde5f9b16d48b42597868c1a49e15cb8007c8778a4\",\n          \"0x0f34fda7cd268bf095354ea6c2631826c349d7518bf094361bc91f61c519c7fb\",\n          \"0x2f549305063b1803a77ba92486c5383e00468fc857ec66d944868c4a309e596a\",\n          \"0x1e626bf9ef3c8920522383ff9be21287c3af8d47fe61ff9af6c6d8d0118154bc\",\n          \"0x02898db49f7022cac0aeabf721a3c7ab96e14ad35e7d6b0410b868ff504b62fc\",\n          \"0x2a150ee7450ad90cace368ea424c8fdf36b3821e79f5f1c617a5f8e74c19b4cf\",\n          \"0x17b46f4ef7740d27511083d60adcc58851d816b9bd6beb427258e1f844cec1af\",\n          \"0x015648545d48eefd9c70b7beb4e133d9fed55e50ef7343adbb888f75e9afe7ec\",\n          \"0x2d22caa08d7aedd8dd6fa15f08112f0af3ff1591bd77aff5d4edebd658f1bdf9\",\n          \"0x212f50cb140b1439231af70fbf1e403664ea10f6edc8dc5b2818d6322ae63806\",\n          \"0x010fbb6ddaf6882610d49c91fabc201f27ed588021cd09b7ff5b6949bf61a697\",\n          \"0x1201e278f1f51709662cc1b6e59f45d564845b007b5770f64d1b1cc3de7eab45\",\n          \"0x2ddac0be41c17d5ef7a199bf5fdd90b191529d751b3c058d33298c949fb49d05\",\n          \"0x064f3f8b9c26c71d0b6cdccc3f34c87df1806629ffc37ecb2c3bfcaca3e64b32\",\n      },\n  };\n\n  constexpr static std::string_view kPermutationsPolys[][kN] = {\n      {\n          \"0x112272f91e39f0f3754b36d52fafaa32b73194639e9077aeeb8fd9eeb5c197f6\",\n          \"0x2c5b7efa2ecceeea67640c3a314bf22430a3635c16e265bf41b69cae74bb6ca8\",\n          \"0x2b63732376158eeb59affea24c42fe2b9f5f0ed91512e866a5010c4a422a2210\",\n          \"0x15ef7f2ee3cb842ed2df84652b6d941208ee251aa1d3d440fdef29ab9c91123d\",\n          \"0x1efdce0e537a38a6167bb190151b64ce84ad66c093e64acda84995afbad32c38\",\n          \"0x15fccfcbfbb7d19b3579da7e187ea1bfc00c071f5fcc688c162006b8fb3802c1\",\n          \"0x044d3c43a1d8bdf98d9ce3973799577b3cc148f71f05c1381c7b28a42b010bdb\",\n          \"0x2601b44a9ceaf52a979de420fd3c6a7d8ef043cd6b845368cf317809c7603844\",\n          \"0x2238916f0b1084c82c027898e274eb46c7d8ac0c96a2b245d563706526ea705e\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x2c8157f94657d88c59af021da365441045bc63e1b38b4487c91b519f9ff62533\",\n          \"0x27fb1c055aaf8a4205acd5be9a2be2e6718de3cb2c83c8e64a3dc0676bb8a945\",\n          \"0x14c116fa641eb6536a6e13a3830fb449d47d31606dd888bdd65630ee6be537d8\",\n          \"0x27edcb6842c33cd5a3127fa5ad1ad538ba7001c66e8b349b320ce35a0d11b8c1\",\n          \"0x22cf5fc5ab1490168dd5c1acb0870ec4a605e9ca6353518e5012c3677d12dfab\",\n          \"0x17e8e6e9a190194640ee7602c85d0c7aeb8bc51862d349be78fb720940e9833e\",\n      },\n      {\n          \"0x1f09a0f7ba26bdc402f8617328d38bce213bad6def415a22398d2727cbfda3c6\",\n          \"0x27918de28986445f3b86531a98ae3c84a22efadff737cb6654200067594f146b\",\n          \"0x0e4adbd349654830f53e209fe0803dc6c9b38c1bd257ffeb4719661b05bca7c8\",\n          \"0x07ba99b0454b12d883a90b7d9fc585a570a5609c117948673d653b75b4f8dbeb\",\n          \"0x05ee0abc9f39a24a6c0448af3b29cc5b70b02961d28a7c75c4bf5665b6e33de3\",\n          \"0x07ba99b0454b12d883a90b7d9fc585a570a5609c117948673d653b75b4f8dbeb\",\n          \"0x089d2cd7436de0d9bdd87214d79aebde9f5f670221c1f5bc7a6905fa720e17ff\",\n          \"0x07ba99b0454b12d883a90b7d9fc585a570a5609c117948673d653b75b4f8dbeb\",\n          \"0x2dc0c43913c4431c08ed1d329bd5dddd857f8aec88417ee833a16c5814c27120\",\n          \"0x07ba99b0454b12d883a90b7d9fc585a570a5609c117948673d653b75b4f8dbeb\",\n          \"0x2ff80704d0f96302a799aa96eeff44148da396e5b3114bb690e7167eacf7da6f\",\n          \"0x07ba99b0454b12d883a90b7d9fc585a570a5609c117948673d653b75b4f8dbeb\",\n          \"0x24b74ff3c01e251b3a5e0ffe31b8ddb64b8fd2879fd76451063d87485fac338f\",\n          \"0x07ba99b0454b12d883a90b7d9fc585a570a5609c117948673d653b75b4f8dbeb\",\n          \"0x2766720e645263aa8c323963ce29fd397a3f1027ef0edd97bbe736dd1ad70dab\",\n          \"0x07ba99b0454b12d883a90b7d9fc585a570a5609c117948673d653b75b4f8dbeb\",\n      },\n      {\n          \"0x19e45f96ed87e5016568eaf3d9ddfa979cde528c9a9f889ea6bca69314ae07a8\",\n          \"0x113ad62e5f1760833e3c623377b1da8c53b6df849f9d26ebb0dff4c13d89c129\",\n          \"0x06a1a39fdf7310f4c8bd3dc6ba194a8c2c064994f519c4ad0bab55e6044ec707\",\n          \"0x14ac062955c7bf10fba42f8d8917de76b0bf5acda89874fe70a323ce68dd67a7\",\n          \"0x0608fe05ef99aa7bfd9e1d5c20d096da7e14e4bbf4c3757ecfb29aa624682461\",\n          \"0x1e4d544947af4acf44e5a2f787700883bcf361afc726f707c15ecd73bf2991a2\",\n          \"0x0c8b10d3d236bf6bb5d7855a3610b44707a62b6bf4cc12c5c7ea30fa87afe7ce\",\n          \"0x0da3a967ee55218c7aeb0e79471780de9df3e0e43b99ab84492e5cee6178812c\",\n          \"0x1f49ce3e263fe94985bc9c4fc8a6da078e88183b84f2d5eccbdd23322ed0e52a\",\n          \"0x2f854e8576b393f8281a4ee34765fae491d456b5ebc255984aee6d1a6bfddb24\",\n          \"0x251d65931d09aea2d18ce4339b3df0c44d97bcb6c89c5352b7033b959b751b4e\",\n          \"0x126bc9da37891c2fb865fc63042ed2d0eac5970369a4c64b02e5c21180fb9905\",\n          \"0x2caedffb118383bf855a6d2133dac4c209be168701ea35614d2972d596102a16\",\n          \"0x08fc6c0a8cc8cd105ef293dbc8d9ea934ba1bbf9b15c190d110056914fb59ce8\",\n          \"0x10eefb3ba0dde80ebdbe59c08c3cc0acf8c8112a8c117c16ffbb599693eb8e42\",\n          \"0x2c0d237a46bd438641ab51032ea47e9416eefddba59bfa54cb0ca85994ae7e2a\",\n      },\n      {\n          \"0x16b8295dfc7aac9a92f40830d0a18021db1c3c32cb0186b2bbea437e4992bc9e\",\n          \"0x060b53de43d5419e8776eaefc4651c57b587045d5ac2ae857fe87be466e6c9d4\",\n          \"0x2ac49b1b54eb7dffed2e32d42b9b780c717802960ace5b4be230326426266058\",\n          \"0x26370289d5f64795d58f0d5cf98a2e694311753f5c5f0fc453d79600c3771454\",\n          \"0x05b3d1abef33209714601dd3dfd3067cdf6aa1e9122d8e22a05ea1bcffbb9c6c\",\n          \"0x300297d66a50a1974cc152a3dc9dc5cb9337b131b3255c36cdada2d141ac49f4\",\n          \"0x19eacc03d9e4d435d98cc9717e74a7c39011da0e8780d9e26c7ac11e1d45d05e\",\n          \"0x0f1d418da7ee6f503af23a71e6423b0dd1fb15de160753b2b961c42fd97b34e4\",\n          \"0x094fff48f42c489724b45af5ff0bf5c125c8a524c2d1fa34029ee95313b6426c\",\n          \"0x2b460fee9892bd0cf8c38333135a007b59a73bb46cccc65df68c525bc3ff4704\",\n          \"0x25f8c1e4a021048da4b79acff3497b746fc72cf27c7351128d8db0c38d0dfeff\",\n          \"0x142b65b35f124182a2c1bc038acbdf94da71b68ed1af1b1f0788ff44f44f7d4c\",\n          \"0x10956ff3bf348daac08be5b154b80d5f5c5a6ab649135c5881905f83a42f14f7\",\n          \"0x1e119c3a2345365cdf28ad7c4801925298d3b68aa4669027b1c0c6302fd293f4\",\n          \"0x2c71de2892808d0391c25b0369e19f351bc0b4f0cdc4c6438c0374f410b980ba\",\n          \"0x1d360eeea3494b3491a1a8901b219910755f94931eb85d5d0f97ca305ff1e9ea\",\n      },\n  };\n\n  constexpr static uint8_t kProof[] = {\n      144, 229, 155, 204, 124, 168, 31,  49,  47,  46,  92,  241, 59,  42,  185,\n      207, 170, 63,  142, 222, 11,  96,  209, 55,  68,  158, 132, 196, 160, 37,\n      58,  9,   11,  179, 5,   174, 11,  70,  229, 110, 251, 12,  31,  8,   37,\n      111, 214, 202, 175, 178, 120, 225, 0,   82,  92,  48,  127, 152, 74,  122,\n      163, 9,   254, 153, 49,  52,  6,   115, 102, 78,  171, 187, 144, 238, 90,\n      52,  145, 83,  130, 239, 132, 191, 0,   148, 105, 169, 113, 24,  122, 67,\n      220, 66,  79,  109, 109, 145, 144, 229, 155, 204, 124, 168, 31,  49,  47,\n      46,  92,  241, 59,  42,  185, 207, 170, 63,  142, 222, 11,  96,  209, 55,\n      68,  158, 132, 196, 160, 37,  58,  9,   11,  179, 5,   174, 11,  70,  229,\n      110, 251, 12,  31,  8,   37,  111, 214, 202, 175, 178, 120, 225, 0,   82,\n      92,  48,  127, 152, 74,  122, 163, 9,   254, 153, 49,  52,  6,   115, 102,\n      78,  171, 187, 144, 238, 90,  52,  145, 83,  130, 239, 132, 191, 0,   148,\n      105, 169, 113, 24,  122, 67,  220, 66,  79,  109, 109, 145, 66,  195, 64,\n      156, 233, 238, 146, 91,  109, 201, 118, 74,  47,  107, 32,  90,  228, 96,\n      159, 192, 134, 170, 173, 118, 182, 145, 155, 120, 220, 10,  172, 10,  250,\n      167, 203, 38,  247, 67,  71,  194, 136, 38,  90,  46,  88,  16,  89,  252,\n      97,  209, 153, 172, 62,  193, 110, 232, 107, 144, 43,  234, 29,  23,  186,\n      171, 18,  80,  164, 62,  2,   173, 176, 165, 24,  63,  198, 148, 44,  181,\n      5,   25,  241, 196, 231, 76,  144, 49,  186, 149, 22,  252, 115, 106, 148,\n      16,  186, 161, 3,   27,  163, 12,  185, 103, 196, 207, 58,  14,  233, 187,\n      160, 165, 229, 43,  214, 100, 121, 38,  159, 72,  240, 57,  250, 190, 138,\n      206, 126, 32,  232, 42,  72,  247, 125, 133, 48,  203, 17,  11,  15,  159,\n      69,  60,  196, 211, 194, 92,  161, 43,  117, 173, 161, 247, 115, 9,   45,\n      110, 189, 153, 173, 57,  74,  20,  200, 55,  85,  187, 107, 252, 50,  220,\n      137, 36,  250, 216, 107, 213, 167, 153, 13,  17,  237, 150, 215, 84,  114,\n      120, 113, 76,  118, 250, 155, 93,  177, 133, 201, 40,  195, 184, 255, 218,\n      118, 222, 145, 112, 24,  249, 63,  132, 230, 84,  148, 76,  235, 216, 185,\n      220, 226, 42,  77,  49,  218, 208, 226, 66,  24,  16,  138, 206, 72,  88,\n      133, 139, 229, 177, 148, 1,   92,  47,  205, 198, 167, 227, 7,   38,  170,\n      146, 208, 17,  155, 207, 249, 174, 33,  70,  200, 31,  200, 5,   1,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      51,  55,  111, 142, 4,   28,  138, 39,  192, 209, 222, 33,  47,  75,  4,\n      142, 178, 204, 244, 217, 201, 219, 137, 244, 240, 139, 135, 251, 199, 140,\n      210, 137, 85,  252, 254, 173, 127, 30,  188, 222, 28,  239, 206, 132, 243,\n      231, 98,  251, 98,  103, 69,  218, 106, 51,  156, 209, 231, 200, 53,  221,\n      126, 97,  226, 23,  189, 206, 145, 190, 213, 66,  153, 91,  21,  223, 200,\n      69,  86,  27,  100, 23,  45,  249, 45,  4,   224, 2,   26,  8,   149, 178,\n      47,  28,  67,  107, 240, 13,  236, 101, 69,  224, 54,  73,  61,  136, 79,\n      41,  179, 249, 174, 224, 239, 203, 2,   117, 165, 193, 79,  104, 156, 104,\n      246, 14,  215, 120, 77,  133, 81,  32,  34,  45,  224, 187, 102, 150, 226,\n      46,  18,  8,   84,  251, 83,  249, 132, 26,  243, 225, 109, 95,  96,  118,\n      45,  251, 122, 208, 73,  42,  148, 107, 235, 39,  189, 206, 145, 190, 213,\n      66,  153, 91,  21,  223, 200, 69,  86,  27,  100, 23,  45,  249, 45,  4,\n      224, 2,   26,  8,   149, 178, 47,  28,  67,  107, 240, 13,  236, 101, 69,\n      224, 54,  73,  61,  136, 79,  41,  179, 249, 174, 224, 239, 203, 2,   117,\n      165, 193, 79,  104, 156, 104, 246, 14,  215, 120, 77,  133, 81,  32,  34,\n      45,  224, 187, 102, 150, 226, 46,  18,  8,   84,  251, 83,  249, 132, 26,\n      243, 225, 109, 95,  96,  118, 45,  251, 122, 208, 73,  42,  148, 107, 235,\n      39,  98,  19,  202, 176, 9,   197, 247, 128, 131, 56,  212, 119, 122, 14,\n      41,  254, 38,  112, 63,  208, 251, 12,  34,  55,  82,  160, 155, 195, 2,\n      189, 227, 45,  1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   42,  61,  253, 254, 60,  108, 175, 204, 50,  55,\n      157, 131, 169, 131, 97,  32,  47,  230, 8,   159, 254, 154, 195, 102, 36,\n      89,  40,  145, 72,  150, 66,  33,  222, 68,  198, 34,  170, 47,  228, 103,\n      141, 201, 105, 11,  137, 102, 0,   84,  106, 51,  228, 143, 17,  250, 60,\n      248, 194, 231, 38,  132, 10,  22,  157, 45,  49,  132, 182, 46,  111, 96,\n      142, 237, 36,  138, 123, 242, 134, 16,  110, 98,  46,  188, 66,  177, 101,\n      1,   123, 251, 13,  168, 106, 183, 230, 204, 184, 45,  83,  246, 59,  197,\n      101, 154, 27,  75,  77,  129, 127, 123, 13,  244, 201, 157, 82,  121, 86,\n      14,  54,  181, 106, 64,  117, 121, 141, 203, 116, 97,  33,  17,  150, 108,\n      76,  52,  78,  139, 209, 9,   104, 70,  36,  23,  128, 198, 100, 238, 253,\n      219, 191, 93,  221, 65,  47,  50,  30,  69,  57,  35,  209, 160, 41,  10,\n      240, 118, 141, 58,  171, 186, 248, 169, 198, 127, 183, 126, 206, 51,  119,\n      184, 9,   228, 19,  40,  133, 191, 182, 87,  141, 23,  236, 71,  102, 208,\n      87,  9,   50,  79,  238, 240, 103, 115, 77,  185, 254, 216, 215, 174, 129,\n      47,  103, 85,  190, 68,  100, 251, 3,   99,  232, 254, 11,  195, 66,  227,\n      12,  40,  90,  16,  85,  216, 182, 190, 227, 130, 69,  54,  71,  247, 32,\n      162, 8,   49,  166, 175, 231, 66,  230, 248, 48,  187, 106, 152, 32,  195,\n      206, 40,  134, 38,  180, 20,  120, 236, 78,  85,  145, 46,  245, 22,  230,\n      101, 194, 96,  45,  6,   0,   211, 131, 100, 68,  78,  161, 65,  161, 44,\n      92,  160, 254, 106, 152, 169, 164, 15,  117, 35,  75,  249, 230, 98,  230,\n      3,   101, 97,  122, 230, 70,  254, 208, 166, 172, 97,  140, 141, 128, 86,\n      178, 203, 39,  219, 101, 144, 187, 198, 6,   26,  226, 6,   198, 229, 69,\n      245, 78,  131, 98,  105, 4,   225, 83,  52,  246, 72,  51,  50,  92,  132,\n      12,  101, 46,  76,  18,  5,   32,  238, 253, 29,  5,   12,  21,  54,  99,\n      83,  141, 46,  210, 247, 240, 159, 93,  231, 243, 252, 10,  235, 142, 148,\n      247, 77,  3,   246, 235, 236, 205, 75,  195, 32,  210, 49,  200, 10,  196,\n      60,  60,  117, 125, 13,  15,  91,  25,  164, 70,  214, 135, 44,  82,  110,\n      89,  103, 204, 49,  208, 64,  176, 90,  97,  148, 110, 181, 93,  136, 15,\n      1,   19,  132, 211, 138, 98,  98,  23,  228, 141, 107, 202, 34,  238, 136,\n      96,  36,  202, 128, 233, 104, 112, 46,  75,  198, 127, 245, 107, 167, 51,\n      90,  102, 23,  70,  122, 156, 119, 158, 161, 141, 128, 3,   108, 34,  117,\n      36,  13,  40,  204, 245, 155, 179, 128, 31,  45,  252, 196, 177, 217, 58,\n      45,  202, 98,  211, 22,  250, 124, 29,  10,  175, 40,  234, 81,  80,  108,\n      44,  198, 93,  252, 23,  45,  162, 254, 10,  246, 100, 107, 60,  76,  141,\n      169, 250, 150, 191, 37,  251, 46,  216, 8,   50,  15,  170, 187, 165, 33,\n      154, 209, 157, 172, 235, 192, 17,  148, 105, 245, 225, 140, 18,  245, 45,\n      207, 150, 250, 23,  124, 92,  182, 134, 44,  242, 128, 66,  181, 202, 241,\n      159, 30,  52,  212, 139, 225, 247, 127, 16,  230, 7,   51,  111, 54,  200,\n      239, 132, 148, 131, 21,  51,  179, 63,  161, 134, 27,  186, 83,  246, 123,\n      47,  234, 184, 199, 37,  51,  61,  243, 10,  19,  109, 64,  46,  197, 9,\n      29,  45,  90,  227, 97,  192, 225, 255, 158, 95,  152, 120, 26,  179, 223,\n      141, 22,  141, 32,  29,  254, 46,  202, 127, 195, 14,  143, 53,  243, 174,\n      155, 90,  18,  7,   69,  171, 59,  28,  172, 202, 190, 166, 62,  132, 15,\n      254, 83,  165, 161, 171, 214, 106, 13,  144, 191, 209, 56,  42,  97,  231,\n      97,  96,  255, 115, 167, 170, 130, 182, 216, 29,  222, 219, 199, 85,  76,\n      95,  45,  46,  151, 117, 253, 18,  106, 95,  98,  223, 189, 149, 25,  140,\n      241, 182, 165, 133, 234, 187, 53,  137, 189, 203, 35,  0,   248, 9,   174,\n      125, 27,  20,  32,  16,  54,  52,  179, 204, 160, 254, 238, 231, 85,  248,\n      244, 159, 243, 12,  178, 40,  5,   238, 41,  215, 95,  165, 143, 180, 5,\n      35,  189, 244, 151, 209, 38,  45,  35,  62,  100, 75,  3,   128, 229, 94,\n      120, 206, 100, 21,  97,  192, 204, 221, 10,  234, 1,   12,  138, 234, 97,\n      153, 6,   89,  240, 152, 239, 75,  13,  5,   245, 65,  199, 236, 52,  135,\n      114, 155, 224, 201, 186, 242, 20,  91,  5,   206, 175, 163, 88,  240, 134,\n      8,   2,   209, 106, 216, 1,   33,  229, 105, 45,  3,   149, 247, 73,  142,\n      64,  229, 157, 63,  227, 49,  227, 185, 188, 156, 240, 212, 16,  233, 168,\n      209, 144, 162, 90,  230, 120, 157, 27,  114, 88,  33,  6,   95,  104, 167,\n      173, 103, 30,  122, 2,   254, 75,  189, 228, 79,  160, 203, 84,  52,  7,\n      153, 0,   155, 189, 24,  2,   31,  86,  220, 207, 204, 164, 73,  138, 6,\n      100, 140, 233, 37,  146, 104, 88,  207, 55,  43,  171, 68,  137, 70,  122,\n      187, 198, 95,  200, 255, 5,   47,  85,  245, 2,   141, 153, 160, 236, 147,\n      4};\n\n  // clang-format off\n  constexpr static Point kAdviceCommitments[][3] = {\n    {\n      {\"0x093a25a0c4849e4437d1600bde8e3faacfb92a3bf15c2e2f311fa87ccc9be590\",\n       \"0x23f368976bd1e762f05c5efc8db4e7590d2300e60ef4a7bd898cc2f5bbd771e6\"},\n      {\"0x19fe09a37a4a987f305c5200e178b2afcad66f25081f0cfb6ee5460bae05b30b\",\n       \"0x0e106dd8def54c87c517f1eb20a56175822ce6e65f99a3a0655fc3e2e8a3e94b\"},\n      {\"0x116d6d4f42dc437a1871a9699400bf84ef825391345aee90bbab4e6673063431\",\n       \"0x2c2d516d5c04579cca6c30460e27130515680a99050770aabcdd8ec5855a9373\"},\n    },\n    {\n      {\"0x093a25a0c4849e4437d1600bde8e3faacfb92a3bf15c2e2f311fa87ccc9be590\",\n       \"0x23f368976bd1e762f05c5efc8db4e7590d2300e60ef4a7bd898cc2f5bbd771e6\"},\n      {\"0x19fe09a37a4a987f305c5200e178b2afcad66f25081f0cfb6ee5460bae05b30b\",\n       \"0x0e106dd8def54c87c517f1eb20a56175822ce6e65f99a3a0655fc3e2e8a3e94b\"},\n      {\"0x116d6d4f42dc437a1871a9699400bf84ef825391345aee90bbab4e6673063431\",\n       \"0x2c2d516d5c04579cca6c30460e27130515680a99050770aabcdd8ec5855a9373\"},\n    },\n  };\n  // clang-format on\n\n  constexpr static std::string_view kTheta =\n      \"0x0e147bc018ea0dae60978bd4c802f89281f85d9cefaeef97e8b1c59d058825ef\";\n\n  constexpr static std::string_view kBeta =\n      \"0x00e8932e10163041ad7c218ae33a024e9bd27598bc694a390cb45feacf635f03\";\n\n  constexpr static std::string_view kGamma =\n      \"0x293b80e793dafce1f1db5e5d7f266c9f18f22a29e2d3cac7437c0eb02964d584\";\n\n  // clang-format off\n  constexpr static Point kPermutationProductCommitments[][4] = {\n      {\n          {\"0x0aac0adc789b91b676adaa86c09f60e45a206b2f4a76c96d5b92eee99c40c342\",\n            \"0x02d89be67f1f0a190db5ac710351156edd14bd4c1c8d971e0c189b24cc1fd25c\"},\n          {\"0x2bba171dea2b906be86ec13eac99d161fc5910582e5a2688c24743f726cba7fa\",\n            \"0x2a83885d5e5d33ce854487c7b3c4f660ea1a0397d506b3309e9d3725ab222a55\"},\n          {\"0x21ba10946a73fc1695ba31904ce7c4f11905b52c94c63f18a5b0ad023ea45012\",\n            \"0x046efe02c21b19217f8bc9347dcd14a45b7fa8a637e140c158db26c2a666feed\"},\n          {\"0x2ae8207ece8abefa39f0489f267964d62be5a5a0bbe90e3acfc467b90ca31b03\",\n            \"0x069f5af770fd98d51ab2d9870ad1320a849edd453e5018b423ee66592157b0da\"},\n      },\n      {\n          {\"0x144a39ad99bd6e2d0973f7a1ad752ba15cc2d3c43c459f0f0b11cb30857df748\",\n            \"0x0de06616abf1d127658e5abb001513408168702f80581e12d817d0f5dcb3a9c0\"},\n          {\"0x05b15d9bfa764c71787254d796ed110d99a7d56bd8fa2489dc32fc6bbb5537c8\",\n            \"0x2463a4ac48f4955fa0aa849200e950d14f85616ffffb51bf9af080d68f676b31\"},\n          {\"0x101842e2d0da314d2ae2dcb9d8eb4c9454e6843ff9187091de76daffb8c328c9\",\n            \"0x11356be411434aed21ed05ef34e4dd688a3794f311f35979c033cb55cb6bd8bc\"},\n          {\"0x05c81fc84621aef9cf9b11d092aa2607e3a7c6cd2f5c0194b1e58b855848ce8a\",\n            \"0x12466a17eb84900aa737801b9adf80ca99482e3e1045a78df5bc9777747e1710\"},\n      },\n  };\n  // clang-format on\n\n  constexpr static std::string_view kY =\n      \"0x287bf794a84ee4b639e643202679380ef8cab5bfd8b3b6b30df1645e170e79f5\";\n\n  constexpr static Point kVanishingHPolyCommitments[] = {\n      {\"0x09d28cc7fb878bf0f489dbc9d9f4ccb28e044b2f21ded1c0278a1c048e6f3733\",\n       \"0x23612388ba8985a9ac2a94e8c79ae5d6ac1d97e5e48582d0c49625fe450766e5\"},\n      {\"0x17e2617edd35c8e7d19c336ada456762fb62e7f384ceef1cdebc1e7fadfefc55\",\n       \"0x061f2154c9097a1ad8f784c6bf043e59142944506ccddc2bbc0fc7c01c50ff36\"},\n  };\n\n  constexpr static std::string_view kX =\n      \"0x2d91afabb171fe83cb29e3753f33a39afc4bbe033bc15d77b776d2b00ae85823\";\n\n  constexpr static std::string_view kAdviceEvals[][3] = {\n      {\n          \"0x0df06b431c2fb295081a02e0042df92d17641b5645c8df155b9942d5be91cebd\",\n          \"0x2051854d78d70ef6689c684fc1a57502cbefe0aef9b3294f883d4936e04565ec\",\n          \"0x27eb6b942a49d07afb2d76605f6de1f31a84f953fb5408122ee29666bbe02d22\",\n      },\n      {\n          \"0x0df06b431c2fb295081a02e0042df92d17641b5645c8df155b9942d5be91cebd\",\n          \"0x2051854d78d70ef6689c684fc1a57502cbefe0aef9b3294f883d4936e04565ec\",\n          \"0x27eb6b942a49d07afb2d76605f6de1f31a84f953fb5408122ee29666bbe02d22\",\n      },\n  };\n\n  constexpr static std::string_view kFixedEvals[] = {\n      \"0x2de3bd02c39ba05237220cfbd03f7026fe290e7a77d4388380f7c509b0ca1362\",\n  };\n\n  constexpr static std::string_view kCommonPermutationEvals[] = {\n      \"0x214296489128592466c39afe9f08e62f206183a9839d3732ccaf6c3cfefd3d2a\",\n      \"0x2d9d160a8426e7c2f83cfa118fe4336a540066890b69c98d67e42faa22c644de\",\n      \"0x2db8cce6b76aa80dfb7b0165b142bc2e626e1086f27b8a24ed8e606f2eb68431\",\n      \"0x11216174cb8d7975406ab5360e5679529dc9f40d7b7f814d4b1b9a65c53bf653\",\n  };\n\n  constexpr static std::string_view kPermutationProductEvals[][4] = {\n      {\n          \"0x0a29a0d12339451e322f41dd5dbfdbfdee64c6801724466809d18b4e344c6c96\",\n          \"0x14b4268628cec320986abb30f8e642e7afa63108a220f747364582e3beb6d855\",\n          \"0x0c051dfdee2005124c2e650c845c323348f63453e1046962834ef545e5c606e2\",\n          \"0x17665a33a76bf57fc64b2e7068e980ca246088ee22ca6b8de41762628ad38413\",\n      },\n      {\n          \"0x2efb25bf96faa98d4c3c6b64f60afea22d17fc5dc62c6c5051ea28af0a1d7cfa\",\n          \"0x1a78985f9effe1c061e35a2d1d09c52e406d130af33d3325c7b8ea2f7bf653ba\",\n          \"0x20141b7dae09f80023cbbd8935bbea85a5b6f18c1995bddf625f6a12fd75972e\",\n          \"0x2d69e52101d86ad1020886f058a3afce055b14f2bac9e09b728734ecc741f505\",\n      },\n  };\n\n  constexpr static std::string_view kPermutationProductNextEvals[][4] = {\n      {\n          \"0x0957d06647ec178d57b6bf852813e409b87733ce7eb77fc6a9f8baab3a8d76f0\",\n          \"0x0fa4a9986afea05c2ca141a14e446483d300062d60c265e616f52e91554eec78\",\n          \"0x0ac831d220c34bcdecebf6034df7948eeb0afcf3e75d9ff0f7d22e8d53633615\",\n          \"0x16d362ca2d3ad9b1c4fc2d1f80b39bf5cc280d2475226c03808da19e779c7a46\",\n      },\n      {\n          \"0x2c86b65c7c17fa96cf2df5128ce1f5699411c0ebac9dd19a21a5bbaa0f3208d8\",\n          \"0x0f843ea6becaac1c3bab4507125a9baef3358f0ec37fca2efe1d208d168ddfb3\",\n          \"0x26d197f4bd2305b48fa55fd729ee0528b20cf39ff4f855e7eefea0ccb3343610\",\n          \"0x062158721b9d78e65aa290d1a8e910d4f09cbcb9e331e33f9de5408e49f79503\",\n      },\n  };\n\n  constexpr static std::string_view kPermutationProductLastEvals[][4] = {\n      {\n          \"0x105a280ce342c30bfee86303fb6444be55672f81aed7d8feb94d7367f0ee4f32\",\n          \"0x1a06c6bb9065db27cbb256808d8c61aca6d0fe46e67a616503e662e6f94b2375\",\n          \"0x010f885db56e94615ab040d031cc67596e522c87d646a4195b0f0d7d753c3cc4\",\n          \"\",\n      },\n      {\n          \"0x1b86a13fb33315839484efc8366f3307e6107ff7e18bd4341e9ff1cab54280f2\",\n          \"0x2d5f4c55c7dbde1dd8b682aaa773ff6061e7612a38d1bf900d6ad6aba1a553fe\",\n          \"0x0d4bef98f059069961ea8a0c01ea0addccc0611564ce785ee580034b643e232d\",\n          \"\",\n      },\n  };\n\n  static void TestConfig(Fibonacci1Config<F>& config) {\n    std::array<AdviceColumnKey, 3> expected_advice = {\n        AdviceColumnKey(0),\n        AdviceColumnKey(1),\n        AdviceColumnKey(2),\n    };\n    EXPECT_EQ(config.advice, expected_advice);\n    EXPECT_EQ(config.instance, InstanceColumnKey(0));\n    EXPECT_EQ(config.selector, Selector::Simple(0));\n  }\n\n  static std::vector<Evals> GetInstanceColumns() {\n    F a(1);\n    F b(1);\n    F out(55);\n    std::vector<F> instance_column = {std::move(a), std::move(b),\n                                      std::move(out)};\n    return std::vector<Evals>{Evals(std::move(instance_column))};\n  }\n};\n\n// V1FloorPlanner\ntemplate <typename Circuit, typename PS>\nclass Fibonacci1TestData<Circuit, PS,\n                         std::enable_if_t<IsV1FloorPlanner<Circuit>>>\n    : public CircuitTestData<Circuit, PS> {\n public:\n  using PCS = typename PS::PCS;\n  using F = typename PCS::Field;\n  using Evals = typename PCS::Evals;\n\n  constexpr static bool kAssemblyPermutationColumnsFlag = true;\n  constexpr static bool kCycleStoreMappingFlag = true;\n  constexpr static bool kCycleStoreAuxFlag = true;\n  constexpr static bool kCycleStoreSizesFlag = true;\n  constexpr static bool kLFirstFlag = true;\n  constexpr static bool kLLastFlag = true;\n  constexpr static bool kLActiveRowFlag = true;\n  constexpr static bool kFixedColumnsFlag = true;\n  constexpr static bool kFixedPolysFlag = true;\n  constexpr static bool kPermutationsColumnsFlag = true;\n  constexpr static bool kPermutationsPolysFlag = true;\n  constexpr static bool kAdviceCommitmentsFlag = true;\n  constexpr static bool kPermutationProductCommitmentsFlag = true;\n  constexpr static bool kAdviceEvalsFlag = true;\n  constexpr static bool kFixedEvalsFlag = true;\n  constexpr static bool kCommonPermutationEvalsFlag = true;\n  constexpr static bool kPermutationProductEvalsFlag = true;\n  constexpr static bool kPermutationProductNextEvalsFlag = true;\n  constexpr static bool kPermutationProductLastEvalsFlag = true;\n\n  constexpr static size_t kN = 16;\n\n  // clang-format off\n  constexpr static std::string_view kPinnedConstraintSystem =\n      \"PinnedConstraintSystem { \"\n        \"num_fixed_columns: 0, \"\n        \"num_advice_columns: 3, \"\n        \"num_instance_columns: 1, \"\n        \"num_selectors: 1, \"\n        \"gates: [Product(\"\n          \"Selector(Selector(0, true)), \"\n          \"Sum(\"\n            \"Sum(\"\n              \"Advice { \"\n                \"query_index: 0, \"\n                \"column_index: 0, \"\n                \"rotation: Rotation(0) \"\n              \"}, \"\n              \"Advice { \"\n                \"query_index: 1, \"\n                \"column_index: 1, \"\n                \"rotation: Rotation(0) \"\n              \"}\"\n            \"), \"\n            \"Negated(Advice { \"\n              \"query_index: 2, \"\n              \"column_index: 2, \"\n              \"rotation: Rotation(0) \"\n            \"})\"\n          \")\"\n        \")], \"\n        \"advice_queries: [\"\n          \"(\"\n            \"Column { \"\n              \"index: 0, \"\n              \"column_type: Advice \"\n            \"}, \"\n            \"Rotation(0)\"\n          \"), \"\n          \"(\"\n            \"Column { \"\n              \"index: 1, \"\n              \"column_type: Advice \"\n            \"}, \"\n            \"Rotation(0)\"\n          \"), \"\n          \"(\"\n            \"Column { \"\n              \"index: 2, \"\n              \"column_type: Advice \"\n            \"}, \"\n            \"Rotation(0)\"\n          \")\"\n        \"], \"\n        \"instance_queries: [(\"\n          \"Column { \"\n            \"index: 0, \"\n            \"column_type: Instance \"\n          \"}, \"\n          \"Rotation(0)\"\n        \")], \"\n        \"fixed_queries: [], \"\n        \"permutation: Argument { columns: [\"\n          \"Column { \"\n            \"index: 0, \"\n            \"column_type: Advice \"\n          \"}, \"\n          \"Column { \"\n            \"index: 1, \"\n            \"column_type: Advice \"\n          \"}, \"\n          \"Column { \"\n            \"index: 2, \"\n            \"column_type: Advice \"\n          \"}, \"\n          \"Column { \"\n            \"index: 0, \"\n            \"column_type: Instance \"\n          \"}\"\n        \"] }, \"\n        \"lookups: [], \"\n        \"constants: [], \"\n        \"minimum_degree: None \"\n      \"}\";\n  // clang-format on\n\n  constexpr static AnyColumnKey kAssemblyPermutationColumns[] = {\n      AdviceColumnKey(0),\n      AdviceColumnKey(1),\n      AdviceColumnKey(2),\n      InstanceColumnKey(0),\n  };\n\n  // clang-format off\n  constexpr static Label kCycleStoreMapping[][kN] = {\n      {{1, 1}, {1, 2},  {1, 3},  {1, 4},  {1, 5},  {1, 6},  {3, 1},  {3, 0},\n        {0, 8}, {0, 9}, {0, 10}, {0, 11}, {0, 12}, {0, 13}, {0, 14}, {0, 15}},\n      {{2, 1}, {2, 2},  {2, 3},  {2, 4},  {2, 5},  {2, 6},  {2, 7},  {0, 6},\n        {1, 8}, {1, 9}, {1, 10}, {1, 11}, {1, 12}, {1, 13}, {1, 14}, {1, 15}},\n      {{3, 2}, {1, 0},  {0, 0},  {0, 1},  {0, 2},  {0, 3},  {0, 4},  {0, 5},\n        {2, 8}, {2, 9}, {2, 10}, {2, 11}, {2, 12}, {2, 13}, {2, 14}, {2, 15}},\n      {{0, 7}, {1, 7},  {2, 0},  {3, 3},  {3, 4},  {3, 5},  {3, 6},  {3, 7},\n        {3, 8}, {3, 9}, {3, 10}, {3, 11}, {3, 12}, {3, 13}, {3, 14}, {3, 15}},\n  };\n\n  constexpr static Label kCycleStoreAux[][kN] = {\n      {{1, 1}, {1, 2},  {1, 3},  {1, 4},  {1, 5},  {1, 6},  {1, 7},  {0, 7},\n        {0, 8}, {0, 9}, {0, 10}, {0, 11}, {0, 12}, {0, 13}, {0, 14}, {0, 15}},\n      {{1, 0}, {1, 1},  {1, 2},  {1, 3},  {1, 4},  {1, 5},  {1, 6},  {1, 7},\n        {1, 8}, {1, 9}, {1, 10}, {1, 11}, {1, 12}, {1, 13}, {1, 14}, {1, 15}},\n      {{2, 0}, {1, 0},  {1, 1},  {1, 2},  {1, 3},  {1, 4},  {1, 5},  {1, 6},\n        {2, 8}, {2, 9}, {2, 10}, {2, 11}, {2, 12}, {2, 13}, {2, 14}, {2, 15}},\n      {{0, 7}, {1, 7},  {2, 0},  {3, 3},  {3, 4},  {3, 5},  {3, 6},  {3, 7},\n        {3, 8}, {3, 9}, {3, 10}, {3, 11}, {3, 12}, {3, 13}, {3, 14}, {3, 15}},\n  };\n  // clang-format on\n\n  constexpr static size_t kCycleStoreSizes[][kN] = {\n      {1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1},\n      {2, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1},\n      {2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},\n      {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},\n  };\n\n  // clang-format off\n  constexpr static bool kSelectors[][kN] = {\n      {true,  true,  true,  true,  true,  true,  true,  true,\n       false, false, false, false, false, false, false, false}\n  };\n  // clang-format on\n\n  // clang-format off\n  constexpr static std::string_view kPinnedVerifyingKey =\n      \"PinnedVerificationKey { \"\n        \"base_modulus: \\\"0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47\\\", \"\n        \"scalar_modulus: \\\"0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001\\\", \"\n        \"domain: PinnedEvaluationDomain { \"\n          \"k: 4, \"\n          \"extended_k: 5, \"\n          \"omega: 0x21082ca216cbbf4e1c6e4f4594dd508c996dfbe1174efb98b11509c6e306460b \"\n        \"}, \"\n        \"cs: PinnedConstraintSystem { \"\n          \"num_fixed_columns: 1, \"\n          \"num_advice_columns: 3, \"\n          \"num_instance_columns: 1, \"\n          \"num_selectors: 1, \"\n          \"gates: [Product(\"\n            \"Fixed { \"\n              \"query_index: 0, \"\n              \"column_index: 0, \"\n              \"rotation: Rotation(0) \"\n            \"}, \"\n            \"Sum(\"\n              \"Sum(\"\n                \"Advice { \"\n                  \"query_index: 0, \"\n                  \"column_index: 0, \"\n                  \"rotation: Rotation(0) \"\n                \"}, \"\n                \"Advice { \"\n                  \"query_index: 1, \"\n                  \"column_index: 1, \"\n                  \"rotation: Rotation(0) \"\n                \"}\"\n              \"), \"\n              \"Negated(Advice { \"\n                \"query_index: 2, \"\n                \"column_index: 2, \"\n                \"rotation: Rotation(0) \"\n              \"})\"\n            \")\"\n          \")], \"\n          \"advice_queries: [\"\n            \"(\"\n              \"Column { \"\n                \"index: 0, \"\n                \"column_type: Advice \"\n              \"}, \"\n              \"Rotation(0)\"\n            \"), \"\n            \"(\"\n              \"Column { \"\n                \"index: 1, \"\n                \"column_type: Advice \"\n              \"}, \"\n              \"Rotation(0)\"\n            \"), \"\n            \"(\"\n              \"Column { \"\n                \"index: 2, \"\n                \"column_type: Advice \"\n              \"}, \"\n              \"Rotation(0)\"\n            \")\"\n          \"], \"\n          \"instance_queries: [(\"\n            \"Column { \"\n              \"index: 0, \"\n              \"column_type: Instance \"\n            \"}, \"\n            \"Rotation(0)\"\n          \")], \"\n          \"fixed_queries: [(\"\n            \"Column { \"\n              \"index: 0, \"\n              \"column_type: Fixed \"\n            \"}, \"\n            \"Rotation(0)\"\n          \")], \"\n          \"permutation: Argument { columns: [\"\n            \"Column { \"\n              \"index: 0, \"\n              \"column_type: Advice \"\n            \"}, \"\n            \"Column { \"\n              \"index: 1, \"\n              \"column_type: Advice \"\n            \"}, \"\n            \"Column { \"\n              \"index: 2, \"\n              \"column_type: Advice \"\n            \"}, \"\n            \"Column { \"\n              \"index: 0, \"\n              \"column_type: Instance \"\n            \"}\"\n          \"] }, \"\n          \"lookups: [], \"\n          \"constants: [], \"\n          \"minimum_degree: None \"\n        \"}, \"\n        \"fixed_commitments: [\"\n          \"(0x297ff8a661d1fa1196c065b6fb7df901fb16b5b83168ddca6c7749d963cc967a, \"\n            \"0x1a7b1f2b5f3e35fc4c706ece6b8f646e95904f9aa417f8a31fd37a41da167ec1)\"\n        \"], \"\n        \"permutation: VerifyingKey { commitments: [\"\n          \"(0x0d753ca469cfea858cfa6ec91acd016399cd294f61ff0d43ebb968d2524a713c, \"\n            \"0x2550d6fb1e5c470ba3e6017989a6d380a4c0c6be14ba6e4275dae09e16a23284), \"\n          \"(0x1357bd4a7abf61580c4d6830f369d242e2268747198bfe5e41cc960f5c475f77, \"\n            \"0x29ca42708ce07d2576298db193d3fd9bf87739177d4bf2b831841798d8b2b50d), \"\n          \"(0x1eba9015d88cec13d423a6c37c5edd453a72e087fcfb36461bf75dd35ebe344a, \"\n            \"0x22c70d284caba78df7da1a1eb8607e5fa298a4e8ff21b8bfeb7b0772f4cb2c71), \"\n          \"(0x1dd1199d21ff272e331fca7683198fed95caea81dd571af67753b96d29111b1d, \"\n            \"0x0274945484229b0f6dcff00e591c859ac06bdb68ccd0bf25425ccdc266df1329)\"\n        \"] } \"\n      \"}\";\n  // clang-format on\n\n  constexpr static std::string_view kTranscriptRepr =\n      \"0x0cf64676a870bcf55c113fb62e896e924807935cb63a2794b382539e995f8673\";\n\n  constexpr static std::string_view kLFirst[] = {\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n  };\n\n  constexpr static std::string_view kLLast[] = {\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2014447de15a99b6df03833e95f96ae1299c9ec6ff990b6e75fa3b3b04846a57\",\n      \"0x0f1f5883e65f820d14d56342dc92fd12a944d4cbbdce5377b7439bd07108fc9d\",\n      \"0x02b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e8\",\n      \"0x030644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000\",\n      \"0x105009f4ffd70672d94cc277eb87ed7bfe9749817a206522cde7ba58eb7b95aa\",\n      \"0x2144f5eefad21e1ca37ae273a4ee5b4a7eef137cbbeb1d198c9e59c37ef70364\",\n      \"0x2db11694c4a58b3789868bd388165969c30dc1120a37fff09a991abf43e42a19\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2014447de15a99b6df03833e95f96ae1299c9ec6ff990b6e75fa3b3b04846a57\",\n      \"0x0f1f5883e65f820d14d56342dc92fd12a944d4cbbdce5377b7439bd07108fc9d\",\n      \"0x02b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e8\",\n      \"0x030644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000\",\n      \"0x105009f4ffd70672d94cc277eb87ed7bfe9749817a206522cde7ba58eb7b95aa\",\n      \"0x2144f5eefad21e1ca37ae273a4ee5b4a7eef137cbbeb1d198c9e59c37ef70364\",\n      \"0x2db11694c4a58b3789868bd388165969c30dc1120a37fff09a991abf43e42a19\",\n  };\n\n  constexpr static std::string_view kLActiveRow[] = {\n      \"0x12259d6b14729c0fa51e1a2470908122ef13771b2da58a367974bc177a000001\",\n      \"0x1a8133201ba2fe22aff30c7fd04fadcb17ceab0715a13371b06d35acd9695b3d\",\n      \"0x0d49c50dd1c3ec703dc7be1c836fd7f62c140afcf284ce19b9a99affac7b95aa\",\n      \"0x117dae38a05bcb8c7c290ee16cec493d73ef8dafa5c61fa4a2efd9a39e63abf4\",\n      \"0x0c19139cb84c680a79505ee7747ae78cd6c196473632bc6ea3057c773208fc9d\",\n      \"0x136156e428a2662bc2fddfd3b39f6475dafecb8699a611f3da6edd22c3479af1\",\n      \"0x2aaad1ad96927134ee0187781ffe43e3f08a828d829c68e7865afb6604e42a19\",\n      \"0x28d7d254c17b7ea40ebc4659996adacebd0d8f52d021284040d407c2f33b896f\",\n      \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n      \"0x1ef7221577b1e8f8cebdcb2fcd10296b6d9d43267b395c820757c0fc87681d81\",\n      \"0x1d0dff96b3477fb4437e7ee32de1555b5719604277fd746561bc1be1c5846a57\",\n      \"0x1be193603aacb384678281793fa8f20b949cf5976d9493017ba8a357ee49da57\",\n      \"0x1e3eb107ccbf041a07f5de183cd645c4ac6bd4f8344f861078603a6a3ff70364\",\n      \"0x20080468beb85b16c9f71b3ea2ce10fb6cdc81c346721c888ebc9109900adec6\",\n      \"0x30114169cfaa9b194b94fb3e12d441cabad6d0fa619f4a28d8ecb10f5d1bd5e9\",\n      \"0x2edcc3ce4ec47abd9b83b31a4db9571236223b590c30997fd30ce24f7bf2fdd6\",\n  };\n\n  constexpr static std::string_view kFixedColumns[][kN] = {\n      {\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n      },\n  };\n\n  constexpr static std::string_view kFixedPolys[][kN] = {\n      {\n          \"0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000001\",\n          \"0x1db5b8e23dbf6d914e37d939eb6b037c619b3e5ea827cdb2b030fc247bdc9dcb\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x15612923e039d29887b96603ef6ab434a2c021b8674489fcfa35d3972944e837\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x186635fde662f3b15c5e1b5fc456d148c26786b9cda347e81f7f4c2f8c9761ed\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x14a1b21f8b9c269f87d74740cf8c84a3046338799106ae503c4dfb0f0b0b250e\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x15b61284f96f4584f96ef5bee1c4a8ae7eca32c5d97b942edf17bbd266f4daf3\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x11f18ea69ea8787324e8219fecfa5c08c0c5e4859cdefa96fbe66ab1e5689e14\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x14f69b80a4d1998bf98cd6fbc1e6791ce06d4987033db882212fe34a48bb17ca\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0ca20bc2474bfe93330e63c5c5e629d521922ce0c25a74cc6b34babcf6236236\",\n      },\n  };\n\n  constexpr static std::string_view kPermutationsColumns[][kN] = {\n      {\n          \"0x133f51f46c2a51201fbb89dc1d6868acfef0f7ce0e572ccb98e04e99eac3ea36\",\n          \"0x1240a374ee6f71e12df10a129946ca9ba824f58a50e3a58b4c68dd5590b74ad8\",\n          \"0x009553089042fe83ab570b54e4e64d307d8e8b20d568e782ece3926981d0a96c\",\n          \"0x14a6c152ace4b16a42e1377e400798e15ac60320c21d90277890dbdd551d1912\",\n          \"0x035992598be4d2ae5334f24f30355a1fca7f0e28762a178d79e993dca70eaabf\",\n          \"0x03b645319eb70d7a692ea8c87fbcab1f292cd502071d9ffb7291ebe28356aa07\",\n          \"0x2f0e061e83e8b12c1bdf8df7cca02483295e89f78a462ce38859661e06501815\",\n          \"0x18afdf23e9bd9302673fc1e076a492d4d65bd18ebc4d854ed189139bab313e52\",\n          \"0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000\",\n          \"0x0f5c21d0ca65e0db9be1f670eca407d08ec5ec67626a74f892ccebcd0cf9b9f6\",\n          \"0x0530d09118705106cbb4a786ead16926d5d174e181a26686af5448492e42a181\",\n          \"0x1fe9a328fad7382fddb3730a89f574d14e57caeac619eeb30d24fb38a4fc6fbe\",\n          \"0x0000000000000000b3c4d79d41a91758cb49c3517c4604a520cff123608fc9cb\",\n          \"0x0dd360411caed09700b52c71a6655715c4d558439e2d34f4307da9a4be13c42e\",\n          \"0x130b17119778465cfb3acaee30f81dee20710ead41671f568b11d9ab07b95a9b\",\n          \"0x02e40daf409556c02bfc85eb303402b774954d30aeb0337eb85a71e6373428de\",\n      },\n      {\n          \"0x08e7cbfea108224b0777f0558503af41585b75ab8d4d807505158f4bc8c771de\",\n          \"0x0b6f861977ce57ddb2e647048ed9b9433cac640ba0599e264e24446765d915d3\",\n          \"0x26f93d99832c6285d9abf8c5b896ea753ed137516972c5ddcc464b56c488d600\",\n          \"0x086398ace043cf0db4e99f2712f392ddbd9c7b9202b2fcadf26e9b145316df9f\",\n          \"0x04765b5102d6627cfcc389436e96bbc9aa478dcb720b546c77063077a40910ce\",\n          \"0x2e39c17d3e15071a9341cfcea233ad5f25a14dea28716de88ae369ac2c9944ac\",\n          \"0x2a3d1fef5cb3ce1065d222dde5f9b16d48b42597868c1a49e15cb8007c8778a4\",\n          \"0x1d59376149b959ccbd157ac850893a6f07c2d99b3852513ab8d01be8e846a566\",\n          \"0x2741e304be6aaf5f53641f0bacb8e9ebccd45eba1b23316bbcd39ed80acc165f\",\n          \"0x1d24fc7e75074f099894bbda6418efb02942f07a6b6243c5ab01a6fa053c15cb\",\n          \"0x1e23aafdf2c22e488a5f3ba3e83a8dc1800ef2be28d5cb05f779183e5f48b529\",\n          \"0x2fcefb6a50eea1a60cf93a619c9b0b2caaa55d27a450890e56fe632a6e2f5695\",\n          \"0x1bbd8d20344ceebf756f0e384179bf7bcd6de527b79be069cb5119b69ae2e6ef\",\n          \"0x2d0abc19554ccd7b651b5367514bfe3d5db4da20038f5903c9f861b748f15542\",\n          \"0x2cae0941427a92af4f219cee01c4ad3dff071346729bd095d15009b16ca955fa\",\n          \"0x0d4615fec1d5611cd5ffa9e358e64eb494829d350acf01c136f55acac8e3624c\",\n      },\n      {\n          \"0x034183d253b6b250dae0a457797029523434d2b6bc41c09b6ef409bd970e4208\",\n          \"0x09226b6e22c6f0ca64ec26aad4c86e715b5f898e5e963f25870e56bbe533e9a2\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x21082ca216cbbf4e1c6e4f4594dd508c996dfbe1174efb98b11509c6e306460b\",\n          \"0x2b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e80\",\n          \"0x107aab49e65a67f9da9cd2abf78be38bd9dc1d5db39f81de36bcfa5b4b039043\",\n          \"0x30644e72e131a029048b6e193fd841045cea24f6fd736bec231204708f703636\",\n          \"0x2290ee31c482cf92b79b1944db1c0147635e9004db8c3b9d13644bef31ec3bd3\",\n          \"0x1cb0ed9df901b713b97ee5357df1bf9b16f16cc0d737b31e07ba77d910efc8d6\",\n          \"0x277c827440297ddeb0d85560fc7da91bcfd8729cec6bf01c3ecc664827388e23\",\n          \"0x24f4c8596963484c0569feb1f2a79f19eb87843cd95fd26af5bdb12c8a26ea2e\",\n          \"0x096b10d95e053da3dea44cf0c8ea6de7e962b0f71046aab3779baa3d2b772a01\",\n          \"0x2800b5c600edd11c0366a68f6e8dc57f6a976cb6770673e351735a7f9ce92062\",\n          \"0x2bedf321de5b3dacbb8cbc7312ea9c937dec5a7d07ae1c24ccdbc51c4bf6ef33\",\n          \"0x022a8cf5a31c990f250e75e7df4daafe02929a5e514802a8b8fe8be7c366bb55\",\n          \"0x06272e83847dd219527e22d89b87a6efdf7fc2b0f32d564762853d937378875d\",\n      },\n      {\n          \"0x2d8040c3a09c49698c53bfcb514d55a5b39e9b17cb093d128b8783adb8cbd723\",\n          \"0x231e38741f5c3f0ce2509bd3289b09a893b14b136eea6ed00cec9ac9271c9db5\",\n          \"0x13b360d4e82fe915fed16081038f98c211427b87a281bd733c277dbadf10372b\",\n          \"0x0f34fda7cd268bf095354ea6c2631826c349d7518bf094361bc91f61c519c7fb\",\n          \"0x2f549305063b1803a77ba92486c5383e00468fc857ec66d944868c4a309e596a\",\n          \"0x1e626bf9ef3c8920522383ff9be21287c3af8d47fe61ff9af6c6d8d0118154bc\",\n          \"0x02898db49f7022cac0aeabf721a3c7ab96e14ad35e7d6b0410b868ff504b62fc\",\n          \"0x2a150ee7450ad90cace368ea424c8fdf36b3821e79f5f1c617a5f8e74c19b4cf\",\n          \"0x17b46f4ef7740d27511083d60adcc58851d816b9bd6beb427258e1f844cec1af\",\n          \"0x015648545d48eefd9c70b7beb4e133d9fed55e50ef7343adbb888f75e9afe7ec\",\n          \"0x2d22caa08d7aedd8dd6fa15f08112f0af3ff1591bd77aff5d4edebd658f1bdf9\",\n          \"0x212f50cb140b1439231af70fbf1e403664ea10f6edc8dc5b2818d6322ae63806\",\n          \"0x010fbb6ddaf6882610d49c91fabc201f27ed588021cd09b7ff5b6949bf61a697\",\n          \"0x1201e278f1f51709662cc1b6e59f45d564845b007b5770f64d1b1cc3de7eab45\",\n          \"0x2ddac0be41c17d5ef7a199bf5fdd90b191529d751b3c058d33298c949fb49d05\",\n          \"0x064f3f8b9c26c71d0b6cdccc3f34c87df1806629ffc37ecb2c3bfcaca3e64b32\",\n      },\n  };\n\n  constexpr static std::string_view kPermutationsPolys[][kN] = {\n      {\n          \"0x112272f91e39f0f3754b36d52fafaa32b73194639e9077aeeb8fd9eeb5c197f6\",\n          \"0x0f6ca21bfbcb52a4aee917564277498d8adcd0ead26ba0df100a7286b752d9d4\",\n          \"0x15a7e8359f35c766c29ed77f4d1d5119901d036e20c15142192a21e09041e827\",\n          \"0x0b4bc964d379eeaa158eca75b6dbb6a7b86f896b10f25adc9b92d8d5475ed2f1\",\n          \"0x25d115149ca21b94ee56a92ef0d8c8602163a397afc69182e9b4411789e2e60b\",\n          \"0x1e4a361c58a1fc2e342bb1b5522c187cfc6134683e6fa0f85eacc9916574db2c\",\n          \"0x18b8417cd7b83ee5b62a8acacf2af01e8ea1a740a58c55047d8c926b8368c14d\",\n          \"0x102953096b3b2549bc2c5f8c310a262a6280534c7e1984c250448ba8b1ff20d1\",\n          \"0x12deb8e349e34c7dbd7857f75ca3dc59a3258bfe82bc3d01f2c8be9df84e4b96\",\n          \"0x14232df27fdebe635d88be19813451e8b574c2458d8ae3c1503dc0833e136502\",\n          \"0x25333b49ba4fe6f17bfa2659b5254c7ab6805b8bb4711ee45163adcdeeafdded\",\n          \"0x13519d493b4d53fdd4aa5e6bfbf50e5e459112c26c54b865e524adc12e3c211e\",\n          \"0x28b5a8a4f3f4184b8747b9d14a086e6e83e878fb40ffc9529d9603cf20a5ce1b\",\n          \"0x105c187bb3938b9863b8a65abb1e84eeff1390bb1f5b99a6f34e1b0f8286db1b\",\n          \"0x1d47bb6d01831abc30218432bcd7f6ebdf7fcdfd9e1c3c42302bb8d9526ed60c\",\n          \"0x019d94bb672f383811eabd489da66d2cc7b2f8ce7e58d88c72e5e354c865eb21\",\n      },\n      {\n          \"0x141e39f89c516db1255ecee3825495dfb18b1bd87123d9257363ae894a291dd3\",\n          \"0x08a4dfd16be05cd2087a54d987991410bba11d1548080afde146412d7de47930\",\n          \"0x2f25daf535a35461b7c8d7f63bdbbefbd8339e1d3c294d9069a788bb901c525c\",\n          \"0x0ec047746f9977d98dc53b31063cd1fe923a04dfa0e7b689138388a53ce7d701\",\n          \"0x256defc2c8f2e2dad221ba4401452d4a6aa8dced60798b0cdc3618e76a4ee2e0\",\n          \"0x22053aee890a5e747c94c48401983f3c2e9cf8aaf7c8c03afaa6c573ef6fa7f8\",\n          \"0x15add4829e360738c4aa9bf9a1ada88926b9bd475b09577c34ac62ebefcea7f6\",\n          \"0x028b81cb54900a1ac0a193c1d76a3a83742de52b5ceb991e475e98982a564c33\",\n          \"0x0361f1609cfec0d84058dbc7fac6dc7f3dde7c674b560517b375a6775bc71d65\",\n          \"0x00603be4f6072cb8adb7b6a6a54cfac89e3c6287ade9d4cf64cbb1d659193490\",\n          \"0x1a13eebb730009c87bc2fb35f61a4f938809953b467d85a8d6c4db8fa2d8547d\",\n          \"0x21a406fe719828502a8b0a857b44865e95f9e368d8d1ba08305e6ceeb3182900\",\n          \"0x1fbfc06d752ce7940fe2ad597c54a9ff0974b915ce28b93da3d3a08a5c60c1e8\",\n          \"0x0e5f1384582741b53bbb81327fe91920f996ef9d81f0b056493b302000905809\",\n          \"0x05de7057059dbb0bf2947a408fca011bb4cf784db57515afb28f9cee22669553\",\n          \"0x2dd8cca78ca1960ef7aeb1f4aa171dd9b406031d1ccdd772fc835cfbc5a9b3ce\",\n      },\n      {\n          \"0x15a78e0e6085f1debac2f4ec73d13ceba739fee72ad97c78b52a98f544aeb8ff\",\n          \"0x1812dc7c7a640429efb6c9f4517a6a26665d974c956fc4b205154578966cb7d5\",\n          \"0x2d25b29b330ef94426a3147d0cfbeac4967b6ad86802c928957e0e84a21a5d87\",\n          \"0x261f4d63c9fdebb6334bf99c16a2b16a6889dbe3db6d53c72bba22395b7b672f\",\n          \"0x1e79eccbbbe54d40275ee2bfbec7ae64b7aebf6e5142e793b30a7ec56dde6aff\",\n          \"0x2f0b5305526a51f04ed9e81f590d5dadf43a2488ebf665c4f85c9ae9edf419c9\",\n          \"0x1a268af4032dced16da12867c46aa1da4b8cbd95767a0829326fe829756fdeda\",\n          \"0x160198e6359a11c21826c7d41371a65a056720e7f3a4ef9036a118e182490fd4\",\n          \"0x27f8f5e308e267a585e5f57c4f1d6a7221517bb415c7de49238e98966bc593b1\",\n          \"0x0b78ca79f16a7d1dae622114e268f3c124081d6a39858811f478685428e2adfc\",\n          \"0x163129eea9b2d7f90a9491c5b1b14501d8446f1f6c89d26f192946472f70d883\",\n          \"0x1a716c12432e1e01a617216c047e29823135fc00fb4cfb292e18d574606f35c0\",\n          \"0x2177d56ea2af7bf7e9b131d78074426092359e6ca2345b36d32c1d2f03566ac3\",\n          \"0x11856670bac1b7c78a8932e8c2137d3ea585b35beac3e92b61765cc3cdf68326\",\n          \"0x26df134ce46792b7f32f53ac7df54484a1da3922ab3cab65ceba02399b5ace1e\",\n          \"0x2a8f208fd791f7f5c13c533407af34929458b6fce3155f602331decc39a18d1b\",\n      },\n      {\n          \"0x25e061e5a751efd01b3390c7dd2d33bc4071216db8e513d573a5c9ba9b66913a\",\n          \"0x2c70d8ce1e96bc83662ff9edf413c8b1ddb7308b30770b8b0363bb16b410966b\",\n          \"0x0fdf631b3cbc1368612327cce61e043125889ca33418bf1b2795fa110bdf77bc\",\n          \"0x233d0ce0f5d908c63505bfca1434d15fc248b560ea827e14ee07d9498ddc8588\",\n          \"0x20741541c6dd548e731c08a36e05f26bcd37a4bf5bd8507b67748ff15b8eaa1e\",\n          \"0x06ceaea47f22a3a3f3cb1fd1a920bad815977860c229146ce63786165b6bac01\",\n          \"0x2516e1b23de252185b9b2c8753c52dad78073c4848830e0ad3b139d7e1043c7b\",\n          \"0x24202f1572d431b51140eab5b4305fff78babdd7726b235a0e8360b97ff1780f\",\n          \"0x275e5b9af3e37e1402656f0c71add7179326c94a3c508b97e3136325374d0086\",\n          \"0x043a8f7d69c75e225994410c0bbbb1b0cdedc772dd29f8b02444d05ab3daaaa2\",\n          \"0x2e7cc9c94b8356104b5c50ae98eec937238938d0b15fd25da84d80a7817a6bcb\",\n          \"0x143def6354020635267fb1edc79f0f36da06d1904800c28b2be63348fd2f7606\",\n          \"0x1c951d6176ed6399f36cb6b8ddece674c50fe1b2415ef397aaff428a29246a59\",\n          \"0x0ab51b71f8d9ba71c7319ad8657f90942a9701c88b7d6f04a51216475f40f779\",\n          \"0x26d4e18d12de6a7b7c071a2dc7fbe89ea4c65f6d647143fcc5984acae9088635\",\n          \"0x2eacc4ca1c50e55c175b62c30ac9a31e50d52c60ed7e7f8640bb920f4c69329a\",\n      },\n  };\n\n  constexpr static uint8_t kProof[] = {\n      197, 90,  54,  138, 254, 187, 187, 144, 27,  39,  20,  39,  15,  181, 249,\n      52,  63,  84,  95,  51,  45,  242, 210, 62,  170, 158, 176, 93,  60,  111,\n      37,  2,   93,  88,  146, 144, 43,  179, 6,   218, 194, 31,  174, 181, 184,\n      44,  226, 22,  117, 37,  50,  100, 239, 5,   38,  36,  177, 245, 5,   62,\n      67,  52,  53,  46,  84,  190, 108, 245, 192, 106, 51,  107, 85,  215, 124,\n      33,  178, 186, 27,  105, 218, 50,  59,  250, 73,  229, 251, 140, 191, 110,\n      167, 71,  86,  11,  127, 25,  197, 90,  54,  138, 254, 187, 187, 144, 27,\n      39,  20,  39,  15,  181, 249, 52,  63,  84,  95,  51,  45,  242, 210, 62,\n      170, 158, 176, 93,  60,  111, 37,  2,   93,  88,  146, 144, 43,  179, 6,\n      218, 194, 31,  174, 181, 184, 44,  226, 22,  117, 37,  50,  100, 239, 5,\n      38,  36,  177, 245, 5,   62,  67,  52,  53,  46,  84,  190, 108, 245, 192,\n      106, 51,  107, 85,  215, 124, 33,  178, 186, 27,  105, 218, 50,  59,  250,\n      73,  229, 251, 140, 191, 110, 167, 71,  86,  11,  127, 25,  15,  108, 79,\n      231, 72,  84,  56,  117, 61,  40,  248, 185, 208, 110, 150, 13,  117, 88,\n      226, 218, 102, 120, 33,  190, 111, 7,   135, 197, 183, 246, 172, 44,  22,\n      228, 172, 219, 254, 237, 163, 0,   10,  251, 101, 190, 60,  235, 177, 50,\n      81,  39,  245, 199, 147, 61,  207, 69,  242, 102, 103, 215, 193, 129, 74,\n      161, 49,  31,  185, 84,  59,  60,  197, 105, 162, 2,   73,  46,  192, 23,\n      112, 64,  184, 76,  138, 38,  135, 129, 7,   34,  254, 199, 199, 128, 203,\n      133, 125, 4,   7,   193, 249, 119, 43,  96,  224, 186, 80,  229, 177, 168,\n      68,  165, 160, 232, 250, 119, 40,  233, 224, 122, 147, 180, 215, 108, 101,\n      132, 148, 172, 39,  145, 155, 96,  69,  102, 243, 137, 112, 78,  242, 80,\n      91,  105, 121, 10,  50,  254, 172, 156, 161, 108, 106, 0,   49,  244, 77,\n      227, 250, 2,   241, 50,  80,  14,  167, 174, 122, 194, 14,  106, 139, 84,\n      233, 24,  202, 125, 123, 229, 196, 200, 149, 88,  110, 141, 209, 208, 32,\n      20,  184, 161, 4,   183, 72,  66,  77,  174, 122, 92,  96,  251, 2,   148,\n      175, 199, 140, 72,  172, 207, 191, 86,  237, 169, 161, 36,  54,  37,  120,\n      68,  72,  75,  34,  131, 253, 136, 155, 230, 167, 36,  102, 191, 16,  3,\n      178, 239, 166, 170, 93,  26,  16,  155, 212, 248, 241, 61,  87,  232, 92,\n      226, 73,  81,  194, 54,  103, 175, 237, 175, 51,  8,   101, 141, 1,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      184, 103, 65,  174, 109, 225, 89,  252, 161, 224, 217, 194, 126, 110, 105,\n      75,  66,  152, 227, 135, 90,  133, 7,   10,  97,  165, 139, 202, 145, 161,\n      151, 151, 42,  93,  124, 131, 213, 41,  224, 106, 205, 160, 84,  83,  192,\n      164, 91,  92,  117, 227, 188, 172, 70,  79,  136, 95,  223, 99,  129, 209,\n      11,  49,  89,  4,   229, 144, 159, 187, 187, 178, 215, 128, 29,  220, 161,\n      34,  115, 90,  56,  8,   223, 250, 67,  35,  89,  196, 146, 101, 84,  251,\n      228, 103, 168, 95,  157, 33,  185, 194, 131, 159, 28,  136, 166, 197, 30,\n      41,  212, 117, 36,  14,  230, 230, 54,  29,  207, 135, 111, 5,   94,  61,\n      70,  5,   110, 98,  119, 252, 82,  48,  33,  182, 224, 230, 183, 171, 84,\n      17,  96,  170, 243, 125, 224, 61,  200, 1,   11,  62,  231, 238, 65,  68,\n      71,  90,  24,  129, 195, 168, 209, 140, 242, 47,  229, 144, 159, 187, 187,\n      178, 215, 128, 29,  220, 161, 34,  115, 90,  56,  8,   223, 250, 67,  35,\n      89,  196, 146, 101, 84,  251, 228, 103, 168, 95,  157, 33,  185, 194, 131,\n      159, 28,  136, 166, 197, 30,  41,  212, 117, 36,  14,  230, 230, 54,  29,\n      207, 135, 111, 5,   94,  61,  70,  5,   110, 98,  119, 252, 82,  48,  33,\n      182, 224, 230, 183, 171, 84,  17,  96,  170, 243, 125, 224, 61,  200, 1,\n      11,  62,  231, 238, 65,  68,  71,  90,  24,  129, 195, 168, 209, 140, 242,\n      47,  53,  227, 137, 2,   139, 36,  164, 62,  244, 198, 188, 216, 134, 238,\n      144, 236, 133, 164, 224, 232, 244, 78,  56,  135, 135, 136, 239, 74,  38,\n      211, 108, 28,  1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   184, 235, 47,  21,  63,  131, 44,  138, 172, 226,\n      76,  79,  63,  61,  169, 239, 249, 107, 91,  189, 160, 68,  163, 31,  175,\n      159, 119, 132, 12,  242, 22,  35,  207, 232, 6,   157, 179, 53,  49,  203,\n      48,  85,  41,  199, 134, 181, 2,   7,   220, 3,   11,  203, 116, 240, 169,\n      227, 161, 216, 224, 155, 227, 118, 196, 37,  74,  66,  189, 21,  211, 53,\n      186, 198, 90,  255, 228, 74,  253, 108, 24,  166, 252, 119, 246, 14,  231,\n      128, 29,  84,  222, 49,  123, 77,  81,  225, 250, 26,  139, 187, 230, 151,\n      101, 85,  101, 102, 22,  192, 212, 74,  249, 57,  220, 201, 42,  68,  5,\n      143, 110, 71,  206, 255, 15,  6,   120, 88,  10,  200, 173, 29,  82,  83,\n      152, 7,   94,  159, 224, 21,  112, 106, 104, 93,  247, 1,   62,  210, 163,\n      221, 32,  169, 184, 82,  239, 15,  152, 210, 8,   209, 69,  38,  199, 41,\n      149, 71,  74,  8,   243, 186, 160, 59,  154, 110, 16,  161, 131, 2,   17,\n      94,  178, 126, 187, 237, 44,  56,  126, 134, 129, 93,  36,  28,  43,  243,\n      112, 3,   150, 21,  45,  201, 107, 4,   39,  129, 145, 20,  54,  101, 22,\n      175, 219, 126, 74,  99,  29,  254, 64,  34,  188, 220, 134, 26,  52,  149,\n      173, 81,  85,  31,  17,  14,  46,  92,  93,  190, 182, 131, 110, 38,  27,\n      150, 218, 44,  36,  215, 116, 49,  131, 57,  5,   171, 232, 160, 153, 194,\n      69,  171, 198, 216, 64,  6,   108, 166, 229, 28,  238, 244, 161, 158, 185,\n      60,  33,  129, 230, 119, 23,  143, 15,  245, 101, 205, 20,  18,  16,  98,\n      242, 54,  162, 135, 211, 40,  209, 35,  82,  81,  252, 151, 133, 131, 184,\n      147, 213, 29,  82,  245, 245, 132, 214, 35,  195, 171, 233, 111, 248, 125,\n      19,  91,  64,  53,  227, 179, 92,  57,  200, 25,  146, 111, 246, 213, 208,\n      41,  243, 240, 174, 63,  194, 246, 243, 42,  83,  86,  172, 152, 17,  197,\n      249, 149, 66,  66,  74,  45,  36,  225, 68,  28,  147, 47,  33,  225, 67,\n      167, 255, 72,  133, 63,  185, 40,  5,   192, 110, 162, 165, 39,  104, 123,\n      39,  184, 230, 227, 246, 44,  6,   165, 94,  173, 162, 133, 41,  20,  238,\n      230, 216, 95,  105, 102, 7,   149, 138, 183, 228, 140, 124, 92,  108, 232,\n      203, 142, 55,  230, 102, 215, 64,  50,  69,  186, 125, 133, 179, 227, 180,\n      8,   181, 46,  174, 81,  174, 59,  208, 166, 255, 49,  207, 166, 216, 179,\n      13,  128, 231, 17,  108, 233, 123, 180, 18,  139, 108, 250, 220, 31,  128,\n      78,  208, 36,  175, 64,  55,  183, 155, 146, 186, 81,  68,  183, 231, 226,\n      83,  248, 197, 135, 115, 56,  211, 163, 17,  244, 21,  251, 104, 175, 234,\n      144, 66,  63,  182, 17,  202, 136, 171, 152, 11,  22,  230, 123, 70,  56,\n      106, 0,   80,  150, 170, 13,  93,  117, 46,  209, 89,  71,  165, 186, 116,\n      58,  45,  120, 100, 34,  10,  2,   181, 23,  60,  231, 128, 117, 94,  6,\n      117, 183, 114, 103, 46,  34,  190, 148, 27,  15,  229, 94,  49,  86,  222,\n      35,  251, 181, 137, 0,   248, 210, 107, 16,  230, 226, 70,  16,  196, 68,\n      82,  52,  36,  72,  226, 87,  40,  41,  35,  75,  12,  143, 40,  127, 112,\n      5,   241, 94,  77,  158, 37,  199, 53,  106, 208, 17,  70,  125, 235, 164,\n      145, 166, 123, 80,  29,  11,  195, 173, 161, 59,  121, 5,   253, 87,  207,\n      127, 35,  199, 56,  186, 86,  173, 73,  41,  101, 170, 105, 45,  187, 203,\n      114, 155, 9,   220, 181, 25,  60,  62,  189, 186, 140, 109, 116, 191, 116,\n      12,  25,  107, 149, 253, 129, 208, 84,  50,  114, 140, 225, 14,  230, 9,\n      101, 114, 6,   42,  225, 168, 144, 130, 118, 127, 192, 132, 89,  97,  195,\n      132, 249, 66,  77,  28,  33,  197, 165, 22,  43,  162, 34,  205, 86,  124,\n      93,  8,   216, 38,  52,  203, 111, 248, 91,  133, 3,   58,  46,  111, 108,\n      84,  62,  19,  146, 228, 29,  206, 165, 50,  95,  237, 61,  50,  100, 160,\n      182, 145, 167, 11,  5,   214, 242, 93,  254, 82,  211, 155, 103, 163, 252,\n      255, 159, 75,  24,  195, 118, 103, 65,  51,  87,  123, 39,  77,  209, 21,\n      252, 88,  170, 253, 87,  14,  181, 242, 212, 248, 90,  139, 4,   29,  188,\n      106, 189, 93,  24,  187, 83,  100, 252, 136, 162, 215, 121, 106, 194, 192,\n      18,  125, 30,  95,  235, 152, 3,   8,   3,   139, 146, 78,  249, 157, 88,\n      253, 38,  38,  125, 67,  96,  100, 145, 252, 10,  196, 97,  232, 115, 218,\n      122, 159, 191, 49,  142, 236, 180, 57,  245, 24,  158, 45,  216, 220, 196,\n      3,   14,  140, 184, 252, 249, 254, 213, 204, 213, 142, 210, 121, 31,  22,\n      230, 171, 68,  58,  0,   215, 176, 228, 198, 26,  103, 1,   198, 85,  203,\n      131, 83,  228, 146, 174, 230, 201, 33,  250, 234, 88,  219, 29,  25,  38,\n      114, 34,  0,   146, 168, 103, 108, 0,   105, 47,  242, 40,  89,  155, 79,\n      209, 88,  253, 136, 149, 249, 78,  251, 52,  164, 133, 223, 62,  71,  15,\n      99,  46,  122, 220, 129, 126, 224, 168, 191, 142, 142, 138, 35,  76,  234,\n      137,\n  };\n\n  // clang-format off\n  constexpr static Point kAdviceCommitments[][3] = {\n    {\n      {\"0x02256f3c5db09eaa3ed2f22d335f543f34f9b50f2714271b90bbbbfe8a365ac5\",\n       \"0x1892bac12516e09b25b7acaa45dccc104fb325529df299fcb08b7fc250723b1a\"},\n      {\"0x2e3534433e05f5b1242605ef6432257516e22cb8b5ae1fc2da06b32b9092585d\",\n       \"0x30035ae0c7892f2d414997acee56077efc83c863be8eca1b40b563ef293b5f06\"},\n      {\"0x197f0b5647a76ebf8cfbe549fa3b32da691bbab2217cd7556b336ac0f56cbe54\",\n       \"0x125234fa5387f32f1959d867a9854afe20c9079047f05a483eca4d5535a4bb3c\"},\n    },\n    {\n      {\"0x02256f3c5db09eaa3ed2f22d335f543f34f9b50f2714271b90bbbbfe8a365ac5\",\n       \"0x1892bac12516e09b25b7acaa45dccc104fb325529df299fcb08b7fc250723b1a\"},\n      {\"0x2e3534433e05f5b1242605ef6432257516e22cb8b5ae1fc2da06b32b9092585d\",\n       \"0x30035ae0c7892f2d414997acee56077efc83c863be8eca1b40b563ef293b5f06\"},\n      {\"0x197f0b5647a76ebf8cfbe549fa3b32da691bbab2217cd7556b336ac0f56cbe54\",\n       \"0x125234fa5387f32f1959d867a9854afe20c9079047f05a483eca4d5535a4bb3c\"},\n    },\n  };\n  // clang-format on\n\n  constexpr static std::string_view kTheta =\n      \"0x14f3b93a80e248a02909e66b2eba85707fd93f0704e4c8cfd3f78f01c5c1f16f\";\n\n  constexpr static std::string_view kBeta =\n      \"0x1308cd01f603ad39104efce7c7e4ab02660f5e4a2560a52a544e54255d989c03\";\n\n  constexpr static std::string_view kGamma =\n      \"0x2166363a3350c7ca72d613cc803216b94bb14b897a573748421cff8f75485369\";\n\n  // clang-format off\n  constexpr static Point kPermutationProductCommitments[][4] = {\n      {\n          {\"0x2cacf6b7c587076fbe217866dae258750d966ed0b9f8283d75385448e74f6c0f\",\n            \"0x17cc0430326288fea27ea90c860e1be5b7b288085a0f60d19a9b72333ad1bf4e\"},\n          {\"0x214a81c1d76766f245cf3d93c7f5275132b1eb3cbe65fb0a00a3edfedbace416\",\n            \"0x09f49fb47cccedc71bbad7498af66b01256a9dc2e8e511b29e72d88f324688af\"},\n          {\"0x047d85cb80c7c7fe22078187268a4cb8407017c02e4902a269c53c3b54b91f31\",\n            \"0x2c95183c5f11dfc9b4109fa3ed22f64432075bcc661f5c5d4ca4c9f5082d9bdc\"},\n          {\"0x1127ac9484656cd7b4937ae0e92877fae8a0a544a8b1e550bae0602b77f9c107\",\n            \"0x00aab73597404e28dc0b410b17176e4e1a18e8dfce1523e2608cd049fa77664b\"},\n      },\n      {\n          {\"0x0e5032f102fae34df431006a6ca19cacfe320a79695b50f24e7089f36645609b\",\n            \"0x136e8ad75e6513526b2fed4a59224bd424a84c27c3fe990783b228b8aad7d878\"},\n          {\"0x2e4d4248b704a1b81420d0d18d6e5895c8c4e57b7dca18e9548b6a0ec27aaea7\",\n            \"0x07921d99e66d6d6287d42133ace652c1e6ecba049895e867cc7bfd16c066d29d\"},\n          {\"0x24a7e69b88fd83224b484478253624a1a9ed56bfcfac488cc7af9402fb605c7a\",\n            \"0x1ac17d343d1efe795d60c3d23a9a46d6d2508a9c2c92ddb8ef93780f4a0ff45e\"},\n          {\"0x0d650833afedaf6736c25149e25ce8573df1f8d49b101a5daaa6efb20310bf66\",\n            \"0x093dd3d8baea83c598c5dd0597c3486a685c09162ed1cf61f040e36308e0cd0f\"},\n      },\n  };\n  // clang-format on\n\n  constexpr static std::string_view kY =\n      \"0x1460fd3f8816fa92d0992f1d6bf739a43414bb029d725259ae620bf2e8deaaff\";\n\n  constexpr static Point kVanishingHPolyCommitments[] = {\n      {\"0x1797a191ca8ba5610a07855a87e398424b696e7ec2d9e0a1fc59e16dae4167b8\",\n       \"0x041e9a88817cd6f2ccd9792c33eddd65c85d2e8cf48bcafd1010e56df277117f\"},\n      {\"0x0459310bd18163df5f884f46acbce3755c5ba4c05354a0cd6ae029d5837c5d2a\",\n       \"0x184d381109726937f9277181d1fd768e4bdbb16b6a48f29ffb3a46ca714083da\"},\n  };\n\n  constexpr static std::string_view kX =\n      \"0x1a31b79708935088f60b0c724b2f04ce2e3f0ff886cfb5717e3d66a2d2da59b0\";\n\n  constexpr static std::string_view kAdviceEvals[][3] = {\n      {\n          \"0x219d5fa867e4fb546592c4592343fadf08385a7322a1dc1d80d7b2bbbb9f90e5\",\n          \"0x3052fc77626e05463d5e056f87cf1d36e6e60e2475d4291ec5a6881c9f83c2b9\",\n          \"0x2ff28cd1a8c381185a474441eee73e0b01c83de07df3aa601154abb7e6e0b621\",\n      },\n      {\n          \"0x219d5fa867e4fb546592c4592343fadf08385a7322a1dc1d80d7b2bbbb9f90e5\",\n          \"0x3052fc77626e05463d5e056f87cf1d36e6e60e2475d4291ec5a6881c9f83c2b9\",\n          \"0x2ff28cd1a8c381185a474441eee73e0b01c83de07df3aa601154abb7e6e0b621\",\n      },\n  };\n\n  constexpr static std::string_view kFixedEvals[] = {\n      \"0x1c6cd3264aef888787384ef4e8e0a485ec90ee86d8bcc6f43ea4248b0289e335\",\n  };\n\n  constexpr static std::string_view kCommonPermutationEvals[] = {\n      \"0x2316f20c84779faf1fa344a0bd5b6bf9efa93d3f4f4ce2ac8a2c833f152febb8\",\n      \"0x25c476e39be0d8a1e3a9f074cb0b03dc0702b586c7295530cb3135b39d06e8cf\",\n      \"0x1afae1514d7b31de541d80e70ef677fca6186cfd4ae4ff5ac6ba35d315bd424a\",\n      \"0x1dadc80a5878060fffce476e8f05442ac9dc39f94ad4c0166665556597e6bb8b\",\n  };\n\n  constexpr static std::string_view kPermutationProductEvals[][4] = {\n      {\n          \"0x29c72645d108d2980fef52b8a920dda3d23e01f75d686a7015e09f5e07985352\",\n          \"0x0640d8c6ab45c299a0e8ab0539833174d7242cda961b266e83b6be5d5c2e0e11\",\n          \"0x2f931c44e1242d4a424295f9c51198ac56532af3f6c23faef0f329d0d5f66f92\",\n          \"0x24d04e801fdcfa6c8b12b47be96c11e7800db3d8a6cf31ffa6d03bae51ae2eb5\",\n      },\n      {\n          \"0x020a2264782d3a74baa54759d12e755d0daa9650006a38467be6160b98ab88ca\",\n          \"0x2d69aa652949ad56ba38c7237fcf57fd05793ba1adc30b1d507ba691a4eb7d46\",\n          \"0x0ba791b6a064323ded5f32a5ce1de492133e546c6f2e3a03855bf86fcb3426d8\",\n          \"0x18f539b4ec8e31bf9f7ada73e861c40afc916460437d2626fd589df94e928b03\",\n      },\n  };\n\n  constexpr static std::string_view kPermutationProductNextEvals[][4] = {\n      {\n          \"0x0370f32b1c245d81867e382cedbb7eb25e110283a1106e9a3ba0baf3084a4795\",\n          \"0x23d128d387a236f262101214cd65f50f8f1777e681213cb99ea1f4ee1ce5a66c\",\n          \"0x142985a2ad5ea5062cf6e3e6b8277b6827a5a26ec00528b93f8548ffa743e121\",\n          \"0x11b63f4290eaaf68fb15f411a3d3387387c5f853e2e7b74451ba929bb73740af\",\n      },\n      {\n          \"0x106bd2f80089b5fb23de56315ee50f1b94be222e6772b775065e7580e73c17b5\",\n          \"0x09e60ee18c723254d081fd956b190c74bf746d8cbabd3e3c19b5dc099b72cbbb\",\n          \"0x0e57fdaa58fc15d14d277b5733416776c3184b9ffffca3679bd352fe5df2d605\",\n          \"0x01671ac6e4b0d7003a44abe6161f79d28ed5ccd5fef9fcb88c0e03c4dcd82d9e\",\n      },\n  };\n\n  constexpr static std::string_view kPermutationProductLastEvals[][4] = {\n      {\n          \"0x1f5551ad95341a86dcbc2240fe1d634a7edbaf16653614918127046bc92d1596\",\n          \"0x19c8395cb3e335405b137df86fe9abc323d684f5f5521dd593b8838597fc5152\",\n          \"0x08b4e3b3857dba453240d766e6378ecbe86c5c7c8ce4b78a950766695fd8e6ee\",\n          \"\",\n      },\n      {\n          \"0x11d06a35c7259e4d5ef105707f288f0c4b23292857e24824345244c41046e2e6\",\n          \"0x085d7c56cd22a22b16a5c5211c4d42f984c3615984c07f768290a8e12a067265\",\n          \"0x080398eb5f1e7d12c0c26a79d7a288fc6453bb185dbd6abc1d048b5af8d4f2b5\",\n          \"\",\n      },\n  };\n\n  static void TestConfig(Fibonacci1Config<F>& config) {\n    std::array<AdviceColumnKey, 3> expected_advice = {\n        AdviceColumnKey(0),\n        AdviceColumnKey(1),\n        AdviceColumnKey(2),\n    };\n    EXPECT_EQ(config.advice, expected_advice);\n    EXPECT_EQ(config.instance, InstanceColumnKey(0));\n    EXPECT_EQ(config.selector, Selector::Simple(0));\n  }\n\n  static std::vector<Evals> GetInstanceColumns() {\n    F a(1);\n    F b(1);\n    F out(55);\n    std::vector<F> instance_column = {std::move(a), std::move(b),\n                                      std::move(out)};\n    return std::vector<Evals>{Evals(std::move(instance_column))};\n  }\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_EXAMPLES_FIBONACCI_FIBONACCI1_CIRCUIT_TEST_DATA_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/examples/fibonacci/fibonacci2_circuit.h",
    "content": "#ifndef TACHYON_ZK_PLONK_EXAMPLES_FIBONACCI_FIBONACCI2_CIRCUIT_H_\n#define TACHYON_ZK_PLONK_EXAMPLES_FIBONACCI_FIBONACCI2_CIRCUIT_H_\n\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/zk/plonk/constraint_system/circuit.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F>\nstruct Fibonacci2Config {\n  using Field = F;\n\n  Fibonacci2Config(const AdviceColumnKey& advice, Selector selector,\n                   const InstanceColumnKey& instance)\n      : advice(advice), selector(selector), instance(instance) {}\n\n  Fibonacci2Config Clone() const { return {advice, selector, instance}; }\n\n  AdviceColumnKey advice;\n  Selector selector;\n  InstanceColumnKey instance;\n};\n\ntemplate <typename F>\nclass Fibonacci2Chip {\n public:\n  explicit Fibonacci2Chip(const Fibonacci2Config<F>& config)\n      : config_(config) {}\n\n  static Fibonacci2Config<F> Configure(ConstraintSystem<F>& meta,\n                                       const AdviceColumnKey& advice,\n                                       const InstanceColumnKey& instance) {\n    Selector selector = meta.CreateSimpleSelector();\n\n    meta.EnableEquality(advice);\n    meta.EnableEquality(instance);\n\n    meta.CreateGate(\"add\", [selector, &advice](VirtualCells<F>& meta) {\n      //\n      // advice | selector\n      //   a    |   s\n      //   b    |\n      //   c    |\n      //\n      std::unique_ptr<Expression<F>> s = meta.QuerySelector(selector);\n      std::unique_ptr<Expression<F>> a =\n          meta.QueryAdvice(advice, Rotation::Cur());\n      std::unique_ptr<Expression<F>> b =\n          meta.QueryAdvice(advice, Rotation::Next());\n      std::unique_ptr<Expression<F>> c = meta.QueryAdvice(advice, Rotation(2));\n      std::vector<Constraint<F>> constraints;\n      constraints.emplace_back(std::move(s) *\n                               (std::move(a) + std::move(b) - std::move(c)));\n      return constraints;\n    });\n\n    return Fibonacci2Config<F>(advice, selector, instance);\n  }\n\n  AssignedCell<F> Assign(Layouter<F>* layouter, RowIndex n_rows) const {\n    AssignedCell<F> ret;\n    layouter->AssignRegion(\n        \"entire fibonacci table\", [this, &ret, n_rows](Region<F>& region) {\n          config_.selector.Enable(region, 0);\n          config_.selector.Enable(region, 1);\n\n          AssignedCell<F> a_cell = region.AssignAdviceFromInstance(\n              \"1\", config_.instance, 0, config_.advice, 0);\n          AssignedCell<F> b_cell = region.AssignAdviceFromInstance(\n              \"1\", config_.instance, 1, config_.advice, 1);\n\n          for (RowIndex row = 2; row < n_rows; ++row) {\n            if (row < n_rows - 2) {\n              config_.selector.Enable(region, row);\n            }\n\n            const AssignedCell<F> c_cell = region.AssignAdvice(\n                \"advice\", config_.advice, row, [&a_cell, &b_cell]() {\n                  return a_cell.value() + b_cell.value();\n                });\n\n            a_cell = b_cell;\n            b_cell = c_cell;\n          }\n\n          ret = b_cell;\n        });\n    return ret;\n  }\n\n  void ExposePublic(Layouter<F>* layouter, const AssignedCell<F>& cell,\n                    RowIndex row) const {\n    layouter->ConstrainInstance(cell.cell(), config_.instance, row);\n  }\n\n private:\n  Fibonacci2Config<F> config_;\n};\n\ntemplate <typename F, template <typename> class _FloorPlanner>\nclass Fibonacci2Circuit : public Circuit<Fibonacci2Config<F>> {\n public:\n  using FloorPlanner = _FloorPlanner<Fibonacci2Circuit<F, _FloorPlanner>>;\n\n  std::unique_ptr<Circuit<Fibonacci2Config<F>>> WithoutWitness()\n      const override {\n    return std::make_unique<Fibonacci2Circuit>();\n  }\n\n  static Fibonacci2Config<F> Configure(ConstraintSystem<F>& meta) {\n    AdviceColumnKey advice = meta.CreateAdviceColumn();\n    InstanceColumnKey instance = meta.CreateInstanceColumn();\n    return Fibonacci2Chip<F>::Configure(meta, advice, instance);\n  }\n\n  void Synthesize(Fibonacci2Config<F>&& config,\n                  Layouter<F>* layouter) const override {\n    Fibonacci2Chip<F> fibonacci2_chip(std::move(config));\n\n    AssignedCell<F> out_cell =\n        fibonacci2_chip.Assign(layouter->Namespace(\"entire table\").get(), 10);\n\n    fibonacci2_chip.ExposePublic(layouter->Namespace(\"out\").get(), out_cell, 2);\n  }\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_EXAMPLES_FIBONACCI_FIBONACCI2_CIRCUIT_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/examples/fibonacci/fibonacci2_circuit_test.cc",
    "content": "#include \"tachyon/zk/plonk/examples/fibonacci/fibonacci2_circuit.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/zk/plonk/examples/circuit_test.h\"\n#include \"tachyon/zk/plonk/examples/fibonacci/fibonacci2_circuit_test_data.h\"\n#include \"tachyon/zk/plonk/layout/floor_planner/simple_floor_planner.h\"\n#include \"tachyon/zk/plonk/layout/floor_planner/v1/v1_floor_planner.h\"\n\nnamespace tachyon::zk::plonk {\n\nnamespace {\n\ntemplate <typename TestArguments>\nclass Fibonacci2CircuitTest\n    : public CircuitTest<TestArguments,\n                         Fibonacci2TestData<typename TestArguments::Circuit,\n                                            typename TestArguments::PS>> {};\n\n}  // namespace\n\nusing Fibonacci2TestArgumentsList = testing::Types<\n    TestArguments<Fibonacci2Circuit<BN254SHPlonk::Field, SimpleFloorPlanner>,\n                  BN254SHPlonkHalo2>,\n    TestArguments<Fibonacci2Circuit<BN254SHPlonk::Field, V1FloorPlanner>,\n                  BN254SHPlonkHalo2>>;\n\nTYPED_TEST_SUITE(Fibonacci2CircuitTest, Fibonacci2TestArgumentsList);\n\nTYPED_TEST(Fibonacci2CircuitTest, Configure) { this->ConfigureTest(); }\nTYPED_TEST(Fibonacci2CircuitTest, Synthesize) { this->SynthesizeTest(); }\nTYPED_TEST(Fibonacci2CircuitTest, LoadVerifyingKey) {\n  this->LoadVerifyingKeyTest();\n}\nTYPED_TEST(Fibonacci2CircuitTest, LoadProvingKey) {\n  this->LoadProvingKeyTest();\n}\nTYPED_TEST(Fibonacci2CircuitTest, CreateProof) { this->CreateProofTest(); }\nTYPED_TEST(Fibonacci2CircuitTest, VerifyProof) { this->VerifyProofTest(); }\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/examples/fibonacci/fibonacci2_circuit_test_data.h",
    "content": "#ifndef TACHYON_ZK_PLONK_EXAMPLES_FIBONACCI_FIBONACCI2_CIRCUIT_TEST_DATA_H_\n#define TACHYON_ZK_PLONK_EXAMPLES_FIBONACCI_FIBONACCI2_CIRCUIT_TEST_DATA_H_\n\n#include <stdint.h>\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/zk/plonk/examples/circuit_test_data.h\"\n#include \"tachyon/zk/plonk/examples/circuit_test_type_traits.h\"\n#include \"tachyon/zk/plonk/examples/point.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename Circuit, typename PS>\nclass Fibonacci2TestData : public CircuitTestData<Circuit, PS> {\n public:\n  using PCS = typename PS::PCS;\n  using F = typename PCS::Field;\n  using Evals = typename PCS::Evals;\n\n  constexpr static bool kAssemblyPermutationColumnsFlag = true;\n  constexpr static bool kCycleStoreMappingFlag = true;\n  constexpr static bool kCycleStoreAuxFlag = true;\n  constexpr static bool kCycleStoreSizesFlag = true;\n  constexpr static bool kLFirstFlag = true;\n  constexpr static bool kLLastFlag = true;\n  constexpr static bool kLActiveRowFlag = true;\n  constexpr static bool kFixedColumnsFlag = true;\n  constexpr static bool kFixedPolysFlag = true;\n  constexpr static bool kPermutationsColumnsFlag = true;\n  constexpr static bool kPermutationsPolysFlag = true;\n  constexpr static bool kAdviceCommitmentsFlag = true;\n  constexpr static bool kPermutationProductCommitmentsFlag = true;\n  constexpr static bool kAdviceEvalsFlag = true;\n  constexpr static bool kFixedEvalsFlag = true;\n  constexpr static bool kCommonPermutationEvalsFlag = true;\n  constexpr static bool kPermutationProductEvalsFlag = true;\n  constexpr static bool kPermutationProductNextEvalsFlag = true;\n  constexpr static bool kPermutationProductLastEvalsFlag = true;\n\n  constexpr static size_t kN = 16;\n\n  // clang-format off\n  constexpr static std::string_view kPinnedConstraintSystem =\n      \"PinnedConstraintSystem { \"\n        \"num_fixed_columns: 0, \"\n        \"num_advice_columns: 1, \"\n        \"num_instance_columns: 1, \"\n        \"num_selectors: 1, \"\n        \"gates: [Product(\"\n          \"Selector(Selector(0, true)), \"\n          \"Sum(\"\n            \"Sum(\"\n              \"Advice { \"\n                \"query_index: 0, \"\n                \"column_index: 0, \"\n                \"rotation: Rotation(0) \"\n              \"}, \"\n              \"Advice { \"\n                \"query_index: 1, \"\n                \"column_index: 0, \"\n                \"rotation: Rotation(1) \"\n              \"}\"\n            \"), \"\n            \"Negated(Advice { \"\n              \"query_index: 2, \"\n              \"column_index: 0, \"\n              \"rotation: Rotation(2) \"\n            \"})\"\n          \")\"\n        \")], \"\n        \"advice_queries: [\"\n          \"(\"\n            \"Column { \"\n              \"index: 0, \"\n              \"column_type: Advice \"\n            \"}, \"\n            \"Rotation(0)\"\n          \"), \"\n          \"(\"\n            \"Column { \"\n              \"index: 0, \"\n              \"column_type: Advice \"\n            \"}, \"\n            \"Rotation(1)\"\n          \"), \"\n          \"(\"\n            \"Column { \"\n              \"index: 0, \"\n              \"column_type: Advice \"\n            \"}, \"\n            \"Rotation(2)\"\n          \")\"\n        \"], \"\n        \"instance_queries: [(\"\n          \"Column { \"\n            \"index: 0, \"\n            \"column_type: Instance \"\n          \"}, \"\n          \"Rotation(0)\"\n        \")], \"\n        \"fixed_queries: [], \"\n        \"permutation: Argument { columns: [\"\n          \"Column { \"\n            \"index: 0, \"\n            \"column_type: Advice \"\n          \"}, \"\n          \"Column { \"\n            \"index: 0, \"\n            \"column_type: Instance \"\n          \"}\"\n        \"] }, \"\n        \"lookups: [], \"\n        \"constants: [], \"\n        \"minimum_degree: None \"\n      \"}\";\n  // clang-format on\n\n  constexpr static AnyColumnKey kAssemblyPermutationColumns[] = {\n      AdviceColumnKey(0),\n      InstanceColumnKey(0),\n  };\n\n  // clang-format off\n    constexpr static Label kCycleStoreMapping[][kN] = {\n        {{1, 0}, {1, 1},  {0, 2},  {0, 3},  {0, 4},  {0, 5},  {0, 6},  {0, 7},\n         {0, 8}, {1, 2}, {0, 10}, {0, 11}, {0, 12}, {0, 13}, {0, 14}, {0, 15}},\n        {{0, 0}, {0, 1},  {0, 9},  {1, 3},  {1, 4},  {1, 5},  {1, 6},  {1, 7},\n         {1, 8}, {1, 9}, {1, 10}, {1, 11}, {1, 12}, {1, 13}, {1, 14}, {1, 15}},\n    };\n\n    constexpr static Label kCycleStoreAux[][kN] = {\n        {{0, 0}, {0, 1},  {0, 2},  {0, 3},  {0, 4},  {0, 5},  {0, 6},  {0, 7},\n         {0, 8}, {0, 9}, {0, 10}, {0, 11}, {0, 12}, {0, 13}, {0, 14}, {0, 15}},\n        {{0, 0}, {0, 1},  {0, 9},  {1, 3},  {1, 4},  {1, 5},  {1, 6},  {1, 7},\n         {1, 8}, {1, 9}, {1, 10}, {1, 11}, {1, 12}, {1, 13}, {1, 14}, {1, 15}},\n    };\n  // clang-format on\n\n  constexpr static size_t kCycleStoreSizes[][kN] = {\n      {2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1},\n      {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},\n  };\n\n  // clang-format off\n  constexpr static bool kSelectors[][kN] = {\n      { true,  true,  true,  true,  true,  true,  true,  true,\n       false, false, false, false, false, false, false, false}\n  };\n  // clang-format on\n\n  // clang-format off\n  constexpr static std::string_view kPinnedVerifyingKey =\n      \"PinnedVerificationKey { \"\n        \"base_modulus: \\\"0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47\\\", \"\n        \"scalar_modulus: \\\"0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001\\\", \"\n        \"domain: PinnedEvaluationDomain { \"\n          \"k: 4, \"\n          \"extended_k: 5, \"\n          \"omega: 0x21082ca216cbbf4e1c6e4f4594dd508c996dfbe1174efb98b11509c6e306460b \"\n        \"}, \"\n        \"cs: PinnedConstraintSystem { \"\n          \"num_fixed_columns: 1, \"\n          \"num_advice_columns: 1, \"\n          \"num_instance_columns: 1, \"\n          \"num_selectors: 1, \"\n          \"gates: [Product(\"\n            \"Fixed { \"\n              \"query_index: 0, \"\n              \"column_index: 0, \"\n              \"rotation: Rotation(0) \"\n            \"}, \"\n            \"Sum(\"\n              \"Sum(\"\n                \"Advice { \"\n                  \"query_index: 0, \"\n                  \"column_index: 0, \"\n                  \"rotation: Rotation(0) \"\n                \"}, \"\n                \"Advice { \"\n                  \"query_index: 1, \"\n                  \"column_index: 0, \"\n                  \"rotation: Rotation(1) \"\n                \"}\"\n              \"), \"\n              \"Negated(Advice { \"\n                \"query_index: 2, \"\n                \"column_index: 0, \"\n                \"rotation: Rotation(2) \"\n              \"})\"\n            \")\"\n          \")], \"\n          \"advice_queries: [\"\n            \"(\"\n              \"Column { \"\n                \"index: 0, \"\n                \"column_type: Advice \"\n              \"}, \"\n              \"Rotation(0)\"\n            \"), \"\n            \"(\"\n              \"Column { \"\n                \"index: 0, \"\n                \"column_type: Advice \"\n              \"}, \"\n              \"Rotation(1)\"\n            \"), \"\n            \"(\"\n              \"Column { \"\n                \"index: 0, \"\n                \"column_type: Advice \"\n              \"}, \"\n              \"Rotation(2)\"\n            \")\"\n          \"], \"\n          \"instance_queries: [(\"\n            \"Column { \"\n              \"index: 0, \"\n              \"column_type: Instance \"\n            \"}, \"\n            \"Rotation(0)\"\n          \")], \"\n          \"fixed_queries: [(\"\n            \"Column { \"\n              \"index: 0, \"\n              \"column_type: Fixed \"\n            \"}, \"\n            \"Rotation(0)\"\n          \")], \"\n          \"permutation: Argument { columns: [\"\n            \"Column { \"\n              \"index: 0, \"\n              \"column_type: Advice \"\n            \"}, \"\n            \"Column { \"\n              \"index: 0, \"\n              \"column_type: Instance \"\n            \"}\"\n          \"] }, \"\n          \"lookups: [], \"\n          \"constants: [], \"\n          \"minimum_degree: None \"\n        \"}, \"\n        \"fixed_commitments: [\"\n          \"(0x297ff8a661d1fa1196c065b6fb7df901fb16b5b83168ddca6c7749d963cc967a, \"\n            \"0x1a7b1f2b5f3e35fc4c706ece6b8f646e95904f9aa417f8a31fd37a41da167ec1)\"\n        \"], \"\n        \"permutation: VerifyingKey { commitments: [\"\n          \"(0x179d4bdbaa0d2b6a977d418a8a90d5fe4d9162cf6569dcab6f2a7ab150cf34f7, \"\n            \"0x00638bb01b57c58d95e93d44a6a6bc126133db05161925701f8d0f16bc8f2049), \"\n          \"(0x0356186a0d83b4475fc2e5b01191cff523ee5cc46ef3fa65eff3f611b69aeae6, \"\n            \"0x13b0cc2fd4f1145e719c43d03ff5dfad5c0bb3e0642a5a40ae98f19e6a3b76d7)\"\n        \"] } \"\n      \"}\";\n  // clang-format on\n\n  constexpr static std::string_view kTranscriptRepr =\n      \"0x0b95ba00d9df3c7f61587edd2ada6ca715d62dc579333da7b80365838146a2a1\";\n\n  constexpr static std::string_view kLFirst[] = {\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n  };\n\n  constexpr static std::string_view kLLast[] = {\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2014447de15a99b6df03833e95f96ae1299c9ec6ff990b6e75fa3b3b04846a57\",\n      \"0x0f1f5883e65f820d14d56342dc92fd12a944d4cbbdce5377b7439bd07108fc9d\",\n      \"0x02b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e8\",\n      \"0x030644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000\",\n      \"0x105009f4ffd70672d94cc277eb87ed7bfe9749817a206522cde7ba58eb7b95aa\",\n      \"0x2144f5eefad21e1ca37ae273a4ee5b4a7eef137cbbeb1d198c9e59c37ef70364\",\n      \"0x2db11694c4a58b3789868bd388165969c30dc1120a37fff09a991abf43e42a19\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2014447de15a99b6df03833e95f96ae1299c9ec6ff990b6e75fa3b3b04846a57\",\n      \"0x0f1f5883e65f820d14d56342dc92fd12a944d4cbbdce5377b7439bd07108fc9d\",\n      \"0x02b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e8\",\n      \"0x030644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000\",\n      \"0x105009f4ffd70672d94cc277eb87ed7bfe9749817a206522cde7ba58eb7b95aa\",\n      \"0x2144f5eefad21e1ca37ae273a4ee5b4a7eef137cbbeb1d198c9e59c37ef70364\",\n      \"0x2db11694c4a58b3789868bd388165969c30dc1120a37fff09a991abf43e42a19\",\n  };\n\n  constexpr static std::string_view kLActiveRow[] = {\n      \"0x12259d6b14729c0fa51e1a2470908122ef13771b2da58a367974bc177a000001\",\n      \"0x1a8133201ba2fe22aff30c7fd04fadcb17ceab0715a13371b06d35acd9695b3d\",\n      \"0x0d49c50dd1c3ec703dc7be1c836fd7f62c140afcf284ce19b9a99affac7b95aa\",\n      \"0x117dae38a05bcb8c7c290ee16cec493d73ef8dafa5c61fa4a2efd9a39e63abf4\",\n      \"0x0c19139cb84c680a79505ee7747ae78cd6c196473632bc6ea3057c773208fc9d\",\n      \"0x136156e428a2662bc2fddfd3b39f6475dafecb8699a611f3da6edd22c3479af1\",\n      \"0x2aaad1ad96927134ee0187781ffe43e3f08a828d829c68e7865afb6604e42a19\",\n      \"0x28d7d254c17b7ea40ebc4659996adacebd0d8f52d021284040d407c2f33b896f\",\n      \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n      \"0x1ef7221577b1e8f8cebdcb2fcd10296b6d9d43267b395c820757c0fc87681d81\",\n      \"0x1d0dff96b3477fb4437e7ee32de1555b5719604277fd746561bc1be1c5846a57\",\n      \"0x1be193603aacb384678281793fa8f20b949cf5976d9493017ba8a357ee49da57\",\n      \"0x1e3eb107ccbf041a07f5de183cd645c4ac6bd4f8344f861078603a6a3ff70364\",\n      \"0x20080468beb85b16c9f71b3ea2ce10fb6cdc81c346721c888ebc9109900adec6\",\n      \"0x30114169cfaa9b194b94fb3e12d441cabad6d0fa619f4a28d8ecb10f5d1bd5e9\",\n      \"0x2edcc3ce4ec47abd9b83b31a4db9571236223b590c30997fd30ce24f7bf2fdd6\",\n  };\n\n  constexpr static std::string_view kFixedColumns[][kN] = {\n      {\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n      },\n  };\n\n  constexpr static std::string_view kFixedPolys[][kN] = {\n      {\n          \"0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000001\",\n          \"0x1db5b8e23dbf6d914e37d939eb6b037c619b3e5ea827cdb2b030fc247bdc9dcb\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x15612923e039d29887b96603ef6ab434a2c021b8674489fcfa35d3972944e837\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x186635fde662f3b15c5e1b5fc456d148c26786b9cda347e81f7f4c2f8c9761ed\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x14a1b21f8b9c269f87d74740cf8c84a3046338799106ae503c4dfb0f0b0b250e\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x15b61284f96f4584f96ef5bee1c4a8ae7eca32c5d97b942edf17bbd266f4daf3\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x11f18ea69ea8787324e8219fecfa5c08c0c5e4859cdefa96fbe66ab1e5689e14\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x14f69b80a4d1998bf98cd6fbc1e6791ce06d4987033db882212fe34a48bb17ca\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0ca20bc2474bfe93330e63c5c5e629d521922ce0c25a74cc6b34babcf6236236\",\n      },\n  };\n\n  constexpr static std::string_view kPermutationsColumns[][kN] = {\n      {\n          \"0x09226b6e22c6f0ca64ec26aad4c86e715b5f898e5e963f25870e56bbe533e9a2\",\n          \"0x133f51f46c2a51201fbb89dc1d6868acfef0f7ce0e572ccb98e04e99eac3ea36\",\n          \"0x2b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e80\",\n          \"0x107aab49e65a67f9da9cd2abf78be38bd9dc1d5db39f81de36bcfa5b4b039043\",\n          \"0x30644e72e131a029048b6e193fd841045cea24f6fd736bec231204708f703636\",\n          \"0x2290ee31c482cf92b79b1944db1c0147635e9004db8c3b9d13644bef31ec3bd3\",\n          \"0x1d59376149b959ccbd157ac850893a6f07c2d99b3852513ab8d01be8e846a566\",\n          \"0x2d8040c3a09c49698c53bfcb514d55a5b39e9b17cb093d128b8783adb8cbd723\",\n          \"0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000\",\n          \"0x1240a374ee6f71e12df10a129946ca9ba824f58a50e3a58b4c68dd5590b74ad8\",\n          \"0x0530d09118705106cbb4a786ead16926d5d174e181a26686af5448492e42a181\",\n          \"0x1fe9a328fad7382fddb3730a89f574d14e57caeac619eeb30d24fb38a4fc6fbe\",\n          \"0x0000000000000000b3c4d79d41a91758cb49c3517c4604a520cff123608fc9cb\",\n          \"0x0dd360411caed09700b52c71a6655715c4d558439e2d34f4307da9a4be13c42e\",\n          \"0x130b17119778465cfb3acaee30f81dee20710ead41671f568b11d9ab07b95a9b\",\n          \"0x02e40daf409556c02bfc85eb303402b774954d30aeb0337eb85a71e6373428de\",\n      },\n      {\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x21082ca216cbbf4e1c6e4f4594dd508c996dfbe1174efb98b11509c6e306460b\",\n          \"0x0f5c21d0ca65e0db9be1f670eca407d08ec5ec67626a74f892ccebcd0cf9b9f6\",\n          \"0x009553089042fe83ab570b54e4e64d307d8e8b20d568e782ece3926981d0a96c\",\n          \"0x14a6c152ace4b16a42e1377e400798e15ac60320c21d90277890dbdd551d1912\",\n          \"0x035992598be4d2ae5334f24f30355a1fca7f0e28762a178d79e993dca70eaabf\",\n          \"0x03b645319eb70d7a692ea8c87fbcab1f292cd502071d9ffb7291ebe28356aa07\",\n          \"0x231e38741f5c3f0ce2509bd3289b09a893b14b136eea6ed00cec9ac9271c9db5\",\n          \"0x2741e304be6aaf5f53641f0bacb8e9ebccd45eba1b23316bbcd39ed80acc165f\",\n          \"0x1d24fc7e75074f099894bbda6418efb02942f07a6b6243c5ab01a6fa053c15cb\",\n          \"0x1e23aafdf2c22e488a5f3ba3e83a8dc1800ef2be28d5cb05f779183e5f48b529\",\n          \"0x2fcefb6a50eea1a60cf93a619c9b0b2caaa55d27a450890e56fe632a6e2f5695\",\n          \"0x1bbd8d20344ceebf756f0e384179bf7bcd6de527b79be069cb5119b69ae2e6ef\",\n          \"0x2d0abc19554ccd7b651b5367514bfe3d5db4da20038f5903c9f861b748f15542\",\n          \"0x2cae0941427a92af4f219cee01c4ad3dff071346729bd095d15009b16ca955fa\",\n          \"0x0d4615fec1d5611cd5ffa9e358e64eb494829d350acf01c136f55acac8e3624c\",\n      },\n  };\n\n  constexpr static std::string_view kPermutationsPolys[][kN] = {\n      {\n          \"0x05f06af4a5e9253f56ae900500cf8fa172aa95f2f378a820db039783f50af1eb\",\n          \"0x0f0fb0d263f5bb14543ae200a13af2e0780d1a4b90f748455c8a62ac18fa3e92\",\n          \"0x04fef3b4de894e2db58283cef83f883f47375989a41b4f09d88f3fdd6834b359\",\n          \"0x1553255b19cd6f6c6f1ca6bd6a63ffbaa9e6d3c4097a6e345e67309580987282\",\n          \"0x0f4081c215e2331724219e823ac5d230430b72fce41edba72edfca2e06ace94e\",\n          \"0x17af7b00c0b54135ad0385ff54daec3e776245036e7e241cb65d6d913f605e7c\",\n          \"0x268fe52e8552462fad2c76e1c436c051a8dadc6faf57a7edb941360f83e46273\",\n          \"0x2622d75b5033efd7da6fc6323de569b738fc901100e416b703b59f3c0a9c8054\",\n          \"0x258ba71da37b24fe773531d00b1aab7e3beec67e42dc6242f143ea34f99b8b4a\",\n          \"0x1c6c613fe56e8f2979a8dfd46aaf483f368c4225a55dc21e6fbd1f0cd5ac3ea4\",\n          \"0x267d1e5d6adafc1018613e0613aab2e0676202e79239bb59f3b841db8671c9dc\",\n          \"0x1628ecb72f96dad15ec71b17a1863b6504b288ad2cda9c2f6de051236e0e0ab3\",\n          \"0x1c3b905033821726a9c22352d12468ef6b8de97452362ebc9d67b78ae7f993e7\",\n          \"0x13cc971188af090820e03bd5b70f4ee13737176dc7d6e64715ea1427af461eb9\",\n          \"0x04ec2ce3c412040e20b74af347b37ace05be800186fd627613064ba96ac21ac2\",\n          \"0x05593ab6f9305a65f373fba2ce04d168759ccc603570f3acc891e27ce409fce1\",\n      },\n      {\n          \"0x2a73e37e3b487aea61a1b5b180b1c8bbb58952558640c87068de5e0ffaf50e16\",\n          \"0x0d4a403ce45e823da64a1fc2ea0fc49c47fac7f9b1263e28bab23e0cc4c6eb46\",\n          \"0x104b847e4252b02b6ab6bd3fae4e4abc9da4214de88940106d3b6c414ee5fc2d\",\n          \"0x216ab6dee7297be12501e1057228d4aacb4e78d7420e9f7deccb6dcd1a5fcc46\",\n          \"0x06bb7a5b66c3983d3f1ad9724b177b6499dbbb69197ad1f1164b8c375e3ac163\",\n          \"0x0d91c08cd724088d203e3abe0b7ea14663b74b4c4d3f98a9061431536ace10bc\",\n          \"0x02179a5d2e64b0905bba4db5dc0bbff870c2f4fbed3fc2c0930e5f4a87c5649b\",\n          \"0x25e4597d0c05edad7d34f0b090b4dc59ef8978c0d2c9d724737da712d69a5597\",\n          \"0x16952abd7181b114fced42dfe132aa9cd3263ab7f77c44604c630a92e1ecc29b\",\n          \"0x2fa3e6e10afbdbb10f41baed2131914a9b349adc88e50966ffeb6909ce397ed9\",\n          \"0x1db0d924eac719816e0b332c53c4ea538baec560927b75fd0e0746c2dd1be35b\",\n          \"0x2aab9ef338aff970a688c3fc3c7291a06c27b673776c63b7fe0f728064fdcbb2\",\n          \"0x22d48d0fde7027eea7cfc12c4134fc7821119fc8e303cebbab6bde03a7967386\",\n          \"0x293b996a4e467679d49ba46cac3962795815c3ab51870c6a3d55512fca187461\",\n          \"0x0a20cf33b54dd1c458ad0f578e909dcf8153244c983dc41e24c5cafb3eebc176\",\n          \"0x2902b4caa7502955b50a4334306200ed44d1287c67b043b45c8241117cbb180c\",\n      },\n  };\n\n  constexpr static uint8_t kProof[] = {\n      102, 195, 181, 202, 144, 137, 33,  216, 68,  179, 205, 95,  227, 168, 162,\n      99,  242, 209, 126, 138, 153, 132, 61,  39,  144, 81,  117, 233, 132, 215,\n      141, 169, 102, 195, 181, 202, 144, 137, 33,  216, 68,  179, 205, 95,  227,\n      168, 162, 99,  242, 209, 126, 138, 153, 132, 61,  39,  144, 81,  117, 233,\n      132, 215, 141, 169, 199, 251, 235, 84,  66,  51,  87,  191, 141, 175, 210,\n      178, 118, 97,  195, 187, 158, 174, 31,  182, 202, 29,  239, 147, 3,   94,\n      157, 61,  185, 67,  181, 31,  32,  65,  40,  125, 62,  194, 31,  196, 239,\n      246, 52,  186, 224, 133, 68,  251, 49,  255, 249, 85,  154, 192, 185, 29,\n      234, 11,  121, 2,   125, 9,   121, 132, 153, 8,   221, 196, 110, 88,  19,\n      58,  221, 161, 188, 20,  134, 199, 164, 26,  198, 159, 55,  86,  246, 253,\n      56,  142, 85,  144, 201, 99,  154, 148, 135, 149, 0,   28,  159, 139, 195,\n      134, 246, 131, 93,  127, 134, 128, 147, 93,  93,  165, 53,  239, 248, 136,\n      73,  190, 181, 44,  35,  167, 64,  108, 46,  111, 8,   168, 1,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   129,\n      255, 212, 247, 163, 73,  110, 208, 75,  138, 9,   53,  32,  252, 54,  213,\n      190, 31,  116, 90,  71,  189, 44,  117, 159, 222, 6,   61,  221, 40,  164,\n      42,  105, 55,  19,  127, 116, 173, 185, 23,  152, 19,  176, 25,  48,  6,\n      251, 177, 27,  254, 122, 125, 135, 195, 197, 116, 36,  177, 41,  127, 216,\n      103, 154, 171, 125, 197, 228, 28,  10,  184, 186, 161, 247, 161, 91,  7,\n      58,  152, 206, 117, 177, 251, 160, 18,  68,  222, 223, 182, 138, 237, 131,\n      160, 140, 38,  121, 16,  221, 65,  254, 132, 2,   22,  88,  105, 193, 100,\n      170, 102, 65,  255, 55,  80,  187, 150, 167, 129, 125, 28,  24,  50,  159,\n      155, 67,  102, 130, 244, 39,  11,  131, 229, 162, 105, 129, 220, 145, 170,\n      51,  239, 235, 30,  41,  250, 234, 161, 154, 66,  195, 15,  157, 168, 183,\n      10,  139, 170, 60,  12,  2,   107, 81,  33,  125, 197, 228, 28,  10,  184,\n      186, 161, 247, 161, 91,  7,   58,  152, 206, 117, 177, 251, 160, 18,  68,\n      222, 223, 182, 138, 237, 131, 160, 140, 38,  121, 16,  221, 65,  254, 132,\n      2,   22,  88,  105, 193, 100, 170, 102, 65,  255, 55,  80,  187, 150, 167,\n      129, 125, 28,  24,  50,  159, 155, 67,  102, 130, 244, 39,  11,  131, 229,\n      162, 105, 129, 220, 145, 170, 51,  239, 235, 30,  41,  250, 234, 161, 154,\n      66,  195, 15,  157, 168, 183, 10,  139, 170, 60,  12,  2,   107, 81,  33,\n      209, 91,  151, 138, 191, 214, 132, 54,  6,   182, 84,  19,  162, 111, 181,\n      9,   100, 202, 201, 107, 82,  201, 176, 241, 0,   249, 229, 252, 104, 90,\n      39,  33,  1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   169, 196, 106, 139, 225, 36,  175, 205, 231, 28,  36,\n      157, 101, 47,  135, 169, 64,  192, 176, 143, 23,  96,  205, 69,  58,  31,\n      142, 211, 101, 54,  252, 6,   85,  239, 31,  161, 14,  201, 1,   22,  226,\n      76,  25,  30,  245, 208, 212, 149, 1,   199, 200, 140, 28,  239, 215, 166,\n      21,  127, 208, 102, 154, 122, 224, 17,  189, 69,  241, 209, 12,  32,  32,\n      132, 189, 221, 119, 132, 15,  210, 225, 252, 116, 55,  61,  161, 78,  255,\n      122, 44,  73,  3,   243, 25,  32,  141, 104, 18,  217, 251, 216, 178, 71,\n      223, 193, 134, 162, 125, 113, 113, 242, 198, 195, 77,  120, 241, 64,  83,\n      57,  127, 80,  215, 63,  21,  119, 117, 185, 101, 227, 37,  74,  77,  210,\n      62,  104, 167, 61,  226, 38,  236, 93,  163, 69,  149, 218, 67,  209, 195,\n      236, 190, 24,  8,   141, 197, 139, 200, 92,  50,  40,  229, 113, 22,  188,\n      196, 133, 103, 238, 206, 224, 83,  33,  171, 99,  81,  83,  215, 94,  198,\n      124, 33,  167, 80,  192, 175, 203, 75,  124, 159, 177, 241, 59,  86,  84,\n      32,  208, 146, 205, 212, 251, 240, 94,  245, 90,  2,   108, 239, 90,  158,\n      17,  118, 197, 8,   137, 152, 91,  246, 247, 250, 40,  46,  129, 227, 151,\n      234, 194, 8,   122, 104, 156, 104, 111, 250, 255, 101, 46,  65,  116, 230,\n      166, 230, 150, 61,  120, 210, 169, 90,  250, 48,  123, 21,  214, 83,  117,\n      174, 225, 95,  67,  11,  244, 11,  243, 189, 144, 54,  235, 74,  244, 40,\n      244, 46,  111, 97,  184, 212, 124, 171, 167, 82,  69,  54,  170, 21,  100,\n      102, 170, 177, 53,  242, 89,  36,  139, 237, 59,  242, 31,  230, 128, 78,\n      44,  213, 243, 181, 35,  117, 59,  46,  2,   195, 64,  158, 211, 227, 53,\n      162, 197, 101, 21,  250, 12,  5,   187, 7,   184, 27,  125, 191, 169, 54,\n      84,  8,   142, 209, 196, 44,  54,  192, 208, 179, 162, 134, 165, 230, 88,\n      193, 194, 145, 176, 102, 153, 225, 166, 145, 16,  27,  65,  3,   44,  203,\n      105, 250, 3,   114, 104, 176, 205, 34,  70,  193, 253, 86,  43,  142, 180,\n      227, 107, 154, 252, 71,  179, 117, 197, 10,  147, 193, 70,  34,  201, 12,\n      167, 189, 208, 222, 198, 100, 189, 112, 208, 234, 3,   223, 217, 128, 255,\n      29,  204, 166, 162, 170, 97,  199, 183, 25,  106, 248, 168, 202, 208, 129,\n      178, 177, 15,  47,  94,  232, 226, 122, 179, 4,   158, 29,  145, 175, 223,\n      16,  11,  224, 233, 205, 29,  0,   252, 211, 68,  190, 160, 156, 15,  129,\n      112, 141};\n\n  // clang-format off\n  constexpr static Point kAdviceCommitments[][1] = {\n    {\n      {\"0x298dd784e9755190273d84998a7ed1f263a2a8e35fcdb344d8218990cab5c366\",\n       \"0x1d42911d6e4d1799ebf92175f0a9210e911ff8dce85cce34235704fb0fd3c6e3\"},\n    },\n    {\n      {\"0x298dd784e9755190273d84998a7ed1f263a2a8e35fcdb344d8218990cab5c366\",\n       \"0x1d42911d6e4d1799ebf92175f0a9210e911ff8dce85cce34235704fb0fd3c6e3\"},\n    },\n  };\n  // clang-format on\n\n  constexpr static std::string_view kTheta =\n      \"0x1b28757736609214ea9e32e10c911f1b96ae85f38925661ddbd996be266fed09\";\n\n  constexpr static std::string_view kBeta =\n      \"0x0602223cf23aa71e528439ab0e0b6d9a2d931e3a22eba1c870bbeddbadb14983\";\n\n  constexpr static std::string_view kGamma =\n      \"0x00ec0e0eba8b1497529775edac3483f2db08278734c770f45b196fa7133faee3\";\n\n  // clang-format off\n  constexpr static Point kPermutationProductCommitments[][2] = {\n        {\n            {\"0x1fb543b93d9d5e0393ef1dcab61fae9ebbc36176b2d2af8dbf57334254ebfbc7\",\n              \"0x0e0ee2f2e8a5b8d690bc3fc179c1baa941b1990539d8a4b0d0029cb9a85cf8ea\"},\n            {\"0x0479097d02790bea1db9c09a55f9ff31fb4485e0ba34f6efc41fc23e7d284120\",\n              \"0x2a351db0e33baf97b4bf16b9937a45bbfeabb04d74d5e06d096a9eca57406d55\"},\n        },\n        {\n            {\"0x1587949a63c990558e38fdf656379fc61aa4c78614bca1dd3a13586ec4dd0899\",\n              \"0x2c5c9cf5d5c2044a43db333083cb3d46a02ac2d2009697ee99fde79f2498bfa7\"},\n            {\"0x28086f2e6c40a7232cb5be4988f8ef35a55d5d9380867f5d83f686c38b9f1c00\",\n              \"0x180a44b0d51f8a1fbdf278a6d2694e6a08f5fb9890b43fac70433fc6f0b62ee5\"},\n        },\n      };\n  // clang-format on\n\n  constexpr static std::string_view kY =\n      \"0x11abab761694e36f90d075f2e4ca450bf11653b3607a676fb9d89621f78e8568\";\n\n  constexpr static Point kVanishingHPolyCommitments[] = {\n      {\"0x2aa428dd3d06de9f752cbd475a741fbed536fc2035098a4bd06e49a3f7d4ff81\",\n       \"0x09a52eb0da623bb6b44e8d58be168483f7a8435700257d8d501827689aa6ee00\"},\n      {\"0x2b9a67d87f29b12474c5c3877d7afe1bb1fb063019b0139817b9ad747f133769\",\n       \"0x24ecbbdabe2de5d884d1fd99e6f7399f0ffce88a0af718d5ac00305aacfc09ff\"},\n  };\n\n  constexpr static std::string_view kX =\n      \"0x1cd48fc4021ff60cc4fdf3d4c96109dba22b63fe21c501310c5b3c14f56ce9aa\";\n\n  constexpr static std::string_view kAdviceEvals[][3] = {\n      {\n          \"0x1079268ca083ed8ab6dfde4412a0fbb175ce983a075ba1f7a1bab80a1ce4c57d\",\n          \"0x0b27f48266439b9f32181c7d81a796bb5037ff4166aa64c16958160284fe41dd\",\n          \"0x21516b020c3caa8b0ab7a89d0fc3429aa1eafa291eebef33aa91dc8169a2e583\",\n      },\n      {\n          \"0x1079268ca083ed8ab6dfde4412a0fbb175ce983a075ba1f7a1bab80a1ce4c57d\",\n          \"0x0b27f48266439b9f32181c7d81a796bb5037ff4166aa64c16958160284fe41dd\",\n          \"0x21516b020c3caa8b0ab7a89d0fc3429aa1eafa291eebef33aa91dc8169a2e583\",\n      },\n  };\n\n  constexpr static std::string_view kFixedEvals[] = {\n      \"0x21275a68fce5f900f1b0c9526bc9ca6409b56fa21354b6063684d6bf8a975bd1\",\n  };\n\n  constexpr static std::string_view kCommonPermutationEvals[] = {\n      \"0x06fc3665d38e1f3a45cd60178fb0c040a9872f659d241ce7cdaf24e18b6ac4a9\",\n      \"0x11e07a9a66d07f15a6d7ef1c8cc8c70195d4d0f51e194ce21601c90ea11fef55\",\n  };\n\n  constexpr static std::string_view kPermutationProductEvals[][2] = {\n      {\n          \"0x12688d2019f303492c7aff4ea13d3774fce1d20f8477ddbd8420200cd1f145bd\",\n          \"0x2054563bf1b19f7c4bcbafc050a7217cc65ed7535163ab2153e0ceee6785c4bc\",\n      },\n      {\n          \"0x0b435fe1ae7553d6157b30fa5aa9d2783d96e6a6e674412e65fffa6f689c687a\",\n          \"0x1b1091a6e19966b091c2c158e6a586a2b3d0c0362cc4d18e085436a9bf7d1bb8\",\n      },\n  };\n\n  constexpr static std::string_view kPermutationProductNextEvals[][2] = {\n      {\n          \"0x25e365b97577153fd7507f395340f1784dc3c6f271717da286c1df47b2d8fbd9\",\n          \"0x08c2ea97e3812e28faf7f65b988908c576119e5aef6c025af55ef0fbd4cd92d0\",\n      },\n      {\n          \"0x2459f235b1aa666415aa364552a7ab7cd4b8616f2ef428f44aeb3690bdf30bf4\",\n          \"0x2246c1930ac575b347fc9a6be3b48e2b56fdc14622cdb0687203fa69cb2c0341\",\n      },\n  };\n\n  constexpr static std::string_view kPermutationProductLastEvals[][2] = {\n      {\n          \"0x1671e528325cc88bc58d0818beecc3d143da9545a35dec26e23da7683ed24d4a\",\n          \"\",\n      },\n      {\n          \"0x07bb050cfa1565c5a235e3d39e40c3022e3b7523b5f3d52c4e80e61ff23bed8b\",\n          \"\",\n      },\n  };\n\n  static void TestConfig(Fibonacci2Config<F>& config) {\n    EXPECT_EQ(config.advice, AdviceColumnKey(0));\n    EXPECT_EQ(config.instance, InstanceColumnKey(0));\n    EXPECT_EQ(config.selector, Selector::Simple(0));\n  }\n\n  static std::vector<Evals> GetInstanceColumns() {\n    F a(1);\n    F b(1);\n    F out(55);\n    std::vector<F> instance_column = {std::move(a), std::move(b),\n                                      std::move(out)};\n    return std::vector<Evals>{Evals(std::move(instance_column))};\n  }\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_EXAMPLES_FIBONACCI_FIBONACCI2_CIRCUIT_TEST_DATA_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/examples/fibonacci/fibonacci3_circuit.h",
    "content": "#ifndef TACHYON_ZK_PLONK_EXAMPLES_FIBONACCI_FIBONACCI3_CIRCUIT_H_\n#define TACHYON_ZK_PLONK_EXAMPLES_FIBONACCI_FIBONACCI3_CIRCUIT_H_\n\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/zk/plonk/constraint_system/circuit.h\"\n#include \"tachyon/zk/plonk/examples/fibonacci/is_zero_chip.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F>\nstruct Fibonacci3Config {\n  using Field = F;\n\n  Fibonacci3Config(Selector selector, const AdviceColumnKey& a,\n                   const AdviceColumnKey& b, const AdviceColumnKey& c,\n                   IsZeroConfig<F>&& a_equals_b, const AdviceColumnKey& output)\n      : selector(selector),\n        a(a),\n        b(b),\n        c(c),\n        a_equals_b(std::move(a_equals_b)),\n        output(output) {}\n\n  Fibonacci3Config Clone() const {\n    return {selector, a, b, c, a_equals_b.Clone(), output};\n  }\n\n  Selector selector;\n  AdviceColumnKey a;\n  AdviceColumnKey b;\n  AdviceColumnKey c;\n  IsZeroConfig<F> a_equals_b;\n  AdviceColumnKey output;\n};\n\ntemplate <typename F>\nclass Fibonacci3Chip {\n public:\n  explicit Fibonacci3Chip(Fibonacci3Config<F>&& config)\n      : config_(std::move(config)) {}\n\n  static Fibonacci3Config<F> Configure(ConstraintSystem<F>& meta) {\n    Selector selector = meta.CreateSimpleSelector();\n    AdviceColumnKey a = meta.CreateAdviceColumn();\n    AdviceColumnKey b = meta.CreateAdviceColumn();\n    AdviceColumnKey c = meta.CreateAdviceColumn();\n    AdviceColumnKey output = meta.CreateAdviceColumn();\n\n    AdviceColumnKey is_zero_advice_column = meta.CreateAdviceColumn();\n    IsZeroConfig<F> a_equals_b = IsZeroChip<F>::Configure(\n        meta,\n        [&selector](VirtualCells<F>& meta) {\n          return meta.QuerySelector(selector);\n        },\n        [&a, &b](VirtualCells<F>& meta) {\n          // FIXME(ashjeong): When attempting to run the following logic in an\n          // in-line return statement, the |meta.QueryAdvice()| function\n          // internally updates the query index, and depending on whether the\n          // system is Linux or macOS, the order of execution between operations\n          // x and y differs. Further testing with clang on Linux is needed\n          // to determine if the issue is with the operating system or the\n          // compiler. GCC could also be a contributing factor.\n          std::unique_ptr<Expression<F>> x =\n              meta.QueryAdvice(a, Rotation::Cur());\n          std::unique_ptr<Expression<F>> y =\n              meta.QueryAdvice(b, Rotation::Cur());\n          return x - y;\n        },\n        is_zero_advice_column);\n\n    meta.CreateGate(\n        \"f(a, b, c) = if a == b {c} else {a - b}\",\n        [&selector, &a, &b, &c, &output, &a_equals_b](VirtualCells<F>& meta) {\n          std::unique_ptr<Expression<F>> s = meta.QuerySelector(selector);\n          std::unique_ptr<Expression<F>> a_expr =\n              meta.QueryAdvice(a, Rotation::Cur());\n          std::unique_ptr<Expression<F>> b_expr =\n              meta.QueryAdvice(b, Rotation::Cur());\n          std::unique_ptr<Expression<F>> c_expr =\n              meta.QueryAdvice(c, Rotation::Cur());\n          std::unique_ptr<Expression<F>> output_expr =\n              meta.QueryAdvice(output, Rotation::Cur());\n\n          std::vector<Constraint<F>> constraints;\n          constraints.emplace_back(\n              s->Clone() *\n              (a_equals_b.expr() * (output_expr->Clone() - std::move(c_expr))));\n          constraints.emplace_back(\n              std::move(s) *\n              (ExpressionFactory<F>::Constant(F::One()) - a_equals_b.expr()) *\n              (std::move(output_expr) -\n               (std::move(a_expr) - std::move(b_expr))));\n          return constraints;\n        });\n\n    return Fibonacci3Config<F>(selector, a, b, c, std::move(a_equals_b),\n                               output);\n  }\n\n  AssignedCell<F> Assign(Layouter<F>* layouter, const F& a, const F& b,\n                         const F& c) const {\n    AssignedCell<F> ret;\n    layouter->AssignRegion(\n        \"f(a, b, c) = if a == b {c} else {a - b}\",\n        [this, &ret, &a, &b, &c](Region<F>& region) {\n          config_.selector.Enable(region, 0);\n\n          region.AssignAdvice(\"a\", config_.a, 0,\n                              [&a]() { return Value<F>::Known(a); });\n          region.AssignAdvice(\"b\", config_.b, 0,\n                              [&b]() { return Value<F>::Known(b); });\n          region.AssignAdvice(\"c\", config_.c, 0,\n                              [&c]() { return Value<F>::Known(c); });\n\n          IsZeroChip<F> is_zero_chip(config_.a_equals_b.Clone());\n          is_zero_chip.Assign(region, 0, Value<F>::Known(a - b));\n\n          F output = a == b ? c : a - b;\n          AssignedCell<F> output_cell = region.AssignAdvice(\n              \"output\", config_.output, 0,\n              [&output]() { return Value<F>::Known(output); });\n\n          ret = output_cell;\n        });\n    return ret;\n  }\n\n private:\n  Fibonacci3Config<F> config_;\n};\n\ntemplate <typename F, template <typename> class _FloorPlanner>\nclass Fibonacci3Circuit : public Circuit<Fibonacci3Config<F>> {\n public:\n  using FloorPlanner = _FloorPlanner<Fibonacci3Circuit<F, _FloorPlanner>>;\n\n  Fibonacci3Circuit() : a_(F(1)), b_(F(1)), c_(F(1)) {}\n  Fibonacci3Circuit(const F& a, const F& b, const F& c) : a_(a), b_(b), c_(c) {}\n  Fibonacci3Circuit(F&& a, F&& b, F&& c)\n      : a_(std::move(a)), b_(std::move(b)), c_(std::move(c)) {}\n\n  std::unique_ptr<Circuit<Fibonacci3Config<F>>> WithoutWitness()\n      const override {\n    return std::make_unique<Fibonacci3Circuit>(a_, b_, c_);\n  }\n\n  static Fibonacci3Config<F> Configure(ConstraintSystem<F>& meta) {\n    return Fibonacci3Chip<F>::Configure(meta);\n  }\n\n  void Synthesize(Fibonacci3Config<F>&& config,\n                  Layouter<F>* layouter) const override {\n    Fibonacci3Chip<F> fibonacci3_chip(std::move(config));\n    fibonacci3_chip.Assign(layouter, a_, b_, c_);\n  }\n\n private:\n  F a_;\n  F b_;\n  F c_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_EXAMPLES_FIBONACCI_FIBONACCI3_CIRCUIT_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/examples/fibonacci/fibonacci3_circuit_test.cc",
    "content": "#include \"tachyon/zk/plonk/examples/fibonacci/fibonacci3_circuit.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/zk/plonk/examples/circuit_test.h\"\n#include \"tachyon/zk/plonk/examples/fibonacci/fibonacci3_circuit_test_data.h\"\n#include \"tachyon/zk/plonk/layout/floor_planner/simple_floor_planner.h\"\n#include \"tachyon/zk/plonk/layout/floor_planner/v1/v1_floor_planner.h\"\n\nnamespace tachyon::zk::plonk {\n\nnamespace {\n\ntemplate <typename TestArguments>\nclass Fibonacci3CircuitTest\n    : public CircuitTest<TestArguments,\n                         Fibonacci3TestData<typename TestArguments::Circuit,\n                                            typename TestArguments::PS>> {};\n\n}  // namespace\n\nusing Fibonacci3TestArgumentsList = testing::Types<\n    TestArguments<Fibonacci3Circuit<BN254SHPlonk::Field, SimpleFloorPlanner>,\n                  BN254SHPlonkHalo2>,\n    TestArguments<Fibonacci3Circuit<BN254SHPlonk::Field, V1FloorPlanner>,\n                  BN254SHPlonkHalo2>>;\n\nTYPED_TEST_SUITE(Fibonacci3CircuitTest, Fibonacci3TestArgumentsList);\n\nTYPED_TEST(Fibonacci3CircuitTest, Configure) { this->ConfigureTest(); }\nTYPED_TEST(Fibonacci3CircuitTest, Synthesize) { this->SynthesizeTest(); }\nTYPED_TEST(Fibonacci3CircuitTest, LoadVerifyingKey) {\n  this->LoadVerifyingKeyTest();\n}\nTYPED_TEST(Fibonacci3CircuitTest, LoadProvingKey) {\n  this->LoadProvingKeyTest();\n}\nTYPED_TEST(Fibonacci3CircuitTest, CreateProof) { this->CreateProofTest(); }\nTYPED_TEST(Fibonacci3CircuitTest, VerifyProof) { this->VerifyProofTest(); }\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/examples/fibonacci/fibonacci3_circuit_test_data.h",
    "content": "#ifndef TACHYON_ZK_PLONK_EXAMPLES_FIBONACCI_FIBONACCI3_CIRCUIT_TEST_DATA_H_\n#define TACHYON_ZK_PLONK_EXAMPLES_FIBONACCI_FIBONACCI3_CIRCUIT_TEST_DATA_H_\n\n#include <stdint.h>\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/zk/plonk/examples/circuit_test_data.h\"\n#include \"tachyon/zk/plonk/examples/circuit_test_type_traits.h\"\n#include \"tachyon/zk/plonk/examples/point.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename Circuit, typename PS>\nclass Fibonacci3TestData : public CircuitTestData<Circuit, PS> {\n public:\n  using PCS = typename PS::PCS;\n  using F = typename PCS::Field;\n\n  constexpr static bool kLFirstFlag = true;\n  constexpr static bool kLLastFlag = true;\n  constexpr static bool kLActiveRowFlag = true;\n  constexpr static bool kFixedColumnsFlag = true;\n  constexpr static bool kFixedPolysFlag = true;\n  constexpr static bool kAdviceCommitmentsFlag = true;\n  constexpr static bool kAdviceEvalsFlag = true;\n  constexpr static bool kFixedEvalsFlag = true;\n\n  constexpr static size_t kN = 16;\n\n  // clang-format off\n  constexpr static std::string_view kPinnedConstraintSystem =\n      \"PinnedConstraintSystem { \"\n        \"num_fixed_columns: 0, \"\n        \"num_advice_columns: 5, \"\n        \"num_instance_columns: 0, \"\n        \"num_selectors: 1, \"\n        \"gates: [\"\n          \"Product(\"\n            \"Product(\"\n              \"Selector(Selector(0, true)), \"\n              \"Sum(\"\n                \"Advice { \"\n                  \"query_index: 0, \"\n                  \"column_index: 0, \"\n                  \"rotation: Rotation(0) \"\n                \"}, \"\n                \"Negated(Advice { \"\n                  \"query_index: 1, \"\n                  \"column_index: 1, \"\n                  \"rotation: Rotation(0) \"\n                \"})\"\n              \")\"\n            \"), \"\n            \"Sum(\"\n              \"Constant(0x0000000000000000000000000000000000000000000000000000000000000001), \"\n              \"Negated(Product(\"\n                \"Sum(\"\n                  \"Advice { \"\n                    \"query_index: 0, \"\n                    \"column_index: 0, \"\n                    \"rotation: Rotation(0) \"\n                  \"}, \"\n                  \"Negated(Advice { \"\n                    \"query_index: 1, \"\n                    \"column_index: 1, \"\n                    \"rotation: Rotation(0) \"\n                  \"})\"\n                \"), \"\n                \"Advice { \"\n                  \"query_index: 2, \"\n                  \"column_index: 4, \"\n                  \"rotation: Rotation(0) \"\n                \"}\"\n              \"))\"\n            \")\"\n          \"), \"\n          \"Product(\"\n            \"Selector(Selector(0, true)), \"\n            \"Product(\"\n              \"Sum(\"\n                \"Constant(0x0000000000000000000000000000000000000000000000000000000000000001), \"\n                \"Negated(Product(\"\n                  \"Sum(\"\n                    \"Advice { \"\n                      \"query_index: 0, \"\n                      \"column_index: 0, \"\n                      \"rotation: Rotation(0) \"\n                    \"}, \"\n                    \"Negated(Advice { \"\n                      \"query_index: 1, \"\n                      \"column_index: 1, \"\n                      \"rotation: Rotation(0) \"\n                    \"})\"\n                  \"), \"\n                  \"Advice { \"\n                    \"query_index: 2, \"\n                    \"column_index: 4, \"\n                    \"rotation: Rotation(0) \"\n                  \"}\"\n                \"))\"\n              \"), \"\n              \"Sum(\"\n                \"Advice { \"\n                  \"query_index: 4, \"\n                  \"column_index: 3, \"\n                  \"rotation: Rotation(0) \"\n                \"}, \"\n                \"Negated(Advice { \"\n                  \"query_index: 3, \"\n                  \"column_index: 2, \"\n                  \"rotation: Rotation(0) \"\n                \"})\"\n              \")\"\n            \")\"\n          \"), \"\n          \"Product(\"\n            \"Product(\"\n              \"Selector(Selector(0, true)), \"\n              \"Sum(\"\n                \"Constant(0x0000000000000000000000000000000000000000000000000000000000000001), \"\n                \"Negated(Sum(\"\n                  \"Constant(0x0000000000000000000000000000000000000000000000000000000000000001), \"\n                  \"Negated(Product(\"\n                    \"Sum(\"\n                      \"Advice { \"\n                        \"query_index: 0, \"\n                        \"column_index: 0, \"\n                        \"rotation: Rotation(0) \"\n                      \"}, \"\n                      \"Negated(Advice { \"\n                        \"query_index: 1, \"\n                        \"column_index: 1, \"\n                        \"rotation: Rotation(0) \"\n                      \"})\"\n                    \"), \"\n                    \"Advice { \"\n                      \"query_index: 2, \"\n                      \"column_index: 4, \"\n                      \"rotation: Rotation(0) \"\n                    \"}\"\n                  \"))\"\n                \"))\"\n              \")\"\n            \"), \"\n            \"Sum(\"\n              \"Advice { \"\n                \"query_index: 4, \"\n                \"column_index: 3, \"\n                \"rotation: Rotation(0) \"\n              \"}, \"\n              \"Negated(Sum(\"\n                \"Advice { \"\n                  \"query_index: 0, \"\n                  \"column_index: 0, \"\n                  \"rotation: Rotation(0) \"\n                \"}, \"\n                \"Negated(Advice { \"\n                  \"query_index: 1, \"\n                  \"column_index: 1, \"\n                  \"rotation: Rotation(0) \"\n                \"})\"\n              \"))\"\n            \")\"\n          \")\"\n        \"], \"\n        \"advice_queries: [\"\n          \"(\"\n            \"Column { \"\n              \"index: 0, \"\n              \"column_type: Advice \"\n            \"}, \"\n            \"Rotation(0)\"\n          \"), \"\n          \"(\"\n            \"Column { \"\n              \"index: 1, \"\n              \"column_type: Advice \"\n            \"}, \"\n            \"Rotation(0)\"\n          \"), \"\n          \"(\"\n            \"Column { \"\n              \"index: 4, \"\n              \"column_type: Advice \"\n            \"}, \"\n            \"Rotation(0)\"\n          \"), \"\n          \"(\"\n            \"Column { \"\n              \"index: 2, \"\n              \"column_type: Advice \"\n            \"}, \"\n            \"Rotation(0)\"\n          \"), \"\n          \"(\"\n            \"Column { \"\n              \"index: 3, \"\n              \"column_type: Advice \"\n            \"}, \"\n            \"Rotation(0)\"\n          \")\"\n        \"], \"\n        \"instance_queries: [], \"\n        \"fixed_queries: [], \"\n        \"permutation: Argument { \"\n          \"columns: [] \"\n        \"}, \"\n        \"lookups: [], \"\n        \"constants: [], \"\n        \"minimum_degree: None \"\n      \"}\";\n  // clang-format on\n\n  // clang-format off\n  constexpr static bool kSelectors[][kN] = {\n      {true, false, false, false, false, false, false, false,\n       false, false, false, false, false, false, false, false}\n  };\n  // clang-format on\n\n  // clang-format off\n  constexpr static std::string_view kPinnedVerifyingKey =\n      \"PinnedVerificationKey { \"\n        \"base_modulus: \\\"0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47\\\", \"\n        \"scalar_modulus: \\\"0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001\\\", \"\n        \"domain: PinnedEvaluationDomain { \"\n          \"k: 4, \"\n          \"extended_k: 6, \"\n          \"omega: 0x21082ca216cbbf4e1c6e4f4594dd508c996dfbe1174efb98b11509c6e306460b \"\n        \"}, \"\n        \"cs: PinnedConstraintSystem { \"\n          \"num_fixed_columns: 1, \"\n          \"num_advice_columns: 5, \"\n          \"num_instance_columns: 0, \"\n          \"num_selectors: 1, \"\n          \"gates: [\"\n            \"Product(\"\n              \"Product(\"\n                \"Fixed { \"\n                  \"query_index: 0, \"\n                  \"column_index: 0, \"\n                  \"rotation: Rotation(0) \"\n                \"}, \"\n                \"Sum(\"\n                  \"Advice { \"\n                    \"query_index: 0, \"\n                    \"column_index: 0, \"\n                    \"rotation: Rotation(0) \"\n                  \"}, \"\n                  \"Negated(Advice { \"\n                    \"query_index: 1, \"\n                    \"column_index: 1, \"\n                    \"rotation: Rotation(0) \"\n                  \"})\"\n                \")\"\n              \"), \"\n              \"Sum(\"\n                \"Constant(0x0000000000000000000000000000000000000000000000000000000000000001), \"\n                \"Negated(Product(\"\n                  \"Sum(\"\n                    \"Advice { \"\n                      \"query_index: 0, \"\n                      \"column_index: 0, \"\n                      \"rotation: Rotation(0) \"\n                    \"}, \"\n                    \"Negated(Advice { \"\n                      \"query_index: 1, \"\n                      \"column_index: 1, \"\n                      \"rotation: Rotation(0) \"\n                    \"})\"\n                  \"), \"\n                  \"Advice { \"\n                    \"query_index: 2, \"\n                    \"column_index: 4, \"\n                    \"rotation: Rotation(0) \"\n                  \"}\"\n                \"))\"\n              \")\"\n            \"), \"\n            \"Product(\"\n              \"Fixed { \"\n                \"query_index: 0, \"\n                \"column_index: 0, \"\n                \"rotation: Rotation(0) \"\n              \"}, \"\n              \"Product(\"\n                \"Sum(\"\n                  \"Constant(0x0000000000000000000000000000000000000000000000000000000000000001), \"\n                  \"Negated(Product(\"\n                    \"Sum(\"\n                      \"Advice { \"\n                        \"query_index: 0, \"\n                        \"column_index: 0, \"\n                        \"rotation: Rotation(0) \"\n                      \"}, \"\n                      \"Negated(Advice { \"\n                        \"query_index: 1, \"\n                        \"column_index: 1, \"\n                        \"rotation: Rotation(0) \"\n                      \"})\"\n                    \"), \"\n                    \"Advice { \"\n                      \"query_index: 2, \"\n                      \"column_index: 4, \"\n                      \"rotation: Rotation(0) \"\n                    \"}\"\n                  \"))\"\n                \"), \"\n                \"Sum(\"\n                  \"Advice { \"\n                    \"query_index: 4, \"\n                    \"column_index: 3, \"\n                    \"rotation: Rotation(0) \"\n                  \"}, \"\n                  \"Negated(Advice { \"\n                    \"query_index: 3, \"\n                    \"column_index: 2, \"\n                    \"rotation: Rotation(0) \"\n                  \"})\"\n                \")\"\n              \")\"\n            \"), \"\n            \"Product(\"\n              \"Product(\"\n                \"Fixed { \"\n                  \"query_index: 0, \"\n                  \"column_index: 0, \"\n                  \"rotation: Rotation(0) \"\n                \"}, \"\n                \"Sum(\"\n                  \"Constant(0x0000000000000000000000000000000000000000000000000000000000000001), \"\n                  \"Negated(Sum(\"\n                    \"Constant(0x0000000000000000000000000000000000000000000000000000000000000001), \"\n                    \"Negated(Product(\"\n                      \"Sum(\"\n                        \"Advice { \"\n                          \"query_index: 0, \"\n                          \"column_index: 0, \"\n                          \"rotation: Rotation(0) \"\n                        \"}, \"\n                        \"Negated(Advice { \"\n                          \"query_index: 1, \"\n                          \"column_index: 1, \"\n                          \"rotation: Rotation(0) \"\n                        \"})\"\n                      \"), \"\n                      \"Advice { \"\n                        \"query_index: 2, \"\n                        \"column_index: 4, \"\n                        \"rotation: Rotation(0) \"\n                      \"}\"\n                    \"))\"\n                  \"))\"\n                \")\"\n              \"), \"\n              \"Sum(\"\n                \"Advice { \"\n                  \"query_index: 4, \"\n                  \"column_index: 3, \"\n                  \"rotation: Rotation(0) \"\n                \"}, \"\n                \"Negated(Sum(\"\n                  \"Advice { \"\n                    \"query_index: 0, \"\n                    \"column_index: 0, \"\n                    \"rotation: Rotation(0) \"\n                  \"}, \"\n                  \"Negated(Advice { \"\n                    \"query_index: 1, \"\n                    \"column_index: 1, \"\n                    \"rotation: Rotation(0) \"\n                  \"})\"\n                \"))\"\n              \")\"\n            \")\"\n          \"], \"\n          \"advice_queries: [\"\n            \"(\"\n              \"Column { \"\n                \"index: 0, \"\n                \"column_type: Advice \"\n              \"}, \"\n              \"Rotation(0)\"\n            \"), \"\n            \"(\"\n              \"Column { \"\n                \"index: 1, \"\n                \"column_type: Advice \"\n              \"}, \"\n              \"Rotation(0)\"\n            \"), \"\n            \"(\"\n              \"Column { \"\n                \"index: 4, \"\n                \"column_type: Advice \"\n              \"}, \"\n              \"Rotation(0)\"\n            \"), \"\n            \"(\"\n              \"Column { \"\n                \"index: 2, \"\n                \"column_type: Advice \"\n              \"}, \"\n              \"Rotation(0)\"\n            \"), \"\n            \"(\"\n              \"Column { \"\n                \"index: 3, \"\n                \"column_type: Advice \"\n              \"}, \"\n              \"Rotation(0)\"\n            \")\"\n          \"], \"\n          \"instance_queries: [], \"\n          \"fixed_queries: [(\"\n            \"Column { \"\n              \"index: 0, \"\n              \"column_type: Fixed \"\n            \"}, \"\n            \"Rotation(0)\"\n          \")], \"\n          \"permutation: Argument { \"\n            \"columns: [] \"\n          \"}, \"\n          \"lookups: [], \"\n          \"constants: [], \"\n          \"minimum_degree: None \"\n        \"}, \"\n        \"fixed_commitments: [\"\n          \"(0x0006d246b1045b5cf7ef706abdca51d7f88992335199a85f594360f81b3435a9, \"\n            \"0x0ce5093fe91a56ef9d54ef73d457aa726be43905202132cd98cb1893cfee96a6)\"\n        \"], \"\n        \"permutation: VerifyingKey { \"\n          \"commitments: [] \"\n        \"} \"\n      \"}\";\n  // clang-format on\n\n  constexpr static std::string_view kTranscriptRepr =\n      \"0x015bd699cd46cf1250b4b2b2553645628551f6c76bd3e5cc995a355972340be4\";\n\n  constexpr static std::string_view kLFirst[] = {\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n  };\n\n  constexpr static std::string_view kLLast[] = {\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2014447de15a99b6df03833e95f96ae1299c9ec6ff990b6e75fa3b3b04846a57\",\n      \"0x0f1f5883e65f820d14d56342dc92fd12a944d4cbbdce5377b7439bd07108fc9d\",\n      \"0x02b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e8\",\n      \"0x030644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000\",\n      \"0x105009f4ffd70672d94cc277eb87ed7bfe9749817a206522cde7ba58eb7b95aa\",\n      \"0x2144f5eefad21e1ca37ae273a4ee5b4a7eef137cbbeb1d198c9e59c37ef70364\",\n      \"0x2db11694c4a58b3789868bd388165969c30dc1120a37fff09a991abf43e42a19\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2014447de15a99b6df03833e95f96ae1299c9ec6ff990b6e75fa3b3b04846a57\",\n      \"0x0f1f5883e65f820d14d56342dc92fd12a944d4cbbdce5377b7439bd07108fc9d\",\n      \"0x02b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e8\",\n      \"0x030644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000\",\n      \"0x105009f4ffd70672d94cc277eb87ed7bfe9749817a206522cde7ba58eb7b95aa\",\n      \"0x2144f5eefad21e1ca37ae273a4ee5b4a7eef137cbbeb1d198c9e59c37ef70364\",\n      \"0x2db11694c4a58b3789868bd388165969c30dc1120a37fff09a991abf43e42a19\",\n  };\n\n  constexpr static std::string_view kLActiveRow[] = {\n      \"0x12259d6b14729c0fa51e1a2470908122ef13771b2da58a367974bc177a000001\",\n      \"0x1a8133201ba2fe22aff30c7fd04fadcb17ceab0715a13371b06d35acd9695b3d\",\n      \"0x0d49c50dd1c3ec703dc7be1c836fd7f62c140afcf284ce19b9a99affac7b95aa\",\n      \"0x117dae38a05bcb8c7c290ee16cec493d73ef8dafa5c61fa4a2efd9a39e63abf4\",\n      \"0x0c19139cb84c680a79505ee7747ae78cd6c196473632bc6ea3057c773208fc9d\",\n      \"0x136156e428a2662bc2fddfd3b39f6475dafecb8699a611f3da6edd22c3479af1\",\n      \"0x2aaad1ad96927134ee0187781ffe43e3f08a828d829c68e7865afb6604e42a19\",\n      \"0x28d7d254c17b7ea40ebc4659996adacebd0d8f52d021284040d407c2f33b896f\",\n      \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n      \"0x1ef7221577b1e8f8cebdcb2fcd10296b6d9d43267b395c820757c0fc87681d81\",\n      \"0x1d0dff96b3477fb4437e7ee32de1555b5719604277fd746561bc1be1c5846a57\",\n      \"0x1be193603aacb384678281793fa8f20b949cf5976d9493017ba8a357ee49da57\",\n      \"0x1e3eb107ccbf041a07f5de183cd645c4ac6bd4f8344f861078603a6a3ff70364\",\n      \"0x20080468beb85b16c9f71b3ea2ce10fb6cdc81c346721c888ebc9109900adec6\",\n      \"0x30114169cfaa9b194b94fb3e12d441cabad6d0fa619f4a28d8ecb10f5d1bd5e9\",\n      \"0x2edcc3ce4ec47abd9b83b31a4db9571236223b590c30997fd30ce24f7bf2fdd6\",\n  };\n\n  constexpr static std::string_view kFixedColumns[][kN] = {\n      {\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n      },\n  };\n\n  constexpr static std::string_view kFixedPolys[][kN] = {\n      {\n          \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n          \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n          \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n          \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n          \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n          \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n          \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n          \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n          \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n          \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n          \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n          \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n          \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n          \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n          \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n          \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      },\n  };\n\n  constexpr static uint8_t kProof[] = {\n      33,  231, 248, 121, 132, 226, 31,  179, 86,  67,  213, 109, 201, 53,  147,\n      93,  41,  30,  16,  172, 115, 107, 0,   172, 225, 38,  5,   143, 136, 68,\n      3,   47,  222, 110, 120, 246, 167, 202, 30,  64,  192, 113, 187, 20,  209,\n      5,   19,  35,  161, 55,  99,  167, 153, 242, 153, 84,  169, 135, 224, 148,\n      140, 22,  18,  148, 5,   145, 196, 201, 20,  216, 6,   183, 82,  73,  62,\n      87,  36,  44,  76,  29,  120, 103, 124, 253, 87,  100, 218, 122, 245, 84,\n      26,  140, 240, 97,  130, 134, 92,  104, 200, 22,  60,  26,  130, 227, 203,\n      220, 139, 80,  219, 111, 129, 53,  194, 0,   185, 59,  50,  147, 22,  2,\n      25,  58,  255, 70,  194, 103, 39,  5,   143, 199, 14,  63,  41,  37,  111,\n      200, 181, 194, 119, 182, 175, 185, 182, 254, 90,  205, 250, 56,  35,  47,\n      214, 38,  249, 166, 235, 34,  66,  177, 210, 132, 33,  231, 248, 121, 132,\n      226, 31,  179, 86,  67,  213, 109, 201, 53,  147, 93,  41,  30,  16,  172,\n      115, 107, 0,   172, 225, 38,  5,   143, 136, 68,  3,   47,  222, 110, 120,\n      246, 167, 202, 30,  64,  192, 113, 187, 20,  209, 5,   19,  35,  161, 55,\n      99,  167, 153, 242, 153, 84,  169, 135, 224, 148, 140, 22,  18,  148, 5,\n      145, 196, 201, 20,  216, 6,   183, 82,  73,  62,  87,  36,  44,  76,  29,\n      120, 103, 124, 253, 87,  100, 218, 122, 245, 84,  26,  140, 240, 97,  130,\n      134, 92,  104, 200, 22,  60,  26,  130, 227, 203, 220, 139, 80,  219, 111,\n      129, 53,  194, 0,   185, 59,  50,  147, 22,  2,   25,  58,  255, 70,  194,\n      103, 39,  5,   143, 199, 14,  63,  41,  37,  111, 200, 181, 194, 119, 182,\n      175, 185, 182, 254, 90,  205, 250, 56,  35,  47,  214, 38,  249, 166, 235,\n      34,  66,  177, 210, 132, 1,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   169, 158, 99,  118, 9,   49,  34,  148,\n      175, 15,  48,  50,  158, 238, 192, 121, 129, 111, 35,  198, 240, 246, 159,\n      196, 103, 149, 207, 12,  223, 180, 245, 157, 130, 0,   103, 220, 171, 217,\n      151, 60,  30,  241, 176, 246, 168, 252, 196, 91,  211, 192, 39,  169, 95,\n      74,  227, 162, 105, 66,  253, 30,  103, 68,  220, 147, 246, 10,  53,  47,\n      157, 64,  10,  241, 80,  81,  142, 177, 3,   100, 79,  137, 34,  210, 16,\n      177, 127, 143, 217, 234, 231, 152, 247, 248, 7,   183, 37,  21,  115, 94,\n      241, 114, 69,  35,  108, 59,  147, 48,  241, 209, 48,  253, 195, 98,  163,\n      86,  2,   242, 247, 40,  255, 139, 223, 248, 71,  107, 143, 125, 199, 43,\n      161, 193, 81,  170, 183, 157, 78,  116, 11,  186, 239, 250, 244, 116, 112,\n      255, 88,  148, 27,  194, 105, 59,  223, 153, 38,  74,  224, 216, 129, 67,\n      35,  8,   189, 85,  55,  24,  84,  79,  78,  95,  78,  100, 246, 214, 36,\n      243, 80,  183, 6,   197, 183, 38,  109, 142, 189, 197, 110, 253, 136, 182,\n      81,  77,  34,  13,  103, 86,  98,  237, 246, 74,  132, 13,  209, 248, 38,\n      178, 227, 16,  167, 146, 70,  201, 194, 251, 202, 156, 255, 230, 58,  100,\n      118, 94,  224, 58,  17,  3,   90,  11,  175, 118, 180, 120, 179, 146, 235,\n      196, 90,  123, 45,  165, 181, 237, 143, 42,  228, 137, 188, 93,  45,  159,\n      100, 240, 189, 115, 162, 81,  171, 15,  115, 94,  241, 114, 69,  35,  108,\n      59,  147, 48,  241, 209, 48,  253, 195, 98,  163, 86,  2,   242, 247, 40,\n      255, 139, 223, 248, 71,  107, 143, 125, 199, 43,  161, 193, 81,  170, 183,\n      157, 78,  116, 11,  186, 239, 250, 244, 116, 112, 255, 88,  148, 27,  194,\n      105, 59,  223, 153, 38,  74,  224, 216, 129, 67,  35,  8,   189, 85,  55,\n      24,  84,  79,  78,  95,  78,  100, 246, 214, 36,  243, 80,  183, 6,   197,\n      183, 38,  109, 142, 189, 197, 110, 253, 136, 182, 81,  77,  34,  13,  103,\n      86,  98,  237, 246, 74,  132, 13,  209, 248, 38,  178, 227, 16,  167, 146,\n      70,  201, 194, 251, 202, 156, 255, 230, 58,  100, 118, 94,  224, 58,  17,\n      3,   90,  11,  175, 118, 180, 120, 179, 146, 235, 196, 90,  123, 45,  165,\n      181, 237, 143, 42,  228, 137, 188, 93,  45,  159, 100, 240, 189, 115, 162,\n      81,  171, 15,  152, 49,  176, 11,  205, 50,  83,  96,  77,  181, 56,  142,\n      42,  36,  138, 246, 55,  247, 141, 105, 239, 78,  64,  63,  205, 200, 253,\n      23,  108, 49,  146, 30,  1,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   71,  41,  149, 131, 0,   144, 163, 29,\n      1,   20,  175, 36,  56,  179, 196, 28,  229, 5,   87,  110, 13,  35,  17,\n      220, 159, 123, 169, 98,  107, 125, 182, 172, 71,  41,  149, 131, 0,   144,\n      163, 29,  1,   20,  175, 36,  56,  179, 196, 28,  229, 5,   87,  110, 13,\n      35,  17,  220, 159, 123, 169, 98,  107, 125, 182, 172,\n  };\n\n  // clang-format off\n  constexpr static Point kAdviceCommitments[][5] = {\n    {\n      {\"0x2f0344888f0526e1ac006b73ac101e295d9335c96dd54356b31fe28479f8e721\",\n       \"0x03d9c8b82e3fdc536e0dc94e102b13f6f02b68a87483ee77ff9d315b2c7d60de\"},\n      {\"0x1412168c94e087a95499f299a76337a1231305d114bb71c0401ecaa7f6786ede\",\n       \"0x13c23c6582942c390a5a788602da1db2a55a2424d0da56357809ac5b2e441f65\"},\n      {\"0x068261f08c1a54f57ada6457fd7c67781d4c2c24573e4952b706d814c9c49105\",\n       \"0x145e88698efe8890d0ac0148f7323474c9a32b7ee6eca0ad95915643c1bd689d\"},\n      {\"0x052767c246ff3a19021693323bb900c235816fdb508bdccbe3821a3c16c8685c\",\n       \"0x1993a757677c0957fb51ca2cccfcef84c924a13fdf4d91786ce1a1c6f733199c\"},\n      {\"0x04d2b14222eba6f926d62f2338facd5afeb6b9afb677c2b5c86f25293f0ec78f\",\n       \"0x22c64d91d4b0dc41516f1f8c3828a535c7918b49936ac04e5b57e466d0face1f\"},\n    },\n    {\n      {\"0x2f0344888f0526e1ac006b73ac101e295d9335c96dd54356b31fe28479f8e721\",\n       \"0x03d9c8b82e3fdc536e0dc94e102b13f6f02b68a87483ee77ff9d315b2c7d60de\"},\n      {\"0x1412168c94e087a95499f299a76337a1231305d114bb71c0401ecaa7f6786ede\",\n       \"0x13c23c6582942c390a5a788602da1db2a55a2424d0da56357809ac5b2e441f65\"},\n      {\"0x068261f08c1a54f57ada6457fd7c67781d4c2c24573e4952b706d814c9c49105\",\n       \"0x145e88698efe8890d0ac0148f7323474c9a32b7ee6eca0ad95915643c1bd689d\"},\n      {\"0x052767c246ff3a19021693323bb900c235816fdb508bdccbe3821a3c16c8685c\",\n       \"0x1993a757677c0957fb51ca2cccfcef84c924a13fdf4d91786ce1a1c6f733199c\"},\n      {\"0x04d2b14222eba6f926d62f2338facd5afeb6b9afb677c2b5c86f25293f0ec78f\",\n       \"0x22c64d91d4b0dc41516f1f8c3828a535c7918b49936ac04e5b57e466d0face1f\"},\n    },\n  };\n  // clang-format on\n\n  constexpr static std::string_view kTheta =\n      \"0x21041410ba638ce7c204e6cc158153c1d0c6d5e38dbd1a7d2d1c5e8dec7e7a41\";\n\n  constexpr static std::string_view kBeta =\n      \"0x2050b307c388ef3282c426b2240a553a27185eb64eaaa3de9f526485c465d3f5\";\n\n  constexpr static std::string_view kGamma =\n      \"0x0e282c5331a800dd692bf42af12eea40e4c67f1cb051cd39ad62b569226e518c\";\n\n  constexpr static std::string_view kY =\n      \"0x2cee61253b2de156f58ed09a43a84ea23caf6425865e16cbbb102d3c598bdaf8\";\n\n  constexpr static Point kVanishingHPolyCommitments[] = {\n      {\"0x1df5b4df0ccf9567c49ff6f0c6236f8179c0ee9e32300faf9422310976639ea9\",\n       \"0x129a725f88657cfb30028a65613cae4f585bc89766aedf1c4c1d0993a58eb001\"},\n      {\"0x13dc44671efd4269a2e34a5fa927c0d35bc4fca8f6b0f11e3c97d9abdc670082\",\n       \"0x2bda490d302dbdce632ca03b09fa7aa1a2e7d695e18d944239db8b7c6595e775\"},\n      {\"0x1525b707f8f798e7ead98f7fb110d222894f6403b18e5150f10a409d2f350af6\",\n       \"0x08f27a95b21e1614f7e249bca88f92292c81da46435dad8417cc132c12c5619e\"},\n  };\n\n  constexpr static std::string_view kX =\n      \"0x1e84d9712614ed3fb13d41f77541a62860d7badfded3ba24f2bda7ecc12a1434\";\n\n  constexpr static std::string_view kAdviceEvals[][5] = {\n      {\n          \"0x2bc77d8f6b47f8df8bff28f7f20256a362c3fd30d1f130933b6c234572f15e73\",\n          \"0x08234381d8e04a2699df3b69c21b9458ff7074f4faefba0b744e9db7aa51c1a1\",\n          \"0x0d224d51b688fd6ec5bd8e6d26b7c506b750f324d6f6644e5f4e4f54183755bd\",\n          \"0x03113ae05e76643ae6ff9ccafbc2c94692a710e3b226f8d10d844af6ed625667\",\n          \"0x0fab51a273bdf0649f2d5dbc89e42a8fedb5a52d7b5ac4eb92b378b476af0b5a\",\n      },\n      {\n          \"0x2bc77d8f6b47f8df8bff28f7f20256a362c3fd30d1f130933b6c234572f15e73\",\n          \"0x08234381d8e04a2699df3b69c21b9458ff7074f4faefba0b744e9db7aa51c1a1\",\n          \"0x0d224d51b688fd6ec5bd8e6d26b7c506b750f324d6f6644e5f4e4f54183755bd\",\n          \"0x03113ae05e76643ae6ff9ccafbc2c94692a710e3b226f8d10d844af6ed625667\",\n          \"0x0fab51a273bdf0649f2d5dbc89e42a8fedb5a52d7b5ac4eb92b378b476af0b5a\",\n      },\n  };\n\n  constexpr static std::string_view kFixedEvals[] = {\n      \"0x1e92316c17fdc8cd3f404eef698df737f68a242a8e38b54d605332cd0bb03198\",\n  };\n\n  static void TestConfig(Fibonacci3Config<F>& config) {\n    EXPECT_EQ(config.a, AdviceColumnKey(0));\n    EXPECT_EQ(config.b, AdviceColumnKey(1));\n    EXPECT_EQ(config.c, AdviceColumnKey(2));\n    EXPECT_EQ(config.a_equals_b.value_inv(), AdviceColumnKey(4));\n    EXPECT_EQ(\n        *config.a_equals_b.expr(),\n        *ExpressionFactory<F>::Sum(\n            ExpressionFactory<F>::Constant(F::One()),\n            ExpressionFactory<F>::Negated(ExpressionFactory<F>::Product(\n                ExpressionFactory<F>::Sum(\n                    ExpressionFactory<F>::Advice(\n                        AdviceQuery(0, Rotation::Cur(), config.a)),\n                    ExpressionFactory<F>::Negated(ExpressionFactory<F>::Advice(\n                        AdviceQuery(1, Rotation::Cur(), config.b)))),\n                ExpressionFactory<F>::Advice(AdviceQuery(\n                    2, Rotation::Cur(), config.a_equals_b.value_inv()))))));\n    EXPECT_EQ(config.output, AdviceColumnKey(3));\n    EXPECT_EQ(config.selector, Selector::Simple(0));\n  }\n\n  static Circuit GetCircuit() {\n    F a(10);\n    F b(12);\n    F c(15);\n    return Circuit(std::move(a), std::move(b), std::move(c));\n  }\n\n  static std::vector<Circuit> Get2Circuits() {\n    Circuit circuit = GetCircuit();\n    return {circuit, std::move(circuit)};\n  }\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_EXAMPLES_FIBONACCI_FIBONACCI3_CIRCUIT_TEST_DATA_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/examples/fibonacci/is_zero_chip.h",
    "content": "#ifndef TACHYON_ZK_PLONK_EXAMPLES_FIBONACCI_IS_ZERO_CHIP_H_\n#define TACHYON_ZK_PLONK_EXAMPLES_FIBONACCI_IS_ZERO_CHIP_H_\n\n#include <memory>\n#include <optional>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/zk/expressions/expression_factory.h\"\n#include \"tachyon/zk/plonk/constraint_system/constraint_system.h\"\n#include \"tachyon/zk/plonk/layout/region.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F>\nclass IsZeroConfig {\n public:\n  using Field = F;\n\n  IsZeroConfig(const AdviceColumnKey& value_inv,\n               std::unique_ptr<Expression<F>> is_zero_expr)\n      : value_inv_(value_inv), is_zero_expr_(std::move(is_zero_expr)) {}\n\n  const AdviceColumnKey& value_inv() const { return value_inv_; }\n  std::unique_ptr<Expression<F>> expr() const { return is_zero_expr_->Clone(); }\n\n  IsZeroConfig Clone() const { return {value_inv_, is_zero_expr_->Clone()}; }\n\n private:\n  AdviceColumnKey value_inv_;\n  std::unique_ptr<Expression<F>> is_zero_expr_;\n};\n\ntemplate <typename F>\nclass IsZeroChip {\n public:\n  explicit IsZeroChip(IsZeroConfig<F>&& config) : config_(std::move(config)) {}\n\n  static IsZeroConfig<F> Configure(\n      ConstraintSystem<F>& meta,\n      base::OnceCallback<std::unique_ptr<Expression<F>>(VirtualCells<F>&)>\n          q_enable,\n      base::OnceCallback<std::unique_ptr<Expression<F>>(VirtualCells<F>&)>\n          value,\n      const AdviceColumnKey& value_inv) {\n    std::unique_ptr<Expression<F>> is_zero_expr =\n        ExpressionFactory<F>::Constant(F::Zero());\n\n    meta.CreateGate(\"is_zero\", [&q_enable, &value, value_inv,\n                                &is_zero_expr](VirtualCells<F>& meta) mutable {\n      // clang-format off\n      //\n      // q_enable | value |  value_inv |  1 - value * value_inv | value * (1 - value * value_inv)\n      // ---------+-------+------------+------------------------+-------------------------------\n      //   yes    |   x   |    1/x     |         0              |         0\n      //   no     |   x   |    0       |         1              |         x\n      //   yes    |   0   |    0       |         1              |         0\n      //   yes    |   0   |    y       |         1              |         0\n      //\n      // clang-format on\n      std::unique_ptr<Expression<F>> q_enable_res =\n          std::move(q_enable).Run(meta);\n      std::unique_ptr<Expression<F>> value_res = std::move(value).Run(meta);\n      std::unique_ptr<Expression<F>> value_inv_res =\n          meta.QueryAdvice(value_inv, Rotation::Cur());\n\n      is_zero_expr = ExpressionFactory<F>::Constant(F::One()) -\n                     value_res->Clone() * std::move(value_inv_res);\n      std::vector<Constraint<F>> constraints;\n      constraints.emplace_back(std::move(q_enable_res) * std::move(value_res) *\n                               is_zero_expr->Clone());\n      return constraints;\n    });\n\n    return IsZeroConfig<F>(value_inv, std::move(is_zero_expr));\n  }\n\n  void Assign(Region<F>& region, RowIndex offset, const Value<F>& value) const {\n    std::optional<F> value_inv = value.value().Inverse();\n    if (UNLIKELY(!value_inv)) *value_inv = F::Zero();\n    region.AssignAdvice(\"value inv\", config_.value_inv(), offset,\n                        [&value_inv]() { return Value<F>::Known(*value_inv); });\n  }\n\n private:\n  IsZeroConfig<F> config_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_EXAMPLES_FIBONACCI_IS_ZERO_CHIP_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/examples/multi_lookup_circuit.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n// This is taken and modified from\n// https://github.com/scroll-tech/halo2/blob/1070391/halo2_proofs/tests/plonk_api.rs.\n\n#ifndef TACHYON_ZK_PLONK_EXAMPLES_MULTI_LOOKUP_CIRCUIT_H_\n#define TACHYON_ZK_PLONK_EXAMPLES_MULTI_LOOKUP_CIRCUIT_H_\n\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/zk/plonk/constraint_system/circuit.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F>\nclass MultiLookupCircuitConfig {\n public:\n  using Field = F;\n\n  MultiLookupCircuitConfig() = default;\n  MultiLookupCircuitConfig(const AdviceColumnKey& a, const AdviceColumnKey& b,\n                           const AdviceColumnKey& c, const AdviceColumnKey& d,\n                           const AdviceColumnKey& e, const FixedColumnKey& sa,\n                           const FixedColumnKey& sb, const FixedColumnKey& sc,\n                           const FixedColumnKey& sf, const FixedColumnKey& sm,\n                           const FixedColumnKey& sp,\n                           const LookupTableColumn& sl)\n      : a_(a),\n        b_(b),\n        c_(c),\n        d_(d),\n        e_(e),\n        sa_(sa),\n        sb_(sb),\n        sc_(sc),\n        sf_(sf),\n        sm_(sm),\n        sp_(sp),\n        sl_(sl) {}\n\n  MultiLookupCircuitConfig Clone() const {\n    return {a_, b_, c_, d_, e_, sa_, sb_, sc_, sf_, sm_, sp_, sl_};\n  }\n\n  const AdviceColumnKey& a() const { return a_; }\n  const AdviceColumnKey& b() const { return b_; }\n  const AdviceColumnKey& c() const { return c_; }\n  const AdviceColumnKey& d() const { return d_; }\n  const AdviceColumnKey& e() const { return e_; }\n  const FixedColumnKey& sa() const { return sa_; }\n  const FixedColumnKey& sb() const { return sb_; }\n  const FixedColumnKey& sc() const { return sc_; }\n  const FixedColumnKey& sf() const { return sf_; }\n  const FixedColumnKey& sm() const { return sm_; }\n  const FixedColumnKey& sp() const { return sp_; }\n  const LookupTableColumn& sl() const { return sl_; }\n\n  static MultiLookupCircuitConfig Configure(ConstraintSystem<F>& meta) {\n    AdviceColumnKey e = meta.CreateAdviceColumn();\n    AdviceColumnKey a = meta.CreateAdviceColumn();\n    AdviceColumnKey b = meta.CreateAdviceColumn();\n    FixedColumnKey sf = meta.CreateFixedColumn();\n    AdviceColumnKey c = meta.CreateAdviceColumn();\n    AdviceColumnKey d = meta.CreateAdviceColumn();\n    InstanceColumnKey p = meta.CreateInstanceColumn();\n\n    meta.EnableEquality(a);\n    meta.EnableEquality(b);\n    meta.EnableEquality(c);\n\n    FixedColumnKey sm = meta.CreateFixedColumn();\n    FixedColumnKey sa = meta.CreateFixedColumn();\n    FixedColumnKey sb = meta.CreateFixedColumn();\n    FixedColumnKey sc = meta.CreateFixedColumn();\n    FixedColumnKey sp = meta.CreateFixedColumn();\n    LookupTableColumn sl = meta.CreateLookupTableColumn();\n\n    Selector dummy = meta.CreateComplexSelector();\n    Selector dummy_2 = meta.CreateComplexSelector();\n    Selector dummy_3 = meta.CreateComplexSelector();\n\n    LookupTableColumn dummy_table = meta.CreateLookupTableColumn();\n\n    //\n    //   A         B      ...  sl\n    // [\n    //   instance  0      ...  0\n    //   a         a      ...  0\n    //   a         a²     ...  0\n    //   a         a      ...  0\n    //   a         a²     ...  0\n    //   ...       ...    ...  ...\n    //   ...       ...    ...  instance\n    //   ...       ...    ...  a\n    //   ...       ...    ...  a\n    //   ...       ...    ...  0\n    // ]\n    //\n\n    meta.Lookup(\"lookup\", [&a, &sl](VirtualCells<F> meta) {\n      std::unique_ptr<Expression<F>> a_expr = meta.QueryAny(a, Rotation::Cur());\n      lookup::Pairs<std::unique_ptr<Expression<F>>, LookupTableColumn>\n          lookup_pairs;\n      lookup_pairs.emplace_back(std::move(a_expr), sl);\n      return lookup_pairs;\n    });\n\n    meta.Lookup(\"lookup_same\", [&a, &sl](VirtualCells<F> meta) {\n      std::unique_ptr<Expression<F>> a_expr = meta.QueryAny(a, Rotation::Cur());\n      lookup::Pairs<std::unique_ptr<Expression<F>>, LookupTableColumn>\n          lookup_pairs;\n      lookup_pairs.emplace_back(std::move(a_expr), sl);\n      return lookup_pairs;\n    });\n\n    meta.Lookup(\"lookup_same\", [&b, &dummy, &dummy_2, &dummy_3,\n                                &dummy_table](VirtualCells<F> meta) {\n      std::unique_ptr<Expression<F>> b_expr = meta.QueryAny(b, Rotation::Cur());\n      std::unique_ptr<Expression<F>> dummy_expr = meta.QuerySelector(dummy);\n      std::unique_ptr<Expression<F>> dummy_2_expr = meta.QuerySelector(dummy_2);\n      std::unique_ptr<Expression<F>> dummy_3_expr = meta.QuerySelector(dummy_3);\n\n      lookup::Pairs<std::unique_ptr<Expression<F>>, LookupTableColumn>\n          lookup_pairs;\n      lookup_pairs.emplace_back(std::move(dummy_expr) *\n                                    std::move(dummy_2_expr) *\n                                    std::move(dummy_3_expr) * std::move(b_expr),\n                                dummy_table);\n      return lookup_pairs;\n    });\n\n    meta.CreateGate(\"Combined add-mult\", [&](VirtualCells<F> meta) {\n      std::unique_ptr<Expression<F>> d_expr =\n          meta.QueryAdvice(d, Rotation::Next());\n      std::unique_ptr<Expression<F>> a_expr =\n          meta.QueryAdvice(a, Rotation::Cur());\n      std::unique_ptr<Expression<F>> sf_expr =\n          meta.QueryFixed(sf, Rotation::Cur());\n      std::unique_ptr<Expression<F>> e_expr =\n          meta.QueryAdvice(e, Rotation::Prev());\n      std::unique_ptr<Expression<F>> b_expr =\n          meta.QueryAdvice(b, Rotation::Cur());\n      std::unique_ptr<Expression<F>> c_expr =\n          meta.QueryAdvice(c, Rotation::Cur());\n\n      std::unique_ptr<Expression<F>> sa_expr =\n          meta.QueryFixed(sa, Rotation::Cur());\n      std::unique_ptr<Expression<F>> sb_expr =\n          meta.QueryFixed(sb, Rotation::Cur());\n      std::unique_ptr<Expression<F>> sc_expr =\n          meta.QueryFixed(sc, Rotation::Cur());\n      std::unique_ptr<Expression<F>> sm_expr =\n          meta.QueryFixed(sm, Rotation::Cur());\n\n      std::unique_ptr<Expression<F>> a_clone = a_expr.get()->Clone();\n      std::unique_ptr<Expression<F>> b_clone = b_expr.get()->Clone();\n\n      std::vector<Constraint<F>> constraints;\n      constraints.emplace_back(\n          std::move(a_clone) * std::move(sa_expr) +\n          std::move(b_clone) * std::move(sb_expr) +\n          std::move(a_expr) * std::move(b_expr) * std::move(sm_expr) -\n          (std::move(c_expr) * std::move(sc_expr)) +\n          std::move(sf_expr) * (std::move(d_expr) * std::move(e_expr)));\n\n      return constraints;\n    });\n\n    meta.CreateGate(\"Public input\", [&](VirtualCells<F> meta) {\n      std::unique_ptr<Expression<F>> a_expr =\n          meta.QueryAdvice(a, Rotation::Cur());\n      std::unique_ptr<Expression<F>> p_expr =\n          meta.QueryInstance(p, Rotation::Cur());\n      std::unique_ptr<Expression<F>> sp_expr =\n          meta.QueryFixed(sp, Rotation::Cur());\n      std::vector<Constraint<F>> constraints;\n      constraints.emplace_back(std::move(sp_expr) *\n                               (std::move(a_expr) - std::move(p_expr)));\n      return constraints;\n    });\n\n    meta.EnableEquality(sf);\n    meta.EnableEquality(e);\n    meta.EnableEquality(d);\n    meta.EnableEquality(p);\n    meta.EnableEquality(sm);\n    meta.EnableEquality(sa);\n    meta.EnableEquality(sb);\n    meta.EnableEquality(sc);\n    meta.EnableEquality(sp);\n\n    return {a, b, c, d, e, sa, sb, sc, sf, sm, sp, sl};\n  }\n\n private:\n  AdviceColumnKey a_;\n  AdviceColumnKey b_;\n  AdviceColumnKey c_;\n  AdviceColumnKey d_;\n  AdviceColumnKey e_;\n  FixedColumnKey sa_;\n  FixedColumnKey sb_;\n  FixedColumnKey sc_;\n  FixedColumnKey sf_;\n  FixedColumnKey sm_;\n  FixedColumnKey sp_;\n  LookupTableColumn sl_;\n};\n\ntemplate <typename F>\nclass StandardPlonkChip {\n public:\n  explicit StandardPlonkChip(MultiLookupCircuitConfig<F>&& config)\n      : config_(std::move(config)) {}\n\n  void PublicInput(Layouter<F>* layouter, const Value<F>& value) {\n    layouter->AssignRegion(\"public_input\", [this, &value](Region<F>& region) {\n      region.AssignAdvice(\"value\", config_.a(), 0,\n                          [&value]() { return value; });\n      region.AssignFixed(\"public\", config_.sp(), 0,\n                         []() { return Value<F>::Known(F::One()); });\n    });\n  }\n\n  std::vector<AssignedCell<F>> RawMultiply(\n      Layouter<F>* layouter, const std::vector<Value<F>>& values) {\n    std::vector<AssignedCell<F>> ret;\n    ret.reserve(3);\n\n    layouter->AssignRegion(\n        \"raw_multiply\", [this, &values, &ret](Region<F>& region) {\n          AssignedCell<F> lhs = region.AssignAdvice(\n              \"lhs\", config_.a(), 0, [&values]() { return values[0]; });\n          ret.push_back(std::move(lhs));\n\n          region.AssignAdvice(\"lhs⁴\", config_.d(), 0,\n                              [&values]() { return values[0].Pow(4); });\n\n          AssignedCell<F> rhs = region.AssignAdvice(\n              \"rhs\", config_.b(), 0, [&values]() { return values[1]; });\n          ret.push_back(std::move(rhs));\n\n          region.AssignAdvice(\"rhs⁴\", config_.e(), 0,\n                              [&values]() { return values[1].Pow(4); });\n\n          AssignedCell<F> out = region.AssignAdvice(\n              \"out\", config_.c(), 0, [&values]() { return values[2]; });\n          ret.push_back(std::move(out));\n\n          region.AssignFixed(\"a\", config_.sa(), 0,\n                             []() { return Value<F>::Known(F::Zero()); });\n          region.AssignFixed(\"b\", config_.sb(), 0,\n                             []() { return Value<F>::Known(F::Zero()); });\n          region.AssignFixed(\"c\", config_.sc(), 0,\n                             []() { return Value<F>::Known(F::One()); });\n          region.AssignFixed(\"a * b\", config_.sm(), 0,\n                             []() { return Value<F>::Known(F::One()); });\n        });\n\n    return ret;\n  }\n\n  std::vector<AssignedCell<F>> RawAdd(Layouter<F>* layouter,\n                                      const std::vector<Value<F>>& values) {\n    std::vector<AssignedCell<F>> ret;\n    ret.reserve(3);\n\n    layouter->AssignRegion(\"raw_add\", [this, &values, &ret](Region<F>& region) {\n      AssignedCell<F> lhs = region.AssignAdvice(\n          \"lhs\", config_.a(), 0, [&values]() { return values[0]; });\n      ret.push_back(std::move(lhs));\n\n      region.AssignAdvice(\"lhs⁴\", config_.d(), 0,\n                          [&values]() { return values[0].Pow(4); });\n\n      AssignedCell<F> rhs = region.AssignAdvice(\n          \"rhs\", config_.b(), 0, [&values]() { return values[1]; });\n      ret.push_back(std::move(rhs));\n\n      region.AssignAdvice(\"rhs⁴\", config_.e(), 0,\n                          [&values]() { return values[1].Pow(4); });\n\n      AssignedCell<F> out = region.AssignAdvice(\n          \"out\", config_.c(), 0, [&values]() { return values[2]; });\n      ret.push_back(std::move(out));\n\n      region.AssignFixed(\"a\", config_.sa(), 0,\n                         []() { return Value<F>::Known(F::One()); });\n      region.AssignFixed(\"b\", config_.sb(), 0,\n                         []() { return Value<F>::Known(F::One()); });\n      region.AssignFixed(\"c\", config_.sc(), 0,\n                         []() { return Value<F>::Known(F::One()); });\n      region.AssignFixed(\"a * b\", config_.sm(), 0,\n                         []() { return Value<F>::Known(F::Zero()); });\n    });\n\n    return ret;\n  }\n\n  void Copy(Layouter<F>* layouter, const Cell& left, const Cell& right) {\n    layouter->AssignRegion(\"copy\", [&left, &right](Region<F>& region) {\n      region.ConstrainEqual(left, right);\n      region.ConstrainEqual(left, right);\n    });\n  }\n\n  void LookupTableFromValues(Layouter<F>* layouter,\n                             const std::vector<F>& values) {\n    layouter->AssignLookupTable(\"\", [this, &values](LookupTable<F>& table) {\n      for (size_t i = 0; i < values.size(); ++i) {\n        if (!table.AssignCell(\"table col\", config_.sl(), i, [&values, i]() {\n              return Value<F>::Known(values[i]);\n            }))\n          return false;\n      }\n      return true;\n    });\n  }\n\n private:\n  MultiLookupCircuitConfig<F> config_;\n};\n\ntemplate <typename F, template <typename> class _FloorPlanner>\nclass MultiLookupCircuit : public Circuit<MultiLookupCircuitConfig<F>> {\n public:\n  using FloorPlanner = _FloorPlanner<MultiLookupCircuit<F, _FloorPlanner>>;\n\n  MultiLookupCircuit() = default;\n  MultiLookupCircuit(const F& a, const std::vector<F>& lookup_table)\n      : a_(Value<F>::Known(a)), lookup_table_(lookup_table) {}\n\n  std::unique_ptr<Circuit<MultiLookupCircuitConfig<F>>> WithoutWitness()\n      const override {\n    return std::make_unique<MultiLookupCircuit>();\n  }\n\n  static MultiLookupCircuitConfig<F> Configure(ConstraintSystem<F>& meta) {\n    return MultiLookupCircuitConfig<F>::Configure(meta);\n  }\n\n  void Synthesize(MultiLookupCircuitConfig<F>&& config,\n                  Layouter<F>* layouter) const override {\n    StandardPlonkChip<F> cs(std::move(config));\n\n    cs.PublicInput(layouter, Value<F>::Known(F::One() + F::One()));\n\n    for (size_t i = 0; i < 10; ++i) {\n      Value<F> a_squared = a_.SquareImpl();\n      std::vector<Value<F>> mul_values;\n      mul_values.reserve(3);\n      mul_values.push_back(a_);\n      mul_values.push_back(a_);\n      mul_values.push_back(a_squared);\n      std::vector<AssignedCell<F>> mul_cells =\n          cs.RawMultiply(layouter, mul_values);\n\n      Value<F> fin = a_squared.Add(a_);\n      std::vector<Value<F>> add_values;\n      add_values.reserve(3);\n      add_values.push_back(a_);\n      add_values.push_back(a_squared);\n      add_values.push_back(fin);\n      std::vector<AssignedCell<F>> add_cells = cs.RawAdd(layouter, add_values);\n\n      cs.Copy(layouter, mul_cells[0].cell(), add_cells[0].cell());\n      cs.Copy(layouter, add_cells[1].cell(), mul_cells[2].cell());\n    }\n\n    cs.LookupTableFromValues(layouter, lookup_table_);\n  }\n\n private:\n  Value<F> a_;\n  std::vector<F> lookup_table_;\n};\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_EXAMPLES_MULTI_LOOKUP_CIRCUIT_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/examples/multi_lookup_circuit_test.cc",
    "content": "#include \"tachyon/zk/plonk/examples/multi_lookup_circuit.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/zk/plonk/examples/circuit_test.h\"\n#include \"tachyon/zk/plonk/examples/multi_lookup_circuit_test_data.h\"\n#include \"tachyon/zk/plonk/layout/floor_planner/simple_floor_planner.h\"\n\nnamespace tachyon::zk::plonk {\n\nnamespace {\n\ntemplate <typename TestArguments>\nclass MultiLookupCircuitTest\n    : public CircuitTest<TestArguments,\n                         MultiLookupTestData<typename TestArguments::Circuit,\n                                             typename TestArguments::PS>> {};\n\n}  // namespace\n\n// clang-format off\nusing MultiLookupTestArgumentsList = testing::Types<\n    TestArguments<MultiLookupCircuit<BN254SHPlonk::Field, SimpleFloorPlanner>, BN254SHPlonkLogDerivativeHalo2>,\n    TestArguments<MultiLookupCircuit<BN254GWC::Field, SimpleFloorPlanner>, BN254GWCLogDerivativeHalo2>>;\n// clang-format on\n\nTYPED_TEST_SUITE(MultiLookupCircuitTest, MultiLookupTestArgumentsList);\n\nTYPED_TEST(MultiLookupCircuitTest, CreateProof) { this->CreateProofTest(); }\nTYPED_TEST(MultiLookupCircuitTest, VerifyProof) { this->VerifyProofTest(); }\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/examples/multi_lookup_circuit_test_data.h",
    "content": "#ifndef TACHYON_ZK_PLONK_EXAMPLES_MULTI_LOOKUP_CIRCUIT_TEST_DATA_H_\n#define TACHYON_ZK_PLONK_EXAMPLES_MULTI_LOOKUP_CIRCUIT_TEST_DATA_H_\n\n#include <stdint.h>\n\n#include <string_view>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/zk/plonk/examples/circuit_test_data.h\"\n#include \"tachyon/zk/plonk/examples/circuit_test_type_traits.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename Circuit, typename PS, typename SFINAE = void>\nclass MultiLookupTestData : public CircuitTestData<Circuit, PS> {};\n\n// PCS = SHPlonk\ntemplate <typename Circuit, typename PS>\nclass MultiLookupTestData<Circuit, PS,\n                          std::enable_if_t<IsSHPlonk<typename PS::PCS>>>\n    : public CircuitTestData<Circuit, PS> {\n public:\n  using PCS = typename PS::PCS;\n  using F = typename PCS::Field;\n  using Evals = typename PCS::Evals;\n\n  // Set flags of values to be used as true\n  constexpr static bool kAdviceCommitmentsFlag = true;\n  constexpr static bool kLookupMPolyCommitmentsFlag = true;\n  constexpr static bool kPermutationProductCommitmentsFlag = true;\n  constexpr static bool kLookupSumCommitmentsFlag = true;\n  constexpr static bool kAdviceEvalsFlag = true;\n  constexpr static bool kFixedEvalsFlag = true;\n  constexpr static bool kCommonPermutationEvalsFlag = true;\n  constexpr static bool kPermutationProductEvalsFlag = true;\n  constexpr static bool kPermutationProductNextEvalsFlag = true;\n  constexpr static bool kLookupSumEvalsFlag = true;\n  constexpr static bool kLookupSumNextEvalsFlag = true;\n  constexpr static bool kPermutationProductLastEvalsFlag = true;\n  constexpr static bool kLookupMEvalsFlag = true;\n\n  constexpr static size_t kN = 32;\n\n  constexpr static std::string_view kPinnedConstraintSystem = \"\";\n\n  constexpr static bool kSelectors[][kN] = {{}};\n\n  constexpr static std::string_view kPinnedVerifyingKey = \"\";\n\n  constexpr static std::string_view kTranscriptRepr = \"\";\n\n  constexpr static std::string_view kLFirst[] = {};\n\n  constexpr static uint8_t kProof[] = {\n      176, 39,  119, 154, 232, 230, 108, 49,  145, 63,  70,  98,  95,  39,  79,\n      60,  158, 217, 82,  29,  203, 144, 111, 78,  192, 88,  4,   136, 239, 87,\n      182, 33,  111, 96,  195, 90,  244, 247, 129, 190, 245, 7,   134, 122, 7,\n      38,  111, 134, 98,  219, 109, 209, 22,  189, 233, 94,  254, 37,  168, 159,\n      176, 235, 222, 156, 235, 153, 242, 158, 111, 46,  218, 237, 137, 157, 79,\n      236, 121, 54,  239, 149, 20,  6,   230, 238, 214, 254, 55,  128, 121, 203,\n      148, 43,  205, 237, 34,  148, 104, 119, 235, 159, 27,  152, 52,  194, 129,\n      28,  241, 142, 82,  119, 54,  218, 65,  98,  24,  12,  186, 15,  178, 46,\n      117, 129, 0,   141, 246, 154, 214, 166, 53,  198, 144, 126, 19,  184, 239,\n      70,  130, 205, 109, 242, 43,  222, 115, 252, 25,  135, 221, 140, 138, 27,\n      149, 217, 0,   210, 118, 86,  211, 115, 60,  19,  176, 39,  119, 154, 232,\n      230, 108, 49,  145, 63,  70,  98,  95,  39,  79,  60,  158, 217, 82,  29,\n      203, 144, 111, 78,  192, 88,  4,   136, 239, 87,  182, 33,  111, 96,  195,\n      90,  244, 247, 129, 190, 245, 7,   134, 122, 7,   38,  111, 134, 98,  219,\n      109, 209, 22,  189, 233, 94,  254, 37,  168, 159, 176, 235, 222, 156, 235,\n      153, 242, 158, 111, 46,  218, 237, 137, 157, 79,  236, 121, 54,  239, 149,\n      20,  6,   230, 238, 214, 254, 55,  128, 121, 203, 148, 43,  205, 237, 34,\n      148, 104, 119, 235, 159, 27,  152, 52,  194, 129, 28,  241, 142, 82,  119,\n      54,  218, 65,  98,  24,  12,  186, 15,  178, 46,  117, 129, 0,   141, 246,\n      154, 214, 166, 53,  198, 144, 126, 19,  184, 239, 70,  130, 205, 109, 242,\n      43,  222, 115, 252, 25,  135, 221, 140, 138, 27,  149, 217, 0,   210, 118,\n      86,  211, 115, 60,  19,  10,  99,  22,  17,  150, 115, 127, 174, 68,  191,\n      125, 172, 243, 223, 29,  19,  65,  199, 56,  90,  36,  185, 145, 245, 49,\n      246, 227, 67,  84,  220, 74,  139, 156, 42,  177, 61,  71,  15,  22,  47,\n      239, 184, 119, 62,  93,  114, 196, 215, 241, 206, 252, 57,  228, 10,  218,\n      80,  89,  7,   22,  56,  81,  45,  251, 149, 10,  99,  22,  17,  150, 115,\n      127, 174, 68,  191, 125, 172, 243, 223, 29,  19,  65,  199, 56,  90,  36,\n      185, 145, 245, 49,  246, 227, 67,  84,  220, 74,  139, 156, 42,  177, 61,\n      71,  15,  22,  47,  239, 184, 119, 62,  93,  114, 196, 215, 241, 206, 252,\n      57,  228, 10,  218, 80,  89,  7,   22,  56,  81,  45,  251, 149, 28,  3,\n      65,  135, 110, 91,  15,  35,  158, 68,  85,  246, 95,  194, 5,   141, 196,\n      140, 25,  179, 248, 121, 239, 211, 144, 144, 44,  26,  67,  129, 244, 149,\n      30,  251, 146, 72,  211, 253, 167, 86,  140, 141, 154, 48,  105, 237, 92,\n      122, 34,  156, 8,   27,  211, 92,  16,  45,  250, 207, 19,  210, 128, 168,\n      98,  2,   241, 174, 125, 226, 188, 249, 214, 158, 121, 18,  43,  250, 242,\n      105, 199, 97,  107, 121, 77,  6,   65,  229, 222, 17,  160, 104, 80,  128,\n      230, 180, 216, 146, 95,  241, 47,  41,  128, 154, 204, 17,  131, 53,  251,\n      100, 244, 193, 22,  7,   77,  38,  44,  83,  35,  42,  192, 245, 37,  227,\n      171, 161, 29,  45,  233, 132, 225, 30,  210, 2,   42,  240, 142, 220, 192,\n      166, 146, 141, 31,  232, 14,  10,  132, 230, 192, 152, 232, 77,  180, 85,\n      171, 229, 149, 32,  144, 16,  39,  170, 94,  30,  114, 27,  232, 16,  47,\n      29,  229, 140, 11,  182, 148, 226, 209, 180, 83,  165, 203, 9,   10,  226,\n      28,  96,  223, 57,  11,  106, 44,  33,  192, 31,  45,  91,  123, 216, 141,\n      234, 53,  191, 131, 128, 151, 193, 50,  234, 182, 178, 40,  69,  229, 234,\n      95,  72,  158, 32,  195, 169, 120, 91,  140, 239, 130, 142, 99,  121, 80,\n      26,  191, 199, 181, 2,   227, 198, 149, 0,   63,  72,  195, 146, 44,  6,\n      26,  10,  66,  102, 245, 217, 239, 78,  145, 112, 176, 224, 104, 174, 1,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   166, 134, 204, 95,  10,  123, 51,  110, 74,  33,  8,   169, 117, 6,\n      101, 208, 63,  186, 18,  250, 121, 181, 73,  242, 110, 20,  59,  196, 62,\n      232, 35,  32,  9,   71,  7,   127, 151, 17,  118, 7,   241, 216, 98,  226,\n      245, 228, 194, 151, 103, 250, 192, 89,  59,  68,  199, 59,  146, 227, 75,\n      38,  196, 122, 86,  156, 251, 202, 126, 62,  148, 62,  181, 105, 14,  140,\n      77,  43,  175, 101, 99,  61,  152, 206, 195, 123, 241, 182, 72,  219, 223,\n      52,  173, 99,  4,   84,  62,  157, 177, 187, 219, 240, 189, 151, 52,  139,\n      61,  150, 103, 11,  51,  223, 135, 237, 200, 138, 36,  133, 71,  142, 123,\n      9,   210, 139, 139, 128, 56,  87,  242, 43,  94,  61,  168, 41,  111, 57,\n      158, 106, 80,  188, 54,  152, 105, 116, 193, 200, 172, 143, 60,  155, 57,\n      177, 206, 30,  15,  103, 2,   3,   155, 102, 75,  47,  154, 51,  79,  6,\n      180, 205, 195, 242, 21,  199, 166, 12,  128, 159, 90,  85,  163, 128, 114,\n      36,  170, 162, 213, 226, 192, 202, 149, 153, 124, 79,  58,  19,  76,  241,\n      124, 244, 255, 99,  177, 35,  190, 240, 190, 223, 143, 180, 137, 153, 127,\n      193, 174, 219, 184, 87,  176, 134, 177, 21,  149, 199, 44,  173, 237, 161,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   134, 89,  86,  17,  79,  228, 58,  209, 169, 130, 131, 180, 240,\n      157, 122, 16,  149, 111, 250, 176, 93,  106, 5,   253, 166, 69,  62,  180,\n      152, 14,  37,  12,  80,  123, 32,  115, 68,  249, 237, 3,   104, 42,  80,\n      254, 129, 185, 248, 49,  37,  101, 219, 220, 118, 167, 239, 218, 25,  50,\n      215, 182, 8,   124, 135, 5,   194, 103, 245, 63,  97,  41,  223, 91,  204,\n      238, 4,   52,  206, 81,  244, 76,  153, 98,  203, 133, 37,  92,  86,  39,\n      48,  249, 238, 53,  31,  57,  188, 28,  248, 194, 244, 100, 18,  136, 104,\n      158, 4,   136, 222, 60,  110, 231, 179, 82,  255, 149, 131, 141, 148, 185,\n      68,  251, 234, 100, 231, 18,  248, 101, 146, 13,  65,  71,  35,  143, 178,\n      197, 109, 232, 59,  140, 174, 111, 131, 26,  130, 37,  79,  40,  56,  116,\n      202, 145, 237, 108, 30,  54,  107, 207, 22,  37,  90,  6,   118, 155, 164,\n      243, 4,   235, 204, 46,  134, 143, 218, 234, 54,  101, 210, 154, 78,  172,\n      34,  233, 188, 41,  208, 112, 180, 10,  8,   94,  59,  106, 102, 42,  134,\n      239, 25,  85,  202, 38,  222, 8,   167, 147, 162, 86,  47,  237, 67,  189,\n      222, 221, 111, 212, 90,  82,  253, 224, 81,  235, 74,  23,  56,  47,  82,\n      21,  134, 89,  86,  17,  79,  228, 58,  209, 169, 130, 131, 180, 240, 157,\n      122, 16,  149, 111, 250, 176, 93,  106, 5,   253, 166, 69,  62,  180, 152,\n      14,  37,  12,  80,  123, 32,  115, 68,  249, 237, 3,   104, 42,  80,  254,\n      129, 185, 248, 49,  37,  101, 219, 220, 118, 167, 239, 218, 25,  50,  215,\n      182, 8,   124, 135, 5,   194, 103, 245, 63,  97,  41,  223, 91,  204, 238,\n      4,   52,  206, 81,  244, 76,  153, 98,  203, 133, 37,  92,  86,  39,  48,\n      249, 238, 53,  31,  57,  188, 28,  248, 194, 244, 100, 18,  136, 104, 158,\n      4,   136, 222, 60,  110, 231, 179, 82,  255, 149, 131, 141, 148, 185, 68,\n      251, 234, 100, 231, 18,  248, 101, 146, 13,  65,  71,  35,  143, 178, 197,\n      109, 232, 59,  140, 174, 111, 131, 26,  130, 37,  79,  40,  56,  116, 202,\n      145, 237, 108, 30,  54,  107, 207, 22,  37,  90,  6,   118, 155, 164, 243,\n      4,   235, 204, 46,  134, 143, 218, 234, 54,  101, 210, 154, 78,  172, 34,\n      233, 188, 41,  208, 112, 180, 10,  8,   94,  59,  106, 102, 42,  134, 239,\n      25,  85,  202, 38,  222, 8,   167, 147, 162, 86,  47,  237, 67,  189, 222,\n      221, 111, 212, 90,  82,  253, 224, 81,  235, 74,  23,  56,  47,  82,  21,\n      82,  3,   151, 152, 111, 202, 165, 255, 191, 28,  192, 197, 237, 122, 53,\n      133, 77,  66,  14,  210, 99,  178, 100, 94,  220, 155, 114, 64,  96,  113,\n      137, 9,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   62,  128, 35,  165, 43,  17,  130, 248, 171,\n      104, 219, 244, 170, 6,   103, 158, 171, 237, 61,  147, 97,  250, 191, 169,\n      230, 200, 38,  150, 141, 93,  180, 7,   62,  128, 35,  165, 43,  17,  130,\n      248, 171, 104, 219, 244, 170, 6,   103, 158, 171, 237, 61,  147, 97,  250,\n      191, 169, 230, 200, 38,  150, 141, 93,  180, 7,   203, 20,  245, 186, 235,\n      114, 192, 197, 108, 216, 190, 119, 43,  244, 82,  15,  139, 33,  49,  137,\n      189, 153, 233, 182, 139, 36,  75,  45,  40,  134, 155, 38,  141, 148, 209,\n      21,  192, 97,  62,  205, 192, 111, 227, 130, 128, 237, 235, 112, 223, 51,\n      243, 245, 91,  159, 41,  13,  165, 91,  36,  151, 154, 40,  231, 30,  168,\n      159, 118, 58,  42,  93,  213, 217, 247, 72,  168, 108, 142, 182, 225, 236,\n      138, 190, 150, 199, 145, 186, 241, 132, 109, 1,   223, 200, 185, 85,  147,\n      18,  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   1,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   209, 15,  158, 121, 176, 200,\n      123, 96,  152, 248, 247, 67,  167, 235, 209, 202, 245, 216, 227, 44,  29,\n      10,  227, 10,  149, 12,  158, 74,  246, 91,  224, 24,  93,  210, 14,  60,\n      202, 3,   66,  45,  105, 41,  153, 40,  165, 16,  120, 82,  136, 195, 88,\n      2,   140, 230, 92,  184, 126, 42,  83,  156, 45,  107, 19,  28,  60,  180,\n      77,  254, 226, 147, 36,  44,  133, 210, 145, 114, 86,  77,  121, 175, 108,\n      176, 151, 24,  126, 2,   39,  166, 145, 50,  203, 70,  40,  156, 85,  47,\n      177, 148, 164, 238, 247, 20,  201, 100, 253, 177, 48,  228, 38,  63,  131,\n      84,  97,  195, 180, 233, 54,  238, 245, 183, 55,  170, 43,  87,  230, 228,\n      10,  19,  119, 218, 199, 216, 69,  63,  0,   115, 212, 97,  144, 2,   46,\n      247, 219, 161, 62,  89,  207, 56,  229, 120, 229, 232, 32,  60,  126, 178,\n      227, 199, 21,  26,  172, 4,   255, 158, 126, 57,  106, 245, 57,  71,  148,\n      212, 51,  227, 176, 160, 28,  206, 254, 76,  118, 222, 222, 46,  225, 186,\n      92,  42,  157, 226, 77,  22,  14,  190, 190, 37,  195, 179, 65,  240, 70,\n      243, 57,  44,  21,  112, 12,  155, 251, 107, 79,  138, 99,  57,  99,  234,\n      33,  222, 85,  117, 74,  40,  97,  40,  66,  57,  209, 48,  55,  208, 181,\n      82,  218, 255, 186, 73,  102, 20,  3,   119, 10,  18,  95,  34,  132, 86,\n      205, 73,  135, 161, 90,  57,  210, 4,   189, 24,  244, 89,  21,  5,   158,\n      241, 21,  7,   221, 158, 196, 110, 73,  160, 123, 84,  234, 36,  6,   16,\n      177, 104, 51,  52,  226, 132, 19,  227, 241, 55,  53,  26,  201, 130, 53,\n      53,  44,  45,  38,  23,  41,  72,  24,  212, 94,  174, 138, 220, 11,  234,\n      123, 235, 147, 166, 164, 62,  109, 57,  20,  205, 59,  41,  53,  40,  27,\n      15,  178, 229, 117, 52,  114, 115, 40,  160, 203, 187, 15,  90,  88,  91,\n      74,  238, 37,  111, 235, 239, 90,  13,  160, 129, 134, 231, 73,  235, 21,\n      24,  81,  28,  49,  78,  110, 10,  159, 175, 91,  255, 34,  172, 197, 132,\n      16,  25,  133, 246, 77,  58,  193, 69,  16,  27,  238, 167, 65,  25,  47,\n      10,  154, 20,  56,  149, 240, 170, 103, 66,  45,  58,  12,  114, 127, 152,\n      195, 160, 247, 253, 75,  148, 54,  145, 13,  149, 37,  9,   145, 122, 193,\n      5,   230, 135, 212, 20,  86,  84,  59,  198, 63,  204, 25,  89,  61,  136,\n      75,  175, 218, 129, 186, 194, 67,  211, 24,  197, 191, 139, 55,  162, 125,\n      73,  59,  144, 134, 172, 157, 39,  200, 66,  232, 184, 76,  95,  20,  243,\n      240, 140, 233, 124, 82,  245, 253, 245, 182, 195, 60,  237, 28,  107, 214,\n      132, 201, 18,  183, 17,  204, 157, 114, 14,  133, 18,  25,  80,  204, 28,\n      59,  173, 0,   172, 75,  254, 192, 104, 55,  8,   236, 93,  176, 75,  245,\n      140, 38,  26,  208, 183, 147, 35,  1,   215, 77,  12,  217, 5,   80,  117,\n      24,  40,  235, 45,  255, 109, 155, 44,  19,  47,  222, 225, 121, 30,  227,\n      43,  16,  231, 186, 24,  165, 234, 226, 8,   183, 122, 108, 15,  197, 245,\n      59,  57,  87,  48,  34,  18,  26,  119, 37,  196, 20,  151, 81,  91,  245,\n      247, 15,  158, 219, 15,  27,  135, 37,  247, 213, 222, 51,  187, 89,  34,\n      81,  90,  189, 107, 18,  169, 241, 173, 93,  93,  36,  118, 84,  252, 46,\n      146, 201, 120, 123, 61,  114, 0,   245, 90,  99,  81,  200, 26,  238, 247,\n      247, 34,  31,  224, 242, 146, 211, 244, 142, 138, 51,  83,  131, 103, 168,\n      201, 75,  77,  204, 60,  196, 115, 79,  123, 50,  194, 206, 184, 132, 213,\n      94,  183, 48,  43,  83,  225, 6,   215, 135, 134, 124, 155, 62,  21,  236,\n      105, 0,   73,  223, 90,  96,  82,  221, 148, 95,  227, 208, 229, 2,   85,\n      133, 1,   190, 166, 239, 1,   140, 147, 239, 190, 216, 33,  82,  232, 1,\n      32,  237, 37,  151, 133, 188, 46,  190, 253, 244, 100, 23,  18,  244, 245,\n      183, 126, 117, 199, 113, 137, 235, 38,  87,  24,  137, 168, 58,  135, 102,\n      76,  129, 127, 111, 235, 27,  79,  61,  172, 37,  161, 100, 222, 105, 91,\n      126, 191, 90,  238, 118, 234, 236, 254, 184, 41,  30,  244, 136, 17,  166,\n      114, 133, 76,  86,  254, 5,   90,  209, 194, 112, 13,  188, 65,  193, 44,\n      161, 152, 50,  214, 196, 17,  49,  252, 23,  111, 20,  38,  17,  99,  206,\n      181, 54,  56,  232, 165, 238, 39,  169, 107, 132, 108, 98,  53,  71,  215,\n      151, 57,  131, 217, 20,  222, 193, 104, 235, 240, 145, 87,  252, 41,  232,\n      39,  46,  52,  136, 254, 234, 67,  122, 131, 108, 128, 12,  41,  5,   26,\n      39,  7,   89,  148, 212, 70,  1,   243, 11,  59,  191, 245, 156, 195, 245,\n      1,   133, 16,  223, 205, 12,  129, 7,   109, 165, 127, 64,  247, 143, 147,\n      227, 20,  155, 22,  43,  222, 213, 142, 163, 169, 181, 12,  177, 28,  6,\n      241, 249, 1,   231, 191, 61,  111, 106, 255, 255, 117, 90,  107, 84,  40,\n      142, 197, 61,  196, 135, 20,  50,  125, 190, 214, 64,  160, 71,  167, 240,\n      66,  216, 83,  8,   18,  208, 156, 247, 164, 127, 19,  113, 138, 114, 193,\n      182, 178, 124, 198, 175, 161, 217, 153, 28,  199, 238, 15,  20,  208, 20,\n      203, 38,  47,  18,  103, 50,  15,  111, 48,  226, 17,  176, 218, 188, 63,\n      57,  206, 84,  26,  215, 89,  175, 210, 17,  0,   13,  201, 227, 211, 35,\n      172, 217, 113, 192, 110, 2,   205, 140, 27,  17,  99,  206, 181, 54,  56,\n      232, 165, 238, 39,  169, 107, 132, 108, 98,  53,  71,  215, 151, 57,  131,\n      217, 20,  222, 193, 104, 235, 240, 145, 87,  252, 41,  180, 248, 176, 184,\n      3,   248, 6,   228, 178, 236, 68,  217, 1,   26,  61,  0,   183, 3,   85,\n      232, 51,  52,  225, 21,  139, 120, 193, 117, 116, 205, 208, 10,  217, 145,\n      43,  96,  236, 126, 240, 143, 134, 185, 74,  30,  182, 234, 176, 249, 81,\n      237, 176, 199, 223, 20,  236, 170, 64,  230, 151, 154, 114, 147, 206, 30,\n      231, 191, 61,  111, 106, 255, 255, 117, 90,  107, 84,  40,  142, 197, 61,\n      196, 135, 20,  50,  125, 190, 214, 64,  160, 71,  167, 240, 66,  216, 83,\n      8,   18,  82,  197, 122, 236, 219, 48,  31,  48,  86,  40,  204, 9,   103,\n      163, 159, 204, 203, 254, 249, 137, 4,   201, 52,  88,  39,  144, 166, 162,\n      48,  185, 0,   174, 197, 145, 182, 133, 144, 111, 159, 35,  206, 235, 179,\n      93,  186, 87,  246, 123, 16,  101, 184, 10,  125, 216, 76,  74,  54,  143,\n      46,  188, 19,  192, 37,  14};\n\n  constexpr static Point kAdviceCommitments[][5] = {\n      {{\"0x21b657ef880458c04e6f90cb1d52d99e3c4f275f62463f91316ce6e89a7727b0\",\n        \"0x018e7e556102c958d925644e9fdec38e1b6c88572a98ce9871485937ff5c9aa4\"},\n       {\"0x1cdeebb09fa825fe5ee9bd16d16ddb62866f26077a8607f5be81f7f45ac3606f\",\n        \"0x0dd225bfadd39c83ca40e0a578ae098e3dce0b62b8bbf3445e63fe035c71ce09\"},\n       {\"0x1422edcd2b94cb798037fed6eee6061495ef3679ec4f9d89edda2e6f9ef299eb\",\n        \"0x07aff8c1c8c754b022575c2e7e2f98483f08111f3dc0a9d7e2b119c5063c97b9\"},\n       {\"0x26d69af68d0081752eb20fba0c186241da3677528ef11c81c234981b9feb7768\",\n        \"0x0cb675234a966e9b7fe6f87c6da86e5b3d534330dd6ebc08302da54ccee2352f\"},\n       {\"0x133c73d35676d200d9951b8a8cdd8719fc73de2bf26dcd8246efb8137e90c635\",\n        \"0x1eb2849fbface2516b2dfa0d9ed2ec2b397a04ae228a2541d32bc83bb429de30\"}},\n      {{\"0x21b657ef880458c04e6f90cb1d52d99e3c4f275f62463f91316ce6e89a7727b0\",\n        \"0x018e7e556102c958d925644e9fdec38e1b6c88572a98ce9871485937ff5c9aa4\"},\n       {\"0x1cdeebb09fa825fe5ee9bd16d16ddb62866f26077a8607f5be81f7f45ac3606f\",\n        \"0x0dd225bfadd39c83ca40e0a578ae098e3dce0b62b8bbf3445e63fe035c71ce09\"},\n       {\"0x1422edcd2b94cb798037fed6eee6061495ef3679ec4f9d89edda2e6f9ef299eb\",\n        \"0x07aff8c1c8c754b022575c2e7e2f98483f08111f3dc0a9d7e2b119c5063c97b9\"},\n       {\"0x26d69af68d0081752eb20fba0c186241da3677528ef11c81c234981b9feb7768\",\n        \"0x0cb675234a966e9b7fe6f87c6da86e5b3d534330dd6ebc08302da54ccee2352f\"},\n       {\"0x133c73d35676d200d9951b8a8cdd8719fc73de2bf26dcd8246efb8137e90c635\",\n        \"0x1eb2849fbface2516b2dfa0d9ed2ec2b397a04ae228a2541d32bc83bb429de30\"}},\n  };\n\n  constexpr static std::string_view kTheta =\n      \"0x1def3cccec9613b680e85b2aec2964d537bf3f8f37c252ec6259cc42c5e4edf8\";\n\n  constexpr static Point kLookupMPolyCommitments[][2] = {\n      {{\"0x0b4adc5443e3f631f591b9245a38c741131ddff3ac7dbf44ae7f73961116630a\",\n        \"0x2a5f18e67444124e520e079b7ff831b8bfee476778b59ff3a7950d6210f02a29\"},\n       {\"0x15fb2d513816075950da0ae439fccef1d7c4725d3e77b8ef2f160f473db12a9c\",\n        \"0x13a5bf0b4bde809ec00d335afbfb4ee7d1a01b17d5f6ba51b353be98ac7034cb\"}},\n      {{\"0x0b4adc5443e3f631f591b9245a38c741131ddff3ac7dbf44ae7f73961116630a\",\n        \"0x2a5f18e67444124e520e079b7ff831b8bfee476778b59ff3a7950d6210f02a29\"},\n       {\"0x15fb2d513816075950da0ae439fccef1d7c4725d3e77b8ef2f160f473db12a9c\",\n        \"0x13a5bf0b4bde809ec00d335afbfb4ee7d1a01b17d5f6ba51b353be98ac7034cb\"}}};\n\n  constexpr static std::string_view kBeta =\n      \"0x196b280fb88110b45b45a5c3c2153208330174e892ba5869520981276747f8cb\";\n\n  constexpr static std::string_view kGamma =\n      \"0x08e3e0ffc02004246de5898763572f2cf707fd9a1a5022be65770281cc78563e\";\n\n  constexpr static Point kPermutationProductCommitments[][2] = {\n      {{\"0x15f481431a2c9090d3ef79f8b3198cc48d05c25ff655449e230f5b6e8741031c\",\n        \"0x25c1831248991382d7fd7036f15c533c6de53bc5539967b3ca9d416ad4e7a581\"},\n       {\"0x0262a880d213cffa2d105cd31b089c227a5ced69309a8d8c56a7fdd34892fb1e\",\n        \"0x2853361cd32d728b82f8707b858c329db9030303f2f19a96f13f578a11e86288\"}},\n      {{\"0x12d8b4e6805068a011dee541064d796b61c769f2fa2b12799ed6f9bce27daef1\",\n        \"0x235c96b39fd1c16bfee6fb78cd4093038b0c898dadcc5a4acb5282e0d59169c3\"},\n       {\"0x04e92d1da1abe325f5c02a23532c264d0716c1f464fb358311cc9a80292ff15f\",\n        \"0x1413f36166810082edec8b75bb4394035e805bdd7c55081f4daf484176907039\"}}};\n\n  constexpr static Point kLookupSumCommitments[][2] = {\n      {{\"0x2a2710902095e5ab55b44de898c0e6840a0ee81f8d92a6c0dc8ef02a02d21ee1\",\n        \"0x28d923782c3a6500050e9910a043fe81e0002cace28ad924adce719b65973e85\"},\n       {\"0x1fc0212c6a0b39df601ce20a09cba553b4d1e294b60b8ce51d2f10e81b721e5e\",\n        \"0x99ccef31dbf9a42002c79edd8820c267ec5823d59022b8497752364654b6922\"}},\n      {{\"0x0e82ef8c5b78a9c3209e485feae54528b2b6ea32c1978083bf35ea8dd87b5b2d\",\n        \"0x1352018b6ba7cb4a89632ef2933ad7a168591a2d7be4e88e54b66065234b4547\"},\n       {\"0x2e68e0b070914eefd9f566420a1a062c92c3483f0095c6e302b5c7bf1a507963\",\n        \"0xefe26c27f8fa86813c975f73bae86e79d96dacd3e12fa52f99580693b53e553\"}}};\n\n  constexpr static std::string_view kY =\n      \"0xad48364e7911830a76b84e3618ab3124237fb5421c5eef3bdf37eb85e21ca4c\";\n\n  constexpr static Point kVanishingHPolyCommitments[] = {\n      {\"0x2023e83ec43b146ef249b579fa12ba3fd0650675a908214a6e337b0a5fcc86a6\",\n       \"0x17b1a65040f640f97bdf400e0a4203f2a4eef0f58615a3faf09f4c3d6787bd94\"},\n      {\"0x1c567ac4264be3923bc7443b59c0fa6797c2e4f5e262d8f1077611977f074709\",\n       \"0x27f0630e7bcec4ff340589a01b2655144883128d2db280f6fc66c83926657859\"},\n      {\"0x1d3e540463ad34dfdb48b6f17bc3ce983d6365af2b4d8c0e69b53e943e7ecafb\",\n       \"0x29685901b691dd27f675967c00eefa2a1c4a31d36f5cd53f2eec56519d2322ab\"},\n      {\"0x2bf25738808b8bd2097b8e4785248ac8ed87df330b67963d8b3497bdf0dbbbb1\",\n       \"0x07aecc4adeb01a2c58aef020848a8f5f06051b63881bb2ef2d34a2ad0bb84c9e\"},\n      {\"0x2f4b669b0302670f1eceb1399b3c8facc8c174699836bc506a9e396f29a83d5e\",\n       \"0x0674e7ca0781a65f8a37dfa81c1e702614a54e0a27970bfc3156c1c0525ed72a\"},\n      {\"0x133a4f7c9995cac0e2d5a2aa247280a3555a9f800ca6c715f2c3cdb4064f339a\",\n       \"0x1d01b1598fc253963ff5896adabef7531b1171539b85c03936c6fecb7f8dd6d0\"},\n      {\"0x21edad2cc79515b186b057b8dbaec17f9989b48fdfbef0be23b163fff47cf14c\",\n       \"0x0ae4a86594df157cd72c8001793034ff605224415ec8e626cdc90afc520fedf5\"},\n      {\"0x0000000000000000000000000000000000000000000000000000000000000000\",\n       \"0x0000000000000000000000000000000000000000000000000000000000000000\"}};\n\n  constexpr static std::string_view kX =\n      \"0x10853af3af648a168d28a6a3e42bf38ea86124712c9a058d2308884e024f0de\";\n\n  constexpr static std::string_view kAdviceEvals[][7] = {\n      {\"0x0c250e98b43e45a6fd056a5db0fa6f95107a9df0b48382a9d13ae44f11565986\",\n       \"0x05877c08b6d73219daefa776dcdb652531f8b981fe502a6803edf94473207b50\",\n       \"0x1cbc391f35eef93027565c2585cb62994cf451ce3404eecc5bdf29613ff567c2\",\n       \"0x0d9265f812e764eafb44b9948d8395ff52b3e76e3cde88049e68881264f4c2f8\",\n       \"0x065a2516cf6b361e6ced91ca7438284f25821a836fae8c3be86dc5b28f234741\",\n       \"0x2a666a3b5e080ab470d029bce922ac4e9ad26536eada8f862ecceb04f3a49b76\",\n       \"0x15522f38174aeb51e0fd525ad46fdddebd43ed2f56a293a708de26ca5519ef86\"},\n      {\"0x0c250e98b43e45a6fd056a5db0fa6f95107a9df0b48382a9d13ae44f11565986\",\n       \"0x05877c08b6d73219daefa776dcdb652531f8b981fe502a6803edf94473207b50\",\n       \"0x1cbc391f35eef93027565c2585cb62994cf451ce3404eecc5bdf29613ff567c2\",\n       \"0x0d9265f812e764eafb44b9948d8395ff52b3e76e3cde88049e68881264f4c2f8\",\n       \"0x065a2516cf6b361e6ced91ca7438284f25821a836fae8c3be86dc5b28f234741\",\n       \"0x2a666a3b5e080ab470d029bce922ac4e9ad26536eada8f862ecceb04f3a49b76\",\n       \"0x15522f38174aeb51e0fd525ad46fdddebd43ed2f56a293a708de26ca5519ef86\"}};\n\n  constexpr static std::string_view kFixedEvals[] = {\n      \"0x0989716040729bdc5e64b263d20e424d85357aedc5c01cbfffa5ca6f98970352\",\n      \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n      \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n      \"0x07b45d8d9626c8e6a9bffa61933dedab9e6706aaf4db68abf882112ba523803e\",\n      \"0x07b45d8d9626c8e6a9bffa61933dedab9e6706aaf4db68abf882112ba523803e\",\n      \"0x269b86282d4b248bb6e999bd8931218b0f52f42b77bed86cc5c072ebbaf514cb\",\n      \"0x1ee7289a97245ba50d299f5bf5f333df70ebed8082e36fc0cd3e61c015d1948d\",\n      \"0x129355b9c8df016d84f1ba91c796be8aece1b68e6ca848f7d9d55d2a3a769fa8\",\n      \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n      \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n      \"0x0000000000000000000000000000000000000000000000000000000000000000\"};\n\n  constexpr static std::string_view kCommonPermutationEvals[] = {\n      \"0x18e05bf64a9e0c950ae30a1d2ce3d8f5cad1eba743f7f898607bc8b0799e0fd1\",\n      \"0x1c136b2d9c532a7eb85ce68c0258c388527810a5289929692d4203ca3c0ed25d\",\n      \"0x2f559c2846cb3291a627027e1897b06caf794d567291d2852c2493e2fe4db43c\",\n      \"0x130ae4e6572baa37b7f5ee36e9b4c36154833f26e430b1fd64c914f7eea494b1\",\n      \"0x1a15c7e3b27e3c20e8e578e538cf593ea1dbf72e029061d473003f45d8c7da77\",\n      \"0x164de29d2a5cbae12edede764cfece1ca0b0e333d4944739f56a397e9eff04ac\",\n      \"0x2861284a7555de21ea6339638a4f6bfb9b0c70152c39f346f041b3c325bebe0e\",\n      \"0x18bd04d2395aa18749cd5684225f120a7703146649baffda52b5d03730d13942\",\n      \"0x1a3537f1e31384e2343368b1100624ea547ba0496ec49edd0715f19e051559f4\",\n      \"0x2835293bcd14396d3ea4a693eb7bea0bdc8aae5ed418482917262d2c353582c9\",\n      \"0x1815eb49e78681a00d5aefeb6f25ee4a5b585a0fbbcba02873723475e5b20f1b\",\n      \"0x149a0a2f1941a7ee1b1045c13a4df685191084c5ac22ff5baf9f0a6e4e311c51\"};\n\n  constexpr static std::string_view kPermutationProductEvals[][2] = {\n      {\"0x14d487e605c17a910925950d9136944bfdf7a0c3987f720c3a2d4267aaf09538\",\n       \"0x0c4dd7012393b7d01a268cf54bb05dec083768c0fe4bac00ad3b1ccc50191285\"},\n      {\"0x2259bb33ded5f725871b0fdb9e0ff7f55b519714c425771a12223057393bf5c5\",\n       \"0x01efa6be01855502e5d0e35f94dd52605adf490069ec153e9b7c8687d706e153\"}};\n\n  constexpr static std::string_view kLookupSumEvals[][2] = {\n      {\"0x29b8feecea76ee5abf7e5b69de64a125ac3d4f1beb6f7f814c66873aa8891857\",\n       \"0x01f5c39cf5bf3b0bf30146d4945907271a05290c806c837a43eafe88342e27e8\"},\n      {\"0x0f3267122f26cb14d0140feec71c99d9a1afc67cb2b6c1728a71137fa4f79cd0\",\n       \"0x0ad0cd7475c1788b15e13433e85503b7003d1a01d944ecb2e406f803b8b0f8b4\"}};\n\n  constexpr static std::string_view kPermutationProductNextEvals[][2] = {\n      {\"0x279dac86903b497da2378bbfc518d343c2ba81daaf4b883d5919cc3fc63b5456\",\n       \"0x0f6c7ab708e2eaa518bae7102be31e79e1de2f132c9b6dff2deb2818755005d9\"},\n      {\"0x22f7f7ee1ac851635af500723d7b78c9922efc5476245d5dadf1a9126bbd5a51\",\n       \"0x26eb8971c7757eb7f5f4121764f4fdbe2ebc859725ed2001e85221d8beef938c\"}};\n\n  constexpr static std::string_view kLookupSumNextEvals[][2] = {\n      {\"0x26146f17fc3111c4d63298a12cc141bc0d70c2d15a05fe564c8572a61188f41e\",\n       \"0x01f9f1061cb10cb5a9a38ed5de2b169b14e3938ff7407fa56d07810ccddf1085\"},\n      {\"0x1b8ccd026ec071d9ac23d3e3c90d0011d2af59d71a54ce393fbcdab011e2306f\",\n       \"0x1ece93729a97e640aaec14dfc7b0ed51f9b0eab61e4ab9868ff07eec602b91d9\"}};\n\n  constexpr static std::string_view kPermutationProductLastEvals[][2] = {\n      {\"0x0e729dcc11b712c984d66b1ced3cc3b6f5fdf5527ce98cf0f3145f4cb8e842c8\",\n       \"\"},\n      {\"0x2b30b75ed584b8cec2327b4f73c43ccc4d4bc9a8678353338a8ef4d392f2e01f\",\n       \"\"}};\n\n  constexpr static std::string_view kLookupMEvals[][2] = {\n      {\"0x29fc5791f0eb68c1de14d9833997d74735626c846ba927eea5e83836b5ce6311\",\n       \"0x120853d842f0a747a040d6be7d321487c43dc58e28546b5a75ffff6a6f3dbfe7\"},\n      {\"0x29fc5791f0eb68c1de14d9833997d74735626c846ba927eea5e83836b5ce6311\",\n       \"0x120853d842f0a747a040d6be7d321487c43dc58e28546b5a75ffff6a6f3dbfe7\"}};\n\n  constexpr static std::string_view kHEval =\n      \"0x0249a2c9b1d931d975ee328e18957e930215e6830e8aa23bf0c9877d3126512e\";\n\n  static void TestConfig(MultiLookupCircuitConfig<F>& config) {}\n\n  static Circuit GetCircuit() {\n    F a = *F::FromHexString(\n        \"0x76a69c75ed45f60e667fb401dd42f877b565f7818b1d94188fb67249\");\n    F instance = F(2);\n    std::vector<F> lookup_table = {instance, a, a, F::Zero()};\n    return Circuit(std::move(a), std::move(lookup_table));\n  }\n\n  static std::vector<Circuit> Get2Circuits() {\n    Circuit circuit = GetCircuit();\n    return {circuit, std::move(circuit)};\n  }\n\n  static std::vector<Evals> GetInstanceColumns() {\n    F instance = F(2);\n    std::vector<F> instance_column = {std::move(instance)};\n    return {Evals(std::move(instance_column))};\n  }\n};\n\n// PCS = GWC\ntemplate <typename Circuit, typename PS>\nclass MultiLookupTestData<Circuit, PS,\n                          std::enable_if_t<IsGWC<typename PS::PCS>>>\n    : public CircuitTestData<Circuit, PS> {\n public:\n  using PCS = typename PS::PCS;\n  using F = typename PCS::Field;\n  using Evals = typename PCS::Evals;\n\n  // Set flags of values to be used as true\n  constexpr static bool kAdviceCommitmentsFlag = true;\n  constexpr static bool kLookupMPolyCommitmentsFlag = true;\n  constexpr static bool kPermutationProductCommitmentsFlag = true;\n  constexpr static bool kLookupSumCommitmentsFlag = true;\n  constexpr static bool kAdviceEvalsFlag = true;\n  constexpr static bool kFixedEvalsFlag = true;\n  constexpr static bool kCommonPermutationEvalsFlag = true;\n  constexpr static bool kPermutationProductEvalsFlag = true;\n  constexpr static bool kLookupSumEvalsFlag = true;\n  constexpr static bool kPermutationProductNextEvalsFlag = true;\n  constexpr static bool kLookupSumNextEvalsFlag = true;\n  constexpr static bool kPermutationProductLastEvalsFlag = true;\n  constexpr static bool kLookupMEvalsFlag = true;\n\n  constexpr static size_t kN = 32;\n\n  constexpr static std::string_view kPinnedConstraintSystem = \"\";\n\n  constexpr static bool kSelectors[][kN] = {{}};\n\n  constexpr static std::string_view kPinnedVerifyingKey = \"\";\n\n  constexpr static std::string_view kTranscriptRepr = \"\";\n\n  constexpr static uint8_t kProof[] = {\n      176, 39,  119, 154, 232, 230, 108, 49,  145, 63,  70,  98,  95,  39,  79,\n      60,  158, 217, 82,  29,  203, 144, 111, 78,  192, 88,  4,   136, 239, 87,\n      182, 33,  111, 96,  195, 90,  244, 247, 129, 190, 245, 7,   134, 122, 7,\n      38,  111, 134, 98,  219, 109, 209, 22,  189, 233, 94,  254, 37,  168, 159,\n      176, 235, 222, 156, 235, 153, 242, 158, 111, 46,  218, 237, 137, 157, 79,\n      236, 121, 54,  239, 149, 20,  6,   230, 238, 214, 254, 55,  128, 121, 203,\n      148, 43,  205, 237, 34,  148, 104, 119, 235, 159, 27,  152, 52,  194, 129,\n      28,  241, 142, 82,  119, 54,  218, 65,  98,  24,  12,  186, 15,  178, 46,\n      117, 129, 0,   141, 246, 154, 214, 166, 53,  198, 144, 126, 19,  184, 239,\n      70,  130, 205, 109, 242, 43,  222, 115, 252, 25,  135, 221, 140, 138, 27,\n      149, 217, 0,   210, 118, 86,  211, 115, 60,  19,  176, 39,  119, 154, 232,\n      230, 108, 49,  145, 63,  70,  98,  95,  39,  79,  60,  158, 217, 82,  29,\n      203, 144, 111, 78,  192, 88,  4,   136, 239, 87,  182, 33,  111, 96,  195,\n      90,  244, 247, 129, 190, 245, 7,   134, 122, 7,   38,  111, 134, 98,  219,\n      109, 209, 22,  189, 233, 94,  254, 37,  168, 159, 176, 235, 222, 156, 235,\n      153, 242, 158, 111, 46,  218, 237, 137, 157, 79,  236, 121, 54,  239, 149,\n      20,  6,   230, 238, 214, 254, 55,  128, 121, 203, 148, 43,  205, 237, 34,\n      148, 104, 119, 235, 159, 27,  152, 52,  194, 129, 28,  241, 142, 82,  119,\n      54,  218, 65,  98,  24,  12,  186, 15,  178, 46,  117, 129, 0,   141, 246,\n      154, 214, 166, 53,  198, 144, 126, 19,  184, 239, 70,  130, 205, 109, 242,\n      43,  222, 115, 252, 25,  135, 221, 140, 138, 27,  149, 217, 0,   210, 118,\n      86,  211, 115, 60,  19,  10,  99,  22,  17,  150, 115, 127, 174, 68,  191,\n      125, 172, 243, 223, 29,  19,  65,  199, 56,  90,  36,  185, 145, 245, 49,\n      246, 227, 67,  84,  220, 74,  139, 156, 42,  177, 61,  71,  15,  22,  47,\n      239, 184, 119, 62,  93,  114, 196, 215, 241, 206, 252, 57,  228, 10,  218,\n      80,  89,  7,   22,  56,  81,  45,  251, 149, 10,  99,  22,  17,  150, 115,\n      127, 174, 68,  191, 125, 172, 243, 223, 29,  19,  65,  199, 56,  90,  36,\n      185, 145, 245, 49,  246, 227, 67,  84,  220, 74,  139, 156, 42,  177, 61,\n      71,  15,  22,  47,  239, 184, 119, 62,  93,  114, 196, 215, 241, 206, 252,\n      57,  228, 10,  218, 80,  89,  7,   22,  56,  81,  45,  251, 149, 184, 120,\n      70,  37,  33,  16,  249, 36,  249, 144, 37,  211, 202, 126, 251, 106, 190,\n      92,  196, 213, 218, 8,   190, 197, 21,  51,  239, 141, 226, 121, 224, 170,\n      30,  251, 146, 72,  211, 253, 167, 86,  140, 141, 154, 48,  105, 237, 92,\n      122, 34,  156, 8,   27,  211, 92,  16,  45,  250, 207, 19,  210, 128, 168,\n      98,  2,   241, 230, 94,  221, 44,  161, 100, 42,  43,  247, 234, 68,  22,\n      100, 110, 6,   253, 233, 145, 175, 3,   74,  176, 61,  196, 71,  108, 167,\n      25,  32,  151, 40,  95,  241, 47,  41,  128, 154, 204, 17,  131, 53,  251,\n      100, 244, 193, 22,  7,   77,  38,  44,  83,  35,  42,  192, 245, 37,  227,\n      171, 161, 29,  45,  233, 132, 21,  38,  95,  240, 206, 169, 177, 50,  33,\n      237, 163, 229, 123, 22,  86,  76,  132, 242, 81,  69,  139, 253, 70,  196,\n      252, 168, 24,  97,  230, 116, 95,  6,   244, 239, 145, 133, 89,  144, 137,\n      210, 250, 164, 167, 148, 53,  40,  120, 250, 161, 209, 55,  210, 203, 232,\n      54,  202, 179, 95,  235, 70,  181, 108, 62,  46,  124, 202, 207, 98,  189,\n      142, 3,   228, 210, 106, 242, 220, 149, 211, 253, 43,  19,  133, 37,  47,\n      158, 134, 17,  62,  56,  203, 90,  171, 80,  140, 187, 166, 22,  28,  227,\n      82,  47,  13,  72,  242, 163, 196, 51,  86,  155, 36,  161, 70,  23,  21,\n      181, 29,  209, 94,  69,  186, 202, 11,  29,  6,   216, 120, 61,  27,  1,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   192, 118, 210, 6,   138, 166, 54,  220, 36,  59,  50,  198, 93,  61,\n      140, 148, 102, 158, 154, 250, 94,  48,  1,   252, 23,  11,  168, 195, 111,\n      57,  94,  44,  70,  77,  156, 209, 142, 98,  118, 48,  87,  219, 37,  198,\n      112, 211, 53,  129, 72,  196, 39,  84,  181, 227, 208, 139, 237, 236, 173,\n      31,  55,  69,  192, 142, 84,  67,  7,   113, 140, 32,  245, 154, 203, 233,\n      85,  67,  66,  90,  240, 84,  198, 155, 89,  38,  30,  48,  112, 14,  48,\n      244, 107, 35,  57,  34,  132, 131, 170, 78,  227, 23,  85,  155, 181, 254,\n      63,  183, 108, 157, 203, 63,  74,  164, 174, 4,   222, 1,   242, 100, 245,\n      83,  251, 243, 119, 100, 79,  29,  224, 133, 83,  60,  244, 48,  92,  228,\n      187, 248, 101, 96,  59,  51,  155, 218, 217, 216, 56,  224, 109, 101, 73,\n      112, 25,  80,  166, 99,  195, 136, 241, 184, 204, 45,  216, 86,  138, 207,\n      94,  225, 229, 124, 0,   122, 103, 20,  114, 193, 165, 9,   33,  226, 18,\n      245, 67,  90,  82,  66,  184, 20,  172, 19,  1,   211, 138, 32,  212, 169,\n      177, 8,   53,  185, 234, 151, 39,  69,  209, 52,  163, 105, 96,  167, 70,\n      6,   39,  74,  98,  213, 38,  54,  110, 143, 45,  195, 51,  250, 239, 152,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   175, 50,  195, 195, 67,  10,  100, 250, 50,  230, 216, 123, 2,\n      77,  16,  228, 200, 229, 254, 19,  44,  45,  118, 73,  8,   255, 83,  22,\n      165, 129, 177, 29,  175, 50,  195, 195, 67,  10,  100, 250, 50,  230, 216,\n      123, 2,   77,  16,  228, 200, 229, 254, 19,  44,  45,  118, 73,  8,   255,\n      83,  22,  165, 129, 177, 29,  245, 169, 152, 46,  184, 126, 208, 14,  238,\n      23,  255, 19,  234, 15,  115, 61,  243, 49,  222, 86,  129, 179, 22,  4,\n      218, 220, 114, 192, 139, 138, 57,  9,   74,  32,  110, 241, 134, 129, 49,\n      168, 203, 230, 10,  25,  247, 94,  218, 108, 143, 206, 88,  75,  68,  137,\n      138, 152, 145, 167, 213, 97,  6,   141, 236, 32,  181, 79,  110, 54,  152,\n      11,  112, 80,  128, 11,  2,   27,  182, 5,   172, 68,  229, 64,  177, 176,\n      141, 140, 76,  227, 157, 173, 168, 166, 77,  39,  206, 27,  188, 21,  212,\n      18,  107, 28,  149, 234, 109, 173, 198, 13,  1,   221, 105, 33,  117, 62,\n      217, 49,  182, 193, 138, 232, 10,  114, 214, 126, 242, 253, 231, 0,   58,\n      120, 211, 55,  38,  2,   178, 96,  140, 209, 229, 3,   199, 247, 120, 180,\n      55,  216, 211, 173, 2,   223, 238, 59,  75,  135, 101, 166, 18,  4,   1,\n      6,   113, 105, 122, 52,  222, 167, 11,  226, 50,  165, 143, 58,  228, 77,\n      8,   2,   213, 195, 138, 241, 40,  88,  24,  11,  89,  162, 151, 130, 211,\n      54,  35,  1,   118, 66,  51,  101, 249, 17,  237, 241, 239, 51,  105, 72,\n      63,  199, 90,  82,  39,  170, 125, 176, 141, 208, 145, 148, 99,  178, 105,\n      216, 49,  247, 132, 26,  245, 169, 152, 46,  184, 126, 208, 14,  238, 23,\n      255, 19,  234, 15,  115, 61,  243, 49,  222, 86,  129, 179, 22,  4,   218,\n      220, 114, 192, 139, 138, 57,  9,   74,  32,  110, 241, 134, 129, 49,  168,\n      203, 230, 10,  25,  247, 94,  218, 108, 143, 206, 88,  75,  68,  137, 138,\n      152, 145, 167, 213, 97,  6,   141, 236, 32,  181, 79,  110, 54,  152, 11,\n      112, 80,  128, 11,  2,   27,  182, 5,   172, 68,  229, 64,  177, 176, 141,\n      140, 76,  227, 157, 173, 168, 166, 77,  39,  206, 27,  188, 21,  212, 18,\n      107, 28,  149, 234, 109, 173, 198, 13,  1,   221, 105, 33,  117, 62,  217,\n      49,  182, 193, 138, 232, 10,  114, 214, 126, 242, 253, 231, 0,   58,  120,\n      211, 55,  38,  2,   178, 96,  140, 209, 229, 3,   199, 247, 120, 180, 55,\n      216, 211, 173, 2,   223, 238, 59,  75,  135, 101, 166, 18,  4,   1,   6,\n      113, 105, 122, 52,  222, 167, 11,  226, 50,  165, 143, 58,  228, 77,  8,\n      2,   213, 195, 138, 241, 40,  88,  24,  11,  89,  162, 151, 130, 211, 54,\n      35,  1,   118, 66,  51,  101, 249, 17,  237, 241, 239, 51,  105, 72,  63,\n      199, 90,  82,  39,  170, 125, 176, 141, 208, 145, 148, 99,  178, 105, 216,\n      49,  247, 132, 26,  187, 15,  120, 67,  5,   199, 8,   214, 131, 147, 209,\n      70,  126, 189, 75,  96,  230, 57,  141, 9,   82,  167, 130, 241, 105, 204,\n      16,  163, 161, 244, 183, 35,  0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   228, 185, 127, 24,  90,\n      184, 234, 112, 155, 33,  56,  249, 59,  36,  36,  157, 229, 3,   95,  174,\n      103, 197, 72,  190, 127, 44,  55,  51,  102, 221, 110, 43,  228, 185, 127,\n      24,  90,  184, 234, 112, 155, 33,  56,  249, 59,  36,  36,  157, 229, 3,\n      95,  174, 103, 197, 72,  190, 127, 44,  55,  51,  102, 221, 110, 43,  34,\n      22,  170, 41,  101, 67,  191, 164, 57,  158, 199, 55,  198, 69,  150, 79,\n      228, 105, 184, 218, 229, 136, 3,   14,  87,  55,  170, 81,  120, 192, 115,\n      0,   63,  92,  42,  1,   159, 128, 182, 119, 47,  237, 72,  184, 210, 9,\n      166, 218, 91,  190, 218, 173, 52,  9,   11,  8,   1,   171, 164, 255, 132,\n      49,  105, 5,   88,  153, 225, 217, 235, 255, 34,  31,  98,  43,  201, 122,\n      165, 26,  34,  6,   19,  31,  192, 74,  113, 57,  227, 0,   153, 207, 194,\n      251, 11,  232, 10,  39,  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   1,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   253, 114,\n      185, 17,  1,   186, 43,  194, 149, 17,  165, 34,  100, 249, 72,  50,  176,\n      1,   93,  17,  150, 66,  154, 238, 36,  177, 211, 182, 195, 29,  17,  19,\n      105, 28,  150, 134, 33,  155, 79,  172, 101, 189, 105, 123, 96,  248, 34,\n      47,  87,  254, 128, 18,  235, 16,  33,  139, 32,  173, 188, 197, 80,  45,\n      78,  2,   16,  213, 245, 195, 155, 235, 67,  34,  126, 105, 70,  196, 5,\n      37,  131, 192, 203, 79,  114, 200, 155, 218, 143, 207, 90,  18,  230, 151,\n      237, 242, 201, 16,  10,  61,  3,   75,  231, 162, 169, 199, 23,  8,   56,\n      220, 144, 183, 65,  178, 195, 91,  139, 49,  245, 127, 2,   251, 13,  134,\n      134, 82,  100, 44,  211, 30,  110, 235, 155, 155, 141, 223, 231, 81,  128,\n      135, 61,  134, 40,  35,  90,  98,  227, 19,  62,  239, 197, 181, 173, 0,\n      41,  233, 72,  159, 156, 57,  208, 27,  69,  221, 136, 194, 170, 118, 111,\n      84,  252, 245, 18,  65,  249, 230, 35,  184, 79,  118, 17,  172, 52,  208,\n      171, 21,  253, 226, 53,  140, 182, 225, 43,  6,   95,  202, 190, 80,  46,\n      183, 164, 12,  0,   190, 36,  243, 53,  209, 174, 214, 241, 22,  228, 93,\n      26,  35,  72,  228, 164, 165, 73,  157, 41,  194, 65,  12,  84,  219, 20,\n      211, 131, 247, 94,  191, 72,  45,  21,  165, 238, 201, 14,  122, 93,  188,\n      151, 115, 95,  189, 208, 117, 176, 227, 37,  116, 181, 67,  108, 35,  217,\n      220, 180, 152, 50,  172, 40,  24,  78,  165, 7,   127, 177, 2,   73,  174,\n      116, 28,  212, 132, 154, 73,  84,  122, 242, 82,  89,  62,  238, 77,  163,\n      40,  7,   73,  225, 227, 181, 80,  19,  1,   157, 134, 220, 181, 54,  117,\n      235, 91,  195, 176, 18,  223, 118, 76,  224, 33,  79,  231, 185, 8,   239,\n      212, 70,  2,   249, 214, 172, 121, 28,  88,  98,  182, 226, 29,  128, 142,\n      69,  135, 199, 137, 68,  148, 236, 23,  50,  217, 101, 82,  150, 181, 93,\n      245, 172, 194, 78,  7,   34,  20,  62,  118, 228, 23,  160, 250, 217, 97,\n      17,  70,  216, 113, 133, 151, 130, 64,  88,  189, 32,  113, 237, 103, 9,\n      62,  30,  166, 58,  116, 193, 16,  9,   103, 230, 37,  229, 68,  87,  237,\n      167, 146, 173, 245, 217, 20,  229, 170, 147, 105, 27,  255, 154, 83,  169,\n      163, 124, 119, 23,  52,  220, 49,  117, 32,  235, 247, 82,  159, 48,  45,\n      18,  10,  249, 186, 135, 155, 127, 69,  196, 161, 0,   161, 77,  21,  225,\n      35,  170, 252, 196, 98,  157, 85,  116, 164, 234, 6,   229, 236, 183, 64,\n      238, 57,  14,  189, 166, 81,  230, 31,  104, 242, 32,  21,  120, 209, 180,\n      1,   53,  253, 151, 28,  13,  204, 242, 202, 129, 9,   150, 17,  175, 202,\n      20,  25,  140, 120, 240, 53,  213, 112, 156, 145, 169, 210, 59,  132, 192,\n      59,  13,  176, 97,  181, 112, 165, 180, 171, 234, 213, 69,  13,  44,  23,\n      149, 201, 100, 78,  155, 9,   49,  87,  199, 118, 125, 247, 61,  52,  11,\n      78,  94,  28,  74,  26,  19,  221, 45,  186, 184, 243, 238, 12,  149, 107,\n      225, 8,   194, 61,  99,  149, 43,  49,  141, 138, 224, 86,  8,   249, 205,\n      27,  68,  152, 75,  146, 90,  67,  115, 188, 223, 198, 217, 154, 192, 127,\n      98,  44,  206, 27,  66,  24,  235, 29,  131, 222, 189, 167, 63,  217, 238,\n      134, 148, 254, 211, 169, 212, 103, 205, 110, 133, 200, 203, 175, 37,  27,\n      246, 126, 41,  62,  221, 40,  69,  162, 240, 48,  243, 251, 188, 37,  32,\n      34,  255, 125, 161, 35,  105, 110, 16,  137, 3,   85,  173, 191, 0,   180,\n      123, 186, 9,   21,  181, 238, 50,  26,  99,  82,  27,  173, 200, 1,   43,\n      86,  246, 228, 34,  186, 206, 184, 0,   219, 188, 113, 169, 228, 33,  45,\n      62,  189, 80,  209, 185, 15,  29,  252, 202, 2,   117, 163, 191, 61,  225,\n      131, 216, 156, 22,  128, 123, 119, 252, 92,  238, 153, 38,  147, 110, 207,\n      113, 75,  89,  63,  213, 99,  143, 134, 228, 143, 245, 2,   169, 85,  73,\n      83,  243, 73,  151, 255, 113, 0,   218, 193, 242, 177, 37,  253, 124, 35,\n      137, 157, 116, 24,  159, 53,  225, 243, 226, 124, 163, 102, 96,  45,  125,\n      154, 145, 4,   99,  98,  173, 90,  3,   241, 138, 210, 14,  146, 211, 5,\n      223, 235, 131, 43,  176, 193, 166, 0,   67,  154, 35,  54,  218, 53,  229,\n      34,  114, 228, 72,  159, 44,  239, 8,   41,  171, 137, 192, 189, 231, 79,\n      68,  61,  76,  63,  150, 209, 174, 153, 113, 180, 188, 243, 231, 161, 129,\n      147, 114, 43,  181, 94,  46,  116, 57,  235, 149, 130, 7,   29,  17,  112,\n      211, 52,  251, 176, 7,   36,  104, 251, 56,  156, 255, 252, 86,  112, 13,\n      81,  45,  102, 199, 14,  33,  249, 99,  176, 223, 251, 99,  125, 64,  153,\n      159, 125, 4,   199, 172, 104, 111, 189, 100, 210, 169, 143, 185, 158, 208,\n      24,  36,  49,  56,  11,  186, 45,  125, 195, 51,  168, 74,  186, 91,  170,\n      84,  131, 177, 48,  11,  2,   250, 167, 64,  70,  58,  224, 146, 77,  192,\n      194, 115, 131, 144, 105, 212, 67,  48,  21,  62,  134, 96,  64,  44,  163,\n      3,   208, 100, 111, 160, 79,  8,   228, 244, 47,  64,  97,  153, 142, 135,\n      169, 22,  243, 233, 170, 76,  138, 54,  242, 142, 24,  75,  213, 2,   153,\n      77,  149, 138, 49,  231, 199, 240, 39,  32,  131, 153, 207, 220, 13,  195,\n      156, 31,  219, 178, 47,  154, 189, 224, 134, 200, 147, 229, 20,  114, 228,\n      72,  159, 44,  239, 8,   41,  171, 137, 192, 189, 231, 79,  68,  61,  76,\n      63,  150, 209, 174, 153, 113, 180, 188, 243, 231, 161, 129, 147, 114, 43,\n      163, 224, 70,  84,  60,  247, 162, 1,   248, 239, 162, 171, 86,  112, 196,\n      126, 150, 85,  166, 97,  42,  11,  171, 245, 96,  227, 40,  180, 131, 132,\n      5,   25,  215, 226, 58,  9,   248, 3,   126, 54,  22,  219, 34,  164, 182,\n      240, 98,  137, 45,  43,  82,  226, 145, 117, 35,  40,  15,  213, 168, 246,\n      110, 62,  166, 40,  125, 195, 51,  168, 74,  186, 91,  170, 84,  131, 177,\n      48,  11,  2,   250, 167, 64,  70,  58,  224, 146, 77,  192, 194, 115, 131,\n      144, 105, 212, 67,  48,  21,  120, 17,  76,  59,  9,   137, 247, 6,   64,\n      156, 92,  198, 236, 26,  25,  202, 236, 232, 72,  224, 137, 241, 23,  82,\n      7,   42,  53,  69,  17,  217, 244, 169, 243, 115, 240, 195, 43,  238, 134,\n      162, 210, 56,  234, 225, 152, 240, 239, 242, 5,   194, 206, 74,  88,  61,\n      226, 168, 156, 126, 183, 13,  184, 95,  133, 46,  168, 138, 238, 78,  82,\n      206, 194, 186, 39,  56,  98,  139, 30,  48,  78,  216, 16,  57,  40,  106,\n      22,  54,  209, 199, 14,  188, 167, 197, 190, 91,  191, 47,  247, 39,  159,\n      100, 129, 82,  252, 101, 9,   139, 86,  57,  127, 35,  66,  61,  174, 154,\n      74,  171, 68,  158, 100, 192, 188, 72,  134, 191, 93,  118, 164, 21};\n\n  constexpr static Point kAdviceCommitments[][5] = {\n      {{\"0x21b657ef880458c04e6f90cb1d52d99e3c4f275f62463f91316ce6e89a7727b0\",\n        \"0x018e7e556102c958d925644e9fdec38e1b6c88572a98ce9871485937ff5c9aa4\"},\n       {\"0x1cdeebb09fa825fe5ee9bd16d16ddb62866f26077a8607f5be81f7f45ac3606f\",\n        \"0x0dd225bfadd39c83ca40e0a578ae098e3dce0b62b8bbf3445e63fe035c71ce09\"},\n       {\"0x1422edcd2b94cb798037fed6eee6061495ef3679ec4f9d89edda2e6f9ef299eb\",\n        \"0x07aff8c1c8c754b022575c2e7e2f98483f08111f3dc0a9d7e2b119c5063c97b9\"},\n       {\"0x26d69af68d0081752eb20fba0c186241da3677528ef11c81c234981b9feb7768\",\n        \"0x0cb675234a966e9b7fe6f87c6da86e5b3d534330dd6ebc08302da54ccee2352f\"},\n       {\"0x133c73d35676d200d9951b8a8cdd8719fc73de2bf26dcd8246efb8137e90c635\",\n        \"0x1eb2849fbface2516b2dfa0d9ed2ec2b397a04ae228a2541d32bc83bb429de30\"}},\n      {{\"0x21b657ef880458c04e6f90cb1d52d99e3c4f275f62463f91316ce6e89a7727b0\",\n        \"0x018e7e556102c958d925644e9fdec38e1b6c88572a98ce9871485937ff5c9aa4\"},\n       {\"0x1cdeebb09fa825fe5ee9bd16d16ddb62866f26077a8607f5be81f7f45ac3606f\",\n        \"0x0dd225bfadd39c83ca40e0a578ae098e3dce0b62b8bbf3445e63fe035c71ce09\"},\n       {\"0x1422edcd2b94cb798037fed6eee6061495ef3679ec4f9d89edda2e6f9ef299eb\",\n        \"0x07aff8c1c8c754b022575c2e7e2f98483f08111f3dc0a9d7e2b119c5063c97b9\"},\n       {\"0x26d69af68d0081752eb20fba0c186241da3677528ef11c81c234981b9feb7768\",\n        \"0x0cb675234a966e9b7fe6f87c6da86e5b3d534330dd6ebc08302da54ccee2352f\"},\n       {\"0x133c73d35676d200d9951b8a8cdd8719fc73de2bf26dcd8246efb8137e90c635\",\n        \"0x1eb2849fbface2516b2dfa0d9ed2ec2b397a04ae228a2541d32bc83bb429de30\"}}};\n\n  constexpr static std::string_view kTheta =\n      \"0x1950defb9d8f3478e0462ac34605c6e7e42bd2b2b7b3fba3f26ccf52d8cdd3d9\";\n\n  constexpr static Point kLookupMPolyCommitments[][2] = {\n      {{\"0x0b4adc5443e3f631f591b9245a38c741131ddff3ac7dbf44ae7f73961116630a\",\n        \"0x2a5f18e67444124e520e079b7ff831b8bfee476778b59ff3a7950d6210f02a29\"},\n       {\"0x15fb2d513816075950da0ae439fccef1d7c4725d3e77b8ef2f160f473db12a9c\",\n        \"0x13a5bf0b4bde809ec00d335afbfb4ee7d1a01b17d5f6ba51b353be98ac7034cb\"}},\n      {{\"0x0b4adc5443e3f631f591b9245a38c741131ddff3ac7dbf44ae7f73961116630a\",\n        \"0x2a5f18e67444124e520e079b7ff831b8bfee476778b59ff3a7950d6210f02a29\"},\n       {\"0x15fb2d513816075950da0ae439fccef1d7c4725d3e77b8ef2f160f473db12a9c\",\n        \"0x13a5bf0b4bde809ec00d335afbfb4ee7d1a01b17d5f6ba51b353be98ac7034cb\"}}};\n\n  constexpr static std::string_view kBeta =\n      \"0x0fe1e90dfdcdb9031329e9a5a41b4a60b6568d69e3c807e395708a8611b3edac\";\n\n  constexpr static std::string_view kGamma =\n      \"0x2bc7cf63734167b150d2e178590c6d5b649959123a3c05e15ca70ac5a7aee16c\";\n\n  constexpr static Point kPermutationProductCommitments[][2] = {\n      {{\"0x2ae079e28def3315c5be08dad5c45cbe6afb7ecad32590f924f91021254678b8\",\n        \"0x1ad3953f92d5995642d47c91490453571bfd063923a31a60f00f05d9ec69cdeb\"},\n       {\"0x0262a880d213cffa2d105cd31b089c227a5ced69309a8d8c56a7fdd34892fb1e\",\n        \"0x2853361cd32d728b82f8707b858c329db9030303f2f19a96f13f578a11e86288\"}},\n      {{\"0x28972019a76c47c43db04a03af91e9fd066e641644eaf72b2a64a12cdd5ee6f1\",\n        \"0x0b9c648fac323f459831a93dd17af8d714b5edb313909e64102d66a215eb14da\"},\n       {\"0x04e92d1da1abe325f5c02a23532c264d0716c1f464fb358311cc9a80292ff15f\",\n        \"0x1413f36166810082edec8b75bb4394035e805bdd7c55081f4daf484176907039\"}}};\n\n  constexpr static Point kLookupSumCommitments[][2] = {\n      {{\"0x065f74e66118a8fcc446fd8b4551f2844c56167be5a3ed2132b1a9cef05f2615\",\n        \"0x0c4e3cb2cb2e5d5a2ee6506e28e79cb93efdc032c4d4c87dc1467fe1fd57f8c0\"},\n       {\"0x2e3e6cb546eb5fb3ca36e8cbd237d1a1fa78283594a7a4fad28990598591eff4\",\n        \"0x127264f950c70df575562c8a6361f47d09d10809a60e6beea76d81ae2ecce654\"}},\n      {{\"0x26bb8c50ab5acb383e11869e2f2585132bfdd395dcf26ad2e4038ebd62cfca7c\",\n        \"0x2c24b2cf59365ad68d491891cd714b94e41953dda33ea6bbcddc78ba354636e3\"},\n       {\"0x1b3d78d8061d0bcaba455ed11db5151746a1249b5633c4a3f2480d2f52e31c16\",\n        \"0x2a372ee0aee1a85e2b3bd158d08d9c8108030b9a70c4e2a80cc7886e5af0c61a\"}}};\n\n  constexpr static std::string_view kY =\n      \"0x225b7b6db5f6eabe5bee80a329ac8cdf4f16446dce073a26540d99f46fdc772d\";\n\n  constexpr static Point kVanishingHPolyCommitments[] = {\n      {\"0x2c5e396fc3a80b17fc01305efa9a9e66948c3d5dc6323b24dc36a68a06d276c0\",\n       \"0x21d86696cd63c9ce4583007d3d099ad4f8c76e9b016e4a1c8f4dcbd81a4b9820\"},\n      {\"0x0ec045371fadeced8bd0e3b55427c4488135d370c625db573076628ed19c4d46\",\n       \"0x22f3ed9908e0f46219180a49eb3ad385ded3ae26d1f517437830f20749f2448b\"},\n      {\"0x03842239236bf4300e70301e26599bc654f05a424355e9cb9af5208c71074354\",\n       \"0x053354e22714f3ac058e47a185ce917ba20e6f2077e9dfc513e0411d2250e9f3\"},\n      {\"0x05e01d4f6477f3fb53f564f201de04aea44a3fcb9d6cb73ffeb59b5517e34eaa\",\n       \"0x03a794602546ec5b1654404a245da5cb2a0776ebe7d34fc5dfa320919f4de2b9\"},\n      {\"0x2dccb8f188c363a650197049656de038d8d9da9b333b6065f8bbe45c30f43c53\",\n       \"0x2549fa80612c8af3876edf8bbb06d2a22f2285c5b532b471b03e466b423e5bde\"},\n      {\"0x208ad30113ac14b842525a43f512e22109a5c17214677a007ce5e15ecf8a56d8\",\n       \"0x0c21b0a05ca8dde9cc4ad91d28c9e9b36b0e2161cea0bbcc3a78c892c1baecbe\"},\n      {\"0x18effa33c32d8f6e3626d5624a270646a76069a334d1452797eab93508b1a9d4\",\n       \"0x101d11a503164346f6b1726a1cf38ca76d98ce40eaaac1aaaf5de2c4b133e8d3\"},\n      {\"0x0000000000000000000000000000000000000000000000000000000000000000\",\n       \"0x0000000000000000000000000000000000000000000000000000000000000000\"}};\n\n  constexpr static std::string_view kX =\n      \"0x2d67391a5688800ccce662394b35c9dfcb61fc24cc696042861d9aec64693e02\";\n\n  constexpr static std::string_view kAdviceEvals[][7] = {\n      {\"0x09398a8bc072dcda0416b38156de31f33d730fea13ff17ee0ed07eb82e98a9f5\",\n       \"0x20ec8d0661d5a791988a89444b58ce8f6cda5ef7190ae6cba8318186f16e204a\",\n       \"0x1bce274da6a8ad9de34c8c8db0b140e544ac05b61b020b8050700b98366e4fb5\",\n       \"0x00e7fdf27ed6720ae88ac1b631d93e752169dd010dc6ad6dea951c6b12d415bc\",\n       \"0x06010412a665874b3beedf02add3d837b478f7c703e5d18c60b2022637d3783a\",\n       \"0x012336d38297a2590b185828f18ac3d502084de43a8fa532e20ba7de347a6971\",\n       \"0x1a84f731d869b2639491d08db07daa27525ac73f486933eff1ed11f965334276\"},\n      {\"0x09398a8bc072dcda0416b38156de31f33d730fea13ff17ee0ed07eb82e98a9f5\",\n       \"0x20ec8d0661d5a791988a89444b58ce8f6cda5ef7190ae6cba8318186f16e204a\",\n       \"0x1bce274da6a8ad9de34c8c8db0b140e544ac05b61b020b8050700b98366e4fb5\",\n       \"0x00e7fdf27ed6720ae88ac1b631d93e752169dd010dc6ad6dea951c6b12d415bc\",\n       \"0x06010412a665874b3beedf02add3d837b478f7c703e5d18c60b2022637d3783a\",\n       \"0x012336d38297a2590b185828f18ac3d502084de43a8fa532e20ba7de347a6971\",\n       \"0x1a84f731d869b2639491d08db07daa27525ac73f486933eff1ed11f965334276\"}};\n\n  constexpr static std::string_view kFixedEvals[] = {\n      \"0x23b7f4a1a310cc69f182a752098d39e6604bbd7e46d19383d608c70543780fbb\",\n      \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n      \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n      \"0x2b6edd6633372c7fbe48c567ae5f03e59d24243bf938219b70eab85a187fb9e4\",\n      \"0x2b6edd6633372c7fbe48c567ae5f03e59d24243bf938219b70eab85a187fb9e4\",\n      \"0x0073c07851aa37570e0388e5dab869e44f9645c637c79e39a4bf436529aa1622\",\n      \"0x05693184ffa4ab01080b0934addabe5bdaa609d2b848ed2f77b6809f012a5c3f\",\n      \"0x270ae80bfbc2cf9900e339714ac01f1306221aa57ac92b621f22ffebd9e19958\",\n      \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n      \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n      \"0x0000000000000000000000000000000000000000000000000000000000000000\"};\n\n  constexpr static std::string_view kCommonPermutationEvals[] = {\n      \"0x13111dc3b6d3b124ee9a4296115d01b03248f96422a51195c22bba0111b972fd\",\n      \"0x024e2d50c5bcad208b2110eb1280fe572f22f8607b69bd65ac4f9b2186961c69\",\n      \"0x10c9f2ed97e6125acf8fda9bc8724fcbc0832505c446697e2243eb9bc3f5d510\",\n      \"0x1ed32c645286860dfb027ff5318b5bc3b241b790dc380817c7a9a2e74b033d0a\",\n      \"0x1bd0399c9f48e92900adb5c5ef3e13e3625a2328863d878051e7df8d9b9beb6e\",\n      \"0x062be1b68c35e2fd15abd034ac11764fb823e6f94112f5fc546f76aac288dd45\",\n      \"0x0c41c2299d49a5a4e448231a5de416f1d6aed135f324be000ca4b72e50beca5f\",\n      \"0x236c43b57425e3b075d0bd5f7397bc5d7a0ec9eea5152d48bf5ef783d314db54\",\n      \"0x28a34dee3e5952f27a54499a84d41c74ae4902b17f07a54e1828ac3298b4dcd9\",\n      \"0x0246d4ef08b9e74f21e04c76df12b0c35beb7536b5dc869d011350b5e3e14907\",\n      \"0x074ec2acf55db5965265d93217ec944489c787458e801de2b662581c79acd6f9\",\n      \"0x10c1743aa61e3e0967ed7120bd584082978571d8461161d9faa017e4763e1422\"};\n\n  constexpr static std::string_view kPermutationProductEvals[][2] = {\n      {\"0x207531dc3417777ca3a9539aff1b6993aae514d9f5ad92a7ed5744e525e66709\",\n       \"0x172c0d45d5eaabb4a570b561b00d3bc0843bd2a9919c70d535f0788c1914caaf\"},\n      {\"0x1bce2c627fc09ad9c6dfbc73435a924b98441bcdf90856e08a8d312b95633dc2\",\n       \"0x02cafc1d0fb9d150bd3e2d21e4a971bcdb00b8ceba22e4f6562b01c8ad1b5263\"}};\n\n  constexpr static std::string_view kLookupSumEvals[][2] = {\n      {\"0x2d6066a37ce2f3e1359f18749d89237cfd25b1f2c1da0071ff9749f3534955a9\",\n       \"0x0ec7662d510d7056fcff9c38fb682407b0fb34d370111d078295eb39742e5eb5\"},\n      {\"0x188ef2368a4caae9f316a9878e9961402ff4e4084fa06f64d003a32c4060863e\",\n       \"0x19058483b428e360f5ab0b2a61a655967ec47056aba2eff801a2f73c5446e0a3\"}};\n\n  constexpr static std::string_view kPermutationProductNextEvals[][2] = {\n      {\"0x06eaa474559d62c4fcaa23e1154da100a1c4457f9b87baf90a122d309f52f7eb\",\n       \"0x08e16b950ceef3b8ba2ddd131a4a1c5e4e0b343df77d76c75731099b4e64c995\"},\n      {\"0x28dd3e297ef61b25afcbc8856ecd67d4a9d3fe9486eed93fa7bdde831deb1842\",\n       \"0x02f58fe4868f63d53f594b71cf6e932699ee5cfc777b80169cd883e13dbfa375\"}};\n\n  constexpr static std::string_view kLookupSumNextEvals[][2] = {\n      {\"0x22e535da36239a4300a6c1b02b83ebdf05d3920ed28af1035aad626304919a7d\",\n       \"0x2dba0b38312418d09eb98fa9d264bd6f68acc7047d9f99407d63fbdfb063f921\"},\n      {\"0x14e593c886e0bd9a2fb2db1f9cc30ddccf99832027f0c7e7318a954d9902d54b\",\n       \"0x28a63e6ef6a8d50f28237591e2522b2d8962f0b6a422db16367e03f8093ae2d7\"}};\n\n  constexpr static std::string_view kPermutationProductLastEvals[][2] = {\n      {\"0x11960981caf2cc0d1c97fd3501b4d1781520f2681fe651a6bd0e39ee40b7ece5\",\n       \"\"},\n      {\"0x1a32eeb51509ba7bb400bfad550389106e6923a17dff222025bcfbf330f0a245\",\n       \"\"}};\n\n  constexpr static std::string_view kLookupMEvals[][2] = {\n      {\"0x2b729381a1e7f3bcb47199aed1963f4c3d444fe7bdc089ab2908ef2c9f48e472\",\n       \"0x153043d469908373c2c04d92e03a4640a7fa020b30b18354aa5bba4aa833c37d\"},\n      {\"0x2b729381a1e7f3bcb47199aed1963f4c3d444fe7bdc089ab2908ef2c9f48e472\",\n       \"0x153043d469908373c2c04d92e03a4640a7fa020b30b18354aa5bba4aa833c37d\"}};\n\n  constexpr static std::string_view kHEval =\n      \"0x15a876a131dd34deb9bfc5ef39cff3910a13a510aa465e447a865154406f2796\";\n\n  static void TestConfig(MultiLookupCircuitConfig<F>& config) {}\n\n  static Circuit GetCircuit() {\n    F a = *F::FromHexString(\n        \"0x76a69c75ed45f60e667fb401dd42f877b565f7818b1d94188fb67249\");\n    F instance = F(2);\n    std::vector<F> lookup_table = {instance, a, a, F::Zero()};\n    return Circuit(std::move(a), std::move(lookup_table));\n  }\n\n  static std::vector<Circuit> Get2Circuits() {\n    Circuit circuit = GetCircuit();\n    return {circuit, std::move(circuit)};\n  }\n\n  static std::vector<Evals> GetInstanceColumns() {\n    F instance = F(2);\n    std::vector<F> instance_column = {std::move(instance)};\n    return {Evals(std::move(instance_column))};\n  }\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_EXAMPLES_MULTI_LOOKUP_CIRCUIT_TEST_DATA_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/examples/point.h",
    "content": "#ifndef TACHYON_ZK_PLONK_EXAMPLES_POINT_H_\n#define TACHYON_ZK_PLONK_EXAMPLES_POINT_H_\n\n#include <string_view>\n\nnamespace tachyon::zk::plonk {\n\nstruct Point {\n  std::string_view x;\n  std::string_view y;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_EXAMPLES_POINT_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/examples/shuffle_api_circuit.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n// This is taken and modified from\n// https://github.com/privacy-scaling-explorations/halo2/blob/bc857a7/halo2_proofs/tests/shuffle_api.rs.\n\n#ifndef TACHYON_ZK_PLONK_EXAMPLES_SHUFFLE_API_CIRCUIT_H_\n#define TACHYON_ZK_PLONK_EXAMPLES_SHUFFLE_API_CIRCUIT_H_\n\n#include <memory>\n#include <numeric>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/zk/plonk/constraint_system/circuit.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F>\nclass ShuffleAPIConfig {\n public:\n  using Field = F;\n\n  ShuffleAPIConfig() = default;\n  ShuffleAPIConfig(const AdviceColumnKey& input_0,\n                   const FixedColumnKey& input_1,\n                   const AdviceColumnKey& shuffle_0,\n                   const AdviceColumnKey& shuffle_1, Selector s_input,\n                   Selector s_shuffle)\n      : input_0_(input_0),\n        input_1_(input_1),\n        shuffle_0_(shuffle_0),\n        shuffle_1_(shuffle_1),\n        s_input_(s_input),\n        s_shuffle_(s_shuffle) {}\n\n  ShuffleAPIConfig Clone() const {\n    return {input_0_, input_1_, shuffle_0_, shuffle_1_, s_input_, s_shuffle_};\n  }\n\n  const AdviceColumnKey& input_0() const { return input_0_; }\n  const FixedColumnKey& input_1() const { return input_1_; }\n  const AdviceColumnKey& shuffle_0() const { return shuffle_0_; }\n  const AdviceColumnKey& shuffle_1() const { return shuffle_1_; }\n  Selector s_input() const { return s_input_; }\n  Selector s_shuffle() const { return s_shuffle_; }\n\n  static ShuffleAPIConfig Configure(ConstraintSystem<F>& meta,\n                                    const AdviceColumnKey& input_0,\n                                    const FixedColumnKey& input_1,\n                                    const AdviceColumnKey& shuffle_0,\n                                    const AdviceColumnKey& shuffle_1) {\n    Selector s_shuffle = meta.CreateComplexSelector();\n    Selector s_input = meta.CreateComplexSelector();\n\n    meta.Shuffle(\"shuffle\", [s_shuffle, s_input, &input_0, &input_1, &shuffle_0,\n                             &shuffle_1](VirtualCells<F>& meta) {\n      std::unique_ptr<Expression<F>> s_input_expr = meta.QuerySelector(s_input);\n      std::unique_ptr<Expression<F>> s_shuffle_expr =\n          meta.QuerySelector(s_shuffle);\n      std::unique_ptr<Expression<F>> input_0_expr =\n          meta.QueryAdvice(input_0, Rotation::Cur());\n      std::unique_ptr<Expression<F>> input_1_expr =\n          meta.QueryFixed(input_1, Rotation::Cur());\n      std::unique_ptr<Expression<F>> shuffle_0_expr =\n          meta.QueryAdvice(shuffle_0, Rotation::Cur());\n      std::unique_ptr<Expression<F>> shuffle_1_expr =\n          meta.QueryAdvice(shuffle_1, Rotation::Cur());\n\n      shuffle::Pairs<std::unique_ptr<Expression<F>>> shuffle_pairs;\n      shuffle_pairs.emplace_back(\n          s_input_expr->Clone() * std::move(input_0_expr),\n          s_shuffle_expr->Clone() * std::move(shuffle_0_expr));\n      shuffle_pairs.emplace_back(\n          std::move(s_input_expr) * std::move(input_1_expr),\n          std::move(s_shuffle_expr) * std::move(shuffle_1_expr));\n      return shuffle_pairs;\n    });\n\n    return {input_0, input_1, shuffle_0, shuffle_1, s_input, s_shuffle};\n  }\n\n private:\n  AdviceColumnKey input_0_;\n  FixedColumnKey input_1_;\n  AdviceColumnKey shuffle_0_;\n  AdviceColumnKey shuffle_1_;\n  Selector s_input_;\n  Selector s_shuffle_;\n};\n\ntemplate <typename F, template <typename> class _FloorPlanner>\nclass ShuffleAPICircuit : public Circuit<ShuffleAPIConfig<F>> {\n public:\n  using FloorPlanner = _FloorPlanner<ShuffleAPICircuit<F, _FloorPlanner>>;\n\n  ShuffleAPICircuit() = default;\n  ShuffleAPICircuit(std::vector<Value<F>>&& input_0, std::vector<F>&& input_1,\n                    std::vector<Value<F>>&& shuffle_0,\n                    std::vector<Value<F>>&& shuffle_1)\n      : input_0_(std::move(input_0)),\n        input_1_(std::move(input_1)),\n        shuffle_0_(std::move(shuffle_0)),\n        shuffle_1_(std::move(shuffle_1)) {\n    CHECK_EQ(input_0.size(), input_1.size());\n  }\n\n  std::unique_ptr<Circuit<ShuffleAPIConfig<F>>> WithoutWitness()\n      const override {\n    return std::make_unique<ShuffleAPICircuit>();\n  }\n\n  static ShuffleAPIConfig<F> Configure(ConstraintSystem<F>& meta) {\n    AdviceColumnKey input_0 = meta.CreateAdviceColumn();\n    FixedColumnKey input_1 = meta.CreateFixedColumn();\n    AdviceColumnKey shuffle_0 = meta.CreateAdviceColumn();\n    AdviceColumnKey shuffle_1 = meta.CreateAdviceColumn();\n    return ShuffleAPIConfig<F>::Configure(meta, input_0, input_1, shuffle_0,\n                                          shuffle_1);\n  }\n\n  void Synthesize(ShuffleAPIConfig<F>&& config,\n                  Layouter<F>* layouter) const override {\n    layouter->AssignRegion(\"load inputs\", [this, &config](Region<F>& region) {\n      for (size_t i = 0; i < input_0_.size(); ++i) {\n        region.AssignAdvice(\"input_0\", config.input_0(), i,\n                            [this, i]() { return input_0_[i]; });\n        region.AssignFixed(\"input_1\", config.input_1(), i, [this, i]() {\n          return Value<F>::Known(input_1_[i]);\n        });\n        config.s_input().Enable(region, i);\n      }\n    });\n\n    layouter->AssignRegion(\"load shuffles\", [this, &config](Region<F>& region) {\n      for (size_t i = 0; i < shuffle_0_.size(); ++i) {\n        region.AssignAdvice(\"shuffle_0\", config.shuffle_0(), i,\n                            [this, i]() { return shuffle_0_[i]; });\n        region.AssignAdvice(\"shuffle_1\", config.shuffle_1(), i,\n                            [this, i]() { return shuffle_1_[i]; });\n        config.s_shuffle().Enable(region, i);\n      }\n    });\n  }\n\n private:\n  std::vector<Value<F>> input_0_;\n  std::vector<F> input_1_;\n  std::vector<Value<F>> shuffle_0_;\n  std::vector<Value<F>> shuffle_1_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_EXAMPLES_SHUFFLE_API_CIRCUIT_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/examples/shuffle_api_circuit_test.cc",
    "content": "#include \"tachyon/zk/plonk/examples/shuffle_api_circuit.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/zk/plonk/examples/circuit_test.h\"\n#include \"tachyon/zk/plonk/examples/shuffle_api_circuit_test_data.h\"\n#include \"tachyon/zk/plonk/layout/floor_planner/simple_floor_planner.h\"\n#include \"tachyon/zk/plonk/layout/floor_planner/v1/v1_floor_planner.h\"\n\nnamespace tachyon::zk::plonk {\n\nnamespace {\n\ntemplate <typename TestArguments>\nclass ShuffleAPICircuitTest\n    : public CircuitTest<TestArguments, ShuffleAPICircuitTestData<\n                                            typename TestArguments::Circuit,\n                                            typename TestArguments::PS>> {\n public:\n  using Circuit = typename TestArguments::Circuit;\n  using PS = typename TestArguments::PS;\n  using PCS = typename PS::PCS;\n  using Commitment = typename PCS::Commitment;\n\n  static void SetUpTestSuite() {\n    CircuitTest<TestArguments,\n                ShuffleAPICircuitTestData<Circuit, PS>>::SetUpTestSuite();\n    halo2::ProofSerializer<Commitment>::s_use_legacy_serialization = false;\n  }\n};\n\n}  // namespace\n\n// clang-format off\nusing ShuffleAPICircuitTestArgumentsList = testing::Types<\n    TestArguments<ShuffleAPICircuit<BN254SHPlonk::Field, SimpleFloorPlanner>, BN254SHPlonkLogDerivativeHalo2>,\n    TestArguments<ShuffleAPICircuit<BN254SHPlonk::Field, V1FloorPlanner>, BN254SHPlonkLogDerivativeHalo2>>;\n// clang-format on\n\nTYPED_TEST_SUITE(ShuffleAPICircuitTest, ShuffleAPICircuitTestArgumentsList);\n\nTYPED_TEST(ShuffleAPICircuitTest, Configure) { this->ConfigureTest(); }\nTYPED_TEST(ShuffleAPICircuitTest, Synthesize) { this->SynthesizeTest(); }\nTYPED_TEST(ShuffleAPICircuitTest, LoadVerifyingKey) {\n  this->LoadVerifyingKeyTest();\n}\nTYPED_TEST(ShuffleAPICircuitTest, LoadProvingKey) {\n  this->LoadProvingKeyTest();\n}\nTYPED_TEST(ShuffleAPICircuitTest, CreateProof) { this->CreateProofTest(); }\nTYPED_TEST(ShuffleAPICircuitTest, VerifyProof) { this->VerifyProofTest(); }\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/examples/shuffle_api_circuit_test_data.h",
    "content": "#ifndef TACHYON_ZK_PLONK_EXAMPLES_SHUFFLE_API_CIRCUIT_TEST_DATA_H_\n#define TACHYON_ZK_PLONK_EXAMPLES_SHUFFLE_API_CIRCUIT_TEST_DATA_H_\n\n#include <stdint.h>\n\n#include <string_view>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/zk/plonk/examples/circuit_test_data.h\"\n#include \"tachyon/zk/plonk/examples/circuit_test_type_traits.h\"\n#include \"tachyon/zk/plonk/examples/point.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename Circuit, typename PS, typename SFINAE = void>\nclass ShuffleAPICircuitTestData : public CircuitTestData<Circuit, PS> {};\n\n// PCS = SHPlonk\ntemplate <typename Circuit, typename PS>\nclass ShuffleAPICircuitTestData<Circuit, PS,\n                                std::enable_if_t<IsSHPlonk<typename PS::PCS>>>\n    : public CircuitTestData<Circuit, PS> {\n public:\n  using PCS = typename PS::PCS;\n  using F = typename PCS::Field;\n\n  // Set flags of values to be used as true\n  constexpr static bool kAssemblyFixedColumnsFlag = true;\n  constexpr static bool kAssemblyPermutationColumnsFlag = false;\n  constexpr static bool kLFirstFlag = true;\n  constexpr static bool kLLastFlag = true;\n  constexpr static bool kLActiveRowFlag = true;\n  constexpr static bool kFixedColumnsFlag = true;\n  constexpr static bool kFixedPolysFlag = true;\n  constexpr static bool kAdviceCommitmentsFlag = true;\n  constexpr static bool kShuffleProductCommitmentsFlag = true;\n  constexpr static bool kVanishingHPolyCommitmentsFlag = true;\n  constexpr static bool kAdviceEvalsFlag = true;\n  constexpr static bool kFixedEvalsFlag = true;\n  constexpr static bool kShuffleProductEvalsFlag = true;\n  constexpr static bool kShuffleProductNextEvalsFlag = true;\n\n  constexpr static size_t kN = 16;\n\n  // clang-format off\n  constexpr static std::string_view kPinnedConstraintSystem =\n      \"PinnedConstraintSystem { \"\n        \"num_fixed_columns: 1, \"\n        \"num_advice_columns: 3, \"\n        \"num_instance_columns: 0, \"\n        \"num_selectors: 2, \"\n        \"gates: [], \"\n        \"advice_queries: [(\"\n          \"Column { \"\n              \"index: 0, \"\n              \"column_type: Advice \"\n          \"}, \"\n          \"Rotation(0)\"\n        \"), (\"\n          \"Column { \"\n              \"index: 1, \"\n              \"column_type: Advice \"\n          \"}, \"\n          \"Rotation(0)\"\n        \"), (\"\n          \"Column { \"\n              \"index: 2, \"\n              \"column_type: Advice \"\n          \"}, \"\n          \"Rotation(0)\"\n        \")], \"\n        \"instance_queries: [], \"\n        \"fixed_queries: [(\"\n          \"Column { \"\n              \"index: 0, \"\n              \"column_type: Fixed \"\n          \"}, \"\n          \"Rotation(0)\"\n        \")], \"\n        \"permutation: Argument { columns: [] }, \"\n        \"lookups_map: {}, \"\n        \"constants: [], \"\n        \"minimum_degree: None \"\n      \"}\";\n  // clang-format on\n\n  constexpr static std::string_view kAssemblyFixedColumns[][kN] = {\n      {\n          \"0x000000000000000000000000000000000000000000000000000000000000000a\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000014\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000028\",\n          \"0x000000000000000000000000000000000000000000000000000000000000000a\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n      },\n  };\n\n  // clang-format off\n  constexpr static bool kSelectors[][kN] = {\n      {true,   true,  true,  true, false, false, false, false,\n       false, false, false, false, false, false, false, false},\n      {true,   true,  true,  true, false, false, false, false,\n       false, false, false, false, false, false, false, false},\n  };\n  // clang-format on\n\n  // clang-format off\n  constexpr static std::string_view kPinnedVerifyingKey =\n      \"PinnedVerificationKey { \"\n        \"base_modulus: \\\"0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47\\\", \"\n        \"scalar_modulus: \\\"0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001\\\", \"\n        \"domain: PinnedEvaluationDomain { \"\n            \"k: 4, \"\n            \"extended_k: 6, \"\n            \"omega: 0x21082ca216cbbf4e1c6e4f4594dd508c996dfbe1174efb98b11509c6e306460b \"\n        \"}, \"\n        \"cs: PinnedConstraintSystem { \"\n            \"num_fixed_columns: 3, \"\n            \"num_advice_columns: 3, \"\n            \"num_instance_columns: 0, \"\n            \"num_selectors: 2, \"\n            \"gates: [], \"\n            \"advice_queries: [(\"\n              \"Column { \"\n                  \"index: 0, \"\n                  \"column_type: Advice \"\n              \"}, \"\n              \"Rotation(0)\"\n            \"), (\"\n              \"Column { \"\n                  \"index: 1, \"\n                  \"column_type: Advice \"\n              \"}, \"\n              \"Rotation(0)\"\n            \"), (\"\n               \"Column { \"\n                  \"index: 2, \"\n                  \"column_type: Advice \"\n                \"}, \"\n                \"Rotation(0)\"\n            \")], \"\n            \"instance_queries: [], \"\n            \"fixed_queries: [(\"\n              \"Column { \"\n                  \"index: 0, \"\n                  \"column_type: Fixed \"\n              \"}, \"\n              \"Rotation(0)\"\n            \"), (\"\n              \"Column { \"\n                \"index: 1, \"\n                \"column_type: Fixed \"\n              \"}, \"\n              \"Rotation(0)\"\n            \"), (\"\n              \"Column { \"\n                \"index: 2, \"\n                \"column_type: Fixed \"\n              \"}, \"\n              \"Rotation(0)\"\n            \")], \"\n            \"permutation: Argument { columns: [] }, \"\n            \"lookups_map: {}, \"\n            \"constants: [], \"\n            \"minimum_degree: None \"\n        \"}, \"\n        \"fixed_commitments: [\"\n            \"(0x2d4e15ed2b857e7804667e13cc75167d4d96b17e0b4f5b550a5b2973ce42b38b, \"\n              \"0x202b370f2deb277586ef975cf01b528ab0aa32750ff474a30736990862079124), \"\n            \"(0x281d9f77f3053cb0fab1a344caab67658751a45d7878c1a82e1641aad602dc4f, \"\n              \"0x2bc66678fb3b22f2fbf7352fc01959f0db70006764124d957655af470c9048d2), \"\n            \"(0x281d9f77f3053cb0fab1a344caab67658751a45d7878c1a82e1641aad602dc4f, \"\n              \"0x2bc66678fb3b22f2fbf7352fc01959f0db70006764124d957655af470c9048d2)\"\n        \"], \"\n        \"permutation: VerifyingKey { commitments: [] } \"\n      \"}\";\n  // clang-format on\n\n  constexpr static std::string_view kTranscriptRepr =\n      \"0x251c2c983235c1ee7172d0394aba3b41563ff49d03e46c40d9802c72c95ceecb\";\n\n  constexpr static std::string_view kLFirst[] = {\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n  };\n\n  constexpr static std::string_view kLLast[] = {\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2014447de15a99b6df03833e95f96ae1299c9ec6ff990b6e75fa3b3b04846a57\",\n      \"0x0f1f5883e65f820d14d56342dc92fd12a944d4cbbdce5377b7439bd07108fc9d\",\n      \"0x02b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e8\",\n      \"0x030644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000\",\n      \"0x105009f4ffd70672d94cc277eb87ed7bfe9749817a206522cde7ba58eb7b95aa\",\n      \"0x2144f5eefad21e1ca37ae273a4ee5b4a7eef137cbbeb1d198c9e59c37ef70364\",\n      \"0x2db11694c4a58b3789868bd388165969c30dc1120a37fff09a991abf43e42a19\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2014447de15a99b6df03833e95f96ae1299c9ec6ff990b6e75fa3b3b04846a57\",\n      \"0x0f1f5883e65f820d14d56342dc92fd12a944d4cbbdce5377b7439bd07108fc9d\",\n      \"0x02b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e8\",\n      \"0x030644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000\",\n      \"0x105009f4ffd70672d94cc277eb87ed7bfe9749817a206522cde7ba58eb7b95aa\",\n      \"0x2144f5eefad21e1ca37ae273a4ee5b4a7eef137cbbeb1d198c9e59c37ef70364\",\n      \"0x2db11694c4a58b3789868bd388165969c30dc1120a37fff09a991abf43e42a19\",\n  };\n\n  constexpr static std::string_view kLActiveRow[] = {\n      \"0x12259d6b14729c0fa51e1a2470908122ef13771b2da58a367974bc177a000001\",\n      \"0x1a8133201ba2fe22aff30c7fd04fadcb17ceab0715a13371b06d35acd9695b3d\",\n      \"0x0d49c50dd1c3ec703dc7be1c836fd7f62c140afcf284ce19b9a99affac7b95aa\",\n      \"0x117dae38a05bcb8c7c290ee16cec493d73ef8dafa5c61fa4a2efd9a39e63abf4\",\n      \"0x0c19139cb84c680a79505ee7747ae78cd6c196473632bc6ea3057c773208fc9d\",\n      \"0x136156e428a2662bc2fddfd3b39f6475dafecb8699a611f3da6edd22c3479af1\",\n      \"0x2aaad1ad96927134ee0187781ffe43e3f08a828d829c68e7865afb6604e42a19\",\n      \"0x28d7d254c17b7ea40ebc4659996adacebd0d8f52d021284040d407c2f33b896f\",\n      \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n      \"0x1ef7221577b1e8f8cebdcb2fcd10296b6d9d43267b395c820757c0fc87681d81\",\n      \"0x1d0dff96b3477fb4437e7ee32de1555b5719604277fd746561bc1be1c5846a57\",\n      \"0x1be193603aacb384678281793fa8f20b949cf5976d9493017ba8a357ee49da57\",\n      \"0x1e3eb107ccbf041a07f5de183cd645c4ac6bd4f8344f861078603a6a3ff70364\",\n      \"0x20080468beb85b16c9f71b3ea2ce10fb6cdc81c346721c888ebc9109900adec6\",\n      \"0x30114169cfaa9b194b94fb3e12d441cabad6d0fa619f4a28d8ecb10f5d1bd5e9\",\n      \"0x2edcc3ce4ec47abd9b83b31a4db9571236223b590c30997fd30ce24f7bf2fdd6\",\n  };\n\n  constexpr static std::string_view kFixedColumns[][kN] = {\n      {\n          \"0x000000000000000000000000000000000000000000000000000000000000000a\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000014\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000028\",\n          \"0x000000000000000000000000000000000000000000000000000000000000000a\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n      },\n      {\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n      },\n      {\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n      },\n  };\n\n  constexpr static std::string_view kFixedPolys[][kN] = {\n      {\n          \"0x0000000000000000000000000000000000000000000000000000000000000005\",\n          \"0x29b74984a8e202a76661c45c0e1c23834ec7253d1314c598e6f77c7ad51cf036\",\n          \"0x02da37f73c03ba855e9e5e8bf35f1551564eaf8cfc98ba948b81dd51fff8ceae\",\n          \"0x228042fcf4bc2d1ad9b24f632d19dd1548bf7f52ebd79064e83380477cbeaf72\",\n          \"0x0000000000000000705b06c24909ae977f0e1a12edabc2e73481f6b61c59de1d\",\n          \"0x251f9565fdb87df90efa68e06fd185b8c4d1ca853f8cb18f855fbac180d014f2\",\n          \"0x064adf83a4eb29366c2663c6bf99c5b0194718e855ce0bfd4f142a28f13faa07\",\n          \"0x08058968c83a758fcb08defe026a6ac37dcf2b050519bcdf6fefd2f549af5a88\",\n          \"0x244b3ad628e5381f4a3c3448e1210245de26ee365b4b146cf2e9782ef4000002\",\n          \"0x29671636942af51f13b020dd465c1c0c09612a1315807af37f6946775581d4d2\",\n          \"0x217102deece17d9d6e760bcf360f61b080490f40cc1071120b77508dd6d6224b\",\n          \"0x1bbf0aaeae5aa0266b112a884a1e32f2c27eb74af79f84a9775b61555e8e7815\",\n          \"0x244b3ad628e5381ed9e12d86981753ae5f18d4236d9f5185be678178d7a621e0\",\n          \"0x00bccefdf8369aa8c3bc602e7f7997a0c787aadfc82da74f3df4683e2c91260b\",\n          \"0x1e005b5283fa0ee55b3d9a6fd939c7d9cc6f04b6981ef135ffc598551ff16504\",\n          \"0x02519e97e6792d6d84ac0fa8489f89c033407ac9ce0556ec16543bcbc3037df5\",\n      },\n      {\n          \"0x244b3ad628e5381f4a3c3448e1210245de26ee365b4b146cf2e9782ef4000001\",\n          \"0x1a6e4f898fe496f01ea6eaaff139c3aa49b7af6655c100c5392f9a1d57d0147b\",\n          \"0x19b5e5b39b97598b23d3677c6697d6157032b89240d1b0f0c7a45bfb7168bc5f\",\n          \"0x2b59c6d6b45408d25b4d11e087615929d58e833eb19438fedddd2ee1ed5223ce\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x05e4b072c5b5c514ab7373d186f5bffccef88a06715bb3b86770a20e870495b3\",\n          \"0x2bdb831eb009f59ab278e6ad2ef3344d45dcf7433eee7a929cff19ee7f56c326\",\n          \"0x26ec8a1cb3b2049f951a406829dd837d8e9bb25d65b9343f41f3521abc3b3884\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x00253a9213a341f05ce8918915a5ec420e7066938f489b6e3a239be2012716d9\",\n          \"0x2ee08ff8b63316b3871d9c0903df516165845c44a54d387dc2489286e2a93cdc\",\n          \"0x29fbdc8478d0d99780b5fd0fb89b2e9e0b8681dc53a498d02ed8c7ea1c763064\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x03d30047bfa79a2a2339443e534b925cb7064e3604e5c4811825a02114043efa\",\n          \"0x10a1def0e97412995d72d5834ab9573c12fab2ad29b0918e53c15ae6009743a2\",\n          \"0x08d1aa440a71916949bf705d7a49a7babede074ee3cbef812c041a700dfc734d\",\n      },\n      {\n          \"0x244b3ad628e5381f4a3c3448e1210245de26ee365b4b146cf2e9782ef4000001\",\n          \"0x1a6e4f898fe496f01ea6eaaff139c3aa49b7af6655c100c5392f9a1d57d0147b\",\n          \"0x19b5e5b39b97598b23d3677c6697d6157032b89240d1b0f0c7a45bfb7168bc5f\",\n          \"0x2b59c6d6b45408d25b4d11e087615929d58e833eb19438fedddd2ee1ed5223ce\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x05e4b072c5b5c514ab7373d186f5bffccef88a06715bb3b86770a20e870495b3\",\n          \"0x2bdb831eb009f59ab278e6ad2ef3344d45dcf7433eee7a929cff19ee7f56c326\",\n          \"0x26ec8a1cb3b2049f951a406829dd837d8e9bb25d65b9343f41f3521abc3b3884\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x00253a9213a341f05ce8918915a5ec420e7066938f489b6e3a239be2012716d9\",\n          \"0x2ee08ff8b63316b3871d9c0903df516165845c44a54d387dc2489286e2a93cdc\",\n          \"0x29fbdc8478d0d99780b5fd0fb89b2e9e0b8681dc53a498d02ed8c7ea1c763064\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x03d30047bfa79a2a2339443e534b925cb7064e3604e5c4811825a02114043efa\",\n          \"0x10a1def0e97412995d72d5834ab9573c12fab2ad29b0918e53c15ae6009743a2\",\n          \"0x08d1aa440a71916949bf705d7a49a7babede074ee3cbef812c041a700dfc734d\",\n      },\n  };\n\n  constexpr static uint8_t kProof[] = {\n      123, 149, 1,   34,  4,   178, 140, 187, 165, 136, 202, 244, 62,  2,   151,\n      119, 141, 172, 38,  112, 93,  191, 240, 213, 173, 141, 104, 35,  108, 108,\n      178, 19,  99,  199, 7,   207, 154, 138, 8,   187, 113, 59,  4,   131, 197,\n      184, 34,  241, 221, 127, 180, 101, 40,  230, 153, 79,  105, 139, 51,  173,\n      113, 30,  0,   36,  122, 60,  229, 71,  171, 195, 59,  222, 207, 84,  230,\n      206, 151, 175, 95,  127, 4,   120, 157, 12,  132, 58,  230, 10,  175, 158,\n      93,  254, 102, 38,  70,  10,  123, 149, 1,   34,  4,   178, 140, 187, 165,\n      136, 202, 244, 62,  2,   151, 119, 141, 172, 38,  112, 93,  191, 240, 213,\n      173, 141, 104, 35,  108, 108, 178, 19,  99,  199, 7,   207, 154, 138, 8,\n      187, 113, 59,  4,   131, 197, 184, 34,  241, 221, 127, 180, 101, 40,  230,\n      153, 79,  105, 139, 51,  173, 113, 30,  0,   36,  122, 60,  229, 71,  171,\n      195, 59,  222, 207, 84,  230, 206, 151, 175, 95,  127, 4,   120, 157, 12,\n      132, 58,  230, 10,  175, 158, 93,  254, 102, 38,  70,  10,  40,  248, 165,\n      230, 155, 94,  21,  42,  243, 43,  4,   150, 132, 65,  71,  208, 235, 189,\n      87,  173, 66,  88,  193, 46,  179, 106, 15,  24,  187, 2,   203, 103, 179,\n      78,  185, 253, 180, 227, 106, 238, 252, 147, 223, 168, 28,  84,  103, 253,\n      68,  13,  163, 107, 92,  33,  77,  82,  222, 210, 85,  156, 0,   144, 90,\n      27,  1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   105, 112, 247, 230, 99,  68,  226, 246, 1,   139, 135, 253,\n      42,  40,  223, 143, 240, 44,  114, 89,  116, 112, 83,  29,  171, 147, 13,\n      113, 127, 69,  120, 18,  171, 203, 78,  146, 126, 138, 144, 126, 38,  59,\n      201, 84,  49,  63,  95,  119, 88,  50,  69,  9,   111, 240, 86,  108, 227,\n      3,   62,  20,  16,  170, 144, 80,  90,  201, 15,  47,  196, 4,   58,  104,\n      106, 35,  15,  171, 132, 32,  249, 8,   210, 85,  33,  159, 147, 145, 181,\n      59,  6,   78,  166, 209, 122, 13,  142, 103, 169, 97,  43,  75,  89,  167,\n      101, 233, 61,  72,  185, 239, 7,   16,  199, 229, 75,  28,  130, 185, 165,\n      122, 205, 187, 114, 165, 17,  11,  62,  190, 91,  44,  209, 149, 118, 224,\n      113, 214, 75,  198, 60,  59,  144, 31,  13,  128, 129, 79,  211, 15,  183,\n      118, 231, 213, 108, 68,  15,  66,  202, 65,  78,  236, 109, 41,  132, 133,\n      165, 158, 191, 193, 66,  11,  235, 199, 153, 168, 232, 177, 144, 89,  6,\n      128, 219, 249, 179, 170, 105, 162, 192, 156, 250, 132, 238, 102, 135, 44,\n      169, 97,  43,  75,  89,  167, 101, 233, 61,  72,  185, 239, 7,   16,  199,\n      229, 75,  28,  130, 185, 165, 122, 205, 187, 114, 165, 17,  11,  62,  190,\n      91,  44,  209, 149, 118, 224, 113, 214, 75,  198, 60,  59,  144, 31,  13,\n      128, 129, 79,  211, 15,  183, 118, 231, 213, 108, 68,  15,  66,  202, 65,\n      78,  236, 109, 41,  132, 133, 165, 158, 191, 193, 66,  11,  235, 199, 153,\n      168, 232, 177, 144, 89,  6,   128, 219, 249, 179, 170, 105, 162, 192, 156,\n      250, 132, 238, 102, 135, 44,  254, 142, 96,  254, 124, 46,  191, 114, 233,\n      1,   222, 185, 106, 101, 1,   177, 181, 78,  108, 159, 195, 148, 109, 160,\n      224, 212, 141, 238, 15,  216, 187, 32,  94,  164, 148, 212, 21,  95,  124,\n      142, 88,  181, 210, 147, 54,  249, 68,  116, 40,  166, 135, 251, 94,  53,\n      123, 127, 122, 169, 236, 211, 188, 16,  206, 9,   94,  164, 148, 212, 21,\n      95,  124, 142, 88,  181, 210, 147, 54,  249, 68,  116, 40,  166, 135, 251,\n      94,  53,  123, 127, 122, 169, 236, 211, 188, 16,  206, 9,   1,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   21,\n      33,  39,  152, 110, 1,   106, 66,  193, 30,  33,  236, 208, 160, 243, 0,\n      227, 17,  226, 232, 246, 24,  184, 154, 9,   99,  110, 202, 176, 8,   127,\n      38,  171, 201, 131, 218, 180, 3,   225, 154, 17,  127, 114, 216, 120, 129,\n      255, 250, 19,  172, 225, 166, 172, 94,  60,  225, 187, 236, 150, 108, 29,\n      178, 211, 36,  197, 110, 67,  66,  4,   243, 99,  92,  118, 119, 166, 253,\n      145, 19,  233, 20,  198, 233, 26,  113, 47,  123, 174, 100, 77,  147, 34,\n      39,  205, 78,  189, 27,  190, 142, 41,  79,  1,   253, 107, 168, 226, 56,\n      123, 143, 162, 88,  115, 99,  145, 164, 236, 237, 250, 62,  225, 212, 44,\n      123, 233, 130, 218, 205, 135, 32,  93,  168, 98,  215, 47,  38,  123, 245,\n      82,  171, 140, 93,  190, 27,  17,  109, 253, 129, 176, 223, 15,  199, 228,\n      215, 184, 224, 79,  137, 196, 110, 11,  42,  211, 42,  183, 215, 47,  79,\n      178, 182, 206, 51,  36,  81,  146, 77,  234, 35,  225, 213, 241, 16,  165,\n      86,  174, 254, 67,  240, 131, 144, 79,  159, 201, 89};\n\n  // clang-format off\n  constexpr static Point kAdviceCommitments[][3] = {\n      {\n          {\"0x13b26c6c23688dadd5f0bf5d7026ac8d7797023ef4ca88a5bb8cb2042201957b\",\n            \"0x0ff9241bc89e18ea71a0890137187b79870096231e31ce087de76e6440773b38\"},\n          {\"0x24001e71ad338b694f99e62865b47fddf122b8c583043b71bb088a9acf07c763\",\n            \"0x16cb64579b12bff951032189709221da1908ac980fcdcecc3058ffe0b331b178\"},\n          {\"0x0a462666fe5d9eaf0ae63a840c9d78047f5faf97cee654cfde3bc3ab47e53c7a\",\n            \"0x1a4978eec4c46d3bf9b9f8012ffe2a8296d1bdf2f702a319fc8439906ca8225e\"},\n      },\n      {\n          {\"0x13b26c6c23688dadd5f0bf5d7026ac8d7797023ef4ca88a5bb8cb2042201957b\",\n            \"0x0ff9241bc89e18ea71a0890137187b79870096231e31ce087de76e6440773b38\"},\n          {\"0x24001e71ad338b694f99e62865b47fddf122b8c583043b71bb088a9acf07c763\",\n            \"0x16cb64579b12bff951032189709221da1908ac980fcdcecc3058ffe0b331b178\"},\n          {\"0x0a462666fe5d9eaf0ae63a840c9d78047f5faf97cee654cfde3bc3ab47e53c7a\",\n            \"0x1a4978eec4c46d3bf9b9f8012ffe2a8296d1bdf2f702a319fc8439906ca8225e\"},\n      },\n  };\n  // clang-format on\n\n  constexpr static std::string_view kTheta =\n      \"0x209655a8aa61cc6fa4a5b5c538dee9c2e45d96e393ac98518a826f2b65096b1d\";\n\n  constexpr static std::string_view kBeta =\n      \"0x25cda161adf9c679a75f7b870fb2e6aa7c75edce5aa790a546365bc564a93172\";\n\n  constexpr static std::string_view kGamma =\n      \"0x1704848a68c783bb204963d0665ea280ce1c73fc7e18f7ac37a4576df63affa2\";\n\n  // clang-format off\n  constexpr static Point kShuffleProductCommitments[][1] = {\n      {\n          {\"0x27cb02bb180f6ab32ec15842ad57bdebd047418496042bf32a155e9be6a5f828\",\n           \"0x0ec8207c93ae2600b61d6357c695af63ca92ed723b53bc721227d32be74b11bb\"},\n      },\n      {\n          {\"0x1b5a90009c55d2de524d215c6ba30d44fd67541ca8df93fcee6ae3b4fdb94eb3\",\n           \"0x26231bd570fbec74da443a9ce5c93e3e933d008032489dbe112560b7796643f2\"},\n      },\n  };\n  // clang-format on\n\n  constexpr static std::string_view kY =\n      \"0x12d2c7429f3b85194400604f68d427d09df281205ab991831cbc2a290e88b7c4\";\n\n  constexpr static Point kVanishingHPolyCommitments[] = {\n      {\"0x1278457f710d93ab1d53707459722cf08fdf282afd878b01f6e24463e6f77069\",\n       \"0x00db9c6dc837d2477649c858851349adb281949a5f1651b89af437088b5ac17c\"},\n      {\"0x1090aa10143e03e36c56f06f09453258775f3f3154c93b267e908a7e924ecbab\",\n       \"0x07845cc1fdf395d5a63a86fcf6a767829e9ca29ddf0264be6904d64d758a3a35\"},\n      {\"0x278e0d7ad1a64e063bb591939f2155d208f92084ab0f236a683a04c42f0fc95a\",\n       \"0x0e3ea5528ad480e93a1bdacdf87fc195153c45a1c57b69066f4869f5254aa8c9\"},\n  };\n\n  constexpr static std::string_view kX =\n      \"0x2ae1c2c898906cdb9db5361d365669b204f5d3f580c0fa3384938fb05825e7e0\";\n\n  constexpr static std::string_view kAdviceEvals[][3] = {\n      {\n          \"0x2c5bbe3e0b11a572bbcd7aa5b9821c4be5c71007efb9483de965a7594b2b61a9\",\n          \"0x296dec4e41ca420f446cd5e776b70fd34f81800d1f903b3cc64bd671e07695d1\",\n          \"0x2c8766ee84fa9cc0a269aab3f9db80065990b1e8a899c7eb0b42c1bf9ea58584\",\n      },\n      {\n          \"0x2c5bbe3e0b11a572bbcd7aa5b9821c4be5c71007efb9483de965a7594b2b61a9\",\n          \"0x296dec4e41ca420f446cd5e776b70fd34f81800d1f903b3cc64bd671e07695d1\",\n          \"0x2c8766ee84fa9cc0a269aab3f9db80065990b1e8a899c7eb0b42c1bf9ea58584\",\n      },\n  };\n\n  constexpr static std::string_view kFixedEvals[] = {\n      \"0x20bbd80fee8dd4e0a06d94c39f6c4eb5b101656ab9de01e972bf2e7cfe608efe\",\n      \"0x09ce10bcd3eca97a7f7b355efb87a6287444f93693d2b5588e7c5f15d494a45e\",\n      \"0x09ce10bcd3eca97a7f7b355efb87a6287444f93693d2b5588e7c5f15d494a45e\",\n  };\n\n  constexpr static std::string_view kShuffleProductEvals[][1] = {\n      {\n          \"0x267f08b0ca6e63099ab818f6e8e211e300f3a0d0ec211ec1426a016e98272115\",\n      },\n      {\n          \"0x1bbd4ecd2722934d64ae7b2f711ae9c614e91391fda677765c63f30442436ec5\",\n      },\n  };\n\n  constexpr static std::string_view kShuffleProductNextEvals[][1] = {\n      {\n          \"0x24d3b21d6c96ecbbe13c5eaca6e1ac13faff8178d8727f119ae103b4da83c9ab\",\n      },\n      {\n          \"0x2087cdda82e97b2cd4e13efaedeca491637358a28f7b38e2a86bfd014f298ebe\",\n      },\n  };\n\n  constexpr static std::string_view kHEval =\n      \"0x0abd11871bf0169fd16294d572944009f8e98aab3c3cfd9bd8c7d8d772605d67\";\n\n  static void TestConfig(ShuffleAPIConfig<F>& config) {\n    EXPECT_EQ(config.input_0(), AdviceColumnKey(0));\n    EXPECT_EQ(config.input_1(), FixedColumnKey(0));\n    EXPECT_EQ(config.shuffle_0(), AdviceColumnKey(1));\n    EXPECT_EQ(config.shuffle_1(), AdviceColumnKey(2));\n    EXPECT_EQ(config.s_input(), Selector::Complex(1));\n    EXPECT_EQ(config.s_shuffle(), Selector::Complex(0));\n  }\n\n  static Circuit GetCircuit() {\n    std::vector<Value<F>> input_0 = {\n        Value<F>::Known(F(1)),\n        Value<F>::Known(F(2)),\n        Value<F>::Known(F(4)),\n        Value<F>::Known(F(1)),\n    };\n    std::vector<F> input_1 = {\n        F(10),\n        F(20),\n        F(40),\n        F(10),\n    };\n    std::vector<Value<F>> shuffle_0 = {\n        Value<F>::Known(F(4)),\n        Value<F>::Known(F(1)),\n        Value<F>::Known(F(1)),\n        Value<F>::Known(F(2)),\n    };\n    std::vector<Value<F>> shuffle_1 = {\n        Value<F>::Known(F(40)),\n        Value<F>::Known(F(10)),\n        Value<F>::Known(F(10)),\n        Value<F>::Known(F(20)),\n    };\n    return {std::move(input_0), std::move(input_1), std::move(shuffle_0),\n            std::move(shuffle_1)};\n  }\n\n  static std::vector<Circuit> Get2Circuits() {\n    Circuit circuit = GetCircuit();\n    return {circuit, std::move(circuit)};\n  }\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_EXAMPLES_SHUFFLE_API_CIRCUIT_TEST_DATA_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/examples/shuffle_circuit.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n// This is taken and modified from\n// https://github.com/privacy-scaling-explorations/halo2/blob/bc857a7/halo2_proofs/tests/shuffle.rs.\n\n#ifndef TACHYON_ZK_PLONK_EXAMPLES_SHUFFLE_CIRCUIT_H_\n#define TACHYON_ZK_PLONK_EXAMPLES_SHUFFLE_CIRCUIT_H_\n\n#include <memory>\n#include <numeric>\n#include <utility>\n#include <vector>\n\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/zk/plonk/constraint_system/circuit.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F, size_t W>\nclass ShuffleCircuitConfig {\n public:\n  using Field = F;\n\n  ShuffleCircuitConfig() = default;\n  ShuffleCircuitConfig(Selector q_shuffle, Selector q_first, Selector q_last,\n                       std::vector<AdviceColumnKey>&& original_column_keys,\n                       std::vector<AdviceColumnKey>&& shuffled_column_keys,\n                       Challenge theta, Challenge gamma, AdviceColumnKey z)\n      : q_shuffle_(q_shuffle),\n        q_first_(q_first),\n        q_last_(q_last),\n        original_column_keys_(std::move(original_column_keys)),\n        shuffled_column_keys_(std::move(shuffled_column_keys)),\n        theta_(theta),\n        gamma_(gamma),\n        z_(z) {\n    CHECK_EQ(original_column_keys_.size(), W);\n    CHECK_EQ(shuffled_column_keys_.size(), W);\n  }\n\n  ShuffleCircuitConfig Clone() const {\n    std::vector<AdviceColumnKey> original_column_keys = original_column_keys_;\n    std::vector<AdviceColumnKey> shuffled_column_keys = shuffled_column_keys_;\n    return {q_shuffle_,\n            q_first_,\n            q_last_,\n            std::move(original_column_keys),\n            std::move(shuffled_column_keys),\n            theta_,\n            gamma_,\n            z_};\n  }\n\n  Selector q_shuffle() const { return q_shuffle_; }\n  Selector q_first() const { return q_first_; }\n  Selector q_last() const { return q_last_; }\n  const std::vector<AdviceColumnKey>& original_column_keys() const {\n    return original_column_keys_;\n  }\n  const std::vector<AdviceColumnKey>& shuffled_column_keys() const {\n    return shuffled_column_keys_;\n  }\n  Challenge theta() const { return theta_; }\n  Challenge gamma() const { return gamma_; }\n  const AdviceColumnKey& z() const { return z_; }\n\n  static ShuffleCircuitConfig Configure(ConstraintSystem<F>& meta) {\n    Selector q_shuffle = meta.CreateSimpleSelector();\n    Selector q_first = meta.CreateSimpleSelector();\n    Selector q_last = meta.CreateSimpleSelector();\n    std::vector<AdviceColumnKey> original =\n        base::CreateVector(W, [&meta]() { return meta.CreateAdviceColumn(); });\n    std::vector<AdviceColumnKey> shuffled =\n        base::CreateVector(W, [&meta]() { return meta.CreateAdviceColumn(); });\n    Challenge theta = meta.CreateChallengeUsableAfter(kFirstPhase);\n    Challenge gamma = meta.CreateChallengeUsableAfter(kFirstPhase);\n    AdviceColumnKey z = meta.CreateAdviceColumn(kSecondPhase);\n\n    meta.CreateGate(\"z should start with 1\", [q_first,\n                                              &z](VirtualCells<F>& meta) {\n      std::unique_ptr<Expression<F>> q_first_expr = meta.QuerySelector(q_first);\n      std::unique_ptr<Expression<F>> z_expr =\n          meta.QueryAdvice(z, Rotation::Cur());\n      std::unique_ptr<Expression<F>> one_expr =\n          ExpressionFactory<F>::Constant(F::One());\n\n      std::vector<Constraint<F>> constraints;\n      constraints.emplace_back(std::move(q_first_expr) *\n                               (std::move(one_expr) - std::move(z_expr)));\n      return constraints;\n    });\n\n    meta.CreateGate(\"z should end with 1\", [q_last, &z](VirtualCells<F>& meta) {\n      std::unique_ptr<Expression<F>> q_last_expr = meta.QuerySelector(q_last);\n      std::unique_ptr<Expression<F>> z_expr =\n          meta.QueryAdvice(z, Rotation::Cur());\n      std::unique_ptr<Expression<F>> one_expr =\n          ExpressionFactory<F>::Constant(F::One());\n\n      std::vector<Constraint<F>> constraints;\n      constraints.emplace_back(std::move(q_last_expr) *\n                               (std::move(one_expr) - std::move(z_expr)));\n      return constraints;\n    });\n\n    meta.CreateGate(\n        \"z should have valid transition\",\n        [q_shuffle, &original, &shuffled, theta, gamma,\n         &z](VirtualCells<F>& meta) {\n          std::unique_ptr<Expression<F>> q_shuffle_expr =\n              meta.QuerySelector(q_shuffle);\n\n          std::vector<std::unique_ptr<Expression<F>>> original_exprs =\n              base::CreateVector(W, [&meta, &original](size_t i) {\n                return meta.QueryAdvice(original[i], Rotation::Cur());\n              });\n\n          std::vector<std::unique_ptr<Expression<F>>> shuffle_exprs =\n              base::CreateVector(W, [&meta, &shuffled](size_t i) {\n                return meta.QueryAdvice(shuffled[i], Rotation::Cur());\n              });\n\n          std::unique_ptr<Expression<F>> theta_expr =\n              meta.QueryChallenge(theta);\n          std::unique_ptr<Expression<F>> gamma_expr =\n              meta.QueryChallenge(gamma);\n\n          std::unique_ptr<Expression<F>> z_expr =\n              meta.QueryAdvice(z, Rotation::Cur());\n          std::unique_ptr<Expression<F>> z_w_expr =\n              meta.QueryAdvice(z, Rotation::Next());\n\n          // Compress\n          std::unique_ptr<Expression<F>> original_acc_expr = std::accumulate(\n              original_exprs.begin() + 1, original_exprs.end(),\n              std::move(original_exprs[0]),\n              [&theta_expr](std::unique_ptr<Expression<F>>& acc,\n                            std::unique_ptr<Expression<F>>& expr) {\n                return std::move(acc) * theta_expr + std::move(expr);\n              });\n\n          // Compress\n          std::unique_ptr<Expression<F>> shuffled_acc_expr = std::accumulate(\n              shuffle_exprs.begin() + 1, shuffle_exprs.end(),\n              std::move(shuffle_exprs[0]),\n              [&theta_expr](std::unique_ptr<Expression<F>>& acc,\n                            const std::unique_ptr<Expression<F>>& expr) {\n                return std::move(acc) * theta_expr + std::move(expr);\n              });\n\n          // TODO(dongchangYoo): Only one instance of |gamma_expr| should be\n          // moved to avoid an unnecessary deep copy. However, the order of\n          // evaluating expression varies by compiler so applying |std::move| to\n          // only one of the |gamma_expr|s may cause an error. Therefore, the\n          // following code has been implemented in a way that copies both.\n          // In the future, we plan to change |gamma_expr| type to |shared_ptr|\n          // to enable shallow copying.\n          std::vector<Constraint<F>> constraints;\n          constraints.emplace_back(\n              std::move(q_shuffle_expr) *\n              (std::move(z_expr) * (std::move(original_acc_expr) + gamma_expr) -\n               std::move(z_w_expr) *\n                   (std::move(shuffled_acc_expr) + gamma_expr)));\n          return constraints;\n        });\n\n    return {q_shuffle,           q_first, q_last, std::move(original),\n            std::move(shuffled), theta,   gamma,  z};\n  }\n\n private:\n  Selector q_shuffle_;\n  Selector q_first_;\n  Selector q_last_;\n  std::vector<AdviceColumnKey> original_column_keys_;\n  std::vector<AdviceColumnKey> shuffled_column_keys_;\n  Challenge theta_;\n  Challenge gamma_;\n  AdviceColumnKey z_;\n};\n\ntemplate <typename F, size_t W, RowIndex H,\n          template <typename> class _FloorPlanner>\nclass ShuffleCircuit : public Circuit<ShuffleCircuitConfig<F, W>> {\n public:\n  using FloorPlanner = _FloorPlanner<ShuffleCircuit<F, W, H, _FloorPlanner>>;\n\n  ShuffleCircuit() = default;\n  ShuffleCircuit(std::vector<std::vector<F>>&& original_table,\n                 std::vector<std::vector<F>>&& shuffled_table)\n      : original_table_(std::move(original_table)),\n        shuffled_table_(std::move(shuffled_table)) {\n    CHECK_EQ(original_table_.size(), W);\n    CHECK_EQ(shuffled_table_.size(), W);\n    for (size_t i = 0; i < W; ++i) {\n      CHECK_EQ(original_table_[i].size(), H);\n      CHECK_EQ(shuffled_table_[i].size(), H);\n    }\n  }\n\n  std::unique_ptr<Circuit<ShuffleCircuitConfig<F, W>>> WithoutWitness()\n      const override {\n    std::vector<std::vector<F>> dummy_original_table(\n        W, std::vector<F>(H, F::Zero()));\n    std::vector<std::vector<F>> dummy_shuffled_table(\n        W, std::vector<F>(H, F::Zero()));\n    ShuffleCircuit dummy_circuit(std::move(dummy_original_table),\n                                 std::move(dummy_shuffled_table));\n    return std::make_unique<ShuffleCircuit>(std::move(dummy_circuit));\n  }\n\n  static ShuffleCircuitConfig<F, W> Configure(ConstraintSystem<F>& meta) {\n    return ShuffleCircuitConfig<F, W>::Configure(meta);\n  }\n\n  void Synthesize(ShuffleCircuitConfig<F, W>&& config,\n                  Layouter<F>* layouter) const override {\n    Value<F> theta = layouter->GetChallenge(config.theta());\n    Value<F> gamma = layouter->GetChallenge(config.gamma());\n\n    layouter->AssignRegion(\n        \"Shuffle original into shuffled\",\n        [this, &config, &theta, &gamma](Region<F>& region) {\n          // Keygen\n          config.q_first().Enable(region, 0);\n          config.q_last().Enable(region, H);\n          for (RowIndex i = 0; i < H; ++i) {\n            config.q_shuffle().Enable(region, i);\n          }\n\n          // First phase\n          for (size_t i = 0; i < W; ++i) {\n            const AdviceColumnKey& original_column_key =\n                config.original_column_keys()[i];\n            const std::vector<F>& original_values = original_table_[i];\n            for (RowIndex j = 0; j < H; ++j) {\n              region.AssignAdvice(absl::Substitute(\"original[$0][$1]\", i, j),\n                                  original_column_key, j,\n                                  [&original_values, j]() {\n                                    return Value<F>::Known(original_values[j]);\n                                  });\n            }\n          }\n          for (size_t i = 0; i < W; ++i) {\n            const AdviceColumnKey& shuffled_column_key =\n                config.shuffled_column_keys()[i];\n            const std::vector<F>& shuffled_values = shuffled_table_[i];\n            for (RowIndex j = 0; j < H; ++j) {\n              region.AssignAdvice(absl::Substitute(\"shuffled[$0][$1]\", i, j),\n                                  shuffled_column_key, j,\n                                  [&shuffled_values, j]() {\n                                    return Value<F>::Known(shuffled_values[j]);\n                                  });\n            }\n          }\n\n          // Second phase\n          std::vector<Value<F>> z;\n          z.reserve(H + 1);\n          if (!theta.IsNone() && !gamma.IsNone()) {\n            std::vector<F> product =\n                base::CreateVector(H, [this, &theta, &gamma](RowIndex i) {\n                  F compressed = std::accumulate(\n                      shuffled_table_.begin(), shuffled_table_.end(), F::Zero(),\n                      [&theta, i](F& acc,\n                                  const std::vector<F>& shuffled_column) {\n                        acc *= theta.value();\n                        return acc += shuffled_column[i];\n                      });\n                  compressed += gamma.value();\n                  return compressed;\n                });\n\n            CHECK(F::BatchInverseInPlace(product));\n\n            for (RowIndex i = 0; i < H; ++i) {\n              F compressed = std::accumulate(\n                  original_table_.begin(), original_table_.end(), F::Zero(),\n                  [&theta, i](F& acc, const std::vector<F>& original_column) {\n                    acc *= theta.value();\n                    return acc += original_column[i];\n                  });\n              compressed += gamma.value();\n              product[i] *= compressed;\n            }\n\n            z[0] = Value<F>::Known(F::One());\n            for (RowIndex i = 0; i < H; ++i) {\n              z[i + 1] = z[i] * Value<F>::Known(product[i]);\n            }\n          } else {\n            z = std::vector<Value<F>>(H + 1);\n          }\n\n          for (RowIndex i = 0; i < H + 1; ++i) {\n            region.AssignAdvice(absl::Substitute(\"z[$0]\", i), config.z(), i,\n                                [&z, i]() { return z[i]; });\n          }\n        });\n  }\n\n private:\n  std::vector<std::vector<F>> original_table_;\n  std::vector<std::vector<F>> shuffled_table_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_EXAMPLES_SHUFFLE_CIRCUIT_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/examples/shuffle_circuit_test.cc",
    "content": "\n#include \"tachyon/zk/plonk/examples/shuffle_circuit.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/zk/plonk/examples/circuit_test.h\"\n#include \"tachyon/zk/plonk/examples/shuffle_circuit_test_data.h\"\n#include \"tachyon/zk/plonk/layout/floor_planner/simple_floor_planner.h\"\n#include \"tachyon/zk/plonk/layout/floor_planner/v1/v1_floor_planner.h\"\n\nnamespace tachyon::zk::plonk {\n\nnamespace {\n\nstatic const size_t kW = 2;\nstatic const size_t kH = 8;\n\ntemplate <typename TestArguments>\nclass ShuffleCircuitTest\n    : public CircuitTest<TestArguments,\n                         ShuffleTestData<typename TestArguments::Circuit,\n                                         typename TestArguments::PS>> {};\n\n}  // namespace\n\n// clang-format off\nusing ShuffleTestArgumentsList = testing::Types<\n    TestArguments<ShuffleCircuit<BN254SHPlonk::Field, kW, kH, SimpleFloorPlanner>, BN254SHPlonkHalo2>,\n    TestArguments<ShuffleCircuit<BN254SHPlonk::Field, kW, kH, V1FloorPlanner>, BN254SHPlonkHalo2>>;\n// clang-format on\n\nTYPED_TEST_SUITE(ShuffleCircuitTest, ShuffleTestArgumentsList);\n\nTYPED_TEST(ShuffleCircuitTest, Configure) { this->ConfigureTest(); }\nTYPED_TEST(ShuffleCircuitTest, Synthesize) { this->SynthesizeTest(); }\nTYPED_TEST(ShuffleCircuitTest, LoadVerifyingKey) {\n  this->LoadVerifyingKeyTest();\n}\nTYPED_TEST(ShuffleCircuitTest, LoadProvingKey) { this->LoadProvingKeyTest(); }\nTYPED_TEST(ShuffleCircuitTest, CreateProof) { this->CreateProofTest(); }\nTYPED_TEST(ShuffleCircuitTest, VerifyProof) { this->VerifyProofTest(); }\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/examples/shuffle_circuit_test_data.h",
    "content": "#ifndef TACHYON_ZK_PLONK_EXAMPLES_SHUFFLE_CIRCUIT_TEST_DATA_H_\n#define TACHYON_ZK_PLONK_EXAMPLES_SHUFFLE_CIRCUIT_TEST_DATA_H_\n\n#include <stdint.h>\n\n#include <string_view>\n#include <vector>\n\n#include \"tachyon/zk/plonk/examples/circuit_test_data.h\"\n#include \"tachyon/zk/plonk/examples/circuit_test_type_traits.h\"\n#include \"tachyon/zk/plonk/examples/point.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename Circuit, typename PS, typename SFINAE = void>\nclass ShuffleTestData : public CircuitTestData<Circuit, PS> {};\n\n// PCS = SHPlonk\ntemplate <typename Circuit, typename PS>\nclass ShuffleTestData<Circuit, PS,\n                      std::enable_if_t<IsSHPlonk<typename PS::PCS>>>\n    : public CircuitTestData<Circuit, PS> {\n public:\n  using PCS = typename PS::PCS;\n  using F = typename PCS::Field;\n\n  // Set flags of values to be used as true\n  constexpr static bool kLFirstFlag = true;\n  constexpr static bool kLLastFlag = true;\n  constexpr static bool kLActiveRowFlag = true;\n  constexpr static bool kFixedColumnsFlag = true;\n  constexpr static bool kFixedPolysFlag = true;\n  constexpr static bool kAdviceCommitmentsFlag = true;\n  constexpr static bool kChallengesFlag = true;\n  constexpr static bool kAdviceEvalsFlag = true;\n  constexpr static bool kFixedEvalsFlag = true;\n\n  constexpr static size_t kW = 2;\n  constexpr static size_t kH = 8;\n  constexpr static size_t kN = 16;\n\n  // clang-format off\n  constexpr static const std::string_view kOriginalTables[2][kW][kH] = {\n    {\n      {\n        \"0x0330fa29c0b79377aa26b9f89ad6c94912201b4c8a854c4fe1db0aae5d3e3139\",\n        \"0x2b53081c28517939f3f3c8dd4feaa4d05c3fc2247814278abe7444937d23b649\",\n        \"0x0435da43504bfe221f7408f38b0528a188e7534714de220af99504627dac267a\",\n        \"0x0db560c95c6a428448974d4d1930d8e92bca484d0d944c0b5912fea74d2d2d14\",\n        \"0x110f1d954c5579a04df83ad01a4cdaa4a0fe840ad909638d28f14d50fd9c688a\",\n        \"0x23037b83210a774ff6b6caa3093f1c3a075ecc20f46ba78ce33f71214797853d\",\n        \"0x003c96b2d19866aca8a6d299763cbfbe8d1e83063a24c1c64e2107bedc1d727e\",\n        \"0x232a5cd6b5a054cf30ce5975952e9154721c0719a8584d8a6f6a90e3e5f1adc1\",\n      },\n      {\n        \"0x263430d1d95ce2c8ee369a2d05b95564a3d00e58c54a6d45388a234afe13a9ce\",\n        \"0x01fa5bd8e153542b410e3798d747c3ea06bf35d3f41ba326b1248d7936584099\",\n        \"0x1030be57544d86a69777762fc9f8dd36a310c6aad17f74bf3c3631fc69d984c1\",\n        \"0x093a3759811b856dfe2aae65b68b80014b6ab208ee942a153e8f1119189a0fc1\",\n        \"0x06e6c64cffaf685871aa4c59dfe409c52dc6380286e234981afb5c36cf28acea\",\n        \"0x18904ab2392340c3ec6230ccd1dcad9ec130f5a7d25306f2dd42d396e0e67fd7\",\n        \"0x27eccd2de304de9dfef4d88b6f076a2c04937442e3e2c35b1f10d9c3fbb6325c\",\n        \"0x1a24d928cc3f0ba998fec5b9b7f4e2a19a1853b75e7628beea1bd799be7f701a\",\n      },\n    },\n    {\n      {\n        \"0x035cba522d844860cb05f47894a56cd1f6e2b3ec50ef96eab264d5f60b05a7f0\",\n        \"0x2fe274ca9f0e7482712083b53697c1bb312ba37685e29df08ed9d4145076b959\",\n        \"0x05ca447eb6d818c0df8ce00b2681700ca6e2f62e0e40f2eacef881b0ad16ceb0\",\n        \"0x0aa9b6011fdae0573e6ec99ccd133b5434d23511c52bf68667d7a71610f24c1b\",\n        \"0x028337e87c1e1d9833aa80d00571f46e0efe87447b73a7ed834dfc2c47e94e02\",\n        \"0x00d025d0d32d6870a245b5f51e7dde6918431e91efc76ae50ceb9cb37a61cef9\",\n        \"0x23ef42b9ae0dc781f8aa389a1388510de43df90c4ea1ed3d1d40811f4bca7015\",\n        \"0x10f2cf0392479514a4f7c14f39483c799889a1045c1dc993c5b4148334e6694e\",\n      },\n      {\n        \"0x035ad99713a98bea3f59b7dc1278c39718eed214329fccce30d70f78d6244815\",\n        \"0x1155282d4492fdcd980f6c8a4507b0050ce36e1368bcdb15af11c42c3fe8a785\",\n        \"0x248a66e7048c66a373945d6f4e57b2fd3cbb6324d606764c6f399edb5225ee36\",\n        \"0x0c39a645b4354e23d8cad1e435f1e67ea351f18552b95988a3d817e2d22c81d0\",\n        \"0x1237421269fe6491e84fbce500d23a57207bdd691ef35c94d378b97f82b16a3a\",\n        \"0x00593f27ddf3f01b11d7556a8c5d187f0f9818873ad6f41696525969e799a9d7\",\n        \"0x20cbdd95eda99ec21ccc683a973bd161416323be059c86187aae4f2d4399be9a\",\n        \"0x131eef59955630cccce68f7f4d4a6a77a4d3831144a2eac1797561c571e67236\",\n      },\n    }\n  };\n\n  constexpr static const std::string_view kShuffledTables[2][kW][kH] = {\n    {\n      {\n        \"0x23037b83210a774ff6b6caa3093f1c3a075ecc20f46ba78ce33f71214797853d\",\n        \"0x0435da43504bfe221f7408f38b0528a188e7534714de220af99504627dac267a\",\n        \"0x003c96b2d19866aca8a6d299763cbfbe8d1e83063a24c1c64e2107bedc1d727e\",\n        \"0x2b53081c28517939f3f3c8dd4feaa4d05c3fc2247814278abe7444937d23b649\",\n        \"0x232a5cd6b5a054cf30ce5975952e9154721c0719a8584d8a6f6a90e3e5f1adc1\",\n        \"0x110f1d954c5579a04df83ad01a4cdaa4a0fe840ad909638d28f14d50fd9c688a\",\n        \"0x0330fa29c0b79377aa26b9f89ad6c94912201b4c8a854c4fe1db0aae5d3e3139\",\n        \"0x0db560c95c6a428448974d4d1930d8e92bca484d0d944c0b5912fea74d2d2d14\",\n      },\n      {\n        \"0x18904ab2392340c3ec6230ccd1dcad9ec130f5a7d25306f2dd42d396e0e67fd7\",\n        \"0x1030be57544d86a69777762fc9f8dd36a310c6aad17f74bf3c3631fc69d984c1\",\n        \"0x27eccd2de304de9dfef4d88b6f076a2c04937442e3e2c35b1f10d9c3fbb6325c\",\n        \"0x01fa5bd8e153542b410e3798d747c3ea06bf35d3f41ba326b1248d7936584099\",\n        \"0x1a24d928cc3f0ba998fec5b9b7f4e2a19a1853b75e7628beea1bd799be7f701a\",\n        \"0x06e6c64cffaf685871aa4c59dfe409c52dc6380286e234981afb5c36cf28acea\",\n        \"0x263430d1d95ce2c8ee369a2d05b95564a3d00e58c54a6d45388a234afe13a9ce\",\n        \"0x093a3759811b856dfe2aae65b68b80014b6ab208ee942a153e8f1119189a0fc1\",\n      },\n    },\n    {\n      {\n        \"0x028337e87c1e1d9833aa80d00571f46e0efe87447b73a7ed834dfc2c47e94e02\",\n        \"0x05ca447eb6d818c0df8ce00b2681700ca6e2f62e0e40f2eacef881b0ad16ceb0\",\n        \"0x035cba522d844860cb05f47894a56cd1f6e2b3ec50ef96eab264d5f60b05a7f0\",\n        \"0x10f2cf0392479514a4f7c14f39483c799889a1045c1dc993c5b4148334e6694e\",\n        \"0x00d025d0d32d6870a245b5f51e7dde6918431e91efc76ae50ceb9cb37a61cef9\",\n        \"0x23ef42b9ae0dc781f8aa389a1388510de43df90c4ea1ed3d1d40811f4bca7015\",\n        \"0x0aa9b6011fdae0573e6ec99ccd133b5434d23511c52bf68667d7a71610f24c1b\",\n        \"0x2fe274ca9f0e7482712083b53697c1bb312ba37685e29df08ed9d4145076b959\",\n      },\n      {\n        \"0x1237421269fe6491e84fbce500d23a57207bdd691ef35c94d378b97f82b16a3a\",\n        \"0x248a66e7048c66a373945d6f4e57b2fd3cbb6324d606764c6f399edb5225ee36\",\n        \"0x035ad99713a98bea3f59b7dc1278c39718eed214329fccce30d70f78d6244815\",\n        \"0x131eef59955630cccce68f7f4d4a6a77a4d3831144a2eac1797561c571e67236\",\n        \"0x00593f27ddf3f01b11d7556a8c5d187f0f9818873ad6f41696525969e799a9d7\",\n        \"0x20cbdd95eda99ec21ccc683a973bd161416323be059c86187aae4f2d4399be9a\",\n        \"0x0c39a645b4354e23d8cad1e435f1e67ea351f18552b95988a3d817e2d22c81d0\",\n        \"0x1155282d4492fdcd980f6c8a4507b0050ce36e1368bcdb15af11c42c3fe8a785\",\n      },\n    },\n  };\n  // clang-format on\n\n  // clang-format off\n  constexpr static std::string_view kPinnedConstraintSystem =\n      \"PinnedConstraintSystem { \"\n        \"num_fixed_columns: 0, \"\n        \"num_advice_columns: 5, \"\n        \"num_instance_columns: 0, \"\n        \"num_selectors: 3, \"\n        \"num_challenges: 2, \"\n        \"advice_column_phase: [\"\n          \"Phase(0), \"\n          \"Phase(0), \"\n          \"Phase(0), \"\n          \"Phase(0), \"\n          \"Phase(1)\"\n        \"], \"\n        \"challenge_phase: [\"\n          \"Phase(0), \"\n          \"Phase(0)\"\n        \"], \"\n        \"gates: [\"\n          \"Product(\"\n            \"Selector(Selector(1, true)), \"\n            \"Sum(\"\n              \"Constant(0x0000000000000000000000000000000000000000000000000000000000000001), \"\n              \"Negated(Advice { \"\n                \"query_index: 0, \"\n                \"column_index: 4, \"\n                \"rotation: Rotation(0), \"\n                \"phase: Phase(1) \"\n              \"})\"\n            \")\"\n          \"), \"\n          \"Product(\"\n            \"Selector(Selector(2, true)), \"\n            \"Sum(\"\n              \"Constant(0x0000000000000000000000000000000000000000000000000000000000000001), \"\n              \"Negated(Advice { \"\n                \"query_index: 0, \"\n                \"column_index: 4, \"\n                \"rotation: Rotation(0), \"\n                \"phase: Phase(1) \"\n              \"})\"\n            \")\"\n          \"), \"\n          \"Product(\"\n            \"Selector(Selector(0, true)), \"\n            \"Sum(\"\n              \"Product(\"\n                \"Advice { \"\n                  \"query_index: 0, \"\n                  \"column_index: 4, \"\n                  \"rotation: Rotation(0), \"\n                  \"phase: Phase(1) \"\n                \"}, \"\n                \"Sum(\"\n                  \"Sum(\"\n                    \"Product(\"\n                      \"Advice { \"\n                        \"query_index: 1, \"\n                        \"column_index: 0, \"\n                        \"rotation: Rotation(0) \"\n                      \"}, \"\n                      \"Challenge(Challenge { \"\n                        \"index: 0, \"\n                        \"phase: Phase(0) \"\n                      \"})\"\n                    \"), \"\n                    \"Advice { \"\n                      \"query_index: 2, \"\n                      \"column_index: 1, \"\n                      \"rotation: Rotation(0) \"\n                    \"}\"\n                  \"), \"\n                  \"Challenge(Challenge { \"\n                    \"index: 1, \"\n                    \"phase: Phase(0) \"\n                  \"})\"\n                \")\"\n              \"), \"\n              \"Negated(Product(\"\n                \"Advice { \"\n                  \"query_index: 5, \"\n                  \"column_index: 4, \"\n                  \"rotation: Rotation(1), \"\n                  \"phase: Phase(1) \"\n                \"}, \"\n                \"Sum(\"\n                  \"Sum(\"\n                    \"Product(\"\n                      \"Advice { \"\n                        \"query_index: 3, \"\n                        \"column_index: 2, \"\n                        \"rotation: Rotation(0) \"\n                      \"}, \"\n                      \"Challenge(Challenge { \"\n                        \"index: 0, \"\n                        \"phase: Phase(0) \"\n                      \"})\"\n                    \"), \"\n                    \"Advice { \"\n                      \"query_index: 4, \"\n                      \"column_index: 3, \"\n                      \"rotation: Rotation(0) \"\n                    \"}\"\n                  \"), \"\n                  \"Challenge(Challenge { \"\n                    \"index: 1, \"\n                    \"phase: Phase(0) \"\n                  \"})\"\n                \")\"\n              \"))\"\n            \")\"\n          \")\"\n        \"], \"\n        \"advice_queries: [\"\n          \"(\"\n            \"Column { \"\n              \"index: 4, \"\n              \"column_type: Advice { \"\n                \"phase: Phase(1) \"\n              \"} \"\n            \"}, \"\n            \"Rotation(0)\"\n          \"), \"\n          \"(\"\n            \"Column { \"\n              \"index: 0, \"\n              \"column_type: Advice \"\n            \"}, \"\n            \"Rotation(0)\"\n          \"), \"\n          \"(\"\n            \"Column { \"\n              \"index: 1, \"\n              \"column_type: Advice \"\n            \"}, \"\n            \"Rotation(0)\"\n          \"), \"\n          \"(\"\n            \"Column { \"\n              \"index: 2, \"\n              \"column_type: Advice \"\n            \"}, \"\n            \"Rotation(0)\"\n          \"), \"\n          \"(\"\n            \"Column { \"\n              \"index: 3, \"\n              \"column_type: Advice \"\n            \"}, \"\n            \"Rotation(0)\"\n          \"), \"\n          \"(\"\n            \"Column { \"\n              \"index: 4, \"\n              \"column_type: Advice { \"\n                \"phase: Phase(1) \"\n              \"} \"\n            \"}, \"\n            \"Rotation(1)\"\n          \")\"\n        \"], \"\n        \"instance_queries: [], \"\n        \"fixed_queries: [], \"\n        \"permutation: Argument { \"\n          \"columns: [] \"\n        \"}, \"\n        \"lookups: [], \"\n        \"constants: [], \"\n        \"minimum_degree: None \"\n      \"}\";\n  // clang-format on\n\n  // clang-format off\n  constexpr static bool kSelectors[][kN] = {\n      {true, true, true, true, true, true, true, true,\n        false, false, false, false, false, false, false, false},\n      {true, false, false, false, false, false, false, false,\n        false, false, false, false, false, false, false, false},\n      {false, false, false, false, false, false, false, false,\n        true, false, false, false, false, false, false, false},\n  };\n  // clang-format on\n\n  // clang-format off\n  constexpr static std::string_view kPinnedVerifyingKey =\n      \"PinnedVerificationKey { \"\n        \"base_modulus: \\\"0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47\\\", \"\n        \"scalar_modulus: \\\"0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001\\\", \"\n        \"domain: PinnedEvaluationDomain { \"\n          \"k: 4, \"\n          \"extended_k: 5, \"\n          \"omega: 0x21082ca216cbbf4e1c6e4f4594dd508c996dfbe1174efb98b11509c6e306460b \"\n        \"}, \"\n        \"cs: PinnedConstraintSystem { \"\n          \"num_fixed_columns: 2, \"\n          \"num_advice_columns: 5, \"\n          \"num_instance_columns: 0, \"\n          \"num_selectors: 3, \"\n          \"num_challenges: 2, \"\n          \"advice_column_phase: [\"\n            \"Phase(0), \"\n            \"Phase(0), \"\n            \"Phase(0), \"\n            \"Phase(0), \"\n            \"Phase(1)\"\n          \"], \"\n          \"challenge_phase: [\"\n            \"Phase(0), \"\n            \"Phase(0)\"\n          \"], \"\n          \"gates: [\"\n            \"Product(\"\n              \"Product(\"\n                \"Fixed { \"\n                  \"query_index: 1, \"\n                  \"column_index: 1, \"\n                  \"rotation: Rotation(0) \"\n                \"}, \"\n                \"Sum(\"\n                  \"Constant(0x0000000000000000000000000000000000000000000000000000000000000002), \"\n                  \"Negated(Fixed { \"\n                    \"query_index: 1, \"\n                    \"column_index: 1, \"\n                    \"rotation: Rotation(0) \"\n                  \"})\"\n                \")\"\n              \"), \"\n              \"Sum(\"\n                \"Constant(0x0000000000000000000000000000000000000000000000000000000000000001), \"\n                \"Negated(Advice { \"\n                  \"query_index: 0, \"\n                  \"column_index: 4, \"\n                  \"rotation: Rotation(0), \"\n                  \"phase: Phase(1) \"\n                \"})\"\n              \")\"\n            \"), \"\n            \"Product(\"\n              \"Product(\"\n                \"Fixed { \"\n                  \"query_index: 1, \"\n                  \"column_index: 1, \"\n                  \"rotation: Rotation(0) \"\n                \"}, \"\n                \"Sum(\"\n                  \"Constant(0x0000000000000000000000000000000000000000000000000000000000000001), \"\n                  \"Negated(Fixed { \"\n                    \"query_index: 1, \"\n                    \"column_index: 1, \"\n                    \"rotation: Rotation(0) \"\n                  \"})\"\n                \")\"\n              \"), \"\n              \"Sum(\"\n                \"Constant(0x0000000000000000000000000000000000000000000000000000000000000001), \"\n                \"Negated(Advice { \"\n                  \"query_index: 0, \"\n                  \"column_index: 4, \"\n                  \"rotation: Rotation(0), \"\n                  \"phase: Phase(1) \"\n                \"})\"\n              \")\"\n            \"), \"\n            \"Product(\"\n              \"Fixed { \"\n                \"query_index: 0, \"\n                \"column_index: 0, \"\n                \"rotation: Rotation(0) \"\n              \"}, \"\n              \"Sum(\"\n                \"Product(\"\n                  \"Advice { \"\n                    \"query_index: 0, \"\n                    \"column_index: 4, \"\n                    \"rotation: Rotation(0), \"\n                    \"phase: Phase(1) \"\n                  \"}, \"\n                  \"Sum(\"\n                    \"Sum(\"\n                      \"Product(\"\n                        \"Advice { \"\n                          \"query_index: 1, \"\n                          \"column_index: 0, \"\n                          \"rotation: Rotation(0) \"\n                        \"}, \"\n                        \"Challenge(Challenge { \"\n                          \"index: 0, \"\n                          \"phase: Phase(0) \"\n                        \"})\"\n                      \"), \"\n                      \"Advice { \"\n                        \"query_index: 2, \"\n                        \"column_index: 1, \"\n                        \"rotation: Rotation(0) \"\n                      \"}\"\n                    \"), \"\n                    \"Challenge(Challenge { \"\n                      \"index: 1, \"\n                      \"phase: Phase(0) \"\n                    \"})\"\n                  \")\"\n                \"), \"\n                \"Negated(Product(\"\n                  \"Advice { \"\n                    \"query_index: 5, \"\n                    \"column_index: 4, \"\n                    \"rotation: Rotation(1), \"\n                    \"phase: Phase(1) \"\n                  \"}, \"\n                  \"Sum(\"\n                    \"Sum(\"\n                      \"Product(\"\n                        \"Advice { \"\n                          \"query_index: 3, \"\n                          \"column_index: 2, \"\n                          \"rotation: Rotation(0) \"\n                        \"}, \"\n                        \"Challenge(Challenge { \"\n                          \"index: 0, \"\n                          \"phase: Phase(0) \"\n                        \"})\"\n                      \"), \"\n                      \"Advice { \"\n                        \"query_index: 4, \"\n                        \"column_index: 3, \"\n                        \"rotation: Rotation(0) \"\n                      \"}\"\n                    \"), \"\n                    \"Challenge(Challenge { \"\n                      \"index: 1, \"\n                      \"phase: Phase(0) \"\n                    \"})\"\n                  \")\"\n                \"))\"\n              \")\"\n            \")\"\n          \"], \"\n          \"advice_queries: [\"\n            \"(\"\n              \"Column { \"\n                \"index: 4, \"\n                \"column_type: Advice { \"\n                  \"phase: Phase(1) \"\n                \"} \"\n              \"}, \"\n              \"Rotation(0)\"\n            \"), \"\n            \"(\"\n              \"Column { \"\n                \"index: 0, \"\n                \"column_type: Advice \"\n              \"}, \"\n              \"Rotation(0)\"\n            \"), \"\n            \"(\"\n              \"Column { \"\n                \"index: 1, \"\n                \"column_type: Advice \"\n              \"}, \"\n              \"Rotation(0)\"\n            \"), \"\n            \"(\"\n              \"Column { \"\n                \"index: 2, \"\n                \"column_type: Advice \"\n              \"}, \"\n              \"Rotation(0)\"\n            \"), \"\n            \"(\"\n              \"Column { \"\n                \"index: 3, \"\n                \"column_type: Advice \"\n              \"}, \"\n              \"Rotation(0)\"\n            \"), \"\n            \"(\"\n              \"Column { \"\n                \"index: 4, \"\n                \"column_type: Advice { \"\n                  \"phase: Phase(1) \"\n                \"} \"\n              \"}, \"\n              \"Rotation(1)\"\n            \")\"\n          \"], \"\n          \"instance_queries: [], \"\n          \"fixed_queries: [\"\n            \"(\"\n              \"Column { \"\n                \"index: 0, \"\n                \"column_type: Fixed \"\n              \"}, \"\n              \"Rotation(0)\"\n            \"), \"\n            \"(\"\n              \"Column { \"\n                \"index: 1, \"\n                \"column_type: Fixed \"\n              \"}, \"\n              \"Rotation(0)\"\n            \")\"\n          \"], \"\n          \"permutation: Argument { \"\n            \"columns: [] \"\n          \"}, \"\n          \"lookups: [], \"\n          \"constants: [], \"\n          \"minimum_degree: None \"\n        \"}, \"\n        \"fixed_commitments: [\"\n          \"(0x297ff8a661d1fa1196c065b6fb7df901fb16b5b83168ddca6c7749d963cc967a, \"\n            \"0x1a7b1f2b5f3e35fc4c706ece6b8f646e95904f9aa417f8a31fd37a41da167ec1), \"\n          \"(0x1025d577f7527c7c9a5d132164beef9ec3ebc63805b146843466f6cdf5fb37d4, \"\n            \"0x1ba406f864ec730f7f725b3478ce1e22c5579b6228593c9ba36e9cb18a10aab3)\"\n        \"], \"\n        \"permutation: VerifyingKey { \"\n          \"commitments: [] \"\n        \"} \"\n      \"}\";\n  // clang-format on\n\n  constexpr static std::string_view kTranscriptRepr =\n      \"0x220eecfcd245ace6db06b0c892cef067bb85cc36dc0401c6e05b618bb939f761\";\n\n  constexpr static std::string_view kLFirst[] = {\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n  };\n\n  constexpr static std::string_view kLLast[] = {\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2014447de15a99b6df03833e95f96ae1299c9ec6ff990b6e75fa3b3b04846a57\",\n      \"0x0f1f5883e65f820d14d56342dc92fd12a944d4cbbdce5377b7439bd07108fc9d\",\n      \"0x02b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e8\",\n      \"0x030644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000\",\n      \"0x105009f4ffd70672d94cc277eb87ed7bfe9749817a206522cde7ba58eb7b95aa\",\n      \"0x2144f5eefad21e1ca37ae273a4ee5b4a7eef137cbbeb1d198c9e59c37ef70364\",\n      \"0x2db11694c4a58b3789868bd388165969c30dc1120a37fff09a991abf43e42a19\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2014447de15a99b6df03833e95f96ae1299c9ec6ff990b6e75fa3b3b04846a57\",\n      \"0x0f1f5883e65f820d14d56342dc92fd12a944d4cbbdce5377b7439bd07108fc9d\",\n      \"0x02b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e8\",\n      \"0x030644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000\",\n      \"0x105009f4ffd70672d94cc277eb87ed7bfe9749817a206522cde7ba58eb7b95aa\",\n      \"0x2144f5eefad21e1ca37ae273a4ee5b4a7eef137cbbeb1d198c9e59c37ef70364\",\n      \"0x2db11694c4a58b3789868bd388165969c30dc1120a37fff09a991abf43e42a19\",\n  };\n\n  constexpr static std::string_view kLActiveRow[] = {\n      \"0x12259d6b14729c0fa51e1a2470908122ef13771b2da58a367974bc177a000001\",\n      \"0x1a8133201ba2fe22aff30c7fd04fadcb17ceab0715a13371b06d35acd9695b3d\",\n      \"0x0d49c50dd1c3ec703dc7be1c836fd7f62c140afcf284ce19b9a99affac7b95aa\",\n      \"0x117dae38a05bcb8c7c290ee16cec493d73ef8dafa5c61fa4a2efd9a39e63abf4\",\n      \"0x0c19139cb84c680a79505ee7747ae78cd6c196473632bc6ea3057c773208fc9d\",\n      \"0x136156e428a2662bc2fddfd3b39f6475dafecb8699a611f3da6edd22c3479af1\",\n      \"0x2aaad1ad96927134ee0187781ffe43e3f08a828d829c68e7865afb6604e42a19\",\n      \"0x28d7d254c17b7ea40ebc4659996adacebd0d8f52d021284040d407c2f33b896f\",\n      \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n      \"0x1ef7221577b1e8f8cebdcb2fcd10296b6d9d43267b395c820757c0fc87681d81\",\n      \"0x1d0dff96b3477fb4437e7ee32de1555b5719604277fd746561bc1be1c5846a57\",\n      \"0x1be193603aacb384678281793fa8f20b949cf5976d9493017ba8a357ee49da57\",\n      \"0x1e3eb107ccbf041a07f5de183cd645c4ac6bd4f8344f861078603a6a3ff70364\",\n      \"0x20080468beb85b16c9f71b3ea2ce10fb6cdc81c346721c888ebc9109900adec6\",\n      \"0x30114169cfaa9b194b94fb3e12d441cabad6d0fa619f4a28d8ecb10f5d1bd5e9\",\n      \"0x2edcc3ce4ec47abd9b83b31a4db9571236223b590c30997fd30ce24f7bf2fdd6\",\n  };\n\n  constexpr static std::string_view kFixedColumns[][kN] = {\n      {\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n      },\n      {\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000002\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n      },\n  };\n\n  constexpr static std::string_view kFixedPolys[][kN] = {\n      {\n          \"0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000001\",\n          \"0x1db5b8e23dbf6d914e37d939eb6b037c619b3e5ea827cdb2b030fc247bdc9dcb\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x15612923e039d29887b96603ef6ab434a2c021b8674489fcfa35d3972944e837\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x186635fde662f3b15c5e1b5fc456d148c26786b9cda347e81f7f4c2f8c9761ed\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x14a1b21f8b9c269f87d74740cf8c84a3046338799106ae503c4dfb0f0b0b250e\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x15b61284f96f4584f96ef5bee1c4a8ae7eca32c5d97b942edf17bbd266f4daf3\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x11f18ea69ea8787324e8219fecfa5c08c0c5e4859cdefa96fbe66ab1e5689e14\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x14f69b80a4d1998bf98cd6fbc1e6791ce06d4987033db882212fe34a48bb17ca\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0ca20bc2474bfe93330e63c5c5e629d521922ce0c25a74cc6b34babcf6236236\",\n      },\n      {\n          \"0x27517fbd56f85221e5c138a4493917cbb0aa2cbae2e6ab760727978833000001\",\n          \"0x030644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000\",\n          \"0x27517fbd56f85221e5c138a4493917cbb0aa2cbae2e6ab760727978833000001\",\n          \"0x030644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000\",\n          \"0x27517fbd56f85221e5c138a4493917cbb0aa2cbae2e6ab760727978833000001\",\n          \"0x030644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000\",\n          \"0x27517fbd56f85221e5c138a4493917cbb0aa2cbae2e6ab760727978833000001\",\n          \"0x030644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000\",\n          \"0x27517fbd56f85221e5c138a4493917cbb0aa2cbae2e6ab760727978833000001\",\n          \"0x030644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000\",\n          \"0x27517fbd56f85221e5c138a4493917cbb0aa2cbae2e6ab760727978833000001\",\n          \"0x030644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000\",\n          \"0x27517fbd56f85221e5c138a4493917cbb0aa2cbae2e6ab760727978833000001\",\n          \"0x030644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000\",\n          \"0x27517fbd56f85221e5c138a4493917cbb0aa2cbae2e6ab760727978833000001\",\n          \"0x030644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000\",\n      },\n  };\n\n  constexpr static uint8_t kProof[] = {\n      122, 117, 229, 163, 114, 52,  159, 223, 87,  191, 210, 4,   56,  250,\n      59,  18,  206, 131, 78,  129, 60,  202, 130, 174, 203, 80,  0,   221,\n      190, 150, 199, 170, 97,  250, 239, 195, 165, 188, 251, 230, 129, 105,\n      212, 209, 41,  124, 75,  202, 166, 142, 57,  16,  160, 164, 175, 119,\n      80,  96,  210, 1,   220, 55,  229, 150, 176, 0,   127, 136, 63,  27,\n      146, 170, 55,  152, 223, 250, 131, 165, 92,  170, 126, 214, 34,  187,\n      172, 236, 248, 250, 195, 21,  121, 118, 69,  108, 167, 132, 242, 42,\n      222, 129, 23,  144, 206, 50,  2,   128, 3,   44,  222, 217, 223, 35,\n      228, 184, 246, 76,  34,  146, 170, 5,   127, 243, 140, 134, 59,  224,\n      63,  139, 97,  91,  176, 229, 208, 22,  81,  76,  102, 212, 230, 42,\n      248, 115, 0,   57,  161, 155, 76,  46,  222, 63,  50,  191, 169, 117,\n      109, 77,  100, 113, 115, 163, 219, 109, 33,  128, 52,  174, 106, 188,\n      197, 159, 82,  64,  13,  77,  214, 88,  94,  3,   59,  239, 242, 15,\n      57,  52,  184, 96,  8,   195, 219, 8,   28,  165, 23,  155, 225, 52,\n      2,   100, 158, 139, 3,   255, 216, 141, 144, 92,  148, 43,  245, 117,\n      105, 244, 203, 45,  169, 199, 189, 108, 165, 254, 123, 192, 88,  166,\n      183, 219, 170, 20,  145, 67,  54,  188, 84,  208, 144, 229, 73,  102,\n      239, 199, 192, 67,  84,  125, 77,  150, 208, 152, 34,  245, 28,  245,\n      53,  241, 240, 142, 238, 116, 44,  171, 9,   62,  209, 106, 14,  52,\n      209, 60,  194, 175, 33,  157, 149, 131, 25,  211, 81,  22,  167, 97,\n      84,  92,  7,   120, 171, 195, 156, 29,  78,  254, 1,   92,  9,   249,\n      182, 255, 222, 133, 161, 215, 117, 56,  201, 12,  65,  236, 138, 136,\n      207, 214, 29,  1,   218, 176, 164, 120, 50,  120, 130, 42,  1,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   96,  134, 180, 185, 62,  13,  187, 73,  198, 194, 240, 58,\n      143, 101, 182, 194, 201, 129, 242, 182, 19,  7,   12,  75,  34,  92,\n      65,  156, 85,  241, 183, 171, 45,  201, 146, 14,  118, 58,  117, 27,\n      61,  108, 249, 137, 144, 71,  115, 19,  73,  98,  4,   77,  237, 52,\n      69,  171, 85,  201, 246, 215, 91,  71,  145, 175, 139, 126, 48,  67,\n      66,  130, 140, 51,  253, 115, 220, 250, 206, 22,  138, 113, 63,  0,\n      74,  233, 167, 144, 210, 243, 212, 44,  42,  58,  221, 5,   215, 30,\n      94,  146, 3,   134, 1,   201, 167, 90,  132, 197, 56,  187, 221, 23,\n      242, 164, 87,  157, 142, 157, 80,  235, 12,  136, 21,  115, 181, 19,\n      96,  199, 209, 5,   135, 209, 93,  98,  16,  78,  106, 217, 160, 233,\n      35,  255, 207, 251, 154, 71,  20,  253, 239, 202, 139, 237, 246, 171,\n      137, 255, 94,  92,  53,  190, 236, 34,  153, 4,   94,  63,  214, 40,\n      181, 15,  147, 150, 81,  110, 181, 99,  38,  34,  56,  239, 28,  121,\n      151, 11,  251, 82,  30,  105, 132, 33,  69,  126, 142, 15,  157, 153,\n      218, 141, 253, 210, 254, 249, 230, 130, 27,  49,  161, 88,  161, 222,\n      147, 49,  245, 92,  45,  16,  131, 95,  217, 194, 215, 16,  69,  71,\n      78,  29,  108, 202, 1,   68,  66,  44,  39,  172, 207, 144, 77,  121,\n      120, 114, 211, 147, 52,  200, 152, 197, 161, 235, 76,  139, 115, 141,\n      206, 208, 134, 251, 78,  15,  16,  93,  166, 218, 134, 85,  60,  230,\n      36,  206, 42,  75,  206, 2,   145, 43,  18,  145, 135, 107, 152, 229,\n      5,   62,  206, 252, 129, 232, 114, 75,  228, 33,  140, 218, 27,  139,\n      246, 80,  127, 36,  80,  115, 177, 219, 69,  80,  235, 123, 191, 251,\n      189, 206, 103, 248, 242, 58,  173, 185, 101, 112, 138, 176, 235, 34,\n      81,  60,  31,  132, 241, 220, 231, 212, 135, 74,  159, 124, 115, 227,\n      149, 192, 56,  152, 43,  182, 158, 246, 63,  125, 148, 144, 243, 217,\n      66,  119, 14,  38,  238, 129, 238, 25,  126, 177, 4,   135, 84,  255,\n      216, 141, 221, 137, 172, 22,  182, 33,  217, 8,   95,  92,  208, 83,\n      226, 5,   67,  65,  218, 137, 253, 12,  231, 216, 174, 122, 162, 239,\n      88,  16,  65,  125, 101, 123, 96,  188, 118, 164, 68,  145, 203, 129,\n      54,  95,  84,  4,   83,  98,  53,  87,  145, 106, 223, 26,  146, 169,\n      158, 159, 209, 157, 89,  102, 204, 254, 97,  92,  149, 104, 21,  94,\n      238, 231, 74,  192, 45,  11,  167, 112, 183, 54,  189, 61,  176, 204,\n      151, 2,   216, 147, 249, 148, 195, 213, 197, 49,  184, 7,   174, 45,\n      175, 251, 144, 126, 180, 32,  145, 186, 94,  84,  170, 71,  228, 213,\n      109, 89,  141, 161, 164, 38,  204, 163, 86,  138, 200, 222, 184, 232,\n      163, 254, 172, 253, 164, 60,  8,   91,  31,  113, 46,  55,  14,  133,\n      120, 246, 172, 208, 57,  35,  123, 179, 212, 32,  1,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      194, 213, 8,   179, 79,  153, 219, 29,  172, 38,  212, 118, 159, 250,\n      149, 134, 98,  218, 34,  34,  95,  113, 0,   26,  133, 92,  226, 144,\n      186, 56,  200, 41,  115, 243, 72,  67,  69,  61,  133, 160, 71,  211,\n      240, 160, 254, 68,  206, 47,  27,  164, 205, 166, 188, 172, 123, 134,\n      154, 35,  148, 250, 50,  159, 23,  168};\n\n  // clang-format off\n  constexpr static Point kAdviceCommitments[][5] = {\n      {\n          {\"0x2ac796bedd0050cbae82ca3c814e83ce123bfa3804d2bf57df9f3472a3e5757a\",\n           \"0x225c37b55b77d7c61ec1b3497b4cd49e88979a7529f69157359cfb31af4baa8d\"},\n          {\"0x16e537dc01d2605077afa4a010398ea6ca4b7c29d1d46981e6fbbca5c3effa61\",\n           \"0x274fdf6ec7a8f5080e28b30278cdc8993517cbae5f2b1d2d09c67e0be64a1419\"},\n          {\"0x04a76c45767915c3faf8ecacbb22d67eaa5ca583fadf9837aa921b3f887f00b0\",\n           \"0x03ed297fdc6199331c528b55c05c0fbf77a1afb96ce7fdc91d2ba74fcc8dac41\"},\n          {\"0x0b3fe03b868cf37f05aa92224cf6b8e423dfd9de2c03800232ce901781de2af2\",\n           \"0x1d3274f270c5b3969008d4e6b4aa75bf816b34a82ad2f01edf7c7b11138d05b5\"},\n          {\"0x1d9cc3ab78075c5461a71651d31983959d21afc23cd1340e6ad13e09ab2c74ee\",\n           \"0x01badd1509eebaca90b100b0fbfbad42486e4619f186715c71aaaa8f8217cfa2\"},\n      },\n      {\n          {\"0x237371644d6d75a9bf323fde2e4c9ba1390073f82ae6d4664c5116d0e5b05b61\",\n           \"0x046f8e3efa26d48fa674c14f6ed386b43503bd345fd9d8d81614723520c57761\"},\n          {\"0x251c08dbc30860b834390ff2ef3b035e58d64d0d40529fc5bc6aae3480216ddb\",\n           \"0x1af96eb77e286bd14427ad56cb681c9ba67716ae44e383b871b53b405b4db24b\"},\n          {\"0x2658c07bfea56cbdc7a92dcbf46975f52b945c908dd8ff038b9e640234e19b17\",\n           \"0x1faea45a2a8673c64b9e122a48724d8ca2978ba5fc7470b92851c5839fb9744b\"},\n          {\"0x0ef0f135f51cf52298d0964d7d5443c0c7ef6649e590d054bc36439114aadbb7\",\n           \"0x2688b891c83687257bfb32b08f072a4d876f652bcaaf673217dcfc8679d4ac39\"},\n          {\"0x2a82783278a4b0da011dd6cf888aec410cc93875d7a185deffb6f9095c01fe4e\",\n           \"0x00610abd37a882e9635be4ec3ab465ac92fa1c029392a7a16c69358c46432450\"},\n      },\n  };\n  // clang-format on\n\n  constexpr static std::string_view kChallenges[] = {\n      \"0x2f2cdac7ef66f10d3c910f13e5a4344318eeeceae9edcdd75b4c1976de43475e\",\n      \"0x154c67d8fa9170f88d424e7634e151902bcf91212c5f7192b58094f50f4da90c\"};\n\n  constexpr static Point kVanishingHPolyCommitments[] = {\n      {\"0x2bb7f1559c415c224b0c0713b6f281c9c2b6658f3af0c2c649bb0d3eb9b48660\",\n       \"0x0b4621657cbc27228adc9011edbd154e9b1b743a9901ce7587b5802c4b93e4a5\"},\n      {\"0x2f91475bd7f6c955ab4534ed4d0462491373479089f96c3d1b753a760e92c92d\",\n       \"0x0f2b30e0179380f1a9dda5cb59eb5ff6c0c093f39ff3129b09a8fb4493fcb719\"},\n  };\n\n  constexpr static std::string_view kTheta =\n      \"0x1b05d778b77bda225d239dffc14f3976e8fe1760fd21713b610c3f213589400f\";\n\n  constexpr static std::string_view kBeta =\n      \"0x1a98a31b28f4089c8af7cdb48e9e056838679dffae6e96ff083cb8dba53a9143\";\n\n  constexpr static std::string_view kGamma =\n      \"0x2dc0d56a5db87e084ca72c1c51467b28d927ff94227126f04f69cb21965081f8\";\n\n  constexpr static std::string_view kY =\n      \"0x17e9a336d7b197716486c320c264d80a5eab37371602ad38a6feb22486894c17\";\n\n  constexpr static std::string_view kX =\n      \"0x14479ed68753bcc9dfd9c779bc02c61fefa774a94b9b9030c94383bdc4bf4c43\";\n\n  constexpr static std::string_view kAdviceEvals[][6] = {\n      {\n          \"0x1ed705dd3a2a2cd4f3d290a7e94a003f718a16cefadc73fd338c824243307e8b\",\n          \"0x05d1c76013b57315880ceb509d8e9d57a4f217ddbb38c5845aa7c9018603925e\",\n          \"0x22ecbe355c5eff89abf6ed8bcaeffd14479afbcfff23e9a0d96a4e10625dd187\",\n          \"0x0f8e7e452184691e52fb0b97791cef38222663b56e5196930fb528d63f5e0499\",\n          \"0x1d4e474510d7c2d95f83102d5cf53193dea158a1311b82e6f9fed2fd8dda999d\",\n          \"0x0f4efb86d0ce8d738b4ceba1c598c83493d37278794d90cfac272c424401ca6c\",\n      },\n      {\n          \"0x21e44b72e881fcce3e05e5986b8791122b9102ce4b2ace24e63c5586daa65d10\",\n          \"0x22ebb08a7065b9ad3af2f867cebdfbbf7beb5045dbb17350247f50f68b1bda8c\",\n          \"0x260e7742d9f390947d3ff69eb62b9838c095e3737c9f4a87d4e7dcf1841f3c51\",\n          \"0x0cfd89da414305e253d05c5f08d921b616ac89dd8dd8ff548704b17e19ee81ee\",\n          \"0x1adf6a915735625304545f3681cb9144a476bc607b657d411058efa27aaed8e7\",\n          \"0x0297ccb03dbd36b770a70b2dc04ae7ee5e1568955c61fecc66599dd19f9ea992\",\n      },\n  };\n\n  constexpr static std::string_view kFixedEvals[] = {\n      \"0x26a4a18d596dd5e447aa545eba9120b47e90fbaf2dae07b831c5d5c394f993d8\",\n      \"0x20d4b37b2339d0acf678850e372e711f5b083ca4fdacfea3e8b8dec88a56a3cc\",\n  };\n\n  constexpr static std::string_view kHEval =\n      \"0x0cdce47f9c4b0dfea0fffc3e184b36a02517c68ecae1d32921c9050771799bd2\";\n\n  static void TestConfig(ShuffleCircuitConfig<F, kW>& config) {\n    EXPECT_EQ(config.q_shuffle(), Selector::Simple(0));\n    EXPECT_EQ(config.q_first(), Selector::Simple(1));\n    EXPECT_EQ(config.q_last(), Selector::Simple(2));\n    for (size_t i = 0; i < kW; i++) {\n      EXPECT_EQ(config.original_column_keys()[i], AdviceColumnKey(i));\n      EXPECT_EQ(config.shuffled_column_keys()[i], AdviceColumnKey(kW + i));\n    }\n    EXPECT_EQ(config.theta(), Challenge(0, kFirstPhase));\n    EXPECT_EQ(config.gamma(), Challenge(1, kFirstPhase));\n    EXPECT_EQ(config.z(), AdviceColumnKey(2 * kW, kSecondPhase));\n  }\n\n  static Circuit GetCircuit(size_t i = 0) {\n    CHECK_LT(i, std::size(kOriginalTables));\n    return {CreateTable(kOriginalTables[i]), CreateTable(kShuffledTables[i])};\n  }\n\n  static std::vector<Circuit> Get2Circuits() {\n    return {GetCircuit(0), GetCircuit(1)};\n  }\n\n private:\n  static std::vector<std::vector<F>> CreateTable(\n      const std::string_view table[kW][kH]) {\n    return base::CreateVector(kW, [table](size_t i) {\n      return base::CreateVector(\n          kH, [table, i](size_t j) { return *F::FromHexString(table[i][j]); });\n    });\n  }\n};\n\n// TODO(ashjeong): Obtain all data for the GWC version and run\n// PCS = GWC\ntemplate <typename Circuit, typename PS>\nclass ShuffleTestData<Circuit, PS, std::enable_if_t<IsGWC<typename PS::PCS>>>\n    : public CircuitTestData<Circuit, PS> {\n public:\n  using PCS = typename PS::PCS;\n  using F = typename PCS::Field;\n\n  constexpr static size_t kW = 2;\n  constexpr static size_t kH = 8;\n  constexpr static size_t kN = 16;\n\n  // clang-format off\n  constexpr static const std::string_view kOriginalTables[2][kW][kH] = {\n    {\n      {\n        \"0x0330fa29c0b79377aa26b9f89ad6c94912201b4c8a854c4fe1db0aae5d3e3139\",\n        \"0x2b53081c28517939f3f3c8dd4feaa4d05c3fc2247814278abe7444937d23b649\",\n        \"0x0435da43504bfe221f7408f38b0528a188e7534714de220af99504627dac267a\",\n        \"0x0db560c95c6a428448974d4d1930d8e92bca484d0d944c0b5912fea74d2d2d14\",\n        \"0x110f1d954c5579a04df83ad01a4cdaa4a0fe840ad909638d28f14d50fd9c688a\",\n        \"0x23037b83210a774ff6b6caa3093f1c3a075ecc20f46ba78ce33f71214797853d\",\n        \"0x003c96b2d19866aca8a6d299763cbfbe8d1e83063a24c1c64e2107bedc1d727e\",\n        \"0x232a5cd6b5a054cf30ce5975952e9154721c0719a8584d8a6f6a90e3e5f1adc1\",\n      },\n      {\n        \"0x263430d1d95ce2c8ee369a2d05b95564a3d00e58c54a6d45388a234afe13a9ce\",\n        \"0x01fa5bd8e153542b410e3798d747c3ea06bf35d3f41ba326b1248d7936584099\",\n        \"0x1030be57544d86a69777762fc9f8dd36a310c6aad17f74bf3c3631fc69d984c1\",\n        \"0x093a3759811b856dfe2aae65b68b80014b6ab208ee942a153e8f1119189a0fc1\",\n        \"0x06e6c64cffaf685871aa4c59dfe409c52dc6380286e234981afb5c36cf28acea\",\n        \"0x18904ab2392340c3ec6230ccd1dcad9ec130f5a7d25306f2dd42d396e0e67fd7\",\n        \"0x27eccd2de304de9dfef4d88b6f076a2c04937442e3e2c35b1f10d9c3fbb6325c\",\n        \"0x1a24d928cc3f0ba998fec5b9b7f4e2a19a1853b75e7628beea1bd799be7f701a\",\n      },\n    },\n    {\n      {\n        \"0x035cba522d844860cb05f47894a56cd1f6e2b3ec50ef96eab264d5f60b05a7f0\",\n        \"0x2fe274ca9f0e7482712083b53697c1bb312ba37685e29df08ed9d4145076b959\",\n        \"0x05ca447eb6d818c0df8ce00b2681700ca6e2f62e0e40f2eacef881b0ad16ceb0\",\n        \"0x0aa9b6011fdae0573e6ec99ccd133b5434d23511c52bf68667d7a71610f24c1b\",\n        \"0x028337e87c1e1d9833aa80d00571f46e0efe87447b73a7ed834dfc2c47e94e02\",\n        \"0x00d025d0d32d6870a245b5f51e7dde6918431e91efc76ae50ceb9cb37a61cef9\",\n        \"0x23ef42b9ae0dc781f8aa389a1388510de43df90c4ea1ed3d1d40811f4bca7015\",\n        \"0x10f2cf0392479514a4f7c14f39483c799889a1045c1dc993c5b4148334e6694e\",\n      },\n      {\n        \"0x035ad99713a98bea3f59b7dc1278c39718eed214329fccce30d70f78d6244815\",\n        \"0x1155282d4492fdcd980f6c8a4507b0050ce36e1368bcdb15af11c42c3fe8a785\",\n        \"0x248a66e7048c66a373945d6f4e57b2fd3cbb6324d606764c6f399edb5225ee36\",\n        \"0x0c39a645b4354e23d8cad1e435f1e67ea351f18552b95988a3d817e2d22c81d0\",\n        \"0x1237421269fe6491e84fbce500d23a57207bdd691ef35c94d378b97f82b16a3a\",\n        \"0x00593f27ddf3f01b11d7556a8c5d187f0f9818873ad6f41696525969e799a9d7\",\n        \"0x20cbdd95eda99ec21ccc683a973bd161416323be059c86187aae4f2d4399be9a\",\n        \"0x131eef59955630cccce68f7f4d4a6a77a4d3831144a2eac1797561c571e67236\",\n      },\n    }\n  };\n\n  constexpr static const std::string_view kShuffledTables[2][kW][kH] = {\n    {\n      {\n        \"0x23037b83210a774ff6b6caa3093f1c3a075ecc20f46ba78ce33f71214797853d\",\n        \"0x0435da43504bfe221f7408f38b0528a188e7534714de220af99504627dac267a\",\n        \"0x003c96b2d19866aca8a6d299763cbfbe8d1e83063a24c1c64e2107bedc1d727e\",\n        \"0x2b53081c28517939f3f3c8dd4feaa4d05c3fc2247814278abe7444937d23b649\",\n        \"0x232a5cd6b5a054cf30ce5975952e9154721c0719a8584d8a6f6a90e3e5f1adc1\",\n        \"0x110f1d954c5579a04df83ad01a4cdaa4a0fe840ad909638d28f14d50fd9c688a\",\n        \"0x0330fa29c0b79377aa26b9f89ad6c94912201b4c8a854c4fe1db0aae5d3e3139\",\n        \"0x0db560c95c6a428448974d4d1930d8e92bca484d0d944c0b5912fea74d2d2d14\",\n      },\n      {\n        \"0x18904ab2392340c3ec6230ccd1dcad9ec130f5a7d25306f2dd42d396e0e67fd7\",\n        \"0x1030be57544d86a69777762fc9f8dd36a310c6aad17f74bf3c3631fc69d984c1\",\n        \"0x27eccd2de304de9dfef4d88b6f076a2c04937442e3e2c35b1f10d9c3fbb6325c\",\n        \"0x01fa5bd8e153542b410e3798d747c3ea06bf35d3f41ba326b1248d7936584099\",\n        \"0x1a24d928cc3f0ba998fec5b9b7f4e2a19a1853b75e7628beea1bd799be7f701a\",\n        \"0x06e6c64cffaf685871aa4c59dfe409c52dc6380286e234981afb5c36cf28acea\",\n        \"0x263430d1d95ce2c8ee369a2d05b95564a3d00e58c54a6d45388a234afe13a9ce\",\n        \"0x093a3759811b856dfe2aae65b68b80014b6ab208ee942a153e8f1119189a0fc1\",\n      },\n    },\n    {\n      {\n        \"0x028337e87c1e1d9833aa80d00571f46e0efe87447b73a7ed834dfc2c47e94e02\",\n        \"0x05ca447eb6d818c0df8ce00b2681700ca6e2f62e0e40f2eacef881b0ad16ceb0\",\n        \"0x035cba522d844860cb05f47894a56cd1f6e2b3ec50ef96eab264d5f60b05a7f0\",\n        \"0x10f2cf0392479514a4f7c14f39483c799889a1045c1dc993c5b4148334e6694e\",\n        \"0x00d025d0d32d6870a245b5f51e7dde6918431e91efc76ae50ceb9cb37a61cef9\",\n        \"0x23ef42b9ae0dc781f8aa389a1388510de43df90c4ea1ed3d1d40811f4bca7015\",\n        \"0x0aa9b6011fdae0573e6ec99ccd133b5434d23511c52bf68667d7a71610f24c1b\",\n        \"0x2fe274ca9f0e7482712083b53697c1bb312ba37685e29df08ed9d4145076b959\",\n      },\n      {\n        \"0x1237421269fe6491e84fbce500d23a57207bdd691ef35c94d378b97f82b16a3a\",\n        \"0x248a66e7048c66a373945d6f4e57b2fd3cbb6324d606764c6f399edb5225ee36\",\n        \"0x035ad99713a98bea3f59b7dc1278c39718eed214329fccce30d70f78d6244815\",\n        \"0x131eef59955630cccce68f7f4d4a6a77a4d3831144a2eac1797561c571e67236\",\n        \"0x00593f27ddf3f01b11d7556a8c5d187f0f9818873ad6f41696525969e799a9d7\",\n        \"0x20cbdd95eda99ec21ccc683a973bd161416323be059c86187aae4f2d4399be9a\",\n        \"0x0c39a645b4354e23d8cad1e435f1e67ea351f18552b95988a3d817e2d22c81d0\",\n        \"0x1155282d4492fdcd980f6c8a4507b0050ce36e1368bcdb15af11c42c3fe8a785\",\n      },\n    },\n  };\n  // clang-format on\n\n  // clang-format off\n  constexpr static std::string_view kPinnedConstraintSystem =\n      \"PinnedConstraintSystem { \"\n        \"num_fixed_columns: 0, \"\n        \"num_advice_columns: 5, \"\n        \"num_instance_columns: 0, \"\n        \"num_selectors: 3, \"\n        \"num_challenges: 2, \"\n        \"advice_column_phase: [\"\n          \"Phase(0), \"\n          \"Phase(0), \"\n          \"Phase(0), \"\n          \"Phase(0), \"\n          \"Phase(1)\"\n        \"], \"\n        \"challenge_phase: [\"\n          \"Phase(0), \"\n          \"Phase(0)\"\n        \"], \"\n        \"gates: [\"\n          \"Product(\"\n            \"Selector(Selector(1, true)), \"\n            \"Sum(\"\n              \"Constant(0x0000000000000000000000000000000000000000000000000000000000000001), \"\n              \"Negated(Advice { \"\n                \"query_index: 0, \"\n                \"column_index: 4, \"\n                \"rotation: Rotation(0), \"\n                \"phase: Phase(1) \"\n              \"})\"\n            \")\"\n          \"), \"\n          \"Product(\"\n            \"Selector(Selector(2, true)), \"\n            \"Sum(\"\n              \"Constant(0x0000000000000000000000000000000000000000000000000000000000000001), \"\n              \"Negated(Advice { \"\n                \"query_index: 0, \"\n                \"column_index: 4, \"\n                \"rotation: Rotation(0), \"\n                \"phase: Phase(1) \"\n              \"})\"\n            \")\"\n          \"), \"\n          \"Product(\"\n            \"Selector(Selector(0, true)), \"\n            \"Sum(\"\n              \"Product(\"\n                \"Advice { \"\n                  \"query_index: 0, \"\n                  \"column_index: 4, \"\n                  \"rotation: Rotation(0), \"\n                  \"phase: Phase(1) \"\n                \"}, \"\n                \"Sum(\"\n                  \"Sum(\"\n                    \"Product(\"\n                      \"Advice { \"\n                        \"query_index: 1, \"\n                        \"column_index: 0, \"\n                        \"rotation: Rotation(0) \"\n                      \"}, \"\n                      \"Challenge(Challenge { \"\n                        \"index: 0, \"\n                        \"phase: Phase(0) \"\n                      \"})\"\n                    \"), \"\n                    \"Advice { \"\n                      \"query_index: 2, \"\n                      \"column_index: 1, \"\n                      \"rotation: Rotation(0) \"\n                    \"}\"\n                  \"), \"\n                  \"Challenge(Challenge { \"\n                    \"index: 1, \"\n                    \"phase: Phase(0) \"\n                  \"})\"\n                \")\"\n              \"), \"\n              \"Negated(Product(\"\n                \"Advice { \"\n                  \"query_index: 5, \"\n                  \"column_index: 4, \"\n                  \"rotation: Rotation(1), \"\n                  \"phase: Phase(1) \"\n                \"}, \"\n                \"Sum(\"\n                  \"Sum(\"\n                    \"Product(\"\n                      \"Advice { \"\n                        \"query_index: 3, \"\n                        \"column_index: 2, \"\n                        \"rotation: Rotation(0) \"\n                      \"}, \"\n                      \"Challenge(Challenge { \"\n                        \"index: 0, \"\n                        \"phase: Phase(0) \"\n                      \"})\"\n                    \"), \"\n                    \"Advice { \"\n                      \"query_index: 4, \"\n                      \"column_index: 3, \"\n                      \"rotation: Rotation(0) \"\n                    \"}\"\n                  \"), \"\n                  \"Challenge(Challenge { \"\n                    \"index: 1, \"\n                    \"phase: Phase(0) \"\n                  \"})\"\n                \")\"\n              \"))\"\n            \")\"\n          \")\"\n        \"], \"\n        \"advice_queries: [\"\n          \"(\"\n            \"Column { \"\n              \"index: 4, \"\n              \"column_type: Advice { \"\n                \"phase: Phase(1) \"\n              \"} \"\n            \"}, \"\n            \"Rotation(0)\"\n          \"), \"\n          \"(\"\n            \"Column { \"\n              \"index: 0, \"\n              \"column_type: Advice \"\n            \"}, \"\n            \"Rotation(0)\"\n          \"), \"\n          \"(\"\n            \"Column { \"\n              \"index: 1, \"\n              \"column_type: Advice \"\n            \"}, \"\n            \"Rotation(0)\"\n          \"), \"\n          \"(\"\n            \"Column { \"\n              \"index: 2, \"\n              \"column_type: Advice \"\n            \"}, \"\n            \"Rotation(0)\"\n          \"), \"\n          \"(\"\n            \"Column { \"\n              \"index: 3, \"\n              \"column_type: Advice \"\n            \"}, \"\n            \"Rotation(0)\"\n          \"), \"\n          \"(\"\n            \"Column { \"\n              \"index: 4, \"\n              \"column_type: Advice { \"\n                \"phase: Phase(1) \"\n              \"} \"\n            \"}, \"\n            \"Rotation(1)\"\n          \")\"\n        \"], \"\n        \"instance_queries: [], \"\n        \"fixed_queries: [], \"\n        \"permutation: Argument { \"\n          \"columns: [] \"\n        \"}, \"\n        \"lookups: [], \"\n        \"constants: [], \"\n        \"minimum_degree: None \"\n      \"}\";\n  // clang-format on\n\n  // clang-format off\n  constexpr static bool kSelectors[][kN] = {\n      {true, true, true, true, true, true, true, true,\n        false, false, false, false, false, false, false, false},\n      {true, false, false, false, false, false, false, false,\n        false, false, false, false, false, false, false, false},\n      {false, false, false, false, false, false, false, false,\n        true, false, false, false, false, false, false, false},\n  };\n  // clang-format on\n\n  // clang-format off\n  constexpr static std::string_view kPinnedVerifyingKey =\n      \"PinnedVerificationKey { \"\n        \"base_modulus: \\\"0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47\\\", \"\n        \"scalar_modulus: \\\"0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001\\\", \"\n        \"domain: PinnedEvaluationDomain { \"\n          \"k: 4, \"\n          \"extended_k: 5, \"\n          \"omega: 0x21082ca216cbbf4e1c6e4f4594dd508c996dfbe1174efb98b11509c6e306460b \"\n        \"}, \"\n        \"cs: PinnedConstraintSystem { \"\n          \"num_fixed_columns: 2, \"\n          \"num_advice_columns: 5, \"\n          \"num_instance_columns: 0, \"\n          \"num_selectors: 3, \"\n          \"num_challenges: 2, \"\n          \"advice_column_phase: [\"\n            \"Phase(0), \"\n            \"Phase(0), \"\n            \"Phase(0), \"\n            \"Phase(0), \"\n            \"Phase(1)\"\n          \"], \"\n          \"challenge_phase: [\"\n            \"Phase(0), \"\n            \"Phase(0)\"\n          \"], \"\n          \"gates: [\"\n            \"Product(\"\n              \"Product(\"\n                \"Fixed { \"\n                  \"query_index: 1, \"\n                  \"column_index: 1, \"\n                  \"rotation: Rotation(0) \"\n                \"}, \"\n                \"Sum(\"\n                  \"Constant(0x0000000000000000000000000000000000000000000000000000000000000002), \"\n                  \"Negated(Fixed { \"\n                    \"query_index: 1, \"\n                    \"column_index: 1, \"\n                    \"rotation: Rotation(0) \"\n                  \"})\"\n                \")\"\n              \"), \"\n              \"Sum(\"\n                \"Constant(0x0000000000000000000000000000000000000000000000000000000000000001), \"\n                \"Negated(Advice { \"\n                  \"query_index: 0, \"\n                  \"column_index: 4, \"\n                  \"rotation: Rotation(0), \"\n                  \"phase: Phase(1) \"\n                \"})\"\n              \")\"\n            \"), \"\n            \"Product(\"\n              \"Product(\"\n                \"Fixed { \"\n                  \"query_index: 1, \"\n                  \"column_index: 1, \"\n                  \"rotation: Rotation(0) \"\n                \"}, \"\n                \"Sum(\"\n                  \"Constant(0x0000000000000000000000000000000000000000000000000000000000000001), \"\n                  \"Negated(Fixed { \"\n                    \"query_index: 1, \"\n                    \"column_index: 1, \"\n                    \"rotation: Rotation(0) \"\n                  \"})\"\n                \")\"\n              \"), \"\n              \"Sum(\"\n                \"Constant(0x0000000000000000000000000000000000000000000000000000000000000001), \"\n                \"Negated(Advice { \"\n                  \"query_index: 0, \"\n                  \"column_index: 4, \"\n                  \"rotation: Rotation(0), \"\n                  \"phase: Phase(1) \"\n                \"})\"\n              \")\"\n            \"), \"\n            \"Product(\"\n              \"Fixed { \"\n                \"query_index: 0, \"\n                \"column_index: 0, \"\n                \"rotation: Rotation(0) \"\n              \"}, \"\n              \"Sum(\"\n                \"Product(\"\n                  \"Advice { \"\n                    \"query_index: 0, \"\n                    \"column_index: 4, \"\n                    \"rotation: Rotation(0), \"\n                    \"phase: Phase(1) \"\n                  \"}, \"\n                  \"Sum(\"\n                    \"Sum(\"\n                      \"Product(\"\n                        \"Advice { \"\n                          \"query_index: 1, \"\n                          \"column_index: 0, \"\n                          \"rotation: Rotation(0) \"\n                        \"}, \"\n                        \"Challenge(Challenge { \"\n                          \"index: 0, \"\n                          \"phase: Phase(0) \"\n                        \"})\"\n                      \"), \"\n                      \"Advice { \"\n                        \"query_index: 2, \"\n                        \"column_index: 1, \"\n                        \"rotation: Rotation(0) \"\n                      \"}\"\n                    \"), \"\n                    \"Challenge(Challenge { \"\n                      \"index: 1, \"\n                      \"phase: Phase(0) \"\n                    \"})\"\n                  \")\"\n                \"), \"\n                \"Negated(Product(\"\n                  \"Advice { \"\n                    \"query_index: 5, \"\n                    \"column_index: 4, \"\n                    \"rotation: Rotation(1), \"\n                    \"phase: Phase(1) \"\n                  \"}, \"\n                  \"Sum(\"\n                    \"Sum(\"\n                      \"Product(\"\n                        \"Advice { \"\n                          \"query_index: 3, \"\n                          \"column_index: 2, \"\n                          \"rotation: Rotation(0) \"\n                        \"}, \"\n                        \"Challenge(Challenge { \"\n                          \"index: 0, \"\n                          \"phase: Phase(0) \"\n                        \"})\"\n                      \"), \"\n                      \"Advice { \"\n                        \"query_index: 4, \"\n                        \"column_index: 3, \"\n                        \"rotation: Rotation(0) \"\n                      \"}\"\n                    \"), \"\n                    \"Challenge(Challenge { \"\n                      \"index: 1, \"\n                      \"phase: Phase(0) \"\n                    \"})\"\n                  \")\"\n                \"))\"\n              \")\"\n            \")\"\n          \"], \"\n          \"advice_queries: [\"\n            \"(\"\n              \"Column { \"\n                \"index: 4, \"\n                \"column_type: Advice { \"\n                  \"phase: Phase(1) \"\n                \"} \"\n              \"}, \"\n              \"Rotation(0)\"\n            \"), \"\n            \"(\"\n              \"Column { \"\n                \"index: 0, \"\n                \"column_type: Advice \"\n              \"}, \"\n              \"Rotation(0)\"\n            \"), \"\n            \"(\"\n              \"Column { \"\n                \"index: 1, \"\n                \"column_type: Advice \"\n              \"}, \"\n              \"Rotation(0)\"\n            \"), \"\n            \"(\"\n              \"Column { \"\n                \"index: 2, \"\n                \"column_type: Advice \"\n              \"}, \"\n              \"Rotation(0)\"\n            \"), \"\n            \"(\"\n              \"Column { \"\n                \"index: 3, \"\n                \"column_type: Advice \"\n              \"}, \"\n              \"Rotation(0)\"\n            \"), \"\n            \"(\"\n              \"Column { \"\n                \"index: 4, \"\n                \"column_type: Advice { \"\n                  \"phase: Phase(1) \"\n                \"} \"\n              \"}, \"\n              \"Rotation(1)\"\n            \")\"\n          \"], \"\n          \"instance_queries: [], \"\n          \"fixed_queries: [\"\n            \"(\"\n              \"Column { \"\n                \"index: 0, \"\n                \"column_type: Fixed \"\n              \"}, \"\n              \"Rotation(0)\"\n            \"), \"\n            \"(\"\n              \"Column { \"\n                \"index: 1, \"\n                \"column_type: Fixed \"\n              \"}, \"\n              \"Rotation(0)\"\n            \")\"\n          \"], \"\n          \"permutation: Argument { \"\n            \"columns: [] \"\n          \"}, \"\n          \"lookups: [], \"\n          \"constants: [], \"\n          \"minimum_degree: None \"\n        \"}, \"\n        \"fixed_commitments: [\"\n          \"(0x297ff8a661d1fa1196c065b6fb7df901fb16b5b83168ddca6c7749d963cc967a, \"\n            \"0x1a7b1f2b5f3e35fc4c706ece6b8f646e95904f9aa417f8a31fd37a41da167ec1), \"\n          \"(0x1025d577f7527c7c9a5d132164beef9ec3ebc63805b146843466f6cdf5fb37d4, \"\n            \"0x1ba406f864ec730f7f725b3478ce1e22c5579b6228593c9ba36e9cb18a10aab3)\"\n        \"], \"\n        \"permutation: VerifyingKey { \"\n          \"commitments: [] \"\n        \"} \"\n      \"}\";\n  // clang-format on\n\n  constexpr static uint8_t kProof[] = {\n      122, 117, 229, 163, 114, 52,  159, 223, 87,  191, 210, 4,   56,  250,\n      59,  18,  206, 131, 78,  129, 60,  202, 130, 174, 203, 80,  0,   221,\n      190, 150, 199, 170, 97,  250, 239, 195, 165, 188, 251, 230, 129, 105,\n      212, 209, 41,  124, 75,  202, 166, 142, 57,  16,  160, 164, 175, 119,\n      80,  96,  210, 1,   220, 55,  229, 150, 176, 0,   127, 136, 63,  27,\n      146, 170, 55,  152, 223, 250, 131, 165, 92,  170, 126, 214, 34,  187,\n      172, 236, 248, 250, 195, 21,  121, 118, 69,  108, 167, 132, 242, 42,\n      222, 129, 23,  144, 206, 50,  2,   128, 3,   44,  222, 217, 223, 35,\n      228, 184, 246, 76,  34,  146, 170, 5,   127, 243, 140, 134, 59,  224,\n      63,  139, 97,  91,  176, 229, 208, 22,  81,  76,  102, 212, 230, 42,\n      248, 115, 0,   57,  161, 155, 76,  46,  222, 63,  50,  191, 169, 117,\n      109, 77,  100, 113, 115, 163, 219, 109, 33,  128, 52,  174, 106, 188,\n      197, 159, 82,  64,  13,  77,  214, 88,  94,  3,   59,  239, 242, 15,\n      57,  52,  184, 96,  8,   195, 219, 8,   28,  165, 23,  155, 225, 52,\n      2,   100, 158, 139, 3,   255, 216, 141, 144, 92,  148, 43,  245, 117,\n      105, 244, 203, 45,  169, 199, 189, 108, 165, 254, 123, 192, 88,  166,\n      183, 219, 170, 20,  145, 67,  54,  188, 84,  208, 144, 229, 73,  102,\n      239, 199, 192, 67,  84,  125, 77,  150, 208, 152, 34,  245, 28,  245,\n      53,  241, 240, 142, 238, 116, 44,  171, 9,   62,  209, 106, 14,  52,\n      209, 60,  194, 175, 33,  157, 149, 131, 25,  211, 81,  22,  167, 97,\n      84,  92,  7,   120, 171, 195, 156, 29,  78,  254, 1,   92,  9,   249,\n      182, 255, 222, 133, 161, 215, 117, 56,  201, 12,  65,  236, 138, 136,\n      207, 214, 29,  1,   218, 176, 164, 120, 50,  120, 130, 42,  1,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   96,  134, 180, 185, 62,  13,  187, 73,  198, 194, 240, 58,\n      143, 101, 182, 194, 201, 129, 242, 182, 19,  7,   12,  75,  34,  92,\n      65,  156, 85,  241, 183, 171, 45,  201, 146, 14,  118, 58,  117, 27,\n      61,  108, 249, 137, 144, 71,  115, 19,  73,  98,  4,   77,  237, 52,\n      69,  171, 85,  201, 246, 215, 91,  71,  145, 175, 139, 126, 48,  67,\n      66,  130, 140, 51,  253, 115, 220, 250, 206, 22,  138, 113, 63,  0,\n      74,  233, 167, 144, 210, 243, 212, 44,  42,  58,  221, 5,   215, 30,\n      94,  146, 3,   134, 1,   201, 167, 90,  132, 197, 56,  187, 221, 23,\n      242, 164, 87,  157, 142, 157, 80,  235, 12,  136, 21,  115, 181, 19,\n      96,  199, 209, 5,   135, 209, 93,  98,  16,  78,  106, 217, 160, 233,\n      35,  255, 207, 251, 154, 71,  20,  253, 239, 202, 139, 237, 246, 171,\n      137, 255, 94,  92,  53,  190, 236, 34,  153, 4,   94,  63,  214, 40,\n      181, 15,  147, 150, 81,  110, 181, 99,  38,  34,  56,  239, 28,  121,\n      151, 11,  251, 82,  30,  105, 132, 33,  69,  126, 142, 15,  157, 153,\n      218, 141, 253, 210, 254, 249, 230, 130, 27,  49,  161, 88,  161, 222,\n      147, 49,  245, 92,  45,  16,  131, 95,  217, 194, 215, 16,  69,  71,\n      78,  29,  108, 202, 1,   68,  66,  44,  39,  172, 207, 144, 77,  121,\n      120, 114, 211, 147, 52,  200, 152, 197, 161, 235, 76,  139, 115, 141,\n      206, 208, 134, 251, 78,  15,  16,  93,  166, 218, 134, 85,  60,  230,\n      36,  206, 42,  75,  206, 2,   145, 43,  18,  145, 135, 107, 152, 229,\n      5,   62,  206, 252, 129, 232, 114, 75,  228, 33,  140, 218, 27,  139,\n      246, 80,  127, 36,  80,  115, 177, 219, 69,  80,  235, 123, 191, 251,\n      189, 206, 103, 248, 242, 58,  173, 185, 101, 112, 138, 176, 235, 34,\n      81,  60,  31,  132, 241, 220, 231, 212, 135, 74,  159, 124, 115, 227,\n      149, 192, 56,  152, 43,  182, 158, 246, 63,  125, 148, 144, 243, 217,\n      66,  119, 14,  38,  238, 129, 238, 25,  126, 177, 4,   135, 84,  255,\n      216, 141, 221, 137, 172, 22,  182, 33,  217, 8,   95,  92,  208, 83,\n      226, 5,   67,  65,  218, 137, 253, 12,  231, 216, 174, 122, 162, 239,\n      88,  16,  65,  125, 101, 123, 96,  188, 118, 164, 68,  145, 203, 129,\n      54,  95,  84,  4,   83,  98,  53,  87,  145, 106, 223, 26,  146, 169,\n      158, 159, 209, 157, 89,  102, 204, 254, 97,  92,  149, 104, 21,  94,\n      238, 231, 74,  192, 45,  11,  167, 112, 183, 54,  189, 61,  176, 204,\n      151, 2,   216, 147, 249, 148, 195, 213, 197, 49,  184, 7,   174, 45,\n      175, 251, 144, 126, 180, 32,  145, 186, 94,  84,  170, 71,  228, 213,\n      109, 89,  141, 161, 164, 38,  204, 163, 86,  138, 200, 222, 184, 232,\n      163, 254, 172, 253, 164, 60,  8,   91,  31,  113, 46,  55,  14,  133,\n      120, 246, 172, 208, 57,  35,  123, 179, 212, 32,  1,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      209, 212, 200, 125, 223, 94,  117, 108, 135, 235, 223, 52,  22,  57,\n      192, 220, 171, 220, 21,  114, 181, 209, 172, 136, 84,  166, 153, 21,\n      191, 31,  241, 162, 67,  132, 199, 67,  95,  218, 218, 119, 67,  127,\n      7,   147, 173, 104, 127, 197, 127, 234, 110, 49,  115, 143, 218, 23,\n      132, 5,   35,  0,   217, 55,  177, 128};\n\n  static void TestConfig(ShuffleCircuitConfig<F, kW>& config) {\n    EXPECT_EQ(config.q_shuffle(), Selector::Simple(0));\n    EXPECT_EQ(config.q_first(), Selector::Simple(1));\n    EXPECT_EQ(config.q_last(), Selector::Simple(2));\n    for (size_t i = 0; i < kW; i++) {\n      EXPECT_EQ(config.original_column_keys()[i], AdviceColumnKey(i));\n      EXPECT_EQ(config.shuffled_column_keys()[i], AdviceColumnKey(kW + i));\n    }\n    EXPECT_EQ(config.theta(), Challenge(0, kFirstPhase));\n    EXPECT_EQ(config.gamma(), Challenge(1, kFirstPhase));\n    EXPECT_EQ(config.z(), AdviceColumnKey(2 * kW, kSecondPhase));\n  }\n\n  static Circuit GetCircuit(size_t i = 0) {\n    CHECK_LT(i, std::size(kOriginalTables));\n    return {CreateTable(kOriginalTables[i]), CreateTable(kShuffledTables[i])};\n  }\n\n  static std::vector<Circuit> Get2Circuits() {\n    return {GetCircuit(0), GetCircuit(1)};\n  }\n\n private:\n  static std::vector<std::vector<F>> CreateTable(\n      const std::string_view table[kW][kH]) {\n    return base::CreateVector(kW, [table](size_t i) {\n      return base::CreateVector(\n          kH, [table, i](size_t j) { return *F::FromHexString(table[i][j]); });\n    });\n  }\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_EXAMPLES_SHUFFLE_CIRCUIT_TEST_DATA_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/examples/simple_circuit.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n// This is taken and modified from\n// https://github.com/privacy-scaling-explorations/halo2/blob/bc857a7/halo2_proofs/examples/simple-example.rs.\n\n#ifndef TACHYON_ZK_PLONK_EXAMPLES_SIMPLE_CIRCUIT_H_\n#define TACHYON_ZK_PLONK_EXAMPLES_SIMPLE_CIRCUIT_H_\n\n#include <array>\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/zk/plonk/constraint_system/circuit.h\"\n\nnamespace tachyon::zk::plonk {\n\n// Chip state is stored in a config struct. This is generated by the chip\n// during configuration, and then stored inside the chip.\ntemplate <typename F>\nclass FieldConfig {\n public:\n  using Field = F;\n\n  FieldConfig() = default;\n  FieldConfig(std::array<AdviceColumnKey, 2>&& advice,\n              const InstanceColumnKey& instance, Selector s_mul)\n      : advice_(std::move(advice)), instance_(instance), s_mul_(s_mul) {}\n\n  FieldConfig Clone() const {\n    std::array<AdviceColumnKey, 2> advice_clone = advice_;\n    return FieldConfig(std::move(advice_clone), instance_, s_mul_);\n  }\n\n  const std::array<AdviceColumnKey, 2>& advice() const { return advice_; }\n  const InstanceColumnKey& instance() const { return instance_; }\n  Selector s_mul() const { return s_mul_; }\n\n private:\n  // For this chip, we will use two advice columns to implement our\n  // instructions. These are also the columns through which we communicate with\n  // other parts of the circuit.\n  std::array<AdviceColumnKey, 2> advice_;\n  // This is the public input (instance) column.\n  InstanceColumnKey instance_;\n  // We need a selector to enable the multiplication gate, so that we aren't\n  // placing any constraints on cells where |FieldChip::Mul| is not\n  // being used. This is important when building larger circuits, where columns\n  // are used by multiple sets of instructions.\n  Selector s_mul_;\n};\n\ntemplate <typename F>\nclass FieldChip {\n public:\n  explicit FieldChip(FieldConfig<F>&& config) : config_(std::move(config)) {}\n\n  static FieldConfig<F> Configure(ConstraintSystem<F>& meta,\n                                  std::array<AdviceColumnKey, 2>&& advice,\n                                  const InstanceColumnKey& instance,\n                                  const FixedColumnKey& constant) {\n    meta.EnableEquality(instance);\n    meta.EnableConstant(constant);\n    for (const AdviceColumnKey& column : advice) {\n      meta.EnableEquality(column);\n    }\n    Selector sel = meta.CreateSimpleSelector();\n\n    // Define our multiplication gate!\n    meta.CreateGate(\"mul\", [&advice, &sel](VirtualCells<F>& meta) {\n      // To implement multiplication, we need three advice cells and a selector\n      // cell. We arrange them like so:\n      //\n      // | a0  | a1  | sel   |\n      // |-----|-----|-------|\n      // | lhs | rhs | s_mul |\n      // | out |     |       |\n      //\n      // Gates may refer to any relative offsets we want, but each distinct\n      // offset adds a cost to the proof. The most common offsets are 0 (the\n      // current row), 1 (the next row), and -1 (the previous row), for which\n      // |Rotation| has specific constructors.\n      std::unique_ptr<Expression<F>> lhs =\n          meta.QueryAdvice(advice[0], Rotation::Cur());\n      std::unique_ptr<Expression<F>> rhs =\n          meta.QueryAdvice(advice[1], Rotation::Cur());\n      std::unique_ptr<Expression<F>> out =\n          meta.QueryAdvice(advice[0], Rotation::Next());\n      std::unique_ptr<Expression<F>> s_mul = meta.QuerySelector(sel);\n\n      // Finally, we return the polynomial expressions that constrain this gate.\n      // For our multiplication gate, we only need a single polynomial\n      // constraint.\n      //\n      // The polynomial expressions returned from |CreateGate()| will be\n      // constrained by the proving system to equal zero. Our expression\n      // has the following properties:\n      // - When s_mul = 0, any value is allowed in lhs, rhs, and out.\n      // - When s_mul != 0, this constrains lhs * rhs = out.\n      std::vector<Constraint<F>> constraints;\n      constraints.emplace_back(\n          std::move(s_mul) *\n          (std::move(lhs) * std::move(rhs) - std::move(out)));\n      return constraints;\n    });\n\n    return FieldConfig<F>(std::move(advice), instance, sel);\n  }\n\n  AssignedCell<F> LoadPrivate(Layouter<F>* layouter,\n                              const Value<F>& value) const {\n    AssignedCell<F> ret;\n    layouter->AssignRegion(\n        \"load private\", [this, &value, &ret](Region<F>& region) {\n          ret = region.AssignAdvice(\"private input\", config_.advice()[0], 0,\n                                    [&value]() { return value; });\n        });\n    return ret;\n  }\n\n  AssignedCell<F> LoadConstant(Layouter<F>* layouter, const F& constant) const {\n    AssignedCell<F> ret;\n    layouter->AssignRegion(\n        \"load constant\", [this, &constant, &ret](Region<F>& region) {\n          ret = region.AssignAdviceFromConstant(\n              \"constant value\", config_.advice()[0], 0, constant);\n        });\n    return ret;\n  }\n\n  AssignedCell<F> Mul(Layouter<F>* layouter, const AssignedCell<F>& a,\n                      const AssignedCell<F>& b) const {\n    AssignedCell<F> ret;\n    layouter->AssignRegion(\"mul\", [this, a, b, &ret](Region<F>& region) {\n      // We only want to use a single multiplication gate in this |region|,\n      // so we enable it at |region| offset 0; this means it will constrain\n      // cells at offsets 0 and 1.\n      config_.s_mul().Enable(region, 0);\n\n      // The inputs we've been given could be located anywhere in the\n      // circuit, but we can only rely on relative offsets inside this\n      // region. So we assign new cells inside the |region| and constrain\n      // them to have the same values as the inputs.\n      a.CopyAdvice(\"lhs\", region, config_.advice()[0], 0);\n      b.CopyAdvice(\"rhs\", region, config_.advice()[1], 0);\n\n      // Now we can assign the multiplication result, which is to be\n      // assigned into the output position.\n      Value<F> value = a.value() * b.value();\n\n      // Finally, we do the assignment to the output, returning a\n      // variable to be used in another part of the circuit.\n      ret = region.AssignAdvice(\"lhs * rhs\", config_.advice()[0], 1,\n                                [&value]() { return value; });\n    });\n    return ret;\n  }\n\n  void ExposePublic(Layouter<F>* layouter, const AssignedCell<F>& cell,\n                    RowIndex row) const {\n    layouter->ConstrainInstance(cell.cell(), config_.instance(), row);\n  }\n\n private:\n  FieldConfig<F> config_;\n};\n\ntemplate <typename F, template <typename> class _FloorPlanner>\nclass SimpleCircuit : public Circuit<FieldConfig<F>> {\n public:\n  using FloorPlanner = _FloorPlanner<SimpleCircuit<F, _FloorPlanner>>;\n\n  SimpleCircuit() = default;\n  SimpleCircuit(const F& constant, const F& a, const F& b)\n      : constant_(constant), a_(Value<F>::Known(a)), b_(Value<F>::Known(b)) {}\n\n  std::unique_ptr<Circuit<FieldConfig<F>>> WithoutWitness() const override {\n    return std::make_unique<SimpleCircuit>();\n  }\n\n  static FieldConfig<F> Configure(ConstraintSystem<F>& meta) {\n    // We create the two advice columns that FieldChip uses for I/O.\n    std::array<AdviceColumnKey, 2> advice(\n        {meta.CreateAdviceColumn(), meta.CreateAdviceColumn()});\n\n    // We also need an instance column to store public inputs.\n    InstanceColumnKey instance = meta.CreateInstanceColumn();\n\n    // Create a fixed column to load constants.\n    FixedColumnKey constant = meta.CreateFixedColumn();\n\n    return FieldChip<F>::Configure(meta, std::move(advice), instance, constant);\n  }\n\n  void Synthesize(FieldConfig<F>&& config,\n                  Layouter<F>* layouter) const override {\n    FieldChip<F> field_chip(std::move(config));\n\n    // Load our private values into the circuit.\n    AssignedCell<F> a =\n        field_chip.LoadPrivate(layouter->Namespace(\"load a\").get(), a_);\n    AssignedCell<F> b =\n        field_chip.LoadPrivate(layouter->Namespace(\"load b\").get(), b_);\n\n    // Load the constant factor into the circuit.\n    AssignedCell<F> constant = field_chip.LoadConstant(\n        layouter->Namespace(\"load constant\").get(), constant_);\n\n    // We only have access to plain multiplication.\n    // We could implement our circuit as:\n    //     asq  = a²\n    //     bsq  = b²\n    //     absq = asq * bsq\n    //     c    = constant * asq * bsq\n    //\n    // but it's more efficient to implement it as:\n    //     ab   = a * b\n    //     absq = (ab)²\n    //     c    = constant * absq\n    AssignedCell<F> ab =\n        field_chip.Mul(layouter->Namespace(\"a * b\").get(), a, b);\n    AssignedCell<F> absq =\n        field_chip.Mul(layouter->Namespace(\"ab * ab\").get(), ab, ab);\n    AssignedCell<F> c = field_chip.Mul(\n        layouter->Namespace(\"constant * absq\").get(), constant, absq);\n\n    // Expose the result as a public input to the circuit.\n    field_chip.ExposePublic(layouter->Namespace(\"expose c\").get(), c, 0);\n  }\n\n private:\n  F constant_;\n  Value<F> a_;\n  Value<F> b_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_EXAMPLES_SIMPLE_CIRCUIT_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/examples/simple_circuit_test.cc",
    "content": "#include \"tachyon/zk/plonk/examples/simple_circuit.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/zk/plonk/examples/circuit_test.h\"\n#include \"tachyon/zk/plonk/examples/simple_circuit_test_data.h\"\n#include \"tachyon/zk/plonk/layout/floor_planner/simple_floor_planner.h\"\n#include \"tachyon/zk/plonk/layout/floor_planner/v1/v1_floor_planner.h\"\n\nnamespace tachyon::zk::plonk {\n\nnamespace {\n\ntemplate <typename TestArguments>\nclass SimpleCircuitTest\n    : public CircuitTest<TestArguments,\n                         SimpleTestData<typename TestArguments::Circuit,\n                                        typename TestArguments::PS>> {};\n\n}  // namespace\n\nusing SimpleTestArgumentsList = testing::Types<\n    TestArguments<SimpleCircuit<BN254SHPlonk::Field, SimpleFloorPlanner>,\n                  BN254SHPlonkHalo2>,\n    TestArguments<SimpleCircuit<BN254SHPlonk::Field, V1FloorPlanner>,\n                  BN254SHPlonkHalo2>>;\n\nTYPED_TEST_SUITE(SimpleCircuitTest, SimpleTestArgumentsList);\n\nTYPED_TEST(SimpleCircuitTest, Configure) { this->ConfigureTest(); }\nTYPED_TEST(SimpleCircuitTest, Synthesize) { this->SynthesizeTest(); }\nTYPED_TEST(SimpleCircuitTest, LoadVerifyingKey) {\n  this->LoadVerifyingKeyTest();\n}\nTYPED_TEST(SimpleCircuitTest, LoadProvingKey) { this->LoadProvingKeyTest(); }\nTYPED_TEST(SimpleCircuitTest, CreateProof) { this->CreateProofTest(); }\nTYPED_TEST(SimpleCircuitTest, VerifyProof) { this->VerifyProofTest(); }\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/examples/simple_circuit_test_data.h",
    "content": "#ifndef TACHYON_ZK_PLONK_EXAMPLES_SIMPLE_CIRCUIT_TEST_DATA_H_\n#define TACHYON_ZK_PLONK_EXAMPLES_SIMPLE_CIRCUIT_TEST_DATA_H_\n\n#include <stdint.h>\n\n#include <string_view>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/zk/plonk/examples/circuit_test_data.h\"\n#include \"tachyon/zk/plonk/examples/circuit_test_type_traits.h\"\n#include \"tachyon/zk/plonk/examples/point.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename Circuit, typename PS, typename SFINAE = void>\nclass SimpleTestData : public CircuitTestData<Circuit, PS> {};\n\n// FloorPlanner = SimpleFloorPlanner\ntemplate <typename Circuit, typename PS>\nclass SimpleTestData<Circuit, PS,\n                     std::enable_if_t<IsSimpleFloorPlanner<Circuit>>>\n    : public CircuitTestData<Circuit, PS> {\n public:\n  using PCS = typename PS::PCS;\n  using F = typename PCS::Field;\n  using Evals = typename PCS::Evals;\n\n  // Set flags of values to be used as true\n  constexpr static bool kAssemblyFixedColumnsFlag = true;\n  constexpr static bool kAssemblyPermutationColumnsFlag = true;\n  constexpr static bool kCycleStoreMappingFlag = true;\n  constexpr static bool kCycleStoreAuxFlag = true;\n  constexpr static bool kCycleStoreSizesFlag = true;\n  constexpr static bool kLFirstFlag = true;\n  constexpr static bool kLLastFlag = true;\n  constexpr static bool kLActiveRowFlag = true;\n  constexpr static bool kFixedColumnsFlag = true;\n  constexpr static bool kFixedPolysFlag = true;\n  constexpr static bool kPermutationsColumnsFlag = true;\n  constexpr static bool kPermutationsPolysFlag = true;\n  constexpr static bool kAdviceCommitmentsFlag = true;\n  constexpr static bool kPermutationProductCommitmentsFlag = true;\n  constexpr static bool kAdviceEvalsFlag = true;\n  constexpr static bool kFixedEvalsFlag = true;\n  constexpr static bool kCommonPermutationEvalsFlag = true;\n  constexpr static bool kPermutationProductEvalsFlag = true;\n  constexpr static bool kPermutationProductNextEvalsFlag = true;\n  constexpr static bool kPermutationProductLastEvalsFlag = true;\n\n  constexpr static size_t kN = 16;\n\n  // clang-format off\n  constexpr static std::string_view kPinnedConstraintSystem =\n      \"PinnedConstraintSystem { \"\n        \"num_fixed_columns: 1, \"\n        \"num_advice_columns: 2, \"\n        \"num_instance_columns: 1, \"\n        \"num_selectors: 1, \"\n        \"gates: [Product(\"\n          \"Selector(Selector(0, true)), \"\n          \"Sum(\"\n            \"Product(\"\n              \"Advice { \"\n                \"query_index: 0, \"\n                \"column_index: 0, \"\n                \"rotation: Rotation(0) \"\n              \"}, \"\n              \"Advice { \"\n                \"query_index: 1, \"\n                \"column_index: 1, \"\n                \"rotation: Rotation(0) \"\n              \"}\"\n            \"), \"\n            \"Negated(Advice { \"\n              \"query_index: 2, \"\n              \"column_index: 0, \"\n              \"rotation: Rotation(1) \"\n            \"})\"\n          \")\"\n        \")], \"\n        \"advice_queries: [\"\n          \"(\"\n            \"Column { \"\n              \"index: 0, \"\n              \"column_type: Advice \"\n            \"}, \"\n            \"Rotation(0)\"\n          \"), \"\n          \"(\"\n            \"Column { \"\n              \"index: 1, \"\n              \"column_type: Advice \"\n            \"}, \"\n            \"Rotation(0)\"\n          \"), \"\n          \"(\"\n            \"Column { \"\n              \"index: 0, \"\n              \"column_type: Advice \"\n            \"}, \"\n            \"Rotation(1)\"\n          \")\"\n        \"], \"\n        \"instance_queries: [(\"\n          \"Column { \"\n            \"index: 0, \"\n            \"column_type: Instance \"\n          \"}, \"\n          \"Rotation(0)\"\n        \")], \"\n        \"fixed_queries: [(\"\n          \"Column { \"\n            \"index: 0, \"\n            \"column_type: Fixed \"\n          \"}, \"\n          \"Rotation(0)\"\n        \")], \"\n        \"permutation: Argument { columns: [\"\n          \"Column { \"\n            \"index: 0, \"\n            \"column_type: Instance \"\n          \"}, \"\n          \"Column { \"\n            \"index: 0, \"\n            \"column_type: Fixed \"\n          \"}, \"\n          \"Column { \"\n            \"index: 0, \"\n            \"column_type: Advice \"\n          \"}, \"\n          \"Column { \"\n            \"index: 1, \"\n            \"column_type: Advice \"\n          \"}\"\n        \"] }, \"\n        \"lookups: [], \"\n        \"constants: [Column { \"\n          \"index: 0, \"\n          \"column_type: Fixed \"\n        \"}], \"\n        \"minimum_degree: None \"\n      \"}\";\n  // clang-format on\n\n  constexpr static std::string_view kAssemblyFixedColumns[][kN] = {\n      {\n          \"0x0000000000000000000000000000000000000000000000000000000000000007\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n      },\n  };\n\n  constexpr static AnyColumnKey kAssemblyPermutationColumns[] = {\n      InstanceColumnKey(0),\n      FixedColumnKey(0),\n      AdviceColumnKey(0),\n      AdviceColumnKey(1),\n  };\n\n  // clang-format off\n  constexpr static Label kCycleStoreMapping[][kN] = {\n      {{2, 8}, {0, 1},  {0, 2},  {0, 3},  {0, 4},  {0, 5},  {0, 6},  {0, 7},\n       {0, 8}, {0, 9}, {0, 10}, {0, 11}, {0, 12}, {0, 13}, {0, 14}, {0, 15}},\n      {{2, 2}, {1, 1},  {1, 2},  {1, 3},  {1, 4},  {1, 5},  {1, 6},  {1, 7},\n       {1, 8}, {1, 9}, {1, 10}, {1, 11}, {1, 12}, {1, 13}, {1, 14}, {1, 15}},\n      {{2, 3}, {3, 3},  {2, 7},  {2, 0},  {3, 5},  {2, 4},  {3, 7},  {1, 0},\n       {0, 0}, {2, 9}, {2, 10}, {2, 11}, {2, 12}, {2, 13}, {2, 14}, {2, 15}},\n      {{3, 0}, {3, 1},  {3, 2},  {2, 1},  {3, 4},  {2, 5},  {3, 6},  {2, 6},\n       {3, 8}, {3, 9}, {3, 10}, {3, 11}, {3, 12}, {3, 13}, {3, 14}, {3, 15}},\n  };\n\n  constexpr static Label kCycleStoreAux[][kN] = {\n      {{2, 8}, {0, 1},  {0, 2},  {0, 3},  {0, 4},  {0, 5},  {0, 6},  {0, 7},\n       {0, 8}, {0, 9}, {0, 10}, {0, 11}, {0, 12}, {0, 13}, {0, 14}, {0, 15}},\n      {{1, 0}, {1, 1},  {1, 2},  {1, 3},  {1, 4},  {1, 5},  {1, 6},  {1, 7},\n       {1, 8}, {1, 9}, {1, 10}, {1, 11}, {1, 12}, {1, 13}, {1, 14}, {1, 15}},\n      {{2, 3}, {3, 3},  {1, 0},  {2, 3},  {2, 5},  {2, 5},  {3, 7},  {1, 0},\n       {2, 8}, {2, 9}, {2, 10}, {2, 11}, {2, 12}, {2, 13}, {2, 14}, {2, 15}},\n      {{3, 0}, {3, 1},  {3, 2},  {3, 3},  {3, 4},  {2, 5},  {3, 6},  {3, 7},\n       {3, 8}, {3, 9}, {3, 10}, {3, 11}, {3, 12}, {3, 13}, {3, 14}, {3, 15}},\n  };\n  // clang-format on\n\n  constexpr static size_t kCycleStoreSizes[][kN] = {\n      {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},\n      {3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},\n      {1, 1, 1, 2, 1, 3, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1},\n      {1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1},\n  };\n\n  // clang-format off\n  constexpr static bool kSelectors[][kN] = {\n      {false, false, false,  true, false,  true, false,  true,\n       false, false, false, false, false, false, false, false}\n  };\n  // clang-format on\n\n  // clang-format off\n  constexpr static std::string_view kPinnedVerifyingKey =\n      \"PinnedVerificationKey { \"\n        \"base_modulus: \\\"0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47\\\", \"\n        \"scalar_modulus: \\\"0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001\\\", \"\n        \"domain: PinnedEvaluationDomain { \"\n          \"k: 4, \"\n          \"extended_k: 5, \"\n          \"omega: 0x21082ca216cbbf4e1c6e4f4594dd508c996dfbe1174efb98b11509c6e306460b \"\n        \"}, \"\n        \"cs: PinnedConstraintSystem { \"\n          \"num_fixed_columns: 2, \"\n          \"num_advice_columns: 2, \"\n          \"num_instance_columns: 1, \"\n          \"num_selectors: 1, \"\n          \"gates: [Product(\"\n            \"Fixed { \"\n              \"query_index: 1, \"\n              \"column_index: 1, \"\n              \"rotation: Rotation(0) \"\n            \"}, \"\n            \"Sum(\"\n              \"Product(\"\n                \"Advice { \"\n                  \"query_index: 0, \"\n                  \"column_index: 0, \"\n                  \"rotation: Rotation(0) \"\n                \"}, \"\n                \"Advice { \"\n                  \"query_index: 1, \"\n                  \"column_index: 1, \"\n                  \"rotation: Rotation(0) \"\n                \"}\"\n              \"), \"\n              \"Negated(Advice { \"\n                \"query_index: 2, \"\n                \"column_index: 0, \"\n                \"rotation: Rotation(1) \"\n              \"})\"\n            \")\"\n          \")], \"\n          \"advice_queries: [\"\n            \"(\"\n              \"Column { \"\n                \"index: 0, \"\n                \"column_type: Advice \"\n              \"}, \"\n              \"Rotation(0)\"\n            \"), \"\n            \"(\"\n              \"Column { \"\n                \"index: 1, \"\n                \"column_type: Advice \"\n              \"}, \"\n              \"Rotation(0)\"\n            \"), \"\n            \"(\"\n              \"Column { \"\n                \"index: 0, \"\n                \"column_type: Advice \"\n              \"}, \"\n              \"Rotation(1)\"\n            \")\"\n          \"], \"\n          \"instance_queries: [(\"\n            \"Column { \"\n              \"index: 0, \"\n              \"column_type: Instance \"\n            \"}, \"\n            \"Rotation(0)\"\n          \")], \"\n          \"fixed_queries: [\"\n            \"(\"\n              \"Column { \"\n                \"index: 0, \"\n                \"column_type: Fixed \"\n              \"}, \"\n              \"Rotation(0)\"\n            \"), \"\n            \"(\"\n              \"Column { \"\n                \"index: 1, \"\n                \"column_type: Fixed \"\n              \"}, \"\n              \"Rotation(0)\"\n            \")\"\n          \"], \"\n          \"permutation: Argument { columns: [\"\n            \"Column { \"\n              \"index: 0, \"\n              \"column_type: Instance \"\n            \"}, \"\n            \"Column { \"\n              \"index: 0, \"\n              \"column_type: Fixed \"\n            \"}, \"\n            \"Column { \"\n              \"index: 0, \"\n              \"column_type: Advice \"\n            \"}, \"\n            \"Column { \"\n              \"index: 1, \"\n              \"column_type: Advice \"\n            \"}\"\n          \"] }, \"\n          \"lookups: [], \"\n          \"constants: [\"\n            \"Column { \"\n              \"index: 0, \"\n              \"column_type: Fixed \"\n            \"}\"\n          \"], \"\n          \"minimum_degree: None \"\n        \"}, \"\n        \"fixed_commitments: [\"\n          \"(0x0f9cc629d0010671d7f755267acccfc8d5854f47d4e437ec84bddcc27b9f19d1, \"\n            \"0x199b1dcc7aff518a4e49c6393bcf847cc3fde52ae69e326dea0d1d901424552a), \"\n          \"(0x10472018b5bfdcc76f3925ea4f660dc7167ef12c96fb70f686d0c1cf7791cde5, \"\n            \"0x0358f44f7cb29a8d129dbe6b61fc1d921a903f1e9209abc137c7a6446c3ae38f)\"\n        \"], \"\n        \"permutation: VerifyingKey { commitments: [\"\n          \"(0x0365b8986f1c38476aa6479eea1b688244b4070413eb393efa5b06441ac2aeaa, \"\n            \"0x303ed0aaed99cb2848d844239ab4ab9a9191b86544edab860c42a2d4e504cf34), \"\n          \"(0x1af13f7dc79a97c1a690215e2ffd3d8386f682fe1a13bdb8588d2fbaa0161edc, \"\n            \"0x10f0c0ddf9db782e88deb3e49464292b1276b2b9ad29b5a25971c85fe0b8aa96), \"\n          \"(0x09837455c613e5b0e0edd2a1d47f1efdee21eeeda00548f3686c733301e23da4, \"\n            \"0x2e2b7776741eb2214916eb80ae0982c32b02e57e83c51e8907b5ffeab048bf2d), \"\n          \"(0x23e033260e2f2ed7dd27295a06d951fad7c1545a493f24e8d7f416ccc3c74670, \"\n            \"0x0957c9d3b0c00ff783bc3357dd0b5cf891f49bc78f569126ba6e68bc394859ef)\"\n        \"] } \"\n      \"}\";\n  // clang-format on\n\n  constexpr static std::string_view kTranscriptRepr =\n      \"0x03b30e0717f2047e825763ccf9c91fff91c82eef5ec0834f66f359f29a3d3b58\";\n\n  constexpr static std::string_view kLFirst[] = {\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n  };\n\n  constexpr static std::string_view kLLast[] = {\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2014447de15a99b6df03833e95f96ae1299c9ec6ff990b6e75fa3b3b04846a57\",\n      \"0x0f1f5883e65f820d14d56342dc92fd12a944d4cbbdce5377b7439bd07108fc9d\",\n      \"0x02b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e8\",\n      \"0x030644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000\",\n      \"0x105009f4ffd70672d94cc277eb87ed7bfe9749817a206522cde7ba58eb7b95aa\",\n      \"0x2144f5eefad21e1ca37ae273a4ee5b4a7eef137cbbeb1d198c9e59c37ef70364\",\n      \"0x2db11694c4a58b3789868bd388165969c30dc1120a37fff09a991abf43e42a19\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2014447de15a99b6df03833e95f96ae1299c9ec6ff990b6e75fa3b3b04846a57\",\n      \"0x0f1f5883e65f820d14d56342dc92fd12a944d4cbbdce5377b7439bd07108fc9d\",\n      \"0x02b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e8\",\n      \"0x030644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000\",\n      \"0x105009f4ffd70672d94cc277eb87ed7bfe9749817a206522cde7ba58eb7b95aa\",\n      \"0x2144f5eefad21e1ca37ae273a4ee5b4a7eef137cbbeb1d198c9e59c37ef70364\",\n      \"0x2db11694c4a58b3789868bd388165969c30dc1120a37fff09a991abf43e42a19\",\n  };\n\n  constexpr static std::string_view kLActiveRow[] = {\n      \"0x12259d6b14729c0fa51e1a2470908122ef13771b2da58a367974bc177a000001\",\n      \"0x1a8133201ba2fe22aff30c7fd04fadcb17ceab0715a13371b06d35acd9695b3d\",\n      \"0x0d49c50dd1c3ec703dc7be1c836fd7f62c140afcf284ce19b9a99affac7b95aa\",\n      \"0x117dae38a05bcb8c7c290ee16cec493d73ef8dafa5c61fa4a2efd9a39e63abf4\",\n      \"0x0c19139cb84c680a79505ee7747ae78cd6c196473632bc6ea3057c773208fc9d\",\n      \"0x136156e428a2662bc2fddfd3b39f6475dafecb8699a611f3da6edd22c3479af1\",\n      \"0x2aaad1ad96927134ee0187781ffe43e3f08a828d829c68e7865afb6604e42a19\",\n      \"0x28d7d254c17b7ea40ebc4659996adacebd0d8f52d021284040d407c2f33b896f\",\n      \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n      \"0x1ef7221577b1e8f8cebdcb2fcd10296b6d9d43267b395c820757c0fc87681d81\",\n      \"0x1d0dff96b3477fb4437e7ee32de1555b5719604277fd746561bc1be1c5846a57\",\n      \"0x1be193603aacb384678281793fa8f20b949cf5976d9493017ba8a357ee49da57\",\n      \"0x1e3eb107ccbf041a07f5de183cd645c4ac6bd4f8344f861078603a6a3ff70364\",\n      \"0x20080468beb85b16c9f71b3ea2ce10fb6cdc81c346721c888ebc9109900adec6\",\n      \"0x30114169cfaa9b194b94fb3e12d441cabad6d0fa619f4a28d8ecb10f5d1bd5e9\",\n      \"0x2edcc3ce4ec47abd9b83b31a4db9571236223b590c30997fd30ce24f7bf2fdd6\",\n  };\n\n  constexpr static std::string_view kFixedColumns[][kN] = {\n      {\n          \"0x0000000000000000000000000000000000000000000000000000000000000007\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n      },\n      {\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n      },\n  };\n\n  constexpr static std::string_view kFixedPolys[][kN] = {\n      {\n          \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n          \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n          \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n          \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n          \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n          \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n          \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n          \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n          \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n          \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n          \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n          \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n          \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n          \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n          \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n          \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n      },\n      {\n          \"0x27517fbd56f85221e5c138a4493917cbb0aa2cbae2e6ab760727978833000001\",\n          \"0x2e2956f8332a2abea8eae65e83211a8cfd4c9c38c6ed5c09186cafec19009edf\",\n          \"0x2014447de15a99b6df03833e95f96ae1299c9ec6ff990b6e75fa3b3b04846a57\",\n          \"0x130034a5a3705c18e67b698f576257c783c3403058f57e9a359495efd00ce8cf\",\n          \"0x2144f5eefad21e1ca37ae273a4ee5b4a7eef137cbbeb1d198c9e59c37ef70364\",\n          \"0x11ded077258dd59f58ab8525c92955ebcb2b1905e676b2fe47ca20d6919e5e16\",\n          \"0x02b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e8\",\n          \"0x152fae7ca9f4520815c46c7ae6996e0cd78f9e211ed4ffa8d8d48d83b3a445cd\",\n          \"0x0912ceb58a394e07d28f0d12384840917789bb8d96d2c51b3cba5e0bbd000000\",\n          \"0x023af77aae07756b0f655f57fe603dd02ae74c0fb2cc14882b7545a7d6ff6122\",\n          \"0x105009f4ffd70672d94cc277eb87ed7bfe9749817a206522cde7ba58eb7b95aa\",\n          \"0x1d6419cd3dc14410d1d4dc272a1f0095a470a81820c3f1f70e4d5fa41ff31732\",\n          \"0x0f1f5883e65f820d14d56342dc92fd12a944d4cbbdce5377b7439bd07108fc9d\",\n          \"0x1e857dfbbba3ca8a5fa4c090b85802715d08cf429342bd92fc17d4bd5e61a1eb\",\n          \"0x2db11694c4a58b3789868bd388165969c30dc1120a37fff09a991abf43e42a19\",\n          \"0x1b349ff6373d4e21a28bd93b9ae7ea5050a44a275ae470e86b0d68103c5bba34\",\n      },\n  };\n\n  constexpr static std::string_view kPermutationsColumns[][kN] = {\n      {\n          \"0x1cb0ed9df901b713b97ee5357df1bf9b16f16cc0d737b31e07ba77d910efc8d6\",\n          \"0x21082ca216cbbf4e1c6e4f4594dd508c996dfbe1174efb98b11509c6e306460b\",\n          \"0x2b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e80\",\n          \"0x107aab49e65a67f9da9cd2abf78be38bd9dc1d5db39f81de36bcfa5b4b039043\",\n          \"0x30644e72e131a029048b6e193fd841045cea24f6fd736bec231204708f703636\",\n          \"0x2290ee31c482cf92b79b1944db1c0147635e9004db8c3b9d13644bef31ec3bd3\",\n          \"0x1d59376149b959ccbd157ac850893a6f07c2d99b3852513ab8d01be8e846a566\",\n          \"0x2d8040c3a09c49698c53bfcb514d55a5b39e9b17cb093d128b8783adb8cbd723\",\n          \"0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000\",\n          \"0x0f5c21d0ca65e0db9be1f670eca407d08ec5ec67626a74f892ccebcd0cf9b9f6\",\n          \"0x0530d09118705106cbb4a786ead16926d5d174e181a26686af5448492e42a181\",\n          \"0x1fe9a328fad7382fddb3730a89f574d14e57caeac619eeb30d24fb38a4fc6fbe\",\n          \"0x0000000000000000b3c4d79d41a91758cb49c3517c4604a520cff123608fc9cb\",\n          \"0x0dd360411caed09700b52c71a6655715c4d558439e2d34f4307da9a4be13c42e\",\n          \"0x130b17119778465cfb3acaee30f81dee20710ead41671f568b11d9ab07b95a9b\",\n          \"0x02e40daf409556c02bfc85eb303402b774954d30aeb0337eb85a71e6373428de\",\n      },\n      {\n          \"0x0b6f861977ce57ddb2e647048ed9b9433cac640ba0599e264e24446765d915d3\",\n          \"0x133f51f46c2a51201fbb89dc1d6868acfef0f7ce0e572ccb98e04e99eac3ea36\",\n          \"0x1240a374ee6f71e12df10a129946ca9ba824f58a50e3a58b4c68dd5590b74ad8\",\n          \"0x009553089042fe83ab570b54e4e64d307d8e8b20d568e782ece3926981d0a96c\",\n          \"0x14a6c152ace4b16a42e1377e400798e15ac60320c21d90277890dbdd551d1912\",\n          \"0x035992598be4d2ae5334f24f30355a1fca7f0e28762a178d79e993dca70eaabf\",\n          \"0x03b645319eb70d7a692ea8c87fbcab1f292cd502071d9ffb7291ebe28356aa07\",\n          \"0x231e38741f5c3f0ce2509bd3289b09a893b14b136eea6ed00cec9ac9271c9db5\",\n          \"0x2741e304be6aaf5f53641f0bacb8e9ebccd45eba1b23316bbcd39ed80acc165f\",\n          \"0x1d24fc7e75074f099894bbda6418efb02942f07a6b6243c5ab01a6fa053c15cb\",\n          \"0x1e23aafdf2c22e488a5f3ba3e83a8dc1800ef2be28d5cb05f779183e5f48b529\",\n          \"0x2fcefb6a50eea1a60cf93a619c9b0b2caaa55d27a450890e56fe632a6e2f5695\",\n          \"0x1bbd8d20344ceebf756f0e384179bf7bcd6de527b79be069cb5119b69ae2e6ef\",\n          \"0x2d0abc19554ccd7b651b5367514bfe3d5db4da20038f5903c9f861b748f15542\",\n          \"0x2cae0941427a92af4f219cee01c4ad3dff071346729bd095d15009b16ca955fa\",\n          \"0x0d4615fec1d5611cd5ffa9e358e64eb494829d350acf01c136f55acac8e3624c\",\n      },\n      {\n          \"0x26f93d99832c6285d9abf8c5b896ea753ed137516972c5ddcc464b56c488d600\",\n          \"0x0f34fda7cd268bf095354ea6c2631826c349d7518bf094361bc91f61c519c7fb\",\n          \"0x2a3d1fef5cb3ce1065d222dde5f9b16d48b42597868c1a49e15cb8007c8778a4\",\n          \"0x13b360d4e82fe915fed16081038f98c211427b87a281bd733c277dbadf10372b\",\n          \"0x1e626bf9ef3c8920522383ff9be21287c3af8d47fe61ff9af6c6d8d0118154bc\",\n          \"0x086398ace043cf0db4e99f2712f392ddbd9c7b9202b2fcadf26e9b145316df9f\",\n          \"0x2a150ee7450ad90cace368ea424c8fdf36b3821e79f5f1c617a5f8e74c19b4cf\",\n          \"0x09226b6e22c6f0ca64ec26aad4c86e715b5f898e5e963f25870e56bbe533e9a2\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x277c827440297ddeb0d85560fc7da91bcfd8729cec6bf01c3ecc664827388e23\",\n          \"0x24f4c8596963484c0569feb1f2a79f19eb87843cd95fd26af5bdb12c8a26ea2e\",\n          \"0x096b10d95e053da3dea44cf0c8ea6de7e962b0f71046aab3779baa3d2b772a01\",\n          \"0x2800b5c600edd11c0366a68f6e8dc57f6a976cb6770673e351735a7f9ce92062\",\n          \"0x2bedf321de5b3dacbb8cbc7312ea9c937dec5a7d07ae1c24ccdbc51c4bf6ef33\",\n          \"0x022a8cf5a31c990f250e75e7df4daafe02929a5e514802a8b8fe8be7c366bb55\",\n          \"0x06272e83847dd219527e22d89b87a6efdf7fc2b0f32d564762853d937378875d\",\n      },\n      {\n          \"0x18afdf23e9bd9302673fc1e076a492d4d65bd18ebc4d854ed189139bab313e52\",\n          \"0x2f0e061e83e8b12c1bdf8df7cca02483295e89f78a462ce38859661e06501815\",\n          \"0x034183d253b6b250dae0a457797029523434d2b6bc41c09b6ef409bd970e4208\",\n          \"0x08e7cbfea108224b0777f0558503af41585b75ab8d4d807505158f4bc8c771de\",\n          \"0x2f549305063b1803a77ba92486c5383e00468fc857ec66d944868c4a309e596a\",\n          \"0x04765b5102d6627cfcc389436e96bbc9aa478dcb720b546c77063077a40910ce\",\n          \"0x02898db49f7022cac0aeabf721a3c7ab96e14ad35e7d6b0410b868ff504b62fc\",\n          \"0x2e39c17d3e15071a9341cfcea233ad5f25a14dea28716de88ae369ac2c9944ac\",\n          \"0x17b46f4ef7740d27511083d60adcc58851d816b9bd6beb427258e1f844cec1af\",\n          \"0x015648545d48eefd9c70b7beb4e133d9fed55e50ef7343adbb888f75e9afe7ec\",\n          \"0x2d22caa08d7aedd8dd6fa15f08112f0af3ff1591bd77aff5d4edebd658f1bdf9\",\n          \"0x212f50cb140b1439231af70fbf1e403664ea10f6edc8dc5b2818d6322ae63806\",\n          \"0x010fbb6ddaf6882610d49c91fabc201f27ed588021cd09b7ff5b6949bf61a697\",\n          \"0x1201e278f1f51709662cc1b6e59f45d564845b007b5770f64d1b1cc3de7eab45\",\n          \"0x2ddac0be41c17d5ef7a199bf5fdd90b191529d751b3c058d33298c949fb49d05\",\n          \"0x064f3f8b9c26c71d0b6cdccc3f34c87df1806629ffc37ecb2c3bfcaca3e64b32\",\n      },\n  };\n\n  constexpr static std::string_view kPermutationsPolys[][kN] = {\n      {\n          \"0x231004c8da62398dea4f1e40d0e808b9bd12c67de122f895bf270053460efc8e\",\n          \"0x231004c8da62398dea4f1e40d0e808b9bd12c67de122f895bf270053460efc8f\",\n          \"0x231004c8da62398dea4f1e40d0e808b9bd12c67de122f895bf270053460efc8e\",\n          \"0x231004c8da62398dea4f1e40d0e808b9bd12c67de122f895bf270053460efc8e\",\n          \"0x231004c8da62398dea4f1e40d0e808b9bd12c67de122f895bf270053460efc8e\",\n          \"0x231004c8da62398dea4f1e40d0e808b9bd12c67de122f895bf270053460efc8e\",\n          \"0x231004c8da62398dea4f1e40d0e808b9bd12c67de122f895bf270053460efc8e\",\n          \"0x231004c8da62398dea4f1e40d0e808b9bd12c67de122f895bf270053460efc8e\",\n          \"0x231004c8da62398dea4f1e40d0e808b9bd12c67de122f895bf270053460efc8e\",\n          \"0x231004c8da62398dea4f1e40d0e808b9bd12c67de122f895bf270053460efc8e\",\n          \"0x231004c8da62398dea4f1e40d0e808b9bd12c67de122f895bf270053460efc8e\",\n          \"0x231004c8da62398dea4f1e40d0e808b9bd12c67de122f895bf270053460efc8e\",\n          \"0x231004c8da62398dea4f1e40d0e808b9bd12c67de122f895bf270053460efc8e\",\n          \"0x231004c8da62398dea4f1e40d0e808b9bd12c67de122f895bf270053460efc8e\",\n          \"0x231004c8da62398dea4f1e40d0e808b9bd12c67de122f895bf270053460efc8e\",\n          \"0x231004c8da62398dea4f1e40d0e808b9bd12c67de122f895bf270053460efc8e\",\n      },\n      {\n          \"0x2d82db36686efc9851aae360b50a578473c5776bc63a0f783c153515690a52c4\",\n          \"0x0640f831aa044d38fe46c45508516d98a6f118b1ab16de0c7f41963d5e3e3c65\",\n          \"0x2d82db36686efc9851aae360b50a578473c5776bc63a0f783c153515690a52c4\",\n          \"0x2d82db36686efc9851aae360b50a578473c5776bc63a0f783c153515690a52c4\",\n          \"0x2d82db36686efc9851aae360b50a578473c5776bc63a0f783c153515690a52c4\",\n          \"0x2d82db36686efc9851aae360b50a578473c5776bc63a0f783c153515690a52c4\",\n          \"0x2d82db36686efc9851aae360b50a578473c5776bc63a0f783c153515690a52c4\",\n          \"0x2d82db36686efc9851aae360b50a578473c5776bc63a0f783c153515690a52c4\",\n          \"0x2d82db36686efc9851aae360b50a578473c5776bc63a0f783c153515690a52c4\",\n          \"0x2d82db36686efc9851aae360b50a578473c5776bc63a0f783c153515690a52c4\",\n          \"0x2d82db36686efc9851aae360b50a578473c5776bc63a0f783c153515690a52c4\",\n          \"0x2d82db36686efc9851aae360b50a578473c5776bc63a0f783c153515690a52c4\",\n          \"0x2d82db36686efc9851aae360b50a578473c5776bc63a0f783c153515690a52c4\",\n          \"0x2d82db36686efc9851aae360b50a578473c5776bc63a0f783c153515690a52c4\",\n          \"0x2d82db36686efc9851aae360b50a578473c5776bc63a0f783c153515690a52c4\",\n          \"0x2d82db36686efc9851aae360b50a578473c5776bc63a0f783c153515690a52c4\",\n      },\n      {\n          \"0x18038fb09dafe4561b7c81a4ddecb5a9fdec9905c914eb53873760ec477b6153\",\n          \"0x01330c7cbea5f36f17d3698ffdacd0e8d561fc0253004050c673cb4825a5d678\",\n          \"0x11bd06280c7c8a6daa71eb9d3b2c97f9f211a37a41798bc63f78d5af70135d20\",\n          \"0x021ebc7877f2b98917ec49b986e1a497743d0464e2e1cf787fcc843b013626bb\",\n          \"0x2965a7e65ce635bbab94a2fffae4c86626d67c0eb10d658380f782f08db31ef9\",\n          \"0x13d9914eeb3084263023150484b8da37654dc6d57f82afffca2739b48de541c3\",\n          \"0x150b22b022aca6d5eacd65a0435fe2b487242afc5df5b9a417a001d5a79554a0\",\n          \"0x1911d0efc0d670781ad228d1df69e12aa502675c2c2b0873421dc9f32f16364f\",\n          \"0x173bca2a5af56060d72e1cf66a4c15350c7a1d0985d14232e9c548bf83a8e270\",\n          \"0x1761ac780a2688e48980ee5f7bb6c6489891ad7207f1b53c9a9aecdf3ca68117\",\n          \"0x2af4468570b8b17c26c074b53131f4dab8f4abc05ac204dfee977cd8f2ec9c0e\",\n          \"0x29a949ffe203c1114c6cc31a6b99269932f7cd7340abbe797c0d6c99a7f802f1\",\n          \"0x2f93c69781f9dd1bcc42e4149107242b847da4d909a47893ebe85b9a1fe5700e\",\n          \"0x2a7dc0ef215246ec520423ffc5f6e6fa3d5c0144e341d3358dd64ce8e648afff\",\n          \"0x134ac81b3f8e47b083bd4a759f2e5be3ec6cdfc0d4b28794740f1a3096f24a6d\",\n          \"0x2115cec3e5a7af5c4f4829c80b9825c24f499de351535dc35cb607a47b85c1b7\",\n      },\n      {\n          \"0x28967ba8c313c600d17a4ddd20a4f32f49d6e1e9fcba5e5249324a66d96b4f5e\",\n          \"0x00a354bfcc60f6a39aa68e9a9fa36f4f2bc9322db22d4c76b20738f12376fa40\",\n          \"0x2d0e02c7383ab7a8d1dc324434cbc0efa7c7da0a5c10f1008c072ad9284c951b\",\n          \"0x295fc9ac7c7023fa3374d3df0959578ede07bc8e8e32a7d46b5928ac0f6051c7\",\n          \"0x2be457106435d313da2cbe5c1304c9b5eac6cef2e824ab55be7e094f779a7680\",\n          \"0x11d0a6eb25ba5944c820e99c5b6bfeacbaed201b58985a3e47ac129d746acb2d\",\n          \"0x10f99e36b6e6090fddea8d5f4048b35d3bf6c5f9b4b84f83c3f8695c1fc00a84\",\n          \"0x1269a9b9c8dd3f9f671c12f107a14cb0be65a15412dc8d8b0fb139184bfbd050\",\n          \"0x07cdd2ca1e1dda28e6d5f7d960dc652dde5d065e7cff123efaafab2d1694b0a3\",\n          \"0x180c8a641d5c9c5ecc993345d7012385aa929f610a2038d81f81daaa87ba4412\",\n          \"0x03564baba8f6e880e67413724cb5976d806c0e3e1da87f90b7dacabac7b36ae6\",\n          \"0x070484c664c17c2f84db71d7782800ce4a2c2bb9eb86c8bcd888cce7e09fae3a\",\n          \"0x047ff7627cfbcd15de23875a6e7c8ea73d6d19559194c53b8563ec4478658981\",\n          \"0x1e93a787bb7746e4f02f5c1a261559b06d46c82d21211652fc35e2f67b9534d4\",\n          \"0x1f6ab03c2a4b9719da65b8574138a4ffec3d224ec501210d7fe98c37d03ff57d\",\n          \"0x1dfaa4b91854608a513432c579e00bac69ce46f466dce3063430bc7ba4042fb1\",\n      },\n  };\n\n  constexpr static uint8_t kProof[] = {\n      206, 109, 139, 136, 181, 35,  204, 231, 212, 93,  105, 116, 154, 77,  204,\n      23,  71,  148, 11,  151, 126, 145, 6,   150, 171, 185, 254, 230, 41,  136,\n      76,  141, 132, 227, 154, 206, 134, 35,  253, 67,  8,   186, 228, 143, 116,\n      139, 145, 119, 85,  253, 127, 208, 95,  153, 195, 112, 209, 116, 172, 45,\n      15,  175, 128, 142, 206, 109, 139, 136, 181, 35,  204, 231, 212, 93,  105,\n      116, 154, 77,  204, 23,  71,  148, 11,  151, 126, 145, 6,   150, 171, 185,\n      254, 230, 41,  136, 76,  141, 132, 227, 154, 206, 134, 35,  253, 67,  8,\n      186, 228, 143, 116, 139, 145, 119, 85,  253, 127, 208, 95,  153, 195, 112,\n      209, 116, 172, 45,  15,  175, 128, 142, 240, 229, 40,  5,   109, 36,  59,\n      227, 58,  205, 89,  157, 199, 193, 252, 51,  168, 195, 186, 126, 190, 9,\n      14,  29,  214, 95,  182, 184, 134, 94,  46,  41,  106, 156, 194, 192, 74,\n      238, 43,  10,  112, 155, 176, 178, 14,  10,  189, 165, 211, 91,  90,  42,\n      17,  165, 116, 23,  78,  117, 45,  125, 110, 1,   233, 170, 200, 135, 181,\n      143, 22,  0,   52,  134, 185, 92,  30,  3,   65,  216, 74,  165, 18,  77,\n      5,   55,  15,  109, 186, 248, 156, 119, 247, 24,  70,  41,  1,   16,  62,\n      36,  174, 136, 24,  93,  149, 45,  191, 112, 228, 131, 82,  170, 188, 118,\n      86,  196, 76,  197, 11,  167, 11,  216, 230, 14,  231, 164, 157, 39,  49,\n      38,  251, 240, 47,  6,   163, 23,  217, 174, 233, 203, 174, 0,   205, 128,\n      144, 251, 60,  132, 157, 32,  33,  112, 229, 156, 139, 47,  162, 5,   202,\n      84,  199, 47,  27,  98,  96,  122, 158, 193, 213, 226, 200, 103, 5,   218,\n      173, 125, 15,  210, 123, 98,  148, 53,  48,  206, 127, 172, 107, 33,  234,\n      174, 142, 79,  186, 134, 142, 249, 224, 82,  159, 246, 235, 120, 167, 5,\n      48,  155, 25,  120, 55,  94,  246, 75,  242, 253, 253, 152, 55,  94,  67,\n      65,  174, 82,  157, 209, 177, 132, 71,  170, 194, 199, 129, 201, 108, 111,\n      231, 243, 252, 145, 116, 7,   88,  87,  28,  81,  97,  30,  116, 31,  167,\n      99,  44,  21,  104, 160, 51,  70,  32,  5,   1,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   159, 45,  146, 241,\n      184, 125, 165, 48,  148, 73,  242, 94,  170, 139, 162, 0,   21,  231, 23,\n      74,  94,  214, 195, 249, 139, 29,  249, 198, 235, 204, 224, 27,  135, 78,\n      126, 51,  100, 157, 42,  110, 141, 157, 29,  152, 51,  209, 243, 76,  0,\n      200, 96,  204, 134, 244, 148, 187, 87,  7,   206, 106, 36,  190, 225, 163,\n      221, 191, 109, 9,   219, 247, 181, 207, 188, 191, 31,  65,  129, 49,  12,\n      89,  46,  245, 184, 213, 17,  43,  232, 225, 2,   80,  183, 171, 82,  185,\n      159, 35,  189, 66,  107, 20,  150, 2,   185, 176, 238, 76,  171, 30,  52,\n      254, 113, 233, 150, 92,  5,   25,  77,  216, 65,  95,  79,  148, 13,  116,\n      24,  183, 14,  46,  67,  229, 240, 109, 227, 68,  145, 172, 135, 53,  164,\n      146, 172, 217, 97,  115, 45,  46,  135, 153, 141, 33,  237, 14,  126, 112,\n      239, 91,  249, 224, 14,  16,  221, 191, 109, 9,   219, 247, 181, 207, 188,\n      191, 31,  65,  129, 49,  12,  89,  46,  245, 184, 213, 17,  43,  232, 225,\n      2,   80,  183, 171, 82,  185, 159, 35,  189, 66,  107, 20,  150, 2,   185,\n      176, 238, 76,  171, 30,  52,  254, 113, 233, 150, 92,  5,   25,  77,  216,\n      65,  95,  79,  148, 13,  116, 24,  183, 14,  46,  67,  229, 240, 109, 227,\n      68,  145, 172, 135, 53,  164, 146, 172, 217, 97,  115, 45,  46,  135, 153,\n      141, 33,  237, 14,  126, 112, 239, 91,  249, 224, 14,  16,  36,  203, 226,\n      64,  99,  130, 12,  59,  222, 24,  94,  88,  253, 218, 172, 0,   25,  215,\n      133, 149, 31,  158, 7,   250, 173, 225, 133, 200, 90,  69,  47,  25,  98,\n      34,  19,  103, 109, 218, 148, 30,  225, 90,  104, 37,  58,  197, 245, 62,\n      9,   238, 44,  199, 61,  72,  2,   28,  51,  231, 189, 28,  3,   29,  35,\n      18,  1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   185, 220, 125, 19,  203, 252, 236, 223, 117, 57,  175, 183,\n      40,  37,  79,  31,  117, 165, 118, 41,  185, 125, 170, 158, 157, 74,  166,\n      57,  64,  149, 206, 40,  120, 244, 169, 170, 133, 173, 68,  101, 140, 48,\n      109, 103, 247, 3,   28,  4,   36,  59,  132, 43,  93,  119, 152, 208, 233,\n      222, 219, 94,  136, 106, 20,  11,  87,  104, 205, 179, 87,  159, 234, 122,\n      152, 191, 255, 13,  15,  38,  155, 81,  173, 252, 150, 180, 173, 179, 196,\n      214, 30,  37,  199, 22,  203, 126, 5,   43,  105, 245, 116, 211, 118, 74,\n      43,  166, 161, 118, 110, 238, 30,  223, 80,  204, 218, 180, 236, 139, 207,\n      173, 229, 182, 166, 225, 169, 205, 21,  196, 193, 24,  145, 35,  130, 126,\n      116, 70,  55,  29,  82,  53,  95,  80,  69,  203, 254, 226, 83,  17,  151,\n      238, 128, 120, 105, 242, 98,  98,  11,  81,  88,  20,  116, 14,  55,  241,\n      196, 81,  126, 218, 207, 52,  163, 159, 251, 43,  165, 147, 33,  107, 18,\n      209, 194, 240, 179, 217, 121, 221, 195, 5,   152, 146, 169, 81,  170, 34,\n      32,  97,  114, 73,  185, 104, 109, 151, 124, 157, 118, 48,  123, 213, 18,\n      111, 5,   102, 78,  130, 237, 77,  157, 116, 202, 124, 90,  51,  224, 114,\n      159, 45,  197, 61,  251, 242, 253, 129, 64,  250, 54,  14,  49,  62,  37,\n      97,  226, 172, 109, 168, 14,  33,  113, 47,  243, 44,  106, 229, 10,  62,\n      87,  83,  43,  7,   92,  6,   195, 239, 233, 58,  92,  12,  233, 115, 233,\n      81,  118, 184, 252, 142, 76,  197, 214, 144, 16,  95,  246, 169, 9,   78,\n      237, 145, 235, 141, 136, 1,   165, 83,  192, 177, 140, 190, 28,  139, 172,\n      249, 119, 129, 211, 92,  226, 51,  226, 185, 203, 21,  121, 174, 115, 152,\n      110, 69,  200, 142, 162, 71,  220, 22,  186, 134, 221, 207, 39,  208, 161,\n      130, 234, 61,  27,  72,  233, 172, 187, 198, 231, 11,  120, 243, 25,  89,\n      77,  38,  86,  20,  89,  80,  237, 100, 142, 41,  136, 32,  138, 31,  215,\n      84,  109, 62,  66,  227, 100, 175, 18,  174, 58,  112, 169, 193, 95,  240,\n      222, 15,  61,  236, 91,  90,  188, 94,  150, 212, 237, 16,  117, 27,  177,\n      202, 190, 133, 200, 85,  173, 174, 13,  89,  177, 186, 65,  146, 113, 41,\n      206, 142, 237, 73,  51,  196, 214, 34,  224, 184, 100, 209, 13,  29,  179,\n      244, 71,  38,  7,   16,  137, 20,  184, 72,  209, 27,  255, 185, 144, 75,\n      35,  7,   36,  68,  119, 203, 89,  205, 4,   242, 26,  190, 212, 139, 57,\n      26,  6,   252, 141, 116, 125, 123, 64,  138, 127, 97,  121, 221, 197, 244,\n      112, 113, 155, 70,  96,  133, 82,  7,   149, 25,  118, 141, 20,  42,  46,\n      88,  239, 42,  7,   96,  209, 165, 40,  191, 164, 67,  8,   89,  152, 7,\n      200, 100, 248, 171, 41,  218, 69,  252, 118, 179, 67,  9,   182, 148, 121,\n      104, 153, 44,  247, 9,   218, 204, 78,  16,  182, 29,  29,  0,   119, 160,\n      190, 17,  211, 11,  23,  241, 100, 12,  180, 243, 34,  102, 12,  187, 41,\n      193, 69,  50,  84,  21,  221, 23,  204, 183, 175, 109, 240, 93,  120, 136,\n      54,  66,  184, 194, 85,  59,  133, 86,  220, 246, 193, 244, 7,   216, 135,\n      59,  92,  158, 65,  144, 41,  220, 100, 28,  21,  111, 26,  217, 23,  179,\n      65,  205, 54,  26,  240, 52,  150, 157, 87,  210, 76,  217, 152, 132, 71,\n      150, 10,  95,  77,  14,  228, 86,  178, 21,  103, 6,   74,  89,  231, 106,\n      134, 147, 137, 174, 39,  174, 48,  221, 254, 215, 4,   39,  230, 15,  196,\n      75,  24,  245, 239, 152, 208, 193, 187, 175, 132, 241, 195, 34,  38,  55,\n      57,  17,  54,  64,  27,  138, 144, 11,  157, 37,  36,  5,   139, 117, 212,\n      173, 39,  85,  164, 25,  206, 217, 149, 150, 4,   250, 77,  130, 115, 9,\n      143, 200, 219, 182, 151, 215, 233, 194, 240, 248, 177, 123, 131, 209, 152,\n      81,  102, 187, 53,  150, 25,  204, 189, 65,  9,   86,  79,  99,  81,  250,\n      151, 4,   11,  172, 72,  139, 204, 43,  44,  115, 202, 121, 48,  124, 236,\n      53,  203, 245, 141, 110, 228, 145, 107, 49,  56,  58,  168, 222, 112, 147,\n      197, 179, 83,  10,  128, 240, 51,  82,  135, 247, 196, 23,  27,  22,  177,\n      117, 78,  249, 227, 93,  64,  8,   176, 132, 143, 218, 225, 185, 123, 159,\n      78,  200, 197, 254, 171, 0,   103, 14,  109, 105, 100, 65,  235, 96,  115,\n      178, 20,  89,  249, 152, 94,  113, 124, 134, 89,  134, 176, 129, 249, 140,\n      162, 116, 135, 158, 230, 87,  9,   32,  227, 25,  158, 147, 29,  22,  77,\n      43,  83,  154, 15,  112, 182, 222, 83,  80,  108, 218, 132, 95,  207, 89,\n      134, 152, 228, 205, 152, 172, 107, 188, 119, 46,  154, 108, 59,  219, 34,\n      8,   146, 12,  91,  13,  166, 147, 124, 190, 65,  228, 254, 170, 42,  228,\n      225, 100, 186, 38,  12,  202, 16,  176, 133, 49,  60,  16,  237, 211, 195,\n      59,  246, 51,  38,  12,  208, 190, 66,  35,  23,  195, 152, 31,  96,  15,\n      198, 165, 206, 149, 89,  130, 172, 23,  119, 105, 140, 216, 51,  6};\n\n  // clang-format off\n  constexpr static Point kAdviceCommitments[][2] = {\n      {\n          {\"0x0d4c8829e6feb9ab9606917e970b944717cc4d9a74695dd4e7cc23b5888b6dce\",\n            \"0x03a99ef4660a95515763e072043119fcbf6d3f3b709af6bf05b5c8b4d815a775\"},\n          {\"0x0e80af0f2dac74d170c3995fd07ffd5577918b748fe4ba0843fd2386ce9ae384\",\n            \"0x058b31b773e7a0e22f1ef9d6bbcc154b3dfaec09ff6c78084c1ae5c150f6624d\"},\n      },\n      {\n          {\"0x0d4c8829e6feb9ab9606917e970b944717cc4d9a74695dd4e7cc23b5888b6dce\",\n            \"0x03a99ef4660a95515763e072043119fcbf6d3f3b709af6bf05b5c8b4d815a775\"},\n          {\"0x0e80af0f2dac74d170c3995fd07ffd5577918b748fe4ba0843fd2386ce9ae384\",\n            \"0x058b31b773e7a0e22f1ef9d6bbcc154b3dfaec09ff6c78084c1ae5c150f6624d\"},\n      },\n  };\n  // clang-format on\n\n  constexpr static std::string_view kTheta =\n      \"0x2b059aeea380dae6d29c1f1709cddfe7a7fa5d239d2ed38f7c5e7db17349d035\";\n\n  constexpr static std::string_view kBeta =\n      \"0x14b7ba74ee783acecf23166da4c9ca6f4fe63cdc66758cae396ce0804e87d509\";\n\n  constexpr static std::string_view kGamma =\n      \"0x12cbf1b371cc5262a7e4a2eda2e1c3171f84b76f64183d5c90305b8b65efe947\";\n\n  // clang-format off\n  constexpr static Point kPermutationProductCommitments[][4] = {\n      {\n          {\"0x292e5e86b8b65fd61d0e09be7ebac3a833fcc1c79d59cd3ae33b246d0528e5f0\",\n           \"0x1f266354b24e20bbae2fa25ffe113392c1b5df9ec5f2a2eb99f6cb5447f4fc24\"},\n          {\"0x2ae9016e7d2d754e1774a5112a5a5bd3a5bd0a0eb2b09b700a2bee4ac0c29c6a\",\n           \"0x15c499ad168f1446aaa20c9af669210de2ad0321c0b338110cbcaf0b1a331023\"},\n          {\"0x1001294618f7779cf8ba6d0f37054d12a54ad841031e5cb9863400168fb587c8\",\n           \"0x19f8d635ee9d52a4ad56ea25cfedaccb9e644bc89ef114036c23c137a6d6dc7a\"},\n          {\"0x2631279da4e70ee6d80ba70bc54cc45676bcaa5283e470bf2d955d1888ae243e\",\n           \"0x2085ebbe76f114b6287f8db2fbadeb9c88a72a94e13c2a280fa4a1823693f47c\"},\n      },\n      {\n          {\"0x2fc754ca05a22f8b9ce57021209d843cfb9080cd00aecbe9aed917a3062ff0fb\",\n           \"0x148f23b9c4ee958cdc46ee3bb7cb8f40b00e1f05c6944fbb166c87d4a34a64bc\"},\n          {\"0x06ba4f8eaeea216bac7fce303594627bd20f7dadda0567c8e2d5c19e7a60621b\",\n           \"0x1b9af694f8f7657a82b27d7376f29e723215a607780b20d9a6064938e58a3203\"},\n          {\"0x04b1d19d52ae41435e3798fdfdf24bf65e3778199b3005a778ebf69f52e0f98e\",\n           \"0x09bedea0070365f54dc7edffce4ddc96e83c74eaa8543e069f8ad7a56f5ef209\"},\n          {\"0x05204633a068152c63a71f741e61511c5758077491fcf3e76f6cc981c7c2aa47\",\n           \"0x01e44060b9b70dc9474317dc324a516f94878ade0ad8221ab46c5b0d5607cad4\"},\n      },\n  };\n  // clang-format on\n\n  constexpr static std::string_view kY =\n      \"0x2b635a1a9615e175bf98c02fcd0eca6a0196c48c2be713ad3c42089ede337d69\";\n\n  constexpr static Point kVanishingHPolyCommitments[] = {\n      {\"0x1be0ccebc6f91d8bf9c3d65e4a17e71500a28baa5ef2499430a57db8f1922d9f\",\n       \"0x0d6ebddee89b00c919f56501344541d27387bd6523b31e4aab73b885c7c70c16\"},\n      {\"0x23e1be246ace0757bb94f486cc60c8004cf3d133981d9d8d6e2a9d64337e4e87\",\n       \"0x277166abda65f12d816f6294fff5f0a258fc37025859ce3ea21a82315aa0d0ef\"},\n  };\n\n  constexpr static std::string_view kX =\n      \"0x1171a27273726e2363fc9167880d5cba29092819cb46c22594092c9a6bd3fc34\";\n\n  constexpr static std::string_view kAdviceEvals[][3] = {\n      {\n          \"0x239fb952abb75002e1e82b11d5b8f52e590c3181411fbfbccfb5f7db096dbfdd\",\n          \"0x2e0eb718740d944f5f41d84d19055c96e971fe341eab4ceeb0b90296146b42bd\",\n          \"0x100ee0f95bef707e0eed218d99872e2d7361d9ac92a43587ac9144e36df0e543\",\n      },\n      {\n          \"0x239fb952abb75002e1e82b11d5b8f52e590c3181411fbfbccfb5f7db096dbfdd\",\n          \"0x2e0eb718740d944f5f41d84d19055c96e971fe341eab4ceeb0b90296146b42bd\",\n          \"0x100ee0f95bef707e0eed218d99872e2d7361d9ac92a43587ac9144e36df0e543\",\n      },\n  };\n\n  constexpr static std::string_view kFixedEvals[] = {\n      \"0x192f455ac885e1adfa079e1f9585d71900acdafd585e18de3b0c826340e2cb24\",\n      \"0x12231d031cbde7331c02483dc72cee093ef5c53a25685ae11e94da6d67132262\",\n  };\n\n  constexpr static std::string_view kCommonPermutationEvals[] = {\n      \"0x28ce954039a64a9d9eaa7db92976a5751f4f2528b7af3975dfecfccb137ddcb9\",\n      \"0x0b146a885edbdee9d098775d2b843b24041c03f7676d308c6544ad85aaa9f478\",\n      \"0x2b057ecb16c7251ed6c4b3adb496fcad519b260f0dffbf987aea9f57b3cd6857\",\n      \"0x18c1c415cda9e1a6b6e5adcf8becb4dacc50df1eee6e76a1a62b4a76d374f569\",\n  };\n\n  constexpr static std::string_view kPermutationProductEvals[][4] = {\n      {\n          \"0x0e741458510b6262f2697880ee971153e2fecb45505f35521d3746747e822391\",\n          \"0x072b53573e0ae56a2cf32f71210ea86dace261253e310e36fa4081fdf2fb3dc5\",\n          \"0x298e64ed50591456264d5919f3780be7c6bbace9481b3dea82a1d027cfdd86ba\",\n          \"0x1a398bd4be1af204cd59cb77442407234b90b9ff1bd148b8148910072647f4b3\",\n      },\n      {\n          \"0x09f72c99687994b60943b376fc45da29abf864c80798590843a4bf28a5d16007\",\n          \"0x066715b256e40e4d5f0a96478498d94cd2579d9634f01a36cd41b317d91a6f15\",\n          \"0x0497fa51634f560941bdcc199635bb665198d1837bb1f8f0c2e9d797b6dbc88f\",\n          \"0x200957e69e8774a28cf981b08659867c715e98f95914b27360eb4164696d0e67\",\n      },\n  };\n\n  constexpr static std::string_view kPermutationProductNextEvals[][4] = {\n      {\n          \"0x22aa51a9929805c3dd79d9b3f0c2d1126b2193a52bfb9fa334cfda7e51c4f137\",\n          \"0x01888deb91ed4e09a9f65f1090d6c54c8efcb87651e973e90c5c3ae9efc3065c\",\n          \"0x10edd4965ebc5a5bec3d0fdef05fc1a9703aae12af64e3423e6d54d71f8a2088\",\n          \"0x2aef582e2a148d76199507528560469b7170f4c5dd79617f8a407b7d748dfc06\",\n      },\n      {\n          \"0x17dd15543245c129bb0c6622f3b40c64f1170bd311bea077001d1db6104eccda\",\n          \"0x22c3f184afbbc1d098eff5184bc40fe62704d7fedd30ae27ae8993866ae7594a\",\n          \"0x0a53b3c59370dea83a38316b91e46e8df5cb35ec7c3079ca732c2bcc8b48ac0b\",\n          \"0x2e77bc6bac98cde4988659cf5f84da6c5053deb6700f9a532b4d161d939e19e3\",\n      },\n  };\n\n  constexpr static std::string_view kPermutationProductLastEvals[][4] = {\n      {\n          \"0x2d9f72e0335a7cca749d4ded824e66056f12d57b30769d7c976d68b949726120\",\n          \"0x16dc47a28ec8456e9873ae7915cbb9e233e25cd38177f9ac8b1cbe8cb1c053a5\",\n          \"0x1d0dd164b8e022d6c43349ed8ece29719241bab1590daead55c885becab11b75\",\n          \"\",\n      },\n      {\n          \"0x1c64dc2990419e5c3b87d807f4c1f6dc56853b55c2b8423688785df06dafb7cc\",\n          \"0x0973824dfa049695d9ce19a45527add4758b0524259d0b908a1b403611393726\",\n          \"0x00abfec5c84e9f7bb9e1da8f84b008405de3f94e75b1161b17c4f7875233f080\",\n          \"\",\n      },\n  };\n\n  constexpr static std::string_view kHEval =\n      \"0x1535a994ce99cdd4b9d17531a2925be1decc650d939dc63753f4fce93d78a5e4\";\n\n  static void TestConfig(FieldConfig<F>& config) {\n    std::array<AdviceColumnKey, 2> expected_advice = {\n        AdviceColumnKey(0),\n        AdviceColumnKey(1),\n    };\n    EXPECT_EQ(config.advice(), expected_advice);\n    EXPECT_EQ(config.instance(), InstanceColumnKey(0));\n    EXPECT_EQ(config.s_mul(), Selector::Simple(0));\n  }\n\n  static Circuit GetCircuit() {\n    F constant(7);\n    F a(2);\n    F b(3);\n    return Circuit(std::move(constant), std::move(a), std::move(b));\n  }\n\n  static std::vector<Circuit> Get2Circuits() {\n    Circuit circuit = GetCircuit();\n    return {circuit, std::move(circuit)};\n  }\n\n  static std::vector<Evals> GetInstanceColumns() {\n    F constant(7);\n    F a(2);\n    F b(3);\n    F c = std::move(constant) * std::move(a).Square() * std::move(b).Square();\n    std::vector<F> instance_column = {std::move(c)};\n    return {Evals(std::move(instance_column))};\n  }\n};\n\n// FloorPlanner = V1FloorPlanner\ntemplate <typename Circuit, typename PS>\nclass SimpleTestData<Circuit, PS, std::enable_if_t<IsV1FloorPlanner<Circuit>>>\n    : public CircuitTestData<Circuit, PS> {\n public:\n  using PCS = typename PS::PCS;\n  using F = typename PCS::Field;\n  using Evals = typename PCS::Evals;\n\n  // Set flags of values to be used as true\n  constexpr static bool kAssemblyFixedColumnsFlag = true;\n  constexpr static bool kAssemblyPermutationColumnsFlag = true;\n  constexpr static bool kCycleStoreMappingFlag = true;\n  constexpr static bool kCycleStoreAuxFlag = true;\n  constexpr static bool kCycleStoreSizesFlag = true;\n  constexpr static bool kLFirstFlag = true;\n  constexpr static bool kLLastFlag = true;\n  constexpr static bool kLActiveRowFlag = true;\n  constexpr static bool kFixedColumnsFlag = true;\n  constexpr static bool kFixedPolysFlag = true;\n  constexpr static bool kPermutationsColumnsFlag = true;\n  constexpr static bool kPermutationsPolysFlag = true;\n  constexpr static bool kAdviceCommitmentsFlag = true;\n  constexpr static bool kPermutationProductCommitmentsFlag = true;\n  constexpr static bool kAdviceEvalsFlag = true;\n  constexpr static bool kFixedEvalsFlag = true;\n  constexpr static bool kCommonPermutationEvalsFlag = true;\n  constexpr static bool kPermutationProductEvalsFlag = true;\n  constexpr static bool kPermutationProductNextEvalsFlag = true;\n  constexpr static bool kPermutationProductLastEvalsFlag = true;\n\n  constexpr static size_t kN = 16;\n\n  // clang-format off\n  constexpr static std::string_view kPinnedConstraintSystem =\n      \"PinnedConstraintSystem { \"\n        \"num_fixed_columns: 1, \"\n        \"num_advice_columns: 2, \"\n        \"num_instance_columns: 1, \"\n        \"num_selectors: 1, \"\n        \"gates: [Product(\"\n          \"Selector(Selector(0, true)), \"\n          \"Sum(\"\n            \"Product(\"\n              \"Advice { \"\n                \"query_index: 0, \"\n                \"column_index: 0, \"\n                \"rotation: Rotation(0) \"\n              \"}, \"\n              \"Advice { \"\n                \"query_index: 1, \"\n                \"column_index: 1, \"\n                \"rotation: Rotation(0) \"\n              \"}\"\n            \"), \"\n            \"Negated(Advice { \"\n              \"query_index: 2, \"\n              \"column_index: 0, \"\n              \"rotation: Rotation(1) \"\n            \"})\"\n          \")\"\n        \")], \"\n        \"advice_queries: [\"\n          \"(\"\n            \"Column { \"\n              \"index: 0, \"\n              \"column_type: Advice \"\n            \"}, \"\n            \"Rotation(0)\"\n          \"), \"\n          \"(\"\n            \"Column { \"\n              \"index: 1, \"\n              \"column_type: Advice \"\n            \"}, \"\n            \"Rotation(0)\"\n          \"), \"\n          \"(\"\n            \"Column { \"\n              \"index: 0, \"\n              \"column_type: Advice \"\n            \"}, \"\n            \"Rotation(1)\"\n          \")\"\n        \"], \"\n        \"instance_queries: [(\"\n          \"Column { \"\n            \"index: 0, \"\n            \"column_type: Instance \"\n          \"}, \"\n          \"Rotation(0)\"\n        \")], \"\n        \"fixed_queries: [(\"\n          \"Column { \"\n            \"index: 0, \"\n            \"column_type: Fixed \"\n          \"}, \"\n          \"Rotation(0)\"\n        \")], \"\n        \"permutation: Argument { columns: [\"\n          \"Column { \"\n            \"index: 0, \"\n            \"column_type: Instance \"\n          \"}, \"\n          \"Column { \"\n            \"index: 0, \"\n            \"column_type: Fixed \"\n          \"}, \"\n          \"Column { \"\n            \"index: 0, \"\n            \"column_type: Advice \"\n          \"}, \"\n          \"Column { \"\n            \"index: 1, \"\n            \"column_type: Advice \"\n          \"}\"\n        \"] }, \"\n        \"lookups: [], \"\n        \"constants: [Column { \"\n          \"index: 0, \"\n          \"column_type: Fixed \"\n        \"}], \"\n        \"minimum_degree: None \"\n      \"}\";\n  // clang-format on\n\n  constexpr static std::string_view kAssemblyFixedColumns[][kN] = {\n      {\n          \"0x0000000000000000000000000000000000000000000000000000000000000007\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n      },\n  };\n\n  constexpr static AnyColumnKey kAssemblyPermutationColumns[] = {\n      InstanceColumnKey(0),\n      FixedColumnKey(0),\n      AdviceColumnKey(0),\n      AdviceColumnKey(1),\n  };\n\n  // clang-format off\n  constexpr static Label kCycleStoreMapping[][kN] = {\n      {{2, 1}, {0, 1},  {0, 2},  {0, 3},  {0, 4},  {0, 5},  {0, 6},  {0, 7},\n       {0, 8}, {0, 9}, {0, 10}, {0, 11}, {0, 12}, {0, 13}, {0, 14}, {0, 15}},\n      {{2, 0}, {1, 1},  {1, 2},  {1, 3},  {1, 4},  {1, 5},  {1, 6},  {1, 7},\n       {1, 8}, {1, 9}, {1, 10}, {1, 11}, {1, 12}, {1, 13}, {1, 14}, {1, 15}},\n      {{2, 6}, {0, 0},  {2, 5},  {3, 0},  {2, 8},  {3, 2},  {1, 0},  {3, 4},\n       {2, 4}, {2, 9}, {2, 10}, {2, 11}, {2, 12}, {2, 13}, {2, 14}, {2, 15}},\n      {{2, 3}, {3, 1},  {2, 2},  {3, 3},  {2, 7},  {3, 5},  {3, 6},  {3, 7},\n       {3, 8}, {3, 9}, {3, 10}, {3, 11}, {3, 12}, {3, 13}, {3, 14}, {3, 15}},\n  };\n\n  constexpr static Label kCycleStoreAux[][kN] = {\n      {{2, 1}, {0, 1},  {0, 2},  {0, 3},  {0, 4},  {0, 5},  {0, 6},  {0, 7},\n       {0, 8}, {0, 9}, {0, 10}, {0, 11}, {0, 12}, {0, 13}, {0, 14}, {0, 15}},\n      {{2, 0}, {1, 1},  {1, 2},  {1, 3},  {1, 4},  {1, 5},  {1, 6},  {1, 7},\n       {1, 8}, {1, 9}, {1, 10}, {1, 11}, {1, 12}, {1, 13}, {1, 14}, {1, 15}},\n      {{2, 0}, {2, 1},  {2, 2},  {3, 0},  {2, 4},  {2, 2},  {2, 0},  {3, 4},\n       {2, 4}, {2, 9}, {2, 10}, {2, 11}, {2, 12}, {2, 13}, {2, 14}, {2, 15}},\n      {{3, 0}, {3, 1},  {2, 2},  {3, 3},  {3, 4},  {3, 5},  {3, 6},  {3, 7},\n       {3, 8}, {3, 9}, {3, 10}, {3, 11}, {3, 12}, {3, 13}, {3, 14}, {3, 15}},\n  };\n  // clang-format on\n\n  constexpr static size_t kCycleStoreSizes[][kN] = {\n      {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},\n      {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},\n      {3, 2, 3, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},\n      {2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},\n  };\n\n  // clang-format off\n  constexpr static bool kSelectors[][kN] = {\n      {true, false, true, false, true, false, false, false,\n       false, false, false, false, false, false, false, false}\n  };\n  // clang-format on\n\n  // clang-format off\n  constexpr static std::string_view kPinnedVerifyingKey =\n      \"PinnedVerificationKey { \"\n        \"base_modulus: \\\"0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47\\\", \"\n        \"scalar_modulus: \\\"0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001\\\", \"\n        \"domain: PinnedEvaluationDomain { \"\n          \"k: 4, \"\n          \"extended_k: 5, \"\n          \"omega: 0x21082ca216cbbf4e1c6e4f4594dd508c996dfbe1174efb98b11509c6e306460b \"\n        \"}, \"\n        \"cs: PinnedConstraintSystem { \"\n          \"num_fixed_columns: 2, \"\n          \"num_advice_columns: 2, \"\n          \"num_instance_columns: 1, \"\n          \"num_selectors: 1, \"\n          \"gates: [Product(\"\n            \"Fixed { \"\n              \"query_index: 1, \"\n              \"column_index: 1, \"\n              \"rotation: Rotation(0) \"\n            \"}, \"\n            \"Sum(\"\n              \"Product(\"\n                \"Advice { \"\n                  \"query_index: 0, \"\n                  \"column_index: 0, \"\n                  \"rotation: Rotation(0) \"\n                \"}, \"\n                \"Advice { \"\n                  \"query_index: 1, \"\n                  \"column_index: 1, \"\n                  \"rotation: Rotation(0) \"\n                \"}\"\n              \"), \"\n              \"Negated(Advice { \"\n                \"query_index: 2, \"\n                \"column_index: 0, \"\n                \"rotation: Rotation(1) \"\n              \"})\"\n            \")\"\n          \")], \"\n          \"advice_queries: [\"\n            \"(\"\n              \"Column { \"\n                \"index: 0, \"\n                \"column_type: Advice \"\n              \"}, \"\n              \"Rotation(0)\"\n            \"), \"\n            \"(\"\n              \"Column { \"\n                \"index: 1, \"\n                \"column_type: Advice \"\n              \"}, \"\n              \"Rotation(0)\"\n            \"), \"\n            \"(\"\n              \"Column { \"\n                \"index: 0, \"\n                \"column_type: Advice \"\n              \"}, \"\n              \"Rotation(1)\"\n            \")\"\n          \"], \"\n          \"instance_queries: [(\"\n            \"Column { \"\n              \"index: 0, \"\n              \"column_type: Instance \"\n            \"}, \"\n            \"Rotation(0)\"\n          \")], \"\n          \"fixed_queries: [\"\n            \"(\"\n              \"Column { \"\n                \"index: 0, \"\n                \"column_type: Fixed \"\n              \"}, \"\n              \"Rotation(0)\"\n            \"), \"\n            \"(\"\n              \"Column { \"\n                \"index: 1, \"\n                \"column_type: Fixed \"\n              \"}, \"\n              \"Rotation(0)\"\n            \")\"\n          \"], \"\n          \"permutation: Argument { columns: [\"\n            \"Column { \"\n              \"index: 0, \"\n              \"column_type: Instance \"\n            \"}, \"\n            \"Column { \"\n              \"index: 0, \"\n              \"column_type: Fixed \"\n            \"}, \"\n            \"Column { \"\n              \"index: 0, \"\n              \"column_type: Advice \"\n            \"}, \"\n            \"Column { \"\n              \"index: 1, \"\n              \"column_type: Advice \"\n            \"}\"\n          \"] }, \"\n          \"lookups: [], \"\n          \"constants: [\"\n            \"Column { \"\n              \"index: 0, \"\n              \"column_type: Fixed \"\n            \"}\"\n          \"], \"\n          \"minimum_degree: None \"\n        \"}, \"\n        \"fixed_commitments: [\"\n          \"(0x0f9cc629d0010671d7f755267acccfc8d5854f47d4e437ec84bddcc27b9f19d1, \"\n            \"0x199b1dcc7aff518a4e49c6393bcf847cc3fde52ae69e326dea0d1d901424552a), \"\n          \"(0x00253ad9f42498136d40e617f9d46d11bf33256914aab88e32fd413bed9baccc, \"\n            \"0x154f7ddc2d7959e1abc7a0cada48f1b8f079c9fb45c6fdc2ee2121cc83a83b16)\"\n        \"], \"\n        \"permutation: VerifyingKey { commitments: [\"\n          \"(0x2f8d8133413e0224e1cbb20aa8281458fe0d9cf4d723ff07ac0524acffc8be48, \"\n            \"0x1f3e4dd4175e92cd38d3a8d3cd473e6daa6e9f28eb616222945904a6eadc52c6), \"\n          \"(0x27f329c25618696f90d09ab41e80f4f2c1e927a6d0f86d8670e81882a449af36, \"\n            \"0x1a69f0c1a2dee36915704c4fdee41ff8b7029c1894dc89356dd2cf4738e95df2), \"\n          \"(0x2a7f86208a87462b85678f6aee3c9c3ee17371167b436ad58501819ae02cbffd, \"\n            \"0x18b536eae002a69d57783fad5c63e35df0a2506dc43ee3d88053faff8f19cc40), \"\n          \"(0x1bb17f6d2c12d9c8cd8860c00fcadebf63986bb330dfde5b9097b17047a4dfc1, \"\n            \"0x1d6be3858a788f18e3c16ca8d3a88893f3678be9f827463af0f30d6b1f7df112)\"\n        \"] } \"\n      \"}\";\n  // clang-format on\n\n  constexpr static std::string_view kTranscriptRepr =\n      \"0x012577899026da8b4a257e25b4edf52711038e19083900a458e1c1e18c29eb08\";\n\n  constexpr static std::string_view kLFirst[] = {\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n  };\n\n  constexpr static std::string_view kLLast[] = {\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2014447de15a99b6df03833e95f96ae1299c9ec6ff990b6e75fa3b3b04846a57\",\n      \"0x0f1f5883e65f820d14d56342dc92fd12a944d4cbbdce5377b7439bd07108fc9d\",\n      \"0x02b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e8\",\n      \"0x030644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000\",\n      \"0x105009f4ffd70672d94cc277eb87ed7bfe9749817a206522cde7ba58eb7b95aa\",\n      \"0x2144f5eefad21e1ca37ae273a4ee5b4a7eef137cbbeb1d198c9e59c37ef70364\",\n      \"0x2db11694c4a58b3789868bd388165969c30dc1120a37fff09a991abf43e42a19\",\n      \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n      \"0x2014447de15a99b6df03833e95f96ae1299c9ec6ff990b6e75fa3b3b04846a57\",\n      \"0x0f1f5883e65f820d14d56342dc92fd12a944d4cbbdce5377b7439bd07108fc9d\",\n      \"0x02b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e8\",\n      \"0x030644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000\",\n      \"0x105009f4ffd70672d94cc277eb87ed7bfe9749817a206522cde7ba58eb7b95aa\",\n      \"0x2144f5eefad21e1ca37ae273a4ee5b4a7eef137cbbeb1d198c9e59c37ef70364\",\n      \"0x2db11694c4a58b3789868bd388165969c30dc1120a37fff09a991abf43e42a19\",\n  };\n\n  constexpr static std::string_view kLActiveRow[] = {\n      \"0x12259d6b14729c0fa51e1a2470908122ef13771b2da58a367974bc177a000001\",\n      \"0x1a8133201ba2fe22aff30c7fd04fadcb17ceab0715a13371b06d35acd9695b3d\",\n      \"0x0d49c50dd1c3ec703dc7be1c836fd7f62c140afcf284ce19b9a99affac7b95aa\",\n      \"0x117dae38a05bcb8c7c290ee16cec493d73ef8dafa5c61fa4a2efd9a39e63abf4\",\n      \"0x0c19139cb84c680a79505ee7747ae78cd6c196473632bc6ea3057c773208fc9d\",\n      \"0x136156e428a2662bc2fddfd3b39f6475dafecb8699a611f3da6edd22c3479af1\",\n      \"0x2aaad1ad96927134ee0187781ffe43e3f08a828d829c68e7865afb6604e42a19\",\n      \"0x28d7d254c17b7ea40ebc4659996adacebd0d8f52d021284040d407c2f33b896f\",\n      \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n      \"0x1ef7221577b1e8f8cebdcb2fcd10296b6d9d43267b395c820757c0fc87681d81\",\n      \"0x1d0dff96b3477fb4437e7ee32de1555b5719604277fd746561bc1be1c5846a57\",\n      \"0x1be193603aacb384678281793fa8f20b949cf5976d9493017ba8a357ee49da57\",\n      \"0x1e3eb107ccbf041a07f5de183cd645c4ac6bd4f8344f861078603a6a3ff70364\",\n      \"0x20080468beb85b16c9f71b3ea2ce10fb6cdc81c346721c888ebc9109900adec6\",\n      \"0x30114169cfaa9b194b94fb3e12d441cabad6d0fa619f4a28d8ecb10f5d1bd5e9\",\n      \"0x2edcc3ce4ec47abd9b83b31a4db9571236223b590c30997fd30ce24f7bf2fdd6\",\n  };\n\n  constexpr static std::string_view kFixedColumns[][kN] = {\n      {\n          \"0x0000000000000000000000000000000000000000000000000000000000000007\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n      },\n      {\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n      },\n  };\n\n  constexpr static std::string_view kFixedPolys[][kN] = {\n      {\n          \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n          \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n          \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n          \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n          \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n          \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n          \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n          \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n          \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n          \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n          \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n          \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n          \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n          \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n          \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n          \"0x1b386c209eabea1777ad2736a8d8c1b4669d32a8c4784f51b62f1a2337000001\",\n      },\n      {\n          \"0x27517fbd56f85221e5c138a4493917cbb0aa2cbae2e6ab760727978833000001\",\n          \"0x1c691d91b8236e7d529d215f6002d508d558dfc8b053219170ed36d01d849247\",\n          \"0x0f1f5883e65f820d14d56342dc92fd12a944d4cbbdce5377b7439bd07108fc9d\",\n          \"0x1b8b7929b032ef27d92c2435436b46d14745adc1c4ce156fcf175f9593db2d7c\",\n          \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n          \"0x2c2d581a99a701c15853e2260a74526e005e350e35cbc7dd18ffb7b2368d66f4\",\n          \"0x2144f5eefad21e1ca37ae273a4ee5b4a7eef137cbbeb1d198c9e59c37ef70364\",\n          \"0x20f1e8e5e94b190c36bf97fb364144b81191fc2ea3d0f6b121a9153eec12d94c\",\n          \"0x27517fbd56f85221e5c138a4493917cbb0aa2cbae2e6ab760727978833000001\",\n          \"0x1c691d91b8236e7d529d215f6002d508d558dfc8b053219170ed36d01d849247\",\n          \"0x0f1f5883e65f820d14d56342dc92fd12a944d4cbbdce5377b7439bd07108fc9d\",\n          \"0x1b8b7929b032ef27d92c2435436b46d14745adc1c4ce156fcf175f9593db2d7c\",\n          \"0x2d5e098bb31e86271ccb415b196942d755b0a9c3f21dd9882fa3d63ab1000001\",\n          \"0x2c2d581a99a701c15853e2260a74526e005e350e35cbc7dd18ffb7b2368d66f4\",\n          \"0x2144f5eefad21e1ca37ae273a4ee5b4a7eef137cbbeb1d198c9e59c37ef70364\",\n          \"0x20f1e8e5e94b190c36bf97fb364144b81191fc2ea3d0f6b121a9153eec12d94c\",\n      },\n  };\n\n  constexpr static std::string_view kPermutationsColumns[][kN] = {\n      {\n          \"0x08e7cbfea108224b0777f0558503af41585b75ab8d4d807505158f4bc8c771de\",\n          \"0x21082ca216cbbf4e1c6e4f4594dd508c996dfbe1174efb98b11509c6e306460b\",\n          \"0x2b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e80\",\n          \"0x107aab49e65a67f9da9cd2abf78be38bd9dc1d5db39f81de36bcfa5b4b039043\",\n          \"0x30644e72e131a029048b6e193fd841045cea24f6fd736bec231204708f703636\",\n          \"0x2290ee31c482cf92b79b1944db1c0147635e9004db8c3b9d13644bef31ec3bd3\",\n          \"0x1d59376149b959ccbd157ac850893a6f07c2d99b3852513ab8d01be8e846a566\",\n          \"0x2d8040c3a09c49698c53bfcb514d55a5b39e9b17cb093d128b8783adb8cbd723\",\n          \"0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000\",\n          \"0x0f5c21d0ca65e0db9be1f670eca407d08ec5ec67626a74f892ccebcd0cf9b9f6\",\n          \"0x0530d09118705106cbb4a786ead16926d5d174e181a26686af5448492e42a181\",\n          \"0x1fe9a328fad7382fddb3730a89f574d14e57caeac619eeb30d24fb38a4fc6fbe\",\n          \"0x0000000000000000b3c4d79d41a91758cb49c3517c4604a520cff123608fc9cb\",\n          \"0x0dd360411caed09700b52c71a6655715c4d558439e2d34f4307da9a4be13c42e\",\n          \"0x130b17119778465cfb3acaee30f81dee20710ead41671f568b11d9ab07b95a9b\",\n          \"0x02e40daf409556c02bfc85eb303402b774954d30aeb0337eb85a71e6373428de\",\n      },\n      {\n          \"0x13b360d4e82fe915fed16081038f98c211427b87a281bd733c277dbadf10372b\",\n          \"0x133f51f46c2a51201fbb89dc1d6868acfef0f7ce0e572ccb98e04e99eac3ea36\",\n          \"0x1240a374ee6f71e12df10a129946ca9ba824f58a50e3a58b4c68dd5590b74ad8\",\n          \"0x009553089042fe83ab570b54e4e64d307d8e8b20d568e782ece3926981d0a96c\",\n          \"0x14a6c152ace4b16a42e1377e400798e15ac60320c21d90277890dbdd551d1912\",\n          \"0x035992598be4d2ae5334f24f30355a1fca7f0e28762a178d79e993dca70eaabf\",\n          \"0x03b645319eb70d7a692ea8c87fbcab1f292cd502071d9ffb7291ebe28356aa07\",\n          \"0x231e38741f5c3f0ce2509bd3289b09a893b14b136eea6ed00cec9ac9271c9db5\",\n          \"0x2741e304be6aaf5f53641f0bacb8e9ebccd45eba1b23316bbcd39ed80acc165f\",\n          \"0x1d24fc7e75074f099894bbda6418efb02942f07a6b6243c5ab01a6fa053c15cb\",\n          \"0x1e23aafdf2c22e488a5f3ba3e83a8dc1800ef2be28d5cb05f779183e5f48b529\",\n          \"0x2fcefb6a50eea1a60cf93a619c9b0b2caaa55d27a450890e56fe632a6e2f5695\",\n          \"0x1bbd8d20344ceebf756f0e384179bf7bcd6de527b79be069cb5119b69ae2e6ef\",\n          \"0x2d0abc19554ccd7b651b5367514bfe3d5db4da20038f5903c9f861b748f15542\",\n          \"0x2cae0941427a92af4f219cee01c4ad3dff071346729bd095d15009b16ca955fa\",\n          \"0x0d4615fec1d5611cd5ffa9e358e64eb494829d350acf01c136f55acac8e3624c\",\n      },\n      {\n          \"0x2e39c17d3e15071a9341cfcea233ad5f25a14dea28716de88ae369ac2c9944ac\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x04765b5102d6627cfcc389436e96bbc9aa478dcb720b546c77063077a40910ce\",\n          \"0x18afdf23e9bd9302673fc1e076a492d4d65bd18ebc4d854ed189139bab313e52\",\n          \"0x1cb0ed9df901b713b97ee5357df1bf9b16f16cc0d737b31e07ba77d910efc8d6\",\n          \"0x034183d253b6b250dae0a457797029523434d2b6bc41c09b6ef409bd970e4208\",\n          \"0x09226b6e22c6f0ca64ec26aad4c86e715b5f898e5e963f25870e56bbe533e9a2\",\n          \"0x2f549305063b1803a77ba92486c5383e00468fc857ec66d944868c4a309e596a\",\n          \"0x086398ace043cf0db4e99f2712f392ddbd9c7b9202b2fcadf26e9b145316df9f\",\n          \"0x277c827440297ddeb0d85560fc7da91bcfd8729cec6bf01c3ecc664827388e23\",\n          \"0x24f4c8596963484c0569feb1f2a79f19eb87843cd95fd26af5bdb12c8a26ea2e\",\n          \"0x096b10d95e053da3dea44cf0c8ea6de7e962b0f71046aab3779baa3d2b772a01\",\n          \"0x2800b5c600edd11c0366a68f6e8dc57f6a976cb6770673e351735a7f9ce92062\",\n          \"0x2bedf321de5b3dacbb8cbc7312ea9c937dec5a7d07ae1c24ccdbc51c4bf6ef33\",\n          \"0x022a8cf5a31c990f250e75e7df4daafe02929a5e514802a8b8fe8be7c366bb55\",\n          \"0x06272e83847dd219527e22d89b87a6efdf7fc2b0f32d564762853d937378875d\",\n      },\n      {\n          \"0x26f93d99832c6285d9abf8c5b896ea753ed137516972c5ddcc464b56c488d600\",\n          \"0x2f0e061e83e8b12c1bdf8df7cca02483295e89f78a462ce38859661e06501815\",\n          \"0x0b6f861977ce57ddb2e647048ed9b9433cac640ba0599e264e24446765d915d3\",\n          \"0x0f34fda7cd268bf095354ea6c2631826c349d7518bf094361bc91f61c519c7fb\",\n          \"0x2a3d1fef5cb3ce1065d222dde5f9b16d48b42597868c1a49e15cb8007c8778a4\",\n          \"0x1e626bf9ef3c8920522383ff9be21287c3af8d47fe61ff9af6c6d8d0118154bc\",\n          \"0x02898db49f7022cac0aeabf721a3c7ab96e14ad35e7d6b0410b868ff504b62fc\",\n          \"0x2a150ee7450ad90cace368ea424c8fdf36b3821e79f5f1c617a5f8e74c19b4cf\",\n          \"0x17b46f4ef7740d27511083d60adcc58851d816b9bd6beb427258e1f844cec1af\",\n          \"0x015648545d48eefd9c70b7beb4e133d9fed55e50ef7343adbb888f75e9afe7ec\",\n          \"0x2d22caa08d7aedd8dd6fa15f08112f0af3ff1591bd77aff5d4edebd658f1bdf9\",\n          \"0x212f50cb140b1439231af70fbf1e403664ea10f6edc8dc5b2818d6322ae63806\",\n          \"0x010fbb6ddaf6882610d49c91fabc201f27ed588021cd09b7ff5b6949bf61a697\",\n          \"0x1201e278f1f51709662cc1b6e59f45d564845b007b5770f64d1b1cc3de7eab45\",\n          \"0x2ddac0be41c17d5ef7a199bf5fdd90b191529d751b3c058d33298c949fb49d05\",\n          \"0x064f3f8b9c26c71d0b6cdccc3f34c87df1806629ffc37ecb2c3bfcaca3e64b32\",\n      },\n  };\n\n  constexpr static std::string_view kPermutationsPolys[][kN] = {\n      {\n          \"0x09a14b757449d02c83068c1790987b858d0f72e84fa79d228d0bb700798c771e\",\n          \"0x09a14b757449d02c83068c1790987b858d0f72e84fa79d228d0bb700798c771f\",\n          \"0x09a14b757449d02c83068c1790987b858d0f72e84fa79d228d0bb700798c771e\",\n          \"0x09a14b757449d02c83068c1790987b858d0f72e84fa79d228d0bb700798c771e\",\n          \"0x09a14b757449d02c83068c1790987b858d0f72e84fa79d228d0bb700798c771e\",\n          \"0x09a14b757449d02c83068c1790987b858d0f72e84fa79d228d0bb700798c771e\",\n          \"0x09a14b757449d02c83068c1790987b858d0f72e84fa79d228d0bb700798c771e\",\n          \"0x09a14b757449d02c83068c1790987b858d0f72e84fa79d228d0bb700798c771e\",\n          \"0x09a14b757449d02c83068c1790987b858d0f72e84fa79d228d0bb700798c771e\",\n          \"0x09a14b757449d02c83068c1790987b858d0f72e84fa79d228d0bb700798c771e\",\n          \"0x09a14b757449d02c83068c1790987b858d0f72e84fa79d228d0bb700798c771e\",\n          \"0x09a14b757449d02c83068c1790987b858d0f72e84fa79d228d0bb700798c771e\",\n          \"0x09a14b757449d02c83068c1790987b858d0f72e84fa79d228d0bb700798c771e\",\n          \"0x09a14b757449d02c83068c1790987b858d0f72e84fa79d228d0bb700798c771e\",\n          \"0x09a14b757449d02c83068c1790987b858d0f72e84fa79d228d0bb700798c771e\",\n          \"0x09a14b757449d02c83068c1790987b858d0f72e84fa79d228d0bb700798c771e\",\n      },\n      {\n          \"0x15d4f1a8aedc4596fa41721d3b95094dccf4e4bf497fd92469046de0a89dc4d9\",\n          \"0x1ef75d16d1a336615f2d98c8105d77bf28546e4da8161849f012c49c8dd1ae7b\",\n          \"0x15d4f1a8aedc4596fa41721d3b95094dccf4e4bf497fd92469046de0a89dc4d9\",\n          \"0x15d4f1a8aedc4596fa41721d3b95094dccf4e4bf497fd92469046de0a89dc4d9\",\n          \"0x15d4f1a8aedc4596fa41721d3b95094dccf4e4bf497fd92469046de0a89dc4d9\",\n          \"0x15d4f1a8aedc4596fa41721d3b95094dccf4e4bf497fd92469046de0a89dc4d9\",\n          \"0x15d4f1a8aedc4596fa41721d3b95094dccf4e4bf497fd92469046de0a89dc4d9\",\n          \"0x15d4f1a8aedc4596fa41721d3b95094dccf4e4bf497fd92469046de0a89dc4d9\",\n          \"0x15d4f1a8aedc4596fa41721d3b95094dccf4e4bf497fd92469046de0a89dc4d9\",\n          \"0x15d4f1a8aedc4596fa41721d3b95094dccf4e4bf497fd92469046de0a89dc4d9\",\n          \"0x15d4f1a8aedc4596fa41721d3b95094dccf4e4bf497fd92469046de0a89dc4d9\",\n          \"0x15d4f1a8aedc4596fa41721d3b95094dccf4e4bf497fd92469046de0a89dc4d9\",\n          \"0x15d4f1a8aedc4596fa41721d3b95094dccf4e4bf497fd92469046de0a89dc4d9\",\n          \"0x15d4f1a8aedc4596fa41721d3b95094dccf4e4bf497fd92469046de0a89dc4d9\",\n          \"0x15d4f1a8aedc4596fa41721d3b95094dccf4e4bf497fd92469046de0a89dc4d9\",\n          \"0x15d4f1a8aedc4596fa41721d3b95094dccf4e4bf497fd92469046de0a89dc4d9\",\n      },\n      {\n          \"0x18eae12fd704e5bc3d0acf5f32330e0f3a23a3600b66f24ce31ff4dcd7950b5f\",\n          \"0x1668c537404f9338905d9b885be857f7927a91f29c6b623e5d744fb232c8c7cb\",\n          \"0x0d1bfa0c9869dfa02919a0d7834f3f2a4d64218aa8582ca4b43c029a435d42f1\",\n          \"0x2ceb4c0d1b5b06357be3af62312632f070a6eac793edb5e815c743e973aaab7d\",\n          \"0x2c814b07e5b0482b27173e2370c3a08662204993e2d8fdb097c17c0a93fb2ebd\",\n          \"0x11656a996c4ae9762c3899876dbeeef8c9638869fa4a66dfe73e4b59685040a3\",\n          \"0x1753fa2f3bf232558b65d8726f2ce1d9b092400c892a93d51017051b72c4aa7e\",\n          \"0x231490648c345450dd40f890419598f601d543b3e319fcacf23a9d9ecb819762\",\n          \"0x092f15a06a9434ad833065f6c50caf3d5afa51cf219ce91f1e429bf444f56a50\",\n          \"0x0b7e67f7e58f3f373c2e9a9cae2e7c1b3a9b8571964ae0cab3e7568106eaa6e0\",\n          \"0x2053b23f55828893e49b77ac55a9d0587fa75004919673d04e07bf0940e88120\",\n          \"0x2f56ca1e5a468ea9c03068b5f43d501484a981ddfde12ad4f4baa97b2144f9f8\",\n          \"0x0cb5df9af846bd3540562259624801f754344c5466a397fef1565be386dc9ef6\",\n          \"0x19ac12db90e5885a5224538b937cbcf1298034e8dde3eda38e033b460ce6afa9\",\n          \"0x2498f7b8d9eb6152686b24b08d67043db5445508860199e20f6aae67d96b6038\",\n          \"0x205f2438ff62bf51c8571e289c1a7eec31cc7961b6d43e347a59808f956596bd\",\n      },\n      {\n          \"0x28677e97c83844d3b64dbdd904a21dd7bc3fd5894ee4788eae93d169e640b8ac\",\n          \"0x0b09ea89c6731047056d4cde1f39228a9a88b04ffa4b7db592540476c69a20d5\",\n          \"0x22c644b2532a4b71b8545dde2743e1d1b73b6b0426942d10912538e47312096d\",\n          \"0x118e682968bef69c68c23668dfb585f320bad9da0b5d89d01a581bd198d8779d\",\n          \"0x09230d4716f64c08081addf15104149f62107231664d96828840909836675e32\",\n          \"0x296a7370fa3f06b11a95a596e8a8b260ea0a6ed3aa2810059fd557dda2ed0369\",\n          \"0x283c4b2b66fee7fbb2a6c2591755de8829136b66ffd2ad6d206a98fa0e9bc5a3\",\n          \"0x1ed5e6497872a98d4f8e0f4da1cbd26a07790e6e6e5be6c12989d9c257278864\",\n          \"0x28677e97c83844d3b64dbdd904a21dd7bc3fd5894ee4788eae93d169e640b8ac\",\n          \"0x22be59d8bde71d6e567dd0b42a15e812ec60c709b7b768f804ace66f0b68e284\",\n          \"0x22c644b2532a4b71b8545dde2743e1d1b73b6b0426942d10912538e47312096d\",\n          \"0x118e682968bef69c68c23668dfb585f320bad9da0b5d89d01a581bd198d8779d\",\n          \"0x09230d4716f64c08081addf15104149f62107231664d96828840909836675e32\",\n          \"0x296a7370fa3f06b11a95a596e8a8b260ea0a6ed3aa2810059fd557dda2ed0369\",\n          \"0x283c4b2b66fee7fbb2a6c2591755de8829136b66ffd2ad6d206a98fa0e9bc5a3\",\n          \"0x1ed5e6497872a98d4f8e0f4da1cbd26a07790e6e6e5be6c12989d9c257278864\",\n      },\n  };\n\n  constexpr static uint8_t kProof[] = {\n      19,  29,  49,  75,  8,   191, 254, 126, 107, 125, 58,  193, 235, 208, 41,\n      102, 196, 144, 108, 7,   165, 223, 43,  109, 53,  216, 237, 43,  184, 227,\n      83,  9,   184, 235, 153, 217, 206, 69,  132, 165, 210, 205, 116, 237, 238,\n      162, 49,  49,  204, 46,  223, 58,  73,  229, 128, 143, 213, 188, 0,   212,\n      5,   85,  163, 5,   19,  29,  49,  75,  8,   191, 254, 126, 107, 125, 58,\n      193, 235, 208, 41,  102, 196, 144, 108, 7,   165, 223, 43,  109, 53,  216,\n      237, 43,  184, 227, 83,  9,   184, 235, 153, 217, 206, 69,  132, 165, 210,\n      205, 116, 237, 238, 162, 49,  49,  204, 46,  223, 58,  73,  229, 128, 143,\n      213, 188, 0,   212, 5,   85,  163, 5,   127, 225, 164, 3,   197, 222, 99,\n      117, 232, 31,  74,  110, 227, 32,  48,  192, 153, 241, 171, 52,  191, 26,\n      68,  45,  197, 13,  113, 23,  246, 13,  231, 166, 64,  66,  93,  243, 151,\n      203, 112, 88,  120, 207, 230, 170, 173, 254, 252, 207, 63,  239, 95,  136,\n      10,  226, 56,  39,  154, 39,  76,  240, 18,  10,  142, 28,  151, 164, 103,\n      32,  254, 100, 230, 86,  82,  1,   70,  1,   180, 136, 218, 38,  91,  237,\n      148, 213, 248, 198, 195, 125, 183, 139, 13,  25,  173, 78,  126, 142, 94,\n      35,  73,  89,  164, 177, 165, 40,  196, 91,  142, 100, 101, 174, 88,  101,\n      62,  118, 77,  113, 64,  35,  18,  251, 201, 60,  109, 41,  87,  12,  233,\n      144, 194, 190, 107, 221, 112, 231, 78,  27,  96,  65,  41,  108, 62,  155,\n      56,  146, 132, 59,  154, 117, 155, 164, 168, 171, 246, 216, 93,  239, 223,\n      204, 133, 2,   39,  160, 123, 232, 7,   238, 13,  107, 196, 241, 124, 154,\n      169, 67,  116, 166, 56,  151, 214, 194, 186, 125, 118, 163, 199, 247, 59,\n      24,  208, 119, 122, 163, 116, 241, 113, 160, 218, 185, 1,   57,  204, 40,\n      197, 69,  109, 184, 245, 200, 155, 210, 27,  184, 180, 198, 202, 51,  4,\n      84,  171, 139, 243, 38,  69,  151, 122, 54,  14,  56,  136, 105, 156, 65,\n      234, 163, 12,  136, 101, 96,  225, 135, 193, 103, 253, 5,   183, 161, 240,\n      107, 76,  93,  63,  255, 193, 89,  223, 140, 1,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   103, 21,  209, 146,\n      1,   82,  113, 58,  64,  67,  95,  172, 141, 84,  72,  42,  229, 27,  151,\n      165, 6,   201, 90,  138, 39,  146, 238, 171, 253, 188, 2,   142, 65,  69,\n      245, 48,  124, 53,  52,  41,  169, 118, 173, 194, 123, 190, 197, 89,  126,\n      206, 19,  99,  193, 187, 33,  64,  235, 33,  161, 242, 64,  224, 182, 28,\n      232, 144, 93,  25,  237, 225, 238, 156, 100, 254, 231, 202, 148, 15,  107,\n      121, 62,  162, 160, 226, 233, 110, 146, 4,   107, 126, 136, 195, 38,  162,\n      61,  11,  204, 20,  246, 68,  156, 199, 230, 119, 41,  240, 125, 4,   175,\n      162, 29,  6,   233, 171, 69,  177, 59,  119, 202, 32,  119, 74,  195, 20,\n      71,  46,  58,  15,  43,  106, 225, 52,  175, 228, 9,   113, 36,  132, 251,\n      50,  157, 212, 147, 80,  135, 1,   153, 128, 227, 208, 221, 153, 4,   6,\n      147, 247, 174, 180, 129, 14,  232, 144, 93,  25,  237, 225, 238, 156, 100,\n      254, 231, 202, 148, 15,  107, 121, 62,  162, 160, 226, 233, 110, 146, 4,\n      107, 126, 136, 195, 38,  162, 61,  11,  204, 20,  246, 68,  156, 199, 230,\n      119, 41,  240, 125, 4,   175, 162, 29,  6,   233, 171, 69,  177, 59,  119,\n      202, 32,  119, 74,  195, 20,  71,  46,  58,  15,  43,  106, 225, 52,  175,\n      228, 9,   113, 36,  132, 251, 50,  157, 212, 147, 80,  135, 1,   153, 128,\n      227, 208, 221, 153, 4,   6,   147, 247, 174, 180, 129, 14,  142, 196, 63,\n      34,  8,   2,   236, 221, 118, 2,   32,  13,  252, 166, 9,   145, 155, 143,\n      102, 87,  176, 241, 131, 217, 131, 153, 128, 123, 100, 68,  113, 39,  56,\n      59,  88,  212, 132, 43,  80,  80,  37,  252, 240, 215, 66,  33,  244, 204,\n      123, 180, 101, 62,  147, 229, 119, 148, 44,  47,  148, 95,  174, 78,  83,\n      38,  1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   44,  186, 67,  191, 236, 7,   211, 217, 169, 158, 174, 166,\n      95,  237, 145, 220, 157, 191, 24,  173, 91,  163, 2,   200, 171, 0,   1,\n      53,  138, 1,   245, 11,  182, 224, 189, 181, 200, 187, 146, 227, 182, 188,\n      15,  122, 71,  167, 113, 161, 114, 112, 233, 133, 169, 77,  167, 32,  163,\n      114, 130, 13,  56,  30,  155, 8,   11,  254, 248, 201, 168, 128, 166, 180,\n      238, 139, 27,  248, 22,  63,  236, 216, 177, 33,  29,  4,   140, 225, 157,\n      127, 31,  92,  70,  103, 251, 233, 142, 32,  152, 76,  190, 44,  81,  109,\n      249, 211, 91,  97,  187, 223, 199, 161, 96,  71,  36,  224, 67,  110, 244,\n      243, 210, 104, 60,  247, 95,  236, 28,  254, 54,  1,   213, 221, 250, 122,\n      212, 75,  9,   33,  153, 25,  10,  37,  210, 5,   8,   142, 138, 210, 147,\n      191, 223, 194, 177, 166, 122, 215, 220, 20,  242, 45,  129, 4,   161, 99,\n      235, 177, 225, 132, 9,   80,  54,  196, 18,  88,  39,  126, 31,  240, 223,\n      255, 126, 92,  55,  141, 8,   220, 239, 201, 149, 213, 115, 140, 232, 7,\n      120, 84,  236, 42,  248, 14,  141, 0,   5,   50,  135, 194, 233, 85,  16,\n      2,   232, 91,  220, 83,  33,  22,  176, 113, 174, 193, 230, 98,  176, 183,\n      227, 42,  40,  74,  92,  28,  234, 158, 50,  106, 46,  37,  185, 160, 129,\n      227, 84,  45,  124, 145, 236, 111, 45,  101, 176, 249, 147, 58,  137, 55,\n      251, 58,  172, 8,   53,  217, 33,  179, 247, 171, 33,  154, 230, 219, 102,\n      98,  51,  94,  42,  222, 177, 197, 235, 8,   128, 33,  106, 181, 141, 53,\n      45,  62,  223, 217, 193, 13,  97,  58,  116, 65,  181, 178, 45,  136, 124,\n      144, 79,  119, 186, 107, 198, 163, 108, 15,  22,  134, 226, 153, 88,  102,\n      52,  121, 185, 189, 205, 123, 75,  19,  192, 203, 139, 211, 156, 110, 67,\n      191, 174, 243, 221, 136, 44,  20,  126, 162, 18,  227, 98,  160, 188, 217,\n      234, 18,  20,  104, 214, 254, 40,  112, 211, 18,  122, 245, 183, 30,  198,\n      129, 160, 62,  219, 111, 140, 121, 38,  57,  214, 37,  223, 154, 190, 249,\n      124, 195, 141, 142, 236, 188, 199, 101, 162, 65,  234, 24,  96,  181, 126,\n      246, 252, 107, 26,  142, 140, 101, 123, 104, 216, 84,  11,  113, 221, 144,\n      66,  96,  50,  225, 185, 255, 201, 18,  223, 192, 222, 128, 79,  16,  125,\n      89,  221, 50,  95,  246, 91,  196, 74,  6,   119, 180, 172, 85,  157, 210,\n      196, 227, 140, 43,  57,  115, 40,  252, 155, 118, 40,  59,  164, 243, 234,\n      39,  10,  236, 201, 162, 65,  195, 134, 15,  147, 117, 241, 184, 28,  8,\n      189, 70,  207, 161, 188, 206, 167, 11,  33,  188, 238, 37,  121, 146, 32,\n      153, 13,  45,  67,  30,  159, 171, 62,  53,  42,  44,  135, 15,  34,  48,\n      186, 181, 40,  54,  17,  2,   11,  26,  49,  48,  126, 76,  104, 57,  151,\n      38,  59,  87,  46,  19,  191, 84,  121, 223, 179, 241, 158, 143, 0,   183,\n      159, 67,  65,  137, 109, 224, 180, 218, 133, 51,  200, 152, 77,  76,  8,\n      134, 207, 91,  31,  102, 230, 21,  124, 131, 21,  228, 218, 145, 179, 121,\n      144, 161, 120, 160, 219, 220, 197, 177, 46,  16,  184, 39,  160, 31,  150,\n      232, 255, 1,   172, 7,   192, 47,  147, 13,  112, 126, 39,  227, 226, 77,\n      246, 2,   127, 76,  4,   65,  20,  72,  174, 226, 9,   85,  222, 18,  119,\n      57,  61,  127, 160, 120, 159, 64,  95,  142, 206, 27,  177, 71,  186, 98,\n      62,  77,  221, 242, 183, 216, 211, 123, 14,  46,  235, 88,  55,  121, 13,\n      192, 250, 25,  4,   229, 43,  45,  40,  141, 98,  1,   118, 25,  20,  183,\n      51,  17,  131, 13,  80,  92,  203, 71,  35,  30,  242, 124, 253, 1,   145,\n      242, 0,   72,  5,   232, 37,  235, 253, 114, 217, 100, 152, 94,  103, 40,\n      4,   85,  204, 35,  109, 114, 118, 5,   34,  214, 2,   182, 236, 47,  124,\n      96,  230, 40,  13,  171, 214, 160, 183, 205, 171, 53,  2,   200, 225, 0,\n      12,  8,   139, 247, 90,  9,   250, 149, 235, 35,  101, 92,  2,   48,  135,\n      6,   210, 146, 7,   143, 136, 217, 119, 66,  116, 22,  66,  151, 77,  102,\n      125, 36,  62,  24,  159, 100, 74,  215, 27,  51,  90,  186, 10,  219, 218,\n      21,  218, 206, 157, 27,  62,  68,  250, 70,  205, 164, 34,  215, 124, 203,\n      45,  187, 171, 131, 36,  1,   106, 159, 99,  124, 92,  119, 72,  9,   156,\n      231, 150, 98,  128, 74,  20,  148, 152, 87,  89,  197, 87,  249, 99,  188,\n      30,  171, 156, 61,  250, 148, 254, 37,  239, 44,  64,  123, 239, 75,  100,\n      82,  196, 227, 147, 7,   251, 34,  5,   66,  222, 147, 110, 235, 124, 40,\n      45,  83,  188, 74,  140, 109, 254, 39,  178, 29,  2,   74,  21,  36,  248,\n      8,   246, 86,  22,  104, 204, 126, 222, 205, 217, 61,  34,  27,  76,  75,\n      209, 54,  81,  170, 235, 3,   40,  82,  255, 71,  214, 15,  195, 232, 94,\n      47,  170, 193, 202, 105, 194, 8,   140, 170, 93,  209, 106, 51,  83,  97,\n      19,  62,  235, 15,  93,  126, 44,  134, 222, 205, 194, 84,  181, 33};\n\n  // clang-format off\n  constexpr static Point kAdviceCommitments[][2] = {\n      {\n          {\"0x0953e3b82bedd8356d2bdfa5076c90c46629d0ebc13a7d6b7efebf084b311d13\",\n            \"0x0e1846fc46b7f84859cf41eabe2cfadf1c08f6be2df3fd75f47dd9945ecdea66\"},\n          {\"0x05a35505d400bcd58f80e5493adf2ecc3131a2eeed74cdd2a58445ced999ebb8\",\n            \"0x167f11ce9f3d3ebc24fe1a9d722ffdc7ee94a1734d544837868c7ccd7de960f0\"},\n      },\n      {\n          {\"0x0953e3b82bedd8356d2bdfa5076c90c46629d0ebc13a7d6b7efebf084b311d13\",\n            \"0x0e1846fc46b7f84859cf41eabe2cfadf1c08f6be2df3fd75f47dd9945ecdea66\"},\n          {\"0x05a35505d400bcd58f80e5493adf2ecc3131a2eeed74cdd2a58445ced999ebb8\",\n            \"0x167f11ce9f3d3ebc24fe1a9d722ffdc7ee94a1734d544837868c7ccd7de960f0\"},\n      },\n  };\n  // clang-format on\n\n  constexpr static std::string_view kTheta =\n      \"0x1bbbaa33ab11f8073b8fd7cd635376bf76535666874fed4eda0aa27081c7224d\";\n\n  constexpr static std::string_view kBeta =\n      \"0x2ea3ec52fc8914364d92275b8e2cc142ff57c1833be6ff1010c0afcb49e418f5\";\n\n  constexpr static std::string_view kGamma =\n      \"0x1b9c1308750907b62d2039a8a1ab39aef60f44d5c0f983c8996a02f282eb6a67\";\n\n  // clang-format off\n  constexpr static Point kPermutationProductCommitments[][4] = {\n      {\n          {\"0x26e70df617710dc52d441abf34abf199c03020e36e4a1fe87563dec503a4e17f\",\n           \"0x12b60345e427c9b7bedabe0582f8ea1757afb34a2aeb7221c111cb809a58b645\"},\n          {\"0x1c8e0a12f04c279a2738e20a885fef3fcffcfeadaae6cf785870cb97f35d4240\",\n           \"0x00cbb8c3b8d1c13de7fbb52cce108e8eac9d25bfdfbe1d34e7f9bda4b5ba493a\"},\n          {\"0x0e7e4ead190d8bb77dc3c6f8d594ed5b26da88b40146015256e664fe2067a497\",\n           \"0x2bec88861fe25d0e544588e1ce1a37dbce9bcbca2d36a6d207806c6feda7ebcb\"},\n          {\"0x10e90c57296d3cc9fb122340714d763e6558ae65648e5bc428a5b1a45949235e\",\n           \"0x10b67dc003e08b6cc40bae249415357be1bf15c198f4e717f6d2b709a4b5ca35\"},\n      },\n      {\n          {\"0x0285ccdfef5dd8f6aba8a49b759a3b8492389b3e6c2941601b4ee770dd6bbec2\",\n           \"0x18c3f0b71002cdda9e5896851e5bd3ab3d4739abf891dccb8680e5c9115ee004\"},\n          {\"0x237a77d0183bf7c7a3767dbac2d69738a67443a99a7cf1c46b0dee07e87ba027\",\n           \"0x1d66dfa81519f0b2e109ccda3604ac8ad0bcfc22bc5b4817da3b54dde01e9545\"},\n          {\"0x174526f38bab540433cac6b4b81bd29bc8f5b86d45c528cc3901b9daa071f174\",\n           \"0x2097a71126e3deec5aa3f47ed4c382f86589bd40eb10a2fa9451b08be5455d79\"},\n          {\"0x0cdf59c1ff3f5d4c6bf0a1b705fd67c187e16065880ca3ea419c6988380e367a\",\n           \"0x2310ba4c689be5bf70c94a52c681713c0e225c615e9dcf0e3d61827921244023\"},\n      },\n  };\n  // clang-format on\n\n  constexpr static std::string_view kY =\n      \"0x144fee08a5557e8bbd9a9ec6e9f5e9ce0b65701a0549dc6b6edacf18414667c3\";\n\n  constexpr static Point kVanishingHPolyCommitments[] = {\n      {\"0x0e02bcfdabee92278a5ac906a5971be52a48548dac5f43403a71520192d11567\",\n       \"0x253356792d038018c8b5db38d611b214d18ad138a02091569c8e38460f5f094f\"},\n      {\"0x1cb6e040f2a121eb4021bbc16313ce7e59c5be7bc2ad76a92934357c30f54541\",\n       \"0x068c8fbf67dc71a3a2ee286150c72c67cc26d5b9286757552f8832fe751d8b26\"},\n  };\n\n  constexpr static std::string_view kX =\n      \"0x2e04b4bdc49c7acd56eabd66f8d404477dbf0e8f0f928b7aea12d312c3bc9421\";\n\n  constexpr static std::string_view kAdviceEvals[][3] = {\n      {\n          \"0x0b3da226c3887e6b04926ee9e2a0a23e796b0f94cae7fe649ceee1ed195d90e8\",\n          \"0x0f3a2e4714c34a7720ca773bb145abe9061da2af047df02977e6c79c44f614cc\",\n          \"0x0e81b4aef793060499ddd0e3809901875093d49d32fb84247109e4af34e16a2b\",\n      },\n      {\n          \"0x0b3da226c3887e6b04926ee9e2a0a23e796b0f94cae7fe649ceee1ed195d90e8\",\n          \"0x0f3a2e4714c34a7720ca773bb145abe9061da2af047df02977e6c79c44f614cc\",\n          \"0x0e81b4aef793060499ddd0e3809901875093d49d32fb84247109e4af34e16a2b\",\n      },\n  };\n\n  constexpr static std::string_view kFixedEvals[] = {\n      \"0x277144647b809983d983f1b057668f9b9109a6fc0d200276ddec0208223fc48e\",\n      \"0x26534eae5f942f2c9477e5933e65b47bccf42142d7f0fc2550502b84d4583b38\",\n  };\n\n  constexpr static std::string_view kCommonPermutationEvals[] = {\n      \"0x0bf5018a350100abc802a35bad18bf9ddc91ed5fa6ae9ea9d9d307ecbf43ba2c\",\n      \"0x089b1e380d8272a320a74da985e97072a171a7477a0fbcb6e392bbc8b5bde0b6\",\n      \"0x208ee9fb67465c1f7f9de18c041d21b1d8ec3f16f81b8beeb4a680a8c9f8fe0b\",\n      \"0x0136fe1cec5ff73c68d2f3f46e43e0244760a1c7dfbb615bd3f96d512cbe4c98\",\n  };\n\n  constexpr static std::string_view kPermutationProductEvals[][4] = {\n      {\n          \"0x04812df214dcd77aa6b1c2dfbf93d28a8e0805d2250a199921094bd47afaddd5\",\n          \"0x08ac3afb37893a93f9b0652d6fec917c2d54e381a0b9252e6a329eea1c5c4a28\",\n          \"0x12d37028fed6681412ead9bca062e312a27e142c88ddf3aebf436e9cd38bcbc0\",\n          \"0x27eaf3a43b28769bfc2873392b8ce3c4d29d55acb477064ac45bf65f32dd597d\",\n      },\n      {\n          \"0x132e573b269739684c7e30311a0b02113628b5ba30220f872c2a353eab9f1e43\",\n          \"0x1bce8e5f409f78a07f3d397712de5509e2ae481441044c7f02f64de2e3277e70\",\n          \"0x080c00e1c80235abcdb7a0d6ab0d28e6607c2fecb602d6220576726d23cc5504\",\n          \"0x25fe94fa3d9cab1ebc63f957c559579894144a806296e79c0948775c7c639f6a\",\n      },\n  };\n  constexpr static std::string_view kPermutationProductNextEvals[][4] = {\n      {\n          \"0x07e88c73d595c9efdc088d375c7effdff01f7e275812c436500984e1b1eb63a1\",\n          \"0x0dc1d9df3e2d358db56a218008ebc5b1de2a5e336266dbe69a21abf7b321d935\",\n          \"0x18ea41a265c7bcec8e8dc37cf9be9adf25d63926798c6fdb3ea081c61eb7f57a\",\n          \"0x2d0d9920927925eebc210ba7cebca1cf46bd081cb8f175930f86c341a2c9ec0a\",\n      },\n      {\n          \"0x15e6661f5bcf86084c4d98c83385dab4e06d8941439fb7008f9ef1b3df7954bf\",\n          \"0x197601628d282d2be50419fac00d793758eb2e0e7bd3d8b7f2dd4d3e62ba47b1\",\n          \"0x183e247d664d974216744277d9888f0792d2068730025c6523eb95fa095af78b\",\n          \"0x1db227fe6d8c4abc532d287ceb6e93de420522fb0793e3c452644bef7b402cef\",\n      },\n  };\n\n  constexpr static std::string_view kPermutationProductLastEvals[][4] = {\n      {\n          \"0x2ae3b7b062e6c1ae71b0162153dc5be8021055e9c2873205008d0ef82aec5478\",\n          \"0x134b7bcdbdb97934665899e286160f6ca3c66bba774f907c882db2b541743a61\",\n          \"0x104f80dec0df12c9ffb9e132604290dd710b54d8687b658c8e1a6bfcf67eb560\",\n          \"\",\n      },\n      {\n          \"0x0d932fc007ac01ffe8961fa027b8102eb1c5dcdba078a19079b391dae415837c\",\n          \"0x28675e9864d972fdeb25e8054800f29101fd7cf21e2347cb5c500d831133b714\",\n          \"0x012483abbb2dcb7cd722a4cd46fa443e1b9dceda15dadb0aba5a331bd74a649f\",\n          \"\",\n      },\n  };\n\n  constexpr static std::string_view kHEval =\n      \"0x2af439530de48c1318f0ecc2905916d3d1b0dfcccdf3dd2a3eeefd6cffae9f62\";\n\n  static void TestConfig(FieldConfig<F>& config) {\n    std::array<AdviceColumnKey, 2> expected_advice = {\n        AdviceColumnKey(0),\n        AdviceColumnKey(1),\n    };\n    EXPECT_EQ(config.advice(), expected_advice);\n    EXPECT_EQ(config.instance(), InstanceColumnKey(0));\n    EXPECT_EQ(config.s_mul(), Selector::Simple(0));\n  }\n\n  static Circuit GetCircuit() {\n    F constant(7);\n    F a(2);\n    F b(3);\n    return Circuit(std::move(constant), std::move(a), std::move(b));\n  }\n\n  static std::vector<Circuit> Get2Circuits() {\n    Circuit circuit = GetCircuit();\n    return {circuit, std::move(circuit)};\n  }\n\n  static std::vector<Evals> GetInstanceColumns() {\n    F constant(7);\n    F a(2);\n    F b(3);\n    F c = std::move(constant) * std::move(a).Square() * std::move(b).Square();\n    std::vector<F> instance_column = {std::move(c)};\n    return {Evals(std::move(instance_column))};\n  }\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_EXAMPLES_SIMPLE_CIRCUIT_TEST_DATA_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/examples/simple_lookup_circuit.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n// This is taken and modified from\n// https://github.com/privacy-scaling-explorations/halo2/blob/bc857a7/halo2_proofs/benches/dev_lookup.rs.\n\n#ifndef TACHYON_ZK_PLONK_EXAMPLES_SIMPLE_LOOKUP_CIRCUIT_H_\n#define TACHYON_ZK_PLONK_EXAMPLES_SIMPLE_LOOKUP_CIRCUIT_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <memory>\n#include <utility>\n\n#include \"tachyon/zk/plonk/constraint_system/circuit.h\"\n\nnamespace tachyon::zk::plonk {\n\n// This is taken and modified from\n// https://github.com/kroma-network/halo2/blob/7d0a369/halo2_proofs/benches/dev_lookup.rs#L21-L26.\ntemplate <typename F, size_t Bits>\nclass SimpleLookupConfig {\n public:\n  using Field = F;\n\n  SimpleLookupConfig(Selector selector, const LookupTableColumn& table,\n                     const AdviceColumnKey& advice)\n      : selector_(selector), table_(table), advice_(advice) {}\n\n  SimpleLookupConfig Clone() const {\n    return SimpleLookupConfig(selector_, table_, advice_);\n  }\n\n  Selector selector() const { return selector_; }\n  const LookupTableColumn& table() const { return table_; }\n  const AdviceColumnKey& advice() const { return advice_; }\n\n  void Load(Layouter<F>* layouter) const {\n    layouter->AssignLookupTable(\n        absl::Substitute(\"$0-bit table\", Bits), [this](LookupTable<F>& table) {\n          for (RowIndex row = 0; row < RowIndex{1} << Bits; ++row) {\n            if (!table.AssignCell(\n                    absl::Substitute(\"row $0\", row), table_, row,\n                    [row]() { return Value<F>::Known(F(row + 1)); }))\n              return false;\n          }\n          return true;\n        });\n  }\n\n private:\n  Selector selector_;\n  LookupTableColumn table_;\n  AdviceColumnKey advice_;\n};\n\n// This is taken and modified from\n// https://github.com/kroma-network/halo2/blob/7d0a369/halo2_proofs/benches/dev_lookup.rs#L28-L91.\ntemplate <typename F, size_t Bits, template <typename> class _FloorPlanner>\nclass SimpleLookupCircuit : public Circuit<SimpleLookupConfig<F, Bits>> {\n public:\n  using FloorPlanner =\n      _FloorPlanner<SimpleLookupCircuit<F, Bits, _FloorPlanner>>;\n\n  SimpleLookupCircuit() = default;\n  explicit SimpleLookupCircuit(uint32_t k) : k_(k) {}\n\n  std::unique_ptr<Circuit<SimpleLookupConfig<F, Bits>>> WithoutWitness()\n      const override {\n    return std::make_unique<SimpleLookupCircuit>();\n  }\n\n  static SimpleLookupConfig<F, Bits> Configure(ConstraintSystem<F>& meta) {\n    SimpleLookupConfig<F, Bits> config(meta.CreateComplexSelector(),\n                                       meta.CreateLookupTableColumn(),\n                                       meta.CreateAdviceColumn());\n\n    meta.Lookup(\"lookup\", [&config](VirtualCells<F>& meta) {\n      std::unique_ptr<Expression<F>> selector =\n          meta.QuerySelector(config.selector());\n      std::unique_ptr<Expression<F>> not_selector =\n          ExpressionFactory<F>::Constant(F::One()) - selector->Clone();\n      std::unique_ptr<Expression<F>> advice =\n          meta.QueryAdvice(config.advice(), Rotation::Cur());\n\n      lookup::Pairs<std::unique_ptr<Expression<F>>, LookupTableColumn>\n          lookup_pairs;\n      lookup_pairs.emplace_back(\n          std::move(selector) * std::move(advice) + std::move(not_selector),\n          config.table());\n      return lookup_pairs;\n    });\n\n    return config;\n  }\n\n  void Synthesize(SimpleLookupConfig<F, Bits>&& config,\n                  Layouter<F>* layouter) const override {\n    config.Load(layouter);\n\n    constexpr static size_t kModulus = size_t{1} << Bits;\n\n    layouter->AssignRegion(\"assign values\", [this, &config](Region<F>& region) {\n      for (RowIndex offset = 0; offset < (RowIndex{1} << k_); ++offset) {\n        config.selector().Enable(region, offset);\n        region.AssignAdvice(\n            absl::Substitute(\"offset $0\", offset), config.advice(), offset,\n            [offset]() { return Value<F>::Known(F(offset % kModulus + 1)); });\n      }\n    });\n  }\n\n private:\n  uint32_t k_ = 0;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_EXAMPLES_SIMPLE_LOOKUP_CIRCUIT_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/examples/simple_lookup_circuit_test.cc",
    "content": "#include \"tachyon/zk/plonk/examples/simple_lookup_circuit.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/zk/plonk/examples/circuit_test.h\"\n#include \"tachyon/zk/plonk/examples/simple_lookup_circuit_test_data.h\"\n#include \"tachyon/zk/plonk/layout/floor_planner/simple_floor_planner.h\"\n#include \"tachyon/zk/plonk/layout/floor_planner/v1/v1_floor_planner.h\"\n\nnamespace tachyon::zk::plonk {\n\nnamespace {\n\nstatic const size_t kBits = 3;\n\ntemplate <typename TestArguments>\nclass SimpleLookupCircuitTest\n    : public CircuitTest<TestArguments,\n                         SimpleLookupTestData<typename TestArguments::Circuit,\n                                              typename TestArguments::PS>> {};\n\n}  // namespace\n\n// clang-format off\nusing SimpleLookupTestArgumentsList = testing::Types<\n    TestArguments<SimpleLookupCircuit<BN254SHPlonk::Field, kBits, SimpleFloorPlanner>, BN254SHPlonkHalo2>,\n    TestArguments<SimpleLookupCircuit<BN254SHPlonk::Field, kBits, V1FloorPlanner>, BN254SHPlonkHalo2>>;\n// clang-format on\n\nTYPED_TEST_SUITE(SimpleLookupCircuitTest, SimpleLookupTestArgumentsList);\n\nTYPED_TEST(SimpleLookupCircuitTest, Configure) { this->ConfigureTest(); }\nTYPED_TEST(SimpleLookupCircuitTest, Synthesize) { this->SynthesizeTest(); }\nTYPED_TEST(SimpleLookupCircuitTest, LoadVerifyingKey) {\n  this->LoadVerifyingKeyTest();\n}\nTYPED_TEST(SimpleLookupCircuitTest, LoadProvingKey) {\n  this->LoadProvingKeyTest();\n}\nTYPED_TEST(SimpleLookupCircuitTest, CreateProof) { this->CreateProofTest(); }\nTYPED_TEST(SimpleLookupCircuitTest, VerifyProof) { this->VerifyProofTest(); }\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/examples/simple_lookup_circuit_test_data.h",
    "content": "#ifndef TACHYON_ZK_PLONK_EXAMPLES_SIMPLE_LOOKUP_CIRCUIT_TEST_DATA_H_\n#define TACHYON_ZK_PLONK_EXAMPLES_SIMPLE_LOOKUP_CIRCUIT_TEST_DATA_H_\n\n#include <stdint.h>\n\n#include <string_view>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/range.h\"\n#include \"tachyon/zk/plonk/examples/circuit_test_data.h\"\n#include \"tachyon/zk/plonk/examples/circuit_test_type_traits.h\"\n#include \"tachyon/zk/plonk/examples/point.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename Circuit, typename PS, typename SFINAE = void>\nclass SimpleLookupTestData : public CircuitTestData<Circuit, PS> {\n public:\n  using PCS = typename PS::PCS;\n  using F = typename PCS::Field;\n\n  // Set flags of values to be used as true\n  constexpr static bool kAssemblyFixedColumnsFlag = true;\n  constexpr static bool kLFirstFlag = true;\n  constexpr static bool kLLastFlag = true;\n  constexpr static bool kLActiveRowFlag = true;\n  constexpr static bool kFixedColumnsFlag = true;\n  constexpr static bool kFixedPolysFlag = true;\n  constexpr static bool kAdviceCommitmentsFlag = true;\n  constexpr static bool kLookupPermutedCommitmentsFlag = true;\n  constexpr static bool kLookupProductCommitmentsFlag = true;\n  constexpr static bool kAdviceEvalsFlag = true;\n  constexpr static bool kFixedEvalsFlag = true;\n  constexpr static bool kLookupProductEvalsFlag = true;\n  constexpr static bool kLookupProductNextEvalsFlag = true;\n  constexpr static bool kLookupPermutedInputEvalsFlag = true;\n  constexpr static bool kLookupPermutedInputPrevEvalsFlag = true;\n  constexpr static bool kLookupPermutedTableEvalsFlag = true;\n\n  constexpr static size_t kN = 32;\n\n  // clang-format off\n  constexpr static std::string_view kPinnedConstraintSystem =\n      \"PinnedConstraintSystem { \"\n        \"num_fixed_columns: 1, \"\n        \"num_advice_columns: 1, \"\n        \"num_instance_columns: 0, \"\n        \"num_selectors: 1, \"\n        \"gates: [], \"\n        \"advice_queries: [(\"\n          \"Column { \"\n            \"index: 0, \"\n            \"column_type: Advice \"\n          \"}, \"\n          \"Rotation(0)\"\n        \")], \"\n        \"instance_queries: [], \"\n        \"fixed_queries: [(\"\n          \"Column { \"\n            \"index: 0, \"\n            \"column_type: Fixed \"\n          \"}, \"\n          \"Rotation(0)\"\n        \")], \"\n        \"permutation: Argument { \"\n          \"columns: [] \"\n        \"}, \"\n        \"lookups: [Argument { \"\n          \"input_expressions: [Sum(\"\n            \"Product(\"\n              \"Selector(Selector(0, false)), \"\n              \"Advice { \"\n                \"query_index: 0, \"\n                \"column_index: 0, \"\n                \"rotation: Rotation(0) \"\n              \"}\"\n            \"), \"\n            \"Sum(\"\n              \"Constant(0x0000000000000000000000000000000000000000000000000000000000000001), \"\n              \"Negated(Selector(Selector(0, false)))\"\n            \")\"\n          \")], \"\n          \"table_expressions: [Fixed { \"\n            \"query_index: 0, \"\n            \"column_index: 0, \"\n            \"rotation: Rotation(0) \"\n          \"}] \"\n        \"}], \"\n        \"constants: [], \"\n        \"minimum_degree: None \"\n      \"}\";\n  // clang-format on\n\n  constexpr static std::string_view kAssemblyFixedColumns[][kN] = {\n      {\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000002\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000003\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000004\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000005\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000006\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000007\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000008\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n      },\n  };\n\n  constexpr static bool kSelectors[][kN] = {\n      {\n          true,  true,  true,  true,  true,  true,  true,  true,\n          true,  true,  true,  true,  true,  true,  true,  true,\n          false, false, false, false, false, false, false, false,\n          false, false, false, false, false, false, false, false,\n      },\n  };\n\n  constexpr static base::Range<RowIndex> kUsableRows =\n      base::Range<RowIndex>::Until(26);\n\n  // clang-format off\n  constexpr static std::string_view kPinnedVerifyingKey =\n      \"PinnedVerificationKey { \"\n        \"base_modulus: \\\"0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47\\\", \"\n        \"scalar_modulus: \\\"0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001\\\", \"\n        \"domain: PinnedEvaluationDomain { \"\n          \"k: 5, \"\n          \"extended_k: 7, \"\n          \"omega: 0x09c532c6306b93d29678200d47c0b2a99c18d51b838eeb1d3eed4c533bb512d0 \"\n        \"}, \"\n        \"cs: PinnedConstraintSystem { \"\n          \"num_fixed_columns: 2, \"\n          \"num_advice_columns: 1, \"\n          \"num_instance_columns: 0, \"\n          \"num_selectors: 1, \"\n          \"gates: [], \"\n          \"advice_queries: [(\"\n            \"Column { \"\n              \"index: 0, \"\n              \"column_type: Advice \"\n            \"}, \"\n            \"Rotation(0)\"\n          \")], \"\n          \"instance_queries: [], \"\n          \"fixed_queries: [\"\n            \"(\"\n              \"Column { \"\n                \"index: 0, \"\n                \"column_type: Fixed \"\n              \"}, \"\n              \"Rotation(0)\"\n            \"), \"\n            \"(\"\n              \"Column { \"\n                \"index: 1, \"\n                \"column_type: Fixed \"\n              \"}, \"\n              \"Rotation(0)\"\n            \")\"\n          \"], \"\n          \"permutation: Argument { \"\n            \"columns: [] \"\n          \"}, \"\n          \"lookups: [Argument { \"\n            \"input_expressions: [Sum(\"\n              \"Product(\"\n                \"Fixed { \"\n                  \"query_index: 1, \"\n                  \"column_index: 1, \"\n                  \"rotation: Rotation(0) \"\n                \"}, \"\n                \"Advice { \"\n                  \"query_index: 0, \"\n                  \"column_index: 0, \"\n                  \"rotation: Rotation(0) \"\n                \"}\"\n              \"), \"\n              \"Sum(\"\n                \"Constant(0x0000000000000000000000000000000000000000000000000000000000000001), \"\n                \"Negated(Fixed { \"\n                  \"query_index: 1, \"\n                  \"column_index: 1, \"\n                  \"rotation: Rotation(0) \"\n                \"})\"\n              \")\"\n            \")], \"\n            \"table_expressions: [Fixed { \"\n              \"query_index: 0, \"\n              \"column_index: 0, \"\n              \"rotation: Rotation(0) \"\n            \"}] \"\n          \"}], \"\n          \"constants: [], \"\n          \"minimum_degree: None \"\n        \"}, \"\n        \"fixed_commitments: [\"\n          \"(0x2491b553ef715ba083a05cf556402967f4eb0d63faac61e1011382f579e92a25, \"\n            \"0x21797eb7fe8cdbb4abbaf6b4a38d22f61555af4ffebd38547f1b0a978c8a1ddf), \"\n          \"(0x06301defe1b3e0ee7910835b9b40d15107294b715b16753d4b9b34aafa4e1418, \"\n            \"0x2d03286f3624c40418f311b994f7c2c7257e0d92d487704983720fce4a99fbe7)\"\n        \"], \"\n        \"permutation: VerifyingKey { \"\n          \"commitments: [] \"\n        \"} \"\n      \"}\";\n  // clang-format on\n\n  constexpr static std::string_view kTranscriptRepr =\n      \"0x01522b814675ed8fa132994e824c46042f5130b140ac340c760eb426c7253ae8\";\n\n  constexpr static std::string_view kLFirst[] = {\n      \"0x2ee12bff4a2813286a8dc388cd754d9a3ef2490635eba50cb9c2e5e750800001\",\n      \"0x2ee12bff4a2813286a8dc388cd754d9a3ef2490635eba50cb9c2e5e750800001\",\n      \"0x2ee12bff4a2813286a8dc388cd754d9a3ef2490635eba50cb9c2e5e750800001\",\n      \"0x2ee12bff4a2813286a8dc388cd754d9a3ef2490635eba50cb9c2e5e750800001\",\n      \"0x2ee12bff4a2813286a8dc388cd754d9a3ef2490635eba50cb9c2e5e750800001\",\n      \"0x2ee12bff4a2813286a8dc388cd754d9a3ef2490635eba50cb9c2e5e750800001\",\n      \"0x2ee12bff4a2813286a8dc388cd754d9a3ef2490635eba50cb9c2e5e750800001\",\n      \"0x2ee12bff4a2813286a8dc388cd754d9a3ef2490635eba50cb9c2e5e750800001\",\n      \"0x2ee12bff4a2813286a8dc388cd754d9a3ef2490635eba50cb9c2e5e750800001\",\n      \"0x2ee12bff4a2813286a8dc388cd754d9a3ef2490635eba50cb9c2e5e750800001\",\n      \"0x2ee12bff4a2813286a8dc388cd754d9a3ef2490635eba50cb9c2e5e750800001\",\n      \"0x2ee12bff4a2813286a8dc388cd754d9a3ef2490635eba50cb9c2e5e750800001\",\n      \"0x2ee12bff4a2813286a8dc388cd754d9a3ef2490635eba50cb9c2e5e750800001\",\n      \"0x2ee12bff4a2813286a8dc388cd754d9a3ef2490635eba50cb9c2e5e750800001\",\n      \"0x2ee12bff4a2813286a8dc388cd754d9a3ef2490635eba50cb9c2e5e750800001\",\n      \"0x2ee12bff4a2813286a8dc388cd754d9a3ef2490635eba50cb9c2e5e750800001\",\n      \"0x2ee12bff4a2813286a8dc388cd754d9a3ef2490635eba50cb9c2e5e750800001\",\n      \"0x2ee12bff4a2813286a8dc388cd754d9a3ef2490635eba50cb9c2e5e750800001\",\n      \"0x2ee12bff4a2813286a8dc388cd754d9a3ef2490635eba50cb9c2e5e750800001\",\n      \"0x2ee12bff4a2813286a8dc388cd754d9a3ef2490635eba50cb9c2e5e750800001\",\n      \"0x2ee12bff4a2813286a8dc388cd754d9a3ef2490635eba50cb9c2e5e750800001\",\n      \"0x2ee12bff4a2813286a8dc388cd754d9a3ef2490635eba50cb9c2e5e750800001\",\n      \"0x2ee12bff4a2813286a8dc388cd754d9a3ef2490635eba50cb9c2e5e750800001\",\n      \"0x2ee12bff4a2813286a8dc388cd754d9a3ef2490635eba50cb9c2e5e750800001\",\n      \"0x2ee12bff4a2813286a8dc388cd754d9a3ef2490635eba50cb9c2e5e750800001\",\n      \"0x2ee12bff4a2813286a8dc388cd754d9a3ef2490635eba50cb9c2e5e750800001\",\n      \"0x2ee12bff4a2813286a8dc388cd754d9a3ef2490635eba50cb9c2e5e750800001\",\n      \"0x2ee12bff4a2813286a8dc388cd754d9a3ef2490635eba50cb9c2e5e750800001\",\n      \"0x2ee12bff4a2813286a8dc388cd754d9a3ef2490635eba50cb9c2e5e750800001\",\n      \"0x2ee12bff4a2813286a8dc388cd754d9a3ef2490635eba50cb9c2e5e750800001\",\n      \"0x2ee12bff4a2813286a8dc388cd754d9a3ef2490635eba50cb9c2e5e750800001\",\n      \"0x2ee12bff4a2813286a8dc388cd754d9a3ef2490635eba50cb9c2e5e750800001\",\n  };\n\n  constexpr static std::string_view kLLast[] = {\n      \"0x2ee12bff4a2813286a8dc388cd754d9a3ef2490635eba50cb9c2e5e750800001\",\n      \"0x2c5ebc726b47cc659ddda5c2c5199730cb3deb6c9bed0a12973aae60ebd81c83\",\n      \"0x283c497861461cf04ba9e47a8bbd619f28e84387bca93dffdcee18677a42352c\",\n      \"0x0f9a39926cb2b113e678257c8fdd8bdba10667f9e11d46d529cd041ca367cdd0\",\n      \"0x1fc1d37b63c8911b6692d47caf0a2ab7e8bc5e8a1bc3e2047d92c8b230847e4f\",\n      \"0x031d6554a817c4b89ce4e88ac199b59b8e27e8ee0d1118a50a00f2e870b9a147\",\n      \"0x01599bef0e460a791764dcf17cb57f79b293139b37c0b85054a46d6a560deaf4\",\n      \"0x14bd475039a03f8d889d751c4b756bf013700add984fb797ec2eee23930f61df\",\n      \"0x0183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f800000\",\n      \"0x0405920075e9d3c41a729ff3bc67c12c5cf5fcdbddcc667eaca747330427e37e\",\n      \"0x082804fa7feb83396ca6613bf5c3f6bdff4ba4c0bd10329166f3dd2c75bdcad5\",\n      \"0x20ca14e0747eef15d1d82039f1a3cc81872d804e989c29bc1a14f1774c983231\",\n      \"0x10a27af77d690f0e51bd7139d2772da53f7789be5df58e8cc64f2ce1bf7b81b2\",\n      \"0x2d46e91e3919db711b6b5d2bbfe7a2c19a0bff5a6ca857ec39e102ab7f465eba\",\n      \"0x2f0ab283d2eb95b0a0eb68c504cbd8e375a0d4ad41f8b840ef3d882999f2150d\",\n      \"0x1ba70722a791609c2fb2d09a360bec6d14c3dd6ae169b8f957b307705cf09e22\",\n      \"0x2ee12bff4a2813286a8dc388cd754d9a3ef2490635eba50cb9c2e5e750800001\",\n      \"0x2c5ebc726b47cc659ddda5c2c5199730cb3deb6c9bed0a12973aae60ebd81c83\",\n      \"0x283c497861461cf04ba9e47a8bbd619f28e84387bca93dffdcee18677a42352c\",\n      \"0x0f9a39926cb2b113e678257c8fdd8bdba10667f9e11d46d529cd041ca367cdd0\",\n      \"0x1fc1d37b63c8911b6692d47caf0a2ab7e8bc5e8a1bc3e2047d92c8b230847e4f\",\n      \"0x031d6554a817c4b89ce4e88ac199b59b8e27e8ee0d1118a50a00f2e870b9a147\",\n      \"0x01599bef0e460a791764dcf17cb57f79b293139b37c0b85054a46d6a560deaf4\",\n      \"0x14bd475039a03f8d889d751c4b756bf013700add984fb797ec2eee23930f61df\",\n      \"0x0183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f800000\",\n      \"0x0405920075e9d3c41a729ff3bc67c12c5cf5fcdbddcc667eaca747330427e37e\",\n      \"0x082804fa7feb83396ca6613bf5c3f6bdff4ba4c0bd10329166f3dd2c75bdcad5\",\n      \"0x20ca14e0747eef15d1d82039f1a3cc81872d804e989c29bc1a14f1774c983231\",\n      \"0x10a27af77d690f0e51bd7139d2772da53f7789be5df58e8cc64f2ce1bf7b81b2\",\n      \"0x2d46e91e3919db711b6b5d2bbfe7a2c19a0bff5a6ca857ec39e102ab7f465eba\",\n      \"0x2f0ab283d2eb95b0a0eb68c504cbd8e375a0d4ad41f8b840ef3d882999f2150d\",\n      \"0x1ba70722a791609c2fb2d09a360bec6d14c3dd6ae169b8f957b307705cf09e22\",\n  };\n\n  constexpr static std::string_view kLActiveRow[] = {\n      \"0x0912ceb58a394e07d28f0d12384840917789bb8d96d2c51b3cba5e0bbd000001\",\n      \"0x1d82943c3cbe2c2860423646e2978e190877fb3428206267de5d42802c5469a4\",\n      \"0x2572c0c97e6a4f263421a91b28e88314200149a7c7ad52017a2795a064b4ad9f\",\n      \"0x0539c553022d8a6706aa7d43b916f826a446cc385baacfb3d7b66a3da2a4a71c\",\n      \"0x06a4e286e8e1f6381ee3df0e41b7ebfb160a057e7942670cdcd4cd7fd63dcad5\",\n      \"0x101a5290a87c4a11d6aecbae4d1ea244bc7ba09f7480d2706e8603b77eef7690\",\n      \"0x08bed71c502de5c63e148770b676249eb9f7c6d7d2e30fd25177ecd1cf31d5fa\",\n      \"0x2b2849fbd4207ee991f8adc3272599de4bb1d799fe540523d10bd7be68f2c494\",\n      \"0x1e3eb107ccbf041a18d0524efafe1ff4ff7abf47d7f6167ff373b90591047e4f\",\n      \"0x265aafd2d283df1150400782a1674fd58cc747651b688d032c0c2600007ccbde\",\n      \"0x21e2d2ab84ea032abda712c51a905e69819959e789afc1428f28695b59a3cd79\",\n      \"0x298173f6c450ff5d1aa710238c4b9be7989078b00b571433f92c41427a5c1b4a\",\n      \"0x2d8790103be208af5328e69750bfce208c5f356afe2aecbc651e787cfa72150d\",\n      \"0x2ed37d6ed1d5abd50a2c5c3b6a13736bbbc5cb637165b93eb9e192e192da55bd\",\n      \"0x2c9e1063d1568f66e38646080d761995f2a0bbcda4ed4c68c25afeab719dc4b8\",\n      \"0x0ad668e43820f188510890c9e5906e54af9aa65550f601e79a28e49dd4c59972\",\n      \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n      \"0x0709cb0b6beec89572c99ab6bc880d038e5aa740e631879f2542794a76af2310\",\n      \"0x27adb8442c71c491438708732748c0e44ae895b77a796689a59cdb483bb40ec1\",\n      \"0x245143f0f1496195f2e77fd90d06b853fa638f55918d45533fc7cbdbfa5719eb\",\n      \"0x26b92704ca3c8feefde7624cd7b156dc3fa6a44578db727b52cf08badac2352c\",\n      \"0x23ac28e9730302a77db6be539530ca51b5e26f90a3b1ded8d627002d059a590e\",\n      \"0x2622f0e98def29d70fe96397e09525345e686eeff3a701c95fc54c75ef24ed2c\",\n      \"0x021084fdcb5ac54d2dea020edc9dbcb6fb8e6b96648138f62474629a46e62494\",\n      \"0x0f1f5883e65f820d03faef0c1e6b22e25635ea7c1a27c3083c301d351ffb81b2\",\n      \"0x107b1e0f0e1a429fefc13f26cfa5c7e162819849eab588d7058653b3e447fba0\",\n      \"0x100402345f5c2d8b64fb8d9f5167087db66e40e1a3390e44475e4884c8056f63\",\n      \"0x2e5c33950a4961df0f01031c87a93f50165b8f55ef24822d12e8e87d9f9f4f05\",\n      \"0x303ac7ee586e1da181f2a07a4a2acd13f1855ca16dac5d5d0e675351a68deaf5\",\n      \"0x039513b90e2671a96fa218f5a975ce9eec90436a48dd57dbdbc70a0b20d38677\",\n      \"0x176e61e727623d5ecdc1d98d26dcab891b111dac86184cbfe9867127bdf97eeb\",\n      \"0x0819511deb18fdaead1bc5e1429f10d85c5e54084b66d6db5c4b577f846a5214\",\n  };\n\n  constexpr static std::string_view kFixedColumns[][kN] = {\n      {\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000002\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000003\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000004\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000005\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000006\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000007\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000008\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n      },\n      {\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000001\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n      },\n  };\n\n  constexpr static std::string_view kFixedPolys[][kN] = {\n      {\n          \"0x0f1f5883e65f820d099915c908786b9d1c903896a609f32d65369cbe3b000002\",\n          \"0x096ac2c44881b041a21b6f1f1bb2d4d7f9c270a7f29e76fffbd15325b95e3ce2\",\n          \"0x00f3e53a93723acf565de2acd9206f6e54e69eb9c2a69ca8b869bfa5a2184898\",\n          \"0x2fa31a7ef4e264272a22782ae9092cd46d84c489b2f0fedf1abdac57aef4f71e\",\n          \"0x039d659292e4e34b8f8d55cbf609982d5dd87ca2715875bc916e0b1ce36c5218\",\n          \"0x18773605c3eb631e5718d4464a2c4cbe1de8c1ff4c6edf5930a8619786a5b9a9\",\n          \"0x29940fd410cae7a7bba44f5b90bfa4ddf627bf99351a729308830b87ea4ab586\",\n          \"0x1573ce82875ad48fa671878c5dbbb4eb60e7d3bc107d840733b81340ae861628\",\n          \"0x060c89ce5c263405262f9480120850db51f792b96b909da2ad68c0172cf28515\",\n          \"0x12fa4fd45cb064c711bb17be2cbd831cfd36440a94099878d242b461acab0d3c\",\n          \"0x2df352948035ec5e15e6cc580f0d6760d0d8bcfb8f8728d4c31d46caf5725957\",\n          \"0x2d516d066b9262f6a9d0ec33b676176e1ef20545f36477c946a1d98016fcdee0\",\n          \"0x0634d845bcffbda3a6875ef3745abde328d92f2cfa0768286f023a33ebc48ec2\",\n          \"0x27f51fe49fe915f62d04ff477b37032163ef864cb34493e535c70e940a1a1a9d\",\n          \"0x0ab166942af71ce2a25e95002523b19b93aeb434fa4f58b0f408355b3edad84a\",\n          \"0x0a30fa7e9d8e734d75881684f1faacf89c76c5758c1c32f87b19cfa4468e7d36\",\n          \"0x060c89ce5c263405370a08b6d0302b0ba5067d090f372e12287c3eb27e000000\",\n          \"0x0124ed3feff85704cb4661b932f64c795f68320181e920bb20e3d4bb45c19525\",\n          \"0x07e9cf3f61b88fd7e58eb4676366a100e0c482b760c53e8f1cdd937ec0c48e1d\",\n          \"0x07f21d6c685addacc008b3973ae25de51d899c64eb619f3cfa7be3b778a65b7c\",\n          \"0x29c0a3f92039a2db604cb5a7d2f564d3c505bc4d21b3e2a25601ced4f56fbb76\",\n          \"0x20e03b037d76f2c1cdd3c664a4c69a200aa6c54e2db3b837ecd0704167b7a78a\",\n          \"0x254a2223f9c61c8df93d29ab265943abaa648d9b97f5ceb94c8866ea3c95f158\",\n          \"0x0010b2e6207c947382e7540d1b82d1e2e1f6cdaa8d3b14265a511885a1400bc5\",\n          \"0x030644e72e131a02ac5f7892263fefb6259228d42b4227788f519df4900d7aeb\",\n          \"0x2f2b91a04f43fd7b356571ac6c81712dabd92ea26fb5a70fe9d24b4767da223c\",\n          \"0x30041fa5a928bd53c6323779d0a8128d6df889403d62ce0f4c5d86a1220aae15\",\n          \"0x06e84ced14e100dd18f0294f3458f1ef7a64c3abd9f1107d7d711fa517804a9c\",\n          \"0x0ef70a0c8585f86ec70cf573b4b81e9bcb8ff7471a4b3a4066e49d85a55f63b1\",\n          \"0x138f1764bf0cab47dacd22a4b3f361d912167e314137bf8ae37dce58b3e382b5\",\n          \"0x012c7a8b30b4eb3571fb6ded0d8c3cf1f818380b2f30562be1b20df1dfeaa2bb\",\n          \"0x05a87d92807e5e84dd239dc00a90413974e1281cd7af602ae9365c1ce392e4ca\",\n      },\n      {\n          \"0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000001\",\n          \"0x2676094caa55c6970e879bd3e4e0c2b266852e1dd23eecf207aa65c27cef1718\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x2fa0a1411a6bdca4430e8a31f6b95622bcf52f1221d69b3a87741ee75ff60861\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0dcdcf1a8592c176d828c4f1bc010641f8d5daaeb024a3b0572614bb8d4b76b6\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0c25fbe8f815a5f0ad1f1da5444aff35fceba50167f818fd44fa151200736e7f\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x24e253551f980578421317bf8e27bd6a4e39164bc90f4435384ff03da6684972\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x22c5a2a8524e53ec9895eaed111377d08c1efdb8d49f3550674a9ec6b1b414ca\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x173933360c1ef0091fd01fd29f368c6847b1ced532f67a3479002bf6f7b1202a\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x05ba0b833e833f0324cabe3e915da9b03266b13aa27b8836433b4a44c212794d\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x27a3fe08749b4723f800831c880b99272349f8894fa25151ec688bf5eeed86b4\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x1624d655a6ff961dfcfb21887a32b66f0dfedaeebf275f53b6a3aa43b94edfd7\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x0a9866e360d0323a8435566e0855cb06c991ac0b1d7ea437c8593773ff4beb37\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x087bb636938680aedab8299b8b41856d07779378290e9552f753e5fd0a97b68f\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x21380da2bb08e0366fac23b5d51e43a158c504c28a25c08aeaa9c128b08c9182\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x1f903a712d8bc4b044a27c695d683c955cdacf1541f935d7d87dc17f23b4894b\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x2e21b6bd79e449ac920cfcdfa4314511c0ef62fa4a00aedeec11ace74109f7a1\",\n          \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n          \"0x06e8003f08c8bf900e43a58734888024ef2b7ba61fdeec9627f970783410e8e9\",\n      },\n  };\n\n  constexpr static uint8_t kProof[] = {\n      47,  182, 87,  188, 243, 30,  63,  165, 203, 25,  53,  75,  159, 66,\n      202, 142, 241, 131, 243, 14,  210, 25,  158, 80,  248, 54,  227, 231,\n      128, 14,  188, 33,  47,  182, 87,  188, 243, 30,  63,  165, 203, 25,\n      53,  75,  159, 66,  202, 142, 241, 131, 243, 14,  210, 25,  158, 80,\n      248, 54,  227, 231, 128, 14,  188, 33,  161, 99,  130, 124, 10,  20,\n      160, 177, 184, 226, 59,  230, 41,  56,  59,  50,  77,  199, 118, 186,\n      205, 135, 239, 134, 240, 216, 178, 143, 224, 186, 112, 133, 110, 18,\n      165, 210, 171, 209, 142, 38,  49,  39,  167, 176, 66,  2,   157, 206,\n      154, 213, 99,  116, 249, 18,  192, 71,  196, 104, 232, 248, 155, 157,\n      34,  159, 89,  158, 214, 71,  187, 172, 132, 250, 244, 246, 27,  26,\n      161, 145, 46,  212, 131, 213, 197, 195, 198, 107, 243, 182, 186, 151,\n      97,  0,   50,  34,  16,  153, 109, 145, 193, 251, 94,  145, 202, 240,\n      196, 236, 95,  31,  161, 173, 90,  245, 183, 206, 42,  94,  187, 113,\n      14,  9,   16,  180, 18,  53,  240, 184, 230, 160, 141, 34,  61,  50,\n      56,  77,  136, 29,  85,  118, 10,  11,  246, 144, 45,  247, 193, 24,\n      159, 171, 151, 79,  99,  231, 4,   196, 85,  14,  155, 140, 73,  154,\n      13,  51,  1,   183, 129, 90,  188, 60,  1,   201, 92,  29,  18,  150,\n      103, 47,  167, 207, 151, 155, 254, 184, 236, 190, 146, 119, 107, 159,\n      165, 160, 64,  165, 1,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   47,  172, 222, 13,  13,  12,\n      224, 97,  93,  200, 160, 75,  5,   163, 114, 90,  192, 141, 254, 13,\n      100, 184, 101, 163, 233, 159, 78,  5,   241, 39,  149, 5,   40,  11,\n      98,  1,   131, 105, 31,  119, 211, 35,  208, 92,  16,  141, 54,  186,\n      85,  51,  48,  147, 26,  212, 124, 16,  28,  2,   89,  147, 218, 21,\n      81,  0,   108, 96,  172, 102, 157, 115, 78,  132, 158, 105, 84,  190,\n      71,  232, 56,  178, 31,  177, 102, 86,  121, 78,  135, 139, 195, 51,\n      89,  225, 105, 42,  121, 156, 82,  179, 44,  221, 114, 243, 65,  222,\n      7,   178, 43,  232, 233, 163, 39,  248, 255, 223, 71,  229, 151, 12,\n      34,  80,  73,  103, 57,  110, 1,   139, 223, 171, 204, 73,  3,   168,\n      27,  182, 207, 72,  36,  191, 169, 49,  218, 64,  166, 147, 146, 6,\n      242, 47,  237, 168, 252, 122, 135, 62,  131, 186, 247, 123, 158, 4,\n      204, 73,  3,   168, 27,  182, 207, 72,  36,  191, 169, 49,  218, 64,\n      166, 147, 146, 6,   242, 47,  237, 168, 252, 122, 135, 62,  131, 186,\n      247, 123, 158, 4,   139, 30,  224, 135, 52,  150, 157, 241, 211, 151,\n      197, 1,   41,  166, 172, 233, 28,  128, 103, 213, 108, 62,  199, 22,\n      27,  44,  107, 62,  220, 198, 172, 24,  202, 35,  32,  107, 51,  158,\n      42,  192, 112, 163, 235, 241, 13,  21,  31,  138, 195, 38,  74,  235,\n      102, 118, 197, 233, 145, 137, 92,  237, 50,  169, 218, 31,  1,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n      0,   0,   147, 9,   27,  191, 53,  255, 133, 161, 207, 13,  48,  194,\n      151, 1,   51,  177, 200, 1,   10,  177, 231, 20,  173, 104, 17,  223,\n      68,  229, 202, 28,  92,  7,   202, 190, 62,  194, 225, 115, 241, 77,\n      54,  107, 222, 211, 213, 40,  221, 124, 236, 132, 45,  28,  50,  124,\n      187, 14,  225, 140, 170, 71,  229, 214, 157, 21,  210, 74,  235, 46,\n      221, 76,  167, 90,  61,  36,  164, 163, 205, 203, 121, 48,  38,  204,\n      205, 60,  50,  87,  40,  8,   134, 231, 171, 226, 150, 94,  48,  7,\n      229, 121, 5,   139, 205, 183, 36,  150, 127, 201, 6,   223, 16,  249,\n      167, 247, 41,  251, 50,  61,  37,  71,  102, 218, 56,  197, 9,   162,\n      69,  159, 96,  10,  83,  156, 93,  126, 48,  239, 157, 14,  151, 244,\n      182, 207, 214, 114, 206, 66,  125, 143, 9,   39,  58,  125, 31,  207,\n      247, 184, 221, 159, 199, 221, 1,   5,   1,   160, 63,  77,  223, 129,\n      195, 86,  34,  53,  60,  89,  38,  147, 91,  250, 196, 77,  154, 203,\n      20,  87,  74,  15,  23,  14,  193, 179, 251, 70,  128, 17,  27,  172,\n      155, 236, 201, 208, 65,  160, 226, 93,  16,  146, 53,  179, 114, 131,\n      165, 122, 137, 68,  149, 154, 38,  109, 61,  21,  151, 184, 239, 157,\n      122, 41,  149, 180, 63,  151, 95,  120, 63,  5,   163, 232, 209, 202,\n      25,  166, 247, 195, 41,  238, 217, 8,   170, 185, 191, 187, 91,  119,\n      200, 233, 235, 168, 156, 9,   172, 76,  239, 215, 3,   217, 222, 86,\n      109, 249, 0,   195, 134, 197, 87,  64,  151, 214, 250, 23,  241, 132,\n      6,   4,   12,  33,  156, 12,  166, 249, 207, 26,  161, 82,  172, 32,\n      172, 188, 238, 122, 176, 68,  121, 246, 202, 25,  9,   65,  144, 93,\n      19,  17,  67,  179, 86,  103, 217, 182, 214, 217, 248, 12,  119, 39,\n      188, 160, 17,  210, 52,  34,  134, 158, 145, 158, 123, 236, 212, 139,\n      45,  173, 239, 186, 235, 196, 183, 190, 66,  121, 8,   119, 95,  12,\n      151, 160, 172, 18,  145, 117, 33,  138, 63,  122, 32,  15,  217, 123,\n      11,  246, 172, 91,  51,  159, 113, 109, 205, 185, 29,  253, 28,  11,\n      35,  238, 106, 172, 44,  253, 192, 158};\n\n  // clang-format off\n  constexpr static Point kAdviceCommitments[][1] = {\n      {\n          {\"0x21bc0e80e7e336f8509e19d20ef383f18eca429f4b3519cba53f1ef3bc57b62f\",\n           \"0x20f77de309a3f81a09883e451c96e1b091f7fc7799e27905444c4759806fc6d2\"},\n      },\n      {\n          {\"0x21bc0e80e7e336f8509e19d20ef383f18eca429f4b3519cba53f1ef3bc57b62f\",\n           \"0x20f77de309a3f81a09883e451c96e1b091f7fc7799e27905444c4759806fc6d2\"},\n      },\n  };\n  // clang-format on\n\n  constexpr static std::string_view kTheta =\n      \"0x19f2e881193a280d2d77a8bc0f9edf053732ec86f93279a580830fb2befeccd7\";\n\n  // clang-format off\n  constexpr static Point kLookupPermutedCommitmentsInput[][1] = {\n      {\n          {\"0x0570bae08fb2d8f086ef87cdba76c74d323b3829e63be2b8b1a0140a7c8263a1\",\n           \"0x0eb968ee1f6663417be8e37d714a7dec22a717d74bc3c8bc1f4a4bd58da49eed\"},\n      },\n      {\n          {\"0x19102232006197bab6f36bc6c3c5d583d42e91a11a1bf6f4fa84acbb47d69e59\",\n           \"0x1bfa3165ed3f9e4941b37e1c8f4d4e05e1c45c82b55168f5b1adc4af96cd81fb\"},\n      },\n  };\n\n  constexpr static Point kLookupPermutedCommitmentsTable[][1] = {\n      {\n          {\"0x1f229d9bf8e868c447c012f97463d59ace9d0242b0a72731268ed1abd2a5126e\",\n           \"0x29edba749b5dc16811d78ee18bce88c5c61cd56230ad68e73c578361062a317d\"},\n      },\n      {\n          {\"0x20e6b8f03512b410090e71bb5e2aceb7f55aada11f5fecc4f0ca915efbc1916d\",\n           \"0x0386bd00ee929fd8e19e80f18f372aca85b7243d28457923d49c511058e3cd55\"},\n      },\n  };\n  // clang-format on\n\n  constexpr static std::string_view kBeta =\n      \"0x006a00a3a227c15c50d662d1a7d34e8a1dca3445c79fe17bd0bba1bd324d513e\";\n\n  constexpr static std::string_view kGamma =\n      \"0x08aacbb21c2c304fa7a8f64d8554dba8e3ad16dba09ab0cba0b711c28210802c\";\n\n  // clang-format off\n  constexpr static Point kLookupProductCommitments[][1] = {\n      {\n          {\"0x1a498c9b0e55c404e7634f97ab9f18c1f72d90f60b0a76551d884d38323d228d\",\n           \"0x158baeb0a976e042a02134658e53c74585234c9f27ed3a9ec40454bca1ae124b\"},\n      },\n      {\n          {\"0x2540a0a59f6b7792beecb8fe9b97cfa72f6796121d5cc9013cbc5a81b701330d\",\n           \"0x00a3fb614af06a4322b1c0fdd2c9c1b5e47f873c8d4ab47aa217a1fec5e39cd1\"},\n      },\n  };\n  // clang-format on\n\n  constexpr static std::string_view kY =\n      \"0x081da6a34bb45df66fa1bbda6850c3b1a5aad71cbc13926a54439251cc884074\";\n\n  // clang-format off\n  constexpr static Point kVanishingHPolyCommitments[] = {\n      {\"0x059527f1054e9fe9a365b8640dfe8dc05a72a3054ba0c85d61e00c0d0ddeac2f\",\n       \"0x244c3a6aead08117b86d1f46cb5dc9175ba3a3ae928955a158345f0a5d62ce4e\"},\n      {\"0x005115da9359021c107cd41a93303355ba368d105cd023d3771f698301620b28\",\n       \"0x21e75a51fb0bf9aeb422e3682af875aeede51f33cca14b4fa4fd8abf569623d2\"},\n      {\"0x1c792a69e15933c38b874e795666b11fb238e847be54699e844e739d66ac606c\",\n       \"0x2e2d4868337c81d3a8557f15231d79cdaa609ec6778d2bf386e04aacace2619f\"},\n      {\"0x2bdf8b016e39674950220c97e547dffff827a3e9e82bb207de41f372dd2cb352\",\n       \"0x1b37ee7442c7c4072fc090ac2b46cc81e7f222306b35d400b0f94db864c82f81\"},\n  };\n  // clang-format on\n\n  constexpr static std::string_view kX =\n      \"0x0a7f0b32529da1d83a2e86cdd521aa1b38b73f08d834762a1e17ba859e38f6c3\";\n\n  constexpr static std::string_view kAdviceEvals[][1] = {\n      {\n          \"0x049e7bf7ba833e877afca8ed2ff2069293a640da31a9bf2448cfb61ba80349cc\",\n      },\n      {\n          \"0x049e7bf7ba833e877afca8ed2ff2069293a640da31a9bf2448cfb61ba80349cc\",\n      },\n  };\n\n  constexpr static std::string_view kFixedEvals[] = {\n      \"0x18acc6dc3e6b2c1b16c73e6cd567801ce9aca62901c597d3f19d963487e01e8b\",\n      \"0x1fdaa932ed5c8991e9c57666eb4a26c38a1f150df1eba370c02a9e336b2023ca\",\n  };\n\n  constexpr static std::string_view kLookupProductEvals[][1] = {\n      {\n          \"0x075c1ccae544df1168ad14e7b10a01c8b1330197c2300dcfa185ff35bf1b0993\",\n      },\n      {\n          \"0x118046fbb3c10e170f4a5714cb9a4dc4fa5b9326593c352256c381df4d3fa001\",\n      },\n  };\n\n  constexpr static std::string_view kLookupProductNextEvals[][1] = {\n      {\n          \"0x159dd6e547aa8ce10ebb7c321c2d84ec7cdd28d5d3de6b364df173e1c23ebeca\",\n      },\n      {\n          \"0x297a9defb897153d6d269a9544897aa58372b33592105de2a041d0c9ec9bac1b\",\n      },\n  };\n\n  constexpr static std::string_view kLookupPermutedInputEvals[][1] = {\n      {\n          \"0x07305e96e2abe786082857323ccdcc263079cbcda3a4243d5aa74cdd2eeb4ad2\",\n      },\n      {\n          \"0x099ca8ebe9c8775bbbbfb9aa08d9ee29c3f7a619cad1e8a3053f785f973fb495\",\n      },\n  };\n\n  constexpr static std::string_view kLookupPermutedInputPrevEvals[][1] = {\n      {\n          \"0x0a609f45a209c538da6647253d32fb29f7a7f910df06c97f9624b7cd8b0579e5\",\n      },\n      {\n          \"0x1acff9a60c9c210c040684f117fad6974057c586c300f96d56ded903d7ef4cac\",\n      },\n  };\n\n  constexpr static std::string_view kLookupPermutedTableEvals[][1] = {\n      {\n          \"0x0501ddc79fddb8f7cf1f7d3a27098f7d42ce72d6cfb6f4970e9def307e5d9c53\",\n      },\n      {\n          \"0x27770cf8d9d6b6d96756b34311135d90410919caf67944b07aeebcac20ac52a1\",\n      },\n  };\n\n  constexpr static std::string_view kHEval =\n      \"0x22000b7c7cb0b3aee0dc8eae939b3dfc5c5482b0e33231fa54cb1ed46e24eab8\";\n\n  static void TestConfig(SimpleLookupConfig<F, 3>& config) {\n    EXPECT_EQ(config.selector(), Selector::Complex(0));\n    EXPECT_EQ(config.table(), LookupTableColumn(FixedColumnKey(0)));\n    EXPECT_EQ(config.advice(), AdviceColumnKey(0));\n  }\n\n  static Circuit GetCircuit() { return Circuit(4); }\n\n  static std::vector<Circuit> Get2Circuits() {\n    Circuit circuit = GetCircuit();\n    return {circuit, std::move(circuit)};\n  }\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_EXAMPLES_SIMPLE_LOOKUP_CIRCUIT_TEST_DATA_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/examples/tracking_testing.md",
    "content": "# Tracking Testing Types Across Example Circuits\n\n## Currently Tested Types\n\n| Circuit             | FloorPlanner       | PCS     | Vendor | LookupType         |\n| ------------------- | ------------------ | ------- | ------ | ------------------ |\n| SimpleCircuit       | SimpleFloorPlanner | SHPlonk | Scroll | Halo2              |\n| SimpleCircuit       | V1FloorPlanner     | SHPlonk | Scroll | Halo2              |\n| SimpleLookupCircuit | SimpleFloorPlanner | SHPlonk | Scroll | Halo2              |\n| SimpleLookupCircuit | V1FloorPlanner     | SHPlonk | Scroll | Halo2              |\n| ShuffleCircuit      | SimpleFloorPlanner | SHPlonk | Scroll | Halo2              |\n| ShuffleCircuit      | V1FloorPlanner     | SHPlonk | Scroll | Halo2              |\n| ShuffleAPICircuit   | SimpleFloorPlanner | SHPlonk | Scroll | LogDerivativeHalo2 |\n| ShuffleAPICircuit   | V1FloorPlanner     | SHPlonk | Scroll | LogDerivativeHalo2 |\n| Fibonacci1Circuit   | SimpleFloorPlanner | SHPlonk | Scroll | Halo2              |\n| Fibonacci1Circuit   | V1FloorPlanner     | SHPlonk | Scroll | Halo2              |\n| Fibonacci2Circuit   | SimpleFloorPlanner | SHPlonk | Scroll | Halo2              |\n| Fibonacci2Circuit   | V1FloorPlanner     | SHPlonk | Scroll | Halo2              |\n| Fibonacci3Circuit   | SimpleFloorPlanner | SHPlonk | Scroll | Halo2              |\n| Fibonacci3Circuit   | V1FloorPlanner     | SHPlonk | Scroll | Halo2              |\n| MultiLookupCircuit  | SimpleFloorPlanner | SHPlonk | Scroll | LogDerivativeHalo2 |\n| MultiLookupCircuit  | SimpleFloorPlanner | GWC     | Scroll | LogDerivativeHalo2 |\n\n**In Progress**:\n\n| Circuit        | FloorPlanner       | PCS | Vendor | LookupType |\n| -------------- | ------------------ | --- | ------ | ---------- |\n| ShuffleCircuit | SimpleFloorPlanner | GWC | Scroll | Halo2      |\n| ShuffleCircuit | V1FloorPlanner     | GWC | Scroll | Halo2      |\n\n## Identical Test Data\n\nThe following circuits result in the same test data when using `SimpleFloorPlanner` or `V1FloorPlanner`\n\n- SimpleLookupCircuit\n- ShuffleCircuit\n- ShuffleAPICircuit\n- Fibonacci2Circuit\n- Fibonacci3Circuit\n\n## Test Data Variables\n\n:red_square: Required variables\n\n:blue_square: Optional variables with flags\n\n:green_square: Optional variables without flags (defaulted)\n\nRefer to the following files to learn more about these variables:\n\n- [circuit_test_data.h](circuit_test_data.h)\n- [circuit_test.cc](circuit_test_data.cc)\n- Any `...test_data.h` file:\n  - [shuffle_circuit_test_data.h](shuffle_circuit_test_data.h)\n  - [shuffle_api_circuit_test_data.h](shuffle_api_circuit_test_data.h)\n  - [simple_circuit_test_data.h](simple_circuit_test_data.h)\n  - [simple_lookup_circuit_test_data.h](simple_lookup_circuit_test_data.h)\n\n1. :red_square: kN\n1. :red_square: kPinnedConstraintSystem\n1. :blue_square: kAssemblyFixedColumns\n1. :blue_square: kAssemblyPermutationColumns\n1. :blue_square: kCycleStoreMapping\n1. :blue_square: kCycleStoreAux\n1. :blue_square: kCycleStoreSizes\n1. :red_square: kSelectors\n1. :green_square: kUsableRows\n1. :red_square: kPinnedVerifyingKey\n1. :red_square: kTranscriptRepr\n1. :blue_square: kLFirst\n1. :blue_square: kLLast\n1. :blue_square: kLActiveRow\n1. :blue_square: kFixedColumns\n1. :blue_square: kFixedPolys\n1. :blue_square: kPermutationsColumns\n1. :blue_square: kPermutationsPolys\n1. :red_square: kProof\n1. :blue_square: kAdviceCommitments\n1. :blue_square: kChallenges\n1. :red_square: kTheta\n1. :blue_square: kLookupPermutedCommitmentsInput\n1. :blue_square: kLookupPermutedCommitmentsTable\n1. :blue_square: kLookupMPolyCommitments\n1. :red_square: kBeta\n1. :red_square: kGamma\n1. :blue_square: kPermutationProductCommitments\n1. :blue_square: kLookupProductCommitments\n1. :blue_square: kLookupSumCommitments\n1. :blue_square: kShuffleProductCommitments\n1. :red_square: kY\n1. :red_square: kVanishingHPolyCommitments\n1. :red_square: kX\n1. :blue_square: kAdviceEvals\n1. :blue_square: kFixedEvals\n1. :blue_square: kCommonPermutationEvals\n1. :blue_square: kPermutationProductEvals\n1. :blue_square: kPermutationProductNextEvals\n1. :blue_square: kPermutationProductLastEvals\n1. :blue_square: kLookupProductEvals\n1. :blue_square: kLookupSumEvals\n1. :blue_square: kLookupProductNextEvals\n1. :blue_square: kLookupSumNextEvals\n1. :blue_square: kLookupPermutedInputEvals\n1. :blue_square: kLookupPermutedInputPrevEvals\n1. :blue_square: kLookupPermutedTableEvals\n1. :blue_square: kLookupMEvals\n1. :blue_square: kShuffleProductEvals\n1. :blue_square: kShuffleProductNextEvals\n1. :red_square: kHEval\n"
  },
  {
    "path": "tachyon/zk/plonk/expressions/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"advice_expression\",\n    hdrs = [\"advice_expression.h\"],\n    deps = [\n        \"//tachyon/zk/expressions:expression\",\n        \"//tachyon/zk/plonk/constraint_system:query\",\n        \"@com_google_absl//absl/memory\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"challenge_expression\",\n    hdrs = [\"challenge_expression.h\"],\n    deps = [\n        \"//tachyon/zk/expressions:expression\",\n        \"//tachyon/zk/plonk/constraint_system:challenge\",\n        \"@com_google_absl//absl/memory\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"compress_expression\",\n    hdrs = [\"compress_expression.h\"],\n    deps = [\n        \":proving_evaluator\",\n        \"//tachyon/base:compiler_specific\",\n        \"//tachyon/base:openmp_util\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"expression_factory\",\n    hdrs = [\"expression_factory.h\"],\n    deps = [\n        \":advice_expression\",\n        \":challenge_expression\",\n        \":fixed_expression\",\n        \":instance_expression\",\n        \":selector_expression\",\n        \"//tachyon/zk/expressions:expression_factory\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"fixed_expression\",\n    hdrs = [\"fixed_expression.h\"],\n    deps = [\n        \"//tachyon/zk/expressions:expression\",\n        \"//tachyon/zk/plonk/constraint_system:query\",\n        \"@com_google_absl//absl/memory\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"instance_expression\",\n    hdrs = [\"instance_expression.h\"],\n    deps = [\n        \"//tachyon/zk/expressions:expression\",\n        \"//tachyon/zk/plonk/constraint_system:query\",\n        \"@com_google_absl//absl/memory\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"proving_evaluator\",\n    hdrs = [\"proving_evaluator.h\"],\n    deps = [\n        \":advice_expression\",\n        \":challenge_expression\",\n        \":fixed_expression\",\n        \":instance_expression\",\n        \":selector_expression\",\n        \"//tachyon/zk/expressions:constant_expression\",\n        \"//tachyon/zk/expressions:evaluator\",\n        \"//tachyon/zk/expressions:negated_expression\",\n        \"//tachyon/zk/expressions:product_expression\",\n        \"//tachyon/zk/expressions:scaled_expression\",\n        \"//tachyon/zk/expressions:sum_expression\",\n        \"//tachyon/zk/plonk/base:multi_phase_ref_table\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"selector_expression\",\n    hdrs = [\"selector_expression.h\"],\n    deps = [\n        \"//tachyon/zk/expressions:expression\",\n        \"//tachyon/zk/plonk/constraint_system:selector\",\n        \"@com_google_absl//absl/memory\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"verifying_evaluator\",\n    hdrs = [\"verifying_evaluator.h\"],\n    deps = [\n        \":advice_expression\",\n        \":challenge_expression\",\n        \":fixed_expression\",\n        \":instance_expression\",\n        \":selector_expression\",\n        \"//tachyon/zk/expressions:constant_expression\",\n        \"//tachyon/zk/expressions:evaluator\",\n        \"//tachyon/zk/expressions:negated_expression\",\n        \"//tachyon/zk/expressions:product_expression\",\n        \"//tachyon/zk/expressions:scaled_expression\",\n        \"//tachyon/zk/expressions:sum_expression\",\n        \"//tachyon/zk/plonk/base:multi_phase_evaluations\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"expression_unittests\",\n    srcs = [\n        \"advice_expression_unittest.cc\",\n        \"challenge_expression_unittest.cc\",\n        \"compress_expression_unittest.cc\",\n        \"expression_unittest.cc\",\n        \"fixed_expression_unittest.cc\",\n        \"instance_expression_unittest.cc\",\n        \"proving_evaluator_unittest.cc\",\n        \"selector_expression_unittest.cc\",\n    ],\n    deps = [\n        \":compress_expression\",\n        \":expression_factory\",\n        \":proving_evaluator\",\n        \"//tachyon/math/finite_fields/test:finite_field_test\",\n        \"//tachyon/math/finite_fields/test:gf7\",\n        \"//tachyon/math/polynomials/univariate:univariate_polynomial\",\n        \"//tachyon/zk/plonk/expressions/evaluator:simple_selector_finder\",\n        \"//tachyon/zk/plonk/expressions/evaluator/test:evaluator_test\",\n        \"//tachyon/zk/plonk/expressions/test:compress_expression_test\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/zk/plonk/expressions/advice_expression.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_EXPRESSIONS_ADVICE_EXPRESSION_H_\n#define TACHYON_ZK_PLONK_EXPRESSIONS_ADVICE_EXPRESSION_H_\n\n#include <memory>\n#include <string>\n\n#include \"absl/memory/memory.h\"\n\n#include \"tachyon/zk/expressions/expression.h\"\n#include \"tachyon/zk/plonk/constraint_system/query.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F>\nclass ExpressionFactory;\n\ntemplate <typename F>\nclass AdviceExpression : public Expression<F> {\n public:\n  static std::unique_ptr<AdviceExpression> CreateForTesting(\n      const AdviceQuery& query) {\n    return absl::WrapUnique(new AdviceExpression(query));\n  }\n\n  const AdviceQuery& query() const { return query_; }\n\n  // Expression methods\n  size_t Degree() const override { return 1; }\n\n  uint64_t Complexity() const override { return 1; }\n\n  std::unique_ptr<Expression<F>> Clone() const override {\n    return absl::WrapUnique(new AdviceExpression(query_));\n  }\n\n  std::string ToString() const override {\n    return absl::Substitute(\"{type: $0, column: $1}\",\n                            ExpressionTypeToString(this->type_),\n                            query_.ToString());\n  }\n\n  bool operator==(const Expression<F>& other) const override {\n    if (!Expression<F>::operator==(other)) return false;\n    const AdviceExpression* advice = other.ToAdvice();\n    return query_ == advice->query_;\n  }\n\n private:\n  friend class ExpressionFactory<F>;\n\n  explicit AdviceExpression(const AdviceQuery& query)\n      : Expression<F>(ExpressionType::kAdvice), query_(query) {}\n\n  AdviceQuery query_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_EXPRESSIONS_ADVICE_EXPRESSION_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/expressions/advice_expression_unittest.cc",
    "content": "#include \"tachyon/zk/plonk/expressions/advice_expression.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n\nnamespace tachyon::zk::plonk {\n\nusing F = math::GF7;\n\nclass AdviceExpressionTest : public math::FiniteFieldTest<F> {};\n\nTEST_F(AdviceExpressionTest, DegreeComplexity) {\n  std::unique_ptr<AdviceExpression<F>> expr =\n      AdviceExpression<F>::CreateForTesting(\n          AdviceQuery(1, Rotation(1), AdviceColumnKey(1, Phase(0))));\n  EXPECT_EQ(expr->Degree(), size_t{1});\n  EXPECT_EQ(expr->Complexity(), uint64_t{1});\n}\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/expressions/challenge_expression.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_EXPRESSIONS_CHALLENGE_EXPRESSION_H_\n#define TACHYON_ZK_PLONK_EXPRESSIONS_CHALLENGE_EXPRESSION_H_\n\n#include <memory>\n#include <string>\n#include <utility>\n\n#include \"absl/memory/memory.h\"\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/zk/expressions/expression.h\"\n#include \"tachyon/zk/plonk/constraint_system/challenge.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F>\nclass ExpressionFactory;\n\ntemplate <typename F>\nclass ChallengeExpression : public Expression<F> {\n public:\n  static std::unique_ptr<ChallengeExpression> CreateForTesting(\n      Challenge challenge) {\n    return absl::WrapUnique(new ChallengeExpression(challenge));\n  }\n\n  Challenge challenge() const { return challenge_; }\n\n  // Expression methods\n  size_t Degree() const override { return 0; }\n\n  uint64_t Complexity() const override { return 0; }\n\n  std::unique_ptr<Expression<F>> Clone() const override {\n    return absl::WrapUnique(new ChallengeExpression(challenge_));\n  }\n\n  std::string ToString() const override {\n    return absl::Substitute(\"{type: $0, challenge: $1}\",\n                            ExpressionTypeToString(this->type_),\n                            challenge_.ToString());\n  }\n\n  bool operator==(const Expression<F>& other) const override {\n    if (!Expression<F>::operator==(other)) return false;\n    const ChallengeExpression* challenge = other.ToChallenge();\n    return challenge_ == challenge->challenge_;\n  }\n\n private:\n  friend class ExpressionFactory<F>;\n\n  explicit ChallengeExpression(Challenge challenge)\n      : Expression<F>(ExpressionType::kChallenge), challenge_(challenge) {}\n\n  Challenge challenge_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_EXPRESSIONS_CHALLENGE_EXPRESSION_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/expressions/challenge_expression_unittest.cc",
    "content": "#include \"tachyon/zk/plonk/expressions/challenge_expression.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n\nnamespace tachyon::zk::plonk {\n\nusing F = math::GF7;\n\nclass ChallengeExpressionTest : public math::FiniteFieldTest<F> {};\n\nTEST_F(ChallengeExpressionTest, DegreeComplexity) {\n  std::unique_ptr<ChallengeExpression<F>> expr =\n      ChallengeExpression<F>::CreateForTesting(Challenge(1, Phase(1)));\n  EXPECT_EQ(expr->Degree(), size_t{0});\n  EXPECT_EQ(expr->Complexity(), uint64_t{0});\n}\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/expressions/compress_expression.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_EXPRESSIONS_COMPRESS_EXPRESSION_H_\n#define TACHYON_ZK_PLONK_EXPRESSIONS_COMPRESS_EXPRESSION_H_\n\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/compiler_specific.h\"\n#include \"tachyon/base/openmp_util.h\"\n#include \"tachyon/zk/plonk/expressions/proving_evaluator.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename Domain, typename Evals, typename F>\nEvals CompressExpressions(\n    const Domain* domain,\n    const std::vector<std::unique_ptr<Expression<F>>>& expressions,\n    const F& theta, const ProvingEvaluator<Evals>& evaluator_tpl) {\n  Evals compressed_evals = domain->template Zero<Evals>();\n  std::vector<F>& compressed_values = compressed_evals.evaluations();\n\n  for (size_t expr_idx = 0; expr_idx < expressions.size(); ++expr_idx) {\n    if (UNLIKELY(expr_idx == 0)) {\n      OMP_PARALLEL_FOR(size_t i = 0; i < compressed_values.size(); ++i) {\n        ProvingEvaluator<Evals> evaluator = evaluator_tpl;\n        evaluator.set_idx(i);\n        compressed_values[i] = evaluator.Evaluate(expressions[expr_idx].get());\n      }\n    } else {\n      OMP_PARALLEL_FOR(size_t i = 0; i < compressed_values.size(); ++i) {\n        ProvingEvaluator<Evals> evaluator = evaluator_tpl;\n        evaluator.set_idx(i);\n        compressed_values[i] *= theta;\n        compressed_values[i] += evaluator.Evaluate(expressions[expr_idx].get());\n      }\n    }\n  }\n  return compressed_evals;\n}\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_EXPRESSIONS_COMPRESS_EXPRESSION_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/expressions/compress_expression_unittest.cc",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#include \"tachyon/zk/plonk/expressions/compress_expression.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/zk/expressions/expression_factory.h\"\n#include \"tachyon/zk/plonk/expressions/test/compress_expression_test.h\"\n\nnamespace tachyon::zk::plonk {\n\nTEST_F(CompressExpressionTest, CompressExpressions) {\n  const size_t kExpressionSize = 10;\n  std::vector<F> values =\n      base::CreateVector(kExpressionSize, [](size_t i) { return F::Random(); });\n\n  // setting |expressions| to be compressed\n  std::vector<std::unique_ptr<Expression<F>>> expressions =\n      base::CreateVector(kExpressionSize, [&values](size_t i) {\n        return ExpressionFactory<F>::Constant(values[i]);\n      });\n\n  size_t n = prover_->pcs().N();\n  ProvingEvaluator<Evals> evaluator = evaluator_;\n  std::vector<F> expected(n, F::Zero());\n  for (size_t i = 0; i < expressions.size(); ++i) {\n    F value = evaluator.Evaluate(expressions[i].get());\n    for (size_t j = 0; j < n; ++j) {\n      expected[j] *= theta_;\n      expected[j] += value;\n    }\n  }\n\n  Evals out =\n      CompressExpressions(prover_->domain(), expressions, theta_, evaluator_);\n  EXPECT_EQ(out, Evals(std::move(expected)));\n}\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/expressions/evaluator/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"cells_querier\",\n    hdrs = [\"cells_querier.h\"],\n    deps = [\n        \"//tachyon/base:logging\",\n        \"//tachyon/zk/expressions:evaluator\",\n        \"//tachyon/zk/expressions:negated_expression\",\n        \"//tachyon/zk/expressions:product_expression\",\n        \"//tachyon/zk/expressions:scaled_expression\",\n        \"//tachyon/zk/expressions:sum_expression\",\n        \"//tachyon/zk/plonk/expressions:advice_expression\",\n        \"//tachyon/zk/plonk/expressions:fixed_expression\",\n        \"//tachyon/zk/plonk/expressions:instance_expression\",\n        \"//tachyon/zk/plonk/expressions:selector_expression\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"identifier\",\n    hdrs = [\"identifier.h\"],\n    deps = [\n        \"//tachyon/base:logging\",\n        \"//tachyon/zk/expressions:constant_expression\",\n        \"//tachyon/zk/expressions:evaluator\",\n        \"//tachyon/zk/expressions:negated_expression\",\n        \"//tachyon/zk/expressions:product_expression\",\n        \"//tachyon/zk/expressions:scaled_expression\",\n        \"//tachyon/zk/expressions:sum_expression\",\n        \"//tachyon/zk/plonk/expressions:advice_expression\",\n        \"//tachyon/zk/plonk/expressions:challenge_expression\",\n        \"//tachyon/zk/plonk/expressions:fixed_expression\",\n        \"//tachyon/zk/plonk/expressions:instance_expression\",\n        \"//tachyon/zk/plonk/expressions:selector_expression\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"selector_replacer\",\n    hdrs = [\"selector_replacer.h\"],\n    deps = [\n        \"//tachyon/base:logging\",\n        \"//tachyon/base:ref\",\n        \"//tachyon/zk/expressions:evaluator\",\n        \"//tachyon/zk/expressions:negated_expression\",\n        \"//tachyon/zk/expressions:product_expression\",\n        \"//tachyon/zk/expressions:scaled_expression\",\n        \"//tachyon/zk/expressions:sum_expression\",\n        \"//tachyon/zk/plonk/expressions:selector_expression\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"simple_selector_extractor\",\n    hdrs = [\"simple_selector_extractor.h\"],\n    deps = [\n        \"//tachyon/zk/expressions:evaluator\",\n        \"//tachyon/zk/expressions:negated_expression\",\n        \"//tachyon/zk/expressions:product_expression\",\n        \"//tachyon/zk/expressions:scaled_expression\",\n        \"//tachyon/zk/expressions:sum_expression\",\n        \"//tachyon/zk/plonk/expressions:selector_expression\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"simple_selector_finder\",\n    hdrs = [\"simple_selector_finder.h\"],\n    deps = [\n        \"//tachyon/zk/expressions:evaluator\",\n        \"//tachyon/zk/expressions:negated_expression\",\n        \"//tachyon/zk/expressions:product_expression\",\n        \"//tachyon/zk/expressions:scaled_expression\",\n        \"//tachyon/zk/expressions:sum_expression\",\n        \"//tachyon/zk/plonk/expressions:selector_expression\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"expression_unittests\",\n    srcs = [\n        \"selector_replacer_unittest.cc\",\n        \"simple_selector_extractor_unittest.cc\",\n        \"simple_selector_finder_unittest.cc\",\n    ],\n    deps = [\n        \":selector_replacer\",\n        \":simple_selector_extractor\",\n        \":simple_selector_finder\",\n        \"//tachyon/zk/plonk/expressions:expression_factory\",\n        \"//tachyon/zk/plonk/expressions/evaluator/test:evaluator_test\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/zk/plonk/expressions/evaluator/cells_querier.h",
    "content": "#ifndef TACHYON_ZK_PLONK_EXPRESSIONS_EVALUATOR_CELLS_QUERIER_H_\n#define TACHYON_ZK_PLONK_EXPRESSIONS_EVALUATOR_CELLS_QUERIER_H_\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/zk/expressions/expression.h\"\n#include \"tachyon/zk/expressions/negated_expression.h\"\n#include \"tachyon/zk/expressions/product_expression.h\"\n#include \"tachyon/zk/expressions/scaled_expression.h\"\n#include \"tachyon/zk/expressions/sum_expression.h\"\n#include \"tachyon/zk/plonk/constraint_system/virtual_cells.h\"\n#include \"tachyon/zk/plonk/expressions/advice_expression.h\"\n#include \"tachyon/zk/plonk/expressions/fixed_expression.h\"\n#include \"tachyon/zk/plonk/expressions/instance_expression.h\"\n#include \"tachyon/zk/plonk/expressions/selector_expression.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F>\nclass CellsQuerier : public Evaluator<F, void> {\n public:\n  explicit CellsQuerier(VirtualCells<F>& cells) : cells_(cells) {}\n\n  // Evaluator methods\n  void Evaluate(const Expression<F>* input) override {\n    switch (input->type()) {\n      case ExpressionType::kConstant:\n        return;\n      case ExpressionType::kNegated:\n        Evaluate(input->ToNegated()->expr());\n        return;\n      case ExpressionType::kSum:\n        Evaluate(input->ToSum()->left());\n        Evaluate(input->ToSum()->right());\n        return;\n      case ExpressionType::kProduct:\n        Evaluate(input->ToProduct()->left());\n        Evaluate(input->ToProduct()->right());\n        return;\n      case ExpressionType::kScaled:\n        Evaluate(input->ToScaled()->expr());\n        return;\n      case ExpressionType::kFixed: {\n        FixedQuery& query = const_cast<FixedQuery&>(input->ToFixed()->query());\n        if (!query.HasIndex()) {\n          cells_.queried_cells_.emplace_back(query.column(), query.rotation());\n          query.SetIndex(\n              cells_.meta_->QueryFixedIndex(query.column(), query.rotation()));\n        }\n        return;\n      }\n      case ExpressionType::kAdvice: {\n        AdviceQuery& query =\n            const_cast<AdviceQuery&>(input->ToAdvice()->query());\n        if (!query.HasIndex()) {\n          cells_.queried_cells_.emplace_back(query.column(), query.rotation());\n          query.SetIndex(\n              cells_.meta_->QueryAdviceIndex(query.column(), query.rotation()));\n        }\n        return;\n      }\n      case ExpressionType::kInstance: {\n        InstanceQuery& query =\n            const_cast<InstanceQuery&>(input->ToInstance()->query());\n        if (!query.HasIndex()) {\n          cells_.queried_cells_.emplace_back(query.column(), query.rotation());\n          query.SetIndex(cells_.meta_->QueryInstanceIndex(query.column(),\n                                                          query.rotation()));\n        }\n        return;\n      }\n      case ExpressionType::kChallenge:\n        return;\n      case ExpressionType::kSelector: {\n        Selector selector = input->ToSelector()->selector();\n        // TODO(chokobole): should it be std::set<Selector>?\n        if (!base::Contains(cells_.queried_selectors_, selector)) {\n          cells_.queried_selectors_.push_back(selector);\n        }\n        return;\n      }\n      case ExpressionType::kFirstRow:\n      case ExpressionType::kLastRow:\n      case ExpressionType::kTransition:\n      case ExpressionType::kVariable:\n        NOTREACHED() << \"AIR expression \"\n                     << ExpressionTypeToString(input->type())\n                     << \" is not allowed in plonk!\";\n    }\n    NOTREACHED();\n  }\n\n private:\n  VirtualCells<F>& cells_;\n};\n\ntemplate <typename F>\nvoid QueryCells(const Expression<F>* input, VirtualCells<F>& cells) {\n  CellsQuerier<F> querier(cells);\n  querier.Evaluate(input);\n}\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_EXPRESSIONS_EVALUATOR_CELLS_QUERIER_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/expressions/evaluator/identifier.h",
    "content": "#ifndef TACHYON_ZK_PLONK_EXPRESSIONS_EVALUATOR_IDENTIFIER_H_\n#define TACHYON_ZK_PLONK_EXPRESSIONS_EVALUATOR_IDENTIFIER_H_\n\n#include <sstream>\n#include <string>\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/zk/expressions/constant_expression.h\"\n#include \"tachyon/zk/expressions/expression.h\"\n#include \"tachyon/zk/expressions/negated_expression.h\"\n#include \"tachyon/zk/expressions/product_expression.h\"\n#include \"tachyon/zk/expressions/scaled_expression.h\"\n#include \"tachyon/zk/expressions/sum_expression.h\"\n#include \"tachyon/zk/plonk/expressions/advice_expression.h\"\n#include \"tachyon/zk/plonk/expressions/challenge_expression.h\"\n#include \"tachyon/zk/plonk/expressions/fixed_expression.h\"\n#include \"tachyon/zk/plonk/expressions/instance_expression.h\"\n#include \"tachyon/zk/plonk/expressions/selector_expression.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F>\nclass Identifier : public Evaluator<F, void> {\n public:\n  explicit Identifier(std::ostringstream& ss) : ss_(ss) {}\n\n  // Evaluator methods\n  void Evaluate(const Expression<F>* input) override {\n    switch (input->type()) {\n      case ExpressionType::kConstant:\n        ss_ << input->ToConstant()->value().ToString();\n        return;\n      case ExpressionType::kNegated:\n        ss_ << \"(-\";\n        Evaluate(input->ToNegated()->expr());\n        ss_ << \")\";\n        return;\n      case ExpressionType::kSum:\n        ss_ << \"(\";\n        Evaluate(input->ToSum()->left());\n        ss_ << \"+\";\n        Evaluate(input->ToSum()->right());\n        ss_ << \")\";\n        return;\n      case ExpressionType::kProduct:\n        ss_ << \"(\";\n        Evaluate(input->ToProduct()->left());\n        ss_ << \"*\";\n        Evaluate(input->ToProduct()->right());\n        ss_ << \")\";\n        return;\n      case ExpressionType::kScaled:\n        ss_ << \"*\";\n        ss_ << input->ToScaled()->scale().ToString();\n        return;\n      case ExpressionType::kFixed: {\n        const FixedExpression<F>* fixed = input->ToFixed();\n        ss_ << \"fixed[\" << fixed->query().column().index() << \"][\"\n            << fixed->query().rotation().value() << \"]\";\n        return;\n      }\n      case ExpressionType::kAdvice: {\n        const AdviceExpression<F>* advice = input->ToAdvice();\n        ss_ << \"advice[\" << advice->query().column().index() << \"][\"\n            << advice->query().rotation().value() << \"]\";\n        return;\n      }\n      case ExpressionType::kInstance: {\n        const InstanceExpression<F>* instance = input->ToInstance();\n        ss_ << \"instance[\" << instance->query().column().index() << \"][\"\n            << instance->query().rotation().value() << \"]\";\n        return;\n      }\n      case ExpressionType::kChallenge: {\n        const ChallengeExpression<F>* challenge = input->ToChallenge();\n        ss_ << \"challenge[\" << challenge->challenge().index() << \"]\";\n        return;\n      }\n      case ExpressionType::kSelector: {\n        const SelectorExpression<F>* selector = input->ToSelector();\n        ss_ << \"selector[\" << selector->selector().index() << \"]\";\n        return;\n      }\n      case ExpressionType::kFirstRow:\n      case ExpressionType::kLastRow:\n      case ExpressionType::kTransition:\n      case ExpressionType::kVariable:\n        NOTREACHED() << \"AIR expression \"\n                     << ExpressionTypeToString(input->type())\n                     << \" is not allowed in plonk!\";\n    }\n    NOTREACHED();\n  }\n\n private:\n  std::ostringstream& ss_;\n};\n\ntemplate <typename F>\nstd::string GetIdentifier(const Expression<F>* input) {\n  std::ostringstream ss;\n  Identifier<F> identifier(ss);\n  identifier.Evaluate(input);\n  return ss.str();\n}\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_EXPRESSIONS_EVALUATOR_IDENTIFIER_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/expressions/evaluator/selector_replacer.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_EXPRESSIONS_EVALUATOR_SELECTOR_REPLACER_H_\n#define TACHYON_ZK_PLONK_EXPRESSIONS_EVALUATOR_SELECTOR_REPLACER_H_\n\n#include <memory>\n#include <vector>\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/ref.h\"\n#include \"tachyon/zk/expressions/evaluator.h\"\n#include \"tachyon/zk/expressions/negated_expression.h\"\n#include \"tachyon/zk/expressions/product_expression.h\"\n#include \"tachyon/zk/expressions/scaled_expression.h\"\n#include \"tachyon/zk/expressions/sum_expression.h\"\n#include \"tachyon/zk/plonk/expressions/selector_expression.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F>\nclass SelectorReplacer : public Evaluator<F, std::unique_ptr<Expression<F>>> {\n public:\n  SelectorReplacer(\n      const std::vector<base::Ref<const Expression<F>>>& replacements,\n      const bool must_be_non_simple)\n      : replacements_(replacements), must_be_non_simple_(must_be_non_simple) {}\n\n  // Evaluator methods\n  std::unique_ptr<Expression<F>> Evaluate(const Expression<F>* input) override {\n    switch (input->type()) {\n      case ExpressionType::kConstant:\n      case ExpressionType::kFixed:\n      case ExpressionType::kAdvice:\n      case ExpressionType::kInstance:\n      case ExpressionType::kChallenge:\n        return input->Clone();\n      case ExpressionType::kSelector: {\n        Selector selector = input->ToSelector()->selector();\n        if (must_be_non_simple_) {\n          // Simple selectors are prohibited from appearing in\n          // expressions in the lookup argument by |ConstraintSystem|.\n          if (selector.is_simple()) {\n            LOG(DFATAL) << \"Simple selector found in lookup argument\";\n          }\n        }\n        return replacements_[selector.index()]->Clone();\n      }\n      case ExpressionType::kNegated:\n        return ExpressionFactory<F>::Negated(\n            Evaluate(input->ToNegated()->expr()));\n      case ExpressionType::kSum: {\n        const SumExpression<F>* sum = input->ToSum();\n        return ExpressionFactory<F>::Sum(Evaluate(sum->left()),\n                                         Evaluate(sum->right()));\n      }\n      case ExpressionType::kProduct: {\n        const ProductExpression<F>* product = input->ToProduct();\n        return ExpressionFactory<F>::Product(Evaluate(product->left()),\n                                             Evaluate(product->right()));\n      }\n      case ExpressionType::kScaled: {\n        const ScaledExpression<F>* scaled = input->ToScaled();\n        return ExpressionFactory<F>::Scaled(Evaluate(scaled->expr()),\n                                            scaled->scale());\n      }\n      case ExpressionType::kFirstRow:\n      case ExpressionType::kLastRow:\n      case ExpressionType::kTransition:\n      case ExpressionType::kVariable:\n        NOTREACHED() << \"AIR expression \"\n                     << ExpressionTypeToString(input->type())\n                     << \" is not allowed in plonk!\";\n    }\n    NOTREACHED();\n    return nullptr;\n  }\n\n private:\n  const std::vector<base::Ref<const Expression<F>>>& replacements_;\n  const bool must_be_non_simple_;\n};\n\ntemplate <typename F>\nstd::unique_ptr<Expression<F>> ReplaceSelectors(\n    const Expression<F>* input,\n    const std::vector<base::Ref<const Expression<F>>>& replacements,\n    bool must_be_non_simple) {\n  SelectorReplacer<F> selector_replacer(replacements, must_be_non_simple);\n  return selector_replacer.Evaluate(input);\n}\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_EXPRESSIONS_EVALUATOR_SELECTOR_REPLACER_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/expressions/evaluator/selector_replacer_unittest.cc",
    "content": "#include \"tachyon/zk/plonk/expressions/evaluator/selector_replacer.h\"\n\n#include <memory>\n\n#include \"tachyon/zk/plonk/expressions/evaluator/simple_selector_finder.h\"\n#include \"tachyon/zk/plonk/expressions/evaluator/test/evaluator_test.h\"\n#include \"tachyon/zk/plonk/expressions/expression_factory.h\"\n\nnamespace tachyon::zk::plonk {\n\nusing Expr = std::unique_ptr<Expression<GF7>>;\n\nclass SelectorReplacerTest : public EvaluatorTest {};\n\nTEST_F(SelectorReplacerTest, Constant) {\n  GF7 value = GF7::Random();\n  Expr expr = ExpressionFactory<GF7>::Constant(value);\n  EXPECT_EQ(*expr, *ReplaceSelectors(expr.get(), {}, false));\n}\n\nTEST_F(SelectorReplacerTest, Selector) {\n  Expr expr = ExpressionFactory<GF7>::Selector(Selector::Simple(1));\n  std::vector<std::unique_ptr<Expression<GF7>>> owned_replacements;\n  owned_replacements.push_back(\n      ExpressionFactory<GF7>::Selector(Selector::Simple(2)));\n  owned_replacements.push_back(\n      ExpressionFactory<GF7>::Selector(Selector::Complex(3)));\n  std::vector<base::Ref<const Expression<GF7>>> replacements;\n  replacements.emplace_back(owned_replacements[0].get());\n  replacements.emplace_back(owned_replacements[1].get());\n\n  EXPECT_DEBUG_DEATH(ReplaceSelectors(expr.get(), replacements, true), \"\");\n  EXPECT_EQ(*ReplaceSelectors(expr.get(), replacements, false),\n            *replacements[1]);\n\n  expr = ExpressionFactory<GF7>::Selector(Selector::Complex(1));\n  EXPECT_EQ(*ReplaceSelectors(expr.get(), replacements, false),\n            *replacements[1]);\n}\n\nTEST_F(SelectorReplacerTest, Fixed) {\n  struct {\n    int32_t rotation;\n    size_t column_index;\n  } tests[] = {\n      {1, 0},\n      {2, 1},\n  };\n\n  for (const auto& test : tests) {\n    FixedQuery query(1, Rotation(test.rotation),\n                     FixedColumnKey(test.column_index));\n    Expr expr = ExpressionFactory<GF7>::Fixed(query);\n    EXPECT_EQ(*expr, *ReplaceSelectors(expr.get(), {}, false));\n  }\n}\n\nTEST_F(SelectorReplacerTest, Advice) {\n  struct {\n    int32_t rotation;\n    size_t column_index;\n  } tests[] = {\n      {6, 2},\n      {7, 3},\n  };\n\n  for (const auto& test : tests) {\n    AdviceQuery query(1, Rotation(test.rotation),\n                      AdviceColumnKey(test.column_index, Phase(0)));\n    Expr expr = ExpressionFactory<GF7>::Advice(query);\n    EXPECT_EQ(*expr, *ReplaceSelectors(expr.get(), {}, false));\n  }\n}\n\nTEST_F(SelectorReplacerTest, Instance) {\n  struct {\n    int32_t rotation;\n    size_t column_index;\n  } tests[] = {\n      {1, 1},\n      {2, 2},\n  };\n\n  for (const auto& test : tests) {\n    InstanceQuery query(1, Rotation(test.rotation),\n                        InstanceColumnKey(test.column_index));\n    Expr expr = ExpressionFactory<GF7>::Instance(query);\n    EXPECT_EQ(*expr, *ReplaceSelectors(expr.get(), {}, false));\n  }\n}\n\nTEST_F(SelectorReplacerTest, Challenges) {\n  Expr expr = ExpressionFactory<GF7>::Challenge(Challenge(1, Phase(0)));\n  EXPECT_EQ(*expr, *ReplaceSelectors(expr.get(), {}, false));\n}\n\nTEST_F(SelectorReplacerTest, Negated) {\n  GF7 value = GF7::Random();\n  Expr expr =\n      ExpressionFactory<GF7>::Negated(ExpressionFactory<GF7>::Constant(value));\n  EXPECT_EQ(*expr, *ReplaceSelectors(expr.get(), {}, false));\n}\n\nTEST_F(SelectorReplacerTest, Sum) {\n  GF7 a = GF7::Random();\n  GF7 b = GF7::Random();\n  Expr expr = ExpressionFactory<GF7>::Sum(ExpressionFactory<GF7>::Constant(a),\n                                          ExpressionFactory<GF7>::Constant(b));\n  EXPECT_FALSE(ContainsSimpleSelector(expr.get()));\n}\n\nTEST_F(SelectorReplacerTest, Product) {\n  GF7 a = GF7::Random();\n  GF7 b = GF7::Random();\n  Expr expr = ExpressionFactory<GF7>::Product(\n      ExpressionFactory<GF7>::Constant(a), ExpressionFactory<GF7>::Constant(b));\n  EXPECT_EQ(*expr, *ReplaceSelectors(expr.get(), {}, false));\n}\n\nTEST_F(SelectorReplacerTest, Scaled) {\n  GF7 a = GF7::Random();\n  GF7 b = GF7::Random();\n  Expr expr =\n      ExpressionFactory<GF7>::Scaled(ExpressionFactory<GF7>::Constant(a), b);\n  EXPECT_EQ(*expr, *ReplaceSelectors(expr.get(), {}, false));\n}\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/expressions/evaluator/simple_selector_extractor.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_EXPRESSIONS_EVALUATOR_SIMPLE_SELECTOR_EXTRACTOR_H_\n#define TACHYON_ZK_PLONK_EXPRESSIONS_EVALUATOR_SIMPLE_SELECTOR_EXTRACTOR_H_\n\n#include <optional>\n\n#include \"tachyon/zk/expressions/evaluator.h\"\n#include \"tachyon/zk/expressions/negated_expression.h\"\n#include \"tachyon/zk/expressions/product_expression.h\"\n#include \"tachyon/zk/expressions/scaled_expression.h\"\n#include \"tachyon/zk/expressions/sum_expression.h\"\n#include \"tachyon/zk/plonk/expressions/selector_expression.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F>\nclass SimpleSelectorExtractor : public Evaluator<F, std::optional<Selector>> {\n public:\n  // Evaluator methods\n  std::optional<Selector> Evaluate(const Expression<F>* input) override {\n    switch (input->type()) {\n      case ExpressionType::kConstant:\n        return std::nullopt;\n      case ExpressionType::kSelector: {\n        Selector selector = input->ToSelector()->selector();\n        if (selector.is_simple()) {\n          return selector;\n        }\n        return std::nullopt;\n      }\n      case ExpressionType::kFixed:\n        return std::nullopt;\n      case ExpressionType::kAdvice:\n        return std::nullopt;\n      case ExpressionType::kInstance:\n        return std::nullopt;\n      case ExpressionType::kChallenge:\n        return std::nullopt;\n      case ExpressionType::kNegated: {\n        return Evaluate(input->ToNegated()->expr());\n      }\n      case ExpressionType::kSum: {\n        const SumExpression<F>* sum = input->ToSum();\n        return Pick(Evaluate(sum->left()), Evaluate(sum->right()));\n      }\n      case ExpressionType::kProduct: {\n        const ProductExpression<F>* product = input->ToProduct();\n        return Pick(Evaluate(product->left()), Evaluate(product->right()));\n      }\n      case ExpressionType::kScaled: {\n        const ScaledExpression<F>* scaled = input->ToScaled();\n        return Evaluate(scaled->expr());\n      }\n      case ExpressionType::kFirstRow:\n      case ExpressionType::kLastRow:\n      case ExpressionType::kTransition:\n      case ExpressionType::kVariable:\n        NOTREACHED() << \"AIR expression \"\n                     << ExpressionTypeToString(input->type())\n                     << \" is not allowed in plonk!\";\n    }\n    NOTREACHED();\n    return std::nullopt;\n  }\n\n private:\n  static std::optional<Selector> Pick(const std::optional<Selector>& left,\n                                      const std::optional<Selector>& right) {\n    CHECK(!(left.has_value() && right.has_value()))\n        << \"two simple selectors cannot be in the same expression\";\n    if (left.has_value()) return left;\n    if (right.has_value()) return right;\n    return std::nullopt;\n  }\n};\n\ntemplate <typename F>\nstd::optional<Selector> ExtractSimpleSelector(const Expression<F>* input) {\n  SimpleSelectorExtractor<F> extractor;\n  return extractor.Evaluate(input);\n}\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_EXPRESSIONS_EVALUATOR_SIMPLE_SELECTOR_EXTRACTOR_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/expressions/evaluator/simple_selector_extractor_unittest.cc",
    "content": "#include \"tachyon/zk/plonk/expressions/evaluator/simple_selector_extractor.h\"\n\n#include <memory>\n\n#include \"tachyon/zk/plonk/expressions/evaluator/test/evaluator_test.h\"\n#include \"tachyon/zk/plonk/expressions/expression_factory.h\"\n\nnamespace tachyon::zk::plonk {\n\nusing Expr = std::unique_ptr<Expression<GF7>>;\n\nclass SimpleSelectorExtractorTest : public EvaluatorTest {};\n\nTEST_F(SimpleSelectorExtractorTest, Constant) {\n  GF7 value = GF7::Random();\n  std::unique_ptr<Expression<GF7>> expr =\n      ExpressionFactory<GF7>::Constant(value);\n  EXPECT_FALSE(ExtractSimpleSelector(expr.get()).has_value());\n}\n\nTEST_F(SimpleSelectorExtractorTest, Selector) {\n  Selector expected_selector = Selector::Simple(1);\n  Expr expr = ExpressionFactory<GF7>::Selector(expected_selector);\n  std::optional<Selector> selector = ExtractSimpleSelector(expr.get());\n  EXPECT_EQ(selector.value(), expected_selector);\n  expr = ExpressionFactory<GF7>::Selector(Selector::Complex(1));\n  EXPECT_FALSE(ExtractSimpleSelector(expr.get()).has_value());\n}\n\nTEST_F(SimpleSelectorExtractorTest, Fixed) {\n  struct {\n    int32_t rotation;\n    size_t column_index;\n  } tests[] = {\n      {1, 0},\n      {2, 1},\n  };\n\n  for (const auto& test : tests) {\n    FixedQuery query(1, Rotation(test.rotation),\n                     FixedColumnKey(test.column_index));\n    Expr expr = ExpressionFactory<GF7>::Fixed(query);\n    EXPECT_FALSE(ExtractSimpleSelector(expr.get()).has_value());\n  }\n}\n\nTEST_F(SimpleSelectorExtractorTest, Advice) {\n  struct {\n    int32_t rotation;\n    size_t column_index;\n  } tests[] = {\n      {6, 2},\n      {7, 3},\n  };\n\n  for (const auto& test : tests) {\n    AdviceQuery query(1, Rotation(test.rotation),\n                      AdviceColumnKey(test.column_index, Phase(0)));\n    Expr expr = ExpressionFactory<GF7>::Advice(query);\n    EXPECT_FALSE(ExtractSimpleSelector(expr.get()).has_value());\n  }\n}\n\nTEST_F(SimpleSelectorExtractorTest, Instance) {\n  struct {\n    int32_t rotation;\n    size_t column_index;\n  } tests[] = {\n      {1, 1},\n      {2, 2},\n  };\n\n  for (const auto& test : tests) {\n    InstanceQuery query(1, Rotation(test.rotation),\n                        InstanceColumnKey(test.column_index));\n    Expr expr = ExpressionFactory<GF7>::Instance(query);\n    EXPECT_FALSE(ExtractSimpleSelector(expr.get()).has_value());\n  }\n}\n\nTEST_F(SimpleSelectorExtractorTest, Challenges) {\n  Expr expr = ExpressionFactory<GF7>::Challenge(Challenge(1, Phase(0)));\n  EXPECT_FALSE(ExtractSimpleSelector(expr.get()).has_value());\n}\n\nTEST_F(SimpleSelectorExtractorTest, Negated) {\n  GF7 value = GF7::Random();\n  Expr expr =\n      ExpressionFactory<GF7>::Negated(ExpressionFactory<GF7>::Constant(value));\n  EXPECT_FALSE(ExtractSimpleSelector(expr.get()).has_value());\n}\n\nTEST_F(SimpleSelectorExtractorTest, Sum) {\n  GF7 a = GF7::Random();\n  GF7 b = GF7::Random();\n  Expr expr = ExpressionFactory<GF7>::Sum(ExpressionFactory<GF7>::Constant(a),\n                                          ExpressionFactory<GF7>::Constant(b));\n  EXPECT_FALSE(ExtractSimpleSelector(expr.get()).has_value());\n  Selector expected_selector = Selector::Simple(1);\n  expr = ExpressionFactory<GF7>::Sum(\n      ExpressionFactory<GF7>::Constant(a),\n      ExpressionFactory<GF7>::Selector(expected_selector));\n  std::optional<Selector> selector = ExtractSimpleSelector(expr.get());\n  EXPECT_EQ(selector.value(), expected_selector);\n}\n\nTEST_F(SimpleSelectorExtractorTest, Product) {\n  GF7 a = GF7::Random();\n  GF7 b = GF7::Random();\n  Expr expr = ExpressionFactory<GF7>::Product(\n      ExpressionFactory<GF7>::Constant(a), ExpressionFactory<GF7>::Constant(b));\n  EXPECT_FALSE(ExtractSimpleSelector(expr.get()).has_value());\n  Selector expected_selector = Selector::Simple(1);\n  expr = ExpressionFactory<GF7>::Product(\n      ExpressionFactory<GF7>::Constant(a),\n      ExpressionFactory<GF7>::Selector(expected_selector));\n  std::optional<Selector> selector = ExtractSimpleSelector(expr.get());\n  EXPECT_EQ(selector.value(), expected_selector);\n}\n\nTEST_F(SimpleSelectorExtractorTest, Scaled) {\n  GF7 a = GF7::Random();\n  GF7 b = GF7::Random();\n  Expr expr =\n      ExpressionFactory<GF7>::Scaled(ExpressionFactory<GF7>::Constant(a), b);\n  EXPECT_FALSE(ExtractSimpleSelector(expr.get()).has_value());\n  Selector expected_selector = Selector::Simple(1);\n  expr = ExpressionFactory<GF7>::Scaled(\n      ExpressionFactory<GF7>::Selector(expected_selector), GF7(3));\n  std::optional<Selector> selector = ExtractSimpleSelector(expr.get());\n  EXPECT_EQ(selector.value(), expected_selector);\n}\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/expressions/evaluator/simple_selector_finder.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_EXPRESSIONS_EVALUATOR_SIMPLE_SELECTOR_FINDER_H_\n#define TACHYON_ZK_PLONK_EXPRESSIONS_EVALUATOR_SIMPLE_SELECTOR_FINDER_H_\n\n#include \"tachyon/zk/expressions/evaluator.h\"\n#include \"tachyon/zk/expressions/negated_expression.h\"\n#include \"tachyon/zk/expressions/product_expression.h\"\n#include \"tachyon/zk/expressions/scaled_expression.h\"\n#include \"tachyon/zk/expressions/sum_expression.h\"\n#include \"tachyon/zk/plonk/expressions/selector_expression.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F>\nclass SimpleSelectorFinder : public Evaluator<F, bool> {\n public:\n  // Evaluator methods\n  bool Evaluate(const Expression<F>* input) override {\n    switch (input->type()) {\n      case ExpressionType::kConstant:\n        return false;\n      case ExpressionType::kSelector:\n        return input->ToSelector()->selector().is_simple();\n      case ExpressionType::kFixed:\n        return false;\n      case ExpressionType::kAdvice:\n        return false;\n      case ExpressionType::kInstance:\n        return false;\n      case ExpressionType::kChallenge:\n        return false;\n      case ExpressionType::kNegated:\n        return Evaluate(input->ToNegated()->expr());\n      case ExpressionType::kSum: {\n        const SumExpression<F>* sum = input->ToSum();\n        return Evaluate(sum->left()) || Evaluate(sum->right());\n      }\n      case ExpressionType::kProduct: {\n        const ProductExpression<F>* product = input->ToProduct();\n        return Evaluate(product->left()) || Evaluate(product->right());\n      }\n      case ExpressionType::kScaled: {\n        const ScaledExpression<F>* scaled = input->ToScaled();\n        return Evaluate(scaled->expr());\n      }\n      case ExpressionType::kFirstRow:\n      case ExpressionType::kLastRow:\n      case ExpressionType::kTransition:\n      case ExpressionType::kVariable:\n        NOTREACHED() << \"AIR expression \"\n                     << ExpressionTypeToString(input->type())\n                     << \" is not allowed in plonk!\";\n    }\n    NOTREACHED();\n    return false;\n  }\n};\n\ntemplate <typename F>\nbool ContainsSimpleSelector(const Expression<F>* input) {\n  SimpleSelectorFinder<F> finder;\n  return finder.Evaluate(input);\n}\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_EXPRESSIONS_EVALUATOR_SIMPLE_SELECTOR_FINDER_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/expressions/evaluator/simple_selector_finder_unittest.cc",
    "content": "#include \"tachyon/zk/plonk/expressions/evaluator/simple_selector_finder.h\"\n\n#include <memory>\n\n#include \"tachyon/zk/plonk/expressions/evaluator/test/evaluator_test.h\"\n#include \"tachyon/zk/plonk/expressions/expression_factory.h\"\n\nnamespace tachyon::zk::plonk {\n\nusing Expr = std::unique_ptr<Expression<GF7>>;\n\nclass SimpleSelectorFinderTest : public EvaluatorTest {};\n\nTEST_F(SimpleSelectorFinderTest, Constant) {\n  GF7 value = GF7::Random();\n  Expr expr = ExpressionFactory<GF7>::Constant(value);\n  EXPECT_FALSE(ContainsSimpleSelector(expr.get()));\n}\n\nTEST_F(SimpleSelectorFinderTest, Selector) {\n  Expr expr = ExpressionFactory<GF7>::Selector(Selector::Simple(1));\n  EXPECT_TRUE(ContainsSimpleSelector(expr.get()));\n  expr = ExpressionFactory<GF7>::Selector(Selector::Complex(1));\n  EXPECT_FALSE(ContainsSimpleSelector(expr.get()));\n}\n\nTEST_F(SimpleSelectorFinderTest, Fixed) {\n  struct {\n    int32_t rotation;\n    size_t column_index;\n  } tests[] = {\n      {1, 0},\n      {2, 1},\n  };\n\n  for (const auto& test : tests) {\n    FixedQuery query(1, Rotation(test.rotation),\n                     FixedColumnKey(test.column_index));\n    Expr expr = ExpressionFactory<GF7>::Fixed(query);\n    EXPECT_FALSE(ContainsSimpleSelector(expr.get()));\n  }\n}\n\nTEST_F(SimpleSelectorFinderTest, Advice) {\n  struct {\n    int32_t rotation;\n    size_t column_index;\n  } tests[] = {\n      {6, 2},\n      {7, 3},\n  };\n\n  for (const auto& test : tests) {\n    AdviceQuery query(1, Rotation(test.rotation),\n                      AdviceColumnKey(test.column_index, Phase(0)));\n    Expr expr = ExpressionFactory<GF7>::Advice(query);\n    EXPECT_FALSE(ContainsSimpleSelector(expr.get()));\n  }\n}\n\nTEST_F(SimpleSelectorFinderTest, Instance) {\n  struct {\n    int32_t rotation;\n    size_t column_index;\n  } tests[] = {\n      {1, 1},\n      {2, 2},\n  };\n\n  for (const auto& test : tests) {\n    InstanceQuery query(1, Rotation(test.rotation),\n                        InstanceColumnKey(test.column_index));\n    Expr expr = ExpressionFactory<GF7>::Instance(query);\n    EXPECT_FALSE(ContainsSimpleSelector(expr.get()));\n  }\n}\n\nTEST_F(SimpleSelectorFinderTest, Challenges) {\n  Expr expr = ExpressionFactory<GF7>::Challenge(Challenge(1, Phase(0)));\n  EXPECT_FALSE(ContainsSimpleSelector(expr.get()));\n}\n\nTEST_F(SimpleSelectorFinderTest, Negated) {\n  GF7 value = GF7::Random();\n  Expr expr =\n      ExpressionFactory<GF7>::Negated(ExpressionFactory<GF7>::Constant(value));\n  EXPECT_FALSE(ContainsSimpleSelector(expr.get()));\n}\n\nTEST_F(SimpleSelectorFinderTest, Sum) {\n  GF7 a = GF7::Random();\n  GF7 b = GF7::Random();\n  Expr expr = ExpressionFactory<GF7>::Sum(ExpressionFactory<GF7>::Constant(a),\n                                          ExpressionFactory<GF7>::Constant(b));\n  EXPECT_FALSE(ContainsSimpleSelector(expr.get()));\n  expr = ExpressionFactory<GF7>::Sum(\n      ExpressionFactory<GF7>::Constant(a),\n      ExpressionFactory<GF7>::Selector(Selector::Simple(1)));\n  EXPECT_TRUE(ContainsSimpleSelector(expr.get()));\n}\n\nTEST_F(SimpleSelectorFinderTest, Product) {\n  GF7 a = GF7::Random();\n  GF7 b = GF7::Random();\n  Expr expr = ExpressionFactory<GF7>::Product(\n      ExpressionFactory<GF7>::Constant(a), ExpressionFactory<GF7>::Constant(b));\n  EXPECT_FALSE(ContainsSimpleSelector(expr.get()));\n  expr = ExpressionFactory<GF7>::Product(\n      ExpressionFactory<GF7>::Constant(a),\n      ExpressionFactory<GF7>::Selector(Selector::Simple(1)));\n  EXPECT_TRUE(ContainsSimpleSelector(expr.get()));\n}\n\nTEST_F(SimpleSelectorFinderTest, Scaled) {\n  GF7 a = GF7::Random();\n  GF7 b = GF7::Random();\n  Expr expr =\n      ExpressionFactory<GF7>::Scaled(ExpressionFactory<GF7>::Constant(a), b);\n  EXPECT_FALSE(ContainsSimpleSelector(expr.get()));\n  expr = ExpressionFactory<GF7>::Scaled(\n      ExpressionFactory<GF7>::Selector(Selector::Simple(1)), GF7(3));\n  EXPECT_TRUE(ContainsSimpleSelector(expr.get()));\n}\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/expressions/evaluator/test/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\ntachyon_cc_library(\n    name = \"evaluator_test\",\n    testonly = True,\n    hdrs = [\"evaluator_test.h\"],\n    visibility = [\n        \"//tachyon/zk/plonk/expressions:__pkg__\",\n        \"//tachyon/zk/plonk/expressions/evaluator:__pkg__\",\n        \"//tachyon/zk/plonk/vanishing:__pkg__\",\n    ],\n    deps = [\n        \"//tachyon/math/finite_fields/test:finite_field_test\",\n        \"//tachyon/math/finite_fields/test:gf7\",\n        \"//tachyon/math/polynomials/univariate:univariate_evaluations\",\n        \"//tachyon/zk/expressions:expression\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/zk/plonk/expressions/evaluator/test/evaluator_test.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_EXPRESSIONS_EVALUATOR_TEST_EVALUATOR_TEST_H_\n#define TACHYON_ZK_PLONK_EXPRESSIONS_EVALUATOR_TEST_EVALUATOR_TEST_H_\n\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluations.h\"\n\nnamespace tachyon::zk::plonk {\n\nconstexpr size_t kMaxDegree = 5;\n\nusing GF7 = math::GF7;\nusing Evals = math::UnivariateEvaluations<GF7, kMaxDegree>;\n\nclass EvaluatorTest : public math::FiniteFieldTest<GF7> {\n protected:\n  std::vector<Evals> fixed_columns_;\n  std::vector<Evals> advice_columns_;\n  std::vector<Evals> instance_columns_;\n  std::vector<GF7> challenges_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_EXPRESSIONS_EVALUATOR_TEST_EVALUATOR_TEST_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/expressions/expression_factory.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_EXPRESSIONS_EXPRESSION_FACTORY_H_\n#define TACHYON_ZK_PLONK_EXPRESSIONS_EXPRESSION_FACTORY_H_\n\n#include <memory>\n#include <utility>\n\n#include \"absl/memory/memory.h\"\n\n#include \"tachyon/zk/expressions/expression_factory.h\"\n#include \"tachyon/zk/plonk/expressions/advice_expression.h\"\n#include \"tachyon/zk/plonk/expressions/challenge_expression.h\"\n#include \"tachyon/zk/plonk/expressions/fixed_expression.h\"\n#include \"tachyon/zk/plonk/expressions/instance_expression.h\"\n#include \"tachyon/zk/plonk/expressions/selector_expression.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F>\nclass ExpressionFactory : public tachyon::zk::ExpressionFactory<F> {\n public:\n  using Expr = Expression<F>;\n\n  static std::unique_ptr<Expr> Advice(const AdviceQuery& query) {\n    return absl::WrapUnique(new AdviceExpression<F>(query));\n  }\n\n  static std::unique_ptr<Expr> Instance(const InstanceQuery& query) {\n    return absl::WrapUnique(new InstanceExpression<F>(query));\n  }\n\n  static std::unique_ptr<Expr> Challenge(Challenge challenge) {\n    return absl::WrapUnique(new ChallengeExpression<F>(challenge));\n  }\n\n  static std::unique_ptr<Expr> Fixed(const FixedQuery& query) {\n    return absl::WrapUnique(new FixedExpression<F>(query));\n  }\n\n  static std::unique_ptr<Expr> Selector(Selector selector) {\n    return absl::WrapUnique(new SelectorExpression<F>(selector));\n  }\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_EXPRESSIONS_EXPRESSION_FACTORY_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/expressions/expression_unittest.cc",
    "content": "#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/random.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n#include \"tachyon/zk/plonk/expressions/evaluator/simple_selector_finder.h\"\n#include \"tachyon/zk/plonk/expressions/expression_factory.h\"\n\nnamespace tachyon::zk::plonk {\n\nusing F = math::GF7;\n\nnamespace {\n\nclass ExpressionTest : public math::FiniteFieldTest<F> {\n public:\n  void SetUp() override {\n    expressions_.push_back(ExpressionFactory<F>::Constant(F(1)));\n    expressions_.push_back(ExpressionFactory<F>::Selector(Selector::Simple(1)));\n    expressions_.push_back(\n        ExpressionFactory<F>::Selector(Selector::Complex(2)));\n    expressions_.push_back(ExpressionFactory<F>::Fixed(\n        FixedQuery(1, Rotation(1), FixedColumnKey(1))));\n    expressions_.push_back(ExpressionFactory<F>::Advice(\n        AdviceQuery(1, Rotation(1), AdviceColumnKey(1, Phase(0)))));\n    expressions_.push_back(ExpressionFactory<F>::Instance(\n        InstanceQuery(1, Rotation(1), InstanceColumnKey(1))));\n    expressions_.push_back(\n        ExpressionFactory<F>::Challenge(Challenge(1, Phase(0))));\n    expressions_.push_back(\n        ExpressionFactory<F>::Negated(ExpressionFactory<F>::Constant(F(1))));\n    expressions_.push_back(\n        ExpressionFactory<F>::Sum(ExpressionFactory<F>::Constant(F(1)),\n                                  ExpressionFactory<F>::Constant(F(2))));\n    expressions_.push_back(\n        ExpressionFactory<F>::Product(ExpressionFactory<F>::Constant(F(3)),\n                                      ExpressionFactory<F>::Constant(F(4))));\n    expressions_.push_back(ExpressionFactory<F>::Scaled(\n        ExpressionFactory<F>::Constant(F(5)), F(6)));\n  }\n\n protected:\n  std::vector<std::unique_ptr<Expression<F>>> expressions_;\n};\n\n}  // namespace\n\nTEST_F(ExpressionTest, ArithmeticOperatorWithClone) {\n  std::unique_ptr<Expression<F>> left =\n      base::UniformElement(expressions_)->Clone();\n  std::unique_ptr<Expression<F>> right =\n      base::UniformElement(expressions_)->Clone();\n\n  if (!ContainsSimpleSelector(left.get()) &&\n      !ContainsSimpleSelector(right.get())) {\n    std::unique_ptr<Expression<F>> add = left + right;\n    EXPECT_EQ(*add->ToSum()->left(), *left);\n    EXPECT_EQ(*add->ToSum()->right(), *right);\n    EXPECT_TRUE(left);\n    EXPECT_TRUE(right);\n\n    std::unique_ptr<Expression<F>> sub = left - right;\n    EXPECT_EQ(*sub->ToSum()->left(), *left);\n    EXPECT_EQ(*sub->ToSum()->right()->ToNegated()->expr(), *right);\n    EXPECT_TRUE(left);\n    EXPECT_TRUE(right);\n\n    std::unique_ptr<Expression<F>> mul = left * right;\n    EXPECT_EQ(*mul->ToProduct()->left(), *left);\n    EXPECT_EQ(*mul->ToProduct()->right(), *right);\n    EXPECT_TRUE(left);\n    EXPECT_TRUE(right);\n  }\n\n  F scale = F::Random();\n  std::unique_ptr<Expression<F>> scaled = left * scale;\n  EXPECT_EQ(*scaled->ToScaled()->expr(), *left);\n  EXPECT_EQ(scaled->ToScaled()->scale(), scale);\n  EXPECT_TRUE(left);\n\n  std::unique_ptr<Expression<F>> neg = -left;\n  EXPECT_EQ(*neg->ToNegated()->expr(), *left);\n  EXPECT_TRUE(left);\n}\n\nTEST_F(ExpressionTest, ArithmeticOperatorWithMove) {\n  std::unique_ptr<Expression<F>> left =\n      base::UniformElement(expressions_)->Clone();\n  std::unique_ptr<Expression<F>> right =\n      base::UniformElement(expressions_)->Clone();\n\n  if (!ContainsSimpleSelector(left.get()) &&\n      !ContainsSimpleSelector(right.get())) {\n    {\n      std::unique_ptr<Expression<F>> left_tmp = left->Clone();\n      std::unique_ptr<Expression<F>> right_tmp = right->Clone();\n      std::unique_ptr<Expression<F>> add =\n          std::move(left_tmp) + std::move(right_tmp);\n      EXPECT_EQ(*add->ToSum()->left(), *left);\n      EXPECT_EQ(*add->ToSum()->right(), *right);\n      EXPECT_FALSE(left_tmp);\n      EXPECT_FALSE(right_tmp);\n    }\n\n    {\n      std::unique_ptr<Expression<F>> left_tmp = left->Clone();\n      std::unique_ptr<Expression<F>> right_tmp = right->Clone();\n      std::unique_ptr<Expression<F>> sub =\n          std::move(left_tmp) - std::move(right_tmp);\n      EXPECT_EQ(*sub->ToSum()->left(), *left);\n      EXPECT_EQ(*sub->ToSum()->right()->ToNegated()->expr(), *right);\n      EXPECT_FALSE(left_tmp);\n      EXPECT_FALSE(right_tmp);\n    }\n\n    {\n      std::unique_ptr<Expression<F>> left_tmp = left->Clone();\n      std::unique_ptr<Expression<F>> right_tmp = right->Clone();\n      std::unique_ptr<Expression<F>> mul =\n          std::move(left_tmp) * std::move(right_tmp);\n      EXPECT_EQ(*mul->ToProduct()->left(), *left);\n      EXPECT_EQ(*mul->ToProduct()->right(), *right);\n      EXPECT_FALSE(left_tmp);\n      EXPECT_FALSE(right_tmp);\n    }\n  }\n\n  {\n    std::unique_ptr<Expression<F>> left_tmp = left->Clone();\n    F scale = F::Random();\n    std::unique_ptr<Expression<F>> scaled = std::move(left_tmp) * scale;\n    EXPECT_EQ(*scaled->ToScaled()->expr(), *left);\n    EXPECT_EQ(scaled->ToScaled()->scale(), scale);\n    EXPECT_FALSE(left_tmp);\n  }\n\n  {\n    std::unique_ptr<Expression<F>> left_tmp = left->Clone();\n    std::unique_ptr<Expression<F>> neg = -std::move(left_tmp);\n    EXPECT_EQ(*neg->ToNegated()->expr(), *left);\n    EXPECT_FALSE(left_tmp);\n  }\n}\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/expressions/fixed_expression.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_EXPRESSIONS_FIXED_EXPRESSION_H_\n#define TACHYON_ZK_PLONK_EXPRESSIONS_FIXED_EXPRESSION_H_\n\n#include <memory>\n#include <string>\n\n#include \"absl/memory/memory.h\"\n\n#include \"tachyon/zk/expressions/expression.h\"\n#include \"tachyon/zk/plonk/constraint_system/query.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F>\nclass ExpressionFactory;\n\ntemplate <typename F>\nclass FixedExpression : public Expression<F> {\n public:\n  static std::unique_ptr<FixedExpression> CreateForTesting(\n      const FixedQuery& query) {\n    return absl::WrapUnique(new FixedExpression(query));\n  }\n\n  const FixedQuery& query() const { return query_; }\n\n  // Expression methods\n  size_t Degree() const override { return 1; }\n\n  uint64_t Complexity() const override { return 1; }\n\n  std::unique_ptr<Expression<F>> Clone() const override {\n    return absl::WrapUnique(new FixedExpression(query_));\n  }\n\n  std::string ToString() const override {\n    return absl::Substitute(\"{type: $0, column: $1}\",\n                            ExpressionTypeToString(this->type_),\n                            query_.ToString());\n  }\n\n  bool operator==(const Expression<F>& other) const override {\n    if (!Expression<F>::operator==(other)) return false;\n    const FixedExpression* fixed = other.ToFixed();\n    return query_ == fixed->query_;\n  }\n\n private:\n  friend class ExpressionFactory<F>;\n\n  explicit FixedExpression(const FixedQuery& query)\n      : Expression<F>(ExpressionType::kFixed), query_(query) {}\n\n  FixedQuery query_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_EXPRESSIONS_FIXED_EXPRESSION_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/expressions/fixed_expression_unittest.cc",
    "content": "#include \"tachyon/zk/plonk/expressions/fixed_expression.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n\nnamespace tachyon::zk::plonk {\n\nusing F = math::GF7;\n\nclass FixedExpressionTest : public math::FiniteFieldTest<F> {};\n\nTEST_F(FixedExpressionTest, DegreeComplexity) {\n  std::unique_ptr<FixedExpression<F>> expr =\n      FixedExpression<F>::CreateForTesting(\n          FixedQuery(1, Rotation(1), FixedColumnKey(1)));\n  EXPECT_EQ(expr->Degree(), size_t{1});\n  EXPECT_EQ(expr->Complexity(), uint64_t{1});\n}\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/expressions/instance_expression.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_EXPRESSIONS_INSTANCE_EXPRESSION_H_\n#define TACHYON_ZK_PLONK_EXPRESSIONS_INSTANCE_EXPRESSION_H_\n\n#include <memory>\n#include <string>\n\n#include \"absl/memory/memory.h\"\n\n#include \"tachyon/zk/expressions/expression.h\"\n#include \"tachyon/zk/plonk/constraint_system/query.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F>\nclass ExpressionFactory;\n\ntemplate <typename F>\nclass InstanceExpression : public Expression<F> {\n public:\n  static std::unique_ptr<InstanceExpression> CreateForTesting(\n      const InstanceQuery& query) {\n    return absl::WrapUnique(new InstanceExpression(query));\n  }\n\n  const InstanceQuery& query() const { return query_; }\n\n  // Expression methods\n  size_t Degree() const override { return 1; }\n\n  uint64_t Complexity() const override { return 1; }\n\n  std::unique_ptr<Expression<F>> Clone() const override {\n    return absl::WrapUnique(new InstanceExpression(query_));\n  }\n\n  std::string ToString() const override {\n    return absl::Substitute(\"{type: $0, column: $1}\",\n                            ExpressionTypeToString(this->type_),\n                            query_.ToString());\n  }\n\n  bool operator==(const Expression<F>& other) const override {\n    if (!Expression<F>::operator==(other)) return false;\n    const InstanceExpression* instance = other.ToInstance();\n    return query_ == instance->query_;\n  }\n\n private:\n  friend class ExpressionFactory<F>;\n\n  explicit InstanceExpression(const InstanceQuery& query)\n      : Expression<F>(ExpressionType::kInstance), query_(query) {}\n\n  InstanceQuery query_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_EXPRESSIONS_INSTANCE_EXPRESSION_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/expressions/instance_expression_unittest.cc",
    "content": "#include \"tachyon/zk/plonk/expressions/instance_expression.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n\nnamespace tachyon::zk::plonk {\n\nusing F = math::GF7;\n\nclass InstanceExpressionTest : public math::FiniteFieldTest<F> {};\n\nTEST_F(InstanceExpressionTest, DegreeComplexity) {\n  std::unique_ptr<InstanceExpression<F>> expr =\n      InstanceExpression<F>::CreateForTesting(\n          InstanceQuery(1, Rotation(1), InstanceColumnKey(1)));\n  EXPECT_EQ(expr->Degree(), size_t{1});\n  EXPECT_EQ(expr->Complexity(), uint64_t{1});\n}\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/expressions/proving_evaluator.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_EXPRESSIONS_PROVING_EVALUATOR_H_\n#define TACHYON_ZK_PLONK_EXPRESSIONS_PROVING_EVALUATOR_H_\n\n#include <vector>\n\n#include \"absl/types/span.h\"\n\n#include \"tachyon/zk/expressions/constant_expression.h\"\n#include \"tachyon/zk/expressions/evaluator.h\"\n#include \"tachyon/zk/expressions/negated_expression.h\"\n#include \"tachyon/zk/expressions/product_expression.h\"\n#include \"tachyon/zk/expressions/scaled_expression.h\"\n#include \"tachyon/zk/expressions/sum_expression.h\"\n#include \"tachyon/zk/plonk/base/multi_phase_ref_table.h\"\n#include \"tachyon/zk/plonk/expressions/advice_expression.h\"\n#include \"tachyon/zk/plonk/expressions/challenge_expression.h\"\n#include \"tachyon/zk/plonk/expressions/fixed_expression.h\"\n#include \"tachyon/zk/plonk/expressions/instance_expression.h\"\n#include \"tachyon/zk/plonk/expressions/selector_expression.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename Evals>\nclass ProvingEvaluator\n    : public Evaluator<typename Evals::Field, typename Evals::Field> {\n public:\n  using Field = typename Evals::Field;\n\n  ProvingEvaluator() = default;\n  ProvingEvaluator(int32_t idx, int32_t size, int32_t rot_scale,\n                   const MultiPhaseRefTable<Evals>& table)\n      : idx_(idx), size_(size), rot_scale_(rot_scale), table_(table) {}\n\n  int32_t idx() const { return idx_; }\n  void set_idx(int32_t idx) { idx_ = idx; }\n  int32_t size() const { return size_; }\n  int32_t rot_scale() const { return rot_scale_; }\n\n  // Evaluator methods\n  Field Evaluate(const Expression<Field>* input) override {\n    switch (input->type()) {\n      case ExpressionType::kConstant:\n        return input->ToConstant()->value();\n      case ExpressionType::kSelector:\n        NOTREACHED() << \"virtual selectors are removed during optimization\";\n        break;\n      case ExpressionType::kFixed: {\n        const FixedExpression<Field>* fixed_expr = input->ToFixed();\n        const FixedQuery& query = fixed_expr->query();\n        const Evals& evals = table_.GetFixedColumns()[query.column().index()];\n        return evals[query.rotation().GetIndex(idx_, rot_scale_, size_)];\n      }\n      case ExpressionType::kAdvice: {\n        const AdviceExpression<Field>* advice_expr = input->ToAdvice();\n        const AdviceQuery& query = advice_expr->query();\n        const Evals& evals = table_.GetAdviceColumns()[query.column().index()];\n        return evals[query.rotation().GetIndex(idx_, rot_scale_, size_)];\n      }\n      case ExpressionType::kInstance: {\n        const InstanceExpression<Field>* instance_expr = input->ToInstance();\n        const InstanceQuery& query = instance_expr->query();\n        const Evals& evals =\n            table_.GetInstanceColumns()[query.column().index()];\n        return evals[query.rotation().GetIndex(idx_, rot_scale_, size_)];\n      }\n      case ExpressionType::kChallenge:\n        return table_.challenges()[input->ToChallenge()->challenge().index()];\n      case ExpressionType::kNegated:\n        return -Evaluate(input->ToNegated()->expr());\n      case ExpressionType::kSum: {\n        const SumExpression<Field>* sum = input->ToSum();\n        return Evaluate(sum->left()) + Evaluate(sum->right());\n      }\n      case ExpressionType::kProduct: {\n        const ProductExpression<Field>* product = input->ToProduct();\n        return Evaluate(product->left()) * Evaluate(product->right());\n      }\n      case ExpressionType::kScaled: {\n        const ScaledExpression<Field>* scaled = input->ToScaled();\n        return Evaluate(scaled->expr()) * scaled->scale();\n      }\n      case ExpressionType::kFirstRow:\n      case ExpressionType::kLastRow:\n      case ExpressionType::kTransition:\n      case ExpressionType::kVariable:\n        NOTREACHED() << \"AIR expression \"\n                     << ExpressionTypeToString(input->type())\n                     << \" is not allowed in plonk!\";\n    }\n    NOTREACHED();\n    return Field::Zero();\n  }\n\n private:\n  int32_t idx_ = 0;\n  int32_t size_ = 0;\n  int32_t rot_scale_ = 0;\n  MultiPhaseRefTable<Evals> table_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_EXPRESSIONS_PROVING_EVALUATOR_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/expressions/proving_evaluator_unittest.cc",
    "content": "#include \"tachyon/zk/plonk/expressions/proving_evaluator.h\"\n\n#include <memory>\n\n#include \"tachyon/zk/plonk/expressions/evaluator/test/evaluator_test.h\"\n#include \"tachyon/zk/plonk/expressions/expression_factory.h\"\n\nnamespace tachyon::zk::plonk {\n\nnamespace {\n\nconstexpr size_t kMaxDegree = 5;\n\nusing GF7 = math::GF7;\nusing Evals = math::UnivariateEvaluations<GF7, kMaxDegree>;\nusing Expr = std::unique_ptr<Expression<GF7>>;\n\nclass ProvingEvaluatorTest : public EvaluatorTest {\n public:\n  void SetUp() override {\n    std::vector<GF7> evaluations;\n\n    for (size_t i = 0; i < 5; ++i) {\n      evaluations =\n          base::CreateVector(kMaxDegree + 1, []() { return GF7::Random(); });\n      fixed_columns_.push_back(Evals(evaluations));\n      advice_columns_.push_back(Evals(evaluations));\n      instance_columns_.push_back(Evals(evaluations));\n\n      challenges_.push_back(GF7::Random());\n    }\n\n    MultiPhaseRefTable<Evals> table(fixed_columns_, advice_columns_,\n                                    instance_columns_, challenges_);\n    proving_evaluator_ =\n        std::make_unique<ProvingEvaluator<Evals>>(3, 4, 1, table);\n  }\n\n protected:\n  std::unique_ptr<ProvingEvaluator<Evals>> proving_evaluator_;\n};\n\n}  // namespace\n\nTEST_F(ProvingEvaluatorTest, Constant) {\n  GF7 value = GF7::Random();\n  Expr expr = ExpressionFactory<GF7>::Constant(value);\n  GF7 evaluated = proving_evaluator_->Evaluate(expr.get());\n  EXPECT_EQ(value, evaluated);\n}\n\nTEST_F(ProvingEvaluatorTest, Selector) {\n  Expr expr = ExpressionFactory<GF7>::Selector(Selector::Simple(1));\n  EXPECT_DEATH(proving_evaluator_->Evaluate(expr.get()), \"\");\n}\n\nTEST_F(ProvingEvaluatorTest, Fixed) {\n  struct {\n    int32_t rotation;\n    size_t column_index;\n  } tests[] = {\n      // fixed_columns_[0], (3 + 1 * 1) % 4 = 0, evaluations[0]\n      {1, 0},\n      // fixed_columns_[1], (4 + 2 * 1) % 4 = 2, evaluations[2]\n      {2, 1},\n  };\n\n  for (const auto& test : tests) {\n    int32_t idx = proving_evaluator_->idx();\n    int32_t rot_scale = proving_evaluator_->rot_scale();\n    int32_t size = proving_evaluator_->size();\n    FixedQuery query(1, Rotation(test.rotation),\n                     FixedColumnKey(test.column_index));\n    RowIndex row_index = query.rotation().GetIndex(idx, rot_scale, size);\n\n    const GF7& expected = fixed_columns_[test.column_index][row_index];\n    Expr expr = ExpressionFactory<GF7>::Fixed(query);\n    GF7 evaluated = proving_evaluator_->Evaluate(expr.get());\n    EXPECT_EQ(evaluated, expected);\n  }\n}\n\nTEST_F(ProvingEvaluatorTest, Advice) {\n  struct {\n    int32_t rotation;\n    size_t column_index;\n  } tests[] = {\n      // advice_columns_[2], (3 + 6 * 1) % 4 = 1, evaluations[1]\n      {6, 2},\n      // advice_columns_[3], (4 + 7 * 1) % 4 = 3, evaluations[3]\n      {7, 3},\n  };\n\n  for (const auto& test : tests) {\n    int32_t idx = proving_evaluator_->idx();\n    int32_t rot_scale = proving_evaluator_->rot_scale();\n    int32_t size = proving_evaluator_->size();\n    AdviceQuery query(1, Rotation(test.rotation),\n                      AdviceColumnKey(test.column_index, Phase(0)));\n    RowIndex row_index = query.rotation().GetIndex(idx, rot_scale, size);\n\n    const GF7& expected = advice_columns_[test.column_index][row_index];\n    Expr expr = ExpressionFactory<GF7>::Advice(query);\n    GF7 evaluated = proving_evaluator_->Evaluate(expr.get());\n    EXPECT_EQ(evaluated, expected);\n  }\n}\n\nTEST_F(ProvingEvaluatorTest, Instance) {\n  struct {\n    int32_t rotation;\n    size_t column_index;\n  } tests[] = {\n      // instance_columns_[1], (3 + 1 * 1) % 4 = 0, evaluations[0]\n      {1, 1},\n      // instance_columns_[2], (4 + 2 * 1) % 4 = 2, evaluations[2]\n      {2, 2},\n  };\n\n  for (const auto& test : tests) {\n    int32_t idx = proving_evaluator_->idx();\n    int32_t rot_scale = proving_evaluator_->rot_scale();\n    int32_t size = proving_evaluator_->size();\n    InstanceQuery query(1, Rotation(test.rotation),\n                        InstanceColumnKey(test.column_index));\n    RowIndex row_index = query.rotation().GetIndex(idx, rot_scale, size);\n\n    const GF7& expected = instance_columns_[test.column_index][row_index];\n    Expr expr = ExpressionFactory<GF7>::Instance(query);\n    GF7 evaluated = proving_evaluator_->Evaluate(expr.get());\n    EXPECT_EQ(evaluated, expected);\n  }\n}\n\nTEST_F(ProvingEvaluatorTest, Challenges) {\n  for (size_t i = 0; i < challenges_.size(); ++i) {\n    Expr expr = ExpressionFactory<GF7>::Challenge(Challenge(i, Phase(0)));\n    GF7 evaluated = proving_evaluator_->Evaluate(expr.get());\n    GF7 expected = challenges_[i];\n    EXPECT_EQ(evaluated, expected);\n  }\n}\n\nTEST_F(ProvingEvaluatorTest, Negated) {\n  GF7 value = GF7::Random();\n  Expr expr =\n      ExpressionFactory<GF7>::Negated(ExpressionFactory<GF7>::Constant(value));\n  GF7 negated = proving_evaluator_->Evaluate(expr.get());\n  EXPECT_EQ(negated, -value);\n}\n\nTEST_F(ProvingEvaluatorTest, Sum) {\n  GF7 a = GF7::Random();\n  GF7 b = GF7::Random();\n  Expr expr = ExpressionFactory<GF7>::Sum(ExpressionFactory<GF7>::Constant(a),\n                                          ExpressionFactory<GF7>::Constant(b));\n  GF7 evaluated = proving_evaluator_->Evaluate(expr.get());\n  EXPECT_EQ(a + b, evaluated);\n}\n\nTEST_F(ProvingEvaluatorTest, Product) {\n  GF7 a = GF7::Random();\n  GF7 b = GF7::Random();\n  Expr expr = ExpressionFactory<GF7>::Product(\n      ExpressionFactory<GF7>::Constant(a), ExpressionFactory<GF7>::Constant(b));\n  GF7 evaluated = proving_evaluator_->Evaluate(expr.get());\n  EXPECT_EQ(a * b, evaluated);\n}\n\nTEST_F(ProvingEvaluatorTest, Scaled) {\n  GF7 a = GF7::Random();\n  GF7 b = GF7::Random();\n  Expr expr =\n      ExpressionFactory<GF7>::Scaled(ExpressionFactory<GF7>::Constant(a), b);\n  GF7 scaled_expr = proving_evaluator_->Evaluate(expr.get());\n  EXPECT_EQ(scaled_expr, a * b);\n}\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/expressions/selector_expression.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_EXPRESSIONS_SELECTOR_EXPRESSION_H_\n#define TACHYON_ZK_PLONK_EXPRESSIONS_SELECTOR_EXPRESSION_H_\n\n#include <memory>\n#include <string>\n\n#include \"absl/memory/memory.h\"\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/zk/expressions/expression.h\"\n#include \"tachyon/zk/plonk/constraint_system/selector.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F>\nclass ExpressionFactory;\n\ntemplate <typename F>\nclass SelectorExpression : public Expression<F> {\n public:\n  static std::unique_ptr<SelectorExpression> CreateForTesting(\n      Selector selector) {\n    return absl::WrapUnique(new SelectorExpression(selector));\n  }\n\n  Selector selector() const { return selector_; }\n\n  // Expression methods\n  size_t Degree() const override { return 1; }\n\n  uint64_t Complexity() const override { return 1; }\n\n  std::unique_ptr<Expression<F>> Clone() const override {\n    return absl::WrapUnique(new SelectorExpression(selector_));\n  }\n\n  std::string ToString() const override {\n    return absl::Substitute(\"{type: $0, selector: $1}\",\n                            ExpressionTypeToString(this->type_),\n                            selector_.ToString());\n  }\n\n  bool operator==(const Expression<F>& other) const override {\n    if (!Expression<F>::operator==(other)) return false;\n    const SelectorExpression* selector = other.ToSelector();\n    return selector_ == selector->selector_;\n  }\n\n private:\n  friend class ExpressionFactory<F>;\n\n  explicit SelectorExpression(Selector selector)\n      : Expression<F>(ExpressionType::kSelector), selector_(selector) {}\n\n  Selector selector_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_EXPRESSIONS_SELECTOR_EXPRESSION_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/expressions/selector_expression_unittest.cc",
    "content": "#include \"tachyon/zk/plonk/expressions/selector_expression.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n\nnamespace tachyon::zk::plonk {\n\nusing F = math::GF7;\n\nclass SelectorExpressionTest : public math::FiniteFieldTest<F> {};\n\nTEST_F(SelectorExpressionTest, Degree_Complexity) {\n  std::unique_ptr<SelectorExpression<F>> expr =\n      SelectorExpression<F>::CreateForTesting(Selector::Simple(1));\n  EXPECT_EQ(expr->Degree(), size_t{1});\n  EXPECT_EQ(expr->Complexity(), uint64_t{1});\n}\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/expressions/test/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\ntachyon_cc_library(\n    name = \"compress_expression_test\",\n    testonly = True,\n    hdrs = [\"compress_expression_test.h\"],\n    visibility = [\"//tachyon/zk/plonk/expressions:__pkg__\"],\n    deps = [\n        \"//tachyon/zk/plonk/expressions:proving_evaluator\",\n        \"//tachyon/zk/plonk/halo2:bn254_shplonk_prover_test\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/zk/plonk/expressions/test/compress_expression_test.h",
    "content": "#ifndef TACHYON_ZK_PLONK_EXPRESSIONS_TEST_COMPRESS_EXPRESSION_TEST_H_\n#define TACHYON_ZK_PLONK_EXPRESSIONS_TEST_COMPRESS_EXPRESSION_TEST_H_\n\n#include <memory>\n#include <vector>\n\n#include \"tachyon/zk/plonk/expressions/proving_evaluator.h\"\n#include \"tachyon/zk/plonk/halo2/bn254_shplonk_prover_test.h\"\n\nnamespace tachyon::zk::plonk {\n\nclass CompressExpressionTest : public halo2::BN254SHPlonkProverTest {\n public:\n  void SetUp() override {\n    halo2::BN254SHPlonkProverTest::SetUp();\n\n    MultiPhaseRefTable<Evals> table(fixed_columns_, advice_columns_,\n                                    instance_columns_, challenges_);\n    evaluator_ = {0, static_cast<int32_t>(prover_->domain()->size()), 1, table};\n    theta_ = F(2);\n  }\n\n protected:\n  ProvingEvaluator<Evals> evaluator_;\n  std::vector<Evals> fixed_columns_;\n  std::vector<Evals> advice_columns_;\n  std::vector<Evals> instance_columns_;\n  std::vector<F> challenges_;\n  F theta_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_EXPRESSIONS_TEST_COMPRESS_EXPRESSION_TEST_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/expressions/verifying_evaluator.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_EXPRESSIONS_VERIFYING_EVALUATOR_H_\n#define TACHYON_ZK_PLONK_EXPRESSIONS_VERIFYING_EVALUATOR_H_\n\n#include \"tachyon/zk/expressions/constant_expression.h\"\n#include \"tachyon/zk/expressions/evaluator.h\"\n#include \"tachyon/zk/expressions/negated_expression.h\"\n#include \"tachyon/zk/expressions/product_expression.h\"\n#include \"tachyon/zk/expressions/scaled_expression.h\"\n#include \"tachyon/zk/expressions/sum_expression.h\"\n#include \"tachyon/zk/plonk/base/multi_phase_evaluations.h\"\n#include \"tachyon/zk/plonk/expressions/advice_expression.h\"\n#include \"tachyon/zk/plonk/expressions/challenge_expression.h\"\n#include \"tachyon/zk/plonk/expressions/fixed_expression.h\"\n#include \"tachyon/zk/plonk/expressions/instance_expression.h\"\n#include \"tachyon/zk/plonk/expressions/selector_expression.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F>\nclass VerifyingEvaluator : public Evaluator<F, F> {\n public:\n  explicit VerifyingEvaluator(const MultiPhaseEvaluations<F>& data)\n      : data_(data) {}\n\n  // Evaluator methods\n  F Evaluate(const Expression<F>* input) override {\n    switch (input->type()) {\n      case ExpressionType::kConstant:\n        return input->ToConstant()->value();\n      case ExpressionType::kSelector:\n        NOTREACHED() << \"virtual selectors are removed during optimization\";\n        break;\n      case ExpressionType::kFixed: {\n        const FixedExpression<F>* fixed_expr = input->ToFixed();\n        const FixedQuery& query = fixed_expr->query();\n        return data_.fixed_evals[query.GetIndex()];\n      }\n      case ExpressionType::kAdvice: {\n        const AdviceExpression<F>* advice_expr = input->ToAdvice();\n        const AdviceQuery& query = advice_expr->query();\n        return data_.advice_evals[query.GetIndex()];\n      }\n      case ExpressionType::kInstance: {\n        const InstanceExpression<F>* instance_expr = input->ToInstance();\n        const InstanceQuery& query = instance_expr->query();\n        return data_.instance_evals[query.GetIndex()];\n      }\n      case ExpressionType::kChallenge: {\n        const ChallengeExpression<F>* challenge_expr = input->ToChallenge();\n        Challenge challenge = challenge_expr->challenge();\n        return data_.challenges[challenge.index()];\n      }\n      case ExpressionType::kNegated: {\n        const NegatedExpression<F>* negated_expr = input->ToNegated();\n        return -Evaluate(negated_expr->expr());\n      }\n      case ExpressionType::kSum: {\n        const SumExpression<F>* sum_expr = input->ToSum();\n        return Evaluate(sum_expr->left()) + Evaluate(sum_expr->right());\n      }\n      case ExpressionType::kProduct: {\n        const ProductExpression<F>* product_expr = input->ToProduct();\n        return Evaluate(product_expr->left()) * Evaluate(product_expr->right());\n      }\n      case ExpressionType::kScaled: {\n        const ScaledExpression<F>* scaled_expr = input->ToScaled();\n        return Evaluate(scaled_expr->expr()) * scaled_expr->scale();\n      }\n      case ExpressionType::kFirstRow:\n      case ExpressionType::kLastRow:\n      case ExpressionType::kTransition:\n      case ExpressionType::kVariable:\n        NOTREACHED() << \"AIR expression \"\n                     << ExpressionTypeToString(input->type())\n                     << \" is not allowed in plonk!\";\n    }\n    NOTREACHED();\n    return F::Zero();\n  }\n\n private:\n  MultiPhaseEvaluations<F> data_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_EXPRESSIONS_VERIFYING_EVALUATOR_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"argument_data\",\n    hdrs = [\"argument_data.h\"],\n    deps = [\n        \":synthesizer\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base:parallelize\",\n        \"//tachyon/base/buffer:copyable\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/math/base:parallelize_threshold\",\n        \"//tachyon/zk/plonk/base:multi_phase_ref_table\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"blake2b_transcript\",\n    hdrs = [\"blake2b_transcript.h\"],\n    deps = [\n        \":constants\",\n        \":prime_field_conversion\",\n        \":proof_serializer\",\n        \"//tachyon/crypto/transcripts:transcript\",\n        \"@com_google_absl//absl/types:span\",\n        \"@com_google_boringssl//:crypto\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"bn254_gwc_prover_test\",\n    testonly = True,\n    hdrs = [\"bn254_gwc_prover_test.h\"],\n    deps = [\n        \":prover_test\",\n        \":proving_scheme\",\n        \"//tachyon/math/elliptic_curves/bn/bn254\",\n        \"//tachyon/math/elliptic_curves/bn/bn254/halo2:bn254\",\n        \"//tachyon/zk/base/commitments:gwc_extension\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"bn254_shplonk_prover_test\",\n    testonly = True,\n    hdrs = [\"bn254_shplonk_prover_test.h\"],\n    deps = [\n        \":prover_test\",\n        \":proving_scheme\",\n        \"//tachyon/math/elliptic_curves/bn/bn254\",\n        \"//tachyon/math/elliptic_curves/bn/bn254/halo2:bn254\",\n        \"//tachyon/zk/base/commitments:shplonk_extension\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"c_prover_impl_base_forward\",\n    hdrs = [\"c_prover_impl_base_forward.h\"],\n)\n\ntachyon_cc_library(\n    name = \"config\",\n    srcs = [\"config.cc\"],\n    hdrs = [\"config.h\"],\n    deps = [\n        \":vendor\",\n        \"//tachyon:export\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"constants\",\n    hdrs = [\"constants.h\"],\n)\n\ntachyon_cc_library(\n    name = \"pcs_type\",\n    hdrs = [\"pcs_type.h\"],\n    deps = [\n        \"//tachyon/base/flag\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"pinned_constraint_system\",\n    hdrs = [\"pinned_constraint_system.h\"],\n    deps = [\n        \":pinned_gates\",\n        \"//tachyon/zk/plonk/constraint_system\",\n        \"//tachyon/zk/plonk/halo2/stringifiers:lookup_argument_stringifier\",\n        \"//tachyon/zk/plonk/halo2/stringifiers:lookup_tracker_stringifier\",\n        \"//tachyon/zk/plonk/halo2/stringifiers:permutation_argument_stringifier\",\n        \"//tachyon/zk/plonk/halo2/stringifiers:phase_stringifier\",\n        \"//tachyon/zk/plonk/halo2/stringifiers:query_stringifier\",\n        \"//tachyon/zk/plonk/halo2/stringifiers:shuffle_argument_stringifier\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"pinned_evaluation_domain\",\n    hdrs = [\"pinned_evaluation_domain.h\"],\n    deps = [\n        \"//tachyon/base/strings:rust_stringifier\",\n        \"//tachyon/zk/base/entities:entity\",\n        \"//tachyon/zk/plonk/halo2/stringifiers:field_stringifier\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"pinned_gates\",\n    hdrs = [\"pinned_gates.h\"],\n    deps = [\"//tachyon/base/strings:rust_stringifier\"],\n)\n\ntachyon_cc_library(\n    name = \"pinned_verifying_key\",\n    hdrs = [\"pinned_verifying_key.h\"],\n    deps = [\n        \":pinned_constraint_system\",\n        \":pinned_evaluation_domain\",\n        \":pinned_verifying_key_forward\",\n        \"//tachyon/zk/plonk/halo2/stringifiers:permutation_verifying_key_stringifier\",\n        \"//tachyon/zk/plonk/keys:verifying_key\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"pinned_verifying_key_forward\",\n    hdrs = [\"pinned_verifying_key_forward.h\"],\n)\n\ntachyon_cc_library(\n    name = \"poseidon_transcript\",\n    hdrs = [\"poseidon_transcript.h\"],\n    deps = [\n        \":prime_field_conversion\",\n        \":proof_serializer\",\n        \"//tachyon/crypto/hashes/sponge/poseidon\",\n        \"//tachyon/crypto/hashes/sponge/poseidon:poseidon_params\",\n        \"//tachyon/crypto/transcripts:transcript\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"prime_field_conversion\",\n    hdrs = [\"prime_field_conversion.h\"],\n    deps = [\n        \"//tachyon/base/buffer:read_only_buffer\",\n        \"//tachyon/base/types:always_false\",\n        \"//tachyon/math/base:big_int\",\n        \"@com_google_absl//absl/numeric:int128\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"proof\",\n    hdrs = [\"proof.h\"],\n    deps = [\n        \"//tachyon/zk/lookup:pair\",\n        \"//tachyon/zk/lookup:type\",\n        \"//tachyon/zk/lookup/halo2:verifier_data\",\n        \"//tachyon/zk/lookup/log_derivative_halo2:verifier_data\",\n        \"//tachyon/zk/plonk/permutation:permutation_verifier_data\",\n        \"//tachyon/zk/plonk/vanishing:vanishing_verifier_data\",\n        \"//tachyon/zk/shuffle:verifier_data\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"proof_reader\",\n    hdrs = [\"proof_reader.h\"],\n    deps = [\n        \":proof\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/crypto/transcripts:transcript\",\n        \"//tachyon/zk/plonk/keys:verifying_key\",\n        \"//tachyon/zk/plonk/permutation:permutation_utils\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"proof_serializer\",\n    hdrs = [\"proof_serializer.h\"],\n    deps = [\n        \"//tachyon/base/buffer\",\n        \"//tachyon/math/finite_fields:prime_field_base\",\n        \"//tachyon/math/geometry:affine_point\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"prover\",\n    hdrs = [\"prover.h\"],\n    deps = [\n        \":argument_data\",\n        \":c_prover_impl_base_forward\",\n        \":random_field_generator\",\n        \":verifier\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base:profiler\",\n        \"//tachyon/zk/base/entities:prover_base\",\n        \"//tachyon/zk/lookup:prover\",\n        \"//tachyon/zk/plonk/permutation:permutation_prover\",\n        \"//tachyon/zk/plonk/vanishing:vanishing_prover\",\n        \"//tachyon/zk/shuffle:prover\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"prover_test\",\n    testonly = True,\n    hdrs = [\"prover_test.h\"],\n    deps = [\n        \":blake2b_transcript\",\n        \":constants\",\n        \":prover\",\n        \"//tachyon/crypto/random/xor_shift:xor_shift_rng\",\n        \"//tachyon/math/polynomials/univariate:univariate_evaluation_domain_factory\",\n        \"@com_google_googletest//:gtest\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"proving_scheme\",\n    hdrs = [\"proving_scheme.h\"],\n    deps = [\n        \":vendor\",\n        \"//tachyon/zk/lookup:type\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"random_field_generator\",\n    hdrs = [\"random_field_generator.h\"],\n    deps = [\n        \":prime_field_conversion\",\n        \"//tachyon/crypto/random:rng\",\n        \"//tachyon/zk/base:random_field_generator_base\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"sha256_transcript\",\n    hdrs = [\"sha256_transcript.h\"],\n    deps = [\n        \":constants\",\n        \":prime_field_conversion\",\n        \":proof_serializer\",\n        \"//tachyon/crypto/transcripts:transcript\",\n        \"@com_google_boringssl//:crypto\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"synthesizer\",\n    hdrs = [\"synthesizer.h\"],\n    deps = [\n        \":witness_collection\",\n        \"//tachyon/zk/base/entities:prover_base\",\n        \"//tachyon/zk/plonk/constraint_system\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"snark_verifier_poseidon_transcript\",\n    hdrs = [\"snark_verifier_poseidon_transcript.h\"],\n    deps = [\n        \":prime_field_conversion\",\n        \":proof_serializer\",\n        \"//tachyon/crypto/hashes/sponge/poseidon\",\n        \"//tachyon/crypto/hashes/sponge/poseidon:poseidon_params\",\n        \"//tachyon/crypto/transcripts:transcript\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"transcript_type\",\n    hdrs = [\"transcript_type.h\"],\n    deps = [\n        \"//tachyon/base/flag\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"vendor\",\n    srcs = [\"vendor.cc\"],\n    hdrs = [\"vendor.h\"],\n    deps = [\n        \"//tachyon:export\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base/flag\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"verifier\",\n    hdrs = [\"verifier.h\"],\n    deps = [\n        \":proof_reader\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/zk/base/entities:verifier_base\",\n        \"//tachyon/zk/lookup:verifier\",\n        \"//tachyon/zk/lookup/halo2:opening_point_set\",\n        \"//tachyon/zk/lookup/halo2:utils\",\n        \"//tachyon/zk/plonk/keys:verifying_key\",\n        \"//tachyon/zk/plonk/permutation:permutation_verifier\",\n        \"//tachyon/zk/plonk/vanishing:vanishing_utils\",\n        \"//tachyon/zk/plonk/vanishing:vanishing_verifier\",\n        \"//tachyon/zk/shuffle:opening_point_set\",\n        \"//tachyon/zk/shuffle:utils\",\n        \"//tachyon/zk/shuffle:verifier\",\n        \"@com_google_googletest//:gtest_prod\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"witness_collection\",\n    hdrs = [\"witness_collection.h\"],\n    deps = [\n        \"//tachyon/base:range\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/zk/plonk/base:phase\",\n        \"//tachyon/zk/plonk/layout:assignment\",\n        \"@com_google_absl//absl/container:btree\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"halo2_unittests\",\n    srcs = [\n        \"argument_data_unittest.cc\",\n        \"blake2b_transcript_unittest.cc\",\n        \"poseidon_transcript_unittest.cc\",\n        \"prime_field_conversion_unittest.cc\",\n        \"proof_serializer_unittest.cc\",\n        \"proof_unittest.cc\",\n        \"random_field_generator_unittest.cc\",\n        \"sha256_transcript_unittest.cc\",\n        \"snark_verifier_poseidon_transcript_unittest.cc\",\n        \"witness_collection_unittest.cc\",\n    ],\n    deps = [\n        \":argument_data\",\n        \":blake2b_transcript\",\n        \":bn254_shplonk_prover_test\",\n        \":poseidon_transcript\",\n        \":proof\",\n        \":proof_serializer\",\n        \":random_field_generator\",\n        \":sha256_transcript\",\n        \":snark_verifier_poseidon_transcript\",\n        \":witness_collection\",\n        \"//tachyon/base:auto_reset\",\n        \"//tachyon/crypto/random/xor_shift:xor_shift_rng\",\n        \"//tachyon/math/elliptic_curves/bn/bn254\",\n        \"//tachyon/math/finite_fields/test:finite_field_test\",\n        \"//tachyon/math/finite_fields/test:gf7\",\n        \"//tachyon/math/polynomials/univariate:univariate_evaluation_domain_factory\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/README.md",
    "content": "# Optimizations\n\nContains a list of optimizations that have been made to the [scroll/halo2](https://github.com/scroll-tech/halo2) codebase. This is a living document and will be updated as new optimizations are made.\n\n## 1. PrimeField in Assembly\n\nWe use a faster prime field which is generated by [iden3/ffiasm](https://github.com/iden3/ffiasm).\n\nRelevant commit: [here](https://github.com/kroma-network/tachyon/commit/5a414e4).\n\n## 2. Lazy IFFT\n\nSome parts of the Halo2 code hold polynomials in both coefficient and evaluation form. Considering that polynomials in coefficient form are only needed after squeezing y, we can delay IFFT until y is squeezed. This helps reduce memory pressure.\n\nRelevant commit: [here](https://github.com/kroma-network/tachyon/pull/294/commits/0a9fe9c)\n\n### 2.1. Lazy IFFT for Permutation Argument\n\nRelevant code optimization: [here](https://github.com/kroma-network/tachyon/pull/294/commits/0a9fe9c56c8d7bf9d193c89991003cee3d6a88b9#diff-a66badbb6b9b05ff643f192101f10cd59fe8edc0d9e63583f088859089c4e80fR202-R203).\n\nRelevant halo2 code: [permutation product poly].\n\n[permutation product poly]: https://github.com/scroll-tech/halo2/blob/1070391/halo2_proofs/src/plonk/permutation/prover.rs#L170-L172\n\n### 2.2. Lazy IFFT for Lookup Argument\n\nRelevant code optimization: [here](https://github.com/kroma-network/tachyon/pull/294/commits/0a9fe9c56c8d7bf9d193c89991003cee3d6a88b9#diff-a66badbb6b9b05ff643f192101f10cd59fe8edc0d9e63583f088859089c4e80fR204-R205).\n\nRelevant halo2 code: [permuted input poly], [permuted table poly] and [lookup product poly].\n\n[permuted input poly]: https://github.com/scroll-tech/halo2/blob/1070391/halo2_proofs/src/plonk/lookup/prover.rs#L136-L137\n[permuted table poly]: https://github.com/scroll-tech/halo2/blob/1070391/halo2_proofs/src/plonk/lookup/prover.rs#L139-L141\n[lookup product poly]: https://github.com/scroll-tech/halo2/blob/1070391/halo2_proofs/src/plonk/lookup/prover.rs#L291-L293\n\n## 3. Batch Normalize\n\nOriginally, when committing to multiple polynomials sequentially, the conversion from xyzz point type to affine point is done individually for each commitment, which leads to a large number of **expensive** inverse operations. We optimize this inefficiency with **Batch Normalize**.\n\nTo set up **Batch Normalize**, conversions from xyzz point type to affine point are delayed until all commitments are calculated. Once all calculations are finished, xyzz point types are converted to affine points at once with [BatchNormalize()](https://github.com/kroma-network/tachyon/blob/522243b/tachyon/math/elliptic_curves/short_weierstrass/point_xyzz.h#L113-L159). This reduces the number of required inverse operations to once per sequence.\n\nRelevant commit: [here](https://github.com/kroma-network/tachyon/pull/294/commits/0a9fe9c)\n\n### 3.1. Batch Normalize for Permuted Polys\n\nRelevant code optimization: [here](https://github.com/kroma-network/tachyon/pull/294/commits/0a9fe9c56c8d7bf9d193c89991003cee3d6a88b9#diff-a66badbb6b9b05ff643f192101f10cd59fe8edc0d9e63583f088859089c4e80fR150-R165).\n\nRelevant halo2 code: [permuted input poly] and [permuted table poly].\n\n### 3.2. Batch Normalize for Grand Product Polys\n\nRelevant code optimization: [here](https://github.com/kroma-network/tachyon/pull/294/commits/0a9fe9c56c8d7bf9d193c89991003cee3d6a88b9#diff-a66badbb6b9b05ff643f192101f10cd59fe8edc0d9e63583f088859089c4e80fR172-R196).\n\nRelevant halo2 code: [permutation product poly] and [lookup product poly].\n\n### 3.3. Batch Inverse in Batch Normalize\n\nInverse operations are costly for elliptic curves. We can reduce the number of inverse operations on multiple points by using [BatchInverse()](https://github.com/kroma-network/tachyon/blob/48891d5/tachyon/math/base/groups.h#L152-L199), which only requires one inverse operation for all the points included.\n\nWe fully utilize **Batch Inverse** to avoid multiple inverse operations. For example, the original [batch_normalize()](https://github.com/zkcrypto/group/blob/0c5b044/src/lib.rs#L102-L110) inefficiently computed an inverse operation for every point, so we optimized our [BatchNormalize()](https://github.com/kroma-network/tachyon/blob/48891d5/tachyon/math/elliptic_curves/short_weierstrass/point_xyzz.h#L113-L159) to use the batch inverse operation to normalize all the points with a single inverse operation.\n\n## 4. Optimizing parallelization code\n\nWe merge multiple parallelized parts into a single parallelized part for better performance by reducing the number of thread spawns and joins. Here are the list of examples:\n\n| Description           | Tachyon       | Halo2         |\n| --------------------- | ------------- | ------------- |\n| Merge parallelization | [merging 1-t] | [merging 1-h] |\n| Use collapse clause   | [merging 2-t] | [merging 2-h] |\n\n[merging 1-t]: https://github.com/kroma-network/tachyon/pull/297/commits/d38013c\n[merging 1-h]: https://github.com/scroll-tech/halo2/blob/1070391/halo2_proofs/src/plonk/evaluation.rs#L300-L715\n[merging 2-t]: https://github.com/kroma-network/tachyon/commit/6d6273e\n[merging 2-h]: https://github.com/scroll-tech/halo2/blob/1070391/halo2_proofs/src/poly/domain.rs#L181-L189\n\n## 5. Optimizing MSM\n\nSeveral optimizations have been made to the Multi-Scalar Multiplication (MSM) operation. For scalar multiplication, we have implemented [windowed non-adjacent form (WNAF)](https://github.com/kroma-network/tachyon/blob/655fdff/tachyon/math/elliptic_curves/msm/algorithms/pippenger/pippenger.h#L110-L160), which is a more efficient way of representing the scalar. For group operations, we have changed the bucket type to the PointXYZZ type, which is more efficient than the Jacobian type. View the benchmark result regarding the change to PointXYZZ type [here](https://github.com/kroma-network/tachyon/pull/36#issuecomment-1715653932).\n\n## 6. Saving heap allocation\n\nConsidering the memory used by polynomials is typically high, reducing the number of memory allocations and reusing the existing allocated memory is very important.\n\n| Description                                           | Tachyon      | Halo2        |\n| ----------------------------------------------------- | ------------ | ------------ |\n| Reuse compressed evals buffer                         | [saving 1-t] | [saving 1-h] |\n| Reuse z buffer                                        | [saving 2-t] | [saving 2-h] |\n| Compute l polys                                       | [saving 3-t] | [saving 3-h] |\n| Avoid intermediate memory allocation when transposing | [saving 4-t] | [saving 4-h] |\n\n[saving 1-t]: https://github.com/kroma-network/tachyon/commit/216288e\n[saving 1-h]: https://github.com/scroll-tech/halo2/blob/1070391/halo2_proofs/src/plonk/lookup/prover.rs#L105\n[saving 2-t]: https://github.com/kroma-network/tachyon/commit/fa3a54c\n[saving 2-h]: https://github.com/scroll-tech/halo2/blob/1070391/halo2_proofs/src/plonk/permutation/prover.rs#L153\n[saving 3-t]: https://github.com/kroma-network/tachyon/commit/f77a70d#diff-15682b0\n[saving 3-h]: https://github.com/scroll-tech/halo2/blob/1070391/halo2_proofs/src/plonk/keygen.rs#L566-L592\n[saving 4-t]: https://github.com/kroma-network/tachyon/pull/288/commits/44e089f\n[saving 4-h]: https://github.com/scroll-tech/halo2/blob/1070391/halo2_proofs/src/poly/domain.rs#L182\n\n## 7. Benchmarks for each circuit\n\nWe provide benchmarks of Tachyon and Halo2 for three main circuits by [Privacy-scaling-explorations](https://github.com/privacy-scaling-explorations/zkevm-circuits) and [Scroll](https://github.com/scroll-tech/zkevm-circuits).\n\n- Tachyon commit hash: [24ebe1f](https://github.com/kroma-network/tachyon/commit/24ebe1f)\n- halo2 commit hash: [0918c29e](https://github.com/kroma-network/halo2/commit/0918c29e)\n- zkevm-circuits commit hash: [4f431bf](https://github.com/kroma-network/zkevm-circuits/commit/4f431bf)\n\nThe following are the results of the `real_prover` test for the `Transfer 0` block in [the integration tests](https://github.com/kroma-network/zkevm-circuits/blob/dev/integration-tests/tests/circuits.rs) of `zkevm-circuits`. The times, in seconds, were recorded on an AWS EC2 instance (name: r6i.8xlarge, vCPU: 32, RAM: 256GB).\n\n| Steps            | Tx:Tachyon |   Tx:Halo2 | EVM:Tachyon | EVM:Halo2 | Super:Tachyon | Super:Halo2 |\n| :--------------- | ---------: | ---------: | ----------: | --------: | ------------: | ----------: |\n| degree           |         20 |         20 |          18 |        18 |            20 |          20 |\n| before theta     |     189.44 | **177.91** |       33.54 | **27.02** |    **400.52** |      405.87 |\n| theta            |  **12.77** |      20.65 |    **6.44** |     13.50 |    **103.24** |      171.76 |\n| - compress       |   **1.12** |       2.73 |    **0.23** |      0.46 |      **6.59** |       14.35 |\n| - permute        |   **9.67** |      11.04 |    **4.93** |      8.37 |     **72.87** |       88.26 |\n| - commit         |   **1.98** |       6.89 |    **1.28** |      4.66 |     **23.78** |       69.15 |\n| beta-gamma       |  **15.75** |      32.18 |    **7.79** |     14.62 |     **91.04** |      174.76 |\n| y                | **130.50** |     261.97 |  **161.96** |    218.37 |   **2130.99** |     3426.80 |\n| - transform poly |   **4.27** |       6.57 |    **1.86** |      2.79 |     **23.83** |       34.37 |\n| - build h poly   | **126.23** |     255.40 |  **160.10** |    215.58 |   **2107.16** |     3392.43 |\n| x                |   **1.49** |       1.58 |    **2.18** |      2.47 |         15.50 |   **14.42** |\n| shplonk          |       8.05 |   **5.87** |    **1.35** |      3.31 |         37.52 |   **25.30** |\n| backend total    | **168.56** |     322.25 |  **179.72** |    252.27 |   **2378.29** |     3813.04 |\n| end-to-end total | **358.58** |     500.16 |  **213.26** |    279.29 |   **2778.81** |     4218.91 |\n\n- Greek characters such as `theta` and `beta` are adopted from the [halo2-book](https://zcash.github.io/halo2/design/proving-system.html#tldr)\n- `before theta` refers to the total time taken by the Halo2 including the circuit frontend.\n- `backend total` refers to the total time taken from `theta` to `shplonk`.\n- `end-to-end total` refers to the total time taken by the program (`before theta` + `backend total`).\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/argument_data.h",
    "content": "#ifndef TACHYON_ZK_PLONK_HALO2_ARGUMENT_DATA_H_\n#define TACHYON_ZK_PLONK_HALO2_ARGUMENT_DATA_H_\n\n#include <stddef.h>\n\n#include <utility>\n#include <vector>\n\n#include \"absl/types/span.h\"\n\n#include \"tachyon/base/buffer/copyable.h\"\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/parallelize.h\"\n#include \"tachyon/math/base/parallelize_threshold.h\"\n#include \"tachyon/zk/plonk/base/multi_phase_ref_table.h\"\n#include \"tachyon/zk/plonk/halo2/argument_data.h\"\n#include \"tachyon/zk/plonk/halo2/synthesizer.h\"\n\nnamespace tachyon {\nnamespace zk::plonk::halo2 {\n\ntemplate <typename Poly, typename Evals>\nclass ArgumentData {\n public:\n  using F = typename Poly::Field;\n\n  ArgumentData() = default;\n\n  // NOTE(chokobole): This constructor is used by the c api.\n  explicit ArgumentData(size_t num_circuits) {\n    advice_columns_vec_.resize(num_circuits);\n    advice_blinds_vec_.resize(num_circuits);\n    instance_columns_vec_.resize(num_circuits);\n    instance_polys_vec_.resize(num_circuits);\n  }\n\n  ArgumentData(std::vector<std::vector<Evals>>&& advice_columns_vec,\n               std::vector<std::vector<F>>&& advice_blinds_vec,\n               std::vector<F>&& challenges,\n               std::vector<std::vector<Evals>>&& instance_columns_vec,\n               std::vector<std::vector<Poly>>&& instance_polys_vec)\n      : advice_columns_vec_(std::move(advice_columns_vec)),\n        advice_blinds_vec_(std::move(advice_blinds_vec)),\n        challenges_(std::move(challenges)),\n        instance_columns_vec_(std::move(instance_columns_vec)),\n        instance_polys_vec_(std::move(instance_polys_vec)) {\n    size_t num_circuits = advice_columns_vec_.size();\n    CHECK_EQ(num_circuits, advice_blinds_vec_.size());\n    CHECK_EQ(num_circuits, instance_columns_vec_.size());\n    CHECK_EQ(num_circuits, instance_polys_vec_.size());\n  }\n\n  template <typename PCS, typename Circuit>\n  static ArgumentData Create(\n      ProverBase<PCS>* prover, std::vector<Circuit>& circuits,\n      const ConstraintSystem<F>& constraint_system,\n      std::vector<std::vector<Evals>>&& instance_columns_vec) {\n    // Generate instance polynomial and write it to transcript.\n    std::vector<std::vector<Poly>> instance_polys_vec =\n        GenerateInstancePolys(prover, instance_columns_vec);\n\n    // Append leading zeros to each column of |instance_columns_vec|.\n    size_t num_circuits = circuits.size();\n    size_t n = prover->pcs().N();\n    for (size_t i = 0; i < num_circuits; ++i) {\n      for (Evals& instance_column : instance_columns_vec[i]) {\n        base::ParallelizeResize(instance_column.evaluations(), n, F::Zero(),\n                                math::ParallelizeThreshold::kFieldInit);\n      }\n    }\n\n    // Generate advice poly by synthesizing circuit and write it to transcript.\n    Synthesizer<Evals> synthesizer(num_circuits, &constraint_system);\n    synthesizer.GenerateAdviceColumns(prover, circuits, instance_columns_vec);\n\n    return ArgumentData(std::move(synthesizer).TakeAdviceColumnsVec(),\n                        std::move(synthesizer).TakeAdviceBlindsVec(),\n                        std::move(synthesizer).TakeChallenges(),\n                        std::move(instance_columns_vec),\n                        std::move(instance_polys_vec));\n  }\n\n  // NOTE(chokobole): These getters are used by the c api.\n  std::vector<std::vector<Evals>>& advice_columns_vec() {\n    return advice_columns_vec_;\n  }\n  std::vector<std::vector<F>>& advice_blinds_vec() {\n    return advice_blinds_vec_;\n  }\n  std::vector<F>& challenges() { return challenges_; }\n  std::vector<std::vector<Evals>>& instance_columns_vec() {\n    return instance_columns_vec_;\n  }\n  std::vector<std::vector<Poly>>& instance_polys_vec() {\n    return instance_polys_vec_;\n  }\n\n  size_t GetNumCircuits() const { return instance_polys_vec_.size(); }\n\n  absl::Span<const F> GetAdviceBlinds(size_t circuit_idx) const {\n    CHECK_LT(circuit_idx, GetNumCircuits());\n    return advice_blinds_vec_[circuit_idx];\n  }\n\n  // Generate a vector of advice coefficient-formed polynomials with a vector\n  // of advice evaluation-formed columns. (a.k.a. Batch IFFT)\n  // And for memory optimization, every evaluations of advice will be released\n  // as soon as transforming it to coefficient form.\n  template <typename Domain>\n  void TransformEvalsToPoly(const Domain* domain) {\n    VLOG(2) << \"Transform advice columns to polys\";\n    CHECK(!advice_transformed_);\n    advice_polys_vec_ = base::Map(\n        advice_columns_vec_, [domain](std::vector<Evals>& advice_columns) {\n          return base::Map(advice_columns, [domain](Evals& advice_column) {\n            return domain->IFFT(std::move(advice_column));\n          });\n        });\n    advice_transformed_ = true;\n  }\n\n  void DeallocateAllColumnsVec() {\n    advice_columns_vec_.clear();\n    instance_columns_vec_.clear();\n  }\n\n  // Return tables including every type of columns in evaluation form.\n  std::vector<MultiPhaseRefTable<Evals>> ExportColumnTables(\n      absl::Span<const Evals> fixed_columns) const {\n    CHECK(!advice_transformed_);\n    return base::CreateVector(GetNumCircuits(), [fixed_columns,\n                                                 this](size_t i) {\n      return MultiPhaseRefTable<Evals>(fixed_columns, advice_columns_vec_[i],\n                                       instance_columns_vec_[i], challenges_);\n    });\n  }\n\n  // Return a table including every type of columns in coefficient form.\n  std::vector<MultiPhaseRefTable<Poly>> ExportPolyTables(\n      absl::Span<const Poly> fixed_polys) const {\n    CHECK(advice_transformed_);\n    return base::CreateVector(GetNumCircuits(), [fixed_polys, this](size_t i) {\n      return MultiPhaseRefTable<Poly>(fixed_polys, advice_polys_vec_[i],\n                                      instance_polys_vec_[i], challenges_);\n    });\n  }\n\n  bool operator==(const ArgumentData& other) const {\n    return advice_transformed_ == other.advice_transformed_ &&\n           advice_columns_vec_ == other.advice_columns_vec_ &&\n           advice_polys_vec_ == other.advice_polys_vec_ &&\n           advice_blinds_vec_ == other.advice_blinds_vec_ &&\n           challenges_ == other.challenges_ &&\n           instance_columns_vec_ == other.instance_columns_vec_ &&\n           instance_polys_vec_ == other.instance_polys_vec_;\n  }\n  bool operator!=(const ArgumentData& other) const {\n    return !operator==(other);\n  }\n\n private:\n  friend class base::Copyable<ArgumentData>;\n\n  // Generate a vector of instance coefficient-formed polynomials with a vector\n  // of instance evaluation-formed columns. (a.k.a. Batch IFFT)\n  template <typename PCS>\n  static std::vector<std::vector<Poly>> GenerateInstancePolys(\n      ProverBase<PCS>* prover,\n      const std::vector<std::vector<Evals>>& instance_columns_vec) {\n    VLOG(2) << \"Generating instance polys\";\n    size_t num_circuit = instance_columns_vec.size();\n    CHECK_GT(num_circuit, size_t{0});\n    size_t num_instance_columns = instance_columns_vec[0].size();\n    if constexpr (PCS::kSupportsBatchMode && PCS::kQueryInstance) {\n      size_t num_commitment = num_circuit * num_instance_columns;\n      prover->pcs().SetBatchMode(num_commitment);\n    }\n\n    std::vector<std::vector<Poly>> instance_polys_vec;\n    instance_polys_vec.reserve(num_circuit);\n    for (size_t i = 0; i < num_circuit; ++i) {\n      const std::vector<Evals>& instance_columns = instance_columns_vec[i];\n      std::vector<Poly> instance_polys;\n      instance_polys.reserve(num_instance_columns);\n      for (size_t j = 0; j < num_instance_columns; ++j) {\n        const Evals& instance_column = instance_columns[j];\n        if constexpr (PCS::kQueryInstance && PCS::kSupportsBatchMode) {\n          prover->BatchCommitAt(instance_column, i * num_instance_columns + j);\n        } else if constexpr (PCS::kQueryInstance && !PCS::kSupportsBatchMode) {\n          prover->CommitAndWriteToTranscript(instance_column);\n        } else {\n          for (const F& instance : instance_column.evaluations()) {\n            CHECK(prover->GetWriter()->WriteToTranscript(instance));\n          }\n        }\n        instance_polys.push_back(prover->domain()->IFFT(instance_column));\n      }\n      instance_polys_vec.push_back(std::move(instance_polys));\n    }\n    if constexpr (PCS::kSupportsBatchMode && PCS::kQueryInstance) {\n      prover->RetrieveAndWriteBatchCommitmentsToTranscript();\n    }\n    return instance_polys_vec;\n  }\n\n  // NOTE(dongchangYoo): to optimize memory usage, release every advice\n  // evaluations after generating an advice polynomial. That is, when\n  // |advice_transformed_| is set to true, |advice_columns_vec_| is\n  // released, and only |advice_polys_vec_| becomes available for use.\n  bool advice_transformed_ = false;\n  std::vector<std::vector<Evals>> advice_columns_vec_;\n  std::vector<std::vector<Poly>> advice_polys_vec_;\n  std::vector<std::vector<F>> advice_blinds_vec_;\n  std::vector<F> challenges_;\n\n  std::vector<std::vector<Evals>> instance_columns_vec_;\n  std::vector<std::vector<Poly>> instance_polys_vec_;\n};\n\n}  // namespace zk::plonk::halo2\n\nnamespace base {\n\ntemplate <typename Poly, typename Evals>\nclass Copyable<zk::plonk::halo2::ArgumentData<Poly, Evals>> {\n public:\n  using F = typename Poly::Field;\n\n  static bool WriteTo(const zk::plonk::halo2::ArgumentData<Poly, Evals>& data,\n                      Buffer* buffer) {\n    return buffer->WriteMany(data.advice_columns_vec_, data.advice_blinds_vec_,\n                             data.challenges_, data.instance_columns_vec_,\n                             data.instance_polys_vec_);\n  }\n\n  static bool ReadFrom(const ReadOnlyBuffer& buffer,\n                       zk::plonk::halo2::ArgumentData<Poly, Evals>* data) {\n    std::vector<std::vector<Evals>> advice_columns_vec;\n    std::vector<std::vector<F>> advice_blinds_vec;\n    std::vector<F> challenges;\n    std::vector<std::vector<Evals>> instance_columns_vec;\n    std::vector<std::vector<Poly>> instance_polys_vec;\n    if (!buffer.ReadMany(&advice_columns_vec, &advice_blinds_vec, &challenges,\n                         &instance_columns_vec, &instance_polys_vec))\n      return false;\n    *data = zk::plonk::halo2::ArgumentData<Poly, Evals>(\n        std::move(advice_columns_vec), std::move(advice_blinds_vec),\n        std::move(challenges), std::move(instance_columns_vec),\n        std::move(instance_polys_vec));\n    return true;\n  }\n\n  static size_t EstimateSize(\n      const zk::plonk::halo2::ArgumentData<Poly, Evals>& data) {\n    return base::EstimateSize(data.advice_columns_vec_, data.advice_blinds_vec_,\n                              data.challenges_, data.instance_columns_vec_,\n                              data.instance_polys_vec_);\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_ZK_PLONK_HALO2_ARGUMENT_DATA_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/argument_data_unittest.cc",
    "content": "#include \"tachyon/zk/plonk/halo2/argument_data.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/buffer/vector_buffer.h\"\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n\nnamespace tachyon::zk::plonk::halo2 {\n\nnamespace {\n\nconstexpr size_t kMaxDegree = 7;\n\nclass ArgumentDataTest : public math::FiniteFieldTest<math::GF7> {\n public:\n  using Poly = math::UnivariateDensePolynomial<math::GF7, kMaxDegree>;\n  using Evals = math::UnivariateEvaluations<math::GF7, kMaxDegree>;\n};\n\n}  // namespace\n\nTEST_F(ArgumentDataTest, Copyable) {\n  constexpr size_t kNumCircuits = 2;\n  constexpr size_t kNumAdvices = 3;\n  constexpr size_t kNumBlinds = 4;\n  constexpr size_t kNumChallenges = 2;\n  constexpr size_t kNumInstances = 2;\n\n  std::vector<std::vector<Evals>> advice_columns_vec =\n      base::CreateVector(kNumCircuits, []() {\n        return base::CreateVector(kNumAdvices,\n                                  []() { return Evals::Random(kMaxDegree); });\n      });\n  std::vector<std::vector<math::GF7>> advice_blinds_vec =\n      base::CreateVector(kNumCircuits, []() {\n        return base::CreateVector(kNumBlinds,\n                                  []() { return math::GF7::Random(); });\n      });\n  std::vector<math::GF7> challenges =\n      base::CreateVector(kNumChallenges, []() { return math::GF7::Random(); });\n  std::vector<std::vector<Evals>> instance_columns_vec =\n      base::CreateVector(kNumCircuits, []() {\n        return base::CreateVector(kNumInstances,\n                                  []() { return Evals::Random(kMaxDegree); });\n      });\n  std::vector<std::vector<Poly>> instance_polys_vec =\n      base::CreateVector(kNumCircuits, []() {\n        return base::CreateVector(kNumInstances,\n                                  []() { return Poly::Random(kMaxDegree); });\n      });\n\n  ArgumentData<Poly, Evals> expected(\n      std::move(advice_columns_vec), std::move(advice_blinds_vec),\n      std::move(challenges), std::move(instance_columns_vec),\n      std::move(instance_polys_vec));\n\n  base::Uint8VectorBuffer write_buf;\n  ASSERT_TRUE(write_buf.Grow(base::EstimateSize(expected)));\n  ASSERT_TRUE(write_buf.Write(expected));\n  ASSERT_TRUE(write_buf.Done());\n\n  write_buf.set_buffer_offset(0);\n\n  ArgumentData<Poly, Evals> value;\n  ASSERT_TRUE(write_buf.Read(&value));\n\n  EXPECT_EQ(value, expected);\n}\n\n}  // namespace tachyon::zk::plonk::halo2\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/blake2b_transcript.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_HALO2_BLAKE2B_TRANSCRIPT_H_\n#define TACHYON_ZK_PLONK_HALO2_BLAKE2B_TRANSCRIPT_H_\n\n#include <stdint.h>\n\n#include <utility>\n#include <vector>\n\n#include \"absl/types/span.h\"\n#include \"openssl/blake2.h\"\n\n#include \"tachyon/crypto/transcripts/transcript.h\"\n#include \"tachyon/zk/plonk/halo2/constants.h\"\n#include \"tachyon/zk/plonk/halo2/prime_field_conversion.h\"\n#include \"tachyon/zk/plonk/halo2/proof_serializer.h\"\n\nnamespace tachyon::zk::plonk::halo2 {\nnamespace internal {\n\ntemplate <typename AffinePoint>\nclass Blake2bBase {\n protected:\n  using BaseField = typename AffinePoint::BaseField;\n  using ScalarField = typename AffinePoint::ScalarField;\n\n  Blake2bBase() { BLAKE2B512_InitWithPersonal(&state_, kTranscriptStr); }\n\n  ScalarField DoSqueezeChallenge() {\n    DoUpdate(kBlake2bPrefixChallenge, 1);\n    uint8_t result[BLAKE2B512_DIGEST_LENGTH] = {0};\n    DoFinalize(result);\n\n    return FromUint512<ScalarField>(result);\n  }\n\n  bool DoWriteToTranscript(const AffinePoint& point) {\n    DoUpdate(kBlake2bPrefixPoint, 1);\n    if (point.IsZero()) {\n      DoUpdate(BaseField::BigIntTy::Zero().ToBytesLE().data(),\n               BaseField::BigIntTy::kByteNums);\n      DoUpdate(typename BaseField::BigIntTy(5).ToBytesLE().data(),\n               BaseField::BigIntTy::kByteNums);\n    } else {\n      DoUpdate(point.x().ToBigInt().ToBytesLE().data(),\n               BaseField::BigIntTy::kByteNums);\n      DoUpdate(point.y().ToBigInt().ToBytesLE().data(),\n               BaseField::BigIntTy::kByteNums);\n    }\n    return true;\n  }\n\n  bool DoWriteToTranscript(const ScalarField& scalar) {\n    DoUpdate(kBlake2bPrefixScalar, 1);\n    DoUpdate(scalar.ToBigInt().ToBytesLE().data(),\n             ScalarField::BigIntTy::kByteNums);\n    return true;\n  }\n\n  void DoUpdate(const void* data, size_t len) {\n    BLAKE2B512_Update(&state_, data, len);\n  }\n\n  void DoFinalize(uint8_t result[BLAKE2B512_DIGEST_LENGTH]) {\n    BLAKE2B_CTX hasher = state_;\n    BLAKE2B512_Final(result, &hasher);\n  }\n\n  std::vector<uint8_t> DoGetState() const {\n    const blake2b_state_st* state_impl =\n        reinterpret_cast<const blake2b_state_st*>(&state_);\n    base::Uint8VectorBuffer buffer;\n    buffer.set_endian(base::Endian::kLittle);\n    CHECK(buffer.Grow(sizeof(blake2b_state_st)));\n    CHECK(buffer.WriteMany(state_impl->h, state_impl->t_low, state_impl->t_high,\n                           state_impl->block, state_impl->block_used));\n    CHECK(buffer.Done());\n    return std::move(buffer).TakeOwnedBuffer();\n  }\n\n  void DoSetState(absl::Span<const uint8_t> state) {\n    base::ReadOnlyBuffer buffer(state.data(), state.size());\n    buffer.set_endian(base::Endian::kLittle);\n    blake2b_state_st* state_impl = reinterpret_cast<blake2b_state_st*>(&state_);\n    CHECK(buffer.ReadMany(state_impl->h, &state_impl->t_low,\n                          &state_impl->t_high, state_impl->block,\n                          &state_impl->block_used));\n    CHECK(buffer.Done());\n  }\n\n  BLAKE2B_CTX state_;\n};\n\n}  // namespace internal\n\n// TODO(TomTaehoonKim): We will replace Blake2b with an algebraic hash function\n// in a later version. See\n// https://github.com/kroma-network/halo2/blob/7d0a369/halo2_proofs/src/transcript/blake2b.rs#L25\ntemplate <typename AffinePoint>\nclass Blake2bReader : public crypto::TranscriptReader<AffinePoint>,\n                      protected internal::Blake2bBase<AffinePoint> {\n public:\n  using ScalarField = typename AffinePoint::ScalarField;\n\n  // Initialize a transcript given an input buffer.\n  explicit Blake2bReader(base::ReadOnlyBuffer read_buf)\n      : crypto::TranscriptReader<AffinePoint>(std::move(read_buf)) {}\n\n  // crypto::TranscriptReader methods\n  ScalarField SqueezeChallenge() override { return this->DoSqueezeChallenge(); }\n\n  bool WriteToTranscript(const AffinePoint& point) override {\n    return this->DoWriteToTranscript(point);\n  }\n\n  bool WriteToTranscript(const ScalarField& scalar) override {\n    return this->DoWriteToTranscript(scalar);\n  }\n\n private:\n  bool DoReadFromProof(AffinePoint* point) const override {\n    return ProofSerializer<AffinePoint>::ReadFromProof(this->buffer_, point);\n  }\n\n  bool DoReadFromProof(ScalarField* scalar) const override {\n    return ProofSerializer<ScalarField>::ReadFromProof(this->buffer_, scalar);\n  }\n};\n\ntemplate <typename AffinePoint>\nclass Blake2bWriter : public crypto::TranscriptWriter<AffinePoint>,\n                      protected internal::Blake2bBase<AffinePoint> {\n public:\n  using ScalarField = typename AffinePoint::ScalarField;\n\n  // Initialize a transcript given an output buffer.\n  explicit Blake2bWriter(base::Uint8VectorBuffer write_buf)\n      : crypto::TranscriptWriter<AffinePoint>(std::move(write_buf)) {}\n\n  // NOTE(chokobole): |GetDigestLen()|, |GetStateLen()|, |Update()|,\n  // |Finalize()|, |GetState()| and |SetState()| are called from rust binding.\n  size_t GetDigestLen() const { return BLAKE2B512_DIGEST_LENGTH; }\n\n  size_t GetStateLen() const { return sizeof(blake2b_state_st); }\n\n  void Update(const void* data, size_t len) { this->DoUpdate(data, len); }\n\n  void Finalize(uint8_t result[BLAKE2B512_DIGEST_LENGTH]) {\n    this->DoFinalize(result);\n  }\n\n  std::vector<uint8_t> GetState() const { return this->DoGetState(); }\n\n  void SetState(absl::Span<const uint8_t> state) { this->DoSetState(state); }\n\n  // crypto::TranscriptWriter methods\n  ScalarField SqueezeChallenge() override { return this->DoSqueezeChallenge(); }\n\n  bool WriteToTranscript(const AffinePoint& point) override {\n    return this->DoWriteToTranscript(point);\n  }\n\n  bool WriteToTranscript(const ScalarField& scalar) override {\n    return this->DoWriteToTranscript(scalar);\n  }\n\n private:\n  bool DoWriteToProof(const AffinePoint& point) override {\n    return ProofSerializer<AffinePoint>::WriteToProof(point, this->buffer_);\n  }\n\n  bool DoWriteToProof(const ScalarField& scalar) override {\n    return ProofSerializer<ScalarField>::WriteToProof(scalar, this->buffer_);\n  }\n};\n\n}  // namespace tachyon::zk::plonk::halo2\n\n#endif  // TACHYON_ZK_PLONK_HALO2_BLAKE2B_TRANSCRIPT_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/blake2b_transcript_unittest.cc",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#include \"tachyon/zk/plonk/halo2/blake2b_transcript.h\"\n\n#include <utility>\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/elliptic_curves/bn/bn254/g1.h\"\n\nnamespace tachyon::zk::plonk::halo2 {\n\nnamespace {\n\nusing namespace math::bn254;\n\nclass Blake2bTranscriptTest : public testing::Test {\n public:\n  static void SetUpTestSuite() { G1Curve::Init(); }\n};\n\n}  // namespace\n\nTEST_F(Blake2bTranscriptTest, WritePoint) {\n  base::Uint8VectorBuffer write_buf;\n  Blake2bWriter<G1AffinePoint> writer(std::move(write_buf));\n  G1AffinePoint expected = G1AffinePoint::Random();\n  ASSERT_TRUE(writer.WriteToProof(expected));\n\n  base::Buffer read_buf(writer.buffer().buffer(), writer.buffer().buffer_len());\n  Blake2bReader<G1AffinePoint> reader(std::move(read_buf));\n  G1AffinePoint actual;\n  ASSERT_TRUE(reader.ReadFromProof(&actual));\n\n  EXPECT_EQ(expected, actual);\n}\n\nTEST_F(Blake2bTranscriptTest, WriteScalar) {\n  base::Uint8VectorBuffer write_buf;\n  Blake2bWriter<G1AffinePoint> writer(std::move(write_buf));\n  Fr expected = Fr::Random();\n  ASSERT_TRUE(writer.WriteToProof(expected));\n\n  base::Buffer read_buf(writer.buffer().buffer(), writer.buffer().buffer_len());\n  Blake2bReader<G1AffinePoint> reader(std::move(read_buf));\n  Fr actual;\n  ASSERT_TRUE(reader.ReadFromProof(&actual));\n\n  EXPECT_EQ(expected, actual);\n}\n\nTEST_F(Blake2bTranscriptTest, SqueezeChallenge) {\n  base::Uint8VectorBuffer write_buf;\n  Blake2bWriter<G1AffinePoint> writer(std::move(write_buf));\n  G1AffinePoint generator = G1AffinePoint::Generator();\n  ASSERT_TRUE(writer.WriteToProof(generator));\n\n  std::vector<uint8_t> expected_bytes = {57, 2,   118, 182, 16,  184, 59,  179,\n                                         70, 176, 223, 71,  62,  168, 222, 171,\n                                         85, 224, 83,  43,  148, 194, 132, 184,\n                                         65, 25,  1,   208, 123, 166, 11,  12};\n  Fr expected = Fr::FromBigInt(math::BigInt<4>::FromBytesLE(expected_bytes));\n\n  Fr actual = writer.SqueezeChallenge();\n\n  EXPECT_EQ(expected, actual);\n}\n\n}  // namespace tachyon::zk::plonk::halo2\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/bn254_gwc_prover_test.h",
    "content": "#ifndef TACHYON_ZK_PLONK_HALO2_BN254_GWC_PROVER_TEST_H_\n#define TACHYON_ZK_PLONK_HALO2_BN254_GWC_PROVER_TEST_H_\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/elliptic_curves/bn/bn254/bn254.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/halo2/bn254.h\"\n#include \"tachyon/zk/base/commitments/gwc_extension.h\"\n#include \"tachyon/zk/plonk/halo2/prover_test.h\"\n#include \"tachyon/zk/plonk/halo2/proving_scheme.h\"\n\nnamespace tachyon::zk::plonk::halo2 {\n\nusing PCS = GWCExtension<math::bn254::BN254Curve, kMaxDegree,\n                         kMaxExtendedDegree, math::bn254::G1AffinePoint>;\nusing PS = ProvingScheme<Vendor::kScroll, lookup::Type::kHalo2, PCS>;\n\nclass BN254GWCProverTest : public ProverTest<PS> {\n public:\n  static void SetUpTestSuite() {\n    math::bn254::BN254Curve::Init();\n    math::halo2::OverrideSubgroupGenerator();\n  }\n};\n\n}  // namespace tachyon::zk::plonk::halo2\n\n#endif  // TACHYON_ZK_PLONK_HALO2_BN254_GWC_PROVER_TEST_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/bn254_shplonk_prover_test.h",
    "content": "#ifndef TACHYON_ZK_PLONK_HALO2_BN254_SHPLONK_PROVER_TEST_H_\n#define TACHYON_ZK_PLONK_HALO2_BN254_SHPLONK_PROVER_TEST_H_\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/elliptic_curves/bn/bn254/bn254.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/halo2/bn254.h\"\n#include \"tachyon/zk/base/commitments/shplonk_extension.h\"\n#include \"tachyon/zk/plonk/halo2/prover_test.h\"\n#include \"tachyon/zk/plonk/halo2/proving_scheme.h\"\n\nnamespace tachyon::zk::plonk::halo2 {\n\nusing PCS = SHPlonkExtension<math::bn254::BN254Curve, kMaxDegree,\n                             kMaxExtendedDegree, math::bn254::G1AffinePoint>;\nusing PS = ProvingScheme<Vendor::kScroll, lookup::Type::kHalo2, PCS>;\n\nclass BN254SHPlonkProverTest : public ProverTest<PS> {\n public:\n  static void SetUpTestSuite() {\n    math::bn254::BN254Curve::Init();\n    math::halo2::OverrideSubgroupGenerator();\n  }\n};\n\n}  // namespace tachyon::zk::plonk::halo2\n\n#endif  // TACHYON_ZK_PLONK_HALO2_BN254_SHPLONK_PROVER_TEST_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/c_prover_impl_base_forward.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_HALO2_C_PROVER_IMPL_BASE_FORWARD_H_\n#define TACHYON_ZK_PLONK_HALO2_C_PROVER_IMPL_BASE_FORWARD_H_\n\nnamespace tachyon::c::zk::plonk::halo2 {\n\ntemplate <typename PS>\nclass ProverImplBase;\n\n}  // namespace tachyon::c::zk::plonk::halo2\n\n#endif  // TACHYON_ZK_PLONK_HALO2_C_PROVER_IMPL_BASE_FORWARD_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/config.cc",
    "content": "#include \"tachyon/zk/plonk/halo2/config.h\"\n\nnamespace tachyon::zk::plonk::halo2 {\n\n// static\nConfig& Config::Get() {\n  static Config config;\n  return config;\n}\n\n}  // namespace tachyon::zk::plonk::halo2\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/config.h",
    "content": "#ifndef TACHYON_ZK_PLONK_HALO2_CONFIG_H_\n#define TACHYON_ZK_PLONK_HALO2_CONFIG_H_\n\n#include <stdint.h>\n\n#include \"tachyon/export.h\"\n#include \"tachyon/zk/plonk/halo2/vendor.h\"\n\nnamespace tachyon::zk::plonk::halo2 {\n\nstruct TACHYON_EXPORT Config {\n  // By default, halo2 behaves like scroll halo2 v1.1.\n  Vendor vendor = Vendor::kScroll;\n  uint32_t version = 10100;\n\n  static Config& Get();\n};\n\n}  // namespace tachyon::zk::plonk::halo2\n\n#endif  // TACHYON_ZK_PLONK_HALO2_CONFIG_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/constants.h",
    "content": "#ifndef TACHYON_ZK_PLONK_HALO2_CONSTANTS_H_\n#define TACHYON_ZK_PLONK_HALO2_CONSTANTS_H_\n\n#include <stdint.h>\n\nnamespace tachyon::zk::plonk::halo2 {\n\nconstexpr char kTranscriptStr[] = \"Halo2-Transcript\";\n\n// Prefix to a prover's message soliciting a challenge\nconstexpr uint8_t kBlake2bPrefixChallenge[1] = {0};\n\n// Prefix to a prover's message containing a curve point\nconstexpr uint8_t kBlake2bPrefixPoint[1] = {1};\n\n// Prefix to a prover's message containing a scalar\nconstexpr uint8_t kBlake2bPrefixScalar[1] = {2};\n\n// Dummy zeros that come before prefix to a prover's message\nconstexpr uint8_t kShaPrefixZeros[31] = {0};\n\n// Prefix to a prover's message soliciting a challenge\nconstexpr uint8_t kShaPrefixChallenge[1] = {0};\n\n// Prefix to a prover's message containing a curve point\nconstexpr uint8_t kShaPrefixPoint[1] = {1};\n\n// Prefix to a prover's message containing a scalar\nconstexpr uint8_t kShaPrefixScalar[1] = {2};\n\nconstexpr char kVerifyingKeyStr[] = \"Halo2-Verify-Key\";\n\nconstexpr uint8_t kXORShiftSeed[] = {0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d,\n                                     0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32,\n                                     0x54, 0x06, 0xbc, 0xe5};\n\n}  // namespace tachyon::zk::plonk::halo2\n\n#endif  // TACHYON_ZK_PLONK_HALO2_CONSTANTS_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/pcs_type.h",
    "content": "#ifndef TACHYON_ZK_PLONK_HALO2_PCS_TYPE_H_\n#define TACHYON_ZK_PLONK_HALO2_PCS_TYPE_H_\n\n#include <stdint.h>\n\n#include <string>\n\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/base/flag/flag_value_traits.h\"\n\nnamespace tachyon {\nnamespace zk::plonk::halo2 {\n\n// THE ORDER OF ELEMENTS ARE IMPORTANT!! DO NOT CHANGE!\n// This order matches with the constants of c APIs.\n// Pleas refer to tachyon/c/zk/plonk/halo2/constants.h for details.\nenum class PCSType : uint8_t {\n  kGWC,\n  kSHPlonk,\n};\n\n}  // namespace zk::plonk::halo2\n\nnamespace base {\n\ntemplate <>\nclass FlagValueTraits<zk::plonk::halo2::PCSType> {\n public:\n  static bool ParseValue(std::string_view input,\n                         zk::plonk::halo2::PCSType* value,\n                         std::string* reason) {\n    if (input == \"gwc\") {\n      *value = zk::plonk::halo2::PCSType::kGWC;\n    } else if (input == \"shplonk\") {\n      *value = zk::plonk::halo2::PCSType::kSHPlonk;\n    } else {\n      *reason = absl::Substitute(\"Unknown pcs type: $0\", input);\n      return false;\n    }\n    return true;\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_ZK_PLONK_HALO2_PCS_TYPE_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/pinned_constraint_system.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_HALO2_PINNED_CONSTRAINT_SYSTEM_H_\n#define TACHYON_ZK_PLONK_HALO2_PINNED_CONSTRAINT_SYSTEM_H_\n\n#include <optional>\n#include <string>\n#include <vector>\n\n#include \"tachyon/zk/plonk/constraint_system/constraint_system.h\"\n#include \"tachyon/zk/plonk/halo2/pinned_gates.h\"\n#include \"tachyon/zk/plonk/halo2/stringifiers/lookup_argument_stringifier.h\"\n#include \"tachyon/zk/plonk/halo2/stringifiers/lookup_tracker_stringifier.h\"\n#include \"tachyon/zk/plonk/halo2/stringifiers/permutation_argument_stringifier.h\"\n#include \"tachyon/zk/plonk/halo2/stringifiers/phase_stringifier.h\"\n#include \"tachyon/zk/plonk/halo2/stringifiers/query_stringifier.h\"\n#include \"tachyon/zk/plonk/halo2/stringifiers/shuffle_argument_stringifier.h\"\n\nnamespace tachyon {\nnamespace zk::plonk::halo2 {\n\ntemplate <typename F>\nclass PinnedConstraintSystem {\n public:\n  // NOTE(chokobole): When serializing, Scroll Halo2 does not include shuffles,\n  // whereas PSE Halo2 does include shuffles. See\n  // https://github.com/scroll-tech/halo2/blob/e5ddf67/halo2_proofs/src/plonk/circuit.rs#L1679-L1705.\n  static bool s_include_shuffles;\n\n  explicit PinnedConstraintSystem(const ConstraintSystem<F>& constraint_system)\n      : lookup_type_(constraint_system.lookup_type()),\n        num_fixed_columns_(constraint_system.num_fixed_columns()),\n        num_advice_columns_(constraint_system.num_advice_columns()),\n        num_instance_columns_(constraint_system.num_instance_columns()),\n        num_selectors_(constraint_system.GetNumSelectors()),\n        num_challenges_(constraint_system.num_challenges()),\n        advice_column_phases_(constraint_system.advice_column_phases()),\n        challenge_phases_(constraint_system.challenge_phases()),\n        gates_(constraint_system.gates()),\n        advice_queries_(constraint_system.advice_queries()),\n        instance_queries_(constraint_system.instance_queries()),\n        fixed_queries_(constraint_system.fixed_queries()),\n        permutation_(constraint_system.permutation()),\n        lookups_(constraint_system.lookups()),\n        lookups_map_(constraint_system.lookups_map()),\n        shuffles_(constraint_system.shuffles()),\n        constants_(constraint_system.constants()),\n        minimum_degree_(constraint_system.minimum_degree()) {}\n\n  lookup::Type lookup_type() const { return lookup_type_; }\n  size_t num_fixed_columns() const { return num_fixed_columns_; }\n  size_t num_advice_columns() const { return num_advice_columns_; }\n  size_t num_instance_columns() const { return num_instance_columns_; }\n  size_t num_selectors() const { return num_selectors_; }\n  size_t num_challenges() const { return num_challenges_; }\n  const std::vector<Phase>& advice_column_phases() const {\n    return advice_column_phases_;\n  }\n  const std::vector<Phase>& challenge_phases() const {\n    return challenge_phases_;\n  }\n  const PinnedGates<F>& gates() const { return gates_; }\n  const std::vector<AdviceQueryData>& advice_queries() const {\n    return advice_queries_;\n  }\n  const std::vector<InstanceQueryData>& instance_queries() const {\n    return instance_queries_;\n  }\n  const std::vector<FixedQueryData>& fixed_queries() const {\n    return fixed_queries_;\n  }\n  const PermutationArgument& permutation() const { return permutation_; }\n  const std::vector<lookup::Argument<F>>& lookups() const { return lookups_; }\n  const absl::btree_map<std::string, LookupTracker<F>>& lookups_map() const {\n    return lookups_map_;\n  }\n  const std::vector<shuffle::Argument<F>>& shuffles() const {\n    return shuffles_;\n  }\n  const std::vector<FixedColumnKey>& constants() const { return constants_; }\n  const std::optional<size_t>& minimum_degree() const {\n    return minimum_degree_;\n  }\n\n private:\n  lookup::Type lookup_type_;\n  size_t num_fixed_columns_;\n  size_t num_advice_columns_;\n  size_t num_instance_columns_;\n  size_t num_selectors_;\n  size_t num_challenges_;\n  const std::vector<Phase>& advice_column_phases_;\n  const std::vector<Phase>& challenge_phases_;\n  PinnedGates<F> gates_;\n  const std::vector<AdviceQueryData>& advice_queries_;\n  const std::vector<InstanceQueryData>& instance_queries_;\n  const std::vector<FixedQueryData>& fixed_queries_;\n  PermutationArgument permutation_;\n  const std::vector<lookup::Argument<F>>& lookups_;\n  const absl::btree_map<std::string, LookupTracker<F>>& lookups_map_;\n  const std::vector<shuffle::Argument<F>>& shuffles_;\n  const std::vector<FixedColumnKey>& constants_;\n  const std::optional<size_t>& minimum_degree_;\n};\n\n// static\ntemplate <typename F>\nbool PinnedConstraintSystem<F>::s_include_shuffles = false;\n\n}  // namespace zk::plonk::halo2\n\nnamespace base::internal {\ntemplate <typename F>\nclass RustDebugStringifier<zk::plonk::halo2::PinnedConstraintSystem<F>> {\n public:\n  static std::ostream& AppendToStream(\n      std::ostream& os, RustFormatter& fmt,\n      const zk::plonk::halo2::PinnedConstraintSystem<F>& constraint_system) {\n    DebugStruct debug_struct = fmt.DebugStruct(\"PinnedConstraintSystem\");\n    debug_struct\n        .Field(\"num_fixed_columns\", constraint_system.num_fixed_columns())\n        .Field(\"num_advice_columns\", constraint_system.num_advice_columns())\n        .Field(\"num_instance_columns\", constraint_system.num_instance_columns())\n        .Field(\"num_selectors\", constraint_system.num_selectors());\n    if (constraint_system.num_challenges() > 0) {\n      debug_struct.Field(\"num_challenges\", constraint_system.num_challenges())\n          .Field(\"advice_column_phase\",\n                 constraint_system.advice_column_phases())\n          .Field(\"challenge_phase\", constraint_system.challenge_phases());\n    }\n    debug_struct.Field(\"gates\", constraint_system.gates())\n        .Field(\"advice_queries\", constraint_system.advice_queries())\n        .Field(\"instance_queries\", constraint_system.instance_queries())\n        .Field(\"fixed_queries\", constraint_system.fixed_queries())\n        .Field(\"permutation\", constraint_system.permutation());\n    if (constraint_system.lookup_type() == zk::lookup::Type::kHalo2) {\n      debug_struct.Field(\"lookups\", constraint_system.lookups());\n    } else if (constraint_system.lookup_type() ==\n               zk::lookup::Type::kLogDerivativeHalo2) {\n      debug_struct.Field(\"lookups_map\", constraint_system.lookups_map());\n    }\n    if (zk::plonk::halo2::PinnedConstraintSystem<F>::s_include_shuffles) {\n      debug_struct.Field(\"shuffles\", constraint_system.shuffles());\n    }\n    debug_struct.Field(\"constants\", constraint_system.constants())\n        .Field(\"minimum_degree\", constraint_system.minimum_degree());\n    return os << debug_struct.Finish();\n  }\n};\n\n}  // namespace base::internal\n}  // namespace tachyon\n\n#endif  // TACHYON_ZK_PLONK_HALO2_PINNED_CONSTRAINT_SYSTEM_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/pinned_evaluation_domain.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_HALO2_PINNED_EVALUATION_DOMAIN_H_\n#define TACHYON_ZK_PLONK_HALO2_PINNED_EVALUATION_DOMAIN_H_\n\n#include <stdint.h>\n\n#include <string>\n#include <utility>\n\n#include \"tachyon/base/strings/rust_stringifier.h\"\n#include \"tachyon/zk/base/entities/entity.h\"\n#include \"tachyon/zk/plonk/halo2/stringifiers/field_stringifier.h\"\n\nnamespace tachyon {\nnamespace zk::plonk::halo2 {\n\ntemplate <typename F>\nclass PinnedEvaluationDomain {\n public:\n  PinnedEvaluationDomain(uint32_t k, uint32_t extended_k, const F& omega)\n      : k_(k), extended_k_(extended_k), omega_(omega) {}\n  PinnedEvaluationDomain(uint32_t k, uint32_t extended_k, F&& omega)\n      : k_(k), extended_k_(extended_k), omega_(std::move(omega)) {}\n  template <typename PCS>\n  explicit PinnedEvaluationDomain(const Entity<PCS>* entity)\n      : k_(entity->domain()->log_size_of_group()),\n        extended_k_(entity->extended_domain()->log_size_of_group()),\n        omega_(entity->domain()->group_gen()) {}\n\n  uint32_t k() const { return k_; }\n  uint32_t extended_k() const { return extended_k_; }\n  const F& omega() const { return omega_; }\n\n private:\n  uint32_t k_;\n  uint32_t extended_k_;\n  F omega_;\n};\n\n}  // namespace zk::plonk::halo2\n\nnamespace base::internal {\n\ntemplate <typename F>\nclass RustDebugStringifier<zk::plonk::halo2::PinnedEvaluationDomain<F>> {\n public:\n  static std::ostream& AppendToStream(\n      std::ostream& os, RustFormatter& fmt,\n      const zk::plonk::halo2::PinnedEvaluationDomain<F>& value) {\n    return os << fmt.DebugStruct(\"PinnedEvaluationDomain\")\n                     .Field(\"k\", value.k())\n                     .Field(\"extended_k\", value.extended_k())\n                     .Field(\"omega\", value.omega())\n                     .Finish();\n  }\n};\n\n}  // namespace base::internal\n}  // namespace tachyon\n\n#endif  // TACHYON_ZK_PLONK_HALO2_PINNED_EVALUATION_DOMAIN_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/pinned_gates.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_HALO2_PINNED_GATES_H_\n#define TACHYON_ZK_PLONK_HALO2_PINNED_GATES_H_\n\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"tachyon/zk/plonk/constraint_system/gate.h\"\n#include \"tachyon/zk/plonk/halo2/stringifiers/expression_stringifier.h\"\n\nnamespace tachyon {\nnamespace zk::plonk::halo2 {\n\ntemplate <typename F>\nclass PinnedGates {\n public:\n  explicit PinnedGates(const std::vector<Gate<F>>& gates) : gates_(gates) {}\n\n  const std::vector<Gate<F>>& gates() const { return gates_; }\n\n private:\n  const std::vector<Gate<F>>& gates_;\n};\n\n}  // namespace zk::plonk::halo2\n\nnamespace base::internal {\n\ntemplate <typename F>\nclass RustDebugStringifier<zk::plonk::halo2::PinnedGates<F>> {\n public:\n  static std::ostream& AppendToStream(\n      std::ostream& os, RustFormatter& fmt,\n      const zk::plonk::halo2::PinnedGates<F>& pinned_gates) {\n    DebugList list = fmt.DebugList();\n    for (const zk::plonk::Gate<F>& gate : pinned_gates.gates()) {\n      const std::vector<std::unique_ptr<zk::Expression<F>>>& polys =\n          gate.polys();\n      for (const std::unique_ptr<zk::Expression<F>>& poly : polys) {\n        list.Entry(*poly);\n      }\n    }\n    return os << list.Finish();\n  }\n};\n\n}  // namespace base::internal\n}  // namespace tachyon\n\n#endif  // TACHYON_ZK_PLONK_HALO2_PINNED_GATES_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/pinned_verifying_key.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_HALO2_PINNED_VERIFYING_KEY_H_\n#define TACHYON_ZK_PLONK_HALO2_PINNED_VERIFYING_KEY_H_\n\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"tachyon/base/strings/rust_stringifier.h\"\n#include \"tachyon/zk/plonk/halo2/pinned_constraint_system.h\"\n#include \"tachyon/zk/plonk/halo2/pinned_evaluation_domain.h\"\n#include \"tachyon/zk/plonk/halo2/pinned_verifying_key_forward.h\"\n#include \"tachyon/zk/plonk/halo2/stringifiers/permutation_verifying_key_stringifier.h\"\n#include \"tachyon/zk/plonk/keys/verifying_key.h\"\n\nnamespace tachyon {\nnamespace zk::plonk::halo2 {\n\ntemplate <typename F, typename C>\nclass PinnedVerifyingKey {\n public:\n  using BaseField = typename C::BaseField;\n  using ScalarField = typename C::ScalarField;\n\n  template <typename PCS>\n  PinnedVerifyingKey(const Entity<PCS>* entity, const VerifyingKey<F, C>& vk)\n      : base_modulus_(BaseField::Config::kModulus.ToHexString(true)),\n        scalar_modulus_(ScalarField::Config::kModulus.ToHexString(true)),\n        domain_(entity),\n        constraint_system_(vk.constraint_system()),\n        fixed_commitments_(vk.fixed_commitments()),\n        permutation_verifying_key_(vk.permutation_verifying_key()) {}\n\n  const std::string& base_modulus() const { return base_modulus_; }\n  const std::string& scalar_modulus() const { return scalar_modulus_; }\n  const PinnedEvaluationDomain<F>& domain() const { return domain_; }\n  const PinnedConstraintSystem<F>& constraint_system() const {\n    return constraint_system_;\n  }\n  const std::vector<C>& fixed_commitments() const { return fixed_commitments_; }\n  const PermutationVerifyingKey<C>& permutation_verifying_key() const {\n    return permutation_verifying_key_;\n  }\n\n private:\n  std::string base_modulus_;\n  std::string scalar_modulus_;\n  PinnedEvaluationDomain<F> domain_;\n  PinnedConstraintSystem<F> constraint_system_;\n  const std::vector<C>& fixed_commitments_;\n  const PermutationVerifyingKey<C>& permutation_verifying_key_;\n};\n\n}  // namespace zk::plonk::halo2\n\nnamespace base::internal {\n\ntemplate <typename F, typename C>\nclass RustDebugStringifier<zk::plonk::halo2::PinnedVerifyingKey<F, C>> {\n public:\n  static std::ostream& AppendToStream(\n      std::ostream& os, RustFormatter& fmt,\n      const zk::plonk::halo2::PinnedVerifyingKey<F, C>& pinned_vk) {\n    // NOTE(chokobole): Original name is PinnedVerificationKey not\n    // PinnedVerifyingKey. See\n    // https://github.com/kroma-network/halo2/blob/7d0a369/halo2_proofs/src/plonk.rs#L252-L263.\n    return os << fmt.DebugStruct(\"PinnedVerificationKey\")\n                     .Field(\"base_modulus\", pinned_vk.base_modulus())\n                     .Field(\"scalar_modulus\", pinned_vk.scalar_modulus())\n                     .Field(\"domain\", pinned_vk.domain())\n                     .Field(\"cs\", pinned_vk.constraint_system())\n                     .Field(\"fixed_commitments\", pinned_vk.fixed_commitments())\n                     .Field(\"permutation\",\n                            pinned_vk.permutation_verifying_key())\n                     .Finish();\n  }\n};\n\n}  // namespace base::internal\n}  // namespace tachyon\n\n#endif  // TACHYON_ZK_PLONK_HALO2_PINNED_VERIFYING_KEY_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/pinned_verifying_key_forward.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_HALO2_PINNED_VERIFYING_KEY_FORWARD_H_\n#define TACHYON_ZK_PLONK_HALO2_PINNED_VERIFYING_KEY_FORWARD_H_\n\nnamespace tachyon::zk::plonk::halo2 {\n\ntemplate <typename F, typename C>\nclass PinnedVerifyingKey;\n\n}  // namespace tachyon::zk::plonk::halo2\n\n#endif  // TACHYON_ZK_PLONK_HALO2_PINNED_VERIFYING_KEY_FORWARD_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/poseidon_transcript.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_HALO2_POSEIDON_TRANSCRIPT_H_\n#define TACHYON_ZK_PLONK_HALO2_POSEIDON_TRANSCRIPT_H_\n\n#include <string.h>\n\n#include <algorithm>\n#include <array>\n#include <utility>\n#include <vector>\n\n#include \"absl/types/span.h\"\n\n#include \"tachyon/base/strings/string_util.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon/poseidon.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon/poseidon_params.h\"\n#include \"tachyon/crypto/transcripts/transcript.h\"\n#include \"tachyon/zk/plonk/halo2/prime_field_conversion.h\"\n#include \"tachyon/zk/plonk/halo2/proof_serializer.h\"\n\nnamespace tachyon::zk::plonk::halo2 {\nnamespace internal {\n\ntemplate <typename AffinePoint>\nclass PoseidonBase {\n protected:\n  using BaseField = typename AffinePoint::BaseField;\n  using ScalarField = typename AffinePoint::ScalarField;\n  using Curve = typename AffinePoint::Curve;\n  using CurveConfig = typename Curve::Config;\n  using Params = crypto::BN254PoseidonParams9;\n\n  PoseidonBase()\n      : poseidon_(\n            // See\n            // https://github.com/kroma-network/halo2/blob/7d0a369/halo2_proofs/src/transcript/poseidon.rs#L28.\n            crypto::PoseidonConfig<Params>::Create(0)) {\n    // See\n    // https://github.com/kroma-network/poseidon/blob/00a2fe0/src/spec.rs#L15.\n    state_.elements[0] = FromUint128<ScalarField>(absl::uint128(1) << 64);\n  }\n\n  ScalarField DoSqueezeChallenge() {\n    ScalarField scalar = DoSqueeze();\n    std::array<uint8_t, 32> scalar_bytes = scalar.ToBigInt().ToBytesLE();\n    uint8_t wide_scalar_bytes[64] = {0};\n    memcpy(wide_scalar_bytes, scalar_bytes.data(), 32);\n    return FromUint512<ScalarField>(wide_scalar_bytes);\n  }\n\n  bool DoWriteToTranscript(const AffinePoint& point) {\n    ScalarField coords[] = {BaseToScalar(point.x()), BaseToScalar(point.y())};\n    DoUpdate(coords, 2);\n    return true;\n  }\n\n  bool DoWriteToTranscript(const ScalarField& scalar) {\n    DoUpdate(&scalar, 1);\n    return true;\n  }\n\n  // See\n  // https://github.com/kroma-network/poseidon/blob/00a2fe0/src/poseidon.rs#L47-L69\n  ScalarField DoSqueeze() {\n    std::vector<ScalarField> last_chunk = absorbing_;\n\n    // Add the finishing sign of the variable length hashing. Note that this mut\n    // is also applied when absorbing line is empty.\n    last_chunk.push_back(ScalarField::One());\n\n    // Add the last chunk of inputs to the state for the final permutation\n    // cycle.\n    for (size_t i = 0; i < last_chunk.size(); ++i) {\n      state_[i + 1] += last_chunk[i];\n    }\n\n    // Perform final permutation.\n    poseidon_.Permute(state_);\n\n    // Flush the absorption line.\n    absorbing_.clear();\n    return state_[1];\n  }\n\n  // See\n  // https://github.com/kroma-network/poseidon/blob/00a2fe0/src/poseidon.rs#L23-L45.\n  void DoUpdate(const ScalarField* data, size_t len) {\n    std::vector<ScalarField> input_elements = absorbing_;\n    input_elements.insert(input_elements.end(), data, data + len);\n\n    size_t num_chunks =\n        (input_elements.size() + Params::kRate - 1) / Params::kRate;\n    for (size_t i = 0; i < num_chunks; ++i) {\n      size_t start = i * Params::kRate;\n      size_t chunk_len =\n          i == num_chunks - 1 ? input_elements.size() - start : Params::kRate;\n      absl::Span<const ScalarField> chunk(input_elements.data() + start,\n                                          chunk_len);\n      if (chunk_len < Params::kRate) {\n        absorbing_ = std::vector<ScalarField>(chunk.begin(), chunk.end());\n      } else {\n        // Add new chunk of inputs for the next permutation cycle.\n        for (size_t i = 0; i < Params::kRate; ++i) {\n          state_[i + 1] += chunk[i];\n        }\n\n        // Perform intermediate permutation.\n        poseidon_.Permute(state_);\n\n        // Flush the absorption line.\n        absorbing_.clear();\n      }\n    }\n  }\n\n  std::vector<uint8_t> DoGetState() const {\n    base::Uint8VectorBuffer buffer;\n    buffer.set_endian(base::Endian::kLittle);\n    CHECK(buffer.Grow(base::EstimateSize(state_, absorbing_)));\n    CHECK(buffer.WriteMany(state_, absorbing_));\n    CHECK(buffer.Done());\n    return std::move(buffer).TakeOwnedBuffer();\n  }\n\n  void DoSetState(absl::Span<const uint8_t> state) {\n    base::ReadOnlyBuffer buffer(state.data(), state.size());\n    buffer.set_endian(base::Endian::kLittle);\n    CHECK(buffer.ReadMany(&state_, &absorbing_));\n    CHECK(buffer.Done());\n  }\n\n  crypto::PoseidonSponge<Params> poseidon_;\n  std::vector<ScalarField> absorbing_;\n  crypto::SpongeState<Params> state_;\n\n private:\n  // See\n  // https://github.com/kroma-network/halo2/blob/7d0a369/halo2_proofs/src/helpers.rs#L37-L58.\n  ScalarField BaseToScalar(const BaseField& base) {\n    constexpr size_t kByteNums = BaseField::BigIntTy::kByteNums;\n\n    std::array<uint8_t, kByteNums> bytes = base.ToBigInt().ToBytesLE();\n    uint64_t buf[64] = {0};\n    memcpy(buf, bytes.data(), std::min(size_t{64}, kByteNums));\n    return FromUint512<ScalarField>(buf);\n  }\n};\n\n}  // namespace internal\n\ntemplate <typename AffinePoint>\nclass PoseidonReader : public crypto::TranscriptReader<AffinePoint>,\n                       protected internal::PoseidonBase<AffinePoint> {\n public:\n  using ScalarField = typename AffinePoint::ScalarField;\n\n  // Initialize a transcript given an input buffer.\n  explicit PoseidonReader(base::ReadOnlyBuffer buffer)\n      : crypto::TranscriptReader<AffinePoint>(std::move(buffer)) {}\n\n  // crypto::TranscriptReader methods\n  ScalarField SqueezeChallenge() override { return this->DoSqueezeChallenge(); }\n\n  bool WriteToTranscript(const AffinePoint& point) override {\n    return this->DoWriteToTranscript(point);\n  }\n\n  bool WriteToTranscript(const ScalarField& scalar) override {\n    return this->DoWriteToTranscript(scalar);\n  }\n\n private:\n  bool DoReadFromProof(AffinePoint* point) const override {\n    return ProofSerializer<AffinePoint>::ReadFromProof(this->buffer_, point);\n  }\n\n  bool DoReadFromProof(ScalarField* scalar) const override {\n    return ProofSerializer<ScalarField>::ReadFromProof(this->buffer_, scalar);\n  }\n};\n\ntemplate <typename AffinePoint>\nclass PoseidonWriter : public crypto::TranscriptWriter<AffinePoint>,\n                       protected internal::PoseidonBase<AffinePoint> {\n public:\n  using ScalarField = typename AffinePoint::ScalarField;\n  using ScalarBigInt = typename ScalarField::BigIntTy;\n\n  // Initialize a transcript given an output buffer.\n  explicit PoseidonWriter(base::Uint8VectorBuffer buffer)\n      : crypto::TranscriptWriter<AffinePoint>(std::move(buffer)) {}\n\n  // NOTE(chokobole): |GetDigestLen()|, |GetStateLen()|, |Update()|,\n  // |Squeeze()|, |GetState()| and |SetState()| are called from rust binding.\n  size_t GetDigestLen() const { return ScalarBigInt::kByteNums; }\n\n  size_t GetStateLen() const {\n    return base::EstimateSize(this->state_, this->absorbing_);\n  }\n\n  void Update(const ScalarField* data, size_t len) {\n    this->DoUpdate(data, len);\n  }\n\n  ScalarField Squeeze() { return this->DoSqueeze(); }\n\n  std::vector<uint8_t> GetState() const { return this->DoGetState(); }\n\n  void SetState(absl::Span<const uint8_t> state) { this->DoSetState(state); }\n\n  // crypto::TranscriptWriter methods\n  ScalarField SqueezeChallenge() override { return this->DoSqueezeChallenge(); }\n\n  bool WriteToTranscript(const AffinePoint& point) override {\n    return this->DoWriteToTranscript(point);\n  }\n\n  bool WriteToTranscript(const ScalarField& scalar) override {\n    return this->DoWriteToTranscript(scalar);\n  }\n\n private:\n  bool DoWriteToProof(const AffinePoint& point) override {\n    return ProofSerializer<AffinePoint>::WriteToProof(point, this->buffer_);\n  }\n\n  bool DoWriteToProof(const ScalarField& scalar) override {\n    return ProofSerializer<ScalarField>::WriteToProof(scalar, this->buffer_);\n  }\n};\n\n}  // namespace tachyon::zk::plonk::halo2\n\n#endif  // TACHYON_ZK_PLONK_HALO2_POSEIDON_TRANSCRIPT_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/poseidon_transcript_unittest.cc",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#include \"tachyon/zk/plonk/halo2/poseidon_transcript.h\"\n\n#include <utility>\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/elliptic_curves/bn/bn254/g1.h\"\n\nnamespace tachyon::zk::plonk::halo2 {\n\nnamespace {\n\nusing namespace math::bn254;\n\nclass PoseidonTranscriptTest : public testing::Test {\n public:\n  static void SetUpTestSuite() { G1Curve::Init(); }\n};\n\n}  // namespace\n\nTEST_F(PoseidonTranscriptTest, WritePoint) {\n  base::Uint8VectorBuffer write_buf;\n  PoseidonWriter<G1AffinePoint> writer(std::move(write_buf));\n  G1AffinePoint expected = G1AffinePoint::Random();\n  ASSERT_TRUE(writer.WriteToProof(expected));\n\n  base::Buffer read_buf(writer.buffer().buffer(), writer.buffer().buffer_len());\n  PoseidonReader<G1AffinePoint> reader(std::move(read_buf));\n  G1AffinePoint actual;\n  ASSERT_TRUE(reader.ReadFromProof(&actual));\n\n  EXPECT_EQ(expected, actual);\n}\n\nTEST_F(PoseidonTranscriptTest, WriteScalar) {\n  base::Uint8VectorBuffer write_buf;\n  PoseidonWriter<G1AffinePoint> writer(std::move(write_buf));\n  Fr expected = Fr::Random();\n  ASSERT_TRUE(writer.WriteToProof(expected));\n\n  base::Buffer read_buf(writer.buffer().buffer(), writer.buffer().buffer_len());\n  PoseidonReader<G1AffinePoint> reader(std::move(read_buf));\n  Fr actual;\n  ASSERT_TRUE(reader.ReadFromProof(&actual));\n\n  EXPECT_EQ(expected, actual);\n}\n\nTEST_F(PoseidonTranscriptTest, SqueezeChallenge) {\n  base::Uint8VectorBuffer write_buf;\n  PoseidonWriter<G1AffinePoint> writer(std::move(write_buf));\n  G1AffinePoint generator = G1AffinePoint::Generator();\n  ASSERT_TRUE(writer.WriteToProof(generator));\n\n  std::vector<uint8_t> expected_bytes = {25,  86,  205, 219, 59,  135, 187, 231,\n                                         192, 54,  23,  138, 114, 176, 9,   157,\n                                         1,   97,  110, 174, 67,  9,   89,  85,\n                                         126, 129, 216, 121, 53,  99,  227, 26};\n  Fr expected = Fr::FromBigInt(math::BigInt<4>::FromBytesLE(expected_bytes));\n\n  Fr actual = writer.SqueezeChallenge();\n\n  EXPECT_EQ(expected, actual);\n}\n\n}  // namespace tachyon::zk::plonk::halo2\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/prime_field_conversion.h",
    "content": "#ifndef TACHYON_ZK_PLONK_HALO2_PRIME_FIELD_CONVERSION_H_\n#define TACHYON_ZK_PLONK_HALO2_PRIME_FIELD_CONVERSION_H_\n\n#include <stdint.h>\n\n#include \"absl/numeric/int128.h\"\n\n#include \"tachyon/base/buffer/read_only_buffer.h\"\n#include \"tachyon/base/types/always_false.h\"\n#include \"tachyon/math/base/big_int.h\"\n\nnamespace tachyon::zk::plonk::halo2 {\n\n// See\n// https://github.com/kroma-network/halo2curves/blob/c0ac193/src/derive/field.rs#L301-L303.\ntemplate <typename F>\nstatic F FromUint128(absl::uint128 value) {\n  if constexpr (F::N != 4) {\n    static_assert(base::AlwaysFalse<F>);\n  }\n  uint64_t limbs[4] = {absl::Uint128Low64(value), absl::Uint128High64(value), 0,\n                       0};\n  return F(math::BigInt<4>(limbs));\n}\n\n// See\n// https://github.com/kroma-network/halo2curves/blob/c0ac193/src/derive/field.rs#L29-L47.\ntemplate <typename F>\nstatic F FromUint512(uint64_t limbs[8]) {\n  if constexpr (F::N != 4) {\n    static_assert(base::AlwaysFalse<F>);\n  }\n  F d0 = F::FromMontgomery(\n      math::BigInt<4>({limbs[0], limbs[1], limbs[2], limbs[3]}));\n  F d1 = F::FromMontgomery(\n      math::BigInt<4>({limbs[4], limbs[5], limbs[6], limbs[7]}));\n  // NOTE(chokobole): When performing d0 * F::Config::kMontgomeryR2 + d1 *\n  // F::Config::kMontgomeryR3, the result may be incorrect. This is due to our\n  // prime field multiplication, where we utilize unused modulus bits for\n  // optimization purposes. However, the given |limbs| can sometimes exceed\n  // the allowed scope of bits.\n  math::BigInt<8> mul_result = d0.value().MulExtend(F::Config::kMontgomeryR2);\n  math::BigInt<4> d2;\n  math::BigInt<4>::MontgomeryReduce64<false>(mul_result, F::Config::kModulus,\n                                             F::Config::kInverse64, &d2);\n  math::BigInt<8> mul_result2 = d1.value().MulExtend(F::Config::kMontgomeryR3);\n  math::BigInt<4> d3;\n  math::BigInt<4>::MontgomeryReduce64<false>(mul_result2, F::Config::kModulus,\n                                             F::Config::kInverse64, &d3);\n  return F::FromMontgomery(d2) + F::FromMontgomery(d3);\n}\n\ntemplate <typename F>\nstatic F FromUint512(uint8_t bytes[64]) {\n  base::ReadOnlyBuffer buffer(bytes, 64);\n  uint64_t limbs[8];\n  for (size_t i = 0; i < 8; ++i) {\n    CHECK(buffer.Read64LE(&limbs[i]));\n  }\n  return FromUint512<F>(limbs);\n}\n\n}  // namespace tachyon::zk::plonk::halo2\n\n#endif  // TACHYON_ZK_PLONK_HALO2_PRIME_FIELD_CONVERSION_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/prime_field_conversion_unittest.cc",
    "content": "#include \"tachyon/zk/plonk/halo2/prime_field_conversion.h\"\n\n#include \"tachyon/math/elliptic_curves/bn/bn254/fq.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n\nnamespace tachyon::zk::plonk::halo2 {\n\nnamespace {\n\nusing F = tachyon::math::bn254::Fq;\n\nclass PrimeFieldConversionTest : public math::FiniteFieldTest<F> {};\n\n}  // namespace\n\nTEST_F(PrimeFieldConversionTest, FromUint128) {\n  uint64_t limbs4[4] = {\n      0x0,\n      0x1,\n      0x0,\n      0x0,\n  };\n  F expected{math::BigInt<4>(limbs4)};\n\n  EXPECT_EQ(FromUint128<F>(absl::int128(1) << 64), expected);\n}\n\nTEST_F(PrimeFieldConversionTest, FromUint512) {\n  uint64_t limbs4[4] = {\n      0x1f8905a172affa8a,\n      0xde45ad177dcf3306,\n      0xaaa7987907d73ae2,\n      0x24d349431d468e30,\n  };\n  F expected{math::BigInt<4>(limbs4)};\n\n  uint64_t limbs8[8] = {0xaaaaaaaaaaaaaaaa, 0xaaaaaaaaaaaaaaaa,\n                        0xaaaaaaaaaaaaaaaa, 0xaaaaaaaaaaaaaaaa,\n                        0xaaaaaaaaaaaaaaaa, 0xaaaaaaaaaaaaaaaa,\n                        0xaaaaaaaaaaaaaaaa, 0xaaaaaaaaaaaaaaaa};\n  EXPECT_EQ(FromUint512<F>(limbs8), expected);\n\n  uint8_t bytes64[64] = {\n      0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,\n      0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,\n      0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,\n      0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,\n      0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,\n      0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa};\n  EXPECT_EQ(FromUint512<F>(bytes64), expected);\n}\n\n}  // namespace tachyon::zk::plonk::halo2\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/proof.h",
    "content": "#ifndef TACHYON_ZK_PLONK_HALO2_PROOF_H_\n#define TACHYON_ZK_PLONK_HALO2_PROOF_H_\n\n#include <optional>\n#include <string>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/json/json.h\"\n#include \"tachyon/zk/lookup/halo2/verifier_data.h\"\n#include \"tachyon/zk/lookup/log_derivative_halo2/verifier_data.h\"\n#include \"tachyon/zk/lookup/pair.h\"\n#include \"tachyon/zk/lookup/type.h\"\n#include \"tachyon/zk/plonk/permutation/permutation_verifier_data.h\"\n#include \"tachyon/zk/plonk/vanishing/vanishing_verifier_data.h\"\n#include \"tachyon/zk/shuffle/verifier_data.h\"\n\nnamespace tachyon {\nnamespace zk::plonk::halo2 {\n\ntemplate <typename F, typename C>\nstruct ProofBase {\n  std::vector<std::vector<C>> advices_commitments_vec;\n  std::vector<F> challenges;\n  F theta = F::Zero();\n  F beta = F::Zero();\n  F gamma = F::Zero();\n  std::vector<std::vector<C>> permutation_product_commitments_vec;\n  C vanishing_random_poly_commitment;\n  F y = F::Zero();\n  std::vector<C> vanishing_h_poly_commitments;\n  F x = F::Zero();\n  std::vector<std::vector<F>> instance_evals_vec;\n  std::vector<std::vector<F>> advice_evals_vec;\n  std::vector<F> fixed_evals;\n  F vanishing_random_eval = F::Zero();\n  std::vector<F> common_permutation_evals;\n  std::vector<std::vector<F>> permutation_product_evals_vec;\n  std::vector<std::vector<F>> permutation_product_next_evals_vec;\n  std::vector<std::vector<std::optional<F>>> permutation_product_last_evals_vec;\n  std::vector<std::vector<C>> shuffle_product_commitments_vec;\n  std::vector<std::vector<F>> shuffle_product_evals_vec;\n  std::vector<std::vector<F>> shuffle_product_next_evals_vec;\n\n  // auxiliary values\n  F l_first = F::Zero();\n  F l_blind = F::Zero();\n  F l_last = F::Zero();\n  F x_next = F::Zero();\n  F x_prev = F::Zero();\n  F x_last = F::Zero();\n  F x_n = F::Zero();\n\n  bool operator==(const ProofBase& other) const {\n    return advices_commitments_vec == other.advices_commitments_vec &&\n           challenges == other.challenges && theta == other.theta &&\n           beta == other.beta && gamma == other.gamma &&\n           permutation_product_commitments_vec ==\n               other.permutation_product_commitments_vec &&\n           vanishing_random_poly_commitment ==\n               other.vanishing_random_poly_commitment &&\n           y == other.y &&\n           vanishing_h_poly_commitments == other.vanishing_h_poly_commitments &&\n           x == other.x && instance_evals_vec == other.instance_evals_vec &&\n           advice_evals_vec == other.advice_evals_vec &&\n           fixed_evals == other.fixed_evals &&\n           vanishing_random_eval == other.vanishing_random_eval &&\n           common_permutation_evals == other.common_permutation_evals &&\n           permutation_product_evals_vec ==\n               other.permutation_product_evals_vec &&\n           permutation_product_next_evals_vec ==\n               other.permutation_product_next_evals_vec &&\n           permutation_product_last_evals_vec ==\n               other.permutation_product_last_evals_vec &&\n           shuffle_product_commitments_vec ==\n               other.shuffle_product_commitments_vec &&\n           shuffle_product_evals_vec == other.shuffle_product_evals_vec &&\n           shuffle_product_next_evals_vec ==\n               other.shuffle_product_next_evals_vec;\n  }\n  bool operator!=(const ProofBase& other) const { return !operator==(other); }\n\n  VanishingVerifierData<F, C> ToVanishingVerifierData(\n      size_t circuit_idx, absl::Span<const C> fixed_commitments,\n      absl::Span<const C> instance_commitments) const {\n    return {\n        fixed_commitments,\n        advices_commitments_vec[circuit_idx],\n        instance_commitments,\n        fixed_evals,\n        advice_evals_vec[circuit_idx],\n        instance_evals_vec[circuit_idx],\n        challenges,\n        vanishing_h_poly_commitments,\n        vanishing_random_poly_commitment,\n        vanishing_random_eval,\n    };\n  }\n\n  PermutationVerifierData<F, C> ToPermutationVerifierData(\n      size_t circuit_idx,\n      absl::Span<const C> common_permutation_commitments) const {\n    return {\n        fixed_evals,\n        advice_evals_vec[circuit_idx],\n        instance_evals_vec[circuit_idx],\n        challenges,\n        permutation_product_commitments_vec[circuit_idx],\n        permutation_product_evals_vec[circuit_idx],\n        permutation_product_next_evals_vec[circuit_idx],\n        permutation_product_last_evals_vec[circuit_idx],\n        common_permutation_commitments,\n        common_permutation_evals,\n        beta,\n        gamma,\n    };\n  }\n\n  shuffle::VerifierData<F, C> ToShuffleVerifierData(size_t circuit_idx) const {\n    return {\n        fixed_evals,\n        advice_evals_vec[circuit_idx],\n        instance_evals_vec[circuit_idx],\n        challenges,\n        shuffle_product_commitments_vec[circuit_idx],\n        shuffle_product_evals_vec[circuit_idx],\n        shuffle_product_next_evals_vec[circuit_idx],\n        theta,\n        gamma,\n    };\n  }\n};\n\ntemplate <typename F, typename C>\nstruct Halo2Proof : public ProofBase<F, C> {\n  std::vector<std::vector<lookup::Pair<C>>> lookup_permuted_commitments_vec;\n  std::vector<std::vector<C>> lookup_product_commitments_vec;\n  std::vector<std::vector<F>> lookup_product_evals_vec;\n  std::vector<std::vector<F>> lookup_product_next_evals_vec;\n  std::vector<std::vector<F>> lookup_permuted_input_evals_vec;\n  std::vector<std::vector<F>> lookup_permuted_input_prev_evals_vec;\n  std::vector<std::vector<F>> lookup_permuted_table_evals_vec;\n\n  bool operator==(const Halo2Proof& other) const {\n    return ProofBase<F, C>::operator==(other) &&\n           lookup_permuted_commitments_vec ==\n               other.lookup_permuted_commitments_vec &&\n           lookup_product_commitments_vec ==\n               other.lookup_product_commitments_vec &&\n           lookup_product_evals_vec == other.lookup_product_evals_vec &&\n           lookup_product_next_evals_vec ==\n               other.lookup_product_next_evals_vec &&\n           lookup_permuted_input_evals_vec ==\n               other.lookup_permuted_input_evals_vec &&\n           lookup_permuted_input_prev_evals_vec ==\n               other.lookup_permuted_input_prev_evals_vec &&\n           lookup_permuted_table_evals_vec ==\n               other.lookup_permuted_table_evals_vec;\n  }\n  bool operator!=(const Halo2Proof& other) const { return !operator==(other); }\n\n  lookup::halo2::VerifierData<F, C> ToLookupVerifierData(\n      size_t circuit_idx) const {\n    return {\n        this->fixed_evals,\n        this->advice_evals_vec[circuit_idx],\n        this->instance_evals_vec[circuit_idx],\n        this->challenges,\n        lookup_permuted_commitments_vec[circuit_idx],\n        lookup_product_commitments_vec[circuit_idx],\n        lookup_product_evals_vec[circuit_idx],\n        lookup_product_next_evals_vec[circuit_idx],\n        lookup_permuted_input_evals_vec[circuit_idx],\n        lookup_permuted_input_prev_evals_vec[circuit_idx],\n        lookup_permuted_table_evals_vec[circuit_idx],\n        this->theta,\n        this->beta,\n        this->gamma,\n    };\n  }\n};\n\ntemplate <typename F, typename C>\nstruct LogDerivativeHalo2Proof : public ProofBase<F, C> {\n  std::vector<std::vector<C>> lookup_m_poly_commitments_vec;\n  std::vector<std::vector<C>> lookup_sum_commitments_vec;\n  std::vector<std::vector<F>> lookup_sum_evals_vec;\n  std::vector<std::vector<F>> lookup_sum_next_evals_vec;\n  std::vector<std::vector<F>> lookup_m_evals_vec;\n\n  bool operator==(const LogDerivativeHalo2Proof& other) const {\n    return ProofBase<F, C>::operator==(other) &&\n           lookup_m_poly_commitments_vec ==\n               other.lookup_m_poly_commitments_vec &&\n           lookup_sum_commitments_vec == other.lookup_sum_commitments_vec &&\n           lookup_sum_evals_vec == other.lookup_sum_evals_vec &&\n           lookup_sum_next_evals_vec == other.lookup_sum_next_evals_vec &&\n           lookup_m_evals_vec == other.lookup_m_evals_vec;\n  }\n  bool operator!=(const LogDerivativeHalo2Proof& other) const {\n    return !operator==(other);\n  }\n\n  lookup::log_derivative_halo2::VerifierData<F, C> ToLookupVerifierData(\n      size_t circuit_idx) const {\n    return {\n        this->fixed_evals,\n        this->advice_evals_vec[circuit_idx],\n        this->instance_evals_vec[circuit_idx],\n        this->challenges,\n        lookup_m_poly_commitments_vec[circuit_idx],\n        lookup_sum_commitments_vec[circuit_idx],\n        lookup_sum_evals_vec[circuit_idx],\n        lookup_sum_next_evals_vec[circuit_idx],\n        lookup_m_evals_vec[circuit_idx],\n        this->theta,\n        this->beta,\n    };\n  }\n};\n\ntemplate <lookup::Type LookupType, typename F, typename C>\nusing Proof =\n    std::conditional_t<LookupType == lookup::Type::kHalo2, Halo2Proof<F, C>,\n                       LogDerivativeHalo2Proof<F, C>>;\n\n}  // namespace zk::plonk::halo2\n\nnamespace base {\n\ntemplate <typename F, typename C>\nclass RapidJsonValueConverter<zk::plonk::halo2::ProofBase<F, C>> {\n public:\n  template <typename Allocator>\n  static rapidjson::Value From(const zk::plonk::halo2::ProofBase<F, C>& value,\n                               Allocator& allocator) {\n    rapidjson::Value object(rapidjson::kObjectType);\n    AddJsonElement(object, \"advices_commitments_vec\",\n                   value.advices_commitments_vec, allocator);\n    AddJsonElement(object, \"challenges\", value.challenges, allocator);\n    AddJsonElement(object, \"theta\", value.theta, allocator);\n    AddJsonElement(object, \"beta\", value.beta, allocator);\n    AddJsonElement(object, \"gamma\", value.gamma, allocator);\n    AddJsonElement(object, \"permutation_product_commitments_vec\",\n                   value.permutation_product_commitments_vec, allocator);\n    AddJsonElement(object, \"vanishing_random_poly_commitment\",\n                   value.vanishing_random_poly_commitment, allocator);\n    AddJsonElement(object, \"y\", value.y, allocator);\n    AddJsonElement(object, \"vanishing_h_poly_commitments\",\n                   value.vanishing_h_poly_commitments, allocator);\n    AddJsonElement(object, \"x\", value.x, allocator);\n    AddJsonElement(object, \"instance_evals_vec\", value.instance_evals_vec,\n                   allocator);\n    AddJsonElement(object, \"advice_evals_vec\", value.advice_evals_vec,\n                   allocator);\n    AddJsonElement(object, \"fixed_evals\", value.fixed_evals, allocator);\n    AddJsonElement(object, \"vanishing_random_eval\", value.vanishing_random_eval,\n                   allocator);\n    AddJsonElement(object, \"common_permutation_evals\",\n                   value.common_permutation_evals, allocator);\n    AddJsonElement(object, \"permutation_product_evals_vec\",\n                   value.permutation_product_evals_vec, allocator);\n    AddJsonElement(object, \"permutation_product_next_evals_vec\",\n                   value.permutation_product_next_evals_vec, allocator);\n    AddJsonElement(object, \"permutation_product_last_evals_vec\",\n                   value.permutation_product_last_evals_vec, allocator);\n    return object;\n  }\n\n  static bool To(const rapidjson::Value& json_value, std::string_view key,\n                 zk::plonk::halo2::ProofBase<F, C>* proof_out,\n                 std::string* error) {\n    zk::plonk::halo2::ProofBase<F, C> proof;\n    if (!ParseJsonElement(json_value, \"advices_commitments_vec\",\n                          &proof.advices_commitments_vec, error))\n      return false;\n    if (!ParseJsonElement(json_value, \"challenges\", &proof.challenges, error))\n      return false;\n    if (!ParseJsonElement(json_value, \"theta\", &proof.theta, error))\n      return false;\n    if (!ParseJsonElement(json_value, \"beta\", &proof.beta, error)) return false;\n    if (!ParseJsonElement(json_value, \"gamma\", &proof.gamma, error))\n      return false;\n    if (!ParseJsonElement(json_value, \"permutation_product_commitments_vec\",\n                          &proof.permutation_product_commitments_vec, error))\n      return false;\n    if (!ParseJsonElement(json_value, \"vanishing_random_poly_commitment\",\n                          &proof.vanishing_random_poly_commitment, error))\n      return false;\n    if (!ParseJsonElement(json_value, \"y\", &proof.y, error)) return false;\n    if (!ParseJsonElement(json_value, \"vanishing_h_poly_commitments\",\n                          &proof.vanishing_h_poly_commitments, error))\n      return false;\n    if (!ParseJsonElement(json_value, \"x\", &proof.x, error)) return false;\n    if (!ParseJsonElement(json_value, \"instance_evals_vec\",\n                          &proof.instance_evals_vec, error))\n      return false;\n    if (!ParseJsonElement(json_value, \"advice_evals_vec\",\n                          &proof.advice_evals_vec, error))\n      return false;\n    if (!ParseJsonElement(json_value, \"fixed_evals\", &proof.fixed_evals, error))\n      return false;\n    if (!ParseJsonElement(json_value, \"vanishing_random_eval\",\n                          &proof.vanishing_random_eval, error))\n      return false;\n    if (!ParseJsonElement(json_value, \"common_permutation_evals\",\n                          &proof.common_permutation_evals, error))\n      return false;\n    if (!ParseJsonElement(json_value, \"permutation_product_evals_vec\",\n                          &proof.permutation_product_evals_vec, error))\n      return false;\n    if (!ParseJsonElement(json_value, \"permutation_product_next_evals_vec\",\n                          &proof.permutation_product_next_evals_vec, error))\n      return false;\n    if (!ParseJsonElement(json_value, \"common_permutation_evals\",\n                          &proof.common_permutation_evals, error))\n      return false;\n    if (!ParseJsonElement(json_value, \"permutation_product_evals_vec\",\n                          &proof.permutation_product_evals_vec, error))\n      return false;\n    if (!ParseJsonElement(json_value, \"permutation_product_next_evals_vec\",\n                          &proof.permutation_product_next_evals_vec, error))\n      return false;\n    if (!ParseJsonElement(json_value, \"permutation_product_last_evals_vec\",\n                          &proof.permutation_product_last_evals_vec, error))\n      return false;\n\n    *proof_out = std::move(proof);\n    return true;\n  }\n};\n\ntemplate <typename F, typename C>\nclass RapidJsonValueConverter<zk::plonk::halo2::Halo2Proof<F, C>> {\n public:\n  template <typename Allocator>\n  static rapidjson::Value From(const zk::plonk::halo2::Halo2Proof<F, C>& value,\n                               Allocator& allocator) {\n    rapidjson::Value object =\n        RapidJsonValueConverter<zk::plonk::halo2::ProofBase<F, C>>::From(\n            value, allocator);\n    AddJsonElement(object, \"lookup_permuted_commitments_vec\",\n                   value.lookup_permuted_commitments_vec, allocator);\n    AddJsonElement(object, \"lookup_product_commitments_vec\",\n                   value.lookup_product_commitments_vec, allocator);\n    AddJsonElement(object, \"lookup_product_evals_vec\",\n                   value.lookup_product_evals_vec, allocator);\n    AddJsonElement(object, \"lookup_product_next_evals_vec\",\n                   value.lookup_product_next_evals_vec, allocator);\n    AddJsonElement(object, \"lookup_permuted_input_evals_vec\",\n                   value.lookup_permuted_input_evals_vec, allocator);\n    AddJsonElement(object, \"lookup_permuted_input_prev_evals_vec\",\n                   value.lookup_permuted_input_prev_evals_vec, allocator);\n    AddJsonElement(object, \"lookup_permuted_table_evals_vec\",\n                   value.lookup_permuted_table_evals_vec, allocator);\n    return object;\n  }\n\n  static bool To(const rapidjson::Value& json_value, std::string_view key,\n                 zk::plonk::halo2::Halo2Proof<F, C>* proof_out,\n                 std::string* error) {\n    zk::plonk::halo2::Halo2Proof<F, C> proof;\n    if (!RapidJsonValueConverter<zk::plonk::halo2::ProofBase<F, C>>::To(\n            json_value, key, &proof, error))\n      return false;\n    if (!ParseJsonElement(json_value, \"lookup_permuted_commitments_vec\",\n                          &proof.lookup_permuted_commitments_vec, error))\n      return false;\n    if (!ParseJsonElement(json_value, \"lookup_product_commitments_vec\",\n                          &proof.lookup_product_commitments_vec, error))\n      return false;\n    if (!ParseJsonElement(json_value, \"lookup_product_evals_vec\",\n                          &proof.lookup_product_evals_vec, error))\n      return false;\n    if (!ParseJsonElement(json_value, \"lookup_product_next_evals_vec\",\n                          &proof.lookup_product_next_evals_vec, error))\n      return false;\n    if (!ParseJsonElement(json_value, \"lookup_permuted_input_evals_vec\",\n                          &proof.lookup_permuted_input_evals_vec, error))\n      return false;\n    if (!ParseJsonElement(json_value, \"lookup_permuted_input_prev_evals_vec\",\n                          &proof.lookup_permuted_input_prev_evals_vec, error))\n      return false;\n    if (!ParseJsonElement(json_value, \"lookup_permuted_table_evals_vec\",\n                          &proof.lookup_permuted_table_evals_vec, error))\n      return false;\n\n    *proof_out = std::move(proof);\n    return true;\n  }\n};\n\ntemplate <typename F, typename C>\nclass RapidJsonValueConverter<zk::plonk::halo2::LogDerivativeHalo2Proof<F, C>> {\n public:\n  template <typename Allocator>\n  static rapidjson::Value From(\n      const zk::plonk::halo2::LogDerivativeHalo2Proof<F, C>& value,\n      Allocator& allocator) {\n    rapidjson::Value object =\n        RapidJsonValueConverter<zk::plonk::halo2::ProofBase<F, C>>::From(\n            value, allocator);\n    AddJsonElement(object, \"lookup_m_poly_commitments_vec\",\n                   value.lookup_m_poly_commitments_vec, allocator);\n    AddJsonElement(object, \"lookup_sum_commitments_vec\",\n                   value.lookup_sum_commitments_vec, allocator);\n    AddJsonElement(object, \"lookup_sum_evals_vec\", value.lookup_sum_evals_vec,\n                   allocator);\n    AddJsonElement(object, \"lookup_sum_next_evals_vec\",\n                   value.lookup_sum_next_evals_vec, allocator);\n    AddJsonElement(object, \"lookup_m_evals_vec\", value.lookup_m_evals_vec,\n                   allocator);\n    return object;\n  }\n\n  static bool To(const rapidjson::Value& json_value, std::string_view key,\n                 zk::plonk::halo2::LogDerivativeHalo2Proof<F, C>* proof_out,\n                 std::string* error) {\n    zk::plonk::halo2::LogDerivativeHalo2Proof<F, C> proof;\n    if (!RapidJsonValueConverter<zk::plonk::halo2::ProofBase<F, C>>::To(\n            json_value, key, &proof, error))\n      return false;\n    if (!ParseJsonElement(json_value, \"lookup_m_poly_commitments_vec\",\n                          &proof.lookup_m_poly_commitments_vec, error))\n      return false;\n    if (!ParseJsonElement(json_value, \"lookup_sum_commitments_vec\",\n                          &proof.lookup_sum_commitments_vec, error))\n      return false;\n    if (!ParseJsonElement(json_value, \"lookup_sum_evals_vec\",\n                          &proof.lookup_sum_evals_vec, error))\n      return false;\n    if (!ParseJsonElement(json_value, \"lookup_sum_next_evals_vec\",\n                          &proof.lookup_sum_next_evals_vec, error))\n      return false;\n    if (!ParseJsonElement(json_value, \"lookup_m_evals_vec\",\n                          &proof.lookup_m_evals_vec, error))\n      return false;\n\n    *proof_out = std::move(proof);\n    return true;\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_ZK_PLONK_HALO2_PROOF_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/proof_reader.h",
    "content": "#ifndef TACHYON_ZK_PLONK_HALO2_PROOF_READER_H_\n#define TACHYON_ZK_PLONK_HALO2_PROOF_READER_H_\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/crypto/transcripts/transcript.h\"\n#include \"tachyon/zk/plonk/halo2/proof.h\"\n#include \"tachyon/zk/plonk/keys/verifying_key.h\"\n#include \"tachyon/zk/plonk/permutation/permutation_utils.h\"\n\nnamespace tachyon::zk::plonk::halo2 {\n\nenum class ProofCursor {\n  kAdviceCommitmentsVecAndChallenges,\n  kTheta,\n  kLookupPreparedCommitments,\n  kBetaAndGamma,\n  kPermutationProductCommitments,\n  kLookupGrandCommitments,\n  kShuffleGrandCommitments,\n  kVanishingRandomPolyCommitment,\n  kY,\n  kVanishingHPolyCommitments,\n  kX,\n  kInstanceEvals,\n  kAdviceEvals,\n  kFixedEvals,\n  kVanishingRandomEval,\n  kCommonPermutationEvals,\n  kPermutationEvals,\n  kLookupEvalsVec,\n  kShuffleEvalsVec,\n  kDone,\n};\n\ntemplate <lookup::Type LookupType, typename F, typename C>\nclass ProofReader {\n public:\n  using Proof = halo2::Proof<LookupType, F, C>;\n\n  ProofReader(const VerifyingKey<F, C>& verifying_key,\n              crypto::TranscriptReader<C>* transcript, size_t num_circuits)\n      : verifying_key_(verifying_key),\n        transcript_(transcript),\n        num_circuits_(num_circuits) {}\n\n  const Proof& proof() const { return proof_; }\n  Proof& proof() { return proof_; }\n\n  void ReadAdviceCommitmentsVecAndChallenges() {\n    CHECK_EQ(cursor_, ProofCursor::kAdviceCommitmentsVecAndChallenges);\n    const ConstraintSystem<F>& constraint_system =\n        verifying_key_.constraint_system();\n    proof_.advices_commitments_vec.resize(num_circuits_);\n    size_t num_advice_columns = constraint_system.num_advice_columns();\n    for (size_t i = 0; i < num_circuits_; ++i) {\n      base::ParallelizeResize(proof_.advices_commitments_vec[i],\n                              num_advice_columns, C::Zero(),\n                              math::ParallelizeThreshold::kFieldInit);\n    }\n    proof_.challenges.reserve(constraint_system.challenge_phases().size());\n    for (Phase current_phase : constraint_system.GetPhases()) {\n      for (size_t i = 0; i < num_circuits_; ++i) {\n        const std::vector<Phase>& advice_column_phases =\n            constraint_system.advice_column_phases();\n        for (size_t j = 0; j < num_advice_columns; ++j) {\n          if (current_phase == advice_column_phases[j]) {\n            proof_.advices_commitments_vec[i][j] = Read<C>();\n          }\n        }\n      }\n      for (Phase phase : constraint_system.challenge_phases()) {\n        if (current_phase == phase) {\n          F challenge = transcript_->SqueezeChallenge();\n          VLOG(2) << \"Halo2(challenge[\" << phase.value()\n                  << \"]): \" << challenge.ToHexString(true);\n          proof_.challenges.push_back(std::move(challenge));\n        }\n      }\n    }\n    cursor_ = ProofCursor::kTheta;\n  }\n\n  void ReadTheta() {\n    CHECK_EQ(cursor_, ProofCursor::kTheta);\n    proof_.theta = transcript_->SqueezeChallenge();\n    VLOG(2) << \"Halo2(theta): \" << proof_.theta.ToHexString(true);\n    cursor_ = ProofCursor::kLookupPreparedCommitments;\n  }\n\n  void ReadLookupPreparedCommitments() {\n    CHECK_EQ(cursor_, ProofCursor::kLookupPreparedCommitments);\n    size_t num_lookups = verifying_key_.constraint_system().lookups().size();\n    if constexpr (LookupType == lookup::Type::kHalo2) {\n      proof_.lookup_permuted_commitments_vec =\n          base::CreateVector(num_circuits_, [this, num_lookups]() {\n            return base::CreateVector(num_lookups, [this]() {\n              C input = Read<C>();\n              C table = Read<C>();\n              return lookup::Pair<C>(std::move(input), std::move(table));\n            });\n          });\n    } else if constexpr (LookupType == lookup::Type::kLogDerivativeHalo2) {\n      proof_.lookup_m_poly_commitments_vec = base::CreateVector(\n          num_circuits_,\n          [this, num_lookups]() { return ReadMany<C>(num_lookups); });\n    } else {\n      NOTREACHED();\n    }\n    cursor_ = ProofCursor::kBetaAndGamma;\n  }\n\n  void ReadBetaAndGamma() {\n    CHECK_EQ(cursor_, ProofCursor::kBetaAndGamma);\n    proof_.beta = transcript_->SqueezeChallenge();\n    VLOG(2) << \"Halo2(beta): \" << proof_.beta.ToHexString(true);\n    proof_.gamma = transcript_->SqueezeChallenge();\n    VLOG(2) << \"Halo2(gamma): \" << proof_.gamma.ToHexString(true);\n    cursor_ = ProofCursor::kPermutationProductCommitments;\n  }\n\n  void ReadPermutationProductCommitments() {\n    CHECK_EQ(cursor_, ProofCursor::kPermutationProductCommitments);\n    const ConstraintSystem<F>& constraint_system =\n        verifying_key_.constraint_system();\n    size_t num_products = constraint_system.ComputePermutationProductNums();\n    proof_.permutation_product_commitments_vec = base::CreateVector(\n        num_circuits_,\n        [this, num_products]() { return ReadMany<C>(num_products); });\n    cursor_ = ProofCursor::kLookupGrandCommitments;\n  }\n\n  void ReadLookupGrandCommitments() {\n    CHECK_EQ(cursor_, ProofCursor::kLookupGrandCommitments);\n    size_t num_lookups = verifying_key_.constraint_system().lookups().size();\n    if constexpr (LookupType == lookup::Type::kHalo2) {\n      proof_.lookup_product_commitments_vec = base::CreateVector(\n          num_circuits_,\n          [this, num_lookups]() { return ReadMany<C>(num_lookups); });\n    } else if constexpr (LookupType == lookup::Type::kLogDerivativeHalo2) {\n      proof_.lookup_sum_commitments_vec = base::CreateVector(\n          num_circuits_,\n          [this, num_lookups]() { return ReadMany<C>(num_lookups); });\n    } else {\n      NOTREACHED();\n    }\n\n    cursor_ = ProofCursor::kShuffleGrandCommitments;\n  }\n\n  void ReadShuffleGrandCommitments() {\n    CHECK_EQ(cursor_, ProofCursor::kShuffleGrandCommitments);\n    size_t num_shuffles = verifying_key_.constraint_system().shuffles().size();\n    proof_.shuffle_product_commitments_vec = base::CreateVector(\n        num_circuits_,\n        [this, num_shuffles]() { return ReadMany<C>(num_shuffles); });\n    cursor_ = ProofCursor::kVanishingRandomPolyCommitment;\n  }\n\n  void ReadVanishingRandomPolyCommitment() {\n    CHECK_EQ(cursor_, ProofCursor::kVanishingRandomPolyCommitment);\n    proof_.vanishing_random_poly_commitment = Read<C>();\n    cursor_ = ProofCursor::kY;\n  }\n\n  void ReadY() {\n    CHECK_EQ(cursor_, ProofCursor::kY);\n    proof_.y = transcript_->SqueezeChallenge();\n    VLOG(2) << \"Halo2(y): \" << proof_.y.ToHexString(true);\n    cursor_ = ProofCursor::kVanishingHPolyCommitments;\n  }\n\n  void ReadVanishingHPolyCommitments() {\n    CHECK_EQ(cursor_, ProofCursor::kVanishingHPolyCommitments);\n    size_t quotient_poly_degree =\n        verifying_key_.constraint_system().ComputeDegree() - 1;\n    proof_.vanishing_h_poly_commitments = ReadMany<C>(quotient_poly_degree);\n    cursor_ = ProofCursor::kX;\n  }\n\n  void ReadX() {\n    CHECK_EQ(cursor_, ProofCursor::kX);\n    proof_.x = transcript_->SqueezeChallenge();\n    VLOG(2) << \"Halo2(x): \" << proof_.x.ToHexString(true);\n    cursor_ = ProofCursor::kInstanceEvals;\n  }\n\n  void ReadInstanceEvalsIfQueryInstance() {\n    CHECK_EQ(cursor_, ProofCursor::kInstanceEvals);\n    size_t num_instance_queries =\n        verifying_key_.constraint_system().instance_queries().size();\n    proof_.instance_evals_vec =\n        base::CreateVector(num_circuits_, [this, num_instance_queries]() {\n          return ReadMany<F>(num_instance_queries);\n        });\n    cursor_ = ProofCursor::kAdviceEvals;\n  }\n\n  void ReadInstanceEvalsIfNoQueryInstance() {\n    CHECK_EQ(cursor_, ProofCursor::kInstanceEvals);\n    cursor_ = ProofCursor::kAdviceEvals;\n  }\n\n  void ReadAdviceEvals() {\n    CHECK_EQ(cursor_, ProofCursor::kAdviceEvals);\n    size_t num_advice_queries =\n        verifying_key_.constraint_system().advice_queries().size();\n    proof_.advice_evals_vec =\n        base::CreateVector(num_circuits_, [this, num_advice_queries]() {\n          return ReadMany<F>(num_advice_queries);\n        });\n    cursor_ = ProofCursor::kFixedEvals;\n  }\n\n  void ReadFixedEvals() {\n    CHECK_EQ(cursor_, ProofCursor::kFixedEvals);\n    size_t num_fixed_queries =\n        verifying_key_.constraint_system().fixed_queries().size();\n    proof_.fixed_evals = ReadMany<F>(num_fixed_queries);\n    cursor_ = ProofCursor::kVanishingRandomEval;\n  }\n\n  void ReadVanishingRandomEval() {\n    CHECK_EQ(cursor_, ProofCursor::kVanishingRandomEval);\n    proof_.vanishing_random_eval = Read<F>();\n    cursor_ = ProofCursor::kCommonPermutationEvals;\n  }\n\n  void ReadCommonPermutationEvals() {\n    CHECK_EQ(cursor_, ProofCursor::kCommonPermutationEvals);\n    proof_.common_permutation_evals = ReadMany<F>(\n        verifying_key_.permutation_verifying_key().commitments().size());\n    cursor_ = ProofCursor::kPermutationEvals;\n  }\n\n  void ReadPermutationEvals() {\n    CHECK_EQ(cursor_, ProofCursor::kPermutationEvals);\n    proof_.permutation_product_evals_vec.resize(num_circuits_);\n    proof_.permutation_product_next_evals_vec.resize(num_circuits_);\n    proof_.permutation_product_last_evals_vec.resize(num_circuits_);\n    for (size_t i = 0; i < num_circuits_; ++i) {\n      size_t size = proof_.permutation_product_commitments_vec[i].size();\n      proof_.permutation_product_evals_vec[i].reserve(size);\n      proof_.permutation_product_next_evals_vec[i].reserve(size);\n      proof_.permutation_product_last_evals_vec[i].reserve(size);\n      for (size_t j = 0; j < size; ++j) {\n        proof_.permutation_product_evals_vec[i].push_back(Read<F>());\n        proof_.permutation_product_next_evals_vec[i].push_back(Read<F>());\n        proof_.permutation_product_last_evals_vec[i].push_back(\n            (j != size - 1) ? std::optional<F>(Read<F>()) : std::optional<F>());\n      }\n    }\n    cursor_ = ProofCursor::kLookupEvalsVec;\n  }\n\n  void ReadLookupEvals() {\n    CHECK_EQ(cursor_, ProofCursor::kLookupEvalsVec);\n\n    if constexpr (LookupType == lookup::Type::kHalo2) {\n      proof_.lookup_product_evals_vec.resize(num_circuits_);\n      proof_.lookup_product_next_evals_vec.resize(num_circuits_);\n      proof_.lookup_permuted_input_evals_vec.resize(num_circuits_);\n      proof_.lookup_permuted_input_prev_evals_vec.resize(num_circuits_);\n      proof_.lookup_permuted_table_evals_vec.resize(num_circuits_);\n      for (size_t i = 0; i < num_circuits_; ++i) {\n        size_t size = proof_.lookup_product_commitments_vec[i].size();\n        proof_.lookup_product_evals_vec[i].reserve(size);\n        proof_.lookup_product_next_evals_vec[i].reserve(size);\n        proof_.lookup_permuted_input_evals_vec[i].reserve(size);\n        proof_.lookup_permuted_input_prev_evals_vec[i].reserve(size);\n        proof_.lookup_permuted_table_evals_vec[i].reserve(size);\n        for (size_t j = 0; j < size; ++j) {\n          proof_.lookup_product_evals_vec[i].push_back(Read<F>());\n          proof_.lookup_product_next_evals_vec[i].push_back(Read<F>());\n          proof_.lookup_permuted_input_evals_vec[i].push_back(Read<F>());\n          proof_.lookup_permuted_input_prev_evals_vec[i].push_back(Read<F>());\n          proof_.lookup_permuted_table_evals_vec[i].push_back(Read<F>());\n        }\n      }\n    } else if constexpr (LookupType == lookup::Type::kLogDerivativeHalo2) {\n      proof_.lookup_sum_evals_vec.resize(num_circuits_);\n      proof_.lookup_sum_next_evals_vec.resize(num_circuits_);\n      proof_.lookup_m_evals_vec.resize(num_circuits_);\n      for (size_t i = 0; i < num_circuits_; ++i) {\n        size_t size = proof_.lookup_sum_commitments_vec[i].size();\n        proof_.lookup_sum_evals_vec.reserve(size);\n        proof_.lookup_sum_next_evals_vec.reserve(size);\n        proof_.lookup_m_evals_vec.reserve(size);\n        for (size_t j = 0; j < size; ++j) {\n          proof_.lookup_sum_evals_vec[i].push_back(Read<F>());\n          proof_.lookup_sum_next_evals_vec[i].push_back(Read<F>());\n          proof_.lookup_m_evals_vec[i].push_back(Read<F>());\n        }\n      }\n    } else {\n      NOTREACHED();\n    }\n\n    cursor_ = ProofCursor::kShuffleEvalsVec;\n  }\n\n  void ReadShuffleEvals() {\n    CHECK_EQ(cursor_, ProofCursor::kShuffleEvalsVec);\n\n    proof_.shuffle_product_evals_vec.resize(num_circuits_);\n    proof_.shuffle_product_next_evals_vec.resize(num_circuits_);\n    for (size_t i = 0; i < num_circuits_; ++i) {\n      size_t size = proof_.shuffle_product_commitments_vec[i].size();\n      proof_.shuffle_product_evals_vec[i].reserve(size);\n      proof_.shuffle_product_next_evals_vec[i].reserve(size);\n      for (size_t j = 0; j < size; ++j) {\n        proof_.shuffle_product_evals_vec[i].push_back(Read<F>());\n        proof_.shuffle_product_next_evals_vec[i].push_back(Read<F>());\n      }\n    }\n\n    cursor_ = ProofCursor::kDone;\n  }\n\n  bool Done() const { return cursor_ == ProofCursor::kDone; }\n\n private:\n  template <typename T>\n  T Read() {\n    T value;\n    CHECK(transcript_->ReadFromProof(&value));\n    return value;\n  }\n\n  template <typename T>\n  std::vector<T> ReadMany(size_t n) {\n    return base::CreateVector(n, [this]() { return Read<T>(); });\n  }\n\n  const VerifyingKey<F, C>& verifying_key_;\n  // not owned\n  crypto::TranscriptReader<C>* const transcript_ = nullptr;\n  size_t num_circuits_ = 0;\n  Proof proof_;\n  ProofCursor cursor_ = ProofCursor::kAdviceCommitmentsVecAndChallenges;\n};\n\n}  // namespace tachyon::zk::plonk::halo2\n\n#endif  // TACHYON_ZK_PLONK_HALO2_PROOF_READER_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/proof_serializer.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_HALO2_PROOF_SERIALIZER_H_\n#define TACHYON_ZK_PLONK_HALO2_PROOF_SERIALIZER_H_\n\n#include <type_traits>\n#include <utility>\n\n#include \"tachyon/base/buffer/buffer.h\"\n#include \"tachyon/math/finite_fields/prime_field_base.h\"\n#include \"tachyon/math/geometry/affine_point.h\"\n\nnamespace tachyon::zk::plonk::halo2 {\n\ntemplate <typename F, typename SFINAE = void>\nclass ProofSerializer;\n\ntemplate <typename F>\nclass ProofSerializer<\n    F, std::enable_if_t<std::is_base_of_v<math::PrimeFieldBase<F>, F>>> {\n public:\n  [[nodiscard]] static bool ReadFromProof(const base::ReadOnlyBuffer& buffer,\n                                          F* scalar) {\n    return buffer.Read(scalar);\n  }\n\n  [[nodiscard]] static bool WriteToProof(const F& scalar,\n                                         base::Buffer& buffer) {\n    return buffer.Write(scalar);\n  }\n};\n\ntemplate <typename Curve>\nclass ProofSerializer<math::AffinePoint<Curve>> {\n public:\n  using BaseField = typename math::AffinePoint<Curve>::BaseField;\n  using BigInt = typename BaseField::BigIntTy;\n\n  static bool s_use_legacy_serialization;\n\n  constexpr static size_t kByteSize = BaseField::kLimbNums * sizeof(uint64_t);\n\n  [[nodiscard]] static bool CheckBits() {\n    size_t bits_needed = s_use_legacy_serialization ? 1 : 2;\n    return BaseField::kModulusBits % 8 >= bits_needed;\n  }\n\n  [[nodiscard]] static bool ReadFromProof(const base::ReadOnlyBuffer& buffer,\n                                          math::AffinePoint<Curve>* point_out) {\n    uint8_t bytes[kByteSize];\n    if (!buffer.Read(bytes)) return false;\n\n    BaseField x;\n    bool is_odd;\n    if (s_use_legacy_serialization) {\n      is_odd = bytes[kByteSize - 1] >> 7;\n      bytes[kByteSize - 1] &= 0b01111111;\n      x = BaseField::FromBigInt(BigInt::FromBytesLE(bytes));\n      if (x.IsZero()) {\n        *point_out = math::AffinePoint<Curve>::Zero();\n        return true;\n      }\n    } else {\n      bool is_inf = bytes[kByteSize - 1] >> 7;\n      is_odd = (bytes[kByteSize - 1] >> 6) & 1;\n      bytes[kByteSize - 1] &= 0b00111111;\n      x = BaseField::FromBigInt(BigInt::FromBytesLE(bytes));\n      if (x.IsZero() && is_inf) {\n        *point_out = math::AffinePoint<Curve>::Zero();\n        return true;\n      }\n    }\n\n    std::optional<math::AffinePoint<Curve>> point =\n        math::AffinePoint<Curve>::CreateFromX(x, is_odd);\n    if (!point.has_value()) return false;\n    *point_out = std::move(point).value();\n    return true;\n  }\n\n  [[nodiscard]] static bool WriteToProof(const math::AffinePoint<Curve>& point,\n                                         base::Buffer& buffer) {\n    if (point.IsZero()) {\n      uint8_t zero_bytes[kByteSize] = {0};\n      if (!s_use_legacy_serialization) {\n        zero_bytes[kByteSize - 1] |= 0b10000000;\n      }\n      return buffer.Write(zero_bytes);\n    } else {\n      size_t bits = s_use_legacy_serialization ? 7 : 6;\n      uint8_t is_odd = uint8_t{point.y().ToBigInt().IsOdd()} << bits;\n      std::array<uint8_t, kByteSize> x = point.x().ToBigInt().ToBytesLE();\n      if (!buffer.Write(x)) return false;\n      return buffer.WriteAt(buffer.buffer_offset() - 1,\n                            static_cast<uint8_t>(x[kByteSize - 1] | is_odd));\n    }\n  }\n};\n\n// static\ntemplate <typename Curve>\nbool ProofSerializer<math::AffinePoint<Curve>>::s_use_legacy_serialization =\n    true;\n\n}  // namespace tachyon::zk::plonk::halo2\n\n#endif  // TACHYON_ZK_PLONK_HALO2_PROOF_SERIALIZER_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/proof_serializer_unittest.cc",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#include \"tachyon/zk/plonk/halo2/proof_serializer.h\"\n\n#include <vector>\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/auto_reset.h\"\n#include \"tachyon/base/buffer/vector_buffer.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/g1.h\"\n\nnamespace tachyon::zk::plonk::halo2 {\n\nnamespace {\n\nusing namespace math::bn254;\n\nclass ProofSerializerTest : public testing::Test {\n public:\n  static void SetUpTestSuite() { G1Curve::Init(); }\n};\n\n}  // namespace\n\nTEST_F(ProofSerializerTest, SerializeScalar) {\n  struct {\n    std::string_view hex;\n    std::vector<uint8_t> proof;\n  } tests[] = {\n      {\"0x2482c9ce1f365ed93c2afe1df9c673b0ba65278badd4d150f3b848cdd3d0cec8\",\n       {200, 206, 208, 211, 205, 72,  184, 243, 80,  209, 212,\n        173, 139, 39,  101, 186, 176, 115, 198, 249, 29,  254,\n        42,  60,  217, 94,  54,  31,  206, 201, 130, 36}},\n  };\n\n  for (const auto& test : tests) {\n    std::vector<uint8_t> buffer;\n    buffer.resize(test.proof.size());\n    base::Buffer write_buf(buffer.data(), buffer.size());\n    Fr expected = *Fr::FromHexString(test.hex);\n    ASSERT_TRUE(ProofSerializer<Fr>::WriteToProof(expected, write_buf));\n    EXPECT_THAT(buffer, testing::ElementsAreArray(test.proof));\n\n    write_buf.set_buffer_offset(0);\n    Fr actual;\n    ASSERT_TRUE(ProofSerializer<Fr>::ReadFromProof(write_buf, &actual));\n    EXPECT_EQ(actual, expected);\n  }\n}\n\nTEST_F(ProofSerializerTest, SerializeProof) {\n  struct {\n    std::array<std::string_view, 2> hex;\n    std::vector<uint8_t> proof;\n    bool use_legacy_serialization;\n  } tests[] = {\n      // point at infinity\n      {{\n           \"0x0\",\n           \"0x0\",\n       },\n       {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},\n       true},\n      // point at infinity\n      {{\n           \"0x0\",\n           \"0x0\",\n       },\n       {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128},\n       false},\n      // even point\n      {{\n           \"0x233bd4dc42ffd123f6d041dca2117acea5f6a201b4612a81e7081cad001df470\",\n           \"0x14ecc49a7d74ee9059862ca5237c72f22dc6c39b64ec3e7c4ec314187577ee56\",\n       },\n       {112, 244, 29,  0,   173, 28,  8,   231, 129, 42,  97,\n        180, 1,   162, 246, 165, 206, 122, 17,  162, 220, 65,\n        208, 246, 35,  209, 255, 66,  220, 212, 59,  35},\n       false},\n      // odd point\n      {{\n           \"0x1ec72fa9df2846c267ad6bc77e438c0d8c0c9bba978be3095cc48b0334299dbb\",\n           \"0x2c1b5dfdca4dfc40a864355fead42fb3656a8a3304ad11b1dee1a4b924ac5a03\",\n       },\n       {187, 157, 41,  52, 3,   139, 196, 92, 9,   227, 139,\n        151, 186, 155, 12, 140, 13,  140, 67, 126, 199, 107,\n        173, 103, 194, 70, 40,  223, 169, 47, 199, 158},\n       true},\n      // odd point\n      {{\n           \"0x1ec72fa9df2846c267ad6bc77e438c0d8c0c9bba978be3095cc48b0334299dbb\",\n           \"0x2c1b5dfdca4dfc40a864355fead42fb3656a8a3304ad11b1dee1a4b924ac5a03\",\n       },\n       {187, 157, 41,  52, 3,   139, 196, 92, 9,   227, 139,\n        151, 186, 155, 12, 140, 13,  140, 67, 126, 199, 107,\n        173, 103, 194, 70, 40,  223, 169, 47, 199, 94},\n       false},\n  };\n\n  for (const auto& test : tests) {\n    base::AutoReset<bool> auto_reset(\n        &ProofSerializer<G1AffinePoint>::s_use_legacy_serialization,\n        test.use_legacy_serialization);\n    std::vector<uint8_t> buffer;\n    buffer.resize(test.proof.size());\n    base::Buffer write_buf(buffer.data(), buffer.size());\n    Fq x = *Fq::FromHexString(test.hex[0]);\n    Fq y = *Fq::FromHexString(test.hex[1]);\n    G1AffinePoint expected(x, y);\n    ASSERT_TRUE(\n        ProofSerializer<G1AffinePoint>::WriteToProof(expected, write_buf));\n    EXPECT_THAT(buffer, testing::ElementsAreArray(test.proof));\n\n    write_buf.set_buffer_offset(0);\n    G1AffinePoint actual;\n    ASSERT_TRUE(\n        ProofSerializer<G1AffinePoint>::ReadFromProof(write_buf, &actual));\n    EXPECT_EQ(actual, expected);\n  }\n}\n\n}  // namespace tachyon::zk::plonk::halo2\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/proof_unittest.cc",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#include \"tachyon/zk/plonk/halo2/proof.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/g1.h\"\n#include \"tachyon/zk/base/row_types.h\"\n\nnamespace tachyon::zk::plonk::halo2 {\nnamespace {\n\nusing F = math::bn254::Fr;\nusing Commitment = math::bn254::G1AffinePoint;\n\ntemplate <typename T>\nstd::vector<std::vector<T>> CreateRandomElementsVec(RowIndex rows,\n                                                    size_t cols) {\n  return base::CreateVector(rows, [cols]() {\n    return base::CreateVector(cols, []() { return T::Random(); });\n  });\n}\n\nstd::vector<std::vector<lookup::Pair<Commitment>>> CreateRandomLookupPairsVec(\n    RowIndex rows, size_t cols) {\n  return base::CreateVector(rows, [cols]() {\n    return base::CreateVector(cols, []() {\n      return lookup::Pair<Commitment>(Commitment::Random(),\n                                      Commitment::Random());\n    });\n  });\n}\n\n}  // namespace\n\nTEST(ProofTest, Halo2JsonValueConverter) {\n  math::bn254::G1Curve::Init();\n\n  size_t num_circuits_ = 2;\n  size_t num_elements_ = 3;\n\n  Halo2Proof<F, Commitment> expected_proof;\n  expected_proof.advices_commitments_vec =\n      CreateRandomElementsVec<Commitment>(num_circuits_, num_elements_);\n  expected_proof.challenges =\n      base::CreateVector(num_circuits_, []() { return F::Random(); });\n  expected_proof.theta = F::Random();\n  expected_proof.lookup_permuted_commitments_vec =\n      CreateRandomLookupPairsVec(num_circuits_, num_elements_);\n  expected_proof.beta = F::Random();\n  expected_proof.gamma = F::Random();\n  expected_proof.permutation_product_commitments_vec =\n      CreateRandomElementsVec<Commitment>(num_circuits_, num_elements_);\n  expected_proof.lookup_product_commitments_vec =\n      CreateRandomElementsVec<Commitment>(num_circuits_, num_elements_);\n  expected_proof.vanishing_random_poly_commitment = Commitment::Random();\n  expected_proof.y = F::Random();\n  expected_proof.vanishing_h_poly_commitments =\n      base::CreateVector(5, []() { return Commitment::Random(); });\n  expected_proof.x = F::Random();\n  expected_proof.instance_evals_vec =\n      CreateRandomElementsVec<F>(num_circuits_, num_elements_);\n  expected_proof.advice_evals_vec =\n      CreateRandomElementsVec<F>(num_circuits_, num_elements_);\n  expected_proof.fixed_evals =\n      base::CreateVector(num_circuits_, []() { return F::Random(); });\n  expected_proof.vanishing_random_eval = F::Random();\n  expected_proof.common_permutation_evals =\n      base::CreateVector(num_circuits_, []() { return F::Random(); });\n  expected_proof.permutation_product_evals_vec =\n      CreateRandomElementsVec<F>(num_circuits_, num_elements_);\n  expected_proof.permutation_product_next_evals_vec =\n      CreateRandomElementsVec<F>(num_circuits_, num_elements_);\n  expected_proof.permutation_product_last_evals_vec =\n      std::vector<std::vector<std::optional<F>>>(\n          num_circuits_, base::CreateVector(num_elements_, [](size_t i) {\n            if (i % 2 == 0) {\n              return std::optional<F>(F::Random());\n            } else {\n              return std::optional<F>();\n            }\n          }));\n  expected_proof.lookup_product_evals_vec =\n      CreateRandomElementsVec<F>(num_circuits_, num_elements_);\n  expected_proof.lookup_product_next_evals_vec =\n      CreateRandomElementsVec<F>(num_circuits_, num_elements_);\n  expected_proof.lookup_permuted_input_evals_vec =\n      CreateRandomElementsVec<F>(num_circuits_, num_elements_);\n  expected_proof.lookup_permuted_input_prev_evals_vec =\n      CreateRandomElementsVec<F>(num_circuits_, num_elements_);\n  expected_proof.lookup_permuted_table_evals_vec =\n      CreateRandomElementsVec<F>(num_circuits_, num_elements_);\n  std::string json = base::WriteToJson(expected_proof);\n\n  Halo2Proof<F, Commitment> proof;\n  std::string error;\n  ASSERT_TRUE(base::ParseJson(json, &proof, &error));\n  ASSERT_TRUE(error.empty());\n  EXPECT_EQ(proof, expected_proof);\n}\n\nTEST(ProofTest, LogDerivativeHalo2JsonValueConverter) {\n  math::bn254::G1Curve::Init();\n\n  size_t num_circuits_ = 2;\n  size_t num_elements_ = 3;\n\n  LogDerivativeHalo2Proof<F, Commitment> expected_proof;\n  expected_proof.advices_commitments_vec =\n      CreateRandomElementsVec<Commitment>(num_circuits_, num_elements_);\n  expected_proof.challenges =\n      base::CreateVector(num_circuits_, []() { return F::Random(); });\n  expected_proof.theta = F::Random();\n  expected_proof.lookup_m_poly_commitments_vec =\n      CreateRandomElementsVec<Commitment>(num_circuits_, num_elements_);\n  expected_proof.beta = F::Random();\n  expected_proof.permutation_product_commitments_vec =\n      CreateRandomElementsVec<Commitment>(num_circuits_, num_elements_);\n  expected_proof.lookup_sum_commitments_vec =\n      CreateRandomElementsVec<Commitment>(num_circuits_, num_elements_);\n  expected_proof.vanishing_random_poly_commitment = Commitment::Random();\n  expected_proof.y = F::Random();\n  expected_proof.vanishing_h_poly_commitments =\n      base::CreateVector(5, []() { return Commitment::Random(); });\n  expected_proof.x = F::Random();\n  expected_proof.instance_evals_vec =\n      CreateRandomElementsVec<F>(num_circuits_, num_elements_);\n  expected_proof.advice_evals_vec =\n      CreateRandomElementsVec<F>(num_circuits_, num_elements_);\n  expected_proof.fixed_evals =\n      base::CreateVector(num_circuits_, []() { return F::Random(); });\n  expected_proof.vanishing_random_eval = F::Random();\n  expected_proof.common_permutation_evals =\n      base::CreateVector(num_circuits_, []() { return F::Random(); });\n  expected_proof.permutation_product_evals_vec =\n      CreateRandomElementsVec<F>(num_circuits_, num_elements_);\n  expected_proof.permutation_product_next_evals_vec =\n      CreateRandomElementsVec<F>(num_circuits_, num_elements_);\n  expected_proof.permutation_product_last_evals_vec =\n      std::vector<std::vector<std::optional<F>>>(\n          num_circuits_, base::CreateVector(num_elements_, [](size_t i) {\n            if (i % 2 == 0) {\n              return std::optional<F>(F::Random());\n            } else {\n              return std::optional<F>();\n            }\n          }));\n  expected_proof.lookup_sum_evals_vec =\n      CreateRandomElementsVec<F>(num_circuits_, num_elements_);\n  expected_proof.lookup_sum_next_evals_vec =\n      CreateRandomElementsVec<F>(num_circuits_, num_elements_);\n  expected_proof.lookup_m_evals_vec =\n      CreateRandomElementsVec<F>(num_circuits_, num_elements_);\n  std::string json = base::WriteToJson(expected_proof);\n\n  LogDerivativeHalo2Proof<F, Commitment> proof;\n  std::string error;\n  ASSERT_TRUE(base::ParseJson(json, &proof, &error));\n  ASSERT_TRUE(error.empty());\n  EXPECT_EQ(proof, expected_proof);\n}\n\n}  // namespace tachyon::zk::plonk::halo2\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/prover.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_HALO2_PROVER_H_\n#define TACHYON_ZK_PLONK_HALO2_PROVER_H_\n\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/profiler.h\"\n#include \"tachyon/zk/base/entities/prover_base.h\"\n#include \"tachyon/zk/lookup/prover.h\"\n#include \"tachyon/zk/plonk/halo2/argument_data.h\"\n#include \"tachyon/zk/plonk/halo2/c_prover_impl_base_forward.h\"\n#include \"tachyon/zk/plonk/halo2/random_field_generator.h\"\n#include \"tachyon/zk/plonk/halo2/verifier.h\"\n#include \"tachyon/zk/plonk/permutation/permutation_prover.h\"\n#include \"tachyon/zk/plonk/vanishing/vanishing_prover.h\"\n#include \"tachyon/zk/shuffle/prover.h\"\n\nnamespace tachyon::zk::plonk::halo2 {\n\ntemplate <typename _PS>\nclass Prover : public ProverBase<typename _PS::PCS> {\n public:\n  constexpr static lookup::Type kLookupType = _PS::kLookupType;\n\n  using PS = _PS;\n  using PCS = typename PS::PCS;\n  using F = typename PCS::Field;\n  using Poly = typename PCS::Poly;\n  using Evals = typename PCS::Evals;\n  using Domain = typename PCS::Domain;\n  using ExtendedPoly = typename PCS::ExtendedPoly;\n  using ExtendedEvals = typename PCS::ExtendedEvals;\n  using Commitment = typename PCS::Commitment;\n  using LookupProver = lookup::Prover<kLookupType, Poly, Evals>;\n\n  static Prover Create(\n      PCS&& pcs, std::unique_ptr<crypto::TranscriptWriter<Commitment>> writer,\n      std::unique_ptr<crypto::RNG> rng, RowIndex blinding_factors) {\n    auto generator = std::make_unique<RandomFieldGenerator<F>>(rng.get());\n    Blinder<F> blinder(generator.get(), blinding_factors);\n    return {std::move(pcs), std::move(writer), std::move(blinder),\n            std::move(rng), std::move(generator)};\n  }\n\n  crypto::RNG* rng() { return rng_.get(); }\n  RandomFieldGenerator<F>* generator() { return generator_.get(); }\n\n  Verifier<PS> ToVerifier(\n      std::unique_ptr<crypto::TranscriptReader<Commitment>> reader) {\n    Verifier<PS> ret(std::move(this->pcs_), std::move(reader));\n    ret.set_domain(std::move(this->domain_));\n    ret.set_extended_domain(std::move(this->extended_domain_));\n#if TACHYON_CUDA\n    ret.set_icicle_ntt_holder(std::move(this->icicle_ntt_holder_));\n#endif\n    return ret;\n  }\n\n  template <typename Circuit>\n  void CreateProof(ProvingKey<PS>& proving_key,\n                   std::vector<std::vector<Evals>>&& instance_columns_vec,\n                   std::vector<Circuit>& circuits) {\n    TRACE_EVENT(\"ProofGeneration\", \"Plonk::Halo2::Prover::CreateProof\");\n    size_t num_circuits = circuits.size();\n\n    // Check length of instances.\n    CHECK_EQ(num_circuits, instance_columns_vec.size());\n    for (const std::vector<Evals>& instances_vec : instance_columns_vec) {\n      CHECK_EQ(instances_vec.size(), proving_key.verifying_key()\n                                         .constraint_system()\n                                         .num_instance_columns());\n    }\n\n    {\n      // Initially write hash value of verification key to transcript.\n      TRACE_EVENT(\"Subtask\", \"WriteVKHashToTranscript\");\n      crypto::TranscriptWriter<Commitment>* writer = this->GetWriter();\n      CHECK(writer->WriteToTranscript(\n          proving_key.verifying_key().transcript_repr()));\n    }\n\n    {\n      // It owns all the columns, polys and the others required in the proof\n      // generation process and provides step-by-step logics as its methods.\n      TRACE_EVENT(\"Subtask\", \"CreateArgumentAndProof\");\n      ArgumentData<Poly, Evals> argument_data =\n          ArgumentData<Poly, Evals>::Create(\n              this, circuits, proving_key.verifying_key().constraint_system(),\n              std::move(instance_columns_vec));\n      CreateProof(proving_key, &argument_data);\n    }\n  }\n\n private:\n  friend class c::zk::plonk::halo2::ProverImplBase<PS>;\n\n  Prover(PCS&& pcs,\n         std::unique_ptr<crypto::TranscriptWriter<Commitment>> writer,\n         Blinder<F>&& blinder, std::unique_ptr<crypto::RNG> rng,\n         std::unique_ptr<RandomFieldGenerator<F>> generator)\n      : ProverBase<PCS>(std::move(pcs), std::move(writer), std::move(blinder)),\n        rng_(std::move(rng)),\n        generator_(std::move(generator)) {}\n\n  void SetRng(std::unique_ptr<crypto::RNG> rng) {\n    rng_ = std::move(rng);\n    generator_ = std::make_unique<RandomFieldGenerator<F>>(rng_.get());\n    this->blinder_ =\n        Blinder<F>(generator_.get(), this->blinder_.blinding_factors());\n  }\n\n  void CreateProof(ProvingKey<PS>& proving_key,\n                   ArgumentData<Poly, Evals>* argument_data) {\n    TRACE_EVENT(\"ProofGeneration\", \"CreateProof\");\n    CHECK_EQ(proving_key.verifying_key().constraint_system().lookup_type(),\n             kLookupType);\n    // NOTE(chokobole): This is an entry point fom Halo2 rust. So this is the\n    // earliest time to log constraint system.\n    VLOG(1) << \"PCS name: \" << this->pcs_.Name() << \", k: \" << this->pcs_.K()\n            << \", n: \" << this->pcs_.N() << \", extended_k: \"\n            << proving_key.verifying_key().constraint_system().ComputeExtendedK(\n                   this->pcs_.K())\n            << \", max_degree: \" << PCS::kMaxDegree\n            << \", extended_max_degree: \" << PCS::kMaxExtendedDegree;\n    VLOG(1) << \"Halo2 Constraint System: \"\n            << proving_key.verifying_key().constraint_system().ToString();\n\n    TRACE_EVENT_BEGIN(\"Subtask\", \"Prepare\");\n    size_t num_circuits = argument_data->GetNumCircuits();\n    std::vector<LookupProver> lookup_provers(num_circuits);\n    std::vector<shuffle::Prover<Poly, Evals>> shuffle_provers(num_circuits);\n    std::vector<PermutationProver<Poly, Evals>> permutation_provers(\n        num_circuits);\n    VanishingProver<Poly, Evals, ExtendedPoly, ExtendedEvals> vanishing_prover;\n    const ConstraintSystem<F>& cs =\n        proving_key.verifying_key().constraint_system();\n    const Domain* domain = this->domain();\n    TRACE_EVENT_END(\"Subtask\");\n\n    crypto::TranscriptWriter<Commitment>* writer = this->GetWriter();\n    F theta = writer->SqueezeChallenge();\n    VLOG(2) << \"Halo2(theta): \" << theta.ToHexString(true);\n\n    std::vector<MultiPhaseRefTable<Evals>> column_tables =\n        argument_data->ExportColumnTables(proving_key.fixed_columns());\n\n    size_t commit_idx = 0;\n    if constexpr (kLookupType == lookup::Type::kHalo2) {\n      TRACE_EVENT(\"Subtask\", \"Commit permuted pairs\");\n      LookupProver::BatchCompressPairs(lookup_provers, domain, cs.lookups(),\n                                       theta, column_tables);\n      LookupProver::BatchPermutePairs(lookup_provers, this);\n\n      if constexpr (PCS::kSupportsBatchMode) {\n        this->pcs_.SetBatchMode(\n            LookupProver::GetNumPermutedPairsCommitments(lookup_provers));\n      }\n      LookupProver::BatchCommitPermutedPairs(lookup_provers, this, commit_idx);\n    } else if constexpr (kLookupType == lookup::Type::kLogDerivativeHalo2) {\n      TRACE_EVENT(\"Subtask\", \"Commit MPolys\");\n      LookupProver::BatchCompressPairs(lookup_provers, domain, cs.lookups(),\n                                       theta, column_tables);\n      LookupProver::BatchComputeMPolys(lookup_provers, this);\n\n      if constexpr (PCS::kSupportsBatchMode) {\n        this->pcs_.SetBatchMode(\n            LookupProver::GetNumMPolysCommitments(lookup_provers));\n      }\n      LookupProver::BatchCommitMPolys(lookup_provers, this, commit_idx);\n    } else {\n      NOTREACHED();\n    }\n\n    if constexpr (PCS::kSupportsBatchMode) {\n      this->RetrieveAndWriteBatchCommitmentsToProof();\n    }\n\n    F beta = writer->SqueezeChallenge();\n    VLOG(2) << \"Halo2(beta): \" << beta.ToHexString(true);\n    F gamma = writer->SqueezeChallenge();\n    VLOG(2) << \"Halo2(gamma): \" << gamma.ToHexString(true);\n\n    {\n      TRACE_EVENT(\"Subtask\", \"Create grand product/sum polys\");\n      PermutationProver<Poly, Evals>::BatchCreateGrandProductPolys(\n          permutation_provers, this, cs.permutation(), column_tables,\n          cs.ComputeDegree(), proving_key.permutation_proving_key(), beta,\n          gamma);\n\n      if constexpr (kLookupType == lookup::Type::kHalo2) {\n        LookupProver::BatchCreateGrandProductPolys(lookup_provers, this, beta,\n                                                   gamma);\n      } else if constexpr (kLookupType == lookup::Type::kLogDerivativeHalo2) {\n        LookupProver::BatchCreateGrandSumPolys(lookup_provers, this, beta);\n      } else {\n        NOTREACHED();\n      }\n\n      shuffle::Prover<Poly, Evals>::BatchCompressPairs(\n          shuffle_provers, domain, cs.shuffles(), theta, column_tables);\n      shuffle::Prover<Poly, Evals>::BatchCreateGrandProductPolys(\n          shuffle_provers, this, gamma);\n    }\n\n    vanishing_prover.CreateRandomPoly(this);\n\n    TRACE_EVENT_BEGIN(\"Subtask\", \"Commit grand product polys\");\n    if constexpr (PCS::kSupportsBatchMode) {\n      size_t num_lookup_poly;\n      if constexpr (kLookupType == lookup::Type::kHalo2) {\n        num_lookup_poly =\n            LookupProver::GetNumGrandProductPolysCommitments(lookup_provers);\n        // NOLINTNEXTLINE(readability/braces)\n      } else if constexpr (kLookupType == lookup::Type::kLogDerivativeHalo2) {\n        num_lookup_poly =\n            LookupProver::GetNumGrandSumPolysCommitments(lookup_provers);\n      } else {\n        NOTREACHED();\n      }\n      this->pcs_.SetBatchMode(\n          PermutationProver<Poly, Evals>::GetNumGrandProductPolysCommitments(\n              permutation_provers) +\n          num_lookup_poly +\n          shuffle::Prover<Poly, Evals>::GetNumGrandProductPolysCommitments(\n              shuffle_provers) +\n          VanishingProver<Poly, Evals, ExtendedPoly,\n                          ExtendedEvals>::GetNumRandomPolyCommitment());\n    }\n    commit_idx = 0;\n    PermutationProver<Poly, Evals>::BatchCommitGrandProductPolys(\n        permutation_provers, this, commit_idx);\n    if constexpr (kLookupType == lookup::Type::kHalo2) {\n      LookupProver::BatchCommitGrandProductPolys(lookup_provers, this,\n                                                 commit_idx);\n    } else if constexpr (kLookupType == lookup::Type::kLogDerivativeHalo2) {\n      LookupProver::BatchCommitGrandSumPolys(lookup_provers, this, commit_idx);\n    } else {\n      NOTREACHED();\n    }\n    shuffle::Prover<Poly, Evals>::BatchCommitGrandProductPolys(\n        shuffle_provers, this, commit_idx);\n    vanishing_prover.CommitRandomPoly(this, commit_idx);\n    if constexpr (PCS::kSupportsBatchMode) {\n      this->RetrieveAndWriteBatchCommitmentsToProof();\n    }\n    TRACE_EVENT_END(\"Subtask\");\n\n    F y = writer->SqueezeChallenge();\n    VLOG(2) << \"Halo2(y): \" << y.ToHexString(true);\n\n    {\n      TRACE_EVENT(\"Subtask\", \"Transform argument data and provers to poly\");\n      argument_data->TransformEvalsToPoly(domain);\n      PermutationProver<Poly, Evals>::TransformEvalsToPoly(permutation_provers,\n                                                           domain);\n      LookupProver::TransformEvalsToPoly(lookup_provers, domain);\n      shuffle::Prover<Poly, Evals>::TransformEvalsToPoly(shuffle_provers,\n                                                         domain);\n    }\n\n    {\n      TRACE_EVENT(\"Subtask\", \"Cleanup argument data and proving key\");\n      argument_data->DeallocateAllColumnsVec();\n      proving_key.fixed_columns().clear();\n      column_tables.clear();\n    }\n\n    std::vector<MultiPhaseRefTable<Poly>> poly_tables =\n        argument_data->ExportPolyTables(proving_key.fixed_polys());\n\n    {\n      TRACE_EVENT(\"Subtask\", \"Compute and commit final H poly\");\n\n      vanishing_prover.CreateHEvals(this, proving_key, poly_tables, theta, beta,\n                                    gamma, y, permutation_provers,\n                                    lookup_provers, shuffle_provers);\n      vanishing_prover.CreateFinalHPoly(this, cs);\n\n      if constexpr (PCS::kSupportsBatchMode) {\n        this->pcs_.SetBatchMode(\n            VanishingProver<Poly, Evals, ExtendedPoly,\n                            ExtendedEvals>::GetNumFinalHPolyCommitment(cs));\n      }\n      commit_idx = 0;\n      vanishing_prover.CommitFinalHPoly(this, cs, commit_idx);\n      if constexpr (PCS::kSupportsBatchMode) {\n        this->RetrieveAndWriteBatchCommitmentsToProof();\n      }\n    }\n\n    F x = writer->SqueezeChallenge();\n    VLOG(2) << \"Halo2(x): \" << x.ToHexString(true);\n\n    {\n      TRACE_EVENT(\"Subtask\", \"Create opening proof\");\n      F x_prev = Rotation::Prev().RotateOmega(domain, x);\n      F x_next = Rotation::Next().RotateOmega(domain, x);\n      Rotation last_rotation = Rotation(this->GetLastRow());\n      F x_last = last_rotation.RotateOmega(domain, x);\n\n      PermutationOpeningPointSet<F> permutation_opening_point_set(x, x_next,\n                                                                  x_last);\n      lookup::halo2::OpeningPointSet<F> lookup_opening_point_set(x, x_prev,\n                                                                 x_next);\n      shuffle::OpeningPointSet<F> shuffle_opening_point_set(x, x_next);\n      Evaluate(proving_key, poly_tables, vanishing_prover, permutation_provers,\n               lookup_provers, shuffle_provers, permutation_opening_point_set,\n               lookup_opening_point_set, shuffle_opening_point_set);\n\n      std::vector<crypto::PolynomialOpening<Poly>> openings =\n          Open(proving_key, poly_tables, vanishing_prover, permutation_provers,\n               lookup_provers, shuffle_provers, permutation_opening_point_set,\n               lookup_opening_point_set, shuffle_opening_point_set);\n      CHECK(this->pcs_.CreateOpeningProof(openings, this->GetWriter()));\n    }\n  }\n\n  void Evaluate(\n      const ProvingKey<PS>& proving_key,\n      const std::vector<MultiPhaseRefTable<Poly>>& poly_tables,\n      VanishingProver<Poly, Evals, ExtendedPoly, ExtendedEvals>&\n          vanishing_prover,\n      const std::vector<PermutationProver<Poly, Evals>>& permutation_provers,\n      const std::vector<LookupProver>& lookup_provers,\n      const std::vector<shuffle::Prover<Poly, Evals>>& shuffle_provers,\n      const PermutationOpeningPointSet<F>& permutation_opening_point_set,\n      const lookup::halo2::OpeningPointSet<F>& lookup_opening_point_set,\n      const shuffle::OpeningPointSet<F>& shuffle_opening_point_set) {\n    TRACE_EVENT(\"Utils\", \"Evaluate\");\n    const ConstraintSystem<F>& constraint_system =\n        proving_key.verifying_key().constraint_system();\n\n    const F& x = permutation_opening_point_set.x;\n    F x_n = x.ExpPowOfTwo(this->pcs_.K());\n    vanishing_prover.BatchEvaluate(this, constraint_system, poly_tables, x,\n                                   x_n);\n    PermutationProver<Poly, Evals>::EvaluateProvingKey(\n        this, proving_key.permutation_proving_key(),\n        permutation_opening_point_set);\n    PermutationProver<Poly, Evals>::BatchEvaluate(\n        permutation_provers, this, permutation_opening_point_set);\n    LookupProver::BatchEvaluate(lookup_provers, this, lookup_opening_point_set);\n    shuffle::Prover<Poly, Evals>::BatchEvaluate(shuffle_provers, this,\n                                                shuffle_opening_point_set);\n  }\n\n  std::vector<crypto::PolynomialOpening<Poly>> Open(\n      const ProvingKey<PS>& proving_key,\n      const std::vector<MultiPhaseRefTable<Poly>>& poly_tables,\n      const VanishingProver<Poly, Evals, ExtendedPoly, ExtendedEvals>&\n          vanishing_prover,\n      const std::vector<PermutationProver<Poly, Evals>>& permutation_provers,\n      const std::vector<LookupProver>& lookup_provers,\n      const std::vector<shuffle::Prover<Poly, Evals>>& shuffle_provers,\n      const PermutationOpeningPointSet<F>& permutation_opening_point_set,\n      const lookup::halo2::OpeningPointSet<F>& lookup_opening_point_set,\n      const shuffle::OpeningPointSet<F>& shuffle_opening_point_set) const {\n    TRACE_EVENT(\"Utils\", \"Open\");\n    const ConstraintSystem<F>& constraint_system =\n        proving_key.verifying_key().constraint_system();\n    const Domain* domain = this->domain();\n\n    std::vector<crypto::PolynomialOpening<Poly>> openings;\n    size_t num_circuits = poly_tables.size();\n    size_t size =\n        GetNumVanishingOpenings<PCS>(\n            num_circuits, constraint_system.advice_queries().size(),\n            constraint_system.instance_queries().size(),\n            constraint_system.fixed_queries().size()) +\n        GetNumPermutationOpenings(\n            num_circuits, permutation_provers[0].grand_product_polys().size(),\n            proving_key.permutation_proving_key().permutations().size()) +\n        lookup::halo2::GetNumOpenings(kLookupType, lookup_provers.size(),\n                                      constraint_system.lookups().size()) +\n        shuffle::GetNumOpenings(shuffle_provers.size(),\n                                constraint_system.shuffles().size());\n    openings.reserve(size);\n\n    const F& x = permutation_opening_point_set.x;\n    for (size_t i = 0; i < num_circuits; ++i) {\n      VanishingProver<Poly, Evals, ExtendedPoly, ExtendedEvals>::\n          template OpenAdviceInstanceColumns<PCS>(domain, constraint_system,\n                                                  poly_tables[i], x, openings);\n      permutation_provers[i].Open(permutation_opening_point_set, openings);\n      lookup_provers[i].Open(lookup_opening_point_set, openings);\n      shuffle_provers[i].Open(shuffle_opening_point_set, openings);\n    }\n    VanishingProver<Poly, Evals, ExtendedPoly, ExtendedEvals>::OpenFixedColumns(\n        domain, constraint_system, poly_tables[0], x, openings);\n    PermutationProver<Poly, Evals>::OpenPermutationProvingKey(\n        proving_key.permutation_proving_key(), permutation_opening_point_set,\n        openings);\n    vanishing_prover.Open(x, openings);\n    CHECK_EQ(openings.size(), size);\n    return openings;\n  }\n\n  std::unique_ptr<crypto::RNG> rng_;\n  std::unique_ptr<RandomFieldGenerator<F>> generator_;\n};\n\n}  // namespace tachyon::zk::plonk::halo2\n\n#endif  // TACHYON_ZK_PLONK_HALO2_PROVER_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/prover_test.h",
    "content": "#ifndef TACHYON_ZK_PLONK_HALO2_PROVER_TEST_H_\n#define TACHYON_ZK_PLONK_HALO2_PROVER_TEST_H_\n\n#include <memory>\n#include <utility>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/crypto/random/xor_shift/xor_shift_rng.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluation_domain_factory.h\"\n#include \"tachyon/zk/plonk/halo2/blake2b_transcript.h\"\n#include \"tachyon/zk/plonk/halo2/constants.h\"\n#include \"tachyon/zk/plonk/halo2/prover.h\"\n\nnamespace tachyon::zk::plonk::halo2 {\n\nconstexpr size_t kMaxDegree = (size_t{1} << 5) - 1;\nconstexpr size_t kMaxDomainSize = kMaxDegree + 1;\nconstexpr size_t kMaxExtendedDegree = (size_t{1} << 8) - 1;\nconstexpr size_t kMaxExtendedDomainSize = kMaxExtendedDegree + 1;\n\ntemplate <typename PS>\nclass ProverTest : public testing::Test {\n public:\n  using PCS = typename PS::PCS;\n  using F = typename PCS::Field;\n  using Commitment = typename PCS::Commitment;\n  using Poly = typename PCS::Poly;\n  using Evals = typename PCS::Evals;\n  using RationalEvals = typename PCS::RationalEvals;\n  using Domain = typename PCS::Domain;\n  using ExtendedDomain = typename PCS::ExtendedDomain;\n  using ExtendedEvals = typename PCS::ExtendedEvals;\n\n  void SetUp() override {\n    PCS pcs;\n    ASSERT_TRUE(pcs.UnsafeSetup(kMaxDomainSize));\n\n    base::Uint8VectorBuffer write_buf;\n    std::unique_ptr<crypto::TranscriptWriter<Commitment>> writer =\n        std::make_unique<Blake2bWriter<Commitment>>(std::move(write_buf));\n\n    auto rng = std::make_unique<crypto::XORShiftRNG>();\n    CHECK(rng->SetSeed(kXORShiftSeed));\n    prover_ = std::make_unique<Prover<PS>>(\n        Prover<PS>::Create(std::move(pcs), std::move(writer), std::move(rng),\n                           /*blinding_factors=*/0));\n    prover_->set_domain(Domain::Create(kMaxDomainSize));\n    prover_->set_extended_domain(\n        ExtendedDomain::Create(kMaxExtendedDomainSize));\n#if TACHYON_CUDA\n    prover_->EnableIcicleNTT();\n#endif\n  }\n\n protected:\n  Verifier<PS> CreateVerifier(base::Buffer read_buf) {\n    std::unique_ptr<crypto::TranscriptReader<Commitment>> reader =\n        std::make_unique<Blake2bReader<Commitment>>(std::move(read_buf));\n    return prover_->ToVerifier(std::move(reader));\n  }\n\n  std::unique_ptr<Prover<PS>> prover_;\n};\n\n}  // namespace tachyon::zk::plonk::halo2\n\n#endif  // TACHYON_ZK_PLONK_HALO2_PROVER_TEST_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/proving_scheme.h",
    "content": "#ifndef TACHYON_ZK_PLONK_HALO2_PROVING_SCHEME_H_\n#define TACHYON_ZK_PLONK_HALO2_PROVING_SCHEME_H_\n\n#include \"tachyon/zk/lookup/type.h\"\n#include \"tachyon/zk/plonk/halo2/vendor.h\"\n\nnamespace tachyon::zk::plonk::halo2 {\n\ntemplate <Vendor _Vendor, lookup::Type _LookupType, typename _PCS>\nclass ProvingScheme {\n public:\n  constexpr static Vendor kVendor = _Vendor;\n  constexpr static lookup::Type kLookupType = _LookupType;\n\n  using PCS = _PCS;\n};\n\n}  // namespace tachyon::zk::plonk::halo2\n\n#endif  // TACHYON_ZK_PLONK_HALO2_PROVING_SCHEME_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/random_field_generator.h",
    "content": "#ifndef TACHYON_ZK_PLONK_HALO2_RANDOM_FIELD_GENERATOR_H_\n#define TACHYON_ZK_PLONK_HALO2_RANDOM_FIELD_GENERATOR_H_\n\n#include \"tachyon/crypto/random/rng.h\"\n#include \"tachyon/zk/base/random_field_generator_base.h\"\n#include \"tachyon/zk/plonk/halo2/prime_field_conversion.h\"\n\nnamespace tachyon::zk::plonk::halo2 {\n\ntemplate <typename F>\nclass RandomFieldGenerator : public RandomFieldGeneratorBase<F> {\n public:\n  static_assert(F::BigIntTy::kLimbNums == 4,\n                \"Halo2Curves seems only supporting ~256 bit prime field.\");\n\n  explicit RandomFieldGenerator(crypto::RNG* generator)\n      : generator_(generator) {}\n\n  // RandomFieldGeneratorBase<F> methods\n  F Generate() override {\n    uint64_t limbs[8] = {\n        generator_->NextUint64(), generator_->NextUint64(),\n        generator_->NextUint64(), generator_->NextUint64(),\n        generator_->NextUint64(), generator_->NextUint64(),\n        generator_->NextUint64(), generator_->NextUint64(),\n    };\n    return FromUint512<F>(limbs);\n  }\n\n private:\n  // not owned\n  crypto::RNG* const generator_;\n};\n\n}  // namespace tachyon::zk::plonk::halo2\n\n#endif  // TACHYON_ZK_PLONK_HALO2_RANDOM_FIELD_GENERATOR_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/random_field_generator_unittest.cc",
    "content": "#include \"tachyon/zk/plonk/halo2/random_field_generator.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/crypto/random/xor_shift/xor_shift_rng.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/fq.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n\nnamespace tachyon::zk::plonk::halo2 {\n\nnamespace {\n\nclass RandomFieldGeneratorTest : public math::FiniteFieldTest<math::bn254::Fq> {\n public:\n  using F = tachyon::math::bn254::Fq;\n};\n\n}  // namespace\n\nTEST_F(RandomFieldGeneratorTest, Random) {\n  const char* kHexes[100] = {\n      \"0x04c0df95382d049b3f56cefbdebef7dc581a26714b42aa21726c2cfe23481d5a\",\n      \"0x08c6d7817f7a5cb6758026c475fa2b7ea7494096c1909c8c95f518d996764d12\",\n      \"0x051c8acd0aabc82eb4d2a8fdc63e3c1f5efaa3fdc77d92e4457828dde0f44f57\",\n      \"0x28e1c531c820a3fbd30315fdd199e2bf19cfa1a24ccd7be082d3cf079a602a76\",\n      \"0x2d5036b931a10dab382a642845414d9399c84c29e7db8fe8b7734c30f03368d5\",\n      \"0x16e4466b9893ed39d6be48229b5d6975bc883632f287873d82995386087c2475\",\n      \"0x057719f52e98c4a879b066c245fb5be56a73a39b5a3799b0abf8d8f1933f1191\",\n      \"0x23e9b8ad937dae0d113d2e7c307848a410d969d3116222cffd1e634fb201a363\",\n      \"0x1e46210497db66cf2b934fc182ff1b2c5f18da53af66f94632ba8949191e9b59\",\n      \"0x27d8590e96092285e77322c171ec04bc0cacb73bbe6ace60f53b48181ca1d40f\",\n      \"0x2abe4c782144af9046bf61a3b46fb5f45990438e39d0d1d8f3022aecc7096a27\",\n      \"0x1597a7d12553ec239ceb6afcc3f49e8c9ff817d129d779b1cc822e2402edd670\",\n      \"0x23e7df5dbe66aa13863387e29d22d24a629ed53c81addb37404bf0a515ce51d9\",\n      \"0x0eb29e372d5dc28817305251710020d46a594c099b2602853d0901885ee422ac\",\n      \"0x2a8ead7809cfec3724716f358778b980dc3554ae2a55bc2959ddd64fbe16d55f\",\n      \"0x1bc5c35a19739a1ebf803ee0d62e54a4a2e526d6346ddffa8d136cb4e4d57238\",\n      \"0x00955bbc5644fe6457736cf837b04f006d883026a9d58f33906bbd05ed5ca83c\",\n      \"0x042b36010b0278aac9100c60d485d093b0c5a3f6c4905421086d3d1d19d2e342\",\n      \"0x01b0fa4335106b48f8c1f97911c3dc9aa5518c26000f4d6bad49482d6b75f4b5\",\n      \"0x1a521f6df72ab428175cb044bb2096f9eddcd3eeda42de3f540a2d4922a6c90e\",\n      \"0x1dd7ea53479574deedd41f679c3745537f3611bbe0b146d163260268430c148e\",\n      \"0x15a71302f85a77665c5851b4a4cbe5bcfd405b68fe351e9d503f41f7003c360b\",\n      \"0x1dcbb36fa28e0d1da5cbcf9c49203b047a249a0fd0ec58642b4641b205e70aa0\",\n      \"0x208ace355e9a9c0db9926a0216e77bcafdcff2c36811d1c3650a842a7ba66793\",\n      \"0x10ed8d8524b4d3bd666b9fcbb2dd96d3e60c2c0e73a527123656894528ef3508\",\n      \"0x01e4ececcf2224251b23bf1c487a63486d7638560cac59175db921be841b845c\",\n      \"0x29cb73f3df698cf5ecb15645bd86d6c554d9779f3d748d62b8671cfa4c0c3b7a\",\n      \"0x1420a19bb322c3f3d9227a385661b1590537d07f8b9f2c01825e17f4c9691e75\",\n      \"0x11f073e249d5e0fc3069e041581ad2e64bac88c9896d50dfe7e4c2a7055e5905\",\n      \"0x1a53132780f543ead917b371cdae8738eb0242a7c868647f426489e74e9eada9\",\n      \"0x2653bdbf4ce9064b52ebe805b60554a297e94d428eb67c0ef44e289143f115b1\",\n      \"0x0b72a8b0fcde9f4264a898276e756aac7755651ef0d30badcf18b94880079fd6\",\n      \"0x1b01a1b8bd81bba5a0f898b269a3de7a77ebdc712814bf843f62fd57138aadcb\",\n      \"0x0f3062d2897d996ed991a47f9e6d88d23a8f324c2a558947ebc9070b56dcd23a\",\n      \"0x272368a1b275ededd904841cb256b0a5347275e3a54aa0b00c1d3b361945083a\",\n      \"0x08f9cfe12b60f0badc3eb1f64e889c6fd71d1b433017fdbdd95b4881ddbfdc88\",\n      \"0x2d78263a43e61e0e6cdeaa8b74a2b588ce3770d6f19b968e2c0c3a923e47b1ee\",\n      \"0x26860edd85d8f942d8a046095fdc1fc0ad762a5a653e0ec7480b42051d471dbf\",\n      \"0x261b42d48ee22d4a6cb0a6b07abd8ba0cbce43e430237d7522aa4d3e91636c95\",\n      \"0x1846961eeaecab911bb14c20b5384572ad05d194d875d3c8d601321cc6f7c3ae\",\n      \"0x0b85e81ae4f123e0202c0aa171f4086179b1abf04dd59c1d020e2f423f08b548\",\n      \"0x1c6a6c4381bc777b8795d6068de21ef34628723f1dac53bf01c8c5e64b5e76eb\",\n      \"0x2d3cdab29809b5a85d7e66eeb02b8511e1dbf2bab6faa85c9aff698594321c7c\",\n      \"0x04f9fa4d2c21eaeed9f4fe31b594edb1eedf34e402f843c7b9e20b1b5b7a2709\",\n      \"0x08d46c2d481bffa90e8442161be2cff717fe2b6c05e44e5a6e11cd8eec31fab7\",\n      \"0x236e2a2bae21ef371ea7adbe7bdb388f7a6a2e61bd84a7111406dad355258bc3\",\n      \"0x22dcb608834a4a53a3adce415fecf74c23fc2d946d16bb825e8ee2a1c9efe7de\",\n      \"0x01fe3570358e2e6944a35bbc0b7f55b2b20a9b52610772f18e871f6a03380b50\",\n      \"0x25417d08811b2b6e10dcde0222c0c49bcf5a431976898015a6276852d19ad361\",\n      \"0x09835b9c6d253092bf85ee4313489d3c96b1766489ed5264124a49c7ca4ceba4\",\n      \"0x06ff2920f3be9e964bf662af2bf51bb191aff6206ef912ee550bc3412ca33926\",\n      \"0x067239e7df206e993f580ac8e56307584883a13fd9b199eb142ec7dff0880254\",\n      \"0x0b49940fa89118550cc0cc5e8774ad162f143ca615509bba35b6549c4367830f\",\n      \"0x2930de6691bf0c0a97b9399116201feb9610bbc7b0faf1e7ad12c69754b742eb\",\n      \"0x1275a7aa719e716a6877539f2e860013ec55ea33c311227864956fb7a4897dcc\",\n      \"0x2f4030a22b740b6364d1ac5c6a6b88a69adf1a730b7d3ff5081d0710f9701cc8\",\n      \"0x074d89a8bc3a19211fbe3b2b60b9a5c28d53513cfb0ce7a1b645346508aef057\",\n      \"0x0f38098ffaac74b743150e137d60b642deb895088ebbef8c9398ad56971c8e94\",\n      \"0x16cc0e0b1abc680184ea8fd0cc28011f0a1374bb3a8676533acbdd610a2c888f\",\n      \"0x297e264e5624095a4503ad9d556dafee1d04b2366813093c1e9e0e959d2cbdcb\",\n      \"0x182fdb9fc272ecaf6ae4893d59b2c64fa2c9904ba917259bfe3448e6f937781c\",\n      \"0x11bcb82c4be4f8b16f3c13163307e71a893e2240636680ef27d1e1d5089ed228\",\n      \"0x08a017fca03c7a2882b31a6b28b87f47ef12904faddaf8fd3d6981e77bc834b9\",\n      \"0x1e550895743a5a9c28d2c8c713710a570843f4823af815e9535d2f40d48d90d9\",\n      \"0x1d007c6eb2f3ced2ec0ff19431b11d0a5b2ceb90ee2f2376b80eaf832113508e\",\n      \"0x18db52504caa6c543e32122da8bb44a63d4c1b412a8bb66193e0299d7e4bde6e\",\n      \"0x1c25d411573b9d0e638faf27a6818fae08586dd788d6ba741a12428c11a1f63c\",\n      \"0x1074c2b043d4569c6934889a2499a70f8ae33a2154df203acee322a3ac179ed9\",\n      \"0x0639d90550920397f0aa9a7236f92f5acf35877022138f8b6c41d492480aba8a\",\n      \"0x1438e75d34d08ebfbb4488f83e7392cca504cfbdd3c9a098c62ba48ca75fb8bc\",\n      \"0x209c6837a41441e98094fdd2cf102320237bc071c7ec96e58deb52d8fda0a03a\",\n      \"0x00d654fcdebb22be811e42504cedb78d1346e72aadde501c9e5411c93639436b\",\n      \"0x1e2192ab1607dc8fd0f250496d38bc2cc1297af9625a28685be017f9cc6cf583\",\n      \"0x2406995ea444a2abeae77aef98bf4b304036419c73209d073baccf67b12de705\",\n      \"0x23cc42bbd88a4bf15859afbaa6f47f81e6d2ffaa4dc683e252877636fb5819fa\",\n      \"0x259ea37ca43f2d367cdb213ad999080d1be0d2312347279d4466c3b6dc8c52c7\",\n      \"0x1270b3dfeff7c7041872ab640b836ba59055262d4c7b41fd31f2cac0483848ba\",\n      \"0x04e5d81ac6519cc3a73c17eb0cd2471db8c910011d8fd090476f7f065a1a22d1\",\n      \"0x18f99bcf3b3ebe30d043a3323d7ae9d92a3642c2dc2ddd1b04b437c92a989074\",\n      \"0x29d30e669dc42608a50d58c3909e7de6c123849f1ee89203aaeeb1013775b68b\",\n      \"0x0ed793fd47fbcc85bd3beb65ecb2a47dd197437b6eb892728fbe0035f006fdc0\",\n      \"0x1bc9e7393b108034e339bd1c54de786b6f4250fd5186a7c1f9950f89bb7a70c1\",\n      \"0x00ed226122b847cfa97deab27fcf5bfa952556884277e4cd6bf1c0f4f5f0abdd\",\n      \"0x0f2acf93f165dcf49e7f1c3aa6213659e4ec5fce125a20e8cc7a2d148e773e0d\",\n      \"0x1730f609d6c5172884db17ac3c132220f82e27585894f118f471eecb7d4eb704\",\n      \"0x2e92d081f869a21c16559ab6ef4fd6729fd45a6aef77460cbc043455fba0ff2c\",\n      \"0x23ddf85c1c485b84d5f7dc972911a660ef0821e4261dd0e1865b6d05e0bd21ad\",\n      \"0x051dfd20c837b49ba68ff57b864db715b881e822018cd41ab877443101b03889\",\n      \"0x12667b3c4c3c12c693a932330075c7ef1c08e66c8440b991c4bd6deafcefe275\",\n      \"0x1161e8a2a44e7f42df4617df8b1197f0baad597e46a833c34351b00844538283\",\n      \"0x02c2bdf17784ad108566daaa49b0f4a0e83a6541358dab3c29cdaa5853759cf6\",\n      \"0x10ece8b3080ae081896314d52622b5a8812d535dd0aab61e51e8ab0d4ceade8d\",\n      \"0x0535a539ab7f6bda53e9f053d3b621f2e714e74d8b96e2a6d49042be973393e5\",\n      \"0x174393c2923b4cba2855fca4a6ba99adcc168a4e8ab2bd0000e1d88ba82040e7\",\n      \"0x024dcba6ecbb810cf03a0979a9200ea8c2979e0f4e1905701d8f4a6c931ecd28\",\n      \"0x0fe54c45d80610c9c5bed17daf58612eb3a3f02ec5d79c3ab2af094bee8ad2b1\",\n      \"0x08b9b0d1543647d7bb4491fd119440b9cd7eebcf237f38471eb2ccfe941063d3\",\n      \"0x035cf5267e698abcabcfd582ed43d982ecc31a4abaad018541aceeb0001db968\",\n      \"0x0046a64362dc3f318d709d0d4059b1696da8fffe483f81c34383e1e092f370b0\",\n      \"0x30606e181a9ba2b56a662dff4543ed30da6aad5fa1c41126dd287c95734ea0af\",\n  };\n\n  uint8_t seed[crypto::XORShiftRNG::kSeedSize] = {\n      0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d,\n      0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5};\n  crypto::XORShiftRNG rng;\n  ASSERT_TRUE(rng.SetSeed(seed));\n  RandomFieldGenerator<F> generator(&rng);\n  for (size_t i = 0; i < 100; ++i) {\n    EXPECT_EQ(generator.Generate(), *F::FromHexString(kHexes[i]));\n  }\n}\n\n}  // namespace tachyon::zk::plonk::halo2\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/sha256_transcript.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_HALO2_SHA256_TRANSCRIPT_H_\n#define TACHYON_ZK_PLONK_HALO2_SHA256_TRANSCRIPT_H_\n\n#include <utility>\n#include <vector>\n\n#include \"openssl/sha.h\"\n\n#include \"tachyon/crypto/transcripts/transcript.h\"\n#include \"tachyon/zk/plonk/halo2/constants.h\"\n#include \"tachyon/zk/plonk/halo2/prime_field_conversion.h\"\n#include \"tachyon/zk/plonk/halo2/proof_serializer.h\"\n\nnamespace tachyon::zk::plonk::halo2 {\nnamespace internal {\n\ntemplate <typename AffinePoint>\nclass Sha256Base {\n protected:\n  using BaseField = typename AffinePoint::BaseField;\n  using ScalarField = typename AffinePoint::ScalarField;\n\n  Sha256Base() { SHA256_Init(&state_); }\n\n  ScalarField DoSqueezeChallenge() {\n    DoUpdate(kShaPrefixChallenge, 1);\n    uint8_t result[SHA256_DIGEST_LENGTH] = {0};\n    DoFinalize(result);\n\n    SHA256_Init(&state_);\n    DoUpdate(result, SHA256_DIGEST_LENGTH);\n\n    uint8_t expanded_result[SHA256_DIGEST_LENGTH * 2] = {0};\n    memcpy(expanded_result, result, SHA256_DIGEST_LENGTH);\n    return FromUint512<ScalarField>(expanded_result);\n  }\n\n  bool DoWriteToTranscript(const AffinePoint& point) {\n    DoUpdate(kShaPrefixZeros, SHA256_DIGEST_LENGTH - 1);\n    DoUpdate(kShaPrefixPoint, 1);\n    DoUpdate(point.x().ToBigInt().ToBytesBE().data(),\n             BaseField::BigIntTy::kByteNums);\n    DoUpdate(point.y().ToBigInt().ToBytesBE().data(),\n             BaseField::BigIntTy::kByteNums);\n    return true;\n  }\n\n  bool DoWriteToTranscript(const ScalarField& scalar) {\n    DoUpdate(kShaPrefixZeros, SHA256_DIGEST_LENGTH - 1);\n    DoUpdate(kShaPrefixScalar, 1);\n    DoUpdate(scalar.ToBigInt().ToBytesBE().data(),\n             ScalarField::BigIntTy::kByteNums);\n    return true;\n  }\n\n  void DoUpdate(const void* data, size_t len) {\n    SHA256_Update(&state_, data, len);\n  }\n\n  void DoFinalize(uint8_t result[SHA256_DIGEST_LENGTH]) {\n    SHA256_CTX hasher = state_;\n    SHA256_Final(result, &hasher);\n  }\n\n  std::vector<uint8_t> DoGetState() const {\n    const sha256_state_st* state_impl =\n        reinterpret_cast<const sha256_state_st*>(&state_);\n    base::Uint8VectorBuffer buffer;\n    buffer.set_endian(base::Endian::kLittle);\n    CHECK(buffer.Grow(sizeof(sha256_state_st)));\n    CHECK(buffer.WriteMany(state_impl->h, state_impl->Nl, state_impl->Nh,\n                           state_impl->data, state_impl->num,\n                           state_impl->md_len));\n    CHECK(buffer.Done());\n    return std::move(buffer).TakeOwnedBuffer();\n  }\n\n  void DoSetState(absl::Span<const uint8_t> state) {\n    base::ReadOnlyBuffer buffer(state.data(), state.size());\n    buffer.set_endian(base::Endian::kLittle);\n    sha256_state_st* state_impl = reinterpret_cast<sha256_state_st*>(&state_);\n    CHECK(buffer.ReadMany(state_impl->h, &state_impl->Nl, &state_impl->Nh,\n                          state_impl->data, &state_impl->num,\n                          &state_impl->md_len));\n    CHECK(buffer.Done());\n  }\n\n  SHA256_CTX state_;\n};\n\n}  // namespace internal\n\ntemplate <typename AffinePoint>\nclass Sha256Reader : public crypto::TranscriptReader<AffinePoint>,\n                     protected internal::Sha256Base<AffinePoint> {\n public:\n  using ScalarField = typename AffinePoint::ScalarField;\n\n  // Initialize a transcript given an input buffer.\n  explicit Sha256Reader(base::ReadOnlyBuffer read_buf)\n      : crypto::TranscriptReader<AffinePoint>(std::move(read_buf)) {}\n\n  // crypto::TranscriptReader methods\n  ScalarField SqueezeChallenge() override { return this->DoSqueezeChallenge(); }\n\n  bool WriteToTranscript(const AffinePoint& point) override {\n    return this->DoWriteToTranscript(point);\n  }\n\n  bool WriteToTranscript(const ScalarField& scalar) override {\n    return this->DoWriteToTranscript(scalar);\n  }\n\n private:\n  bool DoReadFromProof(AffinePoint* point) const override {\n    using BaseField = typename AffinePoint::BaseField;\n\n    // See\n    // https://github.com/kroma-network/halo2-snark-aggregator/blob/2637b51/halo2-snark-aggregator-api/src/transcript/sha.rs#L43-L61.\n    math::BigInt<4> x;\n    math::BigInt<4> y;\n    if (!this->buffer_.ReadMany(&x, &y)) return false;\n    *point = {BaseField(std::move(x)), BaseField(std::move(y))};\n    return true;\n  }\n\n  bool DoReadFromProof(ScalarField* scalar) const override {\n    return ProofSerializer<ScalarField>::ReadFromProof(this->buffer_, scalar);\n  }\n};\n\ntemplate <typename AffinePoint>\nclass Sha256Writer : public crypto::TranscriptWriter<AffinePoint>,\n                     protected internal::Sha256Base<AffinePoint> {\n public:\n  using ScalarField = typename AffinePoint::ScalarField;\n\n  // Initialize a transcript given an output buffer.\n  explicit Sha256Writer(base::Uint8VectorBuffer write_buf)\n      : crypto::TranscriptWriter<AffinePoint>(std::move(write_buf)) {\n    SHA256_Init(&state_);\n  }\n\n  // NOTE(chokobole): |GetDigestLen()|, |GetStateLen()|, |Update()|,\n  // |Finalize()|, |GetState()| and |SetState()| are called from rust binding.\n  size_t GetDigestLen() const { return SHA256_DIGEST_LENGTH; }\n\n  size_t GetStateLen() const { return sizeof(sha256_state_st); }\n\n  void Update(const void* data, size_t len) { this->DoUpdate(data, len); }\n\n  void Finalize(uint8_t result[SHA256_DIGEST_LENGTH]) {\n    this->DoFinalize(result);\n  }\n\n  std::vector<uint8_t> GetState() const { return this->DoGetState(); }\n\n  void SetState(absl::Span<const uint8_t> state) { this->DoSetState(state); }\n\n  // crypto::TranscriptWriter methods\n  ScalarField SqueezeChallenge() override { return this->DoSqueezeChallenge(); }\n\n  bool WriteToTranscript(const AffinePoint& point) override {\n    return this->DoWriteToTranscript(point);\n  }\n\n  bool WriteToTranscript(const ScalarField& scalar) override {\n    return this->DoWriteToTranscript(scalar);\n  }\n\n private:\n  bool DoWriteToProof(const AffinePoint& point) override {\n    // See\n    // https://github.com/kroma-network/halo2-snark-aggregator/blob/2637b51/halo2-snark-aggregator-api/src/transcript/sha.rs#L156-L173.\n    math::BigInt<4> x = point.x().ToBigInt();\n    math::BigInt<4> y = point.y().ToBigInt();\n    return this->buffer_.WriteMany(x, y);\n  }\n\n  bool DoWriteToProof(const ScalarField& scalar) override {\n    return ProofSerializer<ScalarField>::WriteToProof(scalar, this->buffer_);\n  }\n\n  SHA256_CTX state_;\n};\n\n}  // namespace tachyon::zk::plonk::halo2\n\n#endif  // TACHYON_ZK_PLONK_HALO2_SHA256_TRANSCRIPT_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/sha256_transcript_unittest.cc",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#include \"tachyon/zk/plonk/halo2/sha256_transcript.h\"\n\n#include <utility>\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/elliptic_curves/bn/bn254/g1.h\"\n\nnamespace tachyon::zk::plonk::halo2 {\n\nnamespace {\n\nusing namespace math::bn254;\n\nclass Sha256TranscriptTest : public testing::Test {\n public:\n  static void SetUpTestSuite() { G1Curve::Init(); }\n};\n\n}  // namespace\n\nTEST_F(Sha256TranscriptTest, WritePoint) {\n  base::Uint8VectorBuffer write_buf;\n  Sha256Writer<G1AffinePoint> writer(std::move(write_buf));\n  G1AffinePoint expected = G1AffinePoint::Random();\n  ASSERT_TRUE(writer.WriteToProof(expected));\n\n  base::Buffer read_buf(writer.buffer().buffer(), writer.buffer().buffer_len());\n  Sha256Reader<G1AffinePoint> reader(std::move(read_buf));\n  G1AffinePoint actual;\n  ASSERT_TRUE(reader.ReadFromProof(&actual));\n\n  EXPECT_EQ(expected, actual);\n}\n\nTEST_F(Sha256TranscriptTest, WriteScalar) {\n  base::Uint8VectorBuffer write_buf;\n  Sha256Writer<G1AffinePoint> writer(std::move(write_buf));\n  Fr expected = Fr::Random();\n  ASSERT_TRUE(writer.WriteToProof(expected));\n\n  base::Buffer read_buf(writer.buffer().buffer(), writer.buffer().buffer_len());\n  Sha256Reader<G1AffinePoint> reader(std::move(read_buf));\n  Fr actual;\n  ASSERT_TRUE(reader.ReadFromProof(&actual));\n\n  EXPECT_EQ(expected, actual);\n}\n\nTEST_F(Sha256TranscriptTest, SqueezeChallenge) {\n  base::Uint8VectorBuffer write_buf;\n  Sha256Writer<G1AffinePoint> writer(std::move(write_buf));\n  G1AffinePoint generator = G1AffinePoint::Generator();\n  ASSERT_TRUE(writer.WriteToProof(generator));\n\n  std::vector<uint8_t> expected_bytes = {144, 70,  170, 43,  125, 191, 116, 100,\n                                         115, 242, 37,  247, 43,  227, 23,  192,\n                                         153, 176, 105, 131, 142, 165, 91,  3,\n                                         218, 85,  31,  89,  176, 94,  171, 5};\n  Fr expected = Fr::FromBigInt(math::BigInt<4>::FromBytesLE(expected_bytes));\n\n  Fr actual = writer.SqueezeChallenge();\n\n  EXPECT_EQ(expected, actual);\n}\n\n}  // namespace tachyon::zk::plonk::halo2\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/snark_verifier_poseidon_transcript.h",
    "content": "#ifndef TACHYON_ZK_PLONK_HALO2_SNARK_VERIFIER_POSEIDON_TRANSCRIPT_H_\n#define TACHYON_ZK_PLONK_HALO2_SNARK_VERIFIER_POSEIDON_TRANSCRIPT_H_\n\n#include <string.h>\n\n#include <algorithm>\n#include <array>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/crypto/hashes/sponge/poseidon/poseidon.h\"\n#include \"tachyon/crypto/hashes/sponge/poseidon/poseidon_params.h\"\n#include \"tachyon/crypto/transcripts/transcript.h\"\n#include \"tachyon/zk/plonk/halo2/prime_field_conversion.h\"\n#include \"tachyon/zk/plonk/halo2/proof_serializer.h\"\n\nnamespace tachyon::zk::plonk::halo2 {\nnamespace internal {\n\ntemplate <typename AffinePoint>\nclass SnarkVerifierPoseidonBase {\n protected:\n  using BaseField = typename AffinePoint::BaseField;\n  using ScalarField = typename AffinePoint::ScalarField;\n  using Curve = typename AffinePoint::Curve;\n  using CurveConfig = typename Curve::Config;\n  using Params = crypto::BN254PoseidonParams5;\n\n  SnarkVerifierPoseidonBase()\n      : poseidon_(\n            // See\n            // https://github.com/scroll-tech/snark-verifier/blob/58c46b7/snark-verifier-sdk/src/param.rs#L7-L10.\n            crypto::PoseidonConfig<Params>::Create(0)) {\n    // See\n    // https://github.com/scroll-tech/snark-verifier/blob/58c46b7/snark-verifier/src/util/hash/poseidon.rs#L28-L31.\n    state_.elements[0] = FromUint128<ScalarField>(absl::uint128(1) << 64);\n  }\n\n  ScalarField DoSqueezeChallenge() {\n    ScalarField scalar = DoSqueeze();\n    std::array<uint8_t, 32> scalar_bytes = scalar.ToBigInt().ToBytesLE();\n    uint8_t wide_scalar_bytes[64] = {0};\n    memcpy(wide_scalar_bytes, scalar_bytes.data(), 32);\n    return FromUint512<ScalarField>(wide_scalar_bytes);\n  }\n\n  bool DoWriteToTranscript(const AffinePoint& point) {\n    ScalarField coords[] = {BaseToScalar(point.x()), BaseToScalar(point.y())};\n    DoUpdate(coords, 2);\n    return true;\n  }\n\n  bool DoWriteToTranscript(const ScalarField& scalar) {\n    DoUpdate(&scalar, 1);\n    return true;\n  }\n\n  ScalarField DoSqueeze() {\n    size_t num_chunks = (buf_.size() + Params::kRate - 1) / Params::kRate;\n\n    for (size_t i = 0; i < num_chunks; ++i) {\n      size_t start = i * Params::kRate;\n      size_t len = std::min(start + Params::kRate, buf_.size()) - start;\n      // See\n      // https://github.com/scroll-tech/snark-verifier/blob/58c46b7/snark-verifier/src/util/hash/poseidon.rs#L57-L61.\n      for (size_t j = 0; j < len; ++j) {\n        state_[j + 1] += buf_[start + j];\n      }\n      // See\n      // https://github.com/scroll-tech/snark-verifier/blob/58c46b7/snark-verifier/src/util/hash/poseidon.rs#L70-L72.\n      if (len + 1 < state_.size()) {\n        state_[len + 1] += ScalarField::One();\n      }\n      poseidon_.Permute(state_);\n    }\n\n    if (buf_.size() == num_chunks * Params::kRate) {\n      // See\n      // https://github.com/scroll-tech/snark-verifier/blob/58c46b7/snark-verifier/src/util/hash/poseidon.rs#L70-L72.\n      state_[1] += ScalarField::One();\n      poseidon_.Permute(state_);\n    }\n\n    buf_.clear();\n    return state_[1];\n  }\n\n  void DoUpdate(const ScalarField* data, size_t len) {\n    buf_.reserve(buf_.size() + len);\n    for (size_t i = 0; i < len; ++i) {\n      buf_.push_back(data[i]);\n    }\n  }\n\n  std::vector<uint8_t> DoGetState() const {\n    base::Uint8VectorBuffer buffer;\n    buffer.set_endian(base::Endian::kLittle);\n    CHECK(buffer.Grow(base::EstimateSize(buf_, state_)));\n    CHECK(buffer.WriteMany(buf_, state_));\n    CHECK(buffer.Done());\n    return std::move(buffer).TakeOwnedBuffer();\n  }\n\n  void DoSetState(absl::Span<const uint8_t> state) {\n    base::ReadOnlyBuffer buffer(state.data(), state.size());\n    buffer.set_endian(base::Endian::kLittle);\n    CHECK(buffer.ReadMany(&buf_, &state_));\n    CHECK(buffer.Done());\n  }\n\n  crypto::PoseidonSponge<Params> poseidon_;\n  std::vector<ScalarField> buf_;\n  crypto::SpongeState<Params> state_;\n\n private:\n  // See\n  // https://github.com/kroma-network/halo2/blob/7d0a369/halo2_proofs/src/helpers.rs#L37-L58.\n  ScalarField BaseToScalar(const BaseField& base) {\n    constexpr size_t kByteNums = BaseField::BigIntTy::kByteNums;\n\n    std::array<uint8_t, kByteNums> bytes = base.ToBigInt().ToBytesLE();\n    uint64_t buf[64] = {0};\n    memcpy(buf, bytes.data(), std::min(size_t{64}, kByteNums));\n    return FromUint512<ScalarField>(buf);\n  }\n};\n\n}  // namespace internal\n\ntemplate <typename AffinePoint>\nclass SnarkVerifierPoseidonReader\n    : public crypto::TranscriptReader<AffinePoint>,\n      protected internal::SnarkVerifierPoseidonBase<AffinePoint> {\n public:\n  using ScalarField = typename AffinePoint::ScalarField;\n\n  // Initialize a transcript given an input buffer.\n  explicit SnarkVerifierPoseidonReader(base::ReadOnlyBuffer buffer)\n      : crypto::TranscriptReader<AffinePoint>(std::move(buffer)) {}\n\n  // crypto::TranscriptReader methods\n  ScalarField SqueezeChallenge() override { return this->DoSqueezeChallenge(); }\n\n  bool WriteToTranscript(const AffinePoint& point) override {\n    return this->DoWriteToTranscript(point);\n  }\n\n  bool WriteToTranscript(const ScalarField& scalar) override {\n    return this->DoWriteToTranscript(scalar);\n  }\n\n private:\n  bool DoReadFromProof(AffinePoint* point) const override {\n    return ProofSerializer<AffinePoint>::ReadFromProof(this->buffer_, point);\n  }\n\n  bool DoReadFromProof(ScalarField* scalar) const override {\n    return ProofSerializer<ScalarField>::ReadFromProof(this->buffer_, scalar);\n  }\n};\n\ntemplate <typename AffinePoint>\nclass SnarkVerifierPoseidonWriter\n    : public crypto::TranscriptWriter<AffinePoint>,\n      protected internal::SnarkVerifierPoseidonBase<AffinePoint> {\n public:\n  using ScalarField = typename AffinePoint::ScalarField;\n  using ScalarBigInt = typename ScalarField::BigIntTy;\n\n  // Initialize a transcript given an output buffer.\n  explicit SnarkVerifierPoseidonWriter(base::Uint8VectorBuffer buffer)\n      : crypto::TranscriptWriter<AffinePoint>(std::move(buffer)) {}\n\n  // NOTE(chokobole): |GetDigestLen()|, |GetStateLen()|, |Update()|,\n  // |Squeeze()|, |GetState()| and |SetState()| are called from rust binding.\n  size_t GetDigestLen() const { return ScalarBigInt::kByteNums; }\n\n  size_t GetStateLen() const {\n    return base::EstimateSize(this->state_, this->buf_);\n  }\n\n  void Update(const ScalarField* data, size_t len) {\n    this->DoUpdate(data, len);\n  }\n\n  ScalarField Squeeze() { return this->DoSqueeze(); }\n\n  std::vector<uint8_t> GetState() const { return this->DoGetState(); }\n\n  void SetState(absl::Span<const uint8_t> state) { this->DoSetState(state); }\n\n  // crypto::TranscriptWriter methods\n  ScalarField SqueezeChallenge() override { return this->DoSqueezeChallenge(); }\n\n  bool WriteToTranscript(const AffinePoint& point) override {\n    return this->DoWriteToTranscript(point);\n  }\n\n  bool WriteToTranscript(const ScalarField& scalar) override {\n    return this->DoWriteToTranscript(scalar);\n  }\n\n private:\n  bool DoWriteToProof(const AffinePoint& point) override {\n    return ProofSerializer<AffinePoint>::WriteToProof(point, this->buffer_);\n  }\n\n  bool DoWriteToProof(const ScalarField& scalar) override {\n    return ProofSerializer<ScalarField>::WriteToProof(scalar, this->buffer_);\n  }\n};\n\n}  // namespace tachyon::zk::plonk::halo2\n\n#endif  // TACHYON_ZK_PLONK_HALO2_SNARK_VERIFIER_POSEIDON_TRANSCRIPT_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/snark_verifier_poseidon_transcript_unittest.cc",
    "content": "#include \"tachyon/zk/plonk/halo2/snark_verifier_poseidon_transcript.h\"\n\n#include <utility>\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/elliptic_curves/bn/bn254/g1.h\"\n\nnamespace tachyon::zk::plonk::halo2 {\n\nnamespace {\n\nusing namespace math::bn254;\n\nclass SnarkVerifierPoseidonTranscriptTest : public testing::Test {\n public:\n  static void SetUpTestSuite() { G1Curve::Init(); }\n};\n\n}  // namespace\n\nTEST_F(SnarkVerifierPoseidonTranscriptTest, WritePoint) {\n  base::Uint8VectorBuffer write_buf;\n  SnarkVerifierPoseidonWriter<G1AffinePoint> writer(std::move(write_buf));\n  G1AffinePoint expected = G1AffinePoint::Random();\n  ASSERT_TRUE(writer.WriteToProof(expected));\n\n  base::Buffer read_buf(writer.buffer().buffer(), writer.buffer().buffer_len());\n  SnarkVerifierPoseidonReader<G1AffinePoint> reader(std::move(read_buf));\n  G1AffinePoint actual;\n  ASSERT_TRUE(reader.ReadFromProof(&actual));\n\n  EXPECT_EQ(expected, actual);\n}\n\nTEST_F(SnarkVerifierPoseidonTranscriptTest, WriteScalar) {\n  base::Uint8VectorBuffer write_buf;\n  SnarkVerifierPoseidonWriter<G1AffinePoint> writer(std::move(write_buf));\n  Fr expected = Fr::Random();\n  ASSERT_TRUE(writer.WriteToProof(expected));\n\n  base::Buffer read_buf(writer.buffer().buffer(), writer.buffer().buffer_len());\n  SnarkVerifierPoseidonReader<G1AffinePoint> reader(std::move(read_buf));\n  Fr actual;\n  ASSERT_TRUE(reader.ReadFromProof(&actual));\n\n  EXPECT_EQ(expected, actual);\n}\n\nTEST_F(SnarkVerifierPoseidonTranscriptTest, SqueezeChallenge) {\n  base::Uint8VectorBuffer write_buf;\n  SnarkVerifierPoseidonWriter<G1AffinePoint> writer(std::move(write_buf));\n  G1AffinePoint generator = G1AffinePoint::Generator();\n  ASSERT_TRUE(writer.WriteToProof(generator));\n  ASSERT_TRUE(writer.WriteToProof(generator));\n\n  std::vector<uint8_t> expected_bytes = {78,  246, 205, 146, 54,  16,  105, 106,\n                                         240, 24,  115, 146, 126, 203, 44,  166,\n                                         34,  117, 244, 97,  33,  69,  158, 167,\n                                         254, 239, 174, 66,  133, 142, 174, 27};\n  Fr expected = Fr::FromBigInt(math::BigInt<4>::FromBytesLE(expected_bytes));\n\n  Fr actual = writer.SqueezeChallenge();\n\n  EXPECT_EQ(expected, actual);\n}\n\n}  // namespace tachyon::zk::plonk::halo2\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/stringifiers/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"challenge_stringifier\",\n    hdrs = [\"challenge_stringifier.h\"],\n    deps = [\n        \":phase_stringifier\",\n        \"//tachyon/zk/plonk/constraint_system:challenge\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"column_key_stringifier\",\n    hdrs = [\"column_key_stringifier.h\"],\n    deps = [\n        \":column_type_stringifier\",\n        \"//tachyon/zk/plonk/base:column_key\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"column_type_stringifier\",\n    hdrs = [\"column_type_stringifier.h\"],\n    deps = [\n        \"//tachyon/base/strings:rust_stringifier\",\n        \"//tachyon/zk/plonk/base:column_type\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"expression_stringifier\",\n    hdrs = [\"expression_stringifier.h\"],\n    deps = [\n        \":challenge_stringifier\",\n        \":field_stringifier\",\n        \":phase_stringifier\",\n        \":rotation_stringifier\",\n        \":selector_stringifier\",\n        \"//tachyon/zk/expressions:constant_expression\",\n        \"//tachyon/zk/expressions:negated_expression\",\n        \"//tachyon/zk/expressions:product_expression\",\n        \"//tachyon/zk/expressions:scaled_expression\",\n        \"//tachyon/zk/expressions:sum_expression\",\n        \"//tachyon/zk/plonk/expressions:advice_expression\",\n        \"//tachyon/zk/plonk/expressions:challenge_expression\",\n        \"//tachyon/zk/plonk/expressions:fixed_expression\",\n        \"//tachyon/zk/plonk/expressions:instance_expression\",\n        \"//tachyon/zk/plonk/expressions:selector_expression\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"field_stringifier\",\n    hdrs = [\"field_stringifier.h\"],\n    deps = [\n        \"//tachyon/base/strings:rust_stringifier\",\n        \"//tachyon/math/finite_fields:prime_field_base\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"lookup_argument_stringifier\",\n    hdrs = [\"lookup_argument_stringifier.h\"],\n    deps = [\n        \":expression_stringifier\",\n        \"//tachyon/zk/lookup:argument\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"lookup_tracker_stringifier\",\n    hdrs = [\"lookup_tracker_stringifier.h\"],\n    deps = [\n        \"//tachyon/base/strings:rust_stringifier\",\n        \"//tachyon/zk/plonk/constraint_system:lookup_tracker\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"permutation_argument_stringifier\",\n    hdrs = [\"permutation_argument_stringifier.h\"],\n    deps = [\n        \":column_key_stringifier\",\n        \"//tachyon/zk/plonk/permutation:permutation_argument\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"permutation_verifying_key_stringifier\",\n    hdrs = [\"permutation_verifying_key_stringifier.h\"],\n    deps = [\n        \":point_stringifier\",\n        \"//tachyon/zk/plonk/permutation:permutation_verifying_key\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"phase_stringifier\",\n    hdrs = [\"phase_stringifier.h\"],\n    deps = [\n        \"//tachyon/base/strings:rust_stringifier\",\n        \"//tachyon/zk/plonk/base:phase\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"point_stringifier\",\n    hdrs = [\"point_stringifier.h\"],\n    deps = [\n        \":field_stringifier\",\n        \"//tachyon/math/geometry:affine_point\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"query_stringifier\",\n    hdrs = [\"query_stringifier.h\"],\n    deps = [\n        \":column_key_stringifier\",\n        \":rotation_stringifier\",\n        \"//tachyon/zk/plonk/constraint_system:query\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"rotation_stringifier\",\n    hdrs = [\"rotation_stringifier.h\"],\n    deps = [\n        \"//tachyon/base/strings:rust_stringifier\",\n        \"//tachyon/zk/base:rotation\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"selector_stringifier\",\n    hdrs = [\"selector_stringifier.h\"],\n    deps = [\n        \"//tachyon/base/strings:rust_stringifier\",\n        \"//tachyon/zk/plonk/constraint_system:selector\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"shuffle_argument_stringifier\",\n    hdrs = [\"shuffle_argument_stringifier.h\"],\n    deps = [\n        \":expression_stringifier\",\n        \"//tachyon/zk/shuffle:argument\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/stringifiers/challenge_stringifier.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_CHALLENGE_STRINGIFIER_H_\n#define TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_CHALLENGE_STRINGIFIER_H_\n\n#include <ostream>\n\n#include \"tachyon/base/strings/rust_stringifier.h\"\n#include \"tachyon/zk/plonk/constraint_system/challenge.h\"\n\nnamespace tachyon::base::internal {\n\ntemplate <>\nclass RustDebugStringifier<zk::plonk::Challenge> {\n public:\n  static std::ostream& AppendToStream(std::ostream& os, RustFormatter& fmt,\n                                      zk::plonk::Challenge challenge) {\n    return os << fmt.DebugStruct(\"Challenge\")\n                     .Field(\"index\", challenge.index())\n                     .Field(\"phase\", challenge.phase())\n                     .Finish();\n  }\n};\n\n}  // namespace tachyon::base::internal\n\n#endif  // TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_CHALLENGE_STRINGIFIER_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/stringifiers/column_key_stringifier.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_COLUMN_KEY_STRINGIFIER_H_\n#define TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_COLUMN_KEY_STRINGIFIER_H_\n\n#include <ostream>\n\n#include \"tachyon/base/strings/rust_stringifier.h\"\n#include \"tachyon/zk/plonk/base/column_key.h\"\n#include \"tachyon/zk/plonk/halo2/stringifiers/column_type_stringifier.h\"\n\nnamespace tachyon::base::internal {\n\nstruct AdviceColumnType {\n  zk::plonk::Phase phase;\n  explicit AdviceColumnType(zk::plonk::Phase phase) : phase(phase) {}\n};\n\ntemplate <>\nclass RustDebugStringifier<AdviceColumnType> {\n public:\n  static std::ostream& AppendToStream(std::ostream& os, RustFormatter& fmt,\n                                      const AdviceColumnType& column_type) {\n    return os << fmt.DebugStruct(\"Advice\")\n                     .Field(\"phase\", column_type.phase)\n                     .Finish();\n  }\n};\n\ntemplate <zk::plonk::ColumnType C>\nclass RustDebugStringifier<zk::plonk::ColumnKey<C>> {\n public:\n  static std::ostream& AppendToStream(std::ostream& os, RustFormatter& fmt,\n                                      const zk::plonk::ColumnKey<C>& column) {\n    // NOTE(chokobole): See\n    // https://github.com/kroma-network/halo2/blob/7d0a369/halo2_proofs/src/plonk/circuit.rs#L26-L31.\n    if (column.phase() == zk::plonk::kFirstPhase) {\n      return os << fmt.DebugStruct(\"Column\")\n                       .Field(\"index\", column.index())\n                       .Field(\"column_type\", column.type())\n                       .Finish();\n    } else {\n      // NOTE(dongchangYoo): The |ColumnType| in rust-halo2 includes |Phase|.\n      // Example of stringify rules:\n      // - |ColumnType| with |Phase(0)| => \"Advice\",\n      // - |ColumnType| with the other phases => \"Advice { phase: Phase(1) }\"\n      AdviceColumnType advice_column_type(column.phase());\n      return os << fmt.DebugStruct(\"Column\")\n                       .Field(\"index\", column.index())\n                       .Field(\"column_type\", advice_column_type)\n                       .Finish();\n    }\n  }\n};\n\n}  // namespace tachyon::base::internal\n\n#endif  // TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_COLUMN_KEY_STRINGIFIER_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/stringifiers/column_type_stringifier.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_COLUMN_TYPE_STRINGIFIER_H_\n#define TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_COLUMN_TYPE_STRINGIFIER_H_\n\n#include <ostream>\n\n#include \"tachyon/base/strings/rust_stringifier.h\"\n#include \"tachyon/zk/plonk/base/column_type.h\"\n\nnamespace tachyon::base::internal {\n\ntemplate <>\nclass RustDebugStringifier<zk::plonk::ColumnType> {\n public:\n  static std::ostream& AppendToStream(std::ostream& os, RustFormatter& fmt,\n                                      zk::plonk::ColumnType type) {\n    return os << zk::plonk::ColumnTypeToString(type);\n  }\n};\n\n}  // namespace tachyon::base::internal\n\n#endif  // TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_COLUMN_TYPE_STRINGIFIER_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/stringifiers/expression_stringifier.h",
    "content": "#ifndef TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_EXPRESSION_STRINGIFIER_H_\n#define TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_EXPRESSION_STRINGIFIER_H_\n\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/strings/rust_stringifier.h\"\n#include \"tachyon/zk/expressions/constant_expression.h\"\n#include \"tachyon/zk/expressions/negated_expression.h\"\n#include \"tachyon/zk/expressions/product_expression.h\"\n#include \"tachyon/zk/expressions/scaled_expression.h\"\n#include \"tachyon/zk/expressions/sum_expression.h\"\n#include \"tachyon/zk/plonk/expressions/advice_expression.h\"\n#include \"tachyon/zk/plonk/expressions/challenge_expression.h\"\n#include \"tachyon/zk/plonk/expressions/fixed_expression.h\"\n#include \"tachyon/zk/plonk/expressions/instance_expression.h\"\n#include \"tachyon/zk/plonk/expressions/selector_expression.h\"\n#include \"tachyon/zk/plonk/halo2/stringifiers/challenge_stringifier.h\"\n#include \"tachyon/zk/plonk/halo2/stringifiers/field_stringifier.h\"\n#include \"tachyon/zk/plonk/halo2/stringifiers/phase_stringifier.h\"\n#include \"tachyon/zk/plonk/halo2/stringifiers/rotation_stringifier.h\"\n#include \"tachyon/zk/plonk/halo2/stringifiers/selector_stringifier.h\"\n\nnamespace tachyon::base::internal {\n\ntemplate <typename F>\nclass RustDebugStringifier<zk::Expression<F>> {\n public:\n  static std::ostream& AppendToStream(std::ostream& os, RustFormatter& fmt,\n                                      const zk::Expression<F>& expression) {\n    switch (expression.type()) {\n      case zk::ExpressionType::kConstant: {\n        return os << fmt.DebugTuple(\"Constant\")\n                         .Field(expression.ToConstant()->value())\n                         .Finish();\n      }\n      case zk::ExpressionType::kSelector: {\n        zk::plonk::Selector selector = expression.ToSelector()->selector();\n        return os << fmt.DebugTuple(\"Selector\").Field(selector).Finish();\n      }\n      case zk::ExpressionType::kFixed: {\n        const zk::plonk::FixedQuery& query = expression.ToFixed()->query();\n        return os << fmt.DebugStruct(\"Fixed\")\n                         .Field(\"query_index\", query.GetIndex())\n                         .Field(\"column_index\", query.column().index())\n                         .Field(\"rotation\", query.rotation())\n                         .Finish();\n      }\n      case zk::ExpressionType::kAdvice: {\n        const zk::plonk::AdviceQuery& query = expression.ToAdvice()->query();\n        base::internal::DebugStruct debug_struct = fmt.DebugStruct(\"Advice\");\n        debug_struct.Field(\"query_index\", query.GetIndex())\n            .Field(\"column_index\", query.column().index())\n            .Field(\"rotation\", query.rotation());\n        if (query.column().phase() != zk::plonk::kFirstPhase) {\n          debug_struct.Field(\"phase\", query.column().phase());\n        }\n        return os << debug_struct.Finish();\n      }\n      case zk::ExpressionType::kInstance: {\n        const zk::plonk::InstanceQuery& query =\n            expression.ToInstance()->query();\n        return os << fmt.DebugStruct(\"Instance\")\n                         .Field(\"query_index\", query.GetIndex())\n                         .Field(\"column_index\", query.column().index())\n                         .Field(\"rotation\", query.rotation())\n                         .Finish();\n      }\n      case zk::ExpressionType::kChallenge: {\n        zk::plonk::Challenge challenge = expression.ToChallenge()->challenge();\n        return os << fmt.DebugTuple(\"Challenge\").Field(challenge).Finish();\n      }\n      case zk::ExpressionType::kNegated: {\n        return os << fmt.DebugTuple(\"Negated\")\n                         .Field(*expression.ToNegated()->expr())\n                         .Finish();\n      }\n      case zk::ExpressionType::kSum: {\n        const zk::SumExpression<F>* sum = expression.ToSum();\n        return os << fmt.DebugTuple(\"Sum\")\n                         .Field(*sum->left())\n                         .Field(*sum->right())\n                         .Finish();\n      }\n      case zk::ExpressionType::kProduct: {\n        const zk::ProductExpression<F>* product = expression.ToProduct();\n        return os << fmt.DebugTuple(\"Product\")\n                         .Field(*product->left())\n                         .Field(*product->right())\n                         .Finish();\n      }\n      case zk::ExpressionType::kScaled: {\n        const zk::ScaledExpression<F>* scaled = expression.ToScaled();\n        return os << fmt.DebugTuple(\"Scaled\")\n                         .Field(*scaled->expr())\n                         .Field(scaled->scale())\n                         .Finish();\n      }\n      case zk::ExpressionType::kFirstRow:\n      case zk::ExpressionType::kLastRow:\n      case zk::ExpressionType::kTransition:\n      case zk::ExpressionType::kVariable:\n        NOTREACHED() << \"AIR expression \"\n                     << ExpressionTypeToString(expression.type())\n                     << \" is not allowed in Halo2!\";\n    }\n    NOTREACHED();\n    return os;\n  }\n};\n\ntemplate <typename F>\nclass RustDebugStringifier<std::vector<std::unique_ptr<zk::Expression<F>>>> {\n public:\n  static std::ostream& AppendToStream(\n      std::ostream& os, RustFormatter& fmt,\n      const std::vector<std::unique_ptr<zk::Expression<F>>>& expressions) {\n    base::internal::DebugList list = fmt.DebugList();\n    for (const std::unique_ptr<zk::Expression<F>>& expression : expressions) {\n      list.Entry(*expression);\n    }\n    return os << list.Finish();\n  }\n};\n\n}  // namespace tachyon::base::internal\n\n#endif  // TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_EXPRESSION_STRINGIFIER_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/stringifiers/field_stringifier.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_FIELD_STRINGIFIER_H_\n#define TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_FIELD_STRINGIFIER_H_\n\n#include <ostream>\n#include <type_traits>\n\n#include \"tachyon/base/strings/rust_stringifier.h\"\n#include \"tachyon/math/finite_fields/prime_field_base.h\"\n\nnamespace tachyon::base::internal {\n\ntemplate <typename T>\nclass RustDebugStringifier<\n    T, std::enable_if_t<std::is_base_of_v<math::PrimeFieldBase<T>, T>>> {\n public:\n  static std::ostream& AppendToStream(std::ostream& os, RustFormatter& fmt,\n                                      const T& value) {\n    return os << value.ToHexString(true);\n  }\n};\n\n}  // namespace tachyon::base::internal\n\n#endif  // TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_FIELD_STRINGIFIER_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/stringifiers/lookup_argument_stringifier.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_LOOKUP_ARGUMENT_STRINGIFIER_H_\n#define TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_LOOKUP_ARGUMENT_STRINGIFIER_H_\n\n#include <ostream>\n\n#include \"tachyon/base/strings/rust_stringifier.h\"\n#include \"tachyon/zk/lookup/argument.h\"\n#include \"tachyon/zk/plonk/halo2/stringifiers/expression_stringifier.h\"\n\nnamespace tachyon::base::internal {\n\ntemplate <typename F>\nclass RustDebugStringifier<zk::lookup::Argument<F>> {\n public:\n  static std::ostream& AppendToStream(std::ostream& os, RustFormatter& fmt,\n                                      const zk::lookup::Argument<F>& argument) {\n    return os << fmt.DebugStruct(\"Argument\")\n                     .Field(\"input_expressions\", argument.input_expressions())\n                     .Field(\"table_expressions\", argument.table_expressions())\n                     .Finish();\n  }\n};\n\n}  // namespace tachyon::base::internal\n\n#endif  // TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_LOOKUP_ARGUMENT_STRINGIFIER_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/stringifiers/lookup_tracker_stringifier.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_LOOKUP_TRACKER_STRINGIFIER_H_\n#define TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_LOOKUP_TRACKER_STRINGIFIER_H_\n\n#include <ostream>\n\n#include \"tachyon/base/strings/rust_stringifier.h\"\n#include \"tachyon/zk/plonk/constraint_system/lookup_tracker.h\"\n\nnamespace tachyon::base::internal {\n\ntemplate <typename F>\nclass RustDebugStringifier<zk::plonk::LookupTracker<F>> {\n public:\n  static std::ostream& AppendToStream(\n      std::ostream& os, RustFormatter& fmt,\n      const zk::plonk::LookupTracker<F>& lookup_tracker) {\n    return os << fmt.DebugStruct(\"LookupTracker\")\n                     .Field(\"table\", lookup_tracker.table)\n                     .Field(\"inputs\", lookup_tracker.inputs)\n                     .Finish();\n  }\n};\n\n}  // namespace tachyon::base::internal\n\n#endif  // TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_LOOKUP_TRACKER_STRINGIFIER_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/stringifiers/permutation_argument_stringifier.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_PERMUTATION_ARGUMENT_STRINGIFIER_H_\n#define TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_PERMUTATION_ARGUMENT_STRINGIFIER_H_\n\n#include <ostream>\n\n#include \"tachyon/base/strings/rust_stringifier.h\"\n#include \"tachyon/zk/plonk/halo2/stringifiers/column_key_stringifier.h\"\n#include \"tachyon/zk/plonk/permutation/permutation_argument.h\"\n\nnamespace tachyon::base::internal {\n\ntemplate <>\nclass RustDebugStringifier<zk::plonk::PermutationArgument> {\n public:\n  static std::ostream& AppendToStream(\n      std::ostream& os, RustFormatter& fmt,\n      const zk::plonk::PermutationArgument& argument) {\n    return os << fmt.DebugStruct(\"Argument\")\n                     .Field(\"columns\", argument.columns())\n                     .Finish();\n  }\n};\n\n}  // namespace tachyon::base::internal\n\n#endif  // TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_PERMUTATION_ARGUMENT_STRINGIFIER_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/stringifiers/permutation_verifying_key_stringifier.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_PERMUTATION_VERIFYING_KEY_STRINGIFIER_H_\n#define TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_PERMUTATION_VERIFYING_KEY_STRINGIFIER_H_\n\n#include <ostream>\n\n#include \"tachyon/base/strings/rust_stringifier.h\"\n#include \"tachyon/zk/plonk/halo2/stringifiers/point_stringifier.h\"\n#include \"tachyon/zk/plonk/permutation/permutation_verifying_key.h\"\n\nnamespace tachyon::base::internal {\n\ntemplate <typename Commitment>\nclass RustDebugStringifier<zk::plonk::PermutationVerifyingKey<Commitment>> {\n public:\n  static std::ostream& AppendToStream(\n      std::ostream& os, RustFormatter& fmt,\n      const zk::plonk::PermutationVerifyingKey<Commitment>& vk) {\n    // NOTE(chokobole): See\n    // https://github.com/kroma-network/halo2/blob/7d0a369/halo2_proofs/src/plonk/permutation.rs#L80-L84\n    return os << fmt.DebugStruct(\"VerifyingKey\")\n                     .Field(\"commitments\", vk.commitments())\n                     .Finish();\n  }\n};\n\n}  // namespace tachyon::base::internal\n\n#endif  // TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_PERMUTATION_VERIFYING_KEY_STRINGIFIER_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/stringifiers/phase_stringifier.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_PHASE_STRINGIFIER_H_\n#define TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_PHASE_STRINGIFIER_H_\n\n#include <ostream>\n\n#include \"tachyon/base/strings/rust_stringifier.h\"\n#include \"tachyon/zk/plonk/base/phase.h\"\n\nnamespace tachyon::base::internal {\n\ntemplate <>\nclass RustDebugStringifier<zk::plonk::Phase> {\n public:\n  static std::ostream& AppendToStream(std::ostream& os, RustFormatter& fmt,\n                                      zk::plonk::Phase phase) {\n    return os << fmt.DebugTuple(\"Phase\").Field(int{phase.value()}).Finish();\n  }\n};\n\n}  // namespace tachyon::base::internal\n\n#endif  // TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_PHASE_STRINGIFIER_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/stringifiers/point_stringifier.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_POINT_STRINGIFIER_H_\n#define TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_POINT_STRINGIFIER_H_\n\n#include <ostream>\n\n#include \"tachyon/base/strings/rust_stringifier.h\"\n#include \"tachyon/math/geometry/affine_point.h\"\n#include \"tachyon/zk/plonk/halo2/stringifiers/field_stringifier.h\"\n\nnamespace tachyon::base::internal {\n\ntemplate <typename Curve>\nclass RustDebugStringifier<math::AffinePoint<Curve>> {\n public:\n  static std::ostream& AppendToStream(std::ostream& os, RustFormatter& fmt,\n                                      const math::AffinePoint<Curve>& value) {\n    if (value.IsZero()) {\n      return os << \"Infinity\";\n    } else {\n      return os\n             << fmt.DebugTuple(\"\").Field(value.x()).Field(value.y()).Finish();\n    }\n  }\n};\n\n}  // namespace tachyon::base::internal\n\n#endif  // TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_POINT_STRINGIFIER_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/stringifiers/query_stringifier.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_QUERY_STRINGIFIER_H_\n#define TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_QUERY_STRINGIFIER_H_\n\n#include <ostream>\n\n#include \"tachyon/base/strings/rust_stringifier.h\"\n#include \"tachyon/zk/plonk/constraint_system/query.h\"\n#include \"tachyon/zk/plonk/halo2/stringifiers/column_key_stringifier.h\"\n#include \"tachyon/zk/plonk/halo2/stringifiers/rotation_stringifier.h\"\n\nnamespace tachyon::base::internal {\n\ntemplate <zk::plonk::ColumnType C>\nclass RustDebugStringifier<zk::plonk::QueryData<C>> {\n public:\n  static std::ostream& AppendToStream(std::ostream& os, RustFormatter& fmt,\n                                      const zk::plonk::QueryData<C>& query) {\n    // NOTE(chokobole): See\n    // https://github.com/kroma-network/halo2/blob/7d0a369/halo2_proofs/src/plonk/circuit.rs#L1382.\n    return os << fmt.DebugTuple(\"\")\n                     .Field(query.column())\n                     .Field(query.rotation())\n                     .Finish();\n  }\n};\n\n}  // namespace tachyon::base::internal\n\n#endif  // TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_QUERY_STRINGIFIER_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/stringifiers/rotation_stringifier.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_ROTATION_STRINGIFIER_H_\n#define TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_ROTATION_STRINGIFIER_H_\n\n#include <ostream>\n\n#include \"tachyon/base/strings/rust_stringifier.h\"\n#include \"tachyon/zk/base/rotation.h\"\n\nnamespace tachyon::base::internal {\n\ntemplate <>\nclass RustDebugStringifier<zk::Rotation> {\n public:\n  static std::ostream& AppendToStream(std::ostream& os, RustFormatter& fmt,\n                                      zk::Rotation rotation) {\n    return os << fmt.DebugTuple(\"Rotation\").Field(rotation.value()).Finish();\n  }\n};\n\n}  // namespace tachyon::base::internal\n\n#endif  // TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_ROTATION_STRINGIFIER_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/stringifiers/selector_stringifier.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_SELECTOR_STRINGIFIER_H_\n#define TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_SELECTOR_STRINGIFIER_H_\n\n#include <ostream>\n\n#include \"tachyon/base/strings/rust_stringifier.h\"\n#include \"tachyon/zk/plonk/constraint_system/selector.h\"\n\nnamespace tachyon::base::internal {\n\ntemplate <>\nclass RustDebugStringifier<zk::plonk::Selector> {\n public:\n  static std::ostream& AppendToStream(std::ostream& os, RustFormatter& fmt,\n                                      zk::plonk::Selector selector) {\n    return os << fmt.DebugTuple(\"Selector\")\n                     .Field(selector.index())\n                     .Field(selector.is_simple())\n                     .Finish();\n  }\n};\n\n}  // namespace tachyon::base::internal\n\n#endif  // TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_SELECTOR_STRINGIFIER_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/stringifiers/shuffle_argument_stringifier.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_SHUFFLE_ARGUMENT_STRINGIFIER_H_\n#define TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_SHUFFLE_ARGUMENT_STRINGIFIER_H_\n\n#include <ostream>\n\n#include \"tachyon/base/strings/rust_stringifier.h\"\n#include \"tachyon/zk/plonk/halo2/stringifiers/expression_stringifier.h\"\n#include \"tachyon/zk/shuffle/argument.h\"\n\nnamespace tachyon::base::internal {\n\ntemplate <typename F>\nclass RustDebugStringifier<zk::shuffle::Argument<F>> {\n public:\n  static std::ostream& AppendToStream(\n      std::ostream& os, RustFormatter& fmt,\n      const zk::shuffle::Argument<F>& argument) {\n    return os << fmt.DebugStruct(\"Argument\")\n                     .Field(\"input_expressions\", argument.input_expressions())\n                     .Field(\"shuffle_expressions\",\n                            argument.shuffle_expressions())\n                     .Finish();\n  }\n};\n\n}  // namespace tachyon::base::internal\n\n#endif  // TACHYON_ZK_PLONK_HALO2_STRINGIFIERS_SHUFFLE_ARGUMENT_STRINGIFIER_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/synthesizer.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_HALO2_SYNTHESIZER_H_\n#define TACHYON_ZK_PLONK_HALO2_SYNTHESIZER_H_\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/zk/base/entities/prover_base.h\"\n#include \"tachyon/zk/plonk/constraint_system/constraint_system.h\"\n#include \"tachyon/zk/plonk/halo2/witness_collection.h\"\n\nnamespace tachyon::zk::plonk::halo2 {\n\ntemplate <typename Evals>\nclass Synthesizer {\n public:\n  using F = typename Evals::Field;\n\n  Synthesizer() = default;\n  Synthesizer(size_t num_circuits, const ConstraintSystem<F>* constraint_system)\n      : num_circuits_(num_circuits), constraint_system_(constraint_system) {\n    // NOTE(batzor): These vectors are initialized below in |SetAdviceColumn| so\n    // it is safe to keep it uninitialized here.\n    advice_columns_vec_.resize(\n        num_circuits_,\n        std::vector<Evals>(constraint_system->num_advice_columns()));\n    advice_blinds_vec_.resize(\n        num_circuits_, std::vector<F>(constraint_system->num_advice_columns()));\n  }\n\n  // Synthesize circuit and store advice columns.\n  template <typename PCS, typename Circuit,\n            typename RationalEvals = typename PCS::RationalEvals>\n  void GenerateAdviceColumns(\n      ProverBase<PCS>* prover, std::vector<Circuit>& circuits,\n      const std::vector<std::vector<Evals>>& instance_columns_vec) {\n    VLOG(2) << \"Generating advice columns\";\n    CHECK_EQ(num_circuits_, circuits.size());\n\n    ConstraintSystem<F> empty_constraint_system;\n    empty_constraint_system.set_lookup_type(constraint_system_->lookup_type());\n\n    typename Circuit::Config config =\n        Circuit::Configure(empty_constraint_system);\n\n    for (Phase current_phase : constraint_system_->GetPhases()) {\n      size_t write_idx = 0;\n      if constexpr (PCS::kSupportsBatchMode) {\n        std::vector<size_t> current_phase_column_indices = base::FindIndices(\n            constraint_system_->advice_column_phases().begin(),\n            constraint_system_->advice_column_phases().end(), current_phase);\n        prover->pcs().SetBatchMode(current_phase_column_indices.size() *\n                                   num_circuits_);\n      }\n      for (size_t i = 0; i < num_circuits_; ++i) {\n        std::vector<RationalEvals> rational_advice_columns =\n            GenerateRationalAdvices(prover, current_phase,\n                                    instance_columns_vec[i], circuits[i],\n                                    config);\n\n        // Parse only indices related to the |current_phase|.\n        const std::vector<Phase>& advice_phases =\n            constraint_system_->advice_column_phases();\n        for (size_t j = 0; j < rational_advice_columns.size(); ++j) {\n          if (current_phase != advice_phases[j]) continue;\n          const RationalEvals& column = rational_advice_columns[j];\n          std::vector<F> evaluated(column.NumElements());\n          CHECK(math::RationalField<F>::BatchEvaluate(column.evaluations(),\n                                                      &evaluated));\n          // Add blinding factors to advice columns\n          evaluated[prover->pcs().N() - 1] = F::One();\n\n          Evals evaluated_evals(std::move(evaluated));\n          if constexpr (PCS::kSupportsBatchMode) {\n            prover->BatchCommitAt(evaluated_evals, write_idx++);\n          } else {\n            prover->CommitAndWriteToProof(evaluated_evals);\n          }\n          SetAdviceColumn(i, j, std::move(evaluated_evals),\n                          prover->blinder().Generate());\n        }\n      }\n      if constexpr (PCS::kSupportsBatchMode) {\n        prover->RetrieveAndWriteBatchCommitmentsToProof();\n      }\n      UpdateChallenges(prover, current_phase);\n    }\n  }\n\n  // Return |challenges_| as a vector.\n  std::vector<F> ExportChallenges() {\n    return base::Map(\n        std::make_move_iterator(challenges_.begin()),\n        std::make_move_iterator(challenges_.end()),\n        [](std::pair<size_t, F>&& item) { return std::move(item.second); });\n  }\n\n  // Move out |challenges_| as a vector.\n  std::vector<F> TakeChallenges() && {\n    return base::Map(\n        std::make_move_iterator(challenges_.begin()),\n        std::make_move_iterator(challenges_.end()),\n        [](std::pair<size_t, F>&& item) { return std::move(item.second); });\n  }\n  std::vector<std::vector<Evals>>&& TakeAdviceColumnsVec() && {\n    return std::move(advice_columns_vec_);\n  };\n  std::vector<std::vector<F>>&& TakeAdviceBlindsVec() && {\n    return std::move(advice_blinds_vec_);\n  };\n\n private:\n  void SetAdviceColumn(size_t circuit_idx, size_t column_idx, Evals&& column,\n                       F&& blind) {\n    CHECK_LT(circuit_idx, num_circuits_);\n    CHECK_LT(column_idx, constraint_system_->num_advice_columns());\n    advice_columns_vec_[circuit_idx][column_idx] = std::move(column);\n    advice_blinds_vec_[circuit_idx][column_idx] = std::move(blind);\n  }\n\n  // Performs synthesis for a specific |circuit| and a specific |phase|, and\n  // returns a vector of |RationalEvals|.\n  template <typename PCS, typename Circuit,\n            typename RationalEvals = typename PCS::RationalEvals>\n  std::vector<RationalEvals> GenerateRationalAdvices(\n      ProverBase<PCS>* prover, Phase phase,\n      const std::vector<Evals>& instance_columns, const Circuit& circuit,\n      const typename Circuit::Config& config) {\n    // The prover will not be allowed to assign values to advice\n    // cells that exist within inactive rows, which include some\n    // number of blinding factors and an extra row for use in the\n    // permutation argument.\n    WitnessCollection<Evals, RationalEvals> witness(\n        prover->domain(), constraint_system_->num_advice_columns(),\n        prover->GetUsableRows(), phase, challenges_, instance_columns);\n\n    typename Circuit::FloorPlanner floor_planner;\n    floor_planner.Synthesize(&witness, circuit, config.Clone(),\n                             constraint_system_->constants());\n\n    return std::move(witness).TakeAdvices();\n  }\n\n  template <typename PCS>\n  void UpdateChallenges(ProverBase<PCS>* prover, Phase phase) {\n    const std::vector<Phase>& phases = constraint_system_->challenge_phases();\n    for (size_t i = 0; i < phases.size(); ++i) {\n      if (phase == phases[i]) {\n        F challenge = prover->GetWriter()->SqueezeChallenge();\n        VLOG(2) << \"Halo2(challenge[\" << i\n                << \"]): \" << challenge.ToHexString(true);\n        const auto [_, inserted] =\n            challenges_.try_emplace(i, std::move(challenge));\n        CHECK(inserted);\n      }\n    }\n  }\n\n  size_t num_circuits_ = 0;\n  // not owned\n  const ConstraintSystem<F>* constraint_system_ = nullptr;\n\n  absl::btree_map<size_t, F> challenges_;\n  std::vector<std::vector<Evals>> advice_columns_vec_;\n  std::vector<std::vector<F>> advice_blinds_vec_;\n};\n\n}  // namespace tachyon::zk::plonk::halo2\n\n#endif  // TACHYON_ZK_PLONK_HALO2_SYNTHESIZER_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/transcript_type.h",
    "content": "#ifndef TACHYON_ZK_PLONK_HALO2_TRANSCRIPT_TYPE_H_\n#define TACHYON_ZK_PLONK_HALO2_TRANSCRIPT_TYPE_H_\n\n#include <stdint.h>\n\n#include <string>\n\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/base/flag/flag_value_traits.h\"\n\nnamespace tachyon {\nnamespace zk::plonk::halo2 {\n\n// THE ORDER OF ELEMENTS ARE IMPORTANT!! DO NOT CHANGE!\n// This order matches with the constants of c APIs.\n// Pleas refer to tachyon/c/zk/plonk/halo2/constants.h for details.\nenum class TranscriptType : uint8_t {\n  kBlake2b,\n  kPoseidon,\n  kSha256,\n  kSnarkVerifierPoseidon,\n};\n\n}  // namespace zk::plonk::halo2\n\nnamespace base {\n\ntemplate <>\nclass FlagValueTraits<zk::plonk::halo2::TranscriptType> {\n public:\n  static bool ParseValue(std::string_view input,\n                         zk::plonk::halo2::TranscriptType* value,\n                         std::string* reason) {\n    if (input == \"blake2b\") {\n      *value = zk::plonk::halo2::TranscriptType::kBlake2b;\n    } else if (input == \"poseidon\") {\n      *value = zk::plonk::halo2::TranscriptType::kPoseidon;\n    } else if (input == \"sha256\") {\n      *value = zk::plonk::halo2::TranscriptType::kSha256;\n    } else if (input == \"snark_verifier_poseidon\") {\n      *value = zk::plonk::halo2::TranscriptType::kSnarkVerifierPoseidon;\n    } else {\n      *reason = absl::Substitute(\"Unknown transcript type: $0\", input);\n      return false;\n    }\n    return true;\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_ZK_PLONK_HALO2_TRANSCRIPT_TYPE_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/vendor.cc",
    "content": "#include \"tachyon/zk/plonk/halo2/vendor.h\"\n\n#include \"tachyon/base/logging.h\"\n\nnamespace tachyon::zk::plonk::halo2 {\n\nstd::string_view VendorToString(Vendor vendor) {\n  switch (vendor) {\n    case Vendor::kPSE:\n      return \"pse\";\n    case Vendor::kScroll:\n      return \"scroll\";\n  }\n  NOTREACHED();\n  return \"\";\n}\n\n}  // namespace tachyon::zk::plonk::halo2\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/vendor.h",
    "content": "#ifndef TACHYON_ZK_PLONK_HALO2_VENDOR_H_\n#define TACHYON_ZK_PLONK_HALO2_VENDOR_H_\n\n#include <string>\n#include <string_view>\n\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/base/flag/flag_value_traits.h\"\n#include \"tachyon/export.h\"\n\nnamespace tachyon {\nnamespace zk::plonk::halo2 {\n\n// THE ORDER OF ELEMENTS ARE IMPORTANT!! DO NOT CHANGE!\n// This order matches with the constants of c APIs.\n// Pleas refer to tachyon/c/zk/plonk/halo2/constants.h for details.\nenum class Vendor {\n  kPSE,\n  kScroll,\n};\n\nTACHYON_EXPORT std::string_view VendorToString(Vendor vendor);\n\n}  // namespace zk::plonk::halo2\n\nnamespace base {\n\ntemplate <>\nclass FlagValueTraits<zk::plonk::halo2::Vendor> {\n public:\n  static bool ParseValue(std::string_view input,\n                         zk::plonk::halo2::Vendor* value, std::string* reason) {\n    if (input == \"pse\") {\n      *value = zk::plonk::halo2::Vendor::kPSE;\n    } else if (input == \"scroll\") {\n      *value = zk::plonk::halo2::Vendor::kScroll;\n    } else {\n      *reason = absl::Substitute(\"Unknown vendor: $0\", input);\n      return false;\n    }\n    return true;\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_ZK_PLONK_HALO2_VENDOR_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/verifier.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_HALO2_VERIFIER_H_\n#define TACHYON_ZK_PLONK_HALO2_VERIFIER_H_\n\n#include <functional>\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"gtest/gtest_prod.h\"\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/crypto/commitments/polynomial_openings.h\"\n#include \"tachyon/zk/base/entities/verifier_base.h\"\n#include \"tachyon/zk/lookup/halo2/opening_point_set.h\"\n#include \"tachyon/zk/lookup/halo2/utils.h\"\n#include \"tachyon/zk/lookup/verifier.h\"\n#include \"tachyon/zk/plonk/halo2/proof_reader.h\"\n#include \"tachyon/zk/plonk/keys/verifying_key.h\"\n#include \"tachyon/zk/plonk/permutation/permutation_utils.h\"\n#include \"tachyon/zk/plonk/permutation/permutation_verifier.h\"\n#include \"tachyon/zk/plonk/vanishing/vanishing_utils.h\"\n#include \"tachyon/zk/plonk/vanishing/vanishing_verifier.h\"\n#include \"tachyon/zk/shuffle/opening_point_set.h\"\n#include \"tachyon/zk/shuffle/utils.h\"\n#include \"tachyon/zk/shuffle/verifier.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename TestArguments, typename TestData>\nclass CircuitTest;\n\nnamespace halo2 {\n\ntemplate <typename _PS>\nclass Verifier : public VerifierBase<typename _PS::PCS> {\n public:\n  constexpr static lookup::Type kLookupType = _PS::kLookupType;\n\n  using PS = _PS;\n  using PCS = typename PS::PCS;\n  using F = typename PCS::Field;\n  using Commitment = typename PCS::Commitment;\n  using Evals = typename PCS::Evals;\n  using Poly = typename PCS::Poly;\n  using Coefficients = typename Poly::Coefficients;\n  using Opening = crypto::PolynomialOpening<Poly, Commitment>;\n  using Proof = halo2::Proof<kLookupType, F, Commitment>;\n\n  using VerifierBase<PCS>::VerifierBase;\n\n  [[nodiscard]] bool VerifyProof(\n      const VerifyingKey<F, Commitment>& vkey,\n      const std::vector<std::vector<Evals>>& instance_columns_vec) {\n    return VerifyProofForTesting(vkey, instance_columns_vec, nullptr, nullptr);\n  }\n\n private:\n  template <typename TestArguments, typename TestData>\n  friend class plonk::CircuitTest;\n\n  bool VerifyProofForTesting(\n      const VerifyingKey<F, Commitment>& vkey,\n      const std::vector<std::vector<Evals>>& instance_columns_vec,\n      Proof* proof_out, F* expected_h_eval_out) {\n    if (!ValidateInstanceColumnsVec(vkey, instance_columns_vec)) return false;\n\n    std::vector<std::vector<Commitment>> instance_commitments_vec;\n    if constexpr (PCS::kQueryInstance) {\n      instance_commitments_vec = CommitColumnsVec(instance_columns_vec);\n    } else {\n      instance_commitments_vec.resize(instance_columns_vec.size());\n    }\n\n    crypto::TranscriptReader<Commitment>* transcript = this->GetReader();\n    CHECK(transcript->WriteToTranscript(vkey.transcript_repr()));\n\n    if constexpr (PCS::kQueryInstance) {\n      WriteCommitmentsVecToTranscript(transcript, instance_commitments_vec);\n    } else {\n      WriteColumnsVecToTranscript(transcript, instance_columns_vec);\n    }\n\n    ProofReader<kLookupType, F, Commitment> proof_reader(\n        vkey, transcript, instance_commitments_vec.size());\n    Proof& proof = proof_reader.proof();\n    proof_reader.ReadAdviceCommitmentsVecAndChallenges();\n    proof_reader.ReadTheta();\n    proof_reader.ReadLookupPreparedCommitments();\n    proof_reader.ReadBetaAndGamma();\n    proof_reader.ReadPermutationProductCommitments();\n    proof_reader.ReadLookupGrandCommitments();\n    proof_reader.ReadShuffleGrandCommitments();\n    proof_reader.ReadVanishingRandomPolyCommitment();\n    proof_reader.ReadY();\n    proof_reader.ReadVanishingHPolyCommitments();\n    proof_reader.ReadX();\n    if constexpr (PCS::kQueryInstance) {\n      proof_reader.ReadInstanceEvalsIfQueryInstance();\n    } else {\n      proof_reader.ReadInstanceEvalsIfNoQueryInstance();\n      proof.instance_evals_vec =\n          ComputeInstanceEvalsVec(vkey, instance_columns_vec, proof.x);\n    }\n    proof_reader.ReadAdviceEvals();\n    proof_reader.ReadFixedEvals();\n    proof_reader.ReadVanishingRandomEval();\n    proof_reader.ReadCommonPermutationEvals();\n    proof_reader.ReadPermutationEvals();\n    proof_reader.ReadLookupEvals();\n    proof_reader.ReadShuffleEvals();\n    CHECK(proof_reader.Done());\n\n    if (proof_out) {\n      *proof_out = proof;\n    }\n\n    ComputeAuxValues(vkey.constraint_system(), proof);\n\n    return DoVerify(instance_commitments_vec, vkey, proof, expected_h_eval_out);\n  }\n\n  void ComputeAuxValues(const ConstraintSystem<F>& constraint_system,\n                        Proof& proof) const {\n    RowIndex blinding_factors = constraint_system.ComputeBlindingFactors();\n    std::vector<F> l_evals = this->domain_->EvaluatePartialLagrangeCoefficients(\n        proof.x, base::Range<int32_t, /*IsStartInclusive=*/true,\n                             /*IsEndInclusive=*/true>(\n                     -static_cast<int32_t>(blinding_factors + 1), 0));\n    proof.l_first = l_evals[1 + blinding_factors];\n    proof.l_blind = std::accumulate(\n        l_evals.begin() + 1, l_evals.begin() + 1 + blinding_factors, F::Zero(),\n        [](F& acc, const F& eval) { return acc += eval; });\n    proof.l_last = l_evals[0];\n\n    proof.x_next = Rotation::Next().RotateOmega(this->domain(), proof.x);\n    proof.x_prev = Rotation::Prev().RotateOmega(this->domain(), proof.x);\n    proof.x_last =\n        Rotation(-(blinding_factors + 1)).RotateOmega(this->domain(), proof.x);\n    proof.x_n = proof.x.ExpPowOfTwo(this->pcs_.K());\n  }\n\n  bool ValidateInstanceColumnsVec(\n      const VerifyingKey<F, Commitment>& vkey,\n      const std::vector<std::vector<Evals>>& instance_columns_vec) const {\n    size_t num_instance_columns =\n        vkey.constraint_system().num_instance_columns();\n    auto check_num_instance_columns =\n        [num_instance_columns](const std::vector<Evals>& instance_columns) {\n          if (instance_columns.size() != num_instance_columns) {\n            LOG(ERROR) << \"The size of instance columns doesn't match with \"\n                          \"constraint system\";\n            return false;\n          }\n          return true;\n        };\n\n    RowIndex usable_rows =\n        this->GetUsableRows(vkey.constraint_system().ComputeBlindingFactors());\n    auto check_rows = [usable_rows](const Evals& instance_columns) {\n      if (instance_columns.NumElements() > size_t{usable_rows}) {\n        LOG(ERROR) << \"Too many number of elements in instance column\";\n        return false;\n      }\n      return true;\n    };\n\n    return std::all_of(instance_columns_vec.begin(), instance_columns_vec.end(),\n                       [&check_num_instance_columns, &check_rows](\n                           const std::vector<Evals>& instance_columns) {\n                         if (!check_num_instance_columns(instance_columns))\n                           return false;\n                         return std::all_of(instance_columns.begin(),\n                                            instance_columns.end(), check_rows);\n                       });\n  }\n\n  std::vector<Commitment> CommitColumns(const std::vector<Evals>& columns) {\n    return base::Map(columns, [this](const Evals& column) {\n      std::vector<F> expanded_evals(this->pcs_.N());\n      const std::vector<F>& evals = column.evaluations();\n      base::Parallelize(\n          expanded_evals,\n          [&evals](absl::Span<F> chunk, size_t chunk_offset,\n                   size_t chunk_size) {\n            size_t i = chunk_offset * chunk_size;\n            for (F& eval : chunk) {\n              eval = i < evals.size() ? evals[i] : F::Zero();\n              ++i;\n            }\n          },\n          /*threshold=*/math::ParallelizeThreshold::kFieldInit);\n      return this->Commit(Evals(expanded_evals));\n    });\n  }\n\n  std::vector<std::vector<Commitment>> CommitColumnsVec(\n      const std::vector<std::vector<Evals>>& columns_vec) {\n    return base::Map(columns_vec, [this](const std::vector<Evals>& columns) {\n      return CommitColumns(columns);\n    });\n  }\n\n  static void WriteCommitmentsVecToTranscript(\n      crypto::TranscriptReader<Commitment>* transcript,\n      const std::vector<std::vector<Commitment>>& commitments_vec) {\n    for (const std::vector<Commitment>& commitments : commitments_vec) {\n      for (const Commitment& commitment : commitments) {\n        CHECK(transcript->WriteToTranscript(commitment));\n      }\n    }\n  }\n\n  static void WriteColumnsVecToTranscript(\n      crypto::TranscriptReader<Commitment>* transcript,\n      const std::vector<std::vector<Evals>>& columns_vec) {\n    for (const std::vector<Evals>& columns : columns_vec) {\n      for (const Evals& column : columns) {\n        for (const F& value : column.evaluations()) {\n          CHECK(transcript->WriteToTranscript(value));\n        }\n      }\n    }\n  }\n\n  static RowIndex ComputeMaxRow(const std::vector<Evals>& columns) {\n    if (columns.empty()) return 0;\n    std::vector<RowIndex> rows = base::Map(columns, [](const Evals& evals) {\n      // NOTE(chokobole): It's safe to downcast because domain is already\n      // checked.\n      return static_cast<RowIndex>(evals.NumElements());\n    });\n    return *std::max_element(rows.begin(), rows.end());\n  }\n\n  static F ComputeInstanceEval(const std::vector<Evals>& instance_columns,\n                               const InstanceQueryData& instance_query,\n                               const std::vector<F>& partial_lagrange_coeffs,\n                               size_t max_rotation) {\n    const std::vector<F>& instances =\n        instance_columns[instance_query.column().index()].evaluations();\n    size_t offset = max_rotation - instance_query.rotation().value();\n    absl::Span<const F> sub_partial_lagrange_coeffs(partial_lagrange_coeffs);\n    sub_partial_lagrange_coeffs =\n        sub_partial_lagrange_coeffs.subspan(offset, instances.size());\n    return F::SumOfProductsSerial(instances, sub_partial_lagrange_coeffs);\n  }\n\n  static std::vector<F> ComputeInstanceEvals(\n      const std::vector<Evals>& instance_columns,\n      const std::vector<InstanceQueryData>& instance_queries,\n      const std::vector<F>& partial_lagrange_coeffs, size_t max_rotation) {\n    return base::Map(instance_queries,\n                     [&instance_columns, &partial_lagrange_coeffs,\n                      max_rotation](const InstanceQueryData& instance_query) {\n                       return ComputeInstanceEval(\n                           instance_columns, instance_query,\n                           partial_lagrange_coeffs, max_rotation);\n                     });\n  }\n\n  std::vector<std::vector<F>> ComputeInstanceEvalsVec(\n      const VerifyingKey<F, Commitment>& vkey,\n      const std::vector<std::vector<Evals>>& instance_columns_vec, const F& x) {\n    struct RotationRange {\n      int32_t min = 0;\n      int32_t max = 0;\n    };\n\n    const std::vector<InstanceQueryData>& instance_queries =\n        vkey.constraint_system().instance_queries();\n    RotationRange range = std::accumulate(\n        instance_queries.begin(), instance_queries.end(), RotationRange(),\n        [](RotationRange& range, const InstanceQueryData& instance) {\n          int32_t rotation_value = instance.rotation().value();\n          if (rotation_value < range.min) {\n            range.min = rotation_value;\n          } else if (rotation_value > range.max) {\n            range.max = rotation_value;\n          }\n          return range;\n        });\n\n    std::vector<RowIndex> max_instances_rows =\n        base::Map(instance_columns_vec, &ComputeMaxRow);\n    auto max_instances_row_it =\n        std::max_element(max_instances_rows.begin(), max_instances_rows.end());\n    RowIndex max_instances_row =\n        max_instances_row_it != max_instances_rows.end() ? *max_instances_row_it\n                                                         : 0;\n    std::vector<F> partial_lagrange_coeffs =\n        this->domain_->EvaluatePartialLagrangeCoefficients(\n            x, base::Range<int32_t>(-range.max,\n                                    static_cast<int32_t>(max_instances_row) +\n                                        std::abs(range.min)));\n\n    return base::Map(instance_columns_vec,\n                     [&instance_queries, &partial_lagrange_coeffs,\n                      &range](const std::vector<Evals>& instance_columns) {\n                       return ComputeInstanceEvals(\n                           instance_columns, instance_queries,\n                           partial_lagrange_coeffs, range.max);\n                     });\n  }\n\n  F EvaluateH(\n      const std::vector<std::vector<Commitment>>& instance_commitments_vec,\n      const VerifyingKey<F, Commitment>& vkey, const Proof& proof) {\n    size_t num_circuits = proof.advices_commitments_vec.size();\n    const ConstraintSystem<F>& constraint_system = vkey.constraint_system();\n    size_t size =\n        GetNumVanishingEvals(num_circuits, constraint_system.gates()) +\n        GetNumPermutationEvals(\n            num_circuits, proof.permutation_product_commitments_vec[0].size()) +\n        lookup::halo2::GetNumEvals(kLookupType, num_circuits,\n                                   constraint_system.lookups().size()) +\n        shuffle::GetNumEvals(num_circuits, constraint_system.shuffles().size());\n    std::vector<F> evals;\n    evals.reserve(size);\n\n    LValues<F> l_values(proof.l_first, proof.l_blind, proof.l_last);\n    for (size_t i = 0; i < num_circuits; ++i) {\n      VanishingVerifierData<F, Commitment> vanishing_verifier_data =\n          proof.ToVanishingVerifierData(i, vkey.fixed_commitments(),\n                                        instance_commitments_vec[i]);\n      VanishingVerifier<F, Commitment> vanishing_verifier(\n          vanishing_verifier_data);\n      vanishing_verifier.Evaluate(constraint_system, evals);\n\n      PermutationVerifierData<F, Commitment> permutation_verifier_data =\n          proof.ToPermutationVerifierData(\n              i, vkey.permutation_verifying_key().commitments());\n      PermutationVerifier<F, Commitment> permutation_verifier(\n          permutation_verifier_data);\n      permutation_verifier.Evaluate(constraint_system, proof.x, l_values,\n                                    evals);\n\n      lookup::Verifier<kLookupType, F, Commitment> lookup_verifier(proof, i,\n                                                                   l_values);\n      lookup_verifier.Evaluate(constraint_system.lookups(), evals);\n\n      shuffle::Verifier<F, Commitment> shuffle_verifier(\n          proof.ToShuffleVerifierData(i), l_values);\n      shuffle_verifier.Evaluate(constraint_system.shuffles(), evals);\n    }\n    DCHECK_EQ(evals.size(), size);\n    F expected_h_eval =\n        F::template LinearCombination</*forward=*/true>(evals, proof.y);\n    CHECK(expected_h_eval /= (proof.x_n - F::One()));\n    return expected_h_eval;\n  }\n\n  std::vector<Opening> Open(\n      const std::vector<std::vector<Commitment>>& instance_commitments_vec,\n      const VerifyingKey<F, Commitment>& vkey, const Proof& proof,\n      Commitment& expected_h_commitment, const F& expected_h_eval) {\n    size_t num_circuits = proof.advices_commitments_vec.size();\n    const ConstraintSystem<F>& constraint_system = vkey.constraint_system();\n    size_t size =\n        GetNumVanishingOpenings<PCS>(\n            num_circuits, constraint_system.advice_queries().size(),\n            constraint_system.instance_queries().size(),\n            constraint_system.fixed_queries().size()) +\n        GetNumPermutationOpenings(\n            num_circuits, proof.permutation_product_commitments_vec[0].size(),\n            vkey.permutation_verifying_key().commitments().size()) +\n        lookup::halo2::GetNumOpenings(kLookupType, num_circuits,\n                                      constraint_system.lookups().size()) +\n        shuffle::GetNumOpenings(num_circuits,\n                                constraint_system.shuffles().size());\n    std::vector<Opening> openings;\n    openings.reserve(size);\n\n    PermutationOpeningPointSet<F> permutation_point_set(proof.x, proof.x_next,\n                                                        proof.x_last);\n    lookup::halo2::OpeningPointSet<F> lookup_point_set(proof.x, proof.x_prev,\n                                                       proof.x_next);\n    shuffle::OpeningPointSet<F> shuffle_point_set(proof.x, proof.x_next);\n\n    for (size_t i = 0; i < num_circuits; ++i) {\n      VanishingVerifierData<F, Commitment> vanishing_verifier_data =\n          proof.ToVanishingVerifierData(i, vkey.fixed_commitments(),\n                                        instance_commitments_vec[i]);\n      VanishingVerifier<F, Commitment> vanishing_verifier(\n          vanishing_verifier_data);\n      vanishing_verifier.template OpenAdviceInstanceColumns<PCS, Poly>(\n          this->domain(), proof.x, constraint_system, openings);\n\n      PermutationVerifierData<F, Commitment> permutation_verifier_data =\n          proof.ToPermutationVerifierData(\n              i, vkey.permutation_verifying_key().commitments());\n      PermutationVerifier<F, Commitment> permutation_verifier(\n          permutation_verifier_data);\n      permutation_verifier.template Open<Poly>(permutation_point_set, openings);\n\n      lookup::Verifier<kLookupType, F, Commitment> lookup_verifier(proof, i);\n      lookup_verifier.Open(lookup_point_set, openings);\n\n      shuffle::Verifier<F, Commitment> shuffle_verifier(\n          proof.ToShuffleVerifierData(i));\n      shuffle_verifier.Open(shuffle_point_set, openings);\n\n      if (i == num_circuits - 1) {\n        vanishing_verifier.template OpenFixedColumns<Poly>(\n            this->domain(), proof.x, constraint_system, openings);\n\n        permutation_verifier.template OpenPermutationProvingKey<Poly>(proof.x,\n                                                                      openings);\n\n        vanishing_verifier.template Open<Poly>(proof.x, proof.x_n,\n                                               expected_h_commitment,\n                                               expected_h_eval, openings);\n      }\n    }\n\n    DCHECK_EQ(openings.size(), size);\n    return openings;\n  }\n\n  bool DoVerify(\n      const std::vector<std::vector<Commitment>>& instance_commitments_vec,\n      const VerifyingKey<F, Commitment>& vkey, const Proof& proof,\n      F* expected_h_eval_out) {\n    F expected_h_eval = EvaluateH(instance_commitments_vec, vkey, proof);\n    if (expected_h_eval_out) {\n      *expected_h_eval_out = expected_h_eval;\n    }\n\n    // TODO(chokobole): Remove |ToAffine()| since this assumes commitment is an\n    // elliptic curve point.\n    Commitment expected_h_commitment;\n    std::vector<Opening> openings =\n        Open(instance_commitments_vec, vkey, proof, expected_h_commitment,\n             expected_h_eval);\n\n    return this->pcs_.VerifyOpeningProof(openings, this->GetReader());\n  }\n};\n\n}  // namespace halo2\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_HALO2_VERIFIER_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/witness_collection.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_HALO2_WITNESS_COLLECTION_H_\n#define TACHYON_ZK_PLONK_HALO2_WITNESS_COLLECTION_H_\n\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"absl/container/btree_map.h\"\n\n#include \"tachyon/base/range.h\"\n#include \"tachyon/zk/plonk/base/phase.h\"\n#include \"tachyon/zk/plonk/layout/assignment.h\"\n\nnamespace tachyon::zk::plonk::halo2 {\n\ntemplate <typename Evals, typename RationalEvals>\nclass WitnessCollection : public Assignment<typename Evals::Field> {\n public:\n  using F = typename Evals::Field;\n  using AssignCallback = typename Assignment<F>::AssignCallback;\n\n  WitnessCollection() = default;\n  template <typename Domain>\n  WitnessCollection(const Domain* domain, size_t num_advice_columns,\n                    RowIndex usable_rows, Phase current_phase,\n                    const absl::btree_map<size_t, F>& challenges,\n                    const std::vector<Evals>& instance_columns)\n      : advices_(std::vector<RationalEvals>(\n            num_advice_columns, domain->template Zero<RationalEvals>())),\n        usable_rows_(base::Range<RowIndex>::Until(usable_rows)),\n        current_phase_(current_phase),\n        challenges_(challenges),\n        instance_columns_(instance_columns) {}\n\n  // NOTE(dongchangYoo): This getter of |advices| transfers ownership as well.\n  // That's why, |WitnessCollection| will be released as soon as emitting it.\n  std::vector<RationalEvals>&& TakeAdvices() && { return std::move(advices_); }\n\n  Value<F> QueryInstance(const InstanceColumnKey& column,\n                         RowIndex row) override {\n    CHECK(usable_rows_.Contains(row));\n    CHECK_LT(column.index(), instance_columns_.size());\n\n    return Value<F>::Known(instance_columns_[column.index()][row]);\n  }\n\n  void AssignAdvice(std::string_view, const AdviceColumnKey& column,\n                    RowIndex row, AssignCallback assign) override {\n    // Ignore assignment of advice column in different phase than current one.\n    if (current_phase_ < column.phase()) return;\n\n    CHECK(usable_rows_.Contains(row));\n    CHECK_LT(column.index(), advices_.size());\n\n    // NOTE(chokobole): Boundary check is the responsibility of API callers.\n    advices_[column.index()].at(row) = std::move(assign).Run().value();\n  }\n\n  Value<F> GetChallenge(Challenge challenge) override {\n    if (challenges_.find(challenge.index()) != challenges_.end())\n      return Value<F>::Known(challenges_[challenge.index()]);\n    else\n      return Value<F>::Unknown();\n  }\n\n private:\n  std::vector<RationalEvals> advices_;\n  base::Range<RowIndex> usable_rows_;\n  Phase current_phase_;\n  absl::btree_map<size_t, F> challenges_;\n  std::vector<Evals> instance_columns_;\n};\n\n}  // namespace tachyon::zk::plonk::halo2\n\n#endif  // TACHYON_ZK_PLONK_HALO2_WITNESS_COLLECTION_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/halo2/witness_collection_unittest.cc",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#include \"tachyon/zk/plonk/halo2/witness_collection.h\"\n\n#include <memory>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/base/rational_field.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/fr.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluation_domain_factory.h\"\n#include \"tachyon/zk/base/value.h\"\n\nnamespace tachyon::zk::plonk::halo2 {\nnamespace {\n\nusing F = math::bn254::Fr;\n\nclass WitnessCollectionTest : public math::FiniteFieldTest<F> {\n public:\n  constexpr static RowIndex N = 32;\n  constexpr static size_t kMaxDegree = N - 1;\n  constexpr static size_t kNumAdviceColumns = 3;\n  constexpr static RowIndex kBlindingFactors = 5;\n  constexpr static RowIndex kUsableRows = N - (kBlindingFactors + 1);\n\n  using Domain = math::UnivariateEvaluationDomain<F, kMaxDegree>;\n  using Evals = Domain::Evals;\n  using RationalEvals =\n      math::UnivariateEvaluations<math::RationalField<F>, kMaxDegree>;\n\n  void SetUp() override {\n    std::unique_ptr<Domain> domain = Domain::Create(N);\n\n    // There is a single challenge in |expected_challenges_|.\n    expected_challenges_[0] = F::Random();\n    expected_instance_columns_ = {domain->Random<Evals>()};\n\n    Phase current_phase(0);\n    witness_collection_ = WitnessCollection<Evals, RationalEvals>(\n        domain.get(), kNumAdviceColumns, kUsableRows, current_phase,\n        expected_challenges_, expected_instance_columns_);\n  }\n\n protected:\n  absl::btree_map<size_t, F> expected_challenges_;\n  std::vector<Evals> expected_instance_columns_;\n  WitnessCollection<Evals, RationalEvals> witness_collection_;\n};\n\n}  // namespace\n\nTEST_F(WitnessCollectionTest, QueryInstance) {\n  size_t col = 0;\n  RowIndex row = 10;\n\n  // Query a value in specific instance column.\n  Value<F> queried_value =\n      witness_collection_.QueryInstance(InstanceColumnKey(col), row);\n  EXPECT_EQ(queried_value,\n            Value<F>::Known(expected_instance_columns_[col][row]));\n}\n\nTEST_F(WitnessCollectionTest, AssignAdvice) {\n  math::RationalField<F> value_to_be_assign(F::Random());\n  size_t col = 0;\n  RowIndex row = 10;\n\n  // Assign a random value to specific cell.\n  witness_collection_.AssignAdvice(\n      \"\", AdviceColumnKey(col), row, [value_to_be_assign]() {\n        return Value<math::RationalField<F>>::Known(value_to_be_assign);\n      });\n\n  std::vector<RationalEvals> rational_advice_columns =\n      std::move(witness_collection_).TakeAdvices();\n  const RationalEvals& rational_column = rational_advice_columns[col];\n  EXPECT_EQ(value_to_be_assign, rational_column[row]);\n}\n\nTEST_F(WitnessCollectionTest, GetChallenge) {\n  // Get challenge value.\n  size_t target_idx = 0;\n  Value<F> challenge =\n      witness_collection_.GetChallenge(Challenge(target_idx, kFirstPhase));\n  EXPECT_EQ(Value<F>::Known(expected_challenges_[target_idx]), challenge);\n}\n\n}  // namespace tachyon::zk::plonk::halo2\n"
  },
  {
    "path": "tachyon/zk/plonk/keys/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"assembly\",\n    hdrs = [\"assembly.h\"],\n    deps = [\n        \"//tachyon/base:logging\",\n        \"//tachyon/base:range\",\n        \"//tachyon/math/base:rational_field\",\n        \"//tachyon/zk/plonk/layout:assignment\",\n        \"//tachyon/zk/plonk/permutation:permutation_assembly\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"c_proving_key_impl_forward\",\n    hdrs = [\"c_proving_key_impl_forward.h\"],\n)\n\ntachyon_cc_library(\n    name = \"key\",\n    hdrs = [\"key.h\"],\n    deps = [\n        \":assembly\",\n        \"//tachyon:export\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/zk/base/entities:entity\",\n        \"//tachyon/zk/plonk/constraint_system\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"proving_key\",\n    hdrs = [\"proving_key.h\"],\n    deps = [\n        \":verifying_key\",\n        \"//tachyon/base:openmp_util\",\n        \"//tachyon/zk/base/entities:prover_base\",\n        \"//tachyon/zk/plonk/halo2:vendor\",\n        \"//tachyon/zk/plonk/permutation:permutation_proving_key\",\n        \"//tachyon/zk/plonk/vanishing:vanishing_argument\",\n        \"//tachyon/zk/plonk/vanishing:vanishing_utils\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"proving_key_forward\",\n    hdrs = [\"proving_key_forward.h\"],\n)\n\ntachyon_cc_library(\n    name = \"verifying_key\",\n    hdrs = [\"verifying_key.h\"],\n    deps = [\n        \":c_proving_key_impl_forward\",\n        \":key\",\n        \":proving_key_forward\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base/strings:rust_stringifier\",\n        \"//tachyon/zk/plonk/halo2:constants\",\n        \"//tachyon/zk/plonk/halo2:pinned_verifying_key_forward\",\n        \"//tachyon/zk/plonk/halo2:prime_field_conversion\",\n        \"//tachyon/zk/plonk/permutation:permutation_verifying_key\",\n        \"@com_google_boringssl//:crypto\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/zk/plonk/keys/assembly.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_KEYS_ASSEMBLY_H_\n#define TACHYON_ZK_PLONK_KEYS_ASSEMBLY_H_\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/range.h\"\n#include \"tachyon/math/base/rational_field.h\"\n#include \"tachyon/zk/plonk/layout/assignment.h\"\n#include \"tachyon/zk/plonk/permutation/permutation_assembly.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename RationalEvals>\nclass Assembly : public Assignment<typename RationalEvals::Field::InnerField> {\n public:\n  using F = typename RationalEvals::Field::InnerField;\n  using AssignCallback = typename Assignment<F>::AssignCallback;\n\n  Assembly() = default;\n  Assembly(std::vector<RationalEvals>&& fixed_columns,\n           PermutationAssembly&& permutation,\n           std::vector<std::vector<bool>>&& selectors,\n           base::Range<RowIndex> usable_rows)\n      : fixed_columns_(std::move(fixed_columns)),\n        permutation_(std::move(permutation)),\n        selectors_(std::move(selectors)),\n        usable_rows_(usable_rows) {}\n\n  const std::vector<RationalEvals>& fixed_columns() const {\n    return fixed_columns_;\n  }\n  const PermutationAssembly& permutation() const { return permutation_; }\n  const std::vector<std::vector<bool>>& selectors() const { return selectors_; }\n  const base::Range<RowIndex>& usable_rows() const { return usable_rows_; }\n\n  // Assignment methods\n  void EnableSelector(std::string_view, Selector selector,\n                      RowIndex row) override {\n    CHECK(usable_rows_.Contains(row)) << \"Not enough rows available\";\n    selectors_[selector.index()][row] = true;\n  }\n\n  Value<F> QueryInstance(const InstanceColumnKey& column,\n                         RowIndex row) override {\n    CHECK(usable_rows_.Contains(row)) << \"Not enough rows available\";\n    return Value<F>::Unknown();\n  }\n\n  void AssignFixed(std::string_view, const FixedColumnKey& column, RowIndex row,\n                   AssignCallback assign) override {\n    CHECK(usable_rows_.Contains(row)) << \"Not enough rows available\";\n    // NOTE(chokobole): Boundary check is the responsibility of API callers.\n    fixed_columns_[column.index()].at(row) = std::move(assign).Run().value();\n  }\n\n  void Copy(const AnyColumnKey& left_column, RowIndex left_row,\n            const AnyColumnKey& right_column, RowIndex right_row) override {\n    CHECK(usable_rows_.Contains(left_row) && usable_rows_.Contains(right_row))\n        << \"Not enough rows available\";\n    permutation_.Copy(left_column, left_row, right_column, right_row);\n  }\n\n  void FillFromRow(const FixedColumnKey& column, RowIndex from_row,\n                   const math::RationalField<F>& value) override {\n    CHECK(usable_rows_.Contains(from_row)) << \"Not enough rows available\";\n    base::Range<RowIndex> range(usable_rows_.from + from_row, usable_rows_.to);\n    RationalEvals& fixed_column = fixed_columns_[column.index()];\n    for (RowIndex row : range) {\n      // NOTE(chokobole): Boundary check is the responsibility of API callers.\n      fixed_column.at(row) = value;\n    }\n  }\n\n private:\n  std::vector<RationalEvals> fixed_columns_;\n  PermutationAssembly permutation_;\n  std::vector<std::vector<bool>> selectors_;\n  // A range of available rows for assignment and copies.\n  base::Range<RowIndex> usable_rows_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_KEYS_ASSEMBLY_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/keys/c_proving_key_impl_forward.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_KEYS_C_PROVING_KEY_IMPL_FORWARD_H_\n#define TACHYON_ZK_PLONK_KEYS_C_PROVING_KEY_IMPL_FORWARD_H_\n\nnamespace tachyon::c::zk::plonk {\n\ntemplate <typename PS>\nclass ProvingKeyImpl;\n\n}  // namespace tachyon::c::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_KEYS_C_PROVING_KEY_IMPL_FORWARD_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/keys/key.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_KEYS_KEY_H_\n#define TACHYON_ZK_PLONK_KEYS_KEY_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/export.h\"\n#include \"tachyon/zk/base/entities/entity.h\"\n#include \"tachyon/zk/plonk/constraint_system/constraint_system.h\"\n#include \"tachyon/zk/plonk/keys/assembly.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename Evals, typename RationalEvals>\nstruct KeyPreLoadResult {\n  using F = typename Evals::Field;\n\n  ConstraintSystem<F> constraint_system;\n  Assembly<RationalEvals> assembly;\n  std::vector<Evals> fixed_columns;\n\n  KeyPreLoadResult() = default;\n  explicit KeyPreLoadResult(lookup::Type lookup_type) {\n    constraint_system.set_lookup_type(lookup_type);\n  }\n};\n\nclass TACHYON_EXPORT Key {\n public:\n  template <typename RationalEvals, typename Domain, typename F>\n  static Assembly<RationalEvals> CreateAssembly(\n      const Domain* domain, const ConstraintSystem<F>& constraint_system) {\n    // NOTE(chokobole): It's safe to downcast because domain is already checked.\n    RowIndex n = static_cast<RowIndex>(domain->size());\n    return {\n        std::vector<RationalEvals>(constraint_system.num_fixed_columns(),\n                                   domain->template Zero<RationalEvals>()),\n        PermutationAssembly(constraint_system.permutation(), n),\n        std::vector<std::vector<bool>>(constraint_system.GetNumSelectors(),\n                                       std::vector<bool>(n, false)),\n        // NOTE(chokobole): Considering that this is called from a verifier,\n        // then you can't load this number through |prover->GetUsableRows()|.\n        base::Range<RowIndex>::Until(\n            n - (constraint_system.ComputeBlindingFactors() + 1))};\n  }\n\n protected:\n  template <typename PCS, typename Circuit, typename Evals,\n            typename RationalEvals>\n  bool PreLoad(Entity<PCS>* entity, const Circuit& circuit,\n               KeyPreLoadResult<Evals, RationalEvals>* result) {\n    using F = typename Evals::Field;\n    using Config = typename Circuit::Config;\n    using FloorPlanner = typename Circuit::FloorPlanner;\n    using ExtendedDomain = typename PCS::ExtendedDomain;\n\n    ConstraintSystem<F>& constraint_system = result->constraint_system;\n    Config config = Circuit::Configure(constraint_system);\n\n    if (constraint_system.lookup_type() == lookup::Type::kLogDerivativeHalo2) {\n      constraint_system.ChunkLookups();\n    }\n\n    PCS& pcs = entity->pcs();\n    if (pcs.N() < constraint_system.ComputeMinimumRows()) {\n      LOG(ERROR) << \"Not enough rows available \" << pcs.N() << \" vs \"\n                 << constraint_system.ComputeMinimumRows();\n      return false;\n    }\n    uint32_t extended_k = constraint_system.ComputeExtendedK(pcs.K());\n    size_t extended_n = size_t{1} << extended_k;\n    entity->set_extended_domain(ExtendedDomain::Create(extended_n));\n#if TACHYON_CUDA\n    entity->EnableIcicleNTT();\n#endif\n\n    result->assembly =\n        CreateAssembly<RationalEvals>(entity->domain(), constraint_system);\n    Assembly<RationalEvals>& assembly = result->assembly;\n    FloorPlanner floor_planner;\n    floor_planner.Synthesize(&assembly, circuit, std::move(config),\n                             constraint_system.constants());\n\n    result->fixed_columns =\n        base::Map(assembly.fixed_columns(), [](const RationalEvals& evals) {\n          // NOTE(batzor): This vector is initialized below in |BatchEvaluate|\n          // so it is safe to keep it uninitialized here.\n          std::vector<F> result(evals.evaluations().size());\n          CHECK(math::RationalField<F>::BatchEvaluate(evals.evaluations(),\n                                                      &result));\n          return Evals(std::move(result));\n        });\n    std::vector<Evals>& fixed_columns = result->fixed_columns;\n\n    std::vector<std::vector<F>> selector_polys_tmp =\n        constraint_system.CompressSelectors(assembly.selectors());\n    std::vector<Evals> selector_polys =\n        base::Map(std::make_move_iterator(selector_polys_tmp.begin()),\n                  std::make_move_iterator(selector_polys_tmp.end()),\n                  [](std::vector<F>&& vec) { return Evals(std::move(vec)); });\n    base::Extend(fixed_columns, std::move(selector_polys));\n\n    return true;\n  }\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_KEYS_KEY_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/keys/proving_key.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_KEYS_PROVING_KEY_H_\n#define TACHYON_ZK_PLONK_KEYS_PROVING_KEY_H_\n\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/openmp_util.h\"\n#include \"tachyon/zk/base/entities/prover_base.h\"\n#include \"tachyon/zk/plonk/halo2/vendor.h\"\n#include \"tachyon/zk/plonk/keys/verifying_key.h\"\n#include \"tachyon/zk/plonk/permutation/permutation_proving_key.h\"\n#include \"tachyon/zk/plonk/vanishing/vanishing_argument.h\"\n#include \"tachyon/zk/plonk/vanishing/vanishing_utils.h\"\n\nnamespace tachyon {\n\nnamespace zk::plonk {\n\ntemplate <typename PS>\nclass ProvingKey : public Key {\n public:\n  constexpr static halo2::Vendor kVendor = PS::kVendor;\n  constexpr static lookup::Type kLookupType = PS::kLookupType;\n\n  using PCS = typename PS::PCS;\n  using F = typename PCS::Field;\n  using Poly = typename PCS::Poly;\n  using Evals = typename PCS::Evals;\n  using RationalEvals = typename PCS::RationalEvals;\n  using ExtendedEvals = typename PCS::ExtendedEvals;\n  using C = typename PCS::Commitment;\n\n  using PolyOrExtendedEvals =\n      std::conditional_t<kVendor == halo2::Vendor::kPSE, ExtendedEvals, Poly>;\n\n  ProvingKey() = default;\n\n  const VerifyingKey<F, C>& verifying_key() const { return verifying_key_; }\n  const PolyOrExtendedEvals& l_first() const { return l_first_; }\n  const PolyOrExtendedEvals& l_last() const { return l_last_; }\n  const PolyOrExtendedEvals& l_active_row() const { return l_active_row_; }\n  const std::vector<Evals>& fixed_columns() const { return fixed_columns_; }\n  std::vector<Evals>& fixed_columns() { return fixed_columns_; }\n  const std::vector<Poly>& fixed_polys() const { return fixed_polys_; }\n  const std::vector<PolyOrExtendedEvals>& fixed_cosets() const {\n    return fixed_cosets_;\n  }\n  const PermutationProvingKey<Poly, Evals, ExtendedEvals>&\n  permutation_proving_key() const {\n    return permutation_proving_key_;\n  }\n\n  // Return true if it is able to load from an instance of |circuit|.\n  template <typename Circuit>\n  [[nodiscard]] bool Load(ProverBase<PCS>* prover, const Circuit& circuit) {\n    KeyPreLoadResult<Evals, RationalEvals> pre_load_result(kLookupType);\n    if (!this->PreLoad(prover, circuit, &pre_load_result)) return false;\n    VerifyingKeyLoadResult<Evals> vk_result;\n    if (!verifying_key_.DoLoad(prover, std::move(pre_load_result), &vk_result))\n      return false;\n    return DoLoad(prover, std::move(pre_load_result), &vk_result);\n  }\n\n  // Return true if it is able to load from an instance of |circuit| and a\n  // |verifying_key|.\n  template <typename Circuit>\n  [[nodiscard]] bool LoadWithVerifyingKey(ProverBase<PCS>* prover,\n                                          const Circuit& circuit,\n                                          VerifyingKey<F, C>&& verifying_key) {\n    KeyPreLoadResult<Evals, RationalEvals> pre_load_result(kLookupType);\n    if (!this->PreLoad(prover, circuit, &pre_load_result)) return false;\n    verifying_key_ = std::move(verifying_key);\n    return DoLoad(prover, std::move(pre_load_result), nullptr);\n  }\n\n private:\n  friend class c::zk::plonk::ProvingKeyImpl<PS>;\n\n  bool DoLoad(ProverBase<PCS>* prover,\n              KeyPreLoadResult<Evals, RationalEvals>&& pre_load_result,\n              VerifyingKeyLoadResult<Evals>* vk_load_result) {\n    using Domain = typename PCS::Domain;\n\n    // NOTE(chokobole): |ComputeBlindingFactors()| is a second call. The first\n    // was called inside |PreLoad()|. But it's okay since this is cheap to\n    // compute.\n    prover->blinder().set_blinding_factors(\n        verifying_key_.constraint_system().ComputeBlindingFactors());\n\n    const Domain* domain = prover->domain();\n    fixed_columns_ = std::move(pre_load_result.fixed_columns);\n    fixed_polys_ = base::Map(fixed_columns_, [domain](const Evals& evals) {\n      return domain->IFFT(evals);\n    });\n\n    std::vector<Evals> permutations;\n    if (vk_load_result) {\n      permutations = std::move(vk_load_result->permutations);\n    } else {\n      permutations = pre_load_result.assembly.permutation()\n                         .template GeneratePermutations<Evals>(domain);\n    }\n\n    permutation_proving_key_ =\n        pre_load_result.assembly.permutation()\n            .template BuildProvingKey<kVendor>(prover, std::move(permutations));\n\n    // Compute l_first(X).\n    // if |blinding_factors| = 3 and |pcs.N()| = 8,\n    //\n    // | X | l_first(X) |\n    // |---|------------|\n    // | 0 | 1          |\n    // | 1 | 0          |\n    // | 2 | 0          |\n    // | 3 | 0          |\n    // | 4 | 0          |\n    // | 5 | 0          |\n    // | 6 | 0          |\n    // | 7 | 0          |\n    Evals evals = domain->template Zero<Evals>();\n    // NOTE(chokobole): It's safe to access since we created |domain->size()|\n    // |evals|.\n    evals.at(0) = F::One();\n    Poly l_first = domain->IFFT(evals);\n    evals.at(0) = F::Zero();\n\n    // Compute l_last(X) which evaluates to 1 on the first inactive row (just\n    // before the blinding factors) and 0 otherwise over the domain.\n    //\n    // | X | l_last(X) |\n    // |---|-----------|\n    // | 0 | 0         |\n    // | 1 | 0         |\n    // | 2 | 0         |\n    // | 3 | 0         |\n    // | 4 | 1         |\n    // | 5 | 0         |\n    // | 6 | 0         |\n    // | 7 | 0         |\n    RowIndex usable_rows = prover->GetUsableRows();\n    // NOTE(chokobole): It's safe to access since we created |domain->size()|\n    // |evals|, which is greater than |usable_rows|.\n    evals.at(usable_rows) = F::One();\n    Poly l_last = domain->IFFT(evals);\n    evals.at(usable_rows) = F::Zero();\n\n    // Compute l_active_row(X).\n    //\n    // | X | l_active_row(X) |\n    // |---|-----------------|\n    // | 0 | 1               |\n    // | 1 | 1               |\n    // | 2 | 1               |\n    // | 3 | 1               |\n    // | 4 | 0               |\n    // | 5 | 0               |\n    // | 6 | 0               |\n    // | 7 | 0               |\n    OMP_PARALLEL_FOR(size_t i = 0; i < usable_rows; ++i) {\n      // NOTE(chokobole): It's safe to access since we created |domain->size()|\n      // |evals|, which is greater than |usable_rows|.\n      evals.at(i) = F::One();\n    }\n    Poly l_active_row = domain->IFFT(std::move(evals));\n\n    if constexpr (kVendor == halo2::Vendor::kPSE) {\n      using ExtendedDomain = typename PCS::ExtendedDomain;\n\n      const ExtendedDomain* extended_domain = prover->extended_domain();\n      l_first_ = CoeffToExtended(std::move(l_first), extended_domain);\n      l_last_ = CoeffToExtended(std::move(l_last), extended_domain);\n      l_active_row_ = CoeffToExtended(std::move(l_active_row), extended_domain);\n\n      fixed_cosets_ =\n          base::Map(fixed_polys_, [domain, extended_domain](const Poly& poly) {\n            return CoeffToExtended(poly, extended_domain);\n          });\n    } else {\n      l_first_ = std::move(l_first);\n      l_last_ = std::move(l_last);\n      l_active_row_ = std::move(l_active_row);\n    }\n\n    vanishing_argument_ =\n        VanishingArgument<PS>::Create(verifying_key_.constraint_system());\n    return true;\n  }\n\n  VerifyingKey<F, C> verifying_key_;\n  PolyOrExtendedEvals l_first_;\n  PolyOrExtendedEvals l_last_;\n  PolyOrExtendedEvals l_active_row_;\n  std::vector<Evals> fixed_columns_;\n  std::vector<Poly> fixed_polys_;\n  // NOTE(chokobole): Only PSE Halo2 has the member |fixed_cosets_|.\n  // See below:\n  // https://github.com/privacy-scaling-explorations/halo2/blob/bc857a7/halo2_backend/src/plonk.rs#L260-L270\n  // https://github.com/scroll-tech/halo2/blob/1070391/halo2_proofs/src/plonk.rs#L263-L272\n  std::vector<ExtendedEvals> fixed_cosets_;\n  PermutationProvingKey<Poly, Evals, ExtendedEvals> permutation_proving_key_;\n  VanishingArgument<PS> vanishing_argument_;\n};\n\n}  // namespace zk::plonk\n}  // namespace tachyon\n\n#endif  // TACHYON_ZK_PLONK_KEYS_PROVING_KEY_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/keys/proving_key_forward.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_KEYS_PROVING_KEY_FORWARD_H_\n#define TACHYON_ZK_PLONK_KEYS_PROVING_KEY_FORWARD_H_\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename PS>\nclass ProvingKey;\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_KEYS_PROVING_KEY_FORWARD_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/keys/verifying_key.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_KEYS_VERIFYING_KEY_H_\n#define TACHYON_ZK_PLONK_KEYS_VERIFYING_KEY_H_\n\n#include <stddef.h>\n\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"openssl/blake2.h\"\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/strings/rust_stringifier.h\"\n#include \"tachyon/zk/plonk/halo2/constants.h\"\n#include \"tachyon/zk/plonk/halo2/pinned_verifying_key_forward.h\"\n#include \"tachyon/zk/plonk/halo2/prime_field_conversion.h\"\n#include \"tachyon/zk/plonk/keys/c_proving_key_impl_forward.h\"\n#include \"tachyon/zk/plonk/keys/key.h\"\n#include \"tachyon/zk/plonk/keys/proving_key_forward.h\"\n#include \"tachyon/zk/plonk/permutation/permutation_verifying_key.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename Evals>\nstruct VerifyingKeyLoadResult {\n  std::vector<Evals> permutations;\n};\n\ntemplate <typename F, typename C>\nclass VerifyingKey : public Key {\n public:\n  using Commitments = std::vector<C>;\n\n  VerifyingKey() = default;\n\n  const Commitments& fixed_commitments() const { return fixed_commitments_; }\n\n  const PermutationVerifyingKey<C>& permutation_verifying_key() const {\n    return permutation_verifying_key_;\n  }\n\n  const ConstraintSystem<F>& constraint_system() const {\n    return constraint_system_;\n  }\n\n  const F& transcript_repr() const { return transcript_repr_; }\n\n  void SetTranscriptReprForTesting(const F& transcript_repr) {\n    transcript_repr_ = transcript_repr;\n  }\n\n  // Return true if it is able to load from an instance of |circuit|.\n  template <typename PCS, typename Circuit>\n  [[nodiscard]] bool Load(Entity<PCS>* entity, const Circuit& circuit,\n                          lookup::Type lookup_type) {\n    using Evals = typename PCS::Evals;\n    using RationalEvals = typename PCS::RationalEvals;\n    KeyPreLoadResult<Evals, RationalEvals> result(lookup_type);\n    if (!this->PreLoad(entity, circuit, &result)) return false;\n    // NOTE(chokobole): This helps the compiler to infer type easily.\n    // Without this line, it won't compile.\n    VerifyingKeyLoadResult<Evals>* load_result = nullptr;\n    return DoLoad(entity, std::move(result), load_result);\n  }\n\n private:\n  template <typename PS>\n  friend class ProvingKey;\n  template <typename PS>\n  friend class c::zk::plonk::ProvingKeyImpl;\n\n  template <typename PCS, typename Evals, typename RationalEvals>\n  bool DoLoad(Entity<PCS>* entity,\n              KeyPreLoadResult<Evals, RationalEvals>&& pre_load_result,\n              VerifyingKeyLoadResult<Evals>* load_result) {\n    constraint_system_ = std::move(pre_load_result.constraint_system);\n\n    std::vector<Evals> permutations =\n        pre_load_result.assembly.permutation()\n            .template GeneratePermutations<Evals>(entity->domain());\n    permutation_verifying_key_ =\n        pre_load_result.assembly.permutation().BuildVerifyingKey(entity,\n                                                                 permutations);\n    if (load_result) {\n      load_result->permutations = std::move(permutations);\n    }\n\n    const PCS& pcs = entity->pcs();\n    fixed_commitments_ =\n        base::Map(pre_load_result.fixed_columns, [&pcs](const Evals& evals) {\n          C commitment;\n          CHECK(pcs.CommitLagrange(evals, &commitment));\n          return commitment;\n        });\n\n    SetTranscriptRepresentative(entity);\n    return true;\n  }\n\n  template <typename PCS>\n  void SetTranscriptRepresentative(const Entity<PCS>* entity) {\n    halo2::PinnedVerifyingKey<F, C> pinned_verifying_key(entity, *this);\n\n    std::string vk_str = base::ToRustDebugString(pinned_verifying_key);\n    VLOG(3) << vk_str;\n    size_t vk_str_size = vk_str.size();\n\n    BLAKE2B_CTX state;\n    BLAKE2B512_InitWithPersonal(&state, halo2::kVerifyingKeyStr);\n    BLAKE2B512_Update(&state, reinterpret_cast<const uint8_t*>(&vk_str_size),\n                      sizeof(size_t));\n    BLAKE2B512_Update(&state, vk_str.data(), vk_str.size());\n    uint8_t result[BLAKE2B512_DIGEST_LENGTH] = {0};\n    BLAKE2B512_Final(result, &state);\n\n    transcript_repr_ = halo2::FromUint512<F>(result);\n    VLOG(2) << \"Halo2(transcript_repr): \" << transcript_repr_.ToHexString(true);\n  }\n\n  Commitments fixed_commitments_;\n  PermutationVerifyingKey<C> permutation_verifying_key_;\n  ConstraintSystem<F> constraint_system_;\n  // The representative of this |VerifyingKey| in transcripts.\n  F transcript_repr_ = F::Zero();\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_KEYS_VERIFYING_KEY_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/layout/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"assigned_cell\",\n    hdrs = [\"assigned_cell.h\"],\n    deps = [\n        \":cell\",\n        \"//tachyon/zk/base:row_types\",\n        \"//tachyon/zk/base:value\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"assignment\",\n    hdrs = [\"assignment.h\"],\n    deps = [\n        \"//tachyon/base/functional:callback\",\n        \"//tachyon/math/base:rational_field\",\n        \"//tachyon/zk/base:row_types\",\n        \"//tachyon/zk/base:value\",\n        \"//tachyon/zk/plonk/base:column_key\",\n        \"//tachyon/zk/plonk/constraint_system:challenge\",\n        \"//tachyon/zk/plonk/constraint_system:selector\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"cell\",\n    hdrs = [\"cell.h\"],\n    deps = [\n        \"//tachyon:export\",\n        \"//tachyon/zk/base:row_types\",\n        \"//tachyon/zk/plonk/base:column_key\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"layouter\",\n    hdrs = [\n        \"layouter.h\",\n        \"namespaced_layouter.h\",\n    ],\n    deps = [\n        \":lookup_table\",\n        \":region\",\n        \"//tachyon/base/functional:callback\",\n        \"//tachyon/zk/base:row_types\",\n        \"//tachyon/zk/plonk/constraint_system\",\n        \"//tachyon/zk/plonk/constraint_system:challenge\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"lookup_table\",\n    hdrs = [\"lookup_table.h\"],\n    deps = [\n        \":lookup_table_column\",\n        \"//tachyon/math/base:rational_field\",\n        \"//tachyon/zk/base:row_types\",\n        \"//tachyon/zk/base:value\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"lookup_table_column\",\n    hdrs = [\"lookup_table_column.h\"],\n    deps = [\n        \"//tachyon:export\",\n        \"//tachyon/zk/plonk/base:column_key\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"region\",\n    hdrs = [\"region.h\"],\n    deps = [\n        \":assigned_cell\",\n        \":region_layouter\",\n        \"//tachyon/base/functional:callback\",\n        \"//tachyon/zk/plonk/base:column_key\",\n        \"//tachyon/zk/plonk/constraint_system:selector\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"region_column\",\n    hdrs = [\"region_column.h\"],\n    deps = [\n        \"//tachyon/zk/plonk/base:column_key\",\n        \"//tachyon/zk/plonk/constraint_system:selector\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"region_layouter\",\n    hdrs = [\"region_layouter.h\"],\n    deps = [\n        \":assigned_cell\",\n        \"//tachyon/base/functional:callback\",\n        \"//tachyon/math/base:rational_field\",\n        \"//tachyon/zk/base:row_types\",\n        \"//tachyon/zk/base:value\",\n        \"//tachyon/zk/plonk/base:column_key\",\n        \"//tachyon/zk/plonk/constraint_system:selector\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"region_shape\",\n    hdrs = [\"region_shape.h\"],\n    deps = [\n        \":region\",\n        \":region_column\",\n        \"@com_google_absl//absl/container:flat_hash_set\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"circuit_unittests\",\n    srcs = [\n        \"lookup_table_column_unittest.cc\",\n        \"region_column_unittest.cc\",\n    ],\n    deps = [\n        \":lookup_table_column\",\n        \":region_column\",\n        \"@com_google_absl//absl/hash:hash_testing\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/zk/plonk/layout/assigned_cell.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_LAYOUT_ASSIGNED_CELL_H_\n#define TACHYON_ZK_PLONK_LAYOUT_ASSIGNED_CELL_H_\n\n#include <string>\n#include <utility>\n\n#include \"tachyon/zk/base/row_types.h\"\n#include \"tachyon/zk/base/value.h\"\n#include \"tachyon/zk/plonk/layout/cell.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F>\nclass Region;\n\n// An assigned cell.\ntemplate <typename F>\nclass AssignedCell {\n public:\n  AssignedCell() = default;\n  AssignedCell(const Cell& cell, const Value<F>& value)\n      : cell_(cell), value_(value) {}\n  AssignedCell(const Cell& cell, Value<F>&& value)\n      : cell_(cell), value_(std::move(value)) {}\n\n  const Cell& cell() const { return cell_; }\n  const Value<F>& value() const { return value_; }\n\n  // Copies the value to a given advice cell and constrains them to be equal.\n  AssignedCell<F> CopyAdvice(std::string_view, Region<F>& region,\n                             const AdviceColumnKey& column,\n                             RowIndex offset) const;\n\n  std::string ToString() const {\n    return absl::Substitute(\"{cell: $0, value: $1}\", cell_.ToString(),\n                            value_.ToString());\n  }\n\n private:\n  Cell cell_;\n  Value<F> value_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_LAYOUT_ASSIGNED_CELL_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/layout/assignment.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_LAYOUT_ASSIGNMENT_H_\n#define TACHYON_ZK_PLONK_LAYOUT_ASSIGNMENT_H_\n\n#include <optional>\n#include <string>\n\n#include \"tachyon/base/functional/callback.h\"\n#include \"tachyon/math/base/rational_field.h\"\n#include \"tachyon/zk/base/row_types.h\"\n#include \"tachyon/zk/base/value.h\"\n#include \"tachyon/zk/plonk/base/column_key.h\"\n#include \"tachyon/zk/plonk/constraint_system/challenge.h\"\n#include \"tachyon/zk/plonk/constraint_system/selector.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F>\nclass Assignment {\n public:\n  using AssignCallback = base::OnceCallback<Value<math::RationalField<F>>()>;\n\n  virtual ~Assignment() = default;\n\n  // Creates a new region and enters into it.\n  //\n  // Crashes if we are currently in a region (if |ExitRegion()| was not called).\n  //\n  // Not intended for downstream consumption; use |Layouter::AssignRegion()|\n  // instead.\n  virtual void EnterRegion(std::string_view name) {}\n\n  // Allows the developer to include a |name| for a specific column\n  // within a |Region|.\n  //\n  // This is usually useful for debugging circuit failures.\n  virtual void NameColumn(std::string_view name, const AnyColumnKey& column) {}\n\n  // Exits the current region.\n  //\n  // Crashes if we are not currently in a region (if |EnterRegion()| was not\n  // called).\n  //\n  // Not intended for downstream consumption; use |Layouter::AssignRegion()|\n  // instead.\n  virtual void ExitRegion() {}\n\n  // Enables a selector at the given row.\n  virtual void EnableSelector(std::string_view name, Selector selector,\n                              RowIndex row) {}\n\n  // Queries the cell of an instance column at a particular absolute row.\n  virtual Value<F> QueryInstance(const InstanceColumnKey& column,\n                                 RowIndex row) {\n    return Value<F>();\n  }\n\n  // Assign an advice column value (witness).\n  virtual void AssignAdvice(std::string_view name,\n                            const AdviceColumnKey& column, RowIndex row,\n                            AssignCallback assign) {}\n\n  // Assign a fixed value.\n  virtual void AssignFixed(std::string_view name, const FixedColumnKey& column,\n                           RowIndex row, AssignCallback assign) {}\n\n  // Assign two cells to have the same value\n  virtual void Copy(const AnyColumnKey& left_column, RowIndex left_row,\n                    const AnyColumnKey& right_column, RowIndex right_row) {}\n\n  // Fills a fixed |column| starting from the given |row| with |value|.\n  virtual void FillFromRow(const FixedColumnKey& column, RowIndex row,\n                           const math::RationalField<F>& value) {}\n\n  // Queries the value of the given challenge.\n  //\n  // Returns |Value<F>::Unknown()| if the current synthesis phase\n  // is before the challenge can be queried.\n  virtual Value<F> GetChallenge(Challenge challenge) {\n    return Value<F>::Unknown();\n  }\n\n  // Creates a new (sub)namespace and enters into it.\n  //\n  // Not intended for downstream consumption; use |Layouter::Namespace()|\n  // instead.\n  virtual void PushNamespace(std::string_view name) {}\n\n  // Exits out of the existing namespace.\n  //\n  // Not intended for downstream consumption; use |Layouter::Namespace()|\n  // instead.\n  virtual void PopNamespace(const std::optional<std::string>& gadget_name) {}\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_LAYOUT_ASSIGNMENT_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/layout/cell.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_LAYOUT_CELL_H_\n#define TACHYON_ZK_PLONK_LAYOUT_CELL_H_\n\n#include <stddef.h>\n\n#include <string>\n\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/export.h\"\n#include \"tachyon/zk/base/row_types.h\"\n#include \"tachyon/zk/plonk/base/column_key.h\"\n\nnamespace tachyon::zk::plonk {\n\n// A pointer to a cell within a circuit.\nclass TACHYON_EXPORT Cell {\n public:\n  Cell() = default;\n  Cell(size_t region_index, RowIndex row_offset, const AnyColumnKey& column)\n      : region_index_(region_index), row_offset_(row_offset), column_(column) {}\n\n  size_t region_index() const { return region_index_; }\n  RowIndex row_offset() const { return row_offset_; }\n  const AnyColumnKey& column() const { return column_; }\n\n  std::string ToString() const {\n    return absl::Substitute(\"{region_index: $0, row_offset: $1, column: $2}\",\n                            region_index_, row_offset_, column_.ToString());\n  }\n\n private:\n  // Identifies the region in which this cell resides.\n  size_t region_index_ = 0;\n  // The relative offset of this cell within its region.\n  RowIndex row_offset_ = 0;\n  // The column of this cell.\n  AnyColumnKey column_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_LAYOUT_CELL_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/layout/floor_planner/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"allocated_region\",\n    hdrs = [\"allocated_region.h\"],\n    deps = [\n        \"//tachyon:export\",\n        \"//tachyon/zk/base:row_types\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"allocations\",\n    hdrs = [\"allocations.h\"],\n    deps = [\n        \":allocated_region\",\n        \":empty_space\",\n        \"//tachyon:export\",\n        \"@com_google_absl//absl/container:btree\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"constant\",\n    hdrs = [\"constant.h\"],\n    deps = [\n        \"//tachyon/math/base:rational_field\",\n        \"//tachyon/zk/plonk/layout:cell\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"empty_space\",\n    hdrs = [\"empty_space.h\"],\n    deps = [\n        \"//tachyon:export\",\n        \"//tachyon/base:range\",\n        \"//tachyon/zk/base:row_types\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"floor_planner\",\n    hdrs = [\"floor_planner.h\"],\n    deps = [\n        \"//tachyon/zk/plonk/base:column_key\",\n        \"//tachyon/zk/plonk/layout:assignment\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"lookup_table_assigner\",\n    hdrs = [\"lookup_table_assigner.h\"],\n    deps = [\n        \":scoped_region\",\n        \":simple_lookup_table_layouter\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base/functional:identity\",\n        \"//tachyon/zk/plonk/layout:assignment\",\n        \"//tachyon/zk/plonk/layout:layouter\",\n        \"//tachyon/zk/plonk/layout:lookup_table\",\n        \"//tachyon/zk/plonk/layout:lookup_table_column\",\n        \"@com_google_absl//absl/container:flat_hash_map\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"plan_region\",\n    hdrs = [\"plan_region.h\"],\n    deps = [\n        \"//tachyon/math/base:rational_field\",\n        \"//tachyon/zk/plonk/base:column_key\",\n        \"//tachyon/zk/plonk/layout:assignment\",\n        \"//tachyon/zk/plonk/layout:region\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"scoped_region\",\n    hdrs = [\"scoped_region.h\"],\n    deps = [\"//tachyon/zk/plonk/layout:assignment\"],\n)\n\ntachyon_cc_library(\n    name = \"simple_floor_planner\",\n    hdrs = [\"simple_floor_planner.h\"],\n    deps = [\n        \":floor_planner\",\n        \":single_chip_layouter\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"simple_lookup_table_layouter\",\n    hdrs = [\"simple_lookup_table_layouter.h\"],\n    deps = [\n        \"//tachyon/base:logging\",\n        \"//tachyon/base/containers:contains\",\n        \"//tachyon/math/base:rational_field\",\n        \"//tachyon/zk/base:value\",\n        \"//tachyon/zk/plonk/layout:assignment\",\n        \"//tachyon/zk/plonk/layout:lookup_table_column\",\n        \"@com_google_absl//absl/container:flat_hash_map\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"single_chip_layouter\",\n    hdrs = [\"single_chip_layouter.h\"],\n    deps = [\n        \":constant\",\n        \":lookup_table_assigner\",\n        \":plan_region\",\n        \":scoped_region\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/zk/plonk/base:column_key\",\n        \"//tachyon/zk/plonk/layout:layouter\",\n        \"//tachyon/zk/plonk/layout:region_column\",\n        \"//tachyon/zk/plonk/layout:region_shape\",\n        \"@com_google_absl//absl/container:flat_hash_map\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/zk/plonk/layout/floor_planner/allocated_region.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_ALLOCATED_REGION_H_\n#define TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_ALLOCATED_REGION_H_\n\n#include \"tachyon/export.h\"\n#include \"tachyon/zk/base/row_types.h\"\n\nnamespace tachyon::zk::plonk {\n\n// A region allocated within a column.\nclass TACHYON_EXPORT AllocatedRegion {\n public:\n  constexpr AllocatedRegion(RowIndex start, RowIndex length)\n      : start_(start), length_(length) {}\n\n  constexpr RowIndex start() const { return start_; }\n  constexpr RowIndex length() const { return length_; }\n\n  constexpr RowIndex End() const { return start_ + length_; }\n\n  constexpr bool operator<(const AllocatedRegion& other) const {\n    return start_ < other.start_;\n  }\n\n private:\n  // The starting position of the region.\n  RowIndex start_ = 0;\n  // The length of the region.\n  RowIndex length_ = 0;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_ALLOCATED_REGION_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/layout/floor_planner/allocations.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_ALLOCATIONS_H_\n#define TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_ALLOCATIONS_H_\n\n#include <algorithm>\n#include <vector>\n\n#include \"absl/container/btree_set.h\"\n\n#include \"tachyon/export.h\"\n#include \"tachyon/zk/plonk/layout/floor_planner/allocated_region.h\"\n#include \"tachyon/zk/plonk/layout/floor_planner/empty_space.h\"\n\nnamespace tachyon::zk::plonk {\n\n// Allocated rows within a column.\n\n// This is a set of |AllocatedRegion|s, representing disjoint allocated\n// intervals.\nclass TACHYON_EXPORT Allocations {\n public:\n  absl::btree_set<AllocatedRegion>& allocations() { return allocations_; }\n\n  // Returns the row that forms the unbounded unallocated interval.\n  RowIndex UnboundedIntervalStart() const {\n    if (allocations_.empty()) {\n      return 0;\n    }\n    return allocations_.rbegin()->End();\n  }\n\n  // Return all the *unallocated* non-empty intervals intersecting [|start|,\n  // |end|). |end| = std::nullopt represents an unbounded end.\n  std::vector<EmptySpace> FreeIntervals(RowIndex start,\n                                        std::optional<RowIndex> end) const {\n    std::vector<EmptySpace> result;\n    result.reserve(allocations_.size() + 1);\n    RowIndex row = start;\n    for (const AllocatedRegion& region : allocations_) {\n      if (end.has_value() && region.start() >= end.value()) {\n        break;\n      }\n      if (row < region.start()) {\n        result.emplace_back(row, std::optional<RowIndex>(region.start()));\n      }\n      row = std::max(row, region.End());\n    }\n    if (!end.has_value() || row < end.value()) {\n      result.emplace_back(row, end);\n    }\n    return result;\n  }\n\n private:\n  absl::btree_set<AllocatedRegion> allocations_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_ALLOCATIONS_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/layout/floor_planner/constant.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_CONSTANT_H_\n#define TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_CONSTANT_H_\n\n#include <vector>\n\n#include \"tachyon/math/base/rational_field.h\"\n#include \"tachyon/zk/plonk/layout/cell.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F>\nstruct Constant {\n  Constant(const math::RationalField<F>& value, const Cell& cell)\n      : value(value), cell(cell) {}\n\n  math::RationalField<F> value;\n  Cell cell;\n};\n\ntemplate <typename F>\nusing Constants = std::vector<Constant<F>>;\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_CONSTANT_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/layout/floor_planner/empty_space.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_EMPTY_SPACE_H_\n#define TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_EMPTY_SPACE_H_\n\n#include <optional>\n\n#include \"tachyon/base/range.h\"\n#include \"tachyon/export.h\"\n#include \"tachyon/zk/base/row_types.h\"\n\nnamespace tachyon::zk::plonk {\n\n// An area of empty space within a column.\nclass TACHYON_EXPORT EmptySpace {\n public:\n  EmptySpace(RowIndex start, std::optional<RowIndex> end)\n      : start_(start), end_(end) {}\n\n  RowIndex start() const { return start_; }\n  std::optional<RowIndex> end() const { return end_; }\n\n  constexpr std::optional<base::Range<RowIndex>> Range() const {\n    if (end_.has_value()) {\n      return std::optional<base::Range<RowIndex>>(\n          base::Range<RowIndex>(start_, end_.value()));\n    }\n    return std::nullopt;\n  }\n\n private:\n  // The starting position (inclusive) of the empty space.\n  RowIndex start_ = 0;\n  // The ending position (exclusive) of the empty space, or |std::nullopt| if\n  // unbounded.\n  std::optional<RowIndex> end_ = std::nullopt;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_EMPTY_SPACE_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/layout/floor_planner/floor_planner.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_FLOOR_PLANNER_H_\n#define TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_FLOOR_PLANNER_H_\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/zk/plonk/base/column_key.h\"\n#include \"tachyon/zk/plonk/layout/assignment.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename Circuit>\nclass FloorPlanner {\n public:\n  using F = typename Circuit::Field;\n  using Config = typename Circuit::Config;\n\n  virtual void Synthesize(Assignment<F>* assignment, const Circuit& circuit,\n                          Config&& config,\n                          const std::vector<FixedColumnKey>& constants) = 0;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_FLOOR_PLANNER_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/layout/floor_planner/lookup_table_assigner.h",
    "content": "#ifndef TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_LOOKUP_TABLE_ASSIGNER_H_\n#define TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_LOOKUP_TABLE_ASSIGNER_H_\n\n#include <optional>\n#include <utility>\n#include <vector>\n\n#include \"absl/container/flat_hash_map.h\"\n\n#include \"tachyon/base/functional/identity.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/zk/plonk/layout/assignment.h\"\n#include \"tachyon/zk/plonk/layout/floor_planner/scoped_region.h\"\n#include \"tachyon/zk/plonk/layout/floor_planner/simple_lookup_table_layouter.h\"\n#include \"tachyon/zk/plonk/layout/layouter.h\"\n#include \"tachyon/zk/plonk/layout/lookup_table.h\"\n#include \"tachyon/zk/plonk/layout/lookup_table_column.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F>\nclass LookupTableAssigner {\n public:\n  using AssignLookupTableCallback =\n      typename Layouter<F>::AssignLookupTableCallback;\n\n  LookupTableAssigner(Assignment<F>* assignment,\n                      std::vector<LookupTableColumn>& lookup_table_columns)\n      : assignment_(assignment), lookup_table_columns_(lookup_table_columns) {}\n\n  void AssignLookupTable(std::string_view name,\n                         AssignLookupTableCallback assign) {\n    // Assign table cells.\n    SimpleLookupTableLayouter<F> lookup_table_layouter(assignment_,\n                                                       &lookup_table_columns_);\n    {\n      ScopedRegion<F> scoped_region(assignment_, name);\n      LookupTable<F> table(&lookup_table_layouter);\n      std::move(assign).Run(table);\n    }\n    const absl::flat_hash_map<LookupTableColumn,\n                              typename SimpleLookupTableLayouter<F>::Value>&\n        values = lookup_table_layouter.values();\n    // Check that all table columns have the same length |first_unused|,\n    // and all cells up to that length are assigned.\n    std::optional<RowIndex> first_unused;\n    std::vector<std::optional<RowIndex>> assigned_sizes = base::Map(\n        values.begin(), values.end(),\n        [](const std::pair<LookupTableColumn,\n                           typename SimpleLookupTableLayouter<F>::Value>&\n               entry) {\n          const auto& [column, value] = entry;\n          if (std::all_of(value.assigned.begin(), value.assigned.end(),\n                          base::identity())) {\n            return std::optional<RowIndex>(value.assigned.size());\n          } else {\n            return std::optional<RowIndex>();\n          }\n        });\n    for (const std::optional<RowIndex>& assigned_size : assigned_sizes) {\n      CHECK(assigned_size.has_value()) << \"length is missing\";\n\n      if (first_unused.has_value()) {\n        CHECK_EQ(first_unused.value(), assigned_size.value())\n            << \"all table columns must have the same length\";\n      } else {\n        first_unused = assigned_size;\n      }\n    }\n\n    CHECK(first_unused.has_value())\n        << \"length is missing, maybe there are no table columns\";\n\n    // Record these columns so that we can prevent them from being used again.\n    for (const auto& [column, value] : values) {\n      lookup_table_columns_.push_back(column);\n      // |value.default_value| must have value because we must have assigned\n      // at least one cell in each column, and in that case we checked\n      // that all cells up to |first_unused| were assigned.\n      assignment_->FillFromRow(column.column(), first_unused.value(),\n                               value.default_value.value().value());\n    }\n  }\n\n private:\n  // not owned\n  Assignment<F>* const assignment_;\n  std::vector<LookupTableColumn>& lookup_table_columns_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_LOOKUP_TABLE_ASSIGNER_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/layout/floor_planner/plan_region.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_PLAN_REGION_H_\n#define TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_PLAN_REGION_H_\n\n#include <stddef.h>\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/math/base/rational_field.h\"\n#include \"tachyon/zk/plonk/base/column_key.h\"\n#include \"tachyon/zk/plonk/layout/assignment.h\"\n#include \"tachyon/zk/plonk/layout/floor_planner/constant.h\"\n#include \"tachyon/zk/plonk/layout/region_layouter.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F>\nclass PlanRegion : public RegionLayouter<F> {\n public:\n  using AssignCallback = typename RegionLayouter<F>::AssignCallback;\n\n  PlanRegion(Assignment<F>* assignment, const std::vector<RowIndex>& regions,\n             size_t region_index, Constants<F>& constant)\n      : assignment_(assignment),\n        regions_(regions),\n        region_index_(region_index),\n        constants_(constant) {}\n\n  const Constants<F>& constants() const { return constants_; }\n\n  // RegionLayouter methods\n  void EnableSelector(std::string_view name, Selector selector,\n                      RowIndex offset) override {\n    assignment_->EnableSelector(name, selector,\n                                regions_[region_index_] + offset);\n  }\n\n  void NameColumn(std::string_view name, const AnyColumnKey& column) override {\n    assignment_->NameColumn(name, column);\n  }\n\n  Cell AssignAdvice(std::string_view name, const AdviceColumnKey& column,\n                    RowIndex offset, AssignCallback assign) override {\n    assignment_->AssignAdvice(name, column, regions_[region_index_] + offset,\n                              std::move(assign));\n    return {region_index_, offset, column};\n  }\n\n  Cell AssignAdviceFromConstant(\n      std::string_view name, const AdviceColumnKey& column, RowIndex offset,\n      const math::RationalField<F>& constant) override {\n    Cell cell = AssignAdvice(name, column, offset, [&constant]() {\n      return Value<math::RationalField<F>>::Known(constant);\n    });\n    ConstrainConstant(cell, constant);\n    return cell;\n  }\n\n  AssignedCell<F> AssignAdviceFromInstance(std::string_view name,\n                                           const InstanceColumnKey& instance,\n                                           RowIndex row,\n                                           const AdviceColumnKey& advice,\n                                           RowIndex offset) override {\n    Value<F> value = assignment_->QueryInstance(instance, row);\n\n    Cell cell = AssignAdvice(name, advice, offset, [&value]() {\n      return Value<math::RationalField<F>>::Known(\n          math::RationalField<F>(value.value()));\n    });\n\n    assignment_->Copy(cell.column(),\n                      regions_[cell.region_index()] + cell.row_offset(),\n                      instance, row);\n\n    return {std::move(cell), std::move(value)};\n  }\n\n  Cell AssignFixed(std::string_view name, const FixedColumnKey& column,\n                   RowIndex offset, AssignCallback assign) override {\n    assignment_->AssignFixed(name, column, regions_[region_index_] + offset,\n                             std::move(assign));\n    return {region_index_, offset, column};\n  }\n\n  void ConstrainConstant(const Cell& cell,\n                         const math::RationalField<F>& constant) override {\n    constants_.emplace_back(constant, cell);\n  }\n\n  void ConstrainEqual(const Cell& left, const Cell& right) override {\n    assignment_->Copy(\n        left.column(), regions_[left.region_index()] + left.row_offset(),\n        right.column(), regions_[right.region_index()] + right.row_offset());\n  }\n\n private:\n  // not owned\n  Assignment<F>* const assignment_;\n  const std::vector<RowIndex>& regions_;\n  const size_t region_index_;\n  Constants<F>& constants_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_PLAN_REGION_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/layout/floor_planner/scoped_region.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_SCOPED_REGION_H_\n#define TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_SCOPED_REGION_H_\n\n#include <string_view>\n\n#include \"tachyon/zk/plonk/layout/assignment.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F>\nstruct ScopedRegion {\n  ScopedRegion(Assignment<F>* assignment, std::string_view name)\n      : assignment_(assignment) {\n    assignment_->EnterRegion(name);\n  }\n  ~ScopedRegion() { assignment_->ExitRegion(); }\n\n  // not owned\n  Assignment<F>* const assignment_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_SCOPED_REGION_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/layout/floor_planner/simple_floor_planner.h",
    "content": "#ifndef TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_SIMPLE_FLOOR_PLANNER_H_\n#define TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_SIMPLE_FLOOR_PLANNER_H_\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/zk/plonk/layout/floor_planner/floor_planner.h\"\n#include \"tachyon/zk/plonk/layout/floor_planner/single_chip_layouter.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename Circuit>\nclass SimpleFloorPlanner : public FloorPlanner<Circuit> {\n public:\n  using F = typename Circuit::Field;\n  using Config = typename Circuit::Config;\n\n  void Synthesize(Assignment<F>* assignment, const Circuit& circuit,\n                  Config&& config,\n                  const std::vector<FixedColumnKey>& constants) override {\n    SingleChipLayouter layouter(assignment, constants);\n    circuit.Synthesize(std::move(config), &layouter);\n  }\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_SIMPLE_FLOOR_PLANNER_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/layout/floor_planner/simple_lookup_table_layouter.h",
    "content": "#ifndef TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_SIMPLE_LOOKUP_TABLE_LAYOUTER_H_\n#define TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_SIMPLE_LOOKUP_TABLE_LAYOUTER_H_\n\n#include <optional>\n#include <utility>\n#include <vector>\n\n#include \"absl/container/flat_hash_map.h\"\n\n#include \"tachyon/base/containers/contains.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/math/base/rational_field.h\"\n#include \"tachyon/zk/base/value.h\"\n#include \"tachyon/zk/plonk/layout/assignment.h\"\n#include \"tachyon/zk/plonk/layout/lookup_table.h\"\n#include \"tachyon/zk/plonk/layout/lookup_table_column.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F>\nclass SimpleLookupTableLayouter : public LookupTable<F>::Layouter {\n public:\n  using AssignCallback = typename LookupTable<F>::Layouter::AssignCallback;\n\n  struct Value {\n    // The default value to fill a table column with.\n    //\n    // - The outer |std::optional| tracks whether the value in row 0 of the\n    //   table column has been assigned yet. This will always have value once a\n    //   valid table has been completely assigned.\n    // - The inner |zk::Value| tracks whether the underlying\n    //   |math::RationalField| is evaluating witnesses or not.\n    using DefaultValue = std::optional<zk::Value<math::RationalField<F>>>;\n\n    DefaultValue default_value;\n    std::vector<bool> assigned;\n  };\n\n  SimpleLookupTableLayouter(Assignment<F>* assignment,\n                            const std::vector<LookupTableColumn>* used_columns)\n      : assignment_(assignment), used_columns_(used_columns) {}\n\n  const absl::flat_hash_map<LookupTableColumn, Value>& values() const {\n    return values_;\n  }\n\n  // LookupTable<F>::Layouter methods\n  bool AssignCell(std::string_view name, const LookupTableColumn& column,\n                  RowIndex offset, AssignCallback assign) override {\n    if (base::Contains(*used_columns_, column)) {\n      LOG(ERROR) << \"column already has been assigned\";\n      return false;\n    }\n\n    Value& entry = values_[column];\n\n    zk::Value<math::RationalField<F>> value =\n        zk::Value<math::RationalField<F>>::Unknown();\n    assignment_->AssignFixed(\n        name, column.column(), offset, [&value, &assign]() {\n          zk::Value<math::RationalField<F>> ret = std::move(assign).Run();\n          value = ret;\n          return ret;\n        });\n\n    if (offset == 0) {\n      if (!entry.default_value.has_value()) {\n        // Use the value at offset 0 as the default value for this table column.\n        entry.default_value = value;\n      } else {\n        // Since there is already an existing default value for this table\n        // column, the caller should not be attempting to assign another\n        // value at offset 0.\n        LOG(ERROR) << \"default value has been assigned\";\n        return false;\n      }\n    }\n    if (entry.assigned.size() <= offset) {\n      entry.assigned.resize(offset + 1, false);\n    }\n    entry.assigned[offset] = true;\n    return true;\n  }\n\n private:\n  // not owned\n  Assignment<F>* const assignment_;\n  // not owned\n  const std::vector<LookupTableColumn>* const used_columns_;\n  absl::flat_hash_map<LookupTableColumn, Value> values_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_SIMPLE_LOOKUP_TABLE_LAYOUTER_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/layout/floor_planner/single_chip_layouter.h",
    "content": "#ifndef TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_SINGLE_CHIP_LAYOUTER_H_\n#define TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_SINGLE_CHIP_LAYOUTER_H_\n\n#include <stddef.h>\n\n#include <algorithm>\n#include <optional>\n#include <string>\n#include <string_view>\n#include <utility>\n#include <vector>\n\n#include \"absl/container/flat_hash_map.h\"\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/zk/plonk/base/column_key.h\"\n#include \"tachyon/zk/plonk/layout/floor_planner/constant.h\"\n#include \"tachyon/zk/plonk/layout/floor_planner/lookup_table_assigner.h\"\n#include \"tachyon/zk/plonk/layout/floor_planner/plan_region.h\"\n#include \"tachyon/zk/plonk/layout/floor_planner/scoped_region.h\"\n#include \"tachyon/zk/plonk/layout/layouter.h\"\n#include \"tachyon/zk/plonk/layout/region_column.h\"\n#include \"tachyon/zk/plonk/layout/region_shape.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F>\nclass SingleChipLayouter : public Layouter<F> {\n public:\n  using AssignRegionCallback = typename Layouter<F>::AssignRegionCallback;\n  using AssignLookupTableCallback =\n      typename Layouter<F>::AssignLookupTableCallback;\n\n  SingleChipLayouter(Assignment<F>* assignment,\n                     const std::vector<FixedColumnKey>& constants)\n      : assignment_(assignment), constants_(constants) {}\n\n  const Assignment<F>* assignment() const { return assignment_; }\n  const std::vector<FixedColumnKey>& constants() const { return constants_; }\n  const std::vector<RowIndex>& regions() const { return regions_; }\n  const absl::flat_hash_map<RegionColumn, RowIndex>& columns() const {\n    return columns_;\n  }\n  const std::vector<LookupTableColumn>& lookup_table_columns() const {\n    return lookup_table_columns_;\n  }\n\n  // Layouter<F> methods\n  void AssignRegion(std::string_view name,\n                    AssignRegionCallback assign) override {\n    size_t region_index = regions_.size();\n\n    // Get shape of the region.\n    RegionShape<F> shape(region_index);\n    {\n      // TODO(chokobole): Add event trace using\n      // https://github.com/google/perfetto.\n      VLOG(1) << \"Assign region 1st pass: \" << name;\n      Region<F> region(&shape);\n      assign.Run(region);\n    }\n    RowIndex row_count = shape.row_count();\n    bool log_region_info = row_count >= 40;\n    DLOG_IF(INFO, log_region_info)\n        << \"Region row count \\\"\" << name << \"\\\": \" << row_count;\n\n    // Layout this region. We implement the simplest approach here: position\n    // the region starting at the earliest row for which none of the columns are\n    // in use.\n    size_t region_start = 0;\n    for (auto it = shape.columns().begin(); it != shape.columns().end(); ++it) {\n      size_t column_start = columns_[*it];\n      if (column_start != 0 && log_region_info) {\n        VLOG(3) << \"columns \" << it->ToString()\n                << \" reused between multi regions. start: \" << column_start\n                << \" region: \\\"\" << name << \"\\\"\";\n      }\n      region_start = std::max(region_start, column_start);\n    }\n    DLOG_IF(INFO, log_region_info)\n        << \"region \\\"\" << name << \"\\\", idx: \" << regions_.size()\n        << \" start: \" << region_start;\n    regions_.push_back(region_start);\n\n    // Update column usage information.\n    for (const RegionColumn& column : shape.columns()) {\n      columns_[column] = region_start + shape.row_count();\n    }\n\n    Constants<F> constants;\n\n    // Assign region cells.\n    PlanRegion plan_region(assignment_, regions_, region_index, constants);\n    {\n      ScopedRegion<F> scoped_region(assignment_, name);\n      // TODO(chokobole): Add event trace using\n      // https://github.com/google/perfetto.\n      VLOG(1) << \"Assign region 2nd pass: \" << name;\n      Region<F> region(&plan_region);\n      assign.Run(region);\n    }\n\n    // Assign constants. For the simple floor planner, we assign constants in\n    // order in the first |constants| column.\n    if (constants_.empty()) {\n      CHECK(plan_region.constants().empty())\n          << \"Not enough columns for constants\";\n    } else {\n      const FixedColumnKey& constants_column = constants_[0];\n      RowIndex& next_constant_row = columns_[RegionColumn(constants_column)];\n      for (const Constant<F>& constant : plan_region.constants()) {\n        const math::RationalField<F>& value = constant.value;\n        const Cell& advice = constant.cell;\n        std::string name =\n            absl::Substitute(\"Constant($0)\", value.Evaluate().ToString());\n        assignment_->AssignFixed(\n            name, constants_column, next_constant_row,\n            [&value]() { return Value<math::RationalField<F>>::Known(value); });\n        assignment_->Copy(\n            constants_column, next_constant_row, advice.column(),\n            regions_[advice.region_index()] + advice.row_offset());\n        ++next_constant_row;\n      }\n    }\n  }\n\n  void AssignLookupTable(std::string_view name,\n                         AssignLookupTableCallback assign) override {\n    LookupTableAssigner<F> lookup_table_assigner(assignment_,\n                                                 lookup_table_columns_);\n    lookup_table_assigner.AssignLookupTable(name, std::move(assign));\n  }\n\n  void ConstrainInstance(const Cell& cell, const InstanceColumnKey& column,\n                         RowIndex row) override {\n    assignment_->Copy(cell.column(),\n                      regions_[cell.region_index()] + cell.row_offset(), column,\n                      row);\n  }\n\n  Value<F> GetChallenge(Challenge challenge) const override {\n    return assignment_->GetChallenge(challenge);\n  }\n\n  Layouter<F>* GetRoot() override { return this; }\n\n  void PushNamespace(std::string_view name) override {\n    assignment_->PushNamespace(name);\n  }\n\n  void PopNamespace(const std::optional<std::string>& gadget_name) override {\n    assignment_->PopNamespace(gadget_name);\n  }\n\n private:\n  // not owned\n  Assignment<F>* const assignment_;\n  std::vector<FixedColumnKey> constants_;\n  // Stores the starting row for each region.\n  std::vector<RowIndex> regions_;\n  // Stores the first empty row for each column.\n  absl::flat_hash_map<RegionColumn, RowIndex> columns_;\n  // Stores the table fixed columns.\n  std::vector<LookupTableColumn> lookup_table_columns_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_SINGLE_CHIP_LAYOUTER_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/layout/floor_planner/v1/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"assignment_pass\",\n    hdrs = [\"assignment_pass.h\"],\n    deps = [\n        \":v1_plan\",\n        \"//tachyon/zk/plonk/base:column_key\",\n        \"//tachyon/zk/plonk/layout:cell\",\n        \"//tachyon/zk/plonk/layout:layouter\",\n        \"//tachyon/zk/plonk/layout/floor_planner:lookup_table_assigner\",\n        \"//tachyon/zk/plonk/layout/floor_planner:plan_region\",\n        \"//tachyon/zk/plonk/layout/floor_planner:scoped_region\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"measurement_pass\",\n    hdrs = [\"measurement_pass.h\"],\n    deps = [\"//tachyon/zk/plonk/layout:region_shape\"],\n)\n\ntachyon_cc_library(\n    name = \"v1_floor_planner\",\n    hdrs = [\"v1_floor_planner.h\"],\n    deps = [\n        \":measurement_pass\",\n        \":v1_pass\",\n        \":v1_plan\",\n        \":v1_strategy\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/zk/plonk/layout/floor_planner\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"v1_pass\",\n    hdrs = [\"v1_pass.h\"],\n    deps = [\n        \":assignment_pass\",\n        \":measurement_pass\",\n        \"//tachyon/zk/base:value\",\n        \"//tachyon/zk/plonk/layout:layouter\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"v1_plan\",\n    hdrs = [\"v1_plan.h\"],\n    deps = [\n        \"//tachyon/math/base:rational_field\",\n        \"//tachyon/zk/plonk/layout:assignment\",\n        \"//tachyon/zk/plonk/layout:cell\",\n        \"//tachyon/zk/plonk/layout:lookup_table_column\",\n        \"//tachyon/zk/plonk/layout/floor_planner:constant\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"v1_strategy\",\n    srcs = [\"v1_strategy.cc\"],\n    hdrs = [\"v1_strategy.h\"],\n    deps = [\n        \"//tachyon:export\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/base:sort\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/zk/plonk/base:column_key\",\n        \"//tachyon/zk/plonk/base:column_type\",\n        \"//tachyon/zk/plonk/layout:region_shape\",\n        \"//tachyon/zk/plonk/layout/floor_planner:allocations\",\n        \"@com_google_absl//absl/container:flat_hash_map\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/zk/plonk/layout/floor_planner/v1/assignment_pass.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_V1_ASSIGNMENT_PASS_H_\n#define TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_V1_ASSIGNMENT_PASS_H_\n\n#include <stddef.h>\n\n#include <string_view>\n#include <utility>\n\n#include \"tachyon/zk/plonk/base/column_key.h\"\n#include \"tachyon/zk/plonk/layout/cell.h\"\n#include \"tachyon/zk/plonk/layout/floor_planner/lookup_table_assigner.h\"\n#include \"tachyon/zk/plonk/layout/floor_planner/plan_region.h\"\n#include \"tachyon/zk/plonk/layout/floor_planner/scoped_region.h\"\n#include \"tachyon/zk/plonk/layout/floor_planner/v1/v1_plan.h\"\n#include \"tachyon/zk/plonk/layout/layouter.h\"\n\nnamespace tachyon::zk::plonk {\n\n// Assigns the circuit.\ntemplate <typename F>\nclass AssignmentPass {\n public:\n  using AssignRegionCallback = typename Layouter<F>::AssignRegionCallback;\n  using AssignLookupTableCallback =\n      typename Layouter<F>::AssignLookupTableCallback;\n\n  explicit AssignmentPass(V1Plan<F>* plan) : plan_(plan) {}\n\n  V1Plan<F>* plan() const { return plan_; }\n\n  void AssignRegion(std::string_view name, AssignRegionCallback assign) {\n    ScopedRegion<F> scoped_region(plan_->assignment(), name);\n    PlanRegion<F> plan_region(plan_->assignment(), plan_->regions(),\n                              region_index_++, plan_->constants());\n    Region<F> region(&plan_region);\n    assign.Run(region);\n  }\n\n  void AssignLookupTable(std::string_view name,\n                         AssignLookupTableCallback assign) {\n    LookupTableAssigner<F> lookup_table_assigner(plan_->assignment(),\n                                                 plan_->table_columns());\n    lookup_table_assigner.AssignLookupTable(name, std::move(assign));\n  }\n\n  void ConstrainInstance(const Cell& cell, const InstanceColumnKey& instance,\n                         RowIndex row) {\n    plan_->assignment()->Copy(\n        cell.column(),\n        plan_->regions()[cell.region_index()] + cell.row_offset(), instance,\n        row);\n  }\n\n private:\n  // not owned\n  V1Plan<F>* const plan_;\n  // Counter tracking which region we need to assign next.\n  size_t region_index_ = 0;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_V1_ASSIGNMENT_PASS_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/layout/floor_planner/v1/measurement_pass.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_V1_MEASUREMENT_PASS_H_\n#define TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_V1_MEASUREMENT_PASS_H_\n\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/zk/plonk/layout/layouter.h\"\n#include \"tachyon/zk/plonk/layout/region_shape.h\"\n\nnamespace tachyon::zk::plonk {\n\n// Measures the circuit.\ntemplate <typename F>\nclass MeasurementPass {\n public:\n  using AssignRegionCallback = typename Layouter<F>::AssignRegionCallback;\n\n  struct Region {\n    Region(std::string_view name, RegionShape<F>&& shape)\n        : name(std::string(name)), shape(std::move(shape)) {}\n\n    std::string name;\n    RegionShape<F> shape;\n  };\n\n  MeasurementPass() = default;\n\n  const std::vector<Region>& regions() const { return regions_; }\n\n  void AssignRegion(std::string_view name, AssignRegionCallback assign) {\n    size_t region_index = regions_.size();\n\n    // Get shape of the region.\n    RegionShape<F> shape(region_index);\n    {\n      zk::plonk::Region<F> region(&shape);\n      assign.Run(region);\n    }\n    regions_.emplace_back(name, std::move(shape));\n  }\n\n private:\n  std::vector<Region> regions_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_V1_MEASUREMENT_PASS_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/layout/floor_planner/v1/v1_floor_planner.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_V1_V1_FLOOR_PLANNER_H_\n#define TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_V1_V1_FLOOR_PLANNER_H_\n\n#include <stddef.h>\n\n#include <algorithm>\n#include <utility>\n#include <vector>\n\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/zk/plonk/layout/floor_planner/floor_planner.h\"\n#include \"tachyon/zk/plonk/layout/floor_planner/v1/measurement_pass.h\"\n#include \"tachyon/zk/plonk/layout/floor_planner/v1/v1_pass.h\"\n#include \"tachyon/zk/plonk/layout/floor_planner/v1/v1_plan.h\"\n#include \"tachyon/zk/plonk/layout/floor_planner/v1/v1_strategy.h\"\n\nnamespace tachyon::zk::plonk {\n\n// The version 1 |FloorPlanner| provided by \"halo2\".\n//\n// - No column optimizations are performed. Circuit configuration is left\n//   entirely to the circuit designer.\n// - A dual-pass layouter is used to measure regions prior to assignment.\n// - Regions are measured as rectangles, bounded on the cells they assign.\n// - Regions are laid out using a greedy first-fit strategy, after sorting\n//   regions by their \"advice area\" (number of advice columns * rows).\ntemplate <typename Circuit>\nclass V1FloorPlanner : public FloorPlanner<Circuit> {\n public:\n  using F = typename Circuit::Field;\n  using Config = typename Circuit::Config;\n\n  void Synthesize(Assignment<F>* assignment, const Circuit& circuit,\n                  Config&& config,\n                  const std::vector<FixedColumnKey>& constants) override {\n    // First pass: measure the regions within the circuit.\n    MeasurementPass<F> measure;\n    {\n      V1Pass<F> pass(&measure);\n      circuit.WithoutWitness()->Synthesize(config.Clone(), &pass);\n    }\n    for (const typename MeasurementPass<F>::Region& region :\n         measure.regions()) {\n      // TODO(TomTaehoonKim): Add event trace using\n      // https://github.com/google/perfetto.\n      VLOG(1) << \"Region height \" << region.name << \": \"\n              << region.shape.row_count();\n    }\n\n    // Planning:\n    // - Position the regions.\n    V1Plan<F> plan(assignment);\n    std::vector<RegionShape<F>> region_shapes =\n        base::Map(measure.regions(),\n                  [](const typename MeasurementPass<F>::Region& region) {\n                    return region.shape;\n                  });\n    SlotInBiggestAdviceFirstResult result =\n        SlotInBiggestAdviceFirst(region_shapes);\n    plan.set_regions(std::move(result.region_starts));\n\n    // - Determine how many rows our planned circuit will require.\n    RowIndex first_unassigned_row = 0;\n    if (!result.column_allocations.empty()) {\n      std::vector<RowIndex> unbounded_interval_starts = base::Map(\n          result.column_allocations,\n          [](const std::pair<RegionColumn, Allocations>& column_allocation) {\n            return column_allocation.second.UnboundedIntervalStart();\n          });\n      first_unassigned_row = *std::max_element(\n          unbounded_interval_starts.begin(), unbounded_interval_starts.end());\n    }\n\n    // - Position the constants within those rows.\n    std::vector<FixedAllocation> fixed_allocations =\n        base::Map(constants, [&result](const FixedColumnKey& c) {\n          return FixedAllocation(\n              c, result.column_allocations[RegionColumn(AnyColumnKey(c))]);\n        });\n\n    std::vector<ConstantPosition> constant_positions = base::FlatMap(\n        fixed_allocations,\n        [first_unassigned_row](const FixedAllocation& fixed_allocation) {\n          return base::FlatMap(\n              fixed_allocation.rows.FreeIntervals(0, first_unassigned_row),\n              [&column = fixed_allocation.column](const EmptySpace& space) {\n                std::optional<base::Range<RowIndex>> range = space.Range();\n                return range.has_value()\n                           ? base::Map(range.value(),\n                                       [&column](RowIndex row) {\n                                         return ConstantPosition(column, row);\n                                       })\n                           : std::vector<ConstantPosition>();\n              });\n        });\n\n    // Second pass:\n    // - Assign the regions.\n    AssignmentPass<F> assign(&plan);\n    {\n      V1Pass<F> pass(&assign);\n      circuit.Synthesize(std::move(config), &pass);\n    }\n\n    // - Assign the constants.\n    CHECK_GE(constant_positions.size(), plan.constants().size())\n        << \"Not enough columns for constants\";\n    // TODO(TomTaehoonKim): Refac using |base::Zipped| when |ZippedIterator| is\n    // fixed to stop producing values once the shorter iterator is exhausted.\n    for (size_t i = 0;\n         i < std::min(constant_positions.size(), plan.constants().size());\n         ++i) {\n      plan.assignment()->AssignFixed(\n          absl::Substitute(\"Constant($0)\",\n                           plan.constants()[i].value.Evaluate().ToString()),\n          constant_positions[i].column, constant_positions[i].row,\n          [&value = plan.constants()[i].value]() {\n            return Value<math::RationalField<F>>::Known(value);\n          });\n      plan.assignment()->Copy(\n          constant_positions[i].column, constant_positions[i].row,\n          plan.constants()[i].cell.column(),\n          plan.regions()[plan.constants()[i].cell.region_index()] +\n              plan.constants()[i].cell.row_offset());\n    }\n  }\n\n private:\n  struct FixedAllocation {\n    FixedAllocation(const FixedColumnKey& column, const Allocations& rows)\n        : column(column), rows(rows) {}\n\n    FixedColumnKey column;\n    Allocations rows;\n  };\n\n  struct ConstantPosition {\n    ConstantPosition(const FixedColumnKey& column, RowIndex row)\n        : column(column), row(row) {}\n\n    FixedColumnKey column;\n    RowIndex row;\n  };\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_V1_V1_FLOOR_PLANNER_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/layout/floor_planner/v1/v1_pass.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_V1_V1_PASS_H_\n#define TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_V1_V1_PASS_H_\n\n#include <string>\n#include <utility>\n#include <variant>\n\n#include \"tachyon/zk/base/value.h\"\n#include \"tachyon/zk/plonk/layout/floor_planner/v1/assignment_pass.h\"\n#include \"tachyon/zk/plonk/layout/floor_planner/v1/measurement_pass.h\"\n#include \"tachyon/zk/plonk/layout/layouter.h\"\n\nnamespace tachyon::zk::plonk {\n\n// A single pass of the |V1FloorPlanner| layouter.\ntemplate <typename F>\nclass V1Pass : public Layouter<F> {\n public:\n  using AssignRegionCallback = typename Layouter<F>::AssignRegionCallback;\n  using AssignLookupTableCallback =\n      typename Layouter<F>::AssignLookupTableCallback;\n\n  explicit V1Pass(MeasurementPass<F>* measure) : pass_(measure) {}\n  explicit V1Pass(AssignmentPass<F>* assign) : pass_(assign) {}\n\n  // Layouter<F> methods\n  void AssignRegion(std::string_view name,\n                    AssignRegionCallback assign) override {\n    std::visit([&](auto pass) { pass->AssignRegion(name, assign); }, pass_);\n  }\n\n  void AssignLookupTable(std::string_view name,\n                         AssignLookupTableCallback assign) override {\n    if (std::holds_alternative<AssignmentPass<F>*>(pass_)) {\n      std::get<AssignmentPass<F>*>(pass_)->AssignLookupTable(name,\n                                                             std::move(assign));\n    }\n  }\n\n  void ConstrainInstance(const Cell& cell, const InstanceColumnKey& instance,\n                         RowIndex row) override {\n    if (std::holds_alternative<AssignmentPass<F>*>(pass_)) {\n      std::get<AssignmentPass<F>*>(pass_)->ConstrainInstance(cell, instance,\n                                                             row);\n    }\n  }\n\n  Value<F> GetChallenge(Challenge challenge) const override {\n    if (std::holds_alternative<MeasurementPass<F>*>(pass_)) {\n      return Value<F>::Unknown();\n    } else {\n      return std::get<AssignmentPass<F>*>(pass_)\n          ->plan()\n          ->assignment()\n          ->GetChallenge(challenge);\n    }\n  }\n\n  Layouter<F>* GetRoot() override { return this; }\n\n  void PushNamespace(std::string_view name) override {\n    if (std::holds_alternative<AssignmentPass<F>*>(pass_)) {\n      std::get<AssignmentPass<F>*>(pass_)->plan()->assignment()->PushNamespace(\n          name);\n    }\n  }\n\n  void PopNamespace(const std::optional<std::string>& gadget_name) override {\n    if (std::holds_alternative<AssignmentPass<F>*>(pass_)) {\n      std::get<AssignmentPass<F>*>(pass_)->plan()->assignment()->PopNamespace(\n          gadget_name);\n    }\n  }\n\n private:\n  std::variant<MeasurementPass<F>*, AssignmentPass<F>*> pass_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_V1_V1_PASS_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/layout/floor_planner/v1/v1_plan.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_V1_V1_PLAN_H_\n#define TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_V1_V1_PLAN_H_\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/math/base/rational_field.h\"\n#include \"tachyon/zk/plonk/layout/assignment.h\"\n#include \"tachyon/zk/plonk/layout/cell.h\"\n#include \"tachyon/zk/plonk/layout/floor_planner/constant.h\"\n#include \"tachyon/zk/plonk/layout/lookup_table_column.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F>\nclass V1Plan {\n public:\n  explicit V1Plan(Assignment<F>* assignment) : assignment_(assignment) {}\n\n  Assignment<F>* assignment() { return assignment_; }\n  const std::vector<RowIndex>& regions() const { return regions_; }\n  void set_regions(std::vector<RowIndex>&& regions) {\n    regions_ = std::move(regions);\n  }\n  Constants<F>& constants() { return constants_; }\n  std::vector<LookupTableColumn>& table_columns() { return table_columns_; }\n\n private:\n  // not owned\n  Assignment<F>* const assignment_;\n  // Stores the starting row for each region.\n  std::vector<RowIndex> regions_;\n  // Stores the constants to be assigned, and the cells to which\n  // they are copied.\n  Constants<F> constants_;\n  // Stores the table fixed columns.\n  std::vector<LookupTableColumn> table_columns_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_V1_V1_PLAN_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/layout/floor_planner/v1/v1_strategy.cc",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#include \"tachyon/zk/plonk/layout/floor_planner/v1/v1_strategy.h\"\n\n#include <stdint.h>\n\nnamespace tachyon::zk::plonk {\n\n// - |start| is the current start row of the region (not of this column).\n// - |slack| is the maximum number of rows the start could be moved down,\n// taking into account prior columns.\nstd::optional<RowIndex> FirstFitRegion(\n    CircuitAllocations* column_allocations,\n    const std::vector<RegionColumn>& region_columns, RowIndex region_length,\n    RowIndex start, std::optional<RowIndex> slack) {\n  if (region_columns.empty()) {\n    return start;\n  }\n\n  const RegionColumn& c = region_columns[0];\n  std::vector<RegionColumn> remaining_columns(region_columns.begin() + 1,\n                                              region_columns.end());\n\n  std::optional<RowIndex> end;\n  if (slack.has_value()) {\n    end = start + region_length + slack.value();\n  }\n\n  // Iterate over the unallocated non-empty intervals in |c| that intersect\n  // [start, end).\n  for (const EmptySpace& space :\n       column_allocations->try_emplace(c, Allocations())\n           .first->second.FreeIntervals(start, end)) {\n    // Do we have enough room for this column of the region in this interval?\n    std::optional<int32_t> s_slack;\n    if (space.end().has_value()) {\n      s_slack = space.end().value() - space.start() - region_length;\n    }\n    if (slack.has_value() && s_slack.has_value()) {\n      CHECK_LE(s_slack.value(), static_cast<int64_t>(slack.value()));\n    }\n    if (!s_slack.has_value() || s_slack.value() >= 0) {\n      std::optional<RowIndex> row = FirstFitRegion(\n          column_allocations, remaining_columns, region_length, space.start(),\n          s_slack.has_value() ? std::optional<RowIndex>(s_slack.value())\n                              : std::nullopt);\n      if (row.has_value()) {\n        if (end.has_value()) {\n          CHECK_LE(row.value() + region_length, end.value());\n        }\n        column_allocations->at(c).allocations().insert(\n            AllocatedRegion(row.value(), region_length));\n        return row;\n      }\n    }\n  }\n\n  // No placement worked; the caller will need to try other possibilities.\n  return std::nullopt;\n}\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/layout/floor_planner/v1/v1_strategy.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_V1_V1_STRATEGY_H_\n#define TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_V1_V1_STRATEGY_H_\n\n#include <stddef.h>\n\n#include <algorithm>\n#include <optional>\n#include <utility>\n#include <vector>\n\n#include \"absl/container/flat_hash_map.h\"\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/sort.h\"\n#include \"tachyon/export.h\"\n#include \"tachyon/zk/plonk/base/column_key.h\"\n#include \"tachyon/zk/plonk/base/column_type.h\"\n#include \"tachyon/zk/plonk/layout/floor_planner/allocations.h\"\n#include \"tachyon/zk/plonk/layout/region_shape.h\"\n\nnamespace tachyon::zk::plonk {\n\n// Allocated rows within a circuit.\nusing CircuitAllocations = absl::flat_hash_map<RegionColumn, Allocations>;\n\n// - |start| is the current start row of the region (not of this column).\n// - |slack| is the maximum number of rows the start could be moved down,\n//   taking into account prior columns.\nTACHYON_EXPORT std::optional<RowIndex> FirstFitRegion(\n    CircuitAllocations* column_allocations,\n    const std::vector<RegionColumn>& region_columns, RowIndex region_length,\n    RowIndex start, std::optional<RowIndex> slack);\n\ntemplate <typename F>\nstruct RegionInfo {\n  RegionInfo(RowIndex region_start, RegionShape<F>&& region)\n      : region_start(region_start), region(std::move(region)) {}\n\n  RowIndex region_start;\n  RegionShape<F> region;\n};\n\ntemplate <typename F>\nstruct SlotInResult {\n  std::vector<RegionInfo<F>> regions;\n  CircuitAllocations column_allocations;\n};\n\n// Positions the regions starting at the earliest row for which none of the\n// columns are in use, taking into account gaps between earlier regions.\ntemplate <typename F>\nSlotInResult<F> SlotIn(std::vector<RegionShape<F>>& region_shapes) {\n  // Tracks the empty regions for each column.\n  CircuitAllocations column_allocations;\n\n  std::vector<RegionInfo<F>> regions;\n  regions.reserve(region_shapes.size());\n\n  for (RegionShape<F>& region : region_shapes) {\n    // Sort the region's columns to ensure determinism.\n    // NOTE(TomTaehoonKim): Sorted result might be different from the original\n    // See\n    // https://github.com/kroma-network/halo2/blob/7d0a369/halo2_proofs/src/circuit/floor_planner/v1/strategy.rs#L171-L191\n    // - An unstable sort is fine, because region.columns() returns a set.\n    // - The sort order relies on Column's Ord implementation!\n    std::vector<RegionColumn> region_columns(region.columns().begin(),\n                                             region.columns().end());\n    base::UnstableSort(region_columns.begin(), region_columns.end(),\n                       [](const RegionColumn& lhs, const RegionColumn& rhs) {\n                         return lhs < rhs;\n                       });\n\n    std::optional<RowIndex> region_start =\n        FirstFitRegion(&column_allocations, region_columns, region.row_count(),\n                       0, std::nullopt);\n    CHECK(region_start.has_value()) << \"We can always fit a region somewhere\";\n\n    regions.emplace_back(*region_start, std::move(region));\n  }\n\n  return {regions, column_allocations};\n}\n\nTACHYON_EXPORT struct SlotInBiggestAdviceFirstResult {\n  std::vector<RowIndex> region_starts;\n  CircuitAllocations column_allocations;\n};\n\n// Sorts the regions by advice area and then lays them out with the |SlotIn|\n// strategy.\ntemplate <typename F>\nSlotInBiggestAdviceFirstResult SlotInBiggestAdviceFirst(\n    const std::vector<RegionShape<F>>& region_shapes) {\n  std::vector<RegionShape<F>> sorted_regions = region_shapes;\n  // NOTE(TomTaehoonKim): Sorted result might be different from the original\n  // See\n  // https://github.com/kroma-network/halo2/blob/7d0a369/halo2_proofs/src/layout/floor_planner/v1/strategy.rs#L202-L215\n  base::UnstableSort(sorted_regions.begin(), sorted_regions.end(),\n                     [](const RegionShape<F>& lhs, const RegionShape<F>& rhs) {\n                       // Count the number of advice columns\n                       size_t lhs_advice_cols = 0;\n                       for (const RegionColumn& column : lhs.columns()) {\n                         if (column.type() == RegionColumn::Type::kColumn) {\n                           const AnyColumnKey& c = column.column();\n                           if (c.type() == ColumnType::kAdvice) {\n                             ++lhs_advice_cols;\n                           }\n                         }\n                       }\n                       size_t rhs_advice_cols = 0;\n                       for (const RegionColumn& column : rhs.columns()) {\n                         if (column.type() == RegionColumn::Type::kColumn) {\n                           const AnyColumnKey& c = column.column();\n                           if (c.type() == ColumnType::kAdvice) {\n                             ++rhs_advice_cols;\n                           }\n                         }\n                       }\n                       // Sort by advice area (since this has the most\n                       // contention).\n                       return lhs_advice_cols * lhs.row_count() <\n                              rhs_advice_cols * rhs.row_count();\n                     });\n  std::reverse(sorted_regions.begin(), sorted_regions.end());\n\n  // Lay out the sorted regions.\n  SlotInResult<F> result = SlotIn(sorted_regions);\n\n  // Un-sort the regions so they match the original indexing.\n  base::UnstableSort(result.regions.begin(), result.regions.end(),\n                     [](const RegionInfo<F>& lhs, const RegionInfo<F>& rhs) {\n                       return lhs.region.region_index() <\n                              rhs.region.region_index();\n                     });\n  std::vector<RowIndex> region_starts = base::Map(\n      result.regions,\n      [](const RegionInfo<F>& region) { return region.region_start; });\n\n  return {region_starts, result.column_allocations};\n}\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_LAYOUT_FLOOR_PLANNER_V1_V1_STRATEGY_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/layout/layouter.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_LAYOUT_LAYOUTER_H_\n#define TACHYON_ZK_PLONK_LAYOUT_LAYOUTER_H_\n\n#include <memory>\n#include <optional>\n#include <string>\n\n#include \"tachyon/base/functional/callback.h\"\n#include \"tachyon/zk/base/row_types.h\"\n#include \"tachyon/zk/plonk/constraint_system/challenge.h\"\n#include \"tachyon/zk/plonk/constraint_system/constraint_system.h\"\n#include \"tachyon/zk/plonk/layout/lookup_table.h\"\n#include \"tachyon/zk/plonk/layout/region.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F>\nclass NamespacedLayouter;\n\n// A layout strategy within a circuit. The layouter is chip-agnostic and applies\n// its strategy to the context and config it is given.\n//\n// This abstracts over the circuit assignments, handling row indices etc.\ntemplate <typename F>\nclass Layouter {\n public:\n  using AssignRegionCallback = base::RepeatingCallback<void(Region<F>&)>;\n  using AssignLookupTableCallback = base::OnceCallback<void(LookupTable<F>&)>;\n\n  virtual ~Layouter() = default;\n\n  // Assign a region of gates to an absolute row number.\n  //\n  // Inside the closure, the chip may freely use relative offsets; the\n  // |Layouter| will treat these assignments as a single \"region\" within the\n  // circuit. Outside this closure, the |Layouter| is allowed to optimize as it\n  // sees fit.\n  virtual void AssignRegion(std::string_view name,\n                            AssignRegionCallback assign) = 0;\n\n  // Assign a table region to an absolute row number.\n  virtual void AssignLookupTable(std::string_view name,\n                                 AssignLookupTableCallback assign) = 0;\n\n  // Constrains a |cell| to equal an instance |column|'s row value at an\n  // absolute position.\n  virtual void ConstrainInstance(const Cell& cell,\n                                 const InstanceColumnKey& column,\n                                 RowIndex row) = 0;\n\n  // Queries the value of the given challenge.\n  //\n  // Returns |Value<F>::Unknown()| if the current synthesis phase is before the\n  // challenge can be queried.\n  virtual Value<F> GetChallenge(Challenge challenge) const = 0;\n\n  // Gets the \"root\" of this assignment, bypassing the namespacing.\n  //\n  // Not intended for downstream consumption; use |Layouter::Namespace()|\n  // instead.\n  virtual Layouter<F>* GetRoot() = 0;\n\n  // Creates a new (sub)namespace and enters into it.\n  //\n  // Not intended for downstream consumption; use |Layouter::Namespace()|\n  // instead.\n  virtual void PushNamespace(std::string_view name) = 0;\n\n  // Exits out of the existing namespace.\n  //\n  // Not intended for downstream consumption; use |Layouter::Namespace()|\n  // instead.\n  virtual void PopNamespace(const std::optional<std::string>& gadget_name) = 0;\n\n  // Enters into a namespace\n  std::unique_ptr<NamespacedLayouter<F>> Namespace(std::string_view name);\n};\n\n}  // namespace tachyon::zk::plonk\n\n#include \"tachyon/zk/plonk/layout/namespaced_layouter.h\"\n\n#endif  // TACHYON_ZK_PLONK_LAYOUT_LAYOUTER_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/layout/lookup_table.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_LAYOUT_LOOKUP_TABLE_H_\n#define TACHYON_ZK_PLONK_LAYOUT_LOOKUP_TABLE_H_\n\n#include <string>\n#include <utility>\n\n#include \"tachyon/math/base/rational_field.h\"\n#include \"tachyon/zk/base/row_types.h\"\n#include \"tachyon/zk/base/value.h\"\n#include \"tachyon/zk/plonk/layout/lookup_table_column.h\"\n\nnamespace tachyon::zk::plonk {\n\n// A lookup table in the circuit.\ntemplate <typename F>\nclass LookupTable {\n public:\n  using AssignCallback = base::OnceCallback<Value<F>()>;\n\n  class Layouter {\n   public:\n    using AssignCallback = base::OnceCallback<Value<math::RationalField<F>>()>;\n\n    virtual ~Layouter() = default;\n\n    // Assign a fixed value to a table cell.\n    //\n    // Return false if the table cell has already been assigned.\n    [[nodiscard]] virtual bool AssignCell(std::string_view name,\n                                          const LookupTableColumn& column,\n                                          RowIndex offset,\n                                          AssignCallback assign) {\n      return true;\n    }\n  };\n\n  explicit LookupTable(Layouter* layouter) : layouter_(layouter) {}\n\n  [[nodiscard]] bool AssignCell(std::string_view name,\n                                const LookupTableColumn& column,\n                                RowIndex offset, AssignCallback assign) {\n    return layouter_->AssignCell(name, column, offset, [&assign]() {\n      return Value<math::RationalField<F>>::Known(\n          math::RationalField<F>(std::move(assign).Run().value()));\n    });\n  }\n\n private:\n  // not owned\n  Layouter* const layouter_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_LAYOUT_LOOKUP_TABLE_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/layout/lookup_table_column.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_LAYOUT_LOOKUP_TABLE_COLUMN_H_\n#define TACHYON_ZK_PLONK_LAYOUT_LOOKUP_TABLE_COLUMN_H_\n\n#include <utility>\n\n#include \"absl/hash/hash.h\"\n\n#include \"tachyon/export.h\"\n#include \"tachyon/zk/plonk/base/column_key.h\"\n\nnamespace tachyon::zk::plonk {\n\n// A fixed column of a lookup table.\n//\n// A lookup table can be loaded into this column via\n// |Layouter::AssignLookupTable()]. Columns can currently only contain a single\n// table, but they may be used in multiple lookup arguments via\n// |ConstraintSystem::Lookup()|.\n//\n// Lookup table columns are always \"encumbered\" by the lookup arguments they are\n// used in; they cannot simultaneously be used as general fixed columns.\nclass TACHYON_EXPORT LookupTableColumn {\n public:\n  LookupTableColumn() = default;\n  explicit LookupTableColumn(const FixedColumnKey& column) : column_(column) {}\n\n  // The fixed column that this table column is stored in.\n  //\n  // # Security\n  //\n  // This inner column MUST NOT be exposed in the public API, or else chip\n  // developers can load lookup tables into their circuits without\n  // default-value-filling the columns, which can cause soundness bugs.\n  const FixedColumnKey& column() const { return column_; }\n\n  bool operator==(const LookupTableColumn& column) const {\n    return column_ == column.column_;\n  }\n  bool operator!=(const LookupTableColumn& column) const {\n    return column_ != column.column_;\n  }\n\n private:\n  FixedColumnKey column_;\n};\n\ntemplate <typename H>\nH AbslHashValue(H h, const LookupTableColumn& column) {\n  return H::combine(std::move(h), column.column());\n}\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_LAYOUT_LOOKUP_TABLE_COLUMN_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/layout/lookup_table_column_unittest.cc",
    "content": "#include \"tachyon/zk/plonk/layout/lookup_table_column.h\"\n\n#include \"absl/hash/hash_testing.h\"\n#include \"gtest/gtest.h\"\n\nnamespace tachyon::zk::plonk {\n\nTEST(LookupTableColumnTest, Hash) {\n  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(\n      std::make_tuple(LookupTableColumn(), LookupTableColumn(FixedColumnKey(1)),\n                      LookupTableColumn(FixedColumnKey(2)))));\n}\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/layout/namespaced_layouter.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_LAYOUT_NAMESPACED_LAYOUTER_H_\n#define TACHYON_ZK_PLONK_LAYOUT_NAMESPACED_LAYOUTER_H_\n\n#include <memory>\n#include <string>\n#include <utility>\n\n#include \"tachyon/zk/plonk/layout/layouter.h\"\n\n// This is a \"namespaced\" layouter which borrows a |Layouter| (pushing a\n// namespace context) and, when dropped, pops out of the namespace context.\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F>\nclass NamespacedLayouter : public Layouter<F> {\n public:\n  using AssignRegionCallback = typename Layouter<F>::AssignRegionCallback;\n  using AssignLookupTableCallback =\n      typename Layouter<F>::AssignLookupTableCallback;\n\n  explicit NamespacedLayouter(Layouter<F>* layouter) : layouter_(layouter) {}\n\n  ~NamespacedLayouter() override {\n    // TODO(chokobole): Understand why halo2 needs this\n    // See\n    // https://github.com/kroma-network/halo2/blob/7d0a369/halo2_proofs/src/circuit.rs#L554-L579.\n    layouter_->PopNamespace(std::nullopt);\n  }\n\n  // Layouter<F> methods\n  void AssignRegion(std::string_view name,\n                    AssignRegionCallback assign) override {\n    layouter_->AssignRegion(name, std::move(assign));\n  }\n\n  void AssignLookupTable(std::string_view name,\n                         AssignLookupTableCallback assign) override {\n    layouter_->AssignLookupTable(name, std::move(assign));\n  }\n\n  void ConstrainInstance(const Cell& cell, const InstanceColumnKey& column,\n                         RowIndex row) override {\n    layouter_->ConstrainInstance(cell, column, row);\n  }\n\n  Value<F> GetChallenge(Challenge challenge) const override {\n    return layouter_->GetChallenge(challenge);\n  }\n\n  Layouter<F>* GetRoot() override { return layouter_->GetRoot(); }\n\n  void PushNamespace(std::string_view) override {\n    NOTREACHED() << \"Only the root layouter is allowed to call PushNamespace()\";\n  }\n\n  void PopNamespace(const std::optional<std::string>&) override {\n    NOTREACHED() << \"Only the root layouter is allowed to call PopNamespace()\";\n  }\n\n private:\n  // not owned\n  Layouter<F>* const layouter_;\n};\n\ntemplate <typename F>\nstd::unique_ptr<NamespacedLayouter<F>> Layouter<F>::Namespace(\n    std::string_view name) {\n  GetRoot()->PushNamespace(name);\n  return std::make_unique<NamespacedLayouter<F>>(GetRoot());\n}\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_LAYOUT_NAMESPACED_LAYOUTER_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/layout/region.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_LAYOUT_REGION_H_\n#define TACHYON_ZK_PLONK_LAYOUT_REGION_H_\n\n#include <memory>\n#include <string>\n#include <utility>\n\n#include \"tachyon/base/functional/callback.h\"\n#include \"tachyon/zk/plonk/base/column_key.h\"\n#include \"tachyon/zk/plonk/constraint_system/selector.h\"\n#include \"tachyon/zk/plonk/layout/assigned_cell.h\"\n#include \"tachyon/zk/plonk/layout/region_layouter.h\"\n\nnamespace tachyon::zk::plonk {\n\n// See the comments in tachyon/zk/plonk/layout/region_layouter.h\ntemplate <typename F>\nclass Region {\n public:\n  using AssignCallback = base::OnceCallback<Value<F>()>;\n\n  explicit Region(RegionLayouter<F>* layouter) : layouter_(layouter) {}\n\n  void EnableSelector(std::string_view name, Selector selector,\n                      RowIndex offset) {\n    layouter_->EnableSelector(name, selector, offset);\n  }\n\n  void NameColumn(std::string_view name, const AnyColumnKey& column) {\n    layouter_->NameColumn(name, column);\n  }\n\n  AssignedCell<F> AssignAdvice(std::string_view name,\n                               const AdviceColumnKey& column, RowIndex offset,\n                               AssignCallback assign) {\n    Value<F> value = Value<F>::Unknown();\n    Cell cell =\n        layouter_->AssignAdvice(name, column, offset, [&value, &assign]() {\n          value = std::move(assign).Run();\n          return value.ToRationalFieldValue();\n        });\n    return {std::move(cell), std::move(value)};\n  }\n\n  AssignedCell<F> AssignAdviceFromConstant(std::string_view name,\n                                           const AdviceColumnKey& column,\n                                           RowIndex offset, const F& constant) {\n    Cell cell = layouter_->AssignAdviceFromConstant(\n        name, column, offset, math::RationalField<F>(constant));\n    return {std::move(cell), Value<F>::Known(constant)};\n  }\n\n  AssignedCell<F> AssignAdviceFromInstance(std::string_view name,\n                                           const InstanceColumnKey& instance,\n                                           RowIndex row,\n                                           const AdviceColumnKey& advice,\n                                           RowIndex offset) {\n    return layouter_->AssignAdviceFromInstance(name, instance, row, advice,\n                                               offset);\n  }\n\n  AssignedCell<F> AssignFixed(std::string_view name,\n                              const FixedColumnKey& column, RowIndex offset,\n                              AssignCallback assign) {\n    Value<F> value = Value<F>::Unknown();\n    Cell cell =\n        layouter_->AssignFixed(name, column, offset, [&value, &assign]() {\n          value = std::move(assign).Run();\n          return value.ToRationalFieldValue();\n        });\n    return {std::move(cell), std::move(value)};\n  }\n\n  void ConstrainConstant(const Cell& cell,\n                         const math::RationalField<F>& constant) {\n    layouter_->ConstrainConstant(cell, constant);\n  }\n\n  void ConstrainEqual(const Cell& left, const Cell& right) {\n    layouter_->ConstrainEqual(left, right);\n  }\n\n private:\n  // not owned\n  RegionLayouter<F>* const layouter_;\n};\n\ntemplate <typename F>\nvoid Selector::Enable(Region<F>& region, RowIndex offset) const {\n  region.EnableSelector(\"\", *this, offset);\n}\n\ntemplate <typename F>\nAssignedCell<F> AssignedCell<F>::CopyAdvice(std::string_view name,\n                                            Region<F>& region,\n                                            const AdviceColumnKey& column,\n                                            RowIndex offset) const {\n  AssignedCell<F> ret =\n      region.AssignAdvice(name, column, offset, [this]() { return value_; });\n  region.ConstrainEqual(ret.cell_, cell_);\n  return ret;\n}\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_LAYOUT_REGION_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/layout/region_column.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_LAYOUT_REGION_COLUMN_H_\n#define TACHYON_ZK_PLONK_LAYOUT_REGION_COLUMN_H_\n\n#include <string>\n#include <utility>\n\n#include \"absl/hash/hash.h\"\n\n#include \"tachyon/export.h\"\n#include \"tachyon/zk/plonk/base/column_key.h\"\n#include \"tachyon/zk/plonk/constraint_system/selector.h\"\n\nnamespace tachyon::zk::plonk {\n\nclass TACHYON_EXPORT RegionColumn {\n public:\n  // THE ORDER OF ELEMENTS ARE IMPORTANT!! DO NOT CHANGE!\n  // The comparison operator depends on the order of elements.\n  enum class Type {\n    kColumn,\n    kSelector,\n  };\n\n  RegionColumn() {}\n  explicit RegionColumn(const AnyColumnKey& column)\n      : type_(Type::kColumn), column_(column) {}\n  explicit RegionColumn(Selector selector)\n      : type_(Type::kSelector), selector_(selector) {}\n\n  Type type() const { return type_; }\n  const AnyColumnKey& column() const { return column_; }\n  Selector selector() const { return selector_; }\n\n  bool operator==(const RegionColumn& other) const {\n    if (type_ != other.type_) return false;\n    if (type_ == Type::kColumn) return column_ == other.column_;\n    return selector_ == other.selector_;\n  }\n  bool operator!=(const RegionColumn& other) const {\n    return !operator==(other);\n  }\n\n  bool operator<(const RegionColumn& other) const {\n    if (type_ == Type::kColumn && other.type_ == Type::kColumn) {\n      return column_ < other.column_;\n    } else if (type_ == Type::kSelector && other.type_ == Type::kSelector) {\n      return selector_.index() < other.selector_.index();\n    }\n    return static_cast<int>(type_) < static_cast<int>(other.type_);\n  }\n\n  std::string ToString() const {\n    if (type_ == Type::kColumn) {\n      return column_.ToString();\n    } else {\n      return selector_.ToString();\n    }\n  }\n\n private:\n  Type type_;\n  union {\n    AnyColumnKey column_;\n    Selector selector_;\n  };\n};\n\ntemplate <typename H>\nH AbslHashValue(H h, const RegionColumn& m) {\n  // NOTE(chokobole): |kTypePrefixAdder| should prevent it from being a suffix\n  // of the other.\n  // See https://abseil.io/docs/cpp/guides/hash#the-abslhashvalue-overload\n  constexpr static int kTypePrefixAdder = 100;\n  if (m.type() == RegionColumn::Type::kColumn) {\n    return H::combine(std::move(h),\n                      kTypePrefixAdder + static_cast<int>(m.type()),\n                      m.column());\n  } else {\n    return H::combine(std::move(h),\n                      kTypePrefixAdder + static_cast<int>(m.type()),\n                      m.selector());\n  }\n}\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_LAYOUT_REGION_COLUMN_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/layout/region_column_unittest.cc",
    "content": "#include \"tachyon/zk/plonk/layout/region_column.h\"\n\n#include \"absl/hash/hash_testing.h\"\n#include \"gtest/gtest.h\"\n\nnamespace tachyon::zk::plonk {\n\nTEST(RegionColumnTest, Order) {\n  EXPECT_FALSE(RegionColumn(FixedColumnKey()) < RegionColumn(FixedColumnKey()));\n\n  EXPECT_FALSE(RegionColumn(Selector::Simple(0)) <\n               RegionColumn(Selector::Simple(0)));\n  EXPECT_TRUE(RegionColumn(Selector::Simple(0)) <\n              RegionColumn(Selector::Simple(1)));\n\n  EXPECT_FALSE(RegionColumn(Selector::Complex(0)) <\n               RegionColumn(Selector::Complex(0)));\n  EXPECT_TRUE(RegionColumn(Selector::Complex(0)) <\n              RegionColumn(Selector::Complex(1)));\n\n  EXPECT_TRUE(RegionColumn(AnyColumnKey()) < RegionColumn(Selector::Simple(0)));\n  EXPECT_TRUE(RegionColumn(AnyColumnKey()) <\n              RegionColumn(Selector::Complex(0)));\n\n  EXPECT_TRUE(RegionColumn(InstanceColumnKey()) <\n              RegionColumn(Selector::Simple(0)));\n  EXPECT_TRUE(RegionColumn(InstanceColumnKey()) <\n              RegionColumn(Selector::Complex(0)));\n\n  EXPECT_TRUE(RegionColumn(AdviceColumnKey()) <\n              RegionColumn(Selector::Simple(0)));\n  EXPECT_TRUE(RegionColumn(AdviceColumnKey()) <\n              RegionColumn(Selector::Complex(0)));\n\n  EXPECT_TRUE(RegionColumn(FixedColumnKey()) <\n              RegionColumn(Selector::Simple(0)));\n  EXPECT_TRUE(RegionColumn(FixedColumnKey()) <\n              RegionColumn(Selector::Complex(0)));\n}\n\nTEST(RegionColumnTest, Hash) {\n  EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple(\n      RegionColumn(FixedColumnKey(0)), RegionColumn(FixedColumnKey(1)),\n      RegionColumn(AdviceColumnKey(0)), RegionColumn(AdviceColumnKey(1)),\n      RegionColumn(InstanceColumnKey(0)), RegionColumn(InstanceColumnKey(1)),\n      RegionColumn(Selector::Simple(0)), RegionColumn(Selector::Simple(1)),\n      RegionColumn(Selector::Complex(0)), RegionColumn(Selector::Complex(1)))));\n}\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/layout/region_layouter.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_LAYOUT_REGION_LAYOUTER_H_\n#define TACHYON_ZK_PLONK_LAYOUT_REGION_LAYOUTER_H_\n\n#include \"tachyon/base/functional/callback.h\"\n#include \"tachyon/math/base/rational_field.h\"\n#include \"tachyon/zk/base/row_types.h\"\n#include \"tachyon/zk/base/value.h\"\n#include \"tachyon/zk/plonk/base/column_key.h\"\n#include \"tachyon/zk/plonk/constraint_system/selector.h\"\n#include \"tachyon/zk/plonk/layout/assigned_cell.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F>\nclass RegionLayouter {\n public:\n  using AssignCallback = base::OnceCallback<Value<math::RationalField<F>>()>;\n\n  virtual ~RegionLayouter() = default;\n\n  // Enables a |selector| at the given |offset|.\n  virtual void EnableSelector(std::string_view name, Selector selector,\n                              RowIndex offset) {}\n\n  // Allows the circuit implementer to name a Column within a Region\n  // context.\n  //\n  // This is useful in order to improve the amount of information that\n  // |prover.Verify()| and |prover.AssertSatisfied()| can provide.\n  virtual void NameColumn(std::string_view name, const AnyColumnKey& column) {}\n\n  // Assign an advice column value (witness)\n  virtual Cell AssignAdvice(std::string_view name,\n                            const AdviceColumnKey& column, RowIndex offset,\n                            AssignCallback assign) = 0;\n\n  // Assigns a constant value to the column |advice| at |offset| within this\n  // region.\n  //\n  // The constant value will be assigned to a cell within one of the fixed\n  // columns configured via |ConstraintSystem::EnableConstant|.\n  virtual Cell AssignAdviceFromConstant(\n      std::string_view name, const AdviceColumnKey& column, RowIndex offset,\n      const math::RationalField<F>& constant) = 0;\n\n  // Assign the value of the instance column's cell at absolute location\n  // |row| to the column |advice| at |offset| within this region.\n  virtual AssignedCell<F> AssignAdviceFromInstance(\n      std::string_view name, const InstanceColumnKey& instance, RowIndex row,\n      const AdviceColumnKey& advice, RowIndex offset) = 0;\n\n  // Assign a fixed value\n  virtual Cell AssignFixed(std::string_view name, const FixedColumnKey& column,\n                           RowIndex offset, AssignCallback assign) = 0;\n\n  // Constrain a cell to have a constant value.\n  virtual void ConstrainConstant(const Cell& cell,\n                                 const math::RationalField<F>& constant) {}\n\n  // Constrain two cells to have the same value.\n  virtual void ConstrainEqual(const Cell& left, const Cell& right) {}\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_LAYOUT_REGION_LAYOUTER_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/layout/region_shape.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_LAYOUT_REGION_SHAPE_H_\n#define TACHYON_ZK_PLONK_LAYOUT_REGION_SHAPE_H_\n\n#include <algorithm>\n#include <utility>\n\n#include \"absl/container/flat_hash_set.h\"\n\n#include \"tachyon/export.h\"\n#include \"tachyon/zk/plonk/layout/region_column.h\"\n#include \"tachyon/zk/plonk/layout/region_layouter.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F>\nclass RegionShape : public RegionLayouter<F> {\n public:\n  using AssignCallback = typename RegionLayouter<F>::AssignCallback;\n\n  explicit RegionShape(size_t region_index) : region_index_(region_index) {}\n\n  size_t region_index() const { return region_index_; }\n  const absl::flat_hash_set<RegionColumn>& columns() const { return columns_; }\n  RowIndex row_count() const { return row_count_; }\n\n  // RegionLayouter methods\n  void EnableSelector(std::string_view, Selector selector,\n                      RowIndex offset) override {\n    UpdateColumnsAndRowCount(selector, offset);\n  }\n\n  Cell AssignAdvice(std::string_view, const AdviceColumnKey& column,\n                    RowIndex offset, AssignCallback) override {\n    UpdateColumnsAndRowCount(column, offset);\n    return {region_index_, offset, column};\n  }\n\n  Cell AssignAdviceFromConstant(\n      std::string_view name, const AdviceColumnKey& column, RowIndex offset,\n      const math::RationalField<F>& constant) override {\n    return AssignAdvice(name, column, offset, AssignCallback());\n  }\n\n  AssignedCell<F> AssignAdviceFromInstance(std::string_view,\n                                           const InstanceColumnKey&, RowIndex,\n                                           const AdviceColumnKey& advice,\n                                           RowIndex offset) override {\n    UpdateColumnsAndRowCount(advice, offset);\n    Cell cell(region_index_, offset, advice);\n    return {std::move(cell), Value<F>::Unknown()};\n  }\n\n  Cell AssignFixed(std::string_view, const FixedColumnKey& column,\n                   RowIndex offset, AssignCallback) override {\n    UpdateColumnsAndRowCount(column, offset);\n    return {region_index_, offset, column};\n  }\n\n private:\n  template <typename T>\n  void UpdateColumnsAndRowCount(const T& arg, RowIndex offset) {\n    columns_.insert(RegionColumn(arg));\n    row_count_ = std::max(row_count_, offset + 1);\n  }\n\n  size_t region_index_ = 0;\n  absl::flat_hash_set<RegionColumn> columns_;\n  RowIndex row_count_ = 0;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_LAYOUT_REGION_SHAPE_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/permutation/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"cycle_store\",\n    srcs = [\"cycle_store.cc\"],\n    hdrs = [\"cycle_store.h\"],\n    deps = [\n        \":label\",\n        \"//tachyon/base/containers:container_util\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"grand_product_argument\",\n    hdrs = [\"grand_product_argument.h\"],\n    deps = [\n        \"//tachyon/base:parallelize\",\n        \"//tachyon/zk/base/entities:prover_base\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"label\",\n    hdrs = [\"label.h\"],\n    deps = [\n        \"//tachyon:export\",\n        \"//tachyon/zk/base:row_types\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"permutation_argument\",\n    hdrs = [\"permutation_argument.h\"],\n    deps = [\n        \"//tachyon:export\",\n        \"//tachyon/base/containers:contains\",\n        \"//tachyon/zk/plonk/base:column_key\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"permutation_assembly\",\n    hdrs = [\"permutation_assembly.h\"],\n    deps = [\n        \":cycle_store\",\n        \":label\",\n        \":permutation_argument\",\n        \":permutation_proving_key\",\n        \":permutation_verifying_key\",\n        \":unpermuted_table\",\n        \"//tachyon:export\",\n        \"//tachyon/base:openmp_util\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/zk/base/entities:prover_base\",\n        \"//tachyon/zk/plonk/halo2:vendor\",\n        \"//tachyon/zk/plonk/vanishing:vanishing_utils\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"permutation_evaluator\",\n    hdrs = [\"permutation_evaluator.h\"],\n    deps = [\n        \":permutation_prover\",\n        \"//tachyon/base:profiler\",\n        \"//tachyon/zk/plonk/vanishing:circuit_polynomial_builder_forward\",\n        \"//tachyon/zk/plonk/vanishing:vanishing_utils\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"permutation_opening_point_set\",\n    hdrs = [\"permutation_opening_point_set.h\"],\n)\n\ntachyon_cc_library(\n    name = \"permutation_prover\",\n    hdrs = [\n        \"permutation_prover.h\",\n        \"permutation_prover_impl.h\",\n    ],\n    deps = [\n        \":grand_product_argument\",\n        \":permutation_argument\",\n        \":permutation_opening_point_set\",\n        \":permutation_proving_key\",\n        \":permutation_table_store\",\n        \":permutation_utils\",\n        \"//tachyon/base:profiler\",\n        \"//tachyon/base:ref\",\n        \"//tachyon/base/functional:functor_traits\",\n        \"//tachyon/crypto/commitments:polynomial_openings\",\n        \"//tachyon/zk/base:blinded_polynomial\",\n        \"//tachyon/zk/base:row_types\",\n        \"//tachyon/zk/base/entities:prover_base\",\n        \"//tachyon/zk/plonk/base:multi_phase_ref_table\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"permutation_proving_key\",\n    hdrs = [\"permutation_proving_key.h\"],\n    deps = [\"//tachyon/zk/plonk/keys:c_proving_key_impl_forward\"],\n)\n\ntachyon_cc_library(\n    name = \"permutation_table_store\",\n    hdrs = [\"permutation_table_store.h\"],\n    deps = [\n        \":permuted_table\",\n        \":unpermuted_table\",\n        \"//tachyon/zk/plonk/base:ref_table\",\n        \"@com_google_absl//absl/types:span\",\n        \"@com_google_googletest//:gtest_prod\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"permutation_utils\",\n    hdrs = [\"permutation_utils.h\"],\n    deps = [\n        # TODO(chokobole): Remove this dependency once fixing issue specified in permutation_utils.h.\n        \"//tachyon/math/elliptic_curves/bn/bn254:fr\",\n        \"//tachyon/base/numerics:checked_math\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"permutation_verifier\",\n    hdrs = [\"permutation_verifier.h\"],\n    deps = [\n        \":permutation_opening_point_set\",\n        \":permutation_utils\",\n        \":permutation_verifier_data\",\n        \"//tachyon/base/containers:adapters\",\n        \"//tachyon/crypto/commitments:polynomial_openings\",\n        \"//tachyon/zk/plonk/base:l_values\",\n        \"//tachyon/zk/plonk/constraint_system\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"permutation_verifier_data\",\n    hdrs = [\"permutation_verifier_data.h\"],\n    deps = [\"//tachyon/zk/plonk/base:multi_phase_evaluations\"],\n)\n\ntachyon_cc_library(\n    name = \"permutation_verifying_key\",\n    hdrs = [\"permutation_verifying_key.h\"],\n)\n\ntachyon_cc_library(\n    name = \"permuted_table\",\n    hdrs = [\"permuted_table.h\"],\n    deps = [\n        \":label\",\n        \"//tachyon/base:range\",\n        \"//tachyon/base:ref\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"unpermuted_table\",\n    hdrs = [\"unpermuted_table.h\"],\n    deps = [\n        \":label\",\n        \":permutation_utils\",\n        \"//tachyon/base:openmp_util\",\n        \"//tachyon/base:range\",\n        \"//tachyon/base:ref\",\n        \"//tachyon/base/containers:container_util\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"permutation_unittests\",\n    srcs = [\n        \"cycle_store_unittest.cc\",\n        \"permutation_assembly_unittest.cc\",\n        \"permutation_table_store_unittest.cc\",\n        \"permutation_utils_unittest.cc\",\n        \"unpermuted_table_unittest.cc\",\n    ],\n    deps = [\n        \":permutation_assembly\",\n        \":permutation_table_store\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:fq\",\n        \"//tachyon/math/elliptic_curves/short_weierstrass/test:sw_curve_config\",\n        \"//tachyon/math/finite_fields/test:finite_field_test\",\n        \"//tachyon/math/finite_fields/test:gf7\",\n        \"//tachyon/math/polynomials/univariate:univariate_evaluation_domain_factory\",\n        \"//tachyon/zk/plonk/halo2:bn254_shplonk_prover_test\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/zk/plonk/permutation/cycle_store.cc",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#include \"tachyon/zk/plonk/permutation/cycle_store.h\"\n\n#include <utility>\n\nnamespace tachyon::zk::plonk {\n\nbool CycleStore::MergeCycle(const Label& label, const Label& label2) {\n  Label left_cycle_base = GetCycleBase(label);\n  Label right_cycle_base = GetCycleBase(label2);\n  if (left_cycle_base == right_cycle_base) return false;\n\n  // Ensure that the cell with a larger cycle size becomes the left.\n  if (sizes_[left_cycle_base] < sizes_[right_cycle_base]) {\n    std::swap(left_cycle_base, right_cycle_base);\n  }\n\n  // Merge the right cycle into the left one.\n  sizes_[left_cycle_base] += sizes_[right_cycle_base];\n  Label l = right_cycle_base;\n  while (true) {\n    aux_[l] = left_cycle_base;\n    l = mapping_[l];\n    if (l == right_cycle_base) {\n      break;\n    }\n  }\n\n  std::swap(mapping_[label], mapping_[label2]);\n  return true;\n}\n\nstd::vector<Label> CycleStore::GetAllLabels(const Label& label) const {\n  std::vector<Label> ret;\n  Label base = GetCycleBase(label);\n  Label l = base;\n  ret.push_back(l);\n  while (true) {\n    l = mapping_[l];\n    ret.push_back(l);\n    if (l == base) {\n      break;\n    }\n  }\n  return ret;\n}\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/permutation/cycle_store.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_PERMUTATION_CYCLE_STORE_H_\n#define TACHYON_ZK_PLONK_PERMUTATION_CYCLE_STORE_H_\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/export.h\"\n#include \"tachyon/zk/plonk/permutation/label.h\"\n\nnamespace tachyon::zk::plonk {\n\n// CycleStore stores cycle that is used to constrain equality between values.\n//\n//   For example, given two disjoint cycles (A B C D) and (E F G H):\n//\n//   A +---> B\n//   ^       +\n//   |       |\n//   +       v\n//   D <---+ C       E +---> F\n//                   ^       +\n//                   |       |\n//                   +       v\n//                   H <---+ G\n//\n//   CycleStore store;\n//   Label a; // A\n//   Label b; // B\n//   Label e; // E\n//\n//   CHECK_EQ(store.GetCycleBase(a), store.GetCycleBase(b));\n//   CHECK(store.CheckSameCycle(a, b));\n//   CHECK_NE(store.GetCycleBase(a), store.GetCycleBase(e));\n//   CHECK(!store.CheckSameCycle(a, e));\n//\n//   CHECK_EQ(store.GetCycleSize(a), size_t{4});\n//\n//   After adding constraint B ≡ E the above algorithm produces the cycle:\n//\n//   A +---> B +-------------+\n//   ^                       |\n//   |                       |\n//   +                       v\n//   D <---+ C <---+ E       F\n//                   ^       +\n//                   |       |\n//                   +       v\n//                   H <---+ G\n//\n//   CHECK_EQ(store.GetCycleBase(a), store.GetCycleBase(b));\n//   CHECK(store.CheckSameCycle(a, b));\n//   CHECK_EQ(store.GetCycleBase(a), store.GetCycleBase(e));\n//   CHECK(store.CheckSameCycle(a, e));\n//\n//   CHECK_EQ(store.GetCycleSize(a), size_t{8});\n//\n// See\n// https://zcash.github.io/halo2/design/proving-system/permutation.html#algorithm.\nclass TACHYON_EXPORT CycleStore {\n public:\n  template <typename T>\n  class Table {\n   public:\n    Table() = default;\n    explicit Table(std::vector<std::vector<T>>&& values)\n        : values_(std::move(values)) {}\n\n    T& operator[](const Label& l) { return values_[l.col][l.row]; }\n    const T& operator[](const Label& l) const { return values_[l.col][l.row]; }\n\n    bool operator==(const Table<T>& other) const {\n      return values_ == other.values_;\n    }\n    bool operator!=(const Table<T>& other) const {\n      return values_ != other.values_;\n    }\n\n    bool IsEmpty() const { return values_.empty(); }\n\n   private:\n    std::vector<std::vector<T>> values_;\n  };\n\n  CycleStore() = default;\n  CycleStore(size_t cols, RowIndex rows) {\n    mapping_ = Table(base::CreateVector(cols, [rows](size_t col) {\n      return base::CreateVector(\n          rows, [col](RowIndex row) { return Label(col, row); });\n    }));\n    aux_ = mapping_;\n    sizes_ = Table(std::vector<std::vector<size_t>>(\n        cols, std::vector<size_t>(rows, size_t{1})));\n  }\n\n  const Table<Label>& mapping() const { return mapping_; }\n  const Table<Label>& aux() const { return aux_; }\n  const Table<size_t>& sizes() const { return sizes_; }\n\n  // Return the next label of given |label| within a cycle.\n  const Label& GetNextLabel(const Label& label) const {\n    return mapping_[label];\n  }\n\n  // Return the representative of cycle of given |label|.\n  const Label& GetCycleBase(const Label& label) const { return aux_[label]; }\n\n  // Return the size of the representative of cycle of given |label|.\n  size_t GetCycleSize(const Label& label) const {\n    return sizes_[GetCycleBase(label)];\n  }\n\n  // Return whether the representative of cycles of given |label| and |label2|\n  // belong to the same cycle.\n  bool CheckSameCycle(const Label& label, const Label& label2) const {\n    return GetCycleBase(label) == GetCycleBase(label2);\n  }\n\n  // Return false if the cycles of |label| and |label2| are same.\n  // Return true if two cycles are merged.\n  bool MergeCycle(const Label& label, const Label& label2);\n\n  // Return all the labels that are belong to the cycle of given |label|.\n  std::vector<Label> GetAllLabels(const Label& label) const;\n\n private:\n  // |mapping_| keeps track of the next label for each cycle.\n  Table<Label> mapping_;\n  // |aux_| keeps track of the cycle for each label.\n  Table<Label> aux_;\n  // |sizes_| keeps track of the size of each cycle.\n  Table<size_t> sizes_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_PERMUTATION_CYCLE_STORE_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/permutation/cycle_store_unittest.cc",
    "content": "// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#include \"tachyon/zk/plonk/permutation/cycle_store.h\"\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/random.h\"\n\nnamespace tachyon::zk::plonk {\n\nTEST(CycleStoreTest, MergeCycle) {\n  constexpr size_t kCols = 10;\n  constexpr RowIndex kRows = 10;\n\n  CycleStore store(kCols, kRows);\n  std::vector<Label> labels;\n  labels.reserve(kCols * kRows);\n  for (size_t col = 0; col < kCols; ++col) {\n    for (RowIndex row = 0; row < kRows; ++row) {\n      Label l(col, row);\n      EXPECT_EQ(store.GetCycleBase(l), l);\n      EXPECT_EQ(store.GetCycleSize(l), size_t{1});\n      labels.push_back(l);\n    }\n  }\n\n  for (int i = 0; i < 10; ++i) {\n    Label label = base::UniformElement(labels);\n    Label label2 = base::UniformElement(labels);\n\n    bool belong_to_same_cycle = store.CheckSameCycle(label, label2);\n    size_t cycle_size = store.GetCycleSize(label);\n    size_t cycle_size2 = store.GetCycleSize(label2);\n    EXPECT_NE(belong_to_same_cycle, store.MergeCycle(label, label2));\n    if (belong_to_same_cycle) {\n      EXPECT_EQ(cycle_size, cycle_size2);\n      EXPECT_EQ(cycle_size, store.GetCycleSize(label));\n      EXPECT_EQ(cycle_size2, store.GetCycleSize(label2));\n    } else {\n      EXPECT_EQ(cycle_size + cycle_size2, store.GetCycleSize(label));\n      EXPECT_EQ(cycle_size + cycle_size2, store.GetCycleSize(label2));\n      EXPECT_TRUE(store.CheckSameCycle(label, label2));\n    }\n    EXPECT_THAT(store.GetAllLabels(label),\n                testing::UnorderedElementsAreArray(store.GetAllLabels(label2)));\n  }\n}\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/permutation/grand_product_argument.h",
    "content": "#ifndef TACHYON_ZK_PLONK_PERMUTATION_GRAND_PRODUCT_ARGUMENT_H_\n#define TACHYON_ZK_PLONK_PERMUTATION_GRAND_PRODUCT_ARGUMENT_H_\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/parallelize.h\"\n#include \"tachyon/zk/base/entities/prover_base.h\"\n\nnamespace tachyon::zk::plonk {\n\nclass GrandProductArgument {\n public:\n  // If the number of rows is within than the supported size of polynomial\n  // commitment scheme, you should use this version. See lookup argument for use\n  // case.\n  template <typename PCS, typename Callable,\n            typename Evals = typename PCS::Evals>\n  static Evals CreatePoly(ProverBase<PCS>* prover, Callable numerator_callback,\n                          Callable denominator_callback) {\n    using F = typename Evals::Field;\n\n    // NOTE(chokobole): It's safe to downcast because domain is already checked.\n    RowIndex size = static_cast<RowIndex>(prover->pcs().N());\n\n    // NOTE(batzor): This vector is initialized below in |denominator_callback|\n    // so it is safe to keep it uninitialized here. First element is set to\n    // |last_z| in |DoCreatePoly|.\n    std::vector<F> z(size + 1);\n    absl::Span<F> grand_product = absl::MakeSpan(z).subspan(1);\n\n    base::Parallelize(grand_product, std::move(denominator_callback));\n\n    CHECK(F::BatchInverseInPlace(grand_product));\n\n    base::Parallelize(grand_product, std::move(numerator_callback));\n\n    F last_z = F::One();\n    return DoCreatePoly(prover, last_z, std::move(z));\n  }\n\n  template <typename PCS, typename Callable,\n            typename Evals = typename PCS::Evals>\n  static Evals CreatePolySerial(ProverBase<PCS>* prover,\n                                Callable numerator_callback,\n                                Callable denominator_callback) {\n    using F = typename Evals::Field;\n\n    // NOTE(chokobole): It's safe to downcast because domain is already checked.\n    RowIndex size = static_cast<RowIndex>(prover->pcs().N());\n\n    // NOTE(batzor): This vector is initialized below in |denominator_callback|\n    // so it is safe to keep it uninitialized here. First element is set to\n    // |last_z| in |DoCreatePoly|.\n    std::vector<F> z(size + 1);\n    absl::Span<F> grand_product = absl::MakeSpan(z).subspan(1);\n\n    for (RowIndex i = 0; i < size; ++i) {\n      grand_product[i] = denominator_callback(i);\n    }\n\n    CHECK(F::BatchInverseInPlaceSerial(grand_product));\n\n    for (RowIndex i = 0; i < size; ++i) {\n      grand_product[i] *= numerator_callback(i);\n    }\n\n    F last_z = F::One();\n    return DoCreatePoly(prover, last_z, std::move(z));\n  }\n\n  // If the number of rows is out of the supported size of polynomial\n  // commitment scheme, you should use this version. See permutation argument\n  // for use case.\n  // See\n  // https://zcash.github.io/halo2/design/proving-system/permutation.html#spanning-a-large-number-of-columns\n  template <typename PCS, typename Callable, typename F,\n            typename Evals = typename PCS::Evals>\n  static Evals CreateExcessivePoly(ProverBase<PCS>* prover,\n                                   Callable numerator_callback,\n                                   Callable denominator_callback,\n                                   size_t num_cols, F& last_z) {\n    // NOTE(chokobole): It's safe to downcast because domain is already checked.\n    RowIndex size = static_cast<RowIndex>(prover->pcs().N());\n    std::vector<F> z(size + 1, F::One());\n    absl::Span<F> grand_product = absl::MakeSpan(z).subspan(1);\n\n    base::Parallelize(\n        grand_product.size(),\n        [&grand_product, numerator_callback, denominator_callback, num_cols](\n            size_t len, size_t chunk_offset, size_t chunk_size) {\n          RowIndex start = chunk_offset * chunk_size;\n          for (size_t i = 0; i < num_cols; ++i) {\n            for (RowIndex j = start; j < start + len; ++j) {\n              grand_product[j] *= denominator_callback(i, j);\n            }\n          }\n\n          auto grand_subspan = grand_product.subspan(start, len);\n          CHECK(F::BatchInverseInPlaceSerial(grand_subspan));\n\n          for (size_t i = 0; i < num_cols; ++i) {\n            for (RowIndex j = start; j < start + len; ++j) {\n              grand_product[j] *= numerator_callback(i, j);\n            }\n          }\n        });\n\n    return DoCreatePoly(prover, last_z, std::move(z));\n  }\n\n private:\n  template <typename PCS, typename F, typename Evals = typename PCS::Evals>\n  static Evals DoCreatePoly(ProverBase<PCS>* prover, F& last_z,\n                            std::vector<F>&& grand_product) {\n    RowIndex usable_rows = prover->GetUsableRows();\n\n    // TODO(chokobole): Apply the same optimization trick used in grand sum.\n    absl::Span<F> z(grand_product);\n    z[0] = last_z;\n    for (RowIndex i = 0; i < usable_rows; ++i) {\n      z[i + 1] = z[i] * grand_product[i + 1];\n    }\n    last_z = z[usable_rows];\n    grand_product.pop_back();\n\n    Evals z_evals(std::move(grand_product));\n    CHECK(prover->blinder().Blind(z_evals));\n    return z_evals;\n  }\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_PERMUTATION_GRAND_PRODUCT_ARGUMENT_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/permutation/label.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_PERMUTATION_LABEL_H_\n#define TACHYON_ZK_PLONK_PERMUTATION_LABEL_H_\n\n#include <stddef.h>\n\n#include <string>\n\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/export.h\"\n#include \"tachyon/zk/base/row_types.h\"\n\nnamespace tachyon::zk::plonk {\n\nstruct TACHYON_EXPORT Label {\n  size_t col = 0;\n  RowIndex row = 0;\n\n  constexpr Label(size_t col, RowIndex row) : col(col), row(row) {}\n\n  bool operator==(const Label& other) const {\n    return col == other.col && row == other.row;\n  }\n  bool operator!=(const Label& other) const { return !operator==(other); }\n\n  std::string ToString() const {\n    return absl::Substitute(\"($0, $1)\", col, row);\n  }\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_PERMUTATION_LABEL_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/permutation/permutation_argument.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_PERMUTATION_PERMUTATION_ARGUMENT_H_\n#define TACHYON_ZK_PLONK_PERMUTATION_PERMUTATION_ARGUMENT_H_\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/containers/contains.h\"\n#include \"tachyon/export.h\"\n#include \"tachyon/zk/plonk/base/column_key.h\"\n\nnamespace tachyon::zk::plonk {\n\nclass TACHYON_EXPORT PermutationArgument {\n public:\n  PermutationArgument() = default;\n  explicit PermutationArgument(const std::vector<AnyColumnKey>& columns)\n      : columns_(columns) {}\n  explicit PermutationArgument(std::vector<AnyColumnKey>&& columns)\n      : columns_(std::move(columns)) {}\n\n  const std::vector<AnyColumnKey>& columns() const { return columns_; }\n\n  void AddColumn(const AnyColumnKey& column) {\n    if (base::Contains(columns_, column)) return;\n    columns_.push_back(column);\n  }\n\n  // Returns the minimum circuit degree required by the permutation argument.\n  // The argument may use larger degree gates depending on the actual\n  // circuit's degree and how many columns are involved in the permutation.\n  size_t RequiredDegree() const {\n    // See https://zcash.github.io/halo2/design/proving-system/permutation.html\n    // for more details.\n    //\n    // degree 2:\n    // l_first(X) * (1 - Z(X)) = 0\n    //\n    // We will fit as many polynomials pᵢ(X) as possible into the required\n    // degree of the circuit, so the following will not affect the required\n    // degree of this middleware.\n    //\n    // clang-format off\n    // (1 - (l_last(X) + l_blind(X))) * (Z(ω * X) Π (pᵢ(X) + β * sᵢ(X) + γ) - Z(X) Π (pᵢ(X) + β * δⁱ * X + γ))\n    // clang-format on\n    //\n    // On the first sets of columns, except the first set, we will do\n    //\n    // l_first(X) * (Z(X) - Z'(ω^(last) X)) = 0\n    //\n    // where Z'(X) is the permutation for the previous set of columns.\n    //\n    // On the final set of columns, we will do\n    //\n    // degree 3:\n    // l_last(X) * (Z'(X)² - Z'(X)) = 0\n    //\n    // which will allow the last value to be zero to ensure the argument is\n    // perfectly complete.\n\n    // There are constraints of degree 3 regardless of the number of columns\n    // involved.\n    return 3;\n  }\n\n private:\n  std::vector<AnyColumnKey> columns_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_PERMUTATION_PERMUTATION_ARGUMENT_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/permutation/permutation_argument_unittest.cc",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#include \"tachyon/zk/plonk/permutation/permutation_argument.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/random.h\"\n\nnamespace tachyon::zk::plonk {\n\nTEST(PermutationArgumentTest, AddColumn) {\n  std::vector<AnyColumnKey> column_keys = {\n      FixedColumnKey(0), AdviceColumnKey(0), InstanceColumnKey(0),\n      FixedColumnKey(1), AdviceColumnKey(1), InstanceColumnKey(1),\n      FixedColumnKey(2), AdviceColumnKey(2), InstanceColumnKey(2),\n  };\n  PermutationArgument argument(column_keys);\n\n  // Already included column should not be added.\n  AnyColumnKey col = base::UniformElement(column_keys);\n  argument.AddColumn(col);\n  EXPECT_EQ(argument.columns(), column_keys);\n\n  // Advice column whose phase is different should be added.\n  AdviceColumnKey col2(1, Phase(3));\n  argument.AddColumn(col2);\n  column_keys.push_back(col2);\n  EXPECT_EQ(argument.columns(), column_keys);\n\n  // New Instance column should be added.\n  InstanceColumnKey col3(3);\n  argument.AddColumn(col3);\n  column_keys.push_back(col3);\n  EXPECT_EQ(argument.columns(), column_keys);\n}\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/permutation/permutation_assembly.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_PERMUTATION_PERMUTATION_ASSEMBLY_H_\n#define TACHYON_ZK_PLONK_PERMUTATION_PERMUTATION_ASSEMBLY_H_\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/openmp_util.h\"\n#include \"tachyon/export.h\"\n#include \"tachyon/zk/base/entities/prover_base.h\"\n#include \"tachyon/zk/plonk/halo2/vendor.h\"\n#include \"tachyon/zk/plonk/permutation/cycle_store.h\"\n#include \"tachyon/zk/plonk/permutation/label.h\"\n#include \"tachyon/zk/plonk/permutation/permutation_argument.h\"\n#include \"tachyon/zk/plonk/permutation/permutation_proving_key.h\"\n#include \"tachyon/zk/plonk/permutation/permutation_verifying_key.h\"\n#include \"tachyon/zk/plonk/permutation/unpermuted_table.h\"\n#include \"tachyon/zk/plonk/vanishing/vanishing_utils.h\"\n\nnamespace tachyon::zk::plonk {\n\n// Struct that accumulates all the necessary data in order to construct the\n// permutation argument.\nclass TACHYON_EXPORT PermutationAssembly {\n public:\n  PermutationAssembly() = default;\n\n  PermutationAssembly(const PermutationArgument& p, RowIndex rows)\n      : PermutationAssembly(p.columns(), rows) {}\n\n  PermutationAssembly(const std::vector<AnyColumnKey>& columns, RowIndex rows)\n      : columns_(columns),\n        cycle_store_(CycleStore(columns_.size(), rows)),\n        rows_(rows) {}\n\n  PermutationAssembly(std::vector<AnyColumnKey>&& columns, RowIndex rows)\n      : columns_(std::move(columns)),\n        cycle_store_(CycleStore(columns_.size(), rows)),\n        rows_(rows) {}\n\n  static PermutationAssembly CreateForTesting(std::vector<AnyColumnKey> columns,\n                                              CycleStore cycle_store,\n                                              RowIndex rows) {\n    PermutationAssembly ret;\n    ret.columns_ = std::move(columns);\n    ret.cycle_store_ = std::move(cycle_store);\n    ret.rows_ = rows;\n    return ret;\n  }\n\n  const std::vector<AnyColumnKey>& columns() const { return columns_; }\n  const CycleStore& cycle_store() const { return cycle_store_; }\n\n  void Copy(const AnyColumnKey& left_column, RowIndex left_row,\n            const AnyColumnKey& right_column, RowIndex right_row) {\n    CHECK_LE(left_row, rows_);\n    CHECK_LE(right_row, rows_);\n\n    // Get indices of each column.\n    size_t left_col_idx = GetColumnIndex(left_column);\n    size_t right_col_idx = GetColumnIndex(right_column);\n\n    cycle_store_.MergeCycle(Label(left_col_idx, left_row),\n                            Label(right_col_idx, right_row));\n  }\n\n  // Returns |PermutationVerifyingKey| which has commitments for permutations.\n  template <typename PCS, typename Evals,\n            typename Commitment = typename PCS::Commitment>\n  constexpr PermutationVerifyingKey<Commitment> BuildVerifyingKey(\n      const Entity<PCS>* entity, const std::vector<Evals>& permutations) const {\n    const PCS& pcs = entity->pcs();\n    return PermutationVerifyingKey<Commitment>(\n        base::Map(permutations, [&pcs](const Evals& permutation) {\n          Commitment commitment;\n          CHECK(pcs.CommitLagrange(permutation, &commitment));\n          return commitment;\n        }));\n  }\n\n  // Returns the |PermutationProvingKey| that has the coefficient form and\n  // evaluation form of the permutation.\n  template <halo2::Vendor Vendor, typename PCS,\n            typename Poly = typename PCS::Poly,\n            typename Evals = typename PCS::Evals,\n            typename ExtendedEvals = typename PCS::ExtendedEvals>\n  constexpr PermutationProvingKey<Poly, Evals, ExtendedEvals> BuildProvingKey(\n      const ProverBase<PCS>* prover, std::vector<Evals>&& permutations) const {\n    using Domain = typename PCS::Domain;\n\n    const Domain* domain = prover->domain();\n\n    // The polynomials of permutations with coefficients.\n    std::vector<Poly> polys =\n        base::Map(permutations,\n                  [domain](const Evals& evals) { return domain->IFFT(evals); });\n\n    std::vector<ExtendedEvals> cosets;\n    if constexpr (Vendor == halo2::Vendor::kPSE) {\n      using ExtendedDomain = typename PCS::ExtendedDomain;\n\n      const ExtendedDomain* extended_domain = prover->extended_domain();\n      cosets = base::Map(polys, [extended_domain](const Poly& poly) {\n        return CoeffToExtended(poly, extended_domain);\n      });\n    }\n\n    return PermutationProvingKey<Poly, Evals, ExtendedEvals>(\n        std::move(permutations), std::move(polys), std::move(cosets));\n  }\n\n  // Generate the permutation polynomials based on the accumulated copy\n  // permutations. Note that the permutation polynomials are in evaluation\n  // form.\n  template <typename Evals, typename Domain>\n  std::vector<Evals> GeneratePermutations(const Domain* domain) const {\n    CHECK_EQ(domain->size(), size_t{rows_});\n    // TODO(chokobole): This should be changed to be created just once, but this\n    // is created again in `permutation_argument_runner_impl.h`.\n    UnpermutedTable<Evals> unpermuted_table =\n        UnpermutedTable<Evals>::Construct(columns_.size(), rows_, domain);\n\n    // Init evaluation formed polynomials with all-zero coefficients.\n    std::vector<Evals> permutations(columns_.size(),\n                                    domain->template Zero<Evals>());\n\n    // Assign |unpermuted_table| to |permutations|.\n    OMP_PARALLEL_NESTED_FOR(size_t i = 0; i < permutations.size(); ++i) {\n      for (size_t j = 0; j < rows_; ++j) {\n        // NOTE(chokobole): It's safe to access since we created |kDegree|\n        // |Zeros()|.\n        permutations[i].at(j) =\n            unpermuted_table[cycle_store_.GetNextLabel(Label(i, j))];\n      }\n    }\n    return permutations;\n  }\n\n private:\n  size_t GetColumnIndex(const AnyColumnKey& column) const {\n    return base::FindIndex(columns_, column).value();\n  }\n\n  // Columns that participate on the copy permutation argument.\n  std::vector<AnyColumnKey> columns_;\n  CycleStore cycle_store_;\n  RowIndex rows_ = 0;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_PERMUTATION_PERMUTATION_ASSEMBLY_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/permutation/permutation_assembly_unittest.cc",
    "content": "#include \"tachyon/zk/plonk/permutation/permutation_assembly.h\"\n\n#include <memory>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/zk/plonk/halo2/bn254_shplonk_prover_test.h\"\n\nnamespace tachyon::zk::plonk {\nnamespace {\n\nclass PermutationAssemblyTest : public halo2::BN254SHPlonkProverTest {\n public:\n  void SetUp() override {\n    halo2::BN254SHPlonkProverTest::SetUp();\n\n    columns_ = {AnyColumnKey(0), AdviceColumnKey(1), FixedColumnKey(2),\n                InstanceColumnKey(3)};\n    argment_ = PermutationArgument(columns_);\n    assembly_ = PermutationAssembly::CreateForTesting(\n        columns_, CycleStore(columns_.size(), prover_->pcs().N()),\n        prover_->pcs().N());\n  }\n\n protected:\n  std::vector<AnyColumnKey> columns_;\n  PermutationArgument argment_;\n  PermutationAssembly assembly_;\n};\n\n}  // namespace\n\nTEST_F(PermutationAssemblyTest, GeneratePermutation) {\n  // Check initial permutation polynomials w/o any copy.\n  const Domain* domain = prover_->domain();\n  std::vector<Evals> permutations =\n      assembly_.GeneratePermutations<Evals>(domain);\n\n  size_t n = prover_->pcs().N();\n  UnpermutedTable<Evals> unpermuted_table =\n      UnpermutedTable<Evals>::Construct(columns_.size(), n, domain);\n\n  for (size_t i = 0; i < columns_.size(); ++i) {\n    for (size_t j = 0; j < n; ++j) {\n      EXPECT_EQ(permutations[i][j], unpermuted_table[Label(i, j)]);\n    }\n  }\n}\n\nTEST_F(PermutationAssemblyTest, BuildKeys) {\n  const halo2::PCS& pcs = prover_->pcs();\n\n  std::vector<Evals> permutations =\n      assembly_.GeneratePermutations<Evals>(prover_->domain());\n  size_t permutations_size = permutations.size();\n  PermutationVerifyingKey<Commitment> vk =\n      assembly_.BuildVerifyingKey(prover_.get(), permutations);\n  EXPECT_EQ(permutations_size, vk.commitments().size());\n\n  PermutationProvingKey<Poly, Evals, ExtendedEvals> pk =\n      assembly_.BuildProvingKey<halo2::Vendor::kScroll>(\n          prover_.get(), std::move(permutations));\n  EXPECT_EQ(permutations_size, pk.polys().size());\n\n  for (size_t i = 0; i < columns_.size(); ++i) {\n    Commitment commitment_evals;\n    ASSERT_TRUE(pcs.CommitLagrange(pk.permutations()[i], &commitment_evals));\n\n    Commitment commitment_poly;\n    ASSERT_TRUE(pcs.Commit(pk.polys()[i], &commitment_poly));\n\n    // |polys| and |permutations| in the |PermutationProvingKey| represent\n    // the same polynomial. so the commitments must be equal to each other.\n    EXPECT_EQ(commitment_evals, commitment_poly);\n\n    // |commitments| of |PermutationVerifyingKey| are commitments of\n    // |permutations| of |PermutationProvingKey|.\n    EXPECT_EQ(commitment_evals, vk.commitments()[i]);\n  }\n}\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/permutation/permutation_evaluator.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_PERMUTATION_PERMUTATION_EVALUATOR_H_\n#define TACHYON_ZK_PLONK_PERMUTATION_PERMUTATION_EVALUATOR_H_\n\n#include <vector>\n\n#include \"tachyon/base/profiler.h\"\n#include \"tachyon/zk/plonk/permutation/permutation_prover.h\"\n#include \"tachyon/zk/plonk/vanishing/circuit_polynomial_builder_forward.h\"\n#include \"tachyon/zk/plonk/vanishing/vanishing_utils.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename EvalsOrExtendedEvals>\nclass PermutationEvaluator {\n public:\n  using F = typename EvalsOrExtendedEvals::Field;\n\n  template <typename PS>\n  void Evaluate(CircuitPolynomialBuilder<PS>& builder, absl::Span<F> chunk,\n                size_t chunk_offset, size_t chunk_size) {\n    TRACE_EVENT(\"ProofGeneration\", \"Plonk::Permutation::Evaluator::Evaluate\");\n    if (product_cosets_.empty()) return;\n\n    TRACE_EVENT_BEGIN(\"Subtask\", \"Prepare for evaluation\");\n    const std::vector<AnyColumnKey>& column_keys =\n        builder.proving_key_.verifying_key()\n            .constraint_system()\n            .permutation()\n            .columns();\n    std::vector<std::vector<base::Ref<const EvalsOrExtendedEvals>>>\n        column_chunks = base::Map(\n            base::Chunked(column_keys, builder.chunk_len_),\n            [&builder](absl::Span<const AnyColumnKey> column_key_chunk) {\n              return builder.table_.GetColumns(column_key_chunk);\n            });\n    std::vector<absl::Span<const EvalsOrExtendedEvals>> coset_chunks =\n        base::Map(\n            base::Chunked(cosets_, builder.chunk_len_),\n            [](absl::Span<const EvalsOrExtendedEvals> chunk) { return chunk; });\n    TRACE_EVENT_END(\"Subtask\");\n\n    size_t start = chunk_offset * chunk_size;\n    F beta_term = builder.current_extended_omega_ * builder.omega_.Pow(start);\n    for (size_t i = 0; i < chunk.size(); ++i) {\n      size_t idx = start + i;\n\n      {\n        TRACE_EVENT(\"Subtask\", \"Enforce permutation constraints part 1\");\n        // Enforce only for the first set: l_first(X) * (1 - z₀(X)) = 0\n        chunk[i] *= builder.y_;\n        chunk[i] +=\n            (F::One() - product_cosets_.front()[idx]) * builder.l_first_[idx];\n\n        // Enforce only for the last set: l_last(X) * (zₗ(X)² - zₗ(X)) = 0\n        const EvalsOrExtendedEvals& last_coset = product_cosets_.back();\n        chunk[i] *= builder.y_;\n        chunk[i] +=\n            builder.l_last_[idx] * (last_coset[idx].Square() - last_coset[idx]);\n      }\n\n      {\n        TRACE_EVENT(\"Subtask\", \"Evaluate permutation constraints part 2\");\n        // Except for the first set, enforce:\n        // l_first(X) * (zⱼ(X) - zⱼ₋₁(ω⁻¹X)) = 0\n        RowIndex r_last =\n            builder.last_rotation_.GetIndex(idx, /*scale=*/1, builder.n_);\n        for (size_t j = 0; j < product_cosets_.size(); ++j) {\n          if (j == 0) continue;\n          chunk[i] *= builder.y_;\n          chunk[i] += builder.l_first_[idx] * (product_cosets_[j][idx] -\n                                               product_cosets_[j - 1][r_last]);\n        }\n      }\n\n      {\n        TRACE_EVENT(\"Subtask\", \"Evaluate permutation constraints part 3\");\n        // And for all the sets we enforce: (1 - (l_last(X) + l_blind(X))) *\n        // (zⱼ(ωX) * Πⱼ(p(X) + βsⱼ(X) + γ) - zⱼ(X) Πⱼ(p(X) + δʲβX + γ))\n        F current_delta = builder.delta_start_ * beta_term;\n        RowIndex r_next = Rotation(1).GetIndex(idx, /*scale=*/1, builder.n_);\n\n        for (size_t j = 0; j < product_cosets_.size(); ++j) {\n          const std::vector<base::Ref<const EvalsOrExtendedEvals>>&\n              column_chunk = column_chunks[j];\n          absl::Span<const EvalsOrExtendedEvals> coset_chunk = coset_chunks[j];\n          F left = product_cosets_[j][r_next];\n          for (size_t k = 0; k < column_chunk.size(); ++k) {\n            left *= (*column_chunk[k])[idx] +\n                    builder.beta_ * coset_chunk[k][idx] + builder.gamma_;\n          }\n          F right = product_cosets_[j][idx];\n          for (size_t k = 0; k < column_chunk.size(); ++k) {\n            right *= (*column_chunk[k])[idx] + current_delta + builder.gamma_;\n            current_delta *= builder.delta_;\n          }\n          chunk[i] *= builder.y_;\n          chunk[i] += (left - right) * builder.l_active_row_[idx];\n        }\n        beta_term *= builder.omega_;\n      }\n    }\n  }\n\n  template <typename PS>\n  void UpdateCosets(CircuitPolynomialBuilder<PS>& builder, size_t circuit_idx) {\n    using PCS = typename PS::PCS;\n    using Poly = typename PCS::Poly;\n    using Evals = typename PCS::Evals;\n\n    TRACE_EVENT(\"ProofGeneration\",\n                \"Plonk::Permutation::Evaluator::UpdateCosets\");\n\n    constexpr halo2::Vendor kVendor = PS::kVendor;\n\n    size_t num_permutations =\n        builder.permutation_provers_[circuit_idx].grand_product_polys().size();\n    if (num_permutations == 0) return;\n\n    {\n      TRACE_EVENT(\"Subtask\", \"Calculate product cosets\");\n      const PermutationProver<Poly, Evals>& prover =\n          builder.permutation_provers_[circuit_idx];\n      product_cosets_.resize(num_permutations);\n      for (size_t i = 0; i < num_permutations; ++i) {\n        if constexpr (kVendor == halo2::Vendor::kPSE) {\n          product_cosets_[i] = CoeffToExtended(\n              prover.grand_product_polys()[i].poly(), builder.extended_domain_);\n        } else {\n          product_cosets_[i] = builder.coset_domain_->FFT(\n              prover.grand_product_polys()[i].poly());\n        }\n      }\n    }\n\n    {\n      TRACE_EVENT(\"Subtask\", \"Update cosets\");\n      if constexpr (kVendor == halo2::Vendor::kPSE) {\n        cosets_ = builder.proving_key_.permutation_proving_key().cosets();\n      } else {\n        const std::vector<Poly>& polys =\n            builder.proving_key_.permutation_proving_key().polys();\n        owned_cosets_.resize(polys.size());\n        for (size_t i = 0; i < polys.size(); ++i) {\n          owned_cosets_[i] = builder.coset_domain_->FFT(polys[i]);\n        }\n        cosets_ = absl::MakeConstSpan(owned_cosets_);\n      }\n    }\n  }\n\n private:\n  std::vector<EvalsOrExtendedEvals> product_cosets_;\n  std::vector<EvalsOrExtendedEvals> owned_cosets_;\n  absl::Span<const EvalsOrExtendedEvals> cosets_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_PERMUTATION_PERMUTATION_EVALUATOR_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/permutation/permutation_opening_point_set.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_PERMUTATION_PERMUTATION_OPENING_POINT_SET_H_\n#define TACHYON_ZK_PLONK_PERMUTATION_PERMUTATION_OPENING_POINT_SET_H_\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F>\nstruct PermutationOpeningPointSet {\n  PermutationOpeningPointSet(const F& x, const F& x_next, const F& x_last)\n      : x(x), x_next(x_next), x_last(x_last) {}\n\n  const F& x;\n  const F& x_next;\n  const F& x_last;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_PERMUTATION_PERMUTATION_OPENING_POINT_SET_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/permutation/permutation_prover.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_PERMUTATION_PERMUTATION_PROVER_H_\n#define TACHYON_ZK_PLONK_PERMUTATION_PERMUTATION_PROVER_H_\n\n#include <stddef.h>\n\n#include <functional>\n#include <vector>\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/profiler.h\"\n#include \"tachyon/crypto/commitments/polynomial_openings.h\"\n#include \"tachyon/zk/base/blinded_polynomial.h\"\n#include \"tachyon/zk/base/entities/prover_base.h\"\n#include \"tachyon/zk/plonk/base/multi_phase_ref_table.h\"\n#include \"tachyon/zk/plonk/permutation/permutation_argument.h\"\n#include \"tachyon/zk/plonk/permutation/permutation_opening_point_set.h\"\n#include \"tachyon/zk/plonk/permutation/permutation_proving_key.h\"\n#include \"tachyon/zk/plonk/permutation/permutation_table_store.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename Poly, typename Evals>\nclass PermutationProver {\n public:\n  using F = typename Poly::Field;\n\n  const std::vector<BlindedPolynomial<Poly, Evals>>& grand_product_polys()\n      const {\n    return grand_product_polys_;\n  }\n\n  // Creates Zₚ,ᵢ for chunk index i.\n  //\n  // See Halo2 book to figure out logic in detail.\n  // https://zcash.github.io/halo2/design/proving-system/permutation.html\n  template <typename PCS, typename ExtendedEvals = typename PCS::ExtendedEvals>\n  static void BatchCreateGrandProductPolys(\n      std::vector<PermutationProver>& permutation_provers,\n      ProverBase<PCS>* prover, const PermutationArgument& argument,\n      const std::vector<MultiPhaseRefTable<Evals>>& tables,\n      size_t constraint_system_degree,\n      const PermutationProvingKey<Poly, Evals, ExtendedEvals>&\n          permutation_proving_key,\n      const F& beta, const F& gamma);\n\n  constexpr static size_t GetNumGrandProductPolysCommitments(\n      const std::vector<PermutationProver>& permutation_provers) {\n    if (permutation_provers.empty()) return 0;\n    return permutation_provers.size() *\n           permutation_provers[0].grand_product_polys_.size();\n  }\n\n  template <typename PCS>\n  static void BatchCommitGrandProductPolys(\n      const std::vector<PermutationProver>& permutation_provers,\n      ProverBase<PCS>* prover, size_t& commit_idx);\n\n  template <typename Domain>\n  static void TransformEvalsToPoly(\n      std::vector<PermutationProver>& permutation_provers,\n      const Domain* domain) {\n    TRACE_EVENT(\"ProofGeneration\",\n                \"Plonk::Permutation::Prover::TransformEvalsToPoly\");\n    VLOG(2) << \"Transform permutation virtual columns to polys\";\n    for (PermutationProver& permutation_prover : permutation_provers) {\n      permutation_prover.TransformEvalsToPoly(domain);\n    }\n  }\n\n  template <typename PCS>\n  static void BatchEvaluate(\n      const std::vector<PermutationProver>& permutation_provers,\n      ProverBase<PCS>* prover, const PermutationOpeningPointSet<F>& point_set) {\n    TRACE_EVENT(\"ProofGeneration\", \"Plonk::Permutation::Prover::BatchEvaluate\");\n    for (const PermutationProver& permutation_prover : permutation_provers) {\n      permutation_prover.Evaluate(prover, point_set);\n    }\n  }\n\n  template <typename PCS, typename ExtendedEvals = typename PCS::ExtendedEvals>\n  static void EvaluateProvingKey(\n      ProverBase<PCS>* prover,\n      const PermutationProvingKey<Poly, Evals, ExtendedEvals>& proving_key,\n      const PermutationOpeningPointSet<F>& point_set);\n\n  void Open(const PermutationOpeningPointSet<F>& point_set,\n            std::vector<crypto::PolynomialOpening<Poly>>& openings) const;\n\n  template <typename ExtendedEvals>\n  static void OpenPermutationProvingKey(\n      const PermutationProvingKey<Poly, Evals, ExtendedEvals>& proving_key,\n      const PermutationOpeningPointSet<F>& point_set,\n      std::vector<crypto::PolynomialOpening<Poly>>& openings);\n\n private:\n  template <typename PCS>\n  void CreateGrandProductPolys(ProverBase<PCS>* prover,\n                               const PermutationTableStore<Evals>& table_store,\n                               size_t chunk_num, const F& beta, const F& gamma);\n\n  template <typename Domain>\n  void TransformEvalsToPoly(const Domain* domain);\n\n  template <typename PCS>\n  void Evaluate(ProverBase<PCS>* prover,\n                const PermutationOpeningPointSet<F>& point_set) const;\n\n  static std::function<F(size_t, RowIndex)> CreateNumeratorCallback(\n      const std::vector<base::Ref<const Evals>>& unpermuted_columns,\n      const std::vector<base::Ref<const Evals>>& value_columns, const F& beta,\n      const F& gamma);\n\n  static std::function<F(size_t, RowIndex)> CreateDenominatorCallback(\n      const std::vector<base::Ref<const Evals>>& permuted_columns,\n      const std::vector<base::Ref<const Evals>>& value_columns, const F& beta,\n      const F& gamma);\n\n  // Zₚ,ᵢ(X)\n  std::vector<BlindedPolynomial<Poly, Evals>> grand_product_polys_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#include \"tachyon/zk/plonk/permutation/permutation_prover_impl.h\"\n\n#endif  // TACHYON_ZK_PLONK_PERMUTATION_PERMUTATION_PROVER_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/permutation/permutation_prover_impl.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_PERMUTATION_PERMUTATION_PROVER_IMPL_H_\n#define TACHYON_ZK_PLONK_PERMUTATION_PERMUTATION_PROVER_IMPL_H_\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/profiler.h\"\n#include \"tachyon/base/ref.h\"\n#include \"tachyon/zk/plonk/permutation/grand_product_argument.h\"\n#include \"tachyon/zk/plonk/permutation/permutation_prover.h\"\n#include \"tachyon/zk/plonk/permutation/permutation_utils.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename Poly, typename Evals>\ntemplate <typename PCS>\nvoid PermutationProver<Poly, Evals>::CreateGrandProductPolys(\n    ProverBase<PCS>* prover, const PermutationTableStore<Evals>& table_store,\n    size_t chunk_num, const F& beta, const F& gamma) {\n  TRACE_EVENT(\"Utils\", \"CreateGrandProductPolys\");\n  grand_product_polys_.reserve(chunk_num);\n\n  // Track the \"last\" value from the previous column set.\n  F last_z = F::One();\n\n  for (size_t i = 0; i < chunk_num; ++i) {\n    std::vector<base::Ref<const Evals>> permuted_columns =\n        table_store.GetPermutedColumns(i);\n    std::vector<base::Ref<const Evals>> unpermuted_columns =\n        table_store.GetUnpermutedColumns(i);\n    std::vector<base::Ref<const Evals>> value_columns =\n        table_store.GetValueColumns(i);\n\n    size_t chunk_size = table_store.GetChunkSize(i);\n    Evals grand_product_poly = GrandProductArgument::CreateExcessivePoly(\n        prover,\n        CreateNumeratorCallback(unpermuted_columns, value_columns, beta, gamma),\n        CreateDenominatorCallback(permuted_columns, value_columns, beta, gamma),\n        chunk_size, last_z);\n\n    grand_product_polys_.emplace_back(std::move(grand_product_poly),\n                                      prover->blinder().Generate());\n  }\n}\n\n// static\ntemplate <typename Poly, typename Evals>\ntemplate <typename PCS, typename ExtendedEvals>\nvoid PermutationProver<Poly, Evals>::BatchCreateGrandProductPolys(\n    std::vector<PermutationProver>& permutation_provers,\n    ProverBase<PCS>* prover, const PermutationArgument& argument,\n    const std::vector<MultiPhaseRefTable<Evals>>& tables,\n    size_t constraint_system_degree,\n    const PermutationProvingKey<Poly, Evals, ExtendedEvals>&\n        permutation_proving_key,\n    const F& beta, const F& gamma) {\n  TRACE_EVENT(\"ProofGeneration\",\n              \"Plonk::Permutation::Prover::BatchCreateGrandProductPolys\");\n  // How many columns can be included in a single permutation polynomial?\n  // We need to multiply by z(X) and (1 - (l_last(X) + l_blind(X))). This\n  // will never underflow because of the requirement of at least a degree\n  // 3 circuit for the permutation argument.\n  CHECK_GE(constraint_system_degree, argument.RequiredDegree());\n  if (argument.columns().empty()) return;\n\n  size_t chunk_len = ComputePermutationChunkLength(constraint_system_degree);\n  size_t chunk_num = (argument.columns().size() + chunk_len - 1) / chunk_len;\n\n  UnpermutedTable<Evals> unpermuted_table = UnpermutedTable<Evals>::Construct(\n      argument.columns().size(), prover->pcs().N(), prover->domain());\n  PermutedTable<Evals> permuted_table(&permutation_proving_key.permutations());\n  for (size_t i = 0; i < tables.size(); ++i) {\n    PermutationTableStore<Evals> table_store(argument.columns(), tables[i],\n                                             permuted_table, unpermuted_table,\n                                             chunk_len);\n    permutation_provers[i].CreateGrandProductPolys(prover, table_store,\n                                                   chunk_num, beta, gamma);\n  }\n}\n\n// static\ntemplate <typename Poly, typename Evals>\ntemplate <typename PCS>\nvoid PermutationProver<Poly, Evals>::BatchCommitGrandProductPolys(\n    const std::vector<PermutationProver>& permutation_provers,\n    ProverBase<PCS>* prover, size_t& commit_idx) {\n  TRACE_EVENT(\"ProofGeneration\",\n              \"Plonk::Permutation::Prover::BatchCommitGrandProductPolys\");\n  if (permutation_provers.empty()) return;\n\n  if constexpr (PCS::kSupportsBatchMode) {\n    for (const PermutationProver& permutation_prover : permutation_provers) {\n      for (const BlindedPolynomial<Poly, Evals>& grand_product_poly :\n           permutation_prover.grand_product_polys_) {\n        prover->BatchCommitAt(grand_product_poly.evals(), commit_idx++);\n      }\n    }\n  } else {\n    for (const PermutationProver& permutation_prover : permutation_provers) {\n      for (const BlindedPolynomial<Poly, Evals>& grand_product_poly :\n           permutation_prover.grand_product_polys_) {\n        prover->CommitAndWriteToProof(grand_product_poly.evals());\n      }\n    }\n  }\n}\n\ntemplate <typename Poly, typename Evals>\ntemplate <typename Domain>\nvoid PermutationProver<Poly, Evals>::TransformEvalsToPoly(\n    const Domain* domain) {\n  TRACE_EVENT(\"Utils\", \"TransformEvalsToPoly\");\n  for (BlindedPolynomial<Poly, Evals>& grand_product_poly :\n       grand_product_polys_) {\n    grand_product_poly.TransformEvalsToPoly(domain);\n  }\n}\n\ntemplate <typename Poly, typename Evals>\ntemplate <typename PCS>\nvoid PermutationProver<Poly, Evals>::Evaluate(\n    ProverBase<PCS>* prover,\n    const PermutationOpeningPointSet<F>& point_set) const {\n  TRACE_EVENT(\"Utils\", \"Evaluate\");\n  // THE ORDER IS IMPORTANT!! DO NOT CHANGE!\n  // See\n  // https://github.com/kroma-network/halo2/blob/7d0a369/halo2_proofs/src/plonk/permutation/prover.rs#L231-L276.\n  for (size_t i = 0; i < grand_product_polys_.size(); ++i) {\n    const Poly& poly = grand_product_polys_[i].poly();\n    // Zₚ,ᵢ(x)\n    prover->EvaluateAndWriteToProof(poly, point_set.x);\n    // Zₚ,ᵢ(ω * x)\n    prover->EvaluateAndWriteToProof(poly, point_set.x_next);\n    // If we have any remaining sets to process, evaluate this set at ωᵘ\n    // so we can constrain the last value of its running product to equal the\n    // first value of the next set's running product, chaining them together.\n    if (i != grand_product_polys_.size() - 1) {\n      // Zₚ,ᵢ(ω^(last) * x)\n      prover->EvaluateAndWriteToProof(poly, point_set.x_last);\n    }\n  }\n}\n\n// static\ntemplate <typename Poly, typename Evals>\ntemplate <typename PCS, typename ExtendedEvals>\nvoid PermutationProver<Poly, Evals>::EvaluateProvingKey(\n    ProverBase<PCS>* prover,\n    const PermutationProvingKey<Poly, Evals, ExtendedEvals>& proving_key,\n    const PermutationOpeningPointSet<F>& point_set) {\n  TRACE_EVENT(\"ProofGeneration\",\n              \"Plonk::Permutation::Prover::EvaluateProvingKey\");\n  for (const Poly& poly : proving_key.polys()) {\n    prover->EvaluateAndWriteToProof(poly, point_set.x);\n  }\n}\n\ntemplate <typename Poly, typename Evals>\nvoid PermutationProver<Poly, Evals>::Open(\n    const PermutationOpeningPointSet<F>& point_set,\n    std::vector<crypto::PolynomialOpening<Poly>>& openings) const {\n  TRACE_EVENT(\"ProofGeneration\", \"Plonk::Permutation::Prover::Open\");\n  if (grand_product_polys_.empty()) return;\n\n#define OPENING(poly, point) \\\n  base::Ref<const Poly>(&poly), point_set.point, poly.Evaluate(point_set.point)\n\n  // THE ORDER IS IMPORTANT!! DO NOT CHANGE!\n  // See\n  // https://github.com/kroma-network/halo2/blob/7d0a369/halo2_proofs/src/plonk/permutation/prover.rs#L279-L324.\n  for (const BlindedPolynomial<Poly, Evals>& grand_product_poly :\n       grand_product_polys_) {\n    const Poly& poly = grand_product_poly.poly();\n    // Zₚ,ᵢ(x)\n    openings.emplace_back(OPENING(poly, x));\n    // Zₚ,ᵢ(ω * x)\n    openings.emplace_back(OPENING(poly, x_next));\n  }\n\n  for (auto it = grand_product_polys_.rbegin() + 1;\n       it != grand_product_polys_.rend(); ++it) {\n    const Poly& poly = it->poly();\n    // Zₚ,ᵢ(ω^(last) * x)\n    openings.emplace_back(OPENING(poly, x_last));\n  }\n#undef OPENING\n}\n\n// static\ntemplate <typename Poly, typename Evals>\ntemplate <typename ExtendedEvals>\nvoid PermutationProver<Poly, Evals>::OpenPermutationProvingKey(\n    const PermutationProvingKey<Poly, Evals, ExtendedEvals>& proving_key,\n    const PermutationOpeningPointSet<F>& point_set,\n    std::vector<crypto::PolynomialOpening<Poly>>& openings) {\n  TRACE_EVENT(\"ProofGeneration\",\n              \"Plonk::Permutation::Prover::OpenPermutationProvingKey\");\n#define OPENING(poly, point) \\\n  base::Ref<const Poly>(&poly), point_set.point, poly.Evaluate(point_set.point)\n\n  for (const Poly& poly : proving_key.polys()) {\n    openings.emplace_back(OPENING(poly, x));\n  }\n#undef OPENING\n}\n\n// static\ntemplate <typename Poly, typename Evals>\nstd::function<typename Poly::Field(size_t, RowIndex)>\nPermutationProver<Poly, Evals>::CreateNumeratorCallback(\n    const std::vector<base::Ref<const Evals>>& unpermuted_columns,\n    const std::vector<base::Ref<const Evals>>& value_columns, const F& beta,\n    const F& gamma) {\n  // vᵢ(ωʲ) + β * δⁱ * ωʲ + γ\n  return [&unpermuted_columns, &value_columns, &beta, &gamma](\n             size_t column_index, RowIndex row_index) {\n    const Evals& unpermuted_values = *unpermuted_columns[column_index];\n    const Evals& values = *value_columns[column_index];\n    return values[row_index] + beta * unpermuted_values[row_index] + gamma;\n  };\n}\n\n// static\ntemplate <typename Poly, typename Evals>\nstd::function<typename Poly::Field(size_t, RowIndex)>\nPermutationProver<Poly, Evals>::CreateDenominatorCallback(\n    const std::vector<base::Ref<const Evals>>& permuted_columns,\n    const std::vector<base::Ref<const Evals>>& value_columns, const F& beta,\n    const F& gamma) {\n  // vᵢ(ωʲ) + β * sᵢ(ωʲ) + γ\n  return [&permuted_columns, &value_columns, &beta, &gamma](\n             size_t column_index, RowIndex row_index) {\n    const Evals& permuted_values = *permuted_columns[column_index];\n    const Evals& values = *value_columns[column_index];\n    return values[row_index] + beta * permuted_values[row_index] + gamma;\n  };\n}\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_PERMUTATION_PERMUTATION_PROVER_IMPL_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/permutation/permutation_proving_key.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_PERMUTATION_PERMUTATION_PROVING_KEY_H_\n#define TACHYON_ZK_PLONK_PERMUTATION_PERMUTATION_PROVING_KEY_H_\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/zk/plonk/keys/c_proving_key_impl_forward.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename Poly, typename Evals, typename ExtendedEvals>\nclass PermutationProvingKey {\n public:\n  PermutationProvingKey() = default;\n  PermutationProvingKey(const std::vector<Evals>& permutations,\n                        const std::vector<Poly>& polys,\n                        const std::vector<ExtendedEvals>& cosets)\n      : permutations_(permutations), polys_(polys), cosets_(cosets) {}\n  PermutationProvingKey(std::vector<Evals>&& permutations,\n                        std::vector<Poly>&& polys,\n                        std::vector<ExtendedEvals>&& cosets)\n      : permutations_(std::move(permutations)),\n        polys_(std::move(polys)),\n        cosets_(std::move(cosets)) {}\n\n  const std::vector<Evals>& permutations() const { return permutations_; }\n  const std::vector<Poly>& polys() const { return polys_; }\n  std::vector<Poly>& polys() { return polys_; }\n  const std::vector<ExtendedEvals>& cosets() const { return cosets_; }\n\n  size_t BytesLength() const { return base::EstimateSize(this); }\n\n  bool operator==(const PermutationProvingKey& other) const {\n    return permutations_ == other.permutations_ && polys_ == other.polys_ &&\n           cosets_ == other.cosets_;\n  }\n  bool operator!=(const PermutationProvingKey& other) const {\n    return !operator==(other);\n  }\n\n private:\n  template <typename PS>\n  friend class c::zk::plonk::ProvingKeyImpl;\n\n  std::vector<Evals> permutations_;\n  std::vector<Poly> polys_;\n  // NOTE(chokobole): Only PSE Halo2 has the member |cosets_|.\n  // See below:\n  // https://github.com/privacy-scaling-explorations/halo2/blob/bc857a7/halo2_backend/src/plonk/permutation.rs#L59-L63\n  // https://github.com/scroll-tech/halo2/blob/1070391/halo2_proofs/src/plonk/permutation.rs#L123-L126\n  std::vector<ExtendedEvals> cosets_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_PERMUTATION_PERMUTATION_PROVING_KEY_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/permutation/permutation_table_store.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_PERMUTATION_PERMUTATION_TABLE_STORE_H_\n#define TACHYON_ZK_PLONK_PERMUTATION_PERMUTATION_TABLE_STORE_H_\n\n#include <vector>\n\n#include \"absl/types/span.h\"\n#include \"gtest/gtest_prod.h\"\n\n#include \"tachyon/zk/plonk/base/ref_table.h\"\n#include \"tachyon/zk/plonk/permutation/permuted_table.h\"\n#include \"tachyon/zk/plonk/permutation/unpermuted_table.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename Evals>\nclass PermutationTableStore {\n public:\n  using F = typename Evals::Field;\n\n  PermutationTableStore(const std::vector<AnyColumnKey>& column_keys,\n                        const RefTable<Evals>& table,\n                        const PermutedTable<Evals>& permuted_table,\n                        const UnpermutedTable<Evals>& unpermuted_table,\n                        size_t chunk_size)\n      : column_keys_(column_keys),\n        table_(table),\n        permuted_table_(permuted_table),\n        unpermuted_table_(unpermuted_table),\n        chunk_size_(chunk_size) {}\n\n  size_t GetChunkSize(size_t chunk_idx) const {\n    size_t total_size = column_keys_.size();\n    size_t chunk_num = GetChunkNum();\n    CHECK_LT(chunk_idx, chunk_num);\n\n    if (chunk_idx < chunk_num - 1) {\n      return chunk_size_;\n    } else {\n      return total_size - (chunk_num - 1) * chunk_size_;\n    }\n  }\n\n  std::vector<base::Ref<const Evals>> GetValueColumns(size_t chunk_idx) const {\n    size_t chunk_offset = GetChunkOffset(chunk_idx);\n    size_t chunk_size = GetChunkSize(chunk_idx);\n    absl::Span<const AnyColumnKey> keys =\n        absl::MakeConstSpan(column_keys_.data() + chunk_offset, chunk_size);\n    return table_.GetColumns(keys);\n  }\n\n  std::vector<base::Ref<const Evals>> GetPermutedColumns(\n      size_t chunk_idx) const {\n    return GetColumns(permuted_table_, chunk_idx);\n  }\n\n  std::vector<base::Ref<const Evals>> GetUnpermutedColumns(\n      size_t chunk_idx) const {\n    return GetColumns(unpermuted_table_, chunk_idx);\n  }\n\n private:\n  FRIEND_TEST(PermutationTableStoreTest, GetColumns);\n\n  size_t GetChunkNum() const {\n    return (column_keys_.size() + chunk_size_ - 1) / chunk_size_;\n  }\n\n  size_t GetChunkOffset(size_t chunk_idx) const {\n    return chunk_idx * chunk_size_;\n  }\n\n  template <typename T>\n  auto GetColumns(const T& table, size_t chunk_idx) const {\n    size_t chunk_offset = GetChunkOffset(chunk_idx);\n    size_t chunk_size = GetChunkSize(chunk_idx);\n    return table.GetColumns(\n        base::Range<size_t>(chunk_offset, chunk_offset + chunk_size));\n  }\n\n  const std::vector<AnyColumnKey>& column_keys_;\n  const RefTable<Evals>& table_;\n  const PermutedTable<Evals>& permuted_table_;\n  const UnpermutedTable<Evals>& unpermuted_table_;\n  // The number of element in a chunk.\n  size_t chunk_size_ = 0;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_PERMUTATION_PERMUTATION_TABLE_STORE_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/permutation/permutation_table_store_unittest.cc",
    "content": "#include \"tachyon/zk/plonk/permutation/permutation_table_store.h\"\n\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/elliptic_curves/bn/bn254/fr.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluation_domain_factory.h\"\n\nnamespace tachyon::zk::plonk {\nnamespace {\n\nusing F = math::bn254::Fr;\n\nclass PermutationTableStoreTest : public math::FiniteFieldTest<F> {\n public:\n  static constexpr size_t kChunkSize = 4;\n  constexpr static RowIndex kRows = 32;\n  constexpr static size_t kMaxDegree = kRows - 1;\n\n  using Domain = math::UnivariateEvaluationDomain<F, kMaxDegree>;\n  using Evals = Domain::Evals;\n\n  void SetUp() override {\n    std::unique_ptr<Domain> domain = Domain::Create(kRows);\n    const Domain* domain_ptr = domain.get();\n\n    fixed_columns_ = base::CreateVector(\n        3, [domain_ptr]() { return domain_ptr->Random<Evals>(); });\n    advice_columns_ = base::CreateVector(\n        3, [domain_ptr]() { return domain_ptr->Random<Evals>(); });\n    instance_columns_ = base::CreateVector(\n        3, [domain_ptr]() { return domain_ptr->Random<Evals>(); });\n\n    table_ =\n        RefTable<Evals>(fixed_columns_, advice_columns_, instance_columns_);\n\n    column_keys_ = {\n        FixedColumnKey(0),  InstanceColumnKey(0), AdviceColumnKey(0),\n        AdviceColumnKey(1), FixedColumnKey(1),    FixedColumnKey(2),\n        AdviceColumnKey(2), InstanceColumnKey(1), InstanceColumnKey(2)};\n\n    unpermuted_table_ = UnpermutedTable<Evals>::Construct(column_keys_.size(),\n                                                          kRows, domain_ptr);\n    for (const Evals& column : unpermuted_table_.table()) {\n      permutations_.push_back(column);\n    }\n    permuted_table_ = PermutedTable<Evals>(&permutations_);\n  }\n\n protected:\n  std::vector<Evals> fixed_columns_;\n  std::vector<Evals> advice_columns_;\n  std::vector<Evals> instance_columns_;\n  RefTable<Evals> table_;\n\n  std::vector<AnyColumnKey> column_keys_;\n  std::vector<Evals> permutations_;\n  PermutedTable<Evals> permuted_table_;\n  UnpermutedTable<Evals> unpermuted_table_;\n};\n\n}  // namespace\n\nTEST_F(PermutationTableStoreTest, GetColumns) {\n  PermutationTableStore<Evals> permutation_table_store(\n      column_keys_, table_, permuted_table_, unpermuted_table_, kChunkSize);\n\n  // Prepare columns sorted in the order of |column_keys_|\n  std::vector<Evals> expected_value_columns = {\n      fixed_columns_[0],  instance_columns_[0], advice_columns_[0],\n      advice_columns_[1], fixed_columns_[1],    fixed_columns_[2],\n      advice_columns_[2], instance_columns_[1], instance_columns_[2],\n  };\n\n  for (size_t i = 0; i < permutation_table_store.GetChunkNum(); ++i) {\n    std::vector<base::Ref<const Evals>> value_columns =\n        permutation_table_store.GetValueColumns(i);\n    std::vector<base::Ref<const Evals>> permuted_columns =\n        permutation_table_store.GetPermutedColumns(i);\n    std::vector<base::Ref<const Evals>> unpermuted_columns =\n        permutation_table_store.GetUnpermutedColumns(i);\n\n    size_t start = permutation_table_store.GetChunkOffset(i);\n    size_t chunk_size = permutation_table_store.GetChunkSize(i);\n    for (size_t j = 0; j < chunk_size; ++j) {\n      EXPECT_EQ(*permuted_columns[j], *unpermuted_columns[j]);\n      EXPECT_EQ(*value_columns[j], expected_value_columns[start + j]);\n    }\n  }\n}\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/permutation/permutation_utils.h",
    "content": "#ifndef TACHYON_ZK_PLONK_PERMUTATION_PERMUTATION_UTILS_H_\n#define TACHYON_ZK_PLONK_PERMUTATION_PERMUTATION_UTILS_H_\n\n#include <stddef.h>\n\n#include \"tachyon/base/numerics/checked_math.h\"\n// TODO(chokobole): Remove this header. See comment in |GetDelta()| below.\n#include \"tachyon/math/elliptic_curves/bn/bn254/fr.h\"\n\nnamespace tachyon::zk::plonk {\n\nconstexpr size_t ComputePermutationChunkLength(size_t cs_degree) {\n  base::CheckedNumeric<size_t> checked_cs_degree(cs_degree);\n  return (checked_cs_degree - 2).ValueOrDie();\n}\n\n// Calculate δ = g^2ˢ with order T (i.e., T-th root of unity),\n// where T = F::Config::kTrace.\ntemplate <typename F>\nconstexpr F GetDelta() {\n  // NOTE(chokobole): The resulting value is different from the one in\n  // https://github.com/kroma-network/halo2curves/blob/c0ac193/src/bn256/fr.rs#L101-L110.\n  // This is an ugly way to produce a same result with Halo2Curves but we will\n  // remove once we don't have to match it against Halo2 any longer in the\n  // future.\n  if constexpr (std::is_same_v<F, math::bn254::Fr>) {\n    return F::FromMontgomery(math::BigInt<4>(\n        {UINT64_C(11100302345850292309), UINT64_C(5109383341788583484),\n         UINT64_C(6450182039226333095), UINT64_C(2498166472155664813)}));\n  } else {\n    F g = F::FromMontgomery(F::Config::kSubgroupGenerator);\n    F adicity = F(2).Pow(F::Config::kTwoAdicity);\n    return g.Pow(adicity.ToBigInt());\n  }\n}\n\nconstexpr size_t GetNumPermutationEvals(size_t num_circuits,\n                                        size_t num_grand_product_polys) {\n  if (num_circuits == 0) return 0;\n  if (num_grand_product_polys == 0) return 0;\n  return num_circuits * (2 * num_grand_product_polys + 1);\n}\n\nconstexpr size_t GetNumPermutationOpenings(size_t num_circuits,\n                                           size_t num_grand_product_polys,\n                                           size_t num_permutations) {\n  if (num_circuits == 0) return 0;\n  if (num_grand_product_polys == 0) return 0;\n  return num_circuits * (num_grand_product_polys * 3 - 1) + num_permutations;\n}\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_PERMUTATION_PERMUTATION_UTILS_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/permutation/permutation_utils_unittest.cc",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#include \"tachyon/zk/plonk/permutation/permutation_utils.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/elliptic_curves/bn/bn254/fq.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n\nnamespace tachyon::zk::plonk {\n\nnamespace {\n\ntemplate <typename PrimeField>\nclass PermutationUtilsTest : public math::FiniteFieldTest<PrimeField> {};\n\n}  // namespace\n\nusing PrimeFieldTypes = testing::Types<math::bn254::Fq, math::bn254::Fr>;\nTYPED_TEST_SUITE(PermutationUtilsTest, PrimeFieldTypes);\n\nTYPED_TEST(PermutationUtilsTest, GetDelta) {\n  using F = TypeParam;\n\n  F delta = GetDelta<F>();\n  EXPECT_NE(delta, F::One());\n  EXPECT_EQ(delta.Pow(F::Config::kTrace), F::One());\n}\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/permutation/permutation_verifier.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_PERMUTATION_PERMUTATION_VERIFIER_H_\n#define TACHYON_ZK_PLONK_PERMUTATION_PERMUTATION_VERIFIER_H_\n\n#include <vector>\n\n#include \"tachyon/base/containers/adapters.h\"\n#include \"tachyon/crypto/commitments/polynomial_openings.h\"\n#include \"tachyon/zk/plonk/base/l_values.h\"\n#include \"tachyon/zk/plonk/constraint_system/constraint_system.h\"\n#include \"tachyon/zk/plonk/permutation/permutation_opening_point_set.h\"\n#include \"tachyon/zk/plonk/permutation/permutation_utils.h\"\n#include \"tachyon/zk/plonk/permutation/permutation_verifier_data.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F, typename C>\nclass PermutationVerifier {\n public:\n  explicit PermutationVerifier(const PermutationVerifierData<F, C>& data)\n      : data_(data) {}\n\n  void Evaluate(const ConstraintSystem<F>& constraint_system, const F& x,\n                const LValues<F>& l_values, std::vector<F>& evals) const {\n    if (data_.grand_product_evals.empty()) return;\n\n    // l_first(x) * (1 - Zₚ,₀(x)) = 0\n    const F& z_first = data_.grand_product_evals.front();\n    evals.push_back(l_values.first * (F::One() - z_first));\n    // l_last(x) * (Zₚ,ₗₐₛₜ(x)² - Zₚ,ₗₐₛₜ(x)) = 0\n    const F& z_last = data_.grand_product_evals.back();\n    evals.push_back(l_values.last * (z_last.Square() - z_last));\n    // l_first(x) * (Zₚ,ᵢ(x) - Zₚ,ᵢ₋₁(ω^(last) * x)) = 0\n    for (size_t i = 1; i < data_.grand_product_evals.size(); ++i) {\n      const F& z_cur = data_.grand_product_evals[i];\n      const F& z_prev_last = *data_.grand_product_last_evals[i - 1];\n      evals.push_back(l_values.first * (z_cur - z_prev_last));\n    }\n    // (1 - (l_last(x) + l_blind(x))) * (\n    //   Zₚ,ᵢ(ω * x) Π (vᵢ(x) + β * sᵢ(x) + γ)\n    // - Zₚ,ᵢ(x) Π (vᵢ(x) + δⁱ *β * x + γ)\n    // ) = 0\n    const PermutationArgument& argument = constraint_system.permutation();\n    size_t chunk_idx = 0;\n    const F& delta = GetDelta<F>();\n    F active_rows = F::One() - (l_values.last + l_values.blind);\n    size_t chunk_len = constraint_system.ComputePermutationChunkLen();\n    for (absl::Span<const AnyColumnKey> columns :\n         base::Chunked(argument.columns(), chunk_len)) {\n      absl::Span<const F> substitution_evals_chunk =\n          data_.substitution_evals.subspan(chunk_idx * chunk_len,\n                                           columns.size());\n      F left = data_.grand_product_next_evals[chunk_idx];\n      for (size_t i = 0; i < columns.size(); ++i) {\n        const AnyColumnKey& column = columns[i];\n        const F& eval = GetEval(constraint_system, column);\n        left *= eval + data_.beta * substitution_evals_chunk[i] + data_.gamma;\n      }\n\n      F right = data_.grand_product_evals[chunk_idx];\n      F current_delta = data_.beta * x * delta.Pow(chunk_idx * chunk_len);\n      for (size_t i = 0; i < columns.size(); ++i) {\n        const AnyColumnKey& column = columns[i];\n        const F& eval = GetEval(constraint_system, column);\n        right *= eval + current_delta + data_.gamma;\n        current_delta *= delta;\n      }\n      evals.push_back(active_rows * (left - right));\n      ++chunk_idx;\n    }\n  }\n\n  template <typename Poly>\n  void Open(const PermutationOpeningPointSet<F>& point_set,\n            std::vector<crypto::PolynomialOpening<Poly, C>>& openings) const {\n    if (data_.grand_product_evals.empty()) return;\n\n#define OPENING(commitment, point, eval) \\\n  base::Ref<const C>(&data_.commitment), point_set.point, data_.eval\n\n    for (size_t i = 0; i < data_.grand_product_commitments.size(); ++i) {\n      openings.emplace_back(\n          OPENING(grand_product_commitments[i], x, grand_product_evals[i]));\n      openings.emplace_back(OPENING(grand_product_commitments[i], x_next,\n                                    grand_product_next_evals[i]));\n    }\n    if (data_.grand_product_commitments.size() > 1) {\n      for (size_t i = data_.grand_product_commitments.size() - 2; i != SIZE_MAX;\n           --i) {\n        openings.emplace_back(OPENING(grand_product_commitments[i], x_last,\n                                      grand_product_last_evals[i].value()));\n      }\n    }\n\n#undef OPENING\n  }\n\n  template <typename Poly>\n  void OpenPermutationProvingKey(\n      const F& x,\n      std::vector<crypto::PolynomialOpening<Poly, C>>& openings) const {\n#define OPENING(commitment, point, eval) \\\n  base::Ref<const C>(&data_.commitment), point, data_.eval\n\n    for (size_t i = 0; i < data_.substitution_commitments.size(); ++i) {\n      openings.emplace_back(\n          OPENING(substitution_commitments[i], x, substitution_evals[i]));\n    }\n#undef OPENING\n  }\n\n private:\n  const F& GetEval(const ConstraintSystem<F>& constraint_system,\n                   const AnyColumnKey& column) const {\n    const absl::Span<const F>* evals = nullptr;\n    switch (column.type()) {\n      case ColumnType::kAdvice: {\n        evals = &data_.advice_evals;\n        break;\n      }\n      case ColumnType::kFixed: {\n        evals = &data_.fixed_evals;\n        break;\n      }\n      case ColumnType::kInstance: {\n        evals = &data_.instance_evals;\n        break;\n      }\n      case ColumnType::kAny: {\n        NOTREACHED();\n      }\n    }\n    return (\n        *evals)[constraint_system.GetAnyQueryIndex(column, Rotation::Cur())];\n  }\n\n  const PermutationVerifierData<F, C>& data_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_PERMUTATION_PERMUTATION_VERIFIER_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/permutation/permutation_verifier_data.h",
    "content": "#ifndef TACHYON_ZK_PLONK_PERMUTATION_PERMUTATION_VERIFIER_DATA_H_\n#define TACHYON_ZK_PLONK_PERMUTATION_PERMUTATION_VERIFIER_DATA_H_\n\n#include <optional>\n\n#include \"tachyon/zk/plonk/base/multi_phase_evaluations.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F, typename C>\nstruct PermutationVerifierData : public MultiPhaseEvaluations<F> {\n  PermutationVerifierData(\n      absl::Span<const F> fixed_evals, absl::Span<const F> advice_evals,\n      absl::Span<const F> instance_evals, absl::Span<const F> challenges,\n      absl::Span<const C> grand_product_commitments,\n      absl::Span<const F> grand_product_evals,\n      absl::Span<const F> grand_product_next_evals,\n      absl::Span<const std::optional<F>> grand_product_last_evals,\n      absl::Span<const C> substitution_commitments,\n      absl::Span<const F> substitution_evals, const F& beta, const F& gamma)\n      : MultiPhaseEvaluations<F>(fixed_evals, advice_evals, instance_evals,\n                                 challenges),\n        grand_product_commitments(grand_product_commitments),\n        grand_product_evals(grand_product_evals),\n        grand_product_next_evals(grand_product_next_evals),\n        grand_product_last_evals(grand_product_last_evals),\n        substitution_commitments(substitution_commitments),\n        substitution_evals(substitution_evals),\n        beta(beta),\n        gamma(gamma) {}\n\n  // [Zₚ,ᵢ(τ)]₁\n  absl::Span<const C> grand_product_commitments;\n  // Zₚ,ᵢ(x)\n  absl::Span<const F> grand_product_evals;\n  // Zₚ,ᵢ(ω * x)\n  absl::Span<const F> grand_product_next_evals;\n  // Zₚ,ᵢ(ω^(last) * x)\n  absl::Span<const std::optional<F>> grand_product_last_evals;\n  // [sᵢ(τ)]₁\n  absl::Span<const C> substitution_commitments;\n  // sᵢ(x)\n  absl::Span<const F> substitution_evals;\n  const F& beta;\n  const F& gamma;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_PERMUTATION_PERMUTATION_VERIFIER_DATA_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/permutation/permutation_verifying_key.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_PERMUTATION_PERMUTATION_VERIFYING_KEY_H_\n#define TACHYON_ZK_PLONK_PERMUTATION_PERMUTATION_VERIFYING_KEY_H_\n\n#include <utility>\n#include <vector>\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename Commitment>\nclass PermutationVerifyingKey {\n public:\n  using Commitments = std::vector<Commitment>;\n\n  PermutationVerifyingKey() = default;\n\n  explicit PermutationVerifyingKey(const Commitments& commitments)\n      : commitments_(commitments) {}\n  explicit PermutationVerifyingKey(Commitments&& commitments)\n      : commitments_(std::move(commitments)) {}\n\n  const Commitments& commitments() const { return commitments_; }\n\n  size_t BytesLength() const { return base::EstimateSize(this); }\n\n  bool operator==(const PermutationVerifyingKey& other) const {\n    return commitments_ == other.commitments_;\n  }\n  bool operator!=(const PermutationVerifyingKey& other) const {\n    return commitments_ != other.commitments_;\n  }\n\n private:\n  Commitments commitments_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_PERMUTATION_PERMUTATION_VERIFYING_KEY_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/permutation/permuted_table.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_PERMUTATION_PERMUTED_TABLE_H_\n#define TACHYON_ZK_PLONK_PERMUTATION_PERMUTED_TABLE_H_\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/range.h\"\n#include \"tachyon/base/ref.h\"\n#include \"tachyon/zk/plonk/permutation/label.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename Evals>\nclass PermutedTable {\n public:\n  using F = typename Evals::Field;\n  using Table = std::vector<Evals>;\n\n  PermutedTable() = default;\n  explicit PermutedTable(const Table* table) : table_(table) {}\n\n  const F& operator[](const Label& label) const {\n    return (*table_)[label.col][label.row];\n  }\n\n  base::Ref<const Evals> GetColumn(size_t i) const {\n    return base::Ref<const Evals>(&(*table_)[i]);\n  }\n\n  std::vector<base::Ref<const Evals>> GetColumns(\n      base::Range<size_t> range) const {\n    CHECK_EQ(range.Intersect(base::Range<size_t>::Until(table_->size())),\n             range);\n\n    std::vector<base::Ref<const Evals>> ret;\n    ret.reserve(range.GetSize());\n    for (size_t i : range) {\n      ret.push_back(GetColumn(i));\n    }\n    return ret;\n  }\n\n private:\n  // not owned\n  const Table* table_ = nullptr;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_PERMUTATION_PERMUTED_TABLE_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/permutation/unpermuted_table.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_PERMUTATION_UNPERMUTED_TABLE_H_\n#define TACHYON_ZK_PLONK_PERMUTATION_UNPERMUTED_TABLE_H_\n\n#include <utility>\n#include <vector>\n\n#include \"gtest/gtest_prod.h\"\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/openmp_util.h\"\n#include \"tachyon/base/range.h\"\n#include \"tachyon/base/ref.h\"\n#include \"tachyon/zk/plonk/permutation/label.h\"\n#include \"tachyon/zk/plonk/permutation/permutation_utils.h\"\n\nnamespace tachyon::zk::plonk {\n\n// The |UnpermutedTable| contains elements that are the product-of-powers\n// of δ and ω (called \"label\"). And each permutation polynomial (in evaluation\n// form) is constructed by assigning elements in the |UnpermutedTable|.\n//\n// Let modulus = 2ˢ * T + 1, then\n// |UnpermutedTable|\n// = [[δⁱω⁰, δⁱω¹, δⁱω², ..., δⁱωⁿ⁻¹] for i in range(0..T-1)]\ntemplate <typename Evals>\nclass UnpermutedTable {\n public:\n  using F = typename Evals::Field;\n  using Table = std::vector<Evals>;\n\n  UnpermutedTable() = default;\n\n  const Table& table() const& { return table_; }\n\n  const F& operator[](const Label& label) const {\n    return table_[label.col][label.row];\n  }\n\n  base::Ref<const Evals> GetColumn(size_t i) const {\n    return base::Ref<const Evals>(&table_[i]);\n  }\n\n  std::vector<base::Ref<const Evals>> GetColumns(\n      base::Range<size_t> range) const {\n    CHECK_EQ(range.Intersect(base::Range<size_t>::Until(table_.size())), range);\n    return base::Map(range, [this](size_t i) { return GetColumn(i); });\n  }\n\n  template <typename Domain>\n  static UnpermutedTable Construct(size_t cols, RowIndex rows,\n                                   const Domain* domain) {\n    // The ω is gᵀ with order 2ˢ where modulus = 2ˢ * T + 1.\n    std::vector<F> omega_powers =\n        Domain::GetRootsOfUnity(rows, domain->group_gen());\n\n    // The δ is g^2ˢ with order T where modulus = 2ˢ * T + 1.\n    F delta = GetDelta<F>();\n\n    Table unpermuted_table;\n    unpermuted_table.reserve(cols);\n    // Assign [δ⁰ω⁰, δ⁰ω¹, δ⁰ω², ..., δ⁰ωⁿ⁻¹] to the first col.\n    unpermuted_table.push_back(Evals(std::move(omega_powers)));\n\n    // Assign [δⁱω⁰, δⁱω¹, δⁱω², ..., δⁱωⁿ⁻¹] to each col.\n    for (size_t i = 1; i < cols; ++i) {\n      std::vector<F> col(rows);\n      OMP_PARALLEL_FOR(RowIndex j = 0; j < rows; ++j) {\n        col[j] = unpermuted_table[i - 1][j] * delta;\n      }\n      unpermuted_table.push_back(Evals(std::move(col)));\n    }\n    return UnpermutedTable(std::move(unpermuted_table));\n  }\n\n private:\n  explicit UnpermutedTable(Table table) : table_(std::move(table)) {}\n\n  Table table_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_PERMUTATION_UNPERMUTED_TABLE_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/permutation/unpermuted_table_unittest.cc",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#include \"tachyon/zk/plonk/permutation/unpermuted_table.h\"\n\n#include <memory>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/elliptic_curves/bn/bn254/fr.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluation_domain_factory.h\"\n#include \"tachyon/zk/base/row_types.h\"\n\nnamespace tachyon::zk::plonk {\n\nnamespace {\n\nusing F = math::bn254::Fr;\n\nclass UnpermutedTableTest : public math::FiniteFieldTest<F> {\n public:\n  constexpr static RowIndex kRows = 32;\n  constexpr static size_t kCols = 4;\n\n  using Domain = math::UnivariateEvaluationDomain<F, kRows - 1>;\n  using Evals = Domain::Evals;\n\n  void SetUp() override {\n    domain_ = Domain::Create(kRows);\n\n    unpermuted_table_ =\n        UnpermutedTable<Evals>::Construct(kCols, kRows, domain_.get());\n  }\n\n protected:\n  std::unique_ptr<Domain> domain_;\n  UnpermutedTable<Evals> unpermuted_table_;\n};\n\n}  // namespace\n\nTEST_F(UnpermutedTableTest, Construct) {\n  const F& omega = domain_->group_gen();\n  std::vector<F> omega_powers = Domain::GetRootsOfUnity(kRows, omega);\n\n  const F delta = GetDelta<F>();\n  for (size_t i = 1; i < kCols; ++i) {\n    for (RowIndex j = 0; j < kRows; ++j) {\n      omega_powers[j] *= delta;\n      EXPECT_EQ(omega_powers[j], unpermuted_table_[Label(i, j)]);\n    }\n  }\n}\n\nTEST_F(UnpermutedTableTest, GetColumns) {\n  const F& omega = domain_->group_gen();\n  const F delta = GetDelta<F>();\n  std::vector<F> omega_powers = Domain::GetRootsOfUnity(kRows, omega);\n  for (F& omega_power : omega_powers) {\n    omega_power *= delta;\n  }\n\n  base::Ref<const Evals> column = unpermuted_table_.GetColumn(1);\n  for (RowIndex i = 0; i < kRows; ++i) {\n    EXPECT_EQ(omega_powers[i], (*column)[i]);\n  }\n\n  std::vector<base::Ref<const Evals>> columns =\n      unpermuted_table_.GetColumns(base::Range<size_t>(2, 4));\n  for (size_t i = 0; i < 2; ++i) {\n    for (RowIndex j = 0; j < kRows; ++j) {\n      omega_powers[j] *= delta;\n      EXPECT_EQ(omega_powers[j], (*columns[i])[j]);\n    }\n  }\n}\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/vanishing/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"calculation\",\n    srcs = [\"calculation.cc\"],\n    hdrs = [\"calculation.h\"],\n    deps = [\n        \":value_source\",\n        \"//tachyon/base/strings:string_util\",\n        \"//tachyon/zk/plonk/vanishing:evaluation_input\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"custom_gate_evaluator\",\n    hdrs = [\"custom_gate_evaluator.h\"],\n    deps = [\n        \"//tachyon/base:profiler\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/zk/plonk/constraint_system:gate\",\n        \"//tachyon/zk/plonk/vanishing:circuit_polynomial_builder_forward\",\n        \"//tachyon/zk/plonk/vanishing:graph_evaluator\",\n        \"//tachyon/zk/plonk/vanishing:vanishing_utils\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"circuit_polynomial_builder\",\n    hdrs = [\"circuit_polynomial_builder.h\"],\n    deps = [\n        \":evaluation_input\",\n        \":graph_evaluator\",\n        \":vanishing_utils\",\n        \"//tachyon/base:parallelize\",\n        \"//tachyon/base/containers:adapters\",\n        \"//tachyon/zk/base:rotation\",\n        \"//tachyon/zk/lookup:prover\",\n        \"//tachyon/zk/plonk/base:column_key\",\n        \"//tachyon/zk/plonk/keys:proving_key_forward\",\n        \"//tachyon/zk/plonk/permutation:permutation_prover\",\n        \"//tachyon/zk/shuffle:prover\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"circuit_polynomial_builder_forward\",\n    hdrs = [\"circuit_polynomial_builder_forward.h\"],\n)\n\ntachyon_cc_library(\n    name = \"evaluation_input\",\n    hdrs = [\"evaluation_input.h\"],\n    deps = [\"//tachyon/zk/plonk/base:multi_phase_ref_table\"],\n)\n\ntachyon_cc_library(\n    name = \"graph_evaluator\",\n    hdrs = [\"graph_evaluator.h\"],\n    deps = [\n        \":calculation\",\n        \"//tachyon/zk/expressions:constant_expression\",\n        \"//tachyon/zk/expressions:evaluator\",\n        \"//tachyon/zk/expressions:negated_expression\",\n        \"//tachyon/zk/expressions:product_expression\",\n        \"//tachyon/zk/expressions:scaled_expression\",\n        \"//tachyon/zk/expressions:sum_expression\",\n        \"//tachyon/zk/plonk/expressions:advice_expression\",\n        \"//tachyon/zk/plonk/expressions:challenge_expression\",\n        \"//tachyon/zk/plonk/expressions:fixed_expression\",\n        \"//tachyon/zk/plonk/expressions:instance_expression\",\n        \"//tachyon/zk/plonk/expressions:selector_expression\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"value_source\",\n    srcs = [\"value_source.cc\"],\n    hdrs = [\"value_source.h\"],\n    deps = [\n        \"//tachyon:export\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/zk/plonk/vanishing:evaluation_input\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"vanishing_argument\",\n    hdrs = [\"vanishing_argument.h\"],\n    deps = [\n        \":circuit_polynomial_builder\",\n        \":custom_gate_evaluator\",\n        \"//tachyon/zk/lookup:evaluator\",\n        \"//tachyon/zk/plonk/constraint_system\",\n        \"//tachyon/zk/plonk/permutation:permutation_evaluator\",\n        \"//tachyon/zk/shuffle:evaluator\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"vanishing_prover\",\n    hdrs = [\n        \"vanishing_prover.h\",\n        \"vanishing_prover_impl.h\",\n    ],\n    deps = [\n        \":vanishing_argument\",\n        \":vanishing_utils\",\n        \"//tachyon/base:parallelize\",\n        \"//tachyon/base:profiler\",\n        \"//tachyon/crypto/commitments:polynomial_openings\",\n        \"//tachyon/math/base:parallelize_threshold\",\n        \"//tachyon/zk/lookup:prover\",\n        \"//tachyon/zk/plonk/base:multi_phase_ref_table\",\n        \"//tachyon/zk/plonk/keys:proving_key\",\n        \"//tachyon/zk/plonk/permutation:permutation_prover\",\n        \"//tachyon/zk/shuffle:prover\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"vanishing_utils\",\n    hdrs = [\"vanishing_utils.h\"],\n    deps = [\n        \"//tachyon/base:parallelize\",\n        \"//tachyon/base/types:always_false\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:fr\",\n        \"//tachyon/zk/base:blinded_polynomial\",\n        \"//tachyon/zk/base/entities:prover_base\",\n        \"//tachyon/zk/plonk/constraint_system:gate\",\n        \"//tachyon/zk/plonk/halo2:config\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"vanishing_verifier\",\n    hdrs = [\"vanishing_verifier.h\"],\n    deps = [\n        \":vanishing_utils\",\n        \":vanishing_verifier_data\",\n        \"//tachyon/crypto/commitments:polynomial_openings\",\n        \"//tachyon/zk/plonk/expressions:verifying_evaluator\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"vanishing_verifier_data\",\n    hdrs = [\"vanishing_verifier_data.h\"],\n    deps = [\"//tachyon/zk/plonk/base:multi_phase_evaluations\"],\n)\n\ntachyon_cc_unittest(\n    name = \"vanishing_unittests\",\n    srcs = [\n        \"graph_evaluator_unittest.cc\",\n        \"value_source_unittest.cc\",\n        \"vanishing_utils_unittest.cc\",\n    ],\n    deps = [\n        \":graph_evaluator\",\n        \":vanishing_utils\",\n        \"//tachyon/base:random\",\n        \"//tachyon/math/elliptic_curves/bn/bn254:g1\",\n        \"//tachyon/math/finite_fields/test:finite_field_test\",\n        \"//tachyon/math/polynomials/univariate:univariate_evaluation_domain_factory\",\n        \"//tachyon/zk/plonk/expressions:expression_factory\",\n        \"//tachyon/zk/plonk/expressions/evaluator/test:evaluator_test\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/zk/plonk/vanishing/calculation.cc",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#include \"tachyon/zk/plonk/vanishing/calculation.h\"\n\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/base/strings/string_util.h\"\n\nnamespace tachyon::zk::plonk {\n\nstd::string Calculation::ToString() const {\n  switch (type_) {\n    case Type::kAdd:\n      return absl::Substitute(\"Add($0, $1)\", pair().left.ToString(),\n                              pair().right.ToString());\n    case Type::kSub:\n      return absl::Substitute(\"Sub($0, $1)\", pair().left.ToString(),\n                              pair().right.ToString());\n    case Type::kMul:\n      return absl::Substitute(\"Mul($0, $1)\", pair().left.ToString(),\n                              pair().right.ToString());\n    case Type::kSquare:\n      return absl::Substitute(\"Square($0)\", value().ToString());\n    case Type::kDouble:\n      return absl::Substitute(\"Double($0)\", value().ToString());\n    case Type::kNegate:\n      return absl::Substitute(\"Negate($0)\", value().ToString());\n    case Type::kStore:\n      return absl::Substitute(\"Store($0)\", value().ToString());\n    case Type::kHorner:\n      return absl::Substitute(\"Horner($0, $1, $2)\", horner().init.ToString(),\n                              horner().factor.ToString(),\n                              base::ContainerToString(horner().parts));\n  }\n  NOTREACHED();\n  return \"\";\n}\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/vanishing/calculation.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_VANISHING_CALCULATION_H_\n#define TACHYON_ZK_PLONK_VANISHING_CALCULATION_H_\n\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/export.h\"\n#include \"tachyon/zk/plonk/vanishing/evaluation_input.h\"\n#include \"tachyon/zk/plonk/vanishing/value_source.h\"\n\nnamespace tachyon::zk::plonk {\n\nclass TACHYON_EXPORT Calculation {\n public:\n  enum class Type {\n    kAdd,\n    kSub,\n    kMul,\n    kSquare,\n    kDouble,\n    kNegate,\n    kHorner,\n    kStore,\n  };\n\n  Calculation() : Calculation(Type::kAdd) {}\n\n  static Calculation Add(const ValueSource& left, const ValueSource& right) {\n    return Calculation(Type::kAdd, left, right);\n  }\n  static Calculation Sub(const ValueSource& left, const ValueSource& right) {\n    return Calculation(Type::kSub, left, right);\n  }\n  static Calculation Mul(const ValueSource& left, const ValueSource& right) {\n    return Calculation(Type::kMul, left, right);\n  }\n  static Calculation Square(const ValueSource& value) {\n    return Calculation(Type::kSquare, value);\n  }\n  static Calculation Double(const ValueSource& value) {\n    return Calculation(Type::kDouble, value);\n  }\n  static Calculation Negate(const ValueSource& value) {\n    return Calculation(Type::kNegate, value);\n  }\n  static Calculation Horner(const ValueSource& init,\n                            std::vector<ValueSource> parts,\n                            const ValueSource& factor) {\n    return Calculation(Type::kHorner, init, std::move(parts), factor);\n  }\n  static Calculation Store(const ValueSource& value) {\n    return Calculation(Type::kStore, value);\n  }\n\n  bool operator==(const Calculation& other) const {\n    return type_ == other.type_ && pair_ == other.pair_ &&\n           value_ == other.value_ && horner_ == other.horner_;\n  }\n  bool operator!=(const Calculation& other) const { return !operator==(other); }\n\n  template <typename Evals, typename F>\n  F Evaluate(const EvaluationInput<Evals>& data,\n             const std::vector<F>& constants, const F& previous_value) const {\n    switch (type_) {\n      case Type::kAdd:\n        return pair().left.Get(data, constants, previous_value) +\n               pair().right.Get(data, constants, previous_value);\n      case Type::kSub:\n        return pair().left.Get(data, constants, previous_value) -\n               pair().right.Get(data, constants, previous_value);\n      case Type::kMul:\n        return pair().left.Get(data, constants, previous_value) *\n               pair().right.Get(data, constants, previous_value);\n      case Type::kSquare:\n        return value().Get(data, constants, previous_value).Square();\n      case Type::kDouble:\n        return value().Get(data, constants, previous_value).Double();\n      case Type::kNegate:\n        return -value().Get(data, constants, previous_value);\n      case Type::kStore:\n        return value().Get(data, constants, previous_value);\n      case Type::kHorner: {\n        const HornerData& honer = horner();\n        F factor = honer.factor.Get(data, constants, previous_value);\n        F value = honer.init.Get(data, constants, previous_value);\n        for (const ValueSource& part : honer.parts) {\n          value *= factor;\n          value += part.Get(data, constants, previous_value);\n        }\n        return value;\n      }\n    }\n    NOTREACHED();\n    return F();\n  }\n\n  std::string ToString() const;\n\n private:\n  struct Pair {\n    ValueSource left;\n    ValueSource right;\n\n    Pair() = default;\n    Pair(const ValueSource& left, const ValueSource& right)\n        : left(left), right(right) {}\n\n    bool operator==(const Pair& other) const {\n      return left == other.left && right == other.right;\n    }\n    bool operator!=(const Pair& other) const { return !operator==(other); }\n  };\n\n  struct HornerData {\n    ValueSource init;\n    std::vector<ValueSource> parts;\n    ValueSource factor;\n\n    HornerData() = default;\n    HornerData(const ValueSource& init, const std::vector<ValueSource>& parts,\n               const ValueSource& factor)\n        : init(init), parts(parts), factor(factor) {}\n\n    bool operator==(const HornerData& other) const {\n      return init == other.init && parts == other.parts &&\n             factor == other.factor;\n    }\n    bool operator!=(const HornerData& other) const {\n      return !operator==(other);\n    }\n  };\n\n  explicit Calculation(Type type) : type_(type) {}\n  Calculation(Type type, const ValueSource& value)\n      : type_(type), value_(value) {}\n  Calculation(Type type, const ValueSource& left, const ValueSource& right)\n      : type_(type), pair_(Pair(left, right)) {}\n  Calculation(Type type, const ValueSource& init,\n              const std::vector<ValueSource>& parts, const ValueSource& factor)\n      : type_(type), horner_(HornerData(init, parts, factor)) {}\n\n  const ValueSource& value() const {\n    CHECK(value_.has_value());\n    return value_.value();\n  }\n  const Pair& pair() const {\n    CHECK(pair_.has_value());\n    return pair_.value();\n  }\n  const HornerData& horner() const {\n    CHECK(horner_.has_value());\n    return horner_.value();\n  }\n\n  Type type_;\n  std::optional<ValueSource> value_;\n  std::optional<Pair> pair_;\n  std::optional<HornerData> horner_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_VANISHING_CALCULATION_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/vanishing/circuit_polynomial_builder.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_VANISHING_CIRCUIT_POLYNOMIAL_BUILDER_H_\n#define TACHYON_ZK_PLONK_VANISHING_CIRCUIT_POLYNOMIAL_BUILDER_H_\n\n#include <memory>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"absl/types/span.h\"\n\n#include \"tachyon/base/containers/adapters.h\"\n#include \"tachyon/base/parallelize.h\"\n#include \"tachyon/zk/base/rotation.h\"\n#include \"tachyon/zk/lookup/prover.h\"\n#include \"tachyon/zk/plonk/base/column_key.h\"\n#include \"tachyon/zk/plonk/keys/proving_key_forward.h\"\n#include \"tachyon/zk/plonk/permutation/permutation_prover.h\"\n#include \"tachyon/zk/plonk/vanishing/evaluation_input.h\"\n#include \"tachyon/zk/plonk/vanishing/graph_evaluator.h\"\n#include \"tachyon/zk/plonk/vanishing/vanishing_utils.h\"\n#include \"tachyon/zk/shuffle/prover.h\"\n\nnamespace tachyon::zk {\nnamespace lookup::halo2 {\n\ntemplate <typename EvalsOrExtendedEvals>\nclass Evaluator;\n\n}  // namespace lookup::halo2\n\nnamespace lookup::log_derivative_halo2 {\n\ntemplate <typename EvalsOrExtendedEvals>\nclass Evaluator;\n\n}  // namespace lookup::log_derivative_halo2\n\nnamespace shuffle {\n\ntemplate <typename EvalsOrExtendedEvals>\nclass Evaluator;\n\n}  // namespace shuffle\n\nnamespace plonk {\n\ntemplate <typename EvalsOrExtendedEvals>\nclass CustomGateEvaluator;\n\ntemplate <typename EvalsOrExtendedEvals>\nclass PermutationEvaluator;\n\n// It generates \"CircuitPolynomial\" formed below:\n// - gate₀(X) + y * gate₁(X) + ... + yⁱ * gateᵢ(X) + ...\n// You can find more detailed theory in \"Halo2 book\"\n// https://zcash.github.io/halo2/design/proving-system/vanishing.html\ntemplate <typename PS>\nclass CircuitPolynomialBuilder {\n public:\n  constexpr static halo2::Vendor kVendor = PS::kVendor;\n  constexpr static lookup::Type kLookupType = PS::kLookupType;\n\n  using PCS = typename PS::PCS;\n  using F = typename PCS::Field;\n  using C = typename PCS::Commitment;\n  using Poly = typename PCS::Poly;\n  using Evals = typename PCS::Evals;\n  using Domain = typename PCS::Domain;\n  using ExtendedDomain = typename PCS::ExtendedDomain;\n  using ExtendedEvals = typename PCS::ExtendedEvals;\n  using LookupProver = lookup::Prover<kLookupType, Poly, Evals>;\n\n  using EvalsOrExtendedEvals =\n      std::conditional_t<kVendor == halo2::Vendor::kPSE, ExtendedEvals, Evals>;\n\n  CircuitPolynomialBuilder(\n      const F& omega, const F& extended_omega, const F& theta, const F& beta,\n      const F& gamma, const F& y, const F& zeta,\n      const ProvingKey<PS>& proving_key,\n      const std::vector<PermutationProver<Poly, Evals>>& permutation_provers,\n      const std::vector<LookupProver>& lookup_provers,\n      const std::vector<shuffle::Prover<Poly, Evals>>& shuffle_provers,\n      const std::vector<MultiPhaseRefTable<Poly>>& poly_tables)\n      : omega_(omega),\n        extended_omega_(extended_omega),\n        theta_(theta),\n        beta_(beta),\n        gamma_(gamma),\n        y_(y),\n        zeta_(zeta),\n        proving_key_(proving_key),\n        permutation_provers_(permutation_provers),\n        lookup_provers_(lookup_provers),\n        shuffle_provers_(shuffle_provers),\n        poly_tables_(poly_tables) {}\n\n  static CircuitPolynomialBuilder Create(\n      const Domain* domain, const ExtendedDomain* extended_domain, size_t n,\n      RowOffset last_row, size_t cs_degree,\n      const std::vector<MultiPhaseRefTable<Poly>>& poly_tables, const F& theta,\n      const F& beta, const F& gamma, const F& y, const F& zeta,\n      const ProvingKey<PS>& proving_key,\n      const std::vector<PermutationProver<Poly, Evals>>& permutation_provers,\n      const std::vector<LookupProver>& lookup_provers,\n      const std::vector<shuffle::Prover<Poly, Evals>>& shuffle_provers) {\n    CircuitPolynomialBuilder builder(\n        domain->group_gen(), extended_domain->group_gen(), theta, beta, gamma,\n        y, zeta, proving_key, permutation_provers, lookup_provers,\n        shuffle_provers, poly_tables);\n    builder.domain_ = domain;\n    builder.extended_domain_ = extended_domain;\n\n    builder.n_ = static_cast<int32_t>(n);\n    builder.num_parts_ = extended_domain->size() >> domain->log_size_of_group();\n    builder.chunk_len_ = cs_degree - 2;\n\n    builder.delta_ = GetDelta<F>();\n\n    builder.last_rotation_ = Rotation(last_row);\n    builder.delta_start_ = beta * zeta;\n    return builder;\n  }\n\n  // Returns an evaluation-formed polynomial as below.\n  // - gate₀(X) + y * gate₁(X) + ... + yⁱ * gateᵢ(X) + ...\n  ExtendedEvals BuildExtendedCircuitColumn(\n      CustomGateEvaluator<Evals>& custom_gate_evaluator,\n      PermutationEvaluator<Evals>& permutation_evaluator,\n      lookup::Evaluator<kLookupType, Evals>& lookup_evaluator,\n      shuffle::Evaluator<EvalsOrExtendedEvals>& shuffle_evaluator) {\n    if constexpr (kVendor == halo2::Vendor::kPSE) {\n      return BuildExtendedCircuitColumnPSE(custom_gate_evaluator,\n                                           permutation_evaluator,\n                                           lookup_evaluator, shuffle_evaluator);\n    } else {\n      return BuildExtendedCircuitColumnScroll(\n          custom_gate_evaluator, permutation_evaluator, lookup_evaluator,\n          shuffle_evaluator);\n    }\n  }\n\n private:\n  friend class CustomGateEvaluator<EvalsOrExtendedEvals>;\n  friend class PermutationEvaluator<EvalsOrExtendedEvals>;\n  friend class lookup::halo2::Evaluator<EvalsOrExtendedEvals>;\n  friend class lookup::log_derivative_halo2::Evaluator<EvalsOrExtendedEvals>;\n  friend class shuffle::Evaluator<EvalsOrExtendedEvals>;\n\n  EvaluationInput<EvalsOrExtendedEvals> ExtractEvaluationInput(\n      std::vector<F>&& intermediates, std::vector<int32_t>&& rotations) {\n    return EvaluationInput<EvalsOrExtendedEvals>(std::move(intermediates),\n                                                 std::move(rotations), table_,\n                                                 theta_, beta_, gamma_, y_, n_);\n  }\n\n  void UpdateLPolyCosets() {\n    l_first_ = coset_domain_->FFT(proving_key_.l_first());\n    l_last_ = coset_domain_->FFT(proving_key_.l_last());\n    l_active_row_ = coset_domain_->FFT(proving_key_.l_active_row());\n  }\n\n  ExtendedEvals BuildExtendedCircuitColumnPSE(\n      CustomGateEvaluator<Evals>& custom_gate_evaluator,\n      PermutationEvaluator<Evals>& permutation_evaluator,\n      lookup::Evaluator<kLookupType, Evals>& lookup_evaluator,\n      shuffle::Evaluator<EvalsOrExtendedEvals>& shuffle_evaluator) {\n    ExtendedEvals ret = extended_domain_->template Zero<ExtendedEvals>();\n    size_t circuit_num = poly_tables_.size();\n    for (size_t i = 0; i < circuit_num; ++i) {\n      VLOG(1) << \"BuildExtendedCircuitColumn circuit: (\" << i + 1 << \" / \"\n              << circuit_num << \")\";\n      custom_gate_evaluator.UpdateCosets(*this, i);\n      permutation_evaluator.UpdateCosets(*this, i);\n      lookup_evaluator.UpdateCosets(*this, i);\n      shuffle_evaluator.UpdateCosets(*this, i);\n\n      base::Parallelize(\n          ret.evaluations(),\n          [this, &custom_gate_evaluator, &permutation_evaluator,\n           &lookup_evaluator, &shuffle_evaluator](\n              absl::Span<F> chunk, size_t chunk_offset, size_t chunk_size) {\n            custom_gate_evaluator.Evaluate(*this, chunk, chunk_offset,\n                                           chunk_size);\n            permutation_evaluator.Evaluate(*this, chunk, chunk_offset,\n                                           chunk_size);\n            lookup_evaluator.Evaluate(*this, chunk, chunk_offset, chunk_size);\n            shuffle_evaluator.Evaluate(*this, chunk, chunk_offset, chunk_size);\n          });\n    }\n    return ret;\n  }\n\n  ExtendedEvals BuildExtendedCircuitColumnScroll(\n      CustomGateEvaluator<Evals>& custom_gate_evaluator,\n      PermutationEvaluator<Evals>& permutation_evaluator,\n      lookup::Evaluator<kLookupType, Evals>& lookup_evaluator,\n      shuffle::Evaluator<EvalsOrExtendedEvals>& shuffle_evaluator) {\n    std::vector<std::vector<F>> value_parts;\n    value_parts.reserve(num_parts_);\n    // Calculate the quotient polynomial for each part\n    for (size_t i = 0; i < num_parts_; ++i) {\n      VLOG(1) << \"BuildExtendedCircuitColumn part: (\" << i + 1 << \" / \"\n              << num_parts_ << \")\";\n\n      coset_domain_ = domain_->GetCoset(zeta_ * current_extended_omega_);\n\n      UpdateLPolyCosets();\n\n      std::vector<F> value_part(static_cast<size_t>(n_));\n      base::ParallelizeFill(value_part, F::Zero(),\n                            math::ParallelizeThreshold::kFieldInit);\n      size_t circuit_num = poly_tables_.size();\n      for (size_t j = 0; j < circuit_num; ++j) {\n        VLOG(1) << \"BuildExtendedCircuitColumn part: \" << i << \" circuit: (\"\n                << j + 1 << \" / \" << circuit_num << \")\";\n        custom_gate_evaluator.UpdateCosets(*this, j);\n        permutation_evaluator.UpdateCosets(*this, j);\n        lookup_evaluator.UpdateCosets(*this, j);\n        shuffle_evaluator.UpdateCosets(*this, j);\n\n        base::Parallelize(\n            value_part,\n            [this, &custom_gate_evaluator, &permutation_evaluator,\n             &lookup_evaluator, &shuffle_evaluator](\n                absl::Span<F> chunk, size_t chunk_offset, size_t chunk_size) {\n              custom_gate_evaluator.Evaluate(*this, chunk, chunk_offset,\n                                             chunk_size);\n              permutation_evaluator.Evaluate(*this, chunk, chunk_offset,\n                                             chunk_size);\n              lookup_evaluator.Evaluate(*this, chunk, chunk_offset, chunk_size);\n              shuffle_evaluator.Evaluate(*this, chunk, chunk_offset,\n                                         chunk_size);\n            });\n      }\n\n      value_parts.push_back(std::move(value_part));\n      current_extended_omega_ *= extended_omega_;\n    }\n    std::vector<F> extended = BuildExtendedColumnWithColumns(value_parts);\n    return ExtendedEvals(std::move(extended));\n  }\n\n  // not owned\n  const Domain* domain_ = nullptr;\n  const ExtendedDomain* extended_domain_ = nullptr;\n  std::unique_ptr<Domain> coset_domain_;\n\n  F current_extended_omega_ = F::One();\n\n  int32_t n_ = 0;\n  size_t num_parts_ = 0;\n  size_t chunk_len_ = 0;\n  const F& omega_;\n  const F& extended_omega_;\n  F delta_;\n  const F& theta_;\n  const F& beta_;\n  const F& gamma_;\n  const F& y_;\n  const F& zeta_;\n  Rotation last_rotation_;\n  F delta_start_;\n\n  const ProvingKey<PS>& proving_key_;\n  const std::vector<PermutationProver<Poly, Evals>>& permutation_provers_;\n  const std::vector<LookupProver>& lookup_provers_;\n  const std::vector<shuffle::Prover<Poly, Evals>>& shuffle_provers_;\n  const std::vector<MultiPhaseRefTable<Poly>>& poly_tables_;\n\n  EvalsOrExtendedEvals l_first_;\n  EvalsOrExtendedEvals l_last_;\n  EvalsOrExtendedEvals l_active_row_;\n\n  MultiPhaseRefTable<EvalsOrExtendedEvals> table_;\n};\n\n}  // namespace plonk\n}  // namespace tachyon::zk\n\n#endif  // TACHYON_ZK_PLONK_VANISHING_CIRCUIT_POLYNOMIAL_BUILDER_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/vanishing/circuit_polynomial_builder_forward.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_VANISHING_CIRCUIT_POLYNOMIAL_BUILDER_FORWARD_H_\n#define TACHYON_ZK_PLONK_VANISHING_CIRCUIT_POLYNOMIAL_BUILDER_FORWARD_H_\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename PS>\nclass CircuitPolynomialBuilder;\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_VANISHING_CIRCUIT_POLYNOMIAL_BUILDER_FORWARD_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/vanishing/custom_gate_evaluator.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_VANISHING_CUSTOM_GATE_EVALUATOR_H_\n#define TACHYON_ZK_PLONK_VANISHING_CUSTOM_GATE_EVALUATOR_H_\n\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/profiler.h\"\n#include \"tachyon/zk/plonk/constraint_system/gate.h\"\n#include \"tachyon/zk/plonk/vanishing/circuit_polynomial_builder_forward.h\"\n#include \"tachyon/zk/plonk/vanishing/graph_evaluator.h\"\n#include \"tachyon/zk/plonk/vanishing/vanishing_utils.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename EvalsOrExtendedEvals>\nclass CustomGateEvaluator {\n public:\n  using F = typename EvalsOrExtendedEvals::Field;\n\n  void Construct(const std::vector<Gate<F>>& gates) {\n    TRACE_EVENT(\"ProofGeneration\",\n                \"Plonk::Vanishing::CustomGateEvaluator::Construct\");\n    std::vector<ValueSource> parts;\n    for (const Gate<F>& gate : gates) {\n      std::vector<ValueSource> tmp =\n          base::Map(gate.polys(),\n                    [this](const std::unique_ptr<Expression<F>>& expression) {\n                      return evaluator_.AddExpression(expression.get());\n                    });\n      base::Extend(parts, std::move(tmp));\n    }\n    evaluator_.AddCalculation(Calculation::Horner(\n        ValueSource::PreviousValue(), std::move(parts), ValueSource::Y()));\n  }\n\n  template <typename PS>\n  void Evaluate(CircuitPolynomialBuilder<PS>& builder, absl::Span<F> chunk,\n                size_t chunk_offset, size_t chunk_size) {\n    TRACE_EVENT(\"ProofGeneration\",\n                \"Plonk::Vanishing::CustomGateEvaluator::Evaluate\");\n    EvaluationInput<EvalsOrExtendedEvals> evaluation_input =\n        builder.ExtractEvaluationInput(evaluator_.CreateInitialIntermediates(),\n                                       evaluator_.CreateEmptyRotations());\n    size_t start = chunk_offset * chunk_size;\n    for (size_t i = 0; i < chunk.size(); ++i) {\n      chunk[i] = evaluator_.Evaluate(evaluation_input, start + i,\n                                     /*scale=*/1, chunk[i]);\n    }\n  }\n\n  template <typename PS>\n  void UpdateCosets(CircuitPolynomialBuilder<PS>& builder, size_t circuit_idx) {\n    using PCS = typename PS::PCS;\n    using PCS = typename PS::PCS;\n    using Poly = typename PCS::Poly;\n\n    TRACE_EVENT(\"ProofGeneration\",\n                \"Plonk::Vanishing::CustomGateEvaluator::UpdateCosets\");\n\n    constexpr halo2::Vendor kVendor = PS::kVendor;\n\n    if constexpr (kVendor == halo2::Vendor::kScroll) {\n      TRACE_EVENT(\"Subtask\", \"Calculate fixed column cosets\");\n      absl::Span<const Poly> new_fixed_columns =\n          builder.poly_tables_[circuit_idx].GetFixedColumns();\n      fixed_column_cosets_.resize(new_fixed_columns.size());\n      for (size_t i = 0; i < new_fixed_columns.size(); ++i) {\n        fixed_column_cosets_[i] =\n            builder.coset_domain_->FFT(new_fixed_columns[i]);\n      }\n    }\n\n    {\n      TRACE_EVENT(\"Subtask\", \"Calculate advice column cosets\");\n      absl::Span<const Poly> new_advice_columns =\n          builder.poly_tables_[circuit_idx].GetAdviceColumns();\n      advice_column_cosets_.resize(new_advice_columns.size());\n      for (size_t i = 0; i < new_advice_columns.size(); ++i) {\n        if constexpr (kVendor == halo2::Vendor::kPSE) {\n          advice_column_cosets_[i] =\n              CoeffToExtended(new_advice_columns[i], builder.extended_domain_);\n        } else {\n          advice_column_cosets_[i] =\n              builder.coset_domain_->FFT(new_advice_columns[i]);\n        }\n      }\n    }\n\n    {\n      TRACE_EVENT(\"Subtask\", \"Calculate instance column cosets\");\n      absl::Span<const Poly> new_instance_columns =\n          builder.poly_tables_[circuit_idx].GetInstanceColumns();\n      instance_column_cosets_.resize(new_instance_columns.size());\n      for (size_t i = 0; i < new_instance_columns.size(); ++i) {\n        if constexpr (kVendor == halo2::Vendor::kPSE) {\n          instance_column_cosets_[i] = CoeffToExtended(\n              new_instance_columns[i], builder.extended_domain_);\n        } else {\n          instance_column_cosets_[i] =\n              builder.coset_domain_->FFT(new_instance_columns[i]);\n        }\n      }\n    }\n\n    if constexpr (kVendor == halo2::Vendor::kPSE) {\n      TRACE_EVENT(\"Subtask\", \"Construct table PSE\");\n      builder.table_ = {\n          builder.proving_key_.fixed_cosets(),\n          advice_column_cosets_,\n          instance_column_cosets_,\n          builder.poly_tables_[circuit_idx].challenges(),\n      };\n    } else {\n      TRACE_EVENT(\"Subtask\", \"Construct table Scroll\");\n      builder.table_ = {\n          fixed_column_cosets_,\n          advice_column_cosets_,\n          instance_column_cosets_,\n          builder.poly_tables_[circuit_idx].challenges(),\n      };\n    }\n  }\n\n private:\n  GraphEvaluator<F> evaluator_;\n  std::vector<EvalsOrExtendedEvals> fixed_column_cosets_;\n  std::vector<EvalsOrExtendedEvals> advice_column_cosets_;\n  std::vector<EvalsOrExtendedEvals> instance_column_cosets_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_VANISHING_CUSTOM_GATE_EVALUATOR_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/vanishing/evaluation_input.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_VANISHING_EVALUATION_INPUT_H_\n#define TACHYON_ZK_PLONK_VANISHING_EVALUATION_INPUT_H_\n\n#include <stddef.h>\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/zk/plonk/base/multi_phase_ref_table.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename Evals>\nclass EvaluationInput {\n public:\n  using F = typename Evals::Field;\n\n  EvaluationInput(std::vector<F>&& intermediates,\n                  std::vector<int32_t>&& rotations,\n                  const MultiPhaseRefTable<Evals>& table, const F& theta,\n                  const F& beta, const F& gamma, const F& y, int32_t n)\n      : intermediates_(std::move(intermediates)),\n        rotations_(std::move(rotations)),\n        table_(table),\n        theta_(theta),\n        beta_(beta),\n        gamma_(gamma),\n        y_(y),\n        n_(n) {}\n\n  const std::vector<F>& intermediates() const { return intermediates_; }\n  std::vector<F>& intermediates() { return intermediates_; }\n  const std::vector<int32_t>& rotations() const { return rotations_; }\n  std::vector<int32_t>& rotations() { return rotations_; }\n  const MultiPhaseRefTable<Evals>& table() const { return table_; }\n  const F& theta() const { return theta_; }\n  const F& beta() const { return beta_; }\n  const F& gamma() const { return gamma_; }\n  const F& y() const { return y_; }\n  int32_t n() const { return n_; }\n\n private:\n  std::vector<F> intermediates_;\n  std::vector<int32_t> rotations_;\n  const MultiPhaseRefTable<Evals>& table_;\n  const F& theta_;\n  const F& beta_;\n  const F& gamma_;\n  const F& y_;\n  int32_t n_ = 0;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_VANISHING_EVALUATION_INPUT_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/vanishing/graph_evaluator.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_VANISHING_GRAPH_EVALUATOR_H_\n#define TACHYON_ZK_PLONK_VANISHING_GRAPH_EVALUATOR_H_\n\n#include <string>\n#include <vector>\n\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/zk/expressions/constant_expression.h\"\n#include \"tachyon/zk/expressions/evaluator.h\"\n#include \"tachyon/zk/expressions/negated_expression.h\"\n#include \"tachyon/zk/expressions/product_expression.h\"\n#include \"tachyon/zk/expressions/scaled_expression.h\"\n#include \"tachyon/zk/expressions/sum_expression.h\"\n#include \"tachyon/zk/plonk/expressions/advice_expression.h\"\n#include \"tachyon/zk/plonk/expressions/challenge_expression.h\"\n#include \"tachyon/zk/plonk/expressions/fixed_expression.h\"\n#include \"tachyon/zk/plonk/expressions/instance_expression.h\"\n#include \"tachyon/zk/plonk/expressions/selector_expression.h\"\n#include \"tachyon/zk/plonk/vanishing/calculation.h\"\n\nnamespace tachyon::zk::plonk {\n\nstruct TACHYON_EXPORT CalculationInfo {\n  Calculation calculation;\n  size_t target;\n\n  CalculationInfo() = default;\n  CalculationInfo(Calculation calculation, size_t target)\n      : calculation(calculation), target(target) {}\n\n  bool operator==(const CalculationInfo& other) const {\n    return calculation == other.calculation && target == other.target;\n  }\n  bool operator!=(const CalculationInfo& other) const {\n    return !operator==(other);\n  }\n\n  std::string ToString() const {\n    return absl::Substitute(\"{calculation: $0, target: $1}\",\n                            calculation.ToString(), target);\n  }\n};\n\ntemplate <typename F>\nclass GraphEvaluator : public tachyon::zk::Evaluator<F, ValueSource> {\n public:\n  GraphEvaluator() = default;\n\n  const std::vector<F>& constants() const { return constants_; }\n  const std::vector<int32_t>& rotations() { return rotations_; }\n  const std::vector<CalculationInfo>& calculations() { return calculations_; }\n  size_t num_intermediates() const { return num_intermediates_; }\n\n  template <typename Evals>\n  F Evaluate(EvaluationInput<Evals>& data, size_t idx, int32_t scale,\n             const F& previous_value) const {\n    for (size_t i = 0; i < rotations_.size(); ++i) {\n      data.rotations()[i] =\n          Rotation(rotations_[i]).GetIndex(idx, scale, data.n());\n    }\n\n    for (const CalculationInfo& calculation : calculations_) {\n      data.intermediates()[calculation.target] =\n          calculation.calculation.Evaluate(data, constants_, previous_value);\n    }\n\n    if (calculations_.empty()) return F::Zero();\n    return data.intermediates()[calculations_.back().target];\n  }\n\n  // Evaluator methods\n  ValueSource Evaluate(const Expression<F>* input) override {\n    switch (input->type()) {\n      case ExpressionType::kConstant:\n        return AddConstant(input->ToConstant()->value());\n      case ExpressionType::kSelector:\n        break;\n      case ExpressionType::kFixed: {\n        const FixedExpression<F>* expr = input->ToFixed();\n        const FixedQuery& query = expr->query();\n        size_t rotation_index = AddRotation(query.rotation());\n        return AddCalculation(Calculation::Store(\n            ValueSource::Fixed(query.column().index(), rotation_index)));\n      }\n      case ExpressionType::kAdvice: {\n        const AdviceExpression<F>* expr = input->ToAdvice();\n        const AdviceQuery& query = expr->query();\n        size_t rotation_index = AddRotation(query.rotation());\n        return AddCalculation(Calculation::Store(\n            ValueSource::Advice(query.column().index(), rotation_index)));\n      }\n      case ExpressionType::kInstance: {\n        const InstanceExpression<F>* expr = input->ToInstance();\n        const InstanceQuery& query = expr->query();\n        size_t rotation_index = AddRotation(query.rotation());\n        return AddCalculation(Calculation::Store(\n            ValueSource::Instance(query.column().index(), rotation_index)));\n      }\n      case ExpressionType::kChallenge: {\n        const ChallengeExpression<F>* expr = input->ToChallenge();\n        Challenge challenge = expr->challenge();\n        return AddCalculation(\n            Calculation::Store(ValueSource::Challenge(challenge.index())));\n      }\n      case ExpressionType::kNegated: {\n        const NegatedExpression<F>* expr = input->ToNegated();\n        if (expr->expr()->type() == ExpressionType::kConstant) {\n          return AddConstant(-expr->expr()->ToConstant()->value());\n        }\n        ValueSource result = AddExpression(expr->expr());\n        if (result.IsZeroConstant()) {\n          return result;\n        }\n        return AddCalculation(Calculation::Negate(result));\n      }\n      case ExpressionType::kSum: {\n        const SumExpression<F>* sum = input->ToSum();\n        ValueSource left_result = AddExpression(sum->left());\n        if (sum->right()->type() == ExpressionType::kNegated) {\n          const NegatedExpression<F>* right = sum->right()->ToNegated();\n          ValueSource right_result = AddExpression(right->expr());\n          if (left_result.IsZeroConstant()) {\n            return AddCalculation(Calculation::Negate(right_result));\n          } else if (right_result.IsZeroConstant()) {\n            return left_result;\n          } else {\n            return AddCalculation(Calculation::Sub(left_result, right_result));\n          }\n        } else {\n          ValueSource right_result = AddExpression(sum->right());\n          if (left_result.IsZeroConstant()) {\n            return right_result;\n          } else if (right_result.IsZeroConstant()) {\n            return left_result;\n          } else {\n            // NOTE(chokobole): I don't know why this is needed.\n            // See\n            // https://github.com/kroma-network/halo2/blob/7d0a369/halo2_proofs/src/plonk/evaluation.rs#L709-L713.\n            if (left_result <= right_result) {\n              return AddCalculation(\n                  Calculation::Add(left_result, right_result));\n            } else {\n              return AddCalculation(\n                  Calculation::Add(right_result, left_result));\n            }\n          }\n        }\n      }\n      case ExpressionType::kProduct: {\n        const ProductExpression<F>* product = input->ToProduct();\n        ValueSource left_result = AddExpression(product->left());\n        ValueSource right_result = AddExpression(product->right());\n        if (left_result.IsZeroConstant() || right_result.IsZeroConstant()) {\n          return ValueSource::ZeroConstant();\n        } else if (left_result.IsOneConstant()) {\n          return right_result;\n        } else if (right_result.IsOneConstant()) {\n          return left_result;\n        } else if (left_result.IsTwoConstant()) {\n          return AddCalculation(Calculation::Double(right_result));\n        } else if (right_result.IsTwoConstant()) {\n          return AddCalculation(Calculation::Double(left_result));\n        } else if (left_result == right_result) {\n          return AddCalculation(Calculation::Square(left_result));\n        } else {\n          // NOTE(chokobole): we can do |left_result < right_result|.\n          // But ValueSource only implements <= operator and original source\n          // code also compares like below, too. See\n          // https://github.com/kroma-network/halo2/blob/7d0a369/halo2_proofs/src/plonk/evaluation.rs#L732\n          if (left_result <= right_result) {\n            return AddCalculation(Calculation::Mul(left_result, right_result));\n          } else {\n            return AddCalculation(Calculation::Mul(right_result, left_result));\n          }\n        }\n      }\n      case ExpressionType::kScaled: {\n        const ScaledExpression<F>* scaled = input->ToScaled();\n        const F& scale = scaled->scale();\n        if (scale.IsZero()) {\n          return ValueSource::ZeroConstant();\n        } else if (scale.IsOne()) {\n          return AddExpression(scaled->expr());\n        } else {\n          ValueSource constant = AddConstant(scale);\n          ValueSource result = AddExpression(scaled->expr());\n          return AddCalculation(Calculation::Mul(result, constant));\n        }\n      }\n      case ExpressionType::kFirstRow:\n      case ExpressionType::kLastRow:\n      case ExpressionType::kTransition:\n      case ExpressionType::kVariable:\n        NOTREACHED() << \"AIR expression \"\n                     << ExpressionTypeToString(input->type())\n                     << \" is not allowed in plonk!\";\n    }\n    NOTREACHED();\n    return ValueSource();\n  }\n\n  std::vector<F> CreateInitialIntermediates() const {\n    return std::vector<F>(num_intermediates_);\n  }\n  std::vector<int32_t> CreateEmptyRotations() const {\n    return std::vector<int32_t>(rotations_.size(), 0);\n  }\n\n  // Currently does the simplest thing possible: just stores the\n  // resulting value so the result can be reused  when that calculation\n  // is done multiple times.\n  ValueSource AddCalculation(const Calculation& calculation) {\n    auto it = std::find_if(calculations_.begin(), calculations_.end(),\n                           [&calculation](const CalculationInfo& info) {\n                             return info.calculation == calculation;\n                           });\n    if (it != calculations_.end()) return ValueSource::Intermediate(it->target);\n    size_t target = num_intermediates_++;\n    calculations_.emplace_back(calculation, target);\n    return ValueSource::Intermediate(target);\n  }\n\n  // Generates an optimized evaluation for the expression\n  ValueSource AddExpression(const Expression<F>* expression) {\n    return expression->Evaluate(this);\n  }\n\n private:\n  size_t AddRotation(const Rotation& rotation) {\n    size_t rotation_index = rotation.value();\n    std::optional<size_t> position = base::FindIndexIf(\n        rotations_,\n        [rotation_index](size_t value) { return rotation_index == value; });\n    if (position.has_value()) return position.value();\n    rotations_.push_back(rotation_index);\n    return rotations_.size() - 1;\n  }\n\n  ValueSource AddConstant(const F& constant) {\n    std::optional<size_t> position = base::FindIndex(constants_, constant);\n    if (position.has_value()) return ValueSource::Constant(position.value());\n    constants_.push_back(constant);\n    return ValueSource::Constant(constants_.size() - 1);\n  }\n\n  std::vector<F> constants_ = {F::Zero(), F::One(), F(2)};\n  std::vector<int32_t> rotations_;\n  std::vector<CalculationInfo> calculations_;\n  size_t num_intermediates_ = 0;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_VANISHING_GRAPH_EVALUATOR_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/vanishing/graph_evaluator_unittest.cc",
    "content": "#include \"tachyon/zk/plonk/vanishing/graph_evaluator.h\"\n\n#include <memory>\n\n#include \"tachyon/base/random.h\"\n#include \"tachyon/zk/plonk/expressions/evaluator/test/evaluator_test.h\"\n#include \"tachyon/zk/plonk/expressions/expression_factory.h\"\n\nnamespace tachyon::zk::plonk {\n\nnamespace {\n\nusing Expr = std::unique_ptr<Expression<GF7>>;\n\nstruct ColumnInfo {\n  size_t column_index;\n  int32_t rotation;\n  size_t rotation_index;\n  size_t calculation_index;\n};\n\nstd::vector<ColumnInfo> GenerateTestColumnInfos() {\n  std::vector<ColumnInfo> test_column_infos;\n  test_column_infos.push_back({0, 1, 0, 0});\n  test_column_infos.push_back({1, 2, 1, 1});\n  test_column_infos.push_back({0, 1, 0, 0});\n  test_column_infos.push_back({0, 2, 1, 2});\n  return test_column_infos;\n}\n\nclass GraphEvaluatorTest : public EvaluatorTest {};\n\n}  // namespace\n\nTEST_F(GraphEvaluatorTest, Constant) {\n  GraphEvaluator<GF7> graph_evaluator;\n  int values[] = {0, 1, 2, base::Uniform(base::Range<int>(3, 7))};\n  for (int value : values) {\n    std::unique_ptr<Expression<GF7>> expr =\n        ExpressionFactory<GF7>::Constant(GF7(value));\n    ValueSource source = graph_evaluator.Evaluate(expr.get());\n    if (value == 0) {\n      EXPECT_TRUE(source.IsZeroConstant());\n    } else if (value == 1) {\n      EXPECT_TRUE(source.IsOneConstant());\n    } else if (value == 2) {\n      EXPECT_TRUE(source.IsTwoConstant());\n    } else {\n      EXPECT_EQ(source, ValueSource::Constant(3));\n    }\n  }\n}\n\nTEST_F(GraphEvaluatorTest, Selector) {\n  Expr expr = ExpressionFactory<GF7>::Selector(Selector::Simple(1));\n  GraphEvaluator<GF7> graph_evaluator;\n  EXPECT_DEATH(graph_evaluator.Evaluate(expr.get()), \"\");\n}\n\nTEST_F(GraphEvaluatorTest, Fixed) {\n  std::vector<ColumnInfo> test_column_infos = GenerateTestColumnInfos();\n\n  GraphEvaluator<GF7> graph_evaluator;\n  for (const ColumnInfo& column_info : test_column_infos) {\n    FixedQuery query(1, Rotation(column_info.rotation),\n                     FixedColumnKey(column_info.column_index));\n    Expr expr = ExpressionFactory<GF7>::Fixed(query);\n    ValueSource source = graph_evaluator.Evaluate(expr.get());\n    EXPECT_EQ(source, ValueSource::Intermediate(column_info.calculation_index));\n    EXPECT_EQ(graph_evaluator.calculations()[column_info.calculation_index],\n              CalculationInfo(\n                  Calculation::Store(ValueSource::Fixed(\n                      column_info.column_index, column_info.rotation_index)),\n                  column_info.calculation_index));\n    EXPECT_EQ(graph_evaluator.rotations()[column_info.rotation_index],\n              column_info.rotation);\n  }\n}\n\nTEST_F(GraphEvaluatorTest, Advice) {\n  std::vector<ColumnInfo> test_column_infos = GenerateTestColumnInfos();\n\n  GraphEvaluator<GF7> graph_evaluator;\n  for (const ColumnInfo& column_info : test_column_infos) {\n    AdviceQuery query(1, Rotation(column_info.rotation),\n                      AdviceColumnKey(column_info.column_index));\n    Expr expr = ExpressionFactory<GF7>::Advice(query);\n    ValueSource source = graph_evaluator.Evaluate(expr.get());\n    EXPECT_EQ(source, ValueSource::Intermediate(column_info.calculation_index));\n    EXPECT_EQ(graph_evaluator.calculations()[column_info.calculation_index],\n              CalculationInfo(\n                  Calculation::Store(ValueSource::Advice(\n                      column_info.column_index, column_info.rotation_index)),\n                  column_info.calculation_index));\n    EXPECT_EQ(graph_evaluator.rotations()[column_info.rotation_index],\n              column_info.rotation);\n  }\n}\n\nTEST_F(GraphEvaluatorTest, Instance) {\n  std::vector<ColumnInfo> test_column_infos = GenerateTestColumnInfos();\n\n  GraphEvaluator<GF7> graph_evaluator;\n  for (const ColumnInfo& column_info : test_column_infos) {\n    InstanceQuery query(1, Rotation(column_info.rotation),\n                        InstanceColumnKey(column_info.column_index));\n    Expr expr = ExpressionFactory<GF7>::Instance(query);\n    ValueSource source = graph_evaluator.Evaluate(expr.get());\n    EXPECT_EQ(source, ValueSource::Intermediate(column_info.calculation_index));\n    EXPECT_EQ(graph_evaluator.calculations()[column_info.calculation_index],\n              CalculationInfo(\n                  Calculation::Store(ValueSource::Instance(\n                      column_info.column_index, column_info.rotation_index)),\n                  column_info.calculation_index));\n    EXPECT_EQ(graph_evaluator.rotations()[column_info.rotation_index],\n              column_info.rotation);\n  }\n}\n\nTEST_F(GraphEvaluatorTest, Challenges) {\n  GraphEvaluator<GF7> graph_evaluator;\n  Expr expr = ExpressionFactory<GF7>::Challenge(Challenge(1, Phase(0)));\n  ValueSource source = graph_evaluator.Evaluate(expr.get());\n  EXPECT_EQ(source, ValueSource::Intermediate(0));\n  EXPECT_EQ(graph_evaluator.calculations()[0],\n            CalculationInfo(Calculation::Store(ValueSource::Challenge(1)), 0));\n}\n\nTEST_F(GraphEvaluatorTest, ConstantNegated) {\n  GraphEvaluator<GF7> graph_evaluator;\n  for (size_t i = 0; i < 7; ++i) {\n    std::unique_ptr<Expression<GF7>> expr = ExpressionFactory<GF7>::Negated(\n        ExpressionFactory<GF7>::Constant(GF7(i)));\n    ValueSource source = graph_evaluator.Evaluate(expr.get());\n    if (i == 0) {\n      EXPECT_TRUE(source.IsZeroConstant());\n    } else if (i == 6) {\n      // Negation of 6 in GF7 is 1.\n      EXPECT_EQ(source, ValueSource::OneConstant());\n    } else if (i == 5) {\n      // Negation of 5 in GF7 is 2.\n      EXPECT_EQ(source, ValueSource::TwoConstant());\n    } else {\n      // Other constants besides 0, 1, and 2 are stored sequentially regardless\n      // of their value.\n      EXPECT_EQ(source, ValueSource::Constant(i + 2));\n    }\n  }\n}\n\nTEST_F(GraphEvaluatorTest, Negated) {\n  std::vector<ColumnInfo> test_column_infos = GenerateTestColumnInfos();\n  // clang-format off\n  // 1. test_column_infos[0]\n  //      = {column_index: 0, rotation: 1, rotation_index: 0, calculation_index: 0}\n  //    expected |calculations_|\n  //      = {test_column_infos[0], Negate(test_column_infos[0])}\n\n  // 2. test_column_infos[1]\n  //      = {column_index: 1, rotation: 2, rotation_index: 1, calculation_index: 1}\n  //    expected |calculations_|\n  //      = {test_column_infos[0], Negate(test_column_infos[0]), test_column_infos[1], Negate(test_column_infos[1])}\n\n  // 3. test_column_infos[2] (test_column_infos[2] is same as test_column_infos[0])\n  //      = {column_index: 0, rotation: 1, rotation_index: 0, calculation_index: 0}\n  //    expected |calculations_|\n  //      = {test_column_infos[0], Negate(test_column_infos[0]), test_column_infos[1], Negate(test_column_infos[1])}\n\n  // 4. test_column_infos[3]\n  //      = {column_index: 0, rotation: 2, rotation_index: 1, calculation_index: 2}\n  //    expected |calculations_|\n  //      = {test_column_infos[0], Negate(test_column_infos[0]), test_column_infos[1], Negate(test_column_infos[1]), test_column_infos[3], Negate(test_column_infos[3])}\n  // clang-format on\n  std::vector<size_t> expected_indices = {1, 3, 1, 5};\n\n  GraphEvaluator<GF7> graph_evaluator;\n  for (size_t i = 0; i < test_column_infos.size(); ++i) {\n    FixedQuery query(1, Rotation(test_column_infos[i].rotation),\n                     FixedColumnKey(test_column_infos[i].column_index));\n    std::unique_ptr<Expression<GF7>> expr =\n        ExpressionFactory<GF7>::Negated(ExpressionFactory<GF7>::Fixed(query));\n    ValueSource source = graph_evaluator.Evaluate(expr.get());\n    EXPECT_EQ(source, ValueSource::Intermediate(expected_indices[i]));\n\n    EXPECT_EQ(graph_evaluator.calculations()[expected_indices[i]],\n              CalculationInfo(Calculation::Negate(ValueSource::Intermediate(\n                                  expected_indices[i] - 1)),\n                              expected_indices[i]));\n    EXPECT_EQ(graph_evaluator.calculations()[expected_indices[i] - 1],\n              CalculationInfo(Calculation::Store(ValueSource::Fixed(\n                                  test_column_infos[i].column_index,\n                                  test_column_infos[i].rotation_index)),\n                              expected_indices[i] - 1));\n  }\n\n  EXPECT_EQ(graph_evaluator.rotations().size(), 2);\n  EXPECT_EQ(graph_evaluator.rotations()[0], test_column_infos[0].rotation);\n  EXPECT_EQ(graph_evaluator.rotations()[1], test_column_infos[1].rotation);\n}\n\nTEST_F(GraphEvaluatorTest, NegatedSum) {\n  struct {\n    int left;\n    int right;\n    int expected_left_index;\n    int expected_right_index;\n  } tests[]{\n      // default |constants_| = {0, 1, 2}\n\n      // Case 1(left = 0):\n      {0, 4, 0, 3},\n      // expected |constants_| = {0, 1, 2, 4}\n      // expected |calculations_| = {Negate(4)}\n\n      // Case 2(right = 0):\n      {3, 0, 4, 0},\n      // expected |constants_| = {0, 1, 2, 4, 3}\n      // expected |calculations_| = {Negate(4)}\n\n      // Case 3:\n      {2, 6, 2, 5},\n      // expected |constants_| = {0, 1, 2, 4, 3, 6}\n      // expected |calculations_| = {Negate(4), Sub(2, 6)}\n      {5, 4, 6, 3},\n      // expected |constants_| = {0, 1, 2, 4, 3, 6, 5}\n      // expected |calculations_| = {Negate(4), Sub(2, 6), Sub(5, 4)}\n  };\n\n  size_t expected_calculations = 0;\n\n  GraphEvaluator<GF7> graph_evaluator;\n  for (const auto& test : tests) {\n    std::unique_ptr<Expression<GF7>> expr = ExpressionFactory<GF7>::Sum(\n        ExpressionFactory<GF7>::Constant(GF7(test.left)),\n        ExpressionFactory<GF7>::Negated(\n            ExpressionFactory<GF7>::Constant(GF7(test.right))));\n    ValueSource source = graph_evaluator.Evaluate(expr.get());\n\n    if (test.left == 0) {\n      // Case 1(left = 0):\n      EXPECT_EQ(source, ValueSource::Intermediate(expected_calculations));\n      EXPECT_EQ(graph_evaluator.calculations()[expected_calculations],\n                CalculationInfo(Calculation::Negate(ValueSource::Constant(\n                                    test.expected_right_index)),\n                                expected_calculations));\n      ++expected_calculations;\n    } else if (test.right == 0) {\n      // Case 2(right = 0):\n      EXPECT_EQ(source, ValueSource::Constant(test.expected_left_index));\n    } else {\n      // Case 3:\n      EXPECT_EQ(source, ValueSource::Intermediate(expected_calculations));\n      EXPECT_EQ(\n          graph_evaluator.calculations()[expected_calculations],\n          CalculationInfo(Calculation::Sub(\n                              ValueSource::Constant(test.expected_left_index),\n                              ValueSource::Constant(test.expected_right_index)),\n                          expected_calculations));\n      ++expected_calculations;\n    }\n    EXPECT_EQ(graph_evaluator.num_intermediates(), expected_calculations);\n  }\n}\n\nTEST_F(GraphEvaluatorTest, ConstantSum) {\n  struct {\n    int left;\n    int right;\n    int expected_left_index;\n    int expected_right_index;\n  } tests[]{\n      // default |constants_| = {0, 1, 2}\n\n      // Case 1(left = 0):\n      {0, 4, 0, 3},\n      // expected |constants_| = {0, 1, 2, 4}\n      // expected |calculations_| = {}\n\n      // Case 2(right = 0):\n      {3, 0, 4, 0},\n      // expected |constants_| = {0, 1, 2, 4, 3}\n      // expected |calculations_| = {}\n\n      // Case 3(left <= right):\n      {2, 6, 2, 5},\n      // expected |constants_| = {0, 1, 2, 4, 3, 6}\n      // expected |calculations_| = {Add(2, 6)}\n\n      // Case 4(left > right):\n      {5, 4, 6, 3},\n      // expected |constants_| = {0, 1, 2, 4, 3, 6, 5}\n      // expected |calculations_| = {Add(2, 6), Add(5, 4)}\n  };\n\n  size_t expected_calculations = 0;\n\n  GraphEvaluator<GF7> graph_evaluator;\n  for (const auto& test : tests) {\n    std::unique_ptr<Expression<GF7>> expr = ExpressionFactory<GF7>::Sum(\n        ExpressionFactory<GF7>::Constant(GF7(test.left)),\n        ExpressionFactory<GF7>::Constant(GF7(test.right)));\n    ValueSource source = graph_evaluator.Evaluate(expr.get());\n    if (test.left == 0) {\n      // Case 1(left = 0):\n      EXPECT_EQ(source, ValueSource::Constant(test.expected_right_index));\n    } else if (test.right == 0) {\n      // Case 2(right = 0):\n      EXPECT_EQ(source, ValueSource::Constant(test.expected_left_index));\n    } else {\n      EXPECT_EQ(source, ValueSource::Intermediate(expected_calculations));\n      if (test.left <= test.right) {\n        // Case 3(left <= right):\n        EXPECT_EQ(graph_evaluator.calculations()[expected_calculations],\n                  CalculationInfo(\n                      Calculation::Add(\n                          ValueSource::Constant(test.expected_left_index),\n                          ValueSource::Constant(test.expected_right_index)),\n                      expected_calculations));\n      } else {\n        // Case 4(left > right):\n        EXPECT_EQ(graph_evaluator.calculations()[expected_calculations],\n                  CalculationInfo(\n                      Calculation::Add(\n                          ValueSource::Constant(test.expected_right_index),\n                          ValueSource::Constant(test.expected_left_index)),\n                      expected_calculations));\n      }\n      ++expected_calculations;\n    }\n    EXPECT_EQ(graph_evaluator.num_intermediates(), expected_calculations);\n  }\n}\n\nTEST_F(GraphEvaluatorTest, ConstantProduct) {\n  struct {\n    int left;\n    int right;\n    int expected_left_index;\n    int expected_right_index;\n  } tests[]{\n      // default |constants_| = {0, 1, 2}\n\n      // Case 1(left = 0 || right = 0):\n      {0, 3, 0, 3},\n      // expected |constants_| = {0, 1, 2, 3}\n      // expected |calculations_| = {}\n      {5, 0, 4, 0},\n      // expected |constants_| = {0, 1, 2, 3, 5}\n      // expected |calculations_| = {}\n\n      // Case 2(left = 1):\n      {1, 2, 1, 2},\n      // expected |constants_| = {0, 1, 2, 3, 5}\n      // expected |calculations_| = {}\n\n      // Case 3(right = 1):\n      {3, 1, 3, 1},\n      // expected |constants_| = {0, 1, 2, 3, 5}\n      // expected |calculations_| = {}\n\n      // Case 4(left = 2):\n      {2, 4, 2, 5},\n      // expected |constants_| = {0, 1, 2, 3, 5, 4}\n      // expected |calculations_| = {Double(4)}\n\n      // Case 5(right = 2):\n      {3, 2, 3, 2},\n      // expected |constants_| = {0, 1, 2, 3, 5, 4}\n      // expected |calculations_| = {Double(4), Double(3)}\n\n      // Case 6(left = right):\n      {5, 5, 4, 4},\n      // expected |constants_| = {0, 1, 2, 3, 5, 4}\n      // expected |calculations_| = {Double(4), Double(3), Square(5)}\n\n      // Case 7:\n      {4, 6, 5, 6},\n      // expected |constants_| = {0, 1, 2, 3, 5, 4, 6}\n      // expected |calculations_| = {Double(4), Double(3), Square(5), Mul(4, 6)}\n      // expected size of |calculations_| = 4\n  };\n\n  size_t expected_calculations = 0;\n\n  GraphEvaluator<GF7> graph_evaluator;\n  for (const auto& test : tests) {\n    std::unique_ptr<Expression<GF7>> expr = ExpressionFactory<GF7>::Product(\n        ExpressionFactory<GF7>::Constant(GF7(test.left)),\n        ExpressionFactory<GF7>::Constant(GF7(test.right)));\n    ValueSource source = graph_evaluator.Evaluate(expr.get());\n    if (test.left == 0 || test.right == 0) {\n      // Case 1(left = 0 || right = 0):\n      EXPECT_EQ(source, ValueSource::ZeroConstant());\n    } else if (test.left == 1) {\n      // Case 2(left = 1):\n      EXPECT_EQ(source, ValueSource::Constant(test.expected_right_index));\n    } else if (test.right == 1) {\n      // Case 3(right = 1):\n      EXPECT_EQ(source, ValueSource::Constant(test.expected_left_index));\n    } else {\n      EXPECT_EQ(source, ValueSource::Intermediate(expected_calculations));\n      if (test.left == 2) {\n        // Case 4(left = 2):\n        EXPECT_EQ(graph_evaluator.calculations()[expected_calculations],\n                  CalculationInfo(Calculation::Double(ValueSource::Constant(\n                                      test.expected_right_index)),\n                                  expected_calculations));\n      } else if (test.right == 2) {\n        // Case 5(right = 2):\n        EXPECT_EQ(graph_evaluator.calculations()[expected_calculations],\n                  CalculationInfo(Calculation::Double(ValueSource::Constant(\n                                      test.expected_left_index)),\n                                  expected_calculations));\n      } else if (test.left == test.right) {\n        // Case 6(left = right):\n        EXPECT_EQ(graph_evaluator.calculations()[expected_calculations],\n                  CalculationInfo(Calculation::Square(ValueSource::Constant(\n                                      test.expected_left_index)),\n                                  expected_calculations));\n      } else {\n        // Case 7:\n        EXPECT_EQ(graph_evaluator.calculations()[expected_calculations],\n                  CalculationInfo(\n                      Calculation::Mul(\n                          ValueSource::Constant(test.expected_left_index),\n                          ValueSource::Constant(test.expected_right_index)),\n                      expected_calculations));\n      }\n      ++expected_calculations;\n      EXPECT_EQ(graph_evaluator.num_intermediates(), expected_calculations);\n    }\n  }\n}\n\nTEST_F(GraphEvaluatorTest, Scaled) {\n  struct {\n    int expr;\n    int scalar;\n    int expected_expr_index;\n    int expected_scalar_index;\n  } tests[] = {\n      // default |constants_| = {0, 1, 2}\n\n      // Case 1(scalar = 0):\n      {2, 0, 2, 0},\n      // expected |constants_| = {0, 1, 2}\n      // expected |calculations_| = {}\n\n      // Case 2(scalar = 1):\n      {1, 1, 1, 1},\n      // expected |constants_| = {0, 1, 2}\n      // expected |calculations_| = {}\n      {4, 1, 3, 1},\n      // expected |constants_| = {0, 1, 2, 4}\n      // expected |calculations_| = {}\n\n      // Case 3:\n      {2, 2, 2, 2},\n      // expected |constants_| = {0, 1, 2, 4}\n      // expected |calculations_| = {Mul(2, 2)}\n      {6, 3, 5, 4},\n      // expected |constants_| = {0, 1, 2, 4, 6}\n      // expected |calculations_| = {Mul(2, 2), Mul(6, 3)}\n  };\n\n  size_t expected_calculations = 0;\n  GraphEvaluator<GF7> graph_evaluator;\n  for (const auto& test : tests) {\n    std::unique_ptr<Expression<GF7>> expr = ExpressionFactory<GF7>::Scaled(\n        ExpressionFactory<GF7>::Constant(GF7(test.expr)), GF7(test.scalar));\n    ValueSource source = graph_evaluator.Evaluate(expr.get());\n    if (test.scalar == 0) {\n      // Case 1(scalar = 0):\n      EXPECT_EQ(source, ValueSource::ZeroConstant());\n    } else if (test.scalar == 1) {\n      // Case 2(scalar = 1):\n      EXPECT_EQ(source, ValueSource::Constant(test.expected_expr_index));\n    } else {\n      // Case 3:\n      EXPECT_EQ(source, ValueSource::Intermediate(expected_calculations));\n      EXPECT_EQ(graph_evaluator.calculations()[expected_calculations],\n                CalculationInfo(\n                    Calculation::Mul(\n                        ValueSource::Constant(test.expected_expr_index),\n                        ValueSource::Constant(test.expected_scalar_index)),\n                    expected_calculations));\n      ++expected_calculations;\n    }\n    EXPECT_EQ(graph_evaluator.num_intermediates(), expected_calculations);\n  }\n}\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/vanishing/value_source.cc",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#include \"tachyon/zk/plonk/vanishing/value_source.h\"\n\n#include \"absl/strings/substitute.h\"\n\nnamespace tachyon::zk::plonk {\n\nstd::string ValueSource::ToString() const {\n  switch (type_) {\n    case Type::kConstant:\n      return absl::Substitute(\"Constant($0)\", index_);\n    case Type::kIntermediate:\n      return absl::Substitute(\"Intermediate($0)\", index_);\n    case Type::kFixed:\n      return absl::Substitute(\"Fixed($0, $1)\", column_index_, rotation_index_);\n    case Type::kAdvice:\n      return absl::Substitute(\"Advice($0, $1)\", column_index_, rotation_index_);\n    case Type::kInstance:\n      return absl::Substitute(\"Instance($0, $1)\", column_index_,\n                              rotation_index_);\n    case Type::kChallenge:\n      return absl::Substitute(\"Challenge($0)\", index_);\n    case Type::kBeta:\n      return \"Beta\";\n    case Type::kGamma:\n      return \"Gamma\";\n    case Type::kTheta:\n      return \"Theta\";\n    case Type::kY:\n      return \"Y\";\n    case Type::kPreviousValue:\n      return \"PreviousValue\";\n  }\n  NOTREACHED();\n  return \"\";\n}\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/vanishing/value_source.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_VANISHING_VALUE_SOURCE_H_\n#define TACHYON_ZK_PLONK_VANISHING_VALUE_SOURCE_H_\n\n#include <stddef.h>\n\n#include <string>\n#include <vector>\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/export.h\"\n#include \"tachyon/zk/plonk/vanishing/evaluation_input.h\"\n\nnamespace tachyon::zk::plonk {\n\nclass TACHYON_EXPORT ValueSource {\n public:\n  enum class Type {\n    kConstant,\n    kIntermediate,\n    kFixed,\n    kAdvice,\n    kInstance,\n    kChallenge,\n    kBeta,\n    kGamma,\n    kTheta,\n    kY,\n    kPreviousValue,\n  };\n\n  ValueSource() : ValueSource(Type::kConstant, 0) {}\n\n  // NOTE(chokobole): |GraphEvaluator| is already initialized with 3 constant\n  // elements. See graph_evaluator.h\n  static ValueSource ZeroConstant() { return ValueSource::Constant(0); }\n  static ValueSource OneConstant() { return ValueSource::Constant(1); }\n  static ValueSource TwoConstant() { return ValueSource::Constant(2); }\n\n  static ValueSource Constant(size_t index) {\n    return ValueSource(Type::kConstant, index);\n  }\n  static ValueSource Intermediate(size_t index) {\n    return ValueSource(Type::kIntermediate, index);\n  }\n  static ValueSource Fixed(size_t column_index, size_t rotation_index) {\n    return ValueSource(Type::kFixed, column_index, rotation_index);\n  }\n  static ValueSource Advice(size_t column_index, size_t rotation_index) {\n    return ValueSource(Type::kAdvice, column_index, rotation_index);\n  }\n  static ValueSource Instance(size_t column_index, size_t rotation_index) {\n    return ValueSource(Type::kInstance, column_index, rotation_index);\n  }\n  static ValueSource Challenge(size_t index) {\n    return ValueSource(Type::kChallenge, index);\n  }\n  static ValueSource Beta() { return ValueSource(Type::kBeta); }\n  static ValueSource Gamma() { return ValueSource(Type::kGamma); }\n  static ValueSource Theta() { return ValueSource(Type::kTheta); }\n  static ValueSource Y() { return ValueSource(Type::kY); }\n  static ValueSource PreviousValue() {\n    return ValueSource(Type::kPreviousValue);\n  }\n\n  Type type() const { return type_; }\n  size_t index() const { return index_; }\n  size_t column_index() const { return column_index_; }\n  size_t rotation_index() const { return rotation_index_; }\n\n  bool IsZeroConstant() const {\n    return type_ == Type::kConstant && index_ == 0;\n  }\n  bool IsOneConstant() const { return type_ == Type::kConstant && index_ == 1; }\n  bool IsTwoConstant() const { return type_ == Type::kConstant && index_ == 2; }\n\n  // NOTE(chokobole): I am not sure why this is needed. I implemented == and <=\n  // only because I believe this is the only operator used. See\n  // https://github.com/kroma-network/halo2/blob/7d0a369/halo2_proofs/src/plonk/evaluation.rs#L36-L37\n  // and in |GraphEvaluator<F>::Evaluate()| in graph_evaluator.h.\n  bool operator==(const ValueSource& other) const {\n    if (type_ != other.type_) return false;\n    switch (type_) {\n      case Type::kConstant:\n      case Type::kIntermediate:\n      case Type::kChallenge:\n        return index_ == other.index_;\n      case Type::kFixed:\n      case Type::kAdvice:\n      case Type::kInstance:\n        return column_index_ == other.column_index_ &&\n               rotation_index_ == other.rotation_index_;\n      case Type::kBeta:\n      case Type::kGamma:\n      case Type::kTheta:\n      case Type::kY:\n      case Type::kPreviousValue:\n        return true;\n    }\n    NOTREACHED();\n    return false;\n  }\n\n  bool operator<=(const ValueSource& other) const {\n    if (type_ == other.type_) {\n      switch (type_) {\n        case Type::kConstant:\n        case Type::kIntermediate:\n        case Type::kChallenge:\n          return index_ <= other.index_;\n        case Type::kFixed:\n        case Type::kAdvice:\n        case Type::kInstance:\n          if (column_index_ == other.column_index_) {\n            return rotation_index_ <= other.rotation_index_;\n          }\n          return column_index_ < other.column_index_;\n        case Type::kBeta:\n        case Type::kGamma:\n        case Type::kTheta:\n        case Type::kY:\n        case Type::kPreviousValue:\n          return true;\n      }\n      NOTREACHED();\n      return false;\n    }\n    return type_ < other.type_;\n  }\n\n  template <typename Evals, typename F>\n  const F& Get(const EvaluationInput<Evals>& data,\n               const std::vector<F>& constants, const F& previous_value) const {\n    switch (type_) {\n      case Type::kConstant:\n        return constants[index_];\n      case Type::kIntermediate:\n        return data.intermediates()[index_];\n      case Type::kChallenge:\n        return data.table().challenges()[index_];\n      case Type::kFixed:\n        return data.table()\n            .GetFixedColumns()[column_index_]\n                              [data.rotations()[rotation_index_]];\n      case Type::kAdvice:\n        return data.table()\n            .GetAdviceColumns()[column_index_]\n                               [data.rotations()[rotation_index_]];\n      case Type::kInstance:\n        return data.table()\n            .GetInstanceColumns()[column_index_]\n                                 [data.rotations()[rotation_index_]];\n      case Type::kBeta:\n        return data.beta();\n      case Type::kGamma:\n        return data.gamma();\n      case Type::kTheta:\n        return data.theta();\n      case Type::kY:\n        return data.y();\n      case Type::kPreviousValue:\n        return previous_value;\n    }\n    NOTREACHED();\n    return previous_value;\n  }\n\n  std::string ToString() const;\n\n private:\n  explicit ValueSource(Type type) : type_(type) {}\n  ValueSource(Type type, size_t index) : type_(type), index_(index) {}\n  ValueSource(Type type, size_t column_index, size_t rotation_index)\n      : type_(type),\n        column_index_(column_index),\n        rotation_index_(rotation_index) {}\n\n  Type type_;\n  union {\n    size_t index_;\n    struct {\n      size_t column_index_;\n      size_t rotation_index_;\n    };\n  };\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_VANISHING_VALUE_SOURCE_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/vanishing/value_source_unittest.cc",
    "content": "#include \"tachyon/zk/plonk/vanishing/value_source.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/random.h\"\n\nnamespace tachyon::zk::plonk {\n\n#define TEST_CONSTANTS(name, index)                                       \\\n  size_t value = base::Uniform(base::Range<size_t>::From(index + 1));     \\\n                                                                          \\\n  EXPECT_TRUE(ValueSource::Constant(index).Is##name##Constant());         \\\n  EXPECT_FALSE(ValueSource::Constant(value).Is##name##Constant());        \\\n  EXPECT_FALSE(ValueSource::Intermediate(index).Is##name##Constant());    \\\n  EXPECT_FALSE(ValueSource::Fixed(index, index).Is##name##Constant());    \\\n  EXPECT_FALSE(ValueSource::Advice(index, index).Is##name##Constant());   \\\n  EXPECT_FALSE(ValueSource::Instance(index, index).Is##name##Constant()); \\\n  EXPECT_FALSE(ValueSource::Challenge(index).Is##name##Constant());       \\\n  EXPECT_FALSE(ValueSource::Beta().Is##name##Constant());                 \\\n  EXPECT_FALSE(ValueSource::Gamma().Is##name##Constant());                \\\n  EXPECT_FALSE(ValueSource::Theta().Is##name##Constant());                \\\n  EXPECT_FALSE(ValueSource::Y().Is##name##Constant());                    \\\n  EXPECT_FALSE(ValueSource::PreviousValue().Is##name##Constant())\n\nTEST(ValueSourceTest, IsZeroConstant) {\n  TEST_CONSTANTS(Zero, 0);\n  EXPECT_TRUE(ValueSource::ZeroConstant().IsZeroConstant());\n  EXPECT_FALSE(ValueSource::OneConstant().IsZeroConstant());\n  EXPECT_FALSE(ValueSource::TwoConstant().IsZeroConstant());\n}\n\nTEST(ValueSourceTest, IsOneConstant) {\n  TEST_CONSTANTS(One, 1);\n  EXPECT_FALSE(ValueSource::ZeroConstant().IsOneConstant());\n  EXPECT_TRUE(ValueSource::OneConstant().IsOneConstant());\n  EXPECT_FALSE(ValueSource::TwoConstant().IsOneConstant());\n}\n\nTEST(ValueSourceTest, IsTwoConstant) {\n  TEST_CONSTANTS(Two, 2);\n  EXPECT_FALSE(ValueSource::ZeroConstant().IsTwoConstant());\n  EXPECT_FALSE(ValueSource::OneConstant().IsTwoConstant());\n  EXPECT_TRUE(ValueSource::TwoConstant().IsTwoConstant());\n}\n\n#undef TEST_CONSTANTS\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/vanishing/vanishing_argument.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_VANISHING_VANISHING_ARGUMENT_H_\n#define TACHYON_ZK_PLONK_VANISHING_VANISHING_ARGUMENT_H_\n\n#include <stdint.h>\n\n#include <memory>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/zk/base/entities/prover_base.h\"\n#include \"tachyon/zk/lookup/evaluator.h\"\n#include \"tachyon/zk/plonk/constraint_system/constraint_system.h\"\n#include \"tachyon/zk/plonk/keys/proving_key_forward.h\"\n#include \"tachyon/zk/plonk/permutation/permutation_evaluator.h\"\n#include \"tachyon/zk/plonk/vanishing/circuit_polynomial_builder.h\"\n#include \"tachyon/zk/plonk/vanishing/custom_gate_evaluator.h\"\n#include \"tachyon/zk/shuffle/evaluator.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename PS>\nclass VanishingArgument {\n public:\n  constexpr static halo2::Vendor kVendor = PS::kVendor;\n  constexpr static lookup::Type kLookupType = PS::kLookupType;\n\n  using PCS = typename PS::PCS;\n  using F = typename PCS::Field;\n  using Poly = typename PCS::Poly;\n  using Evals = typename PCS::Evals;\n  using ExtendedEvals = typename PCS::ExtendedEvals;\n  using LookupProver = lookup::Prover<kLookupType, Poly, Evals>;\n\n  using EvalsOrExtendedEvals =\n      std::conditional_t<kVendor == halo2::Vendor::kPSE, ExtendedEvals, Evals>;\n\n  VanishingArgument() = default;\n\n  static VanishingArgument Create(\n      const ConstraintSystem<F>& constraint_system) {\n    VanishingArgument evaluator;\n    evaluator.custom_gate_evaluator_.Construct(constraint_system.gates());\n    evaluator.lookup_evaluator_.Construct(constraint_system.lookups());\n    evaluator.shuffle_evaluator_.Construct(constraint_system.shuffles());\n    return evaluator;\n  }\n\n  ExtendedEvals BuildExtendedCircuitColumn(\n      ProverBase<PCS>* prover, const ProvingKey<PS>& proving_key,\n      const std::vector<MultiPhaseRefTable<Poly>>& poly_tables, const F& theta,\n      const F& beta, const F& gamma, const F& y, const F& zeta,\n      const std::vector<PermutationProver<Poly, Evals>>& permutation_provers,\n      const std::vector<LookupProver>& lookup_provers,\n      const std::vector<shuffle::Prover<Poly, Evals>>& shuffle_provers) {\n    size_t cs_degree =\n        proving_key.verifying_key().constraint_system().ComputeDegree();\n\n    CircuitPolynomialBuilder<PS> builder = CircuitPolynomialBuilder<PS>::Create(\n        prover->domain(), prover->extended_domain(), prover->pcs().N(),\n        prover->GetLastRow(), cs_degree, poly_tables, theta, beta, gamma, y,\n        zeta, proving_key, permutation_provers, lookup_provers,\n        shuffle_provers);\n\n    return builder.BuildExtendedCircuitColumn(\n        custom_gate_evaluator_, permutation_evaluator_, lookup_evaluator_,\n        shuffle_evaluator_);\n  }\n\n private:\n  CustomGateEvaluator<EvalsOrExtendedEvals> custom_gate_evaluator_;\n  PermutationEvaluator<EvalsOrExtendedEvals> permutation_evaluator_;\n  lookup::Evaluator<kLookupType, EvalsOrExtendedEvals> lookup_evaluator_;\n  shuffle::Evaluator<EvalsOrExtendedEvals> shuffle_evaluator_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_VANISHING_VANISHING_ARGUMENT_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/vanishing/vanishing_prover.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_VANISHING_VANISHING_PROVER_H_\n#define TACHYON_ZK_PLONK_VANISHING_VANISHING_PROVER_H_\n\n#include <vector>\n\n#include \"absl/types/span.h\"\n\n#include \"tachyon/crypto/commitments/polynomial_openings.h\"\n#include \"tachyon/zk/base/blinded_polynomial.h\"\n#include \"tachyon/zk/base/entities/prover_base.h\"\n#include \"tachyon/zk/lookup/prover.h\"\n#include \"tachyon/zk/plonk/base/multi_phase_ref_table.h\"\n#include \"tachyon/zk/plonk/keys/proving_key.h\"\n#include \"tachyon/zk/plonk/permutation/permutation_prover.h\"\n#include \"tachyon/zk/shuffle/prover.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename Poly, typename Evals, typename ExtendedPoly,\n          typename ExtendedEvals>\nclass VanishingProver {\n public:\n  using F = typename Poly::Field;\n\n  template <typename PCS>\n  void CreateRandomPoly(ProverBase<PCS>* prover);\n\n  constexpr static size_t GetNumRandomPolyCommitment() { return 1; }\n\n  template <typename PCS>\n  void CommitRandomPoly(ProverBase<PCS>* prover, size_t& commit_idx) const;\n\n  template <typename PCS, typename PS>\n  void CreateHEvals(\n      ProverBase<PCS>* prover, const ProvingKey<PS>& proving_key,\n      const std::vector<MultiPhaseRefTable<Poly>>& tables, const F& theta,\n      const F& beta, const F& gamma, const F& y,\n      const std::vector<PermutationProver<Poly, Evals>>& permutation_provers,\n      const std::vector<lookup::Prover<PS::kLookupType, Poly, Evals>>&\n          lookup_provers,\n      const std::vector<shuffle::Prover<Poly, Evals>>& shuffle_provers);\n\n  template <typename PCS>\n  void CreateFinalHPoly(ProverBase<PCS>* prover,\n                        const ConstraintSystem<F>& constraint_system);\n\n  template <typename F>\n  static size_t GetNumFinalHPolyCommitment(\n      const ConstraintSystem<F>& constraint_system) {\n    return constraint_system.ComputeDegree() - 1;\n  }\n\n  template <typename PCS>\n  void CommitFinalHPoly(ProverBase<PCS>* prover,\n                        const ConstraintSystem<F>& constraint_system,\n                        size_t& commit_idx);\n\n  template <typename PCS>\n  void BatchEvaluate(ProverBase<PCS>* prover,\n                     const ConstraintSystem<F>& constraint_system,\n                     const std::vector<MultiPhaseRefTable<Poly>>& tables,\n                     const F& x, const F& x_n);\n\n  template <typename PCS, typename Domain>\n  static void OpenAdviceInstanceColumns(\n      const Domain* domain, const ConstraintSystem<F>& constraint_system,\n      const MultiPhaseRefTable<Poly>& tables, const F& x,\n      std::vector<crypto::PolynomialOpening<Poly>>& openings);\n\n  template <typename Domain>\n  static void OpenFixedColumns(\n      const Domain* domain, const ConstraintSystem<F>& constraint_system,\n      const MultiPhaseRefTable<Poly>& tables, const F& x,\n      std::vector<crypto::PolynomialOpening<Poly>>& openings);\n\n  void Open(const F& x,\n            std::vector<crypto::PolynomialOpening<Poly>>& openings) const;\n\n private:\n  template <typename PCS, ColumnType C>\n  static void EvaluateColumns(ProverBase<PCS>* prover,\n                              const absl::Span<const Poly> polys,\n                              const std::vector<QueryData<C>>& queries,\n                              const F& x);\n\n  template <typename Domain, ColumnType C>\n  static void OpenColumns(\n      const Domain* domain, const absl::Span<const Poly> polys,\n      const std::vector<QueryData<C>>& queries, const F& x,\n      std::vector<crypto::PolynomialOpening<Poly>>& openings);\n\n  BlindedPolynomial<Poly, Evals> random_poly_;\n  ExtendedEvals h_evals_;\n  ExtendedPoly h_poly_;\n  Poly combined_h_poly_;\n  std::vector<F> h_blinds_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#include \"tachyon/zk/plonk/vanishing/vanishing_prover_impl.h\"\n\n#endif  // TACHYON_ZK_PLONK_VANISHING_VANISHING_PROVER_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/vanishing/vanishing_prover_impl.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_VANISHING_VANISHING_PROVER_IMPL_H_\n#define TACHYON_ZK_PLONK_VANISHING_VANISHING_PROVER_IMPL_H_\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/parallelize.h\"\n#include \"tachyon/base/profiler.h\"\n#include \"tachyon/math/base/parallelize_threshold.h\"\n#include \"tachyon/zk/plonk/vanishing/vanishing_argument.h\"\n#include \"tachyon/zk/plonk/vanishing/vanishing_prover.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename Poly, typename Evals, typename ExtendedPoly,\n          typename ExtendedEvals>\ntemplate <typename PCS>\nvoid VanishingProver<Poly, Evals, ExtendedPoly,\n                     ExtendedEvals>::CreateRandomPoly(ProverBase<PCS>* prover) {\n  TRACE_EVENT(\"ProofGeneration\", \"Plonk::Vanishing::Prover::CreateRandomPoly\");\n  // Sample a random polynomial of degree n - 1.\n  // TODO(TomTaehoonKim): Figure out why it is named |random_poly|.\n  // See\n  // https://github.com/kroma-network/halo2/blob/7d0a369/halo2_proofs/src/plonk/vanishing/prover.rs#L52-L54\n  Poly random_poly = Poly::One();\n\n  // Sample a random blinding factor.\n  // TODO(TomTaehoonKim): Figure out why it is named |random_blind|.\n  // See\n  // https://github.com/kroma-network/halo2/blob/7d0a369/halo2_proofs/src/plonk/vanishing/prover.rs#L55-L56\n  F random_blind = F::Zero();\n\n  random_poly_ = {std::move(random_poly), std::move(random_blind)};\n}\n\ntemplate <typename Poly, typename Evals, typename ExtendedPoly,\n          typename ExtendedEvals>\ntemplate <typename PCS>\nvoid VanishingProver<Poly, Evals, ExtendedPoly,\n                     ExtendedEvals>::CommitRandomPoly(ProverBase<PCS>* prover,\n                                                      size_t& commit_idx)\n    const {\n  TRACE_EVENT(\"ProofGeneration\", \"Plonk::Vanishing::Prover::CommitRandomPoly\");\n  if constexpr (PCS::kSupportsBatchMode) {\n    prover->BatchCommitAt(random_poly_.poly(), commit_idx++);\n  } else {\n    prover->CommitAndWriteToProof(random_poly_.poly());\n  }\n}\n\ntemplate <typename Poly, typename Evals, typename ExtendedPoly,\n          typename ExtendedEvals>\ntemplate <typename PCS, typename PS>\nvoid VanishingProver<Poly, Evals, ExtendedPoly, ExtendedEvals>::CreateHEvals(\n    ProverBase<PCS>* prover, const ProvingKey<PS>& proving_key,\n    const std::vector<MultiPhaseRefTable<Poly>>& tables, const F& theta,\n    const F& beta, const F& gamma, const F& y,\n    const std::vector<PermutationProver<Poly, Evals>>& permutation_provers,\n    const std::vector<lookup::Prover<PS::kLookupType, Poly, Evals>>&\n        lookup_provers,\n    const std::vector<shuffle::Prover<Poly, Evals>>& shuffle_provers) {\n  TRACE_EVENT(\"ProofGeneration\", \"Plonk::Vanishing::Prover::CreateHEvals\");\n  VanishingArgument<PS> vanishing_argument = VanishingArgument<PS>::Create(\n      proving_key.verifying_key().constraint_system());\n  F zeta = GetHalo2Zeta<F>();\n  h_evals_ = vanishing_argument.BuildExtendedCircuitColumn(\n      prover, proving_key, tables, theta, beta, gamma, y, zeta,\n      permutation_provers, lookup_provers, shuffle_provers);\n}\n\ntemplate <typename Poly, typename Evals, typename ExtendedPoly,\n          typename ExtendedEvals>\ntemplate <typename PCS>\nvoid VanishingProver<Poly, Evals, ExtendedPoly,\n                     ExtendedEvals>::CreateFinalHPoly(ProverBase<PCS>* prover,\n                                                      const ConstraintSystem<F>&\n                                                          constraint_system) {\n  TRACE_EVENT(\"ProofGeneration\", \"Plonk::Vanishing::Prover::CreateFinalHPoly\");\n  // Divide by t(X) = Xⁿ - 1.\n  DivideByVanishingPolyInPlace(h_evals_, prover->extended_domain(),\n                               prover->domain());\n\n  // Obtain final h(X) polynomial\n  h_poly_ = ExtendedToCoeff(std::move(h_evals_), prover->extended_domain());\n\n  // FIXME(TomTaehoonKim): Remove this if possible.\n  const size_t quotient_poly_degree = constraint_system.ComputeDegree() - 1;\n  h_blinds_ = base::CreateVector(quotient_poly_degree, [prover]() {\n    return prover->blinder().Generate();\n  });\n}\n\ntemplate <typename Poly, typename Evals, typename ExtendedPoly,\n          typename ExtendedEvals>\ntemplate <typename PCS>\nvoid VanishingProver<Poly, Evals, ExtendedPoly,\n                     ExtendedEvals>::CommitFinalHPoly(ProverBase<PCS>* prover,\n                                                      const ConstraintSystem<F>&\n                                                          constraint_system,\n                                                      size_t& commit_idx) {\n  TRACE_EVENT(\"ProofGeneration\", \"Plonk::Vanishing::Prover::CommitFinalHPoly\");\n  // Truncate it to match the size of the quotient polynomial; the\n  // evaluation domain might be slightly larger than necessary because\n  // it always lies on a power-of-two boundary.\n  std::vector<F>& h_coeffs = h_poly_.coefficients().coefficients();\n  const size_t quotient_poly_degree = constraint_system.ComputeDegree() - 1;\n  size_t n = prover->pcs().N();\n  h_coeffs.resize(n * quotient_poly_degree, F::Zero());\n\n  // Compute commitments to each h(X) piece\n  if constexpr (PCS::kSupportsBatchMode) {\n    base::ParallelizeByChunkSize(\n        h_coeffs, n,\n        [commit_idx, prover](absl::Span<const F> h_piece, size_t chunk_index) {\n          prover->BatchCommitAt(h_piece, commit_idx + chunk_index);\n        });\n    commit_idx += quotient_poly_degree;\n  } else {\n    using Commitment = typename PCS::Commitment;\n    std::vector<Commitment> commitments = base::ParallelizeMapByChunkSize(\n        h_coeffs, n, [prover](absl::Span<const F> h_piece) {\n          return prover->Commit(h_piece);\n        });\n    for (const Commitment& commitment : commitments) {\n      CHECK(prover->GetWriter()->WriteToProof(commitment));\n    }\n  }\n}\n\n// static\ntemplate <typename Poly, typename Evals, typename ExtendedPoly,\n          typename ExtendedEvals>\ntemplate <typename PCS, ColumnType C>\nvoid VanishingProver<Poly, Evals, ExtendedPoly, ExtendedEvals>::EvaluateColumns(\n    ProverBase<PCS>* prover, const absl::Span<const Poly> polys,\n    const std::vector<QueryData<C>>& queries, const F& x) {\n  TRACE_EVENT(\"Utils\", \"EvaluateColumns\");\n  for (const QueryData<C>& query : queries) {\n    const Poly& poly = polys[query.column().index()];\n    prover->EvaluateAndWriteToProof(\n        poly, query.rotation().RotateOmega(prover->domain(), x));\n  }\n}\n\ntemplate <typename Poly, typename Evals, typename ExtendedPoly,\n          typename ExtendedEvals>\ntemplate <typename PCS>\nvoid VanishingProver<Poly, Evals, ExtendedPoly, ExtendedEvals>::BatchEvaluate(\n    ProverBase<PCS>* prover, const ConstraintSystem<F>& constraint_system,\n    const std::vector<MultiPhaseRefTable<Poly>>& tables, const F& x,\n    const F& x_n) {\n  using Coefficients = typename Poly::Coefficients;\n\n  TRACE_EVENT(\"ProofGeneration\", \"Plonk::Vanishing::Prover::BatchEvaluate\");\n\n  size_t num_circuits = tables.size();\n  for (size_t i = 0; i < num_circuits; ++i) {\n    if constexpr (PCS::kQueryInstance) {\n      EvaluateColumns(prover, tables[i].GetInstanceColumns(),\n                      constraint_system.instance_queries(), x);\n    }\n  }\n\n  for (size_t i = 0; i < num_circuits; ++i) {\n    EvaluateColumns(prover, tables[i].GetAdviceColumns(),\n                    constraint_system.advice_queries(), x);\n  }\n\n  EvaluateColumns(prover, tables[0].GetFixedColumns(),\n                  constraint_system.fixed_queries(), x);\n\n  size_t n = prover->pcs().N();\n  auto h_chunks = base::Chunked(h_poly_.coefficients().coefficients(), n);\n  std::vector<absl::Span<F>> h_pieces =\n      base::Map(h_chunks.begin(), h_chunks.end(),\n                [](absl::Span<F> h_piece) { return h_piece; });\n  std::vector<F> coeffs(n);\n  base::ParallelizeFill(coeffs, F::Zero(),\n                        /*threshold=*/math::ParallelizeThreshold::kFieldInit);\n  for (size_t i = h_pieces.size() - 1; i != SIZE_MAX; --i) {\n    OMP_PARALLEL_FOR(size_t j = 0; j < n; ++j) {\n      coeffs[j] *= x_n;\n      coeffs[j] += h_pieces[i][j];\n    }\n  }\n  combined_h_poly_ = Poly(Coefficients(std::move(coeffs), true));\n\n  prover->EvaluateAndWriteToProof(random_poly_.poly(), x);\n}\n\n// static\ntemplate <typename Poly, typename Evals, typename ExtendedPoly,\n          typename ExtendedEvals>\ntemplate <typename Domain, ColumnType C>\nvoid VanishingProver<Poly, Evals, ExtendedPoly, ExtendedEvals>::OpenColumns(\n    const Domain* domain, const absl::Span<const Poly> polys,\n    const std::vector<QueryData<C>>& queries, const F& x,\n    std::vector<crypto::PolynomialOpening<Poly>>& openings) {\n  TRACE_EVENT(\"Utils\", \"OpenColumns\");\n#define OPENING(poly, point) \\\n  base::Ref<const Poly>(&poly), point, poly.Evaluate(point)\n\n  for (const QueryData<C>& query : queries) {\n    const Poly& poly = polys[query.column().index()];\n    F point = query.rotation().RotateOmega(domain, x);\n    openings.emplace_back(OPENING(poly, point));\n  }\n#undef OPENING\n}\n\n// static\ntemplate <typename Poly, typename Evals, typename ExtendedPoly,\n          typename ExtendedEvals>\ntemplate <typename PCS, typename Domain>\nvoid VanishingProver<Poly, Evals, ExtendedPoly, ExtendedEvals>::\n    OpenAdviceInstanceColumns(\n        const Domain* domain, const ConstraintSystem<F>& constraint_system,\n        const MultiPhaseRefTable<Poly>& table, const F& x,\n        std::vector<crypto::PolynomialOpening<Poly>>& openings) {\n  TRACE_EVENT(\"ProofGeneration\",\n              \"Plonk::Vanishing::Prover::OpenAdviceInstanceColumns\");\n  if constexpr (PCS::kQueryInstance) {\n    OpenColumns(domain, table.GetInstanceColumns(),\n                constraint_system.instance_queries(), x, openings);\n  }\n  OpenColumns(domain, table.GetAdviceColumns(),\n              constraint_system.advice_queries(), x, openings);\n}\n\n// static\ntemplate <typename Poly, typename Evals, typename ExtendedPoly,\n          typename ExtendedEvals>\ntemplate <typename Domain>\nvoid VanishingProver<Poly, Evals, ExtendedPoly, ExtendedEvals>::\n    OpenFixedColumns(const Domain* domain,\n                     const ConstraintSystem<F>& constraint_system,\n                     const MultiPhaseRefTable<Poly>& table, const F& x,\n                     std::vector<crypto::PolynomialOpening<Poly>>& openings) {\n  TRACE_EVENT(\"ProofGeneration\", \"Plonk::Vanishing::Prover::OpenFixedColumns\");\n  OpenColumns(domain, table.GetFixedColumns(),\n              constraint_system.fixed_queries(), x, openings);\n}\n\ntemplate <typename Poly, typename Evals, typename ExtendedPoly,\n          typename ExtendedEvals>\nvoid VanishingProver<Poly, Evals, ExtendedPoly, ExtendedEvals>::Open(\n    const F& x, std::vector<crypto::PolynomialOpening<Poly>>& openings) const {\n  TRACE_EVENT(\"ProofGeneration\", \"Plonk::Vanishing::Prover::Open\");\n#define OPENING(poly, point) \\\n  base::Ref<const Poly>(&poly), point, poly.Evaluate(point)\n\n  openings.emplace_back(OPENING(combined_h_poly_, x));\n  openings.emplace_back(OPENING(random_poly_.poly(), x));\n#undef OPENING\n}\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_VANISHING_VANISHING_PROVER_IMPL_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/vanishing/vanishing_utils.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_VANISHING_VANISHING_UTILS_H_\n#define TACHYON_ZK_PLONK_VANISHING_VANISHING_UTILS_H_\n\n#include <utility>\n#include <vector>\n\n#include \"absl/types/span.h\"\n\n#include \"tachyon/base/parallelize.h\"\n#include \"tachyon/base/types/always_false.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/fr.h\"\n#include \"tachyon/zk/base/blinded_polynomial.h\"\n#include \"tachyon/zk/base/entities/prover_base.h\"\n#include \"tachyon/zk/plonk/constraint_system/gate.h\"\n#include \"tachyon/zk/plonk/halo2/config.h\"\n\nnamespace tachyon::zk::plonk {\n\n// Calculate ζ = g^((2ˢ * T) / 3).\ntemplate <typename F>\nF GetZeta() {\n  using BigInt = typename F::BigIntTy;\n  CHECK_EQ(F::Config::kTrace % BigInt(3), BigInt(0));\n  BigInt exp = F::Config::kTrace;\n  // NOTE(chokobole): The result of the exponential operation does not exceed\n  // the modulus of the scalar field.\n  exp.MulBy2ExpInPlace(F::Config::kTwoAdicity);\n  CHECK(exp /= BigInt(3));\n  return F::FromMontgomery(F::Config::kSubgroupGenerator).Pow(exp);\n}\n\n// NOTE(TomTaehoonKim): The returning value of |GetZeta()| is different from the\n// one in\n// https://github.com/privacy-scaling-explorations/halo2curves/blob/v0.6.1/src/bn256/fr.rs#L145-L151.\n// This is an ugly way to produce a same result with Halo2Curves but we will\n// remove once we don't have to match it against Halo2 any longer in the\n// future.\n// Calculate ζ = g^((2ˢ * T) / 3)².\ntemplate <typename F>\nF GetHalo2Zeta() {\n  static F cache = F::Zero();\n  if (cache.IsZero()) {\n    auto& config = halo2::Config::Get();\n    if constexpr (std::is_same_v<F, math::bn254::Fr>) {\n      if (config.vendor == halo2::Vendor::kScroll) {\n        // See\n        // https://github.com/scroll-tech/halo2curves/blob/3c268b4/src/bn256/fr.rs#L108-L114.\n        cache = GetZeta<F>();\n      } else {\n        cache = GetZeta<F>().Square();\n      }\n    } else {\n      static_assert(base::AlwaysFalse<F>);\n    }\n  }\n  return cache;\n}\n\n// This divides the polynomial (in the extended domain) by the vanishing\n// polynomial of the 2ᵏ size domain.\ntemplate <typename Domain, typename ExtendedDomain, typename ExtendedEvals>\nExtendedEvals& DivideByVanishingPolyInPlace(\n    ExtendedEvals& evals, const ExtendedDomain* extended_domain,\n    const Domain* domain) {\n  using F = typename ExtendedEvals::Field;\n\n  CHECK_EQ(evals.NumElements(), extended_domain->size());\n\n  const F zeta = GetHalo2Zeta<F>();\n\n  // Compute the evaluations of t(X) = Xⁿ - 1 in the coset evaluation domain.\n  // We don't have to compute all of them, because it will repeat.\n  const size_t kTEvaluationsSize = size_t{1}\n                                   << (extended_domain->log_size_of_group() -\n                                       domain->log_size_of_group());\n  // |coset_gen_pow_n| = w'ⁿ where w' is generator of extended domain.\n  const F coset_gen_pow_n =\n      extended_domain->group_gen().ExpPowOfTwo(domain->log_size_of_group());\n  const F zeta_pow_n = zeta.ExpPowOfTwo(domain->log_size_of_group());\n  std::vector<F> t_evaluations(kTEvaluationsSize);\n  // |t_evaluations| = [ζⁿ - 1, ζⁿ * w'ⁿ - 1, ζⁿ * w'²ⁿ - 1, ...]\n  base::Parallelize(t_evaluations, [&coset_gen_pow_n, &zeta_pow_n](\n                                       absl::Span<F> chunk, size_t chunk_offset,\n                                       size_t chunk_size) {\n    size_t start = chunk_offset * chunk_size;\n    F zeta_i = zeta_pow_n * coset_gen_pow_n.Pow(start);\n\n    // NOTE: It is not possible to have empty chunk so this is safe\n    for (size_t i = 0; i < chunk.size() - 1; ++i) {\n      chunk[i] = zeta_i - F::One();\n      zeta_i *= coset_gen_pow_n;\n    }\n    chunk.back() = std::move(zeta_i);\n    chunk.back() -= F::One();\n    CHECK(F::BatchInverseInPlaceSerial(chunk));\n  });\n\n  // Multiply the inverse to obtain the quotient polynomial in the coset\n  // evaluation domain.\n  std::vector<F>& evaluations = evals.evaluations();\n  OMP_PARALLEL_FOR(size_t i = 0; i < evaluations.size(); ++i) {\n    evaluations[i] *= t_evaluations[i % t_evaluations.size()];\n  }\n\n  return evals;\n}\n\n// Given a |poly| of coefficients  [a₀, a₁, a₂, ...], this returns\n// [a₀, ζa₁, ζ²a₂, a₃, ζa₄, ζ²a₅, a₆, ...], where ζ is a cube root of unity in\n// the multiplicative subgroup with order (p - 1), i.e. ζ³ = 1.\n//\n// |into_coset| should be set to true when moving into the coset, and false\n// when moving out. This toggles the choice of ζ.\ntemplate <typename Poly>\nvoid DistributePowersZeta(Poly& poly, bool into_coset) {\n  using F = typename Poly::Field;\n\n  F zeta = GetHalo2Zeta<F>();\n  F zeta_inv = zeta.Square();\n  F coset_powers[] = {into_coset ? zeta : zeta_inv,\n                      into_coset ? zeta_inv : zeta};\n\n  std::vector<F>& coeffs = poly.coefficients().coefficients();\n  OMP_PARALLEL_FOR(size_t i = 0; i < coeffs.size(); ++i) {\n    size_t j = i % 3;\n    if (j == 0) continue;\n    coeffs[i] *= coset_powers[j - 1];\n  }\n}\n\n// This takes us from an n-length coefficient vector into a coset of the\n// extended evaluation domain.\ntemplate <typename Poly, typename ExtendedDomain,\n          typename ExtendedEvals = typename ExtendedDomain::Evals>\nExtendedEvals CoeffToExtended(Poly&& poly,\n                              const ExtendedDomain* extended_domain) {\n  using ExtendedPoly = typename ExtendedDomain::DensePoly;\n  using Coefficients = typename ExtendedPoly::Coefficients;\n\n  DistributePowersZeta(poly, true);\n\n  ExtendedPoly extended_poly(\n      Coefficients(std::move(poly).TakeCoefficients().TakeCoefficients()));\n  return extended_domain->FFT(std::move(extended_poly));\n}\n\n// This takes us from an n-length coefficient vector into a coset of the\n// extended evaluation domain.\ntemplate <typename Poly, typename ExtendedDomain,\n          typename ExtendedEvals = typename ExtendedDomain::Evals>\nExtendedEvals CoeffToExtended(const Poly& poly,\n                              const ExtendedDomain* extended_domain) {\n  Poly poly_tmp = poly;\n  return CoeffToExtended(std::move(poly_tmp), extended_domain);\n}\n\n// This takes us from the extended evaluation domain and gets us the quotient\n// polynomial coefficients.\n//\n// This function will crash if the provided vector is not the correct length.\ntemplate <typename ExtendedEvals, typename ExtendedDomain,\n          typename ExtendedPoly = typename ExtendedDomain::DensePoly>\nExtendedPoly ExtendedToCoeff(ExtendedEvals&& evals,\n                             const ExtendedDomain* extended_domain) {\n  CHECK_EQ(evals.NumElements(), extended_domain->size());\n\n  ExtendedPoly poly = extended_domain->IFFT(std::move(evals));\n\n  // Distribute powers to move from coset; opposite from the\n  // transformation we performed earlier.\n  DistributePowersZeta(poly, false);\n\n  return poly;\n}\n\ntemplate <typename F>\nstd::vector<F> BuildExtendedColumnWithColumns(\n    const std::vector<std::vector<F>>& columns) {\n  CHECK(!columns.empty());\n  size_t cols = columns.size();\n  size_t rows = columns[0].size();\n\n  std::vector<F> flattened_transposed_columns(cols * rows);\n  OMP_PARALLEL_NESTED_FOR(size_t i = 0; i < columns.size(); ++i) {\n    for (size_t j = 0; j < rows; ++j) {\n      flattened_transposed_columns[j * cols + i] = columns[i][j];\n    }\n  }\n  return flattened_transposed_columns;\n}\n\ntemplate <typename F>\nstatic size_t GetNumVanishingEvals(size_t num_circuits,\n                                   const std::vector<Gate<F>>& gates) {\n  size_t num_polys = std::accumulate(gates.begin(), gates.end(), 0,\n                                     [](size_t acc, const Gate<F>& gate) {\n                                       return acc + gate.polys().size();\n                                     });\n  return num_circuits * num_polys;\n}\n\ntemplate <typename PCS>\nconstexpr static size_t GetNumVanishingOpenings(size_t num_circuits,\n                                                size_t num_advice_queries,\n                                                size_t num_instance_queries,\n                                                size_t num_fixed_queries) {\n  size_t ret = num_advice_queries;\n  if (PCS::kQueryInstance) {\n    ret += num_instance_queries;\n  }\n  ret *= num_circuits;\n  ret += num_fixed_queries;\n  ret += 2;\n  return ret;\n}\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_VANISHING_VANISHING_UTILS_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/vanishing/vanishing_utils_unittest.cc",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#include \"tachyon/zk/plonk/vanishing/vanishing_utils.h\"\n\n#include <vector>\n\n#include \"tachyon/math/elliptic_curves/bn/bn254/g1.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluation_domain_factory.h\"\n\nnamespace tachyon::zk::plonk {\n\nnamespace {\n\nusing F = math::bn254::Fr;\n\nclass VanishingUtilsTest : public math::FiniteFieldTest<F> {\n public:\n  constexpr static size_t N = size_t{1} << 4;\n  constexpr static size_t kMaxDegree = N - 1;\n\n  using Domain = math::UnivariateEvaluationDomain<F, kMaxDegree>;\n  using Poly = typename Domain::DensePoly;\n  using Coeffs = typename Poly::Coefficients;\n  using Evals = typename Domain::Evals;\n};\n\n}  // namespace\n\nTEST_F(VanishingUtilsTest, GetZeta) {\n  F zeta = GetZeta<F>();\n  EXPECT_EQ(zeta.Pow(3), F::One());\n  F halo2_zeta = GetHalo2Zeta<F>();\n  EXPECT_EQ(halo2_zeta.Pow(3), F::One());\n}\n\nTEST_F(VanishingUtilsTest, BuildExtendedColumnWithColumns) {\n  std::vector<std::vector<F>> columns =\n      base::CreateVector(4, [](size_t i) { return std::vector<F>(N, F(i)); });\n\n  std::vector<F> extended = BuildExtendedColumnWithColumns(columns);\n  EXPECT_EQ(extended.size(), 4 * N);\n  for (size_t i = 0; i < extended.size(); ++i) {\n    EXPECT_EQ(F(i % 4), extended[i]);\n  }\n}\n\n}  // namespace tachyon::zk::plonk\n"
  },
  {
    "path": "tachyon/zk/plonk/vanishing/vanishing_verifier.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_PLONK_VANISHING_VANISHING_VERIFIER_H_\n#define TACHYON_ZK_PLONK_VANISHING_VANISHING_VERIFIER_H_\n\n#include <memory>\n#include <vector>\n\n#include \"tachyon/crypto/commitments/polynomial_openings.h\"\n#include \"tachyon/zk/plonk/constraint_system/constraint_system.h\"\n#include \"tachyon/zk/plonk/expressions/verifying_evaluator.h\"\n#include \"tachyon/zk/plonk/vanishing/vanishing_verifier_data.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F, typename C>\nclass VanishingVerifier {\n public:\n  explicit VanishingVerifier(const VanishingVerifierData<F, C>& data)\n      : data_(data) {}\n\n  void Evaluate(const ConstraintSystem<F>& constraint_system,\n                std::vector<F>& evals) {\n    VerifyingEvaluator<F> evaluator(data_);\n    for (const Gate<F>& gate : constraint_system.gates()) {\n      for (const std::unique_ptr<Expression<F>>& poly : gate.polys()) {\n        evals.push_back(poly->Evaluate(&evaluator));\n      }\n    }\n  }\n\n  template <typename PCS, typename Poly, typename Domain>\n  void OpenAdviceInstanceColumns(\n      const Domain* domain, const F& x,\n      const ConstraintSystem<F>& constraint_system,\n      std::vector<crypto::PolynomialOpening<Poly, C>>& openings) const {\n    if constexpr (PCS::kQueryInstance) {\n      OpenColumns(domain, x, constraint_system.instance_queries(),\n                  data_.instance_commitments, data_.instance_evals, openings);\n    }\n    OpenColumns(domain, x, constraint_system.advice_queries(),\n                data_.advice_commitments, data_.advice_evals, openings);\n  }\n\n  template <typename Poly, typename Domain>\n  void OpenFixedColumns(\n      const Domain* domain, const F& x,\n      const ConstraintSystem<F>& constraint_system,\n      std::vector<crypto::PolynomialOpening<Poly, C>>& openings) const {\n    OpenColumns(domain, x, constraint_system.fixed_queries(),\n                data_.fixed_commitments, data_.fixed_evals, openings);\n  }\n\n  template <typename Poly>\n  void Open(const F& x, const F& x_n, C& h_commitment, const F& h_eval,\n            std::vector<crypto::PolynomialOpening<Poly, C>>& openings) const {\n    // TODO(chokobole): Remove |ToAffine()| since this assumes commitment is an\n    // elliptic curve point.\n    h_commitment = C::template LinearCombination</*forward=*/false>(\n                       data_.h_poly_commitments, x_n)\n                       .ToAffine();\n\n#define OPENING(commitment, point, eval) \\\n  base::Ref<const C>(&commitment), point, eval\n\n    openings.emplace_back(OPENING(h_commitment, x, h_eval));\n    openings.emplace_back(\n        OPENING(data_.random_poly_commitment, x, data_.random_eval));\n\n#undef OPENING\n  }\n\n private:\n  template <typename Poly, typename Domain, ColumnType CType>\n  static void OpenColumns(\n      const Domain* domain, const F& x,\n      const std::vector<QueryData<CType>>& queries,\n      absl::Span<const C> commitments, absl::Span<const F> evals,\n      std::vector<crypto::PolynomialOpening<Poly, C>>& openings) {\n#define OPENING(commitment, point, eval) \\\n  base::Ref<const C>(&commitment), point, eval\n\n    for (size_t i = 0; i < queries.size(); ++i) {\n      const QueryData<CType>& query = queries[i];\n      const ColumnKey<CType>& column = query.column();\n      F point = query.rotation().RotateOmega(domain, x);\n      openings.emplace_back(\n          OPENING(commitments[column.index()], point, evals[i]));\n    }\n\n#undef OPENING\n  }\n\n  const VanishingVerifierData<F, C>& data_;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_VANISHING_VANISHING_VERIFIER_H_\n"
  },
  {
    "path": "tachyon/zk/plonk/vanishing/vanishing_verifier_data.h",
    "content": "#ifndef TACHYON_ZK_PLONK_VANISHING_VANISHING_VERIFIER_DATA_H_\n#define TACHYON_ZK_PLONK_VANISHING_VANISHING_VERIFIER_DATA_H_\n\n#include \"tachyon/zk/plonk/base/multi_phase_evaluations.h\"\n\nnamespace tachyon::zk::plonk {\n\ntemplate <typename F, typename C>\nstruct VanishingVerifierData : public MultiPhaseEvaluations<F> {\n  VanishingVerifierData(absl::Span<const C> fixed_commitments,\n                        absl::Span<const C> advice_commitments,\n                        absl::Span<const C> instance_commitments,\n                        absl::Span<const F> fixed_evals,\n                        absl::Span<const F> advice_evals,\n                        absl::Span<const F> instance_evals,\n                        absl::Span<const F> challenges,\n                        absl::Span<const C> h_poly_commitments,\n                        const C& random_poly_commitment, const F& random_eval)\n      : MultiPhaseEvaluations<F>(fixed_evals, advice_evals, instance_evals,\n                                 challenges),\n        fixed_commitments(fixed_commitments),\n        advice_commitments(advice_commitments),\n        instance_commitments(instance_commitments),\n        h_poly_commitments(h_poly_commitments),\n        random_poly_commitment(random_poly_commitment),\n        random_eval(random_eval) {}\n\n  absl::Span<const C> fixed_commitments;\n  absl::Span<const C> advice_commitments;\n  absl::Span<const C> instance_commitments;\n  absl::Span<const C> h_poly_commitments;\n  const C& random_poly_commitment;\n  const F& random_eval;\n};\n\n}  // namespace tachyon::zk::plonk\n\n#endif  // TACHYON_ZK_PLONK_VANISHING_VANISHING_VERIFIER_DATA_H_\n"
  },
  {
    "path": "tachyon/zk/r1cs/constraint_system/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"circuit\",\n    hdrs = [\"circuit.h\"],\n    deps = [\":constraint_system\"],\n)\n\ntachyon_cc_library(\n    name = \"constraint_matrices\",\n    hdrs = [\"constraint_matrices.h\"],\n    deps = [\":matrix\"],\n)\n\ntachyon_cc_library(\n    name = \"constraint_system\",\n    hdrs = [\"constraint_system.h\"],\n    deps = [\n        \":constraint_matrices\",\n        \":linear_combination\",\n        \":optimization_goal\",\n        \":synthesis_mode\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/base/functional:callback\",\n        \"@com_google_absl//absl/container:btree\",\n        \"@com_google_googletest//:gtest_prod\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"linear_combination\",\n    hdrs = [\"linear_combination.h\"],\n    deps = [\n        \":term\",\n        \"//tachyon/base:sort\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/base/ranges:algorithm\",\n        \"@com_google_googletest//:gtest_prod\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"matrix\",\n    hdrs = [\"matrix.h\"],\n    deps = [\n        \"//tachyon/base/containers:container_util\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"optimization_goal\",\n    hdrs = [\"optimization_goal.h\"],\n)\n\ntachyon_cc_library(\n    name = \"qap_instance_map_result\",\n    hdrs = [\"qap_instance_map_result.h\"],\n)\n\ntachyon_cc_library(\n    name = \"qap_witness_map_result\",\n    hdrs = [\"qap_witness_map_result.h\"],\n)\n\ntachyon_cc_library(\n    name = \"quadratic_arithmetic_program\",\n    hdrs = [\"quadratic_arithmetic_program.h\"],\n    deps = [\n        \":constraint_system\",\n        \":qap_instance_map_result\",\n        \":qap_witness_map_result\",\n        \"//tachyon/base:optional\",\n        \"//tachyon/base:parallelize\",\n        \"//tachyon/base:profiler\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"synthesis_mode\",\n    hdrs = [\"synthesis_mode.h\"],\n)\n\ntachyon_cc_library(\n    name = \"term\",\n    hdrs = [\"term.h\"],\n    deps = [\":variable\"],\n)\n\ntachyon_cc_library(\n    name = \"variable\",\n    srcs = [\"variable.cc\"],\n    hdrs = [\"variable.h\"],\n    deps = [\n        \"//tachyon:export\",\n        \"//tachyon/base:logging\",\n        \"@com_google_absl//absl/strings\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"r1cs_unittests\",\n    srcs = [\n        \"constraint_system_unittest.cc\",\n        \"linear_combination_unittest.cc\",\n        \"variable_unittest.cc\",\n    ],\n    deps = [\n        \":constraint_system\",\n        \"//tachyon/base:random\",\n        \"//tachyon/math/finite_fields/test:finite_field_test\",\n        \"//tachyon/math/finite_fields/test:gf7\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/zk/r1cs/constraint_system/circuit.h",
    "content": "#ifndef TACHYON_ZK_R1CS_CONSTRAINT_SYSTEM_CIRCUIT_H_\n#define TACHYON_ZK_R1CS_CONSTRAINT_SYSTEM_CIRCUIT_H_\n\n#include \"tachyon/zk/r1cs/constraint_system/constraint_system.h\"\n\nnamespace tachyon::zk::r1cs {\n\ntemplate <typename F>\nclass Circuit {\n public:\n  virtual ~Circuit() = default;\n\n  virtual void Synthesize(ConstraintSystem<F>& constraint_system) const = 0;\n};\n\n}  // namespace tachyon::zk::r1cs\n\n#endif  // TACHYON_ZK_R1CS_CONSTRAINT_SYSTEM_CIRCUIT_H_\n"
  },
  {
    "path": "tachyon/zk/r1cs/constraint_system/constraint_matrices.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_ZK_R1CS_CONSTRAINT_SYSTEM_CONSTRAINT_MATRICES_H_\n#define TACHYON_ZK_R1CS_CONSTRAINT_SYSTEM_CONSTRAINT_MATRICES_H_\n\n#include <stddef.h>\n\n#include <string>\n\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/zk/r1cs/constraint_system/matrix.h\"\n\nnamespace tachyon::zk::r1cs {\n\n// The A, B and C matrices of a Rank-One |ConstraintSystem|.\n// Also contains metadata on the structure of the constraint system\n// and the matrices.\n// FIXME(chokobole): I want to separate solution vectors from A, B and C\n// matrices since the solution vectors are shared. This is because we can save\n// additional memory space if we adopt this trick.\ntemplate <typename F>\nstruct ConstraintMatrices {\n  // The number of variables that are \"public instances\" to the constraint\n  // system.\n  size_t num_instance_variables = 0;\n  // The number of variables that are \"private witnesses\" to the constraint\n  // system.\n  size_t num_witness_variables = 0;\n  // The number of constraints in the constraint system.\n  size_t num_constraints = 0;\n  // The number of non_zero entries in the A matrix.\n  size_t a_num_non_zero = 0;\n  // The number of non_zero entries in the B matrix.\n  size_t b_num_non_zero = 0;\n  // The number of non_zero entries in the C matrix.\n  size_t c_num_non_zero = 0;\n\n  // The A constraint matrix.\n  Matrix<F> a;\n  // The B constraint matrix.\n  Matrix<F> b;\n  // The C constraint matrix.\n  Matrix<F> c;\n\n  bool operator==(const ConstraintMatrices& other) const {\n    return num_instance_variables == other.num_instance_variables &&\n           num_witness_variables == other.num_witness_variables &&\n           num_constraints == other.num_constraints &&\n           a_num_non_zero == other.a_num_non_zero &&\n           b_num_non_zero == other.b_num_non_zero &&\n           c_num_non_zero == other.c_num_non_zero && a == other.a &&\n           b == other.b && c == other.c;\n  }\n  bool operator!=(const ConstraintMatrices& other) const {\n    return !operator==(other);\n  }\n\n  std::string ToString() const {\n    return absl::Substitute(\n        \"{ num_instance_variables: $0, num_witness_variables: $1, \"\n        \"num_constraints: $2, a_num_non_zero: $3, b_num_non_zero: $4, \"\n        \"c_num_non_zero: $5, a: $6, b: $7, c: $8\",\n        num_instance_variables, num_witness_variables, num_constraints,\n        a_num_non_zero, b_num_non_zero, c_num_non_zero, a.ToString(),\n        b.ToString(), c.ToString());\n  }\n};\n\n}  // namespace tachyon::zk::r1cs\n\n#endif  // TACHYON_ZK_R1CS_CONSTRAINT_SYSTEM_CONSTRAINT_MATRICES_H_\n"
  },
  {
    "path": "tachyon/zk/r1cs/constraint_system/constraint_system.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_ZK_R1CS_CONSTRAINT_SYSTEM_CONSTRAINT_SYSTEM_H_\n#define TACHYON_ZK_R1CS_CONSTRAINT_SYSTEM_CONSTRAINT_SYSTEM_H_\n\n#include <stddef.h>\n\n#include <optional>\n#include <utility>\n#include <vector>\n\n#include \"absl/container/btree_map.h\"\n#include \"gtest/gtest_prod.h\"\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/functional/callback.h\"\n#include \"tachyon/zk/r1cs/constraint_system/constraint_matrices.h\"\n#include \"tachyon/zk/r1cs/constraint_system/linear_combination.h\"\n#include \"tachyon/zk/r1cs/constraint_system/optimization_goal.h\"\n#include \"tachyon/zk/r1cs/constraint_system/synthesis_mode.h\"\n\nnamespace tachyon::zk::r1cs {\n\n// A Rank-One |ConstraintSystem|. Enforces constraints of the form\n// ⟨aᵢ, z⟩ ⋅ ⟨bᵢ, z⟩ = ⟨cᵢ, z⟩, where aᵢ, bᵢ, and cᵢ are linear\n// combinations over variables, and z is the concrete assignment to these\n// variables.\ntemplate <typename F>\nclass ConstraintSystem {\n public:\n  using CreateVariableCallback = base::OnceCallback<F()>;\n\n  struct TransformLCMapResult {\n    size_t num_new_witness_variables_needed;\n    std::optional<std::vector<F>> new_witness_assignments;\n\n    explicit TransformLCMapResult(size_t num_new_witness_variables_needed)\n        : num_new_witness_variables_needed(num_new_witness_variables_needed) {}\n    TransformLCMapResult(size_t num_new_witness_variables_needed,\n                         std::vector<F>&& new_witness_assignments)\n        : num_new_witness_variables_needed(num_new_witness_variables_needed),\n          new_witness_assignments(std::move(new_witness_assignments)) {}\n  };\n\n  using TransformLCMapCallback = base::RepeatingCallback<TransformLCMapResult(\n      size_t, LinearCombination<F>&)>;\n\n  ConstraintSystem() = default;\n\n  SynthesisMode mode() const { return mode_; }\n  void set_mode(SynthesisMode mode) { mode_ = mode; }\n  size_t num_instance_variables() const { return num_instance_variables_; }\n  size_t num_witness_variables() const { return num_witness_variables_; }\n  size_t num_constraints() const { return num_constraints_; }\n  size_t num_linear_combinations() const { return num_linear_combinations_; }\n  OptimizationGoal optimization_goal() const { return optimization_goal_; }\n  // Specify whether this constraint system should aim to optimize weight,\n  // number of constraints, or neither.\n  void set_optimization_goal(OptimizationGoal optimization_goal) {\n    // |set_optimization_goal()| should only be executed before any constraint\n    // or value is created.\n    CHECK_EQ(num_instance_variables_, size_t{1});\n    CHECK_EQ(num_witness_variables_, size_t{0});\n    CHECK_EQ(num_constraints_, size_t{0});\n    CHECK_EQ(num_linear_combinations_, size_t{0});\n    optimization_goal_ = optimization_goal;\n  }\n  const std::vector<F>& instance_assignments() const {\n    return instance_assignments_;\n  }\n  const std::vector<F>& witness_assignments() const {\n    return witness_assignments_;\n  }\n\n  std::vector<Cell<F>> MakeRow(const LinearCombination<F>& lc) const {\n    std::vector<Cell<F>> row;\n    for (const Term<F>& term : lc.terms()) {\n      if (!term.coefficient.IsZero()) {\n        row.push_back({term.coefficient,\n                       *term.variable.GetIndex(num_instance_variables_)});\n      }\n    }\n    return row;\n  }\n\n  // Return a variable representing the constant \"zero\" inside the constraint\n  // system.\n  Variable CreateZero() const { return Variable::Zero(); }\n\n  // Return a variable representing the constant \"one\" inside the constraint\n  // system.\n  Variable CreateOne() const { return Variable::One(); }\n\n  // Obtain a variable representing a new public instance input.\n  Variable CreateInstanceVariable(CreateVariableCallback callback) {\n    if (!mode_.IsSetup()) {\n      instance_assignments_.push_back(std::move(callback).Run());\n    }\n    return Variable::Instance(num_instance_variables_++);\n  }\n\n  // Obtain a variable representing a new private witness input.\n  Variable CreateWitnessVariable(CreateVariableCallback callback) {\n    if (!mode_.IsSetup()) {\n      witness_assignments_.push_back(std::move(callback).Run());\n    }\n    return Variable::Witness(num_witness_variables_++);\n  }\n\n  // Obtain a variable representing a linear combination.\n  Variable CreateLinearCombination(\n      const LinearCombination<F>& linear_combination) {\n    size_t index = num_linear_combinations_++;\n    lc_map_[index] = linear_combination;\n    return Variable::SymbolicLinearCombination(index);\n  }\n\n  // Obtain a variable representing a linear combination.\n  Variable CreateLinearCombination(LinearCombination<F>&& linear_combination) {\n    size_t index = num_linear_combinations_++;\n    lc_map_[index] = std::move(linear_combination);\n    return Variable::SymbolicLinearCombination(index);\n  }\n\n  // Enforce a constraint that a * b = c. It terminates when\n  // |mode_.ShouldConstructMatrices()| is false.\n  void EnforceConstraint(const LinearCombination<F>& a,\n                         const LinearCombination<F>& b,\n                         const LinearCombination<F>& c) {\n    CHECK(mode_.ShouldConstructMatrices());\n    size_t a_index = CreateLinearCombination(a).index();\n    size_t b_index = CreateLinearCombination(b).index();\n    size_t c_index = CreateLinearCombination(c).index();\n    a_constraints_.push_back(a_index);\n    b_constraints_.push_back(b_index);\n    c_constraints_.push_back(c_index);\n    ++num_constraints_;\n    // TODO(chokobole): add constraint trace\n  }\n\n  // Enforce a constraint that a * b = c. It terminates when\n  // |mode_.ShouldConstructMatrices()| is false.\n  void EnforceConstraint(LinearCombination<F>&& a, LinearCombination<F>&& b,\n                         LinearCombination<F>&& c) {\n    CHECK(mode_.ShouldConstructMatrices());\n    size_t a_index = CreateLinearCombination(std::move(a)).index();\n    size_t b_index = CreateLinearCombination(std::move(b)).index();\n    size_t c_index = CreateLinearCombination(std::move(c)).index();\n    a_constraints_.push_back(a_index);\n    b_constraints_.push_back(b_index);\n    c_constraints_.push_back(c_index);\n    ++num_constraints_;\n    // TODO(chokobole): add constraint trace\n  }\n\n  // Count the number of times each LC is used within other LCs in the\n  // constraint system\n  std::vector<size_t> ComputeLCNumTimesUsed(bool count_sinks) const {\n    std::vector<size_t> num_times_used;\n    num_times_used.resize(lc_map_.size());\n\n    for (const std::pair<const size_t, LinearCombination<F>>& entry : lc_map_) {\n      num_times_used[entry.first] += size_t{count_sinks};\n\n      // Increment the counter for each lc that this lc has a direct\n      // dependency on.\n      for (const Term<F>& term : entry.second.terms()) {\n        if (term.variable.IsSymbolicLinearCombination()) {\n          ++num_times_used[term.variable.index()];\n        }\n      }\n    }\n    return num_times_used;\n  }\n\n  // Transform the map of linear combinations.\n  // Specifically, allow the creation of additional witness assignments.\n  //\n  // This method is used as a subroutine of |InlineAllLCs| and |OutlineLCs|.\n  void TransformLCMap(TransformLCMapCallback callback) {\n    absl::btree_map<size_t, LinearCombination<F>> transformed_lc_map;\n    std::vector<size_t> num_times_used = ComputeLCNumTimesUsed(false);\n\n    // This loop goes through all the LCs in the map, starting from\n    // the early ones. The transformer function is applied to the\n    // inlined LC, where new witness variables can be created.\n    for (std::pair<const size_t, LinearCombination<F>>& entry : lc_map_) {\n      LinearCombination<F> transformed_lc;\n\n      // Inline the LC, unwrapping symbolic LCs that may constitute it,\n      // and updating them according to transformations in prior iterations.\n      for (Term<F>& term : entry.second.terms()) {\n        if (term.variable.IsSymbolicLinearCombination()) {\n          size_t lc_index = term.variable.index();\n\n          // If |term.variable| is a |SymbolicLinearCombination|, fetch the\n          // corresponding inlined LC, and substitute it in.\n          //\n          // We have the guarantee that |lc_index| must exist in\n          // |new_lc_map| since a LC can only depend on other\n          // LCs with lower indices, which we have transformed.\n          //\n          auto it = transformed_lc_map.find(lc_index);\n          LinearCombination<F>& lc = it->second;\n          transformed_lc.AppendTerms((lc * term.coefficient).TakeTerms());\n\n          // Delete linear combinations that are no longer used.\n          //\n          // Deletion is safe for both outlining and inlining:\n          // * Inlining: the LC is substituted directly into all use sites, and\n          //   so once it is fully inlined, it is redundant.\n          //\n          // * Outlining: the LC is associated with a new variable |w|, and a\n          //   new constraint of the form |lc_data * 1 = w|, where |lc_data| is\n          //   the actual data in the linear combination. Furthermore, we\n          //   replace its entry in |new_lc_map| with |(1, w)|. Once |w| is\n          //   fully inlined, then we can delete the entry from |new_lc_map|\n          //\n          if (--num_times_used[lc_index] == 0) {\n            transformed_lc_map.erase(it);\n          }\n        } else {\n          // Otherwise, it's a concrete variable and so we\n          // substitute it in directly.\n          transformed_lc.AppendTerm(std::move(term));\n        }\n      }\n      transformed_lc.Deduplicate();\n\n      // Call the transformer function.\n      TransformLCMapResult result =\n          callback.Run(num_times_used[entry.first], transformed_lc);\n\n      // Insert the transformed LC.\n      transformed_lc_map[entry.first] = std::move(transformed_lc);\n\n      // Update the witness counter.\n      num_witness_variables_ += result.num_new_witness_variables_needed;\n\n      // Supply additional witness assignments if not in the\n      // setup mode and if new witness variables are created.\n      if (!mode_.IsSetup() && result.num_new_witness_variables_needed > 0) {\n        CHECK(result.new_witness_assignments.has_value());\n        CHECK_EQ(result.new_witness_assignments->size(),\n                 result.num_new_witness_variables_needed);\n        base::Extend(witness_assignments_,\n                     std::move(*result.new_witness_assignments));\n      }\n    }\n    lc_map_ = transformed_lc_map;\n  }\n\n  // Naively inlines symbolic linear combinations into the linear\n  // combinations that use them.\n  //\n  // Useful for standard pairing-based SNARKs where addition gates are cheap.\n  // For example, in the SNARKs such as\n  // [Groth16](https://eprint.iacr.org/2016/260) and\n  // [Groth-Maller17](https://eprint.iacr.org/2017/540), addition gates\n  // do not contribute to the size of the multi-scalar multiplication, which\n  // is the dominating cost.\n  void InlineAllLCs() {\n    // Only inline when a matrix representing R1CS is needed.\n    if (!mode_.ShouldConstructMatrices()) return;\n\n    // A dummy closure is used, which means that\n    // - it does not modify the |inlined_lc|.\n    // - it does not add new witness variables.\n    TransformLCMap([](size_t, LinearCombination<F>& inlined_lc) {\n      return TransformLCMapResult(0);\n    });\n  }\n\n  // If a |SymbolicLinearCombination| is used in more than one location and has\n  // sufficient length, this method makes a new variable for that\n  // |SymbolicLinearCombination|, adds a constraint ensuring the equality of the\n  // variable and the linear combination, and then uses that variable in every\n  // location the |SymbolicLinearCombination| is used.\n  //\n  // Useful for SNARKs like [Marlin](https://eprint.iacr.org/2019/1047) or\n  // [Fractal](https://eprint.iacr.org/2019/1076), where addition gates\n  // are not cheap.\n  void OutlineLCs() {\n    // Only inline when a matrix representing R1CS is needed.\n    if (!mode_.ShouldConstructMatrices()) return;\n\n    // Store information about new witness variables created\n    // for outlining. New constraints will be added after the\n    // transformation of the LC map.\n    std::vector<LinearCombination<F>> new_witness_linear_combinations;\n    std::vector<size_t> new_witness_indices;\n\n    // It goes through all the LCs in the map, starting from\n    // the early ones, and decides whether or not to dedicate a witness\n    // variable for this LC.\n    //\n    // If true, the LC is replaced with 1 * this witness variable.\n    // Otherwise, the LC is inlined.\n    //\n    // Each iteration first updates the LC according to outlinings in prior\n    // iterations, and then sees if it should be outlined, and if so adds\n    // the outlining to the map.\n    TransformLCMap([this, &new_witness_linear_combinations,\n                    &new_witness_indices](size_t num_times_used,\n                                          LinearCombination<F>& inlined_lc) {\n      bool should_dedicate_a_witness_variable = false;\n      std::optional<size_t> new_witness_index;\n      std::vector<F> new_witness_assignments;\n\n      // Check if it is worthwhile to dedicate a witness variable.\n      size_t this_used_times = num_times_used + 1;\n      size_t this_len = inlined_lc.terms().size();\n\n      // Cost with no outlining = |this_len * this_used_times|\n      // Cost with outlining is one constraint for |(this_len) * 1 = {new\n      // variable}| and using that single new variable in each of the prior\n      // usages. This has total cost |this_used_times + this_len + 2|\n      if (this_len * this_used_times > this_used_times + this_len + 2) {\n        should_dedicate_a_witness_variable = true;\n      }\n\n      // If it is worthwhile to dedicate a witness variable,\n      if (should_dedicate_a_witness_variable) {\n        // Add a new witness (the value of the linear combination).\n        // This part follows the same logic of |new_witness_variable_|.\n        size_t witness_index = num_witness_variables_;\n        new_witness_index = witness_index;\n\n        // Compute the witness assignment.\n        if (!mode_.IsSetup()) {\n          new_witness_assignments.push_back(\n              DoEvalLinearCombination(inlined_lc));\n        }\n\n        // Add a new constraint for this new witness.\n        new_witness_linear_combinations.push_back(std::move(inlined_lc));\n        new_witness_indices.push_back(witness_index);\n\n        // Replace the linear combination with (1 * this new witness).\n        inlined_lc =\n            LinearCombination<F>({Term<F>(Variable::Witness(witness_index))});\n      }\n      // Otherwise, the LC remains unchanged.\n\n      // Return information about new witness variables.\n      if (new_witness_index.has_value()) {\n        return TransformLCMapResult(1, std::move(new_witness_assignments));\n      } else {\n        return TransformLCMapResult(0);\n      }\n    });\n\n    for (size_t i = 0; i < new_witness_linear_combinations.size(); ++i) {\n      EnforceConstraint(new_witness_linear_combinations[i],\n                        LinearCombination<F>({Term<F>(CreateOne())}),\n                        LinearCombination<F>({Term<F>(\n                            Variable::Witness(new_witness_indices[i]))}));\n    }\n  }\n\n  // Finalize the constraint system (either by outlining or inlining,\n  // if an optimization goal is set).\n  void Finalize() {\n    switch (optimization_goal_) {\n      case OptimizationGoal::kConstraints:\n        InlineAllLCs();\n        return;\n      case OptimizationGoal::kWeight:\n        OutlineLCs();\n        return;\n    }\n    NOTREACHED();\n  }\n\n  // This step must be called after constraint generation has completed, and\n  // after all symbolic LCs have been inlined into the places that they\n  // are used.\n  std::optional<ConstraintMatrices<F>> ToMatrices() const {\n    if (!mode_.ShouldConstructMatrices()) return std::nullopt;\n    Matrix<F> a = MakeMatrix(a_constraints_);\n    Matrix<F> b = MakeMatrix(b_constraints_);\n    Matrix<F> c = MakeMatrix(c_constraints_);\n    ConstraintMatrices<F> matrices{num_instance_variables_,\n                                   num_witness_variables_,\n                                   num_constraints_,\n                                   a.CountNonZero(),\n                                   b.CountNonZero(),\n                                   c.CountNonZero(),\n                                   std::move(a),\n                                   std::move(b),\n                                   std::move(c)};\n    return matrices;\n  }\n\n  // Evaluate the linear combination corresponding to the |index|.\n  F EvalLinearCombination(size_t index) const {\n    auto it = lc_map_.find(index);\n    CHECK(it != lc_map_.end());\n    const LinearCombination<F>& lc = it->second;\n    return DoEvalLinearCombination(lc);\n  }\n\n  // Obtain the assignment corresponding to the |v|.\n  F GetAssignedValue(const Variable& v) const {\n    switch (v.type()) {\n      case Variable::Type::kZero:\n        return F::Zero();\n      case Variable::Type::kOne:\n        return F::One();\n      case Variable::Type::kInstance:\n        return instance_assignments_[v.index()];\n      case Variable::Type::kWitness:\n        return witness_assignments_[v.index()];\n      case Variable::Type::kSymbolicLinearCombination: {\n        auto it = lc_assignment_cache_.find(v.index());\n        if (it == lc_assignment_cache_.end()) {\n          it = lc_assignment_cache_\n                   .insert({v.index(), EvalLinearCombination(v.index())})\n                   .first;\n        }\n        return it->second;\n      }\n    }\n    NOTREACHED();\n    return F::Zero();\n  }\n\n  // Return true if |*this| is satisfied.\n  // Return false if |*this| is not satisfied or |mode_.IsSetup()| is true.\n  bool IsSatisfied() const {\n    if (mode_.IsSetup()) {\n      LOG(ERROR) << \"Assignments are missing\";\n      return false;\n    }\n    for (size_t i = 0; i < num_constraints_; ++i) {\n      F a = EvalLinearCombination(a_constraints_[i]);\n      F b = EvalLinearCombination(b_constraints_[i]);\n      F c = EvalLinearCombination(c_constraints_[i]);\n      if (a * b != c) return false;\n    }\n    return true;\n  }\n\n private:\n  FRIEND_TEST(ConstraintSystemTest, GetAssignedValue);\n\n  Matrix<F> MakeMatrix(const std::vector<size_t>& constraints) const {\n    return Matrix<F>(base::Map(constraints, [this](size_t index) {\n      auto it = lc_map_.find(index);\n      return MakeRow(it->second);\n    }));\n  }\n\n  F DoEvalLinearCombination(const LinearCombination<F>& lc) const {\n    return std::accumulate(lc.terms().begin(), lc.terms().end(), F::Zero(),\n                           [this](F& acc, const Term<F>& term) {\n                             return acc += term.coefficient *\n                                           GetAssignedValue(term.variable);\n                           });\n  }\n\n  // The mode in which the constraint system is operating. |this| can either\n  // be in setup mode or in proving mode. If we are in proving mode, then\n  // we have the additional option of whether or not to construct the A, B,\n  // and C matrices of the constraint system (see below).\n  SynthesisMode mode_ = SynthesisMode::Prove(true);\n  // The number of variables that are \"public inputs\" to the\n  // constraint system.\n  size_t num_instance_variables_ = 1;\n  // The number of variables that are \"private inputs\" to the constraint system.\n  size_t num_witness_variables_ = 0;\n  // The number of constraints in the constraint system.\n  size_t num_constraints_ = 0;\n  // The number of linear combinations\n  size_t num_linear_combinations_ = 0;\n\n  // The parameter we aim to minimize in this constraint system (either the\n  // number of constraints or their total weight).\n  OptimizationGoal optimization_goal_ = OptimizationGoal::kConstraints;\n\n  // Assignments to the public input variables. This is empty in setup mode.\n  std::vector<F> instance_assignments_ = {F::One()};\n  // Assignments to the private input variables. This is empty in setup mode.\n  std::vector<F> witness_assignments_;\n\n  absl::btree_map<size_t, LinearCombination<F>> lc_map_;\n\n  std::vector<size_t> a_constraints_;\n  std::vector<size_t> b_constraints_;\n  std::vector<size_t> c_constraints_;\n\n  mutable absl::btree_map<size_t, F> lc_assignment_cache_;\n};\n\n}  // namespace tachyon::zk::r1cs\n\n#endif  // TACHYON_ZK_R1CS_CONSTRAINT_SYSTEM_CONSTRAINT_SYSTEM_H_\n"
  },
  {
    "path": "tachyon/zk/r1cs/constraint_system/constraint_system_unittest.cc",
    "content": "#include \"tachyon/zk/r1cs/constraint_system/constraint_system.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n\nnamespace tachyon::zk::r1cs {\n\nnamespace {\n\nusing F = math::GF7;\n\nclass ConstraintSystemTest : public math::FiniteFieldTest<F> {};\n\n}  // namespace\n\nTEST_F(ConstraintSystemTest, MakeRow) {\n  ConstraintSystem<F> constraint_system;\n  Variable v0 = constraint_system.CreateInstanceVariable([]() { return F(1); });\n  Variable v1 = constraint_system.CreateInstanceVariable([]() { return F(2); });\n  Variable v2 = constraint_system.CreateWitnessVariable([]() { return F(3); });\n  Variable v3 = constraint_system.CreateWitnessVariable([]() { return F(4); });\n  Variable v4 = constraint_system.CreateWitnessVariable([]() { return F(5); });\n  LinearCombination<F> lc({\n      {F(1), v0},\n      {F(2), v1},\n      {F(3), v2},\n      {F(4), v3},\n      {F(5), v4},\n  });\n  std::vector<Cell<F>> expected_row({\n      {F(1), 1},\n      {F(2), 2},\n      {F(3), 3},\n      {F(4), 4},\n      {F(5), 5},\n  });\n  EXPECT_EQ(constraint_system.MakeRow(lc), expected_row);\n}\n\nTEST_F(ConstraintSystemTest, CreateZero) {\n  ConstraintSystem<F> constraint_system;\n  EXPECT_EQ(constraint_system.CreateZero(), Variable::Zero());\n}\n\nTEST_F(ConstraintSystemTest, CreateOne) {\n  ConstraintSystem<F> constraint_system;\n  EXPECT_EQ(constraint_system.CreateOne(), Variable::One());\n}\n\nTEST_F(ConstraintSystemTest, CreateInstanceVariable) {\n  for (size_t i = 0; i < 2; ++i) {\n    ConstraintSystem<F> constraint_system;\n    if (i == 0) {\n      constraint_system.set_mode(SynthesisMode::Setup());\n    } else if (i == 1) {\n      constraint_system.set_mode(SynthesisMode::Prove(true));\n    } else {\n      constraint_system.set_mode(SynthesisMode::Prove(false));\n    }\n    EXPECT_EQ(constraint_system.num_instance_variables(), 1);\n    Variable variable =\n        constraint_system.CreateInstanceVariable([]() { return F(3); });\n    EXPECT_EQ(variable, Variable::Instance(1));\n    EXPECT_EQ(constraint_system.num_instance_variables(), 2);\n    std::vector<F> expected_instance_assignments;\n    if (i == 0) {\n      expected_instance_assignments = {F(1)};\n    } else {\n      expected_instance_assignments = {F(1), F(3)};\n    }\n    EXPECT_EQ(constraint_system.instance_assignments(),\n              expected_instance_assignments);\n  }\n}\n\nTEST_F(ConstraintSystemTest, CreateWitnessVariable) {\n  for (size_t i = 0; i < 3; ++i) {\n    ConstraintSystem<F> constraint_system;\n    if (i == 0) {\n      constraint_system.set_mode(SynthesisMode::Setup());\n    } else if (i == 1) {\n      constraint_system.set_mode(SynthesisMode::Prove(true));\n    } else {\n      constraint_system.set_mode(SynthesisMode::Prove(false));\n    }\n    EXPECT_EQ(constraint_system.num_witness_variables(), 0);\n    Variable variable =\n        constraint_system.CreateWitnessVariable([]() { return F(3); });\n    EXPECT_EQ(variable, Variable::Witness(0));\n    EXPECT_EQ(constraint_system.num_witness_variables(), 1);\n    if (i == 0) {\n      EXPECT_TRUE(constraint_system.witness_assignments().empty());\n    } else {\n      std::vector<F> expected_witness_assignments = {F(3)};\n      EXPECT_EQ(constraint_system.witness_assignments(),\n                expected_witness_assignments);\n    }\n  }\n}\n\nTEST_F(ConstraintSystemTest, CreateLinearCombination) {\n  ConstraintSystem<F> constraint_system;\n  EXPECT_EQ(constraint_system.num_linear_combinations(), 0);\n  EXPECT_EQ(constraint_system.CreateLinearCombination(LinearCombination<F>()),\n            Variable::SymbolicLinearCombination(0));\n  EXPECT_EQ(constraint_system.num_linear_combinations(), 1);\n}\n\nTEST_F(ConstraintSystemTest, EnforceConstraint) {\n  for (size_t i = 0; i < 3; ++i) {\n    ConstraintSystem<F> constraint_system;\n    if (i == 0) {\n      constraint_system.set_mode(SynthesisMode::Setup());\n    } else if (i == 1) {\n      constraint_system.set_mode(SynthesisMode::Prove(true));\n    } else {\n      constraint_system.set_mode(SynthesisMode::Prove(false));\n    }\n    EXPECT_EQ(constraint_system.num_constraints(), 0);\n    EXPECT_EQ(constraint_system.num_linear_combinations(), 0);\n    if (i < 2) {\n      constraint_system.EnforceConstraint(LinearCombination<F>(),\n                                          LinearCombination<F>(),\n                                          LinearCombination<F>());\n      EXPECT_EQ(constraint_system.num_constraints(), 1);\n      EXPECT_EQ(constraint_system.num_linear_combinations(), 3);\n    } else {\n      EXPECT_DEATH(constraint_system.EnforceConstraint(LinearCombination<F>(),\n                                                       LinearCombination<F>(),\n                                                       LinearCombination<F>()),\n                   \"\");\n    }\n  }\n}\n\nTEST_F(ConstraintSystemTest, ComputeLCNumTimesUsed) {\n  ConstraintSystem<F> constraint_system;\n  Variable lc_variable =\n      constraint_system.CreateLinearCombination(LinearCombination<F>());\n  LinearCombination<F> lc;\n  lc += Term<F>(lc_variable);\n  lc += Term<F>(lc_variable);\n  Variable lc_variable2 = constraint_system.CreateLinearCombination(lc);\n  LinearCombination<F> lc2;\n  lc2 += Term<F>(lc_variable);\n  lc2 += Term<F>(lc_variable2);\n  constraint_system.CreateLinearCombination(lc2);\n  std::vector<size_t> expected_lc_times_used = {2, 1, 0};\n  EXPECT_EQ(constraint_system.ComputeLCNumTimesUsed(false),\n            expected_lc_times_used);\n  expected_lc_times_used = {3, 2, 1};\n  EXPECT_EQ(constraint_system.ComputeLCNumTimesUsed(true),\n            expected_lc_times_used);\n}\n\nTEST_F(ConstraintSystemTest, Finalize) {\n  for (size_t i = 0; i < 2; ++i) {\n    ConstraintSystem<F> constraint_system;\n    if (i == 1) {\n      constraint_system.set_optimization_goal(OptimizationGoal::kWeight);\n    }\n    // a = 1\n    Variable a =\n        constraint_system.CreateInstanceVariable([]() { return F(1); });\n    // b = 1\n    Variable b = constraint_system.CreateWitnessVariable([]() { return F(1); });\n    // c = 2\n    Variable c = constraint_system.CreateWitnessVariable([]() { return F(2); });\n    // a * 2b = c\n    constraint_system.EnforceConstraint(LinearCombination<F>({{F(1), a}}),\n                                        LinearCombination<F>({{F(2), b}}),\n                                        LinearCombination<F>({{F(1), c}}));\n    // d = a + b\n    Variable d = constraint_system.CreateLinearCombination(\n        LinearCombination<F>({{F(1), a}, {F(1), b}}));\n    // a * d = d\n    constraint_system.EnforceConstraint(LinearCombination<F>({{F(1), a}}),\n                                        LinearCombination<F>({{F(1), d}}),\n                                        LinearCombination<F>({{F(1), d}}));\n    // e = d + d\n    Variable e = constraint_system.CreateLinearCombination(\n        LinearCombination<F>({{F(1), d}, {F(1), d}}));\n    // 1 * e = e\n    constraint_system.EnforceConstraint(\n        LinearCombination<F>({{F(1), Variable::One()}}),\n        LinearCombination<F>({{F(1), e}}), LinearCombination<F>({{F(1), e}}));\n\n    constraint_system.Finalize();\n\n    ConstraintMatrices<F> matrices = constraint_system.ToMatrices().value();\n    ConstraintMatrices<F> expected_matrices;\n    if (i == 0) {\n      expected_matrices.num_instance_variables = 2;\n      expected_matrices.num_witness_variables = 2;\n      expected_matrices.num_constraints = 3;\n      expected_matrices.a_num_non_zero = 3;\n      expected_matrices.b_num_non_zero = 5;\n      expected_matrices.c_num_non_zero = 5;\n      // a * 2b  = c\n      // a * (a + b) = a + b\n      // 1 * (2a + 2b) = 2a + 2b\n      expected_matrices.a = Matrix<F>({{{F(1), 1}}, {{F(1), 1}}, {{F(1), 0}}});\n      expected_matrices.b = Matrix<F>(\n          {{{F(2), 2}}, {{F(1), 1}, {F(1), 2}}, {{F(2), 1}, {F(2), 2}}});\n      expected_matrices.c = Matrix<F>(\n          {{{F(1), 3}}, {{F(1), 1}, {F(1), 2}}, {{F(2), 1}, {F(2), 2}}});\n    } else {\n      expected_matrices.num_instance_variables = 2;\n      expected_matrices.num_witness_variables = 3;\n      expected_matrices.num_constraints = 4;\n      expected_matrices.a_num_non_zero = 5;\n      expected_matrices.b_num_non_zero = 4;\n      expected_matrices.c_num_non_zero = 4;\n      // a * 2b  = c\n      // a * d = d\n      // 1 * 2d = 2d\n      // (a + b) * 1 = d\n      expected_matrices.a = Matrix<F>(\n          {{{F(1), 1}}, {{F(1), 1}}, {{F(1), 0}}, {{F(1), 1}, {F(1), 2}}});\n      expected_matrices.b =\n          Matrix<F>({{{F(2), 2}}, {{F(1), 4}}, {{F(2), 4}}, {{F(1), 0}}});\n      expected_matrices.c =\n          Matrix<F>({{{F(1), 3}}, {{F(1), 4}}, {{F(2), 4}}, {{F(1), 4}}});\n    }\n    EXPECT_EQ(matrices, expected_matrices);\n  }\n}\n\nTEST_F(ConstraintSystemTest, EvalLinearCombination) {\n  ConstraintSystem<F> constraint_system;\n  Variable instance_variable =\n      constraint_system.CreateInstanceVariable([]() { return F(2); });\n  Variable advice_variable =\n      constraint_system.CreateWitnessVariable([]() { return F(3); });\n  LinearCombination<F> lc;\n  lc += Term<F>(F(4), instance_variable);  // 4 * 2 = 1\n  lc += Term<F>(F(5), advice_variable);    // 5 * 3 = 1\n  Variable lc_variable =\n      constraint_system.CreateLinearCombination(std::move(lc));\n  EXPECT_EQ(constraint_system.EvalLinearCombination(lc_variable.index()), F(2));\n}\n\nTEST_F(ConstraintSystemTest, GetAssignedValue) {\n  ConstraintSystem<F> constraint_system;\n  EXPECT_EQ(constraint_system.GetAssignedValue(Variable::Zero()), F(0));\n  EXPECT_EQ(constraint_system.GetAssignedValue(Variable::One()), F(1));\n  Variable instance_variable =\n      constraint_system.CreateInstanceVariable([]() { return F(2); });\n  EXPECT_EQ(constraint_system.GetAssignedValue(instance_variable), F(2));\n  Variable advice_variable =\n      constraint_system.CreateWitnessVariable([]() { return F(3); });\n  EXPECT_EQ(constraint_system.GetAssignedValue(advice_variable), F(3));\n  LinearCombination<F> lc;\n  lc += Term<F>(F(4), instance_variable);  // 4 * 2 = 1\n  lc += Term<F>(F(5), advice_variable);    // 5 * 3 = 1\n  Variable lc_variable =\n      constraint_system.CreateLinearCombination(std::move(lc));\n  EXPECT_EQ(constraint_system.GetAssignedValue(lc_variable), F(2));\n  EXPECT_EQ(constraint_system.lc_assignment_cache_[lc_variable.index()], F(2));\n}\n\nTEST_F(ConstraintSystemTest, IsSatisfied) {\n  ConstraintSystem<F> constraint_system;\n  LinearCombination<F> a({{F(2), Variable::One()}});\n  LinearCombination<F> b({{F(3), Variable::One()}});\n  LinearCombination<F> c({{F(6), Variable::One()}});\n  constraint_system.EnforceConstraint(a, b, c);\n  ASSERT_TRUE(constraint_system.IsSatisfied());\n  a = LinearCombination<F>({{F(2), Variable::One()}});\n  b = LinearCombination<F>({{F(3), Variable::One()}});\n  c = LinearCombination<F>({{F(5), Variable::One()}});\n  constraint_system.EnforceConstraint(a, b, c);\n  ASSERT_TRUE(!constraint_system.IsSatisfied());\n}\n\n}  // namespace tachyon::zk::r1cs\n"
  },
  {
    "path": "tachyon/zk/r1cs/constraint_system/linear_combination.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_ZK_R1CS_CONSTRAINT_SYSTEM_LINEAR_COMBINATION_H_\n#define TACHYON_ZK_R1CS_CONSTRAINT_SYSTEM_LINEAR_COMBINATION_H_\n\n#include <stddef.h>\n\n#include <algorithm>\n#include <string>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"absl/strings/str_join.h\"\n#include \"gtest/gtest_prod.h\"\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/ranges/algorithm.h\"\n#include \"tachyon/base/sort.h\"\n#include \"tachyon/zk/r1cs/constraint_system/term.h\"\n\nnamespace tachyon::zk::r1cs {\n\ntemplate <typename F>\nclass LinearCombination {\n public:\n  constexpr LinearCombination() = default;\n  constexpr explicit LinearCombination(const std::vector<Term<F>>& terms)\n      : terms_(terms) {}\n  constexpr explicit LinearCombination(std::vector<Term<F>>&& terms)\n      : terms_(std::move(terms)) {}\n\n  const std::vector<Term<F>>& terms() const { return terms_; }\n  std::vector<Term<F>>& terms() { return terms_; }\n\n  constexpr static LinearCombination CreateDeduplicated(\n      const std::vector<Term<F>>& terms) {\n    LinearCombination lc(terms);\n    lc.Deduplicate();\n    return lc;\n  }\n\n  constexpr static LinearCombination CreateDeduplicated(\n      std::vector<Term<F>>&& terms) {\n    LinearCombination lc(std::move(terms));\n    lc.Deduplicate();\n    return lc;\n  }\n\n  void AppendTerm(const Term<F>& term) { terms_.push_back(term); }\n  void AppendTerm(Term<F>&& term) { terms_.push_back(std::move(term)); }\n\n  void AppendTerms(const std::vector<Term<F>>& terms) {\n    terms_.insert(terms_.end(), terms.begin(), terms.end());\n  }\n  void AppendTerms(std::vector<Term<F>>&& terms) {\n    base::Extend(terms_, std::move(terms));\n  }\n\n  std::vector<Term<F>>&& TakeTerms() && { return std::move(terms_); }\n\n  void Deduplicate() {\n    base::UnstableSort(terms_.begin(), terms_.end(),\n                       [](const Term<F>& a, const Term<F>& b) {\n                         return a.variable < b.variable;\n                       });\n    bool is_first = true;\n    auto cur_var_first_it = terms_.begin();\n    auto it = terms_.begin();\n    while (it != terms_.end()) {\n      if (!is_first && cur_var_first_it->variable == it->variable) {\n        cur_var_first_it->coefficient += it->coefficient;\n      } else {\n        cur_var_first_it = it;\n        is_first = false;\n      }\n      ++it;\n    }\n    auto last = std::unique(terms_.begin(), terms_.end(),\n                            [](const Term<F>& a, const Term<F>& b) {\n                              return a.variable == b.variable;\n                            });\n    terms_.erase(last, terms_.end());\n  }\n\n  bool IsSorted() const {\n    return base::ranges::is_sorted(terms_.begin(), terms_.end(),\n                                   [](const Term<F>& a, const Term<F>& b) {\n                                     return a.variable < b.variable;\n                                   });\n  }\n\n  LinearCombination operator+(const Term<F>& term) const {\n    LinearCombination ret = *this;\n    ret += term;\n    return ret;\n  }\n\n  LinearCombination& operator+=(const Term<F>& term) {\n    size_t index = 0;\n    if (GetVarLocation(term.variable, &index)) {\n      terms_[index].coefficient += term.coefficient;\n    } else {\n      terms_.insert(terms_.begin() + index, term);\n    }\n    return *this;\n  }\n\n  LinearCombination operator-(const Term<F>& term) const {\n    return operator+(-term);\n  }\n\n  LinearCombination& operator-=(const Term<F>& term) {\n    return operator+=(-term);\n  }\n\n  template <typename T,\n            std::enable_if_t<!std::is_same_v<std::remove_cv_t<T>, Term<F>&> &&\n                             !std::is_same_v<T, Term<F>> &&\n                             std::is_constructible_v<Term<F>, T>>* = nullptr>\n  LinearCombination operator+(T&& value) const {\n    return operator+(Term<F>(std::forward<T>(value)));\n  }\n\n  template <typename T,\n            std::enable_if_t<!std::is_same_v<std::remove_cv_t<T>, Term<F>&> &&\n                             !std::is_same_v<T, Term<F>> &&\n                             std::is_constructible_v<Term<F>, T>>* = nullptr>\n  LinearCombination& operator+=(T&& value) {\n    return operator+=(Term<F>(std::forward<T>(value)));\n  }\n\n  template <typename T,\n            std::enable_if_t<!std::is_same_v<std::remove_cv_t<T>, Term<F>&> &&\n                             !std::is_same_v<T, Term<F>> &&\n                             std::is_constructible_v<Term<F>, T>>* = nullptr>\n  LinearCombination operator-(T&& value) const {\n    return operator-(Term<F>(std::forward<T>(value)));\n  }\n\n  template <typename T,\n            std::enable_if_t<!std::is_same_v<std::remove_cv_t<T>, Term<F>&> &&\n                             !std::is_same_v<T, Term<F>> &&\n                             std::is_constructible_v<Term<F>, T>>* = nullptr>\n  LinearCombination& operator-=(T&& value) {\n    return operator-=(Term<F>(std::forward<T>(value)));\n  }\n\n  LinearCombination operator-() const {\n    return LinearCombination(\n        base::Map(terms_, [](const Term<F>& term) { return -term; }));\n  }\n  LinearCombination& NegateInPlace() {\n    for (Term<F>& term : terms_) {\n      term.NegateInPlace();\n    }\n    return *this;\n  }\n\n  LinearCombination operator+(const LinearCombination& other) const {\n    if (terms_.empty()) {\n      return other;\n    } else if (other.terms_.empty()) {\n      return *this;\n    }\n    return DoAddition</*NEGATION=*/false>(other);\n  }\n  LinearCombination& operator+=(const LinearCombination& other) {\n    if (terms_.empty()) {\n      return *this = other;\n    } else if (other.terms_.empty()) {\n      return *this;\n    }\n    return *this = DoAddition</*NEGATION=*/false>(other);\n  }\n\n  LinearCombination operator-(const LinearCombination& other) const {\n    if (terms_.empty()) {\n      return -other;\n    } else if (other.terms_.empty()) {\n      return *this;\n    }\n    return DoAddition</*NEGATION=*/true>(other);\n  }\n  LinearCombination& operator-=(const LinearCombination& other) {\n    if (terms_.empty()) {\n      return *this = -other;\n    } else if (other.terms_.empty()) {\n      return *this;\n    }\n    return *this = DoAddition</*NEGATION=*/true>(other);\n  }\n\n  // TODO(chokobole): Let |LinearCombination| have an additional member to\n  // accumulate scalar multiplications and do real multiplication lazily.\n  LinearCombination operator*(const F& scalar) const {\n    LinearCombination ret = *this;\n    ret *= scalar;\n    return ret;\n  }\n  LinearCombination& operator*=(const F& scalar) {\n    for (Term<F>& term : terms_) {\n      term *= scalar;\n    }\n    return *this;\n  }\n\n  bool operator==(const LinearCombination& other) const {\n    return terms_ == other.terms_;\n  }\n  bool operator!=(const LinearCombination& other) const {\n    return terms_ != other.terms_;\n  }\n\n  std::string ToString() const {\n    std::vector<std::string> term_strs =\n        base::Map(terms_, [](const Term<F>& term) { return term.ToString(); });\n    return absl::StrJoin(term_strs, \" + \");\n  }\n\n private:\n  FRIEND_TEST(LinearCombinationTest, BinarySearch);\n\n  bool GetVarLocation(const Variable& variable, size_t* index) const {\n    constexpr static size_t kBinarySearchThreshold = 6;\n    if (terms_.size() < kBinarySearchThreshold) {\n      for (size_t i = 0; i < terms_.size(); ++i) {\n        const Variable& cur_variable = terms_[i].variable;\n        if (cur_variable == variable) {\n          *index = i;\n          return true;\n        } else if (cur_variable > variable) {\n          *index = i;\n          return false;\n        }\n      }\n      *index = terms_.size();\n      return false;\n    } else {\n      return BinarySearch(variable, index);\n    }\n  }\n\n  // Returns true if the |variable| is found and populates |index| with the\n  // index of the |variable|. Otherwise, returns false and and populates |index|\n  // with the index where a matching |variable| could be inserted while\n  // maintaining sorted order. See\n  // https://doc.rust-lang.org/std/primitive.slice.html#method.binary_search_by\n  bool BinarySearch(const Variable& variable, size_t* index) const {\n    size_t size = terms_.size();\n    size_t left = 0;\n    size_t right = size;\n    while (left < right) {\n      size_t mid = left + size / 2;\n      // SAFETY: the while condition means |size| is strictly positive, so\n      // |size / 2 < size|. Thus |left + size / 2 < left + size|, which\n      // coupled with the |left + size <= terms_.size()| invariant means\n      // we have |left + size / 2 < terms_.size()|, and this is in-bounds.\n      const Variable& mid_variable = terms_[mid].variable;\n\n      if (mid_variable < variable) {\n        left = mid + 1;\n      } else if (mid_variable > variable) {\n        right = mid;\n      } else {\n        *index = mid;\n        return true;\n      }\n      size = right - left;\n    }\n\n    *index = left;\n    return false;\n  }\n\n  template <bool NEGATION>\n  LinearCombination DoAddition(const LinearCombination& other) const {\n    const std::vector<Term<F>>& l_terms = terms_;\n    const std::vector<Term<F>>& r_terms = other.terms_;\n\n    auto l_it = l_terms.begin();\n    auto r_it = r_terms.begin();\n    std::vector<Term<F>> ret;\n    ret.reserve(std::max(l_terms.size(), r_terms.size()));\n    while (l_it != l_terms.end() || r_it != r_terms.end()) {\n      if (l_it == l_terms.end()) {\n        if constexpr (NEGATION) {\n          ret.push_back(-(*r_it));\n        } else {\n          ret.push_back(*r_it);\n        }\n        ++r_it;\n        continue;\n      }\n      if (r_it == r_terms.end()) {\n        ret.push_back(*l_it);\n        ++l_it;\n        continue;\n      }\n      if (l_it->variable < r_it->variable) {\n        ret.push_back(*l_it);\n        ++l_it;\n      } else if (l_it->variable > r_it->variable) {\n        if constexpr (NEGATION) {\n          ret.push_back(-(*r_it));\n        } else {\n          ret.push_back(*r_it);\n        }\n        ++r_it;\n      } else {\n        F coefficient;\n        if constexpr (NEGATION) {\n          coefficient = l_it->coefficient - r_it->coefficient;\n        } else {\n          coefficient = l_it->coefficient + r_it->coefficient;\n        }\n        ret.push_back({\n            std::move(coefficient),\n            l_it->variable,\n        });\n        ++l_it;\n        ++r_it;\n      }\n    }\n    return LinearCombination(std::move(ret));\n  }\n\n  std::vector<Term<F>> terms_;\n};\n\n}  // namespace tachyon::zk::r1cs\n\n#endif  // TACHYON_ZK_R1CS_CONSTRAINT_SYSTEM_LINEAR_COMBINATION_H_\n"
  },
  {
    "path": "tachyon/zk/r1cs/constraint_system/linear_combination_unittest.cc",
    "content": "#include \"tachyon/zk/r1cs/constraint_system/linear_combination.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n#include \"tachyon/math/finite_fields/test/gf7.h\"\n\nnamespace tachyon::zk::r1cs {\n\nnamespace {\n\nusing F = math::GF7;\n\nclass LinearCombinationTest : public math::FiniteFieldTest<F> {};\n\nvoid TestDeduplicate(const std::vector<Term<F>>& terms,\n                     const LinearCombination<F>& expected_lc) {\n  LinearCombination<F> lc(terms);\n  lc.Deduplicate();\n  EXPECT_EQ(lc, expected_lc);\n  EXPECT_EQ(LinearCombination<F>::CreateDeduplicated(terms), expected_lc);\n  EXPECT_TRUE(lc.IsSorted());\n}\n\n// |lc| is copied on purpose.\ntemplate <typename T>\nvoid TestAddition(LinearCombination<F> lc, const T& value,\n                  const LinearCombination<F>& expected_lc) {\n  EXPECT_EQ(lc + value, expected_lc);\n  lc += value;\n  EXPECT_EQ(lc, expected_lc);\n}\n\n// |lc| is copied on purpose.\ntemplate <typename T>\nvoid TestSubtraction(LinearCombination<F> lc, const T& value,\n                     const LinearCombination<F>& expected_lc) {\n  EXPECT_EQ(lc - value, expected_lc);\n  lc -= value;\n  EXPECT_EQ(lc, expected_lc);\n}\n\n}  // namespace\n\nTEST_F(LinearCombinationTest, Deduplicate) {\n  Variable zero = Variable::Zero();\n  Variable one = Variable::One();\n\n  TestDeduplicate({}, LinearCombination<F>());\n  TestDeduplicate({{F(2), zero}, {F(3), zero}},\n                  LinearCombination<F>({{F(5), zero}}));\n  TestDeduplicate(\n      {{F(2), zero}, {F(3), one}, {F(2), one}, {F(2), zero}, {F(1), one}},\n      LinearCombination<F>({{F(4), zero}, {F(6), one}}));\n}\n\nTEST_F(LinearCombinationTest, AdditionTerm) {\n  Variable zero = Variable::Zero();\n  Variable one = Variable::One();\n  Variable instance = Variable::Instance(0);\n\n  LinearCombination<F> lc({{F(5), zero}, {F(2), one}});\n  {\n    LinearCombination<F> expected_lc({{F(1), zero}, {F(2), one}});\n    TestAddition(lc, Term<F>(F(3), zero), expected_lc);\n  }\n  {\n    LinearCombination<F> expected_lc({{F(6), zero}, {F(2), one}});\n    TestAddition(lc, zero, expected_lc);\n  }\n\n  {\n    LinearCombination<F> expected_lc({{F(5), zero}, {F(5), one}});\n    TestAddition(lc, Term<F>(F(3), one), expected_lc);\n  }\n  {\n    LinearCombination<F> expected_lc({{F(5), zero}, {F(3), one}});\n    TestAddition(lc, one, expected_lc);\n  }\n\n  {\n    LinearCombination<F> expected_lc(\n        {{F(5), zero}, {F(2), one}, {F(3), instance}});\n    TestAddition(lc, Term<F>(F(3), instance), expected_lc);\n  }\n  {\n    LinearCombination<F> expected_lc(\n        {{F(5), zero}, {F(2), one}, {F(1), instance}});\n    TestAddition(lc, instance, expected_lc);\n  }\n}\n\nTEST_F(LinearCombinationTest, SubtractionTerm) {\n  Variable zero = Variable::Zero();\n  Variable one = Variable::One();\n  Variable instance = Variable::Instance(0);\n\n  LinearCombination<F> lc({{F(5), zero}, {F(2), one}});\n  {\n    LinearCombination<F> expected_lc({{F(2), zero}, {F(2), one}});\n    TestSubtraction(lc, Term<F>(F(3), zero), expected_lc);\n  }\n  {\n    LinearCombination<F> expected_lc({{F(4), zero}, {F(2), one}});\n    TestSubtraction(lc, zero, expected_lc);\n  }\n\n  {\n    LinearCombination<F> expected_lc({{F(5), zero}, {F(6), one}});\n    TestSubtraction(lc, Term<F>(F(3), one), expected_lc);\n  }\n  {\n    LinearCombination<F> expected_lc({{F(5), zero}, {F(1), one}});\n    TestSubtraction(lc, one, expected_lc);\n  }\n\n  {\n    LinearCombination<F> expected_lc(\n        {{F(5), zero}, {F(2), one}, {F(4), instance}});\n    TestSubtraction(lc, Term<F>(F(3), instance), expected_lc);\n  }\n  {\n    LinearCombination<F> expected_lc(\n        {{F(5), zero}, {F(2), one}, {F(6), instance}});\n    TestSubtraction(lc, instance, expected_lc);\n  }\n}\n\nTEST_F(LinearCombinationTest, ScalarMul) {\n  Variable zero = Variable::Zero();\n  Variable one = Variable::One();\n\n  LinearCombination<F> lc({{F(5), zero}, {F(2), one}});\n  LinearCombination<F> expected_lc({{F(3), zero}, {F(4), one}});\n  EXPECT_EQ(lc * F(2), expected_lc);\n  lc *= F(2);\n  EXPECT_EQ(lc, expected_lc);\n}\n\nTEST_F(LinearCombinationTest, BinarySearch) {\n  const size_t kSize = 10;\n  std::vector<Term<F>> terms = base::CreateVector(kSize, [](size_t i) {\n    return Term<F>(F(1), Variable::Instance(i << 1));\n  });\n  LinearCombination<F> lc(std::move(terms));\n  for (size_t i = 0; i < (kSize << 1); ++i) {\n    size_t index;\n    if (i % 2 == 0) {\n      ASSERT_TRUE(lc.BinarySearch(Variable::Instance(i), &index));\n      EXPECT_EQ(index, (i >> 1));\n    } else {\n      ASSERT_FALSE(lc.BinarySearch(Variable::Instance(i), &index));\n      EXPECT_EQ(index, (i >> 1) + 1);\n    }\n  }\n}\n\nTEST_F(LinearCombinationTest, AdditionLinearCombination) {\n  Variable zero = Variable::Zero();\n  Variable one = Variable::One();\n  Variable instance = Variable::Instance(0);\n\n  LinearCombination<F> lc_a({{F(5), zero}, {F(2), instance}});\n  {\n    LinearCombination<F> lc_b({{F(1), zero}});\n    LinearCombination<F> expected_lc({{F(6), zero}, {F(2), instance}});\n    TestAddition(lc_a, lc_b, expected_lc);\n  }\n\n  {\n    LinearCombination<F> lc_b({{F(1), zero}, {F(3), one}, {F(2), instance}});\n    LinearCombination<F> expected_lc(\n        {{F(6), zero}, {F(3), one}, {F(4), instance}});\n    TestAddition(lc_a, lc_b, expected_lc);\n  }\n}\n\nTEST_F(LinearCombinationTest, SubtractionLinearCombination) {\n  Variable zero = Variable::Zero();\n  Variable one = Variable::One();\n  Variable instance = Variable::Instance(0);\n\n  LinearCombination<F> lc_a({{F(5), zero}, {F(2), instance}});\n  {\n    LinearCombination<F> lc_b({{F(1), zero}});\n    LinearCombination<F> expected_lc({{F(4), zero}, {F(2), instance}});\n    TestSubtraction(lc_a, lc_b, expected_lc);\n  }\n\n  {\n    LinearCombination<F> lc_b({{F(1), zero}, {F(3), one}, {F(2), instance}});\n    LinearCombination<F> expected_lc(\n        {{F(4), zero}, {F(4), one}, {F(0), instance}});\n    TestSubtraction(lc_a, lc_b, expected_lc);\n  }\n}\n\n}  // namespace tachyon::zk::r1cs\n"
  },
  {
    "path": "tachyon/zk/r1cs/constraint_system/matrix.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_ZK_R1CS_CONSTRAINT_SYSTEM_MATRIX_H_\n#define TACHYON_ZK_R1CS_CONSTRAINT_SYSTEM_MATRIX_H_\n\n#include <stddef.h>\n\n#include <numeric>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"absl/strings/str_join.h\"\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/base/containers/container_util.h\"\n\nnamespace tachyon::zk::r1cs {\n\n// FIXME(chokobole): I want to separate |coefficient| from |Cell|.\n// See comments in tachyon/zk/r1cs/constraint_system/constraint_matrices.h.\ntemplate <typename F>\nstruct Cell {\n  F coefficient;\n  size_t index;\n\n  bool operator==(const Cell& other) const {\n    return coefficient == other.coefficient && index == other.index;\n  }\n  bool operator!=(const Cell& other) const { return !operator==(other); }\n\n  std::string ToString() const {\n    return absl::Substitute(\"{coefficient: $0, index: $1}\",\n                            coefficient.ToString(), index);\n  }\n};\n\ntemplate <typename F>\nclass Matrix {\n public:\n  Matrix() = default;\n  explicit Matrix(const std::vector<std::vector<Cell<F>>>& cells)\n      : cells_(cells) {}\n  explicit Matrix(std::vector<std::vector<Cell<F>>>&& cells)\n      : cells_(std::move(cells)) {}\n\n  size_t CountNonZero() const {\n    return std::accumulate(cells_.begin(), cells_.end(), 0,\n                           [](size_t acc, const std::vector<Cell<F>>& row) {\n                             return acc + row.size();\n                           });\n  }\n\n  bool operator==(const Matrix& other) const { return cells_ == other.cells_; }\n  bool operator!=(const Matrix& other) const { return cells_ != other.cells_; }\n\n  std::vector<Cell<F>>& operator[](size_t index) { return cells_[index]; }\n  const std::vector<Cell<F>>& operator[](size_t index) const {\n    return cells_[index];\n  }\n\n  std::string ToString() const {\n    std::vector<std::string> rows =\n        base::Map(cells_, [](const std::vector<Cell<F>>& row) {\n          return absl::Substitute(\n              \"[$0]\", absl::StrJoin(base::Map(row,\n                                              [](const Cell<F>& cell) {\n                                                return cell.ToString();\n                                              }),\n                                    \",\"));\n        });\n    return absl::Substitute(\"[$0]\", absl::StrJoin(rows, \",\\n\"));\n  }\n\n private:\n  std::vector<std::vector<Cell<F>>> cells_;\n};\n\n}  // namespace tachyon::zk::r1cs\n\n#endif  // TACHYON_ZK_R1CS_CONSTRAINT_SYSTEM_MATRIX_H_\n"
  },
  {
    "path": "tachyon/zk/r1cs/constraint_system/optimization_goal.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_ZK_R1CS_CONSTRAINT_SYSTEM_OPTIMIZATION_GOAL_H_\n#define TACHYON_ZK_R1CS_CONSTRAINT_SYSTEM_OPTIMIZATION_GOAL_H_\n\nnamespace tachyon::zk::r1cs {\n\nenum class OptimizationGoal {\n  // Minimize the number of constraints.\n  kConstraints,\n  // Minimize the total weight of the constraints (the number of nonzero\n  // entries across all constraints).\n  kWeight,\n};\n\n}  // namespace tachyon::zk::r1cs\n\n#endif  // TACHYON_ZK_R1CS_CONSTRAINT_SYSTEM_OPTIMIZATION_GOAL_H_\n"
  },
  {
    "path": "tachyon/zk/r1cs/constraint_system/qap_instance_map_result.h",
    "content": "#ifndef TACHYON_ZK_R1CS_CONSTRAINT_SYSTEM_QAP_INSTANCE_MAP_RESULT_H_\n#define TACHYON_ZK_R1CS_CONSTRAINT_SYSTEM_QAP_INSTANCE_MAP_RESULT_H_\n\n#include <stddef.h>\n\n#include <vector>\n\nnamespace tachyon::zk::r1cs {\n\ntemplate <typename F>\nstruct QAPInstanceMapResult {\n  std::vector<F> a;\n  std::vector<F> b;\n  std::vector<F> c;\n  // t(x) = xⁿ⁺ˡ⁺¹ - 1\n  F t_x;\n  // n\n  size_t num_constraints;\n  // l + 1\n  size_t num_instance_variables;\n  // m - l\n  size_t num_witness_variables;\n  // m\n  size_t num_qap_variables;\n};\n\n}  // namespace tachyon::zk::r1cs\n\n#endif  // TACHYON_ZK_R1CS_CONSTRAINT_SYSTEM_QAP_INSTANCE_MAP_RESULT_H_\n"
  },
  {
    "path": "tachyon/zk/r1cs/constraint_system/qap_witness_map_result.h",
    "content": "#ifndef TACHYON_ZK_R1CS_CONSTRAINT_SYSTEM_QAP_WITNESS_MAP_RESULT_H_\n#define TACHYON_ZK_R1CS_CONSTRAINT_SYSTEM_QAP_WITNESS_MAP_RESULT_H_\n\n#include <vector>\n\nnamespace tachyon::zk::r1cs {\n\ntemplate <typename F>\nstruct QAPWitnessMapResult {\n  std::vector<F> h;\n  std::vector<F> full_assignments;\n};\n\n}  // namespace tachyon::zk::r1cs\n\n#endif  // TACHYON_ZK_R1CS_CONSTRAINT_SYSTEM_QAP_WITNESS_MAP_RESULT_H_\n"
  },
  {
    "path": "tachyon/zk/r1cs/constraint_system/quadratic_arithmetic_program.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_ZK_R1CS_CONSTRAINT_SYSTEM_QUADRATIC_ARITHMETIC_PROGRAM_H_\n#define TACHYON_ZK_R1CS_CONSTRAINT_SYSTEM_QUADRATIC_ARITHMETIC_PROGRAM_H_\n\n#include <stddef.h>\n\n#include <functional>\n#include <memory>\n#include <optional>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/optional.h\"\n#include \"tachyon/base/parallelize.h\"\n#include \"tachyon/base/profiler.h\"\n#include \"tachyon/zk/r1cs/constraint_system/constraint_system.h\"\n#include \"tachyon/zk/r1cs/constraint_system/qap_instance_map_result.h\"\n#include \"tachyon/zk/r1cs/constraint_system/qap_witness_map_result.h\"\n\nnamespace tachyon::zk::r1cs {\n\ntemplate <typename F>\nF EvaluateConstraint(const std::vector<Cell<F>>& cells,\n                     absl::Span<const F> assignments) {\n  TRACE_EVENT(\"ProofGeneration\", \"QAP::EvaluateConstraint\");\n  F sum = F::Zero();\n  for (const Cell<F>& cell : cells) {\n    if (cell.coefficient.IsOne()) {\n      sum += assignments[cell.index];\n    } else {\n      sum += assignments[cell.index] * cell.coefficient;\n    }\n  }\n  return sum;\n}\n\ntemplate <typename F>\nclass QuadraticArithmeticProgram {\n public:\n  QuadraticArithmeticProgram() = delete;\n\n  // Computes a QAP instance corresponding to the R1CS instance defined by |cs|.\n  template <typename Domain>\n  static QAPInstanceMapResult<F> InstanceMap(const Domain* domain,\n                                             const ConstraintSystem<F>& cs,\n                                             const F& x) {\n    TRACE_EVENT(\"ProofGeneration\", \"QAP::InstanceMap\");\n    CHECK_GE(domain->size(), cs.num_constraints());\n    std::optional<ConstraintMatrices<F>> matrices = cs.ToMatrices();\n    // |num_constraint| = n\n    size_t num_constraints = cs.num_constraints();\n    // |num_instance_variables| = l + 1\n    size_t num_instance_variables = cs.num_instance_variables();\n    // |num_witness_variables| = m - l\n    size_t num_witness_variables = cs.num_witness_variables();\n\n    // t(x) = xⁿ⁺ˡ⁺¹ - 1\n    F t_x = domain->EvaluateVanishingPolynomial(x);\n\n    std::vector<F> l = domain->EvaluateAllLagrangeCoefficients(x);\n\n    // |num_qap_variables| = m = (l + 1 - 1) + m - l\n    size_t num_qap_variables =\n        (num_instance_variables - 1) + num_witness_variables;\n\n    std::vector<F> a(num_qap_variables + 1);\n    std::vector<F> b(num_qap_variables + 1);\n    std::vector<F> c(num_qap_variables + 1);\n    OMP_PARALLEL_FOR(size_t i = 0; i <= num_qap_variables; ++i) {\n      a[i] = F::Zero();\n      b[i] = F::Zero();\n      c[i] = F::Zero();\n    }\n\n    // clang-format off\n    // |a[i]| = lₙ₊ᵢ(x) +  Σⱼ₌₀..ₙ₋₁ (lⱼ(x) * Aⱼ,ᵢ) (if i < |num_instance_variables|)\n    //        = Σⱼ₌₀..ₙ₋₁ (lⱼ(x) * Aⱼ,ᵢ)            (otherwise)\n    // |b[i]| = Σⱼ₌₀..ₙ₋₁ (lⱼ(x) * Bⱼ,ᵢ)\n    // |c[i]| = Σⱼ₌₀..ₙ₋₁ (lⱼ(x) * Cⱼ,ᵢ)\n    // clang-format on\n    for (size_t i = 0; i < num_instance_variables; ++i) {\n      a[i] = l[num_constraints + i];\n    }\n\n    for (size_t i = 0; i < num_constraints; ++i) {\n      for (const Cell<F>& cell : matrices->a[i]) {\n        a[cell.index] += (l[i] * cell.coefficient);\n      }\n      for (const Cell<F>& cell : matrices->b[i]) {\n        b[cell.index] += (l[i] * cell.coefficient);\n      }\n      for (const Cell<F>& cell : matrices->c[i]) {\n        c[cell.index] += (l[i] * cell.coefficient);\n      }\n    }\n\n    return {std::move(a),          std::move(b),     std::move(c),\n            std::move(t_x),        num_constraints,  num_instance_variables,\n            num_witness_variables, num_qap_variables};\n  }\n\n  template <typename Domain>\n  static QAPWitnessMapResult<F> WitnessMap(\n      const Domain* domain, const ConstraintSystem<F>& constraint_system) {\n    TRACE_EVENT(\"ProofGeneration\", \"QAP::WitnessMap\");\n    std::optional<ConstraintMatrices<F>> matrices =\n        constraint_system.ToMatrices();\n    std::vector<F> full_assignments;\n    full_assignments.reserve(constraint_system.num_instance_variables() +\n                             constraint_system.num_witness_variables());\n    full_assignments.insert(full_assignments.end(),\n                            constraint_system.instance_assignments().begin(),\n                            constraint_system.instance_assignments().end());\n    full_assignments.insert(full_assignments.end(),\n                            constraint_system.witness_assignments().begin(),\n                            constraint_system.witness_assignments().end());\n\n    std::vector<F> h_poly =\n        WitnessMapFromMatrices(domain, matrices.value(), full_assignments);\n    return {std::move(h_poly), std::move(full_assignments)};\n  }\n\n  template <typename Domain>\n  static std::vector<F> WitnessMapFromMatrices(\n      const Domain* domain, const ConstraintMatrices<F>& matrices,\n      absl::Span<const F> full_assignments) {\n    TRACE_EVENT(\"ProofGeneration\", \"QAP::WitnessMapFromMatrices\");\n    using Evals = typename Domain::Evals;\n    using DensePoly = typename Domain::DensePoly;\n\n    CHECK_GE(domain->size(), matrices.num_constraints);\n\n    std::vector<F> a(domain->size());\n    std::vector<F> b(domain->size());\n    std::vector<F> c(domain->size());\n    OMP_PARALLEL_FOR(size_t i = matrices.num_constraints; i < domain->size();\n                     ++i) {\n      a[i] = F::Zero();\n      b[i] = F::Zero();\n      c[i] = F::Zero();\n    }\n\n    // clang-format off\n    // |a[i]| = Σⱼ₌₀..ₘ (xⱼ * Aᵢ,ⱼ)    (if i < |num_constraints|)\n    //        = x[i - num_constraints] (otherwise)\n    // |b[i]| = Σⱼ₌₀..ₘ (xⱼ * Bᵢ,ⱼ)    (if i < |num_constraints|)\n    //        = 0                      (otherwise)\n    // |c[i]| = Σⱼ₌₀..ₘ (xⱼ * Cᵢ,ⱼ)    (if i < |num_constraints|)\n    //        = 0                      (otherwise)\n    // where x is |full_assignments|.\n    // clang-format on\n    OMP_PARALLEL {\n      OMP_FOR_NOWAIT(size_t i = 0; i < matrices.num_constraints; ++i) {\n        a[i] = EvaluateConstraint(matrices.a[i], full_assignments);\n      }\n\n      OMP_FOR_NOWAIT(size_t i = 0; i < matrices.num_constraints; ++i) {\n        b[i] = EvaluateConstraint(matrices.b[i], full_assignments);\n      }\n\n      OMP_FOR(size_t i = 0; i < matrices.num_constraints; ++i) {\n        c[i] = EvaluateConstraint(matrices.c[i], full_assignments);\n      }\n    }\n\n    for (size_t i = matrices.num_constraints;\n         i < matrices.num_constraints + matrices.num_instance_variables; ++i) {\n      a[i] = full_assignments[i - matrices.num_constraints];\n    }\n\n    Evals a_evals(std::move(a));\n    DensePoly a_poly = domain->IFFT(std::move(a_evals));\n    Evals b_evals(std::move(b));\n    DensePoly b_poly = domain->IFFT(std::move(b_evals));\n    Evals c_evals(std::move(c));\n    DensePoly c_poly = domain->IFFT(std::move(c_evals));\n\n    std::unique_ptr<Domain> coset_domain =\n        domain->GetCoset(F::FromMontgomery(F::Config::kSubgroupGenerator));\n\n    a_evals = coset_domain->FFT(std::move(a_poly));\n    b_evals = coset_domain->FFT(std::move(b_poly));\n    c_evals = coset_domain->FFT(std::move(c_poly));\n\n    F vanishing_polynomial_over_coset =\n        unwrap(domain\n                   ->EvaluateVanishingPolynomial(\n                       F::FromMontgomery(F::Config::kSubgroupGenerator))\n                   .Inverse());\n\n    // |h_evals[i]| = (|a[i]| * |b[i]| - |c[i]|)) / (g * ωⁿ⁺ˡ⁺¹ - 1)\n    OMP_PARALLEL_FOR(size_t i = 0; i < domain->size(); ++i) {\n      F& h_evals_i = a_evals.at(i);\n      h_evals_i *= b_evals[i];\n      h_evals_i -= c_evals[i];\n      h_evals_i *= vanishing_polynomial_over_coset;\n    }\n\n    return coset_domain->IFFT(std::move(a_evals))\n        .TakeCoefficients()\n        .TakeCoefficients();\n  }\n\n  template <typename Domain>\n  static std::vector<F> ComputeHQuery(const Domain* domain, const F& t_x,\n                                      const F& x, const F& delta_inverse) {\n    TRACE_EVENT(\"ProofGeneration\", \"QAP::ComputeHQuery\");\n    std::vector<F> h_query(domain->size() - 1);\n    base::Parallelize(h_query, [&t_x, &x, &delta_inverse](absl::Span<F> chunk,\n                                                          size_t chunk_index,\n                                                          size_t chunk_size) {\n      size_t i = chunk_index * chunk_size;\n      F x_i = x.Pow(i);\n\n      // NOTE: It is not possible to have empty chunk so this is safe\n      for (size_t i = 0; i < chunk.size() - 1; ++i) {\n        // (xⁱ * t(x)) / δ\n        chunk[i] = x_i * t_x;\n        chunk[i] *= delta_inverse;\n        x_i *= x;\n      }\n      chunk.back() = std::move(x_i);\n      chunk.back() *= t_x;\n      chunk.back() *= delta_inverse;\n    });\n    return h_query;\n  }\n};\n\n}  // namespace tachyon::zk::r1cs\n\n#endif  // TACHYON_ZK_R1CS_CONSTRAINT_SYSTEM_QUADRATIC_ARITHMETIC_PROGRAM_H_\n"
  },
  {
    "path": "tachyon/zk/r1cs/constraint_system/synthesis_mode.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_ZK_R1CS_CONSTRAINT_SYSTEM_SYNTHESIS_MODE_H_\n#define TACHYON_ZK_R1CS_CONSTRAINT_SYSTEM_SYNTHESIS_MODE_H_\n\nnamespace tachyon::zk::r1cs {\n\nclass SynthesisMode {\n public:\n  enum class Type {\n    kSetup,\n    kProve,\n  };\n\n  SynthesisMode() = default;\n\n  constexpr static SynthesisMode Setup() { return SynthesisMode(Type::kSetup); }\n  constexpr static SynthesisMode Prove(bool construct_matrices) {\n    return SynthesisMode(Type::kProve, construct_matrices);\n  }\n\n  Type type() const { return type_; }\n\n  bool IsSetup() const { return type_ == Type::kSetup; }\n  bool IsProve() const { return type_ == Type::kProve; }\n\n  bool ShouldConstructMatrices() const {\n    if (type_ == Type::kSetup) return true;\n    return construct_matrices_;\n  }\n\n private:\n  constexpr explicit SynthesisMode(Type type) : type_(type) {}\n  constexpr SynthesisMode(Type type, bool construct_matrices)\n      : type_(type), construct_matrices_(construct_matrices) {}\n\n  Type type_ = Type::kSetup;\n  bool construct_matrices_ = false;\n};\n\n}  // namespace tachyon::zk::r1cs\n\n#endif  // TACHYON_ZK_R1CS_CONSTRAINT_SYSTEM_SYNTHESIS_MODE_H_\n"
  },
  {
    "path": "tachyon/zk/r1cs/constraint_system/term.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_ZK_R1CS_CONSTRAINT_SYSTEM_TERM_H_\n#define TACHYON_ZK_R1CS_CONSTRAINT_SYSTEM_TERM_H_\n\n#include <string>\n#include <utility>\n\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/zk/r1cs/constraint_system/variable.h\"\n\nnamespace tachyon::zk::r1cs {\n\ntemplate <typename F>\nstruct Term {\n  F coefficient;\n  Variable variable;\n\n  constexpr Term() = default;\n  constexpr explicit Term(const Variable& variable)\n      : coefficient(F::One()), variable(variable) {}\n  constexpr explicit Term(Variable&& variable)\n      : coefficient(F::One()), variable(std::move(variable)) {}\n  constexpr Term(const F& coefficient, const Variable& variable)\n      : coefficient(coefficient), variable(variable) {}\n  constexpr Term(F&& coefficient, const Variable& variable)\n      : coefficient(std::move(coefficient)), variable(variable) {}\n  constexpr Term(F&& coefficient, Variable&& variable)\n      : coefficient(std::move(coefficient)), variable(std::move(variable)) {}\n\n  Term operator-() const { return {-coefficient, variable}; }\n  Term& NegateInPlace() {\n    coefficient.NegateInPlace();\n    return *this;\n  }\n\n  Term operator*(const F& scalar) const {\n    return {this->coefficient * scalar, variable};\n  }\n  Term& operator*=(const F& scalar) {\n    this->coefficient *= scalar;\n    return *this;\n  }\n\n  bool operator==(const Term& other) const {\n    return coefficient == other.coefficient && variable == other.variable;\n  }\n  bool operator!=(const Term& other) const { return !operator==(other); }\n\n  std::string ToString() const {\n    return absl::Substitute(\"{coefficient: $0, variable: $1}\",\n                            coefficient.ToString(), variable.ToString());\n  }\n};\n\n}  // namespace tachyon::zk::r1cs\n\n#endif  // TACHYON_ZK_R1CS_CONSTRAINT_SYSTEM_TERM_H_\n"
  },
  {
    "path": "tachyon/zk/r1cs/constraint_system/test/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"simple_circuit\",\n    testonly = True,\n    hdrs = [\"simple_circuit.h\"],\n)\n"
  },
  {
    "path": "tachyon/zk/r1cs/constraint_system/test/simple_circuit.h",
    "content": "#ifndef TACHYON_ZK_R1CS_CONSTRAINT_SYSTEM_TEST_SIMPLE_CIRCUIT_H_\n#define TACHYON_ZK_R1CS_CONSTRAINT_SYSTEM_TEST_SIMPLE_CIRCUIT_H_\n\n#include <vector>\n\n#include \"tachyon/zk/r1cs/constraint_system/circuit.h\"\n\nnamespace tachyon::zk::r1cs {\n\ntemplate <typename F>\nclass SimpleCircuit : public Circuit<F> {\n public:\n  SimpleCircuit(const F& a, const F& b) : a_(a), b_(b) {}\n\n  const F& a() const { return a_; }\n  const F& b() const { return b_; }\n\n  std::vector<F> GetPublicInputs() const { return {a_ * b_}; }\n\n  void Synthesize(ConstraintSystem<F>& constraint_system) const override {\n    Variable a =\n        constraint_system.CreateWitnessVariable([this]() { return this->a(); });\n    Variable b =\n        constraint_system.CreateWitnessVariable([this]() { return this->b(); });\n    Variable c = constraint_system.CreateInstanceVariable(\n        [this]() { return this->a() * this->b(); });\n\n    constraint_system.EnforceConstraint(LinearCombination<F>({{F::One(), a}}),\n                                        LinearCombination<F>({{F::One(), b}}),\n                                        LinearCombination<F>({{F::One(), c}}));\n  }\n\n private:\n  F a_;\n  F b_;\n};\n\n}  // namespace tachyon::zk::r1cs\n\n#endif  // TACHYON_ZK_R1CS_CONSTRAINT_SYSTEM_TEST_SIMPLE_CIRCUIT_H_\n"
  },
  {
    "path": "tachyon/zk/r1cs/constraint_system/variable.cc",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#include \"tachyon/zk/r1cs/constraint_system/variable.h\"\n\n#include \"absl/strings/substitute.h\"\n\nnamespace tachyon::zk::r1cs {\n\n// static\nstd::string_view Variable::TypeToString(Variable::Type type) {\n  switch (type) {\n    case Variable::Type::kZero:\n      return \"Zero\";\n    case Variable::Type::kOne:\n      return \"One\";\n    case Variable::Type::kInstance:\n      return \"Instance\";\n    case Variable::Type::kWitness:\n      return \"Witness\";\n    case Variable::Type::kSymbolicLinearCombination:\n      return \"SymbolicLinearCombination\";\n  }\n  NOTREACHED();\n  return \"\";\n}\n\nstd::string Variable::ToString() const {\n  if (type_ == Variable::Type::kZero || type_ == Variable::Type::kOne) {\n    return absl::Substitute(\"{type: $0}\", TypeToString(type_));\n  }\n  return absl::Substitute(\"{type: $0, index: $1}\", TypeToString(type_), index_);\n}\n\n}  // namespace tachyon::zk::r1cs\n"
  },
  {
    "path": "tachyon/zk/r1cs/constraint_system/variable.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_ZK_R1CS_CONSTRAINT_SYSTEM_VARIABLE_H_\n#define TACHYON_ZK_R1CS_CONSTRAINT_SYSTEM_VARIABLE_H_\n\n#include <stddef.h>\n\n#include <optional>\n#include <string>\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/export.h\"\n\nnamespace tachyon::zk::r1cs {\n\nclass TACHYON_EXPORT Variable {\n public:\n  // THE ORDER OF ELEMENTS ARE IMPORTANT!! DO NOT CHANGE!\n  // The comparison operator depends on the order of elements.\n  enum class Type {\n    // Represents the \"zero\" constant.\n    kZero,\n    // Represents the \"one\" constant.\n    kOne,\n    // Represents a public instance variable.\n    kInstance,\n    // Represents a private witness variable.\n    kWitness,\n    // Represents of a linear combination.\n    kSymbolicLinearCombination,\n  };\n\n  static std::string_view TypeToString(Type type);\n\n  Variable() = default;\n\n  constexpr static Variable Zero() { return Variable(Type::kZero); }\n  constexpr static Variable One() { return Variable(Type::kOne); }\n  constexpr static Variable Instance(size_t index) {\n    return Variable(Type::kInstance, index);\n  }\n  constexpr static Variable Witness(size_t index) {\n    return Variable(Type::kWitness, index);\n  }\n  constexpr static Variable SymbolicLinearCombination(size_t index) {\n    return Variable(Type::kSymbolicLinearCombination, index);\n  }\n\n  Type type() const { return type_; }\n  size_t index() const { return index_; }\n\n  std::optional<size_t> GetIndex(size_t witness_offset) const {\n    switch (type_) {\n      case Type::kOne:\n        return 0;\n      case Type::kInstance:\n        return index_;\n      case Type::kWitness:\n        return witness_offset + index_;\n      case Type::kZero:\n      case Type::kSymbolicLinearCombination:\n        return std::nullopt;\n    }\n    NOTREACHED();\n    return std::nullopt;\n  }\n  std::optional<size_t> GetSymbolicLinearCombinationIndex() const {\n    if (IsSymbolicLinearCombination()) return index_;\n    return std::nullopt;\n  }\n\n  bool IsZero() const { return type_ == Type::kZero; }\n  bool IsOne() const { return type_ == Type::kOne; }\n  bool IsInstance() const { return type_ == Type::kInstance; }\n  bool IsWitness() const { return type_ == Type::kWitness; }\n  bool IsSymbolicLinearCombination() const {\n    return type_ == Type::kSymbolicLinearCombination;\n  }\n\n  bool operator==(const Variable& other) const {\n    return type_ == other.type_ && index_ == other.index_;\n  }\n  bool operator!=(const Variable& other) const { return !operator==(other); }\n  bool operator<(const Variable& other) const {\n    if (type_ == other.type_) {\n      switch (type_) {\n        case Type::kZero:\n        case Type::kOne:\n          return false;\n        case Type::kInstance:\n        case Type::kWitness:\n        case Type::kSymbolicLinearCombination:\n          return index_ < other.index_;\n      }\n      NOTREACHED();\n    }\n    return static_cast<int>(type_) < static_cast<int>(other.type_);\n  }\n  bool operator<=(const Variable& other) const {\n    if (type_ == other.type_) {\n      switch (type_) {\n        case Type::kZero:\n        case Type::kOne:\n          return true;\n        case Type::kInstance:\n        case Type::kWitness:\n        case Type::kSymbolicLinearCombination:\n          return index_ <= other.index_;\n      }\n      NOTREACHED();\n    }\n    return static_cast<int>(type_) < static_cast<int>(other.type_);\n  }\n  bool operator>(const Variable& other) const { return !operator<=(other); }\n  bool operator>=(const Variable& other) const { return !operator<(other); }\n\n  std::string ToString() const;\n\n private:\n  constexpr explicit Variable(Type type) : type_(type) {}\n  constexpr Variable(Type type, size_t index) : type_(type), index_(index) {}\n\n  Type type_ = Type::kZero;\n  size_t index_ = 0;\n};\n\n}  // namespace tachyon::zk::r1cs\n\n#endif  // TACHYON_ZK_R1CS_CONSTRAINT_SYSTEM_VARIABLE_H_\n"
  },
  {
    "path": "tachyon/zk/r1cs/constraint_system/variable_unittest.cc",
    "content": "#include \"tachyon/zk/r1cs/constraint_system/variable.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/random.h\"\n\nnamespace tachyon::zk::r1cs {\n\nTEST(VariableTest, Comparison) {\n  size_t a = base::Uniform(base::Range<size_t>());\n  size_t b = base::Uniform(base::Range<size_t>());\n\n  EXPECT_EQ(Variable::Zero(), Variable::Zero());\n  EXPECT_LT(Variable::Zero(), Variable::One());\n  EXPECT_LT(Variable::Zero(), Variable::Instance(a));\n  EXPECT_LT(Variable::Zero(), Variable::Witness(a));\n  EXPECT_LT(Variable::Zero(), Variable::SymbolicLinearCombination(a));\n\n  EXPECT_GT(Variable::One(), Variable::Zero());\n  EXPECT_EQ(Variable::One(), Variable::One());\n  EXPECT_LT(Variable::One(), Variable::Instance(a));\n  EXPECT_LT(Variable::One(), Variable::Witness(a));\n  EXPECT_LT(Variable::One(), Variable::SymbolicLinearCombination(a));\n\n  EXPECT_GT(Variable::Instance(a), Variable::Zero());\n  EXPECT_GT(Variable::Instance(a), Variable::One());\n  EXPECT_EQ(Variable::Instance(a), Variable::Instance(a));\n  EXPECT_EQ(Variable::Instance(a) < Variable::Instance(b), a < b);\n  EXPECT_LT(Variable::Instance(a), Variable::Witness(a));\n  EXPECT_LT(Variable::Instance(a), Variable::SymbolicLinearCombination(a));\n\n  EXPECT_GT(Variable::Witness(a), Variable::Zero());\n  EXPECT_GT(Variable::Witness(a), Variable::One());\n  EXPECT_GT(Variable::Witness(a), Variable::Instance(a));\n  EXPECT_EQ(Variable::Witness(a), Variable::Witness(a));\n  EXPECT_EQ(Variable::Witness(a) < Variable::Witness(b), a < b);\n  EXPECT_LT(Variable::Witness(a), Variable::SymbolicLinearCombination(a));\n\n  EXPECT_GT(Variable::SymbolicLinearCombination(a), Variable::Zero());\n  EXPECT_GT(Variable::SymbolicLinearCombination(a), Variable::One());\n  EXPECT_GT(Variable::SymbolicLinearCombination(a), Variable::Instance(a));\n  EXPECT_GT(Variable::SymbolicLinearCombination(a), Variable::Witness(a));\n  EXPECT_EQ(Variable::SymbolicLinearCombination(a),\n            Variable::SymbolicLinearCombination(a));\n  EXPECT_EQ(Variable::SymbolicLinearCombination(a) <\n                Variable::SymbolicLinearCombination(b),\n            a < b);\n}\n\n}  // namespace tachyon::zk::r1cs\n"
  },
  {
    "path": "tachyon/zk/r1cs/groth16/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"key\",\n    hdrs = [\"key.h\"],\n    deps = [\n        \":toxic_waste\",\n        \"//tachyon:export\",\n        \"//tachyon/math/elliptic_curves/msm:fixed_base_msm\",\n        \"//tachyon/math/polynomials/univariate:univariate_evaluation_domain\",\n        \"//tachyon/zk/r1cs/constraint_system:circuit\",\n        \"//tachyon/zk/r1cs/constraint_system:qap_instance_map_result\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"prepared_verifying_key\",\n    hdrs = [\"prepared_verifying_key.h\"],\n    deps = [\n        \":verifying_key\",\n        \"//tachyon/math/elliptic_curves/pairing\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"proof\",\n    hdrs = [\"proof.h\"],\n    deps = [\"@com_google_absl//absl/strings\"],\n)\n\ntachyon_cc_library(\n    name = \"prove\",\n    hdrs = [\"prove.h\"],\n    deps = [\n        \":proof\",\n        \":proving_key\",\n        \"//tachyon/base:optional\",\n        \"//tachyon/base:profiler\",\n        \"//tachyon/device/gpu:scoped_mem_pool\",\n        \"//tachyon/device/gpu:scoped_stream\",\n        \"//tachyon/math/elliptic_curves/msm:variable_base_msm\",\n        \"//tachyon/math/elliptic_curves/msm:variable_base_msm_gpu\",\n        \"//tachyon/zk/r1cs/constraint_system:qap_witness_map_result\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"proving_key\",\n    hdrs = [\"proving_key.h\"],\n    deps = [\n        \":verifying_key\",\n        \"//tachyon/base:maybe_owned\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"toxic_waste\",\n    hdrs = [\"toxic_waste.h\"],\n    deps = [\"@com_google_absl//absl/strings\"],\n)\n\ntachyon_cc_library(\n    name = \"verify\",\n    hdrs = [\"verify.h\"],\n    deps = [\n        \":prepared_verifying_key\",\n        \":proof\",\n        \"//tachyon/base:profiler\",\n        \"//tachyon/math/elliptic_curves/msm:variable_base_msm\",\n        \"//tachyon/math/geometry:point_conversions\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"verifying_key\",\n    hdrs = [\"verifying_key.h\"],\n    deps = [\n        \":key\",\n        \"//tachyon/base:maybe_owned\",\n        \"//tachyon/base:openmp_util\",\n        \"//tachyon/base:optional\",\n        \"//tachyon/base/strings:string_util\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"groth16_unittests\",\n    srcs = [\"groth16_unittest.cc\"],\n    deps = [\n        \":prove\",\n        \":verify\",\n        \"//tachyon/math/elliptic_curves/bn/bn254\",\n        \"//tachyon/math/polynomials/univariate:univariate_evaluation_domain_factory\",\n        \"//tachyon/zk/r1cs/constraint_system:quadratic_arithmetic_program\",\n        \"//tachyon/zk/r1cs/constraint_system/test:simple_circuit\",\n    ],\n)\n"
  },
  {
    "path": "tachyon/zk/r1cs/groth16/groth16_unittest.cc",
    "content": "#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/elliptic_curves/bn/bn254/bn254.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluation_domain_factory.h\"\n#include \"tachyon/zk/r1cs/constraint_system/quadratic_arithmetic_program.h\"\n#include \"tachyon/zk/r1cs/constraint_system/test/simple_circuit.h\"\n#include \"tachyon/zk/r1cs/groth16/prove.h\"\n#include \"tachyon/zk/r1cs/groth16/verify.h\"\n\nnamespace tachyon::zk::r1cs::groth16 {\n\nnamespace {\n\nusing F = math::bn254::Fr;\nusing Curve = math::bn254::BN254Curve;\n\nconstexpr size_t kMaxDegree = 31;\n\nclass Groth16Test : public testing::Test {\n public:\n  static void SetUpTestSuite() { Curve::Init(); }\n};\n\n}  // namespace\n\nTEST_F(Groth16Test, ProveAndVerify) {\n  SimpleCircuit<F> circuit(F::Random(), F::Random());\n  ToxicWaste<Curve> toxic_waste = ToxicWaste<Curve>::RandomWithoutX();\n  ProvingKey<Curve> pk;\n  bool loaded =\n      pk.Load<kMaxDegree, QuadraticArithmeticProgram<F>>(toxic_waste, circuit);\n  ASSERT_TRUE(loaded);\n  Proof<Curve> proof =\n      CreateProofWithReductionZK<kMaxDegree, QuadraticArithmeticProgram<F>>(\n          circuit, pk);\n  PreparedVerifyingKey<Curve> pvk =\n      std::move(pk).TakeVerifyingKey().ToPreparedVerifyingKey();\n  std::vector<F> public_inputs = circuit.GetPublicInputs();\n  ASSERT_TRUE(VerifyProof(pvk, proof, public_inputs));\n\n  proof = ReRandomizeProof(pvk.verifying_key(), proof);\n  ASSERT_TRUE(VerifyProof(pvk, proof, public_inputs));\n}\n\n}  // namespace tachyon::zk::r1cs::groth16\n"
  },
  {
    "path": "tachyon/zk/r1cs/groth16/key.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_ZK_R1CS_GROTH16_KEY_H_\n#define TACHYON_ZK_R1CS_GROTH16_KEY_H_\n\n#include <stddef.h>\n\n#include <algorithm>\n#include <functional>\n#include <memory>\n#include <numeric>\n#include <utility>\n#include <vector>\n\n#include \"absl/types/span.h\"\n\n#include \"tachyon/export.h\"\n#include \"tachyon/math/elliptic_curves/msm/fixed_base_msm.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluation_domain.h\"\n#include \"tachyon/zk/r1cs/constraint_system/circuit.h\"\n#include \"tachyon/zk/r1cs/constraint_system/qap_instance_map_result.h\"\n#include \"tachyon/zk/r1cs/groth16/toxic_waste.h\"\n\nnamespace tachyon::zk::r1cs::groth16 {\n\ntemplate <typename G1Point, size_t MaxDegree>\nstruct KeyPreLoadResult {\n  using F = typename G1Point::ScalarField;\n\n  std::unique_ptr<math::UnivariateEvaluationDomain<F, MaxDegree>> domain;\n  QAPInstanceMapResult<F> qap_instance_map_result;\n  math::FixedBaseMSM<G1Point> g1_msm;\n  size_t non_zero_b;\n};\n\nclass Key {\n protected:\n  template <typename QAP, typename Curve, typename F, typename G1Point,\n            size_t MaxDegree>\n  void PreLoad(ToxicWaste<Curve>& toxic_waste, const Circuit<F>& circuit,\n               KeyPreLoadResult<G1Point, MaxDegree>* result) {\n    using Domain = math::UnivariateEvaluationDomain<F, MaxDegree>;\n\n    ConstraintSystem<F> cs;\n    cs.set_optimization_goal(OptimizationGoal::kConstraints);\n    cs.set_mode(SynthesisMode::Setup());\n\n    circuit.Synthesize(cs);\n\n    cs.Finalize();\n\n    // TODO(chokobole): Apply |IcicleNTT|. Considering that the key is\n    // generated only once the circuit is constructed, this is not a high\n    // priority.\n    std::unique_ptr<Domain> domain =\n        Domain::Create(cs.num_constraints() + cs.num_instance_variables());\n\n    toxic_waste.SampleX(domain.get());\n\n    QAPInstanceMapResult<F> qap_instance_map_result =\n        QAP::InstanceMap(domain.get(), cs, toxic_waste.x());\n\n    size_t non_zero_a = CountNonZeros(qap_instance_map_result.num_qap_variables,\n                                      qap_instance_map_result.a);\n    size_t non_zero_b = CountNonZeros(qap_instance_map_result.num_qap_variables,\n                                      qap_instance_map_result.b);\n\n    result->g1_msm.Reset(non_zero_a + non_zero_b +\n                             qap_instance_map_result.num_qap_variables +\n                             domain->size() + 1,\n                         toxic_waste.g1_generator());\n    result->domain = std::move(domain);\n    result->qap_instance_map_result = std::move(qap_instance_map_result);\n    result->non_zero_b = non_zero_b;\n  }\n\n  template <typename F, typename Curve>\n  F ComputeABC(const F& a, const F& b, const F& c,\n               const ToxicWaste<Curve>& toxic_waste, const F& inverse) {\n    F ret = toxic_waste.beta() * a;\n    ret += toxic_waste.alpha() * b;\n    ret += c;\n    ret *= inverse;\n    return ret;\n  }\n\n private:\n  template <typename F>\n  static size_t CountNonZeros(size_t size, const std::vector<F>& vec) {\n    absl::Span<const F> subspan = absl::MakeConstSpan(vec).subspan(0, size);\n    std::vector<typename std::vector<F>::difference_type> results =\n        base::ParallelizeMap(subspan, [](absl::Span<const F> chunk) {\n          return std::count_if(chunk.begin(), chunk.end(),\n                               [](const F& value) { return !value.IsZero(); });\n        });\n    return std::accumulate(results.begin(), results.end(), size_t{0},\n                           std::plus<>());\n  }\n};\n\n}  // namespace tachyon::zk::r1cs::groth16\n\n#endif  // TACHYON_ZK_R1CS_GROTH16_KEY_H_\n"
  },
  {
    "path": "tachyon/zk/r1cs/groth16/prepared_verifying_key.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_ZK_R1CS_GROTH16_PREPARED_VERIFYING_KEY_H_\n#define TACHYON_ZK_R1CS_GROTH16_PREPARED_VERIFYING_KEY_H_\n\n#include <string>\n#include <utility>\n\n#include \"tachyon/zk/r1cs/groth16/verifying_key.h\"\n\nnamespace tachyon::zk::r1cs::groth16 {\n\ntemplate <typename Curve>\nclass PreparedVerifyingKey {\n public:\n  using G2Prepared = typename Curve::G2Prepared;\n  using Fp12 = typename Curve::Fp12;\n\n  PreparedVerifyingKey() = default;\n  PreparedVerifyingKey(const VerifyingKey<Curve>& verifying_key,\n                       Fp12&& alpha_g1_beta_g2, G2Prepared&& delta_neg_g2,\n                       G2Prepared&& gamma_neg_g2)\n      : verifying_key_(verifying_key),\n        alpha_g1_beta_g2_(std::move(alpha_g1_beta_g2)),\n        delta_neg_g2_(std::move(delta_neg_g2)),\n        gamma_neg_g2_(std::move(gamma_neg_g2)) {}\n  PreparedVerifyingKey(VerifyingKey<Curve>&& verifying_key,\n                       Fp12&& alpha_g1_beta_g2, G2Prepared&& delta_neg_g2,\n                       G2Prepared&& gamma_neg_g2)\n      : verifying_key_(std::move(verifying_key)),\n        alpha_g1_beta_g2_(std::move(alpha_g1_beta_g2)),\n        delta_neg_g2_(std::move(delta_neg_g2)),\n        gamma_neg_g2_(std::move(gamma_neg_g2)) {}\n\n  const VerifyingKey<Curve>& verifying_key() const { return verifying_key_; }\n  const Fp12& alpha_g1_beta_g2() const { return alpha_g1_beta_g2_; }\n  const G2Prepared& delta_neg_g2() const { return delta_neg_g2_; }\n  const G2Prepared& gamma_neg_g2() const { return gamma_neg_g2_; }\n\n  std::string ToString() const {\n    return absl::Substitute(\n        \"{verifying_key: $0, alpha_g1_beta_g2: $1, delta_neg_g2: $2, \"\n        \"gamma_neg_g2: $3}\",\n        verifying_key_.ToString(), alpha_g1_beta_g2_.ToString(),\n        delta_neg_g2_.ToString(), gamma_neg_g2_.ToString());\n  }\n\n  std::string ToHexString(bool pad_zero = false) const {\n    return absl::Substitute(\n        \"{verifying_key: $0, alpha_g1_beta_g2: $1, delta_neg_g2: $2, \"\n        \"gamma_neg_g2: $3}\",\n        verifying_key_.ToHexString(pad_zero),\n        alpha_g1_beta_g2_.ToHexString(pad_zero),\n        delta_neg_g2_.ToHexString(pad_zero),\n        gamma_neg_g2_.ToHexString(pad_zero));\n  }\n\n private:\n  VerifyingKey<Curve> verifying_key_;\n  // e([α]₁, [β]₂)\n  Fp12 alpha_g1_beta_g2_;\n  // [-δ]₂\n  G2Prepared delta_neg_g2_;\n  // [-γ]₂\n  G2Prepared gamma_neg_g2_;\n};\n\ntemplate <typename Curve>\nPreparedVerifyingKey<Curve> VerifyingKey<Curve>::ToPreparedVerifyingKey()\n    const& {\n  using G1AffinePoint = typename Curve::G1Curve::AffinePoint;\n  using G2AffinePoint = typename Curve::G2Curve::AffinePoint;\n  using G2Prepared = typename Curve::G2Prepared;\n  using Fp12 = typename Curve::Fp12;\n\n  G1AffinePoint left[] = {*alpha_g1_};\n  G2AffinePoint right[] = {*beta_g2_};\n  Fp12 alpha_g1_beta_g2 = math::Pairing<Curve>(left, right);\n  G2Prepared delta_neg_g2 = G2Prepared::From(-(*delta_g2_));\n  G2Prepared gamma_neg_g2 = G2Prepared::From(-(*gamma_g2_));\n\n  return {*this, std::move(alpha_g1_beta_g2), std::move(delta_neg_g2),\n          std::move(gamma_neg_g2)};\n}\n\ntemplate <typename Curve>\nPreparedVerifyingKey<Curve> VerifyingKey<Curve>::ToPreparedVerifyingKey() && {\n  using G1AffinePoint = typename Curve::G1Curve::AffinePoint;\n  using G2AffinePoint = typename Curve::G2Curve::AffinePoint;\n  using G2Prepared = typename Curve::G2Prepared;\n  using Fp12 = typename Curve::Fp12;\n\n  G1AffinePoint left[] = {std::move(*alpha_g1_)};\n  G2AffinePoint right[] = {std::move(*beta_g2_)};\n  Fp12 alpha_g1_beta_g2 = math::Pairing<Curve>(left, right);\n  G2Prepared delta_neg_g2 = G2Prepared::From(-(*delta_g2_));\n  G2Prepared gamma_neg_g2 = G2Prepared::From(-(*gamma_g2_));\n\n  return {std::move(*this), std::move(alpha_g1_beta_g2),\n          std::move(delta_neg_g2), std::move(gamma_neg_g2)};\n}\n\n}  // namespace tachyon::zk::r1cs::groth16\n\n#endif  // TACHYON_ZK_R1CS_GROTH16_PREPARED_VERIFYING_KEY_H_\n"
  },
  {
    "path": "tachyon/zk/r1cs/groth16/proof.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_ZK_R1CS_GROTH16_PROOF_H_\n#define TACHYON_ZK_R1CS_GROTH16_PROOF_H_\n\n#include <string>\n#include <utility>\n\n#include \"absl/strings/substitute.h\"\n\nnamespace tachyon::zk::r1cs::groth16 {\n\ntemplate <typename Curve>\nclass Proof {\n public:\n  using G1Point = typename Curve::G1Curve::AffinePoint;\n  using G2Point = typename Curve::G2Curve::AffinePoint;\n\n  Proof() = default;\n  Proof(const G1Point& a, const G2Point& b, const G1Point& c)\n      : a_(a), b_(b), c_(c) {}\n  Proof(G1Point&& a, G2Point&& b, G1Point&& c)\n      : a_(std::move(a)), b_(std::move(b)), c_(std::move(c)) {}\n\n  const G1Point& a() const { return a_; }\n  const G2Point& b() const { return b_; }\n  const G1Point& c() const { return c_; }\n\n  bool operator==(const Proof& other) const {\n    return a_ == other.a_ && b_ == other.b_ && c_ == other.c_;\n  }\n  bool operator!=(const Proof& other) const { return !operator==(other); }\n\n  std::string ToString() const {\n    return absl::Substitute(\"{a: $0, b: $1, c: $2}\", a_.ToString(),\n                            b_.ToString(), c_.ToString());\n  }\n\n  std::string ToHexString(bool pad_zero = false) const {\n    return absl::Substitute(\"{a: $0, b: $1, c: $2}\", a_.ToHexString(pad_zero),\n                            b_.ToHexString(pad_zero), c_.ToHexString(pad_zero));\n  }\n\n private:\n  G1Point a_;\n  G2Point b_;\n  G1Point c_;\n};\n\n}  // namespace tachyon::zk::r1cs::groth16\n\n#endif  // TACHYON_ZK_R1CS_GROTH16_PROOF_H_\n"
  },
  {
    "path": "tachyon/zk/r1cs/groth16/prove.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_ZK_R1CS_GROTH16_PROVE_H_\n#define TACHYON_ZK_R1CS_GROTH16_PROVE_H_\n\n#include <stddef.h>\n\n#include <limits>\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"absl/types/span.h\"\n\n#include \"tachyon/base/optional.h\"\n#include \"tachyon/base/profiler.h\"\n#include \"tachyon/math/elliptic_curves/msm/variable_base_msm.h\"\n#include \"tachyon/zk/r1cs/constraint_system/qap_witness_map_result.h\"\n#include \"tachyon/zk/r1cs/groth16/proof.h\"\n#include \"tachyon/zk/r1cs/groth16/proving_key.h\"\n\n#if TACHYON_CUDA\n#include \"tachyon/device/gpu/scoped_mem_pool.h\"\n#include \"tachyon/device/gpu/scoped_stream.h\"\n#include \"tachyon/math/elliptic_curves/msm/variable_base_msm_gpu.h\"\n#endif\n\nnamespace tachyon::zk::r1cs::groth16 {\n\ntemplate <typename MSM, typename Bucket, typename AffinePoint, typename F>\nBucket CalculateCoeff(MSM& msm, const Bucket& initial,\n                      absl::Span<const AffinePoint> query,\n                      const AffinePoint& vk_param,\n                      absl::Span<const F> assignments) {\n  Bucket acc;\n  CHECK(msm.Run(query.subspan(1), assignments, &acc))\n      << \"If you encounter this error with `--config cuda`, it indicates that \"\n         \"your GPU RAM is insufficient to hold the twiddle caches created \"\n         \"during the icicle NTT domain initialization. If you run this from \"\n         \"the circom prover, try using the `--disable_fast_twiddles_mode` \"\n         \"flag.\";\n\n  Bucket ret = initial + query[0];\n  ret += acc;\n  ret += vk_param;\n  return ret;\n}\n\ntemplate <typename Curve, typename F>\nProof<Curve> CreateProofWithAssignment(const ProvingKey<Curve>& pk, const F& r,\n                                       const F& s,\n                                       absl::Span<const F> h_coefficients,\n                                       absl::Span<const F> instance_assignments,\n                                       absl::Span<const F> witness_assignments,\n                                       absl::Span<const F> full_assignments) {\n  using G1AffinePoint = typename Curve::G1Curve::AffinePoint;\n  using G2AffinePoint = typename Curve::G2Curve::AffinePoint;\n\n  TRACE_EVENT(\"ProofGeneration\", \"Groth16::CreateProofWithAssignment\");\n\n#if TACHYON_CUDA\n  using G1Bucket = typename math::VariableBaseMSMGpu<G1AffinePoint>::Bucket;\n  using G2Bucket = typename math::VariableBaseMSMGpu<G2AffinePoint>::Bucket;\n\n  gpuMemPoolProps props = {gpuMemAllocationTypePinned,\n                           gpuMemHandleTypeNone,\n                           {gpuMemLocationTypeDevice, 0}};\n  device::gpu::ScopedMemPool mem_pool = device::gpu::CreateMemPool(&props);\n\n  uint64_t mem_pool_threshold = std::numeric_limits<uint64_t>::max();\n  gpuError_t error = gpuMemPoolSetAttribute(\n      mem_pool.get(), gpuMemPoolAttrReleaseThreshold, &mem_pool_threshold);\n  CHECK_EQ(error, gpuSuccess);\n  device::gpu::ScopedStream stream = device::gpu::CreateStream();\n\n  math::VariableBaseMSMGpu<G1AffinePoint> msm_g1(mem_pool.get(), stream.get());\n\n  device::gpu::ScopedMemPool mem_pool2 = device::gpu::CreateMemPool(&props);\n\n  error = gpuMemPoolSetAttribute(\n      mem_pool2.get(), gpuMemPoolAttrReleaseThreshold, &mem_pool_threshold);\n  CHECK_EQ(error, gpuSuccess);\n  device::gpu::ScopedStream stream2 = device::gpu::CreateStream();\n\n  math::VariableBaseMSMGpu<G2AffinePoint> msm_g2(mem_pool2.get(),\n                                                 stream2.get());\n#else\n  using G1Bucket = typename math::VariableBaseMSM<G1AffinePoint>::Bucket;\n  using G2Bucket = typename math::VariableBaseMSM<G2AffinePoint>::Bucket;\n\n  math::VariableBaseMSM<G1AffinePoint> msm_g1;\n  math::VariableBaseMSM<G2AffinePoint> msm_g2;\n#endif\n\n  // |witness_acc| = [Σᵢ₌ₗ₊₁..ₘ (β * aᵢ(x) + α * bᵢ(x) + cᵢ(x)) / δ]₁\n  G1Bucket witness_acc;\n  CHECK(msm_g1.Run(pk.l_g1_query(), witness_assignments, &witness_acc));\n\n  // |h_acc| = [(h(x) * t(x)) / δ]₁\n  G1Bucket h_acc;\n  if (h_coefficients.size() > pk.h_g1_query().size()) {\n    absl::Span<const F> h_coefficients_subspan =\n        h_coefficients.subspan(0, h_coefficients.size() - 1);\n    CHECK(msm_g1.Run(pk.h_g1_query(), h_coefficients_subspan, &h_acc));\n  } else {\n    absl::Span<const G1AffinePoint> h_g1_query_subspan =\n        pk.h_g1_query().subspan(0, h_coefficients.size());\n    CHECK(msm_g1.Run(h_g1_query_subspan, h_coefficients, &h_acc));\n  }\n\n  G1Bucket ac_g1_bucket[2];\n\n  // |r_delta_g1_bucket| = [rδ]₁\n  G1Bucket r_delta_g1_bucket = math::ConvertPoint<G1Bucket>(r * pk.delta_g1());\n  // |ac_g1_bucket[0]| = [A]₁ = [α + Σᵢ₌₀..ₘ (xᵢ * aᵢ(x)) + rδ]₁\n  // where x is |full_assignments|.\n  ac_g1_bucket[0] =\n      CalculateCoeff(msm_g1, r_delta_g1_bucket, pk.a_g1_query(),\n                     pk.verifying_key().alpha_g1(), full_assignments);\n\n  // |s_delta_g2_bucket| = [sδ]₂\n  G2Bucket s_delta_g2_bucket =\n      math::ConvertPoint<G2Bucket>(s * pk.verifying_key().delta_g2());\n  // |b_g2_bucket| = [B]₂ = [β + Σᵢ₌₀..ₘ (xᵢ * bᵢ(x)) + sδ]₂\n  // where x is |full_assignments|.\n  G2Bucket b_g2_bucket =\n      CalculateCoeff(msm_g2, s_delta_g2_bucket, pk.b_g2_query(),\n                     pk.verifying_key().beta_g2(), full_assignments);\n\n  // |ac_g1_bucket[1]| = [As]₁\n  ac_g1_bucket[1] = ac_g1_bucket[0] * s;\n  // |ac_g1_bucket[1]| = [As + Br - rsδ]₁\n  if (!r.IsZero()) {\n    // |s_delta_g1_bucket| = [sδ]₁\n    G1Bucket s_delta_g1_bucket =\n        math::ConvertPoint<G1Bucket>(s * pk.delta_g1());\n    // |b_g1_bucket| = [B]₁ = [β + Σᵢ₌₀..ₘ (xᵢ * bᵢ(x)) + sδ]₁\n    // where x is |full_assignments|.\n    G1Bucket b_g1_bucket =\n        CalculateCoeff(msm_g1, s_delta_g1_bucket, pk.b_g1_query(), pk.beta_g1(),\n                       full_assignments);\n    ac_g1_bucket[1] += (r * b_g1_bucket);\n    ac_g1_bucket[1] -= (s * r_delta_g1_bucket);\n  }\n  // clang-format off\n  // |ac_g1_bucket[1]| = [Σᵢ₌ₗ₊₁..ₘ (β * aᵢ(x) + α * bᵢ(x) + cᵢ(x)) / δ + As + Br - rsδ]₁\n  // clang-format on\n  ac_g1_bucket[1] += witness_acc;\n  // clang-format off\n  // |ac_g1_bucket[1]| = [C]₁ = [(Σᵢ₌ₗ₊₁..ₘ (β * aᵢ(x) + α * bᵢ(x) + cᵢ(x)) + h(x)t(x)) / δ + As + Br - rsδ]₁\n  // clang-format on\n  ac_g1_bucket[1] += h_acc;\n\n  G1AffinePoint ac_g1[2];\n  CHECK(G1Bucket::BatchNormalize(ac_g1_bucket, &ac_g1));\n\n  return {\n      std::move(ac_g1[0]),\n      b_g2_bucket.ToAffine(),\n      std::move(ac_g1[1]),\n  };\n}\n\ntemplate <typename Curve, typename F>\nProof<Curve> CreateProofWithAssignmentZK(\n    const ProvingKey<Curve>& pk, absl::Span<const F> h_coefficients,\n    absl::Span<const F> instance_assignments,\n    absl::Span<const F> witness_assignments,\n    absl::Span<const F> full_assignments) {\n  return CreateProofWithAssignment(pk, F::Random(), F::Random(), h_coefficients,\n                                   instance_assignments, witness_assignments,\n                                   full_assignments);\n}\n\ntemplate <typename Curve, typename F>\nProof<Curve> CreateProofWithAssignmentNoZK(\n    const ProvingKey<Curve>& pk, absl::Span<const F> h_coefficients,\n    absl::Span<const F> instance_assignments,\n    absl::Span<const F> witness_assignments,\n    absl::Span<const F> full_assignments) {\n  return CreateProofWithAssignment(pk, F::Zero(), F::Zero(), h_coefficients,\n                                   instance_assignments, witness_assignments,\n                                   full_assignments);\n}\n\n// Create a Groth16 proof using randomness |r| and |s| and the provided\n// R1CS-to-QAP reduction.\ntemplate <size_t MaxDegree, typename QAP, typename F, typename Curve>\nProof<Curve> CreateProofWithReduction(const Circuit<F>& circuit,\n                                      const ProvingKey<Curve>& pk, const F& r,\n                                      const F& s) {\n  using Domain = math::UnivariateEvaluationDomain<F, MaxDegree>;\n\n  TRACE_EVENT(\"ProofGeneration\", \"Groth16::CreateProofWithReduction\");\n\n  ConstraintSystem<F> cs;\n  cs.set_optimization_goal(OptimizationGoal::kConstraints);\n\n  circuit.Synthesize(cs);\n\n  cs.Finalize();\n\n  // TODO(chokobole): Apply |IcicleNTT|. It is tricky because we can't get the\n  // domain size before calling |CreateProofWithReduction()|.\n  std::unique_ptr<Domain> domain =\n      Domain::Create(cs.num_constraints() + cs.num_instance_variables());\n\n  QAPWitnessMapResult<F> result = QAP::WitnessMap(domain.get(), cs);\n\n  const std::vector<F>& instance_assignments = cs.instance_assignments();\n  const std::vector<F>& witness_assignments = cs.witness_assignments();\n  return CreateProofWithAssignment(\n      pk, r, s, absl::MakeConstSpan(result.h),\n      absl::MakeConstSpan(instance_assignments).subspan(1),\n      absl::MakeConstSpan(witness_assignments),\n      absl::MakeConstSpan(result.full_assignments).subspan(1));\n}\n\n// Create a Groth16 proof that is zero-knowledge using the provided\n// R1CS-to-QAP reduction.\ntemplate <size_t MaxDegree, typename QAP, typename F, typename Curve>\nProof<Curve> CreateProofWithReductionZK(const Circuit<F>& circuit,\n                                        const ProvingKey<Curve>& pk) {\n  return CreateProofWithReduction<MaxDegree, QAP>(circuit, pk, F::Random(),\n                                                  F::Random());\n}\n\n// Create a Groth16 proof that isn't zero-knowledge using the provided\n// R1CS-to-QAP reduction.\ntemplate <size_t MaxDegree, typename QAP, typename F, typename Curve>\nProof<Curve> CreateProofWithReductionNoZK(const Circuit<F>& circuit,\n                                          const ProvingKey<Curve>& pk) {\n  return CreateProofWithReduction<MaxDegree, QAP>(circuit, pk, F::Zero(),\n                                                  F::Zero());\n}\n\n// Given a Groth16 proof, returns a fresh proof of the same statement. For a\n// proof π of a statement S, the output of the non-deterministic procedure\n// |ReRandomizeProof()| is statistically indistinguishable from a fresh\n// honest proof of S. For more info, see theorem 3 of\n// `https://eprint.iacr.org/2020/811.\ntemplate <typename Curve>\nProof<Curve> ReRandomizeProof(const VerifyingKey<Curve>& vk,\n                              const Proof<Curve>& proof) {\n  using G1AffinePoint = typename Curve::G1Curve::AffinePoint;\n  using G1JacobianPoint = typename Curve::G1Curve::JacobianPoint;\n  using G2JacobianPoint = typename Curve::G2Curve::JacobianPoint;\n  using F = typename G1AffinePoint::ScalarField;\n\n  TRACE_EVENT(\"ProofGeneration\", \"Groth16::ReRandomizeProof\");\n\n  struct Randoms {\n    F r1;\n    F r2;\n\n    bool AreZeroes() const { return r1.IsZero() && r2.IsZero(); }\n\n    void Sample() {\n      r1 = F::Random();\n      r2 = F::Random();\n    }\n  };\n\n  Randoms randoms;\n  while (randoms.AreZeroes()) {\n    randoms.Sample();\n  }\n\n  // See figure 1 in the paper referenced above:\n  //   A' = (1 / r₁)A\n  //   B' = r₁B + r₁r₂(δG₂)\n  //   C' = C + r₂A\n  G1JacobianPoint ac_jacobian[2];\n  F inv = unwrap(randoms.r1.Inverse());\n  ac_jacobian[0] = proof.a() * inv;\n\n  ac_jacobian[1] = proof.a() * randoms.r2;\n  ac_jacobian[1] += proof.c();\n\n  G1AffinePoint ac[2];\n  CHECK(G1JacobianPoint::BatchNormalize(ac_jacobian, &ac));\n\n  G2JacobianPoint b = vk.delta_g2() * randoms.r2;\n  b += proof.b();\n  b *= randoms.r1;\n\n  return {\n      std::move(ac[0]),\n      b.ToAffine(),\n      std::move(ac[1]),\n  };\n}\n\n}  // namespace tachyon::zk::r1cs::groth16\n\n#endif  // TACHYON_ZK_R1CS_GROTH16_PROVE_H_\n"
  },
  {
    "path": "tachyon/zk/r1cs/groth16/proving_key.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_ZK_R1CS_GROTH16_PROVING_KEY_H_\n#define TACHYON_ZK_R1CS_GROTH16_PROVING_KEY_H_\n\n#include <stddef.h>\n\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/maybe_owned.h\"\n#include \"tachyon/zk/r1cs/groth16/verifying_key.h\"\n\nnamespace tachyon::zk::r1cs::groth16 {\n\ntemplate <typename Curve>\nclass ProvingKey : public Key {\n public:\n  using G1Point = typename Curve::G1Curve::AffinePoint;\n  using G2Point = typename Curve::G2Curve::AffinePoint;\n  using F = typename G1Point::ScalarField;\n\n  ProvingKey() = default;\n  ProvingKey(const VerifyingKey<Curve>& verifying_key, const G1Point& beta_g1,\n             const G1Point& delta_g1, const std::vector<G1Point>& a_g1_query,\n             const std::vector<G1Point>& b_g1_query,\n             const std::vector<G2Point>& b_g2_query,\n             const std::vector<G1Point>& h_g1_query,\n             const std::vector<G1Point>& l_g1_query)\n      : verifying_key_(verifying_key),\n        beta_g1_(beta_g1),\n        delta_g1_(delta_g1),\n        a_g1_query_(a_g1_query),\n        b_g1_query_(b_g1_query),\n        b_g2_query_(b_g2_query),\n        h_g1_query_(h_g1_query),\n        l_g1_query_(l_g1_query) {}\n  ProvingKey(VerifyingKey<Curve>&& verifying_key, G1Point&& beta_g1,\n             G1Point&& delta_g1, std::vector<G1Point>&& a_g1_query,\n             std::vector<G1Point>&& b_g1_query,\n             std::vector<G2Point>&& b_g2_query,\n             std::vector<G1Point>&& h_g1_query,\n             std::vector<G1Point>&& l_g1_query)\n      : verifying_key_(std::move(verifying_key)),\n        beta_g1_(std::move(beta_g1)),\n        delta_g1_(std::move(delta_g1)),\n        a_g1_query_(std::move(a_g1_query)),\n        b_g1_query_(std::move(b_g1_query)),\n        b_g2_query_(std::move(b_g2_query)),\n        h_g1_query_(std::move(h_g1_query)),\n        l_g1_query_(std::move(l_g1_query)) {}\n  ProvingKey(const VerifyingKey<Curve>& verifying_key, G1Point* beta_g1,\n             G1Point* delta_g1, absl::Span<G1Point> a_g1_query,\n             absl::Span<G1Point> b_g1_query, absl::Span<G2Point> b_g2_query,\n             absl::Span<G1Point> h_g1_query, absl::Span<G1Point> l_g1_query)\n      : verifying_key_(verifying_key),\n        beta_g1_(beta_g1),\n        delta_g1_(delta_g1),\n        a_g1_query_(a_g1_query),\n        b_g1_query_(b_g1_query),\n        b_g2_query_(b_g2_query),\n        h_g1_query_(h_g1_query),\n        l_g1_query_(l_g1_query) {}\n\n  const VerifyingKey<Curve>& verifying_key() const { return verifying_key_; }\n  VerifyingKey<Curve>&& TakeVerifyingKey() && {\n    return std::move(verifying_key_);\n  }\n\n  const G1Point& beta_g1() const { return *beta_g1_; }\n  const G1Point& delta_g1() const { return *delta_g1_; }\n  absl::Span<const G1Point> a_g1_query() const { return *a_g1_query_; }\n  absl::Span<const G1Point> b_g1_query() const { return *b_g1_query_; }\n  absl::Span<const G2Point> b_g2_query() const { return *b_g2_query_; }\n  absl::Span<const G1Point> h_g1_query() const { return *h_g1_query_; }\n  absl::Span<const G1Point> l_g1_query() const { return *l_g1_query_; }\n\n  template <size_t MaxDegree, typename QAP>\n  [[nodiscard]] bool Load(ToxicWaste<Curve>& toxic_waste,\n                          const Circuit<F>& circuit) {\n    using G1JacobianPoint = typename Curve::G1Curve::JacobianPoint;\n    using G2JacobianPoint = typename Curve::G2Curve::JacobianPoint;\n\n    F gamma_delta_inverse[] = {\n        toxic_waste.gamma(),\n        toxic_waste.delta(),\n    };\n    CHECK(F::BatchInverseInPlace(gamma_delta_inverse));\n\n    KeyPreLoadResult<G1Point, MaxDegree> result;\n    PreLoad<QAP>(toxic_waste, circuit, &result);\n\n    QAPInstanceMapResult<F>& qap_instance_map_result =\n        result.qap_instance_map_result;\n    math::FixedBaseMSM<G1Point>& g1_msm = result.g1_msm;\n\n    if (!verifying_key_.Load(toxic_waste, result, gamma_delta_inverse[0]))\n      return false;\n\n    const F& delta_inverse = gamma_delta_inverse[1];\n\n    // |h[i]| = (xⁱ * t(x)) / δ\n    std::vector<F> h =\n        QAP::ComputeHQuery(result.domain.get(), qap_instance_map_result.t_x,\n                           toxic_waste.x(), delta_inverse);\n\n    // |l[i]| = (β * aᵢ(x) + α * bᵢ(x) + cᵢ(x)) / δ\n    std::vector<F> l(qap_instance_map_result.num_witness_variables);\n    size_t num_instance_variables =\n        qap_instance_map_result.num_instance_variables;\n    std::vector<F>& a = qap_instance_map_result.a;\n    std::vector<F>& b = qap_instance_map_result.b;\n    std::vector<F>& c = qap_instance_map_result.c;\n    OMP_PARALLEL_FOR(size_t i = 0; i < l.size(); ++i) {\n      l[i] = ComputeABC(\n          a[num_instance_variables + i], b[num_instance_variables + i],\n          c[num_instance_variables + i], toxic_waste, delta_inverse);\n    }\n\n    math::FixedBaseMSM<G2Point> g2_msm;\n    g2_msm.Reset(result.non_zero_b, toxic_waste.g2_generator());\n\n    std::vector<G1JacobianPoint> a_g1_query_jacobian(\n        qap_instance_map_result.a.size());\n    if (!g1_msm.Run(a, &a_g1_query_jacobian)) return false;\n    a.clear();\n\n    std::vector<G1JacobianPoint> b_g1_query_jacobian(b.size());\n    if (!g1_msm.Run(b, &b_g1_query_jacobian)) return false;\n\n    std::vector<G2JacobianPoint> b_g2_query_jacobian(b.size());\n    if (!g2_msm.Run(b, &b_g2_query_jacobian)) return false;\n    b.clear();\n\n    std::vector<G1JacobianPoint> h_g1_query_jacobian(h.size());\n    if (!g1_msm.Run(h, &h_g1_query_jacobian)) return false;\n    h.clear();\n\n    std::vector<G1JacobianPoint> l_g1_query_jacobian(l.size());\n    if (!g1_msm.Run(l, &l_g1_query_jacobian)) return false;\n    l.clear();\n\n    G1JacobianPoint beta_delta_jacobian[] = {\n        toxic_waste.beta() * toxic_waste.g1_generator(),\n        toxic_waste.delta() * toxic_waste.g1_generator(),\n    };\n    G1Point beta_delta[2];\n    if (!G1JacobianPoint::BatchNormalize(beta_delta_jacobian, &beta_delta))\n      return false;\n\n    beta_g1_ = std::move(beta_delta[0]);\n    delta_g1_ = std::move(beta_delta[1]);\n\n    std::vector<G1Point> a_g1_query(a_g1_query_jacobian.size());\n    if (!G1JacobianPoint::BatchNormalize(a_g1_query_jacobian, &a_g1_query))\n      return false;\n    std::vector<G1Point> b_g1_query(b_g1_query_jacobian.size());\n    if (!G1JacobianPoint::BatchNormalize(b_g1_query_jacobian, &b_g1_query))\n      return false;\n    std::vector<G2Point> b_g2_query(b_g2_query_jacobian.size());\n    if (!G2JacobianPoint::BatchNormalize(b_g2_query_jacobian, &b_g2_query))\n      return false;\n    std::vector<G1Point> h_g1_query(h_g1_query_jacobian.size());\n    if (!G1JacobianPoint::BatchNormalize(h_g1_query_jacobian, &h_g1_query))\n      return false;\n    std::vector<G1Point> l_g1_query(l_g1_query_jacobian.size());\n    if (!G1JacobianPoint::BatchNormalize(l_g1_query_jacobian, &l_g1_query))\n      return false;\n    a_g1_query_ = std::move(a_g1_query);\n    b_g1_query_ = std::move(b_g1_query);\n    b_g2_query_ = std::move(b_g2_query);\n    h_g1_query_ = std::move(h_g1_query);\n    l_g1_query_ = std::move(l_g1_query);\n    return true;\n  }\n\n  std::string ToString() const {\n    return absl::Substitute(\n        \"{verifying_key: $0, beta_g1: $1, delta_g1: $2, a_1_query: $3, \"\n        \"b_g1_query: $4, b_g2_query: $5, h_g1_query: $6, l_g1_query: $7}\",\n        verifying_key_.ToString(), beta_g1_.ToString(), delta_g1_.ToString(),\n        base::ContainerToString(a_g1_query_),\n        base::ContainerToString(b_g1_query_),\n        base::ContainerToString(b_g2_query_),\n        base::ContainerToString(h_g1_query_),\n        base::ContainerToString(l_g1_query_));\n  }\n\n  std::string ToHexString(bool pad_zero = false) const {\n    return absl::Substitute(\n        \"{verifying_key: $0, beta_g1: $1, delta_g1: $2, a_1_query: $3, \"\n        \"b_g1_query: $4, b_g2_query: $5, h_g1_query: $6, l_g1_query: $7}\",\n        verifying_key_.ToHexString(pad_zero), beta_g1_.ToHexString(pad_zero),\n        delta_g1_.ToHexString(pad_zero),\n        base::ContainerToHexString(a_g1_query_, pad_zero),\n        base::ContainerToHexString(b_g1_query_, pad_zero),\n        base::ContainerToHexString(b_g2_query_, pad_zero),\n        base::ContainerToHexString(h_g1_query_, pad_zero),\n        base::ContainerToHexString(l_g1_query_, pad_zero));\n  }\n\n private:\n  VerifyingKey<Curve> verifying_key_;\n  // [β]₁\n  base::MaybeOwned<G1Point> beta_g1_;\n  // [δ]₁\n  base::MaybeOwned<G1Point> delta_g1_;\n  // |a_g1_query_[i]| = [aᵢ(x)]₁\n  base::MaybeOwned<std::vector<G1Point>> a_g1_query_;\n  // |b_g1_query_[i]| = [bᵢ(x)]₁\n  base::MaybeOwned<std::vector<G1Point>> b_g1_query_;\n  // |b_g2_query_[i]| = [bᵢ(x)]₂\n  base::MaybeOwned<std::vector<G2Point>> b_g2_query_;\n  // |h_g1_query_[i]| = [(xⁱ * t(x)) / δ]₁\n  base::MaybeOwned<std::vector<G1Point>> h_g1_query_;\n  // |l_g1_query_[i]| = [(β * aᵢ(x) + α * bᵢ(x) + cᵢ(x)) / δ]₁\n  base::MaybeOwned<std::vector<G1Point>> l_g1_query_;\n};\n\n}  // namespace tachyon::zk::r1cs::groth16\n\n#endif  // TACHYON_ZK_R1CS_GROTH16_PROVING_KEY_H_\n"
  },
  {
    "path": "tachyon/zk/r1cs/groth16/toxic_waste.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_ZK_R1CS_GROTH16_TOXIC_WASTE_H_\n#define TACHYON_ZK_R1CS_GROTH16_TOXIC_WASTE_H_\n\n#include <string>\n#include <utility>\n\n#include \"absl/strings/substitute.h\"\n\nnamespace tachyon::zk::r1cs::groth16 {\n\ntemplate <typename Curve>\nclass ToxicWaste {\n public:\n  using G1Point = typename Curve::G1Curve::AffinePoint;\n  using G2Point = typename Curve::G2Curve::AffinePoint;\n  using F = typename G1Point::ScalarField;\n\n  ToxicWaste(const F& alpha, const F& beta, const F& gamma, const F& delta,\n             const F& x, const G1Point& g1_generator,\n             const G2Point& g2_generator)\n      : alpha_(alpha),\n        beta_(beta),\n        gamma_(gamma),\n        delta_(delta),\n        x_(x),\n        g1_generator_(g1_generator),\n        g2_generator_(g2_generator) {}\n  ToxicWaste(F&& alpha, F&& beta, F&& gamma, F&& delta, F&& x,\n             G1Point&& g1_generator, G2Point&& g2_generator)\n      : alpha_(std::move(alpha)),\n        beta_(std::move(beta)),\n        gamma_(std::move(gamma)),\n        delta_(std::move(delta)),\n        x_(std::move(x)),\n        g1_generator_(std::move(g1_generator)),\n        g2_generator_(std::move(g2_generator)) {}\n\n  static ToxicWaste RandomWithoutX() {\n    return {F::Random(), F::Random(),       F::Random(),      F::Random(),\n            F::Zero(),   G1Point::Random(), G2Point::Random()};\n  }\n\n  const F& alpha() const { return alpha_; }\n  const F& beta() const { return beta_; }\n  const F& gamma() const { return gamma_; }\n  const F& delta() const { return delta_; }\n  const F& x() const { return x_; }\n  const G1Point& g1_generator() const { return g1_generator_; }\n  const G2Point& g2_generator() const { return g2_generator_; }\n\n  template <typename Domain>\n  void SampleX(const Domain* domain) {\n    x_ = domain->SampleElementOutsideDomain();\n  }\n\n  std::string ToString() const {\n    return absl::Substitute(\n        \"{alpha: $0, beta: $1, gamma: $2, delta: $3, x: $4, g1_generator: $5, \"\n        \"g2_generator: $6}\",\n        alpha_.ToString(), beta_.ToString(), gamma_.ToString(),\n        delta_.ToString(), x_.ToString(), g1_generator_.ToString(),\n        g2_generator_.ToString());\n  }\n\n  std::string ToHexString(bool pad_zero = false) const {\n    return absl::Substitute(\n        \"{alpha: $0, beta: $1, gamma: $2, delta: $3, x: $4, g1_generator: $5, \"\n        \"g2_generator: $6}\",\n        alpha_.ToHexString(pad_zero), beta_.ToHexString(pad_zero),\n        gamma_.ToHexString(pad_zero), delta_.ToHexString(pad_zero),\n        x_.ToHexString(pad_zero), g1_generator_.ToHexString(pad_zero),\n        g2_generator_.ToHexString(pad_zero));\n  }\n\n private:\n  F alpha_;\n  F beta_;\n  F gamma_;\n  F delta_;\n  F x_;\n  G1Point g1_generator_;\n  G2Point g2_generator_;\n};\n\n}  // namespace tachyon::zk::r1cs::groth16\n\n#endif  // TACHYON_ZK_R1CS_GROTH16_TOXIC_WASTE_H_\n"
  },
  {
    "path": "tachyon/zk/r1cs/groth16/verify.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_ZK_R1CS_GROTH16_VERIFY_H_\n#define TACHYON_ZK_R1CS_GROTH16_VERIFY_H_\n\n#include \"absl/types/span.h\"\n\n#include \"tachyon/base/profiler.h\"\n#include \"tachyon/math/elliptic_curves/msm/variable_base_msm.h\"\n#include \"tachyon/math/elliptic_curves/pairing/pairing.h\"\n#include \"tachyon/math/geometry/point_conversions.h\"\n#include \"tachyon/zk/r1cs/groth16/prepared_verifying_key.h\"\n#include \"tachyon/zk/r1cs/groth16/proof.h\"\n\nnamespace tachyon::zk::r1cs::groth16 {\n\ntemplate <typename Curve, typename Container, typename Bucket>\n[[nodiscard]] bool PrepareInputs(const PreparedVerifyingKey<Curve>& pvk,\n                                 const Container& public_inputs,\n                                 Bucket* prepared_inputs) {\n  using G1Point = typename Curve::G1Curve::AffinePoint;\n\n  TRACE_EVENT(\"ProofVerification\", \"Groth16::PrepareInputs\");\n\n  absl::Span<const G1Point> l_g1_query = pvk.verifying_key().l_g1_query();\n  absl::Span<const G1Point> l_g1_query_first_skipped = l_g1_query.subspan(1);\n  math::VariableBaseMSM<G1Point> msm;\n  if (!msm.Run(l_g1_query_first_skipped, public_inputs, prepared_inputs))\n    return false;\n  *prepared_inputs += l_g1_query[0];\n  return true;\n}\n\ntemplate <typename Curve, typename Bucket>\n[[nodiscard]] bool VerifyProofWithPreparedInputs(\n    const PreparedVerifyingKey<Curve>& pvk, const Proof<Curve>& proof,\n    const Bucket& prepared_inputs) {\n  using G1Point = typename Curve::G1Curve::AffinePoint;\n  using G2Prepared = typename Curve::G2Prepared;\n\n  TRACE_EVENT(\"ProofVerification\", \"Groth16::VerifyProofWithPreparedInputs\");\n\n  // clang-format off\n  // e(A, B) * e([Σ xᵢ * (γ⁻¹ * (β * aᵢ(x) + α * bᵢ(x) + cᵢ(x))]₁, [-γ]₂) * e(C, [-δ]₂) ≟ e([α]₁, [β]₂)\n  // clang-format on\n  G1Point g1[] = {\n      proof.a(),\n      math::ConvertPoint<G1Point>(prepared_inputs),\n      proof.c(),\n  };\n  G2Prepared g2[] = {\n      G2Prepared::From(proof.b()),\n      pvk.gamma_neg_g2(),\n      pvk.delta_neg_g2(),\n  };\n  return math::Pairing<Curve>(g1, g2) == pvk.alpha_g1_beta_g2();\n}\n\ntemplate <typename Curve, typename Container>\n[[nodiscard]] bool VerifyProof(const PreparedVerifyingKey<Curve>& pvk,\n                               const Proof<Curve>& proof,\n                               const Container& public_inputs) {\n  using G1Point = typename Curve::G1Curve::AffinePoint;\n  using Bucket = typename math::VariableBaseMSM<G1Point>::Bucket;\n\n  TRACE_EVENT(\"ProofVerification\", \"Groth16::VerifyProof\");\n\n  Bucket prepared_inputs;\n  if (!PrepareInputs(pvk, public_inputs, &prepared_inputs)) return false;\n  return VerifyProofWithPreparedInputs(pvk, proof, prepared_inputs);\n}\n\n}  // namespace tachyon::zk::r1cs::groth16\n\n#endif  // TACHYON_ZK_R1CS_GROTH16_VERIFY_H_\n"
  },
  {
    "path": "tachyon/zk/r1cs/groth16/verifying_key.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef TACHYON_ZK_R1CS_GROTH16_VERIFYING_KEY_H_\n#define TACHYON_ZK_R1CS_GROTH16_VERIFYING_KEY_H_\n\n#include <stddef.h>\n\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/maybe_owned.h\"\n#include \"tachyon/base/openmp_util.h\"\n#include \"tachyon/base/optional.h\"\n#include \"tachyon/base/strings/string_util.h\"\n#include \"tachyon/zk/r1cs/groth16/key.h\"\n\nnamespace tachyon::zk::r1cs::groth16 {\n\ntemplate <typename Curve>\nclass PreparedVerifyingKey;\n\ntemplate <typename Curve>\nclass VerifyingKey : public Key {\n public:\n  using G1Point = typename Curve::G1Curve::AffinePoint;\n  using G2Point = typename Curve::G2Curve::AffinePoint;\n  using F = typename G1Point::ScalarField;\n\n  VerifyingKey() = default;\n  VerifyingKey(const G1Point& alpha_g1, const G2Point& beta_g2,\n               const G2Point& gamma_g2, const G2Point& delta_g2,\n               const std::vector<G1Point>& l_g1_query)\n      : alpha_g1_(alpha_g1),\n        beta_g2_(beta_g2),\n        gamma_g2_(gamma_g2),\n        delta_g2_(delta_g2),\n        l_g1_query_(l_g1_query) {}\n  VerifyingKey(G1Point&& alpha_g1, G2Point&& beta_g2, G2Point&& gamma_g2,\n               G2Point&& delta_g2, std::vector<G1Point>&& l_g1_query)\n      : alpha_g1_(std::move(alpha_g1)),\n        beta_g2_(std::move(beta_g2)),\n        gamma_g2_(std::move(gamma_g2)),\n        delta_g2_(std::move(delta_g2)),\n        l_g1_query_(std::move(l_g1_query)) {}\n  VerifyingKey(G1Point* alpha_g1, G2Point* beta_g2, G2Point* gamma_g2,\n               G2Point* delta_g2, absl::Span<G1Point> l_g1_query)\n      : alpha_g1_(alpha_g1),\n        beta_g2_(beta_g2),\n        gamma_g2_(gamma_g2),\n        delta_g2_(delta_g2),\n        l_g1_query_(l_g1_query) {}\n\n  const G1Point& alpha_g1() const { return *alpha_g1_; }\n  const G2Point& beta_g2() const { return *beta_g2_; }\n  const G2Point& gamma_g2() const { return *gamma_g2_; }\n  const G2Point& delta_g2() const { return *delta_g2_; }\n  absl::Span<const G1Point> l_g1_query() const { return *l_g1_query_; }\n\n  template <size_t MaxDegree, typename QAP>\n  [[nodiscard]] bool Load(ToxicWaste<Curve>& toxic_waste,\n                          const Circuit<F>& circuit) {\n    KeyPreLoadResult<G1Point, MaxDegree> result;\n    PreLoad<QAP>(toxic_waste, circuit, &result);\n    return Load(toxic_waste, result);\n  }\n\n  template <size_t MaxDegree>\n  [[nodiscard]] bool Load(const ToxicWaste<Curve>& toxic_waste,\n                          KeyPreLoadResult<G1Point, MaxDegree>& result) {\n    F gamma_inverse = unwrap(toxic_waste.gamma().Inverse());\n    return Load(toxic_waste, result, gamma_inverse);\n  }\n\n  template <size_t MaxDegree>\n  [[nodiscard]] bool Load(const ToxicWaste<Curve>& toxic_waste,\n                          KeyPreLoadResult<G1Point, MaxDegree>& result,\n                          const F& gamma_inverse) {\n    using G1JacobianPoint = typename Curve::G1Curve::JacobianPoint;\n    using G2JacobianPoint = typename Curve::G2Curve::JacobianPoint;\n\n    const QAPInstanceMapResult<F>& qap_instance_map_result =\n        result.qap_instance_map_result;\n    math::FixedBaseMSM<G1Point>& g1_msm = result.g1_msm;\n\n    // |l[i]| = (β * aᵢ(x) + α * bᵢ(x) + cᵢ(x)) / γ\n    std::vector<F> l(qap_instance_map_result.num_instance_variables);\n    const std::vector<F>& a = qap_instance_map_result.a;\n    const std::vector<F>& b = qap_instance_map_result.b;\n    const std::vector<F>& c = qap_instance_map_result.c;\n    OMP_PARALLEL_FOR(size_t i = 0; i < l.size(); ++i) {\n      l[i] = ComputeABC(a[i], b[i], c[i], toxic_waste, gamma_inverse);\n    }\n\n    std::vector<G1JacobianPoint> l_g1_query_jacobian(l.size());\n    if (!g1_msm.Run(l, &l_g1_query_jacobian)) return false;\n    l.clear();\n\n    alpha_g1_ = (toxic_waste.alpha() * toxic_waste.g1_generator()).ToAffine();\n\n    G2JacobianPoint beta_gamma_delta_jacobian[] = {\n        toxic_waste.beta() * toxic_waste.g2_generator(),\n        toxic_waste.gamma() * toxic_waste.g2_generator(),\n        toxic_waste.delta() * toxic_waste.g2_generator(),\n    };\n    G2Point beta_gamma_delta[3];\n    if (!G2JacobianPoint::BatchNormalize(beta_gamma_delta_jacobian,\n                                         &beta_gamma_delta))\n      return false;\n    beta_g2_ = std::move(beta_gamma_delta[0]);\n    gamma_g2_ = std::move(beta_gamma_delta[1]);\n    delta_g2_ = std::move(beta_gamma_delta[2]);\n\n    std::vector<G1Point> l_g1_query(l_g1_query_jacobian.size());\n    if (!G1JacobianPoint::BatchNormalize(l_g1_query_jacobian, &l_g1_query))\n      return false;\n    l_g1_query_ = std::move(l_g1_query);\n    return true;\n  }\n\n  PreparedVerifyingKey<Curve> ToPreparedVerifyingKey() const&;\n  PreparedVerifyingKey<Curve> ToPreparedVerifyingKey() &&;\n\n  std::string ToString() const {\n    return absl::Substitute(\n        \"{alpha_g1: $0, beta_g2: $1, gamma_g2: $2, delta_g2: $3, l_g1_query: \"\n        \"$4}\",\n        alpha_g1_->ToString(), beta_g2_->ToString(), gamma_g2_->ToString(),\n        delta_g2_->ToString(), base::ContainerToString(*l_g1_query_));\n  }\n\n  std::string ToHexString(bool pad_zero = false) const {\n    return absl::Substitute(\n        \"{alpha_g1: $0, beta_g2: $1, gamma_g2: $2, delta_g2: $3, l_g1_query: \"\n        \"$4}\",\n        alpha_g1_->ToHexString(pad_zero), beta_g2_->ToHexString(pad_zero),\n        gamma_g2_->ToHexString(pad_zero), delta_g2_->ToHexString(pad_zero),\n        base::ContainerToHexString(*l_g1_query_, pad_zero));\n  }\n\n private:\n  // [α]₁\n  base::MaybeOwned<G1Point> alpha_g1_;\n  // [β]₂\n  base::MaybeOwned<G2Point> beta_g2_;\n  // [γ]₂\n  base::MaybeOwned<G2Point> gamma_g2_;\n  // [δ]₂\n  base::MaybeOwned<G2Point> delta_g2_;\n  // |l_g1_query_[i]| = [(β * aᵢ(x) + α * bᵢ(x) + cᵢ(x)) / γ]₁\n  base::MaybeOwned<std::vector<G1Point>> l_g1_query_;\n};\n\n}  // namespace tachyon::zk::r1cs::groth16\n\n#endif  // TACHYON_ZK_R1CS_GROTH16_VERIFYING_KEY_H_\n"
  },
  {
    "path": "tachyon/zk/shuffle/BUILD.bazel",
    "content": "load(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"argument\",\n    hdrs = [\"argument.h\"],\n    deps = [\"//tachyon/zk/expressions:expression\"],\n)\n\ntachyon_cc_library(\n    name = \"evaluator\",\n    hdrs = [\"evaluator.h\"],\n    deps = [\n        \":prover\",\n        \"//tachyon/base:profiler\",\n        \"//tachyon/zk/plonk/vanishing:circuit_polynomial_builder_forward\",\n        \"//tachyon/zk/plonk/vanishing:graph_evaluator\",\n        \"//tachyon/zk/plonk/vanishing:vanishing_utils\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"opening_point_set\",\n    hdrs = [\"opening_point_set.h\"],\n)\n\ntachyon_cc_library(\n    name = \"pair\",\n    hdrs = [\"pair.h\"],\n    deps = [\"//tachyon/base/json\"],\n)\n\ntachyon_cc_library(\n    name = \"prover\",\n    hdrs = [\n        \"prover.h\",\n        \"prover_impl.h\",\n    ],\n    deps = [\n        \":argument\",\n        \":opening_point_set\",\n        \":pair\",\n        \"//tachyon/base:profiler\",\n        \"//tachyon/base:ref\",\n        \"//tachyon/base/containers:container_util\",\n        \"//tachyon/crypto/commitments:polynomial_openings\",\n        \"//tachyon/zk/base/entities:prover_base\",\n        \"//tachyon/zk/plonk/base:multi_phase_ref_table\",\n        \"//tachyon/zk/plonk/expressions:compress_expression\",\n        \"//tachyon/zk/plonk/expressions:proving_evaluator\",\n        \"//tachyon/zk/plonk/permutation:grand_product_argument\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"utils\",\n    hdrs = [\"utils.h\"],\n)\n\ntachyon_cc_library(\n    name = \"verifier\",\n    hdrs = [\"verifier.h\"],\n    deps = [\n        \":argument\",\n        \":opening_point_set\",\n        \":verifier_data\",\n        \"//tachyon/crypto/commitments:polynomial_openings\",\n        \"//tachyon/zk/plonk/base:l_values\",\n        \"//tachyon/zk/plonk/expressions:verifying_evaluator\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"verifier_data\",\n    hdrs = [\"verifier_data.h\"],\n    deps = [\"//tachyon/zk/plonk/base:multi_phase_evaluations\"],\n)\n"
  },
  {
    "path": "tachyon/zk/shuffle/argument.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_SHUFFLE_ARGUMENT_H_\n#define TACHYON_ZK_SHUFFLE_ARGUMENT_H_\n\n#include <algorithm>\n#include <memory>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/zk/expressions/expression.h\"\n\nnamespace tachyon::zk::shuffle {\n\ntemplate <typename F>\nclass Argument {\n public:\n  Argument() = default;\n  Argument(std::string_view name,\n           std::vector<std::unique_ptr<Expression<F>>> input_expressions,\n           std::vector<std::unique_ptr<Expression<F>>> shuffle_expressions)\n      : name_(std::string(name)),\n        input_expressions_(std::move(input_expressions)),\n        shuffle_expressions_(std::move(shuffle_expressions)) {}\n\n  const std::vector<std::unique_ptr<Expression<F>>>& input_expressions() const {\n    return input_expressions_;\n  }\n\n  std::vector<std::unique_ptr<Expression<F>>>& input_expressions() {\n    return input_expressions_;\n  }\n\n  const std::vector<std::unique_ptr<Expression<F>>>& shuffle_expressions()\n      const {\n    return shuffle_expressions_;\n  }\n\n  std::vector<std::unique_ptr<Expression<F>>>& shuffle_expressions() {\n    return shuffle_expressions_;\n  }\n\n  bool operator==(const Argument& other) const {\n    if (name_ != other.name_) return false;\n    if (input_expressions_.size() != other.input_expressions_.size())\n      return false;\n    if (shuffle_expressions_.size() != other.shuffle_expressions_.size())\n      return false;\n    for (size_t i = 0; i < input_expressions_.size(); ++i) {\n      if (*input_expressions_[i] != *other.input_expressions_[i]) return false;\n    }\n    for (size_t i = 0; i < shuffle_expressions_.size(); ++i) {\n      if (*shuffle_expressions_[i] != *other.shuffle_expressions_[i])\n        return false;\n    }\n    return true;\n  }\n  bool operator!=(const Argument& other) const { return !operator==(other); }\n\n  size_t RequiredDegree() const {\n    size_t max_input_degree = GetMaxExprDegree(input_expressions_);\n    size_t max_shuffle_degree = GetMaxExprDegree(shuffle_expressions_);\n\n    // clang-format off\n    // (1 - (l_last(X) + l_blind(X))) * (z(ω * X) * (s(X) + γ) - z(X) * (a(X) + γ))\n    // clang-format on\n    return 2 + std::max(max_shuffle_degree, max_input_degree);\n  }\n\n private:\n  static size_t GetMaxExprDegree(\n      const std::vector<std::unique_ptr<Expression<F>>>& expressions) {\n    return std::accumulate(\n        expressions.begin(), expressions.end(), 1,\n        [](size_t degree, const std::unique_ptr<Expression<F>>& expr_ptr) {\n          return std::max(degree, expr_ptr->Degree());\n        });\n  }\n\n  std::string name_;\n  std::vector<std::unique_ptr<Expression<F>>> input_expressions_;\n  std::vector<std::unique_ptr<Expression<F>>> shuffle_expressions_;\n};\n\n}  // namespace tachyon::zk::shuffle\n\n#endif  // TACHYON_ZK_SHUFFLE_ARGUMENT_H_\n"
  },
  {
    "path": "tachyon/zk/shuffle/evaluator.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_SHUFFLE_EVALUATOR_H_\n#define TACHYON_ZK_SHUFFLE_EVALUATOR_H_\n\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/profiler.h\"\n#include \"tachyon/zk/plonk/vanishing/circuit_polynomial_builder_forward.h\"\n#include \"tachyon/zk/plonk/vanishing/graph_evaluator.h\"\n#include \"tachyon/zk/plonk/vanishing/vanishing_utils.h\"\n#include \"tachyon/zk/shuffle/prover.h\"\n\nnamespace tachyon::zk::shuffle {\n\ntemplate <typename EvalsOrExtendedEvals>\nclass Evaluator {\n public:\n  using F = typename EvalsOrExtendedEvals::Field;\n\n  void Construct(const std::vector<Argument<F>>& arguments) {\n    TRACE_EVENT(\"ProofGeneration\", \"Plonk::Shuffle::Evaluator::Construct\");\n    // NOTE (chokobole): When constructing the graph, Scroll Halo2 uses beta,\n    // whereas PSE Halo2 uses gamma. However, using beta here prevents the proof\n    // from being verified. See\n    // https://github.com/scroll-tech/halo2/blob/e5ddf67/halo2_proofs/src/plonk/evaluation.rs#L294-L307.\n    evaluators_.reserve(arguments.size() * 2);\n    for (const Argument<F>& argument : arguments) {\n      plonk::GraphEvaluator<F> graph_input;\n      plonk::GraphEvaluator<F> graph_shuffle;\n\n      auto compress =\n          [](plonk::GraphEvaluator<F>& graph,\n             const std::vector<std::unique_ptr<Expression<F>>>& expressions) {\n            std::vector<plonk::ValueSource> parts = base::Map(\n                expressions,\n                [&graph](const std::unique_ptr<Expression<F>>& expression) {\n                  return graph.AddExpression(expression.get());\n                });\n            return graph.AddCalculation(plonk::Calculation::Horner(\n                plonk::ValueSource::ZeroConstant(), std::move(parts),\n                plonk::ValueSource::Theta()));\n          };\n\n      // A_compressed(X) = θᵐ⁻¹A₀(X) + θᵐ⁻²A₁(X) + ... + θAₘ₋₂(X) + Aₘ₋₁(X)\n      plonk::ValueSource compressed_input_coset =\n          compress(graph_input, argument.input_expressions());\n      // S_compressed(X) = θᵐ⁻¹S₀(X) + θᵐ⁻²S₁(X) + ... + θSₘ₋₂(X) + Sₘ₋₁(X)\n      plonk::ValueSource compressed_shuffle_coset =\n          compress(graph_shuffle, argument.shuffle_expressions());\n\n      // A_compressed(X) + γ\n      graph_input.AddCalculation(plonk::Calculation::Add(\n          compressed_input_coset, plonk::ValueSource::Gamma()));\n      // S_compressed(X) + γ\n      graph_shuffle.AddCalculation(plonk::Calculation::Add(\n          compressed_shuffle_coset, plonk::ValueSource::Gamma()));\n\n      evaluators_.push_back(std::move(graph_input));\n      evaluators_.push_back(std::move(graph_shuffle));\n    }\n  }\n\n  template <typename PS>\n  void Evaluate(plonk::CircuitPolynomialBuilder<PS>& builder,\n                absl::Span<F> chunk, size_t chunk_offset, size_t chunk_size) {\n    TRACE_EVENT(\"ProofGeneration\", \"Plonk::Shuffle::Evaluator::Evaluate\");\n    for (size_t i = 0; i < evaluators_.size(); i += 2) {\n      const plonk::GraphEvaluator<F>& input_evaluator = evaluators_[i];\n      const plonk::GraphEvaluator<F>& evaluator = evaluators_[i + 1];\n      const EvalsOrExtendedEvals& product_coset = product_cosets_[i];\n\n      plonk::EvaluationInput<EvalsOrExtendedEvals> input_eval_data =\n          builder.ExtractEvaluationInput(\n              input_evaluator.CreateInitialIntermediates(),\n              input_evaluator.CreateEmptyRotations());\n      plonk::EvaluationInput<EvalsOrExtendedEvals> eval_data =\n          builder.ExtractEvaluationInput(evaluator.CreateInitialIntermediates(),\n                                         evaluator.CreateEmptyRotations());\n\n      size_t start = chunk_offset * chunk_size;\n      for (size_t j = 0; j < chunk.size(); ++j) {\n        size_t idx = start + j;\n\n        F input_value = input_evaluator.Evaluate(input_eval_data, idx,\n                                                 /*scale=*/1, F::Zero());\n        F value = evaluator.Evaluate(eval_data, idx,\n                                     /*scale=*/1, F::Zero());\n\n        RowIndex r_next = Rotation(1).GetIndex(idx, /*scale=*/1, builder.n_);\n\n        // l_first(X) * (1 - z(X)) = 0\n        chunk[j] *= builder.y_;\n        chunk[j] += builder.l_first_[idx] * (F::One() - product_coset[idx]);\n\n        // l_last(X) * (z(X)² - z(X)) = 0\n        chunk[j] *= builder.y_;\n        chunk[j] += builder.l_last_[idx] *\n                    (product_coset[idx].Square() - product_coset[idx]);\n\n        // clang-format off\n        // A * (B - C) = 0 where\n        //  - A = 1 - (l_last(X) + l_blind(X))\n        //  - B = z(ωX) * (θᵐ⁻¹ s₀(X) + ... + sₘ₋₁(X) + γ)\n        //  - C = z(X) * (θᵐ⁻¹ a₀(X) + ... + aₘ₋₁(X) + γ)\n        // clang-format on\n        chunk[j] *= builder.y_;\n        chunk[j] +=\n            builder.l_active_row_[idx] *\n            (product_coset[r_next] * value - product_coset[idx] * input_value);\n      }\n    }\n  }\n\n  template <typename PS>\n  void UpdateCosets(plonk::CircuitPolynomialBuilder<PS>& builder,\n                    size_t circuit_idx) {\n    using PCS = typename PS::PCS;\n    using Poly = typename PCS::Poly;\n    using Evals = typename PCS::Evals;\n    using ShuffleProver = Prover<Poly, Evals>;\n\n    TRACE_EVENT(\"ProofGeneration\", \"Plonk::Shuffle::Evaluator::UpdateCosets\");\n\n    size_t num_shuffles =\n        builder.shuffle_provers_[circuit_idx].grand_product_polys().size();\n    if (num_shuffles == 0) return;\n\n    const ShuffleProver& prover = builder.shuffle_provers_[circuit_idx];\n    product_cosets_.resize(num_shuffles);\n\n    for (size_t i = 0; i < num_shuffles; ++i) {\n      if constexpr (PS::kVendor == plonk::halo2::Vendor::kPSE) {\n        product_cosets_[i] = plonk::CoeffToExtended(\n            prover.grand_product_polys()[i].poly(), builder.extended_domain_);\n      } else {\n        product_cosets_[i] =\n            builder.coset_domain_->FFT(prover.grand_product_polys()[i].poly());\n      }\n    }\n  }\n\n private:\n  std::vector<plonk::GraphEvaluator<F>> evaluators_;\n  std::vector<EvalsOrExtendedEvals> product_cosets_;\n};\n\n}  // namespace tachyon::zk::shuffle\n\n#endif  // TACHYON_ZK_SHUFFLE_EVALUATOR_H_\n"
  },
  {
    "path": "tachyon/zk/shuffle/opening_point_set.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_SHUFFLE_OPENING_POINT_SET_H_\n#define TACHYON_ZK_SHUFFLE_OPENING_POINT_SET_H_\n\nnamespace tachyon::zk::shuffle {\n\ntemplate <typename F>\nstruct OpeningPointSet {\n  OpeningPointSet(const F& x, const F& x_next) : x(x), x_next(x_next) {}\n\n  const F& x;\n  const F& x_next;\n};\n\n}  // namespace tachyon::zk::shuffle\n\n#endif  // TACHYON_ZK_SHUFFLE_OPENING_POINT_SET_H_\n"
  },
  {
    "path": "tachyon/zk/shuffle/pair.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_SHUFFLE_PAIR_H_\n#define TACHYON_ZK_SHUFFLE_PAIR_H_\n\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/json/json.h\"\n\nnamespace tachyon {\nnamespace zk::shuffle {\n\ntemplate <typename T, typename U = T>\nclass Pair {\n public:\n  Pair() = default;\n  Pair(T input, U shuffle)\n      : input_(std::move(input)), shuffle_(std::move(shuffle)) {}\n\n  const T& input() const { return input_; }\n  const U& shuffle() const { return shuffle_; }\n  T& input() { return input_; }\n  U& shuffle() { return shuffle_; }\n\n  T&& TakeInput() && { return std::move(input_); }\n  U&& TakeShuffle() && { return std::move(shuffle_); }\n\n  bool operator==(const Pair& other) const {\n    return input_ == other.input_ && shuffle_ == other.shuffle_;\n  }\n  bool operator!=(const Pair& other) const { return !operator==(other); }\n\n private:\n  T input_;\n  U shuffle_;\n};\n\ntemplate <typename T, typename U = T>\nusing Pairs = std::vector<Pair<T, U>>;\n\n}  // namespace zk::shuffle\n\nnamespace base {\n\ntemplate <typename T, typename U>\nclass RapidJsonValueConverter<zk::shuffle::Pair<T, U>> {\n public:\n  template <typename Allocator>\n  static rapidjson::Value From(const zk::shuffle::Pair<T, U>& value,\n                               Allocator& allocator) {\n    rapidjson::Value object(rapidjson::kObjectType);\n    AddJsonElement(object, \"input\", value.input(), allocator);\n    AddJsonElement(object, \"shuffle\", value.shuffle(), allocator);\n    return object;\n  }\n\n  static bool To(const rapidjson::Value& json_value, std::string_view key,\n                 zk::shuffle::Pair<T, U>* value, std::string* error) {\n    T input;\n    U shuffle;\n    if (!ParseJsonElement(json_value, \"input\", &input, error)) return false;\n    if (!ParseJsonElement(json_value, \"shuffle\", &shuffle, error)) return false;\n    *value = zk::shuffle::Pair<T, U>(std::move(input), std::move(shuffle));\n    return true;\n  }\n};\n\n}  // namespace base\n}  // namespace tachyon\n\n#endif  // TACHYON_ZK_SHUFFLE_PAIR_H_\n"
  },
  {
    "path": "tachyon/zk/shuffle/prover.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_SHUFFLE_PROVER_H_\n#define TACHYON_ZK_SHUFFLE_PROVER_H_\n\n#include <stddef.h>\n\n#include <vector>\n\n#include \"absl/types/span.h\"\n\n#include \"tachyon/base/profiler.h\"\n#include \"tachyon/crypto/commitments/polynomial_openings.h\"\n#include \"tachyon/zk/base/blinded_polynomial.h\"\n#include \"tachyon/zk/base/entities/prover_base.h\"\n#include \"tachyon/zk/plonk/base/multi_phase_ref_table.h\"\n#include \"tachyon/zk/plonk/expressions/proving_evaluator.h\"\n#include \"tachyon/zk/shuffle/argument.h\"\n#include \"tachyon/zk/shuffle/opening_point_set.h\"\n#include \"tachyon/zk/shuffle/pair.h\"\n\nnamespace tachyon::zk::shuffle {\n\ntemplate <typename Poly, typename Evals>\nclass Prover {\n public:\n  using F = typename Poly::Field;\n\n  const std::vector<Pair<Evals>>& compressed_pairs() const {\n    return compressed_pairs_;\n  }\n  const std::vector<BlindedPolynomial<Poly, Evals>>& grand_product_polys()\n      const {\n    return grand_product_polys_;\n  }\n\n  template <typename Domain>\n  static void BatchCompressPairs(\n      std::vector<Prover>& shuffle_provers, const Domain* domain,\n      const std::vector<Argument<F>>& arguments, const F& theta,\n      const std::vector<plonk::MultiPhaseRefTable<Evals>>& tables);\n\n  template <typename PCS>\n  static void BatchPermutePairs(std::vector<Prover>& shuffle_provers,\n                                ProverBase<PCS>* prover) {\n    TRACE_EVENT(\"ProofGeneration\", \"Shuffle::Prover::BatchPermutePairs\");\n    for (Prover& shuffle_prover : shuffle_provers) {\n      shuffle_prover.PermutePairs(prover);\n    }\n  }\n\n  template <typename PCS>\n  static void BatchCreateGrandProductPolys(std::vector<Prover>& shuffle_provers,\n                                           ProverBase<PCS>* prover,\n                                           const F& gamma) {\n    TRACE_EVENT(\"ProofGeneration\",\n                \"Shuffle::Prover::BatchCreateGrandProductPolys\");\n    for (Prover& shuffle_prover : shuffle_provers) {\n      shuffle_prover.CreateGrandProductPolys(prover, gamma);\n    }\n  }\n\n  constexpr static size_t GetNumGrandProductPolysCommitments(\n      const std::vector<Prover>& shuffle_provers) {\n    if (shuffle_provers.empty()) return 0;\n    return shuffle_provers.size() *\n           shuffle_provers[0].grand_product_polys_.size();\n  }\n\n  template <typename PCS>\n  static void BatchCommitGrandProductPolys(\n      const std::vector<Prover>& shuffle_provers, ProverBase<PCS>* prover,\n      size_t& commit_idx);\n\n  template <typename Domain>\n  static void TransformEvalsToPoly(std::vector<Prover>& shuffle_provers,\n                                   const Domain* domain) {\n    TRACE_EVENT(\"ProofGeneration\", \"Shuffle::Prover::TransformEvalsToPoly\");\n    VLOG(2) << \"Transform shuffle virtual columns to polys\";\n    for (Prover& shuffle_prover : shuffle_provers) {\n      shuffle_prover.TransformEvalsToPoly(domain);\n    }\n  }\n\n  template <typename PCS>\n  static void BatchEvaluate(const std::vector<Prover>& shuffle_provers,\n                            ProverBase<PCS>* prover,\n                            const OpeningPointSet<F>& point_set) {\n    TRACE_EVENT(\"ProofGeneration\", \"Shuffle::Prover::BatchEvaluate\");\n    for (const Prover& shuffle_prover : shuffle_provers) {\n      shuffle_prover.Evaluate(prover, point_set);\n    }\n  }\n\n  void Open(const OpeningPointSet<F>& point_set,\n            std::vector<crypto::PolynomialOpening<Poly>>& openings) const;\n\n private:\n  template <typename Domain>\n  static Pair<Evals> CompressPair(\n      const Domain* domain, const Argument<F>& argument, const F& theta,\n      const plonk::ProvingEvaluator<Evals>& evaluator_tpl);\n\n  template <typename PCS>\n  static BlindedPolynomial<Poly, Evals> CreateGrandProductPoly(\n      ProverBase<PCS>* prover, const Pair<Evals>& compressed_pair,\n      const F& gamma);\n\n  template <typename Domain>\n  void CompressPairs(const Domain* domain,\n                     const std::vector<Argument<F>>& arguments, const F& theta,\n                     const plonk::ProvingEvaluator<Evals>& evaluator_tpl);\n\n  template <typename PCS>\n  void CreateGrandProductPolys(ProverBase<PCS>* prover, const F& gamma);\n\n  template <typename Domain>\n  void TransformEvalsToPoly(const Domain* domain);\n\n  template <typename PCS>\n  void Evaluate(ProverBase<PCS>* prover,\n                const OpeningPointSet<F>& point_set) const;\n\n  static std::function<F(RowIndex)> CreateNumeratorCallback(const Evals& input,\n                                                            const F& gamma);\n\n  static std::function<F(RowIndex)> CreateDenominatorCallback(\n      const Evals& input, const F& gamma);\n\n  // A_compressedᵢ(X), S_compressedᵢ(X)\n  std::vector<Pair<Evals>> compressed_pairs_;\n  // Zₛ,ᵢ(X)\n  std::vector<BlindedPolynomial<Poly, Evals>> grand_product_polys_;\n};\n\n}  // namespace tachyon::zk::shuffle\n\n#include \"tachyon/zk/shuffle/prover_impl.h\"\n\n#endif  // TACHYON_ZK_SHUFFLE_PROVER_H_\n"
  },
  {
    "path": "tachyon/zk/shuffle/prover_impl.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_SHUFFLE_PROVER_IMPL_H_\n#define TACHYON_ZK_SHUFFLE_PROVER_IMPL_H_\n\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/profiler.h\"\n#include \"tachyon/base/ref.h\"\n#include \"tachyon/zk/plonk/expressions/compress_expression.h\"\n#include \"tachyon/zk/plonk/permutation/grand_product_argument.h\"\n#include \"tachyon/zk/shuffle/prover.h\"\n\nnamespace tachyon::zk::shuffle {\n\n// static\ntemplate <typename Poly, typename Evals>\ntemplate <typename Domain>\nPair<Evals> Prover<Poly, Evals>::CompressPair(\n    const Domain* domain, const Argument<F>& argument, const F& theta,\n    const plonk::ProvingEvaluator<Evals>& evaluator_tpl) {\n  TRACE_EVENT(\"Utils\", \"CompressPair\");\n  // A_compressedᵢ(X) = θᵐ⁻¹A₀(X) + θᵐ⁻²A₁(X) + ... + θAₘ₋₂(X) + Aₘ₋₁(X)\n  Evals compressed_input = plonk::CompressExpressions(\n      domain, argument.input_expressions(), theta, evaluator_tpl);\n\n  // S_compressedᵢ(X) = θᵐ⁻¹S₀(X) + θᵐ⁻²S₁(X) + ... + θSₘ₋₂(X) + Sₘ₋₁(X)\n  Evals compressed_shuffle = plonk::CompressExpressions(\n      domain, argument.shuffle_expressions(), theta, evaluator_tpl);\n\n  return {std::move(compressed_input), std::move(compressed_shuffle)};\n}\n\ntemplate <typename Poly, typename Evals>\ntemplate <typename Domain>\nvoid Prover<Poly, Evals>::CompressPairs(\n    const Domain* domain, const std::vector<Argument<F>>& arguments,\n    const F& theta, const plonk::ProvingEvaluator<Evals>& evaluator_tpl) {\n  TRACE_EVENT(\"Utils\", \"CompressPairs\");\n  compressed_pairs_ = base::Map(\n      arguments, [domain, &theta, &evaluator_tpl](const Argument<F>& argument) {\n        return CompressPair(domain, argument, theta, evaluator_tpl);\n      });\n}\n\n// static\ntemplate <typename Poly, typename Evals>\ntemplate <typename Domain>\nvoid Prover<Poly, Evals>::BatchCompressPairs(\n    std::vector<Prover>& shuffle_provers, const Domain* domain,\n    const std::vector<Argument<F>>& arguments, const F& theta,\n    const std::vector<plonk::MultiPhaseRefTable<Evals>>& tables) {\n  TRACE_EVENT(\"ProofGeneration\", \"Shuffle::Prover::BatchCompressPairs\");\n  CHECK_EQ(shuffle_provers.size(), tables.size());\n  // NOTE(chokobole): It's safe to downcast because domain is already checked.\n  int32_t n = static_cast<int32_t>(domain->size());\n  for (size_t i = 0; i < shuffle_provers.size(); ++i) {\n    plonk::ProvingEvaluator<Evals> proving_evaluator(0, n, 1, tables[i]);\n    shuffle_provers[i].CompressPairs(domain, arguments, theta,\n                                     proving_evaluator);\n  }\n}\n\n// static\ntemplate <typename Poly, typename Evals>\ntemplate <typename PCS>\nBlindedPolynomial<Poly, Evals> Prover<Poly, Evals>::CreateGrandProductPoly(\n    ProverBase<PCS>* prover, const Pair<Evals>& compressed_pair,\n    const F& gamma) {\n  TRACE_EVENT(\"Utils\", \"CreateGrandProductPoly\");\n  return {plonk::GrandProductArgument::CreatePolySerial(\n              prover, CreateNumeratorCallback(compressed_pair.input(), gamma),\n              CreateDenominatorCallback(compressed_pair.shuffle(), gamma)),\n          prover->blinder().Generate()};\n}\n\ntemplate <typename Poly, typename Evals>\ntemplate <typename PCS>\nvoid Prover<Poly, Evals>::CreateGrandProductPolys(ProverBase<PCS>* prover,\n                                                  const F& gamma) {\n  TRACE_EVENT(\"Utils\", \"CreateGrandProductPolys\");\n  // Zₛ,ᵢ(X)\n  grand_product_polys_.resize(compressed_pairs_.size());\n\n  // NOTE(dongchangYoo): do not change this code to parallelized logic.\n  grand_product_polys_ =\n      base::Map(compressed_pairs_,\n                [prover, &gamma](size_t i, const Pair<Evals>& compressed_pair) {\n                  return CreateGrandProductPoly(prover, compressed_pair, gamma);\n                });\n  compressed_pairs_.clear();\n}\n\n// static\ntemplate <typename Poly, typename Evals>\ntemplate <typename PCS>\nvoid Prover<Poly, Evals>::BatchCommitGrandProductPolys(\n    const std::vector<Prover>& shuffle_provers, ProverBase<PCS>* prover,\n    size_t& commit_idx) {\n  TRACE_EVENT(\"ProofGeneration\",\n              \"Shuffle::Prover::BatchCommitGrandProductPolys\");\n  if (shuffle_provers.empty()) return;\n\n  if constexpr (PCS::kSupportsBatchMode) {\n    for (const Prover& shuffle_prover : shuffle_provers) {\n      for (const BlindedPolynomial<Poly, Evals>& grand_product_poly :\n           shuffle_prover.grand_product_polys_) {\n        prover->BatchCommitAt(grand_product_poly.evals(), commit_idx++);\n      }\n    }\n  } else {\n    for (const Prover& shuffle_prover : shuffle_provers) {\n      for (const BlindedPolynomial<Poly, Evals>& grand_product_poly :\n           shuffle_prover.grand_product_polys_) {\n        prover->CommitAndWriteToProof(grand_product_poly.evals());\n      }\n    }\n  }\n}\n\ntemplate <typename Poly, typename Evals>\ntemplate <typename Domain>\nvoid Prover<Poly, Evals>::TransformEvalsToPoly(const Domain* domain) {\n  TRACE_EVENT(\"Utils\", \"TransformEvalsToPoly\");\n  for (BlindedPolynomial<Poly, Evals>& grand_product_poly :\n       grand_product_polys_) {\n    grand_product_poly.TransformEvalsToPoly(domain);\n  }\n}\n\ntemplate <typename Poly, typename Evals>\ntemplate <typename PCS>\nvoid Prover<Poly, Evals>::Evaluate(ProverBase<PCS>* prover,\n                                   const OpeningPointSet<F>& point_set) const {\n  TRACE_EVENT(\"Utils\", \"Evaluate\");\n  size_t size = grand_product_polys_.size();\n\n#define EVALUATE(polynomial, point) \\\n  prover->EvaluateAndWriteToProof(polynomial.poly(), point_set.point)\n\n  // THE ORDER IS IMPORTANT!! DO NOT CHANGE!\n  // See\n  // https://github.com/scroll-tech/halo2/blob/e5ddf67/halo2_proofs/src/plonk/shuffle/prover.rs#L204-L225.\n  for (size_t i = 0; i < size; ++i) {\n    const BlindedPolynomial<Poly, Evals>& grand_product_poly =\n        grand_product_polys_[i];\n\n    // Zₛ,ᵢ(x)\n    EVALUATE(grand_product_poly, x);\n    // Zₛ,ᵢ(ω * x)\n    EVALUATE(grand_product_poly, x_next);\n  }\n#undef EVALUATE\n}\n\ntemplate <typename Poly, typename Evals>\nvoid Prover<Poly, Evals>::Open(\n    const OpeningPointSet<F>& point_set,\n    std::vector<crypto::PolynomialOpening<Poly>>& openings) const {\n  TRACE_EVENT(\"ProofGeneration\", \"Shuffle::Prover::Open\");\n  size_t size = grand_product_polys_.size();\n\n#define OPENING(polynomial, point)                            \\\n  base::Ref<const Poly>(&polynomial.poly()), point_set.point, \\\n      polynomial.poly().Evaluate(point_set.point)\n\n  // THE ORDER IS IMPORTANT!! DO NOT CHANGE!\n  // See\n  // https://github.com/scroll-tech/halo2/blob/e5ddf67/halo2_proofs/src/plonk/shuffle/prover.rs#L229-L249\n  for (size_t i = 0; i < size; ++i) {\n    const BlindedPolynomial<Poly, Evals>& grand_product_poly =\n        grand_product_polys_[i];\n\n    // Zₛ,ᵢ(x)\n    openings.emplace_back(OPENING(grand_product_poly, x));\n    // Zₛ,ᵢ(ω * x)\n    openings.emplace_back(OPENING(grand_product_poly, x_next));\n  }\n#undef OPENING\n}\n\n// static\ntemplate <typename Poly, typename Evals>\nstd::function<typename Poly::Field(RowIndex)>\nProver<Poly, Evals>::CreateNumeratorCallback(const Evals& input,\n                                             const F& gamma) {\n  // A_compressedᵢ(x) + γ\n  return\n      [&input, &gamma](RowIndex row_index) { return input[row_index] + gamma; };\n}\n\n// static\ntemplate <typename Poly, typename Evals>\nstd::function<typename Poly::Field(RowIndex)>\nProver<Poly, Evals>::CreateDenominatorCallback(const Evals& shuffle,\n                                               const F& gamma) {\n  return [&shuffle, &gamma](RowIndex row_index) {\n    // S_compressedᵢ(x) + γ\n    return shuffle[row_index] + gamma;\n  };\n}\n\n}  // namespace tachyon::zk::shuffle\n\n#endif  // TACHYON_ZK_SHUFFLE_PROVER_IMPL_H_\n"
  },
  {
    "path": "tachyon/zk/shuffle/utils.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_SHUFFLE_UTILS_H_\n#define TACHYON_ZK_SHUFFLE_UTILS_H_\n\n#include <stddef.h>\n\nnamespace tachyon::zk::shuffle {\n\nconstexpr size_t GetNumEvals(size_t num_circuits, size_t num_shuffles) {\n  return num_circuits * num_shuffles * 3;\n}\n\nconstexpr size_t GetNumOpenings(size_t num_circuits, size_t num_shuffles) {\n  return num_circuits * num_shuffles * 2;\n}\n\n}  // namespace tachyon::zk::shuffle\n\n#endif  // TACHYON_ZK_SHUFFLE_UTILS_H_\n"
  },
  {
    "path": "tachyon/zk/shuffle/verifier.h",
    "content": "// Copyright 2020-2022 The Electric Coin Company\n// Copyright 2022 The Halo2 developers\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2\n// file.\n\n#ifndef TACHYON_ZK_SHUFFLE_VERIFIER_H_\n#define TACHYON_ZK_SHUFFLE_VERIFIER_H_\n\n#include <memory>\n#include <vector>\n\n#include \"tachyon/crypto/commitments/polynomial_openings.h\"\n#include \"tachyon/zk/plonk/base/l_values.h\"\n#include \"tachyon/zk/plonk/expressions/verifying_evaluator.h\"\n#include \"tachyon/zk/shuffle/argument.h\"\n#include \"tachyon/zk/shuffle/opening_point_set.h\"\n#include \"tachyon/zk/shuffle/verifier_data.h\"\n\nnamespace tachyon::zk::shuffle {\n\ntemplate <typename F, typename C>\nclass Verifier {\n public:\n  explicit Verifier(const VerifierData<F, C>& data) : data_(data) {}\n\n  Verifier(const VerifierData<F, C>& data, const plonk::LValues<F>& l_values)\n      : data_(data), l_values_(&l_values) {}\n\n  void Evaluate(const std::vector<Argument<F>>& arguments,\n                std::vector<F>& evals) {\n    plonk::VerifyingEvaluator<F> evaluator(data_);\n\n    F active_rows = F::One() - (l_values_->last + l_values_->blind);\n    for (size_t i = 0; i < data_.grand_product_commitments.size(); ++i) {\n      // l_first(x) * (1 - Zₛ,ᵢ(x)) = 0\n      evals.push_back(l_values_->first *\n                      (F::One() - data_.grand_product_evals[i]));\n      // l_last(x) * (Zₛ,ᵢ(x)² - Zₛ,ᵢ(x)) = 0\n      evals.push_back(l_values_->last * (data_.grand_product_evals[i].Square() -\n                                         data_.grand_product_evals[i]));\n      // (1 - (l_last(x) + l_blind(x))) * (\n      //  Zₛ,ᵢ(ω * x) * ( (S_compressedᵢ(x) + γ) -\n      //  Zₛ,ᵢ(x) * (A_compressedᵢ(x) + γ)\n      // ) = 0\n      evals.push_back(active_rows *\n                      CreateGrandProductEvaluation(i, arguments[i], evaluator));\n    }\n  }\n\n  template <typename Poly>\n  void Open(const OpeningPointSet<F>& point_set,\n            std::vector<crypto::PolynomialOpening<Poly, C>>& openings) const {\n    if (data_.grand_product_commitments.empty()) return;\n\n#define OPENING(commitment, point, eval) \\\n  base::Ref<const C>(&data_.commitment), point_set.point, data_.eval\n\n    for (size_t i = 0; i < data_.grand_product_commitments.size(); ++i) {\n      openings.emplace_back(\n          OPENING(grand_product_commitments[i], x, grand_product_evals[i]));\n      openings.emplace_back(OPENING(grand_product_commitments[i], x_next,\n                                    grand_product_next_evals[i]));\n    }\n\n#undef OPENING\n  }\n\n private:\n  F CompressExpressions(\n      const std::vector<std::unique_ptr<Expression<F>>>& expressions,\n      plonk::VerifyingEvaluator<F>& evaluator) const {\n    F compressed_value = F::Zero();\n    for (const std::unique_ptr<Expression<F>>& expression : expressions) {\n      compressed_value *= data_.theta;\n      compressed_value += evaluator.Evaluate(expression.get());\n    }\n    return compressed_value;\n  }\n\n  F CreateGrandProductEvaluation(size_t i, const Argument<F>& argument,\n                                 plonk::VerifyingEvaluator<F>& evaluator) {\n    // Zₛ,ᵢ(ω * x) * (S_compressedᵢ(x) + γ) - Zₛ,ᵢ(x) * (A_compressedᵢ(x) + γ)\n    F compressed_input_expression =\n        CompressExpressions(argument.input_expressions(), evaluator);\n    F compressed_shuffle_expression =\n        CompressExpressions(argument.shuffle_expressions(), evaluator);\n    F left = data_.grand_product_next_evals[i] *\n             (compressed_shuffle_expression + data_.gamma);\n    F right = data_.grand_product_evals[i] *\n              (compressed_input_expression + data_.gamma);\n    return left - right;\n  }\n\n  VerifierData<F, C> data_;\n  const plonk::LValues<F>* l_values_ = nullptr;\n};\n\n}  // namespace tachyon::zk::shuffle\n\n#endif  // TACHYON_ZK_SHUFFLE_VERIFIER_H_\n"
  },
  {
    "path": "tachyon/zk/shuffle/verifier_data.h",
    "content": "#ifndef TACHYON_ZK_SHUFFLE_VERIFIER_DATA_H_\n#define TACHYON_ZK_SHUFFLE_VERIFIER_DATA_H_\n\n#include \"tachyon/zk/plonk/base/multi_phase_evaluations.h\"\n\nnamespace tachyon::zk::shuffle {\n\ntemplate <typename F, typename C>\nstruct VerifierData : public plonk::MultiPhaseEvaluations<F> {\n  VerifierData(absl::Span<const F> fixed_evals,\n               absl::Span<const F> advice_evals,\n               absl::Span<const F> instance_evals,\n               absl::Span<const F> challenges,\n               absl::Span<const C> grand_product_commitments,\n               absl::Span<const F> grand_product_evals,\n               absl::Span<const F> grand_product_next_evals, const F& theta,\n               const F& gamma)\n      : plonk::MultiPhaseEvaluations<F>(fixed_evals, advice_evals,\n                                        instance_evals, challenges),\n        grand_product_commitments(grand_product_commitments),\n        grand_product_evals(grand_product_evals),\n        grand_product_next_evals(grand_product_next_evals),\n        theta(theta),\n        gamma(gamma) {}\n\n  // [Zₛ,ᵢ(τ)]₁\n  absl::Span<const C> grand_product_commitments;\n  // Zₛ,ᵢ(x)\n  absl::Span<const F> grand_product_evals;\n  // Zₛ,ᵢ(ω * x)\n  absl::Span<const F> grand_product_next_evals;\n  const F& theta;\n  const F& gamma;\n};\n\n}  // namespace tachyon::zk::shuffle\n\n#endif  // TACHYON_ZK_SHUFFLE_VERIFIER_DATA_H_\n"
  },
  {
    "path": "third_party/BUILD.bazel",
    "content": ""
  },
  {
    "path": "third_party/absl/BUILD.bazel",
    "content": ""
  },
  {
    "path": "third_party/absl/add_missing_linkopts.patch",
    "content": "diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel\nindex 53c57718..f4188685 100644\n--- a/absl/strings/BUILD.bazel\n+++ b/absl/strings/BUILD.bazel\n@@ -71,7 +71,9 @@ cc_library(\n         \"substitute.h\",\n     ],\n     copts = ABSL_DEFAULT_COPTS,\n-    linkopts = ABSL_DEFAULT_LINKOPTS,\n+    linkopts = ABSL_DEFAULT_LINKOPTS + [\n+        \"-lm\",\n+    ],\n     deps = [\n         \":internal\",\n         \"//absl/base\",\n"
  },
  {
    "path": "third_party/absl/ignore-maybe-uninitialized-warning.patch",
    "content": "diff --git a/absl/container/internal/inlined_vector.h b/absl/container/internal/inlined_vector.h\nindex 0398f530..79caf140 100644\n--- a/absl/container/internal/inlined_vector.h\n+++ b/absl/container/internal/inlined_vector.h\n@@ -465,7 +465,14 @@ class Storage {\n                           other_storage.GetIsAllocated());\n \n     GetSizeAndIsAllocated() = other_storage.GetSizeAndIsAllocated();\n+#if defined(__GNUC__) && !defined(__clang__)\n+#pragma GCC diagnostic push\n+#pragma GCC diagnostic ignored \"-Wmaybe-uninitialized\"\n+#endif\n     data_ = other_storage.data_;\n+#if defined(__GNUC__) && !defined(__clang__)\n+#pragma GCC diagnostic pop\n+#endif\n   }\n \n   void DeallocateIfAllocated() {\n"
  },
  {
    "path": "third_party/boringssl/BUILD.bazel",
    "content": ""
  },
  {
    "path": "third_party/boringssl/blake2b512.patch",
    "content": "diff --git a/src/crypto/blake2/blake2.c b/src/crypto/blake2/blake2.c\nindex 14bbe5b23..bb5cb4107 100644\n--- a/src/crypto/blake2/blake2.c\n+++ b/src/crypto/blake2/blake2.c\n@@ -115,6 +115,50 @@ void BLAKE2B256_Init(BLAKE2B_CTX *b2b) {\n   b2b->h[0] ^= 0x01010000 | BLAKE2B256_DIGEST_LENGTH;\n }\n\n+void BLAKE2B256_InitWithPersonal(BLAKE2B_CTX *b2b, const char personal[BLAKE2B_PERSONAL_LENGTH]) {\n+  OPENSSL_memset(b2b, 0, sizeof(BLAKE2B_CTX));\n+\n+  static_assert(sizeof(kIV) == sizeof(b2b->h), \"\");\n+  OPENSSL_memcpy(&b2b->h, kIV, sizeof(kIV));\n+\n+  // https://tools.ietf.org/html/rfc7693#section-2.5\n+  b2b->h[0] ^= 0x01010000 | BLAKE2B256_DIGEST_LENGTH;\n+\n+  if (personal != NULL) {\n+    uint64_t personal_left = *((uint64_t *)personal);\n+    uint64_t personal_right = *((uint64_t *)(personal + 8));\n+    b2b->h[6] ^= personal_left;\n+    b2b->h[7] ^= personal_right;\n+  }\n+}\n+\n+void BLAKE2B512_Init(BLAKE2B_CTX *b2b) {\n+  OPENSSL_memset(b2b, 0, sizeof(BLAKE2B_CTX));\n+\n+  static_assert(sizeof(kIV) == sizeof(b2b->h), \"\");\n+  OPENSSL_memcpy(&b2b->h, kIV, sizeof(kIV));\n+\n+  // https://tools.ietf.org/html/rfc7693#section-2.5\n+  b2b->h[0] ^= 0x01010000 | BLAKE2B512_DIGEST_LENGTH;\n+}\n+\n+void BLAKE2B512_InitWithPersonal(BLAKE2B_CTX *b2b, const char personal[BLAKE2B_PERSONAL_LENGTH]) {\n+  OPENSSL_memset(b2b, 0, sizeof(BLAKE2B_CTX));\n+\n+  static_assert(sizeof(kIV) == sizeof(b2b->h), \"\");\n+  OPENSSL_memcpy(&b2b->h, kIV, sizeof(kIV));\n+\n+  // https://tools.ietf.org/html/rfc7693#section-2.5\n+  b2b->h[0] ^= 0x01010000 | BLAKE2B512_DIGEST_LENGTH;\n+\n+  if (personal != NULL) {\n+    uint64_t personal_left = *((uint64_t *)personal);\n+    uint64_t personal_right = *((uint64_t *)(personal + 8));\n+    b2b->h[6] ^= personal_left;\n+    b2b->h[7] ^= personal_right;\n+  }\n+}\n+\n void BLAKE2B256_Update(BLAKE2B_CTX *b2b, const void *in_data, size_t len) {\n   if (len == 0) {\n     // Work around a C language bug. See https://crbug.com/1019588.\n@@ -151,6 +195,10 @@ void BLAKE2B256_Update(BLAKE2B_CTX *b2b, const void *in_data, size_t len) {\n   b2b->block_used = len;\n }\n\n+void BLAKE2B512_Update(BLAKE2B_CTX *b2b, const void *in_data, size_t len) {\n+  BLAKE2B256_Update(b2b, in_data, len);\n+}\n+\n void BLAKE2B256_Final(uint8_t out[BLAKE2B256_DIGEST_LENGTH], BLAKE2B_CTX *b2b) {\n   OPENSSL_memset(&b2b->block[b2b->block_used], 0,\n                  sizeof(b2b->block) - b2b->block_used);\n@@ -160,6 +208,15 @@ void BLAKE2B256_Final(uint8_t out[BLAKE2B256_DIGEST_LENGTH], BLAKE2B_CTX *b2b) {\n   memcpy(out, b2b->h, BLAKE2B256_DIGEST_LENGTH);\n }\n\n+void BLAKE2B512_Final(uint8_t out[BLAKE2B512_DIGEST_LENGTH], BLAKE2B_CTX *b2b) {\n+  OPENSSL_memset(&b2b->block[b2b->block_used], 0,\n+                 sizeof(b2b->block) - b2b->block_used);\n+  blake2b_transform(b2b, b2b->block, b2b->block_used,\n+                    /*is_final_block=*/1);\n+  static_assert(BLAKE2B512_DIGEST_LENGTH <= sizeof(b2b->h), \"\");\n+  memcpy(out, b2b->h, BLAKE2B512_DIGEST_LENGTH);\n+}\n+\n void BLAKE2B256(const uint8_t *data, size_t len,\n                 uint8_t out[BLAKE2B256_DIGEST_LENGTH]) {\n   BLAKE2B_CTX ctx;\n@@ -167,3 +224,11 @@ void BLAKE2B256(const uint8_t *data, size_t len,\n   BLAKE2B256_Update(&ctx, data, len);\n   BLAKE2B256_Final(out, &ctx);\n }\n+\n+void BLAKE2B512(const uint8_t *data, size_t len,\n+                uint8_t out[BLAKE2B512_DIGEST_LENGTH]) {\n+  BLAKE2B_CTX ctx;\n+  BLAKE2B512_Init(&ctx);\n+  BLAKE2B512_Update(&ctx, data, len);\n+  BLAKE2B512_Final(out, &ctx);\n+}\ndiff --git a/src/include/openssl/blake2.h b/src/include/openssl/blake2.h\nindex 03e3a465c..b3cd7d5f0 100644\n--- a/src/include/openssl/blake2.h\n+++ b/src/include/openssl/blake2.h\n@@ -21,9 +21,10 @@\n extern \"C\" {\n #endif\n\n-\n #define BLAKE2B256_DIGEST_LENGTH (256 / 8)\n+#define BLAKE2B512_DIGEST_LENGTH (512 / 8)\n #define BLAKE2B_CBLOCK 128\n+#define BLAKE2B_PERSONAL_LENGTH 16\n\n struct blake2b_state_st {\n   uint64_t h[8];\n@@ -36,6 +37,11 @@ struct blake2b_state_st {\n // pointers inside |b2b| thus release of |b2b| is purely managed by the caller.\n OPENSSL_EXPORT void BLAKE2B256_Init(BLAKE2B_CTX *b2b);\n\n+// If |personal| is non-NULL, must point to |BLAKE2B_PERSONAL_LENGTH| bytes of\n+// personalization data. |personal| may be NULL in which case no personalization\n+// is used.\n+OPENSSL_EXPORT void BLAKE2B256_InitWithPersonal(BLAKE2B_CTX *b2b, const char personal[BLAKE2B_PERSONAL_LENGTH]);\n+\n // BLAKE2B256_Update appends |len| bytes from |data| to the digest being\n // calculated by |b2b|.\n OPENSSL_EXPORT void BLAKE2B256_Update(BLAKE2B_CTX *b2b, const void *data,\n@@ -51,6 +57,30 @@ OPENSSL_EXPORT void BLAKE2B256_Final(uint8_t out[BLAKE2B256_DIGEST_LENGTH],\n OPENSSL_EXPORT void BLAKE2B256(const uint8_t *data, size_t len,\n                                uint8_t out[BLAKE2B256_DIGEST_LENGTH]);\n\n+// BLAKE2B512_Init initialises |b2b| to perform a BLAKE2b-512 hash. There are no\n+// pointers inside |b2b| thus release of |b2b| is purely managed by the caller.\n+OPENSSL_EXPORT void BLAKE2B512_Init(BLAKE2B_CTX *b2b);\n+\n+// If |personal| is non-NULL, must point to |BLAKE2B_PERSONAL_LENGTH| bytes of\n+// personalization data. |personal| may be NULL in which case no personalization\n+// is used.\n+OPENSSL_EXPORT void BLAKE2B512_InitWithPersonal(BLAKE2B_CTX *b2b, const char personal[BLAKE2B_PERSONAL_LENGTH]);\n+\n+// BLAKE2B512_Update appends |len| bytes from |data| to the digest being\n+// calculated by |b2b|.\n+OPENSSL_EXPORT void BLAKE2B512_Update(BLAKE2B_CTX *b2b, const void *data,\n+                                      size_t len);\n+\n+// BLAKE2B512_Final completes the digest calculated by |b2b| and writes\n+// |BLAKE2B512_DIGEST_LENGTH| bytes to |out|.\n+OPENSSL_EXPORT void BLAKE2B512_Final(uint8_t out[BLAKE2B512_DIGEST_LENGTH],\n+                                     BLAKE2B_CTX *b2b);\n+\n+// BLAKE2B512 writes the BLAKE2b-512 digest of |len| bytes from |data| to\n+// |out|.\n+OPENSSL_EXPORT void BLAKE2B512(const uint8_t *data, size_t len,\n+                               uint8_t out[BLAKE2B512_DIGEST_LENGTH]);\n+\n\n #if defined(__cplusplus)\n }  // extern C\n"
  },
  {
    "path": "third_party/boringssl/chacha_core.patch",
    "content": "diff --git a/src/crypto/chacha/chacha.c b/src/crypto/chacha/chacha.c\nindex 1092b7aa2..8b7d9663d 100644\n--- a/src/crypto/chacha/chacha.c\n+++ b/src/crypto/chacha/chacha.c\n@@ -24,8 +24,8 @@\n \n \n // sigma contains the ChaCha constants, which happen to be an ASCII string.\n-static const uint8_t sigma[16] = { 'e', 'x', 'p', 'a', 'n', 'd', ' ', '3',\n-                                   '2', '-', 'b', 'y', 't', 'e', ' ', 'k' };\n+static const uint8_t sigma[16] = {'e', 'x', 'p', 'a', 'n', 'd', ' ', '3',\n+                                  '2', '-', 'b', 'y', 't', 'e', ' ', 'k'};\n \n // QUARTERROUND updates a, b, c, d with a ChaCha \"quarter\" round.\n #define QUARTERROUND(a, b, c, d)           \\\n@@ -38,6 +38,33 @@ static const uint8_t sigma[16] = { 'e', 'x', 'p', 'a', 'n', 'd', ' ', '3',\n   x[c] += x[d];                            \\\n   x[b] = CRYPTO_rotl_u32(x[b] ^ x[c], 7);\n \n+// chacha_core performs 20 rounds of ChaCha on the input words in\n+// |input| and writes the 64 output bytes to |output|.\n+void chacha_core(uint8_t output[64], const uint32_t input[16]) {\n+  uint32_t x[16];\n+  int i;\n+\n+  OPENSSL_memcpy(x, input, sizeof(uint32_t) * 16);\n+  for (i = 20; i > 0; i -= 2) {\n+    QUARTERROUND(0, 4, 8, 12)\n+    QUARTERROUND(1, 5, 9, 13)\n+    QUARTERROUND(2, 6, 10, 14)\n+    QUARTERROUND(3, 7, 11, 15)\n+    QUARTERROUND(0, 5, 10, 15)\n+    QUARTERROUND(1, 6, 11, 12)\n+    QUARTERROUND(2, 7, 8, 13)\n+    QUARTERROUND(3, 4, 9, 14)\n+  }\n+\n+  for (i = 0; i < 16; ++i) {\n+    x[i] += input[i];\n+  }\n+  for (i = 0; i < 16; ++i) {\n+    CRYPTO_store_u32_le(output + 4 * i, x[i]);\n+  }\n+}\n+\n+\n void CRYPTO_hchacha20(uint8_t out[32], const uint8_t key[32],\n                       const uint8_t nonce[16]) {\n   uint32_t x[16];\n@@ -96,32 +123,6 @@ void CRYPTO_chacha_20(uint8_t *out, const uint8_t *in, size_t in_len,\n \n #else\n \n-// chacha_core performs 20 rounds of ChaCha on the input words in\n-// |input| and writes the 64 output bytes to |output|.\n-static void chacha_core(uint8_t output[64], const uint32_t input[16]) {\n-  uint32_t x[16];\n-  int i;\n-\n-  OPENSSL_memcpy(x, input, sizeof(uint32_t) * 16);\n-  for (i = 20; i > 0; i -= 2) {\n-    QUARTERROUND(0, 4, 8, 12)\n-    QUARTERROUND(1, 5, 9, 13)\n-    QUARTERROUND(2, 6, 10, 14)\n-    QUARTERROUND(3, 7, 11, 15)\n-    QUARTERROUND(0, 5, 10, 15)\n-    QUARTERROUND(1, 6, 11, 12)\n-    QUARTERROUND(2, 7, 8, 13)\n-    QUARTERROUND(3, 4, 9, 14)\n-  }\n-\n-  for (i = 0; i < 16; ++i) {\n-    x[i] += input[i];\n-  }\n-  for (i = 0; i < 16; ++i) {\n-    CRYPTO_store_u32_le(output + 4 * i, x[i]);\n-  }\n-}\n-\n void CRYPTO_chacha_20(uint8_t *out, const uint8_t *in, size_t in_len,\n                       const uint8_t key[32], const uint8_t nonce[12],\n                       uint32_t counter) {\ndiff --git a/src/include/openssl/chacha.h b/src/include/openssl/chacha.h\nindex cfbaa7568..a0379ad4c 100644\n--- a/src/include/openssl/chacha.h\n+++ b/src/include/openssl/chacha.h\n@@ -33,6 +33,7 @@ OPENSSL_EXPORT void CRYPTO_chacha_20(uint8_t *out, const uint8_t *in,\n                                      size_t in_len, const uint8_t key[32],\n                                      const uint8_t nonce[12], uint32_t counter);\n \n+OPENSSL_EXPORT void chacha_core(uint8_t output[64], const uint32_t input[16]);\n \n #if defined(__cplusplus)\n }  // extern C\n"
  },
  {
    "path": "third_party/circomlib/BUILD.bazel",
    "content": ""
  },
  {
    "path": "third_party/circomlib/workspace.bzl",
    "content": "\"\"\"loads the circomlib library for testing purpose.\"\"\"\n\nload(\"//third_party:repo.bzl\", \"tachyon_http_archive\", \"tf_mirror_urls\")\n\ndef repo():\n    tachyon_http_archive(\n        name = \"kroma_network_circomlib\",\n        urls = tf_mirror_urls(\"https://github.com/kroma-network/circomlib/archive/b040af932308aa7b6e9d034cc43c18bea4ed1ab4.tar.gz\"),\n        sha256 = \"446215fc03bb1d763dd0475d4f21ec269f65c9897a111de5b6272816c244911b\",\n        strip_prefix = \"circomlib-b040af932308aa7b6e9d034cc43c18bea4ed1ab4\",\n    )\n"
  },
  {
    "path": "third_party/clang_toolchain/BUILD.bazel",
    "content": ""
  },
  {
    "path": "third_party/clang_toolchain/cc_configure_clang.bzl",
    "content": "\"\"\" Downloads clang and configures the crosstool using bazel's autoconf.\"\"\"\n\nload(\"@bazel_tools//tools/cpp:cc_configure.bzl\", \"cc_autoconf_impl\")\nload(\":download_clang.bzl\", \"download_clang\")\n\n_TACHYON_DOWNLOAD_CLANG = \"TACHYON_DOWNLOAD_CLANG\"\n_TACHYON_NEED_CUDA = \"TACHYON_NEED_CUDA\"\n\ndef _cc_clang_autoconf(repo_ctx):\n    if repo_ctx.os.environ.get(_TACHYON_DOWNLOAD_CLANG) != \"1\":\n        return\n    if repo_ctx.os.environ.get(_TACHYON_NEED_CUDA) == \"1\":\n        # Clang is handled separately for CUDA configs.\n        # See cuda_configure.bzl for more details.\n        return\n\n    download_clang(repo_ctx, out_folder = \"extra_tools\")\n    overridden_tools = {\"gcc\": \"extra_tools/bin/clang\"}\n    cc_autoconf_impl(repo_ctx, overridden_tools)\n\ncc_download_clang_toolchain = repository_rule(\n    environ = [\n        _TACHYON_DOWNLOAD_CLANG,\n        _TACHYON_NEED_CUDA,\n    ],\n    implementation = _cc_clang_autoconf,\n)\n"
  },
  {
    "path": "third_party/clang_toolchain/download_clang.bzl",
    "content": "\"\"\" Helpers to download a recent clang release.\"\"\"\n\ndef _get_platform_folder(os_name):\n    os_name = os_name.lower()\n    if os_name.startswith(\"windows\"):\n        return \"Win\"\n    if os_name.startswith(\"mac os\"):\n        return \"Mac\"\n    if not os_name.startswith(\"linux\"):\n        fail(\"Unknown platform\")\n    return \"Linux_x64\"\n\ndef _download_chromium_clang(\n        repo_ctx,\n        platform_folder,\n        package_version,\n        sha256,\n        out_folder):\n    cds_url = \"https://commondatastorage.googleapis.com/chromium-browser-clang\"\n    cds_file = \"clang-%s.tgz\" % package_version\n    cds_full_url = \"{0}/{1}/{2}\".format(cds_url, platform_folder, cds_file)\n    repo_ctx.download_and_extract(cds_full_url, output = out_folder, sha256 = sha256)\n\ndef download_clang(repo_ctx, out_folder):\n    \"\"\" Download a fresh clang release and put it into out_folder.\n\n    Clang itself will be located in 'out_folder/bin/clang'.\n    We currently download one of the latest releases of clang by the\n    Chromium project (see\n    https://chromium.googlesource.com/chromium/src/+/master/docs/clang.md).\n\n    Args:\n      repo_ctx: An instance of repository_context object.\n      out_folder: A folder to extract the compiler into.\n    \"\"\"\n    # TODO(ibiryukov): we currently download and extract some extra tools in the\n    # clang release (e.g., sanitizers). We should probably remove the ones\n    # we don't need and document the ones we want provide in addition to clang.\n\n    # Latest CLANG_REVISION and CLANG_SUB_REVISION of the Chromiums's release\n    # can be found in https://chromium.googlesource.com/chromium/src/tools/clang/+/master/scripts/update.py\n    CLANG_REVISION = \"b4160cb94c54f0b31d0ce14694950dac7b6cd83f\"\n    CLANG_SVN_REVISION = \"371856\"\n    CLANG_SUB_REVISION = 1\n    package_version = \"%s-%s-%s\" % (\n        CLANG_SVN_REVISION,\n        CLANG_REVISION[:8],\n        CLANG_SUB_REVISION,\n    )\n\n    checksums = {\n        \"Linux_x64\": \"919c19df3ebd7db03b72575b2de5198404357659fc8c85c2d66e679ad4acbafe\",\n        \"Mac\": \"5632c516f3ac5fab3654d0a874688cad6c7f99b96845da27ab12336a14187aa2\",\n        \"Win\": \"235545b33f4d697190032cb538fdcaba227017c95b752ea8af8f29aab8da7479\",\n    }\n\n    platform_folder = _get_platform_folder(repo_ctx.os.name)\n    _download_chromium_clang(\n        repo_ctx,\n        platform_folder,\n        package_version,\n        checksums[platform_folder],\n        out_folder,\n    )\n"
  },
  {
    "path": "third_party/cxx_rs/BUILD.bazel",
    "content": ""
  },
  {
    "path": "third_party/cxx_rs/add_more_args_to_cxx_bridge.patch",
    "content": "diff --git a/tools/bazel/rust_cxx_bridge.bzl b/tools/bazel/rust_cxx_bridge.bzl\nindex c7d07e8a..e5b699ac 100644\n--- a/tools/bazel/rust_cxx_bridge.bzl\n+++ b/tools/bazel/rust_cxx_bridge.bzl\n@@ -2,48 +2,61 @@\n load(\"@bazel_skylib//rules:run_binary.bzl\", \"run_binary\")\n load(\"@rules_cc//cc:defs.bzl\", \"cc_library\")\n \n-def rust_cxx_bridge(name, src, deps = []):\n+def rust_cxx_bridge(name, src, args = [], deps = [], out_dir = None):\n     \"\"\"A macro defining a cxx bridge library\n \n     Args:\n         name (string): The name of the new target\n         src (string): The rust source file to generate a bridge for\n+        args (list, optional): A list of arguments for the code generator. Defaults to [].\n         deps (list, optional): A list of dependencies for the underlying cc_library. Defaults to [].\n+        out_dir (string, optional): A dirname where outputs generated by the code generator are located. Defaults to None.\n     \"\"\"\n+\n+    if out_dir == None:\n+        header = \"%s.h\" % src\n+        source = \"%s.cc\" % src\n+        includes = []\n+    else:\n+        header = \"%s/%s.h\" % (out_dir, src)\n+        source = \"%s/%s.cc\" % (out_dir, src)\n+        includes = [\".\"]\n+\n     native.alias(\n         name = \"%s/header\" % name,\n-        actual = src + \".h\",\n+        actual = header\n     )\n \n     native.alias(\n         name = \"%s/source\" % name,\n-        actual = src + \".cc\",\n+        actual = source\n     )\n \n     run_binary(\n         name = \"%s/generated\" % name,\n         srcs = [src],\n         outs = [\n-            src + \".h\",\n-            src + \".cc\",\n+            header,\n+            source,\n         ],\n         args = [\n             \"$(location %s)\" % src,\n             \"-o\",\n-            \"$(location %s.h)\" % src,\n+            \"$(location %s)\" % header,\n             \"-o\",\n-            \"$(location %s.cc)\" % src,\n-        ],\n+            \"$(location %s)\" % source,\n+        ] + args,\n         tool = \"@cxx.rs//:codegen\",\n     )\n \n     cc_library(\n         name = name,\n-        srcs = [src + \".cc\"],\n+        srcs = [source],\n         deps = deps + [\":%s/include\" % name],\n     )\n \n     cc_library(\n         name = \"%s/include\" % name,\n-        hdrs = [src + \".h\"],\n+        hdrs = [header],\n+        includes = includes,\n     )\n"
  },
  {
    "path": "third_party/doxygen/BUILD.bazel",
    "content": ""
  },
  {
    "path": "third_party/doxygen/doxygen_archive.BUILD",
    "content": "filegroup(\n    name = \"doxygen_bin\",\n    srcs = [\"bin/doxygen\"],\n    visibility = [\"//visibility:public\"],\n)\n"
  },
  {
    "path": "third_party/doxygen/workspace.bzl",
    "content": "\"\"\"loads the doxygen binary used for generating Tachyon API docs.\"\"\"\n\nload(\"//third_party:repo.bzl\", \"tachyon_http_archive\", \"tf_mirror_urls\")\n\ndef repo():\n    tachyon_http_archive(\n        name = \"doxygen_archive\",\n        build_file = \"//third_party/doxygen:doxygen_archive.BUILD\",\n        urls = tf_mirror_urls(\"https://www.doxygen.nl/files/doxygen-1.10.0.linux.bin.tar.gz\"),\n        sha256 = \"dcfc9aa4cc05aef1f0407817612ad9e9201d9bf2ce67cecf95a024bba7d39747\",\n        strip_prefix = \"doxygen-1.10.0\",\n    )\n"
  },
  {
    "path": "third_party/eigen3/BUILD.bazel",
    "content": "# Description:\n#   Eigen is a C++ template library for linear algebra: vectors,\n#   matrices, and related algorithms.\n# This is the BUILD file with extra code to patch into @eigen_archive.\n\n# TODO(chokobole): Enable this when we have mkl.\n# load(\"//third_party/mkl:build_defs.bzl\", \"if_mkl\")\n\nlicenses([\n    # Note: Eigen is an MPL2 library that includes GPL v3 and LGPL v2.1+ code.\n    #       We've taken special care to not reference any restricted code.\n    \"reciprocal\",  # MPL2\n    \"notice\",  # Portions BSD\n])\n\nexports_files([\"LICENSE\"])\n\nEIGEN3_THIRD_PARTY_HEADERS = [\n    \"Eigen/Core\",\n    \"Eigen/LU\",\n    \"Eigen/Cholesky\",\n    \"Eigen/Eigenvalues\",\n    \"Eigen/OrderingMethods\",\n    \"Eigen/QR\",\n    \"Eigen/SparseCholesky\",\n    \"Eigen/SparseCore\",\n    \"Eigen/SVD\",\n    \"unsupported/Eigen/MatrixFunctions\",\n    \"unsupported/Eigen/SpecialFunctions\",\n    \"unsupported/Eigen/CXX11/ThreadPool\",\n    \"unsupported/Eigen/CXX11/Tensor\",\n]\n\ncc_library(\n    name = \"eigen3\",\n    hdrs = EIGEN3_THIRD_PARTY_HEADERS,\n    # includes = if_mkl([\"./mkl_include\"]),\n    visibility = [\"//visibility:public\"],\n    deps = [\n        \"@eigen_archive//:eigen3_internal\",\n    ],\n)\n\nfilegroup(\n    name = \"eigen_third_party_header_files\",\n    srcs = EIGEN3_THIRD_PARTY_HEADERS,\n    visibility = [\"//visibility:public\"],\n)\n\ngenrule(\n    name = \"install_eigen_headers\",\n    srcs = [\n        \"@eigen_archive//:eigen_header_files\",\n        \"@eigen_archive//:eigen_source_files\",\n        \":eigen_third_party_header_files\",\n    ],\n    outs = [\"include\"],\n    cmd = \"\"\"\n    mkdir $@\n    for f in $(SRCS); do\n      d=\"$${f%/*}\"\n      d=\"$${d#*external/eigen_archive/}\"\n\n      mkdir -p \"$@/$${d}\"\n      cp \"$${f}\" \"$@/$${d}/\"\n    done\n    \"\"\",\n    tags = [\"manual\"],\n)\n"
  },
  {
    "path": "third_party/eigen3/Eigen/Cholesky",
    "content": "#include \"Eigen/Cholesky\"\n"
  },
  {
    "path": "third_party/eigen3/Eigen/Core",
    "content": "#include \"Eigen/Core\"\n"
  },
  {
    "path": "third_party/eigen3/Eigen/Eigenvalues",
    "content": "#include \"Eigen/Eigenvalues\"\n"
  },
  {
    "path": "third_party/eigen3/Eigen/LU",
    "content": "#include \"Eigen/LU\"\n"
  },
  {
    "path": "third_party/eigen3/Eigen/OrderingMethods",
    "content": "#include \"Eigen/OrderingMethods\"\n"
  },
  {
    "path": "third_party/eigen3/Eigen/QR",
    "content": "#include \"Eigen/QR\"\n"
  },
  {
    "path": "third_party/eigen3/Eigen/SVD",
    "content": "#include \"Eigen/SVD\"\n"
  },
  {
    "path": "third_party/eigen3/Eigen/SparseCholesky",
    "content": "#include \"Eigen/SparseCholesky\"\n"
  },
  {
    "path": "third_party/eigen3/Eigen/SparseCore",
    "content": "#include \"Eigen/SparseCore\"\n"
  },
  {
    "path": "third_party/eigen3/LICENSE",
    "content": "Eigen is primarily MPL2 licensed. See COPYING.MPL2 and these links:\n  http://www.mozilla.org/MPL/2.0/\n  http://www.mozilla.org/MPL/2.0/FAQ.html\n\nSome files contain third-party code under BSD or LGPL licenses, whence\nthe other COPYING.* files here.\n\nAll the LGPL code is either LGPL 2.1-only, or LGPL 2.1-or-later.\nFor this reason, the COPYING.LGPL file contains the LGPL 2.1 text.\n\nIf you want to guarantee that the Eigen code that you are #including\nis licensed under the MPL2 and possibly more permissive licenses (like\nBSD), #define this preprocessor symbol: EIGEN_MPL2_ONLY \nFor example, with most compilers, you could add this to your project\n      CXXFLAGS: -DEIGEN_MPL2_ONLY \nThis will cause a compilation error to be generated if you #include\nany code that is LGPL licensed.\n\n----------------------------------------------------------------------\nFollowing applies to:\n./test/mapstaticmethods.cpp\n./test/schur_real.cpp\n./test/prec_inverse_4x4.cpp\n./test/smallvectors.cpp\n./test/redux.cpp\n./test/special_numbers.cpp\n./test/adjoint.cpp\n./test/resize.cpp\n./test/mixingtypes.cpp\n./test/product_trmv.cpp\n./test/sparse_solvers.cpp\n./test/cholesky.cpp\n./test/geo_quaternion.cpp\n./test/miscmatrices.cpp\n./test/stddeque.cpp\n./test/integer_types.cpp\n./test/product_large.cpp\n./test/eigensolver_generic.cpp\n./test/householder.cpp\n./test/geo_orthomethods.cpp\n./test/array_for_matrix.cpp\n./test/sparseLM.cpp\n./test/upperbidiagonalization.cpp\n./test/nomalloc.cpp\n./test/packetmath.cpp\n./test/jacobisvd.cpp\n./test/geo_transformations.cpp\n./test/swap.cpp\n./test/eigensolver_selfadjoint.cpp\n./test/inverse.cpp\n./test/product_selfadjoint.cpp\n./test/product_trsolve.cpp\n./test/product_extra.cpp\n./test/sparse_solver.h\n./test/mapstride.cpp\n./test/mapped_matrix.cpp\n./test/geo_eulerangles.cpp\n./test/eigen2support.cpp\n./test/denseLM.cpp\n./test/stdvector.cpp\n./test/nesting_ops.cpp\n./test/sparse_permutations.cpp\n./test/zerosized.cpp\n./test/exceptions.cpp\n./test/vectorwiseop.cpp\n./test/cwiseop.cpp\n./test/basicstuff.cpp\n./test/product_trmm.cpp\n./test/linearstructure.cpp\n./test/sparse_product.cpp\n./test/stdvector_overload.cpp\n./test/stable_norm.cpp\n./test/umeyama.cpp\n./test/unalignedcount.cpp\n./test/triangular.cpp\n./test/product_mmtr.cpp\n./test/sparse_basic.cpp\n./test/sparse_vector.cpp\n./test/meta.cpp\n./test/real_qz.cpp\n./test/ref.cpp\n./test/eigensolver_complex.cpp\n./test/cholmod_support.cpp\n./test/conjugate_gradient.cpp\n./test/sparse.h\n./test/simplicial_cholesky.cpp\n./test/bicgstab.cpp\n./test/dynalloc.cpp\n./test/product_notemporary.cpp\n./test/geo_hyperplane.cpp\n./test/lu.cpp\n./test/qr.cpp\n./test/hessenberg.cpp\n./test/sizeof.cpp\n./test/main.h\n./test/selfadjoint.cpp\n./test/permutationmatrices.cpp\n./test/superlu_support.cpp\n./test/qtvector.cpp\n./test/geo_homogeneous.cpp\n./test/determinant.cpp\n./test/array_reverse.cpp\n./test/unalignedassert.cpp\n./test/stdlist.cpp\n./test/product_symm.cpp\n./test/corners.cpp\n./test/dontalign.cpp\n./test/visitor.cpp\n./test/geo_alignedbox.cpp\n./test/diagonalmatrices.cpp\n./test/product_small.cpp\n./test/eigensolver_generalized_real.cpp\n./test/umfpack_support.cpp\n./test/first_aligned.cpp\n./test/qr_fullpivoting.cpp\n./test/array_replicate.cpp\n./test/geo_parametrizedline.cpp\n./test/eigen2/eigen2_unalignedassert.cpp\n./test/eigen2/eigen2_prec_inverse_4x4.cpp\n./test/eigen2/eigen2_alignedbox.cpp\n./test/eigen2/eigen2_sparse_product.cpp\n./test/eigen2/eigen2_meta.cpp\n./test/eigen2/eigen2_nomalloc.cpp\n./test/eigen2/eigen2_visitor.cpp\n./test/eigen2/eigen2_packetmath.cpp\n./test/eigen2/eigen2_svd.cpp\n./test/eigen2/eigen2_mixingtypes.cpp\n./test/eigen2/eigen2_qr.cpp\n./test/eigen2/eigen2_cwiseop.cpp\n./test/eigen2/eigen2_geometry_with_eigen2_prefix.cpp\n./test/eigen2/eigen2_smallvectors.cpp\n./test/eigen2/eigen2_commainitializer.cpp\n./test/eigen2/eigen2_sparse_solvers.cpp\n./test/eigen2/eigen2_hyperplane.cpp\n./test/eigen2/eigen2_eigensolver.cpp\n./test/eigen2/eigen2_linearstructure.cpp\n./test/eigen2/eigen2_sizeof.cpp\n./test/eigen2/eigen2_parametrizedline.cpp\n./test/eigen2/eigen2_lu.cpp\n./test/eigen2/eigen2_adjoint.cpp\n./test/eigen2/eigen2_geometry.cpp\n./test/eigen2/eigen2_stdvector.cpp\n./test/eigen2/eigen2_newstdvector.cpp\n./test/eigen2/eigen2_submatrices.cpp\n./test/eigen2/sparse.h\n./test/eigen2/eigen2_swap.cpp\n./test/eigen2/eigen2_triangular.cpp\n./test/eigen2/eigen2_basicstuff.cpp\n./test/eigen2/gsl_helper.h\n./test/eigen2/eigen2_dynalloc.cpp\n./test/eigen2/eigen2_array.cpp\n./test/eigen2/eigen2_map.cpp\n./test/eigen2/main.h\n./test/eigen2/eigen2_miscmatrices.cpp\n./test/eigen2/eigen2_product_large.cpp\n./test/eigen2/eigen2_first_aligned.cpp\n./test/eigen2/eigen2_cholesky.cpp\n./test/eigen2/eigen2_determinant.cpp\n./test/eigen2/eigen2_sum.cpp\n./test/eigen2/eigen2_inverse.cpp\n./test/eigen2/eigen2_regression.cpp\n./test/eigen2/eigen2_product_small.cpp\n./test/eigen2/eigen2_qtvector.cpp\n./test/eigen2/eigen2_sparse_vector.cpp\n./test/eigen2/product.h\n./test/eigen2/eigen2_sparse_basic.cpp\n./test/eigen2/eigen2_bug_132.cpp\n./test/array.cpp\n./test/product_syrk.cpp\n./test/commainitializer.cpp\n./test/conservative_resize.cpp\n./test/qr_colpivoting.cpp\n./test/nullary.cpp\n./test/bandmatrix.cpp\n./test/pastix_support.cpp\n./test/product.h\n./test/block.cpp\n./test/vectorization_logic.cpp\n./test/jacobi.cpp\n./test/diagonal.cpp\n./test/schur_complex.cpp\n./test/sizeoverflow.cpp\n./bench/BenchTimer.h\n./bench/benchFFT.cpp\n./bench/eig33.cpp\n./bench/spbench/spbenchsolver.h\n./bench/spbench/spbenchstyle.h\n./lapack/complex_double.cpp\n./lapack/cholesky.cpp\n./lapack/lapack_common.h\n./lapack/eigenvalues.cpp\n./lapack/single.cpp\n./lapack/lu.cpp\n./lapack/complex_single.cpp\n./lapack/double.cpp\n./demos/mix_eigen_and_c/binary_library.cpp\n./demos/mix_eigen_and_c/binary_library.h\n./demos/mix_eigen_and_c/example.c\n./demos/mandelbrot/mandelbrot.cpp\n./demos/mandelbrot/mandelbrot.h\n./demos/opengl/icosphere.cpp\n./demos/opengl/icosphere.h\n./demos/opengl/camera.cpp\n./demos/opengl/quaternion_demo.h\n./demos/opengl/camera.h\n./demos/opengl/trackball.h\n./demos/opengl/gpuhelper.h\n./demos/opengl/trackball.cpp\n./demos/opengl/gpuhelper.cpp\n./demos/opengl/quaternion_demo.cpp\n./debug/gdb/printers.py\n./unsupported/test/minres.cpp\n./unsupported/test/openglsupport.cpp\n./unsupported/test/jacobisvd.cpp\n./unsupported/test/dgmres.cpp\n./unsupported/test/matrix_square_root.cpp\n./unsupported/test/bdcsvd.cpp\n./unsupported/test/matrix_exponential.cpp\n./unsupported/test/forward_adolc.cpp\n./unsupported/test/polynomialsolver.cpp\n./unsupported/test/matrix_function.cpp\n./unsupported/test/sparse_extra.cpp\n./unsupported/test/matrix_functions.h\n./unsupported/test/svd_common.h\n./unsupported/test/FFTW.cpp\n./unsupported/test/alignedvector3.cpp\n./unsupported/test/autodiff.cpp\n./unsupported/test/gmres.cpp\n./unsupported/test/BVH.cpp\n./unsupported/test/levenberg_marquardt.cpp\n./unsupported/test/matrix_power.cpp\n./unsupported/test/kronecker_product.cpp\n./unsupported/test/splines.cpp\n./unsupported/test/polynomialutils.cpp\n./unsupported/bench/bench_svd.cpp\n./unsupported/Eigen/IterativeSolvers\n./unsupported/Eigen/src/IterativeSolvers/DGMRES.h\n./unsupported/Eigen/src/IterativeSolvers/IncompleteLU.h\n./unsupported/Eigen/src/IterativeSolvers/GMRES.h\n./unsupported/Eigen/src/IterativeSolvers/IncompleteCholesky.h\n./unsupported/Eigen/src/IterativeSolvers/Scaling.h\n./unsupported/Eigen/src/IterativeSolvers/MINRES.h\n./unsupported/Eigen/src/SparseExtra/RandomSetter.h\n./unsupported/Eigen/src/SparseExtra/MatrixMarketIterator.h\n./unsupported/Eigen/src/SparseExtra/DynamicSparseMatrix.h\n./unsupported/Eigen/src/SparseExtra/MarketIO.h\n./unsupported/Eigen/src/SparseExtra/BlockOfDynamicSparseMatrix.h\n./unsupported/Eigen/src/KroneckerProduct/KroneckerTensorProduct.h\n./unsupported/Eigen/src/NonLinearOptimization/LevenbergMarquardt.h\n./unsupported/Eigen/src/NonLinearOptimization/HybridNonLinearSolver.h\n./unsupported/Eigen/src/BVH/BVAlgorithms.h\n./unsupported/Eigen/src/BVH/KdBVH.h\n./unsupported/Eigen/src/AutoDiff/AutoDiffScalar.h\n./unsupported/Eigen/src/AutoDiff/AutoDiffJacobian.h\n./unsupported/Eigen/src/AutoDiff/AutoDiffVector.h\n./unsupported/Eigen/src/Splines/Spline.h\n./unsupported/Eigen/src/Splines/SplineFitting.h\n./unsupported/Eigen/src/Splines/SplineFwd.h\n./unsupported/Eigen/src/SVD/JacobiSVD.h\n./unsupported/Eigen/src/SVD/BDCSVD.h\n./unsupported/Eigen/src/SVD/SVDBase.h\n./unsupported/Eigen/src/MatrixFunctions/MatrixFunction.h\n./unsupported/Eigen/src/MatrixFunctions/MatrixSquareRoot.h\n./unsupported/Eigen/src/MatrixFunctions/MatrixLogarithm.h\n./unsupported/Eigen/src/MatrixFunctions/StemFunction.h\n./unsupported/Eigen/src/MatrixFunctions/MatrixPower.h\n./unsupported/Eigen/src/MatrixFunctions/MatrixExponential.h\n./unsupported/Eigen/src/MatrixFunctions/MatrixFunctionAtomic.h\n./unsupported/Eigen/src/MoreVectorization/MathFunctions.h\n./unsupported/Eigen/src/LevenbergMarquardt/LevenbergMarquardt.h\n./unsupported/Eigen/src/FFT/ei_fftw_impl.h\n./unsupported/Eigen/src/FFT/ei_kissfft_impl.h\n./unsupported/Eigen/src/Polynomials/PolynomialSolver.h\n./unsupported/Eigen/src/Polynomials/Companion.h\n./unsupported/Eigen/src/Polynomials/PolynomialUtils.h\n./unsupported/Eigen/src/NumericalDiff/NumericalDiff.h\n./unsupported/Eigen/src/Skyline/SkylineProduct.h\n./unsupported/Eigen/src/Skyline/SkylineMatrixBase.h\n./unsupported/Eigen/src/Skyline/SkylineStorage.h\n./unsupported/Eigen/src/Skyline/SkylineUtil.h\n./unsupported/Eigen/src/Skyline/SkylineInplaceLU.h\n./unsupported/Eigen/src/Skyline/SkylineMatrix.h\n./unsupported/Eigen/SparseExtra\n./unsupported/Eigen/AdolcForward\n./unsupported/Eigen/KroneckerProduct\n./unsupported/Eigen/NonLinearOptimization\n./unsupported/Eigen/BVH\n./unsupported/Eigen/OpenGLSupport\n./unsupported/Eigen/ArpackSupport\n./unsupported/Eigen/AutoDiff\n./unsupported/Eigen/Splines\n./unsupported/Eigen/MPRealSupport\n./unsupported/Eigen/MatrixFunctions\n./unsupported/Eigen/MoreVectorization\n./unsupported/Eigen/LevenbergMarquardt\n./unsupported/Eigen/AlignedVector3\n./unsupported/Eigen/FFT\n./unsupported/Eigen/Polynomials\n./unsupported/Eigen/NumericalDiff\n./unsupported/Eigen/Skyline\n./COPYING.README\n./COPYING.README\n./LICENSE\n./LICENSE\n./LICENSE\n./Eigen/Eigen2Support\n./Eigen/src/Eigen2Support/VectorBlock.h\n./Eigen/src/Eigen2Support/Cwise.h\n./Eigen/src/Eigen2Support/Minor.h\n./Eigen/src/Eigen2Support/Lazy.h\n./Eigen/src/Eigen2Support/Memory.h\n./Eigen/src/Eigen2Support/MathFunctions.h\n./Eigen/src/Eigen2Support/Geometry/AlignedBox.h\n./Eigen/src/Eigen2Support/Geometry/Hyperplane.h\n./Eigen/src/Eigen2Support/Geometry/Quaternion.h\n./Eigen/src/Eigen2Support/Geometry/Rotation2D.h\n./Eigen/src/Eigen2Support/Geometry/ParametrizedLine.h\n./Eigen/src/Eigen2Support/Geometry/RotationBase.h\n./Eigen/src/Eigen2Support/Geometry/Translation.h\n./Eigen/src/Eigen2Support/Geometry/Scaling.h\n./Eigen/src/Eigen2Support/Geometry/AngleAxis.h\n./Eigen/src/Eigen2Support/Geometry/Transform.h\n./Eigen/src/Eigen2Support/TriangularSolver.h\n./Eigen/src/Eigen2Support/LU.h\n./Eigen/src/Eigen2Support/QR.h\n./Eigen/src/Eigen2Support/SVD.h\n./Eigen/src/Eigen2Support/Meta.h\n./Eigen/src/Eigen2Support/Block.h\n./Eigen/src/Eigen2Support/Macros.h\n./Eigen/src/Eigen2Support/LeastSquares.h\n./Eigen/src/Eigen2Support/CwiseOperators.h\n./Eigen/src/Jacobi/Jacobi.h\n./Eigen/src/misc/Kernel.h\n./Eigen/src/misc/SparseSolve.h\n./Eigen/src/misc/Solve.h\n./Eigen/src/misc/Image.h\n./Eigen/src/SparseCore/SparseColEtree.h\n./Eigen/src/SparseCore/SparseTranspose.h\n./Eigen/src/SparseCore/SparseUtil.h\n./Eigen/src/SparseCore/SparseCwiseBinaryOp.h\n./Eigen/src/SparseCore/SparseDiagonalProduct.h\n./Eigen/src/SparseCore/SparseProduct.h\n./Eigen/src/SparseCore/SparseDot.h\n./Eigen/src/SparseCore/SparseCwiseUnaryOp.h\n./Eigen/src/SparseCore/SparseSparseProductWithPruning.h\n./Eigen/src/SparseCore/SparseBlock.h\n./Eigen/src/SparseCore/SparseDenseProduct.h\n./Eigen/src/SparseCore/CompressedStorage.h\n./Eigen/src/SparseCore/SparseMatrixBase.h\n./Eigen/src/SparseCore/MappedSparseMatrix.h\n./Eigen/src/SparseCore/SparseTriangularView.h\n./Eigen/src/SparseCore/SparseView.h\n./Eigen/src/SparseCore/SparseFuzzy.h\n./Eigen/src/SparseCore/TriangularSolver.h\n./Eigen/src/SparseCore/SparseSelfAdjointView.h\n./Eigen/src/SparseCore/SparseMatrix.h\n./Eigen/src/SparseCore/SparseVector.h\n./Eigen/src/SparseCore/AmbiVector.h\n./Eigen/src/SparseCore/ConservativeSparseSparseProduct.h\n./Eigen/src/SparseCore/SparseRedux.h\n./Eigen/src/SparseCore/SparsePermutation.h\n./Eigen/src/Eigenvalues/RealSchur.h\n./Eigen/src/Eigenvalues/ComplexEigenSolver.h\n./Eigen/src/Eigenvalues/GeneralizedEigenSolver.h\n./Eigen/src/Eigenvalues/ComplexSchur.h\n./Eigen/src/Eigenvalues/RealQZ.h\n./Eigen/src/Eigenvalues/EigenSolver.h\n./Eigen/src/Eigenvalues/HessenbergDecomposition.h\n./Eigen/src/Eigenvalues/GeneralizedSelfAdjointEigenSolver.h\n./Eigen/src/Eigenvalues/Tridiagonalization.h\n./Eigen/src/Eigenvalues/SelfAdjointEigenSolver.h\n./Eigen/src/Eigenvalues/MatrixBaseEigenvalues.h\n./Eigen/src/SuperLUSupport/SuperLUSupport.h\n./Eigen/src/StlSupport/StdDeque.h\n./Eigen/src/StlSupport/StdVector.h\n./Eigen/src/StlSupport/StdList.h\n./Eigen/src/StlSupport/details.h\n./Eigen/src/SparseQR/SparseQR.h\n./Eigen/src/LU/Inverse.h\n./Eigen/src/LU/arch/Inverse_SSE.h\n./Eigen/src/LU/Determinant.h\n./Eigen/src/LU/PartialPivLU.h\n./Eigen/src/LU/FullPivLU.h\n./Eigen/src/UmfPackSupport/UmfPackSupport.h\n./Eigen/src/OrderingMethods/Ordering.h\n./Eigen/src/OrderingMethods/Eigen_Colamd.h\n./Eigen/src/QR/HouseholderQR.h\n./Eigen/src/QR/ColPivHouseholderQR.h\n./Eigen/src/QR/FullPivHouseholderQR.h\n./Eigen/src/SVD/JacobiSVD.h\n./Eigen/src/SVD/UpperBidiagonalization.h\n./Eigen/src/Geometry/OrthoMethods.h\n./Eigen/src/Geometry/AlignedBox.h\n./Eigen/src/Geometry/Hyperplane.h\n./Eigen/src/Geometry/Quaternion.h\n./Eigen/src/Geometry/EulerAngles.h\n./Eigen/src/Geometry/Rotation2D.h\n./Eigen/src/Geometry/ParametrizedLine.h\n./Eigen/src/Geometry/RotationBase.h\n./Eigen/src/Geometry/arch/Geometry_SSE.h\n./Eigen/src/Geometry/Umeyama.h\n./Eigen/src/Geometry/Homogeneous.h\n./Eigen/src/Geometry/Translation.h\n./Eigen/src/Geometry/Scaling.h\n./Eigen/src/Geometry/AngleAxis.h\n./Eigen/src/Geometry/Transform.h\n./Eigen/src/plugins/BlockMethods.h\n./Eigen/src/plugins/CommonCwiseUnaryOps.h\n./Eigen/src/plugins/CommonCwiseBinaryOps.h\n./Eigen/src/plugins/MatrixCwiseUnaryOps.h\n./Eigen/src/plugins/MatrixCwiseBinaryOps.h\n./Eigen/src/Householder/Householder.h\n./Eigen/src/Householder/HouseholderSequence.h\n./Eigen/src/Householder/BlockHouseholder.h\n./Eigen/src/Core/VectorBlock.h\n./Eigen/src/Core/Matrix.h\n./Eigen/src/Core/Ref.h\n./Eigen/src/Core/SelfAdjointView.h\n./Eigen/src/Core/MathFunctions.h\n./Eigen/src/Core/GlobalFunctions.h\n./Eigen/src/Core/MapBase.h\n./Eigen/src/Core/EigenBase.h\n./Eigen/src/Core/GenericPacketMath.h\n./Eigen/src/Core/NestByValue.h\n./Eigen/src/Core/CwiseUnaryOp.h\n./Eigen/src/Core/SolveTriangular.h\n./Eigen/src/Core/Fuzzy.h\n./Eigen/src/Core/Visitor.h\n./Eigen/src/Core/Map.h\n./Eigen/src/Core/NoAlias.h\n./Eigen/src/Core/Diagonal.h\n./Eigen/src/Core/StableNorm.h\n./Eigen/src/Core/CoreIterators.h\n./Eigen/src/Core/products/Parallelizer.h\n./Eigen/src/Core/products/SelfadjointMatrixVector.h\n./Eigen/src/Core/products/GeneralMatrixMatrixTriangular.h\n./Eigen/src/Core/products/TriangularSolverMatrix.h\n./Eigen/src/Core/products/GeneralMatrixMatrix.h\n./Eigen/src/Core/products/SelfadjointProduct.h\n./Eigen/src/Core/products/CoeffBasedProduct.h\n./Eigen/src/Core/products/TriangularMatrixVector.h\n./Eigen/src/Core/products/SelfadjointMatrixMatrix.h\n./Eigen/src/Core/products/TriangularSolverVector.h\n./Eigen/src/Core/products/SelfadjointRank2Update.h\n./Eigen/src/Core/products/GeneralBlockPanelKernel.h\n./Eigen/src/Core/products/GeneralMatrixVector.h\n./Eigen/src/Core/products/TriangularMatrixMatrix.h\n./Eigen/src/Core/Reverse.h\n./Eigen/src/Core/BooleanRedux.h\n./Eigen/src/Core/Replicate.h\n./Eigen/src/Core/arch/AltiVec/PacketMath.h\n./Eigen/src/Core/arch/AltiVec/Complex.h\n./Eigen/src/Core/arch/SSE/PacketMath.h\n./Eigen/src/Core/arch/SSE/Complex.h\n./Eigen/src/Core/arch/SSE/MathFunctions.h\n./Eigen/src/Core/arch/NEON/PacketMath.h\n./Eigen/src/Core/arch/NEON/Complex.h\n./Eigen/src/Core/arch/Default/Settings.h\n./Eigen/src/Core/CwiseUnaryView.h\n./Eigen/src/Core/Array.h\n./Eigen/src/Core/ArrayWrapper.h\n./Eigen/src/Core/Swap.h\n./Eigen/src/Core/Transpositions.h\n./Eigen/src/Core/Random.h\n./Eigen/src/Core/IO.h\n./Eigen/src/Core/SelfCwiseBinaryOp.h\n./Eigen/src/Core/VectorwiseOp.h\n./Eigen/src/Core/Select.h\n./Eigen/src/Core/ArrayBase.h\n./Eigen/src/Core/DenseCoeffsBase.h\n./Eigen/src/Core/DiagonalProduct.h\n./Eigen/src/Core/Assign.h\n./Eigen/src/Core/Redux.h\n./Eigen/src/Core/ForceAlignedAccess.h\n./Eigen/src/Core/BandMatrix.h\n./Eigen/src/Core/PlainObjectBase.h\n./Eigen/src/Core/DenseBase.h\n./Eigen/src/Core/Flagged.h\n./Eigen/src/Core/CwiseBinaryOp.h\n./Eigen/src/Core/ProductBase.h\n./Eigen/src/Core/TriangularMatrix.h\n./Eigen/src/Core/Transpose.h\n./Eigen/src/Core/DiagonalMatrix.h\n./Eigen/src/Core/Dot.h\n./Eigen/src/Core/Functors.h\n./Eigen/src/Core/PermutationMatrix.h\n./Eigen/src/Core/NumTraits.h\n./Eigen/src/Core/MatrixBase.h\n./Eigen/src/Core/DenseStorage.h\n./Eigen/src/Core/util/Memory.h\n./Eigen/src/Core/util/StaticAssert.h\n./Eigen/src/Core/util/BlasUtil.h\n./Eigen/src/Core/util/MatrixMapper.h\n./Eigen/src/Core/util/XprHelper.h\n./Eigen/src/Core/util/ForwardDeclarations.h\n./Eigen/src/Core/util/Meta.h\n./Eigen/src/Core/util/Macros.h\n./Eigen/src/Core/util/Constants.h\n./Eigen/src/Core/CwiseNullaryOp.h\n./Eigen/src/Core/Block.h\n./Eigen/src/Core/GeneralProduct.h\n./Eigen/src/Core/CommaInitializer.h\n./Eigen/src/Core/ReturnByValue.h\n./Eigen/src/Core/Stride.h\n./Eigen/src/SPQRSupport/SuiteSparseQRSupport.h\n./Eigen/src/SparseLU/SparseLU_column_dfs.h\n./Eigen/src/SparseLU/SparseLU_panel_dfs.h\n./Eigen/src/SparseLU/SparseLU_relax_snode.h\n./Eigen/src/SparseLU/SparseLU_panel_bmod.h\n./Eigen/src/SparseLU/SparseLU_SupernodalMatrix.h\n./Eigen/src/SparseLU/SparseLU_Utils.h\n./Eigen/src/SparseLU/SparseLU_gemm_kernel.h\n./Eigen/src/SparseLU/SparseLU_kernel_bmod.h\n./Eigen/src/SparseLU/SparseLU_pivotL.h\n./Eigen/src/SparseLU/SparseLU_Memory.h\n./Eigen/src/SparseLU/SparseLU_heap_relax_snode.h\n./Eigen/src/SparseLU/SparseLUImpl.h\n./Eigen/src/SparseLU/SparseLU_copy_to_ucol.h\n./Eigen/src/SparseLU/SparseLU_Structs.h\n./Eigen/src/SparseLU/SparseLU.h\n./Eigen/src/SparseLU/SparseLU_column_bmod.h\n./Eigen/src/SparseLU/SparseLU_pruneL.h\n./Eigen/src/IterativeLinearSolvers/IncompleteLUT.h\n./Eigen/src/IterativeLinearSolvers/BasicPreconditioners.h\n./Eigen/src/IterativeLinearSolvers/IterativeSolverBase.h\n./Eigen/src/IterativeLinearSolvers/ConjugateGradient.h\n./Eigen/src/IterativeLinearSolvers/BiCGSTAB.h\n./Eigen/src/SparseCholesky/SimplicialCholesky.h\n./Eigen/src/Cholesky/LDLT.h\n./Eigen/src/Cholesky/LLT.h\n./Eigen/src/CholmodSupport/CholmodSupport.h\n./Eigen/src/PaStiXSupport/PaStiXSupport.h\n./Eigen/src/MetisSupport/MetisSupport.h\n./Eigen/StdVector\n./Eigen/Core\n./Eigen/OrderingMethods\n./Eigen/SparseLU\n./Eigen/StdList\n./Eigen/StdDeque\n./Eigen/SparseCholesky\n./Eigen/SparseCore\n./scripts/relicense.py\n./scripts/relicense.py\n./blas/BandTriangularSolver.h\n./blas/PackedTriangularMatrixVector.h\n./blas/complex_double.cpp\n./blas/level2_real_impl.h\n./blas/level1_cplx_impl.h\n./blas/level1_impl.h\n./blas/level1_real_impl.h\n./blas/level3_impl.h\n./blas/single.cpp\n./blas/level2_cplx_impl.h\n./blas/PackedSelfadjointProduct.h\n./blas/Rank2Update.h\n./blas/complex_single.cpp\n./blas/PackedTriangularSolverVector.h\n./blas/double.cpp\n./blas/common.h\n./blas/level2_impl.h\n./blas/GeneralRank1Update.h\n\nMozilla Public License Version 2.0\n==================================\n\n1. Definitions\n--------------\n\n1.1. \"Contributor\"\n    means each individual or legal entity that creates, contributes to\n    the creation of, or owns Covered Software.\n\n1.2. \"Contributor Version\"\n    means the combination of the Contributions of others (if any) used\n    by a Contributor and that particular Contributor's Contribution.\n\n1.3. \"Contribution\"\n    means Covered Software of a particular Contributor.\n\n1.4. \"Covered Software\"\n    means Source Code Form to which the initial Contributor has attached\n    the notice in Exhibit A, the Executable Form of such Source Code\n    Form, and Modifications of such Source Code Form, in each case\n    including portions thereof.\n\n1.5. \"Incompatible With Secondary Licenses\"\n    means\n\n    (a) that the initial Contributor has attached the notice described\n        in Exhibit B to the Covered Software; or\n\n    (b) that the Covered Software was made available under the terms of\n        version 1.1 or earlier of the License, but not also under the\n        terms of a Secondary License.\n\n1.6. \"Executable Form\"\n    means any form of the work other than Source Code Form.\n\n1.7. \"Larger Work\"\n    means a work that combines Covered Software with other material, in \n    a separate file or files, that is not Covered Software.\n\n1.8. \"License\"\n    means this document.\n\n1.9. \"Licensable\"\n    means having the right to grant, to the maximum extent possible,\n    whether at the time of the initial grant or subsequently, any and\n    all of the rights conveyed by this License.\n\n1.10. \"Modifications\"\n    means any of the following:\n\n    (a) any file in Source Code Form that results from an addition to,\n        deletion from, or modification of the contents of Covered\n        Software; or\n\n    (b) any new file in Source Code Form that contains any Covered\n        Software.\n\n1.11. \"Patent Claims\" of a Contributor\n    means any patent claim(s), including without limitation, method,\n    process, and apparatus claims, in any patent Licensable by such\n    Contributor that would be infringed, but for the grant of the\n    License, by the making, using, selling, offering for sale, having\n    made, import, or transfer of either its Contributions or its\n    Contributor Version.\n\n1.12. \"Secondary License\"\n    means either the GNU General Public License, Version 2.0, the GNU\n    Lesser General Public License, Version 2.1, the GNU Affero General\n    Public License, Version 3.0, or any later versions of those\n    licenses.\n\n1.13. \"Source Code Form\"\n    means the form of the work preferred for making modifications.\n\n1.14. \"You\" (or \"Your\")\n    means an individual or a legal entity exercising rights under this\n    License. For legal entities, \"You\" includes any entity that\n    controls, is controlled by, or is under common control with You. For\n    purposes of this definition, \"control\" means (a) the power, direct\n    or indirect, to cause the direction or management of such entity,\n    whether by contract or otherwise, or (b) ownership of more than\n    fifty percent (50%) of the outstanding shares or beneficial\n    ownership of such entity.\n\n2. License Grants and Conditions\n--------------------------------\n\n2.1. Grants\n\nEach Contributor hereby grants You a world-wide, royalty-free,\nnon-exclusive license:\n\n(a) under intellectual property rights (other than patent or trademark)\n    Licensable by such Contributor to use, reproduce, make available,\n    modify, display, perform, distribute, and otherwise exploit its\n    Contributions, either on an unmodified basis, with Modifications, or\n    as part of a Larger Work; and\n\n(b) under Patent Claims of such Contributor to make, use, sell, offer\n    for sale, have made, import, and otherwise transfer either its\n    Contributions or its Contributor Version.\n\n2.2. Effective Date\n\nThe licenses granted in Section 2.1 with respect to any Contribution\nbecome effective for each Contribution on the date the Contributor first\ndistributes such Contribution.\n\n2.3. Limitations on Grant Scope\n\nThe licenses granted in this Section 2 are the only rights granted under\nthis License. No additional rights or licenses will be implied from the\ndistribution or licensing of Covered Software under this License.\nNotwithstanding Section 2.1(b) above, no patent license is granted by a\nContributor:\n\n(a) for any code that a Contributor has removed from Covered Software;\n    or\n\n(b) for infringements caused by: (i) Your and any other third party's\n    modifications of Covered Software, or (ii) the combination of its\n    Contributions with other software (except as part of its Contributor\n    Version); or\n\n(c) under Patent Claims infringed by Covered Software in the absence of\n    its Contributions.\n\nThis License does not grant any rights in the trademarks, service marks,\nor logos of any Contributor (except as may be necessary to comply with\nthe notice requirements in Section 3.4).\n\n2.4. Subsequent Licenses\n\nNo Contributor makes additional grants as a result of Your choice to\ndistribute the Covered Software under a subsequent version of this\nLicense (see Section 10.2) or under the terms of a Secondary License (if\npermitted under the terms of Section 3.3).\n\n2.5. Representation\n\nEach Contributor represents that the Contributor believes its\nContributions are its original creation(s) or it has sufficient rights\nto grant the rights to its Contributions conveyed by this License.\n\n2.6. Fair Use\n\nThis License is not intended to limit any rights You have under\napplicable copyright doctrines of fair use, fair dealing, or other\nequivalents.\n\n2.7. Conditions\n\nSections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted\nin Section 2.1.\n\n3. Responsibilities\n-------------------\n\n3.1. Distribution of Source Form\n\nAll distribution of Covered Software in Source Code Form, including any\nModifications that You create or to which You contribute, must be under\nthe terms of this License. You must inform recipients that the Source\nCode Form of the Covered Software is governed by the terms of this\nLicense, and how they can obtain a copy of this License. You may not\nattempt to alter or restrict the recipients' rights in the Source Code\nForm.\n\n3.2. Distribution of Executable Form\n\nIf You distribute Covered Software in Executable Form then:\n\n(a) such Covered Software must also be made available in Source Code\n    Form, as described in Section 3.1, and You must inform recipients of\n    the Executable Form how they can obtain a copy of such Source Code\n    Form by reasonable means in a timely manner, at a charge no more\n    than the cost of distribution to the recipient; and\n\n(b) You may distribute such Executable Form under the terms of this\n    License, or sublicense it under different terms, provided that the\n    license for the Executable Form does not attempt to limit or alter\n    the recipients' rights in the Source Code Form under this License.\n\n3.3. Distribution of a Larger Work\n\nYou may create and distribute a Larger Work under terms of Your choice,\nprovided that You also comply with the requirements of this License for\nthe Covered Software. If the Larger Work is a combination of Covered\nSoftware with a work governed by one or more Secondary Licenses, and the\nCovered Software is not Incompatible With Secondary Licenses, this\nLicense permits You to additionally distribute such Covered Software\nunder the terms of such Secondary License(s), so that the recipient of\nthe Larger Work may, at their option, further distribute the Covered\nSoftware under the terms of either this License or such Secondary\nLicense(s).\n\n3.4. Notices\n\nYou may not remove or alter the substance of any license notices\n(including copyright notices, patent notices, disclaimers of warranty,\nor limitations of liability) contained within the Source Code Form of\nthe Covered Software, except that You may alter any license notices to\nthe extent required to remedy known factual inaccuracies.\n\n3.5. Application of Additional Terms\n\nYou may choose to offer, and to charge a fee for, warranty, support,\nindemnity or liability obligations to one or more recipients of Covered\nSoftware. However, You may do so only on Your own behalf, and not on\nbehalf of any Contributor. You must make it absolutely clear that any\nsuch warranty, support, indemnity, or liability obligation is offered by\nYou alone, and You hereby agree to indemnify every Contributor for any\nliability incurred by such Contributor as a result of warranty, support,\nindemnity or liability terms You offer. You may include additional\ndisclaimers of warranty and limitations of liability specific to any\njurisdiction.\n\n4. Inability to Comply Due to Statute or Regulation\n---------------------------------------------------\n\nIf it is impossible for You to comply with any of the terms of this\nLicense with respect to some or all of the Covered Software due to\nstatute, judicial order, or regulation then You must: (a) comply with\nthe terms of this License to the maximum extent possible; and (b)\ndescribe the limitations and the code they affect. Such description must\nbe placed in a text file included with all distributions of the Covered\nSoftware under this License. Except to the extent prohibited by statute\nor regulation, such description must be sufficiently detailed for a\nrecipient of ordinary skill to be able to understand it.\n\n5. Termination\n--------------\n\n5.1. The rights granted under this License will terminate automatically\nif You fail to comply with any of its terms. However, if You become\ncompliant, then the rights granted under this License from a particular\nContributor are reinstated (a) provisionally, unless and until such\nContributor explicitly and finally terminates Your grants, and (b) on an\nongoing basis, if such Contributor fails to notify You of the\nnon-compliance by some reasonable means prior to 60 days after You have\ncome back into compliance. Moreover, Your grants from a particular\nContributor are reinstated on an ongoing basis if such Contributor\nnotifies You of the non-compliance by some reasonable means, this is the\nfirst time You have received notice of non-compliance with this License\nfrom such Contributor, and You become compliant prior to 30 days after\nYour receipt of the notice.\n\n5.2. If You initiate litigation against any entity by asserting a patent\ninfringement claim (excluding declaratory judgment actions,\ncounter-claims, and cross-claims) alleging that a Contributor Version\ndirectly or indirectly infringes any patent, then the rights granted to\nYou by any and all Contributors for the Covered Software under Section\n2.1 of this License shall terminate.\n\n5.3. In the event of termination under Sections 5.1 or 5.2 above, all\nend user license agreements (excluding distributors and resellers) which\nhave been validly granted by You or Your distributors under this License\nprior to termination shall survive termination.\n\n************************************************************************\n*                                                                      *\n*  6. Disclaimer of Warranty                                           *\n*  -------------------------                                           *\n*                                                                      *\n*  Covered Software is provided under this License on an \"as is\"       *\n*  basis, without warranty of any kind, either expressed, implied, or  *\n*  statutory, including, without limitation, warranties that the       *\n*  Covered Software is free of defects, merchantable, fit for a        *\n*  particular purpose or non-infringing. The entire risk as to the     *\n*  quality and performance of the Covered Software is with You.        *\n*  Should any Covered Software prove defective in any respect, You     *\n*  (not any Contributor) assume the cost of any necessary servicing,   *\n*  repair, or correction. This disclaimer of warranty constitutes an   *\n*  essential part of this License. No use of any Covered Software is   *\n*  authorized under this License except under this disclaimer.         *\n*                                                                      *\n************************************************************************\n\n************************************************************************\n*                                                                      *\n*  7. Limitation of Liability                                          *\n*  --------------------------                                          *\n*                                                                      *\n*  Under no circumstances and under no legal theory, whether tort      *\n*  (including negligence), contract, or otherwise, shall any           *\n*  Contributor, or anyone who distributes Covered Software as          *\n*  permitted above, be liable to You for any direct, indirect,         *\n*  special, incidental, or consequential damages of any character      *\n*  including, without limitation, damages for lost profits, loss of    *\n*  goodwill, work stoppage, computer failure or malfunction, or any    *\n*  and all other commercial damages or losses, even if such party      *\n*  shall have been informed of the possibility of such damages. This   *\n*  limitation of liability shall not apply to liability for death or   *\n*  personal injury resulting from such party's negligence to the       *\n*  extent applicable law prohibits such limitation. Some               *\n*  jurisdictions do not allow the exclusion or limitation of           *\n*  incidental or consequential damages, so this exclusion and          *\n*  limitation may not apply to You.                                    *\n*                                                                      *\n************************************************************************\n\n8. Litigation\n-------------\n\nAny litigation relating to this License may be brought only in the\ncourts of a jurisdiction where the defendant maintains its principal\nplace of business and such litigation shall be governed by laws of that\njurisdiction, without reference to its conflict-of-law provisions.\nNothing in this Section shall prevent a party's ability to bring\ncross-claims or counter-claims.\n\n9. Miscellaneous\n----------------\n\nThis License represents the complete agreement concerning the subject\nmatter hereof. If any provision of this License is held to be\nunenforceable, such provision shall be reformed only to the extent\nnecessary to make it enforceable. Any law or regulation which provides\nthat the language of a contract shall be construed against the drafter\nshall not be used to construe this License against a Contributor.\n\n10. Versions of the License\n---------------------------\n\n10.1. New Versions\n\nMozilla Foundation is the license steward. Except as provided in Section\n10.3, no one other than the license steward has the right to modify or\npublish new versions of this License. Each version will be given a\ndistinguishing version number.\n\n10.2. Effect of New Versions\n\nYou may distribute the Covered Software under the terms of the version\nof the License under which You originally received the Covered Software,\nor under the terms of any subsequent version published by the license\nsteward.\n\n10.3. Modified Versions\n\nIf you create software not governed by this License, and you want to\ncreate a new license for such software, you may create and use a\nmodified version of this License if you rename the license and remove\nany references to the name of the license steward (except to note that\nsuch modified license differs from this License).\n\n10.4. Distributing Source Code Form that is Incompatible With Secondary\nLicenses\n\nIf You choose to distribute Source Code Form that is Incompatible With\nSecondary Licenses under the terms of this version of the License, the\nnotice described in Exhibit B of this License must be attached.\n\nExhibit A - Source Code Form License Notice\n-------------------------------------------\n\n  This Source Code Form is subject to the terms of the Mozilla Public\n  License, v. 2.0. If a copy of the MPL was not distributed with this\n  file, You can obtain one at http://mozilla.org/MPL/2.0/.\n\nIf it is not possible or desirable to put the notice in a particular\nfile, then You may include the notice in a location (such as a LICENSE\nfile in a relevant directory) where a recipient would be likely to look\nfor such a notice.\n\nYou may add additional accurate notices of copyright ownership.\n\nExhibit B - \"Incompatible With Secondary Licenses\" Notice\n---------------------------------------------------------\n\n  This Source Code Form is \"Incompatible With Secondary Licenses\", as\n  defined by the Mozilla Public License, v. 2.0.\n\n----------------------------------------------------------------------\nFollowing applies to:\n./doc/UsingIntelMKL.dox\n./doc/UsingIntelMKL.dox\n./Eigen/src/Eigenvalues/ComplexSchur_MKL.h\n./Eigen/src/Eigenvalues/ComplexSchur_MKL.h\n./Eigen/src/Eigenvalues/SelfAdjointEigenSolver_MKL.h\n./Eigen/src/Eigenvalues/SelfAdjointEigenSolver_MKL.h\n./Eigen/src/Eigenvalues/RealSchur_MKL.h\n./Eigen/src/Eigenvalues/RealSchur_MKL.h\n./Eigen/src/LU/arch/Inverse_SSE.h\n./Eigen/src/LU/arch/Inverse_SSE.h\n./Eigen/src/LU/PartialPivLU_MKL.h\n./Eigen/src/LU/PartialPivLU_MKL.h\n./Eigen/src/QR/HouseholderQR_MKL.h\n./Eigen/src/QR/HouseholderQR_MKL.h\n./Eigen/src/QR/ColPivHouseholderQR_MKL.h\n./Eigen/src/QR/ColPivHouseholderQR_MKL.h\n./Eigen/src/SVD/JacobiSVD_MKL.h\n./Eigen/src/SVD/JacobiSVD_MKL.h\n./Eigen/src/PardisoSupport/PardisoSupport.h\n./Eigen/src/PardisoSupport/PardisoSupport.h\n./Eigen/src/Core/Assign_MKL.h\n./Eigen/src/Core/Assign_MKL.h\n./Eigen/src/Core/products/SelfadjointMatrixVector_MKL.h\n./Eigen/src/Core/products/SelfadjointMatrixVector_MKL.h\n./Eigen/src/Core/products/GeneralMatrixVector_MKL.h\n./Eigen/src/Core/products/GeneralMatrixVector_MKL.h\n./Eigen/src/Core/products/SelfadjointMatrixMatrix_MKL.h\n./Eigen/src/Core/products/SelfadjointMatrixMatrix_MKL.h\n./Eigen/src/Core/products/TriangularMatrixMatrix_MKL.h\n./Eigen/src/Core/products/TriangularMatrixMatrix_MKL.h\n./Eigen/src/Core/products/GeneralMatrixMatrix_MKL.h\n./Eigen/src/Core/products/GeneralMatrixMatrix_MKL.h\n./Eigen/src/Core/products/TriangularMatrixVector_MKL.h\n./Eigen/src/Core/products/TriangularMatrixVector_MKL.h\n./Eigen/src/Core/products/GeneralMatrixMatrixTriangular_MKL.h\n./Eigen/src/Core/products/GeneralMatrixMatrixTriangular_MKL.h\n./Eigen/src/Core/products/TriangularSolverMatrix_MKL.h\n./Eigen/src/Core/products/TriangularSolverMatrix_MKL.h\n./Eigen/src/Core/util/MKL_support.h\n./Eigen/src/Core/util/MKL_support.h\n./Eigen/src/Cholesky/LLT_MKL.h\n./Eigen/src/Cholesky/LLT_MKL.h\n\n/*\n Copyright (c) 2011, Intel Corporation. All rights reserved.\n\n Redistribution and use in source and binary forms, with or without\n modification, are permitted provided that the following conditions\n are met:\n\n * Redistributions of source code must retain the above copyright\n   notice, this list of conditions and the following disclaimer.  *\n   Redistributions in binary form must reproduce the above copyright\n   notice, this list of conditions and the following disclaimer in the\n   documentation and/or other materials provided with the\n   distribution.  * Neither the name of Intel Corporation nor the\n   names of its contributors may be used to endorse or promote\n   products derived from this software without specific prior written\n   permission.\n\n THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n\n----------------------------------------------------------------------\nFollowing applies to:\n./unsupported/Eigen/src/LevenbergMarquardt/LevenbergMarquardt.h\n./unsupported/Eigen/src/LevenbergMarquardt/LMcovar.h\n./unsupported/Eigen/src/LevenbergMarquardt/LMonestep.h\n./unsupported/Eigen/src/LevenbergMarquardt/LMpar.h\n./unsupported/Eigen/src/LevenbergMarquardt/LMqrsolv.h\n\nMinpack Copyright Notice (1999) University of Chicago.  All rights\nreserved\n\nRedistribution and use in source and binary forms, with or\nwithout modification, are permitted provided that the\nfollowing conditions are met:\n\n1. Redistributions of source code must retain the above\ncopyright notice, this list of conditions and the following\ndisclaimer.\n\n2. Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following\ndisclaimer in the documentation and/or other materials\nprovided with the distribution.\n\n3. The end-user documentation included with the\nredistribution, if any, must include the following\nacknowledgment:\n\n   \"This product includes software developed by the\n   University of Chicago, as Operator of Argonne National\n   Laboratory.\n\nAlternately, this acknowledgment may appear in the software\nitself, if and wherever such third-party acknowledgments\nnormally appear.\n\n4. WARRANTY DISCLAIMER. THE SOFTWARE IS SUPPLIED \"AS IS\"\nWITHOUT WARRANTY OF ANY KIND. THE COPYRIGHT HOLDER, THE\nUNITED STATES, THE UNITED STATES DEPARTMENT OF ENERGY, AND\nTHEIR EMPLOYEES: (1) DISCLAIM ANY WARRANTIES, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES\nOF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE\nOR NON-INFRINGEMENT, (2) DO NOT ASSUME ANY LEGAL LIABILITY\nOR RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS, OR\nUSEFULNESS OF THE SOFTWARE, (3) DO NOT REPRESENT THAT USE OF\nTHE SOFTWARE WOULD NOT INFRINGE PRIVATELY OWNED RIGHTS, (4)\nDO NOT WARRANT THAT THE SOFTWARE WILL FUNCTION\nUNINTERRUPTED, THAT IT IS ERROR-FREE OR THAT ANY ERRORS WILL\nBE CORRECTED.\n\n5. LIMITATION OF LIABILITY. IN NO EVENT WILL THE COPYRIGHT\nHOLDER, THE UNITED STATES, THE UNITED STATES DEPARTMENT OF\nENERGY, OR THEIR EMPLOYEES: BE LIABLE FOR ANY INDIRECT,\nINCIDENTAL, CONSEQUENTIAL, SPECIAL OR PUNITIVE DAMAGES OF\nANY KIND OR NATURE, INCLUDING BUT NOT LIMITED TO LOSS OF\nPROFITS OR LOSS OF DATA, FOR ANY REASON WHATSOEVER, WHETHER\nSUCH LIABILITY IS ASSERTED ON THE BASIS OF CONTRACT, TORT\n(INCLUDING NEGLIGENCE OR STRICT LIABILITY), OR OTHERWISE,\nEVEN IF ANY OF SAID PARTIES HAS BEEN WARNED OF THE\nPOSSIBILITY OF SUCH LOSS OR DAMAGES.\n"
  },
  {
    "path": "third_party/eigen3/README.md",
    "content": "# Eigen3 Bazel\n\nThis is taken and modified from [tensorflow/third_party/eigen3](https://github.com/tensorflow/tensorflow/tree/9e3b0b661e7e7672f80186a34d551e217c0a4a24/third_party/eigen3).\n"
  },
  {
    "path": "third_party/eigen3/add_sfinae_to_num_traits.patch",
    "content": "diff --git a/Eigen/src/Core/NumTraits.h b/Eigen/src/Core/NumTraits.h\nindex ff33aa6fb..89ba2bc0e 100644\n--- a/Eigen/src/Core/NumTraits.h\n+++ b/Eigen/src/Core/NumTraits.h\n@@ -151,7 +151,7 @@ EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC Tgt bit_cast(const Src& src) {\n   * \\li quiet_NaN function returning a non-signaling \"not-a-number\", if available.\n   */\n \n-template<typename T> struct GenericNumTraits\n+template<typename T, typename SFINAE = void> struct GenericNumTraits\n {\n   enum {\n     IsInteger = std::numeric_limits<T>::is_integer,\n@@ -227,7 +227,7 @@ template<typename T> struct GenericNumTraits\n   }\n };\n \n-template<typename T> struct NumTraits : GenericNumTraits<T>\n+template<typename T, typename SFINAE> struct NumTraits : GenericNumTraits<T>\n {};\n \n template<> struct NumTraits<float>\ndiff --git a/Eigen/src/Core/util/ForwardDeclarations.h b/Eigen/src/Core/util/ForwardDeclarations.h\nindex 4d2d77182..feacdcfe4 100644\n--- a/Eigen/src/Core/util/ForwardDeclarations.h\n+++ b/Eigen/src/Core/util/ForwardDeclarations.h\n@@ -44,7 +44,7 @@ template< typename T> struct evaluator;\n \n } // end namespace internal\n \n-template<typename T> struct NumTraits;\n+template<typename T, typename SFINAE> struct NumTraits;\n \n template<typename Derived> struct EigenBase;\n template<typename Derived> class DenseBase;\ndiff --git a/Eigen/src/Core/util/Meta.h b/Eigen/src/Core/util/Meta.h\nindex a192aac87..bf87eaaea 100644\n--- a/Eigen/src/Core/util/Meta.h\n+++ b/Eigen/src/Core/util/Meta.h\n@@ -409,7 +409,7 @@ struct aligned_storage {\n \n } // end namespace internal\n \n-template<typename T> struct NumTraits;\n+template<typename T, typename SFINAE = void> struct NumTraits;\n \n namespace numext {\n \n"
  },
  {
    "path": "third_party/eigen3/add_sfinae_to_scalar_random_op.patch",
    "content": "diff --git a/Eigen/src/Core/Random.h b/Eigen/src/Core/Random.h\nindex fab6889dc..f2df7b1f5 100644\n--- a/Eigen/src/Core/Random.h\n+++ b/Eigen/src/Core/Random.h\n@@ -16,7 +16,7 @@ namespace Eigen {\n \n namespace internal {\n \n-template<typename Scalar> struct scalar_random_op {\n+template<typename Scalar, typename SFINAE> struct scalar_random_op {\n   inline const Scalar operator() () const { return random<Scalar>(); }\n };\n \ndiff --git a/Eigen/src/Core/util/ForwardDeclarations.h b/Eigen/src/Core/util/ForwardDeclarations.h\nindex 8963019a9..4d2d77182 100644\n--- a/Eigen/src/Core/util/ForwardDeclarations.h\n+++ b/Eigen/src/Core/util/ForwardDeclarations.h\n@@ -198,7 +198,7 @@ template<typename Scalar> struct scalar_inverse_op;\n template<typename Scalar> struct scalar_square_op;\n template<typename Scalar> struct scalar_cube_op;\n template<typename Scalar, typename NewType> struct scalar_cast_op;\n-template<typename Scalar> struct scalar_random_op;\n+template<typename Scalar, typename SFINAE = void> struct scalar_random_op;\n template<typename Scalar> struct scalar_constant_op;\n template<typename Scalar> struct scalar_identity_op;\n template<typename Scalar> struct scalar_sign_op;\n"
  },
  {
    "path": "third_party/eigen3/allow_field_inverse.patch",
    "content": "diff --git a/Eigen/src/Core/functors/AssignmentFunctors.h b/Eigen/src/Core/functors/AssignmentFunctors.h\nindex ca0899182..ea4d7fe6c 100644\n--- a/Eigen/src/Core/functors/AssignmentFunctors.h\n+++ b/Eigen/src/Core/functors/AssignmentFunctors.h\n@@ -107,7 +107,7 @@ struct functor_traits<mul_assign_op<DstScalar,SrcScalar> > {\n   */\n template<typename DstScalar, typename SrcScalar=DstScalar> struct div_assign_op {\n \n-  EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void assignCoeff(DstScalar& a, const SrcScalar& b) const { a /= b; }\n+  EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void assignCoeff(DstScalar& a, const SrcScalar& b) const { a = ScalarTraits<DstScalar>::Divide(a, b); }\n \n   template<int Alignment, typename Packet>\n   EIGEN_STRONG_INLINE void assignPacket(DstScalar* a, const Packet& b) const\ndiff --git a/Eigen/src/Core/products/TriangularSolverMatrix.h b/Eigen/src/Core/products/TriangularSolverMatrix.h\nindex 23a8453bd..aa3e2db55 100644\n--- a/Eigen/src/Core/products/TriangularSolverMatrix.h\n+++ b/Eigen/src/Core/products/TriangularSolverMatrix.h\n@@ -60,7 +60,7 @@ EIGEN_STRONG_INLINE void trsmKernelL<Scalar, Index, Mode, Conjugate, TriStorageO\n       Index s  = TriStorageOrder==RowMajor ? (IsLower ? 0 : i+1)\n         :  IsLower ? i+1 : i-rs;\n \n-      Scalar a = (Mode & UnitDiag) ? Scalar::One() : Scalar::One()/conj(tri(i,i));\n+      Scalar a = (Mode & UnitDiag) ? Scalar::One() : ScalarTraits<Scalar>::Divide(Scalar::One(), conj(tri(i,i)));\n       for (Index j=0; j<otherSize; ++j)\n       {\n         if (TriStorageOrder==RowMajor)\ndiff --git a/Eigen/src/Core/util/ForwardDeclarations.h b/Eigen/src/Core/util/ForwardDeclarations.h\nindex 2421fa779..6fb286aab 100644\n--- a/Eigen/src/Core/util/ForwardDeclarations.h\n+++ b/Eigen/src/Core/util/ForwardDeclarations.h\n@@ -50,12 +50,15 @@ template <typename Scalar, typename SFINAE = void> struct ScalarTraits {\n   EIGEN_CONSTEXPR static Scalar Zero() { return Scalar(0); }\n   EIGEN_CONSTEXPR static Scalar One() { return Scalar(1); }\n   EIGEN_CONSTEXPR static Scalar MinsOne() { return Scalar(-1); }\n+  template <typename T>\n+  EIGEN_CONSTEXPR static auto Divide(Scalar a, T b) { return a / b; }\n };\n \n template <typename Scalar> struct ScalarTraits<Scalar, std::enable_if_t<NumTraits<Scalar>::IsField>> {\n   EIGEN_CONSTEXPR static Scalar Zero() { return Scalar::Zero(); }\n   EIGEN_CONSTEXPR static Scalar One() { return Scalar::One(); }\n   EIGEN_CONSTEXPR static Scalar MinusOne() { return Scalar::MinusOne(); }\n+  EIGEN_CONSTEXPR static Scalar Divide(const Scalar& a, const Scalar& b) { return *(a / b); }\n };\n \n template<typename Derived> struct EigenBase;\ndiff --git a/Eigen/src/Core/util/StaticAssert.h b/Eigen/src/Core/util/StaticAssert.h\nindex c938eb800..6bd8428e8 100644\n--- a/Eigen/src/Core/util/StaticAssert.h\n+++ b/Eigen/src/Core/util/StaticAssert.h\n@@ -79,7 +79,7 @@\n      )\n \n #define EIGEN_STATIC_ASSERT_NON_INTEGER(TYPE) \\\n-    EIGEN_STATIC_ASSERT(!Eigen::NumTraits<TYPE>::IsInteger, THIS_FUNCTION_IS_NOT_FOR_INTEGER_NUMERIC_TYPES)\n+    EIGEN_STATIC_ASSERT(!Eigen::NumTraits<TYPE>::IsInteger || NumTraits<TYPE>::IsField, THIS_FUNCTION_IS_NOT_FOR_INTEGER_NUMERIC_TYPES)\n \n \n // static assertion failing if it is guaranteed at compile-time that the two matrix expression types have different sizes\ndiff --git a/Eigen/src/LU/InverseImpl.h b/Eigen/src/LU/InverseImpl.h\nindex bcfe7031e..1b9e4be70 100644\n--- a/Eigen/src/LU/InverseImpl.h\n+++ b/Eigen/src/LU/InverseImpl.h\n@@ -349,7 +349,7 @@ template<typename Derived>\n EIGEN_DEVICE_FUNC\n inline const Inverse<Derived> MatrixBase<Derived>::inverse() const\n {\n-  EIGEN_STATIC_ASSERT(!NumTraits<Scalar>::IsInteger,THIS_FUNCTION_IS_NOT_FOR_INTEGER_NUMERIC_TYPES)\n+  EIGEN_STATIC_ASSERT(!NumTraits<Scalar>::IsInteger || NumTraits<Scalar>::IsField,THIS_FUNCTION_IS_NOT_FOR_INTEGER_NUMERIC_TYPES)\n   eigen_assert(rows() == cols());\n   return Inverse<Derived>(derived());\n }\ndiff --git a/Eigen/src/LU/PartialPivLU.h b/Eigen/src/LU/PartialPivLU.h\nindex bee743794..1e6e72440 100644\n--- a/Eigen/src/LU/PartialPivLU.h\n+++ b/Eigen/src/LU/PartialPivLU.h\n@@ -276,7 +276,7 @@ template<typename MatrixType_, typename PermutationIndex_> class PartialPivLU\n     PermutationType m_p;\n     TranspositionType m_rowsTranspositions;\n     RealScalar m_l1_norm;\n-    signed char m_det_p;\n+    Scalar m_det_p;\n     bool m_isInitialized;\n };\n \n@@ -538,7 +538,7 @@ void PartialPivLU<MatrixType, PermutationIndex>::compute()\n \n   typename TranspositionType::StorageIndex nb_transpositions;\n   internal::partial_lu_inplace(m_lu, m_rowsTranspositions, nb_transpositions);\n-  m_det_p = (nb_transpositions%2) ? -1 : 1;\n+  m_det_p = (nb_transpositions%2) ? -Scalar::One() : Scalar::One();\n \n   m_p = m_rowsTranspositions;\n \n@@ -549,7 +549,7 @@ template<typename MatrixType, typename PermutationIndex>\n typename PartialPivLU<MatrixType, PermutationIndex>::Scalar PartialPivLU<MatrixType, PermutationIndex>::determinant() const\n {\n   eigen_assert(m_isInitialized && \"PartialPivLU is not initialized.\");\n-  return Scalar(m_det_p) * m_lu.diagonal().prod();\n+  return m_det_p * m_lu.diagonal().prod();\n }\n \n /** \\returns the matrix represented by the decomposition,\n"
  },
  {
    "path": "third_party/eigen3/eigen_archive.BUILD",
    "content": "# Description:\n#   Eigen is a C++ template library for linear algebra: vectors,\n#   matrices, and related algorithms.\n# This is the BUILD file used for the @eigen_archive external repository.\n\nlicenses([\n    # Note: Although Eigen also includes GPL V3 and LGPL v2.1+ code, TensorFlow\n    #       has taken special care to not reference any restricted code.\n    \"reciprocal\",  # MPL2\n    \"notice\",  # Portions BSD\n])\n\nexports_files([\"COPYING.MPL2\"])\n\nALL_FILES_WITH_EXTENSIONS = glob([\"**/*.*\"])\n\n# Top-level headers, excluding anything in one of the  ../src/.. directories.\nEIGEN_HEADERS = glob(\n    [\n        \"Eigen/*\",\n        \"unsupported/Eigen/*\",\n        \"unsupported/Eigen/CXX11/*\",\n    ],\n    exclude = [\n        \"**/src/**\",\n    ] + ALL_FILES_WITH_EXTENSIONS,\n)\n\n# Internal eigen headers, known to be under an MPL2 license.\nEIGEN_MPL2_SOURCES = glob(\n    [\n        \"Eigen/**/src/**/*.h\",\n        \"Eigen/**/src/**/*.inc\",\n        \"unsupported/Eigen/**/src/**/*.h\",\n        \"unsupported/Eigen/**/src/**/*.inc\",\n    ],\n    exclude = [\n        # This guarantees that any file depending on non MPL2 licensed code\n        # will not compile.\n        \"Eigen/src/Core/util/NonMPL2.h\",\n    ],\n)\n\nalias(\n    name = \"eigen3\",\n    actual = \"@kroma_network_tachyon//third_party/eigen3\",\n    visibility = [\"//visibility:public\"],\n)\n\ncc_library(\n    name = \"eigen3_internal\",\n    srcs = EIGEN_MPL2_SOURCES,\n    hdrs = EIGEN_HEADERS,\n    defines = [\n        # This define (mostly) guarantees we don't link any problematic\n        # code. We use it, but we do not rely on it, as evidenced above.\n        \"EIGEN_MPL2_ONLY\",\n        \"EIGEN_MAX_ALIGN_BYTES=64\",\n    ],\n    includes = [\".\"],\n    visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n    name = \"eigen_header_files\",\n    srcs = EIGEN_HEADERS,\n    visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n    name = \"eigen_source_files\",\n    srcs = EIGEN_MPL2_SOURCES,\n    visibility = [\"//visibility:public\"],\n)\n"
  },
  {
    "path": "third_party/eigen3/unsupported/Eigen/CXX11/Tensor",
    "content": "#include \"unsupported/Eigen/CXX11/Tensor\"\n"
  },
  {
    "path": "third_party/eigen3/unsupported/Eigen/CXX11/ThreadPool",
    "content": "#include \"unsupported/Eigen/CXX11/ThreadPool\"\n"
  },
  {
    "path": "third_party/eigen3/unsupported/Eigen/MatrixFunctions",
    "content": "#include \"unsupported/Eigen/MatrixFunctions\"\n"
  },
  {
    "path": "third_party/eigen3/unsupported/Eigen/SpecialFunctions",
    "content": "#include \"unsupported/Eigen/SpecialFunctions\"\n"
  },
  {
    "path": "third_party/eigen3/use_faster_constant_method.patch",
    "content": "diff --git a/Eigen/src/Core/CwiseNullaryOp.h b/Eigen/src/Core/CwiseNullaryOp.h\nindex b33c052c3..13bad9b09 100644\n--- a/Eigen/src/Core/CwiseNullaryOp.h\n+++ b/Eigen/src/Core/CwiseNullaryOp.h\n@@ -502,7 +502,7 @@ template<typename Derived>\n EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const typename DenseBase<Derived>::ConstantReturnType\n DenseBase<Derived>::Zero(Index rows, Index cols)\n {\n-  return Constant(rows, cols, Scalar(0));\n+  return Constant(rows, cols, ScalarTraits<Scalar>::Zero());\n }\n \n /** \\returns an expression of a zero vector.\n@@ -525,7 +525,7 @@ template<typename Derived>\n EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const typename DenseBase<Derived>::ConstantReturnType\n DenseBase<Derived>::Zero(Index size)\n {\n-  return Constant(size, Scalar(0));\n+  return Constant(size, ScalarTraits<Scalar>::Zero());\n }\n \n /** \\returns an expression of a fixed-size zero matrix or vector.\n@@ -542,7 +542,7 @@ template<typename Derived>\n EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const typename DenseBase<Derived>::ConstantReturnType\n DenseBase<Derived>::Zero()\n {\n-  return Constant(Scalar(0));\n+  return Constant(ScalarTraits<Scalar>::Zero());\n }\n \n /** \\returns true if *this is approximately equal to the zero matrix,\n@@ -574,7 +574,7 @@ EIGEN_DEVICE_FUNC bool DenseBase<Derived>::isZero(const RealScalar& prec) const\n template<typename Derived>\n EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Derived& DenseBase<Derived>::setZero()\n {\n-  return setConstant(Scalar(0));\n+  return setConstant(ScalarTraits<Scalar>::Zero());\n }\n \n /** Resizes to the given \\a size, and sets all coefficients in this expression to zero.\n@@ -591,7 +591,7 @@ EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Derived&\n PlainObjectBase<Derived>::setZero(Index newSize)\n {\n   resize(newSize);\n-  return setConstant(Scalar(0));\n+  return setConstant(ScalarTraits<Scalar>::Zero());\n }\n \n /** Resizes to the given size, and sets all coefficients in this expression to zero.\n@@ -609,7 +609,7 @@ EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Derived&\n PlainObjectBase<Derived>::setZero(Index rows, Index cols)\n {\n   resize(rows, cols);\n-  return setConstant(Scalar(0));\n+  return setConstant(ScalarTraits<Scalar>::Zero());\n }\n \n /** Resizes to the given size, changing only the number of columns, and sets all\n@@ -658,7 +658,7 @@ template<typename Derived>\n EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const typename DenseBase<Derived>::ConstantReturnType\n DenseBase<Derived>::Ones(Index rows, Index cols)\n {\n-  return Constant(rows, cols, Scalar(1));\n+  return Constant(rows, cols, ScalarTraits<Scalar>::One());\n }\n \n /** \\returns an expression of a vector where all coefficients equal one.\n@@ -681,7 +681,7 @@ template<typename Derived>\n EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const typename DenseBase<Derived>::ConstantReturnType\n DenseBase<Derived>::Ones(Index newSize)\n {\n-  return Constant(newSize, Scalar(1));\n+  return Constant(newSize, ScalarTraits<Scalar>::One());\n }\n \n /** \\returns an expression of a fixed-size matrix or vector where all coefficients equal one.\n@@ -698,7 +698,7 @@ template<typename Derived>\n EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const typename DenseBase<Derived>::ConstantReturnType\n DenseBase<Derived>::Ones()\n {\n-  return Constant(Scalar(1));\n+  return Constant(ScalarTraits<Scalar>::One());\n }\n \n /** \\returns true if *this is approximately equal to the matrix where all coefficients\n@@ -713,7 +713,7 @@ template<typename Derived>\n EIGEN_DEVICE_FUNC bool DenseBase<Derived>::isOnes\n (const RealScalar& prec) const\n {\n-  return isApproxToConstant(Scalar(1), prec);\n+  return isApproxToConstant(ScalarTraits<Scalar>::One(), prec);\n }\n \n /** Sets all coefficients in this expression to one.\n@@ -726,7 +726,7 @@ EIGEN_DEVICE_FUNC bool DenseBase<Derived>::isOnes\n template<typename Derived>\n EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Derived& DenseBase<Derived>::setOnes()\n {\n-  return setConstant(Scalar(1));\n+  return setConstant(ScalarTraits<Scalar>::One());\n }\n \n /** Resizes to the given \\a newSize, and sets all coefficients in this expression to one.\n@@ -743,7 +743,7 @@ EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Derived&\n PlainObjectBase<Derived>::setOnes(Index newSize)\n {\n   resize(newSize);\n-  return setConstant(Scalar(1));\n+  return setConstant(ScalarTraits<Scalar>::One());\n }\n \n /** Resizes to the given size, and sets all coefficients in this expression to one.\n@@ -761,7 +761,7 @@ EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Derived&\n PlainObjectBase<Derived>::setOnes(Index rows, Index cols)\n {\n   resize(rows, cols);\n-  return setConstant(Scalar(1));\n+  return setConstant(ScalarTraits<Scalar>::One());\n }\n \n /** Resizes to the given size, changing only the number of rows, and sets all\n@@ -884,7 +884,7 @@ struct setIdentity_impl<Derived, true>\n   {\n     m.setZero();\n     const Index size = numext::mini(m.rows(), m.cols());\n-    for(Index i = 0; i < size; ++i) m.coeffRef(i,i) = typename Derived::Scalar(1);\n+    for(Index i = 0; i < size; ++i) m.coeffRef(i,i) = ScalarTraits<typename Derived::Scalar>::One();\n     return m;\n   }\n };\n@@ -1003,7 +1003,7 @@ EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Derived& MatrixBase<Derived>::setUnit(Inde\n   EIGEN_STATIC_ASSERT_VECTOR_ONLY(Derived);\n   eigen_assert(i<size());\n   derived().setZero();\n-  derived().coeffRef(i) = Scalar(1);\n+  derived().coeffRef(i) = ScalarTraits<Scalar>::One();\n   return derived();\n }\n \ndiff --git a/Eigen/src/Core/DiagonalMatrix.h b/Eigen/src/Core/DiagonalMatrix.h\nindex 3c389b972..bef9ee894 100644\n--- a/Eigen/src/Core/DiagonalMatrix.h\n+++ b/Eigen/src/Core/DiagonalMatrix.h\n@@ -76,7 +76,7 @@ class DiagonalBase : public EigenBase<Derived>\n     EIGEN_DEVICE_FUNC\n     inline Scalar coeff(Index row, Index col) const {\n       eigen_assert(row >= 0 && col >= 0 && row < rows() && col <= cols());\n-      return row == col ? diagonal().coeff(row) : Scalar(0);\n+      return row == col ? diagonal().coeff(row) : ScalarTraits<Scalar>::Zero();\n     }\n \n     /** \\returns the number of rows. */\ndiff --git a/Eigen/src/Core/GeneralProduct.h b/Eigen/src/Core/GeneralProduct.h\nindex 9f693baa2..a503511c9 100644\n--- a/Eigen/src/Core/GeneralProduct.h\n+++ b/Eigen/src/Core/GeneralProduct.h\n@@ -278,7 +278,7 @@ template<> struct gemv_dense_selector<OnTheRight,ColMajor,true>\n         if(!alphaIsCompatible)\n         {\n           MappedDest(actualDestPtr, dest.size()).setZero();\n-          compatibleAlpha = RhsScalar(1);\n+          compatibleAlpha = ScalarTraits<RhsScalar>::One();\n         }\n         else\n           MappedDest(actualDestPtr, dest.size()) = dest;\ndiff --git a/Eigen/src/Core/MatrixBase.h b/Eigen/src/Core/MatrixBase.h\nindex 605a05e84..145a87803 100644\n--- a/Eigen/src/Core/MatrixBase.h\n+++ b/Eigen/src/Core/MatrixBase.h\n@@ -250,7 +250,7 @@ template<typename Derived> class MatrixBase\n     EIGEN_DEVICE_FUNC\n     typename ConstSelfAdjointViewReturnType<UpLo>::Type selfadjointView() const;\n \n-    const SparseView<Derived> sparseView(const Scalar& m_reference = Scalar(0),\n+    const SparseView<Derived> sparseView(const Scalar& m_reference = ScalarTraits<Scalar>::Zero(),\n                                          const typename NumTraits<Scalar>::Real& m_epsilon = NumTraits<Scalar>::dummy_precision()) const;\n     EIGEN_DEVICE_FUNC static const IdentityReturnType Identity();\n     EIGEN_DEVICE_FUNC static const IdentityReturnType Identity(Index rows, Index cols);\ndiff --git a/Eigen/src/Core/NumTraits.h b/Eigen/src/Core/NumTraits.h\nindex 89ba2bc0e..e4de9bc98 100644\n--- a/Eigen/src/Core/NumTraits.h\n+++ b/Eigen/src/Core/NumTraits.h\n@@ -155,6 +155,7 @@ template<typename T, typename SFINAE = void> struct GenericNumTraits\n {\n   enum {\n     IsInteger = std::numeric_limits<T>::is_integer,\n+    IsField = 0,\n     IsSigned = std::numeric_limits<T>::is_signed,\n     IsComplex = 0,\n     RequireInitialization = internal::is_arithmetic<T>::value ? 0 : 1,\ndiff --git a/Eigen/src/Core/PlainObjectBase.h b/Eigen/src/Core/PlainObjectBase.h\nindex fc2e94178..642b26dde 100644\n--- a/Eigen/src/Core/PlainObjectBase.h\n+++ b/Eigen/src/Core/PlainObjectBase.h\n@@ -13,7 +13,7 @@\n \n #if defined(EIGEN_INITIALIZE_MATRICES_BY_ZERO)\n # define EIGEN_INITIALIZE_COEFFS\n-# define EIGEN_INITIALIZE_COEFFS_IF_THAT_OPTION_IS_ENABLED for(Index i=0;i<base().size();++i) coeffRef(i)=Scalar(0);\n+# define EIGEN_INITIALIZE_COEFFS_IF_THAT_OPTION_IS_ENABLED for(Index i=0;i<base().size();++i) coeffRef(i)=ScalarTraits<Scalar>::Zero();\n #elif defined(EIGEN_INITIALIZE_MATRICES_BY_NAN)\n # define EIGEN_INITIALIZE_COEFFS\n # define EIGEN_INITIALIZE_COEFFS_IF_THAT_OPTION_IS_ENABLED for(Index i=0;i<base().size();++i) coeffRef(i)=std::numeric_limits<Scalar>::quiet_NaN();\ndiff --git a/Eigen/src/Core/ProductEvaluators.h b/Eigen/src/Core/ProductEvaluators.h\nindex 9da240671..5f4b2f8c9 100644\n--- a/Eigen/src/Core/ProductEvaluators.h\n+++ b/Eigen/src/Core/ProductEvaluators.h\n@@ -348,15 +348,15 @@ struct generic_product_impl_base\n \n   template<typename Dst>\n   static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void evalTo(Dst& dst, const Lhs& lhs, const Rhs& rhs)\n-  { dst.setZero(); scaleAndAddTo(dst, lhs, rhs, Scalar(1)); }\n+  { dst.setZero(); scaleAndAddTo(dst, lhs, rhs, ScalarTraits<Scalar>::One()); }\n \n   template<typename Dst>\n   static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void addTo(Dst& dst, const Lhs& lhs, const Rhs& rhs)\n-  { scaleAndAddTo(dst,lhs, rhs, Scalar(1)); }\n+  { scaleAndAddTo(dst,lhs, rhs, ScalarTraits<Scalar>::One()); }\n \n   template<typename Dst>\n   static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void subTo(Dst& dst, const Lhs& lhs, const Rhs& rhs)\n-  { scaleAndAddTo(dst, lhs, rhs, Scalar(-1)); }\n+  { scaleAndAddTo(dst, lhs, rhs, ScalarTraits<Scalar>::MinusOne()); }\n \n   template<typename Dst>\n   static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void scaleAndAddTo(Dst& dst, const Lhs& lhs, const Rhs& rhs, const Scalar& alpha)\ndiff --git a/Eigen/src/Core/Redux.h b/Eigen/src/Core/Redux.h\nindex 796e6c4eb..9890aa782 100644\n--- a/Eigen/src/Core/Redux.h\n+++ b/Eigen/src/Core/Redux.h\n@@ -460,7 +460,7 @@ EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE typename internal::traits<Derived>::Scalar\n DenseBase<Derived>::sum() const\n {\n   if(SizeAtCompileTime==0 || (SizeAtCompileTime==Dynamic && size()==0))\n-    return Scalar(0);\n+    return ScalarTraits<Scalar>::Zero();\n   return derived().redux(Eigen::internal::scalar_sum_op<Scalar,Scalar>());\n }\n \ndiff --git a/Eigen/src/Core/TriangularMatrix.h b/Eigen/src/Core/TriangularMatrix.h\nindex c1bd13afc..3920aef89 100644\n--- a/Eigen/src/Core/TriangularMatrix.h\n+++ b/Eigen/src/Core/TriangularMatrix.h\n@@ -408,10 +408,10 @@ template<typename MatrixType_, unsigned int Mode_> class TriangularViewImpl<Matr\n     { return *this = MatrixType::Constant(derived().rows(), derived().cols(), value); }\n     /** \\sa MatrixBase::setZero() */\n     EIGEN_DEVICE_FUNC\n-    TriangularViewType& setZero() { return setConstant(Scalar(0)); }\n+    TriangularViewType& setZero() { return setConstant(ScalarTraits<Scalar>::Zero()); }\n     /** \\sa MatrixBase::setOnes() */\n     EIGEN_DEVICE_FUNC\n-    TriangularViewType& setOnes() { return setConstant(Scalar(1)); }\n+    TriangularViewType& setOnes() { return setConstant(ScalarTraits<Scalar>::One()); }\n \n     /** \\sa MatrixBase::coeff()\n       * \\warning the coordinates must fit into the referenced triangular part\n@@ -786,8 +786,8 @@ public:\n \n   EIGEN_DEVICE_FUNC void assignDiagonalCoeff(Index id)\n   {\n-         if(Mode==UnitDiag && SetOpposite) m_functor.assignCoeff(m_dst.coeffRef(id,id), Scalar(1));\n-    else if(Mode==ZeroDiag && SetOpposite) m_functor.assignCoeff(m_dst.coeffRef(id,id), Scalar(0));\n+         if(Mode==UnitDiag && SetOpposite) m_functor.assignCoeff(m_dst.coeffRef(id,id), ScalarTraits<Scalar>::One());\n+    else if(Mode==ZeroDiag && SetOpposite) m_functor.assignCoeff(m_dst.coeffRef(id,id), ScalarTraits<Scalar>::Zero());\n     else if(Mode==0)                       Base::assignCoeff(id,id);\n   }\n \n@@ -795,7 +795,7 @@ public:\n   {\n     eigen_internal_assert(row!=col);\n     if(SetOpposite)\n-      m_functor.assignCoeff(m_dst.coeffRef(row,col), Scalar(0));\n+      m_functor.assignCoeff(m_dst.coeffRef(row,col), ScalarTraits<Scalar>::Zero());\n   }\n };\n \n@@ -970,7 +970,7 @@ struct Assignment<DstXprType, Product<Lhs,Rhs,DefaultProduct>, internal::assign_\n     if((dst.rows()!=dstRows) || (dst.cols()!=dstCols))\n       dst.resize(dstRows, dstCols);\n \n-    dst._assignProduct(src, Scalar(1), false);\n+    dst._assignProduct(src, ScalarTraits<Scalar>::One(), false);\n   }\n };\n \n@@ -981,7 +981,7 @@ struct Assignment<DstXprType, Product<Lhs,Rhs,DefaultProduct>, internal::add_ass\n   typedef Product<Lhs,Rhs,DefaultProduct> SrcXprType;\n   static void run(DstXprType &dst, const SrcXprType &src, const internal::add_assign_op<Scalar,typename SrcXprType::Scalar> &)\n   {\n-    dst._assignProduct(src, Scalar(1), true);\n+    dst._assignProduct(src, ScalarTraits<Scalar>::One(), true);\n   }\n };\n \n@@ -992,7 +992,7 @@ struct Assignment<DstXprType, Product<Lhs,Rhs,DefaultProduct>, internal::sub_ass\n   typedef Product<Lhs,Rhs,DefaultProduct> SrcXprType;\n   static void run(DstXprType &dst, const SrcXprType &src, const internal::sub_assign_op<Scalar,typename SrcXprType::Scalar> &)\n   {\n-    dst._assignProduct(src, Scalar(-1), true);\n+    dst._assignProduct(src, ScalarTraits<Scalar>::MinusOne(), true);\n   }\n };\n \ndiff --git a/Eigen/src/Core/functors/BinaryFunctors.h b/Eigen/src/Core/functors/BinaryFunctors.h\nindex cd8ae9ee5..b3a7de968 100644\n--- a/Eigen/src/Core/functors/BinaryFunctors.h\n+++ b/Eigen/src/Core/functors/BinaryFunctors.h\n@@ -470,11 +470,11 @@ struct scalar_boolean_and_op {\n   // `false` any value `a` that satisfies `a == Scalar(0)`\n   // `true` is the complement of `false`\n   EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Scalar operator()(const Scalar& a, const Scalar& b) const {\n-    return (a != Scalar(0)) && (b != Scalar(0)) ? Scalar(1) : Scalar(0);\n+    return (a != ScalarTraits<Scalar>::Zero()) && (b != ScalarTraits<Scalar>::Zero()) ? ScalarTraits<Scalar>::One() : ScalarTraits<Scalar>::Zero();\n   }\n   template <typename Packet>\n   EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet packetOp(const Packet& a, const Packet& b) const {\n-    const Packet cst_one = pset1<Packet>(Scalar(1));\n+    const Packet cst_one = pset1<Packet>(ScalarTraits<Scalar>::One());\n     // and(a,b) == !or(!a,!b)\n     Packet not_a = pcmp_eq(a, pzero(a));\n     Packet not_b = pcmp_eq(b, pzero(b));\n@@ -498,11 +498,11 @@ struct scalar_boolean_or_op {\n   // `false` any value `a` that satisfies `a == Scalar(0)`\n   // `true` is the complement of `false`\n   EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Scalar operator()(const Scalar& a, const Scalar& b) const {\n-    return (a != Scalar(0)) || (b != Scalar(0)) ? Scalar(1) : Scalar(0);\n+    return (a != ScalarTraits<Scalar>::Zero()) || (b != ScalarTraits<Scalar>::Zero()) ? ScalarTraits<Scalar>::One() : ScalarTraits<Scalar>::Zero();\n   }\n   template <typename Packet>\n   EIGEN_STRONG_INLINE Packet packetOp(const Packet& a, const Packet& b) const {\n-    const Packet cst_one = pset1<Packet>(Scalar(1));\n+    const Packet cst_one = pset1<Packet>(ScalarTraits<Scalar>::One());\n     // if or(a,b) == 0, then a == 0 and b == 0\n     // or(a,b) == !nor(a,b)\n     Packet a_nor_b = pcmp_eq(por(a, b), pzero(a));\n@@ -525,11 +525,11 @@ struct scalar_boolean_xor_op {\n   // `false` any value `a` that satisfies `a == Scalar(0)`\n   // `true` is the complement of `false`\n   EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Scalar operator()(const Scalar& a, const Scalar& b) const {\n-    return (a != Scalar(0)) != (b != Scalar(0)) ? Scalar(1) : Scalar(0);\n+    return (a != ScalarTraits<Scalar>::Zero()) != (b != ScalarTraits<Scalar>::Zero()) ? ScalarTraits<Scalar>::One() : ScalarTraits<Scalar>::Zero();\n   }\n   template <typename Packet>\n   EIGEN_STRONG_INLINE Packet packetOp(const Packet& a, const Packet& b) const {\n-    const Packet cst_one = pset1<Packet>(Scalar(1));\n+    const Packet cst_one = pset1<Packet>(ScalarTraits<Scalar>::One());\n     // xor(a,b) == xor(!a,!b)\n     Packet not_a = pcmp_eq(a, pzero(a));\n     Packet not_b = pcmp_eq(b, pzero(b));\ndiff --git a/Eigen/src/Core/functors/NullaryFunctors.h b/Eigen/src/Core/functors/NullaryFunctors.h\nindex 4943d8756..fafef3a97 100644\n--- a/Eigen/src/Core/functors/NullaryFunctors.h\n+++ b/Eigen/src/Core/functors/NullaryFunctors.h\n@@ -32,7 +32,7 @@ struct functor_traits<scalar_constant_op<Scalar> >\n \n template<typename Scalar> struct scalar_identity_op {\n   template<typename IndexType>\n-  EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const Scalar operator() (IndexType row, IndexType col) const { return row==col ? Scalar(1) : Scalar(0); }\n+  EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const Scalar operator() (IndexType row, IndexType col) const { return row==col ? ScalarTraits<Scalar>::One() : ScalarTraits<Scalar>::Zero(); }\n };\n template<typename Scalar>\n struct functor_traits<scalar_identity_op<Scalar> >\ndiff --git a/Eigen/src/Core/functors/UnaryFunctors.h b/Eigen/src/Core/functors/UnaryFunctors.h\nindex 8354c0a76..99c0203fc 100644\n--- a/Eigen/src/Core/functors/UnaryFunctors.h\n+++ b/Eigen/src/Core/functors/UnaryFunctors.h\n@@ -182,7 +182,7 @@ struct scalar_cast_op {\n template <typename Scalar>\n struct scalar_cast_op<Scalar, bool> {\n   typedef bool result_type;\n-  EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE bool operator()(const Scalar& a) const { return a != Scalar(0); }\n+  EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE bool operator()(const Scalar& a) const { return a != ScalarTraits<Scalar>::Zero(); }\n };\n \n template<typename Scalar, typename NewType>\n@@ -711,7 +711,7 @@ struct functor_traits<scalar_acosh_op<Scalar> > {\n   */\n template<typename Scalar>\n struct scalar_inverse_op {\n-  EIGEN_DEVICE_FUNC inline Scalar operator() (const Scalar& a) const { return Scalar(1)/a; }\n+  EIGEN_DEVICE_FUNC inline Scalar operator() (const Scalar& a) const { return ScalarTraits<Scalar>::One()/a; }\n   template<typename Packet>\n   EIGEN_DEVICE_FUNC inline const Packet packetOp(const Packet& a) const\n   { return internal::preciprocal(a); }\n@@ -952,11 +952,11 @@ struct scalar_boolean_not_op {\n   // `false` any value `a` that satisfies `a == Scalar(0)`\n   // `true` is the complement of `false`\n   EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Scalar operator()(const Scalar& a) const {\n-    return a == Scalar(0) ? Scalar(1) : Scalar(0);\n+    return a == ScalarTraits<Scalar>::Zero() ? ScalarTraits<Scalar>::One() : ScalarTraits<Scalar>::Zero();\n   }\n   template <typename Packet>\n   EIGEN_STRONG_INLINE Packet packetOp(const Packet& a) const {\n-    const Packet cst_one = pset1<Packet>(Scalar(1));\n+    const Packet cst_one = pset1<Packet>(ScalarTraits<Scalar>::One());\n     Packet not_a = pcmp_eq(a, pzero(a));\n     return pand(not_a, cst_one);\n   }\ndiff --git a/Eigen/src/Core/products/GeneralMatrixMatrix.h b/Eigen/src/Core/products/GeneralMatrixMatrix.h\nindex 9f2a53da1..80db83e90 100644\n--- a/Eigen/src/Core/products/GeneralMatrixMatrix.h\n+++ b/Eigen/src/Core/products/GeneralMatrixMatrix.h\n@@ -439,7 +439,7 @@ struct generic_product_impl<Lhs,Rhs,DenseShape,DenseShape,GemmProduct>\n     else\n     {\n       dst.setZero();\n-      scaleAndAddTo(dst, lhs, rhs, Scalar(1));\n+      scaleAndAddTo(dst, lhs, rhs, ScalarTraits<Scalar>::One());\n     }\n   }\n \n@@ -449,7 +449,7 @@ struct generic_product_impl<Lhs,Rhs,DenseShape,DenseShape,GemmProduct>\n     if((rhs.rows()+dst.rows()+dst.cols())<EIGEN_GEMM_TO_COEFFBASED_THRESHOLD && rhs.rows()>0)\n       lazyproduct::eval_dynamic(dst, lhs, rhs, internal::add_assign_op<typename Dst::Scalar,Scalar>());\n     else\n-      scaleAndAddTo(dst,lhs, rhs, Scalar(1));\n+      scaleAndAddTo(dst,lhs, rhs, ScalarTraits<Scalar>::One());\n   }\n \n   template<typename Dst>\n@@ -458,7 +458,7 @@ struct generic_product_impl<Lhs,Rhs,DenseShape,DenseShape,GemmProduct>\n     if((rhs.rows()+dst.rows()+dst.cols())<EIGEN_GEMM_TO_COEFFBASED_THRESHOLD && rhs.rows()>0)\n       lazyproduct::eval_dynamic(dst, lhs, rhs, internal::sub_assign_op<typename Dst::Scalar,Scalar>());\n     else\n-      scaleAndAddTo(dst, lhs, rhs, Scalar(-1));\n+      scaleAndAddTo(dst, lhs, rhs, ScalarTraits<Scalar>::MinusOne());\n   }\n \n   template<typename Dest>\ndiff --git a/Eigen/src/Core/products/GeneralMatrixVector.h b/Eigen/src/Core/products/GeneralMatrixVector.h\nindex 7307994ce..a881b2909 100644\n--- a/Eigen/src/Core/products/GeneralMatrixVector.h\n+++ b/Eigen/src/Core/products/GeneralMatrixVector.h\n@@ -157,14 +157,14 @@ EIGEN_DEVICE_FUNC EIGEN_DONT_INLINE void general_matrix_vector_product<Index,Lhs\n     Index i=0;\n     for(; i<n8; i+=ResPacketSize*8)\n     {\n-      ResPacket c0 = pset1<ResPacket>(ResScalar(0)),\n-                c1 = pset1<ResPacket>(ResScalar(0)),\n-                c2 = pset1<ResPacket>(ResScalar(0)),\n-                c3 = pset1<ResPacket>(ResScalar(0)),\n-                c4 = pset1<ResPacket>(ResScalar(0)),\n-                c5 = pset1<ResPacket>(ResScalar(0)),\n-                c6 = pset1<ResPacket>(ResScalar(0)),\n-                c7 = pset1<ResPacket>(ResScalar(0));\n+      ResPacket c0 = pset1<ResPacket>(ScalarTraits<ResScalar>::Zero()),\n+                c1 = pset1<ResPacket>(ScalarTraits<ResScalar>::Zero()),\n+                c2 = pset1<ResPacket>(ScalarTraits<ResScalar>::Zero()),\n+                c3 = pset1<ResPacket>(ScalarTraits<ResScalar>::Zero()),\n+                c4 = pset1<ResPacket>(ScalarTraits<ResScalar>::Zero()),\n+                c5 = pset1<ResPacket>(ScalarTraits<ResScalar>::Zero()),\n+                c6 = pset1<ResPacket>(ScalarTraits<ResScalar>::Zero()),\n+                c7 = pset1<ResPacket>(ScalarTraits<ResScalar>::Zero());\n \n       for(Index j=j2; j<jend; j+=1)\n       {\n@@ -189,10 +189,10 @@ EIGEN_DEVICE_FUNC EIGEN_DONT_INLINE void general_matrix_vector_product<Index,Lhs\n     }\n     if(i<n4)\n     {\n-      ResPacket c0 = pset1<ResPacket>(ResScalar(0)),\n-                c1 = pset1<ResPacket>(ResScalar(0)),\n-                c2 = pset1<ResPacket>(ResScalar(0)),\n-                c3 = pset1<ResPacket>(ResScalar(0));\n+      ResPacket c0 = pset1<ResPacket>(ScalarTraits<ResScalar>::Zero()),\n+                c1 = pset1<ResPacket>(ScalarTraits<ResScalar>::Zero()),\n+                c2 = pset1<ResPacket>(ScalarTraits<ResScalar>::Zero()),\n+                c3 = pset1<ResPacket>(ScalarTraits<ResScalar>::Zero());\n \n       for(Index j=j2; j<jend; j+=1)\n       {\n@@ -211,9 +211,9 @@ EIGEN_DEVICE_FUNC EIGEN_DONT_INLINE void general_matrix_vector_product<Index,Lhs\n     }\n     if(i<n3)\n     {\n-      ResPacket c0 = pset1<ResPacket>(ResScalar(0)),\n-                c1 = pset1<ResPacket>(ResScalar(0)),\n-                c2 = pset1<ResPacket>(ResScalar(0));\n+      ResPacket c0 = pset1<ResPacket>(ScalarTraits<ResScalar>::Zero()),\n+                c1 = pset1<ResPacket>(ScalarTraits<ResScalar>::Zero()),\n+                c2 = pset1<ResPacket>(ScalarTraits<ResScalar>::Zero());\n \n       for(Index j=j2; j<jend; j+=1)\n       {\n@@ -230,8 +230,8 @@ EIGEN_DEVICE_FUNC EIGEN_DONT_INLINE void general_matrix_vector_product<Index,Lhs\n     }\n     if(i<n2)\n     {\n-      ResPacket c0 = pset1<ResPacket>(ResScalar(0)),\n-                c1 = pset1<ResPacket>(ResScalar(0));\n+      ResPacket c0 = pset1<ResPacket>(ScalarTraits<ResScalar>::Zero()),\n+                c1 = pset1<ResPacket>(ScalarTraits<ResScalar>::Zero());\n \n       for(Index j=j2; j<jend; j+=1)\n       {\n@@ -245,7 +245,7 @@ EIGEN_DEVICE_FUNC EIGEN_DONT_INLINE void general_matrix_vector_product<Index,Lhs\n     }\n     if(i<n1)\n     {\n-      ResPacket c0 = pset1<ResPacket>(ResScalar(0));\n+      ResPacket c0 = pset1<ResPacket>(ScalarTraits<ResScalar>::Zero());\n       for(Index j=j2; j<jend; j+=1)\n       {\n         RhsPacket b0 = pset1<RhsPacket>(rhs(j,0));\n@@ -256,7 +256,7 @@ EIGEN_DEVICE_FUNC EIGEN_DONT_INLINE void general_matrix_vector_product<Index,Lhs\n     }\n     if(HasHalf && i<n_half)\n     {\n-      ResPacketHalf c0 = pset1<ResPacketHalf>(ResScalar(0));\n+      ResPacketHalf c0 = pset1<ResPacketHalf>(ScalarTraits<ResScalar>::Zero());\n       for(Index j=j2; j<jend; j+=1)\n       {\n         RhsPacketHalf b0 = pset1<RhsPacketHalf>(rhs(j,0));\n@@ -267,7 +267,7 @@ EIGEN_DEVICE_FUNC EIGEN_DONT_INLINE void general_matrix_vector_product<Index,Lhs\n     }\n     if(HasQuarter && i<n_quarter)\n     {\n-      ResPacketQuarter c0 = pset1<ResPacketQuarter>(ResScalar(0));\n+      ResPacketQuarter c0 = pset1<ResPacketQuarter>(ScalarTraits<ResScalar>::Zero());\n       for(Index j=j2; j<jend; j+=1)\n       {\n         RhsPacketQuarter b0 = pset1<RhsPacketQuarter>(rhs(j,0));\n@@ -364,14 +364,14 @@ EIGEN_DEVICE_FUNC EIGEN_DONT_INLINE void general_matrix_vector_product<Index,Lhs\n   Index i=0;\n   for(; i<n8; i+=8)\n   {\n-    ResPacket c0 = pset1<ResPacket>(ResScalar(0)),\n-              c1 = pset1<ResPacket>(ResScalar(0)),\n-              c2 = pset1<ResPacket>(ResScalar(0)),\n-              c3 = pset1<ResPacket>(ResScalar(0)),\n-              c4 = pset1<ResPacket>(ResScalar(0)),\n-              c5 = pset1<ResPacket>(ResScalar(0)),\n-              c6 = pset1<ResPacket>(ResScalar(0)),\n-              c7 = pset1<ResPacket>(ResScalar(0));\n+    ResPacket c0 = pset1<ResPacket>(ScalarTraits<ResScalar>::Zero()),\n+              c1 = pset1<ResPacket>(ScalarTraits<ResScalar>::Zero()),\n+              c2 = pset1<ResPacket>(ScalarTraits<ResScalar>::Zero()),\n+              c3 = pset1<ResPacket>(ScalarTraits<ResScalar>::Zero()),\n+              c4 = pset1<ResPacket>(ScalarTraits<ResScalar>::Zero()),\n+              c5 = pset1<ResPacket>(ScalarTraits<ResScalar>::Zero()),\n+              c6 = pset1<ResPacket>(ScalarTraits<ResScalar>::Zero()),\n+              c7 = pset1<ResPacket>(ScalarTraits<ResScalar>::Zero());\n \n     Index j=0;\n     for(; j+LhsPacketSize<=cols; j+=LhsPacketSize)\n@@ -419,10 +419,10 @@ EIGEN_DEVICE_FUNC EIGEN_DONT_INLINE void general_matrix_vector_product<Index,Lhs\n   }\n   for(; i<n4; i+=4)\n   {\n-    ResPacket c0 = pset1<ResPacket>(ResScalar(0)),\n-              c1 = pset1<ResPacket>(ResScalar(0)),\n-              c2 = pset1<ResPacket>(ResScalar(0)),\n-              c3 = pset1<ResPacket>(ResScalar(0));\n+    ResPacket c0 = pset1<ResPacket>(ScalarTraits<ResScalar>::Zero()),\n+              c1 = pset1<ResPacket>(ScalarTraits<ResScalar>::Zero()),\n+              c2 = pset1<ResPacket>(ScalarTraits<ResScalar>::Zero()),\n+              c3 = pset1<ResPacket>(ScalarTraits<ResScalar>::Zero());\n \n     Index j=0;\n     for(; j+LhsPacketSize<=cols; j+=LhsPacketSize)\n@@ -454,8 +454,8 @@ EIGEN_DEVICE_FUNC EIGEN_DONT_INLINE void general_matrix_vector_product<Index,Lhs\n   }\n   for(; i<n2; i+=2)\n   {\n-    ResPacket c0 = pset1<ResPacket>(ResScalar(0)),\n-              c1 = pset1<ResPacket>(ResScalar(0));\n+    ResPacket c0 = pset1<ResPacket>(ScalarTraits<ResScalar>::Zero()),\n+              c1 = pset1<ResPacket>(ScalarTraits<ResScalar>::Zero());\n \n     Index j=0;\n     for(; j+LhsPacketSize<=cols; j+=LhsPacketSize)\n@@ -479,9 +479,9 @@ EIGEN_DEVICE_FUNC EIGEN_DONT_INLINE void general_matrix_vector_product<Index,Lhs\n   }\n   for(; i<rows; ++i)\n   {\n-    ResPacket c0 = pset1<ResPacket>(ResScalar(0));\n-    ResPacketHalf c0_h = pset1<ResPacketHalf>(ResScalar(0));\n-    ResPacketQuarter c0_q = pset1<ResPacketQuarter>(ResScalar(0));\n+    ResPacket c0 = pset1<ResPacket>(ScalarTraits<ResScalar>::Zero());\n+    ResPacketHalf c0_h = pset1<ResPacketHalf>(ScalarTraits<ResScalar>::Zero());\n+    ResPacketQuarter c0_q = pset1<ResPacketQuarter>(ScalarTraits<ResScalar>::Zero());\n     Index j=0;\n     for(; j+LhsPacketSize<=cols; j+=LhsPacketSize)\n     {\ndiff --git a/Eigen/src/Core/products/TriangularSolverMatrix.h b/Eigen/src/Core/products/TriangularSolverMatrix.h\nindex b148d9c4f..23a8453bd 100644\n--- a/Eigen/src/Core/products/TriangularSolverMatrix.h\n+++ b/Eigen/src/Core/products/TriangularSolverMatrix.h\n@@ -60,7 +60,7 @@ EIGEN_STRONG_INLINE void trsmKernelL<Scalar, Index, Mode, Conjugate, TriStorageO\n       Index s  = TriStorageOrder==RowMajor ? (IsLower ? 0 : i+1)\n         :  IsLower ? i+1 : i-rs;\n \n-      Scalar a = (Mode & UnitDiag) ? Scalar(1) : Scalar(1)/conj(tri(i,i));\n+      Scalar a = (Mode & UnitDiag) ? Scalar::One() : Scalar::One()/conj(tri(i,i));\n       for (Index j=0; j<otherSize; ++j)\n       {\n         if (TriStorageOrder==RowMajor)\n@@ -273,7 +273,7 @@ EIGEN_DONT_INLINE void triangular_solve_matrix<Scalar,Index,OnTheLeft,Mode,Conju\n \n             pack_lhs(blockA, tri.getSubMapper(startTarget,startBlock), actualPanelWidth, lengthTarget);\n \n-            gebp_kernel(other.getSubMapper(startTarget,j2), blockA, blockB+actual_kc*j2, lengthTarget, actualPanelWidth, actual_cols, Scalar(-1),\n+            gebp_kernel(other.getSubMapper(startTarget,j2), blockA, blockB+actual_kc*j2, lengthTarget, actualPanelWidth, actual_cols, Scalar::MinusOne(),\n                         actualPanelWidth, actual_kc, 0, blockBOffset);\n           }\n         }\n@@ -290,7 +290,7 @@ EIGEN_DONT_INLINE void triangular_solve_matrix<Scalar,Index,OnTheLeft,Mode,Conju\n           {\n             pack_lhs(blockA, tri.getSubMapper(i2, IsLower ? k2 : k2-kc), actual_kc, actual_mc);\n \n-            gebp_kernel(other.getSubMapper(i2, 0), blockA, blockB, actual_mc, actual_kc, cols, Scalar(-1), -1, -1, 0, 0);\n+            gebp_kernel(other.getSubMapper(i2, 0), blockA, blockB, actual_mc, actual_kc, cols, Scalar::MinusOne(), -1, -1, 0, 0);\n           }\n         }\n       }\n@@ -416,7 +416,7 @@ EIGEN_DONT_INLINE void triangular_solve_matrix<Scalar,Index,OnTheRight,Mode,Conj\n               gebp_kernel(lhs.getSubMapper(i2,absolute_j2),\n                           blockA, blockB+j2*actual_kc,\n                           actual_mc, panelLength, actualPanelWidth,\n-                          Scalar(-1),\n+                          Scalar::MinusOne(),\n                           actual_kc, actual_kc, // strides\n                           panelOffset, panelOffset); // offsets\n             }\n@@ -437,7 +437,7 @@ EIGEN_DONT_INLINE void triangular_solve_matrix<Scalar,Index,OnTheRight,Mode,Conj\n \n         if (rs>0)\n           gebp_kernel(lhs.getSubMapper(i2, startPanel), blockA, geb,\n-                      actual_mc, actual_kc, rs, Scalar(-1),\n+                      actual_mc, actual_kc, rs, Scalar::MinusOne(),\n                       -1, -1, 0, 0);\n       }\n     }\ndiff --git a/Eigen/src/Core/util/ForwardDeclarations.h b/Eigen/src/Core/util/ForwardDeclarations.h\nindex feacdcfe4..2421fa779 100644\n--- a/Eigen/src/Core/util/ForwardDeclarations.h\n+++ b/Eigen/src/Core/util/ForwardDeclarations.h\n@@ -46,6 +46,18 @@ template< typename T> struct evaluator;\n \n template<typename T, typename SFINAE> struct NumTraits;\n \n+template <typename Scalar, typename SFINAE = void> struct ScalarTraits {\n+  EIGEN_CONSTEXPR static Scalar Zero() { return Scalar(0); }\n+  EIGEN_CONSTEXPR static Scalar One() { return Scalar(1); }\n+  EIGEN_CONSTEXPR static Scalar MinsOne() { return Scalar(-1); }\n+};\n+\n+template <typename Scalar> struct ScalarTraits<Scalar, std::enable_if_t<NumTraits<Scalar>::IsField>> {\n+  EIGEN_CONSTEXPR static Scalar Zero() { return Scalar::Zero(); }\n+  EIGEN_CONSTEXPR static Scalar One() { return Scalar::One(); }\n+  EIGEN_CONSTEXPR static Scalar MinusOne() { return Scalar::MinusOne(); }\n+};\n+\n template<typename Derived> struct EigenBase;\n template<typename Derived> class DenseBase;\n template<typename Derived> class PlainObjectBase;\ndiff --git a/Eigen/src/SparseCore/SparseCwiseBinaryOp.h b/Eigen/src/SparseCore/SparseCwiseBinaryOp.h\nindex 3aea5ecb5..0bc180a52 100644\n--- a/Eigen/src/SparseCore/SparseCwiseBinaryOp.h\n+++ b/Eigen/src/SparseCore/SparseCwiseBinaryOp.h\n@@ -76,9 +76,9 @@ public:\n   class InnerIterator\n   {\n   public:\n-    \n+\n     EIGEN_STRONG_INLINE InnerIterator(const binary_evaluator& aEval, Index outer)\n-      : m_lhsIter(aEval.m_lhsImpl,outer), m_rhsIter(aEval.m_rhsImpl,outer), m_functor(aEval.m_functor), m_value(Scalar(0))\n+      : m_lhsIter(aEval.m_lhsImpl,outer), m_rhsIter(aEval.m_rhsImpl,outer), m_functor(aEval.m_functor), m_value(ScalarTraits<Scalar>::Zero())\n     {\n       this->operator++();\n     }\n@@ -95,13 +95,13 @@ public:\n       else if (m_lhsIter && (!m_rhsIter || (m_lhsIter.index() < m_rhsIter.index())))\n       {\n         m_id = m_lhsIter.index();\n-        m_value = m_functor(m_lhsIter.value(), Scalar(0));\n+        m_value = m_functor(m_lhsIter.value(), ScalarTraits<Scalar>::Zero());\n         ++m_lhsIter;\n       }\n       else if (m_rhsIter && (!m_lhsIter || (m_lhsIter.index() > m_rhsIter.index())))\n       {\n         m_id = m_rhsIter.index();\n-        m_value = m_functor(Scalar(0), m_rhsIter.value());\n+        m_value = m_functor(ScalarTraits<Scalar>::Zero(), m_rhsIter.value());\n         ++m_rhsIter;\n       }\n       else\n@@ -189,7 +189,7 @@ public:\n           ++m_rhsIter;\n         }\n         else\n-          m_value = m_functor(lhsVal, Scalar(0));\n+          m_value = m_functor(lhsVal, ScalarTraits<Scalar>::Zero());\n       }\n \n       return *this;\n@@ -276,7 +276,7 @@ public:\n           ++m_lhsIter;\n         }\n         else\n-          m_value = m_functor(Scalar(0),rhsVal);\n+          m_value = m_functor(ScalarTraits<Scalar>::Zero(),rhsVal);\n       }\n \n       return *this;\n@@ -670,7 +670,7 @@ struct sparse_disjunction_evaluator<XprType, IteratorBased, IteratorBased> : eva\n         : m_lhsIter(aEval.m_lhsImpl, outer),\n           m_rhsIter(aEval.m_rhsImpl, outer),\n           m_functor(aEval.m_functor),\n-          m_value(Scalar(0)) {\n+          m_value(ScalarTraits<Scalar>::Zero()) {\n       this->operator++();\n     }\n \n"
  },
  {
    "path": "third_party/eigen3/workspace.bzl",
    "content": "\"\"\"Provides the repository macro to import Eigen.\"\"\"\n\nload(\"//third_party:repo.bzl\", \"tachyon_http_archive\", \"tf_mirror_urls\")\n\ndef repo():\n    \"\"\"Imports Eigen.\"\"\"\n\n    # Attention: tools parse and update these lines.\n    # LINT.IfChange\n    EIGEN_COMMIT = \"0b51f763cbbd0ed08168f88972724329f0375498\"\n    EIGEN_SHA256 = \"70a3b0e357fc037740002f5097a15dba1ea0dde28d37f5d9c86f76a06626f4fc\"\n    # LINT.ThenChange(//tensorflow/lite/tools/cmake/modules/eigen.cmake)\n\n    tachyon_http_archive(\n        name = \"eigen_archive\",\n        build_file = \"//third_party/eigen3:eigen_archive.BUILD\",\n        sha256 = EIGEN_SHA256,\n        strip_prefix = \"eigen-{commit}\".format(commit = EIGEN_COMMIT),\n        urls = tf_mirror_urls(\"https://gitlab.com/libeigen/eigen/-/archive/{commit}/eigen-{commit}.tar.gz\".format(commit = EIGEN_COMMIT)),\n        patch_file = [\n            \"@kroma_network_tachyon//third_party/eigen3:add_sfinae_to_scalar_random_op.patch\",\n            \"@kroma_network_tachyon//third_party/eigen3:add_sfinae_to_num_traits.patch\",\n            \"@kroma_network_tachyon//third_party/eigen3:use_faster_constant_method.patch\",\n            \"@kroma_network_tachyon//third_party/eigen3:allow_field_inverse.patch\",\n        ],\n    )\n"
  },
  {
    "path": "third_party/env/BUILD.bazel",
    "content": ""
  },
  {
    "path": "third_party/env/env.bzl.tpl",
    "content": "PROJECT_ROOT=%{PROJECT_ROOT}\n"
  },
  {
    "path": "third_party/env/env_configure.bzl",
    "content": "def _env_autoconf_impl(repository_ctx):\n    repository_ctx.symlink(Label(\"//third_party/env:BUILD.bazel\"), \"BUILD.bazel\")\n    project_root = repository_ctx.path(Label(\"//:BUILD.bazel\")).dirname\n\n    repository_ctx.template(\n        \"env.bzl\",\n        Label(\"//third_party/env:env.bzl.tpl\"),\n        {\n            \"%{PROJECT_ROOT}\": \"\\\"%s\\\"\" % project_root,\n        },\n    )\n\nenv_configure = repository_rule(\n    implementation = _env_autoconf_impl,\n)\n"
  },
  {
    "path": "third_party/ffiasm/BUILD.bazel",
    "content": ""
  },
  {
    "path": "third_party/ffiasm/build_defs.bzl",
    "content": "load(\"@iden3_ffiasm//:build_defs.bzl\", _generate_prime_field = \"generate_prime_field\")\n\ndef generate_prime_field(name, modulus):\n    _generate_prime_field(\n        name = name.capitalize(),\n        arm64_s_out = \"{}_raw_arm64.s\".format(name),\n        asm_out = \"{}.asm\".format(name),\n        c_out = \"{}.cpp\".format(name),\n        element_h_out = \"{}_element.hpp\".format(name),\n        generic_c_out = \"{}_generic.cpp\".format(name),\n        h_out = \"{}.hpp\".format(name),\n        modulus = modulus,\n        raw_generic_c_out = \"{}_raw_generic.cpp\".format(name),\n    )\n\n    cmd_linux_x86 = \"\\n\".join([\n        \"for out in $(OUTS); do\",\n        \"$(location @nasm//:nasm) -f elf64 -o $$out $$(dirname $(location \" + name + \".asm))/$$(basename $${out%.o}.asm)\",\n        \"done\",\n    ])\n\n    cmd_macos_x86 = \"\\n\".join([\n        \"for out in $(OUTS); do\",\n        \"$(location @nasm//:nasm) -f macho64 --prefix _ -o $$out $$(dirname $(location \" + name + \".asm))/$$(basename $${out%.o}.asm)\",\n        \"done\",\n    ])\n\n    native.genrule(\n        name = \"{}_asm\".format(name),\n        srcs = [\"{}.asm\".format(name)],\n        outs = [\"{}.o\".format(name)],\n        cmd = select({\n            \"@kroma_network_tachyon//:linux_x86_64\": cmd_linux_x86,\n            \"@kroma_network_tachyon//:macos_x86_64\": cmd_macos_x86,\n            \"//conditions:default\": \"touch $@\",\n        }),\n        tools = [\"@nasm\"],\n    )\n\n    native.cc_library(\n        name = \"{}_object\".format(name),\n        srcs = select({\n            \"@kroma_network_tachyon//:linux_x86_64\": [\"{}.o\".format(name)],\n            \"@kroma_network_tachyon//:macos_x86_64\": [\"{}.o\".format(name)],\n            \"//conditions:default\": [],\n        }),\n        linkstatic = True,\n    )\n\n    native.cc_library(\n        name = name,\n        srcs = [\"{}.cpp\".format(name)] + select({\n            \"@kroma_network_tachyon//:linux_x86_64\": [],\n            \"@kroma_network_tachyon//:macos_x86_64\": [],\n            \"//conditions:default\": [\n                \"{}_generic.cpp\".format(name),\n                \"{}_raw_generic.cpp\".format(name),\n            ],\n        }),\n        hdrs = [\n            \"{}.hpp\".format(name),\n            \"{}_element.hpp\".format(name),\n        ],\n        defines = select({\n            \"@kroma_network_tachyon//:linux_x86_64\": [\"USE_ASM\", \"ARCH_X86_64\"],\n            \"@kroma_network_tachyon//:macos_x86_64\": [\"USE_ASM\", \"ARCH_X86_64\"],\n            \"//conditions:default\": [],\n        }),\n        includes = [\".\"],\n        deps = [\"@local_config_gmp//:gmp\"] + select({\n            \"@kroma_network_tachyon//:linux_x86_64\": [\":{}_object\".format(name)],\n            \"@kroma_network_tachyon//:macos_x86_64\": [\":{}_object\".format(name)],\n            \"//conditions:default\": [],\n        }),\n    )\n"
  },
  {
    "path": "third_party/ffiasm/ffiasm.BUILD",
    "content": "load(\"@iden3_ffiasm//c:build_defs.bzl\", \"generate_prime_field\")\nload(\"@kroma_network_tachyon//bazel:tachyon.bzl\", \"if_has_openmp\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ngenerate_prime_field(\n    name = \"fr\",\n    modulus = \"21888242871839275222246405745257275088548364400416034343698204186575808495617\",\n)\n\ngenerate_prime_field(\n    name = \"fq\",\n    modulus = \"21888242871839275222246405745257275088696311157297823662689037894645226208583\",\n)\n\ncc_library(\n    name = \"c\",\n    srcs = [\n        \"alt_bn128.cpp\",\n        \"misc.cpp\",\n        \"naf.cpp\",\n        \"splitparstr.cpp\",\n    ],\n    hdrs = [\n        \"alt_bn128.hpp\",\n        \"curve.cpp\",\n        \"curve.hpp\",\n        \"exp.hpp\",\n        \"exp2.hpp\",\n        \"f12field.cpp\",\n        \"f12field.hpp\",\n        \"f2field.cpp\",\n        \"f2field.hpp\",\n        \"f6field.cpp\",\n        \"f6field.hpp\",\n        \"fft.cpp\",\n        \"fft.hpp\",\n        \"misc.hpp\",\n        \"multiexp.cpp\",\n        \"multiexp.hpp\",\n        \"naf.hpp\",\n        \"splitparstr.hpp\",\n    ],\n    copts = if_has_openmp([\"-fopenmp\"]),\n    defines = if_has_openmp([\"USE_OPENMP\"]),\n    linkopts = if_has_openmp([\"-fopenmp\"]),\n    deps = [\n        \":fq\",\n        \":fr\",\n    ],\n)\n"
  },
  {
    "path": "third_party/ffiasm/workspace.bzl",
    "content": "\"\"\"loads the ffiasm library, used by Tachyon.\"\"\"\n\nload(\"//third_party:repo.bzl\", \"tachyon_http_archive\", \"tf_mirror_urls\")\n\ndef repo():\n    tachyon_http_archive(\n        name = \"iden3_ffiasm\",\n        urls = tf_mirror_urls(\"https://github.com/kroma-network/ffiasm/archive/7db5a442b12dba825ec35361441aa116dc3bff8b.tar.gz\"),\n        sha256 = \"f46b13887bbf5c9f07b3b8eda577a95264408b496a16d96f70c7f3a899d4fa0c\",\n        strip_prefix = \"ffiasm-7db5a442b12dba825ec35361441aa116dc3bff8b\",\n        link_files = {\n            \"//third_party/ffiasm:ffiasm.BUILD\": \"c/BUILD.bazel\",\n            \"//third_party/ffiasm:build_defs.bzl\": \"c/build_defs.bzl\",\n        },\n    )\n"
  },
  {
    "path": "third_party/glog/BUILD.bazel",
    "content": ""
  },
  {
    "path": "third_party/glog/enable_constexpr_check_op.patch",
    "content": "diff --git a/src/glog/logging.h.in b/src/glog/logging.h.in\nindex 421f1e0..04cdf3b 100644\n--- a/src/glog/logging.h.in\n+++ b/src/glog/logging.h.in\n@@ -673,10 +673,10 @@ class LogSink;  // defined below\n // A container for a string pointer which can be evaluated to a bool -\n // true iff the pointer is NULL.\n struct CheckOpString {\n-  CheckOpString(std::string* str) : str_(str) { }\n+  constexpr CheckOpString(std::string* str) : str_(str) { }\n   // No destructor: if str_ is non-NULL, we're about to LOG(FATAL),\n   // so there's no point in cleaning up str_.\n-  operator bool() const {\n+  constexpr operator bool() const {\n     return GOOGLE_PREDICT_BRANCH_NOT_TAKEN(str_ != NULL);\n   }\n   std::string* str_;\n@@ -686,18 +686,18 @@ struct CheckOpString {\n // integrals declared in classes and not defined to be used as arguments to\n // CHECK* macros. It's not encouraged though.\n template <class T>\n-inline const T&       GetReferenceableValue(const T&           t) { return t; }\n-inline char           GetReferenceableValue(char               t) { return t; }\n-inline unsigned char  GetReferenceableValue(unsigned char      t) { return t; }\n-inline signed char    GetReferenceableValue(signed char        t) { return t; }\n-inline short          GetReferenceableValue(short              t) { return t; }\n-inline unsigned short GetReferenceableValue(unsigned short     t) { return t; }\n-inline int            GetReferenceableValue(int                t) { return t; }\n-inline unsigned int   GetReferenceableValue(unsigned int       t) { return t; }\n-inline long           GetReferenceableValue(long               t) { return t; }\n-inline unsigned long  GetReferenceableValue(unsigned long      t) { return t; }\n-inline long long      GetReferenceableValue(long long          t) { return t; }\n-inline unsigned long long GetReferenceableValue(unsigned long long t) {\n+constexpr inline const T&       GetReferenceableValue(const T&           t) { return t; }\n+constexpr inline char           GetReferenceableValue(char               t) { return t; }\n+constexpr inline unsigned char  GetReferenceableValue(unsigned char      t) { return t; }\n+constexpr inline signed char    GetReferenceableValue(signed char        t) { return t; }\n+constexpr inline short          GetReferenceableValue(short              t) { return t; }\n+constexpr inline unsigned short GetReferenceableValue(unsigned short     t) { return t; }\n+constexpr inline int            GetReferenceableValue(int                t) { return t; }\n+constexpr inline unsigned int   GetReferenceableValue(unsigned int       t) { return t; }\n+constexpr inline long           GetReferenceableValue(long               t) { return t; }\n+constexpr inline unsigned long  GetReferenceableValue(unsigned long      t) { return t; }\n+constexpr inline long long      GetReferenceableValue(long long          t) { return t; }\n+constexpr inline unsigned long long GetReferenceableValue(unsigned long long t) {\n   return t;\n }\n \n@@ -793,12 +793,12 @@ std::string* MakeCheckOpString(const T1& v1, const T2& v2, const char* exprtext)\n // unnamed enum type - see comment below.\n #define DEFINE_CHECK_OP_IMPL(name, op) \\\n   template <typename T1, typename T2> \\\n-  inline std::string* name##Impl(const T1& v1, const T2& v2,    \\\n+  constexpr inline std::string* name##Impl(const T1& v1, const T2& v2,    \\\n                             const char* exprtext) { \\\n     if (GOOGLE_PREDICT_TRUE(v1 op v2)) return NULL; \\\n     else return MakeCheckOpString(v1, v2, exprtext); \\\n   } \\\n-  inline std::string* name##Impl(int v1, int v2, const char* exprtext) { \\\n+  constexpr inline std::string* name##Impl(int v1, int v2, const char* exprtext) { \\\n     return name##Impl<int, int>(v1, v2, exprtext); \\\n   }\n \n"
  },
  {
    "path": "third_party/glog/enable_elaborate_ostream_operator.patch",
    "content": "diff --git a/src/glog/logging.h.in b/src/glog/logging.h.in\nindex 04cdf3b..d378aac 100644\n--- a/src/glog/logging.h.in\n+++ b/src/glog/logging.h.in\n@@ -44,6 +44,7 @@\n #include <ostream>\n #include <sstream>\n #include <string>\n+#include <type_traits>\n #if @ac_cv_have_unistd_h@\n # include <unistd.h>\n #endif\n@@ -716,13 +717,75 @@ inline std::ostream& operator<<(\n \n @ac_google_start_namespace@\n \n-// This formats a value for a failing CHECK_XX statement.  Ordinarily,\n-// it uses the definition for operator<<, with a few special cases below.\n+// Uses expression SFINAE to detect whether using operator<< would work.\n+template <typename T, typename = void>\n+struct SupportsOstreamOperator : std::false_type {};\n template <typename T>\n-inline void MakeCheckOpValueString(std::ostream* os, const T& v) {\n+struct SupportsOstreamOperator<T, decltype(void(std::declval<std::ostream&>()\n+                                                << std::declval<T>()))>\n+    : std::true_type {};\n+\n+template <typename T, typename = void>\n+struct SupportsToString : std::false_type {};\n+template <typename T>\n+struct SupportsToString<T, decltype(void(std::declval<T>().ToString()))>\n+    : std::true_type {};\n+\n+// I/O manipulators are function pointers, but should be sent directly to the\n+// `ostream` instead of being cast to `const void*` like other function\n+// pointers.\n+template <typename T, typename = void>\n+constexpr bool IsIomanip = false;\n+template <typename T>\n+constexpr bool\n+    IsIomanip<T&(T&), std::enable_if_t<std::is_base_of_v<std::ios_base, T>>> =\n+        true;\n+\n+// Function pointers implicitly convert to `bool`, so use this to avoid printing\n+// function pointers as 1 or 0.\n+template <typename T, typename = void>\n+constexpr bool WillBeIncorrectlyStreamedAsBool = false;\n+template <typename T>\n+constexpr bool WillBeIncorrectlyStreamedAsBool<\n+    T, std::enable_if_t<std::is_function_v<std::remove_pointer_t<T>> &&\n+                        !IsIomanip<std::remove_pointer_t<T>>>> = true;\n+\n+template <typename T>\n+inline std::enable_if_t<\n+    SupportsOstreamOperator<const T&>::value &&\n+    !WillBeIncorrectlyStreamedAsBool<T>>\n+MakeCheckOpValueString(std::ostream* os, const T& v) {\n   (*os) << v;\n }\n \n+// Functions and function pointers.\n+template <typename T>\n+inline std::enable_if_t<\n+    SupportsOstreamOperator<const T&>::value &&\n+   WillBeIncorrectlyStreamedAsBool<T>>\n+MakeCheckOpValueString(std::ostream* os, const T& v) {\n+  const void* vp = reinterpret_cast<const void*>(v);\n+  (*os) << std::addressof(vp);\n+}\n+\n+// Non-streamables that have a `ToString` member.\n+template <typename T>\n+inline std::enable_if_t<!SupportsOstreamOperator<const T&>::value &&\n+                        SupportsToString<const T&>::value>\n+MakeCheckOpValueString(std::ostream* os, const T& v) {\n+  MakeCheckOpValueString(os, v.ToString());\n+}\n+\n+// Non-streamable enums (i.e. scoped enums where no `operator<<` overload was\n+// declared).\n+template <typename T>\n+inline std::enable_if_t<!SupportsOstreamOperator<const T&>::value &&\n+                        std::is_enum_v<T>>\n+MakeCheckOpValueString(std::ostream* os, T v) {\n+  MakeCheckOpValueString(\n+      os, static_cast<std::underlying_type_t<T>>(v));\n+}\n+\n // Overrides for char types provide readable values for unprintable\n // characters.\n template <> GOOGLE_GLOG_DLL_DECL\n"
  },
  {
    "path": "third_party/gmp/BUILD.bazel",
    "content": ""
  },
  {
    "path": "third_party/gmp/gmp.BUILD",
    "content": ""
  },
  {
    "path": "third_party/gmp/gmp.BUILD.tpl",
    "content": "load(\"@rules_cc//cc:defs.bzl\", \"cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nHEADERS = [\n    \"gmp.h\",\n    \"gmpxx.h\",\n]\n\ngenrule(\n    name = \"link_headers\",\n    outs = HEADERS,\n    cmd = select({\n        \"@kroma_network_tachyon//:linux_x86_64\": \"\"\"\n      mkdir -p $(@D)/\n      ln -sf /usr/include/gmpxx.h $(@D)/gmpxx.h\n      ln -sf {usr_include}/gmp.h $(@D)/gmp.h\n    \"\"\",\n        \"@kroma_network_tachyon//:macos_x86_64\": \"\"\"\n      mkdir -p $(@D)/\n      for file in $(OUTS); do\n        file=$${file##*/}\n        ln -sf /usr/local/include/$$file $(@D)/$$file\n      done\n    \"\"\",\n        \"@kroma_network_tachyon//:macos_aarch64\": \"\"\"\n      mkdir -p $(@D)/\n      for file in $(OUTS); do\n        file=$${file##*/}\n        ln -sf /opt/homebrew/include/$$file $(@D)/$$file\n      done\n    \"\"\",\n        \"//conditions:default\": \"\",\n    }),\n)\n\ncc_library(\n    name = \"gmp\",\n    hdrs = HEADERS,\n    include_prefix = \"third_party/gmp/include\",\n    includes = [\".\"],\n    linkopts = [\n        \"-lgmpxx\",\n        \"-lgmp\",\n    ] + select({\n        \"@kroma_network_tachyon//:macos_x86_64\": [\"-L/usr/local/lib\"],\n        \"@kroma_network_tachyon//:macos_aarch64\": [\"-L/opt/homebrew/lib\"],\n        \"//conditions:default\": [],\n    }),\n)\n"
  },
  {
    "path": "third_party/gmp/gmp_configure.bzl",
    "content": "load(\"//bazel:tachyon_repo.bzl\", \"get_usr_include_path_with_machine\")\n\ndef _gmp_configure_impl(repository_ctx):\n    repository_ctx.template(\n        \"third_party/gmp/gmp.BUILD\",\n        Label(\"//third_party/gmp:gmp.BUILD.tpl\"),\n        substitutions = {\n            \"{usr_include}\": get_usr_include_path_with_machine(repository_ctx),\n        },\n    )\n    repository_ctx.symlink(\"third_party/gmp/gmp.BUILD\", \"BUILD.bazel\")\n\ngmp_configure = repository_rule(\n    implementation = _gmp_configure_impl,\n)\n"
  },
  {
    "path": "third_party/goldilocks/BUILD.bazel",
    "content": ""
  },
  {
    "path": "third_party/goldilocks/goldilocks.BUILD",
    "content": "load(\"@kroma_network_tachyon//bazel:tachyon.bzl\", \"if_has_avx512\")\nload(\"@kroma_network_tachyon//bazel:tachyon_cc.bzl\", \"tachyon_openmp_copts\", \"tachyon_openmp_linkopts\")\nload(\"@rules_cc//cc:defs.bzl\", \"cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncc_library(\n    name = \"base_field\",\n    srcs = [\n        \"src/goldilocks_base_field.cpp\",\n    ],\n    hdrs = [\n        \"src/goldilocks_base_field.hpp\",\n        \"src/goldilocks_base_field_avx.hpp\",\n        \"src/goldilocks_base_field_avx512.hpp\",\n        \"src/goldilocks_base_field_batch.hpp\",\n        \"src/goldilocks_base_field_scalar.hpp\",\n        \"src/goldilocks_base_field_tools.hpp\",\n    ],\n    copts = if_has_avx512(\n        [\"-mavx512f\"],\n        [\"-mavx2\"],\n    ) + tachyon_openmp_copts(),\n    defines = if_has_avx512([\"__AVX512__\"]),\n    include_prefix = \"third_party/goldilocks/include\",\n    includes = [\"src\"],\n    linkopts = tachyon_openmp_linkopts(),\n    strip_include_prefix = \"src\",\n    deps = [\"@local_config_gmp//:gmp\"],\n)\n"
  },
  {
    "path": "third_party/goldilocks/workspace.bzl",
    "content": "\"\"\"loads the goldilocks library, used by Tachyon.\"\"\"\n\nload(\"//third_party:repo.bzl\", \"tachyon_http_archive\", \"tf_mirror_urls\")\n\ndef repo():\n    tachyon_http_archive(\n        name = \"goldilocks\",\n        urls = tf_mirror_urls(\"https://github.com/0xPolygonHermez/goldilocks/archive/e33eb7cb60ba483807040ee5e9ad6d1ea8dbf315.tar.gz\"),\n        sha256 = \"675f31ee46f826047a4d9d7a0fa5d4b5f14caec9d43cb175be744e4e8da84fd4\",\n        strip_prefix = \"goldilocks-e33eb7cb60ba483807040ee5e9ad6d1ea8dbf315\",\n        build_file = \"//third_party/goldilocks:goldilocks.BUILD\",\n    )\n"
  },
  {
    "path": "third_party/gpus/BUILD.bazel",
    "content": ""
  },
  {
    "path": "third_party/gpus/check_cuda_libs.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Verifies that a list of libraries is installed on the system.\n\nTakes a list of arguments with every two subsequent arguments being a logical\ntuple of (path, check_soname). The path to the library and either True or False\nto indicate whether to check the soname field on the shared library.\n\nExample Usage:\n./check_cuda_libs.py /path/to/lib1.so True /path/to/lib2.so False\n\"\"\"\nimport os\nimport os.path\nimport platform\nimport subprocess\nimport sys\n\n# pylint: disable=g-import-not-at-top,g-importing-member\ntry:\n  from shutil import which\nexcept ImportError:\n  from distutils.spawn import find_executable as which\n# pylint: enable=g-import-not-at-top,g-importing-member\n\n\nclass ConfigError(Exception):\n  pass\n\n\ndef _is_windows():\n  return platform.system() == \"Windows\"\n\n\ndef check_cuda_lib(path, check_soname=True):\n  \"\"\"Tests if a library exists on disk and whether its soname matches the filename.\n\n  Args:\n    path: the path to the library.\n    check_soname: whether to check the soname as well.\n\n  Raises:\n    ConfigError: If the library does not exist or if its soname does not match\n    the filename.\n  \"\"\"\n  if not os.path.isfile(path):\n    raise ConfigError(\"No library found under: \" + path)\n  objdump = which(\"objdump\")\n  if check_soname and objdump is not None and not _is_windows():\n    # Decode is necessary as in py3 the return type changed from str to bytes\n    output = subprocess.check_output([objdump, \"-p\", path]).decode(\"utf-8\")\n    output = [line for line in output.splitlines() if \"SONAME\" in line]\n    sonames = [line.strip().split(\" \")[-1] for line in output]\n    if not any(soname == os.path.basename(path) for soname in sonames):\n      raise ConfigError(\"None of the libraries match their SONAME: \" + path)\n\n\ndef main():\n  try:\n    args = [argv for argv in sys.argv[1:]]\n    if len(args) % 2 == 1:\n      raise ConfigError(\"Expected even number of arguments\")\n    checked_paths = []\n    for i in range(0, len(args), 2):\n      path = args[i]\n      check_cuda_lib(path, check_soname=args[i + 1] == \"True\")\n      checked_paths.append(path)\n    # pylint: disable=superfluous-parens\n    print(os.linesep.join(checked_paths))\n    # pylint: enable=superfluous-parens\n  except ConfigError as e:\n    sys.stderr.write(str(e))\n    sys.exit(1)\n\n\nif __name__ == \"__main__\":\n  main()\n"
  },
  {
    "path": "third_party/gpus/compress_find_cuda_config.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Compresses the contents of 'find_cuda_config.py'.\n\nThe compressed file is what is actually being used. It works around remote\nconfig not being able to upload files yet.\n\"\"\"\nimport base64\nimport zlib\n\n\ndef main():\n  with open('find_cuda_config.py', 'rb') as f:\n    data = f.read()\n\n  compressed = zlib.compress(data)\n  b64encoded = base64.b64encode(compressed)\n\n  with open('find_cuda_config.py.gz.base64', 'wb') as f:\n    f.write(b64encoded)\n\n\nif __name__ == '__main__':\n  main()\n"
  },
  {
    "path": "third_party/gpus/compress_find_rocm_config.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Compresses the contents of 'find_rocm_config.py'.\n\nThe compressed file is what is actually being used. It works around remote\nconfig not being able to upload files yet.\n\"\"\"\nimport base64\nimport zlib\n\n\ndef main():\n  with open('find_rocm_config.py', 'rb') as f:\n    data = f.read()\n\n  compressed = zlib.compress(data)\n  b64encoded = base64.b64encode(compressed)\n\n  with open('find_rocm_config.py.gz.base64', 'wb') as f:\n    f.write(b64encoded)\n\n\nif __name__ == '__main__':\n  main()\n"
  },
  {
    "path": "third_party/gpus/crosstool/BUILD.bazel",
    "content": ""
  },
  {
    "path": "third_party/gpus/crosstool/BUILD.rocm.tpl",
    "content": "# This file is expanded from a template by cuda_configure.bzl\n# Update cuda_configure.bzl#verify_build_defines when adding new variables.\n\nload(\":cc_toolchain_config.bzl\", \"cc_toolchain_config\")\n\nlicenses([\"restricted\"])\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntoolchain(\n    name = \"toolchain-linux-x86_64\",\n    exec_compatible_with = [\n        \"@platforms//os:linux\",\n        \"@platforms//cpu:x86_64\",\n    ],\n    target_compatible_with = [\n        \"@platforms//os:linux\",\n        \"@platforms//cpu:x86_64\",\n    ],\n    toolchain = \":cc-compiler-local\",\n    toolchain_type = \"@bazel_tools//tools/cpp:toolchain_type\",\n)\n\ncc_toolchain_suite(\n    name = \"toolchain\",\n    toolchains = {\n        \"local|compiler\": \":cc-compiler-local\",\n        \"arm\": \":cc-compiler-local\",\n        \"aarch64\": \":cc-compiler-local\",\n        \"k8\": \":cc-compiler-local\",\n        \"piii\": \":cc-compiler-local\",\n        \"ppc\": \":cc-compiler-local\",\n    },\n)\n\ncc_toolchain(\n    name = \"cc-compiler-local\",\n    all_files = \":crosstool_wrapper_driver_is_not_gcc\",\n    compiler_files = \":crosstool_wrapper_driver_is_not_gcc\",\n    ar_files = \":crosstool_wrapper_driver_is_not_gcc\",\n    as_files = \":crosstool_wrapper_driver_is_not_gcc\",\n    dwp_files = \":empty\",\n    linker_files = \":crosstool_wrapper_driver_is_not_gcc\",\n    objcopy_files = \":empty\",\n    strip_files = \":empty\",\n    # To support linker flags that need to go to the start of command line\n    # we need the toolchain to support parameter files. Parameter files are\n    # last on the command line and contain all shared libraries to link, so all\n    # regular options will be left of them.\n    supports_param_files = 1,\n    toolchain_identifier = \"local_linux\",\n    toolchain_config = \":cc-compiler-local-config\",\n)\n\ncc_toolchain_config(\n    name = \"cc-compiler-local-config\",\n    cpu = \"local\",\n    compiler = \"compiler\",\n    toolchain_identifier = \"local_linux\",\n    host_system_name = \"local\",\n    target_system_name = \"local\",\n    target_libc = \"local\",\n    abi_version = \"local\",\n    abi_libc_version = \"local\",\n    cxx_builtin_include_directories = [%{cxx_builtin_include_directories}],\n    host_compiler_path = \"%{host_compiler_path}\",\n    host_compiler_prefix = \"%{host_compiler_prefix}\",\n    compile_flags = [\n        \"-U_FORTIFY_SOURCE\",\n        \"-fstack-protector\",\n        \"-Wall\",\n        \"-Wunused-but-set-parameter\",\n        \"-Wno-free-nonheap-object\",\n        \"-fno-omit-frame-pointer\",\n    ],\n    opt_compile_flags = [\n        \"-g0\",\n        \"-O2\",\n        \"-D_FORTIFY_SOURCE=1\",\n        \"-DNDEBUG\",\n        \"-ffunction-sections\",\n        \"-fdata-sections\",\n    ],\n    dbg_compile_flags = [\"-g\"],\n    cxx_flags = [\"-std=c++14\"],\n    link_flags = [\n        \"-fuse-ld=gold\",\n        \"-Wl,-no-as-needed\",\n        \"-Wl,-z,relro,-z,now\",\n        \"-pass-exit-codes\",\n        \"-lstdc++\",\n        \"-lm\",\n    ],\n    link_libs = [],\n    opt_link_flags = [],\n    unfiltered_compile_flags = [\n        \"-fno-canonical-system-headers\",\n        \"-Wno-builtin-macro-redefined\",\n        \"-D__DATE__=\\\"redacted\\\"\",\n        \"-D__TIMESTAMP__=\\\"redacted\\\"\",\n        \"-D__TIME__=\\\"redacted\\\"\",\n    ] + [%{unfiltered_compile_flags}],\n    linker_bin_path = \"%{linker_bin_path}\",\n    coverage_compile_flags = [\"--coverage\"],\n    coverage_link_flags = [\"--coverage\"],\n    supports_start_end_lib = True,\n)\n\nfilegroup(\n    name = \"empty\",\n    srcs = [],\n)\n\nfilegroup(\n    name = \"crosstool_wrapper_driver_is_not_gcc\",\n    srcs = [\"clang/bin/crosstool_wrapper_driver_is_not_gcc\"],\n)\n\n"
  },
  {
    "path": "third_party/gpus/crosstool/BUILD.tpl",
    "content": "# This file is expanded from a template by cuda_configure.bzl\n# Update cuda_configure.bzl#verify_build_defines when adding new variables.\n\nload(\":cc_toolchain_config.bzl\", \"cc_toolchain_config\")\n\nlicenses([\"restricted\"])\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntoolchain(\n    name = \"toolchain-linux-x86_64\",\n    exec_compatible_with = [\n        \"@platforms//os:linux\",\n        \"@platforms//cpu:x86_64\",\n    ],\n    target_compatible_with = [\n        \"@platforms//os:linux\",\n        \"@platforms//cpu:x86_64\",\n    ],\n    toolchain = \":cc-compiler-local\",\n    toolchain_type = \"@bazel_tools//tools/cpp:toolchain_type\",\n)\n\ncc_toolchain_suite(\n    name = \"toolchain\",\n    toolchains = {\n        \"local|compiler\": \":cc-compiler-local\",\n        \"darwin|compiler\": \":cc-compiler-darwin\",\n        \"x64_windows|msvc-cl\": \":cc-compiler-windows\",\n        \"x64_windows\": \":cc-compiler-windows\",\n        \"arm\": \":cc-compiler-local\",\n        \"aarch64\": \":cc-compiler-local\",\n        \"k8\": \":cc-compiler-local\",\n        \"piii\": \":cc-compiler-local\",\n        \"ppc\": \":cc-compiler-local\",\n        \"darwin\": \":cc-compiler-darwin\",\n    },\n)\n\ncc_toolchain(\n    name = \"cc-compiler-local\",\n    all_files = \"%{compiler_deps}\",\n    compiler_files = \"%{compiler_deps}\",\n    ar_files = \"%{compiler_deps}\",\n    as_files = \"%{compiler_deps}\",\n    dwp_files = \":empty\",\n    linker_files = \"%{compiler_deps}\",\n    objcopy_files = \":empty\",\n    strip_files = \":empty\",\n    # To support linker flags that need to go to the start of command line\n    # we need the toolchain to support parameter files. Parameter files are\n    # last on the command line and contain all shared libraries to link, so all\n    # regular options will be left of them.\n    supports_param_files = 1,\n    toolchain_identifier = \"local_linux\",\n    toolchain_config = \":cc-compiler-local-config\",\n)\n\ncc_toolchain_config(\n    name = \"cc-compiler-local-config\",\n    cpu = \"local\",\n    builtin_include_directories = [%{cxx_builtin_include_directories}],\n    extra_no_canonical_prefixes_flags = [%{extra_no_canonical_prefixes_flags}],\n    host_compiler_path = \"%{host_compiler_path}\",\n    host_compiler_prefix = \"%{host_compiler_prefix}\",\n    host_compiler_warnings = [%{host_compiler_warnings}],\n    host_unfiltered_compile_flags = [%{unfiltered_compile_flags}],\n    linker_bin_path = \"%{linker_bin_path}\",\n    builtin_sysroot = \"%{builtin_sysroot}\",\n    cuda_path = \"%{cuda_toolkit_path}\",\n    compiler = \"%{compiler}\",\n)\n\ncc_toolchain(\n    name = \"cc-compiler-darwin\",\n    all_files = \"%{compiler_deps}\",\n    compiler_files = \"%{compiler_deps}\",\n    ar_files = \"%{compiler_deps}\",\n    as_files = \"%{compiler_deps}\",\n    dwp_files = \":empty\",\n    linker_files = \"%{compiler_deps}\",\n    objcopy_files = \":empty\",\n    strip_files = \":empty\",\n    supports_param_files = 0,\n    toolchain_identifier = \"local_darwin\",\n    toolchain_config = \":cc-compiler-local-darwin\",\n)\n\ncc_toolchain_config(\n    name = \"cc-compiler-local-darwin\",\n    cpu = \"darwin\",\n    builtin_include_directories = [%{cxx_builtin_include_directories}],\n    extra_no_canonical_prefixes_flags = [%{extra_no_canonical_prefixes_flags}],\n    host_compiler_path = \"%{host_compiler_path}\",\n    host_compiler_prefix = \"%{host_compiler_prefix}\",\n    host_compiler_warnings = [%{host_compiler_warnings}],\n    host_unfiltered_compile_flags = [%{unfiltered_compile_flags}],\n    linker_bin_path = \"%{linker_bin_path}\",\n)\n\ncc_toolchain(\n    name = \"cc-compiler-windows\",\n    all_files = \"%{win_compiler_deps}\",\n    compiler_files = \"%{win_compiler_deps}\",\n    ar_files = \"%{win_compiler_deps}\",\n    as_files = \"%{win_compiler_deps}\",\n    dwp_files = \":empty\",\n    linker_files = \"%{win_compiler_deps}\",\n    objcopy_files = \":empty\",\n    strip_files = \":empty\",\n    supports_param_files = 1,\n    toolchain_identifier = \"local_windows\",\n    toolchain_config = \":cc-compiler-windows-config\",\n)\n\ncc_toolchain_config(\n    name = \"cc-compiler-windows-config\",\n    cpu = \"x64_windows\",\n    builtin_include_directories = [%{cxx_builtin_include_directories}],\n    msvc_cl_path = \"%{msvc_cl_path}\",\n    msvc_env_include = \"%{msvc_env_include}\",\n    msvc_env_lib = \"%{msvc_env_lib}\",\n    msvc_env_path = \"%{msvc_env_path}\",\n    msvc_env_tmp = \"%{msvc_env_tmp}\",\n    msvc_lib_path = \"%{msvc_lib_path}\",\n    msvc_link_path = \"%{msvc_link_path}\",\n    msvc_ml_path = \"%{msvc_ml_path}\",\n    compiler = \"msvc\",\n)\n\nfilegroup(\n    name = \"empty\",\n    srcs = [],\n)\n\nfilegroup(\n    name = \"crosstool_wrapper_driver_is_not_gcc\",\n    srcs = [\"clang/bin/crosstool_wrapper_driver_is_not_gcc\"],\n)\n\nfilegroup(\n    name = \"windows_msvc_wrapper_files\",\n    srcs = glob([\"windows/msvc_*\"]),\n)\n"
  },
  {
    "path": "third_party/gpus/crosstool/LICENSE",
    "content": "Copyright 2015 The TensorFlow Authors.  All rights reserved.\n\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright 2015, The TensorFlow Authors.\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "third_party/gpus/crosstool/cc_toolchain_config.bzl.tpl",
    "content": "\"\"\"cc_toolchain_config rule for configuring CUDA toolchains on Linux, Mac, and Windows.\"\"\"\n\nload(\n    \"@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl\",\n    \"action_config\",\n    \"artifact_name_pattern\",\n    \"env_entry\",\n    \"env_set\",\n    \"feature\",\n    \"feature_set\",\n    \"flag_group\",\n    \"flag_set\",\n    \"tool\",\n    \"tool_path\",\n    \"variable_with_value\",\n    \"with_feature_set\",\n)\nload(\"@bazel_tools//tools/build_defs/cc:action_names.bzl\", \"ACTION_NAMES\")\n\ndef all_assembly_actions():\n    return [\n        ACTION_NAMES.assemble,\n        ACTION_NAMES.preprocess_assemble,\n    ]\n\ndef all_compile_actions():\n    return [\n        ACTION_NAMES.assemble,\n        ACTION_NAMES.c_compile,\n        ACTION_NAMES.cpp_compile,\n        ACTION_NAMES.cpp_header_parsing,\n        ACTION_NAMES.cpp_module_codegen,\n        ACTION_NAMES.cpp_module_compile,\n        ACTION_NAMES.linkstamp_compile,\n        ACTION_NAMES.preprocess_assemble,\n    ]\n\ndef all_c_compile_actions():\n    return [\n        ACTION_NAMES.c_compile,\n    ]\n\ndef all_cpp_compile_actions():\n    return [\n        ACTION_NAMES.cpp_compile,\n        ACTION_NAMES.cpp_header_parsing,\n        ACTION_NAMES.cpp_module_codegen,\n        ACTION_NAMES.cpp_module_compile,\n        ACTION_NAMES.linkstamp_compile,\n    ]\n\ndef all_preprocessed_actions():\n    return [\n        ACTION_NAMES.c_compile,\n        ACTION_NAMES.cpp_compile,\n        ACTION_NAMES.cpp_header_parsing,\n        ACTION_NAMES.cpp_module_codegen,\n        ACTION_NAMES.cpp_module_compile,\n        ACTION_NAMES.linkstamp_compile,\n        ACTION_NAMES.preprocess_assemble,\n    ]\n\ndef all_link_actions():\n    return [\n        ACTION_NAMES.cpp_link_executable,\n        ACTION_NAMES.cpp_link_dynamic_library,\n        ACTION_NAMES.cpp_link_nodeps_dynamic_library,\n    ]\n\ndef all_executable_link_actions():\n    return [\n        ACTION_NAMES.cpp_link_executable,\n    ]\n\ndef all_shared_library_link_actions():\n    return [\n        ACTION_NAMES.cpp_link_dynamic_library,\n        ACTION_NAMES.cpp_link_nodeps_dynamic_library,\n    ]\n\ndef all_archive_actions():\n    return [ACTION_NAMES.cpp_link_static_library]\n\ndef all_strip_actions():\n    return [ACTION_NAMES.strip]\n\ndef _library_to_link(flag_prefix, value, iterate = None):\n    return flag_group(\n        flags = [\n            \"{}%{{libraries_to_link.{}}}\".format(\n                flag_prefix,\n                iterate if iterate else \"name\",\n            ),\n        ],\n        iterate_over = (\"libraries_to_link.\" + iterate if iterate else None),\n        expand_if_equal = variable_with_value(\n            name = \"libraries_to_link.type\",\n            value = value,\n        ),\n    )\n\ndef _surround_static_library(prefix, suffix):\n    return [\n        flag_group(\n            flags = [prefix, \"%{libraries_to_link.name}\", suffix],\n            expand_if_true = \"libraries_to_link.is_whole_archive\",\n        ),\n        flag_group(\n            flags = [\"%{libraries_to_link.name}\"],\n            expand_if_false = \"libraries_to_link.is_whole_archive\",\n        ),\n    ]\n\ndef _prefix_static_library(prefix):\n    return [\n        flag_group(\n            flags = [\"%{libraries_to_link.name}\"],\n            expand_if_false = \"libraries_to_link.is_whole_archive\",\n        ),\n        flag_group(\n            flags = [prefix + \"%{libraries_to_link.name}\"],\n            expand_if_true = \"libraries_to_link.is_whole_archive\",\n        ),\n    ]\n\ndef _static_library_to_link(alwayslink_prefix, alwayslink_suffix = None):\n    if alwayslink_suffix:\n        flag_groups = _surround_static_library(alwayslink_prefix, alwayslink_suffix)\n    else:\n        flag_groups = _prefix_static_library(alwayslink_prefix)\n    return flag_group(\n        flag_groups = flag_groups,\n        expand_if_equal = variable_with_value(\n            name = \"libraries_to_link.type\",\n            value = \"static_library\",\n        ),\n    )\n\ndef _iterate_flag_group(iterate_over, flags = [], flag_groups = []):\n    return flag_group(\n        iterate_over = iterate_over,\n        expand_if_available = iterate_over,\n        flag_groups = flag_groups,\n        flags = flags,\n    )\n\ndef _libraries_to_link_group(flavour):\n    if flavour == \"linux\":\n        return _iterate_flag_group(\n            iterate_over = \"libraries_to_link\",\n            flag_groups = [\n                flag_group(\n                    flags = [\"-Wl,--start-lib\"],\n                    expand_if_equal = variable_with_value(\n                        name = \"libraries_to_link.type\",\n                        value = \"object_file_group\",\n                    ),\n                ),\n                _library_to_link(\"\", \"object_file_group\", \"object_files\"),\n                flag_group(\n                    flags = [\"-Wl,--end-lib\"],\n                    expand_if_equal = variable_with_value(\n                        name = \"libraries_to_link.type\",\n                        value = \"object_file_group\",\n                    ),\n                ),\n                _library_to_link(\"\", \"object_file\"),\n                _library_to_link(\"\", \"interface_library\"),\n                _static_library_to_link(\"-Wl,-whole-archive\", \"-Wl,-no-whole-archive\"),\n                _library_to_link(\"-l\", \"dynamic_library\"),\n                _library_to_link(\"-l:\", \"versioned_dynamic_library\"),\n            ],\n        )\n    elif flavour == \"darwin\":\n        return _iterate_flag_group(\n            iterate_over = \"libraries_to_link\",\n            flag_groups = [\n                _library_to_link(\"\", \"object_file_group\", \"object_files\"),\n                _library_to_link(\"\", \"object_file\"),\n                _library_to_link(\"\", \"interface_library\"),\n                _static_library_to_link(\"-Wl,-force_load,\"),\n                _library_to_link(\"-l\", \"dynamic_library\"),\n                _library_to_link(\"-l:\", \"versioned_dynamic_library\"),\n            ],\n        )\n    elif flavour == \"msvc\":\n        return _iterate_flag_group(\n            iterate_over = \"libraries_to_link\",\n            flag_groups = [\n                _library_to_link(\"\", \"object_file_group\", \"object_files\"),\n                _library_to_link(\"\", \"object_file\"),\n                _library_to_link(\"\", \"interface_library\"),\n                _static_library_to_link(\"/WHOLEARCHIVE:\"),\n            ],\n        )\n\ndef _action_configs_with_tool(path, actions):\n    return [\n        action_config(\n            action_name = name,\n            enabled = True,\n            tools = [tool(path = path)],\n        )\n        for name in actions\n    ]\n\ndef _action_configs(assembly_path, c_compiler_path, cc_compiler_path, archiver_path, linker_path, strip_path):\n    return _action_configs_with_tool(\n        assembly_path,\n        all_assembly_actions(),\n    ) + _action_configs_with_tool(\n        c_compiler_path,\n        all_c_compile_actions(),\n    ) + _action_configs_with_tool(\n        cc_compiler_path,\n        all_cpp_compile_actions(),\n    ) + _action_configs_with_tool(\n        archiver_path,\n        all_archive_actions(),\n    ) + _action_configs_with_tool(\n        linker_path,\n        all_link_actions(),\n    ) + _action_configs_with_tool(\n        strip_path,\n        all_strip_actions(),\n    )\n\ndef _tool_paths(cpu, ctx):\n    if cpu in [\"local\", \"darwin\"]:\n        return [\n            tool_path(name = \"gcc\", path = ctx.attr.host_compiler_path),\n            tool_path(name = \"ar\", path = ctx.attr.host_compiler_prefix + (\n                \"/ar\" if cpu == \"local\" else \"/libtool\"\n            )),\n            tool_path(name = \"compat-ld\", path = ctx.attr.host_compiler_prefix + \"/ld\"),\n            tool_path(name = \"cpp\", path = ctx.attr.host_compiler_prefix + \"/cpp\"),\n            tool_path(name = \"dwp\", path = ctx.attr.host_compiler_prefix + \"/dwp\"),\n            tool_path(name = \"gcov\", path = ctx.attr.host_compiler_prefix + \"/gcov\"),\n            tool_path(name = \"ld\", path = ctx.attr.host_compiler_prefix + \"/ld\"),\n            tool_path(name = \"nm\", path = ctx.attr.host_compiler_prefix + \"/nm\"),\n            tool_path(name = \"objcopy\", path = ctx.attr.host_compiler_prefix + \"/objcopy\"),\n            tool_path(name = \"objdump\", path = ctx.attr.host_compiler_prefix + \"/objdump\"),\n            tool_path(name = \"strip\", path = ctx.attr.host_compiler_prefix + \"/strip\"),\n        ]\n    elif cpu == \"x64_windows\":\n        return [\n            tool_path(name = \"ar\", path = ctx.attr.msvc_lib_path),\n            tool_path(name = \"ml\", path = ctx.attr.msvc_ml_path),\n            tool_path(name = \"cpp\", path = ctx.attr.msvc_cl_path),\n            tool_path(name = \"gcc\", path = ctx.attr.msvc_cl_path),\n            tool_path(name = \"gcov\", path = \"wrapper/bin/msvc_nop.bat\"),\n            tool_path(name = \"ld\", path = ctx.attr.msvc_link_path),\n            tool_path(name = \"nm\", path = \"wrapper/bin/msvc_nop.bat\"),\n            tool_path(\n                name = \"objcopy\",\n                path = \"wrapper/bin/msvc_nop.bat\",\n            ),\n            tool_path(\n                name = \"objdump\",\n                path = \"wrapper/bin/msvc_nop.bat\",\n            ),\n            tool_path(\n                name = \"strip\",\n                path = \"wrapper/bin/msvc_nop.bat\",\n            ),\n        ]\n    else:\n        fail(\"Unreachable\")\n\ndef _sysroot_group():\n    return flag_group(\n        flags = [\"--sysroot=%{sysroot}\"],\n        expand_if_available = \"sysroot\",\n    )\n\ndef _no_canonical_prefixes_group(extra_flags):\n    return flag_group(\n        flags = [\n            \"-no-canonical-prefixes\",\n        ] + extra_flags,\n    )\n\ndef _cuda_set(cuda_path, actions):\n    if cuda_path:\n        return [flag_set(\n            actions = actions,\n            flag_groups = [\n                flag_group(\n                    flags = [\"--cuda-path=\" + cuda_path],\n                ),\n            ],\n        )]\n    else:\n        return []\n\ndef _nologo():\n    return flag_group(flags = [\"/nologo\"])\n\ndef _features(cpu, compiler, ctx):\n    if cpu in [\"local\", \"darwin\"]:\n        return [\n            feature(name = \"no_legacy_features\"),\n            feature(\n                name = \"all_compile_flags\",\n                enabled = True,\n                flag_sets = [\n                    flag_set(\n                        actions = all_compile_actions(),\n                        flag_groups = [\n                            flag_group(\n                                flags = [\"-MD\", \"-MF\", \"%{dependency_file}\"],\n                                expand_if_available = \"dependency_file\",\n                            ),\n                            flag_group(\n                                flags = [\"-gsplit-dwarf\"],\n                                expand_if_available = \"per_object_debug_info_file\",\n                            ),\n                        ],\n                    ),\n                    flag_set(\n                        actions = all_preprocessed_actions(),\n                        flag_groups = [\n                            flag_group(\n                                flags = [\"-frandom-seed=%{output_file}\"],\n                                expand_if_available = \"output_file\",\n                            ),\n                            _iterate_flag_group(\n                                flags = [\"-D%{preprocessor_defines}\"],\n                                iterate_over = \"preprocessor_defines\",\n                            ),\n                            _iterate_flag_group(\n                                flags = [\"-include\", \"%{includes}\"],\n                                iterate_over = \"includes\",\n                            ),\n                            _iterate_flag_group(\n                                flags = [\"-iquote\", \"%{quote_include_paths}\"],\n                                iterate_over = \"quote_include_paths\",\n                            ),\n                            _iterate_flag_group(\n                                flags = [\"-I%{include_paths}\"],\n                                iterate_over = \"include_paths\",\n                            ),\n                            _iterate_flag_group(\n                                flags = [\"-isystem\", \"%{system_include_paths}\"],\n                                iterate_over = \"system_include_paths\",\n                            ),\n                            _iterate_flag_group(\n                                flags = [\"-F\", \"%{framework_include_paths}\"],\n                                iterate_over = \"framework_include_paths\",\n                            ),\n                        ],\n                    ),\n                    flag_set(\n                        actions = all_cpp_compile_actions(),\n                        flag_groups = [\n                            flag_group(flags = [\n                                \"-fmerge-all-constants\",\n                            ]),\n                        ] if compiler == \"clang\" else [],\n                    ),\n                    flag_set(\n                        actions = all_compile_actions(),\n                        flag_groups = [\n                            flag_group(\n                                flags = [\n                                    \"-Wno-builtin-macro-redefined\",\n                                    \"-D__DATE__=\\\"redacted\\\"\",\n                                    \"-D__TIMESTAMP__=\\\"redacted\\\"\",\n                                    \"-D__TIME__=\\\"redacted\\\"\",\n                                ],\n                            ),\n                            flag_group(\n                                flags = [\"-fPIC\"],\n                                expand_if_available = \"pic\",\n                            ),\n                            flag_group(\n                                flags = [\"-fPIE\"],\n                                expand_if_not_available = \"pic\",\n                            ),\n                            flag_group(\n                                flags = [\n                                    \"-U_FORTIFY_SOURCE\",\n                                    \"-D_FORTIFY_SOURCE=1\",\n                                    \"-fstack-protector\",\n                                    \"-Wall\",\n                                ] + ctx.attr.host_compiler_warnings + [\n                                    \"-fno-omit-frame-pointer\",\n                                ],\n                            ),\n                            _no_canonical_prefixes_group(\n                                ctx.attr.extra_no_canonical_prefixes_flags,\n                            ),\n                        ],\n                    ),\n                    flag_set(\n                        actions = all_compile_actions(),\n                        flag_groups = [flag_group(flags = [\"-DNDEBUG\"])],\n                        with_features = [with_feature_set(features = [\"disable-assertions\"])],\n                    ),\n                    flag_set(\n                        actions = all_compile_actions(),\n                        flag_groups = [\n                            flag_group(\n                                flags = [\n                                    \"-g0\",\n                                    \"-O2\",\n                                    \"-ffunction-sections\",\n                                    \"-fdata-sections\",\n                                ],\n                            ),\n                        ],\n                        with_features = [with_feature_set(features = [\"opt\"])],\n                    ),\n                    flag_set(\n                        actions = all_compile_actions(),\n                        flag_groups = [flag_group(flags = [\"-g\"])],\n                        with_features = [with_feature_set(features = [\"dbg\"])],\n                    ),\n                ] + _cuda_set(\n                    ctx.attr.cuda_path,\n                    all_compile_actions(),\n                ) + [\n                    flag_set(\n                        actions = all_compile_actions(),\n                        flag_groups = [\n                            _iterate_flag_group(\n                                flags = [\"%{user_compile_flags}\"],\n                                iterate_over = \"user_compile_flags\",\n                            ),\n                            _sysroot_group(),\n                            flag_group(\n                                expand_if_available = \"source_file\",\n                                flags = [\"-c\", \"%{source_file}\"],\n                            ),\n                            flag_group(\n                                expand_if_available = \"output_assembly_file\",\n                                flags = [\"-S\"],\n                            ),\n                            flag_group(\n                                expand_if_available = \"output_preprocess_file\",\n                                flags = [\"-E\"],\n                            ),\n                            flag_group(\n                                expand_if_available = \"output_file\",\n                                flags = [\"-o\", \"%{output_file}\"],\n                            ),\n                        ],\n                    ),\n                ],\n            ),\n            feature(\n                name = \"all_archive_flags\",\n                enabled = True,\n                flag_sets = [\n                    flag_set(\n                        actions = all_archive_actions(),\n                        flag_groups = [\n                            flag_group(\n                                expand_if_available = \"linker_param_file\",\n                                flags = [\"@%{linker_param_file}\"],\n                            ),\n                            flag_group(flags = [\"rcsD\"]),\n                            flag_group(\n                                flags = [\"%{output_execpath}\"],\n                                expand_if_available = \"output_execpath\",\n                            ),\n                            flag_group(\n                                iterate_over = \"libraries_to_link\",\n                                flag_groups = [\n                                    flag_group(\n                                        flags = [\"%{libraries_to_link.name}\"],\n                                        expand_if_equal = variable_with_value(\n                                            name = \"libraries_to_link.type\",\n                                            value = \"object_file\",\n                                        ),\n                                    ),\n                                    flag_group(\n                                        flags = [\"%{libraries_to_link.object_files}\"],\n                                        iterate_over = \"libraries_to_link.object_files\",\n                                        expand_if_equal = variable_with_value(\n                                            name = \"libraries_to_link.type\",\n                                            value = \"object_file_group\",\n                                        ),\n                                    ),\n                                ],\n                                expand_if_available = \"libraries_to_link\",\n                            ),\n                        ],\n                    ),\n                ],\n            ),\n            feature(\n                name = \"all_link_flags\",\n                enabled = True,\n                flag_sets = [\n                    flag_set(\n                        actions = all_shared_library_link_actions(),\n                        flag_groups = [flag_group(flags = [\"-shared\"])],\n                    ),\n                    flag_set(\n                        actions = all_link_actions(),\n                        flag_groups = ([\n                            flag_group(flags = [\"-Wl,-no-as-needed\"])\n                        ] if cpu == \"local\" else []) + ([\n                            flag_group(flags = [\"-B\" + ctx.attr.linker_bin_path])\n                        ] if ctx.attr.linker_bin_path else []) + [\n                            flag_group(\n                                flags = [\"@%{linker_param_file}\"],\n                                expand_if_available = \"linker_param_file\",\n                            ),\n                            _iterate_flag_group(\n                                flags = [\"%{linkstamp_paths}\"],\n                                iterate_over = \"linkstamp_paths\",\n                            ),\n                            flag_group(\n                                flags = [\"-o\", \"%{output_execpath}\"],\n                                expand_if_available = \"output_execpath\",\n                            ),\n                            _iterate_flag_group(\n                                flags = [\"-L%{library_search_directories}\"],\n                                iterate_over = \"library_search_directories\",\n                            ),\n                            _iterate_flag_group(\n                                iterate_over = \"runtime_library_search_directories\",\n                                flags = [\n                                    \"-Wl,-rpath,$ORIGIN/%{runtime_library_search_directories}\",\n                                ] if cpu == \"local\" else [\n                                    \"-Wl,-rpath,@loader_path/%{runtime_library_search_directories}\",\n                                ],\n                            ),\n                            _libraries_to_link_group(\"darwin\" if cpu == \"darwin\" else \"linux\"),\n                            _iterate_flag_group(\n                                flags = [\"%{user_link_flags}\"],\n                                iterate_over = \"user_link_flags\",\n                            ),\n                            flag_group(\n                                flags = [\"-Wl,--gdb-index\"],\n                                expand_if_available = \"is_using_fission\",\n                            ),\n                            flag_group(\n                                flags = [\"-Wl,-S\"],\n                                expand_if_available = \"strip_debug_symbols\",\n                            ),\n                            flag_group(flags = [\"-lc++\" if cpu == \"darwin\" else \"-lstdc++\"]),\n                            _no_canonical_prefixes_group(\n                                ctx.attr.extra_no_canonical_prefixes_flags,\n                            ),\n                        ],\n                    ),\n                    flag_set(\n                        actions = all_executable_link_actions(),\n                        flag_groups = [flag_group(flags = [\"-pie\"])],\n                    ),\n                ] + ([\n                    flag_set(\n                        actions = all_link_actions(),\n                        flag_groups = [flag_group(flags = [\n                            \"-Wl,-z,relro,-z,now\",\n                        ])],\n                    ),\n                ] if cpu == \"local\" else []) + ([\n                    flag_set(\n                        actions = all_link_actions(),\n                        flag_groups = [\n                            flag_group(flags = [\"-Wl,--gc-sections\"]),\n                            flag_group(\n                                flags = [\"-Wl,--build-id=md5\", \"-Wl,--hash-style=gnu\"],\n                            ),\n                        ],\n                    ),\n                ] if cpu == \"local\" else []) + ([\n                    flag_set(\n                        actions = all_link_actions(),\n                        flag_groups = [flag_group(flags = [\"-undefined\", \"dynamic_lookup\"])],\n                    ),\n                ] if cpu == \"darwin\" else []) + _cuda_set(\n                    ctx.attr.cuda_path,\n                    all_link_actions(),\n                ) + [\n                    flag_set(\n                        actions = all_link_actions(),\n                        flag_groups = [\n                            _sysroot_group(),\n                        ],\n                    ),\n                ],\n            ),\n            feature(name = \"disable-assertions\"),\n            feature(\n                name = \"opt\",\n                implies = [\"disable-assertions\"],\n            ),\n            feature(name = \"fastbuild\"),\n            feature(name = \"dbg\"),\n            feature(name = \"supports_dynamic_linker\", enabled = True),\n            feature(name = \"pic\", enabled = True),\n            feature(name = \"supports_pic\", enabled = True),\n            feature(name = \"has_configured_linker_path\", enabled = True),\n        ]\n    elif cpu == \"x64_windows\":\n        return [\n            feature(name = \"compiler_param_file\"),\n            feature(name = \"no_legacy_features\"),\n            feature(\n                name = \"common_flags\",\n                enabled = True,\n                env_sets = [\n                    env_set(\n                        actions = all_compile_actions() + all_link_actions() + all_archive_actions(),\n                        env_entries = [\n                            env_entry(key = \"PATH\", value = ctx.attr.msvc_env_path),\n                            env_entry(key = \"INCLUDE\", value = ctx.attr.msvc_env_include),\n                            env_entry(key = \"LIB\", value = ctx.attr.msvc_env_lib),\n                            env_entry(key = \"TMP\", value = ctx.attr.msvc_env_tmp),\n                            env_entry(key = \"TEMP\", value = ctx.attr.msvc_env_tmp),\n                        ],\n                    ),\n                ],\n            ),\n            feature(\n                name = \"all_compile_flags\",\n                enabled = True,\n                flag_sets = [\n                    flag_set(\n                        actions = all_compile_actions(),\n                        flag_groups = [\n                            _nologo(),\n                            flag_group(\n                                flags = [\n                                    \"/DCOMPILER_MSVC\",\n                                    \"/DNOMINMAX\",\n                                    \"/D_WIN32_WINNT=0x0600\",\n                                    \"/D_CRT_SECURE_NO_DEPRECATE\",\n                                    \"/D_CRT_SECURE_NO_WARNINGS\",\n                                    \"/D_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS\",\n                                    \"/bigobj\",\n                                    \"/Zm500\",\n                                    \"/J\",\n                                    \"/Gy\",\n                                    \"/GF\",\n                                    \"/EHsc\",\n                                    \"/wd4351\",\n                                    \"/wd4291\",\n                                    \"/wd4250\",\n                                    \"/wd4996\",\n                                ],\n                            ),\n                            _iterate_flag_group(\n                                flags = [\"/I%{quote_include_paths}\"],\n                                iterate_over = \"quote_include_paths\",\n                            ),\n                            _iterate_flag_group(\n                                flags = [\"/I%{include_paths}\"],\n                                iterate_over = \"include_paths\",\n                            ),\n                            _iterate_flag_group(\n                                flags = [\"/I%{system_include_paths}\"],\n                                iterate_over = \"system_include_paths\",\n                            ),\n                            _iterate_flag_group(\n                                flags = [\"/D%{preprocessor_defines}\"],\n                                iterate_over = \"preprocessor_defines\",\n                            ),\n                        ],\n                    ),\n                    flag_set(\n                        actions = all_preprocessed_actions(),\n                        flag_groups = [flag_group(flags = [\"/showIncludes\"])],\n                    ),\n                    flag_set(\n                        actions = all_compile_actions(),\n                        flag_groups = [flag_group(flags = [\"/MT\"])],\n                        with_features = [with_feature_set(features = [\"static_link_msvcrt_no_debug\"])],\n                    ),\n                    flag_set(\n                        actions = all_compile_actions(),\n                        flag_groups = [flag_group(flags = [\"/MD\"])],\n                        with_features = [with_feature_set(features = [\"dynamic_link_msvcrt_no_debug\"])],\n                    ),\n                    flag_set(\n                        actions = all_compile_actions(),\n                        flag_groups = [flag_group(flags = [\"/MTd\"])],\n                        with_features = [with_feature_set(features = [\"static_link_msvcrt_debug\"])],\n                    ),\n                    flag_set(\n                        actions = all_compile_actions(),\n                        flag_groups = [flag_group(flags = [\"/MDd\"])],\n                        with_features = [with_feature_set(features = [\"dynamic_link_msvcrt_debug\"])],\n                    ),\n                    flag_set(\n                        actions = all_compile_actions(),\n                        flag_groups = [flag_group(flags = [\"/Od\", \"/Z7\", \"/DDEBUG\"])],\n                        with_features = [with_feature_set(features = [\"dbg\"])],\n                    ),\n                    flag_set(\n                        actions = all_compile_actions(),\n                        flag_groups = [flag_group(flags = [\"/Od\", \"/Z7\", \"/DDEBUG\"])],\n                        with_features = [with_feature_set(features = [\"fastbuild\"])],\n                    ),\n                    flag_set(\n                        actions = all_compile_actions(),\n                        flag_groups = [flag_group(flags = [\"/O2\", \"/DNDEBUG\"])],\n                        with_features = [with_feature_set(features = [\"opt\"])],\n                    ),\n                    flag_set(\n                        actions = all_preprocessed_actions(),\n                        flag_groups = [\n                            _iterate_flag_group(\n                                flags = [\"%{user_compile_flags}\"],\n                                iterate_over = \"user_compile_flags\",\n                            ),\n                        ] + ([\n                            flag_group(flags = ctx.attr.host_unfiltered_compile_flags),\n                        ] if ctx.attr.host_unfiltered_compile_flags else []),\n                    ),\n                    flag_set(\n                        actions = [ACTION_NAMES.assemble],\n                        flag_groups = [\n                            flag_group(\n                                flag_groups = [\n                                    flag_group(\n                                        flags = [\"/Fo%{output_file}\", \"/Zi\"],\n                                        expand_if_not_available = \"output_preprocess_file\",\n                                    ),\n                                ],\n                                expand_if_available = \"output_file\",\n                                expand_if_not_available = \"output_assembly_file\",\n                            ),\n                        ],\n                    ),\n                    flag_set(\n                        actions = all_preprocessed_actions(),\n                        flag_groups = [\n                            flag_group(\n                                flag_groups = [\n                                    flag_group(\n                                        flags = [\"/Fo%{output_file}\"],\n                                        expand_if_not_available = \"output_preprocess_file\",\n                                    ),\n                                ],\n                                expand_if_available = \"output_file\",\n                                expand_if_not_available = \"output_assembly_file\",\n                            ),\n                            flag_group(\n                                flag_groups = [\n                                    flag_group(\n                                        flags = [\"/Fa%{output_file}\"],\n                                        expand_if_available = \"output_assembly_file\",\n                                    ),\n                                ],\n                                expand_if_available = \"output_file\",\n                            ),\n                            flag_group(\n                                flag_groups = [\n                                    flag_group(\n                                        flags = [\"/P\", \"/Fi%{output_file}\"],\n                                        expand_if_available = \"output_preprocess_file\",\n                                    ),\n                                ],\n                                expand_if_available = \"output_file\",\n                            ),\n                        ],\n                    ),\n                    flag_set(\n                        actions = all_compile_actions(),\n                        flag_groups = [\n                            flag_group(\n                                flags = [\"/c\", \"%{source_file}\"],\n                                expand_if_available = \"source_file\",\n                            ),\n                        ],\n                    ),\n                ],\n            ),\n            feature(\n                name = \"all_archive_flags\",\n                enabled = True,\n                flag_sets = [\n                    flag_set(\n                        actions = all_archive_actions(),\n                        flag_groups = [\n                            _nologo(),\n                            flag_group(\n                                flags = [\"/OUT:%{output_execpath}\"],\n                                expand_if_available = \"output_execpath\",\n                            ),\n                        ],\n                    ),\n                ],\n            ),\n            feature(\n                name = \"all_link_flags\",\n                enabled = True,\n                flag_sets = [\n                    flag_set(\n                        actions = all_shared_library_link_actions(),\n                        flag_groups = [flag_group(flags = [\"/DLL\"])],\n                    ),\n                    flag_set(\n                        actions = all_link_actions(),\n                        flag_groups = [\n                            _nologo(),\n                            _iterate_flag_group(\n                                flags = [\"%{linkstamp_paths}\"],\n                                iterate_over = \"linkstamp_paths\",\n                            ),\n                            flag_group(\n                                flags = [\"/OUT:%{output_execpath}\"],\n                                expand_if_available = \"output_execpath\",\n                            ),\n                        ],\n                    ),\n                    flag_set(\n                        actions = all_shared_library_link_actions(),\n                        flag_groups = [\n                            flag_group(\n                                flags = [\"/IMPLIB:%{interface_library_output_path}\"],\n                                expand_if_available = \"interface_library_output_path\",\n                            ),\n                        ],\n                    ),\n                    flag_set(\n                        actions = all_link_actions() +\n                                  all_archive_actions(),\n                        flag_groups = [\n                            _libraries_to_link_group(\"msvc\"),\n                        ],\n                    ),\n                    flag_set(\n                        actions = all_link_actions(),\n                        flag_groups = [\n                            flag_group(flags = [\"/SUBSYSTEM:CONSOLE\"]),\n                            _iterate_flag_group(\n                                flags = [\"%{user_link_flags}\"],\n                                iterate_over = \"user_link_flags\",\n                            ),\n                            flag_group(flags = [\"/MACHINE:X64\"]),\n                        ],\n                    ),\n                    flag_set(\n                        actions = all_link_actions() +\n                                  all_archive_actions(),\n                        flag_groups = [\n                            flag_group(\n                                flags = [\"@%{linker_param_file}\"],\n                                expand_if_available = \"linker_param_file\",\n                            ),\n                        ],\n                    ),\n                    flag_set(\n                        actions = all_link_actions(),\n                        flag_groups = [flag_group(flags = [\"/DEFAULTLIB:libcmt.lib\"])],\n                        with_features = [with_feature_set(features = [\"static_link_msvcrt_no_debug\"])],\n                    ),\n                    flag_set(\n                        actions = all_link_actions(),\n                        flag_groups = [flag_group(flags = [\"/DEFAULTLIB:msvcrt.lib\"])],\n                        with_features = [with_feature_set(features = [\"dynamic_link_msvcrt_no_debug\"])],\n                    ),\n                    flag_set(\n                        actions = all_link_actions(),\n                        flag_groups = [flag_group(flags = [\"/DEFAULTLIB:libcmtd.lib\"])],\n                        with_features = [with_feature_set(features = [\"static_link_msvcrt_debug\"])],\n                    ),\n                    flag_set(\n                        actions = all_link_actions(),\n                        flag_groups = [flag_group(flags = [\"/DEFAULTLIB:msvcrtd.lib\"])],\n                        with_features = [with_feature_set(features = [\"dynamic_link_msvcrt_debug\"])],\n                    ),\n                    flag_set(\n                        actions = all_link_actions(),\n                        flag_groups = [flag_group(flags = [\"/DEBUG:FULL\", \"/INCREMENTAL:NO\"])],\n                        with_features = [with_feature_set(features = [\"dbg\"])],\n                    ),\n                    flag_set(\n                        actions = all_link_actions(),\n                        flag_groups = [\n                            flag_group(flags = [\"/DEBUG:FASTLINK\", \"/INCREMENTAL:NO\"]),\n                        ],\n                        with_features = [with_feature_set(features = [\"fastbuild\"])],\n                    ),\n                    flag_set(\n                        actions = all_link_actions(),\n                        flag_groups = [\n                            flag_group(\n                                flags = [\"/DEF:%{def_file_path}\", \"/ignore:4070\"],\n                                expand_if_available = \"def_file_path\",\n                            ),\n                        ],\n                    ),\n                ],\n            ),\n            feature(name = \"parse_showincludes\", enabled = True),\n            feature(name = \"no_stripping\", enabled = True),\n            feature(\n                name = \"targets_windows\",\n                enabled = True,\n                implies = [\"copy_dynamic_libraries_to_binary\"],\n            ),\n            feature(name = \"copy_dynamic_libraries_to_binary\"),\n            feature(\n                name = \"generate_pdb_file\",\n                requires = [\n                    feature_set(features = [\"dbg\"]),\n                    feature_set(features = [\"fastbuild\"]),\n                ],\n            ),\n            feature(name = \"static_link_msvcrt\"),\n            feature(\n                name = \"static_link_msvcrt_no_debug\",\n                requires = [\n                    feature_set(features = [\"fastbuild\"]),\n                    feature_set(features = [\"opt\"]),\n                ],\n            ),\n            feature(\n                name = \"dynamic_link_msvcrt_no_debug\",\n                requires = [\n                    feature_set(features = [\"fastbuild\"]),\n                    feature_set(features = [\"opt\"]),\n                ],\n            ),\n            feature(\n                name = \"static_link_msvcrt_debug\",\n                requires = [feature_set(features = [\"dbg\"])],\n            ),\n            feature(\n                name = \"dynamic_link_msvcrt_debug\",\n                requires = [feature_set(features = [\"dbg\"])],\n            ),\n            feature(\n                name = \"dbg\",\n                implies = [\"generate_pdb_file\"],\n            ),\n            feature(\n                name = \"fastbuild\",\n                implies = [\"generate_pdb_file\"],\n            ),\n            feature(\n                name = \"opt\",\n            ),\n            feature(name = \"windows_export_all_symbols\"),\n            feature(name = \"no_windows_export_all_symbols\"),\n            feature(name = \"supports_dynamic_linker\", enabled = True),\n            feature(\n                name = \"supports_interface_shared_libraries\",\n                enabled = True,\n            ),\n            feature(name = \"has_configured_linker_path\", enabled = True),\n        ]\n    else:\n        fail(\"Unreachable\")\n\ndef _impl(ctx):\n    cpu = ctx.attr.cpu\n    compiler = ctx.attr.compiler\n\n    if (cpu == \"darwin\"):\n        toolchain_identifier = \"local_darwin\"\n        target_cpu = \"darwin\"\n        target_libc = \"macosx\"\n        compiler = \"compiler\"\n        action_configs = _action_configs(\n            assembly_path = ctx.attr.host_compiler_path,\n            c_compiler_path = ctx.attr.host_compiler_path,\n            cc_compiler_path = ctx.attr.host_compiler_path,\n            archiver_path = ctx.attr.host_compiler_prefix + \"/libtool\",\n            linker_path = ctx.attr.host_compiler_path,\n            strip_path = ctx.attr.host_compiler_prefix + \"/strip\",\n        )\n        artifact_name_patterns = []\n    elif (cpu == \"local\"):\n        toolchain_identifier = \"local_linux\"\n        target_cpu = \"local\"\n        target_libc = \"local\"\n        action_configs = _action_configs(\n            assembly_path = ctx.attr.host_compiler_path,\n            c_compiler_path = ctx.attr.host_compiler_path,\n            cc_compiler_path = ctx.attr.host_compiler_path,\n            archiver_path = ctx.attr.host_compiler_prefix + \"/ar\",\n            linker_path = ctx.attr.host_compiler_path,\n            strip_path = ctx.attr.host_compiler_prefix + \"/strip\",\n        )\n        artifact_name_patterns = []\n    elif (cpu == \"x64_windows\"):\n        toolchain_identifier = \"local_windows\"\n        target_cpu = \"x64_windows\"\n        target_libc = \"msvcrt\"\n        compiler = \"msvc-cl\"\n        action_configs = _action_configs(\n            assembly_path = ctx.attr.msvc_ml_path,\n            c_compiler_path = ctx.attr.msvc_cl_path,\n            cc_compiler_path = ctx.attr.msvc_cl_path,\n            archiver_path = ctx.attr.msvc_lib_path,\n            linker_path = ctx.attr.msvc_link_path,\n            strip_path = \"fake_tool_strip_not_supported\",\n        )\n        artifact_name_patterns = [\n            artifact_name_pattern(\n                category_name = \"object_file\",\n                prefix = \"\",\n                extension = \".obj\",\n            ),\n            artifact_name_pattern(\n                category_name = \"static_library\",\n                prefix = \"\",\n                extension = \".lib\",\n            ),\n            artifact_name_pattern(\n                category_name = \"alwayslink_static_library\",\n                prefix = \"\",\n                extension = \".lo.lib\",\n            ),\n            artifact_name_pattern(\n                category_name = \"executable\",\n                prefix = \"\",\n                extension = \".exe\",\n            ),\n            artifact_name_pattern(\n                category_name = \"dynamic_library\",\n                prefix = \"\",\n                extension = \".dll\",\n            ),\n            artifact_name_pattern(\n                category_name = \"interface_library\",\n                prefix = \"\",\n                extension = \".if.lib\",\n            ),\n        ]\n    else:\n        fail(\"Unreachable\")\n\n    out = ctx.actions.declare_file(ctx.label.name)\n    ctx.actions.write(out, \"Fake executable\")\n    return [\n        cc_common.create_cc_toolchain_config_info(\n            ctx = ctx,\n            features = _features(cpu, compiler, ctx),\n            action_configs = action_configs,\n            artifact_name_patterns = artifact_name_patterns,\n            cxx_builtin_include_directories = ctx.attr.builtin_include_directories,\n            toolchain_identifier = toolchain_identifier,\n            host_system_name = \"local\",\n            target_system_name = \"local\",\n            target_cpu = target_cpu,\n            target_libc = target_libc,\n            compiler = compiler,\n            abi_version = \"local\",\n            abi_libc_version = \"local\",\n            tool_paths = _tool_paths(cpu, ctx),\n            make_variables = [],\n            builtin_sysroot = ctx.attr.builtin_sysroot,\n            cc_target_os = None,\n        ),\n        DefaultInfo(\n            executable = out,\n        ),\n    ]\n\ncc_toolchain_config = rule(\n    implementation = _impl,\n    attrs = {\n        \"cpu\": attr.string(mandatory = True, values = [\"darwin\", \"local\", \"x64_windows\"]),\n        \"compiler\": attr.string(values = [\"clang\", \"msvc\", \"unknown\"], default = \"unknown\"),\n        \"builtin_include_directories\": attr.string_list(),\n        \"extra_no_canonical_prefixes_flags\": attr.string_list(),\n        \"host_compiler_path\": attr.string(),\n        \"host_compiler_prefix\": attr.string(),\n        \"host_compiler_warnings\": attr.string_list(),\n        \"host_unfiltered_compile_flags\": attr.string_list(),\n        \"linker_bin_path\": attr.string(),\n        \"builtin_sysroot\": attr.string(),\n        \"cuda_path\": attr.string(),\n        \"msvc_cl_path\": attr.string(default = \"msvc_not_used\"),\n        \"msvc_env_include\": attr.string(default = \"msvc_not_used\"),\n        \"msvc_env_lib\": attr.string(default = \"msvc_not_used\"),\n        \"msvc_env_path\": attr.string(default = \"msvc_not_used\"),\n        \"msvc_env_tmp\": attr.string(default = \"msvc_not_used\"),\n        \"msvc_lib_path\": attr.string(default = \"msvc_not_used\"),\n        \"msvc_link_path\": attr.string(default = \"msvc_not_used\"),\n        \"msvc_ml_path\": attr.string(default = \"msvc_not_used\"),\n    },\n    provides = [CcToolchainConfigInfo],\n    executable = True,\n)\n"
  },
  {
    "path": "third_party/gpus/crosstool/clang/bin/crosstool_wrapper_driver_is_not_gcc.tpl",
    "content": "#!/usr/bin/env python\n# Copyright 2015 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\n\"\"\"Crosstool wrapper for compiling CUDA programs.\n\nSYNOPSIS:\n  crosstool_wrapper_is_not_gcc [options passed in by cc_library()\n                                or cc_binary() rule]\n\nDESCRIPTION:\n  This script is expected to be called by the cc_library() or cc_binary() bazel\n  rules. When the option \"-x cuda\" is present in the list of arguments passed\n  to this script, it invokes the nvcc CUDA compiler. Most arguments are passed\n  as is as a string to --compiler-options of nvcc. When \"-x cuda\" is not\n  present, this wrapper invokes hybrid_driver_is_not_gcc with the input\n  arguments as is.\n\nNOTES:\n  Changes to the contents of this file must be propagated from\n  //third_party/gpus/crosstool/crosstool_wrapper_is_not_gcc to\n  //third_party/gpus/crosstool/v*/*/clang/bin/crosstool_wrapper_is_not_gcc\n\"\"\"\n\n__author__ = 'keveman@google.com (Manjunath Kudlur)'\n\nfrom argparse import ArgumentParser\nimport os\nimport subprocess\nimport re\nimport sys\nimport pipes\n\n# Template values set by cuda_autoconf.\nCPU_COMPILER = ('%{cpu_compiler}')\nGCC_HOST_COMPILER_PATH = ('%{gcc_host_compiler_path}')\n\nNVCC_PATH = '%{nvcc_path}'\nPREFIX_DIR = os.path.dirname(GCC_HOST_COMPILER_PATH)\nNVCC_VERSION = '%{cuda_version}'\n\ndef Log(s):\n  print('gpus/crosstool: {0}'.format(s))\n\n\ndef GetOptionValue(argv, option):\n  \"\"\"Extract the list of values for option from the argv list.\n\n  Args:\n    argv: A list of strings, possibly the argv passed to main().\n    option: The option whose value to extract, with the leading '-'.\n\n  Returns:\n    A list of values, either directly following the option,\n    (eg., -opt val1 val2) or values collected from multiple occurrences of\n    the option (eg., -opt val1 -opt val2).\n  \"\"\"\n\n  parser = ArgumentParser()\n  parser.add_argument(option, nargs='*', action='append')\n  option = option.lstrip('-').replace('-', '_')\n  args, _ = parser.parse_known_args(argv)\n  if not args or not vars(args)[option]:\n    return []\n  else:\n    return sum(vars(args)[option], [])\n\n\ndef GetHostCompilerOptions(argv):\n  \"\"\"Collect the -isystem, -iquote, and --sysroot option values from argv.\n\n  Args:\n    argv: A list of strings, possibly the argv passed to main().\n\n  Returns:\n    The string that can be used as the --compiler-options to nvcc.\n  \"\"\"\n\n  parser = ArgumentParser()\n  parser.add_argument('-isystem', nargs='*', action='append')\n  parser.add_argument('-iquote', nargs='*', action='append')\n  parser.add_argument('--sysroot', nargs=1)\n  parser.add_argument('-g', nargs='*', action='append')\n  parser.add_argument('-fno-canonical-system-headers', action='store_true')\n  parser.add_argument('-no-canonical-prefixes', action='store_true')\n\n  args, _ = parser.parse_known_args(argv)\n\n  opts = ''\n\n  if args.isystem:\n    opts += ' -isystem ' + ' -isystem '.join(sum(args.isystem, []))\n  if args.iquote:\n    opts += ' -iquote ' + ' -iquote '.join(sum(args.iquote, []))\n  if args.g:\n    opts += ' -g' + ' -g'.join(sum(args.g, []))\n  if args.fno_canonical_system_headers:\n    opts += ' -fno-canonical-system-headers'\n  if args.no_canonical_prefixes:\n    opts += ' -no-canonical-prefixes'\n  if args.sysroot:\n    opts += ' --sysroot ' + args.sysroot[0]\n\n  return opts\n\ndef _update_options(nvcc_options):\n  if NVCC_VERSION in (\"7.0\",):\n    return nvcc_options\n\n  update_options = { \"relaxed-constexpr\" : \"expt-relaxed-constexpr\" }\n  return [ update_options[opt] if opt in update_options else opt\n                    for opt in nvcc_options ]\n\ndef GetNvccOptions(argv):\n  \"\"\"Collect the -nvcc_options values from argv.\n\n  Args:\n    argv: A list of strings, possibly the argv passed to main().\n\n  Returns:\n    The string that can be passed directly to nvcc.\n  \"\"\"\n\n  parser = ArgumentParser()\n  parser.add_argument('-nvcc_options', nargs='*', action='append')\n\n  args, _ = parser.parse_known_args(argv)\n\n  if args.nvcc_options:\n    options = _update_options(sum(args.nvcc_options, []))\n    return ' '.join(['--'+a for a in options])\n  return ''\n\ndef system(cmd):\n  \"\"\"Invokes cmd with os.system().\n\n  Args:\n    cmd: The command.\n\n  Returns:\n    The exit code if the process exited with exit() or -signal\n    if the process was terminated by a signal.\n  \"\"\"\n  retv = os.system(cmd)\n  if os.WIFEXITED(retv):\n    return os.WEXITSTATUS(retv)\n  else:\n    return -os.WTERMSIG(retv)\n\ndef InvokeNvcc(argv, log=False):\n  \"\"\"Call nvcc with arguments assembled from argv.\n\n  Args:\n    argv: A list of strings, possibly the argv passed to main().\n    log: True if logging is requested.\n\n  Returns:\n    The return value of calling system('nvcc ' + args)\n  \"\"\"\n\n  host_compiler_options = GetHostCompilerOptions(argv)\n  nvcc_compiler_options = GetNvccOptions(argv)\n  opt_option = GetOptionValue(argv, '-O')\n  m_options = GetOptionValue(argv, '-m')\n  m_options = ''.join([' -m' + m for m in m_options if m in ['32', '64']])\n  m_host_options = ''.join([' -m' + m for m in m_options if m not in ['32', '64']])\n  host_compiler_options = ' '.join([host_compiler_options, m_host_options])\n  include_options = GetOptionValue(argv, '-I')\n  out_file = GetOptionValue(argv, '-o')\n  depfiles = GetOptionValue(argv, '-MF')\n  defines = GetOptionValue(argv, '-D')\n  defines = ''.join([' -D' + define for define in defines])\n  undefines = GetOptionValue(argv, '-U')\n  undefines = ''.join([' -U' + define for define in undefines])\n  std_options = GetOptionValue(argv, '-std')\n  # Supported -std flags as of CUDA 9.0. Only keep last to mimic gcc/clang.\n  nvcc_allowed_std_options = [\"c++03\", \"c++11\", \"c++14\"]\n  nvcc_std_map = {}\n  if int(NVCC_VERSION.split('.')[0]) >= 11:\n      nvcc_std_map[\"c++1z\"] = \"c++17\"\n      nvcc_allowed_std_options += [\"c++17\", \"c++1z\"]\n  std_options = ''.join([' -std=' +\n      (nvcc_std_map[define] if define in nvcc_std_map else define)\n      for define in std_options if define in nvcc_allowed_std_options][-1:])\n  fatbin_options = ''.join([' --fatbin-options=' + option\n      for option in GetOptionValue(argv, '-Xcuda-fatbinary')])\n\n  # The list of source files get passed after the -c option. I don't know of\n  # any other reliable way to just get the list of source files to be compiled.\n  src_files = GetOptionValue(argv, '-c')\n\n  # Pass -w through from host to nvcc, but don't do anything fancier with\n  # warnings-related flags, since they're not necessarily the same across\n  # compilers.\n  warning_options = ' -w' if '-w' in argv else ''\n\n  if len(src_files) == 0:\n    return 1\n  if len(out_file) != 1:\n    return 1\n\n  opt = (' -O2' if (len(opt_option) > 0 and int(opt_option[0]) > 0)\n         else ' -g')\n\n  includes = (' -I ' + ' -I '.join(include_options)\n              if len(include_options) > 0\n              else '')\n\n  # Unfortunately, there are other options that have -c prefix too.\n  # So allowing only those look like C/C++ files.\n  src_files = [f for f in src_files if\n               re.search('\\.cpp$|\\.cc$|\\.c$|\\.cxx$|\\.C$', f)]\n  srcs = ' '.join(src_files)\n  out = ' -o ' + out_file[0]\n\n  nvccopts = '-D_FORCE_INLINES '\n  capabilities_sm = set(GetOptionValue(argv, \"--cuda-gpu-arch\"))\n  capabilities_compute = set(GetOptionValue(argv, '--cuda-include-ptx'))\n  # When both \"code=sm_xy\" and \"code=compute_xy\" are requested for a single\n  # arch, they can be combined using \"code=xy,compute_xy\" which avoids a\n  # redundant PTX generation during compilation.\n  capabilities_both = capabilities_sm.intersection(capabilities_compute)\n  for capability in capabilities_both:\n    capability = capability[len('sm_'):]\n    nvccopts += r'-gencode=arch=compute_%s,code=\\\"sm_%s,compute_%s\\\" ' % (\n        capability, capability, capability)\n  for capability in capabilities_sm - capabilities_both:\n    capability = capability[len('sm_'):]\n    nvccopts += r'-gencode=arch=compute_%s,\\\"code=sm_%s\\\" ' % (capability,\n                                                               capability)\n  for capability in capabilities_compute - capabilities_both:\n    capability = capability[len('sm_'):]\n    nvccopts += r'-gencode=arch=compute_%s,\\\"code=compute_%s\\\" ' % (capability,\n                                                                    capability)\n  nvccopts += nvcc_compiler_options\n  nvccopts += undefines\n  nvccopts += defines\n  nvccopts += std_options\n  nvccopts += m_options\n  nvccopts += warning_options\n  # Force C++17 dialect (note, everything in just one string!)\n  nvccopts += ' --std c++17 '\n  nvccopts += fatbin_options\n\n  if depfiles:\n    # Generate the dependency file\n    depfile = depfiles[0]\n    cmd = (NVCC_PATH + ' ' + nvccopts +\n           ' --compiler-options \"' + host_compiler_options + '\"' +\n           ' --compiler-bindir=' + GCC_HOST_COMPILER_PATH +\n           ' -I .' +\n           ' -x cu ' + opt + includes + ' ' + srcs + ' -M -o ' + depfile)\n    if log: Log(cmd)\n    exit_status = system(cmd)\n    if exit_status != 0:\n      return exit_status\n\n  cmd = (NVCC_PATH + ' ' + nvccopts +\n         ' --compiler-options \"' + host_compiler_options + ' -fPIC\"' +\n         ' --compiler-bindir=' + GCC_HOST_COMPILER_PATH +\n         ' -I .' +\n         ' -x cu ' + opt + includes + ' -c ' + srcs + out)\n\n  # TODO(zhengxq): for some reason, 'gcc' needs this help to find 'as'.\n  # Need to investigate and fix.\n  cmd = 'PATH=' + PREFIX_DIR + ':$PATH ' + cmd\n  if log: Log(cmd)\n  return system(cmd)\n\n\ndef main():\n  parser = ArgumentParser()\n  parser.add_argument('-x', nargs=1)\n  parser.add_argument('--cuda_log', action='store_true')\n  args, leftover = parser.parse_known_args(sys.argv[1:])\n\n  if args.x and args.x[0] == 'cuda':\n    if args.cuda_log: Log('-x cuda')\n    leftover = [pipes.quote(s) for s in leftover]\n    if args.cuda_log: Log('using nvcc')\n    return InvokeNvcc(leftover, log=args.cuda_log)\n\n  # Strip our flags before passing through to the CPU compiler for files which\n  # are not -x cuda. We can't just pass 'leftover' because it also strips -x.\n  # We not only want to pass -x to the CPU compiler, but also keep it in its\n  # relative location in the argv list (the compiler is actually sensitive to\n  # this).\n  cpu_compiler_flags = [flag for flag in sys.argv[1:]\n                             if not flag.startswith(('--cuda_log'))]\n\n  return subprocess.call([CPU_COMPILER] + cpu_compiler_flags)\n\nif __name__ == '__main__':\n  sys.exit(main())\n"
  },
  {
    "path": "third_party/gpus/crosstool/clang/bin/crosstool_wrapper_driver_rocm.tpl",
    "content": "#!/usr/bin/env python\n\"\"\"Crosstool wrapper for compiling ROCm programs.\n\nSYNOPSIS:\n  crosstool_wrapper_driver_rocm [options passed in by cc_library()\n                                or cc_binary() rule]\n\nDESCRIPTION:\n  This script is expected to be called by the cc_library() or cc_binary() bazel\n  rules. When the option \"-x rocm\" is present in the list of arguments passed\n  to this script, it invokes the hipcc compiler. Most arguments are passed\n  as is as a string to --compiler-options of hipcc. When \"-x rocm\" is not\n  present, this wrapper invokes gcc with the input arguments as is.\n\"\"\"\n\n__author__ = 'whchung@gmail.com (Wen-Heng (Jack) Chung)'\n\nfrom argparse import ArgumentParser\nimport os\nimport subprocess\nimport re\nimport sys\nimport pipes\n\n# Template values set by rocm_configure.bzl.\nCPU_COMPILER = ('%{cpu_compiler}')\n\nHIPCC_PATH = '%{hipcc_path}'\nHIPCC_ENV = '%{hipcc_env}'\nHIP_RUNTIME_PATH = '%{hip_runtime_path}'\nHIP_RUNTIME_LIBRARY = '%{hip_runtime_library}'\nROCR_RUNTIME_PATH = '%{rocr_runtime_path}'\nROCR_RUNTIME_LIBRARY = '%{rocr_runtime_library}'\nVERBOSE = '%{crosstool_verbose}'=='1'\n\ndef Log(s):\n  print('gpus/crosstool: {0}'.format(s))\n\n\ndef GetOptionValue(argv, option):\n  \"\"\"Extract the list of values for option from the argv list.\n\n  Args:\n    argv: A list of strings, possibly the argv passed to main().\n    option: The option whose value to extract, without the leading '-'.\n\n  Returns:\n    A list of values, either directly following the option,\n    (eg., -opt val1 val2) or values collected from multiple occurrences of\n    the option (eg., -opt val1 -opt val2).\n  \"\"\"\n\n  parser = ArgumentParser()\n  parser.add_argument('-' + option, nargs='*', action='append')\n  args, _ = parser.parse_known_args(argv)\n  if not args or not vars(args)[option]:\n    return []\n  else:\n    return sum(vars(args)[option], [])\n\n\ndef GetHostCompilerOptions(argv):\n  \"\"\"Collect the -isystem, -iquote, and --sysroot option values from argv.\n\n  Args:\n    argv: A list of strings, possibly the argv passed to main().\n\n  Returns:\n    The string that can be used as the --compiler-options to hipcc.\n  \"\"\"\n\n  parser = ArgumentParser()\n  parser.add_argument('-isystem', nargs='*', action='append')\n  parser.add_argument('-iquote', nargs='*', action='append')\n  parser.add_argument('--sysroot', nargs=1)\n  parser.add_argument('-g', nargs='*', action='append')\n  parser.add_argument('-fno-canonical-system-headers', action='store_true')\n\n  args, _ = parser.parse_known_args(argv)\n\n  opts = ''\n\n  if args.isystem:\n    opts += ' -isystem ' + ' -isystem '.join(sum(args.isystem, []))\n  if args.iquote:\n    opts += ' -iquote ' + ' -iquote '.join(sum(args.iquote, []))\n  if args.g:\n    opts += ' -g' + ' -g'.join(sum(args.g, []))\n  #if args.fno_canonical_system_headers:\n  #  opts += ' -fno-canonical-system-headers'\n  if args.sysroot:\n    opts += ' --sysroot ' + args.sysroot[0]\n\n  return opts\n\ndef system(cmd):\n  \"\"\"Invokes cmd with os.system().\n\n  Args:\n    cmd: The command.\n\n  Returns:\n    The exit code if the process exited with exit() or -signal\n    if the process was terminated by a signal.\n  \"\"\"\n  retv = os.system(cmd)\n  if os.WIFEXITED(retv):\n    return os.WEXITSTATUS(retv)\n  else:\n    return -os.WTERMSIG(retv)\n\n\ndef InvokeHipcc(argv, log=False):\n  \"\"\"Call hipcc with arguments assembled from argv.\n\n  Args:\n    argv: A list of strings, possibly the argv passed to main().\n    log: True if logging is requested.\n\n  Returns:\n    The return value of calling os.system('hipcc ' + args)\n  \"\"\"\n\n  host_compiler_options = GetHostCompilerOptions(argv)\n  opt_option = GetOptionValue(argv, 'O')\n  m_options = GetOptionValue(argv, 'm')\n  m_options = ''.join([' -m' + m for m in m_options if m in ['32', '64']])\n  include_options = GetOptionValue(argv, 'I')\n  out_file = GetOptionValue(argv, 'o')\n  depfiles = GetOptionValue(argv, 'MF')\n  defines = GetOptionValue(argv, 'D')\n  defines = ''.join([' -D' + define for define in defines])\n  undefines = GetOptionValue(argv, 'U')\n  undefines = ''.join([' -U' + define for define in undefines])\n  std_options = GetOptionValue(argv, 'std')\n  hipcc_allowed_std_options = [\"c++11\", \"c++14\", \"c++17\"]\n  std_options = ''.join([' -std=' + define\n      for define in std_options if define in hipcc_allowed_std_options])\n\n  # The list of source files get passed after the -c option. I don't know of\n  # any other reliable way to just get the list of source files to be compiled.\n  src_files = GetOptionValue(argv, 'c')\n\n  if len(src_files) == 0:\n    return 1\n  if len(out_file) != 1:\n    return 1\n\n  opt = (' -O2' if (len(opt_option) > 0 and int(opt_option[0]) > 0)\n         else ' -g')\n\n  includes = (' -I ' + ' -I '.join(include_options)\n              if len(include_options) > 0\n              else '')\n\n  # Unfortunately, there are other options that have -c prefix too.\n  # So allowing only those look like C/C++ files.\n  src_files = [f for f in src_files if\n               re.search('\\.cpp$|\\.cc$|\\.c$|\\.cxx$|\\.C$', f)]\n  srcs = ' '.join(src_files)\n  out = ' -o ' + out_file[0]\n\n  hipccopts = ' '\n  # In hip-clang environment, we need to make sure that hip header is included\n  # before some standard math header like <complex> is included in any source.\n  # Otherwise, we get build error.\n  # Also we need to retain warning about uninitialised shared variable as\n  # warning only, even when -Werror option is specified.\n  hipccopts += ' --include=hip/hip_runtime.h '\n  # Force C++17 dialect (note, everything in just one string!)\n  hipccopts += ' --std=c++17 '\n  # Use -fno-gpu-rdc by default for early GPU kernel finalization\n  # This flag would trigger GPU kernels be generated at compile time, instead\n  # of link time. This allows the default host compiler (gcc) be used as the\n  # linker for TensorFlow on ROCm platform.\n  hipccopts += ' -fno-gpu-rdc '\n  hipccopts += ' -fcuda-flush-denormals-to-zero '\n  hipccopts += undefines\n  hipccopts += defines\n  hipccopts += std_options\n  hipccopts += m_options\n\n  if depfiles:\n    # Generate the dependency file\n    depfile = depfiles[0]\n    cmd = (HIPCC_PATH + ' ' + hipccopts +\n           host_compiler_options +\n           ' -I .' + includes + ' ' + srcs + ' -M -o ' + depfile)\n    cmd = HIPCC_ENV.replace(';', ' ') + ' ' + cmd\n    if log: Log(cmd)\n    if VERBOSE: print(cmd)\n    exit_status = os.system(cmd)\n    if exit_status != 0:\n      return exit_status\n\n  cmd = (HIPCC_PATH + ' ' + hipccopts +\n         host_compiler_options + ' -fPIC' +\n         ' -I .' + opt + includes + ' -c ' + srcs + out)\n\n  cmd = HIPCC_ENV.replace(';', ' ') + ' '\\\n        + cmd\n  if log: Log(cmd)\n  if VERBOSE: print(cmd)\n  return system(cmd)\n\n\ndef main():\n  # ignore PWD env var\n  os.environ['PWD']=''\n\n  parser = ArgumentParser(fromfile_prefix_chars='@')\n  parser.add_argument('-x', nargs=1)\n  parser.add_argument('--rocm_log', action='store_true')\n  parser.add_argument('-pass-exit-codes', action='store_true')\n  args, leftover = parser.parse_known_args(sys.argv[1:])\n\n  if VERBOSE: print('PWD=' + os.getcwd())\n  if VERBOSE: print('HIPCC_ENV=' + HIPCC_ENV)\n\n  if args.x and args.x[0] == 'rocm':\n    # compilation for GPU objects\n    if args.rocm_log: Log('-x rocm')\n    leftover = [pipes.quote(s) for s in leftover]\n    if args.rocm_log: Log('using hipcc')\n    return InvokeHipcc(leftover, log=args.rocm_log)\n\n  elif args.pass_exit_codes:\n    # link\n    # with hipcc compiler invoked with -fno-gpu-rdc by default now, it's ok to \n    # use host compiler as linker, but we have to link with HCC/HIP runtime.\n    # Such restriction would be revised further as the bazel script get\n    # improved to fine tune dependencies to ROCm libraries.\n    gpu_linker_flags = [flag for flag in sys.argv[1:]\n                               if not flag.startswith(('--rocm_log'))]\n\n    gpu_linker_flags.append('-L' + ROCR_RUNTIME_PATH)\n    gpu_linker_flags.append('-Wl,-rpath=' + ROCR_RUNTIME_PATH)\n    gpu_linker_flags.append('-l' + ROCR_RUNTIME_LIBRARY)\n    gpu_linker_flags.append('-L' + HIP_RUNTIME_PATH)\n    gpu_linker_flags.append('-Wl,-rpath=' + HIP_RUNTIME_PATH)\n    gpu_linker_flags.append('-l' + HIP_RUNTIME_LIBRARY)\n    gpu_linker_flags.append(\"-lrt\")\n    gpu_linker_flags.append(\"-lstdc++\")\n\n    if VERBOSE: print(' '.join([CPU_COMPILER] + gpu_linker_flags))\n    return subprocess.call([CPU_COMPILER] + gpu_linker_flags)\n\n  else:\n    # compilation for host objects\n\n    # Strip our flags before passing through to the CPU compiler for files which\n    # are not -x rocm. We can't just pass 'leftover' because it also strips -x.\n    # We not only want to pass -x to the CPU compiler, but also keep it in its\n    # relative location in the argv list (the compiler is actually sensitive to\n    # this).\n    cpu_compiler_flags = [flag for flag in sys.argv[1:]\n                               if not flag.startswith(('--rocm_log'))]\n\n    # XXX: SE codes need to be built with gcc, but need this macro defined\n    cpu_compiler_flags.append(\"-D__HIP_PLATFORM_HCC__\")\n    if VERBOSE: print(' '.join([CPU_COMPILER] + cpu_compiler_flags))\n    return subprocess.call([CPU_COMPILER] + cpu_compiler_flags)\n\nif __name__ == '__main__':\n  sys.exit(main())\n"
  },
  {
    "path": "third_party/gpus/crosstool/hipcc_cc_toolchain_config.bzl.tpl",
    "content": "\"\"\"cc_toolchain_config rule for configuring ROCm toolchain on Linux.\"\"\"\n\nload(\n    \"@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl\",\n    \"feature\",\n    \"feature_set\",\n    \"flag_group\",\n    \"flag_set\",\n    \"tool_path\",\n    \"variable_with_value\",\n    \"with_feature_set\",\n)\nload(\"@bazel_tools//tools/build_defs/cc:action_names.bzl\", \"ACTION_NAMES\")\n\nall_compile_actions = [\n    ACTION_NAMES.c_compile,\n    ACTION_NAMES.cpp_compile,\n    ACTION_NAMES.linkstamp_compile,\n    ACTION_NAMES.assemble,\n    ACTION_NAMES.preprocess_assemble,\n    ACTION_NAMES.cpp_header_parsing,\n    ACTION_NAMES.cpp_module_compile,\n    ACTION_NAMES.cpp_module_codegen,\n    ACTION_NAMES.clif_match,\n    ACTION_NAMES.lto_backend,\n]\n\nall_cpp_compile_actions = [\n    ACTION_NAMES.cpp_compile,\n    ACTION_NAMES.linkstamp_compile,\n    ACTION_NAMES.cpp_header_parsing,\n    ACTION_NAMES.cpp_module_compile,\n    ACTION_NAMES.cpp_module_codegen,\n    ACTION_NAMES.clif_match,\n]\n\npreprocessor_compile_actions = [\n    ACTION_NAMES.c_compile,\n    ACTION_NAMES.cpp_compile,\n    ACTION_NAMES.linkstamp_compile,\n    ACTION_NAMES.preprocess_assemble,\n    ACTION_NAMES.cpp_header_parsing,\n    ACTION_NAMES.cpp_module_compile,\n    ACTION_NAMES.clif_match,\n]\n\ncodegen_compile_actions = [\n    ACTION_NAMES.c_compile,\n    ACTION_NAMES.cpp_compile,\n    ACTION_NAMES.linkstamp_compile,\n    ACTION_NAMES.assemble,\n    ACTION_NAMES.preprocess_assemble,\n    ACTION_NAMES.cpp_module_codegen,\n    ACTION_NAMES.lto_backend,\n]\n\nall_link_actions = [\n    ACTION_NAMES.cpp_link_executable,\n    ACTION_NAMES.cpp_link_dynamic_library,\n    ACTION_NAMES.cpp_link_nodeps_dynamic_library,\n]\n\nlto_index_actions = [\n    ACTION_NAMES.lto_index_for_executable,\n    ACTION_NAMES.lto_index_for_dynamic_library,\n    ACTION_NAMES.lto_index_for_nodeps_dynamic_library,\n]\n\ndef _impl(ctx):\n    tool_paths = [\n        tool_path(name = \"gcc\", path = ctx.attr.host_compiler_path),\n        tool_path(name = \"ar\", path = ctx.attr.host_compiler_prefix + \"/ar\"),\n        tool_path(name = \"compat-ld\", path = ctx.attr.host_compiler_prefix + \"/ld\"),\n        tool_path(name = \"cpp\", path = ctx.attr.host_compiler_prefix + \"/cpp\"),\n        tool_path(name = \"dwp\", path = ctx.attr.host_compiler_prefix + \"/dwp\"),\n        tool_path(name = \"gcov\", path = ctx.attr.host_compiler_prefix + \"/gcov\"),\n        tool_path(name = \"ld\", path = ctx.attr.host_compiler_prefix + \"/ld\"),\n        tool_path(name = \"nm\", path = ctx.attr.host_compiler_prefix + \"/nm\"),\n        tool_path(name = \"objcopy\", path = ctx.attr.host_compiler_prefix + \"/objcopy\"),\n        tool_path(name = \"objdump\", path = ctx.attr.host_compiler_prefix + \"/objdump\"),\n        tool_path(name = \"strip\", path = ctx.attr.host_compiler_prefix + \"/strip\"),\n    ]\n\n    action_configs = []\n\n    supports_pic_feature = feature(\n        name = \"supports_pic\",\n        enabled = True,\n    )\n    supports_start_end_lib_feature = feature(\n        name = \"supports_start_end_lib\",\n        enabled = True,\n    )\n\n    default_compile_flags_feature = feature(\n        name = \"default_compile_flags\",\n        enabled = True,\n        flag_sets = [\n            flag_set(\n                actions = all_compile_actions,\n                flag_groups = ([\n                    flag_group(\n                        flags = ctx.attr.compile_flags,\n                    ),\n                ] if ctx.attr.compile_flags else []),\n            ),\n            flag_set(\n                actions = all_compile_actions,\n                flag_groups = ([\n                    flag_group(\n                        flags = ctx.attr.dbg_compile_flags,\n                    ),\n                ] if ctx.attr.dbg_compile_flags else []),\n                with_features = [with_feature_set(features = [\"dbg\"])],\n            ),\n            flag_set(\n                actions = all_compile_actions,\n                flag_groups = ([\n                    flag_group(\n                        flags = ctx.attr.opt_compile_flags,\n                    ),\n                ] if ctx.attr.opt_compile_flags else []),\n                with_features = [with_feature_set(features = [\"opt\"])],\n            ),\n            flag_set(\n                actions = all_cpp_compile_actions + [ACTION_NAMES.lto_backend],\n                flag_groups = ([\n                    flag_group(\n                        flags = ctx.attr.cxx_flags,\n                    ),\n                ] if ctx.attr.cxx_flags else []),\n            ),\n        ],\n    )\n\n    default_link_flags_feature = feature(\n        name = \"default_link_flags\",\n        enabled = True,\n        flag_sets = [\n            flag_set(\n                actions = all_link_actions + lto_index_actions,\n                flag_groups = ([\n                    flag_group(\n                        flags = ctx.attr.link_flags,\n                    ),\n                ] if ctx.attr.link_flags else []),\n            ),\n            flag_set(\n                actions = all_link_actions + lto_index_actions,\n                flag_groups = ([\n                    flag_group(\n                        flags = ctx.attr.opt_link_flags,\n                    ),\n                ] if ctx.attr.opt_link_flags else []),\n                with_features = [with_feature_set(features = [\"opt\"])],\n            ),\n        ],\n    )\n\n    dbg_feature = feature(name = \"dbg\")\n\n    opt_feature = feature(name = \"opt\")\n\n    sysroot_feature = feature(\n        name = \"sysroot\",\n        enabled = True,\n        flag_sets = [\n            flag_set(\n                actions = [\n                    ACTION_NAMES.preprocess_assemble,\n                    ACTION_NAMES.linkstamp_compile,\n                    ACTION_NAMES.c_compile,\n                    ACTION_NAMES.cpp_compile,\n                    ACTION_NAMES.cpp_header_parsing,\n                    ACTION_NAMES.cpp_module_compile,\n                    ACTION_NAMES.cpp_module_codegen,\n                    ACTION_NAMES.lto_backend,\n                    ACTION_NAMES.clif_match,\n                ] + all_link_actions + lto_index_actions,\n                flag_groups = [\n                    flag_group(\n                        flags = [\"--sysroot=%{sysroot}\"],\n                        expand_if_available = \"sysroot\",\n                    ),\n                ],\n            ),\n        ],\n    )\n\n    fdo_optimize_feature = feature(\n        name = \"fdo_optimize\",\n        flag_sets = [\n            flag_set(\n                actions = [ACTION_NAMES.c_compile, ACTION_NAMES.cpp_compile],\n                flag_groups = [\n                    flag_group(\n                        flags = [\n                            \"-fprofile-use=%{fdo_profile_path}\",\n                            \"-fprofile-correction\",\n                        ],\n                        expand_if_available = \"fdo_profile_path\",\n                    ),\n                ],\n            ),\n        ],\n        provides = [\"profile\"],\n    )\n\n    supports_dynamic_linker_feature = feature(name = \"supports_dynamic_linker\", enabled = True)\n\n    user_compile_flags_feature = feature(\n        name = \"user_compile_flags\",\n        enabled = True,\n        flag_sets = [\n            flag_set(\n                actions = all_compile_actions,\n                flag_groups = [\n                    flag_group(\n                        flags = [\"%{user_compile_flags}\"],\n                        iterate_over = \"user_compile_flags\",\n                        expand_if_available = \"user_compile_flags\",\n                    ),\n                ],\n            ),\n        ],\n    )\n\n    unfiltered_compile_flags_feature = feature(\n        name = \"unfiltered_compile_flags\",\n        enabled = True,\n        flag_sets = [\n            flag_set(\n                actions = all_compile_actions,\n                flag_groups = ([\n                    flag_group(\n                        flags = ctx.attr.unfiltered_compile_flags,\n                    ),\n                ] if ctx.attr.unfiltered_compile_flags else []),\n            ),\n        ],\n    )\n\n    library_search_directories_feature = feature(\n        name = \"library_search_directories\",\n        flag_sets = [\n            flag_set(\n                actions = all_link_actions + lto_index_actions,\n                flag_groups = [\n                    flag_group(\n                        flags = [\"-L%{library_search_directories}\"],\n                        iterate_over = \"library_search_directories\",\n                        expand_if_available = \"library_search_directories\",\n                    ),\n                ],\n            ),\n        ],\n    )\n\n    static_libgcc_feature = feature(\n        name = \"static_libgcc\",\n        enabled = True,\n        flag_sets = [\n            flag_set(\n                actions = [\n                    ACTION_NAMES.cpp_link_executable,\n                    ACTION_NAMES.cpp_link_dynamic_library,\n                    ACTION_NAMES.lto_index_for_executable,\n                    ACTION_NAMES.lto_index_for_dynamic_library,\n                ],\n                flag_groups = [flag_group(flags = [\"-static-libgcc\"])],\n                with_features = [\n                    with_feature_set(features = [\"static_link_cpp_runtimes\"]),\n                ],\n            ),\n        ],\n    )\n\n    pic_feature = feature(\n        name = \"pic\",\n        enabled = True,\n        flag_sets = [\n            flag_set(\n                actions = [\n                    ACTION_NAMES.assemble,\n                    ACTION_NAMES.preprocess_assemble,\n                    ACTION_NAMES.linkstamp_compile,\n                    ACTION_NAMES.c_compile,\n                    ACTION_NAMES.cpp_compile,\n                    ACTION_NAMES.cpp_module_codegen,\n                    ACTION_NAMES.cpp_module_compile,\n                ],\n                flag_groups = [\n                    flag_group(flags = [\"-fPIC\"], expand_if_available = \"pic\"),\n                ],\n            ),\n        ],\n    )\n\n    per_object_debug_info_feature = feature(\n        name = \"per_object_debug_info\",\n        flag_sets = [\n            flag_set(\n                actions = [\n                    ACTION_NAMES.assemble,\n                    ACTION_NAMES.preprocess_assemble,\n                    ACTION_NAMES.c_compile,\n                    ACTION_NAMES.cpp_compile,\n                    ACTION_NAMES.cpp_module_codegen,\n                ],\n                flag_groups = [\n                    flag_group(\n                        flags = [\"-gsplit-dwarf\"],\n                        expand_if_available = \"per_object_debug_info_file\",\n                    ),\n                ],\n            ),\n        ],\n    )\n\n    preprocessor_defines_feature = feature(\n        name = \"preprocessor_defines\",\n        enabled = True,\n        flag_sets = [\n            flag_set(\n                actions = [\n                    ACTION_NAMES.preprocess_assemble,\n                    ACTION_NAMES.linkstamp_compile,\n                    ACTION_NAMES.c_compile,\n                    ACTION_NAMES.cpp_compile,\n                    ACTION_NAMES.cpp_header_parsing,\n                    ACTION_NAMES.cpp_module_compile,\n                    ACTION_NAMES.clif_match,\n                ],\n                flag_groups = [\n                    flag_group(\n                        flags = [\"-D%{preprocessor_defines}\"],\n                        iterate_over = \"preprocessor_defines\",\n                    ),\n                ],\n            ),\n        ],\n    )\n\n    cs_fdo_optimize_feature = feature(\n        name = \"cs_fdo_optimize\",\n        flag_sets = [\n            flag_set(\n                actions = [ACTION_NAMES.lto_backend],\n                flag_groups = [\n                    flag_group(\n                        flags = [\n                            \"-fprofile-use=%{fdo_profile_path}\",\n                            \"-Xclang-only=-Wno-profile-instr-unprofiled\",\n                            \"-Xclang-only=-Wno-profile-instr-out-of-date\",\n                            \"-fprofile-correction\",\n                        ],\n                        expand_if_available = \"fdo_profile_path\",\n                    ),\n                ],\n            ),\n        ],\n        provides = [\"csprofile\"],\n    )\n\n    autofdo_feature = feature(\n        name = \"autofdo\",\n        flag_sets = [\n            flag_set(\n                actions = [ACTION_NAMES.c_compile, ACTION_NAMES.cpp_compile],\n                flag_groups = [\n                    flag_group(\n                        flags = [\n                            \"-fauto-profile=%{fdo_profile_path}\",\n                            \"-fprofile-correction\",\n                        ],\n                        expand_if_available = \"fdo_profile_path\",\n                    ),\n                ],\n            ),\n        ],\n        provides = [\"profile\"],\n    )\n\n    runtime_library_search_directories_feature = feature(\n        name = \"runtime_library_search_directories\",\n        flag_sets = [\n            flag_set(\n                actions = all_link_actions + lto_index_actions,\n                flag_groups = [\n                    flag_group(\n                        iterate_over = \"runtime_library_search_directories\",\n                        flag_groups = [\n                            flag_group(\n                                flags = [\n                                    \"-Wl,-rpath,$EXEC_ORIGIN/%{runtime_library_search_directories}\",\n                                ],\n                                expand_if_true = \"is_cc_test\",\n                            ),\n                            flag_group(\n                                flags = [\n                                    \"-Wl,-rpath,$ORIGIN/%{runtime_library_search_directories}\",\n                                ],\n                                expand_if_false = \"is_cc_test\",\n                            ),\n                        ],\n                        expand_if_available =\n                            \"runtime_library_search_directories\",\n                    ),\n                ],\n                with_features = [\n                    with_feature_set(features = [\"static_link_cpp_runtimes\"]),\n                ],\n            ),\n            flag_set(\n                actions = all_link_actions + lto_index_actions,\n                flag_groups = [\n                    flag_group(\n                        iterate_over = \"runtime_library_search_directories\",\n                        flag_groups = [\n                            flag_group(\n                                flags = [\n                                    \"-Wl,-rpath,$ORIGIN/%{runtime_library_search_directories}\",\n                                ],\n                            ),\n                        ],\n                        expand_if_available =\n                            \"runtime_library_search_directories\",\n                    ),\n                ],\n                with_features = [\n                    with_feature_set(\n                        not_features = [\"static_link_cpp_runtimes\"],\n                    ),\n                ],\n            ),\n        ],\n    )\n\n    fission_support_feature = feature(\n        name = \"fission_support\",\n        flag_sets = [\n            flag_set(\n                actions = all_link_actions + lto_index_actions,\n                flag_groups = [\n                    flag_group(\n                        flags = [\"-Wl,--gdb-index\"],\n                        expand_if_available = \"is_using_fission\",\n                    ),\n                ],\n            ),\n        ],\n    )\n\n    shared_flag_feature = feature(\n        name = \"shared_flag\",\n        flag_sets = [\n            flag_set(\n                actions = [\n                    ACTION_NAMES.cpp_link_dynamic_library,\n                    ACTION_NAMES.cpp_link_nodeps_dynamic_library,\n                    ACTION_NAMES.lto_index_for_dynamic_library,\n                    ACTION_NAMES.lto_index_for_nodeps_dynamic_library,\n                ],\n                flag_groups = [flag_group(flags = [\"-shared\"])],\n            ),\n        ],\n    )\n\n    random_seed_feature = feature(\n        name = \"random_seed\",\n        enabled = True,\n        flag_sets = [\n            flag_set(\n                actions = [\n                    ACTION_NAMES.c_compile,\n                    ACTION_NAMES.cpp_compile,\n                    ACTION_NAMES.cpp_module_codegen,\n                    ACTION_NAMES.cpp_module_compile,\n                ],\n                flag_groups = [\n                    flag_group(\n                        flags = [\"-frandom-seed=%{output_file}\"],\n                        expand_if_available = \"output_file\",\n                    ),\n                ],\n            ),\n        ],\n    )\n\n    includes_feature = feature(\n        name = \"includes\",\n        enabled = True,\n        flag_sets = [\n            flag_set(\n                actions = [\n                    ACTION_NAMES.preprocess_assemble,\n                    ACTION_NAMES.linkstamp_compile,\n                    ACTION_NAMES.c_compile,\n                    ACTION_NAMES.cpp_compile,\n                    ACTION_NAMES.cpp_header_parsing,\n                    ACTION_NAMES.cpp_module_compile,\n                    ACTION_NAMES.clif_match,\n                    ACTION_NAMES.objc_compile,\n                    ACTION_NAMES.objcpp_compile,\n                ],\n                flag_groups = [\n                    flag_group(\n                        flags = [\"-include\", \"%{includes}\"],\n                        iterate_over = \"includes\",\n                        expand_if_available = \"includes\",\n                    ),\n                ],\n            ),\n        ],\n    )\n\n    fdo_instrument_feature = feature(\n        name = \"fdo_instrument\",\n        flag_sets = [\n            flag_set(\n                actions = [\n                    ACTION_NAMES.c_compile,\n                    ACTION_NAMES.cpp_compile,\n                ] + all_link_actions + lto_index_actions,\n                flag_groups = [\n                    flag_group(\n                        flags = [\n                            \"-fprofile-generate=%{fdo_instrument_path}\",\n                            \"-fno-data-sections\",\n                        ],\n                        expand_if_available = \"fdo_instrument_path\",\n                    ),\n                ],\n            ),\n        ],\n        provides = [\"profile\"],\n    )\n\n    cs_fdo_instrument_feature = feature(\n        name = \"cs_fdo_instrument\",\n        flag_sets = [\n            flag_set(\n                actions = [\n                    ACTION_NAMES.c_compile,\n                    ACTION_NAMES.cpp_compile,\n                    ACTION_NAMES.lto_backend,\n                ] + all_link_actions + lto_index_actions,\n                flag_groups = [\n                    flag_group(\n                        flags = [\n                            \"-fcs-profile-generate=%{cs_fdo_instrument_path}\",\n                        ],\n                        expand_if_available = \"cs_fdo_instrument_path\",\n                    ),\n                ],\n            ),\n        ],\n        provides = [\"csprofile\"],\n    )\n\n    include_paths_feature = feature(\n        name = \"include_paths\",\n        enabled = True,\n        flag_sets = [\n            flag_set(\n                actions = [\n                    ACTION_NAMES.preprocess_assemble,\n                    ACTION_NAMES.linkstamp_compile,\n                    ACTION_NAMES.c_compile,\n                    ACTION_NAMES.cpp_compile,\n                    ACTION_NAMES.cpp_header_parsing,\n                    ACTION_NAMES.cpp_module_compile,\n                    ACTION_NAMES.clif_match,\n                    ACTION_NAMES.objc_compile,\n                    ACTION_NAMES.objcpp_compile,\n                ],\n                flag_groups = [\n                    flag_group(\n                        flags = [\"-iquote\", \"%{quote_include_paths}\"],\n                        iterate_over = \"quote_include_paths\",\n                    ),\n                    flag_group(\n                        flags = [\"-I%{include_paths}\"],\n                        iterate_over = \"include_paths\",\n                    ),\n                    flag_group(\n                        flags = [\"-isystem\", \"%{system_include_paths}\"],\n                        iterate_over = \"system_include_paths\",\n                    ),\n                ],\n            ),\n        ],\n    )\n\n    symbol_counts_feature = feature(\n        name = \"symbol_counts\",\n        flag_sets = [\n            flag_set(\n                actions = all_link_actions + lto_index_actions,\n                flag_groups = [\n                    flag_group(\n                        flags = [\n                            \"-Wl,--print-symbol-counts=%{symbol_counts_output}\",\n                        ],\n                        expand_if_available = \"symbol_counts_output\",\n                    ),\n                ],\n            ),\n        ],\n    )\n\n    llvm_coverage_map_format_feature = feature(\n        name = \"llvm_coverage_map_format\",\n        flag_sets = [\n            flag_set(\n                actions = [\n                    ACTION_NAMES.preprocess_assemble,\n                    ACTION_NAMES.c_compile,\n                    ACTION_NAMES.cpp_compile,\n                    ACTION_NAMES.cpp_module_compile,\n                    ACTION_NAMES.objc_compile,\n                    ACTION_NAMES.objcpp_compile,\n                ],\n                flag_groups = [\n                    flag_group(\n                        flags = [\n                            \"-fprofile-instr-generate\",\n                            \"-fcoverage-mapping\",\n                        ],\n                    ),\n                ],\n            ),\n            flag_set(\n                actions = all_link_actions + lto_index_actions + [\n                    \"objc-executable\",\n                    \"objc++-executable\",\n                ],\n                flag_groups = [\n                    flag_group(flags = [\"-fprofile-instr-generate\"]),\n                ],\n            ),\n        ],\n        requires = [feature_set(features = [\"coverage\"])],\n        provides = [\"profile\"],\n    )\n\n    strip_debug_symbols_feature = feature(\n        name = \"strip_debug_symbols\",\n        flag_sets = [\n            flag_set(\n                actions = all_link_actions + lto_index_actions,\n                flag_groups = [\n                    flag_group(\n                        flags = [\"-Wl,-S\"],\n                        expand_if_available = \"strip_debug_symbols\",\n                    ),\n                ],\n            ),\n        ],\n    )\n\n    build_interface_libraries_feature = feature(\n        name = \"build_interface_libraries\",\n        flag_sets = [\n            flag_set(\n                actions = [\n                    ACTION_NAMES.cpp_link_dynamic_library,\n                    ACTION_NAMES.cpp_link_nodeps_dynamic_library,\n                    ACTION_NAMES.lto_index_for_dynamic_library,\n                    ACTION_NAMES.lto_index_for_nodeps_dynamic_library,\n                ],\n                flag_groups = [\n                    flag_group(\n                        flags = [\n                            \"%{generate_interface_library}\",\n                            \"%{interface_library_builder_path}\",\n                            \"%{interface_library_input_path}\",\n                            \"%{interface_library_output_path}\",\n                        ],\n                        expand_if_available = \"generate_interface_library\",\n                    ),\n                ],\n                with_features = [\n                    with_feature_set(\n                        features = [\"supports_interface_shared_libraries\"],\n                    ),\n                ],\n            ),\n        ],\n    )\n\n    libraries_to_link_feature = feature(\n        name = \"libraries_to_link\",\n        flag_sets = [\n            flag_set(\n                actions = all_link_actions + lto_index_actions,\n                flag_groups = [\n                    flag_group(\n                        iterate_over = \"libraries_to_link\",\n                        flag_groups = [\n                            flag_group(\n                                flags = [\"-Wl,--start-lib\"],\n                                expand_if_equal = variable_with_value(\n                                    name = \"libraries_to_link.type\",\n                                    value = \"object_file_group\",\n                                ),\n                            ),\n                            flag_group(\n                                flags = [\"-Wl,-whole-archive\"],\n                                expand_if_true =\n                                    \"libraries_to_link.is_whole_archive\",\n                            ),\n                            flag_group(\n                                flags = [\"%{libraries_to_link.object_files}\"],\n                                iterate_over = \"libraries_to_link.object_files\",\n                                expand_if_equal = variable_with_value(\n                                    name = \"libraries_to_link.type\",\n                                    value = \"object_file_group\",\n                                ),\n                            ),\n                            flag_group(\n                                flags = [\"%{libraries_to_link.name}\"],\n                                expand_if_equal = variable_with_value(\n                                    name = \"libraries_to_link.type\",\n                                    value = \"object_file\",\n                                ),\n                            ),\n                            flag_group(\n                                flags = [\"%{libraries_to_link.name}\"],\n                                expand_if_equal = variable_with_value(\n                                    name = \"libraries_to_link.type\",\n                                    value = \"interface_library\",\n                                ),\n                            ),\n                            flag_group(\n                                flags = [\"%{libraries_to_link.name}\"],\n                                expand_if_equal = variable_with_value(\n                                    name = \"libraries_to_link.type\",\n                                    value = \"static_library\",\n                                ),\n                            ),\n                            flag_group(\n                                flags = [\"-l%{libraries_to_link.name}\"],\n                                expand_if_equal = variable_with_value(\n                                    name = \"libraries_to_link.type\",\n                                    value = \"dynamic_library\",\n                                ),\n                            ),\n                            flag_group(\n                                flags = [\"-l:%{libraries_to_link.name}\"],\n                                expand_if_equal = variable_with_value(\n                                    name = \"libraries_to_link.type\",\n                                    value = \"versioned_dynamic_library\",\n                                ),\n                            ),\n                            flag_group(\n                                flags = [\"-Wl,-no-whole-archive\"],\n                                expand_if_true = \"libraries_to_link.is_whole_archive\",\n                            ),\n                            flag_group(\n                                flags = [\"-Wl,--end-lib\"],\n                                expand_if_equal = variable_with_value(\n                                    name = \"libraries_to_link.type\",\n                                    value = \"object_file_group\",\n                                ),\n                            ),\n                        ],\n                        expand_if_available = \"libraries_to_link\",\n                    ),\n                    flag_group(\n                        flags = [\"-Wl,@%{thinlto_param_file}\"],\n                        expand_if_true = \"thinlto_param_file\",\n                    ),\n                ],\n            ),\n        ],\n    )\n\n    user_link_flags_feature = feature(\n        name = \"user_link_flags\",\n        flag_sets = [\n            flag_set(\n                actions = all_link_actions + lto_index_actions,\n                flag_groups = [\n                    flag_group(\n                        flags = [\"%{user_link_flags}\"],\n                        iterate_over = \"user_link_flags\",\n                        expand_if_available = \"user_link_flags\",\n                    ),\n                ] + ([flag_group(flags = ctx.attr.link_libs)] if ctx.attr.link_libs else []),\n            ),\n        ],\n    )\n\n    fdo_prefetch_hints_feature = feature(\n        name = \"fdo_prefetch_hints\",\n        flag_sets = [\n            flag_set(\n                actions = [\n                    ACTION_NAMES.c_compile,\n                    ACTION_NAMES.cpp_compile,\n                    ACTION_NAMES.lto_backend,\n                ],\n                flag_groups = [\n                    flag_group(\n                        flags = [\n                            \"-Xclang-only=-mllvm\",\n                            \"-Xclang-only=-prefetch-hints-file=%{fdo_prefetch_hints_path}\",\n                        ],\n                        expand_if_available = \"fdo_prefetch_hints_path\",\n                    ),\n                ],\n            ),\n        ],\n    )\n\n    linkstamps_feature = feature(\n        name = \"linkstamps\",\n        flag_sets = [\n            flag_set(\n                actions = all_link_actions + lto_index_actions,\n                flag_groups = [\n                    flag_group(\n                        flags = [\"%{linkstamp_paths}\"],\n                        iterate_over = \"linkstamp_paths\",\n                        expand_if_available = \"linkstamp_paths\",\n                    ),\n                ],\n            ),\n        ],\n    )\n\n    gcc_coverage_map_format_feature = feature(\n        name = \"gcc_coverage_map_format\",\n        flag_sets = [\n            flag_set(\n                actions = [\n                    ACTION_NAMES.preprocess_assemble,\n                    ACTION_NAMES.c_compile,\n                    ACTION_NAMES.cpp_compile,\n                    ACTION_NAMES.cpp_module_compile,\n                    ACTION_NAMES.objc_compile,\n                    ACTION_NAMES.objcpp_compile,\n                    \"objc-executable\",\n                    \"objc++-executable\",\n                ],\n                flag_groups = [\n                    flag_group(\n                        flags = [\"-fprofile-arcs\", \"-ftest-coverage\"],\n                        expand_if_available = \"gcov_gcno_file\",\n                    ),\n                ],\n            ),\n            flag_set(\n                actions = all_link_actions + lto_index_actions,\n                flag_groups = [flag_group(flags = [\"--coverage\"])],\n            ),\n        ],\n        requires = [feature_set(features = [\"coverage\"])],\n        provides = [\"profile\"],\n    )\n\n    archiver_flags_feature = feature(\n        name = \"archiver_flags\",\n        flag_sets = [\n            flag_set(\n                actions = [ACTION_NAMES.cpp_link_static_library],\n                flag_groups = [\n                    flag_group(flags = [\"rcsD\"]),\n                    flag_group(\n                        flags = [\"%{output_execpath}\"],\n                        expand_if_available = \"output_execpath\",\n                    ),\n                ],\n            ),\n            flag_set(\n                actions = [ACTION_NAMES.cpp_link_static_library],\n                flag_groups = [\n                    flag_group(\n                        iterate_over = \"libraries_to_link\",\n                        flag_groups = [\n                            flag_group(\n                                flags = [\"%{libraries_to_link.name}\"],\n                                expand_if_equal = variable_with_value(\n                                    name = \"libraries_to_link.type\",\n                                    value = \"object_file\",\n                                ),\n                            ),\n                            flag_group(\n                                flags = [\"%{libraries_to_link.object_files}\"],\n                                iterate_over = \"libraries_to_link.object_files\",\n                                expand_if_equal = variable_with_value(\n                                    name = \"libraries_to_link.type\",\n                                    value = \"object_file_group\",\n                                ),\n                            ),\n                        ],\n                        expand_if_available = \"libraries_to_link\",\n                    ),\n                ],\n            ),\n        ],\n    )\n\n    force_pic_flags_feature = feature(\n        name = \"force_pic_flags\",\n        flag_sets = [\n            flag_set(\n                actions = [\n                    ACTION_NAMES.cpp_link_executable,\n                    ACTION_NAMES.lto_index_for_executable,\n                ],\n                flag_groups = [\n                    flag_group(\n                        flags = [\"-pie\"],\n                        expand_if_available = \"force_pic\",\n                    ),\n                ],\n            ),\n        ],\n    )\n\n    dependency_file_feature = feature(\n        name = \"dependency_file\",\n        enabled = True,\n        flag_sets = [\n            flag_set(\n                actions = [\n                    ACTION_NAMES.assemble,\n                    ACTION_NAMES.preprocess_assemble,\n                    ACTION_NAMES.c_compile,\n                    ACTION_NAMES.cpp_compile,\n                    ACTION_NAMES.cpp_module_compile,\n                    ACTION_NAMES.objc_compile,\n                    ACTION_NAMES.objcpp_compile,\n                    ACTION_NAMES.cpp_header_parsing,\n                    ACTION_NAMES.clif_match,\n                ],\n                flag_groups = [\n                    flag_group(\n                        flags = [\"-MD\", \"-MF\", \"%{dependency_file}\"],\n                        expand_if_available = \"dependency_file\",\n                    ),\n                ],\n            ),\n        ],\n    )\n\n    dynamic_library_linker_tool_path = tool_paths\n    dynamic_library_linker_tool_feature = feature(\n        name = \"dynamic_library_linker_tool\",\n        flag_sets = [\n            flag_set(\n                actions = [\n                    ACTION_NAMES.cpp_link_dynamic_library,\n                    ACTION_NAMES.cpp_link_nodeps_dynamic_library,\n                    ACTION_NAMES.lto_index_for_dynamic_library,\n                    ACTION_NAMES.lto_index_for_nodeps_dynamic_library,\n                ],\n                flag_groups = [\n                    flag_group(\n                        flags = [\" + cppLinkDynamicLibraryToolPath + \"],\n                        expand_if_available = \"generate_interface_library\",\n                    ),\n                ],\n                with_features = [\n                    with_feature_set(\n                        features = [\"supports_interface_shared_libraries\"],\n                    ),\n                ],\n            ),\n        ],\n    )\n\n    output_execpath_flags_feature = feature(\n        name = \"output_execpath_flags\",\n        flag_sets = [\n            flag_set(\n                actions = all_link_actions + lto_index_actions,\n                flag_groups = [\n                    flag_group(\n                        flags = [\"-o\", \"%{output_execpath}\"],\n                        expand_if_available = \"output_execpath\",\n                    ),\n                ],\n            ),\n        ],\n    )\n\n    # Note that we also set --coverage for c++-link-nodeps-dynamic-library. The\n    # generated code contains references to gcov symbols, and the dynamic linker\n    # can't resolve them unless the library is linked against gcov.\n    coverage_feature = feature(\n        name = \"coverage\",\n        provides = [\"profile\"],\n        flag_sets = [\n            flag_set(\n                actions = [\n                    ACTION_NAMES.preprocess_assemble,\n                    ACTION_NAMES.c_compile,\n                    ACTION_NAMES.cpp_compile,\n                    ACTION_NAMES.cpp_header_parsing,\n                    ACTION_NAMES.cpp_module_compile,\n                ],\n                flag_groups = ([\n                    flag_group(flags = ctx.attr.coverage_compile_flags),\n                ] if ctx.attr.coverage_compile_flags else []),\n            ),\n            flag_set(\n                actions = all_link_actions + lto_index_actions,\n                flag_groups = ([\n                    flag_group(flags = ctx.attr.coverage_link_flags),\n                ] if ctx.attr.coverage_link_flags else []),\n            ),\n        ],\n    )\n\n    build_id_feature = feature(\n        name = \"build-id\",\n        enabled = True,\n        flag_sets = [\n            flag_set(\n                actions = all_link_actions,\n                flag_groups = [\n                    flag_group(\n                        flags = [\"-Wl,--build-id=md5\", \"-Wl,--hash-style=gnu\"],\n                    ),\n                ],\n            ),\n        ],\n    )\n\n    no_canonical_prefixes_feature = feature(\n        name = \"no-canonical-prefixes\",\n        enabled = True,\n        flag_sets = [\n            flag_set(\n                actions = [\n                    ACTION_NAMES.c_compile,\n                    ACTION_NAMES.cpp_compile,\n                    ACTION_NAMES.cpp_link_executable,\n                    ACTION_NAMES.cpp_link_dynamic_library,\n                    ACTION_NAMES.cpp_link_nodeps_dynamic_library,\n                ],\n                flag_groups = [\n                    flag_group(\n                        flags = [\n                            \"-no-canonical-prefixes\",\n                            \"-fno-canonical-system-headers\",\n                        ]\n                    ),\n                ],\n            ),\n        ],\n    )\n\n    linker_bin_path_feature = feature(\n        name = \"linker-bin-path\",\n        enabled = True,\n        flag_sets = [\n            flag_set(\n                actions = all_link_actions,\n                flag_groups = [flag_group(flags = [\"-B\" + ctx.attr.linker_bin_path])],\n            ),\n        ],\n    )\n\n    features = [\n        dependency_file_feature,\n        random_seed_feature,\n        pic_feature,\n        per_object_debug_info_feature,\n        preprocessor_defines_feature,\n        includes_feature,\n        include_paths_feature,\n        fdo_instrument_feature,\n        cs_fdo_instrument_feature,\n        cs_fdo_optimize_feature,\n        fdo_prefetch_hints_feature,\n        autofdo_feature,\n        build_interface_libraries_feature,\n        dynamic_library_linker_tool_feature,\n        symbol_counts_feature,\n        shared_flag_feature,\n        linkstamps_feature,\n        output_execpath_flags_feature,\n        runtime_library_search_directories_feature,\n        library_search_directories_feature,\n        archiver_flags_feature,\n        force_pic_flags_feature,\n        fission_support_feature,\n        strip_debug_symbols_feature,\n        coverage_feature,\n        supports_pic_feature,\n    ] + (\n        [\n            supports_start_end_lib_feature,\n        ] if ctx.attr.supports_start_end_lib else []\n    ) + [\n        default_compile_flags_feature,\n        default_link_flags_feature,\n        libraries_to_link_feature,\n        user_link_flags_feature,\n        static_libgcc_feature,\n        fdo_optimize_feature,\n        supports_dynamic_linker_feature,\n        dbg_feature,\n        opt_feature,\n        user_compile_flags_feature,\n        sysroot_feature,\n        unfiltered_compile_flags_feature,\n        build_id_feature,\n        no_canonical_prefixes_feature,\n        linker_bin_path_feature,\n    ]\n\n    return cc_common.create_cc_toolchain_config_info(\n        ctx = ctx,\n        features = features,\n        action_configs = action_configs,\n        cxx_builtin_include_directories = ctx.attr.cxx_builtin_include_directories,\n        toolchain_identifier = ctx.attr.toolchain_identifier,\n        host_system_name = ctx.attr.host_system_name,\n        target_system_name = ctx.attr.target_system_name,\n        target_cpu = ctx.attr.cpu,\n        target_libc = ctx.attr.target_libc,\n        compiler = ctx.attr.compiler,\n        abi_version = ctx.attr.abi_version,\n        abi_libc_version = ctx.attr.abi_libc_version,\n        tool_paths = tool_paths,\n    )\n\ncc_toolchain_config = rule(\n    implementation = _impl,\n    attrs = {\n        \"cpu\": attr.string(mandatory = True),\n        \"compiler\": attr.string(mandatory = True),\n        \"toolchain_identifier\": attr.string(mandatory = True),\n        \"host_system_name\": attr.string(mandatory = True),\n        \"target_system_name\": attr.string(mandatory = True),\n        \"target_libc\": attr.string(mandatory = True),\n        \"abi_version\": attr.string(mandatory = True),\n        \"abi_libc_version\": attr.string(mandatory = True),\n        \"cxx_builtin_include_directories\": attr.string_list(),\n        \"compile_flags\": attr.string_list(),\n        \"dbg_compile_flags\": attr.string_list(),\n        \"opt_compile_flags\": attr.string_list(),\n        \"cxx_flags\": attr.string_list(),\n        \"link_flags\": attr.string_list(),\n        \"link_libs\": attr.string_list(),\n        \"opt_link_flags\": attr.string_list(),\n        \"unfiltered_compile_flags\": attr.string_list(),\n        \"coverage_compile_flags\": attr.string_list(),\n        \"coverage_link_flags\": attr.string_list(),\n        \"supports_start_end_lib\": attr.bool(),\n        \"host_compiler_path\": attr.string(),\n        \"host_compiler_prefix\": attr.string(),\n        \"linker_bin_path\": attr.string(),\n    },\n    provides = [CcToolchainConfigInfo],\n)\n\n"
  },
  {
    "path": "third_party/gpus/crosstool/windows/msvc_wrapper_for_nvcc.py.tpl",
    "content": "#!/usr/bin/env python\n# Copyright 2015 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\n\"\"\"Crosstool wrapper for compiling CUDA programs with nvcc on Windows.\n\nDESCRIPTION:\n  This script is the Windows version of //third_party/gpus/crosstool/crosstool_wrapper_is_not_gcc\n\"\"\"\n\nfrom argparse import ArgumentParser\nimport os\nimport subprocess\nimport re\nimport sys\nimport tempfile\n\n# Template values set by cuda_autoconf.\nCPU_COMPILER = ('%{cpu_compiler}')\nGCC_HOST_COMPILER_PATH = ('%{gcc_host_compiler_path}')\n\nNVCC_PATH = '%{nvcc_path}'\nNVCC_VERSION = '%{cuda_version}'\nNVCC_TEMP_DIR = \"%{nvcc_tmp_dir}\"\n\ndef Log(s):\n  print('gpus/crosstool: {0}'.format(s))\n\n\ndef GetOptionValue(argv, option):\n  \"\"\"Extract the list of values for option from options.\n\n  Args:\n    option: The option whose value to extract.\n\n  Returns:\n    1. A list of values, either directly following the option,\n    (eg., /opt val1 val2) or values collected from multiple occurrences of\n    the option (eg., /opt val1 /opt val2).\n    2. The leftover options.\n  \"\"\"\n\n  parser = ArgumentParser(prefix_chars='-/')\n  parser.add_argument(option, nargs='*', action='append')\n  option = option.lstrip('-/').replace('-', '_')\n  args, leftover = parser.parse_known_args(argv)\n  if args and vars(args)[option]:\n    return (sum(vars(args)[option], []), leftover)\n  return ([], leftover)\n\ndef _update_options(nvcc_options):\n  if NVCC_VERSION in (\"7.0\",):\n    return nvcc_options\n\n  update_options = { \"relaxed-constexpr\" : \"expt-relaxed-constexpr\" }\n  return [ update_options[opt] if opt in update_options else opt\n                    for opt in nvcc_options ]\n\ndef GetNvccOptions(argv):\n  \"\"\"Collect the -nvcc_options values from argv.\n\n  Args:\n    argv: A list of strings, possibly the argv passed to main().\n\n  Returns:\n    1. The string that can be passed directly to nvcc.\n    2. The leftover options.\n  \"\"\"\n\n  parser = ArgumentParser()\n  parser.add_argument('-nvcc_options', nargs='*', action='append')\n\n  args, leftover = parser.parse_known_args(argv)\n\n  if args.nvcc_options:\n    options = _update_options(sum(args.nvcc_options, []))\n    return (['--' + a for a in options], leftover)\n  return ([], leftover)\n\n\ndef InvokeNvcc(argv, log=False):\n  \"\"\"Call nvcc with arguments assembled from argv.\n\n  Args:\n    argv: A list of strings, possibly the argv passed to main().\n    log: True if logging is requested.\n\n  Returns:\n    The return value of calling os.system('nvcc ' + args)\n  \"\"\"\n\n  src_files = [f for f in argv if\n               re.search('\\.cpp$|\\.cc$|\\.c$|\\.cxx$|\\.C$', f)]\n  if len(src_files) == 0:\n    raise Error('No source files found for cuda compilation.')\n\n  out_file = [ f for f in argv if f.startswith('/Fo') ]\n  if len(out_file) != 1:\n    raise Error('Please specify exactly one output file for cuda compilation.')\n  out = ['-o', out_file[0][len('/Fo'):]]\n\n  nvcc_compiler_options, argv = GetNvccOptions(argv)\n\n  opt_option, argv = GetOptionValue(argv, '/O')\n  opt = ['-g']\n  if (len(opt_option) > 0 and opt_option[0] != 'd'):\n    opt = ['-O2']\n\n  include_options, argv = GetOptionValue(argv, '/I')\n  includes = [\"-I \" + include for include in include_options]\n\n  defines, argv = GetOptionValue(argv, '/D')\n  defines = ['-D' + define for define in defines]\n\n  undefines, argv = GetOptionValue(argv, '/U')\n  undefines = ['-U' + define for define in undefines]\n\n  fatbin_options, argv = GetOptionValue(argv, '-Xcuda-fatbinary')\n  fatbin_options = ['--fatbin-options=' + option for option in fatbin_options]\n\n  # The rest of the unrecognized options should be passed to host compiler\n  host_compiler_options = [option for option in argv if option not in (src_files + out_file)]\n\n  m_options = [\"-m64\"]\n\n  nvccopts = ['-D_FORCE_INLINES']\n  compute_capabilities, argv = GetOptionValue(argv, \"--cuda-gpu-arch\")\n  for capability in compute_capabilities:\n    capability = capability[len('sm_'):]\n    nvccopts += [\n        r'-gencode=arch=compute_%s,\"code=sm_%s\"' % (capability, capability)\n    ]\n  compute_capabilities, argv = GetOptionValue(argv, '--cuda-include-ptx')\n  for capability in compute_capabilities:\n    capability = capability[len('sm_'):]\n    nvccopts += [\n        r'-gencode=arch=compute_%s,\"code=compute_%s\"' % (capability, capability)\n    ]\n  _, argv = GetOptionValue(argv, '--no-cuda-include-ptx')\n\n  # nvcc doesn't respect the INCLUDE and LIB env vars from MSVC,\n  # so we explicity specify the system include paths and library search paths.\n  if 'INCLUDE' in os.environ:\n    nvccopts += [('--system-include=\"%s\"' % p) for p in os.environ['INCLUDE'].split(\";\")]\n  if 'LIB' in os.environ:\n    nvccopts += [('--library-path=\"%s\"' % p) for p in os.environ['LIB'].split(\";\")]\n\n  nvccopts += nvcc_compiler_options\n  nvccopts += undefines\n  nvccopts += defines\n  nvccopts += m_options\n  nvccopts += fatbin_options\n  nvccopts += ['--compiler-options=' + \",\".join(host_compiler_options)]\n  nvccopts += ['-x', 'cu'] + opt + includes + out + ['-c'] + src_files\n  # Specify a unique temp directory for nvcc to generate intermediate files,\n  # then Bazel can ignore files under NVCC_TEMP_DIR during dependency check\n  # http://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/index.html#options-for-guiding-compiler-driver\n  # Different actions are sharing NVCC_TEMP_DIR, so we cannot remove it if the directory already exists.\n  if os.path.isfile(NVCC_TEMP_DIR):\n    os.remove(NVCC_TEMP_DIR)\n  if not os.path.exists(NVCC_TEMP_DIR):\n    os.makedirs(NVCC_TEMP_DIR)\n  # Provide a unique dir for each compiling action to avoid conflicts.\n  tempdir = tempfile.mkdtemp(dir = NVCC_TEMP_DIR)\n  nvccopts += ['--keep', '--keep-dir', tempdir]\n  # Force C++17 dialect (note, everything in just one string!)\n  nvccopts += ['--std c++17']\n  if log:\n    Log([NVCC_PATH] + nvccopts)\n\n  # Store command line options in a file to avoid hitting the character limit.\n  optsfile = tempfile.NamedTemporaryFile(mode='w', dir=tempdir, delete=False)\n  optsfile.write(\"\\n\".join(nvccopts))\n  optsfile.close()\n\n  proc = subprocess.Popen([NVCC_PATH, \"--options-file\", optsfile.name],\n                          stdout=sys.stdout,\n                          stderr=sys.stderr,\n                          env=os.environ.copy(),\n                          shell=True)\n  proc.wait()\n  return proc.returncode\n\ndef ExpandParamsFileForArgv():\n  new_argv = []\n  for arg in sys.argv:\n    if arg.startswith(\"@\"):\n      with open(arg.strip(\"@\")) as f:\n        new_argv.extend([l.strip() for l in f.readlines()])\n    else:\n      new_argv.append(arg)\n\n  sys.argv = new_argv\n\ndef ProcessFlagForCommandFile(flag):\n  if flag.startswith(\"/D\") or flag.startswith(\"-D\"):\n    # We need to re-escape /DFOO=\"BAR\" as /DFOO=\\\"BAR\\\", so that we get\n    # `#define FOO \"BAR\"` after expansion as a string literal define\n    if flag.endswith('\"') and not flag.endswith('\\\\\"'):\n      flag = '\\\\\"'.join(flag.split('\"', 1))\n      flag = '\\\\\"'.join(flag.rsplit('\"', 1))\n      return flag\n  return flag\n\ndef main():\n  ExpandParamsFileForArgv()\n  parser = ArgumentParser()\n  parser.add_argument('-x', nargs=1)\n  parser.add_argument('--cuda_log', action='store_true')\n  args, leftover = parser.parse_known_args(sys.argv[1:])\n\n  if args.x and args.x[0] == 'cuda':\n    if args.cuda_log: Log('-x cuda')\n    if args.cuda_log: Log('using nvcc')\n    return InvokeNvcc(leftover, log=args.cuda_log)\n\n  # Strip our flags before passing through to the CPU compiler for files which\n  # are not -x cuda. We can't just pass 'leftover' because it also strips -x.\n  # We not only want to pass -x to the CPU compiler, but also keep it in its\n  # relative location in the argv list (the compiler is actually sensitive to\n  # this).\n  cpu_compiler_flags = [flag for flag in sys.argv[1:]\n                             if not flag.startswith(('--cuda_log'))\n                             and not flag.startswith(('-nvcc_options'))]\n  output = [flag for flag in cpu_compiler_flags if flag.startswith(\"/Fo\")]\n\n  # Store command line options in a file to avoid hitting the character limit.\n  if len(output) == 1:\n    commandfile_path = output[0][3:] + \".msvc_params\"\n    commandfile = open(commandfile_path, \"w\")\n    cpu_compiler_flags = [ProcessFlagForCommandFile(flag) for flag in cpu_compiler_flags]\n    commandfile.write(\"\\n\".join(cpu_compiler_flags))\n    commandfile.close()\n    return subprocess.call([CPU_COMPILER, \"@\" + commandfile_path])\n  else:\n    return subprocess.call([CPU_COMPILER] + cpu_compiler_flags)\n  return subprocess.call([CPU_COMPILER] + cpu_compiler_flags)\n\nif __name__ == '__main__':\n  sys.exit(main())\n"
  },
  {
    "path": "third_party/gpus/cuda/BUILD.bazel",
    "content": ""
  },
  {
    "path": "third_party/gpus/cuda/BUILD.tpl",
    "content": "load(\":build_defs.bzl\", \"cuda_header_library\")\nload(\"@bazel_skylib//:bzl_library.bzl\", \"bzl_library\")\nload(\"@bazel_skylib//lib:selects.bzl\", \"selects\")\n\nlicenses([\"restricted\"])  # MPL2, portions GPL v3, LGPL v3, BSD-like\n\npackage(default_visibility = [\"//visibility:public\"])\n\n# Config setting whether TensorFlow is built with CUDA support using clang.\n#\n# TODO(b/174244321), DEPRECATED: this target will be removed when all users\n# have been converted to :is_cuda_enabled (most) or :is_cuda_compiler_clang.\nselects.config_setting_group(\n    name = \"using_clang\",\n    match_all = [\n        \"@local_config_cuda//:is_cuda_enabled\",\n        \"@local_config_cuda//:is_cuda_compiler_clang\",\n    ],\n)\n\n# Config setting whether TensorFlow is built with CUDA support using nvcc.\n#\n# TODO(b/174244321), DEPRECATED: this target will be removed when all users\n# have been converted to :is_cuda_enabled (most) or :is_cuda_compiler_nvcc.\nselects.config_setting_group(\n    name = \"using_nvcc\",\n    match_all = [\n        \"@local_config_cuda//:is_cuda_enabled\",\n        \"@local_config_cuda//:is_cuda_compiler_nvcc\",\n    ],\n)\n\n# Equivalent to using_clang && -c opt.\nselects.config_setting_group(\n    name = \"using_clang_opt\",\n    match_all = [\n        \":using_clang\",\n        \":_opt\",\n    ],\n)\n\nconfig_setting(\n    name = \"_opt\",\n    values = {\"compilation_mode\": \"opt\"},\n)\n\n# Provides CUDA headers for '#include \"third_party/gpus/cuda/include/cuda.h\"'\n# All clients including Tachyon should use these directives.\ncuda_header_library(\n    name = \"cuda_headers\",\n    hdrs = [\n        \"cuda/cuda_config.h\",\n        \":cuda-include\",\n    ],\n    include_prefix = \"third_party/gpus\",\n    includes = [\n        \".\",  # required to include cuda/cuda/cuda_config.h as cuda/config.h\n        \"cuda/include\",\n    ],\n)\n\ncc_library(\n    name = \"cudart_static\",\n    srcs = [\"cuda/lib/%{cudart_static_lib}\"],\n    linkopts = [\n        \"-ldl\",\n        \"-lpthread\",\n        %{cudart_static_linkopt}\n    ],\n)\n\ncc_library(\n    name = \"cuda_driver\",\n    srcs = [\"cuda/lib/%{cuda_driver_lib}\"],\n)\n\ncc_library(\n    name = \"cudart\",\n    srcs = [\"cuda/lib/%{cudart_lib}\"],\n    data = [\"cuda/lib/%{cudart_lib}\"],\n    linkstatic = 1,\n)\n\ncuda_header_library(\n    name = \"cublas_headers\",\n    hdrs = [\":cublas-include\"],\n    include_prefix = \"third_party/gpus/cuda/include\",\n    includes = [\"cublas/include\"],\n    strip_include_prefix = \"cublas/include\",\n    deps = [\":cuda_headers\"],\n)\n\ncuda_header_library(\n    name = \"cusolver_headers\",\n    hdrs = [\":cusolver-include\"],\n    include_prefix = \"third_party/gpus/cuda/include\",\n    includes = [\"cusolver/include\"],\n    strip_include_prefix = \"cusolver/include\",\n    deps = [\":cuda_headers\"],\n)\n\ncuda_header_library(\n    name = \"cufft_headers\",\n    hdrs = [\":cufft-include\"],\n    include_prefix = \"third_party/gpus/cuda/include\",\n    includes = [\"cufft/include\"],\n    strip_include_prefix = \"cufft/include\",\n    deps = [\":cuda_headers\"],\n)\n\ncuda_header_library(\n    name = \"cusparse_headers\",\n    hdrs = [\":cusparse-include\"],\n    include_prefix = \"third_party/gpus/cuda/include\",\n    includes = [\"cusparse/include\"],\n    strip_include_prefix = \"cusparse/include\",\n    deps = [\":cuda_headers\"],\n)\n\ncuda_header_library(\n    name = \"curand_headers\",\n    hdrs = [\":curand-include\"],\n    include_prefix = \"third_party/gpus/cuda/include\",\n    includes = [\"curand/include\"],\n    strip_include_prefix = \"curand/include\",\n    deps = [\":cuda_headers\"],\n)\n\ncc_library(\n    name = \"cublas\",\n    srcs = [\"cuda/lib/%{cublas_lib}\"],\n    data = [\"cuda/lib/%{cublas_lib}\"],\n    linkstatic = 1,\n)\n\ncc_library(\n    name = \"cublasLt\",\n    srcs = [\"cuda/lib/%{cublasLt_lib}\"],\n    data = [\"cuda/lib/%{cublasLt_lib}\"],\n    linkstatic = 1,\n)\n\ncc_library(\n    name = \"cusolver\",\n    srcs = [\"cuda/lib/%{cusolver_lib}\"],\n    data = [\"cuda/lib/%{cusolver_lib}\"],\n    linkopts = [\"-lgomp\"],\n    linkstatic = 1,\n)\n\ncc_library(\n    name = \"cudnn\",\n    srcs = [\"cuda/lib/%{cudnn_lib}\"],\n    data = [\"cuda/lib/%{cudnn_lib}\"],\n    linkstatic = 1,\n)\n\ncc_library(\n    name = \"cudnn_header\",\n    hdrs = [\":cudnn-include\"],\n    include_prefix = \"third_party/gpus/cudnn\",\n    strip_include_prefix = \"cudnn/include\",\n    deps = [\":cuda_headers\"],\n)\n\ncc_library(\n    name = \"cufft\",\n    srcs = [\"cuda/lib/%{cufft_lib}\"],\n    data = [\"cuda/lib/%{cufft_lib}\"],\n    linkstatic = 1,\n)\n\ncc_library(\n    name = \"curand\",\n    srcs = [\"cuda/lib/%{curand_lib}\"],\n    data = [\"cuda/lib/%{curand_lib}\"],\n    linkstatic = 1,\n)\n\ncc_library(\n    name = \"cuda\",\n    deps = [\n        \":cublas\",\n        \":cublasLt\",\n        \":cuda_headers\",\n        \":cudart\",\n        \":cudnn\",\n        \":cufft\",\n        \":curand\",\n    ],\n)\n\nalias(\n    name = \"cub_headers\",\n    actual = \"%{cub_actual}\",\n)\n\ncuda_header_library(\n    name = \"cupti_headers\",\n    hdrs = [\":cuda-extras\"],\n    include_prefix = \"third_party/gpus\",\n    includes = [\"cuda/extras/CUPTI/include/\"],\n    deps = [\":cuda_headers\"],\n)\n\ncc_library(\n    name = \"cupti_dsos\",\n    data = [\"cuda/lib/%{cupti_lib}\"],\n)\n\ncc_library(\n    name = \"cusparse\",\n    srcs = [\"cuda/lib/%{cusparse_lib}\"],\n    data = [\"cuda/lib/%{cusparse_lib}\"],\n    linkopts = [\"-lgomp\"],\n    linkstatic = 1,\n)\n\ncc_library(\n    name = \"libdevice_root\",\n    data = [\":cuda-nvvm\"],\n)\n\nbzl_library(\n    name = \"build_defs_bzl\",\n    srcs = [\"build_defs.bzl\"],\n    deps = [\n        \"@bazel_skylib//lib:selects\",\n    ],\n)\n\npy_library(\n    name = \"cuda_config_py\",\n    srcs = [\"cuda/cuda_config.py\"],\n)\n\n%{copy_rules}\n"
  },
  {
    "path": "third_party/gpus/cuda/BUILD.windows.tpl",
    "content": "load(\":build_defs.bzl\", \"cuda_header_library\")\nload(\"@bazel_skylib//:bzl_library.bzl\", \"bzl_library\")\nload(\"@bazel_skylib//lib:selects.bzl\", \"selects\")\n\nlicenses([\"restricted\"])  # MPL2, portions GPL v3, LGPL v3, BSD-like\n\npackage(default_visibility = [\"//visibility:public\"])\n\n# Config setting whether TensorFlow is built with CUDA support using clang.\n#\n# TODO(b/174244321), DEPRECATED: this target will be removed when all users\n# have been converted to :is_cuda_enabled (most) or :is_cuda_compiler_clang.\nselects.config_setting_group(\n    name = \"using_clang\",\n    match_all = [\n        \"@local_config_cuda//:is_cuda_enabled\",\n        \"@local_config_cuda//:is_cuda_compiler_clang\",\n    ],\n)\n\n# Config setting whether TensorFlow is built with CUDA support using nvcc.\n#\n# TODO(b/174244321), DEPRECATED: this target will be removed when all users\n# have been converted to :is_cuda_enabled (most) or :is_cuda_compiler_nvcc.\nselects.config_setting_group(\n    name = \"using_nvcc\",\n    match_all = [\n        \"@local_config_cuda//:is_cuda_enabled\",\n        \"@local_config_cuda//:is_cuda_compiler_nvcc\",\n    ],\n)\n\n# Equivalent to using_clang && -c opt.\nselects.config_setting_group(\n    name = \"using_clang_opt\",\n    match_all = [\n        \":using_clang\",\n        \":_opt\",\n    ],\n)\n\nconfig_setting(\n    name = \"_opt\",\n    values = {\"compilation_mode\": \"opt\"},\n)\n\n# Provides CUDA headers for '#include \"third_party/gpus/cuda/include/cuda.h\"'\n# All clients including TensorFlow should use these directives.\ncuda_header_library(\n    name = \"cuda_headers\",\n    hdrs = [\n        \"cuda/cuda_config.h\",\n        \":cuda-include\",\n    ],\n    include_prefix = \"third_party/gpus\",\n    includes = [\n        \".\",  # required to include cuda/cuda/cuda_config.h as cuda/config.h\n        \"cuda/include\",\n    ],\n)\n\ncc_import(\n    name = \"cudart_static\",\n    # /WHOLEARCHIVE:cudart_static.lib will cause a\n    # \"Internal error during CImplib::EmitThunk\" error.\n    # Treat this library as interface library to avoid being whole archived when\n    # linking a DLL that depends on this.\n    # TODO(pcloudy): Remove this rule after b/111278841 is resolved.\n    interface_library = \"cuda/lib/%{cudart_static_lib}\",\n    system_provided = 1,\n)\n\ncc_import(\n    name = \"cuda_driver\",\n    interface_library = \"cuda/lib/%{cuda_driver_lib}\",\n    system_provided = 1,\n)\n\ncc_import(\n    name = \"cudart\",\n    interface_library = \"cuda/lib/%{cudart_lib}\",\n    system_provided = 1,\n)\n\ncuda_header_library(\n    name = \"cublas_headers\",\n    hdrs = [\":cublas-include\"],\n    include_prefix = \"third_party/gpus/cuda/include\",\n    includes = [\"cublas/include\"],\n    strip_include_prefix = \"cublas/include\",\n    deps = [\":cuda_headers\"],\n)\n\ncuda_header_library(\n    name = \"cusolver_headers\",\n    hdrs = [\":cusolver-include\"],\n    include_prefix = \"third_party/gpus/cuda/include\",\n    includes = [\"cusolver/include\"],\n    strip_include_prefix = \"cusolver/include\",\n    deps = [\":cuda_headers\"],\n)\n\ncuda_header_library(\n    name = \"cufft_headers\",\n    hdrs = [\":cufft-include\"],\n    include_prefix = \"third_party/gpus/cuda/include\",\n    includes = [\"cufft/include\"],\n    strip_include_prefix = \"cufft/include\",\n    deps = [\":cuda_headers\"],\n)\n\ncuda_header_library(\n    name = \"cusparse_headers\",\n    hdrs = [\":cusparse-include\"],\n    include_prefix = \"third_party/gpus/cuda/include\",\n    includes = [\"cusparse/include\"],\n    strip_include_prefix = \"cusparse/include\",\n    deps = [\":cuda_headers\"],\n)\n\ncuda_header_library(\n    name = \"curand_headers\",\n    hdrs = [\":curand-include\"],\n    include_prefix = \"third_party/gpus/cuda/include\",\n    includes = [\"curand/include\"],\n    strip_include_prefix = \"curand/include\",\n    deps = [\":cuda_headers\"],\n)\n\ncc_import(\n    name = \"cublas\",\n    interface_library = \"cuda/lib/%{cublas_lib}\",\n    system_provided = 1,\n)\n\ncc_import(\n    name = \"cublasLt\",\n    interface_library = \"cuda/lib/%{cublasLt_lib}\",\n    system_provided = 1,\n)\n\ncc_import(\n    name = \"cusolver\",\n    interface_library = \"cuda/lib/%{cusolver_lib}\",\n    system_provided = 1,\n)\n\ncc_import(\n    name = \"cudnn\",\n    interface_library = \"cuda/lib/%{cudnn_lib}\",\n    system_provided = 1,\n)\n\ncc_library(\n    name = \"cudnn_header\",\n    hdrs = [\":cudnn-include\"],\n    include_prefix = \"third_party/gpus/cudnn\",\n    strip_include_prefix = \"cudnn/include\",\n    deps = [\":cuda_headers\"],\n)\n\ncc_import(\n    name = \"cufft\",\n    interface_library = \"cuda/lib/%{cufft_lib}\",\n    system_provided = 1,\n)\n\ncc_import(\n    name = \"curand\",\n    interface_library = \"cuda/lib/%{curand_lib}\",\n    system_provided = 1,\n)\n\ncc_library(\n    name = \"cuda\",\n    deps = [\n        \":cublas\",\n        \":cublasLt\",\n        \":cuda_headers\",\n        \":cudart\",\n        \":cudnn\",\n        \":cufft\",\n        \":curand\",\n    ],\n)\n\nalias(\n    name = \"cub_headers\",\n    actual = \"%{cub_actual}\",\n)\n\ncuda_header_library(\n    name = \"cupti_headers\",\n    hdrs = [\":cuda-extras\"],\n    include_prefix = \"third_party/gpus\",\n    includes = [\"cuda/extras/CUPTI/include/\"],\n    deps = [\":cuda_headers\"],\n)\n\ncc_import(\n    name = \"cupti_dsos\",\n    interface_library = \"cuda/lib/%{cupti_lib}\",\n    system_provided = 1,\n)\n\ncc_import(\n    name = \"cusparse\",\n    interface_library = \"cuda/lib/%{cusparse_lib}\",\n    system_provided = 1,\n)\n\ncc_library(\n    name = \"libdevice_root\",\n    data = [\":cuda-nvvm\"],\n)\n\nbzl_library(\n    name = \"build_defs_bzl\",\n    srcs = [\"build_defs.bzl\"],\n    deps = [\n        \"@bazel_skylib//lib:selects\",\n    ],\n)\n\npy_library(\n    name = \"cuda_config_py\",\n    srcs = [\"cuda/cuda_config.py\"],\n)\n\n%{copy_rules}\n"
  },
  {
    "path": "third_party/gpus/cuda/LICENSE",
    "content": "Copyright 2015 The TensorFlow Authors.  All rights reserved.\n\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright 2015, The TensorFlow Authors.\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "third_party/gpus/cuda/build_defs.bzl.tpl",
    "content": "# Macros for building CUDA code.\ndef if_cuda(if_true, if_false = []):\n    \"\"\"Shorthand for select()'ing on whether we're building with CUDA.\n\n    Returns a select statement which evaluates to if_true if we're building\n    with CUDA enabled.  Otherwise, the select statement evaluates to if_false.\n\n    \"\"\"\n    return select({\n        \"@local_config_cuda//:is_cuda_enabled\": if_true,\n        \"//conditions:default\": if_false,\n    })\n\ndef if_cuda_clang(if_true, if_false = []):\n   \"\"\"Shorthand for select()'ing on whether we're building with cuda-clang.\n\n    Returns a select statement which evaluates to if_true if we're building\n    with cuda-clang.  Otherwise, the select statement evaluates to if_false.\n\n   \"\"\"\n   return select({\n       \"@local_config_cuda//cuda:using_clang\": if_true,\n       \"//conditions:default\": if_false\n   })\n\ndef cuda_compiler(if_cuda_clang, if_nvcc, neither = []):\n    \"\"\"Shorthand for select()'ing on whether we're building with cuda-clang or nvcc.\n\n     Returns a select statement which evaluates to if_cuda_clang if we're building\n     with cuda-clang, if_nvcc if we're building with NVCC.\n     Otherwise, the select statement evaluates to neither.\n\n    \"\"\"\n    if %{cuda_is_configured}:\n        return select({\n            \"@local_config_cuda//cuda:using_clang\": if_cuda_clang,\n            \"@local_config_cuda//:is_cuda_compiler_nvcc\": if_nvcc,\n            \"//conditions:default\": neither\n        })\n    else:\n        return select({\n            \"//conditions:default\": neither\n        })\n\ndef if_cuda_clang_opt(if_true, if_false = []):\n   \"\"\"Shorthand for select()'ing on whether we're building with cuda-clang\n   in opt mode.\n\n    Returns a select statement which evaluates to if_true if we're building\n    with cuda-clang in opt mode. Otherwise, the select statement evaluates to\n    if_false.\n\n   \"\"\"\n   return select({\n       \"@local_config_cuda//cuda:using_clang_opt\": if_true,\n       \"//conditions:default\": if_false\n   })\n\ndef cuda_default_copts():\n    \"\"\"Default options for all CUDA compilations.\"\"\"\n    return if_cuda([\n        \"-x\", \"cuda\",\n        \"-DTACHYON_CUDA=1\",\n    ] + %{cuda_extra_copts}) + if_cuda_clang_opt(\n        # Some important CUDA optimizations are only enabled at O3.\n        [\"-O3\"]\n    ) + cuda_compiler(\n        if_cuda_clang = [ \"-Xcuda-fatbinary\", \"--compress-all\"],\n        if_nvcc = [\n            \"-Xcuda-fatbinary=--compress-all\",\n            # Ensure that NVCC matches clang's constexpr behavior.\n            \"-nvcc_options=expt-relaxed-constexpr\"\n        ]\n    )\n\ndef cuda_gpu_architectures():\n    \"\"\"Returns a list of supported GPU architectures.\"\"\"\n    return %{cuda_gpu_architectures}\n\ndef if_cuda_is_configured(x, no_cuda = []):\n    \"\"\"Tests if the CUDA was enabled during the configure process.\n\n    Unlike if_cuda(), this does not require that we are building with\n    --config=cuda. Used to allow non-CUDA code to depend on CUDA libraries.\n    \"\"\"\n    if %{cuda_is_configured}:\n      return select({\"//conditions:default\": x})\n    return select({\"//conditions:default\": no_cuda})\n\ndef cuda_header_library(\n        name,\n        hdrs,\n        include_prefix = None,\n        strip_include_prefix = None,\n        deps = [],\n        **kwargs):\n    \"\"\"Generates a cc_library containing both virtual and system include paths.\n\n    Generates both a header-only target with virtual includes plus the full\n    target without virtual includes. This works around the fact that bazel can't\n    mix 'includes' and 'include_prefix' in the same target.\"\"\"\n\n    native.cc_library(\n        name = name + \"_virtual\",\n        hdrs = hdrs,\n        include_prefix = include_prefix,\n        strip_include_prefix = strip_include_prefix,\n        deps = deps,\n        visibility = [\"//visibility:private\"],\n    )\n\n    native.cc_library(\n        name = name,\n        textual_hdrs = hdrs,\n        deps = deps + [\":%s_virtual\" % name],\n        **kwargs\n    )\n\ndef cuda_library(copts = [], **kwargs):\n    \"\"\"Wrapper over cc_library which adds default CUDA options.\"\"\"\n    native.cc_library(copts = cuda_default_copts() + copts, **kwargs)\n\ndef cuda_cc_test(copts = [], **kwargs):\n    \"\"\"Wrapper over cc_test which adds default CUDA options.\"\"\"\n    native.cc_test(copts = copts + if_cuda([\"-DTACHYON_CUDA=1\"]), **kwargs)\n\nEnableCudaInfo = provider()\n\ndef _enable_cuda_flag_impl(ctx):\n    value = ctx.build_setting_value\n    if ctx.attr.enable_override:\n        print(\n            \"\\n\\033[1;33mWarning:\\033[0m '--define=using_cuda_nvcc' will be \" +\n            \"unsupported soon. Use '--@local_config_cuda//:enable_cuda' \" +\n            \"instead.\"\n        )\n        value = True\n    return EnableCudaInfo(value = value)\n\nenable_cuda_flag = rule(\n    implementation = _enable_cuda_flag_impl,\n    build_setting = config.bool(flag = True),\n    attrs = {\"enable_override\": attr.bool()},\n)\n"
  },
  {
    "path": "third_party/gpus/cuda/cuda_config.h.tpl",
    "content": "/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n#ifndef CUDA_CUDA_CONFIG_H_\n#define CUDA_CUDA_CONFIG_H_\n\n#define TACHYON_CUDA_VERSION \"%{cuda_version}\"\n#define TACHYON_CUDART_VERSION \"%{cudart_version}\"\n#define TACHYON_CUPTI_VERSION \"%{cupti_version}\"\n#define TACHYON_CUBLAS_VERSION \"%{cublas_version}\"\n#define TACHYON_CUSOLVER_VERSION \"%{cusolver_version}\"\n#define TACHYON_CURAND_VERSION \"%{curand_version}\"\n#define TACHYON_CUFFT_VERSION \"%{cufft_version}\"\n#define TACHYON_CUSPARSE_VERSION \"%{cusparse_version}\"\n#define TACHYON_CUDNN_VERSION \"%{cudnn_version}\"\n\n#define TACHYON_CUDA_TOOLKIT_PATH \"%{cuda_toolkit_path}\"\n\n#define TACHYON_CUDA_COMPUTE_CAPABILITIES %{cuda_compute_capabilities}\n\n#endif  // CUDA_CUDA_CONFIG_H_\n"
  },
  {
    "path": "third_party/gpus/cuda/cuda_config.py.tpl",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\nconfig = %{cuda_config}\n"
  },
  {
    "path": "third_party/gpus/cuda_configure.bzl",
    "content": "\"\"\"Repository rule for CUDA autoconfiguration.\n\n`cuda_configure` depends on the following environment variables:\n\n  * `TACHYON_NEED_CUDA`: Whether to enable building with CUDA.\n  * `GCC_HOST_COMPILER_PATH`: The GCC host compiler path\n  * `TACHYON_CUDA_CLANG`: Whether to use clang as a cuda compiler.\n  * `CLANG_CUDA_COMPILER_PATH`: The clang compiler path that will be used for\n    both host and device code compilation if TACHYON_CUDA_CLANG is 1.\n  * `TACHYON_SYSROOT`: The sysroot to use when compiling.\n  * `TACHYON_DOWNLOAD_CLANG`: Whether to download a recent release of clang\n    compiler and use it to build tachyon. When this option is set\n    CLANG_CUDA_COMPILER_PATH is ignored.\n  * `TACHYON_CUDA_PATHS`: The base paths to look for CUDA. Default is\n    `/usr/local/cuda,usr/`.\n  * `CUDA_TOOLKIT_PATH` (deprecated): The path to the CUDA toolkit. Default is\n    `/usr/local/cuda`.\n  * `TACHYON_CUDA_VERSION`: The version of the CUDA toolkit. If this is blank, then\n    use the system default.\n  * `TACHYON_CUDA_COMPUTE_CAPABILITIES`: The CUDA compute capabilities. Default is\n    `3.5,5.2`.\n  * `PYTHON_BIN_PATH`: The python binary path\n\"\"\"\n\nload(\n    \"@bazel_tools//tools/cpp:lib_cc_configure.bzl\",\n    \"escape_string\",\n    \"get_env_var\",\n)\nload(\n    \"@bazel_tools//tools/cpp:windows_cc_configure.bzl\",\n    \"find_msvc_tool\",\n    \"find_vc_path\",\n    \"setup_vc_env_vars\",\n)\nload(\"//third_party/clang_toolchain:download_clang.bzl\", \"download_clang\")\nload(\n    \"//third_party/remote_config:common.bzl\",\n    \"config_repo_label\",\n    \"err_out\",\n    \"execute\",\n    \"get_bash_bin\",\n    \"get_cpu_value\",\n    \"get_host_environ\",\n    \"get_python_bin\",\n    \"is_windows\",\n    \"raw_exec\",\n    \"read_dir\",\n    \"realpath\",\n    \"which\",\n)\n\n_GCC_HOST_COMPILER_PATH = \"GCC_HOST_COMPILER_PATH\"\n_GCC_HOST_COMPILER_PREFIX = \"GCC_HOST_COMPILER_PREFIX\"\n_CLANG_CUDA_COMPILER_PATH = \"CLANG_CUDA_COMPILER_PATH\"\n_TACHYON_SYSROOT = \"TACHYON_SYSROOT\"\n_CUDA_TOOLKIT_PATH = \"CUDA_TOOLKIT_PATH\"\n_TACHYON_CUDA_VERSION = \"TACHYON_CUDA_VERSION\"\n_TACHYON_CUDA_COMPUTE_CAPABILITIES = \"TACHYON_CUDA_COMPUTE_CAPABILITIES\"\n_TACHYON_CUDA_CONFIG_REPO = \"TACHYON_CUDA_CONFIG_REPO\"\n_TACHYON_DOWNLOAD_CLANG = \"TACHYON_DOWNLOAD_CLANG\"\n_PYTHON_BIN_PATH = \"PYTHON_BIN_PATH\"\n\ndef to_list_of_strings(elements):\n    \"\"\"Convert the list of [\"a\", \"b\", \"c\"] into '\"a\", \"b\", \"c\"'.\n\n    This is to be used to put a list of strings into the bzl file templates\n    so it gets interpreted as list of strings in Starlark.\n\n    Args:\n      elements: list of string elements\n\n    Returns:\n      single string of elements wrapped in quotes separated by a comma.\"\"\"\n    quoted_strings = [\"\\\"\" + element + \"\\\"\" for element in elements]\n    return \", \".join(quoted_strings)\n\ndef verify_build_defines(params):\n    \"\"\"Verify all variables that crosstool/BUILD.tpl expects are substituted.\n\n    Args:\n      params: dict of variables that will be passed to the BUILD.tpl template.\n    \"\"\"\n    missing = []\n    for param in [\n        \"cxx_builtin_include_directories\",\n        \"extra_no_canonical_prefixes_flags\",\n        \"host_compiler_path\",\n        \"host_compiler_prefix\",\n        \"host_compiler_warnings\",\n        \"linker_bin_path\",\n        \"compiler_deps\",\n        \"msvc_cl_path\",\n        \"msvc_env_include\",\n        \"msvc_env_lib\",\n        \"msvc_env_path\",\n        \"msvc_env_tmp\",\n        \"msvc_lib_path\",\n        \"msvc_link_path\",\n        \"msvc_ml_path\",\n        \"unfiltered_compile_flags\",\n        \"win_compiler_deps\",\n    ]:\n        if (\"%{\" + param + \"}\") not in params:\n            missing.append(param)\n\n    if missing:\n        auto_configure_fail(\n            \"BUILD.tpl template is missing these variables: \" +\n            str(missing) +\n            \".\\nWe only got: \" +\n            str(params) +\n            \".\",\n        )\n\ndef _get_nvcc_tmp_dir_for_windows(repository_ctx):\n    \"\"\"Return the Windows tmp directory for nvcc to generate intermediate source files.\"\"\"\n    escaped_tmp_dir = escape_string(\n        get_env_var(repository_ctx, \"TMP\", \"C:\\\\Windows\\\\Temp\").replace(\n            \"\\\\\",\n            \"\\\\\\\\\",\n        ),\n    )\n    return escaped_tmp_dir + \"\\\\\\\\nvcc_inter_files_tmp_dir\"\n\ndef _get_msvc_compiler(repository_ctx):\n    vc_path = find_vc_path(repository_ctx)\n    return find_msvc_tool(repository_ctx, vc_path, \"cl.exe\").replace(\"\\\\\", \"/\")\n\ndef _get_win_cuda_defines(repository_ctx):\n    \"\"\"Return CROSSTOOL defines for Windows\"\"\"\n\n    # If we are not on Windows, return fake vaules for Windows specific fields.\n    # This ensures the CROSSTOOL file parser is happy.\n    if not is_windows(repository_ctx):\n        return {\n            \"%{msvc_env_tmp}\": \"msvc_not_used\",\n            \"%{msvc_env_path}\": \"msvc_not_used\",\n            \"%{msvc_env_include}\": \"msvc_not_used\",\n            \"%{msvc_env_lib}\": \"msvc_not_used\",\n            \"%{msvc_cl_path}\": \"msvc_not_used\",\n            \"%{msvc_ml_path}\": \"msvc_not_used\",\n            \"%{msvc_link_path}\": \"msvc_not_used\",\n            \"%{msvc_lib_path}\": \"msvc_not_used\",\n        }\n\n    vc_path = find_vc_path(repository_ctx)\n    if not vc_path:\n        auto_configure_fail(\n            \"Visual C++ build tools not found on your machine.\" +\n            \"Please check your installation following https://docs.bazel.build/versions/master/windows.html#using\",\n        )\n        return {}\n\n    env = setup_vc_env_vars(repository_ctx, vc_path)\n    escaped_paths = escape_string(env[\"PATH\"])\n    escaped_include_paths = escape_string(env[\"INCLUDE\"])\n    escaped_lib_paths = escape_string(env[\"LIB\"])\n    escaped_tmp_dir = escape_string(\n        get_env_var(repository_ctx, \"TMP\", \"C:\\\\Windows\\\\Temp\").replace(\n            \"\\\\\",\n            \"\\\\\\\\\",\n        ),\n    )\n\n    msvc_cl_path = \"windows/msvc_wrapper_for_nvcc.bat\"\n    msvc_ml_path = find_msvc_tool(repository_ctx, vc_path, \"ml64.exe\").replace(\n        \"\\\\\",\n        \"/\",\n    )\n    msvc_link_path = find_msvc_tool(repository_ctx, vc_path, \"link.exe\").replace(\n        \"\\\\\",\n        \"/\",\n    )\n    msvc_lib_path = find_msvc_tool(repository_ctx, vc_path, \"lib.exe\").replace(\n        \"\\\\\",\n        \"/\",\n    )\n\n    # nvcc will generate some temporary source files under %{nvcc_tmp_dir}\n    # The generated files are guaranteed to have unique name, so they can share\n    # the same tmp directory\n    escaped_cxx_include_directories = [\n        _get_nvcc_tmp_dir_for_windows(repository_ctx),\n        \"C:\\\\\\\\botcode\\\\\\\\w\",\n    ]\n    for path in escaped_include_paths.split(\";\"):\n        if path:\n            escaped_cxx_include_directories.append(path)\n\n    return {\n        \"%{msvc_env_tmp}\": escaped_tmp_dir,\n        \"%{msvc_env_path}\": escaped_paths,\n        \"%{msvc_env_include}\": escaped_include_paths,\n        \"%{msvc_env_lib}\": escaped_lib_paths,\n        \"%{msvc_cl_path}\": msvc_cl_path,\n        \"%{msvc_ml_path}\": msvc_ml_path,\n        \"%{msvc_link_path}\": msvc_link_path,\n        \"%{msvc_lib_path}\": msvc_lib_path,\n        \"%{cxx_builtin_include_directories}\": to_list_of_strings(\n            escaped_cxx_include_directories,\n        ),\n    }\n\n# TODO(dzc): Once these functions have been factored out of Bazel's\n# cc_configure.bzl, load them from @bazel_tools instead.\n# BEGIN cc_configure common functions.\ndef find_cc(repository_ctx):\n    \"\"\"Find the C++ compiler.\"\"\"\n    if is_windows(repository_ctx):\n        return _get_msvc_compiler(repository_ctx)\n\n    if _use_cuda_clang(repository_ctx):\n        target_cc_name = \"clang\"\n        cc_path_envvar = _CLANG_CUDA_COMPILER_PATH\n        if _flag_enabled(repository_ctx, _TACHYON_DOWNLOAD_CLANG):\n            return \"extra_tools/bin/clang\"\n    else:\n        target_cc_name = \"gcc\"\n        cc_path_envvar = _GCC_HOST_COMPILER_PATH\n    cc_name = target_cc_name\n\n    cc_name_from_env = get_host_environ(repository_ctx, cc_path_envvar)\n    if cc_name_from_env:\n        cc_name = cc_name_from_env\n    if cc_name.startswith(\"/\"):\n        # Absolute path, maybe we should make this supported by our which function.\n        return cc_name\n    cc = which(repository_ctx, cc_name)\n    if cc == None:\n        fail((\"Cannot find {}, either correct your path or set the {}\" +\n              \" environment variable\").format(target_cc_name, cc_path_envvar))\n    return cc\n\n_INC_DIR_MARKER_BEGIN = \"#include <...>\"\n\n# OSX add \" (framework directory)\" at the end of line, strip it.\n_OSX_FRAMEWORK_SUFFIX = \" (framework directory)\"\n_OSX_FRAMEWORK_SUFFIX_LEN = len(_OSX_FRAMEWORK_SUFFIX)\n\ndef _cxx_inc_convert(path):\n    \"\"\"Convert path returned by cc -E xc++ in a complete path.\"\"\"\n    path = path.strip()\n    if path.endswith(_OSX_FRAMEWORK_SUFFIX):\n        path = path[:-_OSX_FRAMEWORK_SUFFIX_LEN].strip()\n    return path\n\ndef _normalize_include_path(repository_ctx, path):\n    \"\"\"Normalizes include paths before writing them to the crosstool.\n\n      If path points inside the 'crosstool' folder of the repository, a relative\n      path is returned.\n      If path points outside the 'crosstool' folder, an absolute path is returned.\n      \"\"\"\n    path = str(repository_ctx.path(path))\n    crosstool_folder = str(repository_ctx.path(\".\").get_child(\"crosstool\"))\n\n    if path.startswith(crosstool_folder):\n        # We drop the path to \"$REPO/crosstool\" and a trailing path separator.\n        return path[len(crosstool_folder) + 1:]\n    return path\n\ndef _is_compiler_option_supported(repository_ctx, cc, option):\n    \"\"\"Checks that `option` is supported by the C compiler. Doesn't %-escape the option.\"\"\"\n    result = repository_ctx.execute([\n        cc,\n        option,\n        \"-o\",\n        \"/dev/null\",\n        \"-c\",\n        str(repository_ctx.path(\"tools/cpp/empty.cc\")),\n    ])\n    return result.stderr.find(option) == -1\n\ndef _get_cxx_inc_directories_impl(repository_ctx, cc, lang_is_cpp, tachyon_sysroot):\n    \"\"\"Compute the list of default C or C++ include directories.\"\"\"\n    if lang_is_cpp:\n        lang = \"c++\"\n    else:\n        lang = \"c\"\n    sysroot = []\n    if tachyon_sysroot:\n        sysroot += [\"--sysroot\", tachyon_sysroot]\n    result = raw_exec(repository_ctx, [cc, \"-E\", \"-x\" + lang, \"-\", \"-v\"] +\n                                      sysroot)\n    stderr = err_out(result)\n    index1 = stderr.find(_INC_DIR_MARKER_BEGIN)\n    if index1 == -1:\n        return []\n    index1 = stderr.find(\"\\n\", index1)\n    if index1 == -1:\n        return []\n    index2 = stderr.rfind(\"\\n \")\n    if index2 == -1 or index2 < index1:\n        return []\n    index2 = stderr.find(\"\\n\", index2 + 1)\n    if index2 == -1:\n        inc_dirs = stderr[index1 + 1:]\n    else:\n        inc_dirs = stderr[index1 + 1:index2].strip()\n\n    print_resource_dir_supported = _is_compiler_option_supported(\n        repository_ctx,\n        cc,\n        \"-print-resource-dir\",\n    )\n\n    if print_resource_dir_supported:\n        resource_dir = repository_ctx.execute(\n            [cc, \"-print-resource-dir\"],\n        ).stdout.strip() + \"/share\"\n        inc_dirs += \"\\n\" + resource_dir\n\n    return [\n        _normalize_include_path(repository_ctx, _cxx_inc_convert(p))\n        for p in inc_dirs.split(\"\\n\")\n    ]\n\ndef get_cxx_inc_directories(repository_ctx, cc, tachyon_sysroot):\n    \"\"\"Compute the list of default C and C++ include directories.\"\"\"\n\n    # For some reason `clang -xc` sometimes returns include paths that are\n    # different from the ones from `clang -xc++`. (Symlink and a dir)\n    # So we run the compiler with both `-xc` and `-xc++` and merge resulting lists\n    includes_cpp = _get_cxx_inc_directories_impl(\n        repository_ctx,\n        cc,\n        True,\n        tachyon_sysroot,\n    )\n    includes_c = _get_cxx_inc_directories_impl(\n        repository_ctx,\n        cc,\n        False,\n        tachyon_sysroot,\n    )\n\n    return includes_cpp + [\n        inc\n        for inc in includes_c\n        if inc not in includes_cpp\n    ]\n\ndef auto_configure_fail(msg):\n    \"\"\"Output failure message when cuda configuration fails.\"\"\"\n    red = \"\\033[0;31m\"\n    no_color = \"\\033[0m\"\n    fail(\"\\n%sCuda Configuration Error:%s %s\\n\" % (red, no_color, msg))\n\n# END cc_configure common functions (see TODO above).\n\ndef _cuda_include_path(repository_ctx, cuda_config):\n    \"\"\"Generates the Starlark string with cuda include directories.\n\n      Args:\n        repository_ctx: The repository context.\n        cc: The path to the gcc host compiler.\n\n      Returns:\n        A list of the gcc host compiler include directories.\n      \"\"\"\n    nvcc_path = repository_ctx.path(\"%s/bin/nvcc%s\" % (\n        cuda_config.cuda_toolkit_path,\n        \".exe\" if cuda_config.cpu_value == \"Windows\" else \"\",\n    ))\n\n    # The expected exit code of this command is non-zero. Bazel remote execution\n    # only caches commands with zero exit code. So force a zero exit code.\n    cmd = \"%s -v /dev/null -o /dev/null ; [ $? -eq 1 ]\" % str(nvcc_path)\n    result = raw_exec(repository_ctx, [get_bash_bin(repository_ctx), \"-c\", cmd])\n    target_dir = \"\"\n    for one_line in err_out(result).splitlines():\n        if one_line.startswith(\"#$ _TARGET_DIR_=\"):\n            target_dir = (\n                cuda_config.cuda_toolkit_path + \"/\" + one_line.replace(\n                    \"#$ _TARGET_DIR_=\",\n                    \"\",\n                ) + \"/include\"\n            )\n    inc_entries = []\n    if target_dir != \"\":\n        inc_entries.append(realpath(repository_ctx, target_dir))\n    inc_entries.append(realpath(repository_ctx, cuda_config.cuda_toolkit_path + \"/include\"))\n    return inc_entries\n\ndef enable_cuda(repository_ctx):\n    \"\"\"Returns whether to build with CUDA support.\"\"\"\n    return int(get_host_environ(repository_ctx, \"TACHYON_NEED_CUDA\", False))\n\ndef matches_version(environ_version, detected_version):\n    \"\"\"Checks whether the user-specified version matches the detected version.\n\n      This function performs a weak matching so that if the user specifies only\n      the\n      major or major and minor versions, the versions are still considered\n      matching\n      if the version parts match. To illustrate:\n\n          environ_version  detected_version  result\n          -----------------------------------------\n          5.1.3            5.1.3             True\n          5.1              5.1.3             True\n          5                5.1               True\n          5.1.3            5.1               False\n          5.2.3            5.1.3             False\n\n      Args:\n        environ_version: The version specified by the user via environment\n          variables.\n        detected_version: The version autodetected from the CUDA installation on\n          the system.\n      Returns: True if user-specified version matches detected version and False\n        otherwise.\n    \"\"\"\n    environ_version_parts = environ_version.split(\".\")\n    detected_version_parts = detected_version.split(\".\")\n    if len(detected_version_parts) < len(environ_version_parts):\n        return False\n    for i, part in enumerate(detected_version_parts):\n        if i >= len(environ_version_parts):\n            break\n        if part != environ_version_parts[i]:\n            return False\n    return True\n\n_NVCC_VERSION_PREFIX = \"Cuda compilation tools, release \"\n\ndef compute_capabilities(repository_ctx):\n    \"\"\"Returns a list of strings representing cuda compute capabilities.\n\n    Args:\n      repository_ctx: the repo rule's context.\n    Returns: list of cuda architectures to compile for. 'compute_xy' refers to\n      both PTX and SASS, 'sm_xy' refers to SASS only.\n    \"\"\"\n    capabilities = get_host_environ(\n        repository_ctx,\n        _TACHYON_CUDA_COMPUTE_CAPABILITIES,\n        \"compute_35,compute_52\",\n    ).split(\",\")\n\n    # Map old 'x.y' capabilities to 'compute_xy'.\n    if len(capabilities) > 0 and all([len(x.split(\".\")) == 2 for x in capabilities]):\n        # If all capabilities are in 'x.y' format, only include PTX for the\n        # highest capability.\n        cc_list = sorted([x.replace(\".\", \"\") for x in capabilities])\n        capabilities = [\"sm_%s\" % x for x in cc_list[:-1]] + [\"compute_%s\" % cc_list[-1]]\n    for i, capability in enumerate(capabilities):\n        parts = capability.split(\".\")\n        if len(parts) != 2:\n            continue\n        capabilities[i] = \"compute_%s%s\" % (parts[0], parts[1])\n\n    # Make list unique\n    capabilities = dict(zip(capabilities, capabilities)).keys()\n\n    # Validate capabilities.\n    for capability in capabilities:\n        if not capability.startswith((\"compute_\", \"sm_\")):\n            auto_configure_fail(\"Invalid compute capability: %s\" % capability)\n        for prefix in [\"compute_\", \"sm_\"]:\n            if not capability.startswith(prefix):\n                continue\n            if len(capability) == len(prefix) + 2 and capability[-2:].isdigit():\n                continue\n            auto_configure_fail(\"Invalid compute capability: %s\" % capability)\n\n    return capabilities\n\ndef lib_name(base_name, cpu_value, version = None, static = False):\n    \"\"\"Constructs the platform-specific name of a library.\n\n      Args:\n        base_name: The name of the library, such as \"cudart\"\n        cpu_value: The name of the host operating system.\n        version: The version of the library.\n        static: True the library is static or False if it is a shared object.\n\n      Returns:\n        The platform-specific name of the library.\n      \"\"\"\n    version = \"\" if not version else \".\" + version\n    if cpu_value in (\"Linux\", \"FreeBSD\"):\n        if static:\n            return \"lib%s.a\" % base_name\n        return \"lib%s.so%s\" % (base_name, version)\n    elif cpu_value == \"Windows\":\n        return \"%s.lib\" % base_name\n    elif cpu_value == \"Darwin\":\n        if static:\n            return \"lib%s.a\" % base_name\n        return \"lib%s%s.dylib\" % (base_name, version)\n    else:\n        auto_configure_fail(\"Invalid cpu_value: %s\" % cpu_value)\n\ndef _lib_path(lib, cpu_value, basedir, version, static):\n    file_name = lib_name(lib, cpu_value, version, static)\n    return \"%s/%s\" % (basedir, file_name)\n\ndef _should_check_soname(version, static):\n    return version and not static\n\ndef _check_cuda_lib_params(lib, cpu_value, basedir, version, static = False):\n    return (\n        _lib_path(lib, cpu_value, basedir, version, static),\n        _should_check_soname(version, static),\n    )\n\ndef _check_cuda_libs(repository_ctx, script_path, libs):\n    python_bin = get_python_bin(repository_ctx)\n    contents = repository_ctx.read(script_path).splitlines()\n\n    cmd = \"from os import linesep;\"\n    cmd += \"f = open('script.py', 'w');\"\n    for line in contents:\n        cmd += \"f.write('%s' + linesep);\" % line\n    cmd += \"f.close();\"\n    cmd += \"from os import system;\"\n    args = \" \".join([\"\\\"\" + path + \"\\\" \" + str(check) for path, check in libs])\n    cmd += \"system('%s script.py %s');\" % (python_bin, args)\n\n    all_paths = [path for path, _ in libs]\n    checked_paths = execute(repository_ctx, [python_bin, \"-c\", cmd]).stdout.splitlines()\n\n    # Filter out empty lines from splitting on '\\r\\n' on Windows\n    checked_paths = [path for path in checked_paths if len(path) > 0]\n    if all_paths != checked_paths:\n        auto_configure_fail(\"Error with installed CUDA libs. Expected '%s'. Actual '%s'.\" % (all_paths, checked_paths))\n\ndef _find_libs(repository_ctx, check_cuda_libs_script, cuda_config):\n    \"\"\"Returns the CUDA libraries on the system.\n\n      Also, verifies that the script actually exist.\n\n      Args:\n        repository_ctx: The repository context.\n        check_cuda_libs_script: The path to a script verifying that the cuda\n          libraries exist on the system.\n        cuda_config: The CUDA config as returned by _get_cuda_config\n\n      Returns:\n        Map of library names to structs of filename and path.\n      \"\"\"\n    cpu_value = cuda_config.cpu_value\n    stub_dir = \"\" if is_windows(repository_ctx) else \"/stubs\"\n\n    check_cuda_libs_params = {\n        \"cuda\": _check_cuda_lib_params(\n            \"cuda\",\n            cpu_value,\n            cuda_config.config[\"cuda_library_dir\"] + stub_dir,\n            version = None,\n            static = False,\n        ),\n        \"cudart\": _check_cuda_lib_params(\n            \"cudart\",\n            cpu_value,\n            cuda_config.config[\"cuda_library_dir\"],\n            cuda_config.cudart_version,\n            static = False,\n        ),\n        \"cudart_static\": _check_cuda_lib_params(\n            \"cudart_static\",\n            cpu_value,\n            cuda_config.config[\"cuda_library_dir\"],\n            cuda_config.cudart_version,\n            static = True,\n        ),\n        \"cublas\": _check_cuda_lib_params(\n            \"cublas\",\n            cpu_value,\n            cuda_config.config[\"cublas_library_dir\"],\n            cuda_config.cublas_version,\n            static = False,\n        ),\n        \"cublasLt\": _check_cuda_lib_params(\n            \"cublasLt\",\n            cpu_value,\n            cuda_config.config[\"cublas_library_dir\"],\n            cuda_config.cublas_version,\n            static = False,\n        ),\n        \"cusolver\": _check_cuda_lib_params(\n            \"cusolver\",\n            cpu_value,\n            cuda_config.config[\"cusolver_library_dir\"],\n            cuda_config.cusolver_version,\n            static = False,\n        ),\n        \"curand\": _check_cuda_lib_params(\n            \"curand\",\n            cpu_value,\n            cuda_config.config[\"curand_library_dir\"],\n            cuda_config.curand_version,\n            static = False,\n        ),\n        \"cufft\": _check_cuda_lib_params(\n            \"cufft\",\n            cpu_value,\n            cuda_config.config[\"cufft_library_dir\"],\n            cuda_config.cufft_version,\n            static = False,\n        ),\n        \"cupti\": _check_cuda_lib_params(\n            \"cupti\",\n            cpu_value,\n            cuda_config.config[\"cupti_library_dir\"],\n            cuda_config.cupti_version,\n            static = False,\n        ),\n        \"cusparse\": _check_cuda_lib_params(\n            \"cusparse\",\n            cpu_value,\n            cuda_config.config[\"cusparse_library_dir\"],\n            cuda_config.cusparse_version,\n            static = False,\n        ),\n    }\n\n    # Verify that the libs actually exist at their locations.\n    _check_cuda_libs(repository_ctx, check_cuda_libs_script, check_cuda_libs_params.values())\n\n    paths = {filename: v[0] for (filename, v) in check_cuda_libs_params.items()}\n    return paths\n\ndef _cudart_static_linkopt(cpu_value):\n    \"\"\"Returns additional platform-specific linkopts for cudart.\"\"\"\n    return \"\" if cpu_value == \"Darwin\" else \"\\\"-lrt\\\",\"\n\ndef _exec_find_cuda_config(repository_ctx, script_path, cuda_libraries):\n    python_bin = get_python_bin(repository_ctx)\n\n    # If used with remote execution then repository_ctx.execute() can't\n    # access files from the source tree. A trick is to read the contents\n    # of the file in Starlark and embed them as part of the command. In\n    # this case the trick is not sufficient as the find_cuda_config.py\n    # script has more than 8192 characters. 8192 is the command length\n    # limit of cmd.exe on Windows. Thus we additionally need to compress\n    # the contents locally and decompress them as part of the execute().\n    compressed_contents = repository_ctx.read(script_path)\n    decompress_and_execute_cmd = (\n        \"from zlib import decompress;\" +\n        \"from base64 import b64decode;\" +\n        \"from os import system;\" +\n        \"script = decompress(b64decode('%s'));\" % compressed_contents +\n        \"f = open('script.py', 'wb');\" +\n        \"f.write(script);\" +\n        \"f.close();\" +\n        \"system('\\\"%s\\\" script.py %s');\" % (python_bin, \" \".join(cuda_libraries))\n    )\n\n    return execute(repository_ctx, [python_bin, \"-c\", decompress_and_execute_cmd])\n\n# TODO(csigg): Only call once instead of from here, tensorrt_configure.bzl,\n# and nccl_configure.bzl.\ndef find_cuda_config(repository_ctx, script_path, cuda_libraries):\n    \"\"\"Returns CUDA config dictionary from running find_cuda_config.py\"\"\"\n    exec_result = _exec_find_cuda_config(repository_ctx, script_path, cuda_libraries)\n    if exec_result.return_code:\n        auto_configure_fail(\"Failed find_cuda_config.py: %s\" % err_out(exec_result))\n\n    # Parse the dict from stdout.\n    return dict([tuple(x.split(\": \")) for x in exec_result.stdout.splitlines()])\n\ndef _get_cuda_config(repository_ctx, find_cuda_config_script):\n    \"\"\"Detects and returns information about the CUDA installation on the system.\n\n      Args:\n        repository_ctx: The repository context.\n\n      Returns:\n        A struct containing the following fields:\n          cuda_toolkit_path: The CUDA toolkit installation directory.\n          cuda_version: The version of CUDA on the system.\n          cudart_version: The CUDA runtime version on the system.\n          compute_capabilities: A list of the system's CUDA compute capabilities.\n          cpu_value: The name of the host operating system.\n      \"\"\"\n    config = find_cuda_config(repository_ctx, find_cuda_config_script, [\"cuda\"])\n    cpu_value = get_cpu_value(repository_ctx)\n    toolkit_path = config[\"cuda_toolkit_path\"]\n\n    is_windows = cpu_value == \"Windows\"\n    cuda_version = config[\"cuda_version\"].split(\".\")\n    cuda_major = cuda_version[0]\n    cuda_minor = cuda_version[1]\n\n    cuda_version = (\"64_%s%s\" if is_windows else \"%s.%s\") % (cuda_major, cuda_minor)\n\n    if int(cuda_major) >= 11:\n        # The libcudart soname in CUDA 11.x is versioned as 11.0 for backward compatability.\n        if int(cuda_major) == 11:\n            cudart_version = \"64_110\" if is_windows else \"11.0\"\n            cupti_version = cuda_version\n        else:\n            cudart_version = (\"64_%s\" if is_windows else \"%s\") % cuda_major\n            cupti_version = cudart_version\n        cublas_version = (\"64_%s\" if is_windows else \"%s\") % config[\"cublas_version\"].split(\".\")[0]\n        cusolver_version = (\"64_%s\" if is_windows else \"%s\") % config[\"cusolver_version\"].split(\".\")[0]\n        curand_version = (\"64_%s\" if is_windows else \"%s\") % config[\"curand_version\"].split(\".\")[0]\n        cufft_version = (\"64_%s\" if is_windows else \"%s\") % config[\"cufft_version\"].split(\".\")[0]\n        cusparse_version = (\"64_%s\" if is_windows else \"%s\") % config[\"cusparse_version\"].split(\".\")[0]\n    elif (int(cuda_major), int(cuda_minor)) >= (10, 1):\n        # cuda_lib_version is for libraries like cuBLAS, cuFFT, cuSOLVER, etc.\n        # It changed from 'x.y' to just 'x' in CUDA 10.1.\n        cuda_lib_version = (\"64_%s\" if is_windows else \"%s\") % cuda_major\n        cudart_version = cuda_version\n        cupti_version = cuda_version\n        cublas_version = cuda_lib_version\n        cusolver_version = cuda_lib_version\n        curand_version = cuda_lib_version\n        cufft_version = cuda_lib_version\n        cusparse_version = cuda_lib_version\n    else:\n        cudart_version = cuda_version\n        cupti_version = cuda_version\n        cublas_version = cuda_version\n        cusolver_version = cuda_version\n        curand_version = cuda_version\n        cufft_version = cuda_version\n        cusparse_version = cuda_version\n\n    return struct(\n        cuda_toolkit_path = toolkit_path,\n        cuda_version = cuda_version,\n        cupti_version = cupti_version,\n        cuda_version_major = cuda_major,\n        cudart_version = cudart_version,\n        cublas_version = cublas_version,\n        cusolver_version = cusolver_version,\n        curand_version = curand_version,\n        cufft_version = cufft_version,\n        cusparse_version = cusparse_version,\n        compute_capabilities = compute_capabilities(repository_ctx),\n        cpu_value = cpu_value,\n        config = config,\n    )\n\ndef _tpl(repository_ctx, tpl, substitutions = {}, out = None):\n    if not out:\n        out = tpl.replace(\":\", \"/\")\n    repository_ctx.template(\n        out,\n        Label(\"//third_party/gpus/%s.tpl\" % tpl),\n        substitutions,\n    )\n\ndef _file(repository_ctx, label):\n    repository_ctx.template(\n        label.replace(\":\", \"/\"),\n        Label(\"//third_party/gpus/%s.tpl\" % label),\n        {},\n    )\n\n_DUMMY_CROSSTOOL_BZL_FILE = \"\"\"\ndef error_gpu_disabled():\n  fail(\"ERROR: Building with --config=cuda but Tachyon is not configured \" +\n       \"to build with GPU support. Please re-run ./configure and enter 'Y' \" +\n       \"at the prompt to build with GPU support.\")\n\n  native.genrule(\n      name = \"error_gen_crosstool\",\n      outs = [\"CROSSTOOL\"],\n      cmd = \"echo 'Should not be run.' && exit 1\",\n  )\n\n  native.filegroup(\n      name = \"crosstool\",\n      srcs = [\":CROSSTOOL\"],\n      output_licenses = [\"unencumbered\"],\n  )\n\"\"\"\n\n_DUMMY_CROSSTOOL_BUILD_FILE = \"\"\"\nload(\"//crosstool:error_gpu_disabled.bzl\", \"error_gpu_disabled\")\n\nerror_gpu_disabled()\n\"\"\"\n\ndef _create_dummy_repository(repository_ctx):\n    cpu_value = get_cpu_value(repository_ctx)\n\n    # Set up BUILD file for cuda/.\n    _tpl(\n        repository_ctx,\n        \"cuda:build_defs.bzl\",\n        {\n            \"%{cuda_is_configured}\": \"False\",\n            \"%{cuda_extra_copts}\": \"[]\",\n            \"%{cuda_gpu_architectures}\": \"[]\",\n        },\n    )\n    _tpl(\n        repository_ctx,\n        \"cuda:BUILD\",\n        {\n            \"%{cuda_driver_lib}\": lib_name(\"cuda\", cpu_value),\n            \"%{cudart_static_lib}\": lib_name(\n                \"cudart_static\",\n                cpu_value,\n                static = True,\n            ),\n            \"%{cudart_static_linkopt}\": _cudart_static_linkopt(cpu_value),\n            \"%{cudart_lib}\": lib_name(\"cudart\", cpu_value),\n            \"%{cublas_lib}\": lib_name(\"cublas\", cpu_value),\n            \"%{cublasLt_lib}\": lib_name(\"cublasLt\", cpu_value),\n            \"%{cusolver_lib}\": lib_name(\"cusolver\", cpu_value),\n            \"%{cufft_lib}\": lib_name(\"cufft\", cpu_value),\n            \"%{curand_lib}\": lib_name(\"curand\", cpu_value),\n            \"%{cupti_lib}\": lib_name(\"cupti\", cpu_value),\n            \"%{cusparse_lib}\": lib_name(\"cusparse\", cpu_value),\n            \"%{cub_actual}\": \":cuda_headers\",\n            \"%{copy_rules}\": \"\"\"\nfilegroup(name=\"cuda-include\")\nfilegroup(name=\"cublas-include\")\nfilegroup(name=\"cusolver-include\")\nfilegroup(name=\"cufft-include\")\nfilegroup(name=\"cusparse-include\")\nfilegroup(name=\"curand-include\")\n\"\"\",\n        },\n    )\n\n    # Create dummy files for the CUDA toolkit since they are still required by\n    # tensorflow/tsl/platform/default/build_config:cuda.\n    repository_ctx.file(\"cuda/cuda/include/cuda.h\")\n    repository_ctx.file(\"cuda/cuda/include/cublas.h\")\n    repository_ctx.file(\"cuda/cuda/extras/CUPTI/include/cupti.h\")\n    repository_ctx.file(\"cuda/cuda/lib/%s\" % lib_name(\"cuda\", cpu_value))\n    repository_ctx.file(\"cuda/cuda/lib/%s\" % lib_name(\"cudart\", cpu_value))\n    repository_ctx.file(\n        \"cuda/cuda/lib/%s\" % lib_name(\"cudart_static\", cpu_value),\n    )\n    repository_ctx.file(\"cuda/cuda/lib/%s\" % lib_name(\"cublas\", cpu_value))\n    repository_ctx.file(\"cuda/cuda/lib/%s\" % lib_name(\"cublasLt\", cpu_value))\n    repository_ctx.file(\"cuda/cuda/lib/%s\" % lib_name(\"cusolver\", cpu_value))\n    repository_ctx.file(\"cuda/cuda/lib/%s\" % lib_name(\"curand\", cpu_value))\n    repository_ctx.file(\"cuda/cuda/lib/%s\" % lib_name(\"cufft\", cpu_value))\n    repository_ctx.file(\"cuda/cuda/lib/%s\" % lib_name(\"cupti\", cpu_value))\n    repository_ctx.file(\"cuda/cuda/lib/%s\" % lib_name(\"cusparse\", cpu_value))\n\n    # Set up cuda_config.h, which is used by\n    # tensorflow/compiler/xla/stream_executor/dso_loader.cc.\n    _tpl(\n        repository_ctx,\n        \"cuda:cuda_config.h\",\n        {\n            \"%{cuda_version}\": \"\",\n            \"%{cudart_version}\": \"\",\n            \"%{cupti_version}\": \"\",\n            \"%{cublas_version}\": \"\",\n            \"%{cusolver_version}\": \"\",\n            \"%{curand_version}\": \"\",\n            \"%{cufft_version}\": \"\",\n            \"%{cusparse_version}\": \"\",\n            \"%{cuda_toolkit_path}\": \"\",\n            \"%{cuda_compute_capabilities}\": \"\",\n        },\n        \"cuda/cuda/cuda_config.h\",\n    )\n\n    # Set up cuda_config.py, which is used by gen_build_info to provide\n    # static build environment info to the API\n    _tpl(\n        repository_ctx,\n        \"cuda:cuda_config.py\",\n        _py_tmpl_dict({}),\n        \"cuda/cuda/cuda_config.py\",\n    )\n\n    # If cuda_configure is not configured to build with GPU support, and the user\n    # attempts to build with --config=cuda, add a dummy build rule to intercept\n    # this and fail with an actionable error message.\n    repository_ctx.file(\n        \"crosstool/error_gpu_disabled.bzl\",\n        _DUMMY_CROSSTOOL_BZL_FILE,\n    )\n    repository_ctx.file(\"crosstool/BUILD\", _DUMMY_CROSSTOOL_BUILD_FILE)\n\ndef _norm_path(path):\n    \"\"\"Returns a path with '/' and remove the trailing slash.\"\"\"\n    path = path.replace(\"\\\\\", \"/\")\n    if path[-1] == \"/\":\n        path = path[:-1]\n    return path\n\ndef make_copy_files_rule(repository_ctx, name, srcs, outs):\n    \"\"\"Returns a rule to copy a set of files.\"\"\"\n    cmds = []\n\n    # Copy files.\n    for src, out in zip(srcs, outs):\n        cmds.append('cp -f \"%s\" \"$(location %s)\"' % (src, out))\n    outs = [('        \"%s\",' % out) for out in outs]\n    return \"\"\"genrule(\n    name = \"%s\",\n    outs = [\n%s\n    ],\n    cmd = \\\"\"\"%s \\\"\"\",\n)\"\"\" % (name, \"\\n\".join(outs), \" && \\\\\\n\".join(cmds))\n\ndef make_copy_dir_rule(repository_ctx, name, src_dir, out_dir, exceptions = None):\n    \"\"\"Returns a rule to recursively copy a directory.\n    If exceptions is not None, it must be a list of files or directories in\n    'src_dir'; these will be excluded from copying.\n    \"\"\"\n    src_dir = _norm_path(src_dir)\n    out_dir = _norm_path(out_dir)\n    outs = read_dir(repository_ctx, src_dir)\n    post_cmd = \"\"\n    if exceptions != None:\n        outs = [x for x in outs if not any([\n            x.startswith(src_dir + \"/\" + y)\n            for y in exceptions\n        ])]\n    outs = [('        \"%s\",' % out.replace(src_dir, out_dir)) for out in outs]\n\n    # '@D' already contains the relative path for a single file, see\n    # http://docs.bazel.build/versions/master/be/make-variables.html#predefined_genrule_variables\n    out_dir = \"$(@D)/%s\" % out_dir if len(outs) > 1 else \"$(@D)\"\n    if exceptions != None:\n        for x in exceptions:\n            post_cmd += \" ; rm -fR \" + out_dir + \"/\" + x\n    return \"\"\"genrule(\n    name = \"%s\",\n    outs = [\n%s\n    ],\n    cmd = \\\"\"\"cp -rLf \"%s/.\" \"%s/\" %s\\\"\"\",\n)\"\"\" % (name, \"\\n\".join(outs), src_dir, out_dir, post_cmd)\n\ndef _flag_enabled(repository_ctx, flag_name):\n    return get_host_environ(repository_ctx, flag_name) == \"1\"\n\ndef _use_cuda_clang(repository_ctx):\n    return _flag_enabled(repository_ctx, \"TACHYON_CUDA_CLANG\")\n\ndef _tachyon_sysroot(repository_ctx):\n    return get_host_environ(repository_ctx, _TACHYON_SYSROOT, \"\")\n\ndef _compute_cuda_extra_copts(repository_ctx, compute_capabilities):\n    copts = [\"--no-cuda-include-ptx=all\"] if _use_cuda_clang(repository_ctx) else []\n    for capability in compute_capabilities:\n        if capability.startswith(\"compute_\"):\n            capability = capability.replace(\"compute_\", \"sm_\")\n            copts.append(\"--cuda-include-ptx=%s\" % capability)\n        copts.append(\"--cuda-gpu-arch=%s\" % capability)\n\n    return str(copts)\n\ndef _tpl_path(repository_ctx, filename):\n    return repository_ctx.path(Label(\"//third_party/gpus/%s.tpl\" % filename))\n\ndef _basename(repository_ctx, path_str):\n    \"\"\"Returns the basename of a path of type string.\n\n    This method is different from path.basename in that it also works if\n    the host platform is different from the execution platform\n    i.e. linux -> windows.\n    \"\"\"\n\n    num_chars = len(path_str)\n    is_win = is_windows(repository_ctx)\n    for i in range(num_chars):\n        r_i = num_chars - 1 - i\n        if (is_win and path_str[r_i] == \"\\\\\") or path_str[r_i] == \"/\":\n            return path_str[r_i + 1:]\n    return path_str\n\ndef _create_local_cuda_repository(repository_ctx):\n    \"\"\"Creates the repository containing files set up to build with CUDA.\"\"\"\n\n    # Resolve all labels before doing any real work. Resolving causes the\n    # function to be restarted with all previous state being lost. This\n    # can easily lead to a O(n^2) runtime in the number of labels.\n    # See https://github.com/tensorflow/tensorflow/commit/62bd353\n    tpl_paths = {filename: _tpl_path(repository_ctx, filename) for filename in [\n        \"cuda:build_defs.bzl\",\n        \"crosstool:clang/bin/crosstool_wrapper_driver_is_not_gcc\",\n        \"crosstool:windows/msvc_wrapper_for_nvcc.py\",\n        \"crosstool:BUILD\",\n        \"crosstool:cc_toolchain_config.bzl\",\n        \"cuda:cuda_config.h\",\n        \"cuda:cuda_config.py\",\n    ]}\n    tpl_paths[\"cuda:BUILD\"] = _tpl_path(repository_ctx, \"cuda:BUILD.windows\" if is_windows(repository_ctx) else \"cuda:BUILD\")\n    find_cuda_config_script = repository_ctx.path(Label(\"@kroma_network_tachyon//third_party/gpus:find_cuda_config.py.gz.base64\"))\n\n    cuda_config = _get_cuda_config(repository_ctx, find_cuda_config_script)\n\n    cuda_include_path = cuda_config.config[\"cuda_include_dir\"]\n    cublas_include_path = cuda_config.config[\"cublas_include_dir\"]\n    cupti_header_dir = cuda_config.config[\"cupti_include_dir\"]\n    nvvm_libdevice_dir = cuda_config.config[\"nvvm_library_dir\"]\n\n    # Create genrule to copy files from the installed CUDA toolkit into execroot.\n    copy_rules = [\n        make_copy_dir_rule(\n            repository_ctx,\n            name = \"cuda-include\",\n            src_dir = cuda_include_path,\n            out_dir = \"cuda/include\",\n        ),\n        make_copy_dir_rule(\n            repository_ctx,\n            name = \"cuda-nvvm\",\n            src_dir = nvvm_libdevice_dir,\n            out_dir = \"cuda/nvvm/libdevice\",\n        ),\n        make_copy_dir_rule(\n            repository_ctx,\n            name = \"cuda-extras\",\n            src_dir = cupti_header_dir,\n            out_dir = \"cuda/extras/CUPTI/include\",\n        ),\n    ]\n\n    copy_rules.append(make_copy_files_rule(\n        repository_ctx,\n        name = \"cublas-include\",\n        srcs = [\n            cublas_include_path + \"/cublas.h\",\n            cublas_include_path + \"/cublas_v2.h\",\n            cublas_include_path + \"/cublas_api.h\",\n            cublas_include_path + \"/cublasLt.h\",\n        ],\n        outs = [\n            \"cublas/include/cublas.h\",\n            \"cublas/include/cublas_v2.h\",\n            \"cublas/include/cublas_api.h\",\n            \"cublas/include/cublasLt.h\",\n        ],\n    ))\n\n    cusolver_include_path = cuda_config.config[\"cusolver_include_dir\"]\n    copy_rules.append(make_copy_files_rule(\n        repository_ctx,\n        name = \"cusolver-include\",\n        srcs = [\n            cusolver_include_path + \"/cusolver_common.h\",\n            cusolver_include_path + \"/cusolverDn.h\",\n        ],\n        outs = [\n            \"cusolver/include/cusolver_common.h\",\n            \"cusolver/include/cusolverDn.h\",\n        ],\n    ))\n\n    cufft_include_path = cuda_config.config[\"cufft_include_dir\"]\n    copy_rules.append(make_copy_files_rule(\n        repository_ctx,\n        name = \"cufft-include\",\n        srcs = [\n            cufft_include_path + \"/cufft.h\",\n        ],\n        outs = [\n            \"cufft/include/cufft.h\",\n        ],\n    ))\n\n    cusparse_include_path = cuda_config.config[\"cusparse_include_dir\"]\n    copy_rules.append(make_copy_files_rule(\n        repository_ctx,\n        name = \"cusparse-include\",\n        srcs = [\n            cusparse_include_path + \"/cusparse.h\",\n        ],\n        outs = [\n            \"cusparse/include/cusparse.h\",\n        ],\n    ))\n\n    curand_include_path = cuda_config.config[\"curand_include_dir\"]\n    copy_rules.append(make_copy_files_rule(\n        repository_ctx,\n        name = \"curand-include\",\n        srcs = [\n            curand_include_path + \"/curand.h\",\n        ],\n        outs = [\n            \"curand/include/curand.h\",\n        ],\n    ))\n\n    check_cuda_libs_script = repository_ctx.path(Label(\"@kroma_network_tachyon//third_party/gpus:check_cuda_libs.py\"))\n    cuda_libs = _find_libs(repository_ctx, check_cuda_libs_script, cuda_config)\n    cuda_lib_srcs = []\n    cuda_lib_outs = []\n    for path in cuda_libs.values():\n        cuda_lib_srcs.append(path)\n        cuda_lib_outs.append(\"cuda/lib/\" + _basename(repository_ctx, path))\n    copy_rules.append(make_copy_files_rule(\n        repository_ctx,\n        name = \"cuda-lib\",\n        srcs = cuda_lib_srcs,\n        outs = cuda_lib_outs,\n    ))\n\n    # copy files mentioned in third_party/nccl/build_defs.bzl.tpl\n    file_ext = \".exe\" if is_windows(repository_ctx) else \"\"\n    bin_files = (\n        [\"crt/link.stub\"] +\n        [f + file_ext for f in [\"bin2c\", \"fatbinary\", \"nvlink\", \"nvprune\"]]\n    )\n    copy_rules.append(make_copy_files_rule(\n        repository_ctx,\n        name = \"cuda-bin\",\n        srcs = [cuda_config.cuda_toolkit_path + \"/bin/\" + f for f in bin_files],\n        outs = [\"cuda/bin/\" + f for f in bin_files],\n    ))\n\n    # Set up BUILD file for cuda/\n    repository_ctx.template(\n        \"cuda/build_defs.bzl\",\n        tpl_paths[\"cuda:build_defs.bzl\"],\n        {\n            \"%{cuda_is_configured}\": \"True\",\n            \"%{cuda_extra_copts}\": _compute_cuda_extra_copts(\n                repository_ctx,\n                cuda_config.compute_capabilities,\n            ),\n            \"%{cuda_gpu_architectures}\": str(cuda_config.compute_capabilities),\n        },\n    )\n\n    cub_actual = \"@cub_archive//:cub\"\n    if int(cuda_config.cuda_version_major) >= 11:\n        cub_actual = \":cuda_headers\"\n\n    repository_ctx.template(\n        \"cuda/BUILD\",\n        tpl_paths[\"cuda:BUILD\"],\n        {\n            \"%{cuda_driver_lib}\": _basename(repository_ctx, cuda_libs[\"cuda\"]),\n            \"%{cudart_static_lib}\": _basename(repository_ctx, cuda_libs[\"cudart_static\"]),\n            \"%{cudart_static_linkopt}\": _cudart_static_linkopt(cuda_config.cpu_value),\n            \"%{cudart_lib}\": _basename(repository_ctx, cuda_libs[\"cudart\"]),\n            \"%{cublas_lib}\": _basename(repository_ctx, cuda_libs[\"cublas\"]),\n            \"%{cublasLt_lib}\": _basename(repository_ctx, cuda_libs[\"cublasLt\"]),\n            \"%{cusolver_lib}\": _basename(repository_ctx, cuda_libs[\"cusolver\"]),\n            \"%{cufft_lib}\": _basename(repository_ctx, cuda_libs[\"cufft\"]),\n            \"%{curand_lib}\": _basename(repository_ctx, cuda_libs[\"curand\"]),\n            \"%{cupti_lib}\": _basename(repository_ctx, cuda_libs[\"cupti\"]),\n            \"%{cusparse_lib}\": _basename(repository_ctx, cuda_libs[\"cusparse\"]),\n            \"%{cub_actual}\": cub_actual,\n            \"%{copy_rules}\": \"\\n\".join(copy_rules),\n        },\n    )\n\n    is_cuda_clang = _use_cuda_clang(repository_ctx)\n    tachyon_sysroot = _tachyon_sysroot(repository_ctx)\n\n    should_download_clang = is_cuda_clang and _flag_enabled(\n        repository_ctx,\n        _TACHYON_DOWNLOAD_CLANG,\n    )\n    if should_download_clang:\n        download_clang(repository_ctx, \"crosstool/extra_tools\")\n\n    # Set up crosstool/\n    cc = find_cc(repository_ctx)\n    cc_fullpath = cc if not should_download_clang else \"crosstool/\" + cc\n\n    host_compiler_includes = get_cxx_inc_directories(\n        repository_ctx,\n        cc_fullpath,\n        tachyon_sysroot,\n    )\n    cuda_defines = {}\n    cuda_defines[\"%{builtin_sysroot}\"] = tachyon_sysroot\n    cuda_defines[\"%{cuda_toolkit_path}\"] = \"\"\n    cuda_defines[\"%{compiler}\"] = \"unknown\"\n    if is_cuda_clang:\n        cuda_defines[\"%{cuda_toolkit_path}\"] = cuda_config.config[\"cuda_toolkit_path\"]\n        cuda_defines[\"%{compiler}\"] = \"clang\"\n\n    host_compiler_prefix = get_host_environ(repository_ctx, _GCC_HOST_COMPILER_PREFIX)\n    if not host_compiler_prefix:\n        host_compiler_prefix = \"/usr/bin\"\n\n    cuda_defines[\"%{host_compiler_prefix}\"] = host_compiler_prefix\n\n    # Bazel sets '-B/usr/bin' flag to workaround build errors on RHEL (see\n    # https://github.com/bazelbuild/bazel/issues/760).\n    # However, this stops our custom clang toolchain from picking the provided\n    # LLD linker, so we're only adding '-B/usr/bin' when using non-downloaded\n    # toolchain.\n    # TODO: when bazel stops adding '-B/usr/bin' by default, remove this\n    #       flag from the CROSSTOOL completely (see\n    #       https://github.com/bazelbuild/bazel/issues/5634)\n    if should_download_clang:\n        cuda_defines[\"%{linker_bin_path}\"] = \"\"\n    else:\n        cuda_defines[\"%{linker_bin_path}\"] = host_compiler_prefix\n\n    cuda_defines[\"%{extra_no_canonical_prefixes_flags}\"] = \"\"\n    cuda_defines[\"%{unfiltered_compile_flags}\"] = \"\"\n    if is_cuda_clang:\n        cuda_defines[\"%{host_compiler_path}\"] = str(cc)\n        cuda_defines[\"%{host_compiler_warnings}\"] = \"\"\"\n        # Some parts of the codebase set -Werror and hit this warning, so\n        # switch it off for now.\n        \"-Wno-invalid-partial-specialization\"\n    \"\"\"\n        cuda_defines[\"%{cxx_builtin_include_directories}\"] = to_list_of_strings(host_compiler_includes)\n        cuda_defines[\"%{compiler_deps}\"] = \":empty\"\n        cuda_defines[\"%{win_compiler_deps}\"] = \":empty\"\n        repository_ctx.file(\n            \"crosstool/clang/bin/crosstool_wrapper_driver_is_not_gcc\",\n            \"\",\n        )\n        repository_ctx.file(\"crosstool/windows/msvc_wrapper_for_nvcc.py\", \"\")\n    else:\n        cuda_defines[\"%{host_compiler_path}\"] = \"clang/bin/crosstool_wrapper_driver_is_not_gcc\"\n        cuda_defines[\"%{host_compiler_warnings}\"] = \"\"\n\n        # nvcc has the system include paths built in and will automatically\n        # search them; we cannot work around that, so we add the relevant cuda\n        # system paths to the allowed compiler specific include paths.\n        cuda_defines[\"%{cxx_builtin_include_directories}\"] = to_list_of_strings(\n            host_compiler_includes + _cuda_include_path(\n                repository_ctx,\n                cuda_config,\n            ) + [cupti_header_dir],\n        )\n\n        # For gcc, do not canonicalize system header paths; some versions of gcc\n        # pick the shortest possible path for system includes when creating the\n        # .d file - given that includes that are prefixed with \"../\" multiple\n        # time quickly grow longer than the root of the tree, this can lead to\n        # bazel's header check failing.\n        cuda_defines[\"%{extra_no_canonical_prefixes_flags}\"] = \"\\\"-fno-canonical-system-headers\\\"\"\n\n        file_ext = \".exe\" if is_windows(repository_ctx) else \"\"\n        nvcc_path = \"%s/nvcc%s\" % (cuda_config.config[\"cuda_binary_dir\"], file_ext)\n        cuda_defines[\"%{compiler_deps}\"] = \":crosstool_wrapper_driver_is_not_gcc\"\n        cuda_defines[\"%{win_compiler_deps}\"] = \":windows_msvc_wrapper_files\"\n\n        wrapper_defines = {\n            \"%{cpu_compiler}\": str(cc),\n            \"%{cuda_version}\": cuda_config.cuda_version,\n            \"%{nvcc_path}\": nvcc_path,\n            \"%{gcc_host_compiler_path}\": str(cc),\n            \"%{nvcc_tmp_dir}\": _get_nvcc_tmp_dir_for_windows(repository_ctx),\n        }\n        repository_ctx.template(\n            \"crosstool/clang/bin/crosstool_wrapper_driver_is_not_gcc\",\n            tpl_paths[\"crosstool:clang/bin/crosstool_wrapper_driver_is_not_gcc\"],\n            wrapper_defines,\n        )\n        repository_ctx.file(\n            \"crosstool/windows/msvc_wrapper_for_nvcc.bat\",\n            content = \"@echo OFF\\n{} -B external/local_config_cuda/crosstool/windows/msvc_wrapper_for_nvcc.py %*\".format(\n                get_python_bin(repository_ctx),\n            ),\n        )\n        repository_ctx.template(\n            \"crosstool/windows/msvc_wrapper_for_nvcc.py\",\n            tpl_paths[\"crosstool:windows/msvc_wrapper_for_nvcc.py\"],\n            wrapper_defines,\n        )\n\n    cuda_defines.update(_get_win_cuda_defines(repository_ctx))\n\n    verify_build_defines(cuda_defines)\n\n    # Only expand template variables in the BUILD file\n    repository_ctx.template(\n        \"crosstool/BUILD\",\n        tpl_paths[\"crosstool:BUILD\"],\n        cuda_defines,\n    )\n\n    # No templating of cc_toolchain_config - use attributes and templatize the\n    # BUILD file.\n    repository_ctx.template(\n        \"crosstool/cc_toolchain_config.bzl\",\n        tpl_paths[\"crosstool:cc_toolchain_config.bzl\"],\n        {},\n    )\n\n    # Set up cuda_config.h, which is used by\n    # tensorflow/compiler/xla/stream_executor/dso_loader.cc.\n    repository_ctx.template(\n        \"cuda/cuda/cuda_config.h\",\n        tpl_paths[\"cuda:cuda_config.h\"],\n        {\n            \"%{cuda_version}\": cuda_config.cuda_version,\n            \"%{cudart_version}\": cuda_config.cudart_version,\n            \"%{cupti_version}\": cuda_config.cupti_version,\n            \"%{cublas_version}\": cuda_config.cublas_version,\n            \"%{cusolver_version}\": cuda_config.cusolver_version,\n            \"%{curand_version}\": cuda_config.curand_version,\n            \"%{cufft_version}\": cuda_config.cufft_version,\n            \"%{cusparse_version}\": cuda_config.cusparse_version,\n            \"%{cuda_toolkit_path}\": cuda_config.cuda_toolkit_path,\n            \"%{cuda_compute_capabilities}\": \", \".join([\n                cc.split(\"_\")[1]\n                for cc in cuda_config.compute_capabilities\n            ]),\n        },\n    )\n\n    # Set up cuda_config.py, which is used by gen_build_info to provide\n    # static build environment info to the API\n    repository_ctx.template(\n        \"cuda/cuda/cuda_config.py\",\n        tpl_paths[\"cuda:cuda_config.py\"],\n        _py_tmpl_dict({\n            \"cuda_version\": cuda_config.cuda_version,\n            \"cuda_compute_capabilities\": cuda_config.compute_capabilities,\n            \"cpu_compiler\": str(cc),\n        }),\n    )\n\ndef _py_tmpl_dict(d):\n    return {\"%{cuda_config}\": str(d)}\n\ndef _create_remote_cuda_repository(repository_ctx, remote_config_repo):\n    \"\"\"Creates pointers to a remotely configured repo set up to build with CUDA.\"\"\"\n    _tpl(\n        repository_ctx,\n        \"cuda:build_defs.bzl\",\n        {\n            \"%{cuda_is_configured}\": \"True\",\n            \"%{cuda_extra_copts}\": _compute_cuda_extra_copts(\n                repository_ctx,\n                compute_capabilities(repository_ctx),\n            ),\n        },\n    )\n    repository_ctx.template(\n        \"cuda/BUILD\",\n        config_repo_label(remote_config_repo, \"cuda:BUILD\"),\n        {},\n    )\n    repository_ctx.template(\n        \"cuda/build_defs.bzl\",\n        config_repo_label(remote_config_repo, \"cuda:build_defs.bzl\"),\n        {},\n    )\n    repository_ctx.template(\n        \"cuda/cuda/cuda_config.h\",\n        config_repo_label(remote_config_repo, \"cuda:cuda/cuda_config.h\"),\n        {},\n    )\n    repository_ctx.template(\n        \"cuda/cuda/cuda_config.py\",\n        config_repo_label(remote_config_repo, \"cuda:cuda/cuda_config.py\"),\n        _py_tmpl_dict({}),\n    )\n\n    repository_ctx.template(\n        \"crosstool/BUILD\",\n        config_repo_label(remote_config_repo, \"crosstool:BUILD\"),\n        {},\n    )\n\n    repository_ctx.template(\n        \"crosstool/cc_toolchain_config.bzl\",\n        config_repo_label(remote_config_repo, \"crosstool:cc_toolchain_config.bzl\"),\n        {},\n    )\n\n    repository_ctx.template(\n        \"crosstool/clang/bin/crosstool_wrapper_driver_is_not_gcc\",\n        config_repo_label(remote_config_repo, \"crosstool:clang/bin/crosstool_wrapper_driver_is_not_gcc\"),\n        {},\n    )\n\ndef _cuda_autoconf_impl(repository_ctx):\n    \"\"\"Implementation of the cuda_autoconf repository rule.\"\"\"\n    build_file = Label(\"//third_party/gpus:local_config_cuda.BUILD\")\n\n    if not enable_cuda(repository_ctx):\n        _create_dummy_repository(repository_ctx)\n    elif get_host_environ(repository_ctx, _TACHYON_CUDA_CONFIG_REPO) != None:\n        has_cuda_version = get_host_environ(repository_ctx, _TACHYON_CUDA_VERSION) != None\n        if not has_cuda_version:\n            auto_configure_fail(\"%s must also be set if %s is specified\" %\n                                (_TACHYON_CUDA_VERSION, _TACHYON_CUDA_CONFIG_REPO))\n        _create_remote_cuda_repository(\n            repository_ctx,\n            get_host_environ(repository_ctx, _TACHYON_CUDA_CONFIG_REPO),\n        )\n    else:\n        _create_local_cuda_repository(repository_ctx)\n\n    repository_ctx.symlink(build_file, \"BUILD\")\n\n# For @bazel_tools//tools/cpp:windows_cc_configure.bzl\n_MSVC_ENVVARS = [\n    \"BAZEL_VC\",\n    \"BAZEL_VC_FULL_VERSION\",\n    \"BAZEL_VS\",\n    \"BAZEL_WINSDK_FULL_VERSION\",\n    \"VS90COMNTOOLS\",\n    \"VS100COMNTOOLS\",\n    \"VS110COMNTOOLS\",\n    \"VS120COMNTOOLS\",\n    \"VS140COMNTOOLS\",\n    \"VS150COMNTOOLS\",\n    \"VS160COMNTOOLS\",\n]\n\n_ENVIRONS = [\n    _GCC_HOST_COMPILER_PATH,\n    _GCC_HOST_COMPILER_PREFIX,\n    _CLANG_CUDA_COMPILER_PATH,\n    \"TACHYON_NEED_CUDA\",\n    \"TACHYON_CUDA_CLANG\",\n    _TACHYON_DOWNLOAD_CLANG,\n    _CUDA_TOOLKIT_PATH,\n    _TACHYON_CUDA_VERSION,\n    _TACHYON_CUDA_COMPUTE_CAPABILITIES,\n    \"NVVMIR_LIBRARY_DIR\",\n    _PYTHON_BIN_PATH,\n    \"TMP\",\n    \"TMPDIR\",\n    \"TACHYON_CUDA_PATHS\",\n] + _MSVC_ENVVARS\n\nremote_cuda_configure = repository_rule(\n    implementation = _create_local_cuda_repository,\n    environ = _ENVIRONS,\n    # NOTE(chokobole): Do we need this?\n    # See https://github.com/google/mediapipe/issues/1744\n    # remotable = True,\n    attrs = {\n        \"environ\": attr.string_dict(),\n    },\n)\n\ncuda_configure = repository_rule(\n    implementation = _cuda_autoconf_impl,\n    environ = _ENVIRONS + [_TACHYON_CUDA_CONFIG_REPO],\n)\n\"\"\"Detects and configures the local CUDA toolchain.\n\nAdd the following to your WORKSPACE FILE:\n\n```python\ncuda_configure(name = \"local_config_cuda\")\n```\n\nArgs:\n  name: A unique name for this workspace rule.\n\"\"\"\n"
  },
  {
    "path": "third_party/gpus/find_cuda_config.py",
    "content": "# Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Prints CUDA library and header directories and versions found on the system.\n\nThe script searches for CUDA library and header files on the system, inspects\nthem to determine their version and prints the configuration to stdout.\nThe paths to inspect and the required versions are specified through environment\nvariables. If no valid configuration is found, the script prints to stderr and\nreturns an error code.\n\nThe list of libraries to find is specified as arguments. Supported libraries are\nCUDA (includes cuBLAS), cuDNN, NCCL, and TensorRT.\n\nThe script takes a list of base directories specified by the TACHYON_CUDA_PATHS\nenvironment variable as comma-separated glob list. The script looks for headers\nand library files in a hard-coded set of subdirectories from these base paths.\nIf TACHYON_CUDA_PATHS is not specified, a OS specific default is used:\n\n  Linux:   /usr/local/cuda, /usr, and paths from 'ldconfig -p'.\n  Windows: CUDA_PATH environment variable, or\n           C:\\\\Program Files\\\\NVIDIA GPU Computing Toolkit\\\\CUDA\\\\*\n\nFor backwards compatibility, some libraries also use alternative base\ndirectories from other environment variables if they are specified. List of\nlibrary-specific environment variables:\n\n  Library   Version env variable       Additional base directories\n  ----------------------------------------------------------------\n  CUDA      TACHYON_CUDA_VERSION       CUDA_TOOLKIT_PATH\n  cuBLAS    TACHYON_CUBLAS_VERSION     CUDA_TOOLKIT_PATH\n  cuDNN     TACHYON_CUDNN_VERSION      CUDNN_INSTALL_PATH\n  NCCL      TACHYON_NCCL_VERSION       NCCL_INSTALL_PATH, NCCL_HDR_PATH\n  TensorRT  TACHYON_TENSORRT_VERSION   TENSORRT_INSTALL_PATH\n\nVersions environment variables can be of the form 'x' or 'x.y' to request a\nspecific version, empty or unspecified to accept any version.\n\nThe output of a found library is of the form:\ntachyon_<library>_version: x.y.z\ntachyon_<library>_header_dir: ...\ntachyon_<library>_library_dir: ...\n\"\"\"\n\nimport io\nimport os\nimport glob\nimport platform\nimport re\nimport subprocess\nimport sys\n\n# pylint: disable=g-import-not-at-top\ntry:\n  from shutil import which\nexcept ImportError:\n  from distutils.spawn import find_executable as which\n# pylint: enable=g-import-not-at-top\n\n\nclass ConfigError(Exception):\n  pass\n\n\ndef _is_linux():\n  return platform.system() == \"Linux\"\n\n\ndef _is_windows():\n  return platform.system() == \"Windows\"\n\n\ndef _is_macos():\n  return platform.system() == \"Darwin\"\n\n\ndef _matches_version(actual_version, required_version):\n  \"\"\"Checks whether some version meets the requirements.\n\n      All elements of the required_version need to be present in the\n      actual_version.\n\n          required_version  actual_version  result\n          -----------------------------------------\n          1                 1.1             True\n          1.2               1               False\n          1.2               1.3             False\n                            1               True\n\n      Args:\n        required_version: The version specified by the user.\n        actual_version: The version detected from the CUDA installation.\n      Returns: Whether the actual version matches the required one.\n  \"\"\"\n  if actual_version is None:\n    return False\n\n  # Strip spaces from the versions.\n  actual_version = actual_version.strip()\n  required_version = required_version.strip()\n  return actual_version.startswith(required_version)\n\n\ndef _at_least_version(actual_version, required_version):\n  actual = [int(v) for v in actual_version.split(\".\")]\n  required = [int(v) for v in required_version.split(\".\")]\n  return actual >= required\n\n\ndef _get_header_version(path, name):\n  \"\"\"Returns preprocessor defines in C header file.\"\"\"\n  for line in io.open(path, \"r\", encoding=\"utf-8\").readlines():\n    match = re.match(\"\\s*#\\s*define %s\\s+(\\d+)\" % name, line)\n    if match:\n      return match.group(1)\n  return \"\"\n\n\ndef _cartesian_product(first, second):\n  \"\"\"Returns all path combinations of first and second.\"\"\"\n  return [os.path.join(f, s) for f in first for s in second]\n\n\ndef _get_ld_config_paths():\n  \"\"\"Returns all directories from 'ldconfig -p'.\"\"\"\n  if not _is_linux():\n    return []\n  ldconfig_path = which(\"ldconfig\") or \"/sbin/ldconfig\"\n  output = subprocess.check_output([ldconfig_path, \"-p\"])\n  pattern = re.compile(\".* => (.*)\")\n  result = set()\n  for line in output.splitlines():\n    try:\n      match = pattern.match(line.decode(\"ascii\"))\n    except UnicodeDecodeError:\n      match = False\n    if match:\n      result.add(os.path.dirname(match.group(1)))\n  return sorted(list(result))\n\n\ndef _get_default_cuda_paths(cuda_version):\n  if not cuda_version:\n    cuda_version = \"*\"\n  elif not \".\" in cuda_version:\n    cuda_version = cuda_version + \".*\"\n\n  if _is_windows():\n    return [\n        os.environ.get(\n            \"CUDA_PATH\",\n            \"C:\\\\Program Files\\\\NVIDIA GPU Computing Toolkit\\\\CUDA\\\\v%s\\\\\" %\n            cuda_version)\n    ]\n  return [\"/usr/local/cuda-%s\" % cuda_version, \"/usr/local/cuda\", \"/usr\",\n         \"/usr/local/cudnn\"] + _get_ld_config_paths()\n\n\ndef _header_paths():\n  \"\"\"Returns hard-coded set of relative paths to look for header files.\"\"\"\n  return [\n      \"\",\n      \"include\",\n      \"include/cuda\",\n      \"include/*-linux-gnu\",\n      \"extras/CUPTI/include\",\n      \"include/cuda/CUPTI\",\n      \"local/cuda/extras/CUPTI/include\",\n  ]\n\n\ndef _library_paths():\n  \"\"\"Returns hard-coded set of relative paths to look for library files.\"\"\"\n  return [\n      \"\",\n      \"lib64\",\n      \"lib\",\n      \"lib/*-linux-gnu\",\n      \"lib/x64\",\n      \"extras/CUPTI/*\",\n      \"local/cuda/lib64\",\n      \"local/cuda/extras/CUPTI/lib64\",\n  ]\n\n\ndef _not_found_error(base_paths, relative_paths, filepattern):\n  base_paths = \"\".join([\"\\n        '%s'\" % path for path in sorted(base_paths)])\n  relative_paths = \"\".join([\"\\n        '%s'\" % path for path in relative_paths])\n  return ConfigError(\n      \"Could not find any %s in any subdirectory:%s\\nof:%s\\n\" %\n      (filepattern, relative_paths, base_paths))\n\n\ndef _find_file(base_paths, relative_paths, filepattern):\n  for path in _cartesian_product(base_paths, relative_paths):\n    for file in glob.glob(os.path.join(path, filepattern)):\n      return file\n  raise _not_found_error(base_paths, relative_paths, filepattern)\n\n\ndef _find_library(base_paths, library_name, required_version):\n  \"\"\"Returns first valid path to the requested library.\"\"\"\n  if _is_windows():\n    filepattern = library_name + \".lib\"\n  elif _is_macos():\n    filepattern = \"%s*.dylib\" % (\".\".join([\"lib\" + library_name] +\n                                          required_version.split(\".\")[:1]))\n  else:\n    filepattern = \".\".join([\"lib\" + library_name, \"so\"] +\n                           required_version.split(\".\")[:1]) + \"*\"\n  return _find_file(base_paths, _library_paths(), filepattern)\n\n\ndef _find_versioned_file(base_paths, relative_paths, filepatterns,\n                         required_version, get_version):\n  \"\"\"Returns first valid path to a file that matches the requested version.\"\"\"\n  if type(filepatterns) not in [list, tuple]:\n    filepatterns = [filepatterns]\n  for path in _cartesian_product(base_paths, relative_paths):\n    for filepattern in filepatterns:\n      for file in glob.glob(os.path.join(path, filepattern)):\n        actual_version = get_version(file)\n        if _matches_version(actual_version, required_version):\n          return file, actual_version\n  raise _not_found_error(\n      base_paths, relative_paths,\n      \", \".join(filepatterns) + \" matching version '%s'\" % required_version)\n\n\ndef _find_header(base_paths, header_name, required_version, get_version):\n  \"\"\"Returns first valid path to a header that matches the requested version.\"\"\"\n  return _find_versioned_file(base_paths, _header_paths(), header_name,\n                              required_version, get_version)\n\n\ndef _find_cuda_config(base_paths, required_version):\n\n  def get_header_version(path):\n    version = int(_get_header_version(path, \"CUDA_VERSION\"))\n    if not version:\n      return None\n    return \"%d.%d\" % (version // 1000, version % 1000 // 10)\n\n  cuda_header_path, header_version = _find_header(base_paths, \"cuda.h\",\n                                                  required_version,\n                                                  get_header_version)\n  cuda_version = header_version  # x.y, see above.\n\n  cuda_library_path = _find_library(base_paths, \"cudart\", cuda_version)\n\n  def get_nvcc_version(path):\n    pattern = \"Cuda compilation tools, release \\d+\\.\\d+, V(\\d+\\.\\d+\\.\\d+)\"\n    for line in subprocess.check_output([path, \"--version\"]).splitlines():\n      match = re.match(pattern, line.decode(\"ascii\"))\n      if match:\n        return match.group(1)\n    return None\n\n  nvcc_name = \"nvcc.exe\" if _is_windows() else \"nvcc\"\n  nvcc_path, nvcc_version = _find_versioned_file(base_paths, [\n      \"\",\n      \"bin\",\n      \"local/cuda/bin\",\n  ], nvcc_name, cuda_version, get_nvcc_version)\n\n  nvvm_path = _find_file(base_paths, [\n      \"nvvm/libdevice\",\n      \"share/cuda\",\n      \"lib/nvidia-cuda-toolkit/libdevice\",\n      \"local/cuda/nvvm/libdevice\",\n  ], \"libdevice*.10.bc\")\n\n  cupti_header_path = _find_file(base_paths, _header_paths(), \"cupti.h\")\n  cupti_library_path = _find_library(base_paths, \"cupti\", required_version)\n\n  cuda_binary_dir = os.path.dirname(nvcc_path)\n  nvvm_library_dir = os.path.dirname(nvvm_path)\n\n  # XLA requires the toolkit path to find ptxas and libdevice.\n  # TODO(csigg): pass in both directories instead.\n  cuda_toolkit_paths = (\n      os.path.normpath(os.path.join(cuda_binary_dir, \"..\")),\n      os.path.normpath(os.path.join(nvvm_library_dir, \"../..\")),\n  )\n  if cuda_toolkit_paths[0] != cuda_toolkit_paths[1]:\n    raise ConfigError(\"Inconsistent CUDA toolkit path: %s vs %s\" %\n                      cuda_toolkit_paths)\n\n  return {\n      \"cuda_version\": cuda_version,\n      \"cuda_include_dir\": os.path.dirname(cuda_header_path),\n      \"cuda_library_dir\": os.path.dirname(cuda_library_path),\n      \"cuda_binary_dir\": cuda_binary_dir,\n      \"nvvm_library_dir\": nvvm_library_dir,\n      \"cupti_include_dir\": os.path.dirname(cupti_header_path),\n      \"cupti_library_dir\": os.path.dirname(cupti_library_path),\n      \"cuda_toolkit_path\": cuda_toolkit_paths[0],\n  }\n\n\ndef _find_cublas_config(base_paths, required_version, cuda_version):\n\n  if _at_least_version(cuda_version, \"10.1\"):\n\n    def get_header_version(path):\n      version = (\n          _get_header_version(path, name)\n          for name in (\"CUBLAS_VER_MAJOR\", \"CUBLAS_VER_MINOR\",\n                       \"CUBLAS_VER_PATCH\"))\n      return \".\".join(version)\n\n    header_path, header_version = _find_header(base_paths, \"cublas_api.h\",\n                                               required_version,\n                                               get_header_version)\n    # cuBLAS uses the major version only.\n    cublas_version = header_version.split(\".\")[0]\n\n  else:\n    # There is no version info available before CUDA 10.1, just find the file.\n    header_version = cuda_version\n    header_path = _find_file(base_paths, _header_paths(), \"cublas_api.h\")\n    # cuBLAS version is the same as CUDA version (x.y).\n    cublas_version = required_version\n\n  library_path = _find_library(base_paths, \"cublas\", cublas_version)\n\n  return {\n      \"cublas_version\": header_version,\n      \"cublas_include_dir\": os.path.dirname(header_path),\n      \"cublas_library_dir\": os.path.dirname(library_path),\n  }\n\n\ndef _find_cusolver_config(base_paths, required_version, cuda_version):\n\n  if _at_least_version(cuda_version, \"11.0\"):\n\n    def get_header_version(path):\n      version = (\n          _get_header_version(path, name)\n          for name in (\"CUSOLVER_VER_MAJOR\", \"CUSOLVER_VER_MINOR\",\n                       \"CUSOLVER_VER_PATCH\"))\n      return \".\".join(version)\n\n    header_path, header_version = _find_header(base_paths, \"cusolver_common.h\",\n                                               required_version,\n                                               get_header_version)\n    cusolver_version = header_version.split(\".\")[0]\n\n  else:\n    header_version = cuda_version\n    header_path = _find_file(base_paths, _header_paths(), \"cusolver_common.h\")\n    cusolver_version = required_version\n\n  library_path = _find_library(base_paths, \"cusolver\", cusolver_version)\n\n  return {\n      \"cusolver_version\": header_version,\n      \"cusolver_include_dir\": os.path.dirname(header_path),\n      \"cusolver_library_dir\": os.path.dirname(library_path),\n  }\n\n\ndef _find_curand_config(base_paths, required_version, cuda_version):\n\n  if _at_least_version(cuda_version, \"11.0\"):\n\n    def get_header_version(path):\n      version = (\n          _get_header_version(path, name)\n          for name in (\"CURAND_VER_MAJOR\", \"CURAND_VER_MINOR\",\n                       \"CURAND_VER_PATCH\"))\n      return \".\".join(version)\n\n    header_path, header_version = _find_header(base_paths, \"curand.h\",\n                                               required_version,\n                                               get_header_version)\n    curand_version = header_version.split(\".\")[0]\n\n  else:\n    header_version = cuda_version\n    header_path = _find_file(base_paths, _header_paths(), \"curand.h\")\n    curand_version = required_version\n\n  library_path = _find_library(base_paths, \"curand\", curand_version)\n\n  return {\n      \"curand_version\": header_version,\n      \"curand_include_dir\": os.path.dirname(header_path),\n      \"curand_library_dir\": os.path.dirname(library_path),\n  }\n\n\ndef _find_cufft_config(base_paths, required_version, cuda_version):\n\n  if _at_least_version(cuda_version, \"11.0\"):\n\n    def get_header_version(path):\n      version = (\n          _get_header_version(path, name)\n          for name in (\"CUFFT_VER_MAJOR\", \"CUFFT_VER_MINOR\", \"CUFFT_VER_PATCH\"))\n      return \".\".join(version)\n\n    header_path, header_version = _find_header(base_paths, \"cufft.h\",\n                                               required_version,\n                                               get_header_version)\n    cufft_version = header_version.split(\".\")[0]\n\n  else:\n    header_version = cuda_version\n    header_path = _find_file(base_paths, _header_paths(), \"cufft.h\")\n    cufft_version = required_version\n\n  library_path = _find_library(base_paths, \"cufft\", cufft_version)\n\n  return {\n      \"cufft_version\": header_version,\n      \"cufft_include_dir\": os.path.dirname(header_path),\n      \"cufft_library_dir\": os.path.dirname(library_path),\n  }\n\n\ndef _find_cudnn_config(base_paths, required_version):\n\n  def get_header_version(path):\n    version = [\n        _get_header_version(path, name)\n        for name in (\"CUDNN_MAJOR\", \"CUDNN_MINOR\", \"CUDNN_PATCHLEVEL\")]\n    return \".\".join(version) if version[0] else None\n\n  header_path, header_version = _find_header(base_paths,\n                                             (\"cudnn.h\", \"cudnn_version.h\"),\n                                             required_version,\n                                             get_header_version)\n  cudnn_version = header_version.split(\".\")[0]\n\n  library_path = _find_library(base_paths, \"cudnn\", cudnn_version)\n\n  return {\n      \"cudnn_version\": cudnn_version,\n      \"cudnn_include_dir\": os.path.dirname(header_path),\n      \"cudnn_library_dir\": os.path.dirname(library_path),\n  }\n\n\ndef _find_cusparse_config(base_paths, required_version, cuda_version):\n\n  if _at_least_version(cuda_version, \"11.0\"):\n\n    def get_header_version(path):\n      version = (\n          _get_header_version(path, name)\n          for name in (\"CUSPARSE_VER_MAJOR\", \"CUSPARSE_VER_MINOR\",\n                       \"CUSPARSE_VER_PATCH\"))\n      return \".\".join(version)\n\n    header_path, header_version = _find_header(base_paths, \"cusparse.h\",\n                                               required_version,\n                                               get_header_version)\n    cusparse_version = header_version.split(\".\")[0]\n\n  else:\n    header_version = cuda_version\n    header_path = _find_file(base_paths, _header_paths(), \"cusparse.h\")\n    cusparse_version = required_version\n\n  library_path = _find_library(base_paths, \"cusparse\", cusparse_version)\n\n  return {\n      \"cusparse_version\": header_version,\n      \"cusparse_include_dir\": os.path.dirname(header_path),\n      \"cusparse_library_dir\": os.path.dirname(library_path),\n  }\n\n\ndef _find_nccl_config(base_paths, required_version):\n\n  def get_header_version(path):\n    version = (\n        _get_header_version(path, name)\n        for name in (\"NCCL_MAJOR\", \"NCCL_MINOR\", \"NCCL_PATCH\"))\n    return \".\".join(version)\n\n  header_path, header_version = _find_header(base_paths, \"nccl.h\",\n                                             required_version,\n                                             get_header_version)\n  nccl_version = header_version.split(\".\")[0]\n\n  library_path = _find_library(base_paths, \"nccl\", nccl_version)\n\n  return {\n      \"nccl_version\": nccl_version,\n      \"nccl_include_dir\": os.path.dirname(header_path),\n      \"nccl_library_dir\": os.path.dirname(library_path),\n  }\n\n\ndef _find_tensorrt_config(base_paths, required_version):\n\n  def get_header_version(path):\n    version = (\n        _get_header_version(path, name)\n        for name in (\"NV_TENSORRT_MAJOR\", \"NV_TENSORRT_MINOR\",\n                     \"NV_TENSORRT_PATCH\"))\n    # `version` is a generator object, so we convert it to a list before using\n    # it (muitiple times below).\n    version = list(version)\n    if not all(version):\n      return None  # Versions not found, make _matches_version returns False.\n    return \".\".join(version)\n\n  header_path, header_version = _find_header(base_paths, \"NvInferVersion.h\",\n                                             required_version,\n                                             get_header_version)\n\n  tensorrt_version = header_version.split(\".\")[0]\n  library_path = _find_library(base_paths, \"nvinfer\", tensorrt_version)\n\n  return {\n      \"tensorrt_version\": tensorrt_version,\n      \"tensorrt_include_dir\": os.path.dirname(header_path),\n      \"tensorrt_library_dir\": os.path.dirname(library_path),\n  }\n\n\ndef _list_from_env(env_name, default=[]):\n  \"\"\"Returns comma-separated list from environment variable.\"\"\"\n  if env_name in os.environ:\n    return os.environ[env_name].split(\",\")\n  return default\n\n\ndef _get_legacy_path(env_name, default=[]):\n  \"\"\"Returns a path specified by a legacy environment variable.\n\n  CUDNN_INSTALL_PATH, NCCL_INSTALL_PATH, TENSORRT_INSTALL_PATH set to\n  '/usr/lib/x86_64-linux-gnu' would previously find both library and header\n  paths. Detect those and return '/usr', otherwise forward to _list_from_env().\n  \"\"\"\n  if env_name in os.environ:\n    match = re.match(\"^(/[^/ ]*)+/lib/\\w+-linux-gnu/?$\", os.environ[env_name])\n    if match:\n      return [match.group(1)]\n  return _list_from_env(env_name, default)\n\n\ndef _normalize_path(path):\n  \"\"\"Returns normalized path, with forward slashes on Windows.\"\"\"\n  path = os.path.realpath(path)\n  if _is_windows():\n    path = path.replace(\"\\\\\", \"/\")\n  return path\n\n\ndef find_cuda_config():\n  \"\"\"Returns a dictionary of CUDA library and header file paths.\"\"\"\n  libraries = [argv.lower() for argv in sys.argv[1:]]\n  cuda_version = os.environ.get(\"TACHYON_CUDA_VERSION\", \"\")\n  base_paths = _list_from_env(\"TACHYON_CUDA_PATHS\",\n                              _get_default_cuda_paths(cuda_version))\n  base_paths = [path for path in base_paths if os.path.exists(path)]\n\n  result = {}\n  if \"cuda\" in libraries:\n    cuda_paths = _list_from_env(\"CUDA_TOOLKIT_PATH\", base_paths)\n    result.update(_find_cuda_config(cuda_paths, cuda_version))\n\n    cuda_version = result[\"cuda_version\"]\n    cublas_paths = base_paths\n    if tuple(int(v) for v in cuda_version.split(\".\")) < (10, 1):\n      # Before CUDA 10.1, cuBLAS was in the same directory as the toolkit.\n      cublas_paths = cuda_paths\n    cublas_version = os.environ.get(\"TACHYON_CUBLAS_VERSION\", \"\")\n    result.update(\n        _find_cublas_config(cublas_paths, cublas_version, cuda_version))\n\n    cusolver_paths = base_paths\n    if tuple(int(v) for v in cuda_version.split(\".\")) < (11, 0):\n      cusolver_paths = cuda_paths\n    cusolver_version = os.environ.get(\"TACHYON_CUSOLVER_VERSION\", \"\")\n    result.update(\n        _find_cusolver_config(cusolver_paths, cusolver_version, cuda_version))\n\n    curand_paths = base_paths\n    if tuple(int(v) for v in cuda_version.split(\".\")) < (11, 0):\n      curand_paths = cuda_paths\n    curand_version = os.environ.get(\"TACHYON_CURAND_VERSION\", \"\")\n    result.update(\n        _find_curand_config(curand_paths, curand_version, cuda_version))\n\n    cufft_paths = base_paths\n    if tuple(int(v) for v in cuda_version.split(\".\")) < (11, 0):\n      cufft_paths = cuda_paths\n    cufft_version = os.environ.get(\"TACHYON_CUFFT_VERSION\", \"\")\n    result.update(_find_cufft_config(cufft_paths, cufft_version, cuda_version))\n\n    cusparse_paths = base_paths\n    if tuple(int(v) for v in cuda_version.split(\".\")) < (11, 0):\n      cusparse_paths = cuda_paths\n    cusparse_version = os.environ.get(\"TACHYON_CUSPARSE_VERSION\", \"\")\n    result.update(\n        _find_cusparse_config(cusparse_paths, cusparse_version, cuda_version))\n\n  if \"cudnn\" in libraries:\n    cudnn_paths = _get_legacy_path(\"CUDNN_INSTALL_PATH\", base_paths)\n    cudnn_version = os.environ.get(\"TACHYON_CUDNN_VERSION\", \"\")\n    result.update(_find_cudnn_config(cudnn_paths, cudnn_version))\n\n  if \"nccl\" in libraries:\n    nccl_paths = _get_legacy_path(\"NCCL_INSTALL_PATH\", base_paths)\n    nccl_version = os.environ.get(\"TACHYON_NCCL_VERSION\", \"\")\n    result.update(_find_nccl_config(nccl_paths, nccl_version))\n\n  if \"tensorrt\" in libraries:\n    tensorrt_paths = _get_legacy_path(\"TENSORRT_INSTALL_PATH\", base_paths)\n    tensorrt_version = os.environ.get(\"TACHYON_TENSORRT_VERSION\", \"\")\n    result.update(_find_tensorrt_config(tensorrt_paths, tensorrt_version))\n\n  for k, v in result.items():\n    if k.endswith(\"_dir\") or k.endswith(\"_path\"):\n      result[k] = _normalize_path(v)\n\n  return result\n\n\ndef main():\n  try:\n    for key, value in sorted(find_cuda_config().items()):\n      print(\"%s: %s\" % (key, value))\n  except ConfigError as e:\n    sys.stderr.write(str(e) + '\\n')\n    sys.exit(1)\n\n\nif __name__ == \"__main__\":\n  main()\n"
  },
  {
    "path": "third_party/gpus/find_cuda_config.py.gz.base64",
    "content": "eJzdPGtz48aR3/krJtjbEqilIMmXcuV4kVOytHvWRZG2JO76UhLDjIAhBS8I8ACQEpPKf093zwODAUC9LHvLqvKaAKZ7unv6Oa837ChbrPN4dluyb/b2/4uNbgUbibTI8g9JdscOl+VtlhcBO0wSdoHNCnYhCpGvRBT03vTesNM4hOYiYss0EjkrAf5wwUP4n/oyYJ9FXsRZyr4J9piPDTz1yev/N2BYZ0s252uWZiVbFgJQxAWbxolg4j4Ui5LFKQuz+SKJeRoKdheXt9SNQgJksL8qFNlNyaE1h/YLeJra7RgviWD8uy3LxXB39+7uLuBEbJDls91ENix2T0+O3p9dvt8BggnkU5qIomC5+P9lnAOrN2vGF0BPyG+AyoTfsSxnfJYL+FZmSO9dHpdxOhuwIpuWdzwXgCWKizKPb5ZlTViaOuDZbgDi4inzDi/ZyaXHvj+8PLkcAI4fT0Y/nH8asR8PLy4Oz0Yn7y/Z+QU7Oj87PhmdnJ/B0wd2ePZX9ueTs+MBEyAq6EbcL3KkH4iMUYw0dOxSiBoB00wSVCxEGE/jEPhKZ0s+E2yWrUSeAjtsIfJ5XOBgFkBeBFiSeB6XvKQ3Daawm4Of9a/ned7HPE5BDY8+HR9C9zc5z9dIDLsVHPuPYIjCMstjQTSyldQ+UKkMCETBEpfrohTzoNdDhS/CPAY9KwTPQRcKEkUXelTMoo5lACOOUiuLHrycowpEokRRpSTiONdEEKKFpB/hwyydxrNlTgJEuKKMsmUZEFULXt4WUp8IOwEjlNFDwxoomB43VMHbPFvObplIV3GepXORlr0Vz2PUVjDlkymYGlvxJI4cAmIlpIFkTkpFk0vEiTyngc9FucxJCRi8AnGFWSSUNBNQY7Q9KTwcBoCdxkA84K+o5Ej2bInUAVGXy8Uiy1HzKzA0GxoGP07DZBnBq3D5/enhZX8AP47Pzgbs7OjodECCkU7rYlQf0pJ/QUSGphsOmm5rSEUPGDVyPfowwT4nHw9HP1z2LBEyLUKkHPzRnO8UYsFBdAA8S7Ib6iRgVu9Jln2R2iSVp+ghpVqppCaRu7rlebSDIoxACYnQYnljkznNszmSB9QTC6QbQQ+GskYvShjdqOEKZMPOLyubjsSUL5MS24GrjYa9HgNjTZf3Q/CKu8si302ykCe74TLiA3ohpSt1kajYSiKpNWxnsRUA/I8wtNldMWSGDtYmtgH4nx6r/o6G19cf82yW8zn7gKK4vj77fHJ8csj+5+MnCEvzxRJdKBtlWfIlLq+vEf319Xav9wEEesPDL+BXIxoKoC6+iZO4XKPDnQtbh5Iio6jCE7DIFBqupAh7DfFm0lm2kA6jRKFkXTe0AERHatVTQ7pj5NyKRUlbjj4zYRHaVrrFDqMoRmPkSUNZAXrnhX+AgiyK/rTqfH5/cQnBQ48Lvhqdn5/++WREowkw0uwMDD7UoNphwEKtfs7O6h3JVydnl6PD01MNhPZsiMMHhzh6ZcNIFzD54fhCo9COgFCMII6fX1yMLDTmVa3r3mftS9sVIARPdyN0SgEmDYZwv4Uhdes+WG+hi0O3LEAfeM+ogXLQEInni3KNrZep5aczxkPKcHi61m2V/4IoAPqP/XEVuLTbANO1qBj2yunkj+rbdxOFZMiAqOAf9W/SB01AoYYsCIL6R/Wj+gqBtteDdAGcMosz/Ssr9C/0ePr3IuElEqOfwW+rX+DGFnkWQvZh3qyLHqQFi3UCYWWIOQ8K+GC2I7/vgPva4eVOmS16Zb4ewoCScRa34A0SppDc3cbhbU+lhyf07j2GIdMcUykEKIJiwe9SDYdRaCLuRbgstSeXqCqCRNpJT68XJhzyqCNyf9Sh/55oAJH3se8FfIZm4GXZJC5AqOBZffoiA6aRVCAzB78P+RGmw9DOswDvpEt9BKhyvjbwnIfZY0CPeQ79GMg5LzH50Srk87Bc8mRiVFhnHfoNdQBacnQrwi8oR0H+k/yvTnfmQqhMR0HLaN9TcQCLCpHIl1qp3W5YqrJqsD5MY9EsY8q+FJI6nQY3/jVwOY2xRQHh0AJ5iifVf/vM/dsP6u9G+VLYAME3LoDz/AHi1gMQwX9uhGj+uX0QUXok8lkxNNCu3IaU1GihNVImCK95YIDrIq6DYlocYr6k8xkZjSDBLXmSUBKqEV3IDHPIflSKha0l7kq9pMrWk+IsFYHUTPgX4rYz5OA+z6CJZFbZh5QdvIGqCCqwBbDIQyvrMok2InbwHbgKiDXcwu/3WtTvoPGq1ppoaWDjeVlgzes3DFBbLi8nieBF+TTTVbI8YFfg9/xVn/LUFeWjDglQMpa+F3j9scVUG2CTOwfU4pB9V0lDMzITpY5SmhVMPAcs5XOh/Y1SDHQGKrRA9wAOxRZl00d2pRZINUAKE6zG4HucBdlCaMxe7kF0TiH3hlzzwFuW050/eP0gBxQIoPwok6pGAxjQT9+7LrbfwH+yZ/a2uC7e+dfRu77H3hK9A+qxT9CghgSlTUxJgt4FM6jWFv6+pQOeccohjL4oYp5OgNdoGZb+NM6LEtJcAUl45MoEjIhSdUyKb+JU1eXgWQmKMnkJqOSi+rvKigDBgp+yOPWngF0O6hTlJUHxkcQr4cf2kCXRRFYEEyoT/DaqGtl2vYww1orVixM6KzJRiTQY9QUDQvHb9/Rrr49ZlrdbAPu75iXAqYTqwEpJghBD10R+8a9qmEEzdhbeuE9BvcTSQQ4+VhugV6DT2+zgO+YH231PjhwGEkQvSjJnW+VkD9IaalqlMhxbwVRvSsuwdRAJLA19jxdhHHt9qVIq+fmUxvjxmJqYHMhGWAWGphYizQGPIl8rAAwT6q5f18y+pZsFVek+1rq+RNDv28qgKswJVpBKH+in7XrUONvvJU32GyDd28aRE4lqD36E5gIfAqs9vgMwQCN7beRWlW6Z+AWiUCVAAPz4tZDqmRLXGzgfnlfNrsBvXIPHqCGryYu+WN7zynPK9J23BbocGwiU12nkqVc22U6bNPXGIK12i9YjrLxzu503ZzFykciK28xl4ZSINSMiJ0Ecb6RI9Ayxnpr/abxQvLlvt3fIfezM0mX1UdyXOS92jz59HJ3sbkQo21TfKjHudiIxDlEXUj+DiGoTRQ/KCFp/+/vaY+2hXSj44d4GqzG43SoDt6MO6VTNjGzAiCdUzE5o0tDHCQ4pqIGRg35GppUvJCFWbdExeDJUXXnXqVbnrbfFFhoCxQUUH/2IjceqEPTH0p/ZHT4VaR16bDlIuzrUMjrKlklEPozmQbHcfyun/uCXNdW3HoI/SLMp/a9yC74ljKakLMaMnVKhi1BPkrHNYEvq0Y1K+VLKGXDhBuBxfiDAf/xaciFjq91x30mK8BtKk8eFeL7K1AShDKkGrO1Upmpdxa22W5kEyTlzkhDYqa46RFFNW6+rTKYl1lgEgr7ZFFCYQpPV8c4p5F1Y722xHURrhADtxAxb6y69eldDDm59Y2FY/9uQxF8N98eUCwhIKVrJ2kgHxKAi8x6g5qHuUVLblifsUHXXD2/QDtWReJrBFINuLlweBgyD6hNUi0szKm952Shypbpp6Rh1K9cLYfsJSOHR34AlXmGuNmDlcpGIcWPU0PNd2c/jn9ERaM2I01qX2uJf5i9aSnFLzCSLvmkaP3eGqxpT450GTsfd3krBb1ApHSHANlT1VRtCUHapAJg7aj51UOqcECC1lglWbaxU9tbu8p6hpCqHe7Sa1kx2g9k5aWad8Adc2Wa2ahKilFnmuY5KN/QAOkW4jgkKpSiVHuLESPdshmevuuh6TtU4tbrGSAxnrOxqxXsbBW8jcv26091dtr+3tzcwVLylZ/m+jwwQu5ZkjVgrujs1x0Pg4NZ7SPqPGpFn4GjKst9r1H0OO+wNroLgVIlg/CZbicBIwQ4Ohu22LIH4zktv4BRllj6kqzBs0wYrKh4BLJMzB3qlP0ukLxC4xncdvbsO4J8B++zr3/RP3zPuVM8ldE5f6FmLHUWLN+63zDi0zGSZxLJ7sqE5d9A9h1VXWnhBAqIkB0SBD4G4F14jRaKkQjbwNJSa/bMkbEZrg/toKY5u4rS1YtHvx4OKzIFTS7uD3JdMreZ19ekmA9tiORSJVRxaNWcB1aBbwmJBlq7iKOY7VNyXcr6gDdxio6UH4Mgzb7aD/b3gJvSUH1iUse0IulloOGKPgMER9A2ip9gStPda/KuxS5yzlIuQgMmdkzIa0dfytxYtW9urIerLqf3/Oz3UXcswpWRrAhrVZovynsuNQ0Z6AYGPzo/P/bCIZ7P+kBb60BpvMgC15zZxKQNkFmiOVB+myNQpgSY2zXLcvnBbz3ocYWByAPlvf/AoYFc0BL1rEPRlvtik7mpvzH530PZhX6WNMsuxC1zvJIUAWkCCietytJhjS3WIZe6qYDRF1eH2m/3ReCkn8k+t7bZNesO6idbaqFkZ5BzauUrhBsF+HdgSWxewre8OdDVimkJrDG1v4PTTGLEKK1rYQxw55tx3wB/iyTVihyl7aDRbrt4gxL+c3Oom4cVjsisntA71VG1jVcuZ4QSPtu/J5o9Jzuz0zJ7UfWDRyWqJUZjCGNi971W7YCZ/Ofzf8wuP8rrq3ckZvutKdeymHw9HRz9UkVaneLqUtp0kY8/P32g8+CJ+Thb34hSuPX9Dt6q2Fi0L5ZTn/Kes2i+ZpclarggrBrqyPXuaYG/cq81RvME16FzIzXEGdZxOoYZZccjJcCvIjYDxVevRqFoD9tOyUPN1tNkGVxPtIWhfb3AH6Wmx1RojRz7W8jXtzUQ95GoTrP7mQ7rb75CWO4IooadEbkRHWbCNt8NR203AY9TlNXDabXZuHW6NIDf7tYZHc/1TkSVA0qt6qP1g71f1UJfnp+hgHB9lv33QS1mNfyE/ZcZlPgez/pqclaHtOU7oFb2GK7FOal/qBCRCcgN13B2OoN5okytQLZ/lDBTsC91BztPoN+0MLg7Pjl1XUL170BGYpr+QG8Dx+Mqsn1TkK7N9JaYOEl9q8IiOzN3G22HsdpNNpk7tnmXoBPlCM59Oy9+0lX/4MHKN3LySNm6/+oVsGYT+lZkyqsFXZslSSO0EvtSOARuZsYW1w4qtFpuMGJs9y4YR8IUmHKXp66zaVHvAHmt/rvXhiRbL8uixsjp8JIs7ff/5/ancE9ttdOhv1G+cmaPZcT2l/jxTfJr5+B6JGg2XyZ/GQEBNn4jshabcufxTEfUIM37Sqk+aykWfqoeuacmqhZweq54H9VbPMhgEfGmlu+A5MPdbDnuXHw8vLt83Kl3r7cOVbtX4l6p0aVy+ssiolOUrC45GVJ1kvri2JYSytq3h7qpta4021ray5fNqWwn7IgeQhmHyOgGzst3nBUw6umnsVT7pcElPNUPcZIbPNUKUzdMN8FWiGQ3TawQzRAwStfG36rTdABelrMdBrc0zNJngXqTFJR3rzR9Vvf0Kmvy5OmpcKbT9clP8qbWs6fwb9ndFxN9x6p8DQ6nIeQmdZzc/iRBPAmXsjm6SgIYli0u5NYsuPFBrGssiTmcKHXz358u4jBe4vzCeiwJaJdmdWjeoJEIHPGqxQe1Q4kniV3LWBmF2fGAnn+VneReBulFizr+IxhY8pu+RoEMqwauZ+dnqJJ2K/LPJX399gwcMRqUfafVPMvpVjByDGrq9tNq+2wjs0301aLR9hh8wsM/1BaiWEzxANhHpyof/1JYddezo4Grsbl10r+ogy6AjaG0n/asNtRo3HeAyZ4Jqh4aq11e69ViP2cCzzgQo6mqH5sSMh5LDR7HB5TaV2uFbMHPC0s4JjnPzkoVB2x0KrXci0AmVMgMsW/KkEJ4V+cO3k29/X50k2WJ3dLRhkYtVnC2LZC1XTGlfTPP2nB5TN5ewYzoJzMrbDO/mgAZKUtTV1kBexnEXy3uJ8KIPdGvO6Pdr53w3jVfz9Obf/N2rv+2y8Xb/HTF2ffeu4mr3T/8BhtM2uhuPc17V98JZh7YeUtt+dUImn/Mk/oc05CpWWYpgmsiduAN5IZaWUpHw4lZeUKSuBlAarVyGtrRc8KTqotd1YkFBKZBFwkPhe9fXdJzMVnBsoHhobK1tanIUh3TFSU53dG26aUmpi2ShutXlgF3xfLYKIHCBq5eHVfEFbZBcFwH+vtofjse9xiZR53if51yCgpwRY7XzRs74ebVLdx4MJo86F9nokzZ01nbiW59htPRIinugrZDjOJa+XR1F/ee/5LjSPh46OmkkaJ2b7OKxcaeLVztqpPwgnSBdLiJwrX5zV3XVgTOzoGpnZ2wkuqv6Xq+xaklr/prYihBtkHS8wXdPpNuYrGjaZ39k/v7egO2bFOYN+76xA0TtvbjjhbpyQu67MKe1cAeGtZFQX13g0FoJwebkAY20r9gxOukKvEpaW/Zc2VS4Gze6RkMtpf6sUgY57hkpN7poCKexaN0qnmpbwtMEVN/0UaemuazdJSZaiHpNIdU6aIjIWeRrFZBern2aeOxFcJsKdwmwSzA4u/+acrHxN8RSXzJplYpa99oolJaVQqtjZxWl05DkvM2rGlK9i6YhuTNk7YZkZj2faEi1OeU6Nc05tDYxqcCUph2RKU2r0OQmzF4zr22JTu76QHvor+4le1AlrJUni0B3mcBwRxMvLczRZEg3b438vIU1Z7KohTP78rQHGLMnCCvanBkjw5Uu4do4M+VdN3etpUYLhy3FcQuX7v1uD3DqTiLV6W2plYlrNM8vA33FDWGNSzE3STJI5QtQFslrejyqaekaktpb7MKzZkwo1fkyRiE5Wf/KLtHVDVUyt57zOJW9mptDiDixHuDhvKWwzrs383BNtSGCrvb0vbfFUO7QZ36FSZ7zlZeMWDv+MeVR0/2YaMsrQQO87lb4RZn7As8sbl2nW33TBhLUEo8G9XpYY1DhM5nQDWSTCTI0mXiIUPLW+zetrxKs"
  },
  {
    "path": "third_party/gpus/find_rocm_config.py",
    "content": "# Copyright 2020 The TensorFlow Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# ==============================================================================\n\"\"\"Prints ROCm library and header directories and versions found on the system.\n\nThe script searches for ROCm library and header files on the system, inspects\nthem to determine their version and prints the configuration to stdout.\nThe path to inspect is specified through an environment variable (ROCM_PATH).\nIf no valid configuration is found, the script prints to stderr and\nreturns an error code.\n\nThe script takes the directory specified by the ROCM_PATH environment variable.\nThe script looks for headers and library files in a hard-coded set of\nsubdirectories from base path of the specified directory. If ROCM_PATH is not\nspecified, then \"/opt/rocm\" is used as it default value\n\n\"\"\"\n\nimport io\nimport os\nimport re\nimport sys\n\n\nclass ConfigError(Exception):\n  pass\n\n\ndef _get_default_rocm_path():\n  return \"/opt/rocm\"\n\n\ndef _get_rocm_install_path():\n  \"\"\"Determines and returns the ROCm installation path.\"\"\"\n  rocm_install_path = _get_default_rocm_path()\n  if \"ROCM_PATH\" in os.environ:\n    rocm_install_path = os.environ[\"ROCM_PATH\"]\n  # rocm_install_path = os.path.realpath(rocm_install_path)\n  return rocm_install_path\n\n\ndef _get_composite_version_number(major, minor, patch):\n  return 10000 * major + 100 * minor + patch\n\n\ndef _get_header_version(path, name):\n  \"\"\"Returns preprocessor defines in C header file.\"\"\"\n  for line in io.open(path, \"r\", encoding=\"utf-8\"):\n    match = re.match(r\"#define %s +(\\d+)\" % name, line)\n    if match:\n      value = match.group(1)\n      return int(value)\n\n  raise ConfigError('#define \"{}\" is either\\n'.format(name) +\n                    \"  not present in file {} OR\\n\".format(path) +\n                    \"  its value is not an integer literal\")\n\n\ndef _find_rocm_config(rocm_install_path):\n\n  def rocm_version_numbers(path):\n    possible_version_files = [\n        \"include/rocm-core/rocm_version.h\",  # ROCm 5.2\n        \"include/rocm_version.h\",  # ROCm 5.1 and prior\n    ]\n    version_file = None\n    for f in possible_version_files:\n      version_file_path = os.path.join(path, f)\n      if os.path.exists(version_file_path):\n        version_file = version_file_path\n        break\n    if not version_file:\n      raise ConfigError(\n          \"ROCm version file not found in {}\".format(possible_version_files))\n\n    major = _get_header_version(version_file, \"ROCM_VERSION_MAJOR\")\n    minor = _get_header_version(version_file, \"ROCM_VERSION_MINOR\")\n    patch = _get_header_version(version_file, \"ROCM_VERSION_PATCH\")\n    return major, minor, patch\n\n  major, minor, patch = rocm_version_numbers(rocm_install_path)\n\n  rocm_config = {\n      \"rocm_version_number\": _get_composite_version_number(major, minor, patch)\n  }\n\n  return rocm_config\n\n\ndef _find_hipruntime_config(rocm_install_path):\n\n  def hipruntime_version_number(path):\n    possible_version_files = [\n        \"include/hip/hip_version.h\",  # ROCm 5.2\n        \"hip/include/hip/hip_version.h\",  # ROCm 5.1 and prior\n    ]\n    version_file = None\n    for f in possible_version_files:\n      version_file_path = os.path.join(path, f)\n      if os.path.exists(version_file_path):\n        version_file = version_file_path\n        break\n    if not version_file:\n      raise ConfigError(\"HIP Runtime version file not found in {}\".format(\n          possible_version_files))\n\n    # This header file has an explicit #define for HIP_VERSION, whose value\n    # is (HIP_VERSION_MAJOR * 100 + HIP_VERSION_MINOR)\n    # Retreive the major + minor and re-calculate here, since we do not\n    # want get into the business of parsing arith exprs\n    major = _get_header_version(version_file, \"HIP_VERSION_MAJOR\")\n    minor = _get_header_version(version_file, \"HIP_VERSION_MINOR\")\n    return 100 * major + minor\n\n  hipruntime_config = {\n      \"hipruntime_version_number\": hipruntime_version_number(rocm_install_path)\n  }\n\n  return hipruntime_config\n\n\ndef _find_miopen_config(rocm_install_path):\n\n  def miopen_version_numbers(path):\n    possible_version_files = [\n        \"include/miopen/version.h\",  # ROCm 5.2 and prior\n        \"miopen/include/miopen/version.h\",  # ROCm 5.1 and prior\n    ]\n    version_file = None\n    for f in possible_version_files:\n      version_file_path = os.path.join(path, f)\n      if os.path.exists(version_file_path):\n        version_file = version_file_path\n        break\n    if not version_file:\n      raise ConfigError(\n          'MIOpen version file \"{}\" not found'.format(version_file))\n    major = _get_header_version(version_file, \"MIOPEN_VERSION_MAJOR\")\n    minor = _get_header_version(version_file, \"MIOPEN_VERSION_MINOR\")\n    patch = _get_header_version(version_file, \"MIOPEN_VERSION_PATCH\")\n    return major, minor, patch\n\n  major, minor, patch = miopen_version_numbers(rocm_install_path)\n\n  miopen_config = {\n      \"miopen_version_number\":\n          _get_composite_version_number(major, minor, patch)\n  }\n\n  return miopen_config\n\n\ndef _find_rocblas_config(rocm_install_path):\n\n  def rocblas_version_numbers(path):\n    possible_version_files = [\n        \"include/rocblas/internal/rocblas-version.h\",  # ROCm 5.2\n        \"rocblas/include/internal/rocblas-version.h\",  # ROCm 5.1 and prior\n    ]\n    version_file = None\n    for f in possible_version_files:\n      version_file_path = os.path.join(path, f)\n      if os.path.exists(version_file_path):\n        version_file = version_file_path\n        break\n    if not version_file:\n      raise ConfigError(\n          \"rocblas version file not found in {}\".format(\n              possible_version_files))\n    major = _get_header_version(version_file, \"ROCBLAS_VERSION_MAJOR\")\n    minor = _get_header_version(version_file, \"ROCBLAS_VERSION_MINOR\")\n    patch = _get_header_version(version_file, \"ROCBLAS_VERSION_PATCH\")\n    return major, minor, patch\n\n  major, minor, patch = rocblas_version_numbers(rocm_install_path)\n\n  rocblas_config = {\n      \"rocblas_version_number\":\n          _get_composite_version_number(major, minor, patch)\n  }\n\n  return rocblas_config\n\n\ndef _find_rocrand_config(rocm_install_path):\n\n  def rocrand_version_number(path):\n    possible_version_files = [\n        \"include/rocrand/rocrand_version.h\",  # ROCm 5.1\n        \"rocrand/include/rocrand_version.h\",  # ROCm 5.0 and prior\n    ]\n    version_file = None\n    for f in possible_version_files:\n      version_file_path = os.path.join(path, f)\n      if os.path.exists(version_file_path):\n        version_file = version_file_path\n        break\n    if not version_file:\n      raise ConfigError(\n          \"rocrand version file not found in {}\".format(possible_version_files))\n    version_number = _get_header_version(version_file, \"ROCRAND_VERSION\")\n    return version_number\n\n  rocrand_config = {\n      \"rocrand_version_number\": rocrand_version_number(rocm_install_path)\n  }\n\n  return rocrand_config\n\n\ndef _find_rocfft_config(rocm_install_path):\n\n  def rocfft_version_numbers(path):\n    possible_version_files = [\n        \"include/rocfft/rocfft-version.h\",  # ROCm 5.2\n        \"rocfft/include/rocfft-version.h\",  # ROCm 5.1 and prior\n    ]\n    version_file = None\n    for f in possible_version_files:\n      version_file_path = os.path.join(path, f)\n      if os.path.exists(version_file_path):\n        version_file = version_file_path\n        break\n    if not version_file:\n      raise ConfigError(\n          \"rocfft version file not found in {}\".format(possible_version_files))\n    major = _get_header_version(version_file, \"rocfft_version_major\")\n    minor = _get_header_version(version_file, \"rocfft_version_minor\")\n    patch = _get_header_version(version_file, \"rocfft_version_patch\")\n    return major, minor, patch\n\n  major, minor, patch = rocfft_version_numbers(rocm_install_path)\n\n  rocfft_config = {\n      \"rocfft_version_number\":\n          _get_composite_version_number(major, minor, patch)\n  }\n\n  return rocfft_config\n\n\ndef _find_hipfft_config(rocm_install_path):\n\n  def hipfft_version_numbers(path):\n    possible_version_files = [\n        \"include/hipfft/hipfft-version.h\",  # ROCm 5.2\n        \"hipfft/include/hipfft-version.h\",  # ROCm 5.1 and prior\n    ]\n    version_file = None\n    for f in possible_version_files:\n      version_file_path = os.path.join(path, f)\n      if os.path.exists(version_file_path):\n        version_file = version_file_path\n        break\n    if not version_file:\n      raise ConfigError(\n          \"hipfft version file not found in {}\".format(possible_version_files))\n    major = _get_header_version(version_file, \"hipfftVersionMajor\")\n    minor = _get_header_version(version_file, \"hipfftVersionMinor\")\n    patch = _get_header_version(version_file, \"hipfftVersionPatch\")\n    return major, minor, patch\n\n  major, minor, patch = hipfft_version_numbers(rocm_install_path)\n\n  hipfft_config = {\n      \"hipfft_version_number\":\n          _get_composite_version_number(major, minor, patch)\n  }\n\n  return hipfft_config\n\n\ndef _find_roctracer_config(rocm_install_path):\n\n  def roctracer_version_numbers(path):\n    possible_version_files = [\n        \"include/roctracer/roctracer.h\",  # ROCm 5.2\n        \"roctracer/include/roctracer.h\",  # ROCm 5.1 and prior\n    ]\n    version_file = None\n    for f in possible_version_files:\n      version_file_path = os.path.join(path, f)\n      if os.path.exists(version_file_path):\n        version_file = version_file_path\n        break\n    if not version_file:\n      raise ConfigError(\"roctracer version file not found in {}\".format(\n          possible_version_files))\n    major = _get_header_version(version_file, \"ROCTRACER_VERSION_MAJOR\")\n    minor = _get_header_version(version_file, \"ROCTRACER_VERSION_MINOR\")\n    # roctracer header does not have a patch version number\n    patch = 0\n    return major, minor, patch\n\n  major, minor, patch = roctracer_version_numbers(rocm_install_path)\n\n  roctracer_config = {\n      \"roctracer_version_number\":\n          _get_composite_version_number(major, minor, patch)\n  }\n\n  return roctracer_config\n\n\ndef _find_hipsparse_config(rocm_install_path):\n\n  def hipsparse_version_numbers(path):\n    possible_version_files = [\n        \"include/hipsparse/hipsparse-version.h\",  # ROCm 5.2\n        \"hipsparse/include/hipsparse-version.h\",  # ROCm 5.1 and prior\n    ]\n    version_file = None\n    for f in possible_version_files:\n      version_file_path = os.path.join(path, f)\n      if os.path.exists(version_file_path):\n        version_file = version_file_path\n        break\n    if not version_file:\n      raise ConfigError(\"hipsparse version file not found in {}\".format(\n          possible_version_files))\n    major = _get_header_version(version_file, \"hipsparseVersionMajor\")\n    minor = _get_header_version(version_file, \"hipsparseVersionMinor\")\n    patch = _get_header_version(version_file, \"hipsparseVersionPatch\")\n    return major, minor, patch\n\n  major, minor, patch = hipsparse_version_numbers(rocm_install_path)\n\n  hipsparse_config = {\n      \"hipsparse_version_number\":\n          _get_composite_version_number(major, minor, patch)\n  }\n\n  return hipsparse_config\n\ndef _find_hipsolver_config(rocm_install_path):\n\n  def hipsolver_version_numbers(path):\n    possible_version_files = [\n        \"include/hipsolver/internal/hipsolver-version.h\",  # ROCm 5.2\n        \"hipsolver/include/internal/hipsolver-version.h\",  # ROCm 5.1\n        \"hipsolver/include/hipsolver-version.h\",  # ROCm 5.0 and prior\n    ]\n    version_file = None\n    for f in possible_version_files:\n      version_file_path = os.path.join(path, f)\n      if os.path.exists(version_file_path):\n        version_file = version_file_path\n        break\n    if not version_file:\n      raise ConfigError(\"hipsolver version file not found in {}\".format(\n          possible_version_files))\n    major = _get_header_version(version_file, \"hipsolverVersionMajor\")\n    minor = _get_header_version(version_file, \"hipsolverVersionMinor\")\n    patch = _get_header_version(version_file, \"hipsolverVersionPatch\")\n    return major, minor, patch\n\n  major, minor, patch = hipsolver_version_numbers(rocm_install_path)\n\n  hipsolver_config = {\n      \"hipsolver_version_number\":\n          _get_composite_version_number(major, minor, patch)\n  }\n\n  return hipsolver_config\n\n\ndef _find_rocsolver_config(rocm_install_path):\n\n  def rocsolver_version_numbers(path):\n    possible_version_files = [\n        \"include/rocsolver/rocsolver-version.h\",  # ROCm 5.2\n        \"rocsolver/include/rocsolver-version.h\",  # ROCm 5.1 and prior\n    ]\n    version_file = None\n    for f in possible_version_files:\n      version_file_path = os.path.join(path, f)\n      if os.path.exists(version_file_path):\n        version_file = version_file_path\n        break\n    if not version_file:\n      raise ConfigError(\"rocsolver version file not found in {}\".format(\n          possible_version_files))\n    major = _get_header_version(version_file, \"ROCSOLVER_VERSION_MAJOR\")\n    minor = _get_header_version(version_file, \"ROCSOLVER_VERSION_MINOR\")\n    patch = _get_header_version(version_file, \"ROCSOLVER_VERSION_PATCH\")\n    return major, minor, patch\n\n  major, minor, patch = rocsolver_version_numbers(rocm_install_path)\n\n  rocsolver_config = {\n      \"rocsolver_version_number\":\n          _get_composite_version_number(major, minor, patch)\n  }\n\n  return rocsolver_config\n\n\ndef find_rocm_config():\n  \"\"\"Returns a dictionary of ROCm components config info.\"\"\"\n  rocm_install_path = _get_rocm_install_path()\n  if not os.path.exists(rocm_install_path):\n    raise ConfigError(\n        'Specified ROCM_PATH \"{}\" does not exist'.format(rocm_install_path))\n\n  result = {}\n\n  result[\"rocm_toolkit_path\"] = rocm_install_path\n  result.update(_find_rocm_config(rocm_install_path))\n  result.update(_find_hipruntime_config(rocm_install_path))\n  result.update(_find_miopen_config(rocm_install_path))\n  result.update(_find_rocblas_config(rocm_install_path))\n  result.update(_find_rocrand_config(rocm_install_path))\n  result.update(_find_rocfft_config(rocm_install_path))\n  if result[\"rocm_version_number\"] >= 40100:\n    result.update(_find_hipfft_config(rocm_install_path))\n  result.update(_find_roctracer_config(rocm_install_path))\n  result.update(_find_hipsparse_config(rocm_install_path))\n  if result[\"rocm_version_number\"] >= 40500:\n    result.update(_find_hipsolver_config(rocm_install_path))\n  result.update(_find_rocsolver_config(rocm_install_path))\n\n  return result\n\n\ndef main():\n  try:\n    for key, value in sorted(find_rocm_config().items()):\n      print(\"%s: %s\" % (key, value))\n  except ConfigError as e:\n    sys.stderr.write(\"\\nERROR: {}\\n\\n\".format(str(e)))\n    sys.exit(1)\n\n\nif __name__ == \"__main__\":\n  main()\n"
  },
  {
    "path": "third_party/gpus/find_rocm_config.py.gz.base64",
    "content": "eJztW21v2zgS/q5fQSgoam9dJSlugUMOOcCbZlHftUlhZ7tYtIVB27TNrSz6SCppUPS/7wxJyRIt2YqjvX6JgTa2NPNwOC8Ph7Z4RC7E+l7yxVKTVyevTsjNkpEblighf43FHemneimkikg/jskQxRQZMsXkLZtFwVFwRN7yKYizGUmTGZNEg35/Tafwx93pkQ9MKi4S8io6IR0UCN2tsPsvQLgXKVnRe5IITVLFAIIrMucxI+zrlK014QmZitU65jSZMnLH9dIM40DADPKHgxATTUGagvwaPs2LcoRqYzC+llqvz46P7+7uImqMjYRcHMdWUB2/HVxcXo0uX4LBRuW3JGZKEcn+l3IJU53cE7oGe6Z0AlbG9I4ISehCMrinBdp7J7nmyaJHlJjrOyoZoMy40pJPUl1yVmYdzLkoAO6iCQn7IzIYheSX/mgw6gHG74ObN9e/3ZDf+8Nh/+pmcDki10NycX31enAzuL6CT7+S/tUf5L+Dq9c9wsBVMAz7upZoPxjJ0Y0mdGTEWMmAubAGqTWb8jmfwrySRUoXjCzELZMJTIesmVxxhcFUYN4MUGK+4ppqc2VrUjjMeauvIAzD95InmIbXFysYfiKpvEdjyJJRHH8GIZpqITkzNpJbm32QUgIMRMeaWd4rzVZREGDCq6nkkGeKUQm5oIwr6uAxMVUZpQcRR69pFcDFFabAjGl0VWJczGVmhAFaW/tRfyqSOV+k0jgQ9ZSeiVRHxqo1xUQXGThmiIsNptlSinSxxCRhyS2XIlmxRJNbKrlJyg7Y/278vn/zphsFgzkUF9yL+cwbkju39Ox0rB8yA405TEoTasl0Kk3YCVwCB03FjJX9p+kXZueVxeC+YDEUDd7K7aq0OyrixUJ8scGwvrfxzGJiA2GqfUnl7CXaM4MYaqj7QKWTYh7MpViRCVXOqY4YNrbl9kYEfLUxEdwDrBTkgsZNUJbHYq2PpZiuQhRJkf4o2KIh7nOaxjifOGUBZmsQQM0JCeET2TuhsnfAC+4dZFIQBNOYQp1emBBdopc7l4YCIVTds4CA9QrFYBQyXjA9dsON0ZQxTq1jxGysimYWlYwwJJWmcVxQAltfZ1lrPZ2F3IVtRZySTR3UjHCCMJ6PSM5r7QNxPidh7uMQQyhU5LIBLakG3Mh8LGh/BvmjOnljomQ0NkNvCXU3rtq6V3QYLj5Ccc3GrozHSbqaMNlZ0T+F7BHwGP4Btemy6P/TE3iRn4gRIy/wM35CafhkxIvD2CTPxuigFT2S0BXLwjN08QA2X4PBQOkABOomYODFiyJHudBg9cTIQ3Cfi0isWYYcyhBWiATKBpj9PEz1/OU/w671/wptAx9KFpm3HRke2YHIM0VedD7NXnRD8sxY1zP4XaMHkTXyFoXYMgAcczFaAGWtO6ddd9N5CbimY+S6AbqOcqjSYgU8z0YOv3035WZXtU/J8wgmB8gd4yPywsGWXyExfQUugMgz4AXTWXz7DuvmpyTMIEw61ENwoEM7F8sIyIJgN1sw9C4UDY3DbhZLMHZmM95ybUXineFUUdjcKmeV6jgRHBvyTnHgxVzGst45+ZibGvJkGqczZgodWFDad5lGtIQwQ4mYAv45elWtVyN9mi1YQhq9z+b/oi1gypVImLmOuTZHF1dbnSdF4aJfrH8KnuXnPMsTSKrsNvsKLZLqbCF0z/JpecZtieaCEyCGL1nWYkyLohnedjoWUiQ0XsqWdjMg4tg+A9wA+Zrll5dY1Q7qmgogji/OK1mhqNBzNPrhcjiC5m/8rv+f62Fo3WZZ5gCMwVWOsXYs8FAMIOaLNw7DFXkFU+JcKy4j61QVRQV7ZyuPLTNQ/ObcHFYghGcHkDngfQ+8VcKOVir2JV/LNNF8xRqUfEHYs+DAugdA/Le/4FGwmdJT3e+s+zeD92RoY/g3lP8RbMJhmSms5dDg2sb7K246oc3MlkT0PBiTVV6P3C0F2G27T4sFSJ2CiCUJaEOwGXlBSnew9LtODXoNyfit3SJmDYwlFdsavpzSeJpCLwjWMQkcoLjZnUPzL0zTbHHuKKy6UHe4XAoDNkkVNiwKu/A1hfnDvhK6f0gH3KeqhzLg1tweToBbTihz12mpiTO4GKitoi8SUG2RAw3VE0Blh1okoK0xSzS04tjeNaAgJ/i4vqPEQRbxuMQm7rVhIo9UchCnvAvriZkaMdPzd4Nr8F2ZlEzfnDNT3jUXwbvdh5YdDPT+8uqxleejHNZ8eCiPbT9qqqO6ASnVXJEBKlHCs0KwHt2OlMb2dx+TmKpmGxAj2d4eBOGOcXMkExpnF17ubU42mhapIcITH+zsVJzz2u1SHkgVEKlf3vZHLWxTyjAH71RKMC1sVioLqHa/UihMb8tSgdMuYZRH9xlDQiU1Ywwj2c7mxaEde6h+lZd4wih4ADWKJ0/0sIceZOE3kt30sJMPytnQuBSH/avXWSmWi7AM6GqnkKJe7VSkJLTaNbm6t88uD+UXynyum9UJCra3sAKa+9NoNUXxsvbTEnpgjYDvfugK6qWS0Xz4AuqjoObD108PxWg+bvmsqpLa1XNTex4BbKO0vnZuxva//WvGCE6wJUawaO7PfkZw4mXtJ0Y4hBGs734oI1gTPtiL7w7jgzLGYWxQwnj/SC6oqY9qLihVnffV29/NBaWx/e5ASzoFhzVqEJxsez2CBdy829khOOEt9Sc2eGB/YN32ozfZN8P+xeWwhW22D1TYaB9tsjZ/3kow+5v4kt4yQl01Z75w7XuRVk4e0yzUVExtv1CqRq9lqMRqvWsoWeA3Dgp/AWn4q6GTba99sICbd42aCKe0BfPUShzYSlj3/ehuwlrx6IaiDHNwT1GCaaGtqCmd2s6iVJZec1GJ1Xp/UbLAJw0R3zbqMDayLZKGAdx8PZ9fasYembb3Jf8+lNNdKPuUn74E3OKgjRcfTDxtko6x4PGkU4I5nHSKMG2QTnXp1ZNOsax90qnCap90ihb4+5rGrLORbW9f4yo+f9foG1CPJvYpP7Uq+/Y5BzJGm6wBsRpdv/3Qyj7HBzr4B0UPqIWfFB/EHX5xetuc/wN3+BY47th6Etp/kp2SGZ/iaQI80CHmthKNIQnDIyhuPjyZi31HDSpONQR5tnslVUVeewrg+Sg/LLI5HmKerMn3vwY8f7xmewwbK6bwfAhE6Pvm40f7wKoWIv7CtZEOP2cPwpZOJWQaUbqeUc064wYPm3drtJo8tVqnu+9Rszq9vQ+m7FDc/fv0DsWd3867LCkFwiuVz+Tf5+QfJ6cnJy5Nqp25d5ga8/Z9Ybgjfvu+Pmg+uZ/3TG7f6r9jfvtVC0RiIBx9rCisj6Y0tbw/y1fdL+y+l53GSIgSUrNZZ5tqIqCylep08+XSHGvrhM/UGXmm8PRKZ4Nk7HfHTQv1j6e63PKo7lVkj8NFeLiTdcJPyeVweD08g1L+lBTOkigtOwDYzdWAGDQeewkCiMV4jMdVxmNyfk7C8RjnOB4bNrbTDf4CEAHDYg=="
  },
  {
    "path": "third_party/gpus/local_config_cuda.BUILD",
    "content": "load(\"@bazel_skylib//rules:common_settings.bzl\", \"string_flag\")\nload(\"@local_config_cuda//cuda:build_defs.bzl\", \"enable_cuda_flag\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\n# Build flag to enable CUDA support.\n#\n# Enable with '--@local_config_cuda//:enable_cuda', or indirectly with\n# ./configure or '--config=cuda'.\nenable_cuda_flag(\n    name = \"enable_cuda\",\n    build_setting_default = False,\n    enable_override = select({\n        \":define_using_cuda_nvcc\": True,\n        \"//conditions:default\": False,\n    }),\n)\n\n# Config setting whether CUDA support has been requested.\n#\n# Enable path: ./configure > --config=cuda (.tachyon_configure.bazelrc)\n#     > --//tachyon:enable_cuda (.bazelrc) > :is_cuda_enabled\nconfig_setting(\n    name = \"is_cuda_enabled\",\n    flag_values = {\":enable_cuda\": \"True\"},\n)\n\n# Build flag to select CUDA compiler.\n#\n# Set with '--@local_config_cuda//:cuda_compiler=...', or indirectly with\n# ./configure, '--config=cuda' or '--config=cuda_clang'.\nstring_flag(\n    name = \"cuda_compiler\",\n    build_setting_default = \"nvcc\",\n    values = [\n        \"clang\",\n        \"nvcc\",\n    ],\n)\n\n# Config setting whether CUDA device code should be compiled with clang.\nconfig_setting(\n    name = \"is_cuda_compiler_clang\",\n    flag_values = {\":cuda_compiler\": \"clang\"},\n)\n\n# Config setting whether CUDA device code should be compiled with nvcc.\nconfig_setting(\n    name = \"is_cuda_compiler_nvcc\",\n    flag_values = {\":cuda_compiler\": \"nvcc\"},\n)\n\n# Config setting to keep `--define=using_cuda_nvcc=true` working.\n# TODO(b/174244321): Remove when downstream projects have been fixed, along\n# with the enable_cuda_flag rule in cuda:build_defs.bzl.tpl.\nconfig_setting(\n    name = \"define_using_cuda_nvcc\",\n    define_values = {\"using_cuda_nvcc\": \"true\"},\n    visibility = [\"//visibility:private\"],\n)\n"
  },
  {
    "path": "third_party/gpus/rocm/BUILD.bazel",
    "content": ""
  },
  {
    "path": "third_party/gpus/rocm/BUILD.tpl",
    "content": "load(\"@bazel_skylib//:bzl_library.bzl\", \"bzl_library\")\n\nlicenses([\"restricted\"])  # MPL2, portions GPL v3, LGPL v3, BSD-like\n\npackage(default_visibility = [\"//visibility:public\"])\n\nconfig_setting(\n    name = \"using_hipcc\",\n    values = {\n        \"define\": \"using_rocm_hipcc=true\",\n    },\n)\n\ncc_library(\n    name = \"rocm_headers\",\n    hdrs = [\n        \"rocm/rocm_config.h\",\n        %{rocm_headers}\n    ],\n    includes = [\n        \".\",\n        \"rocm/include\",\n        \"rocm/include/rocrand\",\n        \"rocm/include/roctracer\",\n    ],\n    visibility = [\"//visibility:public\"],\n)\n\ncc_library(\n    name = \"hip\",\n    srcs = [\"rocm/lib/%{hip_lib}\"],\n    data = [\"rocm/lib/%{hip_lib}\"],\n    includes = [\n        \".\",\n        \"rocm/include\",\n    ],\n    linkstatic = 1,\n    visibility = [\"//visibility:public\"],\n)\n\ncc_library(\n    name = \"rocblas\",\n    srcs = [\"rocm/lib/%{rocblas_lib}\"],\n    data = [\"rocm/lib/%{rocblas_lib}\"],\n    includes = [\n        \".\",\n        \"rocm/include\",\n    ],\n    linkstatic = 1,\n    visibility = [\"//visibility:public\"],\n)\n\ncc_library(\n    name = \"%{hipfft_or_rocfft}\",\n    srcs = [\"rocm/lib/%{hipfft_or_rocfft_lib}\"],\n    data = [\"rocm/lib/%{hipfft_or_rocfft_lib}\"],\n    includes = [\n        \".\",\n        \"rocm/include\",\n    ],\n    linkstatic = 1,\n    visibility = [\"//visibility:public\"],\n)\n\ncc_library(\n    name = \"hiprand\",\n    srcs = [\"rocm/lib/%{hiprand_lib}\"],\n    data = [\"rocm/lib/%{hiprand_lib}\"],\n    includes = [\n        \".\",\n        \"rocm/include\",\n        \"rocm/include/rocrand\",\n    ],\n    linkstatic = 1,\n    visibility = [\"//visibility:public\"],\n)\n\ncc_library(\n    name = \"miopen\",\n    srcs = [\"rocm/lib/%{miopen_lib}\"],\n    data = [\"rocm/lib/%{miopen_lib}\"],\n    includes = [\n        \".\",\n        \"rocm/include\",\n    ],\n    linkstatic = 1,\n    visibility = [\"//visibility:public\"],\n)\n\ncc_library(\n    name = \"rccl\",\n    srcs = [\"rocm/lib/%{rccl_lib}\"],\n    data = [\"rocm/lib/%{rccl_lib}\"],\n    includes = [\n        \".\",\n        \"rocm/include\",\n    ],\n    linkstatic = 1,\n    visibility = [\"//visibility:public\"],\n)\n\ncc_library(\n    name = \"rocm\",\n    visibility = [\"//visibility:public\"],\n    deps = [\n        \":rocm_headers\",\n        \":hip\",\n        \":rocblas\",\n        \":hipblas\",\n        \":%{hipfft_or_rocfft}\",\n        \":hiprand\",\n        \":miopen\",\n        \":hipsparse\",\n        \":roctracer\",\n        \":rocsolver\",\n        \":hipsolver\",\n    ],\n)\n\nbzl_library(\n    name = \"build_defs_bzl\",\n    srcs = [\"build_defs.bzl\"],\n)\n\ncc_library(\n    name = \"rocprim\",\n    srcs = [\n        \"rocm/include/hipcub/hipcub_version.hpp\",\n        \"rocm/include/rocprim/rocprim_version.hpp\",\n    ],\n    hdrs = glob([\n        \"rocm/include/hipcub/**\",\n        \"rocm/include/rocprim/**\",\n    ]),\n    includes = [\n        \".\",\n        \"rocm/include/hipcub\",\n        \"rocm/include/rocprim\",\n    ],\n    visibility = [\"//visibility:public\"],\n    deps = [\n        \"@local_config_rocm//rocm:rocm_headers\",\n    ],\n)\n\ncc_library(\n    name = \"hipsparse\",\n    srcs = [\"rocm/lib/%{hipsparse_lib}\"],\n    data = [\"rocm/lib/%{hipsparse_lib}\"],\n)\n\ncc_library(\n    name = \"roctracer\",\n    data = [\"rocm/lib/%{roctracer_lib}\"],\n)\n\ncc_library(\n    name = \"rocsolver\",\n    srcs = [\"rocm/lib/%{rocsolver_lib}\"],\n    data = [\"rocm/lib/%{rocsolver_lib}\"],\n)\n\ncc_library(\n    name = \"hipsolver\",\n    srcs = [\"rocm/lib/%{hipsolver_lib}\"],\n    data = [\"rocm/lib/%{hipsolver_lib}\"],\n)\n\ncc_library(\n    name = \"hipblas\",\n    srcs = [\"rocm/lib/%{hipblas_lib}\"],\n    data = [\"rocm/lib/%{hipblas_lib}\"],\n)\n\nfilegroup(\n    name = \"rocm_root\",\n    srcs = [\n        \"rocm/bin/clang-offload-bundler\",\n    ],\n)\n\n%{copy_rules}\n"
  },
  {
    "path": "third_party/gpus/rocm/build_defs.bzl.tpl",
    "content": "# Macros for building ROCm code.\ndef if_rocm(if_true, if_false = []):\n    \"\"\"Shorthand for select()'ing on whether we're building with ROCm.\n\n    Returns a select statement which evaluates to if_true if we're building\n    with ROCm enabled.  Otherwise, the select statement evaluates to if_false.\n\n    \"\"\"\n    return select({\n        \"@local_config_rocm//rocm:using_hipcc\": if_true,\n        \"//conditions:default\": if_false\n    })\n\n\ndef rocm_default_copts():\n    \"\"\"Default options for all ROCm compilations.\"\"\"\n    return if_rocm([\"-x\", \"rocm\"] + %{rocm_extra_copts})\n\ndef rocm_copts(opts = []):\n    \"\"\"Gets the appropriate set of copts for (maybe) ROCm compilation.\n\n      If we're doing ROCm compilation, returns copts for our particular ROCm\n      compiler.  If we're not doing ROCm compilation, returns an empty list.\n\n      \"\"\"\n    return rocm_default_copts() + select({\n        \"//conditions:default\": [],\n        \"@local_config_rocm//rocm:using_hipcc\": ([\n            \"\",\n        ]),\n    }) + if_rocm_is_configured(opts)\n\ndef rocm_gpu_architectures():\n    \"\"\"Returns a list of supported GPU architectures.\"\"\"\n    return %{rocm_gpu_architectures}\n\ndef rocm_version_number():\n    \"\"\"Returns a list of supported GPU architectures.\"\"\"\n    return %{rocm_version_number}\n\ndef if_rocm_is_configured(x):\n    \"\"\"Tests if the ROCm was enabled during the configure process.\n\n    Unlike if_rocm(), this does not require that we are building with\n    --config=rocm. Used to allow non-ROCm code to depend on ROCm libraries.\n    \"\"\"\n    if %{rocm_is_configured}:\n      return select({\"//conditions:default\": x})\n    return select({\"//conditions:default\": []})\n\ndef rocm_library(copts = [], **kwargs):\n    \"\"\"Wrapper over cc_library which adds default ROCm options.\"\"\"\n    native.cc_library(copts = rocm_default_copts() + copts, **kwargs)\n"
  },
  {
    "path": "third_party/gpus/rocm/rocm_config.h.tpl",
    "content": "/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n#ifndef ROCM_ROCM_CONFIG_H_\n#define ROCM_ROCM_CONFIG_H_\n\n#define TACHYON_ROCM_TOOLKIT_PATH \"%{rocm_toolkit_path}\"\n\n#define TACHYON_ROCM_VERSION %{rocm_version_number}\n#define TACHYON_MIOPEN_VERSION %{miopen_version_number}\n#define TACHYON_HIPRUNTIME_VERSION %{hipruntime_version_number}\n\n#endif  // ROCM_ROCM_CONFIG_H_\n"
  },
  {
    "path": "third_party/gpus/rocm_configure.bzl",
    "content": "\"\"\"Repository rule for ROCm autoconfiguration.\n\n`rocm_configure` depends on the following environment variables:\n\n  * `TACHYON_NEED_ROCM`: Whether to enable building with ROCm.\n  * `GCC_HOST_COMPILER_PATH`: The GCC host compiler path\n  * `ROCM_PATH`: The path to the ROCm toolkit. Default is `/opt/rocm`.\n  * `TACHYON_ROCM_AMDGPU_TARGETS`: The AMDGPU targets.\n\"\"\"\n\nload(\n    \"//third_party/remote_config:common.bzl\",\n    \"config_repo_label\",\n    \"err_out\",\n    \"execute\",\n    \"files_exist\",\n    \"get_bash_bin\",\n    \"get_cpu_value\",\n    \"get_host_environ\",\n    \"get_python_bin\",\n    \"raw_exec\",\n    \"realpath\",\n    \"which\",\n)\nload(\n    \":cuda_configure.bzl\",\n    \"make_copy_dir_rule\",\n    \"make_copy_files_rule\",\n    \"to_list_of_strings\",\n)\n\n_GCC_HOST_COMPILER_PATH = \"GCC_HOST_COMPILER_PATH\"\n_GCC_HOST_COMPILER_PREFIX = \"GCC_HOST_COMPILER_PREFIX\"\n_ROCM_TOOLKIT_PATH = \"ROCM_PATH\"\n_TACHYON_ROCM_AMDGPU_TARGETS = \"TACHYON_ROCM_AMDGPU_TARGETS\"\n_TACHYON_ROCM_CONFIG_REPO = \"TACHYON_ROCM_CONFIG_REPO\"\n\n_DEFAULT_ROCM_TOOLKIT_PATH = \"/opt/rocm\"\n\ndef verify_build_defines(params):\n    \"\"\"Verify all variables that crosstool/BUILD.rocm.tpl expects are substituted.\n\n    Args:\n      params: dict of variables that will be passed to the BUILD.tpl template.\n    \"\"\"\n    missing = []\n    for param in [\n        \"cxx_builtin_include_directories\",\n        \"extra_no_canonical_prefixes_flags\",\n        \"host_compiler_path\",\n        \"host_compiler_prefix\",\n        \"linker_bin_path\",\n        \"unfiltered_compile_flags\",\n    ]:\n        if (\"%{\" + param + \"}\") not in params:\n            missing.append(param)\n\n    if missing:\n        auto_configure_fail(\n            \"BUILD.rocm.tpl template is missing these variables: \" +\n            str(missing) +\n            \".\\nWe only got: \" +\n            str(params) +\n            \".\",\n        )\n\ndef find_cc(repository_ctx):\n    \"\"\"Find the C++ compiler.\"\"\"\n\n    # Return a dummy value for GCC detection here to avoid error\n    target_cc_name = \"gcc\"\n    cc_path_envvar = _GCC_HOST_COMPILER_PATH\n    cc_name = target_cc_name\n\n    cc_name_from_env = get_host_environ(repository_ctx, cc_path_envvar)\n    if cc_name_from_env:\n        cc_name = cc_name_from_env\n    if cc_name.startswith(\"/\"):\n        # Absolute path, maybe we should make this supported by our which function.\n        return cc_name\n    cc = which(repository_ctx, cc_name)\n    if cc == None:\n        fail((\"Cannot find {}, either correct your path or set the {}\" +\n              \" environment variable\").format(target_cc_name, cc_path_envvar))\n    return cc\n\n_INC_DIR_MARKER_BEGIN = \"#include <...>\"\n\ndef _cxx_inc_convert(path):\n    \"\"\"Convert path returned by cc -E xc++ in a complete path.\"\"\"\n    path = path.strip()\n    return path\n\ndef _get_cxx_inc_directories_impl(repository_ctx, cc, lang_is_cpp):\n    \"\"\"Compute the list of default C or C++ include directories.\"\"\"\n    if lang_is_cpp:\n        lang = \"c++\"\n    else:\n        lang = \"c\"\n\n    # TODO: We pass -no-canonical-prefixes here to match the compiler flags,\n    #       but in rocm_clang CROSSTOOL file that is a `feature` and we should\n    #       handle the case when it's disabled and no flag is passed\n    result = raw_exec(repository_ctx, [\n        cc,\n        \"-no-canonical-prefixes\",\n        \"-E\",\n        \"-x\" + lang,\n        \"-\",\n        \"-v\",\n    ])\n    stderr = err_out(result)\n    index1 = stderr.find(_INC_DIR_MARKER_BEGIN)\n    if index1 == -1:\n        return []\n    index1 = stderr.find(\"\\n\", index1)\n    if index1 == -1:\n        return []\n    index2 = stderr.rfind(\"\\n \")\n    if index2 == -1 or index2 < index1:\n        return []\n    index2 = stderr.find(\"\\n\", index2 + 1)\n    if index2 == -1:\n        inc_dirs = stderr[index1 + 1:]\n    else:\n        inc_dirs = stderr[index1 + 1:index2].strip()\n\n    return [\n        str(repository_ctx.path(_cxx_inc_convert(p)))\n        for p in inc_dirs.split(\"\\n\")\n    ]\n\ndef get_cxx_inc_directories(repository_ctx, cc):\n    \"\"\"Compute the list of default C and C++ include directories.\"\"\"\n\n    # For some reason `clang -xc` sometimes returns include paths that are\n    # different from the ones from `clang -xc++`. (Symlink and a dir)\n    # So we run the compiler with both `-xc` and `-xc++` and merge resulting lists\n    includes_cpp = _get_cxx_inc_directories_impl(repository_ctx, cc, True)\n    includes_c = _get_cxx_inc_directories_impl(repository_ctx, cc, False)\n\n    includes_cpp_set = depset(includes_cpp)\n    return includes_cpp + [\n        inc\n        for inc in includes_c\n        if inc not in includes_cpp_set.to_list()\n    ]\n\ndef auto_configure_fail(msg):\n    \"\"\"Output failure message when rocm configuration fails.\"\"\"\n    red = \"\\033[0;31m\"\n    no_color = \"\\033[0m\"\n    fail(\"\\n%sROCm Configuration Error:%s %s\\n\" % (red, no_color, msg))\n\ndef auto_configure_warning(msg):\n    \"\"\"Output warning message during auto configuration.\"\"\"\n    yellow = \"\\033[1;33m\"\n    no_color = \"\\033[0m\"\n    print(\"\\n%sAuto-Configuration Warning:%s %s\\n\" % (yellow, no_color, msg))\n\n# END cc_configure common functions (see TODO above).\n\ndef _rocm_include_path(repository_ctx, rocm_config, bash_bin):\n    \"\"\"Generates the cxx_builtin_include_directory entries for rocm inc dirs.\n\n    Args:\n      repository_ctx: The repository context.\n      rocm_config: The path to the gcc host compiler.\n\n    Returns:\n      A string containing the Starlark string for each of the gcc\n      host compiler include directories, which can be added to the CROSSTOOL\n      file.\n    \"\"\"\n    inc_dirs = []\n\n    # Add HSA headers (needs to match $HSA_PATH)\n    inc_dirs.append(rocm_config.rocm_toolkit_path + \"/hsa/include\")\n\n    # Add HIP headers (needs to match $HIP_PATH)\n    inc_dirs.append(rocm_config.rocm_toolkit_path + \"/hip/include\")\n    if int(rocm_config.rocm_version_number) >= 50200:\n        inc_dirs.append(rocm_config.rocm_toolkit_path + \"/include\")\n        inc_dirs.append(rocm_config.rocm_toolkit_path + \"/include/hip\")\n        inc_dirs.append(rocm_config.rocm_toolkit_path + \"/include/rocprim\")\n        inc_dirs.append(rocm_config.rocm_toolkit_path + \"/include/rocsolver\")\n        inc_dirs.append(rocm_config.rocm_toolkit_path + \"/include/rocblas\")\n\n    # Add HIP-Clang headers (realpath relative to compiler binary)\n    rocm_toolkit_path = realpath(repository_ctx, rocm_config.rocm_toolkit_path, bash_bin)\n    inc_dirs.append(rocm_toolkit_path + \"/llvm/lib/clang/8.0/include\")\n    inc_dirs.append(rocm_toolkit_path + \"/llvm/lib/clang/9.0.0/include\")\n    inc_dirs.append(rocm_toolkit_path + \"/llvm/lib/clang/10.0.0/include\")\n    inc_dirs.append(rocm_toolkit_path + \"/llvm/lib/clang/11.0.0/include\")\n    inc_dirs.append(rocm_toolkit_path + \"/llvm/lib/clang/12.0.0/include\")\n    inc_dirs.append(rocm_toolkit_path + \"/llvm/lib/clang/13.0.0/include\")\n    inc_dirs.append(rocm_toolkit_path + \"/llvm/lib/clang/14.0.0/include\")\n    inc_dirs.append(rocm_toolkit_path + \"/llvm/lib/clang/15.0.0/include\")\n    inc_dirs.append(rocm_toolkit_path + \"/llvm/lib/clang/16.0.0/include\")\n    inc_dirs.append(rocm_toolkit_path + \"/llvm/lib/clang/17.0.0/include\")\n\n    # Support hcc based off clang 10.0.0 (for ROCm 3.3)\n    inc_dirs.append(rocm_toolkit_path + \"/hcc/compiler/lib/clang/10.0.0/include/\")\n    inc_dirs.append(rocm_toolkit_path + \"/hcc/lib/clang/10.0.0/include\")\n\n    # Add hcc headers\n    inc_dirs.append(rocm_toolkit_path + \"/hcc/include\")\n\n    return inc_dirs\n\ndef _enable_rocm(repository_ctx):\n    enable_rocm = get_host_environ(repository_ctx, \"TACHYON_NEED_ROCM\")\n    if enable_rocm == \"1\":\n        if get_cpu_value(repository_ctx) != \"Linux\":\n            auto_configure_warning(\"ROCm configure is only supported on Linux\")\n            return False\n        return True\n    return False\n\ndef _amdgpu_targets(repository_ctx, rocm_toolkit_path, bash_bin):\n    \"\"\"Returns a list of strings representing AMDGPU targets.\"\"\"\n    amdgpu_targets_str = get_host_environ(repository_ctx, _TACHYON_ROCM_AMDGPU_TARGETS)\n    if not amdgpu_targets_str:\n        cmd = \"%s/bin/rocm_agent_enumerator\" % rocm_toolkit_path\n        result = execute(repository_ctx, [bash_bin, \"-c\", cmd])\n        targets = [target for target in result.stdout.strip().split(\"\\n\") if target != \"gfx000\"]\n        targets = {x: None for x in targets}\n        targets = list(targets.keys())\n        amdgpu_targets_str = \",\".join(targets)\n    amdgpu_targets = amdgpu_targets_str.split(\",\")\n    for amdgpu_target in amdgpu_targets:\n        if amdgpu_target[:3] != \"gfx\":\n            auto_configure_fail(\"Invalid AMDGPU target: %s\" % amdgpu_target)\n    return amdgpu_targets\n\ndef _hipcc_env(repository_ctx):\n    \"\"\"Returns the environment variable string for hipcc.\n\n    Args:\n        repository_ctx: The repository context.\n\n    Returns:\n        A string containing environment variables for hipcc.\n    \"\"\"\n    hipcc_env = \"\"\n    for name in [\n        \"HIP_CLANG_PATH\",\n        \"DEVICE_LIB_PATH\",\n        \"HIP_VDI_HOME\",\n        \"HIPCC_VERBOSE\",\n        \"HIPCC_COMPILE_FLAGS_APPEND\",\n        \"HIPPCC_LINK_FLAGS_APPEND\",\n        \"HCC_AMDGPU_TARGET\",\n        \"HIP_PLATFORM\",\n    ]:\n        env_value = get_host_environ(repository_ctx, name)\n        if env_value:\n            hipcc_env = (hipcc_env + \" \" + name + \"=\\\"\" + env_value + \"\\\";\")\n    return hipcc_env.strip()\n\ndef _crosstool_verbose(repository_ctx):\n    \"\"\"Returns the environment variable value CROSSTOOL_VERBOSE.\n\n    Args:\n        repository_ctx: The repository context.\n\n    Returns:\n        A string containing value of environment variable CROSSTOOL_VERBOSE.\n    \"\"\"\n    return get_host_environ(repository_ctx, \"CROSSTOOL_VERBOSE\", \"0\")\n\ndef _lib_name(lib, version = \"\", static = False):\n    \"\"\"Constructs the name of a library on Linux.\n\n    Args:\n      lib: The name of the library, such as \"hip\"\n      version: The version of the library.\n      static: True the library is static or False if it is a shared object.\n\n    Returns:\n      The platform-specific name of the library.\n    \"\"\"\n    if static:\n        return \"lib%s.a\" % lib\n    else:\n        if version:\n            version = \".%s\" % version\n        return \"lib%s.so%s\" % (lib, version)\n\ndef _rocm_lib_paths(repository_ctx, lib, basedir):\n    file_name = _lib_name(lib, version = \"\", static = False)\n    return [\n        repository_ctx.path(\"%s/lib64/%s\" % (basedir, file_name)),\n        repository_ctx.path(\"%s/lib64/stubs/%s\" % (basedir, file_name)),\n        repository_ctx.path(\"%s/lib/x86_64-linux-gnu/%s\" % (basedir, file_name)),\n        repository_ctx.path(\"%s/lib/%s\" % (basedir, file_name)),\n        repository_ctx.path(\"%s/%s\" % (basedir, file_name)),\n    ]\n\ndef _batch_files_exist(repository_ctx, libs_paths, bash_bin):\n    all_paths = []\n    for _, lib_paths in libs_paths:\n        for lib_path in lib_paths:\n            all_paths.append(lib_path)\n    return files_exist(repository_ctx, all_paths, bash_bin)\n\ndef _select_rocm_lib_paths(repository_ctx, libs_paths, bash_bin):\n    test_results = _batch_files_exist(repository_ctx, libs_paths, bash_bin)\n\n    libs = {}\n    i = 0\n    for name, lib_paths in libs_paths:\n        selected_path = None\n        for path in lib_paths:\n            if test_results[i] and selected_path == None:\n                # For each lib select the first path that exists.\n                selected_path = path\n            i = i + 1\n        if selected_path == None:\n            auto_configure_fail(\"Cannot find rocm library %s\" % name)\n\n        libs[name] = struct(file_name = selected_path.basename, path = realpath(repository_ctx, selected_path, bash_bin))\n\n    return libs\n\ndef _find_libs(repository_ctx, rocm_config, hipfft_or_rocfft, miopen_path, rccl_path, bash_bin):\n    \"\"\"Returns the ROCm libraries on the system.\n\n    Args:\n      repository_ctx: The repository context.\n      rocm_config: The ROCm config as returned by _get_rocm_config\n      bash_bin: the path to the bash interpreter\n\n    Returns:\n      Map of library names to structs of filename and path\n    \"\"\"\n    libs_paths = [\n        (name, _rocm_lib_paths(repository_ctx, name, path))\n        for name, path in [\n            (\"amdhip64\", rocm_config.rocm_toolkit_path + \"/hip\"),\n            (\"rocblas\", rocm_config.rocm_toolkit_path),\n            (hipfft_or_rocfft, rocm_config.rocm_toolkit_path),\n            (\"hiprand\", rocm_config.rocm_toolkit_path),\n            (\"MIOpen\", miopen_path),\n            (\"rccl\", rccl_path),\n            (\"hipsparse\", rocm_config.rocm_toolkit_path),\n            (\"roctracer64\", rocm_config.rocm_toolkit_path + \"/roctracer\"),\n            (\"rocsolver\", rocm_config.rocm_toolkit_path),\n        ]\n    ]\n    if int(rocm_config.rocm_version_number) >= 40500:\n        libs_paths.append((\"hipsolver\", _rocm_lib_paths(repository_ctx, \"hipsolver\", rocm_config.rocm_toolkit_path)))\n        libs_paths.append((\"hipblas\", _rocm_lib_paths(repository_ctx, \"hipblas\", rocm_config.rocm_toolkit_path)))\n    return _select_rocm_lib_paths(repository_ctx, libs_paths, bash_bin)\n\ndef _exec_find_rocm_config(repository_ctx, script_path):\n    python_bin = get_python_bin(repository_ctx)\n\n    # If used with remote execution then repository_ctx.execute() can't\n    # access files from the source tree. A trick is to read the contents\n    # of the file in Starlark and embed them as part of the command. In\n    # this case the trick is not sufficient as the find_cuda_config.py\n    # script has more than 8192 characters. 8192 is the command length\n    # limit of cmd.exe on Windows. Thus we additionally need to compress\n    # the contents locally and decompress them as part of the execute().\n    compressed_contents = repository_ctx.read(script_path)\n    decompress_and_execute_cmd = (\n        \"from zlib import decompress;\" +\n        \"from base64 import b64decode;\" +\n        \"from os import system;\" +\n        \"script = decompress(b64decode('%s'));\" % compressed_contents +\n        \"f = open('script.py', 'wb');\" +\n        \"f.write(script);\" +\n        \"f.close();\" +\n        \"system('\\\"%s\\\" script.py');\" % (python_bin)\n    )\n\n    return execute(repository_ctx, [python_bin, \"-c\", decompress_and_execute_cmd])\n\ndef find_rocm_config(repository_ctx, script_path):\n    \"\"\"Returns ROCm config dictionary from running find_rocm_config.py\"\"\"\n    exec_result = _exec_find_rocm_config(repository_ctx, script_path)\n    if exec_result.return_code:\n        auto_configure_fail(\"Failed find_rocm_config.py: %s\" % err_out(exec_result))\n\n    # Parse the dict from stdout.\n    return dict([tuple(x.split(\": \")) for x in exec_result.stdout.splitlines()])\n\ndef _get_rocm_config(repository_ctx, bash_bin, find_rocm_config_script):\n    \"\"\"Detects and returns information about the ROCm installation on the system.\n\n    Args:\n      repository_ctx: The repository context.\n      bash_bin: the path to the path interpreter\n\n    Returns:\n      A struct containing the following fields:\n        rocm_toolkit_path: The ROCm toolkit installation directory.\n        amdgpu_targets: A list of the system's AMDGPU targets.\n        rocm_version_number: The version of ROCm on the system.\n        miopen_version_number: The version of MIOpen on the system.\n        hipruntime_version_number: The version of HIP Runtime on the system.\n    \"\"\"\n    config = find_rocm_config(repository_ctx, find_rocm_config_script)\n    rocm_toolkit_path = config[\"rocm_toolkit_path\"]\n    rocm_version_number = config[\"rocm_version_number\"]\n    miopen_version_number = config[\"miopen_version_number\"]\n    hipruntime_version_number = config[\"hipruntime_version_number\"]\n    return struct(\n        amdgpu_targets = _amdgpu_targets(repository_ctx, rocm_toolkit_path, bash_bin),\n        rocm_toolkit_path = rocm_toolkit_path,\n        rocm_version_number = rocm_version_number,\n        miopen_version_number = miopen_version_number,\n        hipruntime_version_number = hipruntime_version_number,\n    )\n\ndef _tpl_path(repository_ctx, labelname):\n    return repository_ctx.path(Label(\"//third_party/gpus/%s.tpl\" % labelname))\n\ndef _tpl(repository_ctx, tpl, substitutions = {}, out = None):\n    if not out:\n        out = tpl.replace(\":\", \"/\")\n    repository_ctx.template(\n        out,\n        _tpl_path(repository_ctx, tpl),\n        substitutions,\n    )\n\n_DUMMY_CROSSTOOL_BZL_FILE = \"\"\"\ndef error_gpu_disabled():\n  fail(\"ERROR: Building with --config=rocm but Tachyon is not configured \" +\n       \"to build with GPU support. Please re-run ./configure and enter 'Y' \" +\n       \"at the prompt to build with GPU support.\")\n\n  native.genrule(\n      name = \"error_gen_crosstool\",\n      outs = [\"CROSSTOOL\"],\n      cmd = \"echo 'Should not be run.' && exit 1\",\n  )\n\n  native.filegroup(\n      name = \"crosstool\",\n      srcs = [\":CROSSTOOL\"],\n      output_licenses = [\"unencumbered\"],\n  )\n\"\"\"\n\n_DUMMY_CROSSTOOL_BUILD_FILE = \"\"\"\nload(\"//crosstool:error_gpu_disabled.bzl\", \"error_gpu_disabled\")\n\nerror_gpu_disabled()\n\"\"\"\n\ndef _create_dummy_repository(repository_ctx):\n    # Set up BUILD file for rocm/.\n    _tpl(\n        repository_ctx,\n        \"rocm:build_defs.bzl\",\n        {\n            \"%{rocm_is_configured}\": \"False\",\n            \"%{rocm_extra_copts}\": \"[]\",\n            \"%{rocm_gpu_architectures}\": \"[]\",\n            \"%{rocm_version_number}\": \"0\",\n        },\n    )\n    _tpl(\n        repository_ctx,\n        \"rocm:BUILD\",\n        {\n            \"%{hip_lib}\": _lib_name(\"hip\"),\n            \"%{rocblas_lib}\": _lib_name(\"rocblas\"),\n            \"%{hipblas_lib}\": _lib_name(\"hipblas\"),\n            \"%{miopen_lib}\": _lib_name(\"miopen\"),\n            \"%{rccl_lib}\": _lib_name(\"rccl\"),\n            \"%{hipfft_or_rocfft}\": _lib_name(\"hipfft\"),\n            \"%{hipfft_or_rocfft_lib}\": _lib_name(\"hipfft\"),\n            \"%{hiprand_lib}\": _lib_name(\"hiprand\"),\n            \"%{hipsparse_lib}\": _lib_name(\"hipsparse\"),\n            \"%{roctracer_lib}\": _lib_name(\"roctracer64\"),\n            \"%{rocsolver_lib}\": _lib_name(\"rocsolver\"),\n            \"%{hipsolver_lib}\": _lib_name(\"hipsolver\"),\n            \"%{copy_rules}\": \"\",\n            \"%{rocm_headers}\": \"\",\n        },\n    )\n\n    # Create dummy files for the ROCm toolkit since they are still required by\n    # tensorflow/compiler/xla/stream_executor/rocm:rocm_rpath\n    repository_ctx.file(\"rocm/hip/include/hip/hip_runtime.h\", \"\")\n\n    # Set up rocm_config.h, which is used by\n    # tensorflow/compiler/xla/stream_executor/dso_loader.cc.\n    _tpl(\n        repository_ctx,\n        \"rocm:rocm_config.h\",\n        {\n            \"%{rocm_toolkit_path}\": _DEFAULT_ROCM_TOOLKIT_PATH,\n        },\n        \"rocm/rocm/rocm_config.h\",\n    )\n\n    # If rocm_configure is not configured to build with GPU support, and the user\n    # attempts to build with --config=rocm, add a dummy build rule to intercept\n    # this and fail with an actionable error message.\n    repository_ctx.file(\n        \"crosstool/error_gpu_disabled.bzl\",\n        _DUMMY_CROSSTOOL_BZL_FILE,\n    )\n    repository_ctx.file(\"crosstool/BUILD\", _DUMMY_CROSSTOOL_BUILD_FILE)\n\ndef _norm_path(path):\n    \"\"\"Returns a path with '/' and remove the trailing slash.\"\"\"\n    path = path.replace(\"\\\\\", \"/\")\n    if path[-1] == \"/\":\n        path = path[:-1]\n    return path\n\ndef _genrule(src_dir, genrule_name, command, outs):\n    \"\"\"Returns a string with a genrule.\n\n    Genrule executes the given command and produces the given outputs.\n    \"\"\"\n    return (\n        \"genrule(\\n\" +\n        '    name = \"' +\n        genrule_name + '\",\\n' +\n        \"    outs = [\\n\" +\n        outs +\n        \"\\n    ],\\n\" +\n        '    cmd = \"\"\"\\n' +\n        command +\n        '\\n   \"\"\",\\n' +\n        \")\\n\"\n    )\n\ndef _compute_rocm_extra_copts(repository_ctx, amdgpu_targets):\n    amdgpu_target_flags = [\"--amdgpu-target=\" +\n                           amdgpu_target for amdgpu_target in amdgpu_targets]\n    return str(amdgpu_target_flags)\n\ndef _create_local_rocm_repository(repository_ctx):\n    \"\"\"Creates the repository containing files set up to build with ROCm.\"\"\"\n\n    tpl_paths = {labelname: _tpl_path(repository_ctx, labelname) for labelname in [\n        \"rocm:build_defs.bzl\",\n        \"rocm:BUILD\",\n        \"crosstool:BUILD.rocm\",\n        \"crosstool:hipcc_cc_toolchain_config.bzl\",\n        \"crosstool:clang/bin/crosstool_wrapper_driver_rocm\",\n        \"rocm:rocm_config.h\",\n    ]}\n\n    find_rocm_config_script = repository_ctx.path(Label(\"@kroma_network_tachyon//third_party/gpus:find_rocm_config.py.gz.base64\"))\n\n    bash_bin = get_bash_bin(repository_ctx)\n    rocm_config = _get_rocm_config(repository_ctx, bash_bin, find_rocm_config_script)\n\n    # For ROCm 4.1 and above use hipfft, older ROCm versions use rocfft\n    rocm_version_number = int(rocm_config.rocm_version_number)\n    hipfft_or_rocfft = \"rocfft\" if rocm_version_number < 40100 else \"hipfft\"\n\n    # For ROCm 5.2 and above, find MIOpen and RCCL in the main rocm lib path\n    miopen_path = rocm_config.rocm_toolkit_path + \"/miopen\" if rocm_version_number < 50200 else rocm_config.rocm_toolkit_path\n    rccl_path = rocm_config.rocm_toolkit_path + \"/rccl\" if rocm_version_number < 50200 else rocm_config.rocm_toolkit_path\n\n    # Copy header and library files to execroot.\n    # rocm_toolkit_path\n    rocm_toolkit_path = rocm_config.rocm_toolkit_path\n    copy_rules = [\n        make_copy_dir_rule(\n            repository_ctx,\n            name = \"rocm-include\",\n            src_dir = rocm_toolkit_path + \"/include\",\n            out_dir = \"rocm/include\",\n            exceptions = [\"gtest\", \"gmock\"],\n        ),\n    ]\n\n    # explicitly copy (into the local_config_rocm repo) the $ROCM_PATH/hiprand/include and\n    # $ROCM_PATH/rocrand/include dirs, only once the softlink to them in $ROCM_PATH/include\n    # dir has been removed. This removal will happen in a near-future ROCm release.\n    hiprand_include = \"\"\n    hiprand_include_softlink = rocm_config.rocm_toolkit_path + \"/include/hiprand\"\n    softlink_exists = files_exist(repository_ctx, [hiprand_include_softlink], bash_bin)\n    if not softlink_exists[0]:\n        hiprand_include = '\":hiprand-include\",\\n'\n        copy_rules.append(\n            make_copy_dir_rule(\n                repository_ctx,\n                name = \"hiprand-include\",\n                src_dir = rocm_toolkit_path + \"/hiprand/include\",\n                out_dir = \"rocm/include/hiprand\",\n            ),\n        )\n\n    rocrand_include = \"\"\n    rocrand_include_softlink = rocm_config.rocm_toolkit_path + \"/include/rocrand\"\n    softlink_exists = files_exist(repository_ctx, [rocrand_include_softlink], bash_bin)\n    if not softlink_exists[0]:\n        rocrand_include = '\":rocrand-include\",\\n'\n        copy_rules.append(\n            make_copy_dir_rule(\n                repository_ctx,\n                name = \"rocrand-include\",\n                src_dir = rocm_toolkit_path + \"/rocrand/include\",\n                out_dir = \"rocm/include/rocrand\",\n            ),\n        )\n\n    rocm_libs = _find_libs(repository_ctx, rocm_config, hipfft_or_rocfft, miopen_path, rccl_path, bash_bin)\n    rocm_lib_srcs = []\n    rocm_lib_outs = []\n    for lib in rocm_libs.values():\n        rocm_lib_srcs.append(lib.path)\n        rocm_lib_outs.append(\"rocm/lib/\" + lib.file_name)\n    copy_rules.append(make_copy_files_rule(\n        repository_ctx,\n        name = \"rocm-lib\",\n        srcs = rocm_lib_srcs,\n        outs = rocm_lib_outs,\n    ))\n\n    clang_offload_bundler_path = rocm_toolkit_path + \"/llvm/bin/clang-offload-bundler\"\n\n    # copy files mentioned in third_party/gpus/rocm/BUILD\n    copy_rules.append(make_copy_files_rule(\n        repository_ctx,\n        name = \"rocm-bin\",\n        srcs = [\n            clang_offload_bundler_path,\n        ],\n        outs = [\n            \"rocm/bin/\" + \"clang-offload-bundler\",\n        ],\n    ))\n\n    # Set up BUILD file for rocm/\n    repository_ctx.template(\n        \"rocm/build_defs.bzl\",\n        tpl_paths[\"rocm:build_defs.bzl\"],\n        {\n            \"%{rocm_is_configured}\": \"True\",\n            \"%{rocm_extra_copts}\": _compute_rocm_extra_copts(\n                repository_ctx,\n                rocm_config.amdgpu_targets,\n            ),\n            \"%{rocm_gpu_architectures}\": str(rocm_config.amdgpu_targets),\n            \"%{rocm_version_number}\": str(rocm_version_number),\n        },\n    )\n\n    repository_dict = {\n        \"%{hip_lib}\": rocm_libs[\"amdhip64\"].file_name,\n        \"%{rocblas_lib}\": rocm_libs[\"rocblas\"].file_name,\n        \"%{hipfft_or_rocfft}\": hipfft_or_rocfft,\n        \"%{hipfft_or_rocfft_lib}\": rocm_libs[hipfft_or_rocfft].file_name,\n        \"%{hiprand_lib}\": rocm_libs[\"hiprand\"].file_name,\n        \"%{miopen_lib}\": rocm_libs[\"MIOpen\"].file_name,\n        \"%{rccl_lib}\": rocm_libs[\"rccl\"].file_name,\n        \"%{hipsparse_lib}\": rocm_libs[\"hipsparse\"].file_name,\n        \"%{roctracer_lib}\": rocm_libs[\"roctracer64\"].file_name,\n        \"%{rocsolver_lib}\": rocm_libs[\"rocsolver\"].file_name,\n        \"%{copy_rules}\": \"\\n\".join(copy_rules),\n        \"%{rocm_headers}\": ('\":rocm-include\",\\n' +\n                            hiprand_include +\n                            rocrand_include),\n    }\n    if rocm_version_number >= 40500:\n        repository_dict[\"%{hipsolver_lib}\"] = rocm_libs[\"hipsolver\"].file_name\n        repository_dict[\"%{hipblas_lib}\"] = rocm_libs[\"hipblas\"].file_name\n\n    repository_ctx.template(\n        \"rocm/BUILD\",\n        tpl_paths[\"rocm:BUILD\"],\n        repository_dict,\n    )\n\n    # Set up crosstool/\n\n    cc = find_cc(repository_ctx)\n\n    host_compiler_includes = get_cxx_inc_directories(repository_ctx, cc)\n\n    host_compiler_prefix = get_host_environ(repository_ctx, _GCC_HOST_COMPILER_PREFIX, \"/usr/bin\")\n\n    rocm_defines = {}\n\n    rocm_defines[\"%{host_compiler_prefix}\"] = host_compiler_prefix\n\n    rocm_defines[\"%{linker_bin_path}\"] = rocm_config.rocm_toolkit_path + \"/hcc/compiler/bin\"\n\n    # For gcc, do not canonicalize system header paths; some versions of gcc\n    # pick the shortest possible path for system includes when creating the\n    # .d file - given that includes that are prefixed with \"../\" multiple\n    # time quickly grow longer than the root of the tree, this can lead to\n    # bazel's header check failing.\n    rocm_defines[\"%{extra_no_canonical_prefixes_flags}\"] = \"\\\"-fno-canonical-system-headers\\\"\"\n\n    rocm_defines[\"%{unfiltered_compile_flags}\"] = to_list_of_strings([\n        \"-DTACHYON_USE_ROCM=1\",\n        \"-D__HIP_PLATFORM_HCC__\",\n        \"-DEIGEN_USE_HIP\",\n    ])\n\n    rocm_defines[\"%{host_compiler_path}\"] = \"clang/bin/crosstool_wrapper_driver_is_not_gcc\"\n\n    rocm_defines[\"%{cxx_builtin_include_directories}\"] = to_list_of_strings(\n        host_compiler_includes + _rocm_include_path(repository_ctx, rocm_config, bash_bin),\n    )\n\n    verify_build_defines(rocm_defines)\n\n    # Only expand template variables in the BUILD file\n    repository_ctx.template(\n        \"crosstool/BUILD\",\n        tpl_paths[\"crosstool:BUILD.rocm\"],\n        rocm_defines,\n    )\n\n    # No templating of cc_toolchain_config - use attributes and templatize the\n    # BUILD file.\n    repository_ctx.template(\n        \"crosstool/cc_toolchain_config.bzl\",\n        tpl_paths[\"crosstool:hipcc_cc_toolchain_config.bzl\"],\n    )\n\n    repository_ctx.template(\n        \"crosstool/clang/bin/crosstool_wrapper_driver_is_not_gcc\",\n        tpl_paths[\"crosstool:clang/bin/crosstool_wrapper_driver_rocm\"],\n        {\n            \"%{cpu_compiler}\": str(cc),\n            \"%{hipcc_path}\": rocm_config.rocm_toolkit_path + \"/hip/bin/hipcc\",\n            \"%{hipcc_env}\": _hipcc_env(repository_ctx),\n            \"%{rocr_runtime_path}\": rocm_config.rocm_toolkit_path + \"/lib\",\n            \"%{rocr_runtime_library}\": \"hsa-runtime64\",\n            \"%{hip_runtime_path}\": rocm_config.rocm_toolkit_path + \"/hip/lib\",\n            \"%{hip_runtime_library}\": \"amdhip64\",\n            \"%{crosstool_verbose}\": _crosstool_verbose(repository_ctx),\n            \"%{gcc_host_compiler_path}\": str(cc),\n        },\n    )\n\n    # Set up rocm_config.h, which is used by\n    # tensorflow/compiler/xla/stream_executor/dso_loader.cc.\n    repository_ctx.template(\n        \"rocm/rocm/rocm_config.h\",\n        tpl_paths[\"rocm:rocm_config.h\"],\n        {\n            \"%{rocm_amdgpu_targets}\": \",\".join(\n                [\"\\\"%s\\\"\" % c for c in rocm_config.amdgpu_targets],\n            ),\n            \"%{rocm_toolkit_path}\": rocm_config.rocm_toolkit_path,\n            \"%{rocm_version_number}\": rocm_config.rocm_version_number,\n            \"%{miopen_version_number}\": rocm_config.miopen_version_number,\n            \"%{hipruntime_version_number}\": rocm_config.hipruntime_version_number,\n        },\n    )\n\ndef _create_remote_rocm_repository(repository_ctx, remote_config_repo):\n    \"\"\"Creates pointers to a remotely configured repo set up to build with ROCm.\"\"\"\n    _tpl(\n        repository_ctx,\n        \"rocm:build_defs.bzl\",\n        {\n            \"%{rocm_is_configured}\": \"True\",\n            \"%{rocm_extra_copts}\": _compute_rocm_extra_copts(\n                repository_ctx,\n                [],  #_compute_capabilities(repository_ctx)\n            ),\n        },\n    )\n    repository_ctx.template(\n        \"rocm/BUILD\",\n        config_repo_label(remote_config_repo, \"rocm:BUILD\"),\n        {},\n    )\n    repository_ctx.template(\n        \"rocm/build_defs.bzl\",\n        config_repo_label(remote_config_repo, \"rocm:build_defs.bzl\"),\n        {},\n    )\n    repository_ctx.template(\n        \"rocm/rocm/rocm_config.h\",\n        config_repo_label(remote_config_repo, \"rocm:rocm/rocm_config.h\"),\n        {},\n    )\n    repository_ctx.template(\n        \"crosstool/BUILD\",\n        config_repo_label(remote_config_repo, \"crosstool:BUILD\"),\n        {},\n    )\n    repository_ctx.template(\n        \"crosstool/cc_toolchain_config.bzl\",\n        config_repo_label(remote_config_repo, \"crosstool:cc_toolchain_config.bzl\"),\n        {},\n    )\n    repository_ctx.template(\n        \"crosstool/clang/bin/crosstool_wrapper_driver_is_not_gcc\",\n        config_repo_label(remote_config_repo, \"crosstool:clang/bin/crosstool_wrapper_driver_is_not_gcc\"),\n        {},\n    )\n\ndef _rocm_autoconf_impl(repository_ctx):\n    \"\"\"Implementation of the rocm_autoconf repository rule.\"\"\"\n    if not _enable_rocm(repository_ctx):\n        _create_dummy_repository(repository_ctx)\n    elif get_host_environ(repository_ctx, _TACHYON_ROCM_CONFIG_REPO) != None:\n        _create_remote_rocm_repository(\n            repository_ctx,\n            get_host_environ(repository_ctx, _TACHYON_ROCM_CONFIG_REPO),\n        )\n    else:\n        _create_local_rocm_repository(repository_ctx)\n\n_ENVIRONS = [\n    _GCC_HOST_COMPILER_PATH,\n    _GCC_HOST_COMPILER_PREFIX,\n    \"TACHYON_NEED_ROCM\",\n    _ROCM_TOOLKIT_PATH,\n    _TACHYON_ROCM_AMDGPU_TARGETS,\n]\n\nremote_rocm_configure = repository_rule(\n    implementation = _create_local_rocm_repository,\n    environ = _ENVIRONS,\n    # NOTE(chokobole): Do we need this?\n    # See https://github.com/google/mediapipe/issues/1744\n    # remotable = True,\n    attrs = {\n        \"environ\": attr.string_dict(),\n    },\n)\n\nrocm_configure = repository_rule(\n    implementation = _rocm_autoconf_impl,\n    environ = _ENVIRONS + [_TACHYON_ROCM_CONFIG_REPO],\n)\n\"\"\"Detects and configures the local ROCm toolchain.\n\nAdd the following to your WORKSPACE FILE:\n\n```python\nrocm_configure(name = \"local_config_rocm\")\n```\n\nArgs:\n  name: A unique name for this workspace rule.\n\"\"\"\n"
  },
  {
    "path": "third_party/gtest/BUILD.bazel",
    "content": ""
  },
  {
    "path": "third_party/gtest/add_missing_linkopts.patch",
    "content": "diff --git a/BUILD.bazel b/BUILD.bazel\nindex ac62251e..f4df5be9 100644\n--- a/BUILD.bazel\n+++ b/BUILD.bazel\n@@ -128,7 +128,11 @@ cc_library(\n             \"-lm\",\n             \"-pthread\",\n         ],\n-        \"//conditions:default\": [\"-pthread\"],\n+        \"//conditions:default\": [\n+            # NOTE(chokobole): Needed when compiled with --config cuda\n+            \"-lm\",\n+            \"-pthread\"\n+        ],\n     }),\n     deps = select({\n         \":has_absl\": [\n"
  },
  {
    "path": "third_party/gtest/print_with_to_string.patch",
    "content": "diff --git a/googletest/include/gtest/gtest-printers.h b/googletest/include/gtest/gtest-printers.h\nindex 0055e37f..de805706 100644\n--- a/googletest/include/gtest/gtest-printers.h\n+++ b/googletest/include/gtest/gtest-printers.h\n@@ -220,6 +220,14 @@ struct StreamPrinter {\n \n }  // namespace internal_stream_operator_without_lexical_name_lookup\n \n+struct ToStringPrinter {\n+  template <typename T, typename = typename std::enable_if<\n+                            internal::HasToString<T>::value>::type>\n+  static void PrintValue(const T& value, ::std::ostream* os) {\n+    *os << value.ToString();\n+  }\n+};\n+\n struct ProtobufPrinter {\n   // We print a protobuf using its ShortDebugString() when the string\n   // doesn't exceed this many characters; otherwise we print it using\n@@ -298,6 +306,7 @@ struct FindFirstPrinter<\n //  - Print function pointers.\n //  - Print object pointers.\n //  - Use the stream operator, if available.\n+//  - Use ToString(), if available.\n //  - Print protocol buffers.\n //  - Print types convertible to BiggestInt.\n //  - Print types convertible to StringView, if available.\n@@ -307,7 +316,7 @@ void PrintWithFallback(const T& value, ::std::ostream* os) {\n   using Printer = typename FindFirstPrinter<\n       T, void, ContainerPrinter, FunctionPointerPrinter, PointerPrinter,\n       internal_stream_operator_without_lexical_name_lookup::StreamPrinter,\n-      ProtobufPrinter, ConvertibleToIntegerPrinter,\n+      ToStringPrinter, ProtobufPrinter, ConvertibleToIntegerPrinter,\n       ConvertibleToStringViewPrinter, RawBytesPrinter, FallbackPrinter>::type;\n   Printer::PrintValue(value, os);\n }\ndiff --git a/googletest/include/gtest/internal/gtest-internal.h b/googletest/include/gtest/internal/gtest-internal.h\nindex 3121d428..2e1b428d 100644\n--- a/googletest/include/gtest/internal/gtest-internal.h\n+++ b/googletest/include/gtest/internal/gtest-internal.h\n@@ -888,6 +888,26 @@ class GTEST_API_ Random {\n #define GTEST_REMOVE_REFERENCE_AND_CONST_(T) \\\n   typename std::remove_const<typename std::remove_reference<T>::type>::type\n \n+// HasToString<T>::value is a compile-time bool constant\n+// that's true if and only if T has methods ToString() return std::string.\n+template <typename T>\n+class HasToString {\n+ private:\n+  template <typename C>\n+  static auto CheckToString(C*) -> typename std::is_same<\n+      std::string, decltype(std::declval<const C&>().ToString())>::type;\n+  template <typename>\n+  static std::false_type CheckToString(...);\n+\n+  using HasToStringType = decltype(CheckToString<T>(nullptr));\n+\n+ public:\n+  static constexpr bool value = HasToStringType::value;\n+};\n+\n+template <typename T>\n+constexpr bool HasToString<T>::value;\n+\n // HasDebugStringAndShortDebugString<T>::value is a compile-time bool constant\n // that's true if and only if T has methods DebugString() and ShortDebugString()\n // that return std::string.\n"
  },
  {
    "path": "third_party/hwloc/BUILD.bazel",
    "content": "# BUILD file to make this directory a package.\n\npackage(\n    # copybara:uncomment default_applicable_licenses = [\"//tachyon:license\"],\n    licenses = [\"notice\"],\n)\n\nexports_files(\n    [\"static-components.h\"],\n)\n"
  },
  {
    "path": "third_party/hwloc/BUILD.system",
    "content": "# hwloc: Portable Hardware Locality Library\nlicenses([\"notice\"])\n\nfilegroup(\n    name = \"COPYING\",\n    visibility = [\"//visibility:public\"],\n)\n\ncc_library(\n    name = \"hwloc\",\n    defines = [\"TACHYON_USE_NUMA\"],\n    linkopts = select({\n        \"@kroma_network_tachyon//:tachyon_has_numa\": [\"-lhwloc\"],\n        \"//conditions:default\": [],\n    }),\n    visibility = [\"//visibility:public\"],\n)\n"
  },
  {
    "path": "third_party/hwloc/hwloc.BUILD",
    "content": "load(\"@bazel_skylib//rules:expand_template.bzl\", \"expand_template\")\n\n# hwloc: Portable Hardware Locality Library\nload(\"@local_config_cuda//cuda:build_defs.bzl\", \"if_cuda\")\n\npackage(\n    default_visibility = [\"//visibility:public\"],\n)\n\nlicenses([\"notice\"])\n\nexports_files([\"COPYING\"])\n\nCOMMON_INCLUDE_COPTS = [\n    \"-I.\",\n    \"-Ihwloc\",\n    \"-Iinclude\",\n]\n\nDISABLE_WARNINGS_COPTS = [\n    \"-Wno-vla\",\n]\n\nVAR_SETTINGS_COPTS = [\n    \"-DHWLOC_DUMPED_HWDATA_DIR=\",\n    \"-DRUNSTATEDIR=\",\n]\n\n_INCLUDE_HWLOC_AUTOIGEN_CONFIG_H_COMMON_SUBS = {\n    \"#undef HWLOC_VERSION_MAJOR\": \"#define HWLOC_VERSION_MAJOR 2\",\n    \"#undef HWLOC_VERSION_MINOR\": \"#define HWLOC_VERSION_MINOR 0\",\n    \"#undef HWLOC_VERSION_RELEASE\": \"#define HWLOC_VERSION_RELEASE 3\",\n    \"#undef HWLOC_VERSION_GREEK\": \"#define HWLOC_VERSION_GREEK \\\"\\\"\",\n    \"#undef HWLOC_VERSION\": \"#define HWLOC_VERSION \\\"2.0.3\\\"\",\n    \"#undef hwloc_pid_t\": \"#define hwloc_pid_t pid_t\",\n    \"#undef hwloc_thread_t\": \"#define hwloc_thread_t pthread_t\",\n    \"#  undef HWLOC_HAVE_STDINT_H\": \"#  define HWLOC_HAVE_STDINT_H 1\",\n    \"#undef HWLOC_SYM_TRANSFORM\": \"#define HWLOC_SYM_TRANSFORM 0\",\n    \"#undef HWLOC_SYM_PREFIX_CAPS\": \"#define HWLOC_SYM_PREFIX_CAPS HWLOC_\",\n    \"#undef HWLOC_SYM_PREFIX\": \"#define HWLOC_SYM_PREFIX hwloc_\",\n}\n\n_INCLUDE_HWLOC_AUTOIGEN_CONFIG_H_LINUX_SUBS = dict(_INCLUDE_HWLOC_AUTOIGEN_CONFIG_H_COMMON_SUBS)\n\n_INCLUDE_HWLOC_AUTOIGEN_CONFIG_H_LINUX_SUBS.update({\n    \"#undef HWLOC_LINUX_SYS\": \"#define HWLOC_LINUX_SYS 1\",\n})\n\nexpand_template(\n    name = \"include_hwloc_autogen_config_h\",\n    out = \"include/hwloc/autogen/config.h\",\n    substitutions = select({\n        \"@kroma_network_tachyon//:linux_x86_64\": _INCLUDE_HWLOC_AUTOIGEN_CONFIG_H_LINUX_SUBS,\n        \"//conditions:default\": _INCLUDE_HWLOC_AUTOIGEN_CONFIG_H_COMMON_SUBS,\n    }),\n    template = \"include/hwloc/autogen/config.h.in\",\n)\n\n_INCLUDE_PRIVATE_HWLOC_AUTOIGEN_CONFIG_H_COMMON_SUBS = {\n    \"#undef HAVE_CLOCK_GETTIME\": \"#define HAVE_CLOCK_GETTIME 1\",\n    \"#undef HAVE_CTYPE_H\": \"#define HAVE_CTYPE_H 1\",\n    \"#undef HAVE_DECL_CTL_HW\": \"#define HAVE_DECL_CTL_HW 0\",\n    \"#undef HAVE_DECL_FABSF\": \"#define HAVE_DECL_FABSF 1\",\n    \"#undef HAVE_DECL_GETEXECNAME\": \"#define HAVE_DECL_GETEXECNAME 0\",\n    \"#undef HAVE_DECL_GETMODULEFILENAME\": \"#define HAVE_DECL_GETMODULEFILENAME 0\",\n    \"#undef HAVE_DECL_GETPROGNAME\": \"#define HAVE_DECL_GETPROGNAME 0\",\n    \"#undef HAVE_DECL_HW_NCPU\": \"#define HAVE_DECL_HW_NCPU 0\",\n    \"#undef HAVE_DECL_MODFF\": \"#define HAVE_DECL_MODFF 1\",\n    \"#undef HAVE_DECL_PTHREAD_GETAFFINITY_NP\": \"#define HAVE_DECL_PTHREAD_GETAFFINITY_NP 1\",\n    \"#undef HAVE_DECL_PTHREAD_SETAFFINITY_NP\": \"#define HAVE_DECL_PTHREAD_SETAFFINITY_NP 1\",\n    \"#undef HAVE_DECL_RUNNING_ON_VALGRIND\": \"#define HAVE_DECL_RUNNING_ON_VALGRIND 0\",\n    \"#undef HAVE_DECL_SCHED_GETCPU\": \"#define HAVE_DECL_SCHED_GETCPU 1\",\n    \"#undef HAVE_DECL_SNPRINTF\": \"#define HAVE_DECL_SNPRINTF 1\",\n    \"#undef HAVE_DECL_STRTOULL\": \"#define HAVE_DECL_STRTOULL 1\",\n    \"#undef HAVE_DECL__PUTENV\": \"#define HAVE_DECL__PUTENV 0\",\n    \"#undef HAVE_DECL__SC_LARGE_PAGESIZE\": \"#define HAVE_DECL__SC_LARGE_PAGESIZE 0\",\n    \"#undef HAVE_DECL__SC_NPROCESSORS_CONF\": \"#define HAVE_DECL__SC_NPROCESSORS_CONF 1\",\n    \"#undef HAVE_DECL__SC_NPROCESSORS_ONLN\": \"#define HAVE_DECL__SC_NPROCESSORS_ONLN 1\",\n    \"#undef HAVE_DECL__SC_NPROC_CONF\": \"#define HAVE_DECL__SC_NPROC_CONF 0\",\n    \"#undef HAVE_DECL__SC_NPROC_ONLN\": \"#define HAVE_DECL__SC_NPROC_ONLN 0\",\n    \"#undef HAVE_DECL__SC_PAGESIZE\": \"#define HAVE_DECL__SC_PAGESIZE 1\",\n    \"#undef HAVE_DECL__SC_PAGE_SIZE\": \"#define HAVE_DECL__SC_PAGE_SIZE 1\",\n    \"#undef HAVE_DECL__STRDUP\": \"#define HAVE_DECL__STRDUP 0\",\n    \"#undef HAVE_DIRENT_H\": \"#define HAVE_DIRENT_H 1\",\n    \"#undef HAVE_DLFCN_H\": \"#define HAVE_DLFCN_H 1\",\n    \"#undef HAVE_FFSL\": \"#define HAVE_FFSL 1\",\n    \"#undef HAVE_FFS\": \"#define HAVE_FFS 1\",\n    \"#undef HAVE_GETPAGESIZE\": \"#define HAVE_GETPAGESIZE 1\",\n    \"#undef HAVE_INTTYPES_H\": \"#define HAVE_INTTYPES_H 1\",\n    \"#undef HAVE_LANGINFO_H\": \"#define HAVE_LANGINFO_H 1\",\n    \"#undef HAVE_LOCALE_H\": \"#define HAVE_LOCALE_H 1\",\n    \"#undef HAVE_MALLOC_H\": \"#define HAVE_MALLOC_H 1\",\n    \"#undef HAVE_MEMALIGN\": \"#define HAVE_MEMALIGN 1\",\n    \"#undef HAVE_MEMORY_H\": \"#define HAVE_MEMORY_H 1\",\n    \"#undef HAVE_MKSTEMP\": \"#define HAVE_MKSTEMP 1\",\n    \"#undef HAVE_NL_LANGINFO\": \"#define HAVE_NL_LANGINFO 1\",\n    \"#undef HAVE_OPENAT\": \"#define HAVE_OPENAT 1\",\n    \"#undef HAVE_POSIX_MEMALIGN\": \"#define HAVE_POSIX_MEMALIGN 1\",\n    \"#undef HAVE_PTHREAD_T\": \"#define HAVE_PTHREAD_T 1\",\n    \"#undef HAVE_PUTWC\": \"#define HAVE_PUTWC 1\",\n    \"#undef HAVE_SETLOCALE\": \"#define HAVE_SETLOCALE 1\",\n    \"#undef HAVE_SSIZE_T\": \"#define HAVE_SSIZE_T 1\",\n    \"#undef HAVE_STDINT_H\": \"#define HAVE_STDINT_H 1\",\n    \"#undef HAVE_STDLIB_H\": \"#define HAVE_STDLIB_H 1\",\n    \"#undef HAVE_STRCASECMP\": \"#define HAVE_STRCASECMP 1\",\n    \"#undef HAVE_STRFTIME\": \"#define HAVE_STRFTIME 1\",\n    \"#undef HAVE_STRINGS_H\": \"#define HAVE_STRINGS_H 1\",\n    \"#undef HAVE_STRING_H\": \"#define HAVE_STRING_H 1\",\n    \"#undef HAVE_STRNCASECMP\": \"#define HAVE_STRNCASECMP 1\",\n    \"#undef HAVE_SYS_MMAN_H\": \"#define HAVE_SYS_MMAN_H 1\",\n    \"#undef HAVE_SYS_PARAM_H\": \"#define HAVE_SYS_PARAM_H 1\",\n    \"#undef HAVE_SYS_STAT_H\": \"#define HAVE_SYS_STAT_H 1\",\n    \"#undef HAVE_SYS_SYSCTL_H\": \"#define HAVE_SYS_SYSCTL_H 1\",\n    \"#undef HAVE_SYS_TYPES_H\": \"#define HAVE_SYS_TYPES_H 1\",\n    \"#undef HAVE_SYS_UTSNAME_H\": \"#define HAVE_SYS_UTSNAME_H 1\",\n    \"#undef HAVE_TIME_H\": \"#define HAVE_TIME_H 1\",\n    \"#undef HAVE_UNAME\": \"#define HAVE_UNAME 1\",\n    \"#undef HAVE_UNISTD_H\": \"#define HAVE_UNISTD_H 1\",\n    \"#undef HAVE_USELOCALE\": \"#define HAVE_USELOCALE 1\",\n    \"#undef HAVE_WCHAR_T\": \"#define HAVE_WCHAR_T 1\",\n    \"#undef HAVE_X11_KEYSYM_H\": \"#define HAVE_X11_KEYSYM_H 1\",\n    \"#undef HAVE_X11_XLIB_H\": \"#define HAVE_X11_XLIB_H 1\",\n    \"#undef HAVE_X11_XUTIL_H\": \"#define HAVE_X11_XUTIL_H 1\",\n    \"#undef HAVE___PROGNAME\": \"#define HAVE___PROGNAME 1\",\n    \"#undef HWLOC_C_HAVE_VISIBILITY\": \"#define HWLOC_C_HAVE_VISIBILITY 1\",\n    \"#undef HWLOC_HAVE_ATTRIBUTE_ALIGNED\": \"#define HWLOC_HAVE_ATTRIBUTE_ALIGNED 1\",\n    \"#undef HWLOC_HAVE_ATTRIBUTE_ALWAYS_INLINE\": \"#define HWLOC_HAVE_ATTRIBUTE_ALWAYS_INLINE 1\",\n    \"#undef HWLOC_HAVE_ATTRIBUTE_COLD\": \"#define HWLOC_HAVE_ATTRIBUTE_COLD 1\",\n    \"#undef HWLOC_HAVE_ATTRIBUTE_CONSTRUCTOR\": \"#define HWLOC_HAVE_ATTRIBUTE_CONSTRUCTOR 1\",\n    \"#undef HWLOC_HAVE_ATTRIBUTE_CONST\": \"#define HWLOC_HAVE_ATTRIBUTE_CONST 1\",\n    \"#undef HWLOC_HAVE_ATTRIBUTE_DEPRECATED\": \"#define HWLOC_HAVE_ATTRIBUTE_DEPRECATED 1\",\n    \"#undef HWLOC_HAVE_ATTRIBUTE_FORMAT\": \"#define HWLOC_HAVE_ATTRIBUTE_FORMAT 1\",\n    \"#undef HWLOC_HAVE_ATTRIBUTE_HOT\": \"#define HWLOC_HAVE_ATTRIBUTE_HOT 1\",\n    \"#undef HWLOC_HAVE_ATTRIBUTE_MALLOC\": \"#define HWLOC_HAVE_ATTRIBUTE_MALLOC 1\",\n    \"#undef HWLOC_HAVE_ATTRIBUTE_MAY_ALIAS\": \"#define HWLOC_HAVE_ATTRIBUTE_MAY_ALIAS 1\",\n    \"#undef HWLOC_HAVE_ATTRIBUTE_NONNULL\": \"#define HWLOC_HAVE_ATTRIBUTE_NONNULL 1\",\n    \"#undef HWLOC_HAVE_ATTRIBUTE_NORETURN\": \"#define HWLOC_HAVE_ATTRIBUTE_NORETURN 1\",\n    \"#undef HWLOC_HAVE_ATTRIBUTE_NO_INSTRUMENT_FUNCTION\": \"#define HWLOC_HAVE_ATTRIBUTE_NO_INSTRUMENT_FUNCTION 1\",\n    \"#undef HWLOC_HAVE_ATTRIBUTE_PACKED\": \"#define HWLOC_HAVE_ATTRIBUTE_PACKED 1\",\n    \"#undef HWLOC_HAVE_ATTRIBUTE_PURE\": \"#define HWLOC_HAVE_ATTRIBUTE_PURE 1\",\n    \"#undef HWLOC_HAVE_ATTRIBUTE_SENTINEL\": \"#define HWLOC_HAVE_ATTRIBUTE_SENTINEL 1\",\n    \"#undef HWLOC_HAVE_ATTRIBUTE_UNUSED\": \"#define HWLOC_HAVE_ATTRIBUTE_UNUSED 1\",\n    \"#undef HWLOC_HAVE_ATTRIBUTE_WARN_UNUSED_RESULT\": \"#define HWLOC_HAVE_ATTRIBUTE_WARN_UNUSED_RESULT 1\",\n    \"#undef HWLOC_HAVE_ATTRIBUTE_WEAK_ALIAS\": \"#define HWLOC_HAVE_ATTRIBUTE_WEAK_ALIAS 1\",\n    \"#undef HWLOC_HAVE_ATTRIBUTE\": \"#define HWLOC_HAVE_ATTRIBUTE 1\",\n    \"#undef HWLOC_HAVE_CPU_SET_S\": \"#define HWLOC_HAVE_CPU_SET_S 1\",\n    \"#undef HWLOC_HAVE_CPU_SET\": \"#define HWLOC_HAVE_CPU_SET 1\",\n    \"#undef HWLOC_HAVE_DECL_FFSL\": \"#define HWLOC_HAVE_DECL_FFSL 1\",\n    \"#undef HWLOC_HAVE_DECL_FFS\": \"#define HWLOC_HAVE_DECL_FFS 1\",\n    \"#undef HWLOC_HAVE_DECL_STRCASECMP\": \"#define HWLOC_HAVE_DECL_STRCASECMP 1\",\n    \"#undef HWLOC_HAVE_DECL_STRNCASECMP\": \"#define HWLOC_HAVE_DECL_STRNCASECMP 1\",\n    \"#undef HWLOC_HAVE_FFSL\": \"#define HWLOC_HAVE_FFSL 1\",\n    \"#undef HWLOC_HAVE_FFS\": \"#define HWLOC_HAVE_FFS 1\",\n    \"#undef HWLOC_HAVE_LIBTERMCAP\": \"#define HWLOC_HAVE_LIBTERMCAP 1\",\n    \"#undef HWLOC_HAVE_LINUXIO\": \"#define HWLOC_HAVE_LINUXIO 1\",\n    \"#undef HWLOC_HAVE_PTHREAD_MUTEX\": \"#define HWLOC_HAVE_PTHREAD_MUTEX 1\",\n    \"#undef HWLOC_HAVE_SCHED_SETAFFINITY\": \"#define HWLOC_HAVE_SCHED_SETAFFINITY 1\",\n    \"#undef HWLOC_HAVE_STDINT_H\": \"#define HWLOC_HAVE_STDINT_H 1\",\n    \"#undef HWLOC_HAVE_SYSCALL\": \"#define HWLOC_HAVE_SYSCALL 1\",\n    \"#undef HWLOC_HAVE_X11_KEYSYM\": \"#define HWLOC_HAVE_X11_KEYSYM 1\",\n    \"#undef HWLOC_HAVE_X86_CPUID\": \"#define HWLOC_HAVE_X86_CPUID 1\",\n    \"#undef HWLOC_SIZEOF_UNSIGNED_INT\": \"#define HWLOC_SIZEOF_UNSIGNED_INT 4\",\n    \"#undef HWLOC_SIZEOF_UNSIGNED_LONG\": \"#define HWLOC_SIZEOF_UNSIGNED_LONG 8\",\n    \"#undef HWLOC_SYM_PREFIX_CAPS\": \"#define HWLOC_SYM_PREFIX_CAPS HWLOC_\",\n    \"#undef HWLOC_SYM_PREFIX\": \"#define HWLOC_SYM_PREFIX hwloc_\",\n    \"#undef HWLOC_SYM_TRANSFORM\": \"#define HWLOC_SYM_TRANSFORM 0\",\n    \"#undef HWLOC_USE_NCURSES\": \"#define HWLOC_USE_NCURSES 1\",\n    \"#undef HWLOC_VERSION_GREEK\": \"#define HWLOC_VERSION_GREEK \\\"\\\"\",\n    \"#undef HWLOC_VERSION_MAJOR\": \"#define HWLOC_VERSION_MAJOR 2\",\n    \"#undef HWLOC_VERSION_MINOR\": \"#define HWLOC_VERSION_MINOR 0\",\n    \"#undef HWLOC_VERSION_RELEASE\": \"#define HWLOC_VERSION_RELEASE 3\",\n    \"#undef HWLOC_VERSION\": \"#define HWLOC_VERSION \\\"2.0.3\\\"\",\n    \"#undef HWLOC_X86_64_ARCH\": \"#define HWLOC_X86_64_ARCH 1\",\n    \"#undef LT_OBJDIR\": \"#define LT_OBJDIR \\\".libs/\\\"\",\n    \"#undef PACKAGE_BUGREPORT\": \"#define PACKAGE_BUGREPORT \\\"http://github.com/open-mpi/hwloc/issues\",\n    \"#undef PACKAGE_NAME\": \"#define PACKAGE_NAME \\\"hwloc\\\"\",\n    \"#undef PACKAGE_STRING\": \"#define PACKAGE_STRING \\\"hwloc 2.0.3\\\"\",\n    \"#undef PACKAGE_TARNAME\": \"#define PACKAGE_TARNAME \\\"hwloc\\\"\",\n    \"#undef PACKAGE_URL\": \"#define PACKAGE_URL \\\"\\\"\",\n    \"#undef PACKAGE_VERSION\": \"#define PACKAGE_VERSION \\\"2.0.3\\\"\",\n    \"#undef PACKAGE\": \"#define PACKAGE \\\"hwloc\\\"\",\n    \"#undef SIZEOF_UNSIGNED_INT\": \"#define SIZEOF_UNSIGNED_INT 4\",\n    \"#undef SIZEOF_UNSIGNED_LONG\": \"#define SIZEOF_UNSIGNED_LONG 8\",\n    \"#undef SIZEOF_VOID_P\": \"#define SIZEOF_VOID_P 8\",\n    \"#undef STDC_HEADERS\": \"#define STDC_HEADERS 1\",\n    \"# undef _HPUX_SOURCE\": \"# define _HPUX_SOURCE 1\",\n    \"# undef _ALL_SOURCE\": \"# define _ALL_SOURCE 1\",\n    \"# undef _GNU_SOURCE\": \"# define _GNU_SOURCE 1\",\n    \"# undef _POSIX_PTHREAD_SEMANTICS\": \"# define _POSIX_PTHREAD_SEMANTICS 1\",\n    \"# undef _TANDEM_SOURCE\": \"# define _TANDEM_SOURCE 1\",\n    \"# undef __EXTENSIONS__\": \"# define __EXTENSIONS__ 1\",\n    \"#undef VERSION\": \"#define VERSION \\\"2.0.3\\\"\",\n    \"#undef _HPUX_SOURCE\": \"#define _HPUX_SOURCE 1\",\n    \"#undef hwloc_pid_t\": \"#define hwloc_pid_t pid_t\",\n    \"#undef hwloc_thread_t\": \"#define hwloc_thread_t pthread_t\",\n}\n\n_INCLUDE_PRIVATE_HWLOC_AUTOIGEN_CONFIG_H_CUDA_SUBS = {\n    \"#undef HAVE_CUDA_RUNTIME_API_H\": \"#define HAVE_CUDA_RUNTIME_API_H 1\",\n    \"#undef HAVE_CUDA_H\": \"#define HAVE_CUDA_H 1\",\n    \"#undef HAVE_CUDA\": \"#define HAVE_CUDA 1\",\n}\n\n_INCLUDE_PRIVATE_HWLOC_AUTOIGEN_CONFIG_H_LINUX_SUBS = {\n    \"#undef HAVE_PROGRAM_INVOCATION_NAME\": \"#define HAVE_PROGRAM_INVOCATION_NAME 1\",\n    \"#undef HWLOC_LINUX_SYS\": \"#define HWLOC_LINUX_SYS 1\",\n}\n\n_INCLUDE_PRIVATE_HWLOC_AUTOIGEN_CONFIG_H_LINUX_SUBS.update(_INCLUDE_PRIVATE_HWLOC_AUTOIGEN_CONFIG_H_COMMON_SUBS)\n\n_INCLUDE_PRIVATE_HWLOC_AUTOIGEN_CONFIG_H_CUDA_SUBS.update(_INCLUDE_PRIVATE_HWLOC_AUTOIGEN_CONFIG_H_LINUX_SUBS)\n\nexpand_template(\n    name = \"include_private_hwloc_autogen__config_h\",\n    out = \"include/private/autogen/config.h\",\n    substitutions = if_cuda(\n        _INCLUDE_PRIVATE_HWLOC_AUTOIGEN_CONFIG_H_CUDA_SUBS,\n        if_false = _INCLUDE_PRIVATE_HWLOC_AUTOIGEN_CONFIG_H_LINUX_SUBS,\n    ),\n    template = \"include/private/autogen/config.h.in\",\n)\n\nexpand_template(\n    name = \"move_static_components_h\",\n    out = \"hwloc/static-components.h\",\n    substitutions = {\"&hwloc_linuxio_component\": \"//&hwloc_linuxio_component\"},\n    template = \"@kroma_network_tachyon//third_party/hwloc:static-components.h\",\n)\n\ncc_library(\n    name = \"hwloc\",\n    srcs = [\n        \"hwloc/base64.c\",\n        \"hwloc/bind.c\",\n        \"hwloc/bitmap.c\",\n        \"hwloc/components.c\",\n        \"hwloc/cpukinds.c\",\n        \"hwloc/diff.c\",\n        \"hwloc/distances.c\",\n        \"hwloc/memattrs.c\",\n        \"hwloc/misc.c\",\n        \"hwloc/pci-common.c\",\n        \"hwloc/shmem.c\",\n        \"hwloc/static-components.h\",\n        \"hwloc/topology.c\",\n        \"hwloc/topology-hardwired.c\",\n        \"hwloc/topology-noos.c\",\n        \"hwloc/topology-synthetic.c\",\n        \"hwloc/topology-xml.c\",\n        \"hwloc/topology-xml-nolibxml.c\",\n        \"hwloc/traversal.c\",\n        \"include/hwloc/plugins.h\",\n        \"include/hwloc/shmem.h\",\n        \"include/private/autogen/config.h\",\n        \"include/private/components.h\",\n        \"include/private/debug.h\",\n        \"include/private/internal-components.h\",\n        \"include/private/misc.h\",\n        \"include/private/private.h\",\n        \"include/private/xml.h\",\n    ] + select({\n        \"@kroma_network_tachyon//:linux_x86_64\": [\n            \"hwloc/topology-linux.c\",\n            \"hwloc/topology-x86.c\",\n            \"include/hwloc/linux.h\",\n            \"include/private/cpuid-x86.h\",\n        ],\n        \"@kroma_network_tachyon//:linux_aarch64\": [\n            \"hwloc/topology-linux.c\",\n            \"include/hwloc/linux.h\",\n        ],\n        \"@kroma_network_tachyon//:linux_ppc\": [\n            \"hwloc/topology-linux.c\",\n            \"include/hwloc/linux.h\",\n        ],\n        \"@platforms//os:freebsd\": [\n            \"hwloc/topology-freebsd.c\",\n            \"hwloc/topology-x86.c\",\n            \"include/private/cpuid-x86.h\",\n        ],\n        \"//conditions:default\": [],\n    }),\n    hdrs = [\n        \"include/hwloc.h\",\n        \"include/hwloc/autogen/config.h\",\n        \"include/hwloc/bitmap.h\",\n        \"include/hwloc/cpukinds.h\",\n        \"include/hwloc/deprecated.h\",\n        \"include/hwloc/diff.h\",\n        \"include/hwloc/distances.h\",\n        \"include/hwloc/export.h\",\n        \"include/hwloc/helper.h\",\n        \"include/hwloc/inlines.h\",\n        \"include/hwloc/memattrs.h\",\n        \"include/hwloc/rename.h\",\n    ],\n    copts = COMMON_INCLUDE_COPTS + DISABLE_WARNINGS_COPTS + VAR_SETTINGS_COPTS,\n    defines = [\"TACHYON_USE_NUMA\"],\n    features = [\n        \"-parse_headers\",\n        \"-layering_check\",\n    ],\n    includes = [\n        \"hwloc\",\n        \"include\",\n    ],\n    deps = [],\n)\n\ncc_binary(\n    name = \"hwloc_print\",\n    srcs = [\"hwloc_print.cc\"],\n    copts = COMMON_INCLUDE_COPTS,\n    deps = [\n        \":hwloc\",\n    ],\n)\n"
  },
  {
    "path": "third_party/hwloc/static-components.h",
    "content": "/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n#ifndef THIRD_PARTY_HWLOC_STATIC_COMPONENTS_H_\n#define THIRD_PARTY_HWLOC_STATIC_COMPONENTS_H_\n\n#include <private/internal-components.h>\n\nstatic const struct hwloc_component* hwloc_static_components[] = {\n    &hwloc_noos_component,\n    &hwloc_xml_component,\n    &hwloc_synthetic_component,\n    &hwloc_xml_nolibxml_component,\n#ifdef __linux__\n    &hwloc_linux_component,\n    &hwloc_linuxio_component,\n#endif\n#ifdef __FreeBSD__\n    &hwloc_freebsd_component,\n#endif\n#if defined(__x86_64__) || defined(__amd64__) || defined(_M_IX86) || \\\n    defined(_M_X64)\n    &hwloc_x86_component,\n#endif\n    NULL};\n\n#endif  // THIRD_PARTY_HWLOC_STATIC_COMPONENTS_H_\n"
  },
  {
    "path": "third_party/hwloc/workspace.bzl",
    "content": "\"\"\"loads the hwloc library, used by Tachyon.\"\"\"\n\nload(\"//third_party:repo.bzl\", \"tachyon_http_archive\", \"tf_mirror_urls\")\n\ndef repo():\n    tachyon_http_archive(\n        name = \"hwloc\",\n        urls = tf_mirror_urls(\"https://download.open-mpi.org/release/hwloc/v2.7/hwloc-2.7.1.tar.gz\"),\n        sha256 = \"4cb0a781ed980b03ad8c48beb57407aa67c4b908e45722954b9730379bc7f6d5\",\n        strip_prefix = \"hwloc-2.7.1\",\n        build_file = \"//third_party/hwloc:hwloc.BUILD\",\n        system_build_file = \"//third_party/hwloc:BUILD.system\",\n    )\n"
  },
  {
    "path": "third_party/icicle/BUILD.bazel",
    "content": ""
  },
  {
    "path": "third_party/icicle/build_defs.bzl",
    "content": "BN254 = \"bn254\"\nBLS12_381 = \"bls12_381\"\nBLS12_377 = \"bls12_377\"\nBW6_761 = \"bw6_761\"\nGRUMPKIN = \"grumpkin\"\nBABY_BEAR = \"baby_bear\"\nSTARK_252 = \"stark_252\"\nM31 = \"m31\"\n\nCURVES = [\n    BN254,\n    BLS12_381,\n    BLS12_377,\n    BW6_761,\n    GRUMPKIN,\n]\n\nFIELDS = [\n    BN254,\n    BLS12_381,\n    BLS12_377,\n    BW6_761,\n    GRUMPKIN,\n    BABY_BEAR,\n    STARK_252,\n    M31,\n]\n\nFIELDS_WITH_MERKLE_TREE = FIELDS\n\nFIELDS_WITH_NTT = [\n    BN254,\n    BLS12_381,\n    BLS12_377,\n    BW6_761,\n    BABY_BEAR,\n    STARK_252,\n]\n\nFIELDS_WITH_POSEIDON = CURVES\n\nFIELDS_WITH_POSEIDON2 = [\n    BN254,\n    BABY_BEAR,\n]\n\ndef icicle_defines(field):\n    if field == BN254:\n        return [\n            \"FIELD_ID=BN254\",\n            \"CURVE_ID=BN254\",\n            \"CURVE=bn254\",\n        ]\n    elif field == BLS12_381:\n        return [\n            \"FIELD_ID=BLS12_381\",\n            \"CURVE_ID=BLS12_381\",\n            \"CURVE=bls12_381\",\n        ]\n    elif field == BLS12_377:\n        return [\n            \"FIELD_ID=BLS12_377\",\n            \"CURVE_ID=BLS12_377\",\n            \"CURVE=bls12_377\",\n        ]\n    elif field == BW6_761:\n        return [\n            \"FIELD_ID=BW6_761\",\n            \"CURVE_ID=BW6_761\",\n            \"CURVE=bw6_761\",\n        ]\n    elif field == GRUMPKIN:\n        return [\n            \"FIELD_ID=GRUMPKIN\",\n            \"CURVE_ID=GRUMPKIN\",\n            \"CURVE=grumpkin\",\n        ]\n    elif field == BABY_BEAR:\n        return [\"FIELD_ID=BABY_BEAR\"]\n    elif field == STARK_252:\n        return [\"FIELD_ID=STARK_252\"]\n    return []\n"
  },
  {
    "path": "third_party/icicle/change_sort_function.patch",
    "content": "diff --git a/icicle/src/merkle-tree/mmcs.cu.cc b/icicle/src/merkle-tree/mmcs.cu.cc\nindex 1d695483..e0a0cf76 100644\n--- a/icicle/src/merkle-tree/mmcs.cu.cc\n+++ b/icicle/src/merkle-tree/mmcs.cu.cc\n@@ -203,10 +203,10 @@ namespace merkle_tree {\n         IcicleError_t::InvalidArgument,\n         \"Hash max preimage length does not match merkle tree arity multiplied by digest elements\");\n\n-    std::vector<Matrix<L>> sorted_inputs(number_of_inputs);\n-    std::partial_sort_copy(\n-      inputs, inputs + number_of_inputs, sorted_inputs.begin(), sorted_inputs.end(),\n-      [](const Matrix<L>& left, const Matrix<L>& right) { return left.height > right.height; });\n+    std::vector<Matrix<L>> sorted_inputs(inputs, inputs + number_of_inputs);\n+    std::stable_sort(sorted_inputs.begin(), sorted_inputs.end(), [](const Matrix<L>& left, const Matrix<L>& right) {\n+      return left.height > right.height;\n+    });\n\n     // Check that the height of any two given matrices either rounds up\n     // to the same next power of two or otherwise equal\n"
  },
  {
    "path": "third_party/icicle/fix_first_digest_copy_bug.patch",
    "content": "diff --git a/icicle/src/merkle-tree/mmcs.cu.cc b/icicle/src/merkle-tree/mmcs.cu.cc\nindex 1d695483..1c123caa 100644\n--- a/icicle/src/merkle-tree/mmcs.cu.cc\n+++ b/icicle/src/merkle-tree/mmcs.cu.cc\n@@ -165,7 +165,7 @@ namespace merkle_tree {\n       d_leaves_info, params.number_of_leaves_to_inject, params.number_of_rows, states, params.digest_elements,\n       *params.hasher, *params.ctx));\n \n-    CHK_IF_RETURN(maybe_copy_digests(digests, big_tree_digests, params));\n+    CHK_IF_RETURN(maybe_copy_digests(states, big_tree_digests, params));\n \n     params.number_of_rows_padded /= params.arity;\n     params.segment_size /= params.arity;\n"
  },
  {
    "path": "third_party/icicle/icicle.BUILD",
    "content": "load(\n    \"@icicle//:build_defs.bzl\",\n    \"CURVES\",\n    \"FIELDS\",\n    \"FIELDS_WITH_MERKLE_TREE\",\n    \"FIELDS_WITH_NTT\",\n    \"FIELDS_WITH_POSEIDON\",\n    \"FIELDS_WITH_POSEIDON2\",\n    \"icicle_defines\",\n)\nload(\"@kroma_network_tachyon//bazel:tachyon.bzl\", \"if_gpu_is_configured\")\nload(\"@kroma_network_tachyon//bazel:tachyon_cc.bzl\", \"tachyon_cuda_library\")\nload(\"@local_config_cuda//cuda:build_defs.bzl\", \"if_cuda\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cuda_library(\n    name = \"hdrs\",\n    hdrs = glob([\"icicle/include/**/*.h\"]),\n    include_prefix = \"third_party/icicle/include\",\n    includes = [\"icicle/include\"],\n    strip_include_prefix = \"icicle/include\",\n    deps = if_cuda([\n        \"@local_config_cuda//cuda:cuda_headers\",\n    ]),\n)\n\ntachyon_cuda_library(\n    name = \"hash\",\n    srcs = if_gpu_is_configured([\n        \"icicle/src/hash/keccak/keccak.cu.cc\",\n    ]),\n    include_prefix = \"third_party/icicle/src\",\n    includes = [\"icicle/src/hash\"],\n    strip_include_prefix = \"icicle/src\",\n    deps = [\":hdrs\"],\n)\n\n[tachyon_cuda_library(\n    name = \"merkle_tree_{}\".format(field),\n    hdrs = [\n        \"icicle/src/merkle-tree/merkle.cu.cc\",\n        \"icicle/src/merkle-tree/mmcs.cu.cc\",\n    ],\n    include_prefix = \"third_party/icicle/src\",\n    includes = [\"icicle/src/merkle-tree\"],\n    local_defines = icicle_defines(field),\n    strip_include_prefix = \"icicle/src\",\n    deps = [\":hdrs\"],\n) for field in FIELDS_WITH_MERKLE_TREE]\n\n[tachyon_cuda_library(\n    name = \"msm_{}\".format(field),\n    hdrs = [\"icicle/src/msm/msm.cu.cc\"],\n    include_prefix = \"third_party/icicle/src\",\n    includes = [\"icicle/src/msm\"],\n    local_defines = icicle_defines(field),\n    strip_include_prefix = \"icicle/src\",\n    deps = [\":hdrs\"],\n) for field in CURVES]\n\n[tachyon_cuda_library(\n    name = \"ntt_{}\".format(field),\n    srcs = if_gpu_is_configured([\n        \"icicle/src/ntt/kernel_ntt.cu.cc\",\n    ]),\n    hdrs = [\n        \"icicle/src/ntt/ntt.cu.cc\",\n        \"icicle/src/ntt/thread_ntt.cu.cc\",\n    ],\n    include_prefix = \"third_party/icicle/src\",\n    includes = [\"icicle/src/ntt\"],\n    local_defines = icicle_defines(field),\n    strip_include_prefix = \"icicle/src\",\n    deps = [\":hdrs\"],\n) for field in FIELDS_WITH_NTT]\n\n[tachyon_cuda_library(\n    name = \"polynomials_{}\".format(field),\n    srcs = if_gpu_is_configured([\n        \"icicle/src/polynomials/polynomials.cu.cc\",\n        \"icicle/src/polynomials/polynomials_c_api.cu.cc\",\n        \"icicle/src/polynomials/cuda_backend/polynomial_cuda_backend.cu.cc\",\n    ]),\n    hdrs = [\"icicle/src/polynomials/cuda_backend/kernels.cu.h\"],\n    include_prefix = \"third_party/icicle/src\",\n    includes = [\"icicle/src/polynomials\"],\n    local_defines = icicle_defines(field),\n    strip_include_prefix = \"icicle/src\",\n    deps = [\n        \":hdrs\",\n        \":vec_ops\",\n    ],\n) for field in FIELDS]\n\n[tachyon_cuda_library(\n    name = \"poseidon_{}\".format(field),\n    hdrs = [\n        \"icicle/src/poseidon/constants.cu.cc\",\n    ],\n    include_prefix = \"third_party/icicle/src\",\n    includes = [\"icicle/src/poseidon\"],\n    local_defines = icicle_defines(field),\n    strip_include_prefix = \"icicle/src\",\n    deps = [\":hdrs\"],\n) for field in FIELDS_WITH_POSEIDON]\n\n[tachyon_cuda_library(\n    name = \"poseidon2_{}\".format(field),\n    hdrs = [\n        \"icicle/src/poseidon2/constants.cu.cc\",\n    ],\n    include_prefix = \"third_party/icicle/src\",\n    includes = [\"icicle/src/poseidon2\"],\n    local_defines = icicle_defines(field),\n    strip_include_prefix = \"icicle/src\",\n    deps = [\":hdrs\"],\n) for field in FIELDS_WITH_POSEIDON2]\n\ntachyon_cuda_library(\n    name = \"vec_ops\",\n    hdrs = [\"icicle/src/vec_ops/vec_ops.cu.cc\"],\n    include_prefix = \"third_party/icicle/src\",\n    includes = [\"icicle/src/vec_ops\"],\n    strip_include_prefix = \"icicle/src\",\n    deps = [\":hdrs\"],\n)\n"
  },
  {
    "path": "third_party/icicle/inlinize.patch",
    "content": "diff --git a/icicle/include/msm/msm.cu.h b/icicle/include/msm/msm.cu.h\nindex 1bd27a48..29b7e9b8 100644\n--- a/icicle/include/msm/msm.cu.h\n+++ b/icicle/include/msm/msm.cu.h\n@@ -85,7 +85,7 @@ namespace msm {\n    * A function that returns the default value of [MSMConfig](@ref MSMConfig) for the [MSM](@ref MSM) function.\n    * @return Default value of [MSMConfig](@ref MSMConfig).\n    */\n-  static MSMConfig\n+  inline MSMConfig\n   default_msm_config(const device_context::DeviceContext& ctx = device_context::get_default_device_context())\n   {\n     MSMConfig config = {\n"
  },
  {
    "path": "third_party/icicle/pragma.patch",
    "content": "diff --git a/icicle/include/gpu-utils/modifiers.cu.h b/icicle/include/gpu-utils/modifiers.cu.h\nindex 04ad395..3be1f5d 100644\n--- a/icicle/include/gpu-utils/modifiers.cu.h\n+++ b/icicle/include/gpu-utils/modifiers.cu.h\n@@ -3,7 +3,7 @@\n #define UNROLL\n #else\n #define INLINE_MACRO __forceinline__\n-#define UNROLL       #pragma unroll\n+#define UNROLL       _Pragma(\"unroll\")\n #endif\n \n #define HOST_INLINE        __host__ INLINE_MACRO\n"
  },
  {
    "path": "third_party/icicle/remove_kernels_from_header.patch",
    "content": "diff --git a/icicle/include/ntt/ntt.cu.h b/icicle/include/ntt/ntt.cu.h\nindex 65482d85..ca014899 100644\n--- a/icicle/include/ntt/ntt.cu.h\n+++ b/icicle/include/ntt/ntt.cu.h\n@@ -7,7 +7,6 @@\n #include \"gpu-utils/device_context.cu.h\"\n #include \"gpu-utils/error_handler.cu.h\"\n #include \"gpu-utils/sharedmem.cu.h\"\n-#include \"utils/utils_kernels.cu.h\"\n #include \"utils/utils.h\"\n \n /**\n"
  },
  {
    "path": "third_party/icicle/rename.patch",
    "content": "diff --git a/docs/docs/icicle/polynomials/overview.md b/docs/docs/icicle/polynomials/overview.md\nindex 25ba9dee..c77bce76 100644\n--- a/docs/docs/icicle/polynomials/overview.md\n+++ b/docs/docs/icicle/polynomials/overview.md\n@@ -61,7 +61,7 @@ Initialization with an appropriate factory is required to configure the computat\n \n ```cpp\n #include \"polynomials/polynomials.h\"\n-#include \"polynomials/cuda_backend/polynomial_cuda_backend.cuh\"\n+#include \"polynomials/cuda_backend/polynomial_cuda_backend.cu.h\"\n \n // Initialize with a CUDA backend\n Polynomial::initialize(std::make_shared<CUDAPolynomialFactory>());\ndiff --git a/examples/c++/best-practice-ntt/example.cu b/examples/c++/best-practice-ntt/example.cu.cc\nsimilarity index 99%\nrename from examples/c++/best-practice-ntt/example.cu\nrename to examples/c++/best-practice-ntt/example.cu.cc\nindex 341f42a9..cf5ed0ac 100644\n--- a/examples/c++/best-practice-ntt/example.cu\n+++ b/examples/c++/best-practice-ntt/example.cu.cc\n@@ -3,7 +3,7 @@\n #include <string>\n #include <chrono>\n \n-#include \"curves/params/bn254.cuh\"\n+#include \"curves/params/bn254.cu.h\"\n #include \"api/bn254.h\"\n using namespace bn254;\n using namespace ntt;\ndiff --git a/examples/c++/msm/README.md b/examples/c++/msm/README.md\nindex dbb62be6..f61fd819 100644\n--- a/examples/c++/msm/README.md\n+++ b/examples/c++/msm/README.md\n@@ -17,7 +17,7 @@ We recommend to run our examples in [ZK-containers](../../ZK-containers.md) to s\n \n ```c++\n #define CURVE_ID 1\n-#include \"icicle/appUtils/msm/msm.cu\"\n+#include \"icicle/appUtils/msm/msm.cu.cc\"\n ...\n msm::MSMConfig config = {...};\n ...\ndiff --git a/examples/c++/msm/example.cu b/examples/c++/msm/example.cu.cc\nsimilarity index 100%\nrename from examples/c++/msm/example.cu\nrename to examples/c++/msm/example.cu.cc\ndiff --git a/examples/c++/multi-gpu-poseidon/example.cu b/examples/c++/multi-gpu-poseidon/example.cu.cc\nsimilarity index 98%\nrename from examples/c++/multi-gpu-poseidon/example.cu\nrename to examples/c++/multi-gpu-poseidon/example.cu.cc\nindex fcc3d527..5706f195 100644\n--- a/examples/c++/multi-gpu-poseidon/example.cu\n+++ b/examples/c++/multi-gpu-poseidon/example.cu.cc\n@@ -4,10 +4,10 @@\n #include <nvml.h>\n \n #include \"api/bn254.h\"\n-#include \"gpu-utils/error_handler.cuh\"\n+#include \"gpu-utils/error_handler.cu.h\"\n \n-#include \"poseidon/poseidon.cuh\"\n-#include \"hash/hash.cuh\"\n+#include \"poseidon/poseidon.cu.h\"\n+#include \"hash/hash.cu.h\"\n \n using namespace poseidon;\n using namespace bn254;\ndiff --git a/examples/c++/multiply/README.md b/examples/c++/multiply/README.md\nindex 0e253d3a..e9ec9206 100644\n--- a/examples/c++/multiply/README.md\n+++ b/examples/c++/multiply/README.md\n@@ -14,7 +14,7 @@ Define a `CURVE_ID` and include curve configuration header:\n \n ```c++\n #define CURVE_ID 1\n-#include \"curves/curve_config.cuh\"\n+#include \"curves/curve_config.cu.h\"\n ```\n \n The values of `CURVE_ID` for different curves are in the above header. Multiplication is accelerated both for field scalars and point fields.\ndiff --git a/examples/c++/multiply/example.cu b/examples/c++/multiply/example.cu.cc\nsimilarity index 99%\nrename from examples/c++/multiply/example.cu\nrename to examples/c++/multiply/example.cu.cc\nindex 77eb7a0b..1aec4b49 100644\n--- a/examples/c++/multiply/example.cu\n+++ b/examples/c++/multiply/example.cu.cc\n@@ -4,7 +4,7 @@\n #include <nvml.h>\n \n #include \"api/bn254.h\"\n-#include \"vec_ops/vec_ops.cuh\"\n+#include \"vec_ops/vec_ops.cu.h\"\n \n using namespace vec_ops;\n using namespace bn254;\ndiff --git a/examples/c++/ntt/README.md b/examples/c++/ntt/README.md\nindex 28e8dd45..bd16879e 100644\n--- a/examples/c++/ntt/README.md\n+++ b/examples/c++/ntt/README.md\n@@ -14,7 +14,7 @@ We recommend to run our examples in [ZK-containers](../../ZK-containers.md) to s\n // Select the curve\n #define CURVE_ID 1\n // Include NTT template\n-#include \"appUtils/ntt/ntt.cu\"\n+#include \"appUtils/ntt/ntt.cu.cc\"\n using namespace curve_config;\n using namespace ntt;\n // Configure NTT\ndiff --git a/examples/c++/ntt/example.cu b/examples/c++/ntt/example.cu.cc\nsimilarity index 99%\nrename from examples/c++/ntt/example.cu\nrename to examples/c++/ntt/example.cu.cc\nindex 5e50f0dd..1bea46cf 100644\n--- a/examples/c++/ntt/example.cu\n+++ b/examples/c++/ntt/example.cu.cc\n@@ -3,7 +3,7 @@\n \n // include NTT template\n \n-#include \"curves/params/bn254.cuh\"\n+#include \"curves/params/bn254.cu.h\"\n #include \"api/bn254.h\"\n using namespace bn254;\n using namespace ntt;\ndiff --git a/examples/c++/pedersen-commitment/example.cu b/examples/c++/pedersen-commitment/example.cu.cc\nsimilarity index 99%\nrename from examples/c++/pedersen-commitment/example.cu\nrename to examples/c++/pedersen-commitment/example.cu.cc\nindex 106a47df..fa656baf 100644\n--- a/examples/c++/pedersen-commitment/example.cu\n+++ b/examples/c++/pedersen-commitment/example.cu.cc\n@@ -5,7 +5,7 @@\n #include <nvml.h>\n \n #include \"api/bn254.h\"\n-#include \"msm/msm.cuh\"\n+#include \"msm/msm.cu.h\"\n using namespace bn254;\n \n typedef point_field_t T;\ndiff --git a/examples/c++/polynomial-api/example.cu b/examples/c++/polynomial-api/example.cu.cc\nsimilarity index 99%\nrename from examples/c++/polynomial-api/example.cu\nrename to examples/c++/polynomial-api/example.cu.cc\nindex 76229834..fb04a55d 100644\n--- a/examples/c++/polynomial-api/example.cu\n+++ b/examples/c++/polynomial-api/example.cu.cc\n@@ -1,8 +1,8 @@\n #include <iostream>\n #include <cassert>\n #include \"polynomials/polynomials.h\"\n-#include \"polynomials/cuda_backend/polynomial_cuda_backend.cuh\"\n-#include \"ntt/ntt.cuh\"\n+#include \"polynomials/cuda_backend/polynomial_cuda_backend.cu.h\"\n+#include \"ntt/ntt.cu.h\"\n \n #include \"api/bn254.h\"\n #include <chrono>\ndiff --git a/examples/c++/polynomial_multiplication/example.cu b/examples/c++/polynomial_multiplication/example.cu.cc\nsimilarity index 99%\nrename from examples/c++/polynomial_multiplication/example.cu\nrename to examples/c++/polynomial_multiplication/example.cu.cc\nindex 9c80864e..a714735c 100644\n--- a/examples/c++/polynomial_multiplication/example.cu\n+++ b/examples/c++/polynomial_multiplication/example.cu.cc\n@@ -4,7 +4,7 @@\n #include <memory>\n \n #include \"api/bn254.h\"\n-#include \"gpu-utils/error_handler.cuh\"\n+#include \"gpu-utils/error_handler.cu.h\"\n \n using namespace bn254;\n typedef scalar_t test_scalar;\ndiff --git a/examples/c++/poseidon/README.md b/examples/c++/poseidon/README.md\nindex 7c936694..e4bf8b0b 100644\n--- a/examples/c++/poseidon/README.md\n+++ b/examples/c++/poseidon/README.md\n@@ -11,7 +11,7 @@ We recommend to run our examples in [ZK-containers](../../ZK-containers.md) to s\n ## Concise Usage Explanation\n \n ```c++\n-#include \"appUtils/poseidon/poseidon.cu\"\n+#include \"appUtils/poseidon/poseidon.cu.cc\"\n ...\n poseidon_hash<scalar_t, arity+1>(input, output, n, constants, config);\n ```\ndiff --git a/examples/c++/poseidon/example.cu b/examples/c++/poseidon/example.cu.cc\nsimilarity index 98%\nrename from examples/c++/poseidon/example.cu\nrename to examples/c++/poseidon/example.cu.cc\nindex edc408a0..de6afc6e 100644\n--- a/examples/c++/poseidon/example.cu\n+++ b/examples/c++/poseidon/example.cu.cc\n@@ -3,9 +3,9 @@\n #include <iostream>\n \n #include \"api/bn254.h\"\n-#include \"curves/params/bn254.cuh\"\n-#include \"poseidon/poseidon.cuh\"\n-#include \"hash/hash.cuh\"\n+#include \"curves/params/bn254.cu.h\"\n+#include \"poseidon/poseidon.cu.h\"\n+#include \"hash/hash.cu.h\"\n using namespace poseidon;\n using namespace bn254;\n \ndiff --git a/examples/c++/risc0/example.cu b/examples/c++/risc0/example.cu.cc\nsimilarity index 99%\nrename from examples/c++/risc0/example.cu\nrename to examples/c++/risc0/example.cu.cc\nindex e07128e8..b7c18a32 100644\n--- a/examples/c++/risc0/example.cu\n+++ b/examples/c++/risc0/example.cu.cc\n@@ -5,8 +5,8 @@\n #include <list>\n \n #include \"polynomials/polynomials.h\"\n-#include \"polynomials/cuda_backend/polynomial_cuda_backend.cuh\"\n-#include \"ntt/ntt.cuh\"\n+#include \"polynomials/cuda_backend/polynomial_cuda_backend.cu.h\"\n+#include \"ntt/ntt.cu.h\"\n \n using namespace polynomials;\n \ndiff --git a/icicle/benchmarks/benches.cu b/icicle/benchmarks/benches.cu\ndeleted file mode 100644\nindex f7ff8635..00000000\n--- a/icicle/benchmarks/benches.cu\n+++ /dev/null\n@@ -1,6 +0,0 @@\n-#include \"field_benchmarks.cu\"\n-#ifdef CURVE_ID\n-#include \"curve_benchmarks.cu\"\n-#endif\n-\n-BENCHMARK_MAIN();\n\\ No newline at end of file\ndiff --git a/icicle/benchmarks/benches.cu.cc b/icicle/benchmarks/benches.cu.cc\nnew file mode 100644\nindex 00000000..450f9088\n--- /dev/null\n+++ b/icicle/benchmarks/benches.cu.cc\n@@ -0,0 +1,6 @@\n+#include \"field_benchmarks.cu.cc\"\n+#ifdef CURVE_ID\n+#include \"curve_benchmarks.cu.cc\"\n+#endif\n+\n+BENCHMARK_MAIN();\n\\ No newline at end of file\ndiff --git a/icicle/benchmarks/curve_benchmarks.cu b/icicle/benchmarks/curve_benchmarks.cu.cc\nsimilarity index 97%\nrename from icicle/benchmarks/curve_benchmarks.cu\nrename to icicle/benchmarks/curve_benchmarks.cu.cc\nindex 379793ba..707b2c88 100644\n--- a/icicle/benchmarks/curve_benchmarks.cu\n+++ b/icicle/benchmarks/curve_benchmarks.cu.cc\n@@ -1,6 +1,6 @@\n #include <benchmark/benchmark.h>\n-#include \"utils/test_functions.cuh\"\n-#include \"curves/curve_config.cuh\"\n+#include \"utils/test_functions.cu.h\"\n+#include \"curves/curve_config.cu.h\"\n \n using namespace curve_config;\n using namespace benchmark;\ndiff --git a/icicle/benchmarks/field_benchmarks.cu b/icicle/benchmarks/field_benchmarks.cu.cc\nsimilarity index 97%\nrename from icicle/benchmarks/field_benchmarks.cu\nrename to icicle/benchmarks/field_benchmarks.cu.cc\nindex cf5f2a24..b2d56744 100644\n--- a/icicle/benchmarks/field_benchmarks.cu\n+++ b/icicle/benchmarks/field_benchmarks.cu.cc\n@@ -1,6 +1,6 @@\n #include <benchmark/benchmark.h>\n-#include \"utils/test_functions.cuh\"\n-#include \"fields/field_config.cuh\"\n+#include \"utils/test_functions.cu.h\"\n+#include \"fields/field_config.cu.h\"\n \n using namespace field_config;\n using namespace benchmark;\ndiff --git a/icicle/include/api/babybear.h b/icicle/include/api/babybear.h\nindex 64815ceb..e56ccd6e 100644\n--- a/icicle/include/api/babybear.h\n+++ b/icicle/include/api/babybear.h\n@@ -8,13 +8,13 @@\n #define BABYBEAR_API_H\n \n #include <cuda_runtime.h>\n-#include \"gpu-utils/device_context.cuh\"\n-#include \"merkle-tree/merkle.cuh\"\n-#include \"matrix/matrix.cuh\"\n-#include \"fields/stark_fields/babybear.cuh\"\n-#include \"ntt/ntt.cuh\"\n-#include \"vec_ops/vec_ops.cuh\"\n-#include \"poseidon2/poseidon2.cuh\"\n+#include \"gpu-utils/device_context.cu.h\"\n+#include \"merkle-tree/merkle.cu.h\"\n+#include \"matrix/matrix.cu.h\"\n+#include \"fields/stark_fields/babybear.cu.h\"\n+#include \"ntt/ntt.cu.h\"\n+#include \"vec_ops/vec_ops.cu.h\"\n+#include \"poseidon2/poseidon2.cu.h\"\n \n extern \"C\" cudaError_t babybear_extension_ntt_cuda(\n   const babybear::extension_t* input, int size, ntt::NTTDir dir, ntt::NTTConfig<babybear::scalar_t>& config, babybear::extension_t* output);\ndiff --git a/icicle/include/api/bls12_377.h b/icicle/include/api/bls12_377.h\nindex 75a113d2..b8cc01bc 100644\n--- a/icicle/include/api/bls12_377.h\n+++ b/icicle/include/api/bls12_377.h\n@@ -8,14 +8,14 @@\n #define BLS12_377_API_H\n \n #include <cuda_runtime.h>\n-#include \"gpu-utils/device_context.cuh\"\n-#include \"merkle-tree/merkle.cuh\"\n-#include \"matrix/matrix.cuh\"\n-#include \"curves/params/bls12_377.cuh\"\n-#include \"ntt/ntt.cuh\"\n-#include \"msm/msm.cuh\"\n-#include \"vec_ops/vec_ops.cuh\"\n-#include \"poseidon/poseidon.cuh\"\n+#include \"gpu-utils/device_context.cu.h\"\n+#include \"merkle-tree/merkle.cu.h\"\n+#include \"matrix/matrix.cu.h\"\n+#include \"curves/params/bls12_377.cu.h\"\n+#include \"ntt/ntt.cu.h\"\n+#include \"msm/msm.cu.h\"\n+#include \"vec_ops/vec_ops.cu.h\"\n+#include \"poseidon/poseidon.cu.h\"\n \n extern \"C\" cudaError_t bls12_377_g2_precompute_msm_bases_cuda(\n   bls12_377::g2_affine_t* bases,\ndiff --git a/icicle/include/api/bls12_381.h b/icicle/include/api/bls12_381.h\nindex 9f1a49cd..3814e01a 100644\n--- a/icicle/include/api/bls12_381.h\n+++ b/icicle/include/api/bls12_381.h\n@@ -8,14 +8,14 @@\n #define BLS12_381_API_H\n \n #include <cuda_runtime.h>\n-#include \"gpu-utils/device_context.cuh\"\n-#include \"merkle-tree/merkle.cuh\"\n-#include \"matrix/matrix.cuh\"\n-#include \"curves/params/bls12_381.cuh\"\n-#include \"ntt/ntt.cuh\"\n-#include \"msm/msm.cuh\"\n-#include \"vec_ops/vec_ops.cuh\"\n-#include \"poseidon/poseidon.cuh\"\n+#include \"gpu-utils/device_context.cu.h\"\n+#include \"merkle-tree/merkle.cu.h\"\n+#include \"matrix/matrix.cu.h\"\n+#include \"curves/params/bls12_381.cu.h\"\n+#include \"ntt/ntt.cu.h\"\n+#include \"msm/msm.cu.h\"\n+#include \"vec_ops/vec_ops.cu.h\"\n+#include \"poseidon/poseidon.cu.h\"\n \n extern \"C\" cudaError_t bls12_381_g2_precompute_msm_bases_cuda(\n   bls12_381::g2_affine_t* bases,\ndiff --git a/icicle/include/api/bn254.h b/icicle/include/api/bn254.h\nindex 1a9a46b5..16ce4cb4 100644\n--- a/icicle/include/api/bn254.h\n+++ b/icicle/include/api/bn254.h\n@@ -8,15 +8,15 @@\n #define BN254_API_H\n \n #include <cuda_runtime.h>\n-#include \"gpu-utils/device_context.cuh\"\n-#include \"merkle-tree/merkle.cuh\"\n-#include \"matrix/matrix.cuh\"\n-#include \"curves/params/bn254.cuh\"\n-#include \"ntt/ntt.cuh\"\n-#include \"msm/msm.cuh\"\n-#include \"vec_ops/vec_ops.cuh\"\n-#include \"poseidon/poseidon.cuh\"\n-#include \"poseidon2/poseidon2.cuh\"\n+#include \"gpu-utils/device_context.cu.h\"\n+#include \"merkle-tree/merkle.cu.h\"\n+#include \"matrix/matrix.cu.h\"\n+#include \"curves/params/bn254.cu.h\"\n+#include \"ntt/ntt.cu.h\"\n+#include \"msm/msm.cu.h\"\n+#include \"vec_ops/vec_ops.cu.h\"\n+#include \"poseidon/poseidon.cu.h\"\n+#include \"poseidon2/poseidon2.cu.h\"\n \n extern \"C\" cudaError_t bn254_g2_precompute_msm_bases_cuda(\n   bn254::g2_affine_t* bases,\ndiff --git a/icicle/include/api/bw6_761.h b/icicle/include/api/bw6_761.h\nindex 531e4514..e311c1cd 100644\n--- a/icicle/include/api/bw6_761.h\n+++ b/icicle/include/api/bw6_761.h\n@@ -8,14 +8,14 @@\n #define BW6_761_API_H\n \n #include <cuda_runtime.h>\n-#include \"gpu-utils/device_context.cuh\"\n-#include \"merkle-tree/merkle.cuh\"\n-#include \"matrix/matrix.cuh\"\n-#include \"curves/params/bw6_761.cuh\"\n-#include \"ntt/ntt.cuh\"\n-#include \"msm/msm.cuh\"\n-#include \"vec_ops/vec_ops.cuh\"\n-#include \"poseidon/poseidon.cuh\"\n+#include \"gpu-utils/device_context.cu.h\"\n+#include \"merkle-tree/merkle.cu.h\"\n+#include \"matrix/matrix.cu.h\"\n+#include \"curves/params/bw6_761.cu.h\"\n+#include \"ntt/ntt.cu.h\"\n+#include \"msm/msm.cu.h\"\n+#include \"vec_ops/vec_ops.cu.h\"\n+#include \"poseidon/poseidon.cu.h\"\n \n extern \"C\" cudaError_t bw6_761_g2_precompute_msm_bases_cuda(\n   bw6_761::g2_affine_t* bases,\ndiff --git a/icicle/include/api/grumpkin.h b/icicle/include/api/grumpkin.h\nindex 40241e59..c9b302f4 100644\n--- a/icicle/include/api/grumpkin.h\n+++ b/icicle/include/api/grumpkin.h\n@@ -8,13 +8,13 @@\n #define GRUMPKIN_API_H\n \n #include <cuda_runtime.h>\n-#include \"gpu-utils/device_context.cuh\"\n-#include \"merkle-tree/merkle.cuh\"\n-#include \"matrix/matrix.cuh\"\n-#include \"curves/params/grumpkin.cuh\"\n-#include \"msm/msm.cuh\"\n-#include \"vec_ops/vec_ops.cuh\"\n-#include \"poseidon/poseidon.cuh\"\n+#include \"gpu-utils/device_context.cu.h\"\n+#include \"merkle-tree/merkle.cu.h\"\n+#include \"matrix/matrix.cu.h\"\n+#include \"curves/params/grumpkin.cu.h\"\n+#include \"msm/msm.cu.h\"\n+#include \"vec_ops/vec_ops.cu.h\"\n+#include \"poseidon/poseidon.cu.h\"\n \n extern \"C\" cudaError_t grumpkin_precompute_msm_bases_cuda(\n   grumpkin::affine_t* bases,\ndiff --git a/icicle/include/api/hash.h b/icicle/include/api/hash.h\nindex ffa40d81..2c9436e2 100644\n--- a/icicle/include/api/hash.h\n+++ b/icicle/include/api/hash.h\n@@ -4,9 +4,9 @@\n #define HASH_API_H\n \n #include <cuda_runtime.h>\n-#include \"gpu-utils/device_context.cuh\"\n-#include \"hash/keccak/keccak.cuh\"\n-#include \"merkle-tree/merkle.cuh\"\n+#include \"gpu-utils/device_context.cu.h\"\n+#include \"hash/keccak/keccak.cu.h\"\n+#include \"merkle-tree/merkle.cu.h\"\n \n extern \"C\" cudaError_t\n   keccak256_cuda(uint8_t* input, int input_block_size, int number_of_blocks, uint8_t* output, keccak::HashConfig& config);\ndiff --git a/icicle/include/api/m31.h b/icicle/include/api/m31.h\nindex 277268a1..19879db0 100644\n--- a/icicle/include/api/m31.h\n+++ b/icicle/include/api/m31.h\n@@ -8,11 +8,11 @@\n #define M31_API_H\n \n #include <cuda_runtime.h>\n-#include \"gpu-utils/device_context.cuh\"\n-#include \"merkle-tree/merkle.cuh\"\n-#include \"matrix/matrix.cuh\"\n-#include \"fields/stark_fields/m31.cuh\"\n-#include \"vec_ops/vec_ops.cuh\"\n+#include \"gpu-utils/device_context.cu.h\"\n+#include \"merkle-tree/merkle.cu.h\"\n+#include \"matrix/matrix.cu.h\"\n+#include \"fields/stark_fields/m31.cu.h\"\n+#include \"vec_ops/vec_ops.cu.h\"\n \n extern \"C\" cudaError_t m31_build_merkle_tree(\n   const m31::scalar_t* leaves,\ndiff --git a/icicle/include/api/stark252.h b/icicle/include/api/stark252.h\nindex 867f98e6..24f8dbd5 100644\n--- a/icicle/include/api/stark252.h\n+++ b/icicle/include/api/stark252.h\n@@ -8,12 +8,12 @@\n #define STARK252_API_H\n \n #include <cuda_runtime.h>\n-#include \"gpu-utils/device_context.cuh\"\n-#include \"merkle-tree/merkle.cuh\"\n-#include \"matrix/matrix.cuh\"\n-#include \"fields/stark_fields/stark252.cuh\"\n-#include \"ntt/ntt.cuh\"\n-#include \"vec_ops/vec_ops.cuh\"\n+#include \"gpu-utils/device_context.cu.h\"\n+#include \"merkle-tree/merkle.cu.h\"\n+#include \"matrix/matrix.cu.h\"\n+#include \"fields/stark_fields/stark252.cu.h\"\n+#include \"ntt/ntt.cu.h\"\n+#include \"vec_ops/vec_ops.cu.h\"\n \n extern \"C\" cudaError_t stark252_build_merkle_tree(\n   const stark252::scalar_t* leaves,\ndiff --git a/icicle/include/curves/affine.cuh b/icicle/include/curves/affine.cu.h\nsimilarity index 93%\nrename from icicle/include/curves/affine.cuh\nrename to icicle/include/curves/affine.cu.h\nindex 2b1253e4..c9ddb5c2 100644\n--- a/icicle/include/curves/affine.cuh\n+++ b/icicle/include/curves/affine.cu.h\n@@ -1,7 +1,7 @@\n #pragma once\n \n-#include \"gpu-utils/sharedmem.cuh\"\n-#include \"gpu-utils/modifiers.cuh\"\n+#include \"gpu-utils/sharedmem.cu.h\"\n+#include \"gpu-utils/modifiers.cu.h\"\n #include <iostream>\n \n template <class FF>\ndiff --git a/icicle/include/curves/curve_config.cuh b/icicle/include/curves/curve_config.cu.h\nsimilarity index 74%\nrename from icicle/include/curves/curve_config.cuh\nrename to icicle/include/curves/curve_config.cu.h\nindex c9fe109b..fb219ca2 100644\n--- a/icicle/include/curves/curve_config.cuh\n+++ b/icicle/include/curves/curve_config.cu.h\n@@ -3,7 +3,7 @@\n #define CURVE_CONFIG_H\n \n #include \"fields/id.h\"\n-#include \"curves/projective.cuh\"\n+#include \"curves/projective.cu.h\"\n \n /**\n  * @namespace curve_config\n@@ -12,23 +12,23 @@\n  * with the `-DCURVE` env variable passed during build.\n  */\n #if CURVE_ID == BN254\n-#include \"curves/params/bn254.cuh\"\n+#include \"curves/params/bn254.cu.h\"\n namespace curve_config = bn254;\n \n #elif CURVE_ID == BLS12_381\n-#include \"curves/params/bls12_381.cuh\"\n+#include \"curves/params/bls12_381.cu.h\"\n namespace curve_config = bls12_381;\n \n #elif CURVE_ID == BLS12_377\n-#include \"curves/params/bls12_377.cuh\"\n+#include \"curves/params/bls12_377.cu.h\"\n namespace curve_config = bls12_377;\n \n #elif CURVE_ID == BW6_761\n-#include \"curves/params/bw6_761.cuh\"\n+#include \"curves/params/bw6_761.cu.h\"\n namespace curve_config = bw6_761;\n \n #elif CURVE_ID == GRUMPKIN\n-#include \"curves/params/grumpkin.cuh\"\n+#include \"curves/params/grumpkin.cu.h\"\n namespace curve_config = grumpkin;\n #endif\n #endif\n\\ No newline at end of file\ndiff --git a/icicle/include/curves/params/bls12_377.cuh b/icicle/include/curves/params/bls12_377.cu.h\nsimilarity index 93%\nrename from icicle/include/curves/params/bls12_377.cuh\nrename to icicle/include/curves/params/bls12_377.cu.h\nindex 3e81fb17..5c01bcec 100644\n--- a/icicle/include/curves/params/bls12_377.cuh\n+++ b/icicle/include/curves/params/bls12_377.cu.h\n@@ -2,13 +2,13 @@\n #ifndef BLS12_377_PARAMS_H\n #define BLS12_377_PARAMS_H\n \n-#include \"fields/storage.cuh\"\n+#include \"fields/storage.cu.h\"\n \n #include \"curves/macro.h\"\n-#include \"curves/projective.cuh\"\n-#include \"fields/snark_fields/bls12_377_base.cuh\"\n-#include \"fields/snark_fields/bls12_377_scalar.cuh\"\n-#include \"fields/quadratic_extension.cuh\"\n+#include \"curves/projective.cu.h\"\n+#include \"fields/snark_fields/bls12_377_base.cu.h\"\n+#include \"fields/snark_fields/bls12_377_scalar.cu.h\"\n+#include \"fields/quadratic_extension.cu.h\"\n \n namespace bls12_377 {\n   // G1 and G2 generators\ndiff --git a/icicle/include/curves/params/bls12_381.cuh b/icicle/include/curves/params/bls12_381.cu.h\nsimilarity index 93%\nrename from icicle/include/curves/params/bls12_381.cuh\nrename to icicle/include/curves/params/bls12_381.cu.h\nindex 86c7ef6a..d8998b70 100644\n--- a/icicle/include/curves/params/bls12_381.cuh\n+++ b/icicle/include/curves/params/bls12_381.cu.h\n@@ -2,13 +2,13 @@\n #ifndef BLS12_381_PARAMS_H\n #define BLS12_381_PARAMS_H\n \n-#include \"fields/storage.cuh\"\n+#include \"fields/storage.cu.h\"\n \n #include \"curves/macro.h\"\n-#include \"curves/projective.cuh\"\n-#include \"fields/snark_fields/bls12_381_base.cuh\"\n-#include \"fields/snark_fields/bls12_381_scalar.cuh\"\n-#include \"fields/quadratic_extension.cuh\"\n+#include \"curves/projective.cu.h\"\n+#include \"fields/snark_fields/bls12_381_base.cu.h\"\n+#include \"fields/snark_fields/bls12_381_scalar.cu.h\"\n+#include \"fields/quadratic_extension.cu.h\"\n \n namespace bls12_381 {\n   // G1 and G2 generators\ndiff --git a/icicle/include/curves/params/bn254.cuh b/icicle/include/curves/params/bn254.cu.h\nsimilarity index 91%\nrename from icicle/include/curves/params/bn254.cuh\nrename to icicle/include/curves/params/bn254.cu.h\nindex 4ae43760..88dedd3d 100644\n--- a/icicle/include/curves/params/bn254.cuh\n+++ b/icicle/include/curves/params/bn254.cu.h\n@@ -2,13 +2,13 @@\n #ifndef BN254_PARAMS_H\n #define BN254_PARAMS_H\n \n-#include \"fields/storage.cuh\"\n+#include \"fields/storage.cu.h\"\n \n #include \"curves/macro.h\"\n-#include \"curves/projective.cuh\"\n-#include \"fields/snark_fields/bn254_base.cuh\"\n-#include \"fields/snark_fields/bn254_scalar.cuh\"\n-#include \"fields/quadratic_extension.cuh\"\n+#include \"curves/projective.cu.h\"\n+#include \"fields/snark_fields/bn254_base.cu.h\"\n+#include \"fields/snark_fields/bn254_scalar.cu.h\"\n+#include \"fields/quadratic_extension.cu.h\"\n \n namespace bn254 {\n   // G1 and G2 generators\ndiff --git a/icicle/include/curves/params/bw6_761.cuh b/icicle/include/curves/params/bw6_761.cu.h\nsimilarity index 93%\nrename from icicle/include/curves/params/bw6_761.cuh\nrename to icicle/include/curves/params/bw6_761.cu.h\nindex ce649f5e..48898dce 100644\n--- a/icicle/include/curves/params/bw6_761.cuh\n+++ b/icicle/include/curves/params/bw6_761.cu.h\n@@ -2,13 +2,13 @@\n #ifndef BW6_761_PARAMS_H\n #define BW6_761_PARAMS_H\n \n-#include \"fields/storage.cuh\"\n+#include \"fields/storage.cu.h\"\n \n #include \"curves/macro.h\"\n-#include \"curves/projective.cuh\"\n-#include \"fields/snark_fields/bw6_761_base.cuh\"\n-#include \"fields/snark_fields/bw6_761_scalar.cuh\"\n-#include \"fields/quadratic_extension.cuh\"\n+#include \"curves/projective.cu.h\"\n+#include \"fields/snark_fields/bw6_761_base.cu.h\"\n+#include \"fields/snark_fields/bw6_761_scalar.cu.h\"\n+#include \"fields/quadratic_extension.cu.h\"\n \n namespace bw6_761 {\n   // G1 and G2 generators\ndiff --git a/icicle/include/curves/params/grumpkin.cuh b/icicle/include/curves/params/grumpkin.cu.h\nsimilarity index 84%\nrename from icicle/include/curves/params/grumpkin.cuh\nrename to icicle/include/curves/params/grumpkin.cu.h\nindex 855897a1..13c9e11f 100644\n--- a/icicle/include/curves/params/grumpkin.cuh\n+++ b/icicle/include/curves/params/grumpkin.cu.h\n@@ -2,12 +2,12 @@\n #ifndef GRUMPKIN_PARAMS_H\n #define GRUMPKIN_PARAMS_H\n \n-#include \"fields/storage.cuh\"\n+#include \"fields/storage.cu.h\"\n \n #include \"curves/macro.h\"\n-#include \"curves/projective.cuh\"\n-#include \"fields/snark_fields/grumpkin_base.cuh\"\n-#include \"fields/snark_fields/grumpkin_scalar.cuh\"\n+#include \"curves/projective.cu.h\"\n+#include \"fields/snark_fields/grumpkin_base.cu.h\"\n+#include \"fields/snark_fields/grumpkin_scalar.cu.h\"\n \n namespace grumpkin {\n   typedef bn254::fp_config fq_config;\ndiff --git a/icicle/include/curves/projective.cuh b/icicle/include/curves/projective.cu.h\nsimilarity index 99%\nrename from icicle/include/curves/projective.cuh\nrename to icicle/include/curves/projective.cu.h\nindex 47ff27a8..6d08460c 100644\n--- a/icicle/include/curves/projective.cuh\n+++ b/icicle/include/curves/projective.cu.h\n@@ -1,7 +1,7 @@\n #pragma once\n \n-#include \"affine.cuh\"\n-#include \"gpu-utils/sharedmem.cuh\"\n+#include \"affine.cu.h\"\n+#include \"gpu-utils/sharedmem.cu.h\"\n \n template <typename FF, class SCALAR_FF, const FF& B_VALUE, const FF& GENERATOR_X, const FF& GENERATOR_Y>\n class Projective\ndiff --git a/icicle/include/fields/field.cuh b/icicle/include/fields/field.cu.h\nsimilarity index 99%\nrename from icicle/include/fields/field.cuh\nrename to icicle/include/fields/field.cu.h\nindex 6fe3e069..fbde1b33 100644\n--- a/icicle/include/fields/field.cuh\n+++ b/icicle/include/fields/field.cu.h\n@@ -18,12 +18,12 @@\n \n #pragma once\n \n-#include \"gpu-utils/error_handler.cuh\"\n-#include \"gpu-utils/modifiers.cuh\"\n-#include \"gpu-utils/sharedmem.cuh\"\n-#include \"host_math.cuh\"\n-#include \"ptx.cuh\"\n-#include \"storage.cuh\"\n+#include \"gpu-utils/error_handler.cu.h\"\n+#include \"gpu-utils/modifiers.cu.h\"\n+#include \"gpu-utils/sharedmem.cu.h\"\n+#include \"host_math.cu.h\"\n+#include \"ptx.cu.h\"\n+#include \"storage.cu.h\"\n \n #include <iomanip>\n #include <iostream>\ndiff --git a/icicle/include/fields/field_config.cuh b/icicle/include/fields/field_config.cu.h\nsimilarity index 65%\nrename from icicle/include/fields/field_config.cuh\nrename to icicle/include/fields/field_config.cu.h\nindex d9ec18c0..72e1250e 100644\n--- a/icicle/include/fields/field_config.cuh\n+++ b/icicle/include/fields/field_config.cu.h\n@@ -3,7 +3,7 @@\n #define FIELD_CONFIG_H\n \n #include \"fields/id.h\"\n-#include \"fields/field.cuh\"\n+#include \"fields/field.cu.h\"\n \n /**\n  * @namespace field_config\n@@ -11,30 +11,30 @@\n  * with the `-DFIELD` env variable passed during build.\n  */\n #if FIELD_ID == BN254\n-#include \"fields/snark_fields/bn254_scalar.cuh\"\n+#include \"fields/snark_fields/bn254_scalar.cu.h\"\n namespace field_config = bn254;\n #elif FIELD_ID == BLS12_381\n-#include \"fields/snark_fields/bls12_381_scalar.cuh\"\n+#include \"fields/snark_fields/bls12_381_scalar.cu.h\"\n using bls12_381::fp_config;\n namespace field_config = bls12_381;\n #elif FIELD_ID == BLS12_377\n-#include \"fields/snark_fields/bls12_377_scalar.cuh\"\n+#include \"fields/snark_fields/bls12_377_scalar.cu.h\"\n namespace field_config = bls12_377;\n #elif FIELD_ID == BW6_761\n-#include \"fields/snark_fields/bw6_761_scalar.cuh\"\n+#include \"fields/snark_fields/bw6_761_scalar.cu.h\"\n namespace field_config = bw6_761;\n #elif FIELD_ID == GRUMPKIN\n-#include \"fields/snark_fields/grumpkin_scalar.cuh\"\n+#include \"fields/snark_fields/grumpkin_scalar.cu.h\"\n namespace field_config = grumpkin;\n \n #elif FIELD_ID == BABY_BEAR\n-#include \"fields/stark_fields/babybear.cuh\"\n+#include \"fields/stark_fields/babybear.cu.h\"\n namespace field_config = babybear;\n #elif FIELD_ID == STARK_252\n-#include \"fields/stark_fields/stark252.cuh\"\n+#include \"fields/stark_fields/stark252.cu.h\"\n namespace field_config = stark252;\n #elif FIELD_ID == M31\n-#include \"fields/stark_fields/m31.cuh\"\n+#include \"fields/stark_fields/m31.cu.h\"\n namespace field_config = m31;\n #endif\n \ndiff --git a/icicle/include/fields/host_math.cuh b/icicle/include/fields/host_math.cu.h\nsimilarity index 98%\nrename from icicle/include/fields/host_math.cuh\nrename to icicle/include/fields/host_math.cu.h\nindex 0cdde0ca..7669fd42 100644\n--- a/icicle/include/fields/host_math.cuh\n+++ b/icicle/include/fields/host_math.cu.h\n@@ -4,8 +4,8 @@\n \n #include <cstdint>\n #include <cuda_runtime.h>\n-#include \"gpu-utils/modifiers.cuh\"\n-#include \"storage.cuh\"\n+#include \"gpu-utils/modifiers.cu.h\"\n+#include \"storage.cu.h\"\n \n namespace host_math {\n \ndiff --git a/icicle/include/fields/params_gen.cuh b/icicle/include/fields/params_gen.cu.h\nsimilarity index 99%\nrename from icicle/include/fields/params_gen.cuh\nrename to icicle/include/fields/params_gen.cu.h\nindex 6b21c5e0..638a9391 100644\n--- a/icicle/include/fields/params_gen.cuh\n+++ b/icicle/include/fields/params_gen.cu.h\n@@ -2,8 +2,8 @@\n #ifndef PARAMS_GEN_H\n #define PARAMS_GEN_H\n \n-#include \"storage.cuh\"\n-#include \"host_math.cuh\"\n+#include \"storage.cu.h\"\n+#include \"host_math.cu.h\"\n \n namespace params_gen {\n   template <unsigned NLIMBS, unsigned BIT_SHIFT>\ndiff --git a/icicle/include/fields/ptx.cuh b/icicle/include/fields/ptx.cu.h\nsimilarity index 100%\nrename from icicle/include/fields/ptx.cuh\nrename to icicle/include/fields/ptx.cu.h\ndiff --git a/icicle/include/fields/quadratic_extension.cuh b/icicle/include/fields/quadratic_extension.cu.h\nsimilarity index 98%\nrename from icicle/include/fields/quadratic_extension.cuh\nrename to icicle/include/fields/quadratic_extension.cu.h\nindex 10065386..6c4f0dff 100644\n--- a/icicle/include/fields/quadratic_extension.cuh\n+++ b/icicle/include/fields/quadratic_extension.cu.h\n@@ -1,8 +1,8 @@\n #pragma once\n \n-#include \"field.cuh\"\n-#include \"gpu-utils/modifiers.cuh\"\n-#include \"gpu-utils/sharedmem.cuh\"\n+#include \"field.cu.h\"\n+#include \"gpu-utils/modifiers.cu.h\"\n+#include \"gpu-utils/sharedmem.cu.h\"\n \n template <typename CONFIG, class T>\n class ExtensionField\ndiff --git a/icicle/include/fields/quartic_extension.cuh b/icicle/include/fields/quartic_extension.cu.h\nsimilarity index 99%\nrename from icicle/include/fields/quartic_extension.cuh\nrename to icicle/include/fields/quartic_extension.cu.h\nindex 8fead58c..609ecf36 100644\n--- a/icicle/include/fields/quartic_extension.cuh\n+++ b/icicle/include/fields/quartic_extension.cu.h\n@@ -1,8 +1,8 @@\n #pragma once\n \n-#include \"field.cuh\"\n-#include \"gpu-utils/modifiers.cuh\"\n-#include \"gpu-utils/sharedmem.cuh\"\n+#include \"field.cu.h\"\n+#include \"gpu-utils/modifiers.cu.h\"\n+#include \"gpu-utils/sharedmem.cu.h\"\n \n template <typename CONFIG, class T>\n class ExtensionField\ndiff --git a/icicle/include/fields/snark_fields/bls12_377_base.cuh b/icicle/include/fields/snark_fields/bls12_377_base.cu.h\nsimilarity index 92%\nrename from icicle/include/fields/snark_fields/bls12_377_base.cuh\nrename to icicle/include/fields/snark_fields/bls12_377_base.cu.h\nindex dc7a13fe..abf9b89f 100644\n--- a/icicle/include/fields/snark_fields/bls12_377_base.cuh\n+++ b/icicle/include/fields/snark_fields/bls12_377_base.cu.h\n@@ -2,8 +2,8 @@\n #ifndef BLS12_377_BASE_PARAMS_H\n #define BLS12_377_BASE_PARAMS_H\n \n-#include \"fields/storage.cuh\"\n-#include \"fields/params_gen.cuh\"\n+#include \"fields/storage.cu.h\"\n+#include \"fields/params_gen.cu.h\"\n \n namespace bls12_377 {\n   struct fq_config {\ndiff --git a/icicle/include/fields/snark_fields/bls12_377_scalar.cuh b/icicle/include/fields/snark_fields/bls12_377_scalar.cu.h\nsimilarity index 86%\nrename from icicle/include/fields/snark_fields/bls12_377_scalar.cuh\nrename to icicle/include/fields/snark_fields/bls12_377_scalar.cu.h\nindex 3924f59f..0b0ded12 100644\n--- a/icicle/include/fields/snark_fields/bls12_377_scalar.cuh\n+++ b/icicle/include/fields/snark_fields/bls12_377_scalar.cu.h\n@@ -2,9 +2,9 @@\n #ifndef BLS12_377_SCALAR_PARAMS_H\n #define BLS12_377_SCALAR_PARAMS_H\n \n-#include \"fields/storage.cuh\"\n-#include \"fields/field.cuh\"\n-#include \"fields/params_gen.cuh\"\n+#include \"fields/storage.cu.h\"\n+#include \"fields/field.cu.h\"\n+#include \"fields/params_gen.cu.h\"\n \n namespace bls12_377 {\n   struct fp_config {\ndiff --git a/icicle/include/fields/snark_fields/bls12_381_base.cuh b/icicle/include/fields/snark_fields/bls12_381_base.cu.h\nsimilarity index 89%\nrename from icicle/include/fields/snark_fields/bls12_381_base.cuh\nrename to icicle/include/fields/snark_fields/bls12_381_base.cu.h\nindex 54fc0ab5..cef89c40 100644\n--- a/icicle/include/fields/snark_fields/bls12_381_base.cuh\n+++ b/icicle/include/fields/snark_fields/bls12_381_base.cu.h\n@@ -2,8 +2,8 @@\n #ifndef BLS12_381_BASE_PARAMS_H\n #define BLS12_381_BASE_PARAMS_H\n \n-#include \"fields/storage.cuh\"\n-#include \"fields/params_gen.cuh\"\n+#include \"fields/storage.cu.h\"\n+#include \"fields/params_gen.cu.h\"\n \n namespace bls12_381 {\n   struct fq_config {\ndiff --git a/icicle/include/fields/snark_fields/bls12_381_scalar.cuh b/icicle/include/fields/snark_fields/bls12_381_scalar.cu.h\nsimilarity index 87%\nrename from icicle/include/fields/snark_fields/bls12_381_scalar.cuh\nrename to icicle/include/fields/snark_fields/bls12_381_scalar.cu.h\nindex c6bd12fe..61e9f8bc 100644\n--- a/icicle/include/fields/snark_fields/bls12_381_scalar.cuh\n+++ b/icicle/include/fields/snark_fields/bls12_381_scalar.cu.h\n@@ -2,9 +2,9 @@\n #ifndef BLS12_381_SCALAR_PARAMS_H\n #define BLS12_381_SCALAR_PARAMS_H\n \n-#include \"fields/storage.cuh\"\n-#include \"fields/field.cuh\"\n-#include \"fields/params_gen.cuh\"\n+#include \"fields/storage.cu.h\"\n+#include \"fields/field.cu.h\"\n+#include \"fields/params_gen.cu.h\"\n \n namespace bls12_381 {\n   struct fp_config {\ndiff --git a/icicle/include/fields/snark_fields/bn254_base.cuh b/icicle/include/fields/snark_fields/bn254_base.cu.h\nsimilarity index 89%\nrename from icicle/include/fields/snark_fields/bn254_base.cuh\nrename to icicle/include/fields/snark_fields/bn254_base.cu.h\nindex 95f18ed3..da993cbd 100644\n--- a/icicle/include/fields/snark_fields/bn254_base.cuh\n+++ b/icicle/include/fields/snark_fields/bn254_base.cu.h\n@@ -2,8 +2,8 @@\n #ifndef BN254_BASE_PARAMS_H\n #define BN254_BASE_PARAMS_H\n \n-#include \"fields/storage.cuh\"\n-#include \"fields/params_gen.cuh\"\n+#include \"fields/storage.cu.h\"\n+#include \"fields/params_gen.cu.h\"\n \n namespace bn254 {\n   struct fq_config {\ndiff --git a/icicle/include/fields/snark_fields/bn254_scalar.cuh b/icicle/include/fields/snark_fields/bn254_scalar.cu.h\nsimilarity index 87%\nrename from icicle/include/fields/snark_fields/bn254_scalar.cuh\nrename to icicle/include/fields/snark_fields/bn254_scalar.cu.h\nindex 67478e2d..8911357c 100644\n--- a/icicle/include/fields/snark_fields/bn254_scalar.cuh\n+++ b/icicle/include/fields/snark_fields/bn254_scalar.cu.h\n@@ -2,9 +2,9 @@\n #ifndef BN254_SCALAR_PARAMS_H\n #define BN254_SCALAR_PARAMS_H\n \n-#include \"fields/storage.cuh\"\n-#include \"fields/field.cuh\"\n-#include \"fields/params_gen.cuh\"\n+#include \"fields/storage.cu.h\"\n+#include \"fields/field.cu.h\"\n+#include \"fields/params_gen.cu.h\"\n \n namespace bn254 {\n   struct fp_config {\ndiff --git a/icicle/include/fields/snark_fields/bw6_761_base.cuh b/icicle/include/fields/snark_fields/bw6_761_base.cu.h\nsimilarity index 89%\nrename from icicle/include/fields/snark_fields/bw6_761_base.cuh\nrename to icicle/include/fields/snark_fields/bw6_761_base.cu.h\nindex 4ec110ff..6ab52e19 100644\n--- a/icicle/include/fields/snark_fields/bw6_761_base.cuh\n+++ b/icicle/include/fields/snark_fields/bw6_761_base.cu.h\n@@ -2,8 +2,8 @@\n #ifndef BW6_761_BASE_BASE_H\n #define BW6_761_BASE_BASE_H\n \n-#include \"fields/storage.cuh\"\n-#include \"fields/params_gen.cuh\"\n+#include \"fields/storage.cu.h\"\n+#include \"fields/params_gen.cu.h\"\n \n namespace bw6_761 {\n   struct fq_config {\ndiff --git a/icicle/include/fields/snark_fields/bw6_761_scalar.cuh b/icicle/include/fields/snark_fields/bw6_761_scalar.cu.h\nsimilarity index 68%\nrename from icicle/include/fields/snark_fields/bw6_761_scalar.cuh\nrename to icicle/include/fields/snark_fields/bw6_761_scalar.cu.h\nindex 304595a7..cb9205ab 100644\n--- a/icicle/include/fields/snark_fields/bw6_761_scalar.cuh\n+++ b/icicle/include/fields/snark_fields/bw6_761_scalar.cu.h\n@@ -2,9 +2,9 @@\n #ifndef BW6_761_SCALAR_PARAMS_H\n #define BW6_761_SCALAR_PARAMS_H\n \n-#include \"fields/storage.cuh\"\n-#include \"fields/field.cuh\"\n-#include \"fields/snark_fields/bls12_377_base.cuh\"\n+#include \"fields/storage.cu.h\"\n+#include \"fields/field.cu.h\"\n+#include \"fields/snark_fields/bls12_377_base.cu.h\"\n \n namespace bw6_761 {\n   typedef bls12_377::fq_config fp_config;\ndiff --git a/icicle/include/fields/snark_fields/grumpkin_base.cuh b/icicle/include/fields/snark_fields/grumpkin_base.cu.h\nsimilarity index 61%\nrename from icicle/include/fields/snark_fields/grumpkin_base.cuh\nrename to icicle/include/fields/snark_fields/grumpkin_base.cu.h\nindex 355f2f30..95d7ed3e 100644\n--- a/icicle/include/fields/snark_fields/grumpkin_base.cuh\n+++ b/icicle/include/fields/snark_fields/grumpkin_base.cu.h\n@@ -2,8 +2,8 @@\n #ifndef GRUMPKIN_BASE_PARAMS_H\n #define GRUMPKIN_BASE_PARAMS_H\n \n-#include \"fields/storage.cuh\"\n-#include \"fields/snark_fields/bn254_scalar.cuh\"\n+#include \"fields/storage.cu.h\"\n+#include \"fields/snark_fields/bn254_scalar.cu.h\"\n \n namespace grumpkin {\n   typedef bn254::fp_config fq_config;\ndiff --git a/icicle/include/fields/snark_fields/grumpkin_scalar.cuh b/icicle/include/fields/snark_fields/grumpkin_scalar.cu.h\nsimilarity index 69%\nrename from icicle/include/fields/snark_fields/grumpkin_scalar.cuh\nrename to icicle/include/fields/snark_fields/grumpkin_scalar.cu.h\nindex 4354c8de..87287e1b 100644\n--- a/icicle/include/fields/snark_fields/grumpkin_scalar.cuh\n+++ b/icicle/include/fields/snark_fields/grumpkin_scalar.cu.h\n@@ -2,9 +2,9 @@\n #ifndef GRUMPKIN_SCALAR_PARAMS_H\n #define GRUMPKIN_SCALAR_PARAMS_H\n \n-#include \"fields/storage.cuh\"\n-#include \"fields/field.cuh\"\n-#include \"fields/snark_fields/bn254_base.cuh\"\n+#include \"fields/storage.cu.h\"\n+#include \"fields/field.cu.h\"\n+#include \"fields/snark_fields/bn254_base.cu.h\"\n \n namespace grumpkin {\n   typedef bn254::fq_config fp_config;\ndiff --git a/icicle/include/fields/stark_fields/babybear.cuh b/icicle/include/fields/stark_fields/babybear.cu.h\nsimilarity index 83%\nrename from icicle/include/fields/stark_fields/babybear.cuh\nrename to icicle/include/fields/stark_fields/babybear.cu.h\nindex 57f3f06e..76ea9691 100644\n--- a/icicle/include/fields/stark_fields/babybear.cuh\n+++ b/icicle/include/fields/stark_fields/babybear.cu.h\n@@ -1,9 +1,9 @@\n #pragma once\n \n-#include \"fields/storage.cuh\"\n-#include \"fields/field.cuh\"\n-#include \"fields/quartic_extension.cuh\"\n-#include \"fields/params_gen.cuh\"\n+#include \"fields/storage.cu.h\"\n+#include \"fields/field.cu.h\"\n+#include \"fields/quartic_extension.cu.h\"\n+#include \"fields/params_gen.cu.h\"\n \n namespace babybear {\n   struct fp_config {\ndiff --git a/icicle/include/fields/stark_fields/m31.cuh b/icicle/include/fields/stark_fields/m31.cu.h\nsimilarity index 98%\nrename from icicle/include/fields/stark_fields/m31.cuh\nrename to icicle/include/fields/stark_fields/m31.cu.h\nindex b45592ce..b7fe5e5d 100644\n--- a/icicle/include/fields/stark_fields/m31.cuh\n+++ b/icicle/include/fields/stark_fields/m31.cu.h\n@@ -1,8 +1,8 @@\n #pragma once\n \n-#include \"fields/storage.cuh\"\n-#include \"fields/field.cuh\"\n-#include \"fields/quartic_extension.cuh\"\n+#include \"fields/storage.cu.h\"\n+#include \"fields/field.cu.h\"\n+#include \"fields/quartic_extension.cu.h\"\n \n namespace m31 {\n   template <class CONFIG>\ndiff --git a/icicle/include/fields/stark_fields/stark252.cuh b/icicle/include/fields/stark_fields/stark252.cu.h\nsimilarity index 85%\nrename from icicle/include/fields/stark_fields/stark252.cuh\nrename to icicle/include/fields/stark_fields/stark252.cu.h\nindex d61f757e..6e45386c 100644\n--- a/icicle/include/fields/stark_fields/stark252.cuh\n+++ b/icicle/include/fields/stark_fields/stark252.cu.h\n@@ -1,8 +1,8 @@\n #pragma once\n \n-#include \"fields/storage.cuh\"\n-#include \"fields/field.cuh\"\n-#include \"fields/params_gen.cuh\"\n+#include \"fields/storage.cu.h\"\n+#include \"fields/field.cu.h\"\n+#include \"fields/params_gen.cu.h\"\n \n // modulus = 3618502788666131213697322783095070105623107215331596699973092056135872020481 (2^251+17*2^192+1)\n namespace stark252 {\ndiff --git a/icicle/include/fields/storage.cuh b/icicle/include/fields/storage.cu.h\nsimilarity index 100%\nrename from icicle/include/fields/storage.cuh\nrename to icicle/include/fields/storage.cu.h\ndiff --git a/icicle/include/gpu-utils/device_context.cuh b/icicle/include/gpu-utils/device_context.cu.h\nsimilarity index 97%\nrename from icicle/include/gpu-utils/device_context.cuh\nrename to icicle/include/gpu-utils/device_context.cu.h\nindex b1448a5a..31658426 100644\n--- a/icicle/include/gpu-utils/device_context.cuh\n+++ b/icicle/include/gpu-utils/device_context.cu.h\n@@ -3,7 +3,7 @@\n #define DEVICE_CONTEXT_H\n \n #include <cuda_runtime.h>\n-#include \"gpu-utils/error_handler.cuh\"\n+#include \"gpu-utils/error_handler.cu.h\"\n \n namespace device_context {\n \ndiff --git a/icicle/include/gpu-utils/error_handler.cuh b/icicle/include/gpu-utils/error_handler.cu.h\nsimilarity index 100%\nrename from icicle/include/gpu-utils/error_handler.cuh\nrename to icicle/include/gpu-utils/error_handler.cu.h\ndiff --git a/icicle/include/gpu-utils/modifiers.cuh b/icicle/include/gpu-utils/modifiers.cu.h\nsimilarity index 100%\nrename from icicle/include/gpu-utils/modifiers.cuh\nrename to icicle/include/gpu-utils/modifiers.cu.h\ndiff --git a/icicle/include/gpu-utils/sharedmem.cuh b/icicle/include/gpu-utils/sharedmem.cu.h\nsimilarity index 100%\nrename from icicle/include/gpu-utils/sharedmem.cuh\nrename to icicle/include/gpu-utils/sharedmem.cu.h\ndiff --git a/icicle/include/hash/hash.cuh b/icicle/include/hash/hash.cu.h\nsimilarity index 98%\nrename from icicle/include/hash/hash.cuh\nrename to icicle/include/hash/hash.cu.h\nindex 338d337d..7ca414cc 100644\n--- a/icicle/include/hash/hash.cuh\n+++ b/icicle/include/hash/hash.cu.h\n@@ -2,9 +2,9 @@\n #ifndef HASH_H\n #define HASH_H\n \n-#include \"gpu-utils/device_context.cuh\"\n-#include \"gpu-utils/error_handler.cuh\"\n-#include \"matrix/matrix.cuh\"\n+#include \"gpu-utils/device_context.cu.h\"\n+#include \"gpu-utils/error_handler.cu.h\"\n+#include \"matrix/matrix.cu.h\"\n #include <cassert>\n \n using matrix::Matrix;\ndiff --git a/icicle/include/hash/keccak/keccak.cuh b/icicle/include/hash/keccak/keccak.cu.h\nsimilarity index 87%\nrename from icicle/include/hash/keccak/keccak.cuh\nrename to icicle/include/hash/keccak/keccak.cu.h\nindex d95c22a4..eee2f806 100644\n--- a/icicle/include/hash/keccak/keccak.cuh\n+++ b/icicle/include/hash/keccak/keccak.cu.h\n@@ -3,10 +3,10 @@\n #define KECCAK_H\n \n #include <cstdint>\n-#include \"gpu-utils/device_context.cuh\"\n-#include \"gpu-utils/error_handler.cuh\"\n+#include \"gpu-utils/device_context.cu.h\"\n+#include \"gpu-utils/error_handler.cu.h\"\n \n-#include \"hash/hash.cuh\"\n+#include \"hash/hash.cu.h\"\n \n using namespace hash;\n \ndiff --git a/icicle/include/matrix/matrix.cuh b/icicle/include/matrix/matrix.cu.h\nsimilarity index 100%\nrename from icicle/include/matrix/matrix.cuh\nrename to icicle/include/matrix/matrix.cu.h\ndiff --git a/icicle/include/merkle-tree/merkle.cuh b/icicle/include/merkle-tree/merkle.cu.h\nsimilarity index 96%\nrename from icicle/include/merkle-tree/merkle.cuh\nrename to icicle/include/merkle-tree/merkle.cu.h\nindex ba50ff76..d0a020db 100644\n--- a/icicle/include/merkle-tree/merkle.cuh\n+++ b/icicle/include/merkle-tree/merkle.cu.h\n@@ -2,11 +2,11 @@\n #ifndef MERKLE_H\n #define MERKLE_H\n \n-#include \"gpu-utils/device_context.cuh\"\n-#include \"gpu-utils/error_handler.cuh\"\n+#include \"gpu-utils/device_context.cu.h\"\n+#include \"gpu-utils/error_handler.cu.h\"\n #include \"utils/utils.h\"\n-#include \"hash/hash.cuh\"\n-#include \"matrix/matrix.cuh\"\n+#include \"hash/hash.cu.h\"\n+#include \"matrix/matrix.cu.h\"\n \n #include <vector>\n #include <numeric>\ndiff --git a/icicle/include/msm/msm.cuh b/icicle/include/msm/msm.cu.h\nsimilarity index 98%\nrename from icicle/include/msm/msm.cuh\nrename to icicle/include/msm/msm.cu.h\nindex 1bd27a48..e393799b 100644\n--- a/icicle/include/msm/msm.cuh\n+++ b/icicle/include/msm/msm.cu.h\n@@ -4,11 +4,11 @@\n \n #include <cuda_runtime.h>\n \n-#include \"curves/affine.cuh\"\n-#include \"curves/projective.cuh\"\n-#include \"fields/field.cuh\"\n-#include \"gpu-utils/device_context.cuh\"\n-#include \"gpu-utils/error_handler.cuh\"\n+#include \"curves/affine.cu.h\"\n+#include \"curves/projective.cu.h\"\n+#include \"fields/field.cu.h\"\n+#include \"gpu-utils/device_context.cu.h\"\n+#include \"gpu-utils/error_handler.cu.h\"\n \n /**\n  * @namespace msm\ndiff --git a/icicle/include/ntt/ntt.cuh b/icicle/include/ntt/ntt.cu.h\nsimilarity index 98%\nrename from icicle/include/ntt/ntt.cuh\nrename to icicle/include/ntt/ntt.cu.h\nindex 1f032bb4..65482d85 100644\n--- a/icicle/include/ntt/ntt.cuh\n+++ b/icicle/include/ntt/ntt.cu.h\n@@ -4,10 +4,10 @@\n \n #include <cuda_runtime.h>\n \n-#include \"gpu-utils/device_context.cuh\"\n-#include \"gpu-utils/error_handler.cuh\"\n-#include \"gpu-utils/sharedmem.cuh\"\n-#include \"utils/utils_kernels.cuh\"\n+#include \"gpu-utils/device_context.cu.h\"\n+#include \"gpu-utils/error_handler.cu.h\"\n+#include \"gpu-utils/sharedmem.cu.h\"\n+#include \"utils/utils_kernels.cu.h\"\n #include \"utils/utils.h\"\n \n /**\ndiff --git a/icicle/include/ntt/ntt_impl.cuh b/icicle/include/ntt/ntt_impl.cu.h\nsimilarity index 94%\nrename from icicle/include/ntt/ntt_impl.cuh\nrename to icicle/include/ntt/ntt_impl.cu.h\nindex 5f5bf1e2..56b33bb8 100644\n--- a/icicle/include/ntt/ntt_impl.cuh\n+++ b/icicle/include/ntt/ntt_impl.cu.h\n@@ -3,7 +3,7 @@\n #define _NTT_IMPL_H\n \n #include <stdint.h>\n-#include \"ntt/ntt.cuh\" // for enum Ordering\n+#include \"ntt/ntt.cu.h\" // for enum Ordering\n \n namespace mxntt {\n \ndiff --git a/icicle/include/polynomials/cuda_backend/polynomial_cuda_backend.cuh b/icicle/include/polynomials/cuda_backend/polynomial_cuda_backend.cu.h\nsimilarity index 88%\nrename from icicle/include/polynomials/cuda_backend/polynomial_cuda_backend.cuh\nrename to icicle/include/polynomials/cuda_backend/polynomial_cuda_backend.cu.h\nindex da07254a..40d7bce6 100644\n--- a/icicle/include/polynomials/cuda_backend/polynomial_cuda_backend.cuh\n+++ b/icicle/include/polynomials/cuda_backend/polynomial_cuda_backend.cu.h\n@@ -1,7 +1,7 @@\n #pragma once\n \n-#include \"gpu-utils/device_context.cuh\"\n-#include \"fields/field_config.cuh\"\n+#include \"gpu-utils/device_context.cu.h\"\n+#include \"fields/field_config.cu.h\"\n #include \"polynomials/polynomials.h\"\n \n using device_context::DeviceContext;\ndiff --git a/icicle/include/polynomials/polynomials.h b/icicle/include/polynomials/polynomials.h\nindex 7916b837..f61f794f 100644\n--- a/icicle/include/polynomials/polynomials.h\n+++ b/icicle/include/polynomials/polynomials.h\n@@ -3,7 +3,7 @@\n #include <iostream>\n #include <memory>\n #include \"utils/integrity_pointer.h\"\n-#include \"fields/field_config.cuh\"\n+#include \"fields/field_config.cu.h\"\n \n #include \"polynomial_context.h\"\n #include \"polynomial_backend.h\"\ndiff --git a/icicle/include/poseidon/constants.cuh b/icicle/include/poseidon/constants.cu.h\nsimilarity index 100%\nrename from icicle/include/poseidon/constants.cuh\nrename to icicle/include/poseidon/constants.cu.h\ndiff --git a/icicle/include/poseidon/kernels.cuh b/icicle/include/poseidon/kernels.cu.h\nsimilarity index 98%\nrename from icicle/include/poseidon/kernels.cuh\nrename to icicle/include/poseidon/kernels.cu.h\nindex 4afa9878..b9ca6972 100644\n--- a/icicle/include/poseidon/kernels.cuh\n+++ b/icicle/include/poseidon/kernels.cu.h\n@@ -2,8 +2,8 @@\n #ifndef POSEIDON_KERNELS_H\n #define POSEIDON_KERNELS_H\n \n-#include \"gpu-utils/modifiers.cuh\"\n-#include \"poseidon/constants.cuh\"\n+#include \"gpu-utils/modifiers.cu.h\"\n+#include \"poseidon/constants.cu.h\"\n \n namespace poseidon {\n   template <typename S, int T>\ndiff --git a/icicle/include/poseidon/poseidon.cuh b/icicle/include/poseidon/poseidon.cu.h\nsimilarity index 94%\nrename from icicle/include/poseidon/poseidon.cuh\nrename to icicle/include/poseidon/poseidon.cu.h\nindex 24727c21..3b1d6b3b 100644\n--- a/icicle/include/poseidon/poseidon.cuh\n+++ b/icicle/include/poseidon/poseidon.cu.h\n@@ -4,13 +4,13 @@\n \n #include <cstdint>\n #include <stdexcept>\n-#include \"gpu-utils/device_context.cuh\"\n-#include \"gpu-utils/error_handler.cuh\"\n+#include \"gpu-utils/device_context.cu.h\"\n+#include \"gpu-utils/error_handler.cu.h\"\n #include \"utils/utils.h\"\n \n-#include \"poseidon/kernels.cuh\"\n-#include \"poseidon/constants.cuh\"\n-#include \"hash/hash.cuh\"\n+#include \"poseidon/kernels.cu.h\"\n+#include \"poseidon/constants.cu.h\"\n+#include \"hash/hash.cu.h\"\n using namespace hash;\n \n /**\ndiff --git a/icicle/include/poseidon2/constants.cuh b/icicle/include/poseidon2/constants.cu.h\nsimilarity index 97%\nrename from icicle/include/poseidon2/constants.cuh\nrename to icicle/include/poseidon2/constants.cu.h\nindex 35bc2ebf..5b20ef6b 100644\n--- a/icicle/include/poseidon2/constants.cuh\n+++ b/icicle/include/poseidon2/constants.cu.h\n@@ -2,7 +2,7 @@\n #ifndef POSEIDON2_CONSTANTS_H\n #define POSEIDON2_CONSTANTS_H\n \n-#include \"gpu-utils/device_context.cuh\"\n+#include \"gpu-utils/device_context.cu.h\"\n \n namespace poseidon2 {\n   /**\ndiff --git a/icicle/include/poseidon2/kernels.cuh b/icicle/include/poseidon2/kernels.cu.h\nsimilarity index 98%\nrename from icicle/include/poseidon2/kernels.cuh\nrename to icicle/include/poseidon2/kernels.cu.h\nindex 5f34be92..409098fc 100644\n--- a/icicle/include/poseidon2/kernels.cuh\n+++ b/icicle/include/poseidon2/kernels.cu.h\n@@ -3,10 +3,10 @@\n #define POSEIDON2_KERNELS_H\n \n #include \"utils/utils.h\"\n-#include \"hash/hash.cuh\"\n-#include \"matrix/matrix.cuh\"\n-#include \"poseidon2/constants.cuh\"\n-#include \"gpu-utils/modifiers.cuh\"\n+#include \"hash/hash.cu.h\"\n+#include \"matrix/matrix.cu.h\"\n+#include \"poseidon2/constants.cu.h\"\n+#include \"gpu-utils/modifiers.cu.h\"\n \n using matrix::Matrix;\n \ndiff --git a/icicle/include/poseidon2/poseidon2.cuh b/icicle/include/poseidon2/poseidon2.cu.h\nsimilarity index 96%\nrename from icicle/include/poseidon2/poseidon2.cuh\nrename to icicle/include/poseidon2/poseidon2.cu.h\nindex 016740e7..c85e7d7c 100644\n--- a/icicle/include/poseidon2/poseidon2.cuh\n+++ b/icicle/include/poseidon2/poseidon2.cu.h\n@@ -4,15 +4,15 @@\n \n #include <cstdint>\n #include <stdexcept>\n-#include \"gpu-utils/device_context.cuh\"\n-#include \"gpu-utils/error_handler.cuh\"\n+#include \"gpu-utils/device_context.cu.h\"\n+#include \"gpu-utils/error_handler.cu.h\"\n #include \"utils/utils.h\"\n \n-#include \"hash/hash.cuh\"\n-#include \"matrix/matrix.cuh\"\n+#include \"hash/hash.cu.h\"\n+#include \"matrix/matrix.cu.h\"\n \n-#include \"poseidon2/constants.cuh\"\n-#include \"poseidon2/kernels.cuh\"\n+#include \"poseidon2/constants.cu.h\"\n+#include \"poseidon2/kernels.cu.h\"\n \n using matrix::Matrix;\n \ndiff --git a/icicle/include/utils/mont.cuh b/icicle/include/utils/mont.cu.h\nsimilarity index 100%\nrename from icicle/include/utils/mont.cuh\nrename to icicle/include/utils/mont.cu.h\ndiff --git a/icicle/include/utils/test_functions.cuh b/icicle/include/utils/test_functions.cu.h\nsimilarity index 100%\nrename from icicle/include/utils/test_functions.cuh\nrename to icicle/include/utils/test_functions.cu.h\ndiff --git a/icicle/include/utils/utils_kernels.cuh b/icicle/include/utils/utils_kernels.cu.h\nsimilarity index 100%\nrename from icicle/include/utils/utils_kernels.cuh\nrename to icicle/include/utils/utils_kernels.cu.h\ndiff --git a/icicle/include/vec_ops/vec_ops.cuh b/icicle/include/vec_ops/vec_ops.cu.h\nsimilarity index 99%\nrename from icicle/include/vec_ops/vec_ops.cuh\nrename to icicle/include/vec_ops/vec_ops.cu.h\nindex ee624a07..00c4aac9 100644\n--- a/icicle/include/vec_ops/vec_ops.cuh\n+++ b/icicle/include/vec_ops/vec_ops.cu.h\n@@ -2,7 +2,7 @@\n #ifndef LDE_H\n #define LDE_H\n \n-#include \"gpu-utils/device_context.cuh\"\n+#include \"gpu-utils/device_context.cu.h\"\n \n /**\n  * @namespace vec_ops\ndiff --git a/icicle/src/curves/extern.cu b/icicle/src/curves/extern.cu.cc\nsimilarity index 93%\nrename from icicle/src/curves/extern.cu\nrename to icicle/src/curves/extern.cu.cc\nindex 8ea5bce2..d7384fa2 100644\n--- a/icicle/src/curves/extern.cu\n+++ b/icicle/src/curves/extern.cu.cc\n@@ -1,10 +1,10 @@\n-#include \"curves/curve_config.cuh\"\n+#include \"curves/curve_config.cu.h\"\n \n using namespace curve_config;\n \n-#include \"gpu-utils/device_context.cuh\"\n+#include \"gpu-utils/device_context.cu.h\"\n #include \"utils/utils.h\"\n-#include \"utils/mont.cuh\"\n+#include \"utils/mont.cu.h\"\n \n extern \"C\" bool CONCAT_EXPAND(CURVE, eq)(projective_t* point1, projective_t* point2)\n {\ndiff --git a/icicle/src/curves/extern_g2.cu b/icicle/src/curves/extern_g2.cu.cc\nsimilarity index 93%\nrename from icicle/src/curves/extern_g2.cu\nrename to icicle/src/curves/extern_g2.cu.cc\nindex e8daa5e1..76f8a476 100644\n--- a/icicle/src/curves/extern_g2.cu\n+++ b/icicle/src/curves/extern_g2.cu.cc\n@@ -1,10 +1,10 @@\n-#include \"curves/curve_config.cuh\"\n+#include \"curves/curve_config.cu.h\"\n \n using namespace curve_config;\n \n-#include \"gpu-utils/device_context.cuh\"\n+#include \"gpu-utils/device_context.cu.h\"\n #include \"utils/utils.h\"\n-#include \"utils/mont.cuh\"\n+#include \"utils/mont.cu.h\"\n \n extern \"C\" bool CONCAT_EXPAND(CURVE, g2_eq)(g2_projective_t* point1, g2_projective_t* point2)\n {\ndiff --git a/icicle/src/fields/extern.cu b/icicle/src/fields/extern.cu.cc\nsimilarity index 83%\nrename from icicle/src/fields/extern.cu\nrename to icicle/src/fields/extern.cu.cc\nindex 26c98ac3..7f65c228 100644\n--- a/icicle/src/fields/extern.cu\n+++ b/icicle/src/fields/extern.cu.cc\n@@ -1,10 +1,10 @@\n-#include \"fields/field_config.cuh\"\n+#include \"fields/field_config.cu.h\"\n \n using namespace field_config;\n \n-#include \"utils/mont.cuh\"\n+#include \"utils/mont.cu.h\"\n #include \"utils/utils.h\"\n-#include \"gpu-utils/device_context.cuh\"\n+#include \"gpu-utils/device_context.cu.h\"\n \n extern \"C\" void CONCAT_EXPAND(FIELD, generate_scalars)(scalar_t* scalars, int size)\n {\ndiff --git a/icicle/src/fields/extern_extension.cu b/icicle/src/fields/extern_extension.cu.cc\nsimilarity index 83%\nrename from icicle/src/fields/extern_extension.cu\nrename to icicle/src/fields/extern_extension.cu.cc\nindex 8bc7bc5e..f8a1c822 100644\n--- a/icicle/src/fields/extern_extension.cu\n+++ b/icicle/src/fields/extern_extension.cu.cc\n@@ -1,10 +1,10 @@\n-#include \"fields/field_config.cuh\"\n+#include \"fields/field_config.cu.h\"\n \n using namespace field_config;\n \n-#include \"utils/mont.cuh\"\n+#include \"utils/mont.cu.h\"\n #include \"utils/utils.h\"\n-#include \"gpu-utils/device_context.cuh\"\n+#include \"gpu-utils/device_context.cu.h\"\n \n extern \"C\" void CONCAT_EXPAND(FIELD, extension_generate_scalars)(extension_t* scalars, int size)\n {\ndiff --git a/icicle/src/hash/keccak/extern.cu b/icicle/src/hash/keccak/extern.cu.cc\nsimilarity index 86%\nrename from icicle/src/hash/keccak/extern.cu\nrename to icicle/src/hash/keccak/extern.cu.cc\nindex b1e6d6aa..404ecac5 100644\n--- a/icicle/src/hash/keccak/extern.cu\n+++ b/icicle/src/hash/keccak/extern.cu.cc\n@@ -1,11 +1,11 @@\n #include \"utils/utils.h\"\n-#include \"gpu-utils/error_handler.cuh\"\n+#include \"gpu-utils/error_handler.cu.h\"\n \n-#include \"hash/hash.cuh\"\n-#include \"hash/keccak/keccak.cuh\"\n-#include \"keccak.cu\"\n-#include \"../../merkle-tree/merkle.cu\"\n-#include \"merkle-tree/merkle.cuh\"\n+#include \"hash/hash.cu.h\"\n+#include \"hash/keccak/keccak.cu.h\"\n+#include \"keccak.cu.cc\"\n+#include \"../../merkle-tree/merkle.cu.cc\"\n+#include \"merkle-tree/merkle.cu.h\"\n \n namespace keccak {\n   extern \"C\" cudaError_t\ndiff --git a/icicle/src/hash/keccak/keccak.cu b/icicle/src/hash/keccak/keccak.cu.cc\nsimilarity index 98%\nrename from icicle/src/hash/keccak/keccak.cu\nrename to icicle/src/hash/keccak/keccak.cu.cc\nindex e805bcf6..ca3e52ba 100644\n--- a/icicle/src/hash/keccak/keccak.cu\n+++ b/icicle/src/hash/keccak/keccak.cu.cc\n@@ -1,10 +1,10 @@\n #include <cstdint>\n-#include \"gpu-utils/device_context.cuh\"\n-#include \"gpu-utils/error_handler.cuh\"\n-#include \"gpu-utils/modifiers.cuh\"\n+#include \"gpu-utils/device_context.cu.h\"\n+#include \"gpu-utils/error_handler.cu.h\"\n+#include \"gpu-utils/modifiers.cu.h\"\n \n-#include \"hash/hash.cuh\"\n-#include \"hash/keccak/keccak.cuh\"\n+#include \"hash/hash.cu.h\"\n+#include \"hash/keccak/keccak.cu.h\"\n \n using namespace hash;\n \ndiff --git a/icicle/src/hash/keccak/test.cu b/icicle/src/hash/keccak/test.cu.cc\nsimilarity index 96%\nrename from icicle/src/hash/keccak/test.cu\nrename to icicle/src/hash/keccak/test.cu.cc\nindex 03293f1e..2aac59a4 100644\n--- a/icicle/src/hash/keccak/test.cu\n+++ b/icicle/src/hash/keccak/test.cu.cc\n@@ -1,5 +1,5 @@\n-#include \"gpu-utils/device_context.cuh\"\n-#include \"extern.cu\"\n+#include \"gpu-utils/device_context.cu.h\"\n+#include \"extern.cu.cc\"\n \n // #define DEBUG\n \ndiff --git a/icicle/src/hash/keccak/test_tree.cu b/icicle/src/hash/keccak/test_tree.cu.cc\nsimilarity index 96%\nrename from icicle/src/hash/keccak/test_tree.cu\nrename to icicle/src/hash/keccak/test_tree.cu.cc\nindex ed1de95f..b4efe1c2 100644\n--- a/icicle/src/hash/keccak/test_tree.cu\n+++ b/icicle/src/hash/keccak/test_tree.cu.cc\n@@ -1,6 +1,6 @@\n-#include \"gpu-utils/device_context.cuh\"\n-#include \"merkle-tree/merkle.cuh\"\n-#include \"extern.cu\"\n+#include \"gpu-utils/device_context.cu.h\"\n+#include \"merkle-tree/merkle.cu.h\"\n+#include \"extern.cu.cc\"\n \n #ifndef __CUDA_ARCH__\n #include <cassert>\ndiff --git a/icicle/src/merkle-tree/extern.cu b/icicle/src/merkle-tree/extern.cu.cc\nsimilarity index 75%\nrename from icicle/src/merkle-tree/extern.cu\nrename to icicle/src/merkle-tree/extern.cu.cc\nindex 060afe87..688a4366 100644\n--- a/icicle/src/merkle-tree/extern.cu\n+++ b/icicle/src/merkle-tree/extern.cu.cc\n@@ -1,12 +1,12 @@\n #include \"utils/utils.h\"\n \n-#include \"gpu-utils/error_handler.cuh\"\n-#include \"merkle-tree/merkle.cuh\"\n-#include \"merkle.cu\"\n+#include \"gpu-utils/error_handler.cu.h\"\n+#include \"merkle-tree/merkle.cu.h\"\n+#include \"merkle.cu.cc\"\n \n-#include \"hash/hash.cuh\"\n+#include \"hash/hash.cu.h\"\n \n-#include \"fields/field_config.cuh\"\n+#include \"fields/field_config.cu.h\"\n using namespace field_config;\n \n namespace merkle_tree {\ndiff --git a/icicle/src/merkle-tree/extern_mmcs.cu b/icicle/src/merkle-tree/extern_mmcs.cu.cc\nsimilarity index 71%\nrename from icicle/src/merkle-tree/extern_mmcs.cu\nrename to icicle/src/merkle-tree/extern_mmcs.cu.cc\nindex 3b6fc5cc..3c2ba192 100644\n--- a/icicle/src/merkle-tree/extern_mmcs.cu\n+++ b/icicle/src/merkle-tree/extern_mmcs.cu.cc\n@@ -1,13 +1,13 @@\n #include \"utils/utils.h\"\n \n-#include \"gpu-utils/error_handler.cuh\"\n-#include \"merkle-tree/merkle.cuh\"\n-#include \"matrix/matrix.cuh\"\n-#include \"mmcs.cu\"\n+#include \"gpu-utils/error_handler.cu.h\"\n+#include \"merkle-tree/merkle.cu.h\"\n+#include \"matrix/matrix.cu.h\"\n+#include \"mmcs.cu.cc\"\n \n-#include \"hash/hash.cuh\"\n+#include \"hash/hash.cu.h\"\n \n-#include \"fields/field_config.cuh\"\n+#include \"fields/field_config.cu.h\"\n using namespace field_config;\n \n using matrix::Matrix;\ndiff --git a/icicle/src/merkle-tree/merkle.cu b/icicle/src/merkle-tree/merkle.cu.cc\nsimilarity index 99%\nrename from icicle/src/merkle-tree/merkle.cu\nrename to icicle/src/merkle-tree/merkle.cu.cc\nindex 53f3b8f6..1f783ab3 100644\n--- a/icicle/src/merkle-tree/merkle.cu\n+++ b/icicle/src/merkle-tree/merkle.cu.cc\n@@ -1,5 +1,5 @@\n-#include \"hash/hash.cuh\"\n-#include \"merkle-tree/merkle.cuh\"\n+#include \"hash/hash.cu.h\"\n+#include \"merkle-tree/merkle.cu.h\"\n \n namespace merkle_tree {\n   /// Constructs merkle subtree without parallelization\ndiff --git a/icicle/src/merkle-tree/mmcs.cu b/icicle/src/merkle-tree/mmcs.cu.cc\nsimilarity index 99%\nrename from icicle/src/merkle-tree/mmcs.cu\nrename to icicle/src/merkle-tree/mmcs.cu.cc\nindex f7acbb65..1d695483 100644\n--- a/icicle/src/merkle-tree/mmcs.cu\n+++ b/icicle/src/merkle-tree/mmcs.cu.cc\n@@ -1,7 +1,7 @@\n-#include \"hash/hash.cuh\"\n-#include \"merkle-tree/merkle.cuh\"\n-#include \"matrix/matrix.cuh\"\n-#include \"vec_ops/vec_ops.cuh\"\n+#include \"hash/hash.cu.h\"\n+#include \"merkle-tree/merkle.cu.h\"\n+#include \"matrix/matrix.cu.h\"\n+#include \"vec_ops/vec_ops.cu.h\"\n \n #include <algorithm>\n \ndiff --git a/icicle/src/merkle-tree/tests/merkle/test.cu b/icicle/src/merkle-tree/tests/merkle/test.cu.cc\nsimilarity index 99%\nrename from icicle/src/merkle-tree/tests/merkle/test.cu\nrename to icicle/src/merkle-tree/tests/merkle/test.cu.cc\nindex 0fb34867..92401862 100644\n--- a/icicle/src/merkle-tree/tests/merkle/test.cu\n+++ b/icicle/src/merkle-tree/tests/merkle/test.cu.cc\n@@ -6,9 +6,9 @@\n #include <math.h>\n \n #define DEBUG\n-#include \"merkle-tree/merkle.cuh\"\n+#include \"merkle-tree/merkle.cu.h\"\n \n-#include \"poseidon/poseidon.cuh\"\n+#include \"poseidon/poseidon.cu.h\"\n \n #include \"api/bls12_381.h\"\n using namespace bls12_381;\ndiff --git a/icicle/src/merkle-tree/tests/merkle/test_poseidon2.cu b/icicle/src/merkle-tree/tests/merkle/test_poseidon2.cu.cc\nsimilarity index 98%\nrename from icicle/src/merkle-tree/tests/merkle/test_poseidon2.cu\nrename to icicle/src/merkle-tree/tests/merkle/test_poseidon2.cu.cc\nindex 7bd35c92..c06a1bd2 100644\n--- a/icicle/src/merkle-tree/tests/merkle/test_poseidon2.cu\n+++ b/icicle/src/merkle-tree/tests/merkle/test_poseidon2.cu.cc\n@@ -5,9 +5,9 @@\n #include <iostream>\n #include <math.h>\n \n-#include \"merkle-tree/merkle.cuh\"\n+#include \"merkle-tree/merkle.cu.h\"\n \n-#include \"poseidon2/poseidon2.cuh\"\n+#include \"poseidon2/poseidon2.cu.h\"\n \n #include \"api/babybear.h\"\n using namespace babybear;\ndiff --git a/icicle/src/merkle-tree/tests/mmcs/test_poseidon2.cu b/icicle/src/merkle-tree/tests/mmcs/test_poseidon2.cu.cc\nsimilarity index 98%\nrename from icicle/src/merkle-tree/tests/mmcs/test_poseidon2.cu\nrename to icicle/src/merkle-tree/tests/mmcs/test_poseidon2.cu.cc\nindex 9de240db..09434e63 100644\n--- a/icicle/src/merkle-tree/tests/mmcs/test_poseidon2.cu\n+++ b/icicle/src/merkle-tree/tests/mmcs/test_poseidon2.cu.cc\n@@ -5,9 +5,9 @@\n #include <iostream>\n #include <math.h>\n \n-#include \"merkle-tree/merkle.cuh\"\n+#include \"merkle-tree/merkle.cu.h\"\n \n-#include \"poseidon2/poseidon2.cuh\"\n+#include \"poseidon2/poseidon2.cu.h\"\n \n #include \"api/babybear.h\"\n using namespace babybear;\ndiff --git a/icicle/src/msm/extern.cu b/icicle/src/msm/extern.cu.cc\nsimilarity index 95%\nrename from icicle/src/msm/extern.cu\nrename to icicle/src/msm/extern.cu.cc\nindex 42bc31db..42e05533 100644\n--- a/icicle/src/msm/extern.cu\n+++ b/icicle/src/msm/extern.cu.cc\n@@ -1,10 +1,10 @@\n-#include \"curves/curve_config.cuh\"\n-#include \"fields/field_config.cuh\"\n+#include \"curves/curve_config.cu.h\"\n+#include \"fields/field_config.cu.h\"\n \n using namespace curve_config;\n using namespace field_config;\n \n-#include \"msm.cu\"\n+#include \"msm.cu.cc\"\n #include \"utils/utils.h\"\n \n namespace msm {\ndiff --git a/icicle/src/msm/extern_g2.cu b/icicle/src/msm/extern_g2.cu.cc\nsimilarity index 95%\nrename from icicle/src/msm/extern_g2.cu\nrename to icicle/src/msm/extern_g2.cu.cc\nindex 1a84c66e..1a0d07b4 100644\n--- a/icicle/src/msm/extern_g2.cu\n+++ b/icicle/src/msm/extern_g2.cu.cc\n@@ -1,10 +1,10 @@\n-#include \"curves/curve_config.cuh\"\n-#include \"fields/field_config.cuh\"\n+#include \"curves/curve_config.cu.h\"\n+#include \"fields/field_config.cu.h\"\n \n using namespace curve_config;\n using namespace field_config;\n \n-#include \"msm.cu\"\n+#include \"msm.cu.cc\"\n #include \"utils/utils.h\"\n \n namespace msm {\ndiff --git a/icicle/src/msm/msm.cu b/icicle/src/msm/msm.cu.cc\nsimilarity index 99%\nrename from icicle/src/msm/msm.cu\nrename to icicle/src/msm/msm.cu.cc\nindex 7b6f8851..6bb2139a 100644\n--- a/icicle/src/msm/msm.cu\n+++ b/icicle/src/msm/msm.cu.cc\n@@ -1,4 +1,4 @@\n-#include \"msm/msm.cuh\"\n+#include \"msm/msm.cu.h\"\n \n #include <cooperative_groups.h>\n #include <cub/device/device_radix_sort.cuh>\n@@ -10,11 +10,11 @@\n #include <stdexcept>\n #include <vector>\n \n-#include \"curves/affine.cuh\"\n-#include \"curves/projective.cuh\"\n-#include \"fields/field.cuh\"\n-#include \"gpu-utils/error_handler.cuh\"\n-#include \"utils/mont.cuh\"\n+#include \"curves/affine.cu.h\"\n+#include \"curves/projective.cu.h\"\n+#include \"fields/field.cu.h\"\n+#include \"gpu-utils/error_handler.cu.h\"\n+#include \"utils/mont.cu.h\"\n \n namespace msm {\n \ndiff --git a/icicle/src/msm/tests/msm_test.cu b/icicle/src/msm/tests/msm_test.cu.cc\nsimilarity index 97%\nrename from icicle/src/msm/tests/msm_test.cu\nrename to icicle/src/msm/tests/msm_test.cu.cc\nindex 005a00c5..81c82647 100644\n--- a/icicle/src/msm/tests/msm_test.cu\n+++ b/icicle/src/msm/tests/msm_test.cu.cc\n@@ -1,19 +1,19 @@\n #include \"fields/id.h\"\n // #define FIELD_ID 2\n #define CURVE_ID 3\n-#include \"curves/curve_config.cuh\"\n-// #include \"fields/field_config.cuh\"\n+#include \"curves/curve_config.cu.h\"\n+// #include \"fields/field_config.cu.h\"\n \n-#include \"msm.cu\"\n+#include \"msm.cu.cc\"\n \n #include <chrono>\n #include <iostream>\n #include <vector>\n \n-#include \"curves/params/bn254.cuh\"\n-#include \"fields/field.cuh\"\n-#include \"curves/projective.cuh\"\n-#include \"gpu-utils/device_context.cuh\"\n+#include \"curves/params/bn254.cu.h\"\n+#include \"fields/field.cu.h\"\n+#include \"curves/projective.cu.h\"\n+#include \"gpu-utils/device_context.cu.h\"\n \n // using namespace bn254;\n \ndiff --git a/icicle/src/ntt/extern.cu b/icicle/src/ntt/extern.cu.cc\nsimilarity index 95%\nrename from icicle/src/ntt/extern.cu\nrename to icicle/src/ntt/extern.cu.cc\nindex e64da6e3..50a788ca 100644\n--- a/icicle/src/ntt/extern.cu\n+++ b/icicle/src/ntt/extern.cu.cc\n@@ -1,10 +1,10 @@\n-#include \"fields/field_config.cuh\"\n+#include \"fields/field_config.cu.h\"\n \n using namespace field_config;\n \n-#include \"ntt.cu\"\n+#include \"ntt.cu.cc\"\n \n-#include \"gpu-utils/device_context.cuh\"\n+#include \"gpu-utils/device_context.cu.h\"\n #include \"utils/utils.h\"\n \n namespace ntt {\ndiff --git a/icicle/src/ntt/extern_ecntt.cu b/icicle/src/ntt/extern_ecntt.cu.cc\nsimilarity index 85%\nrename from icicle/src/ntt/extern_ecntt.cu\nrename to icicle/src/ntt/extern_ecntt.cu.cc\nindex 72a4e42b..5cc0a64f 100644\n--- a/icicle/src/ntt/extern_ecntt.cu\n+++ b/icicle/src/ntt/extern_ecntt.cu.cc\n@@ -1,12 +1,12 @@\n-#include \"curves/curve_config.cuh\"\n-#include \"fields/field_config.cuh\"\n+#include \"curves/curve_config.cu.h\"\n+#include \"fields/field_config.cu.h\"\n \n using namespace curve_config;\n using namespace field_config;\n \n-#include \"ntt.cu\"\n+#include \"ntt.cu.cc\"\n \n-#include \"gpu-utils/device_context.cuh\"\n+#include \"gpu-utils/device_context.cu.h\"\n #include \"utils/utils.h\"\n \n namespace ntt {\ndiff --git a/icicle/src/ntt/extern_extension.cu b/icicle/src/ntt/extern_extension.cu.cc\nsimilarity index 88%\nrename from icicle/src/ntt/extern_extension.cu\nrename to icicle/src/ntt/extern_extension.cu.cc\nindex 5def1ddd..0c48adcc 100644\n--- a/icicle/src/ntt/extern_extension.cu\n+++ b/icicle/src/ntt/extern_extension.cu.cc\n@@ -1,10 +1,10 @@\n-#include \"fields/field_config.cuh\"\n+#include \"fields/field_config.cu.h\"\n \n using namespace field_config;\n \n-#include \"ntt.cu\"\n+#include \"ntt.cu.cc\"\n \n-#include \"gpu-utils/device_context.cuh\"\n+#include \"gpu-utils/device_context.cu.h\"\n #include \"utils/utils.h\"\n \n namespace ntt {\ndiff --git a/icicle/src/ntt/kernel_ntt.cu b/icicle/src/ntt/kernel_ntt.cu.cc\nsimilarity index 99%\nrename from icicle/src/ntt/kernel_ntt.cu\nrename to icicle/src/ntt/kernel_ntt.cu.cc\nindex 46e97243..3386fea9 100644\n--- a/icicle/src/ntt/kernel_ntt.cu\n+++ b/icicle/src/ntt/kernel_ntt.cu.cc\n@@ -1,10 +1,10 @@\n-#include \"fields/field_config.cuh\"\n+#include \"fields/field_config.cu.h\"\n \n using namespace field_config;\n \n-#include \"thread_ntt.cu\"\n-#include \"gpu-utils/sharedmem.cuh\"\n-#include \"ntt/ntt.cuh\" // for ntt::Ordering\n+#include \"thread_ntt.cu.cc\"\n+#include \"gpu-utils/sharedmem.cu.h\"\n+#include \"ntt/ntt.cu.h\" // for ntt::Ordering\n \n namespace mxntt {\n \ndiff --git a/icicle/src/ntt/ntt.cu b/icicle/src/ntt/ntt.cu.cc\nsimilarity index 99%\nrename from icicle/src/ntt/ntt.cu\nrename to icicle/src/ntt/ntt.cu.cc\nindex 0afe4a29..a82522a0 100644\n--- a/icicle/src/ntt/ntt.cu\n+++ b/icicle/src/ntt/ntt.cu.cc\n@@ -1,22 +1,22 @@\n-#include \"fields/field_config.cuh\"\n+#include \"fields/field_config.cu.h\"\n \n using namespace field_config;\n \n-#include \"ntt/ntt.cuh\"\n+#include \"ntt/ntt.cu.h\"\n \n #include <unordered_map>\n #include <vector>\n #include <type_traits>\n \n-#include \"gpu-utils/sharedmem.cuh\"\n-#include \"utils/utils_kernels.cuh\"\n+#include \"gpu-utils/sharedmem.cu.h\"\n+#include \"utils/utils_kernels.cu.h\"\n #include \"utils/utils.h\"\n-#include \"ntt/ntt_impl.cuh\"\n+#include \"ntt/ntt_impl.cu.h\"\n \n #include <mutex>\n \n #ifdef CURVE_ID\n-#include \"curves/curve_config.cuh\"\n+#include \"curves/curve_config.cu.h\"\n using namespace curve_config;\n #define IS_ECNTT std::is_same_v<E, projective_t>\n #else\ndiff --git a/icicle/src/ntt/tests/verification.cu b/icicle/src/ntt/tests/verification.cu.cc\nsimilarity index 97%\nrename from icicle/src/ntt/tests/verification.cu\nrename to icicle/src/ntt/tests/verification.cu.cc\nindex 21e143a0..ddf846b9 100644\n--- a/icicle/src/ntt/tests/verification.cu\n+++ b/icicle/src/ntt/tests/verification.cu.cc\n@@ -3,23 +3,23 @@\n \n #ifdef ECNTT\n #define CURVE_ID BN254\n-#include \"curves/curve_config.cuh\"\n+#include \"curves/curve_config.cu.h\"\n typedef field_config::scalar_t test_scalar;\n typedef curve_config::projective_t test_data;\n #else\n-#include \"fields/field_config.cuh\"\n+#include \"fields/field_config.cu.h\"\n typedef field_config::scalar_t test_scalar;\n typedef field_config::scalar_t test_data;\n #endif\n \n-#include \"fields/field.cuh\"\n-#include \"curves/projective.cuh\"\n+#include \"fields/field.cu.h\"\n+#include \"curves/projective.cu.h\"\n #include <chrono>\n #include <iostream>\n #include <vector>\n \n-#include \"ntt.cu\"\n-#include \"kernel_ntt.cu\"\n+#include \"ntt.cu.cc\"\n+#include \"kernel_ntt.cu.cc\"\n #include <memory>\n \n void random_samples(test_data* res, uint32_t count)\ndiff --git a/icicle/src/ntt/thread_ntt.cu b/icicle/src/ntt/thread_ntt.cu.cc\nsimilarity index 99%\nrename from icicle/src/ntt/thread_ntt.cu\nrename to icicle/src/ntt/thread_ntt.cu.cc\nindex 8fd2764d..dd7fd7b6 100644\n--- a/icicle/src/ntt/thread_ntt.cu\n+++ b/icicle/src/ntt/thread_ntt.cu.cc\n@@ -4,7 +4,7 @@\n \n #include <stdio.h>\n #include <stdint.h>\n-#include \"gpu-utils/modifiers.cuh\"\n+#include \"gpu-utils/modifiers.cu.h\"\n \n struct stage_metadata {\n   uint32_t th_stride;\ndiff --git a/icicle/src/polynomials/cuda_backend/kernels.cuh b/icicle/src/polynomials/cuda_backend/kernels.cu.h\nsimilarity index 96%\nrename from icicle/src/polynomials/cuda_backend/kernels.cuh\nrename to icicle/src/polynomials/cuda_backend/kernels.cu.h\nindex 2d1032b6..546889ba 100644\n--- a/icicle/src/polynomials/cuda_backend/kernels.cuh\n+++ b/icicle/src/polynomials/cuda_backend/kernels.cu.h\n@@ -1,6 +1,6 @@\n #pragma once\n #include \"stdint.h\"\n-#include \"../../../src/vec_ops/vec_ops.cu\" // TODO Yuval: avoid this\n+#include \"../../../src/vec_ops/vec_ops.cu.cc\" // TODO Yuval: avoid this\n \n namespace polynomials {\n   using namespace vec_ops;\ndiff --git a/icicle/src/polynomials/cuda_backend/polynomial_cuda_backend.cu b/icicle/src/polynomials/cuda_backend/polynomial_cuda_backend.cu.cc\nsimilarity index 99%\nrename from icicle/src/polynomials/cuda_backend/polynomial_cuda_backend.cu\nrename to icicle/src/polynomials/cuda_backend/polynomial_cuda_backend.cu.cc\nindex 1b9d5d14..d3fef443 100644\n--- a/icicle/src/polynomials/cuda_backend/polynomial_cuda_backend.cu\n+++ b/icicle/src/polynomials/cuda_backend/polynomial_cuda_backend.cu.cc\n@@ -1,11 +1,11 @@\n \n #include \"polynomials/polynomials.h\"\n-#include \"polynomials/cuda_backend/polynomial_cuda_backend.cuh\"\n+#include \"polynomials/cuda_backend/polynomial_cuda_backend.cu.h\"\n \n-#include \"gpu-utils/device_context.cuh\"\n+#include \"gpu-utils/device_context.cu.h\"\n #include \"cuda_runtime.h\"\n-#include \"ntt/ntt.cuh\"\n-#include \"kernels.cuh\"\n+#include \"ntt/ntt.cu.h\"\n+#include \"kernels.cu.h\"\n \n using device_context::DeviceContext;\n \ndiff --git a/icicle/src/polynomials/polynomials.cu b/icicle/src/polynomials/polynomials.cu.cc\nsimilarity index 100%\nrename from icicle/src/polynomials/polynomials.cu\nrename to icicle/src/polynomials/polynomials.cu.cc\ndiff --git a/icicle/src/polynomials/polynomials_c_api.cu b/icicle/src/polynomials/polynomials_c_api.cu.cc\nsimilarity index 99%\nrename from icicle/src/polynomials/polynomials_c_api.cu\nrename to icicle/src/polynomials/polynomials_c_api.cu.cc\nindex 8672fbd9..b9066c4f 100644\n--- a/icicle/src/polynomials/polynomials_c_api.cu\n+++ b/icicle/src/polynomials/polynomials_c_api.cu.cc\n@@ -1,8 +1,8 @@\n #include \"polynomials/polynomials.h\"\n-#include \"fields/field_config.cuh\"\n+#include \"fields/field_config.cu.h\"\n #include \"utils/utils.h\"\n #include \"utils/integrity_pointer.h\"\n-#include \"polynomials/cuda_backend/polynomial_cuda_backend.cuh\"\n+#include \"polynomials/cuda_backend/polynomial_cuda_backend.cu.h\"\n \n namespace polynomials {\n   extern \"C\" {\ndiff --git a/icicle/src/poseidon/constants.cu b/icicle/src/poseidon/constants.cu.cc\nsimilarity index 98%\nrename from icicle/src/poseidon/constants.cu\nrename to icicle/src/poseidon/constants.cu.cc\nindex bed5a9be..95a51673 100644\n--- a/icicle/src/poseidon/constants.cu\n+++ b/icicle/src/poseidon/constants.cu.cc\n@@ -1,5 +1,5 @@\n-#include \"poseidon/constants.cuh\"\n-#include \"gpu-utils/device_context.cuh\"\n+#include \"poseidon/constants.cu.h\"\n+#include \"gpu-utils/device_context.cu.h\"\n \n /// These are pre-calculated constants for different curves\n #include \"fields/id.h\"\ndiff --git a/icicle/src/poseidon/extern.cu b/icicle/src/poseidon/extern.cu.cc\nsimilarity index 92%\nrename from icicle/src/poseidon/extern.cu\nrename to icicle/src/poseidon/extern.cu.cc\nindex 16145bb2..b10db1d8 100644\n--- a/icicle/src/poseidon/extern.cu\n+++ b/icicle/src/poseidon/extern.cu.cc\n@@ -1,11 +1,11 @@\n-#include \"fields/field_config.cuh\"\n+#include \"fields/field_config.cu.h\"\n \n using namespace field_config;\n \n-#include \"poseidon/poseidon.cuh\"\n-#include \"constants.cu\"\n+#include \"poseidon/poseidon.cu.h\"\n+#include \"constants.cu.cc\"\n \n-#include \"gpu-utils/device_context.cuh\"\n+#include \"gpu-utils/device_context.cu.h\"\n #include \"utils/utils.h\"\n \n namespace poseidon {\ndiff --git a/icicle/src/poseidon/test.cu b/icicle/src/poseidon/test.cu.cc\nsimilarity index 99%\nrename from icicle/src/poseidon/test.cu\nrename to icicle/src/poseidon/test.cu.cc\nindex 461b57ae..4384da3d 100644\n--- a/icicle/src/poseidon/test.cu\n+++ b/icicle/src/poseidon/test.cu.cc\n@@ -1,9 +1,9 @@\n // #define DEBUG\n \n-#include \"curves/curve_config.cuh\"\n+#include \"curves/curve_config.cu.h\"\n using namespace curve_config;\n \n-#include \"gpu-utils/device_context.cuh\"\n+#include \"gpu-utils/device_context.cu.h\"\n \n #ifndef __CUDA_ARCH__\n #include <cassert>\n@@ -14,7 +14,7 @@ using namespace curve_config;\n #include \"api/bls12_381.h\"\n using namespace bls12_381;\n \n-#include \"poseidon/poseidon.cuh\"\n+#include \"poseidon/poseidon.cu.h\"\n using namespace poseidon;\n \n #define A 2\ndiff --git a/icicle/src/poseidon/test_m31.cu b/icicle/src/poseidon/test_m31.cu.cc\nsimilarity index 94%\nrename from icicle/src/poseidon/test_m31.cu\nrename to icicle/src/poseidon/test_m31.cu.cc\nindex 7f6bd7ce..cbf22de7 100644\n--- a/icicle/src/poseidon/test_m31.cu\n+++ b/icicle/src/poseidon/test_m31.cu.cc\n@@ -1,10 +1,10 @@\n // #define DEBUG\n \n-#include \"fields/field_config.cuh\"\n+#include \"fields/field_config.cu.h\"\n using namespace field_config;\n \n-#include \"gpu-utils/device_context.cuh\"\n-#include \"poseidon/poseidon.cuh\"\n+#include \"gpu-utils/device_context.cu.h\"\n+#include \"poseidon/poseidon.cu.h\"\n \n #ifndef __CUDA_ARCH__\n #include <cassert>\ndiff --git a/icicle/src/poseidon2/constants.cu b/icicle/src/poseidon2/constants.cu.cc\nsimilarity index 98%\nrename from icicle/src/poseidon2/constants.cu\nrename to icicle/src/poseidon2/constants.cu.cc\nindex 3fa157d2..c30fab04 100644\n--- a/icicle/src/poseidon2/constants.cu\n+++ b/icicle/src/poseidon2/constants.cu.cc\n@@ -1,5 +1,5 @@\n-#include \"poseidon2/constants.cuh\"\n-#include \"gpu-utils/device_context.cuh\"\n+#include \"poseidon2/constants.cu.h\"\n+#include \"gpu-utils/device_context.cu.h\"\n \n /// These are pre-calculated constants for different curves\n #include \"fields/id.h\"\ndiff --git a/icicle/src/poseidon2/extern.cu b/icicle/src/poseidon2/extern.cu.cc\nsimilarity index 92%\nrename from icicle/src/poseidon2/extern.cu\nrename to icicle/src/poseidon2/extern.cu.cc\nindex 7741d29a..d2397255 100644\n--- a/icicle/src/poseidon2/extern.cu\n+++ b/icicle/src/poseidon2/extern.cu.cc\n@@ -1,11 +1,11 @@\n #include \"utils/utils.h\"\n \n-#include \"fields/field_config.cuh\"\n+#include \"fields/field_config.cu.h\"\n using namespace field_config;\n \n-#include \"gpu-utils/error_handler.cuh\"\n-#include \"poseidon2/poseidon2.cuh\"\n-#include \"./constants.cu\"\n+#include \"gpu-utils/error_handler.cu.h\"\n+#include \"poseidon2/poseidon2.cu.h\"\n+#include \"./constants.cu.cc\"\n \n namespace poseidon2 {\n   template class Poseidon2<scalar_t>;\ndiff --git a/icicle/src/poseidon2/test.cu b/icicle/src/poseidon2/test.cu.cc\nsimilarity index 99%\nrename from icicle/src/poseidon2/test.cu\nrename to icicle/src/poseidon2/test.cu.cc\nindex c98bfd63..91e8da8c 100644\n--- a/icicle/src/poseidon2/test.cu\n+++ b/icicle/src/poseidon2/test.cu.cc\n@@ -1,4 +1,4 @@\n-#include \"gpu-utils/device_context.cuh\"\n+#include \"gpu-utils/device_context.cu.h\"\n \n #ifndef __CUDA_ARCH__\n #include <cassert>\n@@ -6,7 +6,7 @@\n #include <fstream>\n #include <iostream>\n \n-#include \"poseidon2/poseidon2.cuh\"\n+#include \"poseidon2/poseidon2.cu.h\"\n using namespace poseidon2;\n \n #include \"api/bn254.h\"\ndiff --git a/icicle/src/poseidon2/test_m31.cu b/icicle/src/poseidon2/test_m31.cu.cc\nsimilarity index 95%\nrename from icicle/src/poseidon2/test_m31.cu\nrename to icicle/src/poseidon2/test_m31.cu.cc\nindex 378d281f..ed77de1f 100644\n--- a/icicle/src/poseidon2/test_m31.cu\n+++ b/icicle/src/poseidon2/test_m31.cu.cc\n@@ -1,4 +1,4 @@\n-#include \"gpu-utils/device_context.cuh\"\n+#include \"gpu-utils/device_context.cu.h\"\n \n #ifndef __CUDA_ARCH__\n #include <cassert>\n@@ -6,13 +6,13 @@\n #include <fstream>\n #include <iostream>\n \n-#include \"poseidon2/poseidon2.cuh\"\n+#include \"poseidon2/poseidon2.cu.h\"\n using namespace poseidon2;\n \n-#include \"fields/field_config.cuh\"\n+#include \"fields/field_config.cu.h\"\n using namespace field_config;\n \n-#include \"hash/hash.cuh\"\n+#include \"hash/hash.cu.h\"\n \n #define T 16\n \ndiff --git a/icicle/src/vec_ops/extern.cu b/icicle/src/vec_ops/extern.cu.cc\nsimilarity index 97%\nrename from icicle/src/vec_ops/extern.cu\nrename to icicle/src/vec_ops/extern.cu.cc\nindex 78a7467c..f715fd83 100644\n--- a/icicle/src/vec_ops/extern.cu\n+++ b/icicle/src/vec_ops/extern.cu.cc\n@@ -1,9 +1,9 @@\n-#include \"fields/field_config.cuh\"\n+#include \"fields/field_config.cu.h\"\n \n using namespace field_config;\n \n #include \"utils/utils.h\"\n-#include \"vec_ops.cu\"\n+#include \"vec_ops.cu.cc\"\n \n namespace vec_ops {\n   /**\ndiff --git a/icicle/src/vec_ops/extern_extension.cu b/icicle/src/vec_ops/extern_extension.cu.cc\nsimilarity index 97%\nrename from icicle/src/vec_ops/extern_extension.cu\nrename to icicle/src/vec_ops/extern_extension.cu.cc\nindex 80653c79..30d12147 100644\n--- a/icicle/src/vec_ops/extern_extension.cu\n+++ b/icicle/src/vec_ops/extern_extension.cu.cc\n@@ -1,9 +1,9 @@\n-#include \"fields/field_config.cuh\"\n+#include \"fields/field_config.cu.h\"\n \n using namespace field_config;\n \n #include \"utils/utils.h\"\n-#include \"vec_ops.cu\"\n+#include \"vec_ops.cu.cc\"\n \n namespace vec_ops {\n   /**\ndiff --git a/icicle/src/vec_ops/vec_ops.cu b/icicle/src/vec_ops/vec_ops.cu.cc\nsimilarity index 98%\nrename from icicle/src/vec_ops/vec_ops.cu\nrename to icicle/src/vec_ops/vec_ops.cu.cc\nindex 9cfa85e6..c813dafa 100644\n--- a/icicle/src/vec_ops/vec_ops.cu\n+++ b/icicle/src/vec_ops/vec_ops.cu.cc\n@@ -1,9 +1,9 @@\n #include <cuda.h>\n #include <stdexcept>\n \n-#include \"vec_ops/vec_ops.cuh\"\n-#include \"gpu-utils/device_context.cuh\"\n-#include \"utils/mont.cuh\"\n+#include \"vec_ops/vec_ops.cu.h\"\n+#include \"gpu-utils/device_context.cu.h\"\n+#include \"utils/mont.cu.h\"\n \n namespace vec_ops {\n \ndiff --git a/icicle/tests/curve_test.cu b/icicle/tests/curve_test.cu.cc\nsimilarity index 99%\nrename from icicle/tests/curve_test.cu\nrename to icicle/tests/curve_test.cu.cc\nindex 087b3e4b..6b122722 100644\n--- a/icicle/tests/curve_test.cu\n+++ b/icicle/tests/curve_test.cu.cc\n@@ -1,5 +1,5 @@\n-#include \"utils/test_functions.cuh\"\n-#include \"curves/curve_config.cuh\"\n+#include \"utils/test_functions.cu.h\"\n+#include \"curves/curve_config.cu.h\"\n #include <cuda_runtime.h>\n #include <gtest/gtest.h>\n #include <iostream>\ndiff --git a/icicle/tests/device_error_test.cu b/icicle/tests/device_error_test.cu.cc\nsimilarity index 97%\nrename from icicle/tests/device_error_test.cu\nrename to icicle/tests/device_error_test.cu.cc\nindex 9071f883..44b3a0bf 100644\n--- a/icicle/tests/device_error_test.cu\n+++ b/icicle/tests/device_error_test.cu.cc\n@@ -1,4 +1,4 @@\n-#include \"gpu-utils/error_handler.cuh\" // Include your error handling header file\n+#include \"gpu-utils/error_handler.cu.h\" // Include your error handling header file\n #include <gtest/gtest.h>\n \n __global__ void a_kernel_with_conditional_sticky_error(bool is_failing)\ndiff --git a/icicle/tests/error_handler_test.cu b/icicle/tests/error_handler_test.cu.cc\nsimilarity index 94%\nrename from icicle/tests/error_handler_test.cu\nrename to icicle/tests/error_handler_test.cu.cc\nindex 64023694..5a88865d 100644\n--- a/icicle/tests/error_handler_test.cu\n+++ b/icicle/tests/error_handler_test.cu.cc\n@@ -1,4 +1,4 @@\n-#include \"gpu-utils/error_handler.cuh\" // Include your error handling header file\n+#include \"gpu-utils/error_handler.cu.h\" // Include your error handling header file\n #include <gtest/gtest.h>\n \n class IcicleErrorTest : public ::testing::Test\ndiff --git a/icicle/tests/field_test.cu b/icicle/tests/field_test.cu.cc\nsimilarity index 98%\nrename from icicle/tests/field_test.cu\nrename to icicle/tests/field_test.cu.cc\nindex 704c3986..ed30c3e5 100644\n--- a/icicle/tests/field_test.cu\n+++ b/icicle/tests/field_test.cu.cc\n@@ -1,5 +1,5 @@\n-#include \"utils/test_functions.cuh\"\n-#include \"fields/field_config.cuh\"\n+#include \"utils/test_functions.cu.h\"\n+#include \"fields/field_config.cu.h\"\n #include <cuda_runtime.h>\n #include <gtest/gtest.h>\n #include <iostream>\ndiff --git a/icicle/tests/polynomial_test.cu b/icicle/tests/polynomial_test.cu.cc\nsimilarity index 99%\nrename from icicle/tests/polynomial_test.cu\nrename to icicle/tests/polynomial_test.cu.cc\nindex 9b433fa1..55c9942e 100644\n--- a/icicle/tests/polynomial_test.cu\n+++ b/icicle/tests/polynomial_test.cu.cc\n@@ -5,10 +5,10 @@\n #include <list>\n \n #include \"polynomials/polynomials.h\"\n-#include \"polynomials/cuda_backend/polynomial_cuda_backend.cuh\"\n+#include \"polynomials/cuda_backend/polynomial_cuda_backend.cu.h\"\n \n-#include \"ntt/ntt.cuh\"\n-#include \"gpu-utils/device_context.cuh\"\n+#include \"ntt/ntt.cu.h\"\n+#include \"gpu-utils/device_context.cu.h\"\n \n /*******************************************/\n \n@@ -516,8 +516,8 @@ TEST_F(PolynomialTest, slicing)\n }\n \n #ifdef CURVE\n-#include \"msm/msm.cuh\"\n-#include \"curves/curve_config.cuh\"\n+#include \"msm/msm.cu.h\"\n+#include \"curves/curve_config.cu.h\"\n using curve_config::affine_t;\n using curve_config::projective_t;\n \ndiff --git a/icicle/tests/runner.cu b/icicle/tests/runner.cu.cc\nsimilarity index 72%\nrename from icicle/tests/runner.cu\nrename to icicle/tests/runner.cu.cc\nindex 330b82c5..5222ba34 100644\n--- a/icicle/tests/runner.cu\n+++ b/icicle/tests/runner.cu.cc\n@@ -3,14 +3,14 @@\n #include <iostream>\n \n // include list of test files\n-#include \"field_test.cu\"\n+#include \"field_test.cu.cc\"\n #ifdef CURVE_ID\n-#include \"curve_test.cu\"\n+#include \"curve_test.cu.cc\"\n #endif\n-#include \"error_handler_test.cu\"\n+#include \"error_handler_test.cu.cc\"\n \n // Ensure the device_error_test.cu is last to prevent aborting mid-test run\n-#include \"device_error_test.cu\"\n+#include \"device_error_test.cu.cc\"\n \n int main(int argc, char** argv)\n {\n"
  },
  {
    "path": "third_party/icicle/separate_hash_config.patch",
    "content": "diff --git a/icicle/include/hash/hash.cu.h b/icicle/include/hash/hash.cu.h\nindex 7ca414cc..85693c9f 100644\n--- a/icicle/include/hash/hash.cu.h\n+++ b/icicle/include/hash/hash.cu.h\n@@ -2,8 +2,8 @@\n #ifndef HASH_H\n #define HASH_H\n\n-#include \"gpu-utils/device_context.cu.h\"\n #include \"gpu-utils/error_handler.cu.h\"\n+#include \"hash/hash_config.h\"\n #include \"matrix/matrix.cu.h\"\n #include <cassert>\n\n@@ -14,22 +14,6 @@ using matrix::Matrix;\n  * Includes classes and methods for describing hash functions.\n  */\n namespace hash {\n-\n-  /**\n-   * @struct HashConfig\n-   * Encodes hash operations parameters.\n-   */\n-  struct HashConfig {\n-    device_context::DeviceContext ctx; /**< Details related to the device such as its id and stream id. */\n-    bool are_inputs_on_device; /**< True if inputs are on device and false if they're on host. Default value: false. */\n-    bool\n-      are_outputs_on_device; /**< True if outputs are on device and false if they're on host. Default value: false. */\n-    bool is_async; /**< Whether to run the hash operations asynchronously. If set to `true`, the functions will be\n-                    *   non-blocking and you'd need to synchronize it explicitly by running\n-                    *   `cudaStreamSynchronize` or `cudaDeviceSynchronize`. If set to false,\n-                    *   functions will block the current CPU thread. */\n-  };\n-\n   /**\n    * A function that returns the default value of [HashConfig](@ref HashConfig) for the [Hasher](@ref\n    * Hasher) class.\n@@ -171,4 +155,4 @@ namespace hash {\n   };\n } // namespace hash\n\n-#endif\n+#endif\ndiff --git a/icicle/include/hash/hash_config.h b/icicle/include/hash/hash_config.h\nnew file mode 100644\nindex 00000000..2f5b05c7\n--- /dev/null\n+++ b/icicle/include/hash/hash_config.h\n@@ -0,0 +1,23 @@\n+#ifndef HASH_CONFIG_H\n+#define HASH_CONFIG_H\n+\n+#include \"gpu-utils/device_context.cu.h\"\n+\n+namespace hash {\n+  /**\n+   * @struct HashConfig\n+   * Encodes hash operations parameters.\n+   */\n+  struct HashConfig {\n+    device_context::DeviceContext ctx; /**< Details related to the device such as its id and stream id. */\n+    bool are_inputs_on_device; /**< True if inputs are on device and false if they're on host. Default value: false. */\n+    bool\n+      are_outputs_on_device; /**< True if outputs are on device and false if they're on host. Default value: false. */\n+    bool is_async; /**< Whether to run the hash operations asynchronously. If set to `true`, the functions will be\n+                    *   non-blocking and you'd need to synchronize it explicitly by running\n+                    *   `cudaStreamSynchronize` or `cudaDeviceSynchronize`. If set to false,\n+                    *   functions will block the current CPU thread. */\n+  };\n+} // namespace hash\n+\n+#endif // HASH_CONFIG_H\n"
  },
  {
    "path": "third_party/icicle/separate_merkle_tree_config.patch",
    "content": "diff --git a/icicle/include/merkle-tree/merkle.cu.h b/icicle/include/merkle-tree/merkle.cu.h\nindex d0a020db..2be4e4d0 100644\n--- a/icicle/include/merkle-tree/merkle.cu.h\n+++ b/icicle/include/merkle-tree/merkle.cu.h\n@@ -2,7 +2,7 @@\n #ifndef MERKLE_H\n #define MERKLE_H\n\n-#include \"gpu-utils/device_context.cu.h\"\n+#include \"merkle-tree/merkle_tree_config.h\"\n #include \"gpu-utils/error_handler.cu.h\"\n #include \"utils/utils.h\"\n #include \"hash/hash.cu.h\"\n@@ -57,27 +57,6 @@ namespace merkle_tree {\n     return height;\n   }\n\n-  /**\n-   * @struct TreeBuilderConfig\n-   * Struct that encodes various Tree builder parameters.\n-   */\n-  struct TreeBuilderConfig {\n-    device_context::DeviceContext ctx; /**< Details related to the device such as its id and stream id. */\n-    unsigned int arity;\n-    unsigned int\n-      keep_rows; /**< How many rows of the Merkle tree rows should be written to output. '0' means all of them */\n-    unsigned int\n-      digest_elements;         /** @param digest_elements the size of output for each bottom layer hash and compression.\n-                                *  Will also be equal to the size of the root of the tree. Default value 1 */\n-    bool are_inputs_on_device; /**< True if inputs are on device and false if they're on host. Default value: false. */\n-    bool\n-      are_outputs_on_device; /**< True if outputs are on device and false if they're on host. Default value: false. */\n-    bool is_async; /**< Whether to run the tree builder asynchronously. If set to `true`, the build_merkle_tree\n-                    *   function will be non-blocking and you'd need to synchronize it explicitly by running\n-                    *   `cudaStreamSynchronize` or `cudaDeviceSynchronize`. If set to false, the\n-                    *   function will block the current CPU thread. */\n-  };\n-\n   static TreeBuilderConfig\n   default_merkle_config(const device_context::DeviceContext& ctx = device_context::get_default_device_context())\n   {\ndiff --git a/icicle/include/merkle-tree/merkle_tree_config.h b/icicle/include/merkle-tree/merkle_tree_config.h\nnew file mode 100644\nindex 00000000..bdccb892\n--- /dev/null\n+++ b/icicle/include/merkle-tree/merkle_tree_config.h\n@@ -0,0 +1,29 @@\n+#ifndef MERKLE_TREE_CONFIG_H\n+#define MERKLE_TREE_CONFIG_H\n+\n+#include \"gpu-utils/device_context.cu.h\"\n+\n+namespace merkle_tree {\n+  /**\n+   * @struct TreeBuilderConfig\n+   * Struct that encodes various Tree builder parameters.\n+   */\n+  struct TreeBuilderConfig {\n+    device_context::DeviceContext ctx; /**< Details related to the device such as its id and stream id. */\n+    unsigned int arity;\n+    unsigned int\n+      keep_rows; /**< How many rows of the Merkle tree rows should be written to output. '0' means all of them */\n+    unsigned int\n+      digest_elements;         /** @param digest_elements the size of output for each bottom layer hash and compression.\n+                                *  Will also be equal to the size of the root of the tree. Default value 1 */\n+    bool are_inputs_on_device; /**< True if inputs are on device and false if they're on host. Default value: false. */\n+    bool\n+      are_outputs_on_device; /**< True if outputs are on device and false if they're on host. Default value: false. */\n+    bool is_async; /**< Whether to run the tree builder asynchronously. If set to `true`, the build_merkle_tree\n+                    *   function will be non-blocking and you'd need to synchronize it explicitly by running\n+                    *   `cudaStreamSynchronize` or `cudaDeviceSynchronize`. If set to false, the\n+                    *   function will block the current CPU thread. */\n+  };\n+} // namespace merkle_tree\n+\n+#endif // MERKLE_TREE_CONFIG_H\n"
  },
  {
    "path": "third_party/icicle/separate_msm_config.patch",
    "content": "diff --git a/icicle/include/msm/msm.cu.h b/icicle/include/msm/msm.cu.h\nindex 95ee447d..c2f81621 100644\n--- a/icicle/include/msm/msm.cu.h\n+++ b/icicle/include/msm/msm.cu.h\n@@ -7,8 +7,8 @@\n #include \"curves/affine.cu.h\"\n #include \"curves/projective.cu.h\"\n #include \"fields/field.cu.h\"\n-#include \"gpu-utils/device_context.cu.h\"\n #include \"gpu-utils/error_handler.cu.h\"\n+#include \"msm/msm_config.h\"\n \n /**\n  * @namespace msm\n@@ -29,58 +29,6 @@\n  */\n namespace msm {\n \n-  /**\n-   * @struct MSMConfig\n-   * Struct that encodes MSM parameters to be passed into the [MSM](@ref MSM) function. The intended use of this struct\n-   * is to create it using [default_msm_config](@ref default_msm_config) function and then you'll hopefully only need to\n-   * change a small number of default values for each of your MSMs.\n-   */\n-  struct MSMConfig {\n-    device_context::DeviceContext ctx; /**< Details related to the device such as its id and stream id. */\n-    int points_size;         /**< Number of points in the MSM. If a batch of MSMs needs to be computed, this should be\n-                              *   a number of different points. So, if each MSM re-uses the same set of points, this\n-                              *   variable is set equal to the MSM size. And if every MSM uses a distinct set of\n-                              *   points, it should be set to the product of MSM size and [batch_size](@ref\n-                              *   batch_size). Default value: 0 (meaning it's equal to the MSM size). */\n-    int precompute_factor;   /**< The number of extra points to pre-compute for each point. See the\n-                              *   [precompute_msm_points](@ref precompute_msm_points) function, `precompute_factor` passed\n-                              *   there needs to be equal to the one used here. Larger values decrease the\n-                              *   number of computations to make, on-line memory footprint, but increase the static\n-                              *   memory footprint. Default value: 1 (i.e. don't pre-compute). */\n-    int c;                   /**< \\f$ c \\f$ value, or \"window bitsize\" which is the main parameter of the \"bucket\n-                              *   method\" that we use to solve the MSM problem. As a rule of thumb, larger value\n-                              *   means more on-line memory footprint but also more parallelism and less computational\n-                              *   complexity (up to a certain point). Currently pre-computation is independent of\n-                              *   \\f$ c \\f$, however in the future value of \\f$ c \\f$ here and the one passed into the\n-                              *   [precompute_msm_points](@ref precompute_msm_points) function will need to be identical.\n-                              *    Default value: 0 (the optimal value of \\f$ c \\f$ is chosen automatically).  */\n-    int bitsize;             /**< Number of bits of the largest scalar. Typically equals the bitsize of scalar field,\n-                              *   but if a different (better) upper bound is known, it should be reflected in this\n-                              *   variable. Default value: 0 (set to the bitsize of scalar field). */\n-    int large_bucket_factor; /**< Variable that controls how sensitive the algorithm is to the buckets that occur\n-                              *   very frequently. Useful for efficient treatment of non-uniform distributions of\n-                              *   scalars and \"top windows\" with few bits. Can be set to 0 to disable separate\n-                              *   treatment of large buckets altogether. Default value: 10. */\n-    int batch_size;          /**< The number of MSMs to compute. Default value: 1. */\n-    bool are_scalars_on_device;       /**< True if scalars are on device and false if they're on host. Default value:\n-                                       *   false. */\n-    bool are_scalars_montgomery_form; /**< True if scalars are in Montgomery form and false otherwise. Default value:\n-                                       *   true. */\n-    bool are_points_on_device; /**< True if points are on device and false if they're on host. Default value: false. */\n-    bool are_points_montgomery_form; /**< True if coordinates of points are in Montgomery form and false otherwise.\n-                                      *   Default value: true. */\n-    bool are_results_on_device; /**< True if the results should be on device and false if they should be on host. If set\n-                                 *   to false, `is_async` won't take effect because a synchronization is needed to\n-                                 *   transfer results to the host. Default value: false. */\n-    bool is_big_triangle;       /**< Whether to do \"bucket accumulation\" serially. Decreases computational complexity\n-                                 *   but also greatly decreases parallelism, so only suitable for large batches of MSMs.\n-                                 *   Default value: false. */\n-    bool is_async;              /**< Whether to run the MSM asynchronously. If set to true, the MSM function will be\n-                                 *   non-blocking and you'd need to synchronize it explicitly by running\n-                                 *   `cudaStreamSynchronize` or `cudaDeviceSynchronize`. If set to false, the MSM\n-                                 *   function will block the current CPU thread. */\n-  };\n-\n   /**\n    * A function that returns the default value of [MSMConfig](@ref MSMConfig) for the [MSM](@ref MSM) function.\n    * @return Default value of [MSMConfig](@ref MSMConfig).\ndiff --git a/icicle/include/msm/msm_config.h b/icicle/include/msm/msm_config.h\nnew file mode 100644\nindex 00000000..be2a82b5\n--- /dev/null\n+++ b/icicle/include/msm/msm_config.h\n@@ -0,0 +1,60 @@\n+#ifndef MSM_CONFIG_H\n+#define MSM_CONFIG_H\n+\n+#include \"gpu-utils/device_context.cu.h\"\n+\n+namespace msm {\n+  /**\n+   * @struct MSMConfig\n+   * Struct that encodes MSM parameters to be passed into the [MSM](@ref MSM) function. The intended use of this struct\n+   * is to create it using [default_msm_config](@ref default_msm_config) function and then you'll hopefully only need to\n+   * change a small number of default values for each of your MSMs.\n+   */\n+  struct MSMConfig {\n+    device_context::DeviceContext ctx; /**< Details related to the device such as its id and stream id. */\n+    int points_size;         /**< Number of points in the MSM. If a batch of MSMs needs to be computed, this should be\n+                              *   a number of different points. So, if each MSM re-uses the same set of points, this\n+                              *   variable is set equal to the MSM size. And if every MSM uses a distinct set of\n+                              *   points, it should be set to the product of MSM size and [batch_size](@ref\n+                              *   batch_size). Default value: 0 (meaning it's equal to the MSM size). */\n+    int precompute_factor;   /**< The number of extra points to pre-compute for each point. See the\n+                              *   [precompute_msm_points](@ref precompute_msm_points) function, `precompute_factor` passed\n+                              *   there needs to be equal to the one used here. Larger values decrease the\n+                              *   number of computations to make, on-line memory footprint, but increase the static\n+                              *   memory footprint. Default value: 1 (i.e. don't pre-compute). */\n+    int c;                   /**< \\f$ c \\f$ value, or \"window bitsize\" which is the main parameter of the \"bucket\n+                              *   method\" that we use to solve the MSM problem. As a rule of thumb, larger value\n+                              *   means more on-line memory footprint but also more parallelism and less computational\n+                              *   complexity (up to a certain point). Currently pre-computation is independent of\n+                              *   \\f$ c \\f$, however in the future value of \\f$ c \\f$ here and the one passed into the\n+                              *   [precompute_msm_points](@ref precompute_msm_points) function will need to be identical.\n+                              *    Default value: 0 (the optimal value of \\f$ c \\f$ is chosen automatically).  */\n+    int bitsize;             /**< Number of bits of the largest scalar. Typically equals the bitsize of scalar field,\n+                              *   but if a different (better) upper bound is known, it should be reflected in this\n+                              *   variable. Default value: 0 (set to the bitsize of scalar field). */\n+    int large_bucket_factor; /**< Variable that controls how sensitive the algorithm is to the buckets that occur\n+                              *   very frequently. Useful for efficient treatment of non-uniform distributions of\n+                              *   scalars and \"top windows\" with few bits. Can be set to 0 to disable separate\n+                              *   treatment of large buckets altogether. Default value: 10. */\n+    int batch_size;          /**< The number of MSMs to compute. Default value: 1. */\n+    bool are_scalars_on_device;       /**< True if scalars are on device and false if they're on host. Default value:\n+                                       *   false. */\n+    bool are_scalars_montgomery_form; /**< True if scalars are in Montgomery form and false otherwise. Default value:\n+                                       *   true. */\n+    bool are_points_on_device; /**< True if points are on device and false if they're on host. Default value: false. */\n+    bool are_points_montgomery_form; /**< True if coordinates of points are in Montgomery form and false otherwise.\n+                                      *   Default value: true. */\n+    bool are_results_on_device; /**< True if the results should be on device and false if they should be on host. If set\n+                                 *   to false, `is_async` won't take effect because a synchronization is needed to\n+                                 *   transfer results to the host. Default value: false. */\n+    bool is_big_triangle;       /**< Whether to do \"bucket accumulation\" serially. Decreases computational complexity\n+                                 *   but also greatly decreases parallelism, so only suitable for large batches of MSMs.\n+                                 *   Default value: false. */\n+    bool is_async;              /**< Whether to run the MSM asynchronously. If set to true, the MSM function will be\n+                                 *   non-blocking and you'd need to synchronize it explicitly by running\n+                                 *   `cudaStreamSynchronize` or `cudaDeviceSynchronize`. If set to false, the MSM\n+                                 *   function will block the current CPU thread. */\n+  };\n+} // namespace msm\n+\n+#endif // MSM_CONFIG_H\n"
  },
  {
    "path": "third_party/icicle/separate_ntt_algorithm.patch",
    "content": "diff --git a/icicle/include/ntt/ntt.cu.h b/icicle/include/ntt/ntt.cu.h\nindex ca014899..880ea944 100644\n--- a/icicle/include/ntt/ntt.cu.h\n+++ b/icicle/include/ntt/ntt.cu.h\n@@ -7,6 +7,7 @@\n #include \"gpu-utils/device_context.cu.h\"\n #include \"gpu-utils/error_handler.cu.h\"\n #include \"gpu-utils/sharedmem.cu.h\"\n+#include \"ntt/ntt_algorithm.h\"\n #include \"utils/utils.h\"\n \n /**\n@@ -107,15 +108,6 @@ namespace ntt {\n    */\n   enum class Ordering { kNN, kNR, kRN, kRR, kNM, kMN };\n \n-  /**\n-   * @enum NttAlgorithm\n-   * Which NTT algorithm to use. options are:\n-   * - Auto: implementation selects automatically based on heuristic. This value is a good default for most cases.\n-   * - Radix2: explicitly select radix-2 NTT algorithm\n-   * - MixedRadix: explicitly select mixed-radix NTT algorithm\n-   */\n-  enum class NttAlgorithm { Auto, Radix2, MixedRadix };\n-\n   /**\n    * @struct NTTConfig\n    * Struct that encodes NTT parameters to be passed into the [NTT](@ref NTT) function.\ndiff --git a/icicle/include/ntt/ntt_algorithm.h b/icicle/include/ntt/ntt_algorithm.h\nnew file mode 100644\nindex 00000000..8743712c\n--- /dev/null\n+++ b/icicle/include/ntt/ntt_algorithm.h\n@@ -0,0 +1,15 @@\n+#ifndef NTT_ALGORHTM_H\n+#define NTT_ALGORHTM_H\n+\n+namespace ntt {\n+  /**\n+   * @enum NttAlgorithm\n+   * Which NTT algorithm to use. options are:\n+   * - Auto: implementation selects automatically based on heuristic. This value is a good default for most cases.\n+   * - Radix2: explicitly select radix-2 NTT algorithm\n+   * - MixedRadix: explicitly select mixed-radix NTT algorithm\n+   */\n+  enum class NttAlgorithm { Auto, Radix2, MixedRadix };\n+} // namespace ntt\n+\n+#endif // NTT_ALGORHTM_H\n"
  },
  {
    "path": "third_party/icicle/workspace.bzl",
    "content": "\"\"\"loads the icicle library, used by Tachyon.\"\"\"\n\nload(\"//third_party:repo.bzl\", \"tachyon_http_archive\", \"tf_mirror_urls\")\n\ndef repo():\n    tachyon_http_archive(\n        name = \"icicle\",\n        urls = tf_mirror_urls(\"https://github.com/ingonyama-zk/icicle/archive/53f34aade57ff2b54a7eb1afadd563c143d4aa69.tar.gz\"),\n        sha256 = \"eac59b94592d3409e83b47a414f77d67d80780a5cf1461e62c0d5213c9b10c50\",\n        strip_prefix = \"icicle-53f34aade57ff2b54a7eb1afadd563c143d4aa69\",\n        patch_file = [\n            \"@kroma_network_tachyon//third_party/icicle:rename.patch\",\n            \"@kroma_network_tachyon//third_party/icicle:pragma.patch\",\n            \"@kroma_network_tachyon//third_party/icicle:inlinize.patch\",\n            \"@kroma_network_tachyon//third_party/icicle:remove_kernels_from_header.patch\",\n            \"@kroma_network_tachyon//third_party/icicle:separate_msm_config.patch\",\n            \"@kroma_network_tachyon//third_party/icicle:separate_ntt_algorithm.patch\",\n            \"@kroma_network_tachyon//third_party/icicle:separate_hash_config.patch\",\n            \"@kroma_network_tachyon//third_party/icicle:separate_merkle_tree_config.patch\",\n            \"@kroma_network_tachyon//third_party/icicle:change_sort_function.patch\",\n            \"@kroma_network_tachyon//third_party/icicle:fix_first_digest_copy_bug.patch\",\n        ],\n        build_file = \"//third_party/icicle:icicle.BUILD\",\n        link_files = {\"//third_party/icicle:build_defs.bzl\": \"build_defs.bzl\"},\n    )\n"
  },
  {
    "path": "third_party/json/BUILD.bazel",
    "content": ""
  },
  {
    "path": "third_party/json/workspace.bzl",
    "content": "\"\"\"loads the json library used by rapidsnark.\"\"\"\n\nload(\"//third_party:repo.bzl\", \"tachyon_http_archive\", \"tf_mirror_urls\")\n\ndef repo():\n    tachyon_http_archive(\n        name = \"nlohmann_json\",\n        urls = tf_mirror_urls(\"https://github.com/nlohmann/json/archive/v3.11.3.tar.gz\"),\n        sha256 = \"0d8ef5af7f9794e3263480193c491549b2ba6cc74bb018906202ada498a79406\",\n        strip_prefix = \"json-3.11.3\",\n    )\n"
  },
  {
    "path": "third_party/matplotlibcpp17/BUILD.bazel",
    "content": ""
  },
  {
    "path": "third_party/matplotlibcpp17/matplotlibcpp17.BUILD",
    "content": "package(default_visibility = [\"//visibility:public\"])\n\nlicenses([\"notice\"])  # MIT\n\nexports_files([\"LICENSE\"])\n\ncc_library(\n    name = \"matplotlibcpp17\",\n    hdrs = glob([\"include/matplotlibcpp17/*.h\"]),\n    include_prefix = \"third_party/matplotlibcpp17/include\",\n    includes = [\"include\"],\n    strip_include_prefix = \"include/matplotlibcpp17\",\n    deps = [\n        \"@local_config_python//:numpy_headers\",\n        \"@pybind11//:pybind11_embed\",\n    ],\n)\n"
  },
  {
    "path": "third_party/nasm/BUILD.bazel",
    "content": "# Needed to make this a package.\n\n# copybara:uncomment package(default_applicable_licenses = [\"//tachyon:license\"])\n"
  },
  {
    "path": "third_party/nasm/BUILD.system",
    "content": "licenses([\"notice\"])  # BSD 2-clause\n\nfilegroup(\n    name = \"LICENSE\",\n    visibility = [\"//visibility:public\"],\n)\n\ngenrule(\n    name = \"lnnasmlink\",\n    outs = [\"nasmlink\"],\n    cmd = \"ln -s $$(which nasm) $@\",\n)\n\nsh_binary(\n    name = \"nasm\",\n    srcs = [\"nasmlink\"],\n    visibility = [\"@libjpeg_turbo//:__pkg__\"],\n)\n"
  },
  {
    "path": "third_party/nasm/config.h",
    "content": "/* config/config.h.  Generated from config.h.in by configure.  */  // NOLINT(build/header_guard)\n/* config/config.h.in.  Generated from configure.ac by autoheader.  */\n\n/* Define to 1 to call abort() on panics (internal errors), for debugging. */\n/* #undef ABORT_ON_PANIC */\n\n/* Define if building universal (internal helper macro) */\n/* #undef AC_APPLE_UNIVERSAL_BUILD */\n\n/* Define to 1 if compiled with the `-fdata-sections' compiler flag */\n/* #undef CFLAG_FDATA_SECTIONS */\n\n/* Define to 1 if compiled with the `-ffunction-sections' compiler flag */\n/* #undef CFLAG_FFUNCTION_SECTIONS */\n\n/* Define to 1 if compiled with the `-fgnu89-inline' compiler flag */\n/* #undef CFLAG_FGNU89_INLINE */\n\n/* Define to 1 if compiled with the `-flto' compiler flag */\n/* #undef CFLAG_FLTO */\n\n/* Define to 1 if compiled with the `-fno-common' compiler flag */\n#define CFLAG_FNO_COMMON 1\n\n/* Define to 1 if compiled with the `-fno-omit-frame-pointer' compiler flag */\n/* #undef CFLAG_FNO_OMIT_FRAME_POINTER */\n\n/* Define to 1 if compiled with the `-fsanitize=address' compiler flag */\n/* #undef CFLAG_FSANITIZE_ADDRESS */\n\n/* Define to 1 if compiled with the `-fsanitize=undefined' compiler flag */\n/* #undef CFLAG_FSANITIZE_UNDEFINED */\n\n/* Define to 1 if compiled with the `-fvisibility=hidden' compiler flag */\n#define CFLAG_FVISIBILITY_HIDDEN 1\n\n/* Define to 1 if compiled with the `-fwrapv' compiler flag */\n#define CFLAG_FWRAPV 1\n\n/* Define to 1 if compiled with the `-ggdb3' compiler flag */\n/* #undef CFLAG_GGDB3 */\n\n/* Define to 1 if compiled with the `-pedantic' compiler flag */\n#define CFLAG_PEDANTIC 1\n\n/* Define to 1 if compiled with the `-U__STRICT_ANSI__' compiler flag */\n#define CFLAG_U_STRICT_ANSI 1\n\n/* Define to 1 if compiled with the `-W' compiler flag */\n#define CFLAG_W 1\n\n/* Define to 1 if compiled with the `-Wall' compiler flag */\n#define CFLAG_WALL 1\n\n/* Define to 1 if compiled with the `-Wc90-c99-compat' compiler flag */\n/* #undef CFLAG_WC90_C99_COMPAT */\n\n/* Define to 1 if compiled with the `-Werror' compiler flag */\n/* #undef CFLAG_WERROR */\n\n/* Define to 1 if compiled with the `-Werror=attributes' compiler flag */\n#define CFLAG_WERROR_ATTRIBUTES 1\n\n/* Define to 1 if compiled with the `-Werror=comment' compiler flag */\n#define CFLAG_WERROR_COMMENT 1\n\n/* Define to 1 if compiled with the `-Werror=implicit' compiler flag */\n#define CFLAG_WERROR_IMPLICIT 1\n\n/* Define to 1 if compiled with the `-Werror=missing-braces' compiler flag */\n#define CFLAG_WERROR_MISSING_BRACES 1\n\n/* Define to 1 if compiled with the `-Werror=missing-declarations' compiler\n   flag */\n#define CFLAG_WERROR_MISSING_DECLARATIONS 1\n\n/* Define to 1 if compiled with the `-Werror=missing-prototypes' compiler flag\n */\n#define CFLAG_WERROR_MISSING_PROTOTYPES 1\n\n/* Define to 1 if compiled with the `-Werror=pointer-arith' compiler flag */\n#define CFLAG_WERROR_POINTER_ARITH 1\n\n/* Define to 1 if compiled with the `-Werror=return-type' compiler flag */\n#define CFLAG_WERROR_RETURN_TYPE 1\n\n/* Define to 1 if compiled with the `-Werror=strict-prototypes' compiler flag\n */\n/* #undef CFLAG_WERROR_STRICT_PROTOTYPES */\n\n/* Define to 1 if compiled with the `-Werror=trigraphs' compiler flag */\n#define CFLAG_WERROR_TRIGRAPHS 1\n\n/* Define to 1 if compiled with the `-Werror=unknown-warning-option' compiler\n   flag */\n/* #undef CFLAG_WERROR_UNKNOWN_WARNING_OPTION */\n\n/* Define to 1 if compiled with the `-Werror=vla' compiler flag */\n#define CFLAG_WERROR_VLA 1\n\n/* Define to 1 if compiled with the `-Wlong-long' compiler flag */\n#define CFLAG_WLONG_LONG 1\n\n/* Define to 1 if compiled with the `-Wl,--gc-sections' compiler flag */\n/* #undef CFLAG_WL_GC_SECTIONS */\n\n/* Define to 1 if compiled with the `-Wpedantic-ms-format' compiler flag */\n/* #undef CFLAG_WPEDANTIC_MS_FORMAT */\n\n/* Define to 1 if compiled with the `-Wshift-negative-value' compiler flag */\n#define CFLAG_WSHIFT_NEGATIVE_VALUE 1\n\n/* Define to 1 if compiled with the `-Wstringop-truncation' compiler flag */\n/* #undef CFLAG_WSTRINGOP_TRUNCATION */\n\n/* Define to 1 if you have the `access' function. */\n#define HAVE_ACCESS 1\n\n/* Define to 1 if you have the `canonicalize_file_name' function. */\n/* #undef HAVE_CANONICALIZE_FILE_NAME */\n\n/* Define to 1 if you have the `cpu_to_le16' intrinsic function. */\n/* #undef HAVE_CPU_TO_LE16 */\n\n/* Define to 1 if you have the `cpu_to_le32' intrinsic function. */\n/* #undef HAVE_CPU_TO_LE32 */\n\n/* Define to 1 if you have the `cpu_to_le64' intrinsic function. */\n/* #undef HAVE_CPU_TO_LE64 */\n\n/* Define to 1 if you have the declaration of `strcasecmp', and to 0 if you\n   don't. */\n#define HAVE_DECL_STRCASECMP 1\n\n/* Define to 1 if you have the declaration of `stricmp', and to 0 if you\n   don't. */\n#define HAVE_DECL_STRICMP 0\n\n/* Define to 1 if you have the declaration of `strlcpy', and to 0 if you\n   don't. */\n#define HAVE_DECL_STRLCPY 0\n\n/* Define to 1 if you have the declaration of `strncasecmp', and to 0 if you\n   don't. */\n#define HAVE_DECL_STRNCASECMP 1\n\n/* Define to 1 if you have the declaration of `strnicmp', and to 0 if you\n   don't. */\n#define HAVE_DECL_STRNICMP 0\n\n/* Define to 1 if you have the declaration of `strnlen', and to 0 if you\n   don't. */\n#define HAVE_DECL_STRNLEN 1\n\n/* Define to 1 if you have the declaration of `strrchrnul', and to 0 if you\n   don't. */\n#define HAVE_DECL_STRRCHRNUL 0\n\n/* Define to 1 if you have the declaration of `strsep', and to 0 if you don't.\n */\n#define HAVE_DECL_STRSEP 1\n\n/* Define to 1 if you have the <endian.h> header file. */\n/* #undef HAVE_ENDIAN_H */\n\n/* Define to 1 if you have the `faccessat' function. */\n#define HAVE_FACCESSAT 1\n\n/* Define to 1 if you have the <fcntl.h> header file. */\n#define HAVE_FCNTL_H 1\n\n/* Define to 1 if you have the `fileno' function. */\n#define HAVE_FILENO 1\n\n/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */\n#define HAVE_FSEEKO 1\n\n/* Define to 1 if you have the `fstat' function. */\n#define HAVE_FSTAT 1\n\n/* Define to 1 if you have the `ftruncate' function. */\n#define HAVE_FTRUNCATE 1\n\n/* Define to 1 if your compiler supports __attribute__((alloc_size)) on\n   functions */\n#define HAVE_FUNC_ATTRIBUTE_ALLOC_SIZE 1\n\n/* Define to 1 if your compiler supports __attribute__((cold)) on functions */\n#define HAVE_FUNC_ATTRIBUTE_COLD 1\n\n/* Define to 1 if your compiler supports __attribute__((const)) on functions\n */\n#define HAVE_FUNC_ATTRIBUTE_CONST 1\n\n/* Define to 1 if your compiler supports __attribute__((error)) on functions\n */\n/* #undef HAVE_FUNC_ATTRIBUTE_ERROR */\n\n/* Define to 1 if your compiler supports __attribute__((format)) on functions\n */\n#define HAVE_FUNC_ATTRIBUTE_FORMAT 1\n\n/* Define to 1 if your compiler supports __attribute__((malloc)) on functions\n */\n#define HAVE_FUNC_ATTRIBUTE_MALLOC 1\n\n/* Define to 1 if your compiler supports __attribute__((noreturn)) on\n   functions */\n#define HAVE_FUNC_ATTRIBUTE_NORETURN 1\n\n/* Define to 1 if your compiler supports __attribute__((pure)) on functions */\n#define HAVE_FUNC_ATTRIBUTE_PURE 1\n\n/* Define to 1 if your compiler supports __attribute__((returns_nonnull)) on\n   functions */\n#define HAVE_FUNC_ATTRIBUTE_RETURNS_NONNULL 1\n\n/* Define to 1 if your compiler supports __attribute__((sentinel)) on\n   functions */\n#define HAVE_FUNC_ATTRIBUTE_SENTINEL 1\n\n/* Define to 1 if you have the `getgid' function. */\n#define HAVE_GETGID 1\n\n/* Define to 1 if you have the `getpagesize' function. */\n#define HAVE_GETPAGESIZE 1\n\n/* Define to 1 if you have the `getuid' function. */\n#define HAVE_GETUID 1\n\n/* Define to 1 if you have the `htole16' intrinsic function. */\n/* #undef HAVE_HTOLE16 */\n\n/* Define to 1 if you have the `htole32' intrinsic function. */\n/* #undef HAVE_HTOLE32 */\n\n/* Define to 1 if you have the `htole64' intrinsic function. */\n/* #undef HAVE_HTOLE64 */\n\n/* Define to 1 if you have the <intrin.h> header file. */\n/* #undef HAVE_INTRIN_H */\n\n/* Define to 1 if you have the <inttypes.h> header file. */\n#define HAVE_INTTYPES_H 1\n\n/* Define to 1 if you have the <io.h> header file. */\n/* #undef HAVE_IO_H */\n\n/* Define to 1 if you have the <machine/endian.h> header file. */\n/* #undef HAVE_MACHINE_ENDIAN_H */\n\n/* Define to 1 if you have the <memory.h> header file. */\n#define HAVE_MEMORY_H 1\n\n/* Define to 1 if you have a working `mmap' system call. */\n#define HAVE_MMAP 1\n\n/* Define to 1 if you have the `pathconf' function. */\n#define HAVE_PATHCONF 1\n\n/* Define to 1 if you have the `realpath' function. */\n#define HAVE_REALPATH 1\n\n/* Define to 1 if you have the `snprintf' function. */\n#define HAVE_SNPRINTF 1\n\n/* Define to 1 if you have the `stat' function. */\n#define HAVE_STAT 1\n\n/* Define to 1 if stdbool.h conforms to C99. */\n#define HAVE_STDBOOL_H 1\n\n/* Define to 1 if your compiler supports C99 extern inline */\n#define HAVE_STDC_INLINE 1\n\n/* Define to 1 if you have the <stdint.h> header file. */\n#define HAVE_STDINT_H 1\n\n/* Define to 1 if you have the <stdlib.h> header file. */\n#define HAVE_STDLIB_H 1\n\n/* Define to 1 if you have the <stdnoreturn.h> header file. */\n#define HAVE_STDNORETURN_H 1\n\n/* Define to 1 if you have the `strcasecmp' function. */\n#define HAVE_STRCASECMP 1\n\n/* Define to 1 if you have the `stricmp' function. */\n/* #undef HAVE_STRICMP */\n\n/* Define to 1 if you have the <strings.h> header file. */\n#define HAVE_STRINGS_H 1\n\n/* Define to 1 if you have the <string.h> header file. */\n#define HAVE_STRING_H 1\n\n/* Define to 1 if you have the `strlcpy' function. */\n/* #undef HAVE_STRLCPY */\n\n/* Define to 1 if you have the `strncasecmp' function. */\n#define HAVE_STRNCASECMP 1\n\n/* Define to 1 if you have the `strnicmp' function. */\n/* #undef HAVE_STRNICMP */\n\n/* Define to 1 if you have the `strnlen' function. */\n#define HAVE_STRNLEN 1\n\n/* Define to 1 if you have the `strrchrnul' function. */\n/* #undef HAVE_STRRCHRNUL */\n\n/* Define to 1 if you have the `strsep' function. */\n#define HAVE_STRSEP 1\n\n/* Define to 1 if the system has the type `struct stat'. */\n#define HAVE_STRUCT_STAT 1\n\n/* Define to 1 if the system has the type `struct _stati64'. */\n/* #undef HAVE_STRUCT__STATI64 */\n\n/* Define to 1 if you have the `sysconf' function. */\n#define HAVE_SYSCONF 1\n\n/* Define to 1 if you have the <sys/endian.h> header file. */\n/* #undef HAVE_SYS_ENDIAN_H */\n\n/* Define to 1 if you have the <sys/mman.h> header file. */\n#define HAVE_SYS_MMAN_H 1\n\n/* Define to 1 if you have the <sys/param.h> header file. */\n#define HAVE_SYS_PARAM_H 1\n\n/* Define to 1 if you have the <sys/stat.h> header file. */\n#define HAVE_SYS_STAT_H 1\n\n/* Define to 1 if you have the <sys/types.h> header file. */\n#define HAVE_SYS_TYPES_H 1\n\n/* Define to 1 if the system has the type `uintptr_t'. */\n#define HAVE_UINTPTR_T 1\n\n/* Define to 1 if you have the <unistd.h> header file. */\n#define HAVE_UNISTD_H 1\n\n/* Define to 1 if you have the `vsnprintf' function. */\n#define HAVE_VSNPRINTF 1\n\n/* Define to 1 if you have the `_access' function. */\n/* #undef HAVE__ACCESS */\n\n/* Define to 1 if you have the `_BitScanReverse' intrinsic function. */\n/* #undef HAVE__BITSCANREVERSE */\n\n/* Define to 1 if you have the `_BitScanReverse64' intrinsic function. */\n/* #undef HAVE__BITSCANREVERSE64 */\n\n/* Define to 1 if the system has the type `_Bool'. */\n#define HAVE__BOOL 1\n\n/* Define to 1 if you have the `_byteswap_uint64' intrinsic function. */\n/* #undef HAVE__BYTESWAP_UINT64 */\n\n/* Define to 1 if you have the `_byteswap_ulong' intrinsic function. */\n/* #undef HAVE__BYTESWAP_ULONG */\n\n/* Define to 1 if you have the `_byteswap_ushort' intrinsic function. */\n/* #undef HAVE__BYTESWAP_USHORT */\n\n/* Define to 1 if you have the `_chsize' function. */\n/* #undef HAVE__CHSIZE */\n\n/* Define to 1 if you have the `_chsize_s' function. */\n/* #undef HAVE__CHSIZE_S */\n\n/* Define to 1 if you have the `_filelengthi64' function. */\n/* #undef HAVE__FILELENGTHI64 */\n\n/* Define to 1 if you have the `_fileno' function. */\n/* #undef HAVE__FILENO */\n\n/* Define to 1 if you have the `_fseeki64' function. */\n/* #undef HAVE__FSEEKI64 */\n\n/* Define to 1 if you have the `_fstati64' function. */\n/* #undef HAVE__FSTATI64 */\n\n/* Define to 1 if you have the `_fullpath' function. */\n/* #undef HAVE__FULLPATH */\n\n/* Define to 1 if you have the `_snprintf' function. */\n/* #undef HAVE__SNPRINTF */\n\n/* Define to 1 if you have the `_stati64' function. */\n/* #undef HAVE__STATI64 */\n\n/* Define to 1 if you have the `_vsnprintf' function. */\n/* #undef HAVE__VSNPRINTF */\n\n/* Define to 1 if you have the `__bswap_16' intrinsic function. */\n/* #undef HAVE___BSWAP_16 */\n\n/* Define to 1 if you have the `__bswap_32' intrinsic function. */\n/* #undef HAVE___BSWAP_32 */\n\n/* Define to 1 if you have the `__bswap_64' intrinsic function. */\n/* #undef HAVE___BSWAP_64 */\n\n/* Define to 1 if you have the `__builtin_bswap16' intrinsic function. */\n#define HAVE___BUILTIN_BSWAP16 1\n\n/* Define to 1 if you have the `__builtin_bswap32' intrinsic function. */\n#define HAVE___BUILTIN_BSWAP32 1\n\n/* Define to 1 if you have the `__builtin_bswap64' intrinsic function. */\n#define HAVE___BUILTIN_BSWAP64 1\n\n/* Define to 1 if you have the `__builtin_clz' intrinsic function. */\n#define HAVE___BUILTIN_CLZ 1\n\n/* Define to 1 if you have the `__builtin_clzl' intrinsic function. */\n#define HAVE___BUILTIN_CLZL 1\n\n/* Define to 1 if you have the `__builtin_clzll' intrinsic function. */\n#define HAVE___BUILTIN_CLZLL 1\n\n/* Define to 1 if you have the `__builtin_constant_p' intrinsic function. */\n#define HAVE___BUILTIN_CONSTANT_P 1\n\n/* Define to 1 if you have the `__builtin_expect' intrinsic function. */\n#define HAVE___BUILTIN_EXPECT 1\n\n/* Define to 1 if you have the `__cpu_to_le16' intrinsic function. */\n/* #undef HAVE___CPU_TO_LE16 */\n\n/* Define to 1 if you have the `__cpu_to_le32' intrinsic function. */\n/* #undef HAVE___CPU_TO_LE32 */\n\n/* Define to 1 if you have the `__cpu_to_le64' intrinsic function. */\n/* #undef HAVE___CPU_TO_LE64 */\n\n/* Define to the address where bug reports for this package should be sent. */\n#define PACKAGE_BUGREPORT \"\"\n\n/* Define to the full name of this package. */\n#define PACKAGE_NAME \"\"\n\n/* Define to the full name and version of this package. */\n#define PACKAGE_STRING \"\"\n\n/* Define to the one symbol short name of this package. */\n#define PACKAGE_TARNAME \"\"\n\n/* Define to the home page for this package. */\n#define PACKAGE_URL \"\"\n\n/* Define to the version of this package. */\n#define PACKAGE_VERSION \"\"\n\n/* Define to 1 if you have the ANSI C header files. */\n#define STDC_HEADERS 1\n\n/* Enable extensions on AIX 3, Interix.  */\n#ifndef _ALL_SOURCE\n#define _ALL_SOURCE 1\n#endif\n/* Enable GNU extensions on systems that have them.  */\n#ifndef _GNU_SOURCE\n#define _GNU_SOURCE 1\n#endif\n/* Enable threading extensions on Solaris.  */\n#ifndef _POSIX_PTHREAD_SEMANTICS\n#define _POSIX_PTHREAD_SEMANTICS 1\n#endif\n/* Enable extensions on HP NonStop.  */\n#ifndef _TANDEM_SOURCE\n#define _TANDEM_SOURCE 1\n#endif\n/* Enable general extensions on Solaris.  */\n#ifndef __EXTENSIONS__\n#define __EXTENSIONS__ 1\n#endif\n\n/* Define to 1 if your processor stores words with the most significant byte\n   first (like Motorola and SPARC, unlike Intel and VAX). */\n/* #undef WORDS_BIGENDIAN */\n\n/* Define to 1 if your processor stores words with the least significant byte\n   first (like Intel and VAX, unlike Motorola and SPARC). */\n#define WORDS_LITTLEENDIAN 1\n\n/* Enable large inode numbers on Mac OS X 10.5.  */\n#ifndef _DARWIN_USE_64_BIT_INODE\n#define _DARWIN_USE_64_BIT_INODE 1\n#endif\n\n/* Number of bits in a file offset, on hosts where this is settable. */\n/* #undef _FILE_OFFSET_BITS */\n\n/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */\n/* #undef _LARGEFILE_SOURCE */\n\n/* Define for large files, on AIX-style hosts. */\n/* #undef _LARGE_FILES */\n\n/* Define to 1 if on MINIX. */\n/* #undef _MINIX */\n\n/* Define to 2 if the system does not provide POSIX.1 features except with\n   this defined. */\n/* #undef _POSIX_1_SOURCE */\n\n/* Define to 1 if you need to in order for `stat' and other things to work. */\n/* #undef _POSIX_SOURCE */\n\n/* Define to empty if `const' does not conform to ANSI C. */\n/* #undef const */\n\n/* Define to `__inline__' or `__inline' if that's what the C compiler\n   calls it, or to nothing if 'inline' is not supported under any name.  */\n#ifndef __cplusplus\n/* #undef inline */\n#endif\n\n/* Define to the equivalent of the C99 'restrict' keyword, or to\n   nothing if this is not supported.  Do not define if restrict is\n   supported directly.  */\n#define restrict __restrict\n/* Work around a bug in Sun C++: it does not support _Restrict or\n   __restrict__, even though the corresponding Sun C compiler ends up with\n   \"#define restrict _Restrict\" or \"#define restrict __restrict__\" in the\n   previous line.  Perhaps some future version of Sun C++ will work with\n   restrict; if so, hopefully it defines __RESTRICT like Sun C does.  */\n#if defined __SUNPRO_CC && !defined __RESTRICT\n#define _Restrict\n#define __restrict__\n#endif\n\n/* Define to `unsigned int' if <sys/types.h> does not define. */\n/* #undef size_t */\n\n/* Define to the type of an unsigned integer type wide enough to hold a\n   pointer, if such a type exists, and if the system does not define it. */\n/* #undef uintptr_t */\n"
  },
  {
    "path": "third_party/nasm/nasm.BUILD",
    "content": "package(default_visibility = [\"//visibility:public\"])\n\nlicenses([\"notice\"])\n\nexports_files([\"LICENSE\"])\n\nINCLUDES = [\n    \".\",\n    \"include\",\n    \"x86\",\n    \"asm\",\n    \"disasm\",\n    \"output\",\n]\n\nCOPTS = select({\n    \":windows\": [],\n    \"//conditions:default\": [\n        \"-w\",\n        \"-DHAVE_CONFIG_H\",\n    ],\n})\n\ncc_library(\n    name = \"nasm_2_14_02\",\n    srcs = [\n        \"asm/assemble.c\",\n        \"asm/directbl.c\",\n        \"asm/directiv.c\",\n        \"asm/error.c\",\n        \"asm/eval.c\",\n        \"asm/exprdump.c\",\n        \"asm/exprlib.c\",\n        \"asm/float.c\",\n        \"asm/labels.c\",\n        \"asm/listing.c\",\n        \"asm/parser.c\",\n        \"asm/pptok.c\",\n        \"asm/pragma.c\",\n        \"asm/preproc.c\",\n        \"asm/preproc-nop.c\",\n        \"asm/quote.c\",\n        \"asm/rdstrnum.c\",\n        \"asm/segalloc.c\",\n        \"asm/stdscan.c\",\n        \"asm/strfunc.c\",\n        \"asm/tokhash.c\",\n        \"common/common.c\",\n        \"disasm/disasm.c\",\n        \"disasm/sync.c\",\n        \"macros/macros.c\",\n        \"nasmlib/badenum.c\",\n        \"nasmlib/bsi.c\",\n        \"nasmlib/crc64.c\",\n        \"nasmlib/errfile.c\",\n        \"nasmlib/file.c\",\n        \"nasmlib/filename.c\",\n        \"nasmlib/hashtbl.c\",\n        \"nasmlib/ilog2.c\",\n        \"nasmlib/malloc.c\",\n        \"nasmlib/md5c.c\",\n        \"nasmlib/mmap.c\",\n        \"nasmlib/path.c\",\n        \"nasmlib/perfhash.c\",\n        \"nasmlib/raa.c\",\n        \"nasmlib/rbtree.c\",\n        \"nasmlib/readnum.c\",\n        \"nasmlib/realpath.c\",\n        \"nasmlib/saa.c\",\n        \"nasmlib/srcfile.c\",\n        \"nasmlib/string.c\",\n        \"nasmlib/strlist.c\",\n        \"nasmlib/ver.c\",\n        \"output/codeview.c\",\n        \"output/legacy.c\",\n        \"output/nulldbg.c\",\n        \"output/nullout.c\",\n        \"output/outaout.c\",\n        \"output/outas86.c\",\n        \"output/outbin.c\",\n        \"output/outcoff.c\",\n        \"output/outdbg.c\",\n        \"output/outelf.c\",\n        \"output/outform.c\",\n        \"output/outieee.c\",\n        \"output/outlib.c\",\n        \"output/outmacho.c\",\n        \"output/outobj.c\",\n        \"output/outrdf2.c\",\n        \"output/strtbl.c\",\n        \"stdlib/snprintf.c\",\n        \"stdlib/strlcpy.c\",\n        \"stdlib/strnlen.c\",\n        \"stdlib/strrchrnul.c\",\n        \"stdlib/vsnprintf.c\",\n        \"x86/disp8.c\",\n        \"x86/iflag.c\",\n        \"x86/insnsa.c\",\n        \"x86/insnsb.c\",\n        \"x86/insnsd.c\",\n        \"x86/insnsn.c\",\n        \"x86/regdis.c\",\n        \"x86/regflags.c\",\n        \"x86/regs.c\",\n        \"x86/regvals.c\",\n    ],\n    hdrs = glob([\n        \"*.h\",\n        \"include/*.h\",\n        \"x86/*.h\",\n        \"disasm/*.h\",\n        \"config/*.h\",\n        \"asm/*.h\",\n        \"output/*.h\",\n        \"nasmlib/*.h\",\n    ]),\n    copts = COPTS,\n    includes = INCLUDES,\n)\n\ncc_binary(\n    name = \"nasm\",\n    srcs = [\n        \"asm/nasm.c\",\n        \"nasmlib/zerobuf.c\",\n    ],\n    copts = COPTS,\n    includes = INCLUDES,\n    deps = [\n        \":nasm_2_14_02\",\n    ],\n)\n\nconfig_setting(\n    name = \"windows\",\n    values = {\n        \"cpu\": \"x64_windows\",\n    },\n)\n"
  },
  {
    "path": "third_party/nasm/workspace.bzl",
    "content": "\"\"\"loads the nasm library, used by TF.\"\"\"\n\nload(\"//third_party:repo.bzl\", \"tachyon_http_archive\")\n\ndef repo():\n    tachyon_http_archive(\n        name = \"nasm\",\n        urls = [\n            \"https://storage.googleapis.com/mirror.tensorflow.org/www.nasm.us/pub/nasm/releasebuilds/2.14.02/nasm-2.14.02.tar.bz2\",\n            \"http://pkgs.fedoraproject.org/repo/pkgs/nasm/nasm-2.14.02.tar.bz2/sha512/d7a6b4cee8dfd603d8d4c976e5287b5cc542fa0b466ff989b743276a6e28114e64289bf02a7819eca63142a5278aa6eed57773007e5f589e15768e6456a8919d/nasm-2.14.02.tar.bz2\",\n            \"http://www.nasm.us/pub/nasm/releasebuilds/2.14.02/nasm-2.14.02.tar.bz2\",\n        ],\n        sha256 = \"34fd26c70a277a9fdd54cb5ecf389badedaf48047b269d1008fbc819b24e80bc\",\n        strip_prefix = \"nasm-2.14.02\",\n        build_file = \"//third_party/nasm:nasm.BUILD\",\n        system_build_file = \"//third_party/nasm:BUILD.system\",\n        link_files = {\"//third_party/nasm:config.h\": \"config/config.h\"},\n    )\n"
  },
  {
    "path": "third_party/node_addon_api/BUILD.bazel",
    "content": ""
  },
  {
    "path": "third_party/node_addon_api/BUILD.tpl",
    "content": "load(\"@rules_cc//cc:defs.bzl\", \"cc_library\")\n\ncc_library(\n    name = \"node_addon_api\",\n    hdrs = [\n        \"node_modules/node-addon-api/napi.h\",\n        \"node_modules/node-addon-api/napi-inl.deprecated.h\",\n        \"node_modules/node-addon-api/napi-inl.h\",\n    ],\n    defines = [\n        \"NODE_GYP_MODULE_NAME\",\n        \"USING_UV_SHARED=1\",\n        \"USING_V8_SHARED=1\",\n        \"V8_DEPRECATION_WARNINGS=1\",\n        \"V8_DEPRECATION_WARNINGS\",\n        \"V8_IMMINENT_DEPRECATION_WARNINGS\",\n        \"_GLIBCXX_USE_CXX11_ABI=1\",\n        \"_LARGEFILE_SOURCE\",\n        \"_FILE_OFFSET_BITS=64\",\n        \"__STDC_FORMAT_MACROS\",\n        \"OPENSSL_NO_PINSHARED\",\n        \"OPENSSL_THREADS\",\n        \"NAPI_DISABLE_CPP_EXCEPTIONS\",\n        \"BUILDING_NODE_EXTENSION\",\n    ],\n    includes = [\"node_modules/node-addon-api\"],\n    strip_include_prefix = \"node_modules/node-addon-api\",\n    include_prefix = \"third_party/node_addon_api\",\n    visibility = [\"//visibility:public\"],\n    deps = [\":node\"],\n)\n\ncc_library(\n    name = \"node\",\n    hdrs = glob([\"%{NODE_VERSION}/include/**/*.h\"]),\n    includes = [\n        \"%{NODE_VERSION}/include\",\n        \"%{NODE_VERSION}/include/node\",\n    ],\n)\n"
  },
  {
    "path": "third_party/node_addon_api/install_node_addon_api.bzl",
    "content": "load(\n    \"//third_party/remote_config:common.bzl\",\n    \"get_bash_bin\",\n)\n\ndef _fail(msg):\n    \"\"\"Output failure message when auto configuration fails.\"\"\"\n    fail(\"node-addon-api install failed: %s\\n\" % (msg))\n\ndef _install_node_addon_api_impl(repository_ctx):\n    repository_ctx.symlink(Label(\"//third_party/node_addon_api:package.json\"), \"package.json\")\n\n    bash_bin = get_bash_bin(repository_ctx)\n\n    cmd = [bash_bin, \"-c\", \"npm install\"]\n    result = repository_ctx.execute(cmd)\n    if result.return_code != 0:\n        _fail(\"Failed npm install: %s.\" % result.stderr)\n\n    cmd = [bash_bin, \"-c\", \"npx node-gyp install --devdir .\"]\n    result = repository_ctx.execute(cmd)\n    if result.return_code != 0:\n        _fail(\"Failed node-gyp install: %s.\" % result.stderr)\n\n    cmd = [bash_bin, \"-c\", \"node --version\"]\n    result = repository_ctx.execute(cmd)\n    if result.return_code != 0:\n        _fail(\"Failed node --version: %s.\" % result.stderr)\n\n    version = result.stdout.strip()\n    if version.startswith(\"v\"):\n        version = version[1:]\n\n    repository_ctx.template(\n        \"BUILD.bazel\",\n        Label(\"//third_party/node_addon_api:BUILD.tpl\"),\n        {\n            \"%{NODE_VERSION}\": version,\n        },\n    )\n\ninstall_node_addon_api = repository_rule(\n    implementation = _install_node_addon_api_impl,\n)\n"
  },
  {
    "path": "third_party/node_addon_api/package.json",
    "content": "{\n  \"name\": \"node-binding\",\n  \"gypfile\": false,\n  \"dependencies\": {\n    \"node-addon-api\": \"^7.0.0\",\n    \"node-gyp\": \"^7.0.0\"\n  }\n}\n"
  },
  {
    "path": "third_party/omp/BUILD.bazel",
    "content": ""
  },
  {
    "path": "third_party/omp/omp.BUILD",
    "content": "load(\"@rules_cc//cc:defs.bzl\", \"cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nHEADERS = [\n    \"omp.h\",\n    \"ompx.h\",\n    \"omp-tools.h\",\n    \"ompt.h\",\n]\n\n# NOTE: This is only for macos.\n#       On other platforms openmp should work without local config.\ngenrule(\n    name = \"link_headers\",\n    outs = HEADERS,\n    cmd = select({\n        \"@kroma_network_tachyon//:macos_x86_64\": \"\"\"\n      mkdir -p $(@D)/\n      for file in $(OUTS); do\n        file=$${file##*/}\n        ln -sf /usr/local/include/$$file $(@D)/$$file\n      done\n    \"\"\",\n        \"@kroma_network_tachyon//:macos_aarch64\": \"\"\"\n      mkdir -p $(@D)/\n      for file in $(OUTS); do\n        file=$${file##*/}\n        ln -sf /opt/homebrew/opt/libomp/include/$$file $(@D)/$$file\n      done\n    \"\"\",\n        \"//conditions:default\": \"\",\n    }),\n)\n\ncc_library(\n    name = \"omp\",\n    hdrs = HEADERS,\n    include_prefix = \"third_party/omp/include\",\n    includes = [\".\"],\n    linkopts = select({\n        \"@kroma_network_tachyon//:macos_x86_64\": [\"-L/usr/local/lib\"],\n        \"@kroma_network_tachyon//:macos_aarch64\": [\"-L/opt/homebrew/opt/libomp/lib\"],\n        \"//conditions:default\": [],\n    }) + [\n        \"-lomp\",\n    ],\n)\n"
  },
  {
    "path": "third_party/omp/omp_configure.bzl",
    "content": "def _omp_configure_impl(repository_ctx):\n    repository_ctx.symlink(Label(\"//third_party/omp:omp.BUILD\"), \"BUILD.bazel\")\n\nomp_configure = repository_rule(\n    implementation = _omp_configure_impl,\n)\n"
  },
  {
    "path": "third_party/pdqsort/BUILD.bazel",
    "content": ""
  },
  {
    "path": "third_party/pdqsort/pdqsort.BUILD",
    "content": "load(\"@rules_cc//cc:defs.bzl\", \"cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncc_library(\n    name = \"pdqsort\",\n    hdrs = [\"pdqsort.h\"],\n    include_prefix = \"third_party/pdqsort/include\",\n)\n"
  },
  {
    "path": "third_party/pdqsort/workspace.bzl",
    "content": "\"\"\"loads the pdqsort library, used by Tachyon.\"\"\"\n\nload(\"//third_party:repo.bzl\", \"tachyon_http_archive\", \"tf_mirror_urls\")\n\ndef repo():\n    tachyon_http_archive(\n        name = \"pdqsort\",\n        urls = tf_mirror_urls(\"https://github.com/orlp/pdqsort/archive/b1ef26a55cdb60d236a5cb199c4234c704f46726.tar.gz\"),\n        sha256 = \"1df2463f94ebd926f402e7bcd92bf4a16f7a35732080a607fe4716888f1edbb5\",\n        strip_prefix = \"pdqsort-b1ef26a55cdb60d236a5cb199c4234c704f46726\",\n        build_file = \"//third_party/pdqsort:pdqsort.BUILD\",\n    )\n"
  },
  {
    "path": "third_party/perfetto/perfetto.BUILD",
    "content": "load(\"@rules_cc//cc:defs.bzl\", \"cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncc_library(\n    name = \"perfetto\",\n    srcs = [\"sdk/perfetto.cc\"],\n    hdrs = [\"sdk/perfetto.h\"],\n    include_prefix = \"third_party/perfetto\",\n    strip_include_prefix = \"sdk\",\n)\n"
  },
  {
    "path": "third_party/powersort/BUILD.bazel",
    "content": ""
  },
  {
    "path": "third_party/powersort/fix_multiple_definitions.patch",
    "content": "diff --git a/src/sorts/merging.h b/src/sorts/merging.h\nindex a2a3769..9afc08f 100644\n--- a/src/sorts/merging.h\n+++ b/src/sorts/merging.h\n@@ -7,14 +7,6 @@\n \n namespace algorithms {\n \n-#ifdef COUNT_MERGECOST\n-\tconst bool COUNT_MERGE_COSTS = true;\n-#else\n-\tconst bool COUNT_MERGE_COSTS = false;\n-#endif\n-\tlong long volatile totalMergeCosts = 0;\n-\tlong long volatile totalBufferCosts = 0;\n-\n     /**\n      * A sentinel value used by some merging method;\n      * this value must be strictly larger than any value in the input.\n@@ -39,7 +31,7 @@ namespace algorithms {\n         COPY_BOTH_WITH_SENTINELS\n     };\n \n-    std::string to_string(merging_methods mergingMethod) {\n+    inline std::string to_string(merging_methods mergingMethod) {\n         switch (mergingMethod) {\n             case UNSTABLE_BITONIC_MERGE:\n                 return \"UNSTABLE_BITONIC_MERGE\";\n@@ -70,10 +62,8 @@ namespace algorithms {\n \t */\n \ttemplate<typename Iter, typename Iter2>\n \tvoid merge_runs_bitonic(Iter l, Iter m, Iter r, Iter2 B) {\n-\t\tif (COUNT_MERGE_COSTS) totalMergeCosts += (r-l);\n \t\tstd::copy_backward(l,m,B+(m-l));\n         std::reverse_copy(m,r,B+(m-l));\n-        if (COUNT_MERGE_COSTS) totalBufferCosts += (r-l);\n         auto i = B, j = B+(r-l-1);\n \t\tfor (auto k = l; k < r; ++k)\n \t\t\t*k = *j < *i ? *j-- : *i++;\n@@ -90,10 +80,8 @@ namespace algorithms {\n \ttemplate<typename Iter, typename Iter2>\n \tvoid merge_runs_bitonic_manual_copy(Iter l, Iter m, Iter r, Iter2 B) {\n \t\tIter i1, j1; Iter2 b;\n-\t\tif (COUNT_MERGE_COSTS) totalMergeCosts += (r-l);\n \t\tfor (i1 = m-1, b = B+(m-1-l); i1 >= l;) *b-- = *i1--;\n \t\tfor (j1 = r, b = B+(m-l); j1 > m;) *b++ = *--j1;\n-        if (COUNT_MERGE_COSTS) totalBufferCosts += (r-l);\n \t\tauto i = B, j = B+(r-l-1);\n \t\tfor (auto k = l; k < r; ++k)\n \t\t\t*k = *j < *i ? *j-- : *i++;\n@@ -111,10 +99,8 @@ namespace algorithms {\n \t */\n \ttemplate<typename Iter, typename Iter2>\n \tvoid merge_runs_bitonic_branchless(Iter l, Iter m, Iter r, Iter2 B) {\n-\t\tif (COUNT_MERGE_COSTS) totalMergeCosts += (r-l);\n \t\tstd::copy_backward(l,m,B+(m-l));\n \t\tstd::reverse_copy(m,r,B+(m-l));\n-        if (COUNT_MERGE_COSTS) totalBufferCosts += (r-l);\n \t\tIter2 i = B, j = B+(r-l-1);\n \t\tfor (auto k = l; k < r; ++k) {\n \t\t\tbool const cmp = *j < *i;\n@@ -133,10 +119,8 @@ namespace algorithms {\n \ttemplate<typename Iter, typename Iter2>\n \tvoid merge_runs_copy_half(Iter l, Iter m, Iter r, Iter2 B) {\n \t\tauto n1 = m-l, n2 = r-m;\n-\t\tif (COUNT_MERGE_COSTS) totalMergeCosts += (n1+n2);\n         if (n1 <= n2) {\n             std::copy(l,m,B);\n-            if (COUNT_MERGE_COSTS) totalBufferCosts += (m-l);\n             auto c1 = B, e1 = B + n1;\n             auto c2 = m, e2 = r, o = l;\n             while (c1 < e1 && c2 < e2)\n@@ -144,7 +128,6 @@ namespace algorithms {\n             while (c1 < e1) *o++ = *c1++;\n         } else {\n             std::copy(m,r,B);\n-            if (COUNT_MERGE_COSTS) totalBufferCosts += (r-m);\n             auto c1 = m-1, s1 = l, o = r-1;\n             auto c2 = B+n2-1, s2 = B;\n             while (c1 >= s1 && c2 >= s2)\n@@ -161,9 +144,7 @@ namespace algorithms {\n \ttemplate<typename Iter, typename Iter2>\n \tvoid merge_runs_basic(Iter l, Iter m, Iter r, Iter2 B) {\n \t\tauto n1 = m-l, n2 = r-m;\n-\t\tif (COUNT_MERGE_COSTS) totalMergeCosts += (n1+n2);\n         std::copy(l,r,B);\n-        if (COUNT_MERGE_COSTS) totalBufferCosts += (n1+n2);\n         auto c1 = B, e1 = B + n1, c2 = e1, e2 = e1 + n2;\n         auto o = l;\n         while (c1 < e1 && c2 < e2)\n@@ -182,12 +163,10 @@ namespace algorithms {\n         typedef typename std::iterator_traits<Iter>::value_type T;\n         static_assert(std::numeric_limits<T>::is_specialized, \"Needs numeric type (for sentinels)\");\n         auto n1 = m-l, n2 = r-m;\n-\t\tif (COUNT_MERGE_COSTS) totalMergeCosts += (n1+n2);\n         std::copy(l, m, B);\n         *(B + (m - l)) = plus_inf_sentinel<T>();\n         std::copy(m, r, B + (m - l + 1));\n         *(B + (r - l) + 1) = plus_inf_sentinel<T>();\n-        if (COUNT_MERGE_COSTS) totalBufferCosts += (n1+n2+2);\n         auto c1 = B, c2 = B + (m - l + 1), o = l;\n         while (o < r) *o++ = *c1 <= *c2 ? *c1++ : *c2++;\n \t}\ndiff --git a/src/sorts/powersort.h b/src/sorts/powersort.h\nindex 93d2ace..6a0b36b 100644\n--- a/src/sorts/powersort.h\n+++ b/src/sorts/powersort.h\n@@ -24,7 +24,7 @@ namespace algorithms {\n \t\tBITWISE_LOOP,\n \t\tMOST_SIGNIFICANT_SET_BIT,\n \t};\n-\tstd::string to_string(node_power_implementations implementation) {\n+\tinline std::string to_string(node_power_implementations implementation) {\n \t\tswitch (implementation) {\n \t\t\tcase TRIVIAL: return \"TRIVIAL\";\n \t\t\tcase DIVISION_LOOP: return \"DIVISION_LOOP\";\n@@ -36,7 +36,7 @@ namespace algorithms {\n \t};\n \n \n-\tpower_t node_power_trivial(size_t begin, size_t end,\n+\tinline power_t node_power_trivial(size_t begin, size_t end,\n \t                            size_t beginA, size_t beginB, size_t endB) {\n \t\tsize_t n = end - begin;\n \t\tsize_t n1 = beginB - beginA, n2 = endB - beginB;\n@@ -51,7 +51,7 @@ namespace algorithms {\n \t\treturn k;\n \t}\n \n-    power_t node_power_div(size_t begin, size_t end,\n+    inline power_t node_power_div(size_t begin, size_t end,\n \t                        size_t beginA, size_t beginB, size_t endB) {\n \t\tsize_t twoN = 2*(end - begin); // 2*n\n \t\tsize_t n1 = beginB - beginA, n2 = endB - beginB; // lengths of runs\n@@ -66,7 +66,7 @@ namespace algorithms {\n \t\treturn k;\n \t}\n \n-    power_t node_power_bitwise(size_t begin, size_t end,\n+    inline power_t node_power_bitwise(size_t begin, size_t end,\n \t                            size_t beginA, size_t beginB, size_t endB) {\n \t\tsize_t n = end - begin;\n \t\tassert (n < (size_t{1} << 63));\n@@ -87,7 +87,7 @@ namespace algorithms {\n \t\treturn nCommonBits + 1;\n \t}\n \n-    power_t node_power_clz(size_t begin, size_t end,\n+    inline power_t node_power_clz(size_t begin, size_t end,\n \t                        size_t beginA, size_t beginB, size_t endB) {\n \t\tsize_t n = end - begin;\n \t\tassert(n <= (size_t{1} << 31));\n@@ -99,7 +99,7 @@ namespace algorithms {\n \t}\n \n \t// not precise enough for large powers ...\n-    power_t node_power_clz_unconstrained(ptrdiff_t begin, ptrdiff_t end,\n+    inline power_t node_power_clz_unconstrained(ptrdiff_t begin, ptrdiff_t end,\n \t                                      ptrdiff_t beginA, ptrdiff_t beginB, ptrdiff_t endB) {\n \t\tassert(begin <= beginA && beginA <= beginB && beginB <= endB && endB <= end);\n \t\tauto n = static_cast<size_t>(end - begin);\n@@ -128,12 +128,12 @@ namespace algorithms {\n \t\t}\n \t}\n \n-\tunsigned floor_log2(unsigned int n) {\n+\tinline unsigned floor_log2(unsigned int n) {\n \t\tif (n <= 0) return 0;\n \t\treturn 31 - __builtin_clz( n );\n \t}\n \n-\tunsigned floor_log2(unsigned long n) {\n+\tinline unsigned floor_log2(unsigned long n) {\n \t\tif (n <= 0) return 0;\n \t\treturn 63 - __builtin_clzl( n );\n \t}\n"
  },
  {
    "path": "third_party/powersort/fix_sign_compare_warning.patch",
    "content": "diff --git a/src/sorts/powersort.h b/src/sorts/powersort.h\nindex 54ab704..93d2ace 100644\n--- a/src/sorts/powersort.h\n+++ b/src/sorts/powersort.h\n@@ -69,7 +69,7 @@ namespace algorithms {\n     power_t node_power_bitwise(size_t begin, size_t end,\n \t                            size_t beginA, size_t beginB, size_t endB) {\n \t\tsize_t n = end - begin;\n-\t\tassert (n < (1L << 63));\n+\t\tassert (n < (size_t{1} << 63));\n \t\tsize_t l = beginA - begin + beginB - begin;\n \t\tsize_t r = beginB - begin + endB - begin;\n \t\t// a and b are given by l/(2*n) and r/(2*n), both are in [0,1).\n@@ -90,7 +90,7 @@ namespace algorithms {\n     power_t node_power_clz(size_t begin, size_t end,\n \t                        size_t beginA, size_t beginB, size_t endB) {\n \t\tsize_t n = end - begin;\n-\t\tassert(n <= (1L << 31));\n+\t\tassert(n <= (size_t{1} << 31));\n \t\tunsigned long l2 = beginA + beginB - 2*begin; // 2*l\n \t\tunsigned long r2 = beginB + endB - 2*begin;   // 2*r\n \t\tauto a = static_cast<unsigned int>((l2 << 30) / n);\n@@ -103,7 +103,7 @@ namespace algorithms {\n \t                                      ptrdiff_t beginA, ptrdiff_t beginB, ptrdiff_t endB) {\n \t\tassert(begin <= beginA && beginA <= beginB && beginB <= endB && endB <= end);\n \t\tauto n = static_cast<size_t>(end - begin);\n-\t\tassert(n < (1L << 63));\n+\t\tassert(n < (size_t{1} << 63));\n \t\tauto l2 = static_cast<size_t>((beginA - begin) + (beginB - begin)); // 2*l\n \t\tauto r2 = static_cast<size_t>((beginB - begin) + (endB - begin));   // 2*r\n \t\tstatic_assert(sizeof(size_t) == 8, \"assume 64bit size_t\"); // can compute with 64 bits\n"
  },
  {
    "path": "third_party/powersort/fix_static_assertion.patch",
    "content": "diff --git a/src/sorts/merging.h b/src/sorts/merging.h\nindex 9afc08f..835b6d2 100644\n--- a/src/sorts/merging.h\n+++ b/src/sorts/merging.h\n@@ -254,27 +254,23 @@ namespace algorithms {\n     template<merging_methods mergingMethod,\n             typename Iter, typename Iter2>\n     void merge_runs(Iter l, Iter m, Iter r, Iter2 B) {\n-        switch(mergingMethod) {\n-            case UNSTABLE_BITONIC_MERGE:\n-                return merge_runs_bitonic(l, m, r, B);\n-            case UNSTABLE_BITONIC_MERGE_MANUAL_COPY:\n-                return merge_runs_bitonic_manual_copy(l, m, r, B);\n-            case UNSTABLE_BITONIC_MERGE_BRANCHLESS:\n-                return merge_runs_bitonic_branchless(l, m, r, B);\n-            case COPY_SMALLER:\n-                return merge_runs_copy_half(l, m, r, B);\n-            case COPY_BOTH:\n-                return merge_runs_basic(l, m, r, B);\n-            case COPY_BOTH_WITH_SENTINELS:\n-                return merge_runs_basic_sentinels(l, m, r, B);\n-            default:\n-                assert(false);\n-                __builtin_unreachable();\n-        }\n-    }\n-\n-\n-\n+\t\t\tif constexpr (mergingMethod == UNSTABLE_BITONIC_MERGE) {\n+\t\t\t\treturn merge_runs_bitonic(l, m, r, B);\n+\t\t\t} else if constexpr (mergingMethod == UNSTABLE_BITONIC_MERGE_MANUAL_COPY) {\n+\t\t\t\treturn merge_runs_bitonic_manual_copy(l, m, r, B);\n+\t\t\t} else if constexpr (mergingMethod == UNSTABLE_BITONIC_MERGE_BRANCHLESS) {\n+\t\t\t\treturn merge_runs_bitonic_branchless(l, m, r, B);\n+\t\t\t} else if constexpr (mergingMethod == COPY_SMALLER) {\n+\t\t\t\treturn merge_runs_copy_half(l, m, r, B);\n+\t\t\t} else if constexpr (mergingMethod == COPY_BOTH) {\n+\t\t\t\treturn merge_runs_basic(l, m, r, B);\n+\t\t\t} else if constexpr (mergingMethod == COPY_BOTH_WITH_SENTINELS) {\n+\t\t\t\treturn merge_runs_basic_sentinels(l, m, r, B);\n+\t\t\t} else {\n+\t\t\t\tassert(false);\n+\t\t\t\t__builtin_unreachable();\n+ \t\t  }\n+\t\t}\n }\n \n #endif //MERGESORTS_MERGING_H\n"
  },
  {
    "path": "third_party/powersort/powersort.BUILD",
    "content": "load(\"@rules_cc//cc:defs.bzl\", \"cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncc_library(\n    name = \"powersort\",\n    hdrs = [\n        \"src/algorithms.h\",\n        \"src/sorts/insertionsort.h\",\n        \"src/sorts/merging.h\",\n        \"src/sorts/powersort.h\",\n    ],\n    include_prefix = \"third_party/powersort/include\",\n    strip_include_prefix = \"src\",\n)\n"
  },
  {
    "path": "third_party/powersort/remove_binary_function.patch",
    "content": "diff --git a/src/algorithms.h b/src/algorithms.h\nindex 4b94d9b..a2a2576 100644\n--- a/src/algorithms.h\n+++ b/src/algorithms.h\n@@ -16,7 +16,7 @@ namespace algorithms {\n \n \t/** superclass for sorting methods */\n \ttemplate<typename Iterator>\n-\tclass sorter : std::binary_function<Iterator, Iterator, void> {\n+\tclass sorter {\n \tprotected:\n \t\tusing elem_t = typename std::iterator_traits<Iterator>::value_type ;\n \t\tusing diff_t = typename std::iterator_traits<Iterator>::difference_type ;\n"
  },
  {
    "path": "third_party/powersort/workspace.bzl",
    "content": "\"\"\"loads the powersort library, used by Tachyon.\"\"\"\n\nload(\"//third_party:repo.bzl\", \"tachyon_http_archive\", \"tf_mirror_urls\")\n\ndef repo():\n    tachyon_http_archive(\n        name = \"powersort\",\n        urls = tf_mirror_urls(\"https://github.com/sebawild/powersort/archive/48e31e909280ca43bb2c33dd3df9922b0a0f3f84.tar.gz\"),\n        sha256 = \"89122b7e7e2a0f0b41cc5411f9adde581769ff2f7d141335ce7e5011b932da06\",\n        strip_prefix = \"powersort-48e31e909280ca43bb2c33dd3df9922b0a0f3f84\",\n        build_file = \"//third_party/powersort:powersort.BUILD\",\n        patch_file = [\n            \"@kroma_network_tachyon//third_party/powersort:fix_sign_compare_warning.patch\",\n            \"@kroma_network_tachyon//third_party/powersort:fix_multiple_definitions.patch\",\n            \"@kroma_network_tachyon//third_party/powersort:fix_static_assertion.patch\",\n            # In c++ 17, std::binary_function is removed.\n            # See https://en.cppreference.com/w/cpp/utility/functional/binary_function.\n            \"@kroma_network_tachyon//third_party/powersort:remove_binary_function.patch\",\n        ],\n    )\n"
  },
  {
    "path": "third_party/py/BUILD.bazel",
    "content": ""
  },
  {
    "path": "third_party/py/BUILD.tpl",
    "content": "licenses([\"restricted\"])\n\npackage(default_visibility = [\"//visibility:public\"])\n\n# Point both runtimes to the same python binary to ensure we always\n# use the python binary specified by ./configure.py script.\nload(\"@bazel_tools//tools/python:toolchain.bzl\", \"py_runtime_pair\")\n\npy_runtime(\n    name = \"py2_runtime\",\n    interpreter_path = \"%{PYTHON_BIN_PATH}\",\n    python_version = \"PY2\",\n)\n\npy_runtime(\n    name = \"py3_runtime\",\n    interpreter_path = \"%{PYTHON_BIN_PATH}\",\n    python_version = \"PY3\",\n)\n\npy_runtime_pair(\n    name = \"py_runtime_pair\",\n    py2_runtime = \":py2_runtime\",\n    py3_runtime = \":py3_runtime\",\n)\n\ntoolchain(\n    name = \"py_toolchain\",\n    toolchain = \":py_runtime_pair\",\n    toolchain_type = \"@bazel_tools//tools/python:toolchain_type\",\n    target_compatible_with = [%{PLATFORM_CONSTRAINT}],\n    exec_compatible_with = [%{PLATFORM_CONSTRAINT}],\n)\n\n# To build Python C/C++ extension on Windows, we need to link to python import library pythonXY.lib\n# See https://docs.python.org/3/extending/windows.html\ncc_import(\n    name = \"python_lib\",\n    interface_library = select({\n        \":windows\": \":python_import_lib\",\n        # A placeholder for Unix platforms which makes --no_build happy.\n        \"//conditions:default\": \"not-existing.lib\",\n    }),\n    system_provided = 1,\n)\n\ncc_library(\n    name = \"python_headers\",\n    hdrs = [\":python_include\"],\n    deps = select({\n        \":windows\": [\":python_lib\"],\n        \"//conditions:default\": [],\n    }),\n    includes = [\"python_include\"],\n)\n\ncc_library(\n    name = \"python_embed\",\n    hdrs = [\":python_include\"],\n    deps = select({\n        \":windows\": [\":python_lib\"],\n        \"//conditions:default\": [],\n    }),\n    includes = [\"python_include\"],\n    linkopts = [\"%{PYTHON_EMBED_LINKOPTS}\"],\n)\n\n# This alias is exists for the use of targets in the @llvm-project dependency,\n# which expect a python_headers target called @python_runtime//:headers. We use\n# a repo_mapping to alias python_runtime to this package, and an alias to create\n# the correct target.\nalias(\n    name = \"headers\",\n    actual = \":python_headers\",\n)\n\ncc_library(\n    name = \"numpy_headers\",\n    hdrs = [\":numpy_include\"],\n    includes = [\"numpy_include\"],\n)\n\nconfig_setting(\n    name = \"windows\",\n    values = {\"cpu\": \"x64_windows\"},\n    visibility = [\"//visibility:public\"],\n)\n\n%{PYTHON_INCLUDE_GENRULE}\n%{NUMPY_INCLUDE_GENRULE}\n%{PYTHON_IMPORT_LIB_GENRULE}\n"
  },
  {
    "path": "third_party/py/numpy/BUILD",
    "content": "licenses([\"restricted\"])\n\npackage(default_visibility = [\"//visibility:public\"])\n\npy_library(\n    name = \"numpy\",\n    srcs = [\"tachyon_numpy_dummy.py\"],\n    srcs_version = \"PY3\",\n)\n\nalias(\n    name = \"headers\",\n    actual = \"@local_config_python//:numpy_headers\",\n)\n\ngenrule(\n    name = \"dummy\",\n    outs = [\"tachyon_numpy_dummy.py\"],\n    cmd = \"touch $@\",\n    visibility = [\"//visibility:private\"],\n)\n"
  },
  {
    "path": "third_party/py/numpy/LICENSE",
    "content": "Copyright (c) 2005-2019, NumPy Developers.\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n    * Redistributions of source code must retain the above copyright\n       notice, this list of conditions and the following disclaimer.\n\n    * Redistributions in binary form must reproduce the above\n       copyright notice, this list of conditions and the following\n       disclaimer in the documentation and/or other materials provided\n       with the distribution.\n\n    * Neither the name of the NumPy Developers nor the names of any\n       contributors may be used to endorse or promote products derived\n       from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n\n\nThe NumPy repository and source distributions bundle several libraries that are\ncompatibly licensed.  We list these here.\n\nName: Numpydoc\nFiles: doc/sphinxext/numpydoc/*\nLicense: 2-clause BSD\n  For details, see doc/sphinxext/LICENSE.txt\n\nName: scipy-sphinx-theme\nFiles: doc/scipy-sphinx-theme/*\nLicense: 3-clause BSD, PSF and Apache 2.0\n  For details, see doc/scipy-sphinx-theme/LICENSE.txt\n\nName: lapack-lite\nFiles: numpy/linalg/lapack_lite/*\nLicense: 3-clause BSD\n  For details, see numpy/linalg/lapack_lite/LICENSE.txt\n\nName: tempita\nFiles: tools/npy_tempita/*\nLicense: BSD derived\n  For details, see tools/npy_tempita/license.txt\n\nName: dragon4\nFiles: numpy/core/src/multiarray/dragon4.c\nLicense: One of a kind\n  For license text, see numpy/core/src/multiarray/dragon4.c\n"
  },
  {
    "path": "third_party/py/python_configure.bzl",
    "content": "\"\"\"Repository rule for Python autoconfiguration.\n\n`python_configure` depends on the following environment variables:\n\n  * `PYTHON_BIN_PATH`: location of python binary.\n  * `PYTHON_LIB_PATH`: Location of python libraries.\n\"\"\"\n\nload(\n    \"//third_party/remote_config:common.bzl\",\n    \"BAZEL_SH\",\n    \"PYTHON_BIN_PATH\",\n    \"PYTHON_CONFIG_BIN_PATH\",\n    \"PYTHON_LIB_PATH\",\n    \"TACHYON_PYTHON_CONFIG_REPO\",\n    \"auto_config_fail\",\n    \"config_repo_label\",\n    \"execute\",\n    \"get_bash_bin\",\n    \"get_host_environ\",\n    \"get_python_bin\",\n    \"is_windows\",\n    \"raw_exec\",\n    \"read_dir\",\n)\n\ndef _genrule(src_dir, genrule_name, command, outs):\n    \"\"\"Returns a string with a genrule.\n\n    Genrule executes the given command and produces the given outputs.\n    \"\"\"\n    return (\n        \"genrule(\\n\" +\n        '    name = \"' +\n        genrule_name + '\",\\n' +\n        \"    outs = [\\n\" +\n        outs +\n        \"\\n    ],\\n\" +\n        '    cmd = \"\"\"\\n' +\n        command +\n        '\\n   \"\"\",\\n' +\n        \")\\n\"\n    )\n\ndef _norm_path(path):\n    \"\"\"Returns a path with '/' and remove the trailing slash.\"\"\"\n    path = path.replace(\"\\\\\", \"/\")\n    if path[-1] == \"/\":\n        path = path[:-1]\n    return path\n\ndef _symlink_genrule_for_dir(\n        repository_ctx,\n        src_dir,\n        dest_dir,\n        genrule_name,\n        src_files = [],\n        dest_files = []):\n    \"\"\"Returns a genrule to symlink(or copy if on Windows) a set of files.\n\n    If src_dir is passed, files will be read from the given directory; otherwise\n    we assume files are in src_files and dest_files\n    \"\"\"\n    if src_dir != None:\n        src_dir = _norm_path(src_dir)\n        dest_dir = _norm_path(dest_dir)\n        files = \"\\n\".join(read_dir(repository_ctx, src_dir))\n\n        # Create a list with the src_dir stripped to use for outputs.\n        dest_files = files.replace(src_dir, \"\").splitlines()\n        src_files = files.splitlines()\n    command = []\n    outs = []\n    for i in range(len(dest_files)):\n        if dest_files[i] != \"\":\n            # If we have only one file to link we do not want to use the dest_dir, as\n            # $(@D) will include the full path to the file.\n            dest = \"$(@D)/\" + dest_dir + dest_files[i] if len(dest_files) != 1 else \"$(@D)/\" + dest_files[i]\n\n            # Copy the headers to create a sandboxable setup.\n            cmd = \"cp -f\"\n            command.append(cmd + ' \"%s\" \"%s\"' % (src_files[i], dest))\n            outs.append('        \"' + dest_dir + dest_files[i] + '\",')\n    genrule = _genrule(\n        src_dir,\n        genrule_name,\n        \" && \".join(command),\n        \"\\n\".join(outs),\n    )\n    return genrule\n\ndef _get_python_lib(repository_ctx, python_bin):\n    \"\"\"Gets the python lib path.\"\"\"\n    python_lib = get_host_environ(repository_ctx, PYTHON_LIB_PATH)\n    if python_lib != None:\n        return python_lib\n\n    # The interesting program to execute.\n    print_lib = [\n        \"from __future__ import print_function\",\n        \"import site\",\n        \"import os\",\n        \"python_paths = []\",\n        \"if os.getenv('PYTHONPATH') is not None:\",\n        \"  python_paths = os.getenv('PYTHONPATH').split(':')\",\n        \"try:\",\n        \"  library_paths = site.getsitepackages()\",\n        \"except AttributeError:\",\n        \"  from distutils.sysconfig import get_python_lib\",\n        \"  library_paths = [get_python_lib()]\",\n        \"all_paths = set(python_paths + library_paths)\",\n        \"paths = []\",\n        \"for path in all_paths:\",\n        \"  if os.path.isdir(path):\",\n        \"    paths.append(path)\",\n        \"if len(paths) >=1:\",\n        \"  print(paths[0])\",\n    ]\n\n    # The below script writes the above program to a file\n    # and executes it. This is to work around the limitation\n    # of not being able to upload files as part of execute.\n    cmd = \"from os import linesep;\"\n    cmd += \"f = open('script.py', 'w');\"\n    for line in print_lib:\n        cmd += \"f.write(\\\"%s\\\" + linesep);\" % line\n    cmd += \"f.close();\"\n    cmd += \"from subprocess import call;\"\n    cmd += \"call([\\\"%s\\\", \\\"script.py\\\"]);\" % python_bin\n\n    result = execute(repository_ctx, [python_bin, \"-c\", cmd])\n    return result.stdout.strip()\n\ndef _check_python_lib(repository_ctx, python_lib):\n    \"\"\"Checks the python lib path.\"\"\"\n    cmd = 'test -d \"%s\" -a -x \"%s\"' % (python_lib, python_lib)\n    result = raw_exec(repository_ctx, [get_bash_bin(repository_ctx), \"-c\", cmd])\n    if result.return_code == 1:\n        auto_config_fail(\"Invalid python library path: %s\" % python_lib)\n\ndef _check_python_bin(repository_ctx, python_bin):\n    \"\"\"Checks the python bin path.\"\"\"\n    cmd = '[[ -x \"%s\" ]] && [[ ! -d \"%s\" ]]' % (python_bin, python_bin)\n    result = raw_exec(repository_ctx, [get_bash_bin(repository_ctx), \"-c\", cmd])\n    if result.return_code == 1:\n        auto_config_fail(\"--define %s='%s' is not executable. Is it the python binary?\" % (\n            PYTHON_BIN_PATH,\n            python_bin,\n        ))\n\ndef _get_python_include(repository_ctx, python_bin):\n    \"\"\"Gets the python include path.\"\"\"\n    result = execute(\n        repository_ctx,\n        [\n            python_bin,\n            \"-Wignore\",\n            \"-c\",\n            \"import importlib; \" +\n            \"import importlib.util; \" +\n            \"print(importlib.import_module('distutils.sysconfig').get_python_inc() \" +\n            \"if importlib.util.find_spec('distutils.sysconfig') \" +\n            \"else importlib.import_module('sysconfig').get_path('include'))\",\n        ],\n        error_msg = \"Problem getting python include path.\",\n        error_details = (\"Is the Python binary path set up right? \" +\n                         \"(See ./configure or \" + PYTHON_BIN_PATH + \".) \" +\n                         \"Is distutils installed?\"),\n    )\n    return result.stdout.splitlines()[0]\n\ndef _get_python_import_lib_name(repository_ctx, python_bin):\n    \"\"\"Get Python import library name (pythonXY.lib) on Windows.\"\"\"\n    result = execute(\n        repository_ctx,\n        [\n            python_bin,\n            \"-c\",\n            \"import sys;\" +\n            'print(\"python\" + str(sys.version_info[0]) + ' +\n            '      str(sys.version_info[1]) + \".lib\")',\n        ],\n        error_msg = \"Problem getting python import library.\",\n        error_details = (\"Is the Python binary path set up right? \" +\n                         \"(See ./configure or \" + PYTHON_BIN_PATH + \".) \"),\n    )\n    return result.stdout.splitlines()[0]\n\ndef _find_python_config(repository_ctx, python_bin):\n    \"\"\"Searches for python-config in the Python bin directory through the Python environment symlinks\n\n    Returns a string path to python-config, or None if not found\n    \"\"\"\n    python_config = repository_ctx.os.environ.get(PYTHON_CONFIG_BIN_PATH)\n    if python_config != None:\n        if is_windows(repository_ctx):\n            python_config = _norm_path(python_config)\n        return python_config\n\n    bin_dir = repository_ctx.path(python_bin).dirname\n    bin_name = \"python-config\"\n\n    for i in bin_dir.readdir():\n        if bin_name == i.basename:\n            return str(bin_dir.get_child(bin_name))\n\n    symlink_base_dir = repository_ctx.path(python_bin).realpath.dirname\n    for i in symlink_base_dir.readdir():\n        if bin_name == i.basename:\n            return str(symlink_base_dir.get_child(bin_name))\n\n    return None\n\ndef _get_embed_flags(repository_ctx, python_config):\n    \"\"\"Identifies compiler and linker flags output by python-config required to embed Python\n\n    Returns a linkopts, or empty strings if unsuccessful.\n    \"\"\"\n    err_cmd = python_config + \" --help\"\n\n    # --embed is an undocumented python >=3.8 flag.\n    # See https://github.com/python/cpython/pull/13500\n    link_cmd = python_config + \" --ldflags --embed\"\n\n    err = repository_ctx.execute([get_bash_bin(repository_ctx), \"-c\", err_cmd]).stdout.strip(\"\\n\")\n    linker_flags = repository_ctx.execute([get_bash_bin(repository_ctx), \"-c\", link_cmd]).stdout.strip(\"\\n\")\n\n    if linker_flags == err:\n        # Try again without --embed\n        link_cmd = python_config + \" --ldflags\"\n        linker_flags = repository_ctx.execute([get_bash_bin(repository_ctx), \"-c\", link_cmd]).stdout.strip(\"\\n\")\n\n    if linker_flags == err:\n        return \"\", \"\"\n\n    return linker_flags\n\ndef _get_numpy_include(repository_ctx, python_bin):\n    \"\"\"Gets the numpy include path.\"\"\"\n    return execute(\n        repository_ctx,\n        [\n            python_bin,\n            \"-c\",\n            \"from __future__ import print_function;\" +\n            \"import numpy;\" +\n            \" print(numpy.get_include());\",\n        ],\n        error_msg = \"Problem getting numpy include path.\",\n        error_details = \"Is numpy installed?\",\n    ).stdout.splitlines()[0]\n\ndef _create_local_python_repository(repository_ctx):\n    \"\"\"Creates the repository containing files set up to build with Python.\"\"\"\n\n    # Resolve all labels before doing any real work. Resolving causes the\n    # function to be restarted with all previous state being lost. This\n    # can easily lead to a O(n^2) runtime in the number of labels.\n    build_tpl = repository_ctx.path(Label(\"//third_party/py:BUILD.tpl\"))\n\n    python_bin = get_python_bin(repository_ctx)\n    _check_python_bin(repository_ctx, python_bin)\n    python_lib = _get_python_lib(repository_ctx, python_bin)\n    _check_python_lib(repository_ctx, python_lib)\n    python_include = _get_python_include(repository_ctx, python_bin)\n    numpy_include = _get_numpy_include(repository_ctx, python_bin) + \"/numpy\"\n    python_include_rule = _symlink_genrule_for_dir(\n        repository_ctx,\n        python_include,\n        \"python_include\",\n        \"python_include\",\n    )\n\n    # To embed python in C++, we need the linker and compiler flags required to embed the Python interpreter\n    # See https://docs.python.org/3/extending/embedding.html#embedding-python-in-c\n    python_config = _find_python_config(repository_ctx, python_bin)\n    python_embed_linkopts = \"\"\n    if python_config:\n        python_embed_linkopts = _get_embed_flags(repository_ctx, python_config)\n\n    python_import_lib_genrule = \"\"\n\n    # To build Python C/C++ extension on Windows, we need to link to python import library pythonXY.lib\n    # See https://docs.python.org/3/extending/windows.html\n    if is_windows(repository_ctx):\n        python_bin = python_bin.replace(\"\\\\\", \"/\")\n        python_include = _norm_path(python_include)\n        python_import_lib_name = _get_python_import_lib_name(repository_ctx, python_bin)\n        python_import_lib_src = python_include.rsplit(\"/\", 1)[0] + \"/libs/\" + python_import_lib_name\n        python_import_lib_genrule = _symlink_genrule_for_dir(\n            repository_ctx,\n            None,\n            \"\",\n            \"python_import_lib\",\n            [python_import_lib_src],\n            [python_import_lib_name],\n        )\n    numpy_include_rule = _symlink_genrule_for_dir(\n        repository_ctx,\n        numpy_include,\n        \"numpy_include/numpy\",\n        \"numpy_include\",\n    )\n\n    platform_constraint = \"\"\n    if repository_ctx.attr.platform_constraint:\n        platform_constraint = \"\\\"%s\\\"\" % repository_ctx.attr.platform_constraint\n    repository_ctx.template(\"BUILD\", build_tpl, {\n        \"%{PYTHON_BIN_PATH}\": python_bin,\n        \"%{PYTHON_INCLUDE_GENRULE}\": python_include_rule,\n        \"%{PYTHON_IMPORT_LIB_GENRULE}\": python_import_lib_genrule,\n        \"%{PYTHON_EMBED_LINKOPTS}\": python_embed_linkopts,\n        \"%{NUMPY_INCLUDE_GENRULE}\": numpy_include_rule,\n        \"%{PLATFORM_CONSTRAINT}\": platform_constraint,\n    })\n\ndef _create_remote_python_repository(repository_ctx, remote_config_repo):\n    \"\"\"Creates pointers to a remotely configured repo set up to build with Python.\n    \"\"\"\n    repository_ctx.template(\"BUILD\", config_repo_label(remote_config_repo, \":BUILD\"), {})\n\ndef _python_autoconf_impl(repository_ctx):\n    \"\"\"Implementation of the python_autoconf repository rule.\"\"\"\n    if get_host_environ(repository_ctx, TACHYON_PYTHON_CONFIG_REPO) != None:\n        _create_remote_python_repository(\n            repository_ctx,\n            get_host_environ(repository_ctx, TACHYON_PYTHON_CONFIG_REPO),\n        )\n    else:\n        _create_local_python_repository(repository_ctx)\n\n_ENVIRONS = [\n    BAZEL_SH,\n    PYTHON_BIN_PATH,\n    PYTHON_LIB_PATH,\n]\n\nlocal_python_configure = repository_rule(\n    implementation = _create_local_python_repository,\n    environ = _ENVIRONS,\n    attrs = {\n        \"environ\": attr.string_dict(),\n        \"platform_constraint\": attr.string(),\n    },\n)\n\nremote_python_configure = repository_rule(\n    implementation = _create_local_python_repository,\n    environ = _ENVIRONS,\n    # NOTE(chokobole): Do we need this?\n    # See https://github.com/google/mediapipe/issues/1744\n    # remotable = True,\n    attrs = {\n        \"environ\": attr.string_dict(),\n        \"platform_constraint\": attr.string(),\n    },\n)\n\npython_configure = repository_rule(\n    implementation = _python_autoconf_impl,\n    environ = _ENVIRONS + [TACHYON_PYTHON_CONFIG_REPO],\n    attrs = {\n        \"platform_constraint\": attr.string(),\n    },\n)\n\"\"\"Detects and configures the local Python.\n\nAdd the following to your WORKSPACE FILE:\n\n```python\npython_configure(name = \"local_config_python\")\n```\n\nArgs:\n  name: A unique name for this workspace rule.\n\"\"\"\n"
  },
  {
    "path": "third_party/rapidjson/rapidjson.BUILD",
    "content": "load(\"@rules_cc//cc:defs.bzl\", \"cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"license.txt\"])\n\nlicenses([\"notice\"])  # MIT, BSD and JSON\n\ncc_library(\n    name = \"rapidjson\",\n    hdrs = glob([\"include/rapidjson/**/*.h\"]),\n    defines = [\"RAPIDJSON_HAS_STDSTRING\"],\n    includes = [\"include\"],\n)\n"
  },
  {
    "path": "third_party/rapidsnark/BUILD.bazel",
    "content": ""
  },
  {
    "path": "third_party/rapidsnark/nozk.patch",
    "content": "diff --git a/src/groth16.cpp b/src/groth16.cpp\nindex e7eda1f..7d99076 100644\n--- a/src/groth16.cpp\n+++ b/src/groth16.cpp\n@@ -244,8 +244,10 @@ std::unique_ptr<Proof<Engine>> Prover<Engine>::prove(typename Engine::FrElement\n     E.fr.copy(r, E.fr.zero());\n     E.fr.copy(s, E.fr.zero());\n \n+#ifndef NOZK\n     randombytes_buf((void *)&(r.v[0]), sizeof(r)-1);\n     randombytes_buf((void *)&(s.v[0]), sizeof(s)-1);\n+#endif\n \n #ifndef USE_OPENMP\n     pA_future.get();\n"
  },
  {
    "path": "third_party/rapidsnark/rapidsnark.BUILD",
    "content": "load(\"@kroma_network_tachyon//bazel:tachyon.bzl\", \"if_has_openmp\")\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\", \"cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncc_library(\n    name = \"rapidsnark\",\n    srcs = [\n        \"src/binfile_utils.cpp\",\n        \"src/fileloader.cpp\",\n        \"src/fullprover.cpp\",\n        \"src/logger.cpp\",\n        \"src/prover.cpp\",\n        \"src/verifier.cpp\",\n        \"src/wtns_utils.cpp\",\n        \"src/zkey_utils.cpp\",\n    ],\n    hdrs = [\n        \"src/binfile_utils.hpp\",\n        \"src/fileloader.hpp\",\n        \"src/fullprover.hpp\",\n        \"src/groth16.cpp\",\n        \"src/groth16.hpp\",\n        \"src/logger.hpp\",\n        \"src/logging.hpp\",\n        \"src/prover.h\",\n        \"src/random_generator.hpp\",\n        \"src/verifier.h\",\n        \"src/wtns_utils.hpp\",\n        \"src/zkey_utils.hpp\",\n    ],\n    copts = if_has_openmp([\"-fopenmp\"]),\n    defines = [\"NOZK\"] + if_has_openmp([\"USE_OPENMP\"]),\n    includes = [\"src\"],\n    linkopts = if_has_openmp([\"-fopenmp\"]),\n    deps = [\n        \"@iden3_ffiasm//c\",\n        \"@nlohmann_json//:json\",\n    ],\n)\n\ncc_binary(\n    name = \"main_prover\",\n    srcs = [\"src/main_prover.cpp\"],\n    deps = [\":rapidsnark\"],\n)\n\ncc_binary(\n    name = \"main_verifier\",\n    srcs = [\"src/main_verifier.cpp\"],\n    deps = [\":rapidsnark\"],\n)\n\ncc_binary(\n    name = \"test_public_size\",\n    srcs = [\"src/test_public_size.c\"],\n    deps = [\":rapidsnark\"],\n)\n\ncc_binary(\n    name = \"test_prover\",\n    srcs = [\"src/test_prover.cpp\"],\n    deps = [\":rapidsnark\"],\n)\n"
  },
  {
    "path": "third_party/rapidsnark/workspace.bzl",
    "content": "\"\"\"loads the rapidsnark library for benchmarking purposes.\"\"\"\n\nload(\"//third_party:repo.bzl\", \"tachyon_http_archive\", \"tf_mirror_urls\")\n\ndef repo():\n    tachyon_http_archive(\n        name = \"iden3_rapidsnark\",\n        urls = tf_mirror_urls(\"https://github.com/iden3/rapidsnark/archive/9fa3f0bc7a31ec791b937d233ef0b444d73eda8b.tar.gz\"),\n        sha256 = \"7f03f95a1bbb6d83c0399f3b962d2b05b5a94574050d6c45c492acc3c07a363b\",\n        strip_prefix = \"rapidsnark-9fa3f0bc7a31ec791b937d233ef0b444d73eda8b\",\n        build_file = \"//third_party/rapidsnark:rapidsnark.BUILD\",\n        patch_file = [\"@kroma_network_tachyon//third_party/rapidsnark:nozk.patch\"],\n    )\n"
  },
  {
    "path": "third_party/remote_config/BUILD.bazel",
    "content": ""
  },
  {
    "path": "third_party/remote_config/BUILD.tpl",
    "content": "# Each platform creates a constraint @<platform>//:platform_constraint that\n# is listed in its constraint_values; rule that want to select a specific\n# platform to run on can put @<platform>//:platform_constraing into their\n# exec_compatible_with attribute.\n# Toolchains can similarly be marked with target_compatible_with or\n# exec_compatible_with to bind them to this platform.\nconstraint_setting(\n    name = \"platform_setting\"\n)\n\nconstraint_value(\n    name = \"platform_constraint\",\n    constraint_setting = \":platform_setting\",\n    visibility = [\"//visibility:public\"],\n)\n\nplatform(\n    name = \"platform\",\n    visibility = [\"//visibility:public\"],\n    constraint_values = [\n        \"@platforms//cpu:%{cpu}\",\n        \"@platforms//os:%{platform}\",\n        \":platform_constraint\",\n    ],\n    exec_properties = %{exec_properties},\n)\n"
  },
  {
    "path": "third_party/remote_config/common.bzl",
    "content": "\"\"\"Functions common across configure rules.\"\"\"\n\nBAZEL_SH = \"BAZEL_SH\"\nPYTHON_BIN_PATH = \"PYTHON_BIN_PATH\"\nPYTHON_CONFIG_BIN_PATH = \"_PYTHON_CONFIG_BIN_PATH\"\nPYTHON_LIB_PATH = \"PYTHON_LIB_PATH\"\nTACHYON_PYTHON_CONFIG_REPO = \"TACHYON_PYTHON_CONFIG_REPO\"\n\ndef auto_config_fail(msg):\n    \"\"\"Output failure message when auto configuration fails.\"\"\"\n    red = \"\\033[0;31m\"\n    no_color = \"\\033[0m\"\n    fail(\"%sConfiguration Error:%s %s\\n\" % (red, no_color, msg))\n\ndef which(repository_ctx, program_name, allow_failure = False):\n    \"\"\"Returns the full path to a program on the execution platform.\n\n    Args:\n      repository_ctx: the repository_ctx\n      program_name: name of the program on the PATH\n\n    Returns:\n      The full path to a program on the execution platform.\n    \"\"\"\n    if is_windows(repository_ctx):\n        if not program_name.endswith(\".exe\"):\n            program_name = program_name + \".exe\"\n        out = execute(\n            repository_ctx,\n            [\"C:\\\\Windows\\\\System32\\\\where.exe\", program_name],\n            allow_failure = allow_failure,\n        ).stdout\n        if out != None:\n            out = out.replace(\"\\\\\", \"\\\\\\\\\").rstrip()\n        return out\n\n    out = execute(\n        repository_ctx,\n        [\"which\", program_name],\n        allow_failure = allow_failure,\n    ).stdout\n    if out != None:\n        out = out.replace(\"\\\\\", \"\\\\\\\\\").rstrip()\n    return out\n\ndef get_python_bin(repository_ctx):\n    \"\"\"Gets the python bin path.\n\n    Args:\n      repository_ctx: the repository_ctx\n\n    Returns:\n      The python bin path.\n    \"\"\"\n    python_bin = get_host_environ(repository_ctx, PYTHON_BIN_PATH)\n    if python_bin:\n        return python_bin\n\n    # First check for an explicit \"python3\"\n    python_bin = which(repository_ctx, \"python3\", True)\n    if python_bin:\n        return python_bin\n\n    # Some systems just call pythone3 \"python\"\n    python_bin = which(repository_ctx, \"python\", True)\n    if python_bin:\n        return python_bin\n\n    auto_config_fail(\"Cannot find python in PATH, please make sure \" +\n                     \"python is installed and add its directory in PATH, or --define \" +\n                     \"%s='/something/else'.\\nPATH=%s\" % (\n                         PYTHON_BIN_PATH,\n                         get_environ(repository_ctx, \"PATH\"),\n                     ))\n    return python_bin  # unreachable\n\ndef get_bash_bin(repository_ctx):\n    \"\"\"Gets the bash bin path.\n\n    Args:\n      repository_ctx: the repository_ctx\n\n    Returns:\n      The bash bin path.\n    \"\"\"\n    bash_bin = get_host_environ(repository_ctx, BAZEL_SH)\n    if bash_bin != None:\n        return bash_bin\n    bash_bin_path = which(repository_ctx, \"bash\")\n    if bash_bin_path == None:\n        auto_config_fail(\"Cannot find bash in PATH, please make sure \" +\n                         \"bash is installed and add its directory in PATH, or --define \" +\n                         \"%s='/path/to/bash'.\\nPATH=%s\" % (\n                             BAZEL_SH,\n                             get_environ(repository_ctx, \"PATH\"),\n                         ))\n    return bash_bin_path\n\ndef read_dir(repository_ctx, src_dir):\n    \"\"\"Returns a sorted list with all files in a directory.\n\n    Finds all files inside a directory, traversing subfolders and following\n    symlinks.\n\n    Args:\n      repository_ctx: the repository_ctx\n      src_dir: the directory to traverse\n\n    Returns:\n      A sorted list with all files in a directory.\n    \"\"\"\n    if is_windows(repository_ctx):\n        src_dir = src_dir.replace(\"/\", \"\\\\\")\n        find_result = execute(\n            repository_ctx,\n            [\"C:\\\\Windows\\\\System32\\\\cmd.exe\", \"/c\", \"dir\", src_dir, \"/b\", \"/s\", \"/a-d\"],\n            allow_failure = True,\n        )\n\n        # src_files will be used in genrule.outs where the paths must\n        # use forward slashes.\n        result = find_result.stdout.replace(\"\\\\\", \"/\")\n    else:\n        find_result = execute(\n            repository_ctx,\n            [\"find\", src_dir, \"-follow\", \"-type\", \"f\"],\n            allow_failure = True,\n        )\n        result = find_result.stdout\n    return sorted(result.splitlines())\n\ndef get_environ(repository_ctx, name, default_value = None):\n    \"\"\"Returns the value of an environment variable on the execution platform.\n\n    Args:\n      repository_ctx: the repository_ctx\n      name: the name of environment variable\n      default_value: the value to return if not set\n\n    Returns:\n      The value of the environment variable 'name' on the execution platform\n      or 'default_value' if it's not set.\n    \"\"\"\n    if is_windows(repository_ctx):\n        result = execute(\n            repository_ctx,\n            [\"C:\\\\Windows\\\\System32\\\\cmd.exe\", \"/c\", \"echo\", \"%\" + name + \"%\"],\n            allow_failure = True,\n        )\n    else:\n        cmd = \"echo -n \\\"$%s\\\"\" % name\n        result = execute(\n            repository_ctx,\n            [get_bash_bin(repository_ctx), \"-c\", cmd],\n            allow_failure = True,\n        )\n    if len(result.stdout) == 0:\n        return default_value\n    return result.stdout\n\ndef get_host_environ(repository_ctx, name, default_value = None):\n    \"\"\"Returns the value of an environment variable on the host platform.\n\n    The host platform is the machine that Bazel runs on.\n\n    Args:\n      repository_ctx: the repository_ctx\n      name: the name of environment variable\n\n    Returns:\n      The value of the environment variable 'name' on the host platform.\n    \"\"\"\n    if name in repository_ctx.os.environ:\n        return repository_ctx.os.environ.get(name).strip()\n\n    if hasattr(repository_ctx.attr, \"environ\") and name in repository_ctx.attr.environ:\n        return repository_ctx.attr.environ.get(name).strip()\n\n    return default_value\n\ndef is_windows(repository_ctx):\n    \"\"\"Returns true if the execution platform is Windows.\n\n    Args:\n      repository_ctx: the repository_ctx\n\n    Returns:\n      If the execution platform is Windows.\n    \"\"\"\n    os_name = \"\"\n    if hasattr(repository_ctx.attr, \"exec_properties\") and \"OSFamily\" in repository_ctx.attr.exec_properties:\n        os_name = repository_ctx.attr.exec_properties[\"OSFamily\"]\n    else:\n        os_name = repository_ctx.os.name\n\n    return os_name.lower().find(\"windows\") != -1\n\ndef get_cpu_value(repository_ctx):\n    \"\"\"Returns the name of the host operating system.\n\n    Args:\n      repository_ctx: The repository context.\n    Returns:\n      A string containing the name of the host operating system.\n    \"\"\"\n    if is_windows(repository_ctx):\n        return \"Windows\"\n    result = raw_exec(repository_ctx, [\"uname\", \"-s\"])\n    return result.stdout.strip()\n\ndef execute(\n        repository_ctx,\n        cmdline,\n        error_msg = None,\n        error_details = None,\n        allow_failure = False):\n    \"\"\"Executes an arbitrary shell command.\n\n    Args:\n      repository_ctx: the repository_ctx object\n      cmdline: list of strings, the command to execute\n      error_msg: string, a summary of the error if the command fails\n      error_details: string, details about the error or steps to fix it\n      allow_failure: bool, if True, an empty stdout result or output to stderr\n        is fine, otherwise either of these is an error\n    Returns:\n      The result of repository_ctx.execute(cmdline)\n    \"\"\"\n    result = raw_exec(repository_ctx, cmdline)\n    if (result.stderr or not result.stdout) and not allow_failure:\n        fail(\n            \"\\n\".join([\n                error_msg.strip() if error_msg else \"Repository command failed\",\n                result.stderr.strip(),\n                error_details if error_details else \"\",\n            ]),\n        )\n    return result\n\ndef raw_exec(repository_ctx, cmdline):\n    \"\"\"Executes a command via repository_ctx.execute() and returns the result.\n\n    This method is useful for debugging purposes. For example, to print all\n    commands executed as well as their return code.\n\n    Args:\n      repository_ctx: the repository_ctx\n      cmdline: the list of args\n\n    Returns:\n      The 'exec_result' of repository_ctx.execute().\n    \"\"\"\n    return repository_ctx.execute(cmdline)\n\ndef files_exist(repository_ctx, paths, bash_bin = None):\n    \"\"\"Checks which files in paths exists.\n\n    Args:\n      repository_ctx: the repository_ctx\n      paths: a list of paths\n      bash_bin: path to the bash interpreter\n\n    Returns:\n      Returns a list of Bool. True means that the path at the\n      same position in the paths list exists.\n    \"\"\"\n    if bash_bin == None:\n        bash_bin = get_bash_bin(repository_ctx)\n\n    cmd_tpl = \"[ -e \\\"%s\\\" ] && echo True || echo False\"\n    cmds = [cmd_tpl % path for path in paths]\n    cmd = \" ; \".join(cmds)\n\n    stdout = execute(repository_ctx, [bash_bin, \"-c\", cmd]).stdout.strip()\n    return [val == \"True\" for val in stdout.splitlines()]\n\ndef realpath(repository_ctx, path, bash_bin = None):\n    \"\"\"Returns the result of \"realpath path\".\n\n    Args:\n      repository_ctx: the repository_ctx\n      path: a path on the file system\n      bash_bin: path to the bash interpreter\n\n    Returns:\n      Returns the result of \"realpath path\"\n    \"\"\"\n    if bash_bin == None:\n        bash_bin = get_bash_bin(repository_ctx)\n\n    return execute(repository_ctx, [bash_bin, \"-c\", \"realpath \\\"%s\\\"\" % path]).stdout.strip()\n\ndef err_out(result):\n    \"\"\"Returns stderr if set, else stdout.\n\n    This function is a workaround for a bug in RBE where stderr is returned as stdout. Instead\n    of using result.stderr use err_out(result) instead.\n\n    Args:\n      result: the exec_result.\n\n    Returns:\n      The stderr if set, else stdout\n    \"\"\"\n    if len(result.stderr) == 0:\n        return result.stdout\n    return result.stderr\n\ndef config_repo_label(config_repo, target):\n    \"\"\"Construct a label from config_repo and target.\n\n    This function exists to ease the migration from preconfig to remote config. In preconfig\n    the TACHYON_*_CONFIG_REPO environ variables are set to packages in the main repo while in\n    remote config they will point to remote repositories.\n\n    Args:\n      config_repo: a remote repository or package.\n      target: a target\n    Returns:\n      A label constructed from config_repo and target.\n    \"\"\"\n    if config_repo.startswith(\"@\") and not config_repo.find(\"//\") > 0:\n        # remote config is being used.\n        return Label(config_repo + \"//\" + target)\n    elif target.startswith(\":\"):\n        return Label(config_repo + target)\n    else:\n        return Label(config_repo + \"/\" + target)\n"
  },
  {
    "path": "third_party/remote_config/remote_platform_configure.bzl",
    "content": "\"\"\"Repository rule to create a platform for a docker image to be used with RBE.\"\"\"\n\ndef _remote_platform_configure_impl(repository_ctx):\n    platform = repository_ctx.attr.platform\n    if platform == \"local\":\n        os = repository_ctx.os.name.lower()\n        if os.startswith(\"windows\"):\n            platform = \"windows\"\n        elif os.startswith(\"mac os\"):\n            platform = \"osx\"\n        else:\n            platform = \"linux\"\n\n    cpu = \"x86_64\"\n    machine_type = repository_ctx.execute([\"bash\", \"-c\", \"echo $MACHTYPE\"]).stdout\n    if (machine_type.startswith(\"ppc\") or\n        machine_type.startswith(\"powerpc\")):\n        cpu = \"ppc\"\n    elif machine_type.startswith(\"s390x\"):\n        cpu = \"s390x\"\n    elif machine_type.startswith(\"aarch64\"):\n        cpu = \"aarch64\"\n    elif machine_type.startswith(\"arm64\"):\n        cpu = \"aarch64\"\n    elif machine_type.startswith(\"arm\"):\n        cpu = \"arm\"\n    elif machine_type.startswith(\"mips64\"):\n        cpu = \"mips64\"\n    elif machine_type.startswith(\"riscv64\"):\n        cpu = \"riscv64\"\n\n    exec_properties = repository_ctx.attr.platform_exec_properties\n\n    serialized_exec_properties = \"{\"\n    for k, v in exec_properties.items():\n        serialized_exec_properties += \"\\\"%s\\\" : \\\"%s\\\",\" % (k, v)\n    serialized_exec_properties += \"}\"\n\n    repository_ctx.template(\n        \"BUILD\",\n        Label(\"@kroma_network_tachyon//third_party/remote_config:BUILD.tpl\"),\n        {\n            \"%{platform}\": platform,\n            \"%{exec_properties}\": serialized_exec_properties,\n            \"%{cpu}\": cpu,\n        },\n    )\n\nremote_platform_configure = repository_rule(\n    implementation = _remote_platform_configure_impl,\n    attrs = {\n        \"platform_exec_properties\": attr.string_dict(mandatory = True),\n        \"platform\": attr.string(default = \"linux\", values = [\"linux\", \"windows\", \"local\"]),\n    },\n)\n"
  },
  {
    "path": "third_party/repo.bzl",
    "content": "# Copyright 2017 The TensorFlow Authors. All rights reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Utilities for defining TensorFlow Bazel dependencies.\"\"\"\n\n# NOTE(chokobole): I don't touch tf_ prefix to express that this uses tf mirror urls.\ndef tf_mirror_urls(url):\n    \"\"\"A helper for generating TF-mirror versions of URLs.\n\n    Given a URL, it returns a list of the TF-mirror cache version of that URL\n    and the original URL, suitable for use in `urls` field of `tf_http_archive`.\n    \"\"\"\n    if not url.startswith(\"https://\"):\n        return [url]\n    return [\n        \"https://storage.googleapis.com/mirror.tensorflow.org/%s\" % url[8:],\n        url,\n    ]\n\ndef _get_env_var(ctx, name):\n    if name in ctx.os.environ:\n        return ctx.os.environ[name]\n    else:\n        return None\n\n# Checks if we should use the system lib instead of the bundled one\ndef _use_system_lib(ctx, name):\n    syslibenv = _get_env_var(ctx, \"TACHYON_SYSTEM_LIBS\")\n    if not syslibenv:\n        return False\n    return name in [n.strip() for n in syslibenv.split(\",\")]\n\ndef _get_link_dict(ctx, link_files, build_file):\n    link_dict = {ctx.path(v): ctx.path(Label(k)) for k, v in link_files.items()}\n    if build_file:\n        # Use BUILD.bazel because it takes precedence over BUILD.\n        link_dict[ctx.path(\"BUILD.bazel\")] = ctx.path(Label(build_file))\n    return link_dict\n\ndef _tf_http_archive_impl(ctx):\n    # Construct all paths early on to prevent rule restart. We want the\n    # attributes to be strings instead of labels because they refer to files\n    # in the TensorFlow repository, not files in repos depending on TensorFlow.\n    # See also https://github.com/bazelbuild/bazel/issues/10515.\n    link_dict = _get_link_dict(ctx, ctx.attr.link_files, ctx.attr.build_file)\n\n    # For some reason, we need to \"resolve\" labels once before the\n    # download_and_extract otherwise it'll invalidate and re-download the\n    # archive each time.\n    # https://github.com/bazelbuild/bazel/issues/10515\n    patch_files = ctx.attr.patch_file\n    for patch_file in patch_files:\n        if patch_file:\n            ctx.path(Label(patch_file))\n\n    if _use_system_lib(ctx, ctx.attr.name):\n        link_dict.update(_get_link_dict(\n            ctx = ctx,\n            link_files = ctx.attr.system_link_files,\n            build_file = ctx.attr.system_build_file,\n        ))\n    else:\n        ctx.download_and_extract(\n            url = ctx.attr.urls,\n            sha256 = ctx.attr.sha256,\n            type = ctx.attr.type,\n            stripPrefix = ctx.attr.strip_prefix,\n        )\n        if patch_files:\n            for patch_file in patch_files:\n                patch_file = ctx.path(Label(patch_file)) if patch_file else None\n                if patch_file:\n                    ctx.patch(patch_file, strip = 1)\n\n    for dst, src in link_dict.items():\n        ctx.delete(dst)\n        ctx.symlink(src, dst)\n\n_tf_http_archive = repository_rule(\n    implementation = _tf_http_archive_impl,\n    attrs = {\n        \"sha256\": attr.string(mandatory = True),\n        \"urls\": attr.string_list(mandatory = True),\n        \"strip_prefix\": attr.string(),\n        \"type\": attr.string(),\n        \"patch_file\": attr.string_list(),\n        \"build_file\": attr.string(),\n        \"system_build_file\": attr.string(),\n        \"link_files\": attr.string_dict(),\n        \"system_link_files\": attr.string_dict(),\n    },\n    environ = [\"TACHYON_SYSTEM_LIBS\"],\n)\n\ndef tachyon_http_archive(name, sha256, urls, **kwargs):\n    \"\"\"Downloads and creates Bazel repos for dependencies.\n\n    This is a swappable replacement for both http_archive() and\n    new_http_archive() that offers some additional features. It also helps\n    ensure best practices are followed.\n\n    File arguments are relative to the TensorFlow repository by default. Dependent\n    repositories that use this rule should refer to files either with absolute\n    labels (e.g. '@foo//:bar') or from a label created in their repository (e.g.\n    'str(Label(\"//:bar\"))').\n    \"\"\"\n    if len(urls) < 2:\n        fail(\"tachyon_http_archive(urls) must have redundant URLs.\")\n\n    if not any([mirror in urls[0] for mirror in (\n        \"mirror.tensorflow.org\",\n        \"mirror.bazel.build\",\n        \"storage.googleapis.com\",\n    )]):\n        fail(\"The first entry of tachyon_http_archive(urls) must be a mirror \" +\n             \"URL, preferrably mirror.tensorflow.org. Even if you don't have \" +\n             \"permission to mirror the file, please put the correctly \" +\n             \"formatted mirror URL there anyway, because someone will come \" +\n             \"along shortly thereafter and mirror the file.\")\n\n    if native.existing_rule(name):\n        print(\"\\n\\033[1;33mWarning:\\033[0m skipping import of repository '\" +\n              name + \"' because it already exists.\\n\")\n        return\n\n    _tf_http_archive(\n        name = name,\n        sha256 = sha256,\n        urls = urls,\n        **kwargs\n    )\n\ndef _tachyon_vendored_impl(repository_ctx):\n    parent_path = repository_ctx.path(repository_ctx.attr.parent).dirname\n\n    # get_child doesn't allow slashes. Yes this is silly. bazel_skylib paths\n    # doesn't work with path objects.\n    relpath_parts = repository_ctx.attr.relpath.split(\"/\")\n    vendored_path = parent_path\n    for part in relpath_parts:\n        vendored_path = vendored_path.get_child(part)\n    repository_ctx.symlink(vendored_path, \".\")\n\ntachyon_vendored = repository_rule(\n    implementation = _tachyon_vendored_impl,\n    attrs = {\n        \"parent\": attr.label(default = \"//:WORKSPACE\"),\n        \"relpath\": attr.string(),\n    },\n)\n"
  },
  {
    "path": "vendors/circom/.bazelrc",
    "content": "build:fastbuild -c fastbuild\n\nbuild:dbg -c dbg\n\nbuild:opt -c opt\n\nbuild:maxopt -c opt\nbuild:maxopt --config=native\nbuild:maxopt --copt=-flto\nbuild:maxopt --linkopt=-flto\n\ncommon --enable_platform_specific_config\n\nbuild:linux --cxxopt=-std=c++17\nbuild:linux --host_cxxopt=-std=c++17\nbuild:macos --cxxopt=-std=c++17\nbuild:macos --host_cxxopt=-std=c++17\nbuild:macos --objccopt=-std=c++17\nbuild:windows --cxxopt=/std:c++17\nbuild:windows --host_cxxopt=/std:c++17\n\nbuild:macos_x86_64 --config=macos\nbuild:macos_x86_64 --cpu=darwin_x86_64\nbuild:macos_x86_64 --host_cpu=darwin_x86_64\nbuild:macos_arm64 --config=macos\nbuild:macos_arm64 --cpu=darwin_arm64\nbuild:macos_arm64 --host_cpu=darwin_arm64\n\n# This config refers to building sources with nvcc.\nbuild:cuda --repo_env TACHYON_NEED_CUDA=1\nbuild:cuda --crosstool_top=@local_config_cuda//crosstool:toolchain\nbuild:cuda --@local_config_cuda//:enable_cuda\nbuild:cuda --@kroma_network_tachyon//:has_rtti\n\nbuild:native --copt=-march=native\n\n# Enable googletest build with absl.\n# See https://github.com/google/googletest/blob/v1.13.0/BUILD.bazel#L67C1-L70\nbuild --define absl=1\n# FIXME(chokobole): If this option is enabled, gtest cannot be built with nvcc.\nbuild:cuda --define absl=0\n\n# gmp needs exception.\nbuild --@kroma_network_tachyon//:has_exception\n\n# Load rc file with user-specific options.\ntry-import %workspace%/.bazelrc.user\n"
  },
  {
    "path": "vendors/circom/.bazelversion",
    "content": "6.3.0\n"
  },
  {
    "path": "vendors/circom/.gitignore",
    "content": "/bazel-*\n/.bazelrc.user\n"
  },
  {
    "path": "vendors/circom/BUILD.bazel",
    "content": "load(\"@kroma_network_tachyon//bazel:tachyon_cc.bzl\", \"tachyon_cc_binary\")\n\ntachyon_cc_binary(\n    name = \"prover_main\",\n    srcs = [\"prover_main.cc\"],\n    deps = [\n        \"//circomlib/circuit:quadratic_arithmetic_program\",\n        \"//circomlib/json\",\n        \"//circomlib/json:groth16_proof\",\n        \"//circomlib/json:prime_field\",\n        \"//circomlib/wtns\",\n        \"//circomlib/zkey\",\n        \"@com_google_absl//absl/strings\",\n        \"@kroma_network_tachyon//tachyon/base:profiler\",\n        \"@kroma_network_tachyon//tachyon/base/console\",\n        \"@kroma_network_tachyon//tachyon/base/files:file_path_flag\",\n        \"@kroma_network_tachyon//tachyon/base/flag:flag_parser\",\n        \"@kroma_network_tachyon//tachyon/math/elliptic_curves/bls12/bls12_381\",\n        \"@kroma_network_tachyon//tachyon/math/elliptic_curves/bn/bn254\",\n        \"@kroma_network_tachyon//tachyon/math/polynomials/univariate:univariate_evaluation_domain_factory\",\n        \"@kroma_network_tachyon//tachyon/math/polynomials/univariate/icicle:icicle_ntt_holder\",\n        \"@kroma_network_tachyon//tachyon/zk/r1cs/groth16:prove\",\n        \"@kroma_network_tachyon//tachyon/zk/r1cs/groth16:verify\",\n    ],\n)\n"
  },
  {
    "path": "vendors/circom/README.md",
    "content": "# Circom\n\n## Features\n\n|                           | Tachyon   | [Circom-compat] | [Rapidsnark]                      |\n| ------------------------- | --------- | --------------- | --------------------------------- |\n| Language                  | C++       | Rust            | C++                               |\n| Witness Generator         | C(Fast)   | WASM(Slow)      | -                                 |\n| Flexible Witness Handling | Yes       | Yes             | No                                |\n| Field Support             | All       | Bn128           | Bn128                             |\n| [FFIASM] Generated Field  | Yes(Fast) | No(Slow)        | Yes(Fast)                         |\n| Gpu Support               | Yes       | No              | Yes([Orbiter-Finance/Rapidsnark]) |\n\n1. Speed: Tachyon runs approximately 10 times faster than [Rapidsnark]! The [benchmark] was conducted using [sha256_512.circom] with a degree of 2¹⁶.\n2. Seamless Workflow: Tachyon doesn't require you to run any additional programs just to generate a circom proof. No need for [snarkjs] for witness file generation.\n3. Flexible Witness Handling: With Tachyon, you can modify the witness directly in your program. No need to create a separate JSON file with [snarkjs].\n4. Integrated Build System: Tachyon seamlessly integrates circom compilation into the build system, specifically bazel. When you make changes to a circom file, it's compiled accordingly and built in parallel, ensuring a safe and efficient process.\n5. Field Support: Tachyon isn't limited to bn128; it easily supports all fields!\n6. Gpu Support: Both Tachyon and [Orbiter-Finance/Rapidsnark] support MSM / NTT using [icicle].\n\n[Circom-compat]: https://github.com/arkworks-rs/circom-compat\n[Rapidsnark]: https://github.com/iden3/rapidsnark\n[FFIASM]: https://github.com/iden3/ffiasm\n[Orbiter-Finance/Rapidsnark]: https://github.com/Orbiter-Finance/rapidsnark\n[benchmark]: /vendors/circom/benchmark/README.md\n[sha256_512.circom]: /vendors/circom/benchmark/sha256_512.circom\n[snarkjs]: https://github.com/iden3/snarkjs\n[icicle]: https://github.com/ingonyama-zk/icicle\n\n## How to build\n\nGo to [prerequisites](../../docs/how_to_use/how_to_build.md#Prerequisites) and follow the instructions.\n\n### With CUDA\n\n```shell\nbazel build --@kroma_network_tachyon//:has_openmp --config maxopt --config cuda //:prover_main\n```\n\n### Without CUDA\n\n```shell\nbazel build --@kroma_network_tachyon//:has_openmp --config maxopt //:prover_main\n```\n\n### With Docker\n\nFor the CUDA build, you need to [setup the NVIDIA Container toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html).\n\n```shell\n# These commands should be run in the base folder\ncd ../..\n# Build without CUDA\ndocker build -f docker/Dockerfile.circom.jammy -t tachyon:cpu  --target builder .\ndocker run -it tachyon:cpu\n# Build with CUDA\ndocker build -f docker/Dockerfile.circom.jammy -t tachyon:cuda  --target builder-cuda .\ndocker run -it --runtime nvidia --gpus all tachyon:cuda\n```\n\nNOTE: To use external files for proving or compiling, you need to mount or copy them to the tachyon folder to use it within docker.\n\n## How to run\n\n```shell\nbazel-bin/prover_main -h\nUsage:\n\nbazel-bin/prover_main zkey wtns proof public [OPTIONS]\n\nPositional arguments:\n\nzkey                The path to zkey file\nwtns                The path to wtns file\nproof               The path to proof json\npublic              The path to public json\n\nOptional arguments:\n\n--trace_path        The path to generate trace file\n--curve             The curve type among ('bn254', 'bls12_381'), by default 'bn254'\n--no_zk             Create proof without zk. By default zk is enabled. Use this flag in case you want to compare the proof with rapidsnark.\n--no_use_mmap       Create proof without mmap(2). By default, mmap(2) is enabled, offering faster proof generation at the cost of increased memory usage due to the memory mapped file. Use this flag if you want to use less memory.\n--verify            Verify the proof. By default verify is disabled. Use this flag to verify the proof with the public inputs.\n-n, --num_runs      The number of times to run the proof generation\n--disable_fast_twiddles_mode\n                    Disables fast twiddle mode on Icicle NTT domain initialization.\n```\n\n## How to compile witness generator\n\nThis creates a witness_generator binary similar to when circom is used, but compiles much faster in comparison.\n\n```shell\nbazel build --config opt //circomlib/build:compile_witness_generator\n\nbazel-bin/circomlib/build/compile_witness_generator --help\nUsage:\nbazel-bin/circomlib/build/compile_witness_generator [OPTION]...\n\n-h, --help  help\n--cpp,      A path to circuit .cpp file\n-f, --field A field to use. default: bn128, supported: bls12381 bn128 goldilocks grumpkin pallas secq256r1 vesta\n```\n"
  },
  {
    "path": "vendors/circom/WORKSPACE",
    "content": "workspace(name = \"kroma_network_circom\")\n\nload(\"@bazel_tools//tools/build_defs/repo:http.bzl\", \"http_archive\")\n\nhttp_archive(\n    name = \"iden3_circom\",\n    build_file = \"@kroma_network_circom//:circom.BUILD\",\n    sha256 = \"ba2c82edabdca7190f4f152e3c4ebcdf3656805dc4c1c7e1d37a991d5a79e1cd\",\n    strip_prefix = \"circom-2.1.9\",\n    urls = [\"https://github.com/iden3/circom/archive/v2.1.9.tar.gz\"],\n)\n\nhttp_archive(\n    name = \"llvm-raw\",\n    build_file_content = \"# empty\",\n    sha256 = \"50b3ef31b228ea0c96ae074005bfac087c56e6a4b1c147592dd33f41cad0706b\",\n    strip_prefix = \"llvm-project-81d5412439efd0860c0a8dd51b831204f118d485\",\n    urls = [\"https://github.com/llvm/llvm-project/archive/81d5412439efd0860c0a8dd51b831204f118d485.tar.gz\"],\n)\n\nload(\"@llvm-raw//utils/bazel:configure.bzl\", \"llvm_configure\", \"llvm_disable_optional_support_deps\")\n\nllvm_configure(name = \"llvm-project\")\n\n# Disables optional dependencies for support like zlib and terminfo. You may\n# instead want to configure them using the macros in the corresponding bzl\n# files.\nllvm_disable_optional_support_deps()\n\nhttp_archive(\n    name = \"kroma_network_rules_circom\",\n    sha256 = \"184998343fae865126fe5a31e126b1f8259259fd2a80315ab21251c01625aaf6\",\n    strip_prefix = \"rules_circom-e1f9be5c03fa91f28f92a042f483c608ab4261c6\",\n    urls = [\"https://github.com/kroma-network/rules_circom/archive/e1f9be5c03fa91f28f92a042f483c608ab4261c6.tar.gz\"],\n)\n\nload(\"@kroma_network_rules_circom//:rules_circom_deps.bzl\", \"rules_circom_deps\")\n\nrules_circom_deps()\n\n# Start of rules_rust\nload(\"@rules_rust//rust:repositories.bzl\", \"rules_rust_dependencies\", \"rust_register_toolchains\")\n\nrules_rust_dependencies()\n\n# We need to change the default value of flag //tachyon/rs/base:rustc_version_ge_1.67.0\n# if we change the default rustc version.\n# See //tachyon/rs/base/BUILD.bazel.\nrust_register_toolchains(\n    edition = \"2021\",\n    versions = [\n        \"1.66.1\",\n    ],\n)\n\nload(\"@rules_rust//crate_universe:repositories.bzl\", \"crate_universe_dependencies\")\n\ncrate_universe_dependencies()\n\nload(\"@rules_rust//crate_universe:defs.bzl\", \"crates_repository\")\n\ncrates_repository(\n    name = \"crate_index\",\n    cargo_lockfile = \"@kroma_network_rules_circom//:Cargo.lock\",\n    lockfile = \"@kroma_network_rules_circom//:Cargo.Bazel.lock\",\n    manifests = [\"@kroma_network_rules_circom//:Cargo.toml\"],\n)\n\nload(\"@crate_index//:defs.bzl\", \"crate_repositories\")\n\ncrate_repositories()\n# End of rules_rust\n\nlocal_repository(\n    name = \"kroma_network_tachyon\",\n    path = \"../../\",\n)\n\nload(\"@kroma_network_tachyon//bazel:tachyon_deps.bzl\", \"tachyon_deps\")\n\ntachyon_deps()\n\nload(\"@kroma_network_tachyon//bazel:buildifier_deps.bzl\", \"buildifier_deps\")\n\nbuildifier_deps()\n\n# Start of rules_js\nload(\"@kroma_network_tachyon//bazel:js_deps.bzl\", \"js_deps\")\n\njs_deps()\n\nload(\"@aspect_rules_js//js:repositories.bzl\", \"rules_js_dependencies\")\n\nrules_js_dependencies()\n\nload(\"@aspect_bazel_lib//lib:repositories.bzl\", \"aspect_bazel_lib_dependencies\")\n\naspect_bazel_lib_dependencies()\n\n# Fetch and register node, if you haven't already\nload(\"@rules_nodejs//nodejs:repositories.bzl\", \"DEFAULT_NODE_VERSION\", \"nodejs_register_toolchains\")\n\nnodejs_register_toolchains(\n    name = \"nodejs\",\n    node_version = DEFAULT_NODE_VERSION,\n)\n\nload(\"@aspect_rules_js//npm:repositories.bzl\", \"npm_translate_lock\")\n\nnpm_translate_lock(\n    name = \"npm\",\n    data = [\"@iden3_ffiasm//:package.json\"],\n    npm_package_lock = \"@iden3_ffiasm//:package-lock.json\",\n    pnpm_lock = \"@iden3_ffiasm//:pnpm-lock.yaml\",\n    update_pnpm_lock = True,\n    verify_node_modules_ignored = \"@iden3_ffiasm//:.bazelignore\",\n)\n\nload(\"@npm//:repositories.bzl\", \"npm_repositories\")\n\nnpm_repositories()\n\n# End of rules_js\n"
  },
  {
    "path": "vendors/circom/benchmark/BUILD.bazel",
    "content": "load(\"@kroma_network_rules_circom//:build_defs.bzl\", \"compile_circuit\")\nload(\"@kroma_network_tachyon//bazel:tachyon_cc.bzl\", \"tachyon_cc_binary\", \"tachyon_cc_library\")\nload(\"//:build_defs.bzl\", \"witness_gen_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncompile_circuit(\n    name = \"compile_sha256_512\",\n    main = \"sha256_512.circom\",\n    deps = [\"@kroma_network_circomlib//circuits/sha256\"],\n)\n\nwitness_gen_library(\n    name = \"gen_witness_sha256_512\",\n    gendep = \":compile_sha256_512\",\n)\n\ntachyon_cc_library(\n    name = \"bit_conversion\",\n    hdrs = [\"bit_conversion.h\"],\n    deps = [\"@com_google_absl//absl/types:span\"],\n)\n\ntachyon_cc_library(\n    name = \"rapidsnark_runner\",\n    hdrs = [\"rapidsnark_runner.h\"],\n    deps = [\n        \":runner\",\n        \"@iden3_rapidsnark//:rapidsnark\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"runner\",\n    hdrs = [\"runner.h\"],\n    deps = [\n        \"@com_google_absl//absl/types:span\",\n        \"@kroma_network_tachyon//tachyon/base/files:file_path\",\n        \"@kroma_network_tachyon//tachyon/base/time\",\n        \"@kroma_network_tachyon//tachyon/math/polynomials/univariate:univariate_evaluation_domain\",\n        \"@kroma_network_tachyon//tachyon/zk/r1cs/groth16:proof\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"tachyon_runner\",\n    hdrs = [\"tachyon_runner.h\"],\n    deps = [\n        \":runner\",\n        \"//circomlib/circuit:quadratic_arithmetic_program\",\n        \"//circomlib/circuit:witness_loader\",\n        \"//circomlib/zkey\",\n        \"@kroma_network_tachyon//tachyon/zk/r1cs/groth16:prove\",\n        \"@kroma_network_tachyon//tachyon/zk/r1cs/groth16:verify\",\n    ],\n)\n\ntachyon_cc_binary(\n    name = \"circom_benchmark\",\n    testonly = True,\n    srcs = [\"circom_benchmark.cc\"],\n    data = [\n        \"sha256_512.zkey\",\n        \"sha256_512_verification_key.json\",\n        \":compile_sha256_512\",\n    ],\n    deps = [\n        \":bit_conversion\",\n        \":gen_witness_sha256_512\",\n        \":rapidsnark_runner\",\n        \":tachyon_runner\",\n        \"@com_google_boringssl//:crypto\",\n        \"@kroma_network_tachyon//tachyon/base/console\",\n        \"@kroma_network_tachyon//tachyon/base/flag:flag_parser\",\n        \"@kroma_network_tachyon//tachyon/math/elliptic_curves/bn/bn254\",\n        \"@kroma_network_tachyon//tachyon/math/polynomials/univariate:univariate_evaluation_domain_factory\",\n        \"@kroma_network_tachyon//tachyon/math/polynomials/univariate/icicle:icicle_ntt_holder\",\n    ],\n)\n"
  },
  {
    "path": "vendors/circom/benchmark/README.md",
    "content": "# Circom Benchmarking\n\n```\nRun on 13th Gen Intel(R) Core(TM) i9-13900K (32 X 5500 MHz CPU s)\nCPU Caches:\n  L1 Data 48 KiB (x16)\n  L1 Instruction 32 KiB (x16)\n  L2 Unified 2048 KiB (x16)\n  L3 Unified 36864 KiB (x1)\n```\n\n## How to run\n\nMove to the `vendors/circom` directory.\n\n```shell\ncd vendors/circom\n```\n\nRun the following line if you are benchmarking for Circom for the first time.\n\n```shell\nCARGO_BAZEL_REPIN=1 bazel sync --only=crate_index\n```\n\nRun Circom benchmarking.\n\n```shell\nbazel run --@kroma_network_tachyon//:has_openmp -c opt //benchmark:circom_benchmark  -- -n 10\n```\n\n## Result\n\n```\n[0]: 0.044026 s\n[1]: 0.039156 s\n[2]: 0.038875 s\n[3]: 0.038884 s\n[4]: 0.038806 s\n[5]: 0.038947 s\n[6]: 0.038836 s\n[7]: 0.038895 s\n[8]: 0.038878 s\n[9]: 0.038811 s\ntachyon(avg): 0.039411 s\n[0]: 0.530905 s\n[1]: 0.75682 s\n[2]: 0.163237 s\n[3]: 0.77573 s\n[4]: 0.530742 s\n[5]: 0.512661 s\n[6]: 0.22959 s\n[7]: 0.542708 s\n[8]: 0.72599 s\n[9]: 0.490333 s\nrapidsnark(avg): 0.525871 s\n```\n"
  },
  {
    "path": "vendors/circom/benchmark/bit_conversion.h",
    "content": "#ifndef VENDORS_CIRCOM_BENCHMARK_BIT_CONVERSION_H_\n#define VENDORS_CIRCOM_BENCHMARK_BIT_CONVERSION_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <vector>\n\n#include \"absl/types/span.h\"\n\nnamespace tachyon::circom {\n\ntemplate <typename F>\nstd::vector<F> Uint8ToBitVector(absl::Span<const uint8_t> uint8_vec) {\n  std::vector<F> bit_vec;\n  bit_vec.reserve(uint8_vec.size() * 8);\n  for (size_t i = 0; i < uint8_vec.size(); i++) {\n    for (size_t j = 0; j < 8; ++j) {\n      bit_vec.push_back(F((uint8_vec[i] >> (7 - j)) & 1));\n    }\n  }\n  return bit_vec;\n}\n\ntemplate <typename F>\nstd::vector<uint8_t> BitToUint8Vector(absl::Span<const F> bit_vec) {\n  std::vector<uint8_t> uint8_vec;\n  size_t size = (bit_vec.size() + 7) / 8;\n  uint8_vec.resize(size, 0);\n  for (size_t i = 0; i < bit_vec.size(); i++) {\n    size_t idx = i / 8;\n    uint8_vec[idx] |= (bit_vec[i].ToBigInt()[0] << (7 - (i % 8)));\n  }\n  return uint8_vec;\n}\n\n}  // namespace tachyon::circom\n\n#endif  // VENDORS_CIRCOM_BENCHMARK_BIT_CONVERSION_H_\n"
  },
  {
    "path": "vendors/circom/benchmark/circom_benchmark.cc",
    "content": "#include <stddef.h>\n#include <stdint.h>\n\n#include <iostream>\n#include <utility>\n\n#include \"alt_bn128.hpp\"  // NOLINT(build/include_subdir)\n#include \"openssl/sha.h\"\n\n// clang-format off\n#include \"benchmark/bit_conversion.h\"\n#include \"benchmark/rapidsnark_runner.h\"\n#include \"benchmark/tachyon_runner.h\"\n// clang-format on\n#include \"tachyon/base/console/iostream.h\"\n#include \"tachyon/base/flag/flag_parser.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/bn254.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluation_domain_factory.h\"\n\n#if TACHYON_CUDA\n#include \"tachyon/math/polynomials/univariate/icicle/icicle_ntt_holder.h\"\n#endif\n\nnamespace tachyon::circom {\n\nusing namespace math;\n\nconstexpr size_t kMaxDegree = (size_t{1} << 16) - 1;\n\nusing F = bn254::Fr;\nusing Curve = bn254::BN254Curve;\nusing Domain = UnivariateEvaluationDomain<F, kMaxDegree>;\n\nvoid CheckPublicInput(const std::vector<uint8_t>& in,\n                      absl::Span<const F> public_inputs) {\n  SHA256_CTX state;\n  SHA256_Init(&state);\n\n  SHA256_Update(&state, in.data(), in.size());\n  uint8_t result[SHA256_DIGEST_LENGTH];\n  SHA256_Final(result, &state);\n\n  std::vector<uint8_t> uint8_vec = BitToUint8Vector(public_inputs);\n  std::vector<uint8_t> result_vec(std::begin(result), std::end(result));\n  CHECK(uint8_vec == result_vec);\n}\n\nint RealMain(int argc, char** argv) {\n  base::FlagParser parser;\n  size_t n;\n  parser.AddFlag<base::Flag<size_t>>(&n)\n      .set_short_name(\"-n\")\n      .set_required()\n      .set_help(\"The number of test to run\");\n  std::string error;\n  if (!parser.Parse(argc, argv, &error)) {\n    tachyon_cerr << error << std::endl;\n    return 1;\n  }\n  if (n == 0) {\n    tachyon_cerr << \"n should be positive\" << std::endl;\n    return 1;\n  }\n\n  Curve::Init();\n\n  std::vector<std::unique_ptr<Runner<Curve, kMaxDegree>>> runners;\n  runners.push_back(std::make_unique<TachyonRunner<Curve, kMaxDegree>>(\n      base::FilePath(\"benchmark/sha256_512_cpp/sha256_512.dat\")));\n  runners.push_back(\n      std::make_unique<RapidsnarkRunner<Curve, kMaxDegree, AltBn128::Engine>>(\n          base::FilePath(\"benchmark/sha256_512_verification_key.json\")));\n  std::vector<zk::r1cs::groth16::Proof<Curve>> proofs;\n\n  std::vector<uint8_t> in = base::CreateVector(\n      64, [](size_t i) { return base::Uniform(base::Range<uint8_t>()); });\n  std::vector<F> full_assignments;\n  absl::Span<const F> public_inputs;\n\n  for (size_t i = 0; i < runners.size(); ++i) {\n    std::unique_ptr<Runner<Curve, kMaxDegree>>& runner = runners[i];\n    runner->LoadZKey(base::FilePath(\"benchmark/sha256_512.zkey\"));\n\n    std::unique_ptr<Domain> domain;\n    if (i == 0) {\n      TachyonRunner<Curve, kMaxDegree>* tachyon_runner =\n          reinterpret_cast<TachyonRunner<Curve, kMaxDegree>*>(runner.get());\n\n      WitnessLoader<F>& witness_loader = tachyon_runner->witness_loader();\n\n      witness_loader.Set(\"in\", Uint8ToBitVector<F>(in));\n      witness_loader.Load();\n\n      domain = Domain::Create(tachyon_runner->GetDomainSize());\n\n      size_t num_instance_variables = tachyon_runner->GetNumInstanceVariables();\n      full_assignments = base::CreateVectorParallel(\n          num_instance_variables + tachyon_runner->GetNumWitnessVariables(),\n          [&witness_loader](size_t i) { return witness_loader.Get(i); });\n\n      public_inputs = absl::MakeConstSpan(full_assignments)\n                          .subspan(1, num_instance_variables - 1);\n      CheckPublicInput(in, public_inputs);\n    }\n\n#if TACHYON_CUDA\n    IcicleNTTHolder<F> icicle_ntt_holder = IcicleNTTHolder<F>::Create();\n    if (i == 0) {\n      CHECK(icicle_ntt_holder->Init(domain->group_gen()));\n      domain->set_icicle(&icicle_ntt_holder);\n    }\n#endif\n\n    base::TimeDelta total_delta;\n    for (size_t j = 0; j < n; ++j) {\n      base::TimeDelta delta;\n      zk::r1cs::groth16::Proof<Curve> proof =\n          runner->Run(domain.get(), full_assignments, public_inputs, delta);\n      if (j == 0) {\n        proofs.push_back(proof);\n      }\n      std::cout << \"[\" << j << \"]: \" << delta << std::endl;\n      total_delta += delta;\n    }\n    if (i == 0) {\n      std::cout << \"tachyon(avg): \";\n    } else {\n      std::cout << \"rapidsnark(avg): \";\n    }\n    std::cout << total_delta / n << std::endl;\n\n    if (i > 0) {\n      CHECK_EQ(proofs[0], proofs.back());\n    }\n  }\n  return 0;\n}\n\n}  // namespace tachyon::circom\n\nint main(int argc, char** argv) {\n  return tachyon::circom::RealMain(argc, argv);\n}\n"
  },
  {
    "path": "vendors/circom/benchmark/rapidsnark_runner.h",
    "content": "#ifndef VENDORS_CIRCOM_BENCHMARK_RAPIDSNARK_RUNNER_H_\n#define VENDORS_CIRCOM_BENCHMARK_RAPIDSNARK_RUNNER_H_\n\n#include <string.h>\n\n#include <memory>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"fileloader.hpp\"  // NOLINT(build/include_subdir)\n#include \"groth16.hpp\"     // NOLINT(build/include_subdir)\n#include \"zkey_utils.hpp\"  // NOLINT(build/include_subdir)\n\n// clang-format off\n#include \"benchmark/runner.h\"\n// clang-format on\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/openmp_util.h\"\n\nnamespace tachyon::circom {\n\nnamespace {\n\ntemplate <typename Engine>\nvoid parse_key(Groth16::VerificationKey<Engine>& key, const char* key_str) {\n  try {\n    json key_json = json::parse(key_str);\n\n    auto protocol = key_json[\"protocol\"].template get<std::string>();\n\n    CHECK_EQ(protocol, \"groth16\");\n\n    key.fromJson(key_json);\n\n    CHECK(!key.IC.empty());\n  } catch (...) {\n    NOTREACHED();\n  }\n}\n\n}  // namespace\n\ntemplate <typename Curve, size_t MaxDegree, typename Engine>\nclass RapidsnarkRunner : public Runner<Curve, MaxDegree> {\n public:\n  using G1AffinePoint = typename Curve::G1Curve::AffinePoint;\n  using G2AffinePoint = typename Curve::G2Curve::AffinePoint;\n  using F = typename Curve::G1Curve::ScalarField;\n  using Domain = math::UnivariateEvaluationDomain<F, MaxDegree>;\n\n  explicit RapidsnarkRunner(const base::FilePath& vk_path)\n      : verification_key_(Engine::engine) {\n    BinFileUtils::FileLoader key(vk_path.value());\n    parse_key(verification_key_, key.dataAsString().c_str());\n  }\n\n  void LoadZKey(const base::FilePath& zkey_path) override {\n    zkey_ = BinFileUtils::openExisting(zkey_path.value(), \"zkey\", 1);\n    zkey_header_ = ZKeyUtils::loadHeader(zkey_.get());\n    prover_ = Groth16::makeProver<Engine>(\n        zkey_header_->nVars, zkey_header_->nPublic, zkey_header_->domainSize,\n        zkey_header_->nCoefs, zkey_header_->vk_alpha1, zkey_header_->vk_beta1,\n        zkey_header_->vk_beta2, zkey_header_->vk_delta1,\n        zkey_header_->vk_delta2,\n        zkey_->getSectionData(4),  // Coeffs\n        zkey_->getSectionData(5),  // pointsA\n        zkey_->getSectionData(6),  // pointsB1\n        zkey_->getSectionData(7),  // pointsB2\n        zkey_->getSectionData(8),  // pointsC\n        zkey_->getSectionData(9)   // pointsH1\n    );                             // NOLINT(whitespace/parens)\n  }\n\n  zk::r1cs::groth16::Proof<Curve> Run(const Domain*,\n                                      const std::vector<F>& full_assignments_in,\n                                      absl::Span<const F> public_inputs_in,\n                                      base::TimeDelta& delta) override {\n    using FrElement = typename Engine::Fr::Element;\n\n    base::TimeTicks now = base::TimeTicks::Now();\n\n    std::vector<FrElement> full_assignments(full_assignments_in.size());\n    OMP_PARALLEL_FOR(size_t i = 0; i < full_assignments_in.size(); ++i) {\n      using BigInt = typename F::BigIntTy;\n      BigInt bigint = full_assignments_in[i].ToBigInt();\n      memcpy(full_assignments[i].v, bigint.limbs, BigInt::kByteNums);\n    }\n\n    std::unique_ptr<Groth16::Proof<Engine>> proof =\n        prover_->prove(full_assignments.data());\n\n    delta = base::TimeTicks::Now() - now;\n\n    std::vector<FrElement> public_inputs =\n        base::Map(public_inputs_in, [](const F& public_input) {\n          return reinterpret_cast<const FrElement&>(public_input);\n        });\n\n    Groth16::Verifier<AltBn128::Engine> verifier;\n    CHECK(verifier.verify(*proof, public_inputs, verification_key_));\n\n    return {\n        G1AffinePointToNative(proof->A),\n        G2AffinePointToNative(proof->B),\n        G1AffinePointToNative(proof->C),\n    };\n  }\n\n private:\n  using G1PointAffine = typename Engine::G1PointAffine;\n  using G2PointAffine = typename Engine::G2PointAffine;\n\n  static G1AffinePoint G1AffinePointToNative(const G1PointAffine& point) {\n    using BaseField = typename Curve::G1Curve::BaseField;\n    using BigInt = typename BaseField::BigIntTy;\n    BigInt x_bigint;\n    memcpy(x_bigint.limbs, &point.x, BigInt::kByteNums);\n    BigInt y_bigint;\n    memcpy(y_bigint.limbs, &point.y, BigInt::kByteNums);\n    return {BaseField::FromMontgomery(x_bigint),\n            BaseField::FromMontgomery(y_bigint)};\n  }\n\n  static G2AffinePoint G2AffinePointToNative(const G2PointAffine& point) {\n    using BaseField = typename Curve::G2Curve::BaseField;\n    using BasePrimeField = typename BaseField::BasePrimeField;\n    using BigInt = typename BasePrimeField::BigIntTy;\n    BaseField x;\n    {\n      BigInt a_bigint;\n      memcpy(a_bigint.limbs, &point.x.a, BigInt::kByteNums);\n      BasePrimeField a = BasePrimeField::FromMontgomery(a_bigint);\n      BigInt b_bigint;\n      memcpy(b_bigint.limbs, &point.x.b, BigInt::kByteNums);\n      BasePrimeField b = BasePrimeField::FromMontgomery(b_bigint);\n      x = BaseField(std::move(a), std::move(b));\n    }\n    BaseField y;\n    {\n      BigInt a_bigint;\n      memcpy(a_bigint.limbs, &point.y.a, BigInt::kByteNums);\n      BasePrimeField a = BasePrimeField::FromMontgomery(a_bigint);\n      BigInt b_bigint;\n      memcpy(b_bigint.limbs, &point.y.b, BigInt::kByteNums);\n      BasePrimeField b = BasePrimeField::FromMontgomery(b_bigint);\n      y = BaseField(std::move(a), std::move(b));\n    }\n    return {std::move(x), std::move(y)};\n  }\n\n  Groth16::VerificationKey<AltBn128::Engine> verification_key_;\n  std::unique_ptr<BinFileUtils::BinFile> zkey_;\n  std::unique_ptr<ZKeyUtils::Header> zkey_header_;\n  std::unique_ptr<Groth16::Prover<Engine>> prover_;\n};\n\n}  // namespace tachyon::circom\n\n#endif  // VENDORS_CIRCOM_BENCHMARK_RAPIDSNARK_RUNNER_H_\n"
  },
  {
    "path": "vendors/circom/benchmark/runner.h",
    "content": "#ifndef VENDORS_CIRCOM_BENCHMARK_RUNNER_H_\n#define VENDORS_CIRCOM_BENCHMARK_RUNNER_H_\n\n#include <vector>\n\n#include \"absl/types/span.h\"\n\n#include \"tachyon/base/files/file_path.h\"\n#include \"tachyon/base/time/time.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluation_domain.h\"\n#include \"tachyon/zk/r1cs/groth16/proof.h\"\n\nnamespace tachyon::circom {\n\ntemplate <typename Curve, size_t MaxDegree>\nclass Runner {\n public:\n  using F = typename Curve::G1Curve::ScalarField;\n  using Domain = math::UnivariateEvaluationDomain<F, MaxDegree>;\n\n  virtual ~Runner() = default;\n\n  virtual void LoadZKey(const base::FilePath& zkey_path) = 0;\n  virtual zk::r1cs::groth16::Proof<Curve> Run(\n      const Domain* domain, const std::vector<F>& full_assignments,\n      absl::Span<const F> public_inputs, base::TimeDelta& duration) = 0;\n};\n\n}  // namespace tachyon::circom\n\n#endif  // VENDORS_CIRCOM_BENCHMARK_RUNNER_H_\n"
  },
  {
    "path": "vendors/circom/benchmark/sha256_512.circom",
    "content": "pragma circom 2.0.0;\n\ninclude \"external/kroma_network_circomlib/circuits/sha256/sha256.circom\";\n\ncomponent main = Sha256(512);\n"
  },
  {
    "path": "vendors/circom/benchmark/sha256_512_verification_key.json",
    "content": "{\n  \"protocol\": \"groth16\",\n  \"curve\": \"bn128\",\n  \"nPublic\": 256,\n  \"vk_alpha_1\": [\n    \"2213874057995780255628336447586470277589717295488423892269818131855526079711\",\n    \"11017914333469783499739431254221639830051232127169045687222920856455806025645\",\n    \"1\"\n  ],\n  \"vk_beta_2\": [\n    [\n      \"13474304142944460764245847314567929584737312299100608468010625253981306935576\",\n      \"9478642247701415648740494757004817194776348213313791072283729888648259724073\"\n    ],\n    [\n      \"9205211045389490688024950342164291286668401494263550259639149063706566382701\",\n      \"10453562706179911794933201071363488153341746476777237580316228343568583975125\"\n    ],\n    [\n      \"1\",\n      \"0\"\n    ]\n  ],\n  \"vk_gamma_2\": [\n    [\n      \"10857046999023057135944570762232829481370756359578518086990519993285655852781\",\n      \"11559732032986387107991004021392285783925812861821192530917403151452391805634\"\n    ],\n    [\n      \"8495653923123431417604973247489272438418190587263600148770280649306958101930\",\n      \"4082367875863433681332203403145435568316851327593401208105741076214120093531\"\n    ],\n    [\n      \"1\",\n      \"0\"\n    ]\n  ],\n  \"vk_delta_2\": [\n    [\n      \"10857046999023057135944570762232829481370756359578518086990519993285655852781\",\n      \"11559732032986387107991004021392285783925812861821192530917403151452391805634\"\n    ],\n    [\n      \"8495653923123431417604973247489272438418190587263600148770280649306958101930\",\n      \"4082367875863433681332203403145435568316851327593401208105741076214120093531\"\n    ],\n    [\n      \"1\",\n      \"0\"\n    ]\n  ],\n  \"vk_alphabeta_12\": [\n    [\n      [\n        \"3230243170580878514453962036903323098317261425540343354165777908789445695525\",\n        \"5836538806005653687677040801833600855958451310061646681604049126660849313245\"\n      ],\n      [\n        \"5039771504566643966523993725737585930965368364815321600178685287273968410935\",\n        \"15616061512376741914758589780475827762418877088753733736639927279268092951251\"\n      ],\n      [\n        \"1663210507204467505584953013997811993819760606285444077443681695878956332929\",\n        \"8458712682672094937057226532734437720987487790121239564690993729421870089414\"\n      ]\n    ],\n    [\n      [\n        \"12390365768501200491624023350529977268921048120989532368719879492798402464728\",\n        \"11663358712924799981888338273615659714186069796011463491008102776682421855018\"\n      ],\n      [\n        \"21337374304544342450679054021392817640836942903910085202005475684522795658711\",\n        \"6730325974660340823958823087626979036546378876631059652870432554370239145288\"\n      ],\n      [\n        \"1204886925143927791258357609390815003120285868688273039929340967403618594682\",\n        \"13671991269879754873859690794086851741532608622891775653466096850033339778553\"\n      ]\n    ]\n  ],\n  \"IC\": [\n    [\n      \"21596400230722474605806111150454370111420203569254838423983272705704086788115\",\n      \"12801656094860194082561115328218781121465919628555202285211805718278003592156\",\n      \"1\"\n    ],\n    [\n      \"4924218440170659220422362182073152985688176952862145893994548164107107498509\",\n      \"21630085507242270807748617791775160529303401173017965260363704246177826838851\",\n      \"1\"\n    ],\n    [\n      \"1367027882275793076352082305394603771321509108492428051950828267962782008193\",\n      \"13871192154629613109347819435842272449651360028391120290225169306966797746721\",\n      \"1\"\n    ],\n    [\n      \"9670465644022516911698357563256477479674090339809963425719350026129749920905\",\n      \"4061372685290553804624527393193404008431674950396965544310454923915709949073\",\n      \"1\"\n    ],\n    [\n      \"4143237705915678980344608364683451575201727923664394796481531452564013078618\",\n      \"19907700488559495662808263690947321632770093494406052488280432021473325345596\",\n      \"1\"\n    ],\n    [\n      \"18135183392123264867553618757013407983445529495238417668707916184333923058920\",\n      \"1149399092256043057422830655830977790346912431182761535007596890897995953729\",\n      \"1\"\n    ],\n    [\n      \"19595593642411827865594570113647221237205608684605944695244037271669022372221\",\n      \"19377128035711591493439496645164643425435458803941921169573969933695953390313\",\n      \"1\"\n    ],\n    [\n      \"8296497287073893112724664827686658886876619379412751042656184096509013251882\",\n      \"19844204279762396498558454046304244319183938981270826317247874792899835010491\",\n      \"1\"\n    ],\n    [\n      \"3754398055130911154902061619581509119684912704548199171347976969617872438299\",\n      \"18406788445292384131338393822571816314619500573954027637359036110818015190790\",\n      \"1\"\n    ],\n    [\n      \"17508487418301656408019461857089640568062677646293279176205187376627851692483\",\n      \"12046322138050114653181452304514848746690722695504406871606865065524250472155\",\n      \"1\"\n    ],\n    [\n      \"12896566337835428938685863691032205615501807874316767475098252041470386611764\",\n      \"7961243416403023265487368373600598225762979209204744804178979050766183589690\",\n      \"1\"\n    ],\n    [\n      \"13676524142115468902603667622434147012006500560453639177344915363685872059485\",\n      \"17045113497824910389848761611298194393907203173055090959101849289200972733753\",\n      \"1\"\n    ],\n    [\n      \"7697388691516725150893157945328021500491296194269730234391375956727007852778\",\n      \"3621451429915695937182654622580718337659105061929236590015377317011781234643\",\n      \"1\"\n    ],\n    [\n      \"13457904723582693638214712771515908236340692064143727199733145076421411001306\",\n      \"19335162051978591312187176727827246197751236826717141583388749709014699969587\",\n      \"1\"\n    ],\n    [\n      \"4180820189131335837286614634244357146608764478487186426574512149902195353381\",\n      \"16974185199865484767334398148478886158804278858121490126971515057830543552487\",\n      \"1\"\n    ],\n    [\n      \"17493620634738889014798156992294667566727607597892497757244464328234638375158\",\n      \"2927365502863285920787377789358276676805367784143997103293435859547180870556\",\n      \"1\"\n    ],\n    [\n      \"12389064456402683170475828801899410818346159645449416602367883130476361031837\",\n      \"3464572651171727276651405951766641003886852002149076250003632819199771659333\",\n      \"1\"\n    ],\n    [\n      \"10442098207556428731097804201891666138838087763904156247662190979551462929842\",\n      \"16886803085804141572831164217887567053845811458418677830748879758910871548582\",\n      \"1\"\n    ],\n    [\n      \"322462376691151560103857643170384208958940129306013166373991863824645530270\",\n      \"17046207942108076523094233267925139731909512122912261094708643919546911001453\",\n      \"1\"\n    ],\n    [\n      \"15746726348156731582021800007582836476938979270338953551156867922869542665382\",\n      \"18413213107113079330540457659018861755309163480412705703668649944476201804260\",\n      \"1\"\n    ],\n    [\n      \"16517417836449516071755193783835762844486040962279294367369355202323610645325\",\n      \"6835377017936063642121475494254550365665453314320066531820227905595456701818\",\n      \"1\"\n    ],\n    [\n      \"10404947591919521490285704843608327730852319682470824725461335250183754275906\",\n      \"3953763427504918447221481780137999234587431891855888952405795959111701516440\",\n      \"1\"\n    ],\n    [\n      \"13109810374179041945437287887931110492505225156436598148956709105028175066196\",\n      \"5501641505734803368322886510915325990463679631521504238774278964548443980913\",\n      \"1\"\n    ],\n    [\n      \"16404882053721343339255076023681785241389967266286459934512761749688565341067\",\n      \"9897425183667576638973706372467956948433510351768811770184279195624047694361\",\n      \"1\"\n    ],\n    [\n      \"1611015822088564874000265062412141022262499205123941613485124375711234504789\",\n      \"6140510389253363710611681471834179679494127420681873137909835788955242997354\",\n      \"1\"\n    ],\n    [\n      \"9778240443745580919024571740088987076707429265850450768802030471413472395651\",\n      \"7815854362226060818058607588143677144373226509191018109053190914803819069738\",\n      \"1\"\n    ],\n    [\n      \"9486228148673434314769139680306843258731070521766389883496794765446742665110\",\n      \"6401508473676846723007993607443000979612708294598281704916347306407081844049\",\n      \"1\"\n    ],\n    [\n      \"14352939212916761781256479555591701014141590184488647545623622721974812763453\",\n      \"857664541471316007601002546877482992356219474921983604946897665498877054120\",\n      \"1\"\n    ],\n    [\n      \"8363944202510245757274943339068568434020779587748980924902270111944452851837\",\n      \"8128780679108036024733480391921561778107637837889565386982653486631063547155\",\n      \"1\"\n    ],\n    [\n      \"3290519844533244531997476487143682889158619607299359623907457058162154915299\",\n      \"21781075995702713607915360312994903977718596793858742033668876912272375184428\",\n      \"1\"\n    ],\n    [\n      \"6359589026523069865218397107135615052512675286785278718547670319971778178816\",\n      \"17522144697162430054736012680057048299247922115516066292425281484406393002286\",\n      \"1\"\n    ],\n    [\n      \"19995395490647126730376776369957022303148982985513716497892475719902898103660\",\n      \"3334051048873745562975924434159452203791602718936629380919463839666488708175\",\n      \"1\"\n    ],\n    [\n      \"4137314480328608012569236145509744849368520447226724775062014667972268604997\",\n      \"13692054026474850563270211040432841681987207117686890163283796177816210182222\",\n      \"1\"\n    ],\n    [\n      \"17552825456388409972412738421776846674362538628294050472951112446039262906414\",\n      \"15016334548234138593883612888901325975329730373119254335906374447864084593555\",\n      \"1\"\n    ],\n    [\n      \"427777315817010700687022648781006614646812745927310504940987687996887110209\",\n      \"3304252712005981010236417228368374952660473491442023864909550806174417162345\",\n      \"1\"\n    ],\n    [\n      \"1148527105915450114027873197254845521539203476822806050215553918390576499011\",\n      \"5682465568584727954286180856807668863412417265610411454092415129861817321018\",\n      \"1\"\n    ],\n    [\n      \"20444279238879181483850741222384134784516223799435831961512898554464386706206\",\n      \"7152500011488699993869546079654586006089789542025445816938745410579660749344\",\n      \"1\"\n    ],\n    [\n      \"20595958952363196540649002391472859722184080042457864064363381726959850189524\",\n      \"12912751679366586060345627837161561781271765014247835789024270476580250864017\",\n      \"1\"\n    ],\n    [\n      \"18643868012282785204724858711220921648304877570338632264308236950569126918570\",\n      \"10921843667482369956526257269226362787929060173121613084449772381083386187644\",\n      \"1\"\n    ],\n    [\n      \"15501348290187842776701788945728823325295072145911187126562217609375221151370\",\n      \"15238953712407340189037771082469831088071765029540982819619944554393674620656\",\n      \"1\"\n    ],\n    [\n      \"7239772795352376475600481539357493032332777479399336731574438985761854933215\",\n      \"4355723479584616294422145254686428709927025732610038298114036262557094392841\",\n      \"1\"\n    ],\n    [\n      \"7771035484901330445440549221785892584954493828944815236640594541343889454593\",\n      \"18891615896998054041245229014567248393318864660289270975625402247253788840844\",\n      \"1\"\n    ],\n    [\n      \"9122477798115384241259383527460057806377100984718625758297176692938867987687\",\n      \"6297979500290352701387095857113834585614997814152511665247236354104819971827\",\n      \"1\"\n    ],\n    [\n      \"8588005094089847654199764031444640138347309180652493548341223272184637016149\",\n      \"2104687155755782057020161897013637387764045906752679734885531372982232684273\",\n      \"1\"\n    ],\n    [\n      \"8635381664097000623335747203194128291350792440874644474465011186033514281316\",\n      \"11573141627217104459995519285610337404424001543839412840709128884440501218196\",\n      \"1\"\n    ],\n    [\n      \"1600349852922202462080700959164259715607110190060543228950129054375765240109\",\n      \"7278966426014152049465187061926999960932424782228719293770632878915948503310\",\n      \"1\"\n    ],\n    [\n      \"2725483150687144074122727141180887082377205242483796121958307049395237111217\",\n      \"616013919712236423842872699050817890867742364373332226049400181556556776997\",\n      \"1\"\n    ],\n    [\n      \"19715129838331848358615156683163272833307506504386809384559548111129805945113\",\n      \"4826882739601957969657261283019898810747402383415824049459232500832390188811\",\n      \"1\"\n    ],\n    [\n      \"17449683670370155752928215845476104178078141146805685607038318281996321491435\",\n      \"8685063072816485849049898743260152464760867375337173365331599443232091496468\",\n      \"1\"\n    ],\n    [\n      \"17264078540403532294639476576523551248904335415800147191932070349551509854879\",\n      \"5213302829486484947453269817633921257289296442325984490424162787438030387289\",\n      \"1\"\n    ],\n    [\n      \"15817142861495050258233289463690697915035439359191351042130632849238093632071\",\n      \"2952523185792868445018594219968508043261554329443110817765609693975302756537\",\n      \"1\"\n    ],\n    [\n      \"19812263125064350242955684964178793201441002442719331510105847106483569841940\",\n      \"14667664730249351357826878377134787326484207352205269277432212407311791996111\",\n      \"1\"\n    ],\n    [\n      \"13671506810279909657551269282485713884766409088090040898366796143919365076806\",\n      \"12309789709231558088602472631566401876289352979245085484896068419725007692374\",\n      \"1\"\n    ],\n    [\n      \"597092846710290720828044778056738396375126944154120906457001952697221174678\",\n      \"8586416044835458071658227161880189897473413857364015375462604895961262843607\",\n      \"1\"\n    ],\n    [\n      \"16312497435447108045804712291412623395991227929738802703530031237564688236280\",\n      \"13487305945499812879511313045831388639984773395406179087752545968768477862142\",\n      \"1\"\n    ],\n    [\n      \"19013647478732324457201955789324172297284471662690037632967983690034407098196\",\n      \"1345630637180672839392757434212307289061850474662880168119727505562422457554\",\n      \"1\"\n    ],\n    [\n      \"18025130957478460180428181897304133367033734609668516510828347885271945065878\",\n      \"2107624858341179394850654560610935232108583971415397433882925003826328714043\",\n      \"1\"\n    ],\n    [\n      \"19898964295760216820492105595588103557755199149163103790769643265312394739709\",\n      \"15682214542275237475997585073611529528823453138975861339503112510290448682039\",\n      \"1\"\n    ],\n    [\n      \"17947697175112935153358823887606413133323996358737938282723480295185662726062\",\n      \"17182802703824113964892038523699604856333808831412674768816020935913697468965\",\n      \"1\"\n    ],\n    [\n      \"1817571694746168019196486767824790895607906420006408064677723833692108711052\",\n      \"3820309994365241308801707771645327039015312377722889121558649461323444477358\",\n      \"1\"\n    ],\n    [\n      \"20250125832793821655654961539740473726046526656846757404106716548416485671608\",\n      \"11340071213842646230243445208786197170866590595997505393807031513042879441503\",\n      \"1\"\n    ],\n    [\n      \"12176290457788738528910949879050044784060481156246094632105996421252631233608\",\n      \"10078741102581159193356540007149321228200255577855654920272087873194179761976\",\n      \"1\"\n    ],\n    [\n      \"4702788236777966533672272450080016626814229920348855419507742256662629722599\",\n      \"791089997798144654815974681172955867494976676183041534719908655892384657069\",\n      \"1\"\n    ],\n    [\n      \"6930562310346871628882105265307968381814608626676091775749783038497628393419\",\n      \"17630020848639271083025744094874851684610912072952860482897476866720792060187\",\n      \"1\"\n    ],\n    [\n      \"11298281340994369583597982572758102932251873778759292664612820455486927455561\",\n      \"21572465774496292527097173672653212693184088079912770224759529450957056989338\",\n      \"1\"\n    ],\n    [\n      \"3436725245293648114645072269777009945663387724707364848165907811142229920110\",\n      \"16564123823809376520285815345528147616672897090111479091753239484125243968697\",\n      \"1\"\n    ],\n    [\n      \"12212983074134414599105758950130720739673101633921752946208891011239427534294\",\n      \"10297305958512104834143904103487329834348257702509859461096560234809072703279\",\n      \"1\"\n    ],\n    [\n      \"5571723874522692316547868595300483390198793643760935966353638279033004410721\",\n      \"19106037177087804650210887529555677476420752240634173159502965776741754330013\",\n      \"1\"\n    ],\n    [\n      \"14355261260866261700803616955434205116122795888360141016103484754096101918210\",\n      \"18291659653998771750572736499408214611823090828394606395482942394634687642270\",\n      \"1\"\n    ],\n    [\n      \"19634533404330092230178036088363892190438720781763538437405515759029938273787\",\n      \"11995729785746851785905034676528531299154500776485113838811537161428785000651\",\n      \"1\"\n    ],\n    [\n      \"17593122562813361578222039427126149651331001386329741884696104991102407035671\",\n      \"1912282046239288819417225419513888382325128430381666061714909196596727828904\",\n      \"1\"\n    ],\n    [\n      \"14296320034010958386217631057042257604638691520199133501803824144903332454015\",\n      \"16655912722476700698946000463184375764072884305790325455301752538193991942943\",\n      \"1\"\n    ],\n    [\n      \"12224530745978872248494895900969897837709844306467253792380362005806277006461\",\n      \"13401678776382897504750993907647002561189524994514565313973793125340134377220\",\n      \"1\"\n    ],\n    [\n      \"20538098863736570308020227136320543772477075820790523511009888664112633832730\",\n      \"14499385150394457551480159678273489212803291953831549286070157189434417532141\",\n      \"1\"\n    ],\n    [\n      \"15749031571535608802404752960939759299339318833906933777214007741658263292256\",\n      \"13105408727343445665644780634839056665544269030240151526615218359103877836305\",\n      \"1\"\n    ],\n    [\n      \"10654664486712565848379120763116580604931660404257988676174365674735723307279\",\n      \"13932441965012846548949657714806181572402806768031361903180745223977364813506\",\n      \"1\"\n    ],\n    [\n      \"9089234725253379159586251326697834746568237853805502758428440135377998512603\",\n      \"15392665695948388430939909897605228416720102581508505767906390705189059390557\",\n      \"1\"\n    ],\n    [\n      \"19084886463029814163127446107512327213743410059089797182766539453857029810101\",\n      \"6902117149555732408163434242434868455988752574617373170010235277770166722503\",\n      \"1\"\n    ],\n    [\n      \"14967978819767266272430726863539292058107955534586290802840479350851234206486\",\n      \"10066711805670295073409252335922440571856255028690182108592342841462587193434\",\n      \"1\"\n    ],\n    [\n      \"13489321735203383915849347296320100739278697084329833132025852986296382821420\",\n      \"4940454510701839581620619446651098262744731775253004061801530627136427728138\",\n      \"1\"\n    ],\n    [\n      \"19707502244605882656144700575522427872612875762563049148889362183947607243668\",\n      \"3348274138432940594536769099255718351353102852787704632688015987161479136224\",\n      \"1\"\n    ],\n    [\n      \"10355774507394880761712194682273765578021586376925936243497655059188645758984\",\n      \"3698352700412540191560219837958030686433049616110366479393230807386738033966\",\n      \"1\"\n    ],\n    [\n      \"9751110404717417386335498717207949951567641044826891921039296365100531954321\",\n      \"1428017185298693171707466541490678617387177078395159205737925622048674722327\",\n      \"1\"\n    ],\n    [\n      \"13950359541839919413229202223089777302260519558375294103261289432358028185201\",\n      \"15036746918648517723686613170861088711783992998124536200864695800467106668051\",\n      \"1\"\n    ],\n    [\n      \"14873662120781798693081274239879343104112166959535829349459128767558611891481\",\n      \"5145569551097834012440434133591549480297268168480679924048331487364313649977\",\n      \"1\"\n    ],\n    [\n      \"4197487765109561794965318408230735529027676274324024317340133882772975858349\",\n      \"18814383121087780489708559176769491050415886442233748304420639719933821892706\",\n      \"1\"\n    ],\n    [\n      \"13215226003840068548169801692022194381537838152979111773375850560000525254883\",\n      \"16495109073098411257763575827443395465036080130031787315440125324632915505361\",\n      \"1\"\n    ],\n    [\n      \"3855499925493372044348716540736581383719929458668524171857043801523214748131\",\n      \"12047348170072626679550485386176793638164784055218283364478507382640605074779\",\n      \"1\"\n    ],\n    [\n      \"16709252477522992609298758664111782093456147455207743853276138292048609019216\",\n      \"13217131187321421757358397182881963300456073235287402006235166206023391880205\",\n      \"1\"\n    ],\n    [\n      \"16923858121254268423541041132700348964389900827359461182791158816682828639740\",\n      \"19446508490976719158664477403532437264215283964204535577856194239203578528686\",\n      \"1\"\n    ],\n    [\n      \"14039833671973025480573669667894334928880550221870868297164698657287507592777\",\n      \"7586064827189441324822504247049387796757654519252077373917796262374284105374\",\n      \"1\"\n    ],\n    [\n      \"12885161107989317280449855916607635406322216334329944919026975468812730955568\",\n      \"11533648600325656088899294418878990019872954905814273834449784151869753675348\",\n      \"1\"\n    ],\n    [\n      \"2126933850920264492646106464478710290055625419141817756879986178631087971765\",\n      \"9539975280970371775784975594216074583613353930728780843322161336098914714138\",\n      \"1\"\n    ],\n    [\n      \"10311400074130790484258604006581648629201079221062133657955475673671021217587\",\n      \"12527110158685055987108611510159634294530970376058260037848192355852305465176\",\n      \"1\"\n    ],\n    [\n      \"12824243932092849438341377945062392415039984254836548257534461113622819272769\",\n      \"10446547615264037324088111372814390986604185350961572048398432074354314747604\",\n      \"1\"\n    ],\n    [\n      \"8067362878724814084685463052741017868426530947842258802180991415549609988964\",\n      \"17864277157456221474089357056625063943087936370298972063706685283293510809174\",\n      \"1\"\n    ],\n    [\n      \"16576874756741664479783705637824672895149192400112647128555172524545603716915\",\n      \"6213310818655944653851666270804830903451272852179253612035662055922602649520\",\n      \"1\"\n    ],\n    [\n      \"13610485128842099913692682809102198847862976026992073244465263172559342216218\",\n      \"14919679063030794218817792507744220480390673559925672759281100611783373523752\",\n      \"1\"\n    ],\n    [\n      \"4733883216097047166784829237113997763543458881534368669989126884436006581833\",\n      \"8692530769175478349510182779410930191932630739453545556998248640358566335303\",\n      \"1\"\n    ],\n    [\n      \"11572456092716796676081282779505307356617370266649543691135163136640496053765\",\n      \"1768384829406206346399602809784836383316420881835658292557705405735858236683\",\n      \"1\"\n    ],\n    [\n      \"7036141404112474919588315286352481512630602035987180087521187596945146502350\",\n      \"20051662999807411015241292066106873579494844556308589476263234819086511822314\",\n      \"1\"\n    ],\n    [\n      \"6850511178650522535637168111623765563732948269869146815897495083169701941871\",\n      \"18600050768835190307529791532529291770964860884168150729274201521688463431313\",\n      \"1\"\n    ],\n    [\n      \"21425983411872307964414882089911774886700435687223763311116383818418538851941\",\n      \"13666312167058686188507892376030325365428871045941705847648822333193635996094\",\n      \"1\"\n    ],\n    [\n      \"6775004925126212795627530955289980121222958551507590199531453205203889796421\",\n      \"3370354733066790537108883661795917980324332409363148885922215304365550823147\",\n      \"1\"\n    ],\n    [\n      \"4170062514488426928997097830445106329561815633860476306811116231156092241600\",\n      \"19100569559468771160956498588091655444858605124224836768975727900098925103463\",\n      \"1\"\n    ],\n    [\n      \"14268337896810909932986378298492984815520029084777559095706900510001848721695\",\n      \"5792173478334303574779823927616541389757167428279587901570947067657696916168\",\n      \"1\"\n    ],\n    [\n      \"17051818952356111436329777679379684538634991731230927610026680627861821694159\",\n      \"10481530584844385150576690580077092194608017773156198031515286876915520567988\",\n      \"1\"\n    ],\n    [\n      \"21708804930504730451954005304419077235419192960312216647166882038277886402988\",\n      \"2708715624832926923258549915562441324743232822558012280219125934364481745497\",\n      \"1\"\n    ],\n    [\n      \"21366629438349487569121756433195822346968892285526520642894822870549809265645\",\n      \"10580457978208627624304448386251171814340376505284399248877075294765048626221\",\n      \"1\"\n    ],\n    [\n      \"7766734395523622533722202195747015157282297046126396962547407604883783359365\",\n      \"18646865331735697110706878978614891604138555442276212505821350457964790395114\",\n      \"1\"\n    ],\n    [\n      \"16131775350271733970842251831835351641615637983191463540236141404345112162917\",\n      \"8106147279551477500770935960603011551110997065029700251311628557061110629356\",\n      \"1\"\n    ],\n    [\n      \"13265945053093174177320068620117056639056073827959321784740970947992064784360\",\n      \"21011877223993072200112107757932828961012382794604846367272408623748655139471\",\n      \"1\"\n    ],\n    [\n      \"14932453293005177954090333841690848124401713628923335114741646547713468038518\",\n      \"12129170417099099709214775801819011678346952775176440121229344542881964636939\",\n      \"1\"\n    ],\n    [\n      \"12435643019623464129851310519095443922520914965034227352442362250718605384092\",\n      \"1257718084882037751859059002491170287427772753607532018406612833367986637461\",\n      \"1\"\n    ],\n    [\n      \"2595581888040488663535289547486769727611390909728825302801430907976221802765\",\n      \"8118508262820563440897610891452149749559958626395090171679778099665787294070\",\n      \"1\"\n    ],\n    [\n      \"12080481405612712853858151051752309091558648501684550265913314477942885627241\",\n      \"3861730603287247307065908276477682127705110085739114068492325075564661932304\",\n      \"1\"\n    ],\n    [\n      \"6448880258301395699169593971744454665864437007996771472755630784871167530683\",\n      \"16547063060432463466688662981320057503541514145595594226174513018690835139805\",\n      \"1\"\n    ],\n    [\n      \"19573654710859427132214082589844565448435956561104101823252067720514291864047\",\n      \"8109651986445647737400936884163129823596958025941463248884163566997030549651\",\n      \"1\"\n    ],\n    [\n      \"5662652729186265766301020746718282130634440141839590116510272609349148685049\",\n      \"21443955035482273293561379262060197316613144222272810253889926738901897357443\",\n      \"1\"\n    ],\n    [\n      \"8003498002882781220236805131808610656282392939028434015901721381927286125148\",\n      \"19179911776581348801485347712219040662102259621872627096903281592699802426027\",\n      \"1\"\n    ],\n    [\n      \"4402135082148439582896697556341959148698027189574437665402181753744136513740\",\n      \"20972914504933607460771942794391919513309432237756327516532747708151326708715\",\n      \"1\"\n    ],\n    [\n      \"8164850104461311466599456224361456141394188911529062492924741047928361809248\",\n      \"4006688341577396280756829166495049475105028059095127850985792490609036061955\",\n      \"1\"\n    ],\n    [\n      \"20658293894862239860272440928996875217923273061763953649663573057662126368489\",\n      \"4426593791566981903551089266102242583046909258446703492425340145283428956368\",\n      \"1\"\n    ],\n    [\n      \"19530070813054031251479696823278693040970174801178045151291707771241366787641\",\n      \"9165658583693070415340826121648827987118403771281147710399116155133764468602\",\n      \"1\"\n    ],\n    [\n      \"3686961423614328233638554554997105184249466633695754392393291931149949249212\",\n      \"11237198048966287094477019802157574175717181997304839427628984793295043217194\",\n      \"1\"\n    ],\n    [\n      \"14203998459308347128166083034969340238594185491382847784778199349903750448527\",\n      \"6761851698035938706150575880024815208990532511709269079730715345763041867478\",\n      \"1\"\n    ],\n    [\n      \"1498285413255882100144645938625262500010656792837283880567176426640086055667\",\n      \"1802304416874263596469369596190829175801385414806533835017169369064336344559\",\n      \"1\"\n    ],\n    [\n      \"21130008249905211845407874080006401031325868932924803248805646141623603074424\",\n      \"14243544459771203515508125439372554051947139927590851192795837780783913117474\",\n      \"1\"\n    ],\n    [\n      \"8174541274992088605126849472588668600107092554953644790101256367516782479414\",\n      \"19988467191672757214223770156888884767149764590329748752482289272034920602314\",\n      \"1\"\n    ],\n    [\n      \"2109007873046689697741855417604036655034935972845049400922064201498941312315\",\n      \"15924633953433361087495019524606023963206719662119298261762330273920721148182\",\n      \"1\"\n    ],\n    [\n      \"9096104695207397544320978611632205067599823790753642914503031452200443027322\",\n      \"11648874658432361613549998860144117337537426150134893876192774494025861776389\",\n      \"1\"\n    ],\n    [\n      \"13693414077441916983992653154667890687313533207209091555712513457947851742698\",\n      \"18074226756418042215575142558843413458953073985798341494440850133748354273068\",\n      \"1\"\n    ],\n    [\n      \"16957813590483496595754913423310742235520815457373065786572371828623146873298\",\n      \"11965331634356107230609034676957541328792057027240359815354659085355175432964\",\n      \"1\"\n    ],\n    [\n      \"14162284634331124432723996429945445370425614423258432700069601504650952644777\",\n      \"5209689645095237996070090155772829502064779776566189299685585860509866464742\",\n      \"1\"\n    ],\n    [\n      \"1496254596128271431760787993057012136175321319164837126710657559006517588557\",\n      \"1704553761998832091781452792029527185376525755933709772744015088140771652166\",\n      \"1\"\n    ],\n    [\n      \"17533019089217823174973529345844737471236760172408567560253627104546665226353\",\n      \"20152127735195880018035365940157441453302119861821540784450772156541221446872\",\n      \"1\"\n    ],\n    [\n      \"1535720543964769287903285519579866895086138920237291789527345709167562319623\",\n      \"11798187586303520320439055026153104301480483651340836534524661835686772053171\",\n      \"1\"\n    ],\n    [\n      \"17480220047223641788770882709595947896017795389213743403049321767369564039722\",\n      \"10279533945183868090589638222788828548563636201776939368508360522218358612433\",\n      \"1\"\n    ],\n    [\n      \"9442985274257799398693260527595239968221682983722919998106774684034243831301\",\n      \"4702811534318387310645028857529910430411915095066529168601408674089331882347\",\n      \"1\"\n    ],\n    [\n      \"3585800664007830352822981533749054375796407520599653189442710276825980500944\",\n      \"20162434361427765354465429772924647387063284174667280590179272140595592111061\",\n      \"1\"\n    ],\n    [\n      \"10481760062654562352990035224423498958330532676851682747795087841201156446602\",\n      \"12165078449095606170106127117656183237214963967332844818081290050132717642314\",\n      \"1\"\n    ],\n    [\n      \"15811942617913093519698619701149512145379341899319191179377444119261901181528\",\n      \"9150275941980772959701046926921558675864558852521105822224832723925540274955\",\n      \"1\"\n    ],\n    [\n      \"19834782127398721136827334035390488679529150374642424103001475030720124870327\",\n      \"19807035975677408738358313292830763671841595403532462220922709748700200818028\",\n      \"1\"\n    ],\n    [\n      \"7218440121501190269593630298696105458278829486617846271504870310980027167116\",\n      \"12113578600214525412860740913348315452497862083027310499529746177525145963713\",\n      \"1\"\n    ],\n    [\n      \"17851911539225490336005785494825901736466373025247277645070791487824275318125\",\n      \"15140235837108993977055193217950123107501460509215194690974798703239675324418\",\n      \"1\"\n    ],\n    [\n      \"21380096993296858315304905259458497935530040418819913742901460635043779798348\",\n      \"10675827072483584836106266612822232787769144243515359181338145162984453241496\",\n      \"1\"\n    ],\n    [\n      \"5201620119229012550675022910164541778576580542053521524375044614953475501781\",\n      \"3199195301607623210808869398528185103057561298467577826010826651567238285017\",\n      \"1\"\n    ],\n    [\n      \"17441572967124839212828473719383463683541953462372036292941263599336736519625\",\n      \"3120455305019974658892031398586202441959982968081525742483995618575388859670\",\n      \"1\"\n    ],\n    [\n      \"11086328826573355122767277347389759278505265667453964727751920383055479682984\",\n      \"5746622476669952425969168133833533475931468495495453799691703568648201261872\",\n      \"1\"\n    ],\n    [\n      \"19477233465464057822054570730687714433927534132955959645959819082555534243224\",\n      \"1500143483903013034619147891128343540730344556041468055641426626435246801229\",\n      \"1\"\n    ],\n    [\n      \"15565815405314558259648745423782595198088236587265869788477879597166085094852\",\n      \"10881130970779394562966093456280991894768604592892089775278525163964585512228\",\n      \"1\"\n    ],\n    [\n      \"19620429877221546707282777387803451843245116880754120319347906403464426343884\",\n      \"19083476066683021781540479038948425279955293523456850757155279489630965542702\",\n      \"1\"\n    ],\n    [\n      \"16229712970627918125951233604394355793188399620102194997575766990762984692738\",\n      \"21667930013702831595263044341725673935685589711113253220379995540786045119997\",\n      \"1\"\n    ],\n    [\n      \"15299191953280665680283846712571951999174148461127128828987135628503598634895\",\n      \"18829249294694911878036070467240537972673243252960426822535842469520026562065\",\n      \"1\"\n    ],\n    [\n      \"9584530979529511189673658279881296539059895243040544509364354934260401694436\",\n      \"21225270247959439603003405323459562211236519420386772530568096499301795460209\",\n      \"1\"\n    ],\n    [\n      \"18220978338571616806579250797970224552752132639064057921959512937689875922280\",\n      \"11853156040261722723822488927708411591520323124811309044068723673645314972142\",\n      \"1\"\n    ],\n    [\n      \"6635578581815915974110450344680319376237671654657730140845798015888132845531\",\n      \"19227705722766107250013327497901239991120870197501968723037725278296384785080\",\n      \"1\"\n    ],\n    [\n      \"4369996735520205334737160304128790056011030654388817030711139101135439125575\",\n      \"14603356205947436510400609874771022021501657674433143121975695772064656467492\",\n      \"1\"\n    ],\n    [\n      \"19808424188891758597121678337402590677958600409687607673765448132346521522817\",\n      \"6142839795278603666334553008445572084327928717803056589018174964346759372929\",\n      \"1\"\n    ],\n    [\n      \"20769971880153414543325784945318485716620371423213338777798110826316587615654\",\n      \"10426058008887160358308888266023344544729998938698640944319258484245219177410\",\n      \"1\"\n    ],\n    [\n      \"11635144481925225560748446220802676129500217042449647842879983079653481842996\",\n      \"18423125354070649768317571731043188256426826016231184673822941994351602480477\",\n      \"1\"\n    ],\n    [\n      \"2288113227464696963570368899794289250618100395839823083928191885906098113525\",\n      \"16557845073681984749437052382865153721072540184714329047330732017787732610541\",\n      \"1\"\n    ],\n    [\n      \"9324303841303607793350980814717036501508656626772285674570030730530269837466\",\n      \"20004776788632559258034139479016486591710680026200063960672773549370085290414\",\n      \"1\"\n    ],\n    [\n      \"7991667975002194846966822917980965509237125742319709364359837469261001331717\",\n      \"17142453641727390296741868735936508732179941475223620178296577651524525980259\",\n      \"1\"\n    ],\n    [\n      \"1997157564944901239576756544425230340025454938940923644971506170289515300705\",\n      \"19318533637088275788395643389192926971234151120386907877030467527380810282577\",\n      \"1\"\n    ],\n    [\n      \"14648163949803317639313146762836978873780496687795432304114302635299035374064\",\n      \"12243878414545836230628869725382895036682763283289184696996401817963540665194\",\n      \"1\"\n    ],\n    [\n      \"11146080192179897175491039819944625939403207653320783209221052505513582646366\",\n      \"21536521487964041212894886877090595848405024011748399658069029319579257456345\",\n      \"1\"\n    ],\n    [\n      \"12463205407348342456352765959112948433542068617240040207511043468101968756146\",\n      \"13591383284138813405615334569225674353498970229593181484240403761368826316713\",\n      \"1\"\n    ],\n    [\n      \"5213002960886172624068995152523575987177280961757904190181831334901530430909\",\n      \"20188777306166090805310193497795099892268463490137642402031462047563516179368\",\n      \"1\"\n    ],\n    [\n      \"11000192005011351452689137075556196892835505689918003180995999743033572496377\",\n      \"21212137383967514114053278857945174727985333217648004839375810717707077207410\",\n      \"1\"\n    ],\n    [\n      \"15172609663546095889639662572725381891279697021193046567880943361089186187757\",\n      \"6603910874079483228130060099925944702961333544896608576720637002054741507763\",\n      \"1\"\n    ],\n    [\n      \"1681900774612056988864291378647915222822343877633170937374610709986404834210\",\n      \"19411233503280059031176956914516178267285246556775441684489530895388560423854\",\n      \"1\"\n    ],\n    [\n      \"13709412067352743220909298779699710525001876294104245678803164079096105667828\",\n      \"13474717517063174188587894144838850874404134922089356284458725990669086482138\",\n      \"1\"\n    ],\n    [\n      \"13753480032129760615320966413558322784583562444325280604799368158987494021455\",\n      \"9305349932459910525478736502331269559000852576805175735245755615205789905048\",\n      \"1\"\n    ],\n    [\n      \"21023762300404442418496223257932267269580292824584965791607494280930989008335\",\n      \"21043565075481881033018711038176718572038851957009702362755829993102212662741\",\n      \"1\"\n    ],\n    [\n      \"3069522527106355980103063734288490637460138391249905702549762307461012398600\",\n      \"15760761983533053801548381625997285539950343601766052589573499065747545684678\",\n      \"1\"\n    ],\n    [\n      \"16827495554051081340780814666115092068407336052864465154136138660200400602686\",\n      \"6740754325448091704038045032596743666213918929140568819336143203023635904145\",\n      \"1\"\n    ],\n    [\n      \"2434281654945551668375675717047951947431806663097637661185608806162114564422\",\n      \"10415010102017115893601317645655769008817066759940747192546467807456053360530\",\n      \"1\"\n    ],\n    [\n      \"19938118922762500957148742410975734456297611487256850310097094763235900279181\",\n      \"8536447637341114053034144088193562482287952992572998015945197883903131185881\",\n      \"1\"\n    ],\n    [\n      \"7251989917098220715051469106620033632112387619886781724238182410324796820194\",\n      \"837384384830837121336665038748100454348333382569697428211679298087422782476\",\n      \"1\"\n    ],\n    [\n      \"13399628552016025349339948283835335013218807059413561780462513610132587584806\",\n      \"5305638290371001953116874991323259516347395000258832959793049233371950648099\",\n      \"1\"\n    ],\n    [\n      \"16363087508019787067304280989691408724159115432145851060265033073170317867492\",\n      \"4828648361736664338558574211487058045458339861512882448994013253377891814359\",\n      \"1\"\n    ],\n    [\n      \"15637396138449528046793128084178074103835347887902812629475323301746676455581\",\n      \"10948950066279957923365200200326995221313527217174184586375503017689418220998\",\n      \"1\"\n    ],\n    [\n      \"11061654122006389193595032616980026317132596868452858214269301914741640707973\",\n      \"20859740286489031087631413733718273048187689155910967479455459915321621860250\",\n      \"1\"\n    ],\n    [\n      \"20734074961506965274534136029372027263361115238583803464828403876695386635258\",\n      \"4514568809493792740316447716255335066990843680874539995936993663006429107152\",\n      \"1\"\n    ],\n    [\n      \"2691285555202799386774229879612237759124668849009107131063256241412352085923\",\n      \"12369480100225652489139496208086410962873563764596898679084007661464574715544\",\n      \"1\"\n    ],\n    [\n      \"20960002661277875238558835495369870114493190650426115849623595809557921882279\",\n      \"15091653102284997497552147391673079658032133328788743957156069264689894879361\",\n      \"1\"\n    ],\n    [\n      \"10850044644457212896371705353718664820823867726581876952065365838097591276980\",\n      \"18733160522019848171760096944934504950000561729181917831873354459395333185298\",\n      \"1\"\n    ],\n    [\n      \"12930309121338132562725985737869300040666443012560731959419399345381690037994\",\n      \"6805763800236044624690668157310949471685402880407039872493879422569215151458\",\n      \"1\"\n    ],\n    [\n      \"16972512746967341798383822377427362128317340942886476716452145057758633940376\",\n      \"19215518363732518399651611596532282946268645311421117516215194169463965033425\",\n      \"1\"\n    ],\n    [\n      \"10085852664718145285832954049171800941378491474377005559135806709539806030947\",\n      \"425464139405508391831194560978275916376095156279459347255760350913076070509\",\n      \"1\"\n    ],\n    [\n      \"7856378925937722375899344417502694919826042285814263267548879802605607228091\",\n      \"16467551780880633985901781636333293258610102226153788889025507171584178570868\",\n      \"1\"\n    ],\n    [\n      \"6343316262606772121223245965187876526004935739829019557938349967678171867376\",\n      \"1246905729108836005161863977319568744777758634653965103431957594960816604342\",\n      \"1\"\n    ],\n    [\n      \"11552783790485201088589994718506632790294719169994259723843492491705168236741\",\n      \"14435060449125767522098806779757922603060815634311989061672382567677319595895\",\n      \"1\"\n    ],\n    [\n      \"344260711347670143271189690611902777840987471049639309182109224831876971123\",\n      \"20420803180083044072698454930805448232367112849101077310362638175388888758535\",\n      \"1\"\n    ],\n    [\n      \"7176383867980720024446972725765074203501790948140853040957072721395257705426\",\n      \"7316926805152487184744877829228132866634562626774323940705742792047016770269\",\n      \"1\"\n    ],\n    [\n      \"4984738574181115034987311648126385741459774250724288947267144808633951611018\",\n      \"13703291473856622969960984772377441726930668222211149261926066411567104392412\",\n      \"1\"\n    ],\n    [\n      \"4507801977964492734398437430606386680654460635179712271075607718911050945818\",\n      \"3484187344836589837083475368892930218048038561756862282474347143278564457610\",\n      \"1\"\n    ],\n    [\n      \"20561957666097528453983245755226290446337346496565733454785563732601501852735\",\n      \"583213625456508634917454811417828202769502456858119888666449934607160689034\",\n      \"1\"\n    ],\n    [\n      \"9317202620006081740991372375384979977542320245832872430938854296577292353040\",\n      \"5487743393778212987261760028135585233312457469009472853862109484424700703377\",\n      \"1\"\n    ],\n    [\n      \"9937520014896782068604320712461883521388838182899382623221680493753506163939\",\n      \"15490777197913348442005172085209390009698724372331372572515967038560915817072\",\n      \"1\"\n    ],\n    [\n      \"3425423684638717552855063708097090454055812069370673778085386887409410605189\",\n      \"17105220374211392161567853743075438930523787823010090200547272486302598992820\",\n      \"1\"\n    ],\n    [\n      \"13061449999419598317760340296670985431217248650393215334076878810205351170574\",\n      \"4915696766954516682141386435909576165171697477455202595878137026767273668208\",\n      \"1\"\n    ],\n    [\n      \"7973416847530857530406436821223054391744995196957625573593554173536026099204\",\n      \"8621251041063412405979359838791760596543296946881795275927414341272619808546\",\n      \"1\"\n    ],\n    [\n      \"16259308926144654319856923240274698902984484865086577417321723609388754851241\",\n      \"21299507698778391278115697045719922906393300030828356825364263116304954674020\",\n      \"1\"\n    ],\n    [\n      \"16456182899141584974636999008104520979128965334092415634649221240440200010906\",\n      \"15805918327493078485678784627784096341485968884918616809531111772698231748840\",\n      \"1\"\n    ],\n    [\n      \"1184176183020317076990562271126733382217240867297166933554162016291968960428\",\n      \"15907344546497351531702215625239642615389312174126135026928240960590537199262\",\n      \"1\"\n    ],\n    [\n      \"12001641006067914687987628400395475980781181831710187358688794122741075835331\",\n      \"995777315610083848308634114117995546537721499724307641398796317492833947837\",\n      \"1\"\n    ],\n    [\n      \"525264226827067407335304297535248338363849347508981942282710219519597717625\",\n      \"20787516702354281247589310026122107096458260556035706924576235138118393024123\",\n      \"1\"\n    ],\n    [\n      \"2996406020469628803462438748660121044833935798879688639323740595003194486890\",\n      \"4985904406059236048310788576010797729654316351593148596019157731928441874259\",\n      \"1\"\n    ],\n    [\n      \"15282584790296176645050380485060491486962945774522599818684239700989408300545\",\n      \"8363931815226962312375856517175139693965105648363186077197456870425686902807\",\n      \"1\"\n    ],\n    [\n      \"7507796050255468785230688961144277720029111866908889899814739929178155224185\",\n      \"12667455752882792898666173114321935891664634774536886305435398678453309871855\",\n      \"1\"\n    ],\n    [\n      \"3374237112474359501999125339787629150307340077032405691049045950650500636322\",\n      \"7931662898709672304541086617552017239156590047331893019619438815515467104078\",\n      \"1\"\n    ],\n    [\n      \"8356838190291444281731184097276661754942233709762642770498411123632123687354\",\n      \"2869068384826924394878754056156671082427649905425657856149371035186240061084\",\n      \"1\"\n    ],\n    [\n      \"4932104474646836341863500206662037552985440893086982298313859395059328356652\",\n      \"17966984112280315736442498454458311995497458045163553019368147912343507944833\",\n      \"1\"\n    ],\n    [\n      \"14028291598836334910420916327069550121442772246018534417720974889987071387923\",\n      \"10360184802636089557876265083442078756334645947004449382391675629185829617191\",\n      \"1\"\n    ],\n    [\n      \"18233664916665099600679613963802552961776967341923617440196219613913064030294\",\n      \"18666828724403844749325834577827206030165583511707293971992807905915463864985\",\n      \"1\"\n    ],\n    [\n      \"17411508558270480623851481297120800846049721553283382116001402745282261267231\",\n      \"20896946134823796915684932151768973668997400926010537073452314066129441490072\",\n      \"1\"\n    ],\n    [\n      \"20474789502347786819632344116683694341251986208573910045173387963168151135137\",\n      \"6005036890619118633301169481787032619225752616501962939507840006176819064616\",\n      \"1\"\n    ],\n    [\n      \"19382659101461459765149420270067105969530741459062914874089684397926341015173\",\n      \"8777623066569986082728871759112247709183663956137188317323916186327098721557\",\n      \"1\"\n    ],\n    [\n      \"17072213697716889549758176862089947770185729299345304845499877722220635114798\",\n      \"10083739369365944703421653393024172147762325964788061229289806322396224750515\",\n      \"1\"\n    ],\n    [\n      \"19477268242749828198498227079967889982492862621975963295686315253037644004331\",\n      \"13415703482085206437601400952411470106847493841888502134222749808498659886986\",\n      \"1\"\n    ],\n    [\n      \"3380637318577241387396210374462944165671896718493723766861702749410762334575\",\n      \"5319398477899629602330934759544839527043112077068570357109809453260343000133\",\n      \"1\"\n    ],\n    [\n      \"15950541702347284908389484286913463655136530215907319949205162545397500659012\",\n      \"13851501049818140789415742550800877238979014108430275019900363063782830466723\",\n      \"1\"\n    ],\n    [\n      \"10259910842274379340835583757921103600997691993999695743426745052785181601704\",\n      \"9321547238734240185037595644533626380687860271950066485551687934994088539552\",\n      \"1\"\n    ],\n    [\n      \"14303698296012342444801528417717282799830953880648233712908910192116933740739\",\n      \"6364449284116512817919996858547066827671952346082442385246508555784453340224\",\n      \"1\"\n    ],\n    [\n      \"13586988638658560070150097573718640365667046872380434568967767812341043201926\",\n      \"8390350847147935447371973881164348436781468162663290992620504062895377262666\",\n      \"1\"\n    ],\n    [\n      \"17982465724665235160701589262847679781395631248010874282281707313556884421397\",\n      \"13878118709158686323916769562919233074355967877326249662778369868979488418956\",\n      \"1\"\n    ],\n    [\n      \"19243725895075602042259390503602073846478186039307022549608704627549317177286\",\n      \"9665291444702816013872323732844952401326127385441551340758457697010832184524\",\n      \"1\"\n    ],\n    [\n      \"6646059594989145743470541769308989295099349000239157777233984619946298068794\",\n      \"17544932902266203709603473923387147218084161806994578339951800379057841875297\",\n      \"1\"\n    ],\n    [\n      \"1591678156543994146857405658368189744241782168943098408445497990177818030362\",\n      \"13768999839041739811898285479847230499130032261129271811134623930740258096299\",\n      \"1\"\n    ],\n    [\n      \"2737274148997010340062130309847134382082965504781498628495035845498524658849\",\n      \"352163855968042252267411675482714803211470578233567457516965319771042862386\",\n      \"1\"\n    ],\n    [\n      \"4898878512786284077395692976570111705906969009810048770132700116021739179110\",\n      \"12351124818259466308404866524403347416763875843195935313331369623431868667830\",\n      \"1\"\n    ],\n    [\n      \"1073634321571916484079435689821246121398499819693684676567517199507066043745\",\n      \"11329521436426421254823218690089940153994669982489837344262213160257079599435\",\n      \"1\"\n    ],\n    [\n      \"8794075422092935085068392036832343681011201264560333506654332847837083829875\",\n      \"11260274428695783166837472636792252008304844004999285525431277430570307220720\",\n      \"1\"\n    ],\n    [\n      \"13225939892783643527999103300155433560119119823091889458855272991073982289905\",\n      \"16974956825682160270530784276714166179679447749399204157069966718627495881070\",\n      \"1\"\n    ],\n    [\n      \"8674413902169467017540643282750228750111123500647726836864678246795509172442\",\n      \"16579429274668464806983980739230678410479944876626083269622237404925922708734\",\n      \"1\"\n    ],\n    [\n      \"17420205865555469903815677252238179762772999075788816055008590888081483365012\",\n      \"5636335945889519507861683061632198158492873391514522973156737586773286670442\",\n      \"1\"\n    ],\n    [\n      \"7044372096069407089172852178766188242412208440931709127188121192496427961058\",\n      \"14466534762359133323947176060096570350256970494691000612188098934505041213277\",\n      \"1\"\n    ],\n    [\n      \"20754323112656300793691911000658559014770702756797415754003534610911924359392\",\n      \"10847741852195030501613928875622931944732781622449561590549688629528870910238\",\n      \"1\"\n    ],\n    [\n      \"20838627453630636084102344350866662876581188416849821838062091163572994377998\",\n      \"15482644476688918256950334676347811187570268864912469492042531069910484599267\",\n      \"1\"\n    ],\n    [\n      \"18084707624975206546670032103155512781850852355064540539407626700840762603756\",\n      \"269947621812960407137861990932613514042565093255152223315777855230601867929\",\n      \"1\"\n    ],\n    [\n      \"3734620660137381490144452327589030061368676265537954597432409804787792322986\",\n      \"10254472488710460290466410570561161609317358729147750983622699692212527317061\",\n      \"1\"\n    ],\n    [\n      \"21299040006321422378998277414480307277416584626523063293924262585282101136696\",\n      \"11912156096569363316825027509910937847534314060409467203037113757679985863873\",\n      \"1\"\n    ],\n    [\n      \"4629248835683101917598545492904469187299950242813998271411069605431689412499\",\n      \"13056323367291675963647676659257600286436484074887139941536080615826337592923\",\n      \"1\"\n    ],\n    [\n      \"2081611624657670252021456891204065421212432238875072502193409987692214611070\",\n      \"14202048374487627264003649939327619099218920184970380837086589206643623865910\",\n      \"1\"\n    ],\n    [\n      \"16649398681277142511008745441977050645590506552434493565970147667274681725903\",\n      \"6474253597976323931864015502842225319938374344377112178287013649285757406937\",\n      \"1\"\n    ],\n    [\n      \"484679466551860742995844734802157292203372907915809130047481418453562100961\",\n      \"15334214071164524281282500362471781650037963621101648270956374123400820590102\",\n      \"1\"\n    ],\n    [\n      \"10580144777517262992595189871519072771488420691998556280600934111502882484182\",\n      \"1382850068694217884422024544446723085960383134021500376363557335146367162939\",\n      \"1\"\n    ],\n    [\n      \"5262539477737796339571221585341507785852980620395603492935682262098120262150\",\n      \"20155286676885695676686046898012296453660382481464090672986133227281320183546\",\n      \"1\"\n    ],\n    [\n      \"9521312093709231630049972454664673916802767619408268720242014921853229069011\",\n      \"13946141033534955765272947933400173109499154523635880009987095455122874449461\",\n      \"1\"\n    ],\n    [\n      \"10829400137759471402392702191118360744533898492673651014730908906932919032804\",\n      \"16155175539900788414689478680179940495742438445458071439443000236558153741472\",\n      \"1\"\n    ],\n    [\n      \"20833781209190373745344963608983402652382353035823692819075167193265096931314\",\n      \"14925549607415910083763028659559080643027512487495837573829820891092312346251\",\n      \"1\"\n    ],\n    [\n      \"20846359739632003748060435164232826270010291696177995957434619651660268748776\",\n      \"1364972687875443822370495524540012124483104278857100463193042633097010424923\",\n      \"1\"\n    ],\n    [\n      \"16745450734217513920110040247239591395269979845536646821054279102916535027169\",\n      \"5616017356628556741236241110049175192939014156179578664358844821683712019691\",\n      \"1\"\n    ],\n    [\n      \"20606847015583796178699804738813656517633798259338101901982590265473679331319\",\n      \"4249263584219993596000138391581653869311014800462611950899491472207379008578\",\n      \"1\"\n    ],\n    [\n      \"18708608199591466253859190797719919730553763406249780781429587129951221082920\",\n      \"14817080627173157896389384732154127998119037645960333975209379385344961942790\",\n      \"1\"\n    ]\n  ]\n}"
  },
  {
    "path": "vendors/circom/benchmark/tachyon_runner.h",
    "content": "#ifndef VENDORS_CIRCOM_BENCHMARK_TACHYON_RUNNER_H_\n#define VENDORS_CIRCOM_BENCHMARK_TACHYON_RUNNER_H_\n\n#include <memory>\n#include <optional>\n#include <vector>\n\n// clang-format off\n#include \"benchmark/runner.h\"\n// clang-format on\n#include \"circomlib/circuit/quadratic_arithmetic_program.h\"\n#include \"circomlib/circuit/witness_loader.h\"\n#include \"circomlib/zkey/zkey.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/time/time.h\"\n#include \"tachyon/zk/r1cs/groth16/prove.h\"\n#include \"tachyon/zk/r1cs/groth16/verify.h\"\n\nnamespace tachyon::circom {\n\ntemplate <typename Curve, size_t MaxDegree>\nclass TachyonRunner : public Runner<Curve, MaxDegree> {\n public:\n  using F = typename Curve::G1Curve::ScalarField;\n  using Domain = math::UnivariateEvaluationDomain<F, MaxDegree>;\n\n  explicit TachyonRunner(const base::FilePath& data_path)\n      : witness_loader_(data_path) {}\n\n  WitnessLoader<F>& witness_loader() { return witness_loader_; }\n\n  const zk::r1cs::groth16::ProvingKey<Curve>& proving_key() const {\n    return proving_key_;\n  }\n\n  size_t GetDomainSize() const { return zkey_->GetDomainSize(); }\n\n  size_t GetNumInstanceVariables() const {\n    return zkey_->GetNumInstanceVariables();\n  }\n\n  size_t GetNumWitnessVariables() const {\n    return zkey_->GetNumWitnessVariables();\n  }\n\n  void LoadZKey(const base::FilePath& zkey_path) override {\n    zkey_ = ParseZKey<Curve>(zkey_path);\n    CHECK(zkey_);\n\n    proving_key_ = zkey_->GetProvingKey().ToNativeProvingKey();\n    coefficients_ = zkey_->GetCoefficients();\n  }\n\n  zk::r1cs::groth16::Proof<Curve> Run(const Domain* domain,\n                                      const std::vector<F>& full_assignments,\n                                      absl::Span<const F> public_inputs,\n                                      base::TimeDelta& delta) override {\n    base::TimeTicks now = base::TimeTicks::Now();\n\n    std::vector<F> h_evals =\n        QuadraticArithmeticProgram<F>::WitnessMapFromMatrices(\n            domain, coefficients_, full_assignments);\n\n    size_t num_instance_variables = GetNumInstanceVariables();\n    zk::r1cs::groth16::Proof<Curve> proof =\n        zk::r1cs::groth16::CreateProofWithAssignmentNoZK(\n            proving_key_, absl::MakeConstSpan(h_evals),\n            absl::MakeConstSpan(full_assignments)\n                .subspan(1, num_instance_variables - 1),\n            absl::MakeConstSpan(full_assignments)\n                .subspan(num_instance_variables),\n            absl::MakeConstSpan(full_assignments).subspan(1));\n\n    delta = base::TimeTicks::Now() - now;\n\n    if (!prepared_verifying_key_.has_value()) {\n      prepared_verifying_key_ =\n          proving_key_.verifying_key().ToPreparedVerifyingKey();\n    }\n    CHECK(zk::r1cs::groth16::VerifyProof(*prepared_verifying_key_, proof,\n                                         public_inputs));\n\n    return proof;\n  }\n\n private:\n  WitnessLoader<F> witness_loader_;\n  std::unique_ptr<ZKey<Curve>> zkey_;\n  zk::r1cs::groth16::ProvingKey<Curve> proving_key_;\n  absl::Span<const Coefficient<F>> coefficients_;\n  std::optional<zk::r1cs::groth16::PreparedVerifyingKey<Curve>>\n      prepared_verifying_key_;\n};\n\n}  // namespace tachyon::circom\n\n#endif  // VENDORS_CIRCOM_BENCHMARK_TACHYON_RUNNER_H_\n"
  },
  {
    "path": "vendors/circom/build_defs.bzl",
    "content": "def witness_gen_library(\n        name,\n        gendep,\n        prime = \"bn128\"):\n    native.cc_library(\n        name = name,\n        srcs = [\n            gendep,\n            \"@kroma_network_circom//circomlib/generated/common:common_srcs\",\n        ],\n        data = [gendep],\n        deps = [\n            \"@com_google_absl//absl/container:flat_hash_map\",\n            \"@kroma_network_circom//circomlib/generated/common:common_hdrs\",\n            \"@kroma_network_circom//circomlib/generated/{}:fr\".format(prime),\n            \"@nlohmann_json//:json\",\n        ],\n    )\n"
  },
  {
    "path": "vendors/circom/circom.BUILD",
    "content": "CURVES = [\n    \"bls12381\",\n    \"bn128\",\n    \"goldilocks\",\n    \"grumpkin\",\n    \"pallas\",\n    \"secq256r1\",\n    \"vesta\",\n]\n\nTPLS = [\n    \"code_producers/src/c_elements/{}/fr.asm\",\n    \"code_producers/src/c_elements/{}/fr.cpp\",\n    \"code_producers/src/c_elements/{}/fr.hpp\",\n]\n\nC_ELEMENTS = [tpl.format(curve) for tpl in TPLS for curve in CURVES]\n\nfilegroup(\n    name = \"generated_files\",\n    srcs = C_ELEMENTS + [\n        \"code_producers/src/c_elements/common/calcwit.cpp\",\n        \"code_producers/src/c_elements/common/calcwit.hpp\",\n        \"code_producers/src/c_elements/common/circom.hpp\",\n        \"code_producers/src/c_elements/common/main.cpp\",\n    ],\n    visibility = [\"//visibility:public\"],\n)\n"
  },
  {
    "path": "vendors/circom/circomlib/CPPLINT.cfg",
    "content": "exclude_files=generated\n"
  },
  {
    "path": "vendors/circom/circomlib/base/BUILD.bazel",
    "content": "load(\"@kroma_network_tachyon//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"fr_element_conversion\",\n    hdrs = [\"fr_element_conversion.h\"],\n)\n\ntachyon_cc_library(\n    name = \"modulus\",\n    srcs = [\"modulus.cc\"],\n    hdrs = [\"modulus.h\"],\n    deps = [\n        \"@kroma_network_tachyon//tachyon/base:logging\",\n        \"@kroma_network_tachyon//tachyon/base/buffer:endian_auto_reset\",\n        \"@kroma_network_tachyon//tachyon/math/base:big_int\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"sections\",\n    hdrs = [\"sections.h\"],\n    deps = [\n        \"@kroma_network_tachyon//tachyon/base:logging\",\n        \"@kroma_network_tachyon//tachyon/base:range\",\n        \"@kroma_network_tachyon//tachyon/base/buffer:endian_auto_reset\",\n        \"@kroma_network_tachyon//tachyon/base/buffer:read_only_buffer\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"base_unittests\",\n    srcs = [\n        \"modulus_unittest.cc\",\n        \"sections_unittest.cc\",\n    ],\n    deps = [\n        \":modulus\",\n        \":sections\",\n        \"@kroma_network_tachyon//tachyon/base/buffer:copyable\",\n        \"@kroma_network_tachyon//tachyon/base/buffer:vector_buffer\",\n        \"@kroma_network_tachyon//tachyon/math/elliptic_curves/bn/bn254:g1\",\n        \"@kroma_network_tachyon//tachyon/math/finite_fields/test:finite_field_test\",\n    ],\n)\n"
  },
  {
    "path": "vendors/circom/circomlib/base/fr_element_conversion.h",
    "content": "#ifndef VENDORS_CIRCOM_CIRCOMLIB_BASE_FR_ELEMENT_CONVERSION_H_\n#define VENDORS_CIRCOM_CIRCOMLIB_BASE_FR_ELEMENT_CONVERSION_H_\n\n#include <string.h>\n\n#include <utility>\n\n#include \"fr.hpp\"  // NOLINT(build/include_subdir)\n\nnamespace tachyon::circom {\n\ntemplate <typename F>\nFrElement ConvertToFrElement(const F& value) {\n  FrElement fr;\n  fr.type = Fr_LONGMONTGOMERY;\n  memcpy(fr.longVal, value.value().limbs, sizeof(uint64_t) * F::kLimbNums);\n  return fr;\n}\n\ntemplate <typename F>\nF ConvertFromFrElement(FrElement& value) {\n  using BigInt = typename F::BigIntTy;\n  FrElement tmp;\n  Fr_toLongNormal(&tmp, &value);\n  BigInt bigint;\n  memcpy(bigint.limbs, tmp.longVal, sizeof(uint64_t) * F::kLimbNums);\n  return F(std::move(bigint));\n}\n\n}  // namespace tachyon::circom\n\n#endif  // VENDORS_CIRCOM_CIRCOMLIB_BASE_FR_ELEMENT_CONVERSION_H_\n"
  },
  {
    "path": "vendors/circom/circomlib/base/modulus.cc",
    "content": "#include \"circomlib/base/modulus.h\"\n\nnamespace tachyon::circom {\n\nstd::string Modulus::ToString() const {\n#define MODULUS_TO_STRING(n) ToBigInt<n>().ToString()\n  switch (bytes.size() / 8) {\n    case 1:\n      return MODULUS_TO_STRING(1);\n    case 2:\n      return MODULUS_TO_STRING(2);\n    case 3:\n      return MODULUS_TO_STRING(3);\n    case 4:\n      return MODULUS_TO_STRING(4);\n    case 5:\n      return MODULUS_TO_STRING(5);\n    case 6:\n      return MODULUS_TO_STRING(6);\n    case 7:\n      return MODULUS_TO_STRING(7);\n    case 8:\n      return MODULUS_TO_STRING(8);\n    case 9:\n      return MODULUS_TO_STRING(9);\n  }\n#undef MODULUS_TO_STRING\n  NOTREACHED();\n  return \"\";\n}\n\n}  // namespace tachyon::circom\n"
  },
  {
    "path": "vendors/circom/circomlib/base/modulus.h",
    "content": "#ifndef VENDORS_CIRCOM_CIRCOMLIB_BASE_MODULUS_H_\n#define VENDORS_CIRCOM_CIRCOMLIB_BASE_MODULUS_H_\n\n#include <stddef.h>\n#include <stdint.h>\n#include <string.h>\n\n#include <array>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"tachyon/base/buffer/endian_auto_reset.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/math/base/big_int.h\"\n\nnamespace tachyon::circom {\n\nstruct Modulus {\n  std::vector<uint8_t> bytes;\n\n  bool operator==(const Modulus& other) const { return bytes == other.bytes; }\n  bool operator!=(const Modulus& other) const { return bytes != other.bytes; }\n\n  template <size_t N>\n  math::BigInt<N> ToBigInt() const {\n    CHECK_EQ(bytes.size() / 8, N);\n    return math::BigInt<N>::FromBytesLE(bytes);\n  }\n\n  template <size_t N>\n  static Modulus FromBigInt(const math::BigInt<N>& big_int) {\n    std::array<uint8_t, N * 8> bytes = big_int.ToBytesLE();\n    return {{bytes.begin(), bytes.end()}};\n  }\n\n  bool Read(const base::ReadOnlyBuffer& buffer) {\n    base::EndianAutoReset reset(buffer, base::Endian::kLittle);\n    uint32_t num_bytes;\n    if (!buffer.Read(&num_bytes)) return false;\n    if (num_bytes % 8 != 0) {\n      LOG(ERROR) << \"field size is not a multiple of 8\";\n      return false;\n    }\n    bytes.resize(num_bytes);\n    return buffer.Read(bytes.data(), bytes.size());\n  }\n\n  std::string ToString() const;\n};\n\n}  // namespace tachyon::circom\n\n#endif  // VENDORS_CIRCOM_CIRCOMLIB_BASE_MODULUS_H_\n"
  },
  {
    "path": "vendors/circom/circomlib/base/modulus_unittest.cc",
    "content": "#include \"circomlib/base/modulus.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/buffer/copyable.h\"\n#include \"tachyon/base/buffer/vector_buffer.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/fr.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n\nnamespace tachyon::circom {\n\nusing F = math::bn254::Fr;\n\nclass ModulusTest : public math::FiniteFieldTest<F> {};\n\nTEST_F(ModulusTest, BigIntConversions) {\n  math::BigInt<4> expected = math::BigInt<4>::Random();\n  Modulus field = Modulus::FromBigInt(expected);\n  EXPECT_EQ(field.ToBigInt<4>(), expected);\n}\n\nTEST_F(ModulusTest, Read) {\n  std::array<uint8_t, 8> data;\n\n  {\n    base::Uint8VectorBuffer buffer;\n    Modulus field;\n    // Should return false when it fails to read the field size.\n    ASSERT_FALSE(field.Read(buffer));\n  }\n\n  {\n    base::Uint8VectorBuffer buffer;\n    ASSERT_TRUE(buffer.Write(uint32_t{3}));\n    buffer.set_buffer_offset(0);\n    Modulus field;\n    // Should return false when the field size is not a multiple of 8.\n    ASSERT_FALSE(field.Read(buffer));\n  }\n\n  {\n    base::Uint8VectorBuffer buffer;\n    ASSERT_TRUE(buffer.Write(uint32_t{8}));\n    buffer.set_buffer_offset(0);\n    Modulus field;\n    // Should return false when it fails to read the field data.\n    ASSERT_FALSE(field.Read(buffer));\n  }\n\n  {\n    base::Uint8VectorBuffer buffer;\n    ASSERT_TRUE(buffer.Write(uint32_t{8}));\n    ASSERT_TRUE(buffer.Write(data));\n    buffer.set_buffer_offset(0);\n    Modulus field;\n    ASSERT_TRUE(field.Read(buffer));\n  }\n}\n\n}  // namespace tachyon::circom\n"
  },
  {
    "path": "vendors/circom/circomlib/base/sections.h",
    "content": "#ifndef VENDORS_CIRCOM_CIRCOMLIB_BASE_SECTIONS_H_\n#define VENDORS_CIRCOM_CIRCOMLIB_BASE_SECTIONS_H_\n\n#include <stdint.h>\n\n#include <string_view>\n#include <vector>\n\n#include \"tachyon/base/buffer/endian_auto_reset.h\"\n#include \"tachyon/base/buffer/read_only_buffer.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/range.h\"\n\nnamespace tachyon::circom {\n\ntemplate <typename T>\nstruct Section {\n  T type;\n  base::Range<uint64_t> range;\n};\n\ntemplate <typename T>\nclass Sections {\n public:\n  typedef std::string_view (*ErrorFn)(T type);\n\n  Sections(const base::ReadOnlyBuffer& buffer, ErrorFn error_fn)\n      : buffer_(buffer), error_fn_(error_fn) {}\n\n  bool Read() {\n    base::EndianAutoReset reset(buffer_, base::Endian::kLittle);\n    uint32_t num_sections;\n    if (!buffer_.Read(&num_sections)) return false;\n\n    sections_.reserve(num_sections);\n    for (uint32_t i = 0; i < num_sections; ++i) {\n      if (!Add()) return false;\n    }\n    return true;\n  }\n\n  bool MoveTo(T type) const {\n    auto it = std::find_if(\n        sections_.begin(), sections_.end(),\n        [type](const Section<T>& section) { return section.type == type; });\n    if (it == sections_.end()) {\n      LOG(ERROR) << error_fn_(type) << \" is empty\";\n      return false;\n    }\n    buffer_.set_buffer_offset(it->range.from);\n    return true;\n  }\n\n private:\n  bool Add() {\n    T type;\n    uint64_t size;\n    if (!buffer_.ReadMany(&type, &size)) return false;\n\n    sections_.push_back({type, {buffer_.buffer_offset(), size}});\n    buffer_.set_buffer_offset(buffer_.buffer_offset() + size);\n    return true;\n  }\n\n  const base::ReadOnlyBuffer& buffer_;\n  ErrorFn error_fn_;\n  std::vector<Section<T>> sections_;\n};\n\n}  // namespace tachyon::circom\n\n#endif  // VENDORS_CIRCOM_CIRCOMLIB_BASE_SECTIONS_H_\n"
  },
  {
    "path": "vendors/circom/circomlib/base/sections_unittest.cc",
    "content": "#include \"circomlib/base/sections.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/buffer/copyable.h\"\n#include \"tachyon/base/buffer/vector_buffer.h\"\n#include \"tachyon/base/logging.h\"\n\nnamespace tachyon::circom {\n\nnamespace {\n\nenum class Type {\n  kDummy,\n  kDummy2,\n};\n\nstd::string_view TypeToString(Type type) {\n  switch (type) {\n    case Type::kDummy:\n      return \"Dummy\";\n    case Type::kDummy2:\n      return \"Dummy2\";\n  }\n  NOTREACHED();\n  return \"\";\n}\n\n}  // namespace\n\nTEST(SectionsTest, ReadAndGet) {\n  {\n    base::Uint8VectorBuffer buffer;\n    Sections<Type> sections(buffer, &TypeToString);\n    // Should return false when it fails to read the number of sections.\n    ASSERT_FALSE(sections.Read());\n  }\n\n  {\n    base::Uint8VectorBuffer buffer;\n    ASSERT_TRUE(buffer.Write(uint32_t{1}));\n    buffer.set_buffer_offset(0);\n    Sections<Type> sections(buffer, &TypeToString);\n    // Should return false when it fails to read the section type.\n    ASSERT_FALSE(sections.Read());\n  }\n\n  {\n    base::Uint8VectorBuffer buffer;\n    ASSERT_TRUE(buffer.Write(uint32_t{1}));\n    ASSERT_TRUE(buffer.Write(Type::kDummy));\n    buffer.set_buffer_offset(0);\n    Sections<Type> sections(buffer, &TypeToString);\n    // Should return false when it fails to read the section size.\n    ASSERT_FALSE(sections.Read());\n  }\n\n  {\n    base::Uint8VectorBuffer buffer;\n    ASSERT_TRUE(buffer.Write(uint32_t{1}));\n    ASSERT_TRUE(buffer.Write(Type::kDummy));\n    ASSERT_TRUE(buffer.Write(uint64_t{32}));\n    size_t expected = buffer.buffer_offset();\n    buffer.set_buffer_offset(0);\n    Sections<Type> sections(buffer, &TypeToString);\n    ASSERT_TRUE(sections.Read());\n\n    ASSERT_FALSE(sections.MoveTo(Type::kDummy2));\n    ASSERT_TRUE(sections.MoveTo(Type::kDummy));\n    EXPECT_EQ(buffer.buffer_offset(), expected);\n  }\n}\n\n}  // namespace tachyon::circom\n"
  },
  {
    "path": "vendors/circom/circomlib/circuit/BUILD.bazel",
    "content": "load(\"@kroma_network_tachyon//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"circuit\",\n    hdrs = [\"circuit.h\"],\n    deps = [\n        \":witness_loader\",\n        \"//circomlib/r1cs\",\n        \"@kroma_network_tachyon//tachyon/zk/r1cs/constraint_system:circuit\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"quadratic_arithmetic_program\",\n    hdrs = [\"quadratic_arithmetic_program.h\"],\n    deps = [\n        \"//circomlib/zkey:coefficient\",\n        \"@kroma_network_tachyon//tachyon/base:logging\",\n        \"@kroma_network_tachyon//tachyon/base:profiler\",\n        \"@kroma_network_tachyon//tachyon/zk/r1cs/constraint_system:quadratic_arithmetic_program\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"witness_loader\",\n    hdrs = [\"witness_loader.h\"],\n    deps = [\n        \"//circomlib/base:fr_element_conversion\",\n        \"@kroma_network_tachyon//tachyon/base/containers:container_util\",\n        \"@kroma_network_tachyon//tachyon/base/files:file_path\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"circuit_test\",\n    testonly = True,\n    hdrs = [\"circuit_test.h\"],\n    deps = [\n        \":circuit\",\n        \"//circomlib/r1cs\",\n        \"//circomlib/zkey\",\n        \"@kroma_network_tachyon//tachyon/math/elliptic_curves/bn/bn254\",\n        \"@kroma_network_tachyon//tachyon/math/polynomials/univariate:univariate_evaluation_domain_factory\",\n        \"@kroma_network_tachyon//tachyon/zk/r1cs/groth16:prove\",\n        \"@kroma_network_tachyon//tachyon/zk/r1cs/groth16:verify\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"adder_circuit_unittest\",\n    srcs = [\"adder_circuit_unittest.cc\"],\n    data = [\n        \"adder_data.json\",\n        \"//examples:adder.zkey\",\n        \"//examples:compile_adder\",\n    ],\n    deps = [\n        \":circuit_test\",\n        \":quadratic_arithmetic_program\",\n        \"//circomlib/r1cs\",\n        \"//circomlib/zkey\",\n        \"//examples:gen_witness_adder\",\n        \"@kroma_network_tachyon//tachyon/zk/r1cs/constraint_system:quadratic_arithmetic_program\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"multiplier_3_circuit_unittest\",\n    srcs = [\"multiplier_3_circuit_unittest.cc\"],\n    data = [\n        \"multiplier_3_data.json\",\n        \"//examples:compile_multiplier_3\",\n        \"//examples:multiplier_3.zkey\",\n    ],\n    deps = [\n        \":circuit_test\",\n        \":quadratic_arithmetic_program\",\n        \"//circomlib/r1cs\",\n        \"//circomlib/zkey\",\n        \"//examples:gen_witness_multiplier_3\",\n        \"@kroma_network_tachyon//tachyon/zk/r1cs/constraint_system:quadratic_arithmetic_program\",\n    ],\n)\n"
  },
  {
    "path": "vendors/circom/circomlib/circuit/adder_circuit_unittest.cc",
    "content": "#include \"circomlib/circuit/circuit_test.h\"\n#include \"circomlib/circuit/quadratic_arithmetic_program.h\"\n#include \"circomlib/r1cs/r1cs.h\"\n#include \"circomlib/zkey/zkey.h\"\n#include \"tachyon/zk/r1cs/constraint_system/quadratic_arithmetic_program.h\"\n\nnamespace tachyon::circom {\n\nclass AdderCircuitTest : public CircuitTest {\n public:\n  void SetUp() override {\n    r1cs_ = ParseR1CS<F>(base::FilePath(\"examples/adder.r1cs\"));\n    ASSERT_TRUE(r1cs_);\n  }\n\n  void LoadRandomWitness() {\n    circuit_.reset(new Circuit<F>(\n        r1cs_.get(), base::FilePath(\"examples/adder_cpp/adder.dat\")));\n    std::vector<uint32_t> values = base::CreateVector(\n        2, []() { return base::Uniform(base::Range<uint32_t>()); });\n    circuit_->witness_loader().Set(\"a\", F(values[0]));\n    circuit_->witness_loader().Set(\"b\", F(values[1]));\n    circuit_->witness_loader().Load();\n    std::vector<F> public_inputs = circuit_->GetPublicInputs();\n    ASSERT_EQ(public_inputs.size(), 1);\n    ASSERT_EQ(public_inputs[0], F(values[0] + values[1]));\n  }\n\n  void LoadWitnessFromJson() {\n    circuit_.reset(new Circuit<F>(\n        r1cs_.get(), base::FilePath(\"examples/adder_cpp/adder.dat\")));\n    circuit_->witness_loader().Load(\n        base::FilePath(\"circomlib/circuit/adder_data.json\"));\n    std::vector<F> public_inputs = circuit_->GetPublicInputs();\n    ASSERT_EQ(public_inputs.size(), 1);\n    ASSERT_EQ(public_inputs[0], F(7));\n  }\n};\n\nTEST_F(AdderCircuitTest, Synthesize) {\n  LoadRandomWitness();\n  this->SynthesizeTest();\n\n  LoadWitnessFromJson();\n  this->SynthesizeTest();\n}\n\nTEST_F(AdderCircuitTest, Groth16ProveAndVerify) {\n  constexpr size_t kMaxDegree = 127;\n  LoadRandomWitness();\n  this->Groth16ProveAndVerifyTest<kMaxDegree,\n                                  zk::r1cs::QuadraticArithmeticProgram<F>>();\n}\n\nTEST_F(AdderCircuitTest, Groth16ProveAndVerifyUsingZKey) {\n  constexpr size_t kMaxDegree = 127;\n\n  LoadRandomWitness();\n  std::unique_ptr<ZKey<Curve>> zkey =\n      ParseZKey<Curve>(base::FilePath(\"examples/adder.zkey\"));\n  ASSERT_TRUE(zkey);\n\n  std::vector<F> full_assignments = base::CreateVector(\n      r1cs_->GetNumVariables(),\n      [this](size_t i) { return circuit_->witness_loader().Get(i); });\n\n  this->Groth16ProveAndVerifyUsingZKeyTest<kMaxDegree,\n                                           QuadraticArithmeticProgram<F>>(\n      *zkey, full_assignments);\n}\n\n}  // namespace tachyon::circom\n"
  },
  {
    "path": "vendors/circom/circomlib/circuit/adder_data.json",
    "content": "{\n  \"a\": \"3\",\n  \"b\": \"4\"\n}\n"
  },
  {
    "path": "vendors/circom/circomlib/circuit/circuit.h",
    "content": "#ifndef VENDORS_CIRCOM_CIRCOMLIB_CIRCUIT_CIRCUIT_H_\n#define VENDORS_CIRCOM_CIRCOMLIB_CIRCUIT_CIRCUIT_H_\n\n#include <string_view>\n#include <vector>\n\n#include \"circomlib/circuit/witness_loader.h\"\n#include \"circomlib/r1cs/r1cs.h\"\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/zk/r1cs/constraint_system/circuit.h\"\n\nnamespace tachyon::circom {\n\ntemplate <typename F>\nclass Circuit : public zk::r1cs::Circuit<F> {\n public:\n  Circuit(R1CS<F>* r1cs, const base::FilePath& data)\n      : r1cs_(r1cs), witness_loader_(data) {}\n\n  WitnessLoader<F>& witness_loader() { return witness_loader_; }\n\n  void Synthesize(\n      zk::r1cs::ConstraintSystem<F>& constraint_system) const override {\n    size_t i = 1;\n    for (; i < r1cs_->GetNumInstanceVariables(); ++i) {\n      constraint_system.CreateInstanceVariable(\n          [this, i]() { return witness_loader_.Get(i); });\n    }\n    for (; i < r1cs_->GetNumVariables(); ++i) {\n      constraint_system.CreateWitnessVariable(\n          [this, i]() { return witness_loader_.Get(i); });\n    }\n    for (const Constraint<F>& constraint : r1cs_->GetConstraints()) {\n      constraint_system.EnforceConstraint(\n          MakeLC(constraint.a), MakeLC(constraint.b), MakeLC(constraint.c));\n    }\n  }\n\n  std::vector<F> GetPublicInputs() const {\n    return base::CreateVector(\n        r1cs_->GetNumInstanceVariables() - 1,\n        [this](size_t i) { return witness_loader_.Get(i + 1); });\n  }\n\n private:\n  zk::r1cs::LinearCombination<F> MakeLC(const LinearCombination<F>& lc) const {\n    return zk::r1cs::LinearCombination<F>::CreateDeduplicated(\n        base::Map(lc.terms, [this](const Term<F>& term) {\n          zk::r1cs::Variable variable;\n          if (term.wire_id < r1cs_->GetNumInstanceVariables()) {\n            variable = zk::r1cs::Variable::Instance(term.wire_id);\n          } else {\n            variable = zk::r1cs::Variable::Witness(\n                term.wire_id - r1cs_->GetNumInstanceVariables());\n          }\n          return zk::r1cs::Term<F>(term.coefficient, variable);\n        }));\n  }\n\n  R1CS<F>* const r1cs_;\n  WitnessLoader<F> witness_loader_;\n};\n\n}  // namespace tachyon::circom\n\n#endif  // VENDORS_CIRCOM_CIRCOMLIB_CIRCUIT_CIRCUIT_H_\n"
  },
  {
    "path": "vendors/circom/circomlib/circuit/circuit_test.h",
    "content": "#ifndef VENDORS_CIRCOM_CIRCOMLIB_CIRCUIT_CIRCUIT_TEST_H_\n#define VENDORS_CIRCOM_CIRCOMLIB_CIRCUIT_CIRCUIT_TEST_H_\n\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\n#include \"circomlib/circuit/circuit.h\"\n#include \"circomlib/r1cs/r1cs.h\"\n#include \"circomlib/zkey/zkey.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/bn254.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluation_domain_factory.h\"\n#include \"tachyon/zk/r1cs/groth16/prove.h\"\n#include \"tachyon/zk/r1cs/groth16/verify.h\"\n\nnamespace tachyon::circom {\n\nusing F = math::bn254::Fr;\nusing Curve = math::bn254::BN254Curve;\n\nclass CircuitTest : public testing::Test {\n public:\n  static void SetUpTestSuite() { Curve::Init(); }\n\n protected:\n  void SynthesizeTest() {\n    zk::r1cs::ConstraintSystem<F> constraint_system;\n    circuit_->Synthesize(constraint_system);\n    ASSERT_TRUE(constraint_system.IsSatisfied());\n  }\n\n  template <size_t MaxDegree, typename QAP>\n  void Groth16ProveAndVerifyTest() {\n    zk::r1cs::groth16::ToxicWaste<Curve> toxic_waste =\n        zk::r1cs::groth16::ToxicWaste<Curve>::RandomWithoutX();\n    zk::r1cs::groth16::ProvingKey<Curve> pk;\n    bool loaded = pk.Load<MaxDegree, QAP>(toxic_waste, *circuit_);\n    ASSERT_TRUE(loaded);\n    zk::r1cs::groth16::Proof<Curve> proof =\n        zk::r1cs::groth16::CreateProofWithReductionZK<MaxDegree, QAP>(*circuit_,\n                                                                      pk);\n    zk::r1cs::groth16::PreparedVerifyingKey<Curve> pvk =\n        std::move(pk).TakeVerifyingKey().ToPreparedVerifyingKey();\n    std::vector<F> public_inputs = circuit_->GetPublicInputs();\n    ASSERT_TRUE(VerifyProof(pvk, proof, public_inputs));\n  }\n\n  template <size_t MaxDegree, typename QAP>\n  void Groth16ProveAndVerifyUsingZKeyTest(\n      const ZKey<Curve>& zkey, absl::Span<const F> full_assignments) {\n    using Domain = math::UnivariateEvaluationDomain<F, MaxDegree>;\n\n    zk::r1cs::groth16::ProvingKey<Curve> pk =\n        zkey.GetProvingKey().ToNativeProvingKey();\n    absl::Span<const Coefficient<F>> coefficients = zkey.GetCoefficients();\n\n    std::unique_ptr<Domain> domain = Domain::Create(zkey.GetDomainSize());\n    std::vector<F> h_evals = QAP::WitnessMapFromMatrices(\n        domain.get(), coefficients, full_assignments);\n\n    size_t num_instance_variables = zkey.GetNumInstanceVariables();\n    zk::r1cs::groth16::Proof<Curve> proof =\n        zk::r1cs::groth16::CreateProofWithAssignmentZK(\n            pk, absl::MakeConstSpan(h_evals),\n            full_assignments.subspan(1, num_instance_variables - 1),\n            full_assignments.subspan(num_instance_variables),\n            full_assignments.subspan(1));\n    zk::r1cs::groth16::PreparedVerifyingKey<Curve> pvk =\n        std::move(pk).TakeVerifyingKey().ToPreparedVerifyingKey();\n    std::vector<F> public_inputs = circuit_->GetPublicInputs();\n    ASSERT_TRUE(VerifyProof(pvk, proof, public_inputs));\n  }\n\n  std::unique_ptr<R1CS<F>> r1cs_;\n  std::unique_ptr<Circuit<F>> circuit_;\n};\n\n}  // namespace tachyon::circom\n\n#endif  // VENDORS_CIRCOM_CIRCOMLIB_CIRCUIT_CIRCUIT_TEST_H_\n"
  },
  {
    "path": "vendors/circom/circomlib/circuit/multiplier_3_circuit_unittest.cc",
    "content": "#include \"circomlib/circuit/circuit_test.h\"\n#include \"circomlib/circuit/quadratic_arithmetic_program.h\"\n#include \"circomlib/r1cs/r1cs.h\"\n#include \"circomlib/zkey/zkey.h\"\n#include \"tachyon/zk/r1cs/constraint_system/quadratic_arithmetic_program.h\"\n\nnamespace tachyon::circom {\n\nclass Multiplier3CircuitTest : public CircuitTest {\n public:\n  void SetUp() override {\n    r1cs_ = ParseR1CS<F>(base::FilePath(\"examples/multiplier_3.r1cs\"));\n    ASSERT_TRUE(r1cs_);\n  }\n\n  void LoadRandomWitness() {\n    circuit_.reset(new Circuit<F>(\n        r1cs_.get(),\n        base::FilePath(\"examples/multiplier_3_cpp/multiplier_3.dat\")));\n    std::vector<F> values = base::CreateVector(3, []() { return F::Random(); });\n    circuit_->witness_loader().Set(\"in\", values);\n    circuit_->witness_loader().Load();\n    std::vector<F> public_inputs = circuit_->GetPublicInputs();\n    ASSERT_EQ(public_inputs.size(), 1);\n    ASSERT_EQ(public_inputs[0], values[0] * values[1] * values[2]);\n  }\n\n  void LoadWitnessFromJson() {\n    circuit_.reset(new Circuit<F>(\n        r1cs_.get(),\n        base::FilePath(\"examples/multiplier_3_cpp/multiplier_3.dat\")));\n    circuit_->witness_loader().Load(\n        base::FilePath(\"circomlib/circuit/multiplier_3_data.json\"));\n    std::vector<F> public_inputs = circuit_->GetPublicInputs();\n    ASSERT_EQ(public_inputs.size(), 1);\n    ASSERT_EQ(public_inputs[0], F(60));\n  }\n};\n\nTEST_F(Multiplier3CircuitTest, Synthesize) {\n  LoadRandomWitness();\n  this->SynthesizeTest();\n\n  LoadWitnessFromJson();\n  this->SynthesizeTest();\n}\n\nTEST_F(Multiplier3CircuitTest, Groth16ProveAndVerify) {\n  constexpr size_t kMaxDegree = 31;\n  LoadRandomWitness();\n  this->Groth16ProveAndVerifyTest<kMaxDegree,\n                                  zk::r1cs::QuadraticArithmeticProgram<F>>();\n}\n\nTEST_F(Multiplier3CircuitTest, Groth16ProveAndVerifyUsingZKey) {\n  constexpr size_t kMaxDegree = 3;\n\n  LoadRandomWitness();\n  std::unique_ptr<ZKey<Curve>> zkey =\n      ParseZKey<Curve>(base::FilePath(\"examples/multiplier_3.zkey\"));\n  ASSERT_TRUE(zkey);\n\n  std::vector<F> full_assignments = base::CreateVector(\n      r1cs_->GetNumVariables(),\n      [this](size_t i) { return circuit_->witness_loader().Get(i); });\n\n  this->Groth16ProveAndVerifyUsingZKeyTest<kMaxDegree,\n                                           QuadraticArithmeticProgram<F>>(\n      *zkey, full_assignments);\n}\n\n}  // namespace tachyon::circom\n"
  },
  {
    "path": "vendors/circom/circomlib/circuit/multiplier_3_data.json",
    "content": "{\n  \"in\": [\"3\", \"4\", \"5\"]\n}\n"
  },
  {
    "path": "vendors/circom/circomlib/circuit/quadratic_arithmetic_program.h",
    "content": "// Copyright 2022 arkworks contributors\n// Use of this source code is governed by a MIT/Apache-2.0 style license that\n// can be found in the LICENSE-MIT.arkworks and the LICENCE-APACHE.arkworks\n// file.\n\n#ifndef VENDORS_CIRCOM_CIRCOMLIB_CIRCUIT_QUADRATIC_ARITHMETIC_PROGRAM_H_\n#define VENDORS_CIRCOM_CIRCOMLIB_CIRCUIT_QUADRATIC_ARITHMETIC_PROGRAM_H_\n\n#include <utility>\n#include <vector>\n\n#include \"circomlib/zkey/coefficient.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/profiler.h\"\n#include \"tachyon/zk/r1cs/constraint_system/quadratic_arithmetic_program.h\"\n\nnamespace tachyon::circom {\n\ntemplate <typename F>\nclass QuadraticArithmeticProgram {\n public:\n  QuadraticArithmeticProgram() = delete;\n\n  template <typename Domain>\n  static std::vector<F> WitnessMapFromMatrices(\n      const Domain* domain, absl::Span<const Coefficient<F>> coefficients,\n      absl::Span<const F> full_assignments) {\n    TRACE_EVENT(\"ProofGeneration\", \"QAP::WitnessMapFromMatrices\");\n    using Evals = typename Domain::Evals;\n    using DensePoly = typename Domain::DensePoly;\n\n    TRACE_EVENT_BEGIN(\"Subtask\", \"ABC Initialization\");\n    std::vector<F> a(domain->size());\n    std::vector<F> b(domain->size());\n    // NOTE: |c| is initialized later with |c[i]| = |a[i]| * |b[i]|.\n    std::vector<F> c(domain->size());\n    OMP_PARALLEL_FOR(size_t i = 0; i < domain->size(); ++i) {\n      a[i] = F::Zero();\n      b[i] = F::Zero();\n    }\n    TRACE_EVENT_END(\"Subtask\");\n\n    TRACE_EVENT_BEGIN(\"Subtask\", \"AB Calculation\");\n    // See\n    // https://github.com/iden3/rapidsnark/blob/b17e6fe/src/groth16.cpp#L116-L156.\n#if defined(TACHYON_HAS_OPENMP)\n    constexpr size_t kNumLocks = 1024;\n    omp_lock_t locks[kNumLocks];\n    for (size_t i = 0; i < kNumLocks; i++) omp_init_lock(&locks[i]);\n#endif\n    OMP_PARALLEL_FOR(size_t i = 0; i < coefficients.size(); i++) {\n      const Coefficient<F>& c = coefficients[i];\n      std::vector<F>& ab = (c.matrix == 0) ? a : b;\n\n#if defined(TACHYON_HAS_OPENMP)\n      omp_set_lock(&locks[c.constraint % kNumLocks]);\n#endif\n      if (c.value.IsOne()) {\n        ab[c.constraint] += full_assignments[c.signal];\n      } else {\n        ab[c.constraint] += c.value * full_assignments[c.signal];\n      }\n#if defined(TACHYON_HAS_OPENMP)\n      omp_unset_lock(&locks[c.constraint % kNumLocks]);\n#endif\n    }\n#if defined(TACHYON_HAS_OPENMP)\n    for (size_t i = 0; i < kNumLocks; i++) omp_destroy_lock(&locks[i]);\n#endif\n    TRACE_EVENT_END(\"Subtask\");\n\n    {\n      TRACE_EVENT(\"Subtask\", \"C Calculation\");\n      OMP_PARALLEL_FOR(size_t i = 0; i < domain->size(); ++i) {\n        c[i] = a[i] * b[i];\n      }\n    }\n\n    Evals a_evals(std::move(a));\n    Evals b_evals(std::move(b));\n    Evals c_evals(std::move(c));\n\n    DensePoly a_poly, b_poly, c_poly;\n    {\n      TRACE_EVENT(\"Subtask\", \"ABC IFFT\");\n      a_poly = domain->IFFT(std::move(a_evals));\n      b_poly = domain->IFFT(std::move(b_evals));\n      c_poly = domain->IFFT(std::move(c_evals));\n    }\n\n    F root_of_unity;\n    CHECK(F::GetRootOfUnity(2 * domain->size(), &root_of_unity));\n\n    {\n      TRACE_EVENT(\"Subtask\", \"ABC UpdateCosets\");\n      Domain::DistributePowers(a_poly, root_of_unity);\n      Domain::DistributePowers(b_poly, root_of_unity);\n      Domain::DistributePowers(c_poly, root_of_unity);\n    }\n\n    {\n      TRACE_EVENT(\"Subtask\", \"ABC FFT\");\n      a_evals = domain->FFT(std::move(a_poly));\n      b_evals = domain->FFT(std::move(b_poly));\n      c_evals = domain->FFT(std::move(c_poly));\n    }\n\n    {\n      TRACE_EVENT(\"Subtask\", \"HEval Calculation\");\n      // |h_evals[i]| = |a[i]| * |b[i]| - |c[i]|\n      OMP_PARALLEL_FOR(size_t i = 0; i < domain->size(); ++i) {\n        F& h_evals_i = a_evals.at(i);\n        h_evals_i *= b_evals[i];\n        h_evals_i -= c_evals[i];\n      }\n    }\n    return std::move(a_evals).TakeEvaluations();\n  }\n};\n\n}  // namespace tachyon::circom\n\n#endif  // VENDORS_CIRCOM_CIRCOMLIB_CIRCUIT_QUADRATIC_ARITHMETIC_PROGRAM_H_\n"
  },
  {
    "path": "vendors/circom/circomlib/circuit/witness_loader.h",
    "content": "#ifndef VENDORS_CIRCOM_CIRCOMLIB_CIRCUIT_WITNESS_LOADER_H_\n#define VENDORS_CIRCOM_CIRCOMLIB_CIRCUIT_WITNESS_LOADER_H_\n\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"circomlib/base/fr_element_conversion.h\"\n#include \"circomlib/generated/common/calcwit.hpp\"\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/base/files/file_path.h\"\n\nnamespace tachyon::circom {\n\ntemplate <typename F>\nclass WitnessLoader {\n public:\n  explicit WitnessLoader(const base::FilePath& data)\n      : circuit_(loadCircuit(data.value())),\n        calc_wit_(new Circom_CalcWit(circuit_.get())) {}\n\n  void Set(std::string_view label, const F& value) {\n    witness_[label] = std::vector<FrElement>{ConvertToFrElement(value)};\n  }\n\n  void Set(std::string_view label, const std::vector<F>& values) {\n    witness_[label] = base::Map(\n        values, [](const F& value) { return ConvertToFrElement(value); });\n  }\n\n  void Load() { loadWitness(calc_wit_.get(), witness_); }\n\n  void Load(const base::FilePath& json) {\n    loadJson(calc_wit_.get(), json.value());\n  }\n\n  void Save(const base::FilePath& wtns) {\n    writeBinWitness(calc_wit_.get(), wtns.value());\n  }\n\n  F Get(uint32_t i) const {\n    FrElement v;\n    calc_wit_->getWitness(i, &v);\n    return ConvertFromFrElement<F>(v);\n  }\n\n private:\n  std::unique_ptr<Circom_Circuit> circuit_;\n  std::unique_ptr<Circom_CalcWit> calc_wit_;\n  absl::flat_hash_map<std::string, std::vector<FrElement>> witness_;\n};\n\n}  // namespace tachyon::circom\n\n#endif  // VENDORS_CIRCOM_CIRCOMLIB_CIRCUIT_WITNESS_LOADER_H_\n"
  },
  {
    "path": "vendors/circom/circomlib/generated/.clang-format",
    "content": "DisableFormat: true\n"
  },
  {
    "path": "vendors/circom/circomlib/generated/BUILD.bazel",
    "content": ""
  },
  {
    "path": "vendors/circom/circomlib/generated/README.md",
    "content": "# Generated\n\nThis is taken and modified from [iden3/circom/code_producers/src/c_elements](https://github.com/iden3/circom/tree/v2.1.8/code_producers/src/c_elements).\n\n## Modifications\n\n- Modified to remove errors from compilation.\n- `loadCircuit` and `writeBinWitness` are moved to [common/calcwit.hpp](/vendors/circom//circomlib/generated/common/calcwit.hpp) and [common/calcwit.hpp](/vendors/circom//circomlib/generated/common/calcwit.cpp).\n- `loadWitness` is created, which loads the witness from the `absl::flat_hash_map<>`.\n\nSee the following files for more details on the modifications.\n\n- [calcwit.cpp.diff](/vendors/circom/circomlib/generated/common/calcwit.cpp.diff)\n- [calcwit.hpp.diff](/vendors/circom/circomlib/generated/common/calcwit.hpp.diff)\n- [circom.hpp.diff](/vendors/circom/circomlib/generated/common/circom.hpp.diff)\n"
  },
  {
    "path": "vendors/circom/circomlib/generated/bls12381/BUILD.bazel",
    "content": "load(\"@iden3_ffiasm//c:build_defs.bzl\", \"generate_prime_field\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ngenerate_prime_field(\n    name = \"fr\",\n    modulus = \"52435875175126190479447740508185965837690552500527637822603658699938581184513\",\n)\n"
  },
  {
    "path": "vendors/circom/circomlib/generated/bn128/BUILD.bazel",
    "content": "load(\"@iden3_ffiasm//c:build_defs.bzl\", \"generate_prime_field\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ngenerate_prime_field(\n    name = \"fr\",\n    modulus = \"21888242871839275222246405745257275088548364400416034343698204186575808495617\",\n)\n"
  },
  {
    "path": "vendors/circom/circomlib/generated/common/BUILD.bazel",
    "content": "package(default_visibility = [\"//visibility:public\"])\n\ncc_library(\n    name = \"common_hdrs\",\n    hdrs = [\n        \"calcwit.hpp\",\n        \"circom.hpp\",\n    ],\n    includes = [\".\"],\n)\n\nfilegroup(\n    name = \"common_srcs\",\n    srcs = [\"calcwit.cpp\"],\n)\n"
  },
  {
    "path": "vendors/circom/circomlib/generated/common/calcwit.cpp",
    "content": "#include <iomanip>\n#include <sstream>\n#include <assert.h>\n#include <iostream>\n#include <fstream>\n#include <sstream>\n#include <iomanip>\n#include <sys/stat.h>\n#include <sys/mman.h>\n#include <fcntl.h>\n#include <unistd.h>\n#include <vector>\n#include <chrono>\n#include \"calcwit.hpp\"\n#include \"nlohmann/json.hpp\"\n\nusing json = nlohmann::json;\n\nextern void run(Circom_CalcWit* ctx);\n\nstd::string int_to_hex( u64 i )\n{\n  std::stringstream stream;\n  stream << \"0x\"\n         << std::setfill ('0') << std::setw(16)\n         << std::hex << i;\n  return stream.str();\n}\n\nu64 fnv1a(std::string s) {\n  u64 hash = 0xCBF29CE484222325LL;\n  for(char& c : s) {\n    hash ^= u64(c);\n    hash *= 0x100000001B3LL;\n  }\n  return hash;\n}\n\nCircom_CalcWit::Circom_CalcWit (Circom_Circuit *aCircuit, uint maxTh) {\n  circuit = aCircuit;\n  inputSignalAssignedCounter = get_main_input_signal_no();\n  inputSignalAssigned = new bool[inputSignalAssignedCounter];\n  for (uint i = 0; i< inputSignalAssignedCounter; i++) {\n    inputSignalAssigned[i] = false;\n  }\n  signalValues = new FrElement[get_total_signal_no()];\n  Fr_str2element(&signalValues[0], \"1\", 10);\n  componentMemory = new Circom_Component[get_number_of_components()];\n  circuitConstants = circuit ->circuitConstants;\n  templateInsId2IOSignalInfo = circuit -> templateInsId2IOSignalInfo;\n\n  maxThread = maxTh;\n\n  // parallelism\n  numThread = 0;\n\n}\n\nCircom_CalcWit::~Circom_CalcWit() {\n  // ...\n}\n\nuint Circom_CalcWit::getInputSignalHashPosition(u64 h) {\n  uint n = get_size_of_input_hashmap();\n  uint pos = (uint)(h % (u64)n);\n  if (circuit->InputHashMap[pos].hash!=h){\n    uint inipos = pos;\n    pos++;\n    while (pos != inipos) {\n      if (circuit->InputHashMap[pos].hash==h) return pos;\n      if (circuit->InputHashMap[pos].hash==0) {\n\tfprintf(stderr, \"Signal not found\\n\");\n\tassert(false);\n      }\n      pos = (pos+1)%n;\n    }\n    fprintf(stderr, \"Signals not found\\n\");\n    assert(false);\n  }\n  return pos;\n}\n\nvoid Circom_CalcWit::tryRunCircuit(){\n  if (inputSignalAssignedCounter == 0) {\n    run(this);\n  }\n}\n\nvoid Circom_CalcWit::setInputSignal(u64 h, uint i,  FrElement const & val){\n  if (inputSignalAssignedCounter == 0) {\n    fprintf(stderr, \"No more signals to be assigned\\n\");\n    assert(false);\n  }\n  uint pos = getInputSignalHashPosition(h);\n  if (i >= circuit->InputHashMap[pos].signalsize) {\n    fprintf(stderr, \"Input signal array access exceeds the size\\n\");\n    assert(false);\n  }\n\n  uint si = circuit->InputHashMap[pos].signalid+i;\n  if (inputSignalAssigned[si-get_main_input_signal_start()]) {\n    fprintf(stderr, \"Signal assigned twice: %d\\n\", si);\n    assert(false);\n  }\n  signalValues[si] = val;\n  inputSignalAssigned[si-get_main_input_signal_start()] = true;\n  inputSignalAssignedCounter--;\n  tryRunCircuit();\n}\n\nu64 Circom_CalcWit::getInputSignalSize(u64 h) {\n  uint pos = getInputSignalHashPosition(h);\n  return circuit->InputHashMap[pos].signalsize;\n}\n\nstd::string Circom_CalcWit::getTrace(u64 id_cmp){\n  if (id_cmp == 0) return componentMemory[id_cmp].componentName;\n  else{\n    u64 id_father = componentMemory[id_cmp].idFather;\n    std::string my_name = componentMemory[id_cmp].componentName;\n\n    return Circom_CalcWit::getTrace(id_father) + \".\" + my_name;\n  }\n\n\n}\n\nstd::string Circom_CalcWit::generate_position_array(uint* dimensions, uint size_dimensions, uint index){\n  std::string positions = \"\";\n\n  for (uint i = 0 ; i < size_dimensions; i++){\n    uint last_pos = index % dimensions[size_dimensions -1 - i];\n    index = index / dimensions[size_dimensions -1 - i];\n    std::string new_pos = \"[\" + std::to_string(last_pos) + \"]\";\n    positions =  new_pos + positions;\n  }\n  return positions;\n}\n\n#define handle_error(msg) \\\n           do { perror(msg); exit(EXIT_FAILURE); } while (0)\n\nCircom_Circuit* loadCircuit(std::string const &datFileName) {\n  Circom_Circuit *circuit = new Circom_Circuit;\n\n  int fd;\n  struct stat sb;\n\n  fd = open(datFileName.c_str(), O_RDONLY);\n  if (fd == -1) {\n    std::cout << \".dat file not found: \" << datFileName << \"\\n\";\n    throw std::system_error(errno, std::generic_category(), \"open\");\n  }\n\n  if (fstat(fd, &sb) == -1) {          /* To obtain file size */\n    throw std::system_error(errno, std::generic_category(), \"fstat\");\n  }\n\n  u8* bdata = (u8*)mmap(NULL, sb.st_size, PROT_READ , MAP_PRIVATE, fd, 0);\n  close(fd);\n\n  circuit->InputHashMap = new HashSignalInfo[get_size_of_input_hashmap()];\n  uint dsize = get_size_of_input_hashmap()*sizeof(HashSignalInfo);\n  memcpy((void *)(circuit->InputHashMap), (void *)bdata, dsize);\n\n  circuit->witness2SignalList = new u64[get_size_of_witness()];\n  uint inisize = dsize;\n  dsize = get_size_of_witness()*sizeof(u64);\n  memcpy((void *)(circuit->witness2SignalList), (void *)(bdata+inisize), dsize);\n\n  circuit->circuitConstants = new FrElement[get_size_of_constants()];\n  if (get_size_of_constants()>0) {\n    inisize += dsize;\n    dsize = get_size_of_constants()*sizeof(FrElement);\n    memcpy((void *)(circuit->circuitConstants), (void *)(bdata+inisize), dsize);\n  }\n\n  std::map<u32,IODefPair> templateInsId2IOSignalInfo1;\n  if (get_size_of_io_map()>0) {\n    u32 index[get_size_of_io_map()];\n    inisize += dsize;\n    dsize = get_size_of_io_map()*sizeof(u32);\n    memcpy((void *)index, (void *)(bdata+inisize), dsize);\n    inisize += dsize;\n    assert(inisize % sizeof(u32) == 0);\n    assert(sb.st_size % sizeof(u32) == 0);\n    u32 dataiomap[(sb.st_size-inisize)/sizeof(u32)];\n    memcpy((void *)dataiomap, (void *)(bdata+inisize), sb.st_size-inisize);\n    u32* pu32 = dataiomap;\n\n    for (uint i = 0; i < get_size_of_io_map(); i++) {\n      u32 n = *pu32;\n      IODefPair p;\n      p.len = n;\n      IODef defs[n];\n      pu32 += 1;\n      for (u32 j = 0; j <n; j++){\n        defs[j].offset=*pu32;\n        u32 len = *(pu32+1);\n        defs[j].len = len;\n        defs[j].lengths = new u32[len];\n        memcpy((void *)defs[j].lengths,(void *)(pu32+2),len*sizeof(u32));\n        pu32 += len + 2;\n      }\n      p.defs = (IODef*)calloc(10, sizeof(IODef));\n      for (u32 j = 0; j < p.len; j++){\n        p.defs[j] = defs[j];\n      }\n\t    templateInsId2IOSignalInfo1[index[i]] = p;\n    }\n  }\n  circuit->templateInsId2IOSignalInfo = std::move(templateInsId2IOSignalInfo1);\n\n  munmap(bdata, sb.st_size);\n\n  return circuit;\n}\n\nbool check_valid_number(std::string & s, uint base){\n  bool is_valid = true;\n  if (base == 16){\n    for (uint i = 0; i < s.size(); i++){\n      is_valid &= (\n        ('0' <= s[i] && s[i] <= '9') ||\n        ('a' <= s[i] && s[i] <= 'f') ||\n        ('A' <= s[i] && s[i] <= 'F')\n      );\n    }\n  } else{\n    for (uint i = 0; i < s.size(); i++){\n      is_valid &= ('0' <= s[i] && s[i] < char(int('0') + base));\n    }\n  }\n  return is_valid;\n}\n\nvoid json2FrElements (json val, std::vector<FrElement> & vval){\n  if (!val.is_array()) {\n    FrElement v;\n    std::string s_aux, s;\n    uint base;\n    if (val.is_string()) {\n      s_aux = val.get<std::string>();\n      std::string possible_prefix = s_aux.substr(0, 2);\n      if (possible_prefix == \"0b\" || possible_prefix == \"0B\"){\n        s = s_aux.substr(2, s_aux.size() - 2);\n        base = 2;\n      } else if (possible_prefix == \"0o\" || possible_prefix == \"0O\"){\n        s = s_aux.substr(2, s_aux.size() - 2);\n        base = 8;\n      } else if (possible_prefix == \"0x\" || possible_prefix == \"0X\"){\n        s = s_aux.substr(2, s_aux.size() - 2);\n        base = 16;\n      } else{\n        s = s_aux;\n        base = 10;\n      }\n      if (!check_valid_number(s, base)){\n        std::ostringstream errStrStream;\n        errStrStream << \"Invalid number in JSON input: \" << s_aux << \"\\n\";\n\t      throw std::runtime_error(errStrStream.str() );\n      }\n    } else if (val.is_number()) {\n        double vd = val.get<double>();\n        std::stringstream stream;\n        stream << std::fixed << std::setprecision(0) << vd;\n        s = stream.str();\n        base = 10;\n    } else {\n        std::ostringstream errStrStream;\n        errStrStream << \"Invalid JSON type\\n\";\n\t      throw std::runtime_error(errStrStream.str() );\n    }\n    Fr_str2element (&v, s.c_str(), base);\n    vval.push_back(v);\n  } else {\n    for (uint i = 0; i < val.size(); i++) {\n      json2FrElements (val[i], vval);\n    }\n  }\n}\n\nvoid loadJson(Circom_CalcWit *ctx, std::string filename) {\n  std::ifstream inStream(filename);\n  json j;\n  inStream >> j;\n\n  u64 nItems = j.size();\n  // printf(\"Items : %llu\\n\",nItems);\n  if (nItems == 0){\n    ctx->tryRunCircuit();\n  }\n  for (json::iterator it = j.begin(); it != j.end(); ++it) {\n    // std::cout << it.key() << \" => \" << it.value() << '\\n';\n    u64 h = fnv1a(it.key());\n    std::vector<FrElement> v;\n    json2FrElements(it.value(),v);\n    uint signalSize = ctx->getInputSignalSize(h);\n    if (v.size() < signalSize) {\n\tstd::ostringstream errStrStream;\n\terrStrStream << \"Error loading signal \" << it.key() << \": Not enough values\\n\";\n\tthrow std::runtime_error(errStrStream.str() );\n    }\n    if (v.size() > signalSize) {\n\tstd::ostringstream errStrStream;\n\terrStrStream << \"Error loading signal \" << it.key() << \": Too many values\\n\";\n\tthrow std::runtime_error(errStrStream.str() );\n    }\n    for (uint i = 0; i<v.size(); i++){\n      try {\n\t// std::cout << it.key() << \",\" << i << \" => \" << Fr_element2str(&(v[i])) << '\\n';\n\tctx->setInputSignal(h,i,v[i]);\n      } catch (std::runtime_error e) {\n\tstd::ostringstream errStrStream;\n\terrStrStream << \"Error setting signal: \" << it.key() << \"\\n\" << e.what();\n\tthrow std::runtime_error(errStrStream.str() );\n      }\n    }\n  }\n}\n\nvoid loadWitness(Circom_CalcWit *ctx, const absl::flat_hash_map<std::string, std::vector<FrElement>>& witness) {\n  size_t nItems = witness.size();\n  // printf(\"Items : %llu\\n\",nItems);\n  if (nItems == 0){\n    ctx->tryRunCircuit();\n  }\n  for (const auto& [key, value] : witness) {\n    u64 h = fnv1a(key);\n    uint signalSize = ctx->getInputSignalSize(h);\n    if (value.size() < signalSize) {\n\t    std::ostringstream errStrStream;\n\t    errStrStream << \"Error loading signal \" << key << \": Not enough values\\n\";\n\t    throw std::runtime_error(errStrStream.str() );\n    }\n    if (value.size() > signalSize) {\n\t    std::ostringstream errStrStream;\n\t    errStrStream << \"Error loading signal \" << key << \": Too many values\\n\";\n\t    throw std::runtime_error(errStrStream.str() );\n    }\n    for (uint i = 0; i<value.size(); i++){\n      try {\n\t      // std::cout << key << \",\" << i << \" => \" << Fr_element2str(&(value[i])) << '\\n';\n\t      ctx->setInputSignal(h,i,value[i]);\n      } catch (std::runtime_error e) {\n\t      std::ostringstream errStrStream;\n\t      errStrStream << \"Error setting signal: \" << key << \"\\n\" << e.what();\n\t      throw std::runtime_error(errStrStream.str() );\n      }\n    }\n  }\n  if (ctx->getRemaingInputsToBeSet()!=0) {\n    std::cerr << \"Not all inputs have been set. Only \" << get_main_input_signal_no()-ctx->getRemaingInputsToBeSet() << \" out of \" << get_main_input_signal_no() << std::endl;\n    assert(false);\n  }\n}\n\nvoid writeBinWitness(Circom_CalcWit *ctx, std::string const &wtnsFileName) {\n  FILE *write_ptr;\n\n  write_ptr = fopen(wtnsFileName.c_str(),\"wb\");\n\n  fwrite(\"wtns\", 4, 1, write_ptr);\n\n  u32 version = 2;\n  fwrite(&version, 4, 1, write_ptr);\n\n  u32 nSections = 2;\n  fwrite(&nSections, 4, 1, write_ptr);\n\n  // Header\n  u32 idSection1 = 1;\n  fwrite(&idSection1, 4, 1, write_ptr);\n\n  u32 n8 = Fr_N64*8;\n\n  u64 idSection1length = 8 + n8;\n  fwrite(&idSection1length, 8, 1, write_ptr);\n\n  fwrite(&n8, 4, 1, write_ptr);\n\n  fwrite(Fr_q.longVal, Fr_N64*8, 1, write_ptr);\n\n  uint Nwtns = get_size_of_witness();\n\n  u32 nVars = (u32)Nwtns;\n  fwrite(&nVars, 4, 1, write_ptr);\n\n  // Data\n  u32 idSection2 = 2;\n  fwrite(&idSection2, 4, 1, write_ptr);\n\n  u64 idSection2length = (u64)n8*(u64)Nwtns;\n  fwrite(&idSection2length, 8, 1, write_ptr);\n\n  FrElement v;\n\n  for (int i=0;i<Nwtns;i++) {\n    ctx->getWitness(i, &v);\n    Fr_toLongNormal(&v, &v);\n    fwrite(v.longVal, Fr_N64*8, 1, write_ptr);\n  }\n  fclose(write_ptr);\n}\n"
  },
  {
    "path": "vendors/circom/circomlib/generated/common/calcwit.cpp.diff",
    "content": "4,13d3\n< #include <iostream>\n< #include <fstream>\n< #include <sstream>\n< #include <iomanip>\n< #include <sys/stat.h>\n< #include <sys/mman.h>\n< #include <fcntl.h>\n< #include <unistd.h>\n< #include <vector>\n< #include <chrono>\n15,17d4\n< #include \"nlohmann/json.hpp\"\n< \n< using json = nlohmann::json;\n43c30\n<   for (uint i = 0; i< inputSignalAssignedCounter; i++) {\n---\n>   for (int i = 0; i< inputSignalAssignedCounter; i++) {\n68c55\n<     pos++;\n---\n>     pos = (pos+1)%n; \n70,71c57,58\n<       if (circuit->InputHashMap[pos].hash==h) return pos;\n<       if (circuit->InputHashMap[pos].hash==0) {\n---\n>       if (circuit->InputHashMap[pos].hash == h) return pos;\n>       if (circuit->InputHashMap[pos].signalid == 0) {\n75c62\n<       pos = (pos+1)%n;\n---\n>       pos = (pos+1)%n; \n83c70\n< void Circom_CalcWit::tryRunCircuit(){\n---\n> void Circom_CalcWit::tryRunCircuit(){ \n89c76\n< void Circom_CalcWit::setInputSignal(u64 h, uint i,  FrElement const & val){\n---\n> void Circom_CalcWit::setInputSignal(u64 h, uint i,  FrElement & val){\n99c86\n< \n---\n>   \n140,404d126\n< #define handle_error(msg) \\\n<            do { perror(msg); exit(EXIT_FAILURE); } while (0)\n< \n< Circom_Circuit* loadCircuit(std::string const &datFileName) {\n<   Circom_Circuit *circuit = new Circom_Circuit;\n< \n<   int fd;\n<   struct stat sb;\n< \n<   fd = open(datFileName.c_str(), O_RDONLY);\n<   if (fd == -1) {\n<     std::cout << \".dat file not found: \" << datFileName << \"\\n\";\n<     throw std::system_error(errno, std::generic_category(), \"open\");\n<   }\n< \n<   if (fstat(fd, &sb) == -1) {          /* To obtain file size */\n<     throw std::system_error(errno, std::generic_category(), \"fstat\");\n<   }\n< \n<   u8* bdata = (u8*)mmap(NULL, sb.st_size, PROT_READ , MAP_PRIVATE, fd, 0);\n<   close(fd);\n< \n<   circuit->InputHashMap = new HashSignalInfo[get_size_of_input_hashmap()];\n<   uint dsize = get_size_of_input_hashmap()*sizeof(HashSignalInfo);\n<   memcpy((void *)(circuit->InputHashMap), (void *)bdata, dsize);\n< \n<   circuit->witness2SignalList = new u64[get_size_of_witness()];\n<   uint inisize = dsize;\n<   dsize = get_size_of_witness()*sizeof(u64);\n<   memcpy((void *)(circuit->witness2SignalList), (void *)(bdata+inisize), dsize);\n< \n<   circuit->circuitConstants = new FrElement[get_size_of_constants()];\n<   if (get_size_of_constants()>0) {\n<     inisize += dsize;\n<     dsize = get_size_of_constants()*sizeof(FrElement);\n<     memcpy((void *)(circuit->circuitConstants), (void *)(bdata+inisize), dsize);\n<   }\n< \n<   std::map<u32,IODefPair> templateInsId2IOSignalInfo1;\n<   if (get_size_of_io_map()>0) {\n<     u32 index[get_size_of_io_map()];\n<     inisize += dsize;\n<     dsize = get_size_of_io_map()*sizeof(u32);\n<     memcpy((void *)index, (void *)(bdata+inisize), dsize);\n<     inisize += dsize;\n<     assert(inisize % sizeof(u32) == 0);\n<     assert(sb.st_size % sizeof(u32) == 0);\n<     u32 dataiomap[(sb.st_size-inisize)/sizeof(u32)];\n<     memcpy((void *)dataiomap, (void *)(bdata+inisize), sb.st_size-inisize);\n<     u32* pu32 = dataiomap;\n< \n<     for (uint i = 0; i < get_size_of_io_map(); i++) {\n<       u32 n = *pu32;\n<       IODefPair p;\n<       p.len = n;\n<       IODef defs[n];\n<       pu32 += 1;\n<       for (u32 j = 0; j <n; j++){\n<         defs[j].offset=*pu32;\n<         u32 len = *(pu32+1);\n<         defs[j].len = len;\n<         defs[j].lengths = new u32[len];\n<         memcpy((void *)defs[j].lengths,(void *)(pu32+2),len*sizeof(u32));\n<         pu32 += len + 2;\n<       }\n<       p.defs = (IODef*)calloc(10, sizeof(IODef));\n<       for (u32 j = 0; j < p.len; j++){\n<         p.defs[j] = defs[j];\n<       }\n< \t    templateInsId2IOSignalInfo1[index[i]] = p;\n<     }\n<   }\n<   circuit->templateInsId2IOSignalInfo = std::move(templateInsId2IOSignalInfo1);\n< \n<   munmap(bdata, sb.st_size);\n< \n<   return circuit;\n< }\n< \n< bool check_valid_number(std::string & s, uint base){\n<   bool is_valid = true;\n<   if (base == 16){\n<     for (uint i = 0; i < s.size(); i++){\n<       is_valid &= (\n<         ('0' <= s[i] && s[i] <= '9') ||\n<         ('a' <= s[i] && s[i] <= 'f') ||\n<         ('A' <= s[i] && s[i] <= 'F')\n<       );\n<     }\n<   } else{\n<     for (uint i = 0; i < s.size(); i++){\n<       is_valid &= ('0' <= s[i] && s[i] < char(int('0') + base));\n<     }\n<   }\n<   return is_valid;\n< }\n< \n< void json2FrElements (json val, std::vector<FrElement> & vval){\n<   if (!val.is_array()) {\n<     FrElement v;\n<     std::string s_aux, s;\n<     uint base;\n<     if (val.is_string()) {\n<       s_aux = val.get<std::string>();\n<       std::string possible_prefix = s_aux.substr(0, 2);\n<       if (possible_prefix == \"0b\" || possible_prefix == \"0B\"){\n<         s = s_aux.substr(2, s_aux.size() - 2);\n<         base = 2;\n<       } else if (possible_prefix == \"0o\" || possible_prefix == \"0O\"){\n<         s = s_aux.substr(2, s_aux.size() - 2);\n<         base = 8;\n<       } else if (possible_prefix == \"0x\" || possible_prefix == \"0X\"){\n<         s = s_aux.substr(2, s_aux.size() - 2);\n<         base = 16;\n<       } else{\n<         s = s_aux;\n<         base = 10;\n<       }\n<       if (!check_valid_number(s, base)){\n<         std::ostringstream errStrStream;\n<         errStrStream << \"Invalid number in JSON input: \" << s_aux << \"\\n\";\n< \t      throw std::runtime_error(errStrStream.str() );\n<       }\n<     } else if (val.is_number()) {\n<         double vd = val.get<double>();\n<         std::stringstream stream;\n<         stream << std::fixed << std::setprecision(0) << vd;\n<         s = stream.str();\n<         base = 10;\n<     } else {\n<         std::ostringstream errStrStream;\n<         errStrStream << \"Invalid JSON type\\n\";\n< \t      throw std::runtime_error(errStrStream.str() );\n<     }\n<     Fr_str2element (&v, s.c_str(), base);\n<     vval.push_back(v);\n<   } else {\n<     for (uint i = 0; i < val.size(); i++) {\n<       json2FrElements (val[i], vval);\n<     }\n<   }\n< }\n< \n< void loadJson(Circom_CalcWit *ctx, std::string filename) {\n<   std::ifstream inStream(filename);\n<   json j;\n<   inStream >> j;\n< \n<   u64 nItems = j.size();\n<   // printf(\"Items : %llu\\n\",nItems);\n<   if (nItems == 0){\n<     ctx->tryRunCircuit();\n<   }\n<   for (json::iterator it = j.begin(); it != j.end(); ++it) {\n<     // std::cout << it.key() << \" => \" << it.value() << '\\n';\n<     u64 h = fnv1a(it.key());\n<     std::vector<FrElement> v;\n<     json2FrElements(it.value(),v);\n<     uint signalSize = ctx->getInputSignalSize(h);\n<     if (v.size() < signalSize) {\n< \tstd::ostringstream errStrStream;\n< \terrStrStream << \"Error loading signal \" << it.key() << \": Not enough values\\n\";\n< \tthrow std::runtime_error(errStrStream.str() );\n<     }\n<     if (v.size() > signalSize) {\n< \tstd::ostringstream errStrStream;\n< \terrStrStream << \"Error loading signal \" << it.key() << \": Too many values\\n\";\n< \tthrow std::runtime_error(errStrStream.str() );\n<     }\n<     for (uint i = 0; i<v.size(); i++){\n<       try {\n< \t// std::cout << it.key() << \",\" << i << \" => \" << Fr_element2str(&(v[i])) << '\\n';\n< \tctx->setInputSignal(h,i,v[i]);\n<       } catch (std::runtime_error e) {\n< \tstd::ostringstream errStrStream;\n< \terrStrStream << \"Error setting signal: \" << it.key() << \"\\n\" << e.what();\n< \tthrow std::runtime_error(errStrStream.str() );\n<       }\n<     }\n<   }\n< }\n< \n< void loadWitness(Circom_CalcWit *ctx, const absl::flat_hash_map<std::string, std::vector<FrElement>>& witness) {\n<   size_t nItems = witness.size();\n<   // printf(\"Items : %llu\\n\",nItems);\n<   if (nItems == 0){\n<     ctx->tryRunCircuit();\n<   }\n<   for (const auto& [key, value] : witness) {\n<     u64 h = fnv1a(key);\n<     uint signalSize = ctx->getInputSignalSize(h);\n<     if (value.size() < signalSize) {\n< \t    std::ostringstream errStrStream;\n< \t    errStrStream << \"Error loading signal \" << key << \": Not enough values\\n\";\n< \t    throw std::runtime_error(errStrStream.str() );\n<     }\n<     if (value.size() > signalSize) {\n< \t    std::ostringstream errStrStream;\n< \t    errStrStream << \"Error loading signal \" << key << \": Too many values\\n\";\n< \t    throw std::runtime_error(errStrStream.str() );\n<     }\n<     for (uint i = 0; i<value.size(); i++){\n<       try {\n< \t      // std::cout << key << \",\" << i << \" => \" << Fr_element2str(&(value[i])) << '\\n';\n< \t      ctx->setInputSignal(h,i,value[i]);\n<       } catch (std::runtime_error e) {\n< \t      std::ostringstream errStrStream;\n< \t      errStrStream << \"Error setting signal: \" << key << \"\\n\" << e.what();\n< \t      throw std::runtime_error(errStrStream.str() );\n<       }\n<     }\n<   }\n<   if (ctx->getRemaingInputsToBeSet()!=0) {\n<     std::cerr << \"Not all inputs have been set. Only \" << get_main_input_signal_no()-ctx->getRemaingInputsToBeSet() << \" out of \" << get_main_input_signal_no() << std::endl;\n<     assert(false);\n<   }\n< }\n< \n< void writeBinWitness(Circom_CalcWit *ctx, std::string const &wtnsFileName) {\n<   FILE *write_ptr;\n< \n<   write_ptr = fopen(wtnsFileName.c_str(),\"wb\");\n< \n<   fwrite(\"wtns\", 4, 1, write_ptr);\n< \n<   u32 version = 2;\n<   fwrite(&version, 4, 1, write_ptr);\n< \n<   u32 nSections = 2;\n<   fwrite(&nSections, 4, 1, write_ptr);\n< \n<   // Header\n<   u32 idSection1 = 1;\n<   fwrite(&idSection1, 4, 1, write_ptr);\n< \n<   u32 n8 = Fr_N64*8;\n< \n<   u64 idSection1length = 8 + n8;\n<   fwrite(&idSection1length, 8, 1, write_ptr);\n< \n<   fwrite(&n8, 4, 1, write_ptr);\n< \n<   fwrite(Fr_q.longVal, Fr_N64*8, 1, write_ptr);\n< \n<   uint Nwtns = get_size_of_witness();\n< \n<   u32 nVars = (u32)Nwtns;\n<   fwrite(&nVars, 4, 1, write_ptr);\n< \n<   // Data\n<   u32 idSection2 = 2;\n<   fwrite(&idSection2, 4, 1, write_ptr);\n< \n<   u64 idSection2length = (u64)n8*(u64)Nwtns;\n<   fwrite(&idSection2length, 8, 1, write_ptr);\n< \n<   FrElement v;\n< \n<   for (int i=0;i<Nwtns;i++) {\n<     ctx->getWitness(i, &v);\n<     Fr_toLongNormal(&v, &v);\n<     fwrite(v.longVal, Fr_N64*8, 1, write_ptr);\n<   }\n<   fclose(write_ptr);\n< }\n"
  },
  {
    "path": "vendors/circom/circomlib/generated/common/calcwit.hpp",
    "content": "#ifndef CIRCOM_CALCWIT_H\n#define CIRCOM_CALCWIT_H\n\n#include <mutex>\n#include <condition_variable>\n#include <functional>\n#include <atomic>\n#include <memory>\n#include <vector>\n\n#include \"absl/container/flat_hash_map.h\"\n\n#include \"circom.hpp\"\n#include \"fr.hpp\"\n\n#define NMUTEXES 32 //512\n\nu64 fnv1a(std::string s);\n\nclass Circom_CalcWit {\n\n  bool *inputSignalAssigned;\n  uint inputSignalAssignedCounter;\n\n  Circom_Circuit *circuit;\n\npublic:\n\n  FrElement *signalValues;\n  Circom_Component* componentMemory;\n  FrElement* circuitConstants;\n  std::map<u32,IODefPair> templateInsId2IOSignalInfo;\n  std::string* listOfTemplateMessages;\n\n  // parallelism\n  std::mutex numThreadMutex;\n  std::condition_variable ntcvs;\n  int numThread;\n\n  int maxThread;\n\n  // Functions called by the circuit\n  Circom_CalcWit(Circom_Circuit *aCircuit, uint numTh = NMUTEXES);\n  ~Circom_CalcWit();\n\n  // Public functions\n  void setInputSignal(u64 h, uint i, FrElement const &val);\n  void tryRunCircuit();\n\n  u64 getInputSignalSize(u64 h);\n\n  inline uint getRemaingInputsToBeSet() {\n    return inputSignalAssignedCounter;\n  }\n\n  inline void getWitness(uint idx, PFrElement val) {\n    Fr_copy(val, &signalValues[circuit->witness2SignalList[idx]]);\n  }\n\n  std::string getTrace(u64 id_cmp);\n\n  std::string generate_position_array(uint* dimensions, uint size_dimensions, uint index);\n\nprivate:\n\n  uint getInputSignalHashPosition(u64 h);\n\n};\n\ntypedef void (*Circom_TemplateFunction)(uint __cIdx, Circom_CalcWit* __ctx);\n\nCircom_Circuit* loadCircuit(std::string const &datFileName);\n\nvoid loadJson(Circom_CalcWit *ctx, std::string filename);\n\nvoid loadWitness(Circom_CalcWit *ctx, absl::flat_hash_map<std::string, std::vector<FrElement>> const &witness);\n\nvoid writeBinWitness(Circom_CalcWit *ctx, std::string const &wtnsFileName);\n\n#endif // CIRCOM_CALCWIT_H\n"
  },
  {
    "path": "vendors/circom/circomlib/generated/common/calcwit.hpp.diff",
    "content": "9,11d8\n< #include <vector>\n< \n< #include \"absl/container/flat_hash_map.h\"\n31,33c28,30\n<   FrElement* circuitConstants;\n<   std::map<u32,IODefPair> templateInsId2IOSignalInfo;\n<   std::string* listOfTemplateMessages;\n---\n>   FrElement* circuitConstants; \n>   std::map<u32,IODefPair> templateInsId2IOSignalInfo; \n>   std::string* listOfTemplateMessages; \n47c44\n<   void setInputSignal(u64 h, uint i, FrElement const &val);\n---\n>   void setInputSignal(u64 h, uint i, FrElement &val);\n49c46\n< \n---\n>   \n55c52\n< \n---\n>   \n65c62\n< \n---\n>   \n70,78c67\n< typedef void (*Circom_TemplateFunction)(uint __cIdx, Circom_CalcWit* __ctx);\n< \n< Circom_Circuit* loadCircuit(std::string const &datFileName);\n< \n< void loadJson(Circom_CalcWit *ctx, std::string filename);\n< \n< void loadWitness(Circom_CalcWit *ctx, absl::flat_hash_map<std::string, std::vector<FrElement>> const &witness);\n< \n< void writeBinWitness(Circom_CalcWit *ctx, std::string const &wtnsFileName);\n---\n> typedef void (*Circom_TemplateFunction)(uint __cIdx, Circom_CalcWit* __ctx); \n"
  },
  {
    "path": "vendors/circom/circomlib/generated/common/circom.hpp",
    "content": "#ifndef __CIRCOM_H\n#define __CIRCOM_H\n\n#include <map>\n#include <gmp.h>\n#include <mutex>\n#include <condition_variable>\n#include <thread>\n\n#include \"fr.hpp\"\n\ntypedef unsigned long long u64;\ntypedef uint32_t u32;\ntypedef uint8_t u8;\n\n//only for the main inputs\nstruct __attribute__((__packed__)) HashSignalInfo {\n    u64 hash;\n    u64 signalid;\n    u64 signalsize;\n};\n\nstruct IODef {\n    u32 offset;\n    u32 len;\n    u32 *lengths;\n};\n\nstruct IODefPair {\n    u32 len;\n    IODef* defs;\n};\n\nstruct Circom_Circuit {\n  //  const char *P;\n  HashSignalInfo* InputHashMap;\n  u64* witness2SignalList;\n  FrElement* circuitConstants;\n  std::map<u32,IODefPair> templateInsId2IOSignalInfo;\n};\n\n\nstruct Circom_Component {\n  u32 templateId;\n  u64 signalStart;\n  u32 inputCounter;\n  std::string templateName;\n  std::string componentName;\n  u64 idFather;\n  u32* subcomponents = NULL;\n  bool* subcomponentsParallel = NULL;\n  bool *outputIsSet = NULL;  //one for each output\n  std::mutex *mutexes = NULL;  //one for each output\n  std::condition_variable *cvs = NULL;\n  std::thread *sbct = NULL;//subcomponent threads\n};\n\n/*\nFor every template instantiation create two functions:\n- name_create\n- name_run\n\n//PFrElement: pointer to FrElement\n\nEvery name_run or circom_function has:\n=====================================\n\n//array of PFrElements for auxiliars in expression computation (known size);\nPFrElements expaux[];\n\n//array of PFrElements for local vars (known size)\nPFrElements lvar[];\n\n*/\n\nuint get_main_input_signal_start();\nuint get_main_input_signal_no();\nuint get_total_signal_no();\nuint get_number_of_components();\nuint get_size_of_input_hashmap();\nuint get_size_of_witness();\nuint get_size_of_constants();\nuint get_size_of_io_map();\n\n#endif  // __CIRCOM_H\n"
  },
  {
    "path": "vendors/circom/circomlib/generated/common/circom.hpp.diff",
    "content": "19,20c19,20\n<     u64 signalid;\n<     u64 signalsize;\n---\n>     u64 signalid; \n>     u64 signalsize; \n23c23\n< struct IODef {\n---\n> struct IODef { \n29c29\n< struct IODefPair {\n---\n> struct IODefPair { \n38c38\n<   FrElement* circuitConstants;\n---\n>   FrElement* circuitConstants;  \n49c49\n<   u64 idFather;\n---\n>   u64 idFather; \n"
  },
  {
    "path": "vendors/circom/circomlib/generated/goldilocks/BUILD.bazel",
    "content": "load(\"@iden3_ffiasm//c:build_defs.bzl\", \"generate_prime_field\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ngenerate_prime_field(\n    name = \"fr\",\n    modulus = \"18446744069414584321\",\n)\n"
  },
  {
    "path": "vendors/circom/circomlib/generated/grumpkin/BUILD.bazel",
    "content": "load(\"@iden3_ffiasm//c:build_defs.bzl\", \"generate_prime_field\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ngenerate_prime_field(\n    name = \"fr\",\n    modulus = \"21888242871839275222246405745257275088696311157297823662689037894645226208583\",\n)\n"
  },
  {
    "path": "vendors/circom/circomlib/generated/pallas/BUILD.bazel",
    "content": "load(\"@iden3_ffiasm//c:build_defs.bzl\", \"generate_prime_field\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ngenerate_prime_field(\n    name = \"fr\",\n    modulus = \"28948022309329048855892746252171976963363056481941647379679742748393362948097\",\n)\n"
  },
  {
    "path": "vendors/circom/circomlib/generated/secq256r1/BUILD.bazel",
    "content": "load(\"@iden3_ffiasm//c:build_defs.bzl\", \"generate_prime_field\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ngenerate_prime_field(\n    name = \"fr\",\n    modulus = \"115792089237316195423570985008687907852837564279074904382605163141518161494337\",\n)\n"
  },
  {
    "path": "vendors/circom/circomlib/generated/vesta/BUILD.bazel",
    "content": "load(\"@iden3_ffiasm//c:build_defs.bzl\", \"generate_prime_field\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ngenerate_prime_field(\n    name = \"fr\",\n    modulus = \"28948022309329048855892746252171976963363056481941560715954676764349967630337\",\n)\n"
  },
  {
    "path": "vendors/circom/circomlib/json/BUILD.bazel",
    "content": "load(\"@kroma_network_tachyon//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"groth16_proof\",\n    hdrs = [\"groth16_proof.h\"],\n    deps = [\n        \":json_converter_forward\",\n        \":points\",\n        \"@kroma_network_tachyon//tachyon/base:logging\",\n        \"@kroma_network_tachyon//tachyon/zk/r1cs/groth16:proof\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"json\",\n    hdrs = [\"json.h\"],\n    deps = [\n        \":json_converter_forward\",\n        \"@com_github_tencent_rapidjson//:rapidjson\",\n        \"@kroma_network_tachyon//tachyon/base/files:file_util\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"json_converter_forward\",\n    hdrs = [\"json_converter_forward.h\"],\n    deps = [\"@com_github_tencent_rapidjson//:rapidjson\"],\n)\n\ntachyon_cc_library(\n    name = \"points\",\n    hdrs = [\"points.h\"],\n    deps = [\n        \"@com_github_tencent_rapidjson//:rapidjson\",\n        \"@kroma_network_tachyon//tachyon/math/geometry:affine_point\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"prime_field\",\n    hdrs = [\"prime_field.h\"],\n    deps = [\n        \":json_converter_forward\",\n        \"@com_google_absl//absl/types:span\",\n        \"@kroma_network_tachyon//tachyon/math/finite_fields:prime_field_base\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"json_unittests\",\n    srcs = [\n        \"groth16_proof_unittest.cc\",\n        \"prime_field_unittest.cc\",\n    ],\n    deps = [\n        \":groth16_proof\",\n        \":prime_field\",\n        \"@kroma_network_tachyon//tachyon/math/elliptic_curves/bn/bn254\",\n        \"@kroma_network_tachyon//tachyon/math/finite_fields/test:finite_field_test\",\n    ],\n)\n"
  },
  {
    "path": "vendors/circom/circomlib/json/groth16_proof.h",
    "content": "#ifndef VENDORS_CIRCOM_CIRCOMLIB_JSON_GROTH16_PROOF_H_\n#define VENDORS_CIRCOM_CIRCOMLIB_JSON_GROTH16_PROOF_H_\n\n#include \"circomlib/json/json_converter_forward.h\"\n#include \"circomlib/json/points.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/zk/r1cs/groth16/proof.h\"\n\nnamespace tachyon::circom {\n\ntemplate <typename Curve>\nclass JsonSerializer<zk::r1cs::groth16::Proof<Curve>> {\n public:\n  static rapidjson::Document ToJson(\n      const zk::r1cs::groth16::Proof<Curve>& proof) {\n    rapidjson::Document document;\n    rapidjson::Document::AllocatorType& allocator = document.GetAllocator();\n    document.SetObject();\n\n    internal::AddMember(document, \"pi_a\", proof.a());\n    internal::AddMember(document, \"pi_b\", proof.b());\n    internal::AddMember(document, \"pi_c\", proof.c());\n\n    document.AddMember(\"protocol\", \"groth16\", allocator);\n\n    std::string_view curve_name = Curve::Config::kName;\n    rapidjson::Value curve;\n    if (curve_name == \"tachyon::math::bn254::BN254\") {\n      curve.SetString(\"bn128\");\n    } else if (curve_name == \"tachyon::math::bls12_381::BLS12_381\") {\n      curve.SetString(\"bls12381\");\n    } else {\n      NOTREACHED();\n    }\n    document.AddMember(\"curve\", curve, allocator);\n    return document;\n  }\n};\n\n}  // namespace tachyon::circom\n\n#endif  // VENDORS_CIRCOM_CIRCOMLIB_JSON_GROTH16_PROOF_H_\n"
  },
  {
    "path": "vendors/circom/circomlib/json/groth16_proof_unittest.cc",
    "content": "#include \"circomlib/json/groth16_proof.h\"\n\n#include <string>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/elliptic_curves/bn/bn254/bn254.h\"\n\nnamespace tachyon::circom {\n\nnamespace {\n\nusing Curve = math::bn254::BN254Curve;\n\nclass Groth16ProofTest : public testing::Test {\n public:\n  static void SetUpTestSuite() { Curve::Init(); }\n};\n\n}  // namespace\n\nTEST_F(Groth16ProofTest, Write) {\n  zk::r1cs::groth16::Proof<Curve> proof(math::bn254::G1AffinePoint::Random(),\n                                        math::bn254::G2AffinePoint::Random(),\n                                        math::bn254::G1AffinePoint::Random());\n\n  rapidjson::Document document = ConvertToJson(proof);\n  rapidjson::StringBuffer buffer;\n  rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);\n  document.Accept(writer);\n  {\n    auto it = document.GetObject().FindMember(\"pi_a\");\n    ASSERT_NE(it, document.MemberEnd());\n    auto it2 = it->value.GetArray().Begin();\n    for (size_t i = 0; i < 3; ++i) {\n      std::string expected;\n      if (i == 0) {\n        expected = proof.a().x().ToString();\n      } else if (i == 1) {\n        expected = proof.a().y().ToString();\n      } else {\n        expected = \"1\";\n      }\n      EXPECT_EQ(*(it2++), expected);\n    }\n    ASSERT_EQ(it2, it->value.GetArray().End());\n  }\n  {\n    auto it = document.GetObject().FindMember(\"pi_b\");\n    ASSERT_NE(it, document.MemberEnd());\n    auto it2 = it->value.GetArray().Begin();\n    for (size_t i = 0; i < 3; ++i) {\n      std::string expected;\n      if (i == 0) {\n        auto it3 = it2->GetArray().Begin();\n        for (size_t j = 0; j < 2; ++j) {\n          if (j == 0) {\n            expected = proof.b().x().c0().ToString();\n          } else {\n            expected = proof.b().x().c1().ToString();\n          }\n          EXPECT_EQ(*(it3++), expected);\n        }\n        ASSERT_EQ(it3, it2->GetArray().End());\n      } else if (i == 1) {\n        auto it3 = it2->GetArray().Begin();\n        for (size_t j = 0; j < 2; ++j) {\n          if (j == 0) {\n            expected = proof.b().y().c0().ToString();\n          } else {\n            expected = proof.b().y().c1().ToString();\n          }\n          EXPECT_EQ(*(it3++), expected);\n        }\n        ASSERT_EQ(it3, it2->GetArray().End());\n      } else {\n        auto it3 = it2->GetArray().Begin();\n        for (size_t j = 0; j < 2; ++j) {\n          if (j == 0) {\n            expected = \"1\";\n          } else {\n            expected = \"0\";\n          }\n          EXPECT_EQ(*(it3++), expected);\n        }\n        ASSERT_EQ(it3, it2->GetArray().End());\n      }\n      ++it2;\n    }\n    ASSERT_EQ(it2, it->value.GetArray().End());\n  }\n  {\n    auto it = document.GetObject().FindMember(\"pi_c\");\n    auto it2 = it->value.GetArray().Begin();\n    for (size_t i = 0; i < 3; ++i) {\n      std::string expected;\n      if (i == 0) {\n        expected = proof.c().x().ToString();\n      } else if (i == 1) {\n        expected = proof.c().y().ToString();\n      } else {\n        expected = \"1\";\n      }\n      EXPECT_EQ(*(it2++), expected);\n    }\n    ASSERT_EQ(it2, it->value.GetArray().End());\n  }\n  {\n    auto it = document.GetObject().FindMember(\"protocol\");\n    ASSERT_NE(it, document.MemberEnd());\n    EXPECT_EQ(it->value.GetString(), \"groth16\");\n  }\n  {\n    auto it = document.GetObject().FindMember(\"curve\");\n    ASSERT_NE(it, document.MemberEnd());\n    EXPECT_EQ(it->value.GetString(), \"bn128\");\n  }\n}\n\n}  // namespace tachyon::circom\n"
  },
  {
    "path": "vendors/circom/circomlib/json/json.h",
    "content": "#ifndef VENDORS_CIRCOM_CIRCOMLIB_JSON_JSON_H_\n#define VENDORS_CIRCOM_CIRCOMLIB_JSON_JSON_H_\n\n#include \"rapidjson/stringbuffer.h\"\n#include \"rapidjson/writer.h\"\n\n#include \"circomlib/json/json_converter_forward.h\"\n#include \"tachyon/base/files/file_util.h\"\n\nnamespace tachyon::circom {\n\ntemplate <typename T>\nbool WriteToJson(const T& value, const base::FilePath& path) {\n  rapidjson::StringBuffer buffer;\n  rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);\n  ConvertToJson(value).Accept(writer);\n  return base::WriteFile(path, buffer.GetString());\n}\n\n}  // namespace tachyon::circom\n\n#endif  // VENDORS_CIRCOM_CIRCOMLIB_JSON_JSON_H_\n"
  },
  {
    "path": "vendors/circom/circomlib/json/json_converter_forward.h",
    "content": "#ifndef VENDORS_CIRCOM_CIRCOMLIB_JSON_JSON_CONVERTER_FORWARD_H_\n#define VENDORS_CIRCOM_CIRCOMLIB_JSON_JSON_CONVERTER_FORWARD_H_\n\n#include \"rapidjson/document.h\"\n\nnamespace tachyon::circom {\n\ntemplate <typename T, typename SFINAE = void>\nclass JsonSerializer;\n\ntemplate <typename T>\nrapidjson::Document ConvertToJson(const T& value) {\n  return JsonSerializer<T>::ToJson(value);\n}\n\n}  // namespace tachyon::circom\n\n#endif  // VENDORS_CIRCOM_CIRCOMLIB_JSON_JSON_CONVERTER_FORWARD_H_\n"
  },
  {
    "path": "vendors/circom/circomlib/json/points.h",
    "content": "#ifndef VENDORS_CIRCOM_CIRCOMLIB_JSON_POINTS_H_\n#define VENDORS_CIRCOM_CIRCOMLIB_JSON_POINTS_H_\n\n#include <string_view>\n\n#include \"rapidjson/document.h\"\n\n#include \"tachyon/math/geometry/affine_point.h\"\n\nnamespace tachyon::circom::internal {\n\ntemplate <typename Curve>\nvoid AddMember(rapidjson::Document& document, std::string_view member,\n               const math::AffinePoint<Curve>& point) {\n  using BaseField = typename math::AffinePoint<Curve>::BaseField;\n\n  rapidjson::Document::AllocatorType& allocator = document.GetAllocator();\n  const BaseField& x = point.x();\n  const BaseField& y = point.y();\n  rapidjson::Value value;\n  value.SetArray();\n  if constexpr (BaseField::ExtensionDegree() == 1) {\n    value.PushBack({x.ToString(), allocator}, allocator);\n    value.PushBack({y.ToString(), allocator}, allocator);\n    value.PushBack(\"1\", allocator);\n  } else {\n    static_assert(BaseField::ExtensionDegree() == 2);\n    {\n      rapidjson::Value inner_array;\n      inner_array.SetArray();\n      inner_array.PushBack({x.c0().ToString(), allocator}, allocator);\n      inner_array.PushBack({x.c1().ToString(), allocator}, allocator);\n      value.PushBack(inner_array, allocator);\n    }\n    {\n      rapidjson::Value inner_array;\n      inner_array.SetArray();\n      inner_array.PushBack({y.c0().ToString(), allocator}, allocator);\n      inner_array.PushBack({y.c1().ToString(), allocator}, allocator);\n      value.PushBack(inner_array, allocator);\n    }\n    {\n      rapidjson::Value inner_array;\n      inner_array.SetArray();\n      inner_array.PushBack(\"1\", allocator);\n      inner_array.PushBack(\"0\", allocator);\n      value.PushBack(inner_array, allocator);\n    }\n  }\n  document.AddMember(rapidjson::StringRef(member.data(), member.size()), value,\n                     document.GetAllocator());\n}\n\n}  // namespace tachyon::circom::internal\n\n#endif  // VENDORS_CIRCOM_CIRCOMLIB_JSON_POINTS_H_\n"
  },
  {
    "path": "vendors/circom/circomlib/json/prime_field.h",
    "content": "#ifndef VENDORS_CIRCOM_CIRCOMLIB_JSON_PRIME_FIELD_H_\n#define VENDORS_CIRCOM_CIRCOMLIB_JSON_PRIME_FIELD_H_\n\n#include <type_traits>\n\n#include \"absl/types/span.h\"\n\n#include \"circomlib/json/json_converter_forward.h\"\n#include \"tachyon/math/finite_fields/prime_field_base.h\"\n\nnamespace tachyon::circom {\n\ntemplate <typename F>\nclass JsonSerializer<\n    absl::Span<const F>,\n    std::enable_if_t<std::is_base_of_v<tachyon::math::PrimeFieldBase<F>, F>>> {\n public:\n  static rapidjson::Document ToJson(absl::Span<const F> prime_fields) {\n    rapidjson::Document document;\n    rapidjson::Document::AllocatorType& allocator = document.GetAllocator();\n    document.SetArray();\n\n    for (const F& prime_field : prime_fields) {\n      rapidjson::Value value;\n      value.SetString(prime_field.ToString(), allocator);\n      document.PushBack(value, allocator);\n    }\n\n    return document;\n  }\n};\n\n}  // namespace tachyon::circom\n\n#endif  // VENDORS_CIRCOM_CIRCOMLIB_JSON_PRIME_FIELD_H_\n"
  },
  {
    "path": "vendors/circom/circomlib/json/prime_field_unittest.cc",
    "content": "#include \"circomlib/json/prime_field.h\"\n\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/base/containers/container_util.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/fr.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n\nnamespace tachyon::circom {\n\nnamespace {\n\nclass PrimeFieldTest : public math::FiniteFieldTest<math::bn254::Fr> {};\n\n}  // namespace\n\nTEST_F(PrimeFieldTest, Write) {\n  std::vector<math::bn254::Fr> prime_fields =\n      base::CreateVector(8, []() { return math::bn254::Fr::Random(); });\n\n  rapidjson::Document document =\n      ConvertToJson(absl::MakeConstSpan(prime_fields));\n  rapidjson::StringBuffer buffer;\n  rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);\n  document.Accept(writer);\n  auto it = document.Begin();\n  auto it2 = prime_fields.begin();\n  for (; it != document.End(); ++it, ++it2) {\n    EXPECT_EQ(it->GetString(), it2->ToString());\n  }\n}\n\n}  // namespace tachyon::circom\n"
  },
  {
    "path": "vendors/circom/circomlib/r1cs/BUILD.bazel",
    "content": "load(\"@kroma_network_tachyon//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"constraint\",\n    hdrs = [\"constraint.h\"],\n    deps = [\"@com_google_absl//absl/strings\"],\n)\n\ntachyon_cc_library(\n    name = \"r1cs\",\n    srcs = [\"r1cs.cc\"],\n    hdrs = [\"r1cs.h\"],\n    deps = [\n        \":constraint\",\n        \"//circomlib/base:modulus\",\n        \"//circomlib/base:sections\",\n        \"@kroma_network_tachyon//tachyon/base:logging\",\n        \"@kroma_network_tachyon//tachyon/base/buffer:endian_auto_reset\",\n        \"@kroma_network_tachyon//tachyon/base/buffer:read_only_buffer\",\n        \"@kroma_network_tachyon//tachyon/base/files:file_util\",\n        \"@kroma_network_tachyon//tachyon/base/strings:string_util\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"r1cs_unittests\",\n    srcs = [\"r1cs_unittest.cc\"],\n    data = [\"//examples:compile_multiplier_3\"],\n    deps = [\n        \":r1cs\",\n        \"@kroma_network_tachyon//tachyon/math/elliptic_curves/bn/bn254:fr\",\n        \"@kroma_network_tachyon//tachyon/math/finite_fields/test:finite_field_test\",\n    ],\n)\n"
  },
  {
    "path": "vendors/circom/circomlib/r1cs/constraint.h",
    "content": "#ifndef VENDORS_CIRCOM_CIRCOMLIB_R1CS_CONSTRAINT_H_\n#define VENDORS_CIRCOM_CIRCOMLIB_R1CS_CONSTRAINT_H_\n\n#include <stdint.h>\n\n#include <sstream>\n#include <string>\n#include <vector>\n\n#include \"absl/strings/substitute.h\"\n\nnamespace tachyon::circom {\n\ntemplate <typename F>\nstruct Term {\n  uint32_t wire_id;\n  F coefficient;\n\n  bool operator==(const Term& other) const {\n    return wire_id == other.wire_id && coefficient == other.coefficient;\n  }\n  bool operator!=(const Term& other) const { return !operator==(other); }\n\n  std::string ToString() const {\n    return absl::Substitute(\"$0ω_$1\", coefficient.ToString(), wire_id);\n  }\n};\n\ntemplate <typename F>\nstruct LinearCombination {\n  std::vector<Term<F>> terms;\n\n  bool operator==(const LinearCombination& other) const {\n    return terms == other.terms;\n  }\n  bool operator!=(const LinearCombination& other) const {\n    return terms != other.terms;\n  }\n\n  std::string ToString() const {\n    std::stringstream ss;\n    for (size_t i = 0; i < terms.size(); ++i) {\n      ss << terms[i].ToString();\n      if (i != terms.size() - 1) ss << \" + \";\n    }\n    return ss.str();\n  }\n};\n\ntemplate <typename F>\nstruct Constraint {\n  LinearCombination<F> a;\n  LinearCombination<F> b;\n  LinearCombination<F> c;\n\n  bool operator==(const Constraint& other) const {\n    return a == other.a && b == other.b && c == other.c;\n  }\n  bool operator!=(const Constraint& other) const { return !operator==(other); }\n\n  std::string ToString() const {\n    if (a.terms.size() > 1 && b.terms.size() > 1) {\n      return absl::Substitute(\"($0) * ($1) = $2\", a.ToString(), b.ToString(),\n                              c.ToString());\n    } else if (a.terms.size() > 1) {\n      return absl::Substitute(\"($0) * $1 = $2\", a.ToString(), b.ToString(),\n                              c.ToString());\n    } else if (b.terms.size() > 1) {\n      return absl::Substitute(\"$0 * ($1) = $2\", a.ToString(), b.ToString(),\n                              c.ToString());\n    } else {\n      return absl::Substitute(\"$0 * $1 = $2\", a.ToString(), b.ToString(),\n                              c.ToString());\n    }\n  }\n};\n\n}  // namespace tachyon::circom\n\n#endif  // VENDORS_CIRCOM_CIRCOMLIB_R1CS_CONSTRAINT_H_\n"
  },
  {
    "path": "vendors/circom/circomlib/r1cs/r1cs.cc",
    "content": "#include \"circomlib/r1cs/r1cs.h\"\n\nnamespace tachyon::circom::v1 {\n\nstd::string_view R1CSSectionTypeToString(R1CSSectionType type) {\n  switch (type) {\n    case R1CSSectionType::kHeader:\n      return \"Header\";\n    case R1CSSectionType::kConstraints:\n      return \"Constraints\";\n    case R1CSSectionType::kWire2LabelIdMap:\n      return \"Wire2LabelIdMap\";\n    case R1CSSectionType::kCustomGatesList:\n      return \"CustomGatesList\";\n    case R1CSSectionType::kCustomGatesApplication:\n      return \"CustomGatesApplication\";\n  }\n  NOTREACHED();\n  return \"\";\n}\n\n}  // namespace tachyon::circom::v1\n"
  },
  {
    "path": "vendors/circom/circomlib/r1cs/r1cs.h",
    "content": "#ifndef VENDORS_CIRCOM_CIRCOMLIB_R1CS_R1CS_H_\n#define VENDORS_CIRCOM_CIRCOMLIB_R1CS_R1CS_H_\n\n#include <stddef.h>\n#include <stdint.h>\n#include <string.h>\n\n#include <memory>\n#include <optional>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"circomlib/base/modulus.h\"\n#include \"circomlib/base/sections.h\"\n#include \"circomlib/r1cs/constraint.h\"\n#include \"tachyon/base/buffer/endian_auto_reset.h\"\n#include \"tachyon/base/buffer/read_only_buffer.h\"\n#include \"tachyon/base/files/file_util.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/strings/string_util.h\"\n\nnamespace tachyon::circom {\nnamespace v1 {\n\ntemplate <typename F>\nstruct R1CS;\n\n}  // namespace v1\n\ntemplate <typename F>\nstruct R1CS {\n  virtual ~R1CS() = default;\n\n  virtual uint32_t GetVersion() const = 0;\n\n  virtual v1::R1CS<F>* ToV1() { return nullptr; }\n\n  virtual bool Read(const base::ReadOnlyBuffer& buffer) = 0;\n\n  virtual size_t GetNumInstanceVariables() const = 0;\n  virtual size_t GetNumVariables() const = 0;\n  virtual const std::vector<Constraint<F>>& GetConstraints() const = 0;\n  virtual const std::vector<uint64_t>& GetWireId2LabelIdMap() const = 0;\n};\n\nconstexpr char kR1CSMagic[4] = {'r', '1', 'c', 's'};\n\n// Return nullptr if the parser failed to parse.\ntemplate <typename F>\nstd::unique_ptr<R1CS<F>> ParseR1CS(const base::FilePath& path) {\n  std::optional<std::vector<uint8_t>> r1cs_data = base::ReadFileToBytes(path);\n  if (!r1cs_data.has_value()) {\n    LOG(ERROR) << \"Failed to read file: \" << path.value();\n    return nullptr;\n  }\n\n  base::ReadOnlyBuffer buffer(r1cs_data->data(), r1cs_data->size());\n  buffer.set_endian(base::Endian::kLittle);\n  char magic[4];\n  uint32_t version;\n  if (!buffer.ReadMany(magic, &version)) return nullptr;\n  if (memcmp(magic, kR1CSMagic, 4) != 0) {\n    LOG(ERROR) << \"Invalid magic: \" << magic;\n    return nullptr;\n  }\n  std::unique_ptr<R1CS<F>> r1cs;\n  if (version == 1) {\n    r1cs.reset(new v1::R1CS<F>());\n    CHECK(r1cs->ToV1()->Read(buffer));\n  } else {\n    LOG(ERROR) << \"Invalid version: \" << version;\n    return nullptr;\n  }\n  return r1cs;\n}\n\nnamespace v1 {\n\nenum class R1CSSectionType : uint32_t {\n  kHeader = 0x1,\n  kConstraints = 0x2,\n  kWire2LabelIdMap = 0x3,\n  kCustomGatesList = 0x4,\n  kCustomGatesApplication = 0x5,\n};\n\nstd::string_view R1CSSectionTypeToString(R1CSSectionType type);\n\nstruct R1CSHeaderSection {\n  Modulus modulus;\n  // Total number of wires including ONE signal (Index 0).\n  uint32_t num_wires;\n  // Total number of public output wires. They should be starting at\n  // idx 1.\n  uint32_t num_public_outputs;\n  // Total number of public input wires. They should be starting just\n  // after the public output.\n  uint32_t num_public_inputs;\n  // Total number of private input wires. They should be starting just\n  // after the public inputs.\n  uint32_t num_private_inputs;\n  // Total number of labels.\n  uint64_t num_labels;\n  // Total number of constraints.\n  uint32_t num_constraints;\n\n  bool operator==(const R1CSHeaderSection& other) const {\n    return modulus == other.modulus && num_wires == other.num_wires &&\n           num_public_outputs == other.num_public_outputs &&\n           num_public_inputs == other.num_public_inputs &&\n           num_private_inputs == other.num_private_inputs &&\n           num_labels == other.num_labels &&\n           num_constraints == other.num_constraints;\n  }\n  bool operator!=(const R1CSHeaderSection& other) const {\n    return !operator==(other);\n  }\n\n  bool Read(const base::ReadOnlyBuffer& buffer) {\n    base::EndianAutoReset reset(buffer, base::Endian::kLittle);\n    if (!modulus.Read(buffer)) return false;\n    return buffer.ReadMany(&num_wires, &num_public_outputs, &num_public_inputs,\n                           &num_private_inputs, &num_labels, &num_constraints);\n  }\n\n  std::string ToString() const {\n    return absl::Substitute(\n        \"{modulus: $0, num_wires: $1, num_public_outputs: $2, \"\n        \"num_public_inputs: $3, num_private_inputs: $4 num_labels: $5, \"\n        \"num_constraints: $6}\",\n        modulus.ToString(), num_wires, num_public_outputs, num_public_inputs,\n        num_private_inputs, num_labels, num_constraints);\n  }\n};\n\ntemplate <typename F>\nstruct R1CSConstraintsSection {\n  std::vector<Constraint<F>> constraints;\n\n  bool operator==(const R1CSConstraintsSection& other) const {\n    return constraints == other.constraints;\n  }\n  bool operator!=(const R1CSConstraintsSection& other) const {\n    return constraints != other.constraints;\n  }\n\n  bool Read(const base::ReadOnlyBuffer& buffer,\n            const R1CSHeaderSection& header) {\n    base::EndianAutoReset reset(buffer, base::Endian::kLittle);\n    constraints.reserve(header.num_constraints);\n    for (uint32_t i = 0; i < header.num_constraints; ++i) {\n      Constraint<F> constraint;\n      for (uint32_t j = 0; j < 3; ++j) {\n        uint32_t n;\n        if (!buffer.Read(&n)) return false;\n        std::vector<Term<F>> terms(n);\n        for (uint32_t k = 0; k < n; ++k) {\n          if (!buffer.ReadMany(&terms[k].wire_id, &terms[k].coefficient))\n            return false;\n        }\n        if (j == 0) {\n          constraint.a = {std::move(terms)};\n        } else if (j == 1) {\n          constraint.b = {std::move(terms)};\n        } else {\n          constraint.c = {std::move(terms)};\n        }\n      }\n      constraints.push_back(std::move(constraint));\n    }\n    return true;\n  }\n\n  std::string ToString() const { return base::ContainerToString(constraints); }\n};\n\nstruct R1CSWireId2LabelIdMapSection {\n  std::vector<uint64_t> label_ids;\n\n  bool operator==(const R1CSWireId2LabelIdMapSection& other) const {\n    return label_ids == other.label_ids;\n  }\n  bool operator!=(const R1CSWireId2LabelIdMapSection& other) const {\n    return label_ids != other.label_ids;\n  }\n\n  bool Read(const base::ReadOnlyBuffer& buffer,\n            const R1CSHeaderSection& header) {\n    base::EndianAutoReset reset(buffer, base::Endian::kLittle);\n    label_ids.resize(header.num_wires);\n    for (uint32_t i = 0; i < header.num_wires; ++i) {\n      if (!buffer.Read(&label_ids[i])) return false;\n    }\n    return true;\n  }\n\n  std::string ToString() const { return base::ContainerToString(label_ids); }\n};\n\ntemplate <typename F>\nstruct R1CS : public circom::R1CS<F> {\n  R1CSHeaderSection header;\n  R1CSConstraintsSection<F> constraints;\n  R1CSWireId2LabelIdMapSection wire_id_to_label_id_map;\n\n  // circom::R1CS methods\n  uint32_t GetVersion() const override { return 1; }\n  R1CS<F>* ToV1() override { return this; }\n\n  bool Read(const base::ReadOnlyBuffer& buffer) override {\n    Sections<R1CSSectionType> sections(buffer, &R1CSSectionTypeToString);\n    if (!sections.Read()) return false;\n\n    if (!sections.MoveTo(R1CSSectionType::kHeader)) return false;\n    if (!header.Read(buffer)) return false;\n\n    if (!sections.MoveTo(R1CSSectionType::kConstraints)) return false;\n    if (!constraints.Read(buffer, header)) return false;\n\n    if (!sections.MoveTo(R1CSSectionType::kWire2LabelIdMap)) return false;\n    if (!wire_id_to_label_id_map.Read(buffer, header)) return false;\n    return true;\n  }\n\n  size_t GetNumInstanceVariables() const override {\n    return 1 + header.num_public_outputs + header.num_public_inputs;\n  }\n\n  size_t GetNumVariables() const override { return header.num_wires; }\n\n  const std::vector<Constraint<F>>& GetConstraints() const override {\n    return constraints.constraints;\n  }\n\n  const std::vector<uint64_t>& GetWireId2LabelIdMap() const override {\n    return wire_id_to_label_id_map.label_ids;\n  }\n\n  std::string ToString() const {\n    return absl::Substitute(\n        \"{header: $0, constraints: $1, wire_id_to_label_id_map: $2}\",\n        header.ToString(), constraints.ToString(),\n        wire_id_to_label_id_map.ToString());\n  }\n};\n\n}  // namespace v1\n}  // namespace tachyon::circom\n\n#endif  // VENDORS_CIRCOM_CIRCOMLIB_R1CS_R1CS_H_\n"
  },
  {
    "path": "vendors/circom/circomlib/r1cs/r1cs_unittest.cc",
    "content": "#include \"circomlib/r1cs/r1cs.h\"\n\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/elliptic_curves/bn/bn254/fr.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n\nnamespace tachyon::circom {\n\nusing F = math::bn254::Fr;\n\nnamespace {\n\nclass R1CSTest : public math::FiniteFieldTest<F> {};\n\n}  // namespace\n\nTEST(R1CSTest, Parse) {\n  std::unique_ptr<R1CS<F>> r1cs =\n      ParseR1CS<F>(base::FilePath(\"examples/multiplier_3.r1cs\"));\n  ASSERT_TRUE(r1cs);\n  ASSERT_EQ(r1cs->GetVersion(), 1);\n\n  std::array<uint8_t, 32> bytes = math::bn254::FrConfig::kModulus.ToBytesLE();\n  v1::R1CSHeaderSection expected_header = {\n      Modulus{std::vector<uint8_t>(bytes.begin(), bytes.end())},\n      6,\n      1,\n      0,\n      3,\n      11,\n      2,\n  };\n  EXPECT_EQ(r1cs->ToV1()->header, expected_header);\n\n  F one = F::One();\n  F neg_one(math::bn254::FrConfig::kModulus - math::BigInt<4>::One());\n  // -ω₂ * ω₃ = -ω₅\n  // -ω₅ * ω₄ = -ω₁\n  v1::R1CSConstraintsSection<F> expected_constraints = {{{\n                                                             {{{2, neg_one}}},\n                                                             {{{3, one}}},\n                                                             {{{5, neg_one}}},\n                                                         },\n                                                         {\n                                                             {{{5, neg_one}}},\n                                                             {{{4, one}}},\n                                                             {{{1, neg_one}}},\n                                                         }}};\n  EXPECT_EQ(r1cs->ToV1()->constraints, expected_constraints);\n  v1::R1CSWireId2LabelIdMapSection expected_wire_id_to_label_id_map = {{\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n  }};\n  EXPECT_EQ(r1cs->ToV1()->wire_id_to_label_id_map,\n            expected_wire_id_to_label_id_map);\n}\n\n}  // namespace tachyon::circom\n"
  },
  {
    "path": "vendors/circom/circomlib/wtns/BUILD.bazel",
    "content": "load(\"@kroma_network_tachyon//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"wtns\",\n    srcs = [\"wtns.cc\"],\n    hdrs = [\"wtns.h\"],\n    deps = [\n        \"//circomlib/base:modulus\",\n        \"//circomlib/base:sections\",\n        \"@kroma_network_tachyon//tachyon/base:logging\",\n        \"@kroma_network_tachyon//tachyon/base:openmp_util\",\n        \"@kroma_network_tachyon//tachyon/base/buffer:endian_auto_reset\",\n        \"@kroma_network_tachyon//tachyon/base/buffer:read_only_buffer\",\n        \"@kroma_network_tachyon//tachyon/base/files:bin_file\",\n        \"@kroma_network_tachyon//tachyon/base/strings:string_util\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"wtns_unittests\",\n    srcs = [\"wtns_unittest.cc\"],\n    data = [\"multiplier_3.wtns\"],\n    deps = [\n        \":wtns\",\n        \"@kroma_network_tachyon//tachyon/math/elliptic_curves/bn/bn254:fr\",\n        \"@kroma_network_tachyon//tachyon/math/finite_fields/test:finite_field_test\",\n    ],\n)\n"
  },
  {
    "path": "vendors/circom/circomlib/wtns/wtns.cc",
    "content": "#include \"circomlib/wtns/wtns.h\"\n\nnamespace tachyon::circom::v2 {\n\nstd::string_view WtnsSectionTypeToString(WtnsSectionType type) {\n  switch (type) {\n    case WtnsSectionType::kHeader:\n      return \"Header\";\n    case WtnsSectionType::kData:\n      return \"Data\";\n  }\n  NOTREACHED();\n  return \"\";\n}\n\n}  // namespace tachyon::circom::v2\n"
  },
  {
    "path": "vendors/circom/circomlib/wtns/wtns.h",
    "content": "#ifndef VENDORS_CIRCOM_CIRCOMLIB_WTNS_WTNS_H_\n#define VENDORS_CIRCOM_CIRCOMLIB_WTNS_WTNS_H_\n\n#include <stddef.h>\n#include <stdint.h>\n#include <string.h>\n#include <sys/mman.h>\n\n#include <memory>\n#include <string>\n#include <utility>\n\n#include \"circomlib/base/modulus.h\"\n#include \"circomlib/base/sections.h\"\n#include \"tachyon/base/buffer/endian_auto_reset.h\"\n#include \"tachyon/base/buffer/read_only_buffer.h\"\n#include \"tachyon/base/files/bin_file.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/openmp_util.h\"\n#include \"tachyon/base/strings/string_util.h\"\n\nnamespace tachyon::circom {\nnamespace v2 {\n\ntemplate <typename F>\nstruct Wtns;\n\n}  // namespace v2\n\ntemplate <typename F>\nstruct Wtns {\n  explicit Wtns(base::BinFile bin_file) : bin_file(std::move(bin_file)) {}\n  virtual ~Wtns() = default;\n\n  virtual uint32_t GetVersion() const = 0;\n\n  virtual v2::Wtns<F>* ToV2() { return nullptr; }\n\n  virtual bool Read(const base::ReadOnlyBuffer& buffer) = 0;\n\n  virtual size_t GetNumWitness() const = 0;\n\n  virtual absl::Span<const F> GetWitnesses() const = 0;\n\n  base::BinFile bin_file;\n};\n\nconstexpr char kWtnsMagic[4] = {'w', 't', 'n', 's'};\n\n// Return nullptr if the parser failed to parse.\ntemplate <typename F>\nstd::unique_ptr<Wtns<F>> ParseWtns(const base::FilePath& path,\n                                   bool use_mmap = true) {\n  base::BinFile bin_file;\n  CHECK(bin_file.Load(path, use_mmap));\n  if (use_mmap) {\n    PCHECK(madvise(bin_file.GetData(), bin_file.GetDataLength(),\n                   MADV_SEQUENTIAL) == 0);\n  }\n\n  base::ReadOnlyBuffer buffer = bin_file.ToReadOnlyBuffer();\n  buffer.set_endian(base::Endian::kLittle);\n  char magic[4];\n  uint32_t version;\n  if (!buffer.ReadMany(magic, &version)) return nullptr;\n  if (memcmp(magic, kWtnsMagic, 4) != 0) {\n    LOG(ERROR) << \"Invalid magic: \" << magic;\n    return nullptr;\n  }\n  std::unique_ptr<Wtns<F>> wtns;\n  if (version == 2) {\n    wtns.reset(new v2::Wtns<F>(std::move(bin_file)));\n    CHECK(wtns->ToV2()->Read(buffer));\n  } else {\n    LOG(ERROR) << \"Invalid version: \" << version;\n    return nullptr;\n  }\n  return wtns;\n}\n\nnamespace v2 {\n\nenum class WtnsSectionType : uint32_t {\n  kHeader = 0x1,\n  kData = 0x2,\n};\n\nstd::string_view WtnsSectionTypeToString(WtnsSectionType type);\n\nstruct WtnsHeaderSection {\n  Modulus modulus;\n  uint32_t num_witness;\n\n  bool operator==(const WtnsHeaderSection& other) const {\n    return modulus == other.modulus && num_witness == other.num_witness;\n  }\n  bool operator!=(const WtnsHeaderSection& other) const {\n    return !operator==(other);\n  }\n\n  bool Read(const base::ReadOnlyBuffer& buffer) {\n    base::EndianAutoReset reset(buffer, base::Endian::kLittle);\n    if (!modulus.Read(buffer)) return false;\n    return buffer.ReadMany(&num_witness);\n  }\n\n  std::string ToString() const {\n    return absl::Substitute(\"{modulus: $0, num_witness: $1}\",\n                            modulus.ToString(), num_witness);\n  }\n};\n\ntemplate <typename F>\nstruct WtnsDataSection {\n  absl::Span<F> witnesses;\n\n  bool operator==(const WtnsDataSection& other) const {\n    return witnesses == other.witnesses;\n  }\n  bool operator!=(const WtnsDataSection& other) const {\n    return witnesses != other.witnesses;\n  }\n\n  bool Read(const base::ReadOnlyBuffer& buffer,\n            const WtnsHeaderSection& header) {\n    F* ptr;\n    if (!buffer.ReadPtr(&ptr, header.num_witness)) return false;\n    witnesses = {ptr, header.num_witness};\n\n    OMP_PARALLEL_FOR(uint32_t i = 0; i < header.num_witness; ++i) {\n      witnesses[i] = F(witnesses[i].value());\n    }\n    return true;\n  }\n\n  std::string ToString() const { return base::ContainerToString(witnesses); }\n};\n\ntemplate <typename F>\nstruct Wtns : public circom::Wtns<F> {\n  WtnsHeaderSection header;\n  WtnsDataSection<F> data;\n\n  explicit Wtns(base::BinFile bin_file)\n      : circom::Wtns<F>(std::move(bin_file)) {}\n\n  // circom::Wtns methods\n  uint32_t GetVersion() const override { return 2; }\n  Wtns* ToV2() override { return this; }\n\n  bool Read(const base::ReadOnlyBuffer& buffer) override {\n    Sections<WtnsSectionType> sections(buffer, &WtnsSectionTypeToString);\n    if (!sections.Read()) return false;\n\n    if (!sections.MoveTo(WtnsSectionType::kHeader)) return false;\n    if (!header.Read(buffer)) return false;\n\n    if (!sections.MoveTo(WtnsSectionType::kData)) return false;\n    if (!data.Read(buffer, header)) return false;\n    return true;\n  }\n\n  size_t GetNumWitness() const override { return data.witnesses.size(); }\n\n  absl::Span<const F> GetWitnesses() const override { return data.witnesses; }\n\n  std::string ToString() const {\n    return absl::Substitute(\"{header: $0, data: $1}\", header.ToString(),\n                            data.ToString());\n  }\n};\n\n}  // namespace v2\n}  // namespace tachyon::circom\n\n#endif  // VENDORS_CIRCOM_CIRCOMLIB_WTNS_WTNS_H_\n"
  },
  {
    "path": "vendors/circom/circomlib/wtns/wtns_unittest.cc",
    "content": "#include \"circomlib/wtns/wtns.h\"\n\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/elliptic_curves/bn/bn254/fr.h\"\n#include \"tachyon/math/finite_fields/test/finite_field_test.h\"\n\nnamespace tachyon::circom {\n\nusing F = math::bn254::Fr;\n\nnamespace {\n\nclass WtnsTest : public math::FiniteFieldTest<F> {};\n\n}  // namespace\n\nTEST(WtnsTest, Parse) {\n  // Generated with { \"in\": [\"3\", \"4\", \"5\"] }\n  std::unique_ptr<Wtns<F>> wtns =\n      ParseWtns<F>(base::FilePath(\"circomlib/wtns/multiplier_3.wtns\"));\n  ASSERT_TRUE(wtns);\n  ASSERT_EQ(wtns->GetVersion(), 2);\n\n  std::array<uint8_t, 32> bytes = math::bn254::FrConfig::kModulus.ToBytesLE();\n  v2::WtnsHeaderSection expected_header = {\n      Modulus{std::vector<uint8_t>(bytes.begin(), bytes.end())},\n      6,\n  };\n  EXPECT_EQ(wtns->ToV2()->header, expected_header);\n\n  std::vector<F> expected_data{\n      F(1), F(60), F(3), F(4), F(5), F(12),\n  };\n  v2::WtnsDataSection<F> expected_data_section = {\n      absl::MakeSpan(expected_data)};\n  EXPECT_EQ(wtns->ToV2()->data, expected_data_section);\n}\n\n}  // namespace tachyon::circom\n"
  },
  {
    "path": "vendors/circom/circomlib/zkey/BUILD.bazel",
    "content": "load(\"@kroma_network_tachyon//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_cc_unittest\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ntachyon_cc_library(\n    name = \"coefficient\",\n    hdrs = [\"coefficient.h\"],\n    deps = [\"@com_google_absl//absl/strings\"],\n)\n\ntachyon_cc_library(\n    name = \"proving_key\",\n    hdrs = [\"proving_key.h\"],\n    deps = [\n        \":verifying_key\",\n        \"@com_google_absl//absl/types:span\",\n        \"@kroma_network_tachyon//tachyon/base/strings:string_util\",\n        \"@kroma_network_tachyon//tachyon/zk/r1cs/groth16:proving_key\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"verifying_key\",\n    hdrs = [\"verifying_key.h\"],\n    deps = [\n        \"@com_google_absl//absl/strings\",\n        \"@kroma_network_tachyon//tachyon/base/buffer:read_only_buffer\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"zkey\",\n    srcs = [\"zkey.cc\"],\n    hdrs = [\"zkey.h\"],\n    deps = [\n        \":coefficient\",\n        \":proving_key\",\n        \"//circomlib/base:modulus\",\n        \"//circomlib/base:sections\",\n        \"@kroma_network_tachyon//tachyon/base:auto_reset\",\n        \"@kroma_network_tachyon//tachyon/base:logging\",\n        \"@kroma_network_tachyon//tachyon/base:openmp_util\",\n        \"@kroma_network_tachyon//tachyon/base/buffer:copyable\",\n        \"@kroma_network_tachyon//tachyon/base/buffer:endian_auto_reset\",\n        \"@kroma_network_tachyon//tachyon/base/buffer:read_only_buffer\",\n        \"@kroma_network_tachyon//tachyon/base/files:bin_file\",\n        \"@kroma_network_tachyon//tachyon/base/strings:string_util\",\n    ],\n)\n\ntachyon_cc_unittest(\n    name = \"zkey_unittests\",\n    srcs = [\"zkey_unittest.cc\"],\n    data = [\"//examples:multiplier_3.zkey\"],\n    deps = [\n        \":zkey\",\n        \"@kroma_network_tachyon//tachyon/math/elliptic_curves/bn/bn254\",\n    ],\n)\n"
  },
  {
    "path": "vendors/circom/circomlib/zkey/coefficient.h",
    "content": "#ifndef VENDORS_CIRCOM_CIRCOMLIB_ZKEY_COEFFICIENT_H_\n#define VENDORS_CIRCOM_CIRCOMLIB_ZKEY_COEFFICIENT_H_\n\n#include <stdint.h>\n\n#include <string>\n\n#include \"absl/strings/substitute.h\"\n\nnamespace tachyon::circom {\n\n#pragma pack(push, 1)\n// R1CS is represented as A * z ∘ B * z = C * z, where ∘ is the Hadamard\n// product. Each constraint is composed as follows:\n//\n// - [aᵢ,₀, ...,  aᵢ,ₘ₋₁] * [z₀, ..., zₘ₋₁]\n// - [bᵢ,₀, ...,  bᵢ,ₘ₋₁] * [z₀, ..., zₘ₋₁]\n// - [cᵢ,₀, ...,  cᵢ,ₘ₋₁] * [z₀, ..., zₘ₋₁]\n//\n// where i is the index of the constraints (0 ≤ i < n),\n// m is the number of QAP variables, and n is the number of constraints.\n//\n// The last constraint is computed if we know the first two constraints.\n// Therefore, |Coefficient| represents the first two constraints.\ntemplate <typename F>\nstruct Coefficient {\n  // A value denoting the matrix this constraint is for. If 0, this constraint\n  // is for matrix A. Else, this constraint is for matrix B.\n  uint32_t matrix;\n  // The index of the constraint, (0 ≤ i < n).\n  uint32_t constraint;\n  // The index of the QAP variables, (0 ≤ j < m).\n  uint32_t signal;\n  // The values of the coefficient; if the |matrix| is 0, then this points to\n  // the a[i][j]. Otherwise, this points to b[i][j].\n  F value;\n\n  bool operator==(const Coefficient& other) const {\n    return matrix == other.matrix && constraint == other.constraint &&\n           signal == other.signal && value == other.value;\n  }\n  bool operator!=(const Coefficient& other) const { return !operator==(other); }\n\n  std::string ToString() const {\n    return absl::Substitute(\n        \"{matrix: $0, constraint: $1, signal: $2, value: $3}\", matrix,\n        constraint, signal, value.ToString());\n  }\n};\n#pragma pack(pop)\n\n}  // namespace tachyon::circom\n\n#endif  // VENDORS_CIRCOM_CIRCOMLIB_ZKEY_COEFFICIENT_H_\n"
  },
  {
    "path": "vendors/circom/circomlib/zkey/proving_key.h",
    "content": "#ifndef VENDORS_CIRCOM_CIRCOMLIB_ZKEY_PROVING_KEY_H_\n#define VENDORS_CIRCOM_CIRCOMLIB_ZKEY_PROVING_KEY_H_\n\n#include <stdint.h>\n\n#include <string>\n#include <vector>\n\n#include \"absl/strings/substitute.h\"\n#include \"absl/types/span.h\"\n\n#include \"circomlib/zkey/verifying_key.h\"\n#include \"tachyon/base/strings/string_util.h\"\n#include \"tachyon/zk/r1cs/groth16/proving_key.h\"\n\nnamespace tachyon::circom {\n\ntemplate <typename Curve>\nstruct ProvingKey {\n  using G1AffinePoint = typename Curve::G1Curve::AffinePoint;\n  using G2AffinePoint = typename Curve::G2Curve::AffinePoint;\n\n  VerifyingKey<Curve> verifying_key;\n  absl::Span<G1AffinePoint> ic;\n  absl::Span<G1AffinePoint> a_g1_query;\n  absl::Span<G1AffinePoint> b_g1_query;\n  absl::Span<G2AffinePoint> b_g2_query;\n  absl::Span<G1AffinePoint> c_g1_query;\n  absl::Span<G1AffinePoint> h_g1_query;\n\n  bool operator==(const ProvingKey& other) const {\n    return verifying_key == other.verifying_key && ic == other.ic &&\n           a_g1_query == other.a_g1_query && b_g1_query == other.b_g1_query &&\n           b_g2_query == other.b_g2_query && c_g1_query == other.c_g1_query &&\n           h_g1_query == other.h_g1_query;\n  }\n  bool operator!=(const ProvingKey& other) const { return !operator==(other); }\n\n  zk::r1cs::groth16::VerifyingKey<Curve> ToNativeVerifyingKey() const {\n    return {\n        verifying_key.alpha_g1,\n        verifying_key.beta_g2,\n        verifying_key.gamma_g2,\n        verifying_key.delta_g2,\n        ic,\n    };\n  }\n\n  zk::r1cs::groth16::ProvingKey<Curve> ToNativeProvingKey() const {\n    return {\n        ToNativeVerifyingKey(),\n        verifying_key.beta_g1,\n        verifying_key.delta_g1,\n        a_g1_query,\n        b_g1_query,\n        b_g2_query,\n        h_g1_query,\n        c_g1_query,\n    };\n  }\n\n  // NOTE(chokobole): the fields are represented in montgomery form.\n  std::string ToString() const {\n    return absl::Substitute(\n        \"{verifying_key: $0, ic:$1, a_g1_query: $2, b_g1_query: $3, \"\n        \"b_g2_query: $4, c_g1_query: $5, h_g1_query: $6}\",\n        verifying_key.ToString(), base::ContainerToString(ic),\n        base::ContainerToString(a_g1_query),\n        base::ContainerToString(b_g1_query),\n        base::ContainerToString(b_g2_query),\n        base::ContainerToString(c_g1_query),\n        base::ContainerToString(h_g1_query));\n  }\n};\n\n}  // namespace tachyon::circom\n\n#endif  // VENDORS_CIRCOM_CIRCOMLIB_ZKEY_PROVING_KEY_H_\n"
  },
  {
    "path": "vendors/circom/circomlib/zkey/verifying_key.h",
    "content": "#ifndef VENDORS_CIRCOM_CIRCOMLIB_ZKEY_VERIFYING_KEY_H_\n#define VENDORS_CIRCOM_CIRCOMLIB_ZKEY_VERIFYING_KEY_H_\n\n#include <string>\n\n#include \"absl/strings/substitute.h\"\n\n#include \"tachyon/base/buffer/read_only_buffer.h\"\n\nnamespace tachyon::circom {\n\ntemplate <typename Curve>\nstruct VerifyingKey {\n  using G1AffinePoint = typename Curve::G1Curve::AffinePoint;\n  using G2AffinePoint = typename Curve::G2Curve::AffinePoint;\n\n  G1AffinePoint* alpha_g1 = nullptr;\n  G1AffinePoint* beta_g1 = nullptr;\n  G2AffinePoint* beta_g2 = nullptr;\n  G2AffinePoint* gamma_g2 = nullptr;\n  G1AffinePoint* delta_g1 = nullptr;\n  G2AffinePoint* delta_g2 = nullptr;\n\n  bool operator==(const VerifyingKey& other) const {\n    return *alpha_g1 == *other.alpha_g1 && *beta_g1 == *other.beta_g1 &&\n           *beta_g2 == *other.beta_g2 && *gamma_g2 == *other.gamma_g2 &&\n           *delta_g1 == *other.delta_g1 && *delta_g2 == *other.delta_g2;\n  }\n  bool operator!=(const VerifyingKey& other) const {\n    return !operator==(other);\n  }\n\n  bool Read(const base::ReadOnlyBuffer& buffer) {\n    return buffer.ReadPtr(&alpha_g1, 1) && buffer.ReadPtr(&beta_g1, 1) &&\n           buffer.ReadPtr(&beta_g2, 1) && buffer.ReadPtr(&gamma_g2, 1) &&\n           buffer.ReadPtr(&delta_g1, 1) && buffer.ReadPtr(&delta_g2, 1);\n  }\n\n  // NOTE(chokobole): the fields are represented in montgomery form.\n  std::string ToString() const {\n    return absl::Substitute(\n        \"{alpha_g1: $0, beta_g1: $1, beta_g2: $2, gamma_g2: $3, delta_g1: $4, \"\n        \"delta_g2: $5}\",\n        alpha_g1->ToString(), beta_g1->ToString(), beta_g2->ToString(),\n        gamma_g2->ToString(), delta_g1->ToString(), delta_g2->ToString());\n  }\n};\n\n}  // namespace tachyon::circom\n\n#endif  // VENDORS_CIRCOM_CIRCOMLIB_ZKEY_VERIFYING_KEY_H_\n"
  },
  {
    "path": "vendors/circom/circomlib/zkey/zkey.cc",
    "content": "#include \"circomlib/zkey/zkey.h\"\n\nnamespace tachyon::circom::v1 {\n\nstd::string_view ZKeySectionTypeToString(ZKeySectionType type) {\n  switch (type) {\n    case ZKeySectionType::kHeader:\n      return \"Header\";\n    case ZKeySectionType::kHeaderGroth:\n      return \"HeaderGroth\";\n    case ZKeySectionType::kIC:\n      return \"IC\";\n    case ZKeySectionType::kCoefficients:\n      return \"Coefficients\";\n    case ZKeySectionType::kPointsA1:\n      return \"PointsA1\";\n    case ZKeySectionType::kPointsB1:\n      return \"PointsB1\";\n    case ZKeySectionType::kPointsB2:\n      return \"PointsB2\";\n    case ZKeySectionType::kPointsC1:\n      return \"PointsC1\";\n    case ZKeySectionType::kPointsH1:\n      return \"PointsH1\";\n    case ZKeySectionType::kContribution:\n      return \"Contribution\";\n  }\n  NOTREACHED();\n  return \"\";\n}\n\n}  // namespace tachyon::circom::v1\n"
  },
  {
    "path": "vendors/circom/circomlib/zkey/zkey.h",
    "content": "#ifndef VENDORS_CIRCOM_CIRCOMLIB_ZKEY_ZKEY_H_\n#define VENDORS_CIRCOM_CIRCOMLIB_ZKEY_ZKEY_H_\n\n#include <string.h>\n#include <sys/mman.h>\n\n#include <algorithm>\n#include <memory>\n#include <string>\n#include <utility>\n\n#include \"circomlib/base/modulus.h\"\n#include \"circomlib/base/sections.h\"\n#include \"circomlib/zkey/coefficient.h\"\n#include \"circomlib/zkey/proving_key.h\"\n#include \"tachyon/base/auto_reset.h\"\n#include \"tachyon/base/buffer/endian_auto_reset.h\"\n#include \"tachyon/base/buffer/read_only_buffer.h\"\n#include \"tachyon/base/files/bin_file.h\"\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/base/openmp_util.h\"\n#include \"tachyon/base/strings/string_util.h\"\n\nnamespace tachyon::circom {\nnamespace v1 {\n\ntemplate <typename Curve>\nstruct ZKey;\n\n}  // namespace v1\n\ntemplate <typename Curve>\nstruct ZKey {\n  using F = typename Curve::G1Curve::ScalarField;\n\n  explicit ZKey(base::BinFile bin_file) : bin_file(std::move(bin_file)) {}\n  virtual ~ZKey() = default;\n\n  virtual uint32_t GetVersion() const = 0;\n\n  virtual v1::ZKey<Curve>* ToV1() { return nullptr; }\n\n  virtual bool Read(const base::ReadOnlyBuffer& buffer) = 0;\n\n  virtual ProvingKey<Curve> GetProvingKey() const = 0;\n  virtual absl::Span<const Coefficient<F>> GetCoefficients() const = 0;\n  virtual size_t GetDomainSize() const = 0;\n  virtual size_t GetNumInstanceVariables() const = 0;\n  virtual size_t GetNumWitnessVariables() const = 0;\n\n  base::BinFile bin_file;\n};\n\nconstexpr char kZKeyMagic[4] = {'z', 'k', 'e', 'y'};\n\n// Return nullptr if the parser failed to parse.\ntemplate <typename Curve>\nstd::unique_ptr<ZKey<Curve>> ParseZKey(const base::FilePath& path,\n                                       bool use_mmap = true) {\n  base::BinFile bin_file;\n  CHECK(bin_file.Load(path, use_mmap));\n  if (use_mmap) {\n    PCHECK(madvise(bin_file.GetData(), bin_file.GetDataLength(),\n                   MADV_SEQUENTIAL) == 0);\n  }\n\n  base::ReadOnlyBuffer buffer = bin_file.ToReadOnlyBuffer();\n  buffer.set_endian(base::Endian::kLittle);\n  char magic[4];\n  uint32_t version;\n  if (!buffer.ReadMany(magic, &version)) return nullptr;\n  if (memcmp(magic, kZKeyMagic, 4) != 0) {\n    LOG(ERROR) << \"Invalid magic: \" << magic;\n    return nullptr;\n  }\n  std::unique_ptr<ZKey<Curve>> zkey;\n  if (version == 1) {\n    zkey.reset(new v1::ZKey<Curve>(std::move(bin_file)));\n    CHECK(zkey->ToV1()->Read(buffer));\n  } else {\n    LOG(ERROR) << \"Invalid version: \" << version;\n    return nullptr;\n  }\n  return zkey;\n}\n\nnamespace v1 {\n\nenum class ZKeySectionType : uint32_t {\n  kHeader = 0x1,\n  kHeaderGroth = 0x2,\n  kIC = 0x3,\n  kCoefficients = 0x4,\n  kPointsA1 = 0x5,\n  kPointsB1 = 0x6,\n  kPointsB2 = 0x7,\n  kPointsC1 = 0x8,\n  kPointsH1 = 0x9,\n  kContribution = 0xa,\n};\n\nstd::string_view ZKeySectionTypeToString(ZKeySectionType type);\n\nstruct ZKeyHeaderSection {\n  uint32_t prover_type;\n\n  bool operator==(const ZKeyHeaderSection& other) const {\n    return prover_type == other.prover_type;\n  }\n  bool operator!=(const ZKeyHeaderSection& other) const {\n    return prover_type != other.prover_type;\n  }\n\n  bool Read(const base::ReadOnlyBuffer& buffer) {\n    base::EndianAutoReset reset(buffer, base::Endian::kLittle);\n    if (!buffer.Read(&prover_type)) return false;\n    if (prover_type != 1) {\n      LOG(ERROR) << \"Unknown prover_type: \" << prover_type;\n      return false;\n    }\n    return true;\n  }\n\n  std::string ToString() const {\n    return absl::Substitute(\"{prover_type: $0}\", prover_type);\n  }\n};\n\ntemplate <typename Curve>\nstruct ZKeyHeaderGrothSection {\n  Modulus q;\n  Modulus r;\n  uint32_t num_vars;\n  uint32_t num_public_inputs;\n  uint32_t domain_size;\n  VerifyingKey<Curve> vkey;\n\n  bool operator==(const ZKeyHeaderGrothSection& other) const {\n    return q == other.q && r == other.r && num_vars == other.num_vars &&\n           num_public_inputs == other.num_public_inputs &&\n           domain_size == other.domain_size && vkey == other.vkey;\n  }\n  bool operator!=(const ZKeyHeaderGrothSection& other) const {\n    return !operator==(other);\n  }\n\n  bool Read(const base::ReadOnlyBuffer& buffer) {\n    base::EndianAutoReset reset(buffer, base::Endian::kLittle);\n    if (!q.Read(buffer)) return false;\n    if (!r.Read(buffer)) return false;\n    if (!buffer.ReadMany(&num_vars, &num_public_inputs, &domain_size))\n      return false;\n    return vkey.Read(buffer);\n  }\n\n  std::string ToString() const {\n    return absl::Substitute(\n        \"{q: $0, r: $1, num_vars: $2, num_public_inputs: $3, domain_size: $4, \"\n        \"vkey: $5}\",\n        q.ToString(), r.ToString(), num_vars, num_public_inputs, domain_size,\n        vkey.ToString());\n  }\n};\n\ntemplate <typename T>\nstruct CommitmentsSection {\n  absl::Span<T> commitments;\n\n  bool operator==(const CommitmentsSection& other) const {\n    return commitments == other.commitments;\n  }\n  bool operator!=(const CommitmentsSection& other) const {\n    return commitments != other.commitments;\n  }\n\n  bool Read(const base::ReadOnlyBuffer& buffer, uint32_t num_commitments) {\n    T* ptr;\n    if (!buffer.ReadPtr(&ptr, num_commitments)) return false;\n    commitments = {ptr, num_commitments};\n    return true;\n  }\n\n  // NOTE(chokobole): the fields are represented in montgomery form.\n  std::string ToString() const { return base::ContainerToString(commitments); }\n};\n\ntemplate <typename C>\nusing ICSection = CommitmentsSection<C>;\ntemplate <typename C>\nusing PointsA1Section = CommitmentsSection<C>;\ntemplate <typename C>\nusing PointsB1Section = CommitmentsSection<C>;\ntemplate <typename C>\nusing PointsB2Section = CommitmentsSection<C>;\ntemplate <typename C>\nusing PointsC1Section = CommitmentsSection<C>;\ntemplate <typename C>\nusing PointsH1Section = CommitmentsSection<C>;\n\ntemplate <typename F>\nstruct CoefficientsSection {\n  absl::Span<Coefficient<F>> coefficients;\n\n  bool operator==(const CoefficientsSection& other) const {\n    return coefficients == other.coefficients;\n  }\n  bool operator!=(const CoefficientsSection& other) const {\n    return !operator==(other);\n  }\n\n  bool Read(const base::ReadOnlyBuffer& buffer) {\n    uint32_t num_coefficients;\n    if (!buffer.Read(&num_coefficients)) return false;\n    Coefficient<F>* ptr;\n    if (!buffer.ReadPtr(&ptr, num_coefficients)) return false;\n    coefficients = {ptr, num_coefficients};\n\n    OMP_PARALLEL_FOR(size_t i = 0; i < coefficients.size(); ++i) {\n      coefficients[i].value =\n          F::FromMontgomery(coefficients[i].value.ToBigInt());\n    }\n\n    return true;\n  }\n\n  std::string ToString() const {\n    return absl::Substitute(\"{coefficients: $0}\",\n                            base::ContainerToString(coefficients));\n  }\n};\n\ntemplate <typename Curve>\nstruct ZKey : public circom::ZKey<Curve> {\n  using G1AffinePoint = typename Curve::G1Curve::AffinePoint;\n  using G2AffinePoint = typename Curve::G2Curve::AffinePoint;\n  using F = typename G1AffinePoint::ScalarField;\n\n  ZKeyHeaderSection header;\n  ZKeyHeaderGrothSection<Curve> header_groth;\n  ICSection<G1AffinePoint> ic;\n  CoefficientsSection<F> coefficients;\n  PointsA1Section<G1AffinePoint> points_a1;\n  PointsB1Section<G1AffinePoint> points_b1;\n  PointsB2Section<G2AffinePoint> points_b2;\n  PointsC1Section<G1AffinePoint> points_c1;\n  PointsH1Section<G1AffinePoint> points_h1;\n\n  explicit ZKey(base::BinFile bin_file)\n      : circom::ZKey<Curve>(std::move(bin_file)) {}\n\n  // circom::ZKey methods\n  uint32_t GetVersion() const override { return 1; }\n  ZKey<Curve>* ToV1() override { return this; }\n\n  bool Read(const base::ReadOnlyBuffer& buffer) override {\n    using BaseField = typename G1AffinePoint::BaseField;\n\n    base::AutoReset<bool> auto_reset(&base::Copyable<F>::s_is_in_montgomery,\n                                     true);\n    base::AutoReset<bool> auto_reset2(\n        &base::Copyable<BaseField>::s_is_in_montgomery, true);\n\n    Sections<ZKeySectionType> sections(buffer, &ZKeySectionTypeToString);\n    if (!sections.Read()) return false;\n\n    if (!sections.MoveTo(ZKeySectionType::kHeader)) return false;\n    if (!header.Read(buffer)) return false;\n\n    if (!sections.MoveTo(ZKeySectionType::kHeaderGroth)) return false;\n    if (!header_groth.Read(buffer)) return false;\n    uint32_t num_vars = header_groth.num_vars;\n    uint32_t num_public_inputs = header_groth.num_public_inputs;\n    uint32_t domain_size = header_groth.domain_size;\n\n    if (!sections.MoveTo(ZKeySectionType::kIC)) return false;\n    if (!ic.Read(buffer, num_public_inputs + 1)) return false;\n\n    if (!sections.MoveTo(ZKeySectionType::kCoefficients)) return false;\n    if (!coefficients.Read(buffer)) return false;\n\n    if (!sections.MoveTo(ZKeySectionType::kPointsA1)) return false;\n    if (!points_a1.Read(buffer, num_vars)) return false;\n\n    if (!sections.MoveTo(ZKeySectionType::kPointsB1)) return false;\n    if (!points_b1.Read(buffer, num_vars)) return false;\n\n    if (!sections.MoveTo(ZKeySectionType::kPointsB2)) return false;\n    if (!points_b2.Read(buffer, num_vars)) return false;\n\n    if (!sections.MoveTo(ZKeySectionType::kPointsC1)) return false;\n    if (!points_c1.Read(buffer, num_vars - num_public_inputs - 1)) return false;\n\n    if (!sections.MoveTo(ZKeySectionType::kPointsH1)) return false;\n    if (!points_h1.Read(buffer, domain_size)) return false;\n    return true;\n  }\n\n  ProvingKey<Curve> GetProvingKey() const override {\n    return {\n        header_groth.vkey,     ic.commitments,        points_a1.commitments,\n        points_b1.commitments, points_b2.commitments, points_c1.commitments,\n        points_h1.commitments,\n    };\n  }\n\n  absl::Span<const Coefficient<F>> GetCoefficients() const override {\n    return coefficients.coefficients;\n  }\n\n  size_t GetDomainSize() const override { return header_groth.domain_size; }\n\n  size_t GetNumInstanceVariables() const override {\n    return header_groth.num_public_inputs + 1;\n  }\n\n  size_t GetNumWitnessVariables() const override {\n    return header_groth.num_vars - header_groth.num_public_inputs - 1;\n  }\n\n  std::string ToString() const {\n    return absl::Substitute(\n        \"{header: $0, header_groth: $1, ic: $2, coefficients: $3, points_a1: \"\n        \"$4, points_b1: $5, points_b2: $6, points_c1: $7, points_h1: $8}\",\n        header.ToString(), header_groth.ToString(), ic.ToString(),\n        coefficients.ToString(), points_a1.ToString(), points_b1.ToString(),\n        points_b2.ToString(), points_c1.ToString(), points_h1.ToString());\n  }\n};\n\n}  // namespace v1\n}  // namespace tachyon::circom\n\n#endif  // VENDORS_CIRCOM_CIRCOMLIB_ZKEY_ZKEY_H_\n"
  },
  {
    "path": "vendors/circom/circomlib/zkey/zkey_unittest.cc",
    "content": "#include \"circomlib/zkey/zkey.h\"\n\n#include <utility>\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\n#include \"tachyon/math/elliptic_curves/bn/bn254/bn254.h\"\n\nnamespace tachyon::circom {\n\nnamespace {\n\nusing Curve = math::bn254::BN254Curve;\nusing G1AffinePoint = math::bn254::G1AffinePoint;\nusing G2AffinePoint = math::bn254::G2AffinePoint;\nusing F = math::bn254::Fr;\n\nclass ZKeyTest : public testing::Test {\n public:\n  static void SetUpTestSuite() { Curve::Init(); }\n};\n\nG1AffinePoint ToG1AffinePoint(std::string_view g1[2]) {\n  math::bn254::Fq x = *math::bn254::Fq::FromDecString(g1[0]);\n  math::bn254::Fq y = *math::bn254::Fq::FromDecString(g1[1]);\n  return {std::move(x), std::move(y)};\n}\n\nG2AffinePoint ToG2AffinePoint(std::string_view g2[2][2]) {\n  math::bn254::Fq2 x(*math::bn254::Fq::FromDecString(g2[0][0]),\n                     *math::bn254::Fq::FromDecString(g2[0][1]));\n  math::bn254::Fq2 y(*math::bn254::Fq::FromDecString(g2[1][0]),\n                     *math::bn254::Fq::FromDecString(g2[1][1]));\n  return {std::move(x), std::move(y)};\n}\n\nstruct CoefficientData {\n  uint32_t matrix;\n  uint32_t constraint;\n  uint32_t signal;\n  std::string_view coefficient;\n};\n\nCoefficient<F> ToCoefficient(const CoefficientData& data) {\n  return {\n      data.matrix,\n      data.constraint,\n      data.signal,\n      *F::FromDecString(data.coefficient),\n  };\n}\n\n}  // namespace\n\nTEST_F(ZKeyTest, Parse) {\n  std::unique_ptr<ZKey<Curve>> zkey =\n      ParseZKey<Curve>(base::FilePath(\"examples/multiplier_3.zkey\"));\n  ASSERT_TRUE(zkey);\n  ASSERT_EQ(zkey->GetVersion(), 1);\n\n  v1::ZKey<Curve>* v1_zkey = zkey->ToV1();\n\n  v1::ZKeyHeaderSection expected_header = {1};\n  EXPECT_EQ(v1_zkey->header, expected_header);\n\n  std::array<uint8_t, 32> fq_bytes =\n      math::bn254::FqConfig::kModulus.ToBytesLE();\n  std::array<uint8_t, 32> fr_bytes =\n      math::bn254::FrConfig::kModulus.ToBytesLE();\n\n  // clang-format off\n  std::string_view alpha_g1_str[2] = {\n    \"5700502584084766622350343367608487274977128430049880895783423261700075212785\",\n    \"9143870410831450591509938003078256759736333300521257694515214164265805259830\",\n  };\n  std::string_view beta_g1_str[2] = {\n    \"12699714711422499622362310820475830692566951228171954587615996781136226772367\",\n    \"2601999511749500018822665665362525344184434745926911293241192574303473253831\",\n  };\n  std::string_view beta_g2_str[2][2] = {\n    {\n      \"11780196173848324687642894328871430898972567583635494711927265792805257024861\",\n      \"3029614260803671687015271480824975868088527303860361358764452805565479529001\",\n    },\n    {\n      \"17817615377642575824268866714659516420384007262298492272608472268977629075434\",\n      \"10565581580493997556536063930500447170628763955833078597453719665182760199848\"\n    },\n  };\n  std::string_view gamma_g2_str[2][2] = {\n    {\n      \"10857046999023057135944570762232829481370756359578518086990519993285655852781\",\n      \"11559732032986387107991004021392285783925812861821192530917403151452391805634\",\n    },\n    {\n      \"8495653923123431417604973247489272438418190587263600148770280649306958101930\",\n      \"4082367875863433681332203403145435568316851327593401208105741076214120093531\"\n    },\n  };\n  std::string_view delta_g1_str[2] =  {\n    \"18121096455458648748006856505340317178704791872899059396361359566439114201168\",\n    \"1584219057669659447306711278235088033786171030532185363250775914928871374123\",\n  };\n  std::string_view delta_g2_str[2][2] = {\n    {\n      \"12202969132968262321607709426694195568617274504067880373401633817598633451917\",\n      \"3518609536734967363313514083213104545803320919611827958997148133314959258666\",\n    },\n    {\n      \"18833075355260917945174052299300691737626083696241282320643937004218902395077\",\n      \"9205653431456404075288921182558898955030763586613450941955464042050735041036\"\n    },\n  };\n  // clang-format on\n\n  G1AffinePoint alpha_g1 = ToG1AffinePoint(alpha_g1_str);\n  G1AffinePoint beta_g1 = ToG1AffinePoint(beta_g1_str);\n  G2AffinePoint beta_g2 = ToG2AffinePoint(beta_g2_str);\n  G2AffinePoint gamma_g2 = ToG2AffinePoint(gamma_g2_str);\n  G1AffinePoint delta_g1 = ToG1AffinePoint(delta_g1_str);\n  G2AffinePoint delta_g2 = ToG2AffinePoint(delta_g2_str);\n  v1::ZKeyHeaderGrothSection<Curve> expected_header_groth = {\n      Modulus{std::vector<uint8_t>(fq_bytes.begin(), fq_bytes.end())},\n      Modulus{std::vector<uint8_t>(fr_bytes.begin(), fr_bytes.end())},\n      6,\n      1,\n      4,\n      {\n          &alpha_g1,\n          &beta_g1,\n          &beta_g2,\n          &gamma_g2,\n          &delta_g1,\n          &delta_g2,\n      }};\n  EXPECT_EQ(v1_zkey->header_groth, expected_header_groth);\n\n  // clang-format off\n  std::string_view expected_ic_strs[][2] = {\n    {\n      \"1400989341879513116647759947859271187117391672677487101192308885590924596480\",\n      \"18827163924960691750679623127657074266908067481725903803154895122477837234033\",\n    },\n    {\n      \"21594466749489205217527764338982336503042047314074010288653645878163201627935\",\n      \"12389944772204356478767528982065842735667468360799583461061670022670871632383\",\n    }\n  };\n  // clang-format on\n  std::vector<G1AffinePoint> expected_ic = base::Map(\n      expected_ic_strs,\n      [](std::string_view g1_str[2]) { return ToG1AffinePoint(g1_str); });\n  v1::ICSection<G1AffinePoint> expected_ic_section = {\n      absl::MakeSpan(expected_ic)};\n  EXPECT_EQ(v1_zkey->ic, expected_ic_section);\n\n  // clang-format off\n  CoefficientData coefficient_datas[] = {\n    {0, 0, 2, \"21888242871839275222246405745257275088548364400416034343698204186575808495616\"},\n    {1, 0, 3, \"1\"},\n    {0, 1, 5, \"21888242871839275222246405745257275088548364400416034343698204186575808495616\"},\n    {1, 1, 4, \"1\"},\n    {0, 2, 0, \"1\"},\n    {0, 3, 1, \"1\"},\n  };\n  // clang-format on\n\n  std::vector<Coefficient<F>> expected_coefficients = base::Map(\n      coefficient_datas,\n      [](const CoefficientData& data) { return ToCoefficient(data); });\n  v1::CoefficientsSection<F> expected_coefficients_section = {\n      absl::MakeSpan(expected_coefficients)};\n  EXPECT_EQ(v1_zkey->coefficients, expected_coefficients_section);\n\n  // clang-format off\n  std::string_view expected_points_a1_strs[][2] = {\n    {\n      \"8858563469144920540528478490224638442973773873152551307670564100347093499191\",\n      \"7888214391937843930525848128254405915157714572978190674521564636068162216311\",\n    },\n    {\n      \"14537214592124271965353533016257772100455033778428577041971202446686849252644\",\n      \"2198766467867023896703420308951432042782623727887618971273865174145643356495\",\n    },\n    {\n      \"8437302598248383817148383036741547214048558400312301295747047351838256772123\",\n      \"4253086419746464003785043685439509391398040483296248505707498714848332192725\",\n    },\n    {\n      \"0\",\n      \"0\",\n    },\n    {\n      \"0\",\n      \"0\",\n    },\n    {\n      \"18141870587741836486360437684811661514896911334995841933942081072546739652377\",\n      \"11898889550822544273094627075076607374273361105699305622414170117806818640166\",\n    },\n  };\n  // clang-format on\n  std::vector<G1AffinePoint> expected_points_a1 = base::Map(\n      expected_points_a1_strs,\n      [](std::string_view g1_str[2]) { return ToG1AffinePoint(g1_str); });\n  v1::PointsA1Section<G1AffinePoint> expected_points_a1_section = {\n      absl::MakeSpan(expected_points_a1)};\n  EXPECT_EQ(v1_zkey->points_a1, expected_points_a1_section);\n\n  // clang-format off\n  std::string_view expected_points_b1_strs[][2] = {\n    {\n      \"0\",\n      \"0\",\n    },\n    {\n      \"0\",\n      \"0\",\n    },\n    {\n      \"0\",\n      \"0\",\n    },\n    {\n      \"8437302598248383817148383036741547214048558400312301295747047351838256772123\",\n      \"17635156452092811218461362059817765697298270674001575156981539179796894015858\",\n    },\n    {\n      \"18141870587741836486360437684811661514896911334995841933942081072546739652377\",\n      \"9989353321016730949151778670180667714422950051598518040274867776838407568417\",\n    },\n    {\n      \"0\",\n      \"0\",\n    },\n  };\n  // clang-format on\n  std::vector<G1AffinePoint> expected_points_b1 = base::Map(\n      expected_points_b1_strs,\n      [](std::string_view g1_str[2]) { return ToG1AffinePoint(g1_str); });\n  v1::PointsA1Section<G1AffinePoint> expected_points_b1_section = {\n      absl::MakeSpan(expected_points_b1)};\n  EXPECT_EQ(v1_zkey->points_b1, expected_points_b1_section);\n\n  // clang-format off\n  std::string_view expected_points_b2_strs[][2][2] = {\n    {\n      {\n        \"0\",\n        \"0\",\n      },\n      {\n        \"0\",\n        \"0\",\n      },\n    },\n    {\n      {\n        \"0\",\n        \"0\",\n      },\n      {\n        \"0\",\n        \"0\",\n      },\n    },\n    {\n      {\n        \"0\",\n        \"0\",\n      },\n      {\n        \"0\",\n        \"0\",\n      },\n    },\n    {\n      {\n        \"11802355142842158477844840276643950524651646845857959153292587217381565327694\",\n        \"457802140950752837652486610695137856486735570684796633854607481168528542690\",\n      },\n      {\n        \"20890171563279906566473411100997246109253767120687561732766386895834736963353\",\n        \"16651273739129079927357167917012486078894432847976493185750898267875291365102\",\n      },\n    },\n    {\n      {\n        \"10497384263581811947331014280742114350358633905325456855203962488034692371918\",\n        \"18830443503724104126054724199526976415213768169570049829023119362975529483862\",\n      },\n      {\n        \"2897811547300447900653975040323384022566235978103493011133012417217802784890\",\n        \"16901016173912215869936744693239612773536725857766261524800318448291024785326\",\n      },\n    },\n    {\n      {\n        \"0\",\n        \"0\",\n      },\n      {\n        \"0\",\n        \"0\",\n      },\n    },\n  };\n  // clang-format on\n  std::vector<G2AffinePoint> expected_points_b2 = base::Map(\n      expected_points_b2_strs,\n      [](std::string_view g2_str[2][2]) { return ToG2AffinePoint(g2_str); });\n  v1::PointsA1Section<G2AffinePoint> expected_points_b2_section = {\n      absl::MakeSpan(expected_points_b2)};\n  EXPECT_EQ(v1_zkey->points_b2, expected_points_b2_section);\n\n  // clang-format off\n  std::string_view expected_points_c1_strs[][2] = {\n    {\n      \"6484623682921116324480150495004051666793989100055782957602702403603681644087\",\n      \"9929291865986144258563515295092942842219006739962304434423595963929152482511\",\n    },\n    {\n      \"2428323497801148585616314929247100239345590148163609103036877009446204389916\",\n      \"1369420932117767430108173316323061012699395351388826295491040858566319703347\",\n    },\n    {\n      \"13708611801147211497962477788407562751837989814094039031176550253520745906453\",\n      \"18151634040197625351146955912693877471406025170780331525940368525891263003135\",\n    },\n    {\n      \"18284461151151484053721771333035891989697029546383442693996002288554315091425\",\n      \"16368214353654036882062732785064467942017556803959036709508171804841339585293\",\n    },\n  };\n  // clang-format on\n  std::vector<G1AffinePoint> expected_points_c1 = base::Map(\n      expected_points_c1_strs,\n      [](std::string_view g1_str[2]) { return ToG1AffinePoint(g1_str); });\n  v1::PointsA1Section<G1AffinePoint> expected_points_c1_section = {\n      absl::MakeSpan(expected_points_c1)};\n  EXPECT_EQ(v1_zkey->points_c1, expected_points_c1_section);\n\n  // clang-format off\n  std::string_view expected_points_h1_strs[][2] = {\n    {\n      \"14491578304961494983864577860005130648083128241617887000567655109826889517765\",\n      \"747219666158167233177659053461241136834613714217214833065101750371763762297\",\n    },\n    {\n      \"18146981344323077863601561933187745037217273647724943548414954460732389867565\",\n      \"12817730263653070328990536695886808120537471796272335985833677017167603721628\",\n    },\n    {\n      \"4366845324910879958335909021287873980685276473035323564144861870453367362588\",\n      \"15317533781337899355741367900150169453079806490427679757823792291054041331892\",\n    },\n    {\n      \"2710629677537908437939962149843601091625518295461558128309313181856887222076\",\n      \"5586033426926327725348881354712158558954133315341909946743947206034841045334\",\n    },\n  };\n  // clang-format on\n  std::vector<G1AffinePoint> expected_points_h1 = base::Map(\n      expected_points_h1_strs,\n      [](std::string_view g1_str[2]) { return ToG1AffinePoint(g1_str); });\n  v1::PointsA1Section<G1AffinePoint> expected_points_h1_section = {\n      absl::MakeSpan(expected_points_h1)};\n  EXPECT_EQ(v1_zkey->points_h1, expected_points_h1_section);\n}\n\n}  // namespace tachyon::circom\n"
  },
  {
    "path": "vendors/circom/examples/BUILD.bazel",
    "content": "load(\"@kroma_network_rules_circom//:build_defs.bzl\", \"compile_circuit\")\nload(\"@kroma_network_tachyon//bazel:tachyon_cc.bzl\", \"tachyon_cc_binary\")\nload(\"//:build_defs.bzl\", \"witness_gen_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\n    \"adder.zkey\",\n    \"multiplier_3.zkey\",\n])\n\nPRIME = \"bn128\"\n\ncompile_circuit(\n    name = \"compile_adder\",\n    main = \"adder.circom\",\n    prime = PRIME,\n    deps = [\n        \"@kroma_network_circomlib//circuits:binsum\",\n        \"@kroma_network_circomlib//circuits:bitify\",\n    ],\n)\n\nwitness_gen_library(\n    name = \"gen_witness_adder\",\n    gendep = \":compile_adder\",\n    prime = PRIME,\n)\n\ncompile_circuit(\n    name = \"compile_multiplier_3\",\n    srcs = [\n        \"multiplier_2.circom\",\n        \"multiplier_n.circom\",\n    ],\n    main = \"multiplier_3.circom\",\n    prime = PRIME,\n)\n\nwitness_gen_library(\n    name = \"gen_witness_multiplier_3\",\n    gendep = \":compile_multiplier_3\",\n    prime = PRIME,\n)\n\ntachyon_cc_binary(\n    name = \"compute_witness_multiplier_3\",\n    srcs = [\"compute_witness_multiplier_3.cc\"],\n    deps = [\":gen_witness_multiplier_3\"],\n)\n"
  },
  {
    "path": "vendors/circom/examples/adder.circom",
    "content": "pragma circom 2.0.0;\n\ninclude \"external/kroma_network_circomlib/circuits/bitify.circom\";\ninclude \"external/kroma_network_circomlib/circuits/binsum.circom\";\n\ntemplate Adder() {\n    signal input a;\n    signal input b;\n    signal output out;\n\n    var i;\n\n    component n2ba = Num2Bits(32);\n    component n2bb = Num2Bits(32);\n    component sum = BinSum(32, 2);\n    component b2n = Bits2Num(32);\n\n    n2ba.in <== a;\n    n2bb.in <== b;\n\n    for (i = 0; i < 32; i++) {\n        sum.in[0][i] <== n2ba.out[i];\n        sum.in[1][i] <== n2bb.out[i];\n    }\n\n    for (i = 0; i < 32; i++) {\n        b2n.in[i] <== sum.out[i];\n    }\n\n    out <== b2n.out;\n}\n\ncomponent main = Adder();\n"
  },
  {
    "path": "vendors/circom/examples/compute_witness_multiplier_3.cc",
    "content": "#include <memory>\n\n#include \"circomlib/generated/common/calcwit.hpp\"\n\nnamespace tachyon {\n\nint RealMain(int argc, char **argv) {\n  std::unique_ptr<Circom_Circuit> circuit(\n      loadCircuit(\"examples/multiplier_3_cpp/multiplier_3.dat\"));\n  std::unique_ptr<Circom_CalcWit> ctx(new Circom_CalcWit(circuit.get()));\n  absl::flat_hash_map<std::string, std::vector<FrElement>> witness;\n  std::vector<FrElement> in(3);\n  Fr_str2element(&in[0], \"3\", 10);\n  Fr_str2element(&in[1], \"4\", 10);\n  Fr_str2element(&in[2], \"5\", 10);\n  witness[\"in\"] = std::move(in);\n  loadWitness(ctx.get(), witness);\n  writeBinWitness(ctx.get(), \"multiplier_3.wtns\");\n  return 0;\n}\n\n}  // namespace tachyon\n\nint main(int argc, char **argv) { return tachyon::RealMain(argc, argv); }\n"
  },
  {
    "path": "vendors/circom/examples/multiplier_2.circom",
    "content": "pragma circom 2.0.0;\n\ntemplate Multiplier2() {\n  signal input in1;\n  signal input in2;\n  signal output out;\n\n  out <== in1 * in2;\n}\n"
  },
  {
    "path": "vendors/circom/examples/multiplier_3.circom",
    "content": "pragma circom 2.0.0;\n\ninclude \"multiplier_n.circom\";\n\ncomponent main = MultiplierN(3);\n"
  },
  {
    "path": "vendors/circom/examples/multiplier_n.circom",
    "content": "pragma circom 2.0.0;\n\ninclude \"multiplier_2.circom\";\n\ntemplate MultiplierN(N) {\n  signal input in[N];\n  signal output out;\n  component comp[N-1];\n\n  for(var i = 0; i < N-1; i++){\n      comp[i] = Multiplier2();\n  }\n  comp[0].in1 <== in[0];\n  comp[0].in2 <== in[1];\n  for(var i = 0; i < N-2; i++){\n      comp[i+1].in1 <== comp[i].out;\n      comp[i+1].in2 <== in[i+2];\n  }\n  out <== comp[N-2].out;\n}\n"
  },
  {
    "path": "vendors/circom/prover_main.cc",
    "content": "#include \"absl/strings/substitute.h\"\n\n#include \"circomlib/circuit/quadratic_arithmetic_program.h\"\n#include \"circomlib/json/groth16_proof.h\"\n#include \"circomlib/json/json.h\"\n#include \"circomlib/json/prime_field.h\"\n#include \"circomlib/wtns/wtns.h\"\n#include \"circomlib/zkey/zkey.h\"\n#include \"tachyon/base/console/iostream.h\"\n#include \"tachyon/base/files/file_path_flag.h\"\n#include \"tachyon/base/flag/flag_parser.h\"\n#include \"tachyon/base/profiler.h\"\n#include \"tachyon/base/time/time.h\"\n#include \"tachyon/math/elliptic_curves/bls12/bls12_381/bls12_381.h\"\n#include \"tachyon/math/elliptic_curves/bn/bn254/bn254.h\"\n#include \"tachyon/math/polynomials/univariate/univariate_evaluation_domain_factory.h\"\n#include \"tachyon/zk/r1cs/groth16/prove.h\"\n#include \"tachyon/zk/r1cs/groth16/verify.h\"\n\n#if TACHYON_CUDA\n#include \"tachyon/math/polynomials/univariate/icicle/icicle_ntt_holder.h\"\n#endif\n\nnamespace tachyon {\n\nenum class Curve {\n  kBN254,\n  kBLS12_381,\n};\n\nnamespace base {\n\ntemplate <>\nclass FlagValueTraits<Curve> {\n public:\n  static bool ParseValue(std::string_view input, Curve* value,\n                         std::string* reason) {\n    if (input == \"bn254\") {\n      *value = Curve::kBN254;\n    } else if (input == \"bls12_381\") {\n      *value = Curve::kBLS12_381;\n    } else {\n      *reason = absl::Substitute(\"Unknown curve: $0\", input);\n      return false;\n    }\n    return true;\n  }\n};\n\n}  // namespace base\n\nnamespace circom {\n\nstruct ProverTime {\n  base::TimeDelta total;\n  base::TimeDelta max;\n  size_t num_runs = 0;\n\n  void Add(base::TimeDelta delta) {\n    ++num_runs;\n    total += delta;\n    max = std::max(delta, max);\n  }\n\n  base::TimeDelta GetAvg() const {\n    CHECK_NE(num_runs, size_t{0});\n    return total / num_runs;\n  }\n};\n\nstruct CreateProofOptions {\n  bool no_zk;\n  bool no_use_mmap;\n  bool verify;\n  size_t num_runs;\n#if TACHYON_CUDA\n  bool disable_fast_twiddles_mode;\n#endif\n};\n\ntemplate <typename Curve>\nvoid CreateProof(const base::FilePath& zkey_path,\n                 const base::FilePath& witness_path,\n                 const base::FilePath& proof_path,\n                 const base::FilePath& public_path,\n                 const CreateProofOptions& options) {\n  using F = typename Curve::G1Curve::ScalarField;\n  using Domain = math::UnivariateEvaluationDomain<F, SIZE_MAX>;\n\n  Curve::Init();\n\n  base::TimeTicks start = base::TimeTicks::Now();\n  std::cout << \"Start parsing zkey\" << std::endl;\n  std::unique_ptr<ZKey<Curve>> zkey =\n      ParseZKey<Curve>(zkey_path, !options.no_use_mmap);\n  CHECK(zkey);\n  zk::r1cs::groth16::ProvingKey<Curve> proving_key =\n      zkey->GetProvingKey().ToNativeProvingKey();\n  absl::Span<const Coefficient<F>> coefficients = zkey->GetCoefficients();\n\n  base::TimeTicks end = base::TimeTicks::Now();\n  std::cout << \"Time taken for parsing zkey: \" << end - start << std::endl;\n  start = end;\n\n  std::cout << \"Start parsing witness\" << std::endl;\n  std::unique_ptr<Wtns<F>> wtns =\n      ParseWtns<F>(witness_path, !options.no_use_mmap);\n  CHECK(wtns);\n\n  end = base::TimeTicks::Now();\n  std::cout << \"Time taken for parsing witness: \" << end - start << std::endl;\n  start = end;\n\n  zk::r1cs::groth16::Proof<Curve> proof;\n  absl::Span<const F> full_assignments = wtns->GetWitnesses();\n  std::unique_ptr<Domain> domain = Domain::Create(zkey->GetDomainSize());\n#if TACHYON_CUDA\n  std::cout << \"Start initializing Icicle NTT domain\" << std::endl;\n  math::IcicleNTTHolder<F> icicle_ntt_holder =\n      math::IcicleNTTHolder<F>::Create();\n  // NOTE(chokobole): For |domain->size()| less than 8, it's very slow to\n  // initialize the domain of |IcicleNTT|.\n  if (domain->size() >= 8) {\n    math::IcicleNTTOptions ntt_options;\n    ntt_options.fast_twiddles_mode = !options.disable_fast_twiddles_mode;\n    CHECK(icicle_ntt_holder->Init(domain->group_gen(), ntt_options));\n    domain->set_icicle(&icicle_ntt_holder);\n  }\n\n  end = base::TimeTicks::Now();\n  std::cout << \"Time taken for initializing Icicle NTT domain: \" << end - start\n            << std::endl;\n  start = end;\n#endif\n  std::cout << \"Start proving\" << std::endl;\n  ProverTime prover_time;\n  size_t num_instance_variables = zkey->GetNumInstanceVariables();\n  for (size_t i = 0; i < options.num_runs; ++i) {\n    std::vector<F> h_evals =\n        QuadraticArithmeticProgram<F>::WitnessMapFromMatrices(\n            domain.get(), coefficients, full_assignments);\n    if (options.no_zk) {\n      proof = zk::r1cs::groth16::CreateProofWithAssignmentNoZK(\n          proving_key, absl::MakeConstSpan(h_evals),\n          full_assignments.subspan(1, num_instance_variables - 1),\n          full_assignments.subspan(num_instance_variables),\n          full_assignments.subspan(1));\n    } else {\n      proof = zk::r1cs::groth16::CreateProofWithAssignmentZK(\n          proving_key, absl::MakeConstSpan(h_evals),\n          full_assignments.subspan(1, num_instance_variables - 1),\n          full_assignments.subspan(num_instance_variables),\n          full_assignments.subspan(1));\n    }\n\n    end = base::TimeTicks::Now();\n    base::TimeDelta delta = end - start;\n    std::cout << \"Time taken for proving #\" << i << \": \" << delta << std::endl;\n    prover_time.Add(delta);\n    start = end;\n  }\n  std::cout << \"Avg time taken for proving: \" << prover_time.GetAvg()\n            << std::endl;\n  std::cout << \"Max time taken for proving: \" << prover_time.max << std::endl;\n\n  absl::Span<const F> public_inputs =\n      full_assignments.subspan(1, num_instance_variables - 1);\n  if (options.verify) {\n    std::cout << \"Start verifying\" << std::endl;\n    zk::r1cs::groth16::PreparedVerifyingKey<Curve> prepared_verifying_key =\n        std::move(proving_key).TakeVerifyingKey().ToPreparedVerifyingKey();\n    CHECK(zk::r1cs::groth16::VerifyProof(prepared_verifying_key, proof,\n                                         public_inputs))\n        << \"If you see this error with `--config cuda`, it means your GPU \"\n           \"doesn't have enough RAM for Icicle. Try running it with a GPU with \"\n           \"more RAM.\";\n    end = base::TimeTicks::Now();\n    std::cout << \"Time taken for verifying: \" << end - start << std::endl;\n  }\n\n  CHECK(WriteToJson(proof, proof_path));\n  std::cout << \"Proof is saved to \\\"\" << proof_path << \"\\\"\" << std::endl;\n  CHECK(WriteToJson(public_inputs, public_path));\n  std::cout << \"Public input is saved to \\\"\" << public_path << \"\\\"\"\n            << std::endl;\n}\n\n}  // namespace circom\n\nint RealMain(int argc, char** argv) {\n  base::FlagParser parser;\n  base::FilePath zkey_path;\n  base::FilePath witness_path;\n  base::FilePath proof_path;\n  base::FilePath public_path;\n  base::FilePath trace_path;\n  Curve curve;\n  circom::CreateProofOptions options;\n  parser.AddFlag<base::FilePathFlag>(&zkey_path)\n      .set_name(\"zkey\")\n      .set_help(\"The path to zkey file\");\n  parser.AddFlag<base::FilePathFlag>(&witness_path)\n      .set_name(\"wtns\")\n      .set_help(\"The path to wtns file\");\n  parser.AddFlag<base::FilePathFlag>(&proof_path)\n      .set_name(\"proof\")\n      .set_help(\"The path to proof json\");\n  parser.AddFlag<base::FilePathFlag>(&public_path)\n      .set_name(\"public\")\n      .set_help(\"The path to public json\");\n  parser.AddFlag<base::FilePathFlag>(&trace_path)\n      .set_long_name(\"--trace_path\")\n      .set_default_value(base::FilePath(\"/tmp/circom.perfetto-trace\"))\n      .set_help(\"The path to generate trace file\");\n  parser.AddFlag<base::Flag<Curve>>(&curve)\n      .set_long_name(\"--curve\")\n      .set_default_value(Curve::kBN254)\n      .set_help(\n          \"The curve type among ('bn254', 'bls12_381'), by default 'bn254'\");\n  parser.AddFlag<base::BoolFlag>(&options.no_zk)\n      .set_long_name(\"--no_zk\")\n      .set_default_value(false)\n      .set_help(\n          \"Create proof without zk. By default zk is enabled. Use this flag in \"\n          \"case you want to compare the proof with rapidsnark.\");\n  parser.AddFlag<base::BoolFlag>(&options.no_use_mmap)\n      .set_long_name(\"--no_use_mmap\")\n      .set_default_value(false)\n      .set_help(\n          \"Create proof without mmap(2). By default, mmap(2) is enabled, \"\n          \"offering faster proof generation at the cost of increased memory \"\n          \"usage due to the memory mapped file. Use this flag if you want to \"\n          \"use less memory.\");\n  parser.AddFlag<base::BoolFlag>(&options.verify)\n      .set_long_name(\"--verify\")\n      .set_default_value(false)\n      .set_help(\n          \"Verify the proof. By default verify is disabled. Use this flag \"\n          \"to verify the proof with the public inputs.\");\n  parser.AddFlag<base::Flag<size_t>>(&options.num_runs)\n      .set_short_name(\"-n\")\n      .set_long_name(\"--num_runs\")\n      .set_default_value(1)\n      .set_help(\"The number of times to run the proof generation\");\n#if TACHYON_CUDA\n  parser.AddFlag<base::BoolFlag>(&options.disable_fast_twiddles_mode)\n      .set_long_name(\"--disable_fast_twiddles_mode\")\n      .set_default_value(false)\n      .set_help(\n          \"Disables fast twiddle mode on Icicle NTT domain initialization.\");\n#endif\n\n  std::string error;\n  if (!parser.Parse(argc, argv, &error)) {\n    tachyon_cerr << error << std::endl;\n    return 1;\n  }\n#if TACHYON_CUDA\n  if (!options.verify) {\n    std::cout << \"Icicle MSM/NTT may produce incorrect results without any \"\n                 \"error if the polynomial degree is too high relative to the \"\n                 \"GPU RAM size. This may be resolved in the future release of \"\n                 \"the Icicle. In the meantime, please run with --verify to \"\n                 \"ensure the accuracy of the results.\"\n              << std::endl;\n  }\n\n#endif\n  if (options.num_runs == 0) {\n    tachyon_cerr << \"num_runs should be positive\" << std::endl;\n    return 1;\n  }\n\n  base::Profiler profiler({trace_path});\n  profiler.Init();\n  profiler.Start();\n\n  switch (curve) {\n    case Curve::kBN254:\n      circom::CreateProof<math::bn254::BN254Curve>(\n          zkey_path, witness_path, proof_path, public_path, options);\n      break;\n    case Curve::kBLS12_381:\n      circom::CreateProof<math::bls12_381::BLS12_381Curve>(\n          zkey_path, witness_path, proof_path, public_path, options);\n      break;\n  }\n\n  return 0;\n}\n\n}  // namespace tachyon\n\nint main(int argc, char** argv) { return tachyon::RealMain(argc, argv); }\n"
  },
  {
    "path": "vendors/plonky3/BUILD.bazel",
    "content": "load(\"@crate_index//:defs.bzl\", \"aliases\", \"all_crate_deps\")\nload(\"@cxx.rs//tools/bazel:rust_cxx_bridge.bzl\", \"rust_cxx_bridge\")\nload(\"@local_config_cuda//cuda:build_defs.bzl\", \"if_cuda\")\nload(\"//bazel:tachyon.bzl\", \"if_gpu_is_configured\", \"if_has_openmp_on_macos\")\nload(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_openmp_linkopts\")\nload(\"//bazel:tachyon_rust.bzl\", \"tachyon_rust_library\", \"tachyon_rust_test\")\n\nFEATURES = if_gpu_is_configured([\"gpu\"])\n\ntachyon_rust_library(\n    name = \"plonky3\",\n    srcs = glob([\"src/**/*.rs\"]),\n    aliases = aliases(),\n    crate_features = FEATURES,\n    proc_macro_deps = all_crate_deps(proc_macro = True),\n    deps = all_crate_deps(normal = True) + [\n        \":baby_bear_cxx_bridge\",\n        \":baby_bear_row_major_matrix\",\n        \"//tachyon/rs:tachyon_rs\",\n    ],\n)\n\n# NOTE(chokobole): This attribute could be added to `plonky3_test`,\n# but this approach doesn't work when compiling with nvcc.\n# rustc_flags = if_has_openmp([\"-lgomp\"]),\ncc_library(\n    name = \"openmp\",\n    linkopts = tachyon_openmp_linkopts(),\n)\n\ntachyon_rust_test(\n    name = \"plonky3_test\",\n    size = \"small\",\n    aliases = aliases(),\n    crate = \":plonky3\",\n    crate_features = FEATURES,\n    proc_macro_deps = all_crate_deps(proc_macro_dev = True),\n    deps = all_crate_deps(normal_dev = True) + [\n        \":openmp\",\n        \"@local_config_gmp//:gmp\",\n    ] + if_cuda([\n        \"@local_config_cuda//cuda:cudart_static\",\n    ]) + if_has_openmp_on_macos([\n        \"@local_config_omp//:omp\",\n    ]),\n)\n\nrust_cxx_bridge(\n    name = \"baby_bear_cxx_bridge\",\n    src = \"src/baby_bear.rs\",\n    args = if_gpu_is_configured([\n        '--cfg=feature=\"gpu\"',\n    ]),\n    deps = [\":baby_bear_api_hdrs\"],\n)\n\ntachyon_cc_library(\n    name = \"baby_bear_api_hdrs\",\n    hdrs = [\"include/baby_bear_row_major_matrix.h\"],\n    deps = [\n        \"//tachyon/c/math/matrix:baby_bear_row_major_matrix\",\n        \"@cxx.rs//:core\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"baby_bear_row_major_matrix\",\n    srcs = [\"src/baby_bear_row_major_matrix.cc\"],\n    deps = [\n        \":baby_bear_api_hdrs\",\n        \":baby_bear_cxx_bridge/include\",\n    ],\n)\n"
  },
  {
    "path": "vendors/plonky3/Cargo.toml",
    "content": "[package]\nname = \"tachyon_plonky3\"\nversion = \"0.0.1\"\nauthors = [\"The Tachyon Authors <tachyon-discuss@kroma.network>\"]\nedition = \"2021\"\nrust-version = \"1.56.1\"\ndescription = \"\"\"\nPlonky3 unittest based on tachyon\n\"\"\"\nlicense = \"MIT OR Apache-2.0\"\nrepository = \"https://github.com/kroma-network/tachyon\"\nreadme = \"README.md\"\ncategories = [\"cryptography\"]\nkeywords = [\"tachyon\", \"plonky3\", \"proofs\", \"zkp\", \"zkSNARKs\"]\npublish = false\n\n[dependencies]\ncxx = \"1.0\"\np3-baby-bear = \"0.1.3-succinct\"\np3-field = \"0.1.3-succinct\"\np3-matrix = \"0.1.3-succinct\"\ntachyon_rs = { path = \"../../tachyon/rs\" }\n\n[features]\ndefault = []\ngpu = []\n"
  },
  {
    "path": "vendors/plonky3/include/baby_bear_row_major_matrix.h",
    "content": "#ifndef VENDORS_PLONKY3_INCLUDE_BABY_BEAR_ROW_MAJOR_MATRIX_H_\n#define VENDORS_PLONKY3_INCLUDE_BABY_BEAR_ROW_MAJOR_MATRIX_H_\n\n#include <memory>\n\n#include \"rust/cxx.h\"\n\n#include \"tachyon/c/math/matrix/baby_bear_row_major_matrix.h\"\n\nnamespace tachyon::plonky3_api::baby_bear {\n\nstruct TachyonBabyBear;\n\nclass RowMajorMatrix {\n public:\n  RowMajorMatrix(TachyonBabyBear* ptr, size_t rows, size_t cols);\n  explicit RowMajorMatrix(tachyon_baby_bear_row_major_matrix* matrix)\n      : matrix_(matrix) {}\n  RowMajorMatrix(const RowMajorMatrix& other) = delete;\n  RowMajorMatrix& operator=(const RowMajorMatrix& other) = delete;\n  ~RowMajorMatrix();\n\n  size_t get_rows() const;\n  size_t get_cols() const;\n  const TachyonBabyBear* get_const_data_ptr() const;\n  std::unique_ptr<RowMajorMatrix> clone() const;\n\n private:\n  tachyon_baby_bear_row_major_matrix* matrix_;\n};\n\nstd::unique_ptr<RowMajorMatrix> new_row_major_matrix(TachyonBabyBear* ptr,\n                                                     size_t rows, size_t cols);\n\n}  // namespace tachyon::plonky3_api::baby_bear\n\n#endif  // VENDORS_PLONKY3_INCLUDE_BABY_BEAR_ROW_MAJOR_MATRIX_H_\n"
  },
  {
    "path": "vendors/plonky3/src/baby_bear.rs",
    "content": "use core::{iter, slice};\nuse std::{fmt::Debug, marker::PhantomData, ops::Deref};\n\nuse p3_baby_bear::BabyBear;\nuse p3_field::PackedValue;\nuse p3_matrix::{\n    dense::{DenseMatrix, RowMajorMatrixView},\n    Matrix,\n};\nuse tachyon_rs::math::finite_fields::baby_bear::BabyBear as TachyonBabyBearImpl;\n\npub struct TachyonBabyBear(pub TachyonBabyBearImpl);\n\n#[cxx::bridge(namespace = \"tachyon::plonky3_api::baby_bear\")]\npub mod ffi {\n    extern \"Rust\" {\n        type TachyonBabyBear;\n    }\n\n    unsafe extern \"C++\" {\n        include!(\"vendors/plonky3/include/baby_bear_row_major_matrix.h\");\n\n        type RowMajorMatrix;\n\n        unsafe fn new_row_major_matrix(\n            data_ptr: *mut TachyonBabyBear,\n            rows: usize,\n            cols: usize,\n        ) -> UniquePtr<RowMajorMatrix>;\n        fn get_rows(self: &RowMajorMatrix) -> usize;\n        fn get_cols(self: &RowMajorMatrix) -> usize;\n        unsafe fn get_const_data_ptr(self: &RowMajorMatrix) -> *const TachyonBabyBear;\n        fn clone(&self) -> UniquePtr<RowMajorMatrix>;\n    }\n}\n\npub struct RowMajorMatrix<F> {\n    inner: cxx::UniquePtr<ffi::RowMajorMatrix>,\n    _marker: PhantomData<F>,\n}\n\nunsafe impl Send for ffi::RowMajorMatrix {}\nunsafe impl Sync for ffi::RowMajorMatrix {}\n\nimpl<F> Clone for RowMajorMatrix<F> {\n    fn clone(&self) -> Self {\n        Self {\n            inner: self.inner.clone(),\n            _marker: PhantomData,\n        }\n    }\n}\n\nimpl<F> Debug for RowMajorMatrix<F> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"RowMajorMatrix\").finish()\n    }\n}\n\nimpl<F> RowMajorMatrix<F> {\n    pub fn new(data_ptr: *mut BabyBear, rows: usize, cols: usize) -> RowMajorMatrix<F> {\n        RowMajorMatrix {\n            inner: unsafe { ffi::new_row_major_matrix(std::mem::transmute(data_ptr), rows, cols) },\n            _marker: PhantomData,\n        }\n    }\n\n    pub fn get_cols(&self) -> usize {\n        self.inner.get_cols()\n    }\n\n    pub fn get_rows(&self) -> usize {\n        self.inner.get_rows()\n    }\n\n    pub fn get_const_data_ptr(&self) -> *const F {\n        unsafe { std::mem::transmute(self.inner.get_const_data_ptr()) }\n    }\n}\n\nimpl<F: Clone + Send + Sync> RowMajorMatrix<F> {\n    pub fn split_rows(&self, r: usize) -> (RowMajorMatrixView<F>, RowMajorMatrixView<F>) {\n        let rows = self.get_rows();\n        let cols = self.get_cols();\n        let row = unsafe { std::slice::from_raw_parts(self.get_const_data_ptr(), rows * cols) };\n        let (lo, hi) = row.split_at(r * cols);\n        (DenseMatrix::new(lo, cols), DenseMatrix::new(hi, cols))\n    }\n}\n\nimpl<F: Clone + Send + Sync> Matrix<F> for RowMajorMatrix<F> {\n    fn width(&self) -> usize {\n        self.get_cols()\n    }\n\n    fn height(&self) -> usize {\n        self.get_rows()\n    }\n\n    type Row<'a> = iter::Cloned<slice::Iter<'a, F>> where Self: 'a;\n    fn row(&self, r: usize) -> Self::Row<'_> {\n        let slice = unsafe {\n            std::slice::from_raw_parts(\n                self.get_const_data_ptr()\n                    .offset((r * self.width()) as isize),\n                self.width(),\n            )\n        };\n        slice.iter().cloned()\n    }\n\n    fn row_slice(&self, r: usize) -> impl Deref<Target = [F]> {\n        unsafe {\n            std::slice::from_raw_parts(\n                self.get_const_data_ptr()\n                    .offset((r * self.width()) as isize),\n                self.width(),\n            )\n        }\n    }\n\n    fn to_row_major_matrix(self) -> p3_matrix::dense::RowMajorMatrix<F>\n    where\n        Self: Sized,\n        F: Clone,\n    {\n        let size = self.width() * self.height();\n        let mut values = Vec::with_capacity(size);\n        unsafe {\n            std::ptr::copy(self.get_const_data_ptr(), values.as_mut_ptr(), size);\n            values.set_len(size);\n        }\n        p3_matrix::dense::RowMajorMatrix::<F>::new(values, self.get_cols())\n    }\n\n    fn horizontally_packed_row<'a, P>(\n        &'a self,\n        r: usize,\n    ) -> (impl Iterator<Item = P>, impl Iterator<Item = F>)\n    where\n        P: PackedValue<Value = F>,\n        F: Clone + 'a,\n    {\n        let buf = unsafe {\n            std::slice::from_raw_parts(\n                self.get_const_data_ptr()\n                    .offset((r * self.width()) as isize),\n                self.width(),\n            )\n        };\n        let (packed, sfx) = P::pack_slice_with_suffix(buf);\n        (packed.iter().cloned(), sfx.iter().cloned())\n    }\n}\n"
  },
  {
    "path": "vendors/plonky3/src/baby_bear_row_major_matrix.cc",
    "content": "#include \"vendors/plonky3/include/baby_bear_row_major_matrix.h\"\n\nnamespace tachyon::plonky3_api::baby_bear {\n\nRowMajorMatrix::RowMajorMatrix(TachyonBabyBear* ptr, size_t rows, size_t cols)\n    : matrix_(tachyon_baby_bear_row_major_matrix_create(\n          reinterpret_cast<tachyon_baby_bear*>(ptr), rows, cols)) {}\n\nRowMajorMatrix::~RowMajorMatrix() {\n  tachyon_baby_bear_row_major_matrix_destroy(matrix_);\n}\n\nsize_t RowMajorMatrix::get_rows() const {\n  return tachyon_baby_bear_row_major_matrix_get_rows(matrix_);\n}\n\nsize_t RowMajorMatrix::get_cols() const {\n  return tachyon_baby_bear_row_major_matrix_get_cols(matrix_);\n}\n\nconst TachyonBabyBear* RowMajorMatrix::get_const_data_ptr() const {\n  return reinterpret_cast<const TachyonBabyBear*>(\n      tachyon_baby_bear_row_major_matrix_get_const_data_ptr(matrix_));\n}\n\nstd::unique_ptr<RowMajorMatrix> RowMajorMatrix::clone() const {\n  return std::make_unique<RowMajorMatrix>(\n      tachyon_baby_bear_row_major_matrix_clone(matrix_));\n}\n\nstd::unique_ptr<RowMajorMatrix> new_row_major_matrix(TachyonBabyBear* ptr,\n                                                     size_t rows, size_t cols) {\n  return std::make_unique<RowMajorMatrix>(ptr, rows, cols);\n}\n\n}  // namespace tachyon::plonky3_api::baby_bear\n"
  },
  {
    "path": "vendors/plonky3/src/lib.rs",
    "content": "pub mod baby_bear;\npub mod row_major_matrix;\n"
  },
  {
    "path": "vendors/plonky3/src/row_major_matrix.rs",
    "content": "#[cfg(test)]\nmod test {\n    use p3_baby_bear::BabyBear;\n    use p3_field::{AbstractField, Field};\n    use p3_matrix::{dense::RowMajorMatrix, Matrix};\n\n    use crate::baby_bear::RowMajorMatrix as TachyonRowMajorMatrix;\n\n    #[test]\n    fn test_row_major_matrix() {\n        let mut data = (0..30)\n            .map(|i| BabyBear::from_canonical_u32(i))\n            .collect::<Vec<_>>();\n        let tachyon_matrix: TachyonRowMajorMatrix<BabyBear> =\n            TachyonRowMajorMatrix::new(data.as_mut_ptr(), 5, 6);\n        let matrix = RowMajorMatrix::new(\n            (0..30)\n                .map(|i| BabyBear::from_canonical_u32(i))\n                .collect::<Vec<_>>(),\n            6,\n        );\n        assert_eq!(tachyon_matrix.width(), matrix.width());\n        assert_eq!(tachyon_matrix.height(), matrix.height());\n        assert_eq!(tachyon_matrix.get(3, 3), matrix.get(3, 3));\n\n        assert_eq!(\n            tachyon_matrix.row(3).collect::<Vec<_>>(),\n            matrix.row(3).collect::<Vec<_>>()\n        );\n\n        assert_eq!(\n            tachyon_matrix.clone().to_row_major_matrix(),\n            matrix.clone().to_row_major_matrix()\n        );\n\n        let tachyon_row = tachyon_matrix.horizontally_packed_row::<<BabyBear as Field>::Packing>(3);\n        let row = matrix.horizontally_packed_row::<<BabyBear as Field>::Packing>(3);\n        assert_eq!(tachyon_row.0.collect::<Vec<_>>(), row.0.collect::<Vec<_>>());\n        assert_eq!(tachyon_row.1.collect::<Vec<_>>(), row.1.collect::<Vec<_>>());\n\n        let tachyon_rows = tachyon_matrix.split_rows(3);\n        let rows = matrix.split_rows(3);\n        assert_eq!(tachyon_rows.0, rows.0);\n        assert_eq!(tachyon_rows.1, rows.1);\n    }\n}\n"
  },
  {
    "path": "vendors/scroll_halo2/BUILD.bazel",
    "content": "load(\"@crate_index//:defs.bzl\", \"aliases\", \"all_crate_deps\")\nload(\"@cxx.rs//tools/bazel:rust_cxx_bridge.bzl\", \"rust_cxx_bridge\")\nload(\"@local_config_cuda//cuda:build_defs.bzl\", \"if_cuda\")\nload(\"//bazel:tachyon.bzl\", \"if_gpu_is_configured\", \"if_has_openmp_on_macos\")\nload(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_openmp_linkopts\")\nload(\"//bazel:tachyon_rust.bzl\", \"tachyon_rust_library\", \"tachyon_rust_test\")\n\nFEATURES = if_gpu_is_configured([\"gpu\"])\n\ntachyon_rust_library(\n    name = \"scroll_halo2\",\n    srcs = glob([\"src/**/*.rs\"]),\n    aliases = aliases(),\n    crate_features = FEATURES,\n    proc_macro_deps = all_crate_deps(proc_macro = True),\n    deps = all_crate_deps(normal = True) + [\n        \":bn254_blake2b_writer\",\n        \":bn254_cxx_bridge\",\n        \":bn254_evals\",\n        \":bn254_msm\",\n        \":bn254_msm_gpu\",\n        \":bn254_poly\",\n        \":bn254_poseidon_writer\",\n        \":bn254_prover\",\n        \":bn254_proving_key\",\n        \":bn254_rational_evals\",\n        \":bn254_rational_evals_view\",\n        \":bn254_sha256_writer\",\n        \":bn254_snark_verifier_poseidon_writer\",\n        \":cha_cha20_rng\",\n        \":cha_cha20_rng_cxx_bridge\",\n        \":xor_shift_rng\",\n        \":xor_shift_rng_cxx_bridge\",\n        \"//tachyon/rs:tachyon_rs\",\n    ],\n)\n\n# NOTE(chokobole): This attribute could be added to `scroll_halo2_test`,\n# but this approach doesn't work when compiling with nvcc.\n# rustc_flags = if_has_openmp([\"-lgomp\"]),\ncc_library(\n    name = \"openmp\",\n    linkopts = tachyon_openmp_linkopts(),\n)\n\ntachyon_rust_test(\n    name = \"scroll_halo2_test\",\n    size = \"small\",\n    aliases = aliases(),\n    crate = \":scroll_halo2\",\n    crate_features = FEATURES,\n    # NOTE(chokobole): IcicleNTT should be initialized with the maximum domain size.\n    # If it runs in parallel, it may execute FFT/IFFT with a domain size larger than the allowed maximum.\n    env = if_cuda(\n        {\"RUST_TEST_THREADS\": \"1\"},\n        {},\n    ),\n    proc_macro_deps = all_crate_deps(proc_macro_dev = True),\n    deps = all_crate_deps(normal_dev = True) + [\n        \":openmp\",\n        \"@local_config_gmp//:gmp\",\n    ] + if_cuda([\n        \"@local_config_cuda//cuda:cudart_static\",\n    ]) + if_has_openmp_on_macos([\n        \"@local_config_omp//:omp\",\n    ]),\n)\n\nrust_cxx_bridge(\n    name = \"bn254_cxx_bridge\",\n    src = \"src/bn254.rs\",\n    args = if_gpu_is_configured([\n        '--cfg=feature=\"gpu\"',\n    ]),\n    deps = [\":bn254_api_hdrs\"],\n)\n\nrust_cxx_bridge(\n    name = \"cha_cha20_rng_cxx_bridge\",\n    src = \"src/cha_cha20_rng.rs\",\n    deps = [\":cha_cha20_rng_api_hdrs\"],\n)\n\nrust_cxx_bridge(\n    name = \"xor_shift_rng_cxx_bridge\",\n    src = \"src/xor_shift_rng.rs\",\n    deps = [\":xor_shift_rng_api_hdrs\"],\n)\n\ntachyon_cc_library(\n    name = \"bn254_api_hdrs\",\n    hdrs = [\n        \"include/bn254_blake2b_writer.h\",\n        \"include/bn254_evals.h\",\n        \"include/bn254_msm.h\",\n        \"include/bn254_msm_gpu.h\",\n        \"include/bn254_poly.h\",\n        \"include/bn254_poseidon_writer.h\",\n        \"include/bn254_prover.h\",\n        \"include/bn254_proving_key.h\",\n        \"include/bn254_rational_evals.h\",\n        \"include/bn254_rational_evals_view.h\",\n        \"include/bn254_sha256_writer.h\",\n        \"include/bn254_snark_verifier_poseidon_writer.h\",\n    ],\n    deps = [\n        \"//tachyon/c/math/polynomials/univariate:bn254_univariate_dense_polynomial\",\n        \"//tachyon/c/math/polynomials/univariate:bn254_univariate_evaluations\",\n        \"//tachyon/c/math/polynomials/univariate:bn254_univariate_rational_evaluations\",\n        \"//tachyon/c/zk/plonk/halo2:bn254_prover\",\n        \"//tachyon/c/zk/plonk/halo2:bn254_transcript\",\n        \"//tachyon/c/zk/plonk/keys:bn254_plonk_proving_key\",\n        \"@cxx.rs//:core\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"bn254_blake2b_writer\",\n    srcs = [\"src/bn254_blake2b_writer.cc\"],\n    deps = [\n        \":bn254_api_hdrs\",\n        \":bn254_cxx_bridge/include\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/rs/base:container_util\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"bn254_evals\",\n    srcs = [\"src/bn254_evals.cc\"],\n    deps = [\n        \":bn254_api_hdrs\",\n        \":bn254_cxx_bridge/include\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"bn254_msm\",\n    srcs = [\"src/bn254_msm.cc\"],\n    deps = [\n        \":bn254_api_hdrs\",\n        \":bn254_cxx_bridge/include\",\n        \"//tachyon/c/math/elliptic_curves/bn/bn254:msm\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"bn254_msm_gpu\",\n    srcs = if_gpu_is_configured([\"src/bn254_msm_gpu.cc\"]),\n    deps = [\n        \":bn254_api_hdrs\",\n        \":bn254_cxx_bridge/include\",\n        \"//tachyon/c/math/elliptic_curves/bn/bn254:msm_gpu\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"bn254_poly\",\n    srcs = [\"src/bn254_poly.cc\"],\n    deps = [\n        \":bn254_api_hdrs\",\n        \":bn254_cxx_bridge/include\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"bn254_poseidon_writer\",\n    srcs = [\"src/bn254_poseidon_writer.cc\"],\n    deps = [\n        \":bn254_api_hdrs\",\n        \":bn254_cxx_bridge/include\",\n        \"//tachyon/rs/base:container_util\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"bn254_prover\",\n    srcs = [\"src/bn254_prover.cc\"],\n    deps = [\n        \":bn254_api_hdrs\",\n        \":bn254_cxx_bridge/include\",\n        \":bn254_evals\",\n        \":bn254_poly\",\n        \":bn254_rational_evals\",\n        \"//tachyon/base/buffer\",\n        \"//tachyon/rs/base:container_util\",\n        \"//tachyon/rs/base:rust_vec\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"bn254_proving_key\",\n    srcs = [\"src/bn254_proving_key.cc\"],\n    deps = [\n        \":bn254_api_hdrs\",\n        \":bn254_cxx_bridge/include\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"bn254_rational_evals\",\n    srcs = [\"src/bn254_rational_evals.cc\"],\n    deps = [\n        \":bn254_api_hdrs\",\n        \":bn254_cxx_bridge/include\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"bn254_rational_evals_view\",\n    srcs = [\"src/bn254_rational_evals_view.cc\"],\n    deps = [\n        \":bn254_api_hdrs\",\n        \":bn254_cxx_bridge/include\",\n        \"//tachyon/base:logging\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"bn254_sha256_writer\",\n    srcs = [\"src/bn254_sha256_writer.cc\"],\n    deps = [\n        \":bn254_api_hdrs\",\n        \":bn254_cxx_bridge/include\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/rs/base:container_util\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"bn254_snark_verifier_poseidon_writer\",\n    srcs = [\"src/bn254_snark_verifier_poseidon_writer.cc\"],\n    deps = [\n        \":bn254_api_hdrs\",\n        \":bn254_cxx_bridge/include\",\n        \"//tachyon/rs/base:container_util\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"cha_cha20_rng_api_hdrs\",\n    hdrs = [\"include/cha_cha20_rng.h\"],\n    deps = [\n        \"//tachyon/c/crypto/random:rng\",\n        \"@cxx.rs//:core\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"cha_cha20_rng\",\n    srcs = [\"src/cha_cha20_rng.cc\"],\n    deps = [\n        \":cha_cha20_rng_api_hdrs\",\n        \":cha_cha20_rng_cxx_bridge/include\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/rs/base:container_util\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"xor_shift_rng_api_hdrs\",\n    hdrs = [\"include/xor_shift_rng.h\"],\n    deps = [\n        \"//tachyon/c/crypto/random:rng\",\n        \"@cxx.rs//:core\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"xor_shift_rng\",\n    srcs = [\"src/xor_shift_rng.cc\"],\n    deps = [\n        \":xor_shift_rng_api_hdrs\",\n        \":xor_shift_rng_cxx_bridge/include\",\n        \"//tachyon/base:logging\",\n        \"//tachyon/rs/base:container_util\",\n    ],\n)\n"
  },
  {
    "path": "vendors/scroll_halo2/Cargo.toml",
    "content": "[package]\nname = \"tachyon_scroll_halo2\"\nversion = \"0.0.1\"\nauthors = [\"The Tachyon Authors <tachyon-discuss@kroma.network>\"]\nedition = \"2021\"\nrust-version = \"1.56.1\"\ndescription = \"\"\"\nHalo2 unittest based on tachyon\n\"\"\"\nlicense = \"MIT OR Apache-2.0\"\nrepository = \"https://github.com/kroma-network/tachyon\"\nreadme = \"README.md\"\ncategories = [\"cryptography\"]\nkeywords = [\"tachyon\", \"halo\", \"proofs\", \"zkp\", \"zkSNARKs\"]\npublish = false\n\n[dependencies]\ncxx = \"1.0\"\ndigest = \"0.10.3\"\nff = \"0.13\"\nlog = \"0.4.22\"\nnum-bigint = { version = \"0.4\", features = [\"rand\"] }\nhalo2_proofs = { git = \"https://github.com/kroma-network/halo2.git\", rev = \"28ceefb\" }\nhalo2curves = { version = \"0.1.0\", features = [\"derive_serde\"] }\nsha2 = \"0.10.2\"\ntachyon_rs = { path = \"../../tachyon/rs\" }\nrand_core = { version = \"0.6\", default-features = false, features = [\n    \"getrandom\",\n] }\n\n[dev-dependencies]\nrand_chacha = \"0.3.1\"\nrand_xorshift = \"0.3\"\n\n[features]\ndefault = []\ngpu = []\n"
  },
  {
    "path": "vendors/scroll_halo2/README.md",
    "content": "# Scroll Halo2\n\n## How to migrate from Scroll Halo2 to Tachyon\n\nTachyon can easily replace existing Halo2 implementations that generate proofs and currently supports the _BN256 (or Bn254)_ curve along with _GWC_ and _SHPlonk_ provers.\n\n### Changing configurations\n\n1. Replace the `halo2` library in your program with [tachyon-halo2](https://github.com/kroma-network/halo2).\n\n   ```diff\n   # In your Cargo.toml\n   - halo2_proofs = { git = \"https://github.com/zcash/halo2.git\" }\n   + halo2_proofs = { git = \"https://github.com/kroma-network/halo2.git\", branch = \"scroll-halo2-v1.1-with-tachyon\" }\n   ```\n\n2. Install the Tachyon debian package. See [Debian packaging](../../docs/how_to_use/how_to_build.md#debian-packaging).\n\n### Editing code\n\n1. Create the Proving Key for Tachyon\n\n   ```diff\n     use halo2_proofs::{\n   +   bn254::ProvingKey as TachyonProvingKey,\n   +   consts::PCSType,\n       halo2curves::bn256::{Bn256, Fr},\n       plonk::keygen_pk2,\n       poly::kzg::commitment::ParamsKZG,\n     };\n\n     // Use just your `circuit` and `instances` so.\n     let circuit = YourCircuit<_>;\n     let instances = <YourInstances>\n\n     // `k`: log of degree, `s`: toxic-waste secret.\n     let k: u32 = 5;\n     let s = Fr::from(2);\n\n     // Generate `params`.\n     let params = ParamsKZG<Bn256>::unsafe_setup_with_s(k, s);\n\n     // Generate `pk` as proving key.\n     let pk = keygen_pk2(&params, &circuit).expect(\"keygen_pk2 should not fail\");\n   + let (mut tachyon_pk, fixed_values) = {\n   +     // Please remember to release `pk` and `pk_bytes` immediately after\n   +     // deserialization, as it can occupy big memory.\n   +     let mut pk_bytes: Vec<u8> = vec![];\n   +     pk.write_including_cs(&mut pk_bytes).unwrap();\n   +     fixed_values = pk.drop_but_fixed_values();\n   +     (TachyonProvingKey::from(PCSType::GWC, pk_bytes.as_slice()), fixed_values)\n   + };\n   ```\n\n2. Replace the transcript\n\n   ```diff\n   -  use halo2_proofs::transcript::{\n   -     halo2curves::bn256::G1Affine,\n   -     Challenge255, PoseidonWrite,\n   - };\n   + use halo2_proofs::bn254::PoseidonWrite as TachyonPoseidonWrite;\n\n   - let mut transcript = PoseidonWrite::<_, G1Affine, Challenge255<_>>::init(vec![]);\n   + let mut transcript = TachyonPoseidonWrite::init(vec![]);\n   ```\n\n   Tachyon supports `PoseidonWrite`, `Blake2bWrite`, and `Sha256Write` as transcript methods.\n\n3. Replace the RNG (Random Number Generator)\n\n   ```diff\n   - use rand_core::{RngCore, SeedableRng};\n   - use rand_xorshift::XorShiftRng;\n   + use halo2_proofs::xor_shift_rng::XORShiftRng;\n   + use rand_core::SeedableRng;\n\n     let seed: [u8; 16] = <SpecificSeed>;\n   - let rng = rand_xorshift::XorShiftRng::from_seed(seed);\n   + let rng = XORShiftRng::from_seed(seed);\n   ```\n\n4. Create the Prover for Tachyon\n\n   ```diff\n   + use halo2_proofs::{\n   +     bn254::{\n   +         GWCProver as TachyonGWCProver,\n   +         TachyonProver,\n   +     },\n   +     consts::TranscriptType,\n   +     halo2curves::bn256::Bn256,\n   +     poly::kzg::commitment::KZGCommitmentScheme,\n   + }\n   +\n   + let mut prover = {\n   +     let mut params_bytes = vec![];\n   +     // Please remember to release `params` and `params_bytes` immediately after\n   +     // deserialization, as it can occupy big memory.\n   +     params.write(&mut params_bytes).unwrap();\n   +     drop(params);\n   +     TachyonGWCProver::<KZGCommitmentScheme<Bn256>>::from_params(\n   +         TranscriptType::Poseidon as u8,\n   +         k,\n   +         params_bytes.as_slice(),\n   +     )\n   + };\n   ```\n\n   Tachyon supports `GWCProver` or `SHPlonkProver` as a prover. Note that the first argument of `XXXProver::from_params()` and `transcript` must refer to the same type.\n\n5. Create the Proof with Tachyon\n\n   ```diff\n     use halo2_proofs::{\n   -     halo2curves::bn256::Bn256,\n   -     poly::kzg::{commitment::KZGCommitmentScheme, multiopen::ProverSHPLONK},\n   +     plonk::tachyon::create_proof as tachyon_create_proof,\n     }\n\n   +\n\n   - create_proof::<KZGCommitmentScheme<Bn256>, ProverSHPLONK<Bn256>, _, _, _, _>(\n   -     &params,\n   -     &pk,\n   + tachyon_create_proof::<_, _, _, _, _, _>(\n   +     &mut prover,\n   +     &mut tachyon_pk,\n         &[&circuit],\n         &[&instances],\n   +     fixed_values,\n         rng,\n         &mut transcript,\n     )\n     .expect(\"proof generation should not fail\");\n\n   - let proof = transcript.finalize();\n   + let mut proof = transcript.finalize();\n   + let proof_last = prover.get_proof();\n   + proof.extend_from_slice(&proof_last);\n   ```\n"
  },
  {
    "path": "vendors/scroll_halo2/include/bn254_blake2b_writer.h",
    "content": "#ifndef VENDORS_SCROLL_HALO2_INCLUDE_BN254_BLAKE2B_WRITER_H_\n#define VENDORS_SCROLL_HALO2_INCLUDE_BN254_BLAKE2B_WRITER_H_\n\n#include <stdint.h>\n\n#include <array>\n#include <memory>\n\n#include \"openssl/blake2.h\"\n#include \"rust/cxx.h\"\n\n#include \"tachyon/c/zk/plonk/halo2/bn254_transcript.h\"\n\nnamespace tachyon::halo2_api::bn254 {\n\nclass Blake2bWriter {\n public:\n  Blake2bWriter();\n  Blake2bWriter(const Blake2bWriter& other) = delete;\n  Blake2bWriter& operator=(const Blake2bWriter& other) = delete;\n  ~Blake2bWriter();\n\n  void update(rust::Slice<const uint8_t> data);\n  void finalize(std::array<uint8_t, BLAKE2B512_DIGEST_LENGTH>& result);\n  rust::Vec<uint8_t> state() const;\n\n private:\n  tachyon_halo2_bn254_transcript_writer* writer_;\n};\n\nstd::unique_ptr<Blake2bWriter> new_blake2b_writer();\n\n}  // namespace tachyon::halo2_api::bn254\n\n#endif  // VENDORS_SCROLL_HALO2_INCLUDE_BN254_BLAKE2B_WRITER_H_\n"
  },
  {
    "path": "vendors/scroll_halo2/include/bn254_evals.h",
    "content": "#ifndef VENDORS_SCROLL_HALO2_INCLUDE_BN254_EVALS_H_\n#define VENDORS_SCROLL_HALO2_INCLUDE_BN254_EVALS_H_\n\n#include <stddef.h>\n\n#include <memory>\n#include <utility>\n\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_evaluations.h\"\n\nnamespace tachyon::halo2_api::bn254 {\n\nstruct Fr;\n\nclass Evals {\n public:\n  Evals();\n  explicit Evals(tachyon_bn254_univariate_evaluations* evals) : evals_(evals) {}\n  Evals(const Evals& other) = delete;\n  Evals& operator=(const Evals& other) = delete;\n  ~Evals();\n\n  tachyon_bn254_univariate_evaluations* evals() { return evals_; }\n  const tachyon_bn254_univariate_evaluations* evals() const { return evals_; }\n\n  tachyon_bn254_univariate_evaluations* release() {\n    return std::exchange(evals_, nullptr);\n  }\n\n  size_t len() const;\n  void set_value(size_t idx, const Fr& value);\n  std::unique_ptr<Evals> clone() const;\n\n private:\n  tachyon_bn254_univariate_evaluations* evals_;\n};\n\nstd::unique_ptr<Evals> zero_evals();\n\n}  // namespace tachyon::halo2_api::bn254\n\n#endif  // VENDORS_SCROLL_HALO2_INCLUDE_BN254_EVALS_H_\n"
  },
  {
    "path": "vendors/scroll_halo2/include/bn254_msm.h",
    "content": "#ifndef VENDORS_SCROLL_HALO2_INCLUDE_BN254_MSM_H_\n#define VENDORS_SCROLL_HALO2_INCLUDE_BN254_MSM_H_\n\n#include \"rust/cxx.h\"\n\nnamespace tachyon::halo2_api::bn254 {\n\nstruct G1MSM;\n\nstruct G1ProjectivePoint;\nstruct G1Point2;\nstruct Fr;\n\nrust::Box<G1MSM> create_g1_msm(uint8_t degree);\n\nvoid destroy_g1_msm(rust::Box<G1MSM> msm);\n\nrust::Box<G1ProjectivePoint> g1_point2_msm(G1MSM* msm,\n                                           rust::Slice<const G1Point2> bases,\n                                           rust::Slice<const Fr> scalars);\n\nvoid create_proof(uint8_t degree);\n\n}  // namespace tachyon::halo2_api::bn254\n\n#endif  // VENDORS_SCROLL_HALO2_INCLUDE_BN254_MSM_H_\n"
  },
  {
    "path": "vendors/scroll_halo2/include/bn254_msm_gpu.h",
    "content": "#ifndef VENDORS_SCROLL_HALO2_INCLUDE_BN254_MSM_GPU_H_\n#define VENDORS_SCROLL_HALO2_INCLUDE_BN254_MSM_GPU_H_\n\n#include \"rust/cxx.h\"\n\nnamespace tachyon::halo2_api::bn254 {\n\nstruct G1MSMGpu;\n\nstruct G1ProjectivePoint;\nstruct G1Point2;\nstruct Fr;\n\nrust::Box<G1MSMGpu> create_g1_msm_gpu(uint8_t degree);\n\nvoid destroy_g1_msm_gpu(rust::Box<G1MSMGpu> msm);\n\nrust::Box<G1ProjectivePoint> g1_point2_msm_gpu(\n    G1MSMGpu* msm, rust::Slice<const G1Point2> bases,\n    rust::Slice<const Fr> scalars);\n\n}  // namespace tachyon::halo2_api::bn254\n\n#endif  // VENDORS_SCROLL_HALO2_INCLUDE_BN254_MSM_GPU_H_\n"
  },
  {
    "path": "vendors/scroll_halo2/include/bn254_poly.h",
    "content": "#ifndef VENDORS_SCROLL_HALO2_INCLUDE_BN254_POLY_H_\n#define VENDORS_SCROLL_HALO2_INCLUDE_BN254_POLY_H_\n\n#include <utility>\n\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_dense_polynomial.h\"\n\nnamespace tachyon::halo2_api::bn254 {\n\nclass Poly {\n public:\n  Poly();\n  explicit Poly(tachyon_bn254_univariate_dense_polynomial* poly)\n      : poly_(poly) {}\n  Poly(const Poly& other) = delete;\n  Poly& operator=(const Poly& other) = delete;\n  ~Poly();\n\n  tachyon_bn254_univariate_dense_polynomial* poly() { return poly_; }\n  const tachyon_bn254_univariate_dense_polynomial* poly() const {\n    return poly_;\n  }\n\n  tachyon_bn254_univariate_dense_polynomial* release() {\n    return std::exchange(poly_, nullptr);\n  }\n\n private:\n  tachyon_bn254_univariate_dense_polynomial* poly_;\n};\n\n}  // namespace tachyon::halo2_api::bn254\n\n#endif  // VENDORS_SCROLL_HALO2_INCLUDE_BN254_POLY_H_\n"
  },
  {
    "path": "vendors/scroll_halo2/include/bn254_poseidon_writer.h",
    "content": "#ifndef VENDORS_SCROLL_HALO2_INCLUDE_BN254_POSEIDON_WRITER_H_\n#define VENDORS_SCROLL_HALO2_INCLUDE_BN254_POSEIDON_WRITER_H_\n\n#include <stdint.h>\n\n#include <memory>\n\n#include \"rust/cxx.h\"\n\n#include \"tachyon/c/zk/plonk/halo2/bn254_transcript.h\"\n\nnamespace tachyon::halo2_api::bn254 {\n\nstruct Fr;\n\nclass PoseidonWriter {\n public:\n  PoseidonWriter();\n  PoseidonWriter(const PoseidonWriter& other) = delete;\n  PoseidonWriter& operator=(const PoseidonWriter& other) = delete;\n  ~PoseidonWriter();\n\n  void update(rust::Slice<const uint8_t> data);\n  rust::Box<Fr> squeeze();\n  rust::Vec<uint8_t> state() const;\n\n private:\n  tachyon_halo2_bn254_transcript_writer* writer_;\n};\n\nstd::unique_ptr<PoseidonWriter> new_poseidon_writer();\n\n}  // namespace tachyon::halo2_api::bn254\n\n#endif  // VENDORS_SCROLL_HALO2_INCLUDE_BN254_POSEIDON_WRITER_H_\n"
  },
  {
    "path": "vendors/scroll_halo2/include/bn254_prover.h",
    "content": "#ifndef VENDORS_SCROLL_HALO2_INCLUDE_BN254_PROVER_H_\n#define VENDORS_SCROLL_HALO2_INCLUDE_BN254_PROVER_H_\n\n#include <stdint.h>\n\n#include <memory>\n\n#include \"rust/cxx.h\"\n\n#include \"tachyon/c/zk/plonk/halo2/bn254_prover.h\"\n\nnamespace tachyon::halo2_api::bn254 {\n\nstruct Fr;\nstruct G1AffinePoint;\nstruct G1ProjectivePoint;\nstruct G2AffinePoint;\nstruct InstanceSingle;\nstruct AdviceSingle;\nclass ProvingKey;\nclass Evals;\nclass RationalEvals;\nclass Poly;\n\nclass Prover {\n public:\n  Prover(uint8_t pcs_type, uint8_t transcript_type, uint32_t k, const Fr& s);\n  Prover(uint8_t pcs_type, uint8_t transcript_type, uint32_t k,\n         const uint8_t* params, size_t params_len);\n  Prover(const Prover& other) = delete;\n  Prover& operator=(const Prover& other) = delete;\n  ~Prover();\n\n  const tachyon_halo2_bn254_prover* prover() const { return prover_; }\n\n  uint32_t k() const;\n  uint64_t n() const;\n  const G2AffinePoint& s_g2() const;\n  rust::Box<G1ProjectivePoint> commit(const Poly& poly) const;\n  rust::Box<G1ProjectivePoint> commit_lagrange(const Evals& evals) const;\n  void batch_start(size_t len) const;\n  void batch_commit(const Poly& poly, size_t i) const;\n  void batch_commit_lagrange(const Evals& evals, size_t i) const;\n  void batch_end(rust::Slice<G1AffinePoint> points) const;\n  std::unique_ptr<Evals> empty_evals() const;\n  std::unique_ptr<RationalEvals> empty_rational_evals() const;\n  std::unique_ptr<Poly> ifft(const Evals& evals) const;\n  void batch_evaluate(\n      rust::Slice<const std::unique_ptr<RationalEvals>> rational_evals,\n      rust::Slice<std::unique_ptr<Evals>> evals) const;\n  void set_rng(uint8_t rng_type, rust::Slice<const uint8_t> state);\n  void set_transcript(rust::Slice<const uint8_t> state);\n  void set_extended_domain(const ProvingKey& pk);\n  void create_proof(ProvingKey& key,\n                    rust::Slice<InstanceSingle> instance_singles,\n                    rust::Slice<AdviceSingle> advice_singles,\n                    rust::Slice<const Fr> challenges);\n  rust::Vec<uint8_t> get_proof() const;\n\n private:\n  tachyon_halo2_bn254_prover* prover_;\n};\n\nstd::unique_ptr<Prover> new_prover(uint8_t pcs_type, uint8_t transcript_type,\n                                   uint32_t k, const Fr& s);\n\nstd::unique_ptr<Prover> new_prover_from_params(\n    uint8_t pcs_type, uint8_t transcript_type, uint32_t k,\n    rust::Slice<const uint8_t> params);\n\n}  // namespace tachyon::halo2_api::bn254\n\n#endif  // VENDORS_SCROLL_HALO2_INCLUDE_BN254_PROVER_H_\n"
  },
  {
    "path": "vendors/scroll_halo2/include/bn254_proving_key.h",
    "content": "#ifndef VENDORS_SCROLL_HALO2_INCLUDE_BN254_PROVING_KEY_H_\n#define VENDORS_SCROLL_HALO2_INCLUDE_BN254_PROVING_KEY_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <memory>\n\n#include \"rust/cxx.h\"\n\n#include \"tachyon/c/zk/plonk/keys/bn254_plonk_proving_key.h\"\n\nnamespace tachyon::halo2_api::bn254 {\n\nstruct Fr;\nclass Prover;\n\nclass ProvingKey {\n public:\n  ProvingKey(uint8_t pcs_type, rust::Slice<const uint8_t> pk_bytes);\n  ProvingKey(const ProvingKey& other) = delete;\n  ProvingKey& operator=(const ProvingKey& other) = delete;\n  ~ProvingKey();\n\n  const tachyon_bn254_plonk_proving_key* pk() const { return pk_; }\n  tachyon_bn254_plonk_proving_key* pk() { return pk_; }\n\n  rust::Vec<uint8_t> advice_column_phases() const;\n  uint32_t blinding_factors() const;\n  rust::Vec<uint8_t> challenge_phases() const;\n  rust::Vec<size_t> constants() const;\n  size_t num_advice_columns() const;\n  size_t num_challenges() const;\n  size_t num_instance_columns() const;\n  rust::Vec<uint8_t> phases() const;\n  rust::Box<Fr> transcript_repr(const Prover& prover);\n\n private:\n  const tachyon_bn254_plonk_verifying_key* GetVerifyingKey() const;\n  const tachyon_bn254_plonk_constraint_system* GetConstraintSystem() const;\n\n  tachyon_bn254_plonk_proving_key* pk_;\n};\n\nstd::unique_ptr<ProvingKey> new_proving_key(\n    uint8_t pcs_type, rust::Slice<const uint8_t> pk_bytes);\n\n}  // namespace tachyon::halo2_api::bn254\n\n#endif  // VENDORS_SCROLL_HALO2_INCLUDE_BN254_PROVING_KEY_H_\n"
  },
  {
    "path": "vendors/scroll_halo2/include/bn254_rational_evals.h",
    "content": "#ifndef VENDORS_SCROLL_HALO2_INCLUDE_BN254_RATIONAL_EVALS_H_\n#define VENDORS_SCROLL_HALO2_INCLUDE_BN254_RATIONAL_EVALS_H_\n\n#include <stddef.h>\n\n#include <memory>\n#include <utility>\n\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_rational_evaluations.h\"\n\nnamespace tachyon::halo2_api::bn254 {\n\nstruct Fr;\nclass RationalEvalsView;\n\nclass RationalEvals {\n public:\n  RationalEvals();\n  explicit RationalEvals(tachyon_bn254_univariate_rational_evaluations* evals)\n      : evals_(evals) {}\n  RationalEvals(const RationalEvals& other) = delete;\n  RationalEvals& operator=(const RationalEvals& other) = delete;\n  ~RationalEvals();\n\n  tachyon_bn254_univariate_rational_evaluations* evals() { return evals_; }\n  const tachyon_bn254_univariate_rational_evaluations* evals() const {\n    return evals_;\n  }\n\n  tachyon_bn254_univariate_rational_evaluations* release() {\n    return std::exchange(evals_, nullptr);\n  }\n\n  size_t len() const;\n  std::unique_ptr<RationalEvalsView> create_view(size_t start, size_t len);\n  std::unique_ptr<RationalEvals> clone() const;\n\n private:\n  tachyon_bn254_univariate_rational_evaluations* evals_;\n};\n\n}  // namespace tachyon::halo2_api::bn254\n\n#endif  // VENDORS_SCROLL_HALO2_INCLUDE_BN254_RATIONAL_EVALS_H_\n"
  },
  {
    "path": "vendors/scroll_halo2/include/bn254_rational_evals_view.h",
    "content": "#ifndef VENDORS_SCROLL_HALO2_INCLUDE_BN254_RATIONAL_EVALS_VIEW_H_\n#define VENDORS_SCROLL_HALO2_INCLUDE_BN254_RATIONAL_EVALS_VIEW_H_\n\n#include <stddef.h>\n\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_rational_evaluations.h\"\n\nnamespace tachyon::halo2_api::bn254 {\n\nstruct Fr;\n\nclass RationalEvalsView {\n public:\n  RationalEvalsView(tachyon_bn254_univariate_rational_evaluations* evals,\n                    size_t start, size_t len);\n  RationalEvalsView(const RationalEvalsView& other) = delete;\n  RationalEvalsView& operator=(const RationalEvalsView& other) = delete;\n  ~RationalEvalsView() = default;\n\n  void set_zero(size_t idx);\n  void set_trivial(size_t idx, const Fr& numerator);\n  void set_rational(size_t idx, const Fr& numerator, const Fr& denominator);\n  void evaluate(size_t idx, Fr& value) const;\n\n private:\n  // not owned\n  tachyon_bn254_univariate_rational_evaluations* const evals_;\n  const size_t start_ = 0;\n  const size_t len_ = 0;\n};\n\n}  // namespace tachyon::halo2_api::bn254\n\n#endif  // VENDORS_SCROLL_HALO2_INCLUDE_BN254_RATIONAL_EVALS_VIEW_H_\n"
  },
  {
    "path": "vendors/scroll_halo2/include/bn254_sha256_writer.h",
    "content": "#ifndef VENDORS_SCROLL_HALO2_INCLUDE_BN254_SHA256_WRITER_H_\n#define VENDORS_SCROLL_HALO2_INCLUDE_BN254_SHA256_WRITER_H_\n\n#include <stdint.h>\n\n#include <array>\n#include <memory>\n\n#include \"openssl/sha.h\"\n#include \"rust/cxx.h\"\n\n#include \"tachyon/c/zk/plonk/halo2/bn254_transcript.h\"\n\nnamespace tachyon::halo2_api::bn254 {\n\nclass Sha256Writer {\n public:\n  Sha256Writer();\n  Sha256Writer(const Sha256Writer& other) = delete;\n  Sha256Writer& operator=(const Sha256Writer& other) = delete;\n  ~Sha256Writer();\n\n  void update(rust::Slice<const uint8_t> data);\n  void finalize(std::array<uint8_t, SHA256_DIGEST_LENGTH>& result);\n  rust::Vec<uint8_t> state() const;\n\n private:\n  tachyon_halo2_bn254_transcript_writer* writer_;\n};\n\nstd::unique_ptr<Sha256Writer> new_sha256_writer();\n\n}  // namespace tachyon::halo2_api::bn254\n\n#endif  // VENDORS_SCROLL_HALO2_INCLUDE_BN254_SHA256_WRITER_H_\n"
  },
  {
    "path": "vendors/scroll_halo2/include/bn254_snark_verifier_poseidon_writer.h",
    "content": "#ifndef VENDORS_SCROLL_HALO2_INCLUDE_BN254_SNARK_VERIFIER_POSEIDON_WRITER_H_\n#define VENDORS_SCROLL_HALO2_INCLUDE_BN254_SNARK_VERIFIER_POSEIDON_WRITER_H_\n\n#include <stdint.h>\n\n#include <memory>\n\n#include \"rust/cxx.h\"\n\n#include \"tachyon/c/zk/plonk/halo2/bn254_transcript.h\"\n\nnamespace tachyon::halo2_api::bn254 {\n\nstruct Fr;\n\nclass SnarkVerifierPoseidonWriter {\n public:\n  SnarkVerifierPoseidonWriter();\n  SnarkVerifierPoseidonWriter(const SnarkVerifierPoseidonWriter& other) =\n      delete;\n  SnarkVerifierPoseidonWriter& operator=(\n      const SnarkVerifierPoseidonWriter& other) = delete;\n  ~SnarkVerifierPoseidonWriter();\n\n  void update(rust::Slice<const uint8_t> data);\n  rust::Box<Fr> squeeze();\n  rust::Vec<uint8_t> state() const;\n\n private:\n  tachyon_halo2_bn254_transcript_writer* writer_;\n};\n\nstd::unique_ptr<SnarkVerifierPoseidonWriter>\nnew_snark_verifier_poseidon_writer();\n\n}  // namespace tachyon::halo2_api::bn254\n\n#endif  // VENDORS_SCROLL_HALO2_INCLUDE_BN254_SNARK_VERIFIER_POSEIDON_WRITER_H_\n"
  },
  {
    "path": "vendors/scroll_halo2/include/cha_cha20_rng.h",
    "content": "#ifndef VENDORS_SCROLL_HALO2_INCLUDE_CHA_CHA20_RNG_H_\n#define VENDORS_SCROLL_HALO2_INCLUDE_CHA_CHA20_RNG_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <array>\n#include <memory>\n\n#include \"rust/cxx.h\"\n\n#include \"tachyon/c/crypto/random/rng.h\"\n\nnamespace tachyon::halo2_api {\n\nclass ChaCha20Rng {\n public:\n  constexpr static size_t kSeedSize = 32;\n  constexpr static size_t kStateSize = sizeof(size_t) + 128;\n\n  explicit ChaCha20Rng(tachyon_rng* rng) : rng_(rng) {}\n  explicit ChaCha20Rng(std::array<uint8_t, kSeedSize> seed);\n  ChaCha20Rng(const ChaCha20Rng& other) = delete;\n  ChaCha20Rng& operator=(const ChaCha20Rng& other) = delete;\n  ~ChaCha20Rng();\n\n  uint32_t next_u32();\n  std::unique_ptr<ChaCha20Rng> clone() const;\n  rust::Vec<uint8_t> state() const;\n\n private:\n  tachyon_rng* rng_;\n};\n\nstd::unique_ptr<ChaCha20Rng> new_cha_cha20_rng(\n    std::array<uint8_t, ChaCha20Rng::kSeedSize> seed);\n\n}  // namespace tachyon::halo2_api\n\n#endif  // VENDORS_SCROLL_HALO2_INCLUDE_CHA_CHA20_RNG_H_\n"
  },
  {
    "path": "vendors/scroll_halo2/include/xor_shift_rng.h",
    "content": "#ifndef VENDORS_SCROLL_HALO2_INCLUDE_XOR_SHIFT_RNG_H_\n#define VENDORS_SCROLL_HALO2_INCLUDE_XOR_SHIFT_RNG_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <array>\n#include <memory>\n\n#include \"rust/cxx.h\"\n\n#include \"tachyon/c/crypto/random/rng.h\"\n\nnamespace tachyon::halo2_api {\n\nclass XORShiftRng {\n public:\n  constexpr static size_t kSeedSize = 16;\n  constexpr static size_t kStateSize = 16;\n\n  explicit XORShiftRng(tachyon_rng* rng) : rng_(rng) {}\n  explicit XORShiftRng(std::array<uint8_t, kSeedSize> seed);\n  XORShiftRng(const XORShiftRng& other) = delete;\n  XORShiftRng& operator=(const XORShiftRng& other) = delete;\n  ~XORShiftRng();\n\n  uint32_t next_u32();\n  std::unique_ptr<XORShiftRng> clone() const;\n  rust::Vec<uint8_t> state() const;\n\n private:\n  tachyon_rng* rng_;\n};\n\nstd::unique_ptr<XORShiftRng> new_xor_shift_rng(\n    std::array<uint8_t, XORShiftRng::kSeedSize> seed);\n\n}  // namespace tachyon::halo2_api\n\n#endif  // VENDORS_SCROLL_HALO2_INCLUDE_XOR_SHIFT_RNG_H_\n"
  },
  {
    "path": "vendors/scroll_halo2/src/bn254.rs",
    "content": "use std::{\n    fmt,\n    io::{self, Write},\n    marker::PhantomData,\n};\n\nuse ff::{Field, PrimeField};\nuse halo2_proofs::{\n    plonk::{sealed, Column, Fixed},\n    poly::commitment::{Blind, CommitmentScheme},\n    transcript::{\n        Challenge255, EncodedChallenge, Transcript, TranscriptWrite, TranscriptWriterBuffer,\n    },\n};\nuse halo2curves::{\n    bn256::{G1Affine, G2Affine},\n    ff::FromUniformBytes,\n    Coordinates, CurveAffine,\n};\nuse num_bigint::BigUint;\n\nuse tachyon_rs::math::elliptic_curves::bn::bn254::{\n    Fr as FrImpl, G1AffinePoint as G1AffinePointImpl, G1Point2 as G1Point2Impl,\n    G1ProjectivePoint as G1ProjectivePointImpl, G2AffinePoint as G2AffinePointImpl,\n};\n\nuse crate::consts::{PCSType, RNGType};\n\npub struct G1MSM;\npub struct G1MSMGpu;\npub struct G1AffinePoint(pub G1AffinePointImpl);\npub struct G1ProjectivePoint(pub G1ProjectivePointImpl);\npub struct G1Point2(pub G1Point2Impl);\npub struct G2AffinePoint(pub G2AffinePointImpl);\npub struct Fr(pub FrImpl);\npub struct InstanceSingle {\n    pub instance_values: Vec<Evals>,\n    pub instance_polys: Vec<Poly>,\n}\n#[derive(Clone)]\npub struct AdviceSingle {\n    pub advice_polys: Vec<Evals>,\n    pub advice_blinds: Vec<Blind<halo2curves::bn256::Fr>>,\n}\n\n#[cxx::bridge(namespace = \"tachyon::halo2_api::bn254\")]\npub mod ffi {\n    extern \"Rust\" {\n        type G1MSM;\n        type G1MSMGpu;\n        type G1AffinePoint;\n        type G1ProjectivePoint;\n        type G1Point2;\n        type G2AffinePoint;\n        type Fr;\n        type InstanceSingle;\n        type AdviceSingle;\n    }\n\n    unsafe extern \"C++\" {\n        include!(\"vendors/scroll_halo2/include/bn254_msm.h\");\n        #[cfg(feature = \"gpu\")]\n        include!(\"vendors/scroll_halo2/include/bn254_msm_gpu.h\");\n\n        fn create_g1_msm(degree: u8) -> Box<G1MSM>;\n        fn destroy_g1_msm(msm: Box<G1MSM>);\n        unsafe fn g1_point2_msm(\n            msm: *mut G1MSM,\n            bases: &[G1Point2],\n            scalars: &[Fr],\n        ) -> Box<G1ProjectivePoint>;\n        #[cfg(feature = \"gpu\")]\n        fn create_g1_msm_gpu(degree: u8) -> Box<G1MSMGpu>;\n        #[cfg(feature = \"gpu\")]\n        fn destroy_g1_msm_gpu(msm: Box<G1MSMGpu>);\n        #[cfg(feature = \"gpu\")]\n        unsafe fn g1_point2_msm_gpu(\n            msm: *mut G1MSMGpu,\n            bases: &[G1Point2],\n            scalars: &[Fr],\n        ) -> Box<G1ProjectivePoint>;\n    }\n\n    unsafe extern \"C++\" {\n        include!(\"vendors/scroll_halo2/include/bn254_blake2b_writer.h\");\n\n        type Blake2bWriter;\n\n        fn new_blake2b_writer() -> UniquePtr<Blake2bWriter>;\n        fn update(self: Pin<&mut Blake2bWriter>, data: &[u8]);\n        fn finalize(self: Pin<&mut Blake2bWriter>, result: &mut [u8; 64]);\n        fn state(&self) -> Vec<u8>;\n    }\n\n    unsafe extern \"C++\" {\n        include!(\"vendors/scroll_halo2/include/bn254_poseidon_writer.h\");\n\n        type PoseidonWriter;\n\n        fn new_poseidon_writer() -> UniquePtr<PoseidonWriter>;\n        fn update(self: Pin<&mut PoseidonWriter>, data: &[u8]);\n        fn squeeze(self: Pin<&mut PoseidonWriter>) -> Box<Fr>;\n        fn state(&self) -> Vec<u8>;\n    }\n\n    unsafe extern \"C++\" {\n        include!(\"vendors/scroll_halo2/include/bn254_sha256_writer.h\");\n\n        type Sha256Writer;\n\n        fn new_sha256_writer() -> UniquePtr<Sha256Writer>;\n        fn update(self: Pin<&mut Sha256Writer>, data: &[u8]);\n        fn finalize(self: Pin<&mut Sha256Writer>, result: &mut [u8; 32]);\n        fn state(&self) -> Vec<u8>;\n    }\n\n    unsafe extern \"C++\" {\n        include!(\"vendors/scroll_halo2/include/bn254_snark_verifier_poseidon_writer.h\");\n\n        type SnarkVerifierPoseidonWriter;\n\n        fn new_snark_verifier_poseidon_writer() -> UniquePtr<SnarkVerifierPoseidonWriter>;\n        fn update(self: Pin<&mut SnarkVerifierPoseidonWriter>, data: &[u8]);\n        fn squeeze(self: Pin<&mut SnarkVerifierPoseidonWriter>) -> Box<Fr>;\n        fn state(&self) -> Vec<u8>;\n    }\n\n    unsafe extern \"C++\" {\n        include!(\"vendors/scroll_halo2/include/bn254_proving_key.h\");\n\n        type ProvingKey;\n\n        fn new_proving_key(pcs_type: u8, data: &[u8]) -> UniquePtr<ProvingKey>;\n        fn advice_column_phases(&self) -> Vec<u8>;\n        fn blinding_factors(&self) -> u32;\n        fn challenge_phases(&self) -> Vec<u8>;\n        fn constants(&self) -> Vec<usize>;\n        fn num_advice_columns(&self) -> usize;\n        fn num_challenges(&self) -> usize;\n        fn num_instance_columns(&self) -> usize;\n        fn phases(&self) -> Vec<u8>;\n        fn transcript_repr(self: Pin<&mut ProvingKey>, prover: &Prover) -> Box<Fr>;\n    }\n\n    unsafe extern \"C++\" {\n        include!(\"vendors/scroll_halo2/include/bn254_evals.h\");\n\n        type Evals;\n\n        fn zero_evals() -> UniquePtr<Evals>;\n        fn len(&self) -> usize;\n        fn set_value(self: Pin<&mut Evals>, idx: usize, value: &Fr);\n        fn clone(&self) -> UniquePtr<Evals>;\n    }\n\n    unsafe extern \"C++\" {\n        include!(\"vendors/scroll_halo2/include/bn254_rational_evals.h\");\n\n        type RationalEvals;\n\n        fn len(&self) -> usize;\n        fn create_view(\n            self: Pin<&mut RationalEvals>,\n            start: usize,\n            len: usize,\n        ) -> UniquePtr<RationalEvalsView>;\n        fn clone(&self) -> UniquePtr<RationalEvals>;\n    }\n\n    unsafe extern \"C++\" {\n        include!(\"vendors/scroll_halo2/include/bn254_rational_evals_view.h\");\n\n        type RationalEvalsView;\n\n        fn set_zero(self: Pin<&mut RationalEvalsView>, idx: usize);\n        fn set_trivial(self: Pin<&mut RationalEvalsView>, idx: usize, numerator: &Fr);\n        fn set_rational(\n            self: Pin<&mut RationalEvalsView>,\n            idx: usize,\n            numerator: &Fr,\n            denominator: &Fr,\n        );\n        fn evaluate(&self, idx: usize, value: &mut Fr);\n    }\n\n    unsafe extern \"C++\" {\n        include!(\"vendors/scroll_halo2/include/bn254_poly.h\");\n\n        type Poly;\n    }\n\n    unsafe extern \"C++\" {\n        include!(\"vendors/scroll_halo2/include/bn254_prover.h\");\n\n        type Prover;\n\n        fn new_prover(pcs_type: u8, transcript_type: u8, k: u32, s: &Fr) -> UniquePtr<Prover>;\n        fn new_prover_from_params(\n            pcs_type: u8,\n            transcript_type: u8,\n            k: u32,\n            params: &[u8],\n        ) -> UniquePtr<Prover>;\n        fn k(&self) -> u32;\n        fn n(&self) -> u64;\n        fn s_g2(&self) -> &G2AffinePoint;\n        fn commit(&self, poly: &Poly) -> Box<G1ProjectivePoint>;\n        fn commit_lagrange(&self, evals: &Evals) -> Box<G1ProjectivePoint>;\n        fn batch_start(&self, size: usize);\n        fn batch_commit(&self, poly: &Poly, idx: usize);\n        fn batch_commit_lagrange(&self, evals: &Evals, idx: usize);\n        fn batch_end(&self, points: &mut [G1AffinePoint]);\n        fn empty_evals(&self) -> UniquePtr<Evals>;\n        fn empty_rational_evals(&self) -> UniquePtr<RationalEvals>;\n        fn ifft(&self, evals: &Evals) -> UniquePtr<Poly>;\n        fn batch_evaluate(\n            &self,\n            rational_evals: &[UniquePtr<RationalEvals>],\n            evals: &mut [UniquePtr<Evals>],\n        );\n        fn set_rng(self: Pin<&mut Prover>, rng_type: u8, state: &[u8]);\n        fn set_transcript(self: Pin<&mut Prover>, state: &[u8]);\n        fn set_extended_domain(self: Pin<&mut Prover>, pk: &ProvingKey);\n        fn create_proof(\n            self: Pin<&mut Prover>,\n            key: Pin<&mut ProvingKey>,\n            instance_singles: &mut [InstanceSingle],\n            advice_singles: &mut [AdviceSingle],\n            challenges: &[Fr],\n        );\n        fn get_proof(self: &Prover) -> Vec<u8>;\n    }\n}\n\npub trait TranscriptWriteState<C: CurveAffine, E: EncodedChallenge<C>>:\n    TranscriptWrite<C, E>\n{\n    fn state(&self) -> Vec<u8>;\n}\n\npub struct Blake2bWrite<W: Write, C: CurveAffine, E: EncodedChallenge<C>> {\n    state: cxx::UniquePtr<ffi::Blake2bWriter>,\n    writer: W,\n    _marker: PhantomData<(W, C, E)>,\n}\n\nimpl<W: Write, C: CurveAffine> Transcript<C, Challenge255<C>>\n    for Blake2bWrite<W, C, Challenge255<C>>\nwhere\n    C::Scalar: FromUniformBytes<64>,\n{\n    fn squeeze_challenge(&mut self) -> Challenge255<C> {\n        // Prefix to a prover's message soliciting a challenge\n        const BLAKE2B_PREFIX_CHALLENGE: u8 = 0;\n        self.state.pin_mut().update(&[BLAKE2B_PREFIX_CHALLENGE]);\n        let mut result: [u8; 64] = [0; 64];\n        self.state.pin_mut().finalize(&mut result);\n        Challenge255::<C>::new(&result)\n    }\n\n    fn common_point(&mut self, point: C) -> io::Result<()> {\n        // Prefix to a prover's message containing a curve point\n        const BLAKE2B_PREFIX_POINT: u8 = 1;\n        self.state.pin_mut().update(&[BLAKE2B_PREFIX_POINT]);\n        let coords: Coordinates<C> = Option::from(point.coordinates()).ok_or_else(|| {\n            io::Error::new(\n                io::ErrorKind::Other,\n                \"cannot write points at infinity to the transcript\",\n            )\n        })?;\n        self.state.pin_mut().update(coords.x().to_repr().as_ref());\n        self.state.pin_mut().update(coords.y().to_repr().as_ref());\n\n        Ok(())\n    }\n\n    fn common_scalar(&mut self, scalar: C::Scalar) -> io::Result<()> {\n        // Prefix to a prover's message containing a scalar\n        const BLAKE2B_PREFIX_SCALAR: u8 = 2;\n        self.state.pin_mut().update(&[BLAKE2B_PREFIX_SCALAR]);\n        self.state.pin_mut().update(scalar.to_repr().as_ref());\n        Ok(())\n    }\n}\n\nimpl<W: Write, C: CurveAffine> TranscriptWrite<C, Challenge255<C>>\n    for Blake2bWrite<W, C, Challenge255<C>>\nwhere\n    C::Scalar: FromUniformBytes<64>,\n{\n    fn write_point(&mut self, point: C) -> io::Result<()> {\n        self.common_point(point)?;\n        let compressed = point.to_bytes();\n        self.writer.write_all(compressed.as_ref())\n    }\n\n    fn write_scalar(&mut self, scalar: C::Scalar) -> io::Result<()> {\n        self.common_scalar(scalar)?;\n        let data = scalar.to_repr();\n        self.writer.write_all(data.as_ref())\n    }\n}\n\nimpl<W: Write, C: CurveAffine> TranscriptWriteState<C, Challenge255<C>>\n    for Blake2bWrite<W, C, Challenge255<C>>\nwhere\n    C::Scalar: FromUniformBytes<64>,\n{\n    fn state(&self) -> Vec<u8> {\n        self.state.state()\n    }\n}\n\nimpl<W: Write, C: CurveAffine> TranscriptWriterBuffer<W, C, Challenge255<C>>\n    for Blake2bWrite<W, C, Challenge255<C>>\nwhere\n    C::Scalar: FromUniformBytes<64>,\n{\n    /// Initialize a transcript given an output buffer.\n    fn init(writer: W) -> Self {\n        Blake2bWrite {\n            state: ffi::new_blake2b_writer(),\n            writer: writer,\n            _marker: PhantomData,\n        }\n    }\n\n    fn finalize(self) -> W {\n        // TODO: handle outstanding scalars?\n        // See https://github.com/zcash/halo2/issues/138.\n        self.writer\n    }\n}\n\npub struct PoseidonWrite<W: Write, C: CurveAffine, E: EncodedChallenge<C>> {\n    state: cxx::UniquePtr<ffi::PoseidonWriter>,\n    writer: W,\n    _marker: PhantomData<(W, C, E)>,\n}\n\nfn field_to_bn<F: PrimeField>(f: &F) -> BigUint {\n    BigUint::from_bytes_le(f.to_repr().as_ref())\n}\n\n/// Input a big integer `bn`, compute a field element `f`\n/// such that `f == bn % F::MODULUS`.\n/// Require:\n/// - `bn` is less than 512 bits.\n/// Return:\n/// - `bn mod F::MODULUS` when `bn > F::MODULUS`\npub fn bn_to_field<F: PrimeField>(bn: &BigUint) -> F\nwhere\n    F: FromUniformBytes<64>,\n{\n    let mut buf = bn.to_bytes_le();\n    buf.resize(64, 0u8);\n\n    let mut buf_array = [0u8; 64];\n    buf_array.copy_from_slice(buf.as_ref());\n    F::from_uniform_bytes(&buf_array)\n}\n\n/// Input a base field element `b`, output a scalar field\n/// element `s` s.t. `s == b % ScalarField::MODULUS`\nfn base_to_scalar<C: CurveAffine>(base: &C::Base) -> C::Scalar\nwhere\n    C::Scalar: FromUniformBytes<64>,\n{\n    let bn = field_to_bn(base);\n    // bn_to_field will perform a mod reduction\n    bn_to_field(&bn)\n}\n\nimpl<W: Write, C: CurveAffine> Transcript<C, Challenge255<C>>\n    for PoseidonWrite<W, C, Challenge255<C>>\nwhere\n    C::Scalar: FromUniformBytes<64>,\n{\n    fn squeeze_challenge(&mut self) -> Challenge255<C> {\n        let scalar = *unsafe {\n            std::mem::transmute::<_, Box<halo2curves::bn256::Fr>>(self.state.pin_mut().squeeze())\n        };\n        let mut scalar_bytes = scalar.to_repr().as_ref().to_vec();\n        scalar_bytes.resize(64, 0u8);\n        Challenge255::<C>::new(&scalar_bytes.try_into().unwrap())\n    }\n\n    fn common_point(&mut self, point: C) -> io::Result<()> {\n        let coords: Coordinates<C> = Option::from(point.coordinates()).ok_or_else(|| {\n            io::Error::new(\n                io::ErrorKind::Other,\n                \"cannot write points at infinity to the transcript\",\n            )\n        })?;\n        let x = coords.x();\n        let y = coords.y();\n        let slice = &[base_to_scalar::<C>(x), base_to_scalar::<C>(y)];\n        let bytes = std::mem::size_of::<C::Scalar>() * 2;\n        unsafe {\n            self.state.pin_mut().update(std::slice::from_raw_parts(\n                slice.as_ptr() as *const u8,\n                bytes,\n            ));\n        }\n\n        Ok(())\n    }\n\n    fn common_scalar(&mut self, scalar: C::Scalar) -> io::Result<()> {\n        let slice = &[scalar];\n        let bytes = std::mem::size_of::<C::Scalar>();\n        unsafe {\n            self.state.pin_mut().update(std::slice::from_raw_parts(\n                slice.as_ptr() as *const u8,\n                bytes,\n            ));\n        }\n\n        Ok(())\n    }\n}\n\nimpl<W: Write, C: CurveAffine> TranscriptWrite<C, Challenge255<C>>\n    for PoseidonWrite<W, C, Challenge255<C>>\nwhere\n    C::Scalar: FromUniformBytes<64>,\n{\n    fn write_point(&mut self, point: C) -> io::Result<()> {\n        self.common_point(point)?;\n        let compressed = point.to_bytes();\n        self.writer.write_all(compressed.as_ref())\n    }\n\n    fn write_scalar(&mut self, scalar: C::Scalar) -> io::Result<()> {\n        self.common_scalar(scalar)?;\n        let data = scalar.to_repr();\n        self.writer.write_all(data.as_ref())\n    }\n}\n\nimpl<W: Write, C: CurveAffine, E: EncodedChallenge<C>> PoseidonWrite<W, C, E> {\n    /// Initialize a transcript given an output buffer.\n    pub fn init(writer: W) -> Self {\n        PoseidonWrite {\n            state: ffi::new_poseidon_writer(),\n            writer,\n            _marker: PhantomData,\n        }\n    }\n\n    /// Conclude the interaction and return the output buffer (writer).\n    pub fn finalize(self) -> W {\n        // TODO: handle outstanding scalars?\n        // See https://github.com/zcash/halo2/issues/138.\n        self.writer\n    }\n}\n\nimpl<W: Write, C: CurveAffine> TranscriptWriteState<C, Challenge255<C>>\n    for PoseidonWrite<W, C, Challenge255<C>>\nwhere\n    C::Scalar: FromUniformBytes<64>,\n{\n    fn state(&self) -> Vec<u8> {\n        self.state.state()\n    }\n}\n\npub struct Sha256Write<W: Write, C: CurveAffine, E: EncodedChallenge<C>> {\n    state: cxx::UniquePtr<ffi::Sha256Writer>,\n    writer: W,\n    _marker: PhantomData<(W, C, E)>,\n}\n\nimpl<W: Write, C: CurveAffine> Transcript<C, Challenge255<C>> for Sha256Write<W, C, Challenge255<C>>\nwhere\n    C::Scalar: FromUniformBytes<64>,\n{\n    fn squeeze_challenge(&mut self) -> Challenge255<C> {\n        const SHA256_PREFIX_CHALLENGE: u8 = 0;\n        self.state.pin_mut().update(&[SHA256_PREFIX_CHALLENGE]);\n        let mut result: [u8; 32] = [0; 32];\n        self.state.pin_mut().finalize(&mut result);\n\n        self.state = ffi::new_sha256_writer();\n        self.state.pin_mut().update(result.as_slice());\n\n        let mut bytes = result.to_vec();\n        bytes.resize(64, 0u8);\n        Challenge255::<C>::new(&bytes.try_into().unwrap())\n    }\n\n    fn common_point(&mut self, point: C) -> io::Result<()> {\n        const SHA256_PREFIX_POINT: u8 = 1;\n        self.state.pin_mut().update(&[0u8; 31]);\n        self.state.pin_mut().update(&[SHA256_PREFIX_POINT]);\n        let coords: Coordinates<C> = Option::from(point.coordinates()).ok_or_else(|| {\n            io::Error::new(\n                io::ErrorKind::Other,\n                \"cannot write points at infinity to the transcript\",\n            )\n        })?;\n\n        for base in &[coords.x(), coords.y()] {\n            let mut buf = base.to_repr().as_ref().to_vec();\n            buf.resize(32, 0u8);\n            buf.reverse();\n            self.state.pin_mut().update(buf.as_slice());\n        }\n\n        Ok(())\n    }\n\n    fn common_scalar(&mut self, scalar: C::Scalar) -> io::Result<()> {\n        const SHA256_PREFIX_SCALAR: u8 = 2;\n        self.state.pin_mut().update(&[0u8; 31]);\n        self.state.pin_mut().update(&[SHA256_PREFIX_SCALAR]);\n\n        {\n            let mut buf = scalar.to_repr().as_ref().to_vec();\n            buf.resize(32, 0u8);\n            buf.reverse();\n            self.state.pin_mut().update(buf.as_slice());\n        }\n\n        Ok(())\n    }\n}\n\nimpl<W: Write, C: CurveAffine> TranscriptWrite<C, Challenge255<C>>\n    for Sha256Write<W, C, Challenge255<C>>\nwhere\n    C::Scalar: FromUniformBytes<64>,\n{\n    fn write_point(&mut self, point: C) -> io::Result<()> {\n        self.common_point(point)?;\n\n        let coords = point.coordinates();\n        let x = coords\n            .map(|v| *v.x())\n            .unwrap_or(<C as CurveAffine>::Base::ZERO);\n        let y = coords\n            .map(|v| *v.y())\n            .unwrap_or(<C as CurveAffine>::Base::ZERO);\n\n        for base in &[&x, &y] {\n            self.writer.write_all(base.to_repr().as_ref())?;\n        }\n\n        Ok(())\n    }\n\n    fn write_scalar(&mut self, scalar: C::Scalar) -> io::Result<()> {\n        self.common_scalar(scalar)?;\n        let data = scalar.to_repr();\n\n        self.writer.write_all(data.as_ref())\n    }\n}\n\nimpl<W: Write, C: CurveAffine> TranscriptWriteState<C, Challenge255<C>>\n    for Sha256Write<W, C, Challenge255<C>>\nwhere\n    C::Scalar: FromUniformBytes<64>,\n{\n    fn state(&self) -> Vec<u8> {\n        self.state.state()\n    }\n}\n\nimpl<W: Write, C: CurveAffine, E: EncodedChallenge<C>> Sha256Write<W, C, E> {\n    /// Initialize a transcript given an output buffer.\n    pub fn init(writer: W) -> Self {\n        Sha256Write {\n            state: ffi::new_sha256_writer(),\n            writer,\n            _marker: PhantomData,\n        }\n    }\n\n    /// Conclude the interaction and return the output buffer (writer).\n    pub fn finalize(self) -> W {\n        // TODO: handle outstanding scalars?\n        // See https://github.com/zcash/halo2/issues/138.\n        self.writer\n    }\n}\n\npub struct SnarkVerifierPoseidonWrite<W: Write, C: CurveAffine, E: EncodedChallenge<C>> {\n    state: cxx::UniquePtr<ffi::SnarkVerifierPoseidonWriter>,\n    writer: W,\n    _marker: PhantomData<(W, C, E)>,\n}\n\nimpl<W: Write, C: CurveAffine> Transcript<C, Challenge255<C>>\n    for SnarkVerifierPoseidonWrite<W, C, Challenge255<C>>\nwhere\n    C::Scalar: FromUniformBytes<64>,\n{\n    fn squeeze_challenge(&mut self) -> Challenge255<C> {\n        let scalar = *unsafe {\n            std::mem::transmute::<_, Box<halo2curves::bn256::Fr>>(self.state.pin_mut().squeeze())\n        };\n        let mut scalar_bytes = scalar.to_repr().as_ref().to_vec();\n        scalar_bytes.resize(64, 0u8);\n        Challenge255::<C>::new(&scalar_bytes.try_into().unwrap())\n    }\n\n    fn common_point(&mut self, point: C) -> io::Result<()> {\n        let coords: Coordinates<C> = Option::from(point.coordinates()).ok_or_else(|| {\n            io::Error::new(\n                io::ErrorKind::Other,\n                \"cannot write points at infinity to the transcript\",\n            )\n        })?;\n        let x = coords.x();\n        let y = coords.y();\n        let slice = &[base_to_scalar::<C>(x), base_to_scalar::<C>(y)];\n        let bytes = std::mem::size_of::<C::Scalar>() * 2;\n        unsafe {\n            self.state.pin_mut().update(std::slice::from_raw_parts(\n                slice.as_ptr() as *const u8,\n                bytes,\n            ));\n        }\n\n        Ok(())\n    }\n\n    fn common_scalar(&mut self, scalar: C::Scalar) -> io::Result<()> {\n        let slice = &[scalar];\n        let bytes = std::mem::size_of::<C::Scalar>();\n        unsafe {\n            self.state.pin_mut().update(std::slice::from_raw_parts(\n                slice.as_ptr() as *const u8,\n                bytes,\n            ));\n        }\n\n        Ok(())\n    }\n}\n\nimpl<W: Write, C: CurveAffine> TranscriptWrite<C, Challenge255<C>>\n    for SnarkVerifierPoseidonWrite<W, C, Challenge255<C>>\nwhere\n    C::Scalar: FromUniformBytes<64>,\n{\n    fn write_point(&mut self, point: C) -> io::Result<()> {\n        self.common_point(point)?;\n        let compressed = point.to_bytes();\n        self.writer.write_all(compressed.as_ref())\n    }\n\n    fn write_scalar(&mut self, scalar: C::Scalar) -> io::Result<()> {\n        self.common_scalar(scalar)?;\n        let data = scalar.to_repr();\n        self.writer.write_all(data.as_ref())\n    }\n}\n\nimpl<W: Write, C: CurveAffine, E: EncodedChallenge<C>> SnarkVerifierPoseidonWrite<W, C, E> {\n    /// Initialize a transcript given an output buffer.\n    pub fn init(writer: W) -> Self {\n        SnarkVerifierPoseidonWrite {\n            state: ffi::new_snark_verifier_poseidon_writer(),\n            writer,\n            _marker: PhantomData,\n        }\n    }\n\n    /// Conclude the interaction and return the output buffer (writer).\n    pub fn finalize(self) -> W {\n        // TODO: handle outstanding scalars?\n        // See https://github.com/zcash/halo2/issues/138.\n        self.writer\n    }\n}\n\nimpl<W: Write, C: CurveAffine> TranscriptWriteState<C, Challenge255<C>>\n    for SnarkVerifierPoseidonWrite<W, C, Challenge255<C>>\nwhere\n    C::Scalar: FromUniformBytes<64>,\n{\n    fn state(&self) -> Vec<u8> {\n        self.state.state()\n    }\n}\n\npub struct ProvingKey<C: CurveAffine> {\n    inner: cxx::UniquePtr<ffi::ProvingKey>,\n    _marker: PhantomData<C>,\n}\n\nimpl<C: CurveAffine> ProvingKey<C> {\n    pub fn from(pcs_type: PCSType, data: &[u8]) -> ProvingKey<C> {\n        ProvingKey {\n            inner: ffi::new_proving_key(pcs_type as u8, data),\n            _marker: PhantomData,\n        }\n    }\n\n    // NOTE(chokobole): We name this as plural since it contains multi phases.\n    // pk.vk.cs.advice_column_phase\n    pub fn advice_column_phases(&self) -> Vec<sealed::Phase> {\n        unsafe {\n            let phases: Vec<sealed::Phase> = std::mem::transmute(self.inner.advice_column_phases());\n            phases\n        }\n    }\n\n    // pk.vk.cs.blinding_factors()\n    pub fn blinding_factors(&self) -> u32 {\n        self.inner.blinding_factors()\n    }\n\n    // NOTE(chokobole): We name this as plural since it contains multi phases.\n    // pk.vk.cs.challenge_phase\n    pub fn challenge_phases(&self) -> Vec<sealed::Phase> {\n        unsafe {\n            let phases: Vec<sealed::Phase> = std::mem::transmute(self.inner.challenge_phases());\n            phases\n        }\n    }\n\n    // pk.vk.cs.constants\n    pub fn constants(&self) -> Vec<Column<Fixed>> {\n        let constants = self\n            .inner\n            .constants()\n            .iter()\n            .map(|index| Column {\n                index: *index,\n                column_type: Fixed,\n            })\n            .collect::<Vec<_>>();\n        constants\n    }\n\n    // pk.vk.cs.num_advice_columns\n    pub fn num_advice_columns(&self) -> usize {\n        self.inner.num_advice_columns()\n    }\n\n    // pk.vk.cs.num_challenges\n    pub fn num_challenges(&self) -> usize {\n        self.inner.num_challenges()\n    }\n\n    // pk.vk.cs.num_instance_columns\n    pub fn num_instance_columns(&self) -> usize {\n        self.inner.num_instance_columns()\n    }\n\n    // pk.vk.cs.phases()\n    pub fn phases(&self) -> Vec<sealed::Phase> {\n        unsafe {\n            let phases: Vec<sealed::Phase> = std::mem::transmute(self.inner.phases());\n            phases\n        }\n    }\n\n    // pk.vk.transcript_repr\n    pub fn transcript_repr<Scheme: CommitmentScheme, P: TachyonProver<Scheme>>(\n        &mut self,\n        prover: &P,\n    ) -> C::Scalar {\n        *unsafe {\n            std::mem::transmute::<_, Box<C::Scalar>>(\n                self.inner.pin_mut().transcript_repr(prover.inner()),\n            )\n        }\n    }\n}\n\npub struct Evals {\n    inner: cxx::UniquePtr<ffi::Evals>,\n}\n\nimpl Evals {\n    pub fn zero() -> Evals {\n        Self::new(ffi::zero_evals())\n    }\n\n    pub fn new(inner: cxx::UniquePtr<ffi::Evals>) -> Evals {\n        Evals { inner }\n    }\n\n    pub fn len(&self) -> usize {\n        self.inner.len()\n    }\n\n    pub fn set_value(&mut self, idx: usize, fr: &halo2curves::bn256::Fr) {\n        let cpp_fr = unsafe { std::mem::transmute(fr) };\n        self.inner.pin_mut().set_value(idx, cpp_fr)\n    }\n}\n\nimpl Clone for Evals {\n    fn clone(&self) -> Self {\n        Self {\n            inner: self.inner.clone(),\n        }\n    }\n}\n\n#[derive(Debug)]\npub struct RationalEvals {\n    inner: cxx::UniquePtr<ffi::RationalEvals>,\n}\n\nimpl RationalEvals {\n    pub fn new(inner: cxx::UniquePtr<ffi::RationalEvals>) -> RationalEvals {\n        RationalEvals { inner }\n    }\n\n    pub fn len(&self) -> usize {\n        self.inner.len()\n    }\n\n    pub fn create_view(&mut self, start: usize, len: usize) -> RationalEvalsView {\n        RationalEvalsView::new(self.inner.pin_mut().create_view(start, len))\n    }\n}\n\nunsafe impl Send for ffi::RationalEvals {}\nunsafe impl Sync for ffi::RationalEvals {}\n\nimpl fmt::Debug for ffi::RationalEvals {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_struct(\"RationalEvals\").finish()\n    }\n}\n\nimpl Clone for RationalEvals {\n    fn clone(&self) -> Self {\n        Self {\n            inner: self.inner.clone(),\n        }\n    }\n}\n\npub struct RationalEvalsView {\n    inner: cxx::UniquePtr<ffi::RationalEvalsView>,\n}\n\nimpl RationalEvalsView {\n    pub fn new(inner: cxx::UniquePtr<ffi::RationalEvalsView>) -> RationalEvalsView {\n        RationalEvalsView { inner }\n    }\n\n    pub fn set_zero(&mut self, idx: usize) {\n        self.inner.pin_mut().set_zero(idx)\n    }\n\n    pub fn set_trivial(&mut self, idx: usize, numerator: &halo2curves::bn256::Fr) {\n        let cpp_numerator = unsafe { std::mem::transmute(numerator) };\n        self.inner.pin_mut().set_trivial(idx, cpp_numerator)\n    }\n\n    pub fn set_rational(\n        &mut self,\n        idx: usize,\n        numerator: &halo2curves::bn256::Fr,\n        denominator: &halo2curves::bn256::Fr,\n    ) {\n        let cpp_numerator = unsafe { std::mem::transmute(numerator) };\n        let cpp_denominator = unsafe { std::mem::transmute(denominator) };\n        self.inner\n            .pin_mut()\n            .set_rational(idx, cpp_numerator, cpp_denominator)\n    }\n\n    pub fn evaluate(&self, idx: usize, value: &mut halo2curves::bn256::Fr) {\n        self.inner\n            .evaluate(idx, unsafe { std::mem::transmute(value) })\n    }\n}\n\nunsafe impl Send for ffi::RationalEvalsView {}\nunsafe impl Sync for ffi::RationalEvalsView {}\n\npub struct Poly {\n    inner: cxx::UniquePtr<ffi::Poly>,\n}\n\nimpl Poly {\n    pub fn new(inner: cxx::UniquePtr<ffi::Poly>) -> Poly {\n        Poly { inner }\n    }\n}\n\npub trait TachyonProver<Scheme: CommitmentScheme> {\n    const QUERY_INSTANCE: bool;\n\n    fn inner(&self) -> &ffi::Prover;\n\n    fn k(&self) -> u32;\n\n    fn n(&self) -> u64;\n\n    fn s_g2(&self) -> &G2Affine;\n\n    fn commit(&self, poly: &Poly) -> <Scheme::Curve as CurveAffine>::CurveExt;\n\n    fn commit_lagrange(&self, evals: &Evals) -> <Scheme::Curve as CurveAffine>::CurveExt;\n\n    fn batch_start(&self, size: usize);\n\n    fn batch_commit(&self, poly: &Poly, idx: usize);\n\n    fn batch_commit_lagrange(&self, evals: &Evals, idx: usize);\n\n    fn batch_end(&self, points: &mut [G1Affine]);\n\n    fn empty_evals(&self) -> Evals;\n\n    fn empty_rational_evals(&self) -> RationalEvals;\n\n    fn batch_evaluate(&self, rational_evals: &[RationalEvals], evals: &mut [Evals]);\n\n    fn ifft(&self, evals: &Evals) -> Poly;\n\n    fn set_rng(&mut self, rng_type: RNGType, state: &[u8]);\n\n    fn set_transcript(&mut self, state: &[u8]);\n\n    fn set_extended_domain(&mut self, pk: &ProvingKey<Scheme::Curve>);\n\n    fn create_proof(\n        &mut self,\n        key: &mut ProvingKey<Scheme::Curve>,\n        instance_singles: &mut [InstanceSingle],\n        advice_singles: &mut [AdviceSingle],\n        challenges: &[Fr],\n    );\n\n    fn get_proof(&self) -> Vec<u8>;\n\n    fn transcript_repr(&self, pk: &mut ProvingKey<Scheme::Curve>) -> Scheme::Scalar;\n}\n\npub struct GWCProver<Scheme: CommitmentScheme> {\n    inner: cxx::UniquePtr<ffi::Prover>,\n    _marker: PhantomData<Scheme>,\n}\n\nimpl<Scheme: CommitmentScheme> GWCProver<Scheme> {\n    pub fn new(transcript_type: u8, k: u32, s: &halo2curves::bn256::Fr) -> GWCProver<Scheme> {\n        let cpp_s = unsafe { std::mem::transmute(s) };\n        GWCProver {\n            inner: ffi::new_prover(PCSType::GWC as u8, transcript_type, k, cpp_s),\n            _marker: PhantomData,\n        }\n    }\n\n    pub fn from_params(transcript_type: u8, k: u32, params: &[u8]) -> GWCProver<Scheme> {\n        GWCProver {\n            inner: ffi::new_prover_from_params(PCSType::GWC as u8, transcript_type, k, params),\n            _marker: PhantomData,\n        }\n    }\n}\n\nimpl<Scheme: CommitmentScheme> TachyonProver<Scheme> for GWCProver<Scheme> {\n    const QUERY_INSTANCE: bool = true;\n\n    fn inner(&self) -> &ffi::Prover {\n        &self.inner\n    }\n\n    fn k(&self) -> u32 {\n        self.inner.k()\n    }\n\n    fn n(&self) -> u64 {\n        self.inner.n()\n    }\n\n    fn s_g2(&self) -> &G2Affine {\n        unsafe { std::mem::transmute(self.inner.s_g2()) }\n    }\n\n    fn commit(&self, poly: &Poly) -> <Scheme::Curve as CurveAffine>::CurveExt {\n        *unsafe {\n            std::mem::transmute::<_, Box<<Scheme::Curve as CurveAffine>::CurveExt>>(\n                self.inner.commit(&poly.inner),\n            )\n        }\n    }\n\n    fn commit_lagrange(&self, evals: &Evals) -> <Scheme::Curve as CurveAffine>::CurveExt {\n        *unsafe {\n            std::mem::transmute::<_, Box<<Scheme::Curve as CurveAffine>::CurveExt>>(\n                self.inner.commit_lagrange(&evals.inner),\n            )\n        }\n    }\n\n    fn batch_start(&self, size: usize) {\n        self.inner.batch_start(size)\n    }\n\n    fn batch_commit(&self, poly: &Poly, idx: usize) {\n        self.inner.batch_commit(&poly.inner, idx)\n    }\n\n    fn batch_commit_lagrange(&self, evals: &Evals, idx: usize) {\n        self.inner.batch_commit_lagrange(&evals.inner, idx)\n    }\n\n    fn batch_end(&self, points: &mut [G1Affine]) {\n        self.inner.batch_end(unsafe { std::mem::transmute(points) })\n    }\n\n    fn empty_evals(&self) -> Evals {\n        Evals::new(self.inner.empty_evals())\n    }\n\n    fn empty_rational_evals(&self) -> RationalEvals {\n        RationalEvals::new(self.inner.empty_rational_evals())\n    }\n\n    fn batch_evaluate(&self, rational_evals: &[RationalEvals], evals: &mut [Evals]) {\n        unsafe {\n            let rational_evals: &[cxx::UniquePtr<ffi::RationalEvals>] =\n                std::mem::transmute(rational_evals);\n            let evals: &mut [cxx::UniquePtr<ffi::Evals>] = std::mem::transmute(evals);\n            self.inner.batch_evaluate(rational_evals, evals)\n        }\n    }\n\n    fn ifft(&self, evals: &Evals) -> Poly {\n        Poly::new(self.inner.ifft(&evals.inner))\n    }\n\n    fn set_rng(&mut self, rng_type: RNGType, state: &[u8]) {\n        self.inner.pin_mut().set_rng(rng_type as u8, state)\n    }\n\n    fn set_transcript(&mut self, state: &[u8]) {\n        self.inner.pin_mut().set_transcript(state)\n    }\n\n    fn set_extended_domain(&mut self, pk: &ProvingKey<Scheme::Curve>) {\n        self.inner.pin_mut().set_extended_domain(&pk.inner)\n    }\n\n    fn create_proof(\n        &mut self,\n        key: &mut ProvingKey<Scheme::Curve>,\n        instance_singles: &mut [InstanceSingle],\n        advice_singles: &mut [AdviceSingle],\n        challenges: &[Fr],\n    ) {\n        self.inner.pin_mut().create_proof(\n            key.inner.pin_mut(),\n            instance_singles,\n            advice_singles,\n            challenges,\n        )\n    }\n\n    fn get_proof(&self) -> Vec<u8> {\n        self.inner.get_proof()\n    }\n\n    fn transcript_repr(\n        &self,\n        pk: &mut ProvingKey<<Scheme as CommitmentScheme>::Curve>,\n    ) -> Scheme::Scalar {\n        pk.transcript_repr(self)\n    }\n}\n\npub struct SHPlonkProver<Scheme: CommitmentScheme> {\n    inner: cxx::UniquePtr<ffi::Prover>,\n    _marker: PhantomData<Scheme>,\n}\n\nimpl<Scheme: CommitmentScheme> SHPlonkProver<Scheme> {\n    pub fn new(transcript_type: u8, k: u32, s: &halo2curves::bn256::Fr) -> SHPlonkProver<Scheme> {\n        let cpp_s = unsafe { std::mem::transmute(s) };\n        SHPlonkProver {\n            inner: ffi::new_prover(PCSType::SHPlonk as u8, transcript_type, k, cpp_s),\n            _marker: PhantomData,\n        }\n    }\n\n    pub fn from_params(transcript_type: u8, k: u32, params: &[u8]) -> SHPlonkProver<Scheme> {\n        SHPlonkProver {\n            inner: ffi::new_prover_from_params(PCSType::SHPlonk as u8, transcript_type, k, params),\n            _marker: PhantomData,\n        }\n    }\n}\n\nimpl<Scheme: CommitmentScheme> TachyonProver<Scheme> for SHPlonkProver<Scheme> {\n    const QUERY_INSTANCE: bool = false;\n\n    fn inner(&self) -> &ffi::Prover {\n        &self.inner\n    }\n\n    fn k(&self) -> u32 {\n        self.inner.k()\n    }\n\n    fn n(&self) -> u64 {\n        self.inner.n()\n    }\n\n    fn s_g2(&self) -> &G2Affine {\n        unsafe { std::mem::transmute(self.inner.s_g2()) }\n    }\n\n    fn commit(&self, poly: &Poly) -> <Scheme::Curve as CurveAffine>::CurveExt {\n        *unsafe {\n            std::mem::transmute::<_, Box<<Scheme::Curve as CurveAffine>::CurveExt>>(\n                self.inner.commit(&poly.inner),\n            )\n        }\n    }\n\n    fn commit_lagrange(&self, evals: &Evals) -> <Scheme::Curve as CurveAffine>::CurveExt {\n        *unsafe {\n            std::mem::transmute::<_, Box<<Scheme::Curve as CurveAffine>::CurveExt>>(\n                self.inner.commit_lagrange(&evals.inner),\n            )\n        }\n    }\n\n    fn batch_start(&self, size: usize) {\n        self.inner.batch_start(size)\n    }\n\n    fn batch_commit(&self, poly: &Poly, idx: usize) {\n        self.inner.batch_commit(&poly.inner, idx)\n    }\n\n    fn batch_commit_lagrange(&self, evals: &Evals, idx: usize) {\n        self.inner.batch_commit_lagrange(&evals.inner, idx)\n    }\n\n    fn batch_end(&self, points: &mut [G1Affine]) {\n        self.inner.batch_end(unsafe { std::mem::transmute(points) })\n    }\n\n    fn empty_evals(&self) -> Evals {\n        Evals::new(self.inner.empty_evals())\n    }\n\n    fn empty_rational_evals(&self) -> RationalEvals {\n        RationalEvals::new(self.inner.empty_rational_evals())\n    }\n\n    fn batch_evaluate(&self, rational_evals: &[RationalEvals], evals: &mut [Evals]) {\n        unsafe {\n            let rational_evals: &[cxx::UniquePtr<ffi::RationalEvals>] =\n                std::mem::transmute(rational_evals);\n            let evals: &mut [cxx::UniquePtr<ffi::Evals>] = std::mem::transmute(evals);\n            self.inner.batch_evaluate(rational_evals, evals)\n        }\n    }\n\n    fn ifft(&self, evals: &Evals) -> Poly {\n        Poly::new(self.inner.ifft(&evals.inner))\n    }\n\n    fn set_rng(&mut self, rng_type: RNGType, state: &[u8]) {\n        self.inner.pin_mut().set_rng(rng_type as u8, state)\n    }\n\n    fn set_transcript(&mut self, state: &[u8]) {\n        self.inner.pin_mut().set_transcript(state)\n    }\n\n    fn set_extended_domain(&mut self, pk: &ProvingKey<Scheme::Curve>) {\n        self.inner.pin_mut().set_extended_domain(&pk.inner)\n    }\n\n    fn create_proof(\n        &mut self,\n        key: &mut ProvingKey<Scheme::Curve>,\n        instance_singles: &mut [InstanceSingle],\n        advice_singles: &mut [AdviceSingle],\n        challenges: &[Fr],\n    ) {\n        self.inner.pin_mut().create_proof(\n            key.inner.pin_mut(),\n            instance_singles,\n            advice_singles,\n            challenges,\n        )\n    }\n\n    fn get_proof(&self) -> Vec<u8> {\n        self.inner.get_proof()\n    }\n\n    fn transcript_repr(\n        &self,\n        pk: &mut ProvingKey<<Scheme as CommitmentScheme>::Curve>,\n    ) -> Scheme::Scalar {\n        pk.transcript_repr(self)\n    }\n}\n"
  },
  {
    "path": "vendors/scroll_halo2/src/bn254_blake2b_writer.cc",
    "content": "#include \"vendors/scroll_halo2/include/bn254_blake2b_writer.h\"\n\n#include <string.h>\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/rs/base/container_util.h\"\n\nnamespace tachyon::halo2_api::bn254 {\n\nBlake2bWriter::Blake2bWriter()\n    : writer_(tachyon_halo2_bn254_transcript_writer_create(\n          TACHYON_HALO2_BLAKE2B_TRANSCRIPT)) {}\n\nBlake2bWriter::~Blake2bWriter() {\n  tachyon_halo2_bn254_transcript_writer_destroy(writer_);\n}\n\nvoid Blake2bWriter::update(rust::Slice<const uint8_t> data) {\n  tachyon_halo2_bn254_transcript_writer_update(writer_, data.data(),\n                                               data.size());\n}\n\nvoid Blake2bWriter::finalize(\n    std::array<uint8_t, BLAKE2B512_DIGEST_LENGTH>& result) {\n  uint8_t data[BLAKE2B512_DIGEST_LENGTH];\n  size_t data_size;\n  tachyon_halo2_bn254_transcript_writer_finalize(writer_, data, &data_size);\n  CHECK_EQ(data_size, size_t{BLAKE2B512_DIGEST_LENGTH});\n  memcpy(result.data(), data, data_size);\n}\n\nrust::Vec<uint8_t> Blake2bWriter::state() const {\n  constexpr size_t kStateSize = sizeof(blake2b_state_st);\n  rust::Vec<uint8_t> ret = rs::CreateDefaultVector<uint8_t>(kStateSize);\n  size_t state_size;\n  tachyon_halo2_bn254_transcript_writer_get_state(writer_, ret.data(),\n                                                  &state_size);\n  CHECK_EQ(state_size, kStateSize);\n  return ret;\n}\n\nstd::unique_ptr<Blake2bWriter> new_blake2b_writer() {\n  return std::make_unique<Blake2bWriter>();\n}\n\n}  // namespace tachyon::halo2_api::bn254\n"
  },
  {
    "path": "vendors/scroll_halo2/src/bn254_evals.cc",
    "content": "#include \"vendors/scroll_halo2/include/bn254_evals.h\"\n\n#include \"vendors/scroll_halo2/src/bn254.rs.h\"\n\nnamespace tachyon::halo2_api::bn254 {\n\nEvals::Evals() : evals_(tachyon_bn254_univariate_evaluations_create()) {}\n\nEvals::~Evals() { tachyon_bn254_univariate_evaluations_destroy(evals_); }\n\nsize_t Evals::len() const {\n  return tachyon_bn254_univariate_evaluations_len(evals_);\n}\n\nvoid Evals::set_value(size_t idx, const Fr& fr) {\n  tachyon_bn254_univariate_evaluations_set_value(\n      evals_, idx, reinterpret_cast<const tachyon_bn254_fr*>(&fr));\n}\n\nstd::unique_ptr<Evals> Evals::clone() const {\n  return std::make_unique<Evals>(\n      tachyon_bn254_univariate_evaluations_clone(evals_));\n}\n\nstd::unique_ptr<Evals> zero_evals() { return std::make_unique<Evals>(); }\n\n}  // namespace tachyon::halo2_api::bn254\n"
  },
  {
    "path": "vendors/scroll_halo2/src/bn254_msm.cc",
    "content": "#include \"vendors/scroll_halo2/include/bn254_msm.h\"\n\n#include <memory>\n\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1_point_type_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/msm.h\"\n#include \"vendors/scroll_halo2/src/bn254.rs.h\"\n\nnamespace tachyon::halo2_api::bn254 {\n\nrust::Box<G1MSM> create_g1_msm(uint8_t degree) {\n  return rust::Box<G1MSM>::from_raw(\n      reinterpret_cast<G1MSM*>(tachyon_bn254_g1_create_msm(degree)));\n}\n\nvoid destroy_g1_msm(rust::Box<G1MSM> msm) {\n  tachyon_bn254_g1_destroy_msm(\n      reinterpret_cast<tachyon_bn254_g1_msm_ptr>(msm.into_raw()));\n}\n\nrust::Box<G1ProjectivePoint> g1_point2_msm(G1MSM* msm,\n                                           rust::Slice<const G1Point2> bases,\n                                           rust::Slice<const Fr> scalars) {\n  std::unique_ptr<tachyon_bn254_g1_jacobian> jacobian(\n      tachyon_bn254_g1_point2_msm(\n          reinterpret_cast<tachyon_bn254_g1_msm_ptr>(msm),\n          reinterpret_cast<const tachyon_bn254_g1_point2*>(bases.data()),\n          reinterpret_cast<const tachyon_bn254_fr*>(scalars.data()),\n          scalars.length()));\n  auto ret = new math::bn254::G1ProjectivePoint(\n      c::base::native_cast(jacobian.get())->ToProjective());\n  return rust::Box<G1ProjectivePoint>::from_raw(\n      reinterpret_cast<G1ProjectivePoint*>(ret));\n}\n\n}  // namespace tachyon::halo2_api::bn254\n"
  },
  {
    "path": "vendors/scroll_halo2/src/bn254_msm_gpu.cc",
    "content": "#include \"vendors/scroll_halo2/include/bn254_msm_gpu.h\"\n\n#include <memory>\n\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/g1_point_type_traits.h\"\n#include \"tachyon/c/math/elliptic_curves/bn/bn254/msm_gpu.h\"\n#include \"vendors/scroll_halo2/src/bn254.rs.h\"\n\nnamespace tachyon::halo2_api::bn254 {\n\nrust::Box<G1MSMGpu> create_g1_msm_gpu(uint8_t degree) {\n  return rust::Box<G1MSMGpu>::from_raw(\n      reinterpret_cast<G1MSMGpu*>(tachyon_bn254_g1_create_msm_gpu(degree)));\n}\n\nvoid destroy_g1_msm_gpu(rust::Box<G1MSMGpu> msm) {\n  tachyon_bn254_g1_destroy_msm_gpu(\n      reinterpret_cast<tachyon_bn254_g1_msm_gpu_ptr>(msm.into_raw()));\n}\n\nrust::Box<G1ProjectivePoint> g1_point2_msm_gpu(\n    G1MSMGpu* msm, rust::Slice<const G1Point2> bases,\n    rust::Slice<const Fr> scalars) {\n  std::unique_ptr<tachyon_bn254_g1_jacobian> jacobian(\n      tachyon_bn254_g1_point2_msm_gpu(\n          reinterpret_cast<tachyon_bn254_g1_msm_gpu_ptr>(msm),\n          reinterpret_cast<const tachyon_bn254_g1_point2*>(bases.data()),\n          reinterpret_cast<const tachyon_bn254_fr*>(scalars.data()),\n          scalars.length()));\n  auto ret = new math::bn254::G1ProjectivePoint(\n      c::base::native_cast(jacobian.get())->ToProjective());\n  return rust::Box<G1ProjectivePoint>::from_raw(\n      reinterpret_cast<G1ProjectivePoint*>(ret));\n}\n\n}  // namespace tachyon::halo2_api::bn254\n"
  },
  {
    "path": "vendors/scroll_halo2/src/bn254_poly.cc",
    "content": "#include \"vendors/scroll_halo2/include/bn254_poly.h\"\n\nnamespace tachyon::halo2_api::bn254 {\n\nPoly::Poly() : poly_(tachyon_bn254_univariate_dense_polynomial_create()) {}\n\nPoly::~Poly() { tachyon_bn254_univariate_dense_polynomial_destroy(poly_); }\n\n}  // namespace tachyon::halo2_api::bn254\n"
  },
  {
    "path": "vendors/scroll_halo2/src/bn254_poseidon_writer.cc",
    "content": "#include \"vendors/scroll_halo2/include/bn254_poseidon_writer.h\"\n\n#include \"tachyon/rs/base/container_util.h\"\n\nnamespace tachyon::halo2_api::bn254 {\n\nPoseidonWriter::PoseidonWriter()\n    : writer_(tachyon_halo2_bn254_transcript_writer_create(\n          TACHYON_HALO2_POSEIDON_TRANSCRIPT)) {}\n\nPoseidonWriter::~PoseidonWriter() {\n  tachyon_halo2_bn254_transcript_writer_destroy(writer_);\n}\n\nvoid PoseidonWriter::update(rust::Slice<const uint8_t> data) {\n  tachyon_halo2_bn254_transcript_writer_update(writer_, data.data(),\n                                               data.size());\n}\n\nrust::Box<Fr> PoseidonWriter::squeeze() {\n  tachyon_bn254_fr* ret = new tachyon_bn254_fr;\n  *ret = tachyon_halo2_bn254_transcript_writer_squeeze(writer_);\n  return rust::Box<Fr>::from_raw(reinterpret_cast<Fr*>(ret));\n}\n\nrust::Vec<uint8_t> PoseidonWriter::state() const {\n  size_t state_size;\n  tachyon_halo2_bn254_transcript_writer_get_state(writer_, nullptr,\n                                                  &state_size);\n  rust::Vec<uint8_t> ret = rs::CreateDefaultVector<uint8_t>(state_size);\n  tachyon_halo2_bn254_transcript_writer_get_state(writer_, ret.data(),\n                                                  &state_size);\n  return ret;\n}\n\nstd::unique_ptr<PoseidonWriter> new_poseidon_writer() {\n  return std::make_unique<PoseidonWriter>();\n}\n\n}  // namespace tachyon::halo2_api::bn254\n"
  },
  {
    "path": "vendors/scroll_halo2/src/bn254_prover.cc",
    "content": "#include \"vendors/scroll_halo2/include/bn254_prover.h\"\n\n#include \"tachyon/base/buffer/buffer.h\"\n#include \"tachyon/c/math/polynomials/univariate/bn254_univariate_evaluation_domain.h\"\n#include \"tachyon/c/zk/plonk/halo2/constants.h\"\n#include \"tachyon/rs/base/container_util.h\"\n#include \"tachyon/rs/base/rust_vec.h\"\n#include \"vendors/scroll_halo2/src/bn254.rs.h\"\n\nnamespace tachyon::halo2_api::bn254 {\n\nProver::Prover(uint8_t pcs_type, uint8_t transcript_type, uint32_t k,\n               const Fr& s)\n    : prover_(tachyon_halo2_bn254_prover_create_from_unsafe_setup(\n          TACHYON_HALO2_SCROLL_VENDOR, pcs_type, transcript_type, k,\n          reinterpret_cast<const tachyon_bn254_fr*>(&s))) {}\n\nProver::Prover(uint8_t pcs_type, uint8_t transcript_type, uint32_t k,\n               const uint8_t* params, size_t params_len)\n    : prover_(tachyon_halo2_bn254_prover_create_from_params(\n          TACHYON_HALO2_SCROLL_VENDOR, pcs_type, transcript_type, k, params,\n          params_len)) {}\n\nProver::~Prover() { tachyon_halo2_bn254_prover_destroy(prover_); }\n\nuint32_t Prover::k() const { return tachyon_halo2_bn254_prover_get_k(prover_); }\n\nuint64_t Prover::n() const {\n  return static_cast<uint64_t>(tachyon_halo2_bn254_prover_get_n(prover_));\n}\n\nconst G2AffinePoint& Prover::s_g2() const {\n  return reinterpret_cast<const G2AffinePoint&>(\n      *tachyon_halo2_bn254_prover_get_s_g2(prover_));\n}\n\nrust::Box<G1ProjectivePoint> Prover::commit(const Poly& poly) const {\n  return rust::Box<G1ProjectivePoint>::from_raw(\n      reinterpret_cast<G1ProjectivePoint*>(\n          tachyon_halo2_bn254_prover_commit(prover_, poly.poly())));\n}\n\nrust::Box<G1ProjectivePoint> Prover::commit_lagrange(const Evals& evals) const {\n  return rust::Box<G1ProjectivePoint>::from_raw(\n      reinterpret_cast<G1ProjectivePoint*>(\n          tachyon_halo2_bn254_prover_commit_lagrange(prover_, evals.evals())));\n}\n\nvoid Prover::batch_start(size_t len) const {\n  tachyon_halo2_bn254_prover_batch_start(prover_, len);\n}\n\nvoid Prover::batch_commit(const Poly& poly, size_t i) const {\n  tachyon_halo2_bn254_prover_batch_commit(prover_, poly.poly(), i);\n}\n\nvoid Prover::batch_commit_lagrange(const Evals& evals, size_t i) const {\n  tachyon_halo2_bn254_prover_batch_commit_lagrange(prover_, evals.evals(), i);\n}\n\nvoid Prover::batch_end(rust::Slice<G1AffinePoint> points) const {\n  tachyon_halo2_bn254_prover_batch_end(\n      prover_,\n      const_cast<tachyon_bn254_g1_affine*>(\n          reinterpret_cast<const tachyon_bn254_g1_affine*>(points.data())),\n      points.size());\n}\n\nstd::unique_ptr<Evals> Prover::empty_evals() const {\n  return std::make_unique<Evals>(\n      tachyon_bn254_univariate_evaluation_domain_empty_evals(\n          tachyon_halo2_bn254_prover_get_domain(prover_)));\n}\n\nstd::unique_ptr<RationalEvals> Prover::empty_rational_evals() const {\n  return std::make_unique<RationalEvals>(\n      tachyon_bn254_univariate_evaluation_domain_empty_rational_evals(\n          tachyon_halo2_bn254_prover_get_domain(prover_)));\n}\n\nstd::unique_ptr<Poly> Prover::ifft(const Evals& evals) const {\n  // NOTE(chokobole): Leading zero values may be removed at this point, so use\n  // this function cautiously. Tachyon currently uses this safely.\n  return std::make_unique<Poly>(tachyon_bn254_univariate_evaluation_domain_ifft(\n      tachyon_halo2_bn254_prover_get_domain(prover_), evals.evals()));\n}\n\nvoid Prover::batch_evaluate(\n    rust::Slice<const std::unique_ptr<RationalEvals>> rational_evals,\n    rust::Slice<std::unique_ptr<Evals>> evals) const {\n  for (size_t i = 0; i < rational_evals.size(); ++i) {\n    evals[i] = std::make_unique<Evals>(\n        tachyon_bn254_univariate_rational_evaluations_batch_evaluate(\n            rational_evals[i]->evals()));\n  }\n}\n\nvoid Prover::set_rng(uint8_t rng_type, rust::Slice<const uint8_t> state) {\n  tachyon_halo2_bn254_prover_set_rng_state(prover_, rng_type, state.data(),\n                                           state.size());\n}\n\nvoid Prover::set_transcript(rust::Slice<const uint8_t> state) {\n  tachyon_halo2_bn254_prover_set_transcript_state(prover_, state.data(),\n                                                  state.size());\n}\n\nvoid Prover::set_extended_domain(const ProvingKey& pk) {\n  tachyon_halo2_bn254_prover_set_extended_domain(prover_, pk.pk());\n}\n\nvoid Prover::create_proof(ProvingKey& key,\n                          rust::Slice<InstanceSingle> instance_singles,\n                          rust::Slice<AdviceSingle> advice_singles,\n                          rust::Slice<const Fr> challenges) {\n  tachyon_bn254_blinder* blinder =\n      tachyon_halo2_bn254_prover_get_blinder(prover_);\n  const tachyon_bn254_plonk_verifying_key* vk =\n      tachyon_bn254_plonk_proving_key_get_verifying_key(key.pk());\n  const tachyon_bn254_plonk_constraint_system* cs =\n      tachyon_bn254_plonk_verifying_key_get_constraint_system(vk);\n  uint32_t blinding_factors =\n      tachyon_bn254_plonk_constraint_system_compute_blinding_factors(cs);\n  tachyon_halo2_bn254_blinder_set_blinding_factors(blinder, blinding_factors);\n\n  size_t num_circuits = instance_singles.size();\n  CHECK_EQ(num_circuits, advice_singles.size())\n      << \"size of |instance_singles| and |advice_singles| don't match\";\n\n  tachyon_halo2_bn254_argument_data* data =\n      tachyon_halo2_bn254_argument_data_create(num_circuits);\n\n  tachyon_halo2_bn254_argument_data_reserve_challenges(data, challenges.size());\n  for (size_t i = 0; i < challenges.size(); ++i) {\n    tachyon_halo2_bn254_argument_data_add_challenge(\n        data, reinterpret_cast<const tachyon_bn254_fr*>(&challenges[i]));\n  }\n\n  size_t num_bytes = base::EstimateSize(rs::RustVec());\n  for (size_t i = 0; i < num_circuits; ++i) {\n    uint8_t* buffer_ptr = reinterpret_cast<uint8_t*>(advice_singles.data());\n    base::Buffer buffer(&buffer_ptr[num_bytes * 2 * i], num_bytes * 2);\n    rs::RustVec vec;\n\n    CHECK(buffer.Read(&vec));\n    size_t num_advice_columns = vec.length;\n    uintptr_t* advice_columns_ptr = reinterpret_cast<uintptr_t*>(vec.ptr);\n    tachyon_halo2_bn254_argument_data_reserve_advice_columns(\n        data, i, num_advice_columns);\n    for (size_t j = 0; j < num_advice_columns; ++j) {\n      tachyon_halo2_bn254_argument_data_add_advice_column(\n          data, i, reinterpret_cast<Evals*>(advice_columns_ptr[j])->release());\n    }\n\n    CHECK(buffer.Read(&vec));\n    size_t num_blinds = vec.length;\n    const tachyon_bn254_fr* blinds_ptr =\n        reinterpret_cast<const tachyon_bn254_fr*>(vec.ptr);\n    tachyon_halo2_bn254_argument_data_reserve_advice_blinds(data, i,\n                                                            num_blinds);\n    for (size_t j = 0; j < num_blinds; ++j) {\n      tachyon_halo2_bn254_argument_data_add_advice_blind(data, i,\n                                                         &blinds_ptr[j]);\n    }\n\n    buffer_ptr = reinterpret_cast<uint8_t*>(instance_singles.data());\n    buffer = base::Buffer(&buffer_ptr[num_bytes * 2 * i], num_bytes * 2);\n\n    CHECK(buffer.Read(&vec));\n    size_t num_instance_columns = vec.length;\n    uintptr_t* instance_columns_ptr = reinterpret_cast<uintptr_t*>(vec.ptr);\n    tachyon_halo2_bn254_argument_data_reserve_instance_columns(\n        data, i, num_instance_columns);\n    for (size_t j = 0; j < num_instance_columns; ++j) {\n      tachyon_halo2_bn254_argument_data_add_instance_column(\n          data, i,\n          reinterpret_cast<Evals*>(instance_columns_ptr[j])->release());\n    }\n\n    CHECK(buffer.Read(&vec));\n    CHECK_EQ(num_instance_columns, vec.length)\n        << \"size of instance columns don't match\";\n    uintptr_t* instance_poly_ptr = reinterpret_cast<uintptr_t*>(vec.ptr);\n    tachyon_halo2_bn254_argument_data_reserve_instance_polys(\n        data, i, num_instance_columns);\n    for (size_t j = 0; j < num_instance_columns; ++j) {\n      tachyon_halo2_bn254_argument_data_add_instance_poly(\n          data, i, reinterpret_cast<Poly*>(instance_poly_ptr[j])->release());\n    }\n\n    CHECK(buffer.Done());\n  }\n\n  tachyon_halo2_bn254_prover_create_proof(prover_, key.pk(), data);\n  tachyon_halo2_bn254_argument_data_destroy(data);\n}\n\nrust::Vec<uint8_t> Prover::get_proof() const {\n  size_t proof_len;\n  tachyon_halo2_bn254_prover_get_proof(prover_, nullptr, &proof_len);\n  rust::Vec<uint8_t> proof = rs::CreateDefaultVector<uint8_t>(proof_len);\n  tachyon_halo2_bn254_prover_get_proof(prover_, proof.data(), &proof_len);\n  return proof;\n}\n\nstd::unique_ptr<Prover> new_prover(uint8_t pcs_type, uint8_t transcript_type,\n                                   uint32_t k, const Fr& s) {\n  return std::make_unique<Prover>(pcs_type, transcript_type, k, s);\n}\n\nstd::unique_ptr<Prover> new_prover_from_params(\n    uint8_t pcs_type, uint8_t transcript_type, uint32_t k,\n    rust::Slice<const uint8_t> params) {\n  return std::make_unique<Prover>(pcs_type, transcript_type, k, params.data(),\n                                  params.size());\n}\n\nrust::Box<Fr> ProvingKey::transcript_repr(const Prover& prover) {\n  tachyon_halo2_bn254_prover_set_transcript_repr(prover.prover(), pk_);\n  tachyon_bn254_fr* ret = new tachyon_bn254_fr;\n  tachyon_bn254_fr repr = tachyon_bn254_plonk_verifying_key_get_transcript_repr(\n      tachyon_bn254_plonk_proving_key_get_verifying_key(pk_));\n  memcpy(ret->limbs, repr.limbs, sizeof(uint64_t) * 4);\n  return rust::Box<Fr>::from_raw(reinterpret_cast<Fr*>(ret));\n}\n\n}  // namespace tachyon::halo2_api::bn254\n"
  },
  {
    "path": "vendors/scroll_halo2/src/bn254_proving_key.cc",
    "content": "#include \"vendors/scroll_halo2/include/bn254_proving_key.h\"\n\n#include \"tachyon/c/zk/plonk/halo2/constants.h\"\n#include \"vendors/scroll_halo2/src/bn254.rs.h\"\n\nnamespace tachyon::halo2_api::bn254 {\n\nnamespace {\n\nusing GetPhasesAPI = void (*)(const tachyon_bn254_plonk_constraint_system*,\n                              tachyon_phase*, size_t*);\nusing GetFixedColumnsAPI =\n    void (*)(const tachyon_bn254_plonk_constraint_system*,\n             tachyon_fixed_column_key*, size_t*);\n\nrust::Vec<uint8_t> DoGetPhases(const tachyon_bn254_plonk_constraint_system* cs,\n                               GetPhasesAPI api) {\n  static_assert(sizeof(uint8_t) == sizeof(tachyon_phase));\n  rust::Vec<uint8_t> phases;\n  size_t phases_len;\n  api(cs, nullptr, &phases_len);\n  phases.reserve(phases_len);\n  for (size_t i = 0; i < phases_len; ++i) {\n    phases.push_back(0);\n  }\n  api(cs, reinterpret_cast<tachyon_phase*>(phases.data()), &phases_len);\n  return phases;\n}\n\nrust::Vec<size_t> GetFixedColumns(\n    const tachyon_bn254_plonk_constraint_system* cs, GetFixedColumnsAPI api) {\n  static_assert(sizeof(size_t) == sizeof(tachyon_fixed_column_key));\n  rust::Vec<size_t> fixed_columns;\n  size_t fixed_columns_len;\n  api(cs, nullptr, &fixed_columns_len);\n  fixed_columns.reserve(fixed_columns_len);\n  for (size_t i = 0; i < fixed_columns_len; ++i) {\n    fixed_columns.push_back(0);\n  }\n  api(cs, reinterpret_cast<tachyon_fixed_column_key*>(fixed_columns.data()),\n      &fixed_columns_len);\n  return fixed_columns;\n}\n\n}  // namespace\n\nProvingKey::ProvingKey(uint8_t pcs_type, rust::Slice<const uint8_t> pk_bytes)\n    : pk_(tachyon_bn254_plonk_proving_key_create_from_state(\n          TACHYON_HALO2_SCROLL_VENDOR, pcs_type, pk_bytes.data(),\n          pk_bytes.size())) {}\n\nProvingKey::~ProvingKey() { tachyon_bn254_plonk_proving_key_destroy(pk_); }\n\nrust::Vec<uint8_t> ProvingKey::advice_column_phases() const {\n  return DoGetPhases(\n      GetConstraintSystem(),\n      &tachyon_bn254_plonk_constraint_system_get_advice_column_phases);\n}\n\nuint32_t ProvingKey::blinding_factors() const {\n  return tachyon_bn254_plonk_constraint_system_compute_blinding_factors(\n      GetConstraintSystem());\n}\n\nrust::Vec<uint8_t> ProvingKey::challenge_phases() const {\n  return DoGetPhases(\n      GetConstraintSystem(),\n      &tachyon_bn254_plonk_constraint_system_get_challenge_phases);\n}\n\nrust::Vec<size_t> ProvingKey::constants() const {\n  return GetFixedColumns(GetConstraintSystem(),\n                         &tachyon_bn254_plonk_constraint_system_get_constants);\n}\n\nsize_t ProvingKey::num_advice_columns() const {\n  return tachyon_bn254_plonk_constraint_system_get_num_advice_columns(\n      GetConstraintSystem());\n}\n\nsize_t ProvingKey::num_challenges() const {\n  return tachyon_bn254_plonk_constraint_system_get_num_challenges(\n      GetConstraintSystem());\n}\n\nsize_t ProvingKey::num_instance_columns() const {\n  return tachyon_bn254_plonk_constraint_system_get_num_instance_columns(\n      GetConstraintSystem());\n}\n\nrust::Vec<uint8_t> ProvingKey::phases() const {\n  return DoGetPhases(GetConstraintSystem(),\n                     &tachyon_bn254_plonk_constraint_system_get_phases);\n}\n\nconst tachyon_bn254_plonk_verifying_key* ProvingKey::GetVerifyingKey() const {\n  return tachyon_bn254_plonk_proving_key_get_verifying_key(pk_);\n}\n\nconst tachyon_bn254_plonk_constraint_system* ProvingKey::GetConstraintSystem()\n    const {\n  return tachyon_bn254_plonk_verifying_key_get_constraint_system(\n      GetVerifyingKey());\n}\n\nstd::unique_ptr<ProvingKey> new_proving_key(\n    uint8_t pcs_type, rust::Slice<const uint8_t> pk_bytes) {\n  return std::make_unique<ProvingKey>(pcs_type, pk_bytes);\n}\n\n}  // namespace tachyon::halo2_api::bn254\n"
  },
  {
    "path": "vendors/scroll_halo2/src/bn254_rational_evals.cc",
    "content": "#include \"vendors/scroll_halo2/include/bn254_rational_evals.h\"\n\n#include \"vendors/scroll_halo2/include/bn254_rational_evals_view.h\"\n\nnamespace tachyon::halo2_api::bn254 {\n\nRationalEvals::RationalEvals()\n    : evals_(tachyon_bn254_univariate_rational_evaluations_create()) {}\n\nRationalEvals::~RationalEvals() {\n  tachyon_bn254_univariate_rational_evaluations_destroy(evals_);\n}\n\nsize_t RationalEvals::len() const {\n  return tachyon_bn254_univariate_rational_evaluations_len(evals_);\n}\n\nstd::unique_ptr<RationalEvalsView> RationalEvals::create_view(size_t start,\n                                                              size_t len) {\n  return std::make_unique<RationalEvalsView>(evals_, start, len);\n}\n\nstd::unique_ptr<RationalEvals> RationalEvals::clone() const {\n  return std::make_unique<RationalEvals>(\n      tachyon_bn254_univariate_rational_evaluations_clone(evals_));\n}\n\n}  // namespace tachyon::halo2_api::bn254\n"
  },
  {
    "path": "vendors/scroll_halo2/src/bn254_rational_evals_view.cc",
    "content": "#include \"vendors/scroll_halo2/include/bn254_rational_evals_view.h\"\n\n#include \"tachyon/base/logging.h\"\n#include \"vendors/scroll_halo2/src/bn254.rs.h\"\n\nnamespace tachyon::halo2_api::bn254 {\n\nRationalEvalsView::RationalEvalsView(\n    tachyon_bn254_univariate_rational_evaluations* evals, size_t start,\n    size_t len)\n    : evals_(evals), start_(start), len_(len) {\n  CHECK_GT(len, size_t{0});\n}\n\nvoid RationalEvalsView::set_zero(size_t idx) {\n  CHECK_LT(start_ + idx, len_);\n  tachyon_bn254_univariate_rational_evaluations_set_zero(evals_, start_ + idx);\n}\n\nvoid RationalEvalsView::set_trivial(size_t idx, const Fr& numerator) {\n  CHECK_LT(start_ + idx, len_);\n  tachyon_bn254_univariate_rational_evaluations_set_trivial(\n      evals_, start_ + idx,\n      reinterpret_cast<const tachyon_bn254_fr*>(&numerator));\n}\n\nvoid RationalEvalsView::set_rational(size_t idx, const Fr& numerator,\n                                     const Fr& denominator) {\n  CHECK_LT(start_ + idx, len_);\n  tachyon_bn254_univariate_rational_evaluations_set_rational(\n      evals_, start_ + idx,\n      reinterpret_cast<const tachyon_bn254_fr*>(&numerator),\n      reinterpret_cast<const tachyon_bn254_fr*>(&denominator));\n}\n\nvoid RationalEvalsView::evaluate(size_t idx, Fr& value) const {\n  CHECK_LT(start_ + idx, len_);\n  tachyon_bn254_univariate_rational_evaluations_evaluate(\n      evals_, start_ + idx, reinterpret_cast<tachyon_bn254_fr*>(&value));\n}\n\n}  // namespace tachyon::halo2_api::bn254\n"
  },
  {
    "path": "vendors/scroll_halo2/src/bn254_sha256_writer.cc",
    "content": "#include \"vendors/scroll_halo2/include/bn254_sha256_writer.h\"\n\n#include <string.h>\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/rs/base/container_util.h\"\n\nnamespace tachyon::halo2_api::bn254 {\n\nSha256Writer::Sha256Writer()\n    : writer_(tachyon_halo2_bn254_transcript_writer_create(\n          TACHYON_HALO2_SHA256_TRANSCRIPT)) {}\n\nSha256Writer::~Sha256Writer() {\n  tachyon_halo2_bn254_transcript_writer_destroy(writer_);\n}\n\nvoid Sha256Writer::update(rust::Slice<const uint8_t> data) {\n  tachyon_halo2_bn254_transcript_writer_update(writer_, data.data(),\n                                               data.size());\n}\n\nvoid Sha256Writer::finalize(std::array<uint8_t, SHA256_DIGEST_LENGTH>& result) {\n  uint8_t data[SHA256_DIGEST_LENGTH];\n  size_t data_size;\n  tachyon_halo2_bn254_transcript_writer_finalize(writer_, data, &data_size);\n  CHECK_EQ(data_size, size_t{SHA256_DIGEST_LENGTH});\n  memcpy(result.data(), data, data_size);\n}\n\nrust::Vec<uint8_t> Sha256Writer::state() const {\n  constexpr size_t kStateSize = sizeof(sha256_state_st);\n  rust::Vec<uint8_t> ret = rs::CreateDefaultVector<uint8_t>(kStateSize);\n  size_t state_size;\n  tachyon_halo2_bn254_transcript_writer_get_state(writer_, ret.data(),\n                                                  &state_size);\n  CHECK_EQ(state_size, kStateSize);\n  return ret;\n}\n\nstd::unique_ptr<Sha256Writer> new_sha256_writer() {\n  return std::make_unique<Sha256Writer>();\n}\n\n}  // namespace tachyon::halo2_api::bn254\n"
  },
  {
    "path": "vendors/scroll_halo2/src/bn254_snark_verifier_poseidon_writer.cc",
    "content": "#include \"vendors/scroll_halo2/include/bn254_snark_verifier_poseidon_writer.h\"\n\n#include \"tachyon/rs/base/container_util.h\"\n\nnamespace tachyon::halo2_api::bn254 {\n\nSnarkVerifierPoseidonWriter::SnarkVerifierPoseidonWriter()\n    : writer_(tachyon_halo2_bn254_transcript_writer_create(\n          TACHYON_HALO2_SNARK_VERIFIER_POSEIDON_TRANSCRIPT)) {}\n\nSnarkVerifierPoseidonWriter::~SnarkVerifierPoseidonWriter() {\n  tachyon_halo2_bn254_transcript_writer_destroy(writer_);\n}\n\nvoid SnarkVerifierPoseidonWriter::update(rust::Slice<const uint8_t> data) {\n  tachyon_halo2_bn254_transcript_writer_update(writer_, data.data(),\n                                               data.size());\n}\n\nrust::Box<Fr> SnarkVerifierPoseidonWriter::squeeze() {\n  tachyon_bn254_fr* ret = new tachyon_bn254_fr;\n  *ret = tachyon_halo2_bn254_transcript_writer_squeeze(writer_);\n  return rust::Box<Fr>::from_raw(reinterpret_cast<Fr*>(ret));\n}\n\nrust::Vec<uint8_t> SnarkVerifierPoseidonWriter::state() const {\n  size_t state_size;\n  tachyon_halo2_bn254_transcript_writer_get_state(writer_, nullptr,\n                                                  &state_size);\n  rust::Vec<uint8_t> ret = rs::CreateDefaultVector<uint8_t>(state_size);\n  tachyon_halo2_bn254_transcript_writer_get_state(writer_, ret.data(),\n                                                  &state_size);\n  return ret;\n}\n\nstd::unique_ptr<SnarkVerifierPoseidonWriter>\nnew_snark_verifier_poseidon_writer() {\n  return std::make_unique<SnarkVerifierPoseidonWriter>();\n}\n\n}  // namespace tachyon::halo2_api::bn254\n"
  },
  {
    "path": "vendors/scroll_halo2/src/cha_cha20_rng.cc",
    "content": "#include \"vendors/scroll_halo2/include/cha_cha20_rng.h\"\n\n#include <string.h>\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/rs/base/container_util.h\"\n\nnamespace tachyon::halo2_api {\n\nChaCha20Rng::ChaCha20Rng(std::array<uint8_t, kSeedSize> seed) {\n  uint8_t seed_copy[kSeedSize];\n  memcpy(seed_copy, seed.data(), kSeedSize);\n  rng_ =\n      tachyon_rng_create_from_seed(TACHYON_RNG_CHA_CHA20, seed_copy, kSeedSize);\n}\n\nChaCha20Rng::~ChaCha20Rng() { tachyon_rng_destroy(rng_); }\n\nuint32_t ChaCha20Rng::next_u32() { return tachyon_rng_get_next_u32(rng_); }\n\nstd::unique_ptr<ChaCha20Rng> ChaCha20Rng::clone() const {\n  uint8_t state[kStateSize];\n  size_t state_len;\n  tachyon_rng_get_state(rng_, state, &state_len);\n  CHECK_EQ(state_len, kStateSize);\n  tachyon_rng* rng =\n      tachyon_rng_create_from_state(TACHYON_RNG_CHA_CHA20, state, kStateSize);\n  return std::make_unique<ChaCha20Rng>(rng);\n}\n\nrust::Vec<uint8_t> ChaCha20Rng::state() const {\n  rust::Vec<uint8_t> ret = rs::CreateDefaultVector<uint8_t>(kStateSize);\n  size_t state_len;\n  tachyon_rng_get_state(rng_, ret.data(), &state_len);\n  CHECK_EQ(state_len, kStateSize);\n  return ret;\n}\n\nstd::unique_ptr<ChaCha20Rng> new_cha_cha20_rng(\n    std::array<uint8_t, ChaCha20Rng::kSeedSize> seed) {\n  return std::make_unique<ChaCha20Rng>(seed);\n}\n\n}  // namespace tachyon::halo2_api\n"
  },
  {
    "path": "vendors/scroll_halo2/src/cha_cha20_rng.rs",
    "content": "use crate::{consts::RNGType, rng::SerializableRng};\n\n#[cxx::bridge(namespace = \"tachyon::halo2_api\")]\npub mod ffi {\n    unsafe extern \"C++\" {\n        include!(\"vendors/scroll_halo2/include/cha_cha20_rng.h\");\n\n        type ChaCha20Rng;\n\n        fn new_cha_cha20_rng(seed: [u8; 32]) -> UniquePtr<ChaCha20Rng>;\n        fn next_u32(self: Pin<&mut ChaCha20Rng>) -> u32;\n        fn clone(&self) -> UniquePtr<ChaCha20Rng>;\n        fn state(&self) -> Vec<u8>;\n    }\n}\n\npub struct ChaCha20Rng {\n    inner: cxx::UniquePtr<ffi::ChaCha20Rng>,\n}\n\nimpl SerializableRng for ChaCha20Rng {\n    fn state(&self) -> Vec<u8> {\n        self.inner.state()\n    }\n\n    fn rng_type() -> RNGType {\n        RNGType::ChaCha20\n    }\n}\n\nimpl Clone for ChaCha20Rng {\n    fn clone(&self) -> Self {\n        Self {\n            inner: self.inner.clone(),\n        }\n    }\n}\n\nimpl rand_core::SeedableRng for ChaCha20Rng {\n    type Seed = [u8; 32];\n\n    fn from_seed(seed: Self::Seed) -> Self {\n        Self {\n            inner: ffi::new_cha_cha20_rng(seed),\n        }\n    }\n}\n\nimpl rand_core::RngCore for ChaCha20Rng {\n    fn next_u32(&mut self) -> u32 {\n        self.inner.pin_mut().next_u32()\n    }\n\n    #[inline]\n    fn next_u64(&mut self) -> u64 {\n        rand_core::impls::next_u64_via_u32(self)\n    }\n\n    #[inline]\n    fn fill_bytes(&mut self, dest: &mut [u8]) {\n        rand_core::impls::fill_bytes_via_next(self, dest)\n    }\n\n    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> {\n        self.fill_bytes(dest);\n        Ok(())\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use rand_core::{RngCore, SeedableRng};\n\n    use crate::{consts::CHA_CHA20_SEED, rng::SerializableRng};\n\n    #[test]\n    fn test_rng() {\n        let mut rng = rand_chacha::ChaCha20Rng::from_seed(CHA_CHA20_SEED);\n        let mut rng_tachyon = crate::cha_cha20_rng::ChaCha20Rng::from_seed(CHA_CHA20_SEED);\n\n        const LEN: i32 = 100;\n        let random_u64s = (0..LEN).map(|_| rng.next_u64()).collect::<Vec<_>>();\n        let random_u64s_tachyon = (0..LEN).map(|_| rng_tachyon.next_u64()).collect::<Vec<_>>();\n        assert_eq!(random_u64s, random_u64s_tachyon);\n    }\n\n    #[test]\n    fn test_clone() {\n        let mut rng = crate::cha_cha20_rng::ChaCha20Rng::from_seed(CHA_CHA20_SEED);\n        let mut rng_clone = rng.clone();\n\n        const LEN: i32 = 100;\n        let random_u64s = (0..LEN).map(|_| rng.next_u64()).collect::<Vec<_>>();\n        let random_u64s_clone = (0..LEN).map(|_| rng_clone.next_u64()).collect::<Vec<_>>();\n        assert_eq!(random_u64s, random_u64s_clone);\n    }\n\n    #[test]\n    fn test_state() {\n        let rng = crate::cha_cha20_rng::ChaCha20Rng::from_seed(CHA_CHA20_SEED);\n        assert_eq!(\n            rng.state(),\n            vec![\n                16, 0, 0, 0, 0, 0, 0, 0, 101, 120, 112, 97, 110, 100, 32, 51, 50, 45, 98, 121, 116,\n                101, 32, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                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,\n                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,\n                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,\n                0, 0, 0, 0, 0\n            ]\n        );\n    }\n}\n"
  },
  {
    "path": "vendors/scroll_halo2/src/circuits/mod.rs",
    "content": "pub mod shuffle_circuit;\npub mod simple_circuit;\npub mod simple_lookup_circuit;\n"
  },
  {
    "path": "vendors/scroll_halo2/src/circuits/shuffle_circuit.rs",
    "content": "use std::iter;\n\nuse ff::BatchInvert;\nuse halo2_proofs::{\n    arithmetic::Field,\n    circuit::{Layouter, SimpleFloorPlanner, Value},\n    plonk::{\n        Advice, Challenge, Circuit, Column, ConstraintSystem, Error, Expression, FirstPhase,\n        SecondPhase, Selector,\n    },\n    poly::Rotation,\n};\nuse rand_core::RngCore;\n\nfn rand_2d_array<F: Field, R: RngCore, const W: usize, const H: usize>(rng: &mut R) -> [[F; H]; W] {\n    [(); W].map(|_| [(); H].map(|_| F::random(&mut *rng)))\n}\n\nfn shuffled<F: Field, R: RngCore, const W: usize, const H: usize>(\n    original: [[F; H]; W],\n    rng: &mut R,\n) -> [[F; H]; W] {\n    let mut shuffled = original;\n\n    for row in (1..H).rev() {\n        let rand_row = (rng.next_u32() as usize) % row;\n        for column in shuffled.iter_mut() {\n            column.swap(row, rand_row);\n        }\n    }\n\n    shuffled\n}\n\n#[derive(Clone)]\nstruct MyConfig<const W: usize> {\n    q_shuffle: Selector,\n    q_first: Selector,\n    q_last: Selector,\n    original: [Column<Advice>; W],\n    shuffled: [Column<Advice>; W],\n    theta: Challenge,\n    gamma: Challenge,\n    z: Column<Advice>,\n}\n\nimpl<const W: usize> MyConfig<W> {\n    fn configure<F: Field>(meta: &mut ConstraintSystem<F>) -> Self {\n        let [q_shuffle, q_first, q_last] = [(); 3].map(|_| meta.selector());\n        // First phase\n        let original = [(); W].map(|_| meta.advice_column_in(FirstPhase));\n        let shuffled = [(); W].map(|_| meta.advice_column_in(FirstPhase));\n        let [theta, gamma] = [(); 2].map(|_| meta.challenge_usable_after(FirstPhase));\n        // Second phase\n        let z = meta.advice_column_in(SecondPhase);\n\n        meta.create_gate(\"z should start with 1\", |meta| {\n            let q_first = meta.query_selector(q_first);\n            let z = meta.query_advice(z, Rotation::cur());\n            let one = Expression::Constant(F::ONE);\n\n            vec![q_first * (one - z)]\n        });\n\n        meta.create_gate(\"z should end with 1\", |meta| {\n            let q_last = meta.query_selector(q_last);\n            let z = meta.query_advice(z, Rotation::cur());\n            let one = Expression::Constant(F::ONE);\n\n            vec![q_last * (one - z)]\n        });\n\n        meta.create_gate(\"z should have valid transition\", |meta| {\n            let q_shuffle = meta.query_selector(q_shuffle);\n            let original = original.map(|advice| meta.query_advice(advice, Rotation::cur()));\n            let shuffled = shuffled.map(|advice| meta.query_advice(advice, Rotation::cur()));\n            let [theta, gamma] = [theta, gamma].map(|challenge| meta.query_challenge(challenge));\n            let [z, z_w] =\n                [Rotation::cur(), Rotation::next()].map(|rotation| meta.query_advice(z, rotation));\n\n            // Compress\n            let original = original\n                .iter()\n                .cloned()\n                .reduce(|acc, a| acc * theta.clone() + a)\n                .unwrap();\n            let shuffled = shuffled\n                .iter()\n                .cloned()\n                .reduce(|acc, a| acc * theta.clone() + a)\n                .unwrap();\n\n            vec![q_shuffle * (z * (original + gamma.clone()) - z_w * (shuffled + gamma))]\n        });\n\n        Self {\n            q_shuffle,\n            q_first,\n            q_last,\n            original,\n            shuffled,\n            theta,\n            gamma,\n            z,\n        }\n    }\n}\n\n#[derive(Clone, Default)]\nstruct MyCircuit<F: Field, const W: usize, const H: usize> {\n    original: Value<[[F; H]; W]>,\n    shuffled: Value<[[F; H]; W]>,\n}\n\nimpl<F: Field, const W: usize, const H: usize> MyCircuit<F, W, H> {\n    fn rand<R: RngCore>(rng: &mut R) -> Self {\n        let original = rand_2d_array::<F, _, W, H>(rng);\n        let shuffled = shuffled(original, rng);\n\n        Self {\n            original: Value::known(original),\n            shuffled: Value::known(shuffled),\n        }\n    }\n}\n\nimpl<F: Field, const W: usize, const H: usize> Circuit<F> for MyCircuit<F, W, H> {\n    type Config = MyConfig<W>;\n    type FloorPlanner = SimpleFloorPlanner;\n\n    fn without_witnesses(&self) -> Self {\n        Self::default()\n    }\n\n    fn configure(meta: &mut ConstraintSystem<F>) -> Self::Config {\n        MyConfig::configure(meta)\n    }\n\n    fn synthesize(\n        &self,\n        config: Self::Config,\n        mut layouter: impl Layouter<F>,\n    ) -> Result<(), Error> {\n        let theta = layouter.get_challenge(config.theta);\n        let gamma = layouter.get_challenge(config.gamma);\n\n        layouter.assign_region(\n            || \"Shuffle original into shuffled\",\n            |mut region| {\n                // Keygen\n                config.q_first.enable(&mut region, 0)?;\n                config.q_last.enable(&mut region, H)?;\n                for offset in 0..H {\n                    config.q_shuffle.enable(&mut region, offset)?;\n                }\n\n                // First phase\n                for (idx, (&column, values)) in config\n                    .original\n                    .iter()\n                    .zip(self.original.transpose_array().iter())\n                    .enumerate()\n                {\n                    for (offset, &value) in values.transpose_array().iter().enumerate() {\n                        region.assign_advice(\n                            || format!(\"original[{}][{}]\", idx, offset),\n                            column,\n                            offset,\n                            || value,\n                        )?;\n                    }\n                }\n                for (idx, (&column, values)) in config\n                    .shuffled\n                    .iter()\n                    .zip(self.shuffled.transpose_array().iter())\n                    .enumerate()\n                {\n                    for (offset, &value) in values.transpose_array().iter().enumerate() {\n                        region.assign_advice(\n                            || format!(\"shuffled[{}][{}]\", idx, offset),\n                            column,\n                            offset,\n                            || value,\n                        )?;\n                    }\n                }\n\n                // Second phase\n                let z = self.original.zip(self.shuffled).zip(theta).zip(gamma).map(\n                    |(((original, shuffled), theta), gamma)| {\n                        let mut product = vec![F::ZERO; H];\n                        for (idx, product) in product.iter_mut().enumerate() {\n                            let mut compressed = F::ZERO;\n                            for value in shuffled.iter() {\n                                compressed *= theta;\n                                compressed += value[idx];\n                            }\n\n                            *product = compressed + gamma\n                        }\n\n                        product.iter_mut().batch_invert();\n\n                        for (idx, product) in product.iter_mut().enumerate() {\n                            let mut compressed = F::ZERO;\n                            for value in original.iter() {\n                                compressed *= theta;\n                                compressed += value[idx];\n                            }\n\n                            *product *= compressed + gamma\n                        }\n\n                        #[allow(clippy::let_and_return)]\n                        let z = iter::once(F::ONE)\n                            .chain(product)\n                            .scan(F::ONE, |state, cur| {\n                                *state *= &cur;\n                                Some(*state)\n                            })\n                            .collect::<Vec<_>>();\n\n                        #[cfg(feature = \"sanity-checks\")]\n                        assert_eq!(F::ONE, *z.last().unwrap());\n\n                        z\n                    },\n                );\n                for (offset, value) in z.transpose_vec(H + 1).into_iter().enumerate() {\n                    region.assign_advice(\n                        || format!(\"z[{}]\", offset),\n                        config.z,\n                        offset,\n                        || value,\n                    )?;\n                }\n\n                Ok(())\n            },\n        )\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use halo2_proofs::{\n        plonk::{create_proof, keygen_pk2},\n        poly::kzg::{\n            commitment::{KZGCommitmentScheme, ParamsKZG},\n            multiopen::{ProverGWC, ProverSHPLONK},\n        },\n        transcript::{Blake2bWrite, Challenge255, PoseidonWrite, TranscriptWriterBuffer},\n    };\n    use halo2curves::bn256::{Bn256, Fr};\n    use rand_core::SeedableRng;\n\n    use crate::{\n        bn254::{\n            Blake2bWrite as TachyonBlake2bWrite, GWCProver, PoseidonWrite as TachyonPoseidonWrite,\n            ProvingKey as TachyonProvingKey, SHPlonkProver, Sha256Write as TachyonSha256Write,\n            TachyonProver,\n        },\n        cha_cha20_rng::ChaCha20Rng,\n        consts::{PCSType, TranscriptType, CHA_CHA20_SEED, XOR_SHIFT_SEED},\n        prover::create_proof as tachyon_create_proof,\n        sha::ShaWrite,\n        xor_shift_rng::XORShiftRng,\n    };\n\n    use super::MyCircuit;\n\n    const W: usize = 2;\n    const H: usize = 8;\n    const K: u32 = 4;\n\n    #[test]\n    fn test_create_gwc_proof_with_various_rngs() {\n        let mut rng_for_table = XORShiftRng::from_seed(XOR_SHIFT_SEED);\n        let circuit = MyCircuit::<Fr, W, H>::rand(&mut rng_for_table);\n\n        let s = Fr::from(2);\n        let params = ParamsKZG::<Bn256>::unsafe_setup_with_s(K, s);\n        let pk = keygen_pk2(&params, &circuit).unwrap();\n        let mut pk_bytes: Vec<u8> = vec![];\n        pk.write_including_cs(&mut pk_bytes).unwrap();\n\n        let rng = XORShiftRng::from_seed(XOR_SHIFT_SEED);\n\n        let halo2_proof = {\n            let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]);\n\n            create_proof::<KZGCommitmentScheme<Bn256>, ProverGWC<_>, _, _, _, _>(\n                &params,\n                &pk,\n                &[circuit.clone(), circuit.clone()],\n                &[&[], &[]],\n                rng.clone(),\n                &mut transcript,\n            )\n            .expect(\"proof generation should not fail\");\n\n            transcript.finalize()\n        };\n\n        let tachyon_proof = {\n            let mut prover =\n                GWCProver::<KZGCommitmentScheme<Bn256>>::new(TranscriptType::Blake2b as u8, K, &s);\n\n            let mut tachyon_pk = TachyonProvingKey::from(PCSType::GWC, pk_bytes.as_slice());\n            let mut transcript = TachyonBlake2bWrite::init(vec![]);\n\n            tachyon_create_proof::<_, _, _, _, _, _>(\n                &mut prover,\n                &mut tachyon_pk,\n                &[circuit.clone(), circuit.clone()],\n                &[&[], &[]],\n                pk.fixed_values.clone(),\n                rng,\n                &mut transcript,\n            )\n            .expect(\"proof generation should not fail\");\n\n            let mut proof = transcript.finalize();\n            let proof_last = prover.get_proof();\n            proof.extend_from_slice(&proof_last);\n            proof\n        };\n        assert_eq!(halo2_proof, tachyon_proof);\n\n        let rng = ChaCha20Rng::from_seed(CHA_CHA20_SEED);\n\n        let halo2_proof = {\n            let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]);\n\n            create_proof::<KZGCommitmentScheme<Bn256>, ProverGWC<_>, _, _, _, _>(\n                &params,\n                &pk,\n                &[circuit.clone(), circuit.clone()],\n                &[&[], &[]],\n                rng.clone(),\n                &mut transcript,\n            )\n            .expect(\"proof generation should not fail\");\n\n            transcript.finalize()\n        };\n\n        let tachyon_proof = {\n            let mut prover =\n                GWCProver::<KZGCommitmentScheme<Bn256>>::new(TranscriptType::Blake2b as u8, K, &s);\n\n            let mut tachyon_pk = TachyonProvingKey::from(PCSType::GWC, pk_bytes.as_slice());\n            let mut transcript = TachyonBlake2bWrite::init(vec![]);\n\n            tachyon_create_proof::<_, _, _, _, _, _>(\n                &mut prover,\n                &mut tachyon_pk,\n                &[circuit.clone(), circuit],\n                &[&[], &[]],\n                pk.fixed_values,\n                rng,\n                &mut transcript,\n            )\n            .expect(\"proof generation should not fail\");\n\n            let mut proof = transcript.finalize();\n            let proof_last = prover.get_proof();\n            proof.extend_from_slice(&proof_last);\n            proof\n        };\n        assert_eq!(halo2_proof, tachyon_proof);\n    }\n\n    #[test]\n    fn test_create_shplonk_proof_with_various_transcripts() {\n        let mut rng_for_table = XORShiftRng::from_seed(XOR_SHIFT_SEED);\n        let circuit = MyCircuit::<Fr, W, H>::rand(&mut rng_for_table);\n\n        let s = Fr::from(2);\n        let params = ParamsKZG::<Bn256>::unsafe_setup_with_s(K, s);\n        let pk = keygen_pk2(&params, &circuit).unwrap();\n        let mut pk_bytes: Vec<u8> = vec![];\n        pk.write_including_cs(&mut pk_bytes).unwrap();\n\n        let rng = XORShiftRng::from_seed(XOR_SHIFT_SEED);\n\n        let halo2_blake2b_proof = {\n            let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]);\n\n            halo2_proofs::plonk::create_proof::<\n                KZGCommitmentScheme<Bn256>,\n                ProverSHPLONK<_>,\n                _,\n                _,\n                _,\n                _,\n            >(\n                &params,\n                &pk,\n                &[circuit.clone(), circuit.clone()],\n                &[&[], &[]],\n                rng.clone(),\n                &mut transcript,\n            )\n            .expect(\"proof generation should not fail\");\n\n            transcript.finalize()\n        };\n\n        let tachyon_blake2b_proof = {\n            let mut prover = SHPlonkProver::<KZGCommitmentScheme<Bn256>>::new(\n                TranscriptType::Blake2b as u8,\n                K,\n                &s,\n            );\n\n            let mut tachyon_pk = TachyonProvingKey::from(PCSType::SHPlonk, pk_bytes.as_slice());\n            let mut transcript = TachyonBlake2bWrite::init(vec![]);\n\n            tachyon_create_proof::<_, _, _, _, _, _>(\n                &mut prover,\n                &mut tachyon_pk,\n                &[circuit.clone(), circuit.clone()],\n                &[&[], &[]],\n                pk.fixed_values.clone(),\n                rng.clone(),\n                &mut transcript,\n            )\n            .expect(\"proof generation should not fail\");\n\n            let mut proof = transcript.finalize();\n            let proof_last = prover.get_proof();\n            proof.extend_from_slice(&proof_last);\n            proof\n        };\n        assert_eq!(halo2_blake2b_proof, tachyon_blake2b_proof);\n\n        let halo2_poseidon_proof = {\n            let mut transcript = PoseidonWrite::<_, _, Challenge255<_>>::init(vec![]);\n\n            halo2_proofs::plonk::create_proof::<\n                KZGCommitmentScheme<Bn256>,\n                ProverSHPLONK<_>,\n                _,\n                _,\n                _,\n                _,\n            >(\n                &params,\n                &pk,\n                &[circuit.clone(), circuit.clone()],\n                &[&[], &[]],\n                rng.clone(),\n                &mut transcript,\n            )\n            .expect(\"proof generation should not fail\");\n\n            transcript.finalize()\n        };\n\n        let tachyon_poseidon_proof = {\n            let mut prover = SHPlonkProver::<KZGCommitmentScheme<Bn256>>::new(\n                TranscriptType::Poseidon as u8,\n                K,\n                &s,\n            );\n\n            let mut tachyon_pk = TachyonProvingKey::from(PCSType::SHPlonk, pk_bytes.as_slice());\n            let mut transcript = TachyonPoseidonWrite::init(vec![]);\n\n            tachyon_create_proof::<_, _, _, _, _, _>(\n                &mut prover,\n                &mut tachyon_pk,\n                &[circuit.clone(), circuit.clone()],\n                &[&[], &[]],\n                pk.fixed_values.clone(),\n                rng.clone(),\n                &mut transcript,\n            )\n            .expect(\"proof generation should not fail\");\n\n            let mut proof = transcript.finalize();\n            let proof_last = prover.get_proof();\n            proof.extend_from_slice(&proof_last);\n            proof\n        };\n        assert_eq!(halo2_poseidon_proof, tachyon_poseidon_proof);\n\n        let halo2_sha256_proof = {\n            let mut transcript = ShaWrite::<_, _, Challenge255<_>, sha2::Sha256>::init(vec![]);\n\n            halo2_proofs::plonk::create_proof::<\n                KZGCommitmentScheme<Bn256>,\n                ProverSHPLONK<_>,\n                _,\n                _,\n                _,\n                _,\n            >(\n                &params,\n                &pk,\n                &[circuit.clone(), circuit.clone()],\n                &[&[], &[]],\n                rng.clone(),\n                &mut transcript,\n            )\n            .expect(\"proof generation should not fail\");\n\n            transcript.finalize()\n        };\n\n        let tachyon_sha256_proof = {\n            let mut prover = SHPlonkProver::<KZGCommitmentScheme<Bn256>>::new(\n                TranscriptType::Sha256 as u8,\n                K,\n                &s,\n            );\n\n            let mut tachyon_pk = TachyonProvingKey::from(PCSType::SHPlonk, pk_bytes.as_slice());\n            let mut transcript = TachyonSha256Write::init(vec![]);\n\n            tachyon_create_proof::<_, _, _, _, _, _>(\n                &mut prover,\n                &mut tachyon_pk,\n                &[circuit.clone(), circuit],\n                &[&[], &[]],\n                pk.fixed_values,\n                rng,\n                &mut transcript,\n            )\n            .expect(\"proof generation should not fail\");\n\n            let mut proof = transcript.finalize();\n            let proof_last = prover.get_proof();\n            proof.extend_from_slice(&proof_last);\n            proof\n        };\n        assert_eq!(halo2_sha256_proof, tachyon_sha256_proof);\n    }\n}\n"
  },
  {
    "path": "vendors/scroll_halo2/src/circuits/simple_circuit.rs",
    "content": "use std::marker::PhantomData;\n\nuse halo2_proofs::{\n    arithmetic::Field,\n    circuit::{AssignedCell, Chip, Layouter, Region, SimpleFloorPlanner, Value},\n    plonk::{Advice, Circuit, Column, ConstraintSystem, Error, Fixed, Instance, Selector},\n    poly::Rotation,\n};\n\n// ANCHOR: instructions\ntrait NumericInstructions<F: Field>: Chip<F> {\n    /// Variable representing a number.\n    type Num;\n\n    /// Loads a number into the circuit as a private input.\n    fn load_private(&self, layouter: impl Layouter<F>, a: Value<F>) -> Result<Self::Num, Error>;\n\n    /// Loads a number into the circuit as a fixed constant.\n    fn load_constant(&self, layouter: impl Layouter<F>, constant: F) -> Result<Self::Num, Error>;\n\n    /// Returns `c = a * b`.\n    fn mul(\n        &self,\n        layouter: impl Layouter<F>,\n        a: Self::Num,\n        b: Self::Num,\n    ) -> Result<Self::Num, Error>;\n\n    /// Exposes a number as a public input to the circuit.\n    fn expose_public(\n        &self,\n        layouter: impl Layouter<F>,\n        num: Self::Num,\n        row: usize,\n    ) -> Result<(), Error>;\n}\n// ANCHOR_END: instructions\n\n// ANCHOR: chip\n/// The chip that will implement our instructions! Chips store their own\n/// config, as well as type markers if necessary.\nstruct FieldChip<F: Field> {\n    config: FieldConfig,\n    _marker: PhantomData<F>,\n}\n// ANCHOR_END: chip\n\n// ANCHOR: chip-config\n/// Chip state is stored in a config struct. This is generated by the chip\n/// during configuration, and then stored inside the chip.\n#[derive(Clone, Debug)]\npub struct FieldConfig {\n    /// For this chip, we will use two advice columns to implement our instructions.\n    /// These are also the columns through which we communicate with other parts of\n    /// the circuit.\n    advice: [Column<Advice>; 2],\n\n    /// This is the public input (instance) column.\n    instance: Column<Instance>,\n\n    // We need a selector to enable the multiplication gate, so that we aren't placing\n    // any constraints on cells where `NumericInstructions::mul` is not being used.\n    // This is important when building larger circuits, where columns are used by\n    // multiple sets of instructions.\n    s_mul: Selector,\n}\n\nimpl<F: Field> FieldChip<F> {\n    fn construct(config: <Self as Chip<F>>::Config) -> Self {\n        Self {\n            config,\n            _marker: PhantomData,\n        }\n    }\n\n    fn configure(\n        meta: &mut ConstraintSystem<F>,\n        advice: [Column<Advice>; 2],\n        instance: Column<Instance>,\n        constant: Column<Fixed>,\n    ) -> <Self as Chip<F>>::Config {\n        meta.enable_equality(instance);\n        meta.enable_constant(constant);\n        for column in &advice {\n            meta.enable_equality(*column);\n        }\n        let s_mul = meta.selector();\n\n        // Define our multiplication gate!\n        meta.create_gate(\"mul\", |meta| {\n            // To implement multiplication, we need three advice cells and a selector\n            // cell. We arrange them like so:\n            //\n            // | a0  | a1  | s_mul |\n            // |-----|-----|-------|\n            // | lhs | rhs | s_mul |\n            // | out |     |       |\n            //\n            // Gates may refer to any relative offsets we want, but each distinct\n            // offset adds a cost to the proof. The most common offsets are 0 (the\n            // current row), 1 (the next row), and -1 (the previous row), for which\n            // `Rotation` has specific constructors.\n            let lhs = meta.query_advice(advice[0], Rotation::cur());\n            let rhs = meta.query_advice(advice[1], Rotation::cur());\n            let out = meta.query_advice(advice[0], Rotation::next());\n            let s_mul = meta.query_selector(s_mul);\n\n            // Finally, we return the polynomial expressions that constrain this gate.\n            // For our multiplication gate, we only need a single polynomial constraint.\n            //\n            // The polynomial expressions returned from `create_gate` will be\n            // constrained by the proving system to equal zero. Our expression\n            // has the following properties:\n            // - When s_mul = 0, any value is allowed in lhs, rhs, and out.\n            // - When s_mul != 0, this constrains lhs * rhs = out.\n            vec![s_mul * (lhs * rhs - out)]\n        });\n\n        FieldConfig {\n            advice,\n            instance,\n            s_mul,\n        }\n    }\n}\n// ANCHOR_END: chip-config\n\n// ANCHOR: chip-impl\nimpl<F: Field> Chip<F> for FieldChip<F> {\n    type Config = FieldConfig;\n    type Loaded = ();\n\n    fn config(&self) -> &Self::Config {\n        &self.config\n    }\n\n    fn loaded(&self) -> &Self::Loaded {\n        &()\n    }\n}\n// ANCHOR_END: chip-impl\n\n// ANCHOR: instructions-impl\n/// A variable representing a number.\n#[derive(Clone)]\nstruct Number<F: Field>(AssignedCell<F, F>);\n\nimpl<F: Field> NumericInstructions<F> for FieldChip<F> {\n    type Num = Number<F>;\n\n    fn load_private(\n        &self,\n        mut layouter: impl Layouter<F>,\n        value: Value<F>,\n    ) -> Result<Self::Num, Error> {\n        let config = self.config();\n\n        layouter.assign_region(\n            || \"load private\",\n            |mut region| {\n                region\n                    .assign_advice(|| \"private input\", config.advice[0], 0, || value)\n                    .map(Number)\n            },\n        )\n    }\n\n    fn load_constant(\n        &self,\n        mut layouter: impl Layouter<F>,\n        constant: F,\n    ) -> Result<Self::Num, Error> {\n        let config = self.config();\n\n        layouter.assign_region(\n            || \"load constant\",\n            |mut region| {\n                region\n                    .assign_advice_from_constant(|| \"constant value\", config.advice[0], 0, constant)\n                    .map(Number)\n            },\n        )\n    }\n\n    fn mul(\n        &self,\n        mut layouter: impl Layouter<F>,\n        a: Self::Num,\n        b: Self::Num,\n    ) -> Result<Self::Num, Error> {\n        let config = self.config();\n\n        layouter.assign_region(\n            || \"mul\",\n            |mut region: Region<'_, F>| {\n                // We only want to use a single multiplication gate in this region,\n                // so we enable it at region offset 0; this means it will constrain\n                // cells at offsets 0 and 1.\n                config.s_mul.enable(&mut region, 0)?;\n\n                // The inputs we've been given could be located anywhere in the circuit,\n                // but we can only rely on relative offsets inside this region. So we\n                // assign new cells inside the region and constrain them to have the\n                // same values as the inputs.\n                a.0.copy_advice(|| \"lhs\", &mut region, config.advice[0], 0)?;\n                b.0.copy_advice(|| \"rhs\", &mut region, config.advice[1], 0)?;\n\n                // Now we can assign the multiplication result, which is to be assigned\n                // into the output position.\n                let value = a.0.value().copied() * b.0.value();\n\n                // Finally, we do the assignment to the output, returning a\n                // variable to be used in another part of the circuit.\n                region\n                    .assign_advice(|| \"lhs * rhs\", config.advice[0], 1, || value)\n                    .map(Number)\n            },\n        )\n    }\n\n    fn expose_public(\n        &self,\n        mut layouter: impl Layouter<F>,\n        num: Self::Num,\n        row: usize,\n    ) -> Result<(), Error> {\n        let config = self.config();\n\n        layouter.constrain_instance(num.0.cell(), config.instance, row)\n    }\n}\n// ANCHOR_END: instructions-impl\n\n// ANCHOR: circuit\n/// The full circuit implementation.\n///\n/// In this struct we store the private input variables. We use `Option<F>` because\n/// they won't have any value during key generation. During proving, if any of these\n/// were `None` we would get an error.\n#[derive(Clone, Default)]\npub struct SimpleCircuit<F: Field> {\n    pub constant: F,\n    pub a: Value<F>,\n    pub b: Value<F>,\n}\n\nimpl<F: Field> Circuit<F> for SimpleCircuit<F> {\n    // Since we are using a single chip for everything, we can just reuse its config.\n    type Config = FieldConfig;\n    type FloorPlanner = SimpleFloorPlanner;\n\n    fn without_witnesses(&self) -> Self {\n        Self::default()\n    }\n\n    fn configure(meta: &mut ConstraintSystem<F>) -> Self::Config {\n        // We create the two advice columns that FieldChip uses for I/O.\n        let advice = [meta.advice_column(), meta.advice_column()];\n\n        // We also need an instance column to store public inputs.\n        let instance = meta.instance_column();\n\n        // Create a fixed column to load constants.\n        let constant = meta.fixed_column();\n\n        FieldChip::configure(meta, advice, instance, constant)\n    }\n\n    fn synthesize(\n        &self,\n        config: Self::Config,\n        mut layouter: impl Layouter<F>,\n    ) -> Result<(), Error> {\n        let field_chip = FieldChip::<F>::construct(config);\n\n        // Load our private values into the circuit.\n        let a = field_chip.load_private(layouter.namespace(|| \"load a\"), self.a)?;\n        let b = field_chip.load_private(layouter.namespace(|| \"load b\"), self.b)?;\n\n        // Load the constant factor into the circuit.\n        let constant =\n            field_chip.load_constant(layouter.namespace(|| \"load constant\"), self.constant)?;\n\n        // We only have access to plain multiplication.\n        // We could implement our circuit as:\n        //     asq  = a*a\n        //     bsq  = b*b\n        //     absq = asq*bsq\n        //     c    = constant*asq*bsq\n        //\n        // but it's more efficient to implement it as:\n        //     ab   = a*b\n        //     absq = (ab)²\n        //     c    = constant*absq\n        let ab = field_chip.mul(layouter.namespace(|| \"a * b\"), a, b)?;\n        let absq = field_chip.mul(layouter.namespace(|| \"ab * ab\"), ab.clone(), ab)?;\n        let c = field_chip.mul(layouter.namespace(|| \"constant * absq\"), constant, absq)?;\n\n        // Expose the result as a public input to the circuit.\n        field_chip.expose_public(layouter.namespace(|| \"expose c\"), c, 0)\n    }\n}\n// ANCHOR_END: circuit\n\n#[cfg(test)]\nmod test {\n    use halo2_proofs::{\n        circuit::Value,\n        plonk::keygen_pk2,\n        poly::kzg::{\n            commitment::{KZGCommitmentScheme, ParamsKZG},\n            multiopen::{ProverGWC, ProverSHPLONK},\n        },\n        transcript::{Blake2bWrite, Challenge255, PoseidonWrite, TranscriptWriterBuffer},\n    };\n    use halo2curves::bn256::{Bn256, Fr, G1Affine};\n    use rand_core::SeedableRng;\n\n    use crate::{\n        bn254::{\n            Blake2bWrite as TachyonBlake2bWrite, GWCProver, PoseidonWrite as TachyonPoseidonWrite,\n            ProvingKey as TachyonProvingKey, SHPlonkProver, Sha256Write as TachyonSha256Write,\n            TachyonProver,\n        },\n        cha_cha20_rng::ChaCha20Rng,\n        circuits::simple_circuit::SimpleCircuit,\n        consts::{PCSType, TranscriptType, CHA_CHA20_SEED, XOR_SHIFT_SEED},\n        prover::create_proof as tachyon_create_proof,\n        sha::ShaWrite,\n        xor_shift_rng::XORShiftRng,\n    };\n\n    #[test]\n    fn test_create_gwc_proof_with_various_rngs() {\n        // ANCHOR: test-circuit\n        // The number of rows in our circuit cannot exceed 2ᵏ. Since our example\n        // circuit is very small, we can pick a very small value here.\n        let k = 4;\n\n        // Prepare the private and public inputs to the circuit!\n        let constant = Fr::from(7);\n        let a = Fr::from(2);\n        let b = Fr::from(3);\n        let c = constant * a.square() * b.square();\n\n        // Instantiate the circuit with the private inputs.\n        let circuit = SimpleCircuit {\n            constant,\n            a: Value::known(a),\n            b: Value::known(b),\n        };\n\n        // Arrange the public input. We expose the multiplication result in row 0\n        // of the instance column, so we position it there in our public inputs.\n        let public_inputs = vec![c];\n        let public_inputs2 = vec![&public_inputs[..]];\n        let public_inputs3 = vec![&public_inputs2[..], &public_inputs2[..]];\n\n        // Given the correct public input, our circuit will verify.\n        let s = Fr::from(2);\n        let params = ParamsKZG::<Bn256>::unsafe_setup_with_s(k, s.clone());\n        let pk = keygen_pk2(&params, &circuit).expect(\"vk should not fail\");\n        let mut pk_bytes: Vec<u8> = vec![];\n        pk.write_including_cs(&mut pk_bytes).unwrap();\n\n        let rng = XORShiftRng::from_seed(XOR_SHIFT_SEED);\n\n        let halo2_proof = {\n            let mut transcript = Blake2bWrite::<_, G1Affine, Challenge255<_>>::init(vec![]);\n\n            halo2_proofs::plonk::create_proof::<\n                KZGCommitmentScheme<Bn256>,\n                ProverGWC<_>,\n                _,\n                _,\n                _,\n                _,\n            >(\n                &params,\n                &pk,\n                &[circuit.clone(), circuit.clone()],\n                public_inputs3.as_slice(),\n                rng.clone(),\n                &mut transcript,\n            )\n            .expect(\"proof generation should not fail\");\n\n            transcript.finalize()\n        };\n\n        let tachyon_proof = {\n            let mut prover =\n                GWCProver::<KZGCommitmentScheme<Bn256>>::new(TranscriptType::Blake2b as u8, k, &s);\n\n            let mut tachyon_pk = TachyonProvingKey::from(PCSType::GWC, pk_bytes.as_slice());\n            let mut transcript = TachyonBlake2bWrite::init(vec![]);\n\n            tachyon_create_proof::<_, _, _, _, _, _>(\n                &mut prover,\n                &mut tachyon_pk,\n                &[circuit.clone(), circuit.clone()],\n                public_inputs3.as_slice(),\n                pk.fixed_values.clone(),\n                rng,\n                &mut transcript,\n            )\n            .expect(\"proof generation should not fail\");\n\n            let mut proof = transcript.finalize();\n            let proof_last = prover.get_proof();\n            proof.extend_from_slice(&proof_last);\n            proof\n        };\n        assert_eq!(halo2_proof, tachyon_proof);\n\n        let rng = ChaCha20Rng::from_seed(CHA_CHA20_SEED);\n\n        let halo2_proof = {\n            let mut transcript = Blake2bWrite::<_, G1Affine, Challenge255<_>>::init(vec![]);\n\n            halo2_proofs::plonk::create_proof::<\n                KZGCommitmentScheme<Bn256>,\n                ProverGWC<_>,\n                _,\n                _,\n                _,\n                _,\n            >(\n                &params,\n                &pk,\n                &[circuit.clone(), circuit.clone()],\n                public_inputs3.as_slice(),\n                rng.clone(),\n                &mut transcript,\n            )\n            .expect(\"proof generation should not fail\");\n\n            transcript.finalize()\n        };\n\n        let tachyon_proof = {\n            let mut prover =\n                GWCProver::<KZGCommitmentScheme<Bn256>>::new(TranscriptType::Blake2b as u8, k, &s);\n\n            let mut tachyon_pk = TachyonProvingKey::from(PCSType::GWC, pk_bytes.as_slice());\n            let mut transcript = TachyonBlake2bWrite::init(vec![]);\n\n            tachyon_create_proof::<_, _, _, _, _, _>(\n                &mut prover,\n                &mut tachyon_pk,\n                &[circuit.clone(), circuit],\n                public_inputs3.as_slice(),\n                pk.fixed_values,\n                rng,\n                &mut transcript,\n            )\n            .expect(\"proof generation should not fail\");\n\n            let mut proof = transcript.finalize();\n            let proof_last = prover.get_proof();\n            proof.extend_from_slice(&proof_last);\n            proof\n        };\n        assert_eq!(halo2_proof, tachyon_proof);\n        // ANCHOR_END: test-circuit\n    }\n\n    #[test]\n    fn test_create_shplonk_proof_with_various_transcripts() {\n        // ANCHOR: test-circuit\n        // The number of rows in our circuit cannot exceed 2ᵏ. Since our example\n        // circuit is very small, we can pick a very small value here.\n        let k = 4;\n\n        // Prepare the private and public inputs to the circuit!\n        let constant = Fr::from(7);\n        let a = Fr::from(2);\n        let b = Fr::from(3);\n        let c = constant * a.square() * b.square();\n\n        // Instantiate the circuit with the private inputs.\n        let circuit = SimpleCircuit {\n            constant,\n            a: Value::known(a),\n            b: Value::known(b),\n        };\n\n        // Arrange the public input. We expose the multiplication result in row 0\n        // of the instance column, so we position it there in our public inputs.\n        let public_inputs = vec![c];\n        let public_inputs2 = vec![&public_inputs[..]];\n        let public_inputs3 = vec![&public_inputs2[..], &public_inputs2[..]];\n\n        // Given the correct public input, our circuit will verify.\n        let s = Fr::from(2);\n        let params = ParamsKZG::<Bn256>::unsafe_setup_with_s(k, s.clone());\n        let pk = keygen_pk2(&params, &circuit).expect(\"vk should not fail\");\n        let mut pk_bytes: Vec<u8> = vec![];\n        pk.write_including_cs(&mut pk_bytes).unwrap();\n\n        let rng = XORShiftRng::from_seed(XOR_SHIFT_SEED);\n\n        let halo2_blake2b_proof = {\n            let mut transcript = Blake2bWrite::<_, G1Affine, Challenge255<_>>::init(vec![]);\n\n            halo2_proofs::plonk::create_proof::<\n                KZGCommitmentScheme<Bn256>,\n                ProverSHPLONK<_>,\n                _,\n                _,\n                _,\n                _,\n            >(\n                &params,\n                &pk,\n                &[circuit.clone(), circuit.clone()],\n                public_inputs3.as_slice(),\n                rng.clone(),\n                &mut transcript,\n            )\n            .expect(\"proof generation should not fail\");\n\n            transcript.finalize()\n        };\n\n        let tachyon_blake2b_proof = {\n            let mut prover = SHPlonkProver::<KZGCommitmentScheme<Bn256>>::new(\n                TranscriptType::Blake2b as u8,\n                k,\n                &s,\n            );\n\n            let mut tachyon_pk = TachyonProvingKey::from(PCSType::SHPlonk, pk_bytes.as_slice());\n            let mut transcript = TachyonBlake2bWrite::init(vec![]);\n\n            tachyon_create_proof::<_, _, _, _, _, _>(\n                &mut prover,\n                &mut tachyon_pk,\n                &[circuit.clone(), circuit.clone()],\n                public_inputs3.as_slice(),\n                pk.fixed_values.clone(),\n                rng.clone(),\n                &mut transcript,\n            )\n            .expect(\"proof generation should not fail\");\n\n            let mut proof = transcript.finalize();\n            let proof_last = prover.get_proof();\n            proof.extend_from_slice(&proof_last);\n            proof\n        };\n        assert_eq!(halo2_blake2b_proof, tachyon_blake2b_proof);\n\n        let halo2_poseidon_proof = {\n            let mut transcript = PoseidonWrite::<_, G1Affine, Challenge255<_>>::init(vec![]);\n\n            halo2_proofs::plonk::create_proof::<\n                KZGCommitmentScheme<Bn256>,\n                ProverSHPLONK<_>,\n                _,\n                _,\n                _,\n                _,\n            >(\n                &params,\n                &pk,\n                &[circuit.clone(), circuit.clone()],\n                public_inputs3.as_slice(),\n                rng.clone(),\n                &mut transcript,\n            )\n            .expect(\"proof generation should not fail\");\n\n            transcript.finalize()\n        };\n\n        let tachyon_poseidon_proof = {\n            let mut prover = SHPlonkProver::<KZGCommitmentScheme<Bn256>>::new(\n                TranscriptType::Poseidon as u8,\n                k,\n                &s,\n            );\n\n            let mut tachyon_pk = TachyonProvingKey::from(PCSType::SHPlonk, pk_bytes.as_slice());\n            let mut transcript = TachyonPoseidonWrite::init(vec![]);\n\n            tachyon_create_proof::<_, _, _, _, _, _>(\n                &mut prover,\n                &mut tachyon_pk,\n                &[circuit.clone(), circuit.clone()],\n                public_inputs3.as_slice(),\n                pk.fixed_values.clone(),\n                rng.clone(),\n                &mut transcript,\n            )\n            .expect(\"proof generation should not fail\");\n\n            let mut proof = transcript.finalize();\n            let proof_last = prover.get_proof();\n            proof.extend_from_slice(&proof_last);\n            proof\n        };\n        assert_eq!(halo2_poseidon_proof, tachyon_poseidon_proof);\n\n        let halo2_sha256_proof = {\n            let mut transcript =\n                ShaWrite::<_, G1Affine, Challenge255<_>, sha2::Sha256>::init(vec![]);\n\n            halo2_proofs::plonk::create_proof::<\n                KZGCommitmentScheme<Bn256>,\n                ProverSHPLONK<_>,\n                _,\n                _,\n                _,\n                _,\n            >(\n                &params,\n                &pk,\n                &[circuit.clone(), circuit.clone()],\n                public_inputs3.as_slice(),\n                rng.clone(),\n                &mut transcript,\n            )\n            .expect(\"proof generation should not fail\");\n\n            transcript.finalize()\n        };\n\n        let tachyon_sha256_proof = {\n            let mut prover = SHPlonkProver::<KZGCommitmentScheme<Bn256>>::new(\n                TranscriptType::Sha256 as u8,\n                k,\n                &s,\n            );\n\n            let mut tachyon_pk = TachyonProvingKey::from(PCSType::SHPlonk, pk_bytes.as_slice());\n            let mut transcript = TachyonSha256Write::init(vec![]);\n\n            tachyon_create_proof::<_, _, _, _, _, _>(\n                &mut prover,\n                &mut tachyon_pk,\n                &[circuit.clone(), circuit],\n                public_inputs3.as_slice(),\n                pk.fixed_values,\n                rng,\n                &mut transcript,\n            )\n            .expect(\"proof generation should not fail\");\n\n            let mut proof = transcript.finalize();\n            let proof_last = prover.get_proof();\n            proof.extend_from_slice(&proof_last);\n            proof\n        };\n        assert_eq!(halo2_sha256_proof, tachyon_sha256_proof);\n        // ANCHOR_END: test-circuit\n    }\n}\n"
  },
  {
    "path": "vendors/scroll_halo2/src/circuits/simple_lookup_circuit.rs",
    "content": "use std::marker::PhantomData;\n\nuse ff::{Field, PrimeField};\nuse halo2_proofs::{\n    circuit::{Layouter, SimpleFloorPlanner, Value},\n    plonk::{Advice, Circuit, Column, ConstraintSystem, Error, Expression, Selector, TableColumn},\n    poly::Rotation,\n};\n\n#[derive(Clone, Default)]\nstruct SimpleLookupCircuit<F: Field> {\n    _marker: PhantomData<F>,\n}\n\n#[derive(Clone)]\nstruct SimpleLookupConfig {\n    selector: Selector,\n    table: TableColumn,\n    advice: Column<Advice>,\n}\n\nimpl<F: PrimeField> Circuit<F> for SimpleLookupCircuit<F> {\n    type Config = SimpleLookupConfig;\n    type FloorPlanner = SimpleFloorPlanner;\n\n    fn without_witnesses(&self) -> Self {\n        Self::default()\n    }\n\n    fn configure(meta: &mut ConstraintSystem<F>) -> SimpleLookupConfig {\n        let config = SimpleLookupConfig {\n            selector: meta.complex_selector(),\n            table: meta.lookup_table_column(),\n            advice: meta.advice_column(),\n        };\n\n        meta.lookup(\"lookup\", |meta| {\n            let selector = meta.query_selector(config.selector);\n            let not_selector = Expression::Constant(F::ONE) - selector.clone();\n            let advice = meta.query_advice(config.advice, Rotation::cur());\n            vec![(selector * advice + not_selector, config.table)]\n        });\n\n        config\n    }\n\n    fn synthesize(\n        &self,\n        config: SimpleLookupConfig,\n        mut layouter: impl Layouter<F>,\n    ) -> Result<(), Error> {\n        layouter.assign_table(\n            || \"3-bit table\",\n            |mut table| {\n                for row in 0u64..(1 << 3) {\n                    table.assign_cell(\n                        || format!(\"row {}\", row),\n                        config.table,\n                        row as usize,\n                        || Value::known(F::from(row + 1)),\n                    )?;\n                }\n\n                Ok(())\n            },\n        )?;\n\n        layouter.assign_region(\n            || \"assign values\",\n            |mut region| {\n                for offset in 0u64..(1 << 4) {\n                    config.selector.enable(&mut region, offset as usize)?;\n                    region.assign_advice(\n                        || format!(\"offset {}\", offset),\n                        config.advice,\n                        offset as usize,\n                        || Value::known(F::from((offset % 8) + 1)),\n                    )?;\n                }\n\n                Ok(())\n            },\n        )\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use std::marker::PhantomData;\n\n    use halo2_proofs::{\n        plonk::keygen_pk2,\n        poly::kzg::{\n            commitment::{KZGCommitmentScheme, ParamsKZG},\n            multiopen::{ProverGWC, ProverSHPLONK},\n        },\n        transcript::{Blake2bWrite, Challenge255, PoseidonWrite, TranscriptWriterBuffer},\n    };\n    use halo2curves::bn256::{Bn256, Fr, G1Affine};\n    use rand_core::SeedableRng;\n\n    use crate::{\n        bn254::{\n            Blake2bWrite as TachyonBlake2bWrite, GWCProver, PoseidonWrite as TachyonPoseidonWrite,\n            ProvingKey as TachyonProvingKey, SHPlonkProver, Sha256Write as TachyonSha256Write,\n            TachyonProver,\n        },\n        cha_cha20_rng::ChaCha20Rng,\n        circuits::simple_lookup_circuit::SimpleLookupCircuit,\n        consts::{PCSType, TranscriptType, CHA_CHA20_SEED, XOR_SHIFT_SEED},\n        prover::create_proof as tachyon_create_proof,\n        sha::ShaWrite,\n        xor_shift_rng::XORShiftRng,\n    };\n\n    #[test]\n    fn test_create_gwc_proof_with_various_rngs() {\n        // ANCHOR: test-circuit\n        // The number of rows in our circuit cannot exceed 2ᵏ. Since our example\n        // circuit is very small, we can pick a very small value here.\n        let k = 5;\n\n        // Instantiate the circuit.\n        let circuit = SimpleLookupCircuit::<Fr> {\n            _marker: PhantomData,\n        };\n\n        // Arrange the public input.\n        let public_inputs = vec![];\n        let public_inputs2 = vec![&public_inputs[..], &public_inputs[..]];\n\n        // Given the correct public input, our circuit will verify.\n        let s = Fr::from(2);\n        let params = ParamsKZG::<Bn256>::unsafe_setup_with_s(k, s.clone());\n        let pk = keygen_pk2(&params, &circuit).expect(\"vk should not fail\");\n        let mut pk_bytes: Vec<u8> = vec![];\n        pk.write_including_cs(&mut pk_bytes).unwrap();\n\n        let rng = XORShiftRng::from_seed(XOR_SHIFT_SEED);\n\n        let halo2_proof = {\n            let mut transcript = Blake2bWrite::<_, G1Affine, Challenge255<_>>::init(vec![]);\n\n            halo2_proofs::plonk::create_proof::<\n                KZGCommitmentScheme<Bn256>,\n                ProverGWC<_>,\n                _,\n                _,\n                _,\n                _,\n            >(\n                &params,\n                &pk,\n                &[circuit.clone(), circuit.clone()],\n                public_inputs2.as_slice(),\n                rng.clone(),\n                &mut transcript,\n            )\n            .expect(\"proof generation should not fail\");\n\n            transcript.finalize()\n        };\n\n        let tachyon_proof = {\n            let mut prover =\n                GWCProver::<KZGCommitmentScheme<Bn256>>::new(TranscriptType::Blake2b as u8, k, &s);\n\n            let mut tachyon_pk = TachyonProvingKey::from(PCSType::GWC, pk_bytes.as_slice());\n            let mut transcript = TachyonBlake2bWrite::init(vec![]);\n\n            tachyon_create_proof::<_, _, _, _, _, _>(\n                &mut prover,\n                &mut tachyon_pk,\n                &[circuit.clone(), circuit.clone()],\n                public_inputs2.as_slice(),\n                pk.fixed_values.clone(),\n                rng,\n                &mut transcript,\n            )\n            .expect(\"proof generation should not fail\");\n\n            let mut proof = transcript.finalize();\n            let proof_last = prover.get_proof();\n            proof.extend_from_slice(&proof_last);\n            proof\n        };\n        assert_eq!(halo2_proof, tachyon_proof);\n\n        let rng = ChaCha20Rng::from_seed(CHA_CHA20_SEED);\n\n        let halo2_proof = {\n            let mut transcript = Blake2bWrite::<_, G1Affine, Challenge255<_>>::init(vec![]);\n\n            halo2_proofs::plonk::create_proof::<\n                KZGCommitmentScheme<Bn256>,\n                ProverGWC<_>,\n                _,\n                _,\n                _,\n                _,\n            >(\n                &params,\n                &pk,\n                &[circuit.clone(), circuit.clone()],\n                public_inputs2.as_slice(),\n                rng.clone(),\n                &mut transcript,\n            )\n            .expect(\"proof generation should not fail\");\n\n            transcript.finalize()\n        };\n\n        let tachyon_proof = {\n            let mut prover =\n                GWCProver::<KZGCommitmentScheme<Bn256>>::new(TranscriptType::Blake2b as u8, k, &s);\n\n            let mut tachyon_pk = TachyonProvingKey::from(PCSType::GWC, pk_bytes.as_slice());\n            let mut transcript = TachyonBlake2bWrite::init(vec![]);\n\n            tachyon_create_proof::<_, _, _, _, _, _>(\n                &mut prover,\n                &mut tachyon_pk,\n                &[circuit.clone(), circuit],\n                public_inputs2.as_slice(),\n                pk.fixed_values,\n                rng,\n                &mut transcript,\n            )\n            .expect(\"proof generation should not fail\");\n\n            let mut proof = transcript.finalize();\n            let proof_last = prover.get_proof();\n            proof.extend_from_slice(&proof_last);\n            proof\n        };\n        assert_eq!(halo2_proof, tachyon_proof);\n        // ANCHOR_END: test-circuit\n    }\n\n    #[test]\n    fn test_create_shplonk_proof_with_various_transcripts() {\n        // ANCHOR: test-circuit\n        // The number of rows in our circuit cannot exceed 2ᵏ. Since our example\n        // circuit is very small, we can pick a very small value here.\n        let k = 5;\n\n        // Instantiate the circuit.\n        let circuit = SimpleLookupCircuit::<Fr> {\n            _marker: PhantomData,\n        };\n\n        // Arrange the public input.\n        let public_inputs = vec![];\n        let public_inputs2 = vec![&public_inputs[..], &public_inputs[..]];\n\n        // Given the correct public input, our circuit will verify.\n        let s = Fr::from(2);\n        let params = ParamsKZG::<Bn256>::unsafe_setup_with_s(k, s.clone());\n        let pk = keygen_pk2(&params, &circuit).expect(\"vk should not fail\");\n        let mut pk_bytes: Vec<u8> = vec![];\n        pk.write_including_cs(&mut pk_bytes).unwrap();\n\n        let rng = XORShiftRng::from_seed(XOR_SHIFT_SEED);\n\n        let halo2_blake2b_proof = {\n            let mut transcript = Blake2bWrite::<_, G1Affine, Challenge255<_>>::init(vec![]);\n\n            halo2_proofs::plonk::create_proof::<\n                KZGCommitmentScheme<Bn256>,\n                ProverSHPLONK<_>,\n                _,\n                _,\n                _,\n                _,\n            >(\n                &params,\n                &pk,\n                &[circuit.clone(), circuit.clone()],\n                public_inputs2.as_slice(),\n                rng.clone(),\n                &mut transcript,\n            )\n            .expect(\"proof generation should not fail\");\n\n            transcript.finalize()\n        };\n\n        let tachyon_blake2b_proof = {\n            let mut prover = SHPlonkProver::<KZGCommitmentScheme<Bn256>>::new(\n                TranscriptType::Blake2b as u8,\n                k,\n                &s,\n            );\n\n            let mut tachyon_pk = TachyonProvingKey::from(PCSType::SHPlonk, pk_bytes.as_slice());\n            let mut transcript = TachyonBlake2bWrite::init(vec![]);\n\n            tachyon_create_proof::<_, _, _, _, _, _>(\n                &mut prover,\n                &mut tachyon_pk,\n                &[circuit.clone(), circuit.clone()],\n                public_inputs2.as_slice(),\n                pk.fixed_values.clone(),\n                rng.clone(),\n                &mut transcript,\n            )\n            .expect(\"proof generation should not fail\");\n\n            let mut proof = transcript.finalize();\n            let proof_last = prover.get_proof();\n            proof.extend_from_slice(&proof_last);\n            proof\n        };\n        assert_eq!(halo2_blake2b_proof, tachyon_blake2b_proof);\n\n        let halo2_poseidon_proof = {\n            let mut transcript = PoseidonWrite::<_, G1Affine, Challenge255<_>>::init(vec![]);\n\n            halo2_proofs::plonk::create_proof::<\n                KZGCommitmentScheme<Bn256>,\n                ProverSHPLONK<_>,\n                _,\n                _,\n                _,\n                _,\n            >(\n                &params,\n                &pk,\n                &[circuit.clone(), circuit.clone()],\n                public_inputs2.as_slice(),\n                rng.clone(),\n                &mut transcript,\n            )\n            .expect(\"proof generation should not fail\");\n\n            transcript.finalize()\n        };\n\n        let tachyon_poseidon_proof = {\n            let mut prover = SHPlonkProver::<KZGCommitmentScheme<Bn256>>::new(\n                TranscriptType::Poseidon as u8,\n                k,\n                &s,\n            );\n\n            let mut tachyon_pk = TachyonProvingKey::from(PCSType::SHPlonk, pk_bytes.as_slice());\n            let mut transcript = TachyonPoseidonWrite::init(vec![]);\n\n            tachyon_create_proof::<_, _, _, _, _, _>(\n                &mut prover,\n                &mut tachyon_pk,\n                &[circuit.clone(), circuit.clone()],\n                public_inputs2.as_slice(),\n                pk.fixed_values.clone(),\n                rng.clone(),\n                &mut transcript,\n            )\n            .expect(\"proof generation should not fail\");\n\n            let mut proof = transcript.finalize();\n            let proof_last = prover.get_proof();\n            proof.extend_from_slice(&proof_last);\n            proof\n        };\n        assert_eq!(halo2_poseidon_proof, tachyon_poseidon_proof);\n\n        let halo2_sha256_proof = {\n            let mut transcript =\n                ShaWrite::<_, G1Affine, Challenge255<_>, sha2::Sha256>::init(vec![]);\n\n            halo2_proofs::plonk::create_proof::<\n                KZGCommitmentScheme<Bn256>,\n                ProverSHPLONK<_>,\n                _,\n                _,\n                _,\n                _,\n            >(\n                &params,\n                &pk,\n                &[circuit.clone(), circuit.clone()],\n                public_inputs2.as_slice(),\n                rng.clone(),\n                &mut transcript,\n            )\n            .expect(\"proof generation should not fail\");\n\n            transcript.finalize()\n        };\n\n        let tachyon_sha256_proof = {\n            let mut prover = SHPlonkProver::<KZGCommitmentScheme<Bn256>>::new(\n                TranscriptType::Sha256 as u8,\n                k,\n                &s,\n            );\n\n            let mut tachyon_pk = TachyonProvingKey::from(PCSType::SHPlonk, pk_bytes.as_slice());\n            let mut transcript = TachyonSha256Write::init(vec![]);\n\n            tachyon_create_proof::<_, _, _, _, _, _>(\n                &mut prover,\n                &mut tachyon_pk,\n                &[circuit.clone(), circuit],\n                public_inputs2.as_slice(),\n                pk.fixed_values,\n                rng,\n                &mut transcript,\n            )\n            .expect(\"proof generation should not fail\");\n\n            let mut proof = transcript.finalize();\n            let proof_last = prover.get_proof();\n            proof.extend_from_slice(&proof_last);\n            proof\n        };\n        assert_eq!(halo2_sha256_proof, tachyon_sha256_proof);\n        // ANCHOR_END: test-circuit\n    }\n}\n"
  },
  {
    "path": "vendors/scroll_halo2/src/consts.rs",
    "content": "pub enum PCSType {\n    GWC,\n    SHPlonk,\n}\n\npub enum TranscriptType {\n    Blake2b,\n    Poseidon,\n    Sha256,\n    SnarkVerifierPoseidon,\n}\n\npub enum RNGType {\n    XORShift,\n    ChaCha20,\n}\n\npub const XOR_SHIFT_SEED: [u8; 16] = [\n    0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5,\n];\n\npub const CHA_CHA20_SEED: [u8; 32] = [\n    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,\n];\n"
  },
  {
    "path": "vendors/scroll_halo2/src/lib.rs",
    "content": "#![allow(dead_code)]\n\nmod bn254;\nmod cha_cha20_rng;\nmod circuits;\nmod consts;\nmod msm;\nmod prover;\nmod proving_key;\nmod rng;\nmod sha;\nmod transcript;\nmod xor_shift_rng;\n"
  },
  {
    "path": "vendors/scroll_halo2/src/msm.rs",
    "content": "#[cfg(test)]\nmod test {\n    use crate::bn254::{ffi, Fr as CppFr, G1Point2 as CppG1Point2};\n    use halo2_proofs::arithmetic::best_multiexp;\n    use halo2curves::{\n        bn256::{Fr, G1Affine, G1},\n        group::{ff::Field, Curve, Group},\n    };\n    use std::{mem, time::Instant};\n\n    struct Timer {\n        now: Instant,\n    }\n\n    impl Timer {\n        fn new() -> Timer {\n            Timer {\n                now: Instant::now(),\n            }\n        }\n\n        fn reset(&mut self) {\n            self.now = Instant::now();\n        }\n\n        fn end(&self, message: &str) {\n            println!(\"{}, elapsed: {:?}\", message, self.now.elapsed());\n        }\n    }\n\n    struct TestSet {\n        bases: Vec<G1Affine>,\n        scalars: Vec<Fr>,\n    }\n\n    impl TestSet {\n        fn create(n: usize) -> TestSet {\n            use rand_core::OsRng;\n\n            let mut base = G1::random(OsRng);\n            TestSet {\n                bases: (0..n)\n                    .map(|_| {\n                        let ret = base.to_affine();\n                        base = base.double();\n                        ret\n                    })\n                    .collect(),\n                scalars: (0..n).map(|_| Fr::random(OsRng)).collect(),\n            }\n        }\n    }\n\n    #[test]\n    fn test_msm() {\n        let degree = 10;\n        let n = 1usize << degree;\n\n        let test_set = TestSet::create(n);\n\n        let mut timer = Timer::new();\n        let expected = {\n            let ret = best_multiexp(&test_set.scalars, &test_set.bases);\n            timer.end(\"best_multiexp\");\n            ret\n        };\n\n        unsafe {\n            timer.reset();\n            let bases: Vec<CppG1Point2> = mem::transmute(test_set.bases);\n            let scalars: Vec<CppFr> = mem::transmute(test_set.scalars);\n\n            let mut msm = ffi::create_g1_msm(degree);\n\n            let actual = ffi::g1_point2_msm(&mut *msm, &bases, &scalars);\n            let actual: Box<G1> = mem::transmute(actual);\n            timer.end(\"msm\");\n            assert_eq!(*actual, expected);\n\n            ffi::destroy_g1_msm(msm);\n        }\n    }\n\n    #[cfg(feature = \"gpu\")]\n    #[test]\n    fn test_msm_gpu() {\n        let degree = 10;\n        let n = 1usize << degree;\n\n        let test_set = TestSet::create(n);\n\n        let mut timer = Timer::new();\n        let mut msm = ffi::create_g1_msm_gpu(degree);\n        timer.end(\"init_msm_gpu\");\n\n        let expected = {\n            timer.reset();\n            let ret = best_multiexp(&test_set.scalars, &test_set.bases);\n            timer.end(\"best_multiexp\");\n            ret\n        };\n\n        unsafe {\n            timer.reset();\n            let bases: Vec<CppG1Point2> = mem::transmute(test_set.bases);\n            let scalars: Vec<CppFr> = mem::transmute(test_set.scalars);\n\n            let actual = ffi::g1_point2_msm_gpu(&mut *msm, &bases, &scalars);\n            let actual: Box<G1> = mem::transmute(actual);\n            timer.end(\"msm_gpu\");\n            assert_eq!(*actual, expected);\n        }\n\n        ffi::destroy_g1_msm_gpu(msm);\n    }\n}\n"
  },
  {
    "path": "vendors/scroll_halo2/src/prover.rs",
    "content": "// This is taken and modified from https://github.com/scroll-tech/halo2/blob/e5ddf67/halo2_proofs/src/plonk/prover.rs#L37-L554.\n\nuse std::{\n    collections::{BTreeSet, HashMap},\n    ops::{Range, RangeTo},\n    sync::Arc,\n};\n\nuse crate::{\n    bn254::{\n        AdviceSingle, Evals, InstanceSingle, ProvingKey as TachyonProvingKey, RationalEvals,\n        RationalEvalsView, TachyonProver, TranscriptWriteState,\n    },\n    rng::SerializableRng,\n};\nuse ff::{Field, FromUniformBytes, WithSmallOrderMulGroup};\nuse halo2_proofs::{\n    circuit::Value,\n    plonk::{\n        sealed, Advice, Any, Assigned, Assignment, Challenge, Circuit, Column, ConstraintSystem,\n        Error, Fixed, FloorPlanner, Instance, Selector,\n    },\n    poly::{\n        commitment::{Blind, CommitmentScheme},\n        LagrangeCoeff, Polynomial,\n    },\n    transcript::EncodedChallenge,\n};\nuse halo2curves::{bn256::Fr, group::prime::PrimeCurveAffine};\nuse rand_core::RngCore;\n\n/// This creates a proof for the provided `circuit` when given the public\n/// parameters `params` and the proving key [`ProvingKey`] that was\n/// generated previously for the same circuit. The provided `instances`\n/// are zero-padded internally.\npub fn create_proof<\n    'params,\n    Scheme: CommitmentScheme,\n    P: TachyonProver<Scheme>,\n    E: EncodedChallenge<Scheme::Curve>,\n    R: RngCore + SerializableRng,\n    T: TranscriptWriteState<Scheme::Curve, E>,\n    ConcreteCircuit: Circuit<Scheme::Scalar>,\n>(\n    prover: &mut P,\n    pk: &mut TachyonProvingKey<Scheme::Curve>,\n    circuits: &[ConcreteCircuit],\n    instances: &[&[&[Scheme::Scalar]]],\n    fixed_values: Vec<Polynomial<Scheme::Scalar, LagrangeCoeff>>,\n    mut rng: R,\n    transcript: &mut T,\n) -> Result<(), Error>\nwhere\n    Scheme::Scalar: WithSmallOrderMulGroup<3> + FromUniformBytes<64> + Ord,\n{\n    if circuits.len() != instances.len() {\n        return Err(Error::InvalidInstances);\n    }\n\n    for instance in instances.iter() {\n        if instance.len() != pk.num_instance_columns() {\n            return Err(Error::InvalidInstances);\n        }\n    }\n\n    prover.set_extended_domain(pk);\n    // Hash verification key into transcript\n    transcript.common_scalar(prover.transcript_repr(pk))?;\n\n    let mut meta = ConstraintSystem::default();\n    #[cfg(feature = \"circuit-params\")]\n    let config = ConcreteCircuit::configure_with_params(&mut meta, circuits[0].params());\n    #[cfg(not(feature = \"circuit-params\"))]\n    let config = ConcreteCircuit::configure(&mut meta);\n\n    // Selector optimizations cannot be applied here; use the ConstraintSystem\n    // from the verification key.\n\n    let mut instance: Vec<InstanceSingle> = instances\n        .iter()\n        .map(|instance| -> Result<InstanceSingle, Error> {\n            let instance_values = instance\n                .iter()\n                .map(|values| {\n                    let mut poly = prover.empty_evals();\n                    assert_eq!(poly.len(), prover.n() as usize);\n                    if values.len() > (poly.len() - ((pk.blinding_factors() as usize) + 1)) {\n                        return Err(Error::InstanceTooLarge);\n                    }\n\n                    for i in 0..values.len() {\n                        if !P::QUERY_INSTANCE {\n                            transcript.common_scalar(values[i])?;\n                        }\n                        poly.set_value(i, unsafe { std::mem::transmute(&values[i]) });\n                    }\n                    Ok(poly)\n                })\n                .collect::<Result<Vec<_>, _>>()?;\n\n            if P::QUERY_INSTANCE {\n                prover.batch_start(instance_values.len());\n                instance_values\n                    .iter()\n                    .enumerate()\n                    .for_each(|(idx, poly)| prover.batch_commit_lagrange(poly, idx));\n                let mut instance_commitments =\n                    vec![Scheme::Curve::identity(); instance_values.len()];\n                prover\n                    .batch_end(unsafe { std::mem::transmute(instance_commitments.as_mut_slice()) });\n                for commitment in &instance_commitments {\n                    transcript.common_point(*commitment)?;\n                }\n            }\n\n            let instance_polys: Vec<_> = instance_values\n                .iter()\n                .map(|evals| prover.ifft(evals))\n                .collect();\n\n            Ok(InstanceSingle {\n                instance_values,\n                instance_polys,\n            })\n        })\n        .collect::<Result<Vec<_>, _>>()?;\n\n    struct WitnessCollection<'a, F: Field> {\n        k: u32,\n        current_phase: sealed::Phase,\n        advice_vec: Arc<Vec<RationalEvals>>,\n        advice: Vec<RationalEvalsView>,\n        challenges: &'a HashMap<usize, F>,\n        instances: &'a [&'a [F]],\n        fixed_values: &'a [Polynomial<F, LagrangeCoeff>],\n        rw_rows: Range<usize>,\n        usable_rows: RangeTo<usize>,\n        _marker: std::marker::PhantomData<F>,\n    }\n\n    impl<'a, F: Field> Assignment<F> for WitnessCollection<'a, F> {\n        fn enter_region<NR, N>(&mut self, _: N)\n        where\n            NR: Into<String>,\n            N: FnOnce() -> NR,\n        {\n            // Do nothing; we don't care about regions in this context.\n        }\n\n        fn exit_region(&mut self) {\n            // Do nothing; we don't care about regions in this context.\n        }\n\n        fn enable_selector<A, AR>(&mut self, _: A, _: &Selector, _: usize) -> Result<(), Error>\n        where\n            A: FnOnce() -> AR,\n            AR: Into<String>,\n        {\n            // We only care about advice columns here\n\n            Ok(())\n        }\n\n        fn fork(&mut self, ranges: &[Range<usize>]) -> Result<Vec<Self>, Error> {\n            let mut range_start = self.rw_rows.start;\n            for (i, sub_range) in ranges.iter().enumerate() {\n                if sub_range.start < range_start {\n                    log::error!(\n                        \"subCS_{} sub_range.start ({}) < range_start ({})\",\n                        i,\n                        sub_range.start,\n                        range_start\n                    );\n                    return Err(Error::Synthesis);\n                }\n                if i == ranges.len() - 1 && sub_range.end > self.rw_rows.end {\n                    log::error!(\n                        \"subCS_{} sub_range.end ({}) > self.rw_rows.end ({})\",\n                        i,\n                        sub_range.end,\n                        self.rw_rows.end\n                    );\n                    return Err(Error::Synthesis);\n                }\n                range_start = sub_range.end;\n                log::debug!(\n                    \"subCS_{} rw_rows: {}..{}\",\n                    i,\n                    sub_range.start,\n                    sub_range.end\n                );\n            }\n\n            let mut sub_cs = vec![];\n            for sub_range in ranges {\n                let advice_vec = self.advice_vec.clone();\n                let advice = unsafe {\n                    let ptr = Arc::as_ptr(&advice_vec) as *mut Vec<RationalEvals>;\n                    let mut_ref = &mut (*ptr);\n                    mut_ref\n                        .iter_mut()\n                        .map(|advice| {\n                            advice.create_view(sub_range.start, sub_range.end - sub_range.start)\n                        })\n                        .collect::<Vec<_>>()\n                };\n\n                sub_cs.push(Self {\n                    k: 0,\n                    current_phase: self.current_phase,\n                    advice_vec,\n                    advice,\n                    challenges: self.challenges,\n                    instances: self.instances,\n                    fixed_values: self.fixed_values,\n                    rw_rows: sub_range.clone(),\n                    usable_rows: self.usable_rows,\n                    _marker: Default::default(),\n                });\n            }\n\n            Ok(sub_cs)\n        }\n\n        fn merge(&mut self, _sub_cs: Vec<Self>) -> Result<(), Error> {\n            Ok(())\n        }\n\n        fn annotate_column<A, AR>(&mut self, _annotation: A, _column: Column<Any>)\n        where\n            A: FnOnce() -> AR,\n            AR: Into<String>,\n        {\n            // Do nothing\n        }\n\n        /// Get the last assigned value of a cell.\n        fn query_advice(&self, column: Column<Advice>, row: usize) -> Result<F, Error> {\n            if !self.usable_rows.contains(&row) {\n                return Err(Error::not_enough_rows_available(self.k));\n            }\n            if !self.rw_rows.contains(&row) {\n                log::error!(\"query_advice: {:?}, row: {}\", column, row);\n                return Err(Error::Synthesis);\n            }\n            self.advice\n                .get(column.index())\n                .and_then(|v| {\n                    let mut r = F::ZERO;\n                    v.evaluate(row - self.rw_rows.start, unsafe {\n                        std::mem::transmute(&mut r)\n                    });\n                    Some(r)\n                })\n                .ok_or(Error::BoundsFailure)\n        }\n\n        fn query_fixed(&self, column: Column<Fixed>, row: usize) -> Result<F, Error> {\n            self.fixed_values\n                .get(column.index())\n                .and_then(|v| v.get(row))\n                .copied()\n                .ok_or(Error::BoundsFailure)\n        }\n\n        fn query_instance(&self, column: Column<Instance>, row: usize) -> Result<Value<F>, Error> {\n            if !self.usable_rows.contains(&row) {\n                return Err(Error::not_enough_rows_available(self.k));\n            }\n\n            self.instances\n                .get(column.index())\n                .and_then(|column| column.get(row))\n                .map(|v| Value::known(*v))\n                .ok_or(Error::BoundsFailure)\n        }\n\n        fn assign_advice<V, VR, A, AR>(\n            &mut self,\n            _: A,\n            column: Column<Advice>,\n            row: usize,\n            to: V,\n        ) -> Result<(), Error>\n        where\n            V: FnOnce() -> Value<VR>,\n            VR: Into<Assigned<F>>,\n            A: FnOnce() -> AR,\n            AR: Into<String>,\n        {\n            // Ignore assignment of advice column in different phase than current one.\n            if self.current_phase.0 < column.column_type().phase.0 {\n                return Ok(());\n            }\n\n            if !self.usable_rows.contains(&row) {\n                return Err(Error::not_enough_rows_available(self.k));\n            }\n\n            if !self.rw_rows.contains(&row) {\n                log::error!(\"assign_advice: {:?}, row: {}\", column, row);\n                return Err(Error::Synthesis);\n            }\n\n            let rational_evals = self\n                .advice\n                .get_mut(column.index())\n                .ok_or(Error::BoundsFailure)?;\n\n            let row_idx = row - self.rw_rows.start;\n            let value = to().into_field().assign()?;\n            match &value {\n                Assigned::Zero => rational_evals.set_zero(row_idx),\n                Assigned::Trivial(numerator) => {\n                    let numerator = unsafe { std::mem::transmute(numerator) };\n                    rational_evals.set_trivial(row_idx, numerator);\n                }\n                Assigned::Rational(numerator, denominator) => {\n                    let numerator = unsafe { std::mem::transmute(numerator) };\n                    let denominator = unsafe { std::mem::transmute(denominator) };\n                    rational_evals.set_rational(row_idx, numerator, denominator)\n                }\n            }\n\n            Ok(())\n        }\n\n        fn assign_fixed<V, VR, A, AR>(\n            &mut self,\n            _: A,\n            _: Column<Fixed>,\n            _: usize,\n            _: V,\n        ) -> Result<(), Error>\n        where\n            V: FnOnce() -> Value<VR>,\n            VR: Into<Assigned<F>>,\n            A: FnOnce() -> AR,\n            AR: Into<String>,\n        {\n            // We only care about advice columns here\n\n            Ok(())\n        }\n\n        fn copy(\n            &mut self,\n            _: Column<Any>,\n            _: usize,\n            _: Column<Any>,\n            _: usize,\n        ) -> Result<(), Error> {\n            // We only care about advice columns here\n\n            Ok(())\n        }\n\n        fn fill_from_row(\n            &mut self,\n            _: Column<Fixed>,\n            _: usize,\n            _: Value<Assigned<F>>,\n        ) -> Result<(), Error> {\n            Ok(())\n        }\n\n        fn get_challenge(&self, challenge: Challenge) -> Value<F> {\n            self.challenges\n                .get(&challenge.index())\n                .cloned()\n                .map(Value::known)\n                .unwrap_or_else(Value::unknown)\n        }\n\n        fn push_namespace<NR, N>(&mut self, _: N)\n        where\n            NR: Into<String>,\n            N: FnOnce() -> NR,\n        {\n            // Do nothing; we don't care about namespaces in this context.\n        }\n\n        fn pop_namespace(&mut self, _: Option<String>) {\n            // Do nothing; we don't care about namespaces in this context.\n        }\n    }\n\n    let (mut advice, challenges) = {\n        let num_advice_columns = pk.num_advice_columns();\n        let num_challenges = pk.num_challenges();\n        let mut advice = vec![\n            AdviceSingle {\n                advice_polys: vec![prover.empty_evals(); num_advice_columns],\n                advice_blinds: vec![Blind::default(); num_advice_columns],\n            };\n            instances.len()\n        ];\n        #[cfg(feature = \"phase-check\")]\n        let mut advice_assignments =\n            vec![vec![prover.empty_rational_evals(); num_advice_columns]; instances.len()];\n        let mut challenges = HashMap::<usize, Scheme::Scalar>::with_capacity(num_challenges);\n\n        let unusable_rows_start = prover.n() as usize - ((pk.blinding_factors() as usize) + 1);\n        for current_phase in pk.phases() {\n            let column_indices = meta\n                .advice_column_phase\n                .iter()\n                .enumerate()\n                .filter_map(|(column_index, phase)| {\n                    if current_phase == *phase {\n                        Some(column_index)\n                    } else {\n                        None\n                    }\n                })\n                .collect::<BTreeSet<_>>();\n\n            for (_circuit_idx, ((circuit, advice), instances)) in circuits\n                .iter()\n                .zip(advice.iter_mut())\n                .zip(instances)\n                .enumerate()\n            {\n                let advice_vec = Arc::new(vec![prover.empty_rational_evals(); num_advice_columns]);\n                let advice_slice = unsafe {\n                    let ptr = Arc::as_ptr(&advice_vec) as *mut Vec<RationalEvals>;\n                    let mut_ref = &mut (*ptr);\n                    mut_ref\n                        .iter_mut()\n                        .map(|advice| advice.create_view(0, advice.len()))\n                        .collect::<Vec<_>>()\n                };\n                let mut witness = WitnessCollection {\n                    k: prover.k(),\n                    current_phase,\n                    advice_vec,\n                    advice: advice_slice,\n                    instances,\n                    fixed_values: fixed_values.as_slice(),\n                    challenges: &challenges,\n                    // The prover will not be allowed to assign values to advice\n                    // cells that exist within inactive rows, which include some\n                    // number of blinding factors and an extra row for use in the\n                    // permutation argument.\n                    usable_rows: ..unusable_rows_start,\n                    rw_rows: 0..unusable_rows_start,\n                    _marker: std::marker::PhantomData,\n                };\n\n                // Synthesize the circuit to obtain the witness and other information.\n\n                log::info!(\"create_proof synthesize phase {current_phase:?} begin\");\n                ConcreteCircuit::FloorPlanner::synthesize(\n                    &mut witness,\n                    circuit,\n                    config.clone(),\n                    pk.constants(),\n                )?;\n                log::info!(\"create_proof synthesize phase {current_phase:?} end\");\n\n                #[cfg(feature = \"phase-check\")]\n                {\n                    let advice_column_phases = pk.advice_column_phases();\n                    for (idx, advice_col) in witness.advice.iter().enumerate() {\n                        if advice_column_phases[idx].0 < current_phase.0 {\n                            if advice_assignments[circuit_idx][idx].values != advice_col.values {\n                                log::error!(\n                                    \"advice column {}(at {:?}) changed when {:?}\",\n                                    idx,\n                                    advice_column_phases[idx],\n                                    current_phase\n                                );\n                            }\n                        }\n                    }\n                }\n\n                let advice_assigned_values = Arc::try_unwrap(witness.advice_vec)\n                    .expect(\"there must only one Arc for advice_vec\")\n                    .into_iter()\n                    .enumerate()\n                    .filter_map(|(column_index, advice)| {\n                        if column_indices.contains(&column_index) {\n                            #[cfg(feature = \"phase-check\")]\n                            {\n                                advice_assignments[circuit_idx][column_index] = advice.clone();\n                            }\n                            Some(advice)\n                        } else {\n                            None\n                        }\n                    })\n                    .collect::<Vec<_>>();\n                let mut advice_values = vec![Evals::zero(); advice_assigned_values.len()];\n                prover.batch_evaluate(\n                    advice_assigned_values.as_slice(),\n                    advice_values.as_mut_slice(),\n                );\n\n                // Add blinding factors to advice columns\n                for advice_values in &mut advice_values {\n                    //for cell in &mut advice_values[unusable_rows_start..] {\n                    //*cell = C::Scalar::random(&mut rng);\n                    //*cell = C::Scalar::one();\n                    //}\n                    let idx = advice_values.len() - 1;\n                    advice_values.set_value(idx, &Fr::one());\n                }\n\n                // Compute commitments to advice column polynomials\n                let blinds: Vec<_> = advice_values\n                    .iter()\n                    .map(|_| Blind(Fr::random(&mut rng)))\n                    .collect();\n                prover.batch_start(advice_values.len());\n                advice_values\n                    .iter()\n                    .enumerate()\n                    .for_each(|(idx, poly)| prover.batch_commit_lagrange(poly, idx));\n                let mut advice_commitments = vec![Scheme::Curve::identity(); advice_values.len()];\n                prover.batch_end(unsafe { std::mem::transmute(advice_commitments.as_mut_slice()) });\n\n                for commitment in &advice_commitments {\n                    transcript.write_point(unsafe { std::mem::transmute(*commitment) })?;\n                }\n                for ((column_index, advice_values), blind) in\n                    column_indices.iter().zip(advice_values).zip(blinds)\n                {\n                    advice.advice_polys[*column_index] = advice_values;\n                    advice.advice_blinds[*column_index] = blind;\n                }\n            }\n\n            for (index, phase) in pk.challenge_phases().iter().enumerate() {\n                if current_phase == *phase {\n                    let existing =\n                        challenges.insert(index, *transcript.squeeze_challenge_scalar::<()>());\n                    assert!(existing.is_none());\n                }\n            }\n        }\n\n        assert_eq!(challenges.len(), num_challenges);\n        let challenges = (0..num_challenges)\n            .map(|index| challenges.remove(&index).unwrap())\n            .collect::<Vec<_>>();\n\n        (advice, challenges)\n    };\n\n    drop(fixed_values);\n\n    prover.set_rng(R::rng_type(), rng.state().as_slice());\n    prover.set_transcript(transcript.state().as_slice());\n\n    let challenges = unsafe { std::mem::transmute::<_, Vec<crate::bn254::Fr>>(challenges) };\n    prover.create_proof(\n        pk,\n        instance.as_mut_slice(),\n        advice.as_mut_slice(),\n        challenges.as_slice(),\n    );\n    Ok(())\n}\n\n#[cfg(test)]\nmod test {\n    use crate::{\n        bn254::{SHPlonkProver as TachyonSHPlonkProver, TachyonProver},\n        consts::TranscriptType,\n    };\n    use ff::Field;\n    use halo2_proofs::poly::{\n        commitment::{Blind, Params, ParamsProver},\n        kzg::commitment::{KZGCommitmentScheme, ParamsKZG},\n        EvaluationDomain,\n    };\n    use halo2curves::bn256::{Bn256, Fr};\n    use rand_core::OsRng;\n\n    #[test]\n    fn test_params() {\n        let k = 4;\n        const N: u64 = 16;\n        let s = Fr::from(2);\n        let params = ParamsKZG::<Bn256>::unsafe_setup_with_s(k, s.clone());\n        let prover_from_s: TachyonSHPlonkProver<KZGCommitmentScheme<Bn256>> =\n            TachyonSHPlonkProver::<KZGCommitmentScheme<Bn256>>::new(\n                TranscriptType::Blake2b as u8,\n                k,\n                &s,\n            );\n        let prover_from_params = {\n            let mut params_bytes: Vec<u8> = vec![];\n            params.write(&mut params_bytes).unwrap();\n            TachyonSHPlonkProver::<KZGCommitmentScheme<Bn256>>::from_params(\n                TranscriptType::Blake2b as u8,\n                k,\n                params_bytes.as_slice(),\n            )\n        };\n\n        assert_eq!(prover_from_s.n(), N);\n        assert_eq!(prover_from_params.n(), N);\n\n        let expected_s_g2 = params.s_g2();\n        assert_eq!(prover_from_s.s_g2(), &expected_s_g2);\n        assert_eq!(prover_from_params.s_g2(), &expected_s_g2);\n\n        let domain = EvaluationDomain::new(1, k);\n        let scalars = (0..N).map(|_| Fr::random(OsRng)).collect::<Vec<_>>();\n        let mut evals = prover_from_s.empty_evals();\n        for i in 0..scalars.len() {\n            evals.set_value(i, &scalars[i]);\n        }\n        let lagrange = domain.lagrange_from_vec(scalars.clone());\n        let expected_commitment = params.commit_lagrange(&lagrange, Blind::default());\n        assert_eq!(prover_from_s.commit_lagrange(&evals), expected_commitment);\n        assert_eq!(\n            prover_from_params.commit_lagrange(&evals),\n            expected_commitment\n        );\n\n        let cpp_poly = prover_from_s.ifft(&evals);\n        let poly = domain.lagrange_to_coeff(lagrange);\n\n        let expected_commitment = params.commit(&poly, Blind::default());\n        assert_eq!(prover_from_s.commit(&cpp_poly), expected_commitment);\n        assert_eq!(prover_from_params.commit(&cpp_poly), expected_commitment);\n    }\n}\n"
  },
  {
    "path": "vendors/scroll_halo2/src/proving_key.rs",
    "content": "#[cfg(test)]\nmod test {\n    use crate::circuits::simple_circuit::SimpleCircuit;\n    use crate::{bn254::ProvingKey, consts::PCSType};\n    use halo2_proofs::{circuit::Value, plonk::keygen_pk2, poly::kzg::commitment::ParamsKZG};\n    use halo2curves::bn256::{Bn256, Fr, G1Affine};\n\n    #[test]\n    fn test_proving_key() {\n        // ANCHOR: test-circuit\n        // The number of rows in our circuit cannot exceed 2ᵏ. Since our example\n        // circuit is very small, we can pick a very small value here.\n        let k = 4;\n\n        // Prepare the private and public inputs to the circuit!\n        let constant = Fr::from(7);\n        let a = Fr::from(2);\n        let b = Fr::from(3);\n\n        // Instantiate the circuit with the private inputs.\n        let circuit = SimpleCircuit {\n            constant,\n            a: Value::known(a),\n            b: Value::known(b),\n        };\n\n        // Given the correct public input, our circuit will verify.\n        let s = Fr::from(2);\n        let params = ParamsKZG::<Bn256>::unsafe_setup_with_s(k, s);\n        let pk = keygen_pk2(&params, &circuit).expect(\"vk should not fail\");\n        let mut pk_bytes: Vec<u8> = vec![];\n        pk.write_including_cs(&mut pk_bytes).unwrap();\n        let tachyon_pk = ProvingKey::<G1Affine>::from(PCSType::GWC, pk_bytes.as_slice());\n\n        assert_eq!(\n            pk.get_vk().cs().advice_column_phase,\n            tachyon_pk.advice_column_phases()\n        );\n        assert_eq!(\n            pk.get_vk().cs().blinding_factors(),\n            tachyon_pk.blinding_factors() as usize\n        );\n        assert_eq!(\n            pk.get_vk().cs().challenge_phase,\n            tachyon_pk.challenge_phases()\n        );\n        assert_eq!(*pk.get_vk().cs().constants(), tachyon_pk.constants());\n        assert_eq!(\n            pk.get_vk().cs().num_advice_columns,\n            tachyon_pk.num_advice_columns()\n        );\n        assert_eq!(\n            pk.get_vk().cs().num_challenges(),\n            tachyon_pk.num_challenges()\n        );\n        assert_eq!(\n            pk.get_vk().cs().num_instance_columns,\n            tachyon_pk.num_instance_columns()\n        );\n        let phases = pk.get_vk().cs().phases().collect::<Vec<_>>();\n        assert_eq!(phases, tachyon_pk.phases());\n    }\n}\n"
  },
  {
    "path": "vendors/scroll_halo2/src/rng.rs",
    "content": "use crate::consts::RNGType;\n\npub trait SerializableRng {\n    fn state(&self) -> Vec<u8>;\n    fn rng_type() -> RNGType;\n}\n"
  },
  {
    "path": "vendors/scroll_halo2/src/sha.rs",
    "content": "// This is taken and modified from https://github.com/kroma-network/halo2-snark-aggregator/blob/2637b51/halo2-snark-aggregator-api/src/transcript/sha.rs.\n\nuse std::{\n    io::{self, Write},\n    marker::PhantomData,\n};\n\nuse digest::Digest;\nuse ff::{Field, FromUniformBytes, PrimeField};\nuse halo2_proofs::transcript::{Challenge255, EncodedChallenge, Transcript, TranscriptWrite};\nuse halo2curves::{Coordinates, CurveAffine};\n\n#[derive(Debug, Clone)]\npub struct ShaWrite<W: Write, C: CurveAffine, E: EncodedChallenge<C>, D: Digest> {\n    state: D,\n    writer: W,\n    _marker: PhantomData<(C, E)>,\n}\n\nimpl<W: Write, C: CurveAffine, E: EncodedChallenge<C>, D: Digest> ShaWrite<W, C, E, D> {\n    /// Initialize a transcript given an output buffer.\n    pub fn init(writer: W) -> Self {\n        ShaWrite {\n            state: D::new(),\n            writer,\n            _marker: PhantomData,\n        }\n    }\n\n    /// Conclude the interaction and return the output buffer (writer).\n    pub fn finalize(self) -> W {\n        // TODO: handle outstanding scalars? see issue #138\n        self.writer\n    }\n}\n\nimpl<W: Write, C: CurveAffine, D: Digest + Clone> TranscriptWrite<C, Challenge255<C>>\n    for ShaWrite<W, C, Challenge255<C>, D>\nwhere\n    C::Scalar: FromUniformBytes<64>,\n{\n    fn write_point(&mut self, point: C) -> io::Result<()> {\n        self.common_point(point)?;\n        // let compressed = point.to_bytes();\n\n        let coords = point.coordinates();\n        let x = coords\n            .map(|v| *v.x())\n            .unwrap_or(<C as CurveAffine>::Base::ZERO);\n        let y = coords\n            .map(|v| *v.y())\n            .unwrap_or(<C as CurveAffine>::Base::ZERO);\n\n        for base in &[&x, &y] {\n            self.writer.write_all(base.to_repr().as_ref())?;\n        }\n\n        Ok(())\n    }\n\n    fn write_scalar(&mut self, scalar: C::Scalar) -> io::Result<()> {\n        self.common_scalar(scalar)?;\n        let data = scalar.to_repr();\n\n        self.writer.write_all(data.as_ref())\n    }\n}\n\nimpl<W: Write, C: CurveAffine, D: Digest + Clone> Transcript<C, Challenge255<C>>\n    for ShaWrite<W, C, Challenge255<C>, D>\nwhere\n    C::Scalar: FromUniformBytes<64>,\n{\n    fn squeeze_challenge(&mut self) -> Challenge255<C> {\n        const SHA_PREFIX_CHALLENGE: u8 = 0;\n        self.state.update(&[SHA_PREFIX_CHALLENGE]);\n        let hasher = self.state.clone();\n        let result: [u8; 32] = hasher.finalize().as_slice().try_into().unwrap();\n\n        self.state = D::new();\n        self.state.update(result);\n\n        let mut bytes = result.to_vec();\n        bytes.resize(64, 0u8);\n        Challenge255::<C>::new(&bytes.try_into().unwrap())\n    }\n\n    fn common_point(&mut self, point: C) -> io::Result<()> {\n        const SHA_PREFIX_POINT: u8 = 1;\n        self.state.update(&[0u8; 31]);\n        self.state.update(&[SHA_PREFIX_POINT]);\n        let coords: Coordinates<C> = Option::from(point.coordinates()).ok_or_else(|| {\n            io::Error::new(\n                io::ErrorKind::Other,\n                \"cannot write points at infinity to the transcript\",\n            )\n        })?;\n\n        for base in &[coords.x(), coords.y()] {\n            let mut buf = base.to_repr().as_ref().to_vec();\n            buf.resize(32, 0u8);\n            buf.reverse();\n            self.state.update(buf);\n        }\n\n        Ok(())\n    }\n\n    fn common_scalar(&mut self, scalar: C::Scalar) -> io::Result<()> {\n        const SHA_PREFIX_SCALAR: u8 = 2;\n        self.state.update(&[0u8; 31]);\n        self.state.update(&[SHA_PREFIX_SCALAR]);\n\n        {\n            let mut buf = scalar.to_repr().as_ref().to_vec();\n            buf.resize(32, 0u8);\n            buf.reverse();\n            self.state.update(buf);\n        }\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "vendors/scroll_halo2/src/transcript.rs",
    "content": "#[cfg(test)]\nmod test {\n    use std::io;\n\n    use crate::{\n        bn254::{\n            Blake2bWrite as TachyonBlake2bWrite, PoseidonWrite as TachyonPoseidonWrite,\n            Sha256Write as TachyonSha256Write, TranscriptWriteState,\n        },\n        sha::ShaWrite,\n    };\n    use ff::Field;\n    use halo2_proofs::transcript::{\n        Blake2bWrite, Challenge255, ChallengeScalar, EncodedChallenge, PoseidonWrite,\n        TranscriptWrite, TranscriptWriterBuffer,\n    };\n    use halo2curves::{\n        bn256::{Bn256, Fr, G1Affine},\n        pairing::Engine,\n    };\n    use rand_core::OsRng;\n\n    #[derive(Clone, Copy, Debug)]\n    struct Theta;\n    type ChallengeTheta<F> = ChallengeScalar<F, Theta>;\n\n    fn squeeze_challenge<\n        E: Engine,\n        C: EncodedChallenge<E::G1Affine>,\n        T: TranscriptWrite<E::G1Affine, C>,\n    >(\n        transcript: &mut T,\n    ) -> ChallengeTheta<E::G1Affine> {\n        transcript.squeeze_challenge_scalar()\n    }\n\n    fn write_point_to_proof<\n        E: Engine,\n        C: EncodedChallenge<E::G1Affine>,\n        T: TranscriptWrite<E::G1Affine, C>,\n    >(\n        transcript: &mut T,\n        point: E::G1Affine,\n    ) -> io::Result<()> {\n        transcript.write_point(point)\n    }\n\n    fn write_scalar_to_proof<\n        E: Engine,\n        C: EncodedChallenge<E::G1Affine>,\n        T: TranscriptWrite<E::G1Affine, C>,\n    >(\n        transcript: &mut T,\n        scalar: E::Scalar,\n    ) -> io::Result<()> {\n        transcript.write_scalar(scalar)\n    }\n\n    #[test]\n    fn test_blake2b_write_scalar_to_proof() {\n        let fr = Fr::random(OsRng);\n        let proof = {\n            let mut transcript = Blake2bWrite::<_, G1Affine, Challenge255<_>>::init(vec![]);\n            write_scalar_to_proof::<Bn256, _, _>(&mut transcript, fr.clone()).unwrap();\n            transcript.finalize()\n        };\n        let proof_tachyon = {\n            let mut transcript = TachyonBlake2bWrite::init(vec![]);\n            write_scalar_to_proof::<Bn256, _, _>(&mut transcript, fr).unwrap();\n            transcript.finalize()\n        };\n        assert_eq!(proof, proof_tachyon);\n    }\n\n    #[test]\n    fn test_blake2b_write_point_to_proof() {\n        let point = G1Affine::random(OsRng);\n        let proof = {\n            let mut transcript = Blake2bWrite::<_, G1Affine, Challenge255<_>>::init(vec![]);\n            write_point_to_proof::<Bn256, _, _>(&mut transcript, point.clone()).unwrap();\n            transcript.finalize()\n        };\n        let proof_tachyon = {\n            let mut transcript = TachyonBlake2bWrite::init(vec![]);\n            write_point_to_proof::<Bn256, _, _>(&mut transcript, point).unwrap();\n            transcript.finalize()\n        };\n        assert_eq!(proof, proof_tachyon);\n    }\n\n    #[test]\n    fn test_blake2b_squeeze_challenge() {\n        let point = G1Affine::random(OsRng);\n        let theta = {\n            let mut transcript = Blake2bWrite::<_, G1Affine, Challenge255<_>>::init(vec![]);\n            write_point_to_proof::<Bn256, _, _>(&mut transcript, point.clone()).unwrap();\n            let theta = squeeze_challenge::<Bn256, _, _>(&mut transcript);\n            *theta\n        };\n        let theta_tachyon = {\n            let mut transcript = TachyonBlake2bWrite::init(vec![]);\n            write_point_to_proof::<Bn256, _, _>(&mut transcript, point).unwrap();\n            let theta = squeeze_challenge::<Bn256, _, _>(&mut transcript);\n            *theta\n        };\n        assert_eq!(theta, theta_tachyon);\n    }\n\n    #[test]\n    fn test_blake2b_state() {\n        let transcript = TachyonBlake2bWrite::<_, G1Affine, Challenge255<_>>::init(vec![]);\n        assert_eq!(\n            transcript.state(),\n            vec![\n                72, 201, 189, 242, 103, 230, 9, 106, 59, 167, 202, 132, 133, 174, 103, 187, 43,\n                248, 148, 254, 114, 243, 110, 60, 241, 54, 29, 95, 58, 245, 79, 165, 209, 130, 230,\n                173, 127, 82, 14, 81, 31, 108, 62, 43, 140, 104, 5, 155, 35, 220, 45, 148, 153,\n                244, 215, 109, 24, 79, 13, 112, 107, 164, 144, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                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,\n                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,\n                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,\n                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,\n                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,\n                0\n            ]\n        );\n    }\n\n    #[test]\n    fn test_poseidon_write_scalar_to_proof() {\n        let fr = Fr::random(OsRng);\n        let proof = {\n            let mut transcript = PoseidonWrite::<_, G1Affine, Challenge255<_>>::init(vec![]);\n            write_scalar_to_proof::<Bn256, _, _>(&mut transcript, fr.clone()).unwrap();\n            transcript.finalize()\n        };\n        let proof_tachyon = {\n            let mut transcript = TachyonPoseidonWrite::init(vec![]);\n            write_scalar_to_proof::<Bn256, _, _>(&mut transcript, fr).unwrap();\n            transcript.finalize()\n        };\n        assert_eq!(proof, proof_tachyon);\n    }\n\n    #[test]\n    fn test_poseidon_write_point_to_proof() {\n        let point = G1Affine::random(OsRng);\n        let proof = {\n            let mut transcript = PoseidonWrite::<_, G1Affine, Challenge255<_>>::init(vec![]);\n            write_point_to_proof::<Bn256, _, _>(&mut transcript, point.clone()).unwrap();\n            transcript.finalize()\n        };\n        let proof_tachyon = {\n            let mut transcript = TachyonPoseidonWrite::init(vec![]);\n            write_point_to_proof::<Bn256, _, _>(&mut transcript, point).unwrap();\n            transcript.finalize()\n        };\n        assert_eq!(proof, proof_tachyon);\n    }\n\n    #[test]\n    fn test_poseidon_squeeze_challenge() {\n        let point = G1Affine::random(OsRng);\n        let theta = {\n            let mut transcript = PoseidonWrite::<_, G1Affine, Challenge255<_>>::init(vec![]);\n            write_point_to_proof::<Bn256, _, _>(&mut transcript, point.clone()).unwrap();\n            let theta = squeeze_challenge::<Bn256, _, _>(&mut transcript);\n            *theta\n        };\n        let theta_tachyon = {\n            let mut transcript = TachyonPoseidonWrite::init(vec![]);\n            write_point_to_proof::<Bn256, _, _>(&mut transcript, point).unwrap();\n            let theta = squeeze_challenge::<Bn256, _, _>(&mut transcript);\n            *theta\n        };\n        assert_eq!(theta, theta_tachyon);\n    }\n\n    #[test]\n    fn test_poseidon_state() {\n        let transcript = TachyonPoseidonWrite::<_, G1Affine, Challenge255<_>>::init(vec![]);\n        assert_eq!(\n            transcript.state(),\n            vec![\n                9, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,\n                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,\n                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,\n                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,\n                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,\n                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,\n                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,\n                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,\n                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,\n                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,\n                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,\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n            ]\n        );\n    }\n\n    #[test]\n    fn test_sha256_write_scalar_to_proof() {\n        let fr = Fr::random(OsRng);\n        let proof = {\n            let mut transcript =\n                ShaWrite::<_, G1Affine, Challenge255<_>, sha2::Sha256>::init(vec![]);\n            write_scalar_to_proof::<Bn256, _, _>(&mut transcript, fr.clone()).unwrap();\n            transcript.finalize()\n        };\n        let proof_tachyon = {\n            let mut transcript = TachyonSha256Write::init(vec![]);\n            write_scalar_to_proof::<Bn256, _, _>(&mut transcript, fr).unwrap();\n            transcript.finalize()\n        };\n        assert_eq!(proof, proof_tachyon);\n    }\n\n    #[test]\n    fn test_sha256_write_point_to_proof() {\n        let point = G1Affine::random(OsRng);\n        let proof = {\n            let mut transcript =\n                ShaWrite::<_, G1Affine, Challenge255<_>, sha2::Sha256>::init(vec![]);\n            write_point_to_proof::<Bn256, _, _>(&mut transcript, point.clone()).unwrap();\n            transcript.finalize()\n        };\n        let proof_tachyon = {\n            let mut transcript = TachyonSha256Write::init(vec![]);\n            write_point_to_proof::<Bn256, _, _>(&mut transcript, point).unwrap();\n            transcript.finalize()\n        };\n        assert_eq!(proof, proof_tachyon);\n    }\n\n    #[test]\n    fn test_sha256_squeeze_challenge() {\n        let point = G1Affine::random(OsRng);\n        let theta = {\n            let mut transcript =\n                ShaWrite::<_, G1Affine, Challenge255<_>, sha2::Sha256>::init(vec![]);\n            write_point_to_proof::<Bn256, _, _>(&mut transcript, point.clone()).unwrap();\n            let theta = squeeze_challenge::<Bn256, _, _>(&mut transcript);\n            *theta\n        };\n        let theta_tachyon = {\n            let mut transcript = TachyonSha256Write::init(vec![]);\n            write_point_to_proof::<Bn256, _, _>(&mut transcript, point).unwrap();\n            let theta = squeeze_challenge::<Bn256, _, _>(&mut transcript);\n            *theta\n        };\n        assert_eq!(theta, theta_tachyon);\n    }\n\n    #[test]\n    fn test_sha256_state() {\n        let transcript = TachyonSha256Write::<_, G1Affine, Challenge255<_>>::init(vec![]);\n        assert_eq!(\n            transcript.state(),\n            vec![\n                103, 230, 9, 106, 133, 174, 103, 187, 114, 243, 110, 60, 58, 245, 79, 165, 127, 82,\n                14, 81, 140, 104, 5, 155, 171, 217, 131, 31, 25, 205, 224, 91, 0, 0, 0, 0, 0, 0, 0,\n                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,\n                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,\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0\n            ]\n        );\n    }\n}\n"
  },
  {
    "path": "vendors/scroll_halo2/src/xor_shift_rng.cc",
    "content": "#include \"vendors/scroll_halo2/include/xor_shift_rng.h\"\n\n#include <string.h>\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/rs/base/container_util.h\"\n\nnamespace tachyon::halo2_api {\n\nXORShiftRng::XORShiftRng(std::array<uint8_t, kSeedSize> seed) {\n  uint8_t seed_copy[kSeedSize];\n  memcpy(seed_copy, seed.data(), kSeedSize);\n  rng_ =\n      tachyon_rng_create_from_seed(TACHYON_RNG_XOR_SHIFT, seed_copy, kSeedSize);\n}\n\nXORShiftRng::~XORShiftRng() { tachyon_rng_destroy(rng_); }\n\nuint32_t XORShiftRng::next_u32() { return tachyon_rng_get_next_u32(rng_); }\n\nstd::unique_ptr<XORShiftRng> XORShiftRng::clone() const {\n  uint8_t state[kStateSize];\n  size_t state_len;\n  tachyon_rng_get_state(rng_, state, &state_len);\n  CHECK_EQ(state_len, kStateSize);\n  tachyon_rng* rng =\n      tachyon_rng_create_from_state(TACHYON_RNG_XOR_SHIFT, state, kStateSize);\n  return std::make_unique<XORShiftRng>(rng);\n}\n\nrust::Vec<uint8_t> XORShiftRng::state() const {\n  rust::Vec<uint8_t> ret = rs::CreateDefaultVector<uint8_t>(kStateSize);\n  size_t state_len;\n  tachyon_rng_get_state(rng_, ret.data(), &state_len);\n  CHECK_EQ(state_len, kStateSize);\n  return ret;\n}\n\nstd::unique_ptr<XORShiftRng> new_xor_shift_rng(\n    std::array<uint8_t, XORShiftRng::kSeedSize> seed) {\n  return std::make_unique<XORShiftRng>(seed);\n}\n\n}  // namespace tachyon::halo2_api\n"
  },
  {
    "path": "vendors/scroll_halo2/src/xor_shift_rng.rs",
    "content": "use crate::{consts::RNGType, rng::SerializableRng};\n\n#[cxx::bridge(namespace = \"tachyon::halo2_api\")]\npub mod ffi {\n    unsafe extern \"C++\" {\n        include!(\"vendors/scroll_halo2/include/xor_shift_rng.h\");\n\n        type XORShiftRng;\n\n        fn new_xor_shift_rng(seed: [u8; 16]) -> UniquePtr<XORShiftRng>;\n        fn next_u32(self: Pin<&mut XORShiftRng>) -> u32;\n        fn clone(&self) -> UniquePtr<XORShiftRng>;\n        fn state(&self) -> Vec<u8>;\n    }\n}\n\npub struct XORShiftRng {\n    inner: cxx::UniquePtr<ffi::XORShiftRng>,\n}\n\nimpl SerializableRng for XORShiftRng {\n    fn state(&self) -> Vec<u8> {\n        self.inner.state()\n    }\n\n    fn rng_type() -> RNGType {\n        RNGType::XORShift\n    }\n}\n\nimpl Clone for XORShiftRng {\n    fn clone(&self) -> Self {\n        Self {\n            inner: self.inner.clone(),\n        }\n    }\n}\n\nimpl rand_core::SeedableRng for XORShiftRng {\n    type Seed = [u8; 16];\n\n    fn from_seed(seed: Self::Seed) -> Self {\n        Self {\n            inner: ffi::new_xor_shift_rng(seed),\n        }\n    }\n}\n\nimpl rand_core::RngCore for XORShiftRng {\n    fn next_u32(&mut self) -> u32 {\n        self.inner.pin_mut().next_u32()\n    }\n\n    #[inline]\n    fn next_u64(&mut self) -> u64 {\n        rand_core::impls::next_u64_via_u32(self)\n    }\n\n    #[inline]\n    fn fill_bytes(&mut self, dest: &mut [u8]) {\n        rand_core::impls::fill_bytes_via_next(self, dest)\n    }\n\n    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> {\n        self.fill_bytes(dest);\n        Ok(())\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use rand_core::{RngCore, SeedableRng};\n\n    use crate::{consts::XOR_SHIFT_SEED, rng::SerializableRng};\n\n    #[test]\n    fn test_rng() {\n        let mut rng = rand_xorshift::XorShiftRng::from_seed(XOR_SHIFT_SEED);\n        let mut rng_tachyon = crate::xor_shift_rng::XORShiftRng::from_seed(XOR_SHIFT_SEED);\n\n        const LEN: i32 = 100;\n        let random_u64s = (0..LEN).map(|_| rng.next_u64()).collect::<Vec<_>>();\n        let random_u64s_tachyon = (0..LEN).map(|_| rng_tachyon.next_u64()).collect::<Vec<_>>();\n        assert_eq!(random_u64s, random_u64s_tachyon);\n    }\n\n    #[test]\n    fn test_clone() {\n        let mut rng = crate::xor_shift_rng::XORShiftRng::from_seed(XOR_SHIFT_SEED);\n        let mut rng_clone = rng.clone();\n\n        const LEN: i32 = 100;\n        let random_u64s = (0..LEN).map(|_| rng.next_u64()).collect::<Vec<_>>();\n        let random_u64s_clone = (0..LEN).map(|_| rng_clone.next_u64()).collect::<Vec<_>>();\n        assert_eq!(random_u64s, random_u64s_clone);\n    }\n\n    #[test]\n    fn test_state() {\n        let rng = crate::xor_shift_rng::XORShiftRng::from_seed(XOR_SHIFT_SEED);\n        assert_eq!(\n            rng.state(),\n            vec![89, 98, 190, 93, 118, 61, 49, 141, 23, 219, 55, 50, 84, 6, 188, 229]\n        );\n    }\n}\n"
  },
  {
    "path": "vendors/sp1/BUILD.bazel",
    "content": "load(\"@crate_index//:defs.bzl\", \"aliases\", \"all_crate_deps\")\nload(\"@cxx.rs//tools/bazel:rust_cxx_bridge.bzl\", \"rust_cxx_bridge\")\nload(\"@local_config_cuda//cuda:build_defs.bzl\", \"if_cuda\")\nload(\"//bazel:tachyon.bzl\", \"if_gpu_is_configured\", \"if_has_openmp_on_macos\")\nload(\"//bazel:tachyon_cc.bzl\", \"tachyon_cc_library\", \"tachyon_openmp_linkopts\")\nload(\"//bazel:tachyon_rust.bzl\", \"tachyon_rust_library\", \"tachyon_rust_test\")\n\nFEATURES = if_gpu_is_configured([\"gpu\"])\n\ntachyon_rust_library(\n    name = \"sp1\",\n    srcs = glob([\"src/**/*.rs\"]),\n    aliases = aliases(),\n    crate_features = FEATURES,\n    proc_macro_deps = all_crate_deps(proc_macro = True),\n    deps = all_crate_deps(normal = True) + [\n        \":baby_bear_poseidon2_commitment_vec\",\n        \":baby_bear_poseidon2_cxx_bridge\",\n        \":baby_bear_poseidon2_domains\",\n        \":baby_bear_poseidon2_duplex_challenger\",\n        \":baby_bear_poseidon2_fri_proof\",\n        \":baby_bear_poseidon2_lde_vec\",\n        \":baby_bear_poseidon2_opened_values\",\n        \":baby_bear_poseidon2_opening_points\",\n        \":baby_bear_poseidon2_opening_proof\",\n        \":baby_bear_poseidon2_prover_data\",\n        \":baby_bear_poseidon2_prover_data_vec\",\n        \":baby_bear_poseidon2_two_adic_fri_pcs\",\n        \"//tachyon/rs:tachyon_rs\",\n    ],\n)\n\n# NOTE(chokobole): This attribute could be added to `sp1_test`,\n# but this approach doesn't work when compiling with nvcc.\n# rustc_flags = if_has_openmp([\"-lgomp\"]),\ncc_library(\n    name = \"openmp\",\n    linkopts = tachyon_openmp_linkopts(),\n)\n\ntachyon_rust_test(\n    name = \"sp1_test\",\n    size = \"small\",\n    # NOTE(chokobole): Timeout time increased due to CI.\n    timeout = \"moderate\",\n    aliases = aliases(),\n    crate = \":sp1\",\n    crate_features = FEATURES,\n    proc_macro_deps = all_crate_deps(proc_macro_dev = True),\n    deps = all_crate_deps(normal_dev = True) + [\n        \":openmp\",\n        \"@local_config_gmp//:gmp\",\n    ] + if_cuda([\n        \"@local_config_cuda//cuda:cudart_static\",\n    ]) + if_has_openmp_on_macos([\n        \"@local_config_omp//:omp\",\n    ]),\n)\n\nrust_cxx_bridge(\n    name = \"baby_bear_poseidon2_cxx_bridge\",\n    src = \"src/baby_bear_poseidon2.rs\",\n    args = if_gpu_is_configured([\n        '--cfg=feature=\"gpu\"',\n    ]),\n    deps = [\":baby_bear_poseidon2_api_hdrs\"],\n)\n\ntachyon_cc_library(\n    name = \"baby_bear_poseidon2_api_hdrs\",\n    hdrs = [\n        \"include/baby_bear_poseidon2_commitment_vec.h\",\n        \"include/baby_bear_poseidon2_domains.h\",\n        \"include/baby_bear_poseidon2_duplex_challenger.h\",\n        \"include/baby_bear_poseidon2_fri_proof.h\",\n        \"include/baby_bear_poseidon2_lde_vec.h\",\n        \"include/baby_bear_poseidon2_opened_values.h\",\n        \"include/baby_bear_poseidon2_opening_points.h\",\n        \"include/baby_bear_poseidon2_opening_proof.h\",\n        \"include/baby_bear_poseidon2_prover_data.h\",\n        \"include/baby_bear_poseidon2_prover_data_vec.h\",\n        \"include/baby_bear_poseidon2_two_adic_fri_pcs.h\",\n    ],\n    deps = [\n        \"//tachyon/c/zk/air/sp1:baby_bear_poseidon2_duplex_challenger\",\n        \"//tachyon/c/zk/air/sp1:baby_bear_poseidon2_two_adic_fri\",\n        \"@cxx.rs//:core\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"baby_bear_poseidon2_commitment_vec\",\n    srcs = [\"src/baby_bear_poseidon2_commitment_vec.cc\"],\n    deps = [\n        \":baby_bear_poseidon2_api_hdrs\",\n        \":baby_bear_poseidon2_cxx_bridge/include\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"baby_bear_poseidon2_domains\",\n    srcs = [\"src/baby_bear_poseidon2_domains.cc\"],\n    deps = [\n        \":baby_bear_poseidon2_api_hdrs\",\n        \":baby_bear_poseidon2_cxx_bridge/include\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"baby_bear_poseidon2_duplex_challenger\",\n    srcs = [\"src/baby_bear_poseidon2_duplex_challenger.cc\"],\n    deps = [\n        \":baby_bear_poseidon2_api_hdrs\",\n        \":baby_bear_poseidon2_cxx_bridge/include\",\n        \"//tachyon/rs/base:container_util\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"baby_bear_poseidon2_fri_proof\",\n    srcs = [\"src/baby_bear_poseidon2_fri_proof.cc\"],\n    deps = [\n        \":baby_bear_poseidon2_api_hdrs\",\n        \":baby_bear_poseidon2_cxx_bridge/include\",\n        \"//tachyon/rs/base:container_util\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"baby_bear_poseidon2_lde_vec\",\n    srcs = [\"src/baby_bear_poseidon2_lde_vec.cc\"],\n    deps = [\n        \":baby_bear_poseidon2_api_hdrs\",\n        \":baby_bear_poseidon2_cxx_bridge/include\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"baby_bear_poseidon2_opened_values\",\n    srcs = [\"src/baby_bear_poseidon2_opened_values.cc\"],\n    deps = [\n        \":baby_bear_poseidon2_api_hdrs\",\n        \":baby_bear_poseidon2_cxx_bridge/include\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"baby_bear_poseidon2_opening_points\",\n    srcs = [\"src/baby_bear_poseidon2_opening_points.cc\"],\n    deps = [\n        \":baby_bear_poseidon2_api_hdrs\",\n        \":baby_bear_poseidon2_cxx_bridge/include\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"baby_bear_poseidon2_opening_proof\",\n    srcs = [\"src/baby_bear_poseidon2_opening_proof.cc\"],\n    deps = [\n        \":baby_bear_poseidon2_api_hdrs\",\n        \":baby_bear_poseidon2_cxx_bridge/include\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"baby_bear_poseidon2_prover_data\",\n    srcs = [\"src/baby_bear_poseidon2_prover_data.cc\"],\n    deps = [\n        \":baby_bear_poseidon2_api_hdrs\",\n        \":baby_bear_poseidon2_cxx_bridge/include\",\n        \"//tachyon/rs/base:container_util\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"baby_bear_poseidon2_prover_data_vec\",\n    srcs = [\"src/baby_bear_poseidon2_prover_data_vec.cc\"],\n    deps = [\n        \":baby_bear_poseidon2_api_hdrs\",\n        \":baby_bear_poseidon2_cxx_bridge/include\",\n    ],\n)\n\ntachyon_cc_library(\n    name = \"baby_bear_poseidon2_two_adic_fri_pcs\",\n    srcs = [\"src/baby_bear_poseidon2_two_adic_fri_pcs.cc\"],\n    deps = [\n        \":baby_bear_poseidon2_api_hdrs\",\n        \":baby_bear_poseidon2_cxx_bridge/include\",\n    ],\n)\n"
  },
  {
    "path": "vendors/sp1/Cargo.toml",
    "content": "[package]\nname = \"tachyon_sp1\"\nversion = \"0.0.1\"\nauthors = [\"The Tachyon Authors <tachyon-discuss@kroma.network>\"]\nedition = \"2021\"\nrust-version = \"1.56.1\"\ndescription = \"\"\"\nSP1 unittest based on tachyon\n\"\"\"\nlicense = \"MIT OR Apache-2.0\"\nrepository = \"https://github.com/kroma-network/tachyon\"\nreadme = \"README.md\"\ncategories = [\"cryptography\"]\nkeywords = [\"tachyon\", \"sp1\", \"proofs\", \"zkp\", \"zkSNARKs\"]\npublish = false\n\n[dependencies]\nanyhow = \"1.0.83\"\nbincode = \"1.3.3\"\ncxx = \"1.0\"\nitertools = \"0.13.0\"\np3-baby-bear = \"0.1.3-succinct\"\np3-challenger = \"0.1.3-succinct\"\np3-commit = \"0.1.3-succinct\"\np3-dft = \"0.1.3-succinct\"\np3-field = \"0.1.3-succinct\"\np3-fri = \"0.1.3-succinct\"\np3-matrix = \"0.1.3-succinct\"\np3-maybe-rayon = \"0.1.3-succinct\"\np3-poseidon2 = \"0.1.3-succinct\"\np3-symmetric = \"0.1.3-succinct\"\np3-util = \"0.1.3-succinct\"\nserde = { version = \"1.0\", default-features = false, features = [\n  \"derive\",\n  \"alloc\",\n] }\nserde_bytes = \"0.11.14\"\nsp1-core = { git = \"https://github.com/kroma-network/sp1.git\", rev = \"dd032eb\" }\nsp1-recursion-core = { git = \"https://github.com/kroma-network/sp1.git\", rev = \"dd032eb\" }\nsp1-recursion-compiler = { git = \"https://github.com/kroma-network/sp1.git\", rev = \"dd032eb\" }\nsp1-recursion-program = { git = \"https://github.com/kroma-network/sp1.git\", rev = \"dd032eb\" }\ntracing = \"0.1.37\"\ntachyon_rs = { path = \"../../tachyon/rs\" }\n\n[dev-dependencies]\nrand = \"0.8.5\"\nrand_chacha = \"0.3.1\"\n\n[features]\ndefault = []\ngpu = []\n"
  },
  {
    "path": "vendors/sp1/include/baby_bear_poseidon2_commitment_vec.h",
    "content": "#ifndef VENDORS_SP1_INCLUDE_BABY_BEAR_POSEIDON2_COMMITMENT_VEC_H_\n#define VENDORS_SP1_INCLUDE_BABY_BEAR_POSEIDON2_COMMITMENT_VEC_H_\n\n#include <stddef.h>\n\n#include <memory>\n\n#include \"rust/cxx.h\"\n\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_commitment_vec.h\"\n\nnamespace tachyon::sp1_api::baby_bear_poseidon2 {\n\nstruct TachyonBabyBear;\n\nclass CommitmentVec {\n public:\n  explicit CommitmentVec(\n      tachyon_sp1_baby_bear_poseidon2_commitment_vec* commitment_vec)\n      : commitment_vec_(commitment_vec) {}\n  CommitmentVec(const CommitmentVec& other) = delete;\n  CommitmentVec& operator=(const CommitmentVec& other) = delete;\n  ~CommitmentVec();\n\n  const tachyon_sp1_baby_bear_poseidon2_commitment_vec* commitment_vec() const {\n    return commitment_vec_;\n  }\n\n  void set(size_t round, rust::Slice<const TachyonBabyBear> commitment);\n\n private:\n  tachyon_sp1_baby_bear_poseidon2_commitment_vec* commitment_vec_;\n};\n\nstd::unique_ptr<CommitmentVec> new_commitment_vec(size_t rounds);\n\n}  // namespace tachyon::sp1_api::baby_bear_poseidon2\n\n#endif  // VENDORS_SP1_INCLUDE_BABY_BEAR_POSEIDON2_COMMITMENT_VEC_H_\n"
  },
  {
    "path": "vendors/sp1/include/baby_bear_poseidon2_domains.h",
    "content": "#ifndef VENDORS_SP1_INCLUDE_BABY_BEAR_POSEIDON2_DOMAINS_H_\n#define VENDORS_SP1_INCLUDE_BABY_BEAR_POSEIDON2_DOMAINS_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <memory>\n\n#include \"rust/cxx.h\"\n\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_domains.h\"\n\nnamespace tachyon::sp1_api::baby_bear_poseidon2 {\n\nstruct TachyonBabyBear;\n\nclass Domains {\n public:\n  explicit Domains(tachyon_sp1_baby_bear_poseidon2_domains* domains)\n      : domains_(domains) {}\n  Domains(const Domains& other) = delete;\n  Domains& operator=(const Domains& other) = delete;\n  ~Domains();\n\n  const tachyon_sp1_baby_bear_poseidon2_domains* domains() const {\n    return domains_;\n  }\n\n  void allocate(size_t round, size_t size);\n  void set(size_t round, size_t idx, uint32_t log_n,\n           const TachyonBabyBear& shift);\n\n private:\n  tachyon_sp1_baby_bear_poseidon2_domains* domains_;\n};\n\nstd::unique_ptr<Domains> new_domains(size_t rounds);\n\n}  // namespace tachyon::sp1_api::baby_bear_poseidon2\n\n#endif  // VENDORS_SP1_INCLUDE_BABY_BEAR_POSEIDON2_DOMAINS_H_\n"
  },
  {
    "path": "vendors/sp1/include/baby_bear_poseidon2_duplex_challenger.h",
    "content": "#ifndef VENDORS_SP1_INCLUDE_BABY_BEAR_POSEIDON2_DUPLEX_CHALLENGER_H_\n#define VENDORS_SP1_INCLUDE_BABY_BEAR_POSEIDON2_DUPLEX_CHALLENGER_H_\n\n#include <stdint.h>\n\n#include <memory>\n\n#include \"rust/cxx.h\"\n\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_duplex_challenger.h\"\n\nnamespace tachyon::sp1_api::baby_bear_poseidon2 {\n\nstruct TachyonBabyBear;\n\nclass DuplexChallenger {\n public:\n  DuplexChallenger();\n  explicit DuplexChallenger(\n      tachyon_sp1_baby_bear_poseidon2_duplex_challenger* challenger)\n      : challenger_(challenger) {}\n  DuplexChallenger(const DuplexChallenger& other) = delete;\n  DuplexChallenger& operator=(const DuplexChallenger& other) = delete;\n  ~DuplexChallenger();\n\n  tachyon_sp1_baby_bear_poseidon2_duplex_challenger* challenger() {\n    return challenger_;\n  }\n\n  void observe(const TachyonBabyBear& value);\n  rust::Box<TachyonBabyBear> sample();\n  rust::Vec<uint8_t> write_hint() const;\n  std::unique_ptr<DuplexChallenger> clone() const;\n\n private:\n  tachyon_sp1_baby_bear_poseidon2_duplex_challenger* challenger_;\n};\n\nstd::unique_ptr<DuplexChallenger> new_duplex_challenger();\n\n}  // namespace tachyon::sp1_api::baby_bear_poseidon2\n\n#endif  // VENDORS_SP1_INCLUDE_BABY_BEAR_POSEIDON2_DUPLEX_CHALLENGER_H_\n"
  },
  {
    "path": "vendors/sp1/include/baby_bear_poseidon2_fri_proof.h",
    "content": "#ifndef VENDORS_SP1_INCLUDE_BABY_BEAR_POSEIDON2_FRI_PROOF_H_\n#define VENDORS_SP1_INCLUDE_BABY_BEAR_POSEIDON2_FRI_PROOF_H_\n\n#include <stdint.h>\n\n#include <memory>\n\n#include \"rust/cxx.h\"\n\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_fri_proof.h\"\n\nnamespace tachyon::sp1_api::baby_bear_poseidon2 {\n\nclass FriProof {\n public:\n  explicit FriProof(tachyon_sp1_baby_bear_poseidon2_fri_proof* proof)\n      : proof_(proof) {}\n  FriProof(const FriProof& other) = delete;\n  FriProof& operator=(const FriProof& other) = delete;\n  ~FriProof();\n\n  const tachyon_sp1_baby_bear_poseidon2_fri_proof* proof() const {\n    return proof_;\n  }\n\n  bool eq(const FriProof& other) const;\n  rust::Vec<uint8_t> write_hint() const;\n  rust::Vec<uint8_t> serialize() const;\n  std::unique_ptr<FriProof> clone() const;\n\n private:\n  tachyon_sp1_baby_bear_poseidon2_fri_proof* proof_;\n};\n\nstd::unique_ptr<FriProof> deserialize_fri_proof(\n    rust::Slice<const uint8_t> data);\n\n}  // namespace tachyon::sp1_api::baby_bear_poseidon2\n\n#endif  // VENDORS_SP1_INCLUDE_BABY_BEAR_POSEIDON2_FRI_PROOF_H_\n"
  },
  {
    "path": "vendors/sp1/include/baby_bear_poseidon2_lde_vec.h",
    "content": "#ifndef VENDORS_SP1_INCLUDE_BABY_BEAR_POSEIDON2_LDE_VEC_H_\n#define VENDORS_SP1_INCLUDE_BABY_BEAR_POSEIDON2_LDE_VEC_H_\n\n#include <stddef.h>\n\n#include <memory>\n\n#include \"rust/cxx.h\"\n\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_lde_vec.h\"\n\nnamespace tachyon::sp1_api::baby_bear_poseidon2 {\n\nstruct TachyonBabyBear;\n\nclass LDEVec {\n public:\n  explicit LDEVec(tachyon_sp1_baby_bear_poseidon2_lde_vec* lde_vec)\n      : lde_vec_(lde_vec) {}\n  LDEVec(const LDEVec& other) = delete;\n  LDEVec& operator=(const LDEVec& other) = delete;\n  ~LDEVec();\n\n  tachyon_sp1_baby_bear_poseidon2_lde_vec* lde_vec() { return lde_vec_; }\n\n  void add(rust::Slice<const TachyonBabyBear> lde, size_t cols);\n\n private:\n  tachyon_sp1_baby_bear_poseidon2_lde_vec* lde_vec_;\n};\n\nstd::unique_ptr<LDEVec> new_lde_vec();\n\n}  // namespace tachyon::sp1_api::baby_bear_poseidon2\n\n#endif  // VENDORS_SP1_INCLUDE_BABY_BEAR_POSEIDON2_LDE_VEC_H_\n"
  },
  {
    "path": "vendors/sp1/include/baby_bear_poseidon2_opened_values.h",
    "content": "#ifndef VENDORS_SP1_INCLUDE_BABY_BEAR_POSEIDON2_OPENED_VALUES_H_\n#define VENDORS_SP1_INCLUDE_BABY_BEAR_POSEIDON2_OPENED_VALUES_H_\n\n#include <stddef.h>\n\n#include <memory>\n\n#include \"rust/cxx.h\"\n\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_opened_values.h\"\n\nnamespace tachyon::sp1_api::baby_bear_poseidon2 {\n\nstruct TachyonBabyBear4;\n\nclass OpenedValues {\n public:\n  explicit OpenedValues(\n      tachyon_sp1_baby_bear_poseidon2_opened_values* opened_values)\n      : opened_values_(opened_values) {}\n  OpenedValues(const OpenedValues& other) = delete;\n  OpenedValues& operator=(const OpenedValues& other) = delete;\n  ~OpenedValues();\n\n  const tachyon_sp1_baby_bear_poseidon2_opened_values* opened_values() const {\n    return opened_values_;\n  }\n\n  void allocate_outer(size_t round, size_t rows, size_t cols);\n  void allocate_inner(size_t round, size_t row, size_t cols, size_t size);\n  void set(size_t round, size_t row, size_t col, size_t idx,\n           const TachyonBabyBear4& value);\n\n private:\n  tachyon_sp1_baby_bear_poseidon2_opened_values* opened_values_;\n};\n\nstd::unique_ptr<OpenedValues> new_opened_values(size_t rounds);\n\n}  // namespace tachyon::sp1_api::baby_bear_poseidon2\n\n#endif  // VENDORS_SP1_INCLUDE_BABY_BEAR_POSEIDON2_OPENED_VALUES_H_\n"
  },
  {
    "path": "vendors/sp1/include/baby_bear_poseidon2_opening_points.h",
    "content": "#ifndef VENDORS_SP1_INCLUDE_BABY_BEAR_POSEIDON2_OPENING_POINTS_H_\n#define VENDORS_SP1_INCLUDE_BABY_BEAR_POSEIDON2_OPENING_POINTS_H_\n\n#include <stddef.h>\n\n#include <memory>\n\n#include \"tachyon/c/math/finite_fields/baby_bear/baby_bear4.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_opening_points.h\"\n\nnamespace tachyon::sp1_api::baby_bear_poseidon2 {\n\nstruct TachyonBabyBear4;\n\nclass OpeningPoints {\n public:\n  explicit OpeningPoints(\n      tachyon_sp1_baby_bear_poseidon2_opening_points* opening_points)\n      : opening_points_(opening_points) {}\n  OpeningPoints(const OpeningPoints& other) = delete;\n  OpeningPoints& operator=(const OpeningPoints& other) = delete;\n  ~OpeningPoints();\n\n  const tachyon_sp1_baby_bear_poseidon2_opening_points* opening_points() const {\n    return opening_points_;\n  }\n\n  std::unique_ptr<OpeningPoints> clone() const;\n  void allocate(size_t round, size_t rows, size_t cols);\n  void set(size_t round, size_t row, size_t col, const TachyonBabyBear4& point);\n\n private:\n  tachyon_sp1_baby_bear_poseidon2_opening_points* opening_points_ = nullptr;\n};\n\nstd::unique_ptr<OpeningPoints> new_opening_points(size_t rounds);\n\n}  // namespace tachyon::sp1_api::baby_bear_poseidon2\n\n#endif  // VENDORS_SP1_INCLUDE_BABY_BEAR_POSEIDON2_OPENING_POINTS_H_\n"
  },
  {
    "path": "vendors/sp1/include/baby_bear_poseidon2_opening_proof.h",
    "content": "#ifndef VENDORS_SP1_INCLUDE_BABY_BEAR_POSEIDON2_OPENING_PROOF_H_\n#define VENDORS_SP1_INCLUDE_BABY_BEAR_POSEIDON2_OPENING_PROOF_H_\n\n#include <memory>\n\n#include \"rust/cxx.h\"\n\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_fri_proof.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_opened_values.h\"\n\nnamespace tachyon::sp1_api::baby_bear_poseidon2 {\n\nclass FriProof;\n\nclass OpeningProof {\n public:\n  OpeningProof();\n  OpeningProof(const OpeningProof& other) = delete;\n  OpeningProof& operator=(const OpeningProof& other) = delete;\n  ~OpeningProof();\n\n  tachyon_sp1_baby_bear_poseidon2_opened_values** opened_values_ptr() {\n    return &opened_values_;\n  }\n  tachyon_sp1_baby_bear_poseidon2_fri_proof** proof_ptr() { return &proof_; }\n\n  rust::Vec<uint8_t> serialize_to_opened_values() const;\n  std::unique_ptr<FriProof> take_fri_proof();\n\n private:\n  tachyon_sp1_baby_bear_poseidon2_opened_values* opened_values_;\n  tachyon_sp1_baby_bear_poseidon2_fri_proof* proof_;\n};\n\n}  // namespace tachyon::sp1_api::baby_bear_poseidon2\n\n#endif  // VENDORS_SP1_INCLUDE_BABY_BEAR_POSEIDON2_OPENING_PROOF_H_\n"
  },
  {
    "path": "vendors/sp1/include/baby_bear_poseidon2_prover_data.h",
    "content": "#ifndef VENDORS_SP1_INCLUDE_BABY_BEAR_POSEIDON2_PROVER_DATA_H_\n#define VENDORS_SP1_INCLUDE_BABY_BEAR_POSEIDON2_PROVER_DATA_H_\n\n#include <memory>\n\n#include \"rust/cxx.h\"\n\n#include \"tachyon/c/math/finite_fields/baby_bear/baby_bear.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_constants.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_field_merkle_tree.h\"\n\nnamespace tachyon::sp1_api::baby_bear_poseidon2 {\n\nstruct TachyonBabyBear;\n\nclass ProverData {\n public:\n  ProverData() = default;\n  explicit ProverData(tachyon_sp1_baby_bear_poseidon2_field_merkle_tree* tree)\n      : tree_(tree) {}\n  ProverData(const ProverData& other) = delete;\n  ProverData& operator=(const ProverData& other) = delete;\n  ~ProverData();\n\n  tachyon_baby_bear* commitment() { return commitment_; }\n  tachyon_sp1_baby_bear_poseidon2_field_merkle_tree** tree_ptr() {\n    return &tree_;\n  }\n  const tachyon_sp1_baby_bear_poseidon2_field_merkle_tree* tree() const {\n    return tree_;\n  }\n\n  bool eq(const ProverData& other) const;\n  void write_commit(rust::Slice<TachyonBabyBear> values) const;\n  rust::Vec<uint8_t> serialize() const;\n  std::unique_ptr<ProverData> clone() const;\n\n private:\n  tachyon_baby_bear commitment_[TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_CHUNK];\n  tachyon_sp1_baby_bear_poseidon2_field_merkle_tree* tree_ = nullptr;\n};\n\nstd::unique_ptr<ProverData> deserialize_prover_data(\n    rust::Slice<const uint8_t> data);\n\n}  // namespace tachyon::sp1_api::baby_bear_poseidon2\n\n#endif  // VENDORS_SP1_INCLUDE_BABY_BEAR_POSEIDON2_PROVER_DATA_H_\n"
  },
  {
    "path": "vendors/sp1/include/baby_bear_poseidon2_prover_data_vec.h",
    "content": "#ifndef VENDORS_SP1_INCLUDE_BABY_BEAR_POSEIDON2_PROVER_DATA_VEC_H_\n#define VENDORS_SP1_INCLUDE_BABY_BEAR_POSEIDON2_PROVER_DATA_VEC_H_\n\n#include <stddef.h>\n\n#include <memory>\n\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_field_merkle_tree_vec.h\"\n\nnamespace tachyon::sp1_api::baby_bear_poseidon2 {\n\nclass ProverData;\nstruct TachyonBabyBear;\n\nclass ProverDataVec {\n public:\n  explicit ProverDataVec(\n      tachyon_sp1_baby_bear_poseidon2_field_merkle_tree_vec* tree_vec)\n      : tree_vec_(tree_vec) {}\n  ProverDataVec(const ProverDataVec& other) = delete;\n  ProverDataVec& operator=(const ProverDataVec& other) = delete;\n  ~ProverDataVec();\n\n  const tachyon_sp1_baby_bear_poseidon2_field_merkle_tree_vec* tree_vec()\n      const {\n    return tree_vec_;\n  }\n\n  std::unique_ptr<ProverDataVec> clone() const;\n  void set(size_t round, const ProverData& prover_data);\n\n private:\n  tachyon_sp1_baby_bear_poseidon2_field_merkle_tree_vec* tree_vec_ = nullptr;\n};\n\nstd::unique_ptr<ProverDataVec> new_prover_data_vec(size_t rounds);\n\n}  // namespace tachyon::sp1_api::baby_bear_poseidon2\n\n#endif  // VENDORS_SP1_INCLUDE_BABY_BEAR_POSEIDON2_PROVER_DATA_VEC_H_\n"
  },
  {
    "path": "vendors/sp1/include/baby_bear_poseidon2_two_adic_fri_pcs.h",
    "content": "#ifndef VENDORS_SP1_INCLUDE_BABY_BEAR_POSEIDON2_TWO_ADIC_FRI_PCS_H_\n#define VENDORS_SP1_INCLUDE_BABY_BEAR_POSEIDON2_TWO_ADIC_FRI_PCS_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <memory>\n\n#include \"rust/cxx.h\"\n\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_two_adic_fri.h\"\n\nnamespace tachyon::sp1_api::baby_bear_poseidon2 {\n\nclass CommitmentVec;\nclass CommitResult;\nclass Domains;\nclass DuplexChallenger;\nclass FriProof;\nclass LDEVec;\nclass OpeningPoints;\nclass OpeningProof;\nclass ProverData;\nclass ProverDataVec;\nclass OpenedValues;\nstruct TachyonBabyBear;\nstruct TachyonBabyBear4;\n\nclass TwoAdicFriPcs {\n public:\n  TwoAdicFriPcs(size_t log_blowup, size_t num_queries,\n                size_t proof_of_work_bits);\n  TwoAdicFriPcs(const TwoAdicFriPcs& other) = delete;\n  TwoAdicFriPcs& operator=(const TwoAdicFriPcs& other) = delete;\n  ~TwoAdicFriPcs();\n\n  void coset_lde_batch(rust::Slice<TachyonBabyBear> values, size_t cols,\n                       rust::Slice<TachyonBabyBear> extended_values,\n                       const TachyonBabyBear& shift) const;\n  std::unique_ptr<ProverData> commit(LDEVec& lde_vec) const;\n  std::unique_ptr<OpeningProof> do_open(const ProverDataVec& prover_data_vec,\n                                        const OpeningPoints& opening_points,\n                                        DuplexChallenger& challenger) const;\n  bool do_verify(const CommitmentVec& commitment_vec, const Domains& domains,\n                 const OpeningPoints& opening_points,\n                 const OpenedValues& opened_values, const FriProof& proof,\n                 DuplexChallenger& challenger) const;\n\n private:\n  tachyon_sp1_baby_bear_poseidon2_two_adic_fri* pcs_;\n};\n\nstd::unique_ptr<TwoAdicFriPcs> new_two_adic_fri_pcs(size_t log_blowup,\n                                                    size_t num_queries,\n                                                    size_t proof_of_work_bits);\n\n}  // namespace tachyon::sp1_api::baby_bear_poseidon2\n\n#endif  // VENDORS_SP1_INCLUDE_BABY_BEAR_POSEIDON2_TWO_ADIC_FRI_PCS_H_\n"
  },
  {
    "path": "vendors/sp1/src/baby_bear_poseidon2.rs",
    "content": "use std::{fmt::Debug, io::Cursor, marker::PhantomData, pin::Pin};\n\nuse p3_baby_bear::{BabyBear, DiffusionMatrixBabyBear};\nuse p3_challenger::{CanObserve, CanSample, CanSampleBits, FieldChallenger, GrindingChallenger};\nuse p3_commit::{Mmcs, Pcs, PolynomialSpace, TwoAdicMultiplicativeCoset};\nuse p3_dft::TwoAdicSubgroupDft;\nuse p3_field::{\n    extension::BinomialExtensionField, ExtensionField, Field, PackedField, PackedValue,\n    PrimeField64, TwoAdicField,\n};\nuse p3_fri::{FriConfig, VerificationError};\nuse p3_matrix::{\n    bitrev::BitReversableMatrix,\n    dense::{DenseMatrix, RowMajorMatrix},\n    Matrix,\n};\nuse p3_maybe_rayon::prelude::*;\nuse p3_poseidon2::{Poseidon2, Poseidon2ExternalMatrixGeneral};\nuse p3_symmetric::{CryptographicPermutation, Hash};\nuse p3_util::log2_strict_usize;\nuse serde::{Deserialize, Serialize};\nuse sp1_core::utils::{InnerBatchOpening, InnerFriProof};\nuse sp1_recursion_compiler::{\n    asm::AsmConfig,\n    ir::{Builder, Config},\n};\nuse sp1_recursion_core::air::Block;\nuse sp1_recursion_program::{\n    challenger::DuplexChallengerVariable, fri::types::TwoAdicPcsProofVariable, hints::Hintable,\n};\nuse tachyon_rs::math::finite_fields::{baby_bear::BabyBear as TachyonBabyBearImpl, Fp4};\nuse tracing::instrument;\n\nuse crate::util::Readable;\n\npub struct TachyonBabyBear(pub TachyonBabyBearImpl);\npub struct TachyonBabyBear4(pub Fp4<TachyonBabyBearImpl>);\n\n#[cxx::bridge(namespace = \"tachyon::sp1_api::baby_bear_poseidon2\")]\npub mod ffi {\n    extern \"Rust\" {\n        type TachyonBabyBear;\n        type TachyonBabyBear4;\n    }\n\n    unsafe extern \"C++\" {\n        include!(\"vendors/sp1/include/baby_bear_poseidon2_commitment_vec.h\");\n\n        type CommitmentVec;\n\n        fn new_commitment_vec(rounds: usize) -> UniquePtr<CommitmentVec>;\n        fn set(self: Pin<&mut CommitmentVec>, round: usize, commitment: &[TachyonBabyBear]);\n    }\n\n    unsafe extern \"C++\" {\n        include!(\"vendors/sp1/include/baby_bear_poseidon2_domains.h\");\n\n        type Domains;\n\n        fn new_domains(rounds: usize) -> UniquePtr<Domains>;\n        fn allocate(self: Pin<&mut Domains>, round: usize, size: usize);\n        fn set(\n            self: Pin<&mut Domains>,\n            round: usize,\n            idx: usize,\n            log_n: u32,\n            shift: &TachyonBabyBear,\n        );\n    }\n\n    unsafe extern \"C++\" {\n        include!(\"vendors/sp1/include/baby_bear_poseidon2_duplex_challenger.h\");\n\n        type DuplexChallenger;\n\n        fn new_duplex_challenger() -> UniquePtr<DuplexChallenger>;\n        fn observe(self: Pin<&mut DuplexChallenger>, value: &TachyonBabyBear);\n        fn sample(self: Pin<&mut DuplexChallenger>) -> Box<TachyonBabyBear>;\n        fn write_hint(&self) -> Vec<u8>;\n        fn clone(&self) -> UniquePtr<DuplexChallenger>;\n    }\n\n    unsafe extern \"C++\" {\n        include!(\"vendors/sp1/include/baby_bear_poseidon2_fri_proof.h\");\n\n        type FriProof;\n\n        fn deserialize_fri_proof(data: &[u8]) -> UniquePtr<FriProof>;\n        fn eq(&self, other: &FriProof) -> bool;\n        fn write_hint(&self) -> Vec<u8>;\n        fn serialize(&self) -> Vec<u8>;\n        fn clone(&self) -> UniquePtr<FriProof>;\n    }\n\n    unsafe extern \"C++\" {\n        include!(\"vendors/sp1/include/baby_bear_poseidon2_lde_vec.h\");\n\n        type LDEVec;\n\n        fn new_lde_vec() -> UniquePtr<LDEVec>;\n        fn add(self: Pin<&mut LDEVec>, values: &[TachyonBabyBear], cols: usize);\n    }\n\n    unsafe extern \"C++\" {\n        include!(\"vendors/sp1/include/baby_bear_poseidon2_opened_values.h\");\n\n        type OpenedValues;\n\n        fn new_opened_values(rounds: usize) -> UniquePtr<OpenedValues>;\n        fn allocate_outer(self: Pin<&mut OpenedValues>, round: usize, rows: usize, cols: usize);\n        fn allocate_inner(\n            self: Pin<&mut OpenedValues>,\n            round: usize,\n            row: usize,\n            cols: usize,\n            size: usize,\n        );\n        fn set(\n            self: Pin<&mut OpenedValues>,\n            round: usize,\n            row: usize,\n            col: usize,\n            idx: usize,\n            value: &TachyonBabyBear4,\n        );\n    }\n\n    unsafe extern \"C++\" {\n        include!(\"vendors/sp1/include/baby_bear_poseidon2_opening_points.h\");\n\n        type OpeningPoints;\n\n        fn new_opening_points(rounds: usize) -> UniquePtr<OpeningPoints>;\n        fn clone(&self) -> UniquePtr<OpeningPoints>;\n        fn allocate(self: Pin<&mut OpeningPoints>, round: usize, rows: usize, cols: usize);\n        fn set(\n            self: Pin<&mut OpeningPoints>,\n            round: usize,\n            row: usize,\n            col: usize,\n            point: &TachyonBabyBear4,\n        );\n    }\n\n    unsafe extern \"C++\" {\n        include!(\"vendors/sp1/include/baby_bear_poseidon2_opening_proof.h\");\n\n        type OpeningProof;\n\n        fn serialize_to_opened_values(&self) -> Vec<u8>;\n        fn take_fri_proof(self: Pin<&mut OpeningProof>) -> UniquePtr<FriProof>;\n    }\n\n    unsafe extern \"C++\" {\n        include!(\"vendors/sp1/include/baby_bear_poseidon2_prover_data.h\");\n\n        type ProverData;\n\n        fn deserialize_prover_data(data: &[u8]) -> UniquePtr<ProverData>;\n        fn eq(&self, other: &ProverData) -> bool;\n        fn write_commit(&self, values: &mut [TachyonBabyBear]);\n        fn serialize(&self) -> Vec<u8>;\n        fn clone(&self) -> UniquePtr<ProverData>;\n    }\n\n    unsafe extern \"C++\" {\n        include!(\"vendors/sp1/include/baby_bear_poseidon2_prover_data_vec.h\");\n\n        type ProverDataVec;\n\n        fn new_prover_data_vec(rounds: usize) -> UniquePtr<ProverDataVec>;\n        fn clone(&self) -> UniquePtr<ProverDataVec>;\n        fn set(self: Pin<&mut ProverDataVec>, round: usize, prover_data: &ProverData);\n    }\n\n    unsafe extern \"C++\" {\n        include!(\"vendors/sp1/include/baby_bear_poseidon2_two_adic_fri_pcs.h\");\n\n        type TwoAdicFriPcs;\n\n        fn new_two_adic_fri_pcs(\n            log_blowup: usize,\n            num_queries: usize,\n            proof_of_work_bits: usize,\n        ) -> UniquePtr<TwoAdicFriPcs>;\n        fn coset_lde_batch(\n            &self,\n            values: &mut [TachyonBabyBear],\n            cols: usize,\n            extended_values: &mut [TachyonBabyBear],\n            shift: &TachyonBabyBear,\n        );\n        fn commit(&self, lde_vec: Pin<&mut LDEVec>) -> UniquePtr<ProverData>;\n        fn do_open(\n            &self,\n            prover_data_vec: &ProverDataVec,\n            opening_points: &OpeningPoints,\n            challenger: Pin<&mut DuplexChallenger>,\n        ) -> UniquePtr<OpeningProof>;\n        fn do_verify(\n            &self,\n            commitments: &CommitmentVec,\n            domains: &Domains,\n            opening_points: &OpeningPoints,\n            opened_values: &OpenedValues,\n            proof: &FriProof,\n            challenger: Pin<&mut DuplexChallenger>,\n        ) -> bool;\n    }\n}\n\npub struct CommitmentVec<Val> {\n    inner: cxx::UniquePtr<ffi::CommitmentVec>,\n    _marker: PhantomData<Val>,\n}\n\nimpl<Val> Debug for CommitmentVec<Val> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"CommitmentVec\").finish()\n    }\n}\n\nimpl<Val> CommitmentVec<Val> {\n    pub fn new(inner: cxx::UniquePtr<ffi::CommitmentVec>) -> Self {\n        Self {\n            inner,\n            _marker: PhantomData,\n        }\n    }\n\n    pub fn set(&mut self, round: usize, commitment: &[Val]) {\n        self.inner\n            .pin_mut()\n            .set(round, unsafe { std::mem::transmute(commitment) })\n    }\n}\n\npub struct Domains<Val> {\n    inner: cxx::UniquePtr<ffi::Domains>,\n    _marker: PhantomData<Val>,\n}\n\nimpl<Val> Debug for Domains<Val> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"Domains\").finish()\n    }\n}\n\nimpl<Val> Domains<Val> {\n    pub fn new(inner: cxx::UniquePtr<ffi::Domains>) -> Self {\n        Self {\n            inner,\n            _marker: PhantomData,\n        }\n    }\n\n    pub fn allocate(&mut self, round: usize, size: usize) {\n        self.inner.pin_mut().allocate(round, size)\n    }\n\n    pub fn set(&mut self, round: usize, idx: usize, log_n: u32, shift: Val) {\n        self.inner\n            .pin_mut()\n            .set(round, idx, log_n, unsafe { std::mem::transmute(&shift) })\n    }\n}\n\npub struct DuplexChallenger<F, P, const WIDTH: usize, const RATE: usize> {\n    inner: cxx::UniquePtr<ffi::DuplexChallenger>,\n    _marker: PhantomData<(F, P)>,\n}\n\n// NOTE(chokobole): This is needed by `GrindingChallenger` trait.\n// See https://github.com/Plonky3/Plonky3/blob/eeb4e37/challenger/src/grinding_challenger.rs#L8-L9.\nunsafe impl Sync for ffi::DuplexChallenger {}\n\nimpl<F, P, const WIDTH: usize, const RATE: usize> Clone for DuplexChallenger<F, P, WIDTH, RATE> {\n    fn clone(&self) -> Self {\n        Self {\n            inner: self.inner.clone(),\n            _marker: PhantomData,\n        }\n    }\n}\n\nimpl<F, P, const WIDTH: usize, const RATE: usize> Debug for DuplexChallenger<F, P, WIDTH, RATE> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"DuplexChallenger\").finish()\n    }\n}\n\nimpl<F, P, const WIDTH: usize, const RATE: usize> DuplexChallenger<F, P, WIDTH, RATE> {\n    pub fn new() -> DuplexChallenger<F, P, WIDTH, RATE> {\n        DuplexChallenger {\n            inner: ffi::new_duplex_challenger(),\n            _marker: PhantomData,\n        }\n    }\n\n    pub fn write_hint(&self) -> Vec<u8> {\n        self.inner.write_hint()\n    }\n}\n\nimpl<F, P, const WIDTH: usize, const RATE: usize> FieldChallenger<F>\n    for DuplexChallenger<F, P, WIDTH, RATE>\nwhere\n    F: PrimeField64,\n    P: CryptographicPermutation<[F; WIDTH]>,\n{\n}\n\nimpl<F, P, const WIDTH: usize, const RATE: usize> CanObserve<F>\n    for DuplexChallenger<F, P, WIDTH, RATE>\nwhere\n    F: Copy,\n    P: CryptographicPermutation<[F; WIDTH]>,\n{\n    fn observe(&mut self, value: F) {\n        self.inner\n            .pin_mut()\n            .observe(unsafe { std::mem::transmute(&value) });\n    }\n}\n\nimpl<F, P, const N: usize, const WIDTH: usize, const RATE: usize> CanObserve<[F; N]>\n    for DuplexChallenger<F, P, WIDTH, RATE>\nwhere\n    F: Copy,\n    P: CryptographicPermutation<[F; WIDTH]>,\n{\n    fn observe(&mut self, values: [F; N]) {\n        for value in values {\n            self.observe(value);\n        }\n    }\n}\n\nimpl<F, P, const N: usize, const WIDTH: usize, const RATE: usize> CanObserve<Hash<F, F, N>>\n    for DuplexChallenger<F, P, WIDTH, RATE>\nwhere\n    F: Copy,\n    P: CryptographicPermutation<[F; WIDTH]>,\n{\n    fn observe(&mut self, values: Hash<F, F, N>) {\n        for value in values {\n            self.observe(value);\n        }\n    }\n}\n\nimpl<F, P, const WIDTH: usize, const RATE: usize> CanObserve<Vec<Vec<F>>>\n    for DuplexChallenger<F, P, WIDTH, RATE>\nwhere\n    F: Copy,\n    P: CryptographicPermutation<[F; WIDTH]>,\n{\n    fn observe(&mut self, valuess: Vec<Vec<F>>) {\n        for values in valuess {\n            for value in values {\n                self.observe(value);\n            }\n        }\n    }\n}\n\nimpl<F, EF, P, const WIDTH: usize, const RATE: usize> CanSample<EF>\n    for DuplexChallenger<F, P, WIDTH, RATE>\nwhere\n    F: Field,\n    EF: ExtensionField<F>,\n    P: CryptographicPermutation<[F; WIDTH]>,\n{\n    fn sample(&mut self) -> EF {\n        EF::from_base_fn(|_| *unsafe {\n            std::mem::transmute::<_, Box<F>>(self.inner.pin_mut().sample())\n        })\n    }\n}\n\nimpl<F, P, const WIDTH: usize, const RATE: usize> CanSampleBits<usize>\n    for DuplexChallenger<F, P, WIDTH, RATE>\nwhere\n    F: PrimeField64,\n    P: CryptographicPermutation<[F; WIDTH]>,\n{\n    fn sample_bits(&mut self, bits: usize) -> usize {\n        debug_assert!(bits < (usize::BITS as usize));\n        debug_assert!((1 << bits) < F::ORDER_U64);\n        let rand_f: F = self.sample();\n        let rand_usize = rand_f.as_canonical_u64() as usize;\n        rand_usize & ((1 << bits) - 1)\n    }\n}\n\nimpl<F, P, const WIDTH: usize, const RATE: usize> GrindingChallenger\n    for DuplexChallenger<F, P, WIDTH, RATE>\nwhere\n    F: PrimeField64,\n    P: CryptographicPermutation<[F; WIDTH]>,\n{\n    type Witness = F;\n\n    #[instrument(name = \"grind for proof-of-work witness\", skip_all)]\n    fn grind(&mut self, bits: usize) -> Self::Witness {\n        let witness = (0..F::ORDER_U64)\n            .into_par_iter()\n            .map(|i| F::from_canonical_u64(i))\n            .find_any(|witness| self.clone().check_witness(bits, *witness))\n            .expect(\"failed to find witness\");\n        assert!(self.check_witness(bits, witness));\n        witness\n    }\n}\n\npub trait HasCXXDuplexChallenger {\n    fn get_inner_pin_mut(&mut self) -> Pin<&mut ffi::DuplexChallenger>;\n}\n\nimpl<F, P, const WIDTH: usize, const RATE: usize> HasCXXDuplexChallenger\n    for DuplexChallenger<F, P, WIDTH, RATE>\n{\n    fn get_inner_pin_mut(&mut self) -> Pin<&mut ffi::DuplexChallenger> {\n        self.inner.pin_mut()\n    }\n}\n\ntype InnerVal = BabyBear;\ntype InnerChallenge = BinomialExtensionField<InnerVal, 4>;\ntype InnerPerm =\n    Poseidon2<InnerVal, Poseidon2ExternalMatrixGeneral, DiffusionMatrixBabyBear, 16, 7>;\ntype InnerConfig = AsmConfig<InnerVal, InnerChallenge>;\ntype C = InnerConfig;\n\nimpl Hintable<C> for DuplexChallenger<BabyBear, InnerPerm, 16, 8> {\n    type HintVariable = DuplexChallengerVariable<C>;\n\n    fn read(builder: &mut Builder<C>) -> Self::HintVariable {\n        let sponge_state = builder.hint_felts();\n        let nb_inputs = builder.hint_var();\n        let input_buffer = builder.hint_felts();\n        let nb_outputs = builder.hint_var();\n        let output_buffer = builder.hint_felts();\n        DuplexChallengerVariable {\n            sponge_state,\n            nb_inputs,\n            input_buffer,\n            nb_outputs,\n            output_buffer,\n        }\n    }\n\n    fn write(&self) -> Vec<Vec<Block<<C as Config>::F>>> {\n        let buffer = self.write_hint();\n        let mut reader = Cursor::new(buffer);\n        let values = Vec::<Vec<[u32; 4]>>::read_from(&mut reader).unwrap();\n        unsafe { std::mem::transmute(values) }\n    }\n}\n\npub struct FriProof {\n    inner: cxx::UniquePtr<ffi::FriProof>,\n}\n\nimpl Serialize for FriProof {\n    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>\n    where\n        S: serde::Serializer,\n    {\n        serializer.serialize_bytes(self.inner.serialize().as_slice())\n    }\n}\n\nimpl<'de> Deserialize<'de> for FriProof {\n    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>\n    where\n        D: serde::Deserializer<'de>,\n    {\n        serde_bytes::deserialize::<serde_bytes::ByteBuf, _>(deserializer)\n            .map(|byte_buf| Self::new(ffi::deserialize_fri_proof(byte_buf.as_slice())))\n    }\n}\n\nimpl Clone for FriProof {\n    fn clone(&self) -> Self {\n        Self {\n            inner: self.inner.clone(),\n        }\n    }\n}\n\nimpl PartialEq for FriProof {\n    fn eq(&self, other: &Self) -> bool {\n        self.inner.eq(&other.inner)\n    }\n}\n\nimpl Eq for FriProof {}\n\nimpl Hintable<C> for FriProof {\n    type HintVariable = TwoAdicPcsProofVariable<C>;\n\n    fn read(builder: &mut Builder<C>) -> Self::HintVariable {\n        let fri_proof = InnerFriProof::read(builder);\n        let query_openings = Vec::<Vec<InnerBatchOpening>>::read(builder);\n        Self::HintVariable {\n            fri_proof,\n            query_openings,\n        }\n    }\n\n    fn write(&self) -> Vec<Vec<Block<<C as Config>::F>>> {\n        let buffer = self.write_hint();\n        let mut reader = Cursor::new(buffer);\n        let values = Vec::<Vec<[u32; 4]>>::read_from(&mut reader).unwrap();\n        unsafe { std::mem::transmute(values) }\n    }\n}\n\nimpl FriProof {\n    pub fn new(inner: cxx::UniquePtr<ffi::FriProof>) -> Self {\n        Self { inner }\n    }\n\n    pub fn write_hint(&self) -> Vec<u8> {\n        self.inner.write_hint()\n    }\n}\npub struct LDEVec<Val> {\n    inner: cxx::UniquePtr<ffi::LDEVec>,\n    _marker: PhantomData<Val>,\n}\n\nimpl<Val> LDEVec<Val> {\n    pub fn new(inner: cxx::UniquePtr<ffi::LDEVec>) -> Self {\n        Self {\n            inner,\n            _marker: PhantomData,\n        }\n    }\n\n    pub fn add(&mut self, values: &[Val], cols: usize) {\n        self.inner\n            .pin_mut()\n            .add(unsafe { std::mem::transmute(values) }, cols)\n    }\n}\n\npub struct OpenedValues<Val> {\n    inner: cxx::UniquePtr<ffi::OpenedValues>,\n    _marker: PhantomData<Val>,\n}\n\nimpl<Val> OpenedValues<Val> {\n    pub fn new(inner: cxx::UniquePtr<ffi::OpenedValues>) -> Self {\n        Self {\n            inner,\n            _marker: PhantomData,\n        }\n    }\n\n    pub fn allocate_outer(&mut self, round: usize, rows: usize, cols: usize) {\n        self.inner.pin_mut().allocate_outer(round, rows, cols)\n    }\n\n    pub fn allocate_inner(&mut self, round: usize, row: usize, cols: usize, size: usize) {\n        self.inner.pin_mut().allocate_inner(round, row, cols, size)\n    }\n\n    pub fn set(&mut self, round: usize, row: usize, col: usize, idx: usize, value: &Val) {\n        self.inner\n            .pin_mut()\n            .set(round, row, col, idx, unsafe { std::mem::transmute(value) })\n    }\n}\n\npub struct OpeningPoints<Val> {\n    inner: cxx::UniquePtr<ffi::OpeningPoints>,\n    _marker: PhantomData<Val>,\n}\n\nimpl<Val: Clone> Clone for OpeningPoints<Val> {\n    fn clone(&self) -> Self {\n        Self {\n            inner: self.inner.clone(),\n            _marker: PhantomData,\n        }\n    }\n}\n\nimpl<Val> OpeningPoints<Val> {\n    pub fn new(inner: cxx::UniquePtr<ffi::OpeningPoints>) -> Self {\n        Self {\n            inner,\n            _marker: PhantomData,\n        }\n    }\n\n    pub fn allocate(&mut self, round: usize, rows: usize, cols: usize) {\n        self.inner.pin_mut().allocate(round, rows, cols)\n    }\n\n    pub fn set(&mut self, round: usize, row: usize, col: usize, point: &Val) {\n        self.inner\n            .pin_mut()\n            .set(round, row, col, unsafe { std::mem::transmute(point) })\n    }\n}\npub struct OpeningProof {\n    inner: cxx::UniquePtr<ffi::OpeningProof>,\n}\n\nimpl OpeningProof {\n    pub fn new(inner: cxx::UniquePtr<ffi::OpeningProof>) -> Self {\n        Self { inner }\n    }\n\n    pub fn serialize_to_opened_values<Challenge>(&self) -> p3_commit::OpenedValues<Challenge> {\n        let buffer = self.inner.serialize_to_opened_values();\n        let mut reader = Cursor::new(buffer);\n        let values = p3_commit::OpenedValues::<[u32; 4]>::read_from(&mut reader).unwrap();\n        unsafe { std::mem::transmute(values) }\n    }\n\n    pub fn take_fri_proof(&mut self) -> FriProof {\n        FriProof::new(self.inner.pin_mut().take_fri_proof())\n    }\n}\n\npub struct ProverData<Val> {\n    inner: cxx::UniquePtr<ffi::ProverData>,\n    pub ldes: Vec<DenseMatrix<Val>>,\n}\n\nimpl<Val: Clone> Clone for ProverData<Val> {\n    fn clone(&self) -> Self {\n        Self {\n            inner: self.inner.clone(),\n            ldes: self.ldes.clone(),\n        }\n    }\n}\n\nimpl<Val> Debug for ProverData<Val> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"ProverData\").finish()\n    }\n}\n\nimpl<Val> PartialEq for ProverData<Val> {\n    fn eq(&self, other: &Self) -> bool {\n        self.inner.eq(&other.inner)\n    }\n}\n\nimpl<Val> Eq for ProverData<Val> {}\n\nimpl<Val> Serialize for ProverData<Val> {\n    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>\n    where\n        S: serde::Serializer,\n    {\n        serializer.serialize_bytes(self.inner.serialize().as_slice())\n    }\n}\n\nimpl<'de, Val> Deserialize<'de> for ProverData<Val> {\n    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>\n    where\n        D: serde::Deserializer<'de>,\n    {\n        serde_bytes::deserialize::<serde_bytes::ByteBuf, _>(deserializer)\n            .map(|byte_buf| Self::new(ffi::deserialize_prover_data(byte_buf.as_slice())))\n    }\n}\n\nimpl<Val> ProverData<Val> {\n    pub fn new(inner: cxx::UniquePtr<ffi::ProverData>) -> Self {\n        Self {\n            inner,\n            ldes: vec![],\n        }\n    }\n\n    pub fn write_commit(&self, values: &mut [BabyBear]) {\n        self.inner\n            .write_commit(unsafe { std::mem::transmute(values) })\n    }\n}\n\npub struct ProverDataVec<Val> {\n    inner: cxx::UniquePtr<ffi::ProverDataVec>,\n    _marker: PhantomData<Val>,\n}\n\nimpl<Val: Clone> Clone for ProverDataVec<Val> {\n    fn clone(&self) -> Self {\n        Self {\n            inner: self.inner.clone(),\n            _marker: PhantomData,\n        }\n    }\n}\n\nimpl<Val> Debug for ProverDataVec<Val> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"ProverDataVec\").finish()\n    }\n}\n\nimpl<Val> ProverDataVec<Val> {\n    pub fn new(inner: cxx::UniquePtr<ffi::ProverDataVec>) -> Self {\n        Self {\n            inner,\n            _marker: PhantomData,\n        }\n    }\n\n    pub fn set(&mut self, round: usize, prover_data: &ProverData<Val>) {\n        self.inner.pin_mut().set(round, &prover_data.inner);\n    }\n}\n\npub struct TwoAdicFriPcs<Val, Dft, InputMmcs, FriMmcs> {\n    log_n: usize,\n    fri: FriConfig<FriMmcs>,\n    inner: cxx::UniquePtr<ffi::TwoAdicFriPcs>,\n    _marker: PhantomData<(Val, Dft, InputMmcs, FriMmcs)>,\n}\n\nimpl<Val, Dft, InputMmcs, FriMmcs> Debug for TwoAdicFriPcs<Val, Dft, InputMmcs, FriMmcs> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"TwoAdicFriPcs\").finish()\n    }\n}\n\nimpl<Val, Dft, InputMmcs, FriMmcs> TwoAdicFriPcs<Val, Dft, InputMmcs, FriMmcs>\nwhere\n    Val: TwoAdicField,\n{\n    pub fn new(\n        log_n: usize,\n        fri_config: FriConfig<FriMmcs>,\n    ) -> TwoAdicFriPcs<Val, Dft, InputMmcs, FriMmcs> {\n        let inner = ffi::new_two_adic_fri_pcs(\n            fri_config.log_blowup,\n            fri_config.num_queries,\n            fri_config.proof_of_work_bits,\n        );\n        Self {\n            log_n,\n            fri: fri_config,\n            inner,\n            _marker: PhantomData,\n        }\n    }\n\n    pub fn fri_config(&self) -> &FriConfig<FriMmcs> {\n        &self.fri\n    }\n\n    pub fn coset_lde_batch(\n        &self,\n        evals: &mut RowMajorMatrix<Val>,\n        extended_evals: &mut RowMajorMatrix<Val>,\n        shift: Val,\n    ) {\n        unsafe {\n            self.inner.coset_lde_batch(\n                std::mem::transmute(evals.values.as_mut_slice()),\n                evals.width,\n                std::mem::transmute(extended_evals.values.as_mut_slice()),\n                std::mem::transmute(&shift),\n            );\n        }\n    }\n\n    pub fn do_commit(&self, ldes: Vec<DenseMatrix<Val>>) -> ProverData<Val> {\n        let mut lde_vec = LDEVec::new(ffi::new_lde_vec());\n        for lde in ldes.iter() {\n            lde_vec.add(lde.values.as_slice(), lde.width);\n        }\n        let mut prover_data = ProverData::new(self.inner.commit(lde_vec.inner.pin_mut()));\n        prover_data.ldes = ldes;\n        prover_data\n    }\n\n    pub fn do_open<Challenge>(\n        &self,\n        prover_data_vec: &ProverDataVec<Val>,\n        opening_points: &OpeningPoints<Challenge>,\n        challenger: Pin<&mut ffi::DuplexChallenger>,\n    ) -> OpeningProof {\n        OpeningProof::new(self.inner.do_open(\n            &prover_data_vec.inner,\n            &opening_points.inner,\n            challenger,\n        ))\n    }\n\n    fn do_verify<Challenge>(\n        &self,\n        commitment_vec: &CommitmentVec<Val>,\n        domains: &Domains<Val>,\n        opening_points: &OpeningPoints<Challenge>,\n        opened_values: &OpenedValues<Challenge>,\n        proof: &FriProof,\n        challenger: Pin<&mut ffi::DuplexChallenger>,\n    ) -> bool {\n        self.inner.do_verify(\n            &commitment_vec.inner,\n            &domains.inner,\n            &opening_points.inner,\n            &opened_values.inner,\n            &proof.inner,\n            challenger,\n        )\n    }\n}\n\nimpl<Val, Dft, InputMmcs, FriMmcs, Challenge, Challenger> Pcs<Challenge, Challenger>\n    for TwoAdicFriPcs<Val, Dft, InputMmcs, FriMmcs>\nwhere\n    Val: TwoAdicField,\n    Dft: TwoAdicSubgroupDft<Val>,\n    InputMmcs: Mmcs<Val>,\n    FriMmcs: Mmcs<Challenge>,\n    Challenge: TwoAdicField + ExtensionField<Val>,\n    Challenger: CanObserve<FriMmcs::Commitment>\n        + CanSample<Challenge>\n        + GrindingChallenger<Witness = Val>\n        + HasCXXDuplexChallenger,\n    <InputMmcs as Mmcs<Val>>::ProverData<RowMajorMatrix<Val>>: Clone,\n{\n    type Domain = TwoAdicMultiplicativeCoset<Val>;\n    type Commitment = Hash<\n        <<Val as Field>::Packing as PackedField>::Scalar,\n        <<Val as Field>::Packing as PackedValue>::Value,\n        8,\n    >;\n    type ProverData = crate::baby_bear_poseidon2::ProverData<Val>;\n    type Proof = FriProof;\n    type Error = VerificationError<InputMmcs::Error, FriMmcs::Error>;\n\n    fn natural_domain_for_degree(&self, degree: usize) -> Self::Domain {\n        let log_n = log2_strict_usize(degree);\n        assert!(log_n <= self.log_n);\n        TwoAdicMultiplicativeCoset {\n            log_n,\n            shift: Val::one(),\n        }\n    }\n\n    fn commit(\n        &self,\n        evaluations: Vec<(Self::Domain, RowMajorMatrix<Val>)>,\n    ) -> (Self::Commitment, Self::ProverData) {\n        let mut ldes = vec![];\n        for (domain, mut evals) in evaluations.into_iter() {\n            assert_eq!(domain.size(), evals.height());\n            let shift = Val::generator() / domain.shift;\n            let mut lde =\n                RowMajorMatrix::default(evals.width(), evals.height() << self.fri.log_blowup);\n            self.coset_lde_batch(&mut evals, &mut lde, shift);\n            ldes.push(lde);\n        }\n        let prover_data = self.do_commit(ldes);\n        let mut value = [<<Val as Field>::Packing as PackedValue>::Value::default(); 8];\n        prover_data.write_commit(unsafe { std::mem::transmute(value.as_mut_slice()) });\n        (Self::Commitment::from(value), prover_data)\n    }\n\n    fn get_evaluations_on_domain<'a>(\n        &self,\n        prover_data: &'a Self::ProverData,\n        idx: usize,\n        domain: Self::Domain,\n    ) -> impl Matrix<Val> + 'a {\n        assert_eq!(domain.shift, Val::generator());\n        let lde = &prover_data.ldes[idx];\n        assert!(lde.height() >= domain.size());\n        lde.split_rows(domain.size()).0.bit_reverse_rows()\n    }\n\n    fn open(\n        &self,\n        // For each round,\n        rounds: Vec<(\n            &Self::ProverData,\n            // for each matrix,\n            Vec<\n                // points to open\n                Vec<Challenge>,\n            >,\n        )>,\n        challenger: &mut Challenger,\n    ) -> (p3_commit::OpenedValues<Challenge>, Self::Proof) {\n        let mut prover_data_vec = ProverDataVec::new(ffi::new_prover_data_vec(rounds.len()));\n        let mut opening_points = OpeningPoints::new(ffi::new_opening_points(rounds.len()));\n        for (round, (prover_data, matrix)) in rounds.iter().enumerate() {\n            prover_data_vec.set(round, &prover_data);\n            opening_points.allocate(round, matrix.len(), matrix[0].len());\n            for (row, rows) in matrix.iter().enumerate() {\n                for (col, challenge) in rows.iter().enumerate() {\n                    opening_points.set(round, row, col, challenge);\n                }\n            }\n        }\n        let mut opening_proof = self.do_open(\n            &prover_data_vec,\n            &opening_points,\n            challenger.get_inner_pin_mut(),\n        );\n        (\n            opening_proof.serialize_to_opened_values(),\n            opening_proof.take_fri_proof(),\n        )\n    }\n\n    fn verify(\n        &self,\n        // For each round:\n        rounds: Vec<(\n            Self::Commitment,\n            // for each matrix:\n            Vec<(\n                // its domain,\n                Self::Domain,\n                // for each point:\n                Vec<(\n                    // the point,\n                    Challenge,\n                    // values at the point\n                    Vec<Challenge>,\n                )>,\n            )>,\n        )>,\n        proof: &Self::Proof,\n        challenger: &mut Challenger,\n    ) -> Result<(), Self::Error> {\n        let mut commitment_vec = CommitmentVec::new(ffi::new_commitment_vec(rounds.len()));\n        let mut domains = Domains::<Val>::new(ffi::new_domains(rounds.len()));\n        let mut opening_points = OpeningPoints::new(ffi::new_opening_points(rounds.len()));\n        let mut opened_values = OpenedValues::new(ffi::new_opened_values(rounds.len()));\n        for (round, (commitment, matrix)) in rounds.iter().enumerate() {\n            commitment_vec.set(round, commitment.as_ref());\n            domains.allocate(round, matrix.len());\n            let claims = &matrix[0].1;\n            opening_points.allocate(round, matrix.len(), claims.len());\n            opened_values.allocate_outer(round, matrix.len(), claims.len());\n            for (row, (domain, claims)) in matrix.iter().enumerate() {\n                domains.set(round, row, domain.log_n as u32, domain.shift);\n                let values = &claims[0].1;\n                opened_values.allocate_inner(round, row, claims.len(), values.len());\n                for (col, (challenge, values)) in claims.iter().enumerate() {\n                    opening_points.set(round, row, col, challenge);\n                    for (idx, value) in values.iter().enumerate() {\n                        opened_values.set(round, row, col, idx, value);\n                    }\n                }\n            }\n        }\n        let succeeded = self.do_verify(\n            &commitment_vec,\n            &domains,\n            &opening_points,\n            &opened_values,\n            proof,\n            challenger.get_inner_pin_mut(),\n        );\n        assert!(succeeded);\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "vendors/sp1/src/baby_bear_poseidon2_commitment_vec.cc",
    "content": "#include \"vendors/sp1/include/baby_bear_poseidon2_commitment_vec.h\"\n\nnamespace tachyon::sp1_api::baby_bear_poseidon2 {\n\nCommitmentVec::~CommitmentVec() {\n  tachyon_sp1_baby_bear_poseidon2_commitment_vec_destroy(commitment_vec_);\n}\n\nvoid CommitmentVec::set(size_t round,\n                        rust::Slice<const TachyonBabyBear> commitment) {\n  tachyon_sp1_baby_bear_poseidon2_commitment_vec_set(\n      commitment_vec_, round,\n      reinterpret_cast<const tachyon_baby_bear*>(commitment.data()));\n}\n\nstd::unique_ptr<CommitmentVec> new_commitment_vec(size_t rounds) {\n  return std::make_unique<CommitmentVec>(\n      tachyon_sp1_baby_bear_poseidon2_commitment_vec_create(rounds));\n}\n\n}  // namespace tachyon::sp1_api::baby_bear_poseidon2\n"
  },
  {
    "path": "vendors/sp1/src/baby_bear_poseidon2_domains.cc",
    "content": "#include \"vendors/sp1/include/baby_bear_poseidon2_domains.h\"\n\nnamespace tachyon::sp1_api::baby_bear_poseidon2 {\n\nDomains::~Domains() {\n  tachyon_sp1_baby_bear_poseidon2_domains_destroy(domains_);\n}\n\nvoid Domains::allocate(size_t round, size_t size) {\n  tachyon_sp1_baby_bear_poseidon2_domains_allocate(domains_, round, size);\n}\n\nvoid Domains::set(size_t round, size_t idx, uint32_t log_n,\n                  const TachyonBabyBear& shift) {\n  tachyon_sp1_baby_bear_poseidon2_domains_set(\n      domains_, round, idx, log_n,\n      reinterpret_cast<const tachyon_baby_bear*>(&shift));\n}\n\nstd::unique_ptr<Domains> new_domains(size_t rounds) {\n  return std::make_unique<Domains>(\n      tachyon_sp1_baby_bear_poseidon2_domains_create(rounds));\n}\n\n}  // namespace tachyon::sp1_api::baby_bear_poseidon2\n"
  },
  {
    "path": "vendors/sp1/src/baby_bear_poseidon2_duplex_challenger.cc",
    "content": "#include \"vendors/sp1/include/baby_bear_poseidon2_duplex_challenger.h\"\n\n#include \"tachyon/rs/base/container_util.h\"\n\nnamespace tachyon::sp1_api::baby_bear_poseidon2 {\n\nDuplexChallenger::DuplexChallenger()\n    : challenger_(tachyon_sp1_baby_bear_poseidon2_duplex_challenger_create()) {}\n\nDuplexChallenger::~DuplexChallenger() {\n  tachyon_sp1_baby_bear_poseidon2_duplex_challenger_destroy(challenger_);\n}\n\nvoid DuplexChallenger::observe(const TachyonBabyBear& value) {\n  tachyon_sp1_baby_bear_poseidon2_duplex_challenger_observe(\n      challenger_, reinterpret_cast<const tachyon_baby_bear*>(&value));\n}\n\nrust::Box<TachyonBabyBear> DuplexChallenger::sample() {\n  tachyon_baby_bear* ret = new tachyon_baby_bear;\n  *ret = tachyon_sp1_baby_bear_poseidon2_duplex_challenger_sample(challenger_);\n  return rust::Box<TachyonBabyBear>::from_raw(\n      reinterpret_cast<TachyonBabyBear*>(ret));\n}\n\nstd::unique_ptr<DuplexChallenger> DuplexChallenger::clone() const {\n  return std::make_unique<DuplexChallenger>(\n      tachyon_sp1_baby_bear_poseidon2_duplex_challenger_clone(challenger_));\n}\n\nrust::Vec<uint8_t> DuplexChallenger::write_hint() const {\n  size_t size;\n  tachyon_sp1_baby_bear_poseidon2_duplex_challenger_write_hint(challenger_,\n                                                               nullptr, &size);\n  rust::Vec<uint8_t> ret = rs::CreateDefaultVector<uint8_t>(size);\n  tachyon_sp1_baby_bear_poseidon2_duplex_challenger_write_hint(\n      challenger_, ret.data(), &size);\n  return ret;\n}\n\nstd::unique_ptr<DuplexChallenger> new_duplex_challenger() {\n  return std::make_unique<DuplexChallenger>();\n}\n\n}  // namespace tachyon::sp1_api::baby_bear_poseidon2\n"
  },
  {
    "path": "vendors/sp1/src/baby_bear_poseidon2_fri_proof.cc",
    "content": "#include \"vendors/sp1/include/baby_bear_poseidon2_fri_proof.h\"\n\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_fri_proof_type_traits.h\"\n#include \"tachyon/rs/base/container_util.h\"\n\nnamespace tachyon::sp1_api::baby_bear_poseidon2 {\n\nFriProof::~FriProof() {\n  tachyon_sp1_baby_bear_poseidon2_fri_proof_destroy(proof_);\n}\n\nrust::Vec<uint8_t> FriProof::write_hint() const {\n  size_t size;\n  tachyon_sp1_baby_bear_poseidon2_fri_proof_write_hint(proof_, nullptr, &size);\n  rust::Vec<uint8_t> ret = rs::CreateDefaultVector<uint8_t>(size);\n  tachyon_sp1_baby_bear_poseidon2_fri_proof_write_hint(proof_, ret.data(),\n                                                       &size);\n  return ret;\n}\n\nbool FriProof::eq(const FriProof& other) const {\n  return c::base::native_cast(*proof_) == c::base::native_cast(*other.proof_);\n}\n\nrust::Vec<uint8_t> FriProof::serialize() const {\n  size_t size;\n  tachyon_sp1_baby_bear_poseidon2_fri_proof_serialize(proof_, nullptr, &size);\n  rust::Vec<uint8_t> ret = rs::CreateDefaultVector<uint8_t>(size);\n  tachyon_sp1_baby_bear_poseidon2_fri_proof_serialize(proof_, ret.data(),\n                                                      &size);\n  return ret;\n}\n\nstd::unique_ptr<FriProof> FriProof::clone() const {\n  return std::make_unique<FriProof>(\n      tachyon_sp1_baby_bear_poseidon2_fri_proof_clone(proof_));\n}\n\nstd::unique_ptr<FriProof> deserialize_fri_proof(\n    rust::Slice<const uint8_t> data) {\n  return std::make_unique<FriProof>(\n      tachyon_sp1_baby_bear_poseidon2_fri_proof_deserialize(data.data(),\n                                                            data.size()));\n}\n\n}  // namespace tachyon::sp1_api::baby_bear_poseidon2\n"
  },
  {
    "path": "vendors/sp1/src/baby_bear_poseidon2_lde_vec.cc",
    "content": "#include \"vendors/sp1/include/baby_bear_poseidon2_lde_vec.h\"\n\nnamespace tachyon::sp1_api::baby_bear_poseidon2 {\n\nLDEVec::~LDEVec() { tachyon_sp1_baby_bear_poseidon2_lde_vec_destroy(lde_vec_); }\n\nvoid LDEVec::add(rust::Slice<const TachyonBabyBear> lde, size_t cols) {\n  tachyon_sp1_baby_bear_poseidon2_lde_vec_add(\n      lde_vec_, reinterpret_cast<const tachyon_baby_bear*>(lde.data()),\n      lde.size() / cols, cols);\n}\n\nstd::unique_ptr<LDEVec> new_lde_vec() {\n  return std::make_unique<LDEVec>(\n      tachyon_sp1_baby_bear_poseidon2_lde_vec_create());\n}\n\n}  // namespace tachyon::sp1_api::baby_bear_poseidon2\n"
  },
  {
    "path": "vendors/sp1/src/baby_bear_poseidon2_opened_values.cc",
    "content": "#include \"vendors/sp1/include/baby_bear_poseidon2_opened_values.h\"\n\n#include \"vendors/sp1/src/baby_bear_poseidon2.rs.h\"\n\nnamespace tachyon::sp1_api::baby_bear_poseidon2 {\n\nOpenedValues::~OpenedValues() {\n  tachyon_sp1_baby_bear_poseidon2_opened_values_destroy(opened_values_);\n}\n\nvoid OpenedValues::allocate_outer(size_t round, size_t rows, size_t cols) {\n  tachyon_sp1_baby_bear_poseidon2_opened_values_allocate_outer(\n      opened_values_, round, rows, cols);\n}\n\nvoid OpenedValues::allocate_inner(size_t round, size_t row, size_t cols,\n                                  size_t size) {\n  tachyon_sp1_baby_bear_poseidon2_opened_values_allocate_inner(\n      opened_values_, round, row, cols, size);\n}\n\nvoid OpenedValues::set(size_t round, size_t row, size_t col, size_t idx,\n                       const TachyonBabyBear4& value) {\n  tachyon_sp1_baby_bear_poseidon2_opened_values_set(\n      opened_values_, round, row, col, idx,\n      reinterpret_cast<const tachyon_baby_bear4*>(&value));\n}\n\nstd::unique_ptr<OpenedValues> new_opened_values(size_t rounds) {\n  return std::make_unique<OpenedValues>(\n      tachyon_sp1_baby_bear_poseidon2_opened_values_create(rounds));\n}\n\n}  // namespace tachyon::sp1_api::baby_bear_poseidon2\n"
  },
  {
    "path": "vendors/sp1/src/baby_bear_poseidon2_opening_points.cc",
    "content": "#include \"vendors/sp1/include/baby_bear_poseidon2_opening_points.h\"\n\n#include \"vendors/sp1/src/baby_bear_poseidon2.rs.h\"\n\nnamespace tachyon::sp1_api::baby_bear_poseidon2 {\n\nOpeningPoints::~OpeningPoints() {\n  tachyon_sp1_baby_bear_poseidon2_opening_points_destroy(opening_points_);\n}\n\nstd::unique_ptr<OpeningPoints> OpeningPoints::clone() const {\n  return std::make_unique<OpeningPoints>(\n      tachyon_sp1_baby_bear_poseidon2_opening_points_clone(opening_points_));\n}\n\nvoid OpeningPoints::allocate(size_t round, size_t rows, size_t cols) {\n  tachyon_sp1_baby_bear_poseidon2_opening_points_allocate(opening_points_,\n                                                          round, rows, cols);\n}\n\nvoid OpeningPoints::set(size_t round, size_t row, size_t col,\n                        const TachyonBabyBear4& point) {\n  tachyon_sp1_baby_bear_poseidon2_opening_points_set(\n      opening_points_, round, row, col,\n      reinterpret_cast<const tachyon_baby_bear4*>(&point));\n}\n\nstd::unique_ptr<OpeningPoints> new_opening_points(size_t rounds) {\n  return std::make_unique<OpeningPoints>(\n      tachyon_sp1_baby_bear_poseidon2_opening_points_create(rounds));\n}\n\n}  // namespace tachyon::sp1_api::baby_bear_poseidon2\n"
  },
  {
    "path": "vendors/sp1/src/baby_bear_poseidon2_opening_proof.cc",
    "content": "#include \"vendors/sp1/include/baby_bear_poseidon2_opening_proof.h\"\n\n#include <utility>\n\n#include \"vendors/sp1/src/baby_bear_poseidon2.rs.h\"\n\nnamespace tachyon::sp1_api::baby_bear_poseidon2 {\n\nOpeningProof::OpeningProof()\n    : opened_values_(tachyon_sp1_baby_bear_poseidon2_opened_values_create(0)),\n      proof_(tachyon_sp1_baby_bear_poseidon2_fri_proof_create()) {}\n\nOpeningProof::~OpeningProof() {\n  tachyon_sp1_baby_bear_poseidon2_opened_values_destroy(opened_values_);\n  if (proof_) {\n    tachyon_sp1_baby_bear_poseidon2_fri_proof_destroy(proof_);\n  }\n}\n\nrust::Vec<uint8_t> OpeningProof::serialize_to_opened_values() const {\n  size_t size;\n  tachyon_sp1_baby_bear_poseidon2_opened_values_serialize(opened_values_,\n                                                          nullptr, &size);\n  rust::Vec<uint8_t> ret;\n  ret.reserve(size);\n  for (size_t i = 0; i < size; ++i) {\n    ret.push_back(0);\n  }\n  tachyon_sp1_baby_bear_poseidon2_opened_values_serialize(opened_values_,\n                                                          ret.data(), &size);\n  return ret;\n}\n\nstd::unique_ptr<FriProof> OpeningProof::take_fri_proof() {\n  return std::make_unique<FriProof>(std::exchange(proof_, nullptr));\n}\n\n}  // namespace tachyon::sp1_api::baby_bear_poseidon2\n"
  },
  {
    "path": "vendors/sp1/src/baby_bear_poseidon2_prover_data.cc",
    "content": "#include \"vendors/sp1/include/baby_bear_poseidon2_prover_data.h\"\n\n#include <string.h>\n\n#include \"tachyon/base/logging.h\"\n#include \"tachyon/c/zk/air/sp1/baby_bear_poseidon2_field_merkle_tree_type_traits.h\"\n#include \"tachyon/rs/base/container_util.h\"\n#include \"vendors/sp1/src/baby_bear_poseidon2.rs.h\"\n\nnamespace tachyon::sp1_api::baby_bear_poseidon2 {\n\nProverData::~ProverData() {\n  tachyon_sp1_baby_bear_poseidon2_field_merkle_tree_destroy(tree_);\n}\n\nbool ProverData::eq(const ProverData& other) const {\n  return c::base::native_cast(*tree_) == c::base::native_cast(*other.tree_);\n}\n\nvoid ProverData::write_commit(rust::Slice<TachyonBabyBear> values) const {\n  CHECK_EQ(values.size(), size_t{TACHYON_PLONKY3_BABY_BEAR_POSEIDON2_CHUNK});\n  for (size_t i = 0; i < values.size(); ++i) {\n    memcpy(&values[i], &commitment_[i], sizeof(uint32_t));\n  }\n}\n\nrust::Vec<uint8_t> ProverData::serialize() const {\n  size_t size;\n  tachyon_sp1_baby_bear_poseidon2_field_merkle_tree_serialize(tree_, nullptr,\n                                                              &size);\n  rust::Vec<uint8_t> ret = rs::CreateDefaultVector<uint8_t>(size);\n  tachyon_sp1_baby_bear_poseidon2_field_merkle_tree_serialize(tree_, ret.data(),\n                                                              &size);\n  return ret;\n}\n\nstd::unique_ptr<ProverData> ProverData::clone() const {\n  return std::make_unique<ProverData>(\n      tachyon_sp1_baby_bear_poseidon2_field_merkle_tree_clone(tree_));\n}\n\nstd::unique_ptr<ProverData> deserialize_prover_data(\n    rust::Slice<const uint8_t> data) {\n  return std::make_unique<ProverData>(\n      tachyon_sp1_baby_bear_poseidon2_field_merkle_tree_deserialize(\n          data.data(), data.size()));\n}\n\n}  // namespace tachyon::sp1_api::baby_bear_poseidon2\n"
  },
  {
    "path": "vendors/sp1/src/baby_bear_poseidon2_prover_data_vec.cc",
    "content": "#include \"vendors/sp1/include/baby_bear_poseidon2_prover_data_vec.h\"\n\n#include \"vendors/sp1/src/baby_bear_poseidon2.rs.h\"\n\nnamespace tachyon::sp1_api::baby_bear_poseidon2 {\n\nProverDataVec::~ProverDataVec() {\n  tachyon_sp1_baby_bear_poseidon2_field_merkle_tree_vec_destroy(tree_vec_);\n}\n\nstd::unique_ptr<ProverDataVec> ProverDataVec::clone() const {\n  return std::make_unique<ProverDataVec>(\n      tachyon_sp1_baby_bear_poseidon2_field_merkle_tree_vec_clone(tree_vec_));\n}\n\nvoid ProverDataVec::set(size_t round, const ProverData& prover_data) {\n  tachyon_sp1_baby_bear_poseidon2_field_merkle_tree_vec_set(tree_vec_, round,\n                                                            prover_data.tree());\n}\n\nstd::unique_ptr<ProverDataVec> new_prover_data_vec(size_t rounds) {\n  return std::make_unique<ProverDataVec>(\n      tachyon_sp1_baby_bear_poseidon2_field_merkle_tree_vec_create(rounds));\n}\n\n}  // namespace tachyon::sp1_api::baby_bear_poseidon2\n"
  },
  {
    "path": "vendors/sp1/src/baby_bear_poseidon2_two_adic_fri_pcs.cc",
    "content": "#include \"vendors/sp1/include/baby_bear_poseidon2_two_adic_fri_pcs.h\"\n\n#include \"vendors/sp1/src/baby_bear_poseidon2.rs.h\"\n\nnamespace tachyon::sp1_api::baby_bear_poseidon2 {\n\nTwoAdicFriPcs::TwoAdicFriPcs(size_t log_blowup, size_t num_queries,\n                             size_t proof_of_work_bits)\n    : pcs_(tachyon_sp1_baby_bear_poseidon2_two_adic_fri_create(\n          static_cast<uint32_t>(log_blowup), num_queries, proof_of_work_bits)) {\n}\n\nTwoAdicFriPcs::~TwoAdicFriPcs() {\n  tachyon_sp1_baby_bear_poseidon2_two_adic_fri_destroy(pcs_);\n}\n\nvoid TwoAdicFriPcs::coset_lde_batch(\n    rust::Slice<TachyonBabyBear> values, size_t cols,\n    rust::Slice<TachyonBabyBear> extended_values,\n    const TachyonBabyBear& shift) const {\n  tachyon_sp1_baby_bear_poseidon2_two_adic_fri_coset_lde_batch(\n      const_cast<tachyon_sp1_baby_bear_poseidon2_two_adic_fri*>(pcs_),\n      reinterpret_cast<tachyon_baby_bear*>(values.data()), values.size() / cols,\n      cols, reinterpret_cast<tachyon_baby_bear*>(extended_values.data()),\n      reinterpret_cast<const tachyon_baby_bear&>(shift));\n}\n\nstd::unique_ptr<ProverData> TwoAdicFriPcs::commit(LDEVec& lde_vec) const {\n  std::unique_ptr<ProverData> ret(new ProverData);\n  tachyon_sp1_baby_bear_poseidon2_two_adic_fri_commit(\n      const_cast<tachyon_sp1_baby_bear_poseidon2_two_adic_fri*>(pcs_),\n      lde_vec.lde_vec(), ret->commitment(), ret->tree_ptr());\n  return ret;\n}\n\nstd::unique_ptr<OpeningProof> TwoAdicFriPcs::do_open(\n    const ProverDataVec& prover_data_vec, const OpeningPoints& opening_points,\n    DuplexChallenger& challenger) const {\n  std::unique_ptr<OpeningProof> ret(new OpeningProof);\n  tachyon_sp1_baby_bear_poseidon2_two_adic_fri_open(\n      pcs_, prover_data_vec.tree_vec(), opening_points.opening_points(),\n      challenger.challenger(), ret->opened_values_ptr(), ret->proof_ptr());\n  return ret;\n}\n\nbool TwoAdicFriPcs::do_verify(const CommitmentVec& commitment_vec,\n                              const Domains& domains,\n                              const OpeningPoints& opening_points,\n                              const OpenedValues& opened_values,\n                              const FriProof& proof,\n                              DuplexChallenger& challenger) const {\n  return tachyon_sp1_baby_bear_poseidon2_two_adic_fri_verify(\n      pcs_, commitment_vec.commitment_vec(), domains.domains(),\n      opening_points.opening_points(), opened_values.opened_values(),\n      proof.proof(), challenger.challenger());\n}\n\nstd::unique_ptr<TwoAdicFriPcs> new_two_adic_fri_pcs(size_t log_blowup,\n                                                    size_t num_queries,\n                                                    size_t proof_of_work_bits) {\n  return std::make_unique<TwoAdicFriPcs>(log_blowup, num_queries,\n                                         proof_of_work_bits);\n}\n\n}  // namespace tachyon::sp1_api::baby_bear_poseidon2\n"
  },
  {
    "path": "vendors/sp1/src/challenger.rs",
    "content": "#[cfg(test)]\nmod test {\n    use crate::baby_bear_poseidon2::DuplexChallenger as TachyonDuplexChallenger;\n    use p3_baby_bear::BabyBear;\n    use p3_challenger::{CanObserve, CanSample, DuplexChallenger};\n    use p3_field::{extension::BinomialExtensionField, AbstractField};\n    use sp1_core::utils::baby_bear_poseidon2::{my_perm, Perm};\n    use sp1_recursion_program::hints::Hintable;\n\n    type F = BabyBear;\n    type EF = BinomialExtensionField<F, 4>;\n\n    #[test]\n    fn test_duplex_challenger() {\n        const WIDTH: usize = 16;\n\n        const RATE: usize = 8;\n\n        let perm = my_perm();\n\n        let mut tachyon_duplex_challenger: TachyonDuplexChallenger<F, Perm, WIDTH, RATE> =\n            TachyonDuplexChallenger::new();\n\n        let mut duplex_challenger: DuplexChallenger<F, Perm, WIDTH, RATE> =\n            DuplexChallenger::new(perm);\n\n        (0..20).for_each(|element| {\n            tachyon_duplex_challenger.observe(F::from_canonical_u8(element as u8));\n            duplex_challenger.observe(F::from_canonical_u8(element as u8));\n        });\n\n        for _ in 0..10 {\n            assert_eq!(\n                <TachyonDuplexChallenger<F, Perm, WIDTH, RATE> as CanSample<F>>::sample(\n                    &mut tachyon_duplex_challenger\n                ),\n                <DuplexChallenger<F, Perm, WIDTH, RATE> as CanSample<F>>::sample(\n                    &mut duplex_challenger\n                )\n            );\n        }\n\n        for _ in 0..10 {\n            assert_eq!(\n                <TachyonDuplexChallenger<F, Perm, WIDTH, RATE> as CanSample<EF>>::sample(\n                    &mut tachyon_duplex_challenger\n                ),\n                <DuplexChallenger<F, Perm, WIDTH, RATE> as CanSample<EF>>::sample(\n                    &mut duplex_challenger\n                )\n            );\n        }\n\n        let result = duplex_challenger.write();\n        let tachyon_result = tachyon_duplex_challenger.write();\n        assert_eq!(result, tachyon_result);\n    }\n}\n"
  },
  {
    "path": "vendors/sp1/src/lib.rs",
    "content": "pub mod baby_bear_poseidon2;\npub mod challenger;\npub mod two_adic_fri_pcs;\npub mod util;\n"
  },
  {
    "path": "vendors/sp1/src/two_adic_fri_pcs.rs",
    "content": "#[cfg(test)]\nmod test {\n    use itertools::{izip, Itertools};\n\n    use p3_challenger::FieldChallenger;\n    use p3_commit::Pcs;\n    use p3_commit::PolynomialSpace;\n    use p3_commit::TwoAdicMultiplicativeCoset;\n    use p3_dft::TwoAdicSubgroupDft;\n    use p3_field::AbstractField;\n    use p3_fri::TwoAdicFriPcs;\n    use p3_matrix::{bitrev::BitReversableMatrix, dense::RowMajorMatrix, Matrix};\n    use rand::{Rng, SeedableRng};\n    use rand_chacha::ChaCha20Rng;\n    use sp1_core::utils::baby_bear_poseidon2::{\n        default_fri_config, my_perm, Challenge, ChallengeMmcs, Dft, MyCompress, MyHash, Perm, Val,\n        ValMmcs,\n    };\n\n    use crate::baby_bear_poseidon2::FriProof;\n    use crate::baby_bear_poseidon2::ProverData;\n    use crate::baby_bear_poseidon2::{\n        DuplexChallenger as TachyonDuplexChallenger, TwoAdicFriPcs as TachyonTwoAdicFriPcs,\n    };\n\n    type Challenger = TachyonDuplexChallenger<Val, Perm, 16, 8>;\n\n    fn seeded_rng() -> impl Rng {\n        ChaCha20Rng::seed_from_u64(0)\n    }\n\n    #[test]\n    fn test_two_adic_fri_pcs() {\n        const LOG_N: usize = 20;\n\n        let perm = my_perm();\n        let hash = MyHash::new(perm.clone());\n        let compress = MyCompress::new(perm.clone());\n        let val_mmcs = ValMmcs::new(hash, compress);\n        let dft = Dft {};\n        let fri_config = default_fri_config();\n\n        let pcs = TwoAdicFriPcs::<Val, Dft, ValMmcs, ChallengeMmcs>::new(\n            LOG_N, dft, val_mmcs, fri_config,\n        );\n\n        let fri_config = default_fri_config();\n        let log_blowup = fri_config.log_blowup;\n        let tachyon_pcs =\n            TachyonTwoAdicFriPcs::<Val, Dft, ValMmcs, ChallengeMmcs>::new(LOG_N, fri_config);\n\n        let mut rng = seeded_rng();\n        let log_degrees_by_round = [[3, 4], [3, 4]];\n\n        let domains_and_polys_by_round = log_degrees_by_round\n            .iter()\n            .map(|log_degrees| {\n                log_degrees\n                    .iter()\n                    .map(|&log_degree| {\n                        let d = 1 << log_degree;\n                        // random width 5-15\n                        let width = 5 + rng.gen_range(0..=10);\n                        (\n                            <TwoAdicFriPcs<Val, Dft, ValMmcs, ChallengeMmcs> as Pcs<\n                                Challenge,\n                                Challenger,\n                            >>::natural_domain_for_degree(&pcs, d),\n                            RowMajorMatrix::<Val>::rand(&mut rng, d, width),\n                        )\n                    })\n                    .collect::<Vec<_>>()\n            })\n            .collect::<Vec<_>>();\n\n        let (commits_by_round, data_by_round): (Vec<_>, Vec<_>) = domains_and_polys_by_round\n            .iter()\n            .map(|domains_and_polys| {\n                <TwoAdicFriPcs<Val, Dft, ValMmcs, ChallengeMmcs> as Pcs<\n                Challenge,\n                Challenger,\n            >>::commit(&pcs, domains_and_polys.clone())\n            })\n            .unzip();\n\n        let (tachyon_commits_by_round, tachyon_data_by_round): (Vec<_>, Vec<_>) =\n            domains_and_polys_by_round\n                .iter()\n                .map(|domains_and_polys| {\n                    <TachyonTwoAdicFriPcs<Val, Dft, ValMmcs, ChallengeMmcs> as Pcs<\n                        Challenge,\n                        Challenger,\n                    >>::commit(&tachyon_pcs, domains_and_polys.clone())\n                })\n                .unzip();\n\n        assert_eq!(commits_by_round, tachyon_commits_by_round);\n\n        let domain = TwoAdicMultiplicativeCoset {\n            log_n: 2,\n            shift: Val::generator(),\n        };\n        let eval = <TwoAdicFriPcs<Val, Dft, ValMmcs, ChallengeMmcs> as Pcs<\n            Challenge,\n            Challenger,\n        >>::get_evaluations_on_domain(&pcs, &data_by_round[0], 1, domain)\n        .to_row_major_matrix();\n        let tachyon_eval = <TachyonTwoAdicFriPcs<Val, Dft, ValMmcs, ChallengeMmcs> as Pcs<\n            Challenge,\n            Challenger,\n        >>::get_evaluations_on_domain(\n            &tachyon_pcs, &tachyon_data_by_round[0], 1, domain\n        )\n        .to_row_major_matrix();\n\n        assert_eq!(eval, tachyon_eval);\n\n        let encoded: Vec<u8> = bincode::serialize(&tachyon_data_by_round[0]).unwrap();\n        let decoded: ProverData<Val> = bincode::deserialize(&encoded[..]).unwrap();\n        assert!(tachyon_data_by_round[0] == decoded);\n\n        let ldes_vec = domains_and_polys_by_round\n            .iter()\n            .map(|domains_and_polys| {\n                domains_and_polys\n                    .into_iter()\n                    .map(|(domain, evals)| {\n                        assert_eq!(domain.size(), evals.height());\n                        let shift = Val::generator() / domain.shift;\n                        // Commit to the bit-reversed LDE.\n                        Dft {}\n                            .coset_lde_batch(evals.clone(), log_blowup, shift)\n                            .bit_reverse_rows()\n                            .to_row_major_matrix()\n                    })\n                    .collect::<Vec<_>>()\n            })\n            .collect::<Vec<_>>();\n\n        for (i, ldes) in ldes_vec.clone().into_iter().enumerate() {\n            for (j, lde) in ldes.into_iter().enumerate() {\n                assert_eq!(\n                    lde.to_row_major_matrix(),\n                    tachyon_data_by_round[i].ldes[j]\n                        .clone()\n                        .to_row_major_matrix()\n                );\n            }\n        }\n\n        let mut challenger = Challenger::new();\n        let zeta: Challenge = challenger.sample_ext_element();\n        let mut tachyon_challenger = challenger.clone();\n        let mut tachyon_challenger_for_verify = challenger.clone();\n\n        let points_by_round = log_degrees_by_round\n            .iter()\n            .map(|log_degrees| vec![vec![zeta]; log_degrees.len()])\n            .collect::<Vec<_>>();\n        let data_and_points = data_by_round\n            .iter()\n            .zip(points_by_round.clone())\n            .collect::<Vec<_>>();\n        let (opening_by_round, _proof) = pcs.open(data_and_points, &mut challenger);\n\n        let tachyon_data_and_points = tachyon_data_by_round\n            .iter()\n            .zip(points_by_round)\n            .collect::<Vec<_>>();\n        let (tachyon_opening_by_round, tachyon_proof) =\n            tachyon_pcs.open(tachyon_data_and_points, &mut tachyon_challenger);\n\n        assert_eq!(opening_by_round, tachyon_opening_by_round);\n\n        let rounds = izip!(\n            commits_by_round,\n            domains_and_polys_by_round,\n            opening_by_round\n        )\n        .map(|(commit, domains_and_polys, openings)| {\n            let claims = domains_and_polys\n                .iter()\n                .zip(openings)\n                .map(|((domain, _), mat_openings)| (*domain, vec![(zeta, mat_openings[0].clone())]))\n                .collect_vec();\n            (commit, claims)\n        })\n        .collect_vec();\n\n        assert!(tachyon_pcs\n            .verify(rounds, &tachyon_proof, &mut tachyon_challenger_for_verify)\n            .is_ok());\n\n        let encoded: Vec<u8> = bincode::serialize(&tachyon_proof).unwrap();\n        let decoded: FriProof = bincode::deserialize(&encoded[..]).unwrap();\n        assert!(tachyon_proof == decoded);\n    }\n}\n"
  },
  {
    "path": "vendors/sp1/src/util.rs",
    "content": "use std::{\n    io::{Read, Result},\n    mem::size_of,\n};\n\nfn read_vec<T, R>(reader: &mut R) -> Result<Vec<T>>\nwhere\n    T: Readable,\n    R: Read,\n{\n    let mut len = [0u8; size_of::<usize>()];\n    reader.read_exact(&mut len)?;\n\n    let len = usize::from_ne_bytes(len);\n\n    (0..len)\n        .map(|_| T::read_from(reader))\n        .collect::<Result<Vec<_>>>()\n}\n\npub trait Readable: Sized {\n    fn read_from<R: Read>(reader: &mut R) -> Result<Self>;\n}\n\nimpl<T: Readable> Readable for Vec<T> {\n    fn read_from<R: Read>(reader: &mut R) -> Result<Self> {\n        read_vec(reader)\n    }\n}\n\nimpl<const D: usize> Readable for [u32; D] {\n    fn read_from<R: Read>(reader: &mut R) -> Result<Self> {\n        let data = [0u32; D];\n        Ok(unsafe {\n            let slice = std::slice::from_raw_parts_mut(data.as_ptr() as *mut u8, data.len() * 4);\n            reader.read_exact(slice)?;\n            data\n        })\n    }\n}\n"
  }
]